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/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"
16 struct ModifierMaskAndKeyCode
{
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
[] = {
46 ui::VKEY_PRIOR
, // page up
47 ui::VKEY_NEXT
, // page down
56 ui::VKEY_OEM_1
, // semicolon
57 ui::VKEY_OEM_PLUS
, // equals
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
;
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(base::char16 key
) {
112 case kWebDriverShiftKey
:
113 case kWebDriverControlKey
:
114 case kWebDriverAltKey
:
115 case kWebDriverCommandKey
:
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
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
));
131 *key_code
= kSpecialWebDriverKeys
[index
];
132 return is_special_key
;
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)
148 bool should_skip
= false;
149 char key
= key_str_utf8
[0];
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
;
164 *client_should_skip
= should_skip
;
170 KeyEvent
CreateKeyDownEvent(ui::KeyboardCode key_code
, int modifiers
) {
172 kRawKeyDownEventType
, modifiers
, std::string(), std::string(), key_code
);
175 KeyEvent
CreateKeyUpEvent(ui::KeyboardCode key_code
, int modifiers
) {
177 kKeyUpEventType
, modifiers
, std::string(), std::string(), key_code
);
180 KeyEvent
CreateCharEvent(const std::string
& unmodified_text
,
181 const std::string
& modified_text
,
183 return KeyEvent(kCharEventType
,
190 Status
ConvertKeysToKeyEvents(const base::string16
& client_keys
,
191 bool release_modifiers
,
193 std::list
<KeyEvent
>* client_key_events
) {
194 std::list
<KeyEvent
> key_events
;
196 base::string16 keys
= client_keys
;
197 // Add an implicit NULL character to the end of the input to depress all
199 if (release_modifiers
)
200 keys
.push_back(kWebDriverNullKey
);
202 int sticky_modifiers
= *modifiers
;
203 for (size_t i
= 0; i
< keys
.size(); ++i
) {
204 base::char16 key
= keys
[i
];
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;
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
;
240 return Status(kUnknownError
, "unknown modifier key");
243 key_events
.push_back(CreateKeyDownEvent(key_code
, sticky_modifiers
));
245 key_events
.push_back(CreateKeyUpEvent(key_code
, sticky_modifiers
));
249 ui::KeyboardCode key_code
= ui::VKEY_UNKNOWN
;
250 std::string unmodified_text
, modified_text
;
251 int all_modifiers
= sticky_modifiers
;
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
)) {
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
),
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();
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
,
286 return Status(kUnknownError
, error_msg
);
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();
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));
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
));
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
));
329 key_events
.push_back(CreateKeyUpEvent(key_code
, all_modifiers
));
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
));
338 client_key_events
->swap(key_events
);
339 *modifiers
= sticky_modifiers
;