summaryrefslogtreecommitdiffstats
path: root/chrome/test/webdriver
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-16 15:20:01 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-16 15:20:01 +0000
commit3e981b2931608892407246bd2823a36097df16f4 (patch)
tree7ca1f69c2cc09d7240ad0bec7a1ce10ea7cbf08e /chrome/test/webdriver
parent21d6745575eabb50da11d49c76173fbaf28c8b1c (diff)
downloadchromium_src-3e981b2931608892407246bd2823a36097df16f4.zip
chromium_src-3e981b2931608892407246bd2823a36097df16f4.tar.gz
chromium_src-3e981b2931608892407246bd2823a36097df16f4.tar.bz2
Refactor chromedriver's script execution to reduce amount of custom Value parsing.
Also, rename utility_functions to webdriver_util and alphabetize gyp target. BUG=none TEST=none Review URL: http://codereview.chromium.org/7522024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96945 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/webdriver')
-rw-r--r--chrome/test/webdriver/automation.cc44
-rw-r--r--chrome/test/webdriver/automation.h21
-rw-r--r--chrome/test/webdriver/commands/mouse_commands.cc42
-rw-r--r--chrome/test/webdriver/commands/webelement_commands.cc9
-rw-r--r--chrome/test/webdriver/dispatch.cc2
-rw-r--r--chrome/test/webdriver/server.cc2
-rw-r--r--chrome/test/webdriver/session.cc862
-rw-r--r--chrome/test/webdriver/session.h60
-rw-r--r--chrome/test/webdriver/utility_functions.h32
-rw-r--r--chrome/test/webdriver/web_element_id.cc24
-rw-r--r--chrome/test/webdriver/web_element_id.h12
-rw-r--r--chrome/test/webdriver/webdriver_basic_types.cc184
-rw-r--r--chrome/test/webdriver/webdriver_basic_types.h89
-rw-r--r--chrome/test/webdriver/webdriver_util.cc (renamed from chrome/test/webdriver/utility_functions.cc)18
-rw-r--r--chrome/test/webdriver/webdriver_util.h88
-rw-r--r--chrome/test/webdriver/webdriver_util_mac.mm (renamed from chrome/test/webdriver/utility_functions_mac.mm)2
-rw-r--r--chrome/test/webdriver/webdriver_util_unittest.cc (renamed from chrome/test/webdriver/utility_functions_unittest.cc)2
17 files changed, 873 insertions, 620 deletions
diff --git a/chrome/test/webdriver/automation.cc b/chrome/test/webdriver/automation.cc
index acca8e8..87dd8d3 100644
--- a/chrome/test/webdriver/automation.cc
+++ b/chrome/test/webdriver/automation.cc
@@ -35,9 +35,9 @@
#include "chrome/test/automation/proxy_launcher.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/webdriver/frame_path.h"
-#include "chrome/test/webdriver/utility_functions.h"
+#include "chrome/test/webdriver/webdriver_basic_types.h"
#include "chrome/test/webdriver/webdriver_error.h"
-#include "ui/gfx/point.h"
+#include "chrome/test/webdriver/webdriver_util.h"
#if defined(OS_WIN)
#include "base/win/registry.h"
@@ -302,7 +302,7 @@ void Automation::ExecuteScript(int tab_id,
}
void Automation::MouseMove(int tab_id,
- const gfx::Point& p,
+ const Point& p,
Error** error) {
int windex = 0, tab_index = 0;
*error = GetIndicesForTab(tab_id, &windex, &tab_index);
@@ -311,13 +311,14 @@ void Automation::MouseMove(int tab_id,
std::string error_msg;
if (!SendMouseMoveJSONRequest(
- automation(), windex, tab_index, p.x(), p.y(), &error_msg)) {
+ automation(), windex, tab_index, p.rounded_x(), p.rounded_y(),
+ &error_msg)) {
*error = new Error(kUnknownError, error_msg);
}
}
void Automation::MouseClick(int tab_id,
- const gfx::Point& p,
+ const Point& p,
automation::MouseButton button,
Error** error) {
int windex = 0, tab_index = 0;
@@ -327,14 +328,15 @@ void Automation::MouseClick(int tab_id,
std::string error_msg;
if (!SendMouseClickJSONRequest(
- automation(), windex, tab_index, button, p.x(), p.y(), &error_msg)) {
+ automation(), windex, tab_index, button, p.rounded_x(),
+ p.rounded_y(), &error_msg)) {
*error = new Error(kUnknownError, error_msg);
}
}
void Automation::MouseDrag(int tab_id,
- const gfx::Point& start,
- const gfx::Point& end,
+ const Point& start,
+ const Point& end,
Error** error) {
int windex = 0, tab_index = 0;
*error = GetIndicesForTab(tab_id, &windex, &tab_index);
@@ -342,14 +344,15 @@ void Automation::MouseDrag(int tab_id,
return;
std::string error_msg;
- if (!SendMouseDragJSONRequest(automation(), windex, tab_index, start.x(),
- start.y(), end.x(), end.y(), &error_msg)) {
+ if (!SendMouseDragJSONRequest(
+ automation(), windex, tab_index, start.rounded_x(), start.rounded_y(),
+ end.rounded_x(), end.rounded_y(), &error_msg)) {
*error = new Error(kUnknownError, error_msg);
}
}
void Automation::MouseButtonUp(int tab_id,
- const gfx::Point& p,
+ const Point& p,
Error** error) {
*error = CheckAdvancedInteractionsSupported();
if (*error)
@@ -362,13 +365,14 @@ void Automation::MouseButtonUp(int tab_id,
std::string error_msg;
if (!SendMouseButtonUpJSONRequest(
- automation(), windex, tab_index, p.x(), p.y(), &error_msg)) {
+ automation(), windex, tab_index, p.rounded_x(), p.rounded_y(),
+ &error_msg)) {
*error = new Error(kUnknownError, error_msg);
}
}
void Automation::MouseButtonDown(int tab_id,
- const gfx::Point& p,
+ const Point& p,
Error** error) {
*error = CheckAdvancedInteractionsSupported();
if (*error)
@@ -381,13 +385,14 @@ void Automation::MouseButtonDown(int tab_id,
std::string error_msg;
if (!SendMouseButtonDownJSONRequest(
- automation(), windex, tab_index, p.x(), p.y(), &error_msg)) {
+ automation(), windex, tab_index, p.rounded_x(), p.rounded_y(),
+ &error_msg)) {
*error = new Error(kUnknownError, error_msg);
}
}
void Automation::MouseDoubleClick(int tab_id,
- const gfx::Point& p,
+ const Point& p,
Error** error) {
*error = CheckAdvancedInteractionsSupported();
if (*error)
@@ -400,13 +405,14 @@ void Automation::MouseDoubleClick(int tab_id,
std::string error_msg;
if (!SendMouseDoubleClickJSONRequest(
- automation(), windex, tab_index, p.x(), p.y(), &error_msg)) {
+ automation(), windex, tab_index, p.rounded_x(), p.rounded_y(),
+ &error_msg)) {
*error = new Error(kUnknownError, error_msg);
}
}
void Automation::DragAndDropFilePaths(
- int tab_id, const gfx::Point& location,
+ int tab_id, const Point& location,
const std::vector<FilePath::StringType>& paths, Error** error) {
int windex = 0, tab_index = 0;
*error = GetIndicesForTab(tab_id, &windex, &tab_index);
@@ -416,8 +422,8 @@ void Automation::DragAndDropFilePaths(
std::string error_msg;
if (!SendDragAndDropFilePathsJSONRequest(
- automation(), windex, tab_index, location.x(), location.y(), paths,
- &error_msg)) {
+ automation(), windex, tab_index, location.rounded_x(),
+ location.rounded_y(), paths, &error_msg)) {
*error = new Error(kUnknownError, error_msg);
}
}
diff --git a/chrome/test/webdriver/automation.h b/chrome/test/webdriver/automation.h
index dcb6dba..3886c94 100644
--- a/chrome/test/webdriver/automation.h
+++ b/chrome/test/webdriver/automation.h
@@ -26,14 +26,11 @@ class DictionaryValue;
class ListValue;
}
-namespace gfx {
-class Point;
-}
-
namespace webdriver {
class Error;
class FramePath;
+class Point;
// Creates and controls the Chrome instance.
// This class should be created and accessed on a single thread.
@@ -83,7 +80,7 @@ class Automation {
// Drag and drop the file paths to the given location.
void DragAndDropFilePaths(int tab_id,
- const gfx::Point& location,
+ const Point& location,
const std::vector<FilePath::StringType>& paths,
Error** error);
@@ -108,18 +105,18 @@ class Automation {
base::DictionaryValue* cookie_dict,
Error** error);
- void MouseMove(int tab_id, const gfx::Point& p, Error** error);
+ void MouseMove(int tab_id, const Point& p, Error** error);
void MouseClick(int tab_id,
- const gfx::Point& p,
+ const Point& p,
automation::MouseButton button,
Error** error);
void MouseDrag(int tab_id,
- const gfx::Point& start,
- const gfx::Point& end,
+ const Point& start,
+ const Point& end,
Error** error);
- void MouseButtonDown(int tab_id, const gfx::Point& p, Error** error);
- void MouseButtonUp(int tab_id, const gfx::Point& p, Error** error);
- void MouseDoubleClick(int tab_id, const gfx::Point& p, Error** error);
+ void MouseButtonDown(int tab_id, const Point& p, Error** error);
+ void MouseButtonUp(int tab_id, const Point& p, Error** error);
+ void MouseDoubleClick(int tab_id, const Point& p, Error** error);
// Get persistent IDs for all the tabs currently open. These IDs can be used
// to identify the tab as long as the tab exists.
diff --git a/chrome/test/webdriver/commands/mouse_commands.cc b/chrome/test/webdriver/commands/mouse_commands.cc
index 70339b8..288ebcb 100644
--- a/chrome/test/webdriver/commands/mouse_commands.cc
+++ b/chrome/test/webdriver/commands/mouse_commands.cc
@@ -6,13 +6,13 @@
#include "base/values.h"
#include "chrome/common/automation_constants.h"
+#include "chrome/test/automation/value_conversion_util.h"
#include "chrome/test/webdriver/commands/response.h"
#include "chrome/test/webdriver/session.h"
-#include "chrome/test/webdriver/utility_functions.h"
#include "chrome/test/webdriver/web_element_id.h"
+#include "chrome/test/webdriver/webdriver_basic_types.h"
#include "chrome/test/webdriver/webdriver_error.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/size.h"
+#include "chrome/test/webdriver/webdriver_util.h"
namespace {
@@ -46,29 +46,23 @@ void MoveAndClickCommand::ExecutePost(Response* response) {
if (tag_name == "option") {
const char* kCanOptionBeToggledScript =
- "return (function(option) {"
+ "function(option) {"
" var select = option.parentElement;"
" if (!select || select.tagName.toLowerCase() != 'select')"
" throw new Error('Option element is not in a select');"
" return select.multiple;"
- "}).apply(null, arguments);";
- ListValue args;
- args.Append(element.ToValue());
- Value* value = NULL;
- error = session_->ExecuteScript(
- session_->current_target(), kCanOptionBeToggledScript, &args, &value);
+ "}";
+ bool can_be_toggled;
+ error = session_->ExecuteScriptAndParse(
+ session_->current_target(),
+ kCanOptionBeToggledScript,
+ "canOptionBeToggled",
+ CreateListValueFrom(element),
+ CreateDirectValueParser(&can_be_toggled));
if (error) {
response->SetError(error);
return;
}
- scoped_ptr<Value> scoped_value(value);
- bool can_be_toggled;
- if (!value->GetAsBoolean(&can_be_toggled)) {
- response->SetError(
- new Error(kUnknownError, "canOptionBeToggled returned non-boolean: " +
- JsonStringify(value)));
- return;
- }
if (can_be_toggled) {
error = session_->ToggleOptionElement(
@@ -78,7 +72,7 @@ void MoveAndClickCommand::ExecutePost(Response* response) {
session_->current_target(), element, true);
}
} else {
- gfx::Point location;
+ Point location;
error = session_->GetClickableLocation(element, &location);
if (!error)
error = session_->MouseMoveAndClick(location, automation::kLeftButton);
@@ -101,7 +95,7 @@ bool HoverCommand::DoesPost() {
void HoverCommand::ExecutePost(Response* response) {
Error* error = NULL;
- gfx::Point location;
+ Point location;
error = session_->GetClickableLocation(element, &location);
if (!error)
error = session_->MouseMove(location);
@@ -136,14 +130,14 @@ bool DragCommand::DoesPost() {
void DragCommand::ExecutePost(Response* response) {
Error* error = NULL;
- gfx::Point drag_from;
+ Point drag_from;
error = session_->GetClickableLocation(element, &drag_from);
if (error) {
response->SetError(error);
return;
}
- gfx::Point drag_to(drag_from);
+ Point drag_to(drag_from);
drag_to.Offset(drag_x_, drag_y_);
if (drag_to.x() < 0 || drag_to.y() < 0)
error = new Error(kBadRequest, "Invalid (x,y) coordinates");
@@ -199,7 +193,7 @@ bool MoveToCommand::Init(Response* const response) {
}
void MoveToCommand::ExecutePost(Response* const response) {
- gfx::Point location;
+ Point location;
Error* error;
if (has_element_) {
@@ -221,7 +215,7 @@ void MoveToCommand::ExecutePost(Response* const response) {
DCHECK(has_element_);
// If not, calculate the half of the element size and translate by it.
- gfx::Size size;
+ Size size;
error = session_->GetElementSize(session_->current_target(),
element_, &size);
if (error) {
diff --git a/chrome/test/webdriver/commands/webelement_commands.cc b/chrome/test/webdriver/commands/webelement_commands.cc
index c76c66b..05a8b66 100644
--- a/chrome/test/webdriver/commands/webelement_commands.cc
+++ b/chrome/test/webdriver/commands/webelement_commands.cc
@@ -13,10 +13,9 @@
#include "base/values.h"
#include "chrome/test/webdriver/commands/response.h"
#include "chrome/test/webdriver/session.h"
+#include "chrome/test/webdriver/webdriver_basic_types.h"
#include "chrome/test/webdriver/webdriver_error.h"
#include "third_party/webdriver/atoms.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/size.h"
namespace webdriver {
@@ -280,7 +279,7 @@ bool ElementLocationInViewCommand::DoesGet() {
}
void ElementLocationInViewCommand::ExecuteGet(Response* const response) {
- gfx::Point location;
+ Point location;
Error* error = session_->GetElementLocationInView(element, &location);
if (error) {
response->SetError(error);
@@ -367,7 +366,7 @@ bool ElementSizeCommand::DoesGet() {
}
void ElementSizeCommand::ExecuteGet(Response* const response) {
- gfx::Size size;
+ Size size;
Error* error = session_->GetElementSize(
session_->current_target(), element, &size);
if (error) {
@@ -565,7 +564,7 @@ Error* ElementValueCommand::DragAndDropFilePaths() const {
paths.push_back(path);
}
- gfx::Point location;
+ Point location;
error = session_->GetClickableLocation(element, &location);
if (error) {
return error;
diff --git a/chrome/test/webdriver/dispatch.cc b/chrome/test/webdriver/dispatch.cc
index 306aeb9..3d80bfc 100644
--- a/chrome/test/webdriver/dispatch.cc
+++ b/chrome/test/webdriver/dispatch.cc
@@ -21,8 +21,8 @@
#include "chrome/test/webdriver/http_response.h"
#include "chrome/test/webdriver/commands/command.h"
#include "chrome/test/webdriver/session_manager.h"
-#include "chrome/test/webdriver/utility_functions.h"
#include "chrome/test/webdriver/webdriver_logging.h"
+#include "chrome/test/webdriver/webdriver_util.h"
namespace webdriver {
diff --git a/chrome/test/webdriver/server.cc b/chrome/test/webdriver/server.cc
index e71134a..9efa533 100644
--- a/chrome/test/webdriver/server.cc
+++ b/chrome/test/webdriver/server.cc
@@ -48,8 +48,8 @@
#include "chrome/test/webdriver/commands/webelement_commands.h"
#include "chrome/test/webdriver/dispatch.h"
#include "chrome/test/webdriver/session_manager.h"
-#include "chrome/test/webdriver/utility_functions.h"
#include "chrome/test/webdriver/webdriver_logging.h"
+#include "chrome/test/webdriver/webdriver_util.h"
#include "third_party/mongoose/mongoose.h"
#if defined(OS_WIN)
diff --git a/chrome/test/webdriver/session.cc b/chrome/test/webdriver/session.cc
index bbfdc49..51b2908 100644
--- a/chrome/test/webdriver/session.cc
+++ b/chrome/test/webdriver/session.cc
@@ -32,14 +32,12 @@
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/automation/automation_json_requests.h"
-#include "chrome/test/webdriver/webdriver_error.h"
+#include "chrome/test/automation/value_conversion_util.h"
#include "chrome/test/webdriver/session_manager.h"
-#include "chrome/test/webdriver/utility_functions.h"
+#include "chrome/test/webdriver/webdriver_error.h"
#include "chrome/test/webdriver/webdriver_key_converter.h"
+#include "chrome/test/webdriver/webdriver_util.h"
#include "third_party/webdriver/atoms.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
namespace webdriver {
@@ -131,7 +129,7 @@ Error* Session::ExecuteScript(const FrameId& frame_id,
"[function(){%s\n},%s,true]));",
atoms::EXECUTE_SCRIPT, script.c_str(), args_as_json.c_str());
- return ExecuteScriptAndParseResponse(frame_id, jscript, value);
+ return ExecuteScriptAndParseValue(frame_id, jscript, value);
}
Error* Session::ExecuteScript(const std::string& script,
@@ -140,6 +138,32 @@ Error* Session::ExecuteScript(const std::string& script,
return ExecuteScript(current_target_, script, args, value);
}
+Error* Session::ExecuteScriptAndParse(const FrameId& frame_id,
+ const std::string& anonymous_func_script,
+ const std::string& script_name,
+ const ListValue* args,
+ const ValueParser* parser) {
+ scoped_ptr<const ListValue> scoped_args(args);
+ scoped_ptr<const ValueParser> scoped_parser(parser);
+ std::string called_script = base::StringPrintf(
+ "return (%s).apply(null, arguments);", anonymous_func_script.c_str());
+ Value* unscoped_value = NULL;
+ Error* error = ExecuteScript(frame_id, called_script, args, &unscoped_value);
+ if (error) {
+ error->AddDetails(script_name + " execution failed");
+ return error;
+ }
+
+ scoped_ptr<Value> value(unscoped_value);
+ std::string error_msg;
+ if (!parser->Parse(value.get())) {
+ error_msg = base::StringPrintf("%s returned invalid value: %s",
+ script_name.c_str(), JsonStringify(value.get()).c_str());
+ return new Error(kUnknownError, error_msg);
+ }
+ return NULL;
+}
+
Error* Session::ExecuteAsyncScript(const FrameId& frame_id,
const std::string& script,
const ListValue* const args,
@@ -162,7 +186,7 @@ Error* Session::ExecuteAsyncScript(const FrameId& frame_id,
timeout_ms,
"function(result) {window.domAutomationController.send(result);}");
- return ExecuteScriptAndParseResponse(frame_id, jscript, value);
+ return ExecuteScriptAndParseValue(frame_id, jscript, value);
}
Error* Session::SendKeys(const WebElementId& element, const string16& keys) {
@@ -181,8 +205,6 @@ Error* Session::SendKeys(const WebElementId& element, const string16& keys) {
if (!is_enabled)
return new Error(kInvalidElementState);
- ListValue args;
- args.Append(element.ToValue());
// Focus the target element in order to send keys to it.
// First, the currently active element is blurred, if it is different from
// the target element. We do not want to blur an element unnecessarily,
@@ -202,24 +224,27 @@ Error* Session::SendKeys(const WebElementId& element, const string16& keys) {
// - You cannot focus a descendant of a content editable node
// TODO(jleyba): Update this to use the correct atom.
const char* kFocusScript =
- "var elem = arguments[0];"
- "var doc = elem.ownerDocument || elem;"
- "var prevActiveElem = doc.activeElement;"
- "if (elem != prevActiveElem && prevActiveElem)"
- " prevActiveElem.blur();"
- "elem.focus();"
- "if (elem != prevActiveElem && elem.value && elem.value.length &&"
- " elem.setSelectionRange) {"
- " elem.setSelectionRange(elem.value.length, elem.value.length);"
- "}"
- "if (elem != doc.activeElement)"
- " throw new Error('Failed to send keys because cannot focus element.');";
- Value* unscoped_result = NULL;
- error = ExecuteScript(kFocusScript, &args, &unscoped_result);
+ "function(elem) {"
+ " var doc = elem.ownerDocument || elem;"
+ " var prevActiveElem = doc.activeElement;"
+ " if (elem != prevActiveElem && prevActiveElem)"
+ " prevActiveElem.blur();"
+ " elem.focus();"
+ " if (elem != prevActiveElem && elem.value && elem.value.length &&"
+ " elem.setSelectionRange) {"
+ " elem.setSelectionRange(elem.value.length, elem.value.length);"
+ " }"
+ " if (elem != doc.activeElement)"
+ " throw new Error('Failed to send keys because cannot focus element');"
+ "}";
+ error = ExecuteScriptAndParse(current_target_,
+ kFocusScript,
+ "focusElement",
+ CreateListValueFrom(element),
+ CreateDirectValueParser(kSkipParsing));
if (error)
return error;
- error = NULL;
RunSessionTask(NewRunnableMethod(
this,
&Session::SendKeysOnSessionThread,
@@ -229,7 +254,7 @@ Error* Session::SendKeys(const WebElementId& element, const string16& keys) {
}
Error* Session::DragAndDropFilePaths(
- const gfx::Point& location,
+ const Point& location,
const std::vector<FilePath::StringType>& paths) {
Error* error = NULL;
RunSessionTask(NewRunnableMethod(
@@ -293,44 +318,29 @@ Error* Session::Reload() {
}
Error* Session::GetURL(std::string* url) {
- ListValue no_args;
- Value* unscoped_value = NULL;
- Error* error = ExecuteScript(current_target_,
- "return document.URL;",
- &no_args,
- &unscoped_value);
- scoped_ptr<Value> value(unscoped_value);
- if (error)
- return error;
- if (!value->GetAsString(url))
- return new Error(kUnknownError, "GetURL Script returned non-string: " +
- JsonStringify(value.get()));
- return NULL;
+ return ExecuteScriptAndParse(current_target_,
+ "function() { return document.URL }",
+ "getUrl",
+ new ListValue(),
+ CreateDirectValueParser(url));
}
Error* Session::GetTitle(std::string* tab_title) {
- std::string script =
- "if (document.title)"
- " return document.title;"
- "else"
- " return document.URL;";
-
- ListValue no_args;
- Value* unscoped_value = NULL;
- Error* error = ExecuteScript(current_target_,
- script,
- &no_args,
- &unscoped_value);
- scoped_ptr<Value> value(unscoped_value);
- if (error)
- return error;
- if (!value->GetAsString(tab_title))
- return new Error(kUnknownError, "GetTitle script returned non-string: " +
- JsonStringify(value.get()));
- return NULL;
-}
-
-Error* Session::MouseMoveAndClick(const gfx::Point& location,
+ const char* kGetTitleScript =
+ "function() {"
+ " if (document.title)"
+ " return document.title;"
+ " else"
+ " return document.URL;"
+ "}";
+ return ExecuteScriptAndParse(current_target_,
+ kGetTitleScript,
+ "getTitle",
+ new ListValue(),
+ CreateDirectValueParser(tab_title));
+}
+
+Error* Session::MouseMoveAndClick(const Point& location,
automation::MouseButton button) {
Error* error = NULL;
RunSessionTask(NewRunnableMethod(
@@ -345,7 +355,7 @@ Error* Session::MouseMoveAndClick(const gfx::Point& location,
return error;
}
-Error* Session::MouseMove(const gfx::Point& location) {
+Error* Session::MouseMove(const Point& location) {
Error* error = NULL;
RunSessionTask(NewRunnableMethod(
automation_.get(),
@@ -358,8 +368,8 @@ Error* Session::MouseMove(const gfx::Point& location) {
return error;
}
-Error* Session::MouseDrag(const gfx::Point& start,
- const gfx::Point& end) {
+Error* Session::MouseDrag(const Point& start,
+ const Point& end) {
Error* error = NULL;
RunSessionTask(NewRunnableMethod(
automation_.get(),
@@ -480,18 +490,16 @@ Error* Session::SwitchToWindow(const std::string& name) {
return error;
// See if any of the window names match |name|.
for (size_t i = 0; i < window_ids.size(); ++i) {
- ListValue empty_list;
- Value* unscoped_name_value = NULL;
std::string window_name;
- Error* error = ExecuteScript(FrameId(window_ids[i], FramePath()),
- "return window.name;",
- &empty_list,
- &unscoped_name_value);
+ Error* error = ExecuteScriptAndParse(
+ FrameId(window_ids[i], FramePath()),
+ "function() { return window.name; }",
+ "getWindowName",
+ new ListValue(),
+ CreateDirectValueParser(&window_name));
if (error)
return error;
- scoped_ptr<Value> name_value(unscoped_name_value);
- if (name_value->GetAsString(&window_name) &&
- name == window_name) {
+ if (name == window_name) {
switch_to_id = window_ids[i];
break;
}
@@ -507,21 +515,21 @@ Error* Session::SwitchToWindow(const std::string& name) {
Error* Session::SwitchToFrameWithNameOrId(const std::string& name_or_id) {
std::string script =
- "var arg = arguments[0];"
- "var xpath = '(/html/body//iframe|/html/frameset/frame)';"
- "var sub = function(s) { return s.replace(/\\$/g, arg); };"
- "xpath += sub('[@name=\"$\" or @id=\"$\"]');"
- "var frame = document.evaluate(xpath, document, null, "
- " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
- "if (!frame) { return null; }"
- "xpath = frame.tagName == 'IFRAME' ? '/html/body//iframe'"
- " : '/html/frameset/frame';"
- "frame_xpath = xpath + "
- " sub('[@' + (frame.id == arg ? 'id' : 'name') + '=\"$\"]');"
- "return [frame, frame_xpath];";
- ListValue args;
- args.Append(new StringValue(name_or_id));
- return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
+ "function(arg) {"
+ " var xpath = '(/html/body//iframe|/html/frameset/frame)';"
+ " var sub = function(s) { return s.replace(/\\$/g, arg); };"
+ " xpath += sub('[@name=\"$\" or @id=\"$\"]');"
+ " var frame = document.evaluate(xpath, document, null, "
+ " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
+ " if (!frame) { return null; }"
+ " xpath = frame.tagName == 'IFRAME' ? '/html/body//iframe'"
+ " : '/html/frameset/frame';"
+ " frame_xpath = xpath + "
+ " sub('[@' + (frame.id == arg ? 'id' : 'name') + '=\"$\"]');"
+ " return [frame, frame_xpath];"
+ "}";
+ return SwitchToFrameWithJavaScriptLocatedFrame(
+ script, CreateListValueFrom(name_or_id));
}
Error* Session::SwitchToFrameWithIndex(int index) {
@@ -533,43 +541,39 @@ Error* Session::SwitchToFrameWithIndex(int index) {
// into the list of all IFRAME and FRAME elements on the page - if we find
// something, then that XPath expression can be used as the new frame's XPath.
std::string script =
- "var index = '[' + (arguments[0] + 1) + ']';"
- "var xpath = '(/html/body//iframe|/html/frameset/frame)' + "
- " index;"
- "console.info('searching for frame by xpath: ' + xpath);"
- "var frame = document.evaluate(xpath, document, null, "
- "XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
- "console.info(frame == null ? 'found nothing' : frame);"
- "if (!frame) { return null; }"
- "frame_xpath = ((frame.tagName == 'IFRAME' ? "
- " '(/html/body//iframe)' : '/html/frameset/frame') + index);"
- "return [frame, frame_xpath];";
- ListValue args;
- args.Append(Value::CreateIntegerValue(index));
- return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
+ "function(index) {"
+ " var xpathIndex = '[' + (index + 1) + ']';"
+ " var xpath = '(/html/body//iframe|/html/frameset/frame)' + "
+ " xpathIndex;"
+ " var frame = document.evaluate(xpath, document, null, "
+ " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
+ " if (!frame) { return null; }"
+ " frame_xpath = ((frame.tagName == 'IFRAME' ? "
+ " '(/html/body//iframe)' : '/html/frameset/frame') + xpathIndex);"
+ " return [frame, frame_xpath];"
+ "}";
+ return SwitchToFrameWithJavaScriptLocatedFrame(
+ script, CreateListValueFrom(index));
}
Error* Session::SwitchToFrameWithElement(const WebElementId& element) {
// TODO(jleyba): Extract this, and the other frame switch methods to an atom.
std::string script =
- "var element = arguments[0];"
- "console.info('Attempting to switch to ' + element);"
- "if (element.nodeType != 1 || !/^i?frame$/i.test(element.tagName)) {"
- " console.info('Element is not a frame: ' + element + "
- "' {nodeType:' + element.nodeType + ',tagName:' + element.tagName + '}');"
- " return null;"
- "}"
- "for (var i = 0; i < window.frames.length; i++) {"
- " if (element.contentWindow == window.frames[i]) {"
- " return [element, '(//iframe|//frame)[' + (i + 1) + ']'];"
+ "function(elem) {"
+ " if (elem.nodeType != 1 || !/^i?frame$/i.test(elem.tagName)) {"
+ " console.error('Element is not a frame');"
+ " return null;"
" }"
- "}"
- "console.info('Frame is not connected to this DOM tree');"
- "return null;";
-
- ListValue args;
- args.Append(element.ToValue());
- return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
+ " for (var i = 0; i < window.frames.length; i++) {"
+ " if (elem.contentWindow == window.frames[i]) {"
+ " return [elem, '(//iframe|//frame)[' + (i + 1) + ']'];"
+ " }"
+ " }"
+ " console.info('Frame is not connected to this DOM tree');"
+ " return null;"
+ "}";
+ return SwitchToFrameWithJavaScriptLocatedFrame(
+ script, CreateListValueFrom(element));
}
void Session::SwitchToTopFrame() {
@@ -591,13 +595,12 @@ Error* Session::SwitchToTopFrameIfCurrentFrameInvalid() {
// frame element is valid, otherwise the automation hangs until a timeout.
for (size_t i = 0; i < frame_elements_.size(); ++i) {
FrameId frame_id(current_target_.window_id, frame_path);
- ListValue args;
- args.Append(frame_elements_[i].ToValue());
- Value* unscoped_value = NULL;
- scoped_ptr<Error> error(ExecuteScript(
- frame_id, "", &args, &unscoped_value));
-
- scoped_ptr<Value> value(unscoped_value);
+ scoped_ptr<Error> error(ExecuteScriptAndParse(
+ frame_id,
+ "function(){ }",
+ "emptyScript",
+ CreateListValueFrom(frame_elements_[i]),
+ CreateDirectValueParser(kSkipParsing)));
if (error.get() && error->code() == kStaleElementReference) {
SwitchToTopFrame();
} else if (error.get()) {
@@ -730,26 +733,26 @@ Error* Session::FindElements(const FrameId& frame_id,
Error* Session::GetElementLocationInView(
const WebElementId& element,
- gfx::Point* location) {
- gfx::Size size;
+ Point* location) {
+ Size size;
Error* error = GetElementSize(current_target_, element, &size);
if (error)
return error;
return GetElementRegionInView(
- element, gfx::Rect(gfx::Point(0, 0), size),
+ element, Rect(Point(0, 0), size),
false /* center */, false /* verify_clickable_at_middle */, location);
}
Error* Session::GetElementRegionInView(
const WebElementId& element,
- const gfx::Rect& region,
+ const Rect& region,
bool center,
bool verify_clickable_at_middle,
- gfx::Point* location) {
+ Point* location) {
CHECK(element.is_valid());
- gfx::Point region_offset = region.origin();
- gfx::Size region_size = region.size();
+ Point region_offset = region.origin();
+ Size region_size = region.size();
Error* error = GetElementRegionInViewHelper(
current_target_, element, region, center, verify_clickable_at_middle,
&region_offset);
@@ -782,7 +785,7 @@ Error* Session::GetElementRegionInView(
region_offset.Offset(border_left, border_top);
error = GetElementRegionInViewHelper(
- frame_id, frame_element, gfx::Rect(region_offset, region_size),
+ frame_id, frame_element, Rect(region_offset, region_size),
center, verify_clickable_at_middle, &region_offset);
if (error)
return error;
@@ -793,66 +796,22 @@ Error* Session::GetElementRegionInView(
Error* Session::GetElementSize(const FrameId& frame_id,
const WebElementId& element,
- gfx::Size* size) {
- std::string script = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::GET_SIZE);
- ListValue args;
- args.Append(element.ToValue());
-
- Value* unscoped_result = NULL;
- Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
- scoped_ptr<Value> result(unscoped_result);
- if (error)
- return error;
- if (!result->IsType(Value::TYPE_DICTIONARY)) {
- return new Error(kUnknownError, "GetSize atom returned non-dict type: " +
- JsonStringify(result.get()));
- }
- DictionaryValue* dict = static_cast<DictionaryValue*>(result.get());
- int width, height;
- if (!dict->GetInteger("width", &width) ||
- !dict->GetInteger("height", &height)) {
- return new Error(kUnknownError, "GetSize atom returned invalid dict: " +
- JsonStringify(dict));
- }
- *size = gfx::Size(width, height);
- return NULL;
+ Size* size) {
+ return ExecuteScriptAndParse(frame_id,
+ atoms::GET_SIZE,
+ "getSize",
+ CreateListValueFrom(element),
+ CreateDirectValueParser(size));
}
Error* Session::GetElementFirstClientRect(const FrameId& frame_id,
const WebElementId& element,
- gfx::Rect* rect) {
- std::string script = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::GET_FIRST_CLIENT_RECT);
- ListValue args;
- args.Append(element.ToValue());
-
- Value* unscoped_result = NULL;
- Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
- scoped_ptr<Value> result(unscoped_result);
- if (error)
- return error;
- if (!result->IsType(Value::TYPE_DICTIONARY)) {
- return new Error(
- kUnknownError,
- "GetFirstClientRect atom returned non-dict type: " +
- JsonStringify(result.get()));
- }
- DictionaryValue* dict = static_cast<DictionaryValue*>(result.get());
- // TODO(kkania): Convert the atom to return integers.
- double left, top, width, height;
- if (!dict->GetDouble("left", &left) ||
- !dict->GetDouble("top", &top) ||
- !dict->GetDouble("width", &width) ||
- !dict->GetDouble("height", &height)) {
- return new Error(
- kUnknownError,
- "GetFirstClientRect atom returned invalid dict: " +
- JsonStringify(dict));
- }
- *rect = gfx::Rect(static_cast<int>(left), static_cast<int>(top),
- static_cast<int>(width), static_cast<int>(height));
- return NULL;
+ Rect* rect) {
+ return ExecuteScriptAndParse(frame_id,
+ atoms::GET_FIRST_CLIENT_RECT,
+ "getFirstClientRect",
+ CreateListValueFrom(element),
+ CreateDirectValueParser(rect));
}
Error* Session::GetElementEffectiveStyle(
@@ -860,28 +819,11 @@ Error* Session::GetElementEffectiveStyle(
const WebElementId& element,
const std::string& prop,
std::string* value) {
- std::string script = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::GET_EFFECTIVE_STYLE);
- ListValue args;
- args.Append(element.ToValue());
- args.Append(Value::CreateStringValue(prop));
- Value* unscoped_result = NULL;
- Error* error = ExecuteScript(
- frame_id, script, &args, &unscoped_result);
- scoped_ptr<Value> result(unscoped_result);
- if (error) {
- error->AddDetails(base::StringPrintf(
- "GetEffectiveStyle atom failed for property (%s)", prop.c_str()));
- return error;
- }
-
- if (!result->GetAsString(value)) {
- std::string context = base::StringPrintf(
- "GetEffectiveStyle atom returned non-string for property (%s): %s",
- prop.c_str(), JsonStringify(result.get()).c_str());
- return new Error(kUnknownError, context);
- }
- return NULL;
+ return ExecuteScriptAndParse(frame_id,
+ atoms::GET_EFFECTIVE_STYLE,
+ "getEffectiveStyle",
+ CreateListValueFrom(element, prop),
+ CreateDirectValueParser(value));
}
Error* Session::GetElementBorder(const FrameId& frame_id,
@@ -907,77 +849,42 @@ Error* Session::IsElementDisplayed(const FrameId& frame_id,
const WebElementId& element,
bool ignore_opacity,
bool* is_displayed) {
- std::string script = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::IS_DISPLAYED);
- ListValue args;
- args.Append(element.ToValue());
- args.Append(Value::CreateBooleanValue(ignore_opacity));
-
- Value* unscoped_result = NULL;
- Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
- scoped_ptr<Value> result(unscoped_result);
- if (error)
- return error;
- if (!result->GetAsBoolean(is_displayed))
- return new Error(kUnknownError, "IsDisplayed atom returned non-boolean: " +
- JsonStringify(result.get()));
- return NULL;
+ return ExecuteScriptAndParse(frame_id,
+ atoms::IS_DISPLAYED,
+ "isDisplayed",
+ CreateListValueFrom(element, ignore_opacity),
+ CreateDirectValueParser(is_displayed));
}
Error* Session::IsElementEnabled(const FrameId& frame_id,
const WebElementId& element,
bool* is_enabled) {
- std::string script = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::IS_ENABLED);
- ListValue args;
- args.Append(element.ToValue());
-
- Value* unscoped_result = NULL;
- Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
- scoped_ptr<Value> result(unscoped_result);
- if (error)
- return error;
- if (!result->GetAsBoolean(is_enabled))
- return new Error(kUnknownError, "IsEnabled atom returned non-boolean: " +
- JsonStringify(result.get()));
- return NULL;
+ return ExecuteScriptAndParse(frame_id,
+ atoms::IS_ENABLED,
+ "isEnabled",
+ CreateListValueFrom(element),
+ CreateDirectValueParser(is_enabled));
}
Error* Session::IsOptionElementSelected(const FrameId& frame_id,
const WebElementId& element,
bool* is_selected) {
- ListValue args;
- args.Append(element.ToValue());
-
- std::string script = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::IS_SELECTED);
-
- Value* result = NULL;
- Error* error = ExecuteScript(frame_id, script, &args, &result);
- if (error)
- return error;
- scoped_ptr<Value> scoped_result(result);
- if (!result->GetAsBoolean(is_selected)) {
- return new Error(kUnknownError, "isSelected atom returned non-boolean: " +
- JsonStringify(result));
- }
- return NULL;
+ return ExecuteScriptAndParse(
+ frame_id,
+ atoms::IS_SELECTED,
+ "isSelected",
+ CreateListValueFrom(element),
+ CreateDirectValueParser(is_selected));
}
Error* Session::SetOptionElementSelected(const FrameId& frame_id,
const WebElementId& element,
bool selected) {
- ListValue args;
- args.Append(element.ToValue());
- args.Append(Value::CreateBooleanValue(selected));
-
- std::string script = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::SET_SELECTED);
-
- Value* unscoped_result = NULL;
- Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
- scoped_ptr<Value> result(unscoped_result);
- return error;
+ return ExecuteScriptAndParse(frame_id,
+ atoms::SET_SELECTED,
+ "setSelected",
+ CreateListValueFrom(element, selected),
+ CreateDirectValueParser(kSkipParsing));
}
Error* Session::ToggleOptionElement(const FrameId& frame_id,
@@ -993,24 +900,16 @@ Error* Session::ToggleOptionElement(const FrameId& frame_id,
Error* Session::GetElementTagName(const FrameId& frame_id,
const WebElementId& element,
std::string* tag_name) {
- ListValue args;
- args.Append(element.ToValue());
-
- std::string script = "return arguments[0].tagName.toLocaleLowerCase();";
-
- Value* unscoped_result = NULL;
- Error* error = ExecuteScript(frame_id, script, &args, &unscoped_result);
- scoped_ptr<Value> result(unscoped_result);
- if (error)
- return error;
- if (!result->GetAsString(tag_name))
- return new Error(kUnknownError, "TagName script returned non-string: " +
- JsonStringify(result.get()));
- return NULL;
+ return ExecuteScriptAndParse(
+ frame_id,
+ "function(elem) { return elem.tagName.toLowerCase() }",
+ "getElementTagName",
+ CreateListValueFrom(element),
+ CreateDirectValueParser(tag_name));
}
Error* Session::GetClickableLocation(const WebElementId& element,
- gfx::Point* location) {
+ Point* location) {
bool is_displayed = false;
Error* error = IsElementDisplayed(
current_target_, element, true /* ignore_opacity */, &is_displayed);
@@ -1019,7 +918,7 @@ Error* Session::GetClickableLocation(const WebElementId& element,
if (!is_displayed)
return new Error(kElementNotVisible, "Element must be displayed to click");
- gfx::Rect rect;
+ Rect rect;
error = GetElementFirstClientRect(current_target_, element, &rect);
if (error)
return error;
@@ -1034,20 +933,13 @@ Error* Session::GetClickableLocation(const WebElementId& element,
}
Error* Session::GetAttribute(const WebElementId& element,
- const std::string& key, Value** value) {
- std::string script = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::GET_ATTRIBUTE);
-
- ListValue args;
- args.Append(element.ToValue());
- args.Append(Value::CreateStringValue(key));
-
- Error* error = ExecuteScript(script, &args, value);
- if (error) {
- return error;
- }
-
- return NULL;
+ const std::string& key,
+ Value** value) {
+ return ExecuteScriptAndParse(current_target_,
+ atoms::GET_ATTRIBUTE,
+ "getAttribute",
+ CreateListValueFrom(element, key),
+ CreateDirectValueParser(value));
}
Error* Session::WaitForAllTabsToStopLoading() {
@@ -1095,7 +987,7 @@ int Session::implicit_wait() const {
return implicit_wait_;
}
-const gfx::Point& Session::get_mouse_position() const {
+const Point& Session::get_mouse_position() const {
return mouse_position_;
}
@@ -1145,9 +1037,9 @@ void Session::TerminateOnSessionThread() {
automation_.reset();
}
-Error* Session::ExecuteScriptAndParseResponse(const FrameId& frame_id,
- const std::string& script,
- Value** script_result) {
+Error* Session::ExecuteScriptAndParseValue(const FrameId& frame_id,
+ const std::string& script,
+ Value** script_result) {
std::string response_json;
Error* error = NULL;
RunSessionTask(NewRunnableMethod(
@@ -1236,31 +1128,43 @@ void Session::SendKeysOnSessionThread(const string16& keys, Error** error) {
Error* Session::SwitchToFrameWithJavaScriptLocatedFrame(
const std::string& script, ListValue* args) {
- Value* unscoped_result = NULL;
- Error* error = ExecuteScript(script, args, &unscoped_result);
- scoped_ptr<Value> result(unscoped_result);
+ class SwitchFrameValueParser : public ValueParser {
+ public:
+ SwitchFrameValueParser(
+ bool* found_frame, WebElementId* frame, std::string* xpath)
+ : found_frame_(found_frame), frame_(frame), xpath_(xpath) { }
+
+ virtual ~SwitchFrameValueParser() { }
+
+ virtual bool Parse(base::Value* value) const OVERRIDE {
+ if (value->IsType(Value::TYPE_NULL)) {
+ *found_frame_ = false;
+ return true;
+ }
+ ListValue* list;
+ if (!value->GetAsList(&list))
+ return false;
+ *found_frame_ = true;
+ return SetFromListValue(list, frame_, xpath_);
+ }
+
+ private:
+ bool* found_frame_;
+ WebElementId* frame_;
+ std::string* xpath_;
+ };
+
+ bool found_frame;
+ WebElementId new_frame_element;
+ std::string xpath;
+ Error* error = ExecuteScriptAndParse(
+ current_target_, script, "switchFrame", args,
+ new SwitchFrameValueParser(&found_frame, &new_frame_element, &xpath));
if (error)
return error;
- ListValue* frame_and_xpath_list;
- if (!result->GetAsList(&frame_and_xpath_list))
+ if (!found_frame)
return new Error(kNoSuchFrame);
- DictionaryValue* element_dict;
- std::string xpath;
- if (!frame_and_xpath_list->GetDictionary(0, &element_dict) ||
- !frame_and_xpath_list->GetString(1, &xpath)) {
- return new Error(
- kUnknownError,
- "Frame finding script did not return correct type: " +
- JsonStringify(frame_and_xpath_list));
- }
- WebElementId new_frame_element(element_dict);
- if (!new_frame_element.is_valid()) {
- return new Error(
- kUnknownError,
- "Frame finding script did not return a frame element: " +
- JsonStringify(element_dict));
- }
frame_elements_.push_back(new_frame_element);
current_target_.frame_path = current_target_.frame_path.Append(xpath);
@@ -1274,132 +1178,143 @@ Error* Session::FindElementsHelper(const FrameId& frame_id,
bool find_one,
std::vector<WebElementId>* elements) {
CHECK(root_element.is_valid());
-
- std::string jscript;
- if (find_one) {
- // TODO(jleyba): Write a Chrome-specific find element atom that will
- // correctly throw an error if the element cannot be found.
- jscript = base::StringPrintf(
- "var result = (%s).apply(null, arguments);"
- "if (!result) {"
- "var e = new Error('Unable to locate element');"
- "e.code = %d;"
- "throw e;"
- "} else { return result; }",
- atoms::FIND_ELEMENT, kNoSuchElement);
- } else {
- jscript = base::StringPrintf("return (%s).apply(null, arguments);",
- atoms::FIND_ELEMENTS);
- }
- ListValue jscript_args;
- DictionaryValue* locator_dict = new DictionaryValue();
- locator_dict->SetString(locator, query);
- jscript_args.Append(locator_dict);
- jscript_args.Append(root_element.ToValue());
-
- // The element search needs to loop until at least one element is found or the
- // session's implicit wait timeout expires, whichever occurs first.
base::Time start_time = base::Time::Now();
+ while (true) {
+ std::vector<WebElementId> temp_elements;
+ Error* error = ExecuteFindElementScriptAndParse(
+ frame_id, root_element, locator, query, find_one, &temp_elements);
+ if (error)
+ return error;
- scoped_ptr<Value> value;
- scoped_ptr<Error> error;
- bool done = false;
- while (!done) {
- Value* unscoped_value = NULL;
- error.reset(ExecuteScript(
- frame_id, jscript, &jscript_args, &unscoped_value));
- value.reset(unscoped_value);
- if (!error.get()) {
- // If searching for many elements, make sure we found at least one before
- // stopping.
- done = find_one ||
- (value->GetType() == Value::TYPE_LIST &&
- static_cast<ListValue*>(value.get())->GetSize() > 0);
- } else if (error->code() != kNoSuchElement) {
- return error.release();
+ if (temp_elements.size() > 0u) {
+ elements->swap(temp_elements);
+ break;
+ }
+
+ if ((base::Time::Now() - start_time).InMilliseconds() > implicit_wait_) {
+ if (find_one)
+ return new Error(kNoSuchElement);
+ break;
}
- int64 elapsed_time = (base::Time::Now() - start_time).InMilliseconds();
- done = done || elapsed_time > implicit_wait_;
- if (!done)
- base::PlatformThread::Sleep(50); // Prevent a busy loop.
+ base::PlatformThread::Sleep(50);
}
+ return NULL;
+}
- if (error.get())
- return error.release();
-
- // Parse the results.
- const std::string kInvalidElementDictionaryMessage =
- "Find element script returned invalid element dictionary: " +
- JsonStringify(value.get());
- if (value->IsType(Value::TYPE_LIST)) {
- ListValue* element_list = static_cast<ListValue*>(value.get());
- for (size_t i = 0; i < element_list->GetSize(); ++i) {
- DictionaryValue* element_dict = NULL;
- if (!element_list->GetDictionary(i, &element_dict)) {
- return new Error(
- kUnknownError,
- "Find element script returned non-dictionary: " +
- JsonStringify(element_list));
- }
+Error* Session::ExecuteFindElementScriptAndParse(
+ const FrameId& frame_id,
+ const WebElementId& root_element,
+ const std::string& locator,
+ const std::string& query,
+ bool find_one,
+ std::vector<WebElementId>* elements) {
+ CHECK(root_element.is_valid());
- WebElementId element(element_dict);
- if (!element.is_valid()) {
- return new Error(kUnknownError, kInvalidElementDictionaryMessage);
+ class FindElementsParser : public ValueParser {
+ public:
+ explicit FindElementsParser(std::vector<WebElementId>* elements)
+ : elements_(elements) { }
+
+ virtual ~FindElementsParser() { }
+
+ virtual bool Parse(base::Value* value) const OVERRIDE {
+ if (!value->IsType(Value::TYPE_LIST))
+ return false;
+ ListValue* list = static_cast<ListValue*>(value);
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ WebElementId element;
+ Value* element_value = NULL;
+ if (!list->Get(i, &element_value))
+ return false;
+ if (!SetFromValue(element_value, &element))
+ return false;
+ elements_->push_back(element);
}
- elements->push_back(element);
+ return true;
}
- } else if (value->IsType(Value::TYPE_DICTIONARY)) {
- DictionaryValue* element_dict =
- static_cast<DictionaryValue*>(value.get());
- WebElementId element(element_dict);
- if (!element.is_valid()) {
- return new Error(kUnknownError, kInvalidElementDictionaryMessage);
+ private:
+ std::vector<WebElementId>* elements_;
+ };
+
+ class FindElementParser : public ValueParser {
+ public:
+ explicit FindElementParser(std::vector<WebElementId>* elements)
+ : elements_(elements) { }
+
+ virtual ~FindElementParser() { }
+
+ virtual bool Parse(base::Value* value) const OVERRIDE {
+ if (value->IsType(Value::TYPE_NULL))
+ return true;
+ WebElementId element;
+ bool set = SetFromValue(value, &element);
+ if (set)
+ elements_->push_back(element);
+ return set;
}
- elements->push_back(element);
+ private:
+ std::vector<WebElementId>* elements_;
+ };
+
+ DictionaryValue locator_dict;
+ locator_dict.SetString(locator, query);
+ std::vector<WebElementId> temp_elements;
+ Error* error = NULL;
+ if (find_one) {
+ error = ExecuteScriptAndParse(
+ frame_id,
+ atoms::FIND_ELEMENT,
+ "findElement",
+ CreateListValueFrom(&locator_dict, root_element),
+ new FindElementParser(&temp_elements));
} else {
- return new Error(
- kUnknownError,
- "Find element script returned unsupported type: " +
- JsonStringify(value.get()));
+ error = ExecuteScriptAndParse(
+ frame_id,
+ atoms::FIND_ELEMENTS,
+ "findElements",
+ CreateListValueFrom(&locator_dict, root_element),
+ new FindElementsParser(&temp_elements));
}
- return NULL;
+ if (!error)
+ elements->swap(temp_elements);
+ return error;
}
Error* Session::VerifyElementIsClickable(
const FrameId& frame_id,
const WebElementId& element,
- const gfx::Point& location) {
- std::string jscript = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::IS_ELEMENT_CLICKABLE);
- ListValue jscript_args;
- jscript_args.Append(element.ToValue());
- DictionaryValue* location_dict = new DictionaryValue();
- location_dict->SetInteger("x", location.x());
- location_dict->SetInteger("y", location.y());
- jscript_args.Append(location_dict);
- Value* unscoped_value = NULL;
- Error* error = ExecuteScript(frame_id, jscript, &jscript_args,
- &unscoped_value);
+ const Point& location) {
+ class IsElementClickableParser : public ValueParser {
+ public:
+ IsElementClickableParser(bool* clickable, std::string* message)
+ : clickable_(clickable), message_(message) { }
+
+ virtual ~IsElementClickableParser() { }
+
+ virtual bool Parse(base::Value* value) const OVERRIDE {
+ if (!value->IsType(Value::TYPE_DICTIONARY))
+ return false;
+ DictionaryValue* dict = static_cast<DictionaryValue*>(value);
+ dict->GetString("message", message_);
+ return dict->GetBoolean("clickable", clickable_);
+ }
+
+ private:
+ bool* clickable_;
+ std::string* message_;
+ };
+
+ bool clickable;
+ std::string message;
+ Error* error = ExecuteScriptAndParse(
+ frame_id,
+ atoms::IS_ELEMENT_CLICKABLE,
+ "isElementClickable",
+ CreateListValueFrom(element, location),
+ new IsElementClickableParser(&clickable, &message));
if (error)
return error;
- scoped_ptr<Value> value(unscoped_value);
- if (!value->IsType(Value::TYPE_DICTIONARY)) {
- return new Error(
- kUnknownError,
- "isElementClickable atom returned non-dictionary type: " +
- JsonStringify(value.get()));
- }
- DictionaryValue* dict = static_cast<DictionaryValue*>(value.get());
- bool clickable = false;
- if (!dict->GetBoolean("clickable", &clickable)) {
- return new Error(
- kUnknownError,
- "isElementClickable atom returned bad invalid dictionary: " +
- JsonStringify(dict));
- }
- std::string message;
- dict->GetString("message", &message);
if (!clickable) {
if (message.empty())
message = "element is not clickable";
@@ -1414,47 +1329,20 @@ Error* Session::VerifyElementIsClickable(
Error* Session::GetElementRegionInViewHelper(
const FrameId& frame_id,
const WebElementId& element,
- const gfx::Rect& region,
+ const Rect& region,
bool center,
bool verify_clickable_at_middle,
- gfx::Point* location) {
- std::string jscript = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::GET_LOCATION_IN_VIEW);
- ListValue jscript_args;
- jscript_args.Append(element.ToValue());
- jscript_args.Append(Value::CreateBooleanValue(center));
- DictionaryValue* elem_offset_dict = new DictionaryValue();
- elem_offset_dict->SetInteger("left", region.x());
- elem_offset_dict->SetInteger("top", region.y());
- elem_offset_dict->SetInteger("width", region.width());
- elem_offset_dict->SetInteger("height", region.height());
- jscript_args.Append(elem_offset_dict);
- Value* unscoped_value = NULL;
- Error* error = ExecuteScript(frame_id, jscript, &jscript_args,
- &unscoped_value);
- scoped_ptr<Value> value(unscoped_value);
- if (error)
- return error;
-
- if (!value->IsType(Value::TYPE_DICTIONARY)) {
- return new Error(
- kUnknownError,
- "Location atom returned non-dictionary type: " +
- JsonStringify(value.get()));
- }
- DictionaryValue* loc_dict = static_cast<DictionaryValue*>(value.get());
- int x = 0, y = 0;
- if (!loc_dict->GetInteger("x", &x) ||
- !loc_dict->GetInteger("y", &y)) {
- return new Error(
- kUnknownError,
- "Location atom returned bad coordinate dictionary: " +
- JsonStringify(loc_dict));
- }
- gfx::Point temp_location = gfx::Point(x, y);
+ Point* location) {
+ Point temp_location;
+ Error* error = ExecuteScriptAndParse(
+ frame_id,
+ atoms::GET_LOCATION_IN_VIEW,
+ "getLocationInView",
+ CreateListValueFrom(element, center, region),
+ CreateDirectValueParser(&temp_location));
if (verify_clickable_at_middle) {
- gfx::Point middle_point = temp_location;
+ Point middle_point = temp_location;
middle_point.Offset(region.width() / 2, region.height() / 2);
error = VerifyElementIsClickable(frame_id, element, middle_point);
if (error)
@@ -1487,39 +1375,21 @@ Error* Session::GetScreenShot(std::string* png) {
}
Error* Session::GetBrowserConnectionState(bool* online) {
- std::string jscript = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::IS_ONLINE);
- base::ListValue no_args;
- Value* unscoped_value = NULL;
- Error* error = ExecuteScript(jscript,
- &no_args,
- &unscoped_value);
- scoped_ptr<base::Value> value(unscoped_value);
- if (error)
- return error;
- if (!value->GetAsBoolean(online))
- return new Error(kUnknownError,
- "IS_ONLINE script returned non-boolean: " +
- JsonStringify(value.get()));
- return NULL;
+ return ExecuteScriptAndParse(
+ current_target_,
+ atoms::IS_ONLINE,
+ "isOnline",
+ new ListValue(),
+ CreateDirectValueParser(online));
}
Error* Session::GetAppCacheStatus(int* status) {
- std::string jscript = base::StringPrintf(
- "return (%s).apply(null, arguments);", atoms::GET_APPCACHE_STATUS);
- base::ListValue no_args;
- Value* unscoped_value = NULL;
- Error* error = ExecuteScript(jscript,
- &no_args,
- &unscoped_value);
- scoped_ptr<base::Value> value(unscoped_value);
- if (error)
- return error;
- if (!value->GetAsInteger(status))
- return new Error(kUnknownError,
- "GET_APPCACHE_STATUS script returned non-integer: " +
- JsonStringify(value.get()));
- return NULL;
+ return ExecuteScriptAndParse(
+ current_target_,
+ atoms::GET_APPCACHE_STATUS,
+ "getAppcacheStatus",
+ new ListValue(),
+ CreateDirectValueParser(status));
}
} // namespace webdriver
diff --git a/chrome/test/webdriver/session.h b/chrome/test/webdriver/session.h
index b1018f2..1cc625f 100644
--- a/chrome/test/webdriver/session.h
+++ b/chrome/test/webdriver/session.h
@@ -17,7 +17,7 @@
#include "chrome/test/webdriver/automation.h"
#include "chrome/test/webdriver/frame_path.h"
#include "chrome/test/webdriver/web_element_id.h"
-#include "ui/gfx/point.h"
+#include "chrome/test/webdriver/webdriver_basic_types.h"
class CommandLine;
class FilePath;
@@ -29,14 +29,10 @@ class Value;
class WaitableEvent;
}
-namespace gfx {
-class Rect;
-class Size;
-}
-
namespace webdriver {
class Error;
+class ValueParser;
// A window ID and frame path combination that uniquely identifies a specific
// frame within a session.
@@ -103,6 +99,16 @@ class Session {
const base::ListValue* const args,
base::Value** value);
+ // Executes the given script in the context of the given frame and parses
+ // the value with the given parser. The script should be in the form of an
+ // anonymous function. |script_name| is only used when creating error
+ // messages. This function takes ownership of |args| and |parser|.
+ Error* ExecuteScriptAndParse(const FrameId& frame_id,
+ const std::string& anonymous_func_script,
+ const std::string& script_name,
+ const base::ListValue* args,
+ const ValueParser* parser);
+
// Executes given |script| in the context of the given frame.
// The |script| should be in the form of a function body
// (e.g. "return arguments[0]"), where |args| is the list of arguments to
@@ -118,14 +124,14 @@ class Session {
Error* SendKeys(const WebElementId& element, const string16& keys);
// Sets the file paths to the file upload control under the given location.
- Error* DragAndDropFilePaths(const gfx::Point& location,
+ Error* DragAndDropFilePaths(const Point& location,
const std::vector<FilePath::StringType>& paths);
// Clicks the mouse at the given location using the given button.
- Error* MouseMoveAndClick(const gfx::Point& location,
+ Error* MouseMoveAndClick(const Point& location,
automation::MouseButton button);
- Error* MouseMove(const gfx::Point& location);
- Error* MouseDrag(const gfx::Point& start, const gfx::Point& end);
+ Error* MouseMove(const Point& location);
+ Error* MouseDrag(const Point& start, const Point& end);
Error* MouseClick(automation::MouseButton button);
Error* MouseButtonDown();
Error* MouseButtonUp();
@@ -214,7 +220,7 @@ class Session {
// the client's viewport.
Error* GetElementLocationInView(
const WebElementId& element,
- gfx::Point* location);
+ Point* location);
// Scroll the element's region into view and get its location relative to
// the client's viewport. If |center| is true, the element will be centered
@@ -223,22 +229,22 @@ class Session {
// of the given region.
Error* GetElementRegionInView(
const WebElementId& element,
- const gfx::Rect& region,
+ const Rect& region,
bool center,
bool verify_clickable_at_middle,
- gfx::Point* location);
+ Point* location);
// Gets the size of the element from the given window and frame, even if
// its display is none.
Error* GetElementSize(const FrameId& frame_id,
const WebElementId& element,
- gfx::Size* size);
+ Size* size);
// Gets the size of the element's first client rect. If the element has
// no client rects, this will return an error.
Error* GetElementFirstClientRect(const FrameId& frame_id,
const WebElementId& element,
- gfx::Rect* rect);
+ Rect* rect);
// Gets the element's effective style for the given property.
Error* GetElementEffectiveStyle(
@@ -289,7 +295,7 @@ class Session {
// location of the element. If the element is not clickable, or if the
// location cannot be determined, an error will be returned.
Error* GetClickableLocation(const WebElementId& element,
- gfx::Point* location);
+ Point* location);
// Gets the attribute of the given element. If there are no errors, the
// function sets |value| and the caller takes ownership.
@@ -312,7 +318,7 @@ class Session {
void set_implicit_wait(int timeout_ms);
int implicit_wait() const;
- const gfx::Point& get_mouse_position() const;
+ const Point& get_mouse_position() const;
const Options& options() const;
@@ -334,9 +340,9 @@ class Session {
// Executes the given |script| in the context of the given frame.
// Waits for script to finish and parses the response.
// The caller is responsible for the script result |value|.
- Error* ExecuteScriptAndParseResponse(const FrameId& frame_id,
- const std::string& script,
- base::Value** value);
+ Error* ExecuteScriptAndParseValue(const FrameId& frame_id,
+ const std::string& script,
+ base::Value** value);
void SendKeysOnSessionThread(const string16& keys, Error** error);
Error* SwitchToFrameWithJavaScriptLocatedFrame(
@@ -348,18 +354,24 @@ class Session {
const std::string& query,
bool find_one,
std::vector<WebElementId>* elements);
+ Error* ExecuteFindElementScriptAndParse(const FrameId& frame_id,
+ const WebElementId& root_element,
+ const std::string& locator,
+ const std::string& query,
+ bool find_one,
+ std::vector<WebElementId>* elements);
// Returns an error if the element is not clickable.
Error* VerifyElementIsClickable(
const FrameId& frame_id,
const WebElementId& element,
- const gfx::Point& location);
+ const Point& location);
Error* GetElementRegionInViewHelper(
const FrameId& frame_id,
const WebElementId& element,
- const gfx::Rect& region,
+ const Rect& region,
bool center,
bool verify_clickable_at_middle,
- gfx::Point* location);
+ Point* location);
const std::string id_;
FrameId current_target_;
@@ -379,7 +391,7 @@ class Session {
std::vector<WebElementId> frame_elements_;
// Last mouse position. Advanced APIs need this value.
- gfx::Point mouse_position_;
+ Point mouse_position_;
// Chrome does not have an individual method for setting the prompt text
// of an alert. Instead, when the WebDriver client wants to set the text,
diff --git a/chrome/test/webdriver/utility_functions.h b/chrome/test/webdriver/utility_functions.h
deleted file mode 100644
index e42b8f7..0000000
--- a/chrome/test/webdriver/utility_functions.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_TEST_WEBDRIVER_UTILITY_FUNCTIONS_H_
-#define CHROME_TEST_WEBDRIVER_UTILITY_FUNCTIONS_H_
-
-#include <string>
-#include <vector>
-
-class FilePath;
-
-namespace base {
-class Value;
-}
-
-namespace webdriver {
-
-// Generates a random, 32-character hexidecimal ID.
-std::string GenerateRandomID();
-
-// Returns the equivalent JSON string for the given value.
-std::string JsonStringify(const base::Value* value);
-
-#if defined(OS_MACOSX)
-// Gets the paths to the user and local application directory.
-void GetApplicationDirs(std::vector<FilePath>* app_dirs);
-#endif
-
-} // namespace webdriver
-
-#endif // CHROME_TEST_WEBDRIVER_UTILITY_FUNCTIONS_H_
diff --git a/chrome/test/webdriver/web_element_id.cc b/chrome/test/webdriver/web_element_id.cc
index 833cb27..0344ab0 100644
--- a/chrome/test/webdriver/web_element_id.cc
+++ b/chrome/test/webdriver/web_element_id.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "base/values.h"
+#include "chrome/test/automation/javascript_message_utils.h"
namespace {
@@ -30,10 +31,10 @@ WebElementId::WebElementId() : is_valid_(false) {}
WebElementId::WebElementId(const std::string& id) : id_(id), is_valid_(true) {}
-WebElementId::WebElementId(Value* value) {
+WebElementId::WebElementId(const Value* value) {
is_valid_ = false;
if (value->IsType(Value::TYPE_DICTIONARY)) {
- is_valid_ = static_cast<DictionaryValue*>(value)->
+ is_valid_ = static_cast<const DictionaryValue*>(value)->
GetString(kWebElementKey, &id_);
}
}
@@ -54,3 +55,22 @@ bool WebElementId::is_valid() const {
}
} // namespace webdriver
+
+base::Value* ValueConversionTraits<webdriver::WebElementId>::CreateValueFrom(
+ const webdriver::WebElementId& t) {
+ return t.ToValue();
+}
+
+bool ValueConversionTraits<webdriver::WebElementId>::SetFromValue(
+ const base::Value* value, webdriver::WebElementId* t) {
+ webdriver::WebElementId id(value);
+ if (id.is_valid())
+ *t = id;
+ return id.is_valid();
+}
+
+bool ValueConversionTraits<webdriver::WebElementId>::CanConvert(
+ const base::Value* value) {
+ webdriver::WebElementId t;
+ return SetFromValue(value, &t);
+}
diff --git a/chrome/test/webdriver/web_element_id.h b/chrome/test/webdriver/web_element_id.h
index 037f633..05716f5e 100644
--- a/chrome/test/webdriver/web_element_id.h
+++ b/chrome/test/webdriver/web_element_id.h
@@ -8,6 +8,8 @@
#include <string>
+#include "chrome/test/automation/value_conversion_traits.h"
+
namespace base {
class Value;
}
@@ -27,7 +29,7 @@ class WebElementId {
// Creates a |WebElementId| from an element dictionary returned by a WebDriver
// atom. It will be valid iff the dictionary is correctly constructed.
- explicit WebElementId(base::Value* value);
+ explicit WebElementId(const base::Value* value);
~WebElementId();
@@ -60,4 +62,12 @@ struct LocatorType {
} // namespace webdriver
+template <>
+struct ValueConversionTraits<webdriver::WebElementId> {
+ static base::Value* CreateValueFrom(const webdriver::WebElementId& t);
+ static bool SetFromValue(
+ const base::Value* value, webdriver::WebElementId* t);
+ static bool CanConvert(const base::Value* value);
+};
+
#endif // CHROME_TEST_WEBDRIVER_WEB_ELEMENT_ID_H_
diff --git a/chrome/test/webdriver/webdriver_basic_types.cc b/chrome/test/webdriver/webdriver_basic_types.cc
new file mode 100644
index 0000000..c72948c
--- /dev/null
+++ b/chrome/test/webdriver/webdriver_basic_types.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/webdriver/webdriver_basic_types.h"
+
+#include <cmath>
+
+#include "base/values.h"
+
+using base::Value;
+
+namespace webdriver {
+
+Point::Point() { }
+
+Point::Point(double x, double y) : x_(x), y_(y) { }
+
+Point::~Point() { }
+
+void Point::Offset(double x, double y) {
+ x_ += x;
+ y_ += y;
+}
+
+double Point::x() const {
+ return x_;
+}
+
+double Point::y() const {
+ return y_;
+}
+
+int Point::rounded_x() const {
+ int truncated = static_cast<int>(x_);
+ if (std::abs(x_ - truncated) < 0.5)
+ return truncated;
+ return truncated + 1;
+}
+
+int Point::rounded_y() const {
+ int truncated = static_cast<int>(y_);
+ if (std::abs(y_ - truncated) < 0.5)
+ return truncated;
+ return truncated + 1;
+}
+
+Size::Size() { }
+
+Size::Size(double width, double height) : width_(width), height_(height) { }
+
+Size::~Size() { }
+
+double Size::width() const {
+ return width_;
+}
+
+double Size::height() const {
+ return height_;
+}
+
+Rect::Rect() { }
+
+Rect::Rect(double x, double y, double width, double height)
+ : origin_(x, y), size_(width, height) { }
+
+Rect::Rect(const Point& origin, const Size& size)
+ : origin_(origin), size_(size) { }
+
+Rect::~Rect() { }
+
+const Point& Rect::origin() const {
+ return origin_;
+}
+
+const Size& Rect::size() const {
+ return size_;
+}
+
+double Rect::x() const {
+ return origin_.x();
+}
+
+double Rect::y() const {
+ return origin_.y();
+}
+
+double Rect::width() const {
+ return size_.width();
+}
+
+double Rect::height() const {
+ return size_.height();
+}
+
+} // namespace webdriver
+
+Value* ValueConversionTraits<webdriver::Point>::CreateValueFrom(
+ const webdriver::Point& t) {
+ DictionaryValue* value = new DictionaryValue();
+ value->SetDouble("x", t.x());
+ value->SetDouble("y", t.y());
+ return value;
+}
+
+bool ValueConversionTraits<webdriver::Point>::SetFromValue(
+ const Value* value, webdriver::Point* t) {
+ if (!value->IsType(Value::TYPE_DICTIONARY))
+ return false;
+
+ const DictionaryValue* dict_value =
+ static_cast<const DictionaryValue*>(value);
+ double x, y;
+ if (!dict_value->GetDouble("x", &x) ||
+ !dict_value->GetDouble("y", &y))
+ return false;
+ *t = webdriver::Point(x, y);
+ return true;
+}
+
+bool ValueConversionTraits<webdriver::Point>::CanConvert(const Value* value) {
+ webdriver::Point t;
+ return SetFromValue(value, &t);
+}
+
+Value* ValueConversionTraits<webdriver::Size>::CreateValueFrom(
+ const webdriver::Size& t) {
+ DictionaryValue* value = new DictionaryValue();
+ value->SetDouble("width", t.width());
+ value->SetDouble("height", t.height());
+ return value;
+}
+
+bool ValueConversionTraits<webdriver::Size>::SetFromValue(
+ const Value* value, webdriver::Size* t) {
+ if (!value->IsType(Value::TYPE_DICTIONARY))
+ return false;
+
+ const DictionaryValue* dict_value =
+ static_cast<const DictionaryValue*>(value);
+ double width, height;
+ if (!dict_value->GetDouble("width", &width) ||
+ !dict_value->GetDouble("height", &height))
+ return false;
+ *t = webdriver::Size(width, height);
+ return true;
+}
+
+bool ValueConversionTraits<webdriver::Size>::CanConvert(const Value* value) {
+ webdriver::Size t;
+ return SetFromValue(value, &t);
+}
+
+Value* ValueConversionTraits<webdriver::Rect>::CreateValueFrom(
+ const webdriver::Rect& t) {
+ DictionaryValue* value = new DictionaryValue();
+ value->SetDouble("left", t.x());
+ value->SetDouble("top", t.y());
+ value->SetDouble("width", t.width());
+ value->SetDouble("height", t.height());
+ return value;
+}
+
+bool ValueConversionTraits<webdriver::Rect>::SetFromValue(
+ const Value* value, webdriver::Rect* t) {
+ if (!value->IsType(Value::TYPE_DICTIONARY))
+ return false;
+
+ const DictionaryValue* dict_value =
+ static_cast<const DictionaryValue*>(value);
+ double x, y, width, height;
+ if (!dict_value->GetDouble("left", &x) ||
+ !dict_value->GetDouble("top", &y) ||
+ !dict_value->GetDouble("width", &width) ||
+ !dict_value->GetDouble("height", &height))
+ return false;
+ *t = webdriver::Rect(x, y, width, height);
+ return true;
+}
+
+bool ValueConversionTraits<webdriver::Rect>::CanConvert(const Value* value) {
+ webdriver::Rect t;
+ return SetFromValue(value, &t);
+}
diff --git a/chrome/test/webdriver/webdriver_basic_types.h b/chrome/test/webdriver/webdriver_basic_types.h
new file mode 100644
index 0000000..c96fcfb
--- /dev/null
+++ b/chrome/test/webdriver/webdriver_basic_types.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_TEST_WEBDRIVER_WEBDRIVER_BASIC_TYPES_H_
+#define CHROME_TEST_WEBDRIVER_WEBDRIVER_BASIC_TYPES_H_
+#pragma once
+
+#include "chrome/test/automation/value_conversion_traits.h"
+
+namespace base {
+class Value;
+}
+
+namespace webdriver {
+
+class Point {
+ public:
+ Point();
+ Point(double x, double y);
+ ~Point();
+
+ void Offset(double x, double y);
+
+ double x() const;
+ double y() const;
+ int rounded_x() const;
+ int rounded_y() const;
+
+ private:
+ double x_, y_;
+};
+
+class Size {
+ public:
+ Size();
+ Size(double width, double height);
+ ~Size();
+
+ double width() const;
+ double height() const;
+
+ private:
+ double width_, height_;
+};
+
+class Rect {
+ public:
+ Rect();
+ Rect(double x, double y, double width, double height);
+ Rect(const Point& origin, const Size& size);
+ ~Rect();
+
+ const Point& origin() const;
+ const Size& size() const;
+ double x() const;
+ double y() const;
+ double width() const;
+ double height() const;
+
+ private:
+ Point origin_;
+ Size size_;
+};
+
+} // namespace webdriver
+
+template <>
+struct ValueConversionTraits<webdriver::Point> {
+ static base::Value* CreateValueFrom(const webdriver::Point& t);
+ static bool SetFromValue(const base::Value* value, webdriver::Point* t);
+ static bool CanConvert(const base::Value* value);
+};
+
+template <>
+struct ValueConversionTraits<webdriver::Size> {
+ static base::Value* CreateValueFrom(const webdriver::Size& t);
+ static bool SetFromValue(const base::Value* value, webdriver::Size* t);
+ static bool CanConvert(const base::Value* value);
+};
+
+template <>
+struct ValueConversionTraits<webdriver::Rect> {
+ static base::Value* CreateValueFrom(const webdriver::Rect& t);
+ static bool SetFromValue(const base::Value* value, webdriver::Rect* t);
+ static bool CanConvert(const base::Value* value);
+};
+
+#endif // CHROME_TEST_WEBDRIVER_WEBDRIVER_BASIC_TYPES_H_
diff --git a/chrome/test/webdriver/utility_functions.cc b/chrome/test/webdriver/webdriver_util.cc
index b6af918..45e080f 100644
--- a/chrome/test/webdriver/utility_functions.cc
+++ b/chrome/test/webdriver/webdriver_util.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/test/webdriver/utility_functions.h"
+#include "chrome/test/webdriver/webdriver_util.h"
#include "base/basictypes.h"
#include "base/format_macros.h"
@@ -13,6 +13,8 @@
namespace webdriver {
+SkipParsing* kSkipParsing = NULL;
+
std::string GenerateRandomID() {
uint64 msb = base::RandUint64();
uint64 lsb = base::RandUint64();
@@ -25,4 +27,18 @@ std::string JsonStringify(const Value* value) {
return json;
}
+ValueParser::ValueParser() { }
+
+ValueParser::~ValueParser() { }
+
} // namespace webdriver
+
+bool ValueConversionTraits<webdriver::SkipParsing>::SetFromValue(
+ const base::Value* value, const webdriver::SkipParsing* t) {
+ return true;
+}
+
+bool ValueConversionTraits<webdriver::SkipParsing>::CanConvert(
+ const base::Value* value) {
+ return true;
+}
diff --git a/chrome/test/webdriver/webdriver_util.h b/chrome/test/webdriver/webdriver_util.h
new file mode 100644
index 0000000..e36bdb7
--- /dev/null
+++ b/chrome/test/webdriver/webdriver_util.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_TEST_WEBDRIVER_WEBDRIVER_UTIL_H_
+#define CHROME_TEST_WEBDRIVER_WEBDRIVER_UTIL_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/test/automation/value_conversion_traits.h"
+
+class FilePath;
+
+namespace base {
+class Value;
+}
+
+namespace webdriver {
+
+// Generates a random, 32-character hexidecimal ID.
+std::string GenerateRandomID();
+
+// Returns the equivalent JSON string for the given value.
+std::string JsonStringify(const base::Value* value);
+
+#if defined(OS_MACOSX)
+// Gets the paths to the user and local application directory.
+void GetApplicationDirs(std::vector<FilePath>* app_dirs);
+#endif
+
+// Parses a given value.
+class ValueParser {
+ public:
+ virtual ~ValueParser();
+ virtual bool Parse(base::Value* value) const = 0;
+
+ protected:
+ ValueParser();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ValueParser);
+};
+
+// Define a special type and constant that allows users to skip parsing a value.
+// Useful when wanting to skip parsing for one value out of many in a list.
+enum SkipParsing { };
+extern SkipParsing* kSkipParsing;
+
+// Parses a given value using the |ValueConversionTraits| to the appropriate
+// type. This assumes that a direct conversion can be performed without
+// pulling the value out of a dictionary or list.
+template <typename T>
+class DirectValueParser : public ValueParser {
+ public:
+ explicit DirectValueParser(T* t) : t_(t) { }
+
+ virtual ~DirectValueParser() { }
+
+ virtual bool Parse(base::Value* value) const OVERRIDE {
+ return ValueConversionTraits<T>::SetFromValue(value, t_);
+ }
+
+ private:
+ T* t_;
+ DISALLOW_COPY_AND_ASSIGN(DirectValueParser);
+};
+
+// Convenience function for creating a DirectValueParser.
+template <typename T>
+DirectValueParser<T>* CreateDirectValueParser(T* t) {
+ return new DirectValueParser<T>(t);
+}
+
+} // namespace webdriver
+
+// Value conversion traits for SkipParsing, which just return true.
+template <>
+struct ValueConversionTraits<webdriver::SkipParsing> {
+ static bool SetFromValue(const base::Value* value,
+ const webdriver::SkipParsing* t);
+ static bool CanConvert(const base::Value* value);
+};
+
+#endif // CHROME_TEST_WEBDRIVER_WEBDRIVER_UTIL_H_
diff --git a/chrome/test/webdriver/utility_functions_mac.mm b/chrome/test/webdriver/webdriver_util_mac.mm
index 0db48f0..dd3ee6f 100644
--- a/chrome/test/webdriver/utility_functions_mac.mm
+++ b/chrome/test/webdriver/webdriver_util_mac.mm
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/test/webdriver/utility_functions.h"
+#include "chrome/test/webdriver/webdriver_util.h"
#import <Foundation/Foundation.h>
diff --git a/chrome/test/webdriver/utility_functions_unittest.cc b/chrome/test/webdriver/webdriver_util_unittest.cc
index 2563905..c80a3d7 100644
--- a/chrome/test/webdriver/utility_functions_unittest.cc
+++ b/chrome/test/webdriver/webdriver_util_unittest.cc
@@ -5,7 +5,7 @@
#include <set>
#include <string>
-#include "chrome/test/webdriver/utility_functions.h"
+#include "chrome/test/webdriver/webdriver_util.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(RandomIDTest, CanGenerateSufficientlyRandomIDs) {