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