ladybird/Libraries/LibWeb/HTML/EventLoop/Task.cpp
Jelle Raaijmakers c8baa6e179 LibWeb: Remove tasks for destroyed documents instead of running them
Previously, destroyed-document tasks were forced to be runnable to
prevent them from leaking in the task queue. Instead, discard them
during task selection so their callbacks never run with stale state.

This used to cause issues with a couple of `spin_until()`s in the past,
but since we've removed some of them that had to do with the document
lifecycle, let's see if we can stick closer to the spec now.
2026-03-19 15:24:46 -05:00

85 lines
2 KiB
C++

/*
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/IDAllocator.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/EventLoop/Task.h>
namespace Web::HTML {
GC_DEFINE_ALLOCATOR(Task);
static IDAllocator s_unique_task_source_allocator { static_cast<int>(Task::Source::UniqueTaskSourceStart) };
[[nodiscard]] static TaskID allocate_task_id()
{
static u64 next_task_id = 1;
return next_task_id++;
}
GC::Ref<Task> Task::create(JS::VM& vm, Source source, GC::Ptr<DOM::Document const> document, GC::Ref<GC::Function<void()>> steps)
{
return vm.heap().allocate<Task>(source, document, move(steps));
}
Task::Task(Source source, GC::Ptr<DOM::Document const> document, GC::Ref<GC::Function<void()>> steps)
: m_id(allocate_task_id())
, m_source(source)
, m_steps(steps)
, m_document(document)
{
}
Task::~Task() = default;
void Task::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_steps);
visitor.visit(m_document);
}
void Task::execute()
{
m_steps->function()();
}
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-task-runnable
bool Task::is_runnable() const
{
// A task is runnable if its document is either null or fully active.
return !m_document || m_document->is_fully_active();
}
DOM::Document const* Task::document() const
{
return m_document.ptr();
}
UniqueTaskSource::UniqueTaskSource()
: source(static_cast<Task::Source>(s_unique_task_source_allocator.allocate()))
{
}
UniqueTaskSource::~UniqueTaskSource()
{
s_unique_task_source_allocator.deallocate(static_cast<int>(source));
}
NonnullRefPtr<ParallelQueue> ParallelQueue::create()
{
return adopt_ref(*new (nothrow) ParallelQueue);
}
TaskID ParallelQueue::enqueue(GC::Ref<GC::Function<void()>> algorithm)
{
auto& event_loop = HTML::main_thread_event_loop();
auto task = HTML::Task::create(event_loop.vm(), m_task_source.source, nullptr, algorithm);
event_loop.task_queue().add(task);
return task->id();
}
}