summaryrefslogtreecommitdiffstats
path: root/accessible/generic/LocalAccessible.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/generic/LocalAccessible.cpp')
-rw-r--r--accessible/generic/LocalAccessible.cpp70
1 files changed, 56 insertions, 14 deletions
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;
}