Bug 386813 - Support table interfaces for grid/treegrid when no HTML table undeneath, r=davdib, marcoz, sr=neil
authorAlexander Surkov <surkov.alexander@gmail.com>
Mon, 18 May 2009 16:35:21 +0800
changeset 28493 44170cc1c68aab97b5207b693b6559ec60d24ba1
parent 28492 6b4855936c0a34176ee885bb008a556218f6c2ad
child 28494 a6611891e0ff302d17a40d975eff46a2ae5b70a6
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdavdib, marcoz, neil
bugs386813
milestone1.9.2a1pre
Bug 386813 - Support table interfaces for grid/treegrid when no HTML table undeneath, r=davdib, marcoz, sr=neil
accessible/public/nsIAccessibleTable.idl
accessible/src/base/Makefile.in
accessible/src/base/nsARIAGridAccessible.cpp
accessible/src/base/nsARIAGridAccessible.h
accessible/src/base/nsAccessibilityService.cpp
accessible/tests/mochitest/Makefile.in
accessible/tests/mochitest/table.js
accessible/tests/mochitest/test_table_indexes.html
accessible/tests/mochitest/test_table_indexes_ariagrid.html
--- a/accessible/public/nsIAccessibleTable.idl
+++ b/accessible/public/nsIAccessibleTable.idl
@@ -41,21 +41,48 @@
 
 #include "nsISupports.idl"
 
 interface nsIAccessible;
 
 [scriptable, uuid(dcc1e5c3-966e-45b2-b30a-839d35432b24)]
 interface nsIAccessibleTable : nsISupports
 {
+  /**
+   * Returns the caption accessible for the table. For example, html:caption
+   * element of html:table element.
+   */
   readonly attribute nsIAccessible       caption;
+
+  /**
+   * Returns summary description for the table. For example, @summary attribute
+   * on html:element.
+   */
   readonly attribute AString             summary;
+
+  /**
+   * Returns columns count in the table.
+   * XXX: not very well named property.
+   */
   readonly attribute long                columns;
+
+  /**
+   * Returns table accessible containing column headers.
+   */
   readonly attribute nsIAccessibleTable  columnHeader;
+
+  /**
+   * Returns rows count in the table.
+   * XXX: not very well named property.
+   */
   readonly attribute long                rows;
+
+  /**
+   * Returns table accessible containing row headers.
+   */
   readonly attribute nsIAccessibleTable  rowHeader;
 
   /**
    * Returns the accessible object at the specified row and column in the table.
    * If both row and column index are valid then the corresponding accessible
    * object is returned that represents the requested cell regardless of whether
    * the cell is currently visible (on the screen).
    *
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -70,16 +70,17 @@ REQUIRES	= appshell \
 		  xpcom \
 		  xuldoc \
 		  imglib2 \
 		  $(NULL)
 
 CPPSRCS = \
   nsAccessNode.cpp \
   nsAccessibleEventData.cpp \
+  nsARIAGridAccessible.cpp \
   nsARIAMap.cpp \
   nsDocAccessible.cpp \
   nsOuterDocAccessible.cpp \
   nsAccessibilityAtoms.cpp \
   nsCoreUtils.cpp \
   nsAccUtils.cpp \
   nsRelUtils.cpp \
   nsAccessibilityService.cpp \
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/nsARIAGridAccessible.cpp
@@ -0,0 +1,663 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Alexander Surkov <surkov.alexander@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsARIAGridAccessible.h"
+
+
+////////////////////////////////////////////////////////////////////////////////
+// nsARIAGridAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Constructor
+
+nsARIAGridAccessible::nsARIAGridAccessible(nsIDOMNode* aDomNode,
+                                           nsIWeakReference* aShell) :
+  nsAccessibleWrap(aDomNode, aShell)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsARIAGridAccessible,
+                             nsAccessible,
+                             nsIAccessibleTable)
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIAccessibleTable
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetCaption(nsIAccessible **aCaption)
+{
+  NS_ENSURE_ARG_POINTER(aCaption);
+  *aCaption = nsnull;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should be pointed by aria-labelledby on grid?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetSummary(nsAString &aSummary)
+{
+  aSummary.Truncate();
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should be pointed by aria-describedby on grid?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetColumns(PRInt32 *aColumns)
+{
+  NS_ENSURE_ARG_POINTER(aColumns);
+  *aColumns = 0;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIAccessible> row, nextChild;
+  GetFirstChild(getter_AddRefs(row));
+  while (row && nsAccUtils::Role(row) != nsIAccessibleRole::ROLE_ROW) {
+    row->GetNextSibling(getter_AddRefs(nextChild));
+    row.swap(nextChild);
+  }
+
+  if (!row)
+    return NS_OK;
+
+  nsCOMPtr<nsIAccessible> cell;
+  row->GetFirstChild(getter_AddRefs(cell));
+  while (cell) {
+    PRUint32 role = nsAccUtils::Role(cell);
+    if (role == nsIAccessibleRole::ROLE_GRID_CELL ||
+        role == nsIAccessibleRole::ROLE_ROWHEADER ||
+        role == nsIAccessibleRole::ROLE_COLUMNHEADER)
+      (*aColumns)++;
+
+    cell->GetNextSibling(getter_AddRefs(nextChild));
+    cell.swap(nextChild);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetColumnHeader(nsIAccessibleTable **aColumnHeader)
+{
+  NS_ENSURE_ARG_POINTER(aColumnHeader);
+  *aColumnHeader = nsnull;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: what should we return here?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetRows(PRInt32 *aRows)
+{
+  NS_ENSURE_ARG_POINTER(aRows);
+  *aRows = 0;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIAccessible> row, nextRow;
+  GetFirstChild(getter_AddRefs(row));
+  while (row) {
+    if (nsAccUtils::Role(row) == nsIAccessibleRole::ROLE_ROW)
+      (*aRows)++;
+
+    row->GetNextSibling(getter_AddRefs(nextRow));
+    row.swap(nextRow);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetRowHeader(nsIAccessibleTable **aRowHeader)
+{
+  NS_ENSURE_ARG_POINTER(aRowHeader);
+  *aRowHeader = nsnull;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: what should we return here?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::CellRefAt(PRInt32 aRow, PRInt32 aColumn,
+                                nsIAccessible **aAccessible)
+{
+  NS_ENSURE_ARG_POINTER(aAccessible);
+  *aAccessible = nsnull;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  PRInt32 rowIdx = aRow + 1;
+  nsCOMPtr<nsIAccessible> row, nextChild;
+  GetFirstChild(getter_AddRefs(row));
+  while (row) {
+    if (nsAccUtils::Role(row) == nsIAccessibleRole::ROLE_ROW)
+      rowIdx--;
+
+    if (rowIdx == 0)
+      break;
+
+    row->GetNextSibling(getter_AddRefs(nextChild));
+    row.swap(nextChild);
+  }
+
+  NS_ENSURE_ARG(row && rowIdx == 0);
+
+  PRInt32 colIdx = aColumn + 1;
+  nsCOMPtr<nsIAccessible> cell;
+  row->GetFirstChild(getter_AddRefs(cell));
+  while (cell) {
+    PRUint32 role = nsAccUtils::Role(cell);
+    if (role == nsIAccessibleRole::ROLE_GRID_CELL ||
+        role == nsIAccessibleRole::ROLE_ROWHEADER ||
+        role == nsIAccessibleRole::ROLE_COLUMNHEADER)
+      colIdx--;
+
+    if (colIdx == 0)
+      break;
+
+    cell->GetNextSibling(getter_AddRefs(nextChild));
+    cell.swap(nextChild);
+  }
+
+  NS_ENSURE_ARG(cell && colIdx == 0);
+
+  NS_ADDREF(*aAccessible = cell);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetIndexAt(PRInt32 aRow, PRInt32 aColumn, PRInt32 *aIndex)
+{
+  NS_ENSURE_ARG_POINTER(aIndex);
+  *aIndex = -1;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(aRow >= 0 && aColumn >= 0);
+
+  PRInt32 rowCount = 0;
+  GetRows(&rowCount);
+  NS_ENSURE_ARG(aRow < rowCount);
+
+  PRInt32 colCount = 0;
+  GetColumns(&colCount);
+  NS_ENSURE_ARG(aColumn < colCount);
+
+  *aIndex = colCount * aRow + aColumn;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetColumnAtIndex(PRInt32 aIndex, PRInt32 *aColumn)
+{
+  NS_ENSURE_ARG_POINTER(aColumn);
+  *aColumn = -1;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(aIndex >= 0);
+
+  PRInt32 rowCount = 0;
+  GetRows(&rowCount);
+  
+  PRInt32 colCount = 0;
+  GetColumns(&colCount);
+
+  NS_ENSURE_ARG(aIndex < rowCount * colCount);
+
+  *aColumn = aIndex % colCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetRowAtIndex(PRInt32 aIndex, PRInt32 *aRow)
+{
+  NS_ENSURE_ARG_POINTER(aRow);
+  *aRow = -1;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(aIndex >= 0);
+
+  PRInt32 rowCount = 0;
+  GetRows(&rowCount);
+
+  PRInt32 colCount = 0;
+  GetColumns(&colCount);
+
+  NS_ENSURE_ARG(aIndex < rowCount * colCount);
+
+  *aRow = aIndex / colCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetColumnExtentAt(PRInt32 aRow, PRInt32 aColumn,
+                                        PRInt32 *aExtentCount)
+{
+  NS_ENSURE_ARG_POINTER(aExtentCount);
+  *aExtentCount = 0;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(IsValidRowNColumn(aRow, aColumn));
+
+  *aExtentCount = 1;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetRowExtentAt(PRInt32 aRow, PRInt32 aColumn,
+                                      PRInt32 *aExtentCount)
+{
+  NS_ENSURE_ARG_POINTER(aExtentCount);
+  *aExtentCount = 0;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(IsValidRowNColumn(aRow, aColumn));
+
+  *aExtentCount = 1;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetColumnDescription(PRInt32 aColumn,
+                                           nsAString& aDescription)
+{
+  aDescription.Truncate();
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(IsValidColumn(aColumn));
+
+  // XXX: not implemented
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetRowDescription(PRInt32 aRow, nsAString& aDescription)
+{
+  aDescription.Truncate();
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(IsValidRow(aRow));
+
+  // XXX: not implemented
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::IsColumnSelected(PRInt32 aColumn, PRBool *aIsSelected)
+{
+  NS_ENSURE_ARG_POINTER(aIsSelected);
+  *aIsSelected = PR_FALSE;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(IsValidColumn(aColumn));
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::IsRowSelected(PRInt32 aRow, PRBool *aIsSelected)
+{
+  NS_ENSURE_ARG_POINTER(aIsSelected);
+  *aIsSelected = PR_FALSE;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(IsValidRow(aRow));
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::IsCellSelected(PRInt32 aRow, PRInt32 aColumn,
+                                     PRBool *aIsSelected)
+{
+  NS_ENSURE_ARG_POINTER(aIsSelected);
+  *aIsSelected = PR_FALSE;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(IsValidRowNColumn(aRow, aColumn));
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetSelectedCellsCount(PRUint32* aCount)
+{
+  NS_ENSURE_ARG_POINTER(aCount);
+  *aCount = 0;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetSelectedColumnsCount(PRUint32* aCount)
+{
+  NS_ENSURE_ARG_POINTER(aCount);
+  *aCount = 0;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetSelectedRowsCount(PRUint32* aCount)
+{
+  NS_ENSURE_ARG_POINTER(aCount);
+  *aCount = 0;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetSelectedCells(PRUint32 *aCellsCount, PRInt32 **aCells)
+{
+  NS_ENSURE_ARG_POINTER(aCellsCount);
+  *aCellsCount = 0;
+  NS_ENSURE_ARG_POINTER(aCells);
+  *aCells = nsnull;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetSelectedColumns(PRUint32 *aColumnsCount,
+                                         PRInt32 **aColumns)
+{
+  NS_ENSURE_ARG_POINTER(aColumnsCount);
+  *aColumnsCount = 0;
+  NS_ENSURE_ARG_POINTER(aColumns);
+  *aColumns = nsnull;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::GetSelectedRows(PRUint32 *aRowsCount, PRInt32 **aRows)
+{
+  NS_ENSURE_ARG_POINTER(aRowsCount);
+  *aRowsCount = 0;
+  NS_ENSURE_ARG_POINTER(aRows);
+  *aRows = nsnull;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::SelectRow(PRInt32 aRow)
+{
+  NS_ENSURE_ARG(IsValidRow(aRow));
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::SelectColumn(PRInt32 aColumn)
+{
+  NS_ENSURE_ARG(IsValidColumn(aColumn));
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::UnselectRow(PRInt32 aRow)
+{
+  NS_ENSURE_ARG(IsValidRow(aRow));
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::UnselectColumn(PRInt32 aColumn)
+{
+  NS_ENSURE_ARG(IsValidColumn(aColumn));
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  // XXX: should we rely on aria-selected or DOM selection?
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsARIAGridAccessible::IsProbablyForLayout(PRBool *aIsProbablyForLayout)
+{
+  NS_ENSURE_ARG_POINTER(aIsProbablyForLayout);
+  *aIsProbablyForLayout = PR_FALSE;
+
+  return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Protected
+
+PRBool
+nsARIAGridAccessible::IsValidRow(PRInt32 aRow)
+{
+  if (aRow < 0)
+    return PR_FALSE;
+  
+  PRInt32 rowCount = 0;
+  GetRows(&rowCount);
+  return aRow < rowCount;
+}
+
+PRBool
+nsARIAGridAccessible::IsValidColumn(PRInt32 aColumn)
+{
+  if (aColumn < 0)
+    return PR_FALSE;
+
+  PRInt32 colCount = 0;
+  GetColumns(&colCount);
+  return aColumn < colCount;
+}
+
+PRBool
+nsARIAGridAccessible::IsValidRowNColumn(PRInt32 aRow, PRInt32 aColumn)
+{
+  if (aRow < 0 || aColumn < 0)
+    return PR_FALSE;
+  
+  PRInt32 rowCount = 0;
+  GetRows(&rowCount);
+  if (aRow >= rowCount)
+    return PR_FALSE;
+
+  PRInt32 colCount = 0;
+  GetColumns(&colCount);
+  return aColumn < colCount;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// nsARIAGridCellAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Constructor
+
+nsARIAGridCellAccessible::nsARIAGridCellAccessible(nsIDOMNode* aDomNode,
+                                                   nsIWeakReference* aShell) :
+  nsHyperTextAccessibleWrap(aDomNode, aShell)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsARIAGridCellAccessible,
+                             nsHyperTextAccessible)
+
+////////////////////////////////////////////////////////////////////////////////
+// nsAccessible
+
+nsresult
+nsARIAGridCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
+{
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+  
+  nsresult rv = nsHyperTextAccessibleWrap::GetAttributesInternal(aAttributes);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Expose "table-cell-index" attribute.
+
+  nsCOMPtr<nsIAccessible> thisRow;
+  GetParent(getter_AddRefs(thisRow));
+  if (nsAccUtils::Role(thisRow) != nsIAccessibleRole::ROLE_ROW)
+    return NS_OK;
+
+  PRInt32 colIdx = 0, colCount = 0;
+  nsCOMPtr<nsIAccessible> child, nextChild;
+  thisRow->GetFirstChild(getter_AddRefs(child));
+  while (child) {
+    if (child == this)
+      colIdx = colCount;
+
+    PRUint32 role = nsAccUtils::Role(child);
+    if (role == nsIAccessibleRole::ROLE_GRID_CELL ||
+        role == nsIAccessibleRole::ROLE_ROWHEADER ||
+        role == nsIAccessibleRole::ROLE_COLUMNHEADER)
+      colCount++;
+
+    child->GetNextSibling(getter_AddRefs(nextChild));
+    child.swap(nextChild);
+  }
+
+  nsCOMPtr<nsIAccessible> table;
+  thisRow->GetParent(getter_AddRefs(table));
+  if (nsAccUtils::Role(table) != nsIAccessibleRole::ROLE_TABLE &&
+      nsAccUtils::Role(table) != nsIAccessibleRole::ROLE_TREE_TABLE)
+    return NS_OK;
+
+  PRInt32 rowIdx = 0;
+  table->GetFirstChild(getter_AddRefs(child));
+  while (child && child != thisRow) {
+    if (nsAccUtils::Role(child) == nsIAccessibleRole::ROLE_ROW)
+      rowIdx++;
+
+    child->GetNextSibling(getter_AddRefs(nextChild));
+    child.swap(nextChild);
+  }
+
+  PRInt32 idx = rowIdx * colCount + colIdx;
+
+  nsAutoString stringIdx;
+  stringIdx.AppendInt(idx);
+  nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::tableCellIndex,
+                         stringIdx);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/nsARIAGridAccessible.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Alexander Surkov <surkov.alexander@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsARIAGridAccessible_h_
+#define nsARIAGridAccessible_h_
+
+#include "nsIAccessibleTable.h"
+
+#include "nsHyperTextAccessibleWrap.h"
+
+/**
+ * Accessible for ARIA grid and treegrid.
+ */
+class nsARIAGridAccessible : public nsAccessibleWrap,
+                             public nsIAccessibleTable
+{
+public:
+  nsARIAGridAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell);
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIAccessibleTable
+  NS_DECL_NSIACCESSIBLETABLE
+
+protected:
+  PRBool IsValidRow(PRInt32 aRow);
+  PRBool IsValidColumn(PRInt32 aColumn);
+  PRBool IsValidRowNColumn(PRInt32 aRow, PRInt32 aColumn);
+};
+
+/**
+ * Accessible for ARIA gridcell and rowheader/columnheader.
+ */
+class nsARIAGridCellAccessible : public nsHyperTextAccessibleWrap
+{
+public:
+  nsARIAGridCellAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell);
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsAccessible
+  virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
+};
+
+#endif
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 // NOTE: alphabetically ordered
 #include "nsAccessibilityAtoms.h"
 #include "nsAccessibilityService.h"
 #include "nsCoreUtils.h"
 #include "nsAccUtils.h"
