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

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: Rebase and address comments. 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* web_view_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 ParsePagesInfo(data, web_view_info_list);
kkania 2013/03/05 17:01:13 how does this know which function without the inte
kkania 2013/03/05 17:01:13 adjust the naming here too
chrisgao (Use stgao instead) 2013/03/06 01:19:54 Done.
chrisgao (Use stgao instead) 2013/03/06 01:19:54 whooops...the compiler even didn't raise an error.
52 }
53
54 bool DoesWebViewExist(const std::string& web_view_id,
kkania 2013/03/05 17:01:13 You could change this to: WebViewInfo* GetWebViewI
chrisgao (Use stgao instead) 2013/03/06 01:19:54 Done.
55 const WebViewInfoList& info_list) {
kkania 2013/03/05 17:01:13 indent
chrisgao (Use stgao instead) 2013/03/06 01:19:54 Done.
56 for (WebViewInfoList::const_iterator it = info_list.begin();
57 it != info_list.end(); ++it) {
58 if (it->id == web_view_id)
59 return true;
60 }
61 return false;
49 } 62 }
50 63
51 Status CloseWebView(URLRequestContextGetter* context_getter, 64 Status CloseWebView(URLRequestContextGetter* context_getter,
52 int port, 65 int port,
53 const std::string& web_view_id) { 66 const std::string& web_view_id) {
54 std::list<std::string> ids; 67 WebViewInfoList web_view_info_list;
55 Status status = FetchPagesInfo(context_getter, port, &ids); 68 Status status = FetchWebViewsInfo(context_getter, port, &web_view_info_list);
56 if (status.IsError()) 69 if (status.IsError())
57 return status; 70 return status;
58 if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end()) 71 if (!DoesWebViewExist(web_view_id, web_view_info_list))
59 return Status(kOk); 72 return Status(kOk);
60 73
61 bool is_last_web_view = ids.size() == 1; 74 bool is_last_web_view = web_view_info_list.size() == 1u;
62 75
63 std::string url = base::StringPrintf( 76 std::string url = base::StringPrintf(
64 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str()); 77 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str());
65 std::string data; 78 std::string data;
66 if (!FetchUrl(GURL(url), context_getter, &data)) 79 if (!FetchUrl(GURL(url), context_getter, &data))
67 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable); 80 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable);
68 if (data != "Target is closing") 81 if (data != "Target is closing")
69 return Status(kOk); 82 return Status(kOk);
70 83
71 // Wait for the target window to be completely closed. 84 // Wait for the target window to be completely closed.
72 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); 85 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
73 while (base::Time::Now() < deadline) { 86 while (base::Time::Now() < deadline) {
74 ids.clear(); 87 web_view_info_list.clear();
75 status = FetchPagesInfo(context_getter, port, &ids); 88 status = FetchWebViewsInfo(context_getter, port, &web_view_info_list);
76 if (is_last_web_view && status.code() == kChromeNotReachable) 89 if (is_last_web_view && status.code() == kChromeNotReachable)
77 return Status(kOk); // Closing the last web view leads chrome to quit. 90 return Status(kOk); // Closing the last web view leads chrome to quit.
78 if (status.IsError()) 91 if (status.IsError())
79 return status; 92 return status;
80 if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end()) 93 if (!DoesWebViewExist(web_view_id, web_view_info_list))
81 return Status(kOk); 94 return Status(kOk);
82 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); 95 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
83 } 96 }
84 97
85 return Status(kUnknownError, "failed to close window in 20 seconds"); 98 return Status(kUnknownError, "failed to close window in 20 seconds");
86 } 99 }
87 100
101 Status FakeCloseWebView() {
102 // This is for the docked DevTools frontend only.
103 return Status(kUnknownError,
104 "docked DevTools frontend should be closed by Javascript");
105 }
106
107 Status FakeCloseDevToolsFrontend() {
108 // This is for the docked DevTools frontend only.
109 return Status(kOk);
110 }
111
112 Status CloseDevToolsFrontend(ChromeImpl* chrome,
113 const SyncWebSocketFactory& socket_factory,
114 URLRequestContextGetter* context_getter,
115 int port,
116 const std::string& web_view_id) {
117 WebViewInfoList web_view_info_list;
118 Status status = FetchWebViewsInfo(context_getter, port, &web_view_info_list);
119 if (status.IsError())
120 return status;
121
122 std::list<std::string> tab_frontend_ids;
123 std::list<std::string> docked_frontend_ids;
124 // Filter out DevTools frontend.
125 for (WebViewInfoList::const_iterator it = web_view_info_list.begin();
126 it != web_view_info_list.end(); ++it) {
127 if (it->url.find("chrome-devtools://") == 0u) {
128 if (it->type == internal::WebViewInfo::kPage)
129 tab_frontend_ids.push_back(it->id);
130 else if (it->type == internal::WebViewInfo::kOther)
131 docked_frontend_ids.push_back(it->id);
132 else
133 return Status(kUnknownError, "unknown type of DevTools frontend");
134 }
135 }
136
137 // Close tab DevTools frontend as if closing a normal web view.
138 for (std::list<std::string>::const_iterator it = tab_frontend_ids.begin();
139 it != tab_frontend_ids.end(); ++it) {
140 status = CloseWebView(context_getter, port, *it);
141 if (status.IsError())
142 return status;
143 }
144
145 // Close docked DevTools frontend by Javascript.
146 for (std::list<std::string>::const_iterator it = docked_frontend_ids.begin();
147 it != docked_frontend_ids.end(); ++it) {
148 std::string ws_url = base::StringPrintf(
149 "ws://127.0.0.1:%d/devtools/page/%s", port, it->c_str());
150 scoped_ptr<WebViewImpl> web_view(new WebViewImpl(
151 *it,
152 new DevToolsClientImpl(socket_factory, ws_url,
153 base::Bind(&FakeCloseDevToolsFrontend)),
154 chrome, base::Bind(&FakeCloseWebView)));
155
156 status = web_view->ConnectIfNecessary();
157 if (status.IsError())
158 return status;
159
160 scoped_ptr<base::Value> result;
161 status = web_view->EvaluateScript(
162 "", "document.getElementById('close-button-right').click();", &result);
163 // Ignore disconnected error, because the DevTools frontend is closed.
164 if (status.IsError() && status.code() != kDisconnected)
165 return status;
166 }
167
168 // Wait until DevTools UI disconnects from the given web view.
169 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
170 bool web_view_still_open = false;
171 while (base::Time::Now() < deadline) {
172 web_view_info_list.clear();
173 status = FetchWebViewsInfo(context_getter, port, &web_view_info_list);
174 if (status.IsError())
175 return status;
176
177 web_view_still_open = false;
178 for (WebViewInfoList::const_iterator it = web_view_info_list.begin();
179 it != web_view_info_list.end(); ++it) {
180 if (it->id == web_view_id) {
181 if (!it->debugger_url.empty())
182 return Status(kOk);
183 web_view_still_open = true;
184 break;
185 }
186 }
187 if (!web_view_still_open)
188 return Status(kUnknownError, "window closed while closing devtools");
189
190 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
191 }
192
193 return Status(kUnknownError, "failed to close DevTools frontend");
194 }
195
88 } // namespace 196 } // namespace
89 197
90 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter, 198 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter,
91 int port, 199 int port,
92 const SyncWebSocketFactory& socket_factory) 200 const SyncWebSocketFactory& socket_factory)
93 : context_getter_(context_getter), 201 : context_getter_(context_getter),
94 port_(port), 202 port_(port),
95 socket_factory_(socket_factory), 203 socket_factory_(socket_factory),
96 version_("unknown version"), 204 version_("unknown version"),
97 build_no_(0) {} 205 build_no_(0) {}
98 206
99 ChromeImpl::~ChromeImpl() { 207 ChromeImpl::~ChromeImpl() {
100 web_view_map_.clear(); 208 web_view_map_.clear();
101 } 209 }
102 210
103 std::string ChromeImpl::GetVersion() { 211 std::string ChromeImpl::GetVersion() {
104 return version_; 212 return version_;
105 } 213 }
106 214
107 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) { 215 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) {
108 std::list<std::string> ids; 216 WebViewInfoList web_view_info_list;
109 Status status = FetchPagesInfo(context_getter_, port_, &ids); 217 Status status = FetchWebViewsInfo(
218 context_getter_, port_, &web_view_info_list);
110 if (status.IsError()) 219 if (status.IsError())
111 return status; 220 return status;
112 221
113 std::list<WebView*> internal_web_views; 222 std::list<WebView*> internal_web_views;
114 for (std::list<std::string>::const_iterator it = ids.begin(); 223 for (WebViewInfoList::const_iterator it = web_view_info_list.begin();
115 it != ids.end(); ++it) { 224 it != web_view_info_list.end(); ++it) {
116 WebViewMap::const_iterator found = web_view_map_.find(*it); 225 WebViewMap::const_iterator found = web_view_map_.find(it->id);
117 if (found != web_view_map_.end()) { 226 if (found != web_view_map_.end()) {
118 internal_web_views.push_back(found->second.get()); 227 internal_web_views.push_back(found->second.get());
119 continue; 228 continue;
120 } 229 }
121 230
122 std::string ws_url = base::StringPrintf( 231 std::string ws_url = base::StringPrintf(
123 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->c_str()); 232 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->id.c_str());
124 web_view_map_[*it] = make_linked_ptr(new WebViewImpl( 233 DevToolsClientImpl::FrontendCloserFunc frontend_closer_func = base::Bind(
125 *it, new DevToolsClientImpl(socket_factory_, ws_url), 234 &CloseDevToolsFrontend, this, socket_factory_,
126 this, base::Bind(&CloseWebView, context_getter_, port_, *it))); 235 context_getter_, port_, it->id);
127 internal_web_views.push_back(web_view_map_[*it].get()); 236 web_view_map_[it->id] = make_linked_ptr(new WebViewImpl(
237 it->id,
238 new DevToolsClientImpl(socket_factory_, ws_url, frontend_closer_func),
239 this,
240 base::Bind(&CloseWebView, context_getter_, port_, it->id)));
241 internal_web_views.push_back(web_view_map_[it->id].get());
128 } 242 }
129 243
130 web_views->swap(internal_web_views); 244 web_views->swap(internal_web_views);
131 return Status(kOk); 245 return Status(kOk);
132 } 246 }
133 247
134 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) { 248 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) {
135 JavaScriptDialogManager* manager; 249 JavaScriptDialogManager* manager;
136 Status status = GetDialogManagerForOpenDialog(&manager); 250 Status status = GetDialogManagerForOpenDialog(&manager);
137 if (status.IsError()) 251 if (status.IsError())
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 break; 291 break;
178 if (status.code() != kChromeNotReachable) 292 if (status.code() != kChromeNotReachable)
179 return status; 293 return status;
180 } 294 }
181 if (status.IsError()) 295 if (status.IsError())
182 return status; 296 return status;
183 status = ParseAndCheckVersion(version); 297 status = ParseAndCheckVersion(version);
184 if (status.IsError()) 298 if (status.IsError())
185 return status; 299 return status;
186 300
187 std::list<std::string> page_ids; 301 WebViewInfoList web_view_info_list;
188 while (base::Time::Now() < deadline) { 302 while (base::Time::Now() < deadline) {
189 FetchPagesInfo(context_getter_, port_, &page_ids); 303 FetchWebViewsInfo(context_getter_, port_, &web_view_info_list);
190 if (page_ids.empty()) 304 if (web_view_info_list.empty())
191 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); 305 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
192 else 306 else
193 break; 307 return Status(kOk);
194 } 308 }
195 if (page_ids.empty()) 309 return Status(kUnknownError, "unable to discover open pages");
196 return Status(kUnknownError, "unable to discover open pages");
197
198 return Status(kOk);
199 } 310 }
200 311
201 int ChromeImpl::GetPort() const { 312 int ChromeImpl::GetPort() const {
202 return port_; 313 return port_;
203 } 314 }
204 315
205 Status ChromeImpl::GetDialogManagerForOpenDialog( 316 Status ChromeImpl::GetDialogManagerForOpenDialog(
206 JavaScriptDialogManager** manager) { 317 JavaScriptDialogManager** manager) {
207 std::list<WebView*> web_views; 318 std::list<WebView*> web_views;
208 Status status = GetWebViews(&web_views); 319 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 >= " + 356 return Status(kUnknownError, "Chrome version must be >= " +
246 GetMinimumSupportedChromeVersion()); 357 GetMinimumSupportedChromeVersion());
247 } 358 }
248 version_ = stripped_version; 359 version_ = stripped_version;
249 build_no_ = build_no; 360 build_no_ = build_no;
250 return Status(kOk); 361 return Status(kOk);
251 } 362 }
252 363
253 namespace internal { 364 namespace internal {
254 365
366 WebViewInfo::WebViewInfo(const std::string& id,
367 const std::string& debugger_url,
368 const std::string& url,
369 Type type)
370 : id(id), debugger_url(debugger_url), url(url), type(type) {}
371
255 Status ParsePagesInfo(const std::string& data, 372 Status ParsePagesInfo(const std::string& data,
256 std::list<std::string>* page_ids) { 373 std::list<WebViewInfo>* web_view_info_list) {
257 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 374 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
258 if (!value.get()) 375 if (!value.get())
259 return Status(kUnknownError, "DevTools returned invalid JSON"); 376 return Status(kUnknownError, "DevTools returned invalid JSON");
260 base::ListValue* list; 377 base::ListValue* list;
261 if (!value->GetAsList(&list)) 378 if (!value->GetAsList(&list))
262 return Status(kUnknownError, "DevTools did not return list"); 379 return Status(kUnknownError, "DevTools did not return list");
263 380
264 std::list<std::string> ids; 381 std::list<WebViewInfo> web_view_info_list_tmp;
265 for (size_t i = 0; i < list->GetSize(); ++i) { 382 for (size_t i = 0; i < list->GetSize(); ++i) {
266 base::DictionaryValue* info; 383 base::DictionaryValue* info;
267 if (!list->GetDictionary(i, &info)) 384 if (!list->GetDictionary(i, &info))
268 return Status(kUnknownError, "DevTools contains non-dictionary item"); 385 return Status(kUnknownError, "DevTools contains non-dictionary item");
386 std::string id;
387 if (!info->GetString("id", &id))
388 return Status(kUnknownError, "DevTools did not include id");
269 std::string type; 389 std::string type;
270 if (!info->GetString("type", &type)) 390 if (!info->GetString("type", &type))
271 return Status(kUnknownError, "DevTools did not include type"); 391 return Status(kUnknownError, "DevTools did not include type");
272 if (type != "page") 392 std::string url;
273 continue; 393 if (!info->GetString("url", &url))
274 std::string id; 394 return Status(kUnknownError, "DevTools did not include url");
275 if (!info->GetString("id", &id)) 395 std::string debugger_url;
276 return Status(kUnknownError, "DevTools did not include id"); 396 info->GetString("webSocketDebuggerUrl", &debugger_url);
277 ids.push_back(id); 397 if (type == "page")
398 web_view_info_list_tmp.push_back(
399 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kPage));
400 else if (type == "other")
401 web_view_info_list_tmp.push_back(
402 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kOther));
403 else
404 return Status(kUnknownError, "DevTools returned unknown type:" + type);
278 } 405 }
279 page_ids->swap(ids); 406 web_view_info_list->swap(web_view_info_list_tmp);
280 return Status(kOk); 407 return Status(kOk);
281 } 408 }
282 409
283 Status ParseVersionInfo(const std::string& data, 410 Status ParseVersionInfo(const std::string& data,
284 std::string* version) { 411 std::string* version) {
285 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 412 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
286 if (!value.get()) 413 if (!value.get())
287 return Status(kUnknownError, "version info not in JSON"); 414 return Status(kUnknownError, "version info not in JSON");
288 base::DictionaryValue* dict; 415 base::DictionaryValue* dict;
289 if (!value->GetAsDictionary(&dict)) 416 if (!value->GetAsDictionary(&dict))
290 return Status(kUnknownError, "version info not a dictionary"); 417 return Status(kUnknownError, "version info not a dictionary");
291 if (!dict->GetString("Browser", version)) { 418 if (!dict->GetString("Browser", version)) {
292 return Status( 419 return Status(
293 kUnknownError, "Chrome version must be >= 26", 420 kUnknownError, "Chrome version must be >= 26",
294 Status(kUnknownError, "version info doesn't include string 'Browser'")); 421 Status(kUnknownError, "version info doesn't include string 'Browser'"));
295 } 422 }
296 return Status(kOk); 423 return Status(kOk);
297 } 424 }
298 425
299 } // namespace internal 426 } // namespace internal
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698