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