blob: 2b1a331004fb489e1e900a61f57d5e392cdc151f [file] [log] [blame]
// Copyright 2013 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 "extensions/common/manifest.h"
#include <utility>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/crx_file/id_util.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/features/feature_provider.h"
#include "extensions/common/install_warning.h"
#include "extensions/common/manifest_constants.h"
namespace extensions {
namespace keys = manifest_keys;
namespace {
// Rank extension locations in a way that allows
// Manifest::GetHigherPriorityLocation() to compare locations.
// An extension installed from two locations will have the location
// with the higher rank, as returned by this function. The actual
// integer values may change, and should never be persisted.
int GetLocationRank(Manifest::Location location) {
const int kInvalidRank = -1;
int rank = kInvalidRank; // Will CHECK that rank is not kInvalidRank.
switch (location) {
// Component extensions can not be overriden by any other type.
case Manifest::COMPONENT:
rank = 9;
break;
case Manifest::EXTERNAL_COMPONENT:
rank = 8;
break;
// Policy controlled extensions may not be overridden by any type
// that is not part of chrome.
case Manifest::EXTERNAL_POLICY:
rank = 7;
break;
case Manifest::EXTERNAL_POLICY_DOWNLOAD:
rank = 6;
break;
// A developer-loaded extension should override any installed type
// that a user can disable. Anything specified on the command-line should
// override one loaded via the extensions UI.
case Manifest::COMMAND_LINE:
rank = 5;
break;
case Manifest::UNPACKED:
rank = 4;
break;
// The relative priority of various external sources is not important,
// but having some order ensures deterministic behavior.
case Manifest::EXTERNAL_REGISTRY:
rank = 3;
break;
case Manifest::EXTERNAL_PREF:
rank = 2;
break;
case Manifest::EXTERNAL_PREF_DOWNLOAD:
rank = 1;
break;
// User installed extensions are overridden by any external type.
case Manifest::INTERNAL:
rank = 0;
break;
default:
NOTREACHED() << "Need to add new extension location " << location;
}
CHECK(rank != kInvalidRank);
return rank;
}
} // namespace
// static
Manifest::Location Manifest::GetHigherPriorityLocation(
Location loc1, Location loc2) {
if (loc1 == loc2)
return loc1;
int loc1_rank = GetLocationRank(loc1);
int loc2_rank = GetLocationRank(loc2);
// If two different locations have the same rank, then we can not
// deterministicly choose a location.
CHECK(loc1_rank != loc2_rank);
// Highest rank has highest priority.
return (loc1_rank > loc2_rank ? loc1 : loc2 );
}
Manifest::Manifest(Location location,
std::unique_ptr<base::DictionaryValue> value)
: location_(location), value_(std::move(value)), type_(TYPE_UNKNOWN) {
if (value_->HasKey(keys::kTheme)) {
type_ = TYPE_THEME;
} else if (value_->HasKey(keys::kExport)) {
type_ = TYPE_SHARED_MODULE;
} else if (value_->HasKey(keys::kApp)) {
if (value_->Get(keys::kWebURLs, NULL) ||
value_->Get(keys::kLaunchWebURL, NULL)) {
type_ = TYPE_HOSTED_APP;
} else if (value_->Get(keys::kPlatformAppBackground, NULL)) {
type_ = TYPE_PLATFORM_APP;
} else {
type_ = TYPE_LEGACY_PACKAGED_APP;
}
} else {
type_ = TYPE_EXTENSION;
}
CHECK_NE(type_, TYPE_UNKNOWN);
}
Manifest::~Manifest() {
}
void Manifest::SetExtensionId(const ExtensionId& id) {
extension_id_ = id;
hashed_id_ = HashedExtensionId(id);
}
bool Manifest::ValidateManifest(
std::string* error,
std::vector<InstallWarning>* warnings) const {
*error = "";
// Check every feature to see if its in the manifest. Note that this means
// we will ignore keys that are not features; we do this for forward
// compatibility.
// TODO(aa): Consider having an error here in the case of strict error
// checking to let developers know when they screw up.
const FeatureProvider* manifest_feature_provider =
FeatureProvider::GetManifestFeatures();
for (const auto& map_entry : manifest_feature_provider->GetAllFeatures()) {
// Use Get instead of HasKey because the former uses path expansion.
if (!value_->Get(map_entry.first, nullptr))
continue;
Feature::Availability result = map_entry.second->IsAvailableToManifest(
hashed_id_, type_, location_, GetManifestVersion());
if (!result.is_available())
warnings->push_back(InstallWarning(result.message(), map_entry.first));
}
// Also generate warnings for keys that are not features.
for (base::DictionaryValue::Iterator it(*value_); !it.IsAtEnd();
it.Advance()) {
if (!manifest_feature_provider->GetFeature(it.key())) {
warnings->push_back(InstallWarning(
ErrorUtils::FormatErrorMessage(
manifest_errors::kUnrecognizedManifestKey, it.key()),
it.key()));
}
}
return true;
}
bool Manifest::HasKey(const std::string& key) const {
return CanAccessKey(key) && value_->HasKey(key);
}
bool Manifest::HasPath(const std::string& path) const {
base::Value* ignored = NULL;
return CanAccessPath(path) && value_->Get(path, &ignored);
}
bool Manifest::Get(
const std::string& path, const base::Value** out_value) const {
return CanAccessPath(path) && value_->Get(path, out_value);
}
bool Manifest::GetBoolean(
const std::string& path, bool* out_value) const {
return CanAccessPath(path) && value_->GetBoolean(path, out_value);
}
bool Manifest::GetInteger(
const std::string& path, int* out_value) const {
return CanAccessPath(path) && value_->GetInteger(path, out_value);
}
bool Manifest::GetString(
const std::string& path, std::string* out_value) const {
return CanAccessPath(path) && value_->GetString(path, out_value);
}
bool Manifest::GetString(
const std::string& path, base::string16* out_value) const {
return CanAccessPath(path) && value_->GetString(path, out_value);
}
bool Manifest::GetDictionary(
const std::string& path, const base::DictionaryValue** out_value) const {
return CanAccessPath(path) && value_->GetDictionary(path, out_value);
}
bool Manifest::GetList(
const std::string& path, const base::ListValue** out_value) const {
return CanAccessPath(path) && value_->GetList(path, out_value);
}
Manifest* Manifest::DeepCopy() const {
Manifest* manifest = new Manifest(
location_, std::unique_ptr<base::DictionaryValue>(value_->DeepCopy()));
manifest->SetExtensionId(extension_id_);
return manifest;
}
bool Manifest::Equals(const Manifest* other) const {
return other && value_->Equals(other->value());
}
int Manifest::GetManifestVersion() const {
// Platform apps were launched after manifest version 2 was the preferred
// version, so they default to that.
int manifest_version = type_ == TYPE_PLATFORM_APP ? 2 : 1;
value_->GetInteger(keys::kManifestVersion, &manifest_version);
return manifest_version;
}
bool Manifest::CanAccessPath(const std::string& path) const {
std::string key;
for (const base::StringPiece& component : base::SplitStringPiece(
path, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
component.AppendToString(&key);
if (!CanAccessKey(key))
return false;
key += '.';
}
return true;
}
bool Manifest::CanAccessKey(const std::string& key) const {
const Feature* feature =
FeatureProvider::GetManifestFeatures()->GetFeature(key);
if (!feature)
return true;
return feature
->IsAvailableToManifest(hashed_id_, type_, location_,
GetManifestVersion())
.is_available();
}
} // namespace extensions