Преглед изворни кода

LibWeb: Implement `AbortSignal.timeout()`

This method returns a signal that will automatically abort after a
given number of milliseconds.
Tim Ledbetter пре 1 година
родитељ
комит
ae42c6ed80

+ 2 - 0
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

+ 15 - 0
Tests/LibWeb/Text/input/abortsignal-timeout.html

@@ -0,0 +1,15 @@
+<script src="include.js"></script>
+<script>
+    asyncTest(done => {
+        const timeout_milliseconds = 10;
+        const test_start_time = performance.now();
+        const signal = AbortSignal.timeout(timeout_milliseconds);
+        signal.onabort = () => {
+            const abort_event_time = performance.now();
+            const time_taken_milliseconds = abort_event_time - test_start_time;
+            println(`Time passed before abort event fired is at least ${timeout_milliseconds} milliseconds: ${time_taken_milliseconds >= timeout_milliseconds}`);
+            println(`Reason type: ${signal.reason.name}`);
+            done();
+        };
+    });
+</script>

+ 28 - 0
Userland/Libraries/LibWeb/DOM/AbortSignal.cpp

@@ -9,6 +9,8 @@
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/EventDispatcher.h>
 #include <LibWeb/HTML/EventHandler.h>
+#include <LibWeb/HTML/Window.h>
+#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
 
 namespace Web::DOM {
 
@@ -132,4 +134,30 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<AbortSignal>> AbortSignal::abort(JS::VM& vm
     return signal;
 }
 
+// https://dom.spec.whatwg.org/#dom-abortsignal-timeout
+WebIDL::ExceptionOr<JS::NonnullGCPtr<AbortSignal>> 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<HTML::WindowOrWorkerGlobalScopeMixin*>(&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;
+}
+
 }

+ 2 - 0
Userland/Libraries/LibWeb/DOM/AbortSignal.h

@@ -11,6 +11,7 @@
 #include <LibJS/Heap/HeapFunction.h>
 #include <LibWeb/DOM/EventTarget.h>
 #include <LibWeb/Forward.h>
+#include <LibWeb/WebIDL/Types.h>
 
 namespace Web::DOM {
 
@@ -44,6 +45,7 @@ public:
     void follow(JS::NonnullGCPtr<AbortSignal> parent_signal);
 
     static WebIDL::ExceptionOr<JS::NonnullGCPtr<AbortSignal>> abort(JS::VM&, JS::Value reason);
+    static WebIDL::ExceptionOr<JS::NonnullGCPtr<AbortSignal>> timeout(JS::VM&, Web::WebIDL::UnsignedLongLong milliseconds);
 
 private:
     explicit AbortSignal(JS::Realm&);

+ 1 - 1
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<AbortSignal> signals);
 
     readonly attribute boolean aborted;