Bug 835121 - ARIA grid should be editable by default, r=tbsaunde
authorAlexander Surkov <surkov.alexander@gmail.com>
Thu, 14 Feb 2013 19:57:35 +0900
changeset 131768 55a453689fb1ff9001035a012b2d0e627616c499
parent 131767 78c0b4615cfef1e62b8741d3546f8e92be2bd46f
child 131769 a0928943de651da72e09aac1662113467fc58c79
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstbsaunde
bugs835121
milestone21.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 835121 - ARIA grid should be editable by default, r=tbsaunde
accessible/src/base/ARIAStateMap.cpp
accessible/src/base/ARIAStateMap.h
accessible/src/base/nsARIAMap.cpp
accessible/src/generic/Accessible.cpp
accessible/src/generic/Accessible.h
accessible/tests/mochitest/states/test_aria.html
--- a/accessible/src/base/ARIAStateMap.cpp
+++ b/accessible/src/base/ARIAStateMap.cpp
@@ -267,16 +267,26 @@ aria::MapToState(EStateRule aRule, dom::
       static const TokenTypeData data(
         nsGkAtoms::aria_readonly, eBoolType | eDefinedIfAbsent,
         0, states::READONLY, states::EDITABLE);
 
       MapTokenType(aElement, aState, data);
       return true;
     }
 
+    case eARIAReadonlyOrEditableIfDefined:
+    {
+      static const TokenTypeData data(
+        nsGkAtoms::aria_readonly, eBoolType,
+        0, states::READONLY, states::EDITABLE);
+
+      MapTokenType(aElement, aState, data);
+      return true;
+    }
+
     case eARIARequired:
     {
       static const TokenTypeData data(
         nsGkAtoms::aria_required, eBoolType,
         0, states::REQUIRED);
 
       MapTokenType(aElement, aState, data);
       return true;
--- a/accessible/src/base/ARIAStateMap.h
+++ b/accessible/src/base/ARIAStateMap.h
@@ -34,16 +34,17 @@ enum EStateRule
   eARIAHasPopup,
   eARIAInvalid,
   eARIAMultiline,
   eARIAMultiSelectable,
   eARIAOrientation,
   eARIAPressed,
   eARIAReadonly,
   eARIAReadonlyOrEditable,
+  eARIAReadonlyOrEditableIfDefined,
   eARIARequired,
   eARIASelectable,
   eReadonlyUntilEditable,
   eIndeterminateIfNoValue
 };
 
 /**
  * Expose the accessible states for the given element accordingly to state
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -173,29 +173,29 @@ static nsRoleMapEntry sWAIRoleMaps[] =
     roles::TABLE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eSelect | eTable,
     states::FOCUSABLE,
     eARIAMultiSelectable,
-    eARIAReadonly
+    eARIAReadonlyOrEditable
   },
   { // gridcell
     &nsGkAtoms::gridcell,
     roles::GRID_CELL,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eTableCell,
     kNoReqStates,
     eARIASelectable,
-    eARIAReadonly
+    eARIAReadonlyOrEditableIfDefined
   },
   { // group
     &nsGkAtoms::group,
     roles::GROUPING,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -17,16 +17,18 @@
 #include "nsIAccessibleRelation.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "States.h"
 #include "StyleInfo.h"
+#include "TableAccessible.h"
+#include "TableCellAccessible.h"
 #include "TreeWalker.h"
 
 #include "nsContentUtils.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMNodeFilter.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMTreeWalker.h"
@@ -1592,16 +1594,32 @@ Accessible::ApplyARIAState(uint64_t* aSt
   if (!mRoleMapEntry)
     return;
 
   *aState |= mRoleMapEntry->state;
 
   if (aria::MapToState(mRoleMapEntry->attributeMap1, element, aState) &&
       aria::MapToState(mRoleMapEntry->attributeMap2, element, aState))
     aria::MapToState(mRoleMapEntry->attributeMap3, element, aState);
+
+  // ARIA gridcell inherits editable/readonly states from the grid until it's
+  // overridden.
+  if (mRoleMapEntry->Is(nsGkAtoms::gridcell) &&
+      !(*aState & (states::READONLY | states::EDITABLE))) {
+    const TableCellAccessible* cell = AsTableCell();
+    if (cell) {
+      TableAccessible* table = cell->Table();
+      if (table) {
+        Accessible* grid = table->AsAccessible();
+        uint64_t gridState = 0;
+        grid->ApplyARIAState(&gridState);
+        *aState |= (gridState & (states::READONLY | states::EDITABLE));
+      }
+    }
+  }
 }
 
 NS_IMETHODIMP
 Accessible::GetValue(nsAString& aValue)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -509,16 +509,18 @@ public:
   a11y::RootAccessible* AsRoot();
 
   bool IsSelect() const { return HasGenericType(eSelect); }
 
   bool IsTable() const { return HasGenericType(eTable); }
   virtual TableAccessible* AsTable() { return nullptr; }
 
   virtual TableCellAccessible* AsTableCell() { return nullptr; }
+  const TableCellAccessible* AsTableCell() const
+    { return const_cast<Accessible*>(this)->AsTableCell(); }
 
   bool IsTableRow() const { return HasGenericType(eTableRow); }
 
   bool IsTextLeaf() const { return mType == eTextLeafType; }
   TextLeafAccessible* AsTextLeaf();
 
   bool IsXULTabpanels() const { return mType == eXULTabpanelsType; }
 
--- a/accessible/tests/mochitest/states/test_aria.html
+++ b/accessible/tests/mochitest/states/test_aria.html
@@ -105,16 +105,30 @@
                  STATE_MULTISELECTABLE | STATE_EXTSELECTABLE);
 
       // aria-pressed
       testStates("aria_pressed_button", STATE_PRESSED | STATE_CHECKABLE);
 
       // aria-readonly
       testStates("aria_readonly_textbox", STATE_READONLY);
 
+      // readonly/editable on grid and gridcell
+      testStates("aria_grid_default", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_grid_default_cell_readonly", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+      testStates("aria_grid_default_cell_inherited", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_grid_readonly", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+      testStates("aria_grid_readonly_cell_editable", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_grid_readonly_cell_inherited", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+
       // aria-selectable
       testStates("aria_selectable_listitem", STATE_SELECTABLE | STATE_SELECTED);
 
       // active state caused by aria-activedescendant
       testStates("as_item1", 0, EXT_STATE_ACTIVE);
       testStates("as_item2", 0, 0, 0, EXT_STATE_ACTIVE);
 
       // universal ARIA properties inherited from file input control
@@ -225,16 +239,21 @@
      title="ARIA undetermined progressmeters should expose mixed state">
     Mozilla Bug 740851
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=762876
      title="fix default horizontal / vertical state of role=scrollbar and ensure only one of horizontal / vertical states is exposed">
     Mozilla Bug 762876
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=835121
+     title="ARIA grid should be editable by default">
+    Mozilla Bug 835121
+  </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>
   <div id="textbox_autocomplete_both" role="textbox" aria-autocomplete="both"></div>
@@ -260,17 +279,36 @@
   </div>
 
   <div id="aria_mixed_checkbox" role="checkbox" aria-checked="mixed">
     I might agree
   </div>
   <div id="aria_multiline_textbox" role="textbox" aria-multiline="true"></div>
   <div id="aria_multiselectable_listbox" role="listbox" aria-multiselectable="true"></div>
   <div id="aria_pressed_button" role="button" aria-pressed="true">Button</div>
-  <div id="aria_readonly_textbox" role="textbox" aria-readonly="true">This text should be readonly</div>
+
+  <div id="aria_readonly_textbox"
+       role="textbox" aria-readonly="true">This text should be readonly</div>
+
+  <div id="aria_grid_default" role="grid">
+    <div role="row">
+      <div id="aria_grid_default_cell_readonly"
+           role="gridcell" aria-readonly="true">gridcell1</div>
+      <div id="aria_grid_default_cell_inherited"
+           role="gridcell">gridcell2</div>
+  </div>
+
+  <div id="aria_grid_readonly" role="grid" aria-readonly="true">
+    <div role="row">
+      <div id="aria_grid_readonly_cell_editable"
+           role="gridcell" aria-readonly="false">gridcell1</div>
+      <div id="aria_grid_readonly_cell_inherited"
+           role="gridcell">gridcell2</div>
+  </div>
+
   <div role="listbox">
     <div id="aria_selectable_listitem" role="option" aria-selected="true">Item1</div>
   </div>
 
   <!-- Test that aria-disabled state gets propagated to all descendants -->
   <div id="group" role="group" aria-disabled="true">
     <button>hi</button>
     <div tabindex="0" role="listbox" aria-activedescendant="item1">
@@ -308,17 +346,17 @@
   <!-- strange edge case: please don't do this in the wild -->
   <a id="aria_link_link" role="link" href="foo">link</a>
   <a id="aria_link_anchor" role="link" name="link_anchor">link</a>
 
   <!-- landmarks: links -->
   <a id="aria_application_link" role="application" href="foo">app</a>
   <a id="aria_main_link" role="main" href="foo">main</a>
   <a id="aria_navigation_link" role="navigation" href="foo">nav</a>
-  
+
   <!-- landmarks: anchors -->
   <a id="aria_application_anchor" role="application" name="app_anchor">app</a>
   <a id="aria_main_anchor" role="main" name="main_anchor">main</a>
   <a id="aria_navigation_anchor" role="navigation" name="nav_anchor">nav</a>
 
   <!-- aria-orientation -->
   <div id="aria_scrollbar" role="scrollbar">scrollbar</div>
   <div id="aria_hscrollbar" role="scrollbar" aria-orientation="horizontal">horizontal scrollbar</div>