Roll src/third_party/WebKit bf18a82:a9cee16 (svn 185297:185304)
[chromium-blink-merge.git] / chrome / test / chromedriver / session_commands.cc
blob9af15c3a49f51164d7b480df0a1c6d5f13448759
1 // Copyright (c) 2013 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.
5 #include "chrome/test/chromedriver/session_commands.h"
7 #include <list>
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/files/file_util.h"
12 #include "base/logging.h" // For CHECK macros.
13 #include "base/memory/ref_counted.h"
14 #include "base/message_loop/message_loop_proxy.h"
15 #include "base/synchronization/lock.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/values.h"
18 #include "chrome/test/chromedriver/basic_types.h"
19 #include "chrome/test/chromedriver/capabilities.h"
20 #include "chrome/test/chromedriver/chrome/automation_extension.h"
21 #include "chrome/test/chromedriver/chrome/browser_info.h"
22 #include "chrome/test/chromedriver/chrome/chrome.h"
23 #include "chrome/test/chromedriver/chrome/chrome_android_impl.h"
24 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
25 #include "chrome/test/chromedriver/chrome/device_manager.h"
26 #include "chrome/test/chromedriver/chrome/devtools_event_listener.h"
27 #include "chrome/test/chromedriver/chrome/geoposition.h"
28 #include "chrome/test/chromedriver/chrome/status.h"
29 #include "chrome/test/chromedriver/chrome/web_view.h"
30 #include "chrome/test/chromedriver/chrome_launcher.h"
31 #include "chrome/test/chromedriver/command_listener.h"
32 #include "chrome/test/chromedriver/logging.h"
33 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
34 #include "chrome/test/chromedriver/session.h"
35 #include "chrome/test/chromedriver/util.h"
36 #include "chrome/test/chromedriver/version.h"
38 namespace {
40 const char kWindowHandlePrefix[] = "CDwindow-";
42 std::string WebViewIdToWindowHandle(const std::string& web_view_id) {
43 return kWindowHandlePrefix + web_view_id;
46 bool WindowHandleToWebViewId(const std::string& window_handle,
47 std::string* web_view_id) {
48 if (window_handle.find(kWindowHandlePrefix) != 0u)
49 return false;
50 *web_view_id = window_handle.substr(
51 std::string(kWindowHandlePrefix).length());
52 return true;
55 } // namespace
57 InitSessionParams::InitSessionParams(
58 scoped_refptr<URLRequestContextGetter> context_getter,
59 const SyncWebSocketFactory& socket_factory,
60 DeviceManager* device_manager,
61 PortServer* port_server,
62 PortManager* port_manager)
63 : context_getter(context_getter),
64 socket_factory(socket_factory),
65 device_manager(device_manager),
66 port_server(port_server),
67 port_manager(port_manager) {}
69 InitSessionParams::~InitSessionParams() {}
71 namespace {
73 scoped_ptr<base::DictionaryValue> CreateCapabilities(Chrome* chrome) {
74 scoped_ptr<base::DictionaryValue> caps(new base::DictionaryValue());
75 caps->SetString("browserName", "chrome");
76 caps->SetString("version", chrome->GetBrowserInfo()->browser_version);
77 caps->SetString("chrome.chromedriverVersion", kChromeDriverVersion);
78 caps->SetString("platform", chrome->GetOperatingSystemName());
79 caps->SetBoolean("javascriptEnabled", true);
80 caps->SetBoolean("takesScreenshot", true);
81 caps->SetBoolean("takesHeapSnapshot", true);
82 caps->SetBoolean("handlesAlerts", true);
83 caps->SetBoolean("databaseEnabled", false);
84 caps->SetBoolean("locationContextEnabled", true);
85 caps->SetBoolean("mobileEmulationEnabled",
86 chrome->IsMobileEmulationEnabled());
87 caps->SetBoolean("applicationCacheEnabled", false);
88 caps->SetBoolean("browserConnectionEnabled", false);
89 caps->SetBoolean("cssSelectorsEnabled", true);
90 caps->SetBoolean("webStorageEnabled", true);
91 caps->SetBoolean("rotatable", false);
92 caps->SetBoolean("acceptSslCerts", true);
93 caps->SetBoolean("nativeEvents", true);
94 scoped_ptr<base::DictionaryValue> chrome_caps(new base::DictionaryValue());
96 ChromeDesktopImpl* desktop = NULL;
97 Status status = chrome->GetAsDesktop(&desktop);
98 if (status.IsOk()) {
99 chrome_caps->SetString(
100 "userDataDir",
101 desktop->command().GetSwitchValueNative("user-data-dir"));
104 caps->Set("chrome", chrome_caps.release());
105 return caps.Pass();
108 Status InitSessionHelper(
109 const InitSessionParams& bound_params,
110 Session* session,
111 const base::DictionaryValue& params,
112 scoped_ptr<base::Value>* value) {
113 session->driver_log.reset(
114 new WebDriverLog(WebDriverLog::kDriverType, Log::kAll));
115 const base::DictionaryValue* desired_caps;
116 if (!params.GetDictionary("desiredCapabilities", &desired_caps))
117 return Status(kUnknownError, "cannot find dict 'desiredCapabilities'");
119 Capabilities capabilities;
120 Status status = capabilities.Parse(*desired_caps);
121 if (status.IsError())
122 return status;
124 Log::Level driver_level = Log::kWarning;
125 if (capabilities.logging_prefs.count(WebDriverLog::kDriverType))
126 driver_level = capabilities.logging_prefs[WebDriverLog::kDriverType];
127 session->driver_log->set_min_level(driver_level);
129 // Create Log's and DevToolsEventListener's for ones that are DevTools-based.
130 // Session will own the Log's, Chrome will own the listeners.
131 // Also create |CommandListener|s for the appropriate logs.
132 ScopedVector<DevToolsEventListener> devtools_event_listeners;
133 ScopedVector<CommandListener> command_listeners;
134 status = CreateLogs(capabilities,
135 session,
136 &session->devtools_logs,
137 &devtools_event_listeners,
138 &command_listeners);
139 if (status.IsError())
140 return status;
142 // |session| will own the |CommandListener|s.
143 session->command_listeners.swap(command_listeners);
145 status = LaunchChrome(bound_params.context_getter.get(),
146 bound_params.socket_factory,
147 bound_params.device_manager,
148 bound_params.port_server,
149 bound_params.port_manager,
150 capabilities,
151 &devtools_event_listeners,
152 &session->chrome);
153 if (status.IsError())
154 return status;
156 std::list<std::string> web_view_ids;
157 status = session->chrome->GetWebViewIds(&web_view_ids);
158 if (status.IsError() || web_view_ids.empty()) {
159 return status.IsError() ? status :
160 Status(kUnknownError, "unable to discover open window in chrome");
163 session->window = web_view_ids.front();
164 session->detach = capabilities.detach;
165 session->force_devtools_screenshot = capabilities.force_devtools_screenshot;
166 session->capabilities = CreateCapabilities(session->chrome.get());
167 value->reset(session->capabilities->DeepCopy());
168 return Status(kOk);
171 } // namespace
173 Status ExecuteInitSession(
174 const InitSessionParams& bound_params,
175 Session* session,
176 const base::DictionaryValue& params,
177 scoped_ptr<base::Value>* value) {
178 Status status = InitSessionHelper(bound_params, session, params, value);
179 if (status.IsError()) {
180 session->quit = true;
181 if (session->chrome != NULL)
182 session->chrome->Quit();
184 return status;
187 Status ExecuteQuit(
188 bool allow_detach,
189 Session* session,
190 const base::DictionaryValue& params,
191 scoped_ptr<base::Value>* value) {
192 session->quit = true;
193 if (allow_detach && session->detach)
194 return Status(kOk);
195 else
196 return session->chrome->Quit();
199 Status ExecuteGetSessionCapabilities(
200 Session* session,
201 const base::DictionaryValue& params,
202 scoped_ptr<base::Value>* value) {
203 value->reset(session->capabilities->DeepCopy());
204 return Status(kOk);
207 Status ExecuteGetCurrentWindowHandle(
208 Session* session,
209 const base::DictionaryValue& params,
210 scoped_ptr<base::Value>* value) {
211 WebView* web_view = NULL;
212 Status status = session->GetTargetWindow(&web_view);
213 if (status.IsError())
214 return status;
216 value->reset(
217 new base::StringValue(WebViewIdToWindowHandle(web_view->GetId())));
218 return Status(kOk);
221 Status ExecuteLaunchApp(
222 Session* session,
223 const base::DictionaryValue& params,
224 scoped_ptr<base::Value>* value) {
225 std::string id;
226 if (!params.GetString("id", &id))
227 return Status(kUnknownError, "'id' must be a string");
229 ChromeDesktopImpl* desktop = NULL;
230 Status status = session->chrome->GetAsDesktop(&desktop);
231 if (status.IsError())
232 return status;
234 AutomationExtension* extension = NULL;
235 status = desktop->GetAutomationExtension(&extension);
236 if (status.IsError())
237 return status;
239 return extension->LaunchApp(id);
242 Status ExecuteClose(
243 Session* session,
244 const base::DictionaryValue& params,
245 scoped_ptr<base::Value>* value) {
246 std::list<std::string> web_view_ids;
247 Status status = session->chrome->GetWebViewIds(&web_view_ids);
248 if (status.IsError())
249 return status;
250 bool is_last_web_view = web_view_ids.size() == 1u;
251 web_view_ids.clear();
253 WebView* web_view = NULL;
254 status = session->GetTargetWindow(&web_view);
255 if (status.IsError())
256 return status;
258 status = session->chrome->CloseWebView(web_view->GetId());
259 if (status.IsError())
260 return status;
262 status = session->chrome->GetWebViewIds(&web_view_ids);
263 if ((status.code() == kChromeNotReachable && is_last_web_view) ||
264 (status.IsOk() && web_view_ids.empty())) {
265 // If no window is open, close is the equivalent of calling "quit".
266 session->quit = true;
267 return session->chrome->Quit();
270 return status;
273 Status ExecuteGetWindowHandles(
274 Session* session,
275 const base::DictionaryValue& params,
276 scoped_ptr<base::Value>* value) {
277 std::list<std::string> web_view_ids;
278 Status status = session->chrome->GetWebViewIds(&web_view_ids);
279 if (status.IsError())
280 return status;
281 scoped_ptr<base::ListValue> window_ids(new base::ListValue());
282 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
283 it != web_view_ids.end(); ++it) {
284 window_ids->AppendString(WebViewIdToWindowHandle(*it));
286 value->reset(window_ids.release());
287 return Status(kOk);
290 Status ExecuteSwitchToWindow(
291 Session* session,
292 const base::DictionaryValue& params,
293 scoped_ptr<base::Value>* value) {
294 std::string name;
295 if (!params.GetString("name", &name) || name.empty())
296 return Status(kUnknownError, "'name' must be a nonempty string");
298 std::list<std::string> web_view_ids;
299 Status status = session->chrome->GetWebViewIds(&web_view_ids);
300 if (status.IsError())
301 return status;
303 std::string web_view_id;
304 bool found = false;
305 if (WindowHandleToWebViewId(name, &web_view_id)) {
306 // Check if any web_view matches |web_view_id|.
307 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
308 it != web_view_ids.end(); ++it) {
309 if (*it == web_view_id) {
310 found = true;
311 break;
314 } else {
315 // Check if any of the tab window names match |name|.
316 const char* kGetWindowNameScript = "function() { return window.name; }";
317 base::ListValue args;
318 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
319 it != web_view_ids.end(); ++it) {
320 scoped_ptr<base::Value> result;
321 WebView* web_view;
322 status = session->chrome->GetWebViewById(*it, &web_view);
323 if (status.IsError())
324 return status;
325 status = web_view->ConnectIfNecessary();
326 if (status.IsError())
327 return status;
328 status = web_view->CallFunction(
329 std::string(), kGetWindowNameScript, args, &result);
330 if (status.IsError())
331 return status;
332 std::string window_name;
333 if (!result->GetAsString(&window_name))
334 return Status(kUnknownError, "failed to get window name");
335 if (window_name == name) {
336 web_view_id = *it;
337 found = true;
338 break;
343 if (!found)
344 return Status(kNoSuchWindow);
346 if (session->overridden_geoposition) {
347 WebView* web_view;
348 status = session->chrome->GetWebViewById(web_view_id, &web_view);
349 if (status.IsError())
350 return status;
351 status = web_view->ConnectIfNecessary();
352 if (status.IsError())
353 return status;
354 status = web_view->OverrideGeolocation(*session->overridden_geoposition);
355 if (status.IsError())
356 return status;
359 session->window = web_view_id;
360 session->SwitchToTopFrame();
361 session->mouse_position = WebPoint(0, 0);
362 return Status(kOk);
365 Status ExecuteSetTimeout(
366 Session* session,
367 const base::DictionaryValue& params,
368 scoped_ptr<base::Value>* value) {
369 double ms_double;
370 if (!params.GetDouble("ms", &ms_double))
371 return Status(kUnknownError, "'ms' must be a double");
372 std::string type;
373 if (!params.GetString("type", &type))
374 return Status(kUnknownError, "'type' must be a string");
376 base::TimeDelta timeout =
377 base::TimeDelta::FromMilliseconds(static_cast<int>(ms_double));
378 // TODO(frankf): implicit and script timeout should be cleared
379 // if negative timeout is specified.
380 if (type == "implicit") {
381 session->implicit_wait = timeout;
382 } else if (type == "script") {
383 session->script_timeout = timeout;
384 } else if (type == "page load") {
385 session->page_load_timeout =
386 ((timeout < base::TimeDelta()) ? Session::kDefaultPageLoadTimeout
387 : timeout);
388 } else {
389 return Status(kUnknownError, "unknown type of timeout:" + type);
391 return Status(kOk);
394 Status ExecuteSetScriptTimeout(
395 Session* session,
396 const base::DictionaryValue& params,
397 scoped_ptr<base::Value>* value) {
398 double ms;
399 if (!params.GetDouble("ms", &ms) || ms < 0)
400 return Status(kUnknownError, "'ms' must be a non-negative number");
401 session->script_timeout =
402 base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
403 return Status(kOk);
406 Status ExecuteImplicitlyWait(
407 Session* session,
408 const base::DictionaryValue& params,
409 scoped_ptr<base::Value>* value) {
410 double ms;
411 if (!params.GetDouble("ms", &ms) || ms < 0)
412 return Status(kUnknownError, "'ms' must be a non-negative number");
413 session->implicit_wait =
414 base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
415 return Status(kOk);
418 Status ExecuteIsLoading(
419 Session* session,
420 const base::DictionaryValue& params,
421 scoped_ptr<base::Value>* value) {
422 WebView* web_view = NULL;
423 Status status = session->GetTargetWindow(&web_view);
424 if (status.IsError())
425 return status;
427 status = web_view->ConnectIfNecessary();
428 if (status.IsError())
429 return status;
431 bool is_pending;
432 status = web_view->IsPendingNavigation(
433 session->GetCurrentFrameId(), &is_pending);
434 if (status.IsError())
435 return status;
436 value->reset(new base::FundamentalValue(is_pending));
437 return Status(kOk);
440 Status ExecuteGetLocation(
441 Session* session,
442 const base::DictionaryValue& params,
443 scoped_ptr<base::Value>* value) {
444 if (!session->overridden_geoposition) {
445 return Status(kUnknownError,
446 "Location must be set before it can be retrieved");
448 base::DictionaryValue location;
449 location.SetDouble("latitude", session->overridden_geoposition->latitude);
450 location.SetDouble("longitude", session->overridden_geoposition->longitude);
451 location.SetDouble("accuracy", session->overridden_geoposition->accuracy);
452 // Set a dummy altitude to make WebDriver clients happy.
453 // https://code.google.com/p/chromedriver/issues/detail?id=281
454 location.SetDouble("altitude", 0);
455 value->reset(location.DeepCopy());
456 return Status(kOk);
459 Status ExecuteGetWindowPosition(
460 Session* session,
461 const base::DictionaryValue& params,
462 scoped_ptr<base::Value>* value) {
463 ChromeDesktopImpl* desktop = NULL;
464 Status status = session->chrome->GetAsDesktop(&desktop);
465 if (status.IsError())
466 return status;
468 AutomationExtension* extension = NULL;
469 status = desktop->GetAutomationExtension(&extension);
470 if (status.IsError())
471 return status;
473 int x, y;
474 status = extension->GetWindowPosition(&x, &y);
475 if (status.IsError())
476 return status;
478 base::DictionaryValue position;
479 position.SetInteger("x", x);
480 position.SetInteger("y", y);
481 value->reset(position.DeepCopy());
482 return Status(kOk);
485 Status ExecuteSetWindowPosition(
486 Session* session,
487 const base::DictionaryValue& params,
488 scoped_ptr<base::Value>* value) {
489 double x = 0;
490 double y = 0;
491 if (!params.GetDouble("x", &x) || !params.GetDouble("y", &y))
492 return Status(kUnknownError, "missing or invalid 'x' or 'y'");
494 ChromeDesktopImpl* desktop = NULL;
495 Status status = session->chrome->GetAsDesktop(&desktop);
496 if (status.IsError())
497 return status;
499 AutomationExtension* extension = NULL;
500 status = desktop->GetAutomationExtension(&extension);
501 if (status.IsError())
502 return status;
504 return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
507 Status ExecuteGetWindowSize(
508 Session* session,
509 const base::DictionaryValue& params,
510 scoped_ptr<base::Value>* value) {
511 ChromeDesktopImpl* desktop = NULL;
512 Status status = session->chrome->GetAsDesktop(&desktop);
513 if (status.IsError())
514 return status;
516 AutomationExtension* extension = NULL;
517 status = desktop->GetAutomationExtension(&extension);
518 if (status.IsError())
519 return status;
521 int width, height;
522 status = extension->GetWindowSize(&width, &height);
523 if (status.IsError())
524 return status;
526 base::DictionaryValue size;
527 size.SetInteger("width", width);
528 size.SetInteger("height", height);
529 value->reset(size.DeepCopy());
530 return Status(kOk);
533 Status ExecuteSetWindowSize(
534 Session* session,
535 const base::DictionaryValue& params,
536 scoped_ptr<base::Value>* value) {
537 double width = 0;
538 double height = 0;
539 if (!params.GetDouble("width", &width) ||
540 !params.GetDouble("height", &height))
541 return Status(kUnknownError, "missing or invalid 'width' or 'height'");
543 ChromeDesktopImpl* desktop = NULL;
544 Status status = session->chrome->GetAsDesktop(&desktop);
545 if (status.IsError())
546 return status;
548 AutomationExtension* extension = NULL;
549 status = desktop->GetAutomationExtension(&extension);
550 if (status.IsError())
551 return status;
553 return extension->SetWindowSize(
554 static_cast<int>(width), static_cast<int>(height));
557 Status ExecuteMaximizeWindow(
558 Session* session,
559 const base::DictionaryValue& params,
560 scoped_ptr<base::Value>* value) {
561 ChromeDesktopImpl* desktop = NULL;
562 Status status = session->chrome->GetAsDesktop(&desktop);
563 if (status.IsError())
564 return status;
566 AutomationExtension* extension = NULL;
567 status = desktop->GetAutomationExtension(&extension);
568 if (status.IsError())
569 return status;
571 return extension->MaximizeWindow();
574 Status ExecuteGetAvailableLogTypes(
575 Session* session,
576 const base::DictionaryValue& params,
577 scoped_ptr<base::Value>* value) {
578 scoped_ptr<base::ListValue> types(new base::ListValue());
579 std::vector<WebDriverLog*> logs = session->GetAllLogs();
580 for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
581 log != logs.end();
582 ++log) {
583 types->AppendString((*log)->type());
585 *value = types.Pass();
586 return Status(kOk);
589 Status ExecuteGetLog(
590 Session* session,
591 const base::DictionaryValue& params,
592 scoped_ptr<base::Value>* value) {
593 std::string log_type;
594 if (!params.GetString("type", &log_type)) {
595 return Status(kUnknownError, "missing or invalid 'type'");
597 std::vector<WebDriverLog*> logs = session->GetAllLogs();
598 for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
599 log != logs.end();
600 ++log) {
601 if (log_type == (*log)->type()) {
602 *value = (*log)->GetAndClearEntries();
603 return Status(kOk);
606 return Status(kUnknownError, "log type '" + log_type + "' not found");
609 Status ExecuteUploadFile(
610 Session* session,
611 const base::DictionaryValue& params,
612 scoped_ptr<base::Value>* value) {
613 std::string base64_zip_data;
614 if (!params.GetString("file", &base64_zip_data))
615 return Status(kUnknownError, "missing or invalid 'file'");
616 std::string zip_data;
617 if (!Base64Decode(base64_zip_data, &zip_data))
618 return Status(kUnknownError, "unable to decode 'file'");
620 if (!session->temp_dir.IsValid()) {
621 if (!session->temp_dir.CreateUniqueTempDir())
622 return Status(kUnknownError, "unable to create temp dir");
624 base::FilePath upload_dir;
625 if (!base::CreateTemporaryDirInDir(session->temp_dir.path(),
626 FILE_PATH_LITERAL("upload"),
627 &upload_dir)) {
628 return Status(kUnknownError, "unable to create temp dir");
630 std::string error_msg;
631 base::FilePath upload;
632 Status status = UnzipSoleFile(upload_dir, zip_data, &upload);
633 if (status.IsError())
634 return Status(kUnknownError, "unable to unzip 'file'", status);
636 value->reset(new base::StringValue(upload.value()));
637 return Status(kOk);
640 Status ExecuteIsAutoReporting(
641 Session* session,
642 const base::DictionaryValue& params,
643 scoped_ptr<base::Value>* value) {
644 value->reset(new base::FundamentalValue(session->auto_reporting_enabled));
645 return Status(kOk);
648 Status ExecuteSetAutoReporting(
649 Session* session,
650 const base::DictionaryValue& params,
651 scoped_ptr<base::Value>* value) {
652 bool enabled;
653 if (!params.GetBoolean("enabled", &enabled))
654 return Status(kUnknownError, "missing parameter 'enabled'");
655 session->auto_reporting_enabled = enabled;
656 return Status(kOk);