LibWeb: Abort dependent signals before firing abort event

Previously, there was a bug in the specification that would cause an
assertion failure, due to the abort event being fired before all
dependent signals were aborted.
This commit is contained in:
Tim Ledbetter 2024-09-08 21:10:52 +01:00 committed by Andreas Kling
commit 3ae4ea7b10
Notes: github-actions[bot] 2024-09-09 13:12:26 +00:00
3 changed files with 46 additions and 13 deletions

View file

@ -0,0 +1 @@
PASS (didn't crash)

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
test(() => {
const abortController = new AbortController();
const signal = AbortSignal.any([abortController.signal]);
abortController.signal.addEventListener("abort", () => { AbortSignal.any([signal]) });
abortController.abort();
println("PASS (didn't crash)");
});
</script>

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2024, Tim Ledbetter <timledbetter@gmail.com>
* Copyright (c) 2024, Tim Ledbetter <tim.ledbetter@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -58,21 +58,42 @@ void AbortSignal::signal_abort(JS::Value reason)
else
m_abort_reason = WebIDL::AbortError::create(realm(), "Aborted without reason"_fly_string).ptr();
// 3. For each algorithm in signals abort algorithms: run algorithm.
for (auto& algorithm : m_abort_algorithms)
algorithm->function()();
// 3. Let dependentSignalsToAbort be a new list.
Vector<JS::Handle<AbortSignal>> dependent_signals_to_abort;
// 4. Empty signals abort algorithms.
m_abort_algorithms.clear();
// 4. For each dependentSignal of signals dependent signals:
for (auto const& dependent_signal : m_dependent_signals) {
// 1. If dependentSignal is not aborted, then:
if (!dependent_signal->aborted()) {
// 1. Set dependentSignals abort reason to signals abort reason.
dependent_signal->set_reason(m_abort_reason);
// 5. Fire an event named abort at signal.
auto abort_event = Event::create(realm(), HTML::EventNames::abort);
abort_event->set_is_trusted(true);
dispatch_event(abort_event);
// 2. Append dependentSignal to dependentSignalsToAbort.
dependent_signals_to_abort.append(*dependent_signal);
}
}
// 6. For each dependentSignal of signals dependent signals, signal abort on dependentSignal with signals abort reason.
for (auto const& dependent_signal : m_dependent_signals)
dependent_signal->signal_abort(reason);
// https://dom.spec.whatwg.org/#run-the-abort-steps
auto run_the_abort_steps = [](auto& signal) {
// 1. For each algorithm in signals abort algorithms: run algorithm.
for (auto& algorithm : signal.m_abort_algorithms)
algorithm->function()();
// 2. Empty signals abort algorithms.
signal.m_abort_algorithms.clear();
// 3. Fire an event named abort at signal.
auto abort_event = Event::create(signal.realm(), HTML::EventNames::abort);
abort_event->set_is_trusted(true);
signal.dispatch_event(abort_event);
};
// 5. Run the abort steps for signal.
run_the_abort_steps(*this);
// 6. For each dependentSignal of dependentSignalsToAbort, run the abort steps for dependentSignal.
for (auto const& dependent_signal : dependent_signals_to_abort)
run_the_abort_steps(*dependent_signal);
}
void AbortSignal::set_onabort(WebIDL::CallbackType* event_handler)