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