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 "third_party/blink/renderer/core/editing/editing_behavior.h"
28 
29 #include "build/build_config.h"
30 #include "third_party/blink/public/common/input/web_input_event.h"
31 #include "third_party/blink/public/web/web_settings.h"
32 #include "third_party/blink/renderer/core/events/keyboard_event.h"
33 #include "third_party/blink/renderer/platform/keyboard_codes.h"
34 #include "third_party/blink/renderer/platform/wtf/assertions.h"
35 
36 namespace blink {
37 
38 namespace {
39 
40 //
41 // The below code was adapted from the WebKit file webview.cpp
42 //
43 
44 const unsigned kCtrlKey = WebInputEvent::kControlKey;
45 const unsigned kAltKey = WebInputEvent::kAltKey;
46 const unsigned kShiftKey = WebInputEvent::kShiftKey;
47 const unsigned kMetaKey = WebInputEvent::kMetaKey;
48 #if defined(OS_MAC)
49 // Aliases for the generic key defintions to make kbd shortcuts definitions more
50 // readable on OS X.
51 const unsigned kOptionKey = kAltKey;
52 
53 // Do not use this constant for anything but cursor movement commands. Keys
54 // with cmd set have their |isSystemKey| bit set, so chances are the shortcut
55 // will not be executed. Another, less important, reason is that shortcuts
56 // defined in the layoutObject do not blink the menu item that they triggered.
57 // See http://crbug.com/25856 and the bugs linked from there for details.
58 const unsigned kCommandKey = kMetaKey;
59 #endif
60 
61 // Keys with special meaning. These will be delegated to the editor using
62 // the execCommand() method
63 struct KeyboardCodeKeyDownEntry {
64   unsigned virtual_key;
65   unsigned modifiers;
66   const char* name;
67 };
68 
69 struct KeyboardCodeKeyPressEntry {
70   unsigned char_code;
71   unsigned modifiers;
72   const char* name;
73 };
74 
75 // DomKey has a broader range than KeyboardCode, we need DomKey to handle some
76 // special keys.
77 // Note: We cannot use DomKey for printable keys since it may vary based on
78 // locale.
79 struct DomKeyKeyDownEntry {
80   const char* key;
81   unsigned modifiers;
82   const char* name;
83 };
84 
85 // Key bindings with command key on Mac and alt key on other platforms are
86 // marked as system key events and will be ignored (with the exception
87 // of Command-B and Command-I) so they shouldn't be added here.
88 const KeyboardCodeKeyDownEntry kKeyboardCodeKeyDownEntries[] = {
89     {VKEY_LEFT, 0, "MoveLeft"},
90     {VKEY_LEFT, kShiftKey, "MoveLeftAndModifySelection"},
91 #if defined(OS_MAC)
92     {VKEY_LEFT, kOptionKey, "MoveWordLeft"},
93     {VKEY_LEFT, kOptionKey | kShiftKey, "MoveWordLeftAndModifySelection"},
94 #else
95     {VKEY_LEFT, kCtrlKey, "MoveWordLeft"},
96     {VKEY_LEFT, kCtrlKey | kShiftKey, "MoveWordLeftAndModifySelection"},
97 #endif
98     {VKEY_RIGHT, 0, "MoveRight"},
99     {VKEY_RIGHT, kShiftKey, "MoveRightAndModifySelection"},
100 #if defined(OS_MAC)
101     {VKEY_RIGHT, kOptionKey, "MoveWordRight"},
102     {VKEY_RIGHT, kOptionKey | kShiftKey, "MoveWordRightAndModifySelection"},
103 #else
104     {VKEY_RIGHT, kCtrlKey, "MoveWordRight"},
105     {VKEY_RIGHT, kCtrlKey | kShiftKey, "MoveWordRightAndModifySelection"},
106 #endif
107     {VKEY_UP, 0, "MoveUp"},
108     {VKEY_UP, kShiftKey, "MoveUpAndModifySelection"},
109     {VKEY_PRIOR, kShiftKey, "MovePageUpAndModifySelection"},
110     {VKEY_DOWN, 0, "MoveDown"},
111     {VKEY_DOWN, kShiftKey, "MoveDownAndModifySelection"},
112     {VKEY_NEXT, kShiftKey, "MovePageDownAndModifySelection"},
113 #if !defined(OS_MAC)
114     {VKEY_UP, kCtrlKey, "MoveParagraphBackward"},
115     {VKEY_UP, kCtrlKey | kShiftKey, "MoveParagraphBackwardAndModifySelection"},
116     {VKEY_DOWN, kCtrlKey, "MoveParagraphForward"},
117     {VKEY_DOWN, kCtrlKey | kShiftKey, "MoveParagraphForwardAndModifySelection"},
118     {VKEY_PRIOR, 0, "MovePageUp"},
119     {VKEY_NEXT, 0, "MovePageDown"},
120 #endif
121     {VKEY_HOME, 0, "MoveToBeginningOfLine"},
122     {VKEY_HOME, kShiftKey, "MoveToBeginningOfLineAndModifySelection"},
123 #if defined(OS_MAC)
124     {VKEY_PRIOR, kOptionKey, "MovePageUp"},
125     {VKEY_NEXT, kOptionKey, "MovePageDown"},
126 #endif
127 #if !defined(OS_MAC)
128     {VKEY_HOME, kCtrlKey, "MoveToBeginningOfDocument"},
129     {VKEY_HOME, kCtrlKey | kShiftKey,
130      "MoveToBeginningOfDocumentAndModifySelection"},
131 #endif
132     {VKEY_END, 0, "MoveToEndOfLine"},
133     {VKEY_END, kShiftKey, "MoveToEndOfLineAndModifySelection"},
134 #if !defined(OS_MAC)
135     {VKEY_END, kCtrlKey, "MoveToEndOfDocument"},
136     {VKEY_END, kCtrlKey | kShiftKey, "MoveToEndOfDocumentAndModifySelection"},
137 #endif
138     {VKEY_BACK, 0, "DeleteBackward"},
139     {VKEY_BACK, kShiftKey, "DeleteBackward"},
140     {VKEY_DELETE, 0, "DeleteForward"},
141 #if defined(OS_MAC)
142     {VKEY_BACK, kOptionKey, "DeleteWordBackward"},
143     {VKEY_DELETE, kOptionKey, "DeleteWordForward"},
144 #else
145     {VKEY_BACK, kCtrlKey, "DeleteWordBackward"},
146     {VKEY_DELETE, kCtrlKey, "DeleteWordForward"},
147 #endif
148 #if defined(OS_MAC)
149     {'B', kCommandKey, "ToggleBold"},
150     {'I', kCommandKey, "ToggleItalic"},
151 #else
152     {'B', kCtrlKey, "ToggleBold"},
153     {'I', kCtrlKey, "ToggleItalic"},
154 #endif
155     {'U', kCtrlKey, "ToggleUnderline"},
156     {VKEY_ESCAPE, 0, "Cancel"},
157     {VKEY_OEM_PERIOD, kCtrlKey, "Cancel"},
158     {VKEY_TAB, 0, "InsertTab"},
159     {VKEY_TAB, kShiftKey, "InsertBacktab"},
160     {VKEY_RETURN, 0, "InsertNewline"},
161     {VKEY_RETURN, kCtrlKey, "InsertNewline"},
162     {VKEY_RETURN, kAltKey, "InsertNewline"},
163     {VKEY_RETURN, kAltKey | kShiftKey, "InsertNewline"},
164     {VKEY_RETURN, kShiftKey, "InsertLineBreak"},
165     {VKEY_INSERT, kCtrlKey, "Copy"},
166     {VKEY_INSERT, kShiftKey, "Paste"},
167     {VKEY_DELETE, kShiftKey, "Cut"},
168 #if !defined(OS_MAC)
169     // On OS X, we pipe these back to the browser, so that it can do menu item
170     // blinking.
171     {'C', kCtrlKey, "Copy"},
172     {'V', kCtrlKey, "Paste"},
173     {'V', kCtrlKey | kShiftKey, "PasteAndMatchStyle"},
174     {'X', kCtrlKey, "Cut"},
175     {'A', kCtrlKey, "SelectAll"},
176     {'Z', kCtrlKey, "Undo"},
177     {'Z', kCtrlKey | kShiftKey, "Redo"},
178     {'Y', kCtrlKey, "Redo"},
179 #endif
180 #if defined(OS_WIN)
181     {VKEY_BACK, kAltKey, "Undo"},
182     {VKEY_BACK, kAltKey | kShiftKey, "Redo"},
183 #endif
184     {VKEY_INSERT, 0, "OverWrite"},
185 };
186 
187 const KeyboardCodeKeyPressEntry kKeyboardCodeKeyPressEntries[] = {
188     {'\t', 0, "InsertTab"},
189     {'\t', kShiftKey, "InsertBacktab"},
190     {'\r', 0, "InsertNewline"},
191     {'\r', kShiftKey, "InsertLineBreak"},
192 };
193 
194 const DomKeyKeyDownEntry kDomKeyKeyDownEntries[] = {
195     {"Copy", 0, "Copy"},
196     {"Cut", 0, "Cut"},
197     {"Paste", 0, "Paste"},
198 };
199 
LookupCommandNameFromDomKeyKeyDown(const String & key,unsigned modifiers)200 const char* LookupCommandNameFromDomKeyKeyDown(const String& key,
201                                                unsigned modifiers) {
202   // This table is not likely to grow, so sequential search is fine here.
203   for (const auto& entry : kDomKeyKeyDownEntries) {
204     if (key == entry.key && modifiers == entry.modifiers)
205       return entry.name;
206   }
207   return nullptr;
208 }
209 
210 }  // anonymous namespace
211 
InterpretKeyEvent(const KeyboardEvent & event) const212 const char* EditingBehavior::InterpretKeyEvent(
213     const KeyboardEvent& event) const {
214   const WebKeyboardEvent* key_event = event.KeyEvent();
215   if (!key_event)
216     return "";
217 
218   static HashMap<int, const char*>* key_down_commands_map = nullptr;
219   static HashMap<int, const char*>* key_press_commands_map = nullptr;
220 
221   if (!key_down_commands_map) {
222     key_down_commands_map = new HashMap<int, const char*>;
223     key_press_commands_map = new HashMap<int, const char*>;
224 
225     for (const auto& entry : kKeyboardCodeKeyDownEntries) {
226       key_down_commands_map->Set(entry.modifiers << 16 | entry.virtual_key,
227                                  entry.name);
228     }
229 
230     for (const auto& entry : kKeyboardCodeKeyPressEntries) {
231       key_press_commands_map->Set(entry.modifiers << 16 | entry.char_code,
232                                   entry.name);
233     }
234   }
235 
236   unsigned modifiers =
237       key_event->GetModifiers() & (kShiftKey | kAltKey | kCtrlKey | kMetaKey);
238 
239   if (key_event->GetType() == WebInputEvent::Type::kRawKeyDown) {
240     int map_key = modifiers << 16 | event.keyCode();
241     const char* name = map_key ? key_down_commands_map->at(map_key) : nullptr;
242     if (!name)
243       name = LookupCommandNameFromDomKeyKeyDown(event.key(), modifiers);
244     return name;
245   }
246 
247   int map_key = modifiers << 16 | event.charCode();
248   return map_key ? key_press_commands_map->at(map_key) : nullptr;
249 }
250 
ShouldInsertCharacter(const KeyboardEvent & event) const251 bool EditingBehavior::ShouldInsertCharacter(const KeyboardEvent& event) const {
252   if (event.KeyEvent()->text[1] != 0)
253     return true;
254 
255   // On Gtk/Linux, it emits key events with ASCII text and ctrl on for ctrl-<x>.
256   // In Webkit, EditorClient::handleKeyboardEvent in
257   // WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp drop such events.
258   // On Mac, it emits key events with ASCII text and meta on for Command-<x>.
259   // These key events should not emit text insert event.
260   // Alt key would be used to insert alternative character, so we should let
261   // through. Also note that Ctrl-Alt combination equals to AltGr key which is
262   // also used to insert alternative character.
263   // http://code.google.com/p/chromium/issues/detail?id=10846
264   // Windows sets both alt and meta are on when "Alt" key pressed.
265   // http://code.google.com/p/chromium/issues/detail?id=2215
266   // Also, we should not rely on an assumption that keyboards don't
267   // send ASCII characters when pressing a control key on Windows,
268   // which may be configured to do it so by user.
269   // See also http://en.wikipedia.org/wiki/Keyboard_Layout
270   // FIXME(ukai): investigate more detail for various keyboard layout.
271   UChar ch = event.KeyEvent()->text[0U];
272 
273   // Don't insert null or control characters as they can result in
274   // unexpected behaviour
275   if (ch < ' ')
276     return false;
277 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
278   // According to XKB map no keyboard combinations with ctrl key are mapped to
279   // printable characters, however we need the filter as the DomKey/text could
280   // contain printable characters.
281   if (event.ctrlKey())
282     return false;
283 #elif !defined(OS_WIN)
284   // Don't insert ASCII character if ctrl w/o alt or meta is on.
285   // On Mac, we should ignore events when meta is on (Command-<x>).
286   if (ch < 0x80) {
287     if (event.ctrlKey() && !event.altKey())
288       return false;
289 #if defined(OS_MAC)
290     if (event.metaKey())
291       return false;
292 #endif
293   }
294 #endif
295 
296   return true;
297 }
298 
299 }  // namespace blink
300