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 
     20 #if defined(OS_POSIX)
     21 #include <errno.h>
     22 #include <signal.h>
     23 #include <sys/wait.h>
     24 #include <unistd.h>
     25 #endif
     26 
     27 namespace {
     28 
     29 bool KillProcess(base::ProcessHandle process_id) {
     30 #if defined(OS_POSIX)
     31   kill(process_id, SIGKILL);
     32   base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(5);
     33   while (base::Time::Now() < deadline) {
     34     pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG));
     35     if (pid == process_id)
     36       return true;
     37     if (pid == -1) {
     38       if (errno == ECHILD) {
     39         // The wait may fail with ECHILD if another process also waited for
     40         // the same pid, causing the process state to get cleaned up.
     41         return true;
     42       }
     43       LOG(WARNING) << "Error waiting for process " << process_id;
     44     }
     45     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
     46   }
     47   return false;
     48 #endif
     49 
     50 #if defined(OS_WIN)
     51   if (!base::KillProcess(process_id, 0, true)) {
     52     int exit_code;
     53     return base::GetTerminationStatus(process_id, &exit_code) !=
     54         base::TERMINATION_STATUS_STILL_RUNNING;
     55   }
     56   return true;
     57 #endif
     58 }
     59 
     60 }  // namespace
     61 
     62 ChromeDesktopImpl::ChromeDesktopImpl(
     63     scoped_ptr<DevToolsHttpClient> client,
     64     ScopedVector<DevToolsEventListener>& devtools_event_listeners,
     65     Log* log,
     66     base::ProcessHandle process,
     67     base::ScopedTempDir* user_data_dir,
     68     base::ScopedTempDir* extension_dir)
     69     : ChromeImpl(client.Pass(),
     70                  devtools_event_listeners,
     71                  log),
     72       process_(process),
     73       quit_(false) {
     74   if (user_data_dir->IsValid())
     75     CHECK(user_data_dir_.Set(user_data_dir->Take()));
     76   if (extension_dir->IsValid())
     77     CHECK(extension_dir_.Set(extension_dir->Take()));
     78 }
     79 
     80 ChromeDesktopImpl::~ChromeDesktopImpl() {
     81   if (!quit_) {
     82     base::FilePath user_data_dir = user_data_dir_.Take();
     83     base::FilePath extension_dir = extension_dir_.Take();
     84     LOG(WARNING) << "chrome detaches, user should take care of directory:"
     85                  << user_data_dir.value() << " and " << extension_dir.value();
     86   }
     87   base::CloseProcessHandle(process_);
     88 }
     89 
     90 Status ChromeDesktopImpl::GetAutomationExtension(
     91     AutomationExtension** extension) {
     92   if (!automation_extension_) {
     93     base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(10);
     94     std::string id;
     95     while (base::Time::Now() < deadline) {
     96       WebViewsInfo views_info;
     97       Status status = devtools_http_client_->GetWebViewsInfo(&views_info);
     98       if (status.IsError())
     99         return status;
    100 
    101       for (size_t i = 0; i < views_info.GetSize(); ++i) {
    102         if (views_info.Get(i).url.find(
    103                 "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb") == 0) {
    104           id = views_info.Get(i).id;
    105           break;
    106         }
    107       }
    108       if (!id.empty())
    109         break;
    110       base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
    111     }
    112     if (id.empty())
    113       return Status(kUnknownError, "automation extension cannot be found");
    114 
    115     scoped_ptr<WebView> web_view(new WebViewImpl(
    116         id, GetBuildNo(), devtools_http_client_->CreateClient(id), log_));
    117     Status status = web_view->ConnectIfNecessary();
    118     if (status.IsError())
    119       return status;
    120 
    121     // Wait for the extension background page to load.
    122     status = web_view->WaitForPendingNavigations(
    123         std::string(), 5 * 60 * 1000);
    124     if (status.IsError())
    125       return status;
    126 
    127     automation_extension_.reset(new AutomationExtension(web_view.Pass()));
    128   }
    129   *extension = automation_extension_.get();
    130   return Status(kOk);
    131 }
    132 
    133 std::string ChromeDesktopImpl::GetOperatingSystemName() {
    134   return base::SysInfo::OperatingSystemName();
    135 }
    136 
    137 Status ChromeDesktopImpl::Quit() {
    138   quit_ = true;
    139   if (!KillProcess(process_))
    140     return Status(kUnknownError, "cannot kill Chrome");
    141   return Status(kOk);
    142 }
    143