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 <algorithm>
      8 #include <list>
      9 #include <utility>
     10 
     11 #include "base/bind.h"
     12 #include "base/bind_helpers.h"
     13 #include "base/logging.h"
     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/values.h"
     21 #include "chrome/test/chromedriver/capabilities.h"
     22 #include "chrome/test/chromedriver/chrome/chrome.h"
     23 #include "chrome/test/chromedriver/chrome/status.h"
     24 #include "chrome/test/chromedriver/logging.h"
     25 #include "chrome/test/chromedriver/session.h"
     26 #include "chrome/test/chromedriver/session_thread_map.h"
     27 #include "chrome/test/chromedriver/util.h"
     28 
     29 void ExecuteGetStatus(
     30     const base::DictionaryValue& params,
     31     const std::string& session_id,
     32     const CommandCallback& callback) {
     33   base::DictionaryValue build;
     34   build.SetString("version", "alpha");
     35 
     36   base::DictionaryValue os;
     37   os.SetString("name", base::SysInfo::OperatingSystemName());
     38   os.SetString("version", base::SysInfo::OperatingSystemVersion());
     39   os.SetString("arch", base::SysInfo::OperatingSystemArchitecture());
     40 
     41   base::DictionaryValue info;
     42   info.Set("build", build.DeepCopy());
     43   info.Set("os", os.DeepCopy());
     44   callback.Run(
     45       Status(kOk), scoped_ptr<base::Value>(info.DeepCopy()), std::string());
     46 }
     47 
     48 void ExecuteCreateSession(
     49     SessionThreadMap* session_thread_map,
     50     const Command& init_session_cmd,
     51     const base::DictionaryValue& params,
     52     const std::string& session_id,
     53     const CommandCallback& callback) {
     54   std::string new_id = session_id;
     55   if (new_id.empty())
     56     new_id = GenerateId();
     57   scoped_ptr<Session> session(new Session(new_id));
     58   scoped_ptr<base::Thread> thread(new base::Thread(new_id.c_str()));
     59   if (!thread->Start()) {
     60     callback.Run(
     61         Status(kUnknownError, "failed to start a thread for the new session"),
     62         scoped_ptr<base::Value>(),
     63         std::string());
     64     return;
     65   }
     66 
     67   thread->message_loop()->PostTask(
     68       FROM_HERE, base::Bind(&SetThreadLocalSession, base::Passed(&session)));
     69   session_thread_map
     70       ->insert(std::make_pair(new_id, make_linked_ptr(thread.release())));
     71   init_session_cmd.Run(params, new_id, callback);
     72 }
     73 
     74 namespace {
     75 
     76 void OnSessionQuit(const base::WeakPtr<size_t>& quit_remaining_count,
     77                    const base::Closure& all_quit_func,
     78                    const Status& status,
     79                    scoped_ptr<base::Value> value,
     80                    const std::string& session_id) {
     81   // |quit_remaining_count| may no longer be valid if a timeout occurred.
     82   if (!quit_remaining_count)
     83     return;
     84 
     85   (*quit_remaining_count)--;
     86   if (!*quit_remaining_count)
     87     all_quit_func.Run();
     88 }
     89 
     90 }  // namespace
     91 
     92 void ExecuteQuitAll(
     93     const Command& quit_command,
     94     SessionThreadMap* session_thread_map,
     95     const base::DictionaryValue& params,
     96     const std::string& session_id,
     97     const CommandCallback& callback) {
     98   size_t quit_remaining_count = session_thread_map->size();
     99   base::WeakPtrFactory<size_t> weak_ptr_factory(&quit_remaining_count);
    100   if (!quit_remaining_count) {
    101     callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
    102     return;
    103   }
    104   base::RunLoop run_loop;
    105   for (SessionThreadMap::const_iterator iter = session_thread_map->begin();
    106        iter != session_thread_map->end();
    107        ++iter) {
    108     quit_command.Run(params,
    109                      iter->first,
    110                      base::Bind(&OnSessionQuit,
    111                                 weak_ptr_factory.GetWeakPtr(),
    112                                 run_loop.QuitClosure()));
    113   }
    114   base::MessageLoop::current()->PostDelayedTask(
    115       FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(10));
    116   // Uses a nested run loop to block this thread until all the quit
    117   // commands have executed, or the timeout expires.
    118   base::MessageLoop::current()->SetNestableTasksAllowed(true);
    119   run_loop.Run();
    120   callback.Run(Status(kOk), scoped_ptr<base::Value>(), session_id);
    121 }
    122 
    123 namespace {
    124 
    125 void TerminateSessionThreadOnCommandThread(SessionThreadMap* session_thread_map,
    126                                            const std::string& session_id) {
    127   session_thread_map->erase(session_id);
    128 }
    129 
    130 void ExecuteSessionCommandOnSessionThread(
    131     const char* command_name,
    132     const SessionCommand& command,
    133     bool return_ok_without_session,
    134     scoped_ptr<base::DictionaryValue> params,
    135     scoped_refptr<base::SingleThreadTaskRunner> cmd_task_runner,
    136     const CommandCallback& callback_on_cmd,
    137     const base::Closure& terminate_on_cmd) {
    138   Session* session = GetThreadLocalSession();
    139   if (!session) {
    140     cmd_task_runner->PostTask(
    141         FROM_HERE,
    142         base::Bind(callback_on_cmd,
    143                    Status(return_ok_without_session ? kOk : kNoSuchSession),
    144                    base::Passed(scoped_ptr<base::Value>()),
    145                    std::string()));
    146     return;
    147   }
    148 
    149   if (IsVLogOn(0)) {
    150     VLOG(0) << "COMMAND " << command_name << " "
    151             << FormatValueForDisplay(*params);
    152   }
    153   scoped_ptr<base::Value> value;
    154   Status status = command.Run(session, *params, &value);
    155 
    156   if (status.IsError() && session->chrome) {
    157     if (!session->quit && session->chrome->HasCrashedWebView()) {
    158       session->quit = true;
    159       std::string message("session deleted because of page crash");
    160       if (!session->detach) {
    161         Status quit_status = session->chrome->Quit();
    162         if (quit_status.IsError())
    163           message += ", but failed to kill browser:" + quit_status.message();
    164       }
    165       status = Status(kUnknownError, message, status);
    166     } else if (status.code() == kDisconnected) {
    167       // Some commands, like clicking a button or link which closes the window,
    168       // may result in a kDisconnected error code.
    169       std::list<std::string> web_view_ids;
    170       Status status_tmp = session->chrome->GetWebViewIds(&web_view_ids);
    171       if (status_tmp.IsError() && status_tmp.code() != kChromeNotReachable) {
    172         status.AddDetails(
    173             "failed to check if window was closed: " + status_tmp.message());
    174       } else if (std::find(web_view_ids.begin(),
    175                            web_view_ids.end(),
    176                            session->window) == web_view_ids.end()) {
    177         status = Status(kOk);
    178       }
    179     }
    180     if (status.IsError()) {
    181       status.AddDetails(
    182           "Session info: chrome=" + session->chrome->GetVersion());
    183     }
    184   }
    185 
    186   if (IsVLogOn(0)) {
    187     std::string result;
    188     if (status.IsError()) {
    189       result = status.message();
    190     } else if (value) {
    191       result = FormatValueForDisplay(*value);
    192     }
    193     VLOG(0) << "RESPONSE " << command_name
    194             << (result.length() ? " " + result : "");
    195   }
    196 
    197   cmd_task_runner->PostTask(
    198       FROM_HERE,
    199       base::Bind(callback_on_cmd, status, base::Passed(&value), session->id));
    200 
    201   if (session->quit) {
    202     SetThreadLocalSession(scoped_ptr<Session>());
    203     delete session;
    204     cmd_task_runner->PostTask(FROM_HERE, terminate_on_cmd);
    205   }
    206 }
    207 
    208 }  // namespace
    209 
    210 void ExecuteSessionCommand(
    211     SessionThreadMap* session_thread_map,
    212     const char* command_name,
    213     const SessionCommand& command,
    214     bool return_ok_without_session,
    215     const base::DictionaryValue& params,
    216     const std::string& session_id,
    217     const CommandCallback& callback) {
    218   SessionThreadMap::iterator iter = session_thread_map->find(session_id);
    219   if (iter == session_thread_map->end()) {
    220     Status status(return_ok_without_session ? kOk : kNoSuchSession);
    221     callback.Run(status, scoped_ptr<base::Value>(), session_id);
    222   } else {
    223     iter->second->message_loop()
    224         ->PostTask(FROM_HERE,
    225                    base::Bind(&ExecuteSessionCommandOnSessionThread,
    226                               command_name,
    227                               command,
    228                               return_ok_without_session,
    229                               base::Passed(make_scoped_ptr(params.DeepCopy())),
    230                               base::MessageLoopProxy::current(),
    231                               callback,
    232                               base::Bind(&TerminateSessionThreadOnCommandThread,
    233                                          session_thread_map,
    234                                          session_id)));
    235   }
    236 }
    237 
    238 namespace internal {
    239 
    240 void CreateSessionOnSessionThreadForTesting(const std::string& id) {
    241   SetThreadLocalSession(make_scoped_ptr(new Session(id)));
    242 }
    243 
    244 }  // namespace internal
    245