Update CrOS OOBE throbber to MD throbber; delete old asset
[chromium-blink-merge.git] / chrome / test / chromedriver / capabilities.cc
blobe4ba8638bc6fb7b7e5f0c2273645ad6c28a4c047
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.
5 #include "chrome/test/chromedriver/capabilities.h"
7 #include <map>
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"
25 namespace {
27 typedef base::Callback<Status(const base::Value&, Capabilities*)> Parser;
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);
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);
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);
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);
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);
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);
90 Status IgnoreCapability(const base::Value& option, Capabilities* capabilities) {
91 return Status(kOk);
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);
100 Status ParseDeviceName(std::string device_name, Capabilities* capabilities) {
101 scoped_ptr<MobileDevice> device;
102 Status status = FindMobileDevice(device_name, &device);
104 if (status.IsError()) {
105 return Status(kUnknownError,
106 "'" + device_name + "' must be a valid device",
107 status);
110 capabilities->device_metrics.reset(device->device_metrics.release());
111 // Don't override the user agent if blank (like for notebooks).
112 if (!device->user_agent.empty())
113 capabilities->switches.SetSwitch("user-agent", device->user_agent);
115 return Status(kOk);
118 Status ParseMobileEmulation(const base::Value& option,
119 Capabilities* capabilities) {
120 const base::DictionaryValue* mobile_emulation;
121 if (!option.GetAsDictionary(&mobile_emulation))
122 return Status(kUnknownError, "'mobileEmulation' must be a dictionary");
124 if (mobile_emulation->HasKey("deviceName")) {
125 // Cannot use any other options with deviceName.
126 if (mobile_emulation->size() > 1)
127 return Status(kUnknownError, "'deviceName' must be used alone");
129 std::string device_name;
130 if (!mobile_emulation->GetString("deviceName", &device_name))
131 return Status(kUnknownError, "'deviceName' must be a string");
133 return ParseDeviceName(device_name, capabilities);
136 if (mobile_emulation->HasKey("deviceMetrics")) {
137 const base::DictionaryValue* metrics;
138 if (!mobile_emulation->GetDictionary("deviceMetrics", &metrics))
139 return Status(kUnknownError, "'deviceMetrics' must be a dictionary");
141 int width = 0;
142 int height = 0;
143 double device_scale_factor = 0;
144 bool touch = true;
145 bool mobile = true;
146 if (!metrics->GetInteger("width", &width) ||
147 !metrics->GetInteger("height", &height) ||
148 !metrics->GetDouble("pixelRatio", &device_scale_factor))
149 return Status(kUnknownError, "invalid 'deviceMetrics'");
151 if (metrics->HasKey("touch")) {
152 if (!metrics->GetBoolean("touch", &touch))
153 return Status(kUnknownError, "'touch' must be a boolean");
156 if (metrics->HasKey("mobile")) {
157 if (!metrics->GetBoolean("mobile", &mobile))
158 return Status(kUnknownError, "'mobile' must be a boolean");
161 DeviceMetrics* device_metrics =
162 new DeviceMetrics(width, height, device_scale_factor, touch, mobile);
163 capabilities->device_metrics =
164 scoped_ptr<DeviceMetrics>(device_metrics);
167 if (mobile_emulation->HasKey("userAgent")) {
168 std::string user_agent;
169 if (!mobile_emulation->GetString("userAgent", &user_agent))
170 return Status(kUnknownError, "'userAgent' must be a string");
172 capabilities->switches.SetSwitch("user-agent", user_agent);
175 return Status(kOk);
178 Status ParseSwitches(const base::Value& option,
179 Capabilities* capabilities) {
180 const base::ListValue* switches_list = NULL;
181 if (!option.GetAsList(&switches_list))
182 return Status(kUnknownError, "must be a list");
183 for (size_t i = 0; i < switches_list->GetSize(); ++i) {
184 std::string arg_string;
185 if (!switches_list->GetString(i, &arg_string))
186 return Status(kUnknownError, "each argument must be a string");
187 capabilities->switches.SetUnparsedSwitch(arg_string);
189 return Status(kOk);
192 Status ParseExtensions(const base::Value& option, Capabilities* capabilities) {
193 const base::ListValue* extensions = NULL;
194 if (!option.GetAsList(&extensions))
195 return Status(kUnknownError, "must be a list");
196 for (size_t i = 0; i < extensions->GetSize(); ++i) {
197 std::string extension;
198 if (!extensions->GetString(i, &extension)) {
199 return Status(kUnknownError,
200 "each extension must be a base64 encoded string");
202 capabilities->extensions.push_back(extension);
204 return Status(kOk);
207 Status ParseProxy(const base::Value& option, Capabilities* capabilities) {
208 const base::DictionaryValue* proxy_dict;
209 if (!option.GetAsDictionary(&proxy_dict))
210 return Status(kUnknownError, "must be a dictionary");
211 std::string proxy_type;
212 if (!proxy_dict->GetString("proxyType", &proxy_type))
213 return Status(kUnknownError, "'proxyType' must be a string");
214 proxy_type = base::ToLowerASCII(proxy_type);
215 if (proxy_type == "direct") {
216 capabilities->switches.SetSwitch("no-proxy-server");
217 } else if (proxy_type == "system") {
218 // Chrome default.
219 } else if (proxy_type == "pac") {
220 base::CommandLine::StringType proxy_pac_url;
221 if (!proxy_dict->GetString("proxyAutoconfigUrl", &proxy_pac_url))
222 return Status(kUnknownError, "'proxyAutoconfigUrl' must be a string");
223 capabilities->switches.SetSwitch("proxy-pac-url", proxy_pac_url);
224 } else if (proxy_type == "autodetect") {
225 capabilities->switches.SetSwitch("proxy-auto-detect");
226 } else if (proxy_type == "manual") {
227 const char* const proxy_servers_options[][2] = {
228 {"ftpProxy", "ftp"}, {"httpProxy", "http"}, {"sslProxy", "https"}};
229 const base::Value* option_value = NULL;
230 std::string proxy_servers;
231 for (size_t i = 0; i < arraysize(proxy_servers_options); ++i) {
232 if (!proxy_dict->Get(proxy_servers_options[i][0], &option_value) ||
233 option_value->IsType(base::Value::TYPE_NULL)) {
234 continue;
236 std::string value;
237 if (!option_value->GetAsString(&value)) {
238 return Status(
239 kUnknownError,
240 base::StringPrintf("'%s' must be a string",
241 proxy_servers_options[i][0]));
243 // Converts into Chrome proxy scheme.
244 // Example: "http=localhost:9000;ftp=localhost:8000".
245 if (!proxy_servers.empty())
246 proxy_servers += ";";
247 proxy_servers += base::StringPrintf(
248 "%s=%s", proxy_servers_options[i][1], value.c_str());
251 std::string proxy_bypass_list;
252 if (proxy_dict->Get("noProxy", &option_value) &&
253 !option_value->IsType(base::Value::TYPE_NULL)) {
254 if (!option_value->GetAsString(&proxy_bypass_list))
255 return Status(kUnknownError, "'noProxy' must be a string");
258 if (proxy_servers.empty() && proxy_bypass_list.empty()) {
259 return Status(kUnknownError,
260 "proxyType is 'manual' but no manual "
261 "proxy capabilities were found");
263 if (!proxy_servers.empty())
264 capabilities->switches.SetSwitch("proxy-server", proxy_servers);
265 if (!proxy_bypass_list.empty()) {
266 capabilities->switches.SetSwitch("proxy-bypass-list",
267 proxy_bypass_list);
269 } else {
270 return Status(kUnknownError, "unrecognized proxy type:" + proxy_type);
272 return Status(kOk);
275 Status ParseExcludeSwitches(const base::Value& option,
276 Capabilities* capabilities) {
277 const base::ListValue* switches = NULL;
278 if (!option.GetAsList(&switches))
279 return Status(kUnknownError, "must be a list");
280 for (size_t i = 0; i < switches->GetSize(); ++i) {
281 std::string switch_name;
282 if (!switches->GetString(i, &switch_name)) {
283 return Status(kUnknownError,
284 "each switch to be removed must be a string");
286 capabilities->exclude_switches.insert(switch_name);
288 return Status(kOk);
291 Status ParseUseRemoteBrowser(const base::Value& option,
292 Capabilities* capabilities) {
293 std::string server_addr;
294 if (!option.GetAsString(&server_addr))
295 return Status(kUnknownError, "must be 'host:port'");
297 std::vector<std::string> values = base::SplitString(
298 server_addr, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
299 if (values.size() != 2)
300 return Status(kUnknownError, "must be 'host:port'");
302 int port = 0;
303 base::StringToInt(values[1], &port);
304 if (port <= 0)
305 return Status(kUnknownError, "port must be > 0");
307 capabilities->debugger_address = NetAddress(values[0], port);
308 return Status(kOk);
311 Status ParseLoggingPrefs(const base::Value& option,
312 Capabilities* capabilities) {
313 const base::DictionaryValue* logging_prefs = NULL;
314 if (!option.GetAsDictionary(&logging_prefs))
315 return Status(kUnknownError, "must be a dictionary");
317 for (base::DictionaryValue::Iterator pref(*logging_prefs);
318 !pref.IsAtEnd(); pref.Advance()) {
319 std::string type = pref.key();
320 Log::Level level;
321 std::string level_name;
322 if (!pref.value().GetAsString(&level_name) ||
323 !WebDriverLog::NameToLevel(level_name, &level)) {
324 return Status(kUnknownError, "invalid log level for '" + type + "' log");
326 capabilities->logging_prefs.insert(std::make_pair(type, level));
328 return Status(kOk);
331 Status ParseInspectorDomainStatus(
332 PerfLoggingPrefs::InspectorDomainStatus* to_set,
333 const base::Value& option,
334 Capabilities* capabilities) {
335 bool desired_value;
336 if (!option.GetAsBoolean(&desired_value))
337 return Status(kUnknownError, "must be a boolean");
338 if (desired_value)
339 *to_set = PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled;
340 else
341 *to_set = PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyDisabled;
342 return Status(kOk);
345 Status ParsePerfLoggingPrefs(const base::Value& option,
346 Capabilities* capabilities) {
347 const base::DictionaryValue* perf_logging_prefs = NULL;
348 if (!option.GetAsDictionary(&perf_logging_prefs))
349 return Status(kUnknownError, "must be a dictionary");
351 std::map<std::string, Parser> parser_map;
352 parser_map["bufferUsageReportingInterval"] = base::Bind(&ParseInterval,
353 &capabilities->perf_logging_prefs.buffer_usage_reporting_interval);
354 parser_map["enableNetwork"] = base::Bind(
355 &ParseInspectorDomainStatus, &capabilities->perf_logging_prefs.network);
356 parser_map["enablePage"] = base::Bind(
357 &ParseInspectorDomainStatus, &capabilities->perf_logging_prefs.page);
358 parser_map["enableTimeline"] = base::Bind(
359 &ParseInspectorDomainStatus, &capabilities->perf_logging_prefs.timeline);
360 parser_map["traceCategories"] = base::Bind(
361 &ParseString, &capabilities->perf_logging_prefs.trace_categories);
363 for (base::DictionaryValue::Iterator it(*perf_logging_prefs); !it.IsAtEnd();
364 it.Advance()) {
365 if (parser_map.find(it.key()) == parser_map.end())
366 return Status(kUnknownError, "unrecognized performance logging "
367 "option: " + it.key());
368 Status status = parser_map[it.key()].Run(it.value(), capabilities);
369 if (status.IsError())
370 return Status(kUnknownError, "cannot parse " + it.key(), status);
372 return Status(kOk);
375 Status ParseChromeOptions(
376 const base::Value& capability,
377 Capabilities* capabilities) {
378 const base::DictionaryValue* chrome_options = NULL;
379 if (!capability.GetAsDictionary(&chrome_options))
380 return Status(kUnknownError, "must be a dictionary");
382 bool is_android = chrome_options->HasKey("androidPackage");
383 bool is_remote = chrome_options->HasKey("debuggerAddress");
385 std::map<std::string, Parser> parser_map;
386 // Ignore 'args', 'binary' and 'extensions' capabilities by default, since the
387 // Java client always passes them.
388 parser_map["args"] = base::Bind(&IgnoreCapability);
389 parser_map["binary"] = base::Bind(&IgnoreCapability);
390 parser_map["extensions"] = base::Bind(&IgnoreCapability);
392 parser_map["perfLoggingPrefs"] = base::Bind(&ParsePerfLoggingPrefs);
394 if (is_android) {
395 parser_map["androidActivity"] =
396 base::Bind(&ParseString, &capabilities->android_activity);
397 parser_map["androidDeviceSerial"] =
398 base::Bind(&ParseString, &capabilities->android_device_serial);
399 parser_map["androidPackage"] =
400 base::Bind(&ParseString, &capabilities->android_package);
401 parser_map["androidProcess"] =
402 base::Bind(&ParseString, &capabilities->android_process);
403 parser_map["androidUseRunningApp"] =
404 base::Bind(&ParseBoolean, &capabilities->android_use_running_app);
405 parser_map["args"] = base::Bind(&ParseSwitches);
406 parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
407 parser_map["loadAsync"] = base::Bind(&IgnoreDeprecatedOption, "loadAsync");
408 } else if (is_remote) {
409 parser_map["debuggerAddress"] = base::Bind(&ParseUseRemoteBrowser);
410 } else {
411 parser_map["args"] = base::Bind(&ParseSwitches);
412 parser_map["binary"] = base::Bind(&ParseFilePath, &capabilities->binary);
413 parser_map["detach"] = base::Bind(&ParseBoolean, &capabilities->detach);
414 parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
415 parser_map["extensions"] = base::Bind(&ParseExtensions);
416 parser_map["forceDevToolsScreenshot"] = base::Bind(
417 &ParseBoolean, &capabilities->force_devtools_screenshot);
418 parser_map["loadAsync"] = base::Bind(&IgnoreDeprecatedOption, "loadAsync");
419 parser_map["localState"] =
420 base::Bind(&ParseDict, &capabilities->local_state);
421 parser_map["logPath"] = base::Bind(&ParseLogPath);
422 parser_map["minidumpPath"] =
423 base::Bind(&ParseString, &capabilities->minidump_path);
424 parser_map["mobileEmulation"] = base::Bind(&ParseMobileEmulation);
425 parser_map["prefs"] = base::Bind(&ParseDict, &capabilities->prefs);
428 for (base::DictionaryValue::Iterator it(*chrome_options); !it.IsAtEnd();
429 it.Advance()) {
430 if (parser_map.find(it.key()) == parser_map.end()) {
431 return Status(kUnknownError,
432 "unrecognized chrome option: " + it.key());
434 Status status = parser_map[it.key()].Run(it.value(), capabilities);
435 if (status.IsError())
436 return Status(kUnknownError, "cannot parse " + it.key(), status);
438 return Status(kOk);
441 } // namespace
443 Switches::Switches() {}
445 Switches::~Switches() {}
447 void Switches::SetSwitch(const std::string& name) {
448 SetSwitch(name, NativeString());
451 void Switches::SetSwitch(const std::string& name, const std::string& value) {
452 #if defined(OS_WIN)
453 SetSwitch(name, base::UTF8ToUTF16(value));
454 #else
455 switch_map_[name] = value;
456 #endif
459 void Switches::SetSwitch(const std::string& name, const base::string16& value) {
460 #if defined(OS_WIN)
461 switch_map_[name] = value;
462 #else
463 SetSwitch(name, base::UTF16ToUTF8(value));
464 #endif
467 void Switches::SetSwitch(const std::string& name, const base::FilePath& value) {
468 SetSwitch(name, value.value());
471 void Switches::SetFromSwitches(const Switches& switches) {
472 for (SwitchMap::const_iterator iter = switches.switch_map_.begin();
473 iter != switches.switch_map_.end();
474 ++iter) {
475 switch_map_[iter->first] = iter->second;
479 void Switches::SetUnparsedSwitch(const std::string& unparsed_switch) {
480 std::string value;
481 size_t equals_index = unparsed_switch.find('=');
482 if (equals_index != std::string::npos)
483 value = unparsed_switch.substr(equals_index + 1);
485 std::string name;
486 size_t start_index = 0;
487 if (unparsed_switch.substr(0, 2) == "--")
488 start_index = 2;
489 name = unparsed_switch.substr(start_index, equals_index - start_index);
491 SetSwitch(name, value);
494 void Switches::RemoveSwitch(const std::string& name) {
495 switch_map_.erase(name);
498 bool Switches::HasSwitch(const std::string& name) const {
499 return switch_map_.count(name) > 0;
502 std::string Switches::GetSwitchValue(const std::string& name) const {
503 NativeString value = GetSwitchValueNative(name);
504 #if defined(OS_WIN)
505 return base::UTF16ToUTF8(value);
506 #else
507 return value;
508 #endif
511 Switches::NativeString Switches::GetSwitchValueNative(
512 const std::string& name) const {
513 SwitchMap::const_iterator iter = switch_map_.find(name);
514 if (iter == switch_map_.end())
515 return NativeString();
516 return iter->second;
519 size_t Switches::GetSize() const {
520 return switch_map_.size();
523 void Switches::AppendToCommandLine(base::CommandLine* command) const {
524 for (SwitchMap::const_iterator iter = switch_map_.begin();
525 iter != switch_map_.end();
526 ++iter) {
527 command->AppendSwitchNative(iter->first, iter->second);
531 std::string Switches::ToString() const {
532 std::string str;
533 SwitchMap::const_iterator iter = switch_map_.begin();
534 while (iter != switch_map_.end()) {
535 str += "--" + iter->first;
536 std::string value = GetSwitchValue(iter->first);
537 if (value.length()) {
538 if (value.find(' ') != std::string::npos)
539 value = base::GetQuotedJSONString(value);
540 str += "=" + value;
542 ++iter;
543 if (iter == switch_map_.end())
544 break;
545 str += " ";
547 return str;
550 PerfLoggingPrefs::PerfLoggingPrefs()
551 : network(InspectorDomainStatus::kDefaultEnabled),
552 page(InspectorDomainStatus::kDefaultEnabled),
553 timeline(InspectorDomainStatus::kDefaultDisabled),
554 trace_categories(),
555 buffer_usage_reporting_interval(1000) {}
557 PerfLoggingPrefs::~PerfLoggingPrefs() {}
559 Capabilities::Capabilities()
560 : android_use_running_app(false),
561 detach(false),
562 force_devtools_screenshot(false) {}
564 Capabilities::~Capabilities() {}
566 bool Capabilities::IsAndroid() const {
567 return !android_package.empty();
570 bool Capabilities::IsRemoteBrowser() const {
571 return debugger_address.IsValid();
574 Status Capabilities::Parse(const base::DictionaryValue& desired_caps) {
575 std::map<std::string, Parser> parser_map;
576 parser_map["chromeOptions"] = base::Bind(&ParseChromeOptions);
577 parser_map["loggingPrefs"] = base::Bind(&ParseLoggingPrefs);
578 parser_map["proxy"] = base::Bind(&ParseProxy);
579 for (std::map<std::string, Parser>::iterator it = parser_map.begin();
580 it != parser_map.end(); ++it) {
581 const base::Value* capability = NULL;
582 if (desired_caps.Get(it->first, &capability)) {
583 Status status = it->second.Run(*capability, this);
584 if (status.IsError()) {
585 return Status(
586 kUnknownError, "cannot parse capability: " + it->first, status);
590 // Perf log must be enabled if perf log prefs are specified; otherwise, error.
591 LoggingPrefs::const_iterator iter = logging_prefs.find(
592 WebDriverLog::kPerformanceType);
593 if (iter == logging_prefs.end() || iter->second == Log::kOff) {
594 const base::DictionaryValue* chrome_options = NULL;
595 if (desired_caps.GetDictionary("chromeOptions", &chrome_options) &&
596 chrome_options->HasKey("perfLoggingPrefs")) {
597 return Status(kUnknownError, "perfLoggingPrefs specified, "
598 "but performance logging was not enabled");
601 return Status(kOk);