Suppression for crbug/241044.
[chromium-blink-merge.git] / chrome / test / chromedriver / key_converter.cc
blobc81a242dc12ca905df63f2287c1d4c9e0a939bd9
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/stringprintf.h"
9 #include "base/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 char16 kWebDriverNullKey = 0xE000U;
95 const char16 kWebDriverShiftKey = 0xE008U;
96 const char16 kWebDriverControlKey = 0xE009U;
97 const char16 kWebDriverAltKey = 0xE00AU;
98 const 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(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(char16 key, ui::KeyboardCode* key_code) {
126 int index = static_cast<int>(key) - 0xE000U;
127 bool is_special_key = index >= 0 &&
128 index < static_cast<int>(arraysize(kSpecialWebDriverKeys));
129 if (is_special_key)
130 *key_code = kSpecialWebDriverKeys[index];
131 return is_special_key;
134 // Gets the key code associated with |key_utf16|, if it is a special shorthand
135 // key. Shorthand keys are common text equivalents for keys, such as the newline
136 // character, which is shorthand for the return key. Returns whether |key| is
137 // a shorthand key. If true, |key_code| will be set and |client_should_skip|
138 // will be set to whether the key should be skipped.
139 bool KeyCodeFromShorthandKey(char16 key_utf16,
140 ui::KeyboardCode* key_code,
141 bool* client_should_skip) {
142 string16 key_str_utf16;
143 key_str_utf16.push_back(key_utf16);
144 std::string key_str_utf8 = UTF16ToUTF8(key_str_utf16);
145 if (key_str_utf8.length() != 1)
146 return false;
147 bool should_skip = false;
148 char key = key_str_utf8[0];
149 if (key == '\n') {
150 *key_code = ui::VKEY_RETURN;
151 } else if (key == '\t') {
152 *key_code = ui::VKEY_TAB;
153 } else if (key == '\b') {
154 *key_code = ui::VKEY_BACK;
155 } else if (key == ' ') {
156 *key_code = ui::VKEY_SPACE;
157 } else if (key == '\r') {
158 *key_code = ui::VKEY_UNKNOWN;
159 should_skip = true;
160 } else {
161 return false;
163 *client_should_skip = should_skip;
164 return true;
167 } // namespace
169 KeyEvent CreateKeyDownEvent(ui::KeyboardCode key_code, int modifiers) {
170 return KeyEvent(
171 kRawKeyDownEventType, modifiers, std::string(), std::string(), key_code);
174 KeyEvent CreateKeyUpEvent(ui::KeyboardCode key_code, int modifiers) {
175 return KeyEvent(
176 kKeyUpEventType, modifiers, std::string(), std::string(), key_code);
179 KeyEvent CreateCharEvent(const std::string& unmodified_text,
180 const std::string& modified_text,
181 int modifiers) {
182 return KeyEvent(kCharEventType,
183 modifiers,
184 modified_text,
185 unmodified_text,
186 ui::VKEY_UNKNOWN);
189 Status ConvertKeysToKeyEvents(const string16& client_keys,
190 bool release_modifiers,
191 int* modifiers,
192 std::list<KeyEvent>* client_key_events) {
193 std::list<KeyEvent> key_events;
195 string16 keys = client_keys;
196 // Add an implicit NULL character to the end of the input to depress all
197 // modifiers.
198 if (release_modifiers)
199 keys.push_back(kWebDriverNullKey);
201 int sticky_modifiers = *modifiers;
202 for (size_t i = 0; i < keys.size(); ++i) {
203 char16 key = keys[i];
205 if (key == kWebDriverNullKey) {
206 // Release all modifier keys and clear |stick_modifiers|.
207 if (sticky_modifiers & kShiftKeyModifierMask)
208 key_events.push_back(CreateKeyUpEvent(ui::VKEY_SHIFT, 0));
209 if (sticky_modifiers & kControlKeyModifierMask)
210 key_events.push_back(CreateKeyUpEvent(ui::VKEY_CONTROL, 0));
211 if (sticky_modifiers & kAltKeyModifierMask)
212 key_events.push_back(CreateKeyUpEvent(ui::VKEY_MENU, 0));
213 if (sticky_modifiers & kMetaKeyModifierMask)
214 key_events.push_back(CreateKeyUpEvent(ui::VKEY_COMMAND, 0));
215 sticky_modifiers = 0;
216 continue;
218 if (IsModifierKey(key)) {
219 // Press or release the modifier, and adjust |sticky_modifiers|.
220 bool modifier_down = false;
221 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
222 if (key == kWebDriverShiftKey) {
223 sticky_modifiers ^= kShiftKeyModifierMask;
224 modifier_down = (sticky_modifiers & kShiftKeyModifierMask) != 0;
225 key_code = ui::VKEY_SHIFT;
226 } else if (key == kWebDriverControlKey) {
227 sticky_modifiers ^= kControlKeyModifierMask;
228 modifier_down = (sticky_modifiers & kControlKeyModifierMask) != 0;
229 key_code = ui::VKEY_CONTROL;
230 } else if (key == kWebDriverAltKey) {
231 sticky_modifiers ^= kAltKeyModifierMask;
232 modifier_down = (sticky_modifiers & kAltKeyModifierMask) != 0;
233 key_code = ui::VKEY_MENU;
234 } else if (key == kWebDriverCommandKey) {
235 sticky_modifiers ^= kMetaKeyModifierMask;
236 modifier_down = (sticky_modifiers & kMetaKeyModifierMask) != 0;
237 key_code = ui::VKEY_COMMAND;
238 } else {
239 return Status(kUnknownError, "unknown modifier key");
241 if (modifier_down)
242 key_events.push_back(CreateKeyDownEvent(key_code, sticky_modifiers));
243 else
244 key_events.push_back(CreateKeyUpEvent(key_code, sticky_modifiers));
245 continue;
248 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
249 std::string unmodified_text, modified_text;
250 int all_modifiers = sticky_modifiers;
252 // Get the key code, text, and modifiers for the given key.
253 bool should_skip = false;
254 bool is_special_key = KeyCodeFromSpecialWebDriverKey(key, &key_code);
255 if (is_special_key ||
256 KeyCodeFromShorthandKey(key, &key_code, &should_skip)) {
257 if (should_skip)
258 continue;
259 if (key_code == ui::VKEY_UNKNOWN) {
260 return Status(kUnknownError, base::StringPrintf(
261 "unknown WebDriver key(%d) at string index (%" PRIuS ")",
262 static_cast<int>(key),
263 i));
265 if (key_code == ui::VKEY_RETURN) {
266 // For some reason Chrome expects a carriage return for the return key.
267 modified_text = unmodified_text = "\r";
268 } else if (is_special_key && !IsSpecialKeyPrintable(key_code)) {
269 // To prevent char event for special keys like DELETE.
270 modified_text = unmodified_text = std::string();
271 } else {
272 // WebDriver assumes a numpad key should translate to the number,
273 // which requires NumLock to be on with some platforms. This isn't
274 // formally in the spec, but is expected by their tests.
275 int webdriver_modifiers = 0;
276 if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_NUMPAD9)
277 webdriver_modifiers = kNumLockKeyModifierMask;
278 unmodified_text = ConvertKeyCodeToText(key_code, webdriver_modifiers);
279 modified_text = ConvertKeyCodeToText(
280 key_code,
281 all_modifiers | webdriver_modifiers);
283 } else {
284 int necessary_modifiers = 0;
285 ConvertCharToKeyCode(key, &key_code, &necessary_modifiers);
286 all_modifiers |= necessary_modifiers;
287 if (key_code != ui::VKEY_UNKNOWN) {
288 unmodified_text = ConvertKeyCodeToText(key_code, 0);
289 modified_text = ConvertKeyCodeToText(key_code, all_modifiers);
290 if (unmodified_text.empty() || modified_text.empty()) {
291 // To prevent char event for special cases like CTRL + x (cut).
292 unmodified_text.clear();
293 modified_text.clear();
295 } else {
296 // Do a best effort and use the raw key we were given.
297 unmodified_text = UTF16ToUTF8(keys.substr(i, 1));
298 modified_text = UTF16ToUTF8(keys.substr(i, 1));
302 // Create the key events.
303 bool necessary_modifiers[3];
304 for (int i = 0; i < 3; ++i) {
305 necessary_modifiers[i] =
306 all_modifiers & kModifiers[i].mask &&
307 !(sticky_modifiers & kModifiers[i].mask);
308 if (necessary_modifiers[i]) {
309 key_events.push_back(
310 CreateKeyDownEvent(kModifiers[i].key_code, sticky_modifiers));
314 key_events.push_back(CreateKeyDownEvent(key_code, all_modifiers));
315 if (unmodified_text.length() || modified_text.length()) {
316 key_events.push_back(
317 CreateCharEvent(unmodified_text, modified_text, all_modifiers));
319 key_events.push_back(CreateKeyUpEvent(key_code, all_modifiers));
321 for (int i = 2; i > -1; --i) {
322 if (necessary_modifiers[i]) {
323 key_events.push_back(
324 CreateKeyUpEvent(kModifiers[i].key_code, sticky_modifiers));
328 client_key_events->swap(key_events);
329 *modifiers = sticky_modifiers;
330 return Status(kOk);