Files.app: Stop to use file system URLs having externalfile:// scheme origin internally.
[chromium-blink-merge.git] / chrome / test / chromedriver / session_commands.cc
blob3eba1b2e093365999fd99661786f78740c8cba82
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/chrome.h"
22 #include "chrome/test/chromedriver/chrome/chrome_android_impl.h"
23 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
24 #include "chrome/test/chromedriver/chrome/device_manager.h"
25 #include "chrome/test/chromedriver/chrome/devtools_event_listener.h"
26 #include "chrome/test/chromedriver/chrome/geoposition.h"
27 #include "chrome/test/chromedriver/chrome/status.h"
28 #include "chrome/test/chromedriver/chrome/version.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());
95 if (chrome->GetAsDesktop()) {
96 chrome_caps->SetString(
97 "userDataDir",
98 chrome->GetAsDesktop()->command().GetSwitchValueNative(
99 "user-data-dir"));
101 caps->Set("chrome", chrome_caps.release());
102 return caps.Pass();
105 Status InitSessionHelper(
106 const InitSessionParams& bound_params,
107 Session* session,
108 const base::DictionaryValue& params,
109 scoped_ptr<base::Value>* value) {
110 session->driver_log.reset(
111 new WebDriverLog(WebDriverLog::kDriverType, Log::kAll));
112 const base::DictionaryValue* desired_caps;
113 if (!params.GetDictionary("desiredCapabilities", &desired_caps))
114 return Status(kUnknownError, "cannot find dict 'desiredCapabilities'");
116 Capabilities capabilities;
117 Status status = capabilities.Parse(*desired_caps);
118 if (status.IsError())
119 return status;
121 Log::Level driver_level = Log::kWarning;
122 if (capabilities.logging_prefs.count(WebDriverLog::kDriverType))
123 driver_level = capabilities.logging_prefs[WebDriverLog::kDriverType];
124 session->driver_log->set_min_level(driver_level);
126 // Create Log's and DevToolsEventListener's for ones that are DevTools-based.
127 // Session will own the Log's, Chrome will own the listeners.
128 // Also create |CommandListener|s for the appropriate logs.
129 ScopedVector<DevToolsEventListener> devtools_event_listeners;
130 ScopedVector<CommandListener> command_listeners;
131 status = CreateLogs(capabilities,
132 session,
133 &session->devtools_logs,
134 &devtools_event_listeners,
135 &command_listeners);
136 if (status.IsError())
137 return status;
139 // |session| will own the |CommandListener|s.
140 session->command_listeners.swap(command_listeners);
142 status = LaunchChrome(bound_params.context_getter.get(),
143 bound_params.socket_factory,
144 bound_params.device_manager,
145 bound_params.port_server,
146 bound_params.port_manager,
147 capabilities,
148 devtools_event_listeners,
149 &session->chrome);
150 if (status.IsError())
151 return status;
153 std::list<std::string> web_view_ids;
154 status = session->chrome->GetWebViewIds(&web_view_ids);
155 if (status.IsError() || web_view_ids.empty()) {
156 return status.IsError() ? status :
157 Status(kUnknownError, "unable to discover open window in chrome");
160 session->window = web_view_ids.front();
161 session->detach = capabilities.detach;
162 session->force_devtools_screenshot = capabilities.force_devtools_screenshot;
163 session->capabilities = CreateCapabilities(session->chrome.get());
164 value->reset(session->capabilities->DeepCopy());
165 return Status(kOk);
168 } // namespace
170 Status ExecuteInitSession(
171 const InitSessionParams& bound_params,
172 Session* session,
173 const base::DictionaryValue& params,
174 scoped_ptr<base::Value>* value) {
175 Status status = InitSessionHelper(bound_params, session, params, value);
176 if (status.IsError()) {
177 session->quit = true;
178 if (session->chrome != NULL)
179 session->chrome->Quit();
181 return status;
184 Status ExecuteQuit(
185 bool allow_detach,
186 Session* session,
187 const base::DictionaryValue& params,
188 scoped_ptr<base::Value>* value) {
189 session->quit = true;
190 if (allow_detach && session->detach)
191 return Status(kOk);
192 else
193 return session->chrome->Quit();
196 Status ExecuteGetSessionCapabilities(
197 Session* session,
198 const base::DictionaryValue& params,
199 scoped_ptr<base::Value>* value) {
200 value->reset(session->capabilities->DeepCopy());
201 return Status(kOk);
204 Status ExecuteGetCurrentWindowHandle(
205 Session* session,
206 const base::DictionaryValue& params,
207 scoped_ptr<base::Value>* value) {
208 WebView* web_view = NULL;
209 Status status = session->GetTargetWindow(&web_view);
210 if (status.IsError())
211 return status;
213 value->reset(
214 new base::StringValue(WebViewIdToWindowHandle(web_view->GetId())));
215 return Status(kOk);
218 Status ExecuteLaunchApp(
219 Session* session,
220 const base::DictionaryValue& params,
221 scoped_ptr<base::Value>* value) {
222 std::string id;
223 if (!params.GetString("id", &id))
224 return Status(kUnknownError, "'id' must be a string");
226 if (!session->chrome->GetAsDesktop())
227 return Status(kUnknownError,
228 "apps can only be launched on desktop platforms");
230 AutomationExtension* extension = NULL;
231 Status status =
232 session->chrome->GetAsDesktop()->GetAutomationExtension(&extension);
233 if (status.IsError())
234 return status;
236 return extension->LaunchApp(id);
239 Status ExecuteClose(
240 Session* session,
241 const base::DictionaryValue& params,
242 scoped_ptr<base::Value>* value) {
243 std::list<std::string> web_view_ids;
244 Status status = session->chrome->GetWebViewIds(&web_view_ids);
245 if (status.IsError())
246 return status;
247 bool is_last_web_view = web_view_ids.size() == 1u;
248 web_view_ids.clear();
250 WebView* web_view = NULL;
251 status = session->GetTargetWindow(&web_view);
252 if (status.IsError())
253 return status;
255 status = session->chrome->CloseWebView(web_view->GetId());
256 if (status.IsError())
257 return status;
259 status = session->chrome->GetWebViewIds(&web_view_ids);
260 if ((status.code() == kChromeNotReachable && is_last_web_view) ||
261 (status.IsOk() && web_view_ids.empty())) {
262 // If no window is open, close is the equivalent of calling "quit".
263 session->quit = true;
264 return session->chrome->Quit();
267 return status;
270 Status ExecuteGetWindowHandles(
271 Session* session,
272 const base::DictionaryValue& params,
273 scoped_ptr<base::Value>* value) {
274 std::list<std::string> web_view_ids;
275 Status status = session->chrome->GetWebViewIds(&web_view_ids);
276 if (status.IsError())
277 return status;
278 scoped_ptr<base::ListValue> window_ids(new base::ListValue());
279 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
280 it != web_view_ids.end(); ++it) {
281 window_ids->AppendString(WebViewIdToWindowHandle(*it));
283 value->reset(window_ids.release());
284 return Status(kOk);
287 Status ExecuteSwitchToWindow(
288 Session* session,
289 const base::DictionaryValue& params,
290 scoped_ptr<base::Value>* value) {
291 std::string name;
292 if (!params.GetString("name", &name) || name.empty())
293 return Status(kUnknownError, "'name' must be a nonempty string");
295 std::list<std::string> web_view_ids;
296 Status status = session->chrome->GetWebViewIds(&web_view_ids);
297 if (status.IsError())
298 return status;
300 std::string web_view_id;
301 bool found = false;
302 if (WindowHandleToWebViewId(name, &web_view_id)) {
303 // Check if any web_view matches |web_view_id|.
304 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
305 it != web_view_ids.end(); ++it) {
306 if (*it == web_view_id) {
307 found = true;
308 break;
311 } else {
312 // Check if any of the tab window names match |name|.
313 const char* kGetWindowNameScript = "function() { return window.name; }";
314 base::ListValue args;
315 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
316 it != web_view_ids.end(); ++it) {
317 scoped_ptr<base::Value> result;
318 WebView* web_view;
319 status = session->chrome->GetWebViewById(*it, &web_view);
320 if (status.IsError())
321 return status;
322 status = web_view->ConnectIfNecessary();
323 if (status.IsError())
324 return status;
325 status = web_view->CallFunction(
326 std::string(), kGetWindowNameScript, args, &result);
327 if (status.IsError())
328 return status;
329 std::string window_name;
330 if (!result->GetAsString(&window_name))
331 return Status(kUnknownError, "failed to get window name");
332 if (window_name == name) {
333 web_view_id = *it;
334 found = true;
335 break;
340 if (!found)
341 return Status(kNoSuchWindow);
343 if (session->overridden_geoposition) {
344 WebView* web_view;
345 status = session->chrome->GetWebViewById(web_view_id, &web_view);
346 if (status.IsError())
347 return status;
348 status = web_view->ConnectIfNecessary();
349 if (status.IsError())
350 return status;
351 status = web_view->OverrideGeolocation(*session->overridden_geoposition);
352 if (status.IsError())
353 return status;
356 session->window = web_view_id;
357 session->SwitchToTopFrame();
358 session->mouse_position = WebPoint(0, 0);
359 return Status(kOk);
362 Status ExecuteSetTimeout(
363 Session* session,
364 const base::DictionaryValue& params,
365 scoped_ptr<base::Value>* value) {
366 double ms_double;
367 if (!params.GetDouble("ms", &ms_double))
368 return Status(kUnknownError, "'ms' must be a double");
369 std::string type;
370 if (!params.GetString("type", &type))
371 return Status(kUnknownError, "'type' must be a string");
373 base::TimeDelta timeout =
374 base::TimeDelta::FromMilliseconds(static_cast<int>(ms_double));
375 // TODO(frankf): implicit and script timeout should be cleared
376 // if negative timeout is specified.
377 if (type == "implicit") {
378 session->implicit_wait = timeout;
379 } else if (type == "script") {
380 session->script_timeout = timeout;
381 } else if (type == "page load") {
382 session->page_load_timeout =
383 ((timeout < base::TimeDelta()) ? Session::kDefaultPageLoadTimeout
384 : timeout);
385 } else {
386 return Status(kUnknownError, "unknown type of timeout:" + type);
388 return Status(kOk);
391 Status ExecuteSetScriptTimeout(
392 Session* session,
393 const base::DictionaryValue& params,
394 scoped_ptr<base::Value>* value) {
395 double ms;
396 if (!params.GetDouble("ms", &ms) || ms < 0)
397 return Status(kUnknownError, "'ms' must be a non-negative number");
398 session->script_timeout =
399 base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
400 return Status(kOk);
403 Status ExecuteImplicitlyWait(
404 Session* session,
405 const base::DictionaryValue& params,
406 scoped_ptr<base::Value>* value) {
407 double ms;
408 if (!params.GetDouble("ms", &ms) || ms < 0)
409 return Status(kUnknownError, "'ms' must be a non-negative number");
410 session->implicit_wait =
411 base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
412 return Status(kOk);
415 Status ExecuteIsLoading(
416 Session* session,
417 const base::DictionaryValue& params,
418 scoped_ptr<base::Value>* value) {
419 WebView* web_view = NULL;
420 Status status = session->GetTargetWindow(&web_view);
421 if (status.IsError())
422 return status;
424 status = web_view->ConnectIfNecessary();
425 if (status.IsError())
426 return status;
428 bool is_pending;
429 status = web_view->IsPendingNavigation(
430 session->GetCurrentFrameId(), &is_pending);
431 if (status.IsError())
432 return status;
433 value->reset(new base::FundamentalValue(is_pending));
434 return Status(kOk);
437 Status ExecuteGetLocation(
438 Session* session,
439 const base::DictionaryValue& params,
440 scoped_ptr<base::Value>* value) {
441 if (!session->overridden_geoposition) {
442 return Status(kUnknownError,
443 "Location must be set before it can be retrieved");
445 base::DictionaryValue location;
446 location.SetDouble("latitude", session->overridden_geoposition->latitude);
447 location.SetDouble("longitude", session->overridden_geoposition->longitude);
448 location.SetDouble("accuracy", session->overridden_geoposition->accuracy);
449 // Set a dummy altitude to make WebDriver clients happy.
450 // https://code.google.com/p/chromedriver/issues/detail?id=281
451 location.SetDouble("altitude", 0);
452 value->reset(location.DeepCopy());
453 return Status(kOk);
456 Status ExecuteGetWindowPosition(
457 Session* session,
458 const base::DictionaryValue& params,
459 scoped_ptr<base::Value>* value) {
460 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
461 if (!desktop) {
462 return Status(
463 kUnknownError,
464 "command only supported for desktop Chrome without debuggerAddress");
467 AutomationExtension* extension = NULL;
468 Status status = desktop->GetAutomationExtension(&extension);
469 if (status.IsError())
470 return status;
472 int x, y;
473 status = extension->GetWindowPosition(&x, &y);
474 if (status.IsError())
475 return status;
477 base::DictionaryValue position;
478 position.SetInteger("x", x);
479 position.SetInteger("y", y);
480 value->reset(position.DeepCopy());
481 return Status(kOk);
484 Status ExecuteSetWindowPosition(
485 Session* session,
486 const base::DictionaryValue& params,
487 scoped_ptr<base::Value>* value) {
488 double x = 0;
489 double y = 0;
490 if (!params.GetDouble("x", &x) || !params.GetDouble("y", &y))
491 return Status(kUnknownError, "missing or invalid 'x' or 'y'");
493 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
494 if (!desktop) {
495 return Status(
496 kUnknownError,
497 "command only supported for desktop Chrome without debuggerAddress");
500 AutomationExtension* extension = NULL;
501 Status status = desktop->GetAutomationExtension(&extension);
502 if (status.IsError())
503 return status;
505 return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
508 Status ExecuteGetWindowSize(
509 Session* session,
510 const base::DictionaryValue& params,
511 scoped_ptr<base::Value>* value) {
512 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
513 if (!desktop) {
514 return Status(
515 kUnknownError,
516 "command only supported for desktop Chrome without debuggerAddress");
519 AutomationExtension* extension = NULL;
520 Status status = desktop->GetAutomationExtension(&extension);
521 if (status.IsError())
522 return status;
524 int width, height;
525 status = extension->GetWindowSize(&width, &height);
526 if (status.IsError())
527 return status;
529 base::DictionaryValue size;
530 size.SetInteger("width", width);
531 size.SetInteger("height", height);
532 value->reset(size.DeepCopy());
533 return Status(kOk);
536 Status ExecuteSetWindowSize(
537 Session* session,
538 const base::DictionaryValue& params,
539 scoped_ptr<base::Value>* value) {
540 double width = 0;
541 double height = 0;
542 if (!params.GetDouble("width", &width) ||
543 !params.GetDouble("height", &height))
544 return Status(kUnknownError, "missing or invalid 'width' or 'height'");
546 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
547 if (!desktop) {
548 return Status(
549 kUnknownError,
550 "command only supported for desktop Chrome without debuggerAddress");
553 AutomationExtension* extension = NULL;
554 Status status = desktop->GetAutomationExtension(&extension);
555 if (status.IsError())
556 return status;
558 return extension->SetWindowSize(
559 static_cast<int>(width), static_cast<int>(height));
562 Status ExecuteMaximizeWindow(
563 Session* session,
564 const base::DictionaryValue& params,
565 scoped_ptr<base::Value>* value) {
566 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
567 if (!desktop) {
568 return Status(
569 kUnknownError,
570 "command only supported for desktop Chrome without debuggerAddress");
573 AutomationExtension* extension = NULL;
574 Status status = desktop->GetAutomationExtension(&extension);
575 if (status.IsError())
576 return status;
578 return extension->MaximizeWindow();
581 Status ExecuteGetAvailableLogTypes(
582 Session* session,
583 const base::DictionaryValue& params,
584 scoped_ptr<base::Value>* value) {
585 scoped_ptr<base::ListValue> types(new base::ListValue());
586 std::vector<WebDriverLog*> logs = session->GetAllLogs();
587 for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
588 log != logs.end();
589 ++log) {
590 types->AppendString((*log)->type());
592 *value = types.Pass();
593 return Status(kOk);
596 Status ExecuteGetLog(
597 Session* session,
598 const base::DictionaryValue& params,
599 scoped_ptr<base::Value>* value) {
600 std::string log_type;
601 if (!params.GetString("type", &log_type)) {
602 return Status(kUnknownError, "missing or invalid 'type'");
604 std::vector<WebDriverLog*> logs = session->GetAllLogs();
605 for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
606 log != logs.end();
607 ++log) {
608 if (log_type == (*log)->type()) {
609 *value = (*log)->GetAndClearEntries();
610 return Status(kOk);
613 return Status(kUnknownError, "log type '" + log_type + "' not found");
616 Status ExecuteUploadFile(
617 Session* session,
618 const base::DictionaryValue& params,
619 scoped_ptr<base::Value>* value) {
620 std::string base64_zip_data;
621 if (!params.GetString("file", &base64_zip_data))
622 return Status(kUnknownError, "missing or invalid 'file'");
623 std::string zip_data;
624 if (!Base64Decode(base64_zip_data, &zip_data))
625 return Status(kUnknownError, "unable to decode 'file'");
627 if (!session->temp_dir.IsValid()) {
628 if (!session->temp_dir.CreateUniqueTempDir())
629 return Status(kUnknownError, "unable to create temp dir");
631 base::FilePath upload_dir;
632 if (!base::CreateTemporaryDirInDir(session->temp_dir.path(),
633 FILE_PATH_LITERAL("upload"),
634 &upload_dir)) {
635 return Status(kUnknownError, "unable to create temp dir");
637 std::string error_msg;
638 base::FilePath upload;
639 Status status = UnzipSoleFile(upload_dir, zip_data, &upload);
640 if (status.IsError())
641 return Status(kUnknownError, "unable to unzip 'file'", status);
643 value->reset(new base::StringValue(upload.value()));
644 return Status(kOk);
647 Status ExecuteIsAutoReporting(
648 Session* session,
649 const base::DictionaryValue& params,
650 scoped_ptr<base::Value>* value) {
651 value->reset(new base::FundamentalValue(session->auto_reporting_enabled));
652 return Status(kOk);
655 Status ExecuteSetAutoReporting(
656 Session* session,
657 const base::DictionaryValue& params,
658 scoped_ptr<base::Value>* value) {
659 bool enabled;
660 if (!params.GetBoolean("enabled", &enabled))
661 return Status(kUnknownError, "missing parameter 'enabled'");
662 session->auto_reporting_enabled = enabled;
663 return Status(kOk);