diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-09 07:51:29 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-09 07:51:29 +0000 |
commit | c7f74e7ca7e2b2428fb7434b0ef0b7a9b4bfbff8 (patch) | |
tree | d69b0da7ab33542edaae9788d8e7d07ca13c98b6 | |
parent | 9b83ab913c7a8574d5b50b9113df93cc1fcb7083 (diff) | |
download | chromium_src-c7f74e7ca7e2b2428fb7434b0ef0b7a9b4bfbff8.zip chromium_src-c7f74e7ca7e2b2428fb7434b0ef0b7a9b4bfbff8.tar.gz chromium_src-c7f74e7ca7e2b2428fb7434b0ef0b7a9b4bfbff8.tar.bz2 |
Revert 125811 - 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.
BUG=104100,117282
TEST=unit_tests,browser_tests
Review URL: http://codereview.chromium.org/9460002
TBR=kalman@chromium.org
Review URL: https://chromiumcodereview.appspot.com/9653022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125813 0039d316-1c4b-4281-b951-d872f2087c98
30 files changed, 514 insertions, 565 deletions
diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc index 67a9acb..91da264 100644 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc +++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc @@ -104,6 +104,7 @@ void ChromeRenderViewHostObserver::InitRenderViewForExtensions() { content::RenderProcessHost* process = render_view_host()->GetProcess(); if (extension->is_app()) { + Send(new ExtensionMsg_ActivateApplication(extension->id())); // Though we already record the associated process ID for the renderer in // InitRenderViewHostForExtensions, the process might have crashed and been // restarted (hence the re-initialization), so we need to update that @@ -126,21 +127,13 @@ void ChromeRenderViewHostObserver::InitRenderViewForExtensions() { } } - switch (type) { - case Extension::TYPE_EXTENSION: - case Extension::TYPE_USER_SCRIPT: - case Extension::TYPE_HOSTED_APP: - case Extension::TYPE_PACKAGED_APP: - case Extension::TYPE_PLATFORM_APP: - Send(new ExtensionMsg_ActivateExtension(extension->id())); - break; - - case Extension::TYPE_UNKNOWN: - case Extension::TYPE_THEME: - break; - - default: - NOTREACHED(); + if (type == Extension::TYPE_EXTENSION || + type == Extension::TYPE_USER_SCRIPT || + type == Extension::TYPE_PACKAGED_APP || + type == Extension::TYPE_PLATFORM_APP || + (type == Extension::TYPE_HOSTED_APP && + extension->location() == Extension::COMPONENT)) { + Send(new ExtensionMsg_ActivateExtension(extension->id())); } } diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 89422f1..dcd2806 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -74,6 +74,8 @@ 'renderer/extensions/chrome_v8_extension_handler.h', 'renderer/extensions/context_menus_custom_bindings.cc', 'renderer/extensions/context_menus_custom_bindings.h', + 'renderer/extensions/custom_bindings_util.cc', + 'renderer/extensions/custom_bindings_util.h', 'renderer/extensions/event_bindings.cc', 'renderer/extensions/event_bindings.h', 'renderer/extensions/experimental.socket_custom_bindings.cc', @@ -128,7 +130,7 @@ 'renderer/resource_bundle_source_map.cc', 'renderer/resource_bundle_source_map.h', 'renderer/resources/extensions/apitest.js', - 'renderer/resources/extensions/app_custom_bindings.js', + 'renderer/resources/extensions/app.js', 'renderer/resources/extensions/browser_action_custom_bindings.js', 'renderer/resources/extensions/chrome_private_custom_bindings.js', 'renderer/resources/extensions/context_menus_custom_bindings.js', diff --git a/chrome/common/common_resources.grd b/chrome/common/common_resources.grd index e556928..253cb1a 100644 --- a/chrome/common/common_resources.grd +++ b/chrome/common/common_resources.grd @@ -10,7 +10,6 @@ <release seq="1"> <includes> <include name="IDR_EXTENSION_MANIFEST_FEATURES" file="extensions\api\_manifest_features.json" type="BINDATA" /> - <include name="IDR_EXTENSION_API_JSON_APP" file="extensions\api\app.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_BOOKMARKS" file="extensions\api\bookmarks.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_BROWSERACTION" file="extensions\api\browserAction.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_BROWSING_DATA" file="extensions\api\browsingData.json" type="BINDATA" /> 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 ade7ce6..eeece57 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, @@ -164,27 +178,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() { @@ -264,109 +257,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 diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h index 7fcd56f..276020e 100644 --- a/chrome/common/extensions/extension_messages.h +++ b/chrome/common/extensions/extension_messages.h @@ -193,6 +193,10 @@ IPC_MESSAGE_CONTROL1(ExtensionMsg_SetFunctionNames, IPC_MESSAGE_CONTROL1(ExtensionMsg_ActivateExtension, std::string /* extension_id */) +// Marks an application as 'active' in a process. +IPC_MESSAGE_CONTROL1(ExtensionMsg_ActivateApplication, + std::string /* extension_id */) + // Notifies the renderer that extensions were loaded in the browser. IPC_MESSAGE_CONTROL1(ExtensionMsg_Loaded, std::vector<ExtensionMsg_Loaded_Params>) diff --git a/chrome/common/extensions/extension_permission_set.cc b/chrome/common/extensions/extension_permission_set.cc index f5c8397..18e1fec 100644 --- a/chrome/common/extensions/extension_permission_set.cc +++ b/chrome/common/extensions/extension_permission_set.cc @@ -42,7 +42,6 @@ bool RcdBetterThan(std::string a, std::string b) { // Names of API modules that can be used without listing it in the // permissions section of the manifest. const char* kNonPermissionModuleNames[] = { - "app", "browserAction", "devtools", "extension", @@ -617,16 +616,6 @@ std::set<std::string> ExtensionPermissionSet::GetAPIsAsStrings() const { return apis_str; } -std::set<std::string> ExtensionPermissionSet:: - GetAPIsWithAnyAccessAsStrings() const { - std::set<std::string> result = GetAPIsAsStrings(); - for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) - result.insert(kNonPermissionModuleNames[i]); - for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) - result.insert(GetPermissionName(kNonPermissionFunctionNames[i])); - return result; -} - bool ExtensionPermissionSet::HasAnyAccessToAPI( const std::string& api_name) const { if (HasAccessToFunction(api_name)) diff --git a/chrome/common/extensions/extension_permission_set.h b/chrome/common/extensions/extension_permission_set.h index 0b7c1a3..c219698 100644 --- a/chrome/common/extensions/extension_permission_set.h +++ b/chrome/common/extensions/extension_permission_set.h @@ -183,6 +183,8 @@ class ExtensionAPIPermission { kTypeDefault = kTypeAll - kTypeHostedApp, }; + typedef std::set<ID> IDSet; + ~ExtensionAPIPermission(); // Returns the localized permission message associated with this api. @@ -392,12 +394,6 @@ class ExtensionPermissionSet // Gets the API permissions in this set as a set of strings. std::set<std::string> GetAPIsAsStrings() const; - // Gets the API permissions in this set, plus any that have implicit access - // (such as APIs that require no permissions, or APIs with functions that - // require no permissions). - // TODO(kalman): return scoped_ptr to avoid copying. - std::set<std::string> GetAPIsWithAnyAccessAsStrings() const; - // Returns whether this namespace has any functions which the extension has // permission to use. For example, even though the extension may not have // the "tabs" permission, "tabs.create" requires no permissions so diff --git a/chrome/common/extensions/feature.cc b/chrome/common/extensions/feature.cc index b2158e4..aad4d01 100644 --- a/chrome/common/extensions/feature.cc +++ b/chrome/common/extensions/feature.cc @@ -21,7 +21,6 @@ struct Mappings { contexts["privileged"] = extensions::Feature::PRIVILEGED_CONTEXT; contexts["unprivileged"] = extensions::Feature::UNPRIVILEGED_CONTEXT; contexts["content_script"] = extensions::Feature::CONTENT_SCRIPT_CONTEXT; - contexts["web_page"] = extensions::Feature::WEB_PAGE_CONTEXT; locations["component"] = extensions::Feature::COMPONENT_LOCATION; diff --git a/chrome/common/extensions/feature.h b/chrome/common/extensions/feature.h index 2d1a00a..0a95689 100644 --- a/chrome/common/extensions/feature.h +++ b/chrome/common/extensions/feature.h @@ -22,21 +22,11 @@ namespace extensions { class Feature { public: // The JavaScript contexts the feature is supported in. - // TODO(kalman): s/PRIVILEGED/BLESSED_EXTENSION/ enum Context { UNSPECIFIED_CONTEXT, - - // A context in a privileged extension process. - PRIVILEGED_CONTEXT, - - // A context in a normal, unprivileged renderer. - UNPRIVILEGED_CONTEXT, - - // A context from a content script. - CONTENT_SCRIPT_CONTEXT, - - // A normal web page. This should have an associated URL matching pattern. - WEB_PAGE_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. diff --git a/chrome/common/extensions/feature_unittest.cc b/chrome/common/extensions/feature_unittest.cc index ea1aa86..0ebaaa9 100644 --- a/chrome/common/extensions/feature_unittest.cc +++ b/chrome/common/extensions/feature_unittest.cc @@ -220,14 +220,12 @@ TEST(ExtensionFeatureTest, ParseContexts) { contexts->Append(Value::CreateStringValue("privileged")); contexts->Append(Value::CreateStringValue("unprivileged")); contexts->Append(Value::CreateStringValue("content_script")); - contexts->Append(Value::CreateStringValue("web_page")); value->Set("contexts", contexts); scoped_ptr<Feature> feature(Feature::Parse(value.get())); - EXPECT_EQ(4u, feature->contexts()->size()); + 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)); - EXPECT_TRUE(feature->contexts()->count(Feature::WEB_PAGE_CONTEXT)); value->SetString("contexts", "all"); scoped_ptr<Feature> feature2(Feature::Parse(value.get())); diff --git a/chrome/renderer/extensions/app_bindings.cc b/chrome/renderer/extensions/app_bindings.cc index a5a902a..34c8047 100644 --- a/chrome/renderer/extensions/app_bindings.cc +++ b/chrome/renderer/extensions/app_bindings.cc @@ -55,6 +55,7 @@ const char* kInvalidCallbackIdError = "Invalid callbackId"; } // namespace + AppBindings::AppBindings(ExtensionDispatcher* dispatcher, ChromeV8Context* context) : ChromeV8Extension(dispatcher), @@ -71,6 +72,7 @@ AppBindings::AppBindings(ExtensionDispatcher* dispatcher, base::Bind(&AppBindings::GetAppNotifyChannel, base::Unretained(this))); } + v8::Handle<v8::Value> AppBindings::GetIsInstalled( const v8::Arguments& args) { // TODO(aa): Hm, maybe ExtensionBindingsContext should have GetExtension() @@ -79,8 +81,9 @@ v8::Handle<v8::Value> AppBindings::GetIsInstalled( extension_dispatcher_->extensions()->GetByID(context_->extension_id()); // TODO(aa): Why only hosted app? + // TODO(aa): GARRR - why is there IsExtensionActive and IsApplicationActive!? bool result = extension && extension->is_hosted_app() && - extension_dispatcher_->IsExtensionActive(extension->id()); + extension_dispatcher_->IsApplicationActive(extension->id()); return v8::Boolean::New(result); } diff --git a/chrome/renderer/extensions/chrome_v8_context.cc b/chrome/renderer/extensions/chrome_v8_context.cc index 6d59a19..7db10ed 100644 --- a/chrome/renderer/extensions/chrome_v8_context.cc +++ b/chrome/renderer/extensions/chrome_v8_context.cc @@ -15,8 +15,6 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "v8/include/v8.h" -using extensions::Feature; - namespace { const char kChromeHidden[] = "chromeHidden"; @@ -26,13 +24,11 @@ const char kValidateCallbacks[] = "validateCallbacks"; const char kValidateAPI[] = "validateAPI"; #endif -std::string GetContextTypeDescription(Feature::Context context_type) { +std::string GetContextTypeDescription( + ChromeV8Context::ContextType context_type) { switch (context_type) { - case Feature::UNSPECIFIED_CONTEXT: return "unspecified"; - case Feature::PRIVILEGED_CONTEXT: return "privileged"; - case Feature::UNPRIVILEGED_CONTEXT: return "unprivileged"; - case Feature::CONTENT_SCRIPT_CONTEXT: return "content script"; - case Feature::WEB_PAGE_CONTEXT: return "web page"; + case ChromeV8Context::OTHER: return "other"; + case ChromeV8Context::CONTENT_SCRIPT: return "content script"; } NOTREACHED(); return ""; @@ -43,7 +39,7 @@ std::string GetContextTypeDescription(Feature::Context context_type) { ChromeV8Context::ChromeV8Context(v8::Handle<v8::Context> v8_context, WebKit::WebFrame* web_frame, const std::string& extension_id, - Feature::Context context_type) + ChromeV8Context::ContextType context_type) : v8_context_(v8::Persistent<v8::Context>::New(v8_context)), web_frame_(web_frame), extension_id_(extension_id), diff --git a/chrome/renderer/extensions/chrome_v8_context.h b/chrome/renderer/extensions/chrome_v8_context.h index e2c8911..3faf9d2 100644 --- a/chrome/renderer/extensions/chrome_v8_context.h +++ b/chrome/renderer/extensions/chrome_v8_context.h @@ -9,7 +9,6 @@ #include <string> #include "base/basictypes.h" -#include "chrome/common/extensions/feature.h" #include "chrome/renderer/module_system.h" #include "v8/include/v8.h" @@ -28,10 +27,19 @@ class RenderView; // we won't need this object and it's a bit less state to keep track of. class ChromeV8Context { public: + enum ContextType { + CONTENT_SCRIPT, + + // TODO(kalman): for now, have this as OTHER, since we only currently need + // know whether something is a content script or not. However, when + // necessary this should enumerate the other types, such as FRAME. + OTHER + }; + ChromeV8Context(v8::Handle<v8::Context> context, WebKit::WebFrame* frame, const std::string& extension_id, - extensions::Feature::Context context_type); + ContextType context_type); ~ChromeV8Context(); v8::Handle<v8::Context> v8_context() const { @@ -49,7 +57,7 @@ class ChromeV8Context { web_frame_ = NULL; } - extensions::Feature::Context context_type() const { + ContextType context_type() const { return context_type_; } @@ -108,7 +116,7 @@ class ChromeV8Context { std::string extension_id_; // The type of context. - extensions::Feature::Context context_type_; + ContextType context_type_; // Owns and structures the JS that is injected to set up extension bindings. scoped_ptr<ModuleSystem> module_system_; diff --git a/chrome/renderer/extensions/chrome_v8_context_set_unittest.cc b/chrome/renderer/extensions/chrome_v8_context_set_unittest.cc index af7b17d..56f4697 100644 --- a/chrome/renderer/extensions/chrome_v8_context_set_unittest.cc +++ b/chrome/renderer/extensions/chrome_v8_context_set_unittest.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "base/message_loop.h" -#include "chrome/common/extensions/feature.h" #include "chrome/renderer/extensions/chrome_v8_context.h" #include "chrome/renderer/extensions/chrome_v8_context_set.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,11 +21,9 @@ TEST(ChromeV8ContextSet, Lifecycle) { // creating a whole webview. WebKit::WebFrame* frame = reinterpret_cast<WebKit::WebFrame*>(1); std::string extension_id = "00000000000000000000000000000000"; - ChromeV8Context* context = new ChromeV8Context( - v8_context, - frame, - extension_id, - extensions::Feature::PRIVILEGED_CONTEXT); + ChromeV8Context* context = + new ChromeV8Context( + v8_context, frame, extension_id, ChromeV8Context::OTHER); context_set.Add(context); EXPECT_EQ(1u, context_set.GetAll().count(context)); diff --git a/chrome/renderer/extensions/custom_bindings_util.cc b/chrome/renderer/extensions/custom_bindings_util.cc new file mode 100644 index 0000000..5de0470b --- /dev/null +++ b/chrome/renderer/extensions/custom_bindings_util.cc @@ -0,0 +1,92 @@ +// 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/renderer/extensions/custom_bindings_util.h" + +#include <map> + +#include "base/logging.h" +#include "base/string_util.h" +#include "chrome/common/extensions/api/extension_api.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/renderer/extensions/chrome_v8_extension.h" +#include "chrome/renderer/extensions/chrome_private_custom_bindings.h" +#include "chrome/renderer/extensions/context_menus_custom_bindings.h" +#include "chrome/renderer/extensions/experimental.socket_custom_bindings.h" +#include "chrome/renderer/extensions/extension_custom_bindings.h" +#include "chrome/renderer/extensions/extension_dispatcher.h" +#include "chrome/renderer/extensions/file_browser_handler_custom_bindings.h" +#include "chrome/renderer/extensions/file_browser_private_custom_bindings.h" +#include "chrome/renderer/extensions/i18n_custom_bindings.h" +#include "chrome/renderer/extensions/page_actions_custom_bindings.h" +#include "chrome/renderer/extensions/page_capture_custom_bindings.h" +#include "chrome/renderer/extensions/tabs_custom_bindings.h" +#include "chrome/renderer/extensions/tts_custom_bindings.h" +#include "chrome/renderer/extensions/web_request_custom_bindings.h" +#include "grit/renderer_resources.h" +#include "v8/include/v8.h" + +namespace extensions { + +namespace custom_bindings_util { + +// Extracts the name of an API from the name of the V8 extension which contains +// custom bindings for it (see kCustomBindingNames). +std::string GetAPIName(const std::string& v8_extension_name) { + // Extract the name of the API from the v8 extension name. + // This is "${api_name}" in "extensions/${api_name}_custom_bindings.js". + std::string prefix = "extensions/"; + const bool kCaseSensitive = true; + if (!StartsWithASCII(v8_extension_name, prefix, kCaseSensitive)) + return ""; + + std::string suffix = "_custom_bindings.js"; + if (!EndsWith(v8_extension_name, suffix, kCaseSensitive)) + return ""; + + // By convention, filenames are use unix_hacker_style, but the APIs we expose + // to javascript use camelCase. + std::string not_camelcase = v8_extension_name.substr( + prefix.size(), + v8_extension_name.size() - prefix.size() - suffix.size()); + + std::string camelcase; + bool next_to_upper = false; + for (std::string::iterator it = not_camelcase.begin(); + it != not_camelcase.end(); ++it) { + if (*it == '_') { + next_to_upper = true; + } else if (next_to_upper) { + camelcase.push_back(base::ToUpperASCII(*it)); + next_to_upper = false; + } else { + camelcase.push_back(*it); + } + } + + return camelcase; +} + +bool AllowAPIInjection(const std::string& api_name, + const Extension& extension, + ExtensionDispatcher* extension_dispatcher) { + CHECK(api_name != ""); + + // As in ExtensionAPI::GetSchemasForExtension, we need to allow any bindings + // for an API that the extension *might* have permission to use. + bool allowed = + extension.required_permission_set()->HasAnyAccessToAPI(api_name) || + extension.optional_permission_set()->HasAnyAccessToAPI(api_name); + + if (extension_dispatcher->is_extension_process()) { + return allowed; + } else { + return allowed && + !ExtensionAPI::GetInstance()->IsWholeAPIPrivileged(api_name); + } +} + +} // namespace custom_bindings_util + +} // namespace extensions diff --git a/chrome/renderer/extensions/custom_bindings_util.h b/chrome/renderer/extensions/custom_bindings_util.h new file mode 100644 index 0000000..73d026d --- /dev/null +++ b/chrome/renderer/extensions/custom_bindings_util.h @@ -0,0 +1,44 @@ +// 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_RENDERER_EXTENSIONS_CUSTOM_BINDINGS_UTIL_H_ +#define CHROME_RENDERER_EXTENSIONS_CUSTOM_BINDINGS_UTIL_H_ +#pragma once + +#include <string> +#include <vector> + +#include "chrome/renderer/extensions/chrome_v8_extension.h" + +class Extension; +class ExtensionDispatcher; + +namespace v8 { +class Extension; +} + +namespace extensions { + +// Utilities for managing the set of V8 extensions for extension API custom +// bindings. +namespace custom_bindings_util { + +// Extracts the name of an API from the name of the V8 extension which contains +// custom bindings for it. +// Returns an empty string if the extension is not for a custom binding. +std::string GetAPIName(const std::string& v8_extension_name); + +// Returns whether the custom binding for an API should be allowed to run for +// |extension|. This is based on whether the extension has any permission +// (required or optional) for that API, and what context the APIs are intended +// to run in. +bool AllowAPIInjection(const std::string& api_name, + const Extension& extension, + ExtensionDispatcher* extension_dispatcher); + +} // namespace custom_bindings_util + +} // namespace extensions + +#endif // CHROME_RENDERER_EXTENSIONS_CUSTOM_BINDINGS_UTIL_H_ diff --git a/chrome/renderer/extensions/extension_custom_bindings.cc b/chrome/renderer/extensions/extension_custom_bindings.cc index afb45e0..c4895cd 100644 --- a/chrome/renderer/extensions/extension_custom_bindings.cc +++ b/chrome/renderer/extensions/extension_custom_bindings.cc @@ -22,6 +22,9 @@ #include "v8/include/v8.h" #include "webkit/glue/webkit_glue.h" +using WebKit::WebFrame; +using WebKit::WebView; + namespace extensions { namespace { diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc index 1961a2f..0d12b18 100644 --- a/chrome/renderer/extensions/extension_dispatcher.cc +++ b/chrome/renderer/extensions/extension_dispatcher.cc @@ -18,23 +18,12 @@ #include "chrome/renderer/extensions/app_bindings.h" #include "chrome/renderer/extensions/chrome_v8_context.h" #include "chrome/renderer/extensions/chrome_v8_extension.h" -#include "chrome/renderer/extensions/chrome_private_custom_bindings.h" -#include "chrome/renderer/extensions/context_menus_custom_bindings.h" +#include "chrome/renderer/extensions/custom_bindings_util.h" #include "chrome/renderer/extensions/event_bindings.h" -#include "chrome/renderer/extensions/experimental.socket_custom_bindings.h" -#include "chrome/renderer/extensions/extension_custom_bindings.h" #include "chrome/renderer/extensions/extension_groups.h" -#include "chrome/renderer/extensions/file_browser_handler_custom_bindings.h" -#include "chrome/renderer/extensions/file_browser_private_custom_bindings.h" -#include "chrome/renderer/extensions/i18n_custom_bindings.h" #include "chrome/renderer/extensions/miscellaneous_bindings.h" -#include "chrome/renderer/extensions/page_actions_custom_bindings.h" -#include "chrome/renderer/extensions/page_capture_custom_bindings.h" #include "chrome/renderer/extensions/schema_generated_bindings.h" -#include "chrome/renderer/extensions/tabs_custom_bindings.h" -#include "chrome/renderer/extensions/tts_custom_bindings.h" #include "chrome/renderer/extensions/user_script_slave.h" -#include "chrome/renderer/extensions/web_request_custom_bindings.h" #include "chrome/renderer/extensions/webstore_bindings.h" #include "chrome/renderer/module_system.h" #include "chrome/renderer/native_handler.h" @@ -44,19 +33,38 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedUserGesture.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRequest.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedUserGesture.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "ui/base/resource/resource_bundle.h" #include "v8/include/v8.h" +#include "chrome/renderer/extensions/chrome_private_custom_bindings.h" +#include "chrome/renderer/extensions/context_menus_custom_bindings.h" +#include "chrome/renderer/extensions/experimental.socket_custom_bindings.h" +#include "chrome/renderer/extensions/extension_custom_bindings.h" +#include "chrome/renderer/extensions/file_browser_handler_custom_bindings.h" +#include "chrome/renderer/extensions/file_browser_private_custom_bindings.h" +#include "chrome/renderer/extensions/i18n_custom_bindings.h" +#include "chrome/renderer/extensions/page_actions_custom_bindings.h" +#include "chrome/renderer/extensions/page_capture_custom_bindings.h" +#include "chrome/renderer/extensions/tabs_custom_bindings.h" +#include "chrome/renderer/extensions/tts_custom_bindings.h" +#include "chrome/renderer/extensions/web_request_custom_bindings.h" + namespace { static const int64 kInitialExtensionIdleHandlerDelayMs = 5*1000; static const int64 kMaxExtensionIdleHandlerDelayMs = 5*60*1000; +ChromeV8Context::ContextType ExtensionGroupToContextType(int extension_group) { + if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) + return ChromeV8Context::CONTENT_SCRIPT; + return ChromeV8Context::OTHER; +} + class ChromeHiddenNativeHandler : public NativeHandler { public: ChromeHiddenNativeHandler() { @@ -91,6 +99,41 @@ class PrintNativeHandler : public NativeHandler { } }; +class ContextInfoNativeHandler : public NativeHandler { + public: + explicit ContextInfoNativeHandler(ExtensionDispatcher* extension_dispatcher, + bool is_bindings_allowed, + WebKit::WebFrame* frame, + int world_id) + : extension_dispatcher_(extension_dispatcher), + is_bindings_allowed_(is_bindings_allowed), + frame_(frame), + world_id_(world_id) { + RouteFunction("IsBindingsAllowed", + base::Bind(&ContextInfoNativeHandler::IsBindingsAllowed, + base::Unretained(this))); + RouteFunction("IsAPIAllowed", + base::Bind(&ContextInfoNativeHandler::IsAPIAllowed, + base::Unretained(this))); + } + + v8::Handle<v8::Value> IsBindingsAllowed(const v8::Arguments& args) { + return v8::Boolean::New(is_bindings_allowed_); + } + + v8::Handle<v8::Value> IsAPIAllowed(const v8::Arguments& args) { + std::string custom_api_name = *v8::String::AsciiValue(args[0]->ToString()); + return v8::Boolean::New(extension_dispatcher_->AllowCustomAPI( + frame_, custom_api_name, world_id_)); + } + + private: + ExtensionDispatcher* extension_dispatcher_; + bool is_bindings_allowed_; + WebKit::WebFrame* frame_; + int world_id_; +}; + } using namespace extensions; @@ -140,6 +183,7 @@ bool ExtensionDispatcher::OnControlMessageReceived( IPC_MESSAGE_HANDLER(ExtensionMsg_SetScriptingWhitelist, OnSetScriptingWhitelist) IPC_MESSAGE_HANDLER(ExtensionMsg_ActivateExtension, OnActivateExtension) + IPC_MESSAGE_HANDLER(ExtensionMsg_ActivateApplication, OnActivateApplication) IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions) IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts) IPC_MESSAGE_HANDLER(ExtensionMsg_UsingWebRequestAPI, OnUsingWebRequestAPI) @@ -289,6 +333,12 @@ void ExtensionDispatcher::OnSetScriptingWhitelist( Extension::SetScriptingWhitelist(extension_ids); } +bool ExtensionDispatcher::IsApplicationActive( + const std::string& extension_id) const { + return active_application_ids_.find(extension_id) != + active_application_ids_.end(); +} + bool ExtensionDispatcher::IsExtensionActive( const std::string& extension_id) const { return active_extension_ids_.find(extension_id) != @@ -323,8 +373,34 @@ bool ExtensionDispatcher::AllowScriptExtension( return true; } +bool ExtensionDispatcher::AllowCustomAPI( + WebFrame* frame, + const std::string& custom_binding_api_name, + int world_id) { + std::string extension_id = GetExtensionID(frame, world_id); + if (test_extension_id_ == extension_id) + return true; + const Extension* extension = extensions_.GetByID(extension_id); + if (!extension) { + // This can happen when a resource is blocked due to CSP; a valid + // chrome-extension:// URL is navigated to, so it passes the initial + // checks, but the URL gets changed to "chrome-extension://invalid" + // afterwards (see chrome_content_renderer_client.cc). An extension + // page still gets loaded, just for the extension with ID "invalid", + // which of course isn't found so GetById extension will be NULL. + // + // Reference: http://crbug.com/111614. + CHECK_EQ("invalid", extension_id); + return false; + } + return custom_bindings_util::AllowAPIInjection( + custom_binding_api_name, *extension, this); +} + void ExtensionDispatcher::RegisterNativeHandlers(ModuleSystem* module_system, ChromeV8Context* context) { + module_system->RegisterNativeHandler("app", + scoped_ptr<NativeHandler>(new AppBindings(this, context))); module_system->RegisterNativeHandler("webstore", scoped_ptr<NativeHandler>(new WebstoreBindings(this, context))); module_system->RegisterNativeHandler("event_bindings", @@ -335,8 +411,6 @@ void ExtensionDispatcher::RegisterNativeHandlers(ModuleSystem* module_system, scoped_ptr<NativeHandler>(SchemaGeneratedBindings::Get(this))); // Custom bindings. - module_system->RegisterNativeHandler("app", - scoped_ptr<NativeHandler>(new AppBindings(this, context))); module_system->RegisterNativeHandler("chrome_private", scoped_ptr<NativeHandler>( new ChromePrivateCustomBindings(this))); @@ -367,6 +441,7 @@ void ExtensionDispatcher::RegisterNativeHandlers(ModuleSystem* module_system, } void ExtensionDispatcher::PopulateSourceMap() { + source_map_.RegisterSource("app", IDR_APP_BINDINGS_JS); source_map_.RegisterSource("webstore", IDR_WEBSTORE_BINDINGS_JS); source_map_.RegisterSource("event_bindings", IDR_EVENT_BINDINGS_JS); source_map_.RegisterSource("miscellaneous_bindings", @@ -377,8 +452,7 @@ void ExtensionDispatcher::PopulateSourceMap() { source_map_.RegisterSource("apitest", IDR_EXTENSION_APITEST_JS); source_map_.RegisterSource("setup_bindings", IDR_SETUP_BINDINGS_JS); - // Custom bindings. - source_map_.RegisterSource("app", IDR_APP_CUSTOM_BINDINGS_JS); + // Custom bindings. source_map_.RegisterSource("browserAction", IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("chromePrivate", @@ -421,29 +495,39 @@ void ExtensionDispatcher::DidCreateScriptContext( extension_group = g_hack_extension_group; std::string extension_id = GetExtensionID(frame, world_id); - ExtensionURLInfo url_info(frame->document().securityOrigin(), - UserScriptSlave::GetDataSourceURLForFrame(frame)); - Feature::Context context_type = - ClassifyJavaScriptContext(extension_id, extension_group, url_info); - ChromeV8Context* context = - new ChromeV8Context(v8_context, frame, extension_id, context_type); + new ChromeV8Context( + v8_context, + frame, + extension_id, + ExtensionGroupToContextType(extension_group)); v8_context_set_.Add(context); scoped_ptr<ModuleSystem> module_system(new ModuleSystem(&source_map_)); RegisterNativeHandlers(module_system.get(), context); + bool is_bindings_allowed = + IsTestExtensionId(extension_id) || + context->context_type() == ChromeV8Context::CONTENT_SCRIPT || + extensions_.ExtensionBindingsAllowed(ExtensionURLInfo( + frame->document().securityOrigin(), + UserScriptSlave::GetDataSourceURLForFrame(frame))); + module_system->RegisterNativeHandler("chrome_hidden", scoped_ptr<NativeHandler>(new ChromeHiddenNativeHandler())); + module_system->RegisterNativeHandler("context_info", + scoped_ptr<NativeHandler>(new ContextInfoNativeHandler( + this, + is_bindings_allowed, + frame, + world_id))); module_system->RegisterNativeHandler("print", scoped_ptr<NativeHandler>(new PrintNativeHandler())); + const Extension* extension = extensions_.GetByID(context->extension_id()); int manifest_version = 1; - { - const Extension* extension = extensions_.GetByID(extension_id); - if (extension) - manifest_version = extension->manifest_version(); - } + if (extension) + manifest_version = extension->manifest_version(); module_system->RunString("var chrome; chrome = chrome || {};", "setup-chrome-object"); @@ -487,7 +571,6 @@ void ExtensionDispatcher::WillReleaseScriptContext( } void ExtensionDispatcher::SetTestExtensionId(const std::string& id) { - CHECK(!id.empty()); test_extension_id_ = id; } @@ -495,6 +578,11 @@ bool ExtensionDispatcher::IsTestExtensionId(const std::string& id) { return !test_extension_id_.empty() && id == test_extension_id_; } +void ExtensionDispatcher::OnActivateApplication( + const std::string& extension_id) { + active_application_ids_.insert(extension_id); +} + void ExtensionDispatcher::OnActivateExtension( const std::string& extension_id) { active_extension_ids_.insert(extension_id); @@ -622,22 +710,3 @@ void ExtensionDispatcher::OnShouldClose(const std::string& extension_id, RenderThread::Get()->Send( new ExtensionHostMsg_ShouldCloseAck(extension_id, sequence_id)); } - -Feature::Context ExtensionDispatcher::ClassifyJavaScriptContext( - const std::string& extension_id, - int extension_group, - const ExtensionURLInfo& url_info) { - if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) - return Feature::CONTENT_SCRIPT_CONTEXT; - - if (IsExtensionActive(extension_id)) - return Feature::PRIVILEGED_CONTEXT; - - if (extensions_.ExtensionBindingsAllowed(url_info)) - return Feature::UNPRIVILEGED_CONTEXT; - - if (url_info.url().is_valid()) - return Feature::WEB_PAGE_CONTEXT; - - return Feature::UNSPECIFIED_CONTEXT; -} diff --git a/chrome/renderer/extensions/extension_dispatcher.h b/chrome/renderer/extensions/extension_dispatcher.h index 3a0d2c1..b1dfb40 100644 --- a/chrome/renderer/extensions/extension_dispatcher.h +++ b/chrome/renderer/extensions/extension_dispatcher.h @@ -14,8 +14,6 @@ #include "base/timer.h" #include "content/public/renderer/render_process_observer.h" #include "chrome/common/extensions/extension_set.h" -#include "chrome/common/extensions/feature.h" -#include "chrome/renderer/extensions/chrome_v8_context.h" #include "chrome/renderer/extensions/chrome_v8_context_set.h" #include "chrome/renderer/resource_bundle_source_map.h" #include "v8/include/v8.h" @@ -56,13 +54,13 @@ class ExtensionDispatcher : public content::RenderProcessObserver { } UserScriptSlave* user_script_slave() { return user_script_slave_.get(); } + bool IsApplicationActive(const std::string& extension_id) const; bool IsExtensionActive(const std::string& extension_id) const; - // Finds the extension ID for the JavaScript context associated with the - // specified |frame| and isolated world. If |world_id| is zero, finds the - // extension ID associated with the main world's JavaScript context. If the - // JavaScript context isn't from an extension, returns empty string. - std::string GetExtensionID(WebKit::WebFrame* frame, int world_id); + // Whether or not we should set up custom bindings for this api. + bool AllowCustomAPI(WebKit::WebFrame* frame, + const std::string& custom_binding_api_name, + int world_id); // See WebKit::WebPermissionClient::allowScriptExtension // TODO(koz): Remove once WebKit no longer calls this. @@ -120,6 +118,7 @@ class ExtensionDispatcher : public content::RenderProcessObserver { const Extension::ScriptingWhitelist& extension_ids); void OnPageActionsUpdated(const std::string& extension_id, const std::vector<std::string>& page_actions); + void OnActivateApplication(const std::string& extension_id); void OnActivateExtension(const std::string& extension_id); void OnUpdatePermissions(int reason_id, const std::string& extension_id, @@ -152,11 +151,9 @@ class ExtensionDispatcher : public content::RenderProcessObserver { // Inserts static source code into |source_map_|. void PopulateSourceMap(); - // Returns the Feature::Context type of context for a JavaScript context. - extensions::Feature::Context ClassifyJavaScriptContext( - const std::string& extension_id, - int extension_group, - const ExtensionURLInfo& url_info); + // Finds the extension ID for the current context. This is determined from + // |world_id| if it's non-zero, or the URL in |frame| if it is. + std::string GetExtensionID(WebKit::WebFrame* frame, int world_id); // True if this renderer is running extensions. bool is_extension_process_; @@ -182,9 +179,12 @@ class ExtensionDispatcher : public content::RenderProcessObserver { // All declared function names. std::set<std::string> function_names_; - // The extensions and apps that are active in this process. + // The extensions that are active in this process. std::set<std::string> active_extension_ids_; + // The applications that are active in this process. + std::set<std::string> active_application_ids_; + // True once WebKit has been initialized (and it is therefore safe to poke). bool is_webkit_initialized_; diff --git a/chrome/renderer/extensions/schema_generated_bindings.cc b/chrome/renderer/extensions/schema_generated_bindings.cc index 6f6bb33..eda15d8 100644 --- a/chrome/renderer/extensions/schema_generated_bindings.cc +++ b/chrome/renderer/extensions/schema_generated_bindings.cc @@ -118,32 +118,30 @@ class ExtensionImpl : public ChromeV8Extension { ChromeV8Context* v8_context = dispatcher->v8_context_set().GetCurrent(); CHECK(v8_context); - // TODO(kalman): can we just cache this in the ChromeV8Context instance? - scoped_ptr<std::set<std::string> > apis; + std::string extension_id = v8_context->extension_id(); + ExtensionAPI::SchemaMap schemas; + ExtensionAPI::GetSchemasFilter filter = + dispatcher->is_extension_process() ? + ExtensionAPI::ALL : ExtensionAPI::ONLY_UNPRIVILEGED; - const std::string& extension_id = v8_context->extension_id(); if (dispatcher->IsTestExtensionId(extension_id)) { - apis.reset(new std::set<std::string>()); - // The minimal set of APIs that tests need. - apis->insert("extension"); + ExtensionAPI::GetInstance()->GetDefaultSchemas(filter, &schemas); } else { - apis = ExtensionAPI::GetInstance()->GetAPIsForContext( - v8_context->context_type(), - dispatcher->extensions()->GetByID(extension_id), - UserScriptSlave::GetDataSourceURLForFrame(v8_context->web_frame())); + const ::Extension* extension = + dispatcher->extensions()->GetByID(extension_id); + CHECK(extension) << extension_id << " not found"; + ExtensionAPI::GetInstance()->GetSchemasForExtension( + *extension, filter, &schemas); } v8::Persistent<v8::Context> context(v8::Context::New()); v8::Context::Scope context_scope(context); - v8::Handle<v8::Array> api(v8::Array::New(apis->size())); + v8::Handle<v8::Array> api(v8::Array::New(schemas.size())); size_t api_index = 0; - for (std::set<std::string>::iterator i = apis->begin(); i != apis->end(); - ++i) { - // TODO(kalman): this caching is actually useless now, because - // SchemaGeneratedBindings is per-context not per-process. We should - // (e.g.) hang a SchemaRegistry off ExtensionDispatcher (which maintains - // a *single* v8::Context rather than multiple ones as here). - api->Set(api_index, GetV8SchemaForAPI(self, context, *i)); + for (ExtensionAPI::SchemaMap::iterator it = schemas.begin(); + it != schemas.end(); ++it) { + std::string api_name = it->first; + api->Set(api_index, GetV8SchemaForAPI(self, context, api_name)); ++api_index; } @@ -325,7 +323,6 @@ class ExtensionImpl : public ChromeV8Extension { namespace extensions { -// static ChromeV8Extension* SchemaGeneratedBindings::Get( ExtensionDispatcher* extension_dispatcher) { return new ExtensionImpl(extension_dispatcher); diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd index e2a7484..99cd777 100644 --- a/chrome/renderer/renderer_resources.grd +++ b/chrome/renderer/renderer_resources.grd @@ -11,6 +11,7 @@ without changes to the corresponding grd file. fb9 --> </outputs> <release seq="1"> <includes> + <include name="IDR_APP_BINDINGS_JS" file="resources\extensions\app.js" type="BINDATA" /> <include name="IDR_BLOCKED_PLUGIN_HTML" file="resources\blocked_plugin.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_CLICK_TO_PLAY_PLUGIN_HTML" file="resources\click_to_play_plugin.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_DISABLED_PLUGIN_HTML" file="resources\disabled_plugin.html" flattenhtml="true" type="BINDATA" /> @@ -29,7 +30,7 @@ without changes to the corresponding grd file. fb9 --> <include name="IDR_WEBSTORE_BINDINGS_JS" file="resources\extensions\webstore.js" type="BINDATA" /> <!-- Custom bindings for extension APIs. --> - <include name="IDR_APP_CUSTOM_BINDINGS_JS" file="resources\extensions\app_custom_bindings.js" type="BINDATA" /> + <include name="IDR_I18N_CUSTOM_BINDINGS_JS" file="resources\extensions\i18n_custom_bindings.js" type="BINDATA" /> <include name="IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS" file="resources\extensions\browser_action_custom_bindings.js" type="BINDATA" /> <include name="IDR_CHROME_PRIVATE_CUSTOM_BINDINGS_JS" file="resources\extensions\chrome_private_custom_bindings.js" type="BINDATA" /> <include name="IDR_CONTENT_SETTINGS_CUSTOM_BINDINGS_JS" file="resources\extensions\content_settings_custom_bindings.js" type="BINDATA" /> @@ -40,7 +41,6 @@ without changes to the corresponding grd file. fb9 --> <include name="IDR_EXTENSION_CUSTOM_BINDINGS_JS" file="resources\extensions\extension_custom_bindings.js" type="BINDATA" /> <include name="IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS" file="resources\extensions\file_browser_handler_custom_bindings.js" type="BINDATA" /> <include name="IDR_FILE_BROWSER_PRIVATE_CUSTOM_BINDINGS_JS" file="resources\extensions\file_browser_private_custom_bindings.js" type="BINDATA" /> - <include name="IDR_I18N_CUSTOM_BINDINGS_JS" file="resources\extensions\i18n_custom_bindings.js" type="BINDATA" /> <include name="IDR_INPUT_IME_CUSTOM_BINDINGS_JS" file="resources\extensions\input.ime_custom_bindings.js" type="BINDATA" /> <include name="IDR_OMNIBOX_CUSTOM_BINDINGS_JS" file="resources\extensions\omnibox_custom_bindings.js" type="BINDATA" /> <include name="IDR_PAGE_ACTIONS_CUSTOM_BINDINGS_JS" file="resources\extensions\page_actions_custom_bindings.js" type="BINDATA" /> diff --git a/chrome/renderer/resource_bundle_source_map.cc b/chrome/renderer/resource_bundle_source_map.cc index 5a42582..2c06b7b 100644 --- a/chrome/renderer/resource_bundle_source_map.cc +++ b/chrome/renderer/resource_bundle_source_map.cc @@ -28,7 +28,7 @@ v8::Handle<v8::Value> ResourceBundleSourceMap::GetSource( } bool ResourceBundleSourceMap::Contains(const std::string& name) { - return !!resource_id_map_.count(name); + return resource_id_map_.count(name) > 0; } v8::Handle<v8::String> ResourceBundleSourceMap::ConvertString( diff --git a/chrome/renderer/resources/extensions/app.js b/chrome/renderer/resources/extensions/app.js new file mode 100644 index 0000000..7337d86 --- /dev/null +++ b/chrome/renderer/resources/extensions/app.js @@ -0,0 +1,41 @@ +// 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. + + var natives = requireNative('app'); + var GetIsInstalled = natives.GetIsInstalled; + var Install = natives.Install; + var GetDetails = natives.GetDetails; + var GetDetailsForFrame = natives.GetDetailsForFrame; + var GetAppNotifyChannel = natives.GetAppNotifyChannel; + + var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); + var callbacks = {}; + var nextCallbackId = 1; + + chrome.app = new function() { + this.__defineGetter__('isInstalled', GetIsInstalled); + this.install = Install; + this.getDetails = GetDetails; + this.getDetailsForFrame = GetDetailsForFrame; + }(); + + chrome.appNotifications = new function() { + this.getChannel = function(clientId, callback) { + var callbackId = 0; + if (callback) { + callbackId = nextCallbackId++; + callbacks[callbackId] = callback; + } + GetAppNotifyChannel(clientId, callbackId); + }; + }(); + + chromeHidden.app = {}; + chromeHidden.app.onGetAppNotifyChannelResponse = + function(channelId, error, callbackId) { + if (callbackId) { + callbacks[callbackId](channelId, error); + delete callbacks[callbackId]; + } + }; diff --git a/chrome/renderer/resources/extensions/app_custom_bindings.js b/chrome/renderer/resources/extensions/app_custom_bindings.js deleted file mode 100644 index 0164f9e..0000000 --- a/chrome/renderer/resources/extensions/app_custom_bindings.js +++ /dev/null @@ -1,56 +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. - -// Custom bindings for the app API. - -var appNatives = requireNative('app'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); - -chromeHidden.registerCustomHook('app', function(bindingsAPI) { - var apiFunctions = bindingsAPI.apiFunctions; - - // Note: everything in chrome.app is synchronous. - apiFunctions.setHandleRequest('getIsInstalled', appNatives.GetIsInstalled); - apiFunctions.setHandleRequest('install', appNatives.Install); - apiFunctions.setHandleRequest('getDetails', appNatives.GetDetails); - apiFunctions.setHandleRequest('getDetailsForFrame', - appNatives.GetDetailsForFrame); - - // Tricky; "getIsInstalled" is actually exposed as the getter "isInstalled", - // but we don't have a way to express this in the schema JSON (nor is it - // worth it for this one special case). - // - // So, define it manually, and let the getIsInstalled function act as its - // documentation. - chrome.app.__defineGetter__('isInstalled', appNatives.GetIsInstalled); - - // Called by app_bindings.cc. - chromeHidden.app = { - onGetAppNotifyChannelResponse: function(channelId, error, callbackId) { - if (callbackId) { - callbacks[callbackId](channelId, error); - delete callbacks[callbackId]; - } - } - }; - - // appNotification stuff. - // - // TODO(kalman): move this stuff to its own custom bindings. - // It will be bit tricky since I'll need to look into why there are - // permissions defined for app notifications, yet this always sets it up? - var callbacks = {}; - var nextCallbackId = 1; - - chrome.appNotifications = new function() { - this.getChannel = function(clientId, callback) { - var callbackId = 0; - if (callback) { - callbackId = nextCallbackId++; - callbacks[callbackId] = callback; - } - appNatives.GetAppNotifyChannel(clientId, callbackId); - }; - }(); -}); diff --git a/chrome/renderer/resources/extensions/setup_bindings.js b/chrome/renderer/resources/extensions/setup_bindings.js index 48a3ac7..86b0cd5 100644 --- a/chrome/renderer/resources/extensions/setup_bindings.js +++ b/chrome/renderer/resources/extensions/setup_bindings.js @@ -2,16 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +var contextInfo = requireNative('context_info'); var sgb = requireNative('schema_generated_bindings'); +require('app'); require('webstore'); -require('json_schema'); -require('event_bindings'); -require('miscellaneous_bindings'); -require('schema_generated_bindings'); -require('apitest'); -// Load the custom bindings for each API. -sgb.GetExtensionAPIDefinition().forEach(function(apiDef) { - require(apiDef.namespace); -}); +if (contextInfo.IsBindingsAllowed()) { + require('miscellaneous_bindings'); + require('schema_generated_bindings'); + require('apitest'); + + // Load the custom bindings for each API. + sgb.GetExtensionAPIDefinition().forEach(function(apiDef) { + if (contextInfo.IsAPIAllowed(apiDef.namespace)) + require(apiDef.namespace); + }); +} diff --git a/chrome/test/data/extensions/api_test/content_scripts/extension_iframe/iframe.js b/chrome/test/data/extensions/api_test/content_scripts/extension_iframe/iframe.js index 572f280..3e37a3a 100644 --- a/chrome/test/data/extensions/api_test/content_scripts/extension_iframe/iframe.js +++ b/chrome/test/data/extensions/api_test/content_scripts/extension_iframe/iframe.js @@ -28,15 +28,11 @@ if (chrome.storage) { success = false; } -// Ditto chrome.tabs, though it's special because it's a dependency of the -// partially unprivileged chrome.extension. -if (chrome.tabs) { - console.log('Error: chrome.tabs exists, it shouldn\'t.'); - success = false; -} - -// Parts of chrome.extension are unavailable. +// Parts of chrome.extension and chrome.tabs (which get included because it's +// a dependency of chrome.extension) are unavailable. if (!runsWithException(function() { return chrome.extension.getViews; })) success = false; +if (!runsWithException(function() { return chrome.tabs.create; })) + success = false; chrome.extension.sendRequest({success: success}); diff --git a/chrome/test/data/extensions/api_test/stubs/content_script.js b/chrome/test/data/extensions/api_test/stubs/content_script.js index 3eacb11..79e491c 100644 --- a/chrome/test/data/extensions/api_test/stubs/content_script.js +++ b/chrome/test/data/extensions/api_test/stubs/content_script.js @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -25,7 +25,7 @@ chrome.extension.sendRequest("getApi", function(apis) { return; module[section].forEach(function(entry) { var path = namespace + "." + entry.name; - if (module.unprivileged || entry.unprivileged) { + if (entry.unprivileged) { unprivilegedPaths.push(path); } else { privilegedPaths.push(path); @@ -36,7 +36,7 @@ chrome.extension.sendRequest("getApi", function(apis) { if (module.properties) { for (var propName in module.properties) { var path = namespace + "." + propName; - if (module.unprivileged || module.properties[propName].unprivileged) { + if (module.properties[propName].unprivileged) { unprivilegedPaths.push(path); } else { privilegedPaths.push(path); @@ -69,7 +69,7 @@ function testPath(path, expectError) { if (typeof(module) == "undefined") return true; } else { - // This is the last component - we expect it to either be undefined or + // This is the last component - we expect it to either be defined or // to throw an error on access. try { if (typeof(module[parts[i]]) == "undefined" && |