From 7632cce5e5bc2069608e3a11801e20888d8a15f8 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 13 Sep 2021 02:03:06 +0200 Subject: [PATCH] LibWeb: Make requestAnimationFrame() work in multi-process mode Since we don't have a direct connection to WindowServer, this is slighly more naive implementation than what we were doing for single-process mode. Basically, there's a global RequestAnimationFrameDriver object that has a 16ms single-shot timer. Whenever someone registers for a RAF callback, we start the timer (if needed). This is not ideal, but it's better than nothing. :^) --- Userland/Libraries/LibWeb/DOM/Window.cpp | 90 ++++++++++++++++++++---- Userland/Libraries/LibWeb/DOM/Window.h | 4 ++ 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/Userland/Libraries/LibWeb/DOM/Window.cpp b/Userland/Libraries/LibWeb/DOM/Window.cpp index 8202ed9825..1a9e986116 100644 --- a/Userland/Libraries/LibWeb/DOM/Window.cpp +++ b/Userland/Libraries/LibWeb/DOM/Window.cpp @@ -19,6 +19,70 @@ namespace Web::DOM { +class RequestAnimationFrameCallback : public RefCounted { +public: + explicit RequestAnimationFrameCallback(i32 id, Function handler) + : m_id(id) + , m_handler(move(handler)) + { + } + ~RequestAnimationFrameCallback() { } + + i32 id() const { return m_id; } + bool is_cancelled() const { return !m_handler; } + + void cancel() { m_handler = nullptr; } + void invoke() { m_handler(m_id); } + +private: + i32 m_id { 0 }; + Function m_handler; +}; + +struct RequestAnimationFrameDriver { + RequestAnimationFrameDriver() + { + m_timer = Core::Timer::create_single_shot(16, [this] { + auto taken_callbacks = move(m_callbacks); + for (auto& it : taken_callbacks) { + if (!it.value->is_cancelled()) + it.value->invoke(); + } + }); + } + + NonnullRefPtr add(Function handler) + { + auto id = m_id_allocator.allocate(); + auto callback = adopt_ref(*new RequestAnimationFrameCallback { id, move(handler) }); + m_callbacks.set(id, callback); + if (!m_timer->is_active()) + m_timer->start(); + return callback; + } + + bool remove(i32 id) + { + auto it = m_callbacks.find(id); + if (it == m_callbacks.end()) + return false; + m_callbacks.remove(it); + m_id_allocator.deallocate(id); + return true; + } + +private: + HashMap> m_callbacks; + IDAllocator m_id_allocator; + RefPtr m_timer; +}; + +static RequestAnimationFrameDriver& request_animation_frame_driver() +{ + static RequestAnimationFrameDriver driver; + return driver; +} + NonnullRefPtr Window::create_with_document(Document& document) { return adopt_ref(*new Window(document)); @@ -113,29 +177,27 @@ void Window::clear_interval(i32 timer_id) m_timers.remove(timer_id); } -i32 Window::request_animation_frame(JS::FunctionObject& callback) +i32 Window::request_animation_frame(JS::FunctionObject& js_callback) { - // FIXME: This is extremely fake! - static double fake_timestamp = 0; - - i32 link_id = GUI::DisplayLink::register_callback([handle = make_handle(&callback)](i32 link_id) { - auto& function = const_cast(static_cast(*handle.cell())); + auto callback = request_animation_frame_driver().add([this, handle = JS::make_handle(&js_callback)](i32 id) mutable { + auto& function = *handle.cell(); auto& vm = function.vm(); - fake_timestamp += 10; - [[maybe_unused]] auto rc = vm.call(function, JS::js_undefined(), JS::Value(fake_timestamp)); + (void)vm.call(function, JS::js_undefined(), JS::Value((double)Core::DateTime::now().timestamp())); if (vm.exception()) vm.clear_exception(); - GUI::DisplayLink::unregister_callback(link_id); + m_request_animation_frame_callbacks.remove(id); }); - - // FIXME: Don't hand out raw DisplayLink ID's to JavaScript! - return link_id; + m_request_animation_frame_callbacks.set(callback->id(), callback); + return callback->id(); } void Window::cancel_animation_frame(i32 id) { - // FIXME: We should not be passing untrusted numbers to DisplayLink::unregister_callback()! - GUI::DisplayLink::unregister_callback(id); + auto it = m_request_animation_frame_callbacks.find(id); + if (it == m_request_animation_frame_callbacks.end()) + return; + it->value->cancel(); + m_request_animation_frame_callbacks.remove(it); } void Window::did_set_location_href(Badge, AK::URL const& new_href) diff --git a/Userland/Libraries/LibWeb/DOM/Window.h b/Userland/Libraries/LibWeb/DOM/Window.h index 36f5fecb5c..cb32612922 100644 --- a/Userland/Libraries/LibWeb/DOM/Window.h +++ b/Userland/Libraries/LibWeb/DOM/Window.h @@ -19,6 +19,8 @@ namespace Web::DOM { +class RequestAnimationFrameCallback; + class Window final : public RefCounted , public EventTarget { @@ -90,6 +92,8 @@ private: NonnullOwnPtr m_performance; NonnullRefPtr m_screen; RefPtr m_current_event; + + HashMap> m_request_animation_frame_callbacks; }; }