From 18b618a8a986d10c3bba8640d328868d1cb3e687 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 21 Aug 2024 17:29:28 +0530 Subject: [PATCH] fix(ext/web): propagate aborted state to dependent signals before firing events Ref https://github.com/whatwg/dom/pull/1295 Fixes https://github.com/denoland/deno/issues/25118 --- ext/web/03_abort_signal.js | 34 +++++++++++++++++++++++------ tests/unit/abort_controller_test.ts | 19 ++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/ext/web/03_abort_signal.js b/ext/web/03_abort_signal.js index 81844d53fce019..404129aba0f74b 100644 --- a/ext/web/03_abort_signal.js +++ b/ext/web/03_abort_signal.js @@ -69,6 +69,7 @@ class WeakRefSet { } const add = Symbol("[[add]]"); +const setAbortReason = Symbol("[[setAbortReason]]"); const signalAbort = Symbol("[[signalAbort]]"); const remove = Symbol("[[remove]]"); const abortReason = Symbol("[[abortReason]]"); @@ -148,7 +149,22 @@ class AbortSignal extends EventTarget { if (this.aborted) { return; } - this[abortReason] = reason; + + this[setAbortReason](reason); + + const dependentSignalsToAbort = []; + if (this[dependentSignals] !== null) { + const dependentSignalArray = this[dependentSignals].toArray(); + for (let i = 0; i < dependentSignalArray.length; ++i) { + const dependentSignal = dependentSignalArray[i]; + + if (!dependentSignal.aborted) { + dependentSignal[setAbortReason](reason); + dependentSignalsToAbort.push(dependentSignal); + } + } + } + const algos = this[abortAlgos]; this[abortAlgos] = null; @@ -163,15 +179,19 @@ class AbortSignal extends EventTarget { } } - if (this[dependentSignals] !== null) { - const dependentSignalArray = this[dependentSignals].toArray(); - for (let i = 0; i < dependentSignalArray.length; ++i) { - const dependentSignal = dependentSignalArray[i]; - dependentSignal[signalAbort](reason); - } + for (let i = 0; i < dependentSignalsToAbort.length; ++i) { + const dependentSignal = dependentSignalsToAbort[i]; + dependentSignal[signalAbort](reason); } } + [setAbortReason](reason) { + if (this.aborted) { + return; + } + this[abortReason] = reason; + } + [remove](algorithm) { this[abortAlgos] && SetPrototypeDelete(this[abortAlgos], algorithm); } diff --git a/tests/unit/abort_controller_test.ts b/tests/unit/abort_controller_test.ts index 60ea6aa24552e4..0062313c16da3a 100644 --- a/tests/unit/abort_controller_test.ts +++ b/tests/unit/abort_controller_test.ts @@ -62,3 +62,22 @@ Deno.test(function abortReason() { assertEquals(signal.aborted, true); assertEquals(signal.reason, "hey!"); }); + +Deno.test(function dependentSignalsAborted() { + const controller = new AbortController(); + const signal1 = AbortSignal.any([controller.signal]); + const signal2 = AbortSignal.any([signal1]); + let eventFired = false; + + controller.signal.addEventListener("abort", () => { + const signal3 = AbortSignal.any([signal2]); + assert(controller.signal.aborted); + assert(signal1.aborted); + assert(signal2.aborted); + assert(signal3.aborted); + eventFired = true; + }); + + controller.abort(); + assert(eventFired, "event fired"); +});