Bug 1355921 - Elements with a defined, non-false value for aria-current should expose ATK_STATE_ACTIVE. r=marcoz
authorJoanmarie Diggs <jdiggs@igalia.com>
Mon, 03 Jul 2017 17:02:00 +0200
changeset 367423 4bc42c671bd263a384d93d3f7472d52faca99a1b
parent 367364 5e736fdcac99fe1c6df3e155719b42c207f05488
child 367424 976ff4ac8eaddca28e3f2b8a7629b180316eebaf
push id32135
push userkwierso@gmail.com
push dateThu, 06 Jul 2017 00:12:17 +0000
treeherdermozilla-central@af0466865a21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarcoz
bugs1355921
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1355921 - Elements with a defined, non-false value for aria-current should expose ATK_STATE_ACTIVE. r=marcoz Create states::CURRENT and add it to the list of "universal" (global) ARIA states. Map states::CURRENT to ATK_STATE_ACTIVE and emit accessible state-change notifications when the value of aria-current is modified.
accessible/atk/nsStateMap.h
accessible/base/ARIAMap.cpp
accessible/base/ARIAStateMap.cpp
accessible/base/ARIAStateMap.h
accessible/base/States.h
accessible/generic/DocAccessible.cpp
accessible/interfaces/nsIAccessibleStates.idl
accessible/tests/mochitest/events/test_aria_statechange.html
accessible/tests/mochitest/states.js
accessible/tests/mochitest/states/test_aria.html
dom/base/nsGkAtomList.h
--- a/accessible/atk/nsStateMap.h
+++ b/accessible/atk/nsStateMap.h
@@ -105,10 +105,11 @@ static const AtkStateMap gAtkStateMap[] 
   { ATK_STATE_SINGLE_LINE,                    kMapDirectly },   // states::SINGLE_LINE             = 1 << 40
   { ATK_STATE_TRANSIENT,                      kMapDirectly },   // states::TRANSIENT               = 1 << 41
   { ATK_STATE_VERTICAL,                       kMapDirectly },   // states::VERTICAL                = 1 << 42
   { ATK_STATE_STALE,                          kMapDirectly },   // states::STALE                   = 1 << 43
   { ATK_STATE_ENABLED,                        kMapDirectly },   // states::ENABLED                 = 1 << 44
   { ATK_STATE_SENSITIVE,                      kMapDirectly },   // states::SENSITIVE               = 1 << 45
   { ATK_STATE_EXPANDABLE,                     kMapDirectly },   // states::EXPANDABLE              = 1 << 46
   { kNone,                                    kMapDirectly },   // states::PINNED                  = 1 << 47
