LibWeb: Support removing callbacks from AbortSignal

This will be needed by Streams. To support this, we now store callbacks
in a hash table, keyed by an ID. Callers may use that ID to remove the
callback at a later point.
This commit is contained in:
Timothy Flynn 2025-04-10 09:04:01 -04:00 committed by Tim Flynn
parent f268f24dd5
commit 4010c4643a
Notes: github-actions[bot] 2025-04-11 16:11:54 +00:00
5 changed files with 22 additions and 11 deletions

View file

@ -35,14 +35,22 @@ void AbortSignal::initialize(JS::Realm& realm)
} }
// https://dom.spec.whatwg.org/#abortsignal-add // https://dom.spec.whatwg.org/#abortsignal-add
void AbortSignal::add_abort_algorithm(Function<void()> abort_algorithm) Optional<AbortSignal::AbortAlgorithmID> AbortSignal::add_abort_algorithm(Function<void()> abort_algorithm)
{ {
// 1. If signal is aborted, then return. // 1. If signal is aborted, then return.
if (aborted()) if (aborted())
return; return {};
// 2. Append algorithm to signals abort algorithms. // 2. Append algorithm to signals abort algorithms.
m_abort_algorithms.append(GC::create_function(vm().heap(), move(abort_algorithm))); m_abort_algorithms.set(++m_next_abort_algorithm_id, GC::create_function(vm().heap(), move(abort_algorithm)));
return m_next_abort_algorithm_id;
}
// https://dom.spec.whatwg.org/#abortsignal-remove
void AbortSignal::remove_abort_algorithm(AbortAlgorithmID id)
{
// To remove an algorithm algorithm from an AbortSignal signal, remove algorithm from signals abort algorithms.
m_abort_algorithms.remove(id);
} }
// https://dom.spec.whatwg.org/#abortsignal-signal-abort // https://dom.spec.whatwg.org/#abortsignal-signal-abort
@ -76,8 +84,8 @@ void AbortSignal::signal_abort(JS::Value reason)
// https://dom.spec.whatwg.org/#run-the-abort-steps // https://dom.spec.whatwg.org/#run-the-abort-steps
auto run_the_abort_steps = [](auto& signal) { auto run_the_abort_steps = [](auto& signal) {
// 1. For each algorithm in signals abort algorithms: run algorithm. // 1. For each algorithm in signals abort algorithms: run algorithm.
for (auto& algorithm : signal.m_abort_algorithms) for (auto const& algorithm : signal.m_abort_algorithms)
algorithm->function()(); algorithm.value->function()();
// 2. Empty signals abort algorithms. // 2. Empty signals abort algorithms.
signal.m_abort_algorithms.clear(); signal.m_abort_algorithms.clear();

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <AK/HashMap.h>
#include <AK/RefCounted.h> #include <AK/RefCounted.h>
#include <AK/Weakable.h> #include <AK/Weakable.h>
#include <LibGC/Function.h> #include <LibGC/Function.h>
@ -26,7 +27,9 @@ public:
virtual ~AbortSignal() override = default; virtual ~AbortSignal() override = default;
void add_abort_algorithm(ESCAPING Function<void()>); using AbortAlgorithmID = u64;
Optional<AbortAlgorithmID> add_abort_algorithm(Function<void()>);
void remove_abort_algorithm(AbortAlgorithmID);
// https://dom.spec.whatwg.org/#dom-abortsignal-aborted // https://dom.spec.whatwg.org/#dom-abortsignal-aborted
// An AbortSignal object is aborted when its abort reason is not undefined. // An AbortSignal object is aborted when its abort reason is not undefined.
@ -68,8 +71,8 @@ private:
JS::Value m_abort_reason { JS::js_undefined() }; JS::Value m_abort_reason { JS::js_undefined() };
// https://dom.spec.whatwg.org/#abortsignal-abort-algorithms // https://dom.spec.whatwg.org/#abortsignal-abort-algorithms
// FIXME: This should be a set. OrderedHashMap<AbortAlgorithmID, GC::Ref<GC::Function<void()>>> m_abort_algorithms;
Vector<GC::Ref<GC::Function<void()>>> m_abort_algorithms; AbortAlgorithmID m_next_abort_algorithm_id { 0 };
// https://dom.spec.whatwg.org/#abortsignal-source-signals // https://dom.spec.whatwg.org/#abortsignal-source-signals
// An AbortSignal object has associated source signals (a weak set of AbortSignal objects that the object is dependent on for its aborted state), which is initially empty. // An AbortSignal object has associated source signals (a weak set of AbortSignal objects that the object is dependent on for its aborted state), which is initially empty.

View file

@ -231,7 +231,7 @@ void EventTarget::add_an_event_listener(DOMEventListener& listener)
// 6. If listeners signal is not null, then add the following abort steps to it: // 6. If listeners signal is not null, then add the following abort steps to it:
if (listener.signal) { if (listener.signal) {
// NOTE: `this` and `listener` are protected by AbortSignal using GC::HeapFunction. // NOTE: `this` and `listener` are protected by AbortSignal using GC::HeapFunction.
listener.signal->add_abort_algorithm([this, &listener] { (void)listener.signal->add_abort_algorithm([this, &listener] {
// 1. Remove an event listener with eventTarget and listener. // 1. Remove an event listener with eventTarget and listener.
remove_an_event_listener(listener); remove_an_event_listener(listener);
}); });

View file

@ -130,7 +130,7 @@ GC::Ref<WebIDL::Promise> fetch(JS::VM& vm, RequestInfo const& input, RequestInit
})))); }))));
// 11. Add the following abort steps to requestObjects signal: // 11. Add the following abort steps to requestObjects signal:
request_object->signal()->add_abort_algorithm([locally_aborted, request, controller_holder, promise_capability, request_object, response_object, &relevant_realm] { (void)request_object->signal()->add_abort_algorithm([locally_aborted, request, controller_holder, promise_capability, request_object, response_object, &relevant_realm] {
dbgln_if(WEB_FETCH_DEBUG, "Fetch: Request object signal's abort algorithm called"); dbgln_if(WEB_FETCH_DEBUG, "Fetch: Request object signal's abort algorithm called");
// 1. Set locallyAborted to true. // 1. Set locallyAborted to true.

View file

@ -65,7 +65,7 @@ WebIDL::ExceptionOr<GC::Ref<CloseWatcher>> CloseWatcher::construct_impl(JS::Real
} }
// 3.2 Add the following steps to options["signal"]: // 3.2 Add the following steps to options["signal"]:
signal->add_abort_algorithm([close_watcher] { (void)signal->add_abort_algorithm([close_watcher] {
// 3.2.1 Destroy closeWatcher. // 3.2.1 Destroy closeWatcher.
close_watcher->destroy(); close_watcher->destroy();
}); });