Skip to content

Commit 79d2dec

Browse files
committedFeb 15, 2024
Add shadowrootclonable and align with declarative shadow root changes
https://bugs.webkit.org/show_bug.cgi?id=269361 Reviewed by Ryosuke Niwa. This makes the following changes: - Adds the new shadowrootclonable attribute to opt into a declarative shadow root being clonable. - As a result, declarative shadow roots are no longer clonable by default. Web developers will have to explicitly opt in. - When attachShadow() is called on a shadow host with an existing declarative tree, throw if mode is a mismatch. - In attachShadow() throw first for mode being set to "user-agent" as this is to be caught by the binding layer in theory. - And finally, only attach a declarative shadow root successfully for the first template element. New tests are upstreamed here: web-platform-tests/wpt#44568 Specification changes are here (not all have landed yet as various nits are still being addressed, but all have agreement): - whatwg/html#10117 - whatwg/html#10069 - whatwg/dom#1246 * LayoutTests/imported/w3c/web-platform-tests/scroll-animations/css/scroll-timeline-name-shadow.html: * LayoutTests/imported/w3c/web-platform-tests/scroll-animations/css/view-timeline-name-shadow.html: * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-attachment-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-basic-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats-2-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats-2.html: Added. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats.html: * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/w3c-import.log: * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/shadow-root-clonable-expected.txt: * Source/WebCore/dom/Element.cpp: * Source/WebCore/dom/Element.h: * Source/WebCore/html/HTMLAttributeNames.in: * Source/WebCore/html/HTMLTemplateElement.cpp: (WebCore::HTMLTemplateElement::attachAsDeclarativeShadowRootIfNeeded): * Source/WebCore/html/parser/HTMLConstructionSite.cpp: (WebCore::HTMLConstructionSite::insertHTMLTemplateElement): Canonical link: https://commits.webkit.org/274727@main
1 parent b60f7f2 commit 79d2dec

15 files changed

+156
-254
lines changed
 

‎LayoutTests/imported/w3c/web-platform-tests/scroll-animations/css/scroll-timeline-name-shadow.html

+4-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
<script src="/resources/testharness.js"></script>
77
<script src="/resources/testharnessreport.js"></script>
88
<script src="/web-animations/testcommon.js"></script>
9-
<script src="/resources/declarative-shadow-dom-polyfill.js"></script>
109

