1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 add_task(async function testSandbox() {
6 const uri = "http://example.com/";
7 const window = createContentWindow(uri);
9 sandboxPrototype: window,
10 wantGlobalProperties: ["ChromeUtils"],
12 const sb = new Cu.Sandbox(uri, sandboxOpts);
15 globalThis["loaded"] = [];
16 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
21 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 0);
22 Cu.evalInSandbox(`ns.incCounter();`, sb);
23 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 1);
25 Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1");
28 add_task(async function testNoWindowSandbox() {
29 // Sandbox without window doesn't have ScriptLoader, and Sandbox's
30 // ModuleLoader cannot be created.
31 const systemPrincipal = Components.Constructor(
32 "@mozilla.org/systemprincipal;1",
36 wantGlobalProperties: ["ChromeUtils"],
39 const sb = new Cu.Sandbox(systemPrincipal, sandboxOpts);
44 ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
50 Assert.stringMatches(e.message, /No ModuleLoader found/);
55 add_task(async function testWindow() {
56 const win1 = createChromeWindow();
59 globalThis["loaded"] = [];
60 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
65 Assert.equal(win1.eval(`ns.getCounter();`), 0);
66 win1.eval(`ns.incCounter();`);
67 Assert.equal(win1.eval(`ns.getCounter();`), 1);
69 Assert.equal(win1.eval(`globalThis["loaded"].join(",")`), "2,1");
72 add_task(async function testReImport() {
73 // Re-importing the same module should return the same thing.
75 const uri = "http://example.com/";
76 const window = createContentWindow(uri);
78 sandboxPrototype: window,
79 wantGlobalProperties: ["ChromeUtils"],
81 const sb = new Cu.Sandbox(uri, sandboxOpts);
84 globalThis["loaded"] = [];
85 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
90 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 0);
91 Cu.evalInSandbox(`ns.incCounter();`, sb);
92 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 1);
94 Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1");
97 var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
102 // The counter should be shared, and also not reset.
103 Assert.equal(Cu.evalInSandbox(`ns2.getCounter();`, sb), 1);
104 Cu.evalInSandbox(`ns2.incCounter();`, sb);
105 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 2);
106 Assert.equal(Cu.evalInSandbox(`ns2.getCounter();`, sb), 2);
108 // The top-level script shouldn't be executed twice.
109 Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1");
112 add_task(async function testNotFound() {
113 // Importing non-existent file should throw error.
115 const uri = "http://example.com/";
116 const window = createContentWindow(uri);
117 const sandboxOpts = {
118 sandboxPrototype: window,
119 wantGlobalProperties: ["ChromeUtils"],
121 const sb = new Cu.Sandbox(uri, sandboxOpts);
126 ChromeUtils.importESModule("resource://test/not_found.mjs", {
132 Assert.stringMatches(e.message, /Failed to load/);
137 add_task(async function testParseError() {
138 // Parse error should be thrown.
140 const uri = "http://example.com/";
141 const window = createContentWindow(uri);
142 const sandboxOpts = {
143 sandboxPrototype: window,
144 wantGlobalProperties: ["ChromeUtils"],
146 const sb = new Cu.Sandbox(uri, sandboxOpts);
151 ChromeUtils.importESModule("resource://test/es6module_parse_error.js", {
157 Assert.stringMatches(e.message, /unexpected token/);
162 add_task(async function testParseErrorInImport() {
163 // Parse error in imported module should be thrown.
165 const uri = "http://example.com/";
166 const window = createContentWindow(uri);
167 const sandboxOpts = {
168 sandboxPrototype: window,
169 wantGlobalProperties: ["ChromeUtils"],
171 const sb = new Cu.Sandbox(uri, sandboxOpts);
176 ChromeUtils.importESModule("resource://test/es6module_parse_error_in_import.js", {
182 Assert.stringMatches(e.message, /unexpected token/);
187 add_task(async function testImportError() {
188 // Error for nested import should be thrown.
190 const uri = "http://example.com/";
191 const window = createContentWindow(uri);
192 const sandboxOpts = {
193 sandboxPrototype: window,
194 wantGlobalProperties: ["ChromeUtils"],
196 const sb = new Cu.Sandbox(uri, sandboxOpts);
201 ChromeUtils.importESModule("resource://test/es6module_import_error.js", {
207 Assert.stringMatches(e.message, /doesn't provide an export named/);
212 add_task(async function testExecutionError() {
213 // Error while execution the top-level script should be thrown.
215 const uri = "http://example.com/";
216 const window = createContentWindow(uri);
217 const sandboxOpts = {
218 sandboxPrototype: window,
219 wantGlobalProperties: ["ChromeUtils"],
221 const sb = new Cu.Sandbox(uri, sandboxOpts);
226 ChromeUtils.importESModule("resource://test/es6module_throws.js", {
232 Assert.stringMatches(e.message, /foobar/);
236 // Re-import should throw the same error.
241 ChromeUtils.importESModule("resource://test/es6module_throws.js", {
247 Assert.stringMatches(e.message, /foobar/);
252 add_task(async function testImportNestShared() {
253 // Importing system ESM should work.
255 const win1 = createChromeWindow();
257 const result = win1.eval(`
258 const { func1 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_shared_1.mjs", {
264 Assert.equal(result, 27);
267 add_task(async function testImportNestNonSharedSame() {
268 // For the same global, nested import for non-shared global is allowed while
269 // executing top-level script.
271 const win1 = createChromeWindow();
273 const result = win1.eval(`
274 const { func } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_1.mjs", {
279 Assert.equal(result, 10);
282 add_task(async function testImportNestNonSharedDifferent() {
283 // For the different globals, nested import for non-shared global isn't
284 // allowed while executing top-level script.
286 const win1 = createChromeWindow();
288 const uri = "http://example.com/";
289 const window = createContentWindow(uri);
290 const sandboxOpts = {
291 sandboxPrototype: window,
292 wantGlobalProperties: ["ChromeUtils"],
294 win1.sb = new Cu.Sandbox(uri, sandboxOpts);
299 ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_2.mjs", {
305 Assert.stringMatches(e.message, /cannot be used for different global/);
310 add_task(async function testImportNestNonSharedAfterImport() {
311 // Nested import for non-shared global is allowed after the import, both for
312 // the same and different globals.
314 const win1 = createChromeWindow();
316 const uri = "http://example.com/";
317 const window = createContentWindow(uri);
318 const sandboxOpts = {
319 sandboxPrototype: window,
320 wantGlobalProperties: ["ChromeUtils"],
322 win1.sb = new Cu.Sandbox(uri, sandboxOpts);
324 const result = win1.eval(`
325 const { func3 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_3.mjs", {
329 // Nested import happens here.
332 Assert.equal(result, 22);
335 add_task(async function testIsolationWithSandbox() {
336 // Modules should be isolated for each sandbox.
338 const uri = "http://example.com/";
339 const window = createContentWindow(uri);
340 const sandboxOpts = {
341 sandboxPrototype: window,
342 wantGlobalProperties: ["ChromeUtils"],
344 const sb1 = new Cu.Sandbox(uri, sandboxOpts);
345 const sb2 = new Cu.Sandbox(uri, sandboxOpts);
346 const sb3 = new Cu.Sandbox(uri, sandboxOpts);
348 // Verify modules in 2 sandboxes are isolated.
351 globalThis["loaded"] = [];
352 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
357 globalThis["loaded"] = [];
358 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
363 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 0);
364 Cu.evalInSandbox(`ns.incCounter();`, sb1);
365 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 1);
367 Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb1), "2,1");
369 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 0);
370 Cu.evalInSandbox(`ns.incCounter();`, sb2);
371 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 1);
373 Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb2), "2,1");
375 // Verify importing after any modification to different global doesn't affect.
377 const ns3 = Cu.evalInSandbox(`
378 globalThis["loaded"] = [];
379 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
384 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 0);
385 Cu.evalInSandbox(`ns.incCounter();`, sb3);
386 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 1);
388 Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb3), "2,1");
390 // Verify yet another modification are still isolated.
392 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 1);
393 Cu.evalInSandbox(`ns.incCounter();`, sb1);
394 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 2);
396 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 1);
397 Cu.evalInSandbox(`ns.incCounter();`, sb2);
398 Cu.evalInSandbox(`ns.incCounter();`, sb2);
399 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 3);
401 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 1);
402 Cu.evalInSandbox(`ns.incCounter();`, sb3);
403 Cu.evalInSandbox(`ns.incCounter();`, sb3);
404 Cu.evalInSandbox(`ns.incCounter();`, sb3);
405 Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 4);
407 // Verify the module's `globalThis` points the target global.
409 Cu.evalInSandbox(`ns.putCounter();`, sb1);
410 Cu.evalInSandbox(`ns.putCounter();`, sb2);
411 Cu.evalInSandbox(`ns.putCounter();`, sb3);
413 const counter1 = Cu.evalInSandbox(`globalThis["counter"]`, sb1);
414 Assert.equal(counter1, 2);
415 const counter2 = Cu.evalInSandbox(`globalThis["counter"]`, sb2);
416 Assert.equal(counter2, 3);
417 const counter3 = Cu.evalInSandbox(`globalThis["counter"]`, sb3);
418 Assert.equal(counter3, 4);
421 add_task(async function testIsolationWithWindow() {
422 // Modules should be isolated for each window.
424 const win1 = createChromeWindow();
425 const win2 = createChromeWindow();
426 const win3 = createChromeWindow();
428 // Verify modules in 2 sandboxes are isolated.
431 globalThis["loaded"] = [];
432 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
437 globalThis["loaded"] = [];
438 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
443 Assert.equal(win1.eval(`ns.getCounter();`), 0);
444 win1.eval(`ns.incCounter();`);
445 Assert.equal(win1.eval(`ns.getCounter();`), 1);
447 Assert.equal(win1.eval(`globalThis["loaded"].join(",")`), "2,1");
449 Assert.equal(win2.eval(`ns.getCounter();`), 0);
450 win2.eval(`ns.incCounter();`);
451 Assert.equal(win2.eval(`ns.getCounter();`), 1);
453 Assert.equal(win2.eval(`globalThis["loaded"].join(",")`), "2,1");
455 // Verify importing after any modification to different global doesn't affect.
457 const ns3 = win3.eval(`
458 globalThis["loaded"] = [];
459 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
464 Assert.equal(win3.eval(`ns.getCounter();`), 0);
465 win3.eval(`ns.incCounter();`);
466 Assert.equal(win3.eval(`ns.getCounter();`), 1);
468 Assert.equal(win3.eval(`globalThis["loaded"].join(",")`), "2,1");
470 // Verify yet another modification are still isolated.
472 Assert.equal(win1.eval(`ns.getCounter();`), 1);
473 win1.eval(`ns.incCounter();`);
474 Assert.equal(win1.eval(`ns.getCounter();`), 2);
476 Assert.equal(win2.eval(`ns.getCounter();`), 1);
477 win2.eval(`ns.incCounter();`);
478 win2.eval(`ns.incCounter();`);
479 Assert.equal(win2.eval(`ns.getCounter();`), 3);
481 Assert.equal(win3.eval(`ns.getCounter();`), 1);
482 win3.eval(`ns.incCounter();`);
483 win3.eval(`ns.incCounter();`);
484 win3.eval(`ns.incCounter();`);
485 Assert.equal(win3.eval(`ns.getCounter();`), 4);
487 // Verify the module's `globalThis` points the target global.
489 win1.eval(`ns.putCounter();`);
490 win2.eval(`ns.putCounter();`);
491 win3.eval(`ns.putCounter();`);
493 const counter1 = win1.eval(`globalThis["counter"]`);
494 Assert.equal(counter1, 2);
495 const counter2 = win2.eval(`globalThis["counter"]`);
496 Assert.equal(counter2, 3);
497 const counter3 = win3.eval(`globalThis["counter"]`);
498 Assert.equal(counter3, 4);
501 add_task(async function testSyncImportBeforeAsyncImportTopLevel() {
502 const window = createChromeWindow();
505 globalThis["loaded"] = [];
506 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
511 Assert.equal(window.eval(`ns.getCounter();`), 0);
512 window.eval(`ns.incCounter();`);
513 Assert.equal(window.eval(`ns.getCounter();`), 1);
515 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
519 const nsPromise = import("resource://test/non_shared_1.mjs");
520 nsPromise.then(v => { ns2 = v; });
523 Services.tm.spinEventLoopUntil(
524 "Wait until dynamic import finishes",
525 () => window.eval(`ns2 !== null`)
528 Assert.equal(window.eval(`ns2.getCounter();`), 1);
529 window.eval(`ns2.incCounter();`);
530 Assert.equal(window.eval(`ns2.getCounter();`), 2);
531 Assert.equal(window.eval(`ns.getCounter();`), 2);
533 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
536 add_task(async function testSyncImportBeforeAsyncImportDependency() {
537 const window = createChromeWindow();
540 globalThis["loaded"] = [];
541 var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
546 Assert.equal(window.eval(`ns.getCounter();`), 0);
547 window.eval(`ns.incCounter();`);
548 Assert.equal(window.eval(`ns.getCounter();`), 1);
550 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
554 const nsPromise = import("resource://test/import_non_shared_1.mjs");
555 nsPromise.then(v => { ns2 = v; });
558 Services.tm.spinEventLoopUntil(
559 "Wait until dynamic import finishes",
560 () => window.eval(`ns2 !== null`)
563 Assert.equal(window.eval(`ns2.getCounter();`), 1);
564 window.eval(`ns2.incCounter();`);
565 Assert.equal(window.eval(`ns2.getCounter();`), 2);
566 Assert.equal(window.eval(`ns.getCounter();`), 2);
568 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
571 add_task(async function testSyncImportAfterAsyncImportTopLevel() {
572 const window = createChromeWindow();
576 globalThis["loaded"] = [];
577 const nsPromise = import("resource://test/non_shared_1.mjs");
578 nsPromise.then(v => { ns = v; });
581 Services.tm.spinEventLoopUntil(
582 "Wait until dynamic import finishes",
583 () => window.eval(`ns !== null`)
586 Assert.equal(window.eval(`ns.getCounter();`), 0);
587 window.eval(`ns.incCounter();`);
588 Assert.equal(window.eval(`ns.getCounter();`), 1);
590 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
593 var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
598 Assert.equal(window.eval(`ns2.getCounter();`), 1);
599 window.eval(`ns2.incCounter();`);
600 Assert.equal(window.eval(`ns2.getCounter();`), 2);
601 Assert.equal(window.eval(`ns.getCounter();`), 2);
603 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
606 add_task(async function testSyncImportAfterAsyncImportDependency() {
607 const window = createChromeWindow();
611 globalThis["loaded"] = [];
612 const nsPromise = import("resource://test/non_shared_1.mjs");
613 nsPromise.then(v => { ns = v; });
616 Services.tm.spinEventLoopUntil(
617 "Wait until dynamic import finishes",
618 () => window.eval(`ns !== null`)
621 Assert.equal(window.eval(`ns.getCounter();`), 0);
622 window.eval(`ns.incCounter();`);
623 Assert.equal(window.eval(`ns.getCounter();`), 1);
625 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
628 var ns2 = ChromeUtils.importESModule("resource://test/import_non_shared_1.mjs", {
633 Assert.equal(window.eval(`ns2.getCounter();`), 1);
634 window.eval(`ns2.incCounter();`);
635 Assert.equal(window.eval(`ns2.getCounter();`), 2);
636 Assert.equal(window.eval(`ns.getCounter();`), 2);
638 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
641 add_task(async function testSyncImportWhileAsyncImportTopLevel() {
642 const window = createChromeWindow();
646 globalThis["loaded"] = [];
647 const nsPromise = import("resource://test/non_shared_1.mjs");
648 nsPromise.then(v => { ns = v; });
652 var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
657 Assert.equal(window.eval(`ns2.getCounter();`), 0);
658 window.eval(`ns2.incCounter();`);
659 Assert.equal(window.eval(`ns2.getCounter();`), 1);
661 Services.tm.spinEventLoopUntil(
662 "Wait until dynamic import finishes",
663 () => window.eval(`ns !== null`)
666 Assert.equal(window.eval(`ns.getCounter();`), 1);
667 window.eval(`ns.incCounter();`);
668 Assert.equal(window.eval(`ns.getCounter();`), 2);
669 Assert.equal(window.eval(`ns2.getCounter();`), 2);
671 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
674 add_task(async function testSyncImportWhileAsyncImportDependency() {
675 const window = createChromeWindow();
679 globalThis["loaded"] = [];
680 const nsPromise = import("resource://test/non_shared_1.mjs");
681 nsPromise.then(v => { ns = v; });
685 var ns2 = ChromeUtils.importESModule("resource://test/import_non_shared_1.mjs", {
690 Assert.equal(window.eval(`ns2.getCounter();`), 0);
691 window.eval(`ns2.incCounter();`);
692 Assert.equal(window.eval(`ns2.getCounter();`), 1);
694 Services.tm.spinEventLoopUntil(
695 "Wait until dynamic import finishes",
696 () => window.eval(`ns !== null`)
699 Assert.equal(window.eval(`ns.getCounter();`), 1);
700 window.eval(`ns.incCounter();`);
701 Assert.equal(window.eval(`ns.getCounter();`), 2);
702 Assert.equal(window.eval(`ns2.getCounter();`), 2);
704 Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
707 add_task(async function testSyncImportBeforeAsyncImportTLA() {
708 // Top-level-await is not supported by sync import.
710 const window = createChromeWindow();
716 ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", {
722 Assert.stringMatches(e.message, /top level await is not supported/);
728 const nsPromise = import("resource://test/es6module_top_level_await.js");
729 nsPromise.then(v => { ns2 = v; });
732 Services.tm.spinEventLoopUntil(
733 "Wait until dynamic import finishes",
734 () => window.eval(`ns2 !== null`)
737 Assert.equal(window.eval(`ns2.foo();`), 10);
740 add_task(async function testSyncImportAfterAsyncImportTLA() {
741 // Top-level-await is not supported by sync import, but if the module is
742 // already imported, the existing module namespace is returned.
744 const window = createChromeWindow();
748 const nsPromise = import("resource://test/es6module_top_level_await.js");
749 nsPromise.then(v => { ns2 = v; });
752 Services.tm.spinEventLoopUntil(
753 "Wait until dynamic import finishes",
754 () => window.eval(`ns2 !== null`)
757 Assert.equal(window.eval(`ns2.foo();`), 10);
760 var ns = ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", {
765 Assert.equal(window.eval(`ns.foo();`), 10);
766 Assert.equal(window.eval(`ns2.foo == ns.foo;`), true);
769 add_task(async function testSyncImportWhileAsyncImportTLA() {
770 // Top-level-await is not supported by sync import, but if the module is
771 // already fetching, ChromeUtils.importESModule waits for it and, the
772 // async-imported module namespace is returned.
774 const window = createChromeWindow();
778 const nsPromise = import("resource://test/es6module_top_level_await.js");
779 nsPromise.then(v => { ns2 = v; });
783 var ns = ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", {
788 Services.tm.spinEventLoopUntil(
789 "Wait until dynamic import finishes",
790 () => window.eval(`ns2 !== null`)
793 Assert.equal(window.eval(`ns2.foo();`), 10);
794 Assert.equal(window.eval(`ns.foo();`), 10);
795 Assert.equal(window.eval(`ns2.foo == ns.foo;`), true);