Bug 1173364 - support ARIA table and cell roles, r=marcoz
authorAlexander Surkov <surkov.alexander@gmail.com>
Thu, 25 Jun 2015 11:59:06 -0700
changeset 250260 a4bcc7ee6e5c0582ec95b5caf34ecb46dcf37bda
parent 250259 a5cd9f18f1b6173844f26da8780635c1683f80c4
child 250261 28bbd1fb7ed10f2ec33b1b0129ed3ad335e5ae59
push id13730
push usercbook@mozilla.com
push dateFri, 26 Jun 2015 11:58:56 +0000
treeherderfx-team@56e207dbb3bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarcoz
bugs1173364
milestone41.0a1
Bug 1173364 - support ARIA table and cell roles, r=marcoz
accessible/base/ARIAMap.cpp
accessible/base/Filters.cpp
accessible/generic/ARIAGridAccessible.cpp
accessible/tests/mochitest/table/a11y.ini
accessible/tests/mochitest/table/test_headers_ariatable.html
accessible/tests/mochitest/tree/a11y.ini
accessible/tests/mochitest/tree/test_aria_table.html
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -93,16 +93,26 @@ static nsRoleMapEntry sWAIRoleMaps[] =
     kUseMapRole,
     eNoValue,
     ePressAction,
     eNoLiveAttr,
     eButton,
     kNoReqStates
     // eARIAPressed is auto applied on any button
   },
+  { // cell
+    &nsGkAtoms::cell,
+    roles::CELL,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eTableCell,
+    kNoReqStates
+  },
   { // checkbox
     &nsGkAtoms::checkbox,
     roles::CHECKBUTTON,
     kUseMapRole,
     eNoValue,
     eCheckUncheckAction,
     eNoLiveAttr,
     kGenericAccType,
@@ -626,16 +636,27 @@ static nsRoleMapEntry sWAIRoleMaps[] =
     kUseMapRole,
     eNoValue,
     eSwitchAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
     eARIASelectable
   },
+  { // table
+    &nsGkAtoms::table,
+    roles::TABLE,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eTable,
+    kNoReqStates,
+    eARIASelectable
+  },
   { // tablist
     &nsGkAtoms::tablist,
     roles::PAGETABLIST,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eSelect,
--- a/accessible/base/Filters.cpp
+++ b/accessible/base/Filters.cpp
@@ -43,18 +43,19 @@ filters::GetRow(Accessible* aAccessible)
 
   return eSkipSubtree;
 }
 
 uint32_t
 filters::GetCell(Accessible* aAccessible)
 {
   a11y::role role = aAccessible->Role();
-  return role == roles::GRID_CELL || role == roles::ROWHEADER ||
-    role == roles::COLUMNHEADER ? eMatch : eSkipSubtree;
+  return role == roles::CELL || role == roles::GRID_CELL ||
+         role == roles::ROWHEADER || role == roles::COLUMNHEADER ?
+    eMatch : eSkipSubtree;
 }
 
 uint32_t
 filters::GetEmbeddedObject(Accessible* aAccessible)
 {
   return nsAccUtils::IsEmbeddedObject(aAccessible) ?
     eMatch | eSkipSubtree : eSkipSubtree;
 }
--- a/accessible/generic/ARIAGridAccessible.cpp
+++ b/accessible/generic/ARIAGridAccessible.cpp
@@ -63,27 +63,30 @@ ARIAGridAccessible::RowCount()
   while (rowIter.Next())
     rowCount++;
 
   return rowCount;
 }
 
 Accessible*
 ARIAGridAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex)
