mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-04-19 02:10:26 +00:00
In `::spin_processing_tasks_with_source_until()`, we would first take a set of tasks based on a filter, and then run them one by one. If there was more than one task matched and put in that vector, they could interfere with each other's runnability by making later tasks permanently unrunnable. The `::take_tasks_matching()` API is a footgun - remove it in favor of an API that takes tasks one by one, performing the runnability check just in time.
115 lines
2.7 KiB
C++
115 lines
2.7 KiB
C++
/*
|
|
* Copyright (c) 2021-2025, Andreas Kling <andreas@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibGC/RootVector.h>
|
|
#include <LibWeb/DOM/Document.h>
|
|
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
|
#include <LibWeb/HTML/EventLoop/TaskQueue.h>
|
|
|
|
namespace Web::HTML {
|
|
|
|
GC_DEFINE_ALLOCATOR(TaskQueue);
|
|
|
|
TaskQueue::TaskQueue(HTML::EventLoop& event_loop)
|
|
: m_event_loop(event_loop)
|
|
{
|
|
}
|
|
|
|
TaskQueue::~TaskQueue() = default;
|
|
|
|
void TaskQueue::visit_edges(Visitor& visitor)
|
|
{
|
|
Base::visit_edges(visitor);
|
|
visitor.visit(m_event_loop);
|
|
visitor.visit(m_tasks);
|
|
}
|
|
|
|
void TaskQueue::add(GC::Ref<Task> task)
|
|
{
|
|
// AD-HOC: Don't enqueue tasks for temporary (inert) documents used for fragment parsing.
|
|
// FIXME: There's ongoing spec work to remove such documents: https://github.com/whatwg/html/pull/11970
|
|
if (task->document() && task->document()->is_temporary_document_for_fragment_parsing())
|
|
return;
|
|
|
|
m_tasks.append(task);
|
|
m_event_loop->schedule();
|
|
}
|
|
|
|
GC::Ptr<Task> TaskQueue::take_first_runnable()
|
|
{
|
|
if (m_event_loop->execution_paused())
|
|
return nullptr;
|
|
|
|
for (size_t i = 0; i < m_tasks.size();) {
|
|
if (m_event_loop->running_rendering_task() && m_tasks[i]->source() == Task::Source::Rendering) {
|
|
++i;
|
|
continue;
|
|
}
|
|
|
|
if (m_tasks[i]->is_runnable())
|
|
return m_tasks.take(i);
|
|
|
|
if (m_tasks[i]->is_permanently_unrunnable()) {
|
|
m_tasks.remove(i);
|
|
continue;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool TaskQueue::has_runnable_tasks() const
|
|
{
|
|
if (m_event_loop->execution_paused())
|
|
return false;
|
|
|
|
for (auto& task : m_tasks) {
|
|
if (m_event_loop->running_rendering_task() && task->source() == Task::Source::Rendering)
|
|
continue;
|
|
if (task->is_runnable())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TaskQueue::remove_tasks_matching(Function<bool(HTML::Task const&)> filter)
|
|
{
|
|
m_tasks.remove_all_matching(filter);
|
|
}
|
|
|
|
GC::Ptr<Task> TaskQueue::take_first_runnable_matching(Function<bool(HTML::Task const&)> filter)
|
|
{
|
|
for (size_t i = 0; i < m_tasks.size();) {
|
|
auto& task = m_tasks.at(i);
|
|
|
|
if (task->is_runnable() && filter(*task))
|
|
return m_tasks.take(i);
|
|
|
|
if (task->is_permanently_unrunnable()) {
|
|
m_tasks.remove(i);
|
|
continue;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Task const* TaskQueue::last_added_task() const
|
|
{
|
|
if (m_tasks.is_empty())
|
|
return nullptr;
|
|
return m_tasks.last();
|
|
}
|
|
|
|
bool TaskQueue::has_rendering_tasks() const
|
|
{
|
|
return m_tasks.contains([](auto const& task) { return task->source() == Task::Source::Rendering; });
|
|
}
|
|
|
|
}
|