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

Use DOM's post-insertion steps for <script> elements #10188

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
129 changes: 112 additions & 17 deletions source
Expand Up @@ -1762,10 +1762,9 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
or string, means that the <span>length</span> of the text is zero (i.e., not even containing <span
data-x="control">controls</span> or U+0020 SPACE).</p>

<p>An HTML element can have specific <dfn>HTML element insertion steps</dfn> defined for the
element's <span data-x="concept-element-local-name">local name</span>. Similarly, an HTML element
can have specific <dfn>HTML element removing steps</dfn> defined for the element's <span
data-x="concept-element-local-name">local name</span>.</p>
<p>An HTML element can have specific <dfn>HTML element insertion steps</dfn>, <dfn>HTML element
post-insertion steps</dfn>, and <dfn>HTML element removing steps</dfn>, all defined for the
element's <span data-x="concept-element-local-name">local name</span>.</p>

<p>The <span data-x="concept-node-insert-ext">insertion steps</span> for the HTML Standard, given
<var>insertedNode</var>, are defined as the following:</p>
Expand Down Expand Up @@ -1796,6 +1795,18 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<span>node document</span>.</p></li>
</ol>

<p>The <span data-x="concept-node-post-insert-ext">post-insertion steps</span> for the HTML
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm just following the lead of the insertion steps, where we override them once for this specification, and fan them out into element-specific ones. I notice we don't do that for "children changed steps" though (each element just uses that dfn directly, with no HTML middle-man). Not sure what's more desirable?

Standard, given <var>insertedNode</var>, are defined as the following:</p>

<ol>
<li><p>If <var>insertedNode</var> is an element whose <span
data-x="concept-element-namespace">namespace</span> is the <span>HTML namespace</span>, and this
standard defines <span data-x="html element post-insertion steps">HTML element post-insertion
steps</span> for <var>insertedNode</var>'s <span data-x="concept-element-local-name">local
name</span>, then run the corresponding <span>HTML element post-insertion steps</span> given
<var>insertedNode</var>.</p></li>
</ol>

<p>The <span data-x="concept-node-remove-ext">removing steps</span> for the HTML Standard, given
<var>removedNode</var> and <var>oldParent</var>, are defined as the following:</p>

Expand Down Expand Up @@ -3154,6 +3165,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li><dfn data-x="dom-Document-createElementNS" data-x-href="https://dom.spec.whatwg.org/#dom-document-createelementns"><code>createElementNS()</code></dfn> method</li>
<li><dfn data-x="dom-Document-getElementById" data-x-href="https://dom.spec.whatwg.org/#dom-nonelementparentnode-getelementbyid"><code>getElementById()</code></dfn> method</li>
<li><dfn data-x="dom-document-getElementsByClassName" data-x-href="https://dom.spec.whatwg.org/#dom-document-getelementsbyclassname"><code>getElementsByClassName()</code></dfn> method</li>
<li><dfn data-x="dom-Node-append" data-x-href="https://dom.spec.whatwg.org/#dom-node-append"><code>append()</code></dfn> method</li>
<li><dfn data-x="dom-Node-appendChild" data-x-href="https://dom.spec.whatwg.org/#dom-node-appendchild"><code>appendChild()</code></dfn> method</li>
<li><dfn data-x="dom-Node-cloneNode" data-x-href="https://dom.spec.whatwg.org/#dom-node-clonenode"><code>cloneNode()</code></dfn> method</li>
<li><dfn data-x="dom-Document-importNode" data-x-href="https://dom.spec.whatwg.org/#dom-document-importnode"><code>importNode()</code></dfn> method</li>
Expand Down Expand Up @@ -3192,6 +3204,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#concept-node-pre-insert">pre-insert</dfn>, <dfn data-x="concept-node-insert" data-x-href="https://dom.spec.whatwg.org/#concept-node-insert">insert</dfn>, <dfn data-x="concept-node-append" data-x-href="https://dom.spec.whatwg.org/#concept-node-append">append</dfn>, <dfn data-x="concept-node-replace" data-x-href="https://dom.spec.whatwg.org/#concept-node-replace">replace</dfn>, <dfn data-x="concept-node-replace-all" data-x-href="https://dom.spec.whatwg.org/#concept-node-replace-all">replace all</dfn>, <dfn data-x-href="https://dom.spec.whatwg.org/#string-replace-all">string replace all</dfn>, <dfn data-x="concept-node-remove" data-x-href="https://dom.spec.whatwg.org/#concept-node-remove">remove</dfn>, and <dfn data-x="concept-node-adopt" data-x-href="https://dom.spec.whatwg.org/#concept-node-adopt">adopt</dfn> algorithms for nodes</li>
<li>The <dfn data-x="concept-tree-descendant" data-x-href="https://dom.spec.whatwg.org/#concept-tree-descendant">descendant</dfn> concept</li>
<li>The <dfn data-x="concept-node-insert-ext" data-x-href="https://dom.spec.whatwg.org/#concept-node-insert-ext">insertion steps</dfn>,
<li>The <dfn data-x="concept-node-post-insert-ext" data-x-href="https://dom.spec.whatwg.org/#concept-node-post-insert-ext">post-insertion steps</dfn>,
<dfn data-x="concept-node-remove-ext" data-x-href="https://dom.spec.whatwg.org/#concept-node-remove-ext">removing steps</dfn>,
<dfn data-x="concept-node-adopt-ext" data-x-href="https://dom.spec.whatwg.org/#concept-node-adopt-ext">adopting steps</dfn>, and
<dfn data-x-href="https://dom.spec.whatwg.org/#concept-node-children-changed-ext">children changed steps</dfn> hooks for elements</li>
Expand Down Expand Up @@ -62242,22 +62255,104 @@ o............A....e

<hr>

<p>When a <code>script</code> element <var>el</var> that is not <span>parser-inserted</span>
experiences one of the events listed in the following list, the user agent must
<span>immediately</span> <span>prepare the script element</span> <var>el</var>:</p>
<p>The <code>script</code> <span>HTML element post-insertion steps</span>, given
<var>insertedNode</var>, are:</p>

<ul>
<li>The <code>script</code> element <span>becomes connected</span>.</li>
<ol>
<li>
<p>If <var>insertedNode</var> is not <span>connected</span>, then return.</p>

<div class="example">
<p>This can happen in the case where an earlier-inserted script removes a later-inserted
script. For example:</p>
domfarolino marked this conversation as resolved.
Show resolved Hide resolved

<li>The <code>script</code> element is <span>connected</span> and a node or document fragment is
<span data-x="concept-node-insert-ext">inserted</span> into the <code>script</code> element,
after any <code>script</code> elements <span data-x="concept-node-insert-ext">inserted</span>
at that time.</li>
<pre><code class="html">&lt;script>
const script1 = document.createElement('script');
script1.innerText = `
document.querySelector('#script2').remove();
`;

<li>The <code>script</code> element is <span>connected</span> and has a <code
data-x="attr-script-src">src</code> attribute set where previously the element had no such
attribute.</li>
</ul>
const script2 = document.createElement('script');
script2.id = 'script2';
script2.innerText = `console.log('script#2 running')`;

document.body.append(script1, script2);
&lt;/script></code></pre>

<p>Nothing is printed to the console in this example. By the time the <span>HTML element
post-insertion steps</span> run for the first script that was atomically inserted by <code
data-x="dom-Node-append">append()</code>, it can observe that the second script is already
<span>connected</span> to the DOM. It removes the second script, so that by the time <i>its</i>
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
<span>HTML element post-insertion steps</span> run, it is no longer <span>connected</span>, and
does not get <span data-x="prepare the script element">prepared</span>.</p>
</div>
</li>

<li><p>If <var>insertedNode</var> is marked as being <span>parser-inserted</span>, then
return.<p></li>

<li><p><span>Prepare the script element</span> given <var>insertedNode</var>.</p></li>
</ol>

<p>The <code>script</code> <span>children changed steps</span> are:</p>

<ol>
<li><p>Run the <code>script</code> <span>HTML element post-insertion steps</span>, given the
<code>script</code> element.</p></li>
</ol>

<div class="example">
<p>This has an interesting implication on the execution order of a <code>script</code> element
and any newly-inserted child <code>script</code> elements. Consider the following example:</p>
domfarolino marked this conversation as resolved.
Show resolved Hide resolved

<pre><code class="html">&lt;script id=outer-script>&lt;/script>

&lt;script>
const outerScript = document.querySelector('#outer-script');

const start = new Text("console.log(1);");
const innerScript = document.createElement('script');
innerScript.innerText = `console.log('inner script executing')`;
const end = new Text("console.log(2);");

outerScript.append(start, innerScript, end);

// Logs:
// 1
// 2
// inner script executing
&lt;/script></code></pre>

<p>By the time the second script block executes, the <code data-x="">outer-script</code> has
already been <span data-x="prepare the script element">prepared</span>, but beacuse it is empty,
it did not execute and therefore is not marked as <span>already started</span>. The atomic
insertion of the <code>Text</code> nodes and nested <code>script</code> element have the
following effects:</p>

<ol>
<li><p>All three child nodes get atomically inserted as children of <code
data-x="">outer-script</code>; all of their <span>HTML element insertion steps</span> run,
which have no observable consequences in this case.</p></li>

<li><p>The <code data-x="">outer-script</code>'s <span>children changed steps</span> run, which
<span data-x="prepare the script element">prepares</span> that script; because its body is now
non-empty, this executes the contents of the two <code>Text</code> nodes, in order.</p></li>

<li><p>The <code>script</code> <span>HTML element post-insertion steps</span> finally run for
<code data-x="">innerScript</code>, causing its body to execute.</p></li>
</ol>
</div>

domfarolino marked this conversation as resolved.
Show resolved Hide resolved
<p>The <code data-x="attr-script-src">src</code> attribute setter sets are:</p>
domfarolino marked this conversation as resolved.
Show resolved Hide resolved

<ol>
<li><p>If the previous value of the <code>script</code>'s <code
data-x="attr-script-src">src</code> content attribute was not <span data-x="">empty</span>, then
return.</p></li>

<li><p>Run the <code>script</code> <span>HTML element post-insertion steps</span>, given the
<code>script</code> element.</p></li>
</ol>

<p id="prepare-a-script">To <dfn>prepare the script element</dfn> given a <code>script</code>
element <var>el</var>:</p>
Expand Down