diff --git a/Tests/LibWeb/Text/expected/abortsignal-any.txt b/Tests/LibWeb/Text/expected/abortsignal-any.txt
new file mode 100644
index 00000000000..4e88fb3ab77
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/abortsignal-any.txt
@@ -0,0 +1,39 @@
+Signal 0 aborted: false
+Signal 1 aborted: false
+Signal 2 aborted: false
+Signal 3 aborted: false
+Signal 4 aborted: false
+Abort callback for signal: 0. Reason: Test AbortController 1
+Abort callback for signal: 1. Reason: Test AbortController 1
+Abort callback for signal: 3. Reason: Test AbortController 1
+Abort callback for signal: 4. Reason: Test AbortController 1
+Signal 0 aborted: true
+Signal 1 aborted: true
+Signal 2 aborted: false
+Signal 3 aborted: true
+Signal 4 aborted: true
+Signal 0 aborted: false
+Signal 1 aborted: false
+Signal 2 aborted: false
+Signal 3 aborted: false
+Signal 4 aborted: false
+Abort callback for signal: 1. Reason: Test AbortController 2
+Abort callback for signal: 2. Reason: Test AbortController 2
+Abort callback for signal: 3. Reason: Test AbortController 2
+Abort callback for signal: 4. Reason: Test AbortController 2
+Signal 0 aborted: false
+Signal 1 aborted: true
+Signal 2 aborted: true
+Signal 3 aborted: true
+Signal 4 aborted: true
+Signal 0 aborted: false
+Signal 1 aborted: false
+Signal 2 aborted: false
+Signal 3 aborted: false
+Signal 4 aborted: false
+Abort callback for signal: 4. Reason: Test AbortController 3
+Signal 0 aborted: false
+Signal 1 aborted: false
+Signal 2 aborted: false
+Signal 3 aborted: false
+Signal 4 aborted: true
diff --git a/Tests/LibWeb/Text/input/abortsignal-any.html b/Tests/LibWeb/Text/input/abortsignal-any.html
new file mode 100644
index 00000000000..b76dc8ebffb
--- /dev/null
+++ b/Tests/LibWeb/Text/input/abortsignal-any.html
@@ -0,0 +1,52 @@
+
+
diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp b/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
index 6852cc783dc..0b5df2fb26c 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
+++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Luke Wilde
+ * Copyright (c) 2024, Tim Ledbetter
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -67,6 +68,10 @@ void AbortSignal::signal_abort(JS::Value reason)
auto abort_event = Event::create(realm(), HTML::EventNames::abort);
abort_event->set_is_trusted(true);
dispatch_event(abort_event);
+
+ // 6. For each dependentSignal of signal’s dependent signals, signal abort on dependentSignal with signal’s abort reason.
+ for (auto const& dependent_signal : m_dependent_signals)
+ dependent_signal->signal_abort(reason);
}
void AbortSignal::set_onabort(WebIDL::CallbackType* event_handler)
@@ -95,6 +100,12 @@ void AbortSignal::visit_edges(JS::Cell::Visitor& visitor)
visitor.visit(m_abort_reason);
for (auto& algorithm : m_abort_algorithms)
visitor.visit(algorithm);
+
+ for (auto& source_signal : m_source_signals)
+ visitor.visit(source_signal);
+
+ for (auto& dependent_signal : m_dependent_signals)
+ visitor.visit(dependent_signal);
}
// https://dom.spec.whatwg.org/#abortsignal-follow
@@ -162,4 +173,74 @@ WebIDL::ExceptionOr> AbortSignal::timeout(JS::VM&
return signal;
}
+// https://dom.spec.whatwg.org/#dom-abortsignal-any
+WebIDL::ExceptionOr> AbortSignal::any(JS::VM& vm, JS::Value signals)
+{
+ Vector> signals_list;
+ auto iterator_record = TRY(get_iterator(vm, signals, JS::IteratorHint::Sync));
+ while (true) {
+ auto next = TRY(iterator_step_value(vm, iterator_record));
+ if (!next.has_value())
+ break;
+
+ auto value = next.release_value();
+ if (!value.is_object() || !is(value.as_object()))
+ return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "AbortSignal");
+
+ auto& signal = static_cast(value.as_object());
+ signals_list.append(JS::make_handle(signal));
+ }
+
+ // The static any(signals) method steps are to return the result of creating a dependent abort signal from signals using AbortSignal and the current realm.
+ return create_dependent_abort_signal(*vm.current_realm(), signals_list);
+}
+
+// https://dom.spec.whatwg.org/#create-a-dependent-abort-signal
+WebIDL::ExceptionOr> AbortSignal::create_dependent_abort_signal(JS::Realm& realm, Vector> const& signals)
+{
+ // 1. Let resultSignal be a new object implementing signalInterface using realm.
+ auto result_signal = TRY(construct_impl(realm));
+
+ // 2. For each signal of signals: if signal is aborted, then set resultSignal’s abort reason to signal’s abort reason and return resultSignal.
+ for (auto const& signal : signals) {
+ if (signal->aborted()) {
+ result_signal->set_reason(signal->reason());
+ return result_signal;
+ }
+ }
+
+ // 3. Set resultSignal’s dependent to true.
+ result_signal->set_dependent(true);
+
+ // 4. For each signal of signals:
+ for (auto const& signal : signals) {
+ // 1. If signal’s dependent is false, then:
+ if (!signal->dependent()) {
+ // 1. Append signal to resultSignal’s source signals.
+ result_signal->append_source_signal({ signal });
+
+ // 2. Append resultSignal to signal’s dependent signals.
+ signal->append_dependent_signal(result_signal);
+ }
+ // 2. Otherwise, for each sourceSignal of signal’s source signals:
+ else {
+ for (auto const& source_signal : signal->source_signals()) {
+ // 1. Assert: sourceSignal is not aborted and not dependent.
+ VERIFY(source_signal);
+ VERIFY(!source_signal->aborted());
+ VERIFY(!source_signal->dependent());
+
+ // 2. Append sourceSignal to resultSignal’s source signals.
+ result_signal->append_source_signal(source_signal);
+
+ // 3. Append resultSignal to sourceSignal’s dependent signals.
+ source_signal->append_dependent_signal(result_signal);
+ }
+ }
+ }
+
+ // 5. Return resultSignal
+ return result_signal;
+}
+
}
diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.h b/Userland/Libraries/LibWeb/DOM/AbortSignal.h
index 7e71de8b5e2..7fb0ad016bf 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortSignal.h
+++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Luke Wilde
+ * Copyright (c) 2024, Tim Ledbetter
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -46,6 +47,7 @@ public:
static WebIDL::ExceptionOr> abort(JS::VM&, JS::Value reason);
static WebIDL::ExceptionOr> timeout(JS::VM&, Web::WebIDL::UnsignedLongLong milliseconds);
+ static WebIDL::ExceptionOr> any(JS::VM&, JS::Value signals);
private:
explicit AbortSignal(JS::Realm&);
@@ -53,6 +55,16 @@ private:
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(JS::Cell::Visitor&) override;
+ static WebIDL::ExceptionOr> create_dependent_abort_signal(JS::Realm&, Vector> const&);
+
+ bool dependent() const { return m_dependent; }
+ void set_dependent(bool dependent) { m_dependent = dependent; }
+
+ Vector> source_signals() const { return m_source_signals; }
+
+ void append_source_signal(JS::GCPtr source_signal) { m_source_signals.append(source_signal); }
+ void append_dependent_signal(JS::GCPtr dependent_signal) { m_dependent_signals.append(dependent_signal); }
+
// https://dom.spec.whatwg.org/#abortsignal-abort-reason
// An AbortSignal object has an associated abort reason, which is a JavaScript value. It is undefined unless specified otherwise.
JS::Value m_abort_reason { JS::js_undefined() };
@@ -60,6 +72,18 @@ private:
// https://dom.spec.whatwg.org/#abortsignal-abort-algorithms
// FIXME: This should be a set.
Vector>> m_abort_algorithms;
+
+ // 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.
+ Vector> m_source_signals;
+
+ // https://dom.spec.whatwg.org/#abortsignal-dependent-signals
+ // An AbortSignal object has associated dependent signals (a weak set of AbortSignal objects that are dependent on the object for their aborted state), which is initially empty.
+ Vector> m_dependent_signals;
+
+ // https://dom.spec.whatwg.org/#abortsignal-dependent
+ // An AbortSignal object has a dependent (a boolean), which is initially false.
+ bool m_dependent { false };
};
}
diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.idl b/Userland/Libraries/LibWeb/DOM/AbortSignal.idl
index 9e515a9b9c1..b168cb5e0bd 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortSignal.idl
+++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.idl
@@ -6,7 +6,8 @@
interface AbortSignal : EventTarget {
[NewObject] static AbortSignal abort(optional any reason);
[Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
- // FIXME: [NewObject] static AbortSignal _any(sequence signals);
+ // FIXME: Argument should be of type: sequence.
+ [NewObject] static AbortSignal _any(any signals);
readonly attribute boolean aborted;
readonly attribute any reason;