Home | History | Annotate | Download | only in chromedriver
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/test/chromedriver/commands.h"
      6 
      7 #include <list>
      8 #include <utility>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/lazy_instance.h"
     13 #include "base/logging.h"  // For CHECK macros.
     14 #include "base/memory/linked_ptr.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/message_loop/message_loop_proxy.h"
     17 #include "base/run_loop.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/sys_info.h"
     20 #include "base/threading/thread_local.h"
     21 #include "base/values.h"
     22 #include "chrome/test/chromedriver/capabilities.h"
     23 #include "chrome/test/chromedriver/chrome/chrome.h"
     24 #include "chrome/test/chromedriver/chrome/chrome_android_impl.h"
     25 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
     26 #include "chrome/test/chromedriver/chrome/device_manager.h"
     27 #include "chrome/test/chromedriver/chrome/devtools_event_listener.h"
     28 #include "chrome/test/chromedriver/chrome/status.h"
     29 #include "chrome/test/chromedriver/chrome/version.h"
     30 #include "chrome/test/chromedriver/chrome/web_view.h"
     31 #include "chrome/test/chromedriver/chrome_launcher.h"
     32 #include "chrome/test/chromedriver/logging.h"
     33 #include "chrome/test/chromedriver/net/net_util.h"
     34 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
     35 #include "chrome/test/chromedriver/session.h"
     36 #include "chrome/test/chromedriver/session_thread_map.h"
     37 #include "chrome/test/chromedriver/util.h"
     38 
     39 void ExecuteGetStatus(
     40     const base::DictionaryValue& params,
     41     const std::string& session_id,
     42     const CommandCallback& callback) {
     43   base::DictionaryValue build;
     44   build.SetString("version", "alpha");
     45 
     46   base::DictionaryValue os;
     47   os.SetString("name", base::SysInfo::OperatingSystemName());
     48   os.SetString("version", base::SysInfo::OperatingSystemVersion());
     49   os.SetString("arch", base::SysInfo::OperatingSystemArchitecture());
     50 
     51   base::DictionaryValue info;
     52   info.Set("build", build.DeepCopy());
     53   info.Set("os", os.DeepCopy());
     54   callback.Run(
     55       Status(kOk), scoped_ptr<base::Value>(info.DeepCopy()), std::string());
     56 }
     57 
     58 NewSessionParams::NewSessionParams(
     59     Log* log,
     60     SessionThreadMap* session_thread_map,
     61     scoped_refptr<URLRequestContextGetter> context_getter,
     62     const SyncWebSocketFactory& socket_factory,
     63     DeviceManager* device_manager)
     64     : log(log),
     65       session_thread_map(session_thread_map),
     66       context_getter(context_getter),
     67       socket_factory(socket_factory),
     68       device_manager(device_manager) {}
     69 
     70 NewSessionParams::~NewSessionParams() {}
     71 
     72 namespace {
     73 
     74 base::LazyInstance<base::ThreadLocalPointer<Session> >
     75     lazy_tls_session = LAZY_INSTANCE_INITIALIZER;
     76 
     77 Status CreateSessionOnSessionThreadHelper(
     78     const NewSessionParams& bound_params,
     79     const base::DictionaryValue& params,
     80     const std::string& session_id,
     81     scoped_ptr<base::Value>* out_value) {
     82   int port;
     83   if (!FindOpenPort(&port))
     84     return Status(kUnknownError, "failed to find an open port for Chrome");
     85 
     86   const base::DictionaryValue* desired_caps;
     87   if (!params.GetDictionary("desiredCapabilities", &desired_caps))
     88     return Status(kUnknownError, "cannot find dict 'desiredCapabilities'");
     89 
     90   Capabilities capabilities;
     91   Status status = capabilities.Parse(*desired_caps, bound_params.log);
     92   if (status.IsError())
     93     return status;
     94 
     95   // Create Log's and DevToolsEventListener's for ones that are DevTools-based.
     96   // Session will own the Log's, Chrome will own the listeners.
     97   ScopedVector<WebDriverLog> devtools_logs;
     98   ScopedVector<DevToolsEventListener> devtools_event_listeners;
     99   status = CreateLogs(capabilities, &devtools_logs, &devtools_event_listeners);
    100   if (status.IsError())
    101     return status;
    102 
    103   scoped_ptr<Chrome> chrome;
    104   status = LaunchChrome(bound_params.context_getter.get(),
    105                         port,
    106                         bound_params.socket_factory,
    107                         bound_params.log,
    108                         bound_params.device_manager,
    109                         capabilities,
    110                         devtools_event_listeners,
    111                         &chrome);
    112   if (status.IsError())
    113     return status;
    114 
    115   std::list<std::string> web_view_ids;
    116   status = chrome->GetWebViewIds(&web_view_ids);
    117   if (status.IsError() || web_view_ids.empty()) {
    118     chrome->Quit();
    119     return status.IsError() ? status :
    120         Status(kUnknownError, "unable to discover open window in chrome");
    121   }
    122 
    123   scoped_ptr<Session> session(new Session(session_id, chrome.Pass()));
    124   session->devtools_logs.swap(devtools_logs);
    125   session->window = web_view_ids.front();
    126   session->detach = capabilities.detach;
    127   out_value->reset(session->capabilities->DeepCopy());
    128   lazy_tls_session.Pointer()->Set(session.release());
    129   return Status(kOk);
    130 }
    131 
    132 void CreateSessionOnSessionThread(
    133     const scoped_refptr<base::SingleThreadTaskRunner>& cmd_task_runner,
    134     const NewSessionParams& bound_params,
    135     scoped_ptr<base::DictionaryValue> params,
    136     const std::string& session_id,
    137     const CommandCallback& callback_on_cmd) {
    138   scoped_ptr<base::Value> value;
    139   Status status = CreateSessionOnSessionThreadHelper(
    140       bound_params, *params, session_id, &value);
    141   cmd_task_runner->PostTask(
    142       FROM_HERE,
    143       base::Bind(callback_on_cmd, status, base::Passed(&value), session_id));
    144 }
    145 
    146 }  // namespace
    147 
    148 void ExecuteNewSession(
    149     const NewSessionParams& bound_params,
    150     const base::DictionaryValue& params,
    151     const std::string& session_id,
    152     const CommandCallback& callback) {
    153   std::string new_id = session_id;
    154   if (new_id.empty())
    155     new_id = GenerateId();
    156   scoped_ptr<base::Thread> thread(new base::Thread(new_id.c_str()));
    157   if (!thread->Start()) {
    158     callback.Run(
    159         Status(kUnknownError, "failed to start a thread for the new session"),
    160         scoped_ptr<base::Value>(),
    161         std::string());
    162     return;
    163   }
    164 
    165   thread->message_loop()
    166       ->PostTask(FROM_HERE,
    167                  base::Bind(&CreateSessionOnSessionThread,
    168                             base::MessageLoopProxy::current(),
    169                             bound_params,
    170                             base::Passed(make_scoped_ptr(params.DeepCopy())),
    171                             new_id,
    172                             callback));
    173   bound_params.session_thread_map
    174       ->insert(std::make_pair(new_id, make_linked_ptr(thread.release())));
    175 }
    176 
    177 namespace {
    178 
    179 void OnSessionQuit(const base::WeakPtr<size_t>& quit_remaining_count,
    180                    const base::Closure& all_quit_func,
    181                    const Status& status,
    182                    scoped_ptr<base::Value> value,
    183                    const std::string& session_id) {
    184   // |quit_remaining_count| may no longer be valid if a timeout occurred.
    185   if (!quit_remaining_count)
    186     return;
    187 
    188   (*quit_remaining_count)--;
    189   if (!*quit_remaining_count)
    190     all_quit_func.Run();
    191 }
    192 
    193 }  // namespace
    194 
    195 void ExecuteQuitAll(
    196     const Command& quit_command,
    197     SessionThreadMap* session_thread_map,
    198     const base::DictionaryValue& params,
    199     const std::string& session_id,
    200     const CommandCallback& callback) {
    201   size_t quit_remaining_count = session_thread_map->size();
    202   base::WeakPtrFactory<size_t> weak_ptr_factory(&quit_remaining_count);
    203   if (!quit_remaining_count) {
    204     callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
    205     return;
    206   }
    207   base::RunLoop run_loop;
    208   for (SessionThreadMap::const_iterator iter = session_thread_map->begin();
    209        iter != session_thread_map->end();
    210        ++iter) {
    211     quit_command.Run(params,
    212                      iter->first,
    213                      base::Bind(&OnSessionQuit,
    214                                 weak_ptr_factory.GetWeakPtr(),
    215                                 run_loop.QuitClosure()));
    216   }
    217   base::MessageLoop::current()->PostDelayedTask(
    218       FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(10));
    219   // Uses a nested run loop to block this thread until all the quit
    220   // commands have executed, or the timeout expires.
    221   base::MessageLoop::current()->SetNestableTasksAllowed(true);
    222   run_loop.Run();
    223   callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
    224 }
    225 
    226 namespace {
    227 
    228 void TerminateSessionThreadOnCommandThread(SessionThreadMap* session_thread_map,
    229                                            const std::string& session_id) {
    230   session_thread_map->erase(session_id);
    231 }
    232 
    233 void ExecuteSessionCommandOnSessionThread(
    234     const SessionCommand& command,
    235     bool return_ok_without_session,
    236     scoped_ptr<base::DictionaryValue> params,
    237     scoped_refptr<base::SingleThreadTaskRunner> cmd_task_runner,
    238     const CommandCallback& callback_on_cmd,
    239     const base::Closure& terminate_on_cmd) {
    240   Session* session = lazy_tls_session.Pointer()->Get();
    241   if (!session) {
    242     cmd_task_runner->PostTask(
    243         FROM_HERE,
    244         base::Bind(callback_on_cmd,
    245                    Status(return_ok_without_session ? kOk : kNoSuchSession),
    246                    base::Passed(scoped_ptr<base::Value>()),
    247                    std::string()));
    248     return;
    249   }
    250 
    251   scoped_ptr<base::Value> value;
    252   Status status = command.Run(session, *params, &value);
    253   if (status.IsError() && session->chrome)
    254     status.AddDetails("Session info: chrome=" + session->chrome->GetVersion());
    255 
    256   cmd_task_runner->PostTask(
    257       FROM_HERE,
    258       base::Bind(callback_on_cmd, status, base::Passed(&value), session->id));
    259 
    260   if (session->quit) {
    261     lazy_tls_session.Pointer()->Set(NULL);
    262     delete session;
    263     cmd_task_runner->PostTask(FROM_HERE, terminate_on_cmd);
    264   }
    265 }
    266 
    267 }  // namespace
    268 
    269 void ExecuteSessionCommand(
    270     SessionThreadMap* session_thread_map,
    271     const SessionCommand& command,
    272     bool return_ok_without_session,
    273     const base::DictionaryValue& params,
    274     const std::string& session_id,
    275     const CommandCallback& callback) {
    276   SessionThreadMap::iterator iter = session_thread_map->find(session_id);
    277   if (iter == session_thread_map->end()) {
    278     Status status(return_ok_without_session ? kOk : kNoSuchSession);
    279     callback.Run(status, scoped_ptr<base::Value>(), session_id);
    280   } else {
    281     iter->second->message_loop()
    282         ->PostTask(FROM_HERE,
    283                    base::Bind(&ExecuteSessionCommandOnSessionThread,
    284                               command,
    285                               return_ok_without_session,
    286                               base::Passed(make_scoped_ptr(params.DeepCopy())),
    287                               base::MessageLoopProxy::current(),
    288                               callback,
    289                               base::Bind(&TerminateSessionThreadOnCommandThread,
    290                                          session_thread_map,
    291                                          session_id)));
    292   }
    293 }
    294 
    295 namespace internal {
    296 
    297 void CreateSessionOnSessionThreadForTesting(const std::string& id) {
    298   lazy_tls_session.Pointer()->Set(new Session(id));
    299 }
    300 
    301 }  // namespace internal
    302