Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 64 additions & 31 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -80190,17 +80190,18 @@ interface <dfn interface>VisibilityStateEntry</dfn> : <span>PerformanceEntry</sp

<h4 id="user-activation-data-model">Data model</h4>

<p>For the purpose of tracking user activation, each <code>Window</code> <var>W</var> has two
relevant values:</p>
<p>For the purpose of tracking user activation, each <code>Window</code> <var>W</var> has the
following relevant values:</p>

<ul>
<li><p>A <dfn>last activation timestamp</dfn>, which is either a
<code>DOMHighResTimeStamp</code>, positive infinity (indicating that <var>W</var> has never been
activated), or negative infinity (indicating that the activation has been <span data-x="consume
user activation">consumed</span>). Initially positive infinity.</p></li>

<li><p>A <dfn>last history-action activation timestamp</dfn>, which is either a
<code>DOMHighResTimeStamp</code> or positive infinity, initially positive infinity.</p></li>
<li><p>A <dfn>has history-action activation</dfn> boolean, initially false.</p></li>

<li><p>A <dfn>has sticky activation</dfn> boolean, initially false.</p></li>
</ul>

<p>A user agent also defines a <dfn>transient activation duration</dfn>, which is a constant
Expand All @@ -80216,13 +80217,14 @@ interface <dfn interface>VisibilityStateEntry</dfn> : <span>PerformanceEntry</sp
<dl>
<dt><dfn export>Sticky activation</dfn></dt>
<dd>
<p>When the <span>current high resolution time</span> given <var>W</var> is greater than or
equal to the <span>last activation timestamp</span> in <var>W</var>, <var>W</var> is said to
have <span>sticky activation</span>.</p>
<p>When <var>W</var>'s <span>has sticky activation</span> is true, <var>W</var> is said to have
<span>sticky activation</span>.</p>

<p>This is <var>W</var>'s historical activation state, indicating whether the user has ever
interacted in <var>W</var>. It starts false, then changes to true (and never changes back to
false) when <var>W</var> gets the very first <span>activation notification</span>.</p>
false) when <var>W</var> gets the very first <span>activation notification</span>. It is also
carried over between windows for same-origin navigations and traversals (including <a
Copy link

@smaug---- smaug---- Aug 21, 2025

Choose a reason for hiding this comment

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

Hmm, is this quite right? If there is a bfcached page without sticky activation, I don't think the algorithms will set the flag on those window objects if another same origin window gets sticky activation. And I'm not sure what behavior we want there.

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, that's a great catch! I think we should carry it over to same-origin bfcached documents too, to minimize differences between cases where the bfcache is hit vs. missed.

I'll add a line to the "reactivate" algorithm similar to the one I added to the "create and initialize a new Document" algorithm.

Copy link
Member Author

Choose a reason for hiding this comment

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

This ended up being more annoying than I'd prefer, as threading things from the predecessor document to the new document seems to be surprisingly unusual. (In particular, if the browser plans to unload and destroy the previous document, we need to grab the state before it does that.)

I have a half-finished local branch with an alternate option, which, at the time we set sticky activation for one window, immediately tries to propagate it to all contiguous same-origin bfcached windows in the same navigable. But I realized that keeping track of "contiguous" would add a good amount of complexity (albeit only locally), and this probably would not be how implementations do it, so I stashed that.

Of course, there's a separate issue here where the whole user activation framework ignores the complexities of propagating the bit across processes, instead just letting people access the Window object from anywhere. That is fairly pervasive in the spec ecosystem though. (That is, although specs these days are relatively good about separating out processes, the rarer cases like this one where we need to propagate state so that it lives in multiple processes are all hand-waved. See w3c/ServiceWorker#1755 (comment) for more rambling.)

href="#note-user-activation-bfcache-carry-over">during reactivation from bfcache</a>).</p>
</dd>

<dt><dfn export>Transient activation</dfn></dt>
Expand All @@ -80245,9 +80247,8 @@ interface <dfn interface>VisibilityStateEntry</dfn> : <span>PerformanceEntry</sp

<dt><dfn>History-action activation</dfn></dt>
<dd>
<p>When the <span>last history-action activation timestamp</span> of <var>W</var> is not equal
to the <span>last activation timestamp</span> of <var>W</var>, then <var>W</var> is said to have
<span>history-action activation</span>.</p>
<p>When <var>W</var>'s <span>has history-action activation</span> is true, then <var>W</var> is
said to have <span>history-action activation</span>.</p>

