Home | History | Annotate | Download | only in WebCoreSupport
      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 
     66 using namespace WebCore;
     67 using namespace WTF;
     68 
     69 using namespace HTMLNames;
     70 
     71 static 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 
    162 void WebEditorClient::pageDestroyed()
    163 {
    164     delete this;
    165 }
    166 
    167 WebEditorClient::WebEditorClient(WebView *webView)
    168     : m_webView(webView)
    169     , m_undoTarget([[[WebEditorUndoTarget alloc] init] autorelease])
    170     , m_haveUndoRedoOperations(false)
    171 {
    172 }
    173 
    174 bool WebEditorClient::isContinuousSpellCheckingEnabled()
    175 {
    176     return [m_webView isContinuousSpellCheckingEnabled];
    177 }
    178 
    179 void WebEditorClient::toggleContinuousSpellChecking()
    180 {
    181     [m_webView toggleContinuousSpellChecking:nil];
    182 }
    183 
    184 bool WebEditorClient::isGrammarCheckingEnabled()
    185 {
    186 #ifdef BUILDING_ON_TIGER
    187     return false;
    188 #else
    189     return [m_webView isGrammarCheckingEnabled];
    190 #endif
    191 }
    192 
    193 void WebEditorClient::toggleGrammarChecking()
    194 {
    195 #ifndef BUILDING_ON_TIGER
    196     [m_webView toggleGrammarChecking:nil];
    197 #endif
    198 }
    199 
    200 int WebEditorClient::spellCheckerDocumentTag()
    201 {
    202     return [m_webView spellCheckerDocumentTag];
    203 }
    204 
    205 bool WebEditorClient::isEditable()
    206 {
    207     return [m_webView isEditable];
    208 }
    209 
    210 bool WebEditorClient::shouldDeleteRange(Range* range)
    211 {
    212     return [[m_webView _editingDelegateForwarder] webView:m_webView
    213         shouldDeleteDOMRange:kit(range)];
    214 }
    215 
    216 bool WebEditorClient::shouldShowDeleteInterface(HTMLElement* element)
    217 {
    218     return [[m_webView _editingDelegateForwarder] webView:m_webView
    219         shouldShowDeleteInterfaceForElement:kit(element)];
    220 }
    221 
    222 bool WebEditorClient::smartInsertDeleteEnabled()
    223 {
    224     return [m_webView smartInsertDeleteEnabled];
    225 }
    226 
    227 bool WebEditorClient::isSelectTrailingWhitespaceEnabled()
    228 {
    229     return [m_webView isSelectTrailingWhitespaceEnabled];
    230 }
    231 
    232 bool WebEditorClient::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
    233 {
    234     return [[m_webView _editingDelegateForwarder] webView:m_webView
    235         shouldApplyStyle:kit(style) toElementsInDOMRange:kit(range)];
    236 }
    237 
    238 bool WebEditorClient::shouldMoveRangeAfterDelete(Range* range, Range* rangeToBeReplaced)
    239 {
    240     return [[m_webView _editingDelegateForwarder] webView:m_webView
    241         shouldMoveRangeAfterDelete:kit(range) replacingRange:kit(rangeToBeReplaced)];
    242 }
    243 
    244 bool WebEditorClient::shouldBeginEditing(Range* range)
    245 {
    246     return [[m_webView _editingDelegateForwarder] webView:m_webView
    247         shouldBeginEditingInDOMRange:kit(range)];
    248 
    249     return false;
    250 }
    251 
    252 bool WebEditorClient::shouldEndEditing(Range* range)
    253 {
    254     return [[m_webView _editingDelegateForwarder] webView:m_webView
    255                              shouldEndEditingInDOMRange:kit(range)];
    256 }
    257 
    258 bool 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 
    264 bool 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 
    269 void WebEditorClient::didBeginEditing()
    270 {
    271     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidBeginEditingNotification object:m_webView];
    272 }
    273 
    274 void 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 
    282 void 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 
    293 void WebEditorClient::didEndEditing()
    294 {
    295     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidEndEditingNotification object:m_webView];
    296 }
    297 
    298 void WebEditorClient::didWriteSelectionToPasteboard()
    299 {
    300     [[m_webView _editingDelegateForwarder] webView:m_webView didWriteSelectionToPasteboard:[NSPasteboard generalPasteboard]];
    301 }
    302 
    303 void WebEditorClient::didSetSelectionTypesForPasteboard()
    304 {
    305     [[m_webView _editingDelegateForwarder] webView:m_webView didSetSelectionTypesForPasteboard:[NSPasteboard generalPasteboard]];
    306 }
    307 
    308 NSString* WebEditorClient::userVisibleString(NSURL *URL)
    309 {
    310     return [URL _web_userVisibleString];
    311 }
    312 
    313 #ifdef BUILDING_ON_TIGER
    314 NSArray* 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)
    322 void WebEditorClient::uppercaseWord()
    323 {
    324     [m_webView uppercaseWord:nil];
    325 }
    326 
    327 void WebEditorClient::lowercaseWord()
    328 {
    329     [m_webView lowercaseWord:nil];
    330 }
    331 
    332 void WebEditorClient::capitalizeWord()
    333 {
    334     [m_webView capitalizeWord:nil];
    335 }
    336 
    337 void 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 
    346 bool WebEditorClient::substitutionsPanelIsShowing()
    347 {
    348     return [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
    349 }
    350 
    351 void WebEditorClient::toggleSmartInsertDelete()
    352 {
    353     [m_webView toggleSmartInsertDelete:nil];
    354 }
    355 
    356 bool WebEditorClient::isAutomaticQuoteSubstitutionEnabled()
    357 {
    358     return [m_webView isAutomaticQuoteSubstitutionEnabled];
    359 }
    360 
    361 void WebEditorClient::toggleAutomaticQuoteSubstitution()
    362 {
    363     [m_webView toggleAutomaticQuoteSubstitution:nil];
    364 }
    365 
    366 bool WebEditorClient::isAutomaticLinkDetectionEnabled()
    367 {
    368     return [m_webView isAutomaticLinkDetectionEnabled];
    369 }
    370 
    371 void WebEditorClient::toggleAutomaticLinkDetection()
    372 {
    373     [m_webView toggleAutomaticLinkDetection:nil];
    374 }
    375 
    376 bool WebEditorClient::isAutomaticDashSubstitutionEnabled()
    377 {
    378     return [m_webView isAutomaticDashSubstitutionEnabled];
    379 }
    380 
    381 void WebEditorClient::toggleAutomaticDashSubstitution()
    382 {
    383     [m_webView toggleAutomaticDashSubstitution:nil];
    384 }
    385 
    386 bool WebEditorClient::isAutomaticTextReplacementEnabled()
    387 {
    388     return [m_webView isAutomaticTextReplacementEnabled];
    389 }
    390 
    391 void WebEditorClient::toggleAutomaticTextReplacement()
    392 {
    393     [m_webView toggleAutomaticTextReplacement:nil];
    394 }
    395 
    396 bool WebEditorClient::isAutomaticSpellingCorrectionEnabled()
    397 {
    398     return [m_webView isAutomaticSpellingCorrectionEnabled];
    399 }
    400 
    401 void WebEditorClient::toggleAutomaticSpellingCorrection()
    402 {
    403     [m_webView toggleAutomaticSpellingCorrection:nil];
    404 }
    405 #endif
    406 
    407 bool 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 
    412 static 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 
    456 void 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 
    469 void WebEditorClient::registerCommandForUndo(PassRefPtr<EditCommand> cmd)
    470 {
    471     registerCommandForUndoOrRedo(cmd, false);
    472 }
    473 
    474 void WebEditorClient::registerCommandForRedo(PassRefPtr<EditCommand> cmd)
    475 {
    476     registerCommandForUndoOrRedo(cmd, true);
    477 }
    478 
    479 void 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 
    499 bool WebEditorClient::canUndo() const
    500 {
    501     return [[m_webView undoManager] canUndo];
    502 }
    503 
    504 bool WebEditorClient::canRedo() const
    505 {
    506     return [[m_webView undoManager] canRedo];
    507 }
    508 
    509 void WebEditorClient::undo()
    510 {
    511     if (canUndo())
    512         [[m_webView undoManager] undo];
    513 }
    514 
    515 void WebEditorClient::redo()
    516 {
    517     if (canRedo())
    518         [[m_webView undoManager] redo];
    519 }
    520 
    521 void 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 
    529 void 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 
    539 void 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 
    549 void 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 
    559 void 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 
    569 static 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 
    592 bool 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 
    604 void 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 
    615 void 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 
    625 void WebEditorClient::ignoreWordInSpellDocument(const String& text)
    626 {
    627     [[NSSpellChecker sharedSpellChecker] ignoreWord:text
    628                              inSpellDocumentWithTag:spellCheckerDocumentTag()];
    629 }
    630 
    631 void WebEditorClient::learnWord(const String& text)
    632 {
    633     [[NSSpellChecker sharedSpellChecker] learnWord:text];
    634 }
    635 
    636 void 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 
    653 String 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 
    660 void 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 
    691 void 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 
    771 void 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 
    787 void WebEditorClient::updateSpellingUIWithMisspelledWord(const String& misspelledWord)
    788 {
    789     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithMisspelledWord:misspelledWord];
    790 }
    791 
    792 void 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 
    801 bool WebEditorClient::spellingUIIsShowing()
    802 {
    803     return [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
    804 }
    805 
    806 void 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 
    819 void WebEditorClient::setInputMethodState(bool)
    820 {
    821 }
    822