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