LibWeb: Make Platform::Timer GC-allocated

This will allow us to remove the use of SafeFunction in it's
implementation. This requires a fair amount of plumbing to wire up the
GC heap to the appropriate places in order to create the timers.
This commit is contained in:
Shannon Booth 2024-10-30 21:37:08 +13:00 committed by Alexander Kalenik
commit ede3c91688
Notes: github-actions[bot] 2024-10-30 19:57:28 +00:00
15 changed files with 52 additions and 38 deletions

View file

@ -28,6 +28,14 @@ ImageStyleValue::ImageStyleValue(URL::URL const& url)
ImageStyleValue::~ImageStyleValue() = default; ImageStyleValue::~ImageStyleValue() = default;
void ImageStyleValue::visit_edges(JS::Cell::Visitor& visitor) const
{
// FIXME: visit_edges in non-GC allocated classes is confusing pattern.
// Consider making CSSStyleValue to be GC allocated instead.
visitor.visit(m_resource_request);
visitor.visit(m_timer);
}
void ImageStyleValue::load_any_resources(DOM::Document& document) void ImageStyleValue::load_any_resources(DOM::Document& document)
{ {
if (m_resource_request) if (m_resource_request)
@ -53,7 +61,7 @@ void ImageStyleValue::load_any_resources(DOM::Document& document)
auto image_data = m_resource_request->image_data(); auto image_data = m_resource_request->image_data();
if (image_data->is_animated() && image_data->frame_count() > 1) { if (image_data->is_animated() && image_data->frame_count() > 1) {
m_timer = Platform::Timer::create(); m_timer = Platform::Timer::create(m_document->heap());
m_timer->set_interval(image_data->frame_duration(0)); m_timer->set_interval(image_data->frame_duration(0));
m_timer->on_timeout = [this] { animate(); }; m_timer->on_timeout = [this] { animate(); };
m_timer->start(); m_timer->start();

View file

@ -28,12 +28,7 @@ public:
} }
virtual ~ImageStyleValue() override; virtual ~ImageStyleValue() override;
void visit_edges(JS::Cell::Visitor& visitor) const void visit_edges(JS::Cell::Visitor& visitor) const;
{
// FIXME: visit_edges in non-GC allocated classes is confusing pattern.
// Consider making CSSStyleValue to be GC allocated instead.
visitor.visit(m_resource_request);
}
virtual String to_string() const override; virtual String to_string() const override;
virtual bool equals(CSSStyleValue const& other) const override; virtual bool equals(CSSStyleValue const& other) const override;
@ -67,7 +62,7 @@ private:
size_t m_current_frame_index { 0 }; size_t m_current_frame_index { 0 };
size_t m_loops_completed { 0 }; size_t m_loops_completed { 0 };
RefPtr<Platform::Timer> m_timer; JS::GCPtr<Platform::Timer> m_timer;
}; };
} }

View file

