Roll src/third_party/WebKit bf18a82:a9cee16 (svn 185297:185304)
[chromium-blink-merge.git] / chrome / test / chromedriver / key_converter.cc
bloba384b96cef255350cc90b9a77591583b18d5721a
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/test/chromedriver/key_converter.h"
7 #include "base/format_macros.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/test/chromedriver/chrome/status.h"
11 #include "chrome/test/chromedriver/chrome/ui_events.h"
12 #include "chrome/test/chromedriver/keycode_text_conversion.h"
14 namespace {
16 struct ModifierMaskAndKeyCode {
17 int mask;
18 ui::KeyboardCode key_code;
21 const ModifierMaskAndKeyCode kModifiers[] = {
22 { kShiftKeyModifierMask, ui::VKEY_SHIFT },
23 { kControlKeyModifierMask, ui::VKEY_CONTROL },
24 { kAltKeyModifierMask, ui::VKEY_MENU }
27 // TODO(kkania): Use this in KeyMap.
28 // Ordered list of all the key codes corresponding to special WebDriver keys.
29 // These WebDriver keys are defined in the Unicode Private Use Area.
30 // http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
31 const ui::KeyboardCode kSpecialWebDriverKeys[] = {
32 ui::VKEY_UNKNOWN,
33 ui::VKEY_UNKNOWN,
34 ui::VKEY_HELP,
35 ui::VKEY_BACK,
36 ui::VKEY_TAB,
37 ui::VKEY_CLEAR,
38 ui::VKEY_RETURN,
39 ui::VKEY_RETURN,
40 ui::VKEY_SHIFT,
41 ui::VKEY_CONTROL,
42 ui::VKEY_MENU,
43 ui::VKEY_PAUSE,
44 ui::VKEY_ESCAPE,
45 ui::VKEY_SPACE,
46 ui::VKEY_PRIOR, // page up
47 ui::VKEY_NEXT, // page down
48 ui::VKEY_END,
49 ui::VKEY_HOME,
50 ui::VKEY_LEFT,
51 ui::VKEY_UP,
52 ui::VKEY_RIGHT,
53 ui::VKEY_DOWN,
54 ui::VKEY_INSERT,
55 ui::VKEY_DELETE,
56 ui::VKEY_OEM_1, // semicolon
57 ui::VKEY_OEM_PLUS, // equals
58 ui::VKEY_NUMPAD0,
59 ui::VKEY_NUMPAD1,
60 ui::VKEY_NUMPAD2,
61 ui::VKEY_NUMPAD3,
62 ui::VKEY_NUMPAD4,
63 ui::VKEY_NUMPAD5,
64 ui::VKEY_NUMPAD6,
65 ui::VKEY_NUMPAD7,
66 ui::VKEY_NUMPAD8,
67 ui::VKEY_NUMPAD9,
68 ui::VKEY_MULTIPLY,
69 ui::VKEY_ADD,
70 ui::VKEY_OEM_COMMA,
71 ui::VKEY_SUBTRACT,
72 ui::VKEY_DECIMAL,
73 ui::VKEY_DIVIDE,
74 ui::VKEY_UNKNOWN,
75 ui::VKEY_UNKNOWN,
76 ui::VKEY_UNKNOWN,
77 ui::VKEY_UNKNOWN,
78 ui::VKEY_UNKNOWN,
79 ui::VKEY_UNKNOWN,
80 ui::VKEY_UNKNOWN,
81 ui::VKEY_F1,
82 ui::VKEY_F2,
83 ui::VKEY_F3,
84 ui::VKEY_F4,
85 ui::VKEY_F5,
86 ui::VKEY_F6,
87 ui::VKEY_F7,
88 ui::VKEY_F8,
89 ui::VKEY_F9,
90 ui::VKEY_F10,
91 ui::VKEY_F11,
92 ui::VKEY_F12};
94 const base::char16 kWebDriverNullKey = 0xE000U;
95 const base::char16 kWebDriverShiftKey = 0xE008U;
96 const base::char16 kWebDriverControlKey = 0xE009U;
97 const base::char16 kWebDriverAltKey = 0xE00AU;
98 const base::char16 kWebDriverCommandKey = 0xE03DU;
100 // Returns whether the given key code has a corresponding printable char.
101 // Notice: The given key code should be a special WebDriver key code.
102 bool IsSpecialKeyPrintable(ui::KeyboardCode key_code) {
103 return key_code == ui::VKEY_TAB || key_code == ui::VKEY_SPACE ||
104 key_code == ui::VKEY_OEM_1 || key_code == ui::VKEY_OEM_PLUS ||
105 key_code == ui::VKEY_OEM_COMMA ||
106 (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_DIVIDE);
109 // Returns whether the given key is a WebDriver key modifier.
110 bool IsModifierKey(base::char16 key) {
111 switch (key) {
112 case kWebDriverShiftKey:
113 case kWebDriverControlKey:
114 case kWebDriverAltKey:
115 case kWebDriverCommandKey:
116 return true;
117 default:
118 return false;
122 // Gets the key code associated with |key|, if it is a special WebDriver key.
123 // Returns whether |key| is a special WebDriver key. If true, |key_code| will
124 // be set.
125 bool KeyCodeFromSpecialWebDriverKey(base::char16 key,
126 ui::KeyboardCode* key_code) {
127 int index = static_cast<int>(key) - 0xE000U;
128 bool is_special_key = index >= 0 &&
129 index < static_cast<int>(arraysize(kSpecialWebDriverKeys));
130 if (is_special_key)
131 *key_code = kSpecialWebDriverKeys[index];
132 return is_special_key;
135 // Gets the key code associated with |key_utf16|, if it is a special shorthand
136 // key. Shorthand keys are common text equivalents for keys, such as the newline
137 // character, which is shorthand for the return key. Returns whether |key| is
138 // a shorthand key. If true, |key_code| will be set and |client_should_skip|
139 // will be set to whether the key should be skipped.
140 bool KeyCodeFromShorthandKey(base::char16 key_utf16,
141 ui::KeyboardCode* key_code,
142 bool* client_should_skip) {
143 base::string16 key_str_utf16;
144 key_str_utf16.push_back(key_utf16);
145 std::string key_str_utf8 = base::UTF16ToUTF8(key_str_utf16);
146 if (key_str_utf8.length() != 1)
147 return false;
148 bool should_skip = false;
149 char key = key_str_utf8[0];
150 if (key == '\n') {
151 *key_code = ui::VKEY_RETURN;
152 } else if (key == '\t') {
153 *key_code = ui::VKEY_TAB;
154 } else if (key == '\b') {
155 *key_code = ui::VKEY_BACK;
156 } else if (key == ' ') {
157 *key_code = ui::VKEY_SPACE;
158 } else if (key == '\r') {
159 *key_code = ui::VKEY_UNKNOWN;
160 should_skip = true;
161 } else {
162 return false;
164 *client_should_skip = should_skip;
165 return true;
168 } // namespace
170 KeyEvent CreateKeyDownEvent(ui::KeyboardCode key_code, int modifiers) {
171 return KeyEvent(
172 kRawKeyDownEventType, modifiers, std::string(), std::string(), key_code);
175 KeyEvent CreateKeyUpEvent(ui::KeyboardCode key_code, int modifiers) {
176 return KeyEvent(
177 kKeyUpEventType, modifiers, std::string(), std::string(), key_code);
180 KeyEvent CreateCharEvent(const std::string& unmodified_text,
181 const std::string& modified_text,
182 int modifiers) {
183 return KeyEvent(kCharEventType,
184 modifiers,
185 modified_text,
186 unmodified_text,
187 ui::VKEY_UNKNOWN);
190 Status ConvertKeysToKeyEvents(const base::string16& client_keys,
191 bool release_modifiers,
192 int* modifiers,
193 std::list<KeyEvent>* client_key_events) {
194 std::list<KeyEvent> key_events;
196 base::string16 keys = client_keys;
197 // Add an implicit NULL character to the end of the input to depress all
198 // modifiers.
199 if (release_modifiers)
200 keys.push_back(kWebDriverNullKey);
202 int sticky_modifiers = *modifiers;
203 for (size_t i = 0; i < keys.size(); ++i) {
204 base::char16 key = keys[i];
206 if (key == kWebDriverNullKey) {
207 // Release all modifier keys and clear |stick_modifiers|.
208 if (sticky_modifiers & kShiftKeyModifierMask)
209 key_events.push_back(CreateKeyUpEvent(ui::VKEY_SHIFT, 0));
210 if (sticky_modifiers & kControlKeyModifierMask)
211 key_events.push_back(CreateKeyUpEvent(ui::VKEY_CONTROL, 0));
212 if (sticky_modifiers & kAltKeyModifierMask)
213 key_events.push_back(CreateKeyUpEvent(ui::VKEY_MENU, 0));
214 if (sticky_modifiers & kMetaKeyModifierMask)
215 key_events.push_back(CreateKeyUpEvent(ui::VKEY_COMMAND, 0));
216 sticky_modifiers = 0;
217 continue;
219 if (IsModifierKey(key)) {
220 // Press or release the modifier, and adjust |sticky_modifiers|.
221 bool modifier_down = false;
222 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
223 if (key == kWebDriverShiftKey) {
224 sticky_modifiers ^= kShiftKeyModifierMask;
225 modifier_down = (sticky_modifiers & kShiftKeyModifierMask) != 0;
226 key_code = ui::VKEY_SHIFT;
227 } else if (key == kWebDriverControlKey) {
228 sticky_modifiers ^= kControlKeyModifierMask;
229 modifier_down = (sticky_modifiers & kControlKeyModifierMask) != 0;
230 key_code = ui::VKEY_CONTROL;
231 } else if (key == kWebDriverAltKey) {
232 sticky_modifiers ^= kAltKeyModifierMask;
233 modifier_down = (sticky_modifiers & kAltKeyModifierMask) != 0;
234 key_code = ui::VKEY_MENU;
235 } else if (key == kWebDriverCommandKey) {
236 sticky_modifiers ^= kMetaKeyModifierMask;
237 modifier_down = (sticky_modifiers & kMetaKeyModifierMask) != 0;
238 key_code = ui::VKEY_COMMAND;
239 } else {
240 return Status(kUnknownError, "unknown modifier key");
242 if (modifier_down)
243 key_events.push_back(CreateKeyDownEvent(key_code, sticky_modifiers));
244 else
245 key_events.push_back(CreateKeyUpEvent(key_code, sticky_modifiers));
246 continue;
249 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
250 std::string unmodified_text, modified_text;
251 int all_modifiers = sticky_modifiers;
253 // Get the key code, text, and modifiers for the given key.
254 bool should_skip = false;
255 bool is_special_key = KeyCodeFromSpecialWebDriverKey(key, &key_code);
256 std::string error_msg;
257 if (is_special_key ||
258 KeyCodeFromShorthandKey(key, &key_code, &should_skip)) {
259 if (should_skip)
260 continue;
261 if (key_code == ui::VKEY_UNKNOWN) {
262 return Status(kUnknownError, base::StringPrintf(
263 "unknown WebDriver key(%d) at string index (%" PRIuS ")",
264 static_cast<int>(key),
265 i));
267 if (key_code == ui::VKEY_RETURN) {
268 // For some reason Chrome expects a carriage return for the return key.
269 modified_text = unmodified_text = "\r";
270 } else if (is_special_key && !IsSpecialKeyPrintable(key_code)) {
271 // To prevent char event for special keys like DELETE.
272 modified_text = unmodified_text = std::string();
273 } else {
274 // WebDriver assumes a numpad key should translate to the number,
275 // which requires NumLock to be on with some platforms. This isn't
276 // formally in the spec, but is expected by their tests.
277 int webdriver_modifiers = 0;
278 if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_NUMPAD9)
279 webdriver_modifiers = kNumLockKeyModifierMask;
280 if (!ConvertKeyCodeToText(
281 key_code, webdriver_modifiers, &unmodified_text, &error_msg))
282 return Status(kUnknownError, error_msg);
283 if (!ConvertKeyCodeToText(
284 key_code, all_modifiers | webdriver_modifiers, &modified_text,
285 &error_msg))
286 return Status(kUnknownError, error_msg);
288 } else {
289 int necessary_modifiers = 0;
290 ConvertCharToKeyCode(key, &key_code, &necessary_modifiers, &error_msg);
291 if (!error_msg.empty())
292 return Status(kUnknownError, error_msg);
293 all_modifiers |= necessary_modifiers;
294 if (key_code != ui::VKEY_UNKNOWN) {
295 if (!ConvertKeyCodeToText(key_code, 0, &unmodified_text, &error_msg))
296 return Status(kUnknownError, error_msg);
297 if (!ConvertKeyCodeToText(
298 key_code, all_modifiers, &modified_text, &error_msg))
299 return Status(kUnknownError, error_msg);
300 if (unmodified_text.empty() || modified_text.empty()) {
301 // To prevent char event for special cases like CTRL + x (cut).
302 unmodified_text.clear();
303 modified_text.clear();
305 } else {
306 // Do a best effort and use the raw key we were given.
307 unmodified_text = base::UTF16ToUTF8(keys.substr(i, 1));
308 modified_text = base::UTF16ToUTF8(keys.substr(i, 1));
312 // Create the key events.
313 bool necessary_modifiers[3];
314 for (int i = 0; i < 3; ++i) {
315 necessary_modifiers[i] =
316 all_modifiers & kModifiers[i].mask &&
317 !(sticky_modifiers & kModifiers[i].mask);
318 if (necessary_modifiers[i]) {
319 key_events.push_back(
320 CreateKeyDownEvent(kModifiers[i].key_code, sticky_modifiers));
324 key_events.push_back(CreateKeyDownEvent(key_code, all_modifiers));
325 if (unmodified_text.length() || modified_text.length()) {
326 key_events.push_back(
327 CreateCharEvent(unmodified_text, modified_text, all_modifiers));
329 key_events.push_back(CreateKeyUpEvent(key_code, all_modifiers));
331 for (int i = 2; i > -1; --i) {
332 if (necessary_modifiers[i]) {
333 key_events.push_back(
334 CreateKeyUpEvent(kModifiers[i].key_code, sticky_modifiers));
338 client_key_events->swap(key_events);
339 *modifiers = sticky_modifiers;
340 return Status(kOk);