/* -*- 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 on top of ChildIterator. * * See ChildIterator.h for the kind of iterators you can use as the template * argument for this class. */ namespace mozilla { namespace dom { template class MOZ_STACK_CLASS TreeIterator { enum class Direction { Forward, Backwards, }; template nsIContent* GetNextChild(ChildIterator& aIter) { return aDirection == Direction::Forward ? aIter.GetNextChild() : aIter.GetPreviousChild(); } template inline void Advance(); template inline void AdvanceSkippingChildren(); public: explicit TreeIterator(nsIContent& aRoot) : mRoot(aRoot), mCurrent(&aRoot) {} nsIContent* GetCurrent() const { return mCurrent; } // Note that this keeps the iterator state consistent in case of failure. inline bool Seek(nsIContent&); inline nsIContent* GetNext(); inline nsIContent* GetNextSkippingChildren(); inline nsIContent* GetPrev(); inline nsIContent* GetPrevSkippingChildren(); private: using IteratorArray = AutoTArray; nsIContent& mRoot; nsIContent* mCurrent; IteratorArray mParentIterators; }; template template ::Direction aDirection> inline void TreeIterator::AdvanceSkippingChildren() { while (true) { if (MOZ_UNLIKELY(mParentIterators.IsEmpty())) { mCurrent = nullptr; return; } if (nsIContent* nextSibling = GetNextChild(mParentIterators.LastElement())) { mCurrent = nextSibling; return; } mParentIterators.RemoveLastElement(); } } template inline bool TreeIterator::Seek(nsIContent& aContent) { IteratorArray parentIterators; nsIContent* current = &aContent; while (current != &mRoot) { nsIContent* parent = ChildIterator::GetParent(*current); if (!parent) { return false; } ChildIterator children(parent); if (!children.Seek(current)) { return false; } parentIterators.AppendElement(std::move(children)); current = parent; } parentIterators.Reverse(); mParentIterators = std::move(parentIterators); mCurrent = &aContent; return true; } template template ::Direction aDirection> inline void TreeIterator::Advance() { MOZ_ASSERT(mCurrent); const bool startAtBeginning = aDirection == Direction::Forward; ChildIterator children(mCurrent, startAtBeginning); if (nsIContent* first = GetNextChild(children)) { mCurrent = first; mParentIterators.AppendElement(std::move(children)); return; } AdvanceSkippingChildren(); } template inline nsIContent* TreeIterator::GetNext() { Advance(); return GetCurrent(); } template inline nsIContent* TreeIterator::GetPrev() { Advance(); return GetCurrent(); } template inline nsIContent* TreeIterator::GetNextSkippingChildren() { AdvanceSkippingChildren(); return GetCurrent(); } template inline nsIContent* TreeIterator::GetPrevSkippingChildren() { AdvanceSkippingChildren(); return GetCurrent(); } } // namespace dom } // namespace mozilla #endif