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