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