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

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

Issue 12848005: [chromedriver] Separate stuff of chrome from chromedriver. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments and fix compile error on mac. 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/test/chromedriver/chrome_impl.h"
6
7 #include <algorithm>
8 #include <list>
9
10 #include "base/bind.h"
11 #include "base/json/json_reader.h"
12 #include "base/stringprintf.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time.h"
17 #include "base/values.h"
18 #include "chrome/test/chromedriver/devtools_client_impl.h"
19 #include "chrome/test/chromedriver/javascript_dialog_manager.h"
20 #include "chrome/test/chromedriver/net/net_util.h"
21 #include "chrome/test/chromedriver/net/sync_websocket_impl.h"
22 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
23 #include "chrome/test/chromedriver/status.h"
24 #include "chrome/test/chromedriver/version.h"
25 #include "chrome/test/chromedriver/web_view_impl.h"
26 #include "googleurl/src/gurl.h"
27
28 namespace {
29
30 typedef std::list<internal::WebViewInfo> WebViewInfoList;
31
32 Status FetchVersionInfo(URLRequestContextGetter* context_getter,
33 int port,
34 std::string* version) {
35 std::string url = base::StringPrintf(
36 "http://127.0.0.1:%d/json/version", port);
37 std::string data;
38 if (!FetchUrl(GURL(url), context_getter, &data))
39 return Status(kChromeNotReachable);
40 return internal::ParseVersionInfo(data, version);
41 }
42
43 Status FetchWebViewsInfo(URLRequestContextGetter* context_getter,
44 int port,
45 WebViewInfoList* info_list) {
46 std::string url = base::StringPrintf("http://127.0.0.1:%d/json", port);
47 std::string data;
48 if (!FetchUrl(GURL(url), context_getter, &data))
49 return Status(kChromeNotReachable);
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;
63 }
64
65 Status CloseWebView(URLRequestContextGetter* context_getter,
66 int port,
67 const std::string& web_view_id) {
68 WebViewInfoList info_list;
69 Status status = FetchWebViewsInfo(context_getter, port, &info_list);
70 if (status.IsError())
71 return status;
72 if (!GetWebViewFromList(web_view_id, info_list))
73 return Status(kOk);
74
75 bool is_last_web_view = info_list.size() == 1u;
76
77 std::string url = base::StringPrintf(
78 "http://127.0.0.1:%d/json/close/%s", port, web_view_id.c_str());
79 std::string data;
80 if (!FetchUrl(GURL(url), context_getter, &data))
81 return is_last_web_view ? Status(kOk) : Status(kChromeNotReachable);
82 if (data != "Target is closing")
83 return Status(kOk);
84
85 // Wait for the target window to be completely closed.
86 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
87 while (base::Time::Now() < deadline) {
88 info_list.clear();
89 status = FetchWebViewsInfo(context_getter, port, &info_list);
90 if (is_last_web_view && status.code() == kChromeNotReachable)
91 return Status(kOk); // Closing the last web view leads chrome to quit.
92 if (status.IsError())
93 return status;
94 if (!GetWebViewFromList(web_view_id, info_list))
95 return Status(kOk);
96 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
97 }
98
99 return Status(kUnknownError, "failed to close window in 20 seconds");
100 }
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
197 } // namespace
198
199 ChromeImpl::ChromeImpl(URLRequestContextGetter* context_getter,
200 int port,
201 const SyncWebSocketFactory& socket_factory)
202 : context_getter_(context_getter),
203 port_(port),
204 socket_factory_(socket_factory),
205 version_("unknown version"),
206 build_no_(0) {}
207
208 ChromeImpl::~ChromeImpl() {
209 web_view_map_.clear();
210 }
211
212 std::string ChromeImpl::GetVersion() {
213 return version_;
214 }
215
216 Status ChromeImpl::GetWebViews(std::list<WebView*>* web_views) {
217 WebViewInfoList info_list;
218 Status status = FetchWebViewsInfo(
219 context_getter_, port_, &info_list);
220 if (status.IsError())
221 return status;
222
223 std::list<WebView*> internal_web_views;
224 for (WebViewInfoList::const_iterator it = info_list.begin();
225 it != info_list.end(); ++it) {
226 WebViewMap::const_iterator found = web_view_map_.find(it->id);
227 if (found != web_view_map_.end()) {
228 internal_web_views.push_back(found->second.get());
229 continue;
230 }
231
232 std::string ws_url = base::StringPrintf(
233 "ws://127.0.0.1:%d/devtools/page/%s", port_, it->id.c_str());
234 DevToolsClientImpl::FrontendCloserFunc frontend_closer_func = base::Bind(
235 &CloseDevToolsFrontend, this, socket_factory_,
236 context_getter_, port_, it->id);
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());
243 }
244
245 web_views->swap(internal_web_views);
246 return Status(kOk);
247 }
248
249 Status ChromeImpl::IsJavaScriptDialogOpen(bool* is_open) {
250 JavaScriptDialogManager* manager;
251 Status status = GetDialogManagerForOpenDialog(&manager);
252 if (status.IsError())
253 return status;
254 *is_open = manager != NULL;
255 return Status(kOk);
256 }
257
258 Status ChromeImpl::GetJavaScriptDialogMessage(std::string* message) {
259 JavaScriptDialogManager* manager;
260 Status status = GetDialogManagerForOpenDialog(&manager);
261 if (status.IsError())
262 return status;
263 if (!manager)
264 return Status(kNoAlertOpen);
265
266 return manager->GetDialogMessage(message);
267 }
268
269 Status ChromeImpl::HandleJavaScriptDialog(bool accept,
270 const std::string& prompt_text) {
271 JavaScriptDialogManager* manager;
272 Status status = GetDialogManagerForOpenDialog(&manager);
273 if (status.IsError())
274 return status;
275 if (!manager)
276 return Status(kNoAlertOpen);
277
278 return manager->HandleDialog(accept, prompt_text);
279 }
280
281 void ChromeImpl::OnWebViewClose(WebView* web_view) {
282 web_view_map_.erase(web_view->GetId());
283 }
284
285 Status ChromeImpl::Init() {
286 base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
287 std::string version;
288 Status status(kOk);
289 while (base::Time::Now() < deadline) {
290 status = FetchVersionInfo(context_getter_, port_, &version);
291 if (status.IsOk())
292 break;
293 if (status.code() != kChromeNotReachable)
294 return status;
295 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
296 }
297 if (status.IsError())
298 return status;
299 status = ParseAndCheckVersion(version);
300 if (status.IsError())
301 return status;
302
303 WebViewInfoList info_list;
304 while (base::Time::Now() < deadline) {
305 FetchWebViewsInfo(context_getter_, port_, &info_list);
306 if (info_list.empty())
307 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
308 else
309 return Status(kOk);
310 }
311 return Status(kUnknownError, "unable to discover open pages");
312 }
313
314 int ChromeImpl::GetPort() const {
315 return port_;
316 }
317
318 Status ChromeImpl::GetDialogManagerForOpenDialog(
319 JavaScriptDialogManager** manager) {
320 std::list<WebView*> web_views;
321 Status status = GetWebViews(&web_views);
322 if (status.IsError())
323 return status;
324
325 for (std::list<WebView*>::const_iterator it = web_views.begin();
326 it != web_views.end(); ++it) {
327 if ((*it)->GetJavaScriptDialogManager()->IsDialogOpen()) {
328 *manager = (*it)->GetJavaScriptDialogManager();
329 return Status(kOk);
330 }
331 }
332 *manager = NULL;
333 return Status(kOk);
334 }
335
336 Status ChromeImpl::ParseAndCheckVersion(const std::string& version) {
337 if (version.empty()) {
338 // Content Shell has an empty product version and a fake user agent.
339 // There's no way to detect the actual version, so assume it is tip of tree.
340 version_ = "content shell";
341 build_no_ = 9999;
342 return Status(kOk);
343 }
344 std::string prefix = "Chrome/";
345 if (version.find(prefix) != 0u)
346 return Status(kUnknownError, "unrecognized Chrome version: " + version);
347
348 std::string stripped_version = version.substr(prefix.length());
349 int build_no;
350 std::vector<std::string> version_parts;
351 base::SplitString(stripped_version, '.', &version_parts);
352 if (version_parts.size() != 4 ||
353 !base::StringToInt(version_parts[2], &build_no)) {
354 return Status(kUnknownError, "unrecognized Chrome version: " + version);
355 }
356
357 if (build_no < kMinimumSupportedChromeBuildNo) {
358 return Status(kUnknownError, "Chrome version must be >= " +
359 GetMinimumSupportedChromeVersion());
360 }
361 version_ = stripped_version;
362 build_no_ = build_no;
363 return Status(kOk);
364 }
365
366 namespace internal {
367
368 WebViewInfo::WebViewInfo(const std::string& id,
369 const std::string& debugger_url,
370 const std::string& url,
371 Type type)
372 : id(id), debugger_url(debugger_url), url(url), type(type) {}
373
374 bool WebViewInfo::IsFrontend() const {
375 return url.find("chrome-devtools://") == 0u;
376 }
377
378 Status ParsePagesInfo(const std::string& data,
379 std::list<WebViewInfo>* info_list) {
380 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
381 if (!value.get())
382 return Status(kUnknownError, "DevTools returned invalid JSON");
383 base::ListValue* list;
384 if (!value->GetAsList(&list))
385 return Status(kUnknownError, "DevTools did not return list");
386
387 std::list<WebViewInfo> info_list_tmp;
388 for (size_t i = 0; i < list->GetSize(); ++i) {
389 base::DictionaryValue* info;
390 if (!list->GetDictionary(i, &info))
391 return Status(kUnknownError, "DevTools contains non-dictionary item");
392 std::string id;
393 if (!info->GetString("id", &id))
394 return Status(kUnknownError, "DevTools did not include id");
395 std::string type;
396 if (!info->GetString("type", &type))
397 return Status(kUnknownError, "DevTools did not include type");
398 std::string url;
399 if (!info->GetString("url", &url))
400 return Status(kUnknownError, "DevTools did not include url");
401 std::string debugger_url;
402 info->GetString("webSocketDebuggerUrl", &debugger_url);
403 if (type == "page")
404 info_list_tmp.push_back(
405 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kPage));
406 else if (type == "other")
407 info_list_tmp.push_back(
408 WebViewInfo(id, debugger_url, url, internal::WebViewInfo::kOther));
409 else
410 return Status(kUnknownError, "DevTools returned unknown type:" + type);
411 }
412 info_list->swap(info_list_tmp);
413 return Status(kOk);
414 }
415
416 Status ParseVersionInfo(const std::string& data,
417 std::string* version) {
418 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
419 if (!value.get())
420 return Status(kUnknownError, "version info not in JSON");
421 base::DictionaryValue* dict;
422 if (!value->GetAsDictionary(&dict))
423 return Status(kUnknownError, "version info not a dictionary");
424 if (!dict->GetString("Browser", version)) {
425 return Status(
426 kUnknownError, "Chrome version must be >= 26",
427 Status(kUnknownError, "version info doesn't include string 'Browser'"));
428 }
429 return Status(kOk);
430 }
431
432 } // 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