From bbfde63f79b62080ade37051fc11a61ca132184f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 3 Oct 2021 15:38:11 +0200 Subject: [PATCH] LibWeb: Only take runnable tasks from the HTML task queue We were previously willing to execute tasks before they had become runnable. --- .../Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp | 3 +-- Userland/Libraries/LibWeb/HTML/EventLoop/Task.cpp | 7 +++++++ Userland/Libraries/LibWeb/HTML/EventLoop/Task.h | 2 ++ .../Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp | 11 ++++++++++- Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h | 6 +++--- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp index a8eb9e43152..3989fc4b0da 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp @@ -90,9 +90,8 @@ void EventLoop::process() // 1. Let taskQueue be one of the event loop's task queues, chosen in an implementation-defined manner, with the constraint that the chosen task queue must contain at least one runnable task. If there is no such task queue, then jump to the microtasks step below. auto& task_queue = m_task_queue; - if (!task_queue.is_empty()) { + if (auto oldest_task = task_queue.take_first_runnable()) { // 2. Let oldestTask be the first runnable task in taskQueue, and remove it from taskQueue. - auto oldest_task = task_queue.take_first_runnable(); // 3. Set the event loop's currently running task to oldestTask. m_currently_running_task = oldest_task.ptr(); diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/Task.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/Task.cpp index ab282941aeb..375220bd656 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/Task.cpp +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/Task.cpp @@ -25,4 +25,11 @@ void Task::execute() m_steps(); } +// https://html.spec.whatwg.org/#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(); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h b/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h index 9b9402f3ccf..51947f58d36 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h @@ -40,6 +40,8 @@ public: DOM::Document* document() { return m_document; } DOM::Document const* document() const { return m_document; } + bool is_runnable() const; + private: Task(Source, DOM::Document*, Function steps); diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp index 50c8598d67e..51fb07df299 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp @@ -20,8 +20,17 @@ TaskQueue::~TaskQueue() void TaskQueue::add(NonnullOwnPtr task) { - m_tasks.enqueue(move(task)); + m_tasks.append(move(task)); m_event_loop.schedule(); } +OwnPtr TaskQueue::take_first_runnable() +{ + for (size_t i = 0; i < m_tasks.size(); ++i) { + if (m_tasks[i]->is_runnable()) + return m_tasks.take(i); + } + return nullptr; +} + } diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h b/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h index 68e971b3342..e7638b07bbe 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h @@ -19,20 +19,20 @@ public: bool is_empty() const { return m_tasks.is_empty(); } void add(NonnullOwnPtr); - OwnPtr take_first_runnable() { return m_tasks.dequeue(); } + OwnPtr take_first_runnable(); void enqueue(NonnullOwnPtr task) { add(move(task)); } OwnPtr dequeue() { if (m_tasks.is_empty()) return {}; - return m_tasks.dequeue(); + return m_tasks.take_first(); } private: HTML::EventLoop& m_event_loop; - Queue> m_tasks; + Vector> m_tasks; }; }