diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp index 27a8a0d4424..c17ef937335 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp @@ -27,6 +27,9 @@ EventLoop::EventLoop() { m_task_queue = heap().allocate_without_realm(*this); m_microtask_queue = heap().allocate_without_realm(*this); + + for (size_t i = 0; i < m_blocked_task_sources.size(); ++i) + m_blocked_task_sources[i] = false; } EventLoop::~EventLoop() = default; @@ -59,6 +62,31 @@ EventLoop& main_thread_event_loop() return *static_cast(Bindings::main_thread_vm().custom_data())->event_loop; } +bool EventLoop::is_task_source_blocked(Task::Source source) const +{ + if (source == Task::Source::Unspecified) + return false; + if (static_cast(to_underlying(source)) < m_blocked_task_sources.size()) + return m_blocked_task_sources[to_underlying(source)]; + return false; +} + +void EventLoop::block_task_source(Task::Source source) +{ + if (source == Task::Source::Unspecified) + return; + if (static_cast(to_underlying(source)) < m_blocked_task_sources.size()) + m_blocked_task_sources[to_underlying(source)] = true; +} + +void EventLoop::unblock_task_source(Task::Source source) +{ + if (source == Task::Source::Unspecified) + return; + if (static_cast(to_underlying(source)) < m_blocked_task_sources.size()) + m_blocked_task_sources[to_underlying(source)] = false; +} + // https://html.spec.whatwg.org/multipage/webappapis.html#spin-the-event-loop void EventLoop::spin_until(JS::SafeFunction goal_condition) { @@ -119,11 +147,13 @@ void EventLoop::spin_processing_tasks_with_source_until(Task::Source source, JS: return task.source() == source && task.is_runnable(); }); + block_task_source(source); for (auto& task : tasks) { m_currently_running_task = task.ptr(); task->execute(); m_currently_running_task = nullptr; } + unblock_task_source(source); } // FIXME: Remove the platform event loop plugin so that this doesn't look out of place @@ -164,6 +194,8 @@ void EventLoop::process() oldest_task = task_queue.take_first_runnable(); if (oldest_task) { + block_task_source(oldest_task->source()); + // 5. Set the event loop's currently running task to oldestTask. m_currently_running_task = oldest_task.ptr(); @@ -172,6 +204,8 @@ void EventLoop::process() // 7. Set the event loop's currently running task back to null. m_currently_running_task = nullptr; + + unblock_task_source(oldest_task->source()); } // 8. Microtasks: Perform a microtask checkpoint. diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h index ebb4a794cdb..dae3a3b521e 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h @@ -39,6 +39,10 @@ public: TaskQueue& microtask_queue() { return *m_microtask_queue; } TaskQueue const& microtask_queue() const { return *m_microtask_queue; } + bool is_task_source_blocked(Task::Source source) const; + void block_task_source(Task::Source source); + void unblock_task_source(Task::Source source); + void spin_until(NOESCAPE JS::SafeFunction goal_condition); void spin_processing_tasks_with_source_until(Task::Source, NOESCAPE JS::SafeFunction goal_condition); void process(); @@ -113,6 +117,8 @@ private: bool m_execution_paused { false }; bool m_skip_event_loop_processing_steps { false }; + + Array m_blocked_task_sources; }; EventLoop& main_thread_event_loop(); diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp index 8cea970cc5f..bee975f1e9b 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp @@ -39,7 +39,10 @@ JS::GCPtr TaskQueue::take_first_runnable() return nullptr; for (size_t i = 0; i < m_tasks.size(); ++i) { - if (m_tasks[i]->is_runnable()) + auto const& task = m_tasks[i]; + if (m_event_loop->is_task_source_blocked(task->source())) + continue; + if (task->is_runnable()) return m_tasks.take(i); } return nullptr; @@ -51,6 +54,8 @@ bool TaskQueue::has_runnable_tasks() const return false; for (auto& task : m_tasks) { + if (m_event_loop->is_task_source_blocked(task->source())) + continue; if (task->is_runnable()) return true; }