-  { kNone,                                    kNoSuchState },   //                                 = 1 << 48
+  { ATK_STATE_ACTIVE,                         kMapDirectly },   // states::CURRENT                 = 1 << 48
+  { kNone,                                    kNoSuchState },   //                                 = 1 << 49
 };
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -1197,16 +1197,17 @@ nsRoleMapEntry aria::gEmptyRoleMap = {
 
 /**
  * Universal (Global) states:
  * The following state rules are applied to any accessible element,
  * whether there is an ARIA role or not:
  */
 static const EStateRule sWAIUnivStateMap[] = {
   eARIABusy,
+  eARIACurrent,
   eARIADisabled,
   eARIAExpanded,  // Currently under spec review but precedent exists
   eARIAHasPopup,  // Note this is technically a "property"
   eARIAInvalid,
   eARIAModal,
   eARIARequired,  // XXX not global, Bug 553117
   eARIANone
 };
--- a/accessible/base/ARIAStateMap.cpp
+++ b/accessible/base/ARIAStateMap.cpp
@@ -140,16 +140,26 @@ aria::MapToState(EStateRule aRule, dom::
       static const TokenTypeData data(
         nsGkAtoms::aria_checked, eMixedType,
         states::CHECKABLE, states::CHECKED);
 
       MapTokenType(aElement, aState, data);
       return true;
     }
 
+    case eARIACurrent:
+    {
+      static const TokenTypeData data(
+        nsGkAtoms::aria_current, eBoolType,
+        0, states::CURRENT);
+
+      MapTokenType(aElement, aState, data);
+      return true;
+    }
+
     case eARIADisabled:
     {
       static const TokenTypeData data(
         nsGkAtoms::aria_disabled, eBoolType,
         0, states::UNAVAILABLE);
 
       MapTokenType(aElement, aState, data);
       return true;
--- a/accessible/base/ARIAStateMap.h
+++ b/accessible/base/ARIAStateMap.h
@@ -24,16 +24,17 @@ namespace aria {
 enum EStateRule
 {
   eARIANone,
   eARIAAutoComplete,
   eARIABusy,
   eARIACheckableBool,
   eARIACheckableMixed,
   eARIACheckedMixed,
+  eARIACurrent,
   eARIADisabled,
   eARIAExpanded,
   eARIAHasPopup,
   eARIAInvalid,
   eARIAModal,
   eARIAMultiline,
   eARIAMultiSelectable,
   eARIAOrientation,
--- a/accessible/base/States.h
+++ b/accessible/base/States.h
@@ -272,14 +272,20 @@ namespace states {
    * @see EXPANDED and COLLAPSED states.
    */
   const uint64_t EXPANDABLE = ((uint64_t) 0x1) << 46;
 
   /**
    * The object is pinned, usually indicating it is fixed in place and has permanence.
    */
   const uint64_t PINNED = ((uint64_t) 0x1) << 47;
+
+  /**
+   * The object is the current item within a container or set of related elements.
+   */
+  const uint64_t CURRENT = ((uint64_t) 0x1) << 48;
+
 } // namespace states
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 	
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1049,16 +1049,23 @@ DocAccessible::ARIAAttributeChanged(Acce
   if (aAttribute == nsGkAtoms::aria_valuenow &&
       (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
        elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
                         nsGkAtoms::_empty, eCaseMatters))) {
     FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
     return;
   }
 
+  if (aAttribute == nsGkAtoms::aria_current) {
+    RefPtr<AccEvent> event =
+      new AccStateChangeEvent(aAccessible, states::CURRENT);
+    FireDelayedEvent(event);
+    return;
+  }
+
   if (aAttribute == nsGkAtoms::aria_owns) {
     mNotificationController->ScheduleRelocation(aAccessible);
   }
 }
 
 void
 DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible)
 {
--- a/accessible/interfaces/nsIAccessibleStates.idl
+++ b/accessible/interfaces/nsIAccessibleStates.idl
@@ -67,10 +67,11 @@ interface nsIAccessibleStates : nsISuppo
   const unsigned long  EXT_STATE_SINGLE_LINE             = 0x00000200;  // This text object can only contain 1 line of text    
   const unsigned long  EXT_STATE_TRANSIENT               = 0x00000400;  // 
   const unsigned long  EXT_STATE_VERTICAL                = 0x00000800;  // Especially used for sliders and scrollbars  
   const unsigned long  EXT_STATE_STALE                   = 0x00001000;  // Object not dead, but not up-to-date either
   const unsigned long  EXT_STATE_ENABLED                 = 0x00002000;  // A widget that is not unavailable
   const unsigned long  EXT_STATE_SENSITIVE               = 0x00004000;  // Same as ENABLED for now
   const unsigned long  EXT_STATE_EXPANDABLE              = 0x00008000;  // If COLLAPSED or EXPANDED
   const unsigned long  EXT_STATE_PINNED                  = 0x00010000;  // Indicates object is pinned.
+  const unsigned long  EXT_STATE_CURRENT                 = 0x00020000;  // Indicates object is the current item in its container
 };
 
--- a/accessible/tests/mochitest/events/test_aria_statechange.html
+++ b/accessible/tests/mochitest/events/test_aria_statechange.html
@@ -63,16 +63,35 @@
       };
 
       this.getID = function busyify_getID()
       {
         return prettyName(aID) + " aria-busy changed to '" + aIsBusy + "'";
       };
     }
 
