Upstream version 5.34.104.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/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"
35
36 namespace {
37
38 const char kWindowHandlePrefix[] = "CDwindow-";
39
40 std::string WebViewIdToWindowHandle(const std::string& web_view_id) {
41   return kWindowHandlePrefix + web_view_id;
42 }
43
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;
51 }
52
53 }  // namespace
54
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) {}
66
67 InitSessionParams::~InitSessionParams() {}
68
69 namespace {
70
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"));
96   }
97   caps->Set("chrome", chrome_caps.release());
98   return caps.Pass();
99 }
100
101
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'");
112
113   Capabilities capabilities;
114   Status status = capabilities.Parse(*desired_caps);
115   if (status.IsError())
116     return status;
117
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);
122
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;
131
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;
142
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");
148   }
149
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);
156 }
157
158 }  // namespace
159
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();
170   }
171   return status;
172 }
173
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();
184 }
185
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);
192 }
193
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;
202
203   value->reset(
204       new base::StringValue(WebViewIdToWindowHandle(web_view->GetId())));
205   return Status(kOk);
206 }
207
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");
215
216   if (!session->chrome->GetAsDesktop())
217     return Status(kUnknownError,
218                   "apps can only be launched on desktop platforms");
219
220   AutomationExtension* extension = NULL;
221   Status status =
222       session->chrome->GetAsDesktop()->GetAutomationExtension(&extension);
223   if (status.IsError())
224     return status;
225
226   return extension->LaunchApp(id);
227 }
228
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();
239
240   WebView* web_view = NULL;
241   status = session->GetTargetWindow(&web_view);
242   if (status.IsError())
243     return status;
244
245   status = session->chrome->CloseWebView(web_view->GetId());
246   if (status.IsError())
247     return status;
248
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();
255   }
256
257   return status;
258 }
259
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));
272   }
273   value->reset(window_ids.release());
274   return Status(kOk);
275 }
276
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");
284
285   std::list<std::string> web_view_ids;
286   Status status = session->chrome->GetWebViewIds(&web_view_ids);
287   if (status.IsError())
288     return status;
289
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;
299       }
300     }
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;
326       }
327     }
328   }
329
330   if (!found)
331     return Status(kNoSuchWindow);
332
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;
344   }
345
346   session->window = web_view_id;
347   session->SwitchToTopFrame();
348   session->mouse_position = WebPoint(0, 0);
349   return Status(kOk);
350 }
351
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");
362
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);
377   }
378   return Status(kOk);
379 }
380
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);
391 }
392
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);
403 }
404
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;
413
414   status = web_view->ConnectIfNecessary();
415   if (status.IsError())
416     return status;
417
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);
425 }
426
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");
434   }
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);
444 }
445
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");
455   }
456
457   AutomationExtension* extension = NULL;
458   Status status = desktop->GetAutomationExtension(&extension);
459   if (status.IsError())
460     return status;
461
462   int x, y;
463   status = extension->GetWindowPosition(&x, &y);
464   if (status.IsError())
465     return status;
466
467   base::DictionaryValue position;
468   position.SetInteger("x", x);
469   position.SetInteger("y", y);
470   value->reset(position.DeepCopy());
471   return Status(kOk);
472 }
473
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'");
481
482   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
483   if (!desktop) {
484     return Status(
485         kUnknownError,
486         "command only supported for desktop Chrome without debuggerAddress");
487   }
488
489   AutomationExtension* extension = NULL;
490   Status status = desktop->GetAutomationExtension(&extension);
491   if (status.IsError())
492     return status;
493
494   return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
495 }
496
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");
506   }
507
508   AutomationExtension* extension = NULL;
509   Status status = desktop->GetAutomationExtension(&extension);
510   if (status.IsError())
511     return status;
512
513   int width, height;
514   status = extension->GetWindowSize(&width, &height);
515   if (status.IsError())
516     return status;
517
518   base::DictionaryValue size;
519   size.SetInteger("width", width);
520   size.SetInteger("height", height);
521   value->reset(size.DeepCopy());
522   return Status(kOk);
523 }
524
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'");
533
534   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
535   if (!desktop) {
536     return Status(
537         kUnknownError,
538         "command only supported for desktop Chrome without debuggerAddress");
539   }
540
541   AutomationExtension* extension = NULL;
542   Status status = desktop->GetAutomationExtension(&extension);
543   if (status.IsError())
544     return status;
545
546   return extension->SetWindowSize(
547       static_cast<int>(width), static_cast<int>(height));
548 }
549
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");
559   }
560
561   AutomationExtension* extension = NULL;
562   Status status = desktop->GetAutomationExtension(&extension);
563   if (status.IsError())
564     return status;
565
566   return extension->MaximizeWindow();
567 }
568
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());
579   }
580   *value = types.Pass();
581   return Status(kOk);
582 }
583
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'");
591   }
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);
599     }
600   }
601   return Status(kUnknownError, "log type '" + log_type + "' not found");
602 }
603
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'");
614
615   if (!session->temp_dir.IsValid()) {
616     if (!session->temp_dir.CreateUniqueTempDir())
617       return Status(kUnknownError, "unable to create temp dir");
618   }
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");
624   }
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);
630
631   value->reset(new base::StringValue(upload.value()));
632   return Status(kOk);
633 }
634
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);
641 }
642
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);
652 }