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