+    function makeCurrent(aID, aIsCurrent, aValue)
+    {
+      this.DOMNode = getNode(aID);
+
+      this.eventSeq = [
+        new stateChangeChecker(EXT_STATE_CURRENT, true, aIsCurrent, this.DOMNode)
+      ];
+
+      this.invoke = function makeCurrent_invoke()
+      {
+        this.DOMNode.setAttribute("aria-current", aValue);
+      };
+
+      this.getID = function makeCurrent_getID()
+      {
+        return prettyName(aID) + " aria-current changed to " + aValue;
+      };
+    }
+
     function setAttrOfMixedType(aID, aAttr, aState, aValue)
     {
       this.DOMNode = getNode(aID);
 
       this.eventSeq = [
         new stateChangeChecker(aState, kOrdinalState,
                                aValue == "true", this.DOMNode)
       ];
@@ -143,16 +162,22 @@
       gQueue.push(new busyify("aria_doc", true));
       gQueue.push(new busyify("aria_doc", false));
 
       buildQueueForAttrOfMixedType(gQueue, "pressable", setPressed);
       buildQueueForAttrOfMixedType(gQueue, "pressable_native", setPressed);
       buildQueueForAttrOfMixedType(gQueue, "checkable", setChecked);
       buildQueueForAttrOfBoolType(gQueue, "checkableBool", setChecked);
 
+      gQueue.push(new makeCurrent("current_page_1", false, "false"));
+      gQueue.push(new makeCurrent("current_page_2", true, "page"));
+      gQueue.push(new makeCurrent("current_page_2", false, "false"));
+      gQueue.push(new makeCurrent("current_page_3", true, "true"));
+      gQueue.push(new makeCurrent("current_page_3", false, ""));
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
@@ -178,16 +203,21 @@
      title="Pressed state is not exposed on a button element with aria-pressed attribute">
     Mozilla Bug 989958
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
      title="Support ARIA 1.1 switch role">
     Mozilla Bug 1136563
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1355921"
+     title="Elements with a defined, non-false value for aria-current should expose ATK_STATE_ACTIVE">
+    Mozilla Bug 1355921
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
   <div id="eventdump"></div>
 
   <!-- aria-expanded -->
@@ -199,10 +229,15 @@
 
   <!-- aria-pressed -->
   <div id="pressable" role="button"></div>
   <button id="pressable_native"></button>
 
   <!-- aria-checked -->
   <div id="checkable" role="checkbox"></div>
   <div id="checkableBool" role="switch"></div>
+
+  <!-- aria-current -->
+  <div id="current_page_1" role="link" aria-current="page">1</div>
+  <div id="current_page_2" role="link" aria-current="false">2</div>
+  <div id="current_page_3" role="link">3</div>
 </body>
 </html>
--- a/accessible/tests/mochitest/states.js
+++ b/accessible/tests/mochitest/states.js
@@ -32,16 +32,17 @@ const STATE_PROTECTED = nsIAccessibleSta
 const STATE_READONLY = nsIAccessibleStates.STATE_READONLY;
 const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED;
 const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE;
 const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED;
 const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED;
 const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE;
 
 const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE;
+const EXT_STATE_CURRENT = nsIAccessibleStates.EXT_STATE_CURRENT;
 const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT;
 const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE;
 const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED;
 const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE;
 const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL;
 const EXT_STATE_MODAL = nsIAccessibleStates.EXT_STATE_MODAL;
 const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE;
 const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED;
--- a/accessible/tests/mochitest/states/test_aria.html
+++ b/accessible/tests/mochitest/states/test_aria.html
@@ -261,16 +261,23 @@
       testStates("aria_vtreegrid", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
 
       // indeterminate ARIA progressbars (no aria-valuenow or aria-valuetext attribute)
       // should expose mixed state
       testStates("aria_progressbar", STATE_MIXED);
       testStates("aria_progressbar_valuenow", 0, 0, STATE_MIXED);
       testStates("aria_progressbar_valuetext", 0, 0, STATE_MIXED);
 
+      // aria-current
+      testStates("current_page_1", 0, EXT_STATE_CURRENT);
+      testStates("page_2", 0, 0, EXT_STATE_CURRENT);
+      testStates("page_3", 0, 0, EXT_STATE_CURRENT);
+      testStates("page_4", 0, 0, EXT_STATE_CURRENT);
+      testStates("current_foo", 0, EXT_STATE_CURRENT);
+
       testStates("aria_listbox", STATE_FOCUSABLE);
       testStates("aria_grid", STATE_FOCUSABLE);
       testStates("aria_tree", STATE_FOCUSABLE);
       testStates("aria_treegrid", STATE_FOCUSABLE);
       testStates("aria_listbox_disabled", 0, 0, STATE_FOCUSABLE);
       testStates("aria_grid_disabled", 0, 0, STATE_FOCUSABLE);
       testStates("aria_tree_disabled", 0, 0, STATE_FOCUSABLE);
       testStates("aria_treegrid_disabled", 0, 0, STATE_FOCUSABLE);
@@ -355,16 +362,21 @@
      title="Pressed state is not exposed on a button element with aria-pressed attribute">
     Mozilla Bug 989958
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
      title="Support ARIA 1.1 switch role">
     Mozilla Bug 1136563
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1355921"
+     title="Elements with a defined, non-false value for aria-current should expose ATK_STATE_ACTIVE">
+    Mozilla Bug 1355921
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="textbox_autocomplete_inline" role="textbox" aria-autocomplete="inline"></div>
   <div id="textbox_autocomplete_list" role="textbox" aria-autocomplete="list"></div>
@@ -626,10 +638,16 @@
   <div id="aria_treegrid_disabled" role="treegrid" aria-disabled="true">
     <div role="row"><div role="gridcell">H</div></div>
     <div role="row"><div role="gridcell">h</div></div>
   </div>
 
   <!-- Test that directory is readonly -->
   <div id="aria_directory" role="directory"></div>
 
+  <!-- aria-current -->
+  <div id="current_page_1" role="link" aria-current="page">1</div>
+  <div id="page_2" role="link" aria-current="false">2</div>
+  <div id="page_3" role="link">3</div>
+  <div id="page_4" role="link" aria-current="">4</div>
+  <div id="current_foo" aria-current="foo">foo</div>
 </body>
 </html>
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -108,16 +108,17 @@ GK_ATOM(applyTemplates, "apply-templates
 GK_ATOM(archive, "archive")
 GK_ATOM(area, "area")
 GK_ATOM(aria_activedescendant, "aria-activedescendant")
 GK_ATOM(aria_atomic, "aria-atomic")
 GK_ATOM(aria_autocomplete, "aria-autocomplete")
 GK_ATOM(aria_busy, "aria-busy")
 GK_ATOM(aria_checked, "aria-checked")
 GK_ATOM(aria_controls, "aria-controls")
+GK_ATOM(aria_current, "aria-current")
 GK_ATOM(aria_describedby, "aria-describedby")
 GK_ATOM(aria_disabled, "aria-disabled")
 GK_ATOM(aria_dropeffect, "aria-dropeffect")
 GK_ATOM(aria_expanded, "aria-expanded")
 GK_ATOM(aria_flowto, "aria-flowto")
 GK_ATOM(aria_haspopup, "aria-haspopup")
 GK_ATOM(aria_hidden, "aria-hidden")
 GK_ATOM(aria_invalid, "aria-invalid")