Skip to content

Commit

Permalink
JimEvans: Atomizing findElement and findElements in the IE driver. Fi…
Browse files Browse the repository at this point in the history
…nding by XPath and CSS selectors do not use the atoms yet.

r11013
  • Loading branch information
jimevans committed Jan 18, 2011
1 parent 8ff1540 commit 1fd778a
Show file tree
Hide file tree
Showing 15 changed files with 585 additions and 348 deletions.
58 changes: 2 additions & 56 deletions WebDriver.sln

Large diffs are not rendered by default.

24 changes: 10 additions & 14 deletions cpp/IEDriver/BrowserManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@
#include "ElementEqualsCommandHandler.h"
#include "ExecuteAsyncScriptCommandHandler.h"
#include "ExecuteScriptCommandHandler.h"
#include "FindByClassNameElementFinder.h"
#include "FindByCssSelectorElementFinder.h"
#include "FindByIdElementFinder.h"
#include "FindByLinkTextElementFinder.h"
#include "FindByNameElementFinder.h"
#include "FindByPartialLinkTextElementFinder.h"
#include "FindByTagNameElementFinder.h"
#include "FindByXPathElementFinder.h"
#include "FindChildElementCommandHandler.h"
#include "FindChildElementsCommandHandler.h"
Expand Down Expand Up @@ -354,14 +348,16 @@ void BrowserManager::BrowserQuittingEventHandler(std::wstring browser_id) {
}

void BrowserManager::PopulateElementFinderRepository(void) {
this->element_finders_[L"id"] = new FindByIdElementFinder;
this->element_finders_[L"name"] = new FindByNameElementFinder;
this->element_finders_[L"tag name"] = new FindByTagNameElementFinder;
this->element_finders_[L"link text"] = new FindByLinkTextElementFinder;
this->element_finders_[L"partial link text"] = new FindByPartialLinkTextElementFinder;
this->element_finders_[L"class name"] = new FindByClassNameElementFinder;
this->element_finders_[L"xpath"] = new FindByXPathElementFinder;
this->element_finders_[L"css selector"] = new FindByCssSelectorElementFinder;
// TODO (JimEvans): This is left over from a previous method of finding
// elements. This needs to be completely refactored.
this->element_finders_[L"id"] = new ElementFinder(L"id");
this->element_finders_[L"name"] = new ElementFinder(L"name");
this->element_finders_[L"tag name"] = new ElementFinder(L"tagName");
this->element_finders_[L"link text"] = new ElementFinder(L"linkText");
this->element_finders_[L"partial link text"] = new ElementFinder(L"partialLinkText");
this->element_finders_[L"class name"] = new ElementFinder(L"className");
this->element_finders_[L"xpath"] = new FindByXPathElementFinder(L"xpath");
this->element_finders_[L"css selector"] = new FindByCssSelectorElementFinder(L"css");
}

