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