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