Bug 761853 - ARIA grid with rowgroup breaks table row/col counting and indices, r=tbsaunde
authorAlexander Surkov <surkov.alexander@gmail.com>
Tue, 28 Aug 2012 22:13:59 +0900
changeset 105688 a1cd7986d2958b610629373875d7992fbfca23ca
parent 105687 468a7e985d82f4aec1fcd40ff91cd66300cf04c9
child 105689 c9e473186b60d28924429b5734e1163c29c58bb5
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewerstbsaunde
bugs761853
milestone18.0a1
Bug 761853 - ARIA grid with rowgroup breaks table row/col counting and indices, r=tbsaunde
accessible/src/base/AccCollector.cpp
accessible/src/base/AccCollector.h
accessible/src/base/AccFilters.cpp
accessible/src/base/AccFilters.h
accessible/src/base/AccIterator.cpp
accessible/src/base/AccIterator.h
accessible/src/base/Makefile.in
accessible/src/base/Relation.h
accessible/src/base/filters.cpp
accessible/src/base/filters.h
accessible/src/base/nsAccessNode.h
accessible/src/generic/ARIAGridAccessible-inl.h
accessible/src/generic/ARIAGridAccessible.cpp
accessible/src/generic/ARIAGridAccessible.h
accessible/src/generic/Accessible.cpp
accessible/src/generic/Accessible.h
accessible/src/generic/DocAccessible.h
accessible/tests/mochitest/table/test_indexes_ariagrid.html
--- a/accessible/src/base/AccCollector.cpp
+++ b/accessible/src/base/AccCollector.cpp
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AccCollector.h"
 
 #include "Accessible.h"
 
