Skip to content

Commit

Permalink
Overhaul document unloading/destruction/aborting
Browse files Browse the repository at this point in the history
As noted in #9148, the way in which destroy calls abort, and unload calls destroy, is not sound when most of these algorithms act on the entire tree. Instead, we need to split these algorithms into single-document base variants, which call each other, and whole-tree "document and its descendant" variants, which proceed carefully.

The whole-tree variants in particular need to take care with how they queue tasks. We cannot fire events (like unload for unloading, or readystatechange for aborting) inside of inactive documents, so we need to delay making a document inactive until its children have been processed. (The related issue #9869 around reloading is not yet solved, however, since that is a special case where the same session history entry is reused and thus making documents inactive is harder to avoid.)

Closes #9148.
  • Loading branch information
domenic committed Nov 15, 2023
1 parent 41c1f94 commit a49f99e
Showing 1 changed file with 157 additions and 55 deletions.
212 changes: 157 additions & 55 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -95068,7 +95068,7 @@ interface <dfn interface>BeforeUnloadEvent</dfn> : <span>Event</span> {
<li><p><span>Inform the navigation API about child navigable destruction</span> given
<var>navigable</var>.</p></li>

<li><p><span data-x="destroy a document">Destroy</span> <var>navigable</var>'s <span
<li><p><span>Destroy a document and its descendants</span> given <var>navigable</var>'s <span
data-x="nav-document">active document</span>.</p></li>

<li><p>Let <var>parentDocState</var> be <var>container</var>'s <span>node navigable</span>'s
Expand Down Expand Up @@ -95110,8 +95110,8 @@ interface <dfn interface>BeforeUnloadEvent</dfn> : <span>Event</span> {
<li><p>Let <var>document</var> be <var>historyEntry</var>'s <span
data-x="she-document">document</span>.</p></li>

<li><p>If <var>document</var> is not null, then <span data-x="destroy a
document">destroy</span> <var>document</var>.</p></li>
<li><p>If <var>document</var> is not null, then <span>destroy a document and its
descendants</span> given <var>document</var>.</p></li>
</ol>
</li>

Expand Down Expand Up @@ -95139,12 +95139,18 @@ interface <dfn interface>BeforeUnloadEvent</dfn> : <span>Event</span> {
<li><p>If the result of <span>checking if unloading is canceled</span> for <var>toUnload</var> is
true, then return.</p></li>

<li><p><span data-x="unload a document">Unload</span> the <span data-x="nav-document">active
documents</span> of each of <var>toUnload</var>. <span class="XXX" data-x="">In what
order?</span></p></li>
<li>
<p><span data-x="tn-append-session-history-traversal-steps">Append the following session
history traversal steps</span> to <var>traversable</var>:</p>

<li><p><span data-x="destroy a top-level traversable">Destroy</span>
<var>traversable</var>.</p></li>
<ol>
<li><p>Let <var>afterAllUnloads</var> be an algorithm step which <p><span data-x="destroy a
top-level traversable">destroys</span> <var>traversable</var>.</p></li>

<li><p><span>Unload a document and its descendants</span> given <var>traversable</var>'s <span
data-x="nav-document">active document</span>, null, and <var>afterAllUnloads</var>.</p></li>
</ol>
</li>
</ol>


Expand Down Expand Up @@ -96363,7 +96369,7 @@ interface <dfn interface>BeforeUnloadEvent</dfn> : <span>Event</span> {
name</dfn> string, initially the empty string.</p></li>
</ul>

<p>User agents may <span data-x="destroy a Document">destroy</span> the <span
<p>User agents may <span>destroy a document and its descendants</span> given the <span
data-x="document-state-document">documents</span> of <span data-x="document state">document
states</span> with non-null <span data-x="document-state-document">documents</span>, as long as
the <code>Document</code> is not <span>fully active</span>.</p>
Expand Down Expand Up @@ -97446,7 +97452,7 @@ location.href = '#foo';</code></pre>

<li><p><span>Queue a global task</span> on the <span>navigation and traversal task
source</span> given <var>navigable</var>'s <span data-x="nav-window">active window</span> to
<span data-x="abort a document">abort</span> <var>navigable</var>'s <span
<span>abort a document and its descendants</span> given <var>navigable</var>'s <span
data-x="nav-document">active document</span>.</p></li>

<li id="navigation-create-document-state">
Expand Down Expand Up @@ -100511,36 +100517,24 @@ location.href = '#foo';</code></pre>
<var>targetStep</var>.</p></li>

<li>
<p><span>Queue a global task</span> on the <span>navigation and traversal task source</span>
given <var>navigable</var>'s <span data-x="nav-window">active window</span> to run the
steps:</p>
<p>If <var>changingNavigableContinuation</var>'s <span
data-x="changing-nav-continuation-update-only">update-only</span> is false, and
<var>targetEntry</var>'s <span data-x="she-document">document</span> does not equal
<var>displayedDocument</var>, then <span>unload a document and its descendants</span> given
<var>displayedDocument</var>, <var>targetEntry</var>'s <span
data-x="she-document">document</span>, and <var>afterPotentialUnloads</var>.</p>

<ol>
<li>
<p>If <var>changingNavigableContinuation</var>'s <span
data-x="changing-nav-continuation-update-only">update-only</span> is false, then:</p>
<p>Otherwise, <span>queue a global task</span> on the <span>navigation and traversal task
source</span> given <var>navigable</var>'s <span data-x="nav-window">active window</span> to
perform <var>afterPotentialUnloads</var>.</p>

<ol>
<li>
<p>If <var>targetEntry</var>'s <span data-x="she-document">document</span> does not equal
<var>displayedDocument</var>, then:</p>
<p>In both cases, let <var>afterPotentialUnloads</var> be the following steps:</p>

<ol>
<li><p><span data-x="unload a document">Unload</span> <var>displayedDocument</var> given
<var>targetEntry</var>'s <span data-x="she-document">document</span>.</p></li>

<li><p>For each <var>childNavigable</var> of <var>displayedDocument</var>'s
<span>descendant navigables</span>, <span>queue a global task</span> on the
<span>navigation and traversal task source</span> given <var>childNavigable</var>'s
<span data-x="nav-window">active window</span> to <span data-x="unload a
document">unload</span> <var>childNavigable</var>'s <span data-x="nav-document">active
document</span>.</p></li>
</ol>

<li><p><span>Activate history entry</span> <var>targetEntry</var> for
<var>navigable</var>.</p></li>
</ol>
</li>
<ol>
<li><p>If <var>changingNavigableContinuation</var>'s <span
data-x="changing-nav-continuation-update-only">update-only</span> is false, then
<span>activate history entry</span> <var>targetEntry</var> for
<var>navigable</var>.</p></li>

<li><p>Let <var>updateDocument</var> be an algorithm step which performs <span>update
document for history step application</span> given <var>targetEntry</var>'s <span
Expand Down Expand Up @@ -102188,7 +102182,8 @@ new PaymentRequest(&hellip;); // Allowed to use

<ol>
<li><p><span>Assert</span>: this is running as part of a <span data-x="concept-task">task</span>
queued on <var>oldDocument</var>'s <span>event loop</span>.</p></li>
queued on <var>oldDocument</var>'s <span>relevant agent</span>'s <span
data-x="concept-agent-event-loop">event loop</span>.</p></li>

<li><p>Let <var>unloadTimingInfo</var> be a new <span>document unload timing
info</span>.</p></li>
Expand Down Expand Up @@ -102295,6 +102290,52 @@ new PaymentRequest(&hellip;); // Allowed to use
<var>unloadTimingInfo</var>.</p></li>
</ol>

<p>To <dfn>unload a document and its descendants</dfn>, given a <code>Document</code>
<var>document</var>, an optional <code>Document</code>-or-null <var>newDocument</var> (default
null), and an optional set of steps <var>afterAllUnloads</var>:</p>

<ol>
<li><p><span>Assert</span>: this is running within <var>document</var>'s <span>node
navigable</span>'s <span data-x="nav-traversable">traversable navigable</span>'s <span
data-x="tn-session-history-traversal-queue">session history traversal queue</span>.</p></li>

<li><p>Let <var>childNavigables</var> be <var>document</var>'s <span data-x="child
navigable">child navigables</span>.</p></li>

<li><p>Let <var>numberUnloaded</var> be 0.</p></li>

<li>
<p><span data-x="list iterate">For each</span> <var>childNavigable</var> of
<var>childNavigable</var>'s <span class="XXX" data-x="">in what order?</span>, <span>queue a
global task</span> on the <span>navigation and traversal task source</span> given
<var>childNavigable</var>'s <span data-x="nav-window">active window</span> to perform the
following steps:</p>

<ol>
<li><p><span data-x="unload a document">Unload</span> <var>childNavigable</var>'s <span
data-x="nav-document">active document</span>.</p></li>

<li><p>Increment <var>numberUnloaded</var>.</p></li>
</ol>
</li>

<li><p>Wait until <var>numberUnloaded</var> equals <var>childNavigable</var>'s <span
data-x="list size">size</span>.</p></li>

<li>
<p><span>Queue a global task</span> on the <span>navigation and traversal task source</span>
given <var>document</var>'s <span>relevant global object</span> to perform the following
steps:</p>

<ol>
<li><p><span data-x="unload a document">Unload</span> <var>document</var>, passing along
<var>newDocument</var> if it is not null.</p></li>

<li><p>If <var>afterAllUnloads</var> was given, then run it.</p></li>
</ol>
</li>
</ol>

<p>This specification defines the following <dfn export>unloading document cleanup steps</dfn>.
Other specifications can define more. Given a <code>Document</code> <var>document</var>:</p>

Expand Down Expand Up @@ -102354,18 +102395,18 @@ new PaymentRequest(&hellip;); // Allowed to use
<code>Document</code> <var>document</var>:</p>

<ol>
<li><p><span data-x="destroy a document">Destroy</span> the <span
data-x="nav-document">active documents</span> of each of <var>document</var>'s <span>descendant
navigables</span>. <span class="XXX" data-x="">In what order?</span></p></li>
<li><p><span>Assert</span>: this is running as part of a <span data-x="concept-task">task</span>
queued on <var>document</var>'s <span>relevant agent</span>'s <span
data-x="concept-agent-event-loop">event loop</span>.</p></li>

<li><p><span data-x="abort a document">Abort</span> <var>document</var>.</p></li>

<li><p>Set <var>document</var>'s <i data-x="concept-document-salvageable">salvageable</i> state
to false.</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>

<li><p><span data-x="abort a document">Abort</span> <var>document</var>.</p></li>

<li><p>Remove any <span data-x="concept-task">tasks</span> whose <span
data-x="concept-task-document">document</span> is <var>document</var> from any <span>task
queue</span> (without running those tasks).</p></li>
Expand All @@ -102392,22 +102433,51 @@ new PaymentRequest(&hellip;); // Allowed to use
accessible to script, in the case where we are <span data-x="destroy a child
navigable">destroying a child navigable</span>.</p>

<p>To <dfn>destroy a document and its descendants</dfn> given a <code>Document</code>
<var>document</var>, perform the following steps <span>in parallel</span>:</p>

<ol>
<li><p>Let <var>childNavigables</var> be <var>document</var>'s <span data-x="child
navigable">child navigables</span>.</p></li>

<li><p>Let <var>numberDestroyed</var> be 0.</p></li>

<li>
<p><span data-x="list iterate">For each</span> <var>childNavigable</var> of
<var>childNavigable</var>'s <span class="XXX" data-x="">in what order?</span>, <span>queue a
global task</span> on the <span>navigation and traversal task source</span> given
<var>childNavigable</var>'s <span data-x="nav-window">active window</span> to perform the
following steps:</p>

<ol>
<li><p><span data-x="destroy a document">Destroy</span> <var>childNavigable</var>'s <span
data-x="nav-document">active document</span>.</p></li>

<li><p>Increment <var>numberDestroyed</var>.</p></li>
</ol>
</li>

<li><p>Wait until <var>numberDestroyed</var> equals <var>childNavigable</var>'s <span
data-x="list size">size</span>.</p></li>

<li><p><span>Queue a global task</span> on the <span>navigation and traversal task source</span>
given <var>document</var>'s <span>relevant global object</span> to <span data-x="destroy a
document">destroy</span> <var>document</var>.</p></li>
</ol>

<h4>Aborting a document load</h4>

<p>To <dfn data-x="abort a document">abort</dfn> a <code>Document</code> <var>document</var>:</p>

<ol>
<li><p><span data-x="abort a document">Abort</span> the <span data-x="nav-document">active
documents</span> of each of <var>document</var>'s <span>descendant navigables</span>. <span
class="XXX" data-x="">In what order?</span> If this results in any of those <code>Document</code>
objects having their <i data-x="concept-document-salvageable">salvageable</i> state set to false,
then set <var>document</var>'s <i data-x="concept-document-salvageable">salvageable</i> state to
false also.</p></li>
<li><p><span>Assert</span>: this is running as part of a <span data-x="concept-task">task</span>
queued on <var>document</var>'s <span>relevant agent</span>'s <span
data-x="concept-agent-event-loop">event loop</span>.</p></li>

<li><p>Cancel any instances of the <span data-x="concept-fetch">fetch</span> algorithm in the
context of <var>document</var>, discarding any <span data-x="concept-task">tasks</span>
<span data-x="queue a task">queued</span> for them, and discarding any further data received from
the network for them. If this resulted in any instances of the <span
context of <var>document</var>, discarding any <span data-x="concept-task">tasks</span> <span
data-x="queue a task">queued</span> for them, and discarding any further data received from the
network for them. If this resulted in any instances of the <span
data-x="concept-fetch">fetch</span> algorithm being canceled or any <span data-x="queue a
task">queued</span> <span data-x="concept-task">tasks</span> or any network data getting
discarded, then set <var>document</var>'s <i
Expand Down Expand Up @@ -102440,12 +102510,44 @@ new PaymentRequest(&hellip;); // Allowed to use

<li><p><span data-x="abort a parser">Abort that parser</span>.</p></li>

<li><p>Set <var>document</var>'s <i data-x="concept-document-salvageable">salvageable</i> state
to false.</p></li>
<li><p>Set <var>document</var>'s <i data-x="concept-document-salvageable">salvageable</i> to
false.</p></li>
</ol>
</li>
</ol>

<p>To <dfn>abort a document and its descendants</dfn> given a <code>Document</code>
<var>document</var>:</p>

<ol>
<li><p><span>Assert</span>: this is running as part of a <span data-x="concept-task">task</span>
queued on <var>document</var>'s <span>relevant agent</span>'s <span
data-x="concept-agent-event-loop">event loop</span>.</p></li>

<li><p>Let <var>descendantNavigables</var> be <var>document</var>'s <span>descendant
navigables</span>.</p></li>

<li>
<p><span data-x="list iterate">For each</span> <var>descendantNavigable</var> of
<var>descendantNavigables</var> <span class="XXX" data-x="">in what order?</span>, <span>queue
a global task</span> on the <span>navigation and traversal task source</span> given
<var>descendantNavigable</var>'s <span data-x="nav-window">active window</span> to perform the
following steps:</p>

<ol>
<li><p><span data-x="abort a document">Abort</span> <var>descendantNavigable</var>'s <span
data-x="nav-document">active document</span>.</p></li>

<li><p>If <var>descendantNavigable</var>'s <span data-x="nav-document">active
document</span>'s <i data-x="concept-document-salvageable">salvageable</i> is false, then set
<var>document</var>'s <i data-x="concept-document-salvageable">salvageable</i> to
false.</p></li>
</ol>
</li>

<li><p><span data-x="abort a document">Abort</span> <var>document</var>.</p></li>
</ol>

<p id="stop-document-loading">To <dfn data-x="nav-stop">stop loading</dfn> a
<span>navigable</span> <var>navigable</var>:</p>

Expand All @@ -102463,7 +102565,7 @@ new PaymentRequest(&hellip;); // Allowed to use
navigation</span> will cause further work to be abandoned.</p>
</li>

<li><p><span data-x="abort a document">Abort</span> <var>document</var>.</p></li>
<li><p><span>Abort a document and its descendants</span> given <var>document</var>.</p></li>
</ol>

<p class="XXX">Through their <a href="#nav-traversal-ui">user interface</a>, user agents also
Expand Down

0 comments on commit a49f99e

Please sign in to comment.