Bug 1899960 - P2: Imply an unavailable state if focusable is in unavailable subtree. r=Jamie

I decided not to use aria-disabled directly because we currently don't
cache it in the parent, and I don't think the benefit would be great
enough.

Differential Revision: https://phabricator.services.mozilla.com/D250357
This commit is contained in:
Eitan Isaacson
2025-05-23 17:17:54 +00:00
committed by eisaacson@mozilla.com
parent f491f2de6e
commit 5906bff6eb
3 changed files with 39 additions and 12 deletions

View File

@@ -716,6 +716,21 @@ void Accessible::ApplyImplicitState(uint64_t& aState) const {
if (aState & kExpandCollapseStates) {
aState |= states::EXPANDABLE;
}
if (aState & states::FOCUSABLE && !(aState & states::UNAVAILABLE)) {
// Propagate UNAVAILABLE state from ancestors down to any focusable
// descendant.
for (auto ancestor = Parent(); ancestor; ancestor = ancestor->Parent()) {
if (ancestor->IsDoc()) {
break;
}
if (ancestor->State() & states::UNAVAILABLE) {
aState |= states::UNAVAILABLE;
break;
}
}
}
}
bool Accessible::NameIsEmpty() const {

View File

@@ -1665,18 +1665,7 @@ void LocalAccessible::ApplyARIAState(uint64_t* aState) const {
}
}
if (*aState & states::FOCUSABLE) {
// Propogate aria-disabled from ancestors down to any focusable descendant.
const LocalAccessible* ancestor = this;
while ((ancestor = ancestor->LocalParent()) && !ancestor->IsDoc()) {
dom::Element* el = ancestor->Elm();
if (el && nsAccUtils::ARIAAttrValueIs(el, nsGkAtoms::aria_disabled,
nsGkAtoms::_true, eCaseMatters)) {
*aState |= states::UNAVAILABLE;
break;
}
}
} else {
if (!(*aState & states::FOCUSABLE)) {
// Sometimes, we use aria-activedescendant targeting something which isn't
// actually a descendant. This is technically a spec violation, but it's a
// useful hack which makes certain things much easier. For example, we use

View File

@@ -778,6 +778,7 @@ addAccessibleTask(
</fieldset>
<div id="ariaDisabled" aria-disabled="true" role="button">ariaDisabled</div>
<input id="enabled">
<div id="ariaDisabledGroup" aria-disabled="true"><input id="inAriaDisabledGroup"></div>
`,
async function testUnavailable(browser, docAcc) {
const input = findAccessibleChildByID(docAcc, "input");
@@ -840,6 +841,28 @@ addAccessibleTask(
// Test a control that is initially enabled.
const enabled = findAccessibleChildByID(docAcc, "enabled");
testStates(enabled, 0, 0, STATE_UNAVAILABLE);
const ariaDisabledGroup = findAccessibleChildByID(
docAcc,
"ariaDisabledGroup"
);
const inAriaDisabledGroup = findAccessibleChildByID(
docAcc,
"inAriaDisabledGroup"
);
testStates(ariaDisabledGroup, STATE_UNAVAILABLE);
testStates(inAriaDisabledGroup, STATE_UNAVAILABLE);
info("Enabling ariaDisabledGroup");
changed = waitForStateChange(ariaDisabledGroup, STATE_UNAVAILABLE, false);
await invokeSetAttribute(
browser,
"ariaDisabledGroup",
"aria-disabled",
null
);
await changed;
testStates(ariaDisabledGroup, 0, 0, STATE_UNAVAILABLE);
testStates(inAriaDisabledGroup, 0, 0, STATE_UNAVAILABLE);
},
{ chrome: true, topLevel: true }
);