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