<p>This is a special variant of user activation, used to allow access to certain session history
APIs which, if used too frequently, would make it harder for the user to traverse back using <a
Expand All @@ -80260,16 +80261,15 @@ interface <dfn interface>VisibilityStateEntry</dfn> : <span>PerformanceEntry</sp
</dd>
</dl>

<p class="note">The <span>last activation timestamp</span> and <span>last history-action
activation timestamp</span> are retained even after the <code>Document</code> changes its
<span>fully active</span> status (e.g., after navigating away from a <code>Document</code>, or
navigating to a cached <code>Document</code>). This means <span>sticky activation</span> state
spans multiple navigations as long as the same <code>Document</code> gets reused. For the
transient activation state, the original <span data-x="activation-expiry">expiry</span> time
remains unchanged (i.e., the state still expires within the <span>transient activation
duration</span> limit from the original <span>activation triggering input event</span>). It is
important to consider this when deciding whether to base certain things off <span>sticky
activation</span> or <span>transient activation</span>.</p>
<p class="note" id="note-user-activation-and-navigation">The underlying values are retained even
after the <code>Document</code> changes its <span>fully active</span> status (e.g., after
navigating away from a <code>Document</code>, or navigating to a cached <code>Document</code>).
This means <span>sticky activation</span> and <span>history-action activation</span> spans
multiple navigations as long as the same <code>Document</code> gets reused. For the transient
activation state, the original <span data-x="activation-expiry">expiry</span> time remains
unchanged (i.e., the state still expires within the <span>transient activation duration</span>
limit from the original <span>activation triggering input event</span>). It is important to
consider this when deciding what type of activation to base certain things off of.</p>

<h4 id="user-activation-processing-model">Processing model</h4>

Expand Down Expand Up @@ -80302,6 +80302,10 @@ interface <dfn interface>VisibilityStateEntry</dfn> : <span>PerformanceEntry</sp
<li><p>Set <var>window</var>'s <span>last activation timestamp</span> to the <span>current
high resolution time</span>.</p></li>

<li><p>Set <var>window</var>'s <span>has history-action activation</span> to true.</p></li>

<li><p>Set <var>window</var>'s <span>has sticky activation</span> to true.</p></li>

<li><p><span>Notify the close watcher manager about user activation</span> given
<var>window</var>.</p></li>
</ol>
Expand Down Expand Up @@ -80369,8 +80373,7 @@ interface <dfn interface>VisibilityStateEntry</dfn> : <span>PerformanceEntry</sp
<var>navigables</var>.</p></li>

<li><p><span data-x="list iterate">For each</span> <var>window</var> in <var>windows</var>, set
<var>window</var>'s <span>last history-action activation timestamp</span> to <var>window</var>'s
<span>last activation timestamp</span>.</p></li>
<var>window</var>'s <span>has history-action activation</span> to false.</p></li>
</ol>

<p class="note">Note the asymmetry in the sets of <span data-x="browsing context">browsing
Expand Down Expand Up @@ -104898,6 +104901,15 @@ location.href = '#foo';</code></pre>
entries for the navigation API</span> given <var>navigable</var> and
<var>targetStep</var>.</p></li>

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

<li><p>If <var>displayedDocument</var>'s <span>relevant global object</span> has <span>sticky
activation</span>, and <var>displayDocument</var>'s <span
data-x="concept-document-origin">origin</span> is <span>same origin</span> with
<var>targetEntry</var>'s <span data-x="she-document">document</span>'s <span
data-x="concept-document-origin">origin</span>, then set <var>stickyActivationToCarryOver</var>
to true.</p></li>

<li>
<p>If <var>changingNavigableContinuation</var>'s <span
data-x="changing-nav-continuation-update-only">update-only</span> is true, or
Expand Down Expand Up @@ -104950,7 +104962,8 @@ location.href = '#foo';</code></pre>
<var>changingNavigableContinuation</var>'s <span
data-x="changing-nav-continuation-update-only">update-only</span>,
<var>scriptHistoryLength</var>, <var>scriptHistoryIndex</var>, <var>navigationType</var>,
<var>entriesForNavigationAPI</var>, and <var>previousEntry</var>.</p></li>
<var>entriesForNavigationAPI</var>, <var>previousEntry</var>, and
<var>stickyActivationToCarryOver</var>.</p></li>

