Skip to content

Commit 891d8a3

Browse files
mfreed7chromium-wpt-export-bot
authored andcommitted
Change the behavior of clonable to be more opt-in
See the discussion here: whatwg/html#10107 (comment) The existing *shipped* behavior (i.e. before the `clonable` concept was introduced) was that any declarative shadow root *within a `<template>`* would be automatically cloned, but no others. The semi-new behavior is the `clonable` bit concept, in which all declarative shadow roots have their `clonable` bit set to true, so they automatically get cloned by `cloneNode()`. That's regardless of whether they are inside or outside a template. The new consensus is that the "semi-new" clonable behavior is likely web-incompatible, because clones will just start getting shadow roots included. Plus it wasn't very developer-desirable. The new consensus is therefore to add a `shadowrootclonable` attribute for declarative shadow dom that allows a shadow root to opt-in to this behavior, but the default for all shadow roots will be `clonable=false`. This CL implements the new consensus behind the ShadowRootClonable flag. If the flag is false, the "shipped" behavior will be emulated via setting `clonable` in an equivalent way. See these three spec PRs: whatwg/dom#1246 whatwg/html#10069 whatwg/html#10117 Bug: 1510466 Change-Id: Ice7c7579094eb08b882c4bb44f93045f23b8f222 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5260748 Reviewed-by: David Baron <[email protected]> Auto-Submit: Mason Freed <[email protected]> Commit-Queue: Mason Freed <[email protected]> Cr-Commit-Position: refs/heads/main@{#1258910}
1 parent afdbabf commit 891d8a3

6 files changed

+69
-35
lines changed

scroll-animations/css/scroll-timeline-name-shadow.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
</style>
3535
<div class=scroller>
3636
<div class=scroller>
37-
<template shadowrootmode=open>
37+
<template shadowrootmode=open shadowrootclonable>
3838
<style>
3939
:host {
4040
scroll-timeline: --timeline y;
@@ -71,7 +71,7 @@
7171
}
7272
</style>
7373
<div class=host>
74-
<template shadowrootmode=open>
74+
<template shadowrootmode=open shadowrootclonable>
7575
<style>
7676
::slotted(.scroller) {
7777
scroll-timeline: --timeline y;
@@ -108,7 +108,7 @@
108108
}
109109
</style>
110110
<div class=host>
111-
<template shadowrootmode=open>
111+
<template shadowrootmode=open shadowrootclonable>
112112
<style>
113113
/* Not using 'anim' at document scope, due to https://crbug.com/1334534 */
114114
@keyframes anim2 {
@@ -152,7 +152,7 @@
152152
</style>
153153
<div class=scroller>
154154
<div class=host>
155-
<template shadowrootmode=open>
155+
<template shadowrootmode=open shadowrootclonable>
156156
<style>
157157
div {
158158
scroll-timeline: --timeline y;

scroll-animations/css/view-timeline-name-shadow.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<div class=scroller>
3737
<div>
3838
<div class=target>
39-
<template shadowrootmode=open>
39+
<template shadowrootmode=open shadowrootclonable>
4040
<style>
4141
:host {
4242
view-timeline: --timeline y;
@@ -73,7 +73,7 @@
7373
</style>
7474
<div class=scroller>
7575
<div class=host>
76-
<template shadowrootmode=open>
76+
<template shadowrootmode=open shadowrootclonable>
7777
<style>
7878
::slotted(.target) {
7979
view-timeline: --timeline y;
@@ -109,7 +109,7 @@
109109
}
110110
</style>
111111
<div class=host>
112-
<template shadowrootmode=open>
112+
<template shadowrootmode=open shadowrootclonable>
113113
<style>
114114
/* Not using 'anim' at document scope, due to https://crbug.com/1334534 */
115115
@keyframes anim2 {
@@ -153,7 +153,7 @@
153153
</style>
154154
<div class=scroller>
155155
<div class=host>
156-
<template shadowrootmode=open>
156+
<template shadowrootmode=open shadowrootclonable>
157157
<style>
158158
div {
159159
view-timeline: --timeline y;

shadow-dom/declarative/declarative-shadow-dom-basic.html

+27-5
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,28 @@
141141
assert_true(!!host.shadowRoot,"No shadow root found");
142142
assert_false(host.shadowRoot.delegatesFocus,"delegatesFocus should be false without the shadowrootdelegatesfocus attribute");
143143
}, 'Declarative Shadow DOM: delegates focus attribute');
144+
145+
test(() => {
146+
const div = document.createElement('div');
147+
div.setHTMLUnsafe(`
148+
<div id="host">
149+
<template shadowrootmode="open" shadowrootclonable>
150+
</template>
151+
</div>
152+
`);
153+
var host = div.querySelector('#host');
154+
assert_true(!!host.shadowRoot,"No shadow root found");
155+
assert_true(host.shadowRoot.clonable,"clonable should be true");
156+
div.setHTMLUnsafe(`
157+
<div id="host">
158+
<template shadowrootmode="open">
159+
</template>
160+
</div>
161+
`);
162+
host = div.querySelector('#host');
163+
assert_true(!!host.shadowRoot,"No shadow root found");
164+
assert_false(host.shadowRoot.clonable,"clonable should be false without the shadowrootclonable attribute");
165+
}, 'Declarative Shadow DOM: clonable attribute');
144166
</script>
145167

146168
<div id="multi-host" style="display:none">
@@ -168,7 +190,7 @@
168190

169191
<template id="template-containing-shadow">
170192
<div class="innerdiv">
171-
<template shadowrootmode=open>Content</template>
193+
<template shadowrootmode=open shadowrootclonable>Content</template>
172194
</div>
173195
</template>
174196
<script>
@@ -205,13 +227,13 @@
205227
assert_equals(innerDiv.querySelector('template'), null, "No leftover template node");
206228
assert_not_equals(shadowRoot1,shadowRoot3,'Should not get back the same shadow root');
207229

208-
}, 'Declarative Shadow DOM: template containing declarative shadow root');
230+
}, 'Declarative Shadow DOM: template containing declarative shadow root (with shadowrootclonable)');
209231
</script>
210232

