Index: chrome/test/webdriver/webdriver_capabilities_parser.cc |
diff --git a/chrome/test/webdriver/webdriver_capabilities_parser.cc b/chrome/test/webdriver/webdriver_capabilities_parser.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d958264fa51e1081bfe4fb1c83c603f280d56ca8 |
--- /dev/null |
+++ b/chrome/test/webdriver/webdriver_capabilities_parser.cc |
@@ -0,0 +1,243 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/test/webdriver/webdriver_capabilities_parser.h" |
+ |
+#include "base/base64.h" |
+#include "base/file_util.h" |
+#include "base/logging.h" |
+#include "base/stringprintf.h" |
+#include "base/values.h" |
+#include "chrome/common/zip.h" |
+#include "chrome/test/webdriver/webdriver_error.h" |
+#include "chrome/test/webdriver/webdriver_util.h" |
+ |
+using base::Value; |
+ |
+namespace webdriver { |
+ |
+namespace { |
+ |
+bool WriteBase64DataToFile(const FilePath& filename, |
+ const std::string& base64data, |
+ std::string* error_msg) { |
+ std::string data; |
+ if (!base::Base64Decode(base64data, &data)) { |
+ *error_msg = "Invalid base64 encoded data."; |
+ return false; |
+ } |
+ if (!file_util::WriteFile(filename, data.c_str(), data.length())) { |
+ *error_msg = "Could not write data to file."; |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+Error* CreateBadInputError(const std::string& name, |
+ Value::Type type, |
+ const Value* option) { |
+ return new Error(kBadRequest, base::StringPrintf( |
+ "%s must be of type %s, not %s", |
+ name.c_str(), GetJsonTypeName(type), |
+ GetJsonTypeName(option->GetType()))); |
+} |
+ |
+} // namespace |
+ |
+Capabilities::Capabilities() |
+ : command(CommandLine::NO_PROGRAM), |
+ detach(false), |
+ load_async(false), |
+ native_events(false), |
+ verbose(false) { } |
+ |
+Capabilities::~Capabilities() { } |
+ |
+CapabilitiesParser::CapabilitiesParser( |
+ const base::DictionaryValue* capabilities_dict, |
+ const FilePath& root_path, |
+ Capabilities* capabilities) |
+ : dict_(capabilities_dict), |
+ root_(root_path), |
+ caps_(capabilities) { |
+} |
+ |
+CapabilitiesParser::~CapabilitiesParser() { } |
+ |
+Error* CapabilitiesParser::Parse() { |
+ const char kOptionsKey[] = "chromeOptions"; |
+ bool legacy_options = !dict_->HasKey(kOptionsKey); |
+ const base::DictionaryValue* options = dict_; |
+ if (!legacy_options) { |
+ base::DictionaryValue* non_const_options; |
+ if (!dict_->GetDictionaryWithoutPathExpansion( |
+ kOptionsKey, &non_const_options)) { |
+ return new Error(kBadRequest, base::StringPrintf( |
+ "'%s' must be a dictionary.", kOptionsKey)); |
+ } |
+ options = non_const_options; |
+ } |
+ |
+ typedef Error* (CapabilitiesParser::*Parser)(const Value*); |
+ std::map<std::string, Parser> parser_map; |
+ if (legacy_options) { |
+ parser_map["chrome.binary"] = &CapabilitiesParser::ParseBinary; |
+ parser_map["chrome.channel"] = &CapabilitiesParser::ParseChannel; |
+ parser_map["chrome.detach"] = &CapabilitiesParser::ParseDetach; |
+ parser_map["chrome.extensions"] = &CapabilitiesParser::ParseExtensions; |
+ parser_map["chrome.loadAsync"] = &CapabilitiesParser::ParseLoadAsync; |
+ parser_map["chrome.nativeEvents"] = &CapabilitiesParser::ParseNativeEvents; |
+ parser_map["chrome.profile"] = &CapabilitiesParser::ParseProfile; |
+ parser_map["chrome.switches"] = &CapabilitiesParser::ParseArgs; |
+ parser_map["chrome.verbose"] = &CapabilitiesParser::ParseVerbose; |
+ } else { |
+ parser_map["args"] = &CapabilitiesParser::ParseArgs; |
+ parser_map["binary"] = &CapabilitiesParser::ParseBinary; |
+ parser_map["channel"] = &CapabilitiesParser::ParseChannel; |
+ parser_map["detach"] = &CapabilitiesParser::ParseDetach; |
+ parser_map["extensions"] = &CapabilitiesParser::ParseExtensions; |
+ parser_map["loadAsync"] = &CapabilitiesParser::ParseLoadAsync; |
+ parser_map["nativeEvents"] = &CapabilitiesParser::ParseNativeEvents; |
+ parser_map["profile"] = &CapabilitiesParser::ParseProfile; |
+ parser_map["verbose"] = &CapabilitiesParser::ParseVerbose; |
+ } |
+ |
+ base::DictionaryValue::key_iterator key_iter = options->begin_keys(); |
+ for (; key_iter != options->end_keys(); ++key_iter) { |
+ if (parser_map.find(*key_iter) == parser_map.end()) { |
+ LOG(WARNING) << "Ignoring unrecognized capability: " << *key_iter; |
+ continue; |
+ } |
+ Value* option = NULL; |
+ options->GetWithoutPathExpansion(*key_iter, &option); |
+ Error* error = (this->*parser_map[*key_iter])(option); |
+ if (error) { |
+ error->AddDetails(base::StringPrintf( |
+ "Error occurred while processing capability '%s'", |
+ (*key_iter).c_str())); |
+ return error; |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseArgs(const Value* option) { |
+ const base::ListValue* args; |
+ if (!option->GetAsList(&args)) |
+ return CreateBadInputError("arguments", Value::TYPE_LIST, option); |
+ for (size_t i = 0; i < args->GetSize(); ++i) { |
+ std::string arg_string; |
+ if (!args->GetString(i, &arg_string)) |
+ return CreateBadInputError("argument", Value::TYPE_STRING, option); |
+ size_t separator_index = arg_string.find("="); |
+ if (separator_index != std::string::npos) { |
+ CommandLine::StringType arg_string_native; |
+ if (!args->GetString(i, &arg_string_native)) |
+ return CreateBadInputError("argument", Value::TYPE_STRING, option); |
+ caps_->command.AppendSwitchNative( |
+ arg_string.substr(0, separator_index), |
+ arg_string_native.substr(separator_index + 1)); |
+ } else { |
+ caps_->command.AppendSwitch(arg_string); |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseBinary(const Value* option) { |
+ FilePath::StringType path; |
+ if (!option->GetAsString(&path)) { |
+ return CreateBadInputError("binary path", Value::TYPE_STRING, option); |
+ } |
+ caps_->command.SetProgram(FilePath(path)); |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseChannel(const Value* option) { |
+ if (!option->GetAsString(&caps_->channel)) |
+ return CreateBadInputError("channel", Value::TYPE_STRING, option); |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseDetach(const Value* option) { |
+ if (!option->GetAsBoolean(&caps_->detach)) |
+ return CreateBadInputError("detach", Value::TYPE_BOOLEAN, option); |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseExtensions(const Value* option) { |
+ const base::ListValue* extensions; |
+ if (!option->GetAsList(&extensions)) |
+ return CreateBadInputError("extensions", Value::TYPE_LIST, option); |
+ for (size_t i = 0; i < extensions->GetSize(); ++i) { |
+ std::string extension_base64; |
+ if (!extensions->GetString(i, &extension_base64)) { |
+ return new Error(kBadRequest, |
+ "Each extension must be a base64 encoded string"); |
+ } |
+ FilePath extension; |
+ std::string error_msg; |
+ if (!DecodeAndWriteFile(extension_base64, false /* unzip */, |
+ &extension, &error_msg)) { |
+ return new Error( |
+ kUnknownError, |
+ "Error occurred while parsing extension: " + error_msg); |
+ } |
+ caps_->extensions.push_back(extension); |
+ } |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseLoadAsync(const Value* option) { |
+ if (!option->GetAsBoolean(&caps_->load_async)) |
+ return CreateBadInputError("loadAsync", Value::TYPE_BOOLEAN, option); |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseNativeEvents(const Value* option) { |
+ if (!option->GetAsBoolean(&caps_->native_events)) |
+ return CreateBadInputError("nativeEvents", Value::TYPE_BOOLEAN, option); |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseProfile(const Value* option) { |
+ std::string profile_base64; |
+ if (!option->GetAsString(&profile_base64)) |
+ return CreateBadInputError("profile", Value::TYPE_STRING, option); |
+ std::string error_msg; |
+ if (!DecodeAndWriteFile(profile_base64, true /* unzip */, |
+ &caps_->profile, &error_msg)) |
+ return new Error(kUnknownError, "unable to unpack profile: " + error_msg); |
+ return NULL; |
+} |
+ |
+Error* CapabilitiesParser::ParseVerbose(const Value* option) { |
+ if (!option->GetAsBoolean(&caps_->verbose)) |
+ return CreateBadInputError("verbose", Value::TYPE_BOOLEAN, option); |
+ return NULL; |
+} |
+ |
+bool CapabilitiesParser::DecodeAndWriteFile( |
+ const std::string& base64, |
+ bool unzip, |
+ FilePath* path, |
+ std::string* error_msg) { |
+ FilePath base64_path = root_.AppendASCII(GenerateRandomID()); |
+ if (!WriteBase64DataToFile(base64_path, base64, error_msg)) |
+ return false; |
+ |
+ if (unzip) { |
+ FilePath unzipped_path = root_.AppendASCII(GenerateRandomID()); |
+ if (!Unzip(base64_path, unzipped_path)) { |
+ *error_msg = "Failed to unzip archive"; |
+ return false; |
+ } |
+ *path = unzipped_path; |
+ } else { |
+ *path = base64_path; |
+ } |
+ return true; |
+} |
+ |
+} // namespace webdriver |