Commit 5494876c authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Instance-version of NGPaintFragmentTraversal

Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng;luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I9300bd5c5565fc143bf315d892c003870b314187
Reviewed-on: https://chromium-review.googlesource.com/1098903
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarXiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Reviewed-by: default avatarYoichi Osato <yoichio@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568733}
parent b7b13868
......@@ -169,6 +169,16 @@ std::unique_ptr<NGPaintFragment> NGPaintFragment::Create(
return paint_fragment;
}
bool NGPaintFragment::IsDescendantOfNotSelf(
const NGPaintFragment& ancestor) const {
for (const NGPaintFragment* fragment = Parent(); fragment;
fragment = fragment->Parent()) {
if (fragment == &ancestor)
return true;
}
return false;
}
bool NGPaintFragment::HasSelfPaintingLayer() const {
return physical_fragment_->IsBox() &&
ToNGPhysicalBoxFragment(*physical_fragment_).HasSelfPaintingLayer();
......
......@@ -50,6 +50,10 @@ class CORE_EXPORT NGPaintFragment : public DisplayItemClient,
const Vector<std::unique_ptr<NGPaintFragment>>& Children() const {
return children_;
}
// Note, as the name implies, |IsDescendantOfNotSelf| returns false for the
// same object. This is different from |LayoutObject::IsDescendant| but is
// same as |Node::IsDescendant|.
bool IsDescendantOfNotSelf(const NGPaintFragment&) const;
// Returns the first line box for a block-level container.
NGPaintFragment* FirstLineBox() const;
......
......@@ -143,8 +143,93 @@ NGPaintFragmentTraversalContext NextSiblingOf(
return {fragment.parent, fragment.index + 1};
}
unsigned IndexOfChild(const NGPaintFragment& parent,
const NGPaintFragment& fragment) {
const auto& children = parent.Children();
const auto* it = std::find_if(
children.begin(), children.end(),
[&fragment](const auto& child) { return &fragment == child.get(); });
DCHECK(it != children.end());
return std::distance(children.begin(), it);
}
} // namespace
NGPaintFragmentTraversal::NGPaintFragmentTraversal(const NGPaintFragment& root)
: root_(root) {
Push(root, 0);
}
NGPaintFragmentTraversal::NGPaintFragmentTraversal(const NGPaintFragment& root,
const NGPaintFragment& start)
: root_(root) {
MoveTo(start);
}
void NGPaintFragmentTraversal::Push(const NGPaintFragment& parent,
unsigned index) {
stack_.push_back(ParentAndIndex{&parent, index});
current_ = parent.Children()[index].get();
}
void NGPaintFragmentTraversal::Push(const NGPaintFragment& fragment) {
const NGPaintFragment* parent = fragment.Parent();
DCHECK(parent);
Push(*parent, IndexOfChild(*parent, fragment));
}
void NGPaintFragmentTraversal::MoveTo(const NGPaintFragment& fragment) {
DCHECK(fragment.IsDescendantOfNotSelf(root_));
// Because we may not traverse all descendants of |root_|, just push the
// specified fragment. Computing its ancestors up to |root_| is deferred to
// |MoveToNextSiblingOrAncestor()|.
stack_.resize(0);
Push(fragment);
}
void NGPaintFragmentTraversal::MoveToNext() {
if (IsAtEnd())
return;
if (!current_->Children().IsEmpty()) {
Push(*current_, 0);
return;
}
MoveToNextSiblingOrAncestor();
}
void NGPaintFragmentTraversal::MoveToNextSiblingOrAncestor() {
if (IsAtEnd())
return;
while (true) {
// Check if we have a next sibling.
auto& stack_top = stack_.back();
if (++stack_top.index < stack_top.parent->Children().size()) {
current_ = stack_top.parent->Children()[stack_top.index].get();
return;
}
// Check the next parent in the stack. If the stack is not empty, traverse
// its next sibiling.
stack_.pop_back();
if (!stack_.IsEmpty())
continue;
// We might have started with |MoveTo()|, and thus computing parent stack
// was deferred. Check parents until we reach the |root_|.
const NGPaintFragment* parent = current_->Parent();
DCHECK(parent);
if (parent == &root_) {
current_ = nullptr;
return;
}
Push(*parent);
}
}
Vector<NGPaintFragmentWithContainerOffset>
NGPaintFragmentTraversal::DescendantsOf(const NGPaintFragment& container) {
Vector<NGPaintFragmentWithContainerOffset> result;
......@@ -198,14 +283,7 @@ NGPaintFragmentTraversalContext NGPaintFragmentTraversalContext::Create(
const NGPaintFragment* fragment) {
if (!fragment)
return NGPaintFragmentTraversalContext();
DCHECK(fragment->Parent());
const auto& siblings = fragment->Parent()->Children();
const auto* self_iter = std::find_if(
siblings.begin(), siblings.end(),
[&fragment](const auto& sibling) { return fragment == sibling.get(); });
DCHECK_NE(self_iter, siblings.end());
return {fragment->Parent(), self_iter - siblings.begin()};
return {fragment->Parent(), IndexOfChild(*fragment->Parent(), *fragment)};
}
NGPaintFragmentTraversalContext NGPaintFragmentTraversal::PreviousInlineLeafOf(
......
......@@ -41,10 +41,51 @@ struct CORE_EXPORT NGPaintFragmentTraversalContext {
};
// Utility class for traversing the paint fragment tree.
//
// This class has two groups of functions; one is a traversing cursor, by
// instantiating and using instance functions. The other is a set of static
// functions that are similar to DOM traversal functions.
class CORE_EXPORT NGPaintFragmentTraversal {
STATIC_ONLY(NGPaintFragmentTraversal);
STACK_ALLOCATED();
public:
// Create an instance to traverse descendants of |root|.
explicit NGPaintFragmentTraversal(const NGPaintFragment& root);
// Create an instance to traverse descendants of |root|, starting at |start|.
// Same as constructing with |root| and then |MoveTo()|.
NGPaintFragmentTraversal(const NGPaintFragment& root,
const NGPaintFragment& start);
bool IsAtEnd() const { return !current_; }
explicit operator bool() const { return !IsAtEnd(); }
const NGPaintFragment* get() const {
DCHECK(current_);
return current_;
}
const NGPaintFragment& operator*() const { return *get(); }
const NGPaintFragment* operator->() const { return get(); }
// Move to the specified fragment. The fragment must be a descendant of
// |root|. This function is O(n) where |n| is the number of senior siblings
// before |fragment|.
void MoveTo(const NGPaintFragment& fragment);
// Move to the next node using the pre-order depth-first-search.
void MoveToNext();
// Move to the next sibling, or next ancestor node using the pre-order
// depth-first-search, skipping children of the current node.
void MoveToNextSiblingOrAncestor();
//
// Following functions are static, similar to DOM traversal utilities.
//
// Because fragments have children as a vector, not a two-way list, static
// functions are not as cheap as their DOM versions. When traversing more than
// once, instace functions are recommended.
// Returns descendants without paint layer in preorder.
static Vector<NGPaintFragmentWithContainerOffset> DescendantsOf(
const NGPaintFragment&);
......@@ -73,6 +114,28 @@ class CORE_EXPORT NGPaintFragmentTraversal {
const NGPaintFragmentTraversalContext&);
static NGPaintFragmentTraversalContext NextInlineLeafOfIgnoringLineBreak(
const NGPaintFragmentTraversalContext&);
private:
void Push(const NGPaintFragment& parent, unsigned index);
void Push(const NGPaintFragment& fragment);
const NGPaintFragment* current_ = nullptr;
const NGPaintFragment& root_;
// The stack of parent and its child index up to the root. Each stack entry
// represents the current node, and thus
// |stack_.back().parent->Children()[stack_.back().index] == current_|.
//
// Computing ancestors maybe deferred until |MoveToNextSiblingOrAncestor()|
// when |Moveto()| is used. In that case, the |stack_| does not contain all
// fragments to the |root_|.
struct ParentAndIndex {
const NGPaintFragment* parent;
unsigned index;
};
Vector<ParentAndIndex, 4> stack_;
DISALLOW_COPY_AND_ASSIGN(NGPaintFragmentTraversal);
};
} // namespace blink
......
......@@ -4,12 +4,15 @@
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
namespace blink {
using testing::ElementsAreArray;
class NGPaintFragmentTraversalTest : public RenderingTest,
private ScopedLayoutNGForTest {
public:
......@@ -28,10 +31,98 @@ class NGPaintFragmentTraversalTest : public RenderingTest,
return root_fragment_->Children();
}
Vector<const NGPaintFragment*> ToDepthFirstList(
NGPaintFragmentTraversal* traversal) const {
Vector<const NGPaintFragment*> results;
for (; *traversal; traversal->MoveToNext()) {
const NGPaintFragment& fragment = **traversal;
results.push_back(&fragment);
}
return results;
}
LayoutBlockFlow* layout_block_flow_;
NGPaintFragment* root_fragment_;
};
TEST_F(NGPaintFragmentTraversalTest, MoveToNext) {
SetUpHtml("t", R"HTML(
<div id=t>
line0
<span style="background: red">red</span>
<br>
line1
</div>
)HTML");
NGPaintFragmentTraversal traversal(*root_fragment_);
NGPaintFragment* line0 = root_fragment_->Children()[0].get();
NGPaintFragment* line1 = root_fragment_->Children()[1].get();
NGPaintFragment* span = line0->Children()[1].get();
NGPaintFragment* br = line0->Children()[2].get();
EXPECT_THAT(ToDepthFirstList(&traversal),
ElementsAreArray({line0, line0->Children()[0].get(), span,
span->Children()[0].get(), br, line1,
line1->Children()[0].get()}));
}
TEST_F(NGPaintFragmentTraversalTest, MoveToNextWithRoot) {
SetUpHtml("t", R"HTML(
<div id=t>
line0
<span style="background: red">red</span>
<br>
line1
</div>
)HTML");
NGPaintFragment* line0 = root_fragment_->Children()[0].get();
NGPaintFragment* span = line0->Children()[1].get();
NGPaintFragment* br = line0->Children()[2].get();
NGPaintFragmentTraversal traversal(*line0);
EXPECT_THAT(ToDepthFirstList(&traversal),
ElementsAreArray({line0->Children()[0].get(), span,
span->Children()[0].get(), br}));
}
TEST_F(NGPaintFragmentTraversalTest, MoveTo) {
SetUpHtml("t", R"HTML(
<div id=t>
line0
<span style="background: red">red</span>
<br>
line1
</div>
)HTML");
NGPaintFragmentTraversal traversal(*root_fragment_);
NGPaintFragment* line0 = root_fragment_->Children()[0].get();
NGPaintFragment* line1 = root_fragment_->Children()[1].get();
NGPaintFragment* span = line0->Children()[1].get();
NGPaintFragment* br = line0->Children()[2].get();
traversal.MoveTo(*span);
EXPECT_EQ(span, &*traversal);
EXPECT_THAT(ToDepthFirstList(&traversal),
ElementsAreArray({span, span->Children()[0].get(), br, line1,
line1->Children()[0].get()}));
}
TEST_F(NGPaintFragmentTraversalTest, MoveToWithRoot) {
SetUpHtml("t", R"HTML(
<div id=t>
line0
<span style="background: red">red</span>
<br>
line1
</div>
)HTML");
NGPaintFragment* line0 = root_fragment_->Children()[0].get();
NGPaintFragment* span = line0->Children()[1].get();
NGPaintFragment* br = line0->Children()[2].get();
NGPaintFragmentTraversal traversal(*line0);
traversal.MoveTo(*span);
EXPECT_EQ(span, &*traversal);
EXPECT_THAT(ToDepthFirstList(&traversal),
ElementsAreArray({span, span->Children()[0].get(), br}));
}
TEST_F(NGPaintFragmentTraversalTest, PreviousLineOf) {
SetUpHtml("t", "<div id=t>foo<br>bar</div>");
ASSERT_EQ(2u, RootChildren().size());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment