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