OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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/webdriver/webdriver_capabilities_parser.h" |
| 6 |
| 7 #include "base/base64.h" |
| 8 #include "base/file_util.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/stringprintf.h" |
| 11 #include "base/values.h" |
| 12 #include "chrome/common/zip.h" |
| 13 #include "chrome/test/webdriver/webdriver_error.h" |
| 14 #include "chrome/test/webdriver/webdriver_util.h" |
| 15 |
| 16 using base::Value; |
| 17 |
| 18 namespace webdriver { |
| 19 |
| 20 namespace { |
| 21 |
| 22 bool WriteBase64DataToFile(const FilePath& filename, |
| 23 const std::string& base64data, |
| 24 std::string* error_msg) { |
| 25 std::string data; |
| 26 if (!base::Base64Decode(base64data, &data)) { |
| 27 *error_msg = "Invalid base64 encoded data."; |
| 28 return false; |
| 29 } |
| 30 if (!file_util::WriteFile(filename, data.c_str(), data.length())) { |
| 31 *error_msg = "Could not write data to file."; |
| 32 return false; |
| 33 } |
| 34 return true; |
| 35 } |
| 36 |
| 37 Error* CreateBadInputError(const std::string& name, |
| 38 Value::Type type, |
| 39 const Value* option) { |
| 40 return new Error(kBadRequest, base::StringPrintf( |
| 41 "%s must be of type %s, not %s", |
| 42 name.c_str(), GetJsonTypeName(type), |
| 43 GetJsonTypeName(option->GetType()))); |
| 44 } |
| 45 |
| 46 } // namespace |
| 47 |
| 48 Capabilities::Capabilities() |
| 49 : command(CommandLine::NO_PROGRAM), |
| 50 detach(false), |
| 51 load_async(false), |
| 52 native_events(false), |
| 53 verbose(false) { } |
| 54 |
| 55 Capabilities::~Capabilities() { } |
| 56 |
| 57 CapabilitiesParser::CapabilitiesParser( |
| 58 const base::DictionaryValue* capabilities_dict, |
| 59 const FilePath& root_path, |
| 60 Capabilities* capabilities) |
| 61 : dict_(capabilities_dict), |
| 62 root_(root_path), |
| 63 caps_(capabilities) { |
| 64 } |
| 65 |
| 66 CapabilitiesParser::~CapabilitiesParser() { } |
| 67 |
| 68 Error* CapabilitiesParser::Parse() { |
| 69 const char kOptionsKey[] = "chromeOptions"; |
| 70 bool legacy_options = !dict_->HasKey(kOptionsKey); |
| 71 const base::DictionaryValue* options = dict_; |
| 72 if (!legacy_options) { |
| 73 base::DictionaryValue* non_const_options; |
| 74 if (!dict_->GetDictionaryWithoutPathExpansion( |
| 75 kOptionsKey, &non_const_options)) { |
| 76 return new Error(kBadRequest, base::StringPrintf( |
| 77 "'%s' must be a dictionary.", kOptionsKey)); |
| 78 } |
| 79 options = non_const_options; |
| 80 } |
| 81 |
| 82 typedef Error* (CapabilitiesParser::*Parser)(const Value*); |
| 83 std::map<std::string, Parser> parser_map; |
| 84 if (legacy_options) { |
| 85 parser_map["chrome.binary"] = &CapabilitiesParser::ParseBinary; |
| 86 parser_map["chrome.channel"] = &CapabilitiesParser::ParseChannel; |
| 87 parser_map["chrome.detach"] = &CapabilitiesParser::ParseDetach; |
| 88 parser_map["chrome.extensions"] = &CapabilitiesParser::ParseExtensions; |
| 89 parser_map["chrome.loadAsync"] = &CapabilitiesParser::ParseLoadAsync; |
| 90 parser_map["chrome.nativeEvents"] = &CapabilitiesParser::ParseNativeEvents; |
| 91 parser_map["chrome.profile"] = &CapabilitiesParser::ParseProfile; |
| 92 parser_map["chrome.switches"] = &CapabilitiesParser::ParseArgs; |
| 93 parser_map["chrome.verbose"] = &CapabilitiesParser::ParseVerbose; |
| 94 } else { |
| 95 parser_map["args"] = &CapabilitiesParser::ParseArgs; |
| 96 parser_map["binary"] = &CapabilitiesParser::ParseBinary; |
| 97 parser_map["channel"] = &CapabilitiesParser::ParseChannel; |
| 98 parser_map["detach"] = &CapabilitiesParser::ParseDetach; |
| 99 parser_map["extensions"] = &CapabilitiesParser::ParseExtensions; |
| 100 parser_map["loadAsync"] = &CapabilitiesParser::ParseLoadAsync; |
| 101 parser_map["nativeEvents"] = &CapabilitiesParser::ParseNativeEvents; |
| 102 parser_map["profile"] = &CapabilitiesParser::ParseProfile; |
| 103 parser_map["verbose"] = &CapabilitiesParser::ParseVerbose; |
| 104 } |
| 105 |
| 106 base::DictionaryValue::key_iterator key_iter = options->begin_keys(); |
| 107 for (; key_iter != options->end_keys(); ++key_iter) { |
| 108 if (parser_map.find(*key_iter) == parser_map.end()) { |
| 109 LOG(WARNING) << "Ignoring unrecognized capability: " << *key_iter; |
| 110 continue; |
| 111 } |
| 112 Value* option = NULL; |
| 113 options->GetWithoutPathExpansion(*key_iter, &option); |
| 114 Error* error = (this->*parser_map[*key_iter])(option); |
| 115 if (error) { |
| 116 error->AddDetails(base::StringPrintf( |
| 117 "Error occurred while processing capability '%s'", |
| 118 (*key_iter).c_str())); |
| 119 return error; |
| 120 } |
| 121 } |
| 122 return NULL; |
| 123 } |
| 124 |
| 125 Error* CapabilitiesParser::ParseArgs(const Value* option) { |
| 126 const base::ListValue* args; |
| 127 if (!option->GetAsList(&args)) |
| 128 return CreateBadInputError("arguments", Value::TYPE_LIST, option); |
| 129 for (size_t i = 0; i < args->GetSize(); ++i) { |
| 130 std::string arg_string; |
| 131 if (!args->GetString(i, &arg_string)) |
| 132 return CreateBadInputError("argument", Value::TYPE_STRING, option); |
| 133 size_t separator_index = arg_string.find("="); |
| 134 if (separator_index != std::string::npos) { |
| 135 CommandLine::StringType arg_string_native; |
| 136 if (!args->GetString(i, &arg_string_native)) |
| 137 return CreateBadInputError("argument", Value::TYPE_STRING, option); |
| 138 caps_->command.AppendSwitchNative( |
| 139 arg_string.substr(0, separator_index), |
| 140 arg_string_native.substr(separator_index + 1)); |
| 141 } else { |
| 142 caps_->command.AppendSwitch(arg_string); |
| 143 } |
| 144 } |
| 145 return NULL; |
| 146 } |
| 147 |
| 148 Error* CapabilitiesParser::ParseBinary(const Value* option) { |
| 149 FilePath::StringType path; |
| 150 if (!option->GetAsString(&path)) { |
| 151 return CreateBadInputError("binary path", Value::TYPE_STRING, option); |
| 152 } |
| 153 caps_->command.SetProgram(FilePath(path)); |
| 154 return NULL; |
| 155 } |
| 156 |
| 157 Error* CapabilitiesParser::ParseChannel(const Value* option) { |
| 158 if (!option->GetAsString(&caps_->channel)) |
| 159 return CreateBadInputError("channel", Value::TYPE_STRING, option); |
| 160 return NULL; |
| 161 } |
| 162 |
| 163 Error* CapabilitiesParser::ParseDetach(const Value* option) { |
| 164 if (!option->GetAsBoolean(&caps_->detach)) |
| 165 return CreateBadInputError("detach", Value::TYPE_BOOLEAN, option); |
| 166 return NULL; |
| 167 } |
| 168 |
| 169 Error* CapabilitiesParser::ParseExtensions(const Value* option) { |
| 170 const base::ListValue* extensions; |
| 171 if (!option->GetAsList(&extensions)) |
| 172 return CreateBadInputError("extensions", Value::TYPE_LIST, option); |
| 173 for (size_t i = 0; i < extensions->GetSize(); ++i) { |
| 174 std::string extension_base64; |
| 175 if (!extensions->GetString(i, &extension_base64)) { |
| 176 return new Error(kBadRequest, |
| 177 "Each extension must be a base64 encoded string"); |
| 178 } |
| 179 FilePath extension; |
| 180 std::string error_msg; |
| 181 if (!DecodeAndWriteFile(extension_base64, false /* unzip */, |
| 182 &extension, &error_msg)) { |
| 183 return new Error( |
| 184 kUnknownError, |
| 185 "Error occurred while parsing extension: " + error_msg); |
| 186 } |
| 187 caps_->extensions.push_back(extension); |
| 188 } |
| 189 return NULL; |
| 190 } |
| 191 |
| 192 Error* CapabilitiesParser::ParseLoadAsync(const Value* option) { |
| 193 if (!option->GetAsBoolean(&caps_->load_async)) |
| 194 return CreateBadInputError("loadAsync", Value::TYPE_BOOLEAN, option); |
| 195 return NULL; |
| 196 } |
| 197 |
| 198 Error* CapabilitiesParser::ParseNativeEvents(const Value* option) { |
| 199 if (!option->GetAsBoolean(&caps_->native_events)) |
| 200 return CreateBadInputError("nativeEvents", Value::TYPE_BOOLEAN, option); |
| 201 return NULL; |
| 202 } |
| 203 |
| 204 Error* CapabilitiesParser::ParseProfile(const Value* option) { |
| 205 std::string profile_base64; |
| 206 if (!option->GetAsString(&profile_base64)) |
| 207 return CreateBadInputError("profile", Value::TYPE_STRING, option); |
| 208 std::string error_msg; |
| 209 if (!DecodeAndWriteFile(profile_base64, true /* unzip */, |
| 210 &caps_->profile, &error_msg)) |
| 211 return new Error(kUnknownError, "unable to unpack profile: " + error_msg); |
| 212 return NULL; |
| 213 } |
| 214 |
| 215 Error* CapabilitiesParser::ParseVerbose(const Value* option) { |
| 216 if (!option->GetAsBoolean(&caps_->verbose)) |
| 217 return CreateBadInputError("verbose", Value::TYPE_BOOLEAN, option); |
| 218 return NULL; |
| 219 } |
| 220 |
| 221 bool CapabilitiesParser::DecodeAndWriteFile( |
| 222 const std::string& base64, |
| 223 bool unzip, |
| 224 FilePath* path, |
| 225 std::string* error_msg) { |
| 226 FilePath base64_path = root_.AppendASCII(GenerateRandomID()); |
| 227 if (!WriteBase64DataToFile(base64_path, base64, error_msg)) |
| 228 return false; |
| 229 |
| 230 if (unzip) { |
| 231 FilePath unzipped_path = root_.AppendASCII(GenerateRandomID()); |
| 232 if (!Unzip(base64_path, unzipped_path)) { |
| 233 *error_msg = "Failed to unzip archive"; |
| 234 return false; |
| 235 } |
| 236 *path = unzipped_path; |
| 237 } else { |
| 238 *path = base64_path; |
| 239 } |
| 240 return true; |
| 241 } |
| 242 |
| 243 } // namespace webdriver |
OLD | NEW |