mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-09 19:16:02 +00:00
LibWeb: Forbid interleaving execution of HTML tasks with same source
From HTML spec https://html.spec.whatwg.org/#definitions-3 "... Note that in this setup, the processing model still enforces that the user agent would never process events from any one task source out of order." I can't come up with an example that is fixed by this change. However, debugging a bug caused by violating this assumption from the spec is likely to be very painful.
This commit is contained in:
parent
ba633882bf
commit
664611bae4
Notes:
sideshowbarker
2024-07-16 22:51:10 +09:00
Author: https://github.com/kalenikaliaksandr
Commit: 664611bae4
Pull-request: https://github.com/SerenityOS/serenity/pull/23903
3 changed files with 46 additions and 1 deletions
|
@ -27,6 +27,9 @@ EventLoop::EventLoop()
|
|||
{
|
||||
m_task_queue = heap().allocate_without_realm<TaskQueue>(*this);
|
||||
m_microtask_queue = heap().allocate_without_realm<TaskQueue>(*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::WebEngineCustomData*>(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<size_t>(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<size_t>(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<size_t>(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<bool()> 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.
|
||||
|
|
|
@ -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<bool()> goal_condition);
|
||||
void spin_processing_tasks_with_source_until(Task::Source, NOESCAPE JS::SafeFunction<bool()> goal_condition);
|
||||
void process();
|
||||
|
@ -113,6 +117,8 @@ private:
|
|||
bool m_execution_paused { false };
|
||||
|
||||
bool m_skip_event_loop_processing_steps { false };
|
||||
|
||||
Array<bool, to_underlying(Task::Source::UniqueTaskSourceStart)> m_blocked_task_sources;
|
||||
};
|
||||
|
||||
EventLoop& main_thread_event_loop();
|
||||
|
|
|
@ -39,7 +39,10 @@ JS::GCPtr<Task> 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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue