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 16, 2024
1 parent 10dbae4 commit da4e899
Showing 1 changed file with 86 additions and 61 deletions.
147 changes: 86 additions & 61 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 @@ -4521,9 +4521,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 @@ -4533,33 +4533,82 @@ each <a for=tree>descendant</a> <a>exclusive <code>Text</code> node</a> <var>nod

<div class=impl>

<div algorithm>
<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 ignore>node</var>, <var ignore>copy</var>, and <var ignore>subtree</var> as
indicated in the <a>clone a 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.
</div>

<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 -->
<div algorithm="clone a node">
<p>To <dfn export id=concept-node-clone>clone a node</dfn> given a <a for=/>node</a> <var>node</var>
and an optional <a for=/>document</a> <dfn export for="clone a node"><var>document</var></dfn>
(default <var>node</var>'s <a for=Node>node document</a>), boolean
<dfn export for="clone a node"><var>subtree</var></dfn> (default false), and
<a for=/>node</a>-or-null <dfn export for="clone a node"><var>parent</var></dfn> (default null):

<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><p><a for=/>Assert</a>: <var>node</var> is not a <a for=/>document</a> or <var>node</var> is
<var>document</var>.

<li><a for=/>Assert</a>: <var>node</var> is not a <a for=/>document</a> or <var>node</var> is
<li><p>Let <var>copy</var> be the result of <a>cloning a single node</a> given <var>node</var> and
<var>document</var>.

<li><p>If <var>parent</var> is non-null, then <a for=/>append</a> <var>copy</var> to
<var>parent</var>.

<li><p>Run any <a>cloning steps</a> defined for <var>node</var> in
<a>other applicable specifications</a> and pass <var>node</var>, <var>copy</var>, and
<var>subtree</var> as parameters.

<li><p>If <var>subtree</var> is true, then for each <var>child</var> of <var>node</var>'s
<a for=tree>children</a>, in <a>tree order</a>: <a>clone a node</a> given <var>child</var> with
<a for="clone a node"><i>document</i></a> set to <var>document</var>,
<a for="clone a node"><i>subtree</i></a> set to <var>subtree</var>, and
<a for="clone a node"><i>parent</i></a> set to <var>copy</var>.

<li>
<p>If <var>node</var> is an <a for=/>element</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><a for=/>Assert</a>: <var>copy</var> is not a <a for=Element>shadow host</a>.

<li><p><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 <var>child</var> of <var>node</var>'s <a for=Element>shadow root</a>'s
<a for=tree>children</a>, in <a>tree order</a>: <a>clone a node</a> given <var>child</var> with
<a for="clone a node"><i>document</i></a> set to <var>document</var>,
<a for="clone a node"><i>subtree</i></a> set to <var>subtree</var>, and
<a for="clone a node"><i>parent</i></a> set to <var>copy</var>'s <a for=Element>shadow root</a>.
</ol>

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

<div algorithm>
<p>To <dfn>clone a single node</dfn> given a <a for=/>node</a> <var>node</var> and
<a for=/>document</a> <var>document</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 @@ -4569,16 +4618,15 @@ dom-Range-extractContents, dom-Range-cloneContents -->
<a for=Element>attribute list</a>:

<ol>
<li><p>Let <var>copyAttribute</var> be a <a>clone</a> of <var>attribute</var>.
<li><p>Let <var>copyAttribute</var> be the result of <a>cloning a single node</a> given
<var>attribute</var> and <var>document</var>.

<li><p><a lt="append an attribute">Append</a> <var>copyAttribute</var> to <var>copy</var>.
</ol>
</li>
</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 @@ -4608,54 +4656,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>

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

<p>The <dfn method for=Node><code>cloneNode(<var>deep</var>)</code></dfn> method steps are:
<div algorithm>
<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> with
<a for="clone a node"><i>subtree</i></a> set to <var>subtree</var>.
</ol>
</div>

<p>A <a for=/>node</a> <var>A</var> <dfn export for=Node id=concept-node-equals>equals</dfn> a
<a for=/>node</a> <var>B</var> if all of the following conditions are true:
Expand Down Expand Up @@ -5127,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 @@ -5554,9 +5576,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 for=/>document</a> or a <a for=/>shadow root</a>, throws a
Expand All @@ -5573,16 +5595,19 @@ method steps are:
"{{HierarchyRequestError!!exception}}" {{DOMException}}.
</dl>

<p>The <dfn method for=Document><code>importNode(<var>node</var>, <var>deep</var>)</code></dfn>
<div algorithm>
<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 for=/>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> with
<a for="clone a node"><i>document</i></a> set to <a>this</a> and
<a for="clone a node"><i>subtree</i></a> set to <var>subtree</var>.
</ol>
</div>

<p><a lt="Other applicable specifications">Specifications</a> may define
<dfn export id=concept-node-adopt-ext>adopting steps</dfn> for all or some <a for=/>nodes</a>. The
Expand Down

0 comments on commit da4e899

Please sign in to comment.