/* * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WebKitDLL.h" #include "WebEditorClient.h" #include "IWebEditingDelegate.h" #include "IWebUndoTarget.h" #include "IWebURLResponse.h" #include "WebLocalizableStrings.h" #include "WebView.h" #include "DOMCoreClasses.h" #pragma warning(push, 0) #include #include #include #include #include #include #include #include #include #include #pragma warning(pop) using namespace WebCore; using namespace HTMLNames; // {09A11D2B-FAFB-4ca0-A6F7-791EE8932C88} static const GUID IID_IWebUndoCommand = { 0x9a11d2b, 0xfafb, 0x4ca0, { 0xa6, 0xf7, 0x79, 0x1e, 0xe8, 0x93, 0x2c, 0x88 } }; class IWebUndoCommand : public IUnknown { public: virtual void execute() = 0; }; // WebEditorUndoTarget ------------------------------------------------------------- class WebEditorUndoTarget : public IWebUndoTarget { public: WebEditorUndoTarget(); // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); virtual ULONG STDMETHODCALLTYPE AddRef(void); virtual ULONG STDMETHODCALLTYPE Release(void); // IWebUndoTarget virtual HRESULT STDMETHODCALLTYPE invoke( /* [in] */ BSTR actionName, /* [in] */ IUnknown *obj); private: ULONG m_refCount; }; WebEditorUndoTarget::WebEditorUndoTarget() : m_refCount(1) { } HRESULT STDMETHODCALLTYPE WebEditorUndoTarget::QueryInterface(REFIID riid, void** ppvObject) { *ppvObject = 0; if (IsEqualGUID(riid, IID_IUnknown)) *ppvObject = static_cast(this); else if (IsEqualGUID(riid, IID_IWebUndoTarget)) *ppvObject = static_cast(this); else return E_NOINTERFACE; AddRef(); return S_OK; } ULONG STDMETHODCALLTYPE WebEditorUndoTarget::AddRef(void) { return ++m_refCount; } ULONG STDMETHODCALLTYPE WebEditorUndoTarget::Release(void) { ULONG newRef = --m_refCount; if (!newRef) delete(this); return newRef; } HRESULT STDMETHODCALLTYPE WebEditorUndoTarget::invoke( /* [in] */ BSTR /*actionName*/, /* [in] */ IUnknown *obj) { IWebUndoCommand* undoCommand = 0; if (SUCCEEDED(obj->QueryInterface(IID_IWebUndoCommand, (void**)&undoCommand))) { undoCommand->execute(); undoCommand->Release(); } return S_OK; } // WebEditorClient ------------------------------------------------------------------ WebEditorClient::WebEditorClient(WebView* webView) : m_webView(webView) , m_undoTarget(0) { m_undoTarget = new WebEditorUndoTarget(); } WebEditorClient::~WebEditorClient() { if (m_undoTarget) m_undoTarget->Release(); } void WebEditorClient::pageDestroyed() { delete this; } bool WebEditorClient::isContinuousSpellCheckingEnabled() { BOOL enabled; if (FAILED(m_webView->isContinuousSpellCheckingEnabled(&enabled))) return false; return !!enabled; } void WebEditorClient::toggleContinuousSpellChecking() { m_webView->toggleContinuousSpellChecking(0); } bool WebEditorClient::isGrammarCheckingEnabled() { BOOL enabled; if (FAILED(m_webView->isGrammarCheckingEnabled(&enabled))) return false; return !!enabled; } void WebEditorClient::toggleGrammarChecking() { m_webView->toggleGrammarChecking(0); } static void initViewSpecificSpelling(IWebViewEditing* viewEditing) { // we just use this as a flag to indicate that we've spell checked the document // and need to close the spell checker out when the view closes. int tag; viewEditing->spellCheckerDocumentTag(&tag); } int WebEditorClient::spellCheckerDocumentTag() { // we don't use the concept of spelling tags notImplemented(); ASSERT_NOT_REACHED(); return 0; } bool WebEditorClient::shouldBeginEditing(Range*) { notImplemented(); return true; } bool WebEditorClient::shouldEndEditing(Range*) { notImplemented(); return true; } void WebEditorClient::didBeginEditing() { notImplemented(); } void WebEditorClient::respondToChangedContents() { notImplemented(); } void WebEditorClient::respondToChangedSelection() { m_webView->selectionChanged(); } void WebEditorClient::didEndEditing() { notImplemented(); } void WebEditorClient::didWriteSelectionToPasteboard() { notImplemented(); } void WebEditorClient::didSetSelectionTypesForPasteboard() { notImplemented(); } bool WebEditorClient::shouldDeleteRange(Range* /*range*/) { notImplemented(); return true; // FIXME: calling m_webView->editingDelegate() will cause an assertion failure so we don't want to enable this code until that's implemented. //BOOL result = false; //IWebViewEditingDelegate* editingDelegate; //// FIXME: DOMRange needs to be implemented before anything meaningful can be done here //IDOMRange* domRange(0); //if (SUCCEEDED(m_webView->editingDelegate(&editingDelegate))) { // editingDelegate->shouldDeleteDOMRange(m_webView, domRange, &result); // editingDelegate->Release(); //} //return !!result; } bool WebEditorClient::shouldInsertNode(Node* /*node*/, Range* /*replacingRange*/, EditorInsertAction /*givenAction*/) { notImplemented(); return true; } bool WebEditorClient::shouldInsertText(String /*str*/, Range* /* replacingRange */, EditorInsertAction /*givenAction*/) { notImplemented(); return true; // FIXME: calling m_webView->editingDelegate() will cause an assertion failure so we don't want to enable this code until that's implemented. //BOOL result = false; //IWebViewEditingDelegate* editingDelegate; //// FIXME: DOMRange needs to be implemented before anything meaningful can be done here //IDOMRange* domRange(0); // make a DOMRange from replacingRange //BString text(str); //if (SUCCEEDED(m_webView->editingDelegate(&editingDelegate))) { // editingDelegate->shouldInsertText(m_webView, text, domRange, (WebViewInsertAction) givenAction, &result); // editingDelegate->Release(); //} //return !!result; } //bool WebEditorClient::shouldChangeSelectedRange(Range *currentRange, Range *toProposedRange, SelectionAffinity selectionAffinity, bool stillSelecting) //{ notImplemented(); return false; } bool WebEditorClient::shouldApplyStyle(CSSStyleDeclaration* /*style*/, Range* /*toElementsInDOMRange*/) { notImplemented(); return true; } bool WebEditorClient::shouldMoveRangeAfterDelete(Range* /*range*/, Range* /*rangeToBeReplaced*/) { notImplemented(); return true; } bool WebEditorClient::shouldChangeTypingStyle(CSSStyleDeclaration* /*currentStyle*/, CSSStyleDeclaration* /*toProposedStyle*/) { notImplemented(); return false; } void WebEditorClient::webViewDidChangeTypingStyle(WebNotification* /*notification*/) { notImplemented(); } void WebEditorClient::webViewDidChangeSelection(WebNotification* /*notification*/) { notImplemented(); } bool WebEditorClient::shouldShowDeleteInterface(HTMLElement* /*element*/) { notImplemented(); return false; } bool WebEditorClient::smartInsertDeleteEnabled(void) { BOOL enabled = FALSE; m_webView->smartInsertDeleteEnabled(&enabled); return !!enabled; } bool WebEditorClient::shouldChangeSelectedRange(WebCore::Range*, WebCore::Range*, WebCore::EAffinity, bool) { notImplemented(); return true; } void WebEditorClient::textFieldDidBeginEditing(Element* e) { IWebFormDelegate* formDelegate; if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { IDOMElement* domElement = DOMElement::createInstance(e); if (domElement) { IDOMHTMLInputElement* domInputElement; if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { formDelegate->textFieldDidBeginEditing(domInputElement, kit(e->document()->frame())); domInputElement->Release(); } domElement->Release(); } formDelegate->Release(); } } void WebEditorClient::textFieldDidEndEditing(Element* e) { IWebFormDelegate* formDelegate; if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { IDOMElement* domElement = DOMElement::createInstance(e); if (domElement) { IDOMHTMLInputElement* domInputElement; if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { formDelegate->textFieldDidEndEditing(domInputElement, kit(e->document()->frame())); domInputElement->Release(); } domElement->Release(); } formDelegate->Release(); } } void WebEditorClient::textDidChangeInTextField(Element* e) { IWebFormDelegate* formDelegate; if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { IDOMElement* domElement = DOMElement::createInstance(e); if (domElement) { IDOMHTMLInputElement* domInputElement; if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { formDelegate->textDidChangeInTextField(domInputElement, kit(e->document()->frame())); domInputElement->Release(); } domElement->Release(); } formDelegate->Release(); } } bool WebEditorClient::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke) { BOOL result = FALSE; IWebFormDelegate* formDelegate; if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { IDOMElement* domElement = DOMElement::createInstance(e); if (domElement) { IDOMHTMLInputElement* domInputElement; if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { String command = m_webView->interpretKeyEvent(ke); // We allow empty commands here because the app code actually depends on this being called for all key presses. // We may want to revisit this later because it doesn't really make sense to send an empty command. formDelegate->doPlatformCommand(domInputElement, BString(command), kit(e->document()->frame()), &result); domInputElement->Release(); } domElement->Release(); } formDelegate->Release(); } return !!result; } void WebEditorClient::textWillBeDeletedInTextField(Element* e) { // We're using the deleteBackward command for all deletion operations since the autofill code treats all deletions the same way. IWebFormDelegate* formDelegate; if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { IDOMElement* domElement = DOMElement::createInstance(e); if (domElement) { IDOMHTMLInputElement* domInputElement; if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { BOOL result; formDelegate->doPlatformCommand(domInputElement, BString("BackwardDelete"), kit(e->document()->frame()), &result); domInputElement->Release(); } domElement->Release(); } formDelegate->Release(); } } void WebEditorClient::textDidChangeInTextArea(Element* e) { IWebFormDelegate* formDelegate; if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { IDOMElement* domElement = DOMElement::createInstance(e); if (domElement) { IDOMHTMLTextAreaElement* domTextAreaElement; if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLTextAreaElement, (void**)&domTextAreaElement))) { formDelegate->textDidChangeInTextArea(domTextAreaElement, kit(e->document()->frame())); domTextAreaElement->Release(); } domElement->Release(); } formDelegate->Release(); } } class WebEditorUndoCommand : public IWebUndoCommand { public: WebEditorUndoCommand(PassRefPtr editCommand, bool isUndo); void execute(); // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); virtual ULONG STDMETHODCALLTYPE AddRef(void); virtual ULONG STDMETHODCALLTYPE Release(void); private: ULONG m_refCount; RefPtr m_editCommand; bool m_isUndo; }; WebEditorUndoCommand::WebEditorUndoCommand(PassRefPtr editCommand, bool isUndo) : m_editCommand(editCommand) , m_isUndo(isUndo) , m_refCount(1) { } void WebEditorUndoCommand::execute() { if (m_isUndo) m_editCommand->unapply(); else m_editCommand->reapply(); } HRESULT STDMETHODCALLTYPE WebEditorUndoCommand::QueryInterface(REFIID riid, void** ppvObject) { *ppvObject = 0; if (IsEqualGUID(riid, IID_IUnknown)) *ppvObject = static_cast(this); else if (IsEqualGUID(riid, IID_IWebUndoCommand)) *ppvObject = static_cast(this); else return E_NOINTERFACE; AddRef(); return S_OK; } ULONG STDMETHODCALLTYPE WebEditorUndoCommand::AddRef(void) { return ++m_refCount; } ULONG STDMETHODCALLTYPE WebEditorUndoCommand::Release(void) { ULONG newRef = --m_refCount; if (!newRef) delete(this); return newRef; } static LPCTSTR undoNameForEditAction(EditAction editAction) { switch (editAction) { case EditActionUnspecified: return 0; case EditActionSetColor: return LPCTSTR_UI_STRING_KEY("Set Color", "Set Color (Undo action name)", "Undo action name"); case EditActionSetBackgroundColor: return LPCTSTR_UI_STRING_KEY("Set Background Color", "Set Background Color (Undo action name)", "Undo action name"); case EditActionTurnOffKerning: return LPCTSTR_UI_STRING_KEY("Turn Off Kerning", "Turn Off Kerning (Undo action name)", "Undo action name"); case EditActionTightenKerning: return LPCTSTR_UI_STRING_KEY("Tighten Kerning", "Tighten Kerning (Undo action name)", "Undo action name"); case EditActionLoosenKerning: return LPCTSTR_UI_STRING_KEY("Loosen Kerning", "Loosen Kerning (Undo action name)", "Undo action name"); case EditActionUseStandardKerning: return LPCTSTR_UI_STRING_KEY("Use Standard Kerning", "Use Standard Kerning (Undo action name)", "Undo action name"); case EditActionTurnOffLigatures: return LPCTSTR_UI_STRING_KEY("Turn Off Ligatures", "Turn Off Ligatures (Undo action name)", "Undo action name"); case EditActionUseStandardLigatures: return LPCTSTR_UI_STRING_KEY("Use Standard Ligatures", "Use Standard Ligatures (Undo action name)", "Undo action name"); case EditActionUseAllLigatures: return LPCTSTR_UI_STRING_KEY("Use All Ligatures", "Use All Ligatures (Undo action name)", "Undo action name"); case EditActionRaiseBaseline: return LPCTSTR_UI_STRING_KEY("Raise Baseline", "Raise Baseline (Undo action name)", "Undo action name"); case EditActionLowerBaseline: return LPCTSTR_UI_STRING_KEY("Lower Baseline", "Lower Baseline (Undo action name)", "Undo action name"); case EditActionSetTraditionalCharacterShape: return LPCTSTR_UI_STRING_KEY("Set Traditional Character Shape", "Set Traditional Character Shape (Undo action name)", "Undo action name"); case EditActionSetFont: return LPCTSTR_UI_STRING_KEY("Set Font", "Set Font (Undo action name)", "Undo action name"); case EditActionChangeAttributes: return LPCTSTR_UI_STRING_KEY("Change Attributes", "Change Attributes (Undo action name)", "Undo action name"); case EditActionAlignLeft: return LPCTSTR_UI_STRING_KEY("Align Left", "Align Left (Undo action name)", "Undo action name"); case EditActionAlignRight: return LPCTSTR_UI_STRING_KEY("Align Right", "Align Right (Undo action name)", "Undo action name"); case EditActionCenter: return LPCTSTR_UI_STRING_KEY("Center", "Center (Undo action name)", "Undo action name"); case EditActionJustify: return LPCTSTR_UI_STRING_KEY("Justify", "Justify (Undo action name)", "Undo action name"); case EditActionSetWritingDirection: return LPCTSTR_UI_STRING_KEY("Set Writing Direction", "Set Writing Direction (Undo action name)", "Undo action name"); case EditActionSubscript: return LPCTSTR_UI_STRING_KEY("Subscript", "Subscript (Undo action name)", "Undo action name"); case EditActionSuperscript: return LPCTSTR_UI_STRING_KEY("Superscript", "Superscript (Undo action name)", "Undo action name"); case EditActionUnderline: return LPCTSTR_UI_STRING_KEY("Underline", "Underline (Undo action name)", "Undo action name"); case EditActionOutline: return LPCTSTR_UI_STRING_KEY("Outline", "Outline (Undo action name)", "Undo action name"); case EditActionUnscript: return LPCTSTR_UI_STRING_KEY("Unscript", "Unscript (Undo action name)", "Undo action name"); case EditActionDrag: return LPCTSTR_UI_STRING_KEY("Drag", "Drag (Undo action name)", "Undo action name"); case EditActionCut: return LPCTSTR_UI_STRING_KEY("Cut", "Cut (Undo action name)", "Undo action name"); case EditActionPaste: return LPCTSTR_UI_STRING_KEY("Paste", "Paste (Undo action name)", "Undo action name"); case EditActionPasteFont: return LPCTSTR_UI_STRING_KEY("Paste Font", "Paste Font (Undo action name)", "Undo action name"); case EditActionPasteRuler: return LPCTSTR_UI_STRING_KEY("Paste Ruler", "Paste Ruler (Undo action name)", "Undo action name"); case EditActionTyping: return LPCTSTR_UI_STRING_KEY("Typing", "Typing (Undo action name)", "Undo action name"); case EditActionCreateLink: return LPCTSTR_UI_STRING_KEY("Create Link", "Create Link (Undo action name)", "Undo action name"); case EditActionUnlink: return LPCTSTR_UI_STRING_KEY("Unlink", "Unlink (Undo action name)", "Undo action name"); case EditActionInsertList: return LPCTSTR_UI_STRING_KEY("Insert List", "Insert List (Undo action name)", "Undo action name"); case EditActionFormatBlock: return LPCTSTR_UI_STRING_KEY("Formatting", "Format Block (Undo action name)", "Undo action name"); case EditActionIndent: return LPCTSTR_UI_STRING_KEY("Indent", "Indent (Undo action name)", "Undo action name"); case EditActionOutdent: return LPCTSTR_UI_STRING_KEY("Outdent", "Outdent (Undo action name)", "Undo action name"); } return 0; } void WebEditorClient::registerCommandForUndo(PassRefPtr command) { IWebUIDelegate* uiDelegate = 0; if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { LPCTSTR actionName = undoNameForEditAction(command->editingAction()); WebEditorUndoCommand* undoCommand = new WebEditorUndoCommand(command, true); if (!undoCommand) return; uiDelegate->registerUndoWithTarget(m_undoTarget, 0, undoCommand); undoCommand->Release(); // the undo manager owns the reference BSTR actionNameBSTR = SysAllocString(actionName); if (actionNameBSTR) { uiDelegate->setActionTitle(actionNameBSTR); SysFreeString(actionNameBSTR); } uiDelegate->Release(); } } void WebEditorClient::registerCommandForRedo(PassRefPtr command) { IWebUIDelegate* uiDelegate = 0; if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { WebEditorUndoCommand* undoCommand = new WebEditorUndoCommand(command, false); if (!undoCommand) return; uiDelegate->registerUndoWithTarget(m_undoTarget, 0, undoCommand); undoCommand->Release(); // the undo manager owns the reference uiDelegate->Release(); } } void WebEditorClient::clearUndoRedoOperations() { IWebUIDelegate* uiDelegate = 0; if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { uiDelegate->removeAllActionsWithTarget(m_undoTarget); uiDelegate->Release(); } } bool WebEditorClient::canUndo() const { BOOL result = FALSE; IWebUIDelegate* uiDelegate = 0; if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { uiDelegate->canUndo(&result); uiDelegate->Release(); } return !!result; } bool WebEditorClient::canRedo() const { BOOL result = FALSE; IWebUIDelegate* uiDelegate = 0; if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { uiDelegate->canRedo(&result); uiDelegate->Release(); } return !!result; } void WebEditorClient::undo() { IWebUIDelegate* uiDelegate = 0; if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { uiDelegate->undo(); uiDelegate->Release(); } } void WebEditorClient::redo() { IWebUIDelegate* uiDelegate = 0; if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { uiDelegate->redo(); uiDelegate->Release(); } } void WebEditorClient::handleKeypress(KeyboardEvent* evt) { if (m_webView->handleEditingKeyboardEvent(evt)) evt->setDefaultHandled(); } void WebEditorClient::handleInputMethodKeypress(KeyboardEvent* evt) { if (m_webView->inIMEKeyDown()) evt->setDefaultHandled(); } bool WebEditorClient::isEditable() { return false; } void WebEditorClient::ignoreWordInSpellDocument(const String& word) { COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return; initViewSpecificSpelling(m_webView); ed->ignoreWordInSpellDocument(m_webView, BString(word)); } void WebEditorClient::learnWord(const String& word) { COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return; ed->learnWord(BString(word)); } void WebEditorClient::checkSpellingOfString(const UChar* text, int length, int* misspellingLocation, int* misspellingLength) { *misspellingLocation = -1; *misspellingLength = 0; COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return; initViewSpecificSpelling(m_webView); ed->checkSpellingOfString(m_webView, text, length, misspellingLocation, misspellingLength); } void WebEditorClient::checkGrammarOfString(const UChar* text, int length, Vector& details, int* badGrammarLocation, int* badGrammarLength) { details.clear(); *badGrammarLocation = -1; *badGrammarLength = 0; COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return; initViewSpecificSpelling(m_webView); COMPtr enumDetailsObj; if (FAILED(ed->checkGrammarOfString(m_webView, text, length, &enumDetailsObj, badGrammarLocation, badGrammarLength))) return; while (true) { ULONG fetched; COMPtr detailObj; if (enumDetailsObj->Next(1, &detailObj, &fetched) != S_OK) break; GrammarDetail detail; if (FAILED(detailObj->length(&detail.length))) continue; if (FAILED(detailObj->location(&detail.location))) continue; BSTR userDesc; if (FAILED(detailObj->userDescription(&userDesc))) continue; detail.userDescription = String(userDesc, SysStringLen(userDesc)); SysFreeString(userDesc); COMPtr enumGuessesObj; if (FAILED(detailObj->guesses(&enumGuessesObj))) continue; while (true) { BSTR guess; if (enumGuessesObj->Next(1, &guess, &fetched) != S_OK) break; detail.guesses.append(String(guess, SysStringLen(guess))); SysFreeString(guess); } details.append(detail); } } void WebEditorClient::updateSpellingUIWithGrammarString(const String& string, const WebCore::GrammarDetail& detail) { COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return; Vector guessesBSTRs; for (unsigned i = 0; i < detail.guesses.size(); i++) { BString guess(detail.guesses[i]); guessesBSTRs.append(guess.release()); } BString userDescriptionBSTR(detail.userDescription); ed->updateSpellingUIWithGrammarString(BString(string), detail.location, detail.length, userDescriptionBSTR, guessesBSTRs.data(), (int)guessesBSTRs.size()); for (unsigned i = 0; i < guessesBSTRs.size(); i++) SysFreeString(guessesBSTRs[i]); } void WebEditorClient::updateSpellingUIWithMisspelledWord(const String& word) { COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return; ed->updateSpellingUIWithMisspelledWord(BString(word)); } void WebEditorClient::showSpellingUI(bool show) { COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return; ed->showSpellingUI(show); } bool WebEditorClient::spellingUIIsShowing() { COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return false; BOOL showing; if (FAILED(ed->spellingUIIsShowing(&showing))) return false; return !!showing; } void WebEditorClient::getGuessesForWord(const String& word, Vector& guesses) { guesses.clear(); COMPtr ed; if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) return; COMPtr enumGuessesObj; if (FAILED(ed->guessesForWord(BString(word), &enumGuessesObj))) return; while (true) { ULONG fetched; BSTR guess; if (enumGuessesObj->Next(1, &guess, &fetched) != S_OK) break; guesses.append(String(guess, SysStringLen(guess))); SysFreeString(guess); } }