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