Bug 1249730 - make TreeWalker bi-directional, r=yzen
authorAlexander Surkov <surkov.alexander@gmail.com>
Tue, 08 Mar 2016 08:27:59 -0500
changeset 287245 677309c89611f3c23ae78d898837efd3781fac5d
parent 287244 f7cd029bb2dc11222bab8a8373bfa1d25dbcf832
child 287246 14838e17cfaf4d53a15884705a1e6c887feb8827
push id30065
push userkwierso@gmail.com
push dateWed, 09 Mar 2016 00:01:05 +0000
treeherdermozilla-central@886b5480b578 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs1249730
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1249730 - make TreeWalker bi-directional, r=yzen
accessible/base/TreeWalker.cpp
accessible/base/TreeWalker.h
--- a/accessible/base/TreeWalker.cpp
+++ b/accessible/base/TreeWalker.cpp
@@ -53,18 +53,67 @@ TreeWalker::
   MOZ_COUNT_CTOR(TreeWalker);
 }
 
 TreeWalker::~TreeWalker()
 {
   MOZ_COUNT_DTOR(TreeWalker);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// TreeWalker: private
+bool
+TreeWalker::Seek(nsIContent* aChildNode)
+{
+  MOZ_ASSERT(aChildNode, "Child cannot be null");
+
+  mPhase = eAtStart;
+  mStateStack.Clear();
+  mARIAOwnsIdx = 0;
+
+  nsIContent* childNode = nullptr;
+  nsINode* parentNode = aChildNode;
+  do {
+    childNode = parentNode->AsContent();
+    parentNode = childNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
+      (mChildFilter & nsIContent::eAllButXBL) ?
+      childNode->GetParentNode() : childNode->GetFlattenedTreeParent();
+
+    if (!parentNode || !parentNode->IsElement()) {
+      return false;
+    }
+
+    // If ARIA owned child.
+    Accessible* child = mDoc->GetAccessible(childNode);
+    if (child && child->IsRelocated()) {
+      if (child->Parent() != mContext) {
+        return false;
+      }
+
+      Accessible* ownedChild = nullptr;
+      while ((ownedChild = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx++)) &&
+             ownedChild != child);
+
+      MOZ_ASSERT(ownedChild, "A child has to be in ARIA owned elements");
+      mPhase = eAtARIAOwns;
+      return true;
+    }
+
+    // Look in DOM.
+    dom::AllChildrenIterator* iter = PrependState(parentNode->AsElement(), true);
+    if (!iter->Seek(childNode)) {
+      return false;
+    }
+
+    if (parentNode == mAnchorNode) {
+      mPhase = eAtDOM;
+      return true;
+    }
+  } while (true);
+
+  return false;
+}
 
 Accessible*
 TreeWalker::Next()
 {
   if (mStateStack.IsEmpty()) {
     if (mPhase == eAtEnd) {
       return nullptr;
     }
--- a/accessible/base/TreeWalker.h
+++ b/accessible/base/TreeWalker.h
@@ -45,42 +45,58 @@ public:
    * @param aAnchorNode [in] the node the search will be prepared relative to
    * @param aFlags   [in] flags (see enum above)
    */
   TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags = 0);
 
   ~TreeWalker();
 
   /**
+   * Clears the tree walker state and resets it to the given child within
+   * the anchor.
+   */
+  bool Seek(nsIContent* aChildNode);
+
+  /**
    * Return the next/prev accessible.
    *
    * @note Returned accessible is bound to the document, if the accessible is
    *       rejected during tree creation then the caller should be unbind it
    *       from the document.
    */
   Accessible* Next();
   Accessible* Prev();
 
+  Accessible* Context() const { return mContext; }
+  DocAccessible* Document() const { return mDoc; }
+
 private:
   TreeWalker();
   TreeWalker(const TreeWalker&);
   TreeWalker& operator =(const TreeWalker&);
 
   /**
-   * Create new state for the given node and push it on top of stack.
+   * Create new state for the given node and push it on top of stack / at bottom
+   * of stack.
    *
    * @note State stack is used to navigate up/down the DOM subtree during
    *        accessible children search.
    */
   dom::AllChildrenIterator* PushState(nsIContent* aContent,
                                       bool aStartAtBeginning)
   {
     return mStateStack.AppendElement(
       dom::AllChildrenIterator(aContent, mChildFilter, aStartAtBeginning));
   }
+  dom::AllChildrenIterator* PrependState(nsIContent* aContent,
+                                         bool aStartAtBeginning)
+  {
+    return mStateStack.InsertElementAt(0,
+      dom::AllChildrenIterator(aContent, mChildFilter, aStartAtBeginning));
+  }
 
   /**
    * Pop state from stack.
    */
   dom::AllChildrenIterator* PopState();
 
   DocAccessible* mDoc;
   Accessible* mContext;