Commit d07e6be7 authored by cduvall@chromium.org's avatar cduvall@chromium.org

Prevent chrome.app JSON schema from loading on every page

The app API along with app.window and app.runtime have been converted to use
the feature system. Bindings are not added to the chrome object for unavailable
APIs that are children of available APIs. For example, if chrome.app is
available, we will not add lazy bindings to chrome for app.window and
app.runtime. This eliminates the need to load the app schema, because we no
longer need to get chrome.app to add the app.runtime and app.window bindings.

BUG=55316

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=194837

Review URL: https://chromiumcodereview.appspot.com/13604005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195143 0039d316-1c4b-4281-b951-d872f2087c98
parent cdf85aef
...@@ -3,6 +3,26 @@ ...@@ -3,6 +3,26 @@
// found in the LICENSE file. // found in the LICENSE file.
{ {
"app": {
"channel": "stable",
"extension_types": ["hosted_app", "extension"],
"contexts": [
"blessed_extension", "unblessed_extension", "content_script", "web_page"
],
"matches": [
"http://*/*", "https://*/*", "chrome-extension://*/*", "file://*/*"
]
},
"app.runtime": {
"channel": "stable",
"contexts": ["blessed_extension"],
"dependencies": ["permission:app.runtime"]
},
"app.window": {
"channel": "stable",
"contexts": ["blessed_extension"],
"dependencies": ["permission:app.window"]
},
"app.currentWindowInternal": { "app.currentWindowInternal": {
"internal": true, "internal": true,
"channel": "stable", "channel": "stable",
...@@ -21,7 +41,9 @@ ...@@ -21,7 +41,9 @@
"events": { "events": {
"internal": true, "internal": true,
"channel": "stable", "channel": "stable",
"contexts": ["blessed_extension", "unblessed_extension", "content_script", "web_page"], "contexts": [
"blessed_extension", "unblessed_extension", "content_script", "web_page"
],
"matches": ["<all_urls>"] "matches": ["<all_urls>"]
}, },
"fileBrowserHandlerInternal": { "fileBrowserHandlerInternal": {
...@@ -40,6 +62,6 @@ ...@@ -40,6 +62,6 @@
"extension_types": ["hosted_app"], "extension_types": ["hosted_app"],
"contexts": ["blessed_extension", "web_page"], "contexts": ["blessed_extension", "web_page"],
// Any webpage can use the webstore API. // Any webpage can use the webstore API.
"matches": [ "http://*/*", "https://*/*" ] "matches": ["http://*/*", "https://*/*"]
} }
} }
...@@ -6,8 +6,6 @@ ...@@ -6,8 +6,6 @@
{ {
"namespace": "app", "namespace": "app",
"nodoc": true, "nodoc": true,
"unprivileged": true,
"matches": [ "<all_urls>" ],
"types": [ "types": [
{ {
"id": "Details", "id": "Details",
......
...@@ -172,10 +172,14 @@ TEST(ExtensionAPI, APIFeatures) { ...@@ -172,10 +172,14 @@ TEST(ExtensionAPI, APIFeatures) {
{ "test2.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, { "test2.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() },
{ "test3", false, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") }, { "test3", false, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") },
{ "test3.foo", true, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") }, { "test3.foo", true, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") },
{ "test3.foo", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, { "test3.foo", true, Feature::BLESSED_EXTENSION_CONTEXT,
{ "test4", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, GURL("http://bad.com") },
{ "test4.foo", false, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, { "test4", true, Feature::BLESSED_EXTENSION_CONTEXT,
{ "test4.foo", false, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, GURL("http://bad.com") },
{ "test4.foo", false, Feature::BLESSED_EXTENSION_CONTEXT,
GURL("http://bad.com") },
{ "test4.foo", false, Feature::UNBLESSED_EXTENSION_CONTEXT,
GURL("http://bad.com") },
{ "test4.foo.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, { "test4.foo.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() },
{ "test5", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, { "test5", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") },
{ "test5", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, { "test5", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") },
...@@ -380,11 +384,12 @@ TEST_F(ExtensionAPITest, URLMatching) { ...@@ -380,11 +384,12 @@ TEST_F(ExtensionAPITest, URLMatching) {
EXPECT_TRUE(MatchesURL(api.get(), "app", "https://blah.net")); EXPECT_TRUE(MatchesURL(api.get(), "app", "https://blah.net"));
EXPECT_TRUE(MatchesURL(api.get(), "app", "file://somefile.html")); EXPECT_TRUE(MatchesURL(api.get(), "app", "file://somefile.html"));
// But not internal URLs (for chrome-extension:// the app API is injected by // But not internal URLs.
// GetSchemasForExtension).
EXPECT_FALSE(MatchesURL(api.get(), "app", "about:flags")); EXPECT_FALSE(MatchesURL(api.get(), "app", "about:flags"));
EXPECT_FALSE(MatchesURL(api.get(), "app", "chrome://flags")); EXPECT_FALSE(MatchesURL(api.get(), "app", "chrome://flags"));
EXPECT_FALSE(MatchesURL(api.get(), "app",
// "app" should be available to chrome-extension URLs.
EXPECT_TRUE(MatchesURL(api.get(), "app",
"chrome-extension://fakeextension")); "chrome-extension://fakeextension"));
// "storage" API (for example) isn't available to any URLs. // "storage" API (for example) isn't available to any URLs.
......
...@@ -154,9 +154,20 @@ bool ChromeV8Context::CallChromeHiddenMethod( ...@@ -154,9 +154,20 @@ bool ChromeV8Context::CallChromeHiddenMethod(
Feature::Availability ChromeV8Context::GetAvailability( Feature::Availability ChromeV8Context::GetAvailability(
const std::string& api_name) { const std::string& api_name) {
return GetAvailabilityInternal(api_name, extension_);
}
Feature::Availability ChromeV8Context::GetAvailabilityForContext(
const std::string& api_name) {
return GetAvailabilityInternal(api_name, NULL);
}
Feature::Availability ChromeV8Context::GetAvailabilityInternal(
const std::string& api_name,
const Extension* extension) {
return ExtensionAPI::GetSharedInstance()->IsAvailable( return ExtensionAPI::GetSharedInstance()->IsAvailable(
api_name, api_name,
extension_, extension,
context_type_, context_type_,
UserScriptSlave::GetDataSourceURLForFrame(web_frame_)); UserScriptSlave::GetDataSourceURLForFrame(web_frame_));
} }
......
...@@ -103,6 +103,10 @@ class ChromeV8Context : public RequestSender::Source { ...@@ -103,6 +103,10 @@ class ChromeV8Context : public RequestSender::Source {
// Returns the availability of the API |api_name|. // Returns the availability of the API |api_name|.
Feature::Availability GetAvailability(const std::string& api_name); Feature::Availability GetAvailability(const std::string& api_name);
// Returns the availability of the API |api_name| without taking into account
// the context's extension.
Feature::Availability GetAvailabilityForContext(const std::string& api_name);
// Returns a string description of the type of context this is. // Returns a string description of the type of context this is.
std::string GetContextTypeDescription(); std::string GetContextTypeDescription();
...@@ -115,6 +119,9 @@ class ChromeV8Context : public RequestSender::Source { ...@@ -115,6 +119,9 @@ class ChromeV8Context : public RequestSender::Source {
const std::string& error) OVERRIDE; const std::string& error) OVERRIDE;
private: private:
Feature::Availability GetAvailabilityInternal(const std::string& api_name,
const Extension* extension);
// The v8 context the bindings are accessible to. // The v8 context the bindings are accessible to.
ScopedPersistent<v8::Context> v8_context_; ScopedPersistent<v8::Context> v8_context_;
......
...@@ -654,16 +654,15 @@ v8::Handle<v8::Object> Dispatcher::GetOrCreateObject( ...@@ -654,16 +654,15 @@ v8::Handle<v8::Object> Dispatcher::GetOrCreateObject(
const std::string& field) { const std::string& field) {
v8::HandleScope handle_scope; v8::HandleScope handle_scope;
v8::Handle<v8::String> key = v8::String::New(field.c_str()); v8::Handle<v8::String> key = v8::String::New(field.c_str());
// This little dance is for APIs that may be unavailable but have available // If the object has a callback property, it is assumed it is an unavailable
// children. For example, chrome.app can be unavailable, while // API, so it is safe to delete. This is checked before GetOrCreateObject is
// chrome.app.runtime is available. The lazy getter for chrome.app must be // called.
// deleted, so that there isn't an error when accessing chrome.app.runtime. if (object->HasRealNamedCallbackProperty(key)) {
if (object->Has(key)) { object->Delete(key);
} else if (object->HasRealNamedProperty(key)) {
v8::Handle<v8::Value> value = object->Get(key); v8::Handle<v8::Value> value = object->Get(key);
if (value->IsObject()) CHECK(value->IsObject());
return handle_scope.Close(v8::Handle<v8::Object>::Cast(value)); return handle_scope.Close(v8::Handle<v8::Object>::Cast(value));
else
object->Delete(key);
} }
v8::Handle<v8::Object> new_object = v8::Object::New(); v8::Handle<v8::Object> new_object = v8::Object::New();
...@@ -673,13 +672,14 @@ v8::Handle<v8::Object> Dispatcher::GetOrCreateObject( ...@@ -673,13 +672,14 @@ v8::Handle<v8::Object> Dispatcher::GetOrCreateObject(
void Dispatcher::RegisterSchemaGeneratedBindings( void Dispatcher::RegisterSchemaGeneratedBindings(
ModuleSystem* module_system, ModuleSystem* module_system,
ChromeV8Context* context, ChromeV8Context* context) {
v8::Handle<v8::Context> v8_context) {
std::set<std::string> apis = std::set<std::string> apis =
ExtensionAPI::GetSharedInstance()->GetAllAPINames(); ExtensionAPI::GetSharedInstance()->GetAllAPINames();
for (std::set<std::string>::iterator it = apis.begin(); for (std::set<std::string>::iterator it = apis.begin();
it != apis.end(); ++it) { it != apis.end(); ++it) {
const std::string& api_name = *it; const std::string& api_name = *it;
if (!context->GetAvailabilityForContext(api_name).is_available())
continue;
Feature* feature = Feature* feature =
BaseFeatureProvider::GetByName("api")->GetFeature(api_name); BaseFeatureProvider::GetByName("api")->GetFeature(api_name);
...@@ -689,12 +689,36 @@ void Dispatcher::RegisterSchemaGeneratedBindings( ...@@ -689,12 +689,36 @@ void Dispatcher::RegisterSchemaGeneratedBindings(
std::vector<std::string> split; std::vector<std::string> split;
base::SplitString(api_name, '.', &split); base::SplitString(api_name, '.', &split);
v8::Handle<v8::Object> bind_object = GetOrCreateChrome(v8_context); v8::Handle<v8::Object> bind_object =
for (size_t i = 0; i < split.size() - 1; ++i) GetOrCreateChrome(context->v8_context());
// Check if this API has an ancestor. If the API's ancestor is available and
// the API is not available, don't install the bindings for this API. If
// the API is available and its ancestor is not, delete the ancestor and
// install the bindings for the API. This is to prevent loading the ancestor
// API schema if it will not be needed.
//
// For example:
// If app is available and app.window is not, just install app.
// If app.window is available and app is not, delete app and install
// app.window on a new object so app does not have to be loaded.
std::string ancestor_name;
bool only_ancestor_available = false;
for (size_t i = 0; i < split.size() - 1; ++i) {
ancestor_name += (i ? ".": "") + split[i];
if (!ancestor_name.empty() &&
context->GetAvailability(ancestor_name).is_available() &&
!context->GetAvailability(api_name).is_available()) {
only_ancestor_available = true;
break;
}
bind_object = GetOrCreateObject(bind_object, split[i]); bind_object = GetOrCreateObject(bind_object, split[i]);
}
if (only_ancestor_available)
continue;
if (lazy_bindings_map_.find(api_name) != lazy_bindings_map_.end()) { if (lazy_bindings_map_.find(api_name) != lazy_bindings_map_.end()) {
InstallBindings(module_system, v8_context, api_name); InstallBindings(module_system, context->v8_context(), api_name);
} else if (!source_map_.Contains(api_name)) { } else if (!source_map_.Contains(api_name)) {
module_system->RegisterNativeHandler( module_system->RegisterNativeHandler(
api_name, api_name,
...@@ -983,31 +1007,14 @@ void Dispatcher::DidCreateScriptContext( ...@@ -983,31 +1007,14 @@ void Dispatcher::DidCreateScriptContext(
GetOrCreateChrome(v8_context); GetOrCreateChrome(v8_context);
// Loading JavaScript is expensive, so only run the full API bindings // TODO(kalman): see comment below about ExtensionAPI.
// generation mechanisms in extension pages (NOT all web pages). InstallBindings(module_system.get(), v8_context, "app");
switch (context_type) { InstallBindings(module_system.get(), v8_context, "webstore");
case Feature::UNSPECIFIED_CONTEXT: if (extension && !extension->is_platform_app())
case Feature::WEB_PAGE_CONTEXT: module_system->Require("miscellaneous_bindings");
// TODO(kalman): see comment below about ExtensionAPI. module_system->Require("json"); // see paranoid comment in json.js
InstallBindings(module_system.get(), v8_context, "app");
InstallBindings(module_system.get(), v8_context, "webstore"); RegisterSchemaGeneratedBindings(module_system.get(), context);
break;
case Feature::BLESSED_EXTENSION_CONTEXT:
case Feature::UNBLESSED_EXTENSION_CONTEXT:
case Feature::CONTENT_SCRIPT_CONTEXT:
if (extension && !extension->is_platform_app())
module_system->Require("miscellaneous_bindings");
module_system->Require("json"); // see paranoid comment in json.js
// TODO(kalman): move this code back out of the switch and execute it
// regardless of |context_type|. ExtensionAPI knows how to return the
// correct APIs, however, until it doesn't have a 2MB overhead we can't
// load it in every process.
RegisterSchemaGeneratedBindings(module_system.get(),
context,
v8_context);
break;
}
bool is_within_platform_app = IsWithinPlatformApp(frame); bool is_within_platform_app = IsWithinPlatformApp(frame);
// Inject custom JS into the platform app context. // Inject custom JS into the platform app context.
......
...@@ -193,8 +193,7 @@ class Dispatcher : public content::RenderProcessObserver { ...@@ -193,8 +193,7 @@ class Dispatcher : public content::RenderProcessObserver {
void RegisterNativeHandlers(ModuleSystem* module_system, void RegisterNativeHandlers(ModuleSystem* module_system,
ChromeV8Context* context); ChromeV8Context* context);
void RegisterSchemaGeneratedBindings(ModuleSystem* module_system, void RegisterSchemaGeneratedBindings(ModuleSystem* module_system,
ChromeV8Context* context, ChromeV8Context* context);
v8::Handle<v8::Context> v8_context);
// Inserts static source code into |source_map_|. // Inserts static source code into |source_map_|.
void PopulateSourceMap(); void PopulateSourceMap();
......
...@@ -31,7 +31,11 @@ chrome.extension.sendRequest("getApi", function(apis) { ...@@ -31,7 +31,11 @@ chrome.extension.sendRequest("getApi", function(apis) {
} }
var path = namespace + "." + entry.name; var path = namespace + "." + entry.name;
if (module.unprivileged || entry.unprivileged) { // TODO(cduvall): Make this inspect _api_features.json.
// http://crbug.com/232247
// Manually add chrome.app to the unprivileged APIs since it uses the
// feature system now.
if (module.unprivileged || entry.unprivileged || namespace == 'app') {
unprivilegedPaths.push(path); unprivilegedPaths.push(path);
} else { } else {
privilegedPaths.push(path); privilegedPaths.push(path);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment