Hook up request and show for typing candidates in WK1
[WebKit-https.git] / Source / WebKit / mac / WebCoreSupport / WebEditorClient.mm
1 /*
2  * Copyright (C) 2006 Apple Inc.  All rights reserved.
3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #import "WebEditorClient.h"
31
32 #import "DOMCSSStyleDeclarationInternal.h"
33 #import "DOMDocumentFragmentInternal.h"
34 #import "DOMHTMLElementInternal.h"
35 #import "DOMHTMLInputElementInternal.h"
36 #import "DOMHTMLTextAreaElementInternal.h"
37 #import "DOMNodeInternal.h"
38 #import "DOMRangeInternal.h"
39 #import "WebArchive.h"
40 #import "WebDataSourceInternal.h"
41 #import "WebDelegateImplementationCaching.h"
42 #import "WebDocument.h"
43 #import "WebEditingDelegatePrivate.h"
44 #import "WebFormDelegate.h"
45 #import "WebFrameInternal.h"
46 #import "WebHTMLView.h"
47 #import "WebHTMLViewInternal.h"
48 #import "WebKitLogging.h"
49 #import "WebKitVersionChecks.h"
50 #import "WebLocalizableStringsInternal.h"
51 #import "WebNSURLExtras.h"
52 #import "WebResourceInternal.h"
53 #import "WebViewInternal.h"
54 #import <WebCore/ArchiveResource.h>
55 #import <WebCore/Document.h>
56 #import <WebCore/DocumentFragment.h>
57 #import <WebCore/Editor.h>
58 #import <WebCore/FloatQuad.h>
59 #import <WebCore/Frame.h>
60 #import <WebCore/FrameView.h>
61 #import <WebCore/HTMLInputElement.h>
62 #import <WebCore/HTMLNames.h>
63 #import <WebCore/HTMLTextAreaElement.h>
64 #import <WebCore/KeyboardEvent.h>
65 #import <WebCore/LegacyWebArchive.h>
66 #import <WebCore/NSSpellCheckerSPI.h>
67 #import <WebCore/Page.h>
68 #import <WebCore/PlatformKeyboardEvent.h>
69 #import <WebCore/Settings.h>
70 #import <WebCore/SpellChecker.h>
71 #import <WebCore/StyleProperties.h>
72 #import <WebCore/TextIterator.h>
73 #import <WebCore/UndoStep.h>
74 #import <WebCore/UserTypingGestureIndicator.h>
75 #import <WebCore/VisibleUnits.h>
76 #import <WebCore/WebCoreObjCExtras.h>
77 #import <runtime/InitializeThreading.h>
78 #import <wtf/MainThread.h>
79 #import <wtf/PassRefPtr.h>
80 #import <wtf/RunLoop.h>
81 #import <wtf/text/WTFString.h>
82
83 #if PLATFORM(IOS)
84 #import <WebCore/WebCoreThreadMessage.h>
85 #import "DOMElementInternal.h"
86 #import "WebFrameView.h"
87 #import "WebUIKitDelegate.h"
88 #endif
89
90 using namespace WebCore;
91
92 using namespace HTMLNames;
93
94 #if !PLATFORM(IOS)
95 @interface NSSpellChecker (WebNSSpellCheckerDetails)
96 - (NSString *)languageForWordRange:(NSRange)range inString:(NSString *)string orthography:(NSOrthography *)orthography;
97 @end
98 #endif
99
100 @interface NSAttributedString (WebNSAttributedStringDetails)
101 - (id)_initWithDOMRange:(DOMRange*)range;
102 - (DOMDocumentFragment*)_documentFromRange:(NSRange)range document:(DOMDocument*)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
103 @end
104
105 static WebViewInsertAction kit(EditorInsertAction coreAction)
106 {
107     return static_cast<WebViewInsertAction>(coreAction);
108 }
109
110 @interface WebUndoStep : NSObject
111 {
112     RefPtr<UndoStep> m_step;   
113 }
114
115 + (WebUndoStep *)stepWithUndoStep:(PassRefPtr<UndoStep>)step;
116 - (UndoStep *)step;
117
118 @end
119
120 @implementation WebUndoStep
121
122 + (void)initialize
123 {
124 #if !PLATFORM(IOS)
125     JSC::initializeThreading();
126     WTF::initializeMainThreadToProcessMainThread();
127     RunLoop::initializeMainRunLoop();
128 #endif
129 }
130
131 - (id)initWithUndoStep:(PassRefPtr<UndoStep>)step
132 {
133     ASSERT(step);
134     self = [super init];
135     if (!self)
136         return nil;
137     m_step = step;
138     return self;
139 }
140
141 - (void)dealloc
142 {
143     if (WebCoreObjCScheduleDeallocateOnMainThread([WebUndoStep class], self))
144         return;
145
146     [super dealloc];
147 }
148
149 + (WebUndoStep *)stepWithUndoStep:(PassRefPtr<UndoStep>)step
150 {
151     return [[[WebUndoStep alloc] initWithUndoStep:step] autorelease];
152 }
153
154 - (UndoStep *)step
155 {
156     return m_step.get();
157 }
158
159 @end
160
161 @interface WebEditorUndoTarget : NSObject
162 {
163 }
164
165 - (void)undoEditing:(id)arg;
166 - (void)redoEditing:(id)arg;
167
168 @end
169
170 @implementation WebEditorUndoTarget
171
172 - (void)undoEditing:(id)arg
173 {
174     ASSERT([arg isKindOfClass:[WebUndoStep class]]);
175     [arg step]->unapply();
176 }
177
178 - (void)redoEditing:(id)arg
179 {
180     ASSERT([arg isKindOfClass:[WebUndoStep class]]);
181     [arg step]->reapply();
182 }
183
184 @end
185
186 void WebEditorClient::pageDestroyed()
187 {
188     delete this;
189 }
190
191 WebEditorClient::WebEditorClient(WebView *webView)
192     : m_webView(webView)
193     , m_undoTarget(adoptNS([[WebEditorUndoTarget alloc] init]))
194     , m_haveUndoRedoOperations(false)
195 #if PLATFORM(IOS)
196     , m_delayingContentChangeNotifications(0)
197     , m_hasDelayedContentChangeNotification(0)
198 #endif
199     , m_weakPtrFactory(this)
200 {
201 }
202
203 WebEditorClient::~WebEditorClient()
204 {
205 }
206
207 bool WebEditorClient::isContinuousSpellCheckingEnabled()
208 {
209     return [m_webView isContinuousSpellCheckingEnabled];
210 }
211
212 #if !PLATFORM(IOS)
213 void WebEditorClient::toggleContinuousSpellChecking()
214 {
215     [m_webView toggleContinuousSpellChecking:nil];
216 }
217
218 bool WebEditorClient::isGrammarCheckingEnabled()
219 {
220     return [m_webView isGrammarCheckingEnabled];
221 }
222
223 void WebEditorClient::toggleGrammarChecking()
224 {
225     [m_webView toggleGrammarChecking:nil];
226 }
227
228 int WebEditorClient::spellCheckerDocumentTag()
229 {
230     return [m_webView spellCheckerDocumentTag];
231 }
232 #endif
233
234 bool WebEditorClient::shouldDeleteRange(Range* range)
235 {
236     return [[m_webView _editingDelegateForwarder] webView:m_webView
237         shouldDeleteDOMRange:kit(range)];
238 }
239
240 bool WebEditorClient::smartInsertDeleteEnabled()
241 {
242     Page* page = [m_webView page];
243     if (!page)
244         return false;
245     return page->settings().smartInsertDeleteEnabled();
246 }
247
248 bool WebEditorClient::isSelectTrailingWhitespaceEnabled()
249 {
250     Page* page = [m_webView page];
251     if (!page)
252         return false;
253     return page->settings().selectTrailingWhitespaceEnabled();
254 }
255
256 bool WebEditorClient::shouldApplyStyle(StyleProperties* style, Range* range)
257 {
258     Ref<MutableStyleProperties> mutableStyle(style->isMutable() ? Ref<MutableStyleProperties>(static_cast<MutableStyleProperties&>(*style)) : style->mutableCopy());
259     return [[m_webView _editingDelegateForwarder] webView:m_webView
260         shouldApplyStyle:kit(mutableStyle->ensureCSSStyleDeclaration()) toElementsInDOMRange:kit(range)];
261 }
262
263 static void updateFontPanel(WebView *webView)
264 {
265 #if !PLATFORM(IOS)
266     NSView <WebDocumentView> *view = [[[webView selectedFrame] frameView] documentView];
267     if ([view isKindOfClass:[WebHTMLView class]])
268         [(WebHTMLView *)view _updateFontPanel];
269 #else
270     UNUSED_PARAM(webView);
271 #endif
272 }
273
274 void WebEditorClient::didApplyStyle()
275 {
276     updateFontPanel(m_webView);
277 }
278
279 bool WebEditorClient::shouldMoveRangeAfterDelete(Range* range, Range* rangeToBeReplaced)
280 {
281     return [[m_webView _editingDelegateForwarder] webView:m_webView
282         shouldMoveRangeAfterDelete:kit(range) replacingRange:kit(rangeToBeReplaced)];
283 }
284
285 bool WebEditorClient::shouldBeginEditing(Range* range)
286 {
287     return [[m_webView _editingDelegateForwarder] webView:m_webView
288         shouldBeginEditingInDOMRange:kit(range)];
289
290     return false;
291 }
292
293 bool WebEditorClient::shouldEndEditing(Range* range)
294 {
295     return [[m_webView _editingDelegateForwarder] webView:m_webView
296                              shouldEndEditingInDOMRange:kit(range)];
297 }
298
299 bool WebEditorClient::shouldInsertText(const String& text, Range* range, EditorInsertAction action)
300 {
301     WebView* webView = m_webView;
302     return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:kit(range) givenAction:kit(action)];
303 }
304
305 bool WebEditorClient::shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity selectionAffinity, bool stillSelecting)
306 {
307     return [m_webView _shouldChangeSelectedDOMRange:kit(fromRange) toDOMRange:kit(toRange) affinity:kit(selectionAffinity) stillSelecting:stillSelecting];
308 }
309
310 void WebEditorClient::didBeginEditing()
311 {
312 #if !PLATFORM(IOS)
313     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidBeginEditingNotification object:m_webView];
314 #else
315     WebThreadPostNotification(WebViewDidBeginEditingNotification, m_webView, nil);
316 #endif
317 }
318
319 #if PLATFORM(IOS)
320 void WebEditorClient::startDelayingAndCoalescingContentChangeNotifications()
321 {
322     m_delayingContentChangeNotifications = true;
323 }
324
325 void WebEditorClient::stopDelayingAndCoalescingContentChangeNotifications()
326 {
327     m_delayingContentChangeNotifications = false;
328     
329     if (m_hasDelayedContentChangeNotification)
330         this->respondToChangedContents();
331     
332     m_hasDelayedContentChangeNotification = false;
333 }
334 #endif
335
336 void WebEditorClient::respondToChangedContents()
337 {
338     updateFontPanel(m_webView);
339 #if !PLATFORM(IOS)
340     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidChangeNotification object:m_webView];    
341 #else
342     if (m_delayingContentChangeNotifications) {
343         m_hasDelayedContentChangeNotification = true;
344     } else {
345         WebThreadPostNotification(WebViewDidChangeNotification, m_webView, nil);
346     }
347 #endif
348 }
349
350 void WebEditorClient::respondToChangedSelection(Frame* frame)
351 {
352     NSView<WebDocumentView> *documentView = [[kit(frame) frameView] documentView];
353     if ([documentView isKindOfClass:[WebHTMLView class]])
354         [(WebHTMLView *)documentView _selectionChanged];
355
356 #if !PLATFORM(IOS)
357     // FIXME: This quirk is needed due to <rdar://problem/5009625> - We can phase it out once Aperture can adopt the new behavior on their end
358     if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_APERTURE_QUIRK) && [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Aperture"])
359         return;
360
361     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidChangeSelectionNotification object:m_webView];
362 #else
363     // Selection can be changed while deallocating down the WebView / Frame / Editor.  Do not post in that case because it's already too late
364     // for the NSInvocation to retain the WebView.
365     if (![m_webView _isClosing])
366         WebThreadPostNotification(WebViewDidChangeSelectionNotification, m_webView, nil);
367 #endif
368 }
369
370 void WebEditorClient::discardedComposition(Frame*)
371 {
372     // The effects of this function are currently achieved via -[WebHTMLView _updateSelectionForInputManager].
373 }
374
375 void WebEditorClient::didEndEditing()
376 {
377 #if !PLATFORM(IOS)
378     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidEndEditingNotification object:m_webView];
379 #else
380     WebThreadPostNotification(WebViewDidEndEditingNotification, m_webView, nil);
381 #endif
382 }
383
384 void WebEditorClient::didWriteSelectionToPasteboard()
385 {
386 #if !PLATFORM(IOS)
387     [[m_webView _editingDelegateForwarder] webView:m_webView didWriteSelectionToPasteboard:[NSPasteboard generalPasteboard]];
388 #endif
389 }
390
391 void WebEditorClient::willWriteSelectionToPasteboard(WebCore::Range*)
392 {
393     // Not implemented WebKit, only WebKit2.
394 }
395
396 void WebEditorClient::getClientPasteboardDataForRange(WebCore::Range*, Vector<String>& pasteboardTypes, Vector<RefPtr<WebCore::SharedBuffer>>& pasteboardData)
397 {
398     // Not implemented WebKit, only WebKit2.
399 }
400
401 NSString *WebEditorClient::userVisibleString(NSURL *URL)
402 {
403     return [URL _web_userVisibleString];
404 }
405
406 NSURL *WebEditorClient::canonicalizeURL(NSURL *URL)
407 {
408     return [URL _webkit_canonicalize];
409 }
410
411 NSURL *WebEditorClient::canonicalizeURLString(NSString *URLString)
412 {
413     NSURL *URL = nil;
414     if ([URLString _webkit_looksLikeAbsoluteURL])
415         URL = [[NSURL _web_URLWithUserTypedString:URLString] _webkit_canonicalize];
416     return URL;
417 }
418
419 static NSArray *createExcludedElementsForAttributedStringConversion()
420 {
421     NSArray *elements = [[NSArray alloc] initWithObjects: 
422         // Omit style since we want style to be inline so the fragment can be easily inserted.
423         @"style", 
424         // Omit xml so the result is not XHTML.
425         @"xml", 
426         // Omit tags that will get stripped when converted to a fragment anyway.
427         @"doctype", @"html", @"head", @"body", 
428         // Omit deprecated tags.
429         @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u", 
430         // Omit object so no file attachments are part of the fragment.
431         @"object", nil];
432     CFRetain(elements);
433     return elements;
434 }
435
436 #if PLATFORM(IOS)
437 static NSString *NSExcludedElementsDocumentAttribute = @"ExcludedElements";
438 #endif
439
440 DocumentFragment* WebEditorClient::documentFragmentFromAttributedString(NSAttributedString *string, Vector<RefPtr<ArchiveResource>>& resources)
441 {
442     static NSArray *excludedElements = createExcludedElementsForAttributedStringConversion();
443     
444     NSDictionary *dictionary = [NSDictionary dictionaryWithObject:excludedElements forKey:NSExcludedElementsDocumentAttribute];
445
446     NSArray *subResources;
447     DOMDocumentFragment* fragment = [string _documentFromRange:NSMakeRange(0, [string length])
448                                                       document:[[m_webView mainFrame] DOMDocument]
449                                             documentAttributes:dictionary
450                                                   subresources:&subResources];
451     for (WebResource* resource in subResources)
452         resources.append([resource _coreResource]);
453
454     return core(fragment);
455 }
456
457 void WebEditorClient::setInsertionPasteboard(const String& pasteboardName)
458 {
459 #if !PLATFORM(IOS)
460     NSPasteboard *pasteboard = pasteboardName.isEmpty() ? nil : [NSPasteboard pasteboardWithName:pasteboardName];
461     [m_webView _setInsertionPasteboard:pasteboard];
462 #endif
463 }
464
465 #if USE(APPKIT)
466 void WebEditorClient::uppercaseWord()
467 {
468     [m_webView uppercaseWord:nil];
469 }
470
471 void WebEditorClient::lowercaseWord()
472 {
473     [m_webView lowercaseWord:nil];
474 }
475
476 void WebEditorClient::capitalizeWord()
477 {
478     [m_webView capitalizeWord:nil];
479 }
480 #endif
481
482 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
483 void WebEditorClient::showSubstitutionsPanel(bool show)
484 {
485     NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] substitutionsPanel];
486     if (show)
487         [spellingPanel orderFront:nil];
488     else
489         [spellingPanel orderOut:nil];
490 }
491
492 bool WebEditorClient::substitutionsPanelIsShowing()
493 {
494     return [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
495 }
496
497 void WebEditorClient::toggleSmartInsertDelete()
498 {
499     [m_webView toggleSmartInsertDelete:nil];
500 }
501
502 bool WebEditorClient::isAutomaticQuoteSubstitutionEnabled()
503 {
504     return [m_webView isAutomaticQuoteSubstitutionEnabled];
505 }
506
507 void WebEditorClient::toggleAutomaticQuoteSubstitution()
508 {
509     [m_webView toggleAutomaticQuoteSubstitution:nil];
510 }
511
512 bool WebEditorClient::isAutomaticLinkDetectionEnabled()
513 {
514     return [m_webView isAutomaticLinkDetectionEnabled];
515 }
516
517 void WebEditorClient::toggleAutomaticLinkDetection()
518 {
519     [m_webView toggleAutomaticLinkDetection:nil];
520 }
521
522 bool WebEditorClient::isAutomaticDashSubstitutionEnabled()
523 {
524     return [m_webView isAutomaticDashSubstitutionEnabled];
525 }
526
527 void WebEditorClient::toggleAutomaticDashSubstitution()
528 {
529     [m_webView toggleAutomaticDashSubstitution:nil];
530 }
531
532 bool WebEditorClient::isAutomaticTextReplacementEnabled()
533 {
534     return [m_webView isAutomaticTextReplacementEnabled];
535 }
536
537 void WebEditorClient::toggleAutomaticTextReplacement()
538 {
539     [m_webView toggleAutomaticTextReplacement:nil];
540 }
541
542 bool WebEditorClient::isAutomaticSpellingCorrectionEnabled()
543 {
544     return [m_webView isAutomaticSpellingCorrectionEnabled];
545 }
546
547 void WebEditorClient::toggleAutomaticSpellingCorrection()
548 {
549     [m_webView toggleAutomaticSpellingCorrection:nil];
550 }
551 #endif // USE(AUTOMATIC_TEXT_REPLACEMENT)
552
553 bool WebEditorClient::shouldInsertNode(Node *node, Range* replacingRange, EditorInsertAction givenAction)
554
555     return [[m_webView _editingDelegateForwarder] webView:m_webView shouldInsertNode:kit(node) replacingDOMRange:kit(replacingRange) givenAction:(WebViewInsertAction)givenAction];
556 }
557
558 static NSString* undoNameForEditAction(EditAction editAction)
559 {
560     // FIXME: This is identical to code in WebKit2's WebEditCommandProxy class; would be nice to share the strings instead of having two copies.
561     switch (editAction) {
562         case EditActionUnspecified: return nil;
563         case EditActionInsert: return nil;
564         case EditActionSetColor: return UI_STRING_KEY_INTERNAL("Set Color", "Set Color (Undo action name)", "Undo action name");
565         case EditActionSetBackgroundColor: return UI_STRING_KEY_INTERNAL("Set Background Color", "Set Background Color (Undo action name)", "Undo action name");
566         case EditActionTurnOffKerning: return UI_STRING_KEY_INTERNAL("Turn Off Kerning", "Turn Off Kerning (Undo action name)", "Undo action name");
567         case EditActionTightenKerning: return UI_STRING_KEY_INTERNAL("Tighten Kerning", "Tighten Kerning (Undo action name)", "Undo action name");
568         case EditActionLoosenKerning: return UI_STRING_KEY_INTERNAL("Loosen Kerning", "Loosen Kerning (Undo action name)", "Undo action name");
569         case EditActionUseStandardKerning: return UI_STRING_KEY_INTERNAL("Use Standard Kerning", "Use Standard Kerning (Undo action name)", "Undo action name");
570         case EditActionTurnOffLigatures: return UI_STRING_KEY_INTERNAL("Turn Off Ligatures", "Turn Off Ligatures (Undo action name)", "Undo action name");
571         case EditActionUseStandardLigatures: return UI_STRING_KEY_INTERNAL("Use Standard Ligatures", "Use Standard Ligatures (Undo action name)", "Undo action name");
572         case EditActionUseAllLigatures: return UI_STRING_KEY_INTERNAL("Use All Ligatures", "Use All Ligatures (Undo action name)", "Undo action name");
573         case EditActionRaiseBaseline: return UI_STRING_KEY_INTERNAL("Raise Baseline", "Raise Baseline (Undo action name)", "Undo action name");
574         case EditActionLowerBaseline: return UI_STRING_KEY_INTERNAL("Lower Baseline", "Lower Baseline (Undo action name)", "Undo action name");
575         case EditActionSetTraditionalCharacterShape: return UI_STRING_KEY_INTERNAL("Set Traditional Character Shape", "Set Traditional Character Shape (Undo action name)", "Undo action name");
576         case EditActionSetFont: return UI_STRING_KEY_INTERNAL("Set Font", "Set Font (Undo action name)", "Undo action name");
577         case EditActionChangeAttributes: return UI_STRING_KEY_INTERNAL("Change Attributes", "Change Attributes (Undo action name)", "Undo action name");
578         case EditActionAlignLeft: return UI_STRING_KEY_INTERNAL("Align Left", "Align Left (Undo action name)", "Undo action name");
579         case EditActionAlignRight: return UI_STRING_KEY_INTERNAL("Align Right", "Align Right (Undo action name)", "Undo action name");
580         case EditActionCenter: return UI_STRING_KEY_INTERNAL("Center", "Center (Undo action name)", "Undo action name");
581         case EditActionJustify: return UI_STRING_KEY_INTERNAL("Justify", "Justify (Undo action name)", "Undo action name");
582         case EditActionSetWritingDirection: return UI_STRING_KEY_INTERNAL("Set Writing Direction", "Set Writing Direction (Undo action name)", "Undo action name");
583         case EditActionSubscript: return UI_STRING_KEY_INTERNAL("Subscript", "Subscript (Undo action name)", "Undo action name");
584         case EditActionSuperscript: return UI_STRING_KEY_INTERNAL("Superscript", "Superscript (Undo action name)", "Undo action name");
585         case EditActionUnderline: return UI_STRING_KEY_INTERNAL("Underline", "Underline (Undo action name)", "Undo action name");
586         case EditActionOutline: return UI_STRING_KEY_INTERNAL("Outline", "Outline (Undo action name)", "Undo action name");
587         case EditActionUnscript: return UI_STRING_KEY_INTERNAL("Unscript", "Unscript (Undo action name)", "Undo action name");
588         case EditActionDrag: return UI_STRING_KEY_INTERNAL("Drag", "Drag (Undo action name)", "Undo action name");
589         case EditActionCut: return UI_STRING_KEY_INTERNAL("Cut", "Cut (Undo action name)", "Undo action name");
590         case EditActionPaste: return UI_STRING_KEY_INTERNAL("Paste", "Paste (Undo action name)", "Undo action name");
591         case EditActionPasteFont: return UI_STRING_KEY_INTERNAL("Paste Font", "Paste Font (Undo action name)", "Undo action name");
592         case EditActionPasteRuler: return UI_STRING_KEY_INTERNAL("Paste Ruler", "Paste Ruler (Undo action name)", "Undo action name");
593         case EditActionTyping: return UI_STRING_KEY_INTERNAL("Typing", "Typing (Undo action name)", "Undo action name");
594         case EditActionCreateLink: return UI_STRING_KEY_INTERNAL("Create Link", "Create Link (Undo action name)", "Undo action name");
595         case EditActionUnlink: return UI_STRING_KEY_INTERNAL("Unlink", "Unlink (Undo action name)", "Undo action name");
596         case EditActionInsertList: return UI_STRING_KEY_INTERNAL("Insert List", "Insert List (Undo action name)", "Undo action name");
597         case EditActionFormatBlock: return UI_STRING_KEY_INTERNAL("Formatting", "Format Block (Undo action name)", "Undo action name");
598         case EditActionIndent: return UI_STRING_KEY_INTERNAL("Indent", "Indent (Undo action name)", "Undo action name");
599         case EditActionOutdent: return UI_STRING_KEY_INTERNAL("Outdent", "Outdent (Undo action name)", "Undo action name");
600         case EditActionBold: return UI_STRING_KEY_INTERNAL("Bold", "Bold (Undo action name)", "Undo action name");
601         case EditActionItalics: return UI_STRING_KEY_INTERNAL("Italics", "Italics (Undo action name)", "Undo action name");
602         case EditActionDelete: return UI_STRING_KEY_INTERNAL("Delete", "Delete (Undo action name)", "Undo action name");
603         case EditActionDictation: return UI_STRING_KEY_INTERNAL("Dictation", "Dictation (Undo action name)", "Undo action name");
604     }
605     return nil;
606 }
607
608 void WebEditorClient::registerUndoOrRedoStep(PassRefPtr<UndoStep> step, bool isRedo)
609 {
610     ASSERT(step);
611     
612     NSUndoManager *undoManager = [m_webView undoManager];
613 #if PLATFORM(IOS)
614     // While we are undoing, we shouldn't be asked to register another Undo operation, we shouldn't even be touching the DOM.  But
615     // just in case this happens, return to avoid putting the undo manager into an inconsistent state.  Same for being
616     // asked to register a Redo operation in the midst of another Redo.
617     if (([undoManager isUndoing] && !isRedo) || ([undoManager isRedoing] && isRedo))
618         return;
619 #endif
620     NSString *actionName = undoNameForEditAction(step->editingAction());
621     WebUndoStep *webEntry = [WebUndoStep stepWithUndoStep:step];
622     [undoManager registerUndoWithTarget:m_undoTarget.get() selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:webEntry];
623     if (actionName)
624         [undoManager setActionName:actionName];
625     m_haveUndoRedoOperations = YES;
626 }
627
628 void WebEditorClient::registerUndoStep(PassRefPtr<UndoStep> cmd)
629 {
630     registerUndoOrRedoStep(cmd, false);
631 }
632
633 void WebEditorClient::registerRedoStep(PassRefPtr<UndoStep> cmd)
634 {
635     registerUndoOrRedoStep(cmd, true);
636 }
637
638 void WebEditorClient::clearUndoRedoOperations()
639 {
640     if (m_haveUndoRedoOperations) {
641         // workaround for <rdar://problem/4645507> NSUndoManager dies
642         // with uncaught exception when undo items cleared while
643         // groups are open
644         NSUndoManager *undoManager = [m_webView undoManager];
645         int groupingLevel = [undoManager groupingLevel];
646         for (int i = 0; i < groupingLevel; ++i)
647             [undoManager endUndoGrouping];
648         
649         [undoManager removeAllActionsWithTarget:m_undoTarget.get()];
650         
651         for (int i = 0; i < groupingLevel; ++i)
652             [undoManager beginUndoGrouping];
653         
654         m_haveUndoRedoOperations = NO;
655     }    
656 }
657
658 bool WebEditorClient::canCopyCut(Frame*, bool defaultValue) const
659 {
660     return defaultValue;
661 }
662
663 bool WebEditorClient::canPaste(Frame*, bool defaultValue) const
664 {
665     return defaultValue;
666 }
667
668 bool WebEditorClient::canUndo() const
669 {
670     return [[m_webView undoManager] canUndo];
671 }
672
673 bool WebEditorClient::canRedo() const
674 {
675     return [[m_webView undoManager] canRedo];
676 }
677
678 void WebEditorClient::undo()
679 {
680     if (canUndo())
681         [[m_webView undoManager] undo];
682 }
683
684 void WebEditorClient::redo()
685 {
686     if (canRedo())
687         [[m_webView undoManager] redo];    
688 }
689
690 void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
691 {
692     Frame* frame = event->target()->toNode()->document().frame();
693 #if !PLATFORM(IOS)
694     WebHTMLView *webHTMLView = [[kit(frame) frameView] documentView];
695     if ([webHTMLView _interpretKeyEvent:event savingCommands:NO])
696         event->setDefaultHandled();
697 #else
698     WebHTMLView *webHTMLView = (WebHTMLView *)[[kit(frame) frameView] documentView];
699     if ([webHTMLView _handleEditingKeyEvent:event])
700         event->setDefaultHandled();
701 #endif
702 }
703
704 void WebEditorClient::handleInputMethodKeydown(KeyboardEvent* event)
705 {
706 #if !PLATFORM(IOS)
707     // FIXME: Switch to WebKit2 model, interpreting the event before it's sent down to WebCore.
708     Frame* frame = event->target()->toNode()->document().frame();
709     WebHTMLView *webHTMLView = [[kit(frame) frameView] documentView];
710     if ([webHTMLView _interpretKeyEvent:event savingCommands:YES])
711         event->setDefaultHandled();
712 #else
713     // iOS does not use input manager this way
714 #endif
715 }
716
717 #define FormDelegateLog(ctrl)  LOG(FormDelegate, "control=%@", ctrl)
718
719 void WebEditorClient::textFieldDidBeginEditing(Element* element)
720 {
721     if (!is<HTMLInputElement>(*element))
722         return;
723
724     DOMHTMLInputElement* inputElement = kit(downcast<HTMLInputElement>(element));
725     FormDelegateLog(inputElement);
726     CallFormDelegate(m_webView, @selector(textFieldDidBeginEditing:inFrame:), inputElement, kit(element->document().frame()));
727 }
728
729 void WebEditorClient::textFieldDidEndEditing(Element* element)
730 {
731     if (!is<HTMLInputElement>(*element))
732         return;
733
734     DOMHTMLInputElement* inputElement = kit(downcast<HTMLInputElement>(element));
735     FormDelegateLog(inputElement);
736     CallFormDelegate(m_webView, @selector(textFieldDidEndEditing:inFrame:), inputElement, kit(element->document().frame()));
737 }
738
739 void WebEditorClient::textDidChangeInTextField(Element* element)
740 {
741     if (!is<HTMLInputElement>(*element))
742         return;
743
744 #if !PLATFORM(IOS)
745     if (!UserTypingGestureIndicator::processingUserTypingGesture() || UserTypingGestureIndicator::focusedElementAtGestureStart() != element)
746         return;
747 #endif
748
749     DOMHTMLInputElement* inputElement = kit(downcast<HTMLInputElement>(element));
750     FormDelegateLog(inputElement);
751     CallFormDelegate(m_webView, @selector(textDidChangeInTextField:inFrame:), inputElement, kit(element->document().frame()));
752 }
753
754 static SEL selectorForKeyEvent(KeyboardEvent* event)
755 {
756     // FIXME: This helper function is for the auto-fill code so we can pass a selector to the form delegate.  
757     // Eventually, we should move all of the auto-fill code down to WebKit and remove the need for this function by
758     // not relying on the selector in the new implementation.
759     // The key identifiers are from <http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set>
760     const String& key = event->keyIdentifier();
761     if (key == "Up")
762         return @selector(moveUp:);
763     if (key == "Down")
764         return @selector(moveDown:);
765     if (key == "U+001B")
766         return @selector(cancel:);
767     if (key == "U+0009") {
768         if (event->shiftKey())
769             return @selector(insertBacktab:);
770         return @selector(insertTab:);
771     }
772     if (key == "Enter")
773         return @selector(insertNewline:);
774     return 0;
775 }
776
777 bool WebEditorClient::doTextFieldCommandFromEvent(Element* element, KeyboardEvent* event)
778 {
779     if (!is<HTMLInputElement>(*element))
780         return NO;
781
782     DOMHTMLInputElement* inputElement = kit(downcast<HTMLInputElement>(element));
783     FormDelegateLog(inputElement);
784     if (SEL commandSelector = selectorForKeyEvent(event))
785         return CallFormDelegateReturningBoolean(NO, m_webView, @selector(textField:doCommandBySelector:inFrame:), inputElement, commandSelector, kit(element->document().frame()));
786     return NO;
787 }
788
789 void WebEditorClient::textWillBeDeletedInTextField(Element* element)
790 {
791     if (!is<HTMLInputElement>(*element))
792         return;
793
794     DOMHTMLInputElement* inputElement = kit(downcast<HTMLInputElement>(element));
795     FormDelegateLog(inputElement);
796     // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
797     CallFormDelegateReturningBoolean(NO, m_webView, @selector(textField:doCommandBySelector:inFrame:), inputElement, @selector(deleteBackward:), kit(element->document().frame()));
798 }
799
800 void WebEditorClient::textDidChangeInTextArea(Element* element)
801 {
802     if (!is<HTMLTextAreaElement>(*element))
803         return;
804
805     DOMHTMLTextAreaElement* textAreaElement = kit(downcast<HTMLTextAreaElement>(element));
806     FormDelegateLog(textAreaElement);
807     CallFormDelegate(m_webView, @selector(textDidChangeInTextArea:inFrame:), textAreaElement, kit(element->document().frame()));
808 }
809
810 #if PLATFORM(IOS)
811
812 void WebEditorClient::writeDataToPasteboard(NSDictionary* representation)
813 {
814     if ([[m_webView _UIKitDelegateForwarder] respondsToSelector:@selector(writeDataToPasteboard:)])
815         [[m_webView _UIKitDelegateForwarder] writeDataToPasteboard:representation];
816 }
817
818 NSArray* WebEditorClient::supportedPasteboardTypesForCurrentSelection() 
819 {
820     if ([[m_webView _UIKitDelegateForwarder] respondsToSelector:@selector(supportedPasteboardTypesForCurrentSelection)]) 
821         return [[m_webView _UIKitDelegateForwarder] supportedPasteboardTypesForCurrentSelection]; 
822
823     return nil; 
824 }
825
826 NSArray* WebEditorClient::readDataFromPasteboard(NSString* type, int index)
827 {
828     if ([[m_webView _UIKitDelegateForwarder] respondsToSelector:@selector(readDataFromPasteboard:withIndex:)])
829         return [[m_webView _UIKitDelegateForwarder] readDataFromPasteboard:type withIndex:index];
830     
831     return nil;
832 }
833
834 bool WebEditorClient::hasRichlyEditableSelection()
835 {
836     if ([[m_webView _UIKitDelegateForwarder] respondsToSelector:@selector(hasRichlyEditableSelection)])
837         return [[m_webView _UIKitDelegateForwarder] hasRichlyEditableSelection];
838     
839     return false;
840 }
841
842 int WebEditorClient::getPasteboardItemsCount()
843 {
844     if ([[m_webView _UIKitDelegateForwarder] respondsToSelector:@selector(getPasteboardItemsCount)])
845         return [[m_webView _UIKitDelegateForwarder] getPasteboardItemsCount];
846     
847     return 0;
848 }
849
850 WebCore::DocumentFragment* WebEditorClient::documentFragmentFromDelegate(int index)
851 {
852     if ([[m_webView _editingDelegateForwarder] respondsToSelector:@selector(documentFragmentForPasteboardItemAtIndex:)]) {
853         DOMDocumentFragment *fragmentFromDelegate = [[m_webView _editingDelegateForwarder] documentFragmentForPasteboardItemAtIndex:index];
854         if (fragmentFromDelegate)
855             return core(fragmentFromDelegate);
856     }
857     
858     return 0;
859 }
860
861 bool WebEditorClient::performsTwoStepPaste(WebCore::DocumentFragment* fragment)
862 {
863     if ([[m_webView _UIKitDelegateForwarder] respondsToSelector:@selector(performsTwoStepPaste:)])
864         return [[m_webView _UIKitDelegateForwarder] performsTwoStepPaste:kit(fragment)];
865
866     return false;
867 }
868
869 int WebEditorClient::pasteboardChangeCount()
870 {
871     if ([[m_webView _UIKitDelegateForwarder] respondsToSelector:@selector(getPasteboardChangeCount)])
872         return [[m_webView _UIKitDelegateForwarder] getPasteboardChangeCount];
873     
874     return 0;
875 }
876
877 Vector<TextCheckingResult> WebEditorClient::checkTextOfParagraph(StringView string, TextCheckingTypeMask checkingTypes)
878 {
879     ASSERT(checkingTypes & NSTextCheckingTypeSpelling);
880
881     Vector<TextCheckingResult> results;
882
883     NSArray *incomingResults = [[m_webView _UIKitDelegateForwarder] checkSpellingOfString:string.createNSStringWithoutCopying().get()];
884     for (NSValue *incomingResult in incomingResults) {
885         NSRange resultRange = [incomingResult rangeValue];
886         ASSERT(resultRange.location != NSNotFound && resultRange.length > 0);
887         TextCheckingResult result;
888         result.type = TextCheckingTypeSpelling;
889         result.location = resultRange.location;
890         result.length = resultRange.length;
891         results.append(result);
892     }
893
894     return results;
895 }
896
897 #endif // PLATFORM(IOS)
898
899 #if !PLATFORM(IOS)
900
901 bool WebEditorClient::shouldEraseMarkersAfterChangeSelection(TextCheckingType type) const
902 {
903     // This prevents erasing spelling markers on OS X Lion or later to match AppKit on these Mac OS X versions.
904     return type != TextCheckingTypeSpelling;
905 }
906
907 void WebEditorClient::ignoreWordInSpellDocument(const String& text)
908 {
909     [[NSSpellChecker sharedSpellChecker] ignoreWord:text inSpellDocumentWithTag:spellCheckerDocumentTag()];
910 }
911
912 void WebEditorClient::learnWord(const String& text)
913 {
914     [[NSSpellChecker sharedSpellChecker] learnWord:text];
915 }
916
917 void WebEditorClient::checkSpellingOfString(StringView text, int* misspellingLocation, int* misspellingLength)
918 {
919     NSRange range = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString:text.createNSStringWithoutCopying().get() startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:spellCheckerDocumentTag() wordCount:NULL];
920
921     if (misspellingLocation) {
922         // WebCore expects -1 to represent "not found"
923         if (range.location == NSNotFound)
924             *misspellingLocation = -1;
925         else
926             *misspellingLocation = range.location;
927     }
928     
929     if (misspellingLength)
930         *misspellingLength = range.length;
931 }
932
933 String WebEditorClient::getAutoCorrectSuggestionForMisspelledWord(const String& inputWord)
934 {
935     // This method can be implemented using customized algorithms for the particular browser.
936     // Currently, it computes an empty string.
937     return String();
938 }
939
940 void WebEditorClient::checkGrammarOfString(StringView text, Vector<GrammarDetail>& details, int* badGrammarLocation, int* badGrammarLength)
941 {
942     NSArray *grammarDetails;
943     NSRange range = [[NSSpellChecker sharedSpellChecker] checkGrammarOfString:text.createNSStringWithoutCopying().get() startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:spellCheckerDocumentTag() details:&grammarDetails];
944     if (badGrammarLocation)
945         // WebCore expects -1 to represent "not found"
946         *badGrammarLocation = (range.location == NSNotFound) ? -1 : static_cast<int>(range.location);
947     if (badGrammarLength)
948         *badGrammarLength = range.length;
949     for (NSDictionary *detail in grammarDetails) {
950         ASSERT(detail);
951         GrammarDetail grammarDetail;
952         NSValue *detailRangeAsNSValue = [detail objectForKey:NSGrammarRange];
953         ASSERT(detailRangeAsNSValue);
954         NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
955         ASSERT(detailNSRange.location != NSNotFound);
956         ASSERT(detailNSRange.length > 0);
957         grammarDetail.location = detailNSRange.location;
958         grammarDetail.length = detailNSRange.length;
959         grammarDetail.userDescription = [detail objectForKey:NSGrammarUserDescription];
960         NSArray *guesses = [detail objectForKey:NSGrammarCorrections];
961         for (NSString *guess in guesses)
962             grammarDetail.guesses.append(String(guess));
963         details.append(grammarDetail);
964     }
965 }
966
967 static Vector<TextCheckingResult> core(NSArray *incomingResults, TextCheckingTypeMask checkingTypes)
968 {
969     Vector<TextCheckingResult> results;
970
971     for (NSTextCheckingResult *incomingResult in incomingResults) {
972         NSRange resultRange = [incomingResult range];
973         NSTextCheckingType resultType = [incomingResult resultType];
974         ASSERT(resultRange.location != NSNotFound);
975         ASSERT(resultRange.length > 0);
976         if (NSTextCheckingTypeSpelling == resultType && 0 != (checkingTypes & NSTextCheckingTypeSpelling)) {
977             TextCheckingResult result;
978             result.type = TextCheckingTypeSpelling;
979             result.location = resultRange.location;
980             result.length = resultRange.length;
981             results.append(result);
982         } else if (NSTextCheckingTypeGrammar == resultType && 0 != (checkingTypes & NSTextCheckingTypeGrammar)) {
983             TextCheckingResult result;
984             NSArray *details = [incomingResult grammarDetails];
985             result.type = TextCheckingTypeGrammar;
986             result.location = resultRange.location;
987             result.length = resultRange.length;
988             for (NSDictionary *incomingDetail in details) {
989                 ASSERT(incomingDetail);
990                 GrammarDetail detail;
991                 NSValue *detailRangeAsNSValue = [incomingDetail objectForKey:NSGrammarRange];
992                 ASSERT(detailRangeAsNSValue);
993                 NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
994                 ASSERT(detailNSRange.location != NSNotFound);
995                 ASSERT(detailNSRange.length > 0);
996                 detail.location = detailNSRange.location;
997                 detail.length = detailNSRange.length;
998                 detail.userDescription = [incomingDetail objectForKey:NSGrammarUserDescription];
999                 NSArray *guesses = [incomingDetail objectForKey:NSGrammarCorrections];
1000                 for (NSString *guess in guesses)
1001                     detail.guesses.append(String(guess));
1002                 result.details.append(detail);
1003             }
1004             results.append(result);
1005         } else if (NSTextCheckingTypeLink == resultType && 0 != (checkingTypes & NSTextCheckingTypeLink)) {
1006             TextCheckingResult result;
1007             result.type = TextCheckingTypeLink;
1008             result.location = resultRange.location;
1009             result.length = resultRange.length;
1010             result.replacement = [[incomingResult URL] absoluteString];
1011             results.append(result);
1012         } else if (NSTextCheckingTypeQuote == resultType && 0 != (checkingTypes & NSTextCheckingTypeQuote)) {
1013             TextCheckingResult result;
1014             result.type = TextCheckingTypeQuote;
1015             result.location = resultRange.location;
1016             result.length = resultRange.length;
1017             result.replacement = [incomingResult replacementString];
1018             results.append(result);
1019         } else if (NSTextCheckingTypeDash == resultType && 0 != (checkingTypes & NSTextCheckingTypeDash)) {
1020             TextCheckingResult result;
1021             result.type = TextCheckingTypeDash;
1022             result.location = resultRange.location;
1023             result.length = resultRange.length;
1024             result.replacement = [incomingResult replacementString];
1025             results.append(result);
1026         } else if (NSTextCheckingTypeReplacement == resultType && 0 != (checkingTypes & NSTextCheckingTypeReplacement)) {
1027             TextCheckingResult result;
1028             result.type = TextCheckingTypeReplacement;
1029             result.location = resultRange.location;
1030             result.length = resultRange.length;
1031             result.replacement = [incomingResult replacementString];
1032             results.append(result);
1033         } else if (NSTextCheckingTypeCorrection == resultType && 0 != (checkingTypes & NSTextCheckingTypeCorrection)) {
1034             TextCheckingResult result;
1035             result.type = TextCheckingTypeCorrection;
1036             result.location = resultRange.location;
1037             result.length = resultRange.length;
1038             result.replacement = [incomingResult replacementString];
1039             results.append(result);
1040         }
1041     }
1042
1043     return results;
1044 }
1045
1046 Vector<TextCheckingResult> WebEditorClient::checkTextOfParagraph(StringView string, TextCheckingTypeMask checkingTypes)
1047 {
1048     return core([[NSSpellChecker sharedSpellChecker] checkString:string.createNSStringWithoutCopying().get() range:NSMakeRange(0, string.length()) types:(checkingTypes | NSTextCheckingTypeOrthography) options:nil inSpellDocumentWithTag:spellCheckerDocumentTag() orthography:NULL wordCount:NULL], checkingTypes);
1049 }
1050
1051 void WebEditorClient::updateSpellingUIWithGrammarString(const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
1052 {
1053     NSMutableArray* corrections = [NSMutableArray array];
1054     for (unsigned i = 0; i < grammarDetail.guesses.size(); i++) {
1055         NSString* guess = grammarDetail.guesses[i];
1056         [corrections addObject:guess];
1057     }
1058     NSRange grammarRange = NSMakeRange(grammarDetail.location, grammarDetail.length);
1059     NSString* grammarUserDescription = grammarDetail.userDescription;
1060     NSDictionary* grammarDetailDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithRange:grammarRange], NSGrammarRange, grammarUserDescription, NSGrammarUserDescription, corrections, NSGrammarCorrections, nil];
1061     
1062     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetailDict];
1063 }
1064
1065 void WebEditorClient::updateSpellingUIWithMisspelledWord(const String& misspelledWord)
1066 {
1067     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithMisspelledWord:misspelledWord];
1068 }
1069
1070 void WebEditorClient::showSpellingUI(bool show)
1071 {
1072     NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] spellingPanel];
1073     if (show)
1074         [spellingPanel orderFront:nil];
1075     else
1076         [spellingPanel orderOut:nil];
1077 }
1078
1079 bool WebEditorClient::spellingUIIsShowing()
1080 {
1081     return [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
1082 }
1083
1084 void WebEditorClient::getGuessesForWord(const String& word, const String& context, Vector<String>& guesses) {
1085     guesses.clear();
1086     NSString* language = nil;
1087     NSOrthography* orthography = nil;
1088     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
1089     if (context.length()) {
1090         [checker checkString:context range:NSMakeRange(0, context.length()) types:NSTextCheckingTypeOrthography options:0 inSpellDocumentWithTag:spellCheckerDocumentTag() orthography:&orthography wordCount:0];
1091         language = [checker languageForWordRange:NSMakeRange(0, context.length()) inString:context orthography:orthography];
1092     }
1093     NSArray* stringsArray = [checker guessesForWordRange:NSMakeRange(0, word.length()) inString:word language:language inSpellDocumentWithTag:spellCheckerDocumentTag()];
1094     unsigned count = [stringsArray count];
1095
1096     if (count > 0) {
1097         NSEnumerator* enumerator = [stringsArray objectEnumerator];
1098         NSString* string;
1099         while ((string = [enumerator nextObject]) != nil)
1100             guesses.append(string);
1101     }
1102 }
1103
1104 #endif // !PLATFORM(IOS)
1105
1106 void WebEditorClient::willSetInputMethodState()
1107 {
1108 }
1109
1110 void WebEditorClient::setInputMethodState(bool)
1111 {
1112 }
1113
1114 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
1115 void WebEditorClient::requestCandidatesForSelection(const VisibleSelection& selection)
1116 {
1117     RefPtr<Range> selectedRange = selection.toNormalizedRange();
1118     if (!selectedRange)
1119         return;
1120
1121     m_lastSelectionForRequestedCandidates = selection;
1122
1123     VisiblePosition selectionStart = selection.visibleStart();
1124     VisiblePosition selectionEnd = selection.visibleEnd();
1125
1126     // Use the surrounding paragraphs of text as context.
1127     VisiblePosition paragraphStart = startOfParagraph(selectionStart);
1128     VisiblePosition paragraphEnd = endOfParagraph(selectionEnd);
1129
1130     int lengthToSelectionStart = TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
1131     int lengthToSelectionEnd = TextIterator::rangeLength(makeRange(paragraphStart, selectionEnd).get());
1132     NSRange rangeForCandidates = NSMakeRange(lengthToSelectionStart, lengthToSelectionEnd - lengthToSelectionStart);
1133
1134     String fullPlainTextStringOfParagraph = plainText(makeRange(paragraphStart, paragraphEnd).get());
1135
1136     NSTextCheckingTypes checkingTypes = NSTextCheckingTypeSpelling | NSTextCheckingTypeReplacement | NSTextCheckingTypeCorrection;
1137     auto weakEditor = m_weakPtrFactory.createWeakPtr();
1138     [[NSSpellChecker sharedSpellChecker] requestCandidatesForSelectedRange:rangeForCandidates inString:fullPlainTextStringOfParagraph types:checkingTypes options:nil inSpellDocumentWithTag:spellCheckerDocumentTag() completionHandler:[weakEditor](NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates) {
1139         dispatch_async(dispatch_get_main_queue(), ^{
1140             if (!weakEditor)
1141                 return;
1142             weakEditor->handleRequestedCandidates(sequenceNumber, candidates);
1143         });
1144     }];
1145 }
1146
1147 static RefPtr<Range> candidateRangeForSelection(const VisibleSelection& selection, Frame* frame)
1148 {
1149     return selection.isCaret() ? wordRangeFromPosition(selection.start()) : frame->selection().toNormalizedRange();
1150 }
1151
1152 static bool candidateWouldReplaceText(const VisibleSelection& selection)
1153 {
1154     // If the character behind the caret in the current selection is anything but a space or a newline then we should
1155     // replace the whole current word with the candidate.
1156     UChar32 characterAfterSelection, characterBeforeSelection, twoCharacterBeforeSelection = 0;
1157     charactersAroundPosition(selection.visibleStart(), characterAfterSelection, characterBeforeSelection, twoCharacterBeforeSelection);
1158     return !(characterBeforeSelection == '\0' || characterBeforeSelection == '\n' || characterBeforeSelection == ' ');
1159 }
1160
1161 void WebEditorClient::handleRequestedCandidates(NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates)
1162 {
1163     Frame* frame = core([m_webView _selectedOrMainFrame]);
1164     if (!frame)
1165         return;
1166
1167     const VisibleSelection& selection = frame->selection().selection();
1168     if (selection != m_lastSelectionForRequestedCandidates)
1169         return;
1170
1171     RefPtr<Range> rangeForCurrentlyTypedString = candidateRangeForSelection(selection, frame);
1172     NSString *currentlyTypedString;
1173     if (rangeForCurrentlyTypedString && candidateWouldReplaceText(selection))
1174         currentlyTypedString = plainText(rangeForCurrentlyTypedString.get());
1175     else
1176         currentlyTypedString = @"";
1177
1178     RefPtr<Range> selectedRange = frame->selection().selection().toNormalizedRange();
1179     if (!selectedRange)
1180         return;
1181
1182     IntRect rectForSelectionCandidates;
1183     Vector<FloatQuad> quads;
1184     selectedRange->absoluteTextQuads(quads);
1185     if (!quads.isEmpty())
1186         rectForSelectionCandidates = frame->view()->contentsToWindow(quads[0].enclosingBoundingBox());
1187
1188     auto weakEditor = m_weakPtrFactory.createWeakPtr();
1189     [[NSSpellChecker sharedSpellChecker] showCandidates:candidates forString:currentlyTypedString inRect:rectForSelectionCandidates view:m_webView completionHandler:[weakEditor](NSTextCheckingResult *acceptedCandidate) {
1190         dispatch_async(dispatch_get_main_queue(), ^{
1191             if (!weakEditor)
1192                 return;
1193             weakEditor->handleAcceptedCandidate(acceptedCandidate);
1194         });
1195     }];
1196
1197 }
1198
1199 void WebEditorClient::handleAcceptedCandidate(NSTextCheckingResult *acceptedCandidate)
1200 {
1201     Frame* frame = core([m_webView _selectedOrMainFrame]);
1202     if (!frame)
1203         return;
1204
1205     const VisibleSelection& selection = frame->selection().selection();
1206     if (selection != m_lastSelectionForRequestedCandidates)
1207         return;
1208
1209     RefPtr<Range> candidateRange = candidateRangeForSelection(selection, frame);
1210
1211     frame->editor().setIgnoreCompositionSelectionChange(true);
1212
1213     if (candidateWouldReplaceText(selection))
1214         frame->selection().setSelectedRange(candidateRange.get(), UPSTREAM, true);
1215
1216     frame->editor().insertText(acceptedCandidate.replacementString, 0);
1217     frame->editor().insertText(String(" "), 0);
1218     frame->editor().setIgnoreCompositionSelectionChange(false);
1219 }
1220 #endif // PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
1221
1222 #if !PLATFORM(IOS)
1223 @interface WebEditorSpellCheckResponder : NSObject
1224 {
1225     WebEditorClient* _client;
1226     int _sequence;
1227     RetainPtr<NSArray> _results;
1228 }
1229 - (id)initWithClient:(WebEditorClient*)client sequence:(int)sequence results:(NSArray*)results;
1230 - (void)perform;
1231 @end
1232
1233 @implementation WebEditorSpellCheckResponder
1234 - (id)initWithClient:(WebEditorClient*)client sequence:(int)sequence results:(NSArray*)results
1235 {
1236     self = [super init];
1237     if (!self)
1238         return nil;
1239     _client = client;
1240     _sequence = sequence;
1241     _results = results;
1242     return self;
1243 }
1244
1245 - (void)perform
1246 {
1247     _client->didCheckSucceed(_sequence, _results.get());
1248 }
1249
1250 @end
1251
1252 void WebEditorClient::didCheckSucceed(int sequence, NSArray* results)
1253 {
1254     ASSERT_UNUSED(sequence, sequence == m_textCheckingRequest->data().sequence());
1255     m_textCheckingRequest->didSucceed(core(results, m_textCheckingRequest->data().mask()));
1256     m_textCheckingRequest = nullptr;
1257 }
1258 #endif
1259
1260 void WebEditorClient::requestCheckingOfString(PassRefPtr<WebCore::TextCheckingRequest> request)
1261 {
1262 #if !PLATFORM(IOS)
1263     ASSERT(!m_textCheckingRequest);
1264     m_textCheckingRequest = request;
1265
1266     int sequence = m_textCheckingRequest->data().sequence();
1267     NSRange range = NSMakeRange(0, m_textCheckingRequest->data().text().length());
1268     NSRunLoop* currentLoop = [NSRunLoop currentRunLoop];
1269     [[NSSpellChecker sharedSpellChecker] requestCheckingOfString:m_textCheckingRequest->data().text() range:range types:NSTextCheckingAllSystemTypes options:0 inSpellDocumentWithTag:0
1270                                          completionHandler:^(NSInteger, NSArray* results, NSOrthography*, NSInteger) {
1271             [currentLoop performSelector:@selector(perform) 
1272                                   target:[[[WebEditorSpellCheckResponder alloc] initWithClient:this sequence:sequence results:results] autorelease]
1273                                 argument:nil order:0 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
1274         }];
1275 #endif
1276 }