Make hitting "Enter" submit the add/change profile dialog.
[chromium-blink-merge.git] / chrome / test / webdriver / webdriver_key_converter.cc
blob25c8957f60c826c0b9c3f62158a93777b97af9dc
1 // Copyright (c) 2012 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/webdriver/webdriver_key_converter.h"
7 #include "base/format_macros.h"
8 #include "base/logging.h"
9 #include "base/stringprintf.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/common/automation_constants.h"
12 #include "chrome/test/automation/automation_json_requests.h"
13 #include "chrome/test/webdriver/keycode_text_conversion.h"
14 #include "chrome/test/webdriver/webdriver_logging.h"
16 namespace {
18 struct ModifierMaskAndKeyCode {
19 int mask;
20 ui::KeyboardCode key_code;
23 const ModifierMaskAndKeyCode kModifiers[] = {
24 { automation::kShiftKeyMask, ui::VKEY_SHIFT },
25 { automation::kControlKeyMask, ui::VKEY_CONTROL },
26 { automation::kAltKeyMask, ui::VKEY_MENU }
29 // TODO(kkania): Use this in KeyMap.
30 // Ordered list of all the key codes corresponding to special WebDriver keys.
31 // These WebDriver keys are defined in the Unicode Private Use Area.
32 // http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
33 const ui::KeyboardCode kSpecialWebDriverKeys[] = {
34 ui::VKEY_UNKNOWN,
35 ui::VKEY_UNKNOWN,
36 ui::VKEY_HELP,
37 ui::VKEY_BACK,
38 ui::VKEY_TAB,
39 ui::VKEY_CLEAR,
40 ui::VKEY_RETURN,
41 ui::VKEY_RETURN,
42 ui::VKEY_SHIFT,
43 ui::VKEY_CONTROL,
44 ui::VKEY_MENU,
45 ui::VKEY_PAUSE,
46 ui::VKEY_ESCAPE,
47 ui::VKEY_SPACE,
48 ui::VKEY_PRIOR, // page up
49 ui::VKEY_NEXT, // page down
50 ui::VKEY_END,
51 ui::VKEY_HOME,
52 ui::VKEY_LEFT,
53 ui::VKEY_UP,
54 ui::VKEY_RIGHT,
55 ui::VKEY_DOWN,
56 ui::VKEY_INSERT,
57 ui::VKEY_DELETE,
58 ui::VKEY_OEM_1, // semicolon
59 ui::VKEY_OEM_PLUS, // equals
60 ui::VKEY_NUMPAD0,
61 ui::VKEY_NUMPAD1,
62 ui::VKEY_NUMPAD2,
63 ui::VKEY_NUMPAD3,
64 ui::VKEY_NUMPAD4,
65 ui::VKEY_NUMPAD5,
66 ui::VKEY_NUMPAD6,
67 ui::VKEY_NUMPAD7,
68 ui::VKEY_NUMPAD8,
69 ui::VKEY_NUMPAD9,
70 ui::VKEY_MULTIPLY,
71 ui::VKEY_ADD,
72 ui::VKEY_OEM_COMMA,
73 ui::VKEY_SUBTRACT,
74 ui::VKEY_DECIMAL,
75 ui::VKEY_DIVIDE,
76 ui::VKEY_UNKNOWN,
77 ui::VKEY_UNKNOWN,
78 ui::VKEY_UNKNOWN,
79 ui::VKEY_UNKNOWN,
80 ui::VKEY_UNKNOWN,
81 ui::VKEY_UNKNOWN,
82 ui::VKEY_UNKNOWN,
83 ui::VKEY_F1,
84 ui::VKEY_F2,
85 ui::VKEY_F3,
86 ui::VKEY_F4,
87 ui::VKEY_F5,
88 ui::VKEY_F6,
89 ui::VKEY_F7,
90 ui::VKEY_F8,
91 ui::VKEY_F9,
92 ui::VKEY_F10,
93 ui::VKEY_F11,
94 ui::VKEY_F12};
96 const char16 kWebDriverNullKey = 0xE000U;
97 const char16 kWebDriverShiftKey = 0xE008U;
98 const char16 kWebDriverControlKey = 0xE009U;
99 const char16 kWebDriverAltKey = 0xE00AU;
100 const char16 kWebDriverCommandKey = 0xE03DU;
102 // Returns whether the given key is a WebDriver key modifier.
103 bool IsModifierKey(char16 key) {
104 switch (key) {
105 case kWebDriverShiftKey:
106 case kWebDriverControlKey:
107 case kWebDriverAltKey:
108 case kWebDriverCommandKey:
109 return true;
110 default:
111 return false;
115 // Gets the key code associated with |key|, if it is a special WebDriver key.
116 // Returns whether |key| is a special WebDriver key. If true, |key_code| will
117 // be set.
118 bool KeyCodeFromSpecialWebDriverKey(char16 key, ui::KeyboardCode* key_code) {
119 int index = static_cast<int>(key) - 0xE000U;
120 bool is_special_key = index >= 0 &&
121 index < static_cast<int>(arraysize(kSpecialWebDriverKeys));
122 if (is_special_key)
123 *key_code = kSpecialWebDriverKeys[index];
124 return is_special_key;
127 // Gets the key code associated with |key|, if it is a special shorthand key.
128 // Shorthand keys are common text equivalents for keys, such as the newline
129 // character, which is shorthand for the return key. Returns whether |key| is
130 // a shorthand key. If true, |key_code| will be set and |client_should_skip|
131 // will be set to whether the key should be skipped.
132 bool KeyCodeFromShorthandKey(char16 key_utf16,
133 ui::KeyboardCode* key_code,
134 bool* client_should_skip) {
135 string16 key_str_utf16;
136 key_str_utf16.push_back(key_utf16);
137 std::string key_str_utf8 = UTF16ToUTF8(key_str_utf16);
138 if (key_str_utf8.length() != 1)
139 return false;
140 bool should_skip = false;
141 char key = key_str_utf8[0];
142 if (key == '\n') {
143 *key_code = ui::VKEY_RETURN;
144 } else if (key == '\t') {
145 *key_code = ui::VKEY_TAB;
146 } else if (key == '\b') {
147 *key_code = ui::VKEY_BACK;
148 } else if (key == ' ') {
149 *key_code = ui::VKEY_SPACE;
150 } else if (key == '\r') {
151 *key_code = ui::VKEY_UNKNOWN;
152 should_skip = true;
153 } else {
154 return false;
156 *client_should_skip = should_skip;
157 return true;
160 } // namespace
162 namespace webdriver {
164 WebKeyEvent CreateKeyDownEvent(ui::KeyboardCode key_code, int modifiers) {
165 return WebKeyEvent(automation::kRawKeyDownType, key_code, "", "", modifiers);
168 WebKeyEvent CreateKeyUpEvent(ui::KeyboardCode key_code, int modifiers) {
169 return WebKeyEvent(automation::kKeyUpType, key_code, "", "", modifiers);
172 WebKeyEvent CreateCharEvent(const std::string& unmodified_text,
173 const std::string& modified_text,
174 int modifiers) {
175 return WebKeyEvent(automation::kCharType,
176 ui::VKEY_UNKNOWN,
177 unmodified_text,
178 modified_text,
179 modifiers);
182 bool ConvertKeysToWebKeyEvents(const string16& client_keys,
183 const Logger& logger,
184 bool release_modifiers,
185 int* modifiers,
186 std::vector<WebKeyEvent>* client_key_events,
187 std::string* error_msg) {
188 std::vector<WebKeyEvent> key_events;
190 string16 keys = client_keys;
191 // Add an implicit NULL character to the end of the input to depress all
192 // modifiers.
193 if (release_modifiers)
194 keys.push_back(kWebDriverNullKey);
196 int sticky_modifiers = *modifiers;
197 for (size_t i = 0; i < keys.size(); ++i) {
198 char16 key = keys[i];
200 if (key == kWebDriverNullKey) {
201 // Release all modifier keys and clear |stick_modifiers|.
202 if (sticky_modifiers & automation::kShiftKeyMask)
203 key_events.push_back(CreateKeyUpEvent(ui::VKEY_SHIFT, 0));
204 if (sticky_modifiers & automation::kControlKeyMask)
205 key_events.push_back(CreateKeyUpEvent(ui::VKEY_CONTROL, 0));
206 if (sticky_modifiers & automation::kAltKeyMask)
207 key_events.push_back(CreateKeyUpEvent(ui::VKEY_MENU, 0));
208 if (sticky_modifiers & automation::kMetaKeyMask)
209 key_events.push_back(CreateKeyUpEvent(ui::VKEY_COMMAND, 0));
210 sticky_modifiers = 0;
211 continue;
213 if (IsModifierKey(key)) {
214 // Press or release the modifier, and adjust |sticky_modifiers|.
215 bool modifier_down = false;
216 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
217 if (key == kWebDriverShiftKey) {
218 sticky_modifiers ^= automation::kShiftKeyMask;
219 modifier_down = sticky_modifiers & automation::kShiftKeyMask;
220 key_code = ui::VKEY_SHIFT;
221 } else if (key == kWebDriverControlKey) {
222 sticky_modifiers ^= automation::kControlKeyMask;
223 modifier_down = sticky_modifiers & automation::kControlKeyMask;
224 key_code = ui::VKEY_CONTROL;
225 } else if (key == kWebDriverAltKey) {
226 sticky_modifiers ^= automation::kAltKeyMask;
227 modifier_down = sticky_modifiers & automation::kAltKeyMask;
228 key_code = ui::VKEY_MENU;
229 } else if (key == kWebDriverCommandKey) {
230 sticky_modifiers ^= automation::kMetaKeyMask;
231 modifier_down = sticky_modifiers & automation::kMetaKeyMask;
232 key_code = ui::VKEY_COMMAND;
233 } else {
234 NOTREACHED();
236 if (modifier_down)
237 key_events.push_back(CreateKeyDownEvent(key_code, sticky_modifiers));
238 else
239 key_events.push_back(CreateKeyUpEvent(key_code, sticky_modifiers));
240 continue;
243 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
244 std::string unmodified_text, modified_text;
245 int all_modifiers = sticky_modifiers;
247 // Get the key code, text, and modifiers for the given key.
248 bool should_skip = false;
249 if (KeyCodeFromSpecialWebDriverKey(key, &key_code) ||
250 KeyCodeFromShorthandKey(key, &key_code, &should_skip)) {
251 if (should_skip)
252 continue;
253 if (key_code == ui::VKEY_UNKNOWN) {
254 *error_msg = StringPrintf(
255 "Unknown WebDriver key(%d) at string index (%" PRIuS ")",
256 static_cast<int>(key),
258 return false;
260 if (key_code == ui::VKEY_RETURN) {
261 // For some reason Chrome expects a carriage return for the return key.
262 modified_text = unmodified_text = "\r";
263 } else {
264 // WebDriver assumes a numpad key should translate to the number,
265 // which requires NumLock to be on with some platforms. This isn't
266 // formally in the spec, but is expected by their tests.
267 int webdriver_modifiers = 0;
268 if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_NUMPAD9)
269 webdriver_modifiers = automation::kNumLockKeyMask;
270 unmodified_text = ConvertKeyCodeToText(key_code, webdriver_modifiers);
271 modified_text = ConvertKeyCodeToText(
272 key_code,
273 all_modifiers | webdriver_modifiers);
275 } else {
276 int necessary_modifiers = 0;
277 ConvertCharToKeyCode(key, &key_code, &necessary_modifiers);
278 all_modifiers |= necessary_modifiers;
279 if (key_code != ui::VKEY_UNKNOWN) {
280 unmodified_text = ConvertKeyCodeToText(key_code, 0);
281 modified_text = ConvertKeyCodeToText(key_code, all_modifiers);
283 if (unmodified_text.empty() || modified_text.empty()) {
284 // Do a best effort and use the raw key we were given.
285 logger.Log(
286 kWarningLogLevel,
287 base::StringPrintf("No translation for key code. Code point: %d",
288 static_cast<int>(key)));
289 if (unmodified_text.empty())
290 unmodified_text = UTF16ToUTF8(keys.substr(i, 1));
291 if (modified_text.empty())
292 modified_text = UTF16ToUTF8(keys.substr(i, 1));
296 // Create the key events.
297 bool necessary_modifiers[3];
298 for (int i = 0; i < 3; ++i) {
299 necessary_modifiers[i] =
300 all_modifiers & kModifiers[i].mask &&
301 !(sticky_modifiers & kModifiers[i].mask);
302 if (necessary_modifiers[i]) {
303 key_events.push_back(
304 CreateKeyDownEvent(kModifiers[i].key_code, sticky_modifiers));
308 key_events.push_back(CreateKeyDownEvent(key_code, all_modifiers));
309 if (unmodified_text.length() || modified_text.length()) {
310 key_events.push_back(
311 CreateCharEvent(unmodified_text, modified_text, all_modifiers));
313 key_events.push_back(CreateKeyUpEvent(key_code, all_modifiers));
315 for (int i = 2; i > -1; --i) {
316 if (necessary_modifiers[i]) {
317 key_events.push_back(
318 CreateKeyUpEvent(kModifiers[i].key_code, sticky_modifiers));
322 client_key_events->swap(key_events);
323 *modifiers = sticky_modifiers;
324 return true;
327 } // namespace webdriver