LibWeb: Allocate AnimationFrameCallbackDriver on the JS heap

This avoids needing to creating root handles for each heap-allocated
object captured in the animation callback. An upcoming commit would add
several of these.
This commit is contained in:
Timothy Flynn 2024-10-30 20:13:31 -04:00 committed by Tim Ledbetter
parent 144907f5bd
commit d9e5ae66a7
Notes: github-actions[bot] 2024-10-31 02:40:42 +00:00
7 changed files with 88 additions and 44 deletions

View file

@ -269,6 +269,7 @@ set(SOURCES
Geometry/DOMRectReadOnly.cpp
HTML/AbstractWorker.cpp
HTML/AnimatedBitmapDecodedImageData.cpp
HTML/AnimationFrameCallbackDriver.cpp
HTML/AttributeNames.cpp
HTML/AudioTrack.cpp
HTML/AudioTrackList.cpp

View file

@ -375,6 +375,7 @@ struct DOMPointInit;
}
namespace Web::HTML {
class AnimationFrameCallbackDriver;
class AudioTrack;
class AudioTrackList;
class BroadcastChannel;

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2022, the SerenityOS developers.
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/HTML/AnimationFrameCallbackDriver.h>
namespace Web::HTML {
JS_DEFINE_ALLOCATOR(AnimationFrameCallbackDriver);
void AnimationFrameCallbackDriver::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_callbacks);
}
WebIDL::UnsignedLong AnimationFrameCallbackDriver::add(Callback handler)
{
auto id = ++m_animation_frame_callback_identifier;
m_callbacks.set(id, handler);
return id;
}
bool AnimationFrameCallbackDriver::remove(WebIDL::UnsignedLong id)
{
return m_callbacks.remove(id);
}
bool AnimationFrameCallbackDriver::has_callbacks() const
{
return !m_callbacks.is_empty();
}
void AnimationFrameCallbackDriver::run(double now)
{
auto taken_callbacks = move(m_callbacks);
for (auto& [id, callback] : taken_callbacks)
callback->function()(now);
}
}

View file