211233
<template id="template-containing-deep-shadow">
212234
<div><div><div><div><div>
213235
<div class="innerdiv">
214-
<template shadowrootmode=open>Content</template>
236+
<template shadowrootmode=open shadowrootclonable>Content</template>
215237
</div>
216238
</div></div></div></div></div>
217239
</template>
@@ -230,7 +252,7 @@
230252
<div>
231253
<template id="inner-template">
232254
<div class="innerdiv">
233-
<template shadowrootmode=open>Content</template>
255+
<template shadowrootmode=open shadowrootclonable>Content</template>
234256
</div>
235257
</template>
236258
</div>
@@ -249,7 +271,7 @@
249271

250272
<template id="template-containing-ua-shadow">
251273
<div class="innerdiv">
252-
<template shadowrootmode=open>
274+
<template shadowrootmode=open shadowrootclonable>
253275
<video></video> <!--Assumed to have UA shadow root-->
254276
</template>
255277
</div>

shadow-dom/declarative/declarative-shadow-dom-repeats.html

+6-8
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@
4949
</script>
5050

5151
<div id=open2>
52-
<template shadowrootmode=open shadowrootdelegatesfocus>
53-
Open, delegates focus (not the default),
54-
named slot assignment (the default), clonable (the default for declarative)
52+
<template shadowrootmode=open shadowrootdelegatesfocus shadowrootclonable>
53+
Open, delegates focus (not the default), clonable (not the default)
54+
named slot assignment (the default)
5555
</template>
5656
</div>
5757

@@ -66,11 +66,9 @@
6666
assert_throws_dom("NotSupportedError",() => {
6767
open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "manual", clonable: true});
6868
},'Mismatched shadow root slotAssignment should throw');
69-
// See https://github.com/whatwg/html/issues/10107: the behavior of the
70-
// clonable flag is still being discussed.
71-
// assert_throws_dom("NotSupportedError",() => {
72-
// open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "named", clonable: false});
73-
// },'Mismatched shadow root clonable should throw');
69+
assert_throws_dom("NotSupportedError",() => {
70+
open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "named", clonable: false});
71+
},'Mismatched shadow root clonable should throw');
7472

7573
const initialShadow = open2.shadowRoot;
7674
const shadow = open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "named", clonable: true}); // Shouldn't throw

shadow-dom/declarative/gethtml.tentative.html

+12-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<body>
1010

