Skip to content
Permalink
Browse files
Mixing manual and named slot assignments causes slotchange event to b…
…e not dispatched

https://bugs.webkit.org/show_bug.cgi?id=243869

Reviewed by Antti Koivisto.

Fixed the bug that manually assigning a node to a slot inside a shadow tree
in the named slot assignment mode does not enqueue slotchange as expected.

* LayoutTests/fast/shadow-dom/imperative-named-slot-mixture-expected.txt: Added.
* LayoutTests/fast/shadow-dom/imperative-named-slot-mixture.html: Added.
* LayoutTests/platform/win/TestExpectations:

* Source/WebCore/html/HTMLSlotElement.cpp:
(WebCore::HTMLSlotElement::assign):

Canonical link: https://commits.webkit.org/253392@main
  • Loading branch information
rniwa committed Aug 12, 2022
1 parent 3a3ad5e commit 00898fe072c607ad789bee1b9d7399a924a35656
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 1 deletion.
@@ -0,0 +1,7 @@

PASS Manually assign a node to a named slot
PASS Assign a node to a manual slot by slot name
PASS Manually assigning a node to a named slot
PASS Manually assigning a node, which was previously assgined to a manual slot, to a default slot
PASS Manually assigning a node, which was previously assgined to a manual slot, and another node to a default slot

@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html>
<body>
<script src="../../resources/gc.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>

promise_test(async function () {
const host = document.createElement('div');
const shadowRoot = host.attachShadow({mode: 'closed'});
const slot = document.createElement('slot');
slot.name = 'foo';
shadowRoot.appendChild(slot);
const child = document.createElement('div');
host.appendChild(child);
slot.assign(child);
assert_array_equals(slot.assignedNodes(), []);
}, 'Manually assign a node to a named slot');

promise_test(async function () {
const host = document.createElement('div');
const shadowRoot = host.attachShadow({mode: 'closed', slotAssignment: 'manual'});
const slot = document.createElement('slot');
slot.name = 'foo';
shadowRoot.appendChild(slot);
const child = document.createElement('div');
child.slot = 'foo';
host.appendChild(child);
assert_array_equals(slot.assignedNodes(), []);
}, 'Assign a node to a manual slot by slot name');

promise_test(async function () {
let logs = [];
function logger(event) { logs.push(this); }
const host = document.createElement('div');
const shadowRoot = host.attachShadow({mode: 'closed'});
const slot = document.createElement('slot');
slot.addEventListener('slotchange', logger);
shadowRoot.appendChild(slot);

const child = document.createElement('div');
host.appendChild(child);
await new Promise(setTimeout);
assert_array_equals(slot.assignedNodes(), [child]);
assert_array_equals(logs, [slot]);
logs = [];

slot.assign(child);
await new Promise(setTimeout);
assert_array_equals(slot.assignedNodes(), [child]);
assert_array_equals(logs, []);
}, 'Manually assigning a node to a named slot');

promise_test(async function () {
let logs = [];
function logger(event) { logs.push(this); }
const host = document.createElement('div');
const shadowRoot = host.attachShadow({mode: 'closed', slotAssignment: 'manual'});
const slot = document.createElement('slot');
slot.addEventListener('slotchange', logger);
shadowRoot.appendChild(slot);

const child = document.createElement('div');
host.appendChild(child);
slot.assign(child);

await new Promise(setTimeout);
assert_array_equals(slot.assignedNodes(), [child]);
assert_array_equals(logs, [slot]);
logs = [];

const namedHost = document.createElement('div');
const namedShadowRoot = namedHost.attachShadow({mode: 'closed', slotAssignment: 'named'});
const namedSlot = document.createElement('slot');
namedSlot.addEventListener('slotchange', logger);
namedShadowRoot.appendChild(namedSlot);
namedSlot.assign(child);

await new Promise(setTimeout);
assert_array_equals(namedSlot.assignedNodes(), []);
assert_array_equals(slot.assignedNodes(), []);
assert_array_equals(logs, [slot]);
}, 'Manually assigning a node, which was previously assgined to a manual slot, to a default slot');

promise_test(async function () {
let logs = [];
function logger(event) { logs.push(this); }
const host = document.createElement('div');
const shadowRoot = host.attachShadow({mode: 'closed', slotAssignment: 'manual'});
const slot = document.createElement('slot');
slot.addEventListener('slotchange', logger);
shadowRoot.appendChild(slot);

const child1 = document.createElement('div');
host.appendChild(child1);
const child2 = document.createElement('div');
host.appendChild(child2);
slot.assign(child1, child2);

await new Promise(setTimeout);
assert_array_equals(slot.assignedNodes(), [child1, child2]);
assert_array_equals(logs, [slot]);
logs = [];

const namedHost = document.createElement('div');
const namedShadowRoot = namedHost.attachShadow({mode: 'closed', slotAssignment: 'named'});
const namedSlot = document.createElement('slot');
namedSlot.addEventListener('slotchange', logger);
namedShadowRoot.appendChild(namedSlot);

const child3 = document.createElement('div');
namedHost.appendChild(child3);

namedSlot.assign(child1, child3);

await new Promise(setTimeout);
assert_array_equals(namedSlot.assignedNodes(), [child3]);
assert_array_equals(slot.assignedNodes(), [child2]);
assert_array_equals(logs, [namedSlot, slot]);
}, 'Manually assigning a node, which was previously assgined to a manual slot, and another node to a default slot');

</script>
</body>
</html>
@@ -3314,6 +3314,7 @@ webkit.org/b/149592 fast/shadow-dom/touch-event-ios.html [ Skip ]

# Imperative slot API isn't enabled on Windows yet.
fast/shadow-dom/manual-assignment-multiple-shadow-roots.html [ Failure ]
fast/shadow-dom/imperative-named-slot-mixture.html [ Failure ]

# The SVG -> OTF Font converter outputs 'kern' tables instead of 'GPOS' tables.
webkit.org/b/137204 fast/text/svg-font-face-with-kerning.html [ Failure ]
@@ -183,7 +183,7 @@ void HTMLSlotElement::assign(FixedVector<std::reference_wrapper<Node>>&& nodes)
return WeakPtr { node };
});

if (RefPtr shadowRoot = containingShadowRoot())
if (RefPtr shadowRoot = containingShadowRoot(); shadowRoot && shadowRoot->slotAssignmentMode() == SlotAssignmentMode::Manual)
shadowRoot->slotManualAssignmentDidChange(*this, previous, m_manuallyAssignedNodes);
else {
for (auto& node : m_manuallyAssignedNodes) {

0 comments on commit 00898fe

Please sign in to comment.