Bug 1146257 - spanned headers don't work well in our table code, r=marcoz
authorAlexander Surkov <surkov.alexander@gmail.com>
Thu, 26 Mar 2015 08:05:06 -0400
changeset 266132 6e52859a78a7b8c5a1c74c825a1ad96c7c201adc
parent 266131 4c099b1b4e58dec6394e98275689136d07eca00e
child 266133 c309c399dfad6d8ef09be2098b88cba96b4ff63b
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarcoz
bugs1146257
milestone39.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 1146257 - spanned headers don't work well in our table code, r=marcoz
accessible/html/HTMLTableAccessible.cpp
accessible/tests/mochitest/table/test_headers_table.html
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -309,44 +309,38 @@ HTMLTableHeaderCellAccessible::NativeRol
     case 0:
     case 1:
       return roles::COLUMNHEADER;
     case 2:
     case 3:
       return roles::ROWHEADER;
   }
 
-  // Assume it's columnheader if there are headers in siblings, otherwise
-  // rowheader.
-  // This should iterate the flattened tree
-  nsIContent* parentContent = mContent->GetParent();
-  if (!parentContent) {
-    NS_ERROR("Deattached content on alive accessible?");
+  TableAccessible* table = Table();
+  if (!table)
     return roles::NOTHING;
-  }
+
+  // If the cell next to this one is not a header cell then assume this cell is
+  // a row header for it.
+  uint32_t rowIdx = RowIdx(), colIdx = ColIdx();
+  Accessible* cell = table->CellAt(rowIdx, colIdx + ColExtent());
+  if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent()))
+    return roles::ROWHEADER;
 
-  for (nsIContent* siblingContent = mContent->GetPreviousSibling(); siblingContent;
-       siblingContent = siblingContent->GetPreviousSibling()) {
-    if (siblingContent->IsElement()) {
-      return nsCoreUtils::IsHTMLTableHeader(siblingContent) ?
-        roles::COLUMNHEADER : roles::ROWHEADER;
-    }
-  }
+  // If the cell below this one is not a header cell then assume this cell is
+  // a column header for it.
+  uint32_t rowExtent = RowExtent();
+  cell = table->CellAt(rowIdx + rowExtent, colIdx);
+  if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent()))
+    return roles::COLUMNHEADER;
 
-  for (nsIContent* siblingContent = mContent->GetNextSibling(); siblingContent;
-       siblingContent = siblingContent->GetNextSibling()) {
-    if (siblingContent->IsElement()) {
-      return nsCoreUtils::IsHTMLTableHeader(siblingContent) ?
-       roles::COLUMNHEADER : roles::ROWHEADER;
-    }
-  }
-
-  // No elements in siblings what means the table has one column only. Therefore
-  // it should be column header.
-  return roles::COLUMNHEADER;
+  // Otherwise if this cell is surrounded by header cells only then make a guess
+  // based on its cell spanning. In other words if it is row spanned then assume
+  // it's a row header, otherwise it's a column header.
+  return rowExtent > 1 ? roles::ROWHEADER : roles::COLUMNHEADER;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableRowAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableRowAccessible, Accessible)
--- a/accessible/tests/mochitest/table/test_headers_table.html
+++ b/accessible/tests/mochitest/table/test_headers_table.html
@@ -287,16 +287,152 @@
           cell: "t8_r3c4",
           rowHeaderCells: [ "t8_10km" ],
           columnHeaderCells: [ "t8_Todd", "t8_Males" ]
         }
       ];
 
       testHeaderCells(headerInfoMap);
 
