Commit 377a7a96 authored by Devlin Cronin's avatar Devlin Cronin Committed by Commit Bot

[Extensions Bindings] Update feature initialization

Make two changes to how we initialize features for contexts in extension
native bindings:
- Treat runtime specially. This is necessary because certain runtime
  methods should be exposed only if there exists an extension that can
  connect to the given context (e.g., an extension that cooperates with
  a given website).  Simply checking the fature availability is
  insufficient. Update unittests to match.
- Treat web contexts specially.  Web contexts only ever have a handful
  (3) APIs that are potentially available to them, and are by far the
  most commonly created context. As such, add a fast path for them by
  only checking those possible features. (This matches logic in the
  JS bindings system).

Bug: 653596
Change-Id: I57f50b6f762f578679771c5e07a1b3ce31da3e0c
Reviewed-on: https://chromium-review.googlesource.com/571962Reviewed-by: default avatarIstiaque Ahmed <lazyboy@chromium.org>
Commit-Queue: Devlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#486965}
parent 640aa398
...@@ -90,6 +90,7 @@ source_set("renderer") { ...@@ -90,6 +90,7 @@ source_set("renderer") {
"dom_activity_logger.h", "dom_activity_logger.h",
"event_bindings.cc", "event_bindings.cc",
"event_bindings.h", "event_bindings.h",
"extension_bindings_system.cc",
"extension_bindings_system.h", "extension_bindings_system.h",
"extension_frame_helper.cc", "extension_frame_helper.cc",
"extension_frame_helper.h", "extension_frame_helper.h",
......
// Copyright 2017 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 "extensions/renderer/extension_bindings_system.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/externally_connectable.h"
#include "extensions/renderer/renderer_extension_registry.h"
#include "extensions/renderer/script_context.h"
namespace extensions {
// static
bool ExtensionBindingsSystem::IsRuntimeAvailableToContext(
ScriptContext* context) {
for (const auto& extension :
*RendererExtensionRegistry::Get()->GetMainThreadExtensionSet()) {
ExternallyConnectableInfo* info = static_cast<ExternallyConnectableInfo*>(
extension->GetManifestData(manifest_keys::kExternallyConnectable));
if (info && info->matches.MatchesURL(context->url()))
return true;
}
return false;
}
// static
const char* ExtensionBindingsSystem::kWebAvailableFeatures[] = {
"app", "webstore", "dashboardPrivate",
};
} // namespace extensions
...@@ -57,6 +57,20 @@ class ExtensionBindingsSystem { ...@@ -57,6 +57,20 @@ class ExtensionBindingsSystem {
// Returns the associated RequestSender, if any. // Returns the associated RequestSender, if any.
// TODO(devlin): Factor this out. // TODO(devlin): Factor this out.
virtual RequestSender* GetRequestSender() = 0; virtual RequestSender* GetRequestSender() = 0;
// Returns true if any portion of the runtime API is available to the given
// |context|. This is different than just checking features because runtime's
// availability depends on the installed extensions and the active URL (in the
// case of extensions communicating with external websites).
static bool IsRuntimeAvailableToContext(ScriptContext* context);
// The APIs that could potentially be available to webpage-like contexts.
// This is the list of possible features; most web pages will not have access
// to these APIs.
// Note: `runtime` is not included here, since it's handled specially above.
// Note: We specify the size of the array to allow for its use in for loops
// without needing to expose a separate "kNumWebAvailableFeatures".
static const char* kWebAvailableFeatures[3];
}; };
} // namespace extensions } // namespace extensions
......
...@@ -116,19 +116,6 @@ v8::Local<v8::Object> GetOrCreateBindObjectIfAvailable( ...@@ -116,19 +116,6 @@ v8::Local<v8::Object> GetOrCreateBindObjectIfAvailable(
return bind_object.IsEmpty() ? GetOrCreateChrome(context) : bind_object; return bind_object.IsEmpty() ? GetOrCreateChrome(context) : bind_object;
} }
// Determines if a ScriptContext can connect to any externally_connectable-
// enabled extension.
bool IsRuntimeAvailableToContext(ScriptContext* context) {
for (const auto& extension :
*RendererExtensionRegistry::Get()->GetMainThreadExtensionSet()) {
ExternallyConnectableInfo* info = static_cast<ExternallyConnectableInfo*>(
extension->GetManifestData(manifest_keys::kExternallyConnectable));
if (info && info->matches.MatchesURL(context->url()))
return true;
}
return false;
}
// Creates the event bindings if necessary for the given |context|. // Creates the event bindings if necessary for the given |context|.
void MaybeCreateEventBindings(ScriptContext* context) { void MaybeCreateEventBindings(ScriptContext* context) {
// chrome.Event is part of the public API (although undocumented). Make it // chrome.Event is part of the public API (although undocumented). Make it
...@@ -177,12 +164,10 @@ void JsExtensionBindingsSystem::UpdateBindingsForContext( ...@@ -177,12 +164,10 @@ void JsExtensionBindingsSystem::UpdateBindingsForContext(
// Hard-code registration of any APIs that are exposed to webpage-like // Hard-code registration of any APIs that are exposed to webpage-like
// contexts, because it's too expensive to run the full bindings code. // contexts, because it's too expensive to run the full bindings code.
// All of the same permission checks will still apply. // All of the same permission checks will still apply.
if (context->GetAvailability("app").is_available()) for (const char* feature_name : kWebAvailableFeatures) {
RegisterBinding("app", "app", context); if (context->GetAvailability(feature_name).is_available())
if (context->GetAvailability("webstore").is_available()) RegisterBinding(feature_name, feature_name, context);
RegisterBinding("webstore", "webstore", context); }
if (context->GetAvailability("dashboardPrivate").is_available())
RegisterBinding("dashboardPrivate", "dashboardPrivate", context);
if (IsRuntimeAvailableToContext(context)) if (IsRuntimeAvailableToContext(context))
RegisterBinding("runtime", "runtime", context); RegisterBinding("runtime", "runtime", context);
break; break;
......
...@@ -420,14 +420,65 @@ void NativeExtensionBindingsSystem::WillReleaseScriptContext( ...@@ -420,14 +420,65 @@ void NativeExtensionBindingsSystem::WillReleaseScriptContext(
void NativeExtensionBindingsSystem::UpdateBindingsForContext( void NativeExtensionBindingsSystem::UpdateBindingsForContext(
ScriptContext* context) { ScriptContext* context) {
v8::HandleScope handle_scope(context->isolate()); v8::Isolate* isolate = context->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> v8_context = context->v8_context(); v8::Local<v8::Context> v8_context = context->v8_context();
v8::Local<v8::Object> chrome = GetOrCreateChrome(v8_context); v8::Local<v8::Object> chrome = GetOrCreateChrome(v8_context);
if (chrome.IsEmpty()) if (chrome.IsEmpty())
return; return;
BindingsSystemPerContextData* data = GetBindingsDataFromContext(v8_context); DCHECK(GetBindingsDataFromContext(v8_context));
DCHECK(data);
auto set_accessor = [chrome, isolate,
v8_context](base::StringPiece accessor_name) {
v8::Local<v8::String> api_name =
gin::StringToSymbol(isolate, accessor_name);
v8::Maybe<bool> success = chrome->SetAccessor(
v8_context, api_name, &BindingAccessor, nullptr, api_name);
return success.IsJust() && success.FromJust();
};
bool is_webpage = false;
switch (context->context_type()) {
case Feature::UNSPECIFIED_CONTEXT:
case Feature::WEB_PAGE_CONTEXT:
case Feature::BLESSED_WEB_PAGE_CONTEXT:
is_webpage = true;
break;
case Feature::SERVICE_WORKER_CONTEXT:
DCHECK(ExtensionsClient::Get()
->ExtensionAPIEnabledInExtensionServiceWorkers());
// Intentional fallthrough.
case Feature::BLESSED_EXTENSION_CONTEXT:
case Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
case Feature::UNBLESSED_EXTENSION_CONTEXT:
case Feature::CONTENT_SCRIPT_CONTEXT:
case Feature::WEBUI_CONTEXT:
is_webpage = false;
}
if (is_webpage) {
// Hard-code registration of any APIs that are exposed to webpage-like
// contexts, because it's more expensive to iterate over all the existing
// features when only a handful could ever be available.
// All of the same permission checks will still apply.
// TODO(devlin): It could be interesting to apply this same logic to all
// context types, especially on a given platform. Something to think about
// for when we generate features.
for (const char* feature_name : kWebAvailableFeatures) {
if (context->GetAvailability(feature_name).is_available() &&
!set_accessor(feature_name)) {
LOG(ERROR) << "Failed to create API on Chrome object.";
return;
}
}
// Runtime is special (see IsRuntimeAvailableToContext()).
if (IsRuntimeAvailableToContext(context) && !set_accessor("runtime"))
LOG(ERROR) << "Failed to create API on Chrome object.";
return;
}
const FeatureProvider* api_feature_provider = const FeatureProvider* api_feature_provider =
FeatureProvider::GetAPIFeatures(); FeatureProvider::GetAPIFeatures();
...@@ -471,11 +522,7 @@ void NativeExtensionBindingsSystem::UpdateBindingsForContext( ...@@ -471,11 +522,7 @@ void NativeExtensionBindingsSystem::UpdateBindingsForContext(
base::StringPiece accessor_name = base::StringPiece accessor_name =
GetFirstDifferentAPIName(map_entry.first, base::StringPiece()); GetFirstDifferentAPIName(map_entry.first, base::StringPiece());
last_accessor = accessor_name; last_accessor = accessor_name;
v8::Local<v8::String> api_name = if (!set_accessor(accessor_name)) {
gin::StringToSymbol(v8_context->GetIsolate(), accessor_name);
v8::Maybe<bool> success = chrome->SetAccessor(
v8_context, api_name, &BindingAccessor, nullptr, api_name);
if (!success.IsJust() || !success.FromJust()) {
LOG(ERROR) << "Failed to create API on Chrome object."; LOG(ERROR) << "Failed to create API on Chrome object.";
return; return;
} }
......
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