Skip to content

Commit

Permalink
Remove quadratic behavior from clone a node
Browse files Browse the repository at this point in the history
Strictly speaking this should be an editorial change, but as it's fairly significant not marking it as such. This improves integration with HTML as well.

As part of this change we move the shadow tree behavior under creating the element clone as that is a more logical fit.

Unfortunately subtree has to be passed everywhere as HTML needs it for its template element logic. That's also why clone a node's children is exported.

Corresponding HTML PR: SOON.

Fixes #1219. Closes #1220.
  • Loading branch information
annevk committed Dec 12, 2024
1 parent 719e45b commit 6c2b768
Showing 1 changed file with 79 additions and 56 deletions.
135 changes: 79 additions & 56 deletions dom.bs
Original file line number Diff line number Diff line change
Expand Up @@ -4097,7 +4097,7 @@ interface Node : EventTarget {
[CEReactions] attribute DOMString? textContent;
[CEReactions] undefined normalize();

[CEReactions, NewObject] Node cloneNode(optional boolean deep = false);
[CEReactions, NewObject] Node cloneNode(optional boolean subtree = false);
boolean isEqualNode(Node? otherNode);
boolean isSameNode(Node? otherNode); // legacy alias of ===

Expand Down Expand Up @@ -4520,9 +4520,9 @@ each <a for=tree>descendant</a> <a>exclusive <code>Text</code> node</a> <var>nod
<hr>

<dl class=domintro>
<dt><code><var>node</var> . <a method for=Node lt=cloneNode()>cloneNode([<var>deep</var> = false])</a></code>
<dt><code><var>node</var> . <a method for=Node lt=cloneNode()>cloneNode([<var>subtree</var> = false])</a></code>
<dd>Returns a copy of <var>node</var>. If
<var>deep</var> is true, the copy also includes the
<var>subtree</var> is true, the copy also includes the
<var>node</var>'s <a for=tree>descendants</a>.

<dt><code><var>node</var> . {{Node/isEqualNode(otherNode)}}</code>
Expand All @@ -4534,31 +4534,59 @@ each <a for=tree>descendant</a> <a>exclusive <code>Text</code> node</a> <var>nod

<a lt="Other applicable specifications">Specifications</a> may define
<dfn export id=concept-node-clone-ext>cloning steps</dfn> for all or some <a for=/>nodes</a>. The
algorithm is passed <var>copy</var>, <var>node</var>, <var>document</var>, and an optional
<i>clone children flag</i>, as indicated in the <a lt="clone a node">clone</a> algorithm.
algorithm is passed <var>node</var>, <var>copy</var>, and <var>subtree</var> as indicated in the
<a>clone a single node</a> algorithm.

<p class=note>HTML defines <a>cloning steps</a> for <{script}> and <{input}>
elements. SVG ought to do the same for its <{script}> elements, but does not call this out
at the moment.
<p class=note>HTML defines <a>cloning steps</a> for several elements, such as <{input}>, <{script}>,
and <{template}>. SVG ought to do the same for its <{script}> elements, but does not.

<p>To <dfn export id=concept-node-clone lt="clone a node" local-lt="clone">clone</dfn> a
<var>node</var>, with an optional <var>document</var> and <i>clone children flag</i>, run these
steps:
<!-- This algorithm is used by dom-Node-cloneNode, dom-Document-importNode,
dom-Range-extractContents, dom-Range-cloneContents -->
<p>To <dfn export id=concept-node-clone local-lt="clone">clone a node</dfn> given a
<a for=/>node</a> <var>node</var> and an optional <a for=/>document</a> <var>document</var> (default
<var>node</var>'s <a for=Node>node document</a>) and boolean <var>subtree</var> (default false):

<ol>
<li><p>If <var>document</var> is not given, then set <var>document</var> to <var>node</var>'s
<a for=Node>node document</a>.

<li><a for=/>Assert</a>: <var>node</var> is not a <a for=/>document</a> or <var>node</var> is
<var>document</var>.

<li><p>Let <var>copy</var> be the result of <a>cloning a single node</a> given <var>node</var>,
<var>document</var>, and <var>subtree</var>.

<li><p>If <var>subtree</var> is true, then <a>clone a node's children</a> given <var>node</var>,
<var>copy</var>, <var>document</var>, and <var>subtree</var>.

<li><p>Return <var>copy</var>.
</ol>

<p>To <dfn export>clone a node's children</dfn> given a <a for=/>node</a> <var>node</var>, a
<a for=/>node</a> <var>copy</var>, a <a for=/>document</a> <var>document</var>, and a boolean
<var>subtree</var>:

<ol>
<li>
<p>For each <var>child</var> of <var>node</var>'s <a for=tree>children</a>, in <a>tree order</a>:

<ol>
<li><p>Let <var>childCopy</var> be the result of <a>cloning a single node</a> given
<var>node</var>, <var>document</var>, and <var>subtree</var>.

<li><p><a for=/>Append</a> <var>childCopy</var> to <var>copy</var>.

<li><p><a>Clone a node's children</a> given <var>child</var>, <var>childCopy</var>,
<var>document</var>, and <var>subtree</var>.
</ol>
</ol>

<p>To <dfn>clone a single node</dfn> given a <a for=/>node</a> <var>node</var>,
<a for=/>document</a> <var>document</var>, and boolean <var>subtree</var>:

<ol>
<li><p>Let <var>copy</var> be null.

<li>
<p>If <var>node</var> is an <a for=/>element</a>:

<ol>
<li><p>Let <var>copy</var> be the result of <a>creating an element</a>, given
<li><p>Set <var>copy</var> to the result of <a>creating an element</a>, given
<var>document</var>, <var>node</var>'s <a for=Element>local name</a>, <var>node</var>'s
<a for=Element>namespace</a>, <var>node</var>'s <a for=Element>namespace prefix</a>, and
<var>node</var>'s <a><code>is</code> value</a>.
Expand All @@ -4573,11 +4601,31 @@ dom-Range-extractContents, dom-Range-cloneContents -->
<li><p><a lt="append an attribute">Append</a> <var>copyAttribute</var> to <var>copy</var>.
</ol>
</li>

<li>
<p>If <var>node</var> is a <a for=Element>shadow host</a> and its
<a for=Element>shadow root</a>'s <a for=ShadowRoot>clonable</a> is true:

<ol>
<li><p>Assert: <var>copy</var> is not a <a for=Element>shadow host</a>.

<li><p>Run <a>attach a shadow root</a> with <var>copy</var>, <var>node</var>'s
<a for=Element>shadow root</a>'s <a for=ShadowRoot>mode</a>, true, <var>node</var>'s
<a for=Element>shadow root</a>'s <a for=ShadowRoot>serializable</a>, <var>node</var>'s
<a for=Element>shadow root</a>'s <a for=ShadowRoot>delegates focus</a>, and <var>node</var>'s
<a for=Element>shadow root</a>'s <a for=ShadowRoot>slot assignment</a>.

<li><p>Set <var>copy</var>'s <a for=Element>shadow root</a>'s <a for=ShadowRoot>declarative</a>
to <var>node</var>'s <a for=Element>shadow root</a>'s <a for=ShadowRoot>declarative</a>.

<li><p><a>Clone a node's children</a> given <var>node</var>'s <a for=Element>shadow root</a>,
<var>copy</var>'s <a for=Element>shadow root</a>, and <var>document</var>.
</ol>
</ol>
</li>

<li>
<p>Otherwise, let <var>copy</var> be a <a for=/>node</a> that <a>implements</a> the same
<p>Otherwise, set <var>copy</var> to a <a for=/>node</a> that <a>implements</a> the same
interfaces as <var>node</var>, and fulfills these additional requirements, switching on the
interface <var>node</var> <a>implements</a>:

Expand Down Expand Up @@ -4607,53 +4655,28 @@ dom-Range-extractContents, dom-Range-cloneContents -->
<dd><p>Do nothing.
</dl>

<li><p><a for=/>Assert</a>: <var>copy</var> is a <a for=/>node</a>.

<li><p>If <var>node</var> is a <a for=/>document</a>, then set <var>document</var> to
<var>copy</var>.

<li><p>Set <var>copy</var>'s <a for=Node>node document</a> to <var>document</var>.

<li><p>Run any <a>cloning steps</a> defined for <var>node</var> in
<a>other applicable specifications</a> and pass <var>copy</var>, <var>node</var>,
<var>document</var>, and the <i>clone children flag</i> if set, as parameters.

<li><p>If the <i>clone children flag</i> is set, then for each <a for=tree>child</a>
<var>child</var> of <var>node</var>, in <a>tree order</a>: <a>append</a> the result of
<a lt="clone a node">cloning</a> <var>child</var> with <var>document</var> and the
<i>clone children flag</i> set, to <var>copy</var>.

<li>
<p>If <var>node</var> is a <a for=Element>shadow host</a> whose <a for=/>shadow root</a>'s
<a for=ShadowRoot>clonable</a> is true:

<ol>
<li><p>Assert: <var>copy</var> is not a <a for=Element>shadow host</a>.

<li><p>Run <a>attach a shadow root</a> with <var>copy</var>, <var>node</var>'s
<a for=Element>shadow root</a>'s <a for=ShadowRoot>mode</a>, true, <var>node</var>'s
<a for=Element>shadow root</a>'s <a for=ShadowRoot>serializable</a>, <var>node</var>'s
<a for=Element>shadow root</a>'s <a for=ShadowRoot>delegates focus</a>, and <var>node</var>'s
<a for=Element>shadow root</a>'s <a for=ShadowRoot>slot assignment</a>.

<li><p>Set <var>copy</var>'s <a for=Element>shadow root</a>'s <a for=ShadowRoot>declarative</a>
to <var>node</var>'s <a for=Element>shadow root</a>'s <a for=ShadowRoot>declarative</a>.

<li><p>For each <a for=tree>child</a> <var>child</var> of <var>node</var>'s
<a for=Element>shadow root</a>, in <a>tree order</a>: <a>append</a> the result of
<a lt="clone a node">cloning</a> <var>child</var> with <var>document</var> and the
<i>clone children flag</i> set, to <var>copy</var>'s <a for=Element>shadow root</a>.
</ol>
<a>other applicable specifications</a> and pass <var>node</var>, <var>copy</var>, and
<var>subtree</var>.

<li><p>Return <var>copy</var>.
</ol>

<p>The <dfn method for=Node><code>cloneNode(<var>deep</var>)</code></dfn> method steps are:
<p>The <dfn method for=Node><code>cloneNode(<var>subtree</var>)</code></dfn> method steps are:

<ol>
<li><p>If <a>this</a> is a <a for=/>shadow root</a>, then <a>throw</a> a
"{{NotSupportedError!!exception}}" {{DOMException}}.

<li><p>Return a <a lt="clone a node">clone</a> of <a>this</a>, with the
<i>clone children flag</i> set if <var>deep</var> is true.
<li><p>Return the result of <a>cloning a node</a> given <a>this</a>, <a>this</a>'s
<a for=Node>node document</a>, and <var>subtree</var>.
</ol>

<p>A <a for=/>node</a> <var>A</var> <dfn export for=Node id=concept-node-equals>equals</dfn> a
Expand Down Expand Up @@ -5126,7 +5149,7 @@ interface Document : Node {
[NewObject] Comment createComment(DOMString data);
[NewObject] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);

[CEReactions, NewObject] Node importNode(Node node, optional boolean deep = false);
[CEReactions, NewObject] Node importNode(Node node, optional boolean subtree = false);
[CEReactions] Node adoptNode(Node node);

[NewObject] Attr createAttribute(DOMString localName);
Expand Down Expand Up @@ -5552,9 +5575,9 @@ method steps are:
<hr>

<dl class=domintro>
<dt><var>clone</var> = <var>document</var> . <a method for=Document lt=importNode()>importNode(<var>node</var> [, <var>deep</var> = false])</a>
<dt><var>clone</var> = <var>document</var> . <a method for=Document lt=importNode()>importNode(<var>node</var> [, <var>subtree</var> = false])</a>
<dd>
<p>Returns a copy of <var>node</var>. If <var>deep</var> is true, the copy also includes the
<p>Returns a copy of <var>node</var>. If <var>subtree</var> is true, the copy also includes the
<var>node</var>'s <a for=tree>descendants</a>.

<p>If <var>node</var> is a <a>document</a> or a <a for=/>shadow root</a>, throws a
Expand All @@ -5571,15 +5594,15 @@ method steps are:
"{{HierarchyRequestError!!exception}}" {{DOMException}}.
</dl>

<p>The <dfn method for=Document><code>importNode(<var>node</var>, <var>deep</var>)</code></dfn>
<p>The <dfn method for=Document><code>importNode(<var>node</var>, <var>subtree</var>)</code></dfn>
method steps are:

<ol>
<li><p>If <var>node</var> is a <a>document</a> or <a for=/>shadow root</a>, then <a>throw</a> a
"{{NotSupportedError!!exception}}" {{DOMException}}.

<li><p>Return a <a lt="clone a node">clone</a> of <var>node</var>, with <a>this</a> and the
<i>clone children flag</i> set if <var>deep</var> is true.
<li><p>Return the result of <a>cloning a node</a> given <var>node</var>, <a>this</a>, and
<var>subtree</var>.
</ol>

<p><a lt="Other applicable specifications">Specifications</a> may define
Expand Down

0 comments on commit 6c2b768

Please sign in to comment.