diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any-expected.txt index 25877db979fa2..3d4e6c4d04d65 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any-expected.txt @@ -9,4 +9,6 @@ PASS AbortSignal.any() signals are composable (using AbortController) PASS AbortSignal.any() works with signals returned by AbortSignal.timeout() (using AbortController) PASS AbortSignal.any() works with intermediate signals (using AbortController) PASS Abort events for AbortSignal.any() signals fire in the right order (using AbortController) +PASS Dependent signals for AbortSignal.any() are marked aborted before abort events fire (using AbortController) +PASS Dependent signals for AbortSignal.any() are aborted correctly for reentrant aborts (using AbortController) diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any.worker-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any.worker-expected.txt index 25877db979fa2..3d4e6c4d04d65 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any.worker-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any.worker-expected.txt @@ -9,4 +9,6 @@ PASS AbortSignal.any() signals are composable (using AbortController) PASS AbortSignal.any() works with signals returned by AbortSignal.timeout() (using AbortController) PASS AbortSignal.any() works with intermediate signals (using AbortController) PASS Abort events for AbortSignal.any() signals fire in the right order (using AbortController) +PASS Dependent signals for AbortSignal.any() are marked aborted before abort events fire (using AbortController) +PASS Dependent signals for AbortSignal.any() are aborted correctly for reentrant aborts (using AbortController) diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/abort/resources/abort-signal-any-tests.js b/LayoutTests/imported/w3c/web-platform-tests/dom/abort/resources/abort-signal-any-tests.js index 66e4141eaccb0..929ee8a2e61ab 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/dom/abort/resources/abort-signal-any-tests.js +++ b/LayoutTests/imported/w3c/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}`); } diff --git a/Source/WebCore/dom/AbortSignal.cpp b/Source/WebCore/dom/AbortSignal.cpp index 9c56ddce9335d..94f4210500c12 100644 --- a/Source/WebCore/dom/AbortSignal.cpp +++ b/Source/WebCore/dom/AbortSignal.cpp @@ -79,7 +79,9 @@ Ref AbortSignal::any(ScriptExecutionContext& context, const Vector< { Ref resultSignal = AbortSignal::create(&context); - auto abortedSignalIndex = signals.findIf([](auto& signal) { return signal->aborted(); }); + auto abortedSignalIndex = signals.findIf([](auto& signal) { + return signal->aborted(); + }); if (abortedSignalIndex != notFound) { resultSignal->signalAbort(signals[abortedSignalIndex]->reason().getValue()); return resultSignal; @@ -126,8 +128,29 @@ void AbortSignal::signalAbort(JSC::JSValue reason) // 1. If signal's aborted flag is set, then return. if (m_aborted) return; - + // 2. Set signal’s aborted flag. + markAborted(reason); + + Vector> dependentSignalsToAbort = { }; + + for (Ref dependentSignal : std::exchange(m_dependentSignals, { })) { + if (!dependentSignal->aborted()) { + dependentSignal->markAborted(reason); + dependentSignalsToAbort.append(dependentSignal); + } + } + + // 5. Run the abort steps + runAbortSteps(); + + // 6. For each dependentSignal of dependentSignalsToAbort, run the abort steps for dependentSignal. + for (auto& dependentSignal : dependentSignalsToAbort) + dependentSignal->runAbortSteps(); +} + +void AbortSignal::markAborted(JSC::JSValue reason) +{ m_aborted = true; m_sourceSignals.clear(); @@ -135,16 +158,21 @@ void AbortSignal::signalAbort(JSC::JSValue reason) // https://bugs.webkit.org/show_bug.cgi?id=236353 ASSERT(reason); m_reason.setWeakly(reason); +} +void AbortSignal::runAbortSteps() +{ + auto reason = m_reason.getValue(); + ASSERT(reason); + + // 1. For each algorithm of signal's abort algorithms: run algorithm. + // 2. Empty signal’s abort algorithms. (std::exchange empties) auto algorithms = std::exchange(m_algorithms, { }); for (auto& algorithm : algorithms) algorithm.second(reason); - // 5. Fire an event named abort at signal. + // 3. Fire an event named abort at signal. dispatchEvent(Event::create(eventNames().abortEvent, Event::CanBubble::No, Event::IsCancelable::No)); - - for (Ref dependentSignal : std::exchange(m_dependentSignals, { })) - dependentSignal->signalAbort(reason); } // https://dom.spec.whatwg.org/#abortsignal-follow diff --git a/Source/WebCore/dom/AbortSignal.h b/Source/WebCore/dom/AbortSignal.h index 57930f5045b51..15f8552da836d 100644 --- a/Source/WebCore/dom/AbortSignal.h +++ b/Source/WebCore/dom/AbortSignal.h @@ -88,13 +88,16 @@ class AbortSignal final : public RefCounted, public EventTarget, pr void addSourceSignal(AbortSignal&); void addDependentSignal(AbortSignal&); + void markAborted(JSC::JSValue); + void runAbortSteps(); + // EventTarget. enum EventTargetInterfaceType eventTargetInterface() const final { return EventTargetInterfaceType::AbortSignal; } ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); } void refEventTarget() final { ref(); } void derefEventTarget() final { deref(); } void eventListenersDidChange() final; - + Vector> m_algorithms; WeakPtr m_followingSignal; AbortSignalSet m_sourceSignals; @@ -110,4 +113,3 @@ class AbortSignal final : public RefCounted, public EventTarget, pr WebCoreOpaqueRoot root(AbortSignal*); } // namespace WebCore -