Commit d9f51dad authored by sammc@chromium.org's avatar sammc@chromium.org

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
parent 86fcf7f3
...@@ -17,6 +17,7 @@ include_rules = [ ...@@ -17,6 +17,7 @@ include_rules = [
"+content/public/renderer", "+content/public/renderer",
"+extensions/common", "+extensions/common",
"+extensions/renderer", "+extensions/renderer",
"+gin",
"+grit", # For generated headers "+grit", # For generated headers
"+ppapi/c", "+ppapi/c",
"+ppapi/shared_impl", "+ppapi/shared_impl",
......
...@@ -13,18 +13,18 @@ class JsonSchemaTest : public ModuleSystemTest { ...@@ -13,18 +13,18 @@ class JsonSchemaTest : public ModuleSystemTest {
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
ModuleSystemTest::SetUp(); ModuleSystemTest::SetUp();
RegisterModule("json_schema", IDR_JSON_SCHEMA_JS); env()->RegisterModule("json_schema", IDR_JSON_SCHEMA_JS);
RegisterModule("utils", IDR_UTILS_JS); env()->RegisterModule("utils", IDR_UTILS_JS);
context_->module_system()->RegisterNativeHandler("schema_registry", env()->module_system()->RegisterNativeHandler(
schema_registry_.AsNativeHandler()); "schema_registry", schema_registry_.AsNativeHandler());
RegisterTestFile("json_schema_test", "json_schema_test.js"); env()->RegisterTestFile("json_schema_test", "json_schema_test.js");
} }
protected: protected:
void TestFunction(const std::string& test_name) { void TestFunction(const std::string& test_name) {
context_->module_system()->CallModuleMethod("json_schema_test", test_name); env()->module_system()->CallModuleMethod("json_schema_test", test_name);
} }
private: private:
......
...@@ -12,19 +12,22 @@ namespace { ...@@ -12,19 +12,22 @@ namespace {
class MessagingUtilsUnittest : public ModuleSystemTest { class MessagingUtilsUnittest : public ModuleSystemTest {
protected: protected:
void RegisterTestModule(const char* code) { void RegisterTestModule(const char* code) {
RegisterModule("test", base::StringPrintf( env()->RegisterModule(
"var assert = requireNative('assert');\n" "test",
"var AssertTrue = assert.AssertTrue;\n" base::StringPrintf(
"var AssertFalse = assert.AssertFalse;\n" "var assert = requireNative('assert');\n"
"var messagingUtils = require('messaging_utils');\n" "var AssertTrue = assert.AssertTrue;\n"
"%s", code)); "var AssertFalse = assert.AssertFalse;\n"
"var messagingUtils = require('messaging_utils');\n"
"%s",
code));
} }
private: private:
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
ModuleSystemTest::SetUp(); ModuleSystemTest::SetUp();
RegisterModule("messaging_utils", IDR_MESSAGING_UTILS_JS); env()->RegisterModule("messaging_utils", IDR_MESSAGING_UTILS_JS);
} }
}; };
...@@ -35,65 +38,65 @@ TEST_F(MessagingUtilsUnittest, TestNothing) { ...@@ -35,65 +38,65 @@ TEST_F(MessagingUtilsUnittest, TestNothing) {
TEST_F(MessagingUtilsUnittest, NoArguments) { TEST_F(MessagingUtilsUnittest, NoArguments) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterTestModule( RegisterTestModule(
"var args = messagingUtils.alignSendMessageArguments();\n" "var args = messagingUtils.alignSendMessageArguments();\n"
"AssertTrue(args === null);"); "AssertTrue(args === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, ZeroArguments) { TEST_F(MessagingUtilsUnittest, ZeroArguments) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterTestModule( RegisterTestModule(
"var args = messagingUtils.alignSendMessageArguments([]);" "var args = messagingUtils.alignSendMessageArguments([]);"
"AssertTrue(args === null);"); "AssertTrue(args === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, TooManyArgumentsNoOptions) { TEST_F(MessagingUtilsUnittest, TooManyArgumentsNoOptions) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterTestModule( RegisterTestModule(
"var args = messagingUtils.alignSendMessageArguments(\n" "var args = messagingUtils.alignSendMessageArguments(\n"
" ['a', 'b', 'c', 'd']);\n" " ['a', 'b', 'c', 'd']);\n"
"AssertTrue(args === null);"); "AssertTrue(args === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, TooManyArgumentsWithOptions) { TEST_F(MessagingUtilsUnittest, TooManyArgumentsWithOptions) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterTestModule( RegisterTestModule(
"var args = messagingUtils.alignSendMessageArguments(\n" "var args = messagingUtils.alignSendMessageArguments(\n"
" ['a', 'b', 'c', 'd', 'e'], true);\n" " ['a', 'b', 'c', 'd', 'e'], true);\n"
"AssertTrue(args === null);"); "AssertTrue(args === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, FinalArgumentIsNotAFunctionNoOptions) { TEST_F(MessagingUtilsUnittest, FinalArgumentIsNotAFunctionNoOptions) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterTestModule( RegisterTestModule(
"var args = messagingUtils.alignSendMessageArguments(\n" "var args = messagingUtils.alignSendMessageArguments(\n"
" ['a', 'b', 'c']);\n" " ['a', 'b', 'c']);\n"
"AssertTrue(args === null);"); "AssertTrue(args === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, FinalArgumentIsNotAFunctionWithOptions) { TEST_F(MessagingUtilsUnittest, FinalArgumentIsNotAFunctionWithOptions) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterTestModule( RegisterTestModule(
"var args = messagingUtils.alignSendMessageArguments(\n" "var args = messagingUtils.alignSendMessageArguments(\n"
" ['a', 'b', 'c', 'd'], true);\n" " ['a', 'b', 'c', 'd'], true);\n"
"AssertTrue(args === null);"); "AssertTrue(args === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, OneStringArgument) { TEST_F(MessagingUtilsUnittest, OneStringArgument) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
// Because the request argument is required, a single argument must get // Because the request argument is required, a single argument must get
// mapped to it rather than to the optional targetId argument. // mapped to it rather than to the optional targetId argument.
RegisterTestModule( RegisterTestModule(
...@@ -102,12 +105,12 @@ TEST_F(MessagingUtilsUnittest, OneStringArgument) { ...@@ -102,12 +105,12 @@ TEST_F(MessagingUtilsUnittest, OneStringArgument) {
"AssertTrue(args[0] === null);\n" "AssertTrue(args[0] === null);\n"
"AssertTrue(args[1] == 'a');\n" "AssertTrue(args[1] == 'a');\n"
"AssertTrue(args[2] === null);"); "AssertTrue(args[2] === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, OneStringAndOneNullArgument) { TEST_F(MessagingUtilsUnittest, OneStringAndOneNullArgument) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
// Explicitly specifying null as the request is allowed. // Explicitly specifying null as the request is allowed.
RegisterTestModule( RegisterTestModule(
"var args = messagingUtils.alignSendMessageArguments(['a', null]);\n" "var args = messagingUtils.alignSendMessageArguments(['a', null]);\n"
...@@ -115,24 +118,24 @@ TEST_F(MessagingUtilsUnittest, OneStringAndOneNullArgument) { ...@@ -115,24 +118,24 @@ TEST_F(MessagingUtilsUnittest, OneStringAndOneNullArgument) {
"AssertTrue(args[0] == 'a');\n" "AssertTrue(args[0] == 'a');\n"
"AssertTrue(args[1] === null);\n" "AssertTrue(args[1] === null);\n"
"AssertTrue(args[2] === null);"); "AssertTrue(args[2] === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, OneNullAndOneStringArgument) { TEST_F(MessagingUtilsUnittest, OneNullAndOneStringArgument) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterTestModule( RegisterTestModule(
"var args = messagingUtils.alignSendMessageArguments([null, 'a']);\n" "var args = messagingUtils.alignSendMessageArguments([null, 'a']);\n"
"AssertTrue(args.length == 3);\n" "AssertTrue(args.length == 3);\n"
"AssertTrue(args[0] === null);\n" "AssertTrue(args[0] === null);\n"
"AssertTrue(args[1] == 'a');\n" "AssertTrue(args[1] == 'a');\n"
"AssertTrue(args[2] === null);"); "AssertTrue(args[2] === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, OneStringAndOneFunctionArgument) { TEST_F(MessagingUtilsUnittest, OneStringAndOneFunctionArgument) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
// When the arguments are a string and a function, the function is // When the arguments are a string and a function, the function is
// unambiguously the responseCallback. Because the request argument is // unambiguously the responseCallback. Because the request argument is
// required, the remaining argument must get mapped to it rather than to the // required, the remaining argument must get mapped to it rather than to the
...@@ -144,12 +147,12 @@ TEST_F(MessagingUtilsUnittest, OneStringAndOneFunctionArgument) { ...@@ -144,12 +147,12 @@ TEST_F(MessagingUtilsUnittest, OneStringAndOneFunctionArgument) {
"AssertTrue(args[0] === null);\n" "AssertTrue(args[0] === null);\n"
"AssertTrue(args[1] == 'a');\n" "AssertTrue(args[1] == 'a');\n"
"AssertTrue(args[2] == cb);"); "AssertTrue(args[2] == cb);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, OneStringAndOneObjectArgument) { TEST_F(MessagingUtilsUnittest, OneStringAndOneObjectArgument) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
// This tests an ambiguous set of arguments when options are present: // This tests an ambiguous set of arguments when options are present:
// chrome.runtime.sendMessage('target', {'msg': 'this is a message'}); // chrome.runtime.sendMessage('target', {'msg': 'this is a message'});
// vs. // vs.
...@@ -168,12 +171,12 @@ TEST_F(MessagingUtilsUnittest, OneStringAndOneObjectArgument) { ...@@ -168,12 +171,12 @@ TEST_F(MessagingUtilsUnittest, OneStringAndOneObjectArgument) {
"AssertTrue(args[1] == obj);\n" "AssertTrue(args[1] == obj);\n"
"AssertTrue(args[2] === null);\n" "AssertTrue(args[2] === null);\n"
"AssertTrue(args[3] === null);"); "AssertTrue(args[3] === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(MessagingUtilsUnittest, TwoObjectArguments) { TEST_F(MessagingUtilsUnittest, TwoObjectArguments) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
// When two non-string arguments are provided and options are present, the // When two non-string arguments are provided and options are present, the
// two arguments must match request and options, respectively, because // two arguments must match request and options, respectively, because
// targetId must be a string. // targetId must be a string.
...@@ -187,7 +190,7 @@ TEST_F(MessagingUtilsUnittest, TwoObjectArguments) { ...@@ -187,7 +190,7 @@ TEST_F(MessagingUtilsUnittest, TwoObjectArguments) {
"AssertTrue(args[1] == obj1);\n" "AssertTrue(args[1] == obj1);\n"
"AssertTrue(args[2] == obj2);\n" "AssertTrue(args[2] == obj2);\n"
"AssertTrue(args[3] === null);"); "AssertTrue(args[3] === null);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
} // namespace } // namespace
......
...@@ -12,55 +12,52 @@ class SafeBuiltinsUnittest : public ModuleSystemTest { ...@@ -12,55 +12,52 @@ class SafeBuiltinsUnittest : public ModuleSystemTest {
TEST_F(SafeBuiltinsUnittest, TestNotOriginalObject) { TEST_F(SafeBuiltinsUnittest, TestNotOriginalObject) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');\n" "var assert = requireNative('assert');\n"
"Array.foo = 10;\n" "Array.foo = 10;\n"
"assert.AssertTrue(!$Array.hasOwnProperty('foo'));\n" "assert.AssertTrue(!$Array.hasOwnProperty('foo'));\n");
); env()->module_system()->Require("test");
context_->module_system()->Require("test");
} }
TEST_F(SafeBuiltinsUnittest, TestSelf) { TEST_F(SafeBuiltinsUnittest, TestSelf) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');\n" "var assert = requireNative('assert');\n"
"Array.foo = 10;\n" "Array.foo = 10;\n"
"assert.AssertTrue($Array.self.foo == 10);\n" "assert.AssertTrue($Array.self.foo == 10);\n"
"var arr = $Array.self(1);\n" "var arr = $Array.self(1);\n"
"assert.AssertTrue(arr.length == 1);\n" "assert.AssertTrue(arr.length == 1);\n"
"assert.AssertTrue(arr[0] === undefined);\n" "assert.AssertTrue(arr[0] === undefined);\n");
); env()->module_system()->Require("test");
context_->module_system()->Require("test");
} }
TEST_F(SafeBuiltinsUnittest, TestStaticFunction) { TEST_F(SafeBuiltinsUnittest, TestStaticFunction) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');\n" "var assert = requireNative('assert');\n"
"Object.keys = function() {throw new Error()};\n" "Object.keys = function() {throw new Error()};\n"
"var obj = {a: 10};\n" "var obj = {a: 10};\n"
"var keys = $Object.keys(obj);\n" "var keys = $Object.keys(obj);\n"
"assert.AssertTrue(keys.length == 1);\n" "assert.AssertTrue(keys.length == 1);\n"
"assert.AssertTrue(keys[0] == 'a');\n" "assert.AssertTrue(keys[0] == 'a');\n");
); env()->module_system()->Require("test");
context_->module_system()->Require("test");
} }
TEST_F(SafeBuiltinsUnittest, TestInstanceMethod) { TEST_F(SafeBuiltinsUnittest, TestInstanceMethod) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule(
"test",
"var assert = requireNative('assert');\n" "var assert = requireNative('assert');\n"
"Array.prototype.push = function() {throw new Error();}\n" "Array.prototype.push = function() {throw new Error();}\n"
"var arr = []\n" "var arr = []\n"
"$Array.push(arr, 1);\n" "$Array.push(arr, 1);\n"
"assert.AssertTrue(arr.length == 1);\n" "assert.AssertTrue(arr.length == 1);\n"
"assert.AssertTrue(arr[0] == 1);\n" "assert.AssertTrue(arr[0] == 1);\n");
); env()->module_system()->Require("test");
context_->module_system()->Require("test");
} }
// NOTE: JSON is already tested in ExtensionApiTest.Messaging, via // NOTE: JSON is already tested in ExtensionApiTest.Messaging, via
......
...@@ -12,26 +12,26 @@ namespace { ...@@ -12,26 +12,26 @@ namespace {
class UtilsUnittest : public ModuleSystemTest { class UtilsUnittest : public ModuleSystemTest {
protected: protected:
void RegisterTestModule(const char* code) { void RegisterTestModule(const char* code) {
RegisterModule("test", env()->RegisterModule("test",
base::StringPrintf( base::StringPrintf(
"var assert = requireNative('assert');\n" "var assert = requireNative('assert');\n"
"var AssertTrue = assert.AssertTrue;\n" "var AssertTrue = assert.AssertTrue;\n"
"var AssertFalse = assert.AssertFalse;\n" "var AssertFalse = assert.AssertFalse;\n"
"var utils = require('utils');\n" "var utils = require('utils');\n"
"%s", "%s",
code)); code));
} }
private: private:
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
ModuleSystemTest::SetUp(); ModuleSystemTest::SetUp();
RegisterModule("utils", IDR_UTILS_JS); env()->RegisterModule("utils", IDR_UTILS_JS);
OverrideNativeHandler("schema_registry", env()->OverrideNativeHandler("schema_registry",
"exports.GetSchema = function() {};"); "exports.GetSchema = function() {};");
OverrideNativeHandler("logging", env()->OverrideNativeHandler("logging",
"exports.CHECK = function() {};\n" "exports.CHECK = function() {};\n"
"exports.WARNING = function() {};"); "exports.WARNING = function() {};");
} }
}; };
...@@ -41,7 +41,7 @@ TEST_F(UtilsUnittest, TestNothing) { ...@@ -41,7 +41,7 @@ TEST_F(UtilsUnittest, TestNothing) {
TEST_F(UtilsUnittest, SuperClass) { TEST_F(UtilsUnittest, SuperClass) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterTestModule( RegisterTestModule(
"function SuperClassImpl() {}\n" "function SuperClassImpl() {}\n"
"\n" "\n"
...@@ -116,7 +116,7 @@ TEST_F(UtilsUnittest, SuperClass) { ...@@ -116,7 +116,7 @@ TEST_F(UtilsUnittest, SuperClass) {
"AssertTrue(subsub instanceof SubClass);\n" "AssertTrue(subsub instanceof SubClass);\n"
"AssertTrue(subsub instanceof SubSubClass);\n"); "AssertTrue(subsub instanceof SubSubClass);\n");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
} // namespace } // namespace
......
...@@ -14,6 +14,7 @@ include_rules = [ ...@@ -14,6 +14,7 @@ include_rules = [
# within content/ and a test within chrome/. # within content/ and a test within chrome/.
"+content/public", "+content/public",
"+gin/public",
"+grit", # For generated headers "+grit", # For generated headers
"+media/base", "+media/base",
"+mojo/embedder", "+mojo/embedder",
......
...@@ -62,7 +62,8 @@ base::LazyInstance<V8ExtensionConfigurator>::Leaky g_v8_extension_configurator = ...@@ -62,7 +62,8 @@ base::LazyInstance<V8ExtensionConfigurator>::Leaky g_v8_extension_configurator =
} // namespace } // namespace
// Native JS functions for doing asserts. // Native JS functions for doing asserts.
class ModuleSystemTest::AssertNatives : public ObjectBackedNativeHandler { class ModuleSystemTestEnvironment::AssertNatives
: public ObjectBackedNativeHandler {
public: public:
explicit AssertNatives(extensions::ChromeV8Context* context) explicit AssertNatives(extensions::ChromeV8Context* context)
: ObjectBackedNativeHandler(context), : ObjectBackedNativeHandler(context),
...@@ -95,7 +96,8 @@ class ModuleSystemTest::AssertNatives : public ObjectBackedNativeHandler { ...@@ -95,7 +96,8 @@ class ModuleSystemTest::AssertNatives : public ObjectBackedNativeHandler {
}; };
// Source map that operates on std::strings. // Source map that operates on std::strings.
class ModuleSystemTest::StringSourceMap : public ModuleSystem::SourceMap { class ModuleSystemTestEnvironment::StringSourceMap
: public extensions::ModuleSystem::SourceMap {
public: public:
StringSourceMap() {} StringSourceMap() {}
virtual ~StringSourceMap() {} virtual ~StringSourceMap() {}
...@@ -120,19 +122,20 @@ class ModuleSystemTest::StringSourceMap : public ModuleSystem::SourceMap { ...@@ -120,19 +122,20 @@ class ModuleSystemTest::StringSourceMap : public ModuleSystem::SourceMap {
std::map<std::string, std::string> source_map_; std::map<std::string, std::string> source_map_;
}; };
ModuleSystemTest::ModuleSystemTest() ModuleSystemTestEnvironment::ModuleSystemTestEnvironment(
: isolate_(v8::Isolate::GetCurrent()), gin::IsolateHolder* isolate_holder)
handle_scope_(isolate_), : isolate_holder_(isolate_holder),
context_( context_holder_(new gin::ContextHolder(isolate_holder_->isolate())),
new extensions::ChromeV8Context( handle_scope_(isolate_holder_->isolate()),
v8::Context::New( source_map_(new StringSourceMap()) {
isolate_, context_holder_->SetContext(
g_v8_extension_configurator.Get().GetConfiguration()), v8::Context::New(isolate_holder->isolate(),
NULL, // WebFrame g_v8_extension_configurator.Get().GetConfiguration()));
NULL, // Extension context_.reset(new extensions::ChromeV8Context(
extensions::Feature::UNSPECIFIED_CONTEXT)), context_holder_->context(),
source_map_(new StringSourceMap()), NULL, // WebFrame
should_assertions_be_made_(true) { NULL, // Extension
extensions::Feature::UNSPECIFIED_CONTEXT));
context_->v8_context()->Enter(); context_->v8_context()->Enter();
assert_natives_ = new AssertNatives(context_.get()); assert_natives_ = new AssertNatives(context_.get());
...@@ -152,30 +155,33 @@ ModuleSystemTest::ModuleSystemTest() ...@@ -152,30 +155,33 @@ ModuleSystemTest::ModuleSystemTest()
scoped_ptr<ModuleSystem::ExceptionHandler>(new FailsOnException)); scoped_ptr<ModuleSystem::ExceptionHandler>(new FailsOnException));
} }
ModuleSystemTest::~ModuleSystemTest() { ModuleSystemTestEnvironment::~ModuleSystemTestEnvironment() {
context_->v8_context()->Exit(); if (context_)
context_->v8_context()->Exit();
} }
void ModuleSystemTest::RegisterModule(const std::string& name, void ModuleSystemTestEnvironment::RegisterModule(const std::string& name,
const std::string& code) { const std::string& code) {
source_map_->RegisterModule(name, code); source_map_->RegisterModule(name, code);
} }
void ModuleSystemTest::RegisterModule(const std::string& name, void ModuleSystemTestEnvironment::RegisterModule(const std::string& name,
int resource_id) { int resource_id) {
const std::string& code = ResourceBundle::GetSharedInstance(). const std::string& code = ResourceBundle::GetSharedInstance().
GetRawDataResource(resource_id).as_string(); GetRawDataResource(resource_id).as_string();
source_map_->RegisterModule(name, code); source_map_->RegisterModule(name, code);
} }
void ModuleSystemTest::OverrideNativeHandler(const std::string& name, void ModuleSystemTestEnvironment::OverrideNativeHandler(
const std::string& code) { const std::string& name,
const std::string& code) {
RegisterModule(name, code); RegisterModule(name, code);
context_->module_system()->OverrideNativeHandlerForTest(name); context_->module_system()->OverrideNativeHandlerForTest(name);
} }
void ModuleSystemTest::RegisterTestFile(const std::string& module_name, void ModuleSystemTestEnvironment::RegisterTestFile(
const std::string& file_name) { const std::string& module_name,
const std::string& file_name) {
base::FilePath test_js_file_path; base::FilePath test_js_file_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_js_file_path)); ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_js_file_path));
test_js_file_path = test_js_file_path.AppendASCII("extensions") test_js_file_path = test_js_file_path.AppendASCII("extensions")
...@@ -185,22 +191,49 @@ void ModuleSystemTest::RegisterTestFile(const std::string& module_name, ...@@ -185,22 +191,49 @@ void ModuleSystemTest::RegisterTestFile(const std::string& module_name,
source_map_->RegisterModule(module_name, test_js); source_map_->RegisterModule(module_name, test_js);
} }
void ModuleSystemTest::TearDown() { void ModuleSystemTestEnvironment::ShutdownGin() {
// All tests must assert at least once unless otherwise specified. context_holder_.reset();
EXPECT_EQ(should_assertions_be_made_,
assert_natives_->assertion_made());
EXPECT_FALSE(assert_natives_->failed());
} }
void ModuleSystemTest::ExpectNoAssertionsMade() { void ModuleSystemTestEnvironment::ShutdownModuleSystem() {
should_assertions_be_made_ = false; context_->v8_context()->Exit();
context_.reset();
} }
v8::Handle<v8::Object> ModuleSystemTest::CreateGlobal(const std::string& name) { v8::Handle<v8::Object> ModuleSystemTestEnvironment::CreateGlobal(
v8::Isolate* isolate = v8::Isolate::GetCurrent(); const std::string& name) {
v8::Isolate* isolate = isolate_holder_->isolate();
v8::EscapableHandleScope handle_scope(isolate); v8::EscapableHandleScope handle_scope(isolate);
v8::Local<v8::Object> object = v8::Object::New(isolate); v8::Local<v8::Object> object = v8::Object::New(isolate);
isolate->GetCurrentContext()->Global()->Set( isolate->GetCurrentContext()->Global()->Set(
v8::String::NewFromUtf8(isolate, name.c_str()), object); v8::String::NewFromUtf8(isolate, name.c_str()), object);
return handle_scope.Escape(object); return handle_scope.Escape(object);
} }
ModuleSystemTest::ModuleSystemTest()
: isolate_holder_(v8::Isolate::GetCurrent(), NULL),
env_(CreateEnvironment()),
should_assertions_be_made_(true) {
}
ModuleSystemTest::~ModuleSystemTest() {
}
void ModuleSystemTest::TearDown() {
// All tests must assert at least once unless otherwise specified.
EXPECT_EQ(should_assertions_be_made_,
env_->assert_natives()->assertion_made());
EXPECT_FALSE(env_->assert_natives()->failed());
}
scoped_ptr<ModuleSystemTestEnvironment> ModuleSystemTest::CreateEnvironment() {
return make_scoped_ptr(new ModuleSystemTestEnvironment(&isolate_holder_));
}
void ModuleSystemTest::ExpectNoAssertionsMade() {
should_assertions_be_made_ = false;
}
void ModuleSystemTest::RunResolvedPromises() {
isolate_holder_.isolate()->RunMicrotasks();
}
...@@ -8,9 +8,63 @@ ...@@ -8,9 +8,63 @@
#include "chrome/renderer/extensions/chrome_v8_context.h" #include "chrome/renderer/extensions/chrome_v8_context.h"
#include "extensions/renderer/module_system.h" #include "extensions/renderer/module_system.h"
#include "extensions/renderer/scoped_persistent.h" #include "extensions/renderer/scoped_persistent.h"
#include "gin/public/context_holder.h"
#include "gin/public/isolate_holder.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
class ModuleSystemTestEnvironment {
public:
class AssertNatives;
class StringSourceMap;
explicit ModuleSystemTestEnvironment(gin::IsolateHolder* isolate_holder);
~ModuleSystemTestEnvironment();
// Register a named JS module in the module system.
void RegisterModule(const std::string& name, const std::string& code);
// Register a named JS module with source retrieved from a ResourceBundle.
void RegisterModule(const std::string& name, int resource_id);
// Register a named JS module in the module system and tell the module system
// to use it to handle any requireNative() calls for native modules with that
// name.
void OverrideNativeHandler(const std::string& name, const std::string& code);
// Registers |file_name| from chrome/test/data/extensions as a module name
// |module_name|.
void RegisterTestFile(const std::string& module_name,
const std::string& file_name);
// Create an empty object in the global scope with name |name|.
v8::Handle<v8::Object> CreateGlobal(const std::string& name);
void ShutdownGin();
void ShutdownModuleSystem();
extensions::ModuleSystem* module_system() {
return context_->module_system();
}
extensions::ChromeV8Context* context() { return context_.get(); }
v8::Isolate* isolate() { return isolate_holder_->isolate(); }
AssertNatives* assert_natives() { return assert_natives_; }
private:
gin::IsolateHolder* isolate_holder_;
scoped_ptr<gin::ContextHolder> context_holder_;
v8::HandleScope handle_scope_;
scoped_ptr<extensions::ChromeV8Context> context_;
AssertNatives* assert_natives_;
scoped_ptr<StringSourceMap> source_map_;
DISALLOW_COPY_AND_ASSIGN(ModuleSystemTestEnvironment);
};
// Test fixture for testing JS that makes use of the module system. // Test fixture for testing JS that makes use of the module system.
// //
// Typically tests will look like: // Typically tests will look like:
...@@ -33,36 +87,21 @@ class ModuleSystemTest : public testing::Test { ...@@ -33,36 +87,21 @@ class ModuleSystemTest : public testing::Test {
virtual void TearDown() OVERRIDE; virtual void TearDown() OVERRIDE;
protected: protected:
// Register a named JS module in the module system. ModuleSystemTestEnvironment* env() { return env_.get(); }
void RegisterModule(const std::string& name, const std::string& code);
// Register a named JS module with source retrieved from a ResourceBundle.
void RegisterModule(const std::string& name, int resource_id);
// Register a named JS module in the module system and tell the module system
// to use it to handle any requireNative() calls for native modules with that
// name.
void OverrideNativeHandler(const std::string& name, const std::string& code);
// Registers |file_name| from chrome/test/data/extensions as a module name scoped_ptr<ModuleSystemTestEnvironment> CreateEnvironment();
// |module_name|.
void RegisterTestFile(const std::string& module_name,
const std::string& file_name);
// Make the test fail if any asserts are called. By default a test will fail // Make the test fail if any asserts are called. By default a test will fail
// if no asserts are called. // if no asserts are called.
void ExpectNoAssertionsMade(); void ExpectNoAssertionsMade();
// Create an empty object in the global scope with name |name|. // Runs promises that have been resolved. Resolved promises will not run
v8::Handle<v8::Object> CreateGlobal(const std::string& name); // until this is called.
void RunResolvedPromises();
v8::Isolate* isolate_; private:
v8::HandleScope handle_scope_; gin::IsolateHolder isolate_holder_;
scoped_ptr<extensions::ChromeV8Context> context_; scoped_ptr<ModuleSystemTestEnvironment> env_;
class AssertNatives;
AssertNatives* assert_natives_;
class StringSourceMap;
scoped_ptr<StringSourceMap> source_map_;
bool should_assertions_be_made_; bool should_assertions_be_made_;
private: private:
......
...@@ -476,6 +476,7 @@ ...@@ -476,6 +476,7 @@
'dependencies': [ 'dependencies': [
'extensions_resources.gyp:extensions_resources', 'extensions_resources.gyp:extensions_resources',
'../chrome/chrome_resources.gyp:chrome_resources', '../chrome/chrome_resources.gyp:chrome_resources',
'../gin/gin.gyp:gin',
'../third_party/WebKit/public/blink.gyp:blink', '../third_party/WebKit/public/blink.gyp:blink',
], ],
'include_dirs': [ 'include_dirs': [
......
...@@ -3,6 +3,8 @@ include_rules = [ ...@@ -3,6 +3,8 @@ include_rules = [
"+content/public/common", "+content/public/common",
"+content/public/renderer", "+content/public/renderer",
"+gin",
"+third_party/skia/include/core", "+third_party/skia/include/core",
"+third_party/WebKit/public/platform", "+third_party/WebKit/public/platform",
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "extensions/renderer/console.h" #include "extensions/renderer/console.h"
#include "extensions/renderer/safe_builtins.h" #include "extensions/renderer/safe_builtins.h"
#include "extensions/renderer/script_context.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/WebFrame.h"
#include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h" #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
...@@ -121,13 +122,17 @@ ModuleSystem::ModuleSystem(ScriptContext* context, SourceMap* source_map) ...@@ -121,13 +122,17 @@ ModuleSystem::ModuleSystem(ScriptContext* context, SourceMap* source_map)
context_(context), context_(context),
source_map_(source_map), source_map_(source_map),
natives_enabled_(0), natives_enabled_(0),
exception_handler_(new DefaultExceptionHandler(context)) { exception_handler_(new DefaultExceptionHandler(context)),
weak_factory_(this) {
RouteFunction( RouteFunction(
"require", "require",
base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this))); base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this)));
RouteFunction( RouteFunction(
"requireNative", "requireNative",
base::Bind(&ModuleSystem::RequireNative, base::Unretained(this))); base::Bind(&ModuleSystem::RequireNative, base::Unretained(this)));
RouteFunction(
"requireAsync",
base::Bind(&ModuleSystem::RequireAsync, base::Unretained(this)));
RouteFunction("privates", RouteFunction("privates",
base::Bind(&ModuleSystem::Private, base::Unretained(this))); base::Bind(&ModuleSystem::Private, base::Unretained(this)));
...@@ -137,6 +142,8 @@ ModuleSystem::ModuleSystem(ScriptContext* context, SourceMap* source_map) ...@@ -137,6 +142,8 @@ ModuleSystem::ModuleSystem(ScriptContext* context, SourceMap* source_map)
v8::Object::New(isolate)); v8::Object::New(isolate));
global->SetHiddenValue(v8::String::NewFromUtf8(isolate, kModuleSystem), global->SetHiddenValue(v8::String::NewFromUtf8(isolate, kModuleSystem),
v8::External::New(isolate, this)); v8::External::New(isolate, this));
gin::ModuleRegistry::From(context->v8_context())->AddObserver(this);
} }
ModuleSystem::~ModuleSystem() { Invalidate(); } ModuleSystem::~ModuleSystem() { Invalidate(); }
...@@ -215,55 +222,7 @@ v8::Local<v8::Value> ModuleSystem::RequireForJsInner( ...@@ -215,55 +222,7 @@ v8::Local<v8::Value> ModuleSystem::RequireForJsInner(
if (!exports->IsUndefined()) if (!exports->IsUndefined())
return handle_scope.Escape(exports); return handle_scope.Escape(exports);
std::string module_name_str = *v8::String::Utf8Value(module_name); exports = LoadModule(*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());
}
}
modules->Set(module_name, exports); modules->Set(module_name, exports);
return handle_scope.Escape(exports); return handle_scope.Escape(exports);
} }
...@@ -558,12 +517,38 @@ v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString( ...@@ -558,12 +517,38 @@ v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
return i->second->NewInstance(); 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::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) {
v8::EscapableHandleScope handle_scope(GetIsolate()); v8::EscapableHandleScope handle_scope(GetIsolate());
// Keep in order with the arguments in RequireForJsInner. // Keep in order with the arguments in RequireForJsInner.
v8::Handle<v8::String> left = v8::String::NewFromUtf8( v8::Handle<v8::String> left = v8::String::NewFromUtf8(
GetIsolate(), GetIsolate(),
"(function(require, requireNative, exports, " "(function(define, require, requireNative, requireAsync, exports, "
"console, privates," "console, privates,"
"$Array, $Function, $JSON, $Object, $RegExp, $String) {" "$Array, $Function, $JSON, $Object, $RegExp, $String) {"
"'use strict';"); "'use strict';");
...@@ -586,4 +571,98 @@ void ModuleSystem::Private(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -586,4 +571,98 @@ void ModuleSystem::Private(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(privates); 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 } // namespace extensions
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "extensions/renderer/native_handler.h" #include "extensions/renderer/native_handler.h"
#include "extensions/renderer/object_backed_native_handler.h" #include "extensions/renderer/object_backed_native_handler.h"
#include "gin/modules/module_registry_observer.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
namespace extensions { namespace extensions {
...@@ -37,7 +38,8 @@ class ScriptContext; ...@@ -37,7 +38,8 @@ class ScriptContext;
// Note that a ModuleSystem must be used only in conjunction with a single // Note that a ModuleSystem must be used only in conjunction with a single
// v8::Context. // v8::Context.
// TODO(koz): Rename this to JavaScriptModuleSystem. // TODO(koz): Rename this to JavaScriptModuleSystem.
class ModuleSystem : public ObjectBackedNativeHandler { class ModuleSystem : public ObjectBackedNativeHandler,
public gin::ModuleRegistryObserver {
public: public:
class SourceMap { class SourceMap {
public: public:
...@@ -158,9 +160,6 @@ class ModuleSystem : public ObjectBackedNativeHandler { ...@@ -158,9 +160,6 @@ class ModuleSystem : public ObjectBackedNativeHandler {
// Called when an exception is thrown but not caught. // Called when an exception is thrown but not caught.
void HandleException(const v8::TryCatch& try_catch); 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); void RequireForJs(const v8::FunctionCallbackInfo<v8::Value>& args);
v8::Local<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name); v8::Local<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name);
...@@ -183,15 +182,29 @@ class ModuleSystem : public ObjectBackedNativeHandler { ...@@ -183,15 +182,29 @@ class ModuleSystem : public ObjectBackedNativeHandler {
v8::Handle<v8::Value> RequireNativeFromString(const std::string& native_name); v8::Handle<v8::Value> RequireNativeFromString(const std::string& native_name);
void RequireNative(const v8::FunctionCallbackInfo<v8::Value>& args); 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); v8::Handle<v8::String> WrapSource(v8::Handle<v8::String> source);
// NativeHandler implementation which returns the private area of an Object. // NativeHandler implementation which returns the private area of an Object.
void Private(const v8::FunctionCallbackInfo<v8::Value>& args); void Private(const v8::FunctionCallbackInfo<v8::Value>& args);
// NativeHandler implementation which returns a function wrapper for a // Loads and runs a Javascript module.
// provided function. v8::Handle<v8::Value> LoadModule(const std::string& module_name);
void CreateFunctionWrapper(const v8::FunctionCallbackInfo<v8::Value>& args);
// 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_; ScriptContext* context_;
...@@ -212,6 +225,8 @@ class ModuleSystem : public ObjectBackedNativeHandler { ...@@ -212,6 +225,8 @@ class ModuleSystem : public ObjectBackedNativeHandler {
std::set<std::string> overridden_native_handlers_; std::set<std::string> overridden_native_handlers_;
base::WeakPtrFactory<ModuleSystem> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ModuleSystem); DISALLOW_COPY_AND_ASSIGN(ModuleSystem);
}; };
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "extensions/common/extension_api.h" #include "extensions/common/extension_api.h"
#include "extensions/common/extension_urls.h" #include "extensions/common/extension_urls.h"
#include "extensions/common/features/base_feature_provider.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/WebDataSource.h"
#include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebFrame.h"
...@@ -42,6 +43,7 @@ ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context, ...@@ -42,6 +43,7 @@ ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context,
<< " extension id: " << GetExtensionID() << "\n" << " extension id: " << GetExtensionID() << "\n"
<< " frame: " << web_frame_ << "\n" << " frame: " << web_frame_ << "\n"
<< " context type: " << GetContextTypeDescription(); << " context type: " << GetContextTypeDescription();
gin::PerContextData::From(v8_context)->set_runner(this);
} }
ScriptContext::~ScriptContext() { ScriptContext::~ScriptContext() {
...@@ -221,4 +223,21 @@ void ScriptContext::OnResponseReceived(const std::string& name, ...@@ -221,4 +223,21 @@ void ScriptContext::OnResponseReceived(const std::string& name,
<< *v8::String::Utf8Value(retval); << *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 } // namespace extensions
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "extensions/renderer/request_sender.h" #include "extensions/renderer/request_sender.h"
#include "extensions/renderer/safe_builtins.h" #include "extensions/renderer/safe_builtins.h"
#include "extensions/renderer/scoped_persistent.h" #include "extensions/renderer/scoped_persistent.h"
#include "gin/runner.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
namespace blink { namespace blink {
...@@ -28,7 +29,7 @@ namespace extensions { ...@@ -28,7 +29,7 @@ namespace extensions {
class Extension; class Extension;
// Extensions wrapper for a v8 context. // Extensions wrapper for a v8 context.
class ScriptContext : public RequestSender::Source { class ScriptContext : public RequestSender::Source, public gin::Runner {
public: public:
ScriptContext(const v8::Handle<v8::Context>& context, ScriptContext(const v8::Handle<v8::Context>& context,
blink::WebFrame* frame, blink::WebFrame* frame,
...@@ -120,6 +121,15 @@ class ScriptContext : public RequestSender::Source { ...@@ -120,6 +121,15 @@ class ScriptContext : public RequestSender::Source {
const base::ListValue& response, const base::ListValue& response,
const std::string& error) OVERRIDE; 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: protected:
// The v8 context the bindings are accessible to. // The v8 context the bindings are accessible to.
ScopedPersistent<v8::Context> v8_context_; ScopedPersistent<v8::Context> v8_context_;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include "extensions/common/features/feature.h" #include "extensions/common/features/feature.h"
#include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context.h"
#include "extensions/renderer/script_context_set.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 "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebFrame.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
...@@ -19,15 +21,20 @@ TEST(ScriptContextSet, Lifecycle) { ...@@ -19,15 +21,20 @@ TEST(ScriptContextSet, Lifecycle) {
ScriptContextSet context_set; ScriptContextSet context_set;
v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Isolate* isolate = v8::Isolate::GetCurrent();
gin::IsolateHolder isolate_holder(isolate, NULL);
v8::HandleScope handle_scope(isolate); 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 // Dirty hack, but we don't actually need the frame, and this is easier than
// creating a whole webview. // creating a whole webview.
blink::WebFrame* frame = reinterpret_cast<blink::WebFrame*>(1); blink::WebFrame* frame = reinterpret_cast<blink::WebFrame*>(1);
const Extension* extension = NULL; const Extension* extension = NULL;
ScriptContext* context = new ScriptContext( ScriptContext* context =
v8_context, frame, extension, Feature::BLESSED_EXTENSION_CONTEXT); new ScriptContext(context_holder.context(),
frame,
extension,
Feature::BLESSED_EXTENSION_CONTEXT);
context_set.Add(context); context_set.Add(context);
EXPECT_EQ(1u, context_set.GetAll().count(context)); EXPECT_EQ(1u, context_set.GetAll().count(context));
......
...@@ -105,13 +105,13 @@ v8::Handle<v8::Context> V8SchemaRegistry::GetOrCreateContext( ...@@ -105,13 +105,13 @@ v8::Handle<v8::Context> V8SchemaRegistry::GetOrCreateContext(
v8::Isolate* isolate) { v8::Isolate* isolate) {
// It's ok to create local handles in this function, since this is only called // It's ok to create local handles in this function, since this is only called
// when we have a HandleScope. // when we have a HandleScope.
if (context_.IsEmpty()) { if (!context_holder_) {
v8::Handle<v8::Context> context = v8::Context::New(isolate); context_holder_.reset(new gin::ContextHolder(isolate));
context_.reset(context); context_holder_->SetContext(v8::Context::New(isolate));
schema_cache_.reset(new SchemaCache(isolate)); schema_cache_.reset(new SchemaCache(isolate));
return context; return context_holder_->context();
} }
return context_.NewHandle(isolate); return context_holder_->context();
} }
} // namespace extensions } // namespace extensions
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.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-util.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
...@@ -43,9 +43,9 @@ class V8SchemaRegistry { ...@@ -43,9 +43,9 @@ class V8SchemaRegistry {
typedef v8::StdPersistentValueMap<std::string, v8::Object> SchemaCache; typedef v8::StdPersistentValueMap<std::string, v8::Object> SchemaCache;
scoped_ptr<SchemaCache> schema_cache_; 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. // Created lazily via GetOrCreateContext.
ScopedPersistent<v8::Context> context_; scoped_ptr<gin::ContextHolder> context_holder_;
DISALLOW_COPY_AND_ASSIGN(V8SchemaRegistry); DISALLOW_COPY_AND_ASSIGN(V8SchemaRegistry);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment