Bug 1104947 - Complete the accessibility mapping for aria-current, r=Jamie
authorMarco Zehe <mzehe@mozilla.com>
Fri, 02 Nov 2018 06:40:47 +0000
changeset 444059 a6493a0b53a3d8df5c23b1300591f1f7ebc573e4
parent 444058 c498718cc8243592d236351788c00e9f1d39d9f0
child 444060 8fa378e960b14d3c2b21d35a116aa04ec54c006f
child 444138 2a050ad70afbdd04f291893511c4653b46b8671f
push id109505
push useraciure@mozilla.com
push dateFri, 02 Nov 2018 09:46:08 +0000
treeherdermozilla-inbound@8fa378e960b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersJamie
bugs1104947, 1365904
milestone65.0a1
first release with
nightly linux32
a6493a0b53a3 / 65.0a1 / 20181102100039 / files
nightly linux64
a6493a0b53a3 / 65.0a1 / 20181102100039 / files
nightly mac
a6493a0b53a3 / 65.0a1 / 20181102100039 / files
nightly win32
a6493a0b53a3 / 65.0a1 / 20181102100039 / files
nightly win64
a6493a0b53a3 / 65.0a1 / 20181102100039 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1104947 - Complete the accessibility mapping for aria-current, r=Jamie We already mapped aria-current to the correct attribute, but we did so unconditionally unless the value was false or undefined. However, if the value was an invalid token, it would not be mapped to "true" as per spec. Now, any bogus value for aria-current will be mapped as if the author had specified "true". In addition, this patch adds aria-current to the known set of attributes with rules, which causes NSAccessibilityService::MustBeAccessible to make sure an accessible gets created if aria-current is set, even if that accessible is a html:span element. This fixes bug 1365904. Differential Revision: https://phabricator.services.mozilla.com/D10331
accessible/base/ARIAMap.cpp
accessible/base/nsAccUtils.cpp
accessible/base/nsAccUtils.h
accessible/tests/mochitest/attributes/test_obj.html
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -1297,16 +1297,17 @@ struct AttrCharacteristics
 
 static const AttrCharacteristics gWAIUnivAttrMap[] = {
   // clang-format off
   {nsGkAtoms::aria_activedescendant,  ATTR_BYPASSOBJ                               },
   {nsGkAtoms::aria_atomic,   ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
   {nsGkAtoms::aria_busy,                               ATTR_VALTOKEN | ATTR_GLOBAL },
   {nsGkAtoms::aria_checked,           ATTR_BYPASSOBJ | ATTR_VALTOKEN               }, /* exposes checkable obj attr */
   {nsGkAtoms::aria_controls,          ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
+  {nsGkAtoms::aria_current,  ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
   {nsGkAtoms::aria_describedby,       ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {nsGkAtoms::aria_details,           ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {nsGkAtoms::aria_disabled,          ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
   {nsGkAtoms::aria_dropeffect,                         ATTR_VALTOKEN | ATTR_GLOBAL },
   {nsGkAtoms::aria_errormessage,      ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {nsGkAtoms::aria_expanded,          ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
   {nsGkAtoms::aria_flowto,            ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {nsGkAtoms::aria_grabbed,                            ATTR_VALTOKEN | ATTR_GLOBAL },
@@ -1469,16 +1470,25 @@ AttrIterator::Next(nsAString& aAttrName,
           mElement->AttrValueIs(kNameSpaceID_None, attrAtom,
                                 nsGkAtoms::_false, eCaseMatters)) {
         continue; // only expose token based attribute if value is not 'false'.
       }
 
       nsAutoString value;
       if (mElement->GetAttr(kNameSpaceID_None, attrAtom, value)) {
         aAttrName.Assign(Substring(attrStr, 5));
+        if (attrFlags & ATTR_VALTOKEN) {
+          nsAtom* normalizedValue =
+            nsAccUtils::NormalizeARIAToken(mElement, attrAtom);
+          if (normalizedValue) {
+            nsDependentAtomString normalizedValueStr(normalizedValue);
+            aAttrValue.Assign(normalizedValueStr);
+            return true;
+          }
+        }
         aAttrValue.Assign(value);
         return true;
       }
     }
   }
 
   return false;
 }
--- a/accessible/base/nsAccUtils.cpp
+++ b/accessible/base/nsAccUtils.cpp
@@ -220,16 +220,36 @@ nsAccUtils::GetARIAToken(dom::Element* a
   int32_t idx = aElement->FindAttrValueIn(kNameSpaceID_None,
                                           aAttr, tokens, eCaseMatters);
   if (idx >= 0)
     return tokens[idx];
 
   return nullptr;
 }
 
+nsStaticAtom*
+nsAccUtils::NormalizeARIAToken(dom::Element* aElement, nsAtom* aAttr)
+{
+  if (!HasDefinedARIAToken(aElement, aAttr)) {
+    return nsGkAtoms::_empty;
+  }
+
+  if (aAttr== nsGkAtoms::aria_current) {
+    static Element::AttrValuesArray tokens[] =
+      { nsGkAtoms::page, nsGkAtoms::step, nsGkAtoms::location_,
+        nsGkAtoms::date, nsGkAtoms::time, nsGkAtoms::_true, nullptr};
+    int32_t idx = aElement->FindAttrValueIn(kNameSpaceID_None,
+                                    aAttr, tokens, eCaseMatters);
+    // If the token is present, return it, otherwise TRUE as per spec.
+    return (idx >= 0) ? tokens[idx] : nsGkAtoms::_true;
+  }
+
+  return nullptr;
+}
+
 Accessible*
 nsAccUtils::GetSelectableContainer(Accessible* aAccessible, uint64_t aState)
 {
   if (!aAccessible)
     return nullptr;
 
   if (!(aState & states::SELECTABLE))
     return nullptr;
--- a/accessible/base/nsAccUtils.h
+++ b/accessible/base/nsAccUtils.h
@@ -100,16 +100,24 @@ public:
 
   /**
    * Return atomic value of ARIA attribute of boolean or NMTOKEN type.
    */
   static nsStaticAtom* GetARIAToken(mozilla::dom::Element* aElement,
                                     nsAtom* aAttr);
 
   /**
+   * If the given ARIA attribute has a specific known token value, return it.
+   * If the specification demands for a fallback value for unknown attribute 
+   * values, return that. For all others, return a nullptr.
+   */
+  static nsStaticAtom* NormalizeARIAToken(mozilla::dom::Element* aElement,
+                                          nsAtom* aAttr);
+
+  /**
    * Return document accessible for the given DOM node.
    */
   static DocAccessible* GetDocAccessibleFor(nsINode* aNode)
   {
     nsIPresShell *presShell = nsCoreUtils::GetPresShellFor(aNode);
     return GetAccService()->GetDocAccessible(presShell);
   }
 
--- a/accessible/tests/mochitest/attributes/test_obj.html
+++ b/accessible/tests/mochitest/attributes/test_obj.html
@@ -46,16 +46,24 @@ https://bugzilla.mozilla.org/show_bug.cg
       testAttrs("haspopupTree", { "haspopup": "tree" }, true);
       testAbsentAttrs("modal", {"modal": "true"});
       testAttrs("sortAscending", {"sort": "ascending"}, true);
       testAttrs("sortDescending", {"sort": "descending"}, true);
       testAttrs("sortNone", {"sort": "none"}, true);
       testAttrs("sortOther", {"sort": "other"}, true);
       testAttrs("roledescr", {"roledescription": "spreadshit"}, true);
       testAttrs("currentPage", {"current": "page"}, true);
+      testAttrs("currentStep", {"current": "step"}, true);
+      testAttrs("currentLocation", {"current": "location"}, true);
+      testAttrs("currentDate", {"current": "date"}, true);
+      testAttrs("currentTime", {"current": "time"}, true);
+      testAttrs("currentTrue", {"current": "true"}, true);
+      testAttrs("currentOther", {"current": "true"}, true);
+      testAbsentAttrs("currentFalse", {"current": "true"});
+      testAttrs("currentSpan", {"current": "page"}, true);
 
       // inherited attributes by subdocuments
       var subdoc = getAccessible("iframe").firstChild;
       testAttrs(subdoc, {"busy": "true"}, true);
 
       // live object attribute
 
       // HTML
@@ -182,16 +190,30 @@ https://bugzilla.mozilla.org/show_bug.cg
   <div id="haspopupTree" aria-haspopup="tree"></div>
   <div id="modal" aria-modal="true"></div>
   <div id="sortAscending" role="columnheader" aria-sort="ascending"></div>
   <div id="sortDescending" role="columnheader" aria-sort="descending"></div>
   <div id="sortNone" role="columnheader" aria-sort="none"></div>
   <div id="sortOther" role="columnheader" aria-sort="other"></div>
   <div id="roledescr" aria-roledescription="spreadshit"></div>
   <div id="currentPage" aria-current="page"></div>
+  <div id="currentStep" aria-current="step"></div>
+  <div id="currentLocation" aria-current="location"></div>
+  <div id="currentDate" aria-current="date"></div>
+  <div id="currentTime" aria-current="time"></div>
+  <div id="currentTrue" aria-current="true"></div>
+  <div id="currentOther" aria-current="other"></div>
+  <div id="currentFalse" aria-current="false"></div>
+
+  <!-- aria-current on a span which must create an accessible -->
+  <ol>
+    <li><a href="...">Page 1</a></li>
+    <li><a href="...">Page 2</a></li>
+    <li><span id="currentSpan" aria-current="page">This page</span></li>
+  </ol>
 
   <!-- inherited from iframe -->
   <iframe id="iframe" src="data:text/html,<html><body></body></html>"
           aria-busy="true"></iframe>
 
   <!-- html -->
   <output id="output"></output>