summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/HTML
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-09-26 14:36:20 +0200
committerAndreas Kling <kling@serenityos.org>2021-09-26 14:39:14 +0200
commita248ec63e31a3ff4079b40bb099d5ce9b92fbea1 (patch)
tree48bdf368f4e1db5661f14f2aeeb1eda46fd1dca4 /Userland/Libraries/LibWeb/HTML
parent831fdcaabc95817d4bff9c8bd8a6741227a0b371 (diff)
downloadserenity-a248ec63e31a3ff4079b40bb099d5ce9b92fbea1.zip
LibWeb: Implement window.queueMicrotask(callback)
This API allows authors to schedule a serialized JS callback that will get invoked at the next spec-allowed opportunity.
Diffstat (limited to 'Userland/Libraries/LibWeb/HTML')
-rw-r--r--Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp94
-rw-r--r--Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h11
-rw-r--r--Userland/Libraries/LibWeb/HTML/EventLoop/Task.h1
-rw-r--r--Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h8
4 files changed, 96 insertions, 18 deletions
diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
index dc1de61a03..dfe99831e1 100644
--- a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
+++ b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp
@@ -14,6 +14,7 @@ namespace Web::HTML {
EventLoop::EventLoop()
: m_task_queue(*this)
+ , m_microtask_queue(*this)
{
}
@@ -49,7 +50,12 @@ void EventLoop::spin_until(Function<bool()> goal_condition)
{
// FIXME: This is an ad-hoc hack until we implement the proper mechanism.
Core::EventLoop loop;
- loop.spin_until(move(goal_condition));
+ loop.spin_until([&]() -> bool {
+ if (goal_condition())
+ return true;
+ perform_a_microtask_checkpoint();
+ return goal_condition();
+ });
// Real spec steps:
@@ -84,27 +90,24 @@ 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())
- return;
-
- // 2. Let oldestTask be the first runnable task in taskQueue, and remove it from taskQueue.
- auto oldest_task = task_queue.take_first_runnable();
+ if (!task_queue.is_empty()) {
+ // 2. Let oldestTask be the first runnable task in taskQueue, and remove it from taskQueue.
+ auto oldest_task = task_queue.take_first_runnable();
- // FIXME: Figure out if we need to be here when there's no task.
- VERIFY(oldest_task);
+ // 3. Set the event loop's currently running task to oldestTask.
+ m_currently_running_task = oldest_task.ptr();
- // 3. Set the event loop's currently running task to oldestTask.
- m_currently_running_task = oldest_task.ptr();
+ // FIXME: 4. Let taskStartTime be the current high resolution time.
- // FIXME: 4. Let taskStartTime be the current high resolution time.
+ // 5. Perform oldestTask's steps.
+ oldest_task->execute();
- // 5. Perform oldestTask's steps.
- oldest_task->execute();
-
- // 6. Set the event loop's currently running task back to null.
- m_currently_running_task = nullptr;
+ // 6. Set the event loop's currently running task back to null.
+ m_currently_running_task = nullptr;
+ }
- // FIXME: 7. Microtasks: Perform a microtask checkpoint.
+ // 7. Microtasks: Perform a microtask checkpoint.
+ perform_a_microtask_checkpoint();
// 8. Let hasARenderingOpportunity be false.
[[maybe_unused]] bool has_a_rendering_opportunity = false;
@@ -174,7 +177,7 @@ void EventLoop::process()
// FIXME: 2. If there are no tasks in the event loop's task queues and the WorkerGlobalScope object's closing flag is true, then destroy the event loop, aborting these steps, resuming the run a worker steps described in the Web workers section below.
// If there are tasks in the queue, schedule a new round of processing. :^)
- if (!m_task_queue.is_empty())
+ if (!m_task_queue.is_empty() || !m_microtask_queue.is_empty())
schedule();
}
@@ -185,4 +188,59 @@ void queue_global_task(HTML::Task::Source source, DOM::Document& document, Funct
main_thread_event_loop().task_queue().add(HTML::Task::create(source, &document, move(steps)));
}
+// https://html.spec.whatwg.org/#queue-a-microtask
+void queue_a_microtask(DOM::Document& document, Function<void()> steps)
+{
+ // 1. If event loop was not given, set event loop to the implied event loop.
+ auto& event_loop = HTML::main_thread_event_loop();
+
+ // FIXME: 2. If document was not given, set document to the implied document.
+
+ // 3. Let microtask be a new task.
+ // 4. Set microtask's steps to steps.
+ // 5. Set microtask's source to the microtask task source.
+ // 6. Set microtask's document to document.
+ auto microtask = HTML::Task::create(HTML::Task::Source::Microtask, &document, move(steps));
+
+ // FIXME: 7. Set microtask's script evaluation environment settings object set to an empty set.
+
+ // 8. Enqueue microtask on event loop's microtask queue.
+ event_loop.microtask_queue().enqueue(move(microtask));
+}
+
+// https://html.spec.whatwg.org/#perform-a-microtask-checkpoint
+void EventLoop::perform_a_microtask_checkpoint()
+{
+ // 1. If the event loop's performing a microtask checkpoint is true, then return.
+ if (m_performing_a_microtask_checkpoint)
+ return;
+
+ // 2. Set the event loop's performing a microtask checkpoint to true.
+ m_performing_a_microtask_checkpoint = true;
+
+ // 3. While the event loop's microtask queue is not empty:
+ while (!m_microtask_queue.is_empty()) {
+ // 1. Let oldestMicrotask be the result of dequeuing from the event loop's microtask queue.
+ auto oldest_microtask = m_microtask_queue.dequeue();
+
+ // 2. Set the event loop's currently running task to oldestMicrotask.
+ m_currently_running_task = oldest_microtask;
+
+ // 3. Run oldestMicrotask.
+ oldest_microtask->execute();
+
+ // 4. Set the event loop's currently running task back to null.
+ m_currently_running_task = nullptr;
+ }
+
+ // FIXME: 4. For each environment settings object whose responsible event loop is this event loop, notify about rejected promises on that environment settings object.
+
+ // FIXME: 5. Cleanup Indexed Database transactions.
+
+ // FIXME: 6. Perform ClearKeptObjects().
+
+ // 7. Set the event loop's performing a microtask checkpoint to false.
+ m_performing_a_microtask_checkpoint = false;
+}
+
}
diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h
index 62fcba9518..50b9246554 100644
--- a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h
+++ b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h
@@ -32,6 +32,9 @@ public:
TaskQueue& task_queue() { return m_task_queue; }
TaskQueue const& task_queue() const { return m_task_queue; }
+ TaskQueue& microtask_queue() { return m_microtask_queue; }
+ TaskQueue const& microtask_queue() const { return m_microtask_queue; }
+
void spin_until(Function<bool()> goal_condition);
void process();
@@ -44,10 +47,13 @@ public:
void schedule();
+ void perform_a_microtask_checkpoint();
+
private:
Type m_type { Type::Window };
TaskQueue m_task_queue;
+ TaskQueue m_microtask_queue;
// https://html.spec.whatwg.org/multipage/webappapis.html#currently-running-task
Task* m_currently_running_task { nullptr };
@@ -55,9 +61,14 @@ private:
JS::VM* m_vm { nullptr };
RefPtr<Core::Timer> m_system_event_loop_timer;
+
+ // https://html.spec.whatwg.org/#performing-a-microtask-checkpoint
+ bool m_performing_a_microtask_checkpoint { false };
};
EventLoop& main_thread_event_loop();
void queue_global_task(HTML::Task::Source, DOM::Document&, Function<void()> steps);
+void queue_a_microtask(DOM::Document&, Function<void()> steps);
+void perform_a_microtask_checkpoint();
}
diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h b/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h
index 76f9ac7704..36d9f30c5a 100644
--- a/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h
+++ b/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h
@@ -24,6 +24,7 @@ public:
HistoryTraversal,
IdleTask,
PostedMessage,
+ Microtask,
};
static NonnullOwnPtr<Task> create(Source source, DOM::Document* document, Function<void()> steps)
diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h b/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h
index 49a8e1c061..68e971b334 100644
--- a/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h
+++ b/Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h
@@ -21,6 +21,14 @@ public:
void add(NonnullOwnPtr<HTML::Task>);
OwnPtr<HTML::Task> take_first_runnable() { return m_tasks.dequeue(); }
+ void enqueue(NonnullOwnPtr<HTML::Task> task) { add(move(task)); }
+ OwnPtr<HTML::Task> dequeue()
+ {
+ if (m_tasks.is_empty())
+ return {};
+ return m_tasks.dequeue();
+ }
+
private:
HTML::EventLoop& m_event_loop;