summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorcduvall@chromium.org <cduvall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-27 06:53:45 +0000
committercduvall@chromium.org <cduvall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-27 06:53:45 +0000
commitd6e42fa5e90c85aa093b012b6836ed287411f652 (patch)
tree73823e4202084edd62cbeee9e48cc1ca04dbc278 /chrome
parente2f9fd10fc89e70f6aaad4901da3240b2a51691a (diff)
downloadchromium_src-d6e42fa5e90c85aa093b012b6836ed287411f652.zip
chromium_src-d6e42fa5e90c85aa093b012b6836ed287411f652.tar.gz
chromium_src-d6e42fa5e90c85aa093b012b6836ed287411f652.tar.bz2
Implement API features for the Extension API feature system
Now features can be declared in the _api_features.json file, and dependencies of the form "api:<api_name>" will refer to these features. This patch also prevents schemas for APIs using the feature system from being loaded when calling ExtensionAPI::IsAvailable(). TBR=ben@chromium.org BUG=55316, 120070 Review URL: https://chromiumcodereview.appspot.com/12846011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190836 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/chrome_common.gypi2
-rw-r--r--chrome/common/common_resources.grd1
-rw-r--r--chrome/common/extensions/api/_api_features.json19
-rw-r--r--chrome/common/extensions/api/bookmarks.json4
-rw-r--r--chrome/common/extensions/api/extension_api.cc154
-rw-r--r--chrome/common/extensions/api/extension_api.h29
-rw-r--r--chrome/common/extensions/api/extension_api_unittest.cc187
-rw-r--r--chrome/common/extensions/api/webstore.json11
-rw-r--r--chrome/common/extensions/features/api_feature.cc26
-rw-r--r--chrome/common/extensions/features/api_feature.h22
-rw-r--r--chrome/common/extensions/features/base_feature_provider.cc31
-rw-r--r--chrome/common/extensions/features/base_feature_provider.h4
-rw-r--r--chrome/common/extensions/features/base_feature_provider_unittest.cc12
-rw-r--r--chrome/common/extensions/features/feature.cc2
-rw-r--r--chrome/common/extensions/features/feature.h4
-rw-r--r--chrome/common/extensions/features/manifest_feature.cc16
-rw-r--r--chrome/common/extensions/features/manifest_feature.h2
-rw-r--r--chrome/common/extensions/features/permission_feature.cc16
-rw-r--r--chrome/common/extensions/features/permission_feature.h2
-rw-r--r--chrome/common/extensions/features/simple_feature.cc8
-rw-r--r--chrome/common/extensions/features/simple_feature.h5
-rw-r--r--chrome/test/data/extensions/extension_api_unittest/api_features.json54
-rw-r--r--chrome/test/data/extensions/extension_api_unittest/is_privileged_features_1.json8
-rw-r--r--chrome/test/data/extensions/extension_api_unittest/is_privileged_features_2.json8
-rw-r--r--chrome/test/data/extensions/extension_api_unittest/is_privileged_features_3.json8
-rw-r--r--chrome/test/data/extensions/extension_api_unittest/is_privileged_features_4.json18
-rw-r--r--chrome/test/data/extensions/extension_api_unittest/is_privileged_features_5.json18
-rw-r--r--chrome/test/data/extensions/extension_api_unittest/privileged_api_features.json32
28 files changed, 396 insertions, 307 deletions
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index de40865..044a1d8 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -223,6 +223,8 @@
'common/extensions/extension_set.h',
'common/extensions/feature_switch.cc',
'common/extensions/feature_switch.h',
+ 'common/extensions/features/api_feature.cc',
+ 'common/extensions/features/api_feature.h',
'common/extensions/features/base_feature_provider.cc',
'common/extensions/features/base_feature_provider.h',
'common/extensions/features/complex_feature.cc',
diff --git a/chrome/common/common_resources.grd b/chrome/common/common_resources.grd
index bc7c186..06f100f 100644
--- a/chrome/common/common_resources.grd
+++ b/chrome/common/common_resources.grd
@@ -9,6 +9,7 @@
</outputs>
<release seq="1">
<includes>
+ <include name="IDR_EXTENSION_API_FEATURES" file="extensions\api\_api_features.json" type="BINDATA" />
<include name="IDR_EXTENSION_MANIFEST_FEATURES" file="extensions\api\_manifest_features.json" type="BINDATA" />
<include name="IDR_EXTENSION_PERMISSION_FEATURES" file="extensions\api\_permission_features.json" type="BINDATA" />
<include name="IDR_WEB_APP_SCHEMA" file="web_app_schema.json" type="BINDATA" />
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
new file mode 100644
index 0000000..8657cdc
--- /dev/null
+++ b/chrome/common/extensions/api/_api_features.json
@@ -0,0 +1,19 @@
+// 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.
+
+{
+ "bookmarks": {
+ "channel": "stable",
+ "dependencies": ["permission:bookmarks"],
+ "contexts": ["blessed_extension"]
+ },
+ "webstore": {
+ // Hosted apps can use the webstore API from within a blessed context.
+ "channel": "stable",
+ "extension_types": ["hosted_app"],
+ "contexts": ["blessed_extension", "web_page"],
+ // Any webpage can use the webstore API.
+ "matches": [ "http://*/*", "https://*/*" ]
+ }
+}
diff --git a/chrome/common/extensions/api/bookmarks.json b/chrome/common/extensions/api/bookmarks.json
index 3ade90c..61ce218 100644
--- a/chrome/common/extensions/api/bookmarks.json
+++ b/chrome/common/extensions/api/bookmarks.json
@@ -5,10 +5,6 @@
[
{
"namespace": "bookmarks",
- "uses_feature_system": true,
- "channel": "stable",
- "dependencies": ["permission:bookmarks"],
- "contexts": ["blessed_extension"],
"properties": {
"MAX_WRITE_OPERATIONS_PER_HOUR": {
"value": 100,
diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc
index c2a9cce..d2c2b78 100644
--- a/chrome/common/extensions/api/extension_api.cc
+++ b/chrome/common/extensions/api/extension_api.cc
@@ -234,11 +234,6 @@ void ExtensionAPI::SplitDependencyName(const std::string& full_name,
*feature_name = full_name.substr(colon_index + 1);
}
-bool ExtensionAPI::UsesFeatureSystem(const std::string& full_name) {
- std::string api_name = GetAPINameFromFullName(full_name, NULL);
- return features_.find(api_name) != features_.end();
-}
-
void ExtensionAPI::LoadSchema(const std::string& name,
const base::StringPiece& schema) {
scoped_ptr<ListValue> schema_list(LoadSchemaList(name, schema));
@@ -332,10 +327,6 @@ void ExtensionAPI::LoadSchema(const std::string& name,
}
ExtensionAPI::ExtensionAPI() {
- RegisterDependencyProvider("api", this);
-
- // TODO(aa): Can remove this when all JSON files are converted.
- RegisterDependencyProvider("", this);
}
ExtensionAPI::~ExtensionAPI() {
@@ -343,6 +334,8 @@ ExtensionAPI::~ExtensionAPI() {
void ExtensionAPI::InitDefaultConfiguration() {
RegisterDependencyProvider(
+ "api", BaseFeatureProvider::GetApiFeatures());
+ RegisterDependencyProvider(
"manifest", BaseFeatureProvider::GetManifestFeatures());
RegisterDependencyProvider(
"permission", BaseFeatureProvider::GetPermissionFeatures());
@@ -422,27 +415,34 @@ Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name,
const Extension* extension,
Feature::Context context,
const GURL& url) {
- std::set<std::string> dependency_names;
- dependency_names.insert(full_name);
- ResolveDependencies(&dependency_names);
+ std::string feature_type;
+ std::string feature_name;
+ SplitDependencyName(full_name, &feature_type, &feature_name);
+
+ std::string child_name;
+ std::string api_name = GetAPINameFromFullName(feature_name, &child_name);
+
+ Feature* feature = GetFeatureDependency(full_name);
// Check APIs not using the feature system first.
- if (!UsesFeatureSystem(full_name)) {
- return IsNonFeatureAPIAvailable(full_name, context, extension, url) ?
+ if (!feature) {
+ return IsNonFeatureAPIAvailable(api_name, context, extension, url) ?
Feature::CreateAvailability(Feature::IS_AVAILABLE, "") :
Feature::CreateAvailability(Feature::INVALID_CONTEXT,
kUnavailableMessage);
}
- for (std::set<std::string>::iterator iter = dependency_names.begin();
- iter != dependency_names.end(); ++iter) {
- Feature* feature = GetFeatureDependency(*iter);
- CHECK(feature) << *iter;
-
- Feature::Availability availability =
- feature->IsAvailableToContext(extension, context, url);
- if (!availability.is_available())
- return availability;
+ Feature::Availability availability =
+ feature->IsAvailableToContext(extension, context, url);
+ if (!availability.is_available())
+ return availability;
+
+ for (std::set<std::string>::iterator iter = feature->dependencies().begin();
+ iter != feature->dependencies().end(); ++iter) {
+ Feature::Availability dependency_availability =
+ IsAvailable(*iter, extension, context, url);
+ if (!dependency_availability.is_available())
+ return dependency_availability;
}
return Feature::CreateAvailability(Feature::IS_AVAILABLE, "");
@@ -451,33 +451,21 @@ Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name,
bool ExtensionAPI::IsPrivileged(const std::string& full_name) {
std::string child_name;
std::string api_name = GetAPINameFromFullName(full_name, &child_name);
+ Feature* feature = GetFeatureDependency(full_name);
// First try to use the feature system.
- Feature* feature(GetFeature(full_name));
if (feature) {
- // An API is 'privileged' if it or any of its dependencies can only be run
- // in a blessed context.
- std::set<std::string> resolved_dependencies;
- resolved_dependencies.insert(full_name);
- ResolveDependencies(&resolved_dependencies);
- for (std::set<std::string>::iterator iter = resolved_dependencies.begin();
- iter != resolved_dependencies.end(); ++iter) {
- Feature* dependency = GetFeatureDependency(*iter);
- for (std::set<Feature::Context>::iterator context =
- dependency->GetContexts()->begin();
- context != dependency->GetContexts()->end(); ++context) {
- if (*context != Feature::BLESSED_EXTENSION_CONTEXT)
- return false;
- }
- }
- return true;
+ // An API is 'privileged' if it can only be run in a blessed context.
+ return feature->GetContexts()->size() ==
+ feature->GetContexts()->count(Feature::BLESSED_EXTENSION_CONTEXT);
}
+ // Get the schema now to populate |completely_unprivileged_apis_|.
+ const DictionaryValue* schema = GetSchema(api_name);
// If this API hasn't been converted yet, fall back to the old system.
if (completely_unprivileged_apis_.count(api_name))
return false;
- const DictionaryValue* schema = GetSchema(api_name);
if (partially_unprivileged_apis_.count(api_name))
return IsChildNamePrivileged(schema, child_name);
@@ -549,6 +537,8 @@ bool ExtensionAPI::IsNonFeatureAPIAvailable(const std::string& name,
Feature::Context context,
const Extension* extension,
const GURL& url) {
+ // Make sure schema is loaded.
+ GetSchema(name);
switch (context) {
case Feature::UNSPECIFIED_CONTEXT:
break;
@@ -597,36 +587,6 @@ std::set<std::string> ExtensionAPI::GetAllAPINames() {
return result;
}
-Feature* ExtensionAPI::GetFeature(const std::string& full_name) {
- // Ensure it's loaded.
- GetSchema(full_name);
-
- std::string child_name;
- std::string api_namespace = GetAPINameFromFullName(full_name, &child_name);
-
- APIFeatureMap::iterator feature_map = features_.find(api_namespace);
- if (feature_map == features_.end())
- return NULL;
-
- Feature* result = NULL;
- FeatureMap::iterator child_feature = feature_map->second->find(child_name);
- if (child_feature != feature_map->second->end()) {
- result = child_feature->second.get();
- } else {
- FeatureMap::iterator parent_feature = feature_map->second->find("");
- CHECK(parent_feature != feature_map->second->end());
- result = parent_feature->second.get();
- }
-
- if (result->GetContexts()->empty()) {
- LOG(ERROR) << "API feature '" << full_name
- << "' must specify at least one context.";
- return NULL;
- }
-
- return result;
-}
-
Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) {
std::string feature_type;
std::string feature_name;
@@ -634,11 +594,16 @@ Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) {
FeatureProviderMap::iterator provider =
dependency_providers_.find(feature_type);
- CHECK(provider != dependency_providers_.end()) << full_name;
+ if (provider == dependency_providers_.end())
+ return NULL;
Feature* feature = provider->second->GetFeature(feature_name);
- CHECK(feature) << full_name;
-
+ // Try getting the feature for the parent API, if this was a child.
+ if (!feature) {
+ std::string child_name;
+ feature = provider->second->GetFeature(
+ GetAPINameFromFullName(feature_name, &child_name));
+ }
return feature;
}
@@ -678,47 +643,6 @@ bool ExtensionAPI::IsAPIAllowed(const std::string& name,
extension->optional_permission_set()->HasAnyAccessToAPI(name);
}
-void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) {
- std::set<std::string> missing_dependencies;
- for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i)
- GetMissingDependencies(*i, *out, &missing_dependencies);
-
- while (missing_dependencies.size()) {
- std::string next = *missing_dependencies.begin();
- missing_dependencies.erase(next);
- out->insert(next);
- GetMissingDependencies(next, *out, &missing_dependencies);
- }
-}
-
-void ExtensionAPI::GetMissingDependencies(
- const std::string& api_name,
- const std::set<std::string>& excluding,
- std::set<std::string>* out) {
- std::string feature_type;
- std::string feature_name;
- SplitDependencyName(api_name, &feature_type, &feature_name);
-
- // Only API features can have dependencies for now.
- if (feature_type != "api")
- return;
-
- const DictionaryValue* schema = GetSchema(feature_name);
- CHECK(schema) << "Schema for " << feature_name << " not found";
-
- const ListValue* dependencies = NULL;
- if (!schema->GetList("dependencies", &dependencies))
- return;
-
- for (size_t i = 0; i < dependencies->GetSize(); ++i) {
- std::string dependency_name;
- if (dependencies->GetString(i, &dependency_name) &&
- !excluding.count(dependency_name)) {
- out->insert(dependency_name);
- }
- }
-}
-
bool ExtensionAPI::IsPrivilegedAPI(const std::string& name) {
return completely_unprivileged_apis_.count(name) ||
partially_unprivileged_apis_.count(name);
diff --git a/chrome/common/extensions/api/extension_api.h b/chrome/common/extensions/api/extension_api.h
index 924ee8c..2e1f7ef 100644
--- a/chrome/common/extensions/api/extension_api.h
+++ b/chrome/common/extensions/api/extension_api.h
@@ -37,7 +37,7 @@ class Feature;
// WARNING: This class is accessed on multiple threads in the browser process
// (see ExtensionFunctionDispatcher). No state should be modified after
// construction.
-class ExtensionAPI : public FeatureProvider {
+class ExtensionAPI {
public:
// Returns a single shared instance of this class. This is the typical use
// case in Chrome.
@@ -88,11 +88,6 @@ class ExtensionAPI : public FeatureProvider {
std::set<std::string> GetAllAPINames();
- // Gets a Feature object describing the API with the specified |full_name|.
- // This can be either an API namespace (like history, or
- // experimental.bookmarks), or it can be an individual function or event.
- virtual Feature* GetFeature(const std::string& full_name) OVERRIDE;
-
// Splits a full name from the extension API into its API and child name
// parts. Some examples:
//
@@ -107,6 +102,10 @@ class ExtensionAPI : public FeatureProvider {
void InitDefaultConfiguration();
+ // Gets a feature from any dependency provider registered with ExtensionAPI.
+ // Returns NULL if the feature could not be found.
+ Feature* GetFeatureDependency(const std::string& dependency_name);
+
private:
friend struct DefaultSingletonTraits<ExtensionAPI>;
@@ -134,24 +133,6 @@ class ExtensionAPI : public FeatureProvider {
const Extension* extension,
const GURL& url);
- // Returns true if the API uses the feature system.
- bool UsesFeatureSystem(const std::string& full_name);
-
- // Gets a feature from any dependency provider.
- Feature* GetFeatureDependency(const std::string& dependency_name);
-
- // Adds dependent schemas to |out| as determined by the "dependencies"
- // property.
- // TODO(aa): Consider making public and adding tests.
- void ResolveDependencies(std::set<std::string>* out);
-
- // Adds any APIs listed in "dependencies" found in the schema for |api_name|
- // but not in |excluding| to |out|.
- void GetMissingDependencies(
- const std::string& api_name,
- const std::set<std::string>& excluding,
- std::set<std::string>* out);
-
// Checks if an API is *entirely* privileged. This won't include APIs such as
// "storage" which is entirely unprivileged, nor "extension" which has
// unprivileged components.
diff --git a/chrome/common/extensions/api/extension_api_unittest.cc b/chrome/common/extensions/api/extension_api_unittest.cc
index d18bd14..a7f99d0 100644
--- a/chrome/common/extensions/api/extension_api_unittest.cc
+++ b/chrome/common/extensions/api/extension_api_unittest.cc
@@ -9,13 +9,17 @@
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
+#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/features/api_feature.h"
+#include "chrome/common/extensions/features/base_feature_provider.h"
#include "chrome/common/extensions/features/simple_feature.h"
#include "chrome/common/extensions/manifest.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -23,25 +27,9 @@
namespace extensions {
namespace {
-class TestFeatureProvider : public FeatureProvider {
- public:
- explicit TestFeatureProvider(Feature::Context context)
- : context_(context) {
- }
-
- virtual Feature* GetFeature(const std::string& name) OVERRIDE {
- SimpleFeature* result = new SimpleFeature();
- result->set_name(name);
- result->extension_types()->insert(Manifest::TYPE_EXTENSION);
- result->GetContexts()->insert(context_);
- to_destroy_.push_back(make_linked_ptr(result));
- return result;
- }
-
- private:
- std::vector<linked_ptr<Feature> > to_destroy_;
- Feature::Context context_;
-};
+SimpleFeature* CreateAPIFeature() {
+ return new APIFeature();
+}
TEST(ExtensionAPI, Creation) {
ExtensionAPI* shared_instance = ExtensionAPI::GetSharedInstance();
@@ -125,46 +113,107 @@ TEST(ExtensionAPI, IsPrivileged) {
TEST(ExtensionAPI, IsPrivilegedFeatures) {
struct {
- std::string filename;
std::string api_full_name;
bool expect_is_privilged;
- Feature::Context test2_contexts;
} test_data[] = {
- { "is_privileged_features_1.json", "test", false,
- Feature::UNSPECIFIED_CONTEXT },
- { "is_privileged_features_2.json", "test", true,
- Feature::UNSPECIFIED_CONTEXT },
- { "is_privileged_features_3.json", "test", false,
- Feature::UNSPECIFIED_CONTEXT },
- { "is_privileged_features_4.json", "test.bar", false,
- Feature::UNSPECIFIED_CONTEXT },
- { "is_privileged_features_5.json", "test.bar", true,
- Feature::BLESSED_EXTENSION_CONTEXT },
- { "is_privileged_features_5.json", "test.bar", false,
- Feature::UNBLESSED_EXTENSION_CONTEXT }
+ { "test1", false },
+ { "test1.foo", true },
+ { "test2", true },
+ { "test2.foo", false },
+ { "test2.bar", false },
+ { "test2.baz", true },
+ { "test3", false },
+ { "test3.foo", true },
+ { "test4", false }
};
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
- base::FilePath manifest_path;
- PathService::Get(chrome::DIR_TEST_DATA, &manifest_path);
- manifest_path = manifest_path.AppendASCII("extensions")
- .AppendASCII("extension_api_unittest")
- .AppendASCII(test_data[i].filename);
+ base::FilePath api_features_path;
+ PathService::Get(chrome::DIR_TEST_DATA, &api_features_path);
+ api_features_path = api_features_path.AppendASCII("extensions")
+ .AppendASCII("extension_api_unittest")
+ .AppendASCII("privileged_api_features.json");
+
+ std::string api_features_str;
+ ASSERT_TRUE(file_util::ReadFileToString(
+ api_features_path, &api_features_str)) << "privileged_api_features.json";
- std::string manifest_str;
- ASSERT_TRUE(file_util::ReadFileToString(manifest_path, &manifest_str))
- << test_data[i].filename;
+ base::DictionaryValue* value = static_cast<DictionaryValue*>(
+ base::JSONReader::Read(api_features_str));
+ BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
ExtensionAPI api;
- api.RegisterSchema("test", manifest_str);
+ api.RegisterDependencyProvider("api", &api_feature_provider);
+ EXPECT_EQ(test_data[i].expect_is_privilged,
+ api.IsPrivileged(test_data[i].api_full_name)) << i;
+ }
+}
+
+TEST(ExtensionAPI, APIFeatures) {
+ struct {
+ std::string api_full_name;
+ bool expect_is_available;
+ Feature::Context context;
+ GURL url;
+ } test_data[] = {
+ { "test1", false, Feature::WEB_PAGE_CONTEXT, GURL() },
+ { "test1", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() },
+ { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() },
+ { "test1", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() },
+ { "test2", true, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") },
+ { "test2", false, Feature::BLESSED_EXTENSION_CONTEXT,
+ GURL("http://google.com") },
+ { "test2.foo", false, Feature::WEB_PAGE_CONTEXT,
+ GURL("http://google.com") },
+ { "test2.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() },
+ { "test3", false, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") },
+ { "test3.foo", true, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") },
+ { "test3.foo", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() },
+ { "test4", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() },
+ { "test4.foo", false, Feature::BLESSED_EXTENSION_CONTEXT, GURL() },
+ { "test4.foo", false, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() },
+ { "test4.foo.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() },
+ { "test5", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") },
+ { "test5", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") },
+ { "test5.blah", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") },
+ { "test5.blah", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") },
+ { "test6", false, Feature::BLESSED_EXTENSION_CONTEXT, GURL() },
+ { "test6.foo", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() },
+ { "test7", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") },
+ { "test7.foo", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") },
+ { "test7.foo", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") },
+ { "test7.bar", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") },
+ { "test7.bar", false, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }
+ };
+
+ base::FilePath api_features_path;
+ PathService::Get(chrome::DIR_TEST_DATA, &api_features_path);
+ api_features_path = api_features_path.AppendASCII("extensions")
+ .AppendASCII("extension_api_unittest")
+ .AppendASCII("api_features.json");
- TestFeatureProvider test2_provider(test_data[i].test2_contexts);
- if (test_data[i].test2_contexts != Feature::UNSPECIFIED_CONTEXT) {
- api.RegisterDependencyProvider("test2", &test2_provider);
+ std::string api_features_str;
+ ASSERT_TRUE(file_util::ReadFileToString(
+ api_features_path, &api_features_str)) << "api_features.json";
+
+ base::DictionaryValue* value = static_cast<DictionaryValue*>(
+ base::JSONReader::Read(api_features_str));
+ BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ ExtensionAPI api;
+ api.RegisterDependencyProvider("api", &api_feature_provider);
+ for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd();
+ iter.Advance()) {
+ if (iter.key().find(".") == std::string::npos)
+ api.RegisterSchema(iter.key(), "");
}
- EXPECT_EQ(test_data[i].expect_is_privilged,
- api.IsPrivileged(test_data[i].api_full_name)) << i;
+ EXPECT_EQ(test_data[i].expect_is_available,
+ api.IsAvailable(test_data[i].api_full_name,
+ NULL,
+ test_data[i].context,
+ test_data[i].url).is_available()) << i;
}
}
@@ -376,10 +425,10 @@ TEST(ExtensionAPI, GetAPINameFromFullName) {
TEST(ExtensionAPI, DefaultConfigurationFeatures) {
scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
- SimpleFeature* bookmarks =
- static_cast<SimpleFeature*>(api->GetFeature("bookmarks"));
- SimpleFeature* bookmarks_create =
- static_cast<SimpleFeature*>(api->GetFeature("bookmarks.create"));
+ SimpleFeature* bookmarks = static_cast<SimpleFeature*>(
+ api->GetFeatureDependency("api:bookmarks"));
+ SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>(
+ api->GetFeatureDependency("api:bookmarks.create"));
struct {
SimpleFeature* feature;
@@ -408,34 +457,30 @@ TEST(ExtensionAPI, DefaultConfigurationFeatures) {
}
TEST(ExtensionAPI, FeaturesRequireContexts) {
- scoped_ptr<base::ListValue> schema1(new base::ListValue());
- base::DictionaryValue* feature_definition = new base::DictionaryValue();
- schema1->Append(feature_definition);
- feature_definition->SetString("namespace", "test");
- feature_definition->SetBoolean("uses_feature_system", true);
-
- scoped_ptr<base::ListValue> schema2(schema1->DeepCopy());
-
+ // TODO(cduvall): Make this check API featues.
+ scoped_ptr<base::DictionaryValue> api_features1(new base::DictionaryValue());
+ scoped_ptr<base::DictionaryValue> api_features2(new base::DictionaryValue());
+ base::DictionaryValue* test1 = new base::DictionaryValue();
+ base::DictionaryValue* test2 = new base::DictionaryValue();
base::ListValue* contexts = new base::ListValue();
contexts->Append(new base::StringValue("content_script"));
- feature_definition->Set("contexts", contexts);
+ test1->Set("contexts", contexts);
+ api_features1->Set("test", test1);
+ api_features2->Set("test", test2);
struct {
- base::ListValue* schema;
+ base::DictionaryValue* api_features;
bool expect_success;
} test_data[] = {
- { schema1.get(), true },
- { schema2.get(), false }
+ { api_features1.get(), true },
+ { api_features2.get(), false }
};
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
- std::string schema_source;
- base::JSONWriter::Write(test_data[i].schema, &schema_source);
-
- ExtensionAPI api;
- api.RegisterSchema("test", base::StringPiece(schema_source));
- Feature* feature = api.GetFeature("test");
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ BaseFeatureProvider api_feature_provider(*test_data[i].api_features,
+ CreateAPIFeature);
+ Feature* feature = api_feature_provider.GetFeature("test");
EXPECT_EQ(test_data[i].expect_success, feature != NULL) << i;
}
}
diff --git a/chrome/common/extensions/api/webstore.json b/chrome/common/extensions/api/webstore.json
index 18b1c25..2b72c0c 100644
--- a/chrome/common/extensions/api/webstore.json
+++ b/chrome/common/extensions/api/webstore.json
@@ -5,17 +5,6 @@
[
{
"namespace": "webstore",
-
- // Any webpage can use the webstore API.
- "matches": [ "http://*/*", "https://*/*" ],
-
- // Hosted apps can use the webstore API from within a blessed context.
- "uses_feature_system": true,
- "channel": "stable",
- "extension_types": ["hosted_app"],
- "contexts": ["blessed_extension"],
- "dependencies": [],
-
"functions": [
{
"name": "install",
diff --git a/chrome/common/extensions/features/api_feature.cc b/chrome/common/extensions/features/api_feature.cc
new file mode 100644
index 0000000..09b8c19
--- /dev/null
+++ b/chrome/common/extensions/features/api_feature.cc
@@ -0,0 +1,26 @@
+// 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/api_feature.h"
+
+namespace extensions {
+
+APIFeature::APIFeature() {
+}
+
+APIFeature::~APIFeature() {
+}
+
+std::string APIFeature::Parse(const DictionaryValue* value) {
+ std::string error = SimpleFeature::Parse(value);
+ if (!error.empty())
+ return error;
+
+ if (GetContexts()->empty())
+ return name() + ": API features must specify at least one context.";
+
+ return "";
+}
+
+} // namespace
diff --git a/chrome/common/extensions/features/api_feature.h b/chrome/common/extensions/features/api_feature.h
new file mode 100644
index 0000000..e6484ae4
--- /dev/null
+++ b/chrome/common/extensions/features/api_feature.h
@@ -0,0 +1,22 @@
+// 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_FEATURES_API_FEATURE_H_
+#define CHROME_COMMON_EXTENSIONS_FEATURES_API_FEATURE_H_
+
+#include "chrome/common/extensions/features/simple_feature.h"
+
+namespace extensions {
+
+class APIFeature : public SimpleFeature {
+ public:
+ APIFeature();
+ virtual ~APIFeature();
+
+ virtual std::string Parse(const DictionaryValue* value) OVERRIDE;
+};
+
+} // extensions
+
+#endif // CHROME_COMMON_EXTENSIONS_FEATURES_API_FEATURE_H_
diff --git a/chrome/common/extensions/features/base_feature_provider.cc b/chrome/common/extensions/features/base_feature_provider.cc
index 44cc454..3e2c5e5 100644
--- a/chrome/common/extensions/features/base_feature_provider.cc
+++ b/chrome/common/extensions/features/base_feature_provider.cc
@@ -6,6 +6,7 @@
#include "base/json/json_reader.h"
#include "base/lazy_instance.h"
+#include "chrome/common/extensions/features/api_feature.h"
#include "chrome/common/extensions/features/complex_feature.h"
#include "chrome/common/extensions/features/manifest_feature.h"
#include "chrome/common/extensions/features/permission_feature.h"
@@ -23,7 +24,11 @@ SimpleFeature* CreateFeature() {
struct Static {
Static()
- : manifest_features(
+ : api_features(
+ LoadProvider("api",
+ &CreateFeature<APIFeature>,
+ IDR_EXTENSION_API_FEATURES)),
+ manifest_features(
LoadProvider("manifest",
&CreateFeature<ManifestFeature>,
IDR_EXTENSION_MANIFEST_FEATURES)),
@@ -33,6 +38,7 @@ struct Static {
IDR_EXTENSION_PERMISSION_FEATURES)) {
}
+ scoped_ptr<BaseFeatureProvider> api_features;
scoped_ptr<BaseFeatureProvider> manifest_features;
scoped_ptr<BaseFeatureProvider> permission_features;
@@ -67,20 +73,10 @@ bool ParseFeature(const DictionaryValue* value,
const std::string& name,
SimpleFeature* feature) {
feature->set_name(name);
- feature->Parse(value);
-
- if (feature->extension_types()->empty()) {
- LOG(ERROR) << name << ": Simple features must specify at least one "
- << "value for extension_types.";
- return false;
- }
-
- if (!feature->GetContexts()->empty()) {
- LOG(ERROR) << name << ": Simple features do not support contexts.";
- return false;
- }
-
- return true;
+ std::string error = feature->Parse(value);
+ if (!error.empty())
+ LOG(ERROR) << error;
+ return error.empty();
}
base::LazyInstance<Static> g_static = LAZY_INSTANCE_INITIALIZER;
@@ -141,6 +137,11 @@ BaseFeatureProvider::~BaseFeatureProvider() {
}
// static
+BaseFeatureProvider* BaseFeatureProvider::GetApiFeatures() {
+ return g_static.Get().api_features.get();
+}
+
+// static
BaseFeatureProvider* BaseFeatureProvider::GetManifestFeatures() {
return g_static.Get().manifest_features.get();
}
diff --git a/chrome/common/extensions/features/base_feature_provider.h b/chrome/common/extensions/features/base_feature_provider.h
index 1e71b0d..5a68f6f 100644
--- a/chrome/common/extensions/features/base_feature_provider.h
+++ b/chrome/common/extensions/features/base_feature_provider.h
@@ -26,6 +26,10 @@ class BaseFeatureProvider : public FeatureProvider {
BaseFeatureProvider(const DictionaryValue& root, FeatureFactory factory);
virtual ~BaseFeatureProvider();
+ // Gets an instance for the _api_features.json file that is baked into
+ // Chrome as a resource.
+ static BaseFeatureProvider* GetApiFeatures();
+
// Gets an instance for the _manifest_features.json file that is baked into
// Chrome as a resource.
static BaseFeatureProvider* GetManifestFeatures();
diff --git a/chrome/common/extensions/features/base_feature_provider_unittest.cc b/chrome/common/extensions/features/base_feature_provider_unittest.cc
index 17284f9..df876dc 100644
--- a/chrome/common/extensions/features/base_feature_provider_unittest.cc
+++ b/chrome/common/extensions/features/base_feature_provider_unittest.cc
@@ -4,6 +4,7 @@
#include "chrome/common/extensions/features/base_feature_provider.h"
+#include "chrome/common/extensions/features/permission_feature.h"
#include "chrome/common/extensions/value_builder.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -14,6 +15,7 @@ using extensions::Extension;
using extensions::Feature;
using extensions::ListBuilder;
using extensions::Manifest;
+using extensions::PermissionFeature;
using extensions::SimpleFeature;
TEST(BaseFeatureProvider, ManifestFeatures) {
@@ -100,6 +102,10 @@ TEST(BaseFeatureProvider, PermissionFeatures) {
extension.get(), Feature::UNSPECIFIED_CONTEXT).result());
}
+SimpleFeature* CreatePermissionFeature() {
+ return new PermissionFeature();
+}
+
TEST(BaseFeatureProvider, Validation) {
scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
@@ -116,14 +122,14 @@ TEST(BaseFeatureProvider, Validation) {
value->Set("feature2", feature2);
scoped_ptr<BaseFeatureProvider> provider(
- new BaseFeatureProvider(*value, NULL));
+ new BaseFeatureProvider(*value, CreatePermissionFeature));
// feature1 won't validate because it lacks an extension type.
EXPECT_FALSE(provider->GetFeature("feature1"));
// If we add one, it works.
feature1->Set("extension_types", extension_types->DeepCopy());
- provider.reset(new BaseFeatureProvider(*value, NULL));
+ provider.reset(new BaseFeatureProvider(*value, CreatePermissionFeature));
EXPECT_TRUE(provider->GetFeature("feature1"));
// feature2 won't validate because of the presence of "contexts".
@@ -131,7 +137,7 @@ TEST(BaseFeatureProvider, Validation) {
// If we remove it, it works.
feature2->Remove("contexts", NULL);
- provider.reset(new BaseFeatureProvider(*value, NULL));
+ provider.reset(new BaseFeatureProvider(*value, CreatePermissionFeature));
EXPECT_TRUE(provider->GetFeature("feature2"));
}
diff --git a/chrome/common/extensions/features/feature.cc b/chrome/common/extensions/features/feature.cc
index f5eb37c..5a93bf8 100644
--- a/chrome/common/extensions/features/feature.cc
+++ b/chrome/common/extensions/features/feature.cc
@@ -61,6 +61,8 @@ Feature::Availability Feature::CreateAvailability(AvailabilityResult result,
return Availability(result, message);
}
+Feature::Feature() {}
+
Feature::~Feature() {}
} // namespace extensions
diff --git a/chrome/common/extensions/features/feature.h b/chrome/common/extensions/features/feature.h
index 53fa06b..748d04f 100644
--- a/chrome/common/extensions/features/feature.h
+++ b/chrome/common/extensions/features/feature.h
@@ -88,6 +88,7 @@ class Feature {
const std::string message_;
};
+ Feature();
virtual ~Feature();
// Used by ChromeV8Context until the feature system is fully functional.
@@ -124,6 +125,7 @@ class Feature {
const std::string& name() const { return name_; }
void set_name(const std::string& name) { name_ = name; }
+ const std::set<std::string>& dependencies() { return dependencies_; }
// Gets the platform the code is currently running on.
static Platform GetCurrentPlatform();
@@ -131,7 +133,6 @@ class Feature {
// Gets the Feature::Location value for the specified Manifest::Location.
static Location ConvertLocation(Manifest::Location extension_location);
- // TODO(justinlin): Remove and move to APIFeature when it exists.
virtual std::set<Context>* GetContexts() = 0;
// Returns true if the feature is available to be parsed into a new extension
@@ -167,6 +168,7 @@ class Feature {
protected:
std::string name_;
+ std::set<std::string> dependencies_;
};
} // namespace extensions
diff --git a/chrome/common/extensions/features/manifest_feature.cc b/chrome/common/extensions/features/manifest_feature.cc
index 51d9fd5..cd4a53f 100644
--- a/chrome/common/extensions/features/manifest_feature.cc
+++ b/chrome/common/extensions/features/manifest_feature.cc
@@ -34,4 +34,20 @@ Feature::Availability ManifestFeature::IsAvailableToContext(
return CreateAvailability(IS_AVAILABLE);
}
+std::string ManifestFeature::Parse(const DictionaryValue* value) {
+ std::string error = SimpleFeature::Parse(value);
+ if (!error.empty())
+ return error;
+
+ if (extension_types()->empty()) {
+ return name() + ": Manifest features must specify at least one " +
+ "value for extension_types.";
+ }
+
+ if (!GetContexts()->empty())
+ return name() + ": Manifest features do not support contexts.";
+
+ return "";
+}
+
} // namespace
diff --git a/chrome/common/extensions/features/manifest_feature.h b/chrome/common/extensions/features/manifest_feature.h
index ca2060a..6fa75bc 100644
--- a/chrome/common/extensions/features/manifest_feature.h
+++ b/chrome/common/extensions/features/manifest_feature.h
@@ -19,6 +19,8 @@ class ManifestFeature : public SimpleFeature {
Feature::Context context,
const GURL& url,
Feature::Platform platform) const OVERRIDE;
+
+ virtual std::string Parse(const DictionaryValue* value) OVERRIDE;
};
} // extensions
diff --git a/chrome/common/extensions/features/permission_feature.cc b/chrome/common/extensions/features/permission_feature.cc
index 1f5da62..72b261f 100644
--- a/chrome/common/extensions/features/permission_feature.cc
+++ b/chrome/common/extensions/features/permission_feature.cc
@@ -36,4 +36,20 @@ Feature::Availability PermissionFeature::IsAvailableToContext(
return CreateAvailability(IS_AVAILABLE);
}
+std::string PermissionFeature::Parse(const DictionaryValue* value) {
+ std::string error = SimpleFeature::Parse(value);
+ if (!error.empty())
+ return error;
+
+ if (extension_types()->empty()) {
+ return name() + ": Permission features must specify at least one " +
+ "value for extension_types.";
+ }
+
+ if (!GetContexts()->empty())
+ return name() + ": Permission features do not support contexts.";
+
+ return "";
+}
+
} // namespace
diff --git a/chrome/common/extensions/features/permission_feature.h b/chrome/common/extensions/features/permission_feature.h
index 8c4b52f..47892ae 100644
--- a/chrome/common/extensions/features/permission_feature.h
+++ b/chrome/common/extensions/features/permission_feature.h
@@ -19,6 +19,8 @@ class PermissionFeature : public SimpleFeature {
Feature::Context context,
const GURL& url,
Feature::Platform platform) const OVERRIDE;
+
+ virtual std::string Parse(const DictionaryValue* value) OVERRIDE;
};
} // extensions
diff --git a/chrome/common/extensions/features/simple_feature.cc b/chrome/common/extensions/features/simple_feature.cc
index de97340..9011e35 100644
--- a/chrome/common/extensions/features/simple_feature.cc
+++ b/chrome/common/extensions/features/simple_feature.cc
@@ -208,9 +208,10 @@ bool SimpleFeature::Equals(const SimpleFeature& other) const {
channel_ == other.channel_;
}
-void SimpleFeature::Parse(const DictionaryValue* value) {
+std::string SimpleFeature::Parse(const 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_,
@@ -224,6 +225,11 @@ void SimpleFeature::Parse(const DictionaryValue* value) {
ParseEnum<VersionInfo::Channel>(
value, "channel", &channel_,
g_mappings.Get().channels);
+ if (matches_.is_empty() && contexts_.count(WEB_PAGE_CONTEXT) != 0) {
+ return name() + ": Allowing web_page contexts requires supplying a value " +
+ "for matches.";
+ }
+ return "";
}
Feature::Availability SimpleFeature::IsAvailableToManifest(
diff --git a/chrome/common/extensions/features/simple_feature.h b/chrome/common/extensions/features/simple_feature.h
index 5b34c6e..4d1581d4 100644
--- a/chrome/common/extensions/features/simple_feature.h
+++ b/chrome/common/extensions/features/simple_feature.h
@@ -30,8 +30,9 @@ class SimpleFeature : public Feature {
// Parses the JSON representation of a feature into the fields of this object.
// Unspecified values in the JSON are not modified in the object. This allows
- // us to implement inheritance by parsing one value after another.
- void Parse(const DictionaryValue* value);
+ // us to implement inheritance by parsing one value after another. Returns
+ // the error found, or an empty string on success.
+ virtual std::string Parse(const DictionaryValue* value);
// Returns true if the feature contains the same values as another.
bool Equals(const SimpleFeature& other) const;
diff --git a/chrome/test/data/extensions/extension_api_unittest/api_features.json b/chrome/test/data/extensions/extension_api_unittest/api_features.json
new file mode 100644
index 0000000..476525d
--- /dev/null
+++ b/chrome/test/data/extensions/extension_api_unittest/api_features.json
@@ -0,0 +1,54 @@
+{
+ "test1": {
+ "contexts": ["content_script", "blessed_extension", "unblessed_extension"]
+ },
+ "test2": {
+ "contexts": ["web_page"],
+ "matches": ["<all_urls>"]
+ },
+ "test2.foo": {
+ "contexts": ["content_script"]
+ },
+ "test3": {
+ "contexts": ["content_script"]
+ },
+ "test3.foo": {
+ "contexts": ["web_page", "blessed_extension"],
+ "matches": ["<all_urls>"]
+ },
+ "test4": {
+ "contexts": ["blessed_extension"],
+ "dependencies": ["api:test3.foo"]
+ },
+ "test4.foo": {
+ "contexts": ["unblessed_extension"],
+ "dependencies": ["api:test4"]
+ },
+ "test4.foo.foo": {
+ "contexts": ["content_script"]
+ },
+ "test5": {
+ "contexts": ["web_page"],
+ "matches": ["http://foo.com/*"]
+ },
+ "test6": {
+ "contexts": ["content_script"]
+ },
+ "test6.foo": {
+ "contexts": ["blessed_extension"]
+ },
+ "test7": {
+ "contexts": ["web_page"],
+ "matches": ["http://foo.com/*"]
+ },
+ "test7.foo": {
+ "contexts": ["web_page"],
+ "matches": ["<all_urls>"],
+ "dependencies": ["test7"]
+ },
+ "test7.bar": {
+ "contexts": ["web_page"],
+ "matches": ["http://bar.com/*"],
+ "dependencies": ["test7.foo"]
+ }
+}
diff --git a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_1.json b/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_1.json
deleted file mode 100644
index ae509f69..0000000
--- a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_1.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
- {
- "namespace": "test",
- "uses_feature_system": true,
- "extension_types": ["extension"],
- "contexts": ["content_script"]
- }
-]
diff --git a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_2.json b/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_2.json
deleted file mode 100644
index 91b9d13..0000000
--- a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_2.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
- {
- "namespace": "test",
- "uses_feature_system": true,
- "extension_types": ["extension"],
- "contexts": ["blessed_extension"]
- }
-]
diff --git a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_3.json b/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_3.json
deleted file mode 100644
index 47b1290e..0000000
--- a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_3.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
- {
- "namespace": "test",
- "uses_feature_system": true,
- "extension_types": ["extension"],
- "contexts": ["content_script", "blessed_extension"]
- }
-]
diff --git a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_4.json b/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_4.json
deleted file mode 100644
index 8839ce1..0000000
--- a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_4.json
+++ /dev/null
@@ -1,18 +0,0 @@
-[
- {
- "namespace": "test",
- "uses_feature_system": true,
- "extension_types": ["extension"],
- "contexts": ["blessed_extension"],
- "functions": [
- {
- "name": "foo",
- "contexts": ["content_script", "blessed_extension"]
- },
- {
- "name": "bar",
- "dependencies": ["api:test.foo"]
- }
- ]
- }
-]
diff --git a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_5.json b/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_5.json
deleted file mode 100644
index 9db5b2d..0000000
--- a/chrome/test/data/extensions/extension_api_unittest/is_privileged_features_5.json
+++ /dev/null
@@ -1,18 +0,0 @@
-[
- {
- "namespace": "test",
- "uses_feature_system": true,
- "extension_types": ["extension"],
- "contexts": ["blessed_extension"],
- "functions": [
- {
- "name": "foo",
- "dependencies": ["test2:monkey"]
- },
- {
- "name": "bar",
- "dependencies": ["api:test.foo"]
- }
- ]
- }
-]
diff --git a/chrome/test/data/extensions/extension_api_unittest/privileged_api_features.json b/chrome/test/data/extensions/extension_api_unittest/privileged_api_features.json
new file mode 100644
index 0000000..b32781d
--- /dev/null
+++ b/chrome/test/data/extensions/extension_api_unittest/privileged_api_features.json
@@ -0,0 +1,32 @@
+{
+ "test1": {
+ "contexts": ["content_script"]
+ },
+ "test1.foo": {
+ "contexts": ["blessed_extension"]
+ },
+ "test2": {
+ "contexts": ["blessed_extension"],
+ "dependencies": ["api:test1"]
+ },
+ "test2.foo": {
+ "contexts": ["content_script"]
+ },
+ "test2.bar": {
+ "contexts": ["content_script", "blessed_extension"]
+ },
+ "test2.baz": {
+ "contexts": ["blessed_extension"],
+ "dependencies": ["api:test2.foo"]
+ },
+ "test3": {
+ "contexts": ["content_script", "blessed_extension"]
+ },
+ "test3.foo": {
+ "contexts": ["blessed_extension"]
+ },
+ "test4": {
+ "contexts": ["content_script"],
+ "dependencies": ["api:test1.foo"]
+ }
+}