void BrowserManager::PopulateCommandHandlerRepository() {
Expand Down
178 changes: 67 additions & 111 deletions cpp/IEDriver/ElementFinder.cpp
Original file line number Diff line number Diff line change
@@ -1,146 +1,102 @@
#include "StdAfx.h"
#include "atoms.h"
#include "BrowserManager.h"

namespace webdriver {

ElementFinder::ElementFinder(void) {
ElementFinder::ElementFinder(std::wstring locator) {
this->locator_ = locator;
}

ElementFinder::~ElementFinder(void) {
}

int ElementFinder::FindElement(BrowserManager *manager, ElementWrapper *parent_wrapper, std::wstring criteria, ElementWrapper **found_element) {
int ElementFinder::FindElement(BrowserManager *manager, ElementWrapper *parent_wrapper, std::wstring criteria, Json::Value *found_element) {
BrowserWrapper *browser;
int status_code = manager->GetCurrentBrowser(&browser);
if (status_code == SUCCESS) {
IHTMLElement *parent_element;
status_code = this->GetParentElement(browser, parent_wrapper, &parent_element);
std::wstring criteria_object_script = L"(function() { return function(){ return { " + this->locator_ + L" : \"" + criteria + L"\" }; };})();";
ScriptWrapper *criteria_wrapper = new ScriptWrapper(criteria_object_script, 0);
status_code = browser->ExecuteScript(criteria_wrapper);
if (status_code == SUCCESS) {
IHTMLElement *element;
status_code = this->FindElementInternal(browser, parent_element, criteria, &element);
if (status_code == SUCCESS) {
ElementWrapper *wrapper;
manager->AddManagedElement(element, &wrapper);
*found_element = wrapper;
CComVariant criteria_object;
::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(L"(function() { return (");

// Read in all the scripts
for (int j = 0; FIND_ELEMENT[j]; j++) {
script += FIND_ELEMENT[j];
}

// Now for the magic and to close things
script += L")})();";

ScriptWrapper *script_wrapper = new ScriptWrapper(script, 2);
script_wrapper->AddArgument(criteria_object);
if (parent_wrapper) {
script_wrapper->AddArgument(parent_wrapper->element());
}

status_code = browser->ExecuteScript(script_wrapper);
if (status_code == SUCCESS && script_wrapper->ResultIsElement()) {
script_wrapper->ConvertResultToJsonValue(manager, found_element);
} else {
status_code = ENOSUCHELEMENT;
}
delete script_wrapper;
} else {
status_code = ENOSUCHELEMENT;
}
delete criteria_wrapper;
}
return status_code;
}

int ElementFinder::FindElements(BrowserManager *manager, ElementWrapper *parent_wrapper, std::wstring criteria, std::vector<ElementWrapper*> *found_elements) {
int ElementFinder::FindElements(BrowserManager *manager, ElementWrapper *parent_wrapper, std::wstring criteria, Json::Value *found_elements) {
BrowserWrapper *browser;
int status_code = manager->GetCurrentBrowser(&browser);
if (status_code == SUCCESS) {
IHTMLElement *parent_element;
status_code = this->GetParentElement(browser, parent_wrapper, &parent_element);
std::wstring criteria_object_script = L"(function() { return function(){ return { " + this->locator_ + L" : \"" + criteria + L"\" }; };})();";
ScriptWrapper *criteria_wrapper = new ScriptWrapper(criteria_object_script, 0);
status_code = browser->ExecuteScript(criteria_wrapper);
if (status_code == SUCCESS) {
std::vector<IHTMLElement*> raw_elements;
status_code = this->FindElementsInternal(browser, parent_element, criteria, &raw_elements);
std::vector<IHTMLElement*>::iterator begin = raw_elements.begin();
std::vector<IHTMLElement*>::iterator end = raw_elements.end();
for (std::vector<IHTMLElement*>::iterator it = begin; it != end; ++it) {
ElementWrapper *wrapper;
manager->AddManagedElement(*it, &wrapper);
found_elements->push_back(wrapper);
}
}
}
return status_code;
}

int ElementFinder::FindElementInternal(BrowserWrapper *browser, IHTMLElement *parent_element, std::wstring criteria, IHTMLElement **element) {
return ENOSUCHELEMENT;
}

int ElementFinder::FindElementsInternal(BrowserWrapper *browser, IHTMLElement *parent_element, std::wstring criteria, std::vector<IHTMLElement*> *elements) {
return ENOSUCHELEMENT;
}
CComVariant criteria_object;
::VariantCopy(&criteria_object, &criteria_wrapper->result());

void ElementFinder::GetHtmlDocument3(BrowserWrapper *browser, IHTMLDocument3 **doc3) {
CComPtr<IHTMLDocument2> doc;
browser->GetDocument(&doc);
// 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(L"(function() { return (");

CComQIPtr<IHTMLDocument3> doc3_qi_pointer(doc);
if (doc3_qi_pointer) {
*doc3 = doc3_qi_pointer.Detach();
}
}

void ElementFinder::ExtractHtmlDocument3FromDomNode(const IHTMLDOMNode* extraction_node, IHTMLDocument3** doc) {
CComQIPtr<IHTMLDOMNode2> element(const_cast<IHTMLDOMNode*>(extraction_node));

CComPtr<IDispatch> dispatch;
element->get_ownerDocument(&dispatch);

CComQIPtr<IHTMLDocument3> doc_qi_pointer(dispatch);
*doc = doc_qi_pointer.Detach();
}

void ElementFinder::ExtractHtmlDocument2FromDomNode(const IHTMLDOMNode* extraction_node, IHTMLDocument2** doc) {
CComQIPtr<IHTMLDOMNode2> element(const_cast<IHTMLDOMNode*>(extraction_node));
// Read in all the scripts
for (int j = 0; FIND_ELEMENTS[j]; j++) {
script += FIND_ELEMENTS[j];
}

CComPtr<IDispatch> dispatch;
element->get_ownerDocument(&dispatch);
// Now for the magic and to close things
script += L")})();";

CComQIPtr<IHTMLDocument2> doc_qi_pointer(dispatch);
*doc = doc_qi_pointer.Detach();
}
ScriptWrapper *script_wrapper = new ScriptWrapper(script, 2);
script_wrapper->AddArgument(criteria_object);
if (parent_wrapper) {
script_wrapper->AddArgument(parent_wrapper->element());
}

int ElementFinder::GetParentElement(BrowserWrapper *browser, ElementWrapper *parent_wrapper, IHTMLElement **parent_element) {
int status_code = SUCCESS;
if (parent_wrapper != NULL) {
*parent_element = parent_wrapper->element();
} else {
// No parent element specified, so get the root document
// element as the parent element.
CComPtr<IHTMLDocument3> root_doc;
this->GetHtmlDocument3(browser, &root_doc);
if (!root_doc) {
status_code = ENOSUCHDOCUMENT;
} else {
root_doc->get_documentElement(parent_element);
status_code = browser->ExecuteScript(script_wrapper);
if (status_code == SUCCESS) {
if (script_wrapper->ResultIsArray() || script_wrapper->ResultIsElementCollection()) {
script_wrapper->ConvertResultToJsonValue(manager, found_elements);
}
}
delete script_wrapper;
}
delete criteria_wrapper;
}

return status_code;
}

bool ElementFinder::IsOrUnder(const IHTMLDOMNode* root, IHTMLElement* child) {
CComQIPtr<IHTMLElement> parent(const_cast<IHTMLDOMNode*>(root));

if (!parent) {
return true;
}

VARIANT_BOOL to_return;
HRESULT hr = parent->contains(child, &to_return);
if (FAILED(hr)) {
// LOGHR(WARN, hr) << "Cannot determine if parent contains child node";
return false;
}

return to_return == VARIANT_TRUE;
}

bool ElementFinder::IsUnder(const IHTMLDOMNode* root, IHTMLElement* child) {
CComQIPtr<IHTMLDOMNode> child_node(child);
return this->IsOrUnder(root, child) && root != child_node;
}

std::wstring ElementFinder::StripTrailingWhitespace(std::wstring input) {
// TODO: make the whitespace finder more comprehensive.
std::wstring whitespace = L" \t\n\f\v\r";
if (input.length() == 0) {
return input;
}

size_t pos = input.find_last_not_of(whitespace);
if ((pos + 1) == input.length() || pos == std::string::npos) {
return input;
}

return input.substr(0, (pos + 1));
}

} // namespace webdriver
18 changes: 4 additions & 14 deletions cpp/IEDriver/ElementFinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,13 @@ class BrowserManager;
class ElementFinder
{
public:
ElementFinder(void);
ElementFinder(std::wstring locator);
virtual ~ElementFinder(void);
int FindElement(BrowserManager *manager, ElementWrapper *parent_wrapper, std::wstring criteria, ElementWrapper **found_element);
int FindElements(BrowserManager *manager, ElementWrapper *parent_wrapper, std::wstring criteria, std::vector<ElementWrapper*> *found_elements);

protected:
virtual int FindElementInternal(BrowserWrapper *browser, IHTMLElement *parent_element, std::wstring criteria, IHTMLElement **found_element);
virtual int FindElementsInternal(BrowserWrapper *browser, IHTMLElement *parent_element, std::wstring criteria, std::vector<IHTMLElement*> *found_elements);
void GetHtmlDocument3(BrowserWrapper *browser, IHTMLDocument3 **doc3);
void ExtractHtmlDocument2FromDomNode(const IHTMLDOMNode* extraction_node, IHTMLDocument2** doc);
void ExtractHtmlDocument3FromDomNode(const IHTMLDOMNode* extraction_node, IHTMLDocument3** doc);
bool IsOrUnder(const IHTMLDOMNode* root, IHTMLElement* child);
bool IsUnder(const IHTMLDOMNode* root, IHTMLElement* child);
std::wstring StripTrailingWhitespace(std::wstring input);
virtual int FindElement(BrowserManager *manager, ElementWrapper *parent_wrapper, std::wstring criteria, Json::Value *found_element);
virtual int FindElements(BrowserManager *manager, ElementWrapper *parent_wrapper, std::wstring criteria, Json::Value *found_elements);

private:
int GetParentElement(BrowserWrapper *browser, ElementWrapper *parent_wrapper, IHTMLElement **parent_element);
std::wstring locator_;
};

} // namespace webdriver
Expand Down
82 changes: 0 additions & 82 deletions cpp/IEDriver/ExecuteScriptCommandHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,88 +87,6 @@ class ExecuteScriptCommandHandler : public WebDriverCommandHandler {

return status_code;
}
//
// int ExecuteScriptCommandHandler::ConvertScriptResult(ScriptWrapper *script_wrapper, BrowserManager *manager, Json::Value *value) {
// int status_code = SUCCESS;
// CComVariant result(script_wrapper->result());
// if (script_wrapper->ResultIsString()) {
// std::string string_value;
// string_value = CW2A(script_wrapper->result().bstrVal, CP_UTF8);
// *value = string_value;
// } else if (script_wrapper->ResultIsInteger()) {
// *value = script_wrapper->result().lVal;
// } else if (script_wrapper->ResultIsDouble()) {
// *value = script_wrapper->result().dblVal;
// } else if (script_wrapper->ResultIsBoolean()) {
// *value = script_wrapper->result().boolVal == VARIANT_TRUE;
// } else if (script_wrapper->ResultIsEmpty()) {
// *value = Json::Value::null;
// } else if (script_wrapper->ResultIsIDispatch()) {
// if (script_wrapper->ResultIsArray() || script_wrapper->ResultIsElementCollection()) {
// BrowserWrapper *browser_wrapper;
// manager->GetCurrentBrowser(&browser_wrapper);
// Json::Value result_array(Json::arrayValue);
//
// long length = 0;
// this->GetArrayLength(script_wrapper, browser_wrapper, &length);
//
// for (long i = 0; i < length; ++i) {
// Json::Value array_item_result;
// int array_item_status = this->GetArrayItem(script_wrapper, browser_wrapper, manager, i, &array_item_result);
// result_array[i] = array_item_result;
// }
// *value = result_array;
// } else {
// IHTMLElement *node = (IHTMLElement*) script_wrapper->result().pdispVal;
// ElementWrapper *element_wrapper;
// manager->AddManagedElement(node, &element_wrapper);
// *value = element_wrapper->ConvertToJson();
// }
// } else {
// status_code = EUNKNOWNSCRIPTRESULT;
// }
// return status_code;
// }
//
//private:
// int ExecuteScriptCommandHandler::GetArrayLength(ScriptWrapper *array_script_wrapper, BrowserWrapper *browser_wrapper, long *length) {
// // Prepare an array for the Javascript execution, containing only one
// // element - the original returned array from a JS execution.
// std::wstring get_length_script(L"(function(){return function() {return arguments[0].length;}})();");
// ScriptWrapper *get_length_script_wrapper = new ScriptWrapper(get_length_script, 1);
// get_length_script_wrapper->AddArgument(array_script_wrapper->result());
// int length_result = browser_wrapper->ExecuteScript(get_length_script_wrapper);
//
// if (length_result != SUCCESS) {
// return length_result;
// }
//
// // Expect the return type to be an integer. A non-integer means this was
// // not an array after all.
// if (!get_length_script_wrapper->ResultIsInteger()) {
// return EUNEXPECTEDJSERROR;
// }
//
// *length = get_length_script_wrapper->result().lVal;
// delete get_length_script_wrapper;
// return SUCCESS;
// }
//
// int ExecuteScriptCommandHandler::GetArrayItem(ScriptWrapper *array_script_wrapper, BrowserWrapper *browser_wrapper, BrowserManager *manager, long index, Json::Value *item){
// std::wstring get_array_item_script(L"(function(){return function() {return arguments[0][arguments[1]];}})();");
// ScriptWrapper *get_array_item_script_wrapper = new ScriptWrapper(get_array_item_script, 2);
// get_array_item_script_wrapper->AddArgument(array_script_wrapper->result());
// get_array_item_script_wrapper->AddArgument(index);
// int get_item_result = browser_wrapper->ExecuteScript(get_array_item_script_wrapper);
// if (get_item_result != SUCCESS) {
// return get_item_result;
// }
//
// Json::Value array_item_result;
// int array_item_status = this->ConvertScriptResult(get_array_item_script_wrapper, manager, item);
// delete get_array_item_script_wrapper;
// return SUCCESS;
// }
};

} // namespace webdriver
Expand Down
Loading

0 comments on commit 1fd778a

Please sign in to comment.