| Index: chrome/common/extensions/features/simple_feature.cc
|
| diff --git a/chrome/common/extensions/features/simple_feature.cc b/chrome/common/extensions/features/simple_feature.cc
|
| deleted file mode 100644
|
| index 7491cd5fc168eab00c79c17dfdd6d3470f3430e2..0000000000000000000000000000000000000000
|
| --- a/chrome/common/extensions/features/simple_feature.cc
|
| +++ /dev/null
|
| @@ -1,482 +0,0 @@
|
| -// Copyright (c) 2012 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/common/extensions/features/simple_feature.h"
|
| -
|
| -#include <map>
|
| -#include <vector>
|
| -
|
| -#include "base/command_line.h"
|
| -#include "base/lazy_instance.h"
|
| -#include "base/sha1.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "extensions/common/switches.h"
|
| -
|
| -namespace extensions {
|
| -
|
| -namespace {
|
| -
|
| -struct Mappings {
|
| - Mappings() {
|
| - extension_types["extension"] = Manifest::TYPE_EXTENSION;
|
| - extension_types["theme"] = Manifest::TYPE_THEME;
|
| - extension_types["legacy_packaged_app"] = Manifest::TYPE_LEGACY_PACKAGED_APP;
|
| - extension_types["hosted_app"] = Manifest::TYPE_HOSTED_APP;
|
| - extension_types["platform_app"] = Manifest::TYPE_PLATFORM_APP;
|
| - extension_types["shared_module"] = Manifest::TYPE_SHARED_MODULE;
|
| -
|
| - contexts["blessed_extension"] = Feature::BLESSED_EXTENSION_CONTEXT;
|
| - contexts["unblessed_extension"] = Feature::UNBLESSED_EXTENSION_CONTEXT;
|
| - contexts["content_script"] = Feature::CONTENT_SCRIPT_CONTEXT;
|
| - contexts["web_page"] = Feature::WEB_PAGE_CONTEXT;
|
| - contexts["blessed_web_page"] = Feature::BLESSED_WEB_PAGE_CONTEXT;
|
| -
|
| - locations["component"] = Feature::COMPONENT_LOCATION;
|
| -
|
| - platforms["chromeos"] = Feature::CHROMEOS_PLATFORM;
|
| - platforms["linux"] = Feature::LINUX_PLATFORM;
|
| - platforms["mac"] = Feature::MACOSX_PLATFORM;
|
| - platforms["win"] = Feature::WIN_PLATFORM;
|
| - }
|
| -
|
| - std::map<std::string, Manifest::Type> extension_types;
|
| - std::map<std::string, Feature::Context> contexts;
|
| - std::map<std::string, Feature::Location> locations;
|
| - std::map<std::string, Feature::Platform> platforms;
|
| -};
|
| -
|
| -base::LazyInstance<Mappings> g_mappings = LAZY_INSTANCE_INITIALIZER;
|
| -
|
| -// TODO(aa): Can we replace all this manual parsing with JSON schema stuff?
|
| -
|
| -void ParseSet(const base::DictionaryValue* value,
|
| - const std::string& property,
|
| - std::set<std::string>* set) {
|
| - const base::ListValue* list_value = NULL;
|
| - if (!value->GetList(property, &list_value))
|
| - return;
|
| -
|
| - set->clear();
|
| - for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| - std::string str_val;
|
| - CHECK(list_value->GetString(i, &str_val)) << property << " " << i;
|
| - set->insert(str_val);
|
| - }
|
| -}
|
| -
|
| -template<typename T>
|
| -void ParseEnum(const std::string& string_value,
|
| - T* enum_value,
|
| - const std::map<std::string, T>& mapping) {
|
| - typename std::map<std::string, T>::const_iterator iter =
|
| - mapping.find(string_value);
|
| - CHECK(iter != mapping.end()) << string_value;
|
| - *enum_value = iter->second;
|
| -}
|
| -
|
| -template<typename T>
|
| -void ParseEnum(const base::DictionaryValue* value,
|
| - const std::string& property,
|
| - T* enum_value,
|
| - const std::map<std::string, T>& mapping) {
|
| - std::string string_value;
|
| - if (!value->GetString(property, &string_value))
|
| - return;
|
| -
|
| - ParseEnum(string_value, enum_value, mapping);
|
| -}
|
| -
|
| -template<typename T>
|
| -void ParseEnumSet(const base::DictionaryValue* value,
|
| - const std::string& property,
|
| - std::set<T>* enum_set,
|
| - const std::map<std::string, T>& mapping) {
|
| - if (!value->HasKey(property))
|
| - return;
|
| -
|
| - enum_set->clear();
|
| -
|
| - std::string property_string;
|
| - if (value->GetString(property, &property_string)) {
|
| - if (property_string == "all") {
|
| - for (typename std::map<std::string, T>::const_iterator j =
|
| - mapping.begin(); j != mapping.end(); ++j) {
|
| - enum_set->insert(j->second);
|
| - }
|
| - }
|
| - return;
|
| - }
|
| -
|
| - std::set<std::string> string_set;
|
| - ParseSet(value, property, &string_set);
|
| - for (std::set<std::string>::iterator iter = string_set.begin();
|
| - iter != string_set.end(); ++iter) {
|
| - T enum_value = static_cast<T>(0);
|
| - ParseEnum(*iter, &enum_value, mapping);
|
| - enum_set->insert(enum_value);
|
| - }
|
| -}
|
| -
|
| -void ParseURLPatterns(const base::DictionaryValue* value,
|
| - const std::string& key,
|
| - URLPatternSet* set) {
|
| - const base::ListValue* matches = NULL;
|
| - if (value->GetList(key, &matches)) {
|
| - set->ClearPatterns();
|
| - for (size_t i = 0; i < matches->GetSize(); ++i) {
|
| - std::string pattern;
|
| - CHECK(matches->GetString(i, &pattern));
|
| - set->AddPattern(URLPattern(URLPattern::SCHEME_ALL, pattern));
|
| - }
|
| - }
|
| -}
|
| -
|
| -// Gets a human-readable name for the given extension type, suitable for giving
|
| -// to developers in an error message.
|
| -std::string GetDisplayName(Manifest::Type type) {
|
| - switch (type) {
|
| - case Manifest::TYPE_UNKNOWN:
|
| - return "unknown";
|
| - case Manifest::TYPE_EXTENSION:
|
| - return "extension";
|
| - case Manifest::TYPE_HOSTED_APP:
|
| - return "hosted app";
|
| - case Manifest::TYPE_LEGACY_PACKAGED_APP:
|
| - return "legacy packaged app";
|
| - case Manifest::TYPE_PLATFORM_APP:
|
| - return "packaged app";
|
| - case Manifest::TYPE_THEME:
|
| - return "theme";
|
| - case Manifest::TYPE_USER_SCRIPT:
|
| - return "user script";
|
| - case Manifest::TYPE_SHARED_MODULE:
|
| - return "shared module";
|
| - }
|
| - NOTREACHED();
|
| - return "";
|
| -}
|
| -
|
| -// Gets a human-readable name for the given context type, suitable for giving
|
| -// to developers in an error message.
|
| -std::string GetDisplayName(Feature::Context context) {
|
| - switch (context) {
|
| - case Feature::UNSPECIFIED_CONTEXT:
|
| - return "unknown";
|
| - case Feature::BLESSED_EXTENSION_CONTEXT:
|
| - // "privileged" is vague but hopefully the developer will understand that
|
| - // means background or app window.
|
| - return "privileged page";
|
| - case Feature::UNBLESSED_EXTENSION_CONTEXT:
|
| - // "iframe" is a bit of a lie/oversimplification, but that's the most
|
| - // common unblessed context.
|
| - return "extension iframe";
|
| - case Feature::CONTENT_SCRIPT_CONTEXT:
|
| - return "content script";
|
| - case Feature::WEB_PAGE_CONTEXT:
|
| - return "web page";
|
| - case Feature::BLESSED_WEB_PAGE_CONTEXT:
|
| - return "hosted app";
|
| - }
|
| - NOTREACHED();
|
| - return "";
|
| -}
|
| -
|
| -// Gets a human-readable list of the display names (pluralized, comma separated
|
| -// with the "and" in the correct place) for each of |enum_types|.
|
| -template <typename EnumType>
|
| -std::string ListDisplayNames(const std::vector<EnumType> enum_types) {
|
| - std::string display_name_list;
|
| - for (size_t i = 0; i < enum_types.size(); ++i) {
|
| - // Pluralize type name.
|
| - display_name_list += GetDisplayName(enum_types[i]) + "s";
|
| - // Comma-separate entries, with an Oxford comma if there is more than 2
|
| - // total entries.
|
| - if (enum_types.size() > 2) {
|
| - if (i < enum_types.size() - 2)
|
| - display_name_list += ", ";
|
| - else if (i == enum_types.size() - 2)
|
| - display_name_list += ", and ";
|
| - } else if (enum_types.size() == 2 && i == 0) {
|
| - display_name_list += " and ";
|
| - }
|
| - }
|
| - return display_name_list;
|
| -}
|
| -
|
| -std::string HashExtensionId(const std::string& extension_id) {
|
| - const std::string id_hash = base::SHA1HashString(extension_id);
|
| - DCHECK(id_hash.length() == base::kSHA1Length);
|
| - return base::HexEncode(id_hash.c_str(), id_hash.length());
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -SimpleFeature::SimpleFeature()
|
| - : location_(UNSPECIFIED_LOCATION),
|
| - min_manifest_version_(0),
|
| - max_manifest_version_(0),
|
| - has_parent_(false) {}
|
| -
|
| -SimpleFeature::~SimpleFeature() {}
|
| -
|
| -void SimpleFeature::AddFilter(scoped_ptr<SimpleFeatureFilter> filter) {
|
| - filters_.push_back(make_linked_ptr(filter.release()));
|
| -}
|
| -
|
| -std::string SimpleFeature::Parse(const base::DictionaryValue* value) {
|
| - ParseURLPatterns(value, "matches", &matches_);
|
| - ParseSet(value, "whitelist", &whitelist_);
|
| - ParseSet(value, "dependencies", &dependencies_);
|
| - ParseEnumSet<Manifest::Type>(value, "extension_types", &extension_types_,
|
| - g_mappings.Get().extension_types);
|
| - ParseEnumSet<Context>(value, "contexts", &contexts_,
|
| - g_mappings.Get().contexts);
|
| - ParseEnum<Location>(value, "location", &location_,
|
| - g_mappings.Get().locations);
|
| - ParseEnumSet<Platform>(value, "platforms", &platforms_,
|
| - g_mappings.Get().platforms);
|
| - value->GetInteger("min_manifest_version", &min_manifest_version_);
|
| - value->GetInteger("max_manifest_version", &max_manifest_version_);
|
| -
|
| - no_parent_ = false;
|
| - value->GetBoolean("noparent", &no_parent_);
|
| -
|
| - if (matches_.is_empty() && contexts_.count(WEB_PAGE_CONTEXT) != 0) {
|
| - return name() + ": Allowing web_page contexts requires supplying a value " +
|
| - "for matches.";
|
| - }
|
| -
|
| - for (FilterList::iterator filter_iter = filters_.begin();
|
| - filter_iter != filters_.end();
|
| - ++filter_iter) {
|
| - std::string result = (*filter_iter)->Parse(value);
|
| - if (!result.empty()) {
|
| - return result;
|
| - }
|
| - }
|
| -
|
| - return std::string();
|
| -}
|
| -
|
| -Feature::Availability SimpleFeature::IsAvailableToManifest(
|
| - const std::string& extension_id,
|
| - Manifest::Type type,
|
| - Location location,
|
| - int manifest_version,
|
| - Platform platform) const {
|
| - // Check extension type first to avoid granting platform app permissions
|
| - // to component extensions.
|
| - // HACK(kalman): user script -> extension. Solve this in a more generic way
|
| - // when we compile feature files.
|
| - Manifest::Type type_to_check = (type == Manifest::TYPE_USER_SCRIPT) ?
|
| - Manifest::TYPE_EXTENSION : type;
|
| - if (!extension_types_.empty() &&
|
| - extension_types_.find(type_to_check) == extension_types_.end()) {
|
| - return CreateAvailability(INVALID_TYPE, type);
|
| - }
|
| -
|
| - // Component extensions can access any feature.
|
| - if (location == COMPONENT_LOCATION)
|
| - return CreateAvailability(IS_AVAILABLE, type);
|
| -
|
| - if (!whitelist_.empty()) {
|
| - if (!IsIdInWhitelist(extension_id)) {
|
| - // TODO(aa): This is gross. There should be a better way to test the
|
| - // whitelist.
|
| - CommandLine* command_line = CommandLine::ForCurrentProcess();
|
| - if (!command_line->HasSwitch(switches::kWhitelistedExtensionID))
|
| - return CreateAvailability(NOT_FOUND_IN_WHITELIST, type);
|
| -
|
| - std::string whitelist_switch_value =
|
| - CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
|
| - switches::kWhitelistedExtensionID);
|
| - if (extension_id != whitelist_switch_value)
|
| - return CreateAvailability(NOT_FOUND_IN_WHITELIST, type);
|
| - }
|
| - }
|
| -
|
| - if (location_ != UNSPECIFIED_LOCATION && location_ != location)
|
| - return CreateAvailability(INVALID_LOCATION, type);
|
| -
|
| - if (!platforms_.empty() &&
|
| - platforms_.find(platform) == platforms_.end())
|
| - return CreateAvailability(INVALID_PLATFORM, type);
|
| -
|
| - if (min_manifest_version_ != 0 && manifest_version < min_manifest_version_)
|
| - return CreateAvailability(INVALID_MIN_MANIFEST_VERSION, type);
|
| -
|
| - if (max_manifest_version_ != 0 && manifest_version > max_manifest_version_)
|
| - return CreateAvailability(INVALID_MAX_MANIFEST_VERSION, type);
|
| -
|
| - for (FilterList::const_iterator filter_iter = filters_.begin();
|
| - filter_iter != filters_.end();
|
| - ++filter_iter) {
|
| - Availability availability = (*filter_iter)->IsAvailableToManifest(
|
| - extension_id, type, location, manifest_version, platform);
|
| - if (!availability.is_available())
|
| - return availability;
|
| - }
|
| -
|
| - return CreateAvailability(IS_AVAILABLE, type);
|
| -}
|
| -
|
| -Feature::Availability SimpleFeature::IsAvailableToContext(
|
| - const Extension* extension,
|
| - SimpleFeature::Context context,
|
| - const GURL& url,
|
| - SimpleFeature::Platform platform) const {
|
| - if (extension) {
|
| - Availability result = IsAvailableToManifest(
|
| - extension->id(),
|
| - extension->GetType(),
|
| - ConvertLocation(extension->location()),
|
| - extension->manifest_version(),
|
| - platform);
|
| - if (!result.is_available())
|
| - return result;
|
| - }
|
| -
|
| - if (!contexts_.empty() && contexts_.find(context) == contexts_.end())
|
| - return CreateAvailability(INVALID_CONTEXT, context);
|
| -
|
| - if (!matches_.is_empty() && !matches_.MatchesURL(url))
|
| - return CreateAvailability(INVALID_URL, url);
|
| -
|
| - for (FilterList::const_iterator filter_iter = filters_.begin();
|
| - filter_iter != filters_.end();
|
| - ++filter_iter) {
|
| - Availability availability =
|
| - (*filter_iter)->IsAvailableToContext(extension, context, url, platform);
|
| - if (!availability.is_available())
|
| - return availability;
|
| - }
|
| -
|
| - return CreateAvailability(IS_AVAILABLE);
|
| -}
|
| -
|
| -std::string SimpleFeature::GetAvailabilityMessage(
|
| - AvailabilityResult result,
|
| - Manifest::Type type,
|
| - const GURL& url,
|
| - Context context) const {
|
| - switch (result) {
|
| - case IS_AVAILABLE:
|
| - return std::string();
|
| - case NOT_FOUND_IN_WHITELIST:
|
| - return base::StringPrintf(
|
| - "'%s' is not allowed for specified extension ID.",
|
| - name().c_str());
|
| - case INVALID_URL:
|
| - return base::StringPrintf("'%s' is not allowed on %s.",
|
| - name().c_str(), url.spec().c_str());
|
| - case INVALID_TYPE:
|
| - return base::StringPrintf(
|
| - "'%s' is only allowed for %s, but this is a %s.",
|
| - name().c_str(),
|
| - ListDisplayNames(std::vector<Manifest::Type>(
|
| - extension_types_.begin(), extension_types_.end())).c_str(),
|
| - GetDisplayName(type).c_str());
|
| - case INVALID_CONTEXT:
|
| - return base::StringPrintf(
|
| - "'%s' is only allowed to run in %s, but this is a %s",
|
| - name().c_str(),
|
| - ListDisplayNames(std::vector<Context>(
|
| - contexts_.begin(), contexts_.end())).c_str(),
|
| - GetDisplayName(context).c_str());
|
| - case INVALID_LOCATION:
|
| - return base::StringPrintf(
|
| - "'%s' is not allowed for specified install location.",
|
| - name().c_str());
|
| - case INVALID_PLATFORM:
|
| - return base::StringPrintf(
|
| - "'%s' is not allowed for specified platform.",
|
| - name().c_str());
|
| - case INVALID_MIN_MANIFEST_VERSION:
|
| - return base::StringPrintf(
|
| - "'%s' requires manifest version of at least %d.",
|
| - name().c_str(),
|
| - min_manifest_version_);
|
| - case INVALID_MAX_MANIFEST_VERSION:
|
| - return base::StringPrintf(
|
| - "'%s' requires manifest version of %d or lower.",
|
| - name().c_str(),
|
| - max_manifest_version_);
|
| - case NOT_PRESENT:
|
| - return base::StringPrintf(
|
| - "'%s' requires a different Feature that is not present.",
|
| - name().c_str());
|
| - case UNSUPPORTED_CHANNEL:
|
| - return base::StringPrintf(
|
| - "'%s' is unsupported in this version of the platform.",
|
| - name().c_str());
|
| - }
|
| -
|
| - NOTREACHED();
|
| - return std::string();
|
| -}
|
| -
|
| -Feature::Availability SimpleFeature::CreateAvailability(
|
| - AvailabilityResult result) const {
|
| - return Availability(
|
| - result, GetAvailabilityMessage(result, Manifest::TYPE_UNKNOWN, GURL(),
|
| - UNSPECIFIED_CONTEXT));
|
| -}
|
| -
|
| -Feature::Availability SimpleFeature::CreateAvailability(
|
| - AvailabilityResult result, Manifest::Type type) const {
|
| - return Availability(result, GetAvailabilityMessage(result, type, GURL(),
|
| - UNSPECIFIED_CONTEXT));
|
| -}
|
| -
|
| -Feature::Availability SimpleFeature::CreateAvailability(
|
| - AvailabilityResult result,
|
| - const GURL& url) const {
|
| - return Availability(
|
| - result, GetAvailabilityMessage(result, Manifest::TYPE_UNKNOWN, url,
|
| - UNSPECIFIED_CONTEXT));
|
| -}
|
| -
|
| -Feature::Availability SimpleFeature::CreateAvailability(
|
| - AvailabilityResult result,
|
| - Context context) const {
|
| - return Availability(
|
| - result, GetAvailabilityMessage(result, Manifest::TYPE_UNKNOWN, GURL(),
|
| - context));
|
| -}
|
| -
|
| -std::set<Feature::Context>* SimpleFeature::GetContexts() {
|
| - return &contexts_;
|
| -}
|
| -
|
| -bool SimpleFeature::IsInternal() const {
|
| - return false;
|
| -}
|
| -
|
| -bool SimpleFeature::IsBlockedInServiceWorker() const { return false; }
|
| -
|
| -bool SimpleFeature::IsIdInWhitelist(const std::string& extension_id) const {
|
| - return IsIdInWhitelist(extension_id, whitelist_);
|
| -}
|
| -
|
| -// static
|
| -bool SimpleFeature::IsIdInWhitelist(const std::string& extension_id,
|
| - const std::set<std::string>& whitelist) {
|
| - // Belt-and-suspenders philosophy here. We should be pretty confident by this
|
| - // point that we've validated the extension ID format, but in case something
|
| - // slips through, we avoid a class of attack where creative ID manipulation
|
| - // leads to hash collisions.
|
| - if (extension_id.length() != 32) // 128 bits / 4 = 32 mpdecimal characters
|
| - return false;
|
| -
|
| - if (whitelist.find(extension_id) != whitelist.end() ||
|
| - whitelist.find(HashExtensionId(extension_id)) != whitelist.end()) {
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -} // namespace extensions
|
|
|