Skip to content

Commit ffe797e

Browse files
mfreed7marcoscaceres
authored andcommitted
Disallow multiple declarative roots and mismatched imperative ones
Two changes: 1. If two declarative shadow roots are included within a single host element, only the *first* one will remain. This is a change in behavior from before, but reached consensus [1] and hopefully won't be a breaking change for anyone. 2. If attachShadow() is used on an existing declarative shadow root, and the parameters (e.g. shadow root type, etc.) do not match, an exception is thrown. This, also, is a breaking change, but hopefully not one that gets hit. See also [1]. See [2] and [3] for the corresponding spec PRs. [1] whatwg/dom#1235 [2] whatwg/html#10069 [3] whatwg/dom#1246 Bug: 1379513,1042130 Change-Id: Ia81088227797013f9f62f5ac90f76898663b0bc4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5191750 Commit-Queue: Mason Freed <[email protected]> Auto-Submit: Mason Freed <[email protected]> Commit-Queue: David Baron <[email protected]> Reviewed-by: David Baron <[email protected]> Cr-Commit-Position: refs/heads/main@{#1249214}
1 parent 71a0e49 commit ffe797e

3 files changed

+85
-4
lines changed

shadow-dom/declarative/declarative-shadow-dom-attachment.html

+6-2
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,13 @@
4242
originalShadowRoot = element.shadowRoot;
4343
}
4444

45-
// Now, call attachShadow() and make sure we get back the same (original) shadowRoot, but empty.
4645
const oppositeMode = (mode === 'open') ? 'closed' : 'open';
47-
const newShadow = element.attachShadow({mode: oppositeMode}); // Should be no exception here
46+
assert_throws_dom('NotSupportedError', () => {
47+
element.attachShadow({mode: oppositeMode});
48+
}, 'Calling attachShadow with a declarative shadow fails if the mode doesn\'t match');
49+
50+
// Now, call attachShadow() and make sure we get back the same (original) shadowRoot, but empty.
51+
const newShadow = element.attachShadow({mode: mode, delegatesFocus: delegatesFocus});
4852
if (mode === 'open') {
4953
assert_equals(element.shadowRoot, originalShadowRoot, 'The same shadow root should be returned');
5054
assert_equals(element.shadowRoot.innerHTML, '', 'Empty shadow content');

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,14 @@
154154
<script>
155155
test(() => {
156156
const host = document.querySelector('#multi-host');
157-
assert_equals(host.querySelector('template'), null, "No leftover template nodes from either root");
157+
const leftover = host.querySelector('template');
158+
assert_true(!!leftover, "The second (duplicate) template should be left in the DOM");
159+
assert_true(leftover instanceof HTMLTemplateElement);
160+
assert_equals(leftover.getAttribute('shadowrootmode'),"closed");
161+
assert_equals(leftover.shadowRootMode,"closed");
158162
assert_true(!!host.shadowRoot,"No open shadow root found - first root should remain");
159163
const innerSpan = host.shadowRoot.querySelector('span');
160-
assert_equals(innerSpan.textContent, 'root 2', "Content should come from last declarative shadow root");
164+
assert_equals(innerSpan.textContent, 'root 1', "Content should come from first declarative shadow root");
161165
}, 'Declarative Shadow DOM: Multiple roots');
162166

163167
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!DOCTYPE html>
2+
<title>Declarative Shadow DOM Element Attachment</title>
3+
<link rel='author' href='mailto:[email protected]'>
4+
<link rel='help' href='https://github.com/whatwg/dom/issues/1235'>
5+
<link rel='help' href='https://github.com/whatwg/html/pull/10069'>
6+
<link rel='help' href='https://github.com/whatwg/dom/pull/1246'>
7+
8+
<script src='/resources/testharness.js'></script>
9+
<script src='/resources/testharnessreport.js'></script>
10+
<script src='../../html/resources/common.js'></script>
11+
<script src="support/helpers.js"></script>
12+
13+
<div id=multiple1>
14+
<template shadowrootmode=open>Open</template>
15+
<template shadowrootmode=closed>Closed</template>
16+
</div>
17+
18+
<div id=multiple2>
19+
<template shadowrootmode=closed>Closed</template>
20+
<template shadowrootmode=open>Open</template>
21+
</div>
22+
23+
<script>
24+
test((t) => {
25+
let shadow = multiple1.shadowRoot;
26+
assert_true(!!shadow,'Remaining shadow root should be open');
27+
assert_equals(shadow.textContent,"Open");
28+
shadow = multiple2.shadowRoot;
29+
assert_false(!!shadow,'Remaining shadow root should be closed');
30+
multiple1.remove(); // Cleanup
31+
multiple2.remove();
32+
},'Repeated declarative shadow roots keep only the first');
33+
</script>
34+
35+
<div id=open1>
36+
<template shadowrootmode=open>Open</template>
37+
</div>
38+
39+
<script>
40+
test((t) => {
41+
assert_throws_dom("NotSupportedError",() => {
42+
open1.attachShadow({mode: "closed"});
43+
},'Mismatched shadow root type should throw');
44+
const initialShadow = open1.shadowRoot;
45+
const shadow = open1.attachShadow({mode: "open"}); // Shouldn't throw
46+
assert_equals(shadow,initialShadow,'Same shadow should be returned');
47+
assert_equals(shadow.textContent,'','Shadow should be empty');
48+
},'Calling attachShadow() on declarative shadow root must match type');
49+
</script>
50+
51+
<div id=open2>
52+
<template shadowrootmode=open shadowrootdelegatesfocus>
53+
Open, delegates focus (not the default), named slot assignment (the default)
54+
</template>
55+
</div>
56+
57+
<script>
58+
test((t) => {
59+
assert_throws_dom("NotSupportedError",() => {
60+
open2.attachShadow({mode: "closed", delegatesFocus: true, slotAssignment: "named"});
61+
},'Mismatched shadow root type should throw');
62+
assert_throws_dom("NotSupportedError",() => {
63+
open2.attachShadow({mode: "open", delegatesFocus: false, slotAssignment: "named"});
64+
},'Mismatched shadow root delegatesFocus should throw');
65+
assert_throws_dom("NotSupportedError",() => {
66+
open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "manual"});
67+
},'Mismatched shadow root slotAssignment should throw');
68+
const initialShadow = open2.shadowRoot;
69+
const shadow = open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "named"}); // Shouldn't throw
70+
assert_equals(shadow,initialShadow,'Same shadow should be returned');
71+
assert_equals(shadow.textContent,'','Shadow should be empty');
72+
},'Calling attachShadow() on declarative shadow root must match all parameters');
73+
</script>

0 commit comments

Comments
 (0)