12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h" 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h" 8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/logging.h" 990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/posix/eintr_wrapper.h" 10bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/process/kill.h" 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/sys_info.h" 12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/threading/platform_thread.h" 13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/test/chromedriver/chrome/automation_extension.h" 15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/test/chromedriver/chrome/devtools_client.h" 16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/test/chromedriver/chrome/devtools_http_client.h" 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/test/chromedriver/chrome/status.h" 18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/test/chromedriver/chrome/web_view_impl.h" 1968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "chrome/test/chromedriver/net/port_server.h" 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#if defined(OS_POSIX) 2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <errno.h> 2390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <signal.h> 2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <sys/wait.h> 2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <unistd.h> 2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#endif 2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)namespace { 2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 3090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)bool KillProcess(base::ProcessHandle process_id) { 3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#if defined(OS_POSIX) 3290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) kill(process_id, SIGKILL); 3358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) base::TimeTicks deadline = 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::TimeTicks::Now() + base::TimeDelta::FromSeconds(30); 3558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) while (base::TimeTicks::Now() < deadline) { 3690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG)); 3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (pid == process_id) 3890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return true; 3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (pid == -1) { 4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (errno == ECHILD) { 4190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // The wait may fail with ECHILD if another process also waited for 4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // the same pid, causing the process state to get cleaned up. 4390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return true; 4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) LOG(WARNING) << "Error waiting for process " << process_id; 4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); 4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return false; 5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#endif 5190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 5290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#if defined(OS_WIN) 5390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!base::KillProcess(process_id, 0, true)) { 5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) int exit_code; 5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return base::GetTerminationStatus(process_id, &exit_code) != 5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::TERMINATION_STATUS_STILL_RUNNING; 5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return true; 5990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#endif 6090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 6190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 6290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} // namespace 6390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeDesktopImpl::ChromeDesktopImpl( 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scoped_ptr<DevToolsHttpClient> http_client, 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scoped_ptr<DevToolsClient> websocket_client, 67b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ScopedVector<DevToolsEventListener>& devtools_event_listeners, 6868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) scoped_ptr<PortReservation> port_reservation, 69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::ProcessHandle process, 701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const CommandLine& command, 71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::ScopedTempDir* user_data_dir, 72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::ScopedTempDir* extension_dir) 735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) : ChromeImpl(http_client.Pass(), 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) websocket_client.Pass(), 7568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) devtools_event_listeners, 7668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) port_reservation.Pass()), 771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) process_(process), 781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) command_(command) { 79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (user_data_dir->IsValid()) 80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK(user_data_dir_.Set(user_data_dir->Take())); 81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (extension_dir->IsValid()) 82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK(extension_dir_.Set(extension_dir->Take())); 83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeDesktopImpl::~ChromeDesktopImpl() { 8690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!quit_) { 8790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::FilePath user_data_dir = user_data_dir_.Take(); 8890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::FilePath extension_dir = extension_dir_.Take(); 8990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) LOG(WARNING) << "chrome detaches, user should take care of directory:" 9090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) << user_data_dir.value() << " and " << extension_dir.value(); 9190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::CloseProcessHandle(process_); 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 95424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)Status ChromeDesktopImpl::WaitForPageToLoad(const std::string& url, 96424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const base::TimeDelta& timeout, 97424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) scoped_ptr<WebView>* web_view) { 9858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) base::TimeTicks deadline = base::TimeTicks::Now() + timeout; 99424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) std::string id; 10058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) while (base::TimeTicks::Now() < deadline) { 101424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) WebViewsInfo views_info; 102424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) Status status = devtools_http_client_->GetWebViewsInfo(&views_info); 103424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (status.IsError()) 104424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return status; 105424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 106424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) for (size_t i = 0; i < views_info.GetSize(); ++i) { 107424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (views_info.Get(i).url.find(url) == 0) { 108424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) id = views_info.Get(i).id; 109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) break; 110424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 112424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (!id.empty()) 113424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) break; 114424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); 115424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 116424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (id.empty()) 117424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return Status(kUnknownError, "page could not be found: " + url); 118424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 119c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch scoped_ptr<WebView> web_view_tmp( 120c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch new WebViewImpl(id, 1210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch devtools_http_client_->browser_info(), 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) devtools_http_client_->CreateClient(id), 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) devtools_http_client_->device_metrics())); 124424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) Status status = web_view_tmp->ConnectIfNecessary(); 125424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (status.IsError()) 126424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return status; 127424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 128424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) status = web_view_tmp->WaitForPendingNavigations( 12958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) std::string(), deadline - base::TimeTicks::Now(), false); 130424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (status.IsOk()) 131424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) *web_view = web_view_tmp.Pass(); 132424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return status; 133424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 135424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)Status ChromeDesktopImpl::GetAutomationExtension( 136424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) AutomationExtension** extension) { 137424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (!automation_extension_) { 138424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) scoped_ptr<WebView> web_view; 139424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) Status status = WaitForPageToLoad( 140424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/" 141424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) "_generated_background_page.html", 142424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) base::TimeDelta::FromSeconds(10), 143424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) &web_view); 1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (status.IsError()) 145424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return Status(kUnknownError, "cannot get automation extension", status); 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) automation_extension_.reset(new AutomationExtension(web_view.Pass())); 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) *extension = automation_extension_.get(); 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return Status(kOk); 1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)ChromeDesktopImpl* ChromeDesktopImpl::GetAsDesktop() { 1541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return this; 1551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 1561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::string ChromeDesktopImpl::GetOperatingSystemName() { 1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::SysInfo::OperatingSystemName(); 1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool ChromeDesktopImpl::IsMobileEmulationEnabled() const { 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return devtools_http_client_->device_metrics() != NULL; 163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 16568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)Status ChromeDesktopImpl::QuitImpl() { 16690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!KillProcess(process_)) 16790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return Status(kUnknownError, "cannot kill Chrome"); 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return Status(kOk); 1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const CommandLine& ChromeDesktopImpl::command() const { 1721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return command_; 1731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 174