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