Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/cpp/iedriver/InputManager.cpp
2867 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
17
#include "InputManager.h"
18
19
#include <ctime>
20
#include <codecvt>
21
#include <locale>
22
23
#include "errorcodes.h"
24
#include "json.h"
25
#include "keycodes.h"
26
#include "logging.h"
27
28
#include "ActionSimulators/JavaScriptActionSimulator.h"
29
#include "ActionSimulators/SendInputActionSimulator.h"
30
#include "ActionSimulators/SendMessageActionSimulator.h"
31
#include "DocumentHost.h"
32
#include "Element.h"
33
#include "HookProcessor.h"
34
#include "IElementManager.h"
35
#include "Script.h"
36
#include "StringUtilities.h"
37
#include "WebDriverConstants.h"
38
#include "Generated/atoms.h"
39
40
#define USER_INTERACTION_MUTEX_NAME L"WebDriverUserInteractionMutex"
41
#define WAIT_TIME_IN_MILLISECONDS_PER_INPUT_EVENT 100
42
#define MOVE_ERROR_TEMPLATE "The requested mouse movement to (%d, %d) would be outside the bounds of the current view port (left: %d, right: %d, top: %d, bottom: %d)"
43
#define MOVE_OVERFLOW_ERROR_TEMPLATE "The requested ending %s (%lld) would be outside the legal bounds (less than -2147483648 or greater than 2147483647"
44
45
#define MODIFIER_KEY_SHIFT 1
46
#define MODIFIER_KEY_CTRL 2
47
#define MODIFIER_KEY_ALT 4
48
49
// "Key" values for mouse buttons
50
#define WD_MOUSE_LBUTTON 0xE05EU
51
#define WD_MOUSE_RBUTTON 0xE05FU
52
53
namespace webdriver {
54
55
InputManager::InputManager() {
56
LOG(TRACE) << "Entering InputManager::InputManager";
57
this->use_native_events_ = true;
58
this->use_persistent_hover_ = false;
59
this->require_window_focus_ = true;
60
this->scroll_behavior_ = TOP;
61
this->current_input_state_.is_alt_pressed = false;
62
this->current_input_state_.is_control_pressed = false;
63
this->current_input_state_.is_shift_pressed = false;
64
this->current_input_state_.is_meta_pressed = false;
65
this->current_input_state_.is_left_button_pressed = false;
66
this->current_input_state_.is_right_button_pressed = false;
67
this->current_input_state_.mouse_x = 0;
68
this->current_input_state_.mouse_y = 0;
69
this->current_input_state_.last_click_time = clock();
70
this->current_input_state_.error_info = "";
71
72
this->action_simulator_ = NULL;
73
}
74
75
InputManager::~InputManager(void) {
76
if (this->action_simulator_ != NULL) {
77
delete this->action_simulator_;
78
}
79
}
80
81
void InputManager::Initialize(InputManagerSettings settings) {
82
LOG(TRACE) << "Entering InputManager::Initialize";
83
this->element_map_ = settings.element_repository;
84
this->scroll_behavior_ = settings.scroll_behavior;
85
this->use_native_events_ = settings.use_native_events;
86
this->use_persistent_hover_ = settings.enable_persistent_hover;
87
this->require_window_focus_ = settings.require_window_focus;
88
if (settings.use_native_events) {
89
if (settings.require_window_focus) {
90
this->action_simulator_ = new SendInputActionSimulator();
91
} else {
92
this->action_simulator_ = new SendMessageActionSimulator();
93
}
94
} else {
95
this->action_simulator_ = new JavaScriptActionSimulator();
96
}
97
this->SetupKeyDescriptions();
98
}
99
100
int InputManager::PerformInputSequence(BrowserHandle browser_wrapper,
101
const Json::Value& sequences,
102
std::string* error_info) {
103
LOG(TRACE) << "Entering InputManager::PerformInputSequence";
104
if (!sequences.isArray()) {
105
return EUNHANDLEDERROR;
106
}
107
108
int status_code = WD_SUCCESS;
109
// Use a single mutex, so that all instances synchronize on the same object
110
// for focus purposes.
111
HANDLE mutex_handle = this->AcquireMutex();
112
113
Json::Value ticks(Json::arrayValue);
114
status_code = this->GetTicks(sequences, &ticks);
115
if (status_code != WD_SUCCESS) {
116
this->ReleaseMutex(mutex_handle);
117
return status_code;
118
}
119
120
this->inputs_.clear();
121
this->current_input_state_.last_click_time = 0;
122
InputState current_input_state = this->CloneCurrentInputState();
123
for (size_t i = 0; i < ticks.size(); ++i) {
124
Json::UInt tick_index = static_cast<Json::UInt>(i);
125
Json::Value tick = ticks[tick_index];
126
for (size_t j = 0; j < tick.size(); ++j) {
127
Json::UInt action_index = static_cast<Json::UInt>(j);
128
Json::Value action = tick[action_index];
129
std::string action_subtype = action["type"].asString();
130
if (action_subtype == "pointerMove") {
131
status_code = this->PointerMoveTo(browser_wrapper, action, &current_input_state);
132
} else if (action_subtype == "pointerDown") {
133
status_code = this->PointerDown(browser_wrapper, action, &current_input_state);
134
} else if (action_subtype == "pointerUp") {
135
status_code = this->PointerUp(browser_wrapper, action, &current_input_state);
136
} else if (action_subtype == "keyDown") {
137
status_code = this->KeyDown(browser_wrapper, action, &current_input_state);
138
} else if (action_subtype == "keyUp") {
139
status_code = this->KeyUp(browser_wrapper, action, &current_input_state);
140
} else if (action_subtype == "pause") {
141
status_code = this->Pause(browser_wrapper, action);
142
}
143
144
if (status_code != WD_SUCCESS) {
145
this->ReleaseMutex(mutex_handle);
146
*error_info = current_input_state.error_info;
147
return status_code;
148
}
149
}
150
}
151
152
// If there are inputs in the array, then we've queued up input actions
153
// to be played back. So play them back.
154
if (this->inputs_.size() > 0) {
155
LOG(DEBUG) << "Processing a total of " << this->inputs_.size() << " input events";
156
this->action_simulator_->SimulateActions(browser_wrapper,
157
this->inputs_,
158
&this->current_input_state_);
159
}
160
161
::Sleep(50);
162
163
this->ReleaseMutex(mutex_handle);
164
return status_code;
165
}
166
167
int InputManager::GetTicks(const Json::Value& sequences, Json::Value* ticks) {
168
for (size_t i = 0; i < sequences.size(); ++i) {
169
Json::UInt index = static_cast<Json::UInt>(i);
170
Json::Value device_sequence = sequences[index];
171
if (!device_sequence.isMember("type") && !device_sequence["type"].isString()) {
172
return EINVALIDARGUMENT;
173
}
174
175
std::string device_type = device_sequence["type"].asString();
176
if (device_type != "key" && device_type != "pointer" && device_type != "none") {
177
return EINVALIDARGUMENT;
178
}
179
180
if (!device_sequence.isMember("id") && !device_sequence["id"].isString()) {
181
return EINVALIDARGUMENT;
182
}
183
184
std::string device_id = device_sequence["id"].asString();
185
186
if (!device_sequence.isMember("actions") && !device_sequence["actions"].isArray()) {
187
return EINVALIDARGUMENT;
188
}
189
190
// TODO: Add guards against bad action structure. Assume correct input for now.
191
Json::Value actions = device_sequence["actions"];
192
for (size_t j = 0; j < actions.size(); ++j) {
193
if (ticks->size() <= j) {
194
Json::Value tick(Json::arrayValue);
195
ticks->append(tick);
196
}
197
Json::UInt action_index = static_cast<Json::UInt>(j);
198
Json::Value action = actions[action_index];
199
if (action.isMember("type") &&
200
action["type"].isString() &&
201
action["type"].asString() == "pause") {
202
if (action.isMember("duration") &&
203
(action["duration"].type() != Json::ValueType::intValue ||
204
action["duration"].asInt() < 0)) {
205
return EINVALIDARGUMENT;
206
}
207
if (device_type == "key") {
208
// HACK! Ignore the duration of pause events in keyboard action
209
// sequences. This is deliberately in violation of the W3C spec.
210
// This allows us to better synchronize mixed keyboard and mouse
211
// action sequences.
212
action["duration"] = 10;
213
}
214
}
215
(*ticks)[action_index].append(action);
216
}
217
}
218
return WD_SUCCESS;
219
}
220
221
HANDLE InputManager::AcquireMutex() {
222
HANDLE mutex_handle = ::CreateMutex(NULL, FALSE, USER_INTERACTION_MUTEX_NAME);
223
if (mutex_handle != NULL) {
224
// Wait for up to the timeout (currently 30 seconds) for the mutex to be
225
// released.
226
DWORD mutex_wait_status = ::WaitForSingleObject(mutex_handle, 30000);
227
if (mutex_wait_status == WAIT_ABANDONED) {
228
LOG(WARN) << "Acquired mutex, but received wait abandoned status. This "
229
<< "could mean the process previously owning the mutex was "
230
<< "unexpectedly terminated.";
231
}
232
else if (mutex_wait_status == WAIT_TIMEOUT) {
233
LOG(WARN) << "Could not acquire mutex within the timeout. Multiple "
234
<< "instances may have incorrect synchronization for interactions";
235
}
236
else if (mutex_wait_status == WAIT_OBJECT_0) {
237
LOG(DEBUG) << "Mutex acquired for user interaction.";
238
}
239
}
240
else {
241
LOG(WARN) << "Could not create user interaction mutex. Multiple "
242
<< "instances of IE may behave unpredictably.";
243
}
244
return mutex_handle;
245
}
246
247
void InputManager::ReleaseMutex(HANDLE mutex_handle) {
248
if (mutex_handle != NULL) {
249
::ReleaseMutex(mutex_handle);
250
::CloseHandle(mutex_handle);
251
}
252
}
253
254
InputState InputManager::CloneCurrentInputState(void) {
255
InputState current_input_state;
256
current_input_state.is_alt_pressed = this->current_input_state_.is_alt_pressed;
257
current_input_state.is_control_pressed = this->current_input_state_.is_control_pressed;
258
current_input_state.is_shift_pressed = this->current_input_state_.is_shift_pressed;
259
current_input_state.is_meta_pressed = this->current_input_state_.is_meta_pressed;
260
current_input_state.is_left_button_pressed = this->current_input_state_.is_left_button_pressed;
261
current_input_state.is_right_button_pressed = this->current_input_state_.is_right_button_pressed;
262
current_input_state.mouse_x = this->current_input_state_.mouse_x;
263
current_input_state.mouse_y = this->current_input_state_.mouse_y;
264
current_input_state.error_info = this->current_input_state_.error_info;
265
return current_input_state;
266
}
267
268
void InputManager::Reset(BrowserHandle browser_wrapper) {
269
LOG(TRACE) << "Entering InputManager::Reset";
270
271
LOG(DEBUG) << "Releasing " << this->pressed_keys_.size() << " keys";
272
std::vector<wchar_t> local_pressed_keys(this->pressed_keys_);
273
274
HWND browser_window_handle = browser_wrapper->GetContentWindowHandle();
275
InputState current_input_state = this->CloneCurrentInputState();
276
this->inputs_.clear();
277
std::vector<wchar_t>::const_reverse_iterator it = local_pressed_keys.rbegin();
278
for (; it != local_pressed_keys.rend(); ++it) {
279
std::wstring key_value = L"";
280
key_value.append(1, *it);
281
std::wstring key_description = this->GetKeyDescription(*it);
282
LOG(DEBUG) << "Key: " << LOGWSTRING(key_description);
283
if (*it == WD_MOUSE_LBUTTON) {
284
this->AddMouseInput(browser_window_handle, MOUSEEVENTF_LEFTUP, 0, 0);
285
} else if (*it == WD_MOUSE_RBUTTON) {
286
this->AddMouseInput(browser_window_handle, MOUSEEVENTF_RIGHTUP, 0, 0);
287
} else {
288
this->AddKeyboardInput(browser_window_handle,
289
key_value,
290
true,
291
&current_input_state);
292
}
293
}
294
295
// If there are inputs in the array, then we've queued up input actions
296
// to be played back. So play them back.
297
if (this->inputs_.size() > 0) {
298
LOG(DEBUG) << "Processing a total of " << this->inputs_.size() << " input events";
299
this->action_simulator_->SimulateActions(browser_wrapper,
300
this->inputs_,
301
&this->current_input_state_);
302
}
303
304
::Sleep(50);
305
306
if (this->current_input_state_.mouse_x > 0 ||
307
this->current_input_state_.mouse_y > 0) {
308
LOG(DEBUG) << "Resetting mouse position";
309
this->current_input_state_.mouse_x = 0;
310
this->current_input_state_.mouse_y = 0;
311
}
312
313
if (this->pressed_keys_.size() > 0) {
314
LOG(WARN) << "Pressed keys should have been empty, but had " << this->pressed_keys_.size() << " values.";
315
this->pressed_keys_.clear();
316
}
317
}
318
319
int InputManager::PointerMoveTo(BrowserHandle browser_wrapper,
320
const Json::Value& move_to_action,
321
InputState* input_state) {
322
LOG(TRACE) << "Entering InputManager::PointerMoveTo";
323
int status_code = WD_SUCCESS;
324
bool element_specified = false;
325
std::string origin = "viewport";
326
if (move_to_action.isMember("origin")) {
327
Json::Value origin_value = move_to_action["origin"];
328
if (origin_value.isString()) {
329
origin = origin_value.asString();
330
} else if (origin_value.isObject() && origin_value.isMember(JSON_ELEMENT_PROPERTY_NAME)) {
331
origin = origin_value[JSON_ELEMENT_PROPERTY_NAME].asString();
332
element_specified = true;
333
}
334
}
335
336
long x_offset = 0;
337
if (move_to_action.isMember("x") && move_to_action["x"].isInt()) {
338
x_offset = move_to_action["x"].asInt();
339
}
340
341
long y_offset = 0;
342
if (move_to_action.isMember("y") && move_to_action["y"].isInt()) {
343
y_offset = move_to_action["y"].asInt();
344
}
345
346
bool offset_specified = move_to_action.isMember("x") &&
347
move_to_action.isMember("y") &&
348
(x_offset != 0 || y_offset != 0);
349
350
long duration = 100;
351
if (move_to_action.isMember("duration") && move_to_action["duration"].isInt()) {
352
duration = move_to_action["duration"].asInt();
353
}
354
355
ElementHandle target_element;
356
if (element_specified) {
357
status_code = this->element_map_->GetManagedElement(origin, &target_element);
358
if (status_code != WD_SUCCESS) {
359
return status_code;
360
}
361
}
362
if (this->action_simulator_->UseExtraInfo()) {
363
MouseExtraInfo* extra_info = new MouseExtraInfo();
364
if (element_specified) {
365
extra_info->element = target_element->element();
366
} else {
367
extra_info->element = NULL;
368
}
369
extra_info->offset_specified = offset_specified;
370
extra_info->offset_x = x_offset;
371
extra_info->offset_y = y_offset;
372
INPUT mouse_input;
373
mouse_input.type = INPUT_MOUSE;
374
mouse_input.mi.dwFlags = MOUSEEVENTF_MOVE;
375
mouse_input.mi.dx = 0;
376
mouse_input.mi.dy = 0;
377
mouse_input.mi.dwExtraInfo = reinterpret_cast<ULONG_PTR>(extra_info);
378
mouse_input.mi.mouseData = 0;
379
mouse_input.mi.time = 0;
380
this->inputs_.push_back(mouse_input);
381
} else {
382
long start_x = input_state->mouse_x;
383
long start_y = input_state->mouse_y;
384
385
long end_x = start_x;
386
long end_y = start_y;
387
if (element_specified) {
388
LocationInfo element_location;
389
// Note: The caller of the action sequence is responsible for making
390
// sure the target element is in the view port. In particular, the
391
// high-level click and sendKeys implementations do this in their
392
// command handlers. Further note that offsets specified in this
393
// move action will be relative to the center of the element as
394
// calculated here.
395
status_code = target_element->GetStaticClickLocation(&element_location);
396
// We can't use the status code alone here. Even though the center of the
397
// element may not reachable via the mouse, we might still be able to move
398
// to whatever portion of the element *is* visible in the viewport, especially
399
// if we have an offset specifed, so we have to have an extra check.
400
if (status_code != WD_SUCCESS) {
401
if (status_code == EELEMENTCLICKPOINTNOTSCROLLED && !offset_specified) {
402
// If no offset is specified (meaning "move to the element's center"),
403
// and the "could not scroll center point into view" status code is
404
// returned, bail out here.
405
LOG(WARN) << "No offset was specified, and the center point of the element could not be scrolled into view.";
406
return status_code;
407
} else {
408
LOG(WARN) << "Element::GetStaticClickLocation() returned an error code indicating the element is not reachable.";
409
return status_code;
410
}
411
}
412
413
// An element was specified as the starting point, so we know the end of the mouse
414
// move will be at some offset from the element origin.
415
end_x = element_location.x;
416
end_y = element_location.y;
417
}
418
419
if (origin == "viewport") {
420
end_x = x_offset;
421
end_y = y_offset;
422
} else {
423
if (offset_specified) {
424
// An offset was specified. At this point, the end coordinates should
425
// be set to either (1) the previous mouse position if there was no
426
// element specified, or (2) the origin of the element from which to
427
// calculate the offset. While it may not be strictly spec-compliant,
428
// attempting to set an offset larger than 2,147,483,647 will be
429
// regarded as outside the move bounds.
430
long long temp_x = static_cast<long long>(end_x) + static_cast<long long>(x_offset);
431
if (temp_x > INT_MAX || temp_x < INT_MIN) {
432
input_state->error_info = StringUtilities::Format(MOVE_OVERFLOW_ERROR_TEMPLATE,
433
"x coordinate",
434
temp_x);
435
return EMOVETARGETOUTOFBOUNDS;
436
}
437
long long temp_y = static_cast<long long>(end_y) + static_cast<long long>(y_offset);
438
if (temp_y > INT_MAX || temp_y < INT_MIN) {
439
input_state->error_info = StringUtilities::Format(MOVE_OVERFLOW_ERROR_TEMPLATE,
440
"y coordinate",
441
temp_y);
442
return EMOVETARGETOUTOFBOUNDS;
443
}
444
445
end_x += x_offset;
446
end_y += y_offset;
447
}
448
}
449
450
451
LOG(DEBUG) << "Queueing SendInput structure for mouse move (origin: " << origin
452
<< ", x: " << end_x << ", y: " << end_y << ")";
453
HWND browser_window_handle = browser_wrapper->GetContentWindowHandle();
454
RECT window_rect;
455
::GetWindowRect(browser_window_handle, &window_rect);
456
POINT click_point = { end_x, end_y };
457
::ClientToScreen(browser_window_handle, &click_point);
458
if (click_point.x < window_rect.left ||
459
click_point.x > window_rect.right ||
460
click_point.y < window_rect.top ||
461
click_point.y > window_rect.bottom) {
462
input_state->error_info = StringUtilities::Format(MOVE_ERROR_TEMPLATE,
463
end_x,
464
end_y,
465
window_rect.left,
466
window_rect.right,
467
window_rect.top,
468
window_rect.bottom);
469
LOG(WARN) << input_state->error_info;
470
return EMOVETARGETOUTOFBOUNDS;
471
}
472
if (end_x == input_state->mouse_x && end_y == input_state->mouse_y) {
473
LOG(DEBUG) << "Omitting SendInput structure for mouse move; no movement required (x: "
474
<< end_x << ", y: " << end_y << ")";
475
} else {
476
const int min_duration = 50;
477
int step_count = 10;
478
if (duration <= min_duration) {
479
step_count = 0;
480
}
481
long step_sleep = duration / max(step_count, 1);
482
483
long x_distance = end_x - input_state->mouse_x;
484
long y_distance = end_y - input_state->mouse_y;
485
for (int i = 0; i < step_count; i++) {
486
//To avoid integer division rounding and cumulative floating point errors,
487
//calculate from scratch each time
488
double step_progress = ((double)i) / step_count;
489
int current_x = (int)(input_state->mouse_x + (x_distance * step_progress));
490
int current_y = (int)(input_state->mouse_y + (y_distance * step_progress));
491
this->AddMouseInput(browser_window_handle, MOUSEEVENTF_MOVE, current_x, current_y);
492
if (step_sleep > 0) {
493
this->AddPauseInput(browser_window_handle, step_sleep);
494
}
495
}
496
this->AddMouseInput(browser_window_handle, MOUSEEVENTF_MOVE, end_x, end_y);
497
}
498
input_state->mouse_x = end_x;
499
input_state->mouse_y = end_y;
500
}
501
return status_code;
502
}
503
504
int InputManager::PointerDown(BrowserHandle browser_wrapper,
505
const Json::Value& down_action,
506
InputState* input_state) {
507
LOG(TRACE) << "Entering InputManager::PointerDown";
508
int button = down_action["button"].asInt();
509
HWND browser_window_handle = browser_wrapper->GetContentWindowHandle();
510
LOG(DEBUG) << "Queueing SendInput structure for mouse button down";
511
long button_event_value = MOUSEEVENTF_LEFTDOWN;
512
if (button == WD_CLIENT_RIGHT_MOUSE_BUTTON) {
513
button_event_value = MOUSEEVENTF_RIGHTDOWN;
514
}
515
this->AddMouseInput(browser_window_handle,
516
button_event_value,
517
input_state->mouse_x,
518
input_state->mouse_y);
519
if (button == WD_CLIENT_RIGHT_MOUSE_BUTTON) {
520
input_state->is_right_button_pressed = true;
521
} else {
522
input_state->is_left_button_pressed = true;
523
}
524
return WD_SUCCESS;
525
}
526
527
int InputManager::PointerUp(BrowserHandle browser_wrapper,
528
const Json::Value& up_action,
529
InputState* input_state) {
530
LOG(TRACE) << "Entering InputManager::PointerUp";
531
int button = up_action["button"].asInt();
532
HWND browser_window_handle = browser_wrapper->GetContentWindowHandle();
533
LOG(DEBUG) << "Queueing SendInput structure for mouse button up";
534
long button_event_value = MOUSEEVENTF_LEFTUP;
535
if (button == WD_CLIENT_RIGHT_MOUSE_BUTTON) {
536
button_event_value = MOUSEEVENTF_RIGHTUP;
537
}
538
this->AddMouseInput(browser_window_handle,
539
button_event_value,
540
input_state->mouse_x,
541
input_state->mouse_y);
542
if (button == WD_CLIENT_RIGHT_MOUSE_BUTTON) {
543
input_state->is_right_button_pressed = false;
544
} else {
545
input_state->is_left_button_pressed = false;
546
}
547
return WD_SUCCESS;
548
}
549
550
int InputManager::KeyDown(BrowserHandle browser_wrapper,
551
const Json::Value& down_action,
552
InputState* input_state) {
553
int status_code = WD_SUCCESS;
554
std::string key_value = down_action["value"].asString();
555
std::wstring key = StringUtilities::ToWString(key_value);
556
557
if (!this->IsSingleKey(key)) {
558
return EINVALIDARGUMENT;
559
}
560
561
if (this->action_simulator_->UseExtraInfo()) {
562
LOG(DEBUG) << "Using synthetic events for sending keys";
563
KeyboardExtraInfo* extra_info = new KeyboardExtraInfo();
564
extra_info->character = key;
565
INPUT input_element;
566
input_element.type = INPUT_KEYBOARD;
567
568
input_element.ki.wVk = 0;
569
input_element.ki.wScan = 0;
570
input_element.ki.dwFlags = 0;
571
input_element.ki.dwExtraInfo = reinterpret_cast<ULONG_PTR>(extra_info);
572
input_element.ki.time = 0;
573
this->inputs_.push_back(input_element);
574
} else {
575
HWND window_handle = browser_wrapper->GetContentWindowHandle();
576
this->AddKeyboardInput(window_handle, key, false, input_state);
577
}
578
return status_code;
579
}
580
581
int InputManager::KeyUp(BrowserHandle browser_wrapper,
582
const Json::Value& up_action,
583
InputState* input_state) {
584
int status_code = WD_SUCCESS;
585
std::string key_value = up_action["value"].asString();
586
std::wstring key = StringUtilities::ToWString(key_value);
587
588
if (!this->IsSingleKey(key)) {
589
return EINVALIDARGUMENT;
590
}
591
592
if (!this->action_simulator_->UseExtraInfo()) {
593
HWND window_handle = browser_wrapper->GetContentWindowHandle();
594
this->AddKeyboardInput(window_handle, key, true, input_state);
595
}
596
return status_code;
597
}
598
599
bool InputManager::IsSingleKey(const std::wstring& input) {
600
bool is_single_key = true;
601
//StringUtilities::ComposeUnicodeString(input);
602
std::wstring composed_input = input;
603
StringUtilities::ComposeUnicodeString(&composed_input);
604
if (composed_input.size() > 1) {
605
WORD combining_bitmask = C3_NONSPACING | C3_DIACRITIC | C3_VOWELMARK;
606
std::vector<WORD> char_types(input.size());
607
BOOL get_type_success = ::GetStringTypeW(CT_CTYPE3,
608
input.c_str(),
609
static_cast<int>(input.size()),
610
&char_types[0]);
611
if (get_type_success) {
612
bool found_alpha = false;
613
for (size_t i = 0; i < char_types.size(); ++i) {
614
if (char_types[i] & combining_bitmask) {
615
continue;
616
}
617
618
if (char_types[i] & C3_ALPHA) {
619
if (!found_alpha) {
620
found_alpha = true;
621
} else {
622
is_single_key = false;
623
break;
624
}
625
}
626
}
627
}
628
}
629
if (!is_single_key) {
630
LOG(WARN) << "key value did not pass validation";
631
}
632
return is_single_key;
633
}
634
635
int InputManager::Pause(BrowserHandle browser_wrapper,
636
const Json::Value& pause_action) {
637
int status_code = 0;
638
int duration = 0;
639
if (pause_action.isMember("duration")) {
640
duration = pause_action["duration"].asInt();
641
}
642
if (duration > 0) {
643
this->AddPauseInput(browser_wrapper->GetContentWindowHandle(), duration);
644
}
645
return status_code;
646
}
647
648
void InputManager::AddPauseInput(HWND window_handle, int duration) {
649
// Leverage the INPUT_HARDWARE type.
650
INPUT pause_input;
651
pause_input.type = INPUT_HARDWARE;
652
pause_input.hi.uMsg = duration;
653
this->inputs_.push_back(pause_input);
654
}
655
656
void InputManager::AddMouseInput(HWND window_handle, long input_action, int x, int y) {
657
LOG(TRACE) << "Entering InputManager::AddMouseInput";
658
INPUT mouse_input;
659
mouse_input.type = INPUT_MOUSE;
660
mouse_input.mi.dwFlags = input_action | MOUSEEVENTF_ABSOLUTE;
661
mouse_input.mi.dx = x;
662
mouse_input.mi.dy = y;
663
mouse_input.mi.dwExtraInfo = 0;
664
mouse_input.mi.mouseData = 0;
665
mouse_input.mi.time = 0;
666
if (input_action == MOUSEEVENTF_LEFTDOWN) {
667
this->UpdatePressedKeys(WD_MOUSE_LBUTTON, true);
668
}
669
if (input_action == MOUSEEVENTF_RIGHTDOWN) {
670
this->UpdatePressedKeys(WD_MOUSE_RBUTTON, true);
671
}
672
if (input_action == MOUSEEVENTF_LEFTUP) {
673
this->UpdatePressedKeys(WD_MOUSE_LBUTTON, false);
674
}
675
if (input_action == MOUSEEVENTF_RIGHTUP) {
676
this->UpdatePressedKeys(WD_MOUSE_RBUTTON, false);
677
}
678
this->inputs_.push_back(mouse_input);
679
}
680
681
void InputManager::AddKeyboardInput(HWND window_handle,
682
std::wstring key,
683
bool key_up,
684
InputState* input_state) {
685
LOG(TRACE) << "Entering InputManager::AddKeyboardInput";
686
687
wchar_t character = key[0];
688
std::wstring log_key = key;
689
if (key.size() == 1) {
690
log_key = this->GetKeyDescription(character);
691
}
692
std::string log_event = "key down";
693
if (key_up) {
694
log_event = "key up";
695
}
696
LOG(DEBUG) << "Queueing SendInput structure for " << log_event
697
<< " (key: " << LOGWSTRING(log_key) << ")";
698
699
if (key.size() > 1) {
700
// If the key string passed in is greater than a single character,
701
// we've been sent a Unicode character with surrogate pairs. Do
702
// no further processing, just create the input items for the
703
// individual pieces of the surrogate pair, and let the system
704
// input manager do the rest.
705
std::wstring::const_iterator it = key.begin();
706
for (; it != key.end(); ++it) {
707
KeyInfo surrogate_key_info = { 0, 0, false, false, false, false, L'\0' };
708
surrogate_key_info.scan_code = static_cast<WORD>(*it);
709
surrogate_key_info.key_code = 0;
710
surrogate_key_info.is_extended_key = false;
711
712
this->CreateKeyboardInputItem(surrogate_key_info, KEYEVENTF_UNICODE, key_up);
713
}
714
return;
715
}
716
717
if (character == WD_KEY_NULL) {
718
std::vector<WORD> modifier_key_codes;
719
if (input_state->is_shift_pressed) {
720
if (this->IsKeyPressed(WD_KEY_SHIFT)) {
721
modifier_key_codes.push_back(VK_SHIFT);
722
this->UpdatePressedKeys(WD_KEY_SHIFT, false);
723
}
724
if (this->IsKeyPressed(WD_KEY_R_SHIFT)) {
725
modifier_key_codes.push_back(VK_RSHIFT);
726
this->UpdatePressedKeys(WD_KEY_R_SHIFT, false);
727
}
728
input_state->is_shift_pressed = false;
729
}
730
731
if (input_state->is_control_pressed) {
732
if (this->IsKeyPressed(WD_KEY_CONTROL)) {
733
modifier_key_codes.push_back(VK_CONTROL);
734
this->UpdatePressedKeys(WD_KEY_CONTROL, false);
735
}
736
if (this->IsKeyPressed(WD_KEY_R_CONTROL)) {
737
modifier_key_codes.push_back(VK_RCONTROL);
738
this->UpdatePressedKeys(WD_KEY_R_CONTROL, false);
739
}
740
input_state->is_control_pressed = false;
741
}
742
743
if (input_state->is_alt_pressed) {
744
if (this->IsKeyPressed(WD_KEY_ALT)) {
745
modifier_key_codes.push_back(VK_MENU);
746
this->UpdatePressedKeys(WD_KEY_ALT, false);
747
}
748
if (this->IsKeyPressed(WD_KEY_R_ALT)) {
749
modifier_key_codes.push_back(VK_RMENU);
750
this->UpdatePressedKeys(WD_KEY_R_ALT, false);
751
}
752
input_state->is_alt_pressed = false;
753
}
754
755
if (input_state->is_meta_pressed) {
756
if (this->IsKeyPressed(WD_KEY_META)) {
757
modifier_key_codes.push_back(VK_LWIN);
758
this->UpdatePressedKeys(WD_KEY_META, false);
759
}
760
if (this->IsKeyPressed(WD_KEY_R_META)) {
761
modifier_key_codes.push_back(VK_RWIN);
762
this->UpdatePressedKeys(WD_KEY_R_META, false);
763
}
764
input_state->is_meta_pressed = false;
765
}
766
767
std::vector<WORD>::const_iterator it = modifier_key_codes.begin();
768
for (; it != modifier_key_codes.end(); ++it) {
769
KeyInfo modifier_key_info = { 0, 0, false, false, false, false, character };
770
UINT scan_code = ::MapVirtualKey(*it, MAPVK_VK_TO_VSC);
771
modifier_key_info.key_code = *it;
772
modifier_key_info.scan_code = scan_code;
773
this->CreateKeyboardInputItem(modifier_key_info,
774
KEYEVENTF_SCANCODE,
775
true);
776
}
777
return;
778
}
779
780
if (this->IsModifierKey(character)) {
781
KeyInfo modifier_key_info = { 0, 0, false, false, false, false, character };
782
// If the character represents the Shift key, or represents the
783
// "release all modifiers" key and the Shift key is down, send
784
// the appropriate down or up keystroke for the Shift key.
785
if (character == WD_KEY_SHIFT ||
786
character == WD_KEY_R_SHIFT) {
787
WORD key_code = VK_SHIFT;
788
if (character == WD_KEY_R_SHIFT) {
789
key_code = VK_RSHIFT;
790
}
791
UINT scan_code = ::MapVirtualKey(key_code, MAPVK_VK_TO_VSC);
792
modifier_key_info.key_code = VK_SHIFT;
793
modifier_key_info.scan_code = scan_code;
794
this->CreateKeyboardInputItem(modifier_key_info,
795
KEYEVENTF_SCANCODE,
796
input_state->is_shift_pressed);
797
if (input_state->is_shift_pressed) {
798
input_state->is_shift_pressed = false;
799
} else {
800
input_state->is_shift_pressed = true;
801
}
802
this->UpdatePressedKeys(character, input_state->is_shift_pressed);
803
}
804
805
// If the character represents the Control key, or represents the
806
// "release all modifiers" key and the Control key is down, send
807
// the appropriate down or up keystroke for the Control key.
808
if (character == WD_KEY_CONTROL ||
809
character == WD_KEY_R_CONTROL) {
810
WORD key_code = VK_CONTROL;
811
if (character == WD_KEY_R_CONTROL) {
812
key_code = VK_RCONTROL;
813
modifier_key_info.is_extended_key = true;
814
}
815
UINT scan_code = ::MapVirtualKey(key_code, MAPVK_VK_TO_VSC);
816
modifier_key_info.key_code = VK_CONTROL;
817
modifier_key_info.scan_code = scan_code;
818
this->CreateKeyboardInputItem(modifier_key_info,
819
KEYEVENTF_SCANCODE,
820
input_state->is_control_pressed);
821
if (input_state->is_control_pressed) {
822
input_state->is_control_pressed = false;
823
} else {
824
input_state->is_control_pressed = true;
825
}
826
this->UpdatePressedKeys(character, input_state->is_control_pressed);
827
}
828
829
// If the character represents the Alt key, or represents the
830
// "release all modifiers" key and the Alt key is down, send
831
// the appropriate down or up keystroke for the Alt key.
832
if (character == WD_KEY_ALT ||
833
character == WD_KEY_R_ALT) {
834
WORD key_code = VK_MENU;
835
if (character == WD_KEY_R_ALT) {
836
key_code = VK_RMENU;
837
modifier_key_info.is_extended_key = true;
838
}
839
UINT scan_code = ::MapVirtualKey(key_code, MAPVK_VK_TO_VSC);
840
modifier_key_info.key_code = VK_MENU;
841
modifier_key_info.scan_code = scan_code;
842
this->CreateKeyboardInputItem(modifier_key_info,
843
KEYEVENTF_SCANCODE,
844
input_state->is_alt_pressed);
845
if (input_state->is_alt_pressed) {
846
input_state->is_alt_pressed = false;
847
} else {
848
input_state->is_alt_pressed = true;
849
}
850
this->UpdatePressedKeys(character, input_state->is_alt_pressed);
851
}
852
853
// If the character represents the Meta (Windows) key, or represents
854
// the "release all modifiers" key and the Meta key is down, send
855
// the appropriate down or up keystroke for the Meta key.
856
if (character == WD_KEY_META ||
857
character == WD_KEY_R_META) {
858
//modifier_key_info.key_code = VK_LWIN;
859
//this->CreateKeyboardInputItem(modifier_key_info,
860
// 0,
861
// input_state->is_meta_pressed);
862
//if (input_state->is_meta_pressed) {
863
// input_state->is_meta_pressed = false;
864
//} else {
865
// input_state->is_meta_pressed = true;
866
//}
867
//this->UpdatePressedKeys(WD_KEY_META, input_state->is_meta_pressed);
868
}
869
870
return;
871
}
872
873
if (key_up && !this->IsKeyPressed(character)) {
874
return;
875
}
876
877
this->UpdatePressedKeys(character, !key_up);
878
879
KeyInfo key_info = this->GetKeyInfo(window_handle, character);
880
if (key_info.is_ignored_key) {
881
return;
882
}
883
884
if (!key_info.is_webdriver_key) {
885
if (!key_info.scan_code || (key_info.key_code == 0xFFFFU)) {
886
LOG(WARN) << "No translation for key. Assuming unicode input: " << character;
887
888
key_info.scan_code = static_cast<WORD>(character);
889
key_info.key_code = 0;
890
key_info.is_extended_key = false;
891
892
this->CreateKeyboardInputItem(key_info, KEYEVENTF_UNICODE, key_up);
893
return;
894
}
895
}
896
897
unsigned short modifier_key_info = HIBYTE(key_info.key_code);
898
if (modifier_key_info != 0) {
899
// Requested key is a <modifier keys> + <key>. Thus, don't use the key code.
900
// Instead, send the modifier keystrokes, and use the scan code of the key.
901
bool is_shift_required = (modifier_key_info & MODIFIER_KEY_SHIFT) != 0 &&
902
!input_state->is_shift_pressed;
903
bool is_control_required = (modifier_key_info & MODIFIER_KEY_CTRL) != 0 &&
904
!input_state->is_control_pressed;
905
bool is_alt_required = (modifier_key_info & MODIFIER_KEY_ALT) != 0 &&
906
!input_state->is_alt_pressed;
907
if (!key_up) {
908
if (is_shift_required) {
909
KeyInfo shift_key_info = { VK_SHIFT, 0, false, false, false, false, character };
910
this->CreateKeyboardInputItem(shift_key_info, 0, false);
911
}
912
if (is_control_required) {
913
KeyInfo control_key_info = { VK_CONTROL, 0, false, false, false, false, character };
914
this->CreateKeyboardInputItem(control_key_info, 0, false);
915
}
916
if (is_alt_required) {
917
KeyInfo alt_key_info = { VK_MENU, 0, false, false, false, false, character };
918
this->CreateKeyboardInputItem(alt_key_info, 0, false);
919
}
920
}
921
922
this->CreateKeyboardInputItem(key_info, KEYEVENTF_SCANCODE, key_up);
923
924
if (key_up) {
925
if (is_shift_required) {
926
KeyInfo shift_key_info = { VK_SHIFT, 0, false, false, false, false, character };
927
this->CreateKeyboardInputItem(shift_key_info, 0, true);
928
}
929
if (is_control_required) {
930
KeyInfo control_key_info = { VK_CONTROL, 0, false, false, false, false, character };
931
this->CreateKeyboardInputItem(control_key_info, 0, true);
932
}
933
if (is_alt_required) {
934
KeyInfo alt_key_info = { VK_MENU, 0, false, false, false, false, character };
935
this->CreateKeyboardInputItem(alt_key_info, 0, true);
936
}
937
}
938
} else {
939
this->CreateKeyboardInputItem(key_info, 0, key_up);
940
}
941
}
942
943
bool InputManager::IsKeyPressed(wchar_t character) {
944
return std::find(this->pressed_keys_.begin(),
945
this->pressed_keys_.end(),
946
character) != this->pressed_keys_.end();
947
}
948
949
void InputManager::UpdatePressedKeys(wchar_t character, bool press_key) {
950
std::wstring log_string = this->GetKeyDescription(character);
951
if (press_key) {
952
LOG(TRACE) << "Adding key: " << LOGWSTRING(log_string);
953
this->pressed_keys_.push_back(character);
954
} else {
955
LOG(TRACE) << "Removing key: " << LOGWSTRING(log_string);
956
std::vector<wchar_t>::const_reverse_iterator reverse_it = this->pressed_keys_.rbegin();
957
for (; reverse_it != this->pressed_keys_.rend(); ++reverse_it) {
958
if (*reverse_it == character) {
959
break;
960
}
961
}
962
if (reverse_it != this->pressed_keys_.rend()) {
963
// Must advance the forward iterator to be on the right element
964
// of the vector.
965
std::vector<wchar_t>::const_iterator it = reverse_it.base();
966
this->pressed_keys_.erase(--it);
967
}
968
}
969
}
970
971
void InputManager::CreateKeyboardInputItem(KeyInfo key_info,
972
DWORD initial_flags,
973
bool is_generating_key_up) {
974
INPUT input_element;
975
input_element.type = INPUT_KEYBOARD;
976
977
input_element.ki.wVk = key_info.key_code;
978
input_element.ki.wScan = key_info.scan_code;
979
input_element.ki.dwFlags = initial_flags;
980
input_element.ki.dwExtraInfo = key_info.character;
981
input_element.ki.time = 0;
982
983
if (key_info.is_extended_key) {
984
input_element.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
985
}
986
if (is_generating_key_up) {
987
input_element.ki.dwFlags |= KEYEVENTF_KEYUP;
988
}
989
if (key_info.is_force_scan_code) {
990
input_element.ki.dwFlags |= KEYEVENTF_SCANCODE;
991
}
992
993
this->inputs_.push_back(input_element);
994
}
995
996
bool InputManager::IsModifierKey(wchar_t character) {
997
return character == WD_KEY_SHIFT ||
998
character == WD_KEY_CONTROL ||
999
character == WD_KEY_ALT ||
1000
character == WD_KEY_META ||
1001
character == WD_KEY_R_SHIFT ||
1002
character == WD_KEY_R_CONTROL ||
1003
character == WD_KEY_R_ALT ||
1004
character == WD_KEY_R_META ||
1005
character == WD_KEY_NULL;
1006
}
1007
1008
KeyInfo InputManager::GetKeyInfo(HWND window_handle, wchar_t character) {
1009
KeyInfo key_info;
1010
key_info.is_ignored_key = false;
1011
key_info.is_extended_key = false;
1012
key_info.is_webdriver_key = true;
1013
key_info.is_force_scan_code = false;
1014
key_info.key_code = 0;
1015
key_info.scan_code = 0;
1016
key_info.character = character;
1017
DWORD process_id = 0;
1018
DWORD thread_id = ::GetWindowThreadProcessId(window_handle, &process_id);
1019
HKL layout = ::GetKeyboardLayout(thread_id);
1020
if (character == WD_KEY_CANCEL) { // ^break
1021
key_info.key_code = VK_CANCEL;
1022
key_info.scan_code = VK_CANCEL;
1023
key_info.is_extended_key = true;
1024
}
1025
else if (character == WD_KEY_HELP) { // help
1026
key_info.key_code = VK_HELP;
1027
key_info.scan_code = VK_HELP;
1028
}
1029
else if (character == WD_KEY_BACKSPACE) { // back space
1030
key_info.key_code = VK_BACK;
1031
key_info.scan_code = VK_BACK;
1032
}
1033
else if (character == WD_KEY_TAB) { // tab
1034
key_info.key_code = VK_TAB;
1035
key_info.scan_code = VK_TAB;
1036
}
1037
else if (character == WD_KEY_CLEAR) { // clear
1038
key_info.key_code = VK_CLEAR;
1039
key_info.scan_code = VK_CLEAR;
1040
}
1041
else if (character == WD_KEY_RETURN) { // return
1042
key_info.key_code = VK_RETURN;
1043
key_info.scan_code = VK_RETURN;
1044
}
1045
else if (character == WD_KEY_ENTER) { // enter
1046
key_info.key_code = VK_RETURN;
1047
key_info.scan_code = VK_RETURN;
1048
key_info.is_extended_key = true;
1049
}
1050
else if (character == WD_KEY_PAUSE) { // pause
1051
key_info.key_code = VK_PAUSE;
1052
key_info.scan_code = VK_PAUSE;
1053
key_info.is_extended_key = true;
1054
}
1055
else if (character == WD_KEY_ESCAPE) { // escape
1056
key_info.key_code = VK_ESCAPE;
1057
key_info.scan_code = VK_ESCAPE;
1058
}
1059
else if (character == WD_KEY_SPACE) { // space
1060
key_info.key_code = VK_SPACE;
1061
key_info.scan_code = VK_SPACE;
1062
}
1063
else if (character == WD_KEY_PAGEUP) { // page up
1064
key_info.key_code = VK_PRIOR;
1065
key_info.scan_code = VK_PRIOR;
1066
key_info.is_extended_key = true;
1067
}
1068
else if (character == WD_KEY_PAGEDOWN) { // page down
1069
key_info.key_code = VK_NEXT;
1070
key_info.scan_code = VK_NEXT;
1071
key_info.is_extended_key = true;
1072
}
1073
else if (character == WD_KEY_END) { // end
1074
key_info.key_code = VK_END;
1075
key_info.scan_code = VK_END;
1076
key_info.is_extended_key = true;
1077
}
1078
else if (character == WD_KEY_HOME) { // home
1079
key_info.key_code = VK_HOME;
1080
key_info.scan_code = VK_HOME;
1081
key_info.is_extended_key = true;
1082
}
1083
else if (character == WD_KEY_LEFT) { // left arrow
1084
key_info.key_code = VK_LEFT;
1085
key_info.scan_code = VK_LEFT;
1086
key_info.is_extended_key = true;
1087
}
1088
else if (character == WD_KEY_UP) { // up arrow
1089
key_info.key_code = VK_UP;
1090
key_info.scan_code = VK_UP;
1091
key_info.is_extended_key = true;
1092
}
1093
else if (character == WD_KEY_RIGHT) { // right arrow
1094
key_info.key_code = VK_RIGHT;
1095
key_info.scan_code = VK_RIGHT;
1096
key_info.is_extended_key = true;
1097
}
1098
else if (character == WD_KEY_DOWN) { // down arrow
1099
key_info.key_code = VK_DOWN;
1100
key_info.scan_code = VK_DOWN;
1101
key_info.is_extended_key = true;
1102
}
1103
else if (character == WD_KEY_INSERT) { // insert
1104
key_info.key_code = VK_INSERT;
1105
key_info.scan_code = VK_INSERT;
1106
key_info.is_extended_key = true;
1107
}
1108
else if (character == WD_KEY_DELETE) { // delete
1109
key_info.key_code = VK_DELETE;
1110
key_info.scan_code = VK_DELETE;
1111
key_info.is_extended_key = true;
1112
}
1113
else if (character == WD_KEY_SEMICOLON) { // semicolon
1114
key_info.key_code = VkKeyScanExW(L';', layout);
1115
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1116
}
1117
else if (character == WD_KEY_EQUALS) { // equals
1118
key_info.key_code = VkKeyScanExW(L'=', layout);
1119
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1120
}
1121
else if (character == WD_KEY_NUMPAD0) { // numpad0
1122
key_info.key_code = VK_NUMPAD0;
1123
key_info.scan_code = VK_NUMPAD0;
1124
key_info.is_extended_key = true;
1125
}
1126
else if (character == WD_KEY_NUMPAD1) { // numpad1
1127
key_info.key_code = VK_NUMPAD1;
1128
key_info.scan_code = VK_NUMPAD1;
1129
key_info.is_extended_key = true;
1130
}
1131
else if (character == WD_KEY_NUMPAD2) { // numpad2
1132
key_info.key_code = VK_NUMPAD2;
1133
key_info.scan_code = VK_NUMPAD2;
1134
key_info.is_extended_key = true;
1135
}
1136
else if (character == WD_KEY_NUMPAD3) { // numpad3
1137
key_info.key_code = VK_NUMPAD3;
1138
key_info.scan_code = VK_NUMPAD3;
1139
key_info.is_extended_key = true;
1140
}
1141
else if (character == WD_KEY_NUMPAD4) { // numpad4
1142
key_info.key_code = VK_NUMPAD4;
1143
key_info.scan_code = VK_NUMPAD4;
1144
key_info.is_extended_key = true;
1145
}
1146
else if (character == WD_KEY_NUMPAD5) { // numpad5
1147
key_info.key_code = VK_NUMPAD5;
1148
key_info.scan_code = VK_NUMPAD5;
1149
key_info.is_extended_key = true;
1150
}
1151
else if (character == WD_KEY_NUMPAD6) { // numpad6
1152
key_info.key_code = VK_NUMPAD6;
1153
key_info.scan_code = VK_NUMPAD6;
1154
key_info.is_extended_key = true;
1155
}
1156
else if (character == WD_KEY_NUMPAD7) { // numpad7
1157
key_info.key_code = VK_NUMPAD7;
1158
key_info.scan_code = VK_NUMPAD7;
1159
key_info.is_extended_key = true;
1160
}
1161
else if (character == WD_KEY_NUMPAD8) { // numpad8
1162
key_info.key_code = VK_NUMPAD8;
1163
key_info.scan_code = VK_NUMPAD8;
1164
key_info.is_extended_key = true;
1165
}
1166
else if (character == WD_KEY_NUMPAD9) { // numpad9
1167
key_info.key_code = VK_NUMPAD9;
1168
key_info.scan_code = VK_NUMPAD9;
1169
key_info.is_extended_key = true;
1170
}
1171
else if (character == WD_KEY_MULTIPLY) { // multiply
1172
key_info.key_code = VK_MULTIPLY;
1173
key_info.scan_code = VK_MULTIPLY;
1174
key_info.is_extended_key = true;
1175
}
1176
else if (character == WD_KEY_ADD) { // add
1177
key_info.key_code = VK_ADD;
1178
key_info.scan_code = VK_ADD;
1179
key_info.is_extended_key = true;
1180
}
1181
else if (character == WD_KEY_SEPARATOR) { // separator
1182
key_info.key_code = VkKeyScanExW(L',', layout);
1183
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1184
key_info.is_extended_key = true;
1185
}
1186
else if (character == WD_KEY_SUBTRACT) { // subtract
1187
key_info.key_code = VK_SUBTRACT;
1188
key_info.scan_code = VK_SUBTRACT;
1189
key_info.is_extended_key = true;
1190
}
1191
else if (character == WD_KEY_DECIMAL) { // decimal
1192
key_info.key_code = VK_DECIMAL;
1193
key_info.scan_code = VK_DECIMAL;
1194
key_info.is_extended_key = true;
1195
}
1196
else if (character == WD_KEY_DIVIDE) { // divide
1197
key_info.key_code = VK_DIVIDE;
1198
key_info.scan_code = VK_DIVIDE;
1199
key_info.is_extended_key = true;
1200
}
1201
else if (character == WD_KEY_R_PAGEUP) {
1202
key_info.key_code = VK_NUMPAD9;
1203
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1204
key_info.is_force_scan_code = true;
1205
}
1206
else if (character == WD_KEY_R_PAGEDN) {
1207
key_info.key_code = VK_NUMPAD3;
1208
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1209
key_info.is_force_scan_code = true;
1210
}
1211
else if (character == WD_KEY_R_END) { // end
1212
key_info.key_code = VK_NUMPAD1;
1213
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1214
key_info.is_force_scan_code = true;
1215
}
1216
else if (character == WD_KEY_R_HOME) { // home
1217
key_info.key_code = VK_NUMPAD7;
1218
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1219
key_info.is_force_scan_code = true;
1220
}
1221
else if (character == WD_KEY_R_LEFT) { // left arrow
1222
key_info.key_code = VK_NUMPAD4;
1223
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1224
key_info.is_force_scan_code = true;
1225
}
1226
else if (character == WD_KEY_R_UP) { // up arrow
1227
key_info.key_code = VK_NUMPAD8;
1228
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1229
key_info.is_force_scan_code = true;
1230
}
1231
else if (character == WD_KEY_R_RIGHT) { // right arrow
1232
key_info.key_code = VK_NUMPAD6;
1233
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1234
key_info.is_force_scan_code = true;
1235
}
1236
else if (character == WD_KEY_R_DOWN) { // down arrow
1237
key_info.key_code = VK_NUMPAD2;
1238
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1239
key_info.is_force_scan_code = true;
1240
}
1241
else if (character == WD_KEY_R_INSERT) { // insert
1242
key_info.key_code = VK_NUMPAD0;
1243
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1244
key_info.is_force_scan_code = true;
1245
}
1246
else if (character == WD_KEY_R_DELETE) { // delete
1247
key_info.key_code = VK_DECIMAL;
1248
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1249
key_info.is_force_scan_code = true;
1250
}
1251
else if (character == WD_KEY_F1) { // F1
1252
key_info.key_code = VK_F1;
1253
key_info.scan_code = VK_F1;
1254
}
1255
else if (character == WD_KEY_F2) { // F2
1256
key_info.key_code = VK_F2;
1257
key_info.scan_code = VK_F2;
1258
}
1259
else if (character == WD_KEY_F3) { // F3
1260
key_info.key_code = VK_F3;
1261
key_info.scan_code = VK_F3;
1262
}
1263
else if (character == WD_KEY_F4) { // F4
1264
key_info.key_code = VK_F4;
1265
key_info.scan_code = VK_F4;
1266
}
1267
else if (character == WD_KEY_F5) { // F5
1268
key_info.key_code = VK_F5;
1269
key_info.scan_code = VK_F5;
1270
}
1271
else if (character == WD_KEY_F6) { // F6
1272
key_info.key_code = VK_F6;
1273
key_info.scan_code = VK_F6;
1274
}
1275
else if (character == WD_KEY_F7) { // F7
1276
key_info.key_code = VK_F7;
1277
key_info.scan_code = VK_F7;
1278
}
1279
else if (character == WD_KEY_F8) { // F8
1280
key_info.key_code = VK_F8;
1281
key_info.scan_code = VK_F8;
1282
}
1283
else if (character == WD_KEY_F9) { // F9
1284
key_info.key_code = VK_F9;
1285
key_info.scan_code = VK_F9;
1286
}
1287
else if (character == WD_KEY_F10) { // F10
1288
key_info.key_code = VK_F10;
1289
key_info.scan_code = VK_F10;
1290
}
1291
else if (character == WD_KEY_F11) { // F11
1292
key_info.key_code = VK_F11;
1293
key_info.scan_code = VK_F11;
1294
}
1295
else if (character == WD_KEY_F12) { // F12
1296
key_info.key_code = VK_F12;
1297
key_info.scan_code = VK_F12;
1298
}
1299
else if (character == L'\n') { // line feed
1300
key_info.key_code = VK_RETURN;
1301
key_info.scan_code = VK_RETURN;
1302
}
1303
else if (character == L'\r') { // carriage return
1304
key_info.is_ignored_key = true; // skip it
1305
} else {
1306
key_info.key_code = VkKeyScanExW(character, layout);
1307
key_info.scan_code = MapVirtualKeyExW(LOBYTE(key_info.key_code), 0, layout);
1308
key_info.is_webdriver_key = false;
1309
}
1310
return key_info;
1311
}
1312
1313
std::wstring InputManager::GetKeyDescription(const wchar_t character) {
1314
std::wstring description = L"";
1315
description.append(1, character);
1316
std::map<wchar_t, std::wstring>::const_iterator it = this->key_descriptions_.find(character);
1317
if (it != this->key_descriptions_.end()) {
1318
description = it->second;
1319
}
1320
return description;
1321
}
1322
1323
void InputManager::SetupKeyDescriptions() {
1324
this->key_descriptions_[WD_KEY_NULL] = L"Unidentified";
1325
this->key_descriptions_[WD_KEY_CANCEL] = L"Cancel";
1326
this->key_descriptions_[WD_KEY_HELP] = L"Help";
1327
this->key_descriptions_[WD_KEY_BACKSPACE] = L"Backspace";
1328
this->key_descriptions_[WD_KEY_TAB] = L"Tab";
1329
this->key_descriptions_[WD_KEY_CLEAR] = L"Clear";
1330
this->key_descriptions_[WD_KEY_RETURN] = L"Return";
1331
this->key_descriptions_[WD_KEY_ENTER] = L"Enter";
1332
this->key_descriptions_[WD_KEY_SHIFT] = L"Shift";
1333
this->key_descriptions_[WD_KEY_CONTROL] = L"Control";
1334
this->key_descriptions_[WD_KEY_ALT] = L"Alt";
1335
this->key_descriptions_[WD_KEY_PAUSE] = L"Pause";
1336
this->key_descriptions_[WD_KEY_ESCAPE] = L"Escape";
1337
this->key_descriptions_[WD_KEY_SPACE] = L"Space";
1338
this->key_descriptions_[WD_KEY_PAGEUP] = L"PageUp";
1339
this->key_descriptions_[WD_KEY_PAGEDOWN] = L"PageDown";
1340
this->key_descriptions_[WD_KEY_END] = L"End";
1341
this->key_descriptions_[WD_KEY_HOME] = L"Home";
1342
this->key_descriptions_[WD_KEY_LEFT] = L"ArrowLeft";
1343
this->key_descriptions_[WD_KEY_UP] = L"ArrowUp";
1344
this->key_descriptions_[WD_KEY_RIGHT] = L"ArrowRight";
1345
this->key_descriptions_[WD_KEY_DOWN] = L"ArrowDown";
1346
this->key_descriptions_[WD_KEY_INSERT] = L"Insert";
1347
this->key_descriptions_[WD_KEY_DELETE] = L"Delete";
1348
this->key_descriptions_[WD_KEY_SEMICOLON] = L";";
1349
this->key_descriptions_[WD_KEY_EQUALS] = L"=";
1350
this->key_descriptions_[WD_KEY_NUMPAD0] = L"0";
1351
this->key_descriptions_[WD_KEY_NUMPAD1] = L"1";
1352
this->key_descriptions_[WD_KEY_NUMPAD2] = L"2";
1353
this->key_descriptions_[WD_KEY_NUMPAD3] = L"3";
1354
this->key_descriptions_[WD_KEY_NUMPAD4] = L"4";
1355
this->key_descriptions_[WD_KEY_NUMPAD5] = L"5";
1356
this->key_descriptions_[WD_KEY_NUMPAD6] = L"6";
1357
this->key_descriptions_[WD_KEY_NUMPAD7] = L"7";
1358
this->key_descriptions_[WD_KEY_NUMPAD8] = L"8";
1359
this->key_descriptions_[WD_KEY_NUMPAD9] = L"9";
1360
this->key_descriptions_[WD_KEY_MULTIPLY] = L"*";
1361
this->key_descriptions_[WD_KEY_ADD] = L"+";
1362
this->key_descriptions_[WD_KEY_SEPARATOR] = L",";
1363
this->key_descriptions_[WD_KEY_SUBTRACT] = L"-";
1364
this->key_descriptions_[WD_KEY_DECIMAL] = L".";
1365
this->key_descriptions_[WD_KEY_DIVIDE] = L"/";
1366
this->key_descriptions_[WD_KEY_F1] = L"F1";
1367
this->key_descriptions_[WD_KEY_F2] = L"F2";
1368
this->key_descriptions_[WD_KEY_F3] = L"F3";
1369
this->key_descriptions_[WD_KEY_F4] = L"F4";
1370
this->key_descriptions_[WD_KEY_F5] = L"F5";
1371
this->key_descriptions_[WD_KEY_F6] = L"F6";
1372
this->key_descriptions_[WD_KEY_F7] = L"F7";
1373
this->key_descriptions_[WD_KEY_F8] = L"F8";
1374
this->key_descriptions_[WD_KEY_F9] = L"F9";
1375
this->key_descriptions_[WD_KEY_F10] = L"F10";
1376
this->key_descriptions_[WD_KEY_F11] = L"F11";
1377
this->key_descriptions_[WD_KEY_F12] = L"F12";
1378
this->key_descriptions_[WD_KEY_META] = L"Meta";
1379
this->key_descriptions_[WD_KEY_ZEN] = L"ZenkakuHankaku";
1380
this->key_descriptions_[WD_KEY_R_SHIFT] = L"Shift";
1381
this->key_descriptions_[WD_KEY_R_CONTROL] = L"Control";
1382
this->key_descriptions_[WD_KEY_R_ALT] = L"Alt";
1383
this->key_descriptions_[WD_KEY_R_META] = L"Meta";
1384
this->key_descriptions_[WD_KEY_R_PAGEUP] = L"PageUp";
1385
this->key_descriptions_[WD_KEY_R_PAGEDN] = L"PageDown";
1386
this->key_descriptions_[WD_KEY_R_END] = L"End";
1387
this->key_descriptions_[WD_KEY_R_HOME] = L"Home";
1388
this->key_descriptions_[WD_KEY_R_LEFT] = L"ArrowLeft";
1389
this->key_descriptions_[WD_KEY_R_UP] = L"ArrowUp";
1390
this->key_descriptions_[WD_KEY_R_RIGHT] = L"ArrowRight";
1391
this->key_descriptions_[WD_KEY_R_DOWN] = L"ArrowDown";
1392
this->key_descriptions_[WD_KEY_R_INSERT] = L"Insert";
1393
this->key_descriptions_[WD_KEY_R_DELETE] = L"Delete";
1394
this->key_descriptions_[WD_MOUSE_LBUTTON] = L"Left Mouse Button";
1395
this->key_descriptions_[WD_MOUSE_RBUTTON] = L"Right Mouse Button";
1396
}
1397
1398
} // namespace webdriver
1399
1400