Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 85dc29aff870bcdfb8cb451f463ef0c33628870c
https://github.com/WebKit/WebKit/commit/85dc29aff870bcdfb8cb451f463ef0c33628870c
Author: Ryan Reno <[email protected]>
Date: 2025-10-22 (Wed, 22 Oct 2025)
Changed paths:
A
LayoutTests/fast/dom/timer-in-microtask-does-not-inherit-nesting-level-expected.txt
A
LayoutTests/fast/dom/timer-in-microtask-does-not-inherit-nesting-level.html
A
LayoutTests/imported/w3c/web-platform-tests/html/webappapis/timers/timer-nesting-not-inherited-in-microtask-expected.txt
A
LayoutTests/imported/w3c/web-platform-tests/html/webappapis/timers/timer-nesting-not-inherited-in-microtask.html
M Source/WebCore/dom/EventLoop.cpp
M Source/WebCore/dom/Microtasks.h
M Source/WebCore/testing/Internals.cpp
M Source/WebCore/testing/Internals.h
M Source/WebCore/testing/Internals.idl
Log Message:
-----------
DOM timers installed during a microtask checkpoint should not inherit DOM
timer nesting level from the task scope.
rdar://162893146
https://bugs.webkit.org/show_bug.cgi?id=301008
Reviewed by Ryosuke Niwa.
DOM timers installed during microtask checkpoints are inheriting the nesting
level of the previously ran task.
Take, for example, this WPT-sourced code:
self.delay = ms => new Promise(resolve => step_timeout(resolve, ms));
self.flushAsyncEvents = () => delay(0).then(() => delay(0)).then(() =>
delay(0)).then(() => delay(0));
Where `step_timeout` is a wrapper around setTimeout.
Each of these promise resolutions will occur during a microtask checkpoint.
They install new zero delay DOM timers.
Currently WebKit will have them inherit the timer nesting level of the last
timer that was on the stack during
the last task that ran. However, this is incorrect according to the HTML spec.
Step 3 of the
"timer initialization steps" [0] states that we should set the nesting level
only if the currently executing task was
a task that was created using these initialization steps. For example:
setTimeout(() => setTimeout(callback, 0), 0);
The inner setTimeout would be nested as it is created while executing the task
for the outer setTimeout.
However, the promise resolution callbacks in the earlier example are run during
a microtask checkpoint after the DOM timer's
task finishes running. According to the "perform a microtask checkpoint" steps
[1] the Event Loop's current task is set to the
microtask that is about to execute, which means we are not fulfilling the
requirement in [0] to inherit the nesting level
and so we should consider these timeouts unnested.
The reason for this behavior is because DOMTimerFireState is a RAII class which
sets the ScriptExecutionContext's timer nesting
level for the entire scope of DOMTimer::fired. Since microtask checkpoints are
running after the task has completed, but before
returning to the DOMTimer::fired frame, all new timers installed during the
microtask get the nesting level +1 of the DOMTimerFireState.
This patch fixes this by introducing a new MicrotaskCheckpointScope object
which will reset the nesting level on entry and restore
the level on exit.
[0]
https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timer-initialisation-steps
[1]
https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
*
LayoutTests/fast/dom/timer-in-microtask-does-not-inherit-nesting-level-expected.txt:
Added.
* LayoutTests/fast/dom/timer-in-microtask-does-not-inherit-nesting-level.html:
Added.
Add tests to ensure DOM timer nesting works when rescheduling DOM timers
but the level
does not get inherited when a microtask is scheduling a DOM timer.
*
LayoutTests/imported/w3c/web-platform-tests/html/webappapis/timers/timer-nesting-not-inherited-in-microtask-expected.txt:
Added.
*
LayoutTests/imported/w3c/web-platform-tests/html/webappapis/timers/timer-nesting-not-inherited-in-microtask.html:
Added.
Add a similar test to the internal one, except this is testing the
web-observable behavior.
* Source/WebCore/dom/EventLoop.cpp:
(WebCore::EventLoop::performMicrotaskCheckpoint):
* Source/WebCore/dom/Microtasks.h:
Add a MicrotaskCheckpointScope object to save the state
of DOM timer nesting during a microtask checkpoint.
* Source/WebCore/testing/Internals.cpp:
(WebCore::Internals::timerNestingLevel):
* Source/WebCore/testing/Internals.h:
* Source/WebCore/testing/Internals.idl:
Expose ScriptExecutionContext::timerNestingLevel to LayoutTests.
Canonical link: https://commits.webkit.org/301979@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications