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