diff options
author | cduvall@chromium.org <cduvall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-09 14:26:24 +0000 |
---|---|---|
committer | cduvall@chromium.org <cduvall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-09 14:26:24 +0000 |
commit | 4f1633f8ab00e5362a585948ff1e5231bcbca5ef (patch) | |
tree | 5f0e3e4d1d7a27970fe97074cf0aabe520a0d8ad /chrome/renderer | |
parent | 7c04e5896a71a6061ebfc13facdcf5579b7bda9d (diff) | |
download | chromium_src-4f1633f8ab00e5362a585948ff1e5231bcbca5ef.zip chromium_src-4f1633f8ab00e5362a585948ff1e5231bcbca5ef.tar.gz chromium_src-4f1633f8ab00e5362a585948ff1e5231bcbca5ef.tar.bz2 |
Set up V8 bindings for extension/app APIs when they're first used, not on
context creation. This should gives us a significant reduction in extension/app
startup time and slightly better memory usage.
It also gives us better error messages, the chance to complete the
implementation of API features, and eventually the ability to expose select
extension APIs (e.g. extension.sendMessage) to web pages.
Resubmitting: changes made to resubmit this patch reviewed in: https://codereview.chromium.org/12378077/
BUG=163678,120070,55316,177163
TBR=ben@chromium.org
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=186643
Review URL: https://chromiumcodereview.appspot.com/11571014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187143 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
120 files changed, 2029 insertions, 1312 deletions
diff --git a/chrome/renderer/extensions/api_definitions_natives.cc b/chrome/renderer/extensions/api_definitions_natives.cc index 1bdd767..85f86b3 100644 --- a/chrome/renderer/extensions/api_definitions_natives.cc +++ b/chrome/renderer/extensions/api_definitions_natives.cc @@ -12,8 +12,10 @@ const char kInvalidExtensionNamespace[] = "Invalid extension namespace"; namespace extensions { -ApiDefinitionsNatives::ApiDefinitionsNatives(Dispatcher* dispatcher) - : ChromeV8Extension(dispatcher) { +ApiDefinitionsNatives::ApiDefinitionsNatives(Dispatcher* dispatcher, + ChromeV8Context* context) + : ChromeV8Extension(dispatcher, context->v8_context()), + context_(context) { RouteFunction("GetExtensionAPIDefinition", base::Bind(&ApiDefinitionsNatives::GetExtensionAPIDefinition, base::Unretained(this))); @@ -21,10 +23,7 @@ ApiDefinitionsNatives::ApiDefinitionsNatives(Dispatcher* dispatcher) v8::Handle<v8::Value> ApiDefinitionsNatives::GetExtensionAPIDefinition( const v8::Arguments& args) { - ChromeV8Context* v8_context = dispatcher()->v8_context_set().GetCurrent(); - CHECK(v8_context); - - std::set<std::string> available_apis(v8_context->GetAvailableExtensionAPIs()); + std::set<std::string> available_apis(context_->GetAvailableExtensionAPIs()); if (args.Length() == 0) return dispatcher()->v8_schema_registry()->GetSchemas(available_apis); diff --git a/chrome/renderer/extensions/api_definitions_natives.h b/chrome/renderer/extensions/api_definitions_natives.h index 45b46b6..f8801e5 100644 --- a/chrome/renderer/extensions/api_definitions_natives.h +++ b/chrome/renderer/extensions/api_definitions_natives.h @@ -10,12 +10,14 @@ #include "v8/include/v8.h" +class ChromeV8Context; + namespace extensions { // Native functions for JS to get access to the schemas for extension APIs. class ApiDefinitionsNatives : public ChromeV8Extension { public: - explicit ApiDefinitionsNatives(Dispatcher* dispatcher); + ApiDefinitionsNatives(Dispatcher* dispatcher, ChromeV8Context* context); private: // Returns the list of schemas that are available to the calling context @@ -23,6 +25,8 @@ class ApiDefinitionsNatives : public ChromeV8Extension { // of all schemas that are available to the calling context. v8::Handle<v8::Value> GetExtensionAPIDefinition(const v8::Arguments& args); + ChromeV8Context* context_; + DISALLOW_COPY_AND_ASSIGN(ApiDefinitionsNatives); }; diff --git a/chrome/renderer/extensions/app_bindings.cc b/chrome/renderer/extensions/app_bindings.cc index aea0606..97472b9 100644 --- a/chrome/renderer/extensions/app_bindings.cc +++ b/chrome/renderer/extensions/app_bindings.cc @@ -57,9 +57,8 @@ const char* kInvalidCallbackIdError = "Invalid callbackId"; } // namespace -AppBindings::AppBindings(Dispatcher* dispatcher, - ChromeV8Context* context) - : ChromeV8Extension(dispatcher), +AppBindings::AppBindings(Dispatcher* dispatcher, ChromeV8Context* context) + : ChromeV8Extension(dispatcher, context->v8_context()), ChromeV8ExtensionHandler(context) { RouteFunction("GetIsInstalled", base::Bind(&AppBindings::GetIsInstalled, base::Unretained(this))); diff --git a/chrome/renderer/extensions/app_bindings.h b/chrome/renderer/extensions/app_bindings.h index 2fff69c..99028e4 100644 --- a/chrome/renderer/extensions/app_bindings.h +++ b/chrome/renderer/extensions/app_bindings.h @@ -24,8 +24,7 @@ class ChromeV8Context; class AppBindings : public ChromeV8Extension, public ChromeV8ExtensionHandler { public: - explicit AppBindings(Dispatcher* dispatcher, - ChromeV8Context* context); + AppBindings(Dispatcher* dispatcher, ChromeV8Context* context); private: // IPC::Listener diff --git a/chrome/renderer/extensions/app_runtime_custom_bindings.cc b/chrome/renderer/extensions/app_runtime_custom_bindings.cc index 0dd73c5..60fbace9 100644 --- a/chrome/renderer/extensions/app_runtime_custom_bindings.cc +++ b/chrome/renderer/extensions/app_runtime_custom_bindings.cc @@ -55,8 +55,9 @@ v8::Handle<v8::Value> CreateBlob(const v8::Arguments &args) { namespace extensions { -AppRuntimeCustomBindings::AppRuntimeCustomBindings() - : ChromeV8Extension(NULL) { +AppRuntimeCustomBindings::AppRuntimeCustomBindings( + Dispatcher* dispatcher, + v8::Handle<v8::Context> context) : ChromeV8Extension(dispatcher, context) { RouteStaticFunction("DeserializeString", &DeserializeString); RouteStaticFunction("SerializeToString", &SerializeToString); RouteStaticFunction("CreateBlob", &CreateBlob); diff --git a/chrome/renderer/extensions/app_runtime_custom_bindings.h b/chrome/renderer/extensions/app_runtime_custom_bindings.h index 5dd8346..c691300 100644 --- a/chrome/renderer/extensions/app_runtime_custom_bindings.h +++ b/chrome/renderer/extensions/app_runtime_custom_bindings.h @@ -12,7 +12,8 @@ namespace extensions { // The native component of custom bindings for the chrome.app.runtime API. class AppRuntimeCustomBindings : public ChromeV8Extension { public: - AppRuntimeCustomBindings(); + AppRuntimeCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); private: DISALLOW_COPY_AND_ASSIGN(AppRuntimeCustomBindings); diff --git a/chrome/renderer/extensions/app_window_custom_bindings.cc b/chrome/renderer/extensions/app_window_custom_bindings.cc index a66bb55..060dc5a 100644 --- a/chrome/renderer/extensions/app_window_custom_bindings.cc +++ b/chrome/renderer/extensions/app_window_custom_bindings.cc @@ -50,8 +50,9 @@ class DidCreateDocumentElementObserver : public content::RenderViewObserver { Dispatcher* dispatcher_; }; -AppWindowCustomBindings::AppWindowCustomBindings(Dispatcher* dispatcher) - : ChromeV8Extension(dispatcher) { +AppWindowCustomBindings::AppWindowCustomBindings( + Dispatcher* dispatcher, + v8::Handle<v8::Context> context) : ChromeV8Extension(dispatcher, context) { RouteFunction("GetView", base::Bind(&AppWindowCustomBindings::GetView, base::Unretained(this))); @@ -153,7 +154,7 @@ v8::Handle<v8::Value> AppWindowCustomBindings::GetView( // need to make sure the security origin is set up before returning the DOM // reference. A better way to do this would be to have the browser pass the // opener through so opener_id is set in RenderViewImpl's constructor. - content::RenderView* render_view = GetCurrentRenderView(); + content::RenderView* render_view = GetRenderView(); if (!render_view) return v8::Undefined(); WebKit::WebFrame* opener = render_view->GetWebView()->mainFrame(); diff --git a/chrome/renderer/extensions/app_window_custom_bindings.h b/chrome/renderer/extensions/app_window_custom_bindings.h index 6333097..be84aa0 100644 --- a/chrome/renderer/extensions/app_window_custom_bindings.h +++ b/chrome/renderer/extensions/app_window_custom_bindings.h @@ -13,7 +13,8 @@ class Dispatcher; // Implements custom bindings for the app.window API. class AppWindowCustomBindings : public ChromeV8Extension { public: - explicit AppWindowCustomBindings(Dispatcher* dispatcher); + AppWindowCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); private: v8::Handle<v8::Value> GetView(const v8::Arguments& args); diff --git a/chrome/renderer/extensions/binding_generating_native_handler.cc b/chrome/renderer/extensions/binding_generating_native_handler.cc new file mode 100644 index 0000000..22c45c5 --- /dev/null +++ b/chrome/renderer/extensions/binding_generating_native_handler.cc @@ -0,0 +1,39 @@ +// 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/binding_generating_native_handler.h" + +#include "chrome/renderer/extensions/module_system.h" + +namespace extensions { + +BindingGeneratingNativeHandler::BindingGeneratingNativeHandler( + ModuleSystem* module_system, + const std::string& api_name, + const std::string& bind_to) + : module_system_(module_system), + api_name_(api_name), + bind_to_(bind_to) { +} + +v8::Handle<v8::Object> BindingGeneratingNativeHandler::NewInstance() { + v8::HandleScope scope; + v8::Handle<v8::Object> binding = v8::Handle<v8::Object>::Cast( + v8::Handle<v8::Object>::Cast(module_system_->Require( + "binding"))->Get(v8::String::New("Binding"))); + v8::Handle<v8::Function> create = v8::Handle<v8::Function>::Cast( + binding->Get(v8::String::New("create"))); + v8::Handle<v8::Value> argv[] = { v8::String::New(api_name_.c_str()) }; + v8::Handle<v8::Object> binding_instance = v8::Handle<v8::Object>::Cast( + create->Call(binding, 1, argv)); + v8::Handle<v8::Function> generate = v8::Handle<v8::Function>::Cast( + binding_instance->Get(v8::String::New("generate"))); + v8::Handle<v8::Value> compiled_schema = + generate->Call(binding_instance, 0, NULL); + v8::Handle<v8::Object> object = v8::Object::New(); + object->Set(v8::String::New(bind_to_.c_str()), compiled_schema); + return scope.Close(object); +} + +} // extensions diff --git a/chrome/renderer/extensions/binding_generating_native_handler.h b/chrome/renderer/extensions/binding_generating_native_handler.h new file mode 100644 index 0000000..9fefb30 --- /dev/null +++ b/chrome/renderer/extensions/binding_generating_native_handler.h @@ -0,0 +1,38 @@ +// 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_BINDING_GENERATING_NATIVE_HANDLER_H_ +#define CHROME_RENDERER_EXTENSIONS_BINDING_GENERATING_NATIVE_HANDLER_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "chrome/renderer/extensions/native_handler.h" + +namespace extensions { + +class ModuleSystem; + +// Generates API bindings based on the JSON/IDL schemas. This is done by +// creating a |Binding| (from binding.js) for the schema and generating the +// bindings from that. +class BindingGeneratingNativeHandler : public NativeHandler { + public: + // Generates binding for |api_name|, and sets the |bind_to| property on the + // Object returned by |NewInstance| to the generated binding. + BindingGeneratingNativeHandler(ModuleSystem* module_system, + const std::string& api_name, + const std::string& bind_to); + + virtual v8::Handle<v8::Object> NewInstance() OVERRIDE; + + private: + ModuleSystem* module_system_; + std::string api_name_; + std::string bind_to_; +}; + +} // extensions + +#endif // CHROME_RENDERER_EXTENSIONS_BINDING_GENERATING_NATIVE_HANDLER_H_ diff --git a/chrome/renderer/extensions/chrome_v8_context.cc b/chrome/renderer/extensions/chrome_v8_context.cc index b683507..e80bf7c 100644 --- a/chrome/renderer/extensions/chrome_v8_context.cc +++ b/chrome/renderer/extensions/chrome_v8_context.cc @@ -12,6 +12,7 @@ #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_set.h" #include "chrome/renderer/extensions/chrome_v8_extension.h" +#include "chrome/renderer/extensions/module_system.h" #include "chrome/renderer/extensions/user_script_slave.h" #include "content/public/renderer/render_view.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" @@ -23,6 +24,10 @@ namespace extensions { namespace { const char kChromeHidden[] = "chromeHidden"; +const char kUnavailableMessage[] = "You do not have permission to access this " + "API. Ensure that the required permission " + "or manifest property is included in your " + "manifest.json."; const char kValidateCallbacks[] = "validateCallbacks"; const char kValidateAPI[] = "validateAPI"; @@ -36,7 +41,8 @@ ChromeV8Context::ChromeV8Context(v8::Handle<v8::Context> v8_context, : v8_context_(v8_context), web_frame_(web_frame), extension_(extension), - context_type_(context_type) { + context_type_(context_type), + available_extension_apis_initialized_(false) { VLOG(1) << "Created context:\n" << " extension id: " << GetExtensionID() << "\n" << " frame: " << web_frame_ << "\n" @@ -46,6 +52,16 @@ ChromeV8Context::ChromeV8Context(v8::Handle<v8::Context> v8_context, ChromeV8Context::~ChromeV8Context() { VLOG(1) << "Destroyed context for extension\n" << " extension id: " << GetExtensionID(); + Invalidate(); +} + +void ChromeV8Context::Invalidate() { + if (v8_context_.get().IsEmpty()) + return; + if (module_system_) + module_system_->Invalidate(); + web_frame_ = NULL; + v8_context_.reset(); } std::string ChromeV8Context::GetExtensionID() { @@ -64,11 +80,11 @@ v8::Handle<v8::Value> ChromeV8Context::GetOrCreateChromeHidden( global->SetHiddenValue(v8::String::New(kChromeHidden), hidden); if (DCHECK_IS_ON()) { - // Tell schema_generated_bindings.js to validate callbacks and events - // against their schema definitions. + // Tell bindings.js to validate callbacks and events against their schema + // definitions. v8::Local<v8::Object>::Cast(hidden)->Set( v8::String::New(kValidateCallbacks), v8::True()); - // Tell schema_generated_bindings.js to validate API for ambiguity. + // Tell bindings.js to validate API for ambiguity. v8::Local<v8::Object>::Cast(hidden)->Set( v8::String::New(kValidateAPI), v8::True()); } @@ -97,9 +113,9 @@ bool ChromeV8Context::CallChromeHiddenMethod( v8::Handle<v8::Value>* result) const { v8::Context::Scope context_scope(v8_context_.get()); - // ChromeV8ContextSet calls clear_web_frame() and then schedules a task to - // delete this object. This check prevents a race from attempting to execute - // script on a NULL web_frame_. + // ChromeV8ContextSet calls Invalidate() and then schedules a task to delete + // this object. This check prevents a race from attempting to execute script + // on a NULL web_frame_. if (!web_frame_) return false; @@ -139,15 +155,28 @@ bool ChromeV8Context::CallChromeHiddenMethod( } const std::set<std::string>& ChromeV8Context::GetAvailableExtensionAPIs() { - if (!available_extension_apis_.get()) { + if (!available_extension_apis_initialized_) { available_extension_apis_ = ExtensionAPI::GetSharedInstance()->GetAPIsForContext( context_type_, extension_, - UserScriptSlave::GetDataSourceURLForFrame( - web_frame_)).Pass(); + UserScriptSlave::GetDataSourceURLForFrame(web_frame_)); + available_extension_apis_initialized_ = true; } - return *(available_extension_apis_.get()); + return available_extension_apis_; +} + +Feature::Availability ChromeV8Context::GetAvailability( + const std::string& api_name) { + const std::set<std::string>& available_apis = GetAvailableExtensionAPIs(); + + // TODO(cduvall/kalman): Switch to ExtensionAPI::IsAvailable() once Features + // are complete. + if (available_apis.find(api_name) != available_apis.end()) + return Feature::CreateAvailability(Feature::IS_AVAILABLE, ""); + + return Feature::CreateAvailability(Feature::INVALID_CONTEXT, + kUnavailableMessage); } void ChromeV8Context::DispatchOnLoadEvent(bool is_incognito_process, diff --git a/chrome/renderer/extensions/chrome_v8_context.h b/chrome/renderer/extensions/chrome_v8_context.h index d541e2c..83142d0 100644 --- a/chrome/renderer/extensions/chrome_v8_context.h +++ b/chrome/renderer/extensions/chrome_v8_context.h @@ -37,6 +37,10 @@ class ChromeV8Context { Feature::Context context_type); ~ChromeV8Context(); + // Clears the WebFrame for this contexts and invalidates the associated + // ModuleSystem. + void Invalidate(); + v8::Handle<v8::Context> v8_context() const { return v8_context_.get(); } @@ -48,9 +52,6 @@ class ChromeV8Context { WebKit::WebFrame* web_frame() const { return web_frame_; } - void clear_web_frame() { - web_frame_ = NULL; - } Feature::Context context_type() const { return context_type_; @@ -101,6 +102,8 @@ class ChromeV8Context { // APIs are available, returns an empty set. const std::set<std::string>& GetAvailableExtensionAPIs(); + Feature::Availability GetAvailability(const std::string& api_name); + // Returns a string description of the type of context this is. std::string GetContextTypeDescription(); @@ -123,7 +126,8 @@ class ChromeV8Context { scoped_ptr<ModuleSystem> module_system_; // The extension APIs available to this context. - scoped_ptr<std::set<std::string> > available_extension_apis_; + std::set<std::string> available_extension_apis_; + bool available_extension_apis_initialized_; DISALLOW_COPY_AND_ASSIGN(ChromeV8Context); }; diff --git a/chrome/renderer/extensions/chrome_v8_context_set.cc b/chrome/renderer/extensions/chrome_v8_context_set.cc index 0be3ece..88f7f98 100644 --- a/chrome/renderer/extensions/chrome_v8_context_set.cc +++ b/chrome/renderer/extensions/chrome_v8_context_set.cc @@ -75,7 +75,7 @@ void ChromeV8ContextSet::Add(ChromeV8Context* context) { void ChromeV8ContextSet::Remove(ChromeV8Context* context) { if (contexts_.erase(context)) { - context->clear_web_frame(); + context->Invalidate(); MessageLoop::current()->DeleteSoon(FROM_HERE, context); } } diff --git a/chrome/renderer/extensions/chrome_v8_extension.cc b/chrome/renderer/extensions/chrome_v8_extension.cc index a5cbf03..2a3e7d6d 100644 --- a/chrome/renderer/extensions/chrome_v8_extension.cc +++ b/chrome/renderer/extensions/chrome_v8_extension.cc @@ -25,61 +25,28 @@ using WebKit::WebView; namespace extensions { -namespace { - -static base::LazyInstance<ChromeV8Extension::InstanceSet> g_instances = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -// static -content::RenderView* ChromeV8Extension::GetCurrentRenderView() { - WebFrame* webframe = WebFrame::frameForCurrentContext(); - DCHECK(webframe) << "RetrieveCurrentFrame called when not in a V8 context."; - if (!webframe) - return NULL; - - WebView* webview = webframe->view(); - if (!webview) - return NULL; // can happen during closing - - content::RenderView* renderview = content::RenderView::FromWebView(webview); - DCHECK(renderview) << "Encountered a WebView without a WebViewDelegate"; - return renderview; -} - -ChromeV8Extension::ChromeV8Extension(Dispatcher* dispatcher) - // TODO(svenpanne) It would be nice to remove the GetCurrent() call and use - // an additional constructor parameter instead, but this would involve too - // many changes for now. - : NativeHandler(v8::Isolate::GetCurrent()), +ChromeV8Extension::ChromeV8Extension(Dispatcher* dispatcher, + v8::Handle<v8::Context> context) + : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) { - g_instances.Get().insert(this); + CHECK(dispatcher); } ChromeV8Extension::~ChromeV8Extension() { - g_instances.Get().erase(this); } -// static -const ChromeV8Extension::InstanceSet& ChromeV8Extension::GetAll() { - return g_instances.Get(); +ChromeV8Context* ChromeV8Extension::GetContext() { + return dispatcher_->v8_context_set().GetByV8Context(v8_context()); } -const Extension* ChromeV8Extension::GetExtensionForCurrentRenderView() const { - content::RenderView* renderview = GetCurrentRenderView(); - if (!renderview) - return NULL; // this can happen as a tab is closing. - - WebDocument document = renderview->GetWebView()->mainFrame()->document(); - GURL url = document.url(); - const ExtensionSet* extensions = dispatcher_->extensions(); - if (!extensions->ExtensionBindingsAllowed( - ExtensionURLInfo(document.securityOrigin(), url))) - return NULL; +content::RenderView* ChromeV8Extension::GetRenderView() { + ChromeV8Context* context = GetContext(); + return context ? context->GetRenderView() : NULL; +} - return extensions->GetExtensionOrAppByURL( - ExtensionURLInfo(document.securityOrigin(), url)); +const Extension* ChromeV8Extension::GetExtensionForRenderView() { + ChromeV8Context* context = GetContext(); + return context ? context->extension() : NULL; } } // namespace extensions diff --git a/chrome/renderer/extensions/chrome_v8_extension.h b/chrome/renderer/extensions/chrome_v8_extension.h index e12af64..42a9e6e 100644 --- a/chrome/renderer/extensions/chrome_v8_extension.h +++ b/chrome/renderer/extensions/chrome_v8_extension.h @@ -9,7 +9,7 @@ #include "base/memory/linked_ptr.h" #include "base/string_piece.h" #include "chrome/renderer/extensions/chrome_v8_extension_handler.h" -#include "chrome/renderer/extensions/native_handler.h" +#include "chrome/renderer/extensions/object_backed_native_handler.h" #include "v8/include/v8.h" #include <map> @@ -26,19 +26,26 @@ class ChromeV8Context; class Dispatcher; class Extension; +// DEPRECATED. +// // This is a base class for chrome extension bindings. Common features that // are shared by different modules go here. -// TODO(koz): Rename this to ExtensionNativeModule. -class ChromeV8Extension : public NativeHandler { +// +// TODO(kalman): Delete this class entirely, it has no value anymore. +// Custom bindings should extend ObjectBackedNativeHandler. +class ChromeV8Extension : public ObjectBackedNativeHandler { public: - typedef std::set<ChromeV8Extension*> InstanceSet; - static const InstanceSet& GetAll(); - - explicit ChromeV8Extension(Dispatcher* dispatcher); + ChromeV8Extension(Dispatcher* dispatcher, v8::Handle<v8::Context> context); virtual ~ChromeV8Extension(); Dispatcher* dispatcher() { return dispatcher_; } + ChromeV8Context* GetContext(); + + // Shortcuts through to the context's render view and extension. + content::RenderView* GetRenderView(); + const Extension* GetExtensionForRenderView(); + protected: template<class T> static T* GetFromArguments(const v8::Arguments& args) { @@ -47,17 +54,6 @@ class ChromeV8Extension : public NativeHandler { return result; } - // Gets the render view for the current v8 context. - static content::RenderView* GetCurrentRenderView(); - - // Note: do not call this function before or during the chromeHidden.onLoad - // event dispatch. The URL might not have been committed yet and might not - // be an extension URL. - const Extension* GetExtensionForCurrentRenderView() const; - - // Returns the chromeHidden object for the current context. - static v8::Handle<v8::Value> GetChromeHidden(const v8::Arguments& args); - Dispatcher* dispatcher_; private: diff --git a/chrome/renderer/extensions/content_watcher.cc b/chrome/renderer/extensions/content_watcher.cc index 5c2bb7e..3176fdd 100644 --- a/chrome/renderer/extensions/content_watcher.cc +++ b/chrome/renderer/extensions/content_watcher.cc @@ -16,11 +16,13 @@ namespace extensions { namespace { + class MutationHandler : public ChromeV8Extension { public: explicit MutationHandler(Dispatcher* dispatcher, + v8::Handle<v8::Context> v8_context, base::WeakPtr<ContentWatcher> content_watcher) - : ChromeV8Extension(dispatcher), + : ChromeV8Extension(dispatcher, v8_context), content_watcher_(content_watcher) { RouteFunction("FrameMutated", base::Bind(&MutationHandler::FrameMutated, @@ -31,7 +33,7 @@ class MutationHandler : public ChromeV8Extension { v8::Handle<v8::Value> FrameMutated(const v8::Arguments& args) { if (content_watcher_) { content_watcher_->ScanAndNotify( - WebKit::WebFrame::frameForCurrentContext()); + WebKit::WebFrame::frameForContext(v8_context())); } return v8::Undefined(); } @@ -46,9 +48,10 @@ ContentWatcher::ContentWatcher(Dispatcher* dispatcher) dispatcher_(dispatcher) {} ContentWatcher::~ContentWatcher() {} -scoped_ptr<NativeHandler> ContentWatcher::MakeNatives() { - return scoped_ptr<NativeHandler>( - new MutationHandler(dispatcher_, weak_ptr_factory_.GetWeakPtr())); +scoped_ptr<NativeHandler> ContentWatcher::MakeNatives( + v8::Handle<v8::Context> v8_context) { + return scoped_ptr<NativeHandler>(new MutationHandler( + dispatcher_, v8_context, weak_ptr_factory_.GetWeakPtr())); } void ContentWatcher::OnWatchPages( diff --git a/chrome/renderer/extensions/content_watcher.h b/chrome/renderer/extensions/content_watcher.h index 908dad3..5753435 100644 --- a/chrome/renderer/extensions/content_watcher.h +++ b/chrome/renderer/extensions/content_watcher.h @@ -36,7 +36,7 @@ class ContentWatcher { ~ContentWatcher(); // Returns the callback to call on a frame change. - scoped_ptr<NativeHandler> MakeNatives(); + scoped_ptr<NativeHandler> MakeNatives(v8::Handle<v8::Context> v8_context); // Handler for ExtensionMsg_WatchPages. void OnWatchPages(const std::vector<std::string>& css_selectors); diff --git a/chrome/renderer/extensions/context_menus_custom_bindings.cc b/chrome/renderer/extensions/context_menus_custom_bindings.cc index 7181a5d..a1746b6 100644 --- a/chrome/renderer/extensions/context_menus_custom_bindings.cc +++ b/chrome/renderer/extensions/context_menus_custom_bindings.cc @@ -22,8 +22,9 @@ v8::Handle<v8::Value> GetNextContextMenuId(const v8::Arguments& args) { namespace extensions { -ContextMenusCustomBindings::ContextMenusCustomBindings() - : ChromeV8Extension(NULL) { +ContextMenusCustomBindings::ContextMenusCustomBindings( + Dispatcher* dispatcher, v8::Handle<v8::Context> v8_context) + : ChromeV8Extension(dispatcher, v8_context) { RouteStaticFunction("GetNextContextMenuId", &GetNextContextMenuId); } diff --git a/chrome/renderer/extensions/context_menus_custom_bindings.h b/chrome/renderer/extensions/context_menus_custom_bindings.h index 368e852..b4c378c 100644 --- a/chrome/renderer/extensions/context_menus_custom_bindings.h +++ b/chrome/renderer/extensions/context_menus_custom_bindings.h @@ -12,7 +12,8 @@ namespace extensions { // Implements custom bindings for the contextMenus API. class ContextMenusCustomBindings : public ChromeV8Extension { public: - ContextMenusCustomBindings(); + ContextMenusCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> v8_context); }; } // extensions diff --git a/chrome/renderer/extensions/dispatcher.cc b/chrome/renderer/extensions/dispatcher.cc index 1c7ebc1..c757e83 100644 --- a/chrome/renderer/extensions/dispatcher.cc +++ b/chrome/renderer/extensions/dispatcher.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/string_piece.h" +#include "base/strings/string_split.h" #include "chrome/common/child_process_logging.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_version_info.h" @@ -15,6 +16,7 @@ #include "chrome/common/extensions/background_info.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_messages.h" +#include "chrome/common/extensions/features/feature.h" #include "chrome/common/extensions/manifest.h" #include "chrome/common/extensions/permissions/permission_set.h" #include "chrome/common/url_constants.h" @@ -24,6 +26,7 @@ #include "chrome/renderer/extensions/app_bindings.h" #include "chrome/renderer/extensions/app_runtime_custom_bindings.h" #include "chrome/renderer/extensions/app_window_custom_bindings.h" +#include "chrome/renderer/extensions/binding_generating_native_handler.h" #include "chrome/renderer/extensions/chrome_v8_context.h" #include "chrome/renderer/extensions/chrome_v8_extension.h" #include "chrome/renderer/extensions/content_watcher.h" @@ -40,7 +43,7 @@ #include "chrome/renderer/extensions/media_galleries_custom_bindings.h" #include "chrome/renderer/extensions/miscellaneous_bindings.h" #include "chrome/renderer/extensions/module_system.h" -#include "chrome/renderer/extensions/native_handler.h" +#include "chrome/renderer/extensions/object_backed_native_handler.h" #include "chrome/renderer/extensions/page_actions_custom_bindings.h" #include "chrome/renderer/extensions/page_capture_custom_bindings.h" #include "chrome/renderer/extensions/request_sender.h" @@ -92,24 +95,97 @@ static const char kEventDispatchFunction[] = "Event.dispatchEvent"; static const char kOnSuspendEvent[] = "runtime.onSuspend"; static const char kOnSuspendCanceledEvent[] = "runtime.onSuspendCanceled"; -class ChromeHiddenNativeHandler : public NativeHandler { +static v8::Handle<v8::Object> GetOrCreateChrome( + v8::Handle<v8::Context> context) { + v8::Handle<v8::String> chrome_string(v8::String::New("chrome")); + v8::Handle<v8::Object> global(context->Global()); + v8::Handle<v8::Value> chrome(global->Get(chrome_string)); + if (chrome.IsEmpty() || chrome->IsUndefined()) { + v8::Handle<v8::Object> chrome_object(v8::Object::New()); + global->Set(chrome_string, chrome_object); + return chrome_object; + } + CHECK(chrome->IsObject()); + return chrome->ToObject(); +} + +class SchemaRegistryNativeHandler : public ObjectBackedNativeHandler { public: - explicit ChromeHiddenNativeHandler(v8::Isolate* isolate) - : NativeHandler(isolate) { + SchemaRegistryNativeHandler(V8SchemaRegistry* registry, + v8::Handle<v8::Context> context) + : ObjectBackedNativeHandler(context), + registry_(registry) { + RouteFunction("GetSchema", + base::Bind(&SchemaRegistryNativeHandler::GetSchema, + base::Unretained(this))); + } + + private: + v8::Handle<v8::Value> GetSchema(const v8::Arguments& args) { + return registry_->GetSchema(*v8::String::AsciiValue(args[0])); + } + + V8SchemaRegistry* registry_; +}; + +class V8ContextNativeHandler : public ObjectBackedNativeHandler { + public: + explicit V8ContextNativeHandler(ChromeV8Context* context) + : ObjectBackedNativeHandler(context->v8_context()), + context_(context) { + RouteFunction("GetAvailability", + base::Bind(&V8ContextNativeHandler::GetAvailability, + base::Unretained(this))); + } + + private: + v8::Handle<v8::Value> GetAvailability(const v8::Arguments& args) { + CHECK_EQ(args.Length(), 1); + std::string api_name = *v8::String::AsciiValue(args[0]->ToString()); + Feature::Availability availability = context_->GetAvailability(api_name); + + v8::Handle<v8::Object> ret = v8::Object::New(); + ret->Set(v8::String::New("is_available"), + v8::Boolean::New(availability.is_available())); + ret->Set(v8::String::New("message"), + v8::String::New(availability.message().c_str())); + return ret; + } + + ChromeV8Context* context_; +}; + +class ChromeHiddenNativeHandler : public ObjectBackedNativeHandler { + public: + explicit ChromeHiddenNativeHandler(v8::Handle<v8::Context> context) + : ObjectBackedNativeHandler(context) { RouteFunction("GetChromeHidden", base::Bind(&ChromeHiddenNativeHandler::GetChromeHidden, base::Unretained(this))); } v8::Handle<v8::Value> GetChromeHidden(const v8::Arguments& args) { - return ChromeV8Context::GetOrCreateChromeHidden(v8::Context::GetCurrent()); + return ChromeV8Context::GetOrCreateChromeHidden(v8_context()); } }; -class PrintNativeHandler : public NativeHandler { +class ChromeNativeHandler : public ObjectBackedNativeHandler { public: - explicit PrintNativeHandler(v8::Isolate* isolate) - : NativeHandler(isolate) { + explicit ChromeNativeHandler(v8::Handle<v8::Context> context) + : ObjectBackedNativeHandler(context) { + RouteFunction("GetChrome", + base::Bind(&ChromeNativeHandler::GetChrome, base::Unretained(this))); + } + + v8::Handle<v8::Value> GetChrome(const v8::Arguments& args) { + return GetOrCreateChrome(v8_context()); + } +}; + +class PrintNativeHandler : public ObjectBackedNativeHandler { + public: + explicit PrintNativeHandler(v8::Handle<v8::Context> context) + : ObjectBackedNativeHandler(context) { RouteFunction("Print", base::Bind(&PrintNativeHandler::Print, base::Unretained(this))); @@ -130,8 +206,9 @@ class PrintNativeHandler : public NativeHandler { class LazyBackgroundPageNativeHandler : public ChromeV8Extension { public: - explicit LazyBackgroundPageNativeHandler(Dispatcher* dispatcher) - : ChromeV8Extension(dispatcher) { + LazyBackgroundPageNativeHandler(Dispatcher* dispatcher, + v8::Handle<v8::Context> context) + : ChromeV8Extension(dispatcher, context) { RouteFunction("IncrementKeepaliveCount", base::Bind(&LazyBackgroundPageNativeHandler::IncrementKeepaliveCount, base::Unretained(this))); @@ -141,7 +218,8 @@ class LazyBackgroundPageNativeHandler : public ChromeV8Extension { } v8::Handle<v8::Value> IncrementKeepaliveCount(const v8::Arguments& args) { - ChromeV8Context* context = dispatcher()->v8_context_set().GetCurrent(); + ChromeV8Context* context = + dispatcher()->v8_context_set().GetByV8Context(v8_context()); if (!context) return v8::Undefined(); RenderView* render_view = context->GetRenderView(); @@ -153,7 +231,8 @@ class LazyBackgroundPageNativeHandler : public ChromeV8Extension { } v8::Handle<v8::Value> DecrementKeepaliveCount(const v8::Arguments& args) { - ChromeV8Context* context = dispatcher()->v8_context_set().GetCurrent(); + ChromeV8Context* context = + dispatcher()->v8_context_set().GetByV8Context(v8_context()); if (!context) return v8::Undefined(); RenderView* render_view = context->GetRenderView(); @@ -178,14 +257,14 @@ class LazyBackgroundPageNativeHandler : public ChromeV8Extension { class ProcessInfoNativeHandler : public ChromeV8Extension { public: - explicit ProcessInfoNativeHandler( - Dispatcher* dispatcher, - const std::string& extension_id, - const std::string& context_type, - bool is_incognito_context, - int manifest_version, - bool send_request_disabled) - : ChromeV8Extension(dispatcher), + ProcessInfoNativeHandler(Dispatcher* dispatcher, + v8::Handle<v8::Context> context, + const std::string& extension_id, + const std::string& context_type, + bool is_incognito_context, + int manifest_version, + bool send_request_disabled) + : ChromeV8Extension(dispatcher, context), extension_id_(extension_id), context_type_(context_type), is_incognito_context_(is_incognito_context), @@ -241,13 +320,12 @@ class ProcessInfoNativeHandler : public ChromeV8Extension { bool send_request_disabled_; }; -class LoggingNativeHandler : public NativeHandler { +class LoggingNativeHandler : public ObjectBackedNativeHandler { public: - explicit LoggingNativeHandler(v8::Isolate* isolate) - : NativeHandler(isolate) { + explicit LoggingNativeHandler(v8::Handle<v8::Context> context) + : ObjectBackedNativeHandler(context) { RouteFunction("DCHECK", - base::Bind(&LoggingNativeHandler::Dcheck, - base::Unretained(this))); + base::Bind(&LoggingNativeHandler::Dcheck, base::Unretained(this))); } v8::Handle<v8::Value> Dcheck(const v8::Arguments& args) { @@ -273,7 +351,6 @@ class LoggingNativeHandler : public NativeHandler { } } DCHECK(check_value) << error_message; - LOG(WARNING) << error_message; return v8::Undefined(); } @@ -305,20 +382,6 @@ void InstallWebstoreBindings(ModuleSystem* module_system, "chromeHiddenWebstore"); } -static v8::Handle<v8::Object> GetOrCreateChrome( - v8::Handle<v8::Context> context) { - v8::Handle<v8::String> chrome_string(v8::String::New("chrome")); - v8::Handle<v8::Object> global(context->Global()); - v8::Handle<v8::Value> chrome(global->Get(chrome_string)); - if (chrome.IsEmpty() || chrome->IsUndefined()) { - v8::Handle<v8::Object> chrome_object(v8::Object::New()); - global->Set(chrome_string, chrome_object); - return chrome_object; - } - CHECK(chrome->IsObject()); - return chrome->ToObject(); -} - } // namespace Dispatcher::Dispatcher() @@ -339,7 +402,7 @@ Dispatcher::Dispatcher() } user_script_slave_.reset(new UserScriptSlave(&extensions_)); - request_sender_.reset(new RequestSender(this, &v8_context_set_)); + request_sender_.reset(new RequestSender(this)); PopulateSourceMap(); PopulateLazyBindingsMap(); } @@ -564,62 +627,136 @@ bool Dispatcher::AllowScriptExtension(WebFrame* frame, return true; } +v8::Handle<v8::Object> Dispatcher::GetOrCreateObject( + v8::Handle<v8::Object> object, + const std::string& field) { + v8::HandleScope handle_scope; + v8::Handle<v8::String> key = v8::String::New(field.c_str()); + // This little dance is for APIs that may be unavailable but have available + // children. For example, chrome.app can be unavailable, while + // chrome.app.runtime is available. The lazy getter for chrome.app must be + // deleted, so that there isn't an error when accessing chrome.app.runtime. + if (object->Has(key)) { + v8::Handle<v8::Value> value = object->Get(key); + if (value->IsObject()) + return handle_scope.Close(v8::Handle<v8::Object>::Cast(value)); + else + object->Delete(key); + } + + v8::Handle<v8::Object> new_object = v8::Object::New(); + object->Set(key, new_object); + return handle_scope.Close(new_object); +} + +void Dispatcher::RegisterSchemaGeneratedBindings( + ModuleSystem* module_system, + ChromeV8Context* context, + v8::Handle<v8::Context> v8_context) { + std::set<std::string> apis = + ExtensionAPI::GetSharedInstance()->GetAllAPINames(); + for (std::set<std::string>::iterator it = apis.begin(); + it != apis.end(); ++it) { + const std::string& api_name = *it; + + std::vector<std::string> split; + base::SplitString(api_name, '.', &split); + + v8::Handle<v8::Object> bind_object = GetOrCreateChrome(v8_context); + for (size_t i = 0; i < split.size() - 1; ++i) + bind_object = GetOrCreateObject(bind_object, split[i]); + + if (lazy_bindings_map_.find(api_name) != lazy_bindings_map_.end()) { + InstallBindings(module_system, v8_context, api_name); + } else if (!source_map_.Contains(api_name)) { + module_system->RegisterNativeHandler( + api_name, + scoped_ptr<NativeHandler>(new BindingGeneratingNativeHandler( + module_system, + api_name, + "binding"))); + module_system->SetNativeLazyField(bind_object, + split.back(), + api_name, + "binding"); + } else { + module_system->SetLazyField(bind_object, + split.back(), + api_name, + "binding"); + } + } +} + void Dispatcher::RegisterNativeHandlers(ModuleSystem* module_system, ChromeV8Context* context) { + v8::Handle<v8::Context> v8_context = context->v8_context(); + module_system->RegisterNativeHandler("event_bindings", - scoped_ptr<NativeHandler>(EventBindings::Get(this))); + scoped_ptr<NativeHandler>(EventBindings::Create(this, v8_context))); module_system->RegisterNativeHandler("miscellaneous_bindings", - scoped_ptr<NativeHandler>(MiscellaneousBindings::Get(this))); + scoped_ptr<NativeHandler>(MiscellaneousBindings::Get(this, v8_context))); module_system->RegisterNativeHandler("apiDefinitions", - scoped_ptr<NativeHandler>(new ApiDefinitionsNatives(this))); + scoped_ptr<NativeHandler>(new ApiDefinitionsNatives(this, context))); module_system->RegisterNativeHandler("sendRequest", scoped_ptr<NativeHandler>( - new SendRequestNatives(this, request_sender_.get()))); + new SendRequestNatives(this, request_sender_.get(), context))); module_system->RegisterNativeHandler("setIcon", scoped_ptr<NativeHandler>( - new SetIconNatives(this, request_sender_.get()))); - module_system->RegisterNativeHandler("contentWatcherNative", - content_watcher_->MakeNatives()); + new SetIconNatives(this, request_sender_.get(), context))); + module_system->RegisterNativeHandler( + "contentWatcherNative", + content_watcher_->MakeNatives(v8_context)); // Natives used by multiple APIs. module_system->RegisterNativeHandler("file_system_natives", - scoped_ptr<NativeHandler>(new FileSystemNatives())); + scoped_ptr<NativeHandler>(new FileSystemNatives(v8_context))); // Custom bindings. module_system->RegisterNativeHandler("app", scoped_ptr<NativeHandler>(new AppBindings(this, context))); module_system->RegisterNativeHandler("app_runtime", - scoped_ptr<NativeHandler>(new AppRuntimeCustomBindings())); + scoped_ptr<NativeHandler>( + new AppRuntimeCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("app_window", - scoped_ptr<NativeHandler>(new AppWindowCustomBindings(this))); + scoped_ptr<NativeHandler>( + new AppWindowCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("context_menus", - scoped_ptr<NativeHandler>(new ContextMenusCustomBindings())); + scoped_ptr<NativeHandler>( + new ContextMenusCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("extension", scoped_ptr<NativeHandler>( - new ExtensionCustomBindings(this))); + new ExtensionCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("sync_file_system", - scoped_ptr<NativeHandler>(new SyncFileSystemCustomBindings())); + scoped_ptr<NativeHandler>( + new SyncFileSystemCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("file_browser_handler", - scoped_ptr<NativeHandler>(new FileBrowserHandlerCustomBindings())); + scoped_ptr<NativeHandler>(new FileBrowserHandlerCustomBindings( + this, v8_context))); module_system->RegisterNativeHandler("file_browser_private", - scoped_ptr<NativeHandler>(new FileBrowserPrivateCustomBindings())); + scoped_ptr<NativeHandler>(new FileBrowserPrivateCustomBindings( + this, v8_context))); module_system->RegisterNativeHandler("i18n", - scoped_ptr<NativeHandler>(new I18NCustomBindings())); + scoped_ptr<NativeHandler>( + new I18NCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("mediaGalleries", - scoped_ptr<NativeHandler>(new MediaGalleriesCustomBindings())); + scoped_ptr<NativeHandler>( + new MediaGalleriesCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("page_actions", scoped_ptr<NativeHandler>( - new PageActionsCustomBindings(this))); + new PageActionsCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("page_capture", - scoped_ptr<NativeHandler>(new PageCaptureCustomBindings())); + scoped_ptr<NativeHandler>( + new PageCaptureCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("runtime", scoped_ptr<NativeHandler>(new RuntimeCustomBindings(this, context))); module_system->RegisterNativeHandler("tabs", - scoped_ptr<NativeHandler>(new TabsCustomBindings())); + scoped_ptr<NativeHandler>(new TabsCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("tts", - scoped_ptr<NativeHandler>(new TTSCustomBindings())); + scoped_ptr<NativeHandler>(new TTSCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("web_request", - scoped_ptr<NativeHandler>(new WebRequestCustomBindings())); + scoped_ptr<NativeHandler>( + new WebRequestCustomBindings(this, v8_context))); module_system->RegisterNativeHandler("webstore", scoped_ptr<NativeHandler>(new WebstoreBindings(this, context))); } @@ -628,11 +765,9 @@ void Dispatcher::PopulateSourceMap() { source_map_.RegisterSource("event_bindings", IDR_EVENT_BINDINGS_JS); source_map_.RegisterSource("miscellaneous_bindings", IDR_MISCELLANEOUS_BINDINGS_JS); - source_map_.RegisterSource("schema_generated_bindings", - IDR_SCHEMA_GENERATED_BINDINGS_JS); source_map_.RegisterSource("json", IDR_JSON_JS); source_map_.RegisterSource("json_schema", IDR_JSON_SCHEMA_JS); - source_map_.RegisterSource("apitest", IDR_EXTENSION_APITEST_JS); + source_map_.RegisterSource("test", IDR_TEST_CUSTOM_BINDINGS_JS); // Libraries. source_map_.RegisterSource("contentWatcher", IDR_CONTENT_WATCHER_JS); @@ -701,13 +836,16 @@ void Dispatcher::PopulateSourceMap() { source_map_.RegisterSource("webRequestInternal", IDR_WEB_REQUEST_INTERNAL_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("webstore", IDR_WEBSTORE_CUSTOM_BINDINGS_JS); + source_map_.RegisterSource("binding", IDR_BINDING_JS); // Platform app sources that are not API-specific.. source_map_.RegisterSource("tagWatcher", IDR_TAG_WATCHER_JS); - source_map_.RegisterSource("webview", IDR_WEB_VIEW_JS); - source_map_.RegisterSource("webview.experimental", + // Note: webView not webview so that this doesn't interfere with the + // chrome.webview API bindings. + source_map_.RegisterSource("webView", IDR_WEB_VIEW_JS); + source_map_.RegisterSource("webViewExperimental", IDR_WEB_VIEW_EXPERIMENTAL_JS); - source_map_.RegisterSource("denyWebview", IDR_WEB_VIEW_DENY_JS); + source_map_.RegisterSource("denyWebView", IDR_WEB_VIEW_DENY_JS); source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS); source_map_.RegisterSource("injectAppTitlebar", IDR_INJECT_APP_TITLEBAR_JS); } @@ -779,15 +917,22 @@ void Dispatcher::DidCreateScriptContext( RegisterNativeHandlers(module_system.get(), context); - v8::Isolate* isolate = v8_context->GetIsolate(); + module_system->RegisterNativeHandler("chrome", + scoped_ptr<NativeHandler>(new ChromeNativeHandler(v8_context))); module_system->RegisterNativeHandler("chrome_hidden", - scoped_ptr<NativeHandler>(new ChromeHiddenNativeHandler(isolate))); + scoped_ptr<NativeHandler>(new ChromeHiddenNativeHandler(v8_context))); module_system->RegisterNativeHandler("print", - scoped_ptr<NativeHandler>(new PrintNativeHandler(isolate))); + scoped_ptr<NativeHandler>(new PrintNativeHandler(v8_context))); module_system->RegisterNativeHandler("lazy_background_page", - scoped_ptr<NativeHandler>(new LazyBackgroundPageNativeHandler(this))); + scoped_ptr<NativeHandler>( + new LazyBackgroundPageNativeHandler(this, v8_context))); module_system->RegisterNativeHandler("logging", - scoped_ptr<NativeHandler>(new LoggingNativeHandler(isolate))); + scoped_ptr<NativeHandler>(new LoggingNativeHandler(v8_context))); + module_system->RegisterNativeHandler("schema_registry", + scoped_ptr<NativeHandler>( + new SchemaRegistryNativeHandler(v8_schema_registry(), v8_context))); + module_system->RegisterNativeHandler("v8_context", + scoped_ptr<NativeHandler>(new V8ContextNativeHandler(context))); int manifest_version = extension ? extension->manifest_version() : 1; bool send_request_disabled = @@ -795,7 +940,7 @@ void Dispatcher::DidCreateScriptContext( BackgroundInfo::HasLazyBackgroundPage(extension)); module_system->RegisterNativeHandler("process", scoped_ptr<NativeHandler>(new ProcessInfoNativeHandler( - this, context->GetExtensionID(), + this, v8_context, context->GetExtensionID(), context->GetContextTypeDescription(), ChromeRenderProcessObserver::is_incognito_process(), manifest_version, send_request_disabled))); @@ -811,25 +956,20 @@ void Dispatcher::DidCreateScriptContext( InstallBindings(module_system.get(), v8_context, "app"); InstallBindings(module_system.get(), v8_context, "webstore"); 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 - module_system->Require("miscellaneous_bindings"); - module_system->Require("schema_generated_bindings"); - module_system->Require("apitest"); // 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. - const std::set<std::string>& apis = context->GetAvailableExtensionAPIs(); - for (std::set<std::string>::const_iterator i = apis.begin(); - i != apis.end(); ++i) { - InstallBindings(module_system.get(), v8_context, *i); - } - + RegisterSchemaGeneratedBindings(module_system.get(), + context, + v8_context); break; } } @@ -839,11 +979,14 @@ void Dispatcher::DidCreateScriptContext( module_system->Require("platformApp"); if (context_type == Feature::BLESSED_EXTENSION_CONTEXT) { - bool has_permission = extension->HasAPIPermission(APIPermission::kWebView); - module_system->Require(has_permission ? "webview" : "denyWebview"); - if (has_permission && - Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV) { - module_system->Require("webview.experimental"); + // Note: setting up the WebView class here, not the chrome.webview API. + // The API will be automatically set up when first used. + if (extension->HasAPIPermission(APIPermission::kWebView)) { + module_system->Require("webView"); + if (Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV) + module_system->Require("webViewExperimental"); + } else { + module_system->Require("denyWebView"); } } @@ -885,6 +1028,8 @@ void Dispatcher::WillReleaseScriptContext( return; context->DispatchOnUnloadEvent(); + // TODO(kalman): add an invalidation observer interface to ChromeV8Context. + request_sender_->InvalidateContext(context); v8_context_set_.Remove(context); VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); @@ -1129,9 +1274,8 @@ void Dispatcher::OnExtensionResponse(int request_id, request_sender_->HandleResponse(request_id, success, response, error); } -bool Dispatcher::CheckCurrentContextAccessToExtensionAPI( - const std::string& function_name) const { - ChromeV8Context* context = v8_context_set().GetCurrent(); +bool Dispatcher::CheckContextAccessToExtensionAPI( + const std::string& function_name, ChromeV8Context* context) const { if (!context) { DLOG(ERROR) << "Not in a v8::Context"; return false; diff --git a/chrome/renderer/extensions/dispatcher.h b/chrome/renderer/extensions/dispatcher.h index b95f5b9..4f568b8 100644 --- a/chrome/renderer/extensions/dispatcher.h +++ b/chrome/renderer/extensions/dispatcher.h @@ -120,8 +120,8 @@ class Dispatcher : public content::RenderProcessObserver { // Checks that the current context contains an extension that has permission // to execute the specified function. If it does not, a v8 exception is thrown // and the method returns false. Otherwise returns true. - bool CheckCurrentContextAccessToExtensionAPI( - const std::string& function_name) const; + bool CheckContextAccessToExtensionAPI( + const std::string& function_name, ChromeV8Context* context) const; private: friend class RenderViewTest; @@ -189,6 +189,9 @@ class Dispatcher : public content::RenderProcessObserver { void RegisterNativeHandlers(ModuleSystem* module_system, ChromeV8Context* context); + void RegisterSchemaGeneratedBindings(ModuleSystem* module_system, + ChromeV8Context* context, + v8::Handle<v8::Context> v8_context); // Inserts static source code into |source_map_|. void PopulateSourceMap(); @@ -211,6 +214,11 @@ class Dispatcher : public content::RenderProcessObserver { int extension_group, const ExtensionURLInfo& url_info); + // Gets |field| from |object| or creates it as an empty object if it doesn't + // exist. + v8::Handle<v8::Object> GetOrCreateObject(v8::Handle<v8::Object> object, + const std::string& field); + // True if this renderer is running extensions. bool is_extension_process_; diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index 12435a5..bbf16f5 100644 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -72,8 +72,9 @@ base::LazyInstance<EventFilter> g_event_filter = LAZY_INSTANCE_INITIALIZER; // TODO(koz): Merge this into EventBindings. class ExtensionImpl : public ChromeV8Extension { public: - explicit ExtensionImpl(Dispatcher* dispatcher) - : ChromeV8Extension(dispatcher) { + explicit ExtensionImpl(Dispatcher* dispatcher, + v8::Handle<v8::Context> v8_context) + : ChromeV8Extension(dispatcher, v8_context) { RouteStaticFunction("AttachEvent", &AttachEvent); RouteStaticFunction("DetachEvent", &DetachEvent); RouteStaticFunction("AttachFilteredEvent", &AttachFilteredEvent); @@ -94,10 +95,10 @@ class ExtensionImpl : public ChromeV8Extension { std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); Dispatcher* dispatcher = self->dispatcher(); const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); - ChromeV8Context* context = context_set.GetCurrent(); + ChromeV8Context* context = context_set.GetByV8Context(self->v8_context()); CHECK(context); - if (!dispatcher->CheckCurrentContextAccessToExtensionAPI(event_name)) + if (!dispatcher->CheckContextAccessToExtensionAPI(event_name, context)) return v8::Undefined(); std::string extension_id = context->GetExtensionID(); @@ -111,7 +112,7 @@ class ExtensionImpl : public ChromeV8Extension { // This is called the first time the page has added a listener. Since // the background page is the only lazy page, we know this is the first // time this listener has been registered. - if (IsLazyBackgroundPage(context->extension())) { + if (IsLazyBackgroundPage(self->GetRenderView(), context->extension())) { content::RenderThread::Get()->Send( new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); } @@ -131,7 +132,7 @@ class ExtensionImpl : public ChromeV8Extension { ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); Dispatcher* dispatcher = self->dispatcher(); const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); - ChromeV8Context* context = context_set.GetCurrent(); + ChromeV8Context* context = context_set.GetByV8Context(self->v8_context()); if (!context) return v8::Undefined(); @@ -148,7 +149,8 @@ class ExtensionImpl : public ChromeV8Extension { // removed. If the context is the background page, and it removes the // last listener manually, then we assume that it is no longer interested // in being awakened for this event. - if (is_manual && IsLazyBackgroundPage(context->extension())) { + if (is_manual && IsLazyBackgroundPage(self->GetRenderView(), + context->extension())) { content::RenderThread::Get()->Send( new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name)); } @@ -169,14 +171,14 @@ class ExtensionImpl : public ChromeV8Extension { ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); Dispatcher* dispatcher = self->dispatcher(); const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); - ChromeV8Context* context = context_set.GetCurrent(); + ChromeV8Context* context = context_set.GetByV8Context(self->v8_context()); DCHECK(context); if (!context) return v8::Integer::New(-1); std::string event_name = *v8::String::AsciiValue(args[0]); // This method throws an exception if it returns false. - if (!dispatcher->CheckCurrentContextAccessToExtensionAPI(event_name)) + if (!dispatcher->CheckContextAccessToExtensionAPI(event_name, context)) return v8::Undefined(); std::string extension_id = context->GetExtensionID(); @@ -188,8 +190,8 @@ class ExtensionImpl : public ChromeV8Extension { content::V8ValueConverter::create()); base::DictionaryValue* filter_dict = NULL; - base::Value* filter_value = converter->FromV8Value(args[1]->ToObject(), - v8::Context::GetCurrent()); + base::Value* filter_value = + converter->FromV8Value(args[1]->ToObject(), context->v8_context()); if (!filter_value) return v8::Integer::New(-1); if (!filter_value->GetAsDictionary(&filter_dict)) { @@ -204,7 +206,8 @@ class ExtensionImpl : public ChromeV8Extension { // Only send IPCs the first time a filter gets added. if (AddFilter(event_name, extension_id, filter.get())) { - bool lazy = IsLazyBackgroundPage(context->extension()); + bool lazy = IsLazyBackgroundPage(self->GetRenderView(), + context->extension()); content::RenderThread::Get()->Send( new ExtensionHostMsg_AddFilteredListener(extension_id, event_name, *filter, lazy)); @@ -253,7 +256,7 @@ class ExtensionImpl : public ChromeV8Extension { ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); Dispatcher* dispatcher = self->dispatcher(); const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); - ChromeV8Context* context = context_set.GetCurrent(); + ChromeV8Context* context = context_set.GetByV8Context(self->v8_context()); if (!context) return v8::Undefined(); @@ -270,7 +273,8 @@ class ExtensionImpl : public ChromeV8Extension { // Only send IPCs the last time a filter gets removed. if (RemoveFilter(event_name, extension_id, event_matcher->value())) { - bool lazy = is_manual && IsLazyBackgroundPage(context->extension()); + bool lazy = is_manual && IsLazyBackgroundPage(self->GetRenderView(), + context->extension()); content::RenderThread::Get()->Send( new ExtensionHostMsg_RemoveFilteredListener(extension_id, event_name, *event_matcher->value(), @@ -311,11 +315,10 @@ class ExtensionImpl : public ChromeV8Extension { } private: - static bool IsLazyBackgroundPage(const Extension* extension) { - content::RenderView* render_view = GetCurrentRenderView(); + static bool IsLazyBackgroundPage(content::RenderView* render_view, + const Extension* extension) { if (!render_view) return false; - ExtensionHelper* helper = ExtensionHelper::Get(render_view); return (extension && BackgroundInfo::HasLazyBackgroundPage(extension) && helper->view_type() == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); @@ -331,8 +334,9 @@ class ExtensionImpl : public ChromeV8Extension { } // namespace // static -ChromeV8Extension* EventBindings::Get(Dispatcher* dispatcher) { - return new ExtensionImpl(dispatcher); +ChromeV8Extension* EventBindings::Create(Dispatcher* dispatcher, + v8::Handle<v8::Context> context) { + return new ExtensionImpl(dispatcher, context); } } // namespace extensions diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h index 9051dae..a263ad3 100644 --- a/chrome/renderer/extensions/event_bindings.h +++ b/chrome/renderer/extensions/event_bindings.h @@ -5,9 +5,7 @@ #ifndef CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_ #define CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_ -namespace v8 { -class Extension; -} +#include "v8/include/v8.h" namespace extensions { class ChromeV8Extension; @@ -17,7 +15,8 @@ class EventFilter; // This class deals with the javascript bindings related to Event objects. class EventBindings { public: - static ChromeV8Extension* Get(Dispatcher* dispatcher); + static ChromeV8Extension* Create(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); }; } // namespace extensions diff --git a/chrome/renderer/extensions/event_unittest.cc b/chrome/renderer/extensions/event_unittest.cc index edd47c0..8258b42 100644 --- a/chrome/renderer/extensions/event_unittest.cc +++ b/chrome/renderer/extensions/event_unittest.cc @@ -62,6 +62,9 @@ class EventUnittest : public ModuleSystemTest { OverrideNativeHandler("chrome_hidden", "var chromeHidden = {};" "exports.GetChromeHidden = function() { return chromeHidden; };"); + OverrideNativeHandler("chrome", + "var chrome = {};" + "exports.GetChrome = function() { return chrome; };"); OverrideNativeHandler("sendRequest", "exports.sendRequest = function() {};"); OverrideNativeHandler("apiDefinitions", @@ -79,9 +82,10 @@ TEST_F(EventUnittest, AddRemoveTwoListeners) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", "var assert = requireNative('assert');" - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var eventBindings = requireNative('event_bindings');" - "var myEvent = new event.Event('named-event');" + "var myEvent = new Event('named-event');" "var cb1 = function() {};" "var cb2 = function() {};" "myEvent.addListener(cb1);" @@ -97,10 +101,11 @@ TEST_F(EventUnittest, OnUnloadDetachesAllListeners) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", "var assert = requireNative('assert');" - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var eventBindings = requireNative('event_bindings');" "var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();" - "var myEvent = new event.Event('named-event');" + "var myEvent = new Event('named-event');" "var cb1 = function() {};" "var cb2 = function() {};" "myEvent.addListener(cb1);" @@ -114,10 +119,11 @@ TEST_F(EventUnittest, OnUnloadDetachesAllListenersEvenDupes) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", "var assert = requireNative('assert');" - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var eventBindings = requireNative('event_bindings');" "var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();" - "var myEvent = new event.Event('named-event');" + "var myEvent = new Event('named-event');" "var cb1 = function() {};" "myEvent.addListener(cb1);" "myEvent.addListener(cb1);" @@ -129,12 +135,13 @@ TEST_F(EventUnittest, OnUnloadDetachesAllListenersEvenDupes) { TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var eventOpts = {supportsRules: true};" "var assert = requireNative('assert');" "var caught = false;" "try {" - " var myEvent = new event.Event(undefined, undefined, eventOpts);" + " var myEvent = new Event(undefined, undefined, eventOpts);" "} catch (e) {" " caught = true;" "}" @@ -145,10 +152,11 @@ TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) { TEST_F(EventUnittest, NamedEventDispatch) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();" "var assert = requireNative('assert');" - "var e = new event.Event('myevent');" + "var e = new Event('myevent');" "var called = false;" "e.addListener(function() { called = true; });" "chromeHidden.Event.dispatchEvent('myevent', []);" @@ -159,9 +167,10 @@ TEST_F(EventUnittest, NamedEventDispatch) { TEST_F(EventUnittest, AddListenerWithFiltersThrowsErrorByDefault) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var assert = requireNative('assert');" - "var e = new event.Event('myevent');" + "var e = new Event('myevent');" "var filter = [{" " url: {hostSuffix: 'google.com'}," "}];" @@ -178,11 +187,12 @@ TEST_F(EventUnittest, AddListenerWithFiltersThrowsErrorByDefault) { TEST_F(EventUnittest, FilteredEventsAttachment) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var assert = requireNative('assert');" "var bindings = requireNative('event_bindings');" "var eventOpts = {supportsListeners: true, supportsFilters: true};" - "var e = new event.Event('myevent', undefined, eventOpts);" + "var e = new Event('myevent', undefined, eventOpts);" "var cb = function() {};" "var filters = {url: [{hostSuffix: 'google.com'}]};" "e.addListener(cb, filters);" @@ -195,11 +205,12 @@ TEST_F(EventUnittest, FilteredEventsAttachment) { TEST_F(EventUnittest, DetachFilteredEvent) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var assert = requireNative('assert');" "var bindings = requireNative('event_bindings');" "var eventOpts = {supportsListeners: true, supportsFilters: true};" - "var e = new event.Event('myevent', undefined, eventOpts);" + "var e = new Event('myevent', undefined, eventOpts);" "var cb1 = function() {};" "var cb2 = function() {};" "var filters = {url: [{hostSuffix: 'google.com'}]};" @@ -213,11 +224,12 @@ TEST_F(EventUnittest, DetachFilteredEvent) { TEST_F(EventUnittest, AttachAndRemoveSameFilteredEventListener) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var assert = requireNative('assert');" "var bindings = requireNative('event_bindings');" "var eventOpts = {supportsListeners: true, supportsFilters: true};" - "var e = new event.Event('myevent', undefined, eventOpts);" + "var e = new Event('myevent', undefined, eventOpts);" "var cb = function() {};" "var filters = {url: [{hostSuffix: 'google.com'}]};" "e.addListener(cb, filters);" @@ -233,10 +245,11 @@ TEST_F(EventUnittest, AttachAndRemoveSameFilteredEventListener) { TEST_F(EventUnittest, AddingFilterWithUrlFieldNotAListThrowsException) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var assert = requireNative('assert');" "var eventOpts = {supportsListeners: true, supportsFilters: true};" - "var e = new event.Event('myevent', undefined, eventOpts);" + "var e = new Event('myevent', undefined, eventOpts);" "var cb = function() {};" "var filters = {url: {hostSuffix: 'google.com'}};" "var caught = false;" @@ -252,10 +265,11 @@ TEST_F(EventUnittest, AddingFilterWithUrlFieldNotAListThrowsException) { TEST_F(EventUnittest, MaxListeners) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); RegisterModule("test", - "var event = require('event');" + "require('event');" + "var Event = requireNative('chrome').GetChrome().Event;" "var assert = requireNative('assert');" "var eventOpts = {supportsListeners: true, maxListeners: 1};" - "var e = new event.Event('myevent', undefined, eventOpts);" + "var e = new Event('myevent', undefined, eventOpts);" "var cb = function() {};" "var caught = false;" "try {" diff --git a/chrome/renderer/extensions/extension_custom_bindings.cc b/chrome/renderer/extensions/extension_custom_bindings.cc index 1cc5e099..2c290e52 100644 --- a/chrome/renderer/extensions/extension_custom_bindings.cc +++ b/chrome/renderer/extensions/extension_custom_bindings.cc @@ -26,8 +26,10 @@ namespace { } // namespace -ExtensionCustomBindings::ExtensionCustomBindings(Dispatcher* dispatcher) - : ChromeV8Extension(dispatcher) { +ExtensionCustomBindings::ExtensionCustomBindings( + Dispatcher* dispatcher, + v8::Handle<v8::Context> context) + : ChromeV8Extension(dispatcher, context) { RouteStaticFunction("GetExtensionViews", &GetExtensionViews); } @@ -72,7 +74,7 @@ v8::Handle<v8::Value> ExtensionCustomBindings::GetExtensionViews( ExtensionCustomBindings* v8_extension = GetFromArguments<ExtensionCustomBindings>(args); const Extension* extension = - v8_extension->GetExtensionForCurrentRenderView(); + v8_extension->GetExtensionForRenderView(); if (!extension) return v8::Undefined(); diff --git a/chrome/renderer/extensions/extension_custom_bindings.h b/chrome/renderer/extensions/extension_custom_bindings.h index 01d27df3..5123a6c 100644 --- a/chrome/renderer/extensions/extension_custom_bindings.h +++ b/chrome/renderer/extensions/extension_custom_bindings.h @@ -13,7 +13,8 @@ class Dispatcher; // Implements custom bindings for the extension API. class ExtensionCustomBindings : public ChromeV8Extension { public: - explicit ExtensionCustomBindings(Dispatcher* dispatcher); + explicit ExtensionCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); private: static v8::Handle<v8::Value> GetExtensionViews(const v8::Arguments& args); diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc index 6e063ce..0e50e8a 100644 --- a/chrome/renderer/extensions/extension_helper.cc +++ b/chrome/renderer/extensions/extension_helper.cc @@ -510,8 +510,7 @@ void ExtensionHelper::DidDownloadApplicationIcon(ImageResourceFetcher* fetcher, if (current_size > kMaxIconSize - actual_icon_size) { AddMessageToRootConsole( content::CONSOLE_MESSAGE_LEVEL_ERROR, - ASCIIToUTF16("Icons are too large. " - "Maximum total size for app icons is 128 KB.")); + "Icons are too large. Maximum total size for app icons is 128 KB."); return; } actual_icon_size += current_size; @@ -523,6 +522,11 @@ void ExtensionHelper::DidDownloadApplicationIcon(ImageResourceFetcher* fetcher, } void ExtensionHelper::AddMessageToRootConsole(ConsoleMessageLevel level, + const std::string& message) { + AddMessageToRootConsole(level, ASCIIToUTF16(message)); +} + +void ExtensionHelper::AddMessageToRootConsole(ConsoleMessageLevel level, const string16& message) { if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { WebConsoleMessage::Level target_level = WebConsoleMessage::LevelLog; diff --git a/chrome/renderer/extensions/extension_helper.h b/chrome/renderer/extensions/extension_helper.h index 9e7d21e..4a6b0b9 100644 --- a/chrome/renderer/extensions/extension_helper.h +++ b/chrome/renderer/extensions/extension_helper.h @@ -64,6 +64,10 @@ class ExtensionHelper int browser_window_id() const { return browser_window_id_; } chrome::ViewType view_type() const { return view_type_; } + // Helper to add a logging message to the root frame's console. + void AddMessageToRootConsole(content::ConsoleMessageLevel level, + const std::string& message); + private: // RenderViewObserver implementation. virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; @@ -112,7 +116,7 @@ class ExtensionHelper void DidDownloadApplicationIcon(webkit_glue::ImageResourceFetcher* fetcher, const SkBitmap& image); - // Helper to add an logging message to the root frame's console. + // Helper to add a logging message to the root frame's console. void AddMessageToRootConsole(content::ConsoleMessageLevel level, const string16& message); diff --git a/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc b/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc index 78800db..9462f25 100644 --- a/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc +++ b/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc @@ -13,9 +13,19 @@ #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" -namespace { +namespace extensions { + +FileBrowserHandlerCustomBindings::FileBrowserHandlerCustomBindings( + Dispatcher* dispatcher, v8::Handle<v8::Context> context) + : ChromeV8Extension(dispatcher, context) { + RouteFunction( + "GetExternalFileEntry", + base::Bind(&FileBrowserHandlerCustomBindings::GetExternalFileEntry, + base::Unretained(this))); +} -v8::Handle<v8::Value> GetExternalFileEntry(const v8::Arguments& args) { +v8::Handle<v8::Value> FileBrowserHandlerCustomBindings::GetExternalFileEntry( + const v8::Arguments& args) { // TODO(zelidrag): Make this magic work on other platforms when file browser // matures enough on ChromeOS. #if defined(OS_CHROMEOS) @@ -33,7 +43,8 @@ v8::Handle<v8::Value> GetExternalFileEntry(const v8::Arguments& args) { v8::String::New("fileFullPath")))); bool is_directory = file_def->Get(v8::String::New("fileIsDirectory"))->ToBoolean()->Value(); - WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext(); + WebKit::WebFrame* webframe = + WebKit::WebFrame::frameForContext(v8_context()); return webframe->createFileEntry( WebKit::WebFileSystem::TypeExternal, WebKit::WebString::fromUTF8(file_system_name.c_str()), @@ -45,14 +56,4 @@ v8::Handle<v8::Value> GetExternalFileEntry(const v8::Arguments& args) { #endif } -} // namespace - -namespace extensions { - -FileBrowserHandlerCustomBindings::FileBrowserHandlerCustomBindings() - : ChromeV8Extension(NULL) { - RouteStaticFunction("GetExternalFileEntry", &GetExternalFileEntry); -} - - } // namespace extensions diff --git a/chrome/renderer/extensions/file_browser_handler_custom_bindings.h b/chrome/renderer/extensions/file_browser_handler_custom_bindings.h index a34769d..7e9948e 100644 --- a/chrome/renderer/extensions/file_browser_handler_custom_bindings.h +++ b/chrome/renderer/extensions/file_browser_handler_custom_bindings.h @@ -13,9 +13,12 @@ namespace extensions { // Custom bindings for the fileBrowserHandler API. class FileBrowserHandlerCustomBindings : public ChromeV8Extension { public: - FileBrowserHandlerCustomBindings(); + FileBrowserHandlerCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); private: + v8::Handle<v8::Value> GetExternalFileEntry(const v8::Arguments& args); + DISALLOW_COPY_AND_ASSIGN(FileBrowserHandlerCustomBindings); }; diff --git a/chrome/renderer/extensions/file_browser_private_custom_bindings.cc b/chrome/renderer/extensions/file_browser_private_custom_bindings.cc index 355e838..ea72c0b 100644 --- a/chrome/renderer/extensions/file_browser_private_custom_bindings.cc +++ b/chrome/renderer/extensions/file_browser_private_custom_bindings.cc @@ -13,9 +13,18 @@ #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" -namespace { +namespace extensions { + +FileBrowserPrivateCustomBindings::FileBrowserPrivateCustomBindings( + Dispatcher* dispatcher, v8::Handle<v8::Context> context) + : ChromeV8Extension(dispatcher, context) { + RouteFunction( + "GetLocalFileSystem", + base::Bind(&FileBrowserPrivateCustomBindings::GetLocalFileSystem, + base::Unretained(this))); +} -static v8::Handle<v8::Value> GetLocalFileSystem( +v8::Handle<v8::Value> FileBrowserPrivateCustomBindings::GetLocalFileSystem( const v8::Arguments& args) { DCHECK(args.Length() == 2); DCHECK(args[0]->IsString()); @@ -23,7 +32,7 @@ static v8::Handle<v8::Value> GetLocalFileSystem( std::string name(*v8::String::Utf8Value(args[0])); std::string path(*v8::String::Utf8Value(args[1])); - WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext(); + WebKit::WebFrame* webframe = WebKit::WebFrame::frameForContext(v8_context()); DCHECK(webframe); return webframe->createFileSystem( WebKit::WebFileSystem::TypeExternal, @@ -31,13 +40,4 @@ static v8::Handle<v8::Value> GetLocalFileSystem( WebKit::WebString::fromUTF8(path.c_str())); } -} // namespace - -namespace extensions { - -FileBrowserPrivateCustomBindings::FileBrowserPrivateCustomBindings() - : ChromeV8Extension(NULL) { - RouteStaticFunction("GetLocalFileSystem", &GetLocalFileSystem); -} - } // namespace extensions diff --git a/chrome/renderer/extensions/file_browser_private_custom_bindings.h b/chrome/renderer/extensions/file_browser_private_custom_bindings.h index f1fce16..9b3e84c 100644 --- a/chrome/renderer/extensions/file_browser_private_custom_bindings.h +++ b/chrome/renderer/extensions/file_browser_private_custom_bindings.h @@ -13,7 +13,10 @@ namespace extensions { // Custom bindings for the fileBrowserPrivate API. class FileBrowserPrivateCustomBindings : public ChromeV8Extension { public: - FileBrowserPrivateCustomBindings(); + FileBrowserPrivateCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); + + v8::Handle<v8::Value> GetLocalFileSystem(const v8::Arguments& args); private: DISALLOW_COPY_AND_ASSIGN(FileBrowserPrivateCustomBindings); diff --git a/chrome/renderer/extensions/file_system_natives.cc b/chrome/renderer/extensions/file_system_natives.cc index 81fbf92..7812fe0 100644 --- a/chrome/renderer/extensions/file_system_natives.cc +++ b/chrome/renderer/extensions/file_system_natives.cc @@ -18,14 +18,23 @@ #include "webkit/fileapi/file_system_types.h" #include "webkit/fileapi/file_system_util.h" -namespace { +namespace extensions { + +FileSystemNatives::FileSystemNatives(v8::Handle<v8::Context> context) + : ObjectBackedNativeHandler(context) { + RouteFunction("GetFileEntry", + base::Bind(&FileSystemNatives::GetFileEntry, base::Unretained(this))); + RouteFunction("GetIsolatedFileSystem", + base::Bind(&FileSystemNatives::GetIsolatedFileSystem, + base::Unretained(this))); +} -static v8::Handle<v8::Value> GetIsolatedFileSystem( +v8::Handle<v8::Value> FileSystemNatives::GetIsolatedFileSystem( const v8::Arguments& args) { DCHECK(args.Length() == 1 || args.Length() == 2); DCHECK(args[0]->IsString()); std::string file_system_id(*v8::String::Utf8Value(args[0])); - WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext(); + WebKit::WebFrame* webframe = WebKit::WebFrame::frameForContext(v8_context()); DCHECK(webframe); GURL context_url = @@ -54,7 +63,8 @@ static v8::Handle<v8::Value> GetIsolatedFileSystem( WebKit::WebString::fromUTF8(root)); } -static v8::Handle<v8::Value> GetFileEntry(const v8::Arguments& args) { +v8::Handle<v8::Value> FileSystemNatives::GetFileEntry( + const v8::Arguments& args) { DCHECK(args.Length() == 5); DCHECK(args[0]->IsString()); std::string type_string = *v8::String::Utf8Value(args[0]->ToString()); @@ -77,7 +87,7 @@ static v8::Handle<v8::Value> GetFileEntry(const v8::Arguments& args) { DCHECK(args[4]->IsBoolean()); bool is_directory = args[4]->BooleanValue(); - WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext(); + WebKit::WebFrame* webframe = WebKit::WebFrame::frameForContext(v8_context()); DCHECK(webframe); return webframe->createFileEntry( type, @@ -87,14 +97,4 @@ static v8::Handle<v8::Value> GetFileEntry(const v8::Arguments& args) { is_directory); } -} // namespace - -namespace extensions { - -FileSystemNatives::FileSystemNatives() - : ChromeV8Extension(NULL) { - RouteStaticFunction("GetFileEntry", &GetFileEntry); - RouteStaticFunction("GetIsolatedFileSystem", &GetIsolatedFileSystem); -} - } // namespace extensions diff --git a/chrome/renderer/extensions/file_system_natives.h b/chrome/renderer/extensions/file_system_natives.h index c57a9a9..6768eae 100644 --- a/chrome/renderer/extensions/file_system_natives.h +++ b/chrome/renderer/extensions/file_system_natives.h @@ -6,16 +6,19 @@ #define CHROME_RENDERER_EXTENSIONS_FILE_SYSTEM_NATIVES_H_ #include "base/compiler_specific.h" -#include "chrome/renderer/extensions/chrome_v8_extension.h" +#include "chrome/renderer/extensions/object_backed_native_handler.h" namespace extensions { // Custom bindings for the nativeFileSystem API. -class FileSystemNatives : public ChromeV8Extension { +class FileSystemNatives : public ObjectBackedNativeHandler { public: - FileSystemNatives(); + explicit FileSystemNatives(v8::Handle<v8::Context> context); private: + v8::Handle<v8::Value> GetFileEntry(const v8::Arguments& args); + v8::Handle<v8::Value> GetIsolatedFileSystem(const v8::Arguments& args); + DISALLOW_COPY_AND_ASSIGN(FileSystemNatives); }; diff --git a/chrome/renderer/extensions/i18n_custom_bindings.cc b/chrome/renderer/extensions/i18n_custom_bindings.cc index 7dfcb0f..44ee27b 100644 --- a/chrome/renderer/extensions/i18n_custom_bindings.cc +++ b/chrome/renderer/extensions/i18n_custom_bindings.cc @@ -12,14 +12,17 @@ namespace extensions { -I18NCustomBindings::I18NCustomBindings() - : ChromeV8Extension(NULL) { +I18NCustomBindings::I18NCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context) + : ChromeV8Extension(dispatcher, context) { RouteStaticFunction("GetL10nMessage", &GetL10nMessage); } // static v8::Handle<v8::Value> I18NCustomBindings::GetL10nMessage( const v8::Arguments& args) { + I18NCustomBindings* self = GetFromArguments<I18NCustomBindings>(args); + if (args.Length() != 3 || !args[0]->IsString()) { NOTREACHED() << "Bad arguments"; return v8::Undefined(); @@ -38,7 +41,7 @@ v8::Handle<v8::Value> I18NCustomBindings::GetL10nMessage( if (!l10n_messages) { // Get the current RenderView so that we can send a routed IPC message // from the correct source. - content::RenderView* renderview = GetCurrentRenderView(); + content::RenderView* renderview = self->GetRenderView(); if (!renderview) return v8::Undefined(); diff --git a/chrome/renderer/extensions/i18n_custom_bindings.h b/chrome/renderer/extensions/i18n_custom_bindings.h index beab173..faa9db7 100644 --- a/chrome/renderer/extensions/i18n_custom_bindings.h +++ b/chrome/renderer/extensions/i18n_custom_bindings.h @@ -12,7 +12,7 @@ namespace extensions { // Implements custom bindings for the i18n API. class I18NCustomBindings : public ChromeV8Extension { public: - I18NCustomBindings(); + I18NCustomBindings(Dispatcher* dispatcher, v8::Handle<v8::Context> context); private: static v8::Handle<v8::Value> GetL10nMessage(const v8::Arguments& args); diff --git a/chrome/renderer/extensions/json_schema_unittest.cc b/chrome/renderer/extensions/json_schema_unittest.cc index 08c12d2..32c7fab 100644 --- a/chrome/renderer/extensions/json_schema_unittest.cc +++ b/chrome/renderer/extensions/json_schema_unittest.cc @@ -27,12 +27,17 @@ class JsonSchemaTest : public V8UnitTest { std::string code = ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_JSON_SCHEMA_JS).as_string(); - // json_schema.js expects to have requireNative() defined. + // json_schema.js expects to have require() and requireNative() defined. ExecuteScriptInContext( "function requireNative(id) {" " return {" " GetChromeHidden: function() { return {}; }," " };" + "}" + "function require(id) {" + " return {" + " loadRefDependency: function(foo) { return {}; }," + " };" "}", "test-code"); ExecuteScriptInContext(code, kJsonSchema); diff --git a/chrome/renderer/extensions/media_galleries_custom_bindings.cc b/chrome/renderer/extensions/media_galleries_custom_bindings.cc index 4a6ede5..1e65f2e 100644 --- a/chrome/renderer/extensions/media_galleries_custom_bindings.cc +++ b/chrome/renderer/extensions/media_galleries_custom_bindings.cc @@ -16,8 +16,9 @@ namespace extensions { -MediaGalleriesCustomBindings::MediaGalleriesCustomBindings() - : ChromeV8Extension(NULL) { +MediaGalleriesCustomBindings::MediaGalleriesCustomBindings( + Dispatcher* dispatcher, v8::Handle<v8::Context> v8_context) + : ChromeV8Extension(dispatcher, v8_context) { RouteFunction( "GetMediaFileSystemObject", base::Bind(&MediaGalleriesCustomBindings::GetMediaFileSystemObject, diff --git a/chrome/renderer/extensions/media_galleries_custom_bindings.h b/chrome/renderer/extensions/media_galleries_custom_bindings.h index 92bd4b9..736c04e 100644 --- a/chrome/renderer/extensions/media_galleries_custom_bindings.h +++ b/chrome/renderer/extensions/media_galleries_custom_bindings.h @@ -12,7 +12,8 @@ namespace extensions { // Implements custom bindings for the media galleries API. class MediaGalleriesCustomBindings : public ChromeV8Extension { public: - MediaGalleriesCustomBindings(); + MediaGalleriesCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); private: // FileSystemObject GetMediaFileSystem(string file_system_url): construct diff --git a/chrome/renderer/extensions/miscellaneous_bindings.cc b/chrome/renderer/extensions/miscellaneous_bindings.cc index da4c58d..37c1020 100644 --- a/chrome/renderer/extensions/miscellaneous_bindings.cc +++ b/chrome/renderer/extensions/miscellaneous_bindings.cc @@ -68,8 +68,9 @@ const char kReceivingEndDoesntExistError[] = class ExtensionImpl : public extensions::ChromeV8Extension { public: - explicit ExtensionImpl(extensions::Dispatcher* dispatcher) - : extensions::ChromeV8Extension(dispatcher) { + explicit ExtensionImpl(extensions::Dispatcher* dispatcher, + v8::Handle<v8::Context> context) + : extensions::ChromeV8Extension(dispatcher, context) { RouteStaticFunction("CloseChannel", &CloseChannel); RouteStaticFunction("PortAddRef", &PortAddRef); RouteStaticFunction("PortRelease", &PortRelease); @@ -81,7 +82,8 @@ class ExtensionImpl : public extensions::ChromeV8Extension { // Sends a message along the given channel. static v8::Handle<v8::Value> PostMessage(const v8::Arguments& args) { - content::RenderView* renderview = GetCurrentRenderView(); + ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); + content::RenderView* renderview = self->GetRenderView(); if (!renderview) return v8::Undefined(); @@ -181,8 +183,10 @@ class ExtensionImpl : public extensions::ChromeV8Extension { namespace extensions { -ChromeV8Extension* MiscellaneousBindings::Get(Dispatcher* dispatcher) { - return new ExtensionImpl(dispatcher); +ChromeV8Extension* MiscellaneousBindings::Get( + Dispatcher* dispatcher, + v8::Handle<v8::Context> context) { + return new ExtensionImpl(dispatcher, context); } // static diff --git a/chrome/renderer/extensions/miscellaneous_bindings.h b/chrome/renderer/extensions/miscellaneous_bindings.h index 0991af3..912d32d 100644 --- a/chrome/renderer/extensions/miscellaneous_bindings.h +++ b/chrome/renderer/extensions/miscellaneous_bindings.h @@ -30,7 +30,8 @@ class Dispatcher; class MiscellaneousBindings { public: // Creates an instance of the extension. - static ChromeV8Extension* Get(Dispatcher* dispatcher); + static ChromeV8Extension* Get(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); // Dispatches the Port.onConnect content script messaging event to some // contexts in |contexts|. If |restrict_to_render_view| is specified, only diff --git a/chrome/renderer/extensions/module_system.cc b/chrome/renderer/extensions/module_system.cc index 24982cc..5615c93 100644 --- a/chrome/renderer/extensions/module_system.cc +++ b/chrome/renderer/extensions/module_system.cc @@ -5,7 +5,13 @@ #include "chrome/renderer/extensions/module_system.h" #include "base/bind.h" +#include "base/debug/alias.h" #include "base/stl_util.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "chrome/common/extensions/extension_messages.h" +#include "chrome/renderer/extensions/chrome_v8_context.h" +#include "content/public/renderer/render_view.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedMicrotaskSuppression.h" namespace { @@ -21,26 +27,32 @@ namespace extensions { ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context, SourceMap* source_map) - : NativeHandler(context->GetIsolate()), - context_(context), + : ObjectBackedNativeHandler(context), source_map_(source_map), natives_enabled_(0) { RouteFunction("require", base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this))); RouteFunction("requireNative", - base::Bind(&ModuleSystem::GetNative, base::Unretained(this))); + base::Bind(&ModuleSystem::RequireNative, base::Unretained(this))); - v8::Handle<v8::Object> global(context_->Global()); + v8::Handle<v8::Object> global(context->Global()); global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New()); global->SetHiddenValue(v8::String::New(kModuleSystem), v8::External::New(this)); } ModuleSystem::~ModuleSystem() { - v8::HandleScope handle_scope; - // Deleting this value here prevents future lazy field accesses from - // referencing ModuleSystem after it has been freed. - context_->Global()->DeleteHiddenValue(v8::String::New(kModuleSystem)); + Invalidate(); +} + +void ModuleSystem::Invalidate() { + if (!is_valid()) + return; + for (NativeHandlerMap::iterator it = native_handler_map_.begin(); + it != native_handler_map_.end(); ++it) { + it->second->Invalidate(); + } + ObjectBackedNativeHandler::Invalidate(); } ModuleSystem::NativesEnabledScope::NativesEnabledScope( @@ -54,16 +66,6 @@ ModuleSystem::NativesEnabledScope::~NativesEnabledScope() { CHECK_GE(module_system_->natives_enabled_, 0); } -// static -bool ModuleSystem::IsPresentInCurrentContext() { - v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global()); - if (global.IsEmpty()) - return false; - v8::Handle<v8::Value> module_system = - global->GetHiddenValue(v8::String::New(kModuleSystem)); - return !module_system.IsEmpty() && !module_system->IsUndefined(); -} - void ModuleSystem::HandleException(const v8::TryCatch& try_catch) { DumpException(try_catch); if (exception_handler_.get()) @@ -71,13 +73,10 @@ void ModuleSystem::HandleException(const v8::TryCatch& try_catch) { } // static -void ModuleSystem::DumpException(const v8::TryCatch& try_catch) { - v8::HandleScope handle_scope; - +std::string ModuleSystem::CreateExceptionString(const v8::TryCatch& try_catch) { v8::Handle<v8::Message> message(try_catch.Message()); if (message.IsEmpty()) { - LOG(ERROR) << "try_catch has no message"; - return; + return "try_catch has no message"; } std::string resource_name = "<unknown resource>"; @@ -90,6 +89,16 @@ void ModuleSystem::DumpException(const v8::TryCatch& try_catch) { if (!message->Get().IsEmpty()) error_message = *v8::String::Utf8Value(message->Get()); + return base::StringPrintf("%s:%d: %s", + resource_name.c_str(), + message->GetLineNumber(), + error_message.c_str()); +} + +// static +void ModuleSystem::DumpException(const v8::TryCatch& try_catch) { + v8::HandleScope handle_scope; + std::string stack_trace = "<stack trace unavailable>"; if (!try_catch.StackTrace().IsEmpty()) { v8::String::Utf8Value stack_value(try_catch.StackTrace()); @@ -99,14 +108,13 @@ void ModuleSystem::DumpException(const v8::TryCatch& try_catch) { stack_trace = "<could not convert stack trace to string>"; } - LOG(ERROR) << "[" << resource_name << "(" << message->GetLineNumber() << ")] " - << error_message - << "{" << stack_trace << "}"; + LOG(ERROR) << CreateExceptionString(try_catch) << "{" << stack_trace << "}"; } -void ModuleSystem::Require(const std::string& module_name) { +v8::Handle<v8::Value> ModuleSystem::Require(const std::string& module_name) { v8::HandleScope handle_scope; - RequireForJsInner(v8::String::New(module_name.c_str())); + return handle_scope.Close( + RequireForJsInner(v8::String::New(module_name.c_str()))); } v8::Handle<v8::Value> ModuleSystem::RequireForJs(const v8::Arguments& args) { @@ -118,7 +126,7 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJs(const v8::Arguments& args) { v8::Handle<v8::Value> ModuleSystem::RequireForJsInner( v8::Handle<v8::String> module_name) { v8::HandleScope handle_scope; - v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global()); + v8::Handle<v8::Object> global(v8_context()->Global()); v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast( global->GetHiddenValue(v8::String::New(kModulesField)))); v8::Handle<v8::Value> exports(modules->Get(module_name)); @@ -158,10 +166,11 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner( return handle_scope.Close(exports); } -void ModuleSystem::CallModuleMethod(const std::string& module_name, - const std::string& method_name) { +v8::Local<v8::Value> ModuleSystem::CallModuleMethod( + const std::string& module_name, + const std::string& method_name) { std::vector<v8::Handle<v8::Value> > args; - CallModuleMethod(module_name, method_name, &args); + return CallModuleMethod(module_name, method_name, &args); } v8::Local<v8::Value> ModuleSystem::CallModuleMethod( @@ -181,8 +190,7 @@ v8::Local<v8::Value> ModuleSystem::CallModuleMethod( return v8::Local<v8::Value>(); v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value); - // TODO(jeremya/koz): refer to context_ here, not the current context. - v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global()); + v8::Handle<v8::Object> global(v8_context()->Global()); v8::Local<v8::Value> result; { WebKit::WebScopedMicrotaskSuppression suppression; @@ -195,6 +203,10 @@ v8::Local<v8::Value> ModuleSystem::CallModuleMethod( return handle_scope.Close(result); } +bool ModuleSystem::HasNativeHandler(const std::string& name) { + return native_handler_map_.find(name) != native_handler_map_.end(); +} + void ModuleSystem::RegisterNativeHandler(const std::string& name, scoped_ptr<NativeHandler> native_handler) { native_handler_map_[name] = @@ -211,13 +223,31 @@ void ModuleSystem::RunString(const std::string& code, const std::string& name) { } // static +v8::Handle<v8::Value> ModuleSystem::NativeLazyFieldGetter( + v8::Local<v8::String> property, const v8::AccessorInfo& info) { + return LazyFieldGetterInner(property, + info, + &ModuleSystem::RequireNativeFromString); +} + +// static v8::Handle<v8::Value> ModuleSystem::LazyFieldGetter( v8::Local<v8::String> property, const v8::AccessorInfo& info) { + return LazyFieldGetterInner(property, info, &ModuleSystem::Require); +} + +// static +v8::Handle<v8::Value> ModuleSystem::LazyFieldGetterInner( + v8::Local<v8::String> property, + const v8::AccessorInfo& info, + RequireFunction require_function) { CHECK(!info.Data().IsEmpty()); CHECK(info.Data()->IsObject()); v8::HandleScope handle_scope; v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data()); - v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global()); + // This context should be the same as v8_context_, but this method is static. + v8::Handle<v8::Context> context = parameters->CreationContext(); + v8::Handle<v8::Object> global(context->Global()); v8::Handle<v8::Value> module_system_value = global->GetHiddenValue(v8::String::New(kModuleSystem)); if (module_system_value.IsEmpty() || module_system_value->IsUndefined()) { @@ -227,38 +257,81 @@ v8::Handle<v8::Value> ModuleSystem::LazyFieldGetter( ModuleSystem* module_system = static_cast<ModuleSystem*>( v8::Handle<v8::External>::Cast(module_system_value)->Value()); - v8::Handle<v8::Object> module; - { - NativesEnabledScope scope(module_system); - module = v8::Handle<v8::Object>::Cast(module_system->RequireForJsInner( - parameters->Get(v8::String::New(kModuleName))->ToString())); + std::string name = *v8::String::AsciiValue( + parameters->Get(v8::String::New(kModuleName))->ToString()); + + // Switch to our v8 context because we need functions created while running + // the require()d module to belong to our context, not the current one. + v8::Context::Scope context_scope(context); + NativesEnabledScope natives_enabled_scope(module_system); + + v8::TryCatch try_catch; + v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast( + (module_system->*require_function)(name)); + if (try_catch.HasCaught()) { + module_system->HandleException(try_catch); + return handle_scope.Close(v8::Handle<v8::Value>()); } + if (module.IsEmpty()) return handle_scope.Close(v8::Handle<v8::Value>()); v8::Handle<v8::String> field = parameters->Get(v8::String::New(kModuleField))->ToString(); - return handle_scope.Close(module->Get(field)); + // http://crbug.com/179741. + std::string field_name = *v8::String::AsciiValue(field); + char stack_debug[64]; + base::debug::Alias(&stack_debug); + base::snprintf(stack_debug, arraysize(stack_debug), + "%s.%s", name.c_str(), field_name.c_str()); + + v8::Local<v8::Value> new_field = module->Get(field); + v8::Handle<v8::Object> object = info.This(); + // Delete the getter and set this field to |new_field| so the same object is + // returned every time a certain API is accessed. + // CHECK is for http://crbug.com/179741. + CHECK(!new_field.IsEmpty()) << "Empty require " << name << "." << field_name; + if (!new_field->IsUndefined()) { + object->Delete(property); + object->Set(property, new_field); + } + return handle_scope.Close(new_field); } void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object, const std::string& field, const std::string& module_name, const std::string& module_field) { + SetLazyField(object, field, module_name, module_field, + &ModuleSystem::LazyFieldGetter); +} + +void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object, + const std::string& field, + const std::string& module_name, + const std::string& module_field, + v8::AccessorGetter getter) { v8::HandleScope handle_scope; v8::Handle<v8::Object> parameters = v8::Object::New(); parameters->Set(v8::String::New(kModuleName), v8::String::New(module_name.c_str())); parameters->Set(v8::String::New(kModuleField), v8::String::New(module_field.c_str())); - object->SetAccessor(v8::String::New(field.c_str()), - &ModuleSystem::LazyFieldGetter, + getter, NULL, parameters); } +void ModuleSystem::SetNativeLazyField(v8::Handle<v8::Object> object, + const std::string& field, + const std::string& module_name, + const std::string& module_field) { + SetLazyField(object, field, module_name, module_field, + &ModuleSystem::NativeLazyFieldGetter); +} + v8::Handle<v8::Value> ModuleSystem::RunString(v8::Handle<v8::String> code, v8::Handle<v8::String> name) { v8::HandleScope handle_scope; @@ -288,13 +361,18 @@ v8::Handle<v8::Value> ModuleSystem::GetSource( return handle_scope.Close(source_map_->GetSource(module_name)); } -v8::Handle<v8::Value> ModuleSystem::GetNative(const v8::Arguments& args) { +v8::Handle<v8::Value> ModuleSystem::RequireNative(const v8::Arguments& args) { CHECK_EQ(1, args.Length()); + std::string native_name = *v8::String::AsciiValue(args[0]->ToString()); + return RequireNativeFromString(native_name); +} + +v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString( + const std::string& native_name) { if (natives_enabled_ == 0) return ThrowException("Natives disabled"); - std::string native_name = *v8::String::AsciiValue(args[0]->ToString()); if (overridden_native_handlers_.count(native_name) > 0u) - return RequireForJs(args); + return RequireForJsInner(v8::String::New(native_name.c_str())); NativeHandlerMap::iterator i = native_handler_map_.find(native_name); if (i == native_handler_map_.end()) return v8::Undefined(); diff --git a/chrome/renderer/extensions/module_system.h b/chrome/renderer/extensions/module_system.h index 605a1c7..9f3d602 100644 --- a/chrome/renderer/extensions/module_system.h +++ b/chrome/renderer/extensions/module_system.h @@ -8,8 +8,7 @@ #include "base/compiler_specific.h" #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" -#include "chrome/renderer/extensions/native_handler.h" -#include "chrome/renderer/extensions/scoped_persistent.h" +#include "chrome/renderer/extensions/object_backed_native_handler.h" #include "v8/include/v8.h" #include <map> @@ -35,7 +34,7 @@ namespace extensions { // Note that a ModuleSystem must be used only in conjunction with a single // v8::Context. // TODO(koz): Rename this to JavaScriptModuleSystem. -class ModuleSystem : public NativeHandler { +class ModuleSystem : public ObjectBackedNativeHandler { public: class SourceMap { public: @@ -62,26 +61,21 @@ class ModuleSystem : public NativeHandler { }; // |source_map| is a weak pointer. - explicit ModuleSystem(v8::Handle<v8::Context> context, SourceMap* source_map); + ModuleSystem(v8::Handle<v8::Context> context, SourceMap* source_map); virtual ~ModuleSystem(); - // Returns true if the current context has a ModuleSystem installed in it. - static bool IsPresentInCurrentContext(); - // Dumps the debug info from |try_catch| to LOG(ERROR). static void DumpException(const v8::TryCatch& try_catch); // Require the specified module. This is the equivalent of calling // require('module_name') from the loaded JS files. - void Require(const std::string& module_name); + v8::Handle<v8::Value> Require(const std::string& module_name); v8::Handle<v8::Value> Require(const v8::Arguments& args); - v8::Handle<v8::Value> RequireForJs(const v8::Arguments& args); - v8::Handle<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name); // Calls the specified method exported by the specified module. This is // equivalent to calling require('module_name').method_name() from JS. - void CallModuleMethod(const std::string& module_name, - const std::string& method_name); + v8::Local<v8::Value> CallModuleMethod(const std::string& module_name, + const std::string& method_name); // Calls the specified method exported by the specified module. This is // equivalent to calling require('module_name').method_name(args) from JS. @@ -95,6 +89,8 @@ class ModuleSystem : public NativeHandler { // |native_handler|. void RegisterNativeHandler(const std::string& name, scoped_ptr<NativeHandler> native_handler); + // Check if a native handler has been registered for this |name|. + bool HasNativeHandler(const std::string& name); // Causes requireNative(|name|) to look for its module in |source_map_| // instead of using a registered native handler. This can be used in unit @@ -107,6 +103,11 @@ class ModuleSystem : public NativeHandler { // Retrieves the lazily defined field specified by |property|. static v8::Handle<v8::Value> LazyFieldGetter(v8::Local<v8::String> property, const v8::AccessorInfo& info); + // Retrieves the lazily defined field specified by |property| on a native + // object. + static v8::Handle<v8::Value> NativeLazyFieldGetter( + v8::Local<v8::String> property, + const v8::AccessorInfo& info); // Make |object|.|field| lazily evaluate to the result of // require(|module_name|)[|module_field|]. @@ -115,16 +116,29 @@ class ModuleSystem : public NativeHandler { const std::string& module_name, const std::string& module_field); + // Make |object|.|field| lazily evaluate to the result of + // requireNative(|module_name|)[|module_field|]. + void SetNativeLazyField(v8::Handle<v8::Object> object, + const std::string& field, + const std::string& module_name, + const std::string& module_field); + void set_exception_handler(scoped_ptr<ExceptionHandler> handler) { exception_handler_ = handler.Pass(); } + protected: + friend class ChromeV8Context; + virtual void Invalidate() OVERRIDE; + private: typedef std::map<std::string, linked_ptr<NativeHandler> > NativeHandlerMap; // Called when an exception is thrown but not caught. void HandleException(const v8::TryCatch& try_catch); + static std::string CreateExceptionString(const v8::TryCatch& try_catch); + // Ensure that require_ has been evaluated from require.js. void EnsureRequireLoaded(); @@ -133,6 +147,25 @@ class ModuleSystem : public NativeHandler { v8::Handle<v8::Value> RunString(v8::Handle<v8::String> code, v8::Handle<v8::String> name); + v8::Handle<v8::Value> RequireForJs(const v8::Arguments& args); + v8::Handle<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name); + + // Sets a lazy field using the specified |getter|. + void SetLazyField(v8::Handle<v8::Object> object, + const std::string& field, + const std::string& module_name, + const std::string& module_field, + v8::AccessorGetter getter); + + typedef v8::Handle<v8::Value> (ModuleSystem::*RequireFunction)( + const std::string&); + // Base implementation of a LazyFieldGetter which uses |require_fn| to require + // modules. + static v8::Handle<v8::Value> LazyFieldGetterInner( + v8::Local<v8::String> property, + const v8::AccessorInfo& info, + RequireFunction require_function); + // Return the named source file stored in the source map. // |args[0]| - the name of a source file in source_map_. v8::Handle<v8::Value> GetSource(v8::Handle<v8::String> source_name); @@ -140,7 +173,8 @@ class ModuleSystem : public NativeHandler { // Return an object that contains the native methods defined by the named // NativeHandler. // |args[0]| - the name of a native handler object. - v8::Handle<v8::Value> GetNative(const v8::Arguments& args); + v8::Handle<v8::Value> RequireNativeFromString(const std::string& native_name); + v8::Handle<v8::Value> RequireNative(const v8::Arguments& args); // Wraps |source| in a (function(require, requireNative, exports) {...}). v8::Handle<v8::String> WrapSource(v8::Handle<v8::String> source); @@ -148,9 +182,6 @@ class ModuleSystem : public NativeHandler { // Throws an exception in the calling JS context. v8::Handle<v8::Value> ThrowException(const std::string& message); - // The context that this ModuleSystem is for. - ScopedPersistent<v8::Context> context_; - // A map from module names to the JS source for that module. GetSource() // performs a lookup on this map. SourceMap* source_map_; diff --git a/chrome/renderer/extensions/module_system_unittest.cc b/chrome/renderer/extensions/module_system_unittest.cc index f5f7db6..4c88cca 100644 --- a/chrome/renderer/extensions/module_system_unittest.cc +++ b/chrome/renderer/extensions/module_system_unittest.cc @@ -6,13 +6,15 @@ #include "base/memory/scoped_ptr.h" #include "chrome/renderer/extensions/module_system.h" +// TODO(cduvall/kalman): Put this file in extensions namespace. using extensions::ModuleSystem; using extensions::NativeHandler; +using extensions::ObjectBackedNativeHandler; -class CounterNatives : public NativeHandler { +class CounterNatives : public ObjectBackedNativeHandler { public: - explicit CounterNatives(v8::Isolate* isolate) - : NativeHandler(isolate), counter_(0) { + explicit CounterNatives(v8::Handle<v8::Context> context) + : ObjectBackedNativeHandler(context), counter_(0) { RouteFunction("Get", base::Bind(&CounterNatives::Get, base::Unretained(this))); RouteFunction("Increment", base::Bind(&CounterNatives::Increment, @@ -173,7 +175,7 @@ TEST_F(ModuleSystemTest, TestLazyFieldIsOnlyEvaledOnce) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); module_system_->RegisterNativeHandler( "counter", - scoped_ptr<NativeHandler>(new CounterNatives(v8::Isolate::GetCurrent()))); + scoped_ptr<NativeHandler>(new CounterNatives(v8::Context::GetCurrent()))); RegisterModule("lazy", "requireNative('counter').Increment();" "exports.x = 5;"); @@ -227,7 +229,7 @@ TEST_F(ModuleSystemTest, TestModulesOnlyGetEvaledOnce) { ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); module_system_->RegisterNativeHandler( "counter", - scoped_ptr<NativeHandler>(new CounterNatives(v8::Isolate::GetCurrent()))); + scoped_ptr<NativeHandler>(new CounterNatives(v8::Context::GetCurrent()))); RegisterModule("incrementsWhenEvaled", "requireNative('counter').Increment();"); diff --git a/chrome/renderer/extensions/native_handler.cc b/chrome/renderer/extensions/native_handler.cc index ee03ad0..d6a9e57 100644 --- a/chrome/renderer/extensions/native_handler.cc +++ b/chrome/renderer/extensions/native_handler.cc @@ -1,56 +1,17 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 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/native_handler.h" -#include "base/memory/linked_ptr.h" -#include "base/logging.h" -#include "chrome/renderer/extensions/module_system.h" -#include "v8/include/v8.h" - namespace extensions { -NativeHandler::NativeHandler(v8::Isolate* isolate) - : object_template_(v8::ObjectTemplate::New()) {} +NativeHandler::NativeHandler() : is_valid_(true) {} NativeHandler::~NativeHandler() {} -v8::Handle<v8::Object> NativeHandler::NewInstance() { - return object_template_->NewInstance(); -} - -// static -v8::Handle<v8::Value> NativeHandler::Router(const v8::Arguments& args) { - // It is possible for JS code to execute after ModuleSystem has been deleted - // in which case the native handlers will also have been deleted, making - // HandlerFunction below point to freed memory. - if (!ModuleSystem::IsPresentInCurrentContext()) { - return v8::ThrowException(v8::Exception::Error( - v8::String::New("ModuleSystem has been deleted"))); - } - HandlerFunction* handler_function = static_cast<HandlerFunction*>( - args.Data().As<v8::External>()->Value()); - return handler_function->Run(args); -} - -void NativeHandler::RouteFunction(const std::string& name, - const HandlerFunction& handler_function) { - linked_ptr<HandlerFunction> function(new HandlerFunction(handler_function)); - // TODO(koz): Investigate using v8's MakeWeak() function instead of holding - // on to these pointers here. - handler_functions_.push_back(function); - v8::Handle<v8::FunctionTemplate> function_template = - v8::FunctionTemplate::New(Router, - v8::External::New(function.get())); - object_template_->Set(name.c_str(), function_template); -} - -void NativeHandler::RouteStaticFunction(const std::string& name, - const HandlerFunc handler_func) { - v8::Handle<v8::FunctionTemplate> function_template = - v8::FunctionTemplate::New(handler_func, v8::External::New(this)); - object_template_->Set(name.c_str(), function_template); +void NativeHandler::Invalidate() { + is_valid_ = false; } -} // extensions +} // namespace extensions diff --git a/chrome/renderer/extensions/native_handler.h b/chrome/renderer/extensions/native_handler.h index dbe6a63..95b0032 100644 --- a/chrome/renderer/extensions/native_handler.h +++ b/chrome/renderer/extensions/native_handler.h @@ -5,53 +5,38 @@ #ifndef CHROME_RENDERER_EXTENSIONS_NATIVE_HANDLER_H_ #define CHROME_RENDERER_EXTENSIONS_NATIVE_HANDLER_H_ -#include "base/bind.h" -#include "base/memory/linked_ptr.h" -#include "chrome/renderer/extensions/scoped_persistent.h" +#include "base/basictypes.h" #include "v8/include/v8.h" -#include <string> -#include <vector> - namespace extensions { -// A NativeHandler is a factory for JS objects with functions on them that map -// to native C++ functions. Subclasses should call RouteFunction() in their -// constructor to define functions on the created JS objects. -// // NativeHandlers are intended to be used with a ModuleSystem. The ModuleSystem // will assume ownership of the NativeHandler, and as a ModuleSystem is tied to // a single v8::Context, this implies that NativeHandlers will also be tied to -// a single v8::context. +// a single v8::Context. // TODO(koz): Rename this to NativeJavaScriptModule. class NativeHandler { public: - explicit NativeHandler(v8::Isolate* isolate); + NativeHandler(); virtual ~NativeHandler(); - // Create an object with bindings to the native functions defined through - // RouteFunction(). - virtual v8::Handle<v8::Object> NewInstance(); - - protected: - typedef v8::Handle<v8::Value> (*HandlerFunc)(const v8::Arguments&); - typedef base::Callback<v8::Handle<v8::Value>(const v8::Arguments&)> - HandlerFunction; + // Create a new instance of the object this handler specifies. + virtual v8::Handle<v8::Object> NewInstance() = 0; - // Installs a new 'route' from |name| to |handler_function|. This means that - // NewInstance()s of this NativeHandler will have a property |name| which - // will be handled by |handler_function|. - void RouteFunction(const std::string& name, - const HandlerFunction& handler_function); + // Invalidate this object so it cannot be used any more. This is needed + // because it's possible for this to outlive its owner context. Invalidate + // must be called before this happens. + // + // Subclasses should override to invalidate their own V8 state. If they do + // they must call their superclass' Invalidate(). + virtual void Invalidate(); - void RouteStaticFunction(const std::string& name, - const HandlerFunc handler_func); + protected: + // Allow subclasses to query valid state. + bool is_valid() { return is_valid_; } private: - static v8::Handle<v8::Value> Router(const v8::Arguments& args); - - std::vector<linked_ptr<HandlerFunction> > handler_functions_; - ScopedPersistent<v8::ObjectTemplate> object_template_; + bool is_valid_; DISALLOW_COPY_AND_ASSIGN(NativeHandler); }; diff --git a/chrome/renderer/extensions/object_backed_native_handler.cc b/chrome/renderer/extensions/object_backed_native_handler.cc new file mode 100644 index 0000000..2a4043c --- /dev/null +++ b/chrome/renderer/extensions/object_backed_native_handler.cc @@ -0,0 +1,81 @@ +// 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/object_backed_native_handler.h" + +#include "base/logging.h" +#include "base/memory/linked_ptr.h" +#include "chrome/renderer/extensions/module_system.h" +#include "v8/include/v8.h" + +namespace extensions { + +// Data to pass to ObjectBackedNativeHandler::Router. +struct ObjectBackedNativeHandler::RouterData { + RouterData(ObjectBackedNativeHandler* self, HandlerFunction function) + : self(self), function(function) {} + + ~RouterData() {} + + // The owner of the routed data. + ObjectBackedNativeHandler* const self; + + // The function to route calls to. + HandlerFunction function; +}; + +ObjectBackedNativeHandler::ObjectBackedNativeHandler( + v8::Handle<v8::Context> context) + : v8_context_(context), + object_template_(v8::ObjectTemplate::New()) { +} + +ObjectBackedNativeHandler::~ObjectBackedNativeHandler() { + Invalidate(); +} + +v8::Handle<v8::Object> ObjectBackedNativeHandler::NewInstance() { + return object_template_->NewInstance(); +} + +// static +v8::Handle<v8::Value> ObjectBackedNativeHandler::Router( + const v8::Arguments& args) { + RouterData* router_data = static_cast<RouterData*>( + args.Data().As<v8::External>()->Value()); + // Router can be called during context destruction. Stop. + if (!router_data->self->is_valid()) + return v8::Handle<v8::Value>(); + return router_data->function.Run(args); +} + +void ObjectBackedNativeHandler::RouteFunction( + const std::string& name, + const HandlerFunction& handler_function) { + linked_ptr<RouterData> data(new RouterData(this, handler_function)); + // TODO(koz): Investigate using v8's MakeWeak() function instead of holding + // on to these pointers here. + router_data_.push_back(data); + v8::Handle<v8::FunctionTemplate> function_template = + v8::FunctionTemplate::New(Router, v8::External::New(data.get())); + object_template_->Set(name.c_str(), function_template); +} + +void ObjectBackedNativeHandler::RouteStaticFunction( + const std::string& name, + const HandlerFunc handler_func) { + v8::Handle<v8::FunctionTemplate> function_template = + v8::FunctionTemplate::New(handler_func, v8::External::New(this)); + object_template_->Set(name.c_str(), function_template); +} + +void ObjectBackedNativeHandler::Invalidate() { + if (!is_valid()) + return; + object_template_.reset(); + v8_context_.reset(); + NativeHandler::Invalidate(); +} + +} // extensions diff --git a/chrome/renderer/extensions/object_backed_native_handler.h b/chrome/renderer/extensions/object_backed_native_handler.h new file mode 100644 index 0000000..16ebe55 --- /dev/null +++ b/chrome/renderer/extensions/object_backed_native_handler.h @@ -0,0 +1,67 @@ +// 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_OBJECT_BACKED_NATIVE_HANDLER_H_ +#define CHROME_RENDERER_EXTENSIONS_OBJECT_BACKED_NATIVE_HANDLER_H_ + +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/memory/linked_ptr.h" +#include "chrome/renderer/extensions/native_handler.h" +#include "chrome/renderer/extensions/scoped_persistent.h" +#include "v8/include/v8.h" + +namespace extensions { + +// An ObjectBackedNativeHandler is a factory for JS objects with functions on +// them that map to native C++ functions. Subclasses should call RouteFunction() +// in their constructor to define functions on the created JS objects. +class ObjectBackedNativeHandler : public NativeHandler { + public: + explicit ObjectBackedNativeHandler(v8::Handle<v8::Context> context); + virtual ~ObjectBackedNativeHandler(); + + // Create an object with bindings to the native functions defined through + // RouteFunction(). + virtual v8::Handle<v8::Object> NewInstance() OVERRIDE; + + protected: + typedef v8::Handle<v8::Value> (*HandlerFunc)(const v8::Arguments&); + typedef base::Callback<v8::Handle<v8::Value>(const v8::Arguments&)> + HandlerFunction; + + // Installs a new 'route' from |name| to |handler_function|. This means that + // NewInstance()s of this ObjectBackedNativeHandler will have a property + // |name| which will be handled by |handler_function|. + void RouteFunction(const std::string& name, + const HandlerFunction& handler_function); + + void RouteStaticFunction(const std::string& name, + const HandlerFunc handler_func); + + v8::Handle<v8::Context> v8_context() { return v8_context_.get(); } + + virtual void Invalidate() OVERRIDE; + + private: + + static v8::Handle<v8::Value> Router(const v8::Arguments& args); + + struct RouterData; + std::vector<linked_ptr<RouterData> > router_data_; + + // TODO(kalman): Just pass around a ChromeV8Context. It already has a + // persistent handle to this context. + ScopedPersistent<v8::Context> v8_context_; + + ScopedPersistent<v8::ObjectTemplate> object_template_; + + DISALLOW_COPY_AND_ASSIGN(ObjectBackedNativeHandler); +}; + +} // extensions + +#endif // CHROME_RENDERER_EXTENSIONS_OBJECT_BACKED_NATIVE_HANDLER_H_ diff --git a/chrome/renderer/extensions/page_actions_custom_bindings.cc b/chrome/renderer/extensions/page_actions_custom_bindings.cc index 77ab867..5ff6bde 100644 --- a/chrome/renderer/extensions/page_actions_custom_bindings.cc +++ b/chrome/renderer/extensions/page_actions_custom_bindings.cc @@ -15,8 +15,8 @@ namespace extensions { PageActionsCustomBindings::PageActionsCustomBindings( - Dispatcher* dispatcher) - : ChromeV8Extension(dispatcher) { + Dispatcher* dispatcher, v8::Handle<v8::Context> v8_context) + : ChromeV8Extension(dispatcher, v8_context) { RouteStaticFunction("GetCurrentPageActions", &GetCurrentPageActions); } diff --git a/chrome/renderer/extensions/page_actions_custom_bindings.h b/chrome/renderer/extensions/page_actions_custom_bindings.h index 10de942..a9faccc 100644 --- a/chrome/renderer/extensions/page_actions_custom_bindings.h +++ b/chrome/renderer/extensions/page_actions_custom_bindings.h @@ -13,7 +13,8 @@ class Dispatcher; // Implements custom bindings for the pageActions API. class PageActionsCustomBindings : public ChromeV8Extension { public: - explicit PageActionsCustomBindings(Dispatcher* extension_dispatcher); + PageActionsCustomBindings(Dispatcher* extension_dispatcher, + v8::Handle<v8::Context> v8_context); private: static v8::Handle<v8::Value> GetCurrentPageActions(const v8::Arguments& args); diff --git a/chrome/renderer/extensions/page_capture_custom_bindings.cc b/chrome/renderer/extensions/page_capture_custom_bindings.cc index c7e41ea..3b684b9 100644 --- a/chrome/renderer/extensions/page_capture_custom_bindings.cc +++ b/chrome/renderer/extensions/page_capture_custom_bindings.cc @@ -13,8 +13,10 @@ namespace extensions { -PageCaptureCustomBindings::PageCaptureCustomBindings() - : ChromeV8Extension(NULL) { +PageCaptureCustomBindings::PageCaptureCustomBindings( + Dispatcher* dispatcher, + v8::Handle<v8::Context> context) + : ChromeV8Extension(dispatcher, context) { RouteStaticFunction("CreateBlob", &CreateBlob); RouteStaticFunction("SendResponseAck", &SendResponseAck); } @@ -37,7 +39,9 @@ v8::Handle<v8::Value> PageCaptureCustomBindings::SendResponseAck( CHECK(args.Length() == 1); CHECK(args[0]->IsInt32()); - content::RenderView* render_view = GetCurrentRenderView(); + PageCaptureCustomBindings* self = + GetFromArguments<PageCaptureCustomBindings>(args); + content::RenderView* render_view = self->GetRenderView(); if (render_view) { render_view->Send(new ExtensionHostMsg_ResponseAck( render_view->GetRoutingID(), args[0]->Int32Value())); diff --git a/chrome/renderer/extensions/page_capture_custom_bindings.h b/chrome/renderer/extensions/page_capture_custom_bindings.h index d6ac69d..2881769 100644 --- a/chrome/renderer/extensions/page_capture_custom_bindings.h +++ b/chrome/renderer/extensions/page_capture_custom_bindings.h @@ -12,7 +12,8 @@ namespace extensions { // Implements custom bindings for the pageCapture API. class PageCaptureCustomBindings : public ChromeV8Extension { public: - PageCaptureCustomBindings(); + PageCaptureCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context); private: // Creates a Blob with the content of the specified file. diff --git a/chrome/renderer/extensions/request_sender.cc b/chrome/renderer/extensions/request_sender.cc index 0c54e17..e59be5a 100644 --- a/chrome/renderer/extensions/request_sender.cc +++ b/chrome/renderer/extensions/request_sender.cc @@ -7,7 +7,6 @@ #include "base/values.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/renderer/extensions/chrome_v8_context.h" -#include "chrome/renderer/extensions/chrome_v8_context_set.h" #include "chrome/renderer/extensions/dispatcher.h" #include "chrome/renderer/extensions/scoped_persistent.h" #include "content/public/renderer/render_view.h" @@ -24,20 +23,15 @@ namespace extensions { // Contains info relevant to a pending API request. struct PendingRequest { public : - PendingRequest(v8::Handle<v8::Context> context, - const std::string& name, - const std::string& extension_id) - : context(context), name(name), extension_id(extension_id) { + PendingRequest(const std::string& name, ChromeV8Context* context) + : name(name), context(context) { } - ScopedPersistent<v8::Context> context; std::string name; - std::string extension_id; + ChromeV8Context* context; }; -RequestSender::RequestSender(Dispatcher* dispatcher, - ChromeV8ContextSet* context_set) - : dispatcher_(dispatcher), context_set_(context_set) { +RequestSender::RequestSender(Dispatcher* dispatcher) : dispatcher_(dispatcher) { } RequestSender::~RequestSender() { @@ -58,18 +52,15 @@ linked_ptr<PendingRequest> RequestSender::RemoveRequest(int request_id) { return result; } -void RequestSender::StartRequest(const std::string& name, +void RequestSender::StartRequest(ChromeV8Context* context, + const std::string& name, int request_id, bool has_callback, bool for_io_thread, base::ListValue* value_args) { - ChromeV8Context* current_context = context_set_->GetCurrent(); - if (!current_context) - return; - // Get the current RenderView so that we can send a routed IPC message from // the correct source. - content::RenderView* renderview = current_context->GetRenderView(); + content::RenderView* renderview = context->GetRenderView(); if (!renderview) return; @@ -81,25 +72,23 @@ void RequestSender::StartRequest(const std::string& name, } // TODO(koz): See if we can make this a CHECK. - if (!dispatcher_->CheckCurrentContextAccessToExtensionAPI(name)) + if (!dispatcher_->CheckContextAccessToExtensionAPI(name, context)) return; GURL source_url; WebKit::WebSecurityOrigin source_origin; - WebKit::WebFrame* webframe = current_context->web_frame(); + WebKit::WebFrame* webframe = context->web_frame(); if (webframe) { source_url = webframe->document().url(); source_origin = webframe->document().securityOrigin(); } - std::string extension_id = current_context->GetExtensionID(); - InsertRequest(request_id, new PendingRequest( - v8::Context::GetCurrent(), name, extension_id)); + InsertRequest(request_id, new PendingRequest(name, context)); ExtensionHostMsg_Request_Params params; params.name = name; params.arguments.Swap(value_args); - params.extension_id = extension_id; + params.extension_id = context->GetExtensionID(); params.source_url = source_url; params.source_origin = source_origin.toString(); params.request_id = request_id; @@ -122,17 +111,10 @@ void RequestSender::HandleResponse(int request_id, linked_ptr<PendingRequest> request = RemoveRequest(request_id); if (!request.get()) { - // This should not be able to happen since we only remove requests when - // they are handled. - LOG(ERROR) << "Could not find specified request id: " << request_id; + // This can happen if a context is destroyed while a request is in flight. return; } - ChromeV8Context* v8_context = - context_set_->GetByV8Context(request->context.get()); - if (!v8_context) - return; // The frame went away. - v8::HandleScope handle_scope; scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); @@ -140,15 +122,15 @@ void RequestSender::HandleResponse(int request_id, v8::Integer::New(request_id), v8::String::New(request->name.c_str()), v8::Boolean::New(success), - converter->ToV8Value(&responseList, v8_context->v8_context()), + converter->ToV8Value(&responseList, request->context->v8_context()), v8::String::New(error.c_str()) }; v8::Handle<v8::Value> retval; - CHECK(v8_context->CallChromeHiddenMethod("handleResponse", - arraysize(argv), - argv, - &retval)); + CHECK(request->context->CallChromeHiddenMethod("handleResponse", + arraysize(argv), + argv, + &retval)); // In debug, the js will validate the callback parameters and return a // string if a validation error has occured. if (DCHECK_IS_ON()) { @@ -159,4 +141,14 @@ void RequestSender::HandleResponse(int request_id, } } +void RequestSender::InvalidateContext(ChromeV8Context* context) { + for (PendingRequestMap::iterator it = pending_requests_.begin(); + it != pending_requests_.end();) { + if (it->second->context == context) + pending_requests_.erase(it++); + else + ++it; + } +} + } // namespace extensions diff --git a/chrome/renderer/extensions/request_sender.h b/chrome/renderer/extensions/request_sender.h index a7bbdb8..b1463fd 100644 --- a/chrome/renderer/extensions/request_sender.h +++ b/chrome/renderer/extensions/request_sender.h @@ -16,7 +16,7 @@ class ListValue; } namespace extensions { -class ChromeV8ContextSet; +class ChromeV8Context; class Dispatcher; struct PendingRequest; @@ -25,15 +25,17 @@ struct PendingRequest; // extension host and routing the responses back to the caller. class RequestSender { public: - explicit RequestSender(Dispatcher* dispatcher, - ChromeV8ContextSet* context_set); + explicit RequestSender(Dispatcher* dispatcher); ~RequestSender(); // Makes a call to the API function |name| that is to be handled by the // extension host. The response to this request will be received in // HandleResponse(). // TODO(koz): Remove |request_id| and generate that internally. - void StartRequest(const std::string& name, + // There are multiple of these per render view though, so we'll + // need to vend the IDs centrally. + void StartRequest(ChromeV8Context* target_context, + const std::string& name, int request_id, bool has_callback, bool for_io_thread, @@ -45,6 +47,9 @@ class RequestSender { const base::ListValue& response, const std::string& error); + // Notifies this that a context is no longer valid. + // TODO(kalman): Do this in a generic/safe way. + void InvalidateContext(ChromeV8Context* context); private: typedef std::map<int, linked_ptr<PendingRequest> > PendingRequestMap; @@ -54,7 +59,6 @@ class RequestSender { Dispatcher* dispatcher_; PendingRequestMap pending_requests_; - ChromeV8ContextSet* context_set_; DISALLOW_COPY_AND_ASSIGN(RequestSender); }; diff --git a/chrome/renderer/extensions/runtime_custom_bindings.cc b/chrome/renderer/extensions/runtime_custom_bindings.cc index b775d49..96139f3 100644 --- a/chrome/renderer/extensions/runtime_custom_bindings.cc +++ b/chrome/renderer/extensions/runtime_custom_bindings.cc @@ -21,11 +21,14 @@ namespace extensions { RuntimeCustomBindings::RuntimeCustomBindings(Dispatcher* dispatcher, ChromeV8Context* context) - : ChromeV8Extension(dispatcher), context_(context) { + : ChromeV8Extension(dispatcher, context->v8_context()), + context_(context) { RouteFunction("GetManifest", base::Bind(&RuntimeCustomBindings::GetManifest, base::Unretained(this))); - RouteStaticFunction("OpenChannelToExtension", &OpenChannelToExtension); + RouteFunction("OpenChannelToExtension", + base::Bind(&RuntimeCustomBindings::OpenChannelToExtension, + base::Unretained(this))); RouteFunction("OpenChannelToNativeApp", base::Bind(&RuntimeCustomBindings::OpenChannelToNativeApp, base::Unretained(this))); @@ -33,12 +36,11 @@ RuntimeCustomBindings::RuntimeCustomBindings(Dispatcher* dispatcher, RuntimeCustomBindings::~RuntimeCustomBindings() {} -// static v8::Handle<v8::Value> RuntimeCustomBindings::OpenChannelToExtension( const v8::Arguments& args) { // Get the current RenderView so that we can send a routed IPC message from // the correct source. - content::RenderView* renderview = GetCurrentRenderView(); + content::RenderView* renderview = GetRenderView(); if (!renderview) return v8::Undefined(); @@ -64,14 +66,14 @@ v8::Handle<v8::Value> RuntimeCustomBindings::OpenChannelToExtension( v8::Handle<v8::Value> RuntimeCustomBindings::OpenChannelToNativeApp( const v8::Arguments& args) { // Verify that the extension has permission to use native messaging. - if (!dispatcher()->CheckCurrentContextAccessToExtensionAPI( - "nativeMessaging")) { + if (!dispatcher()->CheckContextAccessToExtensionAPI( + "nativeMessaging", context_)) { return v8::Undefined(); } // Get the current RenderView so that we can send a routed IPC message from // the correct source. - content::RenderView* renderview = GetCurrentRenderView(); + content::RenderView* renderview = GetRenderView(); if (!renderview) return v8::Undefined(); diff --git a/chrome/renderer/extensions/runtime_custom_bindings.h b/chrome/renderer/extensions/runtime_custom_bindings.h index 5ddf248..d800713 100644 --- a/chrome/renderer/extensions/runtime_custom_bindings.h +++ b/chrome/renderer/extensions/runtime_custom_bindings.h @@ -17,18 +17,15 @@ namespace extensions { // The native component of custom bindings for the chrome.runtime API. class RuntimeCustomBindings : public ChromeV8Extension { public: - RuntimeCustomBindings(Dispatcher* dispatcher, - ChromeV8Context* context); + RuntimeCustomBindings(Dispatcher* dispatcher, ChromeV8Context* context); virtual ~RuntimeCustomBindings(); // Creates a new messaging channel to the given extension. - static v8::Handle<v8::Value> OpenChannelToExtension( - const v8::Arguments& args); + v8::Handle<v8::Value> OpenChannelToExtension(const v8::Arguments& args); // Creates a new messaging channels for the specified native application. - v8::Handle<v8::Value> OpenChannelToNativeApp( - const v8::Arguments& args); + v8::Handle<v8::Value> OpenChannelToNativeApp(const v8::Arguments& args); private: v8::Handle<v8::Value> GetManifest(const v8::Arguments& args); diff --git a/chrome/renderer/extensions/send_request_natives.cc b/chrome/renderer/extensions/send_request_natives.cc index b788a8a..be87840 100644 --- a/chrome/renderer/extensions/send_request_natives.cc +++ b/chrome/renderer/extensions/send_request_natives.cc @@ -13,14 +13,20 @@ using content::V8ValueConverter; namespace extensions { SendRequestNatives::SendRequestNatives(Dispatcher* dispatcher, - RequestSender* request_sender) - : ChromeV8Extension(dispatcher), request_sender_(request_sender) { + RequestSender* request_sender, + ChromeV8Context* context) + : ChromeV8Extension(dispatcher, context->v8_context()), + request_sender_(request_sender), + context_(context) { RouteFunction("GetNextRequestId", base::Bind(&SendRequestNatives::GetNextRequestId, base::Unretained(this))); RouteFunction("StartRequest", base::Bind(&SendRequestNatives::StartRequest, base::Unretained(this))); + RouteFunction("GetGlobal", + base::Bind(&SendRequestNatives::GetGlobal, + base::Unretained(this))); } v8::Handle<v8::Value> SendRequestNatives::GetNextRequestId( @@ -42,22 +48,28 @@ v8::Handle<v8::Value> SendRequestNatives::StartRequest( scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); // See http://crbug.com/149880. The context menus APIs relies on this, but - // we shouln't really be doing it (e.g. for the sake of the storage API). + // we shouldn't really be doing it (e.g. for the sake of the storage API). converter->SetFunctionAllowed(true); if (!preserve_null_in_objects) converter->SetStripNullFromObjects(true); - scoped_ptr<Value> value_args( - converter->FromV8Value(args[1], v8::Context::GetCurrent())); + scoped_ptr<Value> value_args(converter->FromV8Value(args[1], v8_context())); if (!value_args.get() || !value_args->IsType(Value::TYPE_LIST)) { NOTREACHED() << "Unable to convert args passed to StartRequest"; return v8::Undefined(); } - request_sender_->StartRequest(name, request_id, has_callback, for_io_thread, - static_cast<ListValue*>(value_args.get())); + request_sender_->StartRequest( + context_, name, request_id, has_callback, for_io_thread, + static_cast<ListValue*>(value_args.get())); return v8::Undefined(); } +v8::Handle<v8::Value> SendRequestNatives::GetGlobal(const v8::Arguments& args) { + CHECK_EQ(1, args.Length()); + CHECK(args[0]->IsObject()); + return v8::Handle<v8::Object>::Cast(args[0])->CreationContext()->Global(); +} + } // namespace extensions diff --git a/chrome/renderer/extensions/send_request_natives.h b/chrome/renderer/extensions/send_request_natives.h index d92ffe3..1e3437a 100644 --- a/chrome/renderer/extensions/send_request_natives.h +++ b/chrome/renderer/extensions/send_request_natives.h @@ -11,22 +11,31 @@ #include "v8/include/v8.h" namespace extensions { +class ChromeV8Context; class RequestSender; // Native functions exposed to extensions via JS for calling API functions in // the browser. class SendRequestNatives : public ChromeV8Extension { public: - SendRequestNatives(Dispatcher* dispatcher, RequestSender* request_sender); + SendRequestNatives(Dispatcher* dispatcher, + RequestSender* request_sender, + ChromeV8Context* context); private: v8::Handle<v8::Value> GetNextRequestId(const v8::Arguments& args); + // Starts an API request to the browser, with an optional callback. The // callback will be dispatched to EventBindings::HandleResponse. v8::Handle<v8::Value> StartRequest(const v8::Arguments& args); + // Gets a reference to an object's global object. + v8::Handle<v8::Value> GetGlobal(const v8::Arguments& args); + RequestSender* request_sender_; + ChromeV8Context* context_; + DISALLOW_COPY_AND_ASSIGN(SendRequestNatives); }; diff --git a/chrome/renderer/extensions/set_icon_natives.cc b/chrome/renderer/extensions/set_icon_natives.cc index 369cb07..084c5a8 100644 --- a/chrome/renderer/extensions/set_icon_natives.cc +++ b/chrome/renderer/extensions/set_icon_natives.cc @@ -24,9 +24,11 @@ const char kNoMemory[] = "Chrome was unable to initialize icon."; namespace extensions { SetIconNatives::SetIconNatives(Dispatcher* dispatcher, - RequestSender* request_sender) - : ChromeV8Extension(dispatcher), - request_sender_(request_sender) { + RequestSender* request_sender, + ChromeV8Context* context) + : ChromeV8Extension(dispatcher, context->v8_context()), + request_sender_(request_sender), + context_(context) { RouteFunction( "SetIconCommon", base::Bind(&SetIconNatives::SetIconCommon, base::Unretained(this))); @@ -136,7 +138,8 @@ v8::Handle<v8::Value> SetIconNatives::SetIconCommon( bool has_callback = args[3]->BooleanValue(); bool for_io_thread = args[4]->BooleanValue(); - request_sender_->StartRequest(name, + request_sender_->StartRequest(context_, + name, request_id, has_callback, for_io_thread, diff --git a/chrome/renderer/extensions/set_icon_natives.h b/chrome/renderer/extensions/set_icon_natives.h index b03bcee..7e0352a 100644 --- a/chrome/renderer/extensions/set_icon_natives.h +++ b/chrome/renderer/extensions/set_icon_natives.h @@ -20,7 +20,9 @@ class RequestSender; // Functions exposed to extension JS to implement the setIcon extension API. class SetIconNatives : public ChromeV8Extension { public: - SetIconNatives(Dispatcher* dispatcher, RequestSender* request_sender); + SetIconNatives(Dispatcher* dispatcher, + RequestSender* request_sender, + ChromeV8Context* context); private: bool ConvertImageDataToBitmapValue(const v8::Local<v8::Object> image_data, @@ -28,8 +30,11 @@ class SetIconNatives : public ChromeV8Extension { bool ConvertImageDataSetToBitmapValueSet(const v8::Arguments& args, DictionaryValue* bitmap_value); v8::Handle<v8::Value> SetIconCommon(const v8::Arguments& args); + RequestSender* request_sender_; + ChromeV8Context* context_; + DISALLOW_COPY_AND_ASSIGN(SetIconNatives); }; diff --git a/chrome/renderer/extensions/sync_file_system_custom_bindings.cc b/chrome/renderer/extensions/sync_file_system_custom_bindings.cc index 3244815..3fa09da 100644 --- a/chrome/renderer/extensions/sync_file_system_custom_bindings.cc +++ b/chrome/renderer/extensions/sync_file_system_custom_bindings.cc @@ -13,8 +13,9 @@ namespace extensions { -SyncFileSystemCustomBindings::SyncFileSystemCustomBindings() - : ChromeV8Extension(NULL) { +SyncFileSystemCustomBindings::SyncFileSystemCustomBindings( + Dispatcher* dispatcher, v8::Handle<v8::Context> v8_context) + : ChromeV8Extension(dispatcher, v8_context) { RouteFunction( "GetSyncFileSystemObject", base::Bind(&SyncFileSystemCustomBindings::GetSyncFileSystemObject, @@ -47,7 +48,8 @@ v8::Handle<v8::Value> SyncFileSystemCustomBindings::GetSyncFileSystemObject( return v8::Undefined(); } - WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext(); + WebKit::WebFrame* webframe = + WebKit::WebFrame::frameForContext(v8_context()); return webframe->createFileSystem(WebKit::WebFileSystem::TypeExternal, WebKit::WebString::fromUTF8(name), WebKit::WebString::fromUTF8(root_url)); diff --git a/chrome/renderer/extensions/sync_file_system_custom_bindings.h b/chrome/renderer/extensions/sync_file_system_custom_bindings.h index e4f5f09..c5820c7 100644 --- a/chrome/renderer/extensions/sync_file_system_custom_bindings.h +++ b/chrome/renderer/extensions/sync_file_system_custom_bindings.h @@ -6,13 +6,15 @@ #define CHROME_RENDERER_EXTENSIONS_SYNC_FILE_SYSTEM_CUSTOM_BINDINGS_H_ #include "chrome/renderer/extensions/chrome_v8_extension.h" +#include "v8/include/v8.h" namespace extensions { // Implements custom bindings for the sync file system API. class SyncFileSystemCustomBindings : public ChromeV8Extension { public: - SyncFileSystemCustomBindings(); + SyncFileSystemCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> v8_context); private: // FileSystemObject GetSyncFileSystemObject(string name, string root_url): diff --git a/chrome/renderer/extensions/tabs_custom_bindings.cc b/chrome/renderer/extensions/tabs_custom_bindings.cc index 5492d34..7d16093 100644 --- a/chrome/renderer/extensions/tabs_custom_bindings.cc +++ b/chrome/renderer/extensions/tabs_custom_bindings.cc @@ -13,8 +13,9 @@ namespace extensions { -TabsCustomBindings::TabsCustomBindings() - : ChromeV8Extension(NULL) { +TabsCustomBindings::TabsCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> context) + : ChromeV8Extension(dispatcher, context) { RouteStaticFunction("OpenChannelToTab", &OpenChannelToTab); } @@ -23,7 +24,8 @@ v8::Handle<v8::Value> TabsCustomBindings::OpenChannelToTab( const v8::Arguments& args) { // Get the current RenderView so that we can send a routed IPC message from // the correct source. - content::RenderView* renderview = GetCurrentRenderView(); + TabsCustomBindings* self = GetFromArguments<TabsCustomBindings>(args); + content::RenderView* renderview = self->GetRenderView(); if (!renderview) return v8::Undefined(); diff --git a/chrome/renderer/extensions/tabs_custom_bindings.h b/chrome/renderer/extensions/tabs_custom_bindings.h index 0576212..2ac19bb 100644 --- a/chrome/renderer/extensions/tabs_custom_bindings.h +++ b/chrome/renderer/extensions/tabs_custom_bindings.h @@ -12,7 +12,7 @@ namespace extensions { // Implements custom bindings for the tabs API. class TabsCustomBindings : public ChromeV8Extension { public: - TabsCustomBindings(); + TabsCustomBindings(Dispatcher* dispatcher, v8::Handle<v8::Context> context); private: // Creates a new messaging channel to the tab with the given ID. diff --git a/chrome/renderer/extensions/tts_custom_bindings.cc b/chrome/renderer/extensions/tts_custom_bindings.cc index 8720aa8..a03e321 100644 --- a/chrome/renderer/extensions/tts_custom_bindings.cc +++ b/chrome/renderer/extensions/tts_custom_bindings.cc @@ -11,8 +11,9 @@ namespace extensions { -TTSCustomBindings::TTSCustomBindings() - : ChromeV8Extension(NULL) { +TTSCustomBindings::TTSCustomBindings( + Dispatcher* dispatcher, v8::Handle<v8::Context> v8_context) + : ChromeV8Extension(dispatcher, v8_context) { RouteStaticFunction("GetNextTTSEventId", &GetNextTTSEventId); } diff --git a/chrome/renderer/extensions/tts_custom_bindings.h b/chrome/renderer/extensions/tts_custom_bindings.h index dbe212c..8b9b9a5 100644 --- a/chrome/renderer/extensions/tts_custom_bindings.h +++ b/chrome/renderer/extensions/tts_custom_bindings.h @@ -12,7 +12,7 @@ namespace extensions { // Implements custom bindings for the tts API. class TTSCustomBindings : public ChromeV8Extension { public: - TTSCustomBindings(); + TTSCustomBindings(Dispatcher* dispatcher, v8::Handle<v8::Context> v8_context); private: static v8::Handle<v8::Value> GetNextTTSEventId(const v8::Arguments& args); diff --git a/chrome/renderer/extensions/v8_schema_registry.cc b/chrome/renderer/extensions/v8_schema_registry.cc index bf4c779..3b66eed 100644 --- a/chrome/renderer/extensions/v8_schema_registry.cc +++ b/chrome/renderer/extensions/v8_schema_registry.cc @@ -21,12 +21,11 @@ V8SchemaRegistry::~V8SchemaRegistry() { i != schema_cache_.end(); ++i) { i->second.Dispose(isolate); } - context_.Dispose(isolate); } v8::Handle<v8::Array> V8SchemaRegistry::GetSchemas( const std::set<std::string>& apis) { - v8::Context::Scope context_scope(context_); + v8::Context::Scope context_scope(context_.get()); v8::Handle<v8::Array> v8_apis(v8::Array::New(apis.size())); size_t api_index = 0; for (std::set<std::string>::const_iterator i = apis.begin(); i != apis.end(); @@ -50,7 +49,7 @@ v8::Handle<v8::Object> V8SchemaRegistry::GetSchema(const std::string& api) { v8::Persistent<v8::Object>::New( context_->GetIsolate(), v8::Handle<v8::Object>::Cast( - v8_value_converter->ToV8Value(schema, context_))); + v8_value_converter->ToV8Value(schema, context_.get()))); CHECK(!v8_schema.IsEmpty()); schema_cache_[api] = v8_schema; return v8_schema; diff --git a/chrome/renderer/extensions/v8_schema_registry.h b/chrome/renderer/extensions/v8_schema_registry.h index ab3ecf0..4d4b8e1 100644 --- a/chrome/renderer/extensions/v8_schema_registry.h +++ b/chrome/renderer/extensions/v8_schema_registry.h @@ -10,6 +10,7 @@ #include <string> #include "base/basictypes.h" +#include "chrome/renderer/extensions/scoped_persistent.h" #include "v8/include/v8.h" namespace extensions { @@ -24,16 +25,16 @@ class V8SchemaRegistry { // Returns a v8::Array with all the schemas for the APIs in |apis|. v8::Handle<v8::Array> GetSchemas(const std::set<std::string>& apis); - private: // Returns a v8::Object for the schema for |api|, possibly from the cache. v8::Handle<v8::Object> GetSchema(const std::string& api); + private: // Cache of schemas. typedef std::map<std::string, v8::Persistent<v8::Object> > SchemaCache; SchemaCache schema_cache_; // Single per-instance v8::Context to create v8::Values. - v8::Persistent<v8::Context> context_; + ScopedPersistent<v8::Context> context_; DISALLOW_COPY_AND_ASSIGN(V8SchemaRegistry); }; diff --git a/chrome/renderer/extensions/web_request_custom_bindings.cc b/chrome/renderer/extensions/web_request_custom_bindings.cc index 4bee0ca..3785501 100644 --- a/chrome/renderer/extensions/web_request_custom_bindings.cc +++ b/chrome/renderer/extensions/web_request_custom_bindings.cc @@ -12,8 +12,9 @@ namespace extensions { -WebRequestCustomBindings::WebRequestCustomBindings() - : ChromeV8Extension(NULL) { +WebRequestCustomBindings::WebRequestCustomBindings( + Dispatcher* dispatcher, v8::Handle<v8::Context> v8_context) + : ChromeV8Extension(dispatcher, v8_context) { RouteStaticFunction("GetUniqueSubEventName", &GetUniqueSubEventName); } diff --git a/chrome/renderer/extensions/web_request_custom_bindings.h b/chrome/renderer/extensions/web_request_custom_bindings.h index 988c1d0..89c67aa 100644 --- a/chrome/renderer/extensions/web_request_custom_bindings.h +++ b/chrome/renderer/extensions/web_request_custom_bindings.h @@ -12,7 +12,8 @@ namespace extensions { // Implements custom bindings for the webRequest API. class WebRequestCustomBindings : public ChromeV8Extension { public: - WebRequestCustomBindings(); + WebRequestCustomBindings(Dispatcher* dispatcher, + v8::Handle<v8::Context> v8_context); private: static v8::Handle<v8::Value> GetUniqueSubEventName(const v8::Arguments& args); diff --git a/chrome/renderer/extensions/webstore_bindings.cc b/chrome/renderer/extensions/webstore_bindings.cc index 01209f1..275c04e 100644 --- a/chrome/renderer/extensions/webstore_bindings.cc +++ b/chrome/renderer/extensions/webstore_bindings.cc @@ -55,7 +55,7 @@ int g_next_install_id = 0; WebstoreBindings::WebstoreBindings(Dispatcher* dispatcher, ChromeV8Context* context) - : ChromeV8Extension(dispatcher), + : ChromeV8Extension(dispatcher, context->v8_context()), ChromeV8ExtensionHandler(context) { RouteFunction("Install", base::Bind(&WebstoreBindings::Install, base::Unretained(this))); @@ -63,7 +63,7 @@ WebstoreBindings::WebstoreBindings(Dispatcher* dispatcher, v8::Handle<v8::Value> WebstoreBindings::Install( const v8::Arguments& args) { - WebFrame* frame = WebFrame::frameForCurrentContext(); + WebFrame* frame = WebFrame::frameForContext(v8_context()); if (!frame || !frame->view()) return v8::Undefined(); diff --git a/chrome/renderer/resources/extensions/app_custom_bindings.js b/chrome/renderer/resources/extensions/app_custom_bindings.js index 1a8bbcc..0b9dd89 100644 --- a/chrome/renderer/resources/extensions/app_custom_bindings.js +++ b/chrome/renderer/resources/extensions/app_custom_bindings.js @@ -2,9 +2,11 @@ // 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. +// Custom binding for the app API. var appNatives = requireNative('app'); +var chrome = requireNative('chrome').GetChrome(); +var GetAvailability = requireNative('v8_context').GetAvailability; // This becomes chrome.app var app = { @@ -43,7 +45,7 @@ var chromeHiddenApp = { // appNotification stuff. // -// TODO(kalman): move this stuff to its own custom bindings. +// TODO(kalman): move this stuff to its own custom binding. // 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 = {}; @@ -67,8 +69,11 @@ app.installState = function getInstallState(callback) { appNatives.GetInstallState(callbackId); }; -// These must match the names in InstallAppBindings() in +// These must match the names in InstallAppbinding() in // chrome/renderer/extensions/dispatcher.cc. -exports.chromeApp = app; -exports.chromeAppNotifications = appNotifications; -exports.chromeHiddenApp = chromeHiddenApp; +var availability = GetAvailability('app'); +if (availability.is_available) { + exports.chromeApp = app; + exports.chromeAppNotifications = appNotifications; + exports.chromeHiddenApp = chromeHiddenApp; +} diff --git a/chrome/renderer/resources/extensions/app_runtime_custom_bindings.js b/chrome/renderer/resources/extensions/app_runtime_custom_bindings.js index 0a70fc4..a50c964 100644 --- a/chrome/renderer/resources/extensions/app_runtime_custom_bindings.js +++ b/chrome/renderer/resources/extensions/app_runtime_custom_bindings.js @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the chrome.app.runtime API. +// Custom binding for the chrome.app.runtime API. + +var binding = require('binding').Binding.create('app.runtime'); var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var chrome = requireNative('chrome').GetChrome(); var fileSystemHelpers = requireNative('file_system_natives'); var GetIsolatedFileSystem = fileSystemHelpers.GetIsolatedFileSystem; var appNatives = requireNative('app_runtime'); @@ -49,3 +52,5 @@ chromeHidden.Event.registerArgumentMassager('app.runtime.onLaunched', dispatch([]); } }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/app_window_custom_bindings.js b/chrome/renderer/resources/extensions/app_window_custom_bindings.js index f6223c7..83322cf 100644 --- a/chrome/renderer/resources/extensions/app_window_custom_bindings.js +++ b/chrome/renderer/resources/extensions/app_window_custom_bindings.js @@ -2,17 +2,21 @@ // 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_window API. +// Custom binding for the app_window API. +var Binding = require('binding').Binding; var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var chrome = requireNative('chrome').GetChrome(); var sendRequest = require('sendRequest').sendRequest; var appWindowNatives = requireNative('app_window'); var forEach = require('utils').forEach; var GetView = appWindowNatives.GetView; var OnContextReady = appWindowNatives.OnContextReady; -chromeHidden.registerCustomHook('app.window', function(bindingsAPI) { +var appWindow = Binding.create('app.window'); +appWindow.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; + apiFunctions.setCustomCallback('create', function(name, request, windowParams) { var view = null; @@ -81,10 +85,12 @@ chromeHidden.registerCustomHook('app.window', function(bindingsAPI) { // This is an internal function, but needs to be bound with setHandleRequest // because it is called from a different JS context. apiFunctions.setHandleRequest('initializeAppWindow', function(params) { + var currentWindowInternal = + Binding.create('app.currentWindowInternal').generate(); var AppWindow = function() {}; - forEach(chromeHidden.internalAPIs.app.currentWindowInternal, function(fn) { + forEach(currentWindowInternal, function(fn) { AppWindow.prototype[fn] = - chromeHidden.internalAPIs.app.currentWindowInternal[fn]; + currentWindowInternal[fn]; }); AppWindow.prototype.moveTo = window.moveTo.bind(window); AppWindow.prototype.resizeTo = window.resizeTo.bind(window); @@ -155,3 +161,5 @@ chromeHidden.updateAppWindowProperties = function(update) { (oldData.maximized && !update.maximized)) currentWindow["onRestored"].dispatch(); }; + +exports.binding = appWindow.generate(); diff --git a/chrome/renderer/resources/extensions/binding.js b/chrome/renderer/resources/extensions/binding.js new file mode 100644 index 0000000..d6fb4df --- /dev/null +++ b/chrome/renderer/resources/extensions/binding.js @@ -0,0 +1,407 @@ +// 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. + +require('json_schema'); +require('event_bindings'); +var schemaRegistry = requireNative('schema_registry'); +var sendRequest = require('sendRequest').sendRequest; +var utils = require('utils'); +var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var chrome = requireNative('chrome').GetChrome(); +var schemaUtils = require('schemaUtils'); +var process = requireNative('process'); +var manifestVersion = process.GetManifestVersion(); +var extensionId = process.GetExtensionId(); +var contextType = process.GetContextType(); +var GetAvailability = requireNative('v8_context').GetAvailability; +var logging = requireNative('logging'); + +// Stores the name and definition of each API function, with methods to +// modify their behaviour (such as a custom way to handle requests to the +// API, a custom callback, etc). +function APIFunctions() { + this.apiFunctions_ = {}; + this.unavailableApiFunctions_ = {}; +} + +APIFunctions.prototype.register = function(apiName, apiFunction) { + this.apiFunctions_[apiName] = apiFunction; +}; + +// Registers a function as existing but not available, meaning that calls to +// the set* methods that reference this function should be ignored rather +// than throwing Errors. +APIFunctions.prototype.registerUnavailable = function(apiName) { + this.unavailableApiFunctions_[apiName] = apiName; +}; + +APIFunctions.prototype.setHook_ = + function(apiName, propertyName, customizedFunction) { + if (this.unavailableApiFunctions_.hasOwnProperty(apiName)) + return; + if (!this.apiFunctions_.hasOwnProperty(apiName)) + throw new Error('Tried to set hook for unknown API "' + apiName + '"'); + this.apiFunctions_[apiName][propertyName] = customizedFunction; +}; + +APIFunctions.prototype.setHandleRequest = + function(apiName, customizedFunction) { + return this.setHook_(apiName, 'handleRequest', customizedFunction); +}; + +APIFunctions.prototype.setUpdateArgumentsPostValidate = + function(apiName, customizedFunction) { + return this.setHook_( + apiName, 'updateArgumentsPostValidate', customizedFunction); +}; + +APIFunctions.prototype.setUpdateArgumentsPreValidate = + function(apiName, customizedFunction) { + return this.setHook_( + apiName, 'updateArgumentsPreValidate', customizedFunction); +}; + +APIFunctions.prototype.setCustomCallback = + function(apiName, customizedFunction) { + return this.setHook_(apiName, 'customCallback', customizedFunction); +}; + +function CustomBindingsObject() { +} + +CustomBindingsObject.prototype.setSchema = function(schema) { + // The functions in the schema are in list form, so we move them into a + // dictionary for easier access. + var self = this; + self.functionSchemas = {}; + schema.functions.forEach(function(f) { + self.functionSchemas[f.name] = { + name: f.name, + definition: f + } + }); +}; + +// Get the platform from navigator.appVersion. +function getPlatform() { + var platforms = [ + [/CrOS Touch/, "chromeos touch"], + [/CrOS/, "chromeos"], + [/Linux/, "linux"], + [/Mac/, "mac"], + [/Win/, "win"], + ]; + + for (var i = 0; i < platforms.length; i++) { + if (platforms[i][0].test(navigator.appVersion)) { + return platforms[i][1]; + } + } + return "unknown"; +} + +function isPlatformSupported(schemaNode, platform) { + return !schemaNode.platforms || + schemaNode.platforms.indexOf(platform) > -1; +} + +function isManifestVersionSupported(schemaNode, manifestVersion) { + return !schemaNode.maximumManifestVersion || + manifestVersion <= schemaNode.maximumManifestVersion; +} + +function isSchemaNodeSupported(schemaNode, platform, manifestVersion) { + return isPlatformSupported(schemaNode, platform) && + isManifestVersionSupported(schemaNode, manifestVersion); +} + +var platform = getPlatform(); + +function Binding(schema) { + this.schema_ = schema; + this.apiFunctions_ = new APIFunctions(); + this.customEvent_ = null; + this.customTypes_ = {}; + this.customHooks_ = []; +}; + +Binding.create = function(apiName) { + return new Binding(schemaRegistry.GetSchema(apiName)); +}; + +Binding.prototype = { + // The API through which the ${api_name}_custom_bindings.js files customize + // their API bindings beyond what can be generated. + // + // There are 2 types of customizations available: those which are required in + // order to do the schema generation (registerCustomEvent and + // registerCustomType), and those which can only run after the bindings have + // been generated (registerCustomHook). + // + + // Registers a custom type referenced via "$ref" fields in the API schema + // JSON. + registerCustomType: function(typeName, customTypeFactory) { + var customType = customTypeFactory(); + customType.prototype = new CustomBindingsObject(); + this.customTypes_[typeName] = customType; + }, + + // Registers a custom event type for the API identified by |namespace|. + // |event| is the event's constructor. + registerCustomEvent: function(event) { + this.customEvent_ = event; + }, + + // Registers a function |hook| to run after the schema for all APIs has been + // generated. The hook is passed as its first argument an "API" object to + // interact with, and second the current extension ID. See where + // |customHooks| is used. + registerCustomHook: function(fn) { + this.customHooks_.push(fn); + }, + + // TODO(kalman/cduvall): Refactor this so |runHooks_| is not needed. + runHooks_: function(api) { + this.customHooks_.forEach(function(hook) { + if (!isSchemaNodeSupported(this.schema_, platform, manifestVersion)) + return; + + if (!hook) + return; + + hook({ + apiFunctions: this.apiFunctions_, + schema: this.schema_, + compiledApi: api + }, extensionId, contextType); + }, this); + }, + + // Generates the bindings from |this.schema_| and integrates any custom + // bindings that might be present. + generate: function() { + var schema = this.schema_; + var customTypes = this.customTypes_; + + // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the + // supporting code. + if (!isSchemaNodeSupported(schema, platform, manifestVersion)) + return; + + var availability = GetAvailability(schema.namespace); + if (!availability.is_available) { + console.error('chrome.' + schema.namespace + ' is not available: ' + + availability.message); + return; + } + + // See comment on internalAPIs at the top. + var mod = {}; + + var namespaces = schema.namespace.split('.'); + for (var index = 0, name; name = namespaces[index]; index++) { + mod[name] = mod[name] || {}; + mod = mod[name]; + } + + // Add types to global schemaValidator + if (schema.types) { + schema.types.forEach(function(t) { + if (!isSchemaNodeSupported(t, platform, manifestVersion)) + return; + + schemaUtils.schemaValidator.addTypes(t); + if (t.type == 'object' && this.customTypes_[t.id]) { + var parts = t.id.split("."); + this.customTypes_[t.id].prototype.setSchema(t); + mod[parts[parts.length - 1]] = this.customTypes_[t.id]; + } + }, this); + } + + // Returns whether access to the content of a schema should be denied, + // based on the presence of "unprivileged" and whether this is an + // extension process (versus e.g. a content script). + function isSchemaAccessAllowed(itemSchema) { + return (contextType == 'BLESSED_EXTENSION') || + schema.unprivileged || + itemSchema.unprivileged; + }; + + // Adds a getter that throws an access denied error to object |mod| + // for property |name|. + function addUnprivilegedAccessGetter(mod, name) { + mod.__defineGetter__(name, function() { + throw new Error( + '"' + name + '" can only be used in extension processes. See ' + + 'the content scripts documentation for more details.'); + }); + } + + // Setup Functions. + if (schema.functions) { + schema.functions.forEach(function(functionDef) { + if (functionDef.name in mod) { + throw new Error('Function ' + functionDef.name + + ' already defined in ' + schema.namespace); + } + + if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) { + this.apiFunctions_.registerUnavailable(functionDef.name); + return; + } + if (!isSchemaAccessAllowed(functionDef)) { + this.apiFunctions_.registerUnavailable(functionDef.name); + addUnprivilegedAccessGetter(mod, functionDef.name); + return; + } + + var apiFunction = {}; + apiFunction.definition = functionDef; + apiFunction.name = schema.namespace + '.' + functionDef.name; + + // TODO(aa): It would be best to run this in a unit test, but in order + // to do that we would need to better factor this code so that it + // doesn't depend on so much v8::Extension machinery. + if (chromeHidden.validateAPI && + schemaUtils.isFunctionSignatureAmbiguous( + apiFunction.definition)) { + throw new Error( + apiFunction.name + ' has ambiguous optional arguments. ' + + 'To implement custom disambiguation logic, add ' + + '"allowAmbiguousOptionalArguments" to the function\'s schema.'); + } + + this.apiFunctions_.register(functionDef.name, apiFunction); + + mod[functionDef.name] = (function() { + var args = Array.prototype.slice.call(arguments); + if (this.updateArgumentsPreValidate) + args = this.updateArgumentsPreValidate.apply(this, args); + + args = schemaUtils.normalizeArgumentsAndValidate(args, this); + if (this.updateArgumentsPostValidate) + args = this.updateArgumentsPostValidate.apply(this, args); + + var retval; + if (this.handleRequest) { + retval = this.handleRequest.apply(this, args); + } else { + var optArgs = { + customCallback: this.customCallback + }; + retval = sendRequest(this.name, args, + this.definition.parameters, + optArgs); + } + + // Validate return value if defined - only in debug. + if (chromeHidden.validateCallbacks && + this.definition.returns) { + schemaUtils.validate([retval], [this.definition.returns]); + } + return retval; + }).bind(apiFunction); + }, this); + } + + // Setup Events + if (schema.events) { + schema.events.forEach(function(eventDef) { + if (eventDef.name in mod) { + throw new Error('Event ' + eventDef.name + + ' already defined in ' + schema.namespace); + } + if (!isSchemaNodeSupported(eventDef, platform, manifestVersion)) + return; + if (!isSchemaAccessAllowed(eventDef)) { + addUnprivilegedAccessGetter(mod, eventDef.name); + return; + } + + var eventName = schema.namespace + "." + eventDef.name; + var options = eventDef.options || {}; + + if (eventDef.filters && eventDef.filters.length > 0) + options.supportsFilters = true; + + if (this.customEvent_) { + mod[eventDef.name] = new this.customEvent_( + eventName, eventDef.parameters, eventDef.extraParameters, + options); + } else if (eventDef.anonymous) { + mod[eventDef.name] = new chrome.Event(); + } else { + mod[eventDef.name] = new chrome.Event( + eventName, eventDef.parameters, options); + } + }, this); + } + + function addProperties(m, parentDef) { + var properties = parentDef.properties; + if (!properties) + return; + + utils.forEach(properties, function(propertyName, propertyDef) { + if (propertyName in m) + return; // TODO(kalman): be strict like functions/events somehow. + if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion)) + return; + if (!isSchemaAccessAllowed(propertyDef)) { + addUnprivilegedAccessGetter(m, propertyName); + return; + } + + var value = propertyDef.value; + if (value) { + // Values may just have raw types as defined in the JSON, such + // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here. + // TODO(kalman): enforce that things with a "value" property can't + // define their own types. + var type = propertyDef.type || typeof(value); + if (type === 'integer' || type === 'number') { + value = parseInt(value); + } else if (type === 'boolean') { + value = value === 'true'; + } else if (propertyDef['$ref']) { + if (propertyDef['$ref'] in customTypes) { + var constructor = customTypes[propertyDef['$ref']]; + } else { + var refParts = propertyDef['$ref'].split('.'); + // This should never try to load a $ref in the current namespace. + var constructor = utils.loadRefDependency( + propertyDef['$ref'])[refParts[refParts.length - 1]]; + } + if (!constructor) + throw new Error('No custom binding for ' + propertyDef['$ref']); + var args = value; + // For an object propertyDef, |value| is an array of constructor + // arguments, but we want to pass the arguments directly (i.e. + // not as an array), so we have to fake calling |new| on the + // constructor. + value = { __proto__: constructor.prototype }; + constructor.apply(value, args); + // Recursively add properties. + addProperties(value, propertyDef); + } else if (type === 'object') { + // Recursively add properties. + addProperties(value, propertyDef); + } else if (type !== 'string') { + throw new Error('NOT IMPLEMENTED (extension_api.json error): ' + + 'Cannot parse values for type "' + type + '"'); + } + m[propertyName] = value; + } + }); + }; + + addProperties(mod, schema); + this.runHooks_(mod); + return mod; + } +}; + +exports.Binding = Binding; diff --git a/chrome/renderer/resources/extensions/bluetooth_custom_bindings.js b/chrome/renderer/resources/extensions/bluetooth_custom_bindings.js index e1f75f5..273a094 100644 --- a/chrome/renderer/resources/extensions/bluetooth_custom_bindings.js +++ b/chrome/renderer/resources/extensions/bluetooth_custom_bindings.js @@ -2,24 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the Bluetooth API. +// Custom binding for the Bluetooth API. + +var binding = require('binding').Binding.create('bluetooth'); var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var chrome = requireNative('chrome').GetChrome(); var sendRequest = require('sendRequest').sendRequest; var lastError = require('lastError'); -// Use custom bindings to create an undocumented event listener that will +// Use custom binding to create an undocumented event listener that will // receive events about device discovery and call the event listener that was // provided with the request to begin discovery. -chromeHidden.registerCustomHook('bluetooth', function(api) { +binding.registerCustomHook(function(api) { var apiFunctions = api.apiFunctions; chromeHidden.bluetooth = {}; - function callCallbackIfPresent(args) { - if (typeof(args[args.length-1]) == "function") { - args[args.length-1](); - } + function callCallbackIfPresent(args, error) { + var callback = args[args.length - 1]; + if (typeof(callback) == "function") + lastError.run(error, callback); } chromeHidden.bluetooth.deviceDiscoveredHandler = null; @@ -35,8 +38,7 @@ chromeHidden.registerCustomHook('bluetooth', function(api) { var args = arguments; if (args.length > 0 && args[0] && args[0].deviceCallback) { if (chromeHidden.bluetooth.deviceDiscoveredHandler != null) { - lastError.set("Concurrent discovery is not allowed."); - callCallbackIfPresent(args); + callCallbackIfPresent(args, "Concurrent discovery is not allowed."); return; } @@ -49,8 +51,9 @@ chromeHidden.registerCustomHook('bluetooth', function(api) { this.definition.parameters, {customCallback:this.customCallback}); } else { - lastError.set("deviceCallback is required in the options object"); - callCallbackIfPresent(args); + callCallbackIfPresent( + args, "deviceCallback is required in the options object"); + return; } }); apiFunctions.setCustomCallback('startDiscovery', @@ -154,3 +157,5 @@ chromeHidden.registerCustomHook('bluetooth', function(api) { return args; }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/browser_action_custom_bindings.js b/chrome/renderer/resources/extensions/browser_action_custom_bindings.js index 9d1d92e..d90ab18 100644 --- a/chrome/renderer/resources/extensions/browser_action_custom_bindings.js +++ b/chrome/renderer/resources/extensions/browser_action_custom_bindings.js @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the browserAction API. +// Custom binding for the browserAction API. + +var binding = require('binding').Binding.create('browserAction'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var setIcon = require('setIcon').setIcon; -chromeHidden.registerCustomHook('browserAction', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setHandleRequest('setIcon', function(details, callback) { @@ -15,3 +16,5 @@ chromeHidden.registerCustomHook('browserAction', function(bindingsAPI) { 'browser action'); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/content_settings_custom_bindings.js b/chrome/renderer/resources/extensions/content_settings_custom_bindings.js index d92efc1..8347078 100644 --- a/chrome/renderer/resources/extensions/content_settings_custom_bindings.js +++ b/chrome/renderer/resources/extensions/content_settings_custom_bindings.js @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the contentSettings API. +// Custom binding for the contentSettings API. + +var binding = require('binding').Binding.create('contentSettings'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var sendRequest = require('sendRequest').sendRequest; var validate = require('schemaUtils').validate; -chromeHidden.registerCustomType('contentSettings.ContentSetting', function() { +binding.registerCustomType('contentSettings.ContentSetting', function() { function extendSchema(schema) { var extendedSchema = schema.slice(); extendedSchema.unshift({'type': 'string'}); @@ -51,3 +52,5 @@ chromeHidden.registerCustomType('contentSettings.ContentSetting', function() { return ContentSetting; }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/context_menus_custom_bindings.js b/chrome/renderer/resources/extensions/context_menus_custom_bindings.js index e2629f7..4b0a936 100644 --- a/chrome/renderer/resources/extensions/context_menus_custom_bindings.js +++ b/chrome/renderer/resources/extensions/context_menus_custom_bindings.js @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the contextMenus API. +// Custom binding for the contextMenus API. + +var binding = require('binding').Binding.create('contextMenus'); var contextMenus = requireNative('context_menus'); var GetNextContextMenuId = contextMenus.GetNextContextMenuId; @@ -10,7 +12,7 @@ var sendRequest = require('sendRequest').sendRequest; var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); -chromeHidden.registerCustomHook('contextMenus', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; chromeHidden.contextMenus = {}; @@ -98,3 +100,5 @@ chromeHidden.registerCustomHook('contextMenus', function(bindingsAPI) { chromeHidden.contextMenus.stringIdHandlers = {}; }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js b/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js index 3655c46..066413e 100644 --- a/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js +++ b/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js @@ -2,19 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the declarativeContent API. +// Custom binding for the declarativeContent API. + +var binding = require('binding').Binding.create('declarativeContent'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var utils = require('utils'); var validate = require('schemaUtils').validate; -chromeHidden.registerCustomHook('declarativeContent', function(api) { +binding.registerCustomHook( function(api) { + var declarativeContent = api.compiledApi; + // Returns the schema definition of type |typeId| defined in |namespace|. - function getSchema(namespace, typeId) { - var apiSchema = utils.lookup(api.apiDefinitions, 'namespace', namespace); - var resultSchema = utils.lookup( - apiSchema.types, 'id', namespace + '.' + typeId); - return resultSchema; + function getSchema(typeId) { + return utils.lookup(api.schema.types, + 'id', + 'declarativeContent.' + typeId); } // Helper function for the constructor of concrete datatypes of the @@ -29,15 +31,17 @@ chromeHidden.registerCustomHook('declarativeContent', function(api) { } } instance.instanceType = 'declarativeContent.' + typeId; - var schema = getSchema('declarativeContent', typeId); + var schema = getSchema(typeId); validate([instance], [schema]); } // Setup all data types for the declarative content API. - chrome.declarativeContent.PageStateMatcher = function(parameters) { + declarativeContent.PageStateMatcher = function(parameters) { setupInstance(this, parameters, 'PageStateMatcher'); }; - chrome.declarativeContent.ShowPageAction = function(parameters) { + declarativeContent.ShowPageAction = function(parameters) { setupInstance(this, parameters, 'ShowPageAction'); }; }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/declarative_webrequest_custom_bindings.js b/chrome/renderer/resources/extensions/declarative_webrequest_custom_bindings.js index f015bf7..9d0c3c5 100644 --- a/chrome/renderer/resources/extensions/declarative_webrequest_custom_bindings.js +++ b/chrome/renderer/resources/extensions/declarative_webrequest_custom_bindings.js @@ -2,19 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the declarativeWebRequest API. +// Custom binding for the declarativeWebRequest API. + +var binding = require('binding').Binding.create('declarativeWebRequest'); var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var chrome = requireNative('chrome').GetChrome(); var utils = require('utils'); var validate = require('schemaUtils').validate; -chromeHidden.registerCustomHook('declarativeWebRequest', function(api) { +binding.registerCustomHook(function(api) { + var declarativeWebRequest = api.compiledApi; + // Returns the schema definition of type |typeId| defined in |namespace|. - function getSchema(namespace, typeId) { - var apiSchema = utils.lookup(api.apiDefinitions, 'namespace', namespace); - var resultSchema = utils.lookup( - apiSchema.types, 'id', namespace + '.' + typeId); - return resultSchema; + function getSchema(typeId) { + return utils.lookup(api.schema.types, + 'id', + 'declarativeWebRequest.' + typeId); } // Helper function for the constructor of concrete datatypes of the @@ -29,64 +33,66 @@ chromeHidden.registerCustomHook('declarativeWebRequest', function(api) { } } instance.instanceType = 'declarativeWebRequest.' + typeId; - var schema = getSchema('declarativeWebRequest', typeId); + var schema = getSchema(typeId); validate([instance], [schema]); } // Setup all data types for the declarative webRequest API. - chrome.declarativeWebRequest.RequestMatcher = function(parameters) { + declarativeWebRequest.RequestMatcher = function(parameters) { setupInstance(this, parameters, 'RequestMatcher'); }; - chrome.declarativeWebRequest.CancelRequest = function(parameters) { + declarativeWebRequest.CancelRequest = function(parameters) { setupInstance(this, parameters, 'CancelRequest'); }; - chrome.declarativeWebRequest.RedirectRequest = function(parameters) { + declarativeWebRequest.RedirectRequest = function(parameters) { setupInstance(this, parameters, 'RedirectRequest'); }; - chrome.declarativeWebRequest.SetRequestHeader = function(parameters) { + declarativeWebRequest.SetRequestHeader = function(parameters) { setupInstance(this, parameters, 'SetRequestHeader'); }; - chrome.declarativeWebRequest.RemoveRequestHeader = function(parameters) { + declarativeWebRequest.RemoveRequestHeader = function(parameters) { setupInstance(this, parameters, 'RemoveRequestHeader'); }; - chrome.declarativeWebRequest.AddResponseHeader = function(parameters) { + declarativeWebRequest.AddResponseHeader = function(parameters) { setupInstance(this, parameters, 'AddResponseHeader'); }; - chrome.declarativeWebRequest.RemoveResponseHeader = function(parameters) { + declarativeWebRequest.RemoveResponseHeader = function(parameters) { setupInstance(this, parameters, 'RemoveResponseHeader'); }; - chrome.declarativeWebRequest.RedirectToTransparentImage = + declarativeWebRequest.RedirectToTransparentImage = function(parameters) { setupInstance(this, parameters, 'RedirectToTransparentImage'); }; - chrome.declarativeWebRequest.RedirectToEmptyDocument = function(parameters) { + declarativeWebRequest.RedirectToEmptyDocument = function(parameters) { setupInstance(this, parameters, 'RedirectToEmptyDocument'); }; - chrome.declarativeWebRequest.RedirectByRegEx = function(parameters) { + declarativeWebRequest.RedirectByRegEx = function(parameters) { setupInstance(this, parameters, 'RedirectByRegEx'); }; - chrome.declarativeWebRequest.IgnoreRules = function(parameters) { + declarativeWebRequest.IgnoreRules = function(parameters) { setupInstance(this, parameters, 'IgnoreRules'); }; - chrome.declarativeWebRequest.AddRequestCookie = function(parameters) { + declarativeWebRequest.AddRequestCookie = function(parameters) { setupInstance(this, parameters, 'AddRequestCookie'); }; - chrome.declarativeWebRequest.AddResponseCookie = function(parameters) { + declarativeWebRequest.AddResponseCookie = function(parameters) { setupInstance(this, parameters, 'AddResponseCookie'); }; - chrome.declarativeWebRequest.EditRequestCookie = function(parameters) { + declarativeWebRequest.EditRequestCookie = function(parameters) { setupInstance(this, parameters, 'EditRequestCookie'); }; - chrome.declarativeWebRequest.EditResponseCookie = function(parameters) { + declarativeWebRequest.EditResponseCookie = function(parameters) { setupInstance(this, parameters, 'EditResponseCookie'); }; - chrome.declarativeWebRequest.RemoveRequestCookie = function(parameters) { + declarativeWebRequest.RemoveRequestCookie = function(parameters) { setupInstance(this, parameters, 'RemoveRequestCookie'); }; - chrome.declarativeWebRequest.RemoveResponseCookie = function(parameters) { + declarativeWebRequest.RemoveResponseCookie = function(parameters) { setupInstance(this, parameters, 'RemoveResponseCookie'); }; - chrome.declarativeWebRequest.SendMessageToExtension = function(parameters) { + declarativeWebRequest.SendMessageToExtension = function(parameters) { setupInstance(this, parameters, 'SendMessageToExtension'); }; }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/event.js b/chrome/renderer/resources/extensions/event.js index 4e99256..59e04d5 100644 --- a/chrome/renderer/resources/extensions/event.js +++ b/chrome/renderer/resources/extensions/event.js @@ -3,6 +3,8 @@ // found in the LICENSE file. var DCHECK = requireNative('logging').DCHECK; + // TODO(cduvall/kalman): json_schema shouldn't put things on chromeHidden. + require('json_schema'); var eventBindingsNatives = requireNative('event_bindings'); var AttachEvent = eventBindingsNatives.AttachEvent; var DetachEvent = eventBindingsNatives.DetachEvent; @@ -14,8 +16,8 @@ var validate = require('schemaUtils').validate; var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); - var GetExtensionAPIDefinition = - requireNative('apiDefinitions').GetExtensionAPIDefinition; + var chrome = requireNative('chrome').GetChrome(); + var schemaRegistry = requireNative('schema_registry'); // Schemas for the rule-style functions on the events API that // only need to be generated occasionally, so populate them lazily. @@ -30,7 +32,7 @@ function ensureRuleSchemasLoaded() { if (ruleFunctionSchemas.addRules) return; - var eventsSchema = GetExtensionAPIDefinition("events")[0]; + var eventsSchema = schemaRegistry.GetSchema("events"); var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event'); ruleFunctionSchemas.addRules = @@ -151,7 +153,7 @@ // // If opt_eventOptions exists, it is a dictionary that contains the boolean // entries "supportsListeners" and "supportsRules". - chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { + var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) { this.eventName_ = opt_eventName; this.listeners_ = []; this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions); @@ -227,7 +229,7 @@ }; // Registers a callback to be called when this event is dispatched. - chrome.Event.prototype.addListener = function(cb, filters) { + Event.prototype.addListener = function(cb, filters) { if (!this.eventOptions_.supportsListeners) throw new Error("This event does not support listeners."); if (this.eventOptions_.maxListeners && @@ -244,7 +246,7 @@ this.listeners_.push(listener); }; - chrome.Event.prototype.attach_ = function(listener) { + Event.prototype.attach_ = function(listener) { this.attachmentStrategy_.onAddedListener(listener); if (this.listeners_.length == 0) { allAttachedEvents[allAttachedEvents.length] = this; @@ -261,7 +263,7 @@ }; // Unregisters a callback. - chrome.Event.prototype.removeListener = function(cb) { + Event.prototype.removeListener = function(cb) { if (!this.eventOptions_.supportsListeners) throw new Error("This event does not support listeners."); var idx = this.findListener_(cb); @@ -289,19 +291,19 @@ }; // Test if the given callback is registered for this event. - chrome.Event.prototype.hasListener = function(cb) { + Event.prototype.hasListener = function(cb) { if (!this.eventOptions_.supportsListeners) throw new Error("This event does not support listeners."); return this.findListener_(cb) > -1; }; // Test if any callbacks are registered for this event. - chrome.Event.prototype.hasListeners = function() { + Event.prototype.hasListeners = function() { return this.getListenerCount() > 0; }; // Return the number of listeners on this event. - chrome.Event.prototype.getListenerCount = function() { + Event.prototype.getListenerCount = function() { if (!this.eventOptions_.supportsListeners) throw new Error("This event does not support listeners."); return this.listeners_.length; @@ -309,7 +311,7 @@ // Returns the index of the given callback if registered, or -1 if not // found. - chrome.Event.prototype.findListener_ = function(cb) { + Event.prototype.findListener_ = function(cb) { for (var i = 0; i < this.listeners_.length; i++) { if (this.listeners_[i].callback == cb) { return i; @@ -319,7 +321,7 @@ return -1; }; - chrome.Event.prototype.dispatch_ = function(args, listenerIDs) { + Event.prototype.dispatch_ = function(args, listenerIDs) { if (!this.eventOptions_.supportsListeners) throw new Error("This event does not support listeners."); var validationErrors = this.validateEventArgs_(args); @@ -349,28 +351,28 @@ } // Can be overridden to support custom dispatching. - chrome.Event.prototype.dispatchToListener = function(callback, args) { + Event.prototype.dispatchToListener = function(callback, args) { return callback.apply(null, args); } // Dispatches this event object to all listeners, passing all supplied // arguments to this function each listener. - chrome.Event.prototype.dispatch = function(varargs) { + Event.prototype.dispatch = function(varargs) { return this.dispatch_(Array.prototype.slice.call(arguments), undefined); }; // Detaches this event object from its name. - chrome.Event.prototype.detach_ = function() { + Event.prototype.detach_ = function() { this.attachmentStrategy_.detach(false); }; - chrome.Event.prototype.destroy_ = function() { + Event.prototype.destroy_ = function() { this.listeners_ = []; this.validateEventArgs_ = []; this.detach_(false); }; - chrome.Event.prototype.addRules = function(rules, opt_cb) { + Event.prototype.addRules = function(rules, opt_cb) { if (!this.eventOptions_.supportsRules) throw new Error("This event does not support rules."); @@ -420,7 +422,7 @@ ruleFunctionSchemas.addRules.parameters); } - chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { + Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) { if (!this.eventOptions_.supportsRules) throw new Error("This event does not support rules."); ensureRuleSchemasLoaded(); @@ -433,7 +435,7 @@ ruleFunctionSchemas.removeRules.parameters); } - chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) { + Event.prototype.getRules = function(ruleIdentifiers, cb) { if (!this.eventOptions_.supportsRules) throw new Error("This event does not support rules."); ensureRuleSchemasLoaded(); @@ -450,8 +452,8 @@ // Special load events: we don't use the DOM unload because that slows // down tab shutdown. On the other hand, onUnload might not always fire, // since Chrome will terminate renderers on shutdown (SuddenTermination). - chromeHidden.onLoad = new chrome.Event(); - chromeHidden.onUnload = new chrome.Event(); + chromeHidden.onLoad = new Event(); + chromeHidden.onUnload = new Event(); chromeHidden.dispatchOnLoad = chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad); @@ -471,4 +473,4 @@ console.error(msg); }; - exports.Event = chrome.Event; + chrome.Event = Event; diff --git a/chrome/renderer/resources/extensions/experimental.media_galleries_custom_bindings.js b/chrome/renderer/resources/extensions/experimental.media_galleries_custom_bindings.js index 07b33d4..8f82c4e 100644 --- a/chrome/renderer/resources/extensions/experimental.media_galleries_custom_bindings.js +++ b/chrome/renderer/resources/extensions/experimental.media_galleries_custom_bindings.js @@ -2,14 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the Media Gallery API. +// Custom binding for the Media Gallery API. -var mediaGalleriesNatives = requireNative('mediaGalleries'); +var binding = require('binding').Binding.create('experimental.mediaGalleries'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var mediaGalleriesNatives = requireNative('mediaGalleries'); -chromeHidden.registerCustomHook('experimental.mediaGalleries', - function(bindingsAPI, extensionId) { +binding.registerCustomHook(function(bindingsAPI, extensionId) { var apiFunctions = bindingsAPI.apiFunctions; // extractEmbeddedThumbnails uses a renderer side handler so that it can @@ -20,3 +19,5 @@ chromeHidden.registerCustomHook('experimental.mediaGalleries', return mediaGalleriesNatives.ExtractEmbeddedThumbnails(fileEntry); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js b/chrome/renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js index 3635a2c..7903a3d 100644 --- a/chrome/renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js +++ b/chrome/renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js @@ -2,13 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the experimental offscreenTabs API. +// Custom binding for the experimental offscreenTabs API. -(function() { +var binding = require('binding').Binding.create('experimental.offscreenTabs'); -native function GetChromeHidden(); - -GetChromeHidden().registerCustomHook( +binding.registerCustomHook( 'experimental.offscreenTabs', function(api) { var apiFunctions = api.apiFunctions; @@ -60,4 +58,4 @@ GetChromeHidden().registerCustomHook( function() { return validate(arguments, mouseEventFilter); }); }); -})(); +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/extension_custom_bindings.js b/chrome/renderer/resources/extensions/extension_custom_bindings.js index 60b4f12..61e5e16b 100644 --- a/chrome/renderer/resources/extensions/extension_custom_bindings.js +++ b/chrome/renderer/resources/extensions/extension_custom_bindings.js @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the extension API. +// Custom binding for the extension API. + +var binding = require('binding').Binding.create('extension'); var extensionNatives = requireNative('extension'); var GetExtensionViews = extensionNatives.GetExtensionViews; @@ -10,22 +12,14 @@ var runtimeNatives = requireNative('runtime'); var OpenChannelToExtension = runtimeNatives.OpenChannelToExtension; var OpenChannelToNativeApp = runtimeNatives.OpenChannelToNativeApp; var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var chrome = requireNative('chrome').GetChrome(); var sendMessageUpdateArguments = require('miscellaneous_bindings').sendMessageUpdateArguments; var inIncognitoContext = requireNative('process').InIncognitoContext(); var sendRequestIsDisabled = requireNative('process').IsSendRequestDisabled(); var contextType = requireNative('process').GetContextType(); - -chrome.extension = chrome.extension || {}; - var manifestVersion = requireNative('process').GetManifestVersion(); -if (manifestVersion < 2) { - chrome.self = chrome.extension; - chrome.extension.inIncognitoTab = inIncognitoContext; -} - -chrome.extension.inIncognitoContext = inIncognitoContext; // This should match chrome.windows.WINDOW_ID_NONE. // @@ -34,8 +28,14 @@ chrome.extension.inIncognitoContext = inIncognitoContext; // which may not be the case. var WINDOW_ID_NONE = -1; -chromeHidden.registerCustomHook('extension', - function(bindingsAPI, extensionId) { +binding.registerCustomHook(function(bindingsAPI, extensionId) { + var extension = bindingsAPI.compiledApi; + if (manifestVersion < 2) { + chrome.self = extension; + extension.inIncognitoTab = inIncognitoContext; + } + extension.inIncognitoContext = inIncognitoContext; + var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setHandleRequest('getViews', function(properties) { @@ -83,7 +83,7 @@ chromeHidden.registerCustomHook('extension', // getters that throw exceptions. Assume that any getter is such a function. if (chrome.runtime.hasOwnProperty(alias) && chrome.runtime.__lookupGetter__(alias) === undefined) { - chrome.extension[alias] = chrome.runtime[alias]; + extension[alias] = chrome.runtime[alias]; } }); @@ -100,14 +100,15 @@ chromeHidden.registerCustomHook('extension', }); if (sendRequestIsDisabled) { - chrome.extension.onRequest.addListener = function() { + extension.onRequest.addListener = function() { throw new Error(sendRequestIsDisabled); }; if (contextType == 'BLESSED_EXTENSION') { - chrome.extension.onRequestExternal.addListener = function() { + extension.onRequestExternal.addListener = function() { throw new Error(sendRequestIsDisabled); }; } } - }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/file_browser_handler_custom_bindings.js b/chrome/renderer/resources/extensions/file_browser_handler_custom_bindings.js index 17af969..1fbe7b08c 100644 --- a/chrome/renderer/resources/extensions/file_browser_handler_custom_bindings.js +++ b/chrome/renderer/resources/extensions/file_browser_handler_custom_bindings.js @@ -2,12 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the fileBrowserHandler API. +// Custom binding for the fileBrowserHandler API. + +var binding = require('binding').Binding.create('fileBrowserHandler'); var fileBrowserNatives = requireNative('file_browser_handler'); var GetExternalFileEntry = fileBrowserNatives.GetExternalFileEntry; var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var fileBrowserHandlerInternal = require('binding').Binding.create( + 'fileBrowserHandlerInternal').generate(); chromeHidden.Event.registerArgumentMassager('fileBrowserHandler.onExecute', function(args, dispatch) { @@ -28,7 +32,7 @@ chromeHidden.Event.registerArgumentMassager('fileBrowserHandler.onExecute', dispatch(args); }); -chromeHidden.registerCustomHook('fileBrowserHandler', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setHandleRequest('selectFile', @@ -46,7 +50,9 @@ chromeHidden.registerCustomHook('fileBrowserHandler', function(bindingsAPI) { externalCallback(result); } - return chromeHidden.internalAPIs.fileBrowserHandlerInternal.selectFile( + return fileBrowserHandlerInternal.selectFile( selectionParams, internalCallback.bind(null, callback)); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/file_browser_private_custom_bindings.js b/chrome/renderer/resources/extensions/file_browser_private_custom_bindings.js index 0d9369d..10c616e 100644 --- a/chrome/renderer/resources/extensions/file_browser_private_custom_bindings.js +++ b/chrome/renderer/resources/extensions/file_browser_private_custom_bindings.js @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the fileBrowserPrivate API. +// Custom binding for the fileBrowserPrivate API. + +var binding = require('binding').Binding.create('fileBrowserPrivate'); var fileBrowserPrivateNatives = requireNative('file_browser_private'); var GetLocalFileSystem = fileBrowserPrivateNatives.GetLocalFileSystem; @@ -10,9 +12,7 @@ var GetLocalFileSystem = fileBrowserPrivateNatives.GetLocalFileSystem; var fileBrowserNatives = requireNative('file_browser_handler'); var GetExternalFileEntry = fileBrowserNatives.GetExternalFileEntry; -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); - -chromeHidden.registerCustomHook('fileBrowserPrivate', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setCustomCallback('requestLocalFileSystem', @@ -61,3 +61,5 @@ chromeHidden.registerCustomHook('fileBrowserPrivate', function(bindingsAPI) { request.callback = null; }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/file_system_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_custom_bindings.js index 29e20e7..6a34439 100644 --- a/chrome/renderer/resources/extensions/file_system_custom_bindings.js +++ b/chrome/renderer/resources/extensions/file_system_custom_bindings.js @@ -2,16 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the fileSystem API. +// Custom binding for the fileSystem API. + +var binding = require('binding').Binding.create('fileSystem'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var fileSystemNatives = requireNative('file_system_natives'); var GetIsolatedFileSystem = fileSystemNatives.GetIsolatedFileSystem; var lastError = require('lastError'); var entryIdManager = require('entryIdManager'); -chromeHidden.registerCustomHook('fileSystem', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; + var fileSystem = bindingsAPI.compiledApi; + function bindFileEntryFunction(functionName) { apiFunctions.setUpdateArgumentsPostValidate( functionName, function(fileEntry, callback) { @@ -44,12 +47,12 @@ chromeHidden.registerCustomHook('fileSystem', function(bindingsAPI) { entryIdManager.registerEntry(id, fileEntry); callback(fileEntry); }, function(fileError) { - lastError.set('Error getting fileEntry, code: ' + fileError.code); - callback(); + lastError.run('Error getting fileEntry, code: ' + fileError.code, + callback); }); } catch (e) { - lastError.set('Error in event handler for onLaunched: ' + e.stack); - callback(); + lastError.run('Error in event handler for onLaunched: ' + e.stack, + callback); } } }); @@ -65,21 +68,23 @@ chromeHidden.registerCustomHook('fileSystem', function(bindingsAPI) { }); // TODO(benwells): Remove these deprecated versions of the functions. - chrome.fileSystem.getWritableFileEntry = function() { + fileSystem.getWritableFileEntry = function() { console.log("chrome.fileSystem.getWritableFileEntry is deprecated"); console.log("Please use chrome.fileSystem.getWritableEntry instead"); - chrome.fileSystem.getWritableEntry.apply(this, arguments); + fileSystem.getWritableEntry.apply(this, arguments); }; - chrome.fileSystem.isWritableFileEntry = function() { + fileSystem.isWritableFileEntry = function() { console.log("chrome.fileSystem.isWritableFileEntry is deprecated"); console.log("Please use chrome.fileSystem.isWritableEntry instead"); - chrome.fileSystem.isWritableEntry.apply(this, arguments); + fileSystem.isWritableEntry.apply(this, arguments); }; - chrome.fileSystem.chooseFile = function() { + fileSystem.chooseFile = function() { console.log("chrome.fileSystem.chooseFile is deprecated"); console.log("Please use chrome.fileSystem.chooseEntry instead"); - chrome.fileSystem.chooseEntry.apply(this, arguments); + fileSystem.chooseEntry.apply(this, arguments); }; }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/i18n_custom_bindings.js b/chrome/renderer/resources/extensions/i18n_custom_bindings.js index 14120af..39f5a7d 100644 --- a/chrome/renderer/resources/extensions/i18n_custom_bindings.js +++ b/chrome/renderer/resources/extensions/i18n_custom_bindings.js @@ -2,14 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the i18n API. +// Custom binding for the i18n API. + +var binding = require('binding').Binding.create('i18n'); var i18nNatives = requireNative('i18n'); var GetL10nMessage = i18nNatives.GetL10nMessage; var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); -chromeHidden.registerCustomHook('i18n', function(bindingsAPI, extensionId) { +binding.registerCustomHook(function(bindingsAPI, extensionId) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setUpdateArgumentsPreValidate('getMessage', function() { @@ -32,3 +34,5 @@ chromeHidden.registerCustomHook('i18n', function(bindingsAPI, extensionId) { return GetL10nMessage(messageName, substitutions, extensionId); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/input.ime_custom_bindings.js b/chrome/renderer/resources/extensions/input.ime_custom_bindings.js index 60b0105..79ab4a3 100644 --- a/chrome/renderer/resources/extensions/input.ime_custom_bindings.js +++ b/chrome/renderer/resources/extensions/input.ime_custom_bindings.js @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the input ime API. Only injected into the +// Custom binding for the input ime API. Only injected into the // v8 contexts for extensions which have permission for the API. -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var binding = require('binding').Binding.create('input.ime'); -chromeHidden.registerCustomHook('input.ime', function() { - chrome.input.ime.onKeyEvent.dispatchToListener = function(callback, args) { +binding.registerCustomHook(function(api) { + var input_ime = api.compiledApi; + + input_ime.onKeyEvent.dispatchToListener = function(callback, args) { var engineID = args[0]; var keyData = args[1]; @@ -18,19 +20,21 @@ chromeHidden.registerCustomHook('input.ime', function() { } catch (e) { console.error('Error in event handler for onKeyEvent: ' + e.stack); } - if (!chrome.input.ime.onKeyEvent.async) - chrome.input.ime.keyEventHandled(keyData.requestId, result); + if (!input_ime.onKeyEvent.async) + input_ime.keyEventHandled(keyData.requestId, result); }; - chrome.input.ime.onKeyEvent.addListener = function(cb, opt_extraInfo) { - chrome.input.ime.onKeyEvent.async = false; + input_ime.onKeyEvent.addListener = function(cb, opt_extraInfo) { + input_ime.onKeyEvent.async = false; if (opt_extraInfo instanceof Array) { for (var i = 0; i < opt_extraInfo.length; ++i) { if (opt_extraInfo[i] == "async") { - chrome.input.ime.onKeyEvent.async = true; + input_ime.onKeyEvent.async = true; } } } chrome.Event.prototype.addListener.call(this, cb, null); }; }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/json_schema.js b/chrome/renderer/resources/extensions/json_schema.js index 414356c..44ea2a06 100644 --- a/chrome/renderer/resources/extensions/json_schema.js +++ b/chrome/renderer/resources/extensions/json_schema.js @@ -38,7 +38,9 @@ // additional properties will be validated. //============================================================================== +// TODO(cduvall): Make this file not depend on chromeHidden. var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var loadRefDependency = require('utils').loadRefDependency; function isInstanceOfClass(instance, className) { if (!instance) @@ -240,6 +242,7 @@ chromeHidden.JSONSchemaValidator.prototype.validate = // If the schema has a $ref property, the instance must validate against // that schema too. It must be present in this.types to be referenced. if (schema["$ref"]) { + loadRefDependency(schema["$ref"]); if (!this.types[schema["$ref"]]) this.addError(path, "unknownSchemaReference", [ schema["$ref"] ]); else diff --git a/chrome/renderer/resources/extensions/last_error.js b/chrome/renderer/resources/extensions/last_error.js index 2b38a29..25c48c6 100644 --- a/chrome/renderer/resources/extensions/last_error.js +++ b/chrome/renderer/resources/extensions/last_error.js @@ -2,20 +2,57 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -requireNative('runtime'); +var DCHECK = requireNative('logging').DCHECK; +var GetAvailability = requireNative('v8_context').GetAvailability; +var GetGlobal = requireNative('sendRequest').GetGlobal; -function set(message) { +// Utility for setting chrome.*.lastError. +// +// A utility here is useful for two reasons: +// 1. For backwards compatibility we need to set chrome.extension.lastError, +// but not all contexts actually have access to the extension namespace. +// 2. When calling across contexts, the global object that gets lastError set +// needs to be that of the caller. We force callers to explicitly specify +// the chrome object to try to prevent bugs here. + +/** + * Sets the last error on |targetChrome| to |message|. + */ +function set(message, targetChrome) { + DCHECK(targetChrome != undefined); + clear(targetChrome); // in case somebody has set a sneaky getter/setter var errorObject = { 'message': message }; - if (chrome.extension) - chrome.extension.lastError = errorObject; - chrome.runtime.lastError = errorObject; + if (GetAvailability('extension').is_available) + targetChrome.extension.lastError = errorObject; + targetChrome.runtime.lastError = errorObject; }; -function clear() { - if (chrome.extension) - delete chrome.extension.lastError; - delete chrome.runtime.lastError; +/** + * Clears the last error on |targetChrome|. + */ +function clear(targetChrome) { + DCHECK(targetChrome != undefined); + if (GetAvailability('extension').is_available) + delete targetChrome.extension.lastError; + delete targetChrome.runtime.lastError; }; +/** + * Runs |callback(args)| with last error set to |message|. + * + * The target chrome object is the global object's of the callback, so this + * method won't work if the real callback has been wrapped (etc). + */ +function run(message, callback, args) { + var targetChrome = GetGlobal(callback).chrome; + set(message, targetChrome); + try { + callback.apply(undefined, args); + } finally { + clear(targetChrome); + } +} + exports.clear = clear; exports.set = set; +exports.run = run; diff --git a/chrome/renderer/resources/extensions/media_galleries_custom_bindings.js b/chrome/renderer/resources/extensions/media_galleries_custom_bindings.js index f1f9edfe..60ac567 100644 --- a/chrome/renderer/resources/extensions/media_galleries_custom_bindings.js +++ b/chrome/renderer/resources/extensions/media_galleries_custom_bindings.js @@ -2,16 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the Media Gallery API. +// Custom binding for the Media Gallery API. -var mediaGalleriesNatives = requireNative('mediaGalleries'); +var binding = require('binding').Binding.create('mediaGalleries'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var mediaGalleriesNatives = requireNative('mediaGalleries'); var mediaGalleriesMetadata = {}; -chromeHidden.registerCustomHook('mediaGalleries', - function(bindingsAPI, extensionId) { +binding.registerCustomHook(function(bindingsAPI, extensionId) { var apiFunctions = bindingsAPI.apiFunctions; // getMediaFileSystems uses a custom callback so that it can instantiate and @@ -48,3 +47,5 @@ chromeHidden.registerCustomHook('mediaGalleries', return {}; }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/miscellaneous_bindings.js b/chrome/renderer/resources/extensions/miscellaneous_bindings.js index fc8e2cc..78bed08 100644 --- a/chrome/renderer/resources/extensions/miscellaneous_bindings.js +++ b/chrome/renderer/resources/extensions/miscellaneous_bindings.js @@ -8,10 +8,10 @@ // content scripts only. require('json_schema'); - require('event_bindings'); var json = require('json'); var lastError = require('lastError'); var miscNatives = requireNative('miscellaneous_bindings'); + var chrome = requireNative('chrome').GetChrome(); var CloseChannel = miscNatives.CloseChannel; var PortAddRef = miscNatives.PortAddRef; var PortRelease = miscNatives.PortRelease; @@ -121,7 +121,7 @@ if (sourceExtensionId != targetExtensionId) errorMsg += " for extension " + targetExtensionId; errorMsg += ")."; - lastError.set(errorMsg); + lastError.set(errorMsg, chrome); console.error("Could not send response: " + errorMsg); } @@ -231,14 +231,14 @@ // Update the renderer's port bookkeeping, without notifying the browser. CloseChannel(portId, false); if (errorMessage) { - lastError.set(errorMessage); + lastError.set(errorMessage, chrome); console.error("Port error: " + errorMessage); } try { port.onDisconnect.dispatch(port); } finally { port.destroy_(); - lastError.clear(); + lastError.clear(chrome); } } }; diff --git a/chrome/renderer/resources/extensions/notifications_custom_bindings.js b/chrome/renderer/resources/extensions/notifications_custom_bindings.js index 3584f36..d1dd6fa 100644 --- a/chrome/renderer/resources/extensions/notifications_custom_bindings.js +++ b/chrome/renderer/resources/extensions/notifications_custom_bindings.js @@ -3,8 +3,8 @@ // found in the LICENSE file. // Custom bindings for the notifications API. +var binding = require('binding').Binding.create('notifications'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var sendRequest = require('sendRequest').sendRequest; var imageUtil = require('imageUtil'); var lastError = require('lastError'); @@ -109,8 +109,8 @@ function genHandle(failure_function) { that.definition.parameters); return; } - lastError.set('Unable to download all specified images.'); - failure_function(callback, id); + lastError.run('Unable to download all specified images.', + failure_function, [callback, id]) }); }; } @@ -124,4 +124,6 @@ var notificationsCustomHook = function(bindingsAPI, extensionId) { apiFunctions.setHandleRequest('update', handleCreate); }; -chromeHidden.registerCustomHook('notifications', notificationsCustomHook); +binding.registerCustomHook(notificationsCustomHook); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/omnibox_custom_bindings.js b/chrome/renderer/resources/extensions/omnibox_custom_bindings.js index 04ae181..fa0ced3 100644 --- a/chrome/renderer/resources/extensions/omnibox_custom_bindings.js +++ b/chrome/renderer/resources/extensions/omnibox_custom_bindings.js @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the omnibox API. Only injected into the v8 contexts +// Custom binding for the omnibox API. Only injected into the v8 contexts // for extensions which have permission for the omnibox API. +var binding = require('binding').Binding.create('omnibox'); + var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var sendRequest = require('sendRequest').sendRequest; @@ -79,7 +81,7 @@ function parseOmniboxDescription(input) { return result; } -chromeHidden.registerCustomHook('omnibox', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setHandleRequest('setDefaultSuggestion', function(details) { @@ -109,3 +111,5 @@ chromeHidden.Event.registerArgumentMassager('omnibox.onInputChanged', }; dispatch([text, suggestCallback]); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/page_action_custom_bindings.js b/chrome/renderer/resources/extensions/page_action_custom_bindings.js index b527374..97a308e 100644 --- a/chrome/renderer/resources/extensions/page_action_custom_bindings.js +++ b/chrome/renderer/resources/extensions/page_action_custom_bindings.js @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the pageAction API. +// Custom binding for the pageAction API. + +var binding = require('binding').Binding.create('pageAction'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var setIcon = require('setIcon').setIcon; -chromeHidden.registerCustomHook('pageAction', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setHandleRequest('setIcon', function(details, callback) { @@ -15,3 +16,5 @@ chromeHidden.registerCustomHook('pageAction', function(bindingsAPI) { 'page action'); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/page_actions_custom_bindings.js b/chrome/renderer/resources/extensions/page_actions_custom_bindings.js index d5ec888..0b72e07 100644 --- a/chrome/renderer/resources/extensions/page_actions_custom_bindings.js +++ b/chrome/renderer/resources/extensions/page_actions_custom_bindings.js @@ -2,19 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the pageActions API. +// Custom binding for the pageActions API. + +var binding = require('binding').Binding.create('pageActions'); var pageActionsNatives = requireNative('page_actions'); var GetCurrentPageActions = pageActionsNatives.GetCurrentPageActions; -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); - -chromeHidden.registerCustomHook('pageActions', - function(bindingsAPI, extensionId) { +binding.registerCustomHook(function(bindingsAPI, extensionId) { var pageActions = GetCurrentPageActions(extensionId); + var pageActionsApi = bindingsAPI.compiledApi; var oldStyleEventName = 'pageActions'; for (var i = 0; i < pageActions.length; ++i) { // Setup events for each extension_id/page_action_id string we find. - chrome.pageActions[pageActions[i]] = new chrome.Event(oldStyleEventName); + pageActionsApi[pageActions[i]] = new chrome.Event(oldStyleEventName); } }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/page_capture_custom_bindings.js b/chrome/renderer/resources/extensions/page_capture_custom_bindings.js index 15b82f9..f9147d7 100644 --- a/chrome/renderer/resources/extensions/page_capture_custom_bindings.js +++ b/chrome/renderer/resources/extensions/page_capture_custom_bindings.js @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the pageCapture API. +// Custom binding for the pageCapture API. + +var binding = require('binding').Binding.create('pageCapture'); var pageCaptureNatives = requireNative('page_capture'); var CreateBlob = pageCaptureNatives.CreateBlob; var SendResponseAck = pageCaptureNatives.SendResponseAck; -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); - -chromeHidden.registerCustomHook('pageCapture', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setCustomCallback('saveAsMHTML', @@ -27,3 +27,5 @@ chromeHidden.registerCustomHook('pageCapture', function(bindingsAPI) { SendResponseAck(request.id); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/permissions_custom_bindings.js b/chrome/renderer/resources/extensions/permissions_custom_bindings.js index b32a57d..e728080 100644 --- a/chrome/renderer/resources/extensions/permissions_custom_bindings.js +++ b/chrome/renderer/resources/extensions/permissions_custom_bindings.js @@ -2,21 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the Permissions API. +// Custom binding for the Permissions API. + +var binding = require('binding').Binding.create('permissions'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var sendRequest = require('sendRequest').sendRequest; var lastError = require('lastError'); -// These custom bindings are only necessary because it is not currently +// These custom binding are only necessary because it is not currently // possible to have a union of types as the type of the items in an array. // Once that is fixed, this entire file should go away. // See, // https://code.google.com/p/chromium/issues/detail?id=162044 // https://code.google.com/p/chromium/issues/detail?id=162042 // TODO(bryeung): delete this file. -chromeHidden.registerCustomHook('permissions', function(api) { +binding.registerCustomHook(function(api) { var apiFunctions = api.apiFunctions; + var permissions = api.compiledApi; function maybeConvertToObject(str) { var parts = str.split('|'); @@ -80,12 +82,14 @@ chromeHidden.registerCustomHook('permissions', function(api) { // dispatchToListener call happens after argument validation, which works // around the problem that Permissions.permissions is supposed to be a list // of strings. - chrome.permissions.onAdded.dispatchToListener = function(callback, args) { + permissions.onAdded.dispatchToListener = function(callback, args) { for (var i = 0; i < args[0].permissions.length; i += 1) { args[0].permissions[i] = maybeConvertToObject(args[0].permissions[i]); } chrome.Event.prototype.dispatchToListener(callback, args); }; - chrome.permissions.onRemoved.dispatchToListener = - chrome.permissions.onAdded.dispatchToListener; + permissions.onRemoved.dispatchToListener = + permissions.onAdded.dispatchToListener; }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/runtime_custom_bindings.js b/chrome/renderer/resources/extensions/runtime_custom_bindings.js index 91d402b..61be155 100644 --- a/chrome/renderer/resources/extensions/runtime_custom_bindings.js +++ b/chrome/renderer/resources/extensions/runtime_custom_bindings.js @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the runtime API. +// Custom binding for the runtime API. + +var binding = require('binding').Binding.create('runtime'); var runtimeNatives = requireNative('runtime'); var extensionNatives = requireNative('extension'); @@ -13,14 +15,15 @@ var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var sendMessageUpdateArguments = require('miscellaneous_bindings').sendMessageUpdateArguments; -chromeHidden.registerCustomHook('runtime', function(bindings, id, contextType) { - var apiFunctions = bindings.apiFunctions; +binding.registerCustomHook(function(binding, id, contextType) { + var apiFunctions = binding.apiFunctions; + var runtime = binding.compiledApi; // // Unprivileged APIs. // - chrome.runtime.id = id; + runtime.id = id; apiFunctions.setHandleRequest('getManifest', function() { return runtimeNatives.GetManifest(); @@ -40,14 +43,14 @@ chromeHidden.registerCustomHook('runtime', function(bindings, id, contextType) { apiFunctions.setHandleRequest('sendMessage', function(targetId, message, responseCallback) { - var port = chrome.runtime.connect(targetId || chrome.runtime.id, + var port = runtime.connect(targetId || runtime.id, {name: chromeHidden.kMessageChannel}); chromeHidden.Port.sendMessageImpl(port, message, responseCallback); }); apiFunctions.setHandleRequest('sendNativeMessage', function(targetId, message, responseCallback) { - var port = chrome.runtime.connectNative(targetId); + var port = runtime.connectNative(targetId); chromeHidden.Port.sendMessageImpl(port, message, responseCallback); }); @@ -83,7 +86,7 @@ chromeHidden.registerCustomHook('runtime', function(bindings, id, contextType) { apiFunctions.setHandleRequest('connect', function(targetId, connectInfo) { if (!targetId) - targetId = chrome.runtime.id; + targetId = runtime.id; var name = ''; if (connectInfo && connectInfo.name) name = connectInfo.name; @@ -91,7 +94,7 @@ chromeHidden.registerCustomHook('runtime', function(bindings, id, contextType) { // Don't let orphaned content scripts communicate with their extension. // http://crbug.com/168263 if (!chromeHidden.wasUnloaded) { - var portId = OpenChannelToExtension(chrome.runtime.id, targetId, name); + var portId = OpenChannelToExtension(runtime.id, targetId, name); if (portId >= 0) return chromeHidden.Port.createPort(portId, name); } @@ -107,7 +110,7 @@ chromeHidden.registerCustomHook('runtime', function(bindings, id, contextType) { apiFunctions.setHandleRequest('connectNative', function(nativeAppName) { if (!chromeHidden.wasUnloaded) { - var portId = OpenChannelToNativeApp(chrome.runtime.id, nativeAppName); + var portId = OpenChannelToNativeApp(runtime.id, nativeAppName); if (portId >= 0) return chromeHidden.Port.createPort(portId, ''); } @@ -124,3 +127,5 @@ chromeHidden.registerCustomHook('runtime', function(bindings, id, contextType) { }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/schema_generated_bindings.js b/chrome/renderer/resources/extensions/schema_generated_bindings.js deleted file mode 100644 index 65d5cad..0000000 --- a/chrome/renderer/resources/extensions/schema_generated_bindings.js +++ /dev/null @@ -1,437 +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. - -// Generates the chrome.* API bindings from a list of schemas. - - // TODO(battre): cleanup the usage of packages everywhere, as described here - // http://codereview.chromium.org/10392008/diff/38/chrome/renderer/resources/extensions/schema_generated_bindings.js - - require('json_schema'); - require('event_bindings'); - var GetExtensionAPIDefinition = - requireNative('apiDefinitions').GetExtensionAPIDefinition; - var sendRequest = require('sendRequest').sendRequest; - var utils = require('utils'); - var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); - var schemaUtils = require('schemaUtils'); - - // The object to generate the bindings for "internal" APIs in, so that - // extensions can't directly call them (without access to chromeHidden), - // but are still needed for internal mechanisms of extensions (e.g. events). - // - // This is distinct to the "*Private" APIs which are controlled via - // having strict permissions and aren't generated *anywhere* unless needed. - var internalAPIs = {}; - chromeHidden.internalAPIs = internalAPIs; - - // Stores the name and definition of each API function, with methods to - // modify their behaviour (such as a custom way to handle requests to the - // API, a custom callback, etc). - function APIFunctions() { - this._apiFunctions = {}; - this._unavailableApiFunctions = {}; - } - APIFunctions.prototype.register = function(apiName, apiFunction) { - this._apiFunctions[apiName] = apiFunction; - }; - // Registers a function as existing but not available, meaning that calls to - // the set* methods that reference this function should be ignored rather - // than throwing Errors. - APIFunctions.prototype.registerUnavailable = function(apiName) { - this._unavailableApiFunctions[apiName] = apiName; - }; - APIFunctions.prototype._setHook = - function(apiName, propertyName, customizedFunction) { - if (this._unavailableApiFunctions.hasOwnProperty(apiName)) - return; - if (!this._apiFunctions.hasOwnProperty(apiName)) - throw new Error('Tried to set hook for unknown API "' + apiName + '"'); - this._apiFunctions[apiName][propertyName] = customizedFunction; - }; - APIFunctions.prototype.setHandleRequest = - function(apiName, customizedFunction) { - return this._setHook(apiName, 'handleRequest', customizedFunction); - }; - APIFunctions.prototype.setUpdateArgumentsPostValidate = - function(apiName, customizedFunction) { - return this._setHook( - apiName, 'updateArgumentsPostValidate', customizedFunction); - }; - APIFunctions.prototype.setUpdateArgumentsPreValidate = - function(apiName, customizedFunction) { - return this._setHook( - apiName, 'updateArgumentsPreValidate', customizedFunction); - }; - APIFunctions.prototype.setCustomCallback = - function(apiName, customizedFunction) { - return this._setHook(apiName, 'customCallback', customizedFunction); - }; - - var apiFunctions = new APIFunctions(); - - // Wraps the calls to the set* methods of APIFunctions with the namespace of - // an API, and validates that all calls to set* methods aren't prefixed with - // a namespace. - // - // For example, if constructed with 'browserAction', a call to - // handleRequest('foo') will be transformed into - // handleRequest('browserAction.foo'). - // - // Likewise, if a call to handleRequest is called with 'browserAction.foo', - // it will throw an error. - // - // These help with isolating custom bindings from each other. - function NamespacedAPIFunctions(namespace, delegate) { - var self = this; - function wrap(methodName) { - self[methodName] = function(apiName, customizedFunction) { - var prefix = namespace + '.'; - if (apiName.indexOf(prefix) === 0) { - throw new Error(methodName + ' called with "' + apiName + - '" which has a "' + prefix + '" prefix. ' + - 'This is unnecessary and must be left out.'); - } - return delegate[methodName].call(delegate, - prefix + apiName, customizedFunction); - }; - } - - wrap('contains'); - wrap('setHandleRequest'); - wrap('setUpdateArgumentsPostValidate'); - wrap('setUpdateArgumentsPreValidate'); - wrap('setCustomCallback'); - } - - // - // The API through which the ${api_name}_custom_bindings.js files customize - // their API bindings beyond what can be generated. - // - // There are 2 types of customizations available: those which are required in - // order to do the schema generation (registerCustomEvent and - // registerCustomType), and those which can only run after the bindings have - // been generated (registerCustomHook). - // - - // Registers a custom event type for the API identified by |namespace|. - // |event| is the event's constructor. - var customEvents = {}; - chromeHidden.registerCustomEvent = function(namespace, event) { - if (typeof(namespace) !== 'string') { - throw new Error("registerCustomEvent requires the namespace of the " + - "API as its first argument"); - } - customEvents[namespace] = event; - }; - - // Registers a function |hook| to run after the schema for all APIs has been - // generated. The hook is passed as its first argument an "API" object to - // interact with, and second the current extension ID. See where - // |customHooks| is used. - var customHooks = {}; - chromeHidden.registerCustomHook = function(namespace, fn) { - if (typeof(namespace) !== 'string') { - throw new Error("registerCustomHook requires the namespace of the " + - "API as its first argument"); - } - customHooks[namespace] = fn; - }; - - function CustomBindingsObject() { - } - CustomBindingsObject.prototype.setSchema = function(schema) { - // The functions in the schema are in list form, so we move them into a - // dictionary for easier access. - var self = this; - self.functionSchemas = {}; - schema.functions.forEach(function(f) { - self.functionSchemas[f.name] = { - name: f.name, - definition: f - } - }); - }; - - // Registers a custom type referenced via "$ref" fields in the API schema - // JSON. - var customTypes = {}; - chromeHidden.registerCustomType = function(typeName, customTypeFactory) { - var customType = customTypeFactory(); - customType.prototype = new CustomBindingsObject(); - customTypes[typeName] = customType; - }; - - // Get the platform from navigator.appVersion. - function getPlatform() { - var platforms = [ - [/CrOS Touch/, "chromeos touch"], - [/CrOS/, "chromeos"], - [/Linux/, "linux"], - [/Mac/, "mac"], - [/Win/, "win"], - ]; - - for (var i = 0; i < platforms.length; i++) { - if (platforms[i][0].test(navigator.appVersion)) { - return platforms[i][1]; - } - } - return "unknown"; - } - - function isPlatformSupported(schemaNode, platform) { - return !schemaNode.platforms || - schemaNode.platforms.indexOf(platform) > -1; - } - - function isManifestVersionSupported(schemaNode, manifestVersion) { - return !schemaNode.maximumManifestVersion || - manifestVersion <= schemaNode.maximumManifestVersion; - } - - function isSchemaNodeSupported(schemaNode, platform, manifestVersion) { - return isPlatformSupported(schemaNode, platform) && - isManifestVersionSupported(schemaNode, manifestVersion); - } - - chromeHidden.onLoad.addListener(function(extensionId, - contextType, - isIncognitoProcess, - manifestVersion) { - var apiDefinitions = GetExtensionAPIDefinition(); - - // Read api definitions and setup api functions in the chrome namespace. - 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 and chrome.webstore are - // entirely handwritten. - if (['app', 'webstore'].indexOf(apiDef.namespace) >= 0) - return; - - if (!isSchemaNodeSupported(apiDef, platform, manifestVersion)) - return; - - // See comment on internalAPIs at the top. - var mod = apiDef.internal ? internalAPIs : chrome; - - var namespaces = apiDef.namespace.split('.'); - for (var index = 0, name; name = namespaces[index]; index++) { - mod[name] = mod[name] || {}; - mod = mod[name]; - } - - // Add types to global schemaValidator - if (apiDef.types) { - apiDef.types.forEach(function(t) { - if (!isSchemaNodeSupported(t, platform, manifestVersion)) - return; - - schemaUtils.schemaValidator.addTypes(t); - if (t.type == 'object' && customTypes[t.id]) { - customTypes[t.id].prototype.setSchema(t); - } - }); - } - - // Returns whether access to the content of a schema should be denied, - // based on the presence of "unprivileged" and whether this is an - // extension process (versus e.g. a content script). - function isSchemaAccessAllowed(itemSchema) { - return (contextType == 'BLESSED_EXTENSION') || - apiDef.unprivileged || - itemSchema.unprivileged; - } - - // Adds a getter that throws an access denied error to object |mod| - // for property |name|. - function addUnprivilegedAccessGetter(mod, name) { - mod.__defineGetter__(name, function() { - throw new Error( - '"' + name + '" can only be used in extension processes. See ' + - 'the content scripts documentation for more details.'); - }); - } - - // Setup Functions. - if (apiDef.functions) { - apiDef.functions.forEach(function(functionDef) { - if (functionDef.name in mod) { - throw new Error('Function ' + functionDef.name + - ' already defined in ' + apiDef.namespace); - } - - var apiFunctionName = apiDef.namespace + "." + functionDef.name; - - if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) { - apiFunctions.registerUnavailable(apiFunctionName); - return; - } - if (!isSchemaAccessAllowed(functionDef)) { - apiFunctions.registerUnavailable(apiFunctionName); - addUnprivilegedAccessGetter(mod, functionDef.name); - return; - } - - var apiFunction = {}; - apiFunction.definition = functionDef; - apiFunction.name = apiFunctionName; - - // TODO(aa): It would be best to run this in a unit test, but in order - // to do that we would need to better factor this code so that it - // doesn't depend on so much v8::Extension machinery. - if (chromeHidden.validateAPI && - schemaUtils.isFunctionSignatureAmbiguous( - apiFunction.definition)) { - throw new Error( - apiFunction.name + ' has ambiguous optional arguments. ' + - 'To implement custom disambiguation logic, add ' + - '"allowAmbiguousOptionalArguments" to the function\'s schema.'); - } - - apiFunctions.register(apiFunction.name, apiFunction); - - mod[functionDef.name] = (function() { - var args = Array.prototype.slice.call(arguments); - if (this.updateArgumentsPreValidate) - args = this.updateArgumentsPreValidate.apply(this, args); - - args = schemaUtils.normalizeArgumentsAndValidate(args, this); - if (this.updateArgumentsPostValidate) - args = this.updateArgumentsPostValidate.apply(this, args); - - var retval; - if (this.handleRequest) { - retval = this.handleRequest.apply(this, args); - } else { - var optArgs = { - customCallback: this.customCallback - }; - retval = sendRequest(this.name, args, - this.definition.parameters, - optArgs); - } - - // Validate return value if defined - only in debug. - if (chromeHidden.validateCallbacks && - this.definition.returns) { - schemaUtils.validate([retval], [this.definition.returns]); - } - return retval; - }).bind(apiFunction); - }); - } - - // Setup Events - if (apiDef.events) { - apiDef.events.forEach(function(eventDef) { - if (eventDef.name in mod) { - throw new Error('Event ' + eventDef.name + - ' already defined in ' + apiDef.namespace); - } - if (!isSchemaNodeSupported(eventDef, platform, manifestVersion)) - return; - if (!isSchemaAccessAllowed(eventDef)) { - addUnprivilegedAccessGetter(mod, eventDef.name); - return; - } - - var eventName = apiDef.namespace + "." + eventDef.name; - var customEvent = customEvents[apiDef.namespace]; - var options = eventDef.options || {}; - - if (eventDef.filters && eventDef.filters.length > 0) - options.supportsFilters = true; - - if (customEvent) { - mod[eventDef.name] = new customEvent( - eventName, eventDef.parameters, eventDef.extraParameters, - options); - } else if (eventDef.anonymous) { - mod[eventDef.name] = new chrome.Event(); - } else { - mod[eventDef.name] = new chrome.Event( - eventName, eventDef.parameters, options); - } - }); - } - - function addProperties(m, parentDef) { - var properties = parentDef.properties; - if (!properties) - return; - - utils.forEach(properties, function(propertyName, propertyDef) { - if (propertyName in m) - return; // TODO(kalman): be strict like functions/events somehow. - if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion)) - return; - if (!isSchemaAccessAllowed(propertyDef)) { - addUnprivilegedAccessGetter(m, propertyName); - return; - } - - var value = propertyDef.value; - if (value) { - // Values may just have raw types as defined in the JSON, such - // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here. - // TODO(kalman): enforce that things with a "value" property can't - // define their own types. - var type = propertyDef.type || typeof(value); - if (type === 'integer' || type === 'number') { - value = parseInt(value); - } else if (type === 'boolean') { - value = value === "true"; - } else if (propertyDef["$ref"]) { - var constructor = customTypes[propertyDef["$ref"]]; - if (!constructor) - throw new Error("No custom binding for " + propertyDef["$ref"]); - var args = value; - // For an object propertyDef, |value| is an array of constructor - // arguments, but we want to pass the arguments directly (i.e. - // not as an array), so we have to fake calling |new| on the - // constructor. - value = { __proto__: constructor.prototype }; - constructor.apply(value, args); - // Recursively add properties. - addProperties(value, propertyDef); - } else if (type === 'object') { - // Recursively add properties. - addProperties(value, propertyDef); - } else if (type !== 'string') { - throw new Error("NOT IMPLEMENTED (extension_api.json error): " + - "Cannot parse values for type \"" + type + "\""); - } - m[propertyName] = value; - } - }); - } - - addProperties(mod, apiDef); - }); - - // Run the non-declarative custom hooks after all the schemas have been - // generated, in case hooks depend on other APIs being available. - apiDefinitions.forEach(function(apiDef) { - if (!isSchemaNodeSupported(apiDef, platform, manifestVersion)) - return; - - var hook = customHooks[apiDef.namespace]; - if (!hook) - return; - - // Pass through the public API of schema_generated_bindings, to be used - // by custom bindings JS files. Create a new one so that bindings can't - // interfere with each other. - hook({ - apiFunctions: new NamespacedAPIFunctions(apiDef.namespace, - apiFunctions), - apiDefinitions: apiDefinitions, - }, extensionId, contextType); - }); - - if (chrome.test) - chrome.test.getApiDefinitions = GetExtensionAPIDefinition; - }); diff --git a/chrome/renderer/resources/extensions/schema_utils.js b/chrome/renderer/resources/extensions/schema_utils.js index 8cd0212..31531f9 100644 --- a/chrome/renderer/resources/extensions/schema_utils.js +++ b/chrome/renderer/resources/extensions/schema_utils.js @@ -5,6 +5,7 @@ // Routines used to validate and normalize arguments. var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var chrome = requireNative('chrome').GetChrome(); // TODO(benwells): unit test this file. // JSONSchemaValidator is not loaded in unit tests. diff --git a/chrome/renderer/resources/extensions/send_request.js b/chrome/renderer/resources/extensions/send_request.js index d689a5b..829aee2 100644 --- a/chrome/renderer/resources/extensions/send_request.js +++ b/chrome/renderer/resources/extensions/send_request.js @@ -3,25 +3,41 @@ // found in the LICENSE file. var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var DCHECK = requireNative('logging').DCHECK; var json = require('json'); var lastError = require('lastError'); var natives = requireNative('sendRequest'); var validate = require('schemaUtils').validate; +// All outstanding requests from sendRequest(). +var requests = {}; + // Callback handling. -var requests = []; chromeHidden.handleResponse = function(requestId, name, success, responseList, error) { + // The chrome objects we will set lastError on. Really we should only be + // setting this on the callback's chrome object, but set on ours too since + // it's conceivable that something relies on that. + var chromesForLastError = [chrome]; + try { var request = requests[requestId]; - if (success) { - lastError.clear(); - } else { - if (!error) { + DCHECK(request != null); + + // lastError needs to be set on the caller's chrome object no matter what, + // though chances are it's the same as ours (it will be different when + // calling API methods on other contexts). + if (request.callback) { + var chromeForCallback = natives.GetGlobal(request.callback).chrome; + if (chromeForCallback != chrome) + chromesForLastError.push(chromeForCallback); + } + + chromesForLastError.forEach(function(c) {lastError.clear(c)}); + if (!success) { + if (!error) error = "Unknown error."; - } - console.error("Error during " + name + ": " + error); - lastError.set(error); + chromesForLastError.forEach(function(c) {lastError.set(error, c)}); } if (request.customCallback) { @@ -32,7 +48,7 @@ chromeHidden.handleResponse = function(requestId, name, if (request.callback) { // Validate callback in debug only -- and only when the // caller has provided a callback. Implementations of api - // calls my not return data if they observe the caller + // calls may not return data if they observe the caller // has not provided a callback. if (chromeHidden.validateCallbacks && !error) { try { @@ -51,10 +67,8 @@ chromeHidden.handleResponse = function(requestId, name, } } finally { delete requests[requestId]; - lastError.clear(); + chromesForLastError.forEach(function(c) {lastError.clear(c)}); } - - return undefined; }; function prepareRequest(args, argSchemas) { @@ -109,6 +123,7 @@ function sendRequest(functionName, args, argSchemas, optArgs) { var requestId = natives.GetNextRequestId(); request.id = requestId; requests[requestId] = request; + var hasCallback = request.callback || optArgs.customCallback; return nativeFunction(functionName, requestArgs, diff --git a/chrome/renderer/resources/extensions/storage_custom_bindings.js b/chrome/renderer/resources/extensions/storage_custom_bindings.js index d230099..869a555 100644 --- a/chrome/renderer/resources/extensions/storage_custom_bindings.js +++ b/chrome/renderer/resources/extensions/storage_custom_bindings.js @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the storage API. +// Custom binding for the storage API. + +var binding = require('binding').Binding.create('storage'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var normalizeArgumentsAndValidate = require('schemaUtils').normalizeArgumentsAndValidate var sendRequest = require('sendRequest').sendRequest; -chromeHidden.registerCustomType('storage.StorageArea', function() { +binding.registerCustomType('storage.StorageArea', function() { function extendSchema(schema) { var extendedSchema = schema.slice(); extendedSchema.unshift({'type': 'string'}); @@ -21,7 +22,7 @@ chromeHidden.registerCustomType('storage.StorageArea', function() { // storage.sync.get('foo') -> (binds to) -> // storage.get('sync', 'foo'). // - // TODO(kalman): Put as a method on CustomBindingsObject and re-use (or + // TODO(kalman): Put as a method on CustombindingObject and re-use (or // even generate) for other APIs that need to do this. Same for other // callers of registerCustomType(). var self = this; @@ -43,3 +44,5 @@ chromeHidden.registerCustomType('storage.StorageArea', function() { return StorageArea; }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/sync_file_system_custom_bindings.js b/chrome/renderer/resources/extensions/sync_file_system_custom_bindings.js index 03c6caa..bcb1219 100644 --- a/chrome/renderer/resources/extensions/sync_file_system_custom_bindings.js +++ b/chrome/renderer/resources/extensions/sync_file_system_custom_bindings.js @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the syncFileSystem API. +// Custom binding for the syncFileSystem API. + +var binding = require('binding').Binding.create('syncFileSystem'); var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var fileSystemNatives = requireNative('file_system_natives'); var syncFileSystemNatives = requireNative('sync_file_system'); -chromeHidden.registerCustomHook('syncFileSystem', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; // Functions which take in an [instanceOf=FileEntry]. @@ -67,3 +69,5 @@ chromeHidden.Event.registerArgumentMassager( } dispatch([fileInfo]); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/system_indicator_custom_bindings.js b/chrome/renderer/resources/extensions/system_indicator_custom_bindings.js index 0d58de1..0551a83 100644 --- a/chrome/renderer/resources/extensions/system_indicator_custom_bindings.js +++ b/chrome/renderer/resources/extensions/system_indicator_custom_bindings.js @@ -2,14 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the systemIndicator API. -// TODO(dewittj) Refactor custom bindings to reduce redundancy between the +// Custom binding for the systemIndicator API. +// TODO(dewittj) Refactor custom binding to reduce redundancy between the // extension action APIs. +var binding = require('binding').Binding.create('systemIndicator'); + var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var setIcon = require('setIcon').setIcon; -chromeHidden.registerCustomHook('systemIndicator', function(bindingsAPI) { +binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setHandleRequest('setIcon', function(details, callback) { @@ -18,3 +20,4 @@ chromeHidden.registerCustomHook('systemIndicator', function(bindingsAPI) { }); }); +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js b/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js index f458a2f..a75d36d 100644 --- a/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js +++ b/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the Tab Capture API. -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +// Custom binding for the Tab Capture API. -chromeHidden.registerCustomHook('tabCapture', - function(bindingsAPI, extensionId) { +var binding = require('binding').Binding.create('tabCapture'); + +binding.registerCustomHook(function(bindingsAPI, extensionId) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setCustomCallback('capture', @@ -33,3 +33,5 @@ chromeHidden.registerCustomHook('tabCapture', request.callback = null; }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/tabs_custom_bindings.js b/chrome/renderer/resources/extensions/tabs_custom_bindings.js index 90e5912..0d7c405 100644 --- a/chrome/renderer/resources/extensions/tabs_custom_bindings.js +++ b/chrome/renderer/resources/extensions/tabs_custom_bindings.js @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the tabs API. +// Custom binding for the tabs API. + +var binding = require('binding').Binding.create('tabs'); var tabsNatives = requireNative('tabs'); var OpenChannelToTab = tabsNatives.OpenChannelToTab; @@ -10,8 +12,9 @@ var sendRequestIsDisabled = requireNative('process').IsSendRequestDisabled(); var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); -chromeHidden.registerCustomHook('tabs', function(bindingsAPI, extensionId) { +binding.registerCustomHook(function(bindingsAPI, extensionId) { var apiFunctions = bindingsAPI.apiFunctions; + var tabs = bindingsAPI.compiledApi; apiFunctions.setHandleRequest('connect', function(tabId, connectInfo) { var name = ''; @@ -26,13 +29,15 @@ chromeHidden.registerCustomHook('tabs', function(bindingsAPI, extensionId) { function(tabId, request, responseCallback) { if (sendRequestIsDisabled) throw new Error(sendRequestIsDisabled); - var port = chrome.tabs.connect(tabId, {name: chromeHidden.kRequestChannel}); + var port = tabs.connect(tabId, {name: chromeHidden.kRequestChannel}); chromeHidden.Port.sendMessageImpl(port, request, responseCallback); }); apiFunctions.setHandleRequest('sendMessage', function(tabId, message, responseCallback) { - var port = chrome.tabs.connect(tabId, {name: chromeHidden.kMessageChannel}); + var port = tabs.connect(tabId, {name: chromeHidden.kMessageChannel}); chromeHidden.Port.sendMessageImpl(port, message, responseCallback); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/apitest.js b/chrome/renderer/resources/extensions/test_custom_bindings.js index 52414c2..300ba42 100644 --- a/chrome/renderer/resources/extensions/apitest.js +++ b/chrome/renderer/resources/extensions/test_custom_bindings.js @@ -2,12 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// extension_apitest.js +// test_custom_bindings.js // mini-framework for ExtensionApiTest browser tests - chrome.test = chrome.test || {}; +var binding = require('binding').Binding.create('test'); - chrome.test.tests = chrome.test.tests || []; +var chrome = requireNative('chrome').GetChrome(); +var GetExtensionAPIDefinition = + requireNative('apiDefinitions').GetExtensionAPIDefinition; + +binding.registerCustomHook(function(api) { + var chromeTest = api.compiledApi; + var apiFunctions = api.apiFunctions; + + chromeTest.tests = chromeTest.tests || []; var currentTest = null; var lastTest = null; @@ -24,14 +32,14 @@ function testDone() { // Use setTimeout here to allow previous test contexts to be // eligible for garbage collection. - setTimeout(chrome.test.runNextTest, 0); + setTimeout(chromeTest.runNextTest, 0); } function allTestsDone() { if (testsFailed == 0) { - chrome.test.notifyPass(); + chromeTest.notifyPass(); } else { - chrome.test.notifyFail('Failed ' + testsFailed + ' of ' + + chromeTest.notifyFail('Failed ' + testsFailed + ' of ' + testCount + ' tests'); } @@ -42,28 +50,28 @@ var pendingCallbacks = 0; - chrome.test.callbackAdded = function() { + apiFunctions.setHandleRequest('callbackAdded', function() { pendingCallbacks++; var called = false; return function() { - chrome.test.assertFalse(called, 'callback has already been run'); + chromeTest.assertFalse(called, 'callback has already been run'); called = true; pendingCallbacks--; if (pendingCallbacks == 0) { - chrome.test.succeed(); + chromeTest.succeed(); } }; - }; + }); - chrome.test.runNextTest = function() { + apiFunctions.setHandleRequest('runNextTest', function() { // There may have been callbacks which were interrupted by failure // exceptions. pendingCallbacks = 0; lastTest = currentTest; - currentTest = chrome.test.tests.shift(); + currentTest = chromeTest.tests.shift(); if (!currentTest) { allTestsDone(); @@ -71,19 +79,19 @@ } try { - chrome.test.log("( RUN ) " + testName(currentTest)); + chromeTest.log("( RUN ) " + testName(currentTest)); currentTest.call(); } catch (e) { if (e !== failureException) - chrome.test.fail('uncaught exception: ' + e); + chromeTest.fail('uncaught exception: ' + e); } - }; + }); - chrome.test.fail = function(message) { - chrome.test.log("( FAILED ) " + testName(currentTest)); + apiFunctions.setHandleRequest('fail', function(message) { + chromeTest.log("( FAILED ) " + testName(currentTest)); var stack = {}; - Error.captureStackTrace(stack, chrome.test.fail); + Error.captureStackTrace(stack, chromeTest.fail); if (!message) message = "FAIL (no message)"; @@ -95,23 +103,24 @@ // Interrupt the rest of the test. throw failureException; - }; + }); - chrome.test.succeed = function() { + apiFunctions.setHandleRequest('succeed', function() { console.log("[SUCCESS] " + testName(currentTest)); - chrome.test.log("( SUCCESS )"); + chromeTest.log("( SUCCESS )"); testDone(); - }; + }); - chrome.test.assertTrue = function(test, message) { - chrome.test.assertBool(test, true, message); - }; + apiFunctions.setHandleRequest('assertTrue', function(test, message) { + chromeTest.assertBool(test, true, message); + }); - chrome.test.assertFalse = function(test, message) { - chrome.test.assertBool(test, false, message); - }; + apiFunctions.setHandleRequest('assertFalse', function(test, message) { + chromeTest.assertBool(test, false, message); + }); - chrome.test.assertBool = function(test, expected, message) { + apiFunctions.setHandleRequest('assertBool', + function(test, expected, message) { if (test !== expected) { if (typeof(test) == "string") { if (message) @@ -119,11 +128,11 @@ else message = test; } - chrome.test.fail(message); + chromeTest.fail(message); } - }; + }); - chrome.test.checkDeepEq = function (expected, actual) { + apiFunctions.setHandleRequest('checkDeepEq', function(expected, actual) { if ((expected === null) != (actual === null)) return false; @@ -146,7 +155,7 @@ var eq = true; switch (typeof(expected[p])) { case 'object': - eq = chrome.test.checkDeepEq(expected[p], actual[p]); + eq = chromeTest.checkDeepEq(expected[p], actual[p]); break; case 'function': eq = (typeof(actual[p]) != 'undefined' && @@ -161,44 +170,45 @@ return false; } return true; - }; + }); - chrome.test.assertEq = function(expected, actual, message) { + apiFunctions.setHandleRequest('assertEq', + function(expected, actual, message) { var error_msg = "API Test Error in " + testName(currentTest); if (message) error_msg += ": " + message; if (typeof(expected) == 'object') { - if (!chrome.test.checkDeepEq(expected, actual)) { - chrome.test.fail(error_msg + + if (!chromeTest.checkDeepEq(expected, actual)) { + chromeTest.fail(error_msg + "\nActual: " + JSON.stringify(actual) + "\nExpected: " + JSON.stringify(expected)); } return; } if (expected != actual) { - chrome.test.fail(error_msg + + chromeTest.fail(error_msg + "\nActual: " + actual + "\nExpected: " + expected); } if (typeof(expected) != typeof(actual)) { - chrome.test.fail(error_msg + + chromeTest.fail(error_msg + " (type mismatch)\nActual Type: " + typeof(actual) + "\nExpected Type:" + typeof(expected)); } - }; + }); - chrome.test.assertNoLastError = function() { + apiFunctions.setHandleRequest('assertNoLastError', function() { if (chrome.runtime.lastError != undefined) { - chrome.test.fail("lastError.message == " + + chromeTest.fail("lastError.message == " + chrome.runtime.lastError.message); } - }; + }); - chrome.test.assertLastError = function(expectedError) { - chrome.test.assertEq(typeof(expectedError), 'string'); - chrome.test.assertTrue(chrome.runtime.lastError != undefined, + apiFunctions.setHandleRequest('assertLastError', function(expectedError) { + chromeTest.assertEq(typeof(expectedError), 'string'); + chromeTest.assertTrue(chrome.runtime.lastError != undefined, "No lastError, but expected " + expectedError); - chrome.test.assertEq(expectedError, chrome.runtime.lastError.message); - } + chromeTest.assertEq(expectedError, chrome.runtime.lastError.message); + }); function safeFunctionApply(func, args) { try { @@ -206,23 +216,23 @@ func.apply(null, args); } catch (e) { var msg = "uncaught exception " + e; - chrome.test.fail(msg); + chromeTest.fail(msg); } }; // Wrapper for generating test functions, that takes care of calling // assertNoLastError() and (optionally) succeed() for you. - chrome.test.callback = function(func, expectedError) { + apiFunctions.setHandleRequest('callback', function(func, expectedError) { if (func) { - chrome.test.assertEq(typeof(func), 'function'); + chromeTest.assertEq(typeof(func), 'function'); } - var callbackCompleted = chrome.test.callbackAdded(); + var callbackCompleted = chromeTest.callbackAdded(); return function() { if (expectedError == null) { - chrome.test.assertNoLastError(); + chromeTest.assertNoLastError(); } else { - chrome.test.assertLastError(expectedError); + chromeTest.assertLastError(expectedError); } if (func) { @@ -231,20 +241,20 @@ callbackCompleted(); }; - }; + }); - chrome.test.listenOnce = function(event, func) { - var callbackCompleted = chrome.test.callbackAdded(); + apiFunctions.setHandleRequest('listenOnce', function(event, func) { + var callbackCompleted = chromeTest.callbackAdded(); var listener = function() { event.removeListener(listener); safeFunctionApply(func, arguments); callbackCompleted(); }; event.addListener(listener); - }; + }); - chrome.test.listenForever = function(event, func) { - var callbackCompleted = chrome.test.callbackAdded(); + apiFunctions.setHandleRequest('listenForever', function(event, func) { + var callbackCompleted = chromeTest.callbackAdded(); var listener = function() { safeFunctionApply(func, arguments); @@ -257,18 +267,25 @@ event.addListener(listener); return done; - }; + }); - chrome.test.callbackPass = function(func) { - return chrome.test.callback(func); - }; + apiFunctions.setHandleRequest('callbackPass', function(func) { + return chromeTest.callback(func); + }); - chrome.test.callbackFail = function(expectedError, func) { - return chrome.test.callback(func, expectedError); - }; + apiFunctions.setHandleRequest('callbackFail', function(expectedError, func) { + return chromeTest.callback(func, expectedError); + }); - chrome.test.runTests = function(tests) { - chrome.test.tests = tests; - testCount = chrome.test.tests.length; - chrome.test.runNextTest(); - }; + apiFunctions.setHandleRequest('runTests', function(tests) { + chromeTest.tests = tests; + testCount = chromeTest.tests.length; + chromeTest.runNextTest(); + }); + + apiFunctions.setHandleRequest('getApiDefinitions', function(apiNames) { + return GetExtensionAPIDefinition(); + }); +}); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/tts_custom_bindings.js b/chrome/renderer/resources/extensions/tts_custom_bindings.js index 634ee38..5c74666 100644 --- a/chrome/renderer/resources/extensions/tts_custom_bindings.js +++ b/chrome/renderer/resources/extensions/tts_custom_bindings.js @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the tts API. +// Custom binding for the tts API. + +var binding = require('binding').Binding.create('tts'); var ttsNatives = requireNative('tts'); var GetNextTTSEventId = ttsNatives.GetNextTTSEventId; @@ -11,8 +13,9 @@ var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var sendRequest = require('sendRequest').sendRequest; var lazyBG = requireNative('lazy_background_page'); -chromeHidden.registerCustomHook('tts', function(api) { +binding.registerCustomHook(function(api) { var apiFunctions = api.apiFunctions; + var tts = api.compiledApi; chromeHidden.tts = { handlers: {} @@ -39,7 +42,7 @@ chromeHidden.registerCustomHook('tts', function(api) { // add a listener to chrome.tts.onEvent will fail. // See http://crbug.com/122474. try { - chrome.tts.onEvent.addListener(ttsEventListener); + tts.onEvent.addListener(ttsEventListener); } catch (e) {} apiFunctions.setHandleRequest('speak', function() { @@ -56,3 +59,5 @@ chromeHidden.registerCustomHook('tts', function(api) { return id; }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/tts_engine_custom_bindings.js b/chrome/renderer/resources/extensions/tts_engine_custom_bindings.js index f16f143..1d19cea 100644 --- a/chrome/renderer/resources/extensions/tts_engine_custom_bindings.js +++ b/chrome/renderer/resources/extensions/tts_engine_custom_bindings.js @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the ttsEngine API. +// Custom binding for the ttsEngine API. + +var binding = require('binding').Binding.create('ttsEngine'); var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); @@ -16,3 +18,5 @@ chromeHidden.Event.registerArgumentMassager('ttsEngine.onSpeak', }; dispatch([text, options, sendTtsEvent]); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/types_custom_bindings.js b/chrome/renderer/resources/extensions/types_custom_bindings.js index 4af67b4d..1c386f4 100644 --- a/chrome/renderer/resources/extensions/types_custom_bindings.js +++ b/chrome/renderer/resources/extensions/types_custom_bindings.js @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the types API. +// Custom binding for the types API. -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); +var binding = require('binding').Binding.create('types'); + +var chrome = requireNative('chrome').GetChrome(); var sendRequest = require('sendRequest').sendRequest; var validate = require('schemaUtils').validate; -chromeHidden.registerCustomType('types.ChromeSetting', function() { +binding.registerCustomType('types.ChromeSetting', function() { function extendSchema(schema) { var extendedSchema = schema.slice(); @@ -45,3 +47,5 @@ chromeHidden.registerCustomType('types.ChromeSetting', function() { return ChromeSetting; }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/utils.js b/chrome/renderer/resources/extensions/utils.js index beb9a51..ed35e15 100644 --- a/chrome/renderer/resources/extensions/utils.js +++ b/chrome/renderer/resources/extensions/utils.js @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +var chrome = requireNative('chrome').GetChrome(); + function forEach(dict, f) { for (var key in dict) { if (dict.hasOwnProperty(key)) @@ -25,5 +27,16 @@ function lookup(array_of_dictionaries, field, value) { } } +// Specify |currentApi| if this should return an API for $refs in the current +// namespace. +function loadRefDependency(ref, currentApi) { + var parts = ref.split("."); + if (parts.length > 1) + return chrome[parts.slice(0, parts.length - 1).join(".")]; + else + return currentApi; +} + exports.forEach = forEach; +exports.loadRefDependency = loadRefDependency; exports.lookup = lookup; diff --git a/chrome/renderer/resources/extensions/web_request_custom_bindings.js b/chrome/renderer/resources/extensions/web_request_custom_bindings.js index 9a458de..135da99 100644 --- a/chrome/renderer/resources/extensions/web_request_custom_bindings.js +++ b/chrome/renderer/resources/extensions/web_request_custom_bindings.js @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the webRequest API. +// Custom binding for the webRequest API. + +var binding = require('binding').Binding.create('webRequest'); var webRequestNatives = requireNative('web_request'); var GetUniqueSubEventName = webRequestNatives.GetUniqueSubEventName; @@ -10,6 +12,7 @@ var GetUniqueSubEventName = webRequestNatives.GetUniqueSubEventName; var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var sendRequest = require('sendRequest').sendRequest; var validate = require('schemaUtils').validate; +var webRequestInternal = require('webRequestInternal').binding; // WebRequestEvent object. This is used for special webRequest events with // extra parameters. Each invocation of addListener creates a new named @@ -66,7 +69,7 @@ WebRequestEvent.prototype.addListener = // Note: this could fail to validate, in which case we would not add the // subEvent listener. validate(Array.prototype.slice.call(arguments, 1), this.extraArgSchemas_); - chromeHidden.internalAPIs.webRequestInternal.addEventListener( + webRequestInternal.addEventListener( cb, opt_filter, opt_extraInfo, this.eventName_, subEventName); var subEvent = new chrome.Event(subEventName, this.argSchemas_); @@ -77,10 +80,10 @@ WebRequestEvent.prototype.addListener = var requestId = arguments[0].requestId; try { var result = cb.apply(null, arguments); - chromeHidden.internalAPIs.webRequestInternal.eventHandled( + webRequestInternal.eventHandled( eventName, subEventName, requestId, result); } catch (e) { - chromeHidden.internalAPIs.webRequestInternal.eventHandled( + webRequestInternal.eventHandled( eventName, subEventName, requestId); throw e; } @@ -91,7 +94,7 @@ WebRequestEvent.prototype.addListener = var details = arguments[0]; var requestId = details.requestId; var handledCallback = function(response) { - chromeHidden.internalAPIs.webRequestInternal.eventHandled( + webRequestInternal.eventHandled( eventName, subEventName, requestId, response); }; cb.apply(null, [details, handledCallback]); @@ -149,9 +152,9 @@ WebRequestEvent.prototype.getRules = function(ruleIdentifiers, cb) { this.eventForRules_.getRules(ruleIdentifiers, cb); } -chromeHidden.registerCustomEvent('webRequest', WebRequestEvent); +binding.registerCustomEvent(WebRequestEvent); -chromeHidden.registerCustomHook('webRequest', function(api) { +binding.registerCustomHook(function(api) { var apiFunctions = api.apiFunctions; apiFunctions.setHandleRequest('handlerBehaviorChanged', function() { @@ -160,3 +163,5 @@ chromeHidden.registerCustomHook('webRequest', function(api) { {forIOThread: true}); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/web_request_internal_custom_bindings.js b/chrome/renderer/resources/extensions/web_request_internal_custom_bindings.js index a33599c..584be5f 100644 --- a/chrome/renderer/resources/extensions/web_request_internal_custom_bindings.js +++ b/chrome/renderer/resources/extensions/web_request_internal_custom_bindings.js @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the webRequestInternal API. +// Custom binding for the webRequestInternal API. + +var binding = require('binding').Binding.create('webRequestInternal'); -var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); var sendRequest = require('sendRequest').sendRequest; -chromeHidden.registerCustomHook('webRequestInternal', function(api) { +binding.registerCustomHook(function(api) { var apiFunctions = api.apiFunctions; apiFunctions.setHandleRequest('addEventListener', function() { @@ -22,3 +23,5 @@ chromeHidden.registerCustomHook('webRequestInternal', function(api) { {forIOThread: true}); }); }); + +exports.binding = binding.generate(); diff --git a/chrome/renderer/resources/extensions/web_view.js b/chrome/renderer/resources/extensions/web_view.js index dc5f66e..6e81a03 100644 --- a/chrome/renderer/resources/extensions/web_view.js +++ b/chrome/renderer/resources/extensions/web_view.js @@ -7,7 +7,9 @@ // The actual tag is implemented via the browser plugin. The internals of this // are hidden via Shadow DOM. -var watchForTag = require("tagWatcher").watchForTag; +var watchForTag = require('tagWatcher').watchForTag; + +var chrome = requireNative('chrome').GetChrome(); var WEB_VIEW_ATTRIBUTES = ['name', 'src', 'partition', 'autosize', 'minheight', 'minwidth', 'maxheight', 'maxwidth']; diff --git a/chrome/renderer/resources/extensions/web_view_experimental.js b/chrome/renderer/resources/extensions/web_view_experimental.js index 2b75e38..73bc2d09 100644 --- a/chrome/renderer/resources/extensions/web_view_experimental.js +++ b/chrome/renderer/resources/extensions/web_view_experimental.js @@ -11,7 +11,7 @@ // permission API would only be available for channels CHANNEL_DEV and // CHANNEL_CANARY. -var WebView = require('webview').WebView; +var WebView = require('webView').WebView; /** @type {Array.<string>} */ var PERMISSION_TYPES = ['media', 'geolocation']; diff --git a/chrome/renderer/resources/extensions/webstore_custom_bindings.js b/chrome/renderer/resources/extensions/webstore_custom_bindings.js index 19c8f0f..8ef9935 100644 --- a/chrome/renderer/resources/extensions/webstore_custom_bindings.js +++ b/chrome/renderer/resources/extensions/webstore_custom_bindings.js @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Custom bindings for the webstore API. +// Custom binding for the webstore API. var webstoreNatives = requireNative('webstore'); @@ -49,14 +49,14 @@ var chromeWebstore = { } }; -// Called by webstore_bindings.cc. +// Called by webstore_binding.cc. var chromeHiddenWebstore = { onInstallResponse: function(installId, success, error) { installer.onInstallResponse(installId, success, error); } }; -// These must match the names in InstallWebstoreBindings in +// These must match the names in InstallWebstorebinding in // chrome/renderer/extensions/dispatcher.cc. exports.chromeWebstore = chromeWebstore; exports.chromeHiddenWebstore = chromeHiddenWebstore; diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd index 3b390f4..55d70a4 100644 --- a/chrome/renderer/resources/renderer_resources.grd +++ b/chrome/renderer/resources/renderer_resources.grd @@ -26,14 +26,12 @@ without changes to the corresponding grd file. fb9 --> <include name="IDR_DISABLED_PLUGIN_HTML" file="disabled_plugin.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_ERROR_APP_HTML" file="error_app.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_EVENT_BINDINGS_JS" file="extensions\event.js" type="BINDATA" /> - <include name="IDR_EXTENSION_APITEST_JS" file="extensions\apitest.js" type="BINDATA" /> <include name="IDR_GREASEMONKEY_API_JS" file="extensions\greasemonkey_api.js" type="BINDATA" /> <include name="IDR_SEARCHBOX_API" file="extensions\searchbox_api.js" type="BINDATA" /> <include name="IDR_JSON_JS" file="extensions\json.js" type="BINDATA" /> <include name="IDR_JSON_SCHEMA_JS" file="extensions\json_schema.js" type="BINDATA" /> <include name="IDR_MISCELLANEOUS_BINDINGS_JS" file="extensions\miscellaneous_bindings.js" type="BINDATA" /> <include name="IDR_NET_ERROR_HTML" file="neterror.html" flattenhtml="true" type="BINDATA" /> - <include name="IDR_SCHEMA_GENERATED_BINDINGS_JS" file="extensions\schema_generated_bindings.js" type="BINDATA" /> <!-- Libraries. --> <include name="IDR_TAG_WATCHER_JS" file="extensions\tag_watcher.js" type="BINDATA" /> @@ -73,11 +71,13 @@ without changes to the corresponding grd file. fb9 --> <include name="IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS" file="extensions\page_action_custom_bindings.js" type="BINDATA" /> <include name="IDR_PAGE_CAPTURE_CUSTOM_BINDINGS_JS" file="extensions\page_capture_custom_bindings.js" type="BINDATA" /> <include name="IDR_RUNTIME_CUSTOM_BINDINGS_JS" file="extensions\runtime_custom_bindings.js" type="BINDATA" /> + <include name="IDR_BINDING_JS" file="extensions\binding.js" type="BINDATA" /> <include name="IDR_STORAGE_CUSTOM_BINDINGS_JS" file="extensions\storage_custom_bindings.js" type="BINDATA" /> <include name="IDR_SYNC_FILE_SYSTEM_CUSTOM_BINDINGS_JS" file="extensions\sync_file_system_custom_bindings.js" type="BINDATA" /> <include name="IDR_SYSTEM_INDICATOR_CUSTOM_BINDINGS_JS" file="extensions\system_indicator_custom_bindings.js" type="BINDATA" /> <include name="IDR_TAB_CAPTURE_CUSTOM_BINDINGS_JS" file="extensions\tab_capture_custom_bindings.js" type="BINDATA" /> <include name="IDR_TABS_CUSTOM_BINDINGS_JS" file="extensions\tabs_custom_bindings.js" type="BINDATA" /> + <include name="IDR_TEST_CUSTOM_BINDINGS_JS" file="extensions\test_custom_bindings.js" type="BINDATA" /> <include name="IDR_TTS_CUSTOM_BINDINGS_JS" file="extensions\tts_custom_bindings.js" type="BINDATA" /> <include name="IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS" file="extensions\tts_engine_custom_bindings.js" type="BINDATA" /> <include name="IDR_TYPES_CUSTOM_BINDINGS_JS" file="extensions\types_custom_bindings.js" type="BINDATA" /> |