Skip to content

Commit db1be04

Browse files
mfreed7chromium-wpt-export-bot
authored andcommitted
Implement imperative slotting API changes
The original implementation of the imperative slot distribution API was done before the final spec PRs landed. In the process of landing those PRs, several changes were made to the way the API works. Primarily, there are two changes: 1. The "auto" slotAssignment mode was renamed to "named". 2. The "linkage" that is created by HTMLSlotElement.assign() was made more permanent. Previously, moving either the <slot> or the assigned node around in the tree (or across documents) would "break" the linkage. Now, the linkage is more permanent, and the only way to break it is through another call to .assign(). See [1] for the chromestatus entry, [2] for the intent to ship, [3], [4], and [5] for the spec PRs, and [6]/[7] for the landed spec. [1] https://chromestatus.com/feature/4979822998585344 [2] https://groups.google.com/a/chromium.org/g/blink-dev/c/6U78F3KWJ78 [3] whatwg/html#6561 [4] whatwg/html#6585 [5] whatwg/dom#966 [6] https://dom.spec.whatwg.org/#find-slotables [7] https://html.spec.whatwg.org/#dom-slot-assign Fixed: 1196842 Fixed: 1067153 Bug: 1067157 Change-Id: I0ee71043c23f3b49a1461296d722045f06eca540 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2824763 Reviewed-by: Joey Arhar <jarhar@chromium.org> Commit-Queue: Mason Freed <masonf@chromium.org> Cr-Commit-Position: refs/heads/master@{#874413}
1 parent 37ffcaa commit db1be04

File tree

2 files changed

+60
-91
lines changed

2 files changed

+60
-91
lines changed

shadow-dom/slots-imperative-api-slotchange.tentative.html shadow-dom/imperative-slot-api-slotchange.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
let tTree = setupShadowDOM(test_slotchange, test, data);
108108
let [s1Promise, s2Promise] = monitorSlots(data);
109109

110-
assert_throws_dom('NotAllowedError', () => { tTree.s1.assign([tTree.c4]); });
110+
tTree.s1.assign([]);;
111111
tTree.s2.assign([]);
112112
tTree.host.insertBefore(tTree.c4, tTree.c1);
113113

@@ -135,7 +135,7 @@
135135

136136
[s1Promise] = monitorSlots(data);
137137
tTree.s1.assign([tTree.c1, tTree.c2]);
138-
tTree.s1.assign([tTree.c2, tTree.c1, tTree.c1, tTree.c2, tTree.c2]);
138+
tTree.s1.assign([tTree.c1, tTree.c2, tTree.c1, tTree.c2, tTree.c2]);
139139

140140
s1Promise.then(test.step_func_done(() => {
141141
assert_equals(data.s1EventCount, 1);

shadow-dom/slots-imperative-slot-api.tentative.html shadow-dom/imperative-slot-api.html

+58-89
Original file line numberDiff line numberDiff line change
@@ -15,47 +15,14 @@
1515
let tTree = createTestTree(test_basic);
1616
assert_not_equals(tTree.host1.attachShadow({ mode: 'open', slotAssignment: 'manual'}),
1717
null, 'slot assignment manual should work');
18-
assert_not_equals(tTree.host2.attachShadow({ mode: 'open', slotAssignment: 'name'}),
18+
assert_not_equals(tTree.host2.attachShadow({ mode: 'open', slotAssignment: 'named'}),
1919
null, 'slot assignment auto should work');
2020
assert_throws_js(TypeError, () => {
2121
tTree.host3.attachShadow({ mode: 'open', slotAssignment: 'exceptional' })},
2222
'others should throw exception');
2323
}, 'attachShadow can take slotAssignment parameter.');
2424
</script>
2525

26-
<div id="test_errors">
27-
<div id="host1">
28-
<template data-mode="open" data-slot-assignment="name">
29-
<slot id="s1"></slot>
30-
</template>
31-
<div id="c1"></div>
32-
</div>
33-
<div id="host2">
34-
<template data-mode="open" data-slot-assignment="manual">
35-
<slot id="s2"></slot>
36-
</template>
37-
</div>
38-
<div id="c2"></div>
39-
</div>
40-
<script>
41-
test(() => {
42-
let tTree = createTestTree(test_errors);
43-
assert_array_equals(tTree.s1.assignedElements(), [tTree.c1]);
44-
assert_equals(tTree.c1.assignedSlot, tTree.s1);
45-
46-
assert_throws_dom('NotAllowedError', () => { tTree.s1.assign([]); });
47-
assert_array_equals(tTree.s1.assignedElements(), [tTree.c1]);
48-
assert_equals(tTree.c1.assignedSlot, tTree.s1);
49-
}, 'Imperative slot API throws exception when not slotAssignment != \'manual\'.');
50-
51-
test(() => {
52-
let tTree = createTestTree(test_errors);
53-
assert_throws_dom('NotAllowedError', () => { tTree.s2.assign([tTree.c2]); });
54-
55-
assert_throws_dom('NotAllowedError', () => { tTree.s2.assign([tTree.host1]); });
56-
}, 'Imperative slot API throws exception when slottable parentNode != slot\'s host.');
57-
</script>
58-
5926
<div id="test_assign">
6027
<div id="host">
6128
<template id="shadow_root" data-mode="open" data-slot-assignment="manual">
@@ -155,34 +122,31 @@
155122
let tTree = createTestTree(test_assign);
156123

157124
// tTree.c4 is invalid for tTree.host slot assignment.
158-
try {
159-
tTree.s1.assign([tTree.c1, tTree.c2, tTree.c4]);
160-
assert_unreached('assign() should have failed) ');
161-
} catch (err) {
162-
assert_equals(err.name, 'NotAllowedError');
163-
}
125+
// No exception should be thrown here.
126+
tTree.s1.assign([tTree.c1, tTree.c4, tTree.c2]);
164127

165-
assert_array_equals(tTree.s1.assignedNodes(), []);
166-
assert_equals(tTree.c1.assignedSlot, null);
167-
assert_equals(tTree.c2.assignedSlot, null);
128+
// All observable assignments should skip c4.
129+
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
130+
assert_equals(tTree.c1.assignedSlot, tTree.s1);
131+
assert_equals(tTree.c2.assignedSlot, tTree.s1);
168132
assert_equals(tTree.c4.assignedSlot, null);
169133

170-
tTree.s1.assign([tTree.c2, tTree.c3, tTree.c1]);
171-
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c3, tTree.c1]);
172-
173-
try {
174-
tTree.s1.assign([tTree.c4]);
175-
assert_unreached('assign() should have failed) ');
176-
} catch (err) {
177-
assert_equals(err.name, 'NotAllowedError');
178-
}
179-
180-
// Previous state is preserved.
181-
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c3, tTree.c1]);
134+
// Moving c4 into place should reveal the assignment.
135+
tTree.host.append(tTree.c4);
136+
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c4, tTree.c2]);
182137
assert_equals(tTree.c1.assignedSlot, tTree.s1);
183138
assert_equals(tTree.c2.assignedSlot, tTree.s1);
184-
assert_equals(tTree.c3.assignedSlot, tTree.s1);
185-
}, 'Assigning invalid nodes causes exception and slot returns to its previous state.');
139+
assert_equals(tTree.c4.assignedSlot, tTree.s1);
140+
141+
// Moving c4 into a different shadow host and back should
142+
// also not break the assignment.
143+
tTree.host4.append(tTree.c4)
144+
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
145+
assert_equals(tTree.c4.assignedSlot, null);
146+
tTree.host.append(tTree.c4);
147+
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c4, tTree.c2]);
148+
assert_equals(tTree.c4.assignedSlot, tTree.s1);
149+
}, 'Assigning invalid nodes should be allowed.');
186150

187151
test(() => {
188152
let tTree = createTestTree(test_assign);
@@ -227,21 +191,6 @@
227191
assert_equals(tTree.c1.assignedSlot, tTree.s4);
228192
}, 'Appending slottable to different host, it loses slot assignment. It can be re-assigned within a new host.');
229193

230-
test(() => {
231-
let tTree = createTestTree(test_assign);
232-
233-
tTree.s1.assign([tTree.c1]);
234-
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]);
235-
236-
tTree.shadow_root4.insertBefore(tTree.s1, tTree.s4);
237-
assert_array_equals(tTree.s1.assignedNodes(), []);
238-
// Don't trigger slot assignment on previous shadow root.
239-
// assert_array_equals(tTree.s2.assignedNodes(), []);
240-
241-
tTree.shadow_root.insertBefore(tTree.s1, tTree.s2);
242-
assert_array_equals(tTree.s1.assignedNodes(), []);
243-
}, 'Previously assigned node should not be assigned if slot moved to a new shadow root. The slot remains empty when moved back, no trigger recalc.');
244-
245194
test(() => {
246195
let tTree = createTestTree(test_assign);
247196

@@ -264,8 +213,8 @@
264213
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]);
265214

266215
tTree.s1.assign([tTree.c1, tTree.c1, tTree.c2, tTree.c2, tTree.c1]);
267-
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c1]);
268-
}, 'Assignment with the same node in parameters should be ignored, last one wins.');
216+
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
217+
}, 'Assignment with the same node in parameters should be ignored, first one wins.');
269218

270219
test(() => {
271220
let tTree = createTestTree(test_assign);
@@ -281,25 +230,45 @@
281230
test(() => {
282231
let tTree = createTestTree(test_assign);
283232

284-
tTree.s1.assign([tTree.c1]);
285-
tTree.s2.assign([tTree.c2]);
286-
tTree.s3.assign([tTree.c3]);
287-
tTree.shadow_root.insertBefore(tTree.s2, tTree.s1);
288-
tTree.shadow_root.insertBefore(tTree.s3, tTree.s1);
233+
const isolatedDocNode = document.implementation.createHTMLDocument("").body;
234+
isolatedDocNode.appendChild(tTree.c1);
235+
const isolatedDocNode2 = document.implementation.createHTMLDocument("").body;
236+
isolatedDocNode2.appendChild(tTree.s1);
289237

290-
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]);
291-
assert_array_equals(tTree.s2.assignedNodes(), []);
292-
assert_array_equals(tTree.s3.assignedNodes(), []);
293-
assert_equals(tTree.c1.assignedSlot, tTree.s1);
238+
tTree.s1.assign([tTree.c1, tTree.c2]);
239+
assert_array_equals(tTree.s1.assignedNodes(), [], 's1 not inside shadow root');
240+
assert_equals(tTree.c1.assignedSlot, null);
294241
assert_equals(tTree.c2.assignedSlot, null);
295-
assert_equals(tTree.c3.assignedSlot, null);
296242

297-
tTree.s2.remove();
243+
tTree.shadow_root.appendChild(tTree.s1);
244+
tTree.host.appendChild(tTree.c1);
245+
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
246+
assert_equals(tTree.c1.assignedSlot, tTree.s1);
247+
assert_equals(tTree.c2.assignedSlot, tTree.s1);
248+
}, 'Nodes can be assigned even if slots or nodes aren\'t in the same tree.');
298249

299-
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]);
300-
assert_array_equals(tTree.s3.assignedNodes(), []);
250+
test(() => {
251+
let tTree = createTestTree(test_assign);
252+
253+
tTree.s1.assign([tTree.c1, tTree.c2]);
254+
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
301255
assert_equals(tTree.c1.assignedSlot, tTree.s1);
256+
assert_equals(tTree.c2.assignedSlot, tTree.s1);
257+
258+
const isolatedDocNode = document.implementation.createHTMLDocument("").body;
259+
isolatedDocNode.appendChild(tTree.c1);
260+
const isolatedDocNode2 = document.implementation.createHTMLDocument("").body;
261+
isolatedDocNode2.appendChild(tTree.s1);
262+
263+
assert_array_equals(tTree.s1.assignedNodes(), [], 's1 not inside shadow root');
264+
assert_equals(tTree.c1.assignedSlot, null);
302265
assert_equals(tTree.c2.assignedSlot, null);
303-
assert_equals(tTree.c3.assignedSlot, null);
304-
}, 'A slot should be cleared of assigned nodes even if it\'s re-inserted into the same shadow root.');
266+
267+
tTree.shadow_root.appendChild(tTree.s1);
268+
tTree.host.appendChild(tTree.c1);
269+
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
270+
assert_equals(tTree.c1.assignedSlot, tTree.s1);
271+
assert_equals(tTree.c2.assignedSlot, tTree.s1);
272+
}, 'Removing a node from the document does not break manually assigned slot linkage.');
273+
305274
</script>

0 commit comments

Comments
 (0)