Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / test / chromedriver / key_converter.cc
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 "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"
13
14 namespace {
15
16 struct ModifierMaskAndKeyCode {
17   int mask;
18   ui::KeyboardCode key_code;
19 };
20
21 const ModifierMaskAndKeyCode kModifiers[] = {
22   { kShiftKeyModifierMask, ui::VKEY_SHIFT },
23   { kControlKeyModifierMask, ui::VKEY_CONTROL },
24   { kAltKeyModifierMask, ui::VKEY_MENU }
25 };
26
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};
93
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;
99
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);
107 }
108
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;
119   }
120 }
121
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;
133 }
134
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;
163   }
164   *client_should_skip = should_skip;
165   return true;
166 }
167
168 }  // namespace
169
170 KeyEvent CreateKeyDownEvent(ui::KeyboardCode key_code, int modifiers) {
171   return KeyEvent(
172       kRawKeyDownEventType, modifiers, std::string(), std::string(), key_code);
173 }
174
175 KeyEvent CreateKeyUpEvent(ui::KeyboardCode key_code, int modifiers) {
176   return KeyEvent(
177       kKeyUpEventType, modifiers, std::string(), std::string(), key_code);
178 }
179
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);
188 }
189
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;
195
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);
201
202   int sticky_modifiers = *modifiers;
203   for (size_t i = 0; i < keys.size(); ++i) {
204     base::char16 key = keys[i];
205
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;
218     }
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");
241       }
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;
247     }
248
249     ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
250     std::string unmodified_text, modified_text;
251     int all_modifiers = sticky_modifiers;
252
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));
266       }
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);
287       }
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();
304         }
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));
309       }
310     }
311
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));
321       }
322     }
323
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));
328     }
329     key_events.push_back(CreateKeyUpEvent(key_code, all_modifiers));
330
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));
335       }
336     }
337   }
338   client_key_events->swap(key_events);
339   *modifiers = sticky_modifiers;
340   return Status(kOk);
341 }