Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Unified Diff: extensions/renderer/dispatcher.cc

Issue 2512233002: [Extensions Bindings] Add ExtensionBindingsSystem interface; hook it up (Closed)
Patch Set: lazyboys Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « extensions/renderer/dispatcher.h ('k') | extensions/renderer/extension_bindings_system.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: extensions/renderer/dispatcher.cc
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index a47b77c7e6aa1e1ce21c5ec6ccb862050dce59ea..b837a247874f89a0c2da2b98fdcf6a806b77b101 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -21,7 +21,6 @@
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
@@ -49,7 +48,6 @@
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/manifest_handlers/content_capabilities_handler.h"
-#include "extensions/common/manifest_handlers/externally_connectable.h"
#include "extensions/common/manifest_handlers/options_page_info.h"
#include "extensions/common/message_bundle.h"
#include "extensions/common/permissions/permission_set.h"
@@ -59,7 +57,6 @@
#include "extensions/renderer/api_activity_logger.h"
#include "extensions/renderer/api_definitions_natives.h"
#include "extensions/renderer/app_window_custom_bindings.h"
-#include "extensions/renderer/binding_generating_native_handler.h"
#include "extensions/renderer/blob_native_handler.h"
#include "extensions/renderer/content_watcher.h"
#include "extensions/renderer/context_menus_custom_bindings.h"
@@ -74,6 +71,7 @@
#include "extensions/renderer/file_system_natives.h"
#include "extensions/renderer/guest_view/guest_view_internal_custom_bindings.h"
#include "extensions/renderer/id_generator_custom_bindings.h"
+#include "extensions/renderer/js_extension_bindings_system.h"
#include "extensions/renderer/logging_native_handler.h"
#include "extensions/renderer/messaging_bindings.h"
#include "extensions/renderer/module_system.h"
@@ -135,7 +133,6 @@ namespace {
static const int64_t kInitialExtensionIdleHandlerDelayMs = 5 * 1000;
static const int64_t kMaxExtensionIdleHandlerDelayMs = 5 * 60 * 1000;
-static const char kEventDispatchFunction[] = "dispatchEvent";
static const char kOnSuspendEvent[] = "runtime.onSuspend";
static const char kOnSuspendCanceledEvent[] = "runtime.onSuspendCanceled";
@@ -143,28 +140,6 @@ void CrashOnException(const v8::TryCatch& trycatch) {
NOTREACHED();
};
-// Returns the global value for "chrome" from |context|. If one doesn't exist
-// creates a new object for it.
-//
-// Note that this isn't necessarily an object, since webpages can write, for
-// example, "window.chrome = true".
-v8::Local<v8::Value> GetOrCreateChrome(ScriptContext* context) {
- v8::Local<v8::String> chrome_string(
- v8::String::NewFromUtf8(context->isolate(), "chrome"));
- v8::Local<v8::Object> global(context->v8_context()->Global());
- v8::Local<v8::Value> chrome(global->Get(chrome_string));
- if (chrome->IsUndefined()) {
- chrome = v8::Object::New(context->isolate());
- global->Set(chrome_string, chrome);
- }
- return chrome;
-}
-
-// Returns |value| cast to an object if possible, else an empty handle.
-v8::Local<v8::Object> AsObjectOrEmpty(v8::Local<v8::Value> value) {
- return value->IsObject() ? value.As<v8::Object>() : v8::Local<v8::Object>();
-}
-
// Calls a method |method_name| in a module |module_name| belonging to the
// module system from |context|. Intended as a callback target from
// ScriptContextSet::ForEach.
@@ -198,41 +173,22 @@ class ChromeNativeHandler : public ObjectBackedNativeHandler {
}
void GetChrome(const v8::FunctionCallbackInfo<v8::Value>& args) {
- args.GetReturnValue().Set(GetOrCreateChrome(context()));
+ // Check for the chrome property. If one doesn't exist, create one.
+ v8::Local<v8::String> chrome_string(
+ v8::String::NewFromUtf8(context()->isolate(), "chrome"));
+ v8::Local<v8::Object> global(context()->v8_context()->Global());
+ v8::Local<v8::Value> chrome(global->Get(chrome_string));
+ if (chrome->IsUndefined()) {
+ chrome = v8::Object::New(context()->isolate());
+ global->Set(chrome_string, chrome);
+ }
+ args.GetReturnValue().Set(chrome);
}
};
base::LazyInstance<WorkerScriptContextSet> g_worker_script_context_set =
LAZY_INSTANCE_INITIALIZER;
-// Dispatches the event with the given name, arguments, and filtering info in
-// the given |context|.
-void DispatchEventInContext(const std::string& event_name,
- const base::ListValue* event_args,
- const base::DictionaryValue* filtering_info,
- ScriptContext* context) {
- v8::HandleScope handle_scope(context->isolate());
- v8::Context::Scope context_scope(context->v8_context());
-
- std::vector<v8::Local<v8::Value>> arguments;
- arguments.push_back(gin::StringToSymbol(context->isolate(), event_name));
-
- {
- std::unique_ptr<content::V8ValueConverter> converter(
- content::V8ValueConverter::create());
- arguments.push_back(
- converter->ToV8Value(event_args, context->v8_context()));
- if (!filtering_info->empty()) {
- arguments.push_back(
- converter->ToV8Value(filtering_info, context->v8_context()));
- }
- }
-
- context->module_system()->CallModuleMethodSafe(
- kEventBindings, kEventDispatchFunction, arguments.size(),
- arguments.data());
-}
-
} // namespace
// Note that we can't use Blink public APIs in the constructor becase Blink
@@ -242,6 +198,9 @@ Dispatcher::Dispatcher(DispatcherDelegate* delegate)
content_watcher_(new ContentWatcher()),
source_map_(&ResourceBundle::GetSharedInstance()),
v8_schema_registry_(new V8SchemaRegistry),
+ bindings_system_(
+ new JsExtensionBindingsSystem(&source_map_,
+ base::MakeUnique<RequestSender>())),
user_script_set_manager_observer_(this),
activity_logging_enabled_(false) {
const base::CommandLine& command_line =
@@ -260,7 +219,6 @@ Dispatcher::Dispatcher(DispatcherDelegate* delegate)
script_injection_manager_.reset(
new ScriptInjectionManager(user_script_set_manager_.get()));
user_script_set_manager_observer_.Add(user_script_set_manager_.get());
- request_sender_.reset(new RequestSender());
PopulateSourceMap();
WakeEventPage::Get()->Init(RenderThread::Get());
// Ideally this should be done after checking
@@ -366,18 +324,11 @@ void Dispatcher::DidCreateScriptContext(
// Enable natives in startup.
ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system);
- RegisterNativeHandlers(module_system, context, request_sender_.get(),
+ RegisterNativeHandlers(module_system, context,
+ bindings_system_->GetRequestSender(),
v8_schema_registry_.get());
- // chrome.Event is part of the public API (although undocumented). Make it
- // lazily evalulate to Event from event_bindings.js. For extensions only
- // though, not all webpages!
- if (context->extension()) {
- v8::Local<v8::Object> chrome = AsObjectOrEmpty(GetOrCreateChrome(context));
- if (!chrome.IsEmpty())
- module_system->SetLazyField(chrome, "Event", kEventBindings, "Event");
- }
-
+ bindings_system_->DidCreateScriptContext(context);
UpdateBindingsForContext(context);
bool is_within_platform_app = IsWithinPlatformApp();
@@ -473,29 +424,26 @@ void Dispatcher::DidInitializeServiceWorkerContextOnWorkerThread(
context->set_url(url);
if (ExtensionsClient::Get()->ExtensionAPIEnabledInExtensionServiceWorkers()) {
- WorkerThreadDispatcher::Get()->AddWorkerData(service_worker_version_id);
- {
- // TODO(lazyboy): Make sure accessing |source_map_| in worker thread is
- // safe.
- std::unique_ptr<ModuleSystem> module_system(
- new ModuleSystem(context, &source_map_));
- context->set_module_system(std::move(module_system));
- }
+ WorkerThreadDispatcher::Get()->AddWorkerData(service_worker_version_id,
+ &source_map_);
+
+ // TODO(lazyboy): Make sure accessing |source_map_| in worker thread is
+ // safe.
+ context->set_module_system(
+ base::MakeUnique<ModuleSystem>(context, &source_map_));
ModuleSystem* module_system = context->module_system();
// Enable natives in startup.
ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system);
- RegisterNativeHandlers(
- module_system, context,
- WorkerThreadDispatcher::Get()->GetRequestSender(),
- WorkerThreadDispatcher::Get()->GetV8SchemaRegistry());
- // chrome.Event is part of the public API (although undocumented). Make it
- // lazily evalulate to Event from event_bindings.js.
- v8::Local<v8::Object> chrome = AsObjectOrEmpty(GetOrCreateChrome(context));
- if (!chrome.IsEmpty())
- module_system->SetLazyField(chrome, "Event", kEventBindings, "Event");
-
- UpdateBindingsForContext(context);
+ ExtensionBindingsSystem* worker_bindings_system =
+ WorkerThreadDispatcher::GetBindingsSystem();
+ RegisterNativeHandlers(module_system, context,
+ worker_bindings_system->GetRequestSender(),
+ WorkerThreadDispatcher::GetV8SchemaRegistry());
+
+ worker_bindings_system->DidCreateScriptContext(context);
+ worker_bindings_system->UpdateBindingsForContext(context);
+
// TODO(lazyboy): Get rid of RequireGuestViewModules() as this doesn't seem
// necessary for Extension SW.
RequireGuestViewModules(context);
@@ -555,10 +503,7 @@ void Dispatcher::WillReleaseScriptContext(
ScriptContext* context = script_context_set_->GetByV8Context(v8_context);
if (!context)
return;
-
- // TODO(kalman): Make |request_sender| use |context->AddInvalidationObserver|.
- // In fact |request_sender_| should really be owned by ScriptContext.
- request_sender_->InvalidateSource(context);
+ bindings_system_->WillReleaseScriptContext(context);
script_context_set_->Remove(context);
VLOG(1) << "Num tracked contexts: " << script_context_set_->size();
@@ -573,6 +518,10 @@ void Dispatcher::WillDestroyServiceWorkerContextOnWorkerThread(
url.SchemeIs(kExtensionResourceScheme)) {
// See comment in DidInitializeServiceWorkerContextOnWorkerThread.
g_worker_script_context_set.Get().Remove(v8_context, url);
+ // TODO(devlin): We're not calling
+ // ExtensionBindingsSystem::WillReleaseScriptContext() here. This should be
+ // fine, since the entire bindings system is being destroyed when we
+ // remove the worker data, but we might want to notify the system anyway.
}
if (ExtensionsClient::Get()->ExtensionAPIEnabledInExtensionServiceWorkers())
WorkerThreadDispatcher::Get()->RemoveWorkerData(service_worker_version_id);
@@ -649,7 +598,7 @@ void Dispatcher::OnExtensionResponse(int request_id,
bool success,
const base::ListValue& response,
const std::string& error) {
- request_sender_->HandleResponse(request_id, success, response, error);
+ bindings_system_->HandleResponse(request_id, success, response, error);
}
void Dispatcher::DispatchEvent(
@@ -657,9 +606,11 @@ void Dispatcher::DispatchEvent(
const std::string& event_name,
const base::ListValue& event_args,
const base::DictionaryValue& filtering_info) const {
- script_context_set_->ForEach(extension_id, nullptr,
- base::Bind(&DispatchEventInContext, event_name,
- &event_args, &filtering_info));
+ script_context_set_->ForEach(
+ extension_id, nullptr,
+ base::Bind(&ExtensionBindingsSystem::DispatchEventInContext,
+ base::Unretained(bindings_system_.get()), event_name,
+ &event_args, &filtering_info));
// Reset the idle handler each time there's any activity like event or message
// dispatch.
@@ -1035,8 +986,8 @@ void Dispatcher::OnDeliverMessage(int target_port_id,
const Message& message) {
std::unique_ptr<RequestSender::ScopedTabID> scoped_tab_id;
if (source_tab_id != -1) {
- scoped_tab_id.reset(
- new RequestSender::ScopedTabID(request_sender(), source_tab_id));
+ scoped_tab_id.reset(new RequestSender::ScopedTabID(
+ bindings_system_->GetRequestSender(), source_tab_id));
}
MessagingBindings::DeliverMessage(*script_context_set_, target_port_id,
@@ -1208,7 +1159,7 @@ void Dispatcher::OnUnloaded(const std::string& id) {
std::set<ScriptContext*> removed_contexts =
script_context_set_->OnExtensionUnloaded(id);
for (ScriptContext* context : removed_contexts) {
- request_sender_->InvalidateSource(context);
+ bindings_system_->WillReleaseScriptContext(context);
}
// Update the available bindings for the remaining contexts. These may have
@@ -1382,112 +1333,12 @@ void Dispatcher::UpdateBindings(const std::string& extension_id) {
base::Unretained(this)));
}
-// Note: this function runs on multiple threads: main renderer thread and
-// service worker threads.
void Dispatcher::UpdateBindingsForContext(ScriptContext* context) {
- v8::HandleScope handle_scope(context->isolate());
- v8::Context::Scope context_scope(context->v8_context());
-
- // TODO(kalman): Make the bindings registration have zero overhead then run
- // the same code regardless of context type.
- switch (context->context_type()) {
- case Feature::UNSPECIFIED_CONTEXT:
- case Feature::WEB_PAGE_CONTEXT:
- case Feature::BLESSED_WEB_PAGE_CONTEXT:
- // Hard-code registration of any APIs that are exposed to webpage-like
- // contexts, because it's too expensive to run the full bindings code.
- // All of the same permission checks will still apply.
- if (context->GetAvailability("app").is_available())
- RegisterBinding("app", context);
- if (context->GetAvailability("webstore").is_available())
- RegisterBinding("webstore", context);
- if (context->GetAvailability("dashboardPrivate").is_available())
- RegisterBinding("dashboardPrivate", context);
- if (IsRuntimeAvailableToContext(context))
- RegisterBinding("runtime", context);
- UpdateContentCapabilities(context);
- break;
-
- case Feature::SERVICE_WORKER_CONTEXT:
- DCHECK(ExtensionsClient::Get()
- ->ExtensionAPIEnabledInExtensionServiceWorkers());
- // Intentional fallthrough.
- case Feature::BLESSED_EXTENSION_CONTEXT:
- case Feature::UNBLESSED_EXTENSION_CONTEXT:
- case Feature::CONTENT_SCRIPT_CONTEXT:
- case Feature::WEBUI_CONTEXT: {
- // Extension context; iterate through all the APIs and bind the available
- // ones.
- const FeatureProvider* api_feature_provider =
- FeatureProvider::GetAPIFeatures();
- for (const auto& map_entry : api_feature_provider->GetAllFeatures()) {
- // Internal APIs are included via require(api_name) from internal code
- // rather than chrome[api_name].
- if (map_entry.second->IsInternal())
- continue;
-
- // If this API has a parent feature (and isn't marked 'noparent'),
- // then this must be a function or event, so we should not register.
- if (api_feature_provider->GetParent(map_entry.second.get()) != nullptr)
- continue;
-
- // Skip chrome.test if this isn't a test.
- if (map_entry.first == "test" &&
- !base::CommandLine::ForCurrentProcess()->HasSwitch(
- ::switches::kTestType)) {
- continue;
- }
-
- if (context->IsAnyFeatureAvailableToContext(*map_entry.second)) {
- // TODO(lazyboy): RegisterBinding() uses |source_map_|, any thread
- // safety issue?
- RegisterBinding(map_entry.first, context);
- }
- }
- break;
- }
- }
-}
-
-void Dispatcher::RegisterBinding(const std::string& api_name,
- ScriptContext* context) {
- std::string bind_name;
- v8::Local<v8::Object> bind_object =
- GetOrCreateBindObjectIfAvailable(api_name, &bind_name, context);
-
- // Empty if the bind object failed to be created, probably because the
- // extension overrode chrome with a non-object, e.g. window.chrome = true.
- if (bind_object.IsEmpty())
- return;
-
- v8::Local<v8::String> v8_bind_name =
- v8::String::NewFromUtf8(context->isolate(), bind_name.c_str());
- if (bind_object->HasRealNamedProperty(v8_bind_name)) {
- // The bind object may already have the property if the API has been
- // registered before (or if the extension has put something there already,
- // but, whatevs).
- //
- // In the former case, we need to re-register the bindings for the APIs
- // which the extension now has permissions for (if any), but not touch any
- // others so that we don't destroy state such as event listeners.
- //
- // TODO(kalman): Only register available APIs to make this all moot.
- if (bind_object->HasRealNamedCallbackProperty(v8_bind_name))
- return; // lazy binding still there, nothing to do
- if (bind_object->Get(v8_bind_name)->IsObject())
- return; // binding has already been fully installed
- }
-
- ModuleSystem* module_system = context->module_system();
- if (!source_map_.Contains(api_name)) {
- module_system->RegisterNativeHandler(
- api_name,
- std::unique_ptr<NativeHandler>(
- new BindingGeneratingNativeHandler(context, api_name, "binding")));
- module_system->SetNativeLazyField(
- bind_object, bind_name, api_name, "binding");
- } else {
- module_system->SetLazyField(bind_object, bind_name, api_name, "binding");
+ bindings_system_->UpdateBindingsForContext(context);
+ Feature::Context context_type = context->context_type();
+ if (context_type == Feature::WEB_PAGE_CONTEXT ||
+ context_type == Feature::BLESSED_WEB_PAGE_CONTEXT) {
+ UpdateContentCapabilities(context);
}
}
@@ -1516,17 +1367,6 @@ void Dispatcher::RegisterNativeHandlers(ModuleSystem* module_system,
delegate_->RegisterNativeHandlers(this, module_system, context);
}
-bool Dispatcher::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;
-}
-
void Dispatcher::UpdateContentCapabilities(ScriptContext* context) {
APIPermissionSet permissions;
for (const auto& extension :
@@ -1571,80 +1411,6 @@ bool Dispatcher::IsWithinPlatformApp() {
return false;
}
-// static.
-v8::Local<v8::Object> Dispatcher::GetOrCreateObject(
- const v8::Local<v8::Object>& object,
- const std::string& field,
- v8::Isolate* isolate) {
- v8::Local<v8::String> key = v8::String::NewFromUtf8(isolate, field.c_str());
- // If the object has a callback property, it is assumed it is an unavailable
- // API, so it is safe to delete. This is checked before GetOrCreateObject is
- // called.
- if (object->HasRealNamedCallbackProperty(key)) {
- object->Delete(key);
- } else if (object->HasRealNamedProperty(key)) {
- v8::Local<v8::Value> value = object->Get(key);
- CHECK(value->IsObject());
- return v8::Local<v8::Object>::Cast(value);
- }
-
- v8::Local<v8::Object> new_object = v8::Object::New(isolate);
- object->Set(key, new_object);
- return new_object;
-}
-
-// static.
-v8::Local<v8::Object> Dispatcher::GetOrCreateBindObjectIfAvailable(
- const std::string& api_name,
- std::string* bind_name,
- ScriptContext* context) {
- std::vector<std::string> split = base::SplitString(
- api_name, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
- v8::Local<v8::Object> bind_object;
-
- // 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.
- const FeatureProvider* api_feature_provider =
- FeatureProvider::GetAPIFeatures();
- 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 (api_feature_provider->GetFeature(ancestor_name) &&
- context->GetAvailability(ancestor_name).is_available() &&
- !context->GetAvailability(api_name).is_available()) {
- only_ancestor_available = true;
- break;
- }
-
- if (bind_object.IsEmpty()) {
- bind_object = AsObjectOrEmpty(GetOrCreateChrome(context));
- if (bind_object.IsEmpty())
- return v8::Local<v8::Object>();
- }
- bind_object = GetOrCreateObject(bind_object, split[i], context->isolate());
- }
-
- if (only_ancestor_available)
- return v8::Local<v8::Object>();
-
- if (bind_name)
- *bind_name = split.back();
-
- return bind_object.IsEmpty() ? AsObjectOrEmpty(GetOrCreateChrome(context))
- : bind_object;
-}
-
void Dispatcher::RequireGuestViewModules(ScriptContext* context) {
Feature::Context context_type = context->context_type();
ModuleSystem* module_system = context->module_system();
« no previous file with comments | « extensions/renderer/dispatcher.h ('k') | extensions/renderer/extension_bindings_system.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698