| Index: chrome/test/chromedriver/commands.cc
|
| diff --git a/chrome/test/chromedriver/commands.cc b/chrome/test/chromedriver/commands.cc
|
| index 36f403f45d6d3a2ed56dda1bac8b736c6eea7bda..6ab61351985aa967ee28e7afa1208f479b74fdb8 100644
|
| --- a/chrome/test/chromedriver/commands.cc
|
| +++ b/chrome/test/chromedriver/commands.cc
|
| @@ -5,10 +5,19 @@
|
| #include "chrome/test/chromedriver/commands.h"
|
|
|
| #include <list>
|
| +#include <utility>
|
|
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/lazy_instance.h"
|
| #include "base/logging.h" // For CHECK macros.
|
| +#include "base/memory/linked_ptr.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/message_loop/message_loop_proxy.h"
|
| +#include "base/run_loop.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "base/sys_info.h"
|
| +#include "base/threading/thread_local.h"
|
| #include "base/values.h"
|
| #include "chrome/test/chromedriver/capabilities.h"
|
| #include "chrome/test/chromedriver/chrome/chrome.h"
|
| @@ -24,14 +33,13 @@
|
| #include "chrome/test/chromedriver/net/net_util.h"
|
| #include "chrome/test/chromedriver/net/url_request_context_getter.h"
|
| #include "chrome/test/chromedriver/session.h"
|
| -#include "chrome/test/chromedriver/session_map.h"
|
| +#include "chrome/test/chromedriver/session_thread_map.h"
|
| #include "chrome/test/chromedriver/util.h"
|
|
|
| -Status ExecuteGetStatus(
|
| +void ExecuteGetStatus(
|
| const base::DictionaryValue& params,
|
| const std::string& session_id,
|
| - scoped_ptr<base::Value>* out_value,
|
| - std::string* out_session_id) {
|
| + const CommandCallback& callback) {
|
| base::DictionaryValue build;
|
| build.SetString("version", "alpha");
|
|
|
| @@ -43,30 +51,34 @@ Status ExecuteGetStatus(
|
| base::DictionaryValue info;
|
| info.Set("build", build.DeepCopy());
|
| info.Set("os", os.DeepCopy());
|
| - out_value->reset(info.DeepCopy());
|
| - return Status(kOk);
|
| + callback.Run(
|
| + Status(kOk), scoped_ptr<base::Value>(info.DeepCopy()), std::string());
|
| }
|
|
|
| NewSessionParams::NewSessionParams(
|
| Log* log,
|
| - SessionMap* session_map,
|
| + SessionThreadMap* session_thread_map,
|
| scoped_refptr<URLRequestContextGetter> context_getter,
|
| const SyncWebSocketFactory& socket_factory,
|
| DeviceManager* device_manager)
|
| : log(log),
|
| - session_map(session_map),
|
| + session_thread_map(session_thread_map),
|
| context_getter(context_getter),
|
| socket_factory(socket_factory),
|
| device_manager(device_manager) {}
|
|
|
| NewSessionParams::~NewSessionParams() {}
|
|
|
| -Status ExecuteNewSession(
|
| +namespace {
|
| +
|
| +base::LazyInstance<base::ThreadLocalPointer<Session> >
|
| + lazy_tls_session = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +Status CreateSessionOnSessionThreadHelper(
|
| const NewSessionParams& bound_params,
|
| const base::DictionaryValue& params,
|
| const std::string& session_id,
|
| - scoped_ptr<base::Value>* out_value,
|
| - std::string* out_session_id) {
|
| + scoped_ptr<base::Value>* out_value) {
|
| int port;
|
| if (!FindOpenPort(&port))
|
| return Status(kUnknownError, "failed to find an open port for Chrome");
|
| @@ -108,67 +120,182 @@ Status ExecuteNewSession(
|
| Status(kUnknownError, "unable to discover open window in chrome");
|
| }
|
|
|
| - std::string new_id = session_id;
|
| - if (new_id.empty())
|
| - new_id = GenerateId();
|
| - scoped_ptr<Session> session(new Session(new_id, chrome.Pass()));
|
| + scoped_ptr<Session> session(new Session(session_id, chrome.Pass()));
|
| session->devtools_logs.swap(devtools_logs);
|
| - if (!session->thread.Start()) {
|
| - chrome->Quit();
|
| - return Status(kUnknownError,
|
| - "failed to start a thread for the new session");
|
| - }
|
| session->window = web_view_ids.front();
|
| session->detach = capabilities.detach;
|
| out_value->reset(session->capabilities->DeepCopy());
|
| - *out_session_id = new_id;
|
| + lazy_tls_session.Pointer()->Set(session.release());
|
| + return Status(kOk);
|
| +}
|
| +
|
| +void CreateSessionOnSessionThread(
|
| + const scoped_refptr<base::SingleThreadTaskRunner>& cmd_task_runner,
|
| + const NewSessionParams& bound_params,
|
| + scoped_ptr<base::DictionaryValue> params,
|
| + const std::string& session_id,
|
| + const CommandCallback& callback_on_cmd) {
|
| + scoped_ptr<base::Value> value;
|
| + Status status = CreateSessionOnSessionThreadHelper(
|
| + bound_params, *params, session_id, &value);
|
| + cmd_task_runner->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(callback_on_cmd, status, base::Passed(&value), session_id));
|
| +}
|
|
|
| - scoped_refptr<SessionAccessor> accessor(
|
| - new SessionAccessorImpl(session.Pass()));
|
| - bound_params.session_map->Set(new_id, accessor);
|
| +} // namespace
|
|
|
| - return Status(kOk);
|
| +void ExecuteNewSession(
|
| + const NewSessionParams& bound_params,
|
| + const base::DictionaryValue& params,
|
| + const std::string& session_id,
|
| + const CommandCallback& callback) {
|
| + std::string new_id = session_id;
|
| + if (new_id.empty())
|
| + new_id = GenerateId();
|
| + scoped_ptr<base::Thread> thread(new base::Thread(new_id.c_str()));
|
| + if (!thread->Start()) {
|
| + callback.Run(
|
| + Status(kUnknownError, "failed to start a thread for the new session"),
|
| + scoped_ptr<base::Value>(),
|
| + std::string());
|
| + return;
|
| + }
|
| +
|
| + thread->message_loop()
|
| + ->PostTask(FROM_HERE,
|
| + base::Bind(&CreateSessionOnSessionThread,
|
| + base::MessageLoopProxy::current(),
|
| + bound_params,
|
| + base::Passed(make_scoped_ptr(params.DeepCopy())),
|
| + new_id,
|
| + callback));
|
| + bound_params.session_thread_map
|
| + ->insert(std::make_pair(new_id, make_linked_ptr(thread.release())));
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +void OnSessionQuit(const base::WeakPtr<size_t>& quit_remaining_count,
|
| + const base::Closure& all_quit_func,
|
| + const Status& status,
|
| + scoped_ptr<base::Value> value,
|
| + const std::string& session_id) {
|
| + // |quit_remaining_count| may no longer be valid if a timeout occurred.
|
| + if (!quit_remaining_count)
|
| + return;
|
| +
|
| + (*quit_remaining_count)--;
|
| + if (!*quit_remaining_count)
|
| + all_quit_func.Run();
|
| }
|
|
|
| -Status ExecuteQuit(
|
| - bool allow_detach,
|
| - SessionMap* session_map,
|
| +} // namespace
|
| +
|
| +void ExecuteQuitAll(
|
| + const Command& quit_command,
|
| + SessionThreadMap* session_thread_map,
|
| const base::DictionaryValue& params,
|
| const std::string& session_id,
|
| - scoped_ptr<base::Value>* out_value,
|
| - std::string* out_session_id) {
|
| - *out_session_id = session_id;
|
| - scoped_refptr<SessionAccessor> session_accessor;
|
| - if (!session_map->Get(session_id, &session_accessor))
|
| - return Status(kOk);
|
| - scoped_ptr<base::AutoLock> session_lock;
|
| - Session* session = session_accessor->Access(&session_lock);
|
| - if (!session)
|
| - return Status(kOk);
|
| - CHECK(session_map->Remove(session->id));
|
| - if (allow_detach && session->detach) {
|
| - session_accessor->DeleteSession();
|
| - return Status(kOk);
|
| - } else {
|
| - Status status = session->chrome->Quit();
|
| - session_accessor->DeleteSession();
|
| - return status;
|
| + const CommandCallback& callback) {
|
| + size_t quit_remaining_count = session_thread_map->size();
|
| + base::WeakPtrFactory<size_t> weak_ptr_factory(&quit_remaining_count);
|
| + if (!quit_remaining_count) {
|
| + callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
|
| + return;
|
| + }
|
| + base::RunLoop run_loop;
|
| + for (SessionThreadMap::const_iterator iter = session_thread_map->begin();
|
| + iter != session_thread_map->end();
|
| + ++iter) {
|
| + quit_command.Run(params,
|
| + iter->first,
|
| + base::Bind(&OnSessionQuit,
|
| + weak_ptr_factory.GetWeakPtr(),
|
| + run_loop.QuitClosure()));
|
| + }
|
| + base::MessageLoop::current()->PostDelayedTask(
|
| + FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(10));
|
| + // Uses a nested run loop to block this thread until all the quit
|
| + // commands have executed, or the timeout expires.
|
| + base::MessageLoop::current()->SetNestableTasksAllowed(true);
|
| + run_loop.Run();
|
| + callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +void TerminateSessionThreadOnCommandThread(SessionThreadMap* session_thread_map,
|
| + const std::string& session_id) {
|
| + session_thread_map->erase(session_id);
|
| +}
|
| +
|
| +void ExecuteSessionCommandOnSessionThread(
|
| + const SessionCommand& command,
|
| + bool return_ok_without_session,
|
| + scoped_ptr<base::DictionaryValue> params,
|
| + scoped_refptr<base::SingleThreadTaskRunner> cmd_task_runner,
|
| + const CommandCallback& callback_on_cmd,
|
| + const base::Closure& terminate_on_cmd) {
|
| + Session* session = lazy_tls_session.Pointer()->Get();
|
| + if (!session) {
|
| + cmd_task_runner->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(callback_on_cmd,
|
| + Status(return_ok_without_session ? kOk : kNoSuchSession),
|
| + base::Passed(scoped_ptr<base::Value>()),
|
| + std::string()));
|
| + return;
|
| + }
|
| +
|
| + scoped_ptr<base::Value> value;
|
| + Status status = command.Run(session, *params, &value);
|
| + if (status.IsError() && session->chrome)
|
| + status.AddDetails("Session info: chrome=" + session->chrome->GetVersion());
|
| +
|
| + cmd_task_runner->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(callback_on_cmd, status, base::Passed(&value), session->id));
|
| +
|
| + if (session->quit) {
|
| + lazy_tls_session.Pointer()->Set(NULL);
|
| + delete session;
|
| + cmd_task_runner->PostTask(FROM_HERE, terminate_on_cmd);
|
| }
|
| }
|
|
|
| -Status ExecuteQuitAll(
|
| - Command quit_command,
|
| - SessionMap* session_map,
|
| +} // namespace
|
| +
|
| +void ExecuteSessionCommand(
|
| + SessionThreadMap* session_thread_map,
|
| + const SessionCommand& command,
|
| + bool return_ok_without_session,
|
| const base::DictionaryValue& params,
|
| const std::string& session_id,
|
| - scoped_ptr<base::Value>* out_value,
|
| - std::string* out_session_id) {
|
| - std::vector<std::string> session_ids;
|
| - session_map->GetKeys(&session_ids);
|
| - for (size_t i = 0; i < session_ids.size(); ++i) {
|
| - scoped_ptr<base::Value> unused_value;
|
| - std::string unused_session_id;
|
| - quit_command.Run(params, session_ids[i], &unused_value, &unused_session_id);
|
| + const CommandCallback& callback) {
|
| + SessionThreadMap::iterator iter = session_thread_map->find(session_id);
|
| + if (iter == session_thread_map->end()) {
|
| + Status status(return_ok_without_session ? kOk : kNoSuchSession);
|
| + callback.Run(status, scoped_ptr<base::Value>(), session_id);
|
| + } else {
|
| + iter->second->message_loop()
|
| + ->PostTask(FROM_HERE,
|
| + base::Bind(&ExecuteSessionCommandOnSessionThread,
|
| + command,
|
| + return_ok_without_session,
|
| + base::Passed(make_scoped_ptr(params.DeepCopy())),
|
| + base::MessageLoopProxy::current(),
|
| + callback,
|
| + base::Bind(&TerminateSessionThreadOnCommandThread,
|
| + session_thread_map,
|
| + session_id)));
|
| }
|
| - return Status(kOk);
|
| }
|
| +
|
| +namespace internal {
|
| +
|
| +void CreateSessionOnSessionThreadForTesting(const std::string& id) {
|
| + lazy_tls_session.Pointer()->Set(new Session(id));
|
| +}
|
| +
|
| +} // namespace internal
|
|
|