`FlattenedChildIterator` is a non-template class and work only for the flattened tree (i.e., in the `TreeKind::Flat` mode). To centralize the implementation to access children from a parent node in the various kinds of tree except `ShadowIncludingDOM`, this patch makes it a template class and support the simple DOM (for `TreeKind::DOM`), the flattened tree (for`TreeKind::Flat`) and also the flattened tree for selection whose `TreeKind` will be added by the next patch. The reason why this patch does not support `TreeKind::ShadowIncludingDOM` is, it is not clear that which children should be handled when the parent is a shadow host, i.e., whether the children of the host or the `ShadowRoot`. Therefore, the caller should use `ShadowRoot` if the users want to list up the the children from a host. Then, the code is really clear about what it does. Differential Revision: https://phabricator.services.mozilla.com/D281303
147 lines
3.9 KiB
C++
147 lines
3.9 KiB
C++
/* 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 <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; }
|
|
|
|
// 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<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 parentIterators;
|
|
nsIContent* current = &aContent;
|
|
while (current != &mRoot) {
|
|
nsIContent* const parent =
|
|
nsIContent::FromNodeOrNull(ChildIterator::GetParentNodeOf(*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 <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
|