1/*
2 * Copyright (C) 2006, 2007, 2011 Apple Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebKitDLL.h"
28#include "WebEditorClient.h"
29
30#include "WebKit.h"
31#include "WebNotification.h"
32#include "WebNotificationCenter.h"
33#include "WebView.h"
34#include "DOMCoreClasses.h"
35#include <WebCore/BString.h>
36#include <WebCore/Document.h>
37#include <WebCore/HTMLElement.h>
38#include <WebCore/HTMLInputElement.h>
39#include <WebCore/HTMLNames.h>
40#include <WebCore/KeyboardEvent.h>
41#include <WebCore/LocalizedStrings.h>
42#include <WebCore/NotImplemented.h>
43#include <WebCore/Page.h>
44#include <WebCore/PlatformKeyboardEvent.h>
45#include <WebCore/Range.h>
46#include <WebCore/Settings.h>
47#include <WebCore/UndoStep.h>
48#include <WebCore/UserTypingGestureIndicator.h>
49#include <WebCore/VisibleSelection.h>
50#include <wtf/text/StringView.h>
51
52using namespace WebCore;
53using namespace HTMLNames;
54
55// {09A11D2B-FAFB-4ca0-A6F7-791EE8932C88}
56static const GUID IID_IWebUndoCommand =
57{ 0x9a11d2b, 0xfafb, 0x4ca0, { 0xa6, 0xf7, 0x79, 0x1e, 0xe8, 0x93, 0x2c, 0x88 } };
58
59class IWebUndoCommand : public IUnknown {
60public:
61    virtual void execute() = 0;
62};
63
64// WebEditorUndoTarget -------------------------------------------------------------
65
66class WebEditorUndoTarget : public IWebUndoTarget
67{
68public:
69    WebEditorUndoTarget();
70
71    // IUnknown
72    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
73    virtual ULONG STDMETHODCALLTYPE AddRef(void);
74    virtual ULONG STDMETHODCALLTYPE Release(void);
75
76    // IWebUndoTarget
77    virtual HRESULT STDMETHODCALLTYPE invoke(
78        /* [in] */ BSTR actionName,
79        /* [in] */ IUnknown *obj);
80
81private:
82    ULONG m_refCount;
83};
84
85WebEditorUndoTarget::WebEditorUndoTarget()
86: m_refCount(1)
87{
88}
89
90HRESULT STDMETHODCALLTYPE WebEditorUndoTarget::QueryInterface(REFIID riid, void** ppvObject)
91{
92    *ppvObject = 0;
93    if (IsEqualGUID(riid, IID_IUnknown))
94        *ppvObject = static_cast<IWebUndoTarget*>(this);
95    else if (IsEqualGUID(riid, IID_IWebUndoTarget))
96        *ppvObject = static_cast<IWebUndoTarget*>(this);
97    else
98        return E_NOINTERFACE;
99
100    AddRef();
101    return S_OK;
102}
103
104ULONG STDMETHODCALLTYPE WebEditorUndoTarget::AddRef(void)
105{
106    return ++m_refCount;
107}
108
109ULONG STDMETHODCALLTYPE WebEditorUndoTarget::Release(void)
110{
111    ULONG newRef = --m_refCount;
112    if (!newRef)
113        delete(this);
114
115    return newRef;
116}
117
118HRESULT STDMETHODCALLTYPE WebEditorUndoTarget::invoke(
119    /* [in] */ BSTR /*actionName*/,
120    /* [in] */ IUnknown *obj)
121{
122    IWebUndoCommand* undoCommand = 0;
123    if (SUCCEEDED(obj->QueryInterface(IID_IWebUndoCommand, (void**)&undoCommand))) {
124        undoCommand->execute();
125        undoCommand->Release();
126    }
127    return S_OK;
128}
129
130// WebEditorClient ------------------------------------------------------------------
131
132WebEditorClient::WebEditorClient(WebView* webView)
133    : m_webView(webView)
134    , m_undoTarget(0)
135{
136    m_undoTarget = new WebEditorUndoTarget();
137}
138
139WebEditorClient::~WebEditorClient()
140{
141    if (m_undoTarget)
142        m_undoTarget->Release();
143}
144
145void WebEditorClient::pageDestroyed()
146{
147    delete this;
148}
149
150bool WebEditorClient::isContinuousSpellCheckingEnabled()
151{
152    BOOL enabled;
153    if (FAILED(m_webView->isContinuousSpellCheckingEnabled(&enabled)))
154        return false;
155    return !!enabled;
156}
157
158void WebEditorClient::toggleContinuousSpellChecking()
159{
160    m_webView->toggleContinuousSpellChecking(0);
161}
162
163bool WebEditorClient::isGrammarCheckingEnabled()
164{
165    BOOL enabled;
166    if (FAILED(m_webView->isGrammarCheckingEnabled(&enabled)))
167        return false;
168    return !!enabled;
169}
170
171void WebEditorClient::toggleGrammarChecking()
172{
173    m_webView->toggleGrammarChecking(0);
174}
175
176static void initViewSpecificSpelling(IWebViewEditing* viewEditing)
177{
178    // we just use this as a flag to indicate that we've spell checked the document
179    // and need to close the spell checker out when the view closes.
180    int tag;
181    viewEditing->spellCheckerDocumentTag(&tag);
182}
183
184int WebEditorClient::spellCheckerDocumentTag()
185{
186    // we don't use the concept of spelling tags
187    notImplemented();
188    ASSERT_NOT_REACHED();
189    return 0;
190}
191
192bool WebEditorClient::shouldBeginEditing(Range*)
193{
194    notImplemented();
195    return true;
196}
197
198bool WebEditorClient::shouldEndEditing(Range*)
199{
200    notImplemented();
201    return true;
202}
203
204void WebEditorClient::didBeginEditing()
205{
206    notImplemented();
207}
208
209void WebEditorClient::respondToChangedContents()
210{
211    notImplemented();
212}
213
214void WebEditorClient::respondToChangedSelection(Frame*)
215{
216    m_webView->selectionChanged();
217
218    static BSTR webViewDidChangeSelectionNotificationName = SysAllocString(WebViewDidChangeSelectionNotification);
219    IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
220    notifyCenter->postNotificationName(webViewDidChangeSelectionNotificationName, static_cast<IWebView*>(m_webView), 0);
221}
222
223void WebEditorClient::didEndEditing()
224{
225    notImplemented();
226}
227
228void WebEditorClient::didWriteSelectionToPasteboard()
229{
230    notImplemented();
231}
232
233void WebEditorClient::willWriteSelectionToPasteboard(WebCore::Range*)
234{
235    notImplemented();
236}
237
238void WebEditorClient::getClientPasteboardDataForRange(WebCore::Range*, Vector<String>&, Vector<RefPtr<WebCore::SharedBuffer> >&)
239{
240    notImplemented();
241}
242
243bool WebEditorClient::shouldDeleteRange(Range* /*range*/)
244{
245    notImplemented();
246    return true;
247
248    // FIXME: calling m_webView->editingDelegate() will cause an assertion failure so we don't want to enable this code until that's implemented.
249    //BOOL result = false;
250    //IWebViewEditingDelegate* editingDelegate;
251    //// FIXME: DOMRange needs to be implemented before anything meaningful can be done here
252    //IDOMRange* domRange(0);
253    //if (SUCCEEDED(m_webView->editingDelegate(&editingDelegate))) {
254    //    editingDelegate->shouldDeleteDOMRange(m_webView, domRange, &result);
255    //    editingDelegate->Release();
256    //}
257    //return !!result;
258}
259
260bool WebEditorClient::shouldInsertNode(Node* /*node*/, Range* /*replacingRange*/, EditorInsertAction /*givenAction*/)
261{
262    notImplemented();
263    return true;
264}
265
266bool WebEditorClient::shouldInsertText(const String& /*str*/, Range* /* replacingRange */, EditorInsertAction /*givenAction*/)
267{
268    notImplemented();
269    return true;
270
271    // FIXME: calling m_webView->editingDelegate() will cause an assertion failure so we don't want to enable this code until that's implemented.
272    //BOOL result = false;
273    //IWebViewEditingDelegate* editingDelegate;
274    //// FIXME: DOMRange needs to be implemented before anything meaningful can be done here
275    //IDOMRange* domRange(0); // make a DOMRange from replacingRange
276    //BString text(str);
277    //if (SUCCEEDED(m_webView->editingDelegate(&editingDelegate))) {
278    //    editingDelegate->shouldInsertText(m_webView, text, domRange, (WebViewInsertAction) givenAction, &result);
279    //    editingDelegate->Release();
280    //}
281    //return !!result;
282}
283
284//bool WebEditorClient::shouldChangeSelectedRange(Range *currentRange, Range *toProposedRange, SelectionAffinity selectionAffinity, bool stillSelecting)
285//{ notImplemented(); return false; }
286
287bool WebEditorClient::shouldApplyStyle(StyleProperties* /*style*/, Range* /*toElementsInDOMRange*/)
288{ notImplemented(); return true; }
289
290bool WebEditorClient::shouldMoveRangeAfterDelete(Range* /*range*/, Range* /*rangeToBeReplaced*/)
291{ notImplemented(); return true; }
292
293bool WebEditorClient::shouldChangeTypingStyle(StyleProperties* /*currentStyle*/, StyleProperties* /*toProposedStyle*/)
294{ notImplemented(); return false; }
295
296void WebEditorClient::webViewDidChangeTypingStyle(WebNotification* /*notification*/)
297{  notImplemented(); }
298
299void WebEditorClient::webViewDidChangeSelection(WebNotification* /*notification*/)
300{  notImplemented(); }
301
302bool WebEditorClient::smartInsertDeleteEnabled(void)
303{
304    Page* page = m_webView->page();
305    if (!page)
306        return false;
307    return page->settings().smartInsertDeleteEnabled();
308}
309
310bool WebEditorClient::isSelectTrailingWhitespaceEnabled(void)
311{
312    Page* page = m_webView->page();
313    if (!page)
314        return false;
315    return page->settings().selectTrailingWhitespaceEnabled();
316}
317
318bool WebEditorClient::shouldChangeSelectedRange(WebCore::Range*, WebCore::Range*, WebCore::EAffinity, bool)
319{ notImplemented(); return true; }
320
321void WebEditorClient::textFieldDidBeginEditing(Element* e)
322{
323    IWebFormDelegate* formDelegate;
324    if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) {
325        IDOMElement* domElement = DOMElement::createInstance(e);
326        if (domElement) {
327            IDOMHTMLInputElement* domInputElement;
328            if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) {
329                formDelegate->textFieldDidBeginEditing(domInputElement, kit(e->document().frame()));
330                domInputElement->Release();
331            }
332            domElement->Release();
333        }
334        formDelegate->Release();
335    }
336}
337
338void WebEditorClient::textFieldDidEndEditing(Element* e)
339{
340    IWebFormDelegate* formDelegate;
341    if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) {
342        IDOMElement* domElement = DOMElement::createInstance(e);
343        if (domElement) {
344            IDOMHTMLInputElement* domInputElement;
345            if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) {
346                formDelegate->textFieldDidEndEditing(domInputElement, kit(e->document().frame()));
347                domInputElement->Release();
348            }
349            domElement->Release();
350        }
351        formDelegate->Release();
352    }
353}
354
355void WebEditorClient::textDidChangeInTextField(Element* e)
356{
357    if (!UserTypingGestureIndicator::processingUserTypingGesture() || UserTypingGestureIndicator::focusedElementAtGestureStart() != e)
358        return;
359
360    IWebFormDelegate* formDelegate;
361    if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) {
362        IDOMElement* domElement = DOMElement::createInstance(e);
363        if (domElement) {
364            IDOMHTMLInputElement* domInputElement;
365            if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) {
366                formDelegate->textDidChangeInTextField(domInputElement, kit(e->document().frame()));
367                domInputElement->Release();
368            }
369            domElement->Release();
370        }
371        formDelegate->Release();
372    }
373}
374
375bool WebEditorClient::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
376{
377    BOOL result = FALSE;
378    IWebFormDelegate* formDelegate;
379    if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) {
380        IDOMElement* domElement = DOMElement::createInstance(e);
381        if (domElement) {
382            IDOMHTMLInputElement* domInputElement;
383            if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) {
384                String command = m_webView->interpretKeyEvent(ke);
385                // We allow empty commands here because the app code actually depends on this being called for all key presses.
386                // We may want to revisit this later because it doesn't really make sense to send an empty command.
387                formDelegate->doPlatformCommand(domInputElement, BString(command), kit(e->document().frame()), &result);
388                domInputElement->Release();
389            }
390            domElement->Release();
391        }
392        formDelegate->Release();
393    }
394    return !!result;
395}
396
397void WebEditorClient::textWillBeDeletedInTextField(Element* e)
398{
399    // We're using the deleteBackward command for all deletion operations since the autofill code treats all deletions the same way.
400    IWebFormDelegate* formDelegate;
401    if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) {
402        IDOMElement* domElement = DOMElement::createInstance(e);
403        if (domElement) {
404            IDOMHTMLInputElement* domInputElement;
405            if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) {
406                BOOL result;
407                formDelegate->doPlatformCommand(domInputElement, BString(L"DeleteBackward"), kit(e->document().frame()), &result);
408                domInputElement->Release();
409            }
410            domElement->Release();
411        }
412        formDelegate->Release();
413    }
414}
415
416void WebEditorClient::textDidChangeInTextArea(Element* e)
417{
418    IWebFormDelegate* formDelegate;
419    if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) {
420        IDOMElement* domElement = DOMElement::createInstance(e);
421        if (domElement) {
422            IDOMHTMLTextAreaElement* domTextAreaElement;
423            if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLTextAreaElement, (void**)&domTextAreaElement))) {
424                formDelegate->textDidChangeInTextArea(domTextAreaElement, kit(e->document().frame()));
425                domTextAreaElement->Release();
426            }
427            domElement->Release();
428        }
429        formDelegate->Release();
430    }
431}
432
433class WebEditorUndoCommand : public IWebUndoCommand
434{
435public:
436    WebEditorUndoCommand(PassRefPtr<UndoStep>, bool isUndo);
437    void execute();
438
439    // IUnknown
440    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
441    virtual ULONG STDMETHODCALLTYPE AddRef(void);
442    virtual ULONG STDMETHODCALLTYPE Release(void);
443
444private:
445    ULONG m_refCount;
446    RefPtr<UndoStep> m_step;
447    bool m_isUndo;
448};
449
450WebEditorUndoCommand::WebEditorUndoCommand(PassRefPtr<UndoStep> step, bool isUndo)
451    : m_step(step)
452    , m_isUndo(isUndo)
453    , m_refCount(1)
454{
455}
456
457void WebEditorUndoCommand::execute()
458{
459    if (m_isUndo)
460        m_step->unapply();
461    else
462        m_step->reapply();
463}
464
465HRESULT STDMETHODCALLTYPE WebEditorUndoCommand::QueryInterface(REFIID riid, void** ppvObject)
466{
467    *ppvObject = 0;
468    if (IsEqualGUID(riid, IID_IUnknown))
469        *ppvObject = static_cast<IWebUndoCommand*>(this);
470    else if (IsEqualGUID(riid, IID_IWebUndoCommand))
471        *ppvObject = static_cast<IWebUndoCommand*>(this);
472    else
473        return E_NOINTERFACE;
474
475    AddRef();
476    return S_OK;
477}
478
479ULONG STDMETHODCALLTYPE WebEditorUndoCommand::AddRef(void)
480{
481    return ++m_refCount;
482}
483
484ULONG STDMETHODCALLTYPE WebEditorUndoCommand::Release(void)
485{
486    ULONG newRef = --m_refCount;
487    if (!newRef)
488        delete(this);
489
490    return newRef;
491}
492
493static String undoNameForEditAction(EditAction editAction)
494{
495    switch (editAction) {
496    case EditActionUnspecified: return String();
497    case EditActionSetColor: return WEB_UI_STRING_KEY("Set Color", "Set Color (Undo action name)", "Undo action name");
498    case EditActionSetBackgroundColor: return WEB_UI_STRING_KEY("Set Background Color", "Set Background Color (Undo action name)", "Undo action name");
499    case EditActionTurnOffKerning: return WEB_UI_STRING_KEY("Turn Off Kerning", "Turn Off Kerning (Undo action name)", "Undo action name");
500    case EditActionTightenKerning: return WEB_UI_STRING_KEY("Tighten Kerning", "Tighten Kerning (Undo action name)", "Undo action name");
501    case EditActionLoosenKerning: return WEB_UI_STRING_KEY("Loosen Kerning", "Loosen Kerning (Undo action name)", "Undo action name");
502    case EditActionUseStandardKerning: return WEB_UI_STRING_KEY("Use Standard Kerning", "Use Standard Kerning (Undo action name)", "Undo action name");
503    case EditActionTurnOffLigatures: return WEB_UI_STRING_KEY("Turn Off Ligatures", "Turn Off Ligatures (Undo action name)", "Undo action name");
504    case EditActionUseStandardLigatures: return WEB_UI_STRING_KEY("Use Standard Ligatures", "Use Standard Ligatures (Undo action name)", "Undo action name");
505    case EditActionUseAllLigatures: return WEB_UI_STRING_KEY("Use All Ligatures", "Use All Ligatures (Undo action name)", "Undo action name");
506    case EditActionRaiseBaseline: return WEB_UI_STRING_KEY("Raise Baseline", "Raise Baseline (Undo action name)", "Undo action name");
507    case EditActionLowerBaseline: return WEB_UI_STRING_KEY("Lower Baseline", "Lower Baseline (Undo action name)", "Undo action name");
508    case EditActionSetTraditionalCharacterShape: return WEB_UI_STRING_KEY("Set Traditional Character Shape", "Set Traditional Character Shape (Undo action name)", "Undo action name");
509    case EditActionSetFont: return WEB_UI_STRING_KEY("Set Font", "Set Font (Undo action name)", "Undo action name");
510    case EditActionChangeAttributes: return WEB_UI_STRING_KEY("Change Attributes", "Change Attributes (Undo action name)", "Undo action name");
511    case EditActionAlignLeft: return WEB_UI_STRING_KEY("Align Left", "Align Left (Undo action name)", "Undo action name");
512    case EditActionAlignRight: return WEB_UI_STRING_KEY("Align Right", "Align Right (Undo action name)", "Undo action name");
513    case EditActionCenter: return WEB_UI_STRING_KEY("Center", "Center (Undo action name)", "Undo action name");
514    case EditActionJustify: return WEB_UI_STRING_KEY("Justify", "Justify (Undo action name)", "Undo action name");
515    case EditActionSetWritingDirection: return WEB_UI_STRING_KEY("Set Writing Direction", "Set Writing Direction (Undo action name)", "Undo action name");
516    case EditActionSubscript: return WEB_UI_STRING_KEY("Subscript", "Subscript (Undo action name)", "Undo action name");
517    case EditActionSuperscript: return WEB_UI_STRING_KEY("Superscript", "Superscript (Undo action name)", "Undo action name");
518    case EditActionBold: return WEB_UI_STRING_KEY("Bold", "Bold (Undo action name)", "Undo action name");
519    case EditActionItalics: return WEB_UI_STRING_KEY("Italics", "Italics (Undo action name)", "Undo action name");
520    case EditActionUnderline: return WEB_UI_STRING_KEY("Underline", "Underline (Undo action name)", "Undo action name");
521    case EditActionOutline: return WEB_UI_STRING_KEY("Outline", "Outline (Undo action name)", "Undo action name");
522    case EditActionUnscript: return WEB_UI_STRING_KEY("Unscript", "Unscript (Undo action name)", "Undo action name");
523    case EditActionDrag: return WEB_UI_STRING_KEY("Drag", "Drag (Undo action name)", "Undo action name");
524    case EditActionCut: return WEB_UI_STRING_KEY("Cut", "Cut (Undo action name)", "Undo action name");
525    case EditActionPaste: return WEB_UI_STRING_KEY("Paste", "Paste (Undo action name)", "Undo action name");
526    case EditActionPasteFont: return WEB_UI_STRING_KEY("Paste Font", "Paste Font (Undo action name)", "Undo action name");
527    case EditActionPasteRuler: return WEB_UI_STRING_KEY("Paste Ruler", "Paste Ruler (Undo action name)", "Undo action name");
528    case EditActionTyping: return WEB_UI_STRING_KEY("Typing", "Typing (Undo action name)", "Undo action name");
529    case EditActionCreateLink: return WEB_UI_STRING_KEY("Create Link", "Create Link (Undo action name)", "Undo action name");
530    case EditActionUnlink: return WEB_UI_STRING_KEY("Unlink", "Unlink (Undo action name)", "Undo action name");
531    case EditActionInsertList: return WEB_UI_STRING_KEY("Insert List", "Insert List (Undo action name)", "Undo action name");
532    case EditActionFormatBlock: return WEB_UI_STRING_KEY("Formatting", "Format Block (Undo action name)", "Undo action name");
533    case EditActionIndent: return WEB_UI_STRING_KEY("Indent", "Indent (Undo action name)", "Undo action name");
534    case EditActionOutdent: return WEB_UI_STRING_KEY("Outdent", "Outdent (Undo action name)", "Undo action name");
535    }
536    return String();
537}
538
539void WebEditorClient::registerUndoStep(PassRefPtr<UndoStep> step)
540{
541    IWebUIDelegate* uiDelegate = 0;
542    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
543        String actionName = undoNameForEditAction(step->editingAction());
544        WebEditorUndoCommand* undoCommand = new WebEditorUndoCommand(step, true);
545        if (!undoCommand)
546            return;
547        uiDelegate->registerUndoWithTarget(m_undoTarget, 0, undoCommand);
548        undoCommand->Release(); // the undo manager owns the reference
549        if (!actionName.isEmpty())
550            uiDelegate->setActionTitle(BString(actionName));
551        uiDelegate->Release();
552    }
553}
554
555void WebEditorClient::registerRedoStep(PassRefPtr<UndoStep> step)
556{
557    IWebUIDelegate* uiDelegate = 0;
558    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
559        WebEditorUndoCommand* undoCommand = new WebEditorUndoCommand(step, false);
560        if (!undoCommand)
561            return;
562        uiDelegate->registerUndoWithTarget(m_undoTarget, 0, undoCommand);
563        undoCommand->Release(); // the undo manager owns the reference
564        uiDelegate->Release();
565    }
566}
567
568void WebEditorClient::clearUndoRedoOperations()
569{
570    IWebUIDelegate* uiDelegate = 0;
571    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
572        uiDelegate->removeAllActionsWithTarget(m_undoTarget);
573        uiDelegate->Release();
574    }
575}
576
577bool WebEditorClient::canCopyCut(Frame*, bool defaultValue) const
578{
579    return defaultValue;
580}
581
582bool WebEditorClient::canPaste(Frame*, bool defaultValue) const
583{
584    return defaultValue;
585}
586
587bool WebEditorClient::canUndo() const
588{
589    BOOL result = FALSE;
590    IWebUIDelegate* uiDelegate = 0;
591    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
592        uiDelegate->canUndo(&result);
593        uiDelegate->Release();
594    }
595    return !!result;
596}
597
598bool WebEditorClient::canRedo() const
599{
600    BOOL result = FALSE;
601    IWebUIDelegate* uiDelegate = 0;
602    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
603        uiDelegate->canRedo(&result);
604        uiDelegate->Release();
605    }
606    return !!result;
607}
608
609void WebEditorClient::undo()
610{
611    IWebUIDelegate* uiDelegate = 0;
612    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
613        uiDelegate->undo();
614        uiDelegate->Release();
615    }
616}
617
618void WebEditorClient::redo()
619{
620    IWebUIDelegate* uiDelegate = 0;
621    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
622        uiDelegate->redo();
623        uiDelegate->Release();
624    }
625}
626
627void WebEditorClient::handleKeyboardEvent(KeyboardEvent* evt)
628{
629    if (m_webView->handleEditingKeyboardEvent(evt))
630        evt->setDefaultHandled();
631}
632
633void WebEditorClient::handleInputMethodKeydown(KeyboardEvent* )
634{
635}
636
637bool WebEditorClient::shouldEraseMarkersAfterChangeSelection(TextCheckingType) const
638{
639    return true;
640}
641
642void WebEditorClient::ignoreWordInSpellDocument(const String& word)
643{
644    COMPtr<IWebEditingDelegate> ed;
645    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
646        return;
647
648    initViewSpecificSpelling(m_webView);
649    ed->ignoreWordInSpellDocument(m_webView, BString(word));
650}
651
652void WebEditorClient::learnWord(const String& word)
653{
654    COMPtr<IWebEditingDelegate> ed;
655    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
656        return;
657
658    ed->learnWord(BString(word));
659}
660
661void WebEditorClient::checkSpellingOfString(StringView text, int* misspellingLocation, int* misspellingLength)
662{
663    *misspellingLocation = -1;
664    *misspellingLength = 0;
665
666    COMPtr<IWebEditingDelegate> ed;
667    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
668        return;
669
670    initViewSpecificSpelling(m_webView);
671    ed->checkSpellingOfString(m_webView, text.upconvertedCharacters(), text.length(), misspellingLocation, misspellingLength);
672}
673
674String WebEditorClient::getAutoCorrectSuggestionForMisspelledWord(const String& inputWord)
675{
676    // This method can be implemented using customized algorithms for the particular browser.
677    // Currently, it computes an empty string.
678    return String();
679}
680
681void WebEditorClient::checkGrammarOfString(StringView text, Vector<GrammarDetail>& details, int* badGrammarLocation, int* badGrammarLength)
682{
683    details.clear();
684    *badGrammarLocation = -1;
685    *badGrammarLength = 0;
686
687    COMPtr<IWebEditingDelegate> ed;
688    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
689        return;
690
691    initViewSpecificSpelling(m_webView);
692    COMPtr<IEnumWebGrammarDetails> enumDetailsObj;
693    if (FAILED(ed->checkGrammarOfString(m_webView, text.upconvertedCharacters(), text.length(), &enumDetailsObj, badGrammarLocation, badGrammarLength)))
694        return;
695
696    while (true) {
697        ULONG fetched;
698        COMPtr<IWebGrammarDetail> detailObj;
699        if (enumDetailsObj->Next(1, &detailObj, &fetched) != S_OK)
700            break;
701
702        GrammarDetail detail;
703        if (FAILED(detailObj->length(&detail.length)))
704            continue;
705        if (FAILED(detailObj->location(&detail.location)))
706            continue;
707        BString userDesc;
708        if (FAILED(detailObj->userDescription(&userDesc)))
709            continue;
710        detail.userDescription = String(userDesc, SysStringLen(userDesc));
711
712        COMPtr<IEnumSpellingGuesses> enumGuessesObj;
713        if (FAILED(detailObj->guesses(&enumGuessesObj)))
714            continue;
715        while (true) {
716            BString guess;
717            if (enumGuessesObj->Next(1, &guess, &fetched) != S_OK)
718                break;
719            detail.guesses.append(String(guess, SysStringLen(guess)));
720        }
721
722        details.append(detail);
723    }
724}
725
726void WebEditorClient::updateSpellingUIWithGrammarString(const String& string, const WebCore::GrammarDetail& detail)
727{
728    COMPtr<IWebEditingDelegate> ed;
729    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
730        return;
731
732    Vector<BSTR> guessesBSTRs;
733    for (unsigned i = 0; i < detail.guesses.size(); i++) {
734        BString guess(detail.guesses[i]);
735        guessesBSTRs.append(guess.release());
736    }
737    BString userDescriptionBSTR(detail.userDescription);
738    ed->updateSpellingUIWithGrammarString(BString(string), detail.location, detail.length, userDescriptionBSTR, guessesBSTRs.data(), (int)guessesBSTRs.size());
739    for (unsigned i = 0; i < guessesBSTRs.size(); i++)
740        SysFreeString(guessesBSTRs[i]);
741}
742
743void WebEditorClient::updateSpellingUIWithMisspelledWord(const String& word)
744{
745    COMPtr<IWebEditingDelegate> ed;
746    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
747        return;
748
749    ed->updateSpellingUIWithMisspelledWord(BString(word));
750}
751
752void WebEditorClient::showSpellingUI(bool show)
753{
754    COMPtr<IWebEditingDelegate> ed;
755    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
756        return;
757
758    ed->showSpellingUI(show);
759}
760
761bool WebEditorClient::spellingUIIsShowing()
762{
763    COMPtr<IWebEditingDelegate> ed;
764    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
765        return false;
766
767    BOOL showing;
768    if (FAILED(ed->spellingUIIsShowing(&showing)))
769        return false;
770
771    return !!showing;
772}
773
774void WebEditorClient::getGuessesForWord(const String& word, const String& context, Vector<String>& guesses)
775{
776    guesses.clear();
777
778    COMPtr<IWebEditingDelegate> ed;
779    if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get())
780        return;
781
782    COMPtr<IEnumSpellingGuesses> enumGuessesObj;
783    if (FAILED(ed->guessesForWord(BString(word), &enumGuessesObj)))
784        return;
785
786    while (true) {
787        ULONG fetched;
788        BString guess;
789        if (enumGuessesObj->Next(1, &guess, &fetched) != S_OK)
790            break;
791        guesses.append(String(guess, SysStringLen(guess)));
792    }
793}
794
795void WebEditorClient::willSetInputMethodState()
796{
797}
798
799void WebEditorClient::setInputMethodState(bool enabled)
800{
801    m_webView->setInputMethodState(enabled);
802}
803