Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/cpp/iedriver/IECommandExecutor.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 "IECommandExecutor.h"
18
19
#include <algorithm>
20
#include <ctime>
21
#include <vector>
22
#include <mutex>
23
#include <unordered_set>
24
25
#include <iepmapi.h>
26
27
#include "command_types.h"
28
#include "errorcodes.h"
29
#include "logging.h"
30
#include "response.h"
31
32
#include "Alert.h"
33
#include "Browser.h"
34
#include "BrowserFactory.h"
35
#include "CommandExecutor.h"
36
#include "CommandHandlerRepository.h"
37
#include "CookieManager.h"
38
#include "Element.h"
39
#include "ElementFinder.h"
40
#include "ElementRepository.h"
41
#include "IECommandHandler.h"
42
#include "InputManager.h"
43
#include "HtmlDialog.h"
44
#include "ProxyManager.h"
45
#include "StringUtilities.h"
46
#include "Script.h"
47
#include "WebDriverConstants.h"
48
#include "WindowUtilities.h"
49
50
#define MAX_HTML_DIALOG_RETRIES 5
51
#define WAIT_TIME_IN_MILLISECONDS 50
52
#define DEFAULT_SCRIPT_TIMEOUT_IN_MILLISECONDS 30000
53
#define DEFAULT_PAGE_LOAD_TIMEOUT_IN_MILLISECONDS 300000
54
#define DEFAULT_FILE_UPLOAD_DIALOG_TIMEOUT_IN_MILLISECONDS 3000
55
#define DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS 10000
56
57
namespace webdriver {
58
59
struct WaitThreadContext {
60
HWND window_handle;
61
bool is_deferred_command;
62
LPSTR deferred_response;
63
};
64
65
struct DelayPostMessageThreadContext {
66
HWND window_handle;
67
DWORD delay;
68
UINT msg;
69
};
70
71
LRESULT IECommandExecutor::OnCreate(UINT uMsg,
72
WPARAM wParam,
73
LPARAM lParam,
74
BOOL& bHandled) {
75
LOG(TRACE) << "Entering IECommandExecutor::OnCreate";
76
77
CREATESTRUCT* create = reinterpret_cast<CREATESTRUCT*>(lParam);
78
IECommandExecutorThreadContext* context = reinterpret_cast<IECommandExecutorThreadContext*>(create->lpCreateParams);
79
this->port_ = context->port;
80
81
// NOTE: COM should be initialized on this thread, so we
82
// could use CoCreateGuid() and StringFromGUID2() instead.
83
UUID guid;
84
RPC_WSTR guid_string = NULL;
85
RPC_STATUS status = ::UuidCreate(&guid);
86
status = ::UuidToString(&guid, &guid_string);
87
88
// RPC_WSTR is currently typedef'd in RpcDce.h (pulled in by rpc.h)
89
// as unsigned short*. It needs to be typedef'd as wchar_t*
90
wchar_t* cast_guid_string = reinterpret_cast<wchar_t*>(guid_string);
91
this->SetWindowText(cast_guid_string);
92
93
std::string session_id = StringUtilities::ToString(cast_guid_string);
94
this->session_id_ = session_id;
95
this->is_valid_ = true;
96
97
::RpcStringFree(&guid_string);
98
99
this->PopulateElementFinderMethods();
100
this->current_browser_id_ = "";
101
this->serialized_response_ = "";
102
this->unexpected_alert_behavior_ = "";
103
this->implicit_wait_timeout_ = 0;
104
this->async_script_timeout_ = DEFAULT_SCRIPT_TIMEOUT_IN_MILLISECONDS;
105
this->page_load_timeout_ = DEFAULT_PAGE_LOAD_TIMEOUT_IN_MILLISECONDS;
106
this->reattach_browser_timeout_ = DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS;
107
this->is_waiting_ = false;
108
this->is_quitting_ = false;
109
this->is_awaiting_new_window_ = false;
110
this->use_strict_file_interactability_ = false;
111
this->page_load_strategy_ = "normal";
112
this->file_upload_dialog_timeout_ = DEFAULT_FILE_UPLOAD_DIALOG_TIMEOUT_IN_MILLISECONDS;
113
114
this->managed_elements_ = new ElementRepository();
115
this->input_manager_ = new InputManager();
116
this->proxy_manager_ = new ProxyManager();
117
this->factory_ = new BrowserFactory();
118
this->element_finder_ = new ElementFinder();
119
this->command_handlers_ = new CommandHandlerRepository();
120
121
this->is_edge_chromium_ = false;
122
this->edge_temp_dir_ = L"";
123
124
return 0;
125
}
126
127
LRESULT IECommandExecutor::OnDestroy(UINT uMsg,
128
WPARAM wParam,
129
LPARAM lParam,
130
BOOL& bHandled) {
131
LOG(DEBUG) << "Entering IECommandExecutor::OnDestroy";
132
133
LOG(DEBUG) << "Clearing managed element cache";
134
this->managed_elements_->Clear();
135
delete this->managed_elements_;
136
LOG(DEBUG) << "Closing command handler repository";
137
delete this->command_handlers_;
138
LOG(DEBUG) << "Closing element finder";
139
delete this->element_finder_;
140
LOG(DEBUG) << "Closing input manager";
141
delete this->input_manager_;
142
LOG(DEBUG) << "Closing proxy manager";
143
delete this->proxy_manager_;
144
LOG(DEBUG) << "Closing browser factory";
145
delete this->factory_;
146
LOG(DEBUG) << "Posting quit message";
147
::PostQuitMessage(0);
148
LOG(DEBUG) << "Leaving IECommandExecutor::OnDestroy";
149
return 0;
150
}
151
152
LRESULT IECommandExecutor::OnSetCommand(UINT uMsg,
153
WPARAM wParam,
154
LPARAM lParam,
155
BOOL& bHandled) {
156
LOG(TRACE) << "Entering IECommandExecutor::OnSetCommand";
157
LRESULT set_command_result = 0;
158
159
LPCSTR json_command = reinterpret_cast<LPCSTR>(lParam);
160
Command requested_command;
161
requested_command.Deserialize(json_command);
162
163
this->set_command_mutex_.lock();
164
if (this->current_command_.command_type() == CommandType::NoCommand ||
165
requested_command.command_type() == CommandType::Quit) {
166
this->current_command_.Deserialize(json_command);
167
set_command_result = 1;
168
}
169
this->set_command_mutex_.unlock();
170
171
return set_command_result;
172
}
173
174
LRESULT IECommandExecutor::OnExecCommand(UINT uMsg,
175
WPARAM wParam,
176
LPARAM lParam,
177
BOOL& bHandled) {
178
LOG(TRACE) << "Entering IECommandExecutor::OnExecCommand";
179
180
this->DispatchCommand();
181
return 0;
182
}
183
184
LRESULT IECommandExecutor::OnGetResponseLength(UINT uMsg,
185
WPARAM wParam,
186
LPARAM lParam,
187
BOOL& bHandled) {
188
// Not logging trace entering IECommandExecutor::OnGetResponseLength,
189
// because it is polled repeatedly for a non-zero return value.
190
size_t response_length = 0;
191
if (!this->is_waiting_) {
192
response_length = this->serialized_response_.size();
193
}
194
return response_length;
195
}
196
197
LRESULT IECommandExecutor::OnGetResponse(UINT uMsg,
198
WPARAM wParam,
199
LPARAM lParam,
200
BOOL& bHandled) {
201
LOG(TRACE) << "Entering IECommandExecutor::OnGetResponse";
202
203
LPSTR str = reinterpret_cast<LPSTR>(lParam);
204
strcpy_s(str,
205
this->serialized_response_.size() + 1,
206
this->serialized_response_.c_str());
207
208
// Reset the serialized response for the next command.
209
this->serialized_response_ = "";
210
this->current_command_.Reset();
211
return 0;
212
}
213
214
LRESULT IECommandExecutor::OnWait(UINT uMsg,
215
WPARAM wParam,
216
LPARAM lParam,
217
BOOL& bHandled) {
218
LOG(TRACE) << "Entering IECommandExecutor::OnWait";
219
220
LPCSTR str = reinterpret_cast<LPCSTR>(lParam);
221
std::string deferred_response(str);
222
delete[] str;
223
224
LOG(DEBUG) << "Starting wait cycle.";
225
if (this->is_awaiting_new_window_) {
226
LOG(DEBUG) << "Awaiting new window. Aborting current wait cycle and "
227
<< "scheduling another.";
228
this->CreateWaitThread(deferred_response);
229
return 0;
230
}
231
232
bool is_single_wait = (wParam == 0);
233
234
BrowserHandle browser;
235
int status_code = this->GetCurrentBrowser(&browser);
236
if (status_code == WD_SUCCESS) {
237
if (!browser->is_closing()) {
238
if (this->page_load_timeout_ >= 0 && this->wait_timeout_ < clock()) {
239
LOG(DEBUG) << "Page load timeout reached. Ending wait cycle.";
240
Response timeout_response;
241
timeout_response.SetErrorResponse(ERROR_WEBDRIVER_TIMEOUT,
242
"Timed out waiting for page to load.");
243
browser->set_wait_required(false);
244
this->serialized_response_ = timeout_response.Serialize();
245
this->is_waiting_ = false;
246
return 0;
247
} else {
248
LOG(DEBUG) << "Beginning wait.";
249
this->is_waiting_ = !(browser->Wait(this->page_load_strategy_));
250
if (is_single_wait) {
251
LOG(DEBUG) << "Single requested wait with no deferred "
252
<< "response complete. Ending wait cycle.";
253
this->is_waiting_ = false;
254
return 0;
255
} else {
256
if (this->is_waiting_) {
257
LOG(DEBUG) << "Wait not complete. Scheduling another wait cycle.";
258
this->CreateWaitThread(deferred_response);
259
return 0;
260
}
261
}
262
}
263
}
264
}
265
LOG(DEBUG) << "Wait complete. Setting serialized response to deferred value "
266
<< deferred_response;
267
this->serialized_response_ = deferred_response;
268
this->is_waiting_ = false;
269
return 0;
270
}
271
272
LRESULT IECommandExecutor::OnBeforeNewWindow(UINT uMsg,
273
WPARAM wParam,
274
LPARAM lParam,
275
BOOL& bHandled) {
276
LOG(TRACE) << "Entering IECommandExecutor::OnBeforeNewWindow";
277
LOG(DEBUG) << "Setting await new window flag";
278
this->is_awaiting_new_window_ = true;
279
return 0;
280
}
281
282
LRESULT IECommandExecutor::OnAfterNewWindow(UINT uMsg,
283
WPARAM wParam,
284
LPARAM lParam,
285
BOOL& bHandled) {
286
LOG(TRACE) << "Entering IECommandExecutor::OnAfterNewWindow";
287
if (wParam > 0) {
288
LOG(DEBUG) << "Creating thread and reposting message.";
289
this->CreateDelayPostMessageThread(static_cast<DWORD>(wParam),
290
this->m_hWnd,
291
WD_AFTER_NEW_WINDOW);
292
if (lParam > 0) {
293
// a new window is created from Edge in IEMode
294
BrowserHandle browser_wrapper;
295
this->GetCurrentBrowser(&browser_wrapper);
296
HWND top_level_handle = browser_wrapper->GetTopLevelWindowHandle();
297
298
std::vector<HWND>* current_window_handles =
299
reinterpret_cast<std::vector<HWND>*>(lParam);
300
std::unordered_set<HWND> current_window_set(
301
current_window_handles->begin(),
302
current_window_handles->end());
303
delete current_window_handles;
304
305
// sleep 0.5s then get current window handles
306
clock_t end = clock() + (DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS / 1000 * CLOCKS_PER_SEC);
307
std::vector<HWND> diff;
308
int loop_count = 0;
309
bool is_processing_wm = false;
310
::Sleep(1000);
311
while (diff.size() == 0 && clock() < end) {
312
std::vector<HWND> edge_window_handles;
313
::EnumWindows(&BrowserFactory::FindEdgeBrowserHandles,
314
reinterpret_cast<LPARAM>(&edge_window_handles));
315
316
std::vector<HWND> new_ie_window_handles;
317
for (auto& edge_window_handle : edge_window_handles) {
318
std::vector<HWND> child_window_handles;
319
::EnumChildWindows(edge_window_handle,
320
&BrowserFactory::FindIEBrowserHandles,
321
reinterpret_cast<LPARAM>(&child_window_handles));
322
323
for (auto& child_window_handle : child_window_handles) {
324
new_ie_window_handles.push_back(child_window_handle);
325
}
326
}
327
328
for (auto& window_handle : new_ie_window_handles) {
329
if (current_window_set.find(window_handle) != current_window_set.end()) {
330
continue;
331
}
332
diff.push_back(window_handle);
333
}
334
335
if (diff.size() == 0) {
336
MSG msg;
337
if (loop_count >= 1 && !is_processing_wm &&::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_REMOVE)) {
338
::TranslateMessage(&msg);
339
::DispatchMessage(&msg);
340
is_processing_wm = true;
341
LOG(TRACE) << "process WM_USER";
342
::Sleep(500);
343
}
344
::Sleep(500);
345
}
346
loop_count++;
347
}
348
349
if (diff.size() == 0) {
350
LOG(WARN) << "No new window handle found after attempt to open";
351
} else {
352
for (int i = diff.size() - 1; i >= 0; i--) {
353
HWND new_window_window = diff[i];
354
355
DWORD process_id = 0;
356
::GetWindowThreadProcessId(new_window_window, &process_id);
357
if (process_id) {
358
clock_t end = clock() + (DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS / 1000 * CLOCKS_PER_SEC);
359
bool is_ready = this->factory_->IsBrowserProcessInitialized(process_id);
360
while (!is_ready && clock() < end) {
361
::Sleep(100);
362
is_ready = this->factory_->IsBrowserProcessInitialized(process_id);
363
}
364
365
ProcessWindowInfo info;
366
info.dwProcessId = process_id;
367
info.hwndBrowser = new_window_window;
368
info.pBrowser = NULL;
369
std::string error_message = "";
370
bool attach_flag = this->factory_->AttachToBrowser(&info, &error_message);
371
if (attach_flag) {
372
BrowserHandle new_window_wrapper(new Browser(info.pBrowser,
373
NULL,
374
this->m_hWnd,
375
this->is_edge_chromium_));
376
377
// Force a wait cycle to make sure the browser is finished initializing.
378
new_window_wrapper->Wait(NORMAL_PAGE_LOAD_STRATEGY);
379
this->AddManagedBrowser(new_window_wrapper);
380
}
381
} else {
382
LOG(TRACE) << "invalid window " << new_window_window;
383
}
384
}
385
}
386
}
387
} else {
388
LOG(DEBUG) << "Clearing await new window flag";
389
this->is_awaiting_new_window_ = false;
390
}
391
return 0;
392
}
393
394
LRESULT IECommandExecutor::OnBrowserNewWindow(UINT uMsg,
395
WPARAM wParam,
396
LPARAM lParam,
397
BOOL& bHandled) {
398
LOG(TRACE) << "Entering IECommandExecutor::OnBrowserNewWindow";
399
NewWindowInfo* info = reinterpret_cast<NewWindowInfo*>(lParam);
400
std::string target_url = info->target_url;
401
std::string new_browser_id = this->OpenNewBrowsingContext(WINDOW_WINDOW_TYPE,
402
target_url);
403
BrowserHandle new_window_wrapper;
404
this->GetManagedBrowser(new_browser_id, &new_window_wrapper);
405
if (new_window_wrapper->IsCrossZoneUrl(target_url)) {
406
new_window_wrapper->InitiateBrowserReattach();
407
}
408
409
LOG(DEBUG) << "Attempting to marshal interface pointer to requesting thread.";
410
IWebBrowser2* browser = new_window_wrapper->browser();
411
HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(IID_IWebBrowser2,
412
browser,
413
&(info->browser_stream));
414
if (FAILED(hr)) {
415
LOGHR(WARN, hr) << "Marshalling of interface pointer b/w threads is failed.";
416
}
417
418
return 0;
419
}
420
421
LRESULT IECommandExecutor::OnBrowserCloseWait(UINT uMsg,
422
WPARAM wParam,
423
LPARAM lParam,
424
BOOL& bHandled) {
425
LOG(TRACE) << "Entering IECommandExecutor::OnBrowserCloseWait";
426
427
LPCSTR str = reinterpret_cast<LPCSTR>(lParam);
428
std::string browser_id(str);
429
delete[] str;
430
BrowserMap::iterator found_iterator = this->managed_browsers_.find(browser_id);
431
if (found_iterator != this->managed_browsers_.end()) {
432
HWND alert_handle;
433
bool is_alert_active = this->IsAlertActive(found_iterator->second,
434
&alert_handle);
435
if (is_alert_active) {
436
// If there's an alert window active, the browser's Quit event does
437
// not fire until any alerts are handled. Note that OnBeforeUnload
438
// alerts must be handled here; the driver contains the ability to
439
// handle other standard alerts on the next received command. We rely
440
// on the browser's Quit command to remove the driver from the list of
441
// managed browsers.
442
Alert dialog(found_iterator->second, alert_handle);
443
if (!dialog.is_standard_alert()) {
444
dialog.Accept();
445
is_alert_active = false;
446
}
447
}
448
if (!is_alert_active) {
449
::Sleep(100);
450
// If no alert is present, repost the message to the message pump, so
451
// that we can wait until the browser is fully closed to return the
452
// proper still-open list of window handles.
453
LPSTR message_payload = new CHAR[browser_id.size() + 1];
454
strcpy_s(message_payload, browser_id.size() + 1, browser_id.c_str());
455
::PostMessage(this->m_hWnd,
456
WD_BROWSER_CLOSE_WAIT,
457
NULL,
458
reinterpret_cast<LPARAM>(message_payload));
459
return 0;
460
}
461
} else {
462
LOG(WARN) << "Unable to find browser to quit with ID " << browser_id;
463
}
464
Json::Value handles(Json::arrayValue);
465
std::vector<std::string> handle_list;
466
this->GetManagedBrowserHandles(&handle_list);
467
std::vector<std::string>::const_iterator handle_iterator = handle_list.begin();
468
for (; handle_iterator != handle_list.end(); ++handle_iterator) {
469
handles.append(*handle_iterator);
470
}
471
472
Response close_window_response;
473
close_window_response.SetSuccessResponse(handles);
474
this->serialized_response_ = close_window_response.Serialize();
475
this->is_waiting_ = false;
476
return 0;
477
}
478
479
LRESULT IECommandExecutor::OnSessionQuitWait(UINT uMsg,
480
WPARAM wParam,
481
LPARAM lParam,
482
BOOL& bHandled) {
483
LOG(TRACE) << "Entering IECommandExecutor::OnAllBrowserCloseWait";
484
if (this->managed_browsers_.size() > 0) {
485
LOG(TRACE) << "Still have " << this->managed_browsers_.size() << " browsers";
486
BrowserMap::const_iterator it = managed_browsers_.begin();
487
for (; it != managed_browsers_.end(); ++it) {
488
LOG(TRACE) << "Still awaiting close of browser with ID " << it->first;
489
HWND alert_handle;
490
bool is_alert_active = this->IsAlertActive(it->second,
491
&alert_handle);
492
if (is_alert_active) {
493
// If there's an alert window active, the browser's Quit event does
494
// not fire until any alerts are handled. Note that OnBeforeUnload
495
// alerts must be handled here; the driver contains the ability to
496
// handle other standard alerts on the next received command. We rely
497
// on the browser's Quit command to remove the driver from the list of
498
// managed browsers.
499
Alert dialog(it->second, alert_handle);
500
if (!dialog.is_standard_alert()) {
501
dialog.Accept();
502
is_alert_active = false;
503
}
504
}
505
}
506
::Sleep(WAIT_TIME_IN_MILLISECONDS);
507
::PostMessage(this->m_hWnd,
508
WD_SESSION_QUIT_WAIT,
509
NULL,
510
NULL);
511
} else {
512
for (auto& chromium_window_handle : this->chromium_window_handles_) {
513
::PostMessage(chromium_window_handle, WM_CLOSE, NULL, NULL);
514
}
515
this->is_waiting_ = false;
516
Response quit_response;
517
quit_response.SetSuccessResponse(Json::Value::null);
518
this->serialized_response_ = quit_response.Serialize();
519
}
520
return 0;
521
}
522
523
LRESULT IECommandExecutor::OnAddChromiumWindowHandle(UINT uMsg,
524
WPARAM wParam,
525
LPARAM lParam,
526
BOOL& bHandled) {
527
if (wParam != NULL) {
528
this->chromium_window_handles_.emplace(reinterpret_cast<HWND>(wParam));
529
}
530
return 0;
531
}
532
533
LRESULT IECommandExecutor::OnBrowserQuit(UINT uMsg,
534
WPARAM wParam,
535
LPARAM lParam,
536
BOOL& bHandled) {
537
LOG(TRACE) << "Entering IECommandExecutor::OnBrowserQuit";
538
539
LPCSTR str = reinterpret_cast<LPCSTR>(lParam);
540
std::string browser_id(str);
541
delete[] str;
542
LOG(TRACE) << "Removing browser with ID " << browser_id;
543
BrowserMap::iterator found_iterator =
544
this->managed_browsers_.find(browser_id);
545
546
if (found_iterator != this->managed_browsers_.end()) {
547
this->managed_browsers_.erase(browser_id);
548
if (this->managed_browsers_.size() == 0) {
549
this->current_browser_id_ = "";
550
}
551
LOG(TRACE) << "Successfully removed browser with ID " << browser_id;
552
} else {
553
LOG(WARN) << "Unable to find browser to quit with ID " << browser_id;
554
}
555
556
return 0;
557
}
558
559
LRESULT IECommandExecutor::OnBeforeBrowserReattach(UINT uMsg,
560
WPARAM wParam,
561
LPARAM lParam,
562
BOOL& bHandled) {
563
LOG(TRACE) << "Entering IECommandExecutor::OnBeforeBrowserReattach";
564
if (this->factory_->ignore_protected_mode_settings()) {
565
this->reattach_wait_timeout_ = clock() + (static_cast<int>(this->reattach_browser_timeout_) / 1000 * CLOCKS_PER_SEC);
566
}
567
return 0;
568
}
569
570
LRESULT IECommandExecutor::OnBrowserReattach(UINT uMsg,
571
WPARAM wParam,
572
LPARAM lParam,
573
BOOL& bHandled) {
574
LOG(TRACE) << "Entering IECommandExecutor::OnBrowserReattach";
575
BrowserReattachInfo* info = reinterpret_cast<BrowserReattachInfo*>(lParam);
576
DWORD current_process_id = info->current_process_id;
577
std::string browser_id = info->browser_id;
578
std::vector<DWORD> known_process_ids = info->known_process_ids;
579
delete info;
580
581
if (!this->factory_->ignore_protected_mode_settings()) {
582
return 0;
583
}
584
585
if (this->reattach_wait_timeout_ < clock()) {
586
LOG(WARN) << "Reattach attempt has timed out";
587
return 0;
588
}
589
590
LOG(DEBUG) << "Starting browser reattach process";
591
592
std::vector<DWORD> new_process_ids;
593
this->GetNewBrowserProcessIds(&known_process_ids, &new_process_ids);
594
if (new_process_ids.size() == 0) {
595
LOG(DEBUG) << "No new process found, rescheduling reattach";
596
// If no new process IDs were found yet, repost the message
597
this->PostBrowserReattachMessage(current_process_id,
598
browser_id,
599
known_process_ids);
600
}
601
if (new_process_ids.size() > 1) {
602
LOG(WARN) << "Found more than one new iexplore.exe process. It is "
603
<< "impossible to know which is the proper one. Choosing one "
604
<< "at random.";
605
}
606
607
DWORD new_process_id = new_process_ids[0];
608
if (!this->factory_->IsBrowserProcessInitialized(new_process_id)) {
609
// If the browser for the new process ID is not yet ready,
610
// repost the message
611
LOG(DEBUG) << "Browser process " << new_process_id
612
<< " not initialized, rescheduling reattach";
613
this->PostBrowserReattachMessage(current_process_id,
614
browser_id,
615
known_process_ids);
616
return 0;
617
}
618
619
std::string error_message = "";
620
ProcessWindowInfo process_window_info;
621
process_window_info.dwProcessId = new_process_id;
622
process_window_info.hwndBrowser = NULL;
623
process_window_info.pBrowser = NULL;
624
bool attached = this->factory_->AttachToBrowser(&process_window_info, &error_message);
625
626
BrowserMap::iterator found_iterator = this->managed_browsers_.find(browser_id);
627
if (found_iterator != this->managed_browsers_.end()) {
628
this->proxy_manager_->SetProxySettings(process_window_info.hwndBrowser);
629
found_iterator->second->cookie_manager()->Initialize(process_window_info.hwndBrowser);
630
found_iterator->second->ReattachBrowser(process_window_info.pBrowser);
631
} else {
632
LOG(WARN) << "The managed browser was not found to reattach to.";
633
}
634
return 0;
635
}
636
637
LRESULT IECommandExecutor::OnIsSessionValid(UINT uMsg,
638
WPARAM wParam,
639
LPARAM lParam,
640
BOOL& bHandled) {
641
LOG(TRACE) << "Entering IECommandExecutor::OnIsSessionValid";
642
643
return this->is_valid_ ? 1 : 0;
644
}
645
646
LRESULT IECommandExecutor::OnNewHtmlDialog(UINT uMsg,
647
WPARAM wParam,
648
LPARAM lParam,
649
BOOL& bHandles) {
650
LOG(TRACE) << "Entering IECommandExecutor::OnNewHtmlDialog";
651
652
HWND dialog_handle = reinterpret_cast<HWND>(lParam);
653
BrowserMap::const_iterator it = this->managed_browsers_.begin();
654
for (; it != this->managed_browsers_.end(); ++it) {
655
if (dialog_handle == it->second->window_handle()) {
656
LOG(DEBUG) << "Dialog is equal to one managed browser";
657
return 0;
658
}
659
}
660
661
int retry_count = 0;
662
CComPtr<IHTMLDocument2> document;
663
bool found_document = this->factory_->GetDocumentFromWindowHandle(dialog_handle, &document);
664
while (found_document && retry_count < MAX_HTML_DIALOG_RETRIES) {
665
CComPtr<IHTMLWindow2> window;
666
HRESULT hr = document->get_parentWindow(&window);
667
if (FAILED(hr)) {
668
// Getting the parent window of the dialog's document failed. This
669
// usually means that the document changed out from under us before we
670
// could get the window reference. The canonical case for this is a
671
// redirect using JavaScript. Sleep for a short time, then retry to
672
// obtain the reference.
673
LOGHR(DEBUG, hr) << "IHTMLDocument2::get_parentWindow failed. Retrying.";
674
::Sleep(100);
675
document.Release();
676
found_document = this->factory_->GetDocumentFromWindowHandle(dialog_handle, &document);
677
++retry_count;
678
} else {
679
this->AddManagedBrowser(BrowserHandle(new HtmlDialog(window,
680
dialog_handle,
681
this->m_hWnd)));
682
return 0;
683
}
684
}
685
if (found_document) {
686
LOG(WARN) << "Got document from dialog, but could not get window";
687
} else {
688
LOG(WARN) << "Unable to get document from dialog";
689
}
690
return 0;
691
}
692
693
LRESULT IECommandExecutor::OnQuit(UINT uMsg,
694
WPARAM wParam,
695
LPARAM lParam,
696
BOOL& bHandled) {
697
698
// Delete IEDriver temporary folder when IEDriver drvies Edge in IEMode.
699
// Note that the this->factory_ object might have been deleted.
700
if (this->edge_temp_dir_ != L"") {
701
for (int i = 0; i < 100; i++) {
702
// wait for the Edge browser completing read/write work
703
// the delete usually completes in 1 retries
704
::Sleep(100);
705
if (BrowserFactory::DeleteDirectory(edge_temp_dir_)) {
706
// directory delete failed when some files/folders are locked
707
LOG(TRACE) << "Failed to delete Edge temporary user data directory "
708
<< LOGWSTRING(edge_temp_dir_) << ", retrying "
709
<< i + 1 << "...";
710
}
711
else {
712
// the temporary folder has been deleted
713
LOG(TRACE) << "Deleted Edge temporary user data directory "
714
<< LOGWSTRING(edge_temp_dir_) << ".";
715
break;
716
}
717
}
718
this->edge_temp_dir_ = L"";
719
}
720
return 0;
721
}
722
723
LRESULT IECommandExecutor::OnGetQuitStatus(UINT uMsg,
724
WPARAM wParam,
725
LPARAM lParam,
726
BOOL& bHandled) {
727
return this->is_quitting_ && this->managed_browsers_.size() > 0 ? 1 : 0;
728
}
729
730
LRESULT IECommandExecutor::OnScriptWait(UINT uMsg,
731
WPARAM wParam,
732
LPARAM lParam,
733
BOOL& bHandled) {
734
LOG(TRACE) << "Entering IECommandExecutor::OnScriptWait";
735
736
BrowserHandle browser;
737
int status_code = this->GetCurrentBrowser(&browser);
738
if (status_code == WD_SUCCESS && !browser->is_closing()) {
739
if (this->async_script_timeout_ >= 0 && this->wait_timeout_ < clock()) {
740
::SendMessage(browser->script_executor_handle(),
741
WD_ASYNC_SCRIPT_DETACH_LISTENTER,
742
NULL,
743
NULL);
744
Response timeout_response;
745
timeout_response.SetErrorResponse(ERROR_SCRIPT_TIMEOUT,
746
"Timed out waiting for script to complete.");
747
this->serialized_response_ = timeout_response.Serialize();
748
this->is_waiting_ = false;
749
browser->set_script_executor_handle(NULL);
750
} else {
751
HWND alert_handle;
752
bool is_execution_finished = ::SendMessage(browser->script_executor_handle(),
753
WD_ASYNC_SCRIPT_IS_EXECUTION_COMPLETE,
754
NULL,
755
NULL) != 0;
756
bool is_alert_active = this->IsAlertActive(browser, &alert_handle);
757
this->is_waiting_ = !is_execution_finished && !is_alert_active;
758
if (this->is_waiting_) {
759
// If we are still waiting, we need to wait a bit then post a message to
760
// ourselves to run the wait again. However, we can't wait using Sleep()
761
// on this thread. This call happens in a message loop, and we would be
762
// unable to process the COM events in the browser if we put this thread
763
// to sleep.
764
unsigned int thread_id = 0;
765
HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
766
0,
767
&IECommandExecutor::ScriptWaitThreadProc,
768
(void *)this->m_hWnd,
769
0,
770
&thread_id));
771
if (thread_handle != NULL) {
772
::CloseHandle(thread_handle);
773
} else {
774
LOGERR(DEBUG) << "Unable to create waiter thread";
775
}
776
} else {
777
Response response;
778
Json::Value script_result;
779
::SendMessage(browser->script_executor_handle(),
780
WD_ASYNC_SCRIPT_DETACH_LISTENTER,
781
NULL,
782
NULL);
783
int status_code = static_cast<int>(::SendMessage(browser->script_executor_handle(),
784
WD_ASYNC_SCRIPT_GET_RESULT,
785
NULL,
786
reinterpret_cast<LPARAM>(&script_result)));
787
if (status_code != WD_SUCCESS) {
788
std::string error_message = "Error executing JavaScript";
789
if (script_result.isString()) {
790
error_message = script_result.asString();
791
}
792
response.SetErrorResponse(status_code, error_message);
793
} else {
794
response.SetSuccessResponse(script_result);
795
}
796
::SendMessage(browser->script_executor_handle(), WM_CLOSE, NULL, NULL);
797
browser->set_script_executor_handle(NULL);
798
this->serialized_response_ = response.Serialize();
799
}
800
}
801
} else {
802
this->is_waiting_ = false;
803
}
804
return 0;
805
}
806
807
LRESULT IECommandExecutor::OnRefreshManagedElements(UINT uMsg,
808
WPARAM wParam,
809
LPARAM lParam,
810
BOOL& bHandled) {
811
this->managed_elements_->ClearCache();
812
return 0;
813
}
814
815
LRESULT IECommandExecutor::OnHandleUnexpectedAlerts(UINT uMsg,
816
WPARAM wParam,
817
LPARAM lParam,
818
BOOL& bHandled) {
819
LOG(TRACE) << "Entering IECommandExecutor::OnHandleUnexpectedAlerts";
820
BrowserMap::const_iterator it = this->managed_browsers_.begin();
821
for (; it != this->managed_browsers_.end(); ++it) {
822
HWND alert_handle = it->second->GetActiveDialogWindowHandle();
823
if (alert_handle != NULL) {
824
std::string alert_text;
825
this->HandleUnexpectedAlert(it->second, alert_handle, true, &alert_text);
826
}
827
}
828
return 0;
829
}
830
831
LRESULT IECommandExecutor::OnTransferManagedElement(UINT uMsg,
832
WPARAM wParam,
833
LPARAM lParam,
834
BOOL& bHandled) {
835
LOG(TRACE) << "Entering IECommandExecutor::OnTransferManagedElement";
836
ElementInfo* info = reinterpret_cast<ElementInfo*>(lParam);
837
std::string element_id = info->element_id;
838
LPSTREAM element_stream = info->element_stream;
839
BrowserHandle browser_handle;
840
this->GetCurrentBrowser(&browser_handle);
841
CComPtr<IHTMLElement> element;
842
::CoGetInterfaceAndReleaseStream(element_stream,
843
IID_IHTMLElement,
844
reinterpret_cast<void**>(&element));
845
delete info;
846
ElementHandle element_handle;
847
this->managed_elements_->AddManagedElement(browser_handle,
848
element,
849
&element_handle);
850
RemappedElementInfo* return_info = new RemappedElementInfo;
851
return_info->original_element_id = element_id;
852
return_info->element_id = element_handle->element_id();
853
::PostMessage(browser_handle->script_executor_handle(),
854
WD_ASYNC_SCRIPT_NOTIFY_ELEMENT_TRANSFERRED,
855
NULL,
856
reinterpret_cast<LPARAM>(return_info));
857
return WD_SUCCESS;
858
}
859
860
LRESULT IECommandExecutor::OnScheduleRemoveManagedElement(UINT uMsg,
861
WPARAM wParam,
862
LPARAM lParam,
863
BOOL& bHandled) {
864
LOG(TRACE) << "Entering IECommandExecutor::OnScheduleRemoveManagedElement";
865
ElementInfo* info = reinterpret_cast<ElementInfo*>(lParam);
866
std::string element_id = info->element_id;
867
delete info;
868
this->RemoveManagedElement(element_id);
869
return WD_SUCCESS;
870
}
871
872
unsigned int WINAPI IECommandExecutor::WaitThreadProc(LPVOID lpParameter) {
873
LOG(TRACE) << "Entering IECommandExecutor::WaitThreadProc";
874
WaitThreadContext* thread_context = reinterpret_cast<WaitThreadContext*>(lpParameter);
875
HWND window_handle = thread_context->window_handle;
876
bool is_deferred_command = thread_context->is_deferred_command;
877
std::string deferred_response(thread_context->deferred_response);
878
delete thread_context->deferred_response;
879
delete thread_context;
880
881
LPSTR message_payload = new CHAR[deferred_response.size() + 1];
882
strcpy_s(message_payload,
883
deferred_response.size() + 1,
884
deferred_response.c_str());
885
886
::Sleep(WAIT_TIME_IN_MILLISECONDS);
887
::PostMessage(window_handle,
888
WD_WAIT,
889
static_cast<WPARAM>(deferred_response.size()),
890
reinterpret_cast<LPARAM>(message_payload));
891
if (is_deferred_command) {
892
// This wait is requested by the automatic handling of a user prompt
893
// before a command was executed, so re-queue the command execution
894
// for after the wait.
895
::PostMessage(window_handle, WD_EXEC_COMMAND, NULL, NULL);
896
}
897
return 0;
898
}
899
900
unsigned int WINAPI IECommandExecutor::ScriptWaitThreadProc(LPVOID lpParameter) {
901
LOG(TRACE) << "Entering IECommandExecutor::ScriptWaitThreadProc";
902
HWND window_handle = reinterpret_cast<HWND>(lpParameter);
903
::Sleep(SCRIPT_WAIT_TIME_IN_MILLISECONDS);
904
::PostMessage(window_handle, WD_SCRIPT_WAIT, NULL, NULL);
905
return 0;
906
}
907
908
unsigned int WINAPI IECommandExecutor::DelayPostMessageThreadProc(LPVOID lpParameter) {
909
LOG(TRACE) << "Entering IECommandExecutor::DelayPostMessageThreadProc";
910
DelayPostMessageThreadContext* context = reinterpret_cast<DelayPostMessageThreadContext*>(lpParameter);
911
HWND window_handle = context->window_handle;
912
DWORD sleep_time = context->delay;
913
UINT message_to_post = context->msg;
914
delete context;
915
916
::Sleep(sleep_time);
917
::PostMessage(window_handle, message_to_post, NULL, NULL);
918
return 0;
919
}
920
921
unsigned int WINAPI IECommandExecutor::ThreadProc(LPVOID lpParameter) {
922
LOG(TRACE) << "Entering IECommandExecutor::ThreadProc";
923
924
IECommandExecutorThreadContext* thread_context = reinterpret_cast<IECommandExecutorThreadContext*>(lpParameter);
925
HWND window_handle = thread_context->hwnd;
926
927
// it is better to use IECommandExecutorSessionContext instead
928
// but use ThreadContext for code minimization
929
IECommandExecutorThreadContext* session_context = new IECommandExecutorThreadContext();
930
session_context->port = thread_context->port;
931
932
DWORD error = 0;
933
HRESULT hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
934
if (FAILED(hr)) {
935
LOGHR(DEBUG, hr) << "COM library initialization encountered an error";
936
}
937
938
IECommandExecutor new_session;
939
HWND session_window_handle = new_session.Create(/*HWND*/ HWND_MESSAGE,
940
/*_U_RECT rect*/ CWindow::rcDefault,
941
/*LPCTSTR szWindowName*/ NULL,
942
/*DWORD dwStyle*/ NULL,
943
/*DWORD dwExStyle*/ NULL,
944
/*_U_MENUorID MenuOrID*/ 0U,
945
/*LPVOID lpCreateParam*/ reinterpret_cast<LPVOID*>(session_context));
946
if (session_window_handle == NULL) {
947
LOGERR(WARN) << "Unable to create new IECommandExecutor session";
948
}
949
950
MSG msg;
951
::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
952
953
// Return the HWND back through lpParameter, and signal that the
954
// window is ready for messages.
955
thread_context->hwnd = session_window_handle;
956
HANDLE event_handle = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, WEBDRIVER_START_EVENT_NAME);
957
if (event_handle != NULL) {
958
::SetEvent(event_handle);
959
::CloseHandle(event_handle);
960
} else {
961
LOGERR(DEBUG) << "Unable to signal that window is ready";
962
}
963
964
// Run the message loop
965
BOOL get_message_return_value;
966
while ((get_message_return_value = ::GetMessage(&msg, NULL, 0, 0)) != 0) {
967
if (get_message_return_value == -1) {
968
LOGERR(WARN) << "Windows GetMessage() API returned error";
969
break;
970
} else {
971
if (msg.message == WD_SHUTDOWN) {
972
LOG(DEBUG) << "Shutdown message received";
973
new_session.DestroyWindow();
974
LOG(DEBUG) << "Returned from DestroyWindow()";
975
break;
976
} else {
977
// We need to lock this mutex here to make sure only one thread is processing
978
// win32 messages at a time.
979
static std::mutex messageLock;
980
std::lock_guard<std::mutex> lock(messageLock);
981
::TranslateMessage(&msg);
982
::DispatchMessage(&msg);
983
}
984
}
985
}
986
987
LOG(DEBUG) << "Exited IECommandExecutor thread message loop";
988
::CoUninitialize();
989
delete session_context;
990
return 0;
991
}
992
993
void IECommandExecutor::DispatchCommand() {
994
LOG(TRACE) << "Entering IECommandExecutor::DispatchCommand";
995
996
Response response;
997
998
if (!this->command_handlers_->IsValidCommand(this->current_command_.command_type())) {
999
LOG(WARN) << "Unable to find command handler for " << this->current_command_.command_type();
1000
response.SetErrorResponse(ERROR_UNKNOWN_COMMAND, "Command not implemented");
1001
} else if (!this->current_command_.is_valid_parameters()) {
1002
response.SetErrorResponse(ERROR_INVALID_ARGUMENT, "parameters property of command is not a valid JSON object");
1003
} else {
1004
BrowserHandle browser;
1005
int status_code = WD_SUCCESS;
1006
std::string command_type = this->current_command_.command_type();
1007
if (command_type != webdriver::CommandType::NewSession) {
1008
// There should never be a modal dialog or alert to check for if the command
1009
// is the "newSession" command.
1010
status_code = this->GetCurrentBrowser(&browser);
1011
if (status_code == WD_SUCCESS) {
1012
LOG(DEBUG) << "Checking for alert before executing " << command_type << " command";
1013
HWND alert_handle = NULL;
1014
bool alert_is_active = this->IsAlertActive(browser, &alert_handle);
1015
if (alert_is_active) {
1016
if (this->IsCommandValidWithAlertPresent()) {
1017
LOG(DEBUG) << "Alert is detected, and the sent command is valid";
1018
} else {
1019
LOG(DEBUG) << "Unexpected alert is detected, and the sent command "
1020
<< "is invalid when an alert is present";
1021
bool is_quit_command = command_type == webdriver::CommandType::Quit;
1022
1023
std::string alert_text;
1024
bool is_notify_unexpected_alert = this->HandleUnexpectedAlert(browser,
1025
alert_handle,
1026
is_quit_command,
1027
&alert_text);
1028
if (!is_quit_command) {
1029
if (is_notify_unexpected_alert) {
1030
// To keep pace with what the specification suggests, we'll
1031
// return the text of the alert in the error response.
1032
response.SetErrorResponse(EUNEXPECTEDALERTOPEN,
1033
"Modal dialog present with text: " + alert_text);
1034
response.AddAdditionalData("text", alert_text);
1035
this->serialized_response_ = response.Serialize();
1036
return;
1037
} else {
1038
LOG(DEBUG) << "Command other than quit was issued, and option "
1039
<< "to not notify was specified. Continuing with "
1040
<< "command after automatically closing alert.";
1041
// Push a wait cycle, then re-execute the current command (which
1042
// hasn't actually been executed yet). Note that an empty string
1043
// for the deferred response parameter of CreateWaitThread will
1044
// re-queue the execution of the command.
1045
this->CreateWaitThread("", true);
1046
return;
1047
}
1048
} else {
1049
LOG(DEBUG) << "Quit command was issued. Continuing with "
1050
<< "command after automatically closing alert.";
1051
}
1052
}
1053
}
1054
} else {
1055
LOG(WARN) << "Unable to find current browser";
1056
}
1057
}
1058
1059
LOG(DEBUG) << "Executing command: " << command_type;
1060
CommandHandlerHandle command_handler = this->command_handlers_->GetCommandHandler(command_type);
1061
command_handler->Execute(*this, this->current_command_, &response);
1062
LOG(DEBUG) << "Command execution for " << command_type << " complete";
1063
1064
status_code = this->GetCurrentBrowser(&browser);
1065
if (status_code == WD_SUCCESS) {
1066
if (browser->is_closing() && !this->is_quitting_) {
1067
// Case 1: The browser window is closing, but not via the Quit command,
1068
// so the executor must wait for the browser window to be closed and
1069
// removed from the list of managed browser windows.
1070
LOG(DEBUG) << "Browser is closing; awaiting close.";
1071
LPSTR message_payload = new CHAR[browser->browser_id().size() + 1];
1072
strcpy_s(message_payload,
1073
browser->browser_id().size() + 1,
1074
browser->browser_id().c_str());
1075
1076
this->is_waiting_ = true;
1077
::Sleep(WAIT_TIME_IN_MILLISECONDS);
1078
::PostMessage(this->m_hWnd,
1079
WD_BROWSER_CLOSE_WAIT,
1080
NULL,
1081
reinterpret_cast<LPARAM>(message_payload));
1082
return;
1083
} else if (browser->script_executor_handle() != NULL) {
1084
// Case 2: There is a pending asynchronous JavaScript execution in
1085
// progress, so the executor must wait for the script to complete
1086
// or a timeout.
1087
this->is_waiting_ = true;
1088
if (this->async_script_timeout_ >= 0) {
1089
this->wait_timeout_ = clock() + (static_cast<int>(this->async_script_timeout_) / 1000 * CLOCKS_PER_SEC);
1090
}
1091
LOG(DEBUG) << "Awaiting completion of in-progress asynchronous JavaScript execution.";
1092
::PostMessage(this->m_hWnd, WD_SCRIPT_WAIT, NULL, NULL);
1093
return;
1094
} else if (browser->wait_required()) {
1095
// Case 3: The command handler has explicitly asked to wait for page
1096
// load, so the executor must wait for page load or timeout.
1097
this->is_waiting_ = true;
1098
if (this->page_load_timeout_ >= 0) {
1099
this->wait_timeout_ = clock() + (static_cast<int>(this->page_load_timeout_) / 1000 * CLOCKS_PER_SEC);
1100
}
1101
std::string deferred_response = response.Serialize();
1102
LOG(DEBUG) << "Command handler requested wait. This will cause a minimal wait of at least 50 milliseconds.";
1103
this->CreateWaitThread(deferred_response);
1104
return;
1105
}
1106
} else {
1107
if (this->current_command_.command_type() != webdriver::CommandType::Quit) {
1108
LOG(WARN) << "Unable to get current browser";
1109
}
1110
}
1111
1112
if (this->is_edge_chromium_ && this->is_quitting_) {
1113
// If this is Edge in IE Mode, we need to explicitly wait for the
1114
// browsers to be completely deleted before returning for the quit
1115
// command.
1116
this->is_waiting_ = true;
1117
::Sleep(WAIT_TIME_IN_MILLISECONDS);
1118
::PostMessage(this->m_hWnd,
1119
WD_SESSION_QUIT_WAIT,
1120
NULL,
1121
NULL);
1122
return;
1123
}
1124
}
1125
1126
this->serialized_response_ = response.Serialize();
1127
LOG(DEBUG) << "Setting serialized response to " << this->serialized_response_;
1128
LOG(DEBUG) << "Is waiting flag: " << this->is_waiting_ ? "true" : "false";
1129
}
1130
1131
bool IECommandExecutor::IsCommandValidWithAlertPresent() {
1132
std::string command_type = this->current_command_.command_type();
1133
if (command_type == webdriver::CommandType::GetAlertText ||
1134
command_type == webdriver::CommandType::SendKeysToAlert ||
1135
command_type == webdriver::CommandType::AcceptAlert ||
1136
command_type == webdriver::CommandType::DismissAlert ||
1137
command_type == webdriver::CommandType::SetAlertCredentials ||
1138
command_type == webdriver::CommandType::GetTimeouts ||
1139
command_type == webdriver::CommandType::SetTimeouts ||
1140
command_type == webdriver::CommandType::Screenshot ||
1141
command_type == webdriver::CommandType::ElementScreenshot ||
1142
command_type == webdriver::CommandType::GetCurrentWindowHandle ||
1143
command_type == webdriver::CommandType::GetWindowHandles ||
1144
command_type == webdriver::CommandType::SwitchToWindow) {
1145
return true;
1146
}
1147
return false;
1148
}
1149
1150
void IECommandExecutor::CreateWaitThread(const std::string& deferred_response) {
1151
this->CreateWaitThread(deferred_response, false);
1152
}
1153
1154
void IECommandExecutor::CreateWaitThread(const std::string& deferred_response,
1155
const bool is_deferred_command_execution) {
1156
// If we are still waiting, we need to wait a bit then post a message to
1157
// ourselves to run the wait again. However, we can't wait using Sleep()
1158
// on this thread. This call happens in a message loop, and we would be
1159
// unable to process the COM events in the browser if we put this thread
1160
// to sleep.
1161
LOG(DEBUG) << "Creating wait thread with deferred response of `" << deferred_response << "`";
1162
if (is_deferred_command_execution) {
1163
LOG(DEBUG) << "Command execution will be rescheduled.";
1164
}
1165
WaitThreadContext* thread_context = new WaitThreadContext;
1166
thread_context->window_handle = this->m_hWnd;
1167
thread_context->is_deferred_command = is_deferred_command_execution;
1168
thread_context->deferred_response = new CHAR[deferred_response.size() + 1];
1169
strcpy_s(thread_context->deferred_response,
1170
deferred_response.size() + 1,
1171
deferred_response.c_str());
1172
1173
unsigned int thread_id = 0;
1174
HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
1175
0,
1176
&IECommandExecutor::WaitThreadProc,
1177
reinterpret_cast<void*>(thread_context),
1178
0,
1179
&thread_id));
1180
if (thread_handle != NULL) {
1181
::CloseHandle(thread_handle);
1182
}
1183
else {
1184
LOGERR(DEBUG) << "Unable to create waiter thread";
1185
}
1186
}
1187
1188
void IECommandExecutor::CreateDelayPostMessageThread(const DWORD delay_time,
1189
const HWND window_handle,
1190
const UINT message_to_post) {
1191
DelayPostMessageThreadContext* context = new DelayPostMessageThreadContext;
1192
context->delay = delay_time;
1193
context->window_handle = window_handle;
1194
context->msg = message_to_post;
1195
unsigned int thread_id = 0;
1196
HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
1197
0,
1198
&IECommandExecutor::DelayPostMessageThreadProc,
1199
reinterpret_cast<void*>(context),
1200
0,
1201
&thread_id));
1202
if (thread_handle != NULL) {
1203
::CloseHandle(thread_handle);
1204
} else {
1205
LOGERR(DEBUG) << "Unable to create waiter thread";
1206
}
1207
}
1208
1209
bool IECommandExecutor::IsAlertActive(BrowserHandle browser, HWND* alert_handle) {
1210
LOG(TRACE) << "Entering IECommandExecutor::IsAlertActive";
1211
HWND dialog_handle = browser->GetActiveDialogWindowHandle();
1212
if (dialog_handle != NULL) {
1213
// Found a window handle, make sure it's an actual alert,
1214
// and not a showModalDialog() window.
1215
std::vector<char> window_class_name(34);
1216
::GetClassNameA(dialog_handle, &window_class_name[0], 34);
1217
if (strcmp(ALERT_WINDOW_CLASS, &window_class_name[0]) == 0) {
1218
*alert_handle = dialog_handle;
1219
return true;
1220
} else if (strcmp(SECURITY_DIALOG_WINDOW_CLASS, &window_class_name[0]) == 0) {
1221
*alert_handle = dialog_handle;
1222
return true;
1223
} else {
1224
LOG(WARN) << "Found alert handle does not have a window class consistent with an alert";
1225
}
1226
} else {
1227
LOG(DEBUG) << "No alert handle is found";
1228
}
1229
return false;
1230
}
1231
1232
bool IECommandExecutor::HandleUnexpectedAlert(BrowserHandle browser,
1233
HWND alert_handle,
1234
bool force_use_dismiss,
1235
std::string* alert_text) {
1236
LOG(TRACE) << "Entering IECommandExecutor::HandleUnexpectedAlert";
1237
clock_t end = clock() + 5 * CLOCKS_PER_SEC;
1238
bool is_visible = (::IsWindowVisible(alert_handle) == TRUE);
1239
while (!is_visible && clock() < end) {
1240
::Sleep(50);
1241
is_visible = (::IsWindowVisible(alert_handle) == TRUE);
1242
}
1243
Alert dialog(browser, alert_handle);
1244
*alert_text = dialog.GetText();
1245
if (!dialog.is_standard_alert()) {
1246
// The dialog was non-standard. The most common case of this is
1247
// an onBeforeUnload dialog, which must be accepted to continue.
1248
dialog.Accept();
1249
return false;
1250
}
1251
if (this->unexpected_alert_behavior_ == ACCEPT_UNEXPECTED_ALERTS ||
1252
this->unexpected_alert_behavior_ == ACCEPT_AND_NOTIFY_UNEXPECTED_ALERTS) {
1253
LOG(DEBUG) << "Automatically accepting the alert";
1254
dialog.Accept();
1255
} else if (this->unexpected_alert_behavior_.size() == 0 ||
1256
this->unexpected_alert_behavior_ == DISMISS_UNEXPECTED_ALERTS ||
1257
this->unexpected_alert_behavior_ == DISMISS_AND_NOTIFY_UNEXPECTED_ALERTS ||
1258
force_use_dismiss) {
1259
// If a quit command was issued, we should not ignore an unhandled
1260
// alert, even if the alert behavior is set to "ignore".
1261
LOG(DEBUG) << "Automatically dismissing the alert";
1262
if (dialog.is_standard_alert() || dialog.is_security_alert()) {
1263
dialog.Dismiss();
1264
} else {
1265
// The dialog was non-standard. The most common case of this is
1266
// an onBeforeUnload dialog, which must be accepted to continue.
1267
dialog.Accept();
1268
}
1269
}
1270
1271
bool is_notify_unexpected_alert =
1272
this->unexpected_alert_behavior_.size() == 0 ||
1273
this->unexpected_alert_behavior_ == IGNORE_UNEXPECTED_ALERTS ||
1274
this->unexpected_alert_behavior_ == DISMISS_AND_NOTIFY_UNEXPECTED_ALERTS ||
1275
this->unexpected_alert_behavior_ == ACCEPT_AND_NOTIFY_UNEXPECTED_ALERTS;
1276
is_notify_unexpected_alert = is_notify_unexpected_alert && dialog.is_standard_alert();
1277
return is_notify_unexpected_alert;
1278
}
1279
1280
void IECommandExecutor::PostBrowserReattachMessage(const DWORD current_process_id,
1281
const std::string& browser_id,
1282
const std::vector<DWORD>& known_process_ids) {
1283
LOG(TRACE) << "Entering IECommandExecutor::PostBrowserReattachMessage";
1284
::Sleep(100);
1285
BrowserReattachInfo* repost_info = new BrowserReattachInfo;
1286
repost_info->current_process_id = current_process_id;
1287
repost_info->browser_id = browser_id;
1288
repost_info->known_process_ids = known_process_ids;
1289
::PostMessage(this->m_hWnd,
1290
WD_BROWSER_REATTACH,
1291
NULL,
1292
reinterpret_cast<LPARAM>(repost_info));
1293
}
1294
1295
void IECommandExecutor::GetNewBrowserProcessIds(std::vector<DWORD>* known_process_ids,
1296
std::vector<DWORD>* new_process_ids) {
1297
LOG(TRACE) << "Entering IECommandExecutor::GetNewBrowserProcessIds";
1298
std::vector<DWORD> all_ie_process_ids;
1299
WindowUtilities::GetProcessesByName(L"iexplore.exe", &all_ie_process_ids);
1300
1301
// Maximum size of the new process list is if all IE processes are unknown.
1302
std::vector<DWORD> temp_new_process_ids(all_ie_process_ids.size());
1303
std::sort(known_process_ids->begin(), known_process_ids->end());
1304
std::sort(all_ie_process_ids.begin(), all_ie_process_ids.end());
1305
std::vector<DWORD>::iterator end_iterator = std::set_difference(all_ie_process_ids.begin(),
1306
all_ie_process_ids.end(),
1307
known_process_ids->begin(),
1308
known_process_ids->end(),
1309
temp_new_process_ids.begin());
1310
temp_new_process_ids.resize(end_iterator - temp_new_process_ids.begin());
1311
*new_process_ids = temp_new_process_ids;
1312
}
1313
1314
int IECommandExecutor::GetCurrentBrowser(BrowserHandle* browser_wrapper) const {
1315
LOG(TRACE) << "Entering IECommandExecutor::GetCurrentBrowser";
1316
return this->GetManagedBrowser(this->current_browser_id_, browser_wrapper);
1317
}
1318
1319
int IECommandExecutor::GetManagedBrowser(const std::string& browser_id,
1320
BrowserHandle* browser_wrapper) const {
1321
LOG(TRACE) << "Entering IECommandExecutor::GetManagedBrowser";
1322
1323
if (!this->is_valid()) {
1324
LOG(TRACE) << "Current command executor is not valid";
1325
return ENOSUCHDRIVER;
1326
}
1327
1328
if (browser_id == "") {
1329
LOG(WARN) << "Browser ID requested was an empty string";
1330
return ENOSUCHWINDOW;
1331
}
1332
1333
BrowserMap::const_iterator found_iterator =
1334
this->managed_browsers_.find(browser_id);
1335
if (found_iterator == this->managed_browsers_.end()) {
1336
LOG(WARN) << "Unable to find managed browser with id " << browser_id;
1337
return ENOSUCHWINDOW;
1338
}
1339
1340
*browser_wrapper = found_iterator->second;
1341
return WD_SUCCESS;
1342
}
1343
1344
void IECommandExecutor::GetManagedBrowserHandles(std::vector<std::string>* managed_browser_handles) const {
1345
LOG(TRACE) << "Entering IECommandExecutor::GetManagedBrowserHandles";
1346
1347
BrowserMap::const_iterator it = this->managed_browsers_.begin();
1348
for (; it != this->managed_browsers_.end(); ++it) {
1349
if (it->second->IsValidWindow()) {
1350
managed_browser_handles->push_back(it->first);
1351
}
1352
1353
// Look for browser windows created by showModalDialog().
1354
it->second->GetActiveDialogWindowHandle();
1355
}
1356
}
1357
1358
void IECommandExecutor::AddManagedBrowser(BrowserHandle browser_wrapper) {
1359
LOG(TRACE) << "Entering IECommandExecutor::AddManagedBrowser";
1360
1361
this->managed_browsers_[browser_wrapper->browser_id()] = browser_wrapper;
1362
if (this->current_browser_id_ == "") {
1363
LOG(TRACE) << "Setting current browser id to " << browser_wrapper->browser_id();
1364
this->current_browser_id_ = browser_wrapper->browser_id();
1365
}
1366
}
1367
1368
std::string IECommandExecutor::OpenNewBrowsingContext(const std::string& window_type) {
1369
return this->OpenNewBrowsingContext(window_type, "about:blank");
1370
}
1371
1372
std::string IECommandExecutor::OpenNewBrowsingContext(const std::string& window_type,
1373
const std::string& url) {
1374
LOG(TRACE) << "Entering IECommandExecutor::OpenNewBrowsingContext";
1375
std::wstring target_url = StringUtilities::ToWString(url);
1376
std::string new_browser_id = "";
1377
if (window_type == TAB_WINDOW_TYPE) {
1378
new_browser_id = this->OpenNewBrowserTab(target_url);
1379
} else {
1380
new_browser_id = this->OpenNewBrowserWindow(target_url);
1381
}
1382
1383
BrowserHandle new_window_wrapper;
1384
this->GetManagedBrowser(new_browser_id, &new_window_wrapper);
1385
HWND new_window_handle = new_window_wrapper->GetBrowserWindowHandle();
1386
this->proxy_manager_->SetProxySettings(new_window_handle);
1387
new_window_wrapper->cookie_manager()->Initialize(new_window_handle);
1388
1389
return new_browser_id;
1390
}
1391
1392
std::string IECommandExecutor::OpenNewBrowserWindow(const std::wstring& url) {
1393
LOG(TRACE) << "Entering IECommandExecutor::OpenNewBrowserWindow";
1394
bool is_protected_mode_url = ::IEIsProtectedModeURL(url.c_str()) == S_OK;
1395
if (url.find(L"about:blank") == 0) {
1396
// Special-case URLs starting with about:blank, so that the new window
1397
// is in the same Protected Mode zone as the current window from which
1398
// it's being opened.
1399
BrowserHandle current_browser;
1400
this->GetCurrentBrowser(&current_browser);
1401
is_protected_mode_url = current_browser->IsProtectedMode();
1402
}
1403
CComPtr<IWebBrowser2> browser = this->factory_->CreateBrowser(is_protected_mode_url);
1404
if (browser == NULL) {
1405
// No browser was created, so we have to bail early.
1406
// Check the log for the HRESULT why.
1407
return "";
1408
}
1409
LOG(DEBUG) << "New browser window was opened.";
1410
BrowserHandle new_window_wrapper(new Browser(browser,
1411
NULL,
1412
this->m_hWnd,
1413
this->is_edge_chromium_));
1414
// It is acceptable to set the proxy settings here, as the newly-created
1415
// browser window has not yet been navigated to any page. Only after the
1416
// interface has been marshaled back across the thread boundary to the
1417
// NewWindow3 event handler will the navigation begin, which ensures that
1418
// even the initial navigation will get captured by the proxy, if one is
1419
// set. Likewise, the cookie manager needs to have its window handle
1420
// properly set to a non-NULL value so that windows messages are routed
1421
// to the correct window.
1422
// N.B. DocumentHost::GetBrowserWindowHandle returns the tab window handle
1423
// for IE 7 and above, and the top-level window for IE6. This is the window
1424
// required for setting the proxy settings.
1425
this->AddManagedBrowser(new_window_wrapper);
1426
return new_window_wrapper->browser_id();
1427
}
1428
1429
std::string IECommandExecutor::OpenNewBrowserTab(const std::wstring& url) {
1430
LOG(TRACE) << "Entering IECommandExecutor::OpenNewBrowserTab";
1431
BrowserHandle browser_wrapper;
1432
this->GetCurrentBrowser(&browser_wrapper);
1433
HWND top_level_handle = browser_wrapper->GetTopLevelWindowHandle();
1434
1435
if (this->is_edge_chromium_) {
1436
// This is a hack to account for the case where the currently focused
1437
// WebDriver window is a tab without the visual focus. When an IE Mode
1438
// tab is sent to the background, it is reparented to a different top-
1439
// level window than the Edge window. To detect a new tab being opened,
1440
// we must find a top-level Edge window containing and active IE Mode
1441
// tab, and use that as our parent window. This is a rare case that
1442
// will only happen if the user does not switch WebDriver command focus
1443
// to the new window immediately after opening. The Selenium language
1444
// bindings do this automatically, but non-Selenium client bindings may
1445
// not do so.
1446
// ASSUMPTION: The first top-level Edge window we find containing an IE
1447
// Mode tab is the top-level window we want.
1448
std::string window_class =
1449
WindowUtilities::GetWindowClass(top_level_handle);
1450
if (window_class != "Chrome_WidgetWin_1") {
1451
std::vector<HWND> edge_handles;
1452
::EnumWindows(&BrowserFactory::FindEdgeBrowserHandles,
1453
reinterpret_cast<LPARAM>(&edge_handles));
1454
for (auto& edge_handle : edge_handles) {
1455
std::vector<HWND> ie_mode_handles;
1456
::EnumChildWindows(edge_handle,
1457
&BrowserFactory::FindIEBrowserHandles,
1458
reinterpret_cast<LPARAM>(&ie_mode_handles));
1459
if (ie_mode_handles.size() > 0) {
1460
top_level_handle = edge_handle;
1461
break;
1462
}
1463
}
1464
}
1465
}
1466
1467
std::vector<HWND> original_handles;
1468
::EnumChildWindows(top_level_handle,
1469
&BrowserFactory::FindIEBrowserHandles,
1470
reinterpret_cast<LPARAM>(&original_handles));
1471
std::sort(original_handles.begin(), original_handles.end());
1472
1473
// IWebBrowser2::Navigate2 will open the specified URL in a new tab,
1474
// if requested. The Sleep() call after the navigate is necessary,
1475
// since the IECommandExecutor class doesn't have access to the events
1476
// to indicate the navigation is completed.
1477
CComVariant url_variant = url.c_str();
1478
CComVariant flags = navOpenInNewTab;
1479
browser_wrapper->browser()->Navigate2(&url_variant,
1480
&flags,
1481
NULL,
1482
NULL,
1483
NULL);
1484
::Sleep(500);
1485
1486
HWND new_tab_window = NULL;
1487
std::vector<HWND> new_handles;
1488
::EnumChildWindows(top_level_handle,
1489
&BrowserFactory::FindIEBrowserHandles,
1490
reinterpret_cast<LPARAM>(&new_handles));
1491
clock_t end_time = clock() + 5 * CLOCKS_PER_SEC;
1492
if (browser_wrapper->is_edge_chromium()) {
1493
// It appears that for Chromium-based Edge in IE Mode, there will only
1494
// ever be one active child window of the top-level window with window
1495
// class "Internet Explorer_Server", which is the active tab. Inactive
1496
// tabs are re-parented until brought back to being the active tab.
1497
while ((new_handles.size() == 0 || new_handles[0] == original_handles[0])
1498
&& clock() < end_time) {
1499
if (new_handles.size() != 0) {
1500
new_handles.clear();
1501
}
1502
1503
::Sleep(50);
1504
::EnumChildWindows(top_level_handle,
1505
&BrowserFactory::FindIEBrowserHandles,
1506
reinterpret_cast<LPARAM>(&new_handles));
1507
}
1508
1509
if (new_handles.size() == 0 || new_handles[0] == original_handles[0]) {
1510
LOG(WARN) << "No new window handle found after attempt to open";
1511
return "";
1512
}
1513
1514
new_tab_window = new_handles[0];
1515
} else {
1516
while (new_handles.size() <= original_handles.size() &&
1517
clock() < end_time) {
1518
::Sleep(50);
1519
::EnumChildWindows(top_level_handle,
1520
&BrowserFactory::FindIEBrowserHandles,
1521
reinterpret_cast<LPARAM>(&new_handles));
1522
}
1523
std::sort(new_handles.begin(), new_handles.end());
1524
1525
if (new_handles.size() <= original_handles.size()) {
1526
LOG(WARN) << "No new window handle found after attempt to open";
1527
return "";
1528
}
1529
1530
// We are guaranteed to have at least one HWND difference
1531
// between the two vectors if we reach this point, because
1532
// we know the vectors are different sizes.
1533
std::vector<HWND> diff(new_handles.size());
1534
std::vector<HWND>::iterator it = std::set_difference(new_handles.begin(),
1535
new_handles.end(),
1536
original_handles.begin(),
1537
original_handles.end(),
1538
diff.begin());
1539
diff.resize(it - diff.begin());
1540
if (diff.size() > 1) {
1541
std::string handle_list = "";
1542
std::vector<HWND>::const_iterator it = diff.begin();
1543
for (; it != diff.end(); ++it) {
1544
if (handle_list.size() > 0) {
1545
handle_list.append(", ");
1546
}
1547
handle_list.append(StringUtilities::Format("0x%08x", *it));
1548
}
1549
LOG(DEBUG) << "Found more than one new window handles! Found "
1550
<< diff.size() << "windows [" << handle_list << "]";
1551
}
1552
new_tab_window = diff[0];
1553
}
1554
1555
DWORD process_id;
1556
::GetWindowThreadProcessId(new_tab_window, &process_id);
1557
clock_t end = clock() + (DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS / 1000 * CLOCKS_PER_SEC);
1558
bool is_ready = this->factory_->IsBrowserProcessInitialized(process_id);
1559
while (!is_ready && clock() < end) {
1560
::Sleep(100);
1561
is_ready = this->factory_->IsBrowserProcessInitialized(process_id);
1562
}
1563
1564
ProcessWindowInfo info;
1565
info.dwProcessId = process_id;
1566
info.hwndBrowser = new_tab_window;
1567
info.pBrowser = NULL;
1568
std::string error_message = "";
1569
this->factory_->AttachToBrowser(&info, &error_message);
1570
BrowserHandle new_tab_wrapper(new Browser(info.pBrowser,
1571
NULL,
1572
this->m_hWnd,
1573
this->is_edge_chromium_));
1574
// Force a wait cycle to make sure the browser is finished initializing.
1575
new_tab_wrapper->Wait(NORMAL_PAGE_LOAD_STRATEGY);
1576
this->AddManagedBrowser(new_tab_wrapper);
1577
return new_tab_wrapper->browser_id();
1578
}
1579
1580
int IECommandExecutor::CreateNewBrowser(std::string* error_message) {
1581
LOG(TRACE) << "Entering IECommandExecutor::CreateNewBrowser";
1582
1583
DWORD process_id = this->factory_->LaunchBrowserProcess(error_message);
1584
if (process_id == NULL) {
1585
LOG(WARN) << "Unable to launch browser, received NULL process ID";
1586
this->is_waiting_ = false;
1587
return ENOSUCHDRIVER;
1588
}
1589
1590
ProcessWindowInfo process_window_info;
1591
process_window_info.dwProcessId = process_id;
1592
process_window_info.hwndBrowser = NULL;
1593
process_window_info.pBrowser = NULL;
1594
bool attached = this->factory_->AttachToBrowser(&process_window_info,
1595
error_message);
1596
if (!attached) {
1597
LOG(WARN) << "Unable to attach to browser COM object";
1598
this->is_waiting_ = false;
1599
return ENOSUCHDRIVER;
1600
}
1601
1602
// Set persistent hover functionality in the interactions implementation.
1603
//this->input_manager_->StartPersistentEvents();
1604
LOG(INFO) << "Persistent hovering set to: "
1605
<< this->input_manager_->use_persistent_hover();
1606
1607
this->proxy_manager_->SetProxySettings(process_window_info.hwndBrowser);
1608
BrowserHandle wrapper(new Browser(process_window_info.pBrowser,
1609
process_window_info.hwndBrowser,
1610
this->m_hWnd,
1611
this->is_edge_chromium_));
1612
1613
this->AddManagedBrowser(wrapper);
1614
bool is_busy = wrapper->IsBusy();
1615
if (is_busy) {
1616
LOG(WARN) << "Browser was launched and attached to, but is still busy.";
1617
}
1618
wrapper->SetFocusToBrowser();
1619
this->edge_temp_dir_ = this->factory_->GetEdgeTempDir();
1620
return WD_SUCCESS;
1621
}
1622
1623
int IECommandExecutor::GetManagedElement(const std::string& element_id,
1624
ElementHandle* element_wrapper) const {
1625
LOG(TRACE) << "Entering IECommandExecutor::GetManagedElement";
1626
return this->managed_elements_->GetManagedElement(element_id, element_wrapper);
1627
}
1628
1629
bool IECommandExecutor::AddManagedElement(IHTMLElement* element,
1630
ElementHandle* element_wrapper) {
1631
LOG(TRACE) << "Entering IECommandExecutor::AddManagedElement";
1632
BrowserHandle current_browser;
1633
this->GetCurrentBrowser(&current_browser);
1634
return this->managed_elements_->AddManagedElement(current_browser, element, element_wrapper);
1635
}
1636
1637
void IECommandExecutor::RemoveManagedElement(const std::string& element_id) {
1638
LOG(TRACE) << "Entering IECommandExecutor::RemoveManagedElement";
1639
this->managed_elements_->RemoveManagedElement(element_id);
1640
}
1641
1642
void IECommandExecutor::ListManagedElements() {
1643
LOG(TRACE) << "Entering IECommandExecutor::ListManagedElements";
1644
this->managed_elements_->ListManagedElements();
1645
}
1646
1647
int IECommandExecutor::GetElementFindMethod(const std::string& mechanism,
1648
std::wstring* translation) const {
1649
LOG(TRACE) << "Entering IECommandExecutor::GetElementFindMethod";
1650
1651
ElementFindMethodMap::const_iterator found_iterator =
1652
this->element_find_methods_.find(mechanism);
1653
if (found_iterator == this->element_find_methods_.end()) {
1654
LOG(WARN) << "Unable to determine find method " << mechanism;
1655
return EUNHANDLEDERROR;
1656
}
1657
1658
*translation = found_iterator->second;
1659
return WD_SUCCESS;
1660
}
1661
1662
int IECommandExecutor::LocateElement(const ElementHandle parent_wrapper,
1663
const std::string& mechanism,
1664
const std::string& criteria,
1665
Json::Value* found_element) const {
1666
LOG(TRACE) << "Entering IECommandExecutor::LocateElement";
1667
1668
std::wstring mechanism_translation = L"";
1669
int status_code = this->GetElementFindMethod(mechanism,
1670
&mechanism_translation);
1671
if (status_code != WD_SUCCESS) {
1672
LOG(WARN) << "Unable to determine mechanism translation for " << mechanism;
1673
return status_code;
1674
}
1675
1676
std::wstring wide_criteria = StringUtilities::ToWString(criteria);
1677
return this->element_finder()->FindElement(*this,
1678
parent_wrapper,
1679
mechanism_translation,
1680
wide_criteria,
1681
found_element);
1682
}
1683
1684
int IECommandExecutor::LocateElements(const ElementHandle parent_wrapper,
1685
const std::string& mechanism,
1686
const std::string& criteria,
1687
Json::Value* found_elements) const {
1688
LOG(TRACE) << "Entering IECommandExecutor::LocateElements";
1689
1690
std::wstring mechanism_translation = L"";
1691
int status_code = this->GetElementFindMethod(mechanism,
1692
&mechanism_translation);
1693
if (status_code != WD_SUCCESS) {
1694
LOG(WARN) << "Unable to determine mechanism translation for " << mechanism;
1695
return status_code;
1696
}
1697
1698
std::wstring wide_criteria = StringUtilities::ToWString(criteria);
1699
return this->element_finder()->FindElements(*this,
1700
parent_wrapper,
1701
mechanism_translation,
1702
wide_criteria,
1703
found_elements);
1704
}
1705
1706
void IECommandExecutor::PopulateElementFinderMethods(void) {
1707
LOG(TRACE) << "Entering IECommandExecutor::PopulateElementFinderMethods";
1708
1709
this->element_find_methods_["id"] = L"id";
1710
this->element_find_methods_["name"] = L"name";
1711
this->element_find_methods_["tag name"] = L"tagName";
1712
this->element_find_methods_["link text"] = L"linkText";
1713
this->element_find_methods_["partial link text"] = L"partialLinkText";
1714
this->element_find_methods_["class name"] = L"className";
1715
this->element_find_methods_["xpath"] = L"xpath";
1716
this->element_find_methods_["css selector"] = L"css";
1717
}
1718
1719
} // namespace webdriver
1720
1721