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
KillProcess(base::ProcessHandle process_id)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
ChromeDesktopImpl(scoped_ptr<DevToolsHttpClient> client,ScopedVector<DevToolsEventListener> & devtools_event_listeners,scoped_ptr<PortReservation> port_reservation,base::ProcessHandle process,const CommandLine & command,base::ScopedTempDir * user_data_dir,base::ScopedTempDir * extension_dir)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()),
75 process_(process),
76 command_(command) {
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()));
81 }
82
~ChromeDesktopImpl()83 ChromeDesktopImpl::~ChromeDesktopImpl() {
84 if (!quit_) {
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();
89 }
90 base::CloseProcessHandle(process_);
91 }
92
WaitForPageToLoad(const std::string & url,const base::TimeDelta & timeout,scoped_ptr<WebView> * web_view)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;
97 std::string id;
98 while (base::TimeTicks::Now() < deadline) {
99 WebViewsInfo views_info;
100 Status status = devtools_http_client_->GetWebViewsInfo(&views_info);
101 if (status.IsError())
102 return status;
103
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;
107 break;
108 }
109 }
110 if (!id.empty())
111 break;
112 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
113 }
114 if (id.empty())
115 return Status(kUnknownError, "page could not be found: " + url);
116
117 scoped_ptr<WebView> web_view_tmp(
118 new WebViewImpl(id,
119 devtools_http_client_->browser_info(),
120 devtools_http_client_->CreateClient(id),
121 devtools_http_client_->device_metrics()));
122 Status status = web_view_tmp->ConnectIfNecessary();
123 if (status.IsError())
124 return status;
125
126 status = web_view_tmp->WaitForPendingNavigations(
127 std::string(), deadline - base::TimeTicks::Now(), false);
128 if (status.IsOk())
129 *web_view = web_view_tmp.Pass();
130 return status;
131 }
132
GetAutomationExtension(AutomationExtension ** extension)133 Status ChromeDesktopImpl::GetAutomationExtension(
134 AutomationExtension** extension) {
135 if (!automation_extension_) {
136 scoped_ptr<WebView> web_view;
137 Status status = WaitForPageToLoad(
138 "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/"
139 "_generated_background_page.html",
140 base::TimeDelta::FromSeconds(10),
141 &web_view);
142 if (status.IsError())
143 return Status(kUnknownError, "cannot get automation extension", status);
144
145 automation_extension_.reset(new AutomationExtension(web_view.Pass()));
146 }
147 *extension = automation_extension_.get();
148 return Status(kOk);
149 }
150
GetAsDesktop()151 ChromeDesktopImpl* ChromeDesktopImpl::GetAsDesktop() {
152 return this;
153 }
154
GetOperatingSystemName()155 std::string ChromeDesktopImpl::GetOperatingSystemName() {
156 return base::SysInfo::OperatingSystemName();
157 }
158
IsMobileEmulationEnabled() const159 bool ChromeDesktopImpl::IsMobileEmulationEnabled() const {
160 return devtools_http_client_->device_metrics() != NULL;
161 }
162
QuitImpl()163 Status ChromeDesktopImpl::QuitImpl() {
164 if (!KillProcess(process_))
165 return Status(kUnknownError, "cannot kill Chrome");
166 return Status(kOk);
167 }
168
command() const169 const CommandLine& ChromeDesktopImpl::command() const {
170 return command_;
171 }
172