-{ 
+{
   Accessible* row = GetRowAt(aRowIndex);
   if (!row)
     return nullptr;
 
   return GetCellInRowAt(row, aColumnIndex);
 }
 
 bool
 ARIAGridAccessible::IsColSelected(uint32_t aColIdx)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return false;
+
   AccIterator rowIter(this, filters::GetRow);
   Accessible* row = rowIter.Next();
   if (!row)
     return false;
 
   do {
     if (!nsAccUtils::IsARIASelected(row)) {
       Accessible* cell = GetCellInRowAt(row, aColIdx);
@@ -93,16 +96,19 @@ ARIAGridAccessible::IsColSelected(uint32
   } while ((row = rowIter.Next()));
 
   return true;
 }
 
 bool
 ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return false;
+
   Accessible* row = GetRowAt(aRowIdx);
   if(!row)
     return false;
 
   if (!nsAccUtils::IsARIASelected(row)) {
     AccIterator cellIter(row, filters::GetCell);
     Accessible* cell = nullptr;
     while ((cell = cellIter.Next())) {
@@ -112,32 +118,38 @@ ARIAGridAccessible::IsRowSelected(uint32
   }
 
   return true;
 }
 
 bool
 ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return false;
+
   Accessible* row = GetRowAt(aRowIdx);
   if(!row)
     return false;
 
   if (!nsAccUtils::IsARIASelected(row)) {
     Accessible* cell = GetCellInRowAt(row, aColIdx);
     if (!cell || !nsAccUtils::IsARIASelected(cell))
       return false;
   }
 
   return true;
 }
 
 uint32_t
 ARIAGridAccessible::SelectedCellCount()
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return 0;
+
   uint32_t count = 0, colCount = ColCount();
 
   AccIterator rowIter(this, filters::GetRow);
   Accessible* row = nullptr;
 
   while ((row = rowIter.Next())) {
     if (nsAccUtils::IsARIASelected(row)) {
       count += colCount;
@@ -154,16 +166,19 @@ ARIAGridAccessible::SelectedCellCount()
   }
 
   return count;
 }
 
 uint32_t
 ARIAGridAccessible::SelectedColCount()
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return 0;
+
   uint32_t colCount = ColCount();
   if (!colCount)
     return 0;
 
   AccIterator rowIter(this, filters::GetRow);
   Accessible* row = rowIter.Next();
   if (!row)
     return 0;
@@ -188,16 +203,19 @@ ARIAGridAccessible::SelectedColCount()
   } while ((row = rowIter.Next()));
 
   return selColCount;
 }
 
 uint32_t
 ARIAGridAccessible::SelectedRowCount()
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return 0;
+
   uint32_t count = 0;
 
   AccIterator rowIter(this, filters::GetRow);
   Accessible* row = nullptr;
 
   while ((row = rowIter.Next())) {
     if (nsAccUtils::IsARIASelected(row)) {
       count++;
@@ -222,16 +240,19 @@ ARIAGridAccessible::SelectedRowCount()
   }
 
   return count;
 }
 
 void
 ARIAGridAccessible::SelectedCells(nsTArray<Accessible*>* aCells)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return;
+
   AccIterator rowIter(this, filters::GetRow);
 
   Accessible* row = nullptr;
   while ((row = rowIter.Next())) {
     AccIterator cellIter(row, filters::GetCell);
     Accessible* cell = nullptr;
 
     if (nsAccUtils::IsARIASelected(row)) {
@@ -246,16 +267,19 @@ ARIAGridAccessible::SelectedCells(nsTArr
         aCells->AppendElement(cell);
     }
   }
 }
 
 void
 ARIAGridAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return;
+
   uint32_t colCount = ColCount();
 
   AccIterator rowIter(this, filters::GetRow);
   Accessible* row = nullptr;
   for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
     if (nsAccUtils::IsARIASelected(row)) {
       for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
         aCells->AppendElement(rowIdx * colCount + colIdx);
@@ -270,16 +294,19 @@ ARIAGridAccessible::SelectedCellIndices(
         aCells->AppendElement(rowIdx * colCount + colIdx);
     }
   }
 }
 
 void
 ARIAGridAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return;
+
   uint32_t colCount = ColCount();
   if (!colCount)
     return;
 
   AccIterator rowIter(this, filters::GetRow);
   Accessible* row = rowIter.Next();
   if (!row)
     return;
@@ -304,16 +331,19 @@ ARIAGridAccessible::SelectedColIndices(n
   for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
     if (isColSelArray[colIdx])
       aCols->AppendElement(colIdx);
 }
 
 void
 ARIAGridAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return;