@ -48,12 +48,13 @@ void EventLoop::visit_edges(Visitor& visitor)
visitor.visit(m_currently_running_task); visitor.visit(m_currently_running_task);
visitor.visit(m_backup_incumbent_settings_object_stack); visitor.visit(m_backup_incumbent_settings_object_stack);
visitor.visit(m_rendering_task_function); visitor.visit(m_rendering_task_function);
visitor.visit(m_system_event_loop_timer);
} }
void EventLoop::schedule() void EventLoop::schedule()
{ {
if (!m_system_event_loop_timer) { if (!m_system_event_loop_timer) {
m_system_event_loop_timer = Platform::Timer::create_single_shot(0, [this] { m_system_event_loop_timer = Platform::Timer::create_single_shot(heap(), 0, [this] {
process(); process();
}); });
} }

View file

@ -96,7 +96,7 @@ private:
// https://html.spec.whatwg.org/multipage/webappapis.html#last-idle-period-start-time // https://html.spec.whatwg.org/multipage/webappapis.html#last-idle-period-start-time
double m_last_idle_period_start_time { 0 }; double m_last_idle_period_start_time { 0 };
RefPtr<Platform::Timer> m_system_event_loop_timer; JS::GCPtr<Platform::Timer> m_system_event_loop_timer;
// https://html.spec.whatwg.org/#performing-a-microtask-checkpoint // https://html.spec.whatwg.org/#performing-a-microtask-checkpoint
bool m_performing_a_microtask_checkpoint { false }; bool m_performing_a_microtask_checkpoint { false };

View file

@ -414,8 +414,9 @@ void ResourceLoader::load(LoadRequest& request, SuccessCallback success_callback
} }
if (timeout.has_value() && timeout.value() > 0) { if (timeout.has_value() && timeout.value() > 0) {
auto timer = Platform::Timer::create_single_shot(timeout.value(), nullptr); auto timer = Platform::Timer::create_single_shot(m_heap, timeout.value(), nullptr);
timer->on_timeout = [timer, protocol_request, timeout_callback = move(timeout_callback)] { timer->on_timeout = [timer, protocol_request, timeout_callback = move(timeout_callback)] {
(void)timer;
protocol_request->stop(); protocol_request->stop();
if (timeout_callback) if (timeout_callback)
timeout_callback(); timeout_callback();

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <AK/Forward.h> #include <AK/Forward.h>
#include <LibJS/Heap/GCPtr.h>
#include <LibJS/SafeFunction.h> #include <LibJS/SafeFunction.h>
#include <LibWeb/Forward.h> #include <LibWeb/Forward.h>
@ -21,7 +22,7 @@ public:
virtual void spin_until(JS::SafeFunction<bool()> goal_condition) = 0; virtual void spin_until(JS::SafeFunction<bool()> goal_condition) = 0;
virtual void deferred_invoke(ESCAPING JS::SafeFunction<void()>) = 0; virtual void deferred_invoke(ESCAPING JS::SafeFunction<void()>) = 0;
virtual NonnullRefPtr<Timer> create_timer() = 0; virtual JS::NonnullGCPtr<Timer> create_timer(JS::Heap&) = 0;
virtual void quit() = 0; virtual void quit() = 0;
}; };

View file

@ -25,9 +25,9 @@ void EventLoopPluginSerenity::deferred_invoke(JS::SafeFunction<void()> function)
Core::deferred_invoke(move(function)); Core::deferred_invoke(move(function));
} }
NonnullRefPtr<Timer> EventLoopPluginSerenity::create_timer() JS::NonnullGCPtr<Timer> EventLoopPluginSerenity::create_timer(JS::Heap& heap)
{ {
return TimerSerenity::create(); return TimerSerenity::create(heap);
} }
void EventLoopPluginSerenity::quit() void EventLoopPluginSerenity::quit()

View file

@ -17,7 +17,7 @@ public:
virtual void spin_until(JS::SafeFunction<bool()> goal_condition) override; virtual void spin_until(JS::SafeFunction<bool()> goal_condition) override;
virtual void deferred_invoke(JS::SafeFunction<void()>) override; virtual void deferred_invoke(JS::SafeFunction<void()>) override;
virtual NonnullRefPtr<Timer> create_timer() override; virtual JS::NonnullGCPtr<Timer> create_timer(JS::Heap&) override;
virtual void quit() override; virtual void quit() override;
}; };

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/NonnullRefPtr.h>
#include <LibWeb/Platform/EventLoopPlugin.h> #include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/Platform/Timer.h> #include <LibWeb/Platform/Timer.h>
@ -12,23 +11,23 @@ namespace Web::Platform {
Timer::~Timer() = default; Timer::~Timer() = default;
NonnullRefPtr<Timer> Timer::create() JS::NonnullGCPtr<Timer> Timer::create(JS::Heap& heap)
{ {
return EventLoopPlugin::the().create_timer(); return EventLoopPlugin::the().create_timer(heap);
} }
NonnullRefPtr<Timer> Timer::create_repeating(int interval_ms, JS::SafeFunction<void()>&& timeout_handler) JS::NonnullGCPtr<Timer> Timer::create_repeating(JS::Heap& heap, int interval_ms, JS::SafeFunction<void()>&& timeout_handler)
{ {
auto timer = EventLoopPlugin::the().create_timer(); auto timer = EventLoopPlugin::the().create_timer(heap);
timer->set_single_shot(false); timer->set_single_shot(false);
timer->set_interval(interval_ms); timer->set_interval(interval_ms);
timer->on_timeout = move(timeout_handler); timer->on_timeout = move(timeout_handler);
return timer; return timer;
} }
NonnullRefPtr<Timer> Timer::create_single_shot(int interval_ms, JS::SafeFunction<void()>&& timeout_handler) JS::NonnullGCPtr<Timer> Timer::create_single_shot(JS::Heap& heap, int interval_ms, JS::SafeFunction<void()>&& timeout_handler)
{ {
auto timer = EventLoopPlugin::the().create_timer(); auto timer = EventLoopPlugin::the().create_timer(heap);
timer->set_single_shot(true); timer->set_single_shot(true);
timer->set_interval(interval_ms); timer->set_interval(interval_ms);
timer->on_timeout = move(timeout_handler); timer->on_timeout = move(timeout_handler);

View file

@ -7,15 +7,18 @@
#pragma once #pragma once
#include <AK/RefCounted.h> #include <AK/RefCounted.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/SafeFunction.h> #include <LibJS/SafeFunction.h>
namespace Web::Platform { namespace Web::Platform {
class Timer : public RefCounted<Timer> { class Timer : public JS::Cell {
JS_CELL(Timer, JS::Cell);
public: public:
static NonnullRefPtr<Timer> create(); static JS::NonnullGCPtr<Timer> create(JS::Heap&);
static NonnullRefPtr<Timer> create_repeating(int interval_ms, JS::SafeFunction<void()>&& timeout_handler); static JS::NonnullGCPtr<Timer> create_repeating(JS::Heap&, int interval_ms, JS::SafeFunction<void()>&& timeout_handler);
static NonnullRefPtr<Timer> create_single_shot(int interval_ms, JS::SafeFunction<void()>&& timeout_handler); static JS::NonnullGCPtr<Timer> create_single_shot(JS::Heap&, int interval_ms, JS::SafeFunction<void()>&& timeout_handler);
virtual ~Timer(); virtual ~Timer();

View file

@ -7,12 +7,13 @@
#include "TimerSerenity.h" #include "TimerSerenity.h"
#include <AK/NonnullRefPtr.h> #include <AK/NonnullRefPtr.h>
#include <LibCore/Timer.h> #include <LibCore/Timer.h>
#include <LibJS/Heap/Heap.h>
namespace Web::Platform { namespace Web::Platform {
NonnullRefPtr<TimerSerenity> TimerSerenity::create() JS::NonnullGCPtr<TimerSerenity> TimerSerenity::create(JS::Heap& heap)
{ {
return adopt_ref(*new TimerSerenity); return heap.allocate_without_realm<TimerSerenity>();
} }
TimerSerenity::TimerSerenity() TimerSerenity::TimerSerenity()

View file

@ -6,15 +6,16 @@
#pragma once #pragma once
#include <AK/NonnullRefPtr.h>
#include <LibCore/Forward.h> #include <LibCore/Forward.h>
#include <LibWeb/Platform/Timer.h> #include <LibWeb/Platform/Timer.h>
namespace Web::Platform { namespace Web::Platform {
class TimerSerenity final : public Timer { class TimerSerenity final : public Timer {
JS_CELL(TimerSerenity, Timer);
public: public:
static NonnullRefPtr<TimerSerenity> create(); static JS::NonnullGCPtr<TimerSerenity> create(JS::Heap&);
virtual ~TimerSerenity(); virtual ~TimerSerenity();

View file

@ -876,11 +876,12 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::send(Optional<DocumentOrXMLHttpRequest
// 1. Wait until either reqs done flag is set or thiss timeout is not 0 and thiss timeout milliseconds have passed since now. // 1. Wait until either reqs done flag is set or thiss timeout is not 0 and thiss timeout milliseconds have passed since now.
// 2. If reqs done flag is unset, then set thiss timed out flag and terminate thiss fetch controller. // 2. If reqs done flag is unset, then set thiss timed out flag and terminate thiss fetch controller.
if (m_timeout != 0) { if (m_timeout != 0) {
auto timer = Platform::Timer::create_single_shot(m_timeout, nullptr); auto timer = Platform::Timer::create_single_shot(heap(), m_timeout, nullptr);
// NOTE: `timer` is kept alive by copying the NNRP into the lambda, incrementing its ref-count. // NOTE: `timer` is kept alive by capturing into the lambda for the GC to see
// NOTE: `this` and `request` is kept alive by Platform::Timer using JS::SafeFunction. // NOTE: `this` and `request` is kept alive by Platform::Timer using JS::SafeFunction.
timer->on_timeout = [this, request, timer]() { timer->on_timeout = [this, request, timer]() {
(void)timer;
if (!request->done()) { if (!request->done()) {
m_timed_out = true; m_timed_out = true;
m_fetch_controller->terminate(); m_fetch_controller->terminate();
@ -929,10 +930,11 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::send(Optional<DocumentOrXMLHttpRequest
bool did_time_out = false; bool did_time_out = false;
if (m_timeout != 0) { if (m_timeout != 0) {
auto timer = Platform::Timer::create_single_shot(m_timeout, nullptr); auto timer = Platform::Timer::create_single_shot(heap(), m_timeout, nullptr);
// NOTE: `timer` is kept alive by copying the NNRP into the lambda, incrementing its ref-count. // NOTE: `timer` is kept alive by capturing into the lambda for the GC to see
timer->on_timeout = [timer, &did_time_out]() { timer->on_timeout = [timer, &did_time_out]() {
(void)timer;
did_time_out = true; did_time_out = true;
}; };

View file

@ -53,11 +53,12 @@
namespace WebContent { namespace WebContent {
ConnectionFromClient::ConnectionFromClient(IPC::Transport transport) ConnectionFromClient::ConnectionFromClient(JS::Heap& heap, IPC::Transport transport)
: IPC::ConnectionFromClient<WebContentClientEndpoint, WebContentServerEndpoint>(*this, move(transport), 1) : IPC::ConnectionFromClient<WebContentClientEndpoint, WebContentServerEndpoint>(*this, move(transport), 1)
, m_heap(heap)
, m_page_host(PageHost::create(*this)) , m_page_host(PageHost::create(*this))
{ {
m_input_event_queue_timer = Web::Platform::Timer::create_single_shot(0, [this] { process_next_input_event(); }); m_input_event_queue_timer = Web::Platform::Timer::create_single_shot(m_heap, 0, [this] { process_next_input_event(); });
} }
ConnectionFromClient::~ConnectionFromClient() = default; ConnectionFromClient::~ConnectionFromClient() = default;

View file

@ -48,7 +48,7 @@ public:
Function<void(IPC::File const&)> on_image_decoder_connection; Function<void(IPC::File const&)> on_image_decoder_connection;
private: private:
explicit ConnectionFromClient(IPC::Transport); explicit ConnectionFromClient(JS::Heap&, IPC::Transport);
Optional<PageClient&> page(u64 index, SourceLocation = SourceLocation::current()); Optional<PageClient&> page(u64 index, SourceLocation = SourceLocation::current());
Optional<PageClient const&> page(u64 index, SourceLocation = SourceLocation::current()) const; Optional<PageClient const&> page(u64 index, SourceLocation = SourceLocation::current()) const;
@ -150,6 +150,7 @@ private:
void report_finished_handling_input_event(u64 page_id, Web::EventResult event_was_handled); void report_finished_handling_input_event(u64 page_id, Web::EventResult event_was_handled);
JS::Heap& m_heap;
NonnullOwnPtr<PageHost> m_page_host; NonnullOwnPtr<PageHost> m_page_host;
HashMap<int, Web::FileRequest> m_requested_files {}; HashMap<int, Web::FileRequest> m_requested_files {};
@ -166,7 +167,7 @@ private:
Queue<QueuedInputEvent> m_input_event_queue; Queue<QueuedInputEvent> m_input_event_queue;
RefPtr<Web::Platform::Timer> m_input_event_queue_timer; JS::Handle<Web::Platform::Timer> m_input_event_queue_timer;
}; };
} }