Enable PPAPI tests for non-SFI mode.
[chromium-blink-merge.git] / chrome / test / chromedriver / session_commands.cc
blobb9c25aea30036efbf070cb6c30113afb4d2ff693
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/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/web_view.h"
29 #include "chrome/test/chromedriver/chrome_launcher.h"
30 #include "chrome/test/chromedriver/logging.h"
31 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
32 #include "chrome/test/chromedriver/session.h"
33 #include "chrome/test/chromedriver/util.h"
34 #include "chrome/test/chromedriver/version.h"
36 namespace {
38 const char kWindowHandlePrefix[] = "CDwindow-";
40 std::string WebViewIdToWindowHandle(const std::string& web_view_id) {
41 return kWindowHandlePrefix + web_view_id;
44 bool WindowHandleToWebViewId(const std::string& window_handle,
45 std::string* web_view_id) {
46 if (window_handle.find(kWindowHandlePrefix) != 0u)
47 return false;
48 *web_view_id = window_handle.substr(
49 std::string(kWindowHandlePrefix).length());
50 return true;
53 } // namespace
55 InitSessionParams::InitSessionParams(
56 scoped_refptr<URLRequestContextGetter> context_getter,
57 const SyncWebSocketFactory& socket_factory,
58 DeviceManager* device_manager,
59 PortServer* port_server,
60 PortManager* port_manager)
61 : context_getter(context_getter),
62 socket_factory(socket_factory),
63 device_manager(device_manager),
64 port_server(port_server),
65 port_manager(port_manager) {}
67 InitSessionParams::~InitSessionParams() {}
69 namespace {
71 scoped_ptr<base::DictionaryValue> CreateCapabilities(Chrome* chrome) {
72 scoped_ptr<base::DictionaryValue> caps(new base::DictionaryValue());
73 caps->SetString("browserName", "chrome");
74 caps->SetString("version", chrome->GetVersion());
75 caps->SetString("chrome.chromedriverVersion", kChromeDriverVersion);
76 caps->SetString("platform", chrome->GetOperatingSystemName());
77 caps->SetBoolean("javascriptEnabled", true);
78 caps->SetBoolean("takesScreenshot", true);
79 caps->SetBoolean("takesHeapSnapshot", true);
80 caps->SetBoolean("handlesAlerts", true);
81 caps->SetBoolean("databaseEnabled", false);
82 caps->SetBoolean("locationContextEnabled", true);
83 caps->SetBoolean("applicationCacheEnabled", false);
84 caps->SetBoolean("browserConnectionEnabled", false);
85 caps->SetBoolean("cssSelectorsEnabled", true);
86 caps->SetBoolean("webStorageEnabled", true);
87 caps->SetBoolean("rotatable", false);
88 caps->SetBoolean("acceptSslCerts", true);
89 caps->SetBoolean("nativeEvents", true);
90 scoped_ptr<base::DictionaryValue> chrome_caps(new base::DictionaryValue());
91 if (chrome->GetAsDesktop()) {
92 chrome_caps->SetString(
93 "userDataDir",
94 chrome->GetAsDesktop()->command().GetSwitchValueNative(
95 "user-data-dir"));
97 caps->Set("chrome", chrome_caps.release());
98 return caps.Pass();
102 Status InitSessionHelper(
103 const InitSessionParams& bound_params,
104 Session* session,
105 const base::DictionaryValue& params,
106 scoped_ptr<base::Value>* value) {
107 session->driver_log.reset(
108 new WebDriverLog(WebDriverLog::kDriverType, Log::kAll));
109 const base::DictionaryValue* desired_caps;
110 if (!params.GetDictionary("desiredCapabilities", &desired_caps))
111 return Status(kUnknownError, "cannot find dict 'desiredCapabilities'");
113 Capabilities capabilities;
114 Status status = capabilities.Parse(*desired_caps);
115 if (status.IsError())
116 return status;
118 Log::Level driver_level = Log::kWarning;
119 if (capabilities.logging_prefs.count(WebDriverLog::kDriverType))
120 driver_level = capabilities.logging_prefs[WebDriverLog::kDriverType];
121 session->driver_log->set_min_level(driver_level);
123 // Create Log's and DevToolsEventListener's for ones that are DevTools-based.
124 // Session will own the Log's, Chrome will own the listeners.
125 ScopedVector<DevToolsEventListener> devtools_event_listeners;
126 status = CreateLogs(capabilities,
127 &session->devtools_logs,
128 &devtools_event_listeners);
129 if (status.IsError())
130 return status;
132 status = LaunchChrome(bound_params.context_getter.get(),
133 bound_params.socket_factory,
134 bound_params.device_manager,
135 bound_params.port_server,
136 bound_params.port_manager,
137 capabilities,
138 devtools_event_listeners,
139 &session->chrome);
140 if (status.IsError())
141 return status;
143 std::list<std::string> web_view_ids;
144 status = session->chrome->GetWebViewIds(&web_view_ids);
145 if (status.IsError() || web_view_ids.empty()) {
146 return status.IsError() ? status :
147 Status(kUnknownError, "unable to discover open window in chrome");
150 session->window = web_view_ids.front();
151 session->detach = capabilities.detach;
152 session->force_devtools_screenshot = capabilities.force_devtools_screenshot;
153 session->capabilities = CreateCapabilities(session->chrome.get());
154 value->reset(session->capabilities->DeepCopy());
155 return Status(kOk);
158 } // namespace
160 Status ExecuteInitSession(
161 const InitSessionParams& bound_params,
162 Session* session,
163 const base::DictionaryValue& params,
164 scoped_ptr<base::Value>* value) {
165 Status status = InitSessionHelper(bound_params, session, params, value);
166 if (status.IsError()) {
167 session->quit = true;
168 if (session->chrome != NULL)
169 session->chrome->Quit();
171 return status;
174 Status ExecuteQuit(
175 bool allow_detach,
176 Session* session,
177 const base::DictionaryValue& params,
178 scoped_ptr<base::Value>* value) {
179 session->quit = true;
180 if (allow_detach && session->detach)
181 return Status(kOk);
182 else
183 return session->chrome->Quit();
186 Status ExecuteGetSessionCapabilities(
187 Session* session,
188 const base::DictionaryValue& params,
189 scoped_ptr<base::Value>* value) {
190 value->reset(session->capabilities->DeepCopy());
191 return Status(kOk);
194 Status ExecuteGetCurrentWindowHandle(
195 Session* session,
196 const base::DictionaryValue& params,
197 scoped_ptr<base::Value>* value) {
198 WebView* web_view = NULL;
199 Status status = session->GetTargetWindow(&web_view);
200 if (status.IsError())
201 return status;
203 value->reset(
204 new base::StringValue(WebViewIdToWindowHandle(web_view->GetId())));
205 return Status(kOk);
208 Status ExecuteLaunchApp(
209 Session* session,
210 const base::DictionaryValue& params,
211 scoped_ptr<base::Value>* value) {
212 std::string id;
213 if (!params.GetString("id", &id))
214 return Status(kUnknownError, "'id' must be a string");
216 if (!session->chrome->GetAsDesktop())
217 return Status(kUnknownError,
218 "apps can only be launched on desktop platforms");
220 AutomationExtension* extension = NULL;
221 Status status =
222 session->chrome->GetAsDesktop()->GetAutomationExtension(&extension);
223 if (status.IsError())
224 return status;
226 return extension->LaunchApp(id);
229 Status ExecuteClose(
230 Session* session,
231 const base::DictionaryValue& params,
232 scoped_ptr<base::Value>* value) {
233 std::list<std::string> web_view_ids;
234 Status status = session->chrome->GetWebViewIds(&web_view_ids);
235 if (status.IsError())
236 return status;
237 bool is_last_web_view = web_view_ids.size() == 1u;
238 web_view_ids.clear();
240 WebView* web_view = NULL;
241 status = session->GetTargetWindow(&web_view);
242 if (status.IsError())
243 return status;
245 status = session->chrome->CloseWebView(web_view->GetId());
246 if (status.IsError())
247 return status;
249 status = session->chrome->GetWebViewIds(&web_view_ids);
250 if ((status.code() == kChromeNotReachable && is_last_web_view) ||
251 (status.IsOk() && web_view_ids.empty())) {
252 // If no window is open, close is the equivalent of calling "quit".
253 session->quit = true;
254 return session->chrome->Quit();
257 return status;
260 Status ExecuteGetWindowHandles(
261 Session* session,
262 const base::DictionaryValue& params,
263 scoped_ptr<base::Value>* value) {
264 std::list<std::string> web_view_ids;
265 Status status = session->chrome->GetWebViewIds(&web_view_ids);
266 if (status.IsError())
267 return status;
268 scoped_ptr<base::ListValue> window_ids(new base::ListValue());
269 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
270 it != web_view_ids.end(); ++it) {
271 window_ids->AppendString(WebViewIdToWindowHandle(*it));
273 value->reset(window_ids.release());
274 return Status(kOk);
277 Status ExecuteSwitchToWindow(
278 Session* session,
279 const base::DictionaryValue& params,
280 scoped_ptr<base::Value>* value) {
281 std::string name;
282 if (!params.GetString("name", &name) || name.empty())
283 return Status(kUnknownError, "'name' must be a nonempty string");
285 std::list<std::string> web_view_ids;
286 Status status = session->chrome->GetWebViewIds(&web_view_ids);
287 if (status.IsError())
288 return status;
290 std::string web_view_id;
291 bool found = false;
292 if (WindowHandleToWebViewId(name, &web_view_id)) {
293 // Check if any web_view matches |web_view_id|.
294 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
295 it != web_view_ids.end(); ++it) {
296 if (*it == web_view_id) {
297 found = true;
298 break;
301 } else {
302 // Check if any of the tab window names match |name|.
303 const char* kGetWindowNameScript = "function() { return window.name; }";
304 base::ListValue args;
305 for (std::list<std::string>::const_iterator it = web_view_ids.begin();
306 it != web_view_ids.end(); ++it) {
307 scoped_ptr<base::Value> result;
308 WebView* web_view;
309 status = session->chrome->GetWebViewById(*it, &web_view);
310 if (status.IsError())
311 return status;
312 status = web_view->ConnectIfNecessary();
313 if (status.IsError())
314 return status;
315 status = web_view->CallFunction(
316 std::string(), kGetWindowNameScript, args, &result);
317 if (status.IsError())
318 return status;
319 std::string window_name;
320 if (!result->GetAsString(&window_name))
321 return Status(kUnknownError, "failed to get window name");
322 if (window_name == name) {
323 web_view_id = *it;
324 found = true;
325 break;
330 if (!found)
331 return Status(kNoSuchWindow);
333 if (session->overridden_geoposition) {
334 WebView* web_view;
335 status = session->chrome->GetWebViewById(web_view_id, &web_view);
336 if (status.IsError())
337 return status;
338 status = web_view->ConnectIfNecessary();
339 if (status.IsError())
340 return status;
341 status = web_view->OverrideGeolocation(*session->overridden_geoposition);
342 if (status.IsError())
343 return status;
346 session->window = web_view_id;
347 session->SwitchToTopFrame();
348 session->mouse_position = WebPoint(0, 0);
349 return Status(kOk);
352 Status ExecuteSetTimeout(
353 Session* session,
354 const base::DictionaryValue& params,
355 scoped_ptr<base::Value>* value) {
356 double ms_double;
357 if (!params.GetDouble("ms", &ms_double))
358 return Status(kUnknownError, "'ms' must be a double");
359 std::string type;
360 if (!params.GetString("type", &type))
361 return Status(kUnknownError, "'type' must be a string");
363 base::TimeDelta timeout =
364 base::TimeDelta::FromMilliseconds(static_cast<int>(ms_double));
365 // TODO(frankf): implicit and script timeout should be cleared
366 // if negative timeout is specified.
367 if (type == "implicit") {
368 session->implicit_wait = timeout;
369 } else if (type == "script") {
370 session->script_timeout = timeout;
371 } else if (type == "page load") {
372 session->page_load_timeout =
373 ((timeout < base::TimeDelta()) ? Session::kDefaultPageLoadTimeout
374 : timeout);
375 } else {
376 return Status(kUnknownError, "unknown type of timeout:" + type);
378 return Status(kOk);
381 Status ExecuteSetScriptTimeout(
382 Session* session,
383 const base::DictionaryValue& params,
384 scoped_ptr<base::Value>* value) {
385 double ms;
386 if (!params.GetDouble("ms", &ms) || ms < 0)
387 return Status(kUnknownError, "'ms' must be a non-negative number");
388 session->script_timeout =
389 base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
390 return Status(kOk);
393 Status ExecuteImplicitlyWait(
394 Session* session,
395 const base::DictionaryValue& params,
396 scoped_ptr<base::Value>* value) {
397 double ms;
398 if (!params.GetDouble("ms", &ms) || ms < 0)
399 return Status(kUnknownError, "'ms' must be a non-negative number");
400 session->implicit_wait =
401 base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
402 return Status(kOk);
405 Status ExecuteIsLoading(
406 Session* session,
407 const base::DictionaryValue& params,
408 scoped_ptr<base::Value>* value) {
409 WebView* web_view = NULL;
410 Status status = session->GetTargetWindow(&web_view);
411 if (status.IsError())
412 return status;
414 status = web_view->ConnectIfNecessary();
415 if (status.IsError())
416 return status;
418 bool is_pending;
419 status = web_view->IsPendingNavigation(
420 session->GetCurrentFrameId(), &is_pending);
421 if (status.IsError())
422 return status;
423 value->reset(new base::FundamentalValue(is_pending));
424 return Status(kOk);
427 Status ExecuteGetLocation(
428 Session* session,
429 const base::DictionaryValue& params,
430 scoped_ptr<base::Value>* value) {
431 if (!session->overridden_geoposition) {
432 return Status(kUnknownError,
433 "Location must be set before it can be retrieved");
435 base::DictionaryValue location;
436 location.SetDouble("latitude", session->overridden_geoposition->latitude);
437 location.SetDouble("longitude", session->overridden_geoposition->longitude);
438 location.SetDouble("accuracy", session->overridden_geoposition->accuracy);
439 // Set a dummy altitude to make WebDriver clients happy.
440 // https://code.google.com/p/chromedriver/issues/detail?id=281
441 location.SetDouble("altitude", 0);
442 value->reset(location.DeepCopy());
443 return Status(kOk);
446 Status ExecuteGetWindowPosition(
447 Session* session,
448 const base::DictionaryValue& params,
449 scoped_ptr<base::Value>* value) {
450 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
451 if (!desktop) {
452 return Status(
453 kUnknownError,
454 "command only supported for desktop Chrome without debuggerAddress");
457 AutomationExtension* extension = NULL;
458 Status status = desktop->GetAutomationExtension(&extension);
459 if (status.IsError())
460 return status;
462 int x, y;
463 status = extension->GetWindowPosition(&x, &y);
464 if (status.IsError())
465 return status;
467 base::DictionaryValue position;
468 position.SetInteger("x", x);
469 position.SetInteger("y", y);
470 value->reset(position.DeepCopy());
471 return Status(kOk);
474 Status ExecuteSetWindowPosition(
475 Session* session,
476 const base::DictionaryValue& params,
477 scoped_ptr<base::Value>* value) {
478 double x, y;
479 if (!params.GetDouble("x", &x) || !params.GetDouble("y", &y))
480 return Status(kUnknownError, "missing or invalid 'x' or 'y'");
482 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
483 if (!desktop) {
484 return Status(
485 kUnknownError,
486 "command only supported for desktop Chrome without debuggerAddress");
489 AutomationExtension* extension = NULL;
490 Status status = desktop->GetAutomationExtension(&extension);
491 if (status.IsError())
492 return status;
494 return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
497 Status ExecuteGetWindowSize(
498 Session* session,
499 const base::DictionaryValue& params,
500 scoped_ptr<base::Value>* value) {
501 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
502 if (!desktop) {
503 return Status(
504 kUnknownError,
505 "command only supported for desktop Chrome without debuggerAddress");
508 AutomationExtension* extension = NULL;
509 Status status = desktop->GetAutomationExtension(&extension);
510 if (status.IsError())
511 return status;
513 int width, height;
514 status = extension->GetWindowSize(&width, &height);
515 if (status.IsError())
516 return status;
518 base::DictionaryValue size;
519 size.SetInteger("width", width);
520 size.SetInteger("height", height);
521 value->reset(size.DeepCopy());
522 return Status(kOk);
525 Status ExecuteSetWindowSize(
526 Session* session,
527 const base::DictionaryValue& params,
528 scoped_ptr<base::Value>* value) {
529 double width, height;
530 if (!params.GetDouble("width", &width) ||
531 !params.GetDouble("height", &height))
532 return Status(kUnknownError, "missing or invalid 'width' or 'height'");
534 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
535 if (!desktop) {
536 return Status(
537 kUnknownError,
538 "command only supported for desktop Chrome without debuggerAddress");
541 AutomationExtension* extension = NULL;
542 Status status = desktop->GetAutomationExtension(&extension);
543 if (status.IsError())
544 return status;
546 return extension->SetWindowSize(
547 static_cast<int>(width), static_cast<int>(height));
550 Status ExecuteMaximizeWindow(
551 Session* session,
552 const base::DictionaryValue& params,
553 scoped_ptr<base::Value>* value) {
554 ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
555 if (!desktop) {
556 return Status(
557 kUnknownError,
558 "command only supported for desktop Chrome without debuggerAddress");
561 AutomationExtension* extension = NULL;
562 Status status = desktop->GetAutomationExtension(&extension);
563 if (status.IsError())
564 return status;
566 return extension->MaximizeWindow();
569 Status ExecuteGetAvailableLogTypes(
570 Session* session,
571 const base::DictionaryValue& params,
572 scoped_ptr<base::Value>* value) {
573 scoped_ptr<base::ListValue> types(new base::ListValue());
574 std::vector<WebDriverLog*> logs = session->GetAllLogs();
575 for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
576 log != logs.end();
577 ++log) {
578 types->AppendString((*log)->type());
580 *value = types.Pass();
581 return Status(kOk);
584 Status ExecuteGetLog(
585 Session* session,
586 const base::DictionaryValue& params,
587 scoped_ptr<base::Value>* value) {
588 std::string log_type;
589 if (!params.GetString("type", &log_type)) {
590 return Status(kUnknownError, "missing or invalid 'type'");
592 std::vector<WebDriverLog*> logs = session->GetAllLogs();
593 for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
594 log != logs.end();
595 ++log) {
596 if (log_type == (*log)->type()) {
597 *value = (*log)->GetAndClearEntries();
598 return Status(kOk);
601 return Status(kUnknownError, "log type '" + log_type + "' not found");
604 Status ExecuteUploadFile(
605 Session* session,
606 const base::DictionaryValue& params,
607 scoped_ptr<base::Value>* value) {
608 std::string base64_zip_data;
609 if (!params.GetString("file", &base64_zip_data))
610 return Status(kUnknownError, "missing or invalid 'file'");
611 std::string zip_data;
612 if (!Base64Decode(base64_zip_data, &zip_data))
613 return Status(kUnknownError, "unable to decode 'file'");
615 if (!session->temp_dir.IsValid()) {
616 if (!session->temp_dir.CreateUniqueTempDir())
617 return Status(kUnknownError, "unable to create temp dir");
619 base::FilePath upload_dir;
620 if (!base::CreateTemporaryDirInDir(session->temp_dir.path(),
621 FILE_PATH_LITERAL("upload"),
622 &upload_dir)) {
623 return Status(kUnknownError, "unable to create temp dir");
625 std::string error_msg;
626 base::FilePath upload;
627 Status status = UnzipSoleFile(upload_dir, zip_data, &upload);
628 if (status.IsError())
629 return Status(kUnknownError, "unable to unzip 'file'", status);
631 value->reset(new base::StringValue(upload.value()));
632 return Status(kOk);
635 Status ExecuteIsAutoReporting(
636 Session* session,
637 const base::DictionaryValue& params,
638 scoped_ptr<base::Value>* value) {
639 value->reset(new base::FundamentalValue(session->auto_reporting_enabled));
640 return Status(kOk);
643 Status ExecuteSetAutoReporting(
644 Session* session,
645 const base::DictionaryValue& params,
646 scoped_ptr<base::Value>* value) {
647 bool enabled;
648 if (!params.GetBoolean("enabled", &enabled))
649 return Status(kUnknownError, "missing parameter 'enabled'");
650 session->auto_reporting_enabled = enabled;
651 return Status(kOk);