1110
<main id=main></main>
1211
<script>
@@ -15,10 +14,6 @@
1514
main.append(template.content.cloneNode(true));
1615
main.offsetTop;
1716
}
18-
19-
setup(() => {
20-
polyfill_declarative_shadow_dom(document);
21-
});
2217
</script>
2318
<style>
2419
@keyframes anim {
@@ -39,7 +34,7 @@
3934
</style>
4035
<div class=scroller>
4136
<div class=scroller>
42-
<template shadowrootmode=open>
37+
<template shadowrootmode=open shadowrootclonable>
4338
<style>
4439
:host {
4540
scroll-timeline: --timeline y;
@@ -76,7 +71,7 @@
7671
}
7772
</style>
7873
<div class=host>
79-
<template shadowrootmode=open>
74+
<template shadowrootmode=open shadowrootclonable>
8075
<style>
8176
::slotted(.scroller) {
8277
scroll-timeline: --timeline y;
@@ -113,7 +108,7 @@
113108
}
114109
</style>
115110
<div class=host>
116-
<template shadowrootmode=open>
111+
<template shadowrootmode=open shadowrootclonable>
117112
<style>
118113
/* Not using 'anim' at document scope, due to https://crbug.com/1334534 */
119114
@keyframes anim2 {
@@ -157,7 +152,7 @@
157152
</style>
158153
<div class=scroller>
159154
<div class=host>
160-
<template shadowrootmode=open>
155+
<template shadowrootmode=open shadowrootclonable>
161156
<style>
162157
div {
163158
scroll-timeline: --timeline y;

‎LayoutTests/imported/w3c/web-platform-tests/scroll-animations/css/view-timeline-name-shadow.html

+4-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
<script src="/resources/testharness.js"></script>
77
<script src="/resources/testharnessreport.js"></script>
88
<script src="/web-animations/testcommon.js"></script>
9-
<script src="/resources/declarative-shadow-dom-polyfill.js"></script>
109

1110
<main id=main></main>
1211
<script>
@@ -15,10 +14,6 @@
1514
main.append(template.content.cloneNode(true));
1615
main.offsetTop;
1716
}
18-
19-
setup(() => {
20-
polyfill_declarative_shadow_dom(document);
21-
});
2217
</script>
2318
<style>
2419
@keyframes anim {
@@ -41,7 +36,7 @@
4136
<div class=scroller>
4237
<div>
4338
<div class=target>
44-
<template shadowrootmode=open>
39+
<template shadowrootmode=open shadowrootclonable>
4540
<style>
4641
:host {
4742
view-timeline: --timeline y;
@@ -78,7 +73,7 @@
7873
</style>
7974
<div class=scroller>
8075
<div class=host>
81-
<template shadowrootmode=open>
76+
<template shadowrootmode=open shadowrootclonable>
8277
<style>
8378
::slotted(.target) {
8479
view-timeline: --timeline y;
@@ -114,7 +109,7 @@
114109
}
115110
</style>
116111
<div class=host>
117-
<template shadowrootmode=open>
112+
<template shadowrootmode=open shadowrootclonable>
118113
<style>
119114
/* Not using 'anim' at document scope, due to https://crbug.com/1334534 */
120115
@keyframes anim2 {
@@ -158,7 +153,7 @@
158153
</style>
159154
<div class=scroller>
160155
<div class=host>
161-
<template shadowrootmode=open>
156+
<template shadowrootmode=open shadowrootclonable>
162157
<style>
163158
div {
164159
view-timeline: --timeline y;

‎LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-attachment-expected.txt

+68-204
Large diffs are not rendered by default.

‎LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-basic-expected.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ PASS Declarative Shadow DOM: Invalid shadow root attribute
88
PASS Declarative Shadow DOM: Closed shadow root attribute
99
PASS Declarative Shadow DOM: Missing closing tag
1010
PASS Declarative Shadow DOM: delegates focus attribute
11-
FAIL Declarative Shadow DOM: clonable attribute assert_false: clonable should be false without the shadowrootclonable attribute expected false got true
12-
FAIL Declarative Shadow DOM: Multiple roots assert_true: The second (duplicate) template should be left in the DOM expected true got false
11+
PASS Declarative Shadow DOM: clonable attribute
12+
PASS Declarative Shadow DOM: Multiple roots
1313
PASS Declarative Shadow DOM: template containing declarative shadow root (with shadowrootclonable)
1414
PASS Declarative Shadow DOM: template containing (deeply nested) declarative shadow root
1515
PASS Declarative Shadow DOM: template containing a template containing declarative shadow root
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS Repeated declarative shadow roots keep only the first
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE html>
2+
<title>Duplicate declarative shadow trees</title>
3+
<script src=/resources/testharness.js></script>
4+
<script src=/resources/testharnessreport.js></script>
5+
6+
<div id=multiple1>
7+
<template shadowrootmode=open>1</template>
8+
<template shadowrootmode=open>2</template>
9+
<template shadowrootmode=open>3</template>
10+
</div>
11+
12+
<div id=multiple2>
13+
<template shadowrootmode=closed>1</template>
14+
<template shadowrootmode=closed>2</template>
15+
<template shadowrootmode=open>3</template>
16+
</div>
17+
18+
<script>
19+
test((t) => {
20+
t.add_cleanup(() => {
21+
multiple1.remove();
22+
multiple2.remove();
23+
});
24+
let shadow = multiple1.shadowRoot;
25+
assert_true(!!shadow,'Remaining shadow root should be open');
26+
assert_equals(shadow.textContent,"1");
27+
assert_equals(multiple1.childElementCount, 2);
28+
assert_equals(multiple1.firstElementChild.content.textContent, "2");
29+
assert_equals(multiple1.lastElementChild.content.textContent, "3");
30+
shadow = multiple2.shadowRoot;
31+
assert_false(!!shadow,'Remaining shadow root should be closed');
32+
assert_equals(multiple2.childElementCount, 2);
33+
assert_equals(multiple2.firstElementChild.content.textContent, "2");
34+
assert_equals(multiple2.lastElementChild.content.textContent, "3");
35+
},'Repeated declarative shadow roots keep only the first');
36+
</script>
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11

2-
FAIL Repeated declarative shadow roots keep only the first assert_equals: expected "Open" but got "Closed"
3-
FAIL Calling attachShadow() on declarative shadow root must match type assert_throws_dom: Mismatched shadow root type should throw function "() => {
4-
open1.attachShadow({mode: "closed"});
5-
}" did not throw
6-
FAIL Calling attachShadow() on declarative shadow root must match all parameters assert_throws_dom: Mismatched shadow root type should throw function "() => {
7-
open2.attachShadow({mode: "closed", delegatesFocus: true, slotAssignment: "named", clonable: true});
8-
}" did not throw
2+
PASS Repeated declarative shadow roots keep only the first
3+
PASS Calling attachShadow() on declarative shadow root must match type
4+
PASS Calling attachShadow() on declarative shadow root must match all parameters
95

‎LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats.html

+14-12
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,19 @@
2222

2323
<script>
2424
test((t) => {
25+
t.add_cleanup(() => {
26+
multiple1.remove();
27+
multiple2.remove();
28+
});
2529
let shadow = multiple1.shadowRoot;
2630
assert_true(!!shadow,'Remaining shadow root should be open');
2731
assert_equals(shadow.textContent,"Open");
32+
assert_equals(multiple1.childElementCount, 1);
33+
assert_equals(multiple1.firstElementChild.shadowRootMode, "closed");
2834
shadow = multiple2.shadowRoot;
2935
assert_false(!!shadow,'Remaining shadow root should be closed');
30-
multiple1.remove(); // Cleanup
31-
multiple2.remove();
36+
assert_equals(multiple2.childElementCount, 1);
37+
assert_equals(multiple2.firstElementChild.shadowRootMode, "open");
3238
},'Repeated declarative shadow roots keep only the first');
3339
</script>
3440

@@ -59,20 +65,16 @@
5965
test((t) => {
6066
assert_throws_dom("NotSupportedError",() => {
6167
open2.attachShadow({mode: "closed", delegatesFocus: true, slotAssignment: "named", clonable: true});
62-
},'Mismatched shadow root type should throw');
63-
assert_throws_dom("NotSupportedError",() => {
64-
open2.attachShadow({mode: "open", delegatesFocus: false, slotAssignment: "named", clonable: true});
65-
},'Mismatched shadow root delegatesFocus should throw');
66-
assert_throws_dom("NotSupportedError",() => {
67-
open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "manual", clonable: true});
68-
},'Mismatched shadow root slotAssignment 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');
68+
},'Mismatched shadow root mode should throw');
7269

7370
const initialShadow = open2.shadowRoot;
7471
const shadow = open2.attachShadow({mode: "open", delegatesFocus: true, slotAssignment: "named", clonable: true}); // Shouldn't throw
7572
assert_equals(shadow,initialShadow,'Same shadow should be returned');
7673
assert_equals(shadow.textContent,'','Shadow should be empty');
74+
75+
assert_throws_dom("NotSupportedError",() => {
76+
open2.attachShadow({mode: "open"});
77+
},'Invoking attachShadow() on a non-declarative shadow root should throw');
78+
7779
},'Calling attachShadow() on declarative shadow root must match all parameters');
7880
</script>

