diff --git a/accessible/base/States.h b/accessible/base/States.h index 09b0a21d29d1..5588568d89ba 100644 --- a/accessible/base/States.h +++ b/accessible/base/States.h @@ -295,9 +295,9 @@ const uint64_t LAST_ENTRY = CURRENT; /** * States that must be calculated by RemoteAccessible and are thus not cached. */ -const uint64_t kRemoteCalculatedStates = states::FOCUSED | states::INVISIBLE | - states::OFFSCREEN | states::SENSITIVE | - states::OPAQUE1; +const uint64_t kRemoteCalculatedStates = + states::FOCUSED | states::INVISIBLE | states::OFFSCREEN | states::ENABLED | + states::SENSITIVE | states::COLLAPSED | states::OPAQUE1; } // namespace a11y } // namespace mozilla diff --git a/accessible/basetypes/Accessible.cpp b/accessible/basetypes/Accessible.cpp index e77b2861d3ac..8e439040f93f 100644 --- a/accessible/basetypes/Accessible.cpp +++ b/accessible/basetypes/Accessible.cpp @@ -698,35 +698,6 @@ void Accessible::ApplyImplicitState(uint64_t& aState) const { if (Opacity() == 1.0f && !(aState & states::INVISIBLE)) { aState |= states::OPAQUE1; } - - const uint32_t kExpandCollapseStates = states::COLLAPSED | states::EXPANDED; - if ((aState & kExpandCollapseStates) == kExpandCollapseStates) { - // Cannot be both expanded and collapsed -- this happens in ARIA expanded - // combobox because of limitation of ARIAMap. - // XXX: Perhaps we will be able to make this less hacky if we support - // extended states in ARIAMap, e.g. derive COLLAPSED from - // EXPANDABLE && !EXPANDED. - aState &= ~states::COLLAPSED; - } - - if (!(aState & states::UNAVAILABLE)) { - aState |= states::ENABLED | states::SENSITIVE; - } - - 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->IsDoc(); - ancestor = ancestor->Parent()) { - 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 3faaa3e0ba9e..da5ff5257a65 100644 --- a/accessible/generic/LocalAccessible.cpp +++ b/accessible/generic/LocalAccessible.cpp @@ -1602,14 +1602,26 @@ void LocalAccessible::ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize, } } -uint64_t LocalAccessible::ExplicitState() const { +uint64_t LocalAccessible::State() { if (IsDefunct()) return states::DEFUNCT; uint64_t state = NativeState(); // Apply ARIA states to be sure accessible states will be overridden. ApplyARIAState(&state); + const uint32_t kExpandCollapseStates = states::COLLAPSED | states::EXPANDED; + if ((state & kExpandCollapseStates) == kExpandCollapseStates) { + // Cannot be both expanded and collapsed -- this happens in ARIA expanded + // combobox because of limitation of ARIAMap. + // XXX: Perhaps we will be able to make this less hacky if we support + // extended states in ARIAMap, e.g. derive COLLAPSED from + // EXPANDABLE && !EXPANDED. + state &= ~states::COLLAPSED; + } + if (!(state & states::UNAVAILABLE)) { + state |= states::ENABLED | states::SENSITIVE; + // If the object is a current item of container widget then mark it as // ACTIVE. This allows screen reader virtual buffer modes to know which // descendant is the current one that would get focus if the user navigates @@ -1618,11 +1630,9 @@ uint64_t LocalAccessible::ExplicitState() const { if (widget && widget->CurrentItem() == this) state |= states::ACTIVE; } - return state; -} - -uint64_t LocalAccessible::State() { - uint64_t state = ExplicitState(); + if ((state & states::COLLAPSED) || (state & states::EXPANDED)) { + state |= states::EXPANDABLE; + } ApplyImplicitState(state); return state; @@ -1665,7 +1675,18 @@ void LocalAccessible::ApplyARIAState(uint64_t* aState) const { } } - if (!(*aState & states::FOCUSABLE)) { + 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 { // 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 @@ -3917,7 +3938,7 @@ already_AddRefed LocalAccessible::BundleFieldsForCache( if (IsInitialPush(CacheDomain::State)) { // Most states are updated using state change events, so we only send // these for the initial cache push. - uint64_t state = ExplicitState(); + uint64_t state = State(); // Exclude states which must be calculated by RemoteAccessible. state &= ~kRemoteCalculatedStates; fields->SetAttribute(CacheKey::State, state); diff --git a/accessible/generic/LocalAccessible.h b/accessible/generic/LocalAccessible.h index 488047ad9435..334da902b3a1 100644 --- a/accessible/generic/LocalAccessible.h +++ b/accessible/generic/LocalAccessible.h @@ -765,8 +765,6 @@ class LocalAccessible : public nsISupports, public Accessible { */ void NativeDescription(nsString& aDescription) const; - uint64_t ExplicitState() const; - /** * Return object attributes provided by native markup. It doesn't take into * account ARIA. diff --git a/accessible/ipc/RemoteAccessible.cpp b/accessible/ipc/RemoteAccessible.cpp index 0facb88dc618..9955e5a2cdbd 100644 --- a/accessible/ipc/RemoteAccessible.cpp +++ b/accessible/ipc/RemoteAccessible.cpp @@ -1580,6 +1580,13 @@ uint64_t RemoteAccessible::State() { mCachedFields->GetAttribute(CacheKey::State)) { VERIFY_CACHE(CacheDomain::State); state = *rawState; + // Handle states that are derived from other states. + if (!(state & states::UNAVAILABLE)) { + state |= states::ENABLED | states::SENSITIVE; + } + if (state & states::EXPANDABLE && !(state & states::EXPANDED)) { + state |= states::COLLAPSED; + } } ApplyImplicitState(state); diff --git a/accessible/tests/browser/e10s/browser_caching_states.js b/accessible/tests/browser/e10s/browser_caching_states.js index caa24ddda7fe..aa2654d77adf 100644 --- a/accessible/tests/browser/e10s/browser_caching_states.js +++ b/accessible/tests/browser/e10s/browser_caching_states.js @@ -778,7 +778,6 @@ addAccessibleTask(
ariaDisabled
-
`, async function testUnavailable(browser, docAcc) { const input = findAccessibleChildByID(docAcc, "input"); @@ -841,28 +840,6 @@ 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 } ); diff --git a/accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml b/accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml index c3d45471e9d7..9edac19af65d 100644 --- a/accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml +++ b/accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml @@ -15,8 +15,6 @@ src="../events.js" />