Home | History | Annotate | Download | only in chrome
      1 // Copyright (c) 2013 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/chrome/chrome_desktop_impl.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/logging.h"
      9 #include "base/posix/eintr_wrapper.h"
     10 #include "base/process/kill.h"
     11 #include "base/sys_info.h"
     12 #include "base/threading/platform_thread.h"
     13 #include "base/time/time.h"
     14 #include "chrome/test/chromedriver/chrome/automation_extension.h"
     15 #include "chrome/test/chromedriver/chrome/devtools_client.h"
     16 #include "chrome/test/chromedriver/chrome/devtools_http_client.h"
     17 #include "chrome/test/chromedriver/chrome/status.h"
     18 #include "chrome/test/chromedriver/chrome/web_view_impl.h"
     19 #include "chrome/test/chromedriver/net/port_server.h"
     20 
     21 #if defined(OS_POSIX)
     22 #include <errno.h>
     23 #include <signal.h>
     24 #include <sys/wait.h>
     25 #include <unistd.h>
     26 #endif
     27 
     28 namespace {
     29 
     30 bool KillProcess(base::ProcessHandle process_id) {
     31 #if defined(OS_POSIX)
     32   kill(process_id, SIGKILL);
     33   base::TimeTicks deadline =
     34       base::TimeTicks::Now() + base::TimeDelta::FromSeconds(30);
     35   while (base::TimeTicks::Now() < deadline) {
     36     pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG));
     37     if (pid == process_id)
     38       return true;
     39     if (pid == -1) {
     40       if (errno == ECHILD) {
     41         // The wait may fail with ECHILD if another process also waited for
     42         // the same pid, causing the process state to get cleaned up.
     43         return true;
     44       }
     45       LOG(WARNING) << "Error waiting for process " << process_id;
     46     }
     47     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
     48   }
     49   return false;
     50 #endif
     51 
     52 #if defined(OS_WIN)
     53   if (!base::KillProcess(process_id, 0, true)) {
     54     int exit_code;
     55     return base::GetTerminationStatus(process_id, &exit_code) !=
     56         base::TERMINATION_STATUS_STILL_RUNNING;
     57   }
     58   return true;
     59 #endif
     60 }
     61 
     62 }  // namespace
     63 
     64 ChromeDesktopImpl::ChromeDesktopImpl(
     65     scoped_ptr<DevToolsHttpClient> http_client,
     66     scoped_ptr<DevToolsClient> websocket_client,
     67     ScopedVector<DevToolsEventListener>& devtools_event_listeners,
     68     scoped_ptr<PortReservation> port_reservation,
     69     base::ProcessHandle process,
     70     const CommandLine& command,
     71     base::ScopedTempDir* user_data_dir,
     72     base::ScopedTempDir* extension_dir)
     73     : ChromeImpl(http_client.Pass(),
     74                  websocket_client.Pass(),
     75                  devtools_event_listeners,
     76                  port_reservation.Pass()),
     77       process_(process),
     78       command_(command) {
     79   if (user_data_dir->IsValid())
     80     CHECK(user_data_dir_.Set(user_data_dir->Take()));
     81   if (extension_dir->IsValid())
     82     CHECK(extension_dir_.Set(extension_dir->Take()));
     83 }
     84 
     85 ChromeDesktopImpl::~ChromeDesktopImpl() {
     86   if (!quit_) {
     87     base::FilePath user_data_dir = user_data_dir_.Take();
     88     base::FilePath extension_dir = extension_dir_.Take();
     89     LOG(WARNING) << "chrome detaches, user should take care of directory:"
     90                  << user_data_dir.value() << " and " << extension_dir.value();
     91   }
     92   base::CloseProcessHandle(process_);
     93 }
     94 
     95 Status ChromeDesktopImpl::WaitForPageToLoad(const std::string& url,
     96                                             const base::TimeDelta& timeout,
     97                                             scoped_ptr<WebView>* web_view) {
     98   base::TimeTicks deadline = base::TimeTicks::Now() + timeout;
     99   std::string id;
    100   while (base::TimeTicks::Now() < deadline) {
    101     WebViewsInfo views_info;
    102     Status status = devtools_http_client_->GetWebViewsInfo(&views_info);
    103     if (status.IsError())
    104       return status;
    105 
    106     for (size_t i = 0; i < views_info.GetSize(); ++i) {
    107       if (views_info.Get(i).url.find(url) == 0) {
    108         id = views_info.Get(i).id;
    109         break;
    110       }
    111     }
    112     if (!id.empty())
    113       break;
    114     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
    115   }
    116   if (id.empty())
    117     return Status(kUnknownError, "page could not be found: " + url);
    118 
    119   scoped_ptr<WebView> web_view_tmp(
    120       new WebViewImpl(id,
    121                       devtools_http_client_->browser_info(),
    122                       devtools_http_client_->CreateClient(id),
    123                       devtools_http_client_->device_metrics()));
    124   Status status = web_view_tmp->ConnectIfNecessary();
    125   if (status.IsError())
    126     return status;
    127 
    128   status = web_view_tmp->WaitForPendingNavigations(
    129       std::string(), deadline - base::TimeTicks::Now(), false);
    130   if (status.IsOk())
    131     *web_view = web_view_tmp.Pass();
    132   return status;
    133 }
    134 
    135 Status ChromeDesktopImpl::GetAutomationExtension(
    136     AutomationExtension** extension) {
    137   if (!automation_extension_) {
    138     scoped_ptr<WebView> web_view;
    139     Status status = WaitForPageToLoad(
    140         "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/"
    141         "_generated_background_page.html",
    142         base::TimeDelta::FromSeconds(10),
    143         &web_view);
    144     if (status.IsError())
    145       return Status(kUnknownError, "cannot get automation extension", status);
    146 
    147     automation_extension_.reset(new AutomationExtension(web_view.Pass()));
    148   }
    149   *extension = automation_extension_.get();
    150   return Status(kOk);
    151 }
    152 
    153 ChromeDesktopImpl* ChromeDesktopImpl::GetAsDesktop() {
    154   return this;
    155 }
    156 
    157 std::string ChromeDesktopImpl::GetOperatingSystemName() {
    158   return base::SysInfo::OperatingSystemName();
    159 }
    160 
    161 bool ChromeDesktopImpl::IsMobileEmulationEnabled() const {
    162   return devtools_http_client_->device_metrics() != NULL;
    163 }
    164 
    165 Status ChromeDesktopImpl::QuitImpl() {
    166   if (!KillProcess(process_))
    167     return Status(kUnknownError, "cannot kill Chrome");
    168   return Status(kOk);
    169 }
    170 
    171 const CommandLine& ChromeDesktopImpl::command() const {
    172   return command_;
    173 }
    174