Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Side by Side Diff: webkit/api/src/EditorClientImpl.cpp

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

Powered by Google App Engine
This is Rietveld 408576698