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