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"
10 #include "base/callback.h"
11 #include "base/string_util.h"
12 #include "base/stringprintf.h"
13 #include "base/values.h"
14 #include "chrome/test/chromedriver/chrome/status.h"
18 typedef base::Callback
<Status(const base::Value
&, Capabilities
*)> Parser
;
20 Status
ParseChromeBinary(
21 const base::Value
& option
,
22 Capabilities
* capabilities
) {
23 base::FilePath::StringType path_str
;
24 if (!option
.GetAsString(&path_str
))
25 return Status(kUnknownError
, "'binary' must be a string");
26 base::FilePath
chrome_exe(path_str
);
27 capabilities
->command
.SetProgram(chrome_exe
);
31 Status
ParseLogPath(const base::Value
& option
, Capabilities
* capabilities
) {
32 if (!option
.GetAsString(&capabilities
->log_path
))
33 return Status(kUnknownError
, "'logPath' must be a string");
37 Status
ParseArgs(const base::Value
& option
, Capabilities
* capabilities
) {
38 const base::ListValue
* args_list
= NULL
;
39 if (!option
.GetAsList(&args_list
))
40 return Status(kUnknownError
, "'args' must be a list");
41 for (size_t i
= 0; i
< args_list
->GetSize(); ++i
) {
42 std::string arg_string
;
43 if (!args_list
->GetString(i
, &arg_string
))
44 return Status(kUnknownError
, "each argument must be a string");
45 size_t separator_index
= arg_string
.find("=");
46 if (separator_index
!= std::string::npos
) {
47 CommandLine::StringType arg_string_native
;
48 if (!args_list
->GetString(i
, &arg_string_native
))
49 return Status(kUnknownError
, "each argument must be a string");
50 capabilities
->command
.AppendSwitchNative(
51 arg_string
.substr(0, separator_index
),
52 arg_string_native
.substr(separator_index
+ 1));
54 capabilities
->command
.AppendSwitch(arg_string
);
60 Status
ParsePrefs(const base::Value
& option
, Capabilities
* capabilities
) {
61 const base::DictionaryValue
* prefs
= NULL
;
62 if (!option
.GetAsDictionary(&prefs
))
63 return Status(kUnknownError
, "'prefs' must be a dictionary");
64 capabilities
->prefs
.reset(prefs
->DeepCopy());
68 Status
ParseLocalState(const base::Value
& option
, Capabilities
* capabilities
) {
69 const base::DictionaryValue
* local_state
= NULL
;
70 if (!option
.GetAsDictionary(&local_state
))
71 return Status(kUnknownError
, "'localState' must be a dictionary");
72 capabilities
->local_state
.reset(local_state
->DeepCopy());
76 Status
ParseExtensions(const base::Value
& option
, Capabilities
* capabilities
) {
77 const base::ListValue
* extensions
= NULL
;
78 if (!option
.GetAsList(&extensions
))
79 return Status(kUnknownError
, "'extensions' must be a list");
80 for (size_t i
= 0; i
< extensions
->GetSize(); ++i
) {
81 std::string extension
;
82 if (!extensions
->GetString(i
, &extension
)) {
83 return Status(kUnknownError
,
84 "each extension must be a base64 encoded string");
86 capabilities
->extensions
.push_back(extension
);
91 Status
ParseProxy(const base::Value
& option
, Capabilities
* capabilities
) {
92 const base::DictionaryValue
* proxy_dict
;
93 if (!option
.GetAsDictionary(&proxy_dict
))
94 return Status(kUnknownError
, "'proxy' must be a dictionary");
95 std::string proxy_type
;
96 if (!proxy_dict
->GetString("proxyType", &proxy_type
))
97 return Status(kUnknownError
, "'proxyType' must be a string");
98 proxy_type
= StringToLowerASCII(proxy_type
);
99 if (proxy_type
== "direct") {
100 capabilities
->command
.AppendSwitch("no-proxy-server");
101 } else if (proxy_type
== "system") {
103 } else if (proxy_type
== "pac") {
104 CommandLine::StringType proxy_pac_url
;
105 if (!proxy_dict
->GetString("proxyAutoconfigUrl", &proxy_pac_url
))
106 return Status(kUnknownError
, "'proxyAutoconfigUrl' must be a string");
107 capabilities
->command
.AppendSwitchNative("proxy-pac-url", proxy_pac_url
);
108 } else if (proxy_type
== "autodetect") {
109 capabilities
->command
.AppendSwitch("proxy-auto-detect");
110 } else if (proxy_type
== "manual") {
111 const char* proxy_servers_options
[][2] = {
112 {"ftpProxy", "ftp"}, {"httpProxy", "http"}, {"sslProxy", "https"}};
113 const base::Value
* option_value
= NULL
;
114 std::string proxy_servers
;
115 for (size_t i
= 0; i
< arraysize(proxy_servers_options
); ++i
) {
116 if (!proxy_dict
->Get(proxy_servers_options
[i
][0], &option_value
) ||
117 option_value
->IsType(base::Value::TYPE_NULL
)) {
121 if (!option_value
->GetAsString(&value
)) {
124 base::StringPrintf("'%s' must be a string",
125 proxy_servers_options
[i
][0]));
127 // Converts into Chrome proxy scheme.
128 // Example: "http=localhost:9000;ftp=localhost:8000".
129 if (!proxy_servers
.empty())
130 proxy_servers
+= ";";
131 proxy_servers
+= base::StringPrintf(
132 "%s=%s", proxy_servers_options
[i
][1], value
.c_str());
135 std::string proxy_bypass_list
;
136 if (proxy_dict
->Get("noProxy", &option_value
) &&
137 !option_value
->IsType(base::Value::TYPE_NULL
)) {
138 if (!option_value
->GetAsString(&proxy_bypass_list
))
139 return Status(kUnknownError
, "'noProxy' must be a string");
142 if (proxy_servers
.empty() && proxy_bypass_list
.empty()) {
143 return Status(kUnknownError
,
144 "proxyType is 'manual' but no manual "
145 "proxy capabilities were found");
147 if (!proxy_servers
.empty())
148 capabilities
->command
.AppendSwitchASCII("proxy-server", proxy_servers
);
149 if (!proxy_bypass_list
.empty()) {
150 capabilities
->command
.AppendSwitchASCII("proxy-bypass-list",
154 return Status(kUnknownError
, "unrecognized proxy type:" + proxy_type
);
159 Status
ParseDesktopChromeOption(
160 const base::Value
& capability
,
161 Capabilities
* capabilities
) {
162 const base::DictionaryValue
* chrome_options
= NULL
;
163 if (!capability
.GetAsDictionary(&chrome_options
))
164 return Status(kUnknownError
, "'chromeOptions' must be a dictionary");
166 std::map
<std::string
, Parser
> parser_map
;
168 parser_map
["binary"] = base::Bind(&ParseChromeBinary
);
169 parser_map
["logPath"] = base::Bind(&ParseLogPath
);
170 parser_map
["args"] = base::Bind(&ParseArgs
);
171 parser_map
["prefs"] = base::Bind(&ParsePrefs
);
172 parser_map
["localState"] = base::Bind(&ParseLocalState
);
173 parser_map
["extensions"] = base::Bind(&ParseExtensions
);
175 for (base::DictionaryValue::Iterator
it(*chrome_options
); !it
.IsAtEnd();
177 if (parser_map
.find(it
.key()) == parser_map
.end()) {
178 return Status(kUnknownError
,
179 "unrecognized chrome option: " + it
.key());
181 Status status
= parser_map
[it
.key()].Run(it
.value(), capabilities
);
182 if (status
.IsError())
188 Status
ParseAndroidChromeCapabilities(const base::DictionaryValue
& desired_caps
,
189 Capabilities
* capabilities
) {
190 const base::Value
* chrome_options
= NULL
;
191 if (desired_caps
.Get("chromeOptions", &chrome_options
)) {
192 const base::DictionaryValue
* chrome_options_dict
= NULL
;
193 if (!chrome_options
->GetAsDictionary(&chrome_options_dict
))
194 return Status(kUnknownError
, "'chromeOptions' must be a dictionary");
196 const base::Value
* android_package_value
;
197 if (chrome_options_dict
->Get("androidPackage", &android_package_value
)) {
198 if (!android_package_value
->GetAsString(&capabilities
->android_package
) ||
199 capabilities
->android_package
.empty()) {
200 return Status(kUnknownError
,
201 "'androidPackage' must be a non-empty string");
208 Status
ParseLoggingPrefs(const base::DictionaryValue
& desired_caps
,
209 Capabilities
* capabilities
) {
210 const base::Value
* logging_prefs
= NULL
;
211 if (desired_caps
.Get("loggingPrefs", &logging_prefs
)) {
212 const base::DictionaryValue
* logging_prefs_dict
= NULL
;
213 if (!logging_prefs
->GetAsDictionary(&logging_prefs_dict
)) {
214 return Status(kUnknownError
, "'loggingPrefs' must be a dictionary");
216 // TODO(klm): verify log types.
217 // TODO(klm): verify log levels.
218 capabilities
->logging_prefs
.reset(logging_prefs_dict
->DeepCopy());
225 Capabilities::Capabilities() : command(CommandLine::NO_PROGRAM
) {}
227 Capabilities::~Capabilities() {}
229 bool Capabilities::IsAndroid() const {
230 return !android_package
.empty();
233 Status
Capabilities::Parse(const base::DictionaryValue
& desired_caps
) {
234 Status status
= ParseLoggingPrefs(desired_caps
, this);
235 if (status
.IsError())
237 status
= ParseAndroidChromeCapabilities(desired_caps
, this);
238 if (status
.IsError())
243 std::map
<std::string
, Parser
> parser_map
;
244 parser_map
["proxy"] = base::Bind(&ParseProxy
);
245 parser_map
["chromeOptions"] = base::Bind(&ParseDesktopChromeOption
);
246 for (std::map
<std::string
, Parser
>::iterator it
= parser_map
.begin();
247 it
!= parser_map
.end(); ++it
) {
248 const base::Value
* capability
= NULL
;
249 if (desired_caps
.Get(it
->first
, &capability
)) {
250 status
= it
->second
.Run(*capability
, this);
251 if (status
.IsError())