diff --git a/payment-request/rejects_if_not_active-manual.https.html b/payment-request/rejects_if_not_active-manual.https.html index d03032d0498fc4..8e214cb6094a25 100644 --- a/payment-request/rejects_if_not_active-manual.https.html +++ b/payment-request/rejects_if_not_active-manual.https.html @@ -61,6 +61,8 @@ iframe, "/payment-request/resources/page1.html" ); + // Save the DOMException of page1.html before navigating away. + const frameDOMException1 = iframe.contentWindow.DOMException; // We navigate the iframe again, putting request1's document into an inactive state. const request2 = await getLoadedPaymentRequest( iframe, @@ -71,6 +73,7 @@ await promise_rejects_dom( t, "AbortError", + frameDOMException1, request1.show(), "Inactive document, so must throw AbortError" ); @@ -80,6 +83,7 @@ await promise_rejects_dom( t, "InvalidStateError", + iframe.contentWindow.DOMException, request2.show(), "Abort already called, so InvalidStateError" ); @@ -112,6 +116,8 @@ innerIframe, "/payment-request/resources/page2.html" ); + // Save DOMException from innerIframe before navigating away. + const innerIframeDOMException = innerIframe.contentWindow.DOMException; // Navigate the outer iframe to a new location. // Wait for the load event to fire. @@ -128,6 +134,7 @@ await promise_rejects_dom( t, "AbortError", + innerIframeDOMException, showPromise, "Active, but not fully active, so must throw AbortError" ); diff --git a/resources/testharness.js b/resources/testharness.js index 0ec232c1d27d79..4089c20c061c0e 100644 --- a/resources/testharness.js +++ b/resources/testharness.js @@ -638,11 +638,37 @@ policies and contribution forms [3]. }); } + /** + * Make a copy of a Promise in the current realm. + * + * @param {Promise} promise the given promise that may be from a different + * realm + * @returns {Promise} + * + * An arbitrary promise provided by the caller may have originated in + * another frame that have since navigated away, rendering the frame's + * document inactive. Such a promise cannot be used with `await` or + * Promise.resolve(), as microtasks associated with it may be prevented + * from being run. See https://github.com/whatwg/html/issues/5319 for a + * particular case. + * + * In functions we define here, there is an expectation from the caller + * that the promise is from the current realm, that can always be used with + * `await`, etc. We therefore create a new promise in this realm that + * inherit the value and status from the given promise. + */ + + function bring_promise_to_current_realm(promise) { + return new Promise(promise.then.bind(promise)); + } + function promise_rejects_js(test, constructor, promise, description) { - return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) { - assert_throws_js_impl(constructor, function() { throw e }, - description, "promise_rejects_js"); - }); + return bring_promise_to_current_realm(promise) + .then(test.unreached_func("Should have rejected: " + description)) + .catch(function(e) { + assert_throws_js_impl(constructor, function() { throw e }, + description, "promise_rejects_js"); + }); } /** @@ -678,17 +704,21 @@ policies and contribution forms [3]. assert(maybeDescription === undefined, "Too many args pased to no-constructor version of promise_rejects_dom"); } - return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) { - assert_throws_dom_impl(type, function() { throw e }, description, - "promise_rejects_dom", constructor); - }); + return bring_promise_to_current_realm(promise) + .then(test.unreached_func("Should have rejected: " + description)) + .catch(function(e) { + assert_throws_dom_impl(type, function() { throw e }, description, + "promise_rejects_dom", constructor); + }); } function promise_rejects_exactly(test, exception, promise, description) { - return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) { - assert_throws_exactly_impl(exception, function() { throw e }, - description, "promise_rejects_exactly"); - }); + return bring_promise_to_current_realm(promise) + .then(test.unreached_func("Should have rejected: " + description)) + .catch(function(e) { + assert_throws_exactly_impl(exception, function() { throw e }, + description, "promise_rejects_exactly"); + }); } /** diff --git a/wake-lock/wakelock-active-document.https.window.js b/wake-lock/wakelock-active-document.https.window.js index 53f9fb56fa8d77..53745ea3c60e73 100644 --- a/wake-lock/wakelock-active-document.https.window.js +++ b/wake-lock/wakelock-active-document.https.window.js @@ -20,6 +20,8 @@ promise_test(async t => { iframe, "/wake-lock/resources/page1.html" ); + // Save the DOMException of page1.html before navigating away. + const frameDOMException1 = iframe.contentWindow.DOMException; // We navigate the iframe again, putting wakeLock1's document into an inactive state. const wakeLock2 = await getWakeLockObject( iframe, @@ -30,6 +32,7 @@ promise_test(async t => { await promise_rejects_dom( t, "NotAllowedError", + frameDOMException1, wakeLock1.request('screen'), "Inactive document, so must throw NotAllowedError" ); @@ -58,6 +61,8 @@ promise_test(async t => { innerIframe, "/wake-lock/resources/page2.html" ); + // Save DOMException from innerIframe before navigating away. + const innerIframeDOMException = innerIframe.contentWindow.DOMException; // Navigate the outer iframe to a new location. // Wait for the load event to fire. @@ -73,6 +78,7 @@ promise_test(async t => { await promise_rejects_dom( t, "NotAllowedError", + innerIframeDOMException, wakeLock.request('screen'), "Active, but not fully active, so must throw NotAllowedError" );