diff --git a/Libraries/LibWeb/HTML/Timer.cpp b/Libraries/LibWeb/HTML/Timer.cpp
index 7023a00b81d..76b9adf6af0 100644
--- a/Libraries/LibWeb/HTML/Timer.cpp
+++ b/Libraries/LibWeb/HTML/Timer.cpp
@@ -13,27 +13,26 @@ namespace Web::HTML {
GC_DEFINE_ALLOCATOR(Timer);
-GC::Ref Timer::create(JS::Object& window_or_worker_global_scope, i32 milliseconds, Function callback, i32 id)
+GC::Ref Timer::create(JS::Object& window_or_worker_global_scope, i32 milliseconds, Function callback, i32 id, Repeating repeating)
{
- auto heap_function_callback = GC::create_function(window_or_worker_global_scope.heap(), move(callback));
- return window_or_worker_global_scope.heap().allocate(window_or_worker_global_scope, milliseconds, heap_function_callback, id);
+ return window_or_worker_global_scope.heap().allocate(window_or_worker_global_scope, milliseconds, move(callback), id, repeating);
}
-Timer::Timer(JS::Object& window_or_worker_global_scope, i32 milliseconds, GC::Ref> callback, i32 id)
+Timer::Timer(JS::Object& window_or_worker_global_scope, i32 milliseconds, Function callback, i32 id, Repeating repeating)
: m_window_or_worker_global_scope(window_or_worker_global_scope)
- , m_callback(move(callback))
, m_id(id)
{
- m_timer = Core::Timer::create_single_shot(milliseconds, [this] {
- m_callback->function()();
- });
+ if (repeating == Repeating::Yes)
+ m_timer = Core::Timer::create_repeating(milliseconds, move(callback));
+ else
+ m_timer = Core::Timer::create_single_shot(milliseconds, move(callback));
}
void Timer::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_window_or_worker_global_scope);
- visitor.visit(m_callback);
+ visitor.visit_possible_values(m_timer->on_timeout.raw_capture_range());
}
Timer::~Timer()
@@ -51,4 +50,9 @@ void Timer::stop()
m_timer->stop();
}
+void Timer::set_callback(Function callback)
+{
+ m_timer->on_timeout = move(callback);
+}
+
}
diff --git a/Libraries/LibWeb/HTML/Timer.h b/Libraries/LibWeb/HTML/Timer.h
index c87aac0f680..97636482fb1 100644
--- a/Libraries/LibWeb/HTML/Timer.h
+++ b/Libraries/LibWeb/HTML/Timer.h
@@ -22,20 +22,26 @@ class Timer final : public JS::Cell {
GC_DECLARE_ALLOCATOR(Timer);
public:
- static GC::Ref create(JS::Object&, i32 milliseconds, Function callback, i32 id);
+ enum class Repeating {
+ No,
+ Yes,
+ };
+
+ static GC::Ref create(JS::Object&, i32 milliseconds, Function callback, i32 id, Repeating);
virtual ~Timer() override;
void start();
void stop();
+ void set_callback(Function);
+
private:
- Timer(JS::Object& window, i32 milliseconds, GC::Ref> callback, i32 id);
+ Timer(JS::Object& window, i32 milliseconds, Function callback, i32 id, Repeating);
virtual void visit_edges(Cell::Visitor&) override;
RefPtr m_timer;
GC::Ref m_window_or_worker_global_scope;
- GC::Ref> m_callback;
i32 m_id { 0 };
};
diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp
index 458a72ef219..8e48d2e4deb 100644
--- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp
+++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp
@@ -37,6 +37,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -672,7 +673,7 @@ i32 WindowOrWorkerGlobalScopeMixin::run_timer_initialization_steps(TimerHandler
// 13. Set uniqueHandle to the result of running steps after a timeout given global, "setTimeout/setInterval",
// timeout, and completionStep.
// FIXME: run_steps_after_a_timeout() needs to be updated to return a unique internal value that can be used here.
- run_steps_after_a_timeout_impl(timeout, move(completion_step), id);
+ run_steps_after_a_timeout_impl(timeout, move(completion_step), id, repeat);
// FIXME: 14. Set global's map of setTimeout and setInterval IDs[id] to uniqueHandle.
@@ -1074,20 +1075,29 @@ WindowOrWorkerGlobalScopeMixin::AffectedAnyWebSockets WindowOrWorkerGlobalScopeM
// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#run-steps-after-a-timeout
void WindowOrWorkerGlobalScopeMixin::run_steps_after_a_timeout(i32 timeout, Function completion_step)
{
- return run_steps_after_a_timeout_impl(timeout, move(completion_step));
+ return run_steps_after_a_timeout_impl(timeout, move(completion_step), {}, Repeat::No);
}
-void WindowOrWorkerGlobalScopeMixin::run_steps_after_a_timeout_impl(i32 timeout, Function completion_step, Optional timer_key)
+void WindowOrWorkerGlobalScopeMixin::run_steps_after_a_timeout_impl(i32 timeout, Function completion_step, Optional timer_key, Repeat repeat)
{
// 1. Assert: if timerKey is given, then the caller of this algorithm is the timer initialization steps. (Other specifications must not pass timerKey.)
// Note: This is enforced by the caller.
- // 2. If timerKey is not given, then set it to a new unique non-numeric value.
- if (!timer_key.has_value())
+ // NB: We deviate from the spec here slightly by reusing existing timers if a timer_key is provided.
+ GC::Ptr existing_timer;
+ if (timer_key.has_value()) {
+ auto result = m_timers.get(timer_key.value());
+ if (result.has_value()) {
+ existing_timer = result.value().ptr();
+ existing_timer->set_callback(move(completion_step));
+ }
+ } else {
+ // 2. If timerKey is not given, then set it to a new unique non-numeric value.
timer_key = m_timer_id_allocator.allocate();
+ }
// FIXME: 3. Let startTime be the current high resolution time given global.
- auto timer = Timer::create(this_impl(), timeout, move(completion_step), timer_key.value());
+ auto timer = existing_timer ? GC::Ref { *existing_timer } : Timer::create(this_impl(), timeout, move(completion_step), timer_key.value(), repeat == Repeat::Yes ? Timer::Repeating::Yes : Timer::Repeating::No);
// FIXME: 4. Set global's map of active timers[timerKey] to startTime plus milliseconds.
m_timers.set(timer_key.value(), timer);
diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h
index 874031136c4..c858fd7080a 100644
--- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h
+++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h
@@ -117,7 +117,7 @@ private:
No,
};
i32 run_timer_initialization_steps(TimerHandler handler, i32 timeout, GC::RootVector arguments, Repeat repeat, Optional previous_id = {});
- void run_steps_after_a_timeout_impl(i32 timeout, Function completion_step, Optional timer_key = {});
+ void run_steps_after_a_timeout_impl(i32 timeout, Function completion_step, Optional timer_key, Repeat);
GC::Ref create_image_bitmap_impl(ImageBitmapSource& image, Optional sx, Optional sy, Optional sw, Optional sh, Optional& options) const;