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