summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/tests/unit/test_import_global_current.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/tests/unit/test_import_global_current.js')
-rw-r--r--js/xpconnect/tests/unit/test_import_global_current.js796
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);
+});