<li><p>If <var>targetEntry</var>'s <span data-x="she-document">document</span> is equal to
<var>displayedDocument</var>, then perform <var>updateDocument</var>.</p></li>
Expand Down Expand Up @@ -105480,8 +105493,9 @@ location.href = '#foo';</code></pre>
<var>doNotReactivate</var>, integers <var>scriptHistoryLength</var> and
<var>scriptHistoryIndex</var>, <code>NavigationType</code>-or-null <var>navigationType</var>, an
optional <span>list</span> of <span data-x="session history entry">session history entries</span>
<var>entriesForNavigationAPI</var>, and an optional <span>session history entry</span>
<var>previousEntryForActivation</var>:</p>
<var>entriesForNavigationAPI</var>, an optional <span>session history entry</span>
<var>previousEntryForActivation</var>, and an optional boolean
<var>stickyActivationToCarryOver</var> (default false):</p>

<ol>
<li><p>Let <var>documentIsNew</var> be true if <var>document</var>'s <span>latest entry</span>
Expand Down Expand Up @@ -105657,7 +105671,8 @@ location.href = '#foo';</code></pre>
<li><p><span>Assert</span>: <var>entriesForNavigationAPI</var> is given.</p></li>

<li><p><span data-x="reactivate a document">Reactivate</span> <var>document</var> given
<var>entry</var> and <var>entriesForNavigationAPI</var>.</p>
<var>entry</var>, <var>entriesForNavigationAPI</var>, and
<var>stickyActivationToCarryOver</var>.</p>
</ol>

<p class="note"><var>documentsEntryChanged</var> can be false for one of two reasons: either we
Expand Down Expand Up @@ -105708,8 +105723,9 @@ location.href = '#foo';</code></pre>

<p>To <dfn data-x="reactivate a document" export for="Document">reactivate</dfn> a
<code>Document</code> <var>document</var> given a <span>session history entry</span>
<var>reactivatedEntry</var> and a <span>list</span> of <span data-x="session history
entry">session history entries</span> <var>entriesForNavigationAPI</var>:</p>
<var>reactivatedEntry</var>, a <span>list</span> of <span data-x="session history entry">session
history entries</span> <var>entriesForNavigationAPI</var>, and a boolean
<var>stickyActivationToCarryOver</var>:</p>

<p class="note">This algorithm updates <var>document</var> after it has come out of <a
href="#note-bfcache">bfcache</a>, i.e., after it has been made <span>fully active</span>
Expand All @@ -105718,6 +105734,16 @@ location.href = '#foo';</code></pre>
of events that happen in effect of the change is clear.</p>

<ol>
<li>
<p>If <var>stickyActivationToCarryOver</var> is true, then set <var>document</var>'s
<span>relevant global object</span>'s <span>has sticky activation</span> to true.</p>

<p class="note" id="note-user-activation-bfcache-carry-over">This means we are performing a
same-origin traverse from a document with sticky activation, in which case, we want to carry
over sticky activation if present, even if this bfcached <code>Document</code> did not
originally have it.</p>
</li>

<li id="history-autocomplete"><p><span data-x="list iterate">For each</span>
<var>formControl</var> of form controls in <var>document</var> with an <span>autofill field
name</span> of "<code data-x="attr-fe-autocomplete-off">off</code>", invoke the <span
Expand Down Expand Up @@ -106350,8 +106376,9 @@ location.href = '#foo';</code></pre>
data-x="navigation-params-origin">origin</span>.</p></li>

<li>
<p>If <var>navigable</var>'s <span data-x="nav-container">container</span> is not null,
then:</p>
<p>If <var>navigationParams</var>'s <span
data-x="navigation-params-navigable">navigable</span>'s <span
data-x="nav-container">container</span> is not null, then:</p>

<ol>
<li><p>Let <var>parentEnvironment</var> be <var>navigable</var>'s <span
Expand All @@ -106369,6 +106396,12 @@ location.href = '#foo';</code></pre>
<var>realmExecutionContext</var>, <var>navigationParams</var>'s <span
data-x="navigation-params-reserved-environment">reserved environment</span>,
<var>topLevelCreationURL</var>, and <var>topLevelOrigin</var>.</p></li>

<li><p>If <var>browsingContext</var>'s <span>active window</span> has <span>sticky
activation</span>, and <var>browsingContext</var>'s <span>active document</span>'s <span
data-x="concept-document-origin">origin</span> is <span>same origin</span> with
<var>navigationParams</var>'s <span data-x="navigation-params-origin">origin</span>, then set
<var>window</var>'s <span>has sticky activation</span> to true.</p></li>
</ol>

<p class="note">This is the usual case, where the new <code>Document</code> we're about to
Expand Down