1 /* 2 * Copyright (C) 2006, 2007 Apple, Inc. All rights reserved. 3 * Copyright (C) 2012 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 "HTMLNames.h" 31 #include "WebAutofillClient.h" 32 #include "WebEditingAction.h" 33 #include "WebElement.h" 34 #include "WebFrameClient.h" 35 #include "WebFrameImpl.h" 36 #include "WebInputElement.h" 37 #include "WebInputEventConversion.h" 38 #include "WebKit.h" 39 #include "WebNode.h" 40 #include "WebPermissionClient.h" 41 #include "WebRange.h" 42 #include "WebSpellCheckClient.h" 43 #include "WebTextAffinity.h" 44 #include "WebTextCheckingCompletionImpl.h" 45 #include "WebTextCheckingResult.h" 46 #include "WebViewClient.h" 47 #include "WebViewImpl.h" 48 #include "core/dom/Document.h" 49 #include "core/dom/EventNames.h" 50 #include "core/dom/KeyboardEvent.h" 51 #include "core/editing/Editor.h" 52 #include "core/editing/SpellChecker.h" 53 #include "core/editing/UndoStep.h" 54 #include "core/html/HTMLInputElement.h" 55 #include "core/page/EventHandler.h" 56 #include "core/page/Frame.h" 57 #include "core/page/Page.h" 58 #include "core/page/Settings.h" 59 #include "core/platform/NotImplemented.h" 60 #include "core/platform/PlatformKeyboardEvent.h" 61 #include "core/platform/chromium/KeyboardCodes.h" 62 #include "core/rendering/RenderObject.h" 63 #include "wtf/text/WTFString.h" 64 65 using namespace WebCore; 66 67 namespace WebKit { 68 69 // Arbitrary depth limit for the undo stack, to keep it from using 70 // unbounded memory. This is the maximum number of distinct undoable 71 // actions -- unbroken stretches of typed characters are coalesced 72 // into a single action. 73 static const size_t maximumUndoStackDepth = 1000; 74 75 EditorClientImpl::EditorClientImpl(WebViewImpl* webview) 76 : m_webView(webview) 77 , m_inRedo(false) 78 , m_spellCheckThisFieldStatus(SpellCheckAutomatic) 79 { 80 } 81 82 EditorClientImpl::~EditorClientImpl() 83 { 84 } 85 86 bool EditorClientImpl::smartInsertDeleteEnabled() 87 { 88 if (m_webView->page()) 89 return m_webView->page()->settings()->smartInsertDeleteEnabled(); 90 return false; 91 } 92 93 bool EditorClientImpl::isSelectTrailingWhitespaceEnabled() 94 { 95 if (m_webView->page()) 96 return m_webView->page()->settings()->selectTrailingWhitespaceEnabled(); 97 return false; 98 } 99 100 bool EditorClientImpl::shouldSpellcheckByDefault() 101 { 102 // Spellcheck should be enabled for all editable areas (such as textareas, 103 // contentEditable regions, designMode docs and inputs). 104 const Frame* frame = m_webView->focusedWebCoreFrame(); 105 if (!frame) 106 return false; 107 const Editor* editor = frame->editor(); 108 if (!editor) 109 return false; 110 if (editor->isSpellCheckingEnabledInFocusedNode()) 111 return true; 112 const Document* document = frame->document(); 113 if (!document) 114 return false; 115 const Element* element = document->focusedElement(); 116 // If |element| is null, we default to allowing spellchecking. This is done 117 // in order to mitigate the issue when the user clicks outside the textbox, 118 // as a result of which |element| becomes null, resulting in all the spell 119 // check markers being deleted. Also, the Frame will decide not to do 120 // spellchecking if the user can't edit - so returning true here will not 121 // cause any problems to the Frame's behavior. 122 if (!element) 123 return true; 124 const RenderObject* renderer = element->renderer(); 125 if (!renderer) 126 return false; 127 128 return true; 129 } 130 131 bool EditorClientImpl::isContinuousSpellCheckingEnabled() 132 { 133 if (m_spellCheckThisFieldStatus == SpellCheckForcedOff) 134 return false; 135 if (m_spellCheckThisFieldStatus == SpellCheckForcedOn) 136 return true; 137 return shouldSpellcheckByDefault(); 138 } 139 140 void EditorClientImpl::toggleContinuousSpellChecking() 141 { 142 if (isContinuousSpellCheckingEnabled()) 143 m_spellCheckThisFieldStatus = SpellCheckForcedOff; 144 else 145 m_spellCheckThisFieldStatus = SpellCheckForcedOn; 146 } 147 148 bool EditorClientImpl::isGrammarCheckingEnabled() 149 { 150 const Frame* frame = m_webView->focusedWebCoreFrame(); 151 return frame && frame->settings() && (frame->settings()->asynchronousSpellCheckingEnabled() || frame->settings()->unifiedTextCheckerEnabled()); 152 } 153 154 bool EditorClientImpl::shouldBeginEditing(Range* range) 155 { 156 if (m_webView->client()) 157 return m_webView->client()->shouldBeginEditing(WebRange(range)); 158 return true; 159 } 160 161 bool EditorClientImpl::shouldEndEditing(Range* range) 162 { 163 if (m_webView->client()) 164 return m_webView->client()->shouldEndEditing(WebRange(range)); 165 return true; 166 } 167 168 bool EditorClientImpl::shouldInsertNode(Node* node, 169 Range* range, 170 EditorInsertAction action) 171 { 172 if (m_webView->client()) { 173 return m_webView->client()->shouldInsertNode(WebNode(node), 174 WebRange(range), 175 static_cast<WebEditingAction>(action)); 176 } 177 return true; 178 } 179 180 bool EditorClientImpl::shouldInsertText(const String& text, 181 Range* range, 182 EditorInsertAction action) 183 { 184 if (m_webView->client()) { 185 return m_webView->client()->shouldInsertText(WebString(text), 186 WebRange(range), 187 static_cast<WebEditingAction>(action)); 188 } 189 return true; 190 } 191 192 193 bool EditorClientImpl::shouldDeleteRange(Range* range) 194 { 195 if (m_webView->client()) 196 return m_webView->client()->shouldDeleteRange(WebRange(range)); 197 return true; 198 } 199 200 bool EditorClientImpl::shouldChangeSelectedRange(Range* fromRange, 201 Range* toRange, 202 EAffinity affinity, 203 bool stillSelecting) 204 { 205 if (m_webView->client()) { 206 return m_webView->client()->shouldChangeSelectedRange(WebRange(fromRange), 207 WebRange(toRange), 208 static_cast<WebTextAffinity>(affinity), 209 stillSelecting); 210 } 211 return true; 212 } 213 214 bool EditorClientImpl::shouldApplyStyle(StylePropertySet* style, Range* range) 215 { 216 if (m_webView->client()) { 217 // FIXME: Pass a reference to the CSSStyleDeclaration somehow. 218 return m_webView->client()->shouldApplyStyle(WebString(), WebRange(range)); 219 } 220 return true; 221 } 222 223 void EditorClientImpl::didBeginEditing() 224 { 225 if (m_webView->client()) 226 m_webView->client()->didBeginEditing(); 227 } 228 229 void EditorClientImpl::respondToChangedSelection(Frame* frame) 230 { 231 if (m_webView->client() && frame) 232 m_webView->client()->didChangeSelection(!frame->selection()->isRange()); 233 } 234 235 void EditorClientImpl::respondToChangedContents() 236 { 237 if (m_webView->client()) 238 m_webView->client()->didChangeContents(); 239 } 240 241 void EditorClientImpl::didEndEditing() 242 { 243 if (m_webView->client()) 244 m_webView->client()->didEndEditing(); 245 } 246 247 void EditorClientImpl::didCancelCompositionOnSelectionChange() 248 { 249 if (m_webView->client()) 250 m_webView->client()->didCancelCompositionOnSelectionChange(); 251 } 252 253 void EditorClientImpl::registerUndoStep(PassRefPtr<UndoStep> step) 254 { 255 if (m_undoStack.size() == maximumUndoStackDepth) 256 m_undoStack.removeFirst(); // drop oldest item off the far end 257 if (!m_inRedo) 258 m_redoStack.clear(); 259 m_undoStack.append(step); 260 } 261 262 void EditorClientImpl::registerRedoStep(PassRefPtr<UndoStep> step) 263 { 264 m_redoStack.append(step); 265 } 266 267 void EditorClientImpl::clearUndoRedoOperations() 268 { 269 NoEventDispatchAssertion assertNoEventDispatch; 270 m_undoStack.clear(); 271 m_redoStack.clear(); 272 } 273 274 bool EditorClientImpl::canCopyCut(Frame* frame, bool defaultValue) const 275 { 276 if (!m_webView->permissionClient()) 277 return defaultValue; 278 return m_webView->permissionClient()->allowWriteToClipboard(WebFrameImpl::fromFrame(frame), defaultValue); 279 } 280 281 bool EditorClientImpl::canPaste(Frame* frame, bool defaultValue) const 282 { 283 if (!m_webView->permissionClient()) 284 return defaultValue; 285 return m_webView->permissionClient()->allowReadFromClipboard(WebFrameImpl::fromFrame(frame), defaultValue); 286 } 287 288 bool EditorClientImpl::canUndo() const 289 { 290 return !m_undoStack.isEmpty(); 291 } 292 293 bool EditorClientImpl::canRedo() const 294 { 295 return !m_redoStack.isEmpty(); 296 } 297 298 void EditorClientImpl::undo() 299 { 300 if (canUndo()) { 301 UndoManagerStack::iterator back = --m_undoStack.end(); 302 RefPtr<UndoStep> step(*back); 303 m_undoStack.remove(back); 304 step->unapply(); 305 // unapply will call us back to push this command onto the redo stack. 306 } 307 } 308 309 void EditorClientImpl::redo() 310 { 311 if (canRedo()) { 312 UndoManagerStack::iterator back = --m_redoStack.end(); 313 RefPtr<UndoStep> step(*back); 314 m_redoStack.remove(back); 315 316 ASSERT(!m_inRedo); 317 m_inRedo = true; 318 step->reapply(); 319 // reapply will call us back to push this command onto the undo stack. 320 m_inRedo = false; 321 } 322 } 323 324 // 325 // The below code was adapted from the WebKit file webview.cpp 326 // 327 328 static const unsigned CtrlKey = 1 << 0; 329 static const unsigned AltKey = 1 << 1; 330 static const unsigned ShiftKey = 1 << 2; 331 static const unsigned MetaKey = 1 << 3; 332 #if OS(DARWIN) 333 // Aliases for the generic key defintions to make kbd shortcuts definitions more 334 // readable on OS X. 335 static const unsigned OptionKey = AltKey; 336 337 // Do not use this constant for anything but cursor movement commands. Keys 338 // with cmd set have their |isSystemKey| bit set, so chances are the shortcut 339 // will not be executed. Another, less important, reason is that shortcuts 340 // defined in the renderer do not blink the menu item that they triggered. See 341 // http://crbug.com/25856 and the bugs linked from there for details. 342 static const unsigned CommandKey = MetaKey; 343 #endif 344 345 // Keys with special meaning. These will be delegated to the editor using 346 // the execCommand() method 347 struct KeyDownEntry { 348 unsigned virtualKey; 349 unsigned modifiers; 350 const char* name; 351 }; 352 353 struct KeyPressEntry { 354 unsigned charCode; 355 unsigned modifiers; 356 const char* name; 357 }; 358 359 static const KeyDownEntry keyDownEntries[] = { 360 { VKEY_LEFT, 0, "MoveLeft" }, 361 { VKEY_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, 362 #if OS(DARWIN) 363 { VKEY_LEFT, OptionKey, "MoveWordLeft" }, 364 { VKEY_LEFT, OptionKey | ShiftKey, 365 "MoveWordLeftAndModifySelection" }, 366 #else 367 { VKEY_LEFT, CtrlKey, "MoveWordLeft" }, 368 { VKEY_LEFT, CtrlKey | ShiftKey, 369 "MoveWordLeftAndModifySelection" }, 370 #endif 371 { VKEY_RIGHT, 0, "MoveRight" }, 372 { VKEY_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, 373 #if OS(DARWIN) 374 { VKEY_RIGHT, OptionKey, "MoveWordRight" }, 375 { VKEY_RIGHT, OptionKey | ShiftKey, 376 "MoveWordRightAndModifySelection" }, 377 #else 378 { VKEY_RIGHT, CtrlKey, "MoveWordRight" }, 379 { VKEY_RIGHT, CtrlKey | ShiftKey, 380 "MoveWordRightAndModifySelection" }, 381 #endif 382 { VKEY_UP, 0, "MoveUp" }, 383 { VKEY_UP, ShiftKey, "MoveUpAndModifySelection" }, 384 { VKEY_PRIOR, ShiftKey, "MovePageUpAndModifySelection" }, 385 { VKEY_DOWN, 0, "MoveDown" }, 386 { VKEY_DOWN, ShiftKey, "MoveDownAndModifySelection" }, 387 { VKEY_NEXT, ShiftKey, "MovePageDownAndModifySelection" }, 388 #if !OS(DARWIN) 389 { VKEY_UP, CtrlKey, "MoveParagraphBackward" }, 390 { VKEY_UP, CtrlKey | ShiftKey, "MoveParagraphBackwardAndModifySelection" }, 391 { VKEY_DOWN, CtrlKey, "MoveParagraphForward" }, 392 { VKEY_DOWN, CtrlKey | ShiftKey, "MoveParagraphForwardAndModifySelection" }, 393 { VKEY_PRIOR, 0, "MovePageUp" }, 394 { VKEY_NEXT, 0, "MovePageDown" }, 395 #endif 396 { VKEY_HOME, 0, "MoveToBeginningOfLine" }, 397 { VKEY_HOME, ShiftKey, 398 "MoveToBeginningOfLineAndModifySelection" }, 399 #if OS(DARWIN) 400 { VKEY_LEFT, CommandKey, "MoveToBeginningOfLine" }, 401 { VKEY_LEFT, CommandKey | ShiftKey, 402 "MoveToBeginningOfLineAndModifySelection" }, 403 { VKEY_PRIOR, OptionKey, "MovePageUp" }, 404 { VKEY_NEXT, OptionKey, "MovePageDown" }, 405 #endif 406 #if OS(DARWIN) 407 { VKEY_UP, CommandKey, "MoveToBeginningOfDocument" }, 408 { VKEY_UP, CommandKey | ShiftKey, 409 "MoveToBeginningOfDocumentAndModifySelection" }, 410 #else 411 { VKEY_HOME, CtrlKey, "MoveToBeginningOfDocument" }, 412 { VKEY_HOME, CtrlKey | ShiftKey, 413 "MoveToBeginningOfDocumentAndModifySelection" }, 414 #endif 415 { VKEY_END, 0, "MoveToEndOfLine" }, 416 { VKEY_END, ShiftKey, "MoveToEndOfLineAndModifySelection" }, 417 #if OS(DARWIN) 418 { VKEY_DOWN, CommandKey, "MoveToEndOfDocument" }, 419 { VKEY_DOWN, CommandKey | ShiftKey, 420 "MoveToEndOfDocumentAndModifySelection" }, 421 #else 422 { VKEY_END, CtrlKey, "MoveToEndOfDocument" }, 423 { VKEY_END, CtrlKey | ShiftKey, 424 "MoveToEndOfDocumentAndModifySelection" }, 425 #endif 426 #if OS(DARWIN) 427 { VKEY_RIGHT, CommandKey, "MoveToEndOfLine" }, 428 { VKEY_RIGHT, CommandKey | ShiftKey, 429 "MoveToEndOfLineAndModifySelection" }, 430 #endif 431 { VKEY_BACK, 0, "DeleteBackward" }, 432 { VKEY_BACK, ShiftKey, "DeleteBackward" }, 433 { VKEY_DELETE, 0, "DeleteForward" }, 434 #if OS(DARWIN) 435 { VKEY_BACK, OptionKey, "DeleteWordBackward" }, 436 { VKEY_DELETE, OptionKey, "DeleteWordForward" }, 437 #else 438 { VKEY_BACK, CtrlKey, "DeleteWordBackward" }, 439 { VKEY_DELETE, CtrlKey, "DeleteWordForward" }, 440 #endif 441 { 'B', CtrlKey, "ToggleBold" }, 442 { 'I', CtrlKey, "ToggleItalic" }, 443 { 'U', CtrlKey, "ToggleUnderline" }, 444 { VKEY_ESCAPE, 0, "Cancel" }, 445 { VKEY_OEM_PERIOD, CtrlKey, "Cancel" }, 446 { VKEY_TAB, 0, "InsertTab" }, 447 { VKEY_TAB, ShiftKey, "InsertBacktab" }, 448 { VKEY_RETURN, 0, "InsertNewline" }, 449 { VKEY_RETURN, CtrlKey, "InsertNewline" }, 450 { VKEY_RETURN, AltKey, "InsertNewline" }, 451 { VKEY_RETURN, AltKey | ShiftKey, "InsertNewline" }, 452 { VKEY_RETURN, ShiftKey, "InsertLineBreak" }, 453 { VKEY_INSERT, CtrlKey, "Copy" }, 454 { VKEY_INSERT, ShiftKey, "Paste" }, 455 { VKEY_DELETE, ShiftKey, "Cut" }, 456 #if !OS(DARWIN) 457 // On OS X, we pipe these back to the browser, so that it can do menu item 458 // blinking. 459 { 'C', CtrlKey, "Copy" }, 460 { 'V', CtrlKey, "Paste" }, 461 { 'V', CtrlKey | ShiftKey, "PasteAndMatchStyle" }, 462 { 'X', CtrlKey, "Cut" }, 463 { 'A', CtrlKey, "SelectAll" }, 464 { 'Z', CtrlKey, "Undo" }, 465 { 'Z', CtrlKey | ShiftKey, "Redo" }, 466 { 'Y', CtrlKey, "Redo" }, 467 #endif 468 { VKEY_INSERT, 0, "OverWrite" }, 469 }; 470 471 static const KeyPressEntry keyPressEntries[] = { 472 { '\t', 0, "InsertTab" }, 473 { '\t', ShiftKey, "InsertBacktab" }, 474 { '\r', 0, "InsertNewline" }, 475 { '\r', CtrlKey, "InsertNewline" }, 476 { '\r', ShiftKey, "InsertLineBreak" }, 477 { '\r', AltKey, "InsertNewline" }, 478 { '\r', AltKey | ShiftKey, "InsertNewline" }, 479 }; 480 481 const char* EditorClientImpl::interpretKeyEvent(const KeyboardEvent* evt) 482 { 483 const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); 484 if (!keyEvent) 485 return ""; 486 487 static HashMap<int, const char*>* keyDownCommandsMap = 0; 488 static HashMap<int, const char*>* keyPressCommandsMap = 0; 489 490 if (!keyDownCommandsMap) { 491 keyDownCommandsMap = new HashMap<int, const char*>; 492 keyPressCommandsMap = new HashMap<int, const char*>; 493 494 for (unsigned i = 0; i < arraysize(keyDownEntries); i++) { 495 keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, 496 keyDownEntries[i].name); 497 } 498 499 for (unsigned i = 0; i < arraysize(keyPressEntries); i++) { 500 keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, 501 keyPressEntries[i].name); 502 } 503 } 504 505 unsigned modifiers = 0; 506 if (keyEvent->shiftKey()) 507 modifiers |= ShiftKey; 508 if (keyEvent->altKey()) 509 modifiers |= AltKey; 510 if (keyEvent->ctrlKey()) 511 modifiers |= CtrlKey; 512 if (keyEvent->metaKey()) 513 modifiers |= MetaKey; 514 515 if (keyEvent->type() == PlatformEvent::RawKeyDown) { 516 int mapKey = modifiers << 16 | evt->keyCode(); 517 return mapKey ? keyDownCommandsMap->get(mapKey) : 0; 518 } 519 520 int mapKey = modifiers << 16 | evt->charCode(); 521 return mapKey ? keyPressCommandsMap->get(mapKey) : 0; 522 } 523 524 bool EditorClientImpl::handleEditingKeyboardEvent(KeyboardEvent* evt) 525 { 526 const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); 527 // do not treat this as text input if it's a system key event 528 if (!keyEvent || keyEvent->isSystemKey()) 529 return false; 530 531 Frame* frame = evt->target()->toNode()->document()->frame(); 532 if (!frame) 533 return false; 534 535 String commandName = interpretKeyEvent(evt); 536 Editor::Command command = frame->editor()->command(commandName); 537 538 if (keyEvent->type() == PlatformEvent::RawKeyDown) { 539 // WebKit doesn't have enough information about mode to decide how 540 // commands that just insert text if executed via Editor should be treated, 541 // so we leave it upon WebCore to either handle them immediately 542 // (e.g. Tab that changes focus) or let a keypress event be generated 543 // (e.g. Tab that inserts a Tab character, or Enter). 544 if (command.isTextInsertion() || commandName.isEmpty()) 545 return false; 546 if (command.execute(evt)) { 547 if (m_webView->client()) 548 m_webView->client()->didExecuteCommand(WebString(commandName)); 549 return true; 550 } 551 return false; 552 } 553 554 if (command.execute(evt)) { 555 if (m_webView->client()) 556 m_webView->client()->didExecuteCommand(WebString(commandName)); 557 return true; 558 } 559 560 // Here we need to filter key events. 561 // On Gtk/Linux, it emits key events with ASCII text and ctrl on for ctrl-<x>. 562 // In Webkit, EditorClient::handleKeyboardEvent in 563 // WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp drop such events. 564 // On Mac, it emits key events with ASCII text and meta on for Command-<x>. 565 // These key events should not emit text insert event. 566 // Alt key would be used to insert alternative character, so we should let 567 // through. Also note that Ctrl-Alt combination equals to AltGr key which is 568 // also used to insert alternative character. 569 // http://code.google.com/p/chromium/issues/detail?id=10846 570 // Windows sets both alt and meta are on when "Alt" key pressed. 571 // http://code.google.com/p/chromium/issues/detail?id=2215 572 // Also, we should not rely on an assumption that keyboards don't 573 // send ASCII characters when pressing a control key on Windows, 574 // which may be configured to do it so by user. 575 // See also http://en.wikipedia.org/wiki/Keyboard_Layout 576 // FIXME(ukai): investigate more detail for various keyboard layout. 577 if (evt->keyEvent()->text().length() == 1) { 578 UChar ch = evt->keyEvent()->text()[0U]; 579 580 // Don't insert null or control characters as they can result in 581 // unexpected behaviour 582 if (ch < ' ') 583 return false; 584 #if !OS(WINDOWS) 585 // Don't insert ASCII character if ctrl w/o alt or meta is on. 586 // On Mac, we should ignore events when meta is on (Command-<x>). 587 if (ch < 0x80) { 588 if (evt->keyEvent()->ctrlKey() && !evt->keyEvent()->altKey()) 589 return false; 590 #if OS(DARWIN) 591 if (evt->keyEvent()->metaKey()) 592 return false; 593 #endif 594 } 595 #endif 596 } 597 598 if (!frame->editor()->canEdit()) 599 return false; 600 601 return frame->editor()->insertText(evt->keyEvent()->text(), evt); 602 } 603 604 void EditorClientImpl::handleKeyboardEvent(KeyboardEvent* evt) 605 { 606 // Give the embedder a chance to handle the keyboard event. 607 if ((m_webView->client() 608 && m_webView->client()->handleCurrentKeyboardEvent()) 609 || handleEditingKeyboardEvent(evt)) 610 evt->setDefaultHandled(); 611 } 612 613 void EditorClientImpl::textFieldDidEndEditing(Element* element) 614 { 615 if (m_webView->autofillClient() && element->hasTagName(HTMLNames::inputTag)) 616 m_webView->autofillClient()->textFieldDidEndEditing(WebInputElement(toHTMLInputElement(element))); 617 618 // Notification that focus was lost. Be careful with this, it's also sent 619 // when the page is being closed. 620 621 // Hide any showing popup. 622 m_webView->hideAutofillPopup(); 623 } 624 625 void EditorClientImpl::textDidChangeInTextField(Element* element) 626 { 627 HTMLInputElement* inputElement = toHTMLInputElement(element); 628 if (m_webView->autofillClient()) 629 m_webView->autofillClient()->textFieldDidChange(WebInputElement(inputElement)); 630 } 631 632 bool EditorClientImpl::doTextFieldCommandFromEvent(Element* element, 633 KeyboardEvent* event) 634 { 635 if (m_webView->autofillClient() && element->hasTagName(HTMLNames::inputTag)) { 636 m_webView->autofillClient()->textFieldDidReceiveKeyDown(WebInputElement(toHTMLInputElement(element)), 637 WebKeyboardEventBuilder(*event)); 638 } 639 640 // The Mac code appears to use this method as a hook to implement special 641 // keyboard commands specific to Safari's auto-fill implementation. We 642 // just return false to allow the default action. 643 return false; 644 } 645 646 bool EditorClientImpl::shouldEraseMarkersAfterChangeSelection(TextCheckingType type) const 647 { 648 const Frame* frame = m_webView->focusedWebCoreFrame(); 649 return !frame || !frame->settings() || (!frame->settings()->asynchronousSpellCheckingEnabled() && !frame->settings()->unifiedTextCheckerEnabled()); 650 } 651 652 void EditorClientImpl::checkSpellingOfString(const String& text, 653 int* misspellingLocation, 654 int* misspellingLength) 655 { 656 // SpellCheckWord will write (0, 0) into the output vars, which is what our 657 // caller expects if the word is spelled correctly. 658 int spellLocation = -1; 659 int spellLength = 0; 660 661 // Check to see if the provided text is spelled correctly. 662 if (m_webView->spellCheckClient()) 663 m_webView->spellCheckClient()->spellCheck(text, spellLocation, spellLength, 0); 664 else { 665 spellLocation = 0; 666 spellLength = 0; 667 } 668 669 // Note: the Mac code checks if the pointers are null before writing to them, 670 // so we do too. 671 if (misspellingLocation) 672 *misspellingLocation = spellLocation; 673 if (misspellingLength) 674 *misspellingLength = spellLength; 675 } 676 677 void EditorClientImpl::requestCheckingOfString(WTF::PassRefPtr<WebCore::TextCheckingRequest> request) 678 { 679 if (m_webView->spellCheckClient()) { 680 const String& text = request->data().text(); 681 const Vector<uint32_t>& markers = request->data().markers(); 682 const Vector<unsigned>& markerOffsets = request->data().offsets(); 683 m_webView->spellCheckClient()->requestCheckingOfText(text, markers, markerOffsets, new WebTextCheckingCompletionImpl(request)); 684 } 685 } 686 687 String EditorClientImpl::getAutoCorrectSuggestionForMisspelledWord(const String& misspelledWord) 688 { 689 if (!(isContinuousSpellCheckingEnabled() && m_webView->client())) 690 return String(); 691 692 // Do not autocorrect words with capital letters in it except the 693 // first letter. This will remove cases changing "IMB" to "IBM". 694 for (size_t i = 1; i < misspelledWord.length(); i++) { 695 if (u_isupper(static_cast<UChar32>(misspelledWord[i]))) 696 return String(); 697 } 698 699 if (m_webView->spellCheckClient()) 700 return m_webView->spellCheckClient()->autoCorrectWord(WebString(misspelledWord)); 701 return String(); 702 } 703 704 void EditorClientImpl::checkGrammarOfString(const String& text, WTF::Vector<GrammarDetail>& details, int* badGrammarLocation, int* badGrammarLength) 705 { 706 if (badGrammarLocation) 707 *badGrammarLocation = -1; 708 if (badGrammarLength) 709 *badGrammarLength = 0; 710 711 if (!m_webView->spellCheckClient()) 712 return; 713 WebVector<WebTextCheckingResult> webResults; 714 m_webView->spellCheckClient()->checkTextOfParagraph(text, WebTextCheckingTypeGrammar, &webResults); 715 if (!webResults.size()) 716 return; 717 718 // Convert a list of WebTextCheckingResults to a list of GrammarDetails. If 719 // the converted vector of GrammarDetails has grammar errors, we set 720 // badGrammarLocation and badGrammarLength to tell WebKit that the input 721 // text has grammar errors. 722 for (size_t i = 0; i < webResults.size(); ++i) { 723 if (webResults[i].type == WebTextCheckingTypeGrammar) { 724 GrammarDetail detail; 725 detail.location = webResults[i].location; 726 detail.length = webResults[i].length; 727 detail.userDescription = webResults[i].replacement; 728 details.append(detail); 729 } 730 } 731 if (!details.size()) 732 return; 733 if (badGrammarLocation) 734 *badGrammarLocation = 0; 735 if (badGrammarLength) 736 *badGrammarLength = text.length(); 737 } 738 739 void EditorClientImpl::updateSpellingUIWithMisspelledWord(const String& misspelledWord) 740 { 741 if (m_webView->spellCheckClient()) 742 m_webView->spellCheckClient()->updateSpellingUIWithMisspelledWord(WebString(misspelledWord)); 743 } 744 745 void EditorClientImpl::showSpellingUI(bool show) 746 { 747 if (m_webView->spellCheckClient()) 748 m_webView->spellCheckClient()->showSpellingUI(show); 749 } 750 751 bool EditorClientImpl::spellingUIIsShowing() 752 { 753 if (m_webView->spellCheckClient()) 754 return m_webView->spellCheckClient()->isShowingSpellingUI(); 755 return false; 756 } 757 758 bool EditorClientImpl::supportsGlobalSelection() 759 { 760 #if OS(UNIX) && !OS(DARWIN) 761 return true; 762 #else 763 return false; 764 #endif 765 } 766 767 void EditorClientImpl::willSetInputMethodState() 768 { 769 if (m_webView->client()) 770 m_webView->client()->resetInputMethod(); 771 } 772 773 } // namesace WebKit 774