Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / test / chromedriver / session_commands.cc
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.
4
5 #include "chrome/test/chromedriver/session_commands.h"
6
7 #include <list>
8
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"
37
38 namespace {
39
40 const char kWindowHandlePrefix[] = "CDwindow-";
41
42 std::string WebViewIdToWindowHandle(const std::string& web_view_id) {
43   return kWindowHandlePrefix + web_view_id;
44 }
45
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;
53 }
54
55 }  // namespace
56
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) {}
68
69 InitSessionParams::~InitSessionParams() {}
70
71 namespace {
72
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"));
100   }
101   caps->Set("chrome", chrome_caps.release());
102   return caps.Pass();
103 }
104
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'");
115
116   Capabilities capabilities;
117   Status status = capabilities.Parse(*desired_caps);
118   if (status.IsError())
119     return status;
120
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);
125
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;
138
139   // |session| will own the |CommandListener|s.
140   session->command_listeners.swap(command_listeners);
141
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;
152
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");
158   }
159
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);
166 }
167
168 }  // namespace
169
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();
180   }
181   return status;
182 }
183
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();
194 }
195
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);
202 }
203
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;
212
213   value->reset(
214       new base::StringValue(WebViewIdToWindowHandle(web_view->GetId())));
215   return Status(kOk);
216 }
217
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");
225
226   if (!session->chrome->GetAsDesktop())
227     return Status(kUnknownError,
228                   "apps can only be launched on desktop platforms");
229
230   AutomationExtension* extension = NULL;
231   Status status =
232       session->chrome->GetAsDesktop()->GetAutomationExtension(&extension);
233   if (status.IsError())
234     return status;
235
236   return extension->LaunchApp(id);
237 }
238
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();
249
250   WebView* web_view = NULL;
251   status = session->GetTargetWindow(&web_view);
252   if (status.IsError())
253     return status;
254
255   status = session->chrome->CloseWebView(web_view->GetId());
256   if (status.IsError())
257     return status;
258
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();
265   }
266
267   return status;
268 }
269
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));
282   }
283   value->reset(window_ids.release());
284   return Status(kOk);
285 }
286
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");
294
295   std::list<std::string> web_view_ids;
296   Status status = session->chrome->GetWebViewIds(&web_view_ids);
297   if (status.IsError())
298     return status;
299
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;
309       }
310     }
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;
336       }
337     }
338   }
339
340   if (!found)
341     return Status(kNoSuchWindow);
342
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;
354   }
355
356   session->window = web_view_id;
357   session->SwitchToTopFrame();
358   session->mouse_position = WebPoint(0, 0);
359   return Status(kOk);
360 }
361
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");
372
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);
387   }
388   return Status(kOk);
389 }
390
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);
401 }
402
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);
413 }
414
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;
423
424   status = web_view->ConnectIfNecessary();
425   if (status.IsError())
426     return status;
427
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);
435 }
436
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");
444   }
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);
454 }
455
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");
465   }
466
467   AutomationExtension* extension = NULL;
468   Status status = desktop->GetAutomationExtension(&extension);
469   if (status.IsError())
470     return status;
471
472   int x, y;
473   status = extension->GetWindowPosition(&x, &y);
474   if (status.IsError())
475     return status;
476
477   base::DictionaryValue position;
478   position.SetInteger("x", x);
479   position.SetInteger("y", y);
480   value->reset(position.DeepCopy());
481   return Status(kOk);
482 }
483
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'");
492
493   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
494   if (!desktop) {
495     return Status(
496         kUnknownError,
497         "command only supported for desktop Chrome without debuggerAddress");
498   }
499
500   AutomationExtension* extension = NULL;
501   Status status = desktop->GetAutomationExtension(&extension);
502   if (status.IsError())
503     return status;
504
505   return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
506 }
507
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");
517   }
518
519   AutomationExtension* extension = NULL;
520   Status status = desktop->GetAutomationExtension(&extension);
521   if (status.IsError())
522     return status;
523
524   int width, height;
525   status = extension->GetWindowSize(&width, &height);
526   if (status.IsError())
527     return status;
528
529   base::DictionaryValue size;
530   size.SetInteger("width", width);
531   size.SetInteger("height", height);
532   value->reset(size.DeepCopy());
533   return Status(kOk);
534 }
535
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'");
545
546   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
547   if (!desktop) {
548     return Status(
549         kUnknownError,
550         "command only supported for desktop Chrome without debuggerAddress");
551   }
552
553   AutomationExtension* extension = NULL;
554   Status status = desktop->GetAutomationExtension(&extension);
555   if (status.IsError())
556     return status;
557
558   return extension->SetWindowSize(
559       static_cast<int>(width), static_cast<int>(height));
560 }
561
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");
571   }
572
573   AutomationExtension* extension = NULL;
574   Status status = desktop->GetAutomationExtension(&extension);
575   if (status.IsError())
576     return status;
577
578   return extension->MaximizeWindow();
579 }
580
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());
591   }
592   *value = types.Pass();
593   return Status(kOk);
594 }
595
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'");
603   }
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);
611     }
612   }
613   return Status(kUnknownError, "log type '" + log_type + "' not found");
614 }
615
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'");
626
627   if (!session->temp_dir.IsValid()) {
628     if (!session->temp_dir.CreateUniqueTempDir())
629       return Status(kUnknownError, "unable to create temp dir");
630   }
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");
636   }
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);
642
643   value->reset(new base::StringValue(upload.value()));
644   return Status(kOk);
645 }
646
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);
653 }
654
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);
664 }