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.
4
5 #include "chrome/test/chromedriver/key_converter.h"
6
7 #include <stddef.h>
8
9 #include "base/format_macros.h"
10 #include "base/stl_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversion_utils.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/test/chromedriver/chrome/status.h"
15 #include "chrome/test/chromedriver/chrome/ui_events.h"
16 #include "chrome/test/chromedriver/keycode_text_conversion.h"
17
18 namespace {
19
20 struct ModifierMaskAndKeyCode {
21 int mask;
22 ui::KeyboardCode key_code;
23 };
24
25 const ModifierMaskAndKeyCode kModifiers[] = {
26 {kShiftKeyModifierMask, ui::VKEY_SHIFT},
27 {kControlKeyModifierMask, ui::VKEY_CONTROL},
28 {kAltKeyModifierMask, ui::VKEY_MENU},
29 {kMetaKeyModifierMask, ui::VKEY_COMMAND}};
30
31 // Ordered list of all the key codes corresponding to special WebDriver keys.
32 // These keys are "special" in the sense that their code points are defined by
33 // the W3C spec (https://w3c.github.io/webdriver/#dfn-normalised-key-value),
34 // and are in the Unicode Private Use Area. All other keys have their code
35 // points defined by the Unicode standard.
36 const ui::KeyboardCode kSpecialWebDriverKeys[] = {
37 ui::VKEY_UNKNOWN, // \uE000
38 ui::VKEY_CANCEL, // \uE001
39 ui::VKEY_HELP,
40 ui::VKEY_BACK,
41 ui::VKEY_TAB,
42 ui::VKEY_CLEAR,
43 ui::VKEY_RETURN,
44 ui::VKEY_RETURN,
45 ui::VKEY_SHIFT,
46 ui::VKEY_CONTROL,
47 ui::VKEY_MENU,
48 ui::VKEY_PAUSE,
49 ui::VKEY_ESCAPE,
50 ui::VKEY_SPACE,
51 ui::VKEY_PRIOR, // page up
52 ui::VKEY_NEXT, // page down
53 ui::VKEY_END, // \uE010
54 ui::VKEY_HOME,
55 ui::VKEY_LEFT,
56 ui::VKEY_UP,
57 ui::VKEY_RIGHT,
58 ui::VKEY_DOWN,
59 ui::VKEY_INSERT,
60 ui::VKEY_DELETE,
61 ui::VKEY_OEM_1, // semicolon
62 ui::VKEY_OEM_PLUS, // equals
63 ui::VKEY_NUMPAD0,
64 ui::VKEY_NUMPAD1,
65 ui::VKEY_NUMPAD2,
66 ui::VKEY_NUMPAD3,
67 ui::VKEY_NUMPAD4,
68 ui::VKEY_NUMPAD5,
69 ui::VKEY_NUMPAD6, // \uE020
70 ui::VKEY_NUMPAD7,
71 ui::VKEY_NUMPAD8,
72 ui::VKEY_NUMPAD9,
73 ui::VKEY_MULTIPLY,
74 ui::VKEY_ADD,
75 ui::VKEY_OEM_COMMA,
76 ui::VKEY_SUBTRACT,
77 ui::VKEY_DECIMAL,
78 ui::VKEY_DIVIDE,
79 ui::VKEY_UNKNOWN,
80 ui::VKEY_UNKNOWN,
81 ui::VKEY_UNKNOWN,
82 ui::VKEY_UNKNOWN,
83 ui::VKEY_UNKNOWN,
84 ui::VKEY_UNKNOWN,
85 ui::VKEY_UNKNOWN, // \uE030
86 ui::VKEY_F1,
87 ui::VKEY_F2,
88 ui::VKEY_F3,
89 ui::VKEY_F4,
90 ui::VKEY_F5,
91 ui::VKEY_F6,
92 ui::VKEY_F7,
93 ui::VKEY_F8,
94 ui::VKEY_F9,
95 ui::VKEY_F10,
96 ui::VKEY_F11,
97 ui::VKEY_F12,
98 ui::VKEY_LWIN, // meta
99 ui::VKEY_UNKNOWN,
100 ui::VKEY_UNKNOWN,
101 ui::VKEY_DBE_DBCSCHAR, // \uE040 ZenkakuHankaku
102 ui::VKEY_UNKNOWN,
103 ui::VKEY_UNKNOWN,
104 ui::VKEY_UNKNOWN,
105 ui::VKEY_UNKNOWN,
106 ui::VKEY_UNKNOWN,
107 ui::VKEY_UNKNOWN,
108 ui::VKEY_UNKNOWN,
109 ui::VKEY_UNKNOWN,
110 ui::VKEY_UNKNOWN,
111 ui::VKEY_UNKNOWN,
112 ui::VKEY_UNKNOWN,
113 ui::VKEY_UNKNOWN,
114 ui::VKEY_UNKNOWN,
115 ui::VKEY_UNKNOWN,
116 ui::VKEY_UNKNOWN,
117 ui::VKEY_RSHIFT, // \uE050
118 ui::VKEY_RCONTROL,
119 ui::VKEY_RMENU,
120 ui::VKEY_RWIN, // meta
121 ui::VKEY_PRIOR, // page up
122 ui::VKEY_NEXT, // page down
123 ui::VKEY_END,
124 ui::VKEY_HOME,
125 ui::VKEY_LEFT,
126 ui::VKEY_UP,
127 ui::VKEY_RIGHT,
128 ui::VKEY_DOWN,
129 ui::VKEY_INSERT,
130 ui::VKEY_DELETE,
131 };
132
133 const base::char16 kWebDriverNullKey = 0xE000U;
134 const base::char16 kWebDriverShiftKey = 0xE008U;
135 const base::char16 kWebDriverControlKey = 0xE009U;
136 const base::char16 kWebDriverAltKey = 0xE00AU;
137 const base::char16 kWebDriverCommandKey = 0xE03DU;
138 const base::char16 kWebDriverRightShiftKey = 0xE050U;
139 const base::char16 kWebDriverRightControlKey = 0xE051U;
140 const base::char16 kWebDriverRightAltKey = 0xE052U;
141 const base::char16 kWebDriverRightCommandKey = 0xE053U;
142
143 // Returns whether the given key code has a corresponding printable char.
144 // Notice: The given key code should be a special WebDriver key code.
IsSpecialKeyPrintable(ui::KeyboardCode key_code)145 bool IsSpecialKeyPrintable(ui::KeyboardCode key_code) {
146 return key_code == ui::VKEY_TAB || key_code == ui::VKEY_SPACE ||
147 key_code == ui::VKEY_OEM_1 || key_code == ui::VKEY_OEM_PLUS ||
148 key_code == ui::VKEY_OEM_COMMA ||
149 (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_DIVIDE);
150 }
151
152 // Returns whether the given key is a WebDriver key modifier.
IsModifierKey(base::char16 key)153 bool IsModifierKey(base::char16 key) {
154 switch (key) {
155 case kWebDriverShiftKey:
156 case kWebDriverControlKey:
157 case kWebDriverAltKey:
158 case kWebDriverCommandKey:
159 case kWebDriverRightShiftKey:
160 case kWebDriverRightControlKey:
161 case kWebDriverRightAltKey:
162 case kWebDriverRightCommandKey:
163 return true;
164 default:
165 return false;
166 }
167 }
168
169 // Gets the key code associated with |key|, if it is a special WebDriver key.
170 // Returns whether |key| is a special WebDriver key. If true, |key_code| will
171 // be set.
KeyCodeFromSpecialWebDriverKey(base::char16 key,ui::KeyboardCode * key_code)172 bool KeyCodeFromSpecialWebDriverKey(base::char16 key,
173 ui::KeyboardCode* key_code) {
174 int index = static_cast<int>(key) - 0xE000U;
175 bool is_special_key =
176 index >= 0 && index < static_cast<int>(base::size(kSpecialWebDriverKeys));
177 if (is_special_key)
178 *key_code = kSpecialWebDriverKeys[index];
179 return is_special_key;
180 }
181
182 // Gets the key code associated with |key_utf16|, if it is a special shorthand
183 // key. Shorthand keys are common text equivalents for keys, such as the newline
184 // character, which is shorthand for the return key. Returns whether |key| is
185 // a shorthand key. If true, |key_code| will be set and |client_should_skip|
186 // will be set to whether the key should be skipped.
KeyCodeFromShorthandKey(base::char16 key_utf16,ui::KeyboardCode * key_code,bool * client_should_skip)187 bool KeyCodeFromShorthandKey(base::char16 key_utf16,
188 ui::KeyboardCode* key_code,
189 bool* client_should_skip) {
190 base::string16 key_str_utf16;
191 key_str_utf16.push_back(key_utf16);
192 std::string key_str_utf8 = base::UTF16ToUTF8(key_str_utf16);
193 if (key_str_utf8.length() != 1)
194 return false;
195 bool should_skip = false;
196 char key = key_str_utf8[0];
197 if (key == '\n') {
198 *key_code = ui::VKEY_RETURN;
199 } else if (key == '\t') {
200 *key_code = ui::VKEY_TAB;
201 } else if (key == '\b') {
202 *key_code = ui::VKEY_BACK;
203 } else if (key == ' ') {
204 *key_code = ui::VKEY_SPACE;
205 } else if (key == '\r') {
206 *key_code = ui::VKEY_UNKNOWN;
207 should_skip = true;
208 } else {
209 return false;
210 }
211 *client_should_skip = should_skip;
212 return true;
213 }
214
215 // The "normalised key value" table from W3C spec
216 // (https://w3c.github.io/webdriver/#dfn-normalised-key-value).
217 // The code point starts at \uE000 and must increase by 1 with each row,
218 // with placeholders (empty strings) used for unassigned code points.
219 const int kNormalisedKeyValueBase = 0xE000;
220 const char* const kNormalisedKeyValue[] = {
221 "Unidentified", // \uE000
222 "Cancel", // \uE001
223 "Help", // \uE002
224 "Backspace", // \uE003
225 "Tab", // \uE004
226 "Clear", // \uE005
227 "Return", // \uE006
228 "Enter", // \uE007
229 "Shift", // \uE008
230 "Control", // \uE009
231 "Alt", // \uE00A
232 "Pause", // \uE00B
233 "Escape", // \uE00C
234 " ", // \uE00D
235 "PageUp", // \uE00E
236 "PageDown", // \uE00F
237 "End", // \uE010
238 "Home", // \uE011
239 "ArrowLeft", // \uE012
240 "ArrowUp", // \uE013
241 "ArrowRight", // \uE014
242 "ArrowDown", // \uE015
243 "Insert", // \uE016
244 "Delete", // \uE017
245 ";", // \uE018
246 "=", // \uE019
247 "0", // \uE01A
248 "1", // \uE01B
249 "2", // \uE01C
250 "3", // \uE01D
251 "4", // \uE01E
252 "5", // \uE01F
253 "6", // \uE020
254 "7", // \uE021
255 "8", // \uE022
256 "9", // \uE023
257 "*", // \uE024
258 "+", // \uE025
259 ",", // \uE026
260 "-", // \uE027
261 ".", // \uE028
262 "/", // \uE029
263 "",
264 "",
265 "",
266 "",
267 "",
268 "",
269 "",
270 "F1", // \uE031
271 "F2", // \uE032
272 "F3", // \uE033
273 "F4", // \uE034
274 "F5", // \uE035
275 "F6", // \uE036
276 "F7", // \uE037
277 "F8", // \uE038
278 "F9", // \uE039
279 "F10", // \uE03A
280 "F11", // \uE03B
281 "F12", // \uE03C
282 "Meta", // \uE03D
283 "",
284 "",
285 "ZenkakuHankaku", // \uE040
286 "",
287 "",
288 "",
289 "",
290 "",
291 "",
292 "",
293 "",
294 "",
295 "",
296 "",
297 "",
298 "",
299 "",
300 "",
301 "Shift", // \uE050
302 "Control", // \uE051
303 "Alt", // \uE052
304 "Meta", // \uE053
305 "PageUp", // \uE054
306 "PageDown", // \uE055
307 "End", // \uE056
308 "Home", // \uE057
309 "ArrowLeft", // \uE058
310 "ArrowUp", // \uE059
311 "ArrowRight", // \uE05A
312 "ArrowDown", // \uE05B
313 "Insert", // \uE05C
314 "Delete", // \uE05D
315 };
316
317 // The "code for key" table (https://w3c.github.io/webdriver/#dfn-code),
318 // with the following modifications:
319 // * Fixed some inconsistencies in the original table.
320 // See https://github.com/w3c/webdriver/pull/1384.
321 // * Replaced "OSLeft" and "OSRight" with "MetaLeft" and "MetaRight", to be
322 // compatible with Chrome.
323 // TODO(johnchen@chromium.org): Find a better way to handle this.
324 const struct {
325 base::char16 key;
326 base::char16 alternate_key;
327 std::string code;
328 } kCodeForKey[] = {
329 {'`', '~', "Backquote"},
330 {'\\', '|', "Backslash"},
331 {0xE003, 0, "Backspace"},
332 {'[', '{', "BracketLeft"},
333 {']', '}', "BracketRight"},
334 {',', '<', "Comma"},
335 {'0', ')', "Digit0"},
336 {'1', '!', "Digit1"},
337 {'2', '@', "Digit2"},
338 {'3', '#', "Digit3"},
339 {'4', '$', "Digit4"},
340 {'5', '%', "Digit5"},
341 {'6', '^', "Digit6"},
342 {'7', '&', "Digit7"},
343 {'8', '*', "Digit8"},
344 {'9', '(', "Digit9"},
345 {'=', '+', "Equal"},
346 {'<', '>', "IntlBackslash"},
347 {'a', 'A', "KeyA"},
348 {'b', 'B', "KeyB"},
349 {'c', 'C', "KeyC"},
350 {'d', 'D', "KeyD"},
351 {'e', 'E', "KeyE"},
352 {'f', 'F', "KeyF"},
353 {'g', 'G', "KeyG"},
354 {'h', 'H', "KeyH"},
355 {'i', 'I', "KeyI"},
356 {'j', 'J', "KeyJ"},
357 {'k', 'K', "KeyK"},
358 {'l', 'L', "KeyL"},
359 {'m', 'M', "KeyM"},
360 {'n', 'N', "KeyN"},
361 {'o', 'O', "KeyO"},
362 {'p', 'P', "KeyP"},
363 {'q', 'Q', "KeyQ"},
364 {'r', 'R', "KeyR"},
365 {'s', 'S', "KeyS"},
366 {'t', 'T', "KeyT"},
367 {'u', 'U', "KeyU"},
368 {'v', 'V', "KeyV"},
369 {'w', 'W', "KeyW"},
370 {'x', 'X', "KeyX"},
371 {'y', 'Y', "KeyY"},
372 {'z', 'Z', "KeyZ"},
373 {'-', '_', "Minus"},
374 {'.', '>', "Period"},
375 {'\'', '"', "Quote"},
376 {';', ':', "Semicolon"},
377 {'/', '?', "Slash"},
378 {0xE00A, 0, "AltLeft"},
379 {0xE052, 0, "AltRight"},
380 {0xE009, 0, "ControlLeft"},
381 {0xE051, 0, "ControlRight"},
382 {0xE006, 0, "Enter"},
383 {0xE03D, 0, "MetaLeft"},
384 {0xE053, 0, "MetaRight"},
385 {0xE008, 0, "ShiftLeft"},
386 {0xE050, 0, "ShiftRight"},
387 {' ', 0xE00D, "Space"},
388 {0xE004, 0, "Tab"},
389 {0xE017, 0, "Delete"},
390 {0xE010, 0, "End"},
391 {0xE002, 0, "Help"},
392 {0xE011, 0, "Home"},
393 {0xE016, 0, "Insert"},
394 {0xE00F, 0, "PageDown"},
395 {0xE00E, 0, "PageUp"},
396 {0xE015, 0, "ArrowDown"},
397 {0xE012, 0, "ArrowLeft"},
398 {0xE014, 0, "ArrowRight"},
399 {0xE013, 0, "ArrowUp"},
400 {0xE00C, 0, "Escape"},
401 {0xE031, 0, "F1"},
402 {0xE032, 0, "F2"},
403 {0xE033, 0, "F3"},
404 {0xE034, 0, "F4"},
405 {0xE035, 0, "F5"},
406 {0xE036, 0, "F6"},
407 {0xE037, 0, "F7"},
408 {0xE038, 0, "F8"},
409 {0xE039, 0, "F9"},
410 {0xE03A, 0, "F10"},
411 {0xE03B, 0, "F11"},
412 {0xE03C, 0, "F12"},
413 {0xE01A, 0xE05C, "Numpad0"},
414 {0xE01B, 0xE056, "Numpad1"},
415 {0xE01C, 0xE05B, "Numpad2"},
416 {0xE01D, 0xE055, "Numpad3"},
417 {0xE01E, 0xE058, "Numpad4"},
418 {0xE01F, 0, "Numpad5"},
419 {0xE020, 0xE05A, "Numpad6"},
420 {0xE021, 0xE057, "Numpad7"},
421 {0xE022, 0xE059, "Numpad8"},
422 {0xE023, 0xE054, "Numpad9"},
423 {0xE025, 0, "NumpadAdd"},
424 {0xE026, 0, "NumpadComma"},
425 {0xE028, 0xE05D, "NumpadDecimal"},
426 {0xE029, 0, "NumpadDivide"},
427 {0xE007, 0, "NumpadEnter"},
428 {0xE024, 0, "NumpadMultiply"},
429 {0xE027, 0, "NumpadSubtract"},
430 };
431
432 // The "key location for key" table from W3C spec
433 // (https://w3c.github.io/webdriver/#dfn-key-location). For simplicity, it is
434 // implemented as a few 'if' statements, instead of as a true table.
GetKeyLocation(uint32_t code_point)435 int GetKeyLocation(uint32_t code_point) {
436 if (code_point >= 0xe007 && code_point <= 0xe00a)
437 return 1;
438 if (code_point >= 0xe01a && code_point <= 0xe029)
439 return 3;
440 if (code_point == 0xe03d)
441 return 1;
442 if (code_point >= 0xe050 && code_point <= 0xe053)
443 return 2;
444 if (code_point >= 0xe054 && code_point <= 0xe05d)
445 return 3;
446 return 0;
447 }
448
449 } // namespace
450
ConvertKeysToKeyEvents(const base::string16 & client_keys,bool release_modifiers,int * modifiers,std::vector<KeyEvent> * client_key_events)451 Status ConvertKeysToKeyEvents(const base::string16& client_keys,
452 bool release_modifiers,
453 int* modifiers,
454 std::vector<KeyEvent>* client_key_events) {
455 std::vector<KeyEvent> key_events;
456
457 base::string16 keys = client_keys;
458 // Add an implicit NULL character to the end of the input to depress all
459 // modifiers.
460 if (release_modifiers)
461 keys.push_back(kWebDriverNullKey);
462
463 int sticky_modifiers = *modifiers;
464 for (size_t i = 0; i < keys.size(); ++i) {
465 base::char16 key = keys[i];
466
467 if (key == kWebDriverNullKey) {
468 // Release all modifier keys and clear |stick_modifiers|.
469 KeyEventBuilder builder;
470 builder.SetType(kKeyUpEventType);
471 if (sticky_modifiers & kShiftKeyModifierMask)
472 key_events.push_back(builder.SetKeyCode(ui::VKEY_SHIFT)->Build());
473 if (sticky_modifiers & kControlKeyModifierMask)
474 key_events.push_back(builder.SetKeyCode(ui::VKEY_CONTROL)->Build());
475 if (sticky_modifiers & kAltKeyModifierMask)
476 key_events.push_back(builder.SetKeyCode(ui::VKEY_MENU)->Build());
477 if (sticky_modifiers & kMetaKeyModifierMask)
478 key_events.push_back(builder.SetKeyCode(ui::VKEY_COMMAND)->Build());
479 sticky_modifiers = 0;
480 continue;
481 }
482 if (IsModifierKey(key)) {
483 // Press or release the modifier, and adjust |sticky_modifiers|.
484 bool modifier_down = false;
485 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
486 if (key == kWebDriverShiftKey || key == kWebDriverRightShiftKey) {
487 sticky_modifiers ^= kShiftKeyModifierMask;
488 modifier_down = (sticky_modifiers & kShiftKeyModifierMask) != 0;
489 key_code = ui::VKEY_SHIFT;
490 } else if (key == kWebDriverControlKey ||
491 key == kWebDriverRightControlKey) {
492 sticky_modifiers ^= kControlKeyModifierMask;
493 modifier_down = (sticky_modifiers & kControlKeyModifierMask) != 0;
494 key_code = ui::VKEY_CONTROL;
495 } else if (key == kWebDriverAltKey || key == kWebDriverRightAltKey) {
496 sticky_modifiers ^= kAltKeyModifierMask;
497 modifier_down = (sticky_modifiers & kAltKeyModifierMask) != 0;
498 key_code = ui::VKEY_MENU;
499 } else if (key == kWebDriverCommandKey ||
500 key == kWebDriverRightCommandKey) {
501 sticky_modifiers ^= kMetaKeyModifierMask;
502 modifier_down = (sticky_modifiers & kMetaKeyModifierMask) != 0;
503 key_code = ui::VKEY_COMMAND;
504 } else {
505 return Status(kUnknownError, "unknown modifier key");
506 }
507 KeyEventBuilder builder;
508 if (modifier_down)
509 builder.SetType(kRawKeyDownEventType);
510 else
511 builder.SetType(kKeyUpEventType);
512 key_events.push_back(builder.SetKeyCode(key_code)
513 ->SetModifiers(sticky_modifiers)
514 ->Build());
515 continue;
516 }
517
518 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
519 std::string unmodified_text, modified_text;
520 int all_modifiers = sticky_modifiers;
521
522 // Get the key code, text, and modifiers for the given key.
523 bool should_skip = false;
524 bool is_special_key = KeyCodeFromSpecialWebDriverKey(key, &key_code);
525 std::string error_msg;
526 if (is_special_key ||
527 KeyCodeFromShorthandKey(key, &key_code, &should_skip)) {
528 if (should_skip)
529 continue;
530 if (key_code == ui::VKEY_UNKNOWN) {
531 return Status(kUnknownError, base::StringPrintf(
532 "unknown WebDriver key(%d) at string index (%" PRIuS ")",
533 static_cast<int>(key),
534 i));
535 }
536 if (key_code == ui::VKEY_RETURN) {
537 // For some reason Chrome expects a carriage return for the return key.
538 modified_text = unmodified_text = "\r";
539 } else if (is_special_key && !IsSpecialKeyPrintable(key_code)) {
540 // To prevent char event for special keys like DELETE.
541 modified_text = unmodified_text = std::string();
542 } else {
543 // WebDriver assumes a numpad key should translate to the number,
544 // which requires NumLock to be on with some platforms. This isn't
545 // formally in the spec, but is expected by their tests.
546 int webdriver_modifiers = 0;
547 if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_NUMPAD9)
548 webdriver_modifiers = kNumLockKeyModifierMask;
549 if (!ConvertKeyCodeToText(
550 key_code, webdriver_modifiers, &unmodified_text, &error_msg))
551 return Status(kUnknownError, error_msg);
552 if (!ConvertKeyCodeToText(
553 key_code, all_modifiers | webdriver_modifiers, &modified_text,
554 &error_msg))
555 return Status(kUnknownError, error_msg);
556 }
557 } else {
558 int necessary_modifiers = 0;
559 ConvertCharToKeyCode(key, &key_code, &necessary_modifiers, &error_msg);
560 if (!error_msg.empty())
561 return Status(kUnknownError, error_msg);
562 all_modifiers |= necessary_modifiers;
563 if (key_code != ui::VKEY_UNKNOWN) {
564 if (!ConvertKeyCodeToText(key_code, 0, &unmodified_text, &error_msg))
565 return Status(kUnknownError, error_msg);
566 if (!ConvertKeyCodeToText(
567 key_code, all_modifiers, &modified_text, &error_msg))
568 return Status(kUnknownError, error_msg);
569 if (unmodified_text.empty() || modified_text.empty()) {
570 // To prevent char event for special cases like CTRL + x (cut).
571 unmodified_text.clear();
572 modified_text.clear();
573 }
574 } else {
575 // Do a best effort and use the raw key we were given.
576 unmodified_text = base::UTF16ToUTF8(keys.substr(i, 1));
577 modified_text = base::UTF16ToUTF8(keys.substr(i, 1));
578 }
579 }
580
581 // Create the key events.
582 int number_modifiers = base::size(kModifiers);
583 bool necessary_modifiers[number_modifiers];
584 for (int i = 0; i < number_modifiers; ++i) {
585 necessary_modifiers[i] =
586 all_modifiers & kModifiers[i].mask &&
587 !(sticky_modifiers & kModifiers[i].mask);
588 if (necessary_modifiers[i]) {
589 KeyEventBuilder builder;
590 key_events.push_back(builder.SetType(kRawKeyDownEventType)
591 ->SetKeyCode(kModifiers[i].key_code)
592 ->SetModifiers(sticky_modifiers)
593 ->Build());
594 }
595 }
596
597 KeyEventBuilder builder;
598 builder.SetModifiers(all_modifiers)
599 ->SetText(unmodified_text, modified_text)
600 ->SetKeyCode(key_code)
601 ->Generate(&key_events);
602
603 for (int i = 2; i > -1; --i) {
604 if (necessary_modifiers[i]) {
605 KeyEventBuilder builder;
606 key_events.push_back(builder.SetType(kKeyUpEventType)
607 ->SetKeyCode(kModifiers[i].key_code)
608 ->SetModifiers(sticky_modifiers)
609 ->Build());
610 }
611 }
612 }
613 client_key_events->swap(key_events);
614 *modifiers = sticky_modifiers;
615 return Status(kOk);
616 }
617
ConvertKeyActionToKeyEvent(const base::DictionaryValue * action_object,base::DictionaryValue * input_state,bool is_key_down,std::vector<KeyEvent> * key_events)618 Status ConvertKeyActionToKeyEvent(const base::DictionaryValue* action_object,
619 base::DictionaryValue* input_state,
620 bool is_key_down,
621 std::vector<KeyEvent>* key_events) {
622 std::string raw_key;
623 if (!action_object->GetString("value", &raw_key))
624 return Status(kUnknownError, "missing 'value'");
625
626 int32_t char_index = 0;
627 uint32_t code_point;
628 base::ReadUnicodeCharacter(raw_key.c_str(), raw_key.size(), &char_index,
629 &code_point);
630
631 std::string key;
632 if (code_point >= kNormalisedKeyValueBase &&
633 code_point < kNormalisedKeyValueBase + base::size(kNormalisedKeyValue)) {
634 key = kNormalisedKeyValue[code_point - kNormalisedKeyValueBase];
635 }
636 if (key.size() == 0)
637 key = raw_key;
638
639 base::DictionaryValue* pressed;
640 if (!input_state->GetDictionary("pressed", &pressed))
641 return Status(kUnknownError, "missing 'pressed'");
642 bool already_pressed = pressed->HasKey(key);
643 if (!is_key_down && !already_pressed)
644 return Status(kOk);
645
646 std::string code;
647 if (code_point != 0) {
648 for (auto& mapping : kCodeForKey) {
649 if (mapping.key == code_point || mapping.alternate_key == code_point) {
650 code = mapping.code;
651 break;
652 }
653 }
654 }
655
656 int modifiers;
657 if (!input_state->GetInteger("modifiers", &modifiers))
658 return Status(kUnknownError, "missing 'modifiers'");
659
660 bool is_modifier_key = false;
661 bool is_special_key = false;
662 bool should_skip = false;
663 std::string unmodified_text, modified_text;
664 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
665 std::string error_msg;
666
667 is_modifier_key = IsModifierKey(code_point);
668 if (!is_modifier_key)
669 is_special_key = KeyCodeFromSpecialWebDriverKey(code_point, &key_code);
670
671 if (is_modifier_key) {
672 int updated_modifier;
673 if (code_point == kWebDriverShiftKey) {
674 updated_modifier = kShiftKeyModifierMask;
675 key_code = ui::VKEY_SHIFT;
676 } else if (code_point == kWebDriverRightShiftKey) {
677 updated_modifier = kShiftKeyModifierMask;
678 key_code = ui::VKEY_RSHIFT;
679 } else if (code_point == kWebDriverControlKey) {
680 updated_modifier = kControlKeyModifierMask;
681 key_code = ui::VKEY_CONTROL;
682 } else if (code_point == kWebDriverRightControlKey) {
683 updated_modifier = kControlKeyModifierMask;
684 key_code = ui::VKEY_RCONTROL;
685 } else if (code_point == kWebDriverAltKey) {
686 updated_modifier = kAltKeyModifierMask;
687 key_code = ui::VKEY_MENU;
688 } else if (code_point == kWebDriverRightAltKey) {
689 updated_modifier = kAltKeyModifierMask;
690 key_code = ui::VKEY_RMENU;
691 } else if (code_point == kWebDriverCommandKey) {
692 updated_modifier = kMetaKeyModifierMask;
693 key_code = ui::VKEY_COMMAND;
694 } else if (code_point == kWebDriverRightCommandKey) {
695 updated_modifier = kMetaKeyModifierMask;
696 key_code = ui::VKEY_RWIN;
697 } else {
698 return Status(kUnknownError, "unknown modifier key");
699 }
700
701 if (is_key_down)
702 modifiers |= updated_modifier;
703 else
704 modifiers &= ~updated_modifier;
705
706 input_state->SetInteger("modifiers", modifiers);
707 } else if (is_special_key ||
708 KeyCodeFromShorthandKey(code_point, &key_code, &should_skip)) {
709 if (should_skip)
710 return Status(kOk);
711 if (key_code == ui::VKEY_RETURN) {
712 // For some reason Chrome expects a carriage return for the return key.
713 modified_text = unmodified_text = "\r";
714 } else if (is_special_key && !IsSpecialKeyPrintable(key_code)) {
715 // To prevent char event for special keys like DELETE.
716 modified_text = unmodified_text = std::string();
717 } else {
718 // WebDriver assumes a numpad key should translate to the number,
719 // which requires NumLock to be on with some platforms. This isn't
720 // formally in the spec, but is expected by their tests.
721 int webdriver_modifiers = 0;
722 if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_NUMPAD9)
723 webdriver_modifiers = kNumLockKeyModifierMask;
724 if (!ConvertKeyCodeToText(key_code, webdriver_modifiers, &unmodified_text,
725 &error_msg))
726 return Status(kUnknownError, error_msg);
727 if (!ConvertKeyCodeToText(key_code, modifiers | webdriver_modifiers,
728 &modified_text, &error_msg))
729 return Status(kUnknownError, error_msg);
730 }
731 } else {
732 int necessary_modifiers = 0;
733 ConvertCharToKeyCode(code_point, &key_code, &necessary_modifiers,
734 &error_msg);
735 if (!error_msg.empty())
736 return Status(kUnknownError, error_msg);
737 if (key_code != ui::VKEY_UNKNOWN) {
738 modifiers |= necessary_modifiers;
739 if (!ConvertKeyCodeToText(key_code, 0, &unmodified_text, &error_msg))
740 return Status(kUnknownError, error_msg);
741 if (!ConvertKeyCodeToText(key_code, modifiers, &modified_text,
742 &error_msg))
743 return Status(kUnknownError, error_msg);
744 if (unmodified_text.empty() || modified_text.empty()) {
745 // To prevent char event for special cases like CTRL + x (cut).
746 unmodified_text.clear();
747 modified_text.clear();
748 }
749 } else {
750 // Do a best effort and use the raw key we were given.
751 unmodified_text = raw_key;
752 modified_text = raw_key;
753 }
754 }
755
756 if (is_key_down)
757 pressed->SetBoolean(key, true);
758 else
759 pressed->Remove(key, nullptr);
760
761 KeyEventBuilder builder;
762 builder.SetKeyCode(key_code)
763 ->SetModifiers(modifiers)
764 ->SetLocation(GetKeyLocation(code_point))
765 ->SetDefaultKey(key)
766 ->SetCode(code)
767 ->SetIsFromAction();
768 if (!is_modifier_key)
769 builder.SetText(unmodified_text, modified_text);
770 if (is_key_down) {
771 key_events->push_back(builder.SetType(kKeyDownEventType)->Build());
772 } else {
773 key_events->push_back(builder.SetType(kKeyUpEventType)->Build());
774 }
775
776 return Status(kOk);
777 }
778