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