diff --git a/Tests/LibWeb/Text/expected/abortsignal-timeout.txt b/Tests/LibWeb/Text/expected/abortsignal-timeout.txt
new file mode 100644
index 0000000000..a40fa22739
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/abortsignal-timeout.txt
@@ -0,0 +1,2 @@
+Time passed before abort event fired is at least 10 milliseconds: true
+Reason type: TimeoutError
diff --git a/Tests/LibWeb/Text/input/abortsignal-timeout.html b/Tests/LibWeb/Text/input/abortsignal-timeout.html
new file mode 100644
index 0000000000..3edea60c86
--- /dev/null
+++ b/Tests/LibWeb/Text/input/abortsignal-timeout.html
@@ -0,0 +1,15 @@
+
+
diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp b/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
index 5c6d7c946c..ee9834048b 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
+++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
@@ -9,6 +9,8 @@
#include
#include
#include
+#include
+#include
namespace Web::DOM {
@@ -132,4 +134,30 @@ WebIDL::ExceptionOr> AbortSignal::abort(JS::VM& vm
return signal;
}
+// https://dom.spec.whatwg.org/#dom-abortsignal-timeout
+WebIDL::ExceptionOr> AbortSignal::timeout(JS::VM& vm, WebIDL::UnsignedLongLong milliseconds)
+{
+ auto& realm = *vm.current_realm();
+
+ // 1. Let signal be a new AbortSignal object.
+ auto signal = TRY(construct_impl(realm));
+
+ // 2. Let global be signal’s relevant global object.
+ auto& global = HTML::relevant_global_object(signal);
+ auto* window_or_worker = dynamic_cast(&global);
+ VERIFY(window_or_worker);
+
+ // 3. Run steps after a timeout given global, "AbortSignal-timeout", milliseconds, and the following step:
+ window_or_worker->run_steps_after_a_timeout(milliseconds, [&realm, &global, strong_signal = JS::make_handle(signal)]() {
+ // 1. Queue a global task on the timer task source given global to signal abort given signal and a new "TimeoutError" DOMException.
+ HTML::queue_global_task(HTML::Task::Source::TimerTask, global, [&realm, &strong_signal]() mutable {
+ auto reason = WebIDL::TimeoutError::create(realm, "Signal timed out"_fly_string);
+ strong_signal->signal_abort(reason);
+ });
+ });
+
+ // 4. Return signal.
+ return signal;
+}
+
}
diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.h b/Userland/Libraries/LibWeb/DOM/AbortSignal.h
index 12cd8e2122..7e71de8b5e 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortSignal.h
+++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
namespace Web::DOM {
@@ -44,6 +45,7 @@ public:
void follow(JS::NonnullGCPtr parent_signal);
static WebIDL::ExceptionOr> abort(JS::VM&, JS::Value reason);
+ static WebIDL::ExceptionOr> timeout(JS::VM&, Web::WebIDL::UnsignedLongLong milliseconds);
private:
explicit AbortSignal(JS::Realm&);
diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.idl b/Userland/Libraries/LibWeb/DOM/AbortSignal.idl
index 3a344c6351..9e515a9b9c 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortSignal.idl
+++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.idl
@@ -5,7 +5,7 @@
[Exposed=(Window,Worker), CustomVisit]
interface AbortSignal : EventTarget {
[NewObject] static AbortSignal abort(optional any reason);
- // FIXME: [Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
+ [Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
// FIXME: [NewObject] static AbortSignal _any(sequence signals);
readonly attribute boolean aborted;