‎LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/w3c-import.log

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ List of files:
2020
/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-available-to-element-internals.html
2121
/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-basic.html
2222
/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-opt-in.html
23+
/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats-2.html
2324
/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats.html
2425
/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-with-disabled-shadow.html
2526
/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml.tentative.html

‎LayoutTests/imported/w3c/web-platform-tests/shadow-dom/shadow-root-clonable-expected.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
FAIL attachShadow with clonable: true null is not an object (evaluating 'shallowClonedRoot.clonable')
33
PASS attachShadow with clonable: false
44
PASS attachShadow with clonable: undefined
5-
FAIL declarative shadow roots do *not* get clonable: true automatically assert_false: clonable is *not* automatically true for declarative shadow root expected false got true
5+
PASS declarative shadow roots do *not* get clonable: true automatically
66
PASS declarative shadow roots can opt in to clonable with shadowrootclonable
7-
FAIL declarative shadow roots inside templates do *not* get cloned automatically assert_true: no shadow root gets cloned expected true got false
7+
PASS declarative shadow roots inside templates do *not* get cloned automatically
88

‎Source/WebCore/dom/Element.cpp

+8-4
Original file line numberDiff line numberDiff line change
@@ -2946,19 +2946,21 @@ static bool canAttachAuthorShadowRoot(const Element& element)
29462946