+
   AccIterator rowIter(this, filters::GetRow);
   Accessible* row = nullptr;
   for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
     if (nsAccUtils::IsARIASelected(row)) {
       aRows->AppendElement(rowIdx);
       continue;
     }
 
@@ -333,28 +363,34 @@ ARIAGridAccessible::SelectedRowIndices(n
     if (isRowSelected)
       aRows->AppendElement(rowIdx);
   }
 }
 
 void
 ARIAGridAccessible::SelectRow(uint32_t aRowIdx)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return;
+
   AccIterator rowIter(this, filters::GetRow);
 
   Accessible* row = nullptr;
   for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
     DebugOnly<nsresult> rv = SetARIASelected(row, rowIdx == aRowIdx);
     NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
   }
 }
 
 void
 ARIAGridAccessible::SelectCol(uint32_t aColIdx)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return;
+
   AccIterator rowIter(this, filters::GetRow);
 
   Accessible* row = nullptr;
   while ((row = rowIter.Next())) {
     // Unselect all cells in the row.
     DebugOnly<nsresult> rv = SetARIASelected(row, false);
     NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
 
@@ -363,25 +399,30 @@ ARIAGridAccessible::SelectCol(uint32_t a
     if (cell)
       SetARIASelected(cell, true);
   }
 }
 
 void
 ARIAGridAccessible::UnselectRow(uint32_t aRowIdx)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return;
+
   Accessible* row = GetRowAt(aRowIdx);
-
   if (row)
     SetARIASelected(row, false);
 }
 
 void
 ARIAGridAccessible::UnselectCol(uint32_t aColIdx)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return;
+
   AccIterator rowIter(this, filters::GetRow);
 
   Accessible* row = nullptr;
   while ((row = rowIter.Next())) {
     Accessible* cell = GetCellInRowAt(row, aColIdx);
     if (cell)
       SetARIASelected(cell, false);
   }
@@ -416,16 +457,19 @@ ARIAGridAccessible::GetCellInRowAt(Acces
 
   return cell;
 }
 
 nsresult
 ARIAGridAccessible::SetARIASelected(Accessible* aAccessible,
                                     bool aIsSelected, bool aNotify)
 {
+  if (IsARIARole(nsGkAtoms::table))
+    return NS_OK;
+
   nsIContent *content = aAccessible->GetContent();
   NS_ENSURE_STATE(content);
 
   nsresult rv = NS_OK;
   if (aIsSelected)
     rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
                           NS_LITERAL_STRING("true"), aNotify);
   else
@@ -519,18 +563,18 @@ ARIAGridCellAccessible::ColIdx() const
   if (!row)
     return 0;
 
   int32_t indexInRow = IndexInParent();
   uint32_t colIdx = 0;
   for (int32_t idx = 0; idx < indexInRow; idx++) {
     Accessible* cell = row->GetChildAt(idx);
     roles::Role role = cell->Role();
-    if (role == roles::GRID_CELL || role == roles::ROWHEADER ||
-        role == roles::COLUMNHEADER)
+    if (role == roles::CELL || role == roles::GRID_CELL ||
+        role == roles::ROWHEADER || role == roles::COLUMNHEADER)
       colIdx++;
   }
 
   return colIdx;
 }
 
 uint32_t
 ARIAGridCellAccessible::RowIdx() const
@@ -588,18 +632,18 @@ ARIAGridCellAccessible::NativeAttributes
   int32_t colIdx = 0, colCount = 0;
   uint32_t childCount = thisRow->ChildCount();
   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
     Accessible* child = thisRow->GetChildAt(childIdx);
     if (child == this)
       colIdx = colCount;
 
     roles::Role role = child->Role();
-    if (role == roles::GRID_CELL || role == roles::ROWHEADER ||
-        role == roles::COLUMNHEADER)
+    if (role == roles::CELL || role == roles::GRID_CELL ||
+        role == roles::ROWHEADER || role == roles::COLUMNHEADER)
       colCount++;
   }
 
   int32_t rowIdx = RowIndexFor(thisRow);
 
   nsAutoString stringIdx;
   stringIdx.AppendInt(rowIdx * colCount + colIdx);
   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx);
