diff options
author | sammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-09 05:39:38 +0000 |
---|---|---|
committer | sammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-09 05:39:38 +0000 |
commit | d9f51dad0a4f9c154da5ff6ba28cd7c5c8ce0ee7 (patch) | |
tree | ae859aa19d53161377ca81b75b411aa3a8329c3c /extensions/renderer | |
parent | 86fcf7f3db64dd037831ec4233f626fb4428f1e7 (diff) | |
download | chromium_src-d9f51dad0a4f9c154da5ff6ba28cd7c5c8ce0ee7.zip chromium_src-d9f51dad0a4f9c154da5ff6ba28cd7c5c8ce0ee7.tar.gz chromium_src-d9f51dad0a4f9c154da5ff6ba28cd7c5c8ce0ee7.tar.bz2 |
Add support for using AMD modules from extensions modules.
This adds requireAsync, which returns a promise for the requested
module, allowing extension modules to asynchronously import AMD modules:
requireAsync('foo').then(function(foo) {
// Use foo.
});
BUG=390397
Review URL: https://codereview.chromium.org/359413004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281957 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/renderer')
-rw-r--r-- | extensions/renderer/DEPS | 2 | ||||
-rw-r--r-- | extensions/renderer/module_system.cc | 181 | ||||
-rw-r--r-- | extensions/renderer/module_system.h | 31 | ||||
-rw-r--r-- | extensions/renderer/script_context.cc | 19 | ||||
-rw-r--r-- | extensions/renderer/script_context.h | 12 | ||||
-rw-r--r-- | extensions/renderer/script_context_set_unittest.cc | 13 | ||||
-rw-r--r-- | extensions/renderer/v8_schema_registry.cc | 10 | ||||
-rw-r--r-- | extensions/renderer/v8_schema_registry.h | 6 |
8 files changed, 203 insertions, 71 deletions
diff --git a/extensions/renderer/DEPS b/extensions/renderer/DEPS index c1b356a..91bcd4b 100644 --- a/extensions/renderer/DEPS +++ b/extensions/renderer/DEPS @@ -3,6 +3,8 @@ include_rules = [ "+content/public/common", "+content/public/renderer", + "+gin", + "+third_party/skia/include/core", "+third_party/WebKit/public/platform", diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc index 4a685e34..ab265fa 100644 --- a/extensions/renderer/module_system.cc +++ b/extensions/renderer/module_system.cc @@ -16,6 +16,7 @@ #include "extensions/renderer/console.h" #include "extensions/renderer/safe_builtins.h" #include "extensions/renderer/script_context.h" +#include "gin/modules/module_registry.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h" @@ -121,13 +122,17 @@ ModuleSystem::ModuleSystem(ScriptContext* context, SourceMap* source_map) context_(context), source_map_(source_map), natives_enabled_(0), - exception_handler_(new DefaultExceptionHandler(context)) { + exception_handler_(new DefaultExceptionHandler(context)), + weak_factory_(this) { RouteFunction( "require", base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this))); RouteFunction( "requireNative", base::Bind(&ModuleSystem::RequireNative, base::Unretained(this))); + RouteFunction( + "requireAsync", + base::Bind(&ModuleSystem::RequireAsync, base::Unretained(this))); RouteFunction("privates", base::Bind(&ModuleSystem::Private, base::Unretained(this))); @@ -137,6 +142,8 @@ ModuleSystem::ModuleSystem(ScriptContext* context, SourceMap* source_map) v8::Object::New(isolate)); global->SetHiddenValue(v8::String::NewFromUtf8(isolate, kModuleSystem), v8::External::New(isolate, this)); + + gin::ModuleRegistry::From(context->v8_context())->AddObserver(this); } ModuleSystem::~ModuleSystem() { Invalidate(); } @@ -215,55 +222,7 @@ v8::Local<v8::Value> ModuleSystem::RequireForJsInner( if (!exports->IsUndefined()) return handle_scope.Escape(exports); - std::string module_name_str = *v8::String::Utf8Value(module_name); - v8::Handle<v8::Value> source(GetSource(module_name_str)); - if (source.IsEmpty() || source->IsUndefined()) { - Fatal(context_, "No source for require(" + module_name_str + ")"); - return v8::Undefined(GetIsolate()); - } - v8::Handle<v8::String> wrapped_source( - WrapSource(v8::Handle<v8::String>::Cast(source))); - // Modules are wrapped in (function(){...}) so they always return functions. - v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name); - if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) { - Fatal(context_, "Bad source for require(" + module_name_str + ")"); - return v8::Undefined(GetIsolate()); - } - - v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value); - - exports = v8::Object::New(GetIsolate()); - v8::Handle<v8::Object> natives(NewInstance()); - CHECK(!natives.IsEmpty()); // this can happen if v8 has issues - - // These must match the argument order in WrapSource. - v8::Handle<v8::Value> args[] = { - // CommonJS. - natives->Get(v8::String::NewFromUtf8( - GetIsolate(), "require", v8::String::kInternalizedString)), - natives->Get(v8::String::NewFromUtf8( - GetIsolate(), "requireNative", v8::String::kInternalizedString)), - exports, - // Libraries that we magically expose to every module. - console::AsV8Object(), - natives->Get(v8::String::NewFromUtf8( - GetIsolate(), "privates", v8::String::kInternalizedString)), - // Each safe builtin. Keep in order with the arguments in WrapSource. - context_->safe_builtins()->GetArray(), - context_->safe_builtins()->GetFunction(), - context_->safe_builtins()->GetJSON(), - context_->safe_builtins()->GetObjekt(), - context_->safe_builtins()->GetRegExp(), - context_->safe_builtins()->GetString(), }; - { - v8::TryCatch try_catch; - try_catch.SetCaptureMessage(true); - context_->CallFunction(func, arraysize(args), args); - if (try_catch.HasCaught()) { - HandleException(try_catch); - return v8::Undefined(GetIsolate()); - } - } + exports = LoadModule(*v8::String::Utf8Value(module_name)); modules->Set(module_name, exports); return handle_scope.Escape(exports); } @@ -558,12 +517,38 @@ v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString( return i->second->NewInstance(); } +void ModuleSystem::RequireAsync( + const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(1, args.Length()); + std::string module_name = *v8::String::Utf8Value(args[0]->ToString()); + v8::Handle<v8::Promise::Resolver> resolver( + v8::Promise::Resolver::New(GetIsolate())); + args.GetReturnValue().Set(resolver->GetPromise()); + scoped_ptr<v8::UniquePersistent<v8::Promise::Resolver> > persistent_resolver( + new v8::UniquePersistent<v8::Promise::Resolver>(GetIsolate(), resolver)); + gin::ModuleRegistry* module_registry = + gin::ModuleRegistry::From(context_->v8_context()); + if (!module_registry) { + Warn(GetIsolate(), "Extension view no longer exists"); + resolver->Reject(v8::Exception::Error(v8::String::NewFromUtf8( + GetIsolate(), "Extension view no longer exists"))); + return; + } + module_registry->LoadModule(GetIsolate(), + module_name, + base::Bind(&ModuleSystem::OnModuleLoaded, + weak_factory_.GetWeakPtr(), + base::Passed(&persistent_resolver))); + if (module_registry->available_modules().count(module_name) == 0) + LoadModule(module_name); +} + v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) { v8::EscapableHandleScope handle_scope(GetIsolate()); // Keep in order with the arguments in RequireForJsInner. v8::Handle<v8::String> left = v8::String::NewFromUtf8( GetIsolate(), - "(function(require, requireNative, exports, " + "(function(define, require, requireNative, requireAsync, exports, " "console, privates," "$Array, $Function, $JSON, $Object, $RegExp, $String) {" "'use strict';"); @@ -586,4 +571,98 @@ void ModuleSystem::Private(const v8::FunctionCallbackInfo<v8::Value>& args) { args.GetReturnValue().Set(privates); } +v8::Handle<v8::Value> ModuleSystem::LoadModule(const std::string& module_name) { + v8::EscapableHandleScope handle_scope(GetIsolate()); + v8::Context::Scope context_scope(context()->v8_context()); + + v8::Handle<v8::Value> source(GetSource(module_name)); + if (source.IsEmpty() || source->IsUndefined()) { + Fatal(context_, "No source for require(" + module_name + ")"); + return v8::Undefined(GetIsolate()); + } + v8::Handle<v8::String> wrapped_source( + WrapSource(v8::Handle<v8::String>::Cast(source))); + // Modules are wrapped in (function(){...}) so they always return functions. + v8::Handle<v8::Value> func_as_value = + RunString(wrapped_source, + v8::String::NewFromUtf8(GetIsolate(), module_name.c_str())); + if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) { + Fatal(context_, "Bad source for require(" + module_name + ")"); + return v8::Undefined(GetIsolate()); + } + + v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value); + + v8::Handle<v8::Object> define_object = v8::Object::New(GetIsolate()); + gin::ModuleRegistry::InstallGlobals(GetIsolate(), define_object); + + v8::Local<v8::Value> exports = v8::Object::New(GetIsolate()); + v8::Handle<v8::Object> natives(NewInstance()); + CHECK(!natives.IsEmpty()); // this can happen if v8 has issues + + // These must match the argument order in WrapSource. + v8::Handle<v8::Value> args[] = { + // AMD. + define_object->Get(v8::String::NewFromUtf8(GetIsolate(), "define")), + // CommonJS. + natives->Get(v8::String::NewFromUtf8( + GetIsolate(), "require", v8::String::kInternalizedString)), + natives->Get(v8::String::NewFromUtf8( + GetIsolate(), "requireNative", v8::String::kInternalizedString)), + natives->Get(v8::String::NewFromUtf8( + GetIsolate(), "requireAsync", v8::String::kInternalizedString)), + exports, + // Libraries that we magically expose to every module. + console::AsV8Object(), + natives->Get(v8::String::NewFromUtf8( + GetIsolate(), "privates", v8::String::kInternalizedString)), + // Each safe builtin. Keep in order with the arguments in WrapSource. + context_->safe_builtins()->GetArray(), + context_->safe_builtins()->GetFunction(), + context_->safe_builtins()->GetJSON(), + context_->safe_builtins()->GetObjekt(), + context_->safe_builtins()->GetRegExp(), + context_->safe_builtins()->GetString(), + }; + { + v8::TryCatch try_catch; + try_catch.SetCaptureMessage(true); + context_->CallFunction(func, arraysize(args), args); + if (try_catch.HasCaught()) { + HandleException(try_catch); + return v8::Undefined(GetIsolate()); + } + } + return handle_scope.Escape(exports); +} + +void ModuleSystem::OnDidAddPendingModule( + const std::string& id, + const std::vector<std::string>& dependencies) { + if (!source_map_->Contains(id)) + return; + + gin::ModuleRegistry* registry = + gin::ModuleRegistry::From(context_->v8_context()); + DCHECK(registry); + for (std::vector<std::string>::const_iterator it = dependencies.begin(); + it != dependencies.end(); + ++it) { + if (registry->available_modules().count(*it) == 0) + LoadModule(*it); + } + registry->AttemptToLoadMoreModules(GetIsolate()); +} + +void ModuleSystem::OnModuleLoaded( + scoped_ptr<v8::UniquePersistent<v8::Promise::Resolver> > resolver, + v8::Handle<v8::Value> value) { + if (!is_valid()) + return; + v8::HandleScope handle_scope(GetIsolate()); + v8::Handle<v8::Promise::Resolver> resolver_local( + v8::Local<v8::Promise::Resolver>::New(GetIsolate(), *resolver)); + resolver_local->Resolve(value); +} + } // namespace extensions diff --git a/extensions/renderer/module_system.h b/extensions/renderer/module_system.h index eec25cc..4ab628f 100644 --- a/extensions/renderer/module_system.h +++ b/extensions/renderer/module_system.h @@ -15,6 +15,7 @@ #include "base/memory/scoped_ptr.h" #include "extensions/renderer/native_handler.h" #include "extensions/renderer/object_backed_native_handler.h" +#include "gin/modules/module_registry_observer.h" #include "v8/include/v8.h" namespace extensions { @@ -37,7 +38,8 @@ class ScriptContext; // Note that a ModuleSystem must be used only in conjunction with a single // v8::Context. // TODO(koz): Rename this to JavaScriptModuleSystem. -class ModuleSystem : public ObjectBackedNativeHandler { +class ModuleSystem : public ObjectBackedNativeHandler, + public gin::ModuleRegistryObserver { public: class SourceMap { public: @@ -158,9 +160,6 @@ class ModuleSystem : public ObjectBackedNativeHandler { // Called when an exception is thrown but not caught. void HandleException(const v8::TryCatch& try_catch); - // Ensure that require_ has been evaluated from require.js. - void EnsureRequireLoaded(); - void RequireForJs(const v8::FunctionCallbackInfo<v8::Value>& args); v8::Local<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name); @@ -183,15 +182,29 @@ class ModuleSystem : public ObjectBackedNativeHandler { v8::Handle<v8::Value> RequireNativeFromString(const std::string& native_name); void RequireNative(const v8::FunctionCallbackInfo<v8::Value>& args); - // Wraps |source| in a (function(require, requireNative, exports) {...}). + // Return a promise for a requested module. + // |args[0]| - the name of a module. + void RequireAsync(const v8::FunctionCallbackInfo<v8::Value>& args); + + // Wraps |source| in a (function(define, require, requireNative, ...) {...}). v8::Handle<v8::String> WrapSource(v8::Handle<v8::String> source); // NativeHandler implementation which returns the private area of an Object. void Private(const v8::FunctionCallbackInfo<v8::Value>& args); - // NativeHandler implementation which returns a function wrapper for a - // provided function. - void CreateFunctionWrapper(const v8::FunctionCallbackInfo<v8::Value>& args); + // Loads and runs a Javascript module. + v8::Handle<v8::Value> LoadModule(const std::string& module_name); + + // Invoked when a module is loaded in response to a requireAsync call. + // Resolves |resolver| with |value|. + void OnModuleLoaded( + scoped_ptr<v8::UniquePersistent<v8::Promise::Resolver> > resolver, + v8::Handle<v8::Value> value); + + // gin::ModuleRegistryObserver overrides. + virtual void OnDidAddPendingModule( + const std::string& id, + const std::vector<std::string>& dependencies) OVERRIDE; ScriptContext* context_; @@ -212,6 +225,8 @@ class ModuleSystem : public ObjectBackedNativeHandler { std::set<std::string> overridden_native_handlers_; + base::WeakPtrFactory<ModuleSystem> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(ModuleSystem); }; diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc index 60a558b..882837d 100644 --- a/extensions/renderer/script_context.cc +++ b/extensions/renderer/script_context.cc @@ -16,6 +16,7 @@ #include "extensions/common/extension_api.h" #include "extensions/common/extension_urls.h" #include "extensions/common/features/base_feature_provider.h" +#include "gin/per_context_data.h" #include "third_party/WebKit/public/web/WebDataSource.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" @@ -42,6 +43,7 @@ ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context, << " extension id: " << GetExtensionID() << "\n" << " frame: " << web_frame_ << "\n" << " context type: " << GetContextTypeDescription(); + gin::PerContextData::From(v8_context)->set_runner(this); } ScriptContext::~ScriptContext() { @@ -221,4 +223,21 @@ void ScriptContext::OnResponseReceived(const std::string& name, << *v8::String::Utf8Value(retval); } +void ScriptContext::Run(const std::string& source, + const std::string& resource_name) { + module_system_->RunString(source, resource_name); +} + +v8::Handle<v8::Value> ScriptContext::Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) { + return CallFunction(function, argc, argv); +} + +gin::ContextHolder* ScriptContext::GetContextHolder() { + v8::HandleScope handle_scope(isolate()); + return gin::PerContextData::From(v8_context())->context_holder(); +} + } // namespace extensions diff --git a/extensions/renderer/script_context.h b/extensions/renderer/script_context.h index 26002ec..7b19ac1 100644 --- a/extensions/renderer/script_context.h +++ b/extensions/renderer/script_context.h @@ -14,6 +14,7 @@ #include "extensions/renderer/request_sender.h" #include "extensions/renderer/safe_builtins.h" #include "extensions/renderer/scoped_persistent.h" +#include "gin/runner.h" #include "v8/include/v8.h" namespace blink { @@ -28,7 +29,7 @@ namespace extensions { class Extension; // Extensions wrapper for a v8 context. -class ScriptContext : public RequestSender::Source { +class ScriptContext : public RequestSender::Source, public gin::Runner { public: ScriptContext(const v8::Handle<v8::Context>& context, blink::WebFrame* frame, @@ -120,6 +121,15 @@ class ScriptContext : public RequestSender::Source { const base::ListValue& response, const std::string& error) OVERRIDE; + // gin::Runner overrides. + virtual void Run(const std::string& source, + const std::string& resource_name) OVERRIDE; + virtual v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) OVERRIDE; + virtual gin::ContextHolder* GetContextHolder() OVERRIDE; + protected: // The v8 context the bindings are accessible to. ScopedPersistent<v8::Context> v8_context_; diff --git a/extensions/renderer/script_context_set_unittest.cc b/extensions/renderer/script_context_set_unittest.cc index 48272f1..6b8849b 100644 --- a/extensions/renderer/script_context_set_unittest.cc +++ b/extensions/renderer/script_context_set_unittest.cc @@ -7,6 +7,8 @@ #include "extensions/common/features/feature.h" #include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context_set.h" +#include "gin/public/context_holder.h" +#include "gin/public/isolate_holder.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "v8/include/v8.h" @@ -19,15 +21,20 @@ TEST(ScriptContextSet, Lifecycle) { ScriptContextSet context_set; v8::Isolate* isolate = v8::Isolate::GetCurrent(); + gin::IsolateHolder isolate_holder(isolate, NULL); v8::HandleScope handle_scope(isolate); - v8::Handle<v8::Context> v8_context(v8::Context::New(isolate)); + gin::ContextHolder context_holder(isolate); + context_holder.SetContext(v8::Context::New(isolate)); // Dirty hack, but we don't actually need the frame, and this is easier than // creating a whole webview. blink::WebFrame* frame = reinterpret_cast<blink::WebFrame*>(1); const Extension* extension = NULL; - ScriptContext* context = new ScriptContext( - v8_context, frame, extension, Feature::BLESSED_EXTENSION_CONTEXT); + ScriptContext* context = + new ScriptContext(context_holder.context(), + frame, + extension, + Feature::BLESSED_EXTENSION_CONTEXT); context_set.Add(context); EXPECT_EQ(1u, context_set.GetAll().count(context)); diff --git a/extensions/renderer/v8_schema_registry.cc b/extensions/renderer/v8_schema_registry.cc index 77a91f6..3316313 100644 --- a/extensions/renderer/v8_schema_registry.cc +++ b/extensions/renderer/v8_schema_registry.cc @@ -105,13 +105,13 @@ v8::Handle<v8::Context> V8SchemaRegistry::GetOrCreateContext( v8::Isolate* isolate) { // It's ok to create local handles in this function, since this is only called // when we have a HandleScope. - if (context_.IsEmpty()) { - v8::Handle<v8::Context> context = v8::Context::New(isolate); - context_.reset(context); + if (!context_holder_) { + context_holder_.reset(new gin::ContextHolder(isolate)); + context_holder_->SetContext(v8::Context::New(isolate)); schema_cache_.reset(new SchemaCache(isolate)); - return context; + return context_holder_->context(); } - return context_.NewHandle(isolate); + return context_holder_->context(); } } // namespace extensions diff --git a/extensions/renderer/v8_schema_registry.h b/extensions/renderer/v8_schema_registry.h index b26f5b3..5a3b20c 100644 --- a/extensions/renderer/v8_schema_registry.h +++ b/extensions/renderer/v8_schema_registry.h @@ -11,7 +11,7 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "extensions/renderer/scoped_persistent.h" +#include "gin/public/context_holder.h" #include "v8/include/v8-util.h" #include "v8/include/v8.h" @@ -43,9 +43,9 @@ class V8SchemaRegistry { typedef v8::StdPersistentValueMap<std::string, v8::Object> SchemaCache; scoped_ptr<SchemaCache> schema_cache_; - // Single per-instance v8::Context to create v8::Values. + // Single per-instance gin::ContextHolder to create v8::Values. // Created lazily via GetOrCreateContext. - ScopedPersistent<v8::Context> context_; + scoped_ptr<gin::ContextHolder> context_holder_; DISALLOW_COPY_AND_ASSIGN(V8SchemaRegistry); }; |