blob: 4cca6c3c2a76fe2649ffeef3fb24f42d48f6ad46 [file] [log] [blame]
// Copyright 2016 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 "services/catalog/entry.h"
#include "base/values.h"
#include "services/catalog/store.h"
#include "services/shell/public/cpp/names.h"
namespace catalog {
namespace {
bool ReadStringSet(const base::ListValue& list_value,
std::set<std::string>* string_set) {
DCHECK(string_set);
for (const auto& value_value : list_value) {
std::string value;
if (!value_value->GetAsString(&value)) {
LOG(ERROR) << "Entry::Deserialize: list member must be a string";
return false;
}
string_set->insert(value);
}
return true;
}
bool ReadStringSetFromValue(const base::Value& value,
std::set<std::string>* string_set) {
const base::ListValue* list_value = nullptr;
if (!value.GetAsList(&list_value)) {
LOG(ERROR) << "Entry::Deserialize: Value must be a list.";
return false;
}
return ReadStringSet(*list_value, string_set);
}
bool ReadStringSetFromDictionary(const base::DictionaryValue& dictionary,
const std::string& key,
std::set<std::string>* string_set) {
const base::ListValue* list_value = nullptr;
if (dictionary.HasKey(key) && !dictionary.GetList(key, &list_value)) {
LOG(ERROR) << "Entry::Deserialize: " << key << " must be a list.";
return false;
}
if (list_value)
return ReadStringSet(*list_value, string_set);
return true;
}
bool BuildCapabilities(const base::DictionaryValue& value,
shell::CapabilitySpec* capabilities) {
DCHECK(capabilities);
const base::DictionaryValue* provided_value = nullptr;
if (value.HasKey(Store::kCapabilities_ProvidedKey) &&
!value.GetDictionary(Store::kCapabilities_ProvidedKey,
&provided_value)) {
LOG(ERROR) << "Entry::Deserialize: " << Store::kCapabilities_ProvidedKey
<< " must be a dictionary.";
return false;
}
if (provided_value) {
shell::CapabilityRequest provided;
base::DictionaryValue::Iterator it(*provided_value);
for(; !it.IsAtEnd(); it.Advance()) {
shell::Interfaces interfaces;
if (!ReadStringSetFromValue(it.value(), &interfaces)) {
LOG(ERROR) << "Entry::Deserialize: Invalid interface list in provided "
<< " classes dictionary";
return false;
}
capabilities->provided[it.key()] = interfaces;
}
}
const base::DictionaryValue* required_value = nullptr;
if (value.HasKey(Store::kCapabilities_RequiredKey) &&
!value.GetDictionary(Store::kCapabilities_RequiredKey,
&required_value)) {
LOG(ERROR) << "Entry::Deserialize: " << Store::kCapabilities_RequiredKey
<< " must be a dictionary.";
return false;
}
if (required_value) {
base::DictionaryValue::Iterator it(*required_value);
for (; !it.IsAtEnd(); it.Advance()) {
shell::CapabilityRequest spec;
const base::DictionaryValue* entry_value = nullptr;
if (!it.value().GetAsDictionary(&entry_value)) {
LOG(ERROR) << "Entry::Deserialize: " << Store::kCapabilities_RequiredKey
<< " must be a dictionary.";
return false;
}
if (!ReadStringSetFromDictionary(
*entry_value, Store::kCapabilities_ClassesKey, &spec.classes)) {
LOG(ERROR) << "Entry::Deserialize: Invalid classes list in required "
<< "capabilities dictionary.";
return false;
}
shell::Interfaces interfaces;
if (!ReadStringSetFromDictionary(*entry_value,
Store::kCapabilities_InterfacesKey,
&interfaces)) {
LOG(ERROR) << "Entry::Deserialize: Invalid interfaces list in required "
<< "capabilities dictionary.";
return false;
}
if (interfaces.count("*") > 0) {
LOG(ERROR) << "Entry::Deserializer: Wildcard not valid in interfaces "
<< "list.";
return false;
}
spec.interfaces = interfaces;
capabilities->required[it.key()] = spec;
}
}
return true;
}
} // namespace
Entry::Entry() {}
Entry::Entry(const std::string& name)
: name_(name), qualifier_(shell::GetNamePath(name)), display_name_(name) {}
Entry::~Entry() {}
std::unique_ptr<base::DictionaryValue> Entry::Serialize() const {
std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
value->SetInteger(Store::kManifestVersionKey, 1);
value->SetString(Store::kNameKey, name_);
value->SetString(Store::kDisplayNameKey, display_name_);
value->SetString(Store::kQualifierKey, qualifier_);
std::unique_ptr<base::DictionaryValue> spec(new base::DictionaryValue);
std::unique_ptr<base::DictionaryValue> provided(new base::DictionaryValue);
for (const auto& i : capabilities_.provided) {
std::unique_ptr<base::ListValue> interfaces(new base::ListValue);
for (const auto& interface_name : i.second)
interfaces->AppendString(interface_name);
provided->Set(i.first, std::move(interfaces));
}
spec->Set(Store::kCapabilities_ProvidedKey, std::move(provided));
std::unique_ptr<base::DictionaryValue> required(new base::DictionaryValue);
for (const auto& i : capabilities_.required) {
std::unique_ptr<base::DictionaryValue> request(new base::DictionaryValue);
std::unique_ptr<base::ListValue> classes(new base::ListValue);
for (const auto& class_name : i.second.classes)
classes->AppendString(class_name);
request->Set(Store::kCapabilities_ClassesKey, std::move(classes));
std::unique_ptr<base::ListValue> interfaces(new base::ListValue);
for (const auto& interface_name : i.second.interfaces)
interfaces->AppendString(interface_name);
request->Set(Store::kCapabilities_InterfacesKey, std::move(interfaces));
required->Set(i.first, std::move(request));
}
spec->Set(Store::kCapabilities_RequiredKey, std::move(required));
value->Set(Store::kCapabilitiesKey, std::move(spec));
return value;
}
// static
std::unique_ptr<Entry> Entry::Deserialize(const base::DictionaryValue& value) {
std::unique_ptr<Entry> entry(new Entry);
// Manifest version.
int manifest_version = 0;
if (!value.GetInteger(Store::kManifestVersionKey, &manifest_version)) {
LOG(ERROR) << "Entry::Deserialize: " << Store::kManifestVersionKey
<< " must be an integer.";
return nullptr;
}
if (manifest_version != 1) {
LOG(ERROR) << "Entry::Deserialize: Unsupported value of "
<< Store::kManifestVersionKey << ":" << manifest_version;
return nullptr;
}
// Name.
std::string name_string;
if (!value.GetString(Store::kNameKey, &name_string)) {
LOG(ERROR) << "Entry::Deserialize: dictionary has no "
<< Store::kNameKey << " key";
return nullptr;
}
if (!shell::IsValidName(name_string)) {
LOG(ERROR) << "Entry::Deserialize: " << name_string << " is not a valid "
<< "Mojo name";
return nullptr;
}
entry->set_name(name_string);
// Process group.
if (value.HasKey(Store::kQualifierKey)) {
std::string qualifier;
if (!value.GetString(Store::kQualifierKey, &qualifier)) {
LOG(ERROR) << "Entry::Deserialize: " << Store::kQualifierKey << " must "
<< "be a string.";
return nullptr;
}
entry->set_qualifier(qualifier);
} else {
entry->set_qualifier(shell::GetNamePath(name_string));
}
// Human-readable name.
std::string display_name;
if (!value.GetString(Store::kDisplayNameKey, &display_name)) {
LOG(ERROR) << "Entry::Deserialize: dictionary has no "
<< Store::kDisplayNameKey << " key";
return nullptr;
}
entry->set_display_name(display_name);
// Capability spec.
const base::DictionaryValue* capabilities = nullptr;
if (!value.GetDictionary(Store::kCapabilitiesKey, &capabilities)) {
LOG(ERROR) << "Entry::Deserialize: dictionary has no "
<< Store::kCapabilitiesKey << " key";
return nullptr;
}
shell::CapabilitySpec spec;
if (!BuildCapabilities(*capabilities, &spec)) {
LOG(ERROR) << "Entry::Deserialize: failed to build capability spec for "
<< entry->name();
return nullptr;
}
entry->set_capabilities(spec);
if (value.HasKey(Store::kServicesKey)) {
const base::ListValue* services = nullptr;
value.GetList(Store::kServicesKey, &services);
for (size_t i = 0; i < services->GetSize(); ++i) {
const base::DictionaryValue* service = nullptr;
services->GetDictionary(i, &service);
std::unique_ptr<Entry> child = Entry::Deserialize(*service);
if (child) {
child->set_package(entry.get());
// Caller must assume ownership of these items.
entry->children_.emplace_back(std::move(child));
}
}
}
return entry;
}
bool Entry::ProvidesClass(const std::string& clazz) const {
return capabilities_.provided.find(clazz) != capabilities_.provided.end();
}
bool Entry::operator==(const Entry& other) const {
return other.name_ == name_ && other.qualifier_ == qualifier_ &&
other.display_name_ == display_name_ &&
other.capabilities_ == capabilities_;
}
bool Entry::operator<(const Entry& other) const {
return std::tie(name_, qualifier_, display_name_, capabilities_) <
std::tie(other.name_, other.qualifier_, other.display_name_,
other.capabilities_);
}
} // catalog
namespace mojo {
// static
shell::mojom::ResolveResultPtr
TypeConverter<shell::mojom::ResolveResultPtr, catalog::Entry>::Convert(
const catalog::Entry& input) {
shell::mojom::ResolveResultPtr result(shell::mojom::ResolveResult::New());
result->name = input.name();
const catalog::Entry& package = input.package() ? *input.package() : input;
result->resolved_name = package.name();
result->qualifier = input.qualifier();
result->capabilities = input.capabilities();
result->package_path = package.path();
return result;
}
// static
catalog::mojom::EntryPtr
TypeConverter<catalog::mojom::EntryPtr, catalog::Entry>::Convert(
const catalog::Entry& input) {
catalog::mojom::EntryPtr result(catalog::mojom::Entry::New());
result->name = input.name();
result->display_name = input.display_name();
return result;
}
} // namespace mojo