summaryrefslogtreecommitdiffstats
path: root/extensions/renderer
diff options
context:
space:
mode:
authorsammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-09 05:39:38 +0000
committersammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-09 05:39:38 +0000
commitd9f51dad0a4f9c154da5ff6ba28cd7c5c8ce0ee7 (patch)
treeae859aa19d53161377ca81b75b411aa3a8329c3c /extensions/renderer
parent86fcf7f3db64dd037831ec4233f626fb4428f1e7 (diff)
downloadchromium_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/DEPS2
-rw-r--r--extensions/renderer/module_system.cc181
-rw-r--r--extensions/renderer/module_system.h31
-rw-r--r--extensions/renderer/script_context.cc19
-rw-r--r--extensions/renderer/script_context.h12
-rw-r--r--extensions/renderer/script_context_set_unittest.cc13
-rw-r--r--extensions/renderer/v8_schema_registry.cc10
-rw-r--r--extensions/renderer/v8_schema_registry.h6
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);
};