Skip to content

Commit a86d9ae

Browse files
committed
Refactor IE user-initiated JavaScript execution to use JSON objects
This IE driver refactor is internal, but should not change behavior of the driver. It is intended to let the driver's internal script executor use the direct internal JSON objects as arguments for script execution, deferring their conversion into variants until directly before execution.
1 parent d26aeec commit a86d9ae

13 files changed

+557
-62
lines changed

cpp/iedriver/AsyncScriptExecutor.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,22 @@ LRESULT AsyncScriptExecutor::OnSetArgument(UINT uMsg,
105105
this->script_arguments_[this->script_argument_index_] = dispatch_variant;
106106
break;
107107
}
108+
case VT_BSTR: {
109+
CComVariant str_arg(*reinterpret_cast<LPCTSTR*>(lParam));
110+
this->script_arguments_[this->script_argument_index_] = str_arg;
111+
break;
112+
}
113+
case VT_BOOL:
114+
this->script_arguments_[this->script_argument_index_] = static_cast<int>(lParam) != VARIANT_FALSE;
115+
break;
116+
case VT_I4:
117+
case VT_I8:
118+
this->script_arguments_[this->script_argument_index_] = static_cast<long>(lParam);
119+
break;
120+
case VT_R4:
121+
case VT_R8:
122+
this->script_arguments_[this->script_argument_index_] = *reinterpret_cast<double*>(lParam);
123+
break;
108124
default: {
109125
// TODO: Unmarshal arguments of types other than VT_DISPATCH. At present,
110126
// the asynchronous execution of JavaScript is only used for Automation
@@ -130,10 +146,17 @@ LRESULT AsyncScriptExecutor::OnExecuteScript(UINT uMsg,
130146
script_to_execute.AddArgument(this->script_arguments_[index]);
131147
}
132148
this->status_code_ = script_to_execute.Execute();
133-
this->is_execution_completed_ = true;
149+
134150
if (!this->is_listener_attached_) {
135151
::PostMessage(this->m_hWnd, WM_CLOSE, NULL, NULL);
152+
} else {
153+
Script store_result_script(this->script_host_,
154+
"(function() { return function(){ window.document.__webdriver_script_result = arguments[0]; };})();",
155+
1);
156+
store_result_script.AddArgument(script_to_execute.result());
157+
store_result_script.Execute();
136158
}
159+
this->is_execution_completed_ = true;
137160
return 0;
138161
}
139162

cpp/iedriver/CommandHandlers/ExecuteScriptCommandHandler.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,9 @@ void ExecuteScriptCommandHandler::ExecuteInternal(
5656

5757
CComPtr<IHTMLDocument2> doc;
5858
browser_wrapper->GetDocument(&doc);
59-
Script script_wrapper(doc, script_source, json_args.size());
60-
status_code = this->PopulateArgumentArray(executor,
61-
script_wrapper,
62-
json_args);
59+
Script script_wrapper(doc, script_source);
60+
status_code = script_wrapper.AddArguments(executor.window_handle(), json_args);
61+
6362
if (status_code != WD_SUCCESS) {
6463
response->SetErrorResponse(status_code, "Error setting arguments for script");
6564
return;
@@ -72,10 +71,19 @@ void ExecuteScriptCommandHandler::ExecuteInternal(
7271
return;
7372
} else {
7473
Json::Value script_result;
75-
script_wrapper.ConvertResultToJsonValue(executor, &script_result);
74+
script_wrapper.ConvertResultToJsonValue(executor.window_handle(), &script_result);
7675
response->SetSuccessResponse(script_result);
7776
return;
7877
}
78+
79+
//HWND async_executor_handle;
80+
//status_code = script_wrapper.BeginAsyncExecution(&async_executor_handle);
81+
//browser_wrapper->set_script_executor_handle(async_executor_handle);
82+
83+
//if (status_code != WD_SUCCESS) {
84+
// response->SetErrorResponse(status_code, "JavaScript error");
85+
// return;
86+
//}
7987
}
8088
}
8189

