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",
......
...@@ -14,38 +14,35 @@ class EventUnittest : public ModuleSystemTest { ...@@ -14,38 +14,35 @@ class EventUnittest : public ModuleSystemTest {
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
ModuleSystemTest::SetUp(); ModuleSystemTest::SetUp();
RegisterModule(kEventBindings, IDR_EVENT_BINDINGS_JS); env()->RegisterModule(kEventBindings, IDR_EVENT_BINDINGS_JS);
RegisterModule("json_schema", IDR_JSON_SCHEMA_JS); env()->RegisterModule("json_schema", IDR_JSON_SCHEMA_JS);
RegisterModule(kSchemaUtils, IDR_SCHEMA_UTILS_JS); env()->RegisterModule(kSchemaUtils, IDR_SCHEMA_UTILS_JS);
RegisterModule("uncaught_exception_handler", env()->RegisterModule("uncaught_exception_handler",
IDR_UNCAUGHT_EXCEPTION_HANDLER_JS); IDR_UNCAUGHT_EXCEPTION_HANDLER_JS);
RegisterModule("unload_event", IDR_UNLOAD_EVENT_JS); env()->RegisterModule("unload_event", IDR_UNLOAD_EVENT_JS);
RegisterModule("utils", IDR_UTILS_JS); env()->RegisterModule("utils", IDR_UTILS_JS);
// Mock out the native handler for event_bindings. These mocks will fail if // Mock out the native handler for event_bindings. These mocks will fail if
// any invariants maintained by the real event_bindings are broken. // any invariants maintained by the real event_bindings are broken.
OverrideNativeHandler("event_natives", env()->OverrideNativeHandler(
"event_natives",
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var attachedListeners = exports.attachedListeners = {};" "var attachedListeners = exports.attachedListeners = {};"
"var attachedFilteredListeners = " "var attachedFilteredListeners = "
" exports.attachedFilteredListeners = {};" " exports.attachedFilteredListeners = {};"
"var nextId = 0;" "var nextId = 0;"
"var idToName = {};" "var idToName = {};"
"exports.AttachEvent = function(eventName) {" "exports.AttachEvent = function(eventName) {"
" assert.AssertFalse(!!attachedListeners[eventName]);" " assert.AssertFalse(!!attachedListeners[eventName]);"
" attachedListeners[eventName] = 1;" " attachedListeners[eventName] = 1;"
"};" "};"
"exports.DetachEvent = function(eventName) {" "exports.DetachEvent = function(eventName) {"
" assert.AssertTrue(!!attachedListeners[eventName]);" " assert.AssertTrue(!!attachedListeners[eventName]);"
" delete attachedListeners[eventName];" " delete attachedListeners[eventName];"
"};" "};"
"exports.IsEventAttached = function(eventName) {" "exports.IsEventAttached = function(eventName) {"
" return !!attachedListeners[eventName];" " return !!attachedListeners[eventName];"
"};" "};"
"exports.AttachFilteredEvent = function(name, filters) {" "exports.AttachFilteredEvent = function(name, filters) {"
" var id = nextId++;" " var id = nextId++;"
" idToName[id] = name;" " idToName[id] = name;"
...@@ -54,23 +51,21 @@ class EventUnittest : public ModuleSystemTest { ...@@ -54,23 +51,21 @@ class EventUnittest : public ModuleSystemTest {
" attachedFilteredListeners[name][id] = filters;" " attachedFilteredListeners[name][id] = filters;"
" return id;" " return id;"
"};" "};"
"exports.DetachFilteredEvent = function(id, manual) {" "exports.DetachFilteredEvent = function(id, manual) {"
" var i = attachedFilteredListeners[idToName[id]].indexOf(id);" " var i = attachedFilteredListeners[idToName[id]].indexOf(id);"
" attachedFilteredListeners[idToName[id]].splice(i, 1);" " attachedFilteredListeners[idToName[id]].splice(i, 1);"
"};" "};"
"exports.HasFilteredListener = function(name) {" "exports.HasFilteredListener = function(name) {"
" return attachedFilteredListeners[name].length;" " return attachedFilteredListeners[name].length;"
"};"); "};");
OverrideNativeHandler("sendRequest", env()->OverrideNativeHandler("sendRequest",
"exports.sendRequest = function() {};"); "exports.sendRequest = function() {};");
OverrideNativeHandler("apiDefinitions", env()->OverrideNativeHandler(
"apiDefinitions",
"exports.GetExtensionAPIDefinitionsForTest = function() {};"); "exports.GetExtensionAPIDefinitionsForTest = function() {};");
OverrideNativeHandler("logging", env()->OverrideNativeHandler("logging", "exports.DCHECK = function() {};");
"exports.DCHECK = function() {};"); env()->OverrideNativeHandler("schema_registry",
OverrideNativeHandler("schema_registry", "exports.GetSchema = function() {};");
"exports.GetSchema = function() {};");
} }
}; };
...@@ -80,8 +75,9 @@ TEST_F(EventUnittest, TestNothing) { ...@@ -80,8 +75,9 @@ TEST_F(EventUnittest, TestNothing) {
TEST_F(EventUnittest, AddRemoveTwoListeners) { TEST_F(EventUnittest, AddRemoveTwoListeners) {
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');" "var assert = requireNative('assert');"
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var eventNatives = requireNative('event_natives');" "var eventNatives = requireNative('event_natives');"
...@@ -94,13 +90,14 @@ TEST_F(EventUnittest, AddRemoveTwoListeners) { ...@@ -94,13 +90,14 @@ TEST_F(EventUnittest, AddRemoveTwoListeners) {
"assert.AssertTrue(!!eventNatives.attachedListeners['named-event']);" "assert.AssertTrue(!!eventNatives.attachedListeners['named-event']);"
"myEvent.removeListener(cb2);" "myEvent.removeListener(cb2);"
"assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);"); "assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, OnUnloadDetachesAllListeners) { TEST_F(EventUnittest, OnUnloadDetachesAllListeners) {
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');" "var assert = requireNative('assert');"
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var eventNatives = requireNative('event_natives');" "var eventNatives = requireNative('event_natives');"
...@@ -111,13 +108,14 @@ TEST_F(EventUnittest, OnUnloadDetachesAllListeners) { ...@@ -111,13 +108,14 @@ TEST_F(EventUnittest, OnUnloadDetachesAllListeners) {
"myEvent.addListener(cb2);" "myEvent.addListener(cb2);"
"require('unload_event').dispatch();" "require('unload_event').dispatch();"
"assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);"); "assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, OnUnloadDetachesAllListenersEvenDupes) { TEST_F(EventUnittest, OnUnloadDetachesAllListenersEvenDupes) {
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');" "var assert = requireNative('assert');"
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var eventNatives = requireNative('event_natives');" "var eventNatives = requireNative('event_natives');"
...@@ -127,13 +125,14 @@ TEST_F(EventUnittest, OnUnloadDetachesAllListenersEvenDupes) { ...@@ -127,13 +125,14 @@ TEST_F(EventUnittest, OnUnloadDetachesAllListenersEvenDupes) {
"myEvent.addListener(cb1);" "myEvent.addListener(cb1);"
"require('unload_event').dispatch();" "require('unload_event').dispatch();"
"assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);"); "assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) { TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule(
"test",
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var eventOpts = {supportsRules: true};" "var eventOpts = {supportsRules: true};"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
...@@ -144,13 +143,14 @@ TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) { ...@@ -144,13 +143,14 @@ TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) {
" caught = true;" " caught = true;"
"}" "}"
"assert.AssertTrue(caught);"); "assert.AssertTrue(caught);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, NamedEventDispatch) { TEST_F(EventUnittest, NamedEventDispatch) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule(
"test",
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var dispatchEvent = require('event_bindings').dispatchEvent;" "var dispatchEvent = require('event_bindings').dispatchEvent;"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
...@@ -159,33 +159,34 @@ TEST_F(EventUnittest, NamedEventDispatch) { ...@@ -159,33 +159,34 @@ TEST_F(EventUnittest, NamedEventDispatch) {
"e.addListener(function() { called = true; });" "e.addListener(function() { called = true; });"
"dispatchEvent('myevent', []);" "dispatchEvent('myevent', []);"
"assert.AssertTrue(called);"); "assert.AssertTrue(called);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, AddListenerWithFiltersThrowsErrorByDefault) { TEST_F(EventUnittest, AddListenerWithFiltersThrowsErrorByDefault) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule("test",
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var e = new Event('myevent');" "var e = new Event('myevent');"
"var filter = [{" "var filter = [{"
" url: {hostSuffix: 'google.com'}," " url: {hostSuffix: 'google.com'},"
"}];" "}];"
"var caught = false;" "var caught = false;"
"try {" "try {"
" e.addListener(function() {}, filter);" " e.addListener(function() {}, filter);"
"} catch (e) {" "} catch (e) {"
" caught = true;" " caught = true;"
"}" "}"
"assert.AssertTrue(caught);"); "assert.AssertTrue(caught);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, FilteredEventsAttachment) { TEST_F(EventUnittest, FilteredEventsAttachment) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule(
"test",
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var bindings = requireNative('event_natives');" "var bindings = requireNative('event_natives');"
...@@ -197,13 +198,14 @@ TEST_F(EventUnittest, FilteredEventsAttachment) { ...@@ -197,13 +198,14 @@ TEST_F(EventUnittest, FilteredEventsAttachment) {
"assert.AssertTrue(bindings.HasFilteredListener('myevent'));" "assert.AssertTrue(bindings.HasFilteredListener('myevent'));"
"e.removeListener(cb);" "e.removeListener(cb);"
"assert.AssertFalse(bindings.HasFilteredListener('myevent'));"); "assert.AssertFalse(bindings.HasFilteredListener('myevent'));");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, DetachFilteredEvent) { TEST_F(EventUnittest, DetachFilteredEvent) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule(
"test",
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var bindings = requireNative('event_natives');" "var bindings = requireNative('event_natives');"
...@@ -216,13 +218,14 @@ TEST_F(EventUnittest, DetachFilteredEvent) { ...@@ -216,13 +218,14 @@ TEST_F(EventUnittest, DetachFilteredEvent) {
"e.addListener(cb2, filters);" "e.addListener(cb2, filters);"
"privates(e).impl.detach_();" "privates(e).impl.detach_();"
"assert.AssertFalse(bindings.HasFilteredListener('myevent'));"); "assert.AssertFalse(bindings.HasFilteredListener('myevent'));");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, AttachAndRemoveSameFilteredEventListener) { TEST_F(EventUnittest, AttachAndRemoveSameFilteredEventListener) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule(
"test",
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var bindings = requireNative('event_natives');" "var bindings = requireNative('event_natives');"
...@@ -237,13 +240,14 @@ TEST_F(EventUnittest, AttachAndRemoveSameFilteredEventListener) { ...@@ -237,13 +240,14 @@ TEST_F(EventUnittest, AttachAndRemoveSameFilteredEventListener) {
"assert.AssertTrue(bindings.HasFilteredListener('myevent'));" "assert.AssertTrue(bindings.HasFilteredListener('myevent'));"
"e.removeListener(cb);" "e.removeListener(cb);"
"assert.AssertFalse(bindings.HasFilteredListener('myevent'));"); "assert.AssertFalse(bindings.HasFilteredListener('myevent'));");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, AddingFilterWithUrlFieldNotAListThrowsException) { TEST_F(EventUnittest, AddingFilterWithUrlFieldNotAListThrowsException) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule(
"test",
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var eventOpts = {supportsListeners: true, supportsFilters: true};" "var eventOpts = {supportsListeners: true, supportsFilters: true};"
...@@ -257,13 +261,14 @@ TEST_F(EventUnittest, AddingFilterWithUrlFieldNotAListThrowsException) { ...@@ -257,13 +261,14 @@ TEST_F(EventUnittest, AddingFilterWithUrlFieldNotAListThrowsException) {
" caught = true;" " caught = true;"
"}" "}"
"assert.AssertTrue(caught);"); "assert.AssertTrue(caught);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(EventUnittest, MaxListeners) { TEST_F(EventUnittest, MaxListeners) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("test", env()->RegisterModule(
"test",
"var Event = require('event_bindings').Event;" "var Event = require('event_bindings').Event;"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var eventOpts = {supportsListeners: true, maxListeners: 1};" "var eventOpts = {supportsListeners: true, maxListeners: 1};"
...@@ -282,7 +287,7 @@ TEST_F(EventUnittest, MaxListeners) { ...@@ -282,7 +287,7 @@ TEST_F(EventUnittest, MaxListeners) {
" caught = true;" " caught = true;"
"}" "}"
"assert.AssertTrue(caught);"); "assert.AssertTrue(caught);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
} // namespace } // namespace
......
...@@ -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
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "chrome/test/base/module_system_test.h" #include "chrome/test/base/module_system_test.h"
#include "extensions/renderer/module_system.h" #include "extensions/renderer/module_system.h"
#include "gin/modules/module_registry.h"
// TODO(cduvall/kalman): Put this file in extensions namespace. // TODO(cduvall/kalman): Put this file in extensions namespace.
using extensions::ModuleSystem; using extensions::ModuleSystem;
...@@ -51,14 +52,14 @@ class TestExceptionHandler : public ModuleSystem::ExceptionHandler { ...@@ -51,14 +52,14 @@ class TestExceptionHandler : public ModuleSystem::ExceptionHandler {
TEST_F(ModuleSystemTest, TestExceptionHandling) { TEST_F(ModuleSystemTest, TestExceptionHandling) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
TestExceptionHandler* handler = new TestExceptionHandler; TestExceptionHandler* handler = new TestExceptionHandler;
scoped_ptr<ModuleSystem::ExceptionHandler> scoped_handler(handler); scoped_ptr<ModuleSystem::ExceptionHandler> scoped_handler(handler);
ASSERT_FALSE(handler->handled_exception()); ASSERT_FALSE(handler->handled_exception());
context_->module_system()->SetExceptionHandlerForTest(scoped_handler.Pass()); env()->module_system()->SetExceptionHandlerForTest(scoped_handler.Pass());
RegisterModule("test", "throw 'hi';"); env()->RegisterModule("test", "throw 'hi';");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
ASSERT_TRUE(handler->handled_exception()); ASSERT_TRUE(handler->handled_exception());
ExpectNoAssertionsMade(); ExpectNoAssertionsMade();
...@@ -66,210 +67,423 @@ TEST_F(ModuleSystemTest, TestExceptionHandling) { ...@@ -66,210 +67,423 @@ TEST_F(ModuleSystemTest, TestExceptionHandling) {
TEST_F(ModuleSystemTest, TestRequire) { TEST_F(ModuleSystemTest, TestRequire) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("add", "exports.Add = function(x, y) { return x + y; };"); env()->RegisterModule("add",
RegisterModule("test", "exports.Add = function(x, y) { return x + y; };");
"var Add = require('add').Add;" env()->RegisterModule("test",
"requireNative('assert').AssertTrue(Add(3, 5) == 8);"); "var Add = require('add').Add;"
context_->module_system()->Require("test"); "requireNative('assert').AssertTrue(Add(3, 5) == 8);");
env()->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestNestedRequire) { TEST_F(ModuleSystemTest, TestNestedRequire) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("add", "exports.Add = function(x, y) { return x + y; };"); env()->RegisterModule("add",
RegisterModule("double", "exports.Add = function(x, y) { return x + y; };");
"var Add = require('add').Add;" env()->RegisterModule("double",
"exports.Double = function(x) { return Add(x, x); };"); "var Add = require('add').Add;"
RegisterModule("test", "exports.Double = function(x) { return Add(x, x); };");
"var Double = require('double').Double;" env()->RegisterModule("test",
"requireNative('assert').AssertTrue(Double(3) == 6);"); "var Double = require('double').Double;"
context_->module_system()->Require("test"); "requireNative('assert').AssertTrue(Double(3) == 6);");
env()->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestModuleInsulation) { TEST_F(ModuleSystemTest, TestModuleInsulation) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("x", env()->RegisterModule("x",
"var x = 10;" "var x = 10;"
"exports.X = function() { return x; };"); "exports.X = function() { return x; };");
RegisterModule("y", env()->RegisterModule("y",
"var x = 15;" "var x = 15;"
"require('x');" "require('x');"
"exports.Y = function() { return x; };"); "exports.Y = function() { return x; };");
RegisterModule("test", env()->RegisterModule("test",
"var Y = require('y').Y;" "var Y = require('y').Y;"
"var X = require('x').X;" "var X = require('x').X;"
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"assert.AssertTrue(!this.hasOwnProperty('x'));" "assert.AssertTrue(!this.hasOwnProperty('x'));"
"assert.AssertTrue(Y() == 15);" "assert.AssertTrue(Y() == 15);"
"assert.AssertTrue(X() == 10);"); "assert.AssertTrue(X() == 10);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestNativesAreDisabledOutsideANativesEnabledScope) { TEST_F(ModuleSystemTest, TestNativesAreDisabledOutsideANativesEnabledScope) {
RegisterModule("test", env()->RegisterModule("test",
"var assert;" "var assert;"
"try {" "try {"
" assert = requireNative('assert');" " assert = requireNative('assert');"
"} catch (e) {" "} catch (e) {"
" var caught = true;" " var caught = true;"
"}" "}"
"if (assert) {" "if (assert) {"
" assert.AssertTrue(true);" " assert.AssertTrue(true);"
"}"); "}");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
ExpectNoAssertionsMade(); ExpectNoAssertionsMade();
} }
TEST_F(ModuleSystemTest, TestNativesAreEnabledWithinANativesEnabledScope) { TEST_F(ModuleSystemTest, TestNativesAreEnabledWithinANativesEnabledScope) {
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"assert.AssertTrue(true);"); "assert.AssertTrue(true);");
{ {
ModuleSystem::NativesEnabledScope natives_enabled( ModuleSystem::NativesEnabledScope natives_enabled(env()->module_system());
context_->module_system());
{ {
ModuleSystem::NativesEnabledScope natives_enabled_inner( ModuleSystem::NativesEnabledScope natives_enabled_inner(
context_->module_system()); env()->module_system());
} }
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
} }
TEST_F(ModuleSystemTest, TestLazyField) { TEST_F(ModuleSystemTest, TestLazyField) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("lazy", env()->RegisterModule("lazy", "exports.x = 5;");
"exports.x = 5;");
v8::Handle<v8::Object> object = CreateGlobal("object"); v8::Handle<v8::Object> object = env()->CreateGlobal("object");
context_->module_system()->SetLazyField(object, "blah", "lazy", "x"); env()->module_system()->SetLazyField(object, "blah", "lazy", "x");
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"assert.AssertTrue(object.blah == 5);"); "assert.AssertTrue(object.blah == 5);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestLazyFieldYieldingObject) { TEST_F(ModuleSystemTest, TestLazyFieldYieldingObject) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("lazy", env()->RegisterModule(
"lazy",
"var object = {};" "var object = {};"
"object.__defineGetter__('z', function() { return 1; });" "object.__defineGetter__('z', function() { return 1; });"
"object.x = 5;" "object.x = 5;"
"object.y = function() { return 10; };" "object.y = function() { return 10; };"
"exports.object = object;"); "exports.object = object;");
v8::Handle<v8::Object> object = CreateGlobal("object"); v8::Handle<v8::Object> object = env()->CreateGlobal("object");
context_->module_system()->SetLazyField(object, "thing", "lazy", "object"); env()->module_system()->SetLazyField(object, "thing", "lazy", "object");
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"assert.AssertTrue(object.thing.x == 5);" "assert.AssertTrue(object.thing.x == 5);"
"assert.AssertTrue(object.thing.y() == 10);" "assert.AssertTrue(object.thing.y() == 10);"
"assert.AssertTrue(object.thing.z == 1);" "assert.AssertTrue(object.thing.z == 1);");
); env()->module_system()->Require("test");
context_->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestLazyFieldIsOnlyEvaledOnce) { TEST_F(ModuleSystemTest, TestLazyFieldIsOnlyEvaledOnce) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
context_->module_system()->RegisterNativeHandler( env()->module_system()->RegisterNativeHandler(
"counter", "counter",
scoped_ptr<NativeHandler>(new CounterNatives(context_.get()))); scoped_ptr<NativeHandler>(new CounterNatives(env()->context())));
RegisterModule("lazy", env()->RegisterModule("lazy",
"requireNative('counter').Increment();" "requireNative('counter').Increment();"
"exports.x = 5;"); "exports.x = 5;");
v8::Handle<v8::Object> object = CreateGlobal("object"); v8::Handle<v8::Object> object = env()->CreateGlobal("object");
context_->module_system()->SetLazyField(object, "x", "lazy", "x"); env()->module_system()->SetLazyField(object, "x", "lazy", "x");
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var counter = requireNative('counter');" "var counter = requireNative('counter');"
"assert.AssertTrue(counter.Get() == 0);" "assert.AssertTrue(counter.Get() == 0);"
"object.x;" "object.x;"
"assert.AssertTrue(counter.Get() == 1);" "assert.AssertTrue(counter.Get() == 1);"
"object.x;" "object.x;"
"assert.AssertTrue(counter.Get() == 1);"); "assert.AssertTrue(counter.Get() == 1);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestRequireNativesAfterLazyEvaluation) { TEST_F(ModuleSystemTest, TestRequireNativesAfterLazyEvaluation) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("lazy", env()->RegisterModule("lazy", "exports.x = 5;");
"exports.x = 5;"); v8::Handle<v8::Object> object = env()->CreateGlobal("object");
v8::Handle<v8::Object> object = CreateGlobal("object");
env()->module_system()->SetLazyField(object, "x", "lazy", "x");
context_->module_system()->SetLazyField(object, "x", "lazy", "x"); env()->RegisterModule("test",
RegisterModule("test", "object.x;"
"object.x;" "requireNative('assert').AssertTrue(true);");
"requireNative('assert').AssertTrue(true);"); env()->module_system()->Require("test");
context_->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestTransitiveRequire) { TEST_F(ModuleSystemTest, TestTransitiveRequire) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
RegisterModule("dependency", env()->RegisterModule("dependency", "exports.x = 5;");
"exports.x = 5;"); env()->RegisterModule("lazy", "exports.output = require('dependency');");
RegisterModule("lazy",
"exports.output = require('dependency');");
v8::Handle<v8::Object> object = CreateGlobal("object"); v8::Handle<v8::Object> object = env()->CreateGlobal("object");
context_->module_system()->SetLazyField(object, "thing", "lazy", "output"); env()->module_system()->SetLazyField(object, "thing", "lazy", "output");
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"assert.AssertTrue(object.thing.x == 5);"); "assert.AssertTrue(object.thing.x == 5);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestModulesOnlyGetEvaledOnce) { TEST_F(ModuleSystemTest, TestModulesOnlyGetEvaledOnce) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
context_->module_system()->RegisterNativeHandler( env()->module_system()->RegisterNativeHandler(
"counter", "counter",
scoped_ptr<NativeHandler>(new CounterNatives(context_.get()))); scoped_ptr<NativeHandler>(new CounterNatives(env()->context())));
RegisterModule("incrementsWhenEvaled", env()->RegisterModule("incrementsWhenEvaled",
"requireNative('counter').Increment();"); "requireNative('counter').Increment();");
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"var counter = requireNative('counter');" "var counter = requireNative('counter');"
"assert.AssertTrue(counter.Get() == 0);" "assert.AssertTrue(counter.Get() == 0);"
"require('incrementsWhenEvaled');" "require('incrementsWhenEvaled');"
"assert.AssertTrue(counter.Get() == 1);" "assert.AssertTrue(counter.Get() == 1);"
"require('incrementsWhenEvaled');" "require('incrementsWhenEvaled');"
"assert.AssertTrue(counter.Get() == 1);"); "assert.AssertTrue(counter.Get() == 1);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestOverrideNativeHandler) { TEST_F(ModuleSystemTest, TestOverrideNativeHandler) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
OverrideNativeHandler("assert", "exports.AssertTrue = function() {};"); env()->OverrideNativeHandler("assert", "exports.AssertTrue = function() {};");
RegisterModule("test", "requireNative('assert').AssertTrue(true);"); env()->RegisterModule("test", "requireNative('assert').AssertTrue(true);");
ExpectNoAssertionsMade(); ExpectNoAssertionsMade();
context_->module_system()->Require("test"); env()->module_system()->Require("test");
} }
TEST_F(ModuleSystemTest, TestOverrideNonExistentNativeHandler) { TEST_F(ModuleSystemTest, TestOverrideNonExistentNativeHandler) {
ModuleSystem::NativesEnabledScope natives_enabled_scope( ModuleSystem::NativesEnabledScope natives_enabled_scope(
context_->module_system()); env()->module_system());
OverrideNativeHandler("thing", "exports.x = 5;"); env()->OverrideNativeHandler("thing", "exports.x = 5;");
RegisterModule("test", env()->RegisterModule("test",
"var assert = requireNative('assert');" "var assert = requireNative('assert');"
"assert.AssertTrue(requireNative('thing').x == 5);"); "assert.AssertTrue(requireNative('thing').x == 5);");
context_->module_system()->Require("test"); env()->module_system()->Require("test");
}
TEST_F(ModuleSystemTest, TestRequireAsync) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule("add",
"define('add', [], function() {"
" return { Add: function(x, y) { return x + y; } };"
"});");
env()->RegisterModule("math",
"define('math', ['add'], function(add) {"
" return { Add: add.Add };"
"});");
env()->RegisterModule(
"test",
"requireAsync('math').then(function(math) {"
" requireNative('assert').AssertTrue(math.Add(3, 5) == 8);"
"});");
env()->module_system()->Require("test");
RunResolvedPromises();
}
TEST_F(ModuleSystemTest, TestRequireAsyncInParallel) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule("add",
"define('add', [], function() {"
" return { Add: function(x, y) { return x + y; } };"
"});");
env()->RegisterModule(
"subtract",
"define('subtract', [], function() {"
" return { Subtract: function(x, y) { return x - y; } };"
"});");
env()->RegisterModule(
"math",
"exports.AddAndSubtract = function(x, y, z) {"
" return Promise.all([requireAsync('add'),"
" requireAsync('subtract')"
" ]).then(function(modules) {"
" return modules[1].Subtract(modules[0].Add(x, y), z);"
" });"
"};");
env()->RegisterModule("test",
"var AddAndSubtract = require('math').AddAndSubtract;"
"AddAndSubtract(3, 5, 2).then(function(result) {"
" requireNative('assert').AssertTrue(result == 6);"
"});");
env()->module_system()->Require("test");
RunResolvedPromises();
}
TEST_F(ModuleSystemTest, TestNestedRequireAsyncs) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule("first",
"define('first', [], function() {"
" return { next: 'second' };"
"});");
env()->RegisterModule("second",
"define('second', [], function() {"
" return { next: '' };"
"});");
env()->RegisterModule(
"test",
"requireAsync('first').then(function(module) {"
" return requireAsync(module.next)"
"}).then(function(module) {"
" requireNative('assert').AssertTrue(module.next === '');"
"});");
env()->module_system()->Require("test");
RunResolvedPromises();
}
TEST_F(ModuleSystemTest, TestRequireFromAMDModule) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule("add",
"exports.Add = function(x, y) { return x + y; };");
env()->RegisterModule("math",
"define('math', [], function() {"
" var add = require('add');"
" return { Add: add.Add };"
"});");
env()->RegisterModule(
"test",
"requireAsync('math').then(function(math) {"
" requireNative('assert').AssertTrue(math.Add(3, 5) == 8);"
"});");
env()->module_system()->Require("test");
RunResolvedPromises();
}
TEST_F(ModuleSystemTest, TestRequireAsyncFromAMDModule) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule("add",
"define('add', [], function() {"
" return { Add: function(x, y) { return x + y; } };"
"});");
env()->RegisterModule("math",
"define('math', [], function() {"
" function Add(x, y) {"
" return requireAsync('add').then(function(add) {"
" return add.Add(x, y);"
" });"
" }"
" return { Add: Add };"
"});");
env()->RegisterModule("test",
"requireAsync('math').then(function(math) {"
" return math.Add(3, 6);"
"}).then(function(result) {"
" requireNative('assert').AssertTrue(result == 9);"
"});");
env()->module_system()->Require("test");
RunResolvedPromises();
}
TEST_F(ModuleSystemTest, TestRequireAsyncFromAnotherContext) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule(
"test",
"requireAsync('natives').then(function(natives) {"
" natives.requireAsync('ping').then(function(ping) {"
" return ping();"
" }).then(function(result) {"
" requireNative('assert').AssertTrue(result == 'pong');"
" });"
"});");
scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment();
other_env->RegisterModule("ping",
"define('ping', ['natives'], function(natives) {"
" return function() {"
" return 'pong';"
" }"
"});");
gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
env()->isolate(), "natives", other_env->module_system()->NewInstance());
gin::ModuleRegistry::From(other_env->context()->v8_context())
->AddBuiltinModule(
env()->isolate(), "natives", env()->module_system()->NewInstance());
env()->module_system()->Require("test");
RunResolvedPromises();
}
TEST_F(ModuleSystemTest, TestRequireAsyncBetweenContexts) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule("pong",
"define('pong', [], function() {"
" return function() { return 'done'; };"
"});");
env()->RegisterModule(
"test",
"requireAsync('natives').then(function(natives) {"
" natives.requireAsync('ping').then(function(ping) {"
" return ping();"
" }).then(function(pong) {"
" return pong();"
" }).then(function(result) {"
" requireNative('assert').AssertTrue(result == 'done');"
" });"
"});");
scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment();
other_env->RegisterModule("ping",
"define('ping', ['natives'], function(natives) {"
" return function() {"
" return natives.requireAsync('pong');"
" }"
"});");
gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
env()->isolate(), "natives", other_env->module_system()->NewInstance());
gin::ModuleRegistry::From(other_env->context()->v8_context())
->AddBuiltinModule(
env()->isolate(), "natives", env()->module_system()->NewInstance());
env()->module_system()->Require("test");
RunResolvedPromises();
}
TEST_F(ModuleSystemTest, TestRequireAsyncFromContextWithNoModuleRegistry) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule("test",
"requireAsync('natives').then(function(natives) {"
" var AssertTrue = requireNative('assert').AssertTrue;"
" natives.requireAsync('foo').then(function() {"
" AssertTrue(false);"
" }).catch(function(error) {"
" AssertTrue(error.message == "
" 'Extension view no longer exists');"
" });"
"});");
scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment();
gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
env()->isolate(), "natives", other_env->module_system()->NewInstance());
other_env->ShutdownGin();
env()->module_system()->Require("test");
RunResolvedPromises();
}
TEST_F(ModuleSystemTest, TestRequireAsyncFromContextWithNoModuleSystem) {
ModuleSystem::NativesEnabledScope natives_enabled_scope(
env()->module_system());
env()->RegisterModule("test",
"requireAsync('natives').then(function(natives) {"
" requireNative('assert').AssertTrue("
" natives.requireAsync('foo') === undefined);"
"});");
scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment();
gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
env()->isolate(), "natives", other_env->module_system()->NewInstance());
other_env->ShutdownModuleSystem();
env()->module_system()->Require("test");
RunResolvedPromises();
} }
...@@ -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