Bug 1455891: Add a generic pre-order tree iterator given a child iterator. r?smaug draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 25 Jun 2018 10:37:44 +0200
changeset 813917 00d0cb65f72503452bcf83d3ea41240330c2dfb8
parent 813916 38f319f756a311a2545fc1162e2157bb6d739bbc
child 813918 217933c4a41ecf50e5d894b704edbb1df229e6ee
push id115046
push userbmo:emilio@crisal.io
push dateWed, 04 Jul 2018 05:02:13 +0000
reviewerssmaug
bugs1455891
milestone63.0a1
Bug 1455891: Add a generic pre-order tree iterator given a child iterator. r?smaug Happy to change names or what not. MozReview-Commit-ID: IxYIqhRDLo4
dom/base/TreeIterator.h
dom/base/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/base/TreeIterator.h
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 TreeIterator_h
+#define TreeIterator_h
+
+#include "mozilla/Attributes.h"
+#include "nsIContent.h"
+
+/* A generic pre-order tree iterator */
+
+namespace mozilla {
+namespace dom {
+
+template<typename ChildIterator>
+class MOZ_STACK_CLASS TreeIterator
+{
+  enum class Direction
+  {
+    Forward,
+    Backwards,
+  };
+
+  template<Direction aDirection>
+  nsIContent* GetNextChild(ChildIterator& aIter)
+  {
+    return aDirection == Direction::Forward
+      ? aIter.GetNextChild()
+      : aIter.GetPreviousChild();
+  }
+
+  template<Direction> inline void Advance();
+  template<Direction> inline void AdvanceSkippingChildren();
+
+public:
+  explicit TreeIterator(nsIContent& aRoot)
+    : mRoot(aRoot)
+    , mCurrent(&aRoot)
+  {
+  }
+
+  nsIContent* GetCurrent() const
+  {
+    return mCurrent;
+  }
+
+  inline bool Seek(nsIContent&);
+  inline nsIContent* GetNext();
+  inline nsIContent* GetNextSkippingChildren();
+  inline nsIContent* GetPrev();
+  inline nsIContent* GetPrevSkippingChildren();
+
+private:
+  using IteratorArray = AutoTArray<ChildIterator, 30>;
+
+  nsIContent& mRoot;
+  nsIContent* mCurrent;
+  IteratorArray mParentIterators;
+};
+
+template<typename ChildIterator>
+template<typename TreeIterator<ChildIterator>::Direction aDirection>
+inline void
+TreeIterator<ChildIterator>::AdvanceSkippingChildren()
+{
+  while (true) {
+    if (MOZ_UNLIKELY(mParentIterators.IsEmpty())) {
+      mCurrent = nullptr;
+      return;
+    }
+
+    if (nsIContent* nextSibling =
+          GetNextChild<aDirection>(mParentIterators.LastElement())) {
+      mCurrent = nextSibling;
+      return;
+    }
+    mParentIterators.RemoveLastElement();
+  }
+}
+
+template<typename ChildIterator>
+inline bool
+TreeIterator<ChildIterator>::Seek(nsIContent& aContent)
+{
+  IteratorArray oldIterators(std::move(mParentIterators));
+  nsIContent* oldCurrent = mCurrent;
+
+  nsIContent* current = &aContent;
+  mCurrent = current;
+  mParentIterators.Clear();
+
+  while (current != &mRoot) {
+    nsIContent* parent = ChildIterator::GetParent(*current);
+    if (!parent) {
+      mParentIterators = std::move(oldIterators);
+      mCurrent = oldCurrent;
+      return false;
+    }
+
+    ChildIterator children(parent);
+    if (!children.Seek(current)) {
+      mParentIterators = std::move(oldIterators);
+      mCurrent = oldCurrent;
+      return false;
+    }
+
+    mParentIterators.AppendElement(std::move(children));
+    current = parent;
+  }
+  mParentIterators.Reverse();
+  return true;
+}
+
+template<typename ChildIterator>
+template<typename TreeIterator<ChildIterator>::Direction aDirection>
+inline void
+TreeIterator<ChildIterator>::Advance()
+{
+  MOZ_ASSERT(mCurrent);
+  const bool startAtBeginning = aDirection == Direction::Forward;
+  ChildIterator children(mCurrent, startAtBeginning);
+  if (nsIContent* first = GetNextChild<aDirection>(children)) {
+    mCurrent = first;
+    mParentIterators.AppendElement(std::move(children));
+    return;
+  }
+
+  AdvanceSkippingChildren<aDirection>();
+}
+
+template<typename ChildIterator>
+inline nsIContent*
+TreeIterator<ChildIterator>::GetNext()
+{
+  Advance<Direction::Forward>();
+  return GetCurrent();
+}
+
+template<typename ChildIterator>
+inline nsIContent*
+TreeIterator<ChildIterator>::GetPrev()
+{
+  Advance<Direction::Backwards>();
+  return GetCurrent();
+}
+
+template<typename ChildIterator>
+inline nsIContent*
+TreeIterator<ChildIterator>::GetNextSkippingChildren()
+{
+  AdvanceSkippingChildren<Direction::Forward>();
+  return GetCurrent();
+}
+
+template<typename ChildIterator>
+inline nsIContent*
+TreeIterator<ChildIterator>::GetPrevSkippingChildren()
+{
+  AdvanceSkippingChildren<Direction::Backwards>();
+  return GetCurrent();
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -219,16 +219,17 @@ EXPORTS.mozilla.dom += [
     'StyleSheetList.h',
     'SubtleCrypto.h',
     'SyncMessageSender.h',
     'TabGroup.h',
     'Text.h',
     'Timeout.h',
     'TimeoutHandler.h',
     'TimeoutManager.h',
+    'TreeIterator.h',
     'TreeWalker.h',
     'WebKitCSSMatrix.h',
     'WindowOrientationObserver.h',
 ]
 
 if CONFIG['FUZZING']:
     EXPORTS.mozilla.dom += [
         'FuzzingFunctions.h',