Bug 572394 - cache links within hypertext accessible, r=davidb, marcoz, sr=neil
authorAlexander Surkov <surkov.alexander@gmail.com>
Mon, 21 Jun 2010 22:08:27 +0900
changeset 43888 abb7ee444c3186da1e4c5cbe4056462ee2ad6e6f
parent 43887 76c076305bdfc1d83a2d92e349944001d77b59ca
child 43889 b24c5978535967a8d93c43106abab4c4209f0f1e
child 47008 79ae7224e6273199d0db9638043e531942bc10fd
child 47013 e0ca330e7222ac5b482986950c107b69a53559f2
push id13950
push usersurkov.alexander@gmail.com
push dateMon, 21 Jun 2010 13:09:06 +0000
treeherdermozilla-central@abb7ee444c31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdavidb, marcoz, neil
bugs572394
milestone1.9.3a6pre
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 572394 - cache links within hypertext accessible, r=davidb, marcoz, sr=neil
accessible/public/nsIAccessibleHyperText.idl
accessible/src/atk/nsAccessibleWrap.cpp
accessible/src/atk/nsMaiInterfaceHypertext.cpp
accessible/src/base/AccCollector.cpp
accessible/src/base/AccCollector.h
accessible/src/base/AccIterator.cpp
accessible/src/base/AccIterator.h
accessible/src/base/Makefile.in
accessible/src/base/filters.cpp
accessible/src/base/filters.h
accessible/src/base/nsARIAGridAccessible.cpp
accessible/src/base/nsAccIterator.cpp
accessible/src/base/nsAccIterator.h
accessible/src/base/nsAccessNode.h
accessible/src/base/nsAccessible.cpp
accessible/src/base/nsAccessible.h
accessible/src/html/nsHyperTextAccessible.cpp
accessible/src/html/nsHyperTextAccessible.h
accessible/src/msaa/CAccessibleHypertext.cpp
accessible/src/msaa/CAccessibleHypertext.h
accessible/tests/mochitest/test_nsIAccessibleHyperText.html
--- a/accessible/public/nsIAccessibleHyperText.idl
+++ b/accessible/public/nsIAccessibleHyperText.idl
@@ -39,41 +39,51 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIAccessibleHyperLink.idl"
 
 /**
  * A cross-platform interface that deals with text which contains hyperlinks.
+ * Each link is an embedded object representing exactly 1 character within
+ * the hypertext.
+ *
+ * Current implementation assumes every embedded object is a link.
  */
 
-[scriptable, uuid(d56bd454-8ff3-4edc-b266-baeada00267b)]
+[scriptable, uuid(b33684e2-090c-4e1d-a3d9-f4b46f4237b9)]
 interface nsIAccessibleHyperText : nsISupports
 {
   /**
-   * Returns the number of links contained within this hypertext object.
+   * Return the number of links contained within this hypertext object.
    */
   readonly attribute long linkCount;
 
-  /*
-   * Returns the link index at the given character index.
-   * Each link is an embedded object representing exactly 1 character within
-   * the hypertext.
+  /**
+   * Return link accessible at the given index.
    *
-   * @param charIndex  the 0-based character index.
+   * @param index  [in] 0-based index of the link that is to be retrieved
    *
-   * @returns long  0-based link's index.
-   * A return value of -1 indicates no link is present at that index.
+   * @return       link accessible or null if there is no link at that index
    */
-  long getLinkIndex(in long charIndex);
+  nsIAccessibleHyperLink getLinkAt(in long index);
 
   /**
-   * Retrieves the nsIAccessibleHyperLink object at the given link index.
+   * Return index of the given link.
    *
-   * @param linkIndex  0-based index of the link that is to be retrieved.
-   * This can be retrieved via getLinkIndex (see above).
+   * @param link  [in] link accessible the index is requested for
    *
-   * @returns nsIAccessibleHyperLink  Object representing the link properties
-   * or NS_ERROR_INVALID_ARG if there is no link at that index.
+   * @return      index of the given link or null if there's no link within
+   *                hypertext accessible
    */
-  nsIAccessibleHyperLink getLink(in long linkIndex);
+  long getLinkIndex(in nsIAccessibleHyperLink link);
+
+  /*
+   * Return link index at the given offset within hypertext accessible.
+   *
+   * @param offset  [in] the 0-based character index
+   *
+   * @return        0-based link's index or -1 if no link is present at that
+   *                  offset
+   */
+  long getLinkIndexAtOffset(in long offset);
 };
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -863,65 +863,42 @@ getParentCB(AtkObject *aAtkObj)
 gint
 getChildCountCB(AtkObject *aAtkObj)
 {
     nsAccessibleWrap *accWrap = GetAccessibleWrap(aAtkObj);
     if (!accWrap || nsAccUtils::MustPrune(accWrap)) {
         return 0;
     }
 
-    PRInt32 count = 0;
-    nsCOMPtr<nsIAccessibleHyperText> hyperText;
-    accWrap->QueryInterface(NS_GET_IID(nsIAccessibleHyperText), getter_AddRefs(hyperText));
-    if (hyperText) {
-        // If HyperText, then number of links matches number of children
-        hyperText->GetLinkCount(&count);
-    }
-    else {
-        nsCOMPtr<nsIAccessibleText> accText;
-        accWrap->QueryInterface(NS_GET_IID(nsIAccessibleText), getter_AddRefs(accText));
-        if (!accText) {    // Accessible text that is not a HyperText has no children
-            accWrap->GetChildCount(&count);
-        }
-    }
-    return count;
+    // Links within hypertext accessible play role of accessible children in
+    // ATK since every embedded object is a link and text accessibles are
+    // ignored.
+    nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(accWrap);
+    return hyperText ? hyperText->GetLinkCount() : accWrap->GetChildCount();
 }
 
 AtkObject *
 refChildCB(AtkObject *aAtkObj, gint aChildIndex)
 {
     // aChildIndex should not be less than zero
     if (aChildIndex < 0) {
       return nsnull;
     }
 
     nsAccessibleWrap *accWrap = GetAccessibleWrap(aAtkObj);
     if (!accWrap || nsAccUtils::MustPrune(accWrap)) {
         return nsnull;
     }
 
-    nsCOMPtr<nsIAccessible> accChild;
-    nsCOMPtr<nsIAccessibleHyperText> hyperText;
-    accWrap->QueryInterface(NS_GET_IID(nsIAccessibleHyperText), getter_AddRefs(hyperText));
-    if (hyperText) {
-        // If HyperText, then number of links matches number of children.
-        // XXX Fix this so it is not O(n^2) to walk through the children
-        // (bug 566328).
-        nsCOMPtr<nsIAccessibleHyperLink> hyperLink;
-        hyperText->GetLink(aChildIndex, getter_AddRefs(hyperLink));
-        accChild = do_QueryInterface(hyperLink);
-    }
-    else {
-        nsCOMPtr<nsIAccessibleText> accText;
-        accWrap->QueryInterface(NS_GET_IID(nsIAccessibleText), getter_AddRefs(accText));
-        if (!accText) {  // Accessible Text that is not HyperText has no children
-            accWrap->GetChildAt(aChildIndex, getter_AddRefs(accChild));
-        }
-    }
-
+    // Links within hypertext accessible play role of accessible children in
+    // ATK since every embedded object is a link and text accessibles are
+    // ignored.
+    nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(accWrap);
+    nsAccessible* accChild = hyperText ? hyperText->GetLinkAt(aChildIndex) :
+                                         accWrap->GetChildAt(aChildIndex);
     if (!accChild)
         return nsnull;
 
     AtkObject* childAtkObj = nsAccessibleWrap::GetAtkObject(accChild);
 
     NS_ASSERTION(childAtkObj, "Fail to get AtkObj");
     if (!childAtkObj)
         return nsnull;
@@ -942,31 +919,22 @@ getIndexInParentCB(AtkObject *aAtkObj)
         return -1;
     }
 
     nsAccessible *parent = accWrap->GetParent();
     if (!parent) {
         return -1; // No parent
     }
 
