1f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org// Use of this source code is governed by a BSD-style license that can be
3f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org// found in the LICENSE file.
4f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
5f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "chrome/test/chromedriver/chrome/devtools_http_client.h"
6f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
7f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "base/bind.h"
8f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "base/bind_helpers.h"
9f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "base/json/json_reader.h"
10f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "base/strings/stringprintf.h"
11f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "base/threading/platform_thread.h"
12f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "base/time/time.h"
13f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "base/values.h"
14f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "chrome/test/chromedriver/chrome/device_metrics.h"
15f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "chrome/test/chromedriver/chrome/devtools_client_impl.h"
16f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "chrome/test/chromedriver/chrome/log.h"
17f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "chrome/test/chromedriver/chrome/status.h"
18f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "chrome/test/chromedriver/chrome/web_view_impl.h"
19f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "chrome/test/chromedriver/net/net_util.h"
20f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org#include "chrome/test/chromedriver/net/url_request_context_getter.h"
21f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
22f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgWebViewInfo::WebViewInfo(const std::string& id,
23f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org                         const std::string& debugger_url,
24f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org                         const std::string& url,
25f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org                         Type type)
26f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    : id(id), debugger_url(debugger_url), url(url), type(type) {}
27f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
28f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgWebViewInfo::~WebViewInfo() {}
29f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
30f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgbool WebViewInfo::IsFrontend() const {
31f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return url.find("chrome-devtools://") == 0u;
32f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
33f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
34f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgWebViewsInfo::WebViewsInfo() {}
35f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
36f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgWebViewsInfo::WebViewsInfo(const std::vector<WebViewInfo>& info)
37f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    : views_info(info) {}
38f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
39f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgWebViewsInfo::~WebViewsInfo() {}
40f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
41f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgconst WebViewInfo& WebViewsInfo::Get(int index) const {
42f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return views_info[index];
43f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
44f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
45f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgsize_t WebViewsInfo::GetSize() const {
46f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return views_info.size();
47f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
48f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
49f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgconst WebViewInfo* WebViewsInfo::GetForId(const std::string& id) const {
50f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  for (size_t i = 0; i < views_info.size(); ++i) {
51f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (views_info[i].id == id)
52f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return &views_info[i];
53f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
54f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return NULL;
55f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
56f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
57f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgDevToolsHttpClient::DevToolsHttpClient(
58f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    const NetAddress& address,
59f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    scoped_refptr<URLRequestContextGetter> context_getter,
60f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    const SyncWebSocketFactory& socket_factory,
61f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    scoped_ptr<DeviceMetrics> device_metrics)
62f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    : context_getter_(context_getter),
63f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      socket_factory_(socket_factory),
64f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      server_url_("http://" + address.ToString()),
65f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      web_socket_url_prefix_(base::StringPrintf(
66f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org          "ws://%s/devtools/page/", address.ToString().c_str())),
67f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      device_metrics_(device_metrics.Pass()) {}
68f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
69f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgDevToolsHttpClient::~DevToolsHttpClient() {}
70f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
71f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgStatus DevToolsHttpClient::Init(const base::TimeDelta& timeout) {
72f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  base::TimeTicks deadline = base::TimeTicks::Now() + timeout;
73f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  std::string version_url = server_url_ + "/json/version";
74f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  std::string data;
75f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
76f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  while (!FetchUrlAndLog(version_url, context_getter_.get(), &data)
77f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      || data.empty()) {
78f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (base::TimeTicks::Now() > deadline)
79f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kChromeNotReachable);
80f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
81f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
82f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
83f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return ParseBrowserInfo(data, &browser_info_);
84f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
85f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
86f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgStatus DevToolsHttpClient::GetWebViewsInfo(WebViewsInfo* views_info) {
87f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  std::string data;
88f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  if (!FetchUrlAndLog(server_url_ + "/json", context_getter_.get(), &data))
89f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    return Status(kChromeNotReachable);
90f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
91f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return internal::ParseWebViewsInfo(data, views_info);
92f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
93f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
94f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgscoped_ptr<DevToolsClient> DevToolsHttpClient::CreateClient(
95f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    const std::string& id) {
96f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return scoped_ptr<DevToolsClient>(new DevToolsClientImpl(
97f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      socket_factory_,
98f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      web_socket_url_prefix_ + id,
99f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      id,
100f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      base::Bind(
101f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org          &DevToolsHttpClient::CloseFrontends, base::Unretained(this), id)));
102f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
103f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
104f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgStatus DevToolsHttpClient::CloseWebView(const std::string& id) {
105f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  std::string data;
106f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  if (!FetchUrlAndLog(
107f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org          server_url_ + "/json/close/" + id, context_getter_.get(), &data)) {
108f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    return Status(kOk);  // Closing the last web view leads chrome to quit.
109f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
110f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
111f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // Wait for the target window to be completely closed.
112f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  base::TimeTicks deadline =
113f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      base::TimeTicks::Now() + base::TimeDelta::FromSeconds(20);
114f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  while (base::TimeTicks::Now() < deadline) {
115f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    WebViewsInfo views_info;
116f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    Status status = GetWebViewsInfo(&views_info);
117f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (status.code() == kChromeNotReachable)
118f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kOk);
119f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (status.IsError())
120f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return status;
121f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (!views_info.GetForId(id))
122f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kOk);
123f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
124f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
125f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return Status(kUnknownError, "failed to close window in 20 seconds");
126f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
127f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
128f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgStatus DevToolsHttpClient::ActivateWebView(const std::string& id) {
129f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  std::string data;
130f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  if (!FetchUrlAndLog(
131f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org          server_url_ + "/json/activate/" + id, context_getter_.get(), &data))
132f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    return Status(kUnknownError, "cannot activate web view");
133f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return Status(kOk);
134f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
135f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
136f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgconst BrowserInfo* DevToolsHttpClient::browser_info() {
137f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return &browser_info_;
138f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
139f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
140f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgconst DeviceMetrics* DevToolsHttpClient::device_metrics() {
141f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return device_metrics_.get();
142f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
143f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
144f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgStatus DevToolsHttpClient::CloseFrontends(const std::string& for_client_id) {
145f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  WebViewsInfo views_info;
146f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  Status status = GetWebViewsInfo(&views_info);
147f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  if (status.IsError())
148f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    return status;
149f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
150f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // Close frontends. Usually frontends are docked in the same page, although
151f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // some may be in tabs (undocked, chrome://inspect, the DevTools
152f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // discovery page, etc.). Tabs can be closed via the DevTools HTTP close
153f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // URL, but docked frontends can only be closed, by design, by connecting
154f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // to them and clicking the close button. Close the tab frontends first
155f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // in case one of them is debugging a docked frontend, which would prevent
156f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // the code from being able to connect to the docked one.
157f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  std::list<std::string> tab_frontend_ids;
158f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  std::list<std::string> docked_frontend_ids;
159f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  for (size_t i = 0; i < views_info.GetSize(); ++i) {
160f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    const WebViewInfo& view_info = views_info.Get(i);
161f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (view_info.IsFrontend()) {
162f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      if (view_info.type == WebViewInfo::kPage)
163f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        tab_frontend_ids.push_back(view_info.id);
164f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      else if (view_info.type == WebViewInfo::kOther)
165f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        docked_frontend_ids.push_back(view_info.id);
166f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      else
167f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        return Status(kUnknownError, "unknown type of DevTools frontend");
168f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    }
169f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
170f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
171f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  for (std::list<std::string>::const_iterator it = tab_frontend_ids.begin();
172f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org       it != tab_frontend_ids.end(); ++it) {
173f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    status = CloseWebView(*it);
174f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (status.IsError())
175f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return status;
176f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
177f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
178f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  for (std::list<std::string>::const_iterator it = docked_frontend_ids.begin();
179f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org       it != docked_frontend_ids.end(); ++it) {
180f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    scoped_ptr<DevToolsClient> client(new DevToolsClientImpl(
181f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        socket_factory_,
182f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        web_socket_url_prefix_ + *it,
183f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        *it));
184f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    scoped_ptr<WebViewImpl> web_view(
185f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        new WebViewImpl(*it, &browser_info_, client.Pass(), NULL));
186f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
187f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    status = web_view->ConnectIfNecessary();
188f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    // Ignore disconnected error, because the debugger might have closed when
189f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    // its container page was closed above.
190f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (status.IsError() && status.code() != kDisconnected)
191f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return status;
192f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
193f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    scoped_ptr<base::Value> result;
194f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    status = web_view->EvaluateScript(
195f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        std::string(),
196f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        "document.querySelector('*[id^=\"close-button-\"]').click();",
197f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org        &result);
198f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    // Ignore disconnected error, because it may be closed already.
199f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (status.IsError() && status.code() != kDisconnected)
200f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return status;
201f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
202f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
203f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  // Wait until DevTools UI disconnects from the given web view.
204f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  base::TimeTicks deadline =
205f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      base::TimeTicks::Now() + base::TimeDelta::FromSeconds(20);
206f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  while (base::TimeTicks::Now() < deadline) {
207f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    status = GetWebViewsInfo(&views_info);
208f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (status.IsError())
209f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return status;
210f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
211f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    const WebViewInfo* view_info = views_info.GetForId(for_client_id);
212f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (!view_info)
213f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kNoSuchWindow, "window was already closed");
214f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (view_info->debugger_url.size())
215f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kOk);
216f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
217f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
218f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
219f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return Status(kUnknownError, "failed to close UI debuggers");
220f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
221f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
222f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgbool DevToolsHttpClient::FetchUrlAndLog(const std::string& url,
223f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org                                        URLRequestContextGetter* getter,
224f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org                                        std::string* response) {
225f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  VLOG(1) << "DevTools request: " << url;
226f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  bool ok = FetchUrl(url, getter, response);
227f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  if (ok) {
228f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    VLOG(1) << "DevTools response: " << *response;
229f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  } else {
230f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    VLOG(1) << "DevTools request failed";
231f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  }
232f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  return ok;
233f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org}
234f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
235f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgnamespace internal {
236f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
237f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgStatus ParseWebViewsInfo(const std::string& data,
238f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org                         WebViewsInfo* views_info) {
239f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  scoped_ptr<base::Value> value(base::JSONReader::Read(data));
240f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  if (!value.get())
241f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    return Status(kUnknownError, "DevTools returned invalid JSON");
242f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  base::ListValue* list;
243f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  if (!value->GetAsList(&list))
244f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    return Status(kUnknownError, "DevTools did not return list");
245f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org
246f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  std::vector<WebViewInfo> temp_views_info;
247f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org  for (size_t i = 0; i < list->GetSize(); ++i) {
248f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    base::DictionaryValue* info;
249f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (!list->GetDictionary(i, &info))
250f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kUnknownError, "DevTools contains non-dictionary item");
251f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    std::string id;
252f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (!info->GetString("id", &id))
253f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kUnknownError, "DevTools did not include id");
254f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    std::string type_as_string;
255f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (!info->GetString("type", &type_as_string))
256f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kUnknownError, "DevTools did not include type");
257f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    std::string url;
258f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org    if (!info->GetString("url", &url))
259f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org      return Status(kUnknownError, "DevTools did not include url");
260    std::string debugger_url;
261    info->GetString("webSocketDebuggerUrl", &debugger_url);
262    WebViewInfo::Type type;
263    if (type_as_string == "app")
264      type = WebViewInfo::kApp;
265    else if (type_as_string == "background_page")
266      type = WebViewInfo::kBackgroundPage;
267    else if (type_as_string == "page")
268      type = WebViewInfo::kPage;
269    else if (type_as_string == "worker")
270      type = WebViewInfo::kWorker;
271    else if (type_as_string == "other")
272      type = WebViewInfo::kOther;
273    else
274      return Status(kUnknownError,
275                    "DevTools returned unknown type:" + type_as_string);
276    temp_views_info.push_back(WebViewInfo(id, debugger_url, url, type));
277  }
278  *views_info = WebViewsInfo(temp_views_info);
279  return Status(kOk);
280}
281
282}  // namespace internal
283