Skip to content

Commit

Permalink
Disallow multiple declarative roots and mismatched imperative ones
Browse files Browse the repository at this point in the history
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 <masonf@chromium.org>
Auto-Submit: Mason Freed <masonf@chromium.org>
Commit-Queue: David Baron <dbaron@chromium.org>
Reviewed-by: David Baron <dbaron@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1249214}
  • Loading branch information
mfreed7 authored and chromium-wpt-export-bot committed Jan 19, 2024
1 parent d26404e commit f18d8f8
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 4 deletions.
8 changes: 6 additions & 2 deletions shadow-dom/declarative/declarative-shadow-dom-attachment.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@
originalShadowRoot = element.shadowRoot;
}

// Now, call attachShadow() and make sure we get back the same (original) shadowRoot, but empty.
const oppositeMode = (mode === 'open') ? 'closed' : 'open';
const newShadow = element.attachShadow({mode: oppositeMode}); // Should be no exception here
assert_throws_dom('NotSupportedError', () => {
element.attachShadow({mode: oppositeMode});
}, 'Calling attachShadow with a declarative shadow fails if the mode doesn\'t match');

// Now, call attachShadow() and make sure we get back the same (original) shadowRoot, but empty.
const newShadow = element.attachShadow({mode: mode, delegatesFocus: delegatesFocus});
if (mode === 'open') {
assert_equals(element.shadowRoot, originalShadowRoot, 'The same shadow root should be returned');
assert_equals(element.shadowRoot.innerHTML, '', 'Empty shadow content');
Expand Down
8 changes: 6 additions & 2 deletions shadow-dom/declarative/declarative-shadow-dom-basic.html
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,14 @@
<script>
test(() => {
const host = document.querySelector('#multi-host');
assert_equals(host.querySelector('template'), null, "No leftover template nodes from either root");
const leftover = host.querySelector('template');
assert_true(!!leftover, "The second (duplicate) template should be left in the DOM");
assert_true(leftover instanceof HTMLTemplateElement);
assert_equals(leftover.getAttribute('shadowrootmode'),"closed");
assert_equals(leftover.shadowRootMode,"closed");
assert_true(!!host.shadowRoot,"No open shadow root found - first root should remain");
const innerSpan = host.shadowRoot.querySelector('span');
assert_equals(innerSpan.textContent, 'root 2', "Content should come from last declarative shadow root");
assert_equals(innerSpan.textContent, 'root 1', "Content should come from first declarative shadow root");
}, 'Declarative Shadow DOM: Multiple roots');

</script>
Expand Down
73 changes: 73 additions & 0 deletions shadow-dom/declarative/declarative-shadow-dom-repeats.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!DOCTYPE html>
<title>Declarative Shadow DOM Element Attachment</title>
<link rel='author' href='mailto:masonf@chromium.org'>
<link rel='help' href='https://github.com/whatwg/dom/issues/1235'>
<link rel='help' href='https://github.com/whatwg/html/pull/10069'>
<link rel='help' href='https://github.com/whatwg/dom/pull/1246'>

<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<script src='../../html/resources/common.js'></script>
<script src="support/helpers.js"></script>

<div id=multiple1>
<template shadowrootmode=open>Open</template>
<template shadowrootmode=closed>Closed</template>
</div>

<div id=multiple2>
<template shadowrootmode=closed>Closed</template>
<template shadowrootmode=open>Open</template>
</div>

<script>
test((t) => {
let shadow = multiple1.shadowRoot;
assert_true(!!shadow,'Remaining shadow root should be open');
assert_equals(shadow.textContent,"Open");
shadow = multiple2.shadowRoot;
assert_false(!!shadow,'Remaining shadow root should be closed');
multiple1.remove(); // Cleanup
multiple2.remove();
},'Repeated declarative shadow roots keep only the first');
</script>

<div id=open1>
<template shadowrootmode=open>Open</template>
</div>

<script>
test((t) => {
assert_throws_dom("NotSupportedError",() => {
open1.attachShadow({mode: "closed"});
},'Mismatched shadow root type should throw');
const initialShadow = open1.shadowRoot;
const shadow = open1.attachShadow({mode: "open"}); // Shouldn't throw
assert_equals(shadow,initialShadow,'Same shadow should be returned');
assert_equals(shadow.textContent,'','Shadow should be empty');
},'Calling attachShadow() on declarative shadow root must match type');
</script>

<div id=open2>
<template shadowrootmode=open shadowrootdelegatesfocus>
Open, delegates focus (not the default), named slot assignment (the default)
</template>
</div>

<script>
test((t) => {
assert_throws_dom("NotSupportedError",() => {
open2.attachShadow({mode: "closed", delegatesFocus: true, slotAssignment: "named"});
},'Mismatched shadow root type should throw');
assert_throws_dom("NotSupportedError",() => {
open2.attachShadow({mode: "open", delegatesFocus: false, slotAssignment: "named"});
},'Mismatched shadow root delegatesFocus should throw');
assert_throws_dom("NotSupportedError",() => {
open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "manual"});
},'Mismatched shadow root slotAssignment should throw');
const initialShadow = open2.shadowRoot;
const shadow = open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "named"}); // Shouldn't throw
assert_equals(shadow,initialShadow,'Same shadow should be returned');
assert_equals(shadow.textContent,'','Shadow should be empty');
},'Calling attachShadow() on declarative shadow root must match all parameters');
</script>

0 comments on commit f18d8f8

Please sign in to comment.