blob: 2c2c748eb5f29c77984671d0540d51122c2580b5 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "testing/gmock/include/gmock/gmock.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/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/ng_layout_test.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
namespace blink {
namespace {
using ::testing::ElementsAre;
String ToDebugString(const NGInlineCursor& cursor) {
if (cursor.Current().IsLineBox())
return "#linebox";
if (cursor.Current().IsLayoutGeneratedText()) {
StringBuilder result;
result.Append("#'");
result.Append(cursor.CurrentText());
result.Append("'");
return result.ToString();
}
if (cursor.Current().IsText())
return cursor.CurrentText().ToString().StripWhiteSpace();
if (const LayoutObject* layout_object = cursor.Current().GetLayoutObject()) {
if (const Element* element = DynamicTo<Element>(layout_object->GetNode())) {
if (const AtomicString& id = element->GetIdAttribute())
return "#" + id;
}
return layout_object->DebugName();
}
return "#null";
}
Vector<String> LayoutObjectToDebugStringList(NGInlineCursor cursor) {
Vector<String> list;
for (; cursor; cursor.MoveToNextForSameLayoutObject())
list.push_back(ToDebugString(cursor));
return list;
}
class NGInlineCursorTest : public NGLayoutTest,
public testing::WithParamInterface<bool> {
protected:
NGInlineCursor SetupCursor(const String& html) {
SetBodyInnerHTML(html);
const LayoutBlockFlow& block_flow =
*To<LayoutBlockFlow>(GetLayoutObjectByElementId("root"));
return NGInlineCursor(block_flow);
}
Vector<String> ToDebugStringList(const NGInlineCursor& start) {
Vector<String> list;
for (NGInlineCursor cursor(start); cursor; cursor.MoveToNext())
list.push_back(ToDebugString(cursor));
return list;
}
Vector<String> SiblingsToDebugStringList(const NGInlineCursor& start) {
Vector<String> list;
for (NGInlineCursor cursor(start); cursor;
cursor.MoveToNextSkippingChildren())
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) {
DCHECK(start.HasRoot());
Vector<const NGFragmentItem*> forwards;
for (NGInlineCursor cursor(start); cursor;
cursor.MoveToNextSkippingChildren())
forwards.push_back(cursor.CurrentItem());
Vector<const NGFragmentItem*> backwards;
for (NGInlineBackwardCursor cursor(start); cursor;
cursor.MoveToPreviousSibling())
backwards.push_back(cursor.Current().Item());
backwards.Reverse();
EXPECT_THAT(backwards, forwards);
}
Vector<String> ToDebugStringListWithBidiLevel(const NGInlineCursor& start) {
Vector<String> list;
for (NGInlineCursor cursor(start); cursor; cursor.MoveToNext()) {
// Inline boxes do not have bidi level.
if (cursor.Current().IsInlineBox())
continue;
list.push_back(ToDebugStringWithBidiLevel(cursor));
}
return list;
}
String ToDebugStringWithBidiLevel(const NGInlineCursor& cursor) {
if (!cursor.Current().IsText() && !cursor.Current().IsAtomicInline())
return ToDebugString(cursor);
StringBuilder result;
result.Append(ToDebugString(cursor));
result.Append(':');
result.AppendNumber(cursor.Current().BidiLevel());
return result.ToString();
}
};
INSTANTIATE_TEST_SUITE_P(NGInlineCursorTest,
NGInlineCursorTest,
testing::Bool());
TEST_P(NGInlineCursorTest, BidiLevelInlineBoxLTR) {
InsertStyleElement("b { display: inline-block; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root dir=ltr>"
"abc<b id=def>def</b><bdo dir=rtl><b id=ghi>GHI</b></bdo>jkl</div>");
Vector<String> list = ToDebugStringListWithBidiLevel(cursor);
EXPECT_THAT(list,
ElementsAre("#linebox", "abc:0", "#def:0", "#ghi:1", "jkl:0"));
}
TEST_P(NGInlineCursorTest, BidiLevelInlineBoxRTL) {
InsertStyleElement("b { display: inline-block; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root dir=rtl>"
"abc<b id=def>def</b><bdo dir=rtl><b id=ghi>GHI</b></bdo>jkl</div>");
Vector<String> list = ToDebugStringListWithBidiLevel(cursor);
EXPECT_THAT(list,
ElementsAre("#linebox", "#ghi:3", "jkl:2", "#def:1", "abc:2"));
}
TEST_P(NGInlineCursorTest, BidiLevelSimpleLTR) {
NGInlineCursor cursor = SetupCursor(
"<div id=root dir=ltr>"
"<bdo dir=rtl>GHI<bdo dir=ltr>abc</bdo>DEF</bdo><br>"
"123, jkl <bdo dir=rtl>MNO</bdo></div>");
Vector<String> list = ToDebugStringListWithBidiLevel(cursor);
EXPECT_THAT(list, ElementsAre("#linebox", "DEF:1", "abc:2", "GHI:1", ":0",
"#linebox", "123, jkl:0", "MNO:1"));
}
TEST_P(NGInlineCursorTest, BidiLevelSimpleRTL) {
NGInlineCursor cursor = SetupCursor(
"<div id=root dir=rtl>"
"<bdo dir=rtl>GHI<bdo dir=ltr>abc</bdo>DEF</bdo><br>"
"123, jkl <bdo dir=rtl>MNO</bdo></div>");
Vector<String> list = ToDebugStringListWithBidiLevel(cursor);
EXPECT_THAT(
list, ElementsAre("#linebox", ":0", "DEF:3", "abc:4", "GHI:3", "#linebox",
"MNO:3", ":1", "jkl:2", ",:1", "123:2"));
}
TEST_P(NGInlineCursorTest, GetLayoutBlockFlowWithScopedCursor) {
NGInlineCursor line = SetupCursor("<div id=root>line1<br>line2</div>");
ASSERT_TRUE(line.Current().IsLineBox()) << line;
NGInlineCursor cursor = line.CursorForDescendants();
EXPECT_EQ(line.GetLayoutBlockFlow(), cursor.GetLayoutBlockFlow());
}
TEST_P(NGInlineCursorTest, ContainingLine) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<a id=target>def</a>ghi<br>xyz</div>");
const LayoutBlockFlow& block_flow = *cursor.GetLayoutBlockFlow();
NGInlineCursor line1(cursor);
ASSERT_TRUE(line1.Current().IsLineBox());
NGInlineCursor line2(line1);
line2.MoveToNextSkippingChildren();
ASSERT_TRUE(line2.Current().IsLineBox());
cursor.MoveTo(*block_flow.FirstChild());
cursor.MoveToContainingLine();
EXPECT_EQ(line1, cursor);
const auto& target = To<LayoutInline>(*GetLayoutObjectByElementId("target"));
cursor.MoveTo(target);
cursor.MoveToContainingLine();
EXPECT_EQ(line1, cursor);
cursor.MoveTo(*target.FirstChild());
cursor.MoveToContainingLine();
EXPECT_EQ(line1, cursor);
cursor.MoveTo(*block_flow.LastChild());
cursor.MoveToContainingLine();
EXPECT_EQ(line2, cursor);
}
TEST_P(NGInlineCursorTest, CulledInlineWithAtomicInline) {
SetBodyInnerHTML(
"<div id=root>"
"<b id=culled>abc<div style=display:inline>ABC<br>XYZ</div>xyz</b>"
"</div>");
NGInlineCursor cursor;
cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled"));
EXPECT_THAT(LayoutObjectToDebugStringList(cursor),
ElementsAre("abc", "ABC", "", "XYZ", "xyz"));
}
// We should not have float:right fragment, because it isn't in-flow in
// an inline formatting context.
// For https://crbug.com/1026022
TEST_P(NGInlineCursorTest, CulledInlineWithFloat) {
SetBodyInnerHTML(
"<div id=root>"
"<b id=culled>abc<div style=float:right></div>xyz</b>"
"</div>");
NGInlineCursor cursor;
cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled"));
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("abc", "xyz"));
}
TEST_P(NGInlineCursorTest, CulledInlineWithOOF) {
SetBodyInnerHTML(R"HTML(
<div id=root>
<b id=culled>abc<span style="position:absolute"></span>xyz</b>
</div>
)HTML");
NGInlineCursor cursor;
cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled"));
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("abc", "xyz"));
}
TEST_P(NGInlineCursorTest, CulledInlineNested) {
SetBodyInnerHTML(R"HTML(
<div id=root>
<b id=culled><span>abc</span> xyz</b>
</div>
)HTML");
NGInlineCursor cursor;
cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled"));
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("abc", "xyz"));
}
TEST_P(NGInlineCursorTest, CulledInlineBlockChild) {
SetBodyInnerHTML(R"HTML(
<div id=root>
<b id=culled>
<div>block</div>
<span>abc</span> xyz
</b>
</div>
)HTML");
NGInlineCursor cursor;
cursor.MoveToIncludingCulledInline(*GetLayoutObjectByElementId("culled"));
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("#culled"));
}
TEST_P(NGInlineCursorTest, CulledInlineWithRoot) {
NGInlineCursor cursor = SetupCursor(R"HTML(
<div id="root"><a id="a"><b>abc</b><br><i>xyz</i></a></div>
)HTML");
const LayoutObject* layout_inline_a = GetLayoutObjectByElementId("a");
cursor.MoveToIncludingCulledInline(*layout_inline_a);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor),
ElementsAre("abc", "", "xyz"));
}
TEST_P(NGInlineCursorTest, CulledInlineWithoutRoot) {
SetBodyInnerHTML(R"HTML(
<div id="root"><a id="a"><b>abc</b><br><i>xyz</i></a></div>
)HTML");
const LayoutObject* layout_inline_a = GetLayoutObjectByElementId("a");
NGInlineCursor cursor;
cursor.MoveToIncludingCulledInline(*layout_inline_a);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor),
ElementsAre("abc", "", "xyz"));
}
TEST_P(NGInlineCursorTest, FirstChild) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<a>DEF<b>GHI</b></a>xyz</div>");
cursor.MoveToFirstChild();
EXPECT_EQ("abc", ToDebugString(cursor));
EXPECT_FALSE(cursor.TryToMoveToFirstChild());
}
TEST_P(NGInlineCursorTest, FirstChild2) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root><b id=first>abc</b><a>DEF<b>GHI</b></a><a "
"id=last>xyz</a></div>");
cursor.MoveToFirstChild();
EXPECT_EQ("#first", ToDebugString(cursor));
cursor.MoveToFirstChild();
EXPECT_EQ("abc", ToDebugString(cursor));
EXPECT_FALSE(cursor.TryToMoveToFirstChild());
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInSimpleText) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root><b>first</b><b>middle</b><b>last</b></div>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("first", ToDebugString(first_logical_leaf));
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("last", ToDebugString(last_logical_leaf));
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInRtlText) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<bdo id=root dir=rtl style=display:block>"
"<b>first</b><b>middle</b><b>last</b>"
"</bdo>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("first", ToDebugString(first_logical_leaf));
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("last", ToDebugString(last_logical_leaf));
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInTextAsDeepDescendants) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root>"
"<b><b>first</b>ABC</b>"
"<b>middle</b>"
"<b>DEF<b>last</b></b>"
"</div>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("first", ToDebugString(first_logical_leaf));
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("last", ToDebugString(last_logical_leaf));
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafWithInlineBlock) {
InsertStyleElement("b { display: inline-block; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root>"
"<b id=first>first</b>middle<b id=last>last</b>"
"</div>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("#first", ToDebugString(first_logical_leaf))
<< "stop at inline-block";
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("#last", ToDebugString(last_logical_leaf))
<< "stop at inline-block";
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafWithImages) {
NGInlineCursor cursor =
SetupCursor("<div id=root><img id=first>middle<img id=last></div>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("#first", ToDebugString(first_logical_leaf));
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("#last", ToDebugString(last_logical_leaf));
}
TEST_P(NGInlineCursorTest, IsEmptyLineBox) {
InsertStyleElement("b { margin-bottom: 1px; }");
NGInlineCursor cursor = SetupCursor("<div id=root>abc<br><b></b></div>");
EXPECT_FALSE(cursor.Current().IsEmptyLineBox())
<< "'abc\\n' is in non-empty line box.";
cursor.MoveToNextLine();
EXPECT_TRUE(cursor.Current().IsEmptyLineBox())
<< "<b></b> with margin produces empty line box.";
}
TEST_P(NGInlineCursorTest, LastChild) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<a>DEF<b>GHI</b></a>xyz</div>");
cursor.MoveToLastChild();
EXPECT_EQ("xyz", ToDebugString(cursor));
EXPECT_FALSE(cursor.TryToMoveToLastChild());
}
TEST_P(NGInlineCursorTest, LastChild2) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root><b id=first>abc</b><a>DEF<b>GHI</b></a>"
"<a id=last>xyz</a></div>");
cursor.MoveToLastChild();
EXPECT_EQ("#last", ToDebugString(cursor));
cursor.MoveToLastChild();
EXPECT_EQ("xyz", ToDebugString(cursor));
EXPECT_FALSE(cursor.TryToMoveToLastChild());
}
TEST_P(NGInlineCursorTest, Next) {
SetBodyInnerHTML(R"HTML(
<style>
span { background: gray; }
</style>
<div id=root>
text1
<span id="span1">
text2
<span id="span2">
text3
</span>
text4
</span>
text5
</div>
)HTML");
LayoutBlockFlow* block_flow =
To<LayoutBlockFlow>(GetLayoutObjectByElementId("root"));
NGInlineCursor cursor(*block_flow);
Vector<String> list = ToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre("#linebox", "text1", "#span1", "text2",
"#span2", "text3", "text4", "text5"));
}
TEST_P(NGInlineCursorTest, NextWithEllipsis) {
LoadAhem();
InsertStyleElement(
"#root {"
"font: 10px/10px Ahem;"
"width: 5ch;"
"overflow-x: hidden;"
"text-overflow: ellipsis;"
"}");
NGInlineCursor cursor = SetupCursor("<div id=root>abcdefghi</div>");
Vector<String> list = ToDebugStringList(cursor);
// Note: "abcdefghi" is hidden for paint.
EXPECT_THAT(list, ElementsAre("#linebox", "abcdefghi", "abcd", u"#'\u2026'"));
}
TEST_P(NGInlineCursorTest, NextWithEllipsisInlineBoxOnly) {
LoadAhem();
InsertStyleElement(
"#root {"
"font: 10px/1 Ahem;"
"width: 5ch;"
"overflow: hidden;"
"text-overflow: ellipsis;"
"}"
"span { border: solid 10ch blue; }");
NGInlineCursor cursor = SetupCursor("<div id=root><span></span></div>");
Vector<String> list = ToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre("#linebox", "LayoutInline SPAN"));
}
TEST_P(NGInlineCursorTest, NextWithListItem) {
NGInlineCursor cursor = SetupCursor("<ul><li id=root>abc</li></ul>");
Vector<String> list = ToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre("LayoutNGOutsideListMarker ::marker",
"#linebox", "abc"));
EXPECT_EQ(GetLayoutObjectByElementId("root"), cursor.GetLayoutBlockFlow());
}
TEST_P(NGInlineCursorTest, NextWithSoftHyphens) {
// Use "Ahem" font to get U+2010 as soft hyphen instead of U+002D
LoadAhem();
InsertStyleElement("#root {width: 3ch; font: 10px/10px Ahem;}");
NGInlineCursor cursor = SetupCursor("<div id=root>abc&shy;def</div>");
Vector<String> list = ToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre("#linebox", u"abc\u00AD", u"#'\u2010'",
"#linebox", "def"));
}
TEST_P(NGInlineCursorTest, NextInlineLeaf) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<b>DEF</b><br>xyz</div>");
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToNextInlineLeaf();
}
EXPECT_THAT(list, ElementsAre("#linebox", "abc", "DEF", "", "xyz"));
}
// Note: This is for AccessibilityLayoutTest.NextOnLine.
TEST_P(NGInlineCursorTest, NextInlineLeafOnLineFromLayoutInline) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root>"
"<b id=start>abc</b> def<br>"
"<b>ABC</b> DEF<br>"
"</div>");
cursor.MoveTo(*GetElementById("start")->GetLayoutObject());
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToNextInlineLeafOnLine();
}
EXPECT_THAT(list, ElementsAre("#start", "def", ""))
<< "we don't have 'abc' and items in second line.";
}
TEST_P(NGInlineCursorTest, NextInlineLeafOnLineFromLayoutText) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root>"
"<b id=start>abc</b> def<br>"
"<b>ABC</b> DEF<br>"
"</div>");
cursor.MoveTo(*GetElementById("start")->firstChild()->GetLayoutObject());
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToNextInlineLeafOnLine();
}
EXPECT_THAT(list, ElementsAre("abc", "def", ""))
<< "We don't have items from second line.";
}
TEST_P(NGInlineCursorTest, NextInlineLeafWithEllipsis) {
LoadAhem();
InsertStyleElement(
"#root {"
"font: 10px/10px Ahem;"
"width: 5ch;"
"overflow-x: hidden;"
"text-overflow: ellipsis;"
"}");
NGInlineCursor cursor = SetupCursor("<div id=root>abcdefghi</div>");
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToNextInlineLeaf();
}
// Note: We don't see hidden for paint and generated soft hyphen.
// See also |NextWithEllipsis|.
EXPECT_THAT(list, ElementsAre("#linebox", "abcd"));
}
TEST_P(NGInlineCursorTest, NextInlineLeafWithSoftHyphens) {
NGInlineCursor cursor =
SetupCursor("<div id=root style='width:3ch'>abc&shy;def</div>");
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToNextInlineLeaf();
}
// Note: We don't see generated soft hyphen. See also |NextWithSoftHyphens|.
EXPECT_THAT(list, ElementsAre("#linebox", u"abc\u00AD", "def"));
}
TEST_P(NGInlineCursorTest, NextInlineLeafIgnoringLineBreak) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<b>DEF</b><br>xyz</div>");
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToNextInlineLeafIgnoringLineBreak();
}
EXPECT_THAT(list, ElementsAre("#linebox", "abc", "DEF", "xyz"));
}
TEST_P(NGInlineCursorTest, NextLine) {
NGInlineCursor cursor = SetupCursor("<div id=root>abc<br>xyz</div>");
NGInlineCursor line1(cursor);
while (line1 && !line1.Current().IsLineBox())
line1.MoveToNext();
ASSERT_TRUE(line1.IsNotNull());
NGInlineCursor line2(line1);
line2.MoveToNext();
while (line2 && !line2.Current().IsLineBox())
line2.MoveToNext();
ASSERT_NE(line1, line2);
NGInlineCursor should_be_line2(line1);
should_be_line2.MoveToNextLine();
EXPECT_EQ(line2, should_be_line2);
NGInlineCursor should_be_null(line2);
should_be_null.MoveToNextLine();
EXPECT_TRUE(should_be_null.IsNull());
}
TEST_P(NGInlineCursorTest, NextWithImage) {
NGInlineCursor cursor = SetupCursor("<div id=root>abc<img id=img>xyz</div>");
Vector<String> list = ToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre("#linebox", "abc", "#img", "xyz"));
}
TEST_P(NGInlineCursorTest, NextWithInlineBox) {
InsertStyleElement("b { display: inline-block; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<b id=ib>def</b>xyz</div>");
Vector<String> list = ToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre("#linebox", "abc", "#ib", "xyz"));
NGInlineCursor cursor2;
cursor2.MoveTo(*GetElementById("ib")->firstChild()->GetLayoutObject());
EXPECT_EQ(GetLayoutObjectByElementId("ib"), cursor2.GetLayoutBlockFlow());
}
TEST_P(NGInlineCursorTest, NextForSameLayoutObject) {
NGInlineCursor cursor = SetupCursor("<pre id=root>abc\ndef\nghi</pre>");
cursor.MoveTo(*GetLayoutObjectByElementId("root")->SlowFirstChild());
EXPECT_THAT(LayoutObjectToDebugStringList(cursor),
ElementsAre("abc", "", "def", "", "ghi"));
}
// Test |NextForSameLayoutObject| with limit range set.
TEST_P(NGInlineCursorTest, NextForSameLayoutObjectWithRange) {
// In this snippet, `<span>` wraps to 3 lines, and that there are 3 fragments
// for `<span>`.
SetBodyInnerHTML(R"HTML(
<style>
div {
font-size: 10px;
width: 5ch;
}
span {
background: orange;
}
</style>
<div id="root">
<span id="span1">
1111
2222
3333
</span>
</div>
)HTML");
LayoutBlockFlow* root =
To<LayoutBlockFlow>(GetLayoutObjectByElementId("root"));
NGInlineCursor cursor(*root);
cursor.MoveToFirstLine();
cursor.MoveToNextLine();
NGInlineCursor line2 = cursor.CursorForDescendants();
// Now |line2| is limited to the 2nd line. There should be only one framgnet
// for `<span>` if we search using `line2`.
LayoutObject* span1 = GetLayoutObjectByElementId("span1");
line2.MoveTo(*span1);
EXPECT_THAT(LayoutObjectToDebugStringList(line2), ElementsAre("#span1"));
}
TEST_P(NGInlineCursorTest, Sibling) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<a>DEF<b>GHI</b></a>xyz</div>");
TestPrevoiusSibling(cursor.CursorForDescendants());
cursor.MoveToFirstChild(); // go to "abc"
Vector<String> list = SiblingsToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre("abc", "LayoutInline A", "xyz"));
}
TEST_P(NGInlineCursorTest, Sibling2) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root><a>abc<b>def</b>xyz</a></div>");
cursor.MoveToFirstChild(); // go to <a>abc</a>
TestPrevoiusSibling(cursor.CursorForDescendants());
cursor.MoveToFirstChild(); // go to "abc"
Vector<String> list = SiblingsToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre("abc", "LayoutInline B", "xyz"));
}
TEST_P(NGInlineCursorTest, NextSkippingChildren) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("span { background: gray; }");
SetBodyInnerHTML(R"HTML(
<div id=root>
text1
<span id="span1">
text2
<span id="span2">
text3
</span>
text4
</span>
text5
</div>
)HTML");
LayoutBlockFlow* block_flow =
To<LayoutBlockFlow>(GetLayoutObjectByElementId("root"));
NGInlineCursor cursor(*block_flow);
for (unsigned i = 0; i < 3; ++i)
cursor.MoveToNext();
EXPECT_EQ("text2", ToDebugString(cursor));
Vector<String> list;
while (true) {
cursor.MoveToNextSkippingChildren();
if (!cursor)
break;
list.push_back(ToDebugString(cursor));
}
EXPECT_THAT(list, ElementsAre("#span2", "text4", "text5"));
}
TEST_P(NGInlineCursorTest, EmptyOutOfFlow) {
SetBodyInnerHTML(R"HTML(
<div id=root>
<span style="position: absolute"></span>
</div>
)HTML");
LayoutBlockFlow* block_flow =
To<LayoutBlockFlow>(GetLayoutObjectByElementId("root"));
NGInlineCursor cursor(*block_flow);
Vector<String> list = ToDebugStringList(cursor);
EXPECT_THAT(list, ElementsAre());
}
TEST_P(NGInlineCursorTest, PositionForPointInChildHorizontalLTR) {
LoadAhem();
InsertStyleElement(
"p {"
"direction: ltr;"
"font: 10px/20px Ahem;"
"padding: 10px;"
"writing-mode: horizontal-tb;"
"}");
NGInlineCursor cursor = SetupCursor("<p id=root>ab</p>");
const auto& text = *To<Text>(GetElementById("root")->firstChild());
ASSERT_TRUE(cursor.Current().IsLineBox());
EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
cursor.Current().RectInContainerFragment());
cursor.MoveTo(*text.GetLayoutObject());
EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 15), PhysicalSize(20, 10)),
cursor.Current().RectInContainerFragment());
const PhysicalOffset left_top = cursor.Current().OffsetInContainerFragment();
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(-5, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top));
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(5, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(10, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(15, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top + PhysicalOffset(20, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top + PhysicalOffset(25, 0)));
}
TEST_P(NGInlineCursorTest, PositionForPointInChildHorizontalRTL) {
LoadAhem();
InsertStyleElement(
"p {"
"direction: rtl;"
"font: 10px/20px Ahem;"
"padding: 10px;"
"writing-mode: horizontal-tb;"
"}");
NGInlineCursor cursor = SetupCursor("<p id=root><bdo dir=rtl>AB</bdo></p>");
const auto& text =
*To<Text>(GetElementById("root")->firstChild()->firstChild());
ASSERT_TRUE(cursor.Current().IsLineBox());
EXPECT_EQ(PhysicalRect(PhysicalOffset(754, 10), PhysicalSize(20, 20)),
cursor.Current().RectInContainerFragment());
cursor.MoveTo(*text.GetLayoutObject());
EXPECT_EQ(PhysicalRect(PhysicalOffset(754, 15), PhysicalSize(20, 10)),
cursor.Current().RectInContainerFragment());
const PhysicalOffset left_top = cursor.Current().OffsetInContainerFragment();
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top + PhysicalOffset(-5, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top));
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top + PhysicalOffset(5, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(10, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(15, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(20, 0)));
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(25, 0)));
}
TEST_P(NGInlineCursorTest, PositionForPointInChildVerticalLTR) {
LoadAhem();
InsertStyleElement(
"p {"
"direction: ltr;"
"font: 10px/20px Ahem;"
"padding: 10px;"
"writing-mode: vertical-lr;"
"}");
NGInlineCursor cursor = SetupCursor("<p id=root>ab</p>");
const auto& text = *To<Text>(GetElementById("root")->firstChild());
ASSERT_TRUE(cursor.Current().IsLineBox());
EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
cursor.Current().RectInContainerFragment());
cursor.MoveTo(*text.GetLayoutObject());
EXPECT_EQ(PhysicalRect(PhysicalOffset(15, 10), PhysicalSize(10, 20)),
cursor.Current().RectInContainerFragment());
const PhysicalOffset left_top = cursor.Current().OffsetInContainerFragment();
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, -5)));
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top));
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 5)));
EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 10)));
EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 15)));
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 20)));
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 25)));
}
TEST_P(NGInlineCursorTest, PositionForPointInChildVerticalRTL) {
LoadAhem();
InsertStyleElement(
"p {"
"direction: rtl;"
"font: 10px/20px Ahem;"
"padding: 10px;"
"writing-mode: vertical-rl;"
"}");
NGInlineCursor cursor = SetupCursor("<p id=root><bdo dir=rtl>AB</bdo></p>");
const auto& text =
*To<Text>(GetElementById("root")->firstChild()->firstChild());
ASSERT_TRUE(cursor.Current().IsLineBox());
EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
cursor.Current().RectInContainerFragment());
cursor.MoveTo(*text.GetLayoutObject());
EXPECT_EQ(PhysicalRect(PhysicalOffset(15, 10), PhysicalSize(10, 20)),
cursor.Current().RectInContainerFragment());
const PhysicalOffset left_top = cursor.Current().OffsetInContainerFragment();
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, -5)));
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top));
EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 5)));
EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 10)));
EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 15)));
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 20)));
EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 25)));
}
// For http://crbug.com/1096110
TEST_P(NGInlineCursorTest, PositionForPointInChildBlockChildren) {
InsertStyleElement("b { display: inline-block; }");
// Note: <b>.ChildrenInline() == false
NGInlineCursor cursor =
SetupCursor("<div id=root>a<b id=target><div>x</div></b></div>");
const Element& target = *GetElementById("target");
cursor.MoveTo(*target.GetLayoutObject());
EXPECT_EQ(PositionWithAffinity(Position::FirstPositionInNode(target)),
cursor.PositionForPointInChild(PhysicalOffset()));
}
TEST_P(NGInlineCursorTest, Previous) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<b>DEF</b><br>xyz</div>");
cursor.MoveTo(*cursor.GetLayoutBlockFlow()->LastChild());
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToPrevious();
}
EXPECT_THAT(list, ElementsAre("xyz", "#linebox", "", "DEF", "LayoutInline B",
"abc", "#linebox"));
}
TEST_P(NGInlineCursorTest, PreviousInlineLeaf) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<b>DEF</b><br>xyz</div>");
cursor.MoveTo(*cursor.GetLayoutBlockFlow()->LastChild());
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToPreviousInlineLeaf();
}
EXPECT_THAT(list, ElementsAre("xyz", "", "DEF", "abc"));
}
TEST_P(NGInlineCursorTest, PreviousInlineLeafIgnoringLineBreak) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root>abc<b>DEF</b><br>xyz</div>");
cursor.MoveTo(*cursor.GetLayoutBlockFlow()->LastChild());
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToPreviousInlineLeafIgnoringLineBreak();
}
EXPECT_THAT(list, ElementsAre("xyz", "DEF", "abc"));
}
TEST_P(NGInlineCursorTest, PreviousInlineLeafOnLineFromLayoutInline) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root>"
"<b>abc</b> def<br>"
"<b>ABC</b> <b id=start>DEF</b><br>"
"</div>");
cursor.MoveTo(*GetElementById("start")->GetLayoutObject());
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToPreviousInlineLeafOnLine();
}
EXPECT_THAT(list, ElementsAre("#start", "ABC"))
<< "We don't have 'DEF' and items in first line.";
}
TEST_P(NGInlineCursorTest, PreviousInlineLeafOnLineFromLayoutText) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root>"
"<b>abc</b> def<br>"
"<b>ABC</b> <b id=start>DEF</b><br>"
"</div>");
cursor.MoveTo(*GetElementById("start")->firstChild()->GetLayoutObject());
Vector<String> list;
while (cursor) {
list.push_back(ToDebugString(cursor));
cursor.MoveToPreviousInlineLeafOnLine();
}
EXPECT_THAT(list, ElementsAre("DEF", "", "ABC"))
<< "We don't have items in first line.";
}
TEST_P(NGInlineCursorTest, PreviousLine) {
NGInlineCursor cursor = SetupCursor("<div id=root>abc<br>xyz</div>");
NGInlineCursor line1(cursor);
while (line1 && !line1.Current().IsLineBox())
line1.MoveToNext();
ASSERT_TRUE(line1.IsNotNull());
NGInlineCursor line2(line1);
line2.MoveToNext();
while (line2 && !line2.Current().IsLineBox())
line2.MoveToNext();
ASSERT_NE(line1, line2);
NGInlineCursor should_be_null(line1);
should_be_null.MoveToPreviousLine();
EXPECT_TRUE(should_be_null.IsNull());
NGInlineCursor should_be_line1(line2);
should_be_line1.MoveToPreviousLine();
EXPECT_EQ(line1, should_be_line1);
}
TEST_P(NGInlineCursorTest, CursorForDescendants) {
SetBodyInnerHTML(R"HTML(
<style>
span { background: yellow; }
</style>
<div id=root>
text1
<span id="span1">
text2
<span id="span2">
text3
</span>
text4
</span>
text5
<span id="span3">
text6
</span>
text7
</div>
)HTML");
LayoutBlockFlow* block_flow =
To<LayoutBlockFlow>(GetLayoutObjectByElementId("root"));
NGInlineCursor cursor(*block_flow);
EXPECT_TRUE(cursor.Current().IsLineBox());
cursor.MoveToNext();
EXPECT_TRUE(cursor.Current().IsText());
EXPECT_THAT(ToDebugStringList(cursor.CursorForDescendants()), ElementsAre());
cursor.MoveToNext();
EXPECT_EQ(ToDebugString(cursor), "#span1");
EXPECT_THAT(ToDebugStringList(cursor.CursorForDescendants()),
ElementsAre("text2", "#span2", "text3", "text4"));
cursor.MoveToNext();
EXPECT_EQ(ToDebugString(cursor), "text2");
EXPECT_THAT(ToDebugStringList(cursor.CursorForDescendants()), ElementsAre());
cursor.MoveToNext();
EXPECT_EQ(ToDebugString(cursor), "#span2");
EXPECT_THAT(ToDebugStringList(cursor.CursorForDescendants()),
ElementsAre("text3"));
}
class NGInlineCursorBlockFragmentationTest
: public NGLayoutTest,
private ScopedLayoutNGBlockFragmentationForTest {
public:
NGInlineCursorBlockFragmentationTest()
: ScopedLayoutNGBlockFragmentationForTest(true) {}
};
TEST_F(NGInlineCursorBlockFragmentationTest, MoveToLayoutObject) {
// This creates 3 columns, 1 line in each column.
SetBodyInnerHTML(R"HTML(
<style>
#container {
column-width: 6ch;
font-family: monospace;
font-size: 10px;
height: 1.5em;
}
</style>
<div id="container">
<span id="span1">1111 22</span><span id="span2">33 4444</span>
</div>
)HTML");
const LayoutObject* span1 = GetLayoutObjectByElementId("span1");
const LayoutObject* text1 = span1->SlowFirstChild();
const LayoutObject* span2 = GetLayoutObjectByElementId("span2");
const LayoutObject* text2 = span2->SlowFirstChild();
// Enumerate all fragments for |LayoutText|.
{
NGInlineCursor cursor;
cursor.MoveTo(*text1);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor),
ElementsAre("1111", "22"));
}
{
NGInlineCursor cursor;
cursor.MoveTo(*text2);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor),
ElementsAre("33", "4444"));
}
// |MoveTo| can find no fragments for culled inline.
{
NGInlineCursor cursor;
cursor.MoveTo(*span1);
EXPECT_FALSE(cursor);
}
{
NGInlineCursor cursor;
cursor.MoveTo(*span2);
EXPECT_FALSE(cursor);
}
// But |MoveToIncludingCulledInline| should find its descendants.
{
NGInlineCursor cursor;
cursor.MoveToIncludingCulledInline(*span1);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor),
ElementsAre("1111", "22"));
}
{
NGInlineCursor cursor;
cursor.MoveToIncludingCulledInline(*span2);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor),
ElementsAre("33", "4444"));
}
// Line-ranged cursors can find fragments only in the line.
// The 1st line has "1111", from "text1".
const LayoutBlockFlow* block_flow = span1->FragmentItemsContainer();
NGInlineCursor cursor(*block_flow);
EXPECT_TRUE(cursor.Current().IsLineBox());
NGInlineCursor line1 = cursor.CursorForDescendants();
const auto TestFragment1 = [&](const NGInlineCursor& initial_cursor) {
NGInlineCursor cursor = initial_cursor;
cursor.MoveTo(*text1);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("1111"));
cursor = initial_cursor;
cursor.MoveToIncludingCulledInline(*span1);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("1111"));
cursor = initial_cursor;
cursor.MoveTo(*text2);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre());
cursor = initial_cursor;
cursor.MoveToIncludingCulledInline(*span2);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre());
};
TestFragment1(line1);
// The 2nd line has "22" from "text1" and "33" from text2.
cursor.MoveToNextFragmentainer();
EXPECT_TRUE(cursor);
EXPECT_TRUE(cursor.Current().IsLineBox());
NGInlineCursor line2 = cursor.CursorForDescendants();
const auto TestFragment2 = [&](const NGInlineCursor& initial_cursor) {
NGInlineCursor cursor = initial_cursor;
cursor.MoveTo(*text1);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("22"));
cursor = initial_cursor;
cursor.MoveToIncludingCulledInline(*span1);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("22"));
cursor = initial_cursor;
cursor.MoveTo(*text2);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("33"));
cursor = initial_cursor;
cursor.MoveToIncludingCulledInline(*span2);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("33"));
};
TestFragment2(line2);
// The 3rd line has "4444" from text2.
cursor.MoveToNextFragmentainer();
EXPECT_TRUE(cursor);
EXPECT_TRUE(cursor.Current().IsLineBox());
NGInlineCursor line3 = cursor.CursorForDescendants();
const auto TestFragment3 = [&](const NGInlineCursor& initial_cursor) {
NGInlineCursor cursor = initial_cursor;
cursor.MoveTo(*text1);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre());
cursor = initial_cursor;
cursor.MoveToIncludingCulledInline(*span1);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre());
cursor = initial_cursor;
cursor.MoveTo(*text2);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("4444"));
cursor = initial_cursor;
cursor.MoveToIncludingCulledInline(*span2);
EXPECT_THAT(LayoutObjectToDebugStringList(cursor), ElementsAre("4444"));
};
TestFragment3(line3);
// Test cursors rooted at |NGFragmentItems|.
// They can enumerate fragments only in the specified fragmentainer.
Vector<const NGPhysicalBoxFragment*> fragments;
for (const NGPhysicalBoxFragment& fragment :
block_flow->PhysicalFragments()) {
DCHECK(fragment.HasItems());
fragments.push_back(&fragment);
}
EXPECT_EQ(fragments.size(), 3u);
TestFragment1(NGInlineCursor(*fragments[0], *fragments[0]->Items()));
TestFragment2(NGInlineCursor(*fragments[1], *fragments[1]->Items()));
TestFragment3(NGInlineCursor(*fragments[2], *fragments[2]->Items()));
}
} // namespace
} // namespace blink