Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

requestIdleCallback: Explicit algorithm for idle callback "deadline" #7166

Merged
merged 18 commits into from
Nov 15, 2021
156 changes: 135 additions & 21 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -2186,6 +2186,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<dfn data-x-href="https://infra.spec.whatwg.org/#string-position-variable">position variable</dfn></li>
<li><dfn data-x-href="https://infra.spec.whatwg.org/#skip-ascii-whitespace">skip ASCII whitespace</dfn></li>
<li>The <dfn data-x-href="https://infra.spec.whatwg.org/#ordered-map">ordered map</dfn> data structure and the associated definitions for
<dfn data-x="map key" data-x-href="https://infra.spec.whatwg.org/#map-key">key</dfn>,
<dfn data-x="map value" data-x-href="https://infra.spec.whatwg.org/#map-value">value</dfn>,
<dfn data-x="map empty" data-x-href="https://infra.spec.whatwg.org/#map-is-empty">empty</dfn>,
<dfn data-x="map entry" data-x-href="https://infra.spec.whatwg.org/#map-entry">entry</dfn>,
Expand All @@ -2195,6 +2196,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<dfn data-x="map remove" data-x-href="https://infra.spec.whatwg.org/#map-remove">removing an entry</dfn>,
<dfn data-x="map clear" data-x-href="https://infra.spec.whatwg.org/#map-clear">clear</dfn>,
<dfn data-x="map get the keys" data-x-href="https://infra.spec.whatwg.org/#map-getting-the-keys">getting the keys</dfn>,
<dfn data-x="map get the values" data-x-href="https://infra.spec.whatwg.org/#map-getting-the-values">getting the values</dfn>,
<dfn data-x="map size" data-x-href="https://infra.spec.whatwg.org/#map-size">size</dfn>, and
<dfn data-x="map iterate" data-x-href="https://infra.spec.whatwg.org/#map-iterate">iterate</dfn></li>
<li>The <dfn data-x-href="https://infra.spec.whatwg.org/#list">list</dfn> data structure and the associated definitions for
Expand Down Expand Up @@ -88686,6 +88688,26 @@ new PaymentRequest(&hellip;); // Allowed to use
then:</p>

<ol>
<li>
<p>If <var>newDocument</var>'s <span>suspended timer handles</span> is not
<span data-x="list empty">empty</span>:</p>

<ol>
<li><p>Assert: <var>newDocument</var>'s <span>suspension time</span> is not zero.</p></li>

<li><p>Let <var>suspendDuration</var> be the <span>current high resolution time</span> minus
<var>newDocument</var>'s <span>suspension time</span>.</p></li>

<li><p>Let <var>activeTimers</var> be <var>newDocument</var>'s
<span>relevant global object</span>'s <span>map of active timers</span>.</p></li>

<li><p>For each <var>handle</var> in <var>newDocument</var>'s <span>suspended timer
handles</span>, if <var>activeTimers</var>[<var>handle</var>] <span data-x="map
exists">exists</span>, then increase <var>activeTimers</var>[<var>handle</var>] by
<var>suspendDuration</var>.</p></li>
</ol>
</li>

<li><p>Remove any <span data-x="concept-task">tasks</span> queued by the <span>history traversal
task source</span> that are associated with any <code>Document</code> objects in the
<span>top-level browsing context</span>'s <span>document family</span>.</p></li> <!-- so the
Expand Down Expand Up @@ -89122,6 +89144,12 @@ dictionary <dfn dictionary>PageTransitionEventInit</dfn> : <span>EventInit</span
data-x="event-pagehide">pagehide</code> events in a row without an intervening <code
data-x="event-pageshow">pageshow</code>, or vice versa).</p>

<p>A <code>Document</code> has a <code>DOMHighResTimeStamp</code> <dfn>suspension time</dfn>,
initially 0.</p>

<p>A <code>Document</code> has a <span>list</span> of <dfn>suspended timer handles</dfn>,
initially empty.</p>

<p><span data-x="event loop">Event loops</span> have a <dfn>termination nesting level</dfn>
counter, which must initially be 0.</p>

Expand Down Expand Up @@ -89254,6 +89282,13 @@ dictionary <dfn dictionary>PageTransitionEventInit</dfn> : <span>EventInit</span
<li><p>Decrease the <span>event loop</span>'s <span>termination nesting level</span> by
one.</p></li>

<li><p>Set <var>document</var>'s <span>suspension time</span> to the
<span>current high resolution time</span>.</p></li>

<li><p>Set <var>document</var>'s <span>suspended timer handles</span> to the result of
<span data-x="map get the keys">getting the keys</span> for the
<span>map of active timers</span>.</p></li>

<li><p>Run any <span>unloading document cleanup steps</span> for <var>document</var> that are
defined by this specification and <span>other applicable specifications</span>.</p></li>

Expand Down Expand Up @@ -89311,7 +89346,8 @@ dictionary <dfn dictionary>PageTransitionEventInit</dfn> : <span>EventInit</span
data-x="concept-EventSource-forcibly-close">forcibly close</span>
<var>eventSource</var>.</p></li>

<li><p>Empty <var>window</var>'s <span>list of active timers</span>.</p></li>
<li><p><span data-x="map clear">Clear</span> <var>window</var>'s
<span>map of active timers</span>.</p></li>
</ol>
</li>
</ol>
Expand Down Expand Up @@ -93525,6 +93561,16 @@ import "https://example.com/foo/../module2.mjs";</code></pre>
which is initially false. It is used to prevent reentrant invocation of the <span>perform a
microtask checkpoint</span> algorithm.</p>
domenic marked this conversation as resolved.
Show resolved Hide resolved

<p>Each <span>window event loop</span> has a <code>DOMHighResTimeStamp</code>
<dfn>last render opportunity time</dfn>, initially set to zero.</p>

<p>Each <span>window event loop</span> has a <code>DOMHighResTimeStamp</code>
<dfn>last idle period start time</dfn>, initially set to zero.</p>

domenic marked this conversation as resolved.
Show resolved Hide resolved
<p>To get the <dfn>same-loop windows</dfn> for a <span>window event loop</span> <var>loop</var>,
return all <code>Window</code> objects whose <span>relevant agent</span>'s
noamr marked this conversation as resolved.
Show resolved Hide resolved
<span data-x="concept-agent-event-loop">event loop</span> is <var>loop</var>.</p>

domenic marked this conversation as resolved.
Show resolved Hide resolved

<h5>Queuing tasks</h5>

Expand Down Expand Up @@ -93781,7 +93827,8 @@ import "https://example.com/foo/../module2.mjs";</code></pre>
</li>

<li><p>If <var>docs</var> is not empty, then set <var>hasARenderingOpportunity</var> to
true.</p></li>
true and set this <span>event loop</span>'s <span>last render opportunity time</span> to
<var>taskStartTime</var>.</p></li>

<li>
<p><i>Unnecessary rendering</i>: Remove from <var>docs</var> all <code>Document</code> objects
Expand Down Expand Up @@ -93907,10 +93954,70 @@ import "https://example.com/foo/../module2.mjs";</code></pre>
<li><var>hasARenderingOpportunity</var> is false</li>
</ul>

<p>then for each <code>Window</code> object whose <span>relevant agent</span>'s
<span data-x="concept-agent-event-loop">event loop</span> is this event loop, run the
<span>start an idle period algorithm</span>, passing the <code>Window</code>. <ref
spec="REQUESTIDLECALLBACK"></p>
<p>then:

<ol>
<li>
<p>Let <var>computeDeadline</var> be the following steps:</p>

<ol>
<li>
<p>Let <var>deadline</var> be this <span>event loop</span>'s
<span>last idle period start time</span> plus 50.</p>

<p class="note">The cap of 50ms in the future is to ensure responsiveness to new user input
within the threshold of human perception.</p>
</li>

<li><p>Let <var>hasPendingRenders</var> be false.</p></li>

<li>
<p>For each <var>windowInSameLoop</var> of the <span>same-loop windows</span>
for this <span>event loop</span>:</p>

<ol>
<li><p>If <var>windowInSameLoop</var>'s <span>map of animation frame callbacks</span> is
not <span data-x="map empty">empty</span>, or if the user agent believes that the
<var>windowInSameLoop</var> might have pending rendering updates, set
<var>hasPendingRenders</var> to true.</p></li>

<li><p>Let <var>timerCallbackEstimates</var> be the result of
<span data-x="map get the values">getting the values</span> of
<var>windowInSameLoop</var>'s <span>map of active timers</span>.</p></li>

<li><p>For each <var>timeoutDeadline</var> of <var>timerCallbackEstimates</var>, if
<var>timeoutDeadline</var> is less than <var>deadline</var>, set
<var>deadline</var> to <var>timeoutDeadline</var>.</p></li>
</ol>
</li>

<li>
<p>If <var>hasPendingRenders</var> is true, then:</p>

<ol>
<li>
<p>Let <var>nextRenderDeadline</var> be this <span>event loop</span>'s
<span>last render opportunity time</span> plus (1000 divided by the current refresh
rate).</p>

<p>The refresh rate can be hardware- or implementation-specific. For a refresh rate of
60Hz, the <var>nextRenderDeadline</var> would be about 16.67ms after the
<span>last render opportunity time</span>.</p>
</li>

<li><p>If <var>nextRenderDeadline</var> is less than
<var>deadline</var>, then return <var>nextRenderDeadline</var>.</p></li>
</ol>
</li>

<li><p>Return <var>deadline</var>.</p></li>
</ol>
</li>

<li><p>For each <var>win</var> of the <span>same-loop windows</span> for
this <span>event loop</span>, perform the <span>start an idle period algorithm</span> for
<var>win</var> with <var>computeDeadline</var>. <ref spec=REQUESTIDLECALLBACK></p></li>
</ol>
</li>

<li>
Expand Down Expand Up @@ -96326,9 +96433,15 @@ enum <dfn enum>DOMParserSupportedType</dfn> {
<div w-nodev>

<p>Objects that implement the <code>WindowOrWorkerGlobalScope</code> mixin have a <dfn
export>list of active timers</dfn>. Each entry in this lists is identified by a number, which must
be unique within the list for the lifetime of the object that implements the
<code>WindowOrWorkerGlobalScope</code> mixin.</p>
export>map of active timers</dfn>, which is a <span>map</span>, initially empty. Each
<span data-x="map key">key</span> in this map is identified by a number, which must be unique
within the list for the lifetime of the object that implements the
<code>WindowOrWorkerGlobalScope</code> mixin, and each <span data-x="map value">value</span> is a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step conflicts with the existing (and now kinda broken) step 3.

I think the correct fix is just to delete step 3. But I wanted you to double-check the interaction with previous handle, which is used for setInterval.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checked and deleted step 3, it's a leftover. Interaction with previous handle which is used for setInterval is fine... with every interval the target timestamp would be updated and the map entry would be set to that new timestamp.

<code>DOMHighResTimeStamp</code>, representing the expiry time for that timer.</p>

<p>To get the <dfn export>list of active timers</dfn> for <code>WindowOrWorkerGlobalScope</code>
<var>global</var>, return the result of <span data-x="map get the keys">getting the keys</span>
for <var>global</var>'s <span>map of active timers</span>.</p>

<hr>

Expand All @@ -96352,14 +96465,12 @@ enum <dfn enum>DOMParserSupportedType</dfn> {
id="dom-windowtimers-clearTimeout">clearTimeout(<var>handle</var>)</code></dfn> and <dfn method
for="WindowOrWorkerGlobalScope" data-x="dom-clearInterval"><code
id="dom-windowtimers-clearInterval">clearInterval(<var>handle</var>)</code></dfn> methods must
clear the entry identified as <var>handle</var> from the <span>list of active timers</span> of the
<code>WindowOrWorkerGlobalScope</code> object on which the method was invoked, if any, where
<var>handle</var> is the argument passed to the method. (If <var>handle</var> does not identify an
entry in the <span>list of active timers</span> of the <code>WindowOrWorkerGlobalScope</code>
object on which the method was invoked, the method does nothing.)</p>
<span data-x="map remove">remove</span> <span>map of active timers</span>[<var>handle</var>] of
the <code>WindowOrWorkerGlobalScope</code> object on which the method was invoked, if any, where
<var>handle</var> is the argument passed to the method.</p>

<p class="note">Because <code data-x="dom-clearTimeout">clearTimeout()</code> and <code
data-x="dom-clearInterval">clearInterval()</code> clear entries from the same list, either method
data-x="dom-clearInterval">clearInterval()</code> clear entries from the same map, either method
can be used to clear timers created by <code data-x="dom-setTimeout">setTimeout()</code> or <code
data-x="dom-setInterval">setInterval()</code>.</p>

Expand All @@ -96380,9 +96491,6 @@ enum <dfn enum>DOMParserSupportedType</dfn> {
that is greater than zero that will identify the timeout to be set by this call in the <span>list
of active timers</span>.</p></li>

<li><p>If <var>previous handle</var> was not provided, add an entry to the <span>list of
active timers</span> for <var>handle</var>.</p></li>

<li><p>Let <var>callerRealm</var> be the <span>current Realm Record</span>, and
<var>calleeRealm</var> be <var>method context</var>'s <span>JavaScript realm</span>.</p></li>

Expand All @@ -96396,8 +96504,8 @@ enum <dfn enum>DOMParserSupportedType</dfn> {
following substeps:</p>

<ol>
<li><p>If the entry for <var>handle</var> in the <span>list of active timers</span> has been
cleared, then abort these steps.</p></li>
<li><p>If <var>handle</var> does not <span data-x="map exists">exist</span> in
<var>method context</var>'s <span>map of active timers</span>, then abort these steps.</p></li>

<li>
<p>Run the appropriate set of steps from the following list:</p>
Expand Down Expand Up @@ -96494,6 +96602,11 @@ enum <dfn enum>DOMParserSupportedType</dfn> {
<li><p>Let <var>task</var>'s <dfn>timer nesting level</dfn> be <var>nesting
level</var>.</p></li>

<li><p>Let <var>startTime</var> be the <span>current high resolution time</span>.</p></li>

<li><p><span data-x="map set">Set</span> <span>map of active timers</span>[<var>handle</var>] to
<var>startTime</var> plus <var>timeout</var>.</p></li>

<li><p>Return <var>handle</var>, and then continue running this algorithm
<span>in parallel</span>.</p></li>

Expand Down Expand Up @@ -102601,7 +102714,8 @@ interface <dfn interface>SharedWorkerGlobalScope</dfn> : <span>WorkerGlobalScope
</li>

<li>
<p>Empty the <var>worker global scope</var>'s <span>list of active timers</span>.</p>
<p><span data-x="map clear">Clear</span> the <var>worker global scope</var>'s
<span>map of active timers</span>.</p>
</li>

<li>
Expand Down