diff options
Diffstat (limited to 'js/xpconnect/tests/unit/test_import_global_current.js')
-rw-r--r-- | js/xpconnect/tests/unit/test_import_global_current.js | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/js/xpconnect/tests/unit/test_import_global_current.js b/js/xpconnect/tests/unit/test_import_global_current.js new file mode 100644 index 0000000000..cf466a7391 --- /dev/null +++ b/js/xpconnect/tests/unit/test_import_global_current.js @@ -0,0 +1,796 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +add_task(async function testSandbox() { + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + const sb = new Cu.Sandbox(uri, sandboxOpts); + + Cu.evalInSandbox(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`, sb); + + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 0); + Cu.evalInSandbox(`ns.incCounter();`, sb); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 1); + + Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1"); +}); + +add_task(async function testNoWindowSandbox() { + // Sandbox without window doesn't have ScriptLoader, and Sandbox's + // ModuleLoader cannot be created. + const systemPrincipal = Components.Constructor( + "@mozilla.org/systemprincipal;1", + "nsIPrincipal" + )(); + const sandboxOpts = { + wantGlobalProperties: ["ChromeUtils"], + }; + + const sb = new Cu.Sandbox(systemPrincipal, sandboxOpts); + + let caught = false; + try { + Cu.evalInSandbox(` +ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`, sb); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /No ModuleLoader found/); + } + Assert.ok(caught); +}); + +add_task(async function testWindow() { + const win1 = createChromeWindow(); + + win1.eval(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(win1.eval(`ns.getCounter();`), 0); + win1.eval(`ns.incCounter();`); + Assert.equal(win1.eval(`ns.getCounter();`), 1); + + Assert.equal(win1.eval(`globalThis["loaded"].join(",")`), "2,1"); +}); + +add_task(async function testReImport() { + // Re-importing the same module should return the same thing. + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + const sb = new Cu.Sandbox(uri, sandboxOpts); + + Cu.evalInSandbox(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`, sb); + + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 0); + Cu.evalInSandbox(`ns.incCounter();`, sb); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 1); + + Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1"); + + Cu.evalInSandbox(` +var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`, sb); + + // The counter should be shared, and also not reset. + Assert.equal(Cu.evalInSandbox(`ns2.getCounter();`, sb), 1); + Cu.evalInSandbox(`ns2.incCounter();`, sb); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 2); + Assert.equal(Cu.evalInSandbox(`ns2.getCounter();`, sb), 2); + + // The top-level script shouldn't be executed twice. + Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1"); +}); + +add_task(async function testNotFound() { + // Importing non-existent file should throw error. + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + const sb = new Cu.Sandbox(uri, sandboxOpts); + + let caught = false; + try { + Cu.evalInSandbox(` +ChromeUtils.importESModule("resource://test/not_found.mjs", { + global: "current", +}); +`, sb); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /Failed to load/); + } + Assert.ok(caught); +}); + +add_task(async function testParseError() { + // Parse error should be thrown. + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + const sb = new Cu.Sandbox(uri, sandboxOpts); + + let caught = false; + try { + Cu.evalInSandbox(` +ChromeUtils.importESModule("resource://test/es6module_parse_error.js", { + global: "current", +}); +`, sb); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /unexpected token/); + } + Assert.ok(caught); +}); + +add_task(async function testParseErrorInImport() { + // Parse error in imported module should be thrown. + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + const sb = new Cu.Sandbox(uri, sandboxOpts); + + let caught = false; + try { + Cu.evalInSandbox(` +ChromeUtils.importESModule("resource://test/es6module_parse_error_in_import.js", { + global: "current", +}); +`, sb); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /unexpected token/); + } + Assert.ok(caught); +}); + +add_task(async function testImportError() { + // Error for nested import should be thrown. + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + const sb = new Cu.Sandbox(uri, sandboxOpts); + + let caught = false; + try { + Cu.evalInSandbox(` +ChromeUtils.importESModule("resource://test/es6module_import_error.js", { + global: "current", +}); +`, sb); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /import not found/); + } + Assert.ok(caught); +}); + +add_task(async function testExecutionError() { + // Error while execution the top-level script should be thrown. + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + const sb = new Cu.Sandbox(uri, sandboxOpts); + + let caught = false; + try { + Cu.evalInSandbox(` +ChromeUtils.importESModule("resource://test/es6module_throws.js", { + global: "current", +}); +`, sb); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /foobar/); + } + Assert.ok(caught); + + // Re-import should throw the same error. + + caught = false; + try { + Cu.evalInSandbox(` +ChromeUtils.importESModule("resource://test/es6module_throws.js", { + global: "current", +}); +`, sb); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /foobar/); + } + Assert.ok(caught); +}); + +add_task(async function testImportNestShared() { + // Importing system ESM should work. + + const win1 = createChromeWindow(); + + const result = win1.eval(` +const { func1 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_shared_1.mjs", { + global: "current", +}); +func1(); +`); + + Assert.equal(result, 27); +}); + +add_task(async function testImportNestNonSharedSame() { + // For the same global, nested import for non-shared global is allowed while + // executing top-level script. + + const win1 = createChromeWindow(); + + const result = win1.eval(` +const { func } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_1.mjs", { + global: "current", +}); +func(); +`); + Assert.equal(result, 10); +}); + +add_task(async function testImportNestNonSharedDifferent() { + // For the different globals, nested import for non-shared global isn't + // allowed while executing top-level script. + + const win1 = createChromeWindow(); + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + win1.sb = new Cu.Sandbox(uri, sandboxOpts); + + let caught = false; + try { + win1.eval(` +ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_2.mjs", { + global: "current", +}); +`); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /cannot be used for different global/); + } + Assert.ok(caught); +}); + +add_task(async function testImportNestNonSharedAfterImport() { + // Nested import for non-shared global is allowed after the import, both for + // the same and different globals. + + const win1 = createChromeWindow(); + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + win1.sb = new Cu.Sandbox(uri, sandboxOpts); + + const result = win1.eval(` +const { func3 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_3.mjs", { + global: "current", +}); + +// Nested import happens here. +func3(); +`); + Assert.equal(result, 22); +}); + +add_task(async function testIsolationWithSandbox() { + // Modules should be isolated for each sandbox. + + const uri = "http://example.com/"; + const window = createContentWindow(uri); + const sandboxOpts = { + sandboxPrototype: window, + wantGlobalProperties: ["ChromeUtils"], + }; + const sb1 = new Cu.Sandbox(uri, sandboxOpts); + const sb2 = new Cu.Sandbox(uri, sandboxOpts); + const sb3 = new Cu.Sandbox(uri, sandboxOpts); + + // Verify modules in 2 sandboxes are isolated. + + Cu.evalInSandbox(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`, sb1); + Cu.evalInSandbox(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`, sb2); + + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 0); + Cu.evalInSandbox(`ns.incCounter();`, sb1); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 1); + + Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb1), "2,1"); + + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 0); + Cu.evalInSandbox(`ns.incCounter();`, sb2); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 1); + + Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb2), "2,1"); + + // Verify importing after any modification to different global doesn't affect. + + const ns3 = Cu.evalInSandbox(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`, sb3); + + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 0); + Cu.evalInSandbox(`ns.incCounter();`, sb3); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 1); + + Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb3), "2,1"); + + // Verify yet another modification are still isolated. + + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 1); + Cu.evalInSandbox(`ns.incCounter();`, sb1); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 2); + + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 1); + Cu.evalInSandbox(`ns.incCounter();`, sb2); + Cu.evalInSandbox(`ns.incCounter();`, sb2); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 3); + + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 1); + Cu.evalInSandbox(`ns.incCounter();`, sb3); + Cu.evalInSandbox(`ns.incCounter();`, sb3); + Cu.evalInSandbox(`ns.incCounter();`, sb3); + Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 4); + + // Verify the module's `globalThis` points the target global. + + Cu.evalInSandbox(`ns.putCounter();`, sb1); + Cu.evalInSandbox(`ns.putCounter();`, sb2); + Cu.evalInSandbox(`ns.putCounter();`, sb3); + + const counter1 = Cu.evalInSandbox(`globalThis["counter"]`, sb1); + Assert.equal(counter1, 2); + const counter2 = Cu.evalInSandbox(`globalThis["counter"]`, sb2); + Assert.equal(counter2, 3); + const counter3 = Cu.evalInSandbox(`globalThis["counter"]`, sb3); + Assert.equal(counter3, 4); +}); + +add_task(async function testIsolationWithWindow() { + // Modules should be isolated for each window. + + const win1 = createChromeWindow(); + const win2 = createChromeWindow(); + const win3 = createChromeWindow(); + + // Verify modules in 2 sandboxes are isolated. + + win1.eval(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`); + win2.eval(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(win1.eval(`ns.getCounter();`), 0); + win1.eval(`ns.incCounter();`); + Assert.equal(win1.eval(`ns.getCounter();`), 1); + + Assert.equal(win1.eval(`globalThis["loaded"].join(",")`), "2,1"); + + Assert.equal(win2.eval(`ns.getCounter();`), 0); + win2.eval(`ns.incCounter();`); + Assert.equal(win2.eval(`ns.getCounter();`), 1); + + Assert.equal(win2.eval(`globalThis["loaded"].join(",")`), "2,1"); + + // Verify importing after any modification to different global doesn't affect. + + const ns3 = win3.eval(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(win3.eval(`ns.getCounter();`), 0); + win3.eval(`ns.incCounter();`); + Assert.equal(win3.eval(`ns.getCounter();`), 1); + + Assert.equal(win3.eval(`globalThis["loaded"].join(",")`), "2,1"); + + // Verify yet another modification are still isolated. + + Assert.equal(win1.eval(`ns.getCounter();`), 1); + win1.eval(`ns.incCounter();`); + Assert.equal(win1.eval(`ns.getCounter();`), 2); + + Assert.equal(win2.eval(`ns.getCounter();`), 1); + win2.eval(`ns.incCounter();`); + win2.eval(`ns.incCounter();`); + Assert.equal(win2.eval(`ns.getCounter();`), 3); + + Assert.equal(win3.eval(`ns.getCounter();`), 1); + win3.eval(`ns.incCounter();`); + win3.eval(`ns.incCounter();`); + win3.eval(`ns.incCounter();`); + Assert.equal(win3.eval(`ns.getCounter();`), 4); + + // Verify the module's `globalThis` points the target global. + + win1.eval(`ns.putCounter();`); + win2.eval(`ns.putCounter();`); + win3.eval(`ns.putCounter();`); + + const counter1 = win1.eval(`globalThis["counter"]`); + Assert.equal(counter1, 2); + const counter2 = win2.eval(`globalThis["counter"]`); + Assert.equal(counter2, 3); + const counter3 = win3.eval(`globalThis["counter"]`); + Assert.equal(counter3, 4); +}); + +add_task(async function testSyncImportBeforeAsyncImportTopLevel() { + const window = createChromeWindow(); + + window.eval(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(window.eval(`ns.getCounter();`), 0); + window.eval(`ns.incCounter();`); + Assert.equal(window.eval(`ns.getCounter();`), 1); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); + + window.eval(` +var ns2 = null; +const nsPromise = import("resource://test/non_shared_1.mjs"); +nsPromise.then(v => { ns2 = v; }); +`); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns2 !== null`) + ); + + Assert.equal(window.eval(`ns2.getCounter();`), 1); + window.eval(`ns2.incCounter();`); + Assert.equal(window.eval(`ns2.getCounter();`), 2); + Assert.equal(window.eval(`ns.getCounter();`), 2); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); +}); + +add_task(async function testSyncImportBeforeAsyncImportDependency() { + const window = createChromeWindow(); + + window.eval(` +globalThis["loaded"] = []; +var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(window.eval(`ns.getCounter();`), 0); + window.eval(`ns.incCounter();`); + Assert.equal(window.eval(`ns.getCounter();`), 1); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); + + window.eval(` +var ns2 = null; +const nsPromise = import("resource://test/import_non_shared_1.mjs"); +nsPromise.then(v => { ns2 = v; }); +`); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns2 !== null`) + ); + + Assert.equal(window.eval(`ns2.getCounter();`), 1); + window.eval(`ns2.incCounter();`); + Assert.equal(window.eval(`ns2.getCounter();`), 2); + Assert.equal(window.eval(`ns.getCounter();`), 2); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); +}); + +add_task(async function testSyncImportAfterAsyncImportTopLevel() { + const window = createChromeWindow(); + + window.eval(` +var ns = null; +globalThis["loaded"] = []; +const nsPromise = import("resource://test/non_shared_1.mjs"); +nsPromise.then(v => { ns = v; }); +`); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns !== null`) + ); + + Assert.equal(window.eval(`ns.getCounter();`), 0); + window.eval(`ns.incCounter();`); + Assert.equal(window.eval(`ns.getCounter();`), 1); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); + + window.eval(` +var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(window.eval(`ns2.getCounter();`), 1); + window.eval(`ns2.incCounter();`); + Assert.equal(window.eval(`ns2.getCounter();`), 2); + Assert.equal(window.eval(`ns.getCounter();`), 2); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); +}); + +add_task(async function testSyncImportAfterAsyncImportDependency() { + const window = createChromeWindow(); + + window.eval(` +var ns = null; +globalThis["loaded"] = []; +const nsPromise = import("resource://test/non_shared_1.mjs"); +nsPromise.then(v => { ns = v; }); +`); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns !== null`) + ); + + Assert.equal(window.eval(`ns.getCounter();`), 0); + window.eval(`ns.incCounter();`); + Assert.equal(window.eval(`ns.getCounter();`), 1); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); + + window.eval(` +var ns2 = ChromeUtils.importESModule("resource://test/import_non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(window.eval(`ns2.getCounter();`), 1); + window.eval(`ns2.incCounter();`); + Assert.equal(window.eval(`ns2.getCounter();`), 2); + Assert.equal(window.eval(`ns.getCounter();`), 2); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); +}); + +add_task(async function testSyncImportWhileAsyncImportTopLevel() { + const window = createChromeWindow(); + + window.eval(` +var ns = null; +globalThis["loaded"] = []; +const nsPromise = import("resource://test/non_shared_1.mjs"); +nsPromise.then(v => { ns = v; }); +`); + + window.eval(` +var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(window.eval(`ns2.getCounter();`), 0); + window.eval(`ns2.incCounter();`); + Assert.equal(window.eval(`ns2.getCounter();`), 1); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns !== null`) + ); + + Assert.equal(window.eval(`ns.getCounter();`), 1); + window.eval(`ns.incCounter();`); + Assert.equal(window.eval(`ns.getCounter();`), 2); + Assert.equal(window.eval(`ns2.getCounter();`), 2); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); +}); + +add_task(async function testSyncImportWhileAsyncImportDependency() { + const window = createChromeWindow(); + + window.eval(` +var ns = null; +globalThis["loaded"] = []; +const nsPromise = import("resource://test/non_shared_1.mjs"); +nsPromise.then(v => { ns = v; }); +`); + + window.eval(` +var ns2 = ChromeUtils.importESModule("resource://test/import_non_shared_1.mjs", { + global: "current", +}); +`); + + Assert.equal(window.eval(`ns2.getCounter();`), 0); + window.eval(`ns2.incCounter();`); + Assert.equal(window.eval(`ns2.getCounter();`), 1); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns !== null`) + ); + + Assert.equal(window.eval(`ns.getCounter();`), 1); + window.eval(`ns.incCounter();`); + Assert.equal(window.eval(`ns.getCounter();`), 2); + Assert.equal(window.eval(`ns2.getCounter();`), 2); + + Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1"); +}); + +add_task(async function testSyncImportBeforeAsyncImportTLA() { + // Top-level-await is not supported by sync import. + + const window = createChromeWindow(); + + let caught = false; + + try { + window.eval(` +ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", { + global: "current", +}); +`); + } catch (e) { + caught = true; + Assert.stringMatches(e.message, /top level await is not supported/); + } + Assert.ok(caught); + + window.eval(` +var ns2 = null; +const nsPromise = import("resource://test/es6module_top_level_await.js"); +nsPromise.then(v => { ns2 = v; }); +`); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns2 !== null`) + ); + + Assert.equal(window.eval(`ns2.foo();`), 10); +}); + +add_task(async function testSyncImportAfterAsyncImportTLA() { + // Top-level-await is not supported by sync import, but if the module is + // already imported, the existing module namespace is returned. + + const window = createChromeWindow(); + + window.eval(` +var ns2 = null; +const nsPromise = import("resource://test/es6module_top_level_await.js"); +nsPromise.then(v => { ns2 = v; }); +`); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns2 !== null`) + ); + + Assert.equal(window.eval(`ns2.foo();`), 10); + + window.eval(` +var ns = ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", { + global: "current", +}); +`); + + Assert.equal(window.eval(`ns.foo();`), 10); + Assert.equal(window.eval(`ns2.foo == ns.foo;`), true); +}); + +add_task(async function testSyncImportWhileAsyncImportTLA() { + // Top-level-await is not supported by sync import, but if the module is + // already fetching, ChromeUtils.importESModule waits for it and, the + // async-imported module namespace is returned. + + const window = createChromeWindow(); + + window.eval(` +var ns2 = null; +const nsPromise = import("resource://test/es6module_top_level_await.js"); +nsPromise.then(v => { ns2 = v; }); +`); + + window.eval(` +var ns = ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", { + global: "current", +}); +`); + + Services.tm.spinEventLoopUntil( + "Wait until dynamic import finishes", + () => window.eval(`ns2 !== null`) + ); + + Assert.equal(window.eval(`ns2.foo();`), 10); + Assert.equal(window.eval(`ns.foo();`), 10); + Assert.equal(window.eval(`ns2.foo == ns.foo;`), true); +}); |