+#include "nsARIAGridAccessible.h"
 #include "nsARIAMap.h"
 #include "nsIContentViewer.h"
 #include "nsCURILoader.h"
 #include "nsDocAccessible.h"
 #include "nsHTMLImageAccessibleWrap.h"
 #include "nsHTMLLinkAccessible.h"
 #include "nsHTMLSelectAccessible.h"
 #include "nsHTMLTableAccessibleWrap.h"
@@ -1488,41 +1489,28 @@ NS_IMETHODIMP nsAccessibilityService::Ge
   if (roleMapEntry && !nsCRT::strcmp(roleMapEntry->roleString, "presentation") &&
       !content->IsFocusable()) { // For presentation only
     // Only create accessible for role of "presentation" if it is focusable --
     // in that case we need an accessible in case it gets focused, we
     // don't want focus ever to be 'lost'
     return NS_OK;
   }
 
-  // Elements may implement nsIAccessibleProvider via XBL. This allows them to
-  // say what kind of accessible to create.
-  nsresult rv = GetAccessibleByType(aNode, getter_AddRefs(newAcc));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!newAcc && !isHTML) {
-    if (content->GetNameSpaceID() == kNameSpaceID_SVG &&
-             content->Tag() == nsAccessibilityAtoms::svg) {
-      newAcc = new nsEnumRoleAccessible(aNode, aWeakShell,
-                                        nsIAccessibleRole::ROLE_DIAGRAM);
-    }
-    else if (content->GetNameSpaceID() == kNameSpaceID_MathML &&
-             content->Tag() == nsAccessibilityAtoms::math) {
-      newAcc = new nsEnumRoleAccessible(aNode, aWeakShell,
-                                        nsIAccessibleRole::ROLE_EQUATION);
-    }
-  } else if (!newAcc) {  // HTML accessibles
+  if (!newAcc && isHTML) {  // HTML accessibles
     PRBool tryTagNameOrFrame = PR_TRUE;
 
     nsIAtom *frameType = frame->GetType();
-    if (!roleMapEntry &&
-        (frameType == nsAccessibilityAtoms::tableCaptionFrame ||
-         frameType == nsAccessibilityAtoms::tableCellFrame ||
-         frameType == nsAccessibilityAtoms::tableRowGroupFrame ||
-         frameType == nsAccessibilityAtoms::tableRowFrame)) {
+
+    PRBool partOfHTMLTable =
+      frameType == nsAccessibilityAtoms::tableCaptionFrame ||
+      frameType == nsAccessibilityAtoms::tableCellFrame ||
+      frameType == nsAccessibilityAtoms::tableRowGroupFrame ||
+      frameType == nsAccessibilityAtoms::tableRowFrame;
+
+    if (!roleMapEntry && partOfHTMLTable) {
       // Table-related frames don't get table-related roles
       // unless they are inside a table, but they may still get generic
       // accessibles
       nsIContent *tableContent = content;
       while ((tableContent = tableContent->GetParent()) != nsnull) {
         nsIFrame *tableFrame = aPresShell->GetPrimaryFrameFor(tableContent);
         if (!tableFrame)
           continue;
@@ -1557,24 +1545,37 @@ NS_IMETHODIMP nsAccessibilityService::Ge
           break;
         }
       }
 
       if (!tableContent)
         tryTagNameOrFrame = PR_FALSE;
     }
 
-    if (tryTagNameOrFrame) {
+    if (roleMapEntry && (!partOfHTMLTable || !tryTagNameOrFrame ||
+        frameType != nsAccessibilityAtoms::tableOuterFrame)) {
+      // Try to create ARIA grid/treegrid accessibles.
+      if (roleMapEntry->role == nsIAccessibleRole::ROLE_TABLE ||
+          roleMapEntry->role == nsIAccessibleRole::ROLE_TREE_TABLE) {
+        newAcc = new nsARIAGridAccessible(aNode, aWeakShell);
+      } else if (roleMapEntry->role == nsIAccessibleRole::ROLE_GRID_CELL ||
+                 roleMapEntry->role == nsIAccessibleRole::ROLE_ROWHEADER ||
+                 roleMapEntry->role == nsIAccessibleRole::ROLE_COLUMNHEADER) {
+        newAcc = new nsARIAGridCellAccessible(aNode, aWeakShell);
+      }
+    }
+
+    if (!newAcc && tryTagNameOrFrame) {
       // Prefer to use markup (mostly tag name, perhaps attributes) to
       // decide if and what kind of accessible to create.
       // The method creates accessibles for table related content too therefore
       // we do not call it if accessibles for table related content are
       // prevented above.
-      rv = CreateHTMLAccessibleByMarkup(frame, aWeakShell, aNode,
-                                        getter_AddRefs(newAcc));
+      nsresult rv = CreateHTMLAccessibleByMarkup(frame, aWeakShell, aNode,
+                                                 getter_AddRefs(newAcc));
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (!newAcc) {
         // Do not create accessible object subtrees for non-rendered table
         // captions. This could not be done in
         // nsTableCaptionFrame::GetAccessible() because the descendants of
         // the table caption would still be created. By setting
         // *aIsHidden = PR_TRUE we ensure that no descendant accessibles are
@@ -1587,16 +1588,37 @@ NS_IMETHODIMP nsAccessibilityService::Ge
           return NS_OK;
         }
         frame->GetAccessible(getter_AddRefs(newAcc)); // Try using frame to do it
       }
     }
   }
 
   if (!newAcc) {
+    // Elements may implement nsIAccessibleProvider via XBL. This allows them to
+    // say what kind of accessible to create.
+    nsresult rv = GetAccessibleByType(aNode, getter_AddRefs(newAcc));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (!newAcc) {
+    // Create generic accessibles for SVG and MathML nodes.
+    if (content->GetNameSpaceID() == kNameSpaceID_SVG &&
+        content->Tag() == nsAccessibilityAtoms::svg) {
+      newAcc = new nsEnumRoleAccessible(aNode, aWeakShell,
+                                        nsIAccessibleRole::ROLE_DIAGRAM);
+    }
+    else if (content->GetNameSpaceID() == kNameSpaceID_MathML &&
+             content->Tag() == nsAccessibilityAtoms::math) {
+      newAcc = new nsEnumRoleAccessible(aNode, aWeakShell,
+                                        nsIAccessibleRole::ROLE_EQUATION);
+    }
+  }
+
+  if (!newAcc) {
     GetAccessibleForDeckChildren(aNode, getter_AddRefs(newAcc));
   }
 
   // If no accessible, see if we need to create a generic accessible because
   // of some property that makes this object interesting
   // We don't do this for <body>, <html>, <window>, <dialog> etc. which 
   // correspond to the doc accessible and will be created in any case
   if (!newAcc && content->Tag() != nsAccessibilityAtoms::body && content->GetParent() && 
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -60,16 +60,17 @@ include $(topsrcdir)/config/rules.mk
 		name.js \
 		name.xbl \
 		namerules.xml \
  		nsIAccessible_selects.js \
 		nsIAccessible_states.js \
 		nsIAccessibleEditableText.js \
 		relations.js \
 		role.js \
+		table.js \
 		value.js \
 		test_accessnode_invalidation.html \
 		test_actions_aria.html \
 		test_actions_inputs.html \
 		test_actions.xul \
 		test_aria_activedescendant.html \
 		test_aria_role_article.html \
 		test_aria_role_equation.html \
@@ -89,17 +90,16 @@ include $(topsrcdir)/config/rules.mk
 		test_events_tree.xul \
 		test_groupattrs.xul \
 		test_groupattrs.html \
 		test_name.html \
 		test_name.xul \
 		test_name_button.html \
 		test_name_link.html \
 		test_name_markup.html \
-	$(warning test_table_indexes.html temporarily disabled) \
 		test_nsIAccessible_applicationAccessible.html \
 	$(warning test_nsIAccessible_comboboxes.xul temporarily disabled) \
  		test_nsIAccessible_selects.html \
 		test_nsIAccessible_focus.html \
 		test_nsIAccessibleDocument.html \
 		test_nsIAccessibleEditableText.html \
 		test_nsIAccessibleHyperLink.html \
 		test_nsIAccessibleHyperLink.xul \
@@ -118,16 +118,18 @@ include $(topsrcdir)/config/rules.mk
 		test_states_editablebody.html \
 		test_states_doc.html \
 		test_states_docarticle.html \
 		test_states_frames.html \
 		test_table_1.html \
 		test_table_2.html \
 		test_table_3.html \
 		test_table_4.html \
+  $(warning test_table_indexes.html temporarily disabled) \
+		test_table_indexes_ariagrid.html \
 		test_textattrs.html \
 		test_textboxes.html \
 		test_textboxes.xul \
 		test_value.xul \
 		testTextboxes.js \
 		treeview.js \
 		z_states_frame.html \
 		z_states_framearticle.html \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/table.js
@@ -0,0 +1,71 @@
+/**
+ * Test table indexes.
+ *
+ * @param  aIdentifier  [in] table accessible identifier
+ * @param  aLen         [in] cells count
+ * @param  aRowIdxes    [in] array of row indexes for each cell index
+ * @param  aColIdxes    [in] array of column indexes for each cell index
+ */
+function testTableIndexes(aIdentifier, aLen, aRowIdxes, aColIdxes)
+{
+  var tableAcc = getAccessible(aIdentifier, [nsIAccessibleTable]);
+  if (!tableAcc)
+    return;
+
+  var row, column, index;
+  var cellAcc;
+
+  var id = prettyName(aIdentifier);
+
+  for (var i = 0; i < aLen; i++) {
+    try {
+      row = tableAcc.getRowAtIndex(i);
+    } catch (e) {
+      ok(false, id + ": can't get row index for cell index " + i + ".");
+    }
+
+    try {
+      column = tableAcc.getColumnAtIndex(i);
+    } catch (e) {
+      ok(false, id + ": can't get column index for cell index " + i + ".");
+    }
+
+    try {
+      index = tableAcc.getIndexAt(aRowIdxes[i], aColIdxes[i]);
+    } catch (e) {
+      ok(false,
+         id + ": can't get cell index by row index " + aRowIdxes[i] +
+           " and column index: " + aColIdxes[i]  + ".");
+    }
+
+    is(row, aRowIdxes[i], id + ": row  for index " + i +" is nor correct");
+    is(column, aColIdxes[i],
+       id + ": column  for index " + i +" is not correct");
+    is(index, i,
+       id + ": row " + row + " /column " + column + " and index " + index + " aren't inconsistent.");
+
+    try {
+      cellAcc = null;
+      cellAcc = tableAcc.cellRefAt(row, column);
+    } catch (e) { }
+
+    ok(cellAcc,
+       id + ": Can't get cell accessible at row = " + row + ", column = " + column);
+
+    if (cellAcc) {
+      var attrs = cellAcc.attributes;
+      var strIdx = "";
+      try {
+        strIdx = attrs.getStringProperty("table-cell-index");
+      } catch (e) {
+        ok(false,
+           id + ": no cell index from object attributes on the cell accessible at index " + index + ".");
+      }
+
+      if (strIdx) {
+        is (parseInt(strIdx), index,
+            id + ": cell index from object attributes of cell accessible isn't corrent.");
+      }
+    }
+  }
+}
--- a/accessible/tests/mochitest/test_table_indexes.html
+++ b/accessible/tests/mochitest/test_table_indexes.html
@@ -1,105 +1,69 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=410052
 -->
 <head>
   <title>Table indexes chrome tests</title>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
-  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/common.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/table.js"></script>
 
   <script type="application/javascript">
-    const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
-    const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable;
-
-    var gAccService = null;
-
     function doTest()
     {
-      gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
-                    getService(nsIAccessibleRetrieval);
-
       //////////////////////////////////////////////////////////////////////////
       // table
       var tRow = new Array(0,0,0,1,1,1,2,2,3,3);
       var tCol = new Array(0,1,2,0,1,2,0,1,1,2);
 
-      testTable("table", 10, tRow, tCol);
+      testTableIndexes("table", 10, tRow, tCol);
 
       //////////////////////////////////////////////////////////////////////////
       // tableinsane1
       tRow = [0,0,0,1,1,1,2,2,3,3];
       tCol = [0,1,2,0,1,2,0,1,1,2];
 
-      testTable("tableinsane1", 10, tRow, tCol);
+      testTableIndexes("tableinsane1", 10, tRow, tCol);
 
       //////////////////////////////////////////////////////////////////////////
       // tableinsane2
       tRow = [0,0,0,1,1,1,2,2,3,3,4,4,4];
       tCol = [0,1,2,0,1,2,0,1,1,2,1,3,4];
 
-      testTable("tableinsane2", 13, tRow, tCol);
+      testTableIndexes("tableinsane2", 13, tRow, tCol);
 
       //////////////////////////////////////////////////////////////////////////
       // tableinsane4
       tRow = [0,0,0,1,1,1,2,2,3,4];
       tCol = [0,1,2,0,1,2,0,2,0,0];
 
-      testTable("tableinsane4", 10, tRow, tCol);
+      testTableIndexes("tableinsane4", 10, tRow, tCol);
 
       //////////////////////////////////////////////////////////////////////////
       // tableborder
       tRow = [0,0,0,1,1,1,2,2,3,3];
       tCol = [0,1,2,0,1,2,0,1,1,2];
 
-      testTable("tableborder", 10, tRow, tCol);
+      testTableIndexes("tableborder", 10, tRow, tCol);
 
       SimpleTest.finish();
     }
 
-    function testTable(aId, aLen, aRowIdxes, aColIdxes)
-    {
-      var table = document.getElementById(aId);
-      var tableAcc = gAccService.getAccessibleFor(table).
-        QueryInterface(nsIAccessibleTable);
-
-      var row, column, index;
-      var cellAcc;
-
-      for (var i = 0; i < aLen; i++) {
-        row = tableAcc.getRowAtIndex(i);
-        column = tableAcc.getColumnAtIndex(i);
-        index = tableAcc.getIndexAt(aRowIdxes[i], aColIdxes[i]);
-
-        is(row, aRowIdxes[i], aId + ": row  for index " + i +" is nor correct");
-        is(column, aColIdxes[i],
-           aId + ": column  for index " + i +"is nor correct");
-        is(index, i,
-           aId + ": row " + row + " /column " + column + " and index " + index + " aren't inconsistent.");
-
-        try {
-          cellAcc = null;
-          cellAcc = tableAcc.cellRefAt(row, column);
-        } catch (e) { }
-
-        ok(cellAcc,
-           aId + ": Can't get cell accessible at row = " + row + ", column = " + column);
-
-        if (cellAcc) {
-          var attrs = cellAcc.attributes;
-          is (parseInt(attrs.getStringProperty("table-cell-index")), index,
-              aId + ": cell index from object attributes of cell accessible isn't corrent.");
-        }
-      }
-    }
-
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=410052">Mozilla Bug 410052</a>
   <p id="display"></p>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/test_table_indexes_ariagrid.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Table indexes for ARIA grid tests</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/common.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/attributes.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/table.js"></script>
+
+  <script type="application/javascript">
+    function doTest()
+    {
+      //////////////////////////////////////////////////////////////////////////
+      // ARIA grid
+      var tRow = new Array(0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3);
+      var tCol = new Array(0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2);
+
+      testTableIndexes("grid", 12, tRow, tCol);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=386813"
+     title="support nsIAccessibleTable on ARIA grid/treegrid">Mozilla Bug 386813</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div role="grid" id="grid">
+    <div role="row">
+      <span role="columnheader">column1</span>
+      <span id="a" role="columnheader">column2</span>
+      <span role="columnheader">column3</span>
+    </div>
+    <div role="row">
+      <span role="rowheader">row1</span>
+      <span role="gridcell">cell1</span>
+      <span role="gridcell">cell2</span>
+    </div>
+    <div role="row">
+      <span role="rowheader">row2</span>
+      <span role="gridcell">cell3</span>
+      <span role="gridcell">cell4</span>
+    </div>
+    <div role="row">
+      <span role="rowheader">row3</span>
+      <span role="gridcell">cell5</span>
+      <span role="gridcell">cell6</span>
+    </div>
+  </div>
+
+</body>
+</html>