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