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