1 /*
2 * Copyright (C) 2006, 2007 Apple, Inc. All rights reserved.
3 * Copyright (C) 2012 Google, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "core/editing/Editor.h"
29
30 #include "core/events/KeyboardEvent.h"
31 #include "core/frame/Frame.h"
32 #include "core/page/EditorClient.h"
33 #include "platform/KeyboardCodes.h"
34 #include "platform/PlatformKeyboardEvent.h"
35
36 namespace WebCore {
37
38 //
39 // The below code was adapted from the WebKit file webview.cpp
40 //
41
42 static const unsigned CtrlKey = 1 << 0;
43 static const unsigned AltKey = 1 << 1;
44 static const unsigned ShiftKey = 1 << 2;
45 static const unsigned MetaKey = 1 << 3;
46 #if OS(MACOSX)
47 // Aliases for the generic key defintions to make kbd shortcuts definitions more
48 // readable on OS X.
49 static const unsigned OptionKey = AltKey;
50
51 // Do not use this constant for anything but cursor movement commands. Keys
52 // with cmd set have their |isSystemKey| bit set, so chances are the shortcut
53 // will not be executed. Another, less important, reason is that shortcuts
54 // defined in the renderer do not blink the menu item that they triggered. See
55 // http://crbug.com/25856 and the bugs linked from there for details.
56 static const unsigned CommandKey = MetaKey;
57 #endif
58
59 // Keys with special meaning. These will be delegated to the editor using
60 // the execCommand() method
61 struct KeyDownEntry {
62 unsigned virtualKey;
63 unsigned modifiers;
64 const char* name;
65 };
66
67 struct KeyPressEntry {
68 unsigned charCode;
69 unsigned modifiers;
70 const char* name;
71 };
72
73 // Key bindings with command key on Mac and alt key on other platforms are
74 // marked as system key events and will be ignored (with the exception
75 // of Command-B and Command-I) so they shouldn't be added here.
76 static const KeyDownEntry keyDownEntries[] = {
77 { VKEY_LEFT, 0, "MoveLeft" },
78 { VKEY_LEFT, ShiftKey, "MoveLeftAndModifySelection" },
79 #if OS(MACOSX)
80 { VKEY_LEFT, OptionKey, "MoveWordLeft" },
81 { VKEY_LEFT, OptionKey | ShiftKey,
82 "MoveWordLeftAndModifySelection" },
83 #else
84 { VKEY_LEFT, CtrlKey, "MoveWordLeft" },
85 { VKEY_LEFT, CtrlKey | ShiftKey,
86 "MoveWordLeftAndModifySelection" },
87 #endif
88 { VKEY_RIGHT, 0, "MoveRight" },
89 { VKEY_RIGHT, ShiftKey, "MoveRightAndModifySelection" },
90 #if OS(MACOSX)
91 { VKEY_RIGHT, OptionKey, "MoveWordRight" },
92 { VKEY_RIGHT, OptionKey | ShiftKey, "MoveWordRightAndModifySelection" },
93 #else
94 { VKEY_RIGHT, CtrlKey, "MoveWordRight" },
95 { VKEY_RIGHT, CtrlKey | ShiftKey, "MoveWordRightAndModifySelection" },
96 #endif
97 { VKEY_UP, 0, "MoveUp" },
98 { VKEY_UP, ShiftKey, "MoveUpAndModifySelection" },
99 { VKEY_PRIOR, ShiftKey, "MovePageUpAndModifySelection" },
100 { VKEY_DOWN, 0, "MoveDown" },
101 { VKEY_DOWN, ShiftKey, "MoveDownAndModifySelection" },
102 { VKEY_NEXT, ShiftKey, "MovePageDownAndModifySelection" },
103 #if !OS(MACOSX)
104 { VKEY_UP, CtrlKey, "MoveParagraphBackward" },
105 { VKEY_UP, CtrlKey | ShiftKey, "MoveParagraphBackwardAndModifySelection" },
106 { VKEY_DOWN, CtrlKey, "MoveParagraphForward" },
107 { VKEY_DOWN, CtrlKey | ShiftKey, "MoveParagraphForwardAndModifySelection" },
108 { VKEY_PRIOR, 0, "MovePageUp" },
109 { VKEY_NEXT, 0, "MovePageDown" },
110 #endif
111 { VKEY_HOME, 0, "MoveToBeginningOfLine" },
112 { VKEY_HOME, ShiftKey,
113 "MoveToBeginningOfLineAndModifySelection" },
114 #if OS(MACOSX)
115 { VKEY_PRIOR, OptionKey, "MovePageUp" },
116 { VKEY_NEXT, OptionKey, "MovePageDown" },
117 #endif
118 #if !OS(MACOSX)
119 { VKEY_HOME, CtrlKey, "MoveToBeginningOfDocument" },
120 { VKEY_HOME, CtrlKey | ShiftKey,
121 "MoveToBeginningOfDocumentAndModifySelection" },
122 #endif
123 { VKEY_END, 0, "MoveToEndOfLine" },
124 { VKEY_END, ShiftKey, "MoveToEndOfLineAndModifySelection" },
125 #if !OS(MACOSX)
126 { VKEY_END, CtrlKey, "MoveToEndOfDocument" },
127 { VKEY_END, CtrlKey | ShiftKey,
128 "MoveToEndOfDocumentAndModifySelection" },
129 #endif
130 { VKEY_BACK, 0, "DeleteBackward" },
131 { VKEY_BACK, ShiftKey, "DeleteBackward" },
132 { VKEY_DELETE, 0, "DeleteForward" },
133 #if OS(MACOSX)
134 { VKEY_BACK, OptionKey, "DeleteWordBackward" },
135 { VKEY_DELETE, OptionKey, "DeleteWordForward" },
136 #else
137 { VKEY_BACK, CtrlKey, "DeleteWordBackward" },
138 { VKEY_DELETE, CtrlKey, "DeleteWordForward" },
139 #endif
140 #if OS(MACOSX)
141 { 'B', CommandKey, "ToggleBold" },
142 { 'I', CommandKey, "ToggleItalic" },
143 #else
144 { 'B', CtrlKey, "ToggleBold" },
145 { 'I', CtrlKey, "ToggleItalic" },
146 #endif
147 { 'U', CtrlKey, "ToggleUnderline" },
148 { VKEY_ESCAPE, 0, "Cancel" },
149 { VKEY_OEM_PERIOD, CtrlKey, "Cancel" },
150 { VKEY_TAB, 0, "InsertTab" },
151 { VKEY_TAB, ShiftKey, "InsertBacktab" },
152 { VKEY_RETURN, 0, "InsertNewline" },
153 { VKEY_RETURN, CtrlKey, "InsertNewline" },
154 { VKEY_RETURN, AltKey, "InsertNewline" },
155 { VKEY_RETURN, AltKey | ShiftKey, "InsertNewline" },
156 { VKEY_RETURN, ShiftKey, "InsertLineBreak" },
157 { VKEY_INSERT, CtrlKey, "Copy" },
158 { VKEY_INSERT, ShiftKey, "Paste" },
159 { VKEY_DELETE, ShiftKey, "Cut" },
160 #if !OS(MACOSX)
161 // On OS X, we pipe these back to the browser, so that it can do menu item
162 // blinking.
163 { 'C', CtrlKey, "Copy" },
164 { 'V', CtrlKey, "Paste" },
165 { 'V', CtrlKey | ShiftKey, "PasteAndMatchStyle" },
166 { 'X', CtrlKey, "Cut" },
167 { 'A', CtrlKey, "SelectAll" },
168 { 'Z', CtrlKey, "Undo" },
169 { 'Z', CtrlKey | ShiftKey, "Redo" },
170 { 'Y', CtrlKey, "Redo" },
171 #endif
172 { VKEY_INSERT, 0, "OverWrite" },
173 };
174
175 static const KeyPressEntry keyPressEntries[] = {
176 { '\t', 0, "InsertTab" },
177 { '\t', ShiftKey, "InsertBacktab" },
178 { '\r', 0, "InsertNewline" },
179 { '\r', CtrlKey, "InsertNewline" },
180 { '\r', ShiftKey, "InsertLineBreak" },
181 { '\r', AltKey, "InsertNewline" },
182 { '\r', AltKey | ShiftKey, "InsertNewline" },
183 };
184
interpretKeyEvent(const KeyboardEvent * evt)185 const char* Editor::interpretKeyEvent(const KeyboardEvent* evt)
186 {
187 const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
188 if (!keyEvent)
189 return "";
190
191 static HashMap<int, const char*>* keyDownCommandsMap = 0;
192 static HashMap<int, const char*>* keyPressCommandsMap = 0;
193
194 if (!keyDownCommandsMap) {
195 keyDownCommandsMap = new HashMap<int, const char*>;
196 keyPressCommandsMap = new HashMap<int, const char*>;
197
198 for (unsigned i = 0; i < arraysize(keyDownEntries); i++) {
199 keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
200 }
201
202 for (unsigned i = 0; i < arraysize(keyPressEntries); i++) {
203 keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
204 }
205 }
206
207 unsigned modifiers = 0;
208 if (keyEvent->shiftKey())
209 modifiers |= ShiftKey;
210 if (keyEvent->altKey())
211 modifiers |= AltKey;
212 if (keyEvent->ctrlKey())
213 modifiers |= CtrlKey;
214 if (keyEvent->metaKey())
215 modifiers |= MetaKey;
216
217 if (keyEvent->type() == PlatformEvent::RawKeyDown) {
218 int mapKey = modifiers << 16 | evt->keyCode();
219 return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
220 }
221
222 int mapKey = modifiers << 16 | evt->charCode();
223 return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
224 }
225
handleEditingKeyboardEvent(KeyboardEvent * evt)226 bool Editor::handleEditingKeyboardEvent(KeyboardEvent* evt)
227 {
228 const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
229 // do not treat this as text input if it's a system key event
230 if (!keyEvent || keyEvent->isSystemKey())
231 return false;
232
233 String commandName = interpretKeyEvent(evt);
234 Command command = this->command(commandName);
235
236 if (keyEvent->type() == PlatformEvent::RawKeyDown) {
237 // WebKit doesn't have enough information about mode to decide how
238 // commands that just insert text if executed via Editor should be treated,
239 // so we leave it upon WebCore to either handle them immediately
240 // (e.g. Tab that changes focus) or let a keypress event be generated
241 // (e.g. Tab that inserts a Tab character, or Enter).
242 if (command.isTextInsertion() || commandName.isEmpty())
243 return false;
244 if (command.execute(evt)) {
245 client().didExecuteCommand(commandName);
246 return true;
247 }
248 return false;
249 }
250
251 if (command.execute(evt)) {
252 client().didExecuteCommand(commandName);
253 return true;
254 }
255
256 // Here we need to filter key events.
257 // On Gtk/Linux, it emits key events with ASCII text and ctrl on for ctrl-<x>.
258 // In Webkit, EditorClient::handleKeyboardEvent in
259 // WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp drop such events.
260 // On Mac, it emits key events with ASCII text and meta on for Command-<x>.
261 // These key events should not emit text insert event.
262 // Alt key would be used to insert alternative character, so we should let
263 // through. Also note that Ctrl-Alt combination equals to AltGr key which is
264 // also used to insert alternative character.
265 // http://code.google.com/p/chromium/issues/detail?id=10846
266 // Windows sets both alt and meta are on when "Alt" key pressed.
267 // http://code.google.com/p/chromium/issues/detail?id=2215
268 // Also, we should not rely on an assumption that keyboards don't
269 // send ASCII characters when pressing a control key on Windows,
270 // which may be configured to do it so by user.
271 // See also http://en.wikipedia.org/wiki/Keyboard_Layout
272 // FIXME(ukai): investigate more detail for various keyboard layout.
273 if (evt->keyEvent()->text().length() == 1) {
274 UChar ch = evt->keyEvent()->text()[0U];
275
276 // Don't insert null or control characters as they can result in
277 // unexpected behaviour
278 if (ch < ' ')
279 return false;
280 #if !OS(WIN)
281 // Don't insert ASCII character if ctrl w/o alt or meta is on.
282 // On Mac, we should ignore events when meta is on (Command-<x>).
283 if (ch < 0x80) {
284 if (evt->keyEvent()->ctrlKey() && !evt->keyEvent()->altKey())
285 return false;
286 #if OS(MACOSX)
287 if (evt->keyEvent()->metaKey())
288 return false;
289 #endif
290 }
291 #endif
292 }
293
294 if (!canEdit())
295 return false;
296
297 return insertText(evt->keyEvent()->text(), evt);
298 }
299
handleKeyboardEvent(KeyboardEvent * evt)300 void Editor::handleKeyboardEvent(KeyboardEvent* evt)
301 {
302 // Give the embedder a chance to handle the keyboard event.
303 if (client().handleKeyboardEvent() || handleEditingKeyboardEvent(evt))
304 evt->setDefaultHandled();
305 }
306
307 } // namesace WebCore
308