int BrowserFactory::GetZoneProtectedModeSetting(const HKEY key_handle, const std::wstring& zone_subkey_name) { LOG(TRACE) << "Entering BrowserFactory::GetZoneProtectedModeSetting"; int protected_mode_value = 3; HKEY subkey_handle; if (ERROR_SUCCESS == ::RegOpenKeyEx(key_handle, zone_subkey_name.c_str(), 0, KEY_QUERY_VALUE, &subkey_handle)) { DWORD value = 0; DWORD value_length = sizeof(DWORD); if (ERROR_SUCCESS == ::RegQueryValueEx(subkey_handle, IE_PROTECTED_MODE_SETTING_VALUE_NAME, NULL, NULL, reinterpret_cast<LPBYTE>(&value), &value_length)) { LOG(DEBUG) << "Found Protected Mode setting value of " << value << " for zone " << LOGWSTRING(zone_subkey_name.c_str()); protected_mode_value = value; } else { LOG(DEBUG) << "RegQueryValueEx failed for getting Protected Mode setting for a zone: " << LOGWSTRING(zone_subkey_name.c_str()); } ::RegCloseKey(subkey_handle); } else { // The REG_DWORD value doesn't exist, so we have to return the default // value, which is "on" for the Internet and Restricted Sites zones and // is "on" for the Local Intranet zone in IE7 only (the default was // changed to "off" for Local Intranet in IE8), and "off" everywhere // else. // Note that a value of 0 in the registry value indicates that Protected // Mode is "on" for that zone; a value of 3 indicates that Protected Mode // is "off" for that zone. if (zone_subkey_name == ZONE_INTERNET || zone_subkey_name == ZONE_RESTRICTED_SITES || (zone_subkey_name == ZONE_LOCAL_INTRANET && this->ie_major_version_ == 7)) { protected_mode_value = 0; } LOG(DEBUG) << "Protected Mode zone setting value does not exist for zone " << LOGWSTRING(zone_subkey_name.c_str()) << ". Using default value of " << protected_mode_value; } return protected_mode_value; }
void BrowserFactory::LaunchBrowserUsingCreateProcess(const std::string& initial_url, const std::string& command_line_switches, PROCESS_INFORMATION* proc_info, std::string* error_message) { LOG(TRACE) << "Entering BrowserFactory::LaunchBrowserUsingCreateProcess"; LOG(DEBUG) << "Starting IE using the CreateProcess API"; STARTUPINFO start_info; ::ZeroMemory(&start_info, sizeof(start_info)); start_info.cb = sizeof(start_info); std::wstring wide_initial_url = StringUtilities::ToWString(initial_url); std::wstring wide_ie_switches = StringUtilities::ToWString(command_line_switches); std::wstring executable_and_url = this->ie_executable_location_; if (wide_ie_switches.size() != 0) { executable_and_url.append(L" "); executable_and_url.append(wide_ie_switches); } executable_and_url.append(L" "); executable_and_url.append(wide_initial_url); LOG(TRACE) << "IE starting command line is: '" << LOGWSTRING(executable_and_url) << "'."; LPWSTR command_line = new WCHAR[executable_and_url.size() + 1]; wcscpy_s(command_line, executable_and_url.size() + 1, executable_and_url.c_str()); command_line[executable_and_url.size()] = L'\0'; BOOL create_process_result = ::CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, proc_info); if (!create_process_result) { int create_proc_msg_count = _scwprintf(CREATEPROCESS_ERROR_MESSAGE, command_line); vector<wchar_t> create_proc_result_msg(create_proc_msg_count + 1); _snwprintf_s(&create_proc_result_msg[0], create_proc_result_msg.size(), create_proc_msg_count, CREATEPROCESS_ERROR_MESSAGE, command_line); std::string launch_error = StringUtilities::ToString(&create_proc_result_msg[0]); *error_message = launch_error; } delete[] command_line; }
void BrowserFactory::GetIEVersion() { LOG(TRACE) << "Entering BrowserFactory::GetIEVersion"; struct LANGANDCODEPAGE { WORD language; WORD code_page; } *lpTranslate; DWORD dummy; DWORD length = ::GetFileVersionInfoSize(this->ie_executable_location_.c_str(), &dummy); if (length == 0) { // 64-bit Windows 8 has a bug where it does not return the executable location properly this->ie_major_version_ = -1; LOG(WARN) << "Couldn't find IE version for executable " << LOGWSTRING(this->ie_executable_location_.c_str()) << ", falling back to " << this->ie_major_version_; return; } std::vector<BYTE> version_buffer(length); ::GetFileVersionInfo(this->ie_executable_location_.c_str(), dummy, length, &version_buffer[0]); UINT page_count; BOOL query_result = ::VerQueryValue(&version_buffer[0], FILE_LANGUAGE_INFO, reinterpret_cast<void**>(&lpTranslate), &page_count); wchar_t sub_block[MAX_PATH]; _snwprintf_s(sub_block, MAX_PATH, MAX_PATH, FILE_VERSION_INFO, lpTranslate->language, lpTranslate->code_page); LPVOID value = NULL; UINT size; query_result = ::VerQueryValue(&version_buffer[0], sub_block, &value, &size); std::wstring ie_version; ie_version.assign(static_cast<wchar_t*>(value)); std::wstringstream version_stream(ie_version); version_stream >> this->ie_major_version_; }
void BrowserFactory::GetIEVersion() { LOG(TRACE) << "Entering BrowserFactory::GetIEVersion"; std::string ie_version = FileUtilities::GetFileVersion(this->ie_executable_location_); if (ie_version.size() == 0) { // 64-bit Windows 8 has a bug where it does not return the executable location properly this->ie_major_version_ = -1; LOG(WARN) << "Couldn't find IE version for executable " << LOGWSTRING(this->ie_executable_location_) << ", falling back to " << this->ie_major_version_; return; } std::stringstream version_stream(ie_version); version_stream >> this->ie_major_version_; }
void BrowserFactory::LaunchBrowserUsingCreateProcess(PROCESS_INFORMATION* proc_info, std::string* error_message) { LOG(TRACE) << "Entering BrowserFactory::LaunchBrowserUsingCreateProcess"; LOG(DEBUG) << "Starting IE using the CreateProcess API"; STARTUPINFO start_info; ::ZeroMemory(&start_info, sizeof(start_info)); start_info.cb = sizeof(start_info); std::wstring executable_and_url = this->ie_executable_location_; if (this->browser_command_line_switches_.size() != 0) { executable_and_url.append(L" "); executable_and_url.append(this->browser_command_line_switches_); } executable_and_url.append(L" "); executable_and_url.append(this->initial_browser_url_); LOG(TRACE) << "IE starting command line is: '" << LOGWSTRING(executable_and_url) << "'."; LPWSTR command_line = new WCHAR[executable_and_url.size() + 1]; wcscpy_s(command_line, executable_and_url.size() + 1, executable_and_url.c_str()); command_line[executable_and_url.size()] = L'\0'; BOOL create_process_result = ::CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, proc_info); if (!create_process_result) { *error_message = StringUtilities::Format(CREATEPROCESS_ERROR_MESSAGE, StringUtilities::ToString(command_line)); } delete[] command_line; }
bool DocumentHost::IsHtmlPage(IHTMLDocument2* doc) { LOG(TRACE) << "Entering DocumentHost::IsHtmlPage"; CComBSTR type; if (!SUCCEEDED(doc->get_mimeType(&type))) { LOG(WARN) << "Unable to get mime type for document, call to IHTMLDocument2::get_mimeType failed"; return false; } std::wstring document_type_key_name= L""; if (this->factory_.GetRegistryValue(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice", L"Progid", &document_type_key_name)) { // Look for the user-customization under Vista/Windows 7 first. If it's // IE, set the document friendly name lookup key to 'htmlfile'. If not, // set it to blank so that we can look up the proper HTML type. if (document_type_key_name == L"IE.HTTP") { document_type_key_name = L"htmlfile"; } else { LOG(DEBUG) << "Unable to support custom document type: " << LOGWSTRING(document_type_key_name.c_str()); document_type_key_name = L""; } } else { LOG(DEBUG) << "Unable to read document type from registry"; } if (document_type_key_name == L"") { // To be technically correct, we should look up the extension specified // for the text/html MIME type first (located in the "Extension" value // of HKEY_CLASSES_ROOT\MIME\Database\Content Type\text/html), but that // should always resolve to ".htm" anyway. From the extension, we can // find the browser-specific subkey of HKEY_CLASSES_ROOT, the default // value of which should contain the browser-specific friendly name of // the MIME type for HTML documents, which is what // IHTMLDocument2::get_mimeType() returns. if (!this->factory_.GetRegistryValue(HKEY_CLASSES_ROOT, L".htm", L"", &document_type_key_name)) { LOG(WARN) << "Unable to read document type from registry for '.htm'"; return false; } } // First try the (default) value for the subkey. Some browsers (Opera) // do not write this information in the (default) value, so if that fails, // try the FriendlyTypeName value. std::wstring mime_type_name; if (!this->factory_.GetRegistryValue(HKEY_CLASSES_ROOT, document_type_key_name, L"", &mime_type_name)) { if (!this->factory_.GetRegistryValue(HKEY_CLASSES_ROOT, document_type_key_name, L"FriendlyTypeName", &mime_type_name)) { LOG(WARN) << "Unable to read mime type from registry for document type"; return false; } } std::wstring type_string = type; if (type_string == mime_type_name) { return true; } // If the user set Firefox as a default browser at any point, the MIME type // appears to be "sticky". This isn't elegant, but it appears to alleviate // the worst symptoms. Tested by using both Safari and Opera as the default // browser, even after setting IE as the default after Firefox (so the chain // of defaults looks like (IE -> Firefox -> IE -> Opera) if (L"Firefox HTML Document" == mime_type_name) { LOG(INFO) << "It looks like Firefox was once the default browser. " << "Guessing the page type from mime type alone"; return true; } return false; }
bool BrowserFactory::ProtectedModeSettingsAreValid() { LOG(TRACE) << "Entering BrowserFactory::ProtectedModeSettingsAreValid"; bool settings_are_valid = true; LOG(DEBUG) << "Detected IE version: " << this->ie_major_version_ << ", detected Windows version: " << this->windows_major_version_; // Only need to check Protected Mode settings on IE 7 or higher // and on Windows Vista or higher. Otherwise, Protected Mode // doesn't come into play, and are valid. // Documentation of registry settings can be found at the following // Microsoft KnowledgeBase article: // http://support.microsoft.com/kb/182569 if (this->ie_major_version_ >= 7 && this->windows_major_version_ >= 6) { HKEY key_handle; if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_CURRENT_USER, IE_SECURITY_ZONES_REGISTRY_KEY, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &key_handle)) { DWORD subkey_count = 0; DWORD max_subkey_name_length = 0; if (ERROR_SUCCESS == ::RegQueryInfoKey(key_handle, NULL, NULL, NULL, &subkey_count, &max_subkey_name_length, NULL, NULL, NULL, NULL, NULL, NULL)) { int protected_mode_value = -1; std::vector<TCHAR> subkey_name_buffer(max_subkey_name_length + 1); for (size_t index = 0; index < subkey_count; ++index) { DWORD number_of_characters_copied = max_subkey_name_length + 1; ::RegEnumKeyEx(key_handle, static_cast<DWORD>(index), &subkey_name_buffer[0], &number_of_characters_copied, NULL, NULL, NULL, NULL); std::wstring subkey_name = &subkey_name_buffer[0]; // Ignore the "My Computer" zone, since it's not displayed // in the UI. if (subkey_name != ZONE_MY_COMPUTER) { int value = this->GetZoneProtectedModeSetting(key_handle, subkey_name); if (protected_mode_value == -1) { protected_mode_value = value; } else { if (value != protected_mode_value) { settings_are_valid = false; break; } } } } } else { LOG(WARN) << "RegQueryInfoKey to get count of zone setting subkeys failed"; } ::RegCloseKey(key_handle); } else { LOG(WARN) << "RegOpenKeyEx for zone settings registry key " << LOGWSTRING(IE_SECURITY_ZONES_REGISTRY_KEY) << " in HKEY_CURRENT_USER failed"; } } return settings_are_valid; }
bool BrowserFactory::GetRegistryValue(const HKEY root_key, const std::wstring& subkey, const std::wstring& value_name, std::wstring *value) { LOG(TRACE) << "Entering BrowserFactory::GetRegistryValue"; std::string root_key_description = "HKEY_CURRENT_USER"; if (root_key == HKEY_CLASSES_ROOT) { root_key_description = "HKEY_CLASSES_ROOT"; } else if (root_key == HKEY_LOCAL_MACHINE) { root_key_description = "HKEY_LOCAL_MACHINE"; } bool value_retrieved = false; DWORD required_buffer_size; HKEY key_handle; long registry_call_result = ::RegOpenKeyEx(root_key, subkey.c_str(), 0, KEY_QUERY_VALUE, &key_handle); if (ERROR_SUCCESS == registry_call_result) { registry_call_result = ::RegQueryValueEx(key_handle, value_name.c_str(), NULL, NULL, NULL, &required_buffer_size); if (ERROR_SUCCESS == registry_call_result) { std::vector<TCHAR> value_buffer(required_buffer_size); DWORD value_type(0); registry_call_result = ::RegQueryValueEx(key_handle, value_name.c_str(), NULL, &value_type, reinterpret_cast<LPBYTE>(&value_buffer[0]), &required_buffer_size); if (ERROR_SUCCESS == registry_call_result) { *value = &value_buffer[0]; value_retrieved = true; } else { LOG(WARN) << "RegQueryValueEx failed with error code " << registry_call_result << " retrieving value with name " << LOGWSTRING(value_name.c_str()) << " in subkey " << LOGWSTRING(subkey.c_str()) << "in hive " << root_key_description; } } else { LOG(WARN) << "RegQueryValueEx failed with error code " << registry_call_result << " retrieving required buffer size for value with name " << LOGWSTRING(value_name.c_str()) << " in subkey " << LOGWSTRING(subkey.c_str()) << "in hive " << root_key_description; } ::RegCloseKey(key_handle); } else { LOG(WARN) << "RegOpenKeyEx failed with error code " << registry_call_result << " attempting to open subkey " << LOGWSTRING(subkey.c_str()) << "in hive " << root_key_description; } return value_retrieved; }
bool Browser::IsDocumentNavigating(IHTMLDocument2* doc) { LOG(TRACE) << "Entering Browser::IsDocumentNavigating"; bool is_navigating = true; // Starting WaitForDocumentComplete() is_navigating = this->is_navigation_started_; CComBSTR ready_state; HRESULT hr = doc->get_readyState(&ready_state); if (FAILED(hr) || is_navigating || _wcsicmp(ready_state, L"complete") != 0) { if (FAILED(hr)) { LOGHR(DEBUG, hr) << "IHTMLDocument2::get_readyState failed."; } else if (is_navigating) { LOG(DEBUG) << "DocumentComplete event fired, indicating a new navigation."; } else { std::wstring state = ready_state; LOG(DEBUG) << "document.readyState is not 'complete'; it was " << LOGWSTRING(state); } return true; } else { is_navigating = false; } // document.readyState == complete is_navigating = this->is_navigation_started_; CComPtr<IHTMLFramesCollection2> frames; hr = doc->get_frames(&frames); if (is_navigating || FAILED(hr)) { LOG(DEBUG) << "Could not get frames, navigation has started or call to IHTMLDocument2::get_frames failed"; return true; } if (frames != NULL) { long frame_count = 0; hr = frames->get_length(&frame_count); CComVariant index; index.vt = VT_I4; for (long i = 0; i < frame_count; ++i) { // Waiting on each frame index.lVal = i; CComVariant result; hr = frames->item(&index, &result); if (FAILED(hr)) { LOGHR(DEBUG, hr) << "Could not get frame item for index " << i << ", call to IHTMLFramesCollection2::item failed, frame/frameset is broken"; continue; } CComPtr<IHTMLWindow2> window; result.pdispVal->QueryInterface<IHTMLWindow2>(&window); if (!window) { LOG(DEBUG) << "Could not get window for frame item with index " << i << ", cast to IHTMLWindow2 failed, frame is not an HTML frame"; continue; } CComPtr<IHTMLDocument2> frame_document; bool is_valid_frame_document = this->GetDocumentFromWindow(window, &frame_document); is_navigating = this->is_navigation_started_; if (is_navigating) { break; } // Recursively call to wait for the frame document to complete if (is_valid_frame_document) { is_navigating = this->IsDocumentNavigating(frame_document); if (is_navigating) { break; } } } } else { LOG(DEBUG) << "IHTMLDocument2.get_frames() returned empty collection"; } return is_navigating; }
int ElementFinder::FindElements(const IECommandExecutor& executor, const ElementHandle parent_wrapper, const std::wstring& mechanism, const std::wstring& criteria, Json::Value* found_elements) { LOG(TRACE) << "Entering ElementFinder::FindElements"; BrowserHandle browser; int status_code = executor.GetCurrentBrowser(&browser); if (status_code == WD_SUCCESS) { if (mechanism == L"css") { if (!this->HasNativeCssSelectorEngine(executor)) { LOG(DEBUG) << "Element location strategy is CSS selectors, but " << "document does not support CSS selectors. Falling back " << "to using the Sizzle JavaScript CSS selector engine."; return this->FindElementsUsingSizzle(executor, parent_wrapper, criteria, found_elements); } } LOG(DEBUG) << "Using FindElements atom to locate element having " << LOGWSTRING(mechanism) << " = " << LOGWSTRING(criteria); CComPtr<IHTMLDocument2> doc; browser->GetDocument(&doc); std::wstring script_source(L"(function() { return ("); script_source += atoms::asString(atoms::FIND_ELEMENTS); script_source += L")})();"; Script script_wrapper(doc, script_source, 3); script_wrapper.AddArgument(mechanism); script_wrapper.AddArgument(criteria); if (parent_wrapper) { script_wrapper.AddArgument(parent_wrapper->element()); } status_code = script_wrapper.Execute(); if (status_code == WD_SUCCESS) { Json::Value atom_result; script_wrapper.ConvertResultToJsonValue(executor, &atom_result); int atom_status_code = atom_result["status"].asInt(); Json::Value atom_value = atom_result["value"]; status_code = atom_status_code; *found_elements = atom_result["value"]; } else { // Hitting a JavaScript error with the atom is an unrecoverable // error. The most common case of this for IE is when there is a // page refresh, navigation, or similar, and the driver is polling // for element presence. The calling code can't do anything about // it, so we might as well just log and return In the common case, // this means that the error will be transitory, and will sort // itself out once the DOM returns to normal after the page transition // is completed. LOG(WARN) << "A JavaScript error was encountered executing the findElements atom."; } } else { LOG(WARN) << "Unable to get browser"; } return status_code; }
bool RegistryUtilities::GetRegistryValue(const HKEY root_key, const std::wstring& subkey, const std::wstring& value_name, std::wstring *value) { std::string root_key_description = "HKEY_CURRENT_USER"; if (root_key == HKEY_CLASSES_ROOT) { root_key_description = "HKEY_CLASSES_ROOT"; } else if (root_key == HKEY_LOCAL_MACHINE) { root_key_description = "HKEY_LOCAL_MACHINE"; } bool value_retrieved = false; DWORD required_buffer_size; DWORD value_type; HKEY key_handle; long registry_call_result = ::RegOpenKeyEx(root_key, subkey.c_str(), 0, KEY_QUERY_VALUE, &key_handle); if (ERROR_SUCCESS == registry_call_result) { registry_call_result = ::RegQueryValueEx(key_handle, value_name.c_str(), NULL, &value_type, NULL, &required_buffer_size); if (ERROR_SUCCESS == registry_call_result) { if (value_type == REG_SZ || value_type == REG_EXPAND_SZ || value_type == REG_MULTI_SZ) { std::vector<wchar_t> value_buffer(required_buffer_size); registry_call_result = ::RegQueryValueEx(key_handle, value_name.c_str(), NULL, &value_type, reinterpret_cast<LPBYTE>(&value_buffer[0]), &required_buffer_size); if (ERROR_SUCCESS == registry_call_result) { *value = &value_buffer[0]; value_retrieved = true; } } else if (value_type == REG_DWORD) { DWORD numeric_value = 0; registry_call_result = ::RegQueryValueEx(key_handle, value_name.c_str(), NULL, &value_type, reinterpret_cast<LPBYTE>(&numeric_value), &required_buffer_size); if (ERROR_SUCCESS == registry_call_result) { // Coerce the numeric value to a string to return back. // Assume 10 characters will be enough to hold the size // of the value. std::vector<wchar_t> numeric_value_buffer(10); _ltow_s(numeric_value, &numeric_value_buffer[0], numeric_value_buffer.size(), 10); *value = &numeric_value_buffer[0]; value_retrieved = true; } } else { LOG(WARN) << "Unexpected value type of " << value_type << " for RegQueryValueEx was found for value with name " << LOGWSTRING(value_name) << " in subkey " << LOGWSTRING(subkey) << " in hive " << root_key_description; } if (ERROR_SUCCESS != registry_call_result) { LOG(WARN) << "RegQueryValueEx failed with error code " << registry_call_result << " retrieving value with name " << LOGWSTRING(value_name) << " in subkey " << LOGWSTRING(subkey) << "in hive " << root_key_description; } } else { LOG(WARN) << "RegQueryValueEx failed with error code " << registry_call_result << " retrieving required buffer size for value with name " << LOGWSTRING(value_name) << " in subkey " << LOGWSTRING(subkey) << " in hive " << root_key_description; } ::RegCloseKey(key_handle); } else { LOG(WARN) << "RegOpenKeyEx failed with error code " << registry_call_result << " attempting to open subkey " << LOGWSTRING(subkey) << " in hive " << root_key_description; } return value_retrieved; }
int ElementFinder::FindElement(const IECommandExecutor& executor, const ElementHandle parent_wrapper, const std::wstring& mechanism, const std::wstring& criteria, Json::Value* found_element) { LOG(TRACE) << "Entering ElementFinder::FindElement"; BrowserHandle browser; int status_code = executor.GetCurrentBrowser(&browser); if (status_code == WD_SUCCESS) { if (mechanism == L"css") { if (!this->HasNativeCssSelectorEngine(executor)) { LOG(DEBUG) << "Element location strategy is CSS selectors, but " << "document does not support CSS selectors. Falling back " << "to using the Sizzle JavaScript CSS selector engine."; return this->FindElementUsingSizzle(executor, parent_wrapper, criteria, found_element); } } LOG(DEBUG) << L"Using FindElement atom to locate element having " << LOGWSTRING(mechanism) << " = " << LOGWSTRING(criteria); std::wstring sanitized_criteria = criteria; this->SanitizeCriteria(mechanism, &sanitized_criteria); std::wstring criteria_object_script = L"(function() { return function(){ return { \"" + mechanism + L"\" : \"" + sanitized_criteria + L"\" }; };})();"; CComPtr<IHTMLDocument2> doc; browser->GetDocument(&doc); Script criteria_wrapper(doc, criteria_object_script, 0); status_code = criteria_wrapper.Execute(); if (status_code == WD_SUCCESS) { CComVariant criteria_object; criteria_object.Copy(&criteria_wrapper.result()); // The atom is just the definition of an anonymous // function: "function() {...}"; Wrap it in another function so we can // invoke it with our arguments without polluting the current namespace. std::wstring script_source(L"(function() { return ("); script_source += atoms::asString(atoms::FIND_ELEMENT); script_source += L")})();"; Script script_wrapper(doc, script_source, 2); script_wrapper.AddArgument(criteria_object); if (parent_wrapper) { script_wrapper.AddArgument(parent_wrapper->element()); } status_code = script_wrapper.Execute(); if (status_code == WD_SUCCESS) { if (script_wrapper.ResultIsElement()) { script_wrapper.ConvertResultToJsonValue(executor, found_element); } else { LOG(WARN) << "Unable to find element by mechanism " << LOGWSTRING(mechanism) << " and criteria " << LOGWSTRING(sanitized_criteria); status_code = ENOSUCHELEMENT; } } else { // An error in the execution of the FindElement atom for XPath is assumed // to be a syntactically invalid XPath. if (mechanism == L"xpath") { LOG(WARN) << "Attempted to find element using invalid xpath: " << LOGWSTRING(sanitized_criteria); status_code = EINVALIDSELECTOR; } else { LOG(WARN) << "Unexpected error attempting to find element by mechanism " << LOGWSTRING(mechanism) << " with criteria " << LOGWSTRING(sanitized_criteria); status_code = ENOSUCHELEMENT; } } } else { LOG(WARN) << "Unable to create criteria object for mechanism " << LOGWSTRING(mechanism) << " and criteria " << LOGWSTRING(sanitized_criteria); status_code = ENOSUCHELEMENT; } } else { LOG(WARN) << "Unable to get browser"; } return status_code; }
int ElementFinder::FindElements(const IECommandExecutor& executor, const ElementHandle parent_wrapper, const std::wstring& mechanism, const std::wstring& criteria, Json::Value* found_elements) { LOG(TRACE) << "Entering ElementFinder::FindElements"; BrowserHandle browser; int status_code = executor.GetCurrentBrowser(&browser); if (status_code == WD_SUCCESS) { if (mechanism == L"css") { if (!this->HasNativeCssSelectorEngine(executor)) { LOG(DEBUG) << "Element location strategy is CSS selectors, but " << "document does not support CSS selectors. Falling back " << "to using the Sizzle JavaScript CSS selector engine."; return this->FindElementsUsingSizzle(executor, parent_wrapper, criteria, found_elements); } } LOG(DEBUG) << "Using FindElements atom to locate element having " << LOGWSTRING(mechanism) << " = " << LOGWSTRING(criteria); CComPtr<IHTMLDocument2> doc; browser->GetDocument(&doc); std::wstring script_source(L"(function() { return ("); script_source += atoms::asString(atoms::FIND_ELEMENTS); script_source += L")})();"; Script script_wrapper(doc, script_source, 3); script_wrapper.AddArgument(mechanism); script_wrapper.AddArgument(criteria); if (parent_wrapper) { script_wrapper.AddArgument(parent_wrapper->element()); } status_code = script_wrapper.Execute(); if (status_code == WD_SUCCESS) { if (script_wrapper.ResultIsArray() || script_wrapper.ResultIsElementCollection()) { script_wrapper.ConvertResultToJsonValue(executor, found_elements); } else { LOG(WARN) << "Returned value is not an array or element collection"; status_code = ENOSUCHELEMENT; } } else { // An error in the execution of the FindElement atom for XPath is assumed // to be a syntactically invalid XPath. if (mechanism == L"xpath") { LOG(WARN) << "Attempted to find elements using invalid xpath: " << LOGWSTRING(criteria); status_code = EINVALIDSELECTOR; } else { LOG(WARN) << "Unexpected error attempting to find element by mechanism " << LOGWSTRING(mechanism) << " and criteria " << LOGWSTRING(criteria); status_code = ENOSUCHELEMENT; } } } else { LOG(WARN) << "Unable to get browser"; } return status_code; }