Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / test / chromedriver / capabilities.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/capabilities.h"
6
7 #include <map>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/json/string_escape.h"
12 #include "base/logging.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_tokenizer.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "chrome/test/chromedriver/chrome/mobile_device.h"
21 #include "chrome/test/chromedriver/chrome/status.h"
22 #include "chrome/test/chromedriver/logging.h"
23 #include "net/base/net_util.h"
24
25 namespace {
26
27 typedef base::Callback<Status(const base::Value&, Capabilities*)> Parser;
28
29 Status ParseBoolean(
30     bool* to_set,
31     const base::Value& option,
32     Capabilities* capabilities) {
33   if (!option.GetAsBoolean(to_set))
34     return Status(kUnknownError, "must be a boolean");
35   return Status(kOk);
36 }
37
38 Status ParseString(std::string* to_set,
39                    const base::Value& option,
40                    Capabilities* capabilities) {
41   std::string str;
42   if (!option.GetAsString(&str))
43     return Status(kUnknownError, "must be a string");
44   if (str.empty())
45     return Status(kUnknownError, "cannot be empty");
46   *to_set = str;
47   return Status(kOk);
48 }
49
50 Status ParseInterval(int* to_set,
51                      const base::Value& option,
52                      Capabilities* capabilities) {
53   int parsed_int = 0;
54   if (!option.GetAsInteger(&parsed_int))
55     return Status(kUnknownError, "must be an integer");
56   if (parsed_int <= 0)
57     return Status(kUnknownError, "must be positive");
58   *to_set = parsed_int;
59   return Status(kOk);
60 }
61
62 Status ParseFilePath(base::FilePath* to_set,
63                      const base::Value& option,
64                      Capabilities* capabilities) {
65   base::FilePath::StringType str;
66   if (!option.GetAsString(&str))
67     return Status(kUnknownError, "must be a string");
68   *to_set = base::FilePath(str);
69   return Status(kOk);
70 }
71
72 Status ParseDict(scoped_ptr<base::DictionaryValue>* to_set,
73                  const base::Value& option,
74                  Capabilities* capabilities) {
75   const base::DictionaryValue* dict = NULL;
76   if (!option.GetAsDictionary(&dict))
77     return Status(kUnknownError, "must be a dictionary");
78   to_set->reset(dict->DeepCopy());
79   return Status(kOk);
80 }
81
82 Status IgnoreDeprecatedOption(
83     const char* option_name,
84     const base::Value& option,
85     Capabilities* capabilities) {
86   LOG(WARNING) << "Deprecated chrome option is ignored: " << option_name;
87   return Status(kOk);
88 }
89
90 Status IgnoreCapability(const base::Value& option, Capabilities* capabilities) {
91   return Status(kOk);
92 }
93
94 Status ParseLogPath(const base::Value& option, Capabilities* capabilities) {
95   if (!option.GetAsString(&capabilities->log_path))
96     return Status(kUnknownError, "must be a string");
97   return Status(kOk);
98 }
99
100 Status ParseDeviceName(std::string device_name, Capabilities* capabilities) {
101   scoped_ptr<MobileDevice> device;
102   Status status = FindMobileDevice(device_name, &device);
103
104   if (status.IsError()) {
105     return Status(kUnknownError,
106                   "'" + device_name + "' must be a valid device",
107                   status);
108   }
109
110   capabilities->device_metrics.reset(device->device_metrics.release());
111   capabilities->switches.SetSwitch("user-agent", device->user_agent);
112
113   return Status(kOk);
114 }
115
116 Status ParseMobileEmulation(const base::Value& option,
117                             Capabilities* capabilities) {
118   const base::DictionaryValue* mobile_emulation;
119   if (!option.GetAsDictionary(&mobile_emulation))
120     return Status(kUnknownError, "'mobileEmulation' must be a dictionary");
121
122   if (mobile_emulation->HasKey("deviceName")) {
123     // Cannot use any other options with deviceName.
124     if (mobile_emulation->size() > 1)
125       return Status(kUnknownError, "'deviceName' must be used alone");
126
127     std::string device_name;
128     if (!mobile_emulation->GetString("deviceName", &device_name))
129       return Status(kUnknownError, "'deviceName' must be a string");
130
131     return ParseDeviceName(device_name, capabilities);
132   }
133
134   if (mobile_emulation->HasKey("deviceMetrics")) {
135     const base::DictionaryValue* metrics;
136     if (!mobile_emulation->GetDictionary("deviceMetrics", &metrics))
137       return Status(kUnknownError, "'deviceMetrics' must be a dictionary");
138
139     int width = 0;
140     int height = 0;
141     double device_scale_factor = 0;
142     if (!metrics->GetInteger("width", &width) ||
143         !metrics->GetInteger("height", &height) ||
144         !metrics->GetDouble("pixelRatio", &device_scale_factor))
145       return Status(kUnknownError, "invalid 'deviceMetrics'");
146
147     DeviceMetrics* device_metrics =
148         new DeviceMetrics(width, height, device_scale_factor);
149     capabilities->device_metrics =
150         scoped_ptr<DeviceMetrics>(device_metrics);
151   }
152
153   if (mobile_emulation->HasKey("userAgent")) {
154     std::string user_agent;
155     if (!mobile_emulation->GetString("userAgent", &user_agent))
156       return Status(kUnknownError, "'userAgent' must be a string");
157
158     capabilities->switches.SetSwitch("user-agent", user_agent);
159   }
160
161   return Status(kOk);
162 }
163
164 Status ParseSwitches(const base::Value& option,
165                      Capabilities* capabilities) {
166   const base::ListValue* switches_list = NULL;
167   if (!option.GetAsList(&switches_list))
168     return Status(kUnknownError, "must be a list");
169   for (size_t i = 0; i < switches_list->GetSize(); ++i) {
170     std::string arg_string;
171     if (!switches_list->GetString(i, &arg_string))
172       return Status(kUnknownError, "each argument must be a string");
173     capabilities->switches.SetUnparsedSwitch(arg_string);
174   }
175   return Status(kOk);
176 }
177
178 Status ParseExtensions(const base::Value& option, Capabilities* capabilities) {
179   const base::ListValue* extensions = NULL;
180   if (!option.GetAsList(&extensions))
181     return Status(kUnknownError, "must be a list");
182   for (size_t i = 0; i < extensions->GetSize(); ++i) {
183     std::string extension;
184     if (!extensions->GetString(i, &extension)) {
185       return Status(kUnknownError,
186                     "each extension must be a base64 encoded string");
187     }
188     capabilities->extensions.push_back(extension);
189   }
190   return Status(kOk);
191 }
192
193 Status ParseProxy(const base::Value& option, Capabilities* capabilities) {
194   const base::DictionaryValue* proxy_dict;
195   if (!option.GetAsDictionary(&proxy_dict))
196     return Status(kUnknownError, "must be a dictionary");
197   std::string proxy_type;
198   if (!proxy_dict->GetString("proxyType", &proxy_type))
199     return Status(kUnknownError, "'proxyType' must be a string");
200   proxy_type = base::StringToLowerASCII(proxy_type);
201   if (proxy_type == "direct") {
202     capabilities->switches.SetSwitch("no-proxy-server");
203   } else if (proxy_type == "system") {
204     // Chrome default.
205   } else if (proxy_type == "pac") {
206     CommandLine::StringType proxy_pac_url;
207     if (!proxy_dict->GetString("proxyAutoconfigUrl", &proxy_pac_url))
208       return Status(kUnknownError, "'proxyAutoconfigUrl' must be a string");
209     capabilities->switches.SetSwitch("proxy-pac-url", proxy_pac_url);
210   } else if (proxy_type == "autodetect") {
211     capabilities->switches.SetSwitch("proxy-auto-detect");
212   } else if (proxy_type == "manual") {
213     const char* proxy_servers_options[][2] = {
214         {"ftpProxy", "ftp"}, {"httpProxy", "http"}, {"sslProxy", "https"}};
215     const base::Value* option_value = NULL;
216     std::string proxy_servers;
217     for (size_t i = 0; i < arraysize(proxy_servers_options); ++i) {
218       if (!proxy_dict->Get(proxy_servers_options[i][0], &option_value) ||
219           option_value->IsType(base::Value::TYPE_NULL)) {
220         continue;
221       }
222       std::string value;
223       if (!option_value->GetAsString(&value)) {
224         return Status(
225             kUnknownError,
226             base::StringPrintf("'%s' must be a string",
227                                proxy_servers_options[i][0]));
228       }
229       // Converts into Chrome proxy scheme.
230       // Example: "http=localhost:9000;ftp=localhost:8000".
231       if (!proxy_servers.empty())
232         proxy_servers += ";";
233       proxy_servers += base::StringPrintf(
234           "%s=%s", proxy_servers_options[i][1], value.c_str());
235     }
236
237     std::string proxy_bypass_list;
238     if (proxy_dict->Get("noProxy", &option_value) &&
239         !option_value->IsType(base::Value::TYPE_NULL)) {
240       if (!option_value->GetAsString(&proxy_bypass_list))
241         return Status(kUnknownError, "'noProxy' must be a string");
242     }
243
244     if (proxy_servers.empty() && proxy_bypass_list.empty()) {
245       return Status(kUnknownError,
246                     "proxyType is 'manual' but no manual "
247                     "proxy capabilities were found");
248     }
249     if (!proxy_servers.empty())
250       capabilities->switches.SetSwitch("proxy-server", proxy_servers);
251     if (!proxy_bypass_list.empty()) {
252       capabilities->switches.SetSwitch("proxy-bypass-list",
253                                        proxy_bypass_list);
254     }
255   } else {
256     return Status(kUnknownError, "unrecognized proxy type:" + proxy_type);
257   }
258   return Status(kOk);
259 }
260
261 Status ParseExcludeSwitches(const base::Value& option,
262                             Capabilities* capabilities) {
263   const base::ListValue* switches = NULL;
264   if (!option.GetAsList(&switches))
265     return Status(kUnknownError, "must be a list");
266   for (size_t i = 0; i < switches->GetSize(); ++i) {
267     std::string switch_name;
268     if (!switches->GetString(i, &switch_name)) {
269       return Status(kUnknownError,
270                     "each switch to be removed must be a string");
271     }
272     capabilities->exclude_switches.insert(switch_name);
273   }
274   return Status(kOk);
275 }
276
277 Status ParseUseRemoteBrowser(const base::Value& option,
278                                Capabilities* capabilities) {
279   std::string server_addr;
280   if (!option.GetAsString(&server_addr))
281     return Status(kUnknownError, "must be 'host:port'");
282
283   std::vector<std::string> values;
284   base::SplitString(server_addr, ':', &values);
285   if (values.size() != 2)
286     return Status(kUnknownError, "must be 'host:port'");
287
288   int port = 0;
289   base::StringToInt(values[1], &port);
290   if (port <= 0)
291     return Status(kUnknownError, "port must be > 0");
292
293   capabilities->debugger_address = NetAddress(values[0], port);
294   return Status(kOk);
295 }
296
297 Status ParseLoggingPrefs(const base::Value& option,
298                          Capabilities* capabilities) {
299   const base::DictionaryValue* logging_prefs = NULL;
300   if (!option.GetAsDictionary(&logging_prefs))
301     return Status(kUnknownError, "must be a dictionary");
302
303   for (base::DictionaryValue::Iterator pref(*logging_prefs);
304        !pref.IsAtEnd(); pref.Advance()) {
305     std::string type = pref.key();
306     Log::Level level;
307     std::string level_name;
308     if (!pref.value().GetAsString(&level_name) ||
309         !WebDriverLog::NameToLevel(level_name, &level)) {
310       return Status(kUnknownError, "invalid log level for '" + type + "' log");
311     }
312     capabilities->logging_prefs.insert(std::make_pair(type, level));
313   }
314   return Status(kOk);
315 }
316
317 Status ParseInspectorDomainStatus(
318     PerfLoggingPrefs::InspectorDomainStatus* to_set,
319     const base::Value& option,
320     Capabilities* capabilities) {
321   bool desired_value;
322   if (!option.GetAsBoolean(&desired_value))
323     return Status(kUnknownError, "must be a boolean");
324   if (desired_value)
325     *to_set = PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled;
326   else
327     *to_set = PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyDisabled;
328   return Status(kOk);
329 }
330
331 Status ParsePerfLoggingPrefs(const base::Value& option,
332                              Capabilities* capabilities) {
333   const base::DictionaryValue* perf_logging_prefs = NULL;
334   if (!option.GetAsDictionary(&perf_logging_prefs))
335     return Status(kUnknownError, "must be a dictionary");
336
337   std::map<std::string, Parser> parser_map;
338   parser_map["bufferUsageReportingInterval"] = base::Bind(&ParseInterval,
339       &capabilities->perf_logging_prefs.buffer_usage_reporting_interval);
340   parser_map["enableNetwork"] = base::Bind(
341       &ParseInspectorDomainStatus, &capabilities->perf_logging_prefs.network);
342   parser_map["enablePage"] = base::Bind(
343       &ParseInspectorDomainStatus, &capabilities->perf_logging_prefs.page);
344   parser_map["enableTimeline"] = base::Bind(
345       &ParseInspectorDomainStatus, &capabilities->perf_logging_prefs.timeline);
346   parser_map["traceCategories"] = base::Bind(
347       &ParseString, &capabilities->perf_logging_prefs.trace_categories);
348
349   for (base::DictionaryValue::Iterator it(*perf_logging_prefs); !it.IsAtEnd();
350        it.Advance()) {
351      if (parser_map.find(it.key()) == parser_map.end())
352        return Status(kUnknownError, "unrecognized performance logging "
353                      "option: " + it.key());
354      Status status = parser_map[it.key()].Run(it.value(), capabilities);
355      if (status.IsError())
356        return Status(kUnknownError, "cannot parse " + it.key(), status);
357    }
358    return Status(kOk);
359 }
360
361 Status ParseChromeOptions(
362     const base::Value& capability,
363     Capabilities* capabilities) {
364   const base::DictionaryValue* chrome_options = NULL;
365   if (!capability.GetAsDictionary(&chrome_options))
366     return Status(kUnknownError, "must be a dictionary");
367
368   bool is_android = chrome_options->HasKey("androidPackage");
369   bool is_remote = chrome_options->HasKey("debuggerAddress");
370
371   std::map<std::string, Parser> parser_map;
372   // Ignore 'args', 'binary' and 'extensions' capabilities by default, since the
373   // Java client always passes them.
374   parser_map["args"] = base::Bind(&IgnoreCapability);
375   parser_map["binary"] = base::Bind(&IgnoreCapability);
376   parser_map["extensions"] = base::Bind(&IgnoreCapability);
377
378   parser_map["perfLoggingPrefs"] = base::Bind(&ParsePerfLoggingPrefs);
379
380   if (is_android) {
381     parser_map["androidActivity"] =
382         base::Bind(&ParseString, &capabilities->android_activity);
383     parser_map["androidDeviceSerial"] =
384         base::Bind(&ParseString, &capabilities->android_device_serial);
385     parser_map["androidPackage"] =
386         base::Bind(&ParseString, &capabilities->android_package);
387     parser_map["androidProcess"] =
388         base::Bind(&ParseString, &capabilities->android_process);
389     parser_map["androidUseRunningApp"] =
390         base::Bind(&ParseBoolean, &capabilities->android_use_running_app);
391     parser_map["args"] = base::Bind(&ParseSwitches);
392     parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
393     parser_map["loadAsync"] = base::Bind(&IgnoreDeprecatedOption, "loadAsync");
394   } else if (is_remote) {
395     parser_map["debuggerAddress"] = base::Bind(&ParseUseRemoteBrowser);
396   } else {
397     parser_map["args"] = base::Bind(&ParseSwitches);
398     parser_map["binary"] = base::Bind(&ParseFilePath, &capabilities->binary);
399     parser_map["detach"] = base::Bind(&ParseBoolean, &capabilities->detach);
400     parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
401     parser_map["extensions"] = base::Bind(&ParseExtensions);
402     parser_map["forceDevToolsScreenshot"] = base::Bind(
403         &ParseBoolean, &capabilities->force_devtools_screenshot);
404     parser_map["loadAsync"] = base::Bind(&IgnoreDeprecatedOption, "loadAsync");
405     parser_map["localState"] =
406         base::Bind(&ParseDict, &capabilities->local_state);
407     parser_map["logPath"] = base::Bind(&ParseLogPath);
408     parser_map["minidumpPath"] =
409         base::Bind(&ParseString, &capabilities->minidump_path);
410     parser_map["mobileEmulation"] = base::Bind(&ParseMobileEmulation);
411     parser_map["prefs"] = base::Bind(&ParseDict, &capabilities->prefs);
412   }
413
414   for (base::DictionaryValue::Iterator it(*chrome_options); !it.IsAtEnd();
415        it.Advance()) {
416     if (parser_map.find(it.key()) == parser_map.end()) {
417       return Status(kUnknownError,
418                     "unrecognized chrome option: " + it.key());
419     }
420     Status status = parser_map[it.key()].Run(it.value(), capabilities);
421     if (status.IsError())
422       return Status(kUnknownError, "cannot parse " + it.key(), status);
423   }
424   return Status(kOk);
425 }
426
427 }  // namespace
428
429 Switches::Switches() {}
430
431 Switches::~Switches() {}
432
433 void Switches::SetSwitch(const std::string& name) {
434   SetSwitch(name, NativeString());
435 }
436
437 void Switches::SetSwitch(const std::string& name, const std::string& value) {
438 #if defined(OS_WIN)
439   SetSwitch(name, base::UTF8ToUTF16(value));
440 #else
441   switch_map_[name] = value;
442 #endif
443 }
444
445 void Switches::SetSwitch(const std::string& name, const base::string16& value) {
446 #if defined(OS_WIN)
447   switch_map_[name] = value;
448 #else
449   SetSwitch(name, base::UTF16ToUTF8(value));
450 #endif
451 }
452
453 void Switches::SetSwitch(const std::string& name, const base::FilePath& value) {
454   SetSwitch(name, value.value());
455 }
456
457 void Switches::SetFromSwitches(const Switches& switches) {
458   for (SwitchMap::const_iterator iter = switches.switch_map_.begin();
459        iter != switches.switch_map_.end();
460        ++iter) {
461     switch_map_[iter->first] = iter->second;
462   }
463 }
464
465 void Switches::SetUnparsedSwitch(const std::string& unparsed_switch) {
466   std::string value;
467   size_t equals_index = unparsed_switch.find('=');
468   if (equals_index != std::string::npos)
469     value = unparsed_switch.substr(equals_index + 1);
470
471   std::string name;
472   size_t start_index = 0;
473   if (unparsed_switch.substr(0, 2) == "--")
474     start_index = 2;
475   name = unparsed_switch.substr(start_index, equals_index - start_index);
476
477   SetSwitch(name, value);
478 }
479
480 void Switches::RemoveSwitch(const std::string& name) {
481   switch_map_.erase(name);
482 }
483
484 bool Switches::HasSwitch(const std::string& name) const {
485   return switch_map_.count(name) > 0;
486 }
487
488 std::string Switches::GetSwitchValue(const std::string& name) const {
489   NativeString value = GetSwitchValueNative(name);
490 #if defined(OS_WIN)
491   return base::UTF16ToUTF8(value);
492 #else
493   return value;
494 #endif
495 }
496
497 Switches::NativeString Switches::GetSwitchValueNative(
498     const std::string& name) const {
499   SwitchMap::const_iterator iter = switch_map_.find(name);
500   if (iter == switch_map_.end())
501     return NativeString();
502   return iter->second;
503 }
504
505 size_t Switches::GetSize() const {
506   return switch_map_.size();
507 }
508
509 void Switches::AppendToCommandLine(CommandLine* command) const {
510   for (SwitchMap::const_iterator iter = switch_map_.begin();
511        iter != switch_map_.end();
512        ++iter) {
513     command->AppendSwitchNative(iter->first, iter->second);
514   }
515 }
516
517 std::string Switches::ToString() const {
518   std::string str;
519   SwitchMap::const_iterator iter = switch_map_.begin();
520   while (iter != switch_map_.end()) {
521     str += "--" + iter->first;
522     std::string value = GetSwitchValue(iter->first);
523     if (value.length()) {
524       if (value.find(' ') != std::string::npos)
525         value = base::GetQuotedJSONString(value);
526       str += "=" + value;
527     }
528     ++iter;
529     if (iter == switch_map_.end())
530       break;
531     str += " ";
532   }
533   return str;
534 }
535
536 PerfLoggingPrefs::PerfLoggingPrefs()
537     : network(InspectorDomainStatus::kDefaultEnabled),
538       page(InspectorDomainStatus::kDefaultEnabled),
539       timeline(InspectorDomainStatus::kDefaultEnabled),
540       trace_categories(),
541       buffer_usage_reporting_interval(1000) {}
542
543 PerfLoggingPrefs::~PerfLoggingPrefs() {}
544
545 Capabilities::Capabilities()
546     : android_use_running_app(false),
547       detach(false),
548       force_devtools_screenshot(false) {}
549
550 Capabilities::~Capabilities() {}
551
552 bool Capabilities::IsAndroid() const {
553   return !android_package.empty();
554 }
555
556 bool Capabilities::IsRemoteBrowser() const {
557   return debugger_address.IsValid();
558 }
559
560 Status Capabilities::Parse(const base::DictionaryValue& desired_caps) {
561   std::map<std::string, Parser> parser_map;
562   parser_map["chromeOptions"] = base::Bind(&ParseChromeOptions);
563   parser_map["loggingPrefs"] = base::Bind(&ParseLoggingPrefs);
564   parser_map["proxy"] = base::Bind(&ParseProxy);
565   for (std::map<std::string, Parser>::iterator it = parser_map.begin();
566        it != parser_map.end(); ++it) {
567     const base::Value* capability = NULL;
568     if (desired_caps.Get(it->first, &capability)) {
569       Status status = it->second.Run(*capability, this);
570       if (status.IsError()) {
571         return Status(
572             kUnknownError, "cannot parse capability: " + it->first, status);
573       }
574     }
575   }
576   // Perf log must be enabled if perf log prefs are specified; otherwise, error.
577   LoggingPrefs::const_iterator iter = logging_prefs.find(
578       WebDriverLog::kPerformanceType);
579   if (iter == logging_prefs.end() || iter->second == Log::kOff) {
580     const base::DictionaryValue* chrome_options = NULL;
581     if (desired_caps.GetDictionary("chromeOptions", &chrome_options) &&
582         chrome_options->HasKey("perfLoggingPrefs")) {
583       return Status(kUnknownError, "perfLoggingPrefs specified, "
584                     "but performance logging was not enabled");
585     }
586   }
587   return Status(kOk);
588 }