- add sources.
[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   return status;
169 }
170
171 Status ExecuteQuit(
172     bool allow_detach,
173     Session* session,
174     const base::DictionaryValue& params,
175     scoped_ptr<base::Value>* value) {
176   session->quit = true;
177   if (allow_detach && session->detach)
178     return Status(kOk);
179   else
180     return session->chrome->Quit();
181 }
182
183 Status ExecuteGetSessionCapabilities(
184     Session* session,
185     const base::DictionaryValue& params,
186     scoped_ptr<base::Value>* value) {
187   value->reset(session->capabilities->DeepCopy());
188   return Status(kOk);
189 }
190
191 Status ExecuteGetCurrentWindowHandle(
192     Session* session,
193     const base::DictionaryValue& params,
194     scoped_ptr<base::Value>* value) {
195   WebView* web_view = NULL;
196   Status status = session->GetTargetWindow(&web_view);
197   if (status.IsError())
198     return status;
199
200   value->reset(new StringValue(WebViewIdToWindowHandle(web_view->GetId())));
201   return Status(kOk);
202 }
203
204 Status ExecuteClose(
205     Session* session,
206     const base::DictionaryValue& params,
207     scoped_ptr<base::Value>* value) {
208   std::list<std::string> web_view_ids;
209   Status status = session->chrome->GetWebViewIds(&web_view_ids);
210   if (status.IsError())
211     return status;
212   bool is_last_web_view = web_view_ids.size() == 1u;
213   web_view_ids.clear();
214
215   WebView* web_view = NULL;
216   status = session->GetTargetWindow(&web_view);
217   if (status.IsError())
218     return status;
219
220   status = session->chrome->CloseWebView(web_view->GetId());
221   if (status.IsError())
222     return status;
223
224   status = session->chrome->GetWebViewIds(&web_view_ids);
225   if ((status.code() == kChromeNotReachable && is_last_web_view) ||
226       (status.IsOk() && web_view_ids.empty())) {
227     // If no window is open, close is the equivalent of calling "quit".
228     session->quit = true;
229     return session->chrome->Quit();
230   }
231
232   return status;
233 }
234
235 Status ExecuteGetWindowHandles(
236     Session* session,
237     const base::DictionaryValue& params,
238     scoped_ptr<base::Value>* value) {
239   std::list<std::string> web_view_ids;
240   Status status = session->chrome->GetWebViewIds(&web_view_ids);
241   if (status.IsError())
242     return status;
243   scoped_ptr<base::ListValue> window_ids(new base::ListValue());
244   for (std::list<std::string>::const_iterator it = web_view_ids.begin();
245        it != web_view_ids.end(); ++it) {
246     window_ids->AppendString(WebViewIdToWindowHandle(*it));
247   }
248   value->reset(window_ids.release());
249   return Status(kOk);
250 }
251
252 Status ExecuteSwitchToWindow(
253     Session* session,
254     const base::DictionaryValue& params,
255     scoped_ptr<base::Value>* value) {
256   std::string name;
257   if (!params.GetString("name", &name) || name.empty())
258     return Status(kUnknownError, "'name' must be a nonempty string");
259
260   std::list<std::string> web_view_ids;
261   Status status = session->chrome->GetWebViewIds(&web_view_ids);
262   if (status.IsError())
263     return status;
264
265   std::string web_view_id;
266   bool found = false;
267   if (WindowHandleToWebViewId(name, &web_view_id)) {
268     // Check if any web_view matches |web_view_id|.
269     for (std::list<std::string>::const_iterator it = web_view_ids.begin();
270          it != web_view_ids.end(); ++it) {
271       if (*it == web_view_id) {
272         found = true;
273         break;
274       }
275     }
276   } else {
277     // Check if any of the tab window names match |name|.
278     const char* kGetWindowNameScript = "function() { return window.name; }";
279     base::ListValue args;
280     for (std::list<std::string>::const_iterator it = web_view_ids.begin();
281          it != web_view_ids.end(); ++it) {
282       scoped_ptr<base::Value> result;
283       WebView* web_view;
284       status = session->chrome->GetWebViewById(*it, &web_view);
285       if (status.IsError())
286         return status;
287       status = web_view->ConnectIfNecessary();
288       if (status.IsError())
289         return status;
290       status = web_view->CallFunction(
291           std::string(), kGetWindowNameScript, args, &result);
292       if (status.IsError())
293         return status;
294       std::string window_name;
295       if (!result->GetAsString(&window_name))
296         return Status(kUnknownError, "failed to get window name");
297       if (window_name == name) {
298         web_view_id = *it;
299         found = true;
300         break;
301       }
302     }
303   }
304
305   if (!found)
306     return Status(kNoSuchWindow);
307
308   if (session->overridden_geoposition) {
309     WebView* web_view;
310     status = session->chrome->GetWebViewById(web_view_id, &web_view);
311     if (status.IsError())
312       return status;
313     status = web_view->ConnectIfNecessary();
314     if (status.IsError())
315       return status;
316     status = web_view->OverrideGeolocation(*session->overridden_geoposition);
317     if (status.IsError())
318       return status;
319   }
320
321   session->window = web_view_id;
322   session->SwitchToTopFrame();
323   session->mouse_position = WebPoint(0, 0);
324   return Status(kOk);
325 }
326
327 Status ExecuteSetTimeout(
328     Session* session,
329     const base::DictionaryValue& params,
330     scoped_ptr<base::Value>* value) {
331   double ms_double;
332   if (!params.GetDouble("ms", &ms_double))
333     return Status(kUnknownError, "'ms' must be a double");
334   std::string type;
335   if (!params.GetString("type", &type))
336     return Status(kUnknownError, "'type' must be a string");
337
338   base::TimeDelta timeout =
339       base::TimeDelta::FromMilliseconds(static_cast<int>(ms_double));
340   // TODO(frankf): implicit and script timeout should be cleared
341   // if negative timeout is specified.
342   if (type == "implicit") {
343     session->implicit_wait = timeout;
344   } else if (type == "script") {
345     session->script_timeout = timeout;
346   } else if (type == "page load") {
347     session->page_load_timeout =
348         ((timeout < base::TimeDelta()) ? Session::kDefaultPageLoadTimeout
349                                        : timeout);
350   } else {
351     return Status(kUnknownError, "unknown type of timeout:" + type);
352   }
353   return Status(kOk);
354 }
355
356 Status ExecuteSetScriptTimeout(
357     Session* session,
358     const base::DictionaryValue& params,
359     scoped_ptr<base::Value>* value) {
360   double ms;
361   if (!params.GetDouble("ms", &ms) || ms < 0)
362     return Status(kUnknownError, "'ms' must be a non-negative number");
363   session->script_timeout =
364       base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
365   return Status(kOk);
366 }
367
368 Status ExecuteImplicitlyWait(
369     Session* session,
370     const base::DictionaryValue& params,
371     scoped_ptr<base::Value>* value) {
372   double ms;
373   if (!params.GetDouble("ms", &ms) || ms < 0)
374     return Status(kUnknownError, "'ms' must be a non-negative number");
375   session->implicit_wait =
376       base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
377   return Status(kOk);
378 }
379
380 Status ExecuteIsLoading(
381     Session* session,
382     const base::DictionaryValue& params,
383     scoped_ptr<base::Value>* value) {
384   WebView* web_view = NULL;
385   Status status = session->GetTargetWindow(&web_view);
386   if (status.IsError())
387     return status;
388
389   status = web_view->ConnectIfNecessary();
390   if (status.IsError())
391     return status;
392
393   bool is_pending;
394   status = web_view->IsPendingNavigation(
395       session->GetCurrentFrameId(), &is_pending);
396   if (status.IsError())
397     return status;
398   value->reset(new base::FundamentalValue(is_pending));
399   return Status(kOk);
400 }
401
402 Status ExecuteGetLocation(
403     Session* session,
404     const base::DictionaryValue& params,
405     scoped_ptr<base::Value>* value) {
406   if (!session->overridden_geoposition) {
407     return Status(kUnknownError,
408                   "Location must be set before it can be retrieved");
409   }
410   base::DictionaryValue location;
411   location.SetDouble("latitude", session->overridden_geoposition->latitude);
412   location.SetDouble("longitude", session->overridden_geoposition->longitude);
413   location.SetDouble("accuracy", session->overridden_geoposition->accuracy);
414   // Set a dummy altitude to make WebDriver clients happy.
415   // https://code.google.com/p/chromedriver/issues/detail?id=281
416   location.SetDouble("altitude", 0);
417   value->reset(location.DeepCopy());
418   return Status(kOk);
419 }
420
421 Status ExecuteGetWindowPosition(
422     Session* session,
423     const base::DictionaryValue& params,
424     scoped_ptr<base::Value>* value) {
425   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
426   if (!desktop) {
427     return Status(
428         kUnknownError,
429         "command only supported for desktop Chrome without debuggerAddress");
430   }
431
432   AutomationExtension* extension = NULL;
433   Status status = desktop->GetAutomationExtension(&extension);
434   if (status.IsError())
435     return status;
436
437   int x, y;
438   status = extension->GetWindowPosition(&x, &y);
439   if (status.IsError())
440     return status;
441
442   base::DictionaryValue position;
443   position.SetInteger("x", x);
444   position.SetInteger("y", y);
445   value->reset(position.DeepCopy());
446   return Status(kOk);
447 }
448
449 Status ExecuteSetWindowPosition(
450     Session* session,
451     const base::DictionaryValue& params,
452     scoped_ptr<base::Value>* value) {
453   double x, y;
454   if (!params.GetDouble("x", &x) || !params.GetDouble("y", &y))
455     return Status(kUnknownError, "missing or invalid 'x' or 'y'");
456
457   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
458   if (!desktop) {
459     return Status(
460         kUnknownError,
461         "command only supported for desktop Chrome without debuggerAddress");
462   }
463
464   AutomationExtension* extension = NULL;
465   Status status = desktop->GetAutomationExtension(&extension);
466   if (status.IsError())
467     return status;
468
469   return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
470 }
471
472 Status ExecuteGetWindowSize(
473     Session* session,
474     const base::DictionaryValue& params,
475     scoped_ptr<base::Value>* value) {
476   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
477   if (!desktop) {
478     return Status(
479         kUnknownError,
480         "command only supported for desktop Chrome without debuggerAddress");
481   }
482
483   AutomationExtension* extension = NULL;
484   Status status = desktop->GetAutomationExtension(&extension);
485   if (status.IsError())
486     return status;
487
488   int width, height;
489   status = extension->GetWindowSize(&width, &height);
490   if (status.IsError())
491     return status;
492
493   base::DictionaryValue size;
494   size.SetInteger("width", width);
495   size.SetInteger("height", height);
496   value->reset(size.DeepCopy());
497   return Status(kOk);
498 }
499
500 Status ExecuteSetWindowSize(
501     Session* session,
502     const base::DictionaryValue& params,
503     scoped_ptr<base::Value>* value) {
504   double width, height;
505   if (!params.GetDouble("width", &width) ||
506       !params.GetDouble("height", &height))
507     return Status(kUnknownError, "missing or invalid 'width' or 'height'");
508
509   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
510   if (!desktop) {
511     return Status(
512         kUnknownError,
513         "command only supported for desktop Chrome without debuggerAddress");
514   }
515
516   AutomationExtension* extension = NULL;
517   Status status = desktop->GetAutomationExtension(&extension);
518   if (status.IsError())
519     return status;
520
521   return extension->SetWindowSize(
522       static_cast<int>(width), static_cast<int>(height));
523 }
524
525 Status ExecuteMaximizeWindow(
526     Session* session,
527     const base::DictionaryValue& params,
528     scoped_ptr<base::Value>* value) {
529   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
530   if (!desktop) {
531     return Status(
532         kUnknownError,
533         "command only supported for desktop Chrome without debuggerAddress");
534   }
535
536   AutomationExtension* extension = NULL;
537   Status status = desktop->GetAutomationExtension(&extension);
538   if (status.IsError())
539     return status;
540
541   return extension->MaximizeWindow();
542 }
543
544 Status ExecuteGetAvailableLogTypes(
545     Session* session,
546     const base::DictionaryValue& params,
547     scoped_ptr<base::Value>* value) {
548   scoped_ptr<base::ListValue> types(new base::ListValue());
549   std::vector<WebDriverLog*> logs = session->GetAllLogs();
550   for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
551        log != logs.end();
552        ++log) {
553     types->AppendString((*log)->type());
554   }
555   *value = types.Pass();
556   return Status(kOk);
557 }
558
559 Status ExecuteGetLog(
560     Session* session,
561     const base::DictionaryValue& params,
562     scoped_ptr<base::Value>* value) {
563   std::string log_type;
564   if (!params.GetString("type", &log_type)) {
565     return Status(kUnknownError, "missing or invalid 'type'");
566   }
567   std::vector<WebDriverLog*> logs = session->GetAllLogs();
568   for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
569        log != logs.end();
570        ++log) {
571     if (log_type == (*log)->type()) {
572       *value = (*log)->GetAndClearEntries();
573       return Status(kOk);
574     }
575   }
576   return Status(kUnknownError, "log type '" + log_type + "' not found");
577 }
578
579 Status ExecuteUploadFile(
580     Session* session,
581     const base::DictionaryValue& params,
582     scoped_ptr<base::Value>* value) {
583     std::string base64_zip_data;
584   if (!params.GetString("file", &base64_zip_data))
585     return Status(kUnknownError, "missing or invalid 'file'");
586   std::string zip_data;
587   if (!Base64Decode(base64_zip_data, &zip_data))
588     return Status(kUnknownError, "unable to decode 'file'");
589
590   if (!session->temp_dir.IsValid()) {
591     if (!session->temp_dir.CreateUniqueTempDir())
592       return Status(kUnknownError, "unable to create temp dir");
593   }
594   base::FilePath upload_dir;
595   if (!file_util::CreateTemporaryDirInDir(
596           session->temp_dir.path(), FILE_PATH_LITERAL("upload"), &upload_dir)) {
597     return Status(kUnknownError, "unable to create temp dir");
598   }
599   std::string error_msg;
600   base::FilePath upload;
601   Status status = UnzipSoleFile(upload_dir, zip_data, &upload);
602   if (status.IsError())
603     return Status(kUnknownError, "unable to unzip 'file'", status);
604
605   value->reset(new base::StringValue(upload.value()));
606   return Status(kOk);
607 }