diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-18 08:22:22 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-18 08:22:22 +0000 |
commit | cdb3a8bffd314443da88ae89bc87dc34f43a0e66 (patch) | |
tree | 4be9af79bfe8add68961d54f3677b14b8a5413fc /chrome/common/extensions/api | |
parent | 4e167479b6eab4c9bb6aaf2284478bc009909851 (diff) | |
download | chromium_src-cdb3a8bffd314443da88ae89bc87dc34f43a0e66.zip chromium_src-cdb3a8bffd314443da88ae89bc87dc34f43a0e66.tar.gz chromium_src-cdb3a8bffd314443da88ae89bc87dc34f43a0e66.tar.bz2 |
Revert 127391 - Convert app_bindings.js to the schema_generated_bindings.js infrastructure.
This involves opening up *all* custom bindings to web pages, where access is
controlled content-script style using a URL-matches property in the API schema.
Reverted due to performance regression.
BUG=104100,117282
TEST=unit_tests,browser_tests
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=125811
Review URL: http://codereview.chromium.org/9460002
TBR=kalman@chromium.org
Review URL: https://chromiumcodereview.appspot.com/9718023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127402 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/extensions/api')
-rw-r--r-- | chrome/common/extensions/api/app.json | 70 | ||||
-rw-r--r-- | chrome/common/extensions/api/extension_api.cc | 169 | ||||
-rw-r--r-- | chrome/common/extensions/api/extension_api.h | 68 | ||||
-rw-r--r-- | chrome/common/extensions/api/extension_api_unittest.cc | 144 |
4 files changed, 118 insertions, 333 deletions
diff --git a/chrome/common/extensions/api/app.json b/chrome/common/extensions/api/app.json deleted file mode 100644 index 0e813e6..0000000 --- a/chrome/common/extensions/api/app.json +++ /dev/null @@ -1,70 +0,0 @@ -[ - { - "namespace": "app", - "nodoc": true, - "unprivileged": true, - "matches": [ "<all_urls>" ], - "types": [ - { - "id": "Details", - "description": "TODO (it's a manifest)", - "type": "object", - "properties": {}, - "additionalProperties": { "type": "any" } - }, - { - "id": "DOMWindow", - "type": "object", - "properties": {}, - "additionalProperties": { "type": "any" } - } - ], - "functions": [ - { - "name": "getIsInstalled", - "description": "TODO", - "type": "function", - "parameters": [], - "returns": { - "name": "isInstalled", - "description": "TODO", - "type": "boolean" - } - }, - { - "name": "install", - "description": "TODO", - "type": "function", - "parameters": [] - }, - { - "name": "getDetails", - "description": "TODO", - "type": "function", - "parameters": [], - "returns": { - "$ref": "Details", - "optional": true, - "description": "TODO" - } - }, - { - "name": "getDetailsForFrame", - "description": "TODO", - "type": "function", - "parameters": [ - { - "name": "frame", - "description": "TODO", - "$ref": "DOMWindow" - } - ], - "returns": { - "$ref": "Details", - "optional": true, - "description": "TODO" - } - } - ] - } -] diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc index 5bd45f9..f8909b0 100644 --- a/chrome/common/extensions/api/extension_api.cc +++ b/chrome/common/extensions/api/extension_api.cc @@ -15,7 +15,6 @@ #include "base/values.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_permission_set.h" -#include "googleurl/src/gurl.h" #include "grit/common_resources.h" #include "ui/base/resource/resource_bundle.h" @@ -23,6 +22,22 @@ namespace extensions { namespace { +// Adds any APIs listed in "dependencies" found in |schema| but not in +// |reference| to |out|. +void GetMissingDependencies( + const DictionaryValue& schema, + const ExtensionAPI::SchemaMap& reference, + std::set<std::string>* out) { + ListValue* dependencies = NULL; + if (!schema.GetList("dependencies", &dependencies)) + return; + for (size_t i = 0; i < dependencies->GetSize(); ++i) { + std::string api_name; + if (dependencies->GetString(i, &api_name) && !reference.count(api_name)) + out->insert(api_name); + } +} + // Returns whether the list at |name_space_node|.|child_kind| contains any // children with an { "unprivileged": true } property. bool HasUnprivilegedChild(const DictionaryValue* name_space_node, @@ -80,7 +95,6 @@ void ExtensionAPI::LoadSchemaFromResource(int resource_id) { ExtensionAPI::ExtensionAPI() { static int kJsonApiResourceIds[] = { - IDR_EXTENSION_API_JSON_APP, IDR_EXTENSION_API_JSON_BOOKMARKS, IDR_EXTENSION_API_JSON_BROWSERACTION, IDR_EXTENSION_API_JSON_BROWSING_DATA, @@ -165,27 +179,6 @@ ExtensionAPI::ExtensionAPI() { partially_unprivileged_apis_.insert(it->first); } } - - // Populate |url_matching_apis_|. - for (SchemaMap::const_iterator it = schemas_.begin(); - it != schemas_.end(); ++it) { - ListValue* matches = NULL; - { - Value* matches_value = NULL; - if (!it->second->Get("matches", &matches_value)) - continue; - CHECK_EQ(Value::TYPE_LIST, matches_value->GetType()); - matches = static_cast<ListValue*>(matches_value); - } - URLPatternSet pattern_set; - for (size_t i = 0; i < matches->GetSize(); ++i) { - std::string pattern; - CHECK(matches->GetString(i, &pattern)); - pattern_set.AddPattern( - URLPattern(UserScript::kValidUserScriptSchemes, pattern)); - } - url_matching_apis_[it->first] = pattern_set; - } } ExtensionAPI::~ExtensionAPI() { @@ -265,109 +258,59 @@ const base::DictionaryValue* ExtensionAPI::GetSchema( return maybe_schema != schemas_.end() ? maybe_schema->second.get() : NULL; } -scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext( - Feature::Context context, - const Extension* extension, - const GURL& url) const { - scoped_ptr<std::set<std::string> > result(new std::set<std::string>()); - - switch (context) { - case Feature::UNSPECIFIED_CONTEXT: - break; - - case Feature::PRIVILEGED_CONTEXT: - // Availability is determined by the permissions of the extension. - CHECK(extension); - GetAllowedAPIs(extension, result.get()); - ResolveDependencies(result.get()); - break; - - case Feature::UNPRIVILEGED_CONTEXT: - case Feature::CONTENT_SCRIPT_CONTEXT: - // Availability is determined by the permissions of the extension - // (but only those APIs that are unprivileged). - CHECK(extension); - GetAllowedAPIs(extension, result.get()); - // Resolving dependencies before removing unprivileged APIs means that - // some unprivileged APIs may have unrealised dependencies. Too bad! - ResolveDependencies(result.get()); - RemovePrivilegedAPIs(result.get()); - break; - - case Feature::WEB_PAGE_CONTEXT: - // Availablility is determined by the url. - CHECK(url.is_valid()); - GetAPIsMatchingURL(url, result.get()); - break; - } - - return result.Pass(); -} - -void ExtensionAPI::GetAllowedAPIs( - const Extension* extension, std::set<std::string>* out) const { - for (SchemaMap::const_iterator i = schemas_.begin(); i != schemas_.end(); - ++i) { - if (extension->required_permission_set()->HasAnyAccessToAPI(i->first) || - extension->optional_permission_set()->HasAnyAccessToAPI(i->first)) { - out->insert(i->first); - } - } +void ExtensionAPI::GetSchemasForExtension(const Extension& extension, + GetSchemasFilter filter, + SchemaMap* out) const { + // Check both required_permissions and optional_permissions since we need + // to return all schemas that might be needed. + GetSchemasForPermissions(*extension.required_permission_set(), filter, out); + GetSchemasForPermissions(*extension.optional_permission_set(), filter, out); + + // Note that dependency resolution might introduce APIs outside of the filter + // (for example, "extensions" has unprivileged componenents but relies on + // "tabs" which doesn't). It doesn't matter because schema_generated_bindings + // does individual function/event based checking anyway, but it's a shame. + ResolveDependencies(out); } -void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) const { +void ExtensionAPI::ResolveDependencies(SchemaMap* out) const { std::set<std::string> missing_dependencies; - for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i) - GetMissingDependencies(*i, *out, &missing_dependencies); + for (SchemaMap::const_iterator i = out->begin(); i != out->end(); ++i) + GetMissingDependencies(*i->second, *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); + std::string api_name = *missing_dependencies.begin(); + missing_dependencies.erase(api_name); + linked_ptr<const DictionaryValue> schema = schemas_.find(api_name)->second; + (*out)[api_name] = schema; + GetMissingDependencies(*schema, *out, &missing_dependencies); } } -void ExtensionAPI::GetMissingDependencies( - const std::string& api_name, - const std::set<std::string>& excluding, - std::set<std::string>* out) const { - const base::DictionaryValue* schema = GetSchema(api_name); - CHECK(schema) << "Schema for " << api_name << " not found"; - - ListValue* dependencies = NULL; - if (!schema->GetList("dependencies", &dependencies)) - return; - - for (size_t i = 0; i < dependencies->GetSize(); ++i) { - std::string api_name; - if (dependencies->GetString(i, &api_name) && !excluding.count(api_name)) - out->insert(api_name); - } +void ExtensionAPI::GetDefaultSchemas(GetSchemasFilter filter, + SchemaMap* out) const { + scoped_refptr<ExtensionPermissionSet> default_permissions( + new ExtensionPermissionSet()); + GetSchemasForPermissions(*default_permissions, filter, out); + ResolveDependencies(out); } -void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) const { - std::set<std::string> privileged_apis; - for (std::set<std::string>::iterator i = apis->begin(); i != apis->end(); - ++i) { - if (!completely_unprivileged_apis_.count(*i) && - !partially_unprivileged_apis_.count(*i)) { - privileged_apis.insert(*i); - } - } - for (std::set<std::string>::iterator i = privileged_apis.begin(); - i != privileged_apis.end(); ++i) { - apis->erase(*i); +void ExtensionAPI::GetSchemasForPermissions( + const ExtensionPermissionSet& permissions, + GetSchemasFilter filter, + SchemaMap* out) const { + for (SchemaMap::const_iterator it = schemas_.begin(); it != schemas_.end(); + ++it) { + if (filter == ONLY_UNPRIVILEGED && IsWholeAPIPrivileged(it->first)) + continue; + if (permissions.HasAnyAccessToAPI(it->first)) + (*out)[it->first] = it->second; } } -void ExtensionAPI::GetAPIsMatchingURL(const GURL& url, - std::set<std::string>* out) const { - for (std::map<std::string, URLPatternSet>::const_iterator i = - url_matching_apis_.begin(); i != url_matching_apis_.end(); ++i) { - if (i->second.MatchesURL(url)) - out->insert(i->first); - } +bool ExtensionAPI::IsWholeAPIPrivileged(const std::string& api_name) const { + return !completely_unprivileged_apis_.count(api_name) && + !partially_unprivileged_apis_.count(api_name); } } // namespace extensions diff --git a/chrome/common/extensions/api/extension_api.h b/chrome/common/extensions/api/extension_api.h index 6e2c604..25ac679 100644 --- a/chrome/common/extensions/api/extension_api.h +++ b/chrome/common/extensions/api/extension_api.h @@ -12,11 +12,8 @@ #include "base/basictypes.h" #include "base/memory/linked_ptr.h" -#include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/values.h" -#include "chrome/common/extensions/feature.h" -#include "chrome/common/extensions/url_pattern_set.h" namespace base { class DictionaryValue; @@ -24,7 +21,6 @@ class ListValue; class Value; } -class GURL; class Extension; class ExtensionPermissionSet; @@ -33,6 +29,18 @@ namespace extensions { // C++ Wrapper for the JSON API definitions in chrome/common/extensions/api/. class ExtensionAPI { public: + // Filtering option for the GetSchemas functions. + enum GetSchemasFilter { + // Returns all schemas that an extension has permission for. + ALL, + + // Returns schemas for only APIs with unprivileged components (i.e. those + // where !IsWholeAPIPrivileged). + ONLY_UNPRIVILEGED + }; + + typedef std::map<std::string, linked_ptr<const DictionaryValue> > SchemaMap; + // Returns the single instance of this class. static ExtensionAPI* GetInstance(); @@ -42,17 +50,26 @@ class ExtensionAPI { // content scripts, or other low-privileged contexts. bool IsPrivileged(const std::string& name) const; + // Returns whether *every* path in the API is privileged. This will be false + // for APIs such as "storage" which is entirely unprivileged, and "test" + // which has unprivileged components. + bool IsWholeAPIPrivileged(const std::string& api_name) const; + + // Gets a map of API name (aka namespace) to API schema. + const SchemaMap& schemas() { return schemas_; } + // Gets the schema for the extension API with namespace |api_name|. // Ownership remains with this object. const base::DictionaryValue* GetSchema(const std::string& api_name) const; - // Gets the APIs available to |context| given an |extension| and |url|. The - // extension or URL may not be relevant to all contexts, and may be left - // NULL/empty. - scoped_ptr<std::set<std::string> > GetAPIsForContext( - Feature::Context context, - const Extension* extension, - const GURL& url) const; + // Gets the API schemas that are available to an Extension. + void GetSchemasForExtension(const Extension& extension, + GetSchemasFilter filter, + SchemaMap* out) const; + + // Gets the schemas for the default set of APIs that are available to every + // extension. + void GetDefaultSchemas(GetSchemasFilter filter, SchemaMap* out) const; private: friend struct DefaultSingletonTraits<ExtensionAPI>; @@ -76,34 +93,18 @@ class ExtensionAPI { const std::string& child_kind, const std::string& child_name) const; - // Adds all APIs to |out| that |extension| has any permission (required or - // optional) to use. - void GetAllowedAPIs( - const Extension* extension, std::set<std::string>* out) const; + // Gets the schemas for the APIs that are allowed by a permission set. + void GetSchemasForPermissions(const ExtensionPermissionSet& permissions, + GetSchemasFilter filter, + SchemaMap* out) const; // Adds dependent schemas to |out| as determined by the "dependencies" // property. - void ResolveDependencies(std::set<std::string>* out) const; - - // 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) const; - - // Removes all APIs from |apis| which are *entirely* privileged. This won't - // include APIs such as "storage" which is entirely unprivileged, nor - // "extension" which has unprivileged components. - void RemovePrivilegedAPIs(std::set<std::string>* apis) const; - - // Adds an APIs that match |url| to |out|. - void GetAPIsMatchingURL(const GURL& url, std::set<std::string>* out) const; + void ResolveDependencies(SchemaMap* out) const; static ExtensionAPI* instance_; // Schemas for each namespace. - typedef std::map<std::string, linked_ptr<const DictionaryValue> > SchemaMap; SchemaMap schemas_; // APIs that are entirely unprivileged. @@ -112,9 +113,6 @@ class ExtensionAPI { // APIs that are not entirely unprivileged, but have unprivileged components. std::set<std::string> partially_unprivileged_apis_; - // APIs that have URL matching permissions. - std::map<std::string, URLPatternSet> url_matching_apis_; - DISALLOW_COPY_AND_ASSIGN(ExtensionAPI); }; diff --git a/chrome/common/extensions/api/extension_api_unittest.cc b/chrome/common/extensions/api/extension_api_unittest.cc index 3f4a0af..70982f7 100644 --- a/chrome/common/extensions/api/extension_api_unittest.cc +++ b/chrome/common/extensions/api/extension_api_unittest.cc @@ -13,10 +13,7 @@ #include "chrome/common/extensions/extension.h" #include "testing/gtest/include/gtest/gtest.h" -namespace { - using extensions::ExtensionAPI; -using extensions::Feature; TEST(ExtensionAPI, IsPrivileged) { ExtensionAPI* extension_api = ExtensionAPI::GetInstance(); @@ -36,8 +33,6 @@ TEST(ExtensionAPI, IsPrivileged) { EXPECT_TRUE(extension_api->IsPrivileged("history.search")); // Whole APIs that are unprivileged. - EXPECT_FALSE(extension_api->IsPrivileged("app.getDetails")); - EXPECT_FALSE(extension_api->IsPrivileged("app.isInstalled")); EXPECT_FALSE(extension_api->IsPrivileged("storage.local")); EXPECT_FALSE(extension_api->IsPrivileged("storage.local.onChanged")); EXPECT_FALSE(extension_api->IsPrivileged("storage.local.set")); @@ -45,18 +40,34 @@ TEST(ExtensionAPI, IsPrivileged) { EXPECT_FALSE(extension_api->IsPrivileged("storage.set")); } -scoped_refptr<Extension> CreateExtensionWithPermissions( - const std::set<std::string>& permissions) { +TEST(ExtensionAPI, IsWholeAPIPrivileged) { + ExtensionAPI* extension_api = ExtensionAPI::GetInstance(); + + // Completely unprivileged. + EXPECT_FALSE(extension_api->IsWholeAPIPrivileged("storage")); + + // Partially unprivileged. + EXPECT_FALSE(extension_api->IsWholeAPIPrivileged("extension")); + EXPECT_FALSE(extension_api->IsWholeAPIPrivileged("test")); + + // Nothing unprivileged. + EXPECT_TRUE(extension_api->IsWholeAPIPrivileged("history")); + + // Paranoid above... paranoid here, too. + EXPECT_TRUE(extension_api->IsWholeAPIPrivileged("")); + EXPECT_TRUE(extension_api->IsWholeAPIPrivileged("<unknown-namespace>")); +} + +TEST(ExtensionAPI, Depends) { + // Fake extension with the "ttsEngine" permission but not the "tts" + // permission; it must load TTS. DictionaryValue manifest; - manifest.SetString("name", "extension"); + manifest.SetString("name", "test extension"); manifest.SetString("version", "1.0"); { - scoped_ptr<ListValue> permissions_list(new ListValue()); - for (std::set<std::string>::const_iterator i = permissions.begin(); - i != permissions.end(); ++i) { - permissions_list->Append(Value::CreateStringValue(*i)); - } - manifest.Set("permissions", permissions_list.release()); + scoped_ptr<ListValue> permissions(new ListValue()); + permissions->Append(Value::CreateStringValue("ttsEngine")); + manifest.Set("permissions", permissions.release()); } std::string error; @@ -65,105 +76,8 @@ scoped_refptr<Extension> CreateExtensionWithPermissions( CHECK(extension.get()); CHECK(error.empty()); - return extension; -} - -scoped_refptr<Extension> CreateExtensionWithPermission( - const std::string& permission) { - std::set<std::string> permissions; - permissions.insert(permission); - return CreateExtensionWithPermissions(permissions); -} - -TEST(ExtensionAPI, ExtensionWithUnprivilegedAPIs) { - scoped_refptr<Extension> extension; - { - std::set<std::string> permissions; - permissions.insert("storage"); - permissions.insert("history"); - extension = CreateExtensionWithPermissions(permissions); - } - - scoped_ptr<std::set<std::string> > privileged_apis = - ExtensionAPI::GetInstance()->GetAPIsForContext( - Feature::PRIVILEGED_CONTEXT, extension.get(), GURL()); - - scoped_ptr<std::set<std::string> > unprivileged_apis = - ExtensionAPI::GetInstance()->GetAPIsForContext( - Feature::UNPRIVILEGED_CONTEXT, extension.get(), GURL()); - - scoped_ptr<std::set<std::string> > content_script_apis = - ExtensionAPI::GetInstance()->GetAPIsForContext( - Feature::CONTENT_SCRIPT_CONTEXT, extension.get(), GURL()); - - // "storage" is completely unprivileged. - EXPECT_EQ(1u, privileged_apis->count("storage")); - EXPECT_EQ(1u, unprivileged_apis->count("storage")); - EXPECT_EQ(1u, content_script_apis->count("storage")); - - // "extension" is partially unprivileged. - EXPECT_EQ(1u, privileged_apis->count("extension")); - EXPECT_EQ(1u, unprivileged_apis->count("extension")); - EXPECT_EQ(1u, content_script_apis->count("extension")); - - // "history" is entirely privileged. - EXPECT_EQ(1u, privileged_apis->count("history")); - EXPECT_EQ(0u, unprivileged_apis->count("history")); - EXPECT_EQ(0u, content_script_apis->count("history")); -} - -TEST(ExtensionAPI, ExtensionWithDependencies) { - // Extension with the "ttsEngine" permission but not the "tts" permission; it - // must load TTS. - { - scoped_refptr<Extension> extension = - CreateExtensionWithPermission("ttsEngine"); - scoped_ptr<std::set<std::string> > apis = - ExtensionAPI::GetInstance()->GetAPIsForContext( - Feature::PRIVILEGED_CONTEXT, extension.get(), GURL()); - EXPECT_EQ(1u, apis->count("ttsEngine")); - EXPECT_EQ(1u, apis->count("tts")); - } - - // Conversely, extension with the "tts" permission but not the "ttsEngine" - // permission shouldn't get the "ttsEngine" permission. - { - scoped_refptr<Extension> extension = - CreateExtensionWithPermission("tts"); - scoped_ptr<std::set<std::string> > apis = - ExtensionAPI::GetInstance()->GetAPIsForContext( - Feature::PRIVILEGED_CONTEXT, extension.get(), GURL()); - EXPECT_EQ(0u, apis->count("ttsEngine")); - EXPECT_EQ(1u, apis->count("tts")); - } -} - -bool MatchesURL(const std::string& api_name, const std::string& url) { - scoped_ptr<std::set<std::string> > apis = - ExtensionAPI::GetInstance()->GetAPIsForContext( - Feature::WEB_PAGE_CONTEXT, NULL, GURL(url)); - return apis->count(api_name); -} - -TEST(ExtensionAPI, URLMatching) { - // "app" API is available to all URLs that content scripts can be injected. - EXPECT_TRUE(MatchesURL("app", "http://example.com/example.html")); - EXPECT_TRUE(MatchesURL("app", "https://blah.net")); - EXPECT_TRUE(MatchesURL("app", "file://somefile.html")); - - // But not internal URLs (for chrome-extension:// the app API is injected by - // GetSchemasForExtension). - EXPECT_FALSE(MatchesURL("app", "about:flags")); - EXPECT_FALSE(MatchesURL("app", "chrome://flags")); - EXPECT_FALSE(MatchesURL("app", "chrome-extension://fakeextension")); - - // "storage" API (for example) isn't available to any URLs. - EXPECT_FALSE(MatchesURL("storage", "http://example.com/example.html")); - EXPECT_FALSE(MatchesURL("storage", "https://blah.net")); - EXPECT_FALSE(MatchesURL("storage", "file://somefile.html")); - EXPECT_FALSE(MatchesURL("storage", "about:flags")); - EXPECT_FALSE(MatchesURL("storage", "chrome://flags")); - EXPECT_FALSE(MatchesURL("storage", "chrome-extension://fakeextension")); + ExtensionAPI::SchemaMap schemas; + ExtensionAPI::GetInstance()->GetSchemasForExtension( + *extension, ExtensionAPI::ALL, &schemas); + EXPECT_EQ(1u, schemas.count("tts")); } - -} // namespace |