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