Path: blob/trunk/cpp/iedriver/IECommandExecutor.cpp
2867 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the "License");5// you may not use this file except in compliance with the License.6// You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing, software11// distributed under the License is distributed on an "AS IS" BASIS,12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13// See the License for the specific language governing permissions and14// limitations under the License.1516#include "IECommandExecutor.h"1718#include <algorithm>19#include <ctime>20#include <vector>21#include <mutex>22#include <unordered_set>2324#include <iepmapi.h>2526#include "command_types.h"27#include "errorcodes.h"28#include "logging.h"29#include "response.h"3031#include "Alert.h"32#include "Browser.h"33#include "BrowserFactory.h"34#include "CommandExecutor.h"35#include "CommandHandlerRepository.h"36#include "CookieManager.h"37#include "Element.h"38#include "ElementFinder.h"39#include "ElementRepository.h"40#include "IECommandHandler.h"41#include "InputManager.h"42#include "HtmlDialog.h"43#include "ProxyManager.h"44#include "StringUtilities.h"45#include "Script.h"46#include "WebDriverConstants.h"47#include "WindowUtilities.h"4849#define MAX_HTML_DIALOG_RETRIES 550#define WAIT_TIME_IN_MILLISECONDS 5051#define DEFAULT_SCRIPT_TIMEOUT_IN_MILLISECONDS 3000052#define DEFAULT_PAGE_LOAD_TIMEOUT_IN_MILLISECONDS 30000053#define DEFAULT_FILE_UPLOAD_DIALOG_TIMEOUT_IN_MILLISECONDS 300054#define DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS 100005556namespace webdriver {5758struct WaitThreadContext {59HWND window_handle;60bool is_deferred_command;61LPSTR deferred_response;62};6364struct DelayPostMessageThreadContext {65HWND window_handle;66DWORD delay;67UINT msg;68};6970LRESULT IECommandExecutor::OnCreate(UINT uMsg,71WPARAM wParam,72LPARAM lParam,73BOOL& bHandled) {74LOG(TRACE) << "Entering IECommandExecutor::OnCreate";7576CREATESTRUCT* create = reinterpret_cast<CREATESTRUCT*>(lParam);77IECommandExecutorThreadContext* context = reinterpret_cast<IECommandExecutorThreadContext*>(create->lpCreateParams);78this->port_ = context->port;7980// NOTE: COM should be initialized on this thread, so we81// could use CoCreateGuid() and StringFromGUID2() instead.82UUID guid;83RPC_WSTR guid_string = NULL;84RPC_STATUS status = ::UuidCreate(&guid);85status = ::UuidToString(&guid, &guid_string);8687// RPC_WSTR is currently typedef'd in RpcDce.h (pulled in by rpc.h)88// as unsigned short*. It needs to be typedef'd as wchar_t*89wchar_t* cast_guid_string = reinterpret_cast<wchar_t*>(guid_string);90this->SetWindowText(cast_guid_string);9192std::string session_id = StringUtilities::ToString(cast_guid_string);93this->session_id_ = session_id;94this->is_valid_ = true;9596::RpcStringFree(&guid_string);9798this->PopulateElementFinderMethods();99this->current_browser_id_ = "";100this->serialized_response_ = "";101this->unexpected_alert_behavior_ = "";102this->implicit_wait_timeout_ = 0;103this->async_script_timeout_ = DEFAULT_SCRIPT_TIMEOUT_IN_MILLISECONDS;104this->page_load_timeout_ = DEFAULT_PAGE_LOAD_TIMEOUT_IN_MILLISECONDS;105this->reattach_browser_timeout_ = DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS;106this->is_waiting_ = false;107this->is_quitting_ = false;108this->is_awaiting_new_window_ = false;109this->use_strict_file_interactability_ = false;110this->page_load_strategy_ = "normal";111this->file_upload_dialog_timeout_ = DEFAULT_FILE_UPLOAD_DIALOG_TIMEOUT_IN_MILLISECONDS;112113this->managed_elements_ = new ElementRepository();114this->input_manager_ = new InputManager();115this->proxy_manager_ = new ProxyManager();116this->factory_ = new BrowserFactory();117this->element_finder_ = new ElementFinder();118this->command_handlers_ = new CommandHandlerRepository();119120this->is_edge_chromium_ = false;121this->edge_temp_dir_ = L"";122123return 0;124}125126LRESULT IECommandExecutor::OnDestroy(UINT uMsg,127WPARAM wParam,128LPARAM lParam,129BOOL& bHandled) {130LOG(DEBUG) << "Entering IECommandExecutor::OnDestroy";131132LOG(DEBUG) << "Clearing managed element cache";133this->managed_elements_->Clear();134delete this->managed_elements_;135LOG(DEBUG) << "Closing command handler repository";136delete this->command_handlers_;137LOG(DEBUG) << "Closing element finder";138delete this->element_finder_;139LOG(DEBUG) << "Closing input manager";140delete this->input_manager_;141LOG(DEBUG) << "Closing proxy manager";142delete this->proxy_manager_;143LOG(DEBUG) << "Closing browser factory";144delete this->factory_;145LOG(DEBUG) << "Posting quit message";146::PostQuitMessage(0);147LOG(DEBUG) << "Leaving IECommandExecutor::OnDestroy";148return 0;149}150151LRESULT IECommandExecutor::OnSetCommand(UINT uMsg,152WPARAM wParam,153LPARAM lParam,154BOOL& bHandled) {155LOG(TRACE) << "Entering IECommandExecutor::OnSetCommand";156LRESULT set_command_result = 0;157158LPCSTR json_command = reinterpret_cast<LPCSTR>(lParam);159Command requested_command;160requested_command.Deserialize(json_command);161162this->set_command_mutex_.lock();163if (this->current_command_.command_type() == CommandType::NoCommand ||164requested_command.command_type() == CommandType::Quit) {165this->current_command_.Deserialize(json_command);166set_command_result = 1;167}168this->set_command_mutex_.unlock();169170return set_command_result;171}172173LRESULT IECommandExecutor::OnExecCommand(UINT uMsg,174WPARAM wParam,175LPARAM lParam,176BOOL& bHandled) {177LOG(TRACE) << "Entering IECommandExecutor::OnExecCommand";178179this->DispatchCommand();180return 0;181}182183LRESULT IECommandExecutor::OnGetResponseLength(UINT uMsg,184WPARAM wParam,185LPARAM lParam,186BOOL& bHandled) {187// Not logging trace entering IECommandExecutor::OnGetResponseLength,188// because it is polled repeatedly for a non-zero return value.189size_t response_length = 0;190if (!this->is_waiting_) {191response_length = this->serialized_response_.size();192}193return response_length;194}195196LRESULT IECommandExecutor::OnGetResponse(UINT uMsg,197WPARAM wParam,198LPARAM lParam,199BOOL& bHandled) {200LOG(TRACE) << "Entering IECommandExecutor::OnGetResponse";201202LPSTR str = reinterpret_cast<LPSTR>(lParam);203strcpy_s(str,204this->serialized_response_.size() + 1,205this->serialized_response_.c_str());206207// Reset the serialized response for the next command.208this->serialized_response_ = "";209this->current_command_.Reset();210return 0;211}212213LRESULT IECommandExecutor::OnWait(UINT uMsg,214WPARAM wParam,215LPARAM lParam,216BOOL& bHandled) {217LOG(TRACE) << "Entering IECommandExecutor::OnWait";218219LPCSTR str = reinterpret_cast<LPCSTR>(lParam);220std::string deferred_response(str);221delete[] str;222223LOG(DEBUG) << "Starting wait cycle.";224if (this->is_awaiting_new_window_) {225LOG(DEBUG) << "Awaiting new window. Aborting current wait cycle and "226<< "scheduling another.";227this->CreateWaitThread(deferred_response);228return 0;229}230231bool is_single_wait = (wParam == 0);232233BrowserHandle browser;234int status_code = this->GetCurrentBrowser(&browser);235if (status_code == WD_SUCCESS) {236if (!browser->is_closing()) {237if (this->page_load_timeout_ >= 0 && this->wait_timeout_ < clock()) {238LOG(DEBUG) << "Page load timeout reached. Ending wait cycle.";239Response timeout_response;240timeout_response.SetErrorResponse(ERROR_WEBDRIVER_TIMEOUT,241"Timed out waiting for page to load.");242browser->set_wait_required(false);243this->serialized_response_ = timeout_response.Serialize();244this->is_waiting_ = false;245return 0;246} else {247LOG(DEBUG) << "Beginning wait.";248this->is_waiting_ = !(browser->Wait(this->page_load_strategy_));249if (is_single_wait) {250LOG(DEBUG) << "Single requested wait with no deferred "251<< "response complete. Ending wait cycle.";252this->is_waiting_ = false;253return 0;254} else {255if (this->is_waiting_) {256LOG(DEBUG) << "Wait not complete. Scheduling another wait cycle.";257this->CreateWaitThread(deferred_response);258return 0;259}260}261}262}263}264LOG(DEBUG) << "Wait complete. Setting serialized response to deferred value "265<< deferred_response;266this->serialized_response_ = deferred_response;267this->is_waiting_ = false;268return 0;269}270271LRESULT IECommandExecutor::OnBeforeNewWindow(UINT uMsg,272WPARAM wParam,273LPARAM lParam,274BOOL& bHandled) {275LOG(TRACE) << "Entering IECommandExecutor::OnBeforeNewWindow";276LOG(DEBUG) << "Setting await new window flag";277this->is_awaiting_new_window_ = true;278return 0;279}280281LRESULT IECommandExecutor::OnAfterNewWindow(UINT uMsg,282WPARAM wParam,283LPARAM lParam,284BOOL& bHandled) {285LOG(TRACE) << "Entering IECommandExecutor::OnAfterNewWindow";286if (wParam > 0) {287LOG(DEBUG) << "Creating thread and reposting message.";288this->CreateDelayPostMessageThread(static_cast<DWORD>(wParam),289this->m_hWnd,290WD_AFTER_NEW_WINDOW);291if (lParam > 0) {292// a new window is created from Edge in IEMode293BrowserHandle browser_wrapper;294this->GetCurrentBrowser(&browser_wrapper);295HWND top_level_handle = browser_wrapper->GetTopLevelWindowHandle();296297std::vector<HWND>* current_window_handles =298reinterpret_cast<std::vector<HWND>*>(lParam);299std::unordered_set<HWND> current_window_set(300current_window_handles->begin(),301current_window_handles->end());302delete current_window_handles;303304// sleep 0.5s then get current window handles305clock_t end = clock() + (DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS / 1000 * CLOCKS_PER_SEC);306std::vector<HWND> diff;307int loop_count = 0;308bool is_processing_wm = false;309::Sleep(1000);310while (diff.size() == 0 && clock() < end) {311std::vector<HWND> edge_window_handles;312::EnumWindows(&BrowserFactory::FindEdgeBrowserHandles,313reinterpret_cast<LPARAM>(&edge_window_handles));314315std::vector<HWND> new_ie_window_handles;316for (auto& edge_window_handle : edge_window_handles) {317std::vector<HWND> child_window_handles;318::EnumChildWindows(edge_window_handle,319&BrowserFactory::FindIEBrowserHandles,320reinterpret_cast<LPARAM>(&child_window_handles));321322for (auto& child_window_handle : child_window_handles) {323new_ie_window_handles.push_back(child_window_handle);324}325}326327for (auto& window_handle : new_ie_window_handles) {328if (current_window_set.find(window_handle) != current_window_set.end()) {329continue;330}331diff.push_back(window_handle);332}333334if (diff.size() == 0) {335MSG msg;336if (loop_count >= 1 && !is_processing_wm &&::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_REMOVE)) {337::TranslateMessage(&msg);338::DispatchMessage(&msg);339is_processing_wm = true;340LOG(TRACE) << "process WM_USER";341::Sleep(500);342}343::Sleep(500);344}345loop_count++;346}347348if (diff.size() == 0) {349LOG(WARN) << "No new window handle found after attempt to open";350} else {351for (int i = diff.size() - 1; i >= 0; i--) {352HWND new_window_window = diff[i];353354DWORD process_id = 0;355::GetWindowThreadProcessId(new_window_window, &process_id);356if (process_id) {357clock_t end = clock() + (DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS / 1000 * CLOCKS_PER_SEC);358bool is_ready = this->factory_->IsBrowserProcessInitialized(process_id);359while (!is_ready && clock() < end) {360::Sleep(100);361is_ready = this->factory_->IsBrowserProcessInitialized(process_id);362}363364ProcessWindowInfo info;365info.dwProcessId = process_id;366info.hwndBrowser = new_window_window;367info.pBrowser = NULL;368std::string error_message = "";369bool attach_flag = this->factory_->AttachToBrowser(&info, &error_message);370if (attach_flag) {371BrowserHandle new_window_wrapper(new Browser(info.pBrowser,372NULL,373this->m_hWnd,374this->is_edge_chromium_));375376// Force a wait cycle to make sure the browser is finished initializing.377new_window_wrapper->Wait(NORMAL_PAGE_LOAD_STRATEGY);378this->AddManagedBrowser(new_window_wrapper);379}380} else {381LOG(TRACE) << "invalid window " << new_window_window;382}383}384}385}386} else {387LOG(DEBUG) << "Clearing await new window flag";388this->is_awaiting_new_window_ = false;389}390return 0;391}392393LRESULT IECommandExecutor::OnBrowserNewWindow(UINT uMsg,394WPARAM wParam,395LPARAM lParam,396BOOL& bHandled) {397LOG(TRACE) << "Entering IECommandExecutor::OnBrowserNewWindow";398NewWindowInfo* info = reinterpret_cast<NewWindowInfo*>(lParam);399std::string target_url = info->target_url;400std::string new_browser_id = this->OpenNewBrowsingContext(WINDOW_WINDOW_TYPE,401target_url);402BrowserHandle new_window_wrapper;403this->GetManagedBrowser(new_browser_id, &new_window_wrapper);404if (new_window_wrapper->IsCrossZoneUrl(target_url)) {405new_window_wrapper->InitiateBrowserReattach();406}407408LOG(DEBUG) << "Attempting to marshal interface pointer to requesting thread.";409IWebBrowser2* browser = new_window_wrapper->browser();410HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(IID_IWebBrowser2,411browser,412&(info->browser_stream));413if (FAILED(hr)) {414LOGHR(WARN, hr) << "Marshalling of interface pointer b/w threads is failed.";415}416417return 0;418}419420LRESULT IECommandExecutor::OnBrowserCloseWait(UINT uMsg,421WPARAM wParam,422LPARAM lParam,423BOOL& bHandled) {424LOG(TRACE) << "Entering IECommandExecutor::OnBrowserCloseWait";425426LPCSTR str = reinterpret_cast<LPCSTR>(lParam);427std::string browser_id(str);428delete[] str;429BrowserMap::iterator found_iterator = this->managed_browsers_.find(browser_id);430if (found_iterator != this->managed_browsers_.end()) {431HWND alert_handle;432bool is_alert_active = this->IsAlertActive(found_iterator->second,433&alert_handle);434if (is_alert_active) {435// If there's an alert window active, the browser's Quit event does436// not fire until any alerts are handled. Note that OnBeforeUnload437// alerts must be handled here; the driver contains the ability to438// handle other standard alerts on the next received command. We rely439// on the browser's Quit command to remove the driver from the list of440// managed browsers.441Alert dialog(found_iterator->second, alert_handle);442if (!dialog.is_standard_alert()) {443dialog.Accept();444is_alert_active = false;445}446}447if (!is_alert_active) {448::Sleep(100);449// If no alert is present, repost the message to the message pump, so450// that we can wait until the browser is fully closed to return the451// proper still-open list of window handles.452LPSTR message_payload = new CHAR[browser_id.size() + 1];453strcpy_s(message_payload, browser_id.size() + 1, browser_id.c_str());454::PostMessage(this->m_hWnd,455WD_BROWSER_CLOSE_WAIT,456NULL,457reinterpret_cast<LPARAM>(message_payload));458return 0;459}460} else {461LOG(WARN) << "Unable to find browser to quit with ID " << browser_id;462}463Json::Value handles(Json::arrayValue);464std::vector<std::string> handle_list;465this->GetManagedBrowserHandles(&handle_list);466std::vector<std::string>::const_iterator handle_iterator = handle_list.begin();467for (; handle_iterator != handle_list.end(); ++handle_iterator) {468handles.append(*handle_iterator);469}470471Response close_window_response;472close_window_response.SetSuccessResponse(handles);473this->serialized_response_ = close_window_response.Serialize();474this->is_waiting_ = false;475return 0;476}477478LRESULT IECommandExecutor::OnSessionQuitWait(UINT uMsg,479WPARAM wParam,480LPARAM lParam,481BOOL& bHandled) {482LOG(TRACE) << "Entering IECommandExecutor::OnAllBrowserCloseWait";483if (this->managed_browsers_.size() > 0) {484LOG(TRACE) << "Still have " << this->managed_browsers_.size() << " browsers";485BrowserMap::const_iterator it = managed_browsers_.begin();486for (; it != managed_browsers_.end(); ++it) {487LOG(TRACE) << "Still awaiting close of browser with ID " << it->first;488HWND alert_handle;489bool is_alert_active = this->IsAlertActive(it->second,490&alert_handle);491if (is_alert_active) {492// If there's an alert window active, the browser's Quit event does493// not fire until any alerts are handled. Note that OnBeforeUnload494// alerts must be handled here; the driver contains the ability to495// handle other standard alerts on the next received command. We rely496// on the browser's Quit command to remove the driver from the list of497// managed browsers.498Alert dialog(it->second, alert_handle);499if (!dialog.is_standard_alert()) {500dialog.Accept();501is_alert_active = false;502}503}504}505::Sleep(WAIT_TIME_IN_MILLISECONDS);506::PostMessage(this->m_hWnd,507WD_SESSION_QUIT_WAIT,508NULL,509NULL);510} else {511for (auto& chromium_window_handle : this->chromium_window_handles_) {512::PostMessage(chromium_window_handle, WM_CLOSE, NULL, NULL);513}514this->is_waiting_ = false;515Response quit_response;516quit_response.SetSuccessResponse(Json::Value::null);517this->serialized_response_ = quit_response.Serialize();518}519return 0;520}521522LRESULT IECommandExecutor::OnAddChromiumWindowHandle(UINT uMsg,523WPARAM wParam,524LPARAM lParam,525BOOL& bHandled) {526if (wParam != NULL) {527this->chromium_window_handles_.emplace(reinterpret_cast<HWND>(wParam));528}529return 0;530}531532LRESULT IECommandExecutor::OnBrowserQuit(UINT uMsg,533WPARAM wParam,534LPARAM lParam,535BOOL& bHandled) {536LOG(TRACE) << "Entering IECommandExecutor::OnBrowserQuit";537538LPCSTR str = reinterpret_cast<LPCSTR>(lParam);539std::string browser_id(str);540delete[] str;541LOG(TRACE) << "Removing browser with ID " << browser_id;542BrowserMap::iterator found_iterator =543this->managed_browsers_.find(browser_id);544545if (found_iterator != this->managed_browsers_.end()) {546this->managed_browsers_.erase(browser_id);547if (this->managed_browsers_.size() == 0) {548this->current_browser_id_ = "";549}550LOG(TRACE) << "Successfully removed browser with ID " << browser_id;551} else {552LOG(WARN) << "Unable to find browser to quit with ID " << browser_id;553}554555return 0;556}557558LRESULT IECommandExecutor::OnBeforeBrowserReattach(UINT uMsg,559WPARAM wParam,560LPARAM lParam,561BOOL& bHandled) {562LOG(TRACE) << "Entering IECommandExecutor::OnBeforeBrowserReattach";563if (this->factory_->ignore_protected_mode_settings()) {564this->reattach_wait_timeout_ = clock() + (static_cast<int>(this->reattach_browser_timeout_) / 1000 * CLOCKS_PER_SEC);565}566return 0;567}568569LRESULT IECommandExecutor::OnBrowserReattach(UINT uMsg,570WPARAM wParam,571LPARAM lParam,572BOOL& bHandled) {573LOG(TRACE) << "Entering IECommandExecutor::OnBrowserReattach";574BrowserReattachInfo* info = reinterpret_cast<BrowserReattachInfo*>(lParam);575DWORD current_process_id = info->current_process_id;576std::string browser_id = info->browser_id;577std::vector<DWORD> known_process_ids = info->known_process_ids;578delete info;579580if (!this->factory_->ignore_protected_mode_settings()) {581return 0;582}583584if (this->reattach_wait_timeout_ < clock()) {585LOG(WARN) << "Reattach attempt has timed out";586return 0;587}588589LOG(DEBUG) << "Starting browser reattach process";590591std::vector<DWORD> new_process_ids;592this->GetNewBrowserProcessIds(&known_process_ids, &new_process_ids);593if (new_process_ids.size() == 0) {594LOG(DEBUG) << "No new process found, rescheduling reattach";595// If no new process IDs were found yet, repost the message596this->PostBrowserReattachMessage(current_process_id,597browser_id,598known_process_ids);599}600if (new_process_ids.size() > 1) {601LOG(WARN) << "Found more than one new iexplore.exe process. It is "602<< "impossible to know which is the proper one. Choosing one "603<< "at random.";604}605606DWORD new_process_id = new_process_ids[0];607if (!this->factory_->IsBrowserProcessInitialized(new_process_id)) {608// If the browser for the new process ID is not yet ready,609// repost the message610LOG(DEBUG) << "Browser process " << new_process_id611<< " not initialized, rescheduling reattach";612this->PostBrowserReattachMessage(current_process_id,613browser_id,614known_process_ids);615return 0;616}617618std::string error_message = "";619ProcessWindowInfo process_window_info;620process_window_info.dwProcessId = new_process_id;621process_window_info.hwndBrowser = NULL;622process_window_info.pBrowser = NULL;623bool attached = this->factory_->AttachToBrowser(&process_window_info, &error_message);624625BrowserMap::iterator found_iterator = this->managed_browsers_.find(browser_id);626if (found_iterator != this->managed_browsers_.end()) {627this->proxy_manager_->SetProxySettings(process_window_info.hwndBrowser);628found_iterator->second->cookie_manager()->Initialize(process_window_info.hwndBrowser);629found_iterator->second->ReattachBrowser(process_window_info.pBrowser);630} else {631LOG(WARN) << "The managed browser was not found to reattach to.";632}633return 0;634}635636LRESULT IECommandExecutor::OnIsSessionValid(UINT uMsg,637WPARAM wParam,638LPARAM lParam,639BOOL& bHandled) {640LOG(TRACE) << "Entering IECommandExecutor::OnIsSessionValid";641642return this->is_valid_ ? 1 : 0;643}644645LRESULT IECommandExecutor::OnNewHtmlDialog(UINT uMsg,646WPARAM wParam,647LPARAM lParam,648BOOL& bHandles) {649LOG(TRACE) << "Entering IECommandExecutor::OnNewHtmlDialog";650651HWND dialog_handle = reinterpret_cast<HWND>(lParam);652BrowserMap::const_iterator it = this->managed_browsers_.begin();653for (; it != this->managed_browsers_.end(); ++it) {654if (dialog_handle == it->second->window_handle()) {655LOG(DEBUG) << "Dialog is equal to one managed browser";656return 0;657}658}659660int retry_count = 0;661CComPtr<IHTMLDocument2> document;662bool found_document = this->factory_->GetDocumentFromWindowHandle(dialog_handle, &document);663while (found_document && retry_count < MAX_HTML_DIALOG_RETRIES) {664CComPtr<IHTMLWindow2> window;665HRESULT hr = document->get_parentWindow(&window);666if (FAILED(hr)) {667// Getting the parent window of the dialog's document failed. This668// usually means that the document changed out from under us before we669// could get the window reference. The canonical case for this is a670// redirect using JavaScript. Sleep for a short time, then retry to671// obtain the reference.672LOGHR(DEBUG, hr) << "IHTMLDocument2::get_parentWindow failed. Retrying.";673::Sleep(100);674document.Release();675found_document = this->factory_->GetDocumentFromWindowHandle(dialog_handle, &document);676++retry_count;677} else {678this->AddManagedBrowser(BrowserHandle(new HtmlDialog(window,679dialog_handle,680this->m_hWnd)));681return 0;682}683}684if (found_document) {685LOG(WARN) << "Got document from dialog, but could not get window";686} else {687LOG(WARN) << "Unable to get document from dialog";688}689return 0;690}691692LRESULT IECommandExecutor::OnQuit(UINT uMsg,693WPARAM wParam,694LPARAM lParam,695BOOL& bHandled) {696697// Delete IEDriver temporary folder when IEDriver drvies Edge in IEMode.698// Note that the this->factory_ object might have been deleted.699if (this->edge_temp_dir_ != L"") {700for (int i = 0; i < 100; i++) {701// wait for the Edge browser completing read/write work702// the delete usually completes in 1 retries703::Sleep(100);704if (BrowserFactory::DeleteDirectory(edge_temp_dir_)) {705// directory delete failed when some files/folders are locked706LOG(TRACE) << "Failed to delete Edge temporary user data directory "707<< LOGWSTRING(edge_temp_dir_) << ", retrying "708<< i + 1 << "...";709}710else {711// the temporary folder has been deleted712LOG(TRACE) << "Deleted Edge temporary user data directory "713<< LOGWSTRING(edge_temp_dir_) << ".";714break;715}716}717this->edge_temp_dir_ = L"";718}719return 0;720}721722LRESULT IECommandExecutor::OnGetQuitStatus(UINT uMsg,723WPARAM wParam,724LPARAM lParam,725BOOL& bHandled) {726return this->is_quitting_ && this->managed_browsers_.size() > 0 ? 1 : 0;727}728729LRESULT IECommandExecutor::OnScriptWait(UINT uMsg,730WPARAM wParam,731LPARAM lParam,732BOOL& bHandled) {733LOG(TRACE) << "Entering IECommandExecutor::OnScriptWait";734735BrowserHandle browser;736int status_code = this->GetCurrentBrowser(&browser);737if (status_code == WD_SUCCESS && !browser->is_closing()) {738if (this->async_script_timeout_ >= 0 && this->wait_timeout_ < clock()) {739::SendMessage(browser->script_executor_handle(),740WD_ASYNC_SCRIPT_DETACH_LISTENTER,741NULL,742NULL);743Response timeout_response;744timeout_response.SetErrorResponse(ERROR_SCRIPT_TIMEOUT,745"Timed out waiting for script to complete.");746this->serialized_response_ = timeout_response.Serialize();747this->is_waiting_ = false;748browser->set_script_executor_handle(NULL);749} else {750HWND alert_handle;751bool is_execution_finished = ::SendMessage(browser->script_executor_handle(),752WD_ASYNC_SCRIPT_IS_EXECUTION_COMPLETE,753NULL,754NULL) != 0;755bool is_alert_active = this->IsAlertActive(browser, &alert_handle);756this->is_waiting_ = !is_execution_finished && !is_alert_active;757if (this->is_waiting_) {758// If we are still waiting, we need to wait a bit then post a message to759// ourselves to run the wait again. However, we can't wait using Sleep()760// on this thread. This call happens in a message loop, and we would be761// unable to process the COM events in the browser if we put this thread762// to sleep.763unsigned int thread_id = 0;764HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,7650,766&IECommandExecutor::ScriptWaitThreadProc,767(void *)this->m_hWnd,7680,769&thread_id));770if (thread_handle != NULL) {771::CloseHandle(thread_handle);772} else {773LOGERR(DEBUG) << "Unable to create waiter thread";774}775} else {776Response response;777Json::Value script_result;778::SendMessage(browser->script_executor_handle(),779WD_ASYNC_SCRIPT_DETACH_LISTENTER,780NULL,781NULL);782int status_code = static_cast<int>(::SendMessage(browser->script_executor_handle(),783WD_ASYNC_SCRIPT_GET_RESULT,784NULL,785reinterpret_cast<LPARAM>(&script_result)));786if (status_code != WD_SUCCESS) {787std::string error_message = "Error executing JavaScript";788if (script_result.isString()) {789error_message = script_result.asString();790}791response.SetErrorResponse(status_code, error_message);792} else {793response.SetSuccessResponse(script_result);794}795::SendMessage(browser->script_executor_handle(), WM_CLOSE, NULL, NULL);796browser->set_script_executor_handle(NULL);797this->serialized_response_ = response.Serialize();798}799}800} else {801this->is_waiting_ = false;802}803return 0;804}805806LRESULT IECommandExecutor::OnRefreshManagedElements(UINT uMsg,807WPARAM wParam,808LPARAM lParam,809BOOL& bHandled) {810this->managed_elements_->ClearCache();811return 0;812}813814LRESULT IECommandExecutor::OnHandleUnexpectedAlerts(UINT uMsg,815WPARAM wParam,816LPARAM lParam,817BOOL& bHandled) {818LOG(TRACE) << "Entering IECommandExecutor::OnHandleUnexpectedAlerts";819BrowserMap::const_iterator it = this->managed_browsers_.begin();820for (; it != this->managed_browsers_.end(); ++it) {821HWND alert_handle = it->second->GetActiveDialogWindowHandle();822if (alert_handle != NULL) {823std::string alert_text;824this->HandleUnexpectedAlert(it->second, alert_handle, true, &alert_text);825}826}827return 0;828}829830LRESULT IECommandExecutor::OnTransferManagedElement(UINT uMsg,831WPARAM wParam,832LPARAM lParam,833BOOL& bHandled) {834LOG(TRACE) << "Entering IECommandExecutor::OnTransferManagedElement";835ElementInfo* info = reinterpret_cast<ElementInfo*>(lParam);836std::string element_id = info->element_id;837LPSTREAM element_stream = info->element_stream;838BrowserHandle browser_handle;839this->GetCurrentBrowser(&browser_handle);840CComPtr<IHTMLElement> element;841::CoGetInterfaceAndReleaseStream(element_stream,842IID_IHTMLElement,843reinterpret_cast<void**>(&element));844delete info;845ElementHandle element_handle;846this->managed_elements_->AddManagedElement(browser_handle,847element,848&element_handle);849RemappedElementInfo* return_info = new RemappedElementInfo;850return_info->original_element_id = element_id;851return_info->element_id = element_handle->element_id();852::PostMessage(browser_handle->script_executor_handle(),853WD_ASYNC_SCRIPT_NOTIFY_ELEMENT_TRANSFERRED,854NULL,855reinterpret_cast<LPARAM>(return_info));856return WD_SUCCESS;857}858859LRESULT IECommandExecutor::OnScheduleRemoveManagedElement(UINT uMsg,860WPARAM wParam,861LPARAM lParam,862BOOL& bHandled) {863LOG(TRACE) << "Entering IECommandExecutor::OnScheduleRemoveManagedElement";864ElementInfo* info = reinterpret_cast<ElementInfo*>(lParam);865std::string element_id = info->element_id;866delete info;867this->RemoveManagedElement(element_id);868return WD_SUCCESS;869}870871unsigned int WINAPI IECommandExecutor::WaitThreadProc(LPVOID lpParameter) {872LOG(TRACE) << "Entering IECommandExecutor::WaitThreadProc";873WaitThreadContext* thread_context = reinterpret_cast<WaitThreadContext*>(lpParameter);874HWND window_handle = thread_context->window_handle;875bool is_deferred_command = thread_context->is_deferred_command;876std::string deferred_response(thread_context->deferred_response);877delete thread_context->deferred_response;878delete thread_context;879880LPSTR message_payload = new CHAR[deferred_response.size() + 1];881strcpy_s(message_payload,882deferred_response.size() + 1,883deferred_response.c_str());884885::Sleep(WAIT_TIME_IN_MILLISECONDS);886::PostMessage(window_handle,887WD_WAIT,888static_cast<WPARAM>(deferred_response.size()),889reinterpret_cast<LPARAM>(message_payload));890if (is_deferred_command) {891// This wait is requested by the automatic handling of a user prompt892// before a command was executed, so re-queue the command execution893// for after the wait.894::PostMessage(window_handle, WD_EXEC_COMMAND, NULL, NULL);895}896return 0;897}898899unsigned int WINAPI IECommandExecutor::ScriptWaitThreadProc(LPVOID lpParameter) {900LOG(TRACE) << "Entering IECommandExecutor::ScriptWaitThreadProc";901HWND window_handle = reinterpret_cast<HWND>(lpParameter);902::Sleep(SCRIPT_WAIT_TIME_IN_MILLISECONDS);903::PostMessage(window_handle, WD_SCRIPT_WAIT, NULL, NULL);904return 0;905}906907unsigned int WINAPI IECommandExecutor::DelayPostMessageThreadProc(LPVOID lpParameter) {908LOG(TRACE) << "Entering IECommandExecutor::DelayPostMessageThreadProc";909DelayPostMessageThreadContext* context = reinterpret_cast<DelayPostMessageThreadContext*>(lpParameter);910HWND window_handle = context->window_handle;911DWORD sleep_time = context->delay;912UINT message_to_post = context->msg;913delete context;914915::Sleep(sleep_time);916::PostMessage(window_handle, message_to_post, NULL, NULL);917return 0;918}919920unsigned int WINAPI IECommandExecutor::ThreadProc(LPVOID lpParameter) {921LOG(TRACE) << "Entering IECommandExecutor::ThreadProc";922923IECommandExecutorThreadContext* thread_context = reinterpret_cast<IECommandExecutorThreadContext*>(lpParameter);924HWND window_handle = thread_context->hwnd;925926// it is better to use IECommandExecutorSessionContext instead927// but use ThreadContext for code minimization928IECommandExecutorThreadContext* session_context = new IECommandExecutorThreadContext();929session_context->port = thread_context->port;930931DWORD error = 0;932HRESULT hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);933if (FAILED(hr)) {934LOGHR(DEBUG, hr) << "COM library initialization encountered an error";935}936937IECommandExecutor new_session;938HWND session_window_handle = new_session.Create(/*HWND*/ HWND_MESSAGE,939/*_U_RECT rect*/ CWindow::rcDefault,940/*LPCTSTR szWindowName*/ NULL,941/*DWORD dwStyle*/ NULL,942/*DWORD dwExStyle*/ NULL,943/*_U_MENUorID MenuOrID*/ 0U,944/*LPVOID lpCreateParam*/ reinterpret_cast<LPVOID*>(session_context));945if (session_window_handle == NULL) {946LOGERR(WARN) << "Unable to create new IECommandExecutor session";947}948949MSG msg;950::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);951952// Return the HWND back through lpParameter, and signal that the953// window is ready for messages.954thread_context->hwnd = session_window_handle;955HANDLE event_handle = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, WEBDRIVER_START_EVENT_NAME);956if (event_handle != NULL) {957::SetEvent(event_handle);958::CloseHandle(event_handle);959} else {960LOGERR(DEBUG) << "Unable to signal that window is ready";961}962963// Run the message loop964BOOL get_message_return_value;965while ((get_message_return_value = ::GetMessage(&msg, NULL, 0, 0)) != 0) {966if (get_message_return_value == -1) {967LOGERR(WARN) << "Windows GetMessage() API returned error";968break;969} else {970if (msg.message == WD_SHUTDOWN) {971LOG(DEBUG) << "Shutdown message received";972new_session.DestroyWindow();973LOG(DEBUG) << "Returned from DestroyWindow()";974break;975} else {976// We need to lock this mutex here to make sure only one thread is processing977// win32 messages at a time.978static std::mutex messageLock;979std::lock_guard<std::mutex> lock(messageLock);980::TranslateMessage(&msg);981::DispatchMessage(&msg);982}983}984}985986LOG(DEBUG) << "Exited IECommandExecutor thread message loop";987::CoUninitialize();988delete session_context;989return 0;990}991992void IECommandExecutor::DispatchCommand() {993LOG(TRACE) << "Entering IECommandExecutor::DispatchCommand";994995Response response;996997if (!this->command_handlers_->IsValidCommand(this->current_command_.command_type())) {998LOG(WARN) << "Unable to find command handler for " << this->current_command_.command_type();999response.SetErrorResponse(ERROR_UNKNOWN_COMMAND, "Command not implemented");1000} else if (!this->current_command_.is_valid_parameters()) {1001response.SetErrorResponse(ERROR_INVALID_ARGUMENT, "parameters property of command is not a valid JSON object");1002} else {1003BrowserHandle browser;1004int status_code = WD_SUCCESS;1005std::string command_type = this->current_command_.command_type();1006if (command_type != webdriver::CommandType::NewSession) {1007// There should never be a modal dialog or alert to check for if the command1008// is the "newSession" command.1009status_code = this->GetCurrentBrowser(&browser);1010if (status_code == WD_SUCCESS) {1011LOG(DEBUG) << "Checking for alert before executing " << command_type << " command";1012HWND alert_handle = NULL;1013bool alert_is_active = this->IsAlertActive(browser, &alert_handle);1014if (alert_is_active) {1015if (this->IsCommandValidWithAlertPresent()) {1016LOG(DEBUG) << "Alert is detected, and the sent command is valid";1017} else {1018LOG(DEBUG) << "Unexpected alert is detected, and the sent command "1019<< "is invalid when an alert is present";1020bool is_quit_command = command_type == webdriver::CommandType::Quit;10211022std::string alert_text;1023bool is_notify_unexpected_alert = this->HandleUnexpectedAlert(browser,1024alert_handle,1025is_quit_command,1026&alert_text);1027if (!is_quit_command) {1028if (is_notify_unexpected_alert) {1029// To keep pace with what the specification suggests, we'll1030// return the text of the alert in the error response.1031response.SetErrorResponse(EUNEXPECTEDALERTOPEN,1032"Modal dialog present with text: " + alert_text);1033response.AddAdditionalData("text", alert_text);1034this->serialized_response_ = response.Serialize();1035return;1036} else {1037LOG(DEBUG) << "Command other than quit was issued, and option "1038<< "to not notify was specified. Continuing with "1039<< "command after automatically closing alert.";1040// Push a wait cycle, then re-execute the current command (which1041// hasn't actually been executed yet). Note that an empty string1042// for the deferred response parameter of CreateWaitThread will1043// re-queue the execution of the command.1044this->CreateWaitThread("", true);1045return;1046}1047} else {1048LOG(DEBUG) << "Quit command was issued. Continuing with "1049<< "command after automatically closing alert.";1050}1051}1052}1053} else {1054LOG(WARN) << "Unable to find current browser";1055}1056}10571058LOG(DEBUG) << "Executing command: " << command_type;1059CommandHandlerHandle command_handler = this->command_handlers_->GetCommandHandler(command_type);1060command_handler->Execute(*this, this->current_command_, &response);1061LOG(DEBUG) << "Command execution for " << command_type << " complete";10621063status_code = this->GetCurrentBrowser(&browser);1064if (status_code == WD_SUCCESS) {1065if (browser->is_closing() && !this->is_quitting_) {1066// Case 1: The browser window is closing, but not via the Quit command,1067// so the executor must wait for the browser window to be closed and1068// removed from the list of managed browser windows.1069LOG(DEBUG) << "Browser is closing; awaiting close.";1070LPSTR message_payload = new CHAR[browser->browser_id().size() + 1];1071strcpy_s(message_payload,1072browser->browser_id().size() + 1,1073browser->browser_id().c_str());10741075this->is_waiting_ = true;1076::Sleep(WAIT_TIME_IN_MILLISECONDS);1077::PostMessage(this->m_hWnd,1078WD_BROWSER_CLOSE_WAIT,1079NULL,1080reinterpret_cast<LPARAM>(message_payload));1081return;1082} else if (browser->script_executor_handle() != NULL) {1083// Case 2: There is a pending asynchronous JavaScript execution in1084// progress, so the executor must wait for the script to complete1085// or a timeout.1086this->is_waiting_ = true;1087if (this->async_script_timeout_ >= 0) {1088this->wait_timeout_ = clock() + (static_cast<int>(this->async_script_timeout_) / 1000 * CLOCKS_PER_SEC);1089}1090LOG(DEBUG) << "Awaiting completion of in-progress asynchronous JavaScript execution.";1091::PostMessage(this->m_hWnd, WD_SCRIPT_WAIT, NULL, NULL);1092return;1093} else if (browser->wait_required()) {1094// Case 3: The command handler has explicitly asked to wait for page1095// load, so the executor must wait for page load or timeout.1096this->is_waiting_ = true;1097if (this->page_load_timeout_ >= 0) {1098this->wait_timeout_ = clock() + (static_cast<int>(this->page_load_timeout_) / 1000 * CLOCKS_PER_SEC);1099}1100std::string deferred_response = response.Serialize();1101LOG(DEBUG) << "Command handler requested wait. This will cause a minimal wait of at least 50 milliseconds.";1102this->CreateWaitThread(deferred_response);1103return;1104}1105} else {1106if (this->current_command_.command_type() != webdriver::CommandType::Quit) {1107LOG(WARN) << "Unable to get current browser";1108}1109}11101111if (this->is_edge_chromium_ && this->is_quitting_) {1112// If this is Edge in IE Mode, we need to explicitly wait for the1113// browsers to be completely deleted before returning for the quit1114// command.1115this->is_waiting_ = true;1116::Sleep(WAIT_TIME_IN_MILLISECONDS);1117::PostMessage(this->m_hWnd,1118WD_SESSION_QUIT_WAIT,1119NULL,1120NULL);1121return;1122}1123}11241125this->serialized_response_ = response.Serialize();1126LOG(DEBUG) << "Setting serialized response to " << this->serialized_response_;1127LOG(DEBUG) << "Is waiting flag: " << this->is_waiting_ ? "true" : "false";1128}11291130bool IECommandExecutor::IsCommandValidWithAlertPresent() {1131std::string command_type = this->current_command_.command_type();1132if (command_type == webdriver::CommandType::GetAlertText ||1133command_type == webdriver::CommandType::SendKeysToAlert ||1134command_type == webdriver::CommandType::AcceptAlert ||1135command_type == webdriver::CommandType::DismissAlert ||1136command_type == webdriver::CommandType::SetAlertCredentials ||1137command_type == webdriver::CommandType::GetTimeouts ||1138command_type == webdriver::CommandType::SetTimeouts ||1139command_type == webdriver::CommandType::Screenshot ||1140command_type == webdriver::CommandType::ElementScreenshot ||1141command_type == webdriver::CommandType::GetCurrentWindowHandle ||1142command_type == webdriver::CommandType::GetWindowHandles ||1143command_type == webdriver::CommandType::SwitchToWindow) {1144return true;1145}1146return false;1147}11481149void IECommandExecutor::CreateWaitThread(const std::string& deferred_response) {1150this->CreateWaitThread(deferred_response, false);1151}11521153void IECommandExecutor::CreateWaitThread(const std::string& deferred_response,1154const bool is_deferred_command_execution) {1155// If we are still waiting, we need to wait a bit then post a message to1156// ourselves to run the wait again. However, we can't wait using Sleep()1157// on this thread. This call happens in a message loop, and we would be1158// unable to process the COM events in the browser if we put this thread1159// to sleep.1160LOG(DEBUG) << "Creating wait thread with deferred response of `" << deferred_response << "`";1161if (is_deferred_command_execution) {1162LOG(DEBUG) << "Command execution will be rescheduled.";1163}1164WaitThreadContext* thread_context = new WaitThreadContext;1165thread_context->window_handle = this->m_hWnd;1166thread_context->is_deferred_command = is_deferred_command_execution;1167thread_context->deferred_response = new CHAR[deferred_response.size() + 1];1168strcpy_s(thread_context->deferred_response,1169deferred_response.size() + 1,1170deferred_response.c_str());11711172unsigned int thread_id = 0;1173HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,11740,1175&IECommandExecutor::WaitThreadProc,1176reinterpret_cast<void*>(thread_context),11770,1178&thread_id));1179if (thread_handle != NULL) {1180::CloseHandle(thread_handle);1181}1182else {1183LOGERR(DEBUG) << "Unable to create waiter thread";1184}1185}11861187void IECommandExecutor::CreateDelayPostMessageThread(const DWORD delay_time,1188const HWND window_handle,1189const UINT message_to_post) {1190DelayPostMessageThreadContext* context = new DelayPostMessageThreadContext;1191context->delay = delay_time;1192context->window_handle = window_handle;1193context->msg = message_to_post;1194unsigned int thread_id = 0;1195HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,11960,1197&IECommandExecutor::DelayPostMessageThreadProc,1198reinterpret_cast<void*>(context),11990,1200&thread_id));1201if (thread_handle != NULL) {1202::CloseHandle(thread_handle);1203} else {1204LOGERR(DEBUG) << "Unable to create waiter thread";1205}1206}12071208bool IECommandExecutor::IsAlertActive(BrowserHandle browser, HWND* alert_handle) {1209LOG(TRACE) << "Entering IECommandExecutor::IsAlertActive";1210HWND dialog_handle = browser->GetActiveDialogWindowHandle();1211if (dialog_handle != NULL) {1212// Found a window handle, make sure it's an actual alert,1213// and not a showModalDialog() window.1214std::vector<char> window_class_name(34);1215::GetClassNameA(dialog_handle, &window_class_name[0], 34);1216if (strcmp(ALERT_WINDOW_CLASS, &window_class_name[0]) == 0) {1217*alert_handle = dialog_handle;1218return true;1219} else if (strcmp(SECURITY_DIALOG_WINDOW_CLASS, &window_class_name[0]) == 0) {1220*alert_handle = dialog_handle;1221return true;1222} else {1223LOG(WARN) << "Found alert handle does not have a window class consistent with an alert";1224}1225} else {1226LOG(DEBUG) << "No alert handle is found";1227}1228return false;1229}12301231bool IECommandExecutor::HandleUnexpectedAlert(BrowserHandle browser,1232HWND alert_handle,1233bool force_use_dismiss,1234std::string* alert_text) {1235LOG(TRACE) << "Entering IECommandExecutor::HandleUnexpectedAlert";1236clock_t end = clock() + 5 * CLOCKS_PER_SEC;1237bool is_visible = (::IsWindowVisible(alert_handle) == TRUE);1238while (!is_visible && clock() < end) {1239::Sleep(50);1240is_visible = (::IsWindowVisible(alert_handle) == TRUE);1241}1242Alert dialog(browser, alert_handle);1243*alert_text = dialog.GetText();1244if (!dialog.is_standard_alert()) {1245// The dialog was non-standard. The most common case of this is1246// an onBeforeUnload dialog, which must be accepted to continue.1247dialog.Accept();1248return false;1249}1250if (this->unexpected_alert_behavior_ == ACCEPT_UNEXPECTED_ALERTS ||1251this->unexpected_alert_behavior_ == ACCEPT_AND_NOTIFY_UNEXPECTED_ALERTS) {1252LOG(DEBUG) << "Automatically accepting the alert";1253dialog.Accept();1254} else if (this->unexpected_alert_behavior_.size() == 0 ||1255this->unexpected_alert_behavior_ == DISMISS_UNEXPECTED_ALERTS ||1256this->unexpected_alert_behavior_ == DISMISS_AND_NOTIFY_UNEXPECTED_ALERTS ||1257force_use_dismiss) {1258// If a quit command was issued, we should not ignore an unhandled1259// alert, even if the alert behavior is set to "ignore".1260LOG(DEBUG) << "Automatically dismissing the alert";1261if (dialog.is_standard_alert() || dialog.is_security_alert()) {1262dialog.Dismiss();1263} else {1264// The dialog was non-standard. The most common case of this is1265// an onBeforeUnload dialog, which must be accepted to continue.1266dialog.Accept();1267}1268}12691270bool is_notify_unexpected_alert =1271this->unexpected_alert_behavior_.size() == 0 ||1272this->unexpected_alert_behavior_ == IGNORE_UNEXPECTED_ALERTS ||1273this->unexpected_alert_behavior_ == DISMISS_AND_NOTIFY_UNEXPECTED_ALERTS ||1274this->unexpected_alert_behavior_ == ACCEPT_AND_NOTIFY_UNEXPECTED_ALERTS;1275is_notify_unexpected_alert = is_notify_unexpected_alert && dialog.is_standard_alert();1276return is_notify_unexpected_alert;1277}12781279void IECommandExecutor::PostBrowserReattachMessage(const DWORD current_process_id,1280const std::string& browser_id,1281const std::vector<DWORD>& known_process_ids) {1282LOG(TRACE) << "Entering IECommandExecutor::PostBrowserReattachMessage";1283::Sleep(100);1284BrowserReattachInfo* repost_info = new BrowserReattachInfo;1285repost_info->current_process_id = current_process_id;1286repost_info->browser_id = browser_id;1287repost_info->known_process_ids = known_process_ids;1288::PostMessage(this->m_hWnd,1289WD_BROWSER_REATTACH,1290NULL,1291reinterpret_cast<LPARAM>(repost_info));1292}12931294void IECommandExecutor::GetNewBrowserProcessIds(std::vector<DWORD>* known_process_ids,1295std::vector<DWORD>* new_process_ids) {1296LOG(TRACE) << "Entering IECommandExecutor::GetNewBrowserProcessIds";1297std::vector<DWORD> all_ie_process_ids;1298WindowUtilities::GetProcessesByName(L"iexplore.exe", &all_ie_process_ids);12991300// Maximum size of the new process list is if all IE processes are unknown.1301std::vector<DWORD> temp_new_process_ids(all_ie_process_ids.size());1302std::sort(known_process_ids->begin(), known_process_ids->end());1303std::sort(all_ie_process_ids.begin(), all_ie_process_ids.end());1304std::vector<DWORD>::iterator end_iterator = std::set_difference(all_ie_process_ids.begin(),1305all_ie_process_ids.end(),1306known_process_ids->begin(),1307known_process_ids->end(),1308temp_new_process_ids.begin());1309temp_new_process_ids.resize(end_iterator - temp_new_process_ids.begin());1310*new_process_ids = temp_new_process_ids;1311}13121313int IECommandExecutor::GetCurrentBrowser(BrowserHandle* browser_wrapper) const {1314LOG(TRACE) << "Entering IECommandExecutor::GetCurrentBrowser";1315return this->GetManagedBrowser(this->current_browser_id_, browser_wrapper);1316}13171318int IECommandExecutor::GetManagedBrowser(const std::string& browser_id,1319BrowserHandle* browser_wrapper) const {1320LOG(TRACE) << "Entering IECommandExecutor::GetManagedBrowser";13211322if (!this->is_valid()) {1323LOG(TRACE) << "Current command executor is not valid";1324return ENOSUCHDRIVER;1325}13261327if (browser_id == "") {1328LOG(WARN) << "Browser ID requested was an empty string";1329return ENOSUCHWINDOW;1330}13311332BrowserMap::const_iterator found_iterator =1333this->managed_browsers_.find(browser_id);1334if (found_iterator == this->managed_browsers_.end()) {1335LOG(WARN) << "Unable to find managed browser with id " << browser_id;1336return ENOSUCHWINDOW;1337}13381339*browser_wrapper = found_iterator->second;1340return WD_SUCCESS;1341}13421343void IECommandExecutor::GetManagedBrowserHandles(std::vector<std::string>* managed_browser_handles) const {1344LOG(TRACE) << "Entering IECommandExecutor::GetManagedBrowserHandles";13451346BrowserMap::const_iterator it = this->managed_browsers_.begin();1347for (; it != this->managed_browsers_.end(); ++it) {1348if (it->second->IsValidWindow()) {1349managed_browser_handles->push_back(it->first);1350}13511352// Look for browser windows created by showModalDialog().1353it->second->GetActiveDialogWindowHandle();1354}1355}13561357void IECommandExecutor::AddManagedBrowser(BrowserHandle browser_wrapper) {1358LOG(TRACE) << "Entering IECommandExecutor::AddManagedBrowser";13591360this->managed_browsers_[browser_wrapper->browser_id()] = browser_wrapper;1361if (this->current_browser_id_ == "") {1362LOG(TRACE) << "Setting current browser id to " << browser_wrapper->browser_id();1363this->current_browser_id_ = browser_wrapper->browser_id();1364}1365}13661367std::string IECommandExecutor::OpenNewBrowsingContext(const std::string& window_type) {1368return this->OpenNewBrowsingContext(window_type, "about:blank");1369}13701371std::string IECommandExecutor::OpenNewBrowsingContext(const std::string& window_type,1372const std::string& url) {1373LOG(TRACE) << "Entering IECommandExecutor::OpenNewBrowsingContext";1374std::wstring target_url = StringUtilities::ToWString(url);1375std::string new_browser_id = "";1376if (window_type == TAB_WINDOW_TYPE) {1377new_browser_id = this->OpenNewBrowserTab(target_url);1378} else {1379new_browser_id = this->OpenNewBrowserWindow(target_url);1380}13811382BrowserHandle new_window_wrapper;1383this->GetManagedBrowser(new_browser_id, &new_window_wrapper);1384HWND new_window_handle = new_window_wrapper->GetBrowserWindowHandle();1385this->proxy_manager_->SetProxySettings(new_window_handle);1386new_window_wrapper->cookie_manager()->Initialize(new_window_handle);13871388return new_browser_id;1389}13901391std::string IECommandExecutor::OpenNewBrowserWindow(const std::wstring& url) {1392LOG(TRACE) << "Entering IECommandExecutor::OpenNewBrowserWindow";1393bool is_protected_mode_url = ::IEIsProtectedModeURL(url.c_str()) == S_OK;1394if (url.find(L"about:blank") == 0) {1395// Special-case URLs starting with about:blank, so that the new window1396// is in the same Protected Mode zone as the current window from which1397// it's being opened.1398BrowserHandle current_browser;1399this->GetCurrentBrowser(¤t_browser);1400is_protected_mode_url = current_browser->IsProtectedMode();1401}1402CComPtr<IWebBrowser2> browser = this->factory_->CreateBrowser(is_protected_mode_url);1403if (browser == NULL) {1404// No browser was created, so we have to bail early.1405// Check the log for the HRESULT why.1406return "";1407}1408LOG(DEBUG) << "New browser window was opened.";1409BrowserHandle new_window_wrapper(new Browser(browser,1410NULL,1411this->m_hWnd,1412this->is_edge_chromium_));1413// It is acceptable to set the proxy settings here, as the newly-created1414// browser window has not yet been navigated to any page. Only after the1415// interface has been marshaled back across the thread boundary to the1416// NewWindow3 event handler will the navigation begin, which ensures that1417// even the initial navigation will get captured by the proxy, if one is1418// set. Likewise, the cookie manager needs to have its window handle1419// properly set to a non-NULL value so that windows messages are routed1420// to the correct window.1421// N.B. DocumentHost::GetBrowserWindowHandle returns the tab window handle1422// for IE 7 and above, and the top-level window for IE6. This is the window1423// required for setting the proxy settings.1424this->AddManagedBrowser(new_window_wrapper);1425return new_window_wrapper->browser_id();1426}14271428std::string IECommandExecutor::OpenNewBrowserTab(const std::wstring& url) {1429LOG(TRACE) << "Entering IECommandExecutor::OpenNewBrowserTab";1430BrowserHandle browser_wrapper;1431this->GetCurrentBrowser(&browser_wrapper);1432HWND top_level_handle = browser_wrapper->GetTopLevelWindowHandle();14331434if (this->is_edge_chromium_) {1435// This is a hack to account for the case where the currently focused1436// WebDriver window is a tab without the visual focus. When an IE Mode1437// tab is sent to the background, it is reparented to a different top-1438// level window than the Edge window. To detect a new tab being opened,1439// we must find a top-level Edge window containing and active IE Mode1440// tab, and use that as our parent window. This is a rare case that1441// will only happen if the user does not switch WebDriver command focus1442// to the new window immediately after opening. The Selenium language1443// bindings do this automatically, but non-Selenium client bindings may1444// not do so.1445// ASSUMPTION: The first top-level Edge window we find containing an IE1446// Mode tab is the top-level window we want.1447std::string window_class =1448WindowUtilities::GetWindowClass(top_level_handle);1449if (window_class != "Chrome_WidgetWin_1") {1450std::vector<HWND> edge_handles;1451::EnumWindows(&BrowserFactory::FindEdgeBrowserHandles,1452reinterpret_cast<LPARAM>(&edge_handles));1453for (auto& edge_handle : edge_handles) {1454std::vector<HWND> ie_mode_handles;1455::EnumChildWindows(edge_handle,1456&BrowserFactory::FindIEBrowserHandles,1457reinterpret_cast<LPARAM>(&ie_mode_handles));1458if (ie_mode_handles.size() > 0) {1459top_level_handle = edge_handle;1460break;1461}1462}1463}1464}14651466std::vector<HWND> original_handles;1467::EnumChildWindows(top_level_handle,1468&BrowserFactory::FindIEBrowserHandles,1469reinterpret_cast<LPARAM>(&original_handles));1470std::sort(original_handles.begin(), original_handles.end());14711472// IWebBrowser2::Navigate2 will open the specified URL in a new tab,1473// if requested. The Sleep() call after the navigate is necessary,1474// since the IECommandExecutor class doesn't have access to the events1475// to indicate the navigation is completed.1476CComVariant url_variant = url.c_str();1477CComVariant flags = navOpenInNewTab;1478browser_wrapper->browser()->Navigate2(&url_variant,1479&flags,1480NULL,1481NULL,1482NULL);1483::Sleep(500);14841485HWND new_tab_window = NULL;1486std::vector<HWND> new_handles;1487::EnumChildWindows(top_level_handle,1488&BrowserFactory::FindIEBrowserHandles,1489reinterpret_cast<LPARAM>(&new_handles));1490clock_t end_time = clock() + 5 * CLOCKS_PER_SEC;1491if (browser_wrapper->is_edge_chromium()) {1492// It appears that for Chromium-based Edge in IE Mode, there will only1493// ever be one active child window of the top-level window with window1494// class "Internet Explorer_Server", which is the active tab. Inactive1495// tabs are re-parented until brought back to being the active tab.1496while ((new_handles.size() == 0 || new_handles[0] == original_handles[0])1497&& clock() < end_time) {1498if (new_handles.size() != 0) {1499new_handles.clear();1500}15011502::Sleep(50);1503::EnumChildWindows(top_level_handle,1504&BrowserFactory::FindIEBrowserHandles,1505reinterpret_cast<LPARAM>(&new_handles));1506}15071508if (new_handles.size() == 0 || new_handles[0] == original_handles[0]) {1509LOG(WARN) << "No new window handle found after attempt to open";1510return "";1511}15121513new_tab_window = new_handles[0];1514} else {1515while (new_handles.size() <= original_handles.size() &&1516clock() < end_time) {1517::Sleep(50);1518::EnumChildWindows(top_level_handle,1519&BrowserFactory::FindIEBrowserHandles,1520reinterpret_cast<LPARAM>(&new_handles));1521}1522std::sort(new_handles.begin(), new_handles.end());15231524if (new_handles.size() <= original_handles.size()) {1525LOG(WARN) << "No new window handle found after attempt to open";1526return "";1527}15281529// We are guaranteed to have at least one HWND difference1530// between the two vectors if we reach this point, because1531// we know the vectors are different sizes.1532std::vector<HWND> diff(new_handles.size());1533std::vector<HWND>::iterator it = std::set_difference(new_handles.begin(),1534new_handles.end(),1535original_handles.begin(),1536original_handles.end(),1537diff.begin());1538diff.resize(it - diff.begin());1539if (diff.size() > 1) {1540std::string handle_list = "";1541std::vector<HWND>::const_iterator it = diff.begin();1542for (; it != diff.end(); ++it) {1543if (handle_list.size() > 0) {1544handle_list.append(", ");1545}1546handle_list.append(StringUtilities::Format("0x%08x", *it));1547}1548LOG(DEBUG) << "Found more than one new window handles! Found "1549<< diff.size() << "windows [" << handle_list << "]";1550}1551new_tab_window = diff[0];1552}15531554DWORD process_id;1555::GetWindowThreadProcessId(new_tab_window, &process_id);1556clock_t end = clock() + (DEFAULT_BROWSER_REATTACH_TIMEOUT_IN_MILLISECONDS / 1000 * CLOCKS_PER_SEC);1557bool is_ready = this->factory_->IsBrowserProcessInitialized(process_id);1558while (!is_ready && clock() < end) {1559::Sleep(100);1560is_ready = this->factory_->IsBrowserProcessInitialized(process_id);1561}15621563ProcessWindowInfo info;1564info.dwProcessId = process_id;1565info.hwndBrowser = new_tab_window;1566info.pBrowser = NULL;1567std::string error_message = "";1568this->factory_->AttachToBrowser(&info, &error_message);1569BrowserHandle new_tab_wrapper(new Browser(info.pBrowser,1570NULL,1571this->m_hWnd,1572this->is_edge_chromium_));1573// Force a wait cycle to make sure the browser is finished initializing.1574new_tab_wrapper->Wait(NORMAL_PAGE_LOAD_STRATEGY);1575this->AddManagedBrowser(new_tab_wrapper);1576return new_tab_wrapper->browser_id();1577}15781579int IECommandExecutor::CreateNewBrowser(std::string* error_message) {1580LOG(TRACE) << "Entering IECommandExecutor::CreateNewBrowser";15811582DWORD process_id = this->factory_->LaunchBrowserProcess(error_message);1583if (process_id == NULL) {1584LOG(WARN) << "Unable to launch browser, received NULL process ID";1585this->is_waiting_ = false;1586return ENOSUCHDRIVER;1587}15881589ProcessWindowInfo process_window_info;1590process_window_info.dwProcessId = process_id;1591process_window_info.hwndBrowser = NULL;1592process_window_info.pBrowser = NULL;1593bool attached = this->factory_->AttachToBrowser(&process_window_info,1594error_message);1595if (!attached) {1596LOG(WARN) << "Unable to attach to browser COM object";1597this->is_waiting_ = false;1598return ENOSUCHDRIVER;1599}16001601// Set persistent hover functionality in the interactions implementation.1602//this->input_manager_->StartPersistentEvents();1603LOG(INFO) << "Persistent hovering set to: "1604<< this->input_manager_->use_persistent_hover();16051606this->proxy_manager_->SetProxySettings(process_window_info.hwndBrowser);1607BrowserHandle wrapper(new Browser(process_window_info.pBrowser,1608process_window_info.hwndBrowser,1609this->m_hWnd,1610this->is_edge_chromium_));16111612this->AddManagedBrowser(wrapper);1613bool is_busy = wrapper->IsBusy();1614if (is_busy) {1615LOG(WARN) << "Browser was launched and attached to, but is still busy.";1616}1617wrapper->SetFocusToBrowser();1618this->edge_temp_dir_ = this->factory_->GetEdgeTempDir();1619return WD_SUCCESS;1620}16211622int IECommandExecutor::GetManagedElement(const std::string& element_id,1623ElementHandle* element_wrapper) const {1624LOG(TRACE) << "Entering IECommandExecutor::GetManagedElement";1625return this->managed_elements_->GetManagedElement(element_id, element_wrapper);1626}16271628bool IECommandExecutor::AddManagedElement(IHTMLElement* element,1629ElementHandle* element_wrapper) {1630LOG(TRACE) << "Entering IECommandExecutor::AddManagedElement";1631BrowserHandle current_browser;1632this->GetCurrentBrowser(¤t_browser);1633return this->managed_elements_->AddManagedElement(current_browser, element, element_wrapper);1634}16351636void IECommandExecutor::RemoveManagedElement(const std::string& element_id) {1637LOG(TRACE) << "Entering IECommandExecutor::RemoveManagedElement";1638this->managed_elements_->RemoveManagedElement(element_id);1639}16401641void IECommandExecutor::ListManagedElements() {1642LOG(TRACE) << "Entering IECommandExecutor::ListManagedElements";1643this->managed_elements_->ListManagedElements();1644}16451646int IECommandExecutor::GetElementFindMethod(const std::string& mechanism,1647std::wstring* translation) const {1648LOG(TRACE) << "Entering IECommandExecutor::GetElementFindMethod";16491650ElementFindMethodMap::const_iterator found_iterator =1651this->element_find_methods_.find(mechanism);1652if (found_iterator == this->element_find_methods_.end()) {1653LOG(WARN) << "Unable to determine find method " << mechanism;1654return EUNHANDLEDERROR;1655}16561657*translation = found_iterator->second;1658return WD_SUCCESS;1659}16601661int IECommandExecutor::LocateElement(const ElementHandle parent_wrapper,1662const std::string& mechanism,1663const std::string& criteria,1664Json::Value* found_element) const {1665LOG(TRACE) << "Entering IECommandExecutor::LocateElement";16661667std::wstring mechanism_translation = L"";1668int status_code = this->GetElementFindMethod(mechanism,1669&mechanism_translation);1670if (status_code != WD_SUCCESS) {1671LOG(WARN) << "Unable to determine mechanism translation for " << mechanism;1672return status_code;1673}16741675std::wstring wide_criteria = StringUtilities::ToWString(criteria);1676return this->element_finder()->FindElement(*this,1677parent_wrapper,1678mechanism_translation,1679wide_criteria,1680found_element);1681}16821683int IECommandExecutor::LocateElements(const ElementHandle parent_wrapper,1684const std::string& mechanism,1685const std::string& criteria,1686Json::Value* found_elements) const {1687LOG(TRACE) << "Entering IECommandExecutor::LocateElements";16881689std::wstring mechanism_translation = L"";1690int status_code = this->GetElementFindMethod(mechanism,1691&mechanism_translation);1692if (status_code != WD_SUCCESS) {1693LOG(WARN) << "Unable to determine mechanism translation for " << mechanism;1694return status_code;1695}16961697std::wstring wide_criteria = StringUtilities::ToWString(criteria);1698return this->element_finder()->FindElements(*this,1699parent_wrapper,1700mechanism_translation,1701wide_criteria,1702found_elements);1703}17041705void IECommandExecutor::PopulateElementFinderMethods(void) {1706LOG(TRACE) << "Entering IECommandExecutor::PopulateElementFinderMethods";17071708this->element_find_methods_["id"] = L"id";1709this->element_find_methods_["name"] = L"name";1710this->element_find_methods_["tag name"] = L"tagName";1711this->element_find_methods_["link text"] = L"linkText";1712this->element_find_methods_["partial link text"] = L"partialLinkText";1713this->element_find_methods_["class name"] = L"className";1714this->element_find_methods_["xpath"] = L"xpath";1715this->element_find_methods_["css selector"] = L"css";1716}17171718} // namespace webdriver171917201721