cpp/iedriver/DocumentHost.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ DocumentHost::DocumentHost(HWND hwnd, HWND executor_handle) {
5858

5959
this->window_handle_ = hwnd;
6060
this->executor_handle_ = executor_handle;
61+
this->script_executor_handle_ = NULL;
6162
this->is_closing_ = false;
6263
this->wait_required_ = false;
6364
this->focused_frame_window_ = NULL;

cpp/iedriver/DocumentHost.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ class DocumentHost {
8181
bool wait_required(void) const { return this->wait_required_; }
8282
void set_wait_required(const bool value) { this->wait_required_ = value; }
8383

84+
bool script_wait_required(void) const { return this->script_wait_required_; }
85+
void set_script_wait_required(const bool value) { this->script_wait_required_ = value; }
86+
87+
HWND script_executor_handle(void) const { return this->script_executor_handle_; }
88+
void set_script_executor_handle(HWND value) { this->script_executor_handle_ = value; }
89+
8490
bool is_closing(void) const { return this->is_closing_; }
8591

8692
std::string browser_id(void) const { return this->browser_id_; }
@@ -110,8 +116,10 @@ class DocumentHost {
110116
CComPtr<IHTMLWindow2> focused_frame_window_;
111117
HWND window_handle_;
112118
HWND executor_handle_;
119+
HWND script_executor_handle_;
113120
std::string browser_id_;
114121
bool wait_required_;
122+
bool script_wait_required_;
115123
bool is_closing_;
116124
};
117125

cpp/iedriver/IECommandExecutor.cpp

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@
3131
#include "BrowserFactory.h"
3232
#include "CommandExecutor.h"
3333
#include "CommandHandlerRepository.h"
34+
#include "Element.h"
3435
#include "ElementFinder.h"
3536
#include "ElementRepository.h"
3637
#include "IECommandHandler.h"
3738
#include "InputManager.h"
3839
#include "HtmlDialog.h"
3940
#include "ProxyManager.h"
4041
#include "StringUtilities.h"
42+
#include "Script.h"
4143

4244
namespace webdriver {
4345

@@ -350,6 +352,71 @@ LRESULT IECommandExecutor::OnGetQuitStatus(UINT uMsg,
350352
return this->is_quitting_ && this->managed_browsers_.size() > 0 ? 1 : 0;
351353
}
352354

355+
LRESULT IECommandExecutor::OnScriptWait(UINT uMsg,
356+
WPARAM wParam,
357+
LPARAM lParam,
358+
BOOL& bHandled) {
359+
LOG(TRACE) << "Entering IECommandExecutor::OnScriptWait";
360+
361+
BrowserHandle browser;
362+
int status_code = this->GetCurrentBrowser(&browser);
363+
if (status_code == WD_SUCCESS && !browser->is_closing()) {
364+
if (this->async_script_timeout_ >= 0 && this->wait_timeout_ < clock()) {
365+
::SendMessage(browser->script_executor_handle(), WD_ASYNC_SCRIPT_DETACH_LISTENTER, NULL, NULL);
366+
Response timeout_response;
367+
timeout_response.SetErrorResponse(ERROR_SCRIPT_TIMEOUT, "Timed out waiting for script to complete.");
368+
this->serialized_response_ = timeout_response.Serialize();
369+
this->is_waiting_ = false;
370+
browser->set_script_executor_handle(NULL);
371+
} else {
372+
HWND alert_handle;
373+
bool is_execution_finished = ::SendMessage(browser->script_executor_handle(), WD_ASYNC_SCRIPT_IS_EXECUTION_COMPLETE, NULL, NULL) != 0;
374+
bool is_alert_active = this->IsAlertActive(browser, &alert_handle);
375+
this->is_waiting_ = !is_execution_finished && !is_alert_active;
376+
if (this->is_waiting_) {
377+
// If we are still waiting, we need to wait a bit then post a message to
378+
// ourselves to run the wait again. However, we can't wait using Sleep()
379+
// on this thread. This call happens in a message loop, and we would be
380+
// unable to process the COM events in the browser if we put this thread
381+
// to sleep.
382+
unsigned int thread_id = 0;
383+
HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
384+
0,
385+
&IECommandExecutor::ScriptWaitThreadProc,
386+
(void *)this->m_hWnd,
387+
0,
388+
&thread_id));
389+
if (thread_handle != NULL) {
390+
::CloseHandle(thread_handle);
391+
} else {
392+
LOGERR(DEBUG) << "Unable to create waiter thread";
393+
}
394+
} else {
395+
Response response;
396+
::SendMessage(browser->script_executor_handle(), WD_ASYNC_SCRIPT_DETACH_LISTENTER, NULL, NULL);
397+
int status_code = static_cast<int>(::SendMessage(browser->script_executor_handle(), WD_ASYNC_SCRIPT_GET_RESULT, NULL, NULL));
398+
if (status_code != WD_SUCCESS) {
399+
response.SetErrorResponse(status_code, "Error executing JavaScript");
400+
} else {
401+
CComPtr<IHTMLDocument2> doc;
402+
browser->GetDocument(&doc);
403+
Script result_retrieval_script(doc,
404+
"(function() { return function(){ return window.document.__webdriver_script_result; };})();",
405+
0);
406+
status_code = result_retrieval_script.Execute();
407+
Json::Value script_result;
408+
result_retrieval_script.ConvertResultToJsonValue(*this, &script_result);
409+
response.SetSuccessResponse(script_result);
410+
}
411+
this->serialized_response_ = response.Serialize();
412+
}
413+
}
414+
} else {
415+
this->is_waiting_ = false;
416+
}
417+
return 0;
418+
}
419+
353420
LRESULT IECommandExecutor::OnRefreshManagedElements(UINT uMsg,
354421
WPARAM wParam,
355422
LPARAM lParam,
@@ -374,6 +441,39 @@ LRESULT IECommandExecutor::OnHandleUnexpectedAlerts(UINT uMsg,
374441
return 0;
375442
}
376443

444+
LRESULT IECommandExecutor::OnGetManagedElement(UINT uMsg,
445+
WPARAM wParam,
446+
LPARAM lParam,
447+
BOOL& bHandled) {
448+
ElementInfo* info = reinterpret_cast<ElementInfo*>(lParam);
449+
ElementHandle element_handle;
450+
int status_code = this->GetManagedElement(info->element_id, &element_handle);
451+
if (status_code == WD_SUCCESS) {
452+
info->element = element_handle->element();
453+
}
454+
return status_code;
455+
}
456+
457+
LRESULT IECommandExecutor::OnAddManagedElement(UINT uMsg,
458+
WPARAM wParam,
459+
LPARAM lParam,
460+
BOOL& bHandled) {
461+
ElementInfo* info = reinterpret_cast<ElementInfo*>(lParam);
462+
ElementHandle element_handle;
463+
this->AddManagedElement(info->element, &element_handle);
464+
info->element_id = element_handle->element_id();
465+
return WD_SUCCESS;
466+
}
467+
468+
LRESULT IECommandExecutor::OnRemoveManagedElement(UINT uMsg,
469+
WPARAM wParam,
470+
LPARAM lParam,
471+
BOOL& bHandled) {
472+
ElementInfo* info = reinterpret_cast<ElementInfo*>(lParam);
473+
this->RemoveManagedElement(info->element_id);
474+
return WD_SUCCESS;
475+
}
476+
377477
unsigned int WINAPI IECommandExecutor::WaitThreadProc(LPVOID lpParameter) {
378478
LOG(TRACE) << "Entering IECommandExecutor::WaitThreadProc";
379479
HWND window_handle = reinterpret_cast<HWND>(lpParameter);
@@ -382,6 +482,13 @@ unsigned int WINAPI IECommandExecutor::WaitThreadProc(LPVOID lpParameter) {
382482
return 0;
383483
}
384484

485+
unsigned int WINAPI IECommandExecutor::ScriptWaitThreadProc(LPVOID lpParameter) {
486+
LOG(TRACE) << "Entering IECommandExecutor::ScriptWaitThreadProc";
487+
HWND window_handle = reinterpret_cast<HWND>(lpParameter);
488+
::Sleep(WAIT_TIME_IN_MILLISECONDS);
489+
::PostMessage(window_handle, WD_SCRIPT_WAIT, NULL, NULL);
490+
return 0;
491+
}
385492

386493
unsigned int WINAPI IECommandExecutor::ThreadProc(LPVOID lpParameter) {
387494
LOG(TRACE) << "Entering IECommandExecutor::ThreadProc";
@@ -522,6 +629,16 @@ void IECommandExecutor::DispatchCommand() {
522629
this->wait_timeout_ = clock() + (static_cast<int>(this->page_load_timeout_) / 1000 * CLOCKS_PER_SEC);
523630
}
524631
::PostMessage(this->m_hWnd, WD_WAIT, NULL, NULL);
632+
} else {
633+
HWND script_executor_handle = browser->script_executor_handle();
634+
this->is_waiting_ = script_executor_handle != NULL;
635+
if (this->is_waiting_) {
636+
if (this->async_script_timeout_ >= 0) {
637+
this->wait_timeout_ = clock() + (static_cast<int>(this->async_script_timeout_) / 1000 * CLOCKS_PER_SEC);
638+
}
639+
::PostMessage(this->m_hWnd, WD_SCRIPT_WAIT, NULL, NULL);
640+
return;
641+
}
525642
}
526643
} else {
527644
if (this->current_command_.command_type() != webdriver::CommandType::Quit) {
@@ -677,7 +794,37 @@ int IECommandExecutor::CreateNewBrowser(std::string* error_message) {
677794
int IECommandExecutor::GetManagedElement(const std::string& element_id,
678795
ElementHandle* element_wrapper) const {
679796
LOG(TRACE) << "Entering IECommandExecutor::GetManagedElement";
680-
return this->managed_elements_->GetManagedElement(element_id, element_wrapper);
797+
ElementHandle candidate_wrapper;
798+
int result = this->managed_elements_->GetManagedElement(element_id, &candidate_wrapper);
799+
if (result != WD_SUCCESS) {
800+
LOG(WARN) << "Unable to get managed element, element not found";
801+
return result;
802+
} else {
803+
if (!candidate_wrapper->IsAttachedToDom()) {
804+
LOG(WARN) << "Found managed element is no longer valid";
805+
this->managed_elements_->RemoveManagedElement(element_id);
806+
return EOBSOLETEELEMENT;
807+
} else {
808+
// If the element is attached to the DOM, validate that its document
809+
// is the currently-focused document (via frames).
810+
BrowserHandle current_browser;
811+
this->GetCurrentBrowser(&current_browser);
812+
CComPtr<IHTMLDocument2> focused_doc;
813+
current_browser->GetDocument(&focused_doc);
814+
815+
CComPtr<IDispatch> parent_doc_dispatch;
816+
candidate_wrapper->element()->get_document(&parent_doc_dispatch);
817+
818+
if (focused_doc.IsEqualObject(parent_doc_dispatch)) {
819+
*element_wrapper = candidate_wrapper;
820+
return WD_SUCCESS;
821+
} else {
822+
LOG(WARN) << "Found managed element's document is not currently focused";
823+
}
824+
}
825+
}
826+
827+
return EOBSOLETEELEMENT;
681828
}
682829

683830
void IECommandExecutor::AddManagedElement(IHTMLElement* element,

cpp/iedriver/IECommandExecutor.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242

4343
namespace webdriver {
4444

45+
struct ElementInfo {
46+
std::string element_id;
47+
IHTMLElement* element;
48+
};
49+
4550
// Forward declaration of classes.
4651
class BrowserFactory;
4752
class CommandHandlerRepository;
@@ -74,6 +79,10 @@ class IECommandExecutor : public CWindowImpl<IECommandExecutor> {
7479
MESSAGE_HANDLER(WD_REFRESH_MANAGED_ELEMENTS, OnRefreshManagedElements)
7580
MESSAGE_HANDLER(WD_HANDLE_UNEXPECTED_ALERTS, OnHandleUnexpectedAlerts)
7681
MESSAGE_HANDLER(WD_QUIT, OnQuit)
82+
MESSAGE_HANDLER(WD_SCRIPT_WAIT, OnScriptWait)
83+
MESSAGE_HANDLER(WD_GET_MANAGED_ELEMENT, OnGetManagedElement)
84+
MESSAGE_HANDLER(WD_ADD_MANAGED_ELEMENT, OnAddManagedElement)
85+
MESSAGE_HANDLER(WD_REMOVE_MANAGED_ELEMENT, OnRemoveManagedElement)
7786
END_MSG_MAP()
7887

7988
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
@@ -91,11 +100,16 @@ class IECommandExecutor : public CWindowImpl<IECommandExecutor> {
91100
LRESULT OnRefreshManagedElements(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
92101
LRESULT OnHandleUnexpectedAlerts(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
93102
LRESULT OnQuit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
103+
LRESULT OnScriptWait(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
104+
LRESULT OnGetManagedElement(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
105+
LRESULT OnAddManagedElement(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
106+
LRESULT OnRemoveManagedElement(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
94107

95108
std::string session_id(void) const { return this->session_id_; }
96109

97110
static unsigned int WINAPI ThreadProc(LPVOID lpParameter);
98111
static unsigned int WINAPI WaitThreadProc(LPVOID lpParameter);
112+
static unsigned int WINAPI ScriptWaitThreadProc(LPVOID lpParameter);
99113

100114
std::string current_browser_id(void) const {
101115
return this->current_browser_id_;
@@ -130,6 +144,8 @@ class IECommandExecutor : public CWindowImpl<IECommandExecutor> {
130144
const std::string& criteria,
131145
Json::Value* found_elements) const;
132146

147+
HWND window_handle(void) const { return this->m_hWnd; }
148+
133149
unsigned long long implicit_wait_timeout(void) const {
134150
return this->implicit_wait_timeout_;
135151
}

cpp/iedriver/IECommandHandler.cpp

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -43,39 +43,7 @@ int IECommandHandler::GetElement(const IECommandExecutor& executor,
4343
const std::string& element_id,
4444
ElementHandle* element_wrapper) {
4545
LOG(TRACE) << "Entering IECommandHandler::GetElement";
46-
47-
ElementHandle candidate_wrapper;
48-
int result = executor.GetManagedElement(element_id, &candidate_wrapper);
49-
if (result != WD_SUCCESS) {
50-
LOG(WARN) << "Unable to get managed element, element not found";
51-
return ENOSUCHELEMENT;
52-
} else {
53-
if (!candidate_wrapper->IsAttachedToDom()) {
54-
LOG(WARN) << "Found managed element is no longer valid";
55-
IECommandExecutor& mutable_executor = const_cast<IECommandExecutor&>(executor);
56-
mutable_executor.RemoveManagedElement(element_id);
57-
return EOBSOLETEELEMENT;
58-
} else {
59-
// If the element is attached to the DOM, validate that its document
60-
// is the currently-focused document (via frames).
61-
BrowserHandle current_browser;
62-
executor.GetCurrentBrowser(&current_browser);
63-
CComPtr<IHTMLDocument2> focused_doc;
64-
current_browser->GetDocument(&focused_doc);
65-
66-
CComPtr<IDispatch> parent_doc_dispatch;
67-
candidate_wrapper->element()->get_document(&parent_doc_dispatch);
68-
69-
if (focused_doc.IsEqualObject(parent_doc_dispatch)) {
70-
*element_wrapper = candidate_wrapper;
71-
return WD_SUCCESS;
72-
} else {
73-
LOG(WARN) << "Found managed element's document is not currently focused";
74-
}
75-
}
76-
}
77-
78-
return EOBSOLETEELEMENT;
46+
return executor.GetManagedElement(element_id, element_wrapper);
7947
}
8048

8149
Json::Value IECommandHandler::RecreateJsonParameterObject(const ParametersMap& command_parameters) {

0 commit comments

Comments
 (0)