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
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',