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;