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