@ -1,52 +1,35 @@
/*
* Copyright (c) 2022, the SerenityOS developers.
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <LibCore/Timer.h>
#include <LibWeb/HTML/EventLoop/EventLoop.h>
#include <LibJS/Heap/GCPtr.h>
#include <LibJS/Heap/HeapFunction.h>
#include <LibWeb/WebIDL/Types.h>
namespace Web::HTML {
struct AnimationFrameCallbackDriver {
using Callback = Function<void(double)>;
class AnimationFrameCallbackDriver final : public JS::Cell {
JS_CELL(AnimationFrameCallbackDriver, JS::Cell);
JS_DECLARE_ALLOCATOR(AnimationFrameCallbackDriver);
[[nodiscard]] WebIDL::UnsignedLong add(Callback handler)
{
auto id = ++m_animation_frame_callback_identifier;
m_callbacks.set(id, move(handler));
return id;
}
using Callback = JS::NonnullGCPtr<JS::HeapFunction<void(double)>>;
bool remove(WebIDL::UnsignedLong id)
{
auto it = m_callbacks.find(id);
if (it == m_callbacks.end())
return false;
m_callbacks.remove(it);
return true;
}
void run(double now)
{
auto taken_callbacks = move(m_callbacks);
for (auto& [id, callback] : taken_callbacks)
callback(now);
}
bool has_callbacks() const
{
return !m_callbacks.is_empty();
}
public:
[[nodiscard]] WebIDL::UnsignedLong add(Callback handler);
bool remove(WebIDL::UnsignedLong);
bool has_callbacks() const;
void run(double now);
private:
virtual void visit_edges(Cell::Visitor&) override;
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#animation-frame-callback-identifier
WebIDL::UnsignedLong m_animation_frame_callback_identifier { 0 };

View file

@ -31,6 +31,7 @@
#include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/HTMLCollection.h>
#include <LibWeb/DOMURL/DOMURL.h>
#include <LibWeb/HTML/AnimationFrameCallbackDriver.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/CloseWatcherManager.h>
#include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
@ -123,6 +124,7 @@ void Window::visit_edges(JS::Cell::Visitor& visitor)
visitor.visit(m_navigator);
visitor.visit(m_navigation);
visitor.visit(m_custom_element_registry);
visitor.visit(m_animation_frame_callback_driver);
visitor.visit(m_pdf_viewer_plugin_objects);
visitor.visit(m_pdf_viewer_mime_type_objects);
visitor.visit(m_count_queuing_strategy_size_function);
@ -1541,15 +1543,15 @@ double Window::device_pixel_ratio() const
}
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#dom-animationframeprovider-requestanimationframe
WebIDL::UnsignedLong Window::request_animation_frame(WebIDL::CallbackType& callback)
WebIDL::UnsignedLong Window::request_animation_frame(JS::NonnullGCPtr<WebIDL::CallbackType> callback)
{
// FIXME: Make this fully spec compliant. Currently implements a mix of 'requestAnimationFrame()' and 'run the animation frame callbacks'.
return m_animation_frame_callback_driver.add([this, callback = JS::make_handle(callback)](double now) {
return animation_frame_callback_driver().add(JS::create_heap_function(heap(), [this, callback](double now) {
// 3. Invoke callback, passing now as the only argument, and if an exception is thrown, report the exception.
auto result = WebIDL::invoke_callback(*callback, {}, JS::Value(now));
if (result.is_error())
report_exception(result, realm());
});
}));
}
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#animationframeprovider-cancelanimationframe
@ -1560,7 +1562,21 @@ void Window::cancel_animation_frame(WebIDL::UnsignedLong handle)
// 2. Let callbacks be this's target object's map of animation frame callbacks.
// 3. Remove callbacks[handle].
(void)m_animation_frame_callback_driver.remove(handle);
(void)animation_frame_callback_driver().remove(handle);
}
AnimationFrameCallbackDriver& Window::animation_frame_callback_driver()
{
if (!m_animation_frame_callback_driver)
m_animation_frame_callback_driver = heap().allocate<AnimationFrameCallbackDriver>(realm());
return *m_animation_frame_callback_driver;
}
bool Window::has_animation_frame_callbacks()
{
if (!m_animation_frame_callback_driver)
return false;
return m_animation_frame_callback_driver->has_callbacks();
}
// https://w3c.github.io/requestidlecallback/#dom-window-requestidlecallback

View file

@ -16,7 +16,6 @@
#include <LibWeb/Bindings/WindowGlobalMixin.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/AnimationFrameCallbackDriver.h>
#include <LibWeb/HTML/CrossOrigin/CrossOriginPropertyDescriptorMap.h>
#include <LibWeb/HTML/GlobalEventHandlers.h>
#include <LibWeb/HTML/MimeType.h>
@ -110,8 +109,6 @@ public:
};
WebIDL::ExceptionOr<OpenedWindow> window_open_steps_internal(StringView url, StringView target, StringView features);
bool has_animation_frame_callbacks() const { return m_animation_frame_callback_driver.has_callbacks(); }
DOM::Event* current_event() { return m_current_event.ptr(); }
DOM::Event const* current_event() const { return m_current_event.ptr(); }
void set_current_event(DOM::Event* event);
@ -125,8 +122,6 @@ public:
void start_an_idle_period();
AnimationFrameCallbackDriver& animation_frame_callback_driver() { return m_animation_frame_callback_driver; }
// https://html.spec.whatwg.org/multipage/interaction.html#sticky-activation
bool has_sticky_activation() const;
@ -211,7 +206,10 @@ public:
i32 outer_height() const;
double device_pixel_ratio() const;
WebIDL::UnsignedLong request_animation_frame(WebIDL::CallbackType&);
AnimationFrameCallbackDriver& animation_frame_callback_driver();
bool has_animation_frame_callbacks();
WebIDL::UnsignedLong request_animation_frame(JS::NonnullGCPtr<WebIDL::CallbackType>);
void cancel_animation_frame(WebIDL::UnsignedLong handle);
u32 request_idle_callback(WebIDL::CallbackType&, RequestIdleCallback::IdleRequestOptions const&);
@ -289,7 +287,7 @@ private:
// Each Window object is associated with a unique instance of a CustomElementRegistry object, allocated when the Window object is created.
JS::GCPtr<CustomElementRegistry> m_custom_element_registry;
AnimationFrameCallbackDriver m_animation_frame_callback_driver;
JS::GCPtr<AnimationFrameCallbackDriver> m_animation_frame_callback_driver;
// https://w3c.github.io/requestidlecallback/#dfn-list-of-idle-request-callbacks
Vector<NonnullRefPtr<IdleCallback>> m_idle_request_callbacks;

View file

@ -93,14 +93,14 @@ Response capture_element_screenshot(Painter const& painter, Page& page, DOM::Ele
return canvas;
};
(void)element.document().window()->animation_frame_callback_driver().add([&](auto) {
(void)element.document().window()->animation_frame_callback_driver().add(JS::create_heap_function(element.heap(), [&](double) {
auto canvas_or_error = draw_bounding_box_from_the_framebuffer();
if (canvas_or_error.is_error()) {
encoded_string_or_error = canvas_or_error.release_error();
return;
}
encoded_string_or_error = encode_canvas_element(canvas_or_error.release_value());
});
}));
Platform::EventLoopPlugin::the().spin_until(JS::create_heap_function(element.document().heap(), [&]() { return encoded_string_or_error.has_value(); }));
return encoded_string_or_error.release_value();