-    PRInt32 currentIndex = 0;
-
-    PRInt32 childCount = parent->GetChildCount();
-    for (PRInt32 idx = 0; idx < childCount; idx++) {
-      nsAccessible *sibling = parent->GetChildAt(idx);
-      if (sibling == accWrap) {
-          return currentIndex;
-      }
-
-      if (nsAccUtils::IsEmbeddedObject(sibling)) {
-          ++ currentIndex;
-      }
-    }
-
-    return -1;
+    // Links within hypertext accessible play role of accessible children in
+    // ATK since every embedded object is a link and text accessibles are
+    // ignored.
+    nsRefPtr<nsHyperTextAccessible> hyperTextParent(do_QueryObject(parent));
+    return hyperTextParent ?
+        hyperTextParent->GetLinkIndex(accWrap) : parent->GetIndexOf(accWrap);
 }
 
 static void TranslateStates(PRUint32 aState, const AtkStateMap *aStateMap,
                             AtkStateSet *aStateSet)
 {
   NS_ASSERTION(aStateSet, "Can't pass in null state set");
 
   // Convert every state to an entry in AtkStateMap
--- a/accessible/src/atk/nsMaiInterfaceHypertext.cpp
+++ b/accessible/src/atk/nsMaiInterfaceHypertext.cpp
@@ -35,17 +35,17 @@
  * 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 "nsMaiInterfaceHypertext.h"
 #include "nsIAccessibleDocument.h"
-#include "nsAccessNode.h"
+#include "nsHyperTextAccessible.h"
 
 void
 hypertextInterfaceInitCB(AtkHypertextIface *aIface)
 {
     g_return_if_fail(aIface != NULL);
 
     aIface->get_link = getLinkCB;
     aIface->get_n_links = getLinkCountCB;
@@ -54,65 +54,53 @@ hypertextInterfaceInitCB(AtkHypertextIfa
 
 AtkHyperlink *
 getLinkCB(AtkHypertext *aText, gint aLinkIndex)
 {
     nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
     if (!accWrap)
         return nsnull;
 
-    nsCOMPtr<nsIAccessibleHyperText> hyperText;
-    accWrap->QueryInterface(NS_GET_IID(nsIAccessibleHyperText),
-                            getter_AddRefs(hyperText));
+    nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(accWrap);
     NS_ENSURE_TRUE(hyperText, nsnull);
 
-    nsCOMPtr<nsIAccessibleHyperLink> hyperLink;
-    nsresult rv = hyperText->GetLink(aLinkIndex, getter_AddRefs(hyperLink));
-    if (NS_FAILED(rv) || !hyperLink)
+    nsAccessible* hyperLink = hyperText->GetLinkAt(aLinkIndex);
+    if (!hyperLink)
         return nsnull;
 
-    nsCOMPtr<nsIAccessible> hyperLinkAcc(do_QueryInterface(hyperLink));
-    AtkObject *hyperLinkAtkObj = nsAccessibleWrap::GetAtkObject(hyperLinkAcc);
+    AtkObject* hyperLinkAtkObj = nsAccessibleWrap::GetAtkObject(hyperLink);
     nsAccessibleWrap *accChild = GetAccessibleWrap(hyperLinkAtkObj);
     NS_ENSURE_TRUE(accChild, nsnull);
 
     MaiHyperlink *maiHyperlink = accChild->GetMaiHyperlink();
     NS_ENSURE_TRUE(maiHyperlink, nsnull);
     return maiHyperlink->GetAtkHyperlink();
 }
 
 gint
 getLinkCountCB(AtkHypertext *aText)
 {
     nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
     if (!accWrap)
         return -1;
 
-    nsCOMPtr<nsIAccessibleHyperText> hyperText;
-    accWrap->QueryInterface(NS_GET_IID(nsIAccessibleHyperText),
-                            getter_AddRefs(hyperText));
+    nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(accWrap);
     NS_ENSURE_TRUE(hyperText, -1);
 
-    PRInt32 count = -1;
-    nsresult rv = hyperText->GetLinkCount(&count);
-    NS_ENSURE_SUCCESS(rv, -1);
-
-    return count;
+    return hyperText->GetLinkCount();
 }
 
 gint
 getLinkIndexCB(AtkHypertext *aText, gint aCharIndex)
 {
     nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
     if (!accWrap)
         return -1;
 
-    nsCOMPtr<nsIAccessibleHyperText> hyperText;
-    accWrap->QueryInterface(NS_GET_IID(nsIAccessibleHyperText),
-                            getter_AddRefs(hyperText));
+    nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(accWrap);
     NS_ENSURE_TRUE(hyperText, -1);
 
     PRInt32 index = -1;
-    nsresult rv = hyperText->GetLinkIndex(aCharIndex, &index);
+    nsresult rv = hyperText->GetLinkIndexAtOffset(aCharIndex, &index);
     NS_ENSURE_SUCCESS(rv, -1);
 
     return index;
 }
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/AccCollector.cpp
@@ -0,0 +1,118 @@
+/* ***** 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 Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 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 "AccCollector.h"
+
+#include "nsAccessible.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// nsAccCollector
+////////////////////////////////////////////////////////////////////////////////
+
+AccCollector::
+  AccCollector(nsAccessible* aRoot, filters::FilterFuncPtr aFilterFunc) :
+  mFilterFunc(aFilterFunc), mRoot(aRoot), mRootChildIdx(0)
+{
+}
+
+AccCollector::~AccCollector()
+{
+}
+
+PRUint32
+AccCollector::Count()
+{
+  EnsureNGetIndex(nsnull);
+  return mObjects.Length();
+}
+
+nsAccessible*
+AccCollector::GetAccessibleAt(PRUint32 aIndex)
+{
+  nsAccessible *accessible = mObjects.SafeElementAt(aIndex, nsnull);
+  if (accessible)
+    return accessible;
+
+  return EnsureNGetObject(aIndex);
+}
+
+PRInt32
+AccCollector::GetIndexAt(nsAccessible *aAccessible)
+{
+  PRInt32 index = mObjects.IndexOf(aAccessible);
+  if (index != -1)
+    return index;
+
+  return EnsureNGetIndex(aAccessible);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsAccCollector protected
+
+nsAccessible*
+AccCollector::EnsureNGetObject(PRUint32 aIndex)
+{
+  PRInt32 childCount = mRoot->GetChildCount();
+  while (mRootChildIdx < childCount) {
+    nsAccessible* child = mRoot->GetChildAt(mRootChildIdx++);
+    if (!mFilterFunc(child))
+      continue;
+
+    mObjects.AppendElement(child);
+    if (mObjects.Length() - 1 == aIndex)
+      return mObjects[aIndex];
+  }
+
+  return nsnull;
+}
+
+PRInt32
+AccCollector::EnsureNGetIndex(nsAccessible* aAccessible)
+{
+  PRInt32 childCount = mRoot->GetChildCount();
+  while (mRootChildIdx < childCount) {
+    nsAccessible* child = mRoot->GetChildAt(mRootChildIdx++);
+    if (!mFilterFunc(child))
+      continue;
+
+    mObjects.AppendElement(child);
+    if (child == aAccessible)
+      return mObjects.Length() - 1;
+  }
+
+  return -1;
+}
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/AccCollector.h
@@ -0,0 +1,94 @@
+/* ***** 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 Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 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 AccCollector_h_
+#define AccCollector_h_
+
+#include "filters.h"
+
+#include "nscore.h"
+#include "nsTArray.h"
+
+/**
+ * Collect accessible children complying with filter function. Provides quick
+ * access to accessible by index.
+ */
+class AccCollector
+{
+public:
+  AccCollector(nsAccessible* aRoot, filters::FilterFuncPtr aFilterFunc);
+  virtual ~AccCollector();
+
+  /**
+   * Return accessible count within the collection.
+   */
+  PRUint32 Count();
+
+  /**
+   * Return an accessible from the collection at the given index.
+   */
+  nsAccessible* GetAccessibleAt(PRUint32 aIndex);
+
+  /**
+   * Return index of the given accessible within the collection.
+   */
+  PRInt32 GetIndexAt(nsAccessible* aAccessible);
+
+protected:
+  /**
+   * Ensure accessible at the given index is stored and return it.
+   */
+  nsAccessible* EnsureNGetObject(PRUint32 aIndex);
+
+  /**
+   * Ensure index for the given accessible is stored and return it.
+   */
+  PRInt32 EnsureNGetIndex(nsAccessible* aAccessible);
+
+  filters::FilterFuncPtr mFilterFunc;
+  nsAccessible* mRoot;
+  PRInt32 mRootChildIdx;
+
+  nsTArray<nsAccessible*> mObjects;
+
+private:
+  AccCollector();
+  AccCollector(const AccCollector&);
+  AccCollector& operator =(const AccCollector&);
+};
+
+#endif
rename from accessible/src/base/nsAccIterator.cpp
rename to accessible/src/base/AccIterator.cpp
--- a/accessible/src/base/nsAccIterator.cpp
+++ b/accessible/src/base/AccIterator.cpp
@@ -30,40 +30,42 @@
  * 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 "nsAccIterator.h"
+#include "AccIterator.h"
+
+#include "nsAccessible.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccIterator
 
-nsAccIterator::nsAccIterator(nsAccessible *aAccessible,
-                             AccIteratorFilterFuncPtr aFilterFunc,
-                             IterationType aIterationType) :
+AccIterator::AccIterator(nsAccessible *aAccessible,
+                         filters::FilterFuncPtr aFilterFunc,
+                         IterationType aIterationType) :
   mFilterFunc(aFilterFunc), mIsDeep(aIterationType != eFlatNav)
 {
   mState = new IteratorState(aAccessible);
 }
 
-nsAccIterator::~nsAccIterator()
+AccIterator::~AccIterator()
 {
   while (mState) {
     IteratorState *tmp = mState;
     mState = tmp->mParentState;
     delete tmp;
   }
 }
 
 nsAccessible*
-nsAccIterator::GetNext()
+AccIterator::GetNext()
 {
   while (mState) {
     nsAccessible *child = mState->mParent->GetChildAt(mState->mIndex++);
     if (!child) {
       IteratorState *tmp = mState;
       mState = mState->mParentState;
       delete tmp;
 
@@ -81,13 +83,13 @@ nsAccIterator::GetNext()
   }
 
   return nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccIterator::IteratorState
 
-nsAccIterator::IteratorState::IteratorState(nsAccessible *aParent,
-                                            IteratorState *mParentState) :
+AccIterator::IteratorState::IteratorState(nsAccessible *aParent,
+                                          IteratorState *mParentState) :
   mParent(aParent), mIndex(0), mParentState(mParentState)
 {
 }
rename from accessible/src/base/nsAccIterator.h
rename to accessible/src/base/AccIterator.h
--- a/accessible/src/base/nsAccIterator.h
+++ b/accessible/src/base/AccIterator.h
@@ -33,29 +33,24 @@
  * 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 nsAccIterator_h_
 #define nsAccIterator_h_
 
-#include "nsAccessible.h"
-#include "nsAccUtils.h"
-
-/**
- * Return true if the traversed accessible complies with filter.
- */
-typedef PRBool (*AccIteratorFilterFuncPtr) (nsAccessible *);
+#include "filters.h"
+#include "nscore.h"
 
 /**
  * Allows to iterate through accessible children or subtree complying with
  * filter function.
  */
-class nsAccIterator
+class AccIterator
 {
 public:
   /**
    * Used to define iteration type.
    */
   enum IterationType {
     /**
      * Navigation happens through direct children.
@@ -64,61 +59,38 @@ public:
 
     /**
      * Navigation through subtree excluding iterator root; if the accessible
      * complies with filter, iterator ignores its children.
      */
     eTreeNav
   };
 
-  nsAccIterator(nsAccessible *aRoot, AccIteratorFilterFuncPtr aFilterFunc,
-                IterationType aIterationType = eFlatNav);
-  ~nsAccIterator();
+  AccIterator(nsAccessible* aRoot, filters::FilterFuncPtr aFilterFunc,
+              IterationType aIterationType = eFlatNav);
+  ~AccIterator();
 
   /**
    * Return next accessible complying with filter function. Return the first
    * accessible for the first time.
    */
   nsAccessible *GetNext();
 
-  /**
-   * Predefined filters.
-   */
-  static PRBool GetSelected(nsAccessible *aAccessible)
-  {
-    return nsAccUtils::State(aAccessible) & nsIAccessibleStates::STATE_SELECTED;
-  }
-  static PRBool GetSelectable(nsAccessible *aAccessible)
-  {
-    return nsAccUtils::State(aAccessible) & nsIAccessibleStates::STATE_SELECTABLE;
-  }
-  static PRBool GetRow(nsAccessible *aAccessible)
-  {
-    return nsAccUtils::Role(aAccessible) == nsIAccessibleRole::ROLE_ROW;
-  }
-  static PRBool GetCell(nsAccessible *aAccessible)
-  {
-    PRUint32 role = nsAccUtils::Role(aAccessible);
-    return role == nsIAccessibleRole::ROLE_GRID_CELL ||
-           role == nsIAccessibleRole::ROLE_ROWHEADER ||
-           role == nsIAccessibleRole::ROLE_COLUMNHEADER;
-  }
-
 private:
-  nsAccIterator();
-  nsAccIterator(const nsAccIterator&);
-  nsAccIterator& operator =(const nsAccIterator&);
+  AccIterator();
+  AccIterator(const AccIterator&);
+  AccIterator& operator =(const AccIterator&);
 
   struct IteratorState
   {
     IteratorState(nsAccessible *aParent, IteratorState *mParentState = nsnull);
 
     nsAccessible *mParent;
     PRInt32 mIndex;
     IteratorState *mParentState;
   };
 
-  AccIteratorFilterFuncPtr mFilterFunc;
+  filters::FilterFuncPtr mFilterFunc;
   PRBool mIsDeep;
   IteratorState *mState;
 };
 
 #endif
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -43,20 +43,22 @@ VPATH = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = accessibility
 LIBRARY_NAME = accessibility_base_s
 LIBXUL_LIBRARY = 1
 
 
 CPPSRCS = \
+  AccCollector.cpp \
+  AccIterator.cpp \
+  filters.cpp \
   nsAccDocManager.cpp \
   nsAccessNode.cpp \
   nsAccEvent.cpp \
-  nsAccIterator.cpp \
   nsARIAGridAccessible.cpp \
   nsARIAMap.cpp \
   nsDocAccessible.cpp \
   nsOuterDocAccessible.cpp \
   nsAccessibilityAtoms.cpp \
   nsCoreUtils.cpp \
   nsAccUtils.cpp \
   nsRelUtils.cpp \
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/filters.cpp
@@ -0,0 +1,74 @@
+/* ***** 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 Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 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 "filters.h"
+
+#include "nsAccessible.h"
+#include "nsAccUtils.h"
+
+bool
+filters::GetSelected(nsAccessible* aAccessible)
+{
+  return nsAccUtils::State(aAccessible) & nsIAccessibleStates::STATE_SELECTED;
+}
+
+bool
+filters::GetSelectable(nsAccessible* aAccessible)
+{
+  return nsAccUtils::State(aAccessible) & nsIAccessibleStates::STATE_SELECTABLE;
+}
+
+bool
+filters::GetRow(nsAccessible* aAccessible)
+{
+  return nsAccUtils::Role(aAccessible) == nsIAccessibleRole::ROLE_ROW;
+}
+
+bool
+filters::GetCell(nsAccessible* aAccessible)
+{
+  PRUint32 role = nsAccUtils::Role(aAccessible);
+  return role == nsIAccessibleRole::ROLE_GRID_CELL ||
+      role == nsIAccessibleRole::ROLE_ROWHEADER ||
+      role == nsIAccessibleRole::ROLE_COLUMNHEADER;
+}
+
+bool
+filters::GetEmbeddedObject(nsAccessible* aAccessible)
+{
+  return nsAccUtils::IsEmbeddedObject(aAccessible);
+}
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/filters.h
@@ -0,0 +1,60 @@
+/* ***** 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 Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 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 a11yFilters_h_
+#define a11yFilters_h_
+
+class nsAccessible;
+
+/**
+ * Predefined filters used for nsAccIterator and nsAccCollector.
+ */
+namespace filters {
+
+  /**
+   * Return true if the traversed accessible complies with filter.
+   */
+  typedef bool (*FilterFuncPtr) (nsAccessible*);
+
+  bool GetSelected(nsAccessible* aAccessible);
+  bool GetSelectable(nsAccessible* aAccessible);
+  bool GetRow(nsAccessible* aAccessible);
+  bool GetCell(nsAccessible* aAccessible);
+  bool GetEmbeddedObject(nsAccessible* aAccessible);
+}
+
+#endif
--- a/accessible/src/base/nsARIAGridAccessible.cpp
+++ b/accessible/src/base/nsARIAGridAccessible.cpp
@@ -33,17 +33,18 @@
  * 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"
 
-#include "nsAccIterator.h"
+#include "AccIterator.h"
+#include "nsAccUtils.h"
 
 #include "nsIMutableArray.h"
 #include "nsComponentManagerUtils.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsARIAGridAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -96,20 +97,20 @@ NS_IMETHODIMP
 nsARIAGridAccessible::GetColumnCount(PRInt32 *acolumnCount)
 {
   NS_ENSURE_ARG_POINTER(acolumnCount);
   *acolumnCount = 0;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
   nsAccessible *row = rowIter.GetNext();
 
-  nsAccIterator cellIter(row, nsAccIterator::GetCell);
+  AccIterator cellIter(row, filters::GetCell);
   nsAccessible *cell = nsnull;
 
   while ((cell = cellIter.GetNext()))
     (*acolumnCount)++;
 
   return NS_OK;
 }
 
@@ -117,17 +118,17 @@ NS_IMETHODIMP
 nsARIAGridAccessible::GetRowCount(PRInt32 *arowCount)
 {
   NS_ENSURE_ARG_POINTER(arowCount);
   *arowCount = 0;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
   while (rowIter.GetNext())
     (*arowCount)++;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsARIAGridAccessible::GetCellAt(PRInt32 aRowIndex, PRInt32 aColumnIndex,
@@ -287,17 +288,17 @@ nsARIAGridAccessible::IsColumnSelected(P
   NS_ENSURE_ARG_POINTER(aIsSelected);
   *aIsSelected = PR_FALSE;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   NS_ENSURE_ARG(IsValidColumn(aColumn));
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
   nsAccessible *row = rowIter.GetNext();
   if (!row)
     return NS_OK;
 
   do {
     if (!nsAccUtils::IsARIASelected(row)) {
       nsAccessible *cell = GetCellInRowAt(row, aColumn);
       if (!cell) // Do not fail due to wrong markup
@@ -320,17 +321,17 @@ nsARIAGridAccessible::IsRowSelected(PRIn
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   nsAccessible *row = GetRowAt(aRow);
   NS_ENSURE_ARG(row);
 
   if (!nsAccUtils::IsARIASelected(row)) {
-    nsAccIterator cellIter(row, nsAccIterator::GetCell);
+    AccIterator cellIter(row, filters::GetCell);
     nsAccessible *cell = nsnull;
     while ((cell = cellIter.GetNext())) {
       if (!nsAccUtils::IsARIASelected(cell))
         return NS_OK;
     }
   }
 
   *aIsSelected = PR_TRUE;
@@ -369,26 +370,26 @@ nsARIAGridAccessible::GetSelectedCellCou
   *aCount = 0;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   PRInt32 colCount = 0;
   GetColumnCount(&colCount);
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = nsnull;
   while ((row = rowIter.GetNext())) {
     if (nsAccUtils::IsARIASelected(row)) {
       (*aCount) += colCount;
       continue;
     }
 
-    nsAccIterator cellIter(row, nsAccIterator::GetCell);
+    AccIterator cellIter(row, filters::GetCell);
     nsAccessible *cell = nsnull;
 
     while ((cell = cellIter.GetNext())) {
       if (nsAccUtils::IsARIASelected(cell))
         (*aCount)++;
     }
   }
 
@@ -405,26 +406,26 @@ NS_IMETHODIMP
 nsARIAGridAccessible::GetSelectedRowCount(PRUint32* aCount)
 {
   NS_ENSURE_ARG_POINTER(aCount);
   *aCount = 0;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = nsnull;
   while ((row = rowIter.GetNext())) {
     if (nsAccUtils::IsARIASelected(row)) {
       (*aCount)++;
       continue;
     }
 
-    nsAccIterator cellIter(row, nsAccIterator::GetCell);
+    AccIterator cellIter(row, filters::GetCell);
     nsAccessible *cell = cellIter.GetNext();
     if (!cell)
       continue;
 
     PRBool isRowSelected = PR_TRUE;
     do {
       if (!nsAccUtils::IsARIASelected(cell)) {
         isRowSelected = PR_FALSE;
@@ -448,21 +449,21 @@ nsARIAGridAccessible::GetSelectedCells(n
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   nsresult rv = NS_OK;
   nsCOMPtr<nsIMutableArray> selCells =
     do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = nsnull;
   while (row = rowIter.GetNext()) {
-    nsAccIterator cellIter(row, nsAccIterator::GetCell);
+    AccIterator cellIter(row, filters::GetCell);
     nsAccessible *cell = nsnull;
 
     if (nsAccUtils::IsARIASelected(row)) {
       while (cell = cellIter.GetNext())
         selCells->AppendElement(static_cast<nsIAccessible *>(cell), PR_FALSE);
 
       continue;
     }
@@ -492,28 +493,28 @@ nsARIAGridAccessible::GetSelectedCellInd
   PRInt32 rowCount = 0;
   GetRowCount(&rowCount);
 
   PRInt32 colCount = 0;
   GetColumnCount(&colCount);
 
   nsTArray<PRInt32> selCells(rowCount * colCount);
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = nsnull;
   for (PRInt32 rowIdx = 0; row = rowIter.GetNext(); rowIdx++) {
     if (nsAccUtils::IsARIASelected(row)) {
       for (PRInt32 colIdx = 0; colIdx < colCount; colIdx++)
         selCells.AppendElement(rowIdx * colCount + colIdx);
 
       continue;
     }
 
-    nsAccIterator cellIter(row, nsAccIterator::GetCell);
+    AccIterator cellIter(row, filters::GetCell);
     nsAccessible *cell = nsnull;
 
     for (PRInt32 colIdx = 0; cell = cellIter.GetNext(); colIdx++) {
       if (nsAccUtils::IsARIASelected(cell))
         selCells.AppendElement(rowIdx * colCount + colIdx);
     }
   }
 
@@ -552,26 +553,26 @@ nsARIAGridAccessible::GetSelectedRowIndi
 
   PRInt32 rowCount = 0;
   GetRowCount(&rowCount);
   if (!rowCount)
     return NS_OK;
 
   nsTArray<PRInt32> selRows(rowCount);
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = nsnull;
   for (PRInt32 rowIdx = 0; row = rowIter.GetNext(); rowIdx++) {
     if (nsAccUtils::IsARIASelected(row)) {
       selRows.AppendElement(rowIdx);
       continue;
     }
 
-    nsAccIterator cellIter(row, nsAccIterator::GetCell);
+    AccIterator cellIter(row, filters::GetCell);
     nsAccessible *cell = cellIter.GetNext();
     if (!cell)
       continue;
 
     PRBool isRowSelected = PR_TRUE;
     do {
       if (!nsAccUtils::IsARIASelected(cell)) {
         isRowSelected = PR_FALSE;
@@ -598,17 +599,17 @@ nsARIAGridAccessible::GetSelectedRowIndi
 NS_IMETHODIMP
 nsARIAGridAccessible::SelectRow(PRInt32 aRow)
 {
   NS_ENSURE_ARG(IsValidRow(aRow));
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = nsnull;
   for (PRInt32 rowIdx = 0; row = rowIter.GetNext(); rowIdx++) {
     nsresult rv = SetARIASelected(row, rowIdx == aRow);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
@@ -617,17 +618,17 @@ nsARIAGridAccessible::SelectRow(PRInt32 
 NS_IMETHODIMP
 nsARIAGridAccessible::SelectColumn(PRInt32 aColumn)
 {
   NS_ENSURE_ARG(IsValidColumn(aColumn));
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = nsnull;
   while ((row = rowIter.GetNext())) {
     // Unselect all cells in the row.
     nsresult rv = SetARIASelected(row, PR_FALSE);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Select cell at the column index.
@@ -656,17 +657,17 @@ nsARIAGridAccessible::UnselectRow(PRInt3
 NS_IMETHODIMP
 nsARIAGridAccessible::UnselectColumn(PRInt32 aColumn)
 {
   NS_ENSURE_ARG(IsValidColumn(aColumn));
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = nsnull;
   while ((row = rowIter.GetNext())) {
     nsAccessible *cell = GetCellInRowAt(row, aColumn);
     if (cell) {
       nsresult rv = SetARIASelected(cell, PR_FALSE);
       NS_ENSURE_SUCCESS(rv, rv);
     }
@@ -725,31 +726,31 @@ nsARIAGridAccessible::IsValidRowNColumn(
   return aColumn < colCount;
 }
 
 nsAccessible*
 nsARIAGridAccessible::GetRowAt(PRInt32 aRow)
 {
   PRInt32 rowIdx = aRow;
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
 
   nsAccessible *row = rowIter.GetNext();
   while (rowIdx != 0 && (row = rowIter.GetNext()))
     rowIdx--;
 
   return row;
 }
 
 nsAccessible*
 nsARIAGridAccessible::GetCellInRowAt(nsAccessible *aRow, PRInt32 aColumn)
 {
   PRInt32 colIdx = aColumn;
 
-  nsAccIterator cellIter(aRow, nsAccIterator::GetCell);
+  AccIterator cellIter(aRow, filters::GetCell);
   nsAccessible *cell = cellIter.GetNext();
   while (colIdx != 0 && (cell = cellIter.GetNext()))
     colIdx--;
 
   return cell;
 }
 
 nsresult
@@ -779,17 +780,17 @@ nsARIAGridAccessible::SetARIASelected(ns
   if (aIsSelected)
     return NS_OK;
 
   PRUint32 role = nsAccUtils::Role(aAccessible);
 
   // If the given accessible is row that was unselected then remove
   // aria-selected from cell accessible.
   if (role == nsIAccessibleRole::ROLE_ROW) {
-    nsAccIterator cellIter(aAccessible, nsAccIterator::GetCell);
+    AccIterator cellIter(aAccessible, filters::GetCell);
     nsAccessible *cell = nsnull;
 
     while ((cell = cellIter.GetNext())) {
       rv = SetARIASelected(cell, PR_FALSE, PR_FALSE);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return NS_OK;
   }
@@ -802,17 +803,17 @@ nsARIAGridAccessible::SetARIASelected(ns
       role == nsIAccessibleRole::ROLE_COLUMNHEADER) {
     nsAccessible *row = aAccessible->GetParent();
 
     if (nsAccUtils::Role(row) == nsIAccessibleRole::ROLE_ROW &&
         nsAccUtils::IsARIASelected(row)) {
       rv = SetARIASelected(row, PR_FALSE, PR_FALSE);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      nsAccIterator cellIter(row, nsAccIterator::GetCell);
+      AccIterator cellIter(row, filters::GetCell);
       nsAccessible *cell = nsnull;
       while ((cell = cellIter.GetNext())) {
         if (cell != aAccessible) {
           rv = SetARIASelected(cell, PR_TRUE, PR_FALSE);
           NS_ENSURE_SUCCESS(rv, rv);
         }
       }
     }
@@ -828,17 +829,17 @@ nsARIAGridAccessible::GetSelectedColumns
   NS_ENSURE_ARG_POINTER(acolumnCount);
   *acolumnCount = 0;
   if (aColumns)
     *aColumns = nsnull;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsAccIterator rowIter(this, nsAccIterator::GetRow);
+  AccIterator rowIter(this, filters::GetRow);
   nsAccessible *row = rowIter.GetNext();
   if (!row)
     return NS_OK;
 
   PRInt32 colCount = 0;
   GetColumnCount(&colCount);
   if (!colCount)
     return NS_OK;
@@ -851,17 +852,17 @@ nsARIAGridAccessible::GetSelectedColumns
     isColSelArray[i] = PR_TRUE;
 
   do {
     if (nsAccUtils::IsARIASelected(row))
       continue;
 
     PRInt32 colIdx = 0;
 
-    nsAccIterator cellIter(row, nsAccIterator::GetCell);
+    AccIterator cellIter(row, filters::GetCell);
     nsAccessible *cell = nsnull;
     for (colIdx = 0; cell = cellIter.GetNext(); colIdx++) {
       if (isColSelArray.SafeElementAt(colIdx, PR_FALSE) &&
           !nsAccUtils::IsARIASelected(cell)) {
         isColSelArray[colIdx] = PR_FALSE;
         selColCount--;
       }
     }
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -47,17 +47,16 @@
 #include "nsIAccessibleTypes.h"
 
 #include "a11yGeneric.h"
 
 #include "nsIContent.h"
 #include "nsIDOMNode.h"
 #include "nsINameSpaceManager.h"
 #include "nsIStringBundle.h"
-#include "nsRefPtrHashtable.h"
 #include "nsWeakReference.h"
 
 class nsAccessNode;
 class nsApplicationAccessible;
 class nsDocAccessible;
 class nsIAccessibleDocument;
 class nsRootAccessible;
 
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -36,17 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsAccessible.h"
 
 #include "nsIXBLAccessible.h"
 
-#include "nsAccIterator.h"
+#include "AccIterator.h"
 #include "nsAccUtils.h"
 #include "nsARIAMap.h"
 #include "nsDocAccessible.h"
 #include "nsEventShell.h"
 
 #include "nsAccessibilityService.h"
 #include "nsAccTreeWalker.h"
 #include "nsRelUtils.h"
@@ -2373,17 +2373,17 @@ nsAccessible::DispatchClickEvent(nsICont
 NS_IMETHODIMP nsAccessible::GetSelectedChildren(nsIArray **aSelectedAccessibles)
 {
   *aSelectedAccessibles = nsnull;
 
   nsCOMPtr<nsIMutableArray> selectedAccessibles =
     do_CreateInstance(NS_ARRAY_CONTRACTID);
   NS_ENSURE_STATE(selectedAccessibles);
 
-  nsAccIterator iter(this, nsAccIterator::GetSelected, nsAccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
   nsIAccessible *selected = nsnull;
   while ((selected = iter.GetNext()))
     selectedAccessibles->AppendElement(selected, PR_FALSE);
 
   PRUint32 length = 0;
   selectedAccessibles->GetLength(&length); 
   if (length) { // length of nsIArray containing selected options
     *aSelectedAccessibles = selectedAccessibles;
@@ -2398,17 +2398,17 @@ NS_IMETHODIMP nsAccessible::RefSelection
 {
   NS_ENSURE_ARG_POINTER(aSelected);
   *aSelected = nsnull;
 
   if (aIndex < 0) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsAccIterator iter(this, nsAccIterator::GetSelected, nsAccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
   nsAccessible *selected = nsnull;
 
   PRInt32 count = 0;
   while (count ++ <= aIndex) {
     selected = iter.GetNext();
     if (!selected) {
       // The index is out of range.
       return NS_ERROR_INVALID_ARG;
@@ -2418,17 +2418,17 @@ NS_IMETHODIMP nsAccessible::RefSelection
   return NS_OK;
 }
 
 NS_IMETHODIMP nsAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
 {
   NS_ENSURE_ARG_POINTER(aSelectionCount);
   *aSelectionCount = 0;
 
-  nsAccIterator iter(this, nsAccIterator::GetSelected, nsAccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
   nsAccessible *selected = nsnull;
   while ((selected = iter.GetNext()))
     ++(*aSelectionCount);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsAccessible::AddChildToSelection(PRInt32 aIndex)
@@ -2480,27 +2480,27 @@ NS_IMETHODIMP nsAccessible::IsChildSelec
     *aIsSelected = PR_TRUE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccessible::ClearSelection()
 {
-  nsAccIterator iter(this, nsAccIterator::GetSelected, nsAccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
   nsAccessible *selected = nsnull;
   while ((selected = iter.GetNext()))
     selected->SetSelected(PR_FALSE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsAccessible::SelectAllSelection(PRBool *_retval)
 {
-  nsAccIterator iter(this, nsAccIterator::GetSelectable, nsAccIterator::eTreeNav);
+  AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
   nsAccessible *selectable = nsnull;
   while((selectable = iter.GetNext()))
     selectable->SetSelected(PR_TRUE);
 
   return NS_OK;
 }
 
 // nsIAccessibleHyperLink
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -45,16 +45,17 @@
 #include "nsIAccessibleHyperLink.h"
 #include "nsIAccessibleSelectable.h"
 #include "nsIAccessibleValue.h"
 #include "nsIAccessibleRole.h"
 #include "nsIAccessibleStates.h"
 
 #include "nsStringGlue.h"
 #include "nsTArray.h"
+#include "nsRefPtrHashtable.h"
 
 class nsAccessible;
 class nsAccEvent;
 struct nsRoleMapEntry;
 
 struct nsRect;
 class nsIContent;
 class nsIFrame;
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -1316,58 +1316,66 @@ nsHyperTextAccessible::GetOffsetAtPoint(
     }
 
     offset += nsAccUtils::TextLength(childAcc);
   }
 
   return NS_OK; // Not found, will return -1
 }
 
-// ------- nsIAccessibleHyperText ---------------
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIAccessibleHyperText
+
 NS_IMETHODIMP
 nsHyperTextAccessible::GetLinkCount(PRInt32 *aLinkCount)
 {
   NS_ENSURE_ARG_POINTER(aLinkCount);
   *aLinkCount = 0;
+
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  PRInt32 childCount = GetChildCount();
-  for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
-    nsAccessible *childAcc = mChildren[childIdx];
-    if (nsAccUtils::IsEmbeddedObject(childAcc))
-      ++*aLinkCount;
-  }
+  *aLinkCount = GetLinkCount();
   return NS_OK;
 }
 
-
 NS_IMETHODIMP
-nsHyperTextAccessible::GetLink(PRInt32 aLinkIndex, nsIAccessibleHyperLink **aLink)
+nsHyperTextAccessible::GetLinkAt(PRInt32 aIndex, nsIAccessibleHyperLink** aLink)
 {
   NS_ENSURE_ARG_POINTER(aLink);
   *aLink = nsnull;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  PRInt32 linkIndex = aLinkIndex;
+  nsAccessible* link = GetLinkAt(aIndex);
+  if (link)
+    CallQueryInterface(link, aLink);
 
-  PRInt32 childCount = GetChildCount();
-  for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
-    nsAccessible *childAcc = mChildren[childIdx];
-    if (nsAccUtils::IsEmbeddedObject(childAcc) && linkIndex-- == 0)
-      return CallQueryInterface(childAcc, aLink);
-  }
-
-  return NS_ERROR_INVALID_ARG;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
-nsHyperTextAccessible::GetLinkIndex(PRInt32 aCharIndex, PRInt32 *aLinkIndex)
+nsHyperTextAccessible::GetLinkIndex(nsIAccessibleHyperLink* aLink,
+                                    PRInt32* aIndex)
+{
+  NS_ENSURE_ARG_POINTER(aLink);
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsRefPtr<nsAccessible> link(do_QueryObject(aLink));
+  *aIndex = GetLinkIndex(link);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHyperTextAccessible::GetLinkIndexAtOffset(PRInt32 aCharIndex,
+                                            PRInt32* aLinkIndex)
 {
   NS_ENSURE_ARG_POINTER(aLinkIndex);
   *aLinkIndex = -1; // API says this magic value means 'not found'
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   PRInt32 characterCount = 0;
@@ -2024,16 +2032,29 @@ nsHyperTextAccessible::ScrollSubstringTo
       }
     }
     frame = parentFrame;
   }
 
   return NS_OK;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsAccessible public
+
+void
+nsHyperTextAccessible::InvalidateChildren()
+{
+  mLinks = nsnull;
+  nsAccessibleWrap::InvalidateChildren();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsHyperTextAccessible public static
+
 nsresult nsHyperTextAccessible::ContentToRenderedOffset(nsIFrame *aFrame, PRInt32 aContentOffset,
                                                         PRUint32 *aRenderedOffset)
 {
   if (!aFrame) {
     // Current frame not rendered -- this can happen if text is set on
     // something with display: none
     *aRenderedOffset = 0;
     return NS_OK;
@@ -2081,16 +2102,27 @@ nsresult nsHyperTextAccessible::Rendered
   *aContentOffset = iter.ConvertSkippedToOriginal(aRenderedOffset + ourRenderedStart) - ourContentStart;
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHyperTextAccessible protected
 
+AccCollector*
+nsHyperTextAccessible::GetLinkCollector()
+{
+  if (IsDefunct())
+    return nsnull;
+
+  if (!mLinks)
+    mLinks = new AccCollector(this, filters::GetEmbeddedObject);
+  return mLinks;
+}
+
 nsAccessible *
 nsHyperTextAccessible::GetAccessibleAtOffset(PRInt32 aOffset, PRInt32 *aAccIdx,
                                              PRInt32 *aStartOffset,
                                              PRInt32 *aEndOffset)
 {
   PRInt32 startOffset = 0, endOffset = 0;
   PRInt32 childCount = GetChildCount();
   for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -35,20 +35,22 @@
  * 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 _nsHyperTextAccessible_H_
 #define _nsHyperTextAccessible_H_
 
-#include "nsAccessibleWrap.h"
 #include "nsIAccessibleText.h"
 #include "nsIAccessibleHyperText.h"
 #include "nsIAccessibleEditableText.h"
+
+#include "AccCollector.h"
+#include "nsAccessibleWrap.h"
 #include "nsTextAttrs.h"
 
 #include "nsFrameSelection.h"
 #include "nsISelectionController.h"
 
 enum EGetTextType { eGetBefore=-1, eGetAt=0, eGetAfter=1 };
 
 // This character marks where in the text returned via nsIAccessibleText(),
@@ -82,25 +84,56 @@ public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_HYPERTEXTACCESSIBLE_IMPL_CID)
 
   // nsAccessible
   virtual PRInt32 GetLevelInternal();
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
   virtual nsresult GetRoleInternal(PRUint32 *aRole);
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
 
+  virtual void InvalidateChildren();
+
+  // nsHyperTextAccessible
+
   // Convert content offset to rendered text offset  
   static nsresult ContentToRenderedOffset(nsIFrame *aFrame, PRInt32 aContentOffset,
                                           PRUint32 *aRenderedOffset);
   
   // Convert rendered text offset to content offset
   static nsresult RenderedToContentOffset(nsIFrame *aFrame, PRUint32 aRenderedOffset,
                                           PRInt32 *aContentOffset);
 
   /**
+   * Return link count within this hypertext accessible.
+   */
+  inline PRUint32 GetLinkCount()
+  {
+    AccCollector* links = GetLinkCollector();
+    return links ? links->Count() : 0;
+  }
+
+  /**
+   * Return link accessible at the given index.
+   */
+  inline nsAccessible* GetLinkAt(PRUint32 aIndex)
+  {
+    AccCollector* links = GetLinkCollector();
+    return links ? links->GetAccessibleAt(aIndex) : nsnull;
+  }
+
+  /**
+   * Return index for the given link accessible.
+   */
+  inline PRInt32 GetLinkIndex(nsAccessible* aLink)
+  {
+    AccCollector* links = GetLinkCollector();
+    return links ? links->GetIndexAt(aLink) : -1;
+  }
+
+  /**
     * Turn a DOM Node and offset into a character offset into this hypertext.
     * Will look for closest match when the DOM node does not have an accessible
     * object associated with it. Will return an offset for the end of
     * the string if the node is not found.
     *
     * @param aNode - the node to look for
     * @param aNodeOffset - the offset to look for
     *                      if -1 just look directly for the node
@@ -149,18 +182,22 @@ public:
   nsresult HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset,
                                       PRInt32 aEndHTOffset,
                                       nsIDOMNode **aStartNode,
                                       PRInt32 *aStartOffset,
                                       nsIDOMNode **aEndNode,
                                       PRInt32 *aEndOffset);
 
 protected:
+  // nsHyperTextAccessible
 
-  // nsHyperTextAccessible
+  /**
+   * Return link collection, create it if necessary.
+   */
+  AccCollector* GetLinkCollector();
 
   /*
    * This does the work for nsIAccessibleText::GetText[At|Before|After]Offset
    * @param aType, eGetBefore, eGetAt, eGetAfter
    * @param aBoundaryType, char/word-start/word-end/line-start/line-end/paragraph/attribute
    * @param aOffset, offset into the hypertext to start from
    * @param *aStartOffset, the resulting start offset for the returned substring
    * @param *aEndOffset, the resulting end offset for the returned substring
@@ -300,15 +337,18 @@ protected:
    * @param aStartOffset      [in, out] the start offset
    * @param aEndOffset        [in, out] the end offset
    * @param aAttributes       [out, optional] result attributes
    */
   nsresult GetSpellTextAttribute(nsIDOMNode *aNode, PRInt32 aNodeOffset,
                                  PRInt32 *aStartOffset,
                                  PRInt32 *aEndOffset,
                                  nsIPersistentProperties *aAttributes);
+
+private:
+  nsAutoPtr<AccCollector> mLinks;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsHyperTextAccessible,
                               NS_HYPERTEXTACCESSIBLE_IMPL_CID)
 
 #endif  // _nsHyperTextAccessible_H_
 
--- a/accessible/src/msaa/CAccessibleHypertext.cpp
+++ b/accessible/src/msaa/CAccessibleHypertext.cpp
@@ -37,21 +37,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "CAccessibleHypertext.h"
 
 #include "AccessibleHypertext_i.c"
 
-#include "nsIAccessibleHypertext.h"
-#include "nsIWinAccessNode.h"
-#include "nsAccessNodeWrap.h"
-
-#include "nsCOMPtr.h"
+#include "nsHyperTextAccessible.h"
 
 // IUnknown
 
 STDMETHODIMP
 CAccessibleHypertext::QueryInterface(REFIID iid, void** ppv)
 {
   *ppv = NULL;
   if (IID_IAccessibleHypertext == iid) {
@@ -70,55 +66,46 @@ CAccessibleHypertext::QueryInterface(REF
 // IAccessibleHypertext
 
 STDMETHODIMP
 CAccessibleHypertext::get_nHyperlinks(long *aHyperlinkCount)
 {
 __try {
   *aHyperlinkCount = 0;
 
-  nsCOMPtr<nsIAccessibleHyperText> hyperAcc(do_QueryInterface(this));
-  if (!hyperAcc)
+  nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(this);
+  if (!hyperText)
     return E_FAIL;
 
-  PRInt32 count = 0;
-  nsresult rv = hyperAcc->GetLinkCount(&count);
-  if (NS_FAILED(rv))
-    return GetHRESULT(rv);
-
-  *aHyperlinkCount = count;
+  *aHyperlinkCount = hyperText->GetLinkCount();
   return S_OK;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
 CAccessibleHypertext::get_hyperlink(long aLinkIndex,
                                     IAccessibleHyperlink **aHyperlink)
 {
 __try {
   *aHyperlink = NULL;
 
-  nsCOMPtr<nsIAccessibleHyperText> hyperAcc(do_QueryInterface(this));
-  if (!hyperAcc)
+  nsRefPtr<nsHyperTextAccessible> hyperText = do_QueryObject(this);
+  if (!hyperText)
     return E_FAIL;
 
-  nsCOMPtr<nsIAccessibleHyperLink> hyperLink;
-  nsresult rv = hyperAcc->GetLink(aLinkIndex, getter_AddRefs(hyperLink));
-  if (NS_FAILED(rv))
-    return GetHRESULT(rv);
-
-  nsCOMPtr<nsIWinAccessNode> winAccessNode(do_QueryInterface(hyperLink));
+  nsAccessible* hyperLink = hyperText->GetLinkAt(aLinkIndex);
+  nsCOMPtr<nsIWinAccessNode> winAccessNode(do_QueryObject(hyperLink));
   if (!winAccessNode)
     return E_FAIL;
 
   void *instancePtr = NULL;
-  rv =  winAccessNode->QueryNativeInterface(IID_IAccessibleHyperlink,
-                                            &instancePtr);
+  nsresult rv =  winAccessNode->QueryNativeInterface(IID_IAccessibleHyperlink,
+                                                     &instancePtr);
   if (NS_FAILED(rv))
     return E_FAIL;
 
   *aHyperlink = static_cast<IAccessibleHyperlink*>(instancePtr);
   return S_OK;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
@@ -130,17 +117,17 @@ CAccessibleHypertext::get_hyperlinkIndex
 __try {
   *aHyperlinkIndex = 0;
 
   nsCOMPtr<nsIAccessibleHyperText> hyperAcc(do_QueryInterface(this));
   if (!hyperAcc)
     return E_FAIL;
 
   PRInt32 index = 0;
-  nsresult rv = hyperAcc->GetLinkIndex(aCharIndex, &index);
+  nsresult rv = hyperAcc->GetLinkIndexAtOffset(aCharIndex, &index);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
   *aHyperlinkIndex = index;
   return S_OK;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
--- a/accessible/src/msaa/CAccessibleHypertext.h
+++ b/accessible/src/msaa/CAccessibleHypertext.h
@@ -63,12 +63,15 @@ public:
 
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_hyperlink(
       /* [in] */ long index,
       /* [retval][out] */ IAccessibleHyperlink **hyperlink);
 
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_hyperlinkIndex(
       /* [in] */ long charIndex,
       /* [retval][out] */ long *hyperlinkIndex);
+
+  // nsISupports
+  NS_IMETHOD QueryInterface(const nsIID& uuid, void** result) = 0;
 };
 
 #endif
 
--- a/accessible/tests/mochitest/test_nsIAccessibleHyperText.html
+++ b/accessible/tests/mochitest/test_nsIAccessibleHyperText.html
@@ -15,33 +15,51 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript"
           src="chrome://mochikit/content/a11y/accessible/common.js"></script>
 
   <script type="application/javascript">
     var gParagraphAcc;
 
     function testThis(aID, aCharIndex, aExpectedLinkIndex, aName)
     {
-      is(gParagraphAcc.getLinkIndex(aCharIndex), aExpectedLinkIndex,
-         "Wrong link index for ID " + aID + "!");
-      var linkAcc = null;
-      try {
-        linkAcc = gParagraphAcc.getLink(aExpectedLinkIndex);
-      } catch(e) { }
+      is(gParagraphAcc.getLinkIndexAtOffset(aCharIndex), aExpectedLinkIndex,
+         "Wrong link index at offset " + aCharIndex + " for ID " + aID + "!");
+
+      var linkAcc = gParagraphAcc.getLinkAt(aExpectedLinkIndex);
       ok(linkAcc, "No accessible for link " + aID + "!");
 
+      var linkIndex = gParagraphAcc.getLinkIndex(linkAcc);
+      is(linkIndex, aExpectedLinkIndex, "Wrong link index for " + aID + "!");
+
       // Just test the link's name to make sure we get the right one.
       is(linkAcc.getAnchor(0).name, aName, "Wrong name for " + aID + "!");
     }
 
+    const kLinksCount = 128;
+    function prepareTest()
+    {
+      var container = document.getElementById("p3");
+      for (var jdx = 0; jdx < kLinksCount; jdx++) {
+        var a = document.createElement("a");
+        a.setAttribute("href", "mozilla.org");
+        a.textContent = "mozilla";
+        container.appendChild(a);
+
+        var span = document.createElement("span");
+        span.textContent = " text ";
+        container.appendChild(span);
+      }
+
+      window.setTimeout(doTest, 0);
+    }
+
     function doTest()
     {
+      // Test link count
       gParagraphAcc = getAccessible("testParagraph", [nsIAccessibleHyperText]);
-
-      // Test link count
       is(gParagraphAcc.linkCount, 7, "Wrong link count for paragraph!");
 
       // normal hyperlink
       testThis("NormalHyperlink", 14, 0, "Mozilla Foundation");
 
       // ARIA hyperlink
       testThis("AriaHyperlink", 27, 1, "Mozilla Foundation Home");
 
@@ -55,26 +73,52 @@ https://bugzilla.mozilla.org/show_bug.cg
       testThis("emptyLink", 90, 4, null);
 
       // normal hyperlink with embedded span
       testThis("LinkWithSpan", 116, 5, "Heise Online");
 
       // Named anchor
       testThis("namedAnchor", 193, 6, "This should never be of state_linked");
 
+      // Paragraph with link
+      var p2 = getAccessible("p2", [nsIAccessibleHyperText]);
+      var link = p2.getLinkAt(0);
+      is(link, p2.getChildAt(0), "Wrong link for p2");
+      is(p2.linkCount, 1, "Wrong link count for p2");
+
+      // getLinkAt and getLinkIndex.
+      var container = document.getElementById("p3");
+      var htAcc = getAccessible(container, [nsIAccessibleHyperText]);
+      for (var jdx = 0; jdx < kLinksCount; jdx++) {
+        var link = htAcc.getLinkAt(jdx);
+        ok(link, "No link at index " + jdx + " for 'p3'");
+
+        var linkIdx = htAcc.getLinkIndex(link);
+        is(linkIdx, jdx, "Wrong link index for 'p3'!");
+      };
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
+    addA11yLoadEvent(prepareTest);
   </script>
 </head>
 <body>
 
-  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418368">Mozilla Bug 418368</a>
+  <a target="_blank"
+     title="Create tests for NSIAccessibleHyperlink interface"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=418368">
+    Mozilla Bug 418368
+  </a><br>
+  <a target="_blank"
+     title="Cache links within hypertext accessible"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=572394">
+    Mozilla Bug 572394
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
   <p id="testParagraph"><br
   >Simple link:<br
   ><a id="NormalHyperlink" href="http://www.mozilla.org">Mozilla Foundation</a><br
   >ARIA link:<br
@@ -99,10 +143,12 @@ https://bugzilla.mozilla.org/show_bug.cg
          src="letters.gif"></img><br
   >Empty link:<br
   ><a id="emptyLink" href=""><img src=""></img></a><br
   >Link with embedded span<br
   ><a id="LinkWithSpan" href="http://www.heise.de/"><span lang="de">Heise Online</span></a><br
   >Named anchor, must not have "linked" state for it to be exposed correctly:<br
   ><a id="namedAnchor" name="named_anchor">This should never be of state_linked</a>
   </p>
+  <p id="p2"><a href="http://mozilla.org">mozilla.org</a></p>
+  <p id="p3"></p>
 </body>
 </html>