diff --git a/accessible/base/ARIAMap.cpp b/accessible/base/ARIAMap.cpp index aad9126f564..b4d90ca5ca2 100644 --- a/accessible/base/ARIAMap.cpp +++ b/accessible/base/ARIAMap.cpp @@ -711,7 +711,7 @@ static const AttrCharacteristics gWAIUnivAttrMap[] = { {&nsGkAtoms::aria_flowto, ATTR_BYPASSOBJ | ATTR_GLOBAL }, {&nsGkAtoms::aria_grabbed, ATTR_VALTOKEN | ATTR_GLOBAL }, {&nsGkAtoms::aria_haspopup, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, - {&nsGkAtoms::aria_hidden, ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL }, + {&nsGkAtoms::aria_hidden, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, /* handled special way */ {&nsGkAtoms::aria_invalid, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, {&nsGkAtoms::aria_label, ATTR_BYPASSOBJ | ATTR_GLOBAL }, {&nsGkAtoms::aria_labelledby, ATTR_BYPASSOBJ | ATTR_GLOBAL }, @@ -798,6 +798,15 @@ aria::AttrCharacteristicsFor(nsIAtom* aAtom) return 0; } +bool +aria::HasDefinedARIAHidden(nsIContent* aContent) +{ + return aContent && + nsAccUtils::HasDefinedARIAToken(aContent, nsGkAtoms::aria_hidden) && + !aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden, + nsGkAtoms::_false, eCaseMatters); +} + //////////////////////////////////////////////////////////////////////////////// // AttrIterator class diff --git a/accessible/base/ARIAMap.h b/accessible/base/ARIAMap.h index b629b51c22e..bfb9d11c3fe 100644 --- a/accessible/base/ARIAMap.h +++ b/accessible/base/ARIAMap.h @@ -227,6 +227,11 @@ uint64_t UniversalStatesFor(mozilla::dom::Element* aElement); */ uint8_t AttrCharacteristicsFor(nsIAtom* aAtom); +/** + * Return true if the element has defined aria-hidden. + */ +bool HasDefinedARIAHidden(nsIContent* aContent); + /** * Represents a simple enumerator for iterating through ARIA attributes * exposed as object attributes on a given accessible. diff --git a/accessible/base/nsAccessiblePivot.cpp b/accessible/base/nsAccessiblePivot.cpp index 371801e5653..3d33f467e82 100644 --- a/accessible/base/nsAccessiblePivot.cpp +++ b/accessible/base/nsAccessiblePivot.cpp @@ -892,11 +892,7 @@ RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult) return NS_OK; if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN & mPreFilter) { - nsIContent* content = aAccessible->GetContent(); - if (content && - nsAccUtils::HasDefinedARIAToken(content, nsGkAtoms::aria_hidden) && - !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden, - nsGkAtoms::_false, eCaseMatters)) { + if (aAccessible->IsARIAHidden()) { *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; return NS_OK; } diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index b5c96a0eb55..2ad00f7a5a8 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -873,6 +873,11 @@ Accessible::Attributes() while(attribIter.Next(name, value)) attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused); + if (IsARIAHidden()) { + nsAccUtils::SetAccAttr(attributes, nsGkAtoms::hidden, + NS_LITERAL_STRING("true")); + } + // If there is no aria-live attribute then expose default value of 'live' // object attribute used for ARIA role of this accessible. if (mRoleMapEntry) { @@ -1920,6 +1925,9 @@ Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent) mContextFlags |= eHasNameDependentParent; else mContextFlags &= ~eHasNameDependentParent; + + if (mParent->IsARIAHidden() || aria::HasDefinedARIAHidden(mContent)) + SetARIAHidden(true); } // Accessible protected @@ -2377,6 +2385,20 @@ Accessible::ContainerWidget() const return nullptr; } +void +Accessible::SetARIAHidden(bool aIsDefined) +{ + if (aIsDefined) + mContextFlags |= eARIAHidden; + else + mContextFlags &= ~eARIAHidden; + + uint32_t length = mChildren.Length(); + for (uint32_t i = 0; i < length; i++) { + mChildren[i]->SetARIAHidden(aIsDefined); + } +} + //////////////////////////////////////////////////////////////////////////////// // Accessible protected methods diff --git a/accessible/generic/Accessible.h b/accessible/generic/Accessible.h index bc33a1ac93e..540859cd967 100644 --- a/accessible/generic/Accessible.h +++ b/accessible/generic/Accessible.h @@ -889,6 +889,13 @@ public: bool HasNameDependentParent() const { return mContextFlags & eHasNameDependentParent; } + /** + * Return true if aria-hidden="true" is applied to the accessible or inherited + * from the parent. + */ + bool IsARIAHidden() const { return mContextFlags & eARIAHidden; } + void SetARIAHidden(bool aIsDefined); + protected: virtual ~Accessible(); @@ -975,8 +982,9 @@ protected: */ enum ContextFlags { eHasNameDependentParent = 1 << 0, // Parent's name depends on this accessible. + eARIAHidden = 1 << 1, - eLastContextFlag = eHasNameDependentParent + eLastContextFlag = eARIAHidden }; protected: @@ -1082,7 +1090,7 @@ protected: static const uint8_t kChildrenFlagsBits = 2; static const uint8_t kStateFlagsBits = 9; - static const uint8_t kContextFlagsBits = 1; + static const uint8_t kContextFlagsBits = 2; static const uint8_t kTypeBits = 6; static const uint8_t kGenericTypesBits = 13; diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 7c72a7390ec..862e5b452c2 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -955,6 +955,22 @@ DocAccessible::ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute nsIContent* elm = aAccessible->GetContent(); + // Update aria-hidden flag for the whole subtree iff aria-hidden is changed + // on the root, i.e. ignore any affiliated aria-hidden changes in the subtree + // of top aria-hidden. + if (aAttribute == nsGkAtoms::aria_hidden) { + bool isDefined = aria::HasDefinedARIAHidden(elm); + if (isDefined != aAccessible->IsARIAHidden() && + !aAccessible->Parent()->IsARIAHidden()) { + aAccessible->SetARIAHidden(isDefined); + + nsRefPtr event = + new AccObjectAttrChangedEvent(aAccessible, aAttribute); + FireDelayedEvent(event); + } + return; + } + if (aAttribute == nsGkAtoms::aria_checked || (aAccessible->IsButton() && aAttribute == nsGkAtoms::aria_pressed)) { diff --git a/accessible/tests/mochitest/events/test_aria_objattr.html b/accessible/tests/mochitest/events/test_aria_objattr.html index 1410a53c0e6..5f16ba7948e 100644 --- a/accessible/tests/mochitest/events/test_aria_objattr.html +++ b/accessible/tests/mochitest/events/test_aria_objattr.html @@ -11,6 +11,8 @@ + @@ -40,6 +42,23 @@ }; } + function updateARIAHidden(aID, aIsDefined, aChildId) + { + this.__proto__ = new updateAttribute(aID, "aria-hidden", + aIsDefined ? "true" : "false"); + + this.finalCheck = function updateARIAHidden() + { + if (aIsDefined) { + testAttrs(aID, {"hidden" : "true"}, true); + testAttrs(aChildId, {"hidden" : "true"}, true); + } else { + testAbsentAttrs(aID, { "hidden": "true"}); + testAbsentAttrs(aChildId, { "hidden": "true"}); + } + } + } + // Debug stuff. // gA11yEventDumpID = "eventdump"; //gA11yEventDumpToConsole = true; @@ -48,7 +67,8 @@ { gQueue = new eventQueue(); - gQueue.push(new updateAttribute("hideable", "aria-hidden", "true")); + gQueue.push(new updateARIAHidden("hideable", true, "hideable_child")); + gQueue.push(new updateARIAHidden("hideable", false, "hideable_child")); gQueue.push(new updateAttribute("sortable", "aria-sort", "ascending")); @@ -89,7 +109,7 @@
-
Hi
there
+
Hi
there
aria-sort