1 /* 2 * Copyright (C) 2006, 2007 Apple, Inc. All rights reserved. 3 * Copyright (C) 2010 Google, Inc. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #include "EditorClientImpl.h" 29 30 #include "Document.h" 31 #include "EditCommand.h" 32 #include "Editor.h" 33 #include "EventHandler.h" 34 #include "EventNames.h" 35 #include "Frame.h" 36 #include "HTMLInputElement.h" 37 #include "HTMLNames.h" 38 #include "KeyboardCodes.h" 39 #include "KeyboardEvent.h" 40 #include "PlatformKeyboardEvent.h" 41 #include "PlatformString.h" 42 #include "RenderObject.h" 43 44 #include "DOMUtilitiesPrivate.h" 45 #include "WebEditingAction.h" 46 #include "WebFrameImpl.h" 47 #include "WebKit.h" 48 #include "WebInputElement.h" 49 #include "WebNode.h" 50 #include "WebPasswordAutocompleteListener.h" 51 #include "WebRange.h" 52 #include "WebTextAffinity.h" 53 #include "WebViewClient.h" 54 #include "WebViewImpl.h" 55 56 using namespace WebCore; 57 58 namespace WebKit { 59 60 // Arbitrary depth limit for the undo stack, to keep it from using 61 // unbounded memory. This is the maximum number of distinct undoable 62 // actions -- unbroken stretches of typed characters are coalesced 63 // into a single action. 64 static const size_t maximumUndoStackDepth = 1000; 65 66 // The size above which we stop triggering autofill for an input text field 67 // (so to avoid sending long strings through IPC). 68 static const size_t maximumTextSizeForAutofill = 1000; 69 70 EditorClientImpl::EditorClientImpl(WebViewImpl* webview) 71 : m_webView(webview) 72 , m_inRedo(false) 73 , m_backspaceOrDeletePressed(false) 74 , m_spellCheckThisFieldStatus(SpellCheckAutomatic) 75 , m_autofillTimer(this, &EditorClientImpl::doAutofill) 76 { 77 } 78 79 EditorClientImpl::~EditorClientImpl() 80 { 81 } 82 83 void EditorClientImpl::pageDestroyed() 84 { 85 // Our lifetime is bound to the WebViewImpl. 86 } 87 88 bool EditorClientImpl::shouldShowDeleteInterface(HTMLElement* elem) 89 { 90 // Normally, we don't care to show WebCore's deletion UI, so we only enable 91 // it if in testing mode and the test specifically requests it by using this 92 // magic class name. 93 return WebKit::layoutTestMode() 94 && elem->getAttribute(HTMLNames::classAttr) == "needsDeletionUI"; 95 } 96 97 bool EditorClientImpl::smartInsertDeleteEnabled() 98 { 99 if (m_webView->client()) 100 return m_webView->client()->isSmartInsertDeleteEnabled(); 101 return true; 102 } 103 104 bool EditorClientImpl::isSelectTrailingWhitespaceEnabled() 105 { 106 if (m_webView->client()) 107 return m_webView->client()->isSelectTrailingWhitespaceEnabled(); 108 #if OS(WINDOWS) 109 return true; 110 #else 111 return false; 112 #endif 113 } 114 115 bool EditorClientImpl::shouldSpellcheckByDefault() 116 { 117 // Spellcheck should be enabled for all editable areas (such as textareas, 118 // contentEditable regions, and designMode docs), except text inputs. 119 const Frame* frame = m_webView->focusedWebCoreFrame(); 120 if (!frame) 121 return false; 122 const Editor* editor = frame->editor(); 123 if (!editor) 124 return false; 125 if (editor->spellCheckingEnabledInFocusedNode()) 126 return true; 127 const Document* document = frame->document(); 128 if (!document) 129 return false; 130 const Node* node = document->focusedNode(); 131 // If |node| is null, we default to allowing spellchecking. This is done in 132 // order to mitigate the issue when the user clicks outside the textbox, as a 133 // result of which |node| becomes null, resulting in all the spell check 134 // markers being deleted. Also, the Frame will decide not to do spellchecking 135 // if the user can't edit - so returning true here will not cause any problems 136 // to the Frame's behavior. 137 if (!node) 138 return true; 139 const RenderObject* renderer = node->renderer(); 140 if (!renderer) 141 return false; 142 143 return !renderer->isTextField(); 144 } 145 146 bool EditorClientImpl::isContinuousSpellCheckingEnabled() 147 { 148 if (m_spellCheckThisFieldStatus == SpellCheckForcedOff) 149 return false; 150 if (m_spellCheckThisFieldStatus == SpellCheckForcedOn) 151 return true; 152 return shouldSpellcheckByDefault(); 153 } 154 155 void EditorClientImpl::toggleContinuousSpellChecking() 156 { 157 if (isContinuousSpellCheckingEnabled()) 158 m_spellCheckThisFieldStatus = SpellCheckForcedOff; 159 else 160 m_spellCheckThisFieldStatus = SpellCheckForcedOn; 161 } 162 163 bool EditorClientImpl::isGrammarCheckingEnabled() 164 { 165 return false; 166 } 167 168 void EditorClientImpl::toggleGrammarChecking() 169 { 170 notImplemented(); 171 } 172 173 int EditorClientImpl::spellCheckerDocumentTag() 174 { 175 ASSERT_NOT_REACHED(); 176 return 0; 177 } 178 179 bool EditorClientImpl::isEditable() 180 { 181 return false; 182 } 183 184 bool EditorClientImpl::shouldBeginEditing(Range* range) 185 { 186 if (m_webView->client()) 187 return m_webView->client()->shouldBeginEditing(WebRange(range)); 188 return true; 189 } 190 191 bool EditorClientImpl::shouldEndEditing(Range* range) 192 { 193 if (m_webView->client()) 194 return m_webView->client()->shouldEndEditing(WebRange(range)); 195 return true; 196 } 197 198 bool EditorClientImpl::shouldInsertNode(Node* node, 199 Range* range, 200 EditorInsertAction action) 201 { 202 if (m_webView->client()) { 203 return m_webView->client()->shouldInsertNode(WebNode(node), 204 WebRange(range), 205 static_cast<WebEditingAction>(action)); 206 } 207 return true; 208 } 209 210 bool EditorClientImpl::shouldInsertText(const String& text, 211 Range* range, 212 EditorInsertAction action) 213 { 214 if (m_webView->client()) { 215 return m_webView->client()->shouldInsertText(WebString(text), 216 WebRange(range), 217 static_cast<WebEditingAction>(action)); 218 } 219 return true; 220 } 221 222 223 bool EditorClientImpl::shouldDeleteRange(Range* range) 224 { 225 if (m_webView->client()) 226 return m_webView->client()->shouldDeleteRange(WebRange(range)); 227 return true; 228 } 229 230 bool EditorClientImpl::shouldChangeSelectedRange(Range* fromRange, 231 Range* toRange, 232 EAffinity affinity, 233 bool stillSelecting) 234 { 235 if (m_webView->client()) { 236 return m_webView->client()->shouldChangeSelectedRange(WebRange(fromRange), 237 WebRange(toRange), 238 static_cast<WebTextAffinity>(affinity), 239 stillSelecting); 240 } 241 return true; 242 } 243 244 bool EditorClientImpl::shouldApplyStyle(CSSStyleDeclaration* style, 245 Range* range) 246 { 247 if (m_webView->client()) { 248 // FIXME: Pass a reference to the CSSStyleDeclaration somehow. 249 return m_webView->client()->shouldApplyStyle(WebString(), 250 WebRange(range)); 251 } 252 return true; 253 } 254 255 bool EditorClientImpl::shouldMoveRangeAfterDelete(Range* range, 256 Range* rangeToBeReplaced) 257 { 258 return true; 259 } 260 261 void EditorClientImpl::didBeginEditing() 262 { 263 if (m_webView->client()) 264 m_webView->client()->didBeginEditing(); 265 } 266 267 void EditorClientImpl::respondToChangedSelection() 268 { 269 if (m_webView->client()) { 270 Frame* frame = m_webView->focusedWebCoreFrame(); 271 if (frame) 272 m_webView->client()->didChangeSelection(!frame->selection()->isRange()); 273 } 274 } 275 276 void EditorClientImpl::respondToChangedContents() 277 { 278 if (m_webView->client()) 279 m_webView->client()->didChangeContents(); 280 } 281 282 void EditorClientImpl::didEndEditing() 283 { 284 if (m_webView->client()) 285 m_webView->client()->didEndEditing(); 286 } 287 288 void EditorClientImpl::didWriteSelectionToPasteboard() 289 { 290 } 291 292 void EditorClientImpl::didSetSelectionTypesForPasteboard() 293 { 294 } 295 296 void EditorClientImpl::registerCommandForUndo(PassRefPtr<EditCommand> command) 297 { 298 if (m_undoStack.size() == maximumUndoStackDepth) 299 m_undoStack.removeFirst(); // drop oldest item off the far end 300 if (!m_inRedo) 301 m_redoStack.clear(); 302 m_undoStack.append(command); 303 } 304 305 void EditorClientImpl::registerCommandForRedo(PassRefPtr<EditCommand> command) 306 { 307 m_redoStack.append(command); 308 } 309 310 void EditorClientImpl::clearUndoRedoOperations() 311 { 312 m_undoStack.clear(); 313 m_redoStack.clear(); 314 } 315 316 bool EditorClientImpl::canUndo() const 317 { 318 return !m_undoStack.isEmpty(); 319 } 320 321 bool EditorClientImpl::canRedo() const 322 { 323 return !m_redoStack.isEmpty(); 324 } 325 326 void EditorClientImpl::undo() 327 { 328 if (canUndo()) { 329 EditCommandStack::iterator back = --m_undoStack.end(); 330 RefPtr<EditCommand> command(*back); 331 m_undoStack.remove(back); 332 command->unapply(); 333 // unapply will call us back to push this command onto the redo stack. 334 } 335 } 336 337 void EditorClientImpl::redo() 338 { 339 if (canRedo()) { 340 EditCommandStack::iterator back = --m_redoStack.end(); 341 RefPtr<EditCommand> command(*back); 342 m_redoStack.remove(back); 343 344 ASSERT(!m_inRedo); 345 m_inRedo = true; 346 command->reapply(); 347 // reapply will call us back to push this command onto the undo stack. 348 m_inRedo = false; 349 } 350 } 351 352 // 353 // The below code was adapted from the WebKit file webview.cpp 354 // 355 356 static const unsigned CtrlKey = 1 << 0; 357 static const unsigned AltKey = 1 << 1; 358 static const unsigned ShiftKey = 1 << 2; 359 static const unsigned MetaKey = 1 << 3; 360 #if OS(DARWIN) 361 // Aliases for the generic key defintions to make kbd shortcuts definitions more 362 // readable on OS X. 363 static const unsigned OptionKey = AltKey; 364 365 // Do not use this constant for anything but cursor movement commands. Keys 366 // with cmd set have their |isSystemKey| bit set, so chances are the shortcut 367 // will not be executed. Another, less important, reason is that shortcuts 368 // defined in the renderer do not blink the menu item that they triggered. See 369 // http://crbug.com/25856 and the bugs linked from there for details. 370 static const unsigned CommandKey = MetaKey; 371 #endif 372 373 // Keys with special meaning. These will be delegated to the editor using 374 // the execCommand() method 375 struct KeyDownEntry { 376 unsigned virtualKey; 377 unsigned modifiers; 378 const char* name; 379 }; 380 381 struct KeyPressEntry { 382 unsigned charCode; 383 unsigned modifiers; 384 const char* name; 385 }; 386 387 static const KeyDownEntry keyDownEntries[] = { 388 { VKEY_LEFT, 0, "MoveLeft" }, 389 { VKEY_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, 390 #if OS(DARWIN) 391 { VKEY_LEFT, OptionKey, "MoveWordLeft" }, 392 { VKEY_LEFT, OptionKey | ShiftKey, 393 "MoveWordLeftAndModifySelection" }, 394 #else 395 { VKEY_LEFT, CtrlKey, "MoveWordLeft" }, 396 { VKEY_LEFT, CtrlKey | ShiftKey, 397 "MoveWordLeftAndModifySelection" }, 398 #endif 399 { VKEY_RIGHT, 0, "MoveRight" }, 400 { VKEY_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, 401 #if OS(DARWIN) 402 { VKEY_RIGHT, OptionKey, "MoveWordRight" }, 403 { VKEY_RIGHT, OptionKey | ShiftKey, 404 "MoveWordRightAndModifySelection" }, 405 #else 406 { VKEY_RIGHT, CtrlKey, "MoveWordRight" }, 407 { VKEY_RIGHT, CtrlKey | ShiftKey, 408 "MoveWordRightAndModifySelection" }, 409 #endif 410 { VKEY_UP, 0, "MoveUp" }, 411 { VKEY_UP, ShiftKey, "MoveUpAndModifySelection" }, 412 { VKEY_PRIOR, ShiftKey, "MovePageUpAndModifySelection" }, 413 { VKEY_DOWN, 0, "MoveDown" }, 414 { VKEY_DOWN, ShiftKey, "MoveDownAndModifySelection" }, 415 { VKEY_NEXT, ShiftKey, "MovePageDownAndModifySelection" }, 416 { VKEY_PRIOR, 0, "MovePageUp" }, 417 { VKEY_NEXT, 0, "MovePageDown" }, 418 { VKEY_HOME, 0, "MoveToBeginningOfLine" }, 419 { VKEY_HOME, ShiftKey, 420 "MoveToBeginningOfLineAndModifySelection" }, 421 #if OS(DARWIN) 422 { VKEY_LEFT, CommandKey, "MoveToBeginningOfLine" }, 423 { VKEY_LEFT, CommandKey | ShiftKey, 424 "MoveToBeginningOfLineAndModifySelection" }, 425 #endif 426 #if OS(DARWIN) 427 { VKEY_UP, CommandKey, "MoveToBeginningOfDocument" }, 428 { VKEY_UP, CommandKey | ShiftKey, 429 "MoveToBeginningOfDocumentAndModifySelection" }, 430 #else 431 { VKEY_HOME, CtrlKey, "MoveToBeginningOfDocument" }, 432 { VKEY_HOME, CtrlKey | ShiftKey, 433 "MoveToBeginningOfDocumentAndModifySelection" }, 434 #endif 435 { VKEY_END, 0, "MoveToEndOfLine" }, 436 { VKEY_END, ShiftKey, "MoveToEndOfLineAndModifySelection" }, 437 #if OS(DARWIN) 438 { VKEY_DOWN, CommandKey, "MoveToEndOfDocument" }, 439 { VKEY_DOWN, CommandKey | ShiftKey, 440 "MoveToEndOfDocumentAndModifySelection" }, 441 #else 442 { VKEY_END, CtrlKey, "MoveToEndOfDocument" }, 443 { VKEY_END, CtrlKey | ShiftKey, 444 "MoveToEndOfDocumentAndModifySelection" }, 445 #endif 446 #if OS(DARWIN) 447 { VKEY_RIGHT, CommandKey, "MoveToEndOfLine" }, 448 { VKEY_RIGHT, CommandKey | ShiftKey, 449 "MoveToEndOfLineAndModifySelection" }, 450 #endif 451 { VKEY_BACK, 0, "DeleteBackward" }, 452 { VKEY_BACK, ShiftKey, "DeleteBackward" }, 453 { VKEY_DELETE, 0, "DeleteForward" }, 454 #if OS(DARWIN) 455 { VKEY_BACK, OptionKey, "DeleteWordBackward" }, 456 { VKEY_DELETE, OptionKey, "DeleteWordForward" }, 457 #else 458 { VKEY_BACK, CtrlKey, "DeleteWordBackward" }, 459 { VKEY_DELETE, CtrlKey, "DeleteWordForward" }, 460 #endif 461 { 'B', CtrlKey, "ToggleBold" }, 462 { 'I', CtrlKey, "ToggleItalic" }, 463 { 'U', CtrlKey, "ToggleUnderline" }, 464 { VKEY_ESCAPE, 0, "Cancel" }, 465 { VKEY_OEM_PERIOD, CtrlKey, "Cancel" }, 466 { VKEY_TAB, 0, "InsertTab" }, 467 { VKEY_TAB, ShiftKey, "InsertBacktab" }, 468 { VKEY_RETURN, 0, "InsertNewline" }, 469 { VKEY_RETURN, CtrlKey, "InsertNewline" }, 470 { VKEY_RETURN, AltKey, "InsertNewline" }, 471 { VKEY_RETURN, AltKey | ShiftKey, "InsertNewline" }, 472 { VKEY_RETURN, ShiftKey, "InsertLineBreak" }, 473 { VKEY_INSERT, CtrlKey, "Copy" }, 474 { VKEY_INSERT, ShiftKey, "Paste" }, 475 { VKEY_DELETE, ShiftKey, "Cut" }, 476 #if !OS(DARWIN) 477 // On OS X, we pipe these back to the browser, so that it can do menu item 478 // blinking. 479 { 'C', CtrlKey, "Copy" }, 480 { 'V', CtrlKey, "Paste" }, 481 { 'V', CtrlKey | ShiftKey, "PasteAndMatchStyle" }, 482 { 'X', CtrlKey, "Cut" }, 483 { 'A', CtrlKey, "SelectAll" }, 484 { 'Z', CtrlKey, "Undo" }, 485 { 'Z', CtrlKey | ShiftKey, "Redo" }, 486 { 'Y', CtrlKey, "Redo" }, 487 #endif 488 }; 489 490 static const KeyPressEntry keyPressEntries[] = { 491 { '\t', 0, "InsertTab" }, 492 { '\t', ShiftKey, "InsertBacktab" }, 493 { '\r', 0, "InsertNewline" }, 494 { '\r', CtrlKey, "InsertNewline" }, 495 { '\r', ShiftKey, "InsertLineBreak" }, 496 { '\r', AltKey, "InsertNewline" }, 497 { '\r', AltKey | ShiftKey, "InsertNewline" }, 498 }; 499 500 const char* EditorClientImpl::interpretKeyEvent(const KeyboardEvent* evt) 501 { 502 const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); 503 if (!keyEvent) 504 return ""; 505 506 static HashMap<int, const char*>* keyDownCommandsMap = 0; 507 static HashMap<int, const char*>* keyPressCommandsMap = 0; 508 509 if (!keyDownCommandsMap) { 510 keyDownCommandsMap = new HashMap<int, const char*>; 511 keyPressCommandsMap = new HashMap<int, const char*>; 512 513 for (unsigned i = 0; i < arraysize(keyDownEntries); i++) { 514 keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, 515 keyDownEntries[i].name); 516 } 517 518 for (unsigned i = 0; i < arraysize(keyPressEntries); i++) { 519 keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, 520 keyPressEntries[i].name); 521 } 522 } 523 524 unsigned modifiers = 0; 525 if (keyEvent->shiftKey()) 526 modifiers |= ShiftKey; 527 if (keyEvent->altKey()) 528 modifiers |= AltKey; 529 if (keyEvent->ctrlKey()) 530 modifiers |= CtrlKey; 531 if (keyEvent->metaKey()) 532 modifiers |= MetaKey; 533 534 if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) { 535 int mapKey = modifiers << 16 | evt->keyCode(); 536 return mapKey ? keyDownCommandsMap->get(mapKey) : 0; 537 } 538 539 int mapKey = modifiers << 16 | evt->charCode(); 540 return mapKey ? keyPressCommandsMap->get(mapKey) : 0; 541 } 542 543 bool EditorClientImpl::handleEditingKeyboardEvent(KeyboardEvent* evt) 544 { 545 const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); 546 // do not treat this as text input if it's a system key event 547 if (!keyEvent || keyEvent->isSystemKey()) 548 return false; 549 550 Frame* frame = evt->target()->toNode()->document()->frame(); 551 if (!frame) 552 return false; 553 554 String commandName = interpretKeyEvent(evt); 555 Editor::Command command = frame->editor()->command(commandName); 556 557 if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) { 558 // WebKit doesn't have enough information about mode to decide how 559 // commands that just insert text if executed via Editor should be treated, 560 // so we leave it upon WebCore to either handle them immediately 561 // (e.g. Tab that changes focus) or let a keypress event be generated 562 // (e.g. Tab that inserts a Tab character, or Enter). 563 if (command.isTextInsertion() || commandName.isEmpty()) 564 return false; 565 if (command.execute(evt)) { 566 if (m_webView->client()) 567 m_webView->client()->didExecuteCommand(WebString(commandName)); 568 return true; 569 } 570 return false; 571 } 572 573 if (command.execute(evt)) { 574 if (m_webView->client()) 575 m_webView->client()->didExecuteCommand(WebString(commandName)); 576 return true; 577 } 578 579 // Here we need to filter key events. 580 // On Gtk/Linux, it emits key events with ASCII text and ctrl on for ctrl-<x>. 581 // In Webkit, EditorClient::handleKeyboardEvent in 582 // WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp drop such events. 583 // On Mac, it emits key events with ASCII text and meta on for Command-<x>. 584 // These key events should not emit text insert event. 585 // Alt key would be used to insert alternative character, so we should let 586 // through. Also note that Ctrl-Alt combination equals to AltGr key which is 587 // also used to insert alternative character. 588 // http://code.google.com/p/chromium/issues/detail?id=10846 589 // Windows sets both alt and meta are on when "Alt" key pressed. 590 // http://code.google.com/p/chromium/issues/detail?id=2215 591 // Also, we should not rely on an assumption that keyboards don't 592 // send ASCII characters when pressing a control key on Windows, 593 // which may be configured to do it so by user. 594 // See also http://en.wikipedia.org/wiki/Keyboard_Layout 595 // FIXME(ukai): investigate more detail for various keyboard layout. 596 if (evt->keyEvent()->text().length() == 1) { 597 UChar ch = evt->keyEvent()->text()[0U]; 598 599 // Don't insert null or control characters as they can result in 600 // unexpected behaviour 601 if (ch < ' ') 602 return false; 603 #if !OS(WINDOWS) 604 // Don't insert ASCII character if ctrl w/o alt or meta is on. 605 // On Mac, we should ignore events when meta is on (Command-<x>). 606 if (ch < 0x80) { 607 if (evt->keyEvent()->ctrlKey() && !evt->keyEvent()->altKey()) 608 return false; 609 #if OS(DARWIN) 610 if (evt->keyEvent()->metaKey()) 611 return false; 612 #endif 613 } 614 #endif 615 } 616 617 if (!frame->editor()->canEdit()) 618 return false; 619 620 return frame->editor()->insertText(evt->keyEvent()->text(), evt); 621 } 622 623 void EditorClientImpl::handleKeyboardEvent(KeyboardEvent* evt) 624 { 625 if (evt->keyCode() == VKEY_DOWN 626 || evt->keyCode() == VKEY_UP) { 627 ASSERT(evt->target()->toNode()); 628 showFormAutofillForNode(evt->target()->toNode()); 629 } 630 631 // Give the embedder a chance to handle the keyboard event. 632 if ((m_webView->client() 633 && m_webView->client()->handleCurrentKeyboardEvent()) 634 || handleEditingKeyboardEvent(evt)) 635 evt->setDefaultHandled(); 636 } 637 638 void EditorClientImpl::handleInputMethodKeydown(KeyboardEvent* keyEvent) 639 { 640 // We handle IME within chrome. 641 } 642 643 void EditorClientImpl::textFieldDidBeginEditing(Element*) 644 { 645 } 646 647 void EditorClientImpl::textFieldDidEndEditing(Element* element) 648 { 649 // Notification that focus was lost. Be careful with this, it's also sent 650 // when the page is being closed. 651 652 // Cancel any pending DoAutofill call. 653 m_autofillArgs.clear(); 654 m_autofillTimer.stop(); 655 656 // Hide any showing popup. 657 m_webView->hideSuggestionsPopup(); 658 659 if (!m_webView->client()) 660 return; // The page is getting closed, don't fill the password. 661 662 // Notify any password-listener of the focus change. 663 HTMLInputElement* inputElement = WebKit::toHTMLInputElement(element); 664 if (!inputElement) 665 return; 666 667 WebFrameImpl* webframe = WebFrameImpl::fromFrame(inputElement->document()->frame()); 668 if (!webframe) 669 return; 670 671 WebPasswordAutocompleteListener* listener = webframe->getPasswordListener(inputElement); 672 if (!listener) 673 return; 674 675 listener->didBlurInputElement(inputElement->value()); 676 } 677 678 void EditorClientImpl::textDidChangeInTextField(Element* element) 679 { 680 ASSERT(element->hasLocalName(HTMLNames::inputTag)); 681 // Note that we only show the autofill popup in this case if the caret is at 682 // the end. This matches FireFox and Safari but not IE. 683 autofill(static_cast<HTMLInputElement*>(element), false, false, 684 true); 685 } 686 687 bool EditorClientImpl::showFormAutofillForNode(Node* node) 688 { 689 HTMLInputElement* inputElement = WebKit::toHTMLInputElement(node); 690 if (inputElement) 691 return autofill(inputElement, true, true, false); 692 return false; 693 } 694 695 bool EditorClientImpl::autofill(HTMLInputElement* inputElement, 696 bool autofillFormOnly, 697 bool autofillOnEmptyValue, 698 bool requireCaretAtEnd) 699 { 700 // Cancel any pending DoAutofill call. 701 m_autofillArgs.clear(); 702 m_autofillTimer.stop(); 703 704 // Let's try to trigger autofill for that field, if applicable. 705 if (!inputElement->isEnabledFormControl() || !inputElement->isTextField() 706 || inputElement->isPasswordField() 707 || !inputElement->autoComplete()) 708 return false; 709 710 WebString name = WebInputElement(inputElement).nameForAutofill(); 711 if (name.isEmpty()) // If the field has no name, then we won't have values. 712 return false; 713 714 // Don't attempt to autofill with values that are too large. 715 if (inputElement->value().length() > maximumTextSizeForAutofill) 716 return false; 717 718 m_autofillArgs = new AutofillArgs(); 719 m_autofillArgs->inputElement = inputElement; 720 m_autofillArgs->autofillFormOnly = autofillFormOnly; 721 m_autofillArgs->autofillOnEmptyValue = autofillOnEmptyValue; 722 m_autofillArgs->requireCaretAtEnd = requireCaretAtEnd; 723 m_autofillArgs->backspaceOrDeletePressed = m_backspaceOrDeletePressed; 724 725 if (!requireCaretAtEnd) 726 doAutofill(0); 727 else { 728 // We post a task for doing the autofill as the caret position is not set 729 // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) 730 // and we need it to determine whether or not to trigger autofill. 731 m_autofillTimer.startOneShot(0.0); 732 } 733 return true; 734 } 735 736 void EditorClientImpl::doAutofill(Timer<EditorClientImpl>* timer) 737 { 738 OwnPtr<AutofillArgs> args(m_autofillArgs.release()); 739 HTMLInputElement* inputElement = args->inputElement.get(); 740 741 const String& value = inputElement->value(); 742 743 // Enforce autofill_on_empty_value and caret_at_end. 744 745 bool isCaretAtEnd = true; 746 if (args->requireCaretAtEnd) 747 isCaretAtEnd = inputElement->selectionStart() == inputElement->selectionEnd() 748 && inputElement->selectionEnd() == static_cast<int>(value.length()); 749 750 if ((!args->autofillOnEmptyValue && value.isEmpty()) || !isCaretAtEnd) { 751 m_webView->hideSuggestionsPopup(); 752 return; 753 } 754 755 // First let's see if there is a password listener for that element. 756 // We won't trigger form autofill in that case, as having both behavior on 757 // a node would be confusing. 758 WebFrameImpl* webframe = WebFrameImpl::fromFrame(inputElement->document()->frame()); 759 if (!webframe) 760 return; 761 WebPasswordAutocompleteListener* listener = webframe->getPasswordListener(inputElement); 762 if (listener) { 763 if (args->autofillFormOnly) 764 return; 765 766 listener->performInlineAutocomplete(value, 767 args->backspaceOrDeletePressed, 768 true); 769 return; 770 } 771 772 // Then trigger form autofill. 773 WebString name = WebInputElement(inputElement).nameForAutofill(); 774 ASSERT(static_cast<int>(name.length()) > 0); 775 776 if (m_webView->client()) 777 m_webView->client()->queryAutofillSuggestions(WebNode(inputElement), 778 name, WebString(value)); 779 } 780 781 void EditorClientImpl::cancelPendingAutofill() 782 { 783 m_autofillArgs.clear(); 784 m_autofillTimer.stop(); 785 } 786 787 void EditorClientImpl::onAutofillSuggestionAccepted(HTMLInputElement* textField) 788 { 789 WebFrameImpl* webframe = WebFrameImpl::fromFrame(textField->document()->frame()); 790 if (!webframe) 791 return; 792 793 WebPasswordAutocompleteListener* listener = webframe->getPasswordListener(textField); 794 // Password listeners need to autocomplete other fields that depend on the 795 // input element with autofill suggestions. 796 if (listener) 797 listener->performInlineAutocomplete(textField->value(), false, false); 798 } 799 800 bool EditorClientImpl::doTextFieldCommandFromEvent(Element* element, 801 KeyboardEvent* event) 802 { 803 // Remember if backspace was pressed for the autofill. It is not clear how to 804 // find if backspace was pressed from textFieldDidBeginEditing and 805 // textDidChangeInTextField as when these methods are called the value of the 806 // input element already contains the type character. 807 m_backspaceOrDeletePressed = event->keyCode() == VKEY_BACK || event->keyCode() == VKEY_DELETE; 808 809 // The Mac code appears to use this method as a hook to implement special 810 // keyboard commands specific to Safari's auto-fill implementation. We 811 // just return false to allow the default action. 812 return false; 813 } 814 815 void EditorClientImpl::textWillBeDeletedInTextField(Element*) 816 { 817 } 818 819 void EditorClientImpl::textDidChangeInTextArea(Element*) 820 { 821 } 822 823 void EditorClientImpl::ignoreWordInSpellDocument(const String&) 824 { 825 notImplemented(); 826 } 827 828 void EditorClientImpl::learnWord(const String&) 829 { 830 notImplemented(); 831 } 832 833 void EditorClientImpl::checkSpellingOfString(const UChar* text, int length, 834 int* misspellingLocation, 835 int* misspellingLength) 836 { 837 // SpellCheckWord will write (0, 0) into the output vars, which is what our 838 // caller expects if the word is spelled correctly. 839 int spellLocation = -1; 840 int spellLength = 0; 841 842 // Check to see if the provided text is spelled correctly. 843 if (isContinuousSpellCheckingEnabled() && m_webView->client()) 844 m_webView->client()->spellCheck(WebString(text, length), spellLocation, spellLength); 845 else { 846 spellLocation = 0; 847 spellLength = 0; 848 } 849 850 // Note: the Mac code checks if the pointers are null before writing to them, 851 // so we do too. 852 if (misspellingLocation) 853 *misspellingLocation = spellLocation; 854 if (misspellingLength) 855 *misspellingLength = spellLength; 856 } 857 858 String EditorClientImpl::getAutoCorrectSuggestionForMisspelledWord(const String& misspelledWord) 859 { 860 if (!(isContinuousSpellCheckingEnabled() && m_webView->client())) 861 return String(); 862 863 // Do not autocorrect words with capital letters in it except the 864 // first letter. This will remove cases changing "IMB" to "IBM". 865 for (size_t i = 1; i < misspelledWord.length(); i++) { 866 if (u_isupper(static_cast<UChar32>(misspelledWord[i]))) 867 return String(); 868 } 869 870 return m_webView->client()->autoCorrectWord(WebString(misspelledWord)); 871 } 872 873 void EditorClientImpl::checkGrammarOfString(const UChar*, int length, 874 WTF::Vector<GrammarDetail>&, 875 int* badGrammarLocation, 876 int* badGrammarLength) 877 { 878 notImplemented(); 879 if (badGrammarLocation) 880 *badGrammarLocation = 0; 881 if (badGrammarLength) 882 *badGrammarLength = 0; 883 } 884 885 void EditorClientImpl::updateSpellingUIWithGrammarString(const String&, 886 const GrammarDetail& detail) 887 { 888 notImplemented(); 889 } 890 891 void EditorClientImpl::updateSpellingUIWithMisspelledWord(const String& misspelledWord) 892 { 893 if (m_webView->client()) 894 m_webView->client()->updateSpellingUIWithMisspelledWord(WebString(misspelledWord)); 895 } 896 897 void EditorClientImpl::showSpellingUI(bool show) 898 { 899 if (m_webView->client()) 900 m_webView->client()->showSpellingUI(show); 901 } 902 903 bool EditorClientImpl::spellingUIIsShowing() 904 { 905 if (m_webView->client()) 906 return m_webView->client()->isShowingSpellingUI(); 907 return false; 908 } 909 910 void EditorClientImpl::getGuessesForWord(const String&, 911 WTF::Vector<String>& guesses) 912 { 913 notImplemented(); 914 } 915 916 void EditorClientImpl::setInputMethodState(bool enabled) 917 { 918 if (m_webView->client()) 919 m_webView->client()->setInputMethodEnabled(enabled); 920 } 921 922 } // namesace WebKit 923