Commit 6c138bfb authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[FragmentItem] Add NGInlineBackwardCursor

Hit-testing requires traversing siblings in the reverse
order, which is not easy for either |NGPaintFragment| nor
|NGFragmentItem|. Currently |NGBoxFragmentPainter| copies all
siblings to a list and iterate the list in the reverse order.
This is not very efficient, but hit-testing is the only user
today, and seems to be fast enough.

In order to abstract the operation, this patch adds
|NGInlineBackwardCursor|. This is a separate class, to avoid
increasing stack size for every use of |NGInlineCursor|.

We may revisit this if more users appear.

Bug: 982194
Change-Id: Ie07564dccbcfc3ff7222392fc6f483c95e0579eb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1939152Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#719568}
parent 74f89d83
...@@ -1011,6 +1011,51 @@ void NGInlineCursor::MoveToPreviousSiblingPaintFragment() { ...@@ -1011,6 +1011,51 @@ void NGInlineCursor::MoveToPreviousSiblingPaintFragment() {
NOTREACHED(); NOTREACHED();
} }
NGInlineBackwardCursor::NGInlineBackwardCursor(const NGInlineCursor& cursor) {
DCHECK(cursor);
if (cursor.root_paint_fragment_) {
for (NGInlineCursor sibling(cursor); sibling; sibling.MoveToNextSibling())
sibling_paint_fragments_.push_back(sibling.CurrentPaintFragment());
current_index_ = sibling_paint_fragments_.size();
if (current_index_)
current_paint_fragment_ = sibling_paint_fragments_[--current_index_];
return;
}
if (cursor.IsItemCursor()) {
for (NGInlineCursor sibling(cursor); sibling; sibling.MoveToNextSibling())
sibling_item_iterators_.push_back(sibling.item_iter_);
current_index_ = sibling_item_iterators_.size();
if (current_index_)
current_item_ = sibling_item_iterators_[--current_index_]->get();
return;
}
NOTREACHED();
}
NGInlineCursor NGInlineBackwardCursor::CursorForDescendants() const {
if (const NGPaintFragment* current_paint_fragment = CurrentPaintFragment())
return NGInlineCursor(*current_paint_fragment);
// TODO(kojii): Implement for items.
NOTREACHED();
return NGInlineCursor();
}
void NGInlineBackwardCursor::MoveToPreviousSibling() {
if (current_index_) {
if (current_paint_fragment_) {
current_paint_fragment_ = sibling_paint_fragments_[--current_index_];
return;
}
if (current_item_) {
current_item_ = sibling_item_iterators_[--current_index_]->get();
return;
}
NOTREACHED();
}
current_paint_fragment_ = nullptr;
current_item_ = nullptr;
}
std::ostream& operator<<(std::ostream& ostream, const NGInlineCursor& cursor) { std::ostream& operator<<(std::ostream& ostream, const NGInlineCursor& cursor) {
if (cursor.IsNull()) if (cursor.IsNull())
return ostream << "NGInlineCursor()"; return ostream << "NGInlineCursor()";
......
...@@ -348,6 +348,35 @@ class CORE_EXPORT NGInlineCursor { ...@@ -348,6 +348,35 @@ class CORE_EXPORT NGInlineCursor {
// Used in |MoveToNextForSameLayoutObject()| to support culled inline. // Used in |MoveToNextForSameLayoutObject()| to support culled inline.
const LayoutInline* layout_inline_ = nullptr; const LayoutInline* layout_inline_ = nullptr;
friend class NGInlineBackwardCursor;
};
class CORE_EXPORT NGInlineBackwardCursor {
STACK_ALLOCATED();
public:
NGInlineBackwardCursor(const NGInlineCursor& cursor);
NGInlineCursor CursorForDescendants() const;
explicit operator bool() const {
return current_paint_fragment_ || current_item_;
}
const NGFragmentItem* CurrentItem() const { return current_item_; }
const NGPaintFragment* CurrentPaintFragment() const {
return current_paint_fragment_;
}
void MoveToPreviousSibling();
private:
Vector<const NGPaintFragment*, 16> sibling_paint_fragments_;
Vector<NGInlineCursor::ItemsSpan::iterator, 16> sibling_item_iterators_;
const NGPaintFragment* current_paint_fragment_ = nullptr;
const NGFragmentItem* current_item_ = nullptr;
wtf_size_t current_index_;
}; };
CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGInlineCursor&); CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGInlineCursor&);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/layout/layout_text.h" #include "third_party/blink/renderer/core/layout/layout_text.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
...@@ -35,6 +36,40 @@ class NGInlineCursorTest : public NGLayoutTest, ...@@ -35,6 +36,40 @@ class NGInlineCursorTest : public NGLayoutTest,
return list; return list;
} }
Vector<String> SiblingsToDebugStringList(const NGInlineCursor& start) {
Vector<String> list;
for (NGInlineCursor cursor(start); cursor; cursor.MoveToNextSibling())
list.push_back(ToDebugString(cursor));
return list;
}
// Test |MoveToNextSibling| and |NGInlineBackwardCursor| return the same
// instances, except that the order is reversed.
void TestPrevoiusSibling(const NGInlineCursor& start) {
if (start.IsPaintFragmentCursor()) {
Vector<const NGPaintFragment*> forwards;
for (NGInlineCursor cursor(start); cursor; cursor.MoveToNextSibling())
forwards.push_back(cursor.CurrentPaintFragment());
Vector<const NGPaintFragment*> backwards;
for (NGInlineBackwardCursor cursor(start); cursor;
cursor.MoveToPreviousSibling())
backwards.push_back(cursor.CurrentPaintFragment());
backwards.Reverse();
EXPECT_THAT(backwards, forwards);
return;
}
DCHECK(start.IsItemCursor());
Vector<const NGFragmentItem*> forwards;
for (NGInlineCursor cursor(start); cursor; cursor.MoveToNextSibling())
forwards.push_back(cursor.CurrentItem());
Vector<const NGFragmentItem*> backwards;
for (NGInlineBackwardCursor cursor(start); cursor;
cursor.MoveToPreviousSibling())
backwards.push_back(cursor.CurrentItem());
backwards.Reverse();
EXPECT_THAT(backwards, forwards);
}
String ToDebugString(const NGInlineCursor& cursor) { String ToDebugString(const NGInlineCursor& cursor) {
if (cursor.IsLineBox()) if (cursor.IsLineBox())
return "#linebox"; return "#linebox";
...@@ -510,33 +545,27 @@ TEST_P(NGInlineCursorTest, NextForSameLayoutObject) { ...@@ -510,33 +545,27 @@ TEST_P(NGInlineCursorTest, NextForSameLayoutObject) {
EXPECT_THAT(list, ElementsAre("abc", "", "def", "", "ghi")); EXPECT_THAT(list, ElementsAre("abc", "", "def", "", "ghi"));
} }
TEST_P(NGInlineCursorTest, NextSibling) { TEST_P(NGInlineCursorTest, Sibling) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline. // TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }"); InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor = NGInlineCursor cursor =
SetupCursor("<div id=root>abc<a>DEF<b>GHI</b></a>xyz</div>"); SetupCursor("<div id=root>abc<a>DEF<b>GHI</b></a>xyz</div>");
cursor.MoveToFirstChild(); // go to "abc" cursor.MoveToFirstChild(); // go to "abc"
Vector<String> list; Vector<String> list = SiblingsToDebugStringList(cursor);
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToNextSibling();
}
EXPECT_THAT(list, ElementsAre("abc", "LayoutInline A", "xyz")); EXPECT_THAT(list, ElementsAre("abc", "LayoutInline A", "xyz"));
TestPrevoiusSibling(cursor);
} }
TEST_P(NGInlineCursorTest, NextSibling2) { TEST_P(NGInlineCursorTest, Sibling2) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline. // TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }"); InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor = NGInlineCursor cursor =
SetupCursor("<div id=root><a>abc<b>def</b>xyz</a></div>"); SetupCursor("<div id=root><a>abc<b>def</b>xyz</a></div>");
cursor.MoveToFirstChild(); // go to <a>abc</a> cursor.MoveToFirstChild(); // go to <a>abc</a>
cursor.MoveToFirstChild(); // go to "abc" cursor.MoveToFirstChild(); // go to "abc"
Vector<String> list; Vector<String> list = SiblingsToDebugStringList(cursor);
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToNextSibling();
}
EXPECT_THAT(list, ElementsAre("abc", "LayoutInline B", "xyz")); EXPECT_THAT(list, ElementsAre("abc", "LayoutInline B", "xyz"));
TestPrevoiusSibling(cursor);
} }
TEST_P(NGInlineCursorTest, NextSkippingChildren) { TEST_P(NGInlineCursorTest, NextSkippingChildren) {
......
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