gecko-dev/gfx/layers/TreeTraversal.h
Kevin Wern 7ae5cf1e52 Bug 1227231: Use generic tree traversal algorithms for loops over Layer trees. r=botond
Create an Iterator type with classes ForwardIterator and ReverseIterator,
having GetFirstChild/GetNextSibling and GetLastChild/GetPrevSibling
methods, respectively. Specify the iterator type for each call to
ForEachNode. With this, we can support trees with forward and reverse
sibling structures.

Additionally, apply these algorithms to all Layer recursive traversals,
where applicable. Update tests to ensure both directions yield expected
results.

MozReview-Commit-ID: iYpX22XHTa

--HG--
extra : rebase_source : 016d3c37e0679df7037dacd3366d3fa7748d0703
extra : amend_source : 089c4a62bd4716be087669742bb542c39a9f97c6
2016-03-10 01:20:40 -08:00

260 lines
7.3 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et 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 mozilla_layers_TreeTraversal_h
#define mozilla_layers_TreeTraversal_h
#include <queue>
namespace mozilla {
namespace layers {
/*
* Returned by |aPostAction| and |aPreAction| in ForEachNode, indicates
* the behavior to follow either action:
*
* TraversalFlag::Skip - the node's children are not traversed. If this
* flag is returned by |aPreAction|, |aPostAction| is skipped for the
* current node, as well.
* TraversalFlag::Continue - traversal continues normally.
* TraversalFlag::Abort - traversal stops immediately.
*/
enum class TraversalFlag { Skip, Continue, Abort };
/*
* Iterator types to be specified in traversal function calls:
*
* ForwardIterator - for nodes using GetFirstChild() and GetNextSibling()
* ReverseIterator - for nodes using GetLastChild() and GetPrevSibling()
*/
class ForwardIterator
{
public:
template <typename Node>
static Node* NextSibling(Node* n) {
return n->GetNextSibling();
}
template <typename Node>
static Node* FirstChild(Node* n) {
return n->GetFirstChild();
}
};
class ReverseIterator
{
public:
template <typename Node>
static Node* NextSibling(Node* n) {
return n->GetPrevSibling();
}
template <typename Node>
static Node* FirstChild(Node* n) {
return n->GetLastChild();
}
};
/*
* Do a depth-first traversal of the tree rooted at |aRoot|, performing
* |aPreAction| before traversal of children and |aPostAction| after.
*
* Returns true if traversal aborted, false if continued normally. If
* TraversalFlag::Skip is returned in |aPreAction|, then |aPostAction|
* is not performed.
*
* |Iterator| should have static methods named NextSibling() and FirstChild()
* that accept an argument of type Node*. For convenience, classes
* |ForwardIterator| and |ReverseIterator| are provided which implement these
* methods as GetNextSibling()/GetFirstChild() and GetPrevSibling()/GetLastChild(),
* respectively.
*/
template <typename Iterator, typename Node, typename PreAction, typename PostAction>
static auto ForEachNode(Node* aRoot, const PreAction& aPreAction, const PostAction& aPostAction) ->
typename EnableIf<IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value &&
IsSame<decltype(aPostAction(aRoot)),TraversalFlag>::value, bool>::Type
{
if (!aRoot) {
return false;
}
TraversalFlag result = aPreAction(aRoot);
if (result == TraversalFlag::Abort) {
return true;
}
if (result == TraversalFlag::Continue) {
for (Node* child = Iterator::FirstChild(aRoot);
child;
child = Iterator::NextSibling(child)) {
bool abort = ForEachNode<Iterator>(child, aPreAction, aPostAction);
if (abort) {
return true;
}
}
result = aPostAction(aRoot);
if (result == TraversalFlag::Abort) {
return true;
}
}
return false;
}
/*
* Do a depth-first traversal of the tree rooted at |aRoot|, performing
* |aPreAction| before traversal of children and |aPostAction| after.
*/
template <typename Iterator, typename Node, typename PreAction, typename PostAction>
static auto ForEachNode(Node* aRoot, const PreAction& aPreAction, const PostAction& aPostAction) ->
typename EnableIf<IsSame<decltype(aPreAction(aRoot)), void>::value &&
IsSame<decltype(aPostAction(aRoot)),void>::value, void>::Type
{
if (!aRoot) {
return;
}
aPreAction(aRoot);
for (Node* child = Iterator::FirstChild(aRoot);
child;
child = Iterator::NextSibling(child)) {
ForEachNode<Iterator>(child, aPreAction, aPostAction);
}
aPostAction(aRoot);
}
/*
* ForEachNode pre-order traversal, using TraversalFlag.
*/
template <typename Iterator, typename Node, typename PreAction>
auto ForEachNode(Node* aRoot, const PreAction& aPreAction) ->
typename EnableIf<IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value, bool>::Type
{
return ForEachNode<Iterator>(aRoot, aPreAction, [](Node* aNode){ return TraversalFlag::Continue; });
}
/*
* ForEachNode pre-order, not using TraversalFlag.
*/
template <typename Iterator, typename Node, typename PreAction>
auto ForEachNode(Node* aRoot, const PreAction& aPreAction) ->
typename EnableIf<IsSame<decltype(aPreAction(aRoot)), void>::value, void>::Type
{
ForEachNode<Iterator>(aRoot, aPreAction, [](Node* aNode){});
}
/*
* ForEachNode post-order traversal, using TraversalFlag.
*/
template <typename Iterator, typename Node, typename PostAction>
auto ForEachNodePostOrder(Node* aRoot, const PostAction& aPostAction) ->
typename EnableIf<IsSame<decltype(aPostAction(aRoot)), TraversalFlag>::value, bool>::Type
{
return ForEachNode<Iterator>(aRoot, [](Node* aNode){ return TraversalFlag::Continue; }, aPostAction);
}
/*
* ForEachNode post-order, not using TraversalFlag.
*/
template <typename Iterator, typename Node, typename PostAction>
auto ForEachNodePostOrder(Node* aRoot, const PostAction& aPostAction) ->
typename EnableIf<IsSame<decltype(aPostAction(aRoot)), void>::value, void>::Type
{
ForEachNode<Iterator>(aRoot, [](Node* aNode){}, aPostAction);
}
/*
* Do a breadth-first search of the tree rooted at |aRoot|, and return the
* first visited node that satisfies |aCondition|, or nullptr if no such node
* was found.
*
* See ForEachNode() for the requirements on |Iterator| and |Node|
*/
template <typename Iterator, typename Node, typename Condition>
Node* BreadthFirstSearch(Node* aRoot, const Condition& aCondition)
{
if (!aRoot) {
return nullptr;
}
std::queue<Node*> queue;
queue.push(aRoot);
while (!queue.empty()) {
Node* node = queue.front();
queue.pop();
if (aCondition(node)) {
return node;
}
for (Node* child = Iterator::FirstChild(node);
child;
child = Iterator::NextSibling(child)) {
queue.push(child);
}
}
return nullptr;
}
/*
* Do a pre-order, depth-first search of the tree rooted at |aRoot|, and
* return the first visited node that satisfies |aCondition|, or nullptr
* if no such node was found.
*
* See ForEachNode() for the requirements on |Iterator| and |Node|
*/
template <typename Iterator, typename Node, typename Condition>
Node* DepthFirstSearch(Node* aRoot, const Condition& aCondition)
{
Node* result = nullptr;
ForEachNode<Iterator>(aRoot,
[&aCondition, &result](Node* aNode)
{
if (aCondition(aNode)) {
result = aNode;
return TraversalFlag::Abort;
}
return TraversalFlag::Continue;
});
return result;
}
/*
* Perform a post-order, depth-first search starting at aRoot.
*
* See ForEachNode() for the requirements on |Iterator| and |Node|
*/
template <typename Iterator, typename Node, typename Condition>
Node* DepthFirstSearchPostOrder(Node* aRoot, const Condition& aCondition)
{
Node* result = nullptr;
ForEachNodePostOrder<Iterator>(aRoot,
[&aCondition, &result](Node* aNode)
{
if (aCondition(aNode)) {
result = aNode;
return TraversalFlag::Abort;
}
return TraversalFlag::Continue;
});
return result;
}
}
}
#endif // mozilla_layers_TreeTraversal_h