| 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
|
|
|