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