Chromium Code Reviews| Index: chrome/test/webdriver/session.cc |
| diff --git a/chrome/test/webdriver/session.cc b/chrome/test/webdriver/session.cc |
| index 665726ed4ab7c39c5792e946d4cc10176425dfef..34457b99ee2e3153d8b4dbadcb89a1438cd58b0f 100644 |
| --- a/chrome/test/webdriver/session.cc |
| +++ b/chrome/test/webdriver/session.cc |
| @@ -12,6 +12,8 @@ |
| #include "base/message_loop_proxy.h" |
| #include "base/process.h" |
| #include "base/process_util.h" |
| +#include "base/scoped_ptr.h" |
| +#include "base/string_number_conversions.h" |
| #include "base/string_util.h" |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| @@ -21,33 +23,38 @@ |
| #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 "third_party/webdriver/atoms.h" |
| 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; |
| } |
| @@ -55,9 +62,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; |
| @@ -82,7 +92,8 @@ ErrorCode Session::ExecuteScript(const std::string& script, |
| RunSessionTask(NewRunnableMethod( |
| automation_.get(), |
| &Automation::ExecuteScript, |
| - current_frame_xpath_, |
| + window_id, |
| + frame_xpath, |
| jscript, |
| &result, |
| &success)); |
| @@ -128,6 +139,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); |
| @@ -155,6 +173,7 @@ bool Session::NavigateToURL(const std::string& url) { |
| RunSessionTask(NewRunnableMethod( |
| automation_.get(), |
| &Automation::NavigateToURL, |
| + current_window_id_, |
| url, |
| &success)); |
| return success; |
| @@ -165,6 +184,7 @@ bool Session::GoForward() { |
| RunSessionTask(NewRunnableMethod( |
| automation_.get(), |
| &Automation::GoForward, |
| + current_window_id_, |
| &success)); |
| return success; |
| } |
| @@ -174,6 +194,7 @@ bool Session::GoBack() { |
| RunSessionTask(NewRunnableMethod( |
| automation_.get(), |
| &Automation::GoBack, |
| + current_window_id_, |
| &success)); |
| return success; |
| } |
| @@ -183,6 +204,7 @@ bool Session::Reload() { |
| RunSessionTask(NewRunnableMethod( |
| automation_.get(), |
| &Automation::Reload, |
| + current_window_id_, |
| &success)); |
| return success; |
| } |
| @@ -192,6 +214,7 @@ bool Session::GetURL(std::string* url) { |
| RunSessionTask(NewRunnableMethod( |
| automation_.get(), |
| &Automation::GetURL, |
| + current_window_id_, |
| url, |
| &success)); |
| return success; |
| @@ -202,11 +225,130 @@ bool Session::GetTabTitle(std::string* tab_title) { |
| RunSessionTask(NewRunnableMethod( |
| automation_.get(), |
| &Automation::GetTabTitle, |
| + current_window_id_, |
| tab_title, |
| &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) { |
| + std::vector<int> window_ids; |
| + if (!GetWindowIds(&window_ids)) { |
| + LOG(ERROR) << "Could not get window handles"; |
| + return kUnknownError; |
| + } |
| + int switch_to_id = 0; |
| + int name_no = 0; |
| + if (base::StringToInt(name, &name_no)) { |
| + for (size_t i = 0; i < window_ids.size(); ++i) { |
|
Paweł Hajdan Jr.
2011/02/14 09:29:43
Another case of linear-scan lookup. Can we make it
kkania
2011/02/14 17:42:12
I can add a method to Automation which does someth
|
| + if (window_ids[i] == name_no) { |
| + switch_to_id = name_no; |
| + break; |
| + } |
| + } |
| + } |
| + |
| + if (!switch_to_id) { |
| + // 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; |
| + |
| + std::vector<int> window_ids; |
| + if (!GetWindowIds(&window_ids) || window_ids.empty()) { |
| + LOG(ERROR) << "Could not get window ids"; |
| + return false; |
| + } |
| + RunSessionTask(NewRunnableMethod( |
| + automation_.get(), |
| + &Automation::CloseTab, |
| + current_window_id_, |
| + &success)); |
| + if (success) { |
| + // The automation connection will have closed if we just closed the last |
| + // window, so terminate the session. |
| + if (window_ids.size() == 1u) |
| + Terminate(); |
| + } |
| + return success; |
| +} |
| + |
| void Session::RunSessionTask(Task* task) { |
| base::WaitableEvent done_event(false, false); |
| thread_.message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod( |
| @@ -227,10 +369,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(); |
| } |
| @@ -241,7 +399,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" |
| @@ -254,4 +413,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 |