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

Reply via email to