From 5906bff6ebd9c12855060629666b7cb9b9a5ef62 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Fri, 23 May 2025 17:17:54 +0000 Subject: [PATCH] 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 --- accessible/basetypes/Accessible.cpp | 15 ++++++++++++ accessible/generic/LocalAccessible.cpp | 13 +---------- .../browser/e10s/browser_caching_states.js | 23 +++++++++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/accessible/basetypes/Accessible.cpp b/accessible/basetypes/Accessible.cpp index e2b53c73f6c4..013fb952c5a9 100644 --- a/accessible/basetypes/Accessible.cpp +++ b/accessible/basetypes/Accessible.cpp @@ -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 { diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp index ef354364c814..3faaa3e0ba9e 100644 --- a/accessible/generic/LocalAccessible.cpp +++ b/accessible/generic/LocalAccessible.cpp @@ -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 diff --git a/accessible/tests/browser/e10s/browser_caching_states.js b/accessible/tests/browser/e10s/browser_caching_states.js index aa2654d77adf..caa24ddda7fe 100644 --- a/accessible/tests/browser/e10s/browser_caching_states.js +++ b/accessible/tests/browser/e10s/browser_caching_states.js @@ -778,6 +778,7 @@ addAccessibleTask(
ariaDisabled
+
`, 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 } );