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 idunknown
push userunknown
push dateunknown
reviewersyzen
bugs1249730
milestone47.0a1
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;