+      //////////////////////////////////////////////////////////////////////////
+      // spanned table header cells (v1), @headers define header order
+
+      headerInfoMap = [
+        {
+          cell: "t9_r1c1",
+          rowHeaderCells: [ "t9_females", "t9_mary" ],
+          columnHeaderCells: [ "t9_1km" ]
+        },
+        {
+          cell: "t9_r1c2",
+          rowHeaderCells: [ "t9_females", "t9_mary" ],
+          columnHeaderCells: [ "t9_5km" ]
+        },
+        {
+          cell: "t9_r1c3",
+          rowHeaderCells: [ "t9_females", "t9_mary" ],
+          columnHeaderCells: [ "t9_10km" ]
+        },
+        {
+          cell: "t9_r2c1",
+          rowHeaderCells: [ "t9_females", "t9_betsy" ],
+          columnHeaderCells: [ "t9_1km" ]
+        },
+        {
+          cell: "t9_r2c2",
+          rowHeaderCells: [ "t9_females", "t9_betsy" ],
+          columnHeaderCells: [ "t9_5km" ]
+        },
+        {
+          cell: "t9_r2c3",
+          rowHeaderCells: [ "t9_females", "t9_betsy" ],
+          columnHeaderCells: [ "t9_10km" ]
+        },
+        {
+          cell: "t9_r3c1",
+          rowHeaderCells: [ "t9_males", "t9_matt" ],
+          columnHeaderCells: [ "t9_1km" ]
+        },
+        {
+          cell: "t9_r3c2",
+          rowHeaderCells: [ "t9_males", "t9_matt" ],
+          columnHeaderCells: [ "t9_5km" ]
+        },
+        {
+          cell: "t9_r3c3",
+          rowHeaderCells: [ "t9_males", "t9_matt" ],
+          columnHeaderCells: [ "t9_10km" ]
+        },
+        {
+          cell: "t9_r4c1",
+          rowHeaderCells: [ "t9_males", "t9_todd" ],
+          columnHeaderCells: [ "t9_1km" ]
+        },
+        {
+          cell: "t9_r4c2",
+          rowHeaderCells: [ "t9_males", "t9_todd" ],
+          columnHeaderCells: [ "t9_5km" ]
+        },
+        {
+          cell: "t9_r4c3",
+          rowHeaderCells: [ "t9_males", "t9_todd" ],
+          columnHeaderCells: [ "t9_10km" ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
+      //////////////////////////////////////////////////////////////////////////
+      // spanned table header cells (v2), @headers define header order
+
+      headerInfoMap = [
+        {
+          cell: "t10_r1c1",
+          rowHeaderCells: [ "t10_1km" ],
+          columnHeaderCells: [ "t10_females", "t10_mary" ]
+        },
+        {
+          cell: "t10_r1c2",
+          rowHeaderCells: [ "t10_1km" ],
+          columnHeaderCells: [ "t10_females", "t10_betsy" ]
+        },
+        {
+          cell: "t10_r1c3",
+          rowHeaderCells: [ "t10_1km" ],
+          columnHeaderCells: [ "t10_males", "t10_matt" ]
+        },
+        {
+          cell: "t10_r1c4",
+          rowHeaderCells: [ "t10_1km" ],
+          columnHeaderCells: [ "t10_males", "t10_todd" ]
+        },
+        {
+          cell: "t10_r2c1",
+          rowHeaderCells: [ "t10_5km" ],
+          columnHeaderCells: [ "t10_females", "t10_mary" ]
+        },
+        {
+          cell: "t10_r2c2",
+          rowHeaderCells: [ "t10_5km" ],
+          columnHeaderCells: [ "t10_females", "t10_betsy" ]
+        },
+        {
+          cell: "t10_r2c3",
+          rowHeaderCells: [ "t10_5km" ],
+          columnHeaderCells: [ "t10_males", "t10_matt" ]
+        },
+        {
+          cell: "t10_r2c4",
+          rowHeaderCells: [ "t10_5km" ],
+          columnHeaderCells: [ "t10_males", "t10_todd" ]
+        },
+        {
+          cell: "t10_r3c1",
+          rowHeaderCells: [ "t10_10km" ],
+          columnHeaderCells: [ "t10_females", "t10_mary" ]
+        },
+        {
+          cell: "t10_r3c2",
+          rowHeaderCells: [ "t10_10km" ],
+          columnHeaderCells: [ "t10_females", "t10_betsy" ]
+        },
+        {
+          cell: "t10_r3c3",
+          rowHeaderCells: [ "t10_10km" ],
+          columnHeaderCells: [ "t10_males", "t10_matt" ]
+        },
+        {
+          cell: "t10_r3c4",
+          rowHeaderCells: [ "t10_10km" ],
+          columnHeaderCells: [ "t10_males", "t10_todd" ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
@@ -493,10 +629,85 @@
         <td id="t8_r3c2">55:38</td>
         <td id="t8_r3c3">57:04</td>
         <td id="t8_r3c4">50:35</td>
       </tr>
 
     </tbody>
   </table>
 
+  <table id="table9" border="1">
+    <caption>
+      Example 1 (row group headers):
+    </caption>
+    <tr>
+      <td colspan="2"><span class="offscreen">empty</span></td>
+      <th id="t9_1km" width="40">1 km</th>
+      <th id="t9_5km" width="35">5 km</th>
+      <th id="t9_10km" width="42">10 km</th>
+    </tr>
+    <tr>
+      <th id="t9_females" width="56" rowspan="2">Females</th>
+      <th id="t9_mary" width="39">Mary</th>
+      <td id="t9_r1c1" headers="t9_females t9_mary t9_1km">8:32</td>
+      <td id="t9_r1c2" headers="t9_females t9_mary t9_5km">28:04</td>
+      <td id="t9_r1c3" headers="t9_females t9_mary t9_10km">1:01:16</td>
+    </tr>
+    <tr>
+      <th id="t9_betsy">Betsy</th>
+      <td id="t9_r2c1" headers="t9_females t9_betsy t9_1km">7:43</td>
+      <td id="t9_r2c2" headers="t9_females t9_betsy t9_5km">26:47</td>
+      <td id="t9_r2c3" headers="t9_females t9_betsy t9_10km">55:38</td>
+    </tr>
+    <tr>
+      <th id="t9_males" rowspan="2">Males</th>
+      <th id="t9_matt">Matt</th>
+      <td id="t9_r3c1" headers="t9_males t9_matt t9_1km">7:55</td>
+      <td id="t9_r3c2" headers="t9_males t9_matt t9_5km">27:29</td>
+      <td id="t9_r3c3" headers="t9_males t9_matt t9_10km">57:04</td>
+    </tr>
+    <tr>
+      <th id="t9_todd">Todd</th>
+      <td id="t9_r4c1" headers="t9_males t9_todd t9_1km">7:01</td>
+      <td id="t9_r4c2" headers="t9_males t9_todd t9_5km">24:21</td>
+      <td id="t9_r4c3" headers="t9_males t9_todd t9_10km">50:35</td>
+    </tr>
+  </table>
+
+  <table id="table10" border="1">
+    <caption>
+      Example 2 (column group headers):
+    </caption>
+    <tr>
+      <td rowspan="2"><span class="offscreen">empty</span></td>
+      <th colspan="2" id="t10_females">Females</th>
+      <th colspan="2" id="t10_males">Males</th>
+    </tr>
+    <tr>
+      <th width="40" id="t10_mary">Mary</th>
+      <th width="35" id="t10_betsy">Betsy</th>
+      <th width="42" id="t10_matt">Matt</th>
+      <th width="42" id="t10_todd">Todd</th>
+    </tr>
+    <tr>
+      <th width="39" id="t10_1km">1 km</th>
+      <td headers="t10_females t10_mary t10_1km" id="t10_r1c1">8:32</td>
+      <td headers="t10_females t10_betsy t10_1km" id="t10_r1c2">7:43</td>
+      <td headers="t10_males t10_matt t10_1km" id="t10_r1c3">7:55</td>
+      <td headers="t10_males t10_todd t10_1km" id="t10_r1c4">7:01</td>
+    </tr>
+    <tr>
+      <th id="t10_5km">5 km</th>
+      <td headers="t10_females t10_mary t10_5km" id="t10_r2c1">28:04</td>
+      <td headers="t10_females t10_betsy t10_5km" id="t10_r2c2">26:47</td>
+      <td headers="t10_males t10_matt t10_5km" id="t10_r2c3">27:29</td>
+      <td headers="t10_males t10_todd t10_5km" id="t10_r2c4">24:21</td>
+    </tr>
+    <tr>
+      <th id="t10_10km">10 km</th>
+      <td headers="t10_females t10_mary t10_10km" id="t10_r3c1">1:01:16</td>
+      <td headers="t10_females t10_betsy t10_10km" id="t10_r3c2">55:38</td>
+      <td headers="t10_males t10_matt t10_10km" id="t10_r3c3">57:04</td>
+      <td headers="t10_males t10_todd t10_10km" id="t10_r3c4">50:35</td>
+    </tr>
+  </table>
 </body>
 </html>