1111
<script>
12-
function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, declarativeShadowDom, mode, delegatesFocus, serializable) {
12+
function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, declarativeShadowDom, mode, delegatesFocus, serializable, clonable) {
1313
const t = test(t => {
1414
// Create and attach element
1515
let wrapper;
@@ -27,7 +27,7 @@
2727

2828
let shadowRoot;
2929
const isOpen = mode === 'open';
30-
let initDict = {mode: mode, delegatesFocus: delegatesFocus};
30+
let initDict = {mode: mode, delegatesFocus: delegatesFocus, clonable};
3131
let expectedSerializable = null;
3232
switch (serializable) {
3333
case undefined: expectedSerializable = false; break;
@@ -37,9 +37,10 @@
3737
}
3838
const delegatesAttr = delegatesFocus ? ' shadowrootdelegatesfocus=""' : '';
3939
const serializableAttr = expectedSerializable ? ' serializable=""' : '';
40+
const clonableAttr = clonable ? ' shadowrootclonable=""' : '';
4041

4142
if (allowsShadowDom && declarativeShadowDom) {
42-
const html = `<${elementType}><template shadowrootmode=${mode}${delegatesAttr}${serializableAttr}>`;
43+
const html = `<${elementType}><template shadowrootmode=${mode}${delegatesAttr}${serializableAttr}${clonableAttr}>`;
4344
wrapper.setHTMLUnsafe(html);
4445
if (isOpen) {
4546
shadowRoot = wrapper.firstElementChild.shadowRoot;
@@ -58,11 +59,12 @@
5859
assert_true(!allowsShadowDom || !!shadowRoot);
5960

6061
if (allowsShadowDom) {
61-
const correctShadowHtml = `<template shadowrootmode="${mode}"${delegatesAttr}${serializableAttr}><slot></slot></template>`;
62+
const correctShadowHtml = `<template shadowrootmode="${mode}"${delegatesAttr}${serializableAttr}${clonableAttr}><slot></slot></template>`;
6263
const correctHtml = `<${elementType}>${correctShadowHtml}</${elementType}>`;
6364
assert_equals(shadowRoot.mode,mode);
6465
assert_equals(shadowRoot.delegatesFocus,delegatesFocus);
6566
assert_equals(shadowRoot.serializable,expectedSerializable);
67+
assert_equals(shadowRoot.clonable,clonable);
6668
shadowRoot.appendChild(document.createElement('slot'));
6769
const emptyElement = `<${elementType}></${elementType}>`;
6870
if (isOpen) {
@@ -91,7 +93,7 @@
9193
// ...and that the default for includeShadowRoots is false.
9294
assert_equals(wrapper.getHTML(),wrapper.innerHTML,'The default for includeShadowRoots should be false');
9395

94-
}, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on <${elementType}>${allowsShadowDom ? `, with ${declarativeShadowDom ? 'declarative' : 'imperative'} shadow, mode=${mode}, delegatesFocus=${delegatesFocus}, serializable=${serializable}.` : ''}`);
96+
}, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on <${elementType}>${allowsShadowDom ? `, with ${declarativeShadowDom ? 'declarative' : 'imperative'} shadow, mode=${mode}, delegatesFocus=${delegatesFocus}, serializable=${serializable}, clonable=${clonable}.` : ''}`);
9597
}
9698

9799
function runAllTests() {
@@ -103,9 +105,11 @@
103105
if (allowsShadowDom) {
104106
for (const declarativeShadowDom of [false, true]) {
105107
for (const delegatesFocus of [false, true]) {
106-
for (const mode of ['open', 'closed']) {
107-
for (const serializable of [undefined, 'false', 'true']) {
108-
testElementType(true, elementName, runGetHTMLOnShadowRoot, declarativeShadowDom, mode, delegatesFocus, serializable);
108+
for (const clonable of [false, true]) {
109+
for (const mode of ['open', 'closed']) {
110+
for (const serializable of [undefined, 'false', 'true']) {
111+
testElementType(true, elementName, runGetHTMLOnShadowRoot, declarativeShadowDom, mode, delegatesFocus, serializable, clonable);
112+
}
109113
}
110114
}
111115
}

shadow-dom/shadow-root-clonable.html

+16-6
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,26 @@
4747
div.setHTMLUnsafe('<div><template shadowrootmode=open><input></template></div>');
4848
const root = div.firstElementChild.shadowRoot;
4949
assert_true(!!root);
50-
assert_true(root.clonable, "clonable is automatically true for declarative shadow root");
50+
assert_false(root.clonable, "clonable is *not* automatically true for declarative shadow root");
51+
52+
const clone = div.cloneNode(true);
53+
const clonedRoot = clone.firstElementChild.shadowRoot;
54+
assert_true(!clonedRoot,'no shadow root gets cloned');
55+
}, "declarative shadow roots do *not* get clonable: true automatically");
56+
57+
test(() => {
58+
const div = document.createElement("div");
59+
div.setHTMLUnsafe('<div><template shadowrootmode=open shadowrootclonable><input></template></div>');
60+
const root = div.firstElementChild.shadowRoot;
61+
assert_true(!!root);
62+
assert_true(root.clonable, "clonable gets added when shadowrootclonable is present");
5163

5264
const clone = div.cloneNode(true);
5365
const clonedRoot = clone.firstElementChild.shadowRoot;
5466
assert_true(!!clonedRoot);
5567
assert_equals(clonedRoot.children.length, 1, "children count");
5668
assert_equals(clonedRoot.children[0].localName, "input", "children content");
57-
}, "declarative shadow roots get clonable: true automatically");
69+
}, "declarative shadow roots can opt in to clonable with shadowrootclonable");
5870
</script>
5971

6072
<template id="test">
@@ -70,8 +82,6 @@
7082
assert_true(!!root);
7183
const clone = template.content.cloneNode(true);
7284
const clonedRoot = clone.querySelector('#host').shadowRoot;
73-
assert_true(!!clonedRoot);
74-
assert_equals(clonedRoot.children.length, 1, "children count");
75-
assert_equals(clonedRoot.children[0].localName, "input", "children content");
76-
}, "declarative shadow roots inside templates also get cloned automatically");
85+
assert_true(!clonedRoot,'no shadow root gets cloned');
86+
}, "declarative shadow roots inside templates do *not* get cloned automatically");
7787
</script>

0 commit comments

Comments
 (0)