summaryrefslogtreecommitdiffstats
path: root/accessible/generic
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
commitfbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch)
tree4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /accessible/generic
parentReleasing progress-linux version 124.0.1-1~progress7.99u1. (diff)
downloadfirefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz
firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'accessible/generic')
-rw-r--r--accessible/generic/BaseAccessibles.cpp3
-rw-r--r--accessible/generic/DocAccessible.cpp176
-rw-r--r--accessible/generic/DocAccessible.h59
-rw-r--r--accessible/generic/LocalAccessible.cpp70
-rw-r--r--accessible/generic/LocalAccessible.h18
-rw-r--r--accessible/generic/moz.build4
6 files changed, 276 insertions, 54 deletions
diff --git a/accessible/generic/BaseAccessibles.cpp b/accessible/generic/BaseAccessibles.cpp
index 520f54e96b..eaaad02296 100644
--- a/accessible/generic/BaseAccessibles.cpp
+++ b/accessible/generic/BaseAccessibles.cpp
@@ -120,8 +120,7 @@ const LocalAccessible* LinkableAccessible::ActionWalk(bool* aIsLink,
}
KeyBinding LinkableAccessible::AccessKey() const {
- if (const LocalAccessible* actionAcc =
- const_cast<LinkableAccessible*>(this)->ActionWalk()) {
+ if (const LocalAccessible* actionAcc = ActionWalk()) {
return actionAcc->AccessKey();
}
diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp
index 28ed8bcbb4..0d84e8be9c 100644
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -67,6 +67,9 @@ static nsStaticAtom* const kRelationAttrs[] = {
static const uint32_t kRelationAttrsLen = ArrayLength(kRelationAttrs);
+static nsStaticAtom* const kSingleElementRelationIdlAttrs[] = {
+ nsGkAtoms::popovertarget};
+
////////////////////////////////////////////////////////////////////////////////
// Constructor/desctructor
@@ -383,25 +386,25 @@ void DocAccessible::QueueCacheUpdate(LocalAccessible* aAcc,
void DocAccessible::QueueCacheUpdateForDependentRelations(
LocalAccessible* aAcc) {
- if (!mIPCDoc || !aAcc || !aAcc->Elm() || !aAcc->IsInDocument() ||
- aAcc->IsDefunct()) {
+ if (!mIPCDoc || !aAcc || !aAcc->IsInDocument() || aAcc->IsDefunct()) {
return;
}
- nsAutoString ID;
- aAcc->DOMNodeID(ID);
- if (AttrRelProviders* list = GetRelProviders(aAcc->Elm(), ID)) {
- // We call this function when we've noticed an ID change, or when an acc
- // is getting bound to its document. We need to ensure any existing accs
- // that depend on this acc's ID have their rel cache entries updated.
- for (const auto& provider : *list) {
- LocalAccessible* relatedAcc = GetAccessible(provider->mContent);
- if (!relatedAcc || relatedAcc->IsDefunct() ||
- !relatedAcc->IsInDocument() ||
- mInsertedAccessibles.Contains(relatedAcc)) {
- continue;
- }
- QueueCacheUpdate(relatedAcc, CacheDomain::Relations);
+ dom::Element* el = aAcc->Elm();
+ if (!el) {
+ return;
+ }
+
+ // We call this function when we've noticed an ID change, or when an acc
+ // is getting bound to its document. We need to ensure any existing accs
+ // that depend on this acc's ID or Element have their relation cache entries
+ // updated.
+ RelatedAccIterator iter(this, el, nullptr);
+ while (LocalAccessible* relatedAcc = iter.Next()) {
+ if (relatedAcc->IsDefunct() || !relatedAcc->IsInDocument() ||
+ mInsertedAccessibles.Contains(relatedAcc)) {
+ continue;
}
+ QueueCacheUpdate(relatedAcc, CacheDomain::Relations);
}
}
@@ -507,6 +510,7 @@ void DocAccessible::Shutdown() {
}
mDependentIDsHashes.Clear();
+ mDependentElementsMap.Clear();
mNodeToAccessibleMap.Clear();
mAnchorJumpElm = nullptr;
@@ -729,9 +733,26 @@ std::pair<nsPoint, nsRect> DocAccessible::ComputeScrollData(
NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(DocAccessible)
NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(DocAccessible)
+// When a reflected element IDL attribute changes, we might get the following
+// synchronous calls:
+// 1. AttributeWillChange for the element.
+// 2. AttributeWillChange for the content attribute.
+// 3. AttributeChanged for the content attribute.
+// 4. AttributeChanged for the element.
+// Since the content attribute value is "" for any element, we won't always get
+// 2 or 3. Even if we do, they might occur after the element has already
+// changed, which means we can't detect any relevant state changes there; e.g.
+// mPrevStateBits. Thus, we need 1 and 4, and we must ignore 2 and 3. To
+// facilitate this, sIsAttrElementChanging will be set to true for 2 and 3.
+static bool sIsAttrElementChanging = false;
+
void DocAccessible::AttributeWillChange(dom::Element* aElement,
int32_t aNameSpaceID,
nsAtom* aAttribute, int32_t aModType) {
+ if (sIsAttrElementChanging) {
+ // See the comment above the definition of sIsAttrElementChanging.
+ return;
+ }
LocalAccessible* accessible = GetAccessible(aElement);
if (!accessible) {
if (aElement != mContent) return;
@@ -744,10 +765,11 @@ void DocAccessible::AttributeWillChange(dom::Element* aElement,
// elements.
if (aModType != dom::MutationEvent_Binding::ADDITION) {
RemoveDependentIDsFor(accessible, aAttribute);
+ RemoveDependentElementsFor(accessible, aAttribute);
}
if (aAttribute == nsGkAtoms::id) {
- if (accessible->IsActiveDescendant()) {
+ if (accessible->IsActiveDescendantId()) {
RefPtr<AccEvent> event =
new AccStateChangeEvent(accessible, states::ACTIVE, false);
FireDelayedEvent(event);
@@ -778,6 +800,10 @@ void DocAccessible::AttributeChanged(dom::Element* aElement,
int32_t aNameSpaceID, nsAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue) {
+ if (sIsAttrElementChanging) {
+ // See the comment above the definition of sIsAttrElementChanging.
+ return;
+ }
NS_ASSERTION(!IsDefunct(),
"Attribute changed called on defunct document accessible!");
@@ -851,6 +877,7 @@ void DocAccessible::AttributeChanged(dom::Element* aElement,
if (aModType == dom::MutationEvent_Binding::MODIFICATION ||
aModType == dom::MutationEvent_Binding::ADDITION) {
AddDependentIDsFor(accessible, aAttribute);
+ AddDependentElementsFor(accessible, aAttribute);
}
}
@@ -883,25 +910,23 @@ void DocAccessible::ARIAAttributeDefaultChanged(dom::Element* aElement,
void DocAccessible::ARIAActiveDescendantChanged(LocalAccessible* aAccessible) {
if (dom::Element* elm = aAccessible->Elm()) {
nsAutoString id;
- if (elm->GetAttr(nsGkAtoms::aria_activedescendant, id)) {
- dom::Element* activeDescendantElm = IDRefsIterator::GetElem(elm, id);
- if (activeDescendantElm) {
- LocalAccessible* activeDescendant = GetAccessible(activeDescendantElm);
- if (activeDescendant) {
- RefPtr<AccEvent> event =
- new AccStateChangeEvent(activeDescendant, states::ACTIVE, true);
- FireDelayedEvent(event);
- if (aAccessible->IsActiveWidget()) {
- FocusMgr()->ActiveItemChanged(activeDescendant, false);
+ if (dom::Element* activeDescendantElm =
+ nsCoreUtils::GetAriaActiveDescendantElement(elm)) {
+ LocalAccessible* activeDescendant = GetAccessible(activeDescendantElm);
+ if (activeDescendant) {
+ RefPtr<AccEvent> event =
+ new AccStateChangeEvent(activeDescendant, states::ACTIVE, true);
+ FireDelayedEvent(event);
+ if (aAccessible->IsActiveWidget()) {
+ FocusMgr()->ActiveItemChanged(activeDescendant, false);
#ifdef A11Y_LOG
- if (logging::IsEnabled(logging::eFocus)) {
- logging::ActiveItemChangeCausedBy("ARIA activedescedant changed",
- activeDescendant);
- }
-#endif
+ if (logging::IsEnabled(logging::eFocus)) {
+ logging::ActiveItemChangeCausedBy("ARIA activedescedant changed",
+ activeDescendant);
}
- return;
+#endif
}
+ return;
}
}
@@ -991,6 +1016,11 @@ void DocAccessible::ElementStateChanged(dom::Document* aDocument,
new AccStateChangeEvent(accessible, states::DEFAULT);
FireDelayedEvent(event);
}
+
+ if (aStateMask.HasState(dom::ElementState::INDETERMINATE)) {
+ RefPtr<AccEvent> event = new AccStateChangeEvent(accessible, states::MIXED);
+ FireDelayedEvent(event);
+ }
}
void DocAccessible::CharacterDataWillChange(nsIContent* aContent,
@@ -1150,6 +1180,7 @@ void DocAccessible::BindToDocument(LocalAccessible* aAccessible,
if (aAccessible->HasOwnContent()) {
AddDependentIDsFor(aAccessible);
+ AddDependentElementsFor(aAccessible);
nsIContent* content = aAccessible->GetContent();
if (content->IsElement() &&
@@ -1769,6 +1800,61 @@ void DocAccessible::RemoveDependentIDsFor(LocalAccessible* aRelProvider,
}
}
+void DocAccessible::AddDependentElementsFor(LocalAccessible* aRelProvider,
+ nsAtom* aRelAttr) {
+ dom::Element* providerEl = aRelProvider->Elm();
+ if (!providerEl) {
+ return;
+ }
+ for (nsStaticAtom* attr : kSingleElementRelationIdlAttrs) {
+ if (aRelAttr && aRelAttr != attr) {
+ continue;
+ }
+ if (dom::Element* targetEl =
+ providerEl->GetExplicitlySetAttrElement(attr)) {
+ AttrRelProviders& providers =
+ mDependentElementsMap.LookupOrInsert(targetEl);
+ AttrRelProvider* provider = new AttrRelProvider(attr, providerEl);
+ providers.AppendElement(provider);
+ }
+ // If the relation attribute was given, we've already handled it. We don't
+ // have anything else to check.
+ if (aRelAttr) {
+ break;
+ }
+ }
+}
+
+void DocAccessible::RemoveDependentElementsFor(LocalAccessible* aRelProvider,
+ nsAtom* aRelAttr) {
+ dom::Element* providerEl = aRelProvider->Elm();
+ if (!providerEl) {
+ return;
+ }
+ for (nsStaticAtom* attr : kSingleElementRelationIdlAttrs) {
+ if (aRelAttr && aRelAttr != attr) {
+ continue;
+ }
+ if (dom::Element* targetEl =
+ providerEl->GetExplicitlySetAttrElement(attr)) {
+ if (auto providers = mDependentElementsMap.Lookup(targetEl)) {
+ providers.Data().RemoveElementsBy([attr,
+ providerEl](const auto& provider) {
+ return provider->mRelAttr == attr && provider->mContent == providerEl;
+ });
+ if (providers.Data().IsEmpty()) {
+ providers.Remove();
+ }
+ }
+ }
+ // If the relation attribute was given, we've already handled it. We don't
+ // have anything else to check.
+ if (aRelAttr) {
+ break;
+ }
+ }
+}
+
bool DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
nsAtom* aAttribute) {
if (aAttribute == nsGkAtoms::role) {
@@ -2003,7 +2089,7 @@ bool InsertIterator::Next() {
return false;
}
-void DocAccessible::MaybeFireEventsForChangedPopover(LocalAccessible *aAcc) {
+void DocAccessible::MaybeFireEventsForChangedPopover(LocalAccessible* aAcc) {
dom::Element* el = aAcc->Elm();
if (!el || !el->IsHTMLElement() || !el->HasAttr(nsGkAtoms::popover)) {
return; // Not a popover.
@@ -2620,6 +2706,7 @@ void DocAccessible::UncacheChildrenInSubtree(LocalAccessible* aRoot) {
MaybeFireEventsForChangedPopover(aRoot);
aRoot->mStateFlags |= eIsNotInDocument;
RemoveDependentIDsFor(aRoot);
+ RemoveDependentElementsFor(aRoot);
// The parent of the removed subtree is about to be cleared, so we must do
// this here rather than in LocalAccessible::UnbindFromParent because we need
@@ -2757,7 +2844,7 @@ void DocAccessible::DispatchScrollingEvent(nsINode* aTarget,
void DocAccessible::ARIAActiveDescendantIDMaybeMoved(
LocalAccessible* aAccessible) {
LocalAccessible* widget = nullptr;
- if (aAccessible->IsActiveDescendant(&widget) && widget) {
+ if (aAccessible->IsActiveDescendantId(&widget) && widget) {
// The active descendant might have just been inserted and may not be in the
// tree yet. Therefore, schedule this async to ensure the tree is up to
// date.
@@ -2845,3 +2932,22 @@ void DocAccessible::MaybeHandleChangeToHiddenNameOrDescription(
}
}
}
+
+void DocAccessible::AttrElementWillChange(dom::Element* aElement,
+ nsAtom* aAttr) {
+ MOZ_ASSERT(!sIsAttrElementChanging);
+ AttributeWillChange(aElement, kNameSpaceID_None, aAttr,
+ dom::MutationEvent_Binding::MODIFICATION);
+ // We might get notified about a related content attribute change. Ignore
+ // it.
+ sIsAttrElementChanging = true;
+}
+
+void DocAccessible::AttrElementChanged(dom::Element* aElement, nsAtom* aAttr) {
+ MOZ_ASSERT(sIsAttrElementChanging);
+ // The element has changed and the content attribute change notifications
+ // (if any) have been sent.
+ sIsAttrElementChanging = false;
+ AttributeChanged(aElement, kNameSpaceID_None, aAttr,
+ dom::MutationEvent_Binding::MODIFICATION, nullptr);
+}
diff --git a/accessible/generic/DocAccessible.h b/accessible/generic/DocAccessible.h
index 52cbdd68cf..791d09661e 100644
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -121,7 +121,7 @@ class DocAccessible : public HyperTextAccessible,
void QueueCacheUpdate(LocalAccessible* aAcc, uint64_t aNewDomain);
/**
- * Walks the mDependentIDsHashes list for the given accessible and
+ * Walks the dependent ids and elements maps for the given accessible and
* queues a CacheDomain::Relations cache update fore each related acc.
* We call this when we observe an ID mutation or when an acc is bound
* to its document.
@@ -410,6 +410,9 @@ class DocAccessible : public HyperTextAccessible,
return mMovedAccessibles.Contains(aAcc);
}
+ void AttrElementWillChange(dom::Element* aElement, nsAtom* aAttr);
+ void AttrElementChanged(dom::Element* aElement, nsAtom* aAttr);
+
protected:
virtual ~DocAccessible();
@@ -486,6 +489,35 @@ class DocAccessible : public HyperTextAccessible,
nsAtom* aRelAttr = nullptr);
/**
+ * Add dependent elements targeted by a relation attribute on an accessible
+ * element to the dependent elements cache. This is used for reflected IDL
+ * attributes which return DOM elements and reflect a content attribute, where
+ * the IDL attribute has been set to an element. For example, if the
+ * .popoverTargetElement IDL attribute is set to an element using JS, the
+ * target element will be added to the dependent elements cache. If the
+ * relation attribute is not specified, then all relation attributes are
+ * checked.
+ *
+ * @param aRelProvider [in] the accessible with the relation IDL attribute.
+ * @param aRelAttr [in, optional] the name of the reflected content attribute.
+ * For example, for the popoverTargetElement IDL attribute, this would be
+ * "popovertarget".
+ */
+ void AddDependentElementsFor(LocalAccessible* aRelProvider,
+ nsAtom* aRelAttr = nullptr);
+
+ /**
+ * Remove dependent elements targeted by a relation attribute on an accessible
+ * element from the dependent elements cache. If the relation attribute is
+ * not specified, then all relation attributes are checked.
+ *
+ * @param aRelProvider [in] the accessible with the relation IDL attribute.
+ * @param aRelAttr [in, optional] the name of the reflected content attribute.
+ */
+ void RemoveDependentElementsFor(LocalAccessible* aRelProvider,
+ nsAtom* aRelAttr = nullptr);
+
+ /**
* Update or recreate an accessible depending on a changed attribute.
*
* @param aElement [in] the element the attribute was changed on
@@ -727,12 +759,35 @@ class DocAccessible : public HyperTextAccessible,
void RemoveRelProvidersIfEmpty(dom::Element* aElement, const nsAString& aID);
/**
- * The cache of IDs pointed by relation attributes.
+ * A map used to look up the target node for an implicit reverse relation
+ * where the target of the explicit relation is specified as an id.
+ * For example:
+ * <div id="label">Name:</div><input aria-labelledby="label">
+ * The div should get a LABEL_FOR relation targeting the input. To facilitate
+ * that, mDependentIDsHashes maps from "label" to an AttrRelProvider
+ * specifying aria-labelledby and the input. Because ids are scoped to the
+ * nearest ancestor document or shadow root, mDependentIDsHashes maps from the
+ * DocumentOrShadowRoot first.
*/
nsClassHashtable<nsPtrHashKey<dom::DocumentOrShadowRoot>,
DependentIDsHashtable>
mDependentIDsHashes;
+ /**
+ * A map used to look up the target element for an implicit reverse relation
+ * where the target of the explicit relation is also specified as an element.
+ * This is similar to mDependentIDsHashes, except that this is used when a
+ * DOM property is used to set the relation target element directly, rather
+ * than using an id. For example:
+ * <button>More info</button><div popover>Some info</div>
+ * The button's .popoverTargetElement property is set to the div so that the
+ * button invokes the popover.
+ * To facilitate finding the invoker given the popover, mDependentElementsMap
+ * maps from the div to an AttrRelProvider specifying popovertarget and the
+ * button.
+ */
+ nsTHashMap<nsIContent*, AttrRelProviders> mDependentElementsMap;
+
friend class RelatedAccIterator;
/**
diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp
index f02f68faa6..a758164e2b 100644
--- a/accessible/generic/LocalAccessible.cpp
+++ b/accessible/generic/LocalAccessible.cpp
@@ -127,6 +127,7 @@ ENameValueFlag LocalAccessible::Name(nsString& aName) const {
if (!aName.IsEmpty()) return eNameOK;
ENameValueFlag nameFlag = NativeName(aName);
+ nsCoreUtils::TrimNonBreakingSpaces(aName);
if (!aName.IsEmpty()) return nameFlag;
// In the end get the name from tooltip.
@@ -1840,10 +1841,40 @@ bool LocalAccessible::SetCurValue(double aValue) {
kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true));
}
+role LocalAccessible::FindNextValidARIARole(
+ std::initializer_list<nsStaticAtom*> aRolesToSkip) const {
+ const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+ if (roleMapEntry && mContent && mContent->IsElement()) {
+ dom::Element* elem = mContent->AsElement();
+ if (!nsAccUtils::ARIAAttrValueIs(elem, nsGkAtoms::role,
+ roleMapEntry->roleAtom, eIgnoreCase)) {
+ // Get the next valid token that isn't in the list of roles to skip.
+ uint8_t roleMapIndex =
+ aria::GetFirstValidRoleMapIndexExcluding(elem, aRolesToSkip);
+ // If we don't find a valid token, fall back to the native role.
+ if (roleMapIndex == aria::NO_ROLE_MAP_ENTRY_INDEX ||
+ roleMapIndex == aria::LANDMARK_ROLE_MAP_ENTRY_INDEX) {
+ return NativeRole();
+ }
+ const nsRoleMapEntry* fallbackRoleMapEntry =
+ aria::GetRoleMapFromIndex(roleMapIndex);
+ if (!fallbackRoleMapEntry) {
+ return NativeRole();
+ }
+ // Return the next valid role, but validate that first, too.
+ return ARIATransformRole(fallbackRoleMapEntry->role);
+ }
+ }
+ // Fall back to the native role.
+ return NativeRole();
+}
+
role LocalAccessible::ARIATransformRole(role aRole) const {
// Beginning with ARIA 1.1, user agents are expected to use the native host
- // language role of the element when the region role is used without a name.
- // https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-region
+ // language role of the element when the form or region roles are used without
+ // a name. Says the spec, "the user agent MUST treat such elements as if no
+ // role had been provided."
+ // https://w3c.github.io/aria/#document-handling_author-errors_roles
//
// XXX: While the name computation algorithm can be non-trivial in the general
// case, it should not be especially bad here: If the author hasn't used the
@@ -1851,10 +1882,18 @@ role LocalAccessible::ARIATransformRole(role aRole) const {
// calculation rule excludes name from content. That said, this use case is
// another example of why we should consider caching the accessible name. See:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1378235.
- if (aRole == roles::REGION) {
- nsAutoString name;
- Name(name);
- return name.IsEmpty() ? NativeRole() : aRole;
+ if (aRole == roles::REGION || aRole == roles::FORM) {
+ if (NameIsEmpty()) {
+ // If we have a "form" or "region" role, but no accessible name, we need
+ // to search for the next valid role. First, we search through the role
+ // attribute value string - there might be a valid fallback there. Skip
+ // all "form" or "region" attributes; we know they're not valid since
+ // there's no accessible name. If we find a valid role that's not "form"
+ // or "region", fall back to it (but run it through ARIATransformRole
+ // first). Otherwise, fall back to the element's native role.
+ return FindNextValidARIARole({nsGkAtoms::region, nsGkAtoms::form});
+ }
+ return aRole;
}
// XXX: these unfortunate exceptions don't fit into the ARIA table. This is
@@ -2510,6 +2549,10 @@ void LocalAccessible::Shutdown() {
// LocalAccessible protected
void LocalAccessible::ARIAName(nsString& aName) const {
+ // 'slot' elements should ignore aria-label and aria-labelledby.
+ if (mContent->IsHTMLElement(nsGkAtoms::slot)) {
+ return;
+ }
// aria-labelledby now takes precedence over aria-label
nsresult rv = nsTextEquivUtils::GetTextEquivFromIDRefs(
this, nsGkAtoms::aria_labelledby, aName);
@@ -3010,11 +3053,10 @@ LocalAccessible* LocalAccessible::CurrentItem() const {
// For activedescendant, the ARIA spec does not require that the user agent
// checks whether pointed node is actually a DOM descendant of the element
// with the aria-activedescendant attribute.
- nsAutoString id;
- if (HasOwnContent() && mContent->IsElement() &&
- mContent->AsElement()->GetAttr(nsGkAtoms::aria_activedescendant, id)) {
- dom::Element* activeDescendantElm = IDRefsIterator::GetElem(mContent, id);
- if (activeDescendantElm) {
+ if (HasOwnContent() && mContent->IsElement()) {
+ if (dom::Element* activeDescendantElm =
+ nsCoreUtils::GetAriaActiveDescendantElement(
+ mContent->AsElement())) {
if (mContent->IsInclusiveDescendantOf(activeDescendantElm)) {
// Don't want a cyclical descendant relationship. That would be bad.
return nullptr;
@@ -3043,8 +3085,8 @@ LocalAccessible* LocalAccessible::ContainerWidget() const {
parent = parent->LocalParent()) {
nsIContent* parentContent = parent->GetContent();
if (parentContent && parentContent->IsElement() &&
- parentContent->AsElement()->HasAttr(
- nsGkAtoms::aria_activedescendant)) {
+ nsCoreUtils::GetAriaActiveDescendantElement(
+ parentContent->AsElement())) {
return parent;
}
@@ -3055,7 +3097,7 @@ LocalAccessible* LocalAccessible::ContainerWidget() const {
return nullptr;
}
-bool LocalAccessible::IsActiveDescendant(LocalAccessible** aWidget) const {
+bool LocalAccessible::IsActiveDescendantId(LocalAccessible** aWidget) const {
if (!HasOwnContent() || !mContent->HasID()) {
return false;
}
diff --git a/accessible/generic/LocalAccessible.h b/accessible/generic/LocalAccessible.h
index a3620f4cbd..51c4cc9424 100644
--- a/accessible/generic/LocalAccessible.h
+++ b/accessible/generic/LocalAccessible.h
@@ -587,7 +587,12 @@ class LocalAccessible : public nsISupports, public Accessible {
*/
virtual LocalAccessible* ContainerWidget() const;
- bool IsActiveDescendant(LocalAccessible** aWidget = nullptr) const;
+ /**
+ * Accessible's element ID is referenced as a aria-activedescendant in the
+ * document. This method is only used for ID changes and therefore does not
+ * need to work for direct element references via ariaActiveDescendantElement.
+ */
+ bool IsActiveDescendantId(LocalAccessible** aWidget = nullptr) const;
/**
* Return true if the accessible is defunct.
@@ -1010,6 +1015,17 @@ class LocalAccessible : public nsISupports, public Accessible {
*/
nsIFrame* FindNearestAccessibleAncestorFrame();
+ /*
+ * This function assumes that the current role is not valid. It searches for a
+ * fallback role in the role attribute string, and returns it. If there is no
+ * valid fallback role in the role attribute string, the function returns the
+ * native role. The aRolesToSkip parameter will cause the function to skip any
+ * roles found in the role attribute string when searching for the next valid
+ * role.
+ */
+ role FindNextValidARIARole(
+ std::initializer_list<nsStaticAtom*> aRolesToSkip) const;
+
LocalAccessible* GetPopoverTargetDetailsRelation() const;
};
diff --git a/accessible/generic/moz.build b/accessible/generic/moz.build
index 06e229b999..6d34fe7d68 100644
--- a/accessible/generic/moz.build
+++ b/accessible/generic/moz.build
@@ -53,6 +53,10 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
LOCAL_INCLUDES += [
"/accessible/android",
]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "uikit":
+ LOCAL_INCLUDES += [
+ "/accessible/ios",
+ ]
else:
LOCAL_INCLUDES += [
"/accessible/other",