--- a/accessible/tests/mochitest/table/a11y.ini
+++ b/accessible/tests/mochitest/table/a11y.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 
 [test_headers_ariagrid.html]
+[test_headers_ariatable.html]
 [test_headers_listbox.xul]
 [test_headers_table.html]
 [test_headers_tree.xul]
 [test_indexes_ariagrid.html]
 [test_indexes_listbox.xul]
 [test_indexes_table.html]
 [test_indexes_tree.xul]
 [test_layoutguess.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_headers_ariatable.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+  <title>Table header information cells for ARIA table</title>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../table.js"></script>
+
+  <script type="application/javascript">
+
+    function doTest()
+    {
+      //////////////////////////////////////////////////////////////////////////
+      // column and row headers from markup
+
+      headerInfoMap = [
+        {
+          cell: "table_dc_1",
+          rowHeaderCells: [ "table_rh_1" ],
+          columnHeaderCells: [ "table_ch_2" ]
+        },
+        {
+          cell: "table_dc_2",
+          rowHeaderCells: [ "table_rh_1" ],
+          columnHeaderCells: [ "table_ch_3" ]
+        },
+        {
+          cell: "table_dc_3",
+          rowHeaderCells: [ "table_rh_2" ],
+          columnHeaderCells: [ "table_ch_2" ]
+        },
+        {
+          cell: "table_dc_4",
+          rowHeaderCells: [ "table_rh_2" ],
+          columnHeaderCells: [ "table_ch_3" ]
+        },
+        {
+          cell: "table_rh_1",
+          rowHeaderCells: [],
+          columnHeaderCells: [ "table_ch_1" ]
+        },
+        {
+          cell: "table_rh_2",
+          rowHeaderCells: [],
+          columnHeaderCells: [ "table_ch_1" ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+
+<body>
+  <a target="_blank"
+     title="support ARIA table and cell roles"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173364">Bug 1173364</a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div role="table">
+    <div role="row">
+      <span id="table_ch_1" role="columnheader">col_1</span>
+      <span id="table_ch_2" role="columnheader">col_2</span>
+      <span id="table_ch_3" role="columnheader">col_3</span>
+    </div>
+    <div role="row">
+      <span id="table_rh_1" role="rowheader">row_1</span>
+      <span id="table_dc_1" role="cell">cell1</span>
+      <span id="table_dc_2" role="cell">cell2</span>
+    </div>
+    <div role="row">
+      <span id="table_rh_2" role="rowheader">row_2</span>
+      <span id="table_dc_3" role="cell">cell3</span>
+      <span id="table_dc_4" role="cell">cell4</span>
+    </div>
+  </div>
+
+</body>
+</html>
--- a/accessible/tests/mochitest/tree/a11y.ini
+++ b/accessible/tests/mochitest/tree/a11y.ini
@@ -6,16 +6,17 @@ support-files =
 [test_applicationacc.xul]
 skip-if = true # Bug 561508
 [test_aria_globals.html]
 [test_aria_grid.html]
 [test_aria_imgmap.html]
 [test_aria_list.html]
 [test_aria_menu.html]
 [test_aria_presentation.html]
+[test_aria_table.html]
 [test_brokencontext.html]
 [test_button.xul]
 [test_canvas.html]
 [test_combobox.xul]
 [test_cssoverflow.html]
 [test_dochierarchy.html]
 [test_dockids.html]
 [test_filectrl.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_table.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>ARIA table tests</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+
+  <script type="application/javascript">
+    function doTest()
+    {
+      //////////////////////////////////////////////////////////////////////////
+      // table having rowgroups
+
+      var accTree =
+        { TABLE: [
+          { GROUPING: [
+            { ROW: [
+              { CELL: [
+                { TEXT_LEAF: [ ] }
+              ] }
+            ] }
+          ] },
+        ] };
+
+      testAccessibleTree("table", accTree);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="support ARIA table and cell roles"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173364">
+    Bug 1173364
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="table" role="table">
+    <div role="rowgroup">
+      <div role="row">
+        <div role="cell">cell</div>
+      </div>
+    </div>
+  </div>
+
+</body>
+</html>