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-connection steps for <script> elements #10188

Merged
merged 14 commits into from
Aug 29, 2024
130 changes: 113 additions & 17 deletions source
Original file line number Diff line number Diff line change
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-connection 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-connection steps</span> for the HTML
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
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-connection steps">HTML element post-connection
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-connection 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-connection-ext">post-connection 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,105 @@ 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-connection 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>

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

<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>
<pre><code class="html">&lt;script>
const script1 = document.createElement('script');
script1.innerText = `
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
document.querySelector('#script2').remove();
`;

const script2 = document.createElement('script');
script2.id = 'script2';
script2.textContent = `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-connection steps</span> run for the first <code>script</code> that was atomically inserted
by <code data-x="dom-Node-append">append()</code>, it can observe that the second
<code>script</code> is already <span>connected</span> to the DOM. It removes the second
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
<code>script</code>, so that by the time <em>its</em> <span>HTML element post-connection
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 <span>parser-inserted</span>, then return.<p></li>
domfarolino marked this conversation as resolved.
Show resolved Hide resolved

<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-connection 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 snippet:</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.textContent = `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 because it is empty,
it did not execute and therefore is not marked as <span>already started</span>. The atomic
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
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 data-x="concept-node-insert-ext">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-connection 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 following <span data-x="concept-element-attributes-change-ext">attribute change
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
steps</span>, given <var>element</var>, <var>localName</var>, <var>oldValue</var>,
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
<var>value</var>, and <var>namespace</var>, are used for all <code>script</code> elements:</p>

<ol>
<li><p>If <var>namespace</var> is not null, then return.</p></li>

domfarolino marked this conversation as resolved.
Show resolved Hide resolved
<li><p>If <var>localName</var> is <code data-x="attr-script-src">src</code>, then run the
<code>script</code> <span>HTML element post-connection steps</span>, given
<var>element</var>.</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