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

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/stringprintf.h" 12 #include "base/stringprintf.h"
13 #include "base/threading/platform_thread.h" 13 #include "base/threading/platform_thread.h"
14 #include "base/time.h" 14 #include "base/time.h"
15 #include "base/values.h" 15 #include "base/values.h"
16 #include "chrome/test/chromedriver/devtools_client_impl.h" 16 #include "chrome/test/chromedriver/devtools_client_impl.h"
17 #include "chrome/test/chromedriver/javascript_dialog_manager.h" 17 #include "chrome/test/chromedriver/javascript_dialog_manager.h"
18 #include "chrome/test/chromedriver/net/net_util.h" 18 #include "chrome/test/chromedriver/net/net_util.h"
19 #include "chrome/test/chromedriver/net/sync_websocket_impl.h" 19 #include "chrome/test/chromedriver/net/sync_websocket_impl.h"
20 #include "chrome/test/chromedriver/net/url_request_context_getter.h" 20 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
21 #include "chrome/test/chromedriver/status.h" 21 #include "chrome/test/chromedriver/status.h"
22 #include "chrome/test/chromedriver/web_view_impl.h" 22 #include "chrome/test/chromedriver/web_view_impl.h"
23 #include "googleurl/src/gurl.h" 23 #include "googleurl/src/gurl.h"
24 24
25 namespace { 25 namespace {
26 26
27 typedef std::list<internal::WebViewInfo> WebViewInfoList;
28
27 Status FetchPagesInfo(URLRequestContextGetter* context_getter, 29 Status FetchPagesInfo(URLRequestContextGetter* context_getter,
kkania 2013/03/04 23:33:57 this naming is no longer consistent since we've ch
chrisgao (Use stgao instead) 2013/03/05 06:58:17 Done. Changed name of function.
28 int port, 30 int port,
29 std::list<std::string>* page_ids) { 31 WebViewInfoList* web_view_info_list) {
30 std::string url = base::StringPrintf("http://127.0.0.1:%d/json", port); 32 std::string url = base::StringPrintf("http://127.0.0.1:%d/json", port);
31 std::string data; 33 std::string data;
32 if (!FetchUrl(GURL(url), context_getter, &data)) 34 if (!FetchUrl(GURL(url), context_getter, &data))
33 return Status(kChromeNotReachable); 35 return Status(kChromeNotReachable);
34 return internal::ParsePagesInfo(data, page_ids); 36
37 return ParsePagesInfo(data, web_view_info_list);
38 }
39
40 bool WebViewExist(const std::string& web_view_id,
kkania 2013/03/04 23:33:57 convention is to name this as DoesWebViewExist
chrisgao (Use stgao instead) 2013/03/05 06:58:17 Done.
41 const WebViewInfoList& info_list) {
42 for (WebViewInfoList::const_iterator it = info_list.begin();
43 it != info_list.end(); ++it) {
44 if (it->id == web_view_id)
45 return true;
46 }
47 return false;
35 } 48 }
36 49
37 Status CloseWebView(URLRequestContextGetter* context_getter, 50 Status CloseWebView(URLRequestContextGetter* context_getter,
38 int port, 51 int port,
39 const std::string& web_view_id) { 52 const std::string& web_view_id) {
40 std::list<std::string> ids; 53 WebViewInfoList web_view_info_list;
41 Status status = FetchPagesInfo(context_getter, port, &ids); 54 Status status = FetchPagesInfo(context_getter, port, &web_view_info_list);
42 if (status.IsError()) 55 if (status.IsError())
43 return status; 56 return status;
44 if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end()) 57 if (!WebViewExist(web_view_id, web_view_info_list))
45 return Status(kOk); 58 return Status(kOk);
46 59
47 bool is_last_web_view = ids.size() == 1; 60 bool is_last_web_view = web_view_info_list.size() == 1u;
48 61
49 std::string url = base::StringPrintf( 62 std::string url = base::StringPrintf(
50 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str()); 63 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str());
51 std::string data; 64 std::string data;
52 if (!FetchUrl(GURL(url), context_getter, &data)) 65 if (!FetchUrl(GURL(url), context_getter, &data))
53 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable); 66 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable);
54 if (data != "Target is closing") 67 if (data != "Target is closing")
55 return Status(kOk); 68 return Status(kOk);
56 69
57 // Wait for the target window to be completely closed. 70 // Wait for the target window to be completely closed.
58 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); 71 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
59 while (base::Time::Now() < deadline) { 72 while (base::Time::Now() < deadline) {
60 ids.clear(); 73 web_view_info_list.clear();
61 status = FetchPagesInfo(context_getter, port, &ids); 74 status = FetchPagesInfo(context_getter, port, &web_view_info_list);
62 if (is_last_web_view && status.code() == kChromeNotReachable) 75 if (is_last_web_view && status.code() == kChromeNotReachable)
63 return Status(kOk); // Closing the last web view leads chrome to quit. 76 return Status(kOk); // Closing the last web view leads chrome to quit.
64 if (status.IsError()) 77 if (status.IsError())
65 return status; 78 return status;
66 if (std::find(ids.begin(), ids.end(), web_view_id) == ids.end()) 79 if (!WebViewExist(web_view_id, web_view_info_list))
67 return Status(kOk); 80 return Status(kOk);
68 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); 81 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
69 } 82 }
70 83
71 return Status(kUnknownError, "failed to close window in 20 seconds"); 84 return Status(kUnknownError, "failed to close window in 20 seconds");
72 } 85 }
73 86
87 Status FakeCloseWebView() {
88 // This is for the docked DevTools frontend only.
89 return Status(kUnknownError,
90 "docked DevTools frontend should be closed by Javascript");
91 }
92
93 Status FakeCloseDevToolsFrontend() {
94 // This is for the docked DevTools frontend only.
95 return Status(kOk);
96 }
97
98 Status CloseDevToolsFrontend(ChromeImpl* chrome,
99 const SyncWebSocketFactory& socket_factory,
100 URLRequestContextGetter* context_getter,
101 int port,
102 const std::string& web_view_id) {
103 WebViewInfoList web_view_info_list;
104 Status status = FetchPagesInfo(context_getter, port, &web_view_info_list);
105 if (status.IsError())
106 return status;
107
108 std::list<std::string> tab_frontend_ids;
109 std::list<std::string> docked_frontend_ids;
110 // Filter out DevTools frontend.
111 for (WebViewInfoList::const_iterator it = web_view_info_list.begin();
112 it != web_view_info_list.end(); ++it) {
113 if (it->url.find("chrome-devtools://") == 0u) {
114 if (it->type == internal::WebViewInfo::kPage)
115 tab_frontend_ids.push_back(it->id);
116 else if (it->type == internal::WebViewInfo::kOther)
117 docked_frontend_ids.push_back(it->id);
118 else
119 return Status(kUnknownError, "unknown type of DevTools frontend");
120 }
121 }
122
123 // Close tab DevTools frontend as if closing a normal web view.
124 for (std::list<std::string>::const_iterator it = tab_frontend_ids.begin();
125 it != tab_frontend_ids.end(); ++it) {
126 status = CloseWebView(context_getter, port, *it);
127 if (status.IsError())
128 return status;
129 }
130
131 // Close docked DevTools frontend by Javascript.
132 for (std::list<std::string>::const_iterator it = docked_frontend_ids.begin();
133 it != docked_frontend_ids.end(); ++it) {
134 std::string ws_url = base::StringPrintf(
135 "ws://127.0.0.1:%d/devtools/page/%s", port, it->c_str());
136 scoped_ptr<WebViewImpl> web_view(new WebViewImpl(
137 *it,
138 new DevToolsClientImpl(socket_factory, ws_url,
139 base::Bind(&FakeCloseDevToolsFrontend)),
140 chrome, base::Bind(&FakeCloseWebView)));
141
142 status = web_view->EnsureAlive();
143 if (status.IsError())
144 return status;
145
146 scoped_ptr<base::Value> result;
147 status = web_view->EvaluateScript(
148 "", "document.getElementById('close-button-right').click();", &result);
149 // Ignore disconnected error, because the DevTools frontend is closed.
150 if (status.IsError() && status.code() != kDisconnected)
151 return status;
152 }
153
154 // Wait until DevTools UI disconnects from the given web view.
155 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
156 bool web_view_still_open = false;
157 while (base::Time::Now() < deadline) {
158 web_view_info_list.clear();
159 status = FetchPagesInfo(context_getter, port, &web_view_info_list);
160 if (status.IsError())
161 return status;
162
163 web_view_still_open = false;
164 for (WebViewInfoList::const_iterator it = web_view_info_list.begin();
165 it != web_view_info_list.end(); ++it) {
166 if (it->id == web_view_id) {
167 if (!it->debugger_url.empty())
168 return Status(kOk);
169 web_view_still_open = true;
170 break;
171 }
172 }
173 if (!web_view_still_open)
174 return Status(kUnknownError, "window closed while closing devtools");
175
176 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
177 }
178
179 return Status(kUnknownError, "failed to close DevTools frontend");
180 }
181
74 } // namespace 182 } // namespace
75 183
76 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter, 184 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter,
77 int port, 185 int port,
78 const SyncWebSocketFactory& socket_factory) 186 const SyncWebSocketFactory& socket_factory)
79 : context_getter_(context_getter), 187 : context_getter_(context_getter),
80 port_(port), 188 port_(port),
81 socket_factory_(socket_factory) {} 189 socket_factory_(socket_factory) {}
82 190
83 ChromeImpl::~ChromeImpl() { 191 ChromeImpl::~ChromeImpl() {
84 web_view_map_.clear(); 192 web_view_map_.clear();
85 } 193 }
86 194
87 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) { 195 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) {
88 std::list<std::string> ids; 196 WebViewInfoList web_view_info_list;
89 Status status = FetchPagesInfo(context_getter_, port_, &ids); 197 Status status = FetchPagesInfo(context_getter_, port_, &web_view_info_list);
90 if (status.IsError()) 198 if (status.IsError())
91 return status; 199 return status;
92 200
93 std::list<WebView*> internal_web_views; 201 std::list<WebView*> internal_web_views;
94 for (std::list<std::string>::const_iterator it = ids.begin(); 202 for (WebViewInfoList::const_iterator it = web_view_info_list.begin();
95 it != ids.end(); ++it) { 203 it != web_view_info_list.end(); ++it) {
96 WebViewMap::const_iterator found = web_view_map_.find(*it); 204 WebViewMap::const_iterator found = web_view_map_.find(it->id);
97 if (found != web_view_map_.end()) { 205 if (found != web_view_map_.end()) {
98 internal_web_views.push_back(found->second.get()); 206 internal_web_views.push_back(found->second.get());
99 continue; 207 continue;
100 } 208 }
101 209
102 std::string ws_url = base::StringPrintf( 210 std::string ws_url = base::StringPrintf(
103 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->c_str()); 211 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->id.c_str());
104 web_view_map_[*it] = make_linked_ptr(new WebViewImpl( 212 DevToolsClientImpl::FrontendCloserFunc frontend_closer_func = base::Bind(
105 *it, new DevToolsClientImpl(socket_factory_, ws_url), 213 &CloseDevToolsFrontend, this, socket_factory_,
106 this, base::Bind(&CloseWebView, context_getter_, port_, *it))); 214 context_getter_, port_, it->id);
107 internal_web_views.push_back(web_view_map_[*it].get()); 215 web_view_map_[it->id] = make_linked_ptr(new WebViewImpl(
216 it->id,
217 new DevToolsClientImpl(socket_factory_, ws_url, frontend_closer_func),
218 this,
219 base::Bind(&CloseWebView, context_getter_, port_, it->id)));
220 internal_web_views.push_back(web_view_map_[it->id].get());
108 } 221 }
109 222
110 web_views->swap(internal_web_views); 223 web_views->swap(internal_web_views);
111 return Status(kOk); 224 return Status(kOk);
112 } 225 }
113 226
114 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) { 227 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) {
115 JavaScriptDialogManager* manager; 228 JavaScriptDialogManager* manager;
116 Status status = GetDialogManagerForOpenDialog(&manager); 229 Status status = GetDialogManagerForOpenDialog(&manager);
117 if (status.IsError()) 230 if (status.IsError())
(...skipping 24 matching lines...) Expand all
142 255
143 return manager->HandleDialog(accept, prompt_text); 256 return manager->HandleDialog(accept, prompt_text);
144 } 257 }
145 258
146 void ChromeImpl::OnWebViewClose(WebView* web_view) { 259 void ChromeImpl::OnWebViewClose(WebView* web_view) {
147 web_view_map_.erase(web_view->GetId()); 260 web_view_map_.erase(web_view->GetId());
148 } 261 }
149 262
150 Status ChromeImpl::Init() { 263 Status ChromeImpl::Init() {
151 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20); 264 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
152 std::list<std::string> page_ids; 265 WebViewInfoList web_view_info_list;
153 while (base::Time::Now() < deadline) { 266 while (base::Time::Now() < deadline) {
154 FetchPagesInfo(context_getter_, port_, &page_ids); 267 FetchPagesInfo(context_getter_, port_, &web_view_info_list);
155 if (page_ids.empty()) 268 if (web_view_info_list.empty())
156 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); 269 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
157 else 270 else
158 break; 271 return Status(kOk);
159 } 272 }
160 if (page_ids.empty()) 273 return Status(kUnknownError, "unable to discover open pages");
161 return Status(kUnknownError, "unable to discover open pages");
162
163 return Status(kOk);
164 } 274 }
165 275
166 int ChromeImpl::GetPort() const { 276 int ChromeImpl::GetPort() const {
167 return port_; 277 return port_;
168 } 278 }
169 279
170 Status ChromeImpl::GetDialogManagerForOpenDialog( 280 Status ChromeImpl::GetDialogManagerForOpenDialog(
171 JavaScriptDialogManager** manager) { 281 JavaScriptDialogManager** manager) {
172 std::list<WebView*> web_views; 282 std::list<WebView*> web_views;
173 Status status = GetWebViews(&web_views); 283 Status status = GetWebViews(&web_views);
174 if (status.IsError()) 284 if (status.IsError())
175 return status; 285 return status;
176 286
177 for (std::list<WebView*>::const_iterator it = web_views.begin(); 287 for (std::list<WebView*>::const_iterator it = web_views.begin();
178 it != web_views.end(); ++it) { 288 it != web_views.end(); ++it) {
179 if ((*it)->GetJavaScriptDialogManager()->IsDialogOpen()) { 289 if ((*it)->GetJavaScriptDialogManager()->IsDialogOpen()) {
180 *manager = (*it)->GetJavaScriptDialogManager(); 290 *manager = (*it)->GetJavaScriptDialogManager();
181 return Status(kOk); 291 return Status(kOk);
182 } 292 }
183 } 293 }
184 *manager = NULL; 294 *manager = NULL;
185 return Status(kOk); 295 return Status(kOk);
186 } 296 }
187 297
188 namespace internal { 298 namespace internal {
189 299
300 WebViewInfo::WebViewInfo(const std::string& id,
301 const std::string& debugger_url,
302 const std::string& url,
303 Type type)
304 : id(id), debugger_url(debugger_url), url(url), type(type) {}
305
190 Status ParsePagesInfo(const std::string& data, 306 Status ParsePagesInfo(const std::string& data,
191 std::list<std::string>* page_ids) { 307 std::list<WebViewInfo>* web_view_info_list) {
192 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 308 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
193 if (!value.get()) 309 if (!value.get())
194 return Status(kUnknownError, "DevTools returned invalid JSON"); 310 return Status(kUnknownError, "DevTools returned invalid JSON");
195 base::ListValue* list; 311 base::ListValue* list;
196 if (!value->GetAsList(&list)) 312 if (!value->GetAsList(&list))
197 return Status(kUnknownError, "DevTools did not return list"); 313 return Status(kUnknownError, "DevTools did not return list");
198 314
199 std::list<std::string> ids; 315 std::list<WebViewInfo> web_view_info_list_tmp;
200 for (size_t i = 0; i < list->GetSize(); ++i) { 316 for (size_t i = 0; i < list->GetSize(); ++i) {
201 base::DictionaryValue* info; 317 base::DictionaryValue* info;
202 if (!list->GetDictionary(i, &info)) 318 if (!list->GetDictionary(i, &info))
203 return Status(kUnknownError, "DevTools contains non-dictionary item"); 319 return Status(kUnknownError, "DevTools contains non-dictionary item");
320 std::string id;
321 if (!info->GetString("id", &id))
322 return Status(kUnknownError, "DevTools did not include id");
204 std::string type; 323 std::string type;
205 if (!info->GetString("type", &type)) 324 if (!info->GetString("type", &type))
206 return Status(kUnknownError, "DevTools did not include type"); 325 return Status(kUnknownError, "DevTools did not include type");
207 if (type != "page") 326 std::string url;
208 continue; 327 if (!info->GetString("url", &url))
209 std::string id; 328 return Status(kUnknownError, "DevTools did not include url");
210 if (!info->GetString("id", &id)) 329 std::string debugger_url;
211 return Status(kUnknownError, "DevTools did not include id"); 330 info->GetString("webSocketDebuggerUrl", &debugger_url);
212 ids.push_back(id); 331 if (type == "page")
332 web_view_info_list_tmp.push_back(
333 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kPage));
334 else if (type == "other")
335 web_view_info_list_tmp.push_back(
336 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kOther));
337 else
338 return Status(kUnknownError, "DevTools returned unknown type:" + type);
213 } 339 }
214 page_ids->swap(ids); 340 web_view_info_list->swap(web_view_info_list_tmp);
215 return Status(kOk); 341 return Status(kOk);
216 } 342 }
217 343
218 } // namespace internal 344 } // namespace internal
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698