+using namespace mozilla::a11y;
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccCollector
 ////////////////////////////////////////////////////////////////////////////////
 
 AccCollector::
   AccCollector(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc) :
   mFilterFunc(aFilterFunc), mRoot(aRoot), mRootChildIdx(0)
 {
@@ -51,34 +53,34 @@ AccCollector::GetIndexAt(Accessible* aAc
 // nsAccCollector protected
 
 Accessible*
 AccCollector::EnsureNGetObject(uint32_t aIndex)
 {
   uint32_t childCount = mRoot->ChildCount();
   while (mRootChildIdx < childCount) {
     Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
-    if (!mFilterFunc(child))
+    if (!(mFilterFunc(child) & filters::eMatch))
       continue;
 
     AppendObject(child);
     if (mObjects.Length() - 1 == aIndex)
       return mObjects[aIndex];
   }
 
   return nullptr;
 }
 
 int32_t
 AccCollector::EnsureNGetIndex(Accessible* aAccessible)
 {
   uint32_t childCount = mRoot->ChildCount();
   while (mRootChildIdx < childCount) {
     Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
-    if (!mFilterFunc(child))
+    if (!(mFilterFunc(child) & filters::eMatch))
       continue;
 
     AppendObject(child);
     if (child == aAccessible)
       return mObjects.Length() - 1;
   }
 
   return -1;
@@ -98,17 +100,18 @@ int32_t
 EmbeddedObjCollector::GetIndexAt(Accessible* aAccessible)
 {
   if (aAccessible->mParent != mRoot)
     return -1;
 
   if (aAccessible->mIndexOfEmbeddedChild != -1)
     return aAccessible->mIndexOfEmbeddedChild;
 
-  return mFilterFunc(aAccessible) ? EnsureNGetIndex(aAccessible) : -1;
+  return mFilterFunc(aAccessible) & filters::eMatch ?
+    EnsureNGetIndex(aAccessible) : -1;
 }
 
 void
 EmbeddedObjCollector::AppendObject(Accessible* aAccessible)
 {
   aAccessible->mIndexOfEmbeddedChild = mObjects.Length();
   mObjects.AppendElement(aAccessible);
 }
--- a/accessible/src/base/AccCollector.h
+++ b/accessible/src/base/AccCollector.h
@@ -1,19 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef AccCollector_h_
-#define AccCollector_h_
+#ifndef mozilla_a11y_AccCollector_h__
+#define mozilla_a11y_AccCollector_h__
+
+#include "AccFilters.h"
 
-#include "filters.h"
+#include "nsTArray.h"
 
-#include "nscore.h"
-#include "nsTArray.h"
+class Accessible;
+
+namespace mozilla {
+namespace a11y {
 
 /**
  * Collect accessible children complying with filter function. Provides quick
  * access to accessible by index.
  */
 class AccCollector
 {
 public:
@@ -77,12 +81,15 @@ public:
 
 protected:
   // Make sure it's used by Accessible class only.
   EmbeddedObjCollector(Accessible* aRoot) :
     AccCollector(aRoot, filters::GetEmbeddedObject) { }
 
   virtual void AppendObject(Accessible* aAccessible);
 
-  friend class Accessible;
+  friend class ::Accessible;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
rename from accessible/src/base/filters.cpp
rename to accessible/src/base/AccFilters.cpp
--- a/accessible/src/base/filters.cpp
+++ b/accessible/src/base/AccFilters.cpp
@@ -1,44 +1,60 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "filters.h"
+#include "AccFilters.h"
 
 #include "Accessible-inl.h"
 #include "nsAccUtils.h"
 #include "Role.h"
 #include "States.h"
 
 using namespace mozilla::a11y;
+using namespace mozilla::a11y::filters;
 
-bool
+uint32_t
 filters::GetSelected(Accessible* aAccessible)
 {
-  return aAccessible->State() & states::SELECTED;
+  if (aAccessible->State() & states::SELECTED)
+    return eMatch | eSkipSubtree;
+
+  return eSkip;
 }
 
-bool
+uint32_t
 filters::GetSelectable(Accessible* aAccessible)
 {
-  return aAccessible->InteractiveState() & states::SELECTABLE;
+  if (aAccessible->InteractiveState() & states::SELECTABLE)
+    return eMatch | eSkipSubtree;
+
+  return eSkip;
 }
 
-bool
+uint32_t
 filters::GetRow(Accessible* aAccessible)
 {
-  return aAccessible->Role() == roles::ROW;
+  a11y::role role = aAccessible->Role();
+  if (role == roles::ROW)
+    return eMatch | eSkipSubtree;
+
+  // Look for rows inside rowgroup.
+  if (role == roles::SECTION)
+    return eSkip;
+
+  return eSkipSubtree;
 }
 
-bool
+uint32_t
 filters::GetCell(Accessible* aAccessible)
 {
-  roles::Role role = aAccessible->Role();
+  a11y::role role = aAccessible->Role();
   return role == roles::GRID_CELL || role == roles::ROWHEADER ||
-      role == roles::COLUMNHEADER;
+    role == roles::COLUMNHEADER ? eMatch : eSkipSubtree;
 }
 
-bool
+uint32_t
 filters::GetEmbeddedObject(Accessible* aAccessible)
 {
-  return nsAccUtils::IsEmbeddedObject(aAccessible);
+  return nsAccUtils::IsEmbeddedObject(aAccessible) ?
+    eMatch | eSkipSubtree : eSkipSubtree;
 }
rename from accessible/src/base/filters.h
rename to accessible/src/base/AccFilters.h
--- a/accessible/src/base/filters.h
+++ b/accessible/src/base/AccFilters.h
@@ -1,27 +1,55 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef a11yFilters_h_
-#define a11yFilters_h_
+#ifndef mozilla_a11y_Filters_h__
+#define mozilla_a11y_Filters_h__
+
+#include "mozilla/StandardInteger.h"
 
 class Accessible;
 
 /**
  * Predefined filters used for nsAccIterator and nsAccCollector.
  */
+namespace mozilla {
+namespace a11y {
 namespace filters {
 
-  /**
-   * Return true if the traversed accessible complies with filter.
-   */
-  typedef bool (*FilterFuncPtr) (Accessible*);
+enum EResult {
+  eSkip = 0,
+  eMatch = 1,
+  eSkipSubtree = 2
+};
+
+/**
+ * Return true if the traversed accessible complies with filter.
+ */
+typedef uint32_t (*FilterFuncPtr) (Accessible*);
+
+/**
+ * Matches selected/selectable accessibles in subtree.
+ */
+uint32_t GetSelected(Accessible* aAccessible);
+uint32_t GetSelectable(Accessible* aAccessible);
 
-  bool GetSelected(Accessible* aAccessible);
-  bool GetSelectable(Accessible* aAccessible);
-  bool GetRow(Accessible* aAccessible);
-  bool GetCell(Accessible* aAccessible);
-  bool GetEmbeddedObject(Accessible* aAccessible);
-}
+/**
+ * Matches row accessibles in subtree.
+ */
+uint32_t GetRow(Accessible* aAccessible);
+
+/**
+ * Matches cell accessibles in children.
+ */
+uint32_t GetCell(Accessible* aAccessible);
+
+/**
+ * Matches embedded objects in children.
+ */
+uint32_t GetEmbeddedObject(Accessible* aAccessible);
+
+} // namespace filters
+} // namespace a11y
+} // namespace mozilla
 
 #endif
--- a/accessible/src/base/AccIterator.cpp
+++ b/accessible/src/base/AccIterator.cpp
@@ -13,19 +13,18 @@
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccIterator
 ////////////////////////////////////////////////////////////////////////////////
 
 AccIterator::AccIterator(Accessible* aAccessible,
-                         filters::FilterFuncPtr aFilterFunc,
-                         IterationType aIterationType) :
-  mFilterFunc(aFilterFunc), mIsDeep(aIterationType != eFlatNav)
+                         filters::FilterFuncPtr aFilterFunc) :
+  mFilterFunc(aFilterFunc)
 {
   mState = new IteratorState(aAccessible);
 }
 
 AccIterator::~AccIterator()
 {
   while (mState) {
     IteratorState *tmp = mState;
@@ -35,29 +34,29 @@ AccIterator::~AccIterator()
 }
 
 Accessible*
 AccIterator::Next()
 {
   while (mState) {
     Accessible* child = mState->mParent->GetChildAt(mState->mIndex++);
     if (!child) {
-      IteratorState *tmp = mState;
+      IteratorState* tmp = mState;
       mState = mState->mParentState;
       delete tmp;
 
       continue;
     }
 
-    bool isComplying = mFilterFunc(child);
-    if (isComplying)
+    uint32_t result = mFilterFunc(child);
+    if (result & filters::eMatch)
       return child;
 
-    if (mIsDeep) {
-      IteratorState *childState = new IteratorState(child, mState);
+    if (!(result & filters::eSkipSubtree)) {
+      IteratorState* childState = new IteratorState(child, mState);
       mState = childState;
     }
   }
 
   return nullptr;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/accessible/src/base/AccIterator.h
+++ b/accessible/src/base/AccIterator.h
@@ -1,21 +1,23 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef nsAccIterator_h_
-#define nsAccIterator_h_
+#ifndef mozilla_a11y_AccIterator_h__
+#define mozilla_a11y_AccIterator_h__
 
+#include "DocAccessible.h"
+#include "AccFilters.h"
 #include "nsAccessibilityService.h"
-#include "filters.h"
-#include "nscore.h"
-#include "DocAccessible.h"
+
+namespace mozilla {
+namespace a11y {
 
 /**
  * AccIterable is a basic interface for iterators over accessibles.
  */
 class AccIterable
 {
 public:
   virtual ~AccIterable() { }
@@ -28,34 +30,17 @@ private:
 
 /**
  * Allows to iterate through accessible children or subtree complying with
  * filter function.
  */
 class AccIterator : public AccIterable
 {
 public:
-  /**
-   * Used to define iteration type.
-   */
-  enum IterationType {
-    /**
-     * Navigation happens through direct children.
-     */
-    eFlatNav,
-
-    /**
-     * Navigation through subtree excluding iterator root; if the accessible
-     * complies with filter, iterator ignores its children.
-     */
-    eTreeNav
-  };
-
-  AccIterator(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc,
-              IterationType aIterationType = eFlatNav);
+  AccIterator(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc);
   virtual ~AccIterator();
 
   /**
    * Return next accessible complying with filter function. Return the first
    * accessible for the first time.
    */
   virtual Accessible* Next();
 
@@ -65,22 +50,21 @@ private:
   AccIterator& operator =(const AccIterator&);
 
   struct IteratorState
   {
     IteratorState(Accessible* aParent, IteratorState* mParentState = nullptr);
 
     Accessible* mParent;
     int32_t mIndex;
-    IteratorState *mParentState;
+    IteratorState* mParentState;
   };
 
   filters::FilterFuncPtr mFilterFunc;
-  bool mIsDeep;
-  IteratorState *mState;
+  IteratorState* mState;
 };
 
 
 /**
  * Allows to traverse through related accessibles that are pointing to the given
  * dependent accessible by relation attribute.
  */
 class RelatedAccIterator : public AccIterable
@@ -277,9 +261,12 @@ public:
 private:
   SingleAccIterator();
   SingleAccIterator(const SingleAccIterator&);
   SingleAccIterator& operator = (const SingleAccIterator&);
 
   nsRefPtr<Accessible> mAcc;
 };
 
+} // namespace a11y
+} // namespace mozilla
+
 #endif
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -14,18 +14,18 @@ LIBRARY_NAME = accessibility_base_s
 LIBXUL_LIBRARY = 1
 
 
 CPPSRCS = \
   AccCollector.cpp \
   AccEvent.cpp \
   AccGroupInfo.cpp \
   AccIterator.cpp \
+  AccFilters.cpp \
   ARIAStateMap.cpp \
-  filters.cpp \
   FocusManager.cpp \
   NotificationController.cpp \
   nsAccDocManager.cpp \
   nsAccessNode.cpp \
   nsARIAMap.cpp \
   nsCoreUtils.cpp \
   nsAccUtils.cpp \
   nsAccessibilityService.cpp \
--- a/accessible/src/base/Relation.h
+++ b/accessible/src/base/Relation.h
@@ -14,36 +14,38 @@ namespace a11y {
 
 /**
  * This class is used to return Relation objects from functions.  A copy
  * constructor doesn't work here because we need to mutate the old relation to
  * have its nsAutoPtr forget what it points to.
  */
 struct RelationCopyHelper
 {
-  RelationCopyHelper(AccIterable* aFirstIter, AccIterable* aLastIter) :
+  RelationCopyHelper(mozilla::a11y::AccIterable* aFirstIter,
+                     mozilla::a11y::AccIterable* aLastIter) :
     mFirstIter(aFirstIter), mLastIter(aLastIter) { }
 
-  AccIterable* mFirstIter;
-  AccIterable* mLastIter;
+  mozilla::a11y::AccIterable* mFirstIter;
+  mozilla::a11y::AccIterable* mLastIter;
 };
 
 /**
  * A collection of relation targets of a certain type.  Targets are computed
  * lazily while enumerating.
  */
 class Relation
 {
 public:
   Relation() : mFirstIter(nullptr), mLastIter(nullptr) { }
 
   Relation(const RelationCopyHelper aRelation) :
     mFirstIter(aRelation.mFirstIter), mLastIter(aRelation.mLastIter) { }
 
-  Relation(AccIterable* aIter) : mFirstIter(aIter), mLastIter(aIter) { }
+  Relation(mozilla::a11y::AccIterable* aIter) :
+    mFirstIter(aIter), mLastIter(aIter) { }
 
   Relation(Accessible* aAcc) :
     mFirstIter(nullptr), mLastIter(nullptr)
     { AppendTarget(aAcc); }
 
   Relation(DocAccessible* aDocument, nsIContent* aContent) :
     mFirstIter(nullptr), mLastIter(nullptr)
     { AppendTarget(aDocument, aContent); }
@@ -62,33 +64,33 @@ public:
     return *this;
   }
 
   operator RelationCopyHelper()
   {
     return RelationCopyHelper(mFirstIter.forget(), mLastIter);
   }
 
-  inline void AppendIter(AccIterable* aIter)
+  inline void AppendIter(mozilla::a11y::AccIterable* aIter)
   {
     if (mLastIter)
       mLastIter->mNextIter = aIter;
     else
       mFirstIter = aIter;
 
     mLastIter = aIter;
   }
 
   /**
    * Append the given accessible to the set of related accessibles.
    */
   inline void AppendTarget(Accessible* aAcc)
   {
     if (aAcc)
-      AppendIter(new SingleAccIterator(aAcc));
+      AppendIter(new mozilla::a11y::SingleAccIterator(aAcc));
   }
 
   /**
    * Append the one accessible for this content node to the set of related
    * accessibles.
    */
   void AppendTarget(DocAccessible* aDocument, nsIContent* aContent)
   {
@@ -111,17 +113,17 @@ public:
       mLastIter = nullptr;
 
     return target;
   }
 
 private:
   Relation& operator = (const Relation&);
 
-  nsAutoPtr<AccIterable> mFirstIter;
-  AccIterable* mLastIter;
+  nsAutoPtr<mozilla::a11y::AccIterable> mFirstIter;
+  mozilla::a11y::AccIterable* mLastIter;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -98,17 +98,17 @@ public:
 
   /**
    * Return true if the accessible is primary accessible for the given DOM node.
    *
    * Accessible hierarchy may be complex for single DOM node, in this case
    * these accessibles share the same DOM node. The primary accessible "owns"
    * that DOM node in terms it gets stored in the accessible to node map.
    */
-  virtual bool IsPrimaryForNode() const;
+  virtual bool IsPrimaryForNode() const;//hello
 
   /**
    * Interface methods on nsIAccessible shared with ISimpleDOM.
    */
   void Language(nsAString& aLocale);
 
 protected:
   void LastRelease();
new file mode 100644
--- /dev/null
+++ b/accessible/src/generic/ARIAGridAccessible-inl.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_ARIAGridAccessible_inl_h__
+#define mozilla_a11y_ARIAGridAccessible_inl_h__
+
+#include "ARIAGridAccessible.h"
+
+#include "AccIterator.h"
+
+inline Accessible*
+mozilla::a11y::ARIAGridCellAccessible::TableFor(Accessible* aRow) const
+{
+  if (aRow) {
+    Accessible* table = aRow->Parent();
+    if (table) {
+      roles::Role tableRole = table->Role();
+      if (tableRole == roles::SECTION) { // if there's a rowgroup.
+        table = table->Parent();
+        if (table)
+          tableRole = table->Role();
+      }
+
+      return tableRole == roles::TABLE || tableRole == roles::TREE_TABLE ?
+        table : nullptr;
+    }
+  }
+
+  return nullptr;
+}
+
+inline int32_t
+mozilla::a11y::ARIAGridCellAccessible::RowIndexFor(Accessible* aRow) const
+{
+  Accessible* table = TableFor(aRow);
+  if (table) {
+    int32_t rowIdx = 0;
+    Accessible* row = nullptr;
+    AccIterator rowIter(table, filters::GetRow);
+    while ((row = rowIter.Next()) && row != aRow)
+      rowIdx++;
+
+    if (row)
+      return rowIdx;
+  }
+
+  return -1;
+}
+
+#endif
--- a/accessible/src/generic/ARIAGridAccessible.cpp
+++ b/accessible/src/generic/ARIAGridAccessible.cpp
@@ -1,14 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ARIAGridAccessible.h"
+#include "ARIAGridAccessible-inl.h"
 
 #include "Accessible-inl.h"
 #include "AccIterator.h"
 #include "nsAccUtils.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsIMutableArray.h"
@@ -546,29 +546,20 @@ NS_IMPL_ISUPPORTS_INHERITED1(ARIAGridCel
 // nsIAccessibleTableCell
 
 NS_IMETHODIMP
 ARIAGridCellAccessible::GetTable(nsIAccessibleTable** aTable)
 {
   NS_ENSURE_ARG_POINTER(aTable);
   *aTable = nullptr;
 
-  Accessible* thisRow = Parent();
-  if (!thisRow || thisRow->Role() != roles::ROW)
-    return NS_OK;
+  Accessible* table = TableFor(Row());
+  if (table)
+    CallQueryInterface(table, aTable);
 
-  Accessible* table = thisRow->Parent();
-  if (!table)
-    return NS_OK;
-
-  roles::Role tableRole = table->Role();
-  if (tableRole != roles::TABLE && tableRole != roles::TREE_TABLE)
-    return NS_OK;
-
-  CallQueryInterface(table, aTable);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ARIAGridCellAccessible::GetColumnIndex(int32_t* aColumnIndex)
 {
   NS_ENSURE_ARG_POINTER(aColumnIndex);
   *aColumnIndex = -1;
@@ -598,33 +589,17 @@ NS_IMETHODIMP
 ARIAGridCellAccessible::GetRowIndex(int32_t* aRowIndex)
 {
   NS_ENSURE_ARG_POINTER(aRowIndex);
   *aRowIndex = -1;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  Accessible* row = Parent();
-  if (!row)
-    return NS_OK;
-
-  Accessible* table = row->Parent();
-  if (!table)
-    return NS_OK;
-
-  *aRowIndex = 0;
-
-  int32_t indexInTable = row->IndexInParent();
-  for (int32_t idx = 0; idx < indexInTable; idx++) {
-    row = table->GetChildAt(idx);
-    if (row->Role() == roles::ROW)
-      (*aRowIndex)++;
-  }
-
+  *aRowIndex = RowIndexFor(Row());
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ARIAGridCellAccessible::GetColumnExtent(int32_t* aExtentCount)
 {
   NS_ENSURE_ARG_POINTER(aExtentCount);
   *aExtentCount = 0;
@@ -733,62 +708,42 @@ ARIAGridCellAccessible::ApplyARIAState(u
     *aState |= states::SELECTABLE | states::SELECTED;
 }
 
 nsresult
 ARIAGridCellAccessible::GetAttributesInternal(nsIPersistentProperties* aAttributes)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
-  
+
   nsresult rv = HyperTextAccessibleWrap::GetAttributesInternal(aAttributes);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Expose "table-cell-index" attribute.
-
-  Accessible* thisRow = Parent();
-  if (!thisRow || thisRow->Role() != roles::ROW)
+  Accessible* thisRow = Row();
+  if (!thisRow)
     return NS_OK;
 
   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)
       colCount++;
   }
 
-  Accessible* table = thisRow->Parent();
-  if (!table)
-    return NS_OK;
-
-  roles::Role tableRole = table->Role();
-  if (tableRole != roles::TABLE && tableRole != roles::TREE_TABLE)
-    return NS_OK;
-
-  int32_t rowIdx = 0;
-  childCount = table->ChildCount();
-  for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
-    Accessible* child = table->GetChildAt(childIdx);
-    if (child == thisRow)
-      break;
-
-    if (child->Role() == roles::ROW)
-      rowIdx++;
-  }
-
-  int32_t idx = rowIdx * colCount + colIdx;
+  int32_t rowIdx = RowIndexFor(thisRow);
 
   nsAutoString stringIdx;
-  stringIdx.AppendInt(idx);
+  stringIdx.AppendInt(rowIdx * colCount + colIdx);
   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::tableCellIndex,
                          stringIdx);
 
   return NS_OK;
 }
 
 void
 ARIAGridCellAccessible::Shutdown()
--- a/accessible/src/generic/ARIAGridAccessible.h
+++ b/accessible/src/generic/ARIAGridAccessible.h
@@ -55,16 +55,17 @@ public:
   virtual void SelectedColIndices(nsTArray<uint32_t>* aCols);
   virtual void SelectedRowIndices(nsTArray<uint32_t>* aRows);
   virtual void SelectCol(uint32_t aColIdx);
   virtual void SelectRow(uint32_t aRowIdx);
   virtual void UnselectCol(uint32_t aColIdx);
   virtual void UnselectRow(uint32_t aRowIdx);
 
 protected:
+
   /**
    * Return true if the given row index is valid.
    */
   bool IsValidRow(int32_t aRow);
 
   /**
    * Retrn true if the given column index is valid.
    */
@@ -109,14 +110,35 @@ public:
 
   // nsIAccessibleTableCell
   NS_DECL_OR_FORWARD_NSIACCESSIBLETABLECELL_WITH_XPCACCESSIBLETABLECELL
 
   // Accessible
   virtual void Shutdown();
   virtual void ApplyARIAState(uint64_t* aState) const;
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
+
+protected:
+
+  /**
+   * Return a containing row.
+   */
+  Accessible* Row() const
+  {
+    Accessible* row = Parent();
+    return row && row->Role() == roles::ROW ? row : nullptr;
+  }
+
+  /**
+   * Return a table for the given row.
+   */
+  Accessible* TableFor(Accessible* aRow) const;
+
+  /**
+   * Return index of the given row.
+   */
+  int32_t RowIndexFor(Accessible* aRow) const;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -2723,115 +2723,115 @@ Accessible::IsSelect()
 
 already_AddRefed<nsIArray>
 Accessible::SelectedItems()
 {
   nsCOMPtr<nsIMutableArray> selectedItems = do_CreateInstance(NS_ARRAY_CONTRACTID);
   if (!selectedItems)
     return nullptr;
 
-  AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelected);
   nsIAccessible* selected = nullptr;
   while ((selected = iter.Next()))
     selectedItems->AppendElement(selected, false);
 
   nsIMutableArray* items = nullptr;
   selectedItems.forget(&items);
   return items;
 }
 
 uint32_t
 Accessible::SelectedItemCount()
 {
   uint32_t count = 0;
-  AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelected);
   Accessible* selected = nullptr;
   while ((selected = iter.Next()))
     ++count;
 
   return count;
 }
 
 Accessible*
 Accessible::GetSelectedItem(uint32_t aIndex)
 {
-  AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelected);
   Accessible* selected = nullptr;
 
   uint32_t index = 0;
   while ((selected = iter.Next()) && index < aIndex)
     index++;
 
   return selected;
 }
 
 bool
 Accessible::IsItemSelected(uint32_t aIndex)
 {
   uint32_t index = 0;
-  AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelectable);
   Accessible* selected = nullptr;
   while ((selected = iter.Next()) && index < aIndex)
     index++;
 
   return selected &&
     selected->State() & states::SELECTED;
 }
 
 bool
 Accessible::AddItemToSelection(uint32_t aIndex)
 {
   uint32_t index = 0;
-  AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelectable);
   Accessible* selected = nullptr;
   while ((selected = iter.Next()) && index < aIndex)
     index++;
 
   if (selected)
     selected->SetSelected(true);
 
   return static_cast<bool>(selected);
 }
 
 bool
 Accessible::RemoveItemFromSelection(uint32_t aIndex)
 {
   uint32_t index = 0;
-  AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelectable);
   Accessible* selected = nullptr;
   while ((selected = iter.Next()) && index < aIndex)
     index++;
 
   if (selected)
     selected->SetSelected(false);
 
   return static_cast<bool>(selected);
 }
 
 bool
 Accessible::SelectAll()
 {
   bool success = false;
   Accessible* selectable = nullptr;
 
-  AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelectable);
   while((selectable = iter.Next())) {
     success = true;
     selectable->SetSelected(true);
   }
   return success;
 }
 
 bool
 Accessible::UnselectAll()
 {
   bool success = false;
   Accessible* selected = nullptr;
 
-  AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelected);
   while ((selected = iter.Next())) {
     success = true;
     selected->SetSelected(false);
   }
   return success;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -18,25 +18,25 @@
 #include "nsIContent.h"
 
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "nsRefPtrHashtable.h"
 
 class AccEvent;
 class AccGroupInfo;
-class EmbeddedObjCollector;
 class KeyBinding;
 class Accessible;
 class HyperTextAccessible;
 struct nsRoleMapEntry;
 
 namespace mozilla {
 namespace a11y {
 
+class EmbeddedObjCollector;
 class HTMLImageMapAccessible;
 class HTMLLIAccessible;
 class ImageAccessible;
 class Relation;
 class TableAccessible;
 class TextLeafAccessible;
 class XULTreeAccessible;
 
@@ -870,19 +870,19 @@ protected:
   int32_t mIndexInParent;
 
   static const uint32_t kChildrenFlagsMask =
     eChildrenUninitialized | eMixedChildren | eEmbeddedChildren;
 
   uint32_t mFlags;
   friend class DocAccessible;
 
-  nsAutoPtr<EmbeddedObjCollector> mEmbeddedObjCollector;
+  nsAutoPtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
   int32_t mIndexOfEmbeddedChild;
-  friend class EmbeddedObjCollector;
+  friend class mozilla::a11y::EmbeddedObjCollector;
 
   nsAutoPtr<AccGroupInfo> mGroupInfo;
   friend class AccGroupInfo;
 
   /**
    * Non-null indicates author-supplied role; possibly state & value as well
    */
   nsRoleMapEntry* mRoleMapEntry;
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -28,16 +28,24 @@ template<class Class, class Arg>
 class TNotification;
 class NotificationController;
 
 class nsIScrollableView;
 class nsAccessiblePivot;
 
 const uint32_t kDefaultCacheSize = 256;
 
+namespace mozilla {
+namespace a11y {
+
+class RelatedAccIterator;
+
+} // namespace a11y
+} // namespace mozilla
+
 class DocAccessible : public HyperTextAccessibleWrap,
                       public nsIAccessibleDocument,
                       public nsIDocumentObserver,
                       public nsIObserver,
                       public nsIScrollPositionListener,
                       public nsSupportsWeakReference,
                       public nsIAccessibleCursorable,
                       public nsIAccessiblePivotObserver
@@ -562,17 +570,17 @@ protected:
   };
 
   /**
    * The cache of IDs pointed by relation attributes.
    */
   typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
   nsClassHashtable<nsStringHashKey, AttrRelProviderArray> mDependentIDsHash;
 
-  friend class RelatedAccIterator;
+  friend class mozilla::a11y::RelatedAccIterator;
 
   /**
    * Used for our caching algorithm. We store the list of nodes that should be
    * invalidated.
    *
    * @see ProcessInvalidationList
    */
   nsTArray<nsIContent*> mInvalidationList;
--- a/accessible/tests/mochitest/table/test_indexes_ariagrid.html
+++ b/accessible/tests/mochitest/table/test_indexes_ariagrid.html
@@ -23,16 +23,24 @@
       var idxes = [
         [0, 1, 2],
         [3, 4, 5],
         [6, 7, 8],
         [9, 10, 11]
       ];
       testTableIndexes("grid", idxes);
 
+      idxes = [
+        [0, 1, 2],
+        [3, 4, 5],
+        [6, 7, 8],
+        [9, 10, 11]
+      ];
+      testTableIndexes("grid-rowgroups", idxes);
+
       //////////////////////////////////////////////////////////////////////////
       // a bit crazy ARIA grid
       idxes = [
         [0, 1],
         [2, 3]
       ];
       testTableIndexes("grid2", idxes);
 
@@ -46,16 +54,19 @@
 <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>
   <a target="_blank"
      title="nsHTMLTableCellAccessible is used in dojo's crazy ARIA grid"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=513848">Mozilla Bug 513848</a>
+  <a target="_blank"
+     title="ARIA grid with rowgroup breaks table row/col counting and indices"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=761853">Mozilla Bug 761853</a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div role="grid" id="grid">
     <div role="row">
@@ -75,16 +86,41 @@
     </div>
     <div role="row">
       <span role="rowheader">row3</span>
       <span role="gridcell">cell5</span>
       <span role="gridcell">cell6</span>
     </div>
   </div>
 
+  <div role="grid" id="grid-rowgroups">
+    <div role="row">
+      <span role="columnheader">grid-rowgroups-col1</span>
+      <span role="columnheader">grid-rowgroups-col2</span>
+      <span role="columnheader">grid-rowgroups-col3</span>
+    </div>
+    <div role="rowgroup">
+      <div role="row">
+        <span role="rowheader">grid-rowgroups-row1</span>
+        <span role="gridcell">grid-rowgroups-cell1</span>
+        <span role="gridcell">grid-rowgroups-cell2</span>
+      </div>
+      <div role="row">
+        <span role="rowheader">grid-rowgroups-row2</span>
+        <span role="gridcell">grid-rowgroups-cell3</span>
+        <span role="gridcell">grid-rowgroups-cell4</span>
+      </div>
+    </div>
+    <div role="row">
+      <span role="rowheader">grid-rowgroups-row3</span>
+      <span role="gridcell">grid-rowgroups-cell5</span>
+      <span role="gridcell">grid-rowgroups-cell6</span>
+    </div>
+  </div>
+
   <div role="grid" id="grid2">
     <div role="row">
       <table role="presentation">
         <tr>
           <td role="columnheader">header1</td>
           <td role="columnheader">header2</td>
         </tr>
       </table>