From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- dom/promise/tests/chrome.toml | 27 + .../tests/file_promise_and_timeout_ordering.js | 18 + dom/promise/tests/file_promise_argument_tests.js | 175 +++++ ...romise_job_with_bind_from_discarded_iframe.html | 14 + dom/promise/tests/file_promise_retval_tests.js | 56 ++ dom/promise/tests/file_promise_xrays.html | 34 + dom/promise/tests/mochitest.toml | 44 ++ dom/promise/tests/promise_uncatchable_exception.js | 11 + dom/promise/tests/test_bug883683.html | 41 + dom/promise/tests/test_on_new_promise.html | 45 ++ dom/promise/tests/test_on_promise_settled.html | 53 ++ .../tests/test_on_promise_settled_duplicates.html | 58 ++ dom/promise/tests/test_promise.html | 844 +++++++++++++++++++++ .../tests/test_promise_and_timeout_ordering.html | 16 + .../test_promise_and_timeout_ordering_workers.html | 14 + dom/promise/tests/test_promise_argument.html | 49 ++ dom/promise/tests/test_promise_argument_xrays.html | 90 +++ .../tests/test_promise_callback_retval.html | 53 ++ ...romise_job_with_bind_from_discarded_iframe.html | 63 ++ dom/promise/tests/test_promise_retval.html | 51 ++ dom/promise/tests/test_promise_retval_xrays.html | 94 +++ .../tests/test_promise_uncatchable_exception.html | 35 + dom/promise/tests/test_promise_utils.html | 313 ++++++++ dom/promise/tests/test_promise_xrays.html | 365 +++++++++ dom/promise/tests/test_resolve.html | 66 ++ dom/promise/tests/test_resolver_return_value.html | 40 + dom/promise/tests/test_species_getter.html | 25 + .../tests/test_thenable_vs_promise_ordering.html | 29 + dom/promise/tests/test_webassembly_compile.html | 446 +++++++++++ .../tests/test_webassembly_compile_sample.wasm | Bin 0 -> 16053 bytes .../tests/test_webassembly_compile_worker.js | 55 ++ .../test_webassembly_compile_worker_terminate.js | 13 + dom/promise/tests/unit/test_monitor_uncaught.js | 322 ++++++++ .../tests/unit/test_promise_job_across_sandbox.js | 221 ++++++ .../tests/unit/test_promise_unhandled_rejection.js | 139 ++++ dom/promise/tests/unit/xpcshell.toml | 8 + 36 files changed, 3927 insertions(+) create mode 100644 dom/promise/tests/chrome.toml create mode 100644 dom/promise/tests/file_promise_and_timeout_ordering.js create mode 100644 dom/promise/tests/file_promise_argument_tests.js create mode 100644 dom/promise/tests/file_promise_job_with_bind_from_discarded_iframe.html create mode 100644 dom/promise/tests/file_promise_retval_tests.js create mode 100644 dom/promise/tests/file_promise_xrays.html create mode 100644 dom/promise/tests/mochitest.toml create mode 100644 dom/promise/tests/promise_uncatchable_exception.js create mode 100644 dom/promise/tests/test_bug883683.html create mode 100644 dom/promise/tests/test_on_new_promise.html create mode 100644 dom/promise/tests/test_on_promise_settled.html create mode 100644 dom/promise/tests/test_on_promise_settled_duplicates.html create mode 100644 dom/promise/tests/test_promise.html create mode 100644 dom/promise/tests/test_promise_and_timeout_ordering.html create mode 100644 dom/promise/tests/test_promise_and_timeout_ordering_workers.html create mode 100644 dom/promise/tests/test_promise_argument.html create mode 100644 dom/promise/tests/test_promise_argument_xrays.html create mode 100644 dom/promise/tests/test_promise_callback_retval.html create mode 100644 dom/promise/tests/test_promise_job_with_bind_from_discarded_iframe.html create mode 100644 dom/promise/tests/test_promise_retval.html create mode 100644 dom/promise/tests/test_promise_retval_xrays.html create mode 100644 dom/promise/tests/test_promise_uncatchable_exception.html create mode 100644 dom/promise/tests/test_promise_utils.html create mode 100644 dom/promise/tests/test_promise_xrays.html create mode 100644 dom/promise/tests/test_resolve.html create mode 100644 dom/promise/tests/test_resolver_return_value.html create mode 100644 dom/promise/tests/test_species_getter.html create mode 100644 dom/promise/tests/test_thenable_vs_promise_ordering.html create mode 100644 dom/promise/tests/test_webassembly_compile.html create mode 100644 dom/promise/tests/test_webassembly_compile_sample.wasm create mode 100644 dom/promise/tests/test_webassembly_compile_worker.js create mode 100644 dom/promise/tests/test_webassembly_compile_worker_terminate.js create mode 100644 dom/promise/tests/unit/test_monitor_uncaught.js create mode 100644 dom/promise/tests/unit/test_promise_job_across_sandbox.js create mode 100644 dom/promise/tests/unit/test_promise_unhandled_rejection.js create mode 100644 dom/promise/tests/unit/xpcshell.toml (limited to 'dom/promise/tests') diff --git a/dom/promise/tests/chrome.toml b/dom/promise/tests/chrome.toml new file mode 100644 index 0000000000..9b85c2de69 --- /dev/null +++ b/dom/promise/tests/chrome.toml @@ -0,0 +1,27 @@ +[DEFAULT] + +["test_on_new_promise.html"] + +["test_on_promise_settled.html"] + +["test_on_promise_settled_duplicates.html"] + +["test_promise_argument_xrays.html"] +support-files = [ + "file_promise_xrays.html", + "file_promise_argument_tests.js", +] +skip-if = ["!debug"] + +["test_promise_job_with_bind_from_discarded_iframe.html"] +support-files = ["file_promise_job_with_bind_from_discarded_iframe.html"] + +["test_promise_retval_xrays.html"] +support-files = [ + "file_promise_xrays.html", + "file_promise_retval_tests.js" +] +skip-if = ["debug == false"] + +["test_promise_xrays.html"] +support-files = ["file_promise_xrays.html"] diff --git a/dom/promise/tests/file_promise_and_timeout_ordering.js b/dom/promise/tests/file_promise_and_timeout_ordering.js new file mode 100644 index 0000000000..734621eae0 --- /dev/null +++ b/dom/promise/tests/file_promise_and_timeout_ordering.js @@ -0,0 +1,18 @@ +var log = []; +var resolvedPromise = Promise.resolve(null); +function schedulePromiseTask(f) { + resolvedPromise.then(f); +} + +setTimeout(function () { + log.push("t1start"); + schedulePromiseTask(function () { + log.push("promise"); + }); + log.push("t1end"); +}, 10); + +setTimeout(function () { + log.push("t2"); + postMessage(log.join(", ")); +}, 10); diff --git a/dom/promise/tests/file_promise_argument_tests.js b/dom/promise/tests/file_promise_argument_tests.js new file mode 100644 index 0000000000..2a3b4e78c9 --- /dev/null +++ b/dom/promise/tests/file_promise_argument_tests.js @@ -0,0 +1,175 @@ +/* + * This file is meant to provide common infrastructure for several consumers. + * The consumer is expected to define the following things: + * + * 1) An verifyPromiseGlobal function which does whatever test the consumer + * wants. + * 2) An isXrayArgumentTest global boolean, because some of these tests act + * differenly based on that boolean. + * 3) A function named getPromise. This function is given a global object and a + * single argument to use for getting the promise. The getPromise function + * is expected to trigger the canonical Promise.resolve for the given global + * with the given argument in some way that depends on the test, and return + * the result. + * 4) A subframe (frames[0]) which can be used as a second global for creating + * promises. + */ + +/* global verifyPromiseGlobal, getPromise, isXrayArgumentTest */ + +var label = "parent"; + +function passBasicPromise() { + var p1 = Promise.resolve(); + verifyPromiseGlobal(p1, window, "Promise.resolve return value 1"); + var p2 = getPromise(window, p1); + is(p1, p2, "Basic promise should just pass on through"); + return p2; +} + +function passPrimitive(global) { + var p = getPromise(global, 5); + verifyPromiseGlobal(p, global, "Promise wrapping primitive"); + return p.then(function (arg) { + is(arg, 5, "Should have the arg we passed in"); + }); +} + +function passThenable(global) { + var called = false; + var thenable = { + then(f) { + called = true; + f(7); + }, + }; + var p = getPromise(global, thenable); + verifyPromiseGlobal(p, global, "Promise wrapping thenable"); + return p.then(function (arg) { + ok(called, "Thenable should have been called"); + is(arg, 7, "Should have the arg our thenable passed in"); + }); +} + +function passWrongPromiseWithMatchingConstructor() { + var p1 = Promise.resolve(); + verifyPromiseGlobal(p1, window, "Promise.resolve() return value 2"); + p1.constructor = frames[0].Promise; + var p2 = getPromise(frames[0], p1); + // The behavior here will depend on whether we're touching frames[0] via Xrays + // or not. If we are not, the current compartment while getting our promise + // will be that of frames[0]. If we are, it will be our window's compartment. + if (isXrayArgumentTest) { + isnot( + p1, + p2, + "Should have wrapped the Promise in a new promise, because its constructor is not matching the current-compartment Promise constructor" + ); + verifyPromiseGlobal( + p2, + window, + "Promise wrapping xrayed promise with therefore non-matching constructor" + ); + } else { + is( + p1, + p2, + "Should have left the Promise alone because its constructor matched" + ); + } + return p2; +} + +function passCorrectPromiseWithMismatchedConstructor() { + var p1 = Promise.resolve(9); + verifyPromiseGlobal(p1, window, "Promise.resolve() return value 3"); + p1.constructor = frames[0].Promise; + var p2 = getPromise(window, p1); + isnot( + p1, + p2, + "Should have wrapped promise in a new promise, since its .constructor was wrong" + ); + verifyPromiseGlobal( + p2, + window, + "Promise wrapping passed-in promise with mismatched constructor" + ); + return p2.then(function (arg) { + is(arg, 9, "Should have propagated along our resolution value"); + }); +} + +function passPromiseToOtherGlobal() { + var p1 = Promise.resolve(); + verifyPromiseGlobal(p1, window, "Promise.resolve() return value 4"); + var p2 = getPromise(frames[0], p1); + // The behavior here will depend on whether we're touching frames[0] via Xrays + // or not. If we are not, the current compartment while getting our promise + // will be that of frames[0]. If we are, it will be our window's compartment. + if (isXrayArgumentTest) { + is( + p1, + p2, + "Should have left the Promise alone, because its constructor matches the current compartment's constructor" + ); + } else { + isnot( + p1, + p2, + "Should have wrapped promise in a promise from the other global" + ); + verifyPromiseGlobal( + p2, + frames[0], + "Promise wrapping passed-in basic promise" + ); + } + return p2; +} + +function passPromiseSubclass() { + class PromiseSubclass extends Promise { + constructor(func) { + super(func); + } + } + + var p1 = PromiseSubclass.resolve(11); + verifyPromiseGlobal(p1, window, "PromiseSubclass.resolve() return value"); + var p2 = getPromise(window, p1); + isnot(p1, p2, "Should have wrapped promise subclass in a new promise"); + verifyPromiseGlobal( + p2, + window, + "Promise wrapping passed-in promise subclass" + ); + return p2.then(function (arg) { + is( + arg, + 11, + "Should have propagated along our resolution value from subclass" + ); + }); +} + +function runPromiseArgumentTests(finishFunc) { + Promise.resolve() + .then(passBasicPromise) + .then(passPrimitive.bind(undefined, window)) + .then(passPrimitive.bind(undefined, frames[0])) + .then(passThenable.bind(undefined, window)) + .then(passThenable.bind(undefined, frames[0])) + .then(passWrongPromiseWithMatchingConstructor) + .then(passCorrectPromiseWithMismatchedConstructor) + .then(passPromiseToOtherGlobal) + .then(passPromiseSubclass) + .then(finishFunc) + .catch(function (e) { + ok( + false, + `Exception thrown: ${e}@${location.pathname}:${e.lineNumber}:${e.columnNumber}` + ); + finishFunc(); + }); +} diff --git a/dom/promise/tests/file_promise_job_with_bind_from_discarded_iframe.html b/dom/promise/tests/file_promise_job_with_bind_from_discarded_iframe.html new file mode 100644 index 0000000000..352cf6623e --- /dev/null +++ b/dom/promise/tests/file_promise_job_with_bind_from_discarded_iframe.html @@ -0,0 +1,14 @@ + + + +iframe in http + + +
+ + + diff --git a/dom/promise/tests/file_promise_retval_tests.js b/dom/promise/tests/file_promise_retval_tests.js new file mode 100644 index 0000000000..37ede9e514 --- /dev/null +++ b/dom/promise/tests/file_promise_retval_tests.js @@ -0,0 +1,56 @@ +/* + * This file is meant to provide common infrastructure for several consumers. + * The consumer is expected to define the following things: + * + * 1) A verifyPromiseGlobal function which does whatever test the consumer + * wants. This function is passed a promise and the global whose + * TestFunctions was used to get the promise. + * 2) A expectedExceptionGlobal function which is handed the global whose + * TestFunctions was used to trigger the exception and should return the + * global the exception is expected to live in. + * 3) A subframe (frames[0]) which can be used as a second global for creating + * promises. + */ + +/* global verifyPromiseGlobal, expectedExceptionGlobal */ + +var label = "parent"; + +function testThrownException(global) { + var p = global.TestFunctions.throwToRejectPromise(); + verifyPromiseGlobal(p, global, "throwToRejectPromise return value"); + return p + .then(() => {}) + .catch(err => { + var expected = expectedExceptionGlobal(global); + is( + SpecialPowers.unwrap(SpecialPowers.Cu.getGlobalForObject(err)), + expected, + "Should have an exception object from the right global too" + ); + ok( + err instanceof expected.DOMException, + "Should have a DOMException here" + ); + is( + Object.getPrototypeOf(err), + expected.DOMException.prototype, + "Should have a DOMException from the right global" + ); + is(err.name, "InvalidStateError", "Should have the right DOMException"); + }); +} + +function runPromiseRetvalTests(finishFunc) { + Promise.resolve() + .then(testThrownException.bind(undefined, window)) + .then(testThrownException.bind(undefined, frames[0])) + .then(finishFunc) + .catch(function (e) { + ok( + false, + `Exception thrown: ${e}@${location.pathname}:${e.lineNumber}:${e.columnNumber}` + ); + finishFunc(); + }); +} diff --git a/dom/promise/tests/file_promise_xrays.html b/dom/promise/tests/file_promise_xrays.html new file mode 100644 index 0000000000..e08014a337 --- /dev/null +++ b/dom/promise/tests/file_promise_xrays.html @@ -0,0 +1,34 @@ + + + + diff --git a/dom/promise/tests/mochitest.toml b/dom/promise/tests/mochitest.toml new file mode 100644 index 0000000000..e7171b5ccf --- /dev/null +++ b/dom/promise/tests/mochitest.toml @@ -0,0 +1,44 @@ +[DEFAULT] +support-files = ["promise_uncatchable_exception.js"] + +["test_bug883683.html"] + +["test_promise.html"] + +["test_promise_and_timeout_ordering.html"] +support-files = ["file_promise_and_timeout_ordering.js"] + +["test_promise_and_timeout_ordering_workers.html"] +support-files = ["file_promise_and_timeout_ordering.js"] + +["test_promise_argument.html"] +support-files = ["file_promise_argument_tests.js"] +skip-if = ["!debug"] + +["test_promise_callback_retval.html"] +support-files = ["file_promise_argument_tests.js"] +skip-if = ["!debug"] + +["test_promise_retval.html"] +support-files = ["file_promise_retval_tests.js"] +skip-if = ["!debug"] + +["test_promise_uncatchable_exception.html"] +skip-if = ["!debug"] + +["test_promise_utils.html"] + +["test_resolve.html"] + +["test_resolver_return_value.html"] + +["test_species_getter.html"] + +["test_thenable_vs_promise_ordering.html"] + +["test_webassembly_compile.html"] +support-files = [ + "test_webassembly_compile_sample.wasm", + "test_webassembly_compile_worker.js", + "test_webassembly_compile_worker_terminate.js", +] diff --git a/dom/promise/tests/promise_uncatchable_exception.js b/dom/promise/tests/promise_uncatchable_exception.js new file mode 100644 index 0000000000..eafc9e5448 --- /dev/null +++ b/dom/promise/tests/promise_uncatchable_exception.js @@ -0,0 +1,11 @@ +/* global TestFunctions */ + +postMessage("Done", "*"); + +var p = new Promise(function (resolve, reject) { + TestFunctions.throwUncatchableException(); + ok(false, "Shouldn't get here!"); +}).catch(function (exception) { + ok(false, "Shouldn't get here!"); +}); +ok(false, "Shouldn't get here!"); diff --git a/dom/promise/tests/test_bug883683.html b/dom/promise/tests/test_bug883683.html new file mode 100644 index 0000000000..1b31e32330 --- /dev/null +++ b/dom/promise/tests/test_bug883683.html @@ -0,0 +1,41 @@ + + + + Promise - bug 883683 + + + + +

+ +
+
+
+ + diff --git a/dom/promise/tests/test_on_new_promise.html b/dom/promise/tests/test_on_new_promise.html new file mode 100644 index 0000000000..195707f1d7 --- /dev/null +++ b/dom/promise/tests/test_on_new_promise.html @@ -0,0 +1,45 @@ + + + + + + + Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise + + + + +

+ +
+  
+
+ + diff --git a/dom/promise/tests/test_on_promise_settled.html b/dom/promise/tests/test_on_promise_settled.html new file mode 100644 index 0000000000..b40475206b --- /dev/null +++ b/dom/promise/tests/test_on_promise_settled.html @@ -0,0 +1,53 @@ + + + + + + + Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise + + + + +

+ +
+  
+
+ + diff --git a/dom/promise/tests/test_on_promise_settled_duplicates.html b/dom/promise/tests/test_on_promise_settled_duplicates.html new file mode 100644 index 0000000000..e11f4eaa60 --- /dev/null +++ b/dom/promise/tests/test_on_promise_settled_duplicates.html @@ -0,0 +1,58 @@ + + + + + + + Test for interaction with SpiderMonkey's Debugger.prototype.onNewPromise + + + + +

+ +
+  
+
+ + diff --git a/dom/promise/tests/test_promise.html b/dom/promise/tests/test_promise.html new file mode 100644 index 0000000000..7c724daf51 --- /dev/null +++ b/dom/promise/tests/test_promise.html @@ -0,0 +1,844 @@ + + + + Basic Promise Test + + + + +

+ +
+
+
+ + diff --git a/dom/promise/tests/test_promise_and_timeout_ordering.html b/dom/promise/tests/test_promise_and_timeout_ordering.html new file mode 100644 index 0000000000..e92e928e75 --- /dev/null +++ b/dom/promise/tests/test_promise_and_timeout_ordering.html @@ -0,0 +1,16 @@ + + +Test for promise and timeout ordering + + +
+ + diff --git a/dom/promise/tests/test_promise_and_timeout_ordering_workers.html b/dom/promise/tests/test_promise_and_timeout_ordering_workers.html new file mode 100644 index 0000000000..85fd4c0019 --- /dev/null +++ b/dom/promise/tests/test_promise_and_timeout_ordering_workers.html @@ -0,0 +1,14 @@ + + +Test for promise and timeout ordering in workers + + +
+ diff --git a/dom/promise/tests/test_promise_argument.html b/dom/promise/tests/test_promise_argument.html new file mode 100644 index 0000000000..22343ef00f --- /dev/null +++ b/dom/promise/tests/test_promise_argument.html @@ -0,0 +1,49 @@ + + + + + + Test for Bug 1323324 + + + + + + +Mozilla Bug 1323324 +

+ +
+
+ + diff --git a/dom/promise/tests/test_promise_argument_xrays.html b/dom/promise/tests/test_promise_argument_xrays.html new file mode 100644 index 0000000000..f3a234cf6d --- /dev/null +++ b/dom/promise/tests/test_promise_argument_xrays.html @@ -0,0 +1,90 @@ + + + + + + Test for Bug 1233324 + + + + + +Mozilla Bug 1233324 +

+ + +
+
+
+
+ + diff --git a/dom/promise/tests/test_promise_callback_retval.html b/dom/promise/tests/test_promise_callback_retval.html new file mode 100644 index 0000000000..332c370a3e --- /dev/null +++ b/dom/promise/tests/test_promise_callback_retval.html @@ -0,0 +1,53 @@ + + + + + + Test for Bug 1323324 + + + + + + +Mozilla Bug 1323324 +

+ +
+
+ + diff --git a/dom/promise/tests/test_promise_job_with_bind_from_discarded_iframe.html b/dom/promise/tests/test_promise_job_with_bind_from_discarded_iframe.html new file mode 100644 index 0000000000..1cea250072 --- /dev/null +++ b/dom/promise/tests/test_promise_job_with_bind_from_discarded_iframe.html @@ -0,0 +1,63 @@ + + + + + + Test for Bug 1723124. + + + + + +Mozilla Bug 1723124. + + + +
+
+
+ + diff --git a/dom/promise/tests/test_promise_retval.html b/dom/promise/tests/test_promise_retval.html new file mode 100644 index 0000000000..e425b8e203 --- /dev/null +++ b/dom/promise/tests/test_promise_retval.html @@ -0,0 +1,51 @@ + + + + + + Test for Bug 1436276. + + + + + + +Mozilla Bug 1323324 +

+ +
+
+ + diff --git a/dom/promise/tests/test_promise_retval_xrays.html b/dom/promise/tests/test_promise_retval_xrays.html new file mode 100644 index 0000000000..1270e3a3bb --- /dev/null +++ b/dom/promise/tests/test_promise_retval_xrays.html @@ -0,0 +1,94 @@ + + + + + + Test for Bug 1436276. + + + + + +Mozilla Bug 1436276. +

+ + +
+
+
+
+ + diff --git a/dom/promise/tests/test_promise_uncatchable_exception.html b/dom/promise/tests/test_promise_uncatchable_exception.html new file mode 100644 index 0000000000..2bb6f1fe17 --- /dev/null +++ b/dom/promise/tests/test_promise_uncatchable_exception.html @@ -0,0 +1,35 @@ + + + + Promise - uncatchable exceptions + + + + +

+ +
+
+
+ + diff --git a/dom/promise/tests/test_promise_utils.html b/dom/promise/tests/test_promise_utils.html new file mode 100644 index 0000000000..b20d909351 --- /dev/null +++ b/dom/promise/tests/test_promise_utils.html @@ -0,0 +1,313 @@ + + + + Test for Promise.all, Promise.race + + + + +

+ +
+
+
+ + diff --git a/dom/promise/tests/test_promise_xrays.html b/dom/promise/tests/test_promise_xrays.html new file mode 100644 index 0000000000..8559dbb2a4 --- /dev/null +++ b/dom/promise/tests/test_promise_xrays.html @@ -0,0 +1,365 @@ + + + + + + Test for Bug 1170760 + + + + + +Mozilla Bug 1170760 +

+ + +
+
+
+ + diff --git a/dom/promise/tests/test_resolve.html b/dom/promise/tests/test_resolve.html new file mode 100644 index 0000000000..7e4745b47a --- /dev/null +++ b/dom/promise/tests/test_resolve.html @@ -0,0 +1,66 @@ + + + + Promise.resolve(anything) Test + + + + +

+ +
+
+
+ + diff --git a/dom/promise/tests/test_resolver_return_value.html b/dom/promise/tests/test_resolver_return_value.html new file mode 100644 index 0000000000..82e8793824 --- /dev/null +++ b/dom/promise/tests/test_resolver_return_value.html @@ -0,0 +1,40 @@ + + + + + + Test for Bug 1120235 + + + + + +Mozilla Bug 1120235 +

+ +
+
+ + diff --git a/dom/promise/tests/test_species_getter.html b/dom/promise/tests/test_species_getter.html new file mode 100644 index 0000000000..23cb7d8ae0 --- /dev/null +++ b/dom/promise/tests/test_species_getter.html @@ -0,0 +1,25 @@ + + +Test for ... + + +
+ diff --git a/dom/promise/tests/test_thenable_vs_promise_ordering.html b/dom/promise/tests/test_thenable_vs_promise_ordering.html new file mode 100644 index 0000000000..a537490b16 --- /dev/null +++ b/dom/promise/tests/test_thenable_vs_promise_ordering.html @@ -0,0 +1,29 @@ + + +Test for promise resolution ordering with thenables and promises + + +
+ diff --git a/dom/promise/tests/test_webassembly_compile.html b/dom/promise/tests/test_webassembly_compile.html new file mode 100644 index 0000000000..351f0f4ae4 --- /dev/null +++ b/dom/promise/tests/test_webassembly_compile.html @@ -0,0 +1,446 @@ + + + + WebAssembly.compile Test + + + + + + + diff --git a/dom/promise/tests/test_webassembly_compile_sample.wasm b/dom/promise/tests/test_webassembly_compile_sample.wasm new file mode 100644 index 0000000000..787e19a5df Binary files /dev/null and b/dom/promise/tests/test_webassembly_compile_sample.wasm differ diff --git a/dom/promise/tests/test_webassembly_compile_worker.js b/dom/promise/tests/test_webassembly_compile_worker.js new file mode 100644 index 0000000000..90c3551137 --- /dev/null +++ b/dom/promise/tests/test_webassembly_compile_worker.js @@ -0,0 +1,55 @@ +const sampleURL = "test_webassembly_compile_sample.wasm"; +const sampleExportName = "run"; +const sampleResult = 1275; + +/* eslint-disable no-throw-literal */ + +function checkSampleModule(m) { + if (!(m instanceof WebAssembly.Module)) { + throw "not a module"; + } + var i = new WebAssembly.Instance(m); + if (!(i instanceof WebAssembly.Instance)) { + throw "not an instance"; + } + if (i.exports[sampleExportName]() !== sampleResult) { + throw "wrong result"; + } +} + +function checkSampleInstance(i) { + if (!(i instanceof WebAssembly.Instance)) { + throw "not an instance"; + } + if (i.exports[sampleExportName]() !== sampleResult) { + throw "wrong result"; + } +} + +const initObj = { headers: { "Content-Type": "application/wasm" } }; + +onmessage = e => { + WebAssembly.compile(e.data) + .then(m => checkSampleModule(m)) + .then(() => WebAssembly.instantiate(e.data)) + .then(({ module, instance }) => { + checkSampleModule(module); + checkSampleInstance(instance); + }) + .then(() => WebAssembly.compileStreaming(new Response(e.data, initObj))) + .then(m => checkSampleModule(m)) + .then(() => WebAssembly.instantiateStreaming(new Response(e.data, initObj))) + .then(({ module, instance }) => { + checkSampleModule(module); + checkSampleInstance(instance); + }) + .then(() => WebAssembly.compileStreaming(fetch(sampleURL))) + .then(m => checkSampleModule(m)) + .then(() => WebAssembly.instantiateStreaming(fetch(sampleURL))) + .then(({ module, instance }) => { + checkSampleModule(module); + checkSampleInstance(instance); + }) + .then(() => postMessage("ok")) + .catch(err => postMessage("fail: " + err)); +}; diff --git a/dom/promise/tests/test_webassembly_compile_worker_terminate.js b/dom/promise/tests/test_webassembly_compile_worker_terminate.js new file mode 100644 index 0000000000..5b96c9034b --- /dev/null +++ b/dom/promise/tests/test_webassembly_compile_worker_terminate.js @@ -0,0 +1,13 @@ +const sampleURL = "test_webassembly_compile_sample.wasm"; + +function spawnWork() { + const N = 50; + var arr = []; + for (var i = 0; i < N; i++) { + arr.push(WebAssembly.compileStreaming(fetch(sampleURL))); + } + Promise.all(arr).then(spawnWork); +} + +spawnWork(); +postMessage("ok"); diff --git a/dom/promise/tests/unit/test_monitor_uncaught.js b/dom/promise/tests/unit/test_monitor_uncaught.js new file mode 100644 index 0000000000..afa328cb97 --- /dev/null +++ b/dom/promise/tests/unit/test_monitor_uncaught.js @@ -0,0 +1,322 @@ +/* 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/. */ + +"use strict"; + +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); +const { PromiseTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PromiseTestUtils.sys.mjs" +); + +// Prevent test failures due to the unhandled rejections in this test file. +PromiseTestUtils.disableUncaughtRejectionObserverForSelfTest(); + +add_task(async function test_globals() { + Assert.notEqual( + PromiseDebugging, + undefined, + "PromiseDebugging is available." + ); +}); + +add_task(async function test_promiseID() { + let p1 = new Promise(resolve => {}); + let p2 = new Promise(resolve => {}); + let p3 = p2.catch(null); + let promise = [p1, p2, p3]; + + let identifiers = promise.map(PromiseDebugging.getPromiseID); + info("Identifiers: " + JSON.stringify(identifiers)); + let idSet = new Set(identifiers); + Assert.equal( + idSet.size, + identifiers.length, + "PromiseDebugging.getPromiseID returns a distinct id per promise" + ); + + let identifiers2 = promise.map(PromiseDebugging.getPromiseID); + Assert.equal( + JSON.stringify(identifiers), + JSON.stringify(identifiers2), + "Successive calls to PromiseDebugging.getPromiseID return the same id for the same promise" + ); +}); + +add_task(async function test_observe_uncaught() { + // The names of Promise instances + let names = new Map(); + + // The results for UncaughtPromiseObserver callbacks. + let CallbackResults = function (name) { + this.name = name; + this.expected = new Set(); + this.observed = new Set(); + this.blocker = new Promise(resolve => (this.resolve = resolve)); + }; + CallbackResults.prototype = { + observe(promise) { + info(this.name + " observing Promise " + names.get(promise)); + Assert.equal( + PromiseDebugging.getState(promise).state, + "rejected", + this.name + " observed a rejected Promise" + ); + if (!this.expected.has(promise)) { + Assert.ok( + false, + this.name + + " observed a Promise that it expected to observe, " + + names.get(promise) + + " (" + + PromiseDebugging.getPromiseID(promise) + + ", " + + PromiseDebugging.getAllocationStack(promise) + + ")" + ); + } + Assert.ok( + this.expected.delete(promise), + this.name + + " observed a Promise that it expected to observe, " + + names.get(promise) + + " (" + + PromiseDebugging.getPromiseID(promise) + + ")" + ); + Assert.ok( + !this.observed.has(promise), + this.name + " observed a Promise that it has not observed yet" + ); + this.observed.add(promise); + if (this.expected.size == 0) { + this.resolve(); + } else { + info( + this.name + + " is still waiting for " + + this.expected.size + + " observations:" + ); + info( + JSON.stringify(Array.from(this.expected.values(), x => names.get(x))) + ); + } + }, + }; + + let onLeftUncaught = new CallbackResults("onLeftUncaught"); + let onConsumed = new CallbackResults("onConsumed"); + + let observer = { + onLeftUncaught(promise, data) { + onLeftUncaught.observe(promise); + }, + onConsumed(promise) { + onConsumed.observe(promise); + }, + }; + + let resolveLater = function (delay = 20) { + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + return new Promise((resolve, reject) => setTimeout(resolve, delay)); + }; + let rejectLater = function (delay = 20) { + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + return new Promise((resolve, reject) => setTimeout(reject, delay)); + }; + let makeSamples = function* () { + yield { + promise: Promise.resolve(0), + name: "Promise.resolve", + }; + yield { + promise: Promise.resolve(resolve => resolve(0)), + name: "Resolution callback", + }; + yield { + promise: Promise.resolve(0).catch(null), + name: "`catch(null)`", + }; + yield { + promise: Promise.reject(0).catch(() => {}), + name: "Reject and catch immediately", + }; + yield { + promise: resolveLater(), + name: "Resolve later", + }; + yield { + promise: Promise.reject("Simple rejection"), + leftUncaught: true, + consumed: false, + name: "Promise.reject", + }; + + // Reject a promise now, consume it later. + let p = Promise.reject("Reject now, consume later"); + + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout( + () => + p.catch(() => { + info("Consumed promise"); + }), + 200 + ); + yield { + promise: p, + leftUncaught: true, + consumed: true, + name: "Reject now, consume later", + }; + + yield { + promise: Promise.all([Promise.resolve("Promise.all"), rejectLater()]), + leftUncaught: true, + name: "Rejecting through Promise.all", + }; + yield { + promise: Promise.race([resolveLater(500), Promise.reject()]), + leftUncaught: true, // The rejection wins the race. + name: "Rejecting through Promise.race", + }; + yield { + promise: Promise.race([Promise.resolve(), rejectLater(500)]), + leftUncaught: false, // The resolution wins the race. + name: "Resolving through Promise.race", + }; + + let boom = new Error("`throw` in the constructor"); + yield { + promise: new Promise(() => { + throw boom; + }), + leftUncaught: true, + name: "Throwing in the constructor", + }; + + let rejection = Promise.reject("`reject` during resolution"); + yield { + promise: rejection, + leftUncaught: false, + consumed: false, // `rejection` is consumed immediately (see below) + name: "Promise.reject, again", + }; + + yield { + promise: new Promise(resolve => resolve(rejection)), + leftUncaught: true, + consumed: false, + name: "Resolving with a rejected promise", + }; + + yield { + promise: Promise.resolve(0).then(() => rejection), + leftUncaught: true, + consumed: false, + name: "Returning a rejected promise from success handler", + }; + + yield { + promise: Promise.resolve(0).then(() => { + throw new Error(); + }), + leftUncaught: true, + consumed: false, + name: "Throwing during the call to the success callback", + }; + }; + let samples = []; + for (let s of makeSamples()) { + samples.push(s); + info( + "Promise '" + + s.name + + "' has id " + + PromiseDebugging.getPromiseID(s.promise) + ); + } + + PromiseDebugging.addUncaughtRejectionObserver(observer); + + for (let s of samples) { + names.set(s.promise, s.name); + if (s.leftUncaught || false) { + onLeftUncaught.expected.add(s.promise); + } + if (s.consumed || false) { + onConsumed.expected.add(s.promise); + } + } + + info("Test setup, waiting for callbacks."); + await onLeftUncaught.blocker; + + info("All calls to onLeftUncaught are complete."); + if (onConsumed.expected.size != 0) { + info("onConsumed is still waiting for the following Promise:"); + info( + JSON.stringify( + Array.from(onConsumed.expected.values(), x => names.get(x)) + ) + ); + await onConsumed.blocker; + } + + info("All calls to onConsumed are complete."); + let removed = PromiseDebugging.removeUncaughtRejectionObserver(observer); + Assert.ok(removed, "removeUncaughtRejectionObserver succeeded"); + removed = PromiseDebugging.removeUncaughtRejectionObserver(observer); + Assert.ok( + !removed, + "second call to removeUncaughtRejectionObserver didn't remove anything" + ); +}); + +add_task(async function test_uninstall_observer() { + let Observer = function () { + this.blocker = new Promise(resolve => (this.resolve = resolve)); + this.active = true; + }; + Observer.prototype = { + set active(x) { + this._active = x; + if (x) { + PromiseDebugging.addUncaughtRejectionObserver(this); + } else { + PromiseDebugging.removeUncaughtRejectionObserver(this); + } + }, + onLeftUncaught() { + Assert.ok(this._active, "This observer is active."); + this.resolve(); + }, + onConsumed() { + Assert.ok(false, "We should not consume any Promise."); + }, + }; + + info("Adding an observer."); + let deactivate = new Observer(); + Promise.reject("I am an uncaught rejection."); + await deactivate.blocker; + Assert.ok(true, "The observer has observed an uncaught Promise."); + deactivate.active = false; + info( + "Removing the observer, it should not observe any further uncaught Promise." + ); + + info( + "Rejecting a Promise and waiting a little to give a chance to observers." + ); + let wait = new Observer(); + Promise.reject("I am another uncaught rejection."); + await wait.blocker; + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(resolve => setTimeout(resolve, 100)); + // Normally, `deactivate` should not be notified of the uncaught rejection. + wait.active = false; +}); diff --git a/dom/promise/tests/unit/test_promise_job_across_sandbox.js b/dom/promise/tests/unit/test_promise_job_across_sandbox.js new file mode 100644 index 0000000000..ff1d1575e3 --- /dev/null +++ b/dom/promise/tests/unit/test_promise_job_across_sandbox.js @@ -0,0 +1,221 @@ +function createSandbox() { + const uri = Services.io.newURI("https://example.com"); + const principal = Services.scriptSecurityManager.createContentPrincipal( + uri, + {} + ); + return new Cu.Sandbox(principal, {}); +} + +add_task(async function testReactionJob() { + const sandbox = createSandbox(); + + sandbox.eval(` +var testPromise = Promise.resolve(10); +`); + + // Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm + // on wrapped `resolve` in sandbox realm, and it fails to unwrap the security + // wrapper. The reaction job should be created with sandbox realm. + const p = new Promise(resolve => { + sandbox.resolve = resolve; + + sandbox.eval(` +testPromise.then(resolve); +`); + }); + + const result = await p; + + equal(result, 10); +}); + +add_task(async function testReactionJobNuked() { + const sandbox = createSandbox(); + + sandbox.eval(` +var testPromise = Promise.resolve(10); +`); + + // Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm + // on wrapped `resolve` in sandbox realm, and it fails to unwrap the security + // wrapper. The reaction job should be created with sandbox realm. + const p1 = new Promise(resolve => { + sandbox.resolve = resolve; + + sandbox.eval(` +testPromise.then(resolve); +`); + + // Given the reaction job is created with the sandbox realm, nuking the + // sandbox prevents the job gets executed. + Cu.nukeSandbox(sandbox); + }); + + const p2 = Promise.resolve(11); + + // Given the p1 doesn't get resolved, p2 should win. + const result = await Promise.race([p1, p2]); + + equal(result, 11); +}); + +add_task(async function testReactionJobWithXray() { + const sandbox = createSandbox(); + + sandbox.eval(` +var testPromise = Promise.resolve(10); +`); + + // Calling `Promise.prototype.then` from privileged realm via Xray uses + // privileged `Promise.prototype.then` function, and GetFunctionRealm + // performed there successfully gets top-level realm. The reaction job + // should be created with top-level realm. + const result = await new Promise(resolve => { + sandbox.testPromise.then(resolve); + + // Given the reaction job is created with the top-level realm, nuking the + // sandbox doesn't affect the reaction job. + Cu.nukeSandbox(sandbox); + }); + + equal(result, 10); +}); + +add_task(async function testBoundReactionJob() { + const sandbox = createSandbox(); + + sandbox.eval(` +var resolve = undefined; +var callbackPromise = new Promise(r => { resolve = r; }); +var callback = function (v) { resolve(v + 1); }; +`); + + // Create a bound function where its realm is privileged realm, and + // its target is from sandbox realm. + sandbox.bound_callback = Function.prototype.bind.call( + sandbox.callback, + sandbox + ); + + // Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm + // and it fails. The reaction job should be created with sandbox realm. + sandbox.eval(` +Promise.resolve(10).then(bound_callback); +`); + + const result = await sandbox.callbackPromise; + equal(result, 11); +}); + +add_task(async function testThenableJob() { + const sandbox = createSandbox(); + + const p = new Promise(resolve => { + // Create a bound function where its realm is privileged realm, and + // its target is from sandbox realm. + sandbox.then = function (onFulfilled, onRejected) { + resolve(10); + }; + }); + + // Creating a promise thenable job in the following `Promise.resolve` performs + // GetFunctionRealm on the bound thenable.then and fails. The reaction job + // should be created with sandbox realm. + sandbox.eval(` +var thenable = { + then: then, +}; + +Promise.resolve(thenable); +`); + + const result = await p; + equal(result, 10); +}); + +add_task(async function testThenableJobNuked() { + const sandbox = createSandbox(); + + let called = false; + sandbox.then = function (onFulfilled, onRejected) { + called = true; + }; + + // Creating a promise thenable job in the following `Promise.resolve` performs + // GetFunctionRealm on the bound thenable.then and fails. The reaction job + // should be created with sandbox realm. + sandbox.eval(` +var thenable = { + then: then, +}; + +Promise.resolve(thenable); +`); + + Cu.nukeSandbox(sandbox); + + // Drain the job queue, to make sure we hit dead object error inside the + // thenable job. + await Promise.resolve(10); + + equal( + Services.console.getMessageArray().find(x => { + return x.toString().includes("can't access dead object"); + }) !== undefined, + true + ); + equal(called, false); +}); + +add_task(async function testThenableJobAccessError() { + const sandbox = createSandbox(); + + let accessed = false; + sandbox.thenable = { + get then() { + accessed = true; + }, + }; + + // The following operation silently fails when accessing `then` property. + sandbox.eval(` +var x = typeof thenable.then; + +Promise.resolve(thenable); +`); + + equal(accessed, false); +}); + +add_task(async function testBoundThenableJob() { + const sandbox = createSandbox(); + + sandbox.eval(` +var resolve = undefined; +var callbackPromise = new Promise(r => { resolve = r; }); +var callback = function (v) { resolve(v + 1); }; + +var then = function(onFulfilled, onRejected) { + onFulfilled(10); +}; +`); + + // Create a bound function where its realm is privileged realm, and + // its target is from sandbox realm. + sandbox.bound_then = Function.prototype.bind.call(sandbox.then, sandbox); + + // Creating a promise thenable job in the following `Promise.resolve` performs + // GetFunctionRealm on the bound thenable.then and fails. The reaction job + // should be created with sandbox realm. + sandbox.eval(` +var thenable = { + then: bound_then, +}; + +Promise.resolve(thenable).then(callback); +`); + + const result = await sandbox.callbackPromise; + equal(result, 11); +}); diff --git a/dom/promise/tests/unit/test_promise_unhandled_rejection.js b/dom/promise/tests/unit/test_promise_unhandled_rejection.js new file mode 100644 index 0000000000..68471569ec --- /dev/null +++ b/dom/promise/tests/unit/test_promise_unhandled_rejection.js @@ -0,0 +1,139 @@ +"use strict"; + +// Tests that unhandled promise rejections generate the appropriate +// console messages. + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); +const { PromiseTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PromiseTestUtils.sys.mjs" +); + +PromiseTestUtils.expectUncaughtRejection(/could not be cloned/); +PromiseTestUtils.expectUncaughtRejection(/An exception was thrown/); +PromiseTestUtils.expectUncaughtRejection(/Bleah/); + +const filename = "resource://foo/Bar.jsm"; + +async function getSandboxMessages(sandbox, code) { + let { messages } = await AddonTestUtils.promiseConsoleOutput(async () => { + Cu.evalInSandbox(code, sandbox, null, filename, 1); + + // We need two trips through the event loop for this error to be reported. + await new Promise(executeSoon); + await new Promise(executeSoon); + }); + + // xpcshell tests on OS-X sometimes include an extra warning, which we + // unfortunately need to ignore: + return messages.filter( + msg => + !msg.message.includes( + "No chrome package registered for chrome://branding/locale/brand.properties" + ) + ); +} + +add_task(async function test_unhandled_dom_exception() { + let sandbox = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal()); + sandbox.StructuredCloneHolder = StructuredCloneHolder; + + let messages = await getSandboxMessages( + sandbox, + `new Promise(() => { + new StructuredCloneHolder("", "", () => {}); + });` + ); + + equal(messages.length, 1, "Got one console message"); + + let [msg] = messages; + ok(msg instanceof Ci.nsIScriptError, "Message is a script error"); + equal(msg.sourceName, filename, "Got expected filename"); + equal(msg.lineNumber, 2, "Got expected line number"); + equal( + msg.errorMessage, + "DataCloneError: Function object could not be cloned.", + "Got expected error message" + ); +}); + +add_task(async function test_unhandled_dom_exception_wrapped() { + let sandbox = Cu.Sandbox( + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "http://example.com/" + ) + ); + Cu.exportFunction( + function frick() { + throw new Components.Exception( + "Bleah.", + Cr.NS_ERROR_FAILURE, + Components.stack.caller + ); + }, + sandbox, + { defineAs: "frick" } + ); + + let messages = await getSandboxMessages( + sandbox, + `new Promise(() => { + frick(); + });` + ); + + equal(messages.length, 2, "Got two console messages"); + + let [msg1, msg2] = messages; + ok(msg1 instanceof Ci.nsIScriptError, "Message is a script error"); + equal(msg1.sourceName, filename, "Got expected filename"); + equal(msg1.lineNumber, 2, "Got expected line number"); + equal( + msg1.errorMessage, + "NS_ERROR_FAILURE: Bleah.", + "Got expected error message" + ); + + ok(msg2 instanceof Ci.nsIScriptError, "Message is a script error"); + equal(msg2.sourceName, filename, "Got expected filename"); + equal(msg2.lineNumber, 2, "Got expected line number"); + equal( + msg2.errorMessage, + "InvalidStateError: An exception was thrown", + "Got expected error message" + ); +}); + +add_task(async function test_unhandled_dom_exception_from_sandbox() { + let sandbox = Cu.Sandbox( + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "http://example.com/" + ), + { wantGlobalProperties: ["DOMException"] } + ); + let ctor = Cu.evalInSandbox("DOMException", sandbox); + Cu.exportFunction( + function frick() { + throw new ctor("Bleah."); + }, + sandbox, + { defineAs: "frick" } + ); + + let messages = await getSandboxMessages( + sandbox, + `new Promise(() => { + frick(); + });` + ); + + equal(messages.length, 1, "Got one console messages"); + + let [msg] = messages; + ok(msg instanceof Ci.nsIScriptError, "Message is a script error"); + equal(msg.sourceName, filename, "Got expected filename"); + equal(msg.lineNumber, 2, "Got expected line number"); + equal(msg.errorMessage, "Error: Bleah.", "Got expected error message"); +}); diff --git a/dom/promise/tests/unit/xpcshell.toml b/dom/promise/tests/unit/xpcshell.toml new file mode 100644 index 0000000000..f54bbc4c9e --- /dev/null +++ b/dom/promise/tests/unit/xpcshell.toml @@ -0,0 +1,8 @@ +[DEFAULT] +head = "" + +["test_monitor_uncaught.js"] + +["test_promise_job_across_sandbox.js"] + +["test_promise_unhandled_rejection.js"] -- cgit v1.2.3