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