Skip to content

Commit

Permalink
Make more algorithmic
Browse files Browse the repository at this point in the history
In particular, this defines a clear algorithm for consumption and activation, and stores the last activation time explicitly instead of just suggesting that in a note.
  • Loading branch information
domenic committed Apr 12, 2019
1 parent 377377b commit 127a67c
Showing 1 changed file with 135 additions and 97 deletions.
232 changes: 135 additions & 97 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -30149,7 +30149,7 @@ interface <dfn>HTMLIFrameElement</dfn> : <span>HTMLElement</span> {
the <code
data-x="attr-iframe-sandbox-allow-top-navigation-by-user-activation">allow-top-navigation-by-user-activation</code>
keyword behaves similarly but allows such <span data-x="navigate">navigation</span> only when
<span>transient activation flag</span> is true; and the <code
the <span>transient activation flag</span> is true; and the <code
data-x="attr-iframe-sandbox-allow-forms">allow-forms</code>, <code
data-x="attr-iframe-sandbox-allow-modals">allow-modals</code>, <code
data-x="attr-iframe-sandbox-allow-orientation-lock">allow-orientation-lock</code>, <code
Expand Down Expand Up @@ -34932,7 +34932,7 @@ interface <dfn>MediaError</dfn> {
<p>A <span>media element</span> is said to be <dfn>allowed to play</dfn> if the user agent and the
system allow media playback in the current context.</p>

<p class="note">For example, a user agent could allow playback only when <span>transient
<p class="note">For example, a user agent could allow playback only when the <span>transient
activation flag</span> is true, but an exception could be made to allow playback while <span
data-x="concept-media-muted">muted</span>.</p>

Expand Down Expand Up @@ -47742,7 +47742,7 @@ ldh-str = &lt; as defined in <a href="https://tools.ietf.org/html/rfc1034#
<p>The element's <span>input activation behavior</span> is to run the following steps:</p>

<ol>
<li><p>If the algorithm is invoked when <span>transient activation flag</span> is false, then
<li><p>If the algorithm is invoked when the <span>transient activation flag</span> is false, then
return without doing anything else.</p></li>

<li>
Expand Down Expand Up @@ -72493,137 +72493,177 @@ END:VCARD</pre>
document</span>'s <span>top layer</span>.</p>


<!-- mDebug: new sections begin -->

<h3>Tracking user activation</h3>

<h4>Introduction</h4>

<!-- NON-NORMATIVE SECTION -->

<p>To prevent abuse of certain APIs that could be annoying to users (e.g. opening popups or
vibrating phones), Web browsers allow the use of these APIs only when the user is actively
interacting with the web page or have interacted with the page at least once. This "active
interaction" state is maintained through the <span>user activation state</span> object in each
<code>Window</code>.</p>
interacting with the web page or has interacted with the page at least once. This "active
interaction" state is maintained on each <code>Window</code> through the mechanisms in this
section.</p>

<h4>Processing model</h4>
<div w-nodev>

<p>When a user interaction in a <span>browsing context</span> <var>B</var> causes firing of an <a
href="#activation-triggering-event">activation triggering input events</a> in that context, it
<dfn data-x="activate-window">activates the <code>Window</code> objects</dfn> in all <span
data-x="ancestor browsing context">ancestor browsing contexts</span> of <var>B</var> (including
the one in <var>B</var> itself). The UA activates all relevant <code>Window</code> objects before
it dispatches the input event to any <code>EventTarget</code>.</p>
<h4 id="user-activation-processing-model">Processing model</h4>

<p>The <dfn>user activation state</dfn> of a <code>Window</code> object <var>W</var> consists of
two boolean flags, both of which are initially set to false:</p>
<p>For the purposes of tracking user activation, each <code>Window</code> object <var>W</var> has
the following associated values:</p>

<ul>
<dl>
<dt>The <dfn>sticky activation flag</dfn></dt>
<dd>
<p>A boolean value indicating the historical activation state: whether the user has ever
interacted with <var>W</var> or any of its <span
data-x="list of the descendant browsing contexts">descendant</span> windows. Initially false.</p>

<li><p>The <dfn>sticky activation flag</dfn> indicates the historical activation state: whether
the user has ever interacted with <var>W</var> or its subframes. When <var>W</var> is <span
data-x="activate-window">activated</span> for the very first time, the UA sets the flag to true.
This flag is never set to false during the lifetime of <var>W</var>.</p></li>
<p>When <var>W</var> is <span data-x="activate-window">activated</span> for the very first time,
this flag gets set to true. It is never set to false during the lifetime of <var>W</var>.</p>
</dd>

<li><p>The <dfn>transient activation flag</dfn> indicates the current activation state: whether
the user is currently interacting with <var>W</var> or its subframes. Every time <var>W</var> is
<span data-x="activate-window">activated</span>, the UA sets the flag to true. The UA sets the
flag to false when the flag either <span data-x="expiration of transient
activation">expires</span> or gets <span data-x="consumption of transient
activation">consumed</span>.</p></li>
<dt>The <dfn>consumable activation flag</dfn></dt>
<dd>
<p>A boolean value used to implement the <span>transient activation flag</span>. It indicates
whether the user has interacted with <var>W</var> or any of its <span data-x="list of the
descendant browsing contexts">descendant</span> windows, since the last time an API <span
data-x="consume user activation">consumed</span> a user activation. Initially false.</p>

</ul>
<p><span data-x="activation consuming API">Activation consuming APIs</span>, such as <code
data-x="dom-open">window.open()</code>, set the <span>consumable activation flag</span> to
false.</p>
</dd>

<dl>
<dt>The <dfn>last activation time</dfn></dt>
<dd>
<p>A timestamp indicating the last time <var>W</var> was <span
data-x="activate-window">activated</span>, used for <span data-x="transient activation
expiration cutoff">expiration</span> of the <span>transient activation flag</span>. Initially
null.</p>
</dd>
</dl>

<dt><dfn>Expiration of transient activation</dfn></dt>
<p>Then, the value of the <dfn>transient activation flag</dfn> for <var>W</var> is determined by
the following steps:</p>

<dd>
<ol>
<li><p>If <var>W</var>'s <span>consumable activation flag</span> is false, return false.</p></li>

<p>The UA sets the <span>transient activation flag</span> to false after a UA-defined expiry
time has elapsed since the last time <var>W</var> was
<span data-x="activate-window">activated</span>.</p>
<li><p>Assert: <var>W</var>'s <span>last activation time</span> is not null.</p></li>

<p class="note">The expiry time has to be at most a few seconds so that the user can possibly
perceive the link between an interaction with a page and the page opening a popup, for
example.</p>
<li>
<p>If <var>W</var>'s <span>last activation time</span> is longer ago than a
user-agent-defined <dfn>transient activation expiration cutoff</dfn>, return false.</p>

<p class="note">The UA can possibly implement the expiration of <span>transient activation
flag</span> through time-stamping: the UA would have an internal time-stamp field in <span>user
activation state</span> which is updated to current time-stamp every time <var>W</var> is
<span data-x="activate-window">activated</span>. Then every time the <span>transient activation
flag</span> is checked, the UA would return true iff current time-stamp is less than the stored
time-stamp plus the expiry time.</p>
<p>The <span>transient activation expiration cutoff</span> should be at most a few seconds, so
that the user can possibly perceive the link between an interaction with a page and the page
opening a popup (for example).</p>
</li>

</dd>
<li><p>Return true.</p></li>
</ol>

<dt><dfn>Consumption of transient activation</dfn><dt>
<p>When a user interaction in a <span>browsing context</span> <var>activatedBrowsingContext</var>
causes firing of an <a href="#activation-triggering-event">activation triggering input event</a>
in that context, the user agent must perform the following steps <em>before</em> <span
data-x="concept-event-dispatch">dispatching</span> the event. These steps are said to <dfn
data-x="activate-window">activate</dfn> the relevant <code>Window</code> objects.</p>

<dd>
<ol>
<li><p>Let <var>browsingContexts</var> be a list consisting of
<var>activatedBrowsingContext</var> plus all <span data-x="ancestor browsing context">ancestor
browsing contexts</span> of <var>activatedBrowsingContext</var>.</p></li>

<li><p>Let <var>windows</var> be the list of <code>Window</code> objects constructed by, for each
<var>browsingContext</var> of <var>browsingContexts</var>, taking the [[Window]] internal slot
value of <var>browsingContext</var>'s <code>WindowProxy</code> object.</p></li>

<p>The UA sets the <span>transient activation flag</span> to whenever any script in the page
makes a call to an <span>activation consuming API</span> such as <code
data-x="dom-open">window.open()</code>.</p>
<li>
<p><span data-x="list iterate">For each</span> <var>window</var> of <var>windows</var>:</p>

<p class="note" w-nodev>Note the assymetry between setting the <span>transient activation
flag</span> to true and false from the perspective of the <span data-x="browsing
context">browsing contexts</span> of the page. A user interaction with a <code>Window</code>
object <var>W</var> sets to true the transient activation flags in only the <span
data-x="ancestor browsing context">ancestor browsing contexts</span> of <var>W</var>. However,
a consumping in <var>W</var> sets to false the flags in all browsing contexts of the page (more
precisely, all <span data-x="list of the descendant browsing contexts">descendant browsing
contexts</span> of the <span>top-level browsing context</span> of <var>W</var>). Consuming the
flag in this manner prevents malicious sites from making multiple calls to an <span>activation
consuming API</span> from a single user activation (possibly by exploiting a deep hierarchy of
browsing contexts).</p>
<ol>
<li><p>Set <var>window</var>'s <span>sticky activation flag</span> to true.</p></li>

</dd>
<li><p>Set <var>window</var>'s <span>consumable activation flag</span> to true.</p></li>

</dl>
<li><p>Set <var>window</var>'s <span>last activation time</span> to the current time.</p></li>
</ol>
</li>
</ol>

<p>To <dfn>consume user activation</dfn> for a <code>Window</code> <var>W</var>, perform the
following steps:</p>

<ol>
<li><p>If <var>W</var>'s <span data-x="window bc">browsing context</span> is null, then
return.</p></li>

<li><p>Let <var>top</var> be <var>W</var>'s <span data-x="window bc">browsing context</span>'s
<span>top-level browsing context</span>.</p></li>

<li><p>Let <var>browsingContexts</var> be the <span>list of the descendant browsing
contexts</span> of <var>top</var>'s <span>active document</span>.</p></li>

<li><p><span data-x="list append">Append</span> <var>top</var> to
<var>browsingContexts</var>.</p></li>

<li><p>Let <var>windows</var> be the list of <code>Window</code> objects constructed by, for each
<var>browsingContext</var> of <var>browsingContexts</var>, taking the [[Window]] internal slot
value of <var>browsingContext</var>'s <code>WindowProxy</code> object.</p></li>

<li>
<p><span data-x="list iterate">For each</span> <var>window</var> of <var>windows</var>:</p>

<ol>
<li><p>Set <var>window</var>'s <span>consumable activation flag</span> to false.</p></li>
</ol>
</li>
</ol>

<p class="note">Note the asymmetry between <span data-x="activate-window">activation</span> and
<span data-x="consume user activation">consumption</span> from the perspective of the <span
data-x="browsing context">browsing contexts</span> of the page. Activation only sets the
<span>consumable activation flag</span> to true for a browsing context's inclusive ancestors, but
consumption sets the flag to false for both ancestor and descendant browsing contexts. Consuming
the flag in this manner prevents malicious sites from making multiple calls to an <span>activation
consuming API</span> from a single user activation (possibly by exploiting a deep hierarchy of
browsing contexts).</p>

</div>

<h4>APIs gated by user activation</h4>

<p>APIs that are dependent on <span>user activation state</span> are classified into three
different levels. The levels are as follows, sorted by their "strength of dependence" on user
activation (from strongest to weakest):</p>
<p>APIs that are dependent on user activation are classified into three different levels. The
levels are as follows, sorted by their "strength of dependence" on user activation (from strongest
to weakest):</p>

<dl>
<dt><dfn data-x="activation consuming api">Transient activation consuming APIs</dfn></dt>
<dt><dfn data-x="activation consuming api">Transient activation-consuming APIs</dfn></dt>

<dd><p>These APIs require the transient activation flag to be true, and they <span
data-x="consumption of transient activation">consume</span> the flag in each call to prevent multiple
calls per user activation.</p></dd>
<dd><p>These APIs require the <span>transient activation flag</span> to be true, and they
<span>consume user activation</span> in each call to prevent multiple calls per user
activation.</p></dd>

<dt><dfn data-x="transient activation gated api">Transient activation gated APIs</dfn><dt>
<dt><dfn data-x="transient activation gated api">Transient activation-gated APIs</dfn><dt>

<dd><p>These APIs require the transient activation flag to be true but don't consume it, so
multiple calls are allowed per user activation until the transient bit <span data-x="expiration
of transient activation">expires</span>.</p></dd>
<dd><p>These APIs require the <span>transient activation flag</span> to be true, but don't
consume it, so multiple calls are allowed per user activation until the transient bit <span
data-x="transient activation expiration cutoff">expires</span>.</p></dd>

<dt><dfn data-x="sticky activation gated api">Sticky activation gated APIs</dfn></dt>
<dt><dfn data-x="sticky activation gated api">Sticky activation-gated APIs</dfn></dt>

<dd><p>These APIs require the sticky activation flag to be true, so they are blocked
<dd><p>These APIs require the <span>sticky activation flag</span> to be true, so they are blocked
until the very first user activation.</p></dd>

</dl>


<h4 id="activation-triggering-event">Input events triggering user activation</h4>

<div w-nodev>

<!-- NON-NORMATIVE SECTION -->

<!-- The following comment/tag came from the old text. Not sure who/which-spec anchors here. -->
<!-- interaction event spec point -->

<p class="&#x0058;&#x0058;&#x0058;">The event set is inconsistent across major browsers. See <a
href="https://github.com/whatwg/html/issues/3849">issue #3849</a>.</p>

<p>A <code>Window</code> object <var>W</var> is considered activated when <var>W</var> or any
elements of <var>W</var> receives an event whose <code
data-x="dom-Event-isTrusted">isTrusted</code> attribute is true and whose <code
data-x="dom-Event-type">type</code> is one of:</p>

<ul class="brief">
<li><code data-x="event-change">change</code></li>
<li><code data-x="event-click">click</code></li>
Expand All @@ -72636,9 +72676,7 @@ END:VCARD</pre>
<li><code data-x="event-touchend">touchend</code></li>
</ul>

</div>

<h3>Activation behavior of elements</h3>
<h3 id="activation">Activation behavior of elements</h3>

<p>Certain elements in HTML have an <span>activation behavior</span>, which means that the user
can activate them. This is always caused by a <code data-x="event-click">click</code> event.</p>
Expand Down Expand Up @@ -77829,12 +77867,12 @@ console.assert(iframeWindow.frameElement === null);
then:</p>

<ol>
<li><p>If <span>transient activation flag</span> is true and <var>A</var>'s
<li><p>If the <span>transient activation flag</span> is true and <var>A</var>'s
<span>active document</span>'s <span>active sandboxing flag set</span> has its <span>sandboxed
top-level navigation with user activation browsing context flag</span> set, then return
false.</p></li>

<li><p>Otherwise, if <span>transient activation flag</span> is false and
<li><p>Otherwise, if the <span>transient activation flag</span> is false and
<var>A</var>'s <span>active document</span>'s <span>active sandboxing flag set</span> has its
<span>sandboxed top-level navigation without user activation browsing context flag</span> set,
then return false.</p></li>
Expand Down Expand Up @@ -78142,7 +78180,7 @@ console.assert(iframeWindow.frameElement === null);
applicable option from the following list:</p>

<dl class="switch">
<dt id="popup-blocker">If <span>transient activation flag</span> is false and
<dt id="popup-blocker">If the <span>transient activation flag</span> is false and
the user agent has been configured to not show popups (i.e. the user agent has a "popup
blocker" enabled)</dt>

Expand Down Expand Up @@ -80337,7 +80375,7 @@ interface <dfn>BarProp</dfn> {

<p>This flag <a href="#sandboxLinks">prevents content from navigating their <span>top-level
browsing context</span></a> and <a href="#sandboxClose">prevents content from closing their
<span>top-level browsing context</span></a>. It is consulted only when <span>transient
<span>top-level browsing context</span></a>. It is consulted only when the <span>transient
activation flag</span> is false.</p>

<p>When the <span>sandboxed top-level navigation without user activation browsing context
Expand All @@ -80356,7 +80394,7 @@ interface <dfn>BarProp</dfn> {

<p>This flag <a href="#sandboxLinks">prevents content from navigating their <span>top-level
browsing context</span></a> and <a href="#sandboxClose">prevents content from closing their
<span>top-level browsing context</span></a>. It is consulted only when <span>transient
<span>top-level browsing context</span></a>. It is consulted only when the <span>transient
activation flag</span> is true.</p>

<p>As with the <span>sandboxed top-level navigation without user activation browsing context
Expand Down Expand Up @@ -82838,7 +82876,7 @@ interface <dfn>Location</dfn> { // but see also <a href="#the-location-interface
processor), user agents should attempt to mitigate the risk that this is an attempt to exploit the
target software, e.g. by prompting the user to confirm that the <span>source browsing
context</span>'s <span>active document</span>'s <span>origin</span> is to be allowed to invoke the
specified software. In particular, if the <span>navigate</span> algorithm was invoked when
specified software. In particular, if the <span>navigate</span> algorithm was invoked when the
<span>transient activation flag</span> was false, the user agent should not invoke the external
software package without prior user confirmation.</p>

Expand Down

0 comments on commit 127a67c

Please sign in to comment.