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