From 0aa48328339cae84252f3f8ba5b036fb9fc8e6af Mon Sep 17 00:00:00 2001 From: Scott Haseley Date: Wed, 21 Aug 2024 17:06:24 +0000 Subject: [PATCH] Bug 1913535 [wpt PR 47653] - AbortSignal: Propagate aborted state to dependent signals before firing events, a=testonly Automatic update from web-platform-tests AbortSignal: Propagate aborted state to dependent signals before firing events The implementation and spec of dependent signals assumes and asserts that a dependent signal is aborted if any of its sources have been aborted. But this property does not hold during the abort process, since intermediate states of abort propagation can be observed. For example, calling AbortSignal.any([signal]) in an "abort" event listener for one of signal's sources is a way to observe intermediate state, since the "abort" event fires before the source signal's dependents are updated. To fix this, this CL decouples setting the abort state and reacting to abort event: 1. Mark the source signal as aborted 2. Propagate the aborted state to any dependent signals 3. Run abort steps (run algorithms, fire events) for the source signal 4. Run abort steps for each of the dependent signals PR: https://github.com/whatwg/dom/pull/1295 Change-Id: I65a97eb46b01a0071d661e945f64c90e33954088 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5664649 Commit-Queue: Scott Haseley Reviewed-by: Joey Arhar Cr-Commit-Position: refs/heads/main@{#1344364} -- wpt-commits: 07a9d09a8fbe95c2c2b439b6a88ef2499543133d wpt-pr: 47653 --- .../abort/resources/abort-signal-any-tests.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/testing/web-platform/tests/dom/abort/resources/abort-signal-any-tests.js b/testing/web-platform/tests/dom/abort/resources/abort-signal-any-tests.js index 66e4141eaccb0..929ee8a2e61ab 100644 --- a/testing/web-platform/tests/dom/abort/resources/abort-signal-any-tests.js +++ b/testing/web-platform/tests/dom/abort/resources/abort-signal-any-tests.js @@ -182,4 +182,43 @@ function abortSignalAnyTests(signalInterface, controllerInterface) { controller.abort(); assert_equals(result, "01234"); }, `Abort events for ${desc} signals fire in the right order ${suffix}`); + + test(t => { + const controller = new controllerInterface(); + const signal1 = signalInterface.any([controller.signal]); + const signal2 = signalInterface.any([signal1]); + let eventFired = false; + + controller.signal.addEventListener('abort', () => { + const signal3 = signalInterface.any([signal2]); + assert_true(controller.signal.aborted); + assert_true(signal1.aborted); + assert_true(signal2.aborted); + assert_true(signal3.aborted); + eventFired = true; + }); + + controller.abort(); + assert_true(eventFired, "event fired"); + }, `Dependent signals for ${desc} are marked aborted before abort events fire ${suffix}`); + + test(t => { + const controller1 = new controllerInterface(); + const controller2 = new controllerInterface(); + const signal = signalInterface.any([controller1.signal, controller2.signal]); + let count = 0; + + controller1.signal.addEventListener('abort', () => { + controller2.abort("reason 2"); + }); + + signal.addEventListener('abort', () => { + count++; + }); + + controller1.abort("reason 1"); + assert_equals(count, 1); + assert_true(signal.aborted); + assert_equals(signal.reason, "reason 1"); + }, `Dependent signals for ${desc} are aborted correctly for reentrant aborts ${suffix}`); }