Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(887)

Unified Diff: chrome/test/webdriver/session.cc

Issue 6507015: Implement the target locator commands for ChromeDriver. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: ... Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/test/webdriver/session.h ('k') | chrome/test/webdriver/session_manager.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/test/webdriver/session.cc
diff --git a/chrome/test/webdriver/session.cc b/chrome/test/webdriver/session.cc
index bfbefeb8055825f8222388531314e153044591b1..849e7c607a8fbc95388c2ca326a194e6cd1b6447 100644
--- a/chrome/test/webdriver/session.cc
+++ b/chrome/test/webdriver/session.cc
@@ -12,7 +12,9 @@
#include "base/message_loop_proxy.h"
#include "base/process.h"
#include "base/process_util.h"
+#include "base/scoped_ptr.h"
#include "base/stringprintf.h"
+#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
@@ -22,6 +24,7 @@
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/test_launcher_utils.h"
+#include "chrome/test/webdriver/session_manager.h"
#include "chrome/test/webdriver/utility_functions.h"
#include "chrome/test/webdriver/webdriver_key_converter.h"
#include "googleurl/src/gurl.h"
@@ -29,27 +32,31 @@
namespace webdriver {
-Session::Session(const std::string& id)
- : thread_(id.c_str()),
- id_(id),
- window_num_(0),
+Session::Session()
+ : id_(GenerateRandomID()),
+ thread_(id_.c_str()),
implicit_wait_(0),
- current_frame_xpath_("") {
+ current_frame_xpath_(""),
+ current_window_id_(0) {
+ SessionManager::GetInstance()->Add(this);
}
-Session::~Session() {}
+Session::~Session() {
+ SessionManager::GetInstance()->Remove(id_);
+}
bool Session::Init() {
- if (!thread_.Start()) {
+ bool success = false;
+ if (thread_.Start()) {
+ RunSessionTask(NewRunnableMethod(
+ this,
+ &Session::InitOnSessionThread,
+ &success));
+ } else {
LOG(ERROR) << "Cannot start session thread";
- return false;
}
-
- bool success = false;
- RunSessionTask(NewRunnableMethod(
- this,
- &Session::InitOnSessionThread,
- &success));
+ if (!success)
+ delete this;
return success;
}
@@ -57,9 +64,12 @@ void Session::Terminate() {
RunSessionTask(NewRunnableMethod(
this,
&Session::TerminateOnSessionThread));
+ delete this;
}
-ErrorCode Session::ExecuteScript(const std::string& script,
+ErrorCode Session::ExecuteScript(int window_id,
+ const std::string& frame_xpath,
+ const std::string& script,
const ListValue* const args,
Value** value) {
std::string args_as_json;
@@ -80,15 +90,17 @@ ErrorCode Session::ExecuteScript(const std::string& script,
VLOG(1) << "Executing script in frame: " << current_frame_xpath_;
std::string result;
- bool success;
+ bool success = false;
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::ExecuteScript,
- current_frame_xpath_,
+ window_id,
+ frame_xpath,
jscript,
&result,
&success));
if (!success) {
+ LOG(ERROR) << "Automation failed to execute script";
*value = Value::CreateStringValue(
"Unknown internal script execution failure");
return kUnknownError;
@@ -98,12 +110,14 @@ ErrorCode Session::ExecuteScript(const std::string& script,
scoped_ptr<Value> r(base::JSONReader::ReadAndReturnError(
result, true, NULL, NULL));
if (!r.get()) {
+ LOG(ERROR) << "Failed to parse script result";
*value = Value::CreateStringValue(
"Internal script execution error: failed to parse script result");
return kUnknownError;
}
if (r->GetType() != Value::TYPE_DICTIONARY) {
+ LOG(ERROR) << "Execute script returned non-dictionary type";
std::ostringstream stream;
stream << "Internal script execution error: script result must be a "
<< print_valuetype(Value::TYPE_DICTIONARY) << ", but was "
@@ -130,6 +144,13 @@ ErrorCode Session::ExecuteScript(const std::string& script,
return static_cast<ErrorCode>(status);
}
+ErrorCode Session::ExecuteScript(const std::string& script,
+ const ListValue* const args,
+ Value** value) {
+ return ExecuteScript(
+ current_window_id_, current_frame_xpath_, script, args, value);
+}
+
ErrorCode Session::SendKeys(DictionaryValue* element, const string16& keys) {
ListValue args;
args.Append(element);
@@ -138,8 +159,10 @@ ErrorCode Session::SendKeys(DictionaryValue* element, const string16& keys) {
Value* unscoped_result = NULL;
ErrorCode code = ExecuteScript(script, &args, &unscoped_result);
scoped_ptr<Value> result(unscoped_result);
- if (code != kSuccess)
+ if (code != kSuccess) {
+ LOG(ERROR) << "Failed to focus element before sending keys";
return code;
+ }
bool success = false;
RunSessionTask(NewRunnableMethod(
@@ -157,6 +180,7 @@ bool Session::NavigateToURL(const std::string& url) {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::NavigateToURL,
+ current_window_id_,
url,
&success));
return success;
@@ -167,6 +191,7 @@ bool Session::GoForward() {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::GoForward,
+ current_window_id_,
&success));
return success;
}
@@ -176,6 +201,7 @@ bool Session::GoBack() {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::GoBack,
+ current_window_id_,
&success));
return success;
}
@@ -185,6 +211,7 @@ bool Session::Reload() {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::Reload,
+ current_window_id_,
&success));
return success;
}
@@ -194,6 +221,7 @@ bool Session::GetURL(std::string* url) {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::GetURL,
+ current_window_id_,
url,
&success));
return success;
@@ -204,6 +232,7 @@ bool Session::GetURL(GURL* gurl) {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::GetGURL,
+ current_window_id_,
gurl,
&success));
return success;
@@ -214,6 +243,7 @@ bool Session::GetTabTitle(std::string* tab_title) {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::GetTabTitle,
+ current_window_id_,
tab_title,
&success));
return success;
@@ -224,6 +254,7 @@ bool Session::GetCookies(const GURL& url, std::string* cookies) {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::GetCookies,
+ current_window_id_,
url,
cookies,
&success));
@@ -237,6 +268,7 @@ bool Session::GetCookieByName(const GURL& url,
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::GetCookieByName,
+ current_window_id_,
url,
cookie_name,
cookie,
@@ -249,6 +281,7 @@ bool Session::DeleteCookie(const GURL& url, const std::string& cookie_name) {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::DeleteCookie,
+ current_window_id_,
url,
cookie_name,
&success));
@@ -260,12 +293,131 @@ bool Session::SetCookie(const GURL& url, const std::string& cookie) {
RunSessionTask(NewRunnableMethod(
automation_.get(),
&Automation::SetCookie,
+ current_window_id_,
url,
cookie,
&success));
return success;
}
+bool Session::GetWindowIds(std::vector<int>* window_ids) {
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::GetTabIds,
+ window_ids,
+ &success));
+ return success;
+}
+
+ErrorCode Session::SwitchToWindow(const std::string& name) {
+ int switch_to_id = 0;
+ int name_no = 0;
+ if (base::StringToInt(name, &name_no)) {
+ bool does_exist = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::DoesTabExist,
+ name_no,
+ &does_exist));
+ if (does_exist)
+ switch_to_id = name_no;
+ }
+
+ if (!switch_to_id) {
+ std::vector<int> window_ids;
+ GetWindowIds(&window_ids);
+ // 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;
+ std::string window_name;
+ ErrorCode code = ExecuteScript(window_ids[i],
+ "",
+ "return window.name;",
+ &empty_list,
+ &unscoped_name_value);
+ scoped_ptr<Value> name_value(unscoped_name_value);
+ if (code == kSuccess &&
+ name_value->GetAsString(&window_name) &&
+ name == window_name) {
+ switch_to_id = window_ids[i];
+ break;
+ }
+ }
+ }
+
+ if (!switch_to_id)
+ return kNoSuchWindow;
+ current_window_id_ = switch_to_id;
+ current_frame_xpath_ = "";
+ return kSuccess;
+}
+
+ErrorCode 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';"
+ "return xpath + sub('[@' + (frame.id == arg ? 'id' : 'name')"
+ " + '=\"$\"]');";
+ ListValue args;
+ args.Append(new StringValue(name_or_id));
+ return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
+}
+
+ErrorCode Session::SwitchToFrameWithIndex(int index) {
+ // We cannot simply index into window.frames because we need to know the
+ // tagName of the frameElement. If child frame N is from another domain, then
+ // the following will run afoul of the same origin policy:
+ // window.frames[N].frameElement;
+ // Instead of indexing window.frames, we use a an XPath expression to 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);"
+ "return frame == null ? null : ((frame.tagName == 'IFRAME' ? "
+ " '/html/body//iframe' : '/html/frameset/frame') + index);";
+ ListValue args;
+ args.Append(Value::CreateIntegerValue(index));
+ return SwitchToFrameWithJavaScriptLocatedFrame(script, &args);
+}
+
+bool Session::CloseWindow() {
+ bool success = false;
+ RunSessionTask(NewRunnableMethod(
+ automation_.get(),
+ &Automation::CloseTab,
+ current_window_id_,
+ &success));
+
+ if (success) {
+ std::vector<int> window_ids;
+ if (!GetWindowIds(&window_ids) || window_ids.empty()) {
+ // The automation connection will soon be closed, if not already,
+ // because we supposedly just closed the last window. Terminate the
+ // session.
+ // TODO(kkania): This will cause us problems if GetWindowIds fails for a
+ // reason other than the channel is disconnected. Look into having
+ // |GetWindowIds| tell us if it just closed the last window.
+ Terminate();
+ }
+ }
+ return success;
+}
+
void Session::RunSessionTask(Task* task) {
base::WaitableEvent done_event(false, false);
thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(
@@ -286,10 +438,26 @@ void Session::RunSessionTaskOnSessionThread(Task* task,
void Session::InitOnSessionThread(bool* success) {
automation_.reset(new Automation());
automation_->Init(success);
+ if (!*success)
+ return;
+
+ std::vector<int> tab_ids;
+ automation_->GetTabIds(&tab_ids, success);
+ if (!*success) {
+ LOG(ERROR) << "Could not get tab ids";
+ return;
+ }
+ if (tab_ids.empty()) {
+ LOG(ERROR) << "No tab ids after initialization";
+ *success = false;
+ } else {
+ current_window_id_ = tab_ids[0];
+ }
}
void Session::TerminateOnSessionThread() {
- automation_->Terminate();
+ if (automation_.get())
+ automation_->Terminate();
automation_.reset();
}
@@ -300,7 +468,8 @@ void Session::SendKeysOnSessionThread(const string16& keys,
ConvertKeysToWebKeyEvents(keys, &key_events);
for (size_t i = 0; i < key_events.size(); ++i) {
bool key_success = false;
- automation_->SendWebKeyEvent(key_events[i], &key_success);
+ automation_->SendWebKeyEvent(
+ current_window_id_, key_events[i], &key_success);
if (!key_success) {
LOG(ERROR) << "Failed to send key event. Event details:\n"
<< "Type: " << key_events[i].type << "\n"
@@ -313,4 +482,22 @@ void Session::SendKeysOnSessionThread(const string16& keys,
}
}
+ErrorCode Session::SwitchToFrameWithJavaScriptLocatedFrame(
+ const std::string& script,
+ ListValue* args) {
+ Value* unscoped_result = NULL;
+ ErrorCode code = ExecuteScript(script, args, &unscoped_result);
+ scoped_ptr<Value> result(unscoped_result);
+ if (code != kSuccess)
+ return code;
+ std::string xpath;
+ if (result->GetAsString(&xpath)) {
+ if (current_frame_xpath_.length())
+ current_frame_xpath_ += "\n";
+ current_frame_xpath_ += xpath;
+ return kSuccess;
+ }
+ return kNoSuchFrame;
+}
+
} // namespace webdriver
« no previous file with comments | « chrome/test/webdriver/session.h ('k') | chrome/test/webdriver/session_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698