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 "DOMHTMLElementInternal.h"
34#import "DOMHTMLInputElementInternal.h"
35#import "DOMHTMLTextAreaElementInternal.h"
36#import "DOMNodeInternal.h"
37#import "DOMRangeInternal.h"
38#import "WebArchive.h"
39#import "WebDataSourceInternal.h"
40#import "WebDelegateImplementationCaching.h"
41#import "WebDocument.h"
42#import "WebEditingDelegatePrivate.h"
43#import "WebFormDelegate.h"
44#import "WebFrameInternal.h"
45#import "WebHTMLView.h"
46#import "WebHTMLViewInternal.h"
47#import "WebKitLogging.h"
48#import "WebKitVersionChecks.h"
49#import "WebLocalizableStrings.h"
50#import "WebNSURLExtras.h"
51#import "WebViewInternal.h"
52#import <WebCore/Document.h>
53#import <WebCore/EditAction.h>
54#import <WebCore/EditCommand.h>
55#import <WebCore/HTMLInputElement.h>
56#import <WebCore/HTMLNames.h>
57#import <WebCore/HTMLTextAreaElement.h>
58#import <WebCore/KeyboardEvent.h>
59#import <WebCore/LegacyWebArchive.h>
60#import <WebCore/PlatformKeyboardEvent.h>
61#import <WebCore/PlatformString.h>
62#import <WebCore/WebCoreObjCExtras.h>
63#import <runtime/InitializeThreading.h>
64#import <wtf/PassRefPtr.h>
65
66using namespace WebCore;
67using namespace WTF;
68
69using namespace HTMLNames;
70
71static WebViewInsertAction kit(EditorInsertAction coreAction)
72{
73    return static_cast<WebViewInsertAction>(coreAction);
74}
75
76#ifdef BUILDING_ON_TIGER
77@interface NSSpellChecker (NotYetPublicMethods)
78- (void)learnWord:(NSString *)word;
79@end
80#endif
81
82@interface WebEditCommand : NSObject
83{
84    RefPtr<EditCommand> m_command;
85}
86
87+ (WebEditCommand *)commandWithEditCommand:(PassRefPtr<EditCommand>)command;
88- (EditCommand *)command;
89
90@end
91
92@implementation WebEditCommand
93
94+ (void)initialize
95{
96    JSC::initializeThreading();
97#ifndef BUILDING_ON_TIGER
98    WebCoreObjCFinalizeOnMainThread(self);
99#endif
100}
101
102- (id)initWithEditCommand:(PassRefPtr<EditCommand>)command
103{
104    ASSERT(command);
105    [super init];
106    m_command = command;
107    return self;
108}
109
110- (void)dealloc
111{
112    if (WebCoreObjCScheduleDeallocateOnMainThread([WebEditCommand class], self))
113        return;
114
115    [super dealloc];
116}
117
118- (void)finalize
119{
120    ASSERT_MAIN_THREAD();
121
122    [super finalize];
123}
124
125+ (WebEditCommand *)commandWithEditCommand:(PassRefPtr<EditCommand>)command
126{
127    return [[[WebEditCommand alloc] initWithEditCommand:command] autorelease];
128}
129
130- (EditCommand *)command
131{
132    return m_command.get();
133}
134
135@end
136
137@interface WebEditorUndoTarget : NSObject
138{
139}
140
141- (void)undoEditing:(id)arg;
142- (void)redoEditing:(id)arg;
143
144@end
145
146@implementation WebEditorUndoTarget
147
148- (void)undoEditing:(id)arg
149{
150    ASSERT([arg isKindOfClass:[WebEditCommand class]]);
151    [arg command]->unapply();
152}
153
154- (void)redoEditing:(id)arg
155{
156    ASSERT([arg isKindOfClass:[WebEditCommand class]]);
157    [arg command]->reapply();
158}
159
160@end
161
162void WebEditorClient::pageDestroyed()
163{
164    delete this;
165}
166
167WebEditorClient::WebEditorClient(WebView *webView)
168    : m_webView(webView)
169    , m_undoTarget([[[WebEditorUndoTarget alloc] init] autorelease])
170    , m_haveUndoRedoOperations(false)
171{
172}
173
174bool WebEditorClient::isContinuousSpellCheckingEnabled()
175{
176    return [m_webView isContinuousSpellCheckingEnabled];
177}
178
179void WebEditorClient::toggleContinuousSpellChecking()
180{
181    [m_webView toggleContinuousSpellChecking:nil];
182}
183
184bool WebEditorClient::isGrammarCheckingEnabled()
185{
186#ifdef BUILDING_ON_TIGER
187    return false;
188#else
189    return [m_webView isGrammarCheckingEnabled];
190#endif
191}
192
193void WebEditorClient::toggleGrammarChecking()
194{
195#ifndef BUILDING_ON_TIGER
196    [m_webView toggleGrammarChecking:nil];
197#endif
198}
199
200int WebEditorClient::spellCheckerDocumentTag()
201{
202    return [m_webView spellCheckerDocumentTag];
203}
204
205bool WebEditorClient::isEditable()
206{
207    return [m_webView isEditable];
208}
209
210bool WebEditorClient::shouldDeleteRange(Range* range)
211{
212    return [[m_webView _editingDelegateForwarder] webView:m_webView
213        shouldDeleteDOMRange:kit(range)];
214}
215
216bool WebEditorClient::shouldShowDeleteInterface(HTMLElement* element)
217{
218    return [[m_webView _editingDelegateForwarder] webView:m_webView
219        shouldShowDeleteInterfaceForElement:kit(element)];
220}
221
222bool WebEditorClient::smartInsertDeleteEnabled()
223{
224    return [m_webView smartInsertDeleteEnabled];
225}
226
227bool WebEditorClient::isSelectTrailingWhitespaceEnabled()
228{
229    return [m_webView isSelectTrailingWhitespaceEnabled];
230}
231
232bool WebEditorClient::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
233{
234    return [[m_webView _editingDelegateForwarder] webView:m_webView
235        shouldApplyStyle:kit(style) toElementsInDOMRange:kit(range)];
236}
237
238bool WebEditorClient::shouldMoveRangeAfterDelete(Range* range, Range* rangeToBeReplaced)
239{
240    return [[m_webView _editingDelegateForwarder] webView:m_webView
241        shouldMoveRangeAfterDelete:kit(range) replacingRange:kit(rangeToBeReplaced)];
242}
243
244bool WebEditorClient::shouldBeginEditing(Range* range)
245{
246    return [[m_webView _editingDelegateForwarder] webView:m_webView
247        shouldBeginEditingInDOMRange:kit(range)];
248
249    return false;
250}
251
252bool WebEditorClient::shouldEndEditing(Range* range)
253{
254    return [[m_webView _editingDelegateForwarder] webView:m_webView
255                             shouldEndEditingInDOMRange:kit(range)];
256}
257
258bool WebEditorClient::shouldInsertText(const String& text, Range* range, EditorInsertAction action)
259{
260    WebView* webView = m_webView;
261    return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:kit(range) givenAction:kit(action)];
262}
263
264bool WebEditorClient::shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity selectionAffinity, bool stillSelecting)
265{
266    return [m_webView _shouldChangeSelectedDOMRange:kit(fromRange) toDOMRange:kit(toRange) affinity:kit(selectionAffinity) stillSelecting:stillSelecting];
267}
268
269void WebEditorClient::didBeginEditing()
270{
271    [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidBeginEditingNotification object:m_webView];
272}
273
274void WebEditorClient::respondToChangedContents()
275{
276    NSView <WebDocumentView> *view = [[[m_webView selectedFrame] frameView] documentView];
277    if ([view isKindOfClass:[WebHTMLView class]])
278        [(WebHTMLView *)view _updateFontPanel];
279    [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidChangeNotification object:m_webView];
280}
281
282void WebEditorClient::respondToChangedSelection()
283{
284    [m_webView _selectionChanged];
285
286    // 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
287    if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_APERTURE_QUIRK) && [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Aperture"])
288        return;
289
290    [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidChangeSelectionNotification object:m_webView];
291}
292
293void WebEditorClient::didEndEditing()
294{
295    [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidEndEditingNotification object:m_webView];
296}
297
298void WebEditorClient::didWriteSelectionToPasteboard()
299{
300    [[m_webView _editingDelegateForwarder] webView:m_webView didWriteSelectionToPasteboard:[NSPasteboard generalPasteboard]];
301}
302
303void WebEditorClient::didSetSelectionTypesForPasteboard()
304{
305    [[m_webView _editingDelegateForwarder] webView:m_webView didSetSelectionTypesForPasteboard:[NSPasteboard generalPasteboard]];
306}
307
308NSString* WebEditorClient::userVisibleString(NSURL *URL)
309{
310    return [URL _web_userVisibleString];
311}
312
313#ifdef BUILDING_ON_TIGER
314NSArray* WebEditorClient::pasteboardTypesForSelection(Frame* selectedFrame)
315{
316    WebFrame* frame = kit(selectedFrame);
317    return [[[frame frameView] documentView] pasteboardTypesForSelection];
318}
319#endif
320
321#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
322void WebEditorClient::uppercaseWord()
323{
324    [m_webView uppercaseWord:nil];
325}
326
327void WebEditorClient::lowercaseWord()
328{
329    [m_webView lowercaseWord:nil];
330}
331
332void WebEditorClient::capitalizeWord()
333{
334    [m_webView capitalizeWord:nil];
335}
336
337void WebEditorClient::showSubstitutionsPanel(bool show)
338{
339    NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] substitutionsPanel];
340    if (show)
341        [spellingPanel orderFront:nil];
342    else
343        [spellingPanel orderOut:nil];
344}
345
346bool WebEditorClient::substitutionsPanelIsShowing()
347{
348    return [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
349}
350
351void WebEditorClient::toggleSmartInsertDelete()
352{
353    [m_webView toggleSmartInsertDelete:nil];
354}
355
356bool WebEditorClient::isAutomaticQuoteSubstitutionEnabled()
357{
358    return [m_webView isAutomaticQuoteSubstitutionEnabled];
359}
360
361void WebEditorClient::toggleAutomaticQuoteSubstitution()
362{
363    [m_webView toggleAutomaticQuoteSubstitution:nil];
364}
365
366bool WebEditorClient::isAutomaticLinkDetectionEnabled()
367{
368    return [m_webView isAutomaticLinkDetectionEnabled];
369}
370
371void WebEditorClient::toggleAutomaticLinkDetection()
372{
373    [m_webView toggleAutomaticLinkDetection:nil];
374}
375
376bool WebEditorClient::isAutomaticDashSubstitutionEnabled()
377{
378    return [m_webView isAutomaticDashSubstitutionEnabled];
379}
380
381void WebEditorClient::toggleAutomaticDashSubstitution()
382{
383    [m_webView toggleAutomaticDashSubstitution:nil];
384}
385
386bool WebEditorClient::isAutomaticTextReplacementEnabled()
387{
388    return [m_webView isAutomaticTextReplacementEnabled];
389}
390
391void WebEditorClient::toggleAutomaticTextReplacement()
392{
393    [m_webView toggleAutomaticTextReplacement:nil];
394}
395
396bool WebEditorClient::isAutomaticSpellingCorrectionEnabled()
397{
398    return [m_webView isAutomaticSpellingCorrectionEnabled];
399}
400
401void WebEditorClient::toggleAutomaticSpellingCorrection()
402{
403    [m_webView toggleAutomaticSpellingCorrection:nil];
404}
405#endif
406
407bool WebEditorClient::shouldInsertNode(Node *node, Range* replacingRange, EditorInsertAction givenAction)
408{
409    return [[m_webView _editingDelegateForwarder] webView:m_webView shouldInsertNode:kit(node) replacingDOMRange:kit(replacingRange) givenAction:(WebViewInsertAction)givenAction];
410}
411
412static NSString* undoNameForEditAction(EditAction editAction)
413{
414    switch (editAction) {
415        case EditActionUnspecified: return nil;
416        case EditActionSetColor: return UI_STRING_KEY("Set Color", "Set Color (Undo action name)", "Undo action name");
417        case EditActionSetBackgroundColor: return UI_STRING_KEY("Set Background Color", "Set Background Color (Undo action name)", "Undo action name");
418        case EditActionTurnOffKerning: return UI_STRING_KEY("Turn Off Kerning", "Turn Off Kerning (Undo action name)", "Undo action name");
419        case EditActionTightenKerning: return UI_STRING_KEY("Tighten Kerning", "Tighten Kerning (Undo action name)", "Undo action name");
420        case EditActionLoosenKerning: return UI_STRING_KEY("Loosen Kerning", "Loosen Kerning (Undo action name)", "Undo action name");
421        case EditActionUseStandardKerning: return UI_STRING_KEY("Use Standard Kerning", "Use Standard Kerning (Undo action name)", "Undo action name");
422        case EditActionTurnOffLigatures: return UI_STRING_KEY("Turn Off Ligatures", "Turn Off Ligatures (Undo action name)", "Undo action name");
423        case EditActionUseStandardLigatures: return UI_STRING_KEY("Use Standard Ligatures", "Use Standard Ligatures (Undo action name)", "Undo action name");
424        case EditActionUseAllLigatures: return UI_STRING_KEY("Use All Ligatures", "Use All Ligatures (Undo action name)", "Undo action name");
425        case EditActionRaiseBaseline: return UI_STRING_KEY("Raise Baseline", "Raise Baseline (Undo action name)", "Undo action name");
426        case EditActionLowerBaseline: return UI_STRING_KEY("Lower Baseline", "Lower Baseline (Undo action name)", "Undo action name");
427        case EditActionSetTraditionalCharacterShape: return UI_STRING_KEY("Set Traditional Character Shape", "Set Traditional Character Shape (Undo action name)", "Undo action name");
428        case EditActionSetFont: return UI_STRING_KEY("Set Font", "Set Font (Undo action name)", "Undo action name");
429        case EditActionChangeAttributes: return UI_STRING_KEY("Change Attributes", "Change Attributes (Undo action name)", "Undo action name");
430        case EditActionAlignLeft: return UI_STRING_KEY("Align Left", "Align Left (Undo action name)", "Undo action name");
431        case EditActionAlignRight: return UI_STRING_KEY("Align Right", "Align Right (Undo action name)", "Undo action name");
432        case EditActionCenter: return UI_STRING_KEY("Center", "Center (Undo action name)", "Undo action name");
433        case EditActionJustify: return UI_STRING_KEY("Justify", "Justify (Undo action name)", "Undo action name");
434        case EditActionSetWritingDirection: return UI_STRING_KEY("Set Writing Direction", "Set Writing Direction (Undo action name)", "Undo action name");
435        case EditActionSubscript: return UI_STRING_KEY("Subscript", "Subscript (Undo action name)", "Undo action name");
436        case EditActionSuperscript: return UI_STRING_KEY("Superscript", "Superscript (Undo action name)", "Undo action name");
437        case EditActionUnderline: return UI_STRING_KEY("Underline", "Underline (Undo action name)", "Undo action name");
438        case EditActionOutline: return UI_STRING_KEY("Outline", "Outline (Undo action name)", "Undo action name");
439        case EditActionUnscript: return UI_STRING_KEY("Unscript", "Unscript (Undo action name)", "Undo action name");
440        case EditActionDrag: return UI_STRING_KEY("Drag", "Drag (Undo action name)", "Undo action name");
441        case EditActionCut: return UI_STRING_KEY("Cut", "Cut (Undo action name)", "Undo action name");
442        case EditActionPaste: return UI_STRING_KEY("Paste", "Paste (Undo action name)", "Undo action name");
443        case EditActionPasteFont: return UI_STRING_KEY("Paste Font", "Paste Font (Undo action name)", "Undo action name");
444        case EditActionPasteRuler: return UI_STRING_KEY("Paste Ruler", "Paste Ruler (Undo action name)", "Undo action name");
445        case EditActionTyping: return UI_STRING_KEY("Typing", "Typing (Undo action name)", "Undo action name");
446        case EditActionCreateLink: return UI_STRING_KEY("Create Link", "Create Link (Undo action name)", "Undo action name");
447        case EditActionUnlink: return UI_STRING_KEY("Unlink", "Unlink (Undo action name)", "Undo action name");
448        case EditActionInsertList: return UI_STRING_KEY("Insert List", "Insert List (Undo action name)", "Undo action name");
449        case EditActionFormatBlock: return UI_STRING_KEY("Formatting", "Format Block (Undo action name)", "Undo action name");
450        case EditActionIndent: return UI_STRING_KEY("Indent", "Indent (Undo action name)", "Undo action name");
451        case EditActionOutdent: return UI_STRING_KEY("Outdent", "Outdent (Undo action name)", "Undo action name");
452    }
453    return nil;
454}
455
456void WebEditorClient::registerCommandForUndoOrRedo(PassRefPtr<EditCommand> cmd, bool isRedo)
457{
458    ASSERT(cmd);
459
460    NSUndoManager *undoManager = [m_webView undoManager];
461    NSString *actionName = undoNameForEditAction(cmd->editingAction());
462    WebEditCommand *command = [WebEditCommand commandWithEditCommand:cmd];
463    [undoManager registerUndoWithTarget:m_undoTarget.get() selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:command];
464    if (actionName)
465        [undoManager setActionName:actionName];
466    m_haveUndoRedoOperations = YES;
467}
468
469void WebEditorClient::registerCommandForUndo(PassRefPtr<EditCommand> cmd)
470{
471    registerCommandForUndoOrRedo(cmd, false);
472}
473
474void WebEditorClient::registerCommandForRedo(PassRefPtr<EditCommand> cmd)
475{
476    registerCommandForUndoOrRedo(cmd, true);
477}
478
479void WebEditorClient::clearUndoRedoOperations()
480{
481    if (m_haveUndoRedoOperations) {
482        // workaround for <rdar://problem/4645507> NSUndoManager dies
483        // with uncaught exception when undo items cleared while
484        // groups are open
485        NSUndoManager *undoManager = [m_webView undoManager];
486        int groupingLevel = [undoManager groupingLevel];
487        for (int i = 0; i < groupingLevel; ++i)
488            [undoManager endUndoGrouping];
489
490        [undoManager removeAllActionsWithTarget:m_undoTarget.get()];
491
492        for (int i = 0; i < groupingLevel; ++i)
493            [undoManager beginUndoGrouping];
494
495        m_haveUndoRedoOperations = NO;
496    }
497}
498
499bool WebEditorClient::canUndo() const
500{
501    return [[m_webView undoManager] canUndo];
502}
503
504bool WebEditorClient::canRedo() const
505{
506    return [[m_webView undoManager] canRedo];
507}
508
509void WebEditorClient::undo()
510{
511    if (canUndo())
512        [[m_webView undoManager] undo];
513}
514
515void WebEditorClient::redo()
516{
517    if (canRedo())
518        [[m_webView undoManager] redo];
519}
520
521void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
522{
523    Frame* frame = event->target()->toNode()->document()->frame();
524    WebHTMLView *webHTMLView = [[kit(frame) frameView] documentView];
525    if ([webHTMLView _interceptEditingKeyEvent:event shouldSaveCommand:NO])
526        event->setDefaultHandled();
527}
528
529void WebEditorClient::handleInputMethodKeydown(KeyboardEvent* event)
530{
531    Frame* frame = event->target()->toNode()->document()->frame();
532    WebHTMLView *webHTMLView = [[kit(frame) frameView] documentView];
533    if ([webHTMLView _interceptEditingKeyEvent:event shouldSaveCommand:YES])
534        event->setDefaultHandled();
535}
536
537#define FormDelegateLog(ctrl)  LOG(FormDelegate, "control=%@", ctrl)
538
539void WebEditorClient::textFieldDidBeginEditing(Element* element)
540{
541    if (!element->hasTagName(inputTag))
542        return;
543
544    DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
545    FormDelegateLog(inputElement);
546    CallFormDelegate(m_webView, @selector(textFieldDidBeginEditing:inFrame:), inputElement, kit(element->document()->frame()));
547}
548
549void WebEditorClient::textFieldDidEndEditing(Element* element)
550{
551    if (!element->hasTagName(inputTag))
552        return;
553
554    DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
555    FormDelegateLog(inputElement);
556    CallFormDelegate(m_webView, @selector(textFieldDidEndEditing:inFrame:), inputElement, kit(element->document()->frame()));
557}
558
559void WebEditorClient::textDidChangeInTextField(Element* element)
560{
561    if (!element->hasTagName(inputTag))
562        return;
563
564    DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
565    FormDelegateLog(inputElement);
566    CallFormDelegate(m_webView, @selector(textDidChangeInTextField:inFrame:), inputElement, kit(element->document()->frame()));
567}
568
569static SEL selectorForKeyEvent(KeyboardEvent* event)
570{
571    // FIXME: This helper function is for the auto-fill code so we can pass a selector to the form delegate.
572    // Eventually, we should move all of the auto-fill code down to WebKit and remove the need for this function by
573    // not relying on the selector in the new implementation.
574    // The key identifiers are from <http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set>
575    String key = event->keyIdentifier();
576    if (key == "Up")
577        return @selector(moveUp:);
578    if (key == "Down")
579        return @selector(moveDown:);
580    if (key == "U+001B")
581        return @selector(cancel:);
582    if (key == "U+0009") {
583        if (event->shiftKey())
584            return @selector(insertBacktab:);
585        return @selector(insertTab:);
586    }
587    if (key == "Enter")
588        return @selector(insertNewline:);
589    return 0;
590}
591
592bool WebEditorClient::doTextFieldCommandFromEvent(Element* element, KeyboardEvent* event)
593{
594    if (!element->hasTagName(inputTag))
595        return NO;
596
597    DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
598    FormDelegateLog(inputElement);
599    if (SEL commandSelector = selectorForKeyEvent(event))
600        return CallFormDelegateReturningBoolean(NO, m_webView, @selector(textField:doCommandBySelector:inFrame:), inputElement, commandSelector, kit(element->document()->frame()));
601    return NO;
602}
603
604void WebEditorClient::textWillBeDeletedInTextField(Element* element)
605{
606    if (!element->hasTagName(inputTag))
607        return;
608
609    DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
610    FormDelegateLog(inputElement);
611    // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
612    CallFormDelegateReturningBoolean(NO, m_webView, @selector(textField:doCommandBySelector:inFrame:), inputElement, @selector(deleteBackward:), kit(element->document()->frame()));
613}
614
615void WebEditorClient::textDidChangeInTextArea(Element* element)
616{
617    if (!element->hasTagName(textareaTag))
618        return;
619
620    DOMHTMLTextAreaElement* textAreaElement = kit(static_cast<HTMLTextAreaElement*>(element));
621    FormDelegateLog(textAreaElement);
622    CallFormDelegate(m_webView, @selector(textDidChangeInTextArea:inFrame:), textAreaElement, kit(element->document()->frame()));
623}
624
625void WebEditorClient::ignoreWordInSpellDocument(const String& text)
626{
627    [[NSSpellChecker sharedSpellChecker] ignoreWord:text
628                             inSpellDocumentWithTag:spellCheckerDocumentTag()];
629}
630
631void WebEditorClient::learnWord(const String& text)
632{
633    [[NSSpellChecker sharedSpellChecker] learnWord:text];
634}
635
636void WebEditorClient::checkSpellingOfString(const UChar* text, int length, int* misspellingLocation, int* misspellingLength)
637{
638    NSString* textString = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(text) length:length freeWhenDone:NO];
639    NSRange range = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString:textString startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:spellCheckerDocumentTag() wordCount:NULL];
640    [textString release];
641    if (misspellingLocation) {
642        // WebCore expects -1 to represent "not found"
643        if (range.location == NSNotFound)
644            *misspellingLocation = -1;
645        else
646            *misspellingLocation = range.location;
647    }
648
649    if (misspellingLength)
650        *misspellingLength = range.length;
651}
652
653String WebEditorClient::getAutoCorrectSuggestionForMisspelledWord(const String& inputWord)
654{
655    // This method can be implemented using customized algorithms for the particular browser.
656    // Currently, it computes an empty string.
657    return String();
658}
659
660void WebEditorClient::checkGrammarOfString(const UChar* text, int length, Vector<GrammarDetail>& details, int* badGrammarLocation, int* badGrammarLength)
661{
662#ifndef BUILDING_ON_TIGER
663    NSArray *grammarDetails;
664    NSString* textString = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(text) length:length freeWhenDone:NO];
665    NSRange range = [[NSSpellChecker sharedSpellChecker] checkGrammarOfString:textString startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:spellCheckerDocumentTag() details:&grammarDetails];
666    [textString release];
667    if (badGrammarLocation)
668        // WebCore expects -1 to represent "not found"
669        *badGrammarLocation = (range.location == NSNotFound) ? -1 : range.location;
670    if (badGrammarLength)
671        *badGrammarLength = range.length;
672    for (NSDictionary *detail in grammarDetails) {
673        ASSERT(detail);
674        GrammarDetail grammarDetail;
675        NSValue *detailRangeAsNSValue = [detail objectForKey:NSGrammarRange];
676        ASSERT(detailRangeAsNSValue);
677        NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
678        ASSERT(detailNSRange.location != NSNotFound);
679        ASSERT(detailNSRange.length > 0);
680        grammarDetail.location = detailNSRange.location;
681        grammarDetail.length = detailNSRange.length;
682        grammarDetail.userDescription = [detail objectForKey:NSGrammarUserDescription];
683        NSArray *guesses = [detail objectForKey:NSGrammarCorrections];
684        for (NSString *guess in guesses)
685            grammarDetail.guesses.append(String(guess));
686        details.append(grammarDetail);
687    }
688#endif
689}
690
691void WebEditorClient::checkTextOfParagraph(const UChar* text, int length, uint64_t checkingTypes, Vector<TextCheckingResult>& results)
692{
693#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
694    NSString *textString = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(text) length:length freeWhenDone:NO];
695    NSArray *incomingResults = [[NSSpellChecker sharedSpellChecker] checkString:textString range:NSMakeRange(0, [textString length]) types:(checkingTypes|NSTextCheckingTypeOrthography) options:nil inSpellDocumentWithTag:spellCheckerDocumentTag() orthography:NULL wordCount:NULL];
696    [textString release];
697    for (NSTextCheckingResult *incomingResult in incomingResults) {
698        NSRange resultRange = [incomingResult range];
699        NSTextCheckingType resultType = [incomingResult resultType];
700        ASSERT(resultRange.location != NSNotFound);
701        ASSERT(resultRange.length > 0);
702        if (NSTextCheckingTypeSpelling == resultType && 0 != (checkingTypes & NSTextCheckingTypeSpelling)) {
703            TextCheckingResult result;
704            result.type = TextCheckingTypeSpelling;
705            result.location = resultRange.location;
706            result.length = resultRange.length;
707            results.append(result);
708        } else if (NSTextCheckingTypeGrammar == resultType && 0 != (checkingTypes & NSTextCheckingTypeGrammar)) {
709            TextCheckingResult result;
710            NSArray *details = [incomingResult grammarDetails];
711            result.type = TextCheckingTypeGrammar;
712            result.location = resultRange.location;
713            result.length = resultRange.length;
714            for (NSDictionary *incomingDetail in details) {
715                ASSERT(incomingDetail);
716                GrammarDetail detail;
717                NSValue *detailRangeAsNSValue = [incomingDetail objectForKey:NSGrammarRange];
718                ASSERT(detailRangeAsNSValue);
719                NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
720                ASSERT(detailNSRange.location != NSNotFound);
721                ASSERT(detailNSRange.length > 0);
722                detail.location = detailNSRange.location;
723                detail.length = detailNSRange.length;
724                detail.userDescription = [incomingDetail objectForKey:NSGrammarUserDescription];
725                NSArray *guesses = [incomingDetail objectForKey:NSGrammarCorrections];
726                for (NSString *guess in guesses)
727                    detail.guesses.append(String(guess));
728                result.details.append(detail);
729            }
730            results.append(result);
731        } else if (NSTextCheckingTypeLink == resultType && 0 != (checkingTypes & NSTextCheckingTypeLink)) {
732            TextCheckingResult result;
733            result.type = TextCheckingTypeLink;
734            result.location = resultRange.location;
735            result.length = resultRange.length;
736            result.replacement = [[incomingResult URL] absoluteString];
737            results.append(result);
738        } else if (NSTextCheckingTypeQuote == resultType && 0 != (checkingTypes & NSTextCheckingTypeQuote)) {
739            TextCheckingResult result;
740            result.type = TextCheckingTypeQuote;
741            result.location = resultRange.location;
742            result.length = resultRange.length;
743            result.replacement = [incomingResult replacementString];
744            results.append(result);
745        } else if (NSTextCheckingTypeDash == resultType && 0 != (checkingTypes & NSTextCheckingTypeDash)) {
746            TextCheckingResult result;
747            result.type = TextCheckingTypeDash;
748            result.location = resultRange.location;
749            result.length = resultRange.length;
750            result.replacement = [incomingResult replacementString];
751            results.append(result);
752        } else if (NSTextCheckingTypeReplacement == resultType && 0 != (checkingTypes & NSTextCheckingTypeReplacement)) {
753            TextCheckingResult result;
754            result.type = TextCheckingTypeReplacement;
755            result.location = resultRange.location;
756            result.length = resultRange.length;
757            result.replacement = [incomingResult replacementString];
758            results.append(result);
759        } else if (NSTextCheckingTypeCorrection == resultType && 0 != (checkingTypes & NSTextCheckingTypeCorrection)) {
760            TextCheckingResult result;
761            result.type = TextCheckingTypeCorrection;
762            result.location = resultRange.location;
763            result.length = resultRange.length;
764            result.replacement = [incomingResult replacementString];
765            results.append(result);
766        }
767    }
768#endif
769}
770
771void WebEditorClient::updateSpellingUIWithGrammarString(const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
772{
773#ifndef BUILDING_ON_TIGER
774    NSMutableArray* corrections = [NSMutableArray array];
775    for (unsigned i = 0; i < grammarDetail.guesses.size(); i++) {
776        NSString* guess = grammarDetail.guesses[i];
777        [corrections addObject:guess];
778    }
779    NSRange grammarRange = NSMakeRange(grammarDetail.location, grammarDetail.length);
780    NSString* grammarUserDescription = grammarDetail.userDescription;
781    NSMutableDictionary* grammarDetailDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithRange:grammarRange], NSGrammarRange, grammarUserDescription, NSGrammarUserDescription, corrections, NSGrammarCorrections, nil];
782
783    [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetailDict];
784#endif
785}
786
787void WebEditorClient::updateSpellingUIWithMisspelledWord(const String& misspelledWord)
788{
789    [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithMisspelledWord:misspelledWord];
790}
791
792void WebEditorClient::showSpellingUI(bool show)
793{
794    NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] spellingPanel];
795    if (show)
796        [spellingPanel orderFront:nil];
797    else
798        [spellingPanel orderOut:nil];
799}
800
801bool WebEditorClient::spellingUIIsShowing()
802{
803    return [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
804}
805
806void WebEditorClient::getGuessesForWord(const String& word, WTF::Vector<String>& guesses)
807{
808    NSArray* stringsArray = [[NSSpellChecker sharedSpellChecker] guessesForWord:word];
809    unsigned count = [stringsArray count];
810    guesses.clear();
811    if (count > 0) {
812        NSEnumerator* enumerator = [stringsArray objectEnumerator];
813        NSString* string;
814        while ((string = [enumerator nextObject]) != nil)
815            guesses.append(string);
816    }
817}
818
819void WebEditorClient::setInputMethodState(bool)
820{
821}
822