diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 02:52:47 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 02:52:47 +0000 |
commit | 9cd8a1a9dc80aff30171a5d2bb77254b4a8f0dff (patch) | |
tree | f05eb00a4584bfcef8dc63be58334df4c7531867 | |
parent | f5bf184dd99e3e5331d6cdf979698a381dba1180 (diff) | |
download | chromium_src-9cd8a1a9dc80aff30171a5d2bb77254b4a8f0dff.zip chromium_src-9cd8a1a9dc80aff30171a5d2bb77254b4a8f0dff.tar.gz chromium_src-9cd8a1a9dc80aff30171a5d2bb77254b4a8f0dff.tar.bz2 |
Introduce the extensions::Feature class.
This will be used for declaring and validating manifest, permission, and API features in a generalized way.
Review URL: http://codereview.chromium.org/9386005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122010 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/chrome_common.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/extensions/feature.cc | 187 | ||||
-rw-r--r-- | chrome/common/extensions/feature.h | 125 | ||||
-rw-r--r-- | chrome/common/extensions/feature_unittest.cc | 258 |
5 files changed, 573 insertions, 0 deletions
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 08362d0..b6af34d 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -137,6 +137,8 @@ 'common/extensions/extension_set.h', 'common/extensions/extension_unpacker.cc', 'common/extensions/extension_unpacker.h', + 'common/extensions/feature.cc', + 'common/extensions/feature.h', 'common/extensions/file_browser_handler.cc', 'common/extensions/file_browser_handler.h', 'common/extensions/manifest.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 19d8288..d97b136 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2030,6 +2030,7 @@ 'common/extensions/extension_test_util.cc', 'common/extensions/extension_unittest.cc', 'common/extensions/extension_unpacker_unittest.cc', + 'common/extensions/feature_unittest.cc', 'common/extensions/manifest_unittest.cc', 'common/extensions/update_manifest_unittest.cc', 'common/extensions/url_pattern_set_unittest.cc', diff --git a/chrome/common/extensions/feature.cc b/chrome/common/extensions/feature.cc new file mode 100644 index 0000000..6556eb5 --- /dev/null +++ b/chrome/common/extensions/feature.cc @@ -0,0 +1,187 @@ +// 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/feature.h" + +#include <map> + +#include "base/lazy_instance.h" + +namespace { + +struct Mappings { + Mappings() { + extension_types["extension"] = Extension::TYPE_EXTENSION; + extension_types["theme"] = Extension::TYPE_THEME; + extension_types["packaged_app"] = Extension::TYPE_PACKAGED_APP; + extension_types["hosted_app"] = Extension::TYPE_HOSTED_APP; + extension_types["platform_app"] = Extension::TYPE_PLATFORM_APP; + + contexts["privileged"] = extensions::Feature::PRIVILEGED_CONTEXT; + contexts["unprivileged"] = extensions::Feature::UNPRIVILEGED_CONTEXT; + contexts["content_script"] = extensions::Feature::CONTENT_SCRIPT_CONTEXT; + + locations["component"] = extensions::Feature::COMPONENT_LOCATION; + + platforms["chromeos"] = extensions::Feature::CHROMEOS_PLATFORM; + } + + std::map<std::string, Extension::Type> extension_types; + std::map<std::string, extensions::Feature::Context> contexts; + std::map<std::string, extensions::Feature::Location> locations; + std::map<std::string, extensions::Feature::Platform> platforms; +}; + +static base::LazyInstance<Mappings> g_mappings = + LAZY_INSTANCE_INITIALIZER; + +// TODO(aa): Can we replace all this manual parsing with JSON schema stuff? + +void ParseSet(const DictionaryValue* value, + const std::string& property, + std::set<std::string>* set) { + ListValue* list_value = NULL; + if (!value->GetList(property, &list_value)) + return; + + 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 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 DictionaryValue* value, + const std::string& property, + std::set<T>* enum_set, + const std::map<std::string, T>& mapping) { + 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) { + if (*iter == "all") { + for (typename std::map<std::string, T>::const_iterator j = + mapping.begin(); j != mapping.end(); ++j) { + enum_set->insert(j->second); + } + return; + } + + T enum_value = static_cast<T>(0); + ParseEnum(*iter, &enum_value, mapping); + enum_set->insert(enum_value); + } +} + +} // namespace + +namespace extensions { + +Feature::Feature() + : location_(UNSPECIFIED_LOCATION), + platform_(UNSPECIFIED_PLATFORM), + min_manifest_version_(0), + max_manifest_version_(0) { +} + +Feature::~Feature() { +} + +// static +scoped_ptr<Feature> Feature::Parse(const DictionaryValue* value) { + scoped_ptr<Feature> feature(new Feature()); + + ParseSet(value, "whitelist", feature->whitelist()); + ParseEnumSet<Extension::Type>(value, "package_type", + feature->extension_types(), + g_mappings.Get().extension_types); + ParseEnumSet<Context>(value, "contexts", feature->contexts(), + g_mappings.Get().contexts); + ParseEnum<Location>(value, "location", &feature->location_, + g_mappings.Get().locations); + ParseEnum<Platform>(value, "platform", &feature->platform_, + g_mappings.Get().platforms); + + value->GetInteger("min_manifest_version", &feature->min_manifest_version_); + value->GetInteger("max_manifest_version", &feature->max_manifest_version_); + + return feature.Pass(); +} + +// static +Feature::Platform Feature::GetCurrentPlatform() { +#if defined(OS_CHROMEOS) + return CHROMEOS_PLATFORM; +#else + return UNSPECIFIED_PLATFORM; +#endif +} + +// static +Feature::Location Feature::ConvertLocation(Extension::Location location) { + if (location == Extension::COMPONENT) + return COMPONENT_LOCATION; + else + return UNSPECIFIED_LOCATION; +} + +bool Feature::IsAvailable(const std::string& extension_id, + Extension::Type type, + Location location, + Context context, + Platform platform, + int manifest_version) { + if (!whitelist_.empty() && + whitelist_.find(extension_id) == whitelist_.end()) { + return false; + } + + if (!extension_types_.empty() && + extension_types_.find(type) == extension_types_.end()) { + return false; + } + + if (!contexts_.empty() && + contexts_.find(context) == contexts_.end()) { + return false; + } + + if (location_ != UNSPECIFIED_LOCATION && location_ != location) + return false; + + if (platform_ != UNSPECIFIED_PLATFORM && platform_ != platform) + return false; + + if (min_manifest_version_ != 0 && manifest_version < min_manifest_version_) + return false; + + if (max_manifest_version_ != 0 && manifest_version > max_manifest_version_) + return false; + + return true; +} + +} // namespace diff --git a/chrome/common/extensions/feature.h b/chrome/common/extensions/feature.h new file mode 100644 index 0000000..0a95689 --- /dev/null +++ b/chrome/common/extensions/feature.h @@ -0,0 +1,125 @@ +// 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. + +#ifndef CHROME_COMMON_EXTENSIONS_FEATURE_H_ +#define CHROME_COMMON_EXTENSIONS_FEATURE_H_ +#pragma once + +#include <set> +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chrome/common/extensions/extension.h" + +namespace extensions { + +// Represents a single feature accessible to an extension developer, such as a +// top-level manifest key, a permission, or a programmatic API. A feature can +// express requirements for where it can be accessed, and supports testing +// support for those requirements. +class Feature { + public: + // The JavaScript contexts the feature is supported in. + enum Context { + UNSPECIFIED_CONTEXT, + PRIVILEGED_CONTEXT, // A context in a privileged extension process. + UNPRIVILEGED_CONTEXT, // A context in a normal, unprivileged renderer. + CONTENT_SCRIPT_CONTEXT // A context from a content script. + }; + + // The location required of extensions the feature is supported in. + enum Location { + UNSPECIFIED_LOCATION, + COMPONENT_LOCATION + }; + + // The platforms the feature is supported in. + enum Platform { + UNSPECIFIED_PLATFORM, + CHROMEOS_PLATFORM + }; + + Feature(); + ~Feature(); + + // Parses a feature from its JSON representation. + static scoped_ptr<Feature> Parse(const DictionaryValue* value); + + // Gets the platform the code is currently running on. + static Platform GetCurrentPlatform(); + + // Gets the Feature::Location value for the specified Extension::Location. + static Location ConvertLocation(Extension::Location extension_location); + + std::set<std::string>* whitelist() { return &whitelist_; } + std::set<Extension::Type>* extension_types() { return &extension_types_; } + std::set<Context>* contexts() { return &contexts_; } + + Location location() const { return location_; } + void set_location(Location location) { location_ = location; } + + Platform platform() const { return platform_; } + void set_platform(Platform platform) { platform_ = platform; } + + int min_manifest_version() const { return min_manifest_version_; } + void set_min_manifest_version(int min_manifest_version) { + min_manifest_version_ = min_manifest_version; + } + + int max_manifest_version() const { return max_manifest_version_; } + void set_max_manifest_version(int max_manifest_version) { + max_manifest_version_ = max_manifest_version; + } + + // Returns true if the feature is available to the specified extension. Use + // this overload for features that are not associated with a specific context. + bool IsAvailable(const Extension* extension) { + return IsAvailable(extension, UNSPECIFIED_CONTEXT); + } + + // Returns true if the feature is available to the specified extension, in the + // specified context type. + bool IsAvailable(const Extension* extension, Context context) { + return IsAvailable(extension->id(), extension->GetType(), + ConvertLocation(extension->location()), context, + GetCurrentPlatform(), + extension->manifest_version()); + } + + // Returns true if the feature is available to extensions with the specified + // properties. Use this overload for features that are not associated with a + // specific context, and when a full Extension object is not available. + bool IsAvailable(const std::string& extension_id, Extension::Type type, + Location location, int manifest_version) { + return IsAvailable(extension_id, type, location, UNSPECIFIED_CONTEXT, + GetCurrentPlatform(), manifest_version); + } + + // Returns true if the feature is available to extensions with the specified + // properties, in the specified context type, and on the specified platform. + // This overload is mainly used for testing. + bool IsAvailable(const std::string& extension_id, Extension::Type type, + Location location, Context context, + Platform platform, int manifest_version); + + private: + // For clarify and consistency, we handle the default value of each of these + // members the same way: it matches everything. It is up to the higher level + // code that reads Features out of static data to validate that data and set + // sensible defaults. + std::set<std::string> whitelist_; + std::set<Extension::Type> extension_types_; + std::set<Context> contexts_; + Location location_; // we only care about component/not-component now + Platform platform_; // we only care about chromeos/not-chromeos now + int min_manifest_version_; + int max_manifest_version_; + + DISALLOW_COPY_AND_ASSIGN(Feature); +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_FEATURE_H_ diff --git a/chrome/common/extensions/feature_unittest.cc b/chrome/common/extensions/feature_unittest.cc new file mode 100644 index 0000000..9fb2df9 --- /dev/null +++ b/chrome/common/extensions/feature_unittest.cc @@ -0,0 +1,258 @@ +// 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/feature.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using extensions::Feature; + +namespace { + +struct IsAvailableTestData { + std::string extension_id; + Extension::Type extension_type; + Feature::Context context; + Feature::Location location; + Feature::Platform platform; + int manifest_version; + bool expected_result; +}; + +} // namespace + +TEST(ExtensionFeatureTest, IsAvailableNullCase) { + const IsAvailableTestData tests[] = { + { "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_CONTEXT, + Feature::UNSPECIFIED_LOCATION, Feature::UNSPECIFIED_PLATFORM, -1, true }, + { "random-extension", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_CONTEXT, + Feature::UNSPECIFIED_LOCATION, Feature::UNSPECIFIED_PLATFORM, -1, true }, + { "", Extension::TYPE_PACKAGED_APP, Feature::UNSPECIFIED_CONTEXT, + Feature::UNSPECIFIED_LOCATION, Feature::UNSPECIFIED_PLATFORM, -1, true }, + { "", Extension::TYPE_UNKNOWN, Feature::PRIVILEGED_CONTEXT, + Feature::UNSPECIFIED_LOCATION, Feature::UNSPECIFIED_PLATFORM, -1, true }, + { "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_CONTEXT, + Feature::COMPONENT_LOCATION, Feature::UNSPECIFIED_PLATFORM, -1, true }, + { "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_CONTEXT, + Feature::UNSPECIFIED_LOCATION, Feature::CHROMEOS_PLATFORM, -1, true }, + { "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_CONTEXT, + Feature::UNSPECIFIED_LOCATION, Feature::UNSPECIFIED_PLATFORM, 25, true } + }; + + Feature feature; + for (size_t i = 0; i < arraysize(tests); ++i) { + const IsAvailableTestData& test = tests[i]; + EXPECT_EQ(test.expected_result, + feature.IsAvailable(test.extension_id, test.extension_type, + test.location, test.context, test.platform, + test.manifest_version)); + } +} + +TEST(ExtensionFeatureTest, Whitelist) { + Feature feature; + feature.whitelist()->insert("foo"); + feature.whitelist()->insert("bar"); + + EXPECT_TRUE(feature.IsAvailable( + "foo", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + EXPECT_TRUE(feature.IsAvailable( + "bar", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + + EXPECT_FALSE(feature.IsAvailable( + "baz", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + + feature.extension_types()->insert(Extension::TYPE_PACKAGED_APP); + EXPECT_FALSE(feature.IsAvailable( + "baz", Extension::TYPE_PACKAGED_APP, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); +} + +TEST(ExtensionFeatureTest, PackageType) { + Feature feature; + feature.extension_types()->insert(Extension::TYPE_EXTENSION); + feature.extension_types()->insert(Extension::TYPE_PACKAGED_APP); + + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_EXTENSION, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_PACKAGED_APP, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_THEME, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); +} + +TEST(ExtensionFeatureTest, Context) { + Feature feature; + feature.contexts()->insert(Feature::PRIVILEGED_CONTEXT); + feature.contexts()->insert(Feature::CONTENT_SCRIPT_CONTEXT); + + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::PRIVILEGED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::CONTENT_SCRIPT_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNPRIVILEGED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); +} + +TEST(ExtensionFeatureTest, Location) { + Feature feature; + feature.set_location(Feature::COMPONENT_LOCATION); + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::COMPONENT_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); +} + +TEST(ExtensionFeatureTest, Platform) { + Feature feature; + feature.set_platform(Feature::CHROMEOS_PLATFORM); + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::CHROMEOS_PLATFORM, -1)); + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, -1)); +} + +TEST(ExtensionFeatureTest, Version) { + Feature feature; + feature.set_min_manifest_version(5); + + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, 0)); + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, 4)); + + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, 5)); + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, 10)); + + feature.set_max_manifest_version(8); + + EXPECT_FALSE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, 10)); + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, 8)); + EXPECT_TRUE(feature.IsAvailable( + "", Extension::TYPE_UNKNOWN, Feature::UNSPECIFIED_LOCATION, + Feature::UNSPECIFIED_CONTEXT, Feature::UNSPECIFIED_PLATFORM, 7)); +} + +TEST(ExtensionFeatureTest, ParseNull) { + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + scoped_ptr<Feature> feature(Feature::Parse(value.get())); + EXPECT_TRUE(feature->whitelist()->empty()); + EXPECT_TRUE(feature->extension_types()->empty()); + EXPECT_TRUE(feature->contexts()->empty()); + EXPECT_EQ(Feature::UNSPECIFIED_LOCATION, feature->location()); + EXPECT_EQ(Feature::UNSPECIFIED_PLATFORM, feature->platform()); + EXPECT_EQ(0, feature->min_manifest_version()); + EXPECT_EQ(0, feature->max_manifest_version()); +} + +TEST(ExtensionFeatureTest, ParseWhitelist) { + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + ListValue* whitelist = new ListValue(); + whitelist->Append(Value::CreateStringValue("foo")); + whitelist->Append(Value::CreateStringValue("bar")); + value->Set("whitelist", whitelist); + scoped_ptr<Feature> feature(Feature::Parse(value.get())); + EXPECT_EQ(2u, feature->whitelist()->size()); + EXPECT_TRUE(feature->whitelist()->count("foo")); + EXPECT_TRUE(feature->whitelist()->count("bar")); +} + +TEST(ExtensionFeatureTest, ParsePackageTypes) { + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + ListValue* extension_types = new ListValue(); + extension_types->Append(Value::CreateStringValue("extension")); + extension_types->Append(Value::CreateStringValue("theme")); + extension_types->Append(Value::CreateStringValue("packaged_app")); + extension_types->Append(Value::CreateStringValue("hosted_app")); + extension_types->Append(Value::CreateStringValue("platform_app")); + value->Set("package_type", extension_types); + scoped_ptr<Feature> feature(Feature::Parse(value.get())); + EXPECT_EQ(5u, feature->extension_types()->size()); + EXPECT_TRUE(feature->extension_types()->count(Extension::TYPE_EXTENSION)); + EXPECT_TRUE(feature->extension_types()->count(Extension::TYPE_THEME)); + EXPECT_TRUE(feature->extension_types()->count(Extension::TYPE_PACKAGED_APP)); + EXPECT_TRUE(feature->extension_types()->count(Extension::TYPE_HOSTED_APP)); + EXPECT_TRUE(feature->extension_types()->count(Extension::TYPE_PLATFORM_APP)); + + extension_types->Clear(); + extension_types->Append(Value::CreateStringValue("all")); + scoped_ptr<Feature> feature2(Feature::Parse(value.get())); + EXPECT_EQ(*(feature->extension_types()), *(feature2->extension_types())); +} + +TEST(ExtensionFeatureTest, ParseContexts) { + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + ListValue* contexts = new ListValue(); + contexts->Append(Value::CreateStringValue("privileged")); + contexts->Append(Value::CreateStringValue("unprivileged")); + contexts->Append(Value::CreateStringValue("content_script")); + value->Set("contexts", contexts); + scoped_ptr<Feature> feature(Feature::Parse(value.get())); + EXPECT_EQ(3u, feature->contexts()->size()); + EXPECT_TRUE(feature->contexts()->count(Feature::PRIVILEGED_CONTEXT)); + EXPECT_TRUE(feature->contexts()->count(Feature::UNPRIVILEGED_CONTEXT)); + EXPECT_TRUE(feature->contexts()->count(Feature::CONTENT_SCRIPT_CONTEXT)); + + contexts->Clear(); + contexts->Append(Value::CreateStringValue("all")); + scoped_ptr<Feature> feature2(Feature::Parse(value.get())); + EXPECT_EQ(*(feature->contexts()), *(feature2->contexts())); +} + +TEST(ExtensionFeatureTest, ParseLocation) { + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + value->SetString("location", "component"); + scoped_ptr<Feature> feature(Feature::Parse(value.get())); + EXPECT_EQ(Feature::COMPONENT_LOCATION, feature->location()); +} + +TEST(ExtensionFeatureTest, ParsePlatform) { + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + value->SetString("platform", "chromeos"); + scoped_ptr<Feature> feature(Feature::Parse(value.get())); + EXPECT_EQ(Feature::CHROMEOS_PLATFORM, feature->platform()); +} + +TEST(ExtensionFeatureTest, ManifestVersion) { + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + value->SetInteger("min_manifest_version", 1); + value->SetInteger("max_manifest_version", 5); + scoped_ptr<Feature> feature(Feature::Parse(value.get())); + EXPECT_EQ(1, feature->min_manifest_version()); + EXPECT_EQ(5, feature->max_manifest_version()); +} |