diff options
31 files changed, 616 insertions, 522 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 91da264..67a9acb 100644 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc +++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc @@ -104,7 +104,6 @@ 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 @@ -127,13 +126,21 @@ void ChromeRenderViewHostObserver::InitRenderViewForExtensions() { } } - 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())); + 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(); } } diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index b986668..07ae202 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -74,8 +74,6 @@ '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', @@ -130,7 +128,7 @@ 'renderer/resource_bundle_source_map.cc', 'renderer/resource_bundle_source_map.h', 'renderer/resources/extensions/apitest.js', - 'renderer/resources/extensions/app.js', + 'renderer/resources/extensions/app_custom_bindings.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', @@ -150,7 +148,6 @@ 'renderer/resources/extensions/page_actions_custom_bindings.js', 'renderer/resources/extensions/page_capture_custom_bindings.js', 'renderer/resources/extensions/schema_generated_bindings.js', - 'renderer/resources/extensions/setup_bindings.js', 'renderer/resources/extensions/tts_custom_bindings.js', 'renderer/resources/extensions/tts_engine_custom_bindings.js', 'renderer/resources/extensions/types_custom_bindings.js', diff --git a/chrome/common/common_resources.grd b/chrome/common/common_resources.grd index 536fd1b..e898d36 100644 --- a/chrome/common/common_resources.grd +++ b/chrome/common/common_resources.grd @@ -11,6 +11,8 @@ <includes> <include name="IDR_EXTENSION_MANIFEST_FEATURES" file="extensions\api\_manifest_features.json" type="BINDATA" /> <include name="IDR_EXTENSION_PERMISSION_FEATURES" file="extensions\api\_permission_features.json" type="BINDATA" /> + + <include name="IDR_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 new file mode 100644 index 0000000..0e813e6 --- /dev/null +++ b/chrome/common/extensions/api/app.json @@ -0,0 +1,70 @@ +[ + { + "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 f8909b0..a36989d 100644 --- a/chrome/common/extensions/api/extension_api.cc +++ b/chrome/common/extensions/api/extension_api.cc @@ -15,6 +15,7 @@ #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" @@ -22,22 +23,6 @@ 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, @@ -95,6 +80,7 @@ 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, @@ -179,6 +165,27 @@ 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() { @@ -258,59 +265,108 @@ const base::DictionaryValue* ExtensionAPI::GetSchema( return maybe_schema != schemas_.end() ? maybe_schema->second.get() : NULL; } -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); +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: + // Same as PRIVILEGED_CONTEXT, 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::ResolveDependencies(SchemaMap* out) const { +void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) const { std::set<std::string> missing_dependencies; - for (SchemaMap::const_iterator i = out->begin(); i != out->end(); ++i) - GetMissingDependencies(*i->second, *out, &missing_dependencies); + for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i) + GetMissingDependencies(*i, *out, &missing_dependencies); while (missing_dependencies.size()) { - std::string 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); + std::string next = *missing_dependencies.begin(); + missing_dependencies.erase(next); + out->insert(next); + GetMissingDependencies(next, *out, &missing_dependencies); } } -void ExtensionAPI::GetDefaultSchemas(GetSchemasFilter filter, - SchemaMap* out) const { - scoped_refptr<ExtensionPermissionSet> default_permissions( - new ExtensionPermissionSet()); - GetSchemasForPermissions(*default_permissions, filter, out); - ResolveDependencies(out); +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::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::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); } } -bool ExtensionAPI::IsWholeAPIPrivileged(const std::string& api_name) const { - return !completely_unprivileged_apis_.count(api_name) && - !partially_unprivileged_apis_.count(api_name); +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); + } } } // namespace extensions diff --git a/chrome/common/extensions/api/extension_api.h b/chrome/common/extensions/api/extension_api.h index 25ac679..6e2c604 100644 --- a/chrome/common/extensions/api/extension_api.h +++ b/chrome/common/extensions/api/extension_api.h @@ -12,8 +12,11 @@ #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; @@ -21,6 +24,7 @@ class ListValue; class Value; } +class GURL; class Extension; class ExtensionPermissionSet; @@ -29,18 +33,6 @@ 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(); @@ -50,26 +42,17 @@ 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 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; + // 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; private: friend struct DefaultSingletonTraits<ExtensionAPI>; @@ -93,18 +76,34 @@ class ExtensionAPI { const std::string& child_kind, const std::string& child_name) 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 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; // Adds dependent schemas to |out| as determined by the "dependencies" // property. - void ResolveDependencies(SchemaMap* out) const; + 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; static ExtensionAPI* instance_; // Schemas for each namespace. + typedef std::map<std::string, linked_ptr<const DictionaryValue> > SchemaMap; SchemaMap schemas_; // APIs that are entirely unprivileged. @@ -113,6 +112,9 @@ 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 70982f7..3f4a0af 100644 --- a/chrome/common/extensions/api/extension_api_unittest.cc +++ b/chrome/common/extensions/api/extension_api_unittest.cc @@ -13,7 +13,10 @@ #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(); @@ -33,6 +36,8 @@ 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")); @@ -40,34 +45,18 @@ TEST(ExtensionAPI, IsPrivileged) { EXPECT_FALSE(extension_api->IsPrivileged("storage.set")); } -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. +scoped_refptr<Extension> CreateExtensionWithPermissions( + const std::set<std::string>& permissions) { DictionaryValue manifest; - manifest.SetString("name", "test extension"); + manifest.SetString("name", "extension"); manifest.SetString("version", "1.0"); { - scoped_ptr<ListValue> permissions(new ListValue()); - permissions->Append(Value::CreateStringValue("ttsEngine")); - manifest.Set("permissions", permissions.release()); + 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()); } std::string error; @@ -76,8 +65,105 @@ TEST(ExtensionAPI, Depends) { CHECK(extension.get()); CHECK(error.empty()); - ExtensionAPI::SchemaMap schemas; - ExtensionAPI::GetInstance()->GetSchemasForExtension( - *extension, ExtensionAPI::ALL, &schemas); - EXPECT_EQ(1u, schemas.count("tts")); + 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")); } + +} // namespace diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h index 276020e..7fcd56f 100644 --- a/chrome/common/extensions/extension_messages.h +++ b/chrome/common/extensions/extension_messages.h @@ -193,10 +193,6 @@ 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 60ed1fa..8dcbab2 100644 --- a/chrome/common/extensions/extension_permission_set.cc +++ b/chrome/common/extensions/extension_permission_set.cc @@ -42,6 +42,7 @@ 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", @@ -565,6 +566,16 @@ 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 35418a9..cec4078 100644 --- a/chrome/common/extensions/extension_permission_set.h +++ b/chrome/common/extensions/extension_permission_set.h @@ -318,6 +318,12 @@ 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 79e8338..d511c19 100644 --- a/chrome/common/extensions/feature.cc +++ b/chrome/common/extensions/feature.cc @@ -24,6 +24,7 @@ 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 61a96a5..749f4b3 100644 --- a/chrome/common/extensions/feature.h +++ b/chrome/common/extensions/feature.h @@ -22,11 +22,21 @@ namespace extensions { class Feature { public: // The JavaScript contexts the feature is supported in. + // TODO(kalman): s/PRIVILEGED/BLESSED_EXTENSION/ enum Context { UNSPECIFIED_CONTEXT, - PRIVILEGED_CONTEXT, // A context in a privileged extension process. - UNPRIVILEGED_CONTEXT, // A context in a normal, unprivileged renderer. - CONTENT_SCRIPT_CONTEXT // A context from a content script. + + // 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, }; // 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 8147ca7..432e4ac 100644 --- a/chrome/common/extensions/feature_unittest.cc +++ b/chrome/common/extensions/feature_unittest.cc @@ -236,12 +236,14 @@ 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(3u, feature->contexts()->size()); + EXPECT_EQ(4u, 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 34c8047..a5a902a 100644 --- a/chrome/renderer/extensions/app_bindings.cc +++ b/chrome/renderer/extensions/app_bindings.cc @@ -55,7 +55,6 @@ const char* kInvalidCallbackIdError = "Invalid callbackId"; } // namespace - AppBindings::AppBindings(ExtensionDispatcher* dispatcher, ChromeV8Context* context) : ChromeV8Extension(dispatcher), @@ -72,7 +71,6 @@ 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() @@ -81,9 +79,8 @@ 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_->IsApplicationActive(extension->id()); + extension_dispatcher_->IsExtensionActive(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 7db10ed..6d59a19 100644 --- a/chrome/renderer/extensions/chrome_v8_context.cc +++ b/chrome/renderer/extensions/chrome_v8_context.cc @@ -15,6 +15,8 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "v8/include/v8.h" +using extensions::Feature; + namespace { const char kChromeHidden[] = "chromeHidden"; @@ -24,11 +26,13 @@ const char kValidateCallbacks[] = "validateCallbacks"; const char kValidateAPI[] = "validateAPI"; #endif -std::string GetContextTypeDescription( - ChromeV8Context::ContextType context_type) { +std::string GetContextTypeDescription(Feature::Context context_type) { switch (context_type) { - case ChromeV8Context::OTHER: return "other"; - case ChromeV8Context::CONTENT_SCRIPT: return "content script"; + 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"; } NOTREACHED(); return ""; @@ -39,7 +43,7 @@ std::string GetContextTypeDescription( ChromeV8Context::ChromeV8Context(v8::Handle<v8::Context> v8_context, WebKit::WebFrame* web_frame, const std::string& extension_id, - ChromeV8Context::ContextType context_type) + Feature::Context 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 3faf9d2..e2c8911 100644 --- a/chrome/renderer/extensions/chrome_v8_context.h +++ b/chrome/renderer/extensions/chrome_v8_context.h @@ -9,6 +9,7 @@ #include <string> #include "base/basictypes.h" +#include "chrome/common/extensions/feature.h" #include "chrome/renderer/module_system.h" #include "v8/include/v8.h" @@ -27,19 +28,10 @@ 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, - ContextType context_type); + extensions::Feature::Context context_type); ~ChromeV8Context(); v8::Handle<v8::Context> v8_context() const { @@ -57,7 +49,7 @@ class ChromeV8Context { web_frame_ = NULL; } - ContextType context_type() const { + extensions::Feature::Context context_type() const { return context_type_; } @@ -116,7 +108,7 @@ class ChromeV8Context { std::string extension_id_; // The type of context. - ContextType context_type_; + extensions::Feature::Context 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 56f4697..af7b17d 100644 --- a/chrome/renderer/extensions/chrome_v8_context_set_unittest.cc +++ b/chrome/renderer/extensions/chrome_v8_context_set_unittest.cc @@ -3,6 +3,7 @@ // 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" @@ -21,9 +22,11 @@ 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, ChromeV8Context::OTHER); + ChromeV8Context* context = new ChromeV8Context( + v8_context, + frame, + extension_id, + extensions::Feature::PRIVILEGED_CONTEXT); 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 deleted file mode 100644 index 5de0470b..0000000 --- a/chrome/renderer/extensions/custom_bindings_util.cc +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/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 deleted file mode 100644 index 73d026d..0000000 --- a/chrome/renderer/extensions/custom_bindings_util.h +++ /dev/null @@ -1,44 +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. - -#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 c4895cd..afb45e0 100644 --- a/chrome/renderer/extensions/extension_custom_bindings.cc +++ b/chrome/renderer/extensions/extension_custom_bindings.cc @@ -22,9 +22,6 @@ #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 4d187bb2..f3d44df 100644 --- a/chrome/renderer/extensions/extension_dispatcher.cc +++ b/chrome/renderer/extensions/extension_dispatcher.cc @@ -10,6 +10,7 @@ #include "base/string_piece.h" #include "chrome/common/child_process_logging.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/api/extension_api.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/extension_permission_set.h" @@ -18,12 +19,23 @@ #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/custom_bindings_util.h" +#include "chrome/renderer/extensions/chrome_private_custom_bindings.h" +#include "chrome/renderer/extensions/context_menus_custom_bindings.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" @@ -33,32 +45,21 @@ #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" - using content::RenderThread; using extensions::ChromePrivateCustomBindings; using extensions::ContextMenusCustomBindings; using extensions::ExperimentalSocketCustomBindings; +using extensions::ExtensionAPI; using extensions::ExtensionCustomBindings; +using extensions::Feature; using extensions::FileBrowserHandlerCustomBindings; using extensions::FileBrowserPrivateCustomBindings; using extensions::I18NCustomBindings; @@ -78,19 +79,11 @@ using WebKit::WebScopedUserGesture; using WebKit::WebVector; using WebKit::WebView; -namespace util = extensions::custom_bindings_util; - 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() { @@ -125,41 +118,6 @@ 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_; -}; - } ExtensionDispatcher::ExtensionDispatcher() @@ -197,7 +155,6 @@ 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) @@ -347,12 +304,6 @@ 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) != @@ -387,34 +338,8 @@ 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 (IsTestExtensionId(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 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", @@ -425,6 +350,8 @@ 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))); @@ -455,7 +382,6 @@ 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", @@ -464,9 +390,9 @@ void ExtensionDispatcher::PopulateSourceMap() { IDR_SCHEMA_GENERATED_BINDINGS_JS); source_map_.RegisterSource("json_schema", IDR_JSON_SCHEMA_JS); source_map_.RegisterSource("apitest", IDR_EXTENSION_APITEST_JS); - source_map_.RegisterSource("setup_bindings", IDR_SETUP_BINDINGS_JS); - // Custom bindings. + // Custom bindings. + source_map_.RegisterSource("app", IDR_APP_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("browserAction", IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("chromePrivate", @@ -509,36 +435,37 @@ void ExtensionDispatcher::DidCreateScriptContext( extension_group = g_hack_extension_group; std::string extension_id = GetExtensionID(frame, world_id); + + const Extension* extension = extensions_.GetByID(extension_id); + if (!extension && !extension_id.empty() && !IsTestExtensionId(extension_id)) { + // There are conditions where despite a context being associated with an + // extension, no extension actually gets found. Ignore "invalid" because + // CSP blocks extension page loading by switching the extension ID to + // "invalid". This isn't interesting. + if (extension_id != "invalid") { + LOG(ERROR) << "Extension \"" << extension_id << "\" not found"; + RenderThread::Get()->RecordUserMetrics("ExtensionNotFound_ED"); + } + } + + 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, - ExtensionGroupToContextType(extension_group)); + new ChromeV8Context(v8_context, frame, extension_id, context_type); 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; if (extension) manifest_version = extension->manifest_version(); @@ -551,11 +478,46 @@ void ExtensionDispatcher::DidCreateScriptContext( if (global->Get(chrome_string)->IsUndefined()) global->Set(chrome_string, v8::Object::New()); } - module_system->Require("app"); - module_system->Require("webstore"); - if (is_bindings_allowed) { - module_system->Require("setup_bindings"); + + // Loading JavaScript is expensive, so only run the full API bindings + // generation mechanisms in extension pages (NOT all web pages). + switch (context_type) { + case Feature::UNSPECIFIED_CONTEXT: + case Feature::WEB_PAGE_CONTEXT: + break; + + case Feature::PRIVILEGED_CONTEXT: + case Feature::UNPRIVILEGED_CONTEXT: + case Feature::CONTENT_SCRIPT_CONTEXT: + module_system->Require("json_schema"); + module_system->Require("event_bindings"); + module_system->Require("miscellaneous_bindings"); + module_system->Require("schema_generated_bindings"); + module_system->Require("apitest"); + break; + } + + scoped_ptr<std::set<std::string> > apis = + ExtensionAPI::GetInstance()->GetAPIsForContext( + context_type, extension, url_info.url()); + + // TODO(kalman): include this in the APIs returned from GetAPIsForContext. + apis->insert("webstore"); + + // TODO(kalman): this is probably the most unfortunate thing I've ever had + // to write. We really need to factor things differently to delete the + // concept of a test extension ID. + if (IsTestExtensionId(extension_id)) { + module_system->Require("miscellaneous_bindings"); + module_system->Require("schema_generated_bindings"); + apis->insert("extension"); } + + for (std::set<std::string>::iterator i = apis->begin(); i != apis->end(); + ++i) { + module_system->Require(*i); + } + module_system->set_natives_enabled(false); context->set_module_system(module_system.Pass()); @@ -595,6 +557,7 @@ void ExtensionDispatcher::WillReleaseScriptContext( } void ExtensionDispatcher::SetTestExtensionId(const std::string& id) { + CHECK(!id.empty()); test_extension_id_ = id; } @@ -602,11 +565,6 @@ 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); @@ -734,3 +692,22 @@ 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 b1dfb40..3a0d2c1 100644 --- a/chrome/renderer/extensions/extension_dispatcher.h +++ b/chrome/renderer/extensions/extension_dispatcher.h @@ -14,6 +14,8 @@ #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" @@ -54,13 +56,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; - // 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); + // 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); // See WebKit::WebPermissionClient::allowScriptExtension // TODO(koz): Remove once WebKit no longer calls this. @@ -118,7 +120,6 @@ 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, @@ -151,9 +152,11 @@ class ExtensionDispatcher : public content::RenderProcessObserver { // Inserts static source code into |source_map_|. void PopulateSourceMap(); - // 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); + // 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); // True if this renderer is running extensions. bool is_extension_process_; @@ -179,12 +182,9 @@ class ExtensionDispatcher : public content::RenderProcessObserver { // All declared function names. std::set<std::string> function_names_; - // The extensions that are active in this process. + // The extensions and apps 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 eda15d8..257f3f9 100644 --- a/chrome/renderer/extensions/schema_generated_bindings.cc +++ b/chrome/renderer/extensions/schema_generated_bindings.cc @@ -31,6 +31,7 @@ #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/miscellaneous_bindings.h" #include "chrome/renderer/extensions/user_script_slave.h" +#include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/v8_value_converter.h" #include "grit/common_resources.h" @@ -44,8 +45,10 @@ #include "v8/include/v8.h" #include "webkit/glue/webkit_glue.h" +using content::RenderThread; using content::V8ValueConverter; using extensions::ExtensionAPI; +using extensions::Feature; using WebKit::WebFrame; using WebKit::WebSecurityOrigin; @@ -118,30 +121,37 @@ class ExtensionImpl : public ChromeV8Extension { ChromeV8Context* v8_context = dispatcher->v8_context_set().GetCurrent(); CHECK(v8_context); - std::string extension_id = v8_context->extension_id(); - ExtensionAPI::SchemaMap schemas; - ExtensionAPI::GetSchemasFilter filter = - dispatcher->is_extension_process() ? - ExtensionAPI::ALL : ExtensionAPI::ONLY_UNPRIVILEGED; + // TODO(kalman): This is being calculated twice, first in + // ExtensionDispatcher then again here. It might as well be a property of + // ChromeV8Context, however, this would require making ChromeV8Context take + // an Extension rather than an extension ID. In itself this is fine, + // however it does not play correctly with the "IsTestExtensionId" checks. + // We need to remove that first. + scoped_ptr<std::set<std::string> > apis; + const std::string& extension_id = v8_context->extension_id(); if (dispatcher->IsTestExtensionId(extension_id)) { - ExtensionAPI::GetInstance()->GetDefaultSchemas(filter, &schemas); + apis.reset(new std::set<std::string>()); + // The minimal set of APIs that tests need. + apis->insert("extension"); } else { - const ::Extension* extension = - dispatcher->extensions()->GetByID(extension_id); - CHECK(extension) << extension_id << " not found"; - ExtensionAPI::GetInstance()->GetSchemasForExtension( - *extension, filter, &schemas); + apis = ExtensionAPI::GetInstance()->GetAPIsForContext( + v8_context->context_type(), + dispatcher->extensions()->GetByID(extension_id), + UserScriptSlave::GetDataSourceURLForFrame(v8_context->web_frame())); } v8::Persistent<v8::Context> context(v8::Context::New()); v8::Context::Scope context_scope(context); - v8::Handle<v8::Array> api(v8::Array::New(schemas.size())); + v8::Handle<v8::Array> api(v8::Array::New(apis->size())); size_t api_index = 0; - 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)); + 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)); ++api_index; } @@ -323,6 +333,7 @@ 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 a0df32c..32c9b9f 100644 --- a/chrome/renderer/renderer_resources.grd +++ b/chrome/renderer/renderer_resources.grd @@ -11,7 +11,6 @@ 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" /> @@ -25,11 +24,10 @@ without changes to the corresponding grd file. fb9 --> <include name="IDR_PLATFORM_APP_CSS" file="resources\extensions\platform_app.css" type="BINDATA" /> <include name="IDR_SAD_PLUGIN" file="resources\sadplugin.png" type="BINDATA" /> <include name="IDR_SCHEMA_GENERATED_BINDINGS_JS" file="resources\extensions\schema_generated_bindings.js" type="BINDATA" /> - <include name="IDR_SETUP_BINDINGS_JS" file="resources\extensions\setup_bindings.js" type="BINDATA" /> <include name="IDR_WEBSTORE_BINDINGS_JS" file="resources\extensions\webstore.js" type="BINDATA" /> <!-- Custom bindings for extension APIs. --> - <include name="IDR_I18N_CUSTOM_BINDINGS_JS" file="resources\extensions\i18n_custom_bindings.js" type="BINDATA" /> + <include name="IDR_APP_CUSTOM_BINDINGS_JS" file="resources\extensions\app_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,6 +38,7 @@ 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 2c06b7b..5a42582 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) > 0; + return !!resource_id_map_.count(name); } v8::Handle<v8::String> ResourceBundleSourceMap::ConvertString( diff --git a/chrome/renderer/resources/extensions/app.js b/chrome/renderer/resources/extensions/app.js deleted file mode 100644 index 7337d86..0000000 --- a/chrome/renderer/resources/extensions/app.js +++ /dev/null @@ -1,41 +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. - - 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 new file mode 100644 index 0000000..9cae9d4 --- /dev/null +++ b/chrome/renderer/resources/extensions/app_custom_bindings.js @@ -0,0 +1,52 @@ +// 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(); + +chrome.app = { + getIsInstalled: appNatives.GetIsInstalled, + install: appNatives.Install, + getDetails: appNatives.GetDetails, + 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 = { + getChannel: function getChannel(clientId, callback) { + var callbackId = 0; + if (callback) { + callbackId = nextCallbackId++; + callbacks[callbackId] = callback; + } + appNatives.GetAppNotifyChannel(clientId, callbackId); + } +}; diff --git a/chrome/renderer/resources/extensions/schema_generated_bindings.js b/chrome/renderer/resources/extensions/schema_generated_bindings.js index ab11923..217414c 100644 --- a/chrome/renderer/resources/extensions/schema_generated_bindings.js +++ b/chrome/renderer/resources/extensions/schema_generated_bindings.js @@ -531,6 +531,11 @@ var platform = getPlatform(); apiDefinitions.forEach(function(apiDef) { + // TODO(kalman): Remove this, or refactor schema_generated_bindings.js so + // that it isn't necessary. For now, chrome.app is entirely handwritten. + if (apiDef.namespace === 'app') + return; + if (!isSchemaNodeSupported(apiDef, platform, manifestVersion)) return; diff --git a/chrome/renderer/resources/extensions/setup_bindings.js b/chrome/renderer/resources/extensions/setup_bindings.js deleted file mode 100644 index ba002d9..0000000 --- a/chrome/renderer/resources/extensions/setup_bindings.js +++ /dev/null @@ -1,16 +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. - -var contextInfo = requireNative('context_info'); -var sgb = requireNative('schema_generated_bindings'); - -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 3e37a3a..572f280 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,11 +28,15 @@ if (chrome.storage) { success = false; } -// 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; })) +// 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; -if (!runsWithException(function() { return chrome.tabs.create; })) +} + +// Parts of chrome.extension are unavailable. +if (!runsWithException(function() { return chrome.extension.getViews; })) 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 79e491c..3eacb11 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) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -25,7 +25,7 @@ chrome.extension.sendRequest("getApi", function(apis) { return; module[section].forEach(function(entry) { var path = namespace + "." + entry.name; - if (entry.unprivileged) { + if (module.unprivileged || 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.properties[propName].unprivileged) { + if (module.unprivileged || 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 defined or + // This is the last component - we expect it to either be undefined or // to throw an error on access. try { if (typeof(module[parts[i]]) == "undefined" && |