29472947
ExceptionOr<ShadowRoot&> Element::attachShadow(const ShadowRootInit& init)
29482948
{
2949+
if (init.mode == ShadowRootMode::UserAgent)
2950+
return Exception { ExceptionCode::TypeError };
29492951
if (!canAttachAuthorShadowRoot(*this))
29502952
return Exception { ExceptionCode::NotSupportedError };
29512953
if (RefPtr shadowRoot = this->shadowRoot()) {
29522954
if (shadowRoot->isDeclarativeShadowRoot()) {
2955+
if (init.mode != shadowRoot->mode())
2956+
return Exception { ExceptionCode::NotSupportedError };
29532957
ChildListMutationScope mutation(*shadowRoot);
29542958
shadowRoot->removeChildren();
29552959
shadowRoot->setIsDeclarativeShadowRoot(false);
29562960
return *shadowRoot;
29572961
}
29582962
return Exception { ExceptionCode::NotSupportedError };
29592963
}
2960-
if (init.mode == ShadowRootMode::UserAgent)
2961-
return Exception { ExceptionCode::TypeError };
29622964
Ref shadow = ShadowRoot::create(document(), init.mode, init.slotAssignment,
29632965
init.delegatesFocus ? ShadowRoot::DelegatesFocus::Yes : ShadowRoot::DelegatesFocus::No,
29642966
init.clonable ? ShadowRoot::Clonable::Yes : ShadowRoot::Clonable::No,
@@ -2967,9 +2969,11 @@ ExceptionOr<ShadowRoot&> Element::attachShadow(const ShadowRootInit& init)
29672969
return shadow.get();
29682970
}
29692971

2970-
ExceptionOr<ShadowRoot&> Element::attachDeclarativeShadow(ShadowRootMode mode, bool delegatesFocus)
2972+
ExceptionOr<ShadowRoot&> Element::attachDeclarativeShadow(ShadowRootMode mode, bool delegatesFocus, bool clonable)
29712973
{
2972-
auto exceptionOrShadowRoot = attachShadow({ mode, delegatesFocus, /* clonable */ true });
2974+
if (this->shadowRoot())
2975+
return Exception { ExceptionCode::NotSupportedError };
2976+
auto exceptionOrShadowRoot = attachShadow({ mode, delegatesFocus, clonable });
29732977
if (exceptionOrShadowRoot.hasException())
29742978
return exceptionOrShadowRoot.releaseException();
29752979
Ref shadowRoot = exceptionOrShadowRoot.releaseReturnValue();

‎Source/WebCore/dom/Element.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ class Element : public ContainerNode {
362362
RefPtr<ShadowRoot> shadowRootForBindings(JSC::JSGlobalObject&) const;
363363

364364
WEBCORE_EXPORT ExceptionOr<ShadowRoot&> attachShadow(const ShadowRootInit&);
365-
ExceptionOr<ShadowRoot&> attachDeclarativeShadow(ShadowRootMode, bool delegatesFocus);
365+
ExceptionOr<ShadowRoot&> attachDeclarativeShadow(ShadowRootMode, bool delegatesFocus, bool clonable);
366366

367367
ShadowRoot* userAgentShadowRoot() const;
368368
RefPtr<ShadowRoot> protectedUserAgentShadowRoot() const;

‎Source/WebCore/html/HTMLAttributeNames.in

+2-1
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,9 @@ scrolldelay
385385
scrolling
386386
select
387387
selected
388-
shadowrootmode
388+
shadowrootclonable
389389
shadowrootdelegatesfocus
390+
shadowrootmode
390391
shape
391392
size
392393
sizes

‎Source/WebCore/html/HTMLTemplateElement.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,11 @@ void HTMLTemplateElement::attachAsDeclarativeShadowRootIfNeeded(Element& host)
154154
if (!mode)
155155
return;
156156

157-
bool delegatesFocus = hasAttributeWithoutSynchronization(HTMLNames::shadowrootdelegatesfocusAttr);
157+
auto delegatesFocus = hasAttributeWithoutSynchronization(HTMLNames::shadowrootdelegatesfocusAttr);
158158

159-
auto exceptionOrShadowRoot = host.attachDeclarativeShadow(*mode, delegatesFocus);
159+
auto clonable = hasAttributeWithoutSynchronization(HTMLNames::shadowrootclonableAttr);
160+
161+
auto exceptionOrShadowRoot = host.attachDeclarativeShadow(*mode, delegatesFocus, clonable);
160162
if (exceptionOrShadowRoot.hasException())
161163
return;
162164

‎Source/WebCore/html/parser/HTMLConstructionSite.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ void HTMLConstructionSite::insertHTMLTemplateElement(AtomHTMLToken&& token)
544544
{
545545
if (document().settings().declarativeShadowRootsEnabled() && m_parserContentPolicy.contains(ParserContentPolicy::AllowDeclarativeShadowRoots)) {
546546
std::optional<ShadowRootMode> mode;
547+
bool clonable = false;
547548
bool delegatesFocus = false;
548549
for (auto& attribute : token.attributes()) {
549550
if (attribute.name() == HTMLNames::shadowrootmodeAttr) {
@@ -553,9 +554,11 @@ void HTMLConstructionSite::insertHTMLTemplateElement(AtomHTMLToken&& token)
553554
mode = ShadowRootMode::Open;
554555
} else if (attribute.name() == HTMLNames::shadowrootdelegatesfocusAttr)
555556
delegatesFocus = true;
557+
else if (attribute.name() == HTMLNames::shadowrootclonableAttr)
558+
clonable = true;
556559
}
557560
if (mode && is<Element>(currentNode())) {
558-
auto exceptionOrShadowRoot = currentElement().attachDeclarativeShadow(*mode, delegatesFocus);
561+
auto exceptionOrShadowRoot = currentElement().attachDeclarativeShadow(*mode, delegatesFocus, clonable);
559562
if (!exceptionOrShadowRoot.hasException()) {
560563
Ref shadowRoot = exceptionOrShadowRoot.releaseReturnValue();
561564
auto element = createHTMLElement(token);

0 commit comments

Comments
 (0)
Please sign in to comment.