Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(821)

Side by Side Diff: chrome/test/chromedriver/chrome_impl.cc

Issue 12321057: [chromedriver] Implement reconnection to DevTools. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix nits. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/test/chromedriver/chrome_impl.h" 5 #include "chrome/test/chromedriver/chrome_impl.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <list> 8 #include <list>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/json/json_reader.h" 11 #include "base/json/json_reader.h"
12 #include "base/string_split.h" 12 #include "base/string_split.h"
13 #include "base/stringprintf.h" 13 #include "base/stringprintf.h"
14 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
15 #include "base/threading/platform_thread.h" 15 #include "base/threading/platform_thread.h"
16 #include "base/time.h" 16 #include "base/time.h"
17 #include "base/values.h" 17 #include "base/values.h"
18 #include "chrome/test/chromedriver/devtools_client_impl.h" 18 #include "chrome/test/chromedriver/devtools_client_impl.h"
19 #include "chrome/test/chromedriver/javascript_dialog_manager.h" 19 #include "chrome/test/chromedriver/javascript_dialog_manager.h"
20 #include "chrome/test/chromedriver/net/net_util.h" 20 #include "chrome/test/chromedriver/net/net_util.h"
21 #include "chrome/test/chromedriver/net/sync_websocket_impl.h" 21 #include "chrome/test/chromedriver/net/sync_websocket_impl.h"
22 #include "chrome/test/chromedriver/net/url_request_context_getter.h" 22 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
23 #include "chrome/test/chromedriver/status.h" 23 #include "chrome/test/chromedriver/status.h"
24 #include "chrome/test/chromedriver/version.h" 24 #include "chrome/test/chromedriver/version.h"
25 #include "chrome/test/chromedriver/web_view_impl.h" 25 #include "chrome/test/chromedriver/web_view_impl.h"
26 #include "googleurl/src/gurl.h" 26 #include "googleurl/src/gurl.h"
27 27
28 namespace { 28 namespace {
29 29
30 typedef std::list<internal::WebViewInfo> WebViewInfoList;
31
30 Status FetchVersionInfo(URLRequestContextGetter* context_getter, 32 Status FetchVersionInfo(URLRequestContextGetter* context_getter,
31 int port, 33 int port,
32 std::string* version) { 34 std::string* version) {
33 std::string url = base::StringPrintf( 35 std::string url = base::StringPrintf(
34 "http://127.0.0.1:%d/json/version", port); 36 "http://127.0.0.1:%d/json/version", port);
35 std::string data; 37 std::string data;
36 if (!FetchUrl(GURL(url), context_getter, &data)) 38 if (!FetchUrl(GURL(url), context_getter, &data))
37 return Status(kChromeNotReachable); 39 return Status(kChromeNotReachable);
38 return internal::ParseVersionInfo(data, version); 40 return internal::ParseVersionInfo(data, version);
39 } 41 }
40 42
41 Status FetchPagesInfo(URLRequestContextGetter* context_getter, 43 Status FetchWebViewsInfo(URLRequestContextGetter* context_getter,
42 int port, 44 int port,
43 std::list<std::string>* page_ids) { 45 WebViewInfoList* info_list) {
44 std::string url = base::StringPrintf("http://127.0.0.1:%d/json", port); 46 std::string url = base::StringPrintf("http://127.0.0.1:%d/json", port);
45 std::string data; 47 std::string data;
46 if (!FetchUrl(GURL(url), context_getter, &data)) 48 if (!FetchUrl(GURL(url), context_getter, &data))
47 return Status(kChromeNotReachable); 49 return Status(kChromeNotReachable);
48 return internal::ParsePagesInfo(data, page_ids); 50
51 return internal::ParsePagesInfo(data, info_list);
52 }
53
54 const internal::WebViewInfo* GetWebViewFromList(
55 const std::string& id,
56 const WebViewInfoList& info_list) {
57 for (WebViewInfoList::const_iterator it = info_list.begin();
58 it != info_list.end(); ++it) {
59 if (it->id == id)
60 return &(*it);
61 }
62 return NULL;
49 } 63 }
50 64
51 Status CloseWebView(URLRequestContextGetter* context_getter, 65 Status CloseWebView(URLRequestContextGetter* context_getter,
52 int port, 66 int port,
53 const std::string& web_view_id) { 67 const std::string& web_view_id) {
54 std::list<std::string> ids; 68 WebViewInfoList info_list;
55 Status status = FetchPagesInfo(context_getter, port, &ids); 69 Status status = FetchWebViewsInfo(context_getter, port, &info_list);
56 if (status.IsError()) 70 if (status.IsError())
57 return status; 71 return status;
58 if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end()) 72 if (!GetWebViewFromList(web_view_id, info_list))
59 return Status(kOk); 73 return Status(kOk);
60 74
61 bool is_last_web_view = ids.size() == 1; 75 bool is_last_web_view = info_list.size() == 1u;
62 76
63 std::string url = base::StringPrintf( 77 std::string url = base::StringPrintf(
64 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str()); 78 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str());
65 std::string data; 79 std::string data;
66 if (!FetchUrl(GURL(url), context_getter, &data)) 80 if (!FetchUrl(GURL(url), context_getter, &data))
67 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable); 81 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable);
68 if (data != "Target is closing") 82 if (data != "Target is closing")
69 return Status(kOk); 83 return Status(kOk);
70 84
71 // Wait for the target window to be completely closed. 85 // Wait for the target window to be completely closed.
72 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); 86 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
73 while (base::Time::Now() < deadline) { 87 while (base::Time::Now() < deadline) {
74 ids.clear(); 88 info_list.clear();
75 status = FetchPagesInfo(context_getter, port, &ids); 89 status = FetchWebViewsInfo(context_getter, port, &info_list);
76 if (is_last_web_view && status.code() == kChromeNotReachable) 90 if (is_last_web_view && status.code() == kChromeNotReachable)
77 return Status(kOk); // Closing the last web view leads chrome to quit. 91 return Status(kOk); // Closing the last web view leads chrome to quit.
78 if (status.IsError()) 92 if (status.IsError())
79 return status; 93 return status;
80 if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end()) 94 if (!GetWebViewFromList(web_view_id, info_list))
81 return Status(kOk); 95 return Status(kOk);
82 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); 96 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
83 } 97 }
84 98
85 return Status(kUnknownError, "failed to close window in 20 seconds"); 99 return Status(kUnknownError, "failed to close window in 20 seconds");
86 } 100 }
87 101
102 Status FakeCloseWebView() {
103 // This is for the docked DevTools frontend only.
104 return Status(kUnknownError,
105 "docked DevTools frontend should be closed by Javascript");
106 }
107
108 Status FakeCloseDevToolsFrontend() {
109 // This is for the docked DevTools frontend only.
110 return Status(kOk);
111 }
112
113 Status CloseDevToolsFrontend(ChromeImpl* chrome,
114 const SyncWebSocketFactory& socket_factory,
115 URLRequestContextGetter* context_getter,
116 int port,
117 const std::string& web_view_id) {
118 WebViewInfoList info_list;
119 Status status = FetchWebViewsInfo(context_getter, port, &info_list);
120 if (status.IsError())
121 return status;
122
123 std::list<std::string> tab_frontend_ids;
124 std::list<std::string> docked_frontend_ids;
125 // Filter out DevTools frontend.
126 for (WebViewInfoList::const_iterator it = info_list.begin();
127 it != info_list.end(); ++it) {
128 if (it->IsFrontend()) {
129 if (it->type == internal::WebViewInfo::kPage)
130 tab_frontend_ids.push_back(it->id);
131 else if (it->type == internal::WebViewInfo::kOther)
132 docked_frontend_ids.push_back(it->id);
133 else
134 return Status(kUnknownError, "unknown type of DevTools frontend");
135 }
136 }
137
138 // Close tab DevTools frontend as if closing a normal web view.
139 for (std::list<std::string>::const_iterator it = tab_frontend_ids.begin();
140 it != tab_frontend_ids.end(); ++it) {
141 status = CloseWebView(context_getter, port, *it);
142 if (status.IsError())
143 return status;
144 }
145
146 // Close docked DevTools frontend by Javascript.
147 for (std::list<std::string>::const_iterator it = docked_frontend_ids.begin();
148 it != docked_frontend_ids.end(); ++it) {
149 std::string ws_url = base::StringPrintf(
150 "ws://127.0.0.1:%d/devtools/page/%s", port, it->c_str());
151 scoped_ptr<WebViewImpl> web_view(new WebViewImpl(
152 *it,
153 new DevToolsClientImpl(socket_factory, ws_url,
154 base::Bind(&FakeCloseDevToolsFrontend)),
155 chrome, base::Bind(&FakeCloseWebView)));
156
157 status = web_view->ConnectIfNecessary();
158 if (status.IsError())
159 return status;
160
161 scoped_ptr<base::Value> result;
162 status = web_view->EvaluateScript(
163 "", "document.getElementById('close-button-right').click();", &result);
164 // Ignore disconnected error, because the DevTools frontend is closed.
165 if (status.IsError() && status.code() != kDisconnected)
166 return status;
167 }
168
169 // Wait until DevTools UI disconnects from the given web view.
170 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
171 bool web_view_still_open = false;
172 while (base::Time::Now() < deadline) {
173 info_list.clear();
174 status = FetchWebViewsInfo(context_getter, port, &info_list);
175 if (status.IsError())
176 return status;
177
178 web_view_still_open = false;
179 for (WebViewInfoList::const_iterator it = info_list.begin();
180 it != info_list.end(); ++it) {
181 if (it->id == web_view_id) {
182 if (!it->debugger_url.empty())
183 return Status(kOk);
184 web_view_still_open = true;
185 break;
186 }
187 }
188 if (!web_view_still_open)
189 return Status(kUnknownError, "window closed while closing devtools");
190
191 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
192 }
193
194 return Status(kUnknownError, "failed to close DevTools frontend");
195 }
196
88 } // namespace 197 } // namespace
89 198
90 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter, 199 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter,
91 int port, 200 int port,
92 const SyncWebSocketFactory& socket_factory) 201 const SyncWebSocketFactory& socket_factory)
93 : context_getter_(context_getter), 202 : context_getter_(context_getter),
94 port_(port), 203 port_(port),
95 socket_factory_(socket_factory), 204 socket_factory_(socket_factory),
96 version_("unknown version"), 205 version_("unknown version"),
97 build_no_(0) {} 206 build_no_(0) {}
98 207
99 ChromeImpl::~ChromeImpl() { 208 ChromeImpl::~ChromeImpl() {
100 web_view_map_.clear(); 209 web_view_map_.clear();
101 } 210 }
102 211
103 std::string ChromeImpl::GetVersion() { 212 std::string ChromeImpl::GetVersion() {
104 return version_; 213 return version_;
105 } 214 }
106 215
107 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) { 216 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) {
108 std::list<std::string> ids; 217 WebViewInfoList info_list;
109 Status status = FetchPagesInfo(context_getter_, port_, &ids); 218 Status status = FetchWebViewsInfo(
219 context_getter_, port_, &info_list);
110 if (status.IsError()) 220 if (status.IsError())
111 return status; 221 return status;
112 222
113 std::list<WebView*> internal_web_views; 223 std::list<WebView*> internal_web_views;
114 for (std::list<std::string>::const_iterator it = ids.begin(); 224 for (WebViewInfoList::const_iterator it = info_list.begin();
115 it != ids.end(); ++it) { 225 it != info_list.end(); ++it) {
116 WebViewMap::const_iterator found = web_view_map_.find(*it); 226 WebViewMap::const_iterator found = web_view_map_.find(it->id);
117 if (found != web_view_map_.end()) { 227 if (found != web_view_map_.end()) {
118 internal_web_views.push_back(found->second.get()); 228 internal_web_views.push_back(found->second.get());
119 continue; 229 continue;
120 } 230 }
121 231
122 std::string ws_url = base::StringPrintf( 232 std::string ws_url = base::StringPrintf(
123 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->c_str()); 233 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->id.c_str());
124 web_view_map_[*it] = make_linked_ptr(new WebViewImpl( 234 DevToolsClientImpl::FrontendCloserFunc frontend_closer_func = base::Bind(
125 *it, new DevToolsClientImpl(socket_factory_, ws_url), 235 &CloseDevToolsFrontend, this, socket_factory_,
126 this, base::Bind(&CloseWebView, context_getter_, port_, *it))); 236 context_getter_, port_, it->id);
127 internal_web_views.push_back(web_view_map_[*it].get()); 237 web_view_map_[it->id] = make_linked_ptr(new WebViewImpl(
238 it->id,
239 new DevToolsClientImpl(socket_factory_, ws_url, frontend_closer_func),
240 this,
241 base::Bind(&CloseWebView, context_getter_, port_, it->id)));
242 internal_web_views.push_back(web_view_map_[it->id].get());
128 } 243 }
129 244
130 web_views->swap(internal_web_views); 245 web_views->swap(internal_web_views);
131 return Status(kOk); 246 return Status(kOk);
132 } 247 }
133 248
134 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) { 249 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) {
135 JavaScriptDialogManager* manager; 250 JavaScriptDialogManager* manager;
136 Status status = GetDialogManagerForOpenDialog(&manager); 251 Status status = GetDialogManagerForOpenDialog(&manager);
137 if (status.IsError()) 252 if (status.IsError())
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 break; 292 break;
178 if (status.code() != kChromeNotReachable) 293 if (status.code() != kChromeNotReachable)
179 return status; 294 return status;
180 } 295 }
181 if (status.IsError()) 296 if (status.IsError())
182 return status; 297 return status;
183 status = ParseAndCheckVersion(version); 298 status = ParseAndCheckVersion(version);
184 if (status.IsError()) 299 if (status.IsError())
185 return status; 300 return status;
186 301
187 std::list<std::string> page_ids; 302 WebViewInfoList info_list;
188 while (base::Time::Now() < deadline) { 303 while (base::Time::Now() < deadline) {
189 FetchPagesInfo(context_getter_, port_, &page_ids); 304 FetchWebViewsInfo(context_getter_, port_, &info_list);
190 if (page_ids.empty()) 305 if (info_list.empty())
191 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); 306 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
192 else 307 else
193 break; 308 return Status(kOk);
194 } 309 }
195 if (page_ids.empty()) 310 return Status(kUnknownError, "unable to discover open pages");
196 return Status(kUnknownError, "unable to discover open pages");
197
198 return Status(kOk);
199 } 311 }
200 312
201 int ChromeImpl::GetPort() const { 313 int ChromeImpl::GetPort() const {
202 return port_; 314 return port_;
203 } 315 }
204 316
205 Status ChromeImpl::GetDialogManagerForOpenDialog( 317 Status ChromeImpl::GetDialogManagerForOpenDialog(
206 JavaScriptDialogManager** manager) { 318 JavaScriptDialogManager** manager) {
207 std::list<WebView*> web_views; 319 std::list<WebView*> web_views;
208 Status status = GetWebViews(&web_views); 320 Status status = GetWebViews(&web_views);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 return Status(kUnknownError, "Chrome version must be >= " + 357 return Status(kUnknownError, "Chrome version must be >= " +
246 GetMinimumSupportedChromeVersion()); 358 GetMinimumSupportedChromeVersion());
247 } 359 }
248 version_ = stripped_version; 360 version_ = stripped_version;
249 build_no_ = build_no; 361 build_no_ = build_no;
250 return Status(kOk); 362 return Status(kOk);
251 } 363 }
252 364
253 namespace internal { 365 namespace internal {
254 366
367 WebViewInfo::WebViewInfo(const std::string& id,
368 const std::string& debugger_url,
369 const std::string& url,
370 Type type)
371 : id(id), debugger_url(debugger_url), url(url), type(type) {}
372
373 bool WebViewInfo::IsFrontend() const {
374 return url.find("chrome-devtools://") == 0u;
375 }
376
255 Status ParsePagesInfo(const std::string& data, 377 Status ParsePagesInfo(const std::string& data,
256 std::list<std::string>* page_ids) { 378 std::list<WebViewInfo>* info_list) {
257 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 379 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
258 if (!value.get()) 380 if (!value.get())
259 return Status(kUnknownError, "DevTools returned invalid JSON"); 381 return Status(kUnknownError, "DevTools returned invalid JSON");
260 base::ListValue* list; 382 base::ListValue* list;
261 if (!value->GetAsList(&list)) 383 if (!value->GetAsList(&list))
262 return Status(kUnknownError, "DevTools did not return list"); 384 return Status(kUnknownError, "DevTools did not return list");
263 385
264 std::list<std::string> ids; 386 std::list<WebViewInfo> info_list_tmp;
265 for (size_t i = 0; i < list->GetSize(); ++i) { 387 for (size_t i = 0; i < list->GetSize(); ++i) {
266 base::DictionaryValue* info; 388 base::DictionaryValue* info;
267 if (!list->GetDictionary(i, &info)) 389 if (!list->GetDictionary(i, &info))
268 return Status(kUnknownError, "DevTools contains non-dictionary item"); 390 return Status(kUnknownError, "DevTools contains non-dictionary item");
391 std::string id;
392 if (!info->GetString("id", &id))
393 return Status(kUnknownError, "DevTools did not include id");
269 std::string type; 394 std::string type;
270 if (!info->GetString("type", &type)) 395 if (!info->GetString("type", &type))
271 return Status(kUnknownError, "DevTools did not include type"); 396 return Status(kUnknownError, "DevTools did not include type");
272 if (type != "page") 397 std::string url;
273 continue; 398 if (!info->GetString("url", &url))
274 std::string id; 399 return Status(kUnknownError, "DevTools did not include url");
275 if (!info->GetString("id", &id)) 400 std::string debugger_url;
276 return Status(kUnknownError, "DevTools did not include id"); 401 info->GetString("webSocketDebuggerUrl", &debugger_url);
277 ids.push_back(id); 402 if (type == "page")
403 info_list_tmp.push_back(
404 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kPage));
405 else if (type == "other")
406 info_list_tmp.push_back(
407 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kOther));
408 else
409 return Status(kUnknownError, "DevTools returned unknown type:" + type);
278 } 410 }
279 page_ids->swap(ids); 411 info_list->swap(info_list_tmp);
280 return Status(kOk); 412 return Status(kOk);
281 } 413 }
282 414
283 Status ParseVersionInfo(const std::string& data, 415 Status ParseVersionInfo(const std::string& data,
284 std::string* version) { 416 std::string* version) {
285 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 417 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
286 if (!value.get()) 418 if (!value.get())
287 return Status(kUnknownError, "version info not in JSON"); 419 return Status(kUnknownError, "version info not in JSON");
288 base::DictionaryValue* dict; 420 base::DictionaryValue* dict;
289 if (!value->GetAsDictionary(&dict)) 421 if (!value->GetAsDictionary(&dict))
290 return Status(kUnknownError, "version info not a dictionary"); 422 return Status(kUnknownError, "version info not a dictionary");
291 if (!dict->GetString("Browser", version)) { 423 if (!dict->GetString("Browser", version)) {
292 return Status( 424 return Status(
293 kUnknownError, "Chrome version must be >= 26", 425 kUnknownError, "Chrome version must be >= 26",
294 Status(kUnknownError, "version info doesn't include string 'Browser'")); 426 Status(kUnknownError, "version info doesn't include string 'Browser'"));
295 } 427 }
296 return Status(kOk); 428 return Status(kOk);
297 } 429 }
298 430
299 } // namespace internal 431 } // namespace internal
OLDNEW
« no previous file with comments | « chrome/test/chromedriver/chrome_impl.h ('k') | chrome/test/chromedriver/chrome_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698