Skip to content

Commit

Permalink
Implement imperative slotting API changes
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mfreed7 authored and chromium-wpt-export-bot committed Apr 15, 2021
1 parent d0d5cb6 commit 084f000
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
let tTree = setupShadowDOM(test_slotchange, test, data);
let [s1Promise, s2Promise] = monitorSlots(data);

assert_throws_dom('NotAllowedError', () => { tTree.s1.assign([tTree.c4]); });
tTree.s1.assign([]);;
tTree.s2.assign([]);
tTree.host.insertBefore(tTree.c4, tTree.c1);

Expand Down Expand Up @@ -135,7 +135,7 @@

[s1Promise] = monitorSlots(data);
tTree.s1.assign([tTree.c1, tTree.c2]);
tTree.s1.assign([tTree.c2, tTree.c1, tTree.c1, tTree.c2, tTree.c2]);
tTree.s1.assign([tTree.c1, tTree.c2, tTree.c1, tTree.c2, tTree.c2]);

s1Promise.then(test.step_func_done(() => {
assert_equals(data.s1EventCount, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,47 +15,14 @@
let tTree = createTestTree(test_basic);
assert_not_equals(tTree.host1.attachShadow({ mode: 'open', slotAssignment: 'manual'}),
null, 'slot assignment manual should work');
assert_not_equals(tTree.host2.attachShadow({ mode: 'open', slotAssignment: 'name'}),
assert_not_equals(tTree.host2.attachShadow({ mode: 'open', slotAssignment: 'named'}),
null, 'slot assignment auto should work');
assert_throws_js(TypeError, () => {
tTree.host3.attachShadow({ mode: 'open', slotAssignment: 'exceptional' })},
'others should throw exception');
}, 'attachShadow can take slotAssignment parameter.');
</script>

<div id="test_errors">
<div id="host1">
<template data-mode="open" data-slot-assignment="name">
<slot id="s1"></slot>
</template>
<div id="c1"></div>
</div>
<div id="host2">
<template data-mode="open" data-slot-assignment="manual">
<slot id="s2"></slot>
</template>
</div>
<div id="c2"></div>
</div>
<script>
test(() => {
let tTree = createTestTree(test_errors);
assert_array_equals(tTree.s1.assignedElements(), [tTree.c1]);
assert_equals(tTree.c1.assignedSlot, tTree.s1);

assert_throws_dom('NotAllowedError', () => { tTree.s1.assign([]); });
assert_array_equals(tTree.s1.assignedElements(), [tTree.c1]);
assert_equals(tTree.c1.assignedSlot, tTree.s1);
}, 'Imperative slot API throws exception when not slotAssignment != \'manual\'.');

test(() => {
let tTree = createTestTree(test_errors);
assert_throws_dom('NotAllowedError', () => { tTree.s2.assign([tTree.c2]); });

assert_throws_dom('NotAllowedError', () => { tTree.s2.assign([tTree.host1]); });
}, 'Imperative slot API throws exception when slottable parentNode != slot\'s host.');
</script>

<div id="test_assign">
<div id="host">
<template id="shadow_root" data-mode="open" data-slot-assignment="manual">
Expand Down Expand Up @@ -155,34 +122,31 @@
let tTree = createTestTree(test_assign);

// tTree.c4 is invalid for tTree.host slot assignment.
try {
tTree.s1.assign([tTree.c1, tTree.c2, tTree.c4]);
assert_unreached('assign() should have failed) ');
} catch (err) {
assert_equals(err.name, 'NotAllowedError');
}
// No exception should be thrown here.
tTree.s1.assign([tTree.c1, tTree.c4, tTree.c2]);

assert_array_equals(tTree.s1.assignedNodes(), []);
assert_equals(tTree.c1.assignedSlot, null);
assert_equals(tTree.c2.assignedSlot, null);
// All observable assignments should skip c4.
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
assert_equals(tTree.c1.assignedSlot, tTree.s1);
assert_equals(tTree.c2.assignedSlot, tTree.s1);
assert_equals(tTree.c4.assignedSlot, null);

tTree.s1.assign([tTree.c2, tTree.c3, tTree.c1]);
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c3, tTree.c1]);

try {
tTree.s1.assign([tTree.c4]);
assert_unreached('assign() should have failed) ');
} catch (err) {
assert_equals(err.name, 'NotAllowedError');
}

// Previous state is preserved.
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c3, tTree.c1]);
// Moving c4 into place should reveal the assignment.
tTree.host.append(tTree.c4);
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c4, tTree.c2]);
assert_equals(tTree.c1.assignedSlot, tTree.s1);
assert_equals(tTree.c2.assignedSlot, tTree.s1);
assert_equals(tTree.c3.assignedSlot, tTree.s1);
}, 'Assigning invalid nodes causes exception and slot returns to its previous state.');
assert_equals(tTree.c4.assignedSlot, tTree.s1);

// Moving c4 into a different shadow host and back should
// also not break the assignment.
tTree.host4.append(tTree.c4)
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
assert_equals(tTree.c4.assignedSlot, null);
tTree.host.append(tTree.c4);
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c4, tTree.c2]);
assert_equals(tTree.c4.assignedSlot, tTree.s1);
}, 'Assigning invalid nodes should be allowed.');

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

test(() => {
let tTree = createTestTree(test_assign);

tTree.s1.assign([tTree.c1]);
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]);

tTree.shadow_root4.insertBefore(tTree.s1, tTree.s4);
assert_array_equals(tTree.s1.assignedNodes(), []);
// Don't trigger slot assignment on previous shadow root.
// assert_array_equals(tTree.s2.assignedNodes(), []);

tTree.shadow_root.insertBefore(tTree.s1, tTree.s2);
assert_array_equals(tTree.s1.assignedNodes(), []);
}, '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.');

test(() => {
let tTree = createTestTree(test_assign);

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

tTree.s1.assign([tTree.c1, tTree.c1, tTree.c2, tTree.c2, tTree.c1]);
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c1]);
}, 'Assignment with the same node in parameters should be ignored, last one wins.');
assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]);
}, 'Assignment with the same node in parameters should be ignored, first one wins.');

test(() => {
let tTree = createTestTree(test_assign);
Expand All @@ -278,28 +227,4 @@
assert_equals(tTree.c3.assignedSlot, null);
}, 'Removing a slot from DOM resets its slottable\'s slot assignment.');

test(() => {
let tTree = createTestTree(test_assign);

tTree.s1.assign([tTree.c1]);
tTree.s2.assign([tTree.c2]);
tTree.s3.assign([tTree.c3]);
tTree.shadow_root.insertBefore(tTree.s2, tTree.s1);
tTree.shadow_root.insertBefore(tTree.s3, tTree.s1);

assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]);
assert_array_equals(tTree.s2.assignedNodes(), []);
assert_array_equals(tTree.s3.assignedNodes(), []);
assert_equals(tTree.c1.assignedSlot, tTree.s1);
assert_equals(tTree.c2.assignedSlot, null);
assert_equals(tTree.c3.assignedSlot, null);

tTree.s2.remove();

assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]);
assert_array_equals(tTree.s3.assignedNodes(), []);
assert_equals(tTree.c1.assignedSlot, tTree.s1);
assert_equals(tTree.c2.assignedSlot, null);
assert_equals(tTree.c3.assignedSlot, null);
}, 'A slot should be cleared of assigned nodes even if it\'s re-inserted into the same shadow root.');
</script>

0 comments on commit 084f000

Please sign in to comment.