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.
5 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
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"
30 bool KillProcess(base::ProcessHandle process_id) {
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)
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.
45 LOG(WARNING) << "Error waiting for process " << process_id;
47 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
53 if (!base::KillProcess(process_id, 0, true)) {
55 return base::GetTerminationStatus(process_id, &exit_code) !=
56 base::TERMINATION_STATUS_STILL_RUNNING;
64 ChromeDesktopImpl::ChromeDesktopImpl(
65 scoped_ptr<DevToolsHttpClient> client,
66 ScopedVector<DevToolsEventListener>& devtools_event_listeners,
67 scoped_ptr<PortReservation> port_reservation,
68 base::ProcessHandle process,
69 const CommandLine& command,
70 base::ScopedTempDir* user_data_dir,
71 base::ScopedTempDir* extension_dir)
72 : ChromeImpl(client.Pass(),
73 devtools_event_listeners,
74 port_reservation.Pass()),
77 if (user_data_dir->IsValid())
78 CHECK(user_data_dir_.Set(user_data_dir->Take()));
79 if (extension_dir->IsValid())
80 CHECK(extension_dir_.Set(extension_dir->Take()));
83 ChromeDesktopImpl::~ChromeDesktopImpl() {
85 base::FilePath user_data_dir = user_data_dir_.Take();
86 base::FilePath extension_dir = extension_dir_.Take();
87 LOG(WARNING) << "chrome detaches, user should take care of directory:"
88 << user_data_dir.value() << " and " << extension_dir.value();
90 base::CloseProcessHandle(process_);
93 Status ChromeDesktopImpl::WaitForPageToLoad(const std::string& url,
94 const base::TimeDelta& timeout,
95 scoped_ptr<WebView>* web_view) {
96 base::TimeTicks deadline = base::TimeTicks::Now() + timeout;
98 while (base::TimeTicks::Now() < deadline) {
99 WebViewsInfo views_info;
100 Status status = devtools_http_client_->GetWebViewsInfo(&views_info);
101 if (status.IsError())
104 for (size_t i = 0; i < views_info.GetSize(); ++i) {
105 if (views_info.Get(i).url.find(url) == 0) {
106 id = views_info.Get(i).id;
112 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
115 return Status(kUnknownError, "page could not be found: " + url);
117 scoped_ptr<WebView> web_view_tmp(new WebViewImpl(
118 id, GetBuildNo(), devtools_http_client_->CreateClient(id)));
119 Status status = web_view_tmp->ConnectIfNecessary();
120 if (status.IsError())
123 status = web_view_tmp->WaitForPendingNavigations(
124 std::string(), deadline - base::TimeTicks::Now(), false);
126 *web_view = web_view_tmp.Pass();
130 Status ChromeDesktopImpl::GetAutomationExtension(
131 AutomationExtension** extension) {
132 if (!automation_extension_) {
133 scoped_ptr<WebView> web_view;
134 Status status = WaitForPageToLoad(
135 "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/"
136 "_generated_background_page.html",
137 base::TimeDelta::FromSeconds(10),
139 if (status.IsError())
140 return Status(kUnknownError, "cannot get automation extension", status);
142 automation_extension_.reset(new AutomationExtension(web_view.Pass()));
144 *extension = automation_extension_.get();
148 ChromeDesktopImpl* ChromeDesktopImpl::GetAsDesktop() {
152 std::string ChromeDesktopImpl::GetOperatingSystemName() {
153 return base::SysInfo::OperatingSystemName();
156 Status ChromeDesktopImpl::QuitImpl() {
157 if (!KillProcess(process_))
158 return Status(kUnknownError, "cannot kill Chrome");
162 const CommandLine& ChromeDesktopImpl::command() const {