- add sources.
[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/status.h"
21 #include "chrome/test/chromedriver/logging.h"
22 #include "net/base/net_util.h"
23
24 namespace {
25
26 typedef base::Callback<Status(const base::Value&, Capabilities*)> Parser;
27
28 Status ParseBoolean(
29     bool* to_set,
30     const base::Value& option,
31     Capabilities* capabilities) {
32   if (!option.GetAsBoolean(to_set))
33     return Status(kUnknownError, "must be a boolean");
34   return Status(kOk);
35 }
36
37 Status ParseString(std::string* to_set,
38                    const base::Value& option,
39                    Capabilities* capabilities) {
40   std::string str;
41   if (!option.GetAsString(&str))
42     return Status(kUnknownError, "must be a string");
43   if (str.empty())
44     return Status(kUnknownError, "cannot be empty");
45   *to_set = str;
46   return Status(kOk);
47 }
48
49 Status ParseFilePath(base::FilePath* to_set,
50                      const base::Value& option,
51                      Capabilities* capabilities) {
52   base::FilePath::StringType str;
53   if (!option.GetAsString(&str))
54     return Status(kUnknownError, "must be a string");
55   *to_set = base::FilePath(str);
56   return Status(kOk);
57 }
58
59 Status ParseDict(scoped_ptr<base::DictionaryValue>* to_set,
60                  const base::Value& option,
61                  Capabilities* capabilities) {
62   const base::DictionaryValue* dict = NULL;
63   if (!option.GetAsDictionary(&dict))
64     return Status(kUnknownError, "must be a dictionary");
65   to_set->reset(dict->DeepCopy());
66   return Status(kOk);
67 }
68
69 Status IgnoreDeprecatedOption(
70     const char* option_name,
71     const base::Value& option,
72     Capabilities* capabilities) {
73   LOG(WARNING) << "Deprecated chrome option is ignored: " << option_name;
74   return Status(kOk);
75 }
76
77 Status IgnoreCapability(const base::Value& option, Capabilities* capabilities) {
78   return Status(kOk);
79 }
80
81 Status ParseLogPath(const base::Value& option, Capabilities* capabilities) {
82   if (!option.GetAsString(&capabilities->log_path))
83     return Status(kUnknownError, "must be a string");
84   return Status(kOk);
85 }
86
87 Status ParseSwitches(const base::Value& option,
88                      Capabilities* capabilities) {
89   const base::ListValue* switches_list = NULL;
90   if (!option.GetAsList(&switches_list))
91     return Status(kUnknownError, "must be a list");
92   for (size_t i = 0; i < switches_list->GetSize(); ++i) {
93     std::string arg_string;
94     if (!switches_list->GetString(i, &arg_string))
95       return Status(kUnknownError, "each argument must be a string");
96     capabilities->switches.SetUnparsedSwitch(arg_string);
97   }
98   return Status(kOk);
99 }
100
101 Status ParseExtensions(const base::Value& option, Capabilities* capabilities) {
102   const base::ListValue* extensions = NULL;
103   if (!option.GetAsList(&extensions))
104     return Status(kUnknownError, "must be a list");
105   for (size_t i = 0; i < extensions->GetSize(); ++i) {
106     std::string extension;
107     if (!extensions->GetString(i, &extension)) {
108       return Status(kUnknownError,
109                     "each extension must be a base64 encoded string");
110     }
111     capabilities->extensions.push_back(extension);
112   }
113   return Status(kOk);
114 }
115
116 Status ParseProxy(const base::Value& option, Capabilities* capabilities) {
117   const base::DictionaryValue* proxy_dict;
118   if (!option.GetAsDictionary(&proxy_dict))
119     return Status(kUnknownError, "must be a dictionary");
120   std::string proxy_type;
121   if (!proxy_dict->GetString("proxyType", &proxy_type))
122     return Status(kUnknownError, "'proxyType' must be a string");
123   proxy_type = StringToLowerASCII(proxy_type);
124   if (proxy_type == "direct") {
125     capabilities->switches.SetSwitch("no-proxy-server");
126   } else if (proxy_type == "system") {
127     // Chrome default.
128   } else if (proxy_type == "pac") {
129     CommandLine::StringType proxy_pac_url;
130     if (!proxy_dict->GetString("proxyAutoconfigUrl", &proxy_pac_url))
131       return Status(kUnknownError, "'proxyAutoconfigUrl' must be a string");
132     capabilities->switches.SetSwitch("proxy-pac-url", proxy_pac_url);
133   } else if (proxy_type == "autodetect") {
134     capabilities->switches.SetSwitch("proxy-auto-detect");
135   } else if (proxy_type == "manual") {
136     const char* proxy_servers_options[][2] = {
137         {"ftpProxy", "ftp"}, {"httpProxy", "http"}, {"sslProxy", "https"}};
138     const base::Value* option_value = NULL;
139     std::string proxy_servers;
140     for (size_t i = 0; i < arraysize(proxy_servers_options); ++i) {
141       if (!proxy_dict->Get(proxy_servers_options[i][0], &option_value) ||
142           option_value->IsType(base::Value::TYPE_NULL)) {
143         continue;
144       }
145       std::string value;
146       if (!option_value->GetAsString(&value)) {
147         return Status(
148             kUnknownError,
149             base::StringPrintf("'%s' must be a string",
150                                proxy_servers_options[i][0]));
151       }
152       // Converts into Chrome proxy scheme.
153       // Example: "http=localhost:9000;ftp=localhost:8000".
154       if (!proxy_servers.empty())
155         proxy_servers += ";";
156       proxy_servers += base::StringPrintf(
157           "%s=%s", proxy_servers_options[i][1], value.c_str());
158     }
159
160     std::string proxy_bypass_list;
161     if (proxy_dict->Get("noProxy", &option_value) &&
162         !option_value->IsType(base::Value::TYPE_NULL)) {
163       if (!option_value->GetAsString(&proxy_bypass_list))
164         return Status(kUnknownError, "'noProxy' must be a string");
165     }
166
167     if (proxy_servers.empty() && proxy_bypass_list.empty()) {
168       return Status(kUnknownError,
169                     "proxyType is 'manual' but no manual "
170                     "proxy capabilities were found");
171     }
172     if (!proxy_servers.empty())
173       capabilities->switches.SetSwitch("proxy-server", proxy_servers);
174     if (!proxy_bypass_list.empty()) {
175       capabilities->switches.SetSwitch("proxy-bypass-list",
176                                        proxy_bypass_list);
177     }
178   } else {
179     return Status(kUnknownError, "unrecognized proxy type:" + proxy_type);
180   }
181   return Status(kOk);
182 }
183
184 Status ParseExcludeSwitches(const base::Value& option,
185                             Capabilities* capabilities) {
186   const base::ListValue* switches = NULL;
187   if (!option.GetAsList(&switches))
188     return Status(kUnknownError, "must be a list");
189   for (size_t i = 0; i < switches->GetSize(); ++i) {
190     std::string switch_name;
191     if (!switches->GetString(i, &switch_name)) {
192       return Status(kUnknownError,
193                     "each switch to be removed must be a string");
194     }
195     capabilities->exclude_switches.insert(switch_name);
196   }
197   return Status(kOk);
198 }
199
200 Status ParseUseExistingBrowser(const base::Value& option,
201                                Capabilities* capabilities) {
202   std::string server_addr;
203   if (!option.GetAsString(&server_addr))
204     return Status(kUnknownError, "must be 'host:port'");
205
206   std::vector<std::string> values;
207   base::SplitString(server_addr, ':', &values);
208   if (values.size() != 2)
209     return Status(kUnknownError, "must be 'host:port'");
210
211   int port = 0;
212   base::StringToInt(values[1], &port);
213   if (port <= 0)
214     return Status(kUnknownError, "port must be > 0");
215
216   capabilities->debugger_address = NetAddress(values[0], port);
217   return Status(kOk);
218 }
219
220 Status ParseLoggingPrefs(const base::Value& option,
221                          Capabilities* capabilities) {
222   const base::DictionaryValue* logging_prefs = NULL;
223   if (!option.GetAsDictionary(&logging_prefs))
224     return Status(kUnknownError, "must be a dictionary");
225
226   for (base::DictionaryValue::Iterator pref(*logging_prefs);
227        !pref.IsAtEnd(); pref.Advance()) {
228     std::string type = pref.key();
229     Log::Level level;
230     std::string level_name;
231     if (!pref.value().GetAsString(&level_name) ||
232         !WebDriverLog::NameToLevel(level_name, &level)) {
233       return Status(kUnknownError, "invalid log level for '" + type + "' log");
234     }
235     capabilities->logging_prefs.insert(std::make_pair(type, level));
236   }
237   return Status(kOk);
238 }
239
240 Status ParseChromeOptions(
241     const base::Value& capability,
242     Capabilities* capabilities) {
243   const base::DictionaryValue* chrome_options = NULL;
244   if (!capability.GetAsDictionary(&chrome_options))
245     return Status(kUnknownError, "must be a dictionary");
246
247   bool is_android = chrome_options->HasKey("androidPackage");
248   bool is_existing = chrome_options->HasKey("debuggerAddress");
249
250   std::map<std::string, Parser> parser_map;
251   // Ignore 'args', 'binary' and 'extensions' capabilities by default, since the
252   // Java client always passes them.
253   parser_map["args"] = base::Bind(&IgnoreCapability);
254   parser_map["binary"] = base::Bind(&IgnoreCapability);
255   parser_map["extensions"] = base::Bind(&IgnoreCapability);
256   if (is_android) {
257     parser_map["androidActivity"] =
258         base::Bind(&ParseString, &capabilities->android_activity);
259     parser_map["androidDeviceSerial"] =
260         base::Bind(&ParseString, &capabilities->android_device_serial);
261     parser_map["androidPackage"] =
262         base::Bind(&ParseString, &capabilities->android_package);
263     parser_map["androidProcess"] =
264         base::Bind(&ParseString, &capabilities->android_process);
265     parser_map["args"] = base::Bind(&ParseSwitches);
266   } else if (is_existing) {
267     parser_map["debuggerAddress"] = base::Bind(&ParseUseExistingBrowser);
268   } else {
269     parser_map["args"] = base::Bind(&ParseSwitches);
270     parser_map["binary"] = base::Bind(&ParseFilePath, &capabilities->binary);
271     parser_map["detach"] = base::Bind(&ParseBoolean, &capabilities->detach);
272     parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
273     parser_map["extensions"] = base::Bind(&ParseExtensions);
274     parser_map["forceDevToolsScreenshot"] = base::Bind(
275         &ParseBoolean, &capabilities->force_devtools_screenshot);
276     parser_map["loadAsync"] = base::Bind(&IgnoreDeprecatedOption, "loadAsync");
277     parser_map["localState"] =
278         base::Bind(&ParseDict, &capabilities->local_state);
279     parser_map["logPath"] = base::Bind(&ParseLogPath);
280     parser_map["minidumpPath"] =
281         base::Bind(&ParseString, &capabilities->minidump_path);
282     parser_map["prefs"] = base::Bind(&ParseDict, &capabilities->prefs);
283   }
284
285   for (base::DictionaryValue::Iterator it(*chrome_options); !it.IsAtEnd();
286        it.Advance()) {
287     if (parser_map.find(it.key()) == parser_map.end()) {
288       return Status(kUnknownError,
289                     "unrecognized chrome option: " + it.key());
290     }
291     Status status = parser_map[it.key()].Run(it.value(), capabilities);
292     if (status.IsError())
293       return Status(kUnknownError, "cannot parse " + it.key(), status);
294   }
295   return Status(kOk);
296 }
297
298 }  // namespace
299
300 Switches::Switches() {}
301
302 Switches::~Switches() {}
303
304 void Switches::SetSwitch(const std::string& name) {
305   SetSwitch(name, NativeString());
306 }
307
308 void Switches::SetSwitch(const std::string& name, const std::string& value) {
309 #if defined(OS_WIN)
310   SetSwitch(name, UTF8ToUTF16(value));
311 #else
312   switch_map_[name] = value;
313 #endif
314 }
315
316 void Switches::SetSwitch(const std::string& name, const string16& value) {
317 #if defined(OS_WIN)
318   switch_map_[name] = value;
319 #else
320   SetSwitch(name, UTF16ToUTF8(value));
321 #endif
322 }
323
324 void Switches::SetSwitch(const std::string& name, const base::FilePath& value) {
325   SetSwitch(name, value.value());
326 }
327
328 void Switches::SetFromSwitches(const Switches& switches) {
329   for (SwitchMap::const_iterator iter = switches.switch_map_.begin();
330        iter != switches.switch_map_.end();
331        ++iter) {
332     switch_map_[iter->first] = iter->second;
333   }
334 }
335
336 void Switches::SetUnparsedSwitch(const std::string& unparsed_switch) {
337   std::string value;
338   size_t equals_index = unparsed_switch.find('=');
339   if (equals_index != std::string::npos)
340     value = unparsed_switch.substr(equals_index + 1);
341
342   std::string name;
343   size_t start_index = 0;
344   if (unparsed_switch.substr(0, 2) == "--")
345     start_index = 2;
346   name = unparsed_switch.substr(start_index, equals_index - start_index);
347
348   SetSwitch(name, value);
349 }
350
351 void Switches::RemoveSwitch(const std::string& name) {
352   switch_map_.erase(name);
353 }
354
355 bool Switches::HasSwitch(const std::string& name) const {
356   return switch_map_.count(name) > 0;
357 }
358
359 std::string Switches::GetSwitchValue(const std::string& name) const {
360   NativeString value = GetSwitchValueNative(name);
361 #if defined(OS_WIN)
362   return UTF16ToUTF8(value);
363 #else
364   return value;
365 #endif
366 }
367
368 Switches::NativeString Switches::GetSwitchValueNative(
369     const std::string& name) const {
370   SwitchMap::const_iterator iter = switch_map_.find(name);
371   if (iter == switch_map_.end())
372     return NativeString();
373   return iter->second;
374 }
375
376 size_t Switches::GetSize() const {
377   return switch_map_.size();
378 }
379
380 void Switches::AppendToCommandLine(CommandLine* command) const {
381   for (SwitchMap::const_iterator iter = switch_map_.begin();
382        iter != switch_map_.end();
383        ++iter) {
384     command->AppendSwitchNative(iter->first, iter->second);
385   }
386 }
387
388 std::string Switches::ToString() const {
389   std::string str;
390   SwitchMap::const_iterator iter = switch_map_.begin();
391   while (iter != switch_map_.end()) {
392     str += "--" + iter->first;
393     std::string value = GetSwitchValue(iter->first);
394     if (value.length()) {
395       if (value.find(' ') != std::string::npos)
396         value = base::GetDoubleQuotedJson(value);
397       str += "=" + value;
398     }
399     ++iter;
400     if (iter == switch_map_.end())
401       break;
402     str += " ";
403   }
404   return str;
405 }
406
407 Capabilities::Capabilities()
408     : detach(false),
409       force_devtools_screenshot(false) {}
410
411 Capabilities::~Capabilities() {}
412
413 bool Capabilities::IsAndroid() const {
414   return !android_package.empty();
415 }
416
417 bool Capabilities::IsExistingBrowser() const {
418   return debugger_address.IsValid();
419 }
420
421 Status Capabilities::Parse(const base::DictionaryValue& desired_caps) {
422   std::map<std::string, Parser> parser_map;
423   parser_map["chromeOptions"] = base::Bind(&ParseChromeOptions);
424   parser_map["loggingPrefs"] = base::Bind(&ParseLoggingPrefs);
425   parser_map["proxy"] = base::Bind(&ParseProxy);
426   for (std::map<std::string, Parser>::iterator it = parser_map.begin();
427        it != parser_map.end(); ++it) {
428     const base::Value* capability = NULL;
429     if (desired_caps.Get(it->first, &capability)) {
430       Status status = it->second.Run(*capability, this);
431       if (status.IsError()) {
432         return Status(
433             kUnknownError, "cannot parse capability: " + it->first, status);
434       }
435     }
436   }
437   return Status(kOk);
438 }