Bug 1806548: When the slot attribute is changed, remove the Accessible if the element is now unslotted. r=nlapre

If the slot attribute is changed to assign the element to a slot which doesn't exist in the shadow DOM, the element is now unslotted and thus shouldn't be rendered.
We have code in PruneOrInsertSubtree to handle changes like this, but that only works if the frame for the shadow host or something inside the shadow DOM gets reconstructed.
In the case of a shadow host with a single child, there might not necessarily be any frame reconstruction.
To handle this, we listen for changes to the slot attribute.
We check if the element is still part of the flat tree (i.e. it is slotted).
If it isn't, we remove its Accessible.

Differential Revision: https://phabricator.services.mozilla.com/D165135
This commit is contained in:
James Teh 2022-12-22 00:51:27 +00:00
Родитель 0228570301
Коммит 6fc37030c0
2 изменённых файлов: 52 добавлений и 1 удалений

Просмотреть файл

@ -1439,6 +1439,12 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
// also changed. Push a cache update for Relations.
mDoc->QueueCacheUpdate(this, CacheDomain::Relations);
}
if (aAttribute == nsGkAtoms::slot &&
!mContent->GetFlattenedTreeParentNode()) {
// This is inside a shadow host but is no longer slotted.
mDoc->ContentRemoved(this);
}
}
void LocalAccessible::ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize,

Просмотреть файл

@ -80,6 +80,25 @@
}, { SECTION: [{ SECTION: [{ SECTION: [{ name: "green"} ] }] }] }, ["shadowdiv"]);
}
async function changeSlotSingleChild() {
await _dynamicShadowTest("changeSlotSingleChild",
(container, host) => {
container.querySelector('.red').setAttribute('slot', 'invalid');
}, { SECTION: [{ SECTION: [] }] });
}
async function changeSlotNoShadow() {
await _dynamicShadowTest("changeSlotNoShadow",
(container, host) => {
// Make sure changing the slot attribute doesn't remove an element
// even when it remains in the flat tree.
container.querySelector('.red').setAttribute('slot', 'invalid');
// We need a reorder to know when we're done here, so remove another
// child.
container.querySelector('.green').remove();
}, { SECTION: [{ SECTION: [{ name: "red"} ] }] });
}
// Dynamic mutations to both shadow root and shadow host subtrees
// testing/web-platform/tests/css/css-scoping/shadow-assign-dynamic-001.html
async function assignSlotDynamic() {
@ -200,6 +219,10 @@
await changeSlotNested();
await changeSlotSingleChild();
await changeSlotNoShadow();
await assignSlotDynamic();
await shadowFallbackDynamic_1();
@ -295,7 +318,7 @@
</script>
</div>
<div id="changeSlotNested">
<div id="changeSlotNested">
<template class="shadowtree outer">
<div id="shadowdiv">
<slot name="myslot">FAIL</slot>
@ -327,6 +350,28 @@
</script>
</div>
<div id="changeSlotSingleChild">
<template class="shadowtree">
<style>::slotted(div) { width: 100px; height: 100px }</style>
<slot></slot>
</template>
<section class="host">
<div class="red" style="background: red" aria-label="red"></div>
</section>
<script>
document.querySelector("#changeSlotSingleChild > .host")
.attachShadow({ mode: "open" })
.appendChild(document.querySelector("#changeSlotSingleChild > .shadowtree").content.cloneNode(true));
</script>
</div>
<div id="changeSlotNoShadow">
<section class="host">
<div class="red" style="background: red; width: 100px; height: 100px;" aria-label="red"></div>
<div class="green" style="background: green; width: 100px; height: 100px;" aria-label="green"></div>
</section>
</div>
<div id="assignSlotDynamic">
<template class="shadowtree">
<style>::slotted(div) { width: 50px; height: 100px }</style>