LRESULT IECommandExecutor::OnWait(UINT uMsg,
                                  WPARAM wParam,
                                  LPARAM lParam,
                                  BOOL& bHandled) {
  BrowserHandle browser;
  int status_code = this->GetCurrentBrowser(&browser);
  if (status_code == SUCCESS && !browser->is_closing()) {
    this->is_waiting_ = !(browser->Wait());
    if (this->is_waiting_) {
      // If we are still waiting, we need to wait a bit then post a message to
      // ourselves to run the wait again. However, we can't wait using Sleep()
      // on this thread. This call happens in a message loop, and we would be 
      // unable to process the COM events in the browser if we put this thread
      // to sleep.
      unsigned int thread_id = 0;
      HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
                                                      0,
                                                      &IECommandExecutor::WaitThreadProc,
                                                      (void *)this->m_hWnd,
                                                      0,
                                                      &thread_id));
      if (thread_handle != NULL) {
        ::CloseHandle(thread_handle);
      }
    }
  } else {
    this->is_waiting_ = false;
  }
  return 0;
}
예제 #2
0
int ElementFinder::FindElementsByCssSelector(const IESessionWindow& session, const ElementHandle parent_wrapper, const std::wstring& criteria, Json::Value* found_elements) {
	int result = ENOSUCHELEMENT;

	BrowserHandle browser;
	result = session.GetCurrentBrowser(&browser);
	if (result != SUCCESS) {
		return result;
	}

	std::wstring script_source(L"(function() { return function(){");
	script_source += atoms::SIZZLE;
	script_source += L"\n";
	script_source += L"var root = arguments[1] ? arguments[1] : document.documentElement;";
	script_source += L"if (root['querySelectorAll']) { return root.querySelectorAll(arguments[0]); } ";
	script_source += L"var results = []; Sizzle(arguments[0], root, results);";
	script_source += L"return results;";
	script_source += L"};})();";

	CComPtr<IHTMLDocument2> doc;
	browser->GetDocument(&doc);

	Script script_wrapper(doc, script_source, 2);
	script_wrapper.AddArgument(criteria);
	if (parent_wrapper) {
		// Use a copy for the parent element?
		CComPtr<IHTMLElement> parent(parent_wrapper->element());
		IHTMLElement* parent_element_copy;
		HRESULT hr = parent.CopyTo(&parent_element_copy);
		script_wrapper.AddArgument(parent_element_copy);
	}

	result = script_wrapper.Execute();
	CComVariant snapshot = script_wrapper.result();

	std::wstring get_element_count_script = L"(function(){return function() {return arguments[0].length;}})();";
	Script get_element_count_script_wrapper(doc, get_element_count_script, 1);
	get_element_count_script_wrapper.AddArgument(snapshot);
	result = get_element_count_script_wrapper.Execute();
	if (result == SUCCESS) {
		if (!get_element_count_script_wrapper.ResultIsInteger()) {
			result = EUNEXPECTEDJSERROR;
		} else {
			long length = get_element_count_script_wrapper.result().lVal;
			std::wstring get_next_element_script = L"(function(){return function() {return arguments[0][arguments[1]];}})();";
			for (long i = 0; i < length; ++i) {
				Script get_element_script_wrapper(doc, get_next_element_script, 2);
				get_element_script_wrapper.AddArgument(snapshot);
				get_element_script_wrapper.AddArgument(i);
				result = get_element_script_wrapper.Execute();
				Json::Value json_element;
				get_element_script_wrapper.ConvertResultToJsonValue(session, &json_element);
				found_elements->append(json_element);
			}
		}
	}

	return result;
}
void IECommandExecutor::AddManagedBrowser(BrowserHandle browser_wrapper) {
  LOG(TRACE) << "Entering IECommandExecutor::AddManagedBrowser";

  this->managed_browsers_[browser_wrapper->browser_id()] = browser_wrapper;
  if (this->current_browser_id_ == "") {
    LOG(TRACE) << "Setting current browser id to " << browser_wrapper->browser_id();
    this->current_browser_id_ = browser_wrapper->browser_id();
  }
}
예제 #4
0
int ElementFinder::FindElementUsingSizzle(const IECommandExecutor& executor,
                                          const ElementHandle parent_wrapper,
                                          const std::wstring& criteria,
                                          Json::Value* found_element) {
  LOG(TRACE) << "Entering ElementFinder::FindElementUsingSizzle";

  int result;

  BrowserHandle browser;
  result = executor.GetCurrentBrowser(&browser);
  if (result != WD_SUCCESS) {
    LOG(WARN) << "Unable to get browser";
    return result;
  }

  std::wstring script_source(L"(function() { return function(){ if (!window.Sizzle) {");
  script_source += atoms::asString(atoms::SIZZLE);
  script_source += L"}\n";
  script_source += L"var root = arguments[1] ? arguments[1] : document.documentElement;";
  script_source += L"if (root['querySelector']) { return root.querySelector(arguments[0]); } ";
  script_source += L"var results = []; Sizzle(arguments[0], root, results);";
  script_source += L"return results.length > 0 ? results[0] : null;";
  script_source += L"};})();";

  CComPtr<IHTMLDocument2> doc;
  browser->GetDocument(&doc);
  Script script_wrapper(doc, script_source, 2);
  script_wrapper.AddArgument(criteria);
  if (parent_wrapper) {
    CComPtr<IHTMLElement> parent(parent_wrapper->element());
    script_wrapper.AddArgument(parent);
  }
  result = script_wrapper.Execute();

  if (result == WD_SUCCESS) {
    if (!script_wrapper.ResultIsElement()) {
      LOG(WARN) << "Found result is not element";
      result = ENOSUCHELEMENT;
    } else {
      result = script_wrapper.ConvertResultToJsonValue(executor,
                                                       found_element);
    }
  } else {
    LOG(WARN) << "Unable to find elements";
    result = ENOSUCHELEMENT;
  }

  return result;
}
예제 #5
0
void IECommandExecutor::DispatchCommand() {
  Response response(this->session_id_);
  CommandHandlerMap::const_iterator found_iterator = 
      this->command_handlers_.find(this->current_command_.command_type());

  if (found_iterator == this->command_handlers_.end()) {
    response.SetErrorResponse(501, "Command not implemented");
  } else {
    BrowserHandle browser;
    int status_code = this->GetCurrentBrowser(&browser);
    if (status_code == SUCCESS) {
      bool alert_is_active = false;
      HWND alert_handle = browser->GetActiveDialogWindowHandle();
      if (alert_handle != NULL) {
        // Found a window handle, make sure it's an actual alert,
        // and not a showModalDialog() window.
        vector<char> window_class_name(34);
        ::GetClassNameA(alert_handle, &window_class_name[0], 34);
        if (strcmp("#32770", &window_class_name[0]) == 0) {
          alert_is_active = true;
        }
      }
      int command_type = this->current_command_.command_type();
      if (alert_is_active &&
          command_type != GetAlertText &&
          command_type != SendKeysToAlert &&
          command_type != AcceptAlert &&
          command_type != DismissAlert) {
        response.SetErrorResponse(EMODALDIALOGOPENED, "Modal dialog present");
        ::SendMessage(alert_handle, WM_COMMAND, IDCANCEL, NULL);
        this->serialized_response_ = response.Serialize();
        return;
      }
    }

	CommandHandlerHandle command_handler = found_iterator->second;
    command_handler->Execute(*this, this->current_command_, &response);

    status_code = this->GetCurrentBrowser(&browser);
    if (status_code == SUCCESS) {
      this->is_waiting_ = browser->wait_required();
      if (this->is_waiting_) {
        ::PostMessage(this->m_hWnd, WD_WAIT, NULL, NULL);
      }
    }
  }

  this->serialized_response_ = response.Serialize();
}
예제 #6
0
int ElementFinder::FindElement(const IESessionWindow& session, const ElementHandle parent_wrapper, const std::wstring& mechanism, const std::wstring& criteria, Json::Value* found_element) {
	BrowserHandle browser;
	int status_code = session.GetCurrentBrowser(&browser);
	if (status_code == SUCCESS) {
		if (mechanism == L"css") {
			return this->FindElementByCssSelector(session, parent_wrapper, criteria, found_element);
		} else if (mechanism == L"xpath") {
			return this->FindElementByXPath(session, parent_wrapper, criteria, found_element);
		} else {
			std::wstring criteria_object_script = L"(function() { return function(){ return  { \"" + mechanism + L"\" : \"" + criteria + L"\" }; };})();";
			CComPtr<IHTMLDocument2> doc;
			browser->GetDocument(&doc);

			Script criteria_wrapper(doc, criteria_object_script, 0);
			status_code = criteria_wrapper.Execute();
			if (status_code == SUCCESS) {
				CComVariant criteria_object;
				HRESULT hr = ::VariantCopy(&criteria_object, &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::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 == SUCCESS && script_wrapper.ResultIsElement()) {
					script_wrapper.ConvertResultToJsonValue(session, found_element);
				} else {
					status_code = ENOSUCHELEMENT;
				}
			} else {
				status_code = ENOSUCHELEMENT;
			}
		}
	}
	return status_code;
}
예제 #7
0
int ElementFinder::FindElementByXPath(const IESessionWindow& session, const ElementHandle parent_wrapper, const std::wstring& criteria, Json::Value* found_element) {
	int result = ENOSUCHELEMENT;

	BrowserHandle browser;
	result = session.GetCurrentBrowser(&browser);
	if (result != SUCCESS) {
		return result;
	}

	result = this->InjectXPathEngine(browser);
	// TODO(simon): Why does the injecting sometimes fail?
	if (result != SUCCESS) {
		return result;
	}

	// Call it
	std::wstring query;
	if (parent_wrapper) {
		query += L"(function() { return function(){var res = document.__webdriver_evaluate(arguments[0], arguments[1], null, 7, null); return res.snapshotItem(0) ;};})();";
	} else {
		query += L"(function() { return function(){var res = document.__webdriver_evaluate(arguments[0], document, null, 7, null); return res.snapshotLength != 0 ? res.snapshotItem(0) : undefined ;};})();";
	}

	CComPtr<IHTMLDocument2> doc;
	browser->GetDocument(&doc);
	Script script_wrapper(doc, query, 2);
	script_wrapper.AddArgument(criteria);
	if (parent_wrapper) {
		CComPtr<IHTMLElement> parent(parent_wrapper->element());
		IHTMLElement* parent_element_copy;
		HRESULT hr = parent.CopyTo(&parent_element_copy);
		script_wrapper.AddArgument(parent_element_copy);
	}
	result = script_wrapper.Execute();

	if (result == SUCCESS) {
		if (!script_wrapper.ResultIsElement()) {
			result = ENOSUCHELEMENT;
		} else {
			result = script_wrapper.ConvertResultToJsonValue(session, found_element);
		}
	}

	return result;
}
예제 #8
0
bool ElementFinder::HasNativeCssSelectorEngine(const IECommandExecutor& executor) {
  LOG(TRACE) << "Entering ElementFinder::HasNativeCssSelectorEngine";

  BrowserHandle browser;
  executor.GetCurrentBrowser(&browser);

  std::wstring script_source(L"(function() { return function(){");
  script_source += L"var root = document.documentElement;";
  script_source += L"if (root['querySelectorAll']) { return true; } ";
  script_source += L"return false;";
  script_source += L"};})();";

  CComPtr<IHTMLDocument2> doc;
  browser->GetDocument(&doc);

  Script script_wrapper(doc, script_source, 0);
  script_wrapper.Execute();
  return script_wrapper.result().boolVal == VARIANT_TRUE;
}
예제 #9
0
  HRESULT ScreenshotCommandHandler::CaptureViewport(BrowserHandle browser) {
    LOG(TRACE) << "Entering ScreenshotCommandHandler::CaptureViewport";

    HWND content_window_handle = browser->GetContentWindowHandle();
    int content_width = 0, content_height = 0;

    this->GetWindowDimensions(content_window_handle, &content_width, &content_height);
    CaptureWindow(content_window_handle, content_width, content_height);

    return S_OK;
  }
LRESULT IECommandExecutor::OnWait(UINT uMsg,
                                  WPARAM wParam,
                                  LPARAM lParam,
                                  BOOL& bHandled) {
  LOG(TRACE) << "Entering IECommandExecutor::OnWait";

  BrowserHandle browser;
  int status_code = this->GetCurrentBrowser(&browser);
  if (status_code == WD_SUCCESS && !browser->is_closing()) {
    if (this->page_load_timeout_ >= 0 && this->wait_timeout_ < clock()) {
      Response timeout_response;
      timeout_response.SetErrorResponse(ETIMEOUT, "Timed out waiting for page to load.");
      this->serialized_response_ = timeout_response.Serialize();
      this->is_waiting_ = false;
      browser->set_wait_required(false);
    } else {
      this->is_waiting_ = !(browser->Wait());
      if (this->is_waiting_) {
        // If we are still waiting, we need to wait a bit then post a message to
        // ourselves to run the wait again. However, we can't wait using Sleep()
        // on this thread. This call happens in a message loop, and we would be 
        // unable to process the COM events in the browser if we put this thread
        // to sleep.
        unsigned int thread_id = 0;
        HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
                                                        0,
                                                        &IECommandExecutor::WaitThreadProc,
                                                        (void *)this->m_hWnd,
                                                        0,
                                                        &thread_id));
        if (thread_handle != NULL) {
          ::CloseHandle(thread_handle);
        }
      }
    }
  } else {
    this->is_waiting_ = false;
  }
  return 0;
}
예제 #11
0
int ElementFinder::FindElementByCssSelector(const IESessionWindow& session, const ElementHandle parent_wrapper, const std::wstring& criteria, Json::Value* found_element) {
	int result = ENOSUCHELEMENT;

	BrowserHandle browser;
	result = session.GetCurrentBrowser(&browser);
	if (result != SUCCESS) {
		return result;
	}

	std::wstring script_source(L"(function() { return function(){");
	script_source += atoms::SIZZLE;
	script_source += L"\n";
	script_source += L"var root = arguments[1] ? arguments[1] : document.documentElement;";
	script_source += L"if (root['querySelector']) { return root.querySelector(arguments[0]); } ";
	script_source += L"var results = []; Sizzle(arguments[0], root, results);";
	script_source += L"return results.length > 0 ? results[0] : null;";
	script_source += L"};})();";

	CComPtr<IHTMLDocument2> doc;
	browser->GetDocument(&doc);
	Script script_wrapper(doc, script_source, 2);
	script_wrapper.AddArgument(criteria);
	if (parent_wrapper) {
		CComPtr<IHTMLElement> parent(parent_wrapper->element());
		IHTMLElement* parent_element_copy;
		HRESULT hr = parent.CopyTo(&parent_element_copy);
		script_wrapper.AddArgument(parent_element_copy);
	}
	result = script_wrapper.Execute();

	if (result == SUCCESS) {
		if (!script_wrapper.ResultIsElement()) {
			result = ENOSUCHELEMENT;
		} else {
			result = script_wrapper.ConvertResultToJsonValue(session, found_element);
		}
	}

	return result;
}
예제 #12
0
void IESessionWindow::DispatchCommand() {
	std::string session_id = CW2A(this->session_id_.c_str(), CP_UTF8);
	Response response(session_id);
	CommandHandlerMap::const_iterator found_iterator = this->command_handlers_.find(this->current_command_.command_value());

	if (found_iterator == this->command_handlers_.end()) {
		response.SetErrorResponse(501, "Command not implemented");
	} else {
		CommandHandlerHandle command_handler = found_iterator->second;
		command_handler->Execute(*this, this->current_command_, &response);

		BrowserHandle browser;
		int status_code = this->GetCurrentBrowser(&browser);
		if (status_code == SUCCESS) {
			this->is_waiting_ = browser->wait_required();
			if (this->is_waiting_) {
				::PostMessage(this->m_hWnd, WD_WAIT, NULL, NULL);
			}
		}
	}

	this->serialized_response_ = response.Serialize();
}
예제 #13
0
int ElementFinder::InjectXPathEngine(BrowserHandle browser_wrapper) {
	// Inject the XPath engine
	std::wstring script_source;
	for (int i = 0; XPATHJS[i]; i++) {
		script_source += XPATHJS[i];
	}

	CComPtr<IHTMLDocument2> doc;
	browser_wrapper->GetDocument(&doc);
	Script script_wrapper(doc, script_source, 0);
	int status_code = script_wrapper.Execute();

	return status_code;
}
예제 #14
0
int ClickElementCommandHandler::ExecuteAtom(
    const std::wstring& atom_script_source,
    BrowserHandle browser_wrapper,
    ElementHandle element_wrapper,
    std::string* error_msg) {
  CComPtr<IHTMLDocument2> doc;
  browser_wrapper->GetDocument(&doc);
  Script script_wrapper(doc, atom_script_source, 1);
  script_wrapper.AddArgument(element_wrapper);
  int status_code = script_wrapper.ExecuteAsync(ASYNC_SCRIPT_EXECUTION_TIMEOUT_IN_MILLISECONDS);
  if (status_code != WD_SUCCESS) {
    if (script_wrapper.ResultIsString()) {
      std::wstring error = script_wrapper.result().bstrVal;
      *error_msg = StringUtilities::ToString(error);
    } else {
      std::string error = "Executing JavaScript click function returned an";
      error.append(" unexpected error, but no error could be returned from");
      error.append(" Internet Explorer's JavaScript engine.");
      *error_msg = error;
    }
  }
  return status_code;
}
예제 #15
0
void IESessionWindow::AddManagedBrowser(BrowserHandle browser_wrapper) {
	this->managed_browsers_[browser_wrapper->browser_id()] = browser_wrapper;
	if (this->current_browser_id_ == L"") {
		this->current_browser_id_ = browser_wrapper->browser_id();
	}
}
void IECommandExecutor::AddManagedBrowser(BrowserHandle browser_wrapper) {
  this->managed_browsers_[browser_wrapper->browser_id()] = browser_wrapper;
  if (this->current_browser_id_ == "") {
    this->current_browser_id_ = browser_wrapper->browser_id();
  }
}
예제 #17
0
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;
}
예제 #18
0
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;
}
예제 #19
0
int ElementFinder::FindElementsUsingSizzle(const IECommandExecutor& executor,
                                           const ElementHandle parent_wrapper,
                                           const std::wstring& criteria,
                                           Json::Value* found_elements) {
  LOG(TRACE) << "Entering ElementFinder::FindElementsUsingSizzle";

  int result;

  if (criteria == L"") {
    // Apparently, Sizzle will happily return an empty array for an empty
    // string as the selector. We do not want this.
    return ENOSUCHELEMENT;
  }

  BrowserHandle browser;
  result = executor.GetCurrentBrowser(&browser);
  if (result != WD_SUCCESS) {
    LOG(WARN) << "Unable to get browser";
    return result;
  }

  std::wstring script_source(L"(function() { return function(){ if (!window.Sizzle) {");
  script_source += atoms::asString(atoms::SIZZLE);
  script_source += L"}\n";
  script_source += L"var root = arguments[1] ? arguments[1] : document.documentElement;";
  script_source += L"if (root['querySelectorAll']) { return root.querySelectorAll(arguments[0]); } ";
  script_source += L"var results = []; try { Sizzle(arguments[0], root, results); } catch(ex) { results = null; }";
  script_source += L"return results;";
  script_source += L"};})();";

  CComPtr<IHTMLDocument2> doc;
  browser->GetDocument(&doc);

  Script script_wrapper(doc, script_source, 2);
  script_wrapper.AddArgument(criteria);
  if (parent_wrapper) {
    // Use a copy for the parent element?
    CComPtr<IHTMLElement> parent(parent_wrapper->element());
    script_wrapper.AddArgument(parent);
  }

  result = script_wrapper.Execute();
  if (result == WD_SUCCESS) {
    CComVariant snapshot = script_wrapper.result();
    if (snapshot.vt == VT_NULL || snapshot.vt == VT_EMPTY) {
      // We explicitly caught an error from Sizzle. Return ENOSUCHELEMENT.
      return ENOSUCHELEMENT;
    }
    std::wstring get_element_count_script = L"(function(){return function() {return arguments[0].length;}})();";
    Script get_element_count_script_wrapper(doc, get_element_count_script, 1);
    get_element_count_script_wrapper.AddArgument(snapshot);
    result = get_element_count_script_wrapper.Execute();
    if (result == WD_SUCCESS) {
      *found_elements = Json::Value(Json::arrayValue);
      if (!get_element_count_script_wrapper.ResultIsInteger()) {
        LOG(WARN) << "Found elements count is not integer";
        result = EUNEXPECTEDJSERROR;
      } else {
        long length = get_element_count_script_wrapper.result().lVal;
        std::wstring get_next_element_script = L"(function(){return function() {return arguments[0][arguments[1]];}})();";
        for (long i = 0; i < length; ++i) {
          Script get_element_script_wrapper(doc, get_next_element_script, 2);
          get_element_script_wrapper.AddArgument(snapshot);
          get_element_script_wrapper.AddArgument(i);
          result = get_element_script_wrapper.Execute();
          if (result == WD_SUCCESS) {
            Json::Value json_element;
            get_element_script_wrapper.ConvertResultToJsonValue(executor,
                                                            &json_element);
            found_elements->append(json_element);
          } else {
            LOG(WARN) << "Unable to get " << i << " found element";
          }
        }
      }
    } else {
      LOG(WARN) << "Unable to get count of found elements";
      result = EUNEXPECTEDJSERROR;
    }

  } else {
    LOG(WARN) << "Execution returned error";
  }

  return result;
}
예제 #20
0
int ElementFinder::FindElementsByXPath(const IESessionWindow& session, const ElementHandle parent_wrapper, const std::wstring& criteria, Json::Value* found_elements) {
	int result = ENOSUCHELEMENT;

	BrowserHandle browser;
	result = session.GetCurrentBrowser(&browser);
	if (result != SUCCESS) {
		return result;
	}
	result = this->InjectXPathEngine(browser);
	// TODO(simon): Why does the injecting sometimes fail?
	if (result != SUCCESS) {
		return result;
	}

	// Call it
	std::wstring query;
	if (parent_wrapper) {
		query += L"(function() { return function() {var res = document.__webdriver_evaluate(arguments[0], arguments[1], null, 7, null); return res;};})();";
	} else {
		query += L"(function() { return function() {var res = document.__webdriver_evaluate(arguments[0], document, null, 7, null); return res;};})();";
	}

	CComPtr<IHTMLDocument2> doc;
	browser->GetDocument(&doc);

	Script script_wrapper(doc, query, 2);
	script_wrapper.AddArgument(criteria);
	if (parent_wrapper) {
		// Use a copy for the parent element?
		CComPtr<IHTMLElement> parent(parent_wrapper->element());
		IHTMLElement* parent_element_copy;
		HRESULT hr = parent.CopyTo(&parent_element_copy);
		script_wrapper.AddArgument(parent_element_copy);
	}

	result = script_wrapper.Execute();
	CComVariant snapshot = script_wrapper.result();

	std::wstring get_element_count_script = L"(function(){return function() {return arguments[0].snapshotLength;}})();";
	Script get_element_count_script_wrapper(doc, get_element_count_script, 1);
	get_element_count_script_wrapper.AddArgument(snapshot);
	result = get_element_count_script_wrapper.Execute();
	if (result == SUCCESS) {
		if (!get_element_count_script_wrapper.ResultIsInteger()) {
			result = EUNEXPECTEDJSERROR;
		} else {
			long length = get_element_count_script_wrapper.result().lVal;
			std::wstring get_next_element_script(L"(function(){return function() {return arguments[0].iterateNext();}})();");
			for (long i = 0; i < length; ++i) {
				Script get_element_script_wrapper(doc, get_next_element_script, 2);
				get_element_script_wrapper.AddArgument(snapshot);
				get_element_script_wrapper.AddArgument(i);
				result = get_element_script_wrapper.Execute();
				Json::Value json_element;
				get_element_script_wrapper.ConvertResultToJsonValue(session, &json_element);
				found_elements->append(json_element);
			}
		}
	}

	return result;
}
void IECommandExecutor::DispatchCommand() {
  LOG(TRACE) << "Entering IECommandExecutor::DispatchCommand";

  Response response(this->session_id_);
  CommandHandlerMap::const_iterator found_iterator = 
      this->command_handlers_.find(this->current_command_.command_type());

  if (found_iterator == this->command_handlers_.end()) {
    LOG(WARN) << "Unable to find command handler for " << this->current_command_.command_type();
    response.SetErrorResponse(501, "Command not implemented");
  } else {
    BrowserHandle browser;
    int status_code = WD_SUCCESS;
    if (this->current_command_.command_type() != webdriver::CommandType::NewSession) {
      // There should never be a modal dialog or alert to check for if the command
      // is the "newSession" command.
      status_code = this->GetCurrentBrowser(&browser);
      if (status_code == WD_SUCCESS) {
        bool alert_is_active = false;
        HWND alert_handle = browser->GetActiveDialogWindowHandle();
        if (alert_handle != NULL) {
          // Found a window handle, make sure it's an actual alert,
          // and not a showModalDialog() window.
          vector<char> window_class_name(34);
          ::GetClassNameA(alert_handle, &window_class_name[0], 34);
          if (strcmp(ALERT_WINDOW_CLASS, &window_class_name[0]) == 0) {
            alert_is_active = true;
          } else {
            LOG(WARN) << "Found alert handle does not have a window class consistent with an alert";
          }
        } else {
          LOG(DEBUG) << "No alert handle is found";
        }
        if (alert_is_active) {
          Alert dialog(browser, alert_handle);
          std::string command_type = this->current_command_.command_type();
          if (command_type == webdriver::CommandType::GetAlertText ||
              command_type == webdriver::CommandType::SendKeysToAlert ||
              command_type == webdriver::CommandType::AcceptAlert ||
              command_type == webdriver::CommandType::DismissAlert) {
            LOG(DEBUG) << "Alert is detected, and the sent command is valid";
          } else {
            LOG(DEBUG) << "Unexpected alert is detected, and the sent command is invalid when an alert is present";
            std::string alert_text = dialog.GetText();
            if (this->unexpected_alert_behavior_ == ACCEPT_UNEXPECTED_ALERTS) {
              LOG(DEBUG) << "Automatically accepting the alert";
              dialog.Accept();
            } else if (this->unexpected_alert_behavior_ == DISMISS_UNEXPECTED_ALERTS || command_type == webdriver::CommandType::Quit) {
              // If a quit command was issued, we should not ignore an unhandled
              // alert, even if the alert behavior is set to "ignore".
              LOG(DEBUG) << "Automatically dismissing the alert";
              if (dialog.is_standard_alert()) {
                dialog.Dismiss();
              } else {
                // The dialog was non-standard. The most common case of this is
                // an onBeforeUnload dialog, which must be accepted to continue.
                dialog.Accept();
              }
            }
            if (command_type != webdriver::CommandType::Quit) {
              // To keep pace with what Firefox does, we'll return the text of the
              // alert in the error response.
              Json::Value response_value;
              response_value["message"] = "Modal dialog present";
              response_value["alert"]["text"] = alert_text;
              response.SetResponse(EMODALDIALOGOPENED, response_value);
              this->serialized_response_ = response.Serialize();
              return;
            } else {
              LOG(DEBUG) << "Quit command was issued. Continuing with command after automatically closing alert.";
            }
          }
        }
      } else {
        LOG(WARN) << "Unable to find current browser";
      }
    }
	  CommandHandlerHandle command_handler = found_iterator->second;
    command_handler->Execute(*this, this->current_command_, &response);

    status_code = this->GetCurrentBrowser(&browser);
    if (status_code == WD_SUCCESS) {
      this->is_waiting_ = browser->wait_required();
      if (this->is_waiting_) {
        if (this->page_load_timeout_ >= 0) {
          this->wait_timeout_ = clock() + (this->page_load_timeout_ / 1000 * CLOCKS_PER_SEC);
        }
        ::PostMessage(this->m_hWnd, WD_WAIT, NULL, NULL);
      }
    } else {
      if (this->current_command_.command_type() != webdriver::CommandType::Quit) {
        LOG(WARN) << "Unable to get current browser";
      }
    }
  }

  this->serialized_response_ = response.Serialize();
}
예제 #22
0
  HRESULT ScreenshotCommandHandler::CaptureFullPage(BrowserHandle browser) {
    LOG(TRACE) << "Entering ScreenshotCommandHandler::CaptureFullPage";

    HWND ie_window_handle = browser->GetTopLevelWindowHandle();
    HWND content_window_handle = browser->GetContentWindowHandle();

    CComPtr<IHTMLDocument2> document;
    browser->GetDocument(true, &document);
    if (!document) {
      LOG(WARN) << "Unable to get document from browser. Are you viewing a non-HTML document?";
      return E_ABORT;
    }

    LocationInfo document_info;
    bool result = DocumentHost::GetDocumentDimensions(document, &document_info);
    if (!result) {
      LOG(DEBUG) << "Unable to get document dimensions";
      return E_FAIL;
    }
    LOG(DEBUG) << "Initial document sizes (scrollWidth, scrollHeight) are (w, h): "
               << document_info.width << ", " << document_info.height;

    int chrome_width(0);
    int chrome_height(0);
    this->GetBrowserChromeDimensions(ie_window_handle,
                                     content_window_handle,
                                     &chrome_width,
                                     &chrome_height);
    LOG(DEBUG) << "Initial chrome sizes are (w, h): "
               << chrome_width << ", " << chrome_height;

    int target_window_width = document_info.width + chrome_width;
    int target_window_height = document_info.height + chrome_height;

    // For some reason, this technique does not allow the user to resize
    // the browser window to greater than SIZE_LIMIT x SIZE_LIMIT. This is
    // pretty big, so we'll cap the allowable screenshot size to that.
    //
    // GDI+ limit after which it may report Generic error for some image types
    int SIZE_LIMIT = 65534; 
    if (target_window_height > SIZE_LIMIT) {
      LOG(WARN) << "Required height is greater than limit. Truncating screenshot height.";
      target_window_height = SIZE_LIMIT;
      document_info.height = target_window_height - chrome_height;
    }
    if (target_window_width > SIZE_LIMIT) {
      LOG(WARN) << "Required width is greater than limit. Truncating screenshot width.";
      target_window_width = SIZE_LIMIT;
      document_info.width = target_window_width - chrome_width;
    }

    long original_width = browser->GetWidth();
    long original_height = browser->GetHeight();
    LOG(DEBUG) << "Initial browser window sizes are (w, h): "
               << original_width << ", " << original_height;

    // If the window is already wide enough to accomodate
    // the document, don't resize that dimension. Otherwise,
    // the window will display a horizontal scroll bar, and
    // we need to retain the scrollbar to avoid rerendering
    // during the resize, so reduce the target window width
    // by two pixels.
    if (original_width > target_window_width) {
      target_window_width = original_width;
    } else {
      target_window_width -= 2;
    }

    // If the window is already tall enough to accomodate
    // the document, don't resize that dimension. Otherwise,
    // the window will display a vertical scroll bar, and
    // we need to retain the scrollbar to avoid rerendering
    // during the resize, so reduce the target window height
    // by two pixels.
    if (original_height > target_window_height) {
      target_window_height = original_height;
    } else {
      target_window_height -= 2;
    }

    BOOL is_maximized = ::IsZoomed(ie_window_handle);
    bool requires_resize = original_width < target_window_width ||
                           original_height < target_window_height;

    if (requires_resize) {
      // The resize message is being ignored if the window appears to be
      // maximized.  There's likely a way to bypass that. The kludgy way 
      // is to unmaximize the window, then move on with setting the window
      // to the dimensions we really want.  This is okay because we revert
      // back to the original dimensions afterward.
      if (is_maximized) {
        LOG(DEBUG) << "Window is maximized currently. Demaximizing.";
        ::ShowWindow(ie_window_handle, SW_SHOWNORMAL);
      }

      // NOTE: There is a *very* slight chance that resizing the window
      // so there are no longer scroll bars to be displayed *might* cause
      // layout redraws such that the screenshot does not show the entire
      // DOM after the resize. Since we should always be expanding the
      // window size, never contracting it, this is a corner case that
      // explicitly will *not* be fixed. Any issue reports describing this
      // corner case will be closed without action.
      RECT ie_window_rect;
      ::GetWindowRect(ie_window_handle, &ie_window_rect);
      ::SetWindowPos(ie_window_handle,
                     NULL, 
                     ie_window_rect.left,
                     ie_window_rect.top,
                     target_window_width,
                     target_window_height,
                     SWP_NOSENDCHANGING);
    }

    CaptureWindow(content_window_handle, document_info.width, document_info.height);

    if (requires_resize) {
      // Restore the browser to the original dimensions.
      if (is_maximized) {
        ::ShowWindow(ie_window_handle, SW_MAXIMIZE);
      } else {
        browser->SetHeight(original_height);
        browser->SetWidth(original_width);
      }
    }

    return S_OK;
  }
예제 #23
0
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;
}