Commit c2367a87 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Mark lines dirty when floats/OOF needs layout

This patch fixes to mark lines dirty when floats or OOF that
appear in the line needs layout.

Before r656770, this issue occurs only when |PrepareLayout()|
runs before the layout (e.g., |ComputeMinMax()|). r656770
made this bug to appear more often, by changing to use
|PrepareLayout()| to mark when |NeedsCollectInlines()| is set.

Change-Id: I0267b87aa83e6058262a9c04aaf159f72030a0b7
Bug: 949222, 959521
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1601014Reviewed-by: default avatarAleks Totic <atotic@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#657792}
parent 2791d074
...@@ -59,6 +59,16 @@ class CORE_EXPORT NGDirtyLines { ...@@ -59,6 +59,16 @@ class CORE_EXPORT NGDirtyLines {
return false; return false;
} }
bool HandleFloatingOrOutOfFlowPositioned(LayoutObject* layout_object) {
DCHECK(layout_object->IsFloatingOrOutOfFlowPositioned());
if (layout_object->NeedsLayout()) {
MarkLastFragment();
return true;
}
// Don't update last fragment. Floats and OOF are opaque.
return false;
}
// Mark the line box at the specified text offset dirty. // Mark the line box at the specified text offset dirty.
void MarkAtTextOffset(unsigned offset); void MarkAtTextOffset(unsigned offset);
......
...@@ -819,6 +819,11 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendFloating( ...@@ -819,6 +819,11 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendFloating(
LayoutObject* layout_object) { LayoutObject* layout_object) {
AppendOpaque(NGInlineItem::kFloating, kObjectReplacementCharacter, AppendOpaque(NGInlineItem::kFloating, kObjectReplacementCharacter,
layout_object); layout_object);
// Mark dirty lines. Clear if marked, only the first dirty line is relevant.
if (dirty_lines_ &&
dirty_lines_->HandleFloatingOrOutOfFlowPositioned(layout_object))
dirty_lines_ = nullptr;
} }
template <typename OffsetMappingBuilder> template <typename OffsetMappingBuilder>
...@@ -826,6 +831,11 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>:: ...@@ -826,6 +831,11 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
AppendOutOfFlowPositioned(LayoutObject* layout_object) { AppendOutOfFlowPositioned(LayoutObject* layout_object) {
AppendOpaque(NGInlineItem::kOutOfFlowPositioned, kObjectReplacementCharacter, AppendOpaque(NGInlineItem::kOutOfFlowPositioned, kObjectReplacementCharacter,
layout_object); layout_object);
// Mark dirty lines. Clear if marked, only the first dirty line is relevant.
if (dirty_lines_ &&
dirty_lines_->HandleFloatingOrOutOfFlowPositioned(layout_object))
dirty_lines_ = nullptr;
} }
template <typename OffsetMappingBuilder> template <typename OffsetMappingBuilder>
......
...@@ -581,14 +581,21 @@ struct StyleChangeData { ...@@ -581,14 +581,21 @@ struct StyleChangeData {
kAll = kText | kParentAndAbove, kAll = kText | kParentAndAbove,
}; };
unsigned needs_collect_inlines; unsigned needs_collect_inlines;
base::Optional<bool> is_line_dirty;
} style_change_data[] = { } style_change_data[] = {
// Changing color, text-decoration, etc. should not re-run // Changing color, text-decoration, etc. should not re-run
// |CollectInlines()|. // |CollectInlines()|.
{"#parent.after { color: red; }", StyleChangeData::kNone}, {"#parent.after { color: red; }", StyleChangeData::kNone, false},
{"#parent.after { text-decoration-line: underline; }", {"#parent.after { text-decoration-line: underline; }",
StyleChangeData::kNone}, StyleChangeData::kNone, false},
// Changing fonts should re-run |CollectInlines()|. // Changing fonts should re-run |CollectInlines()|.
{"#parent.after { font-size: 200%; }", StyleChangeData::kAll}, {"#parent.after { font-size: 200%; }", StyleChangeData::kAll, true},
// Changing from/to out-of-flow should re-rerun |CollectInlines()|.
{"#parent.after { position: absolute; }", StyleChangeData::kContainer,
true},
{"#parent { position: absolute; }"
"#parent.after { position: initial; }",
StyleChangeData::kContainer, true},
// List markers are captured in |NGInlineItem|. // List markers are captured in |NGInlineItem|.
{"#parent.after { display: list-item; }", StyleChangeData::kContainer}, {"#parent.after { display: list-item; }", StyleChangeData::kContainer},
{"#parent { display: list-item; list-style-type: none; }" {"#parent { display: list-item; list-style-type: none; }"
...@@ -600,9 +607,9 @@ struct StyleChangeData { ...@@ -600,9 +607,9 @@ struct StyleChangeData {
// Changing properties related with bidi resolution should re-run // Changing properties related with bidi resolution should re-run
// |CollectInlines()|. // |CollectInlines()|.
{"#parent.after { unicode-bidi: bidi-override; }", {"#parent.after { unicode-bidi: bidi-override; }",
StyleChangeData::kParentAndAbove}, StyleChangeData::kParentAndAbove, true},
{"#container.after { unicode-bidi: bidi-override; }", {"#container.after { unicode-bidi: bidi-override; }",
StyleChangeData::kContainer}, StyleChangeData::kContainer, false},
}; };
std::ostream& operator<<(std::ostream& os, const StyleChangeData& data) { std::ostream& operator<<(std::ostream& os, const StyleChangeData& data) {
...@@ -656,6 +663,14 @@ TEST_P(StyleChangeTest, NeedsCollectInlinesOnStyle) { ...@@ -656,6 +663,14 @@ TEST_P(StyleChangeTest, NeedsCollectInlinesOnStyle) {
Element* next = GetElementById("next"); Element* next = GetElementById("next");
EXPECT_FALSE(previous->GetLayoutObject()->NeedsCollectInlines()); EXPECT_FALSE(previous->GetLayoutObject()->NeedsCollectInlines());
EXPECT_FALSE(next->GetLayoutObject()->NeedsCollectInlines()); EXPECT_FALSE(next->GetLayoutObject()->NeedsCollectInlines());
if (data.is_line_dirty) {
layout_block_flow_ = ToLayoutNGBlockFlow(container->GetLayoutObject());
auto lines = MarkLineBoxesDirty();
EXPECT_EQ(*data.is_line_dirty, lines[0]->IsDirty());
}
ForceLayout(); // Ensure running layout does not crash.
} }
using CreateNode = Node* (*)(Document&); using CreateNode = Node* (*)(Document&);
...@@ -966,47 +981,80 @@ TEST_F(NGInlineNodeTest, SpaceRestoredByInsertingWord) { ...@@ -966,47 +981,80 @@ TEST_F(NGInlineNodeTest, SpaceRestoredByInsertingWord) {
} }
// Test marking line boxes when inserting a span before the first child. // Test marking line boxes when inserting a span before the first child.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnInsert) { TEST_P(NodeInsertTest, MarkLineBoxesDirtyOnInsert) {
SetupHtml("container", R"HTML( SetupHtml("container", R"HTML(
<style>
.abspos { position: absolute; }
.float { float: left; }
</style>
<div id=container style="font-size: 10px; width: 10ch"> <div id=container style="font-size: 10px; width: 10ch">
12345678 12345678
</div> </div>
)HTML"); )HTML");
Element* span = GetDocument().CreateElementForBinding("span"); Node* insert = (*GetParam())(GetDocument());
Element* container = GetElementById("container"); Element* container = GetElementById("container");
container->insertBefore(span, container->firstChild()); container->insertBefore(insert, container->firstChild());
auto lines = MarkLineBoxesDirty(); auto lines = MarkLineBoxesDirty();
EXPECT_TRUE(lines[0]->IsDirty()); EXPECT_TRUE(lines[0]->IsDirty());
} }
// Test marking line boxes when appending a span. // Test marking line boxes when appending a span.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnAppend) { TEST_P(NodeInsertTest, MarkLineBoxesDirtyOnAppend) {
SetupHtml("container", R"HTML( SetupHtml("container", R"HTML(
<style>
.abspos { position: absolute; }
.float { float: left; }
</style>
<div id=container style="font-size: 10px; width: 10ch"> <div id=container style="font-size: 10px; width: 10ch">
12345678 12345678
</div> </div>
)HTML"); )HTML");
Element* span = GetDocument().CreateElementForBinding("span"); Node* insert = (*GetParam())(GetDocument());
layout_block_flow_->GetNode()->appendChild(span); layout_block_flow_->GetNode()->appendChild(insert);
auto lines = MarkLineBoxesDirty(); auto lines = MarkLineBoxesDirty();
EXPECT_TRUE(lines[0]->IsDirty()); EXPECT_TRUE(lines[0]->IsDirty());
} }
// Test marking line boxes when appending a span on 2nd line. // Test marking line boxes when appending a span on 2nd line.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnAppend2) { TEST_P(NodeInsertTest, MarkLineBoxesDirtyOnAppend2) {
SetupHtml("container", R"HTML( SetupHtml("container", R"HTML(
<style>
.abspos { position: absolute; }
.float { float: left; }
</style>
<div id=container style="font-size: 10px; width: 10ch"> <div id=container style="font-size: 10px; width: 10ch">
12345678 12345678
2234 2234
</div> </div>
)HTML"); )HTML");
Element* span = GetDocument().CreateElementForBinding("span"); Node* insert = (*GetParam())(GetDocument());
layout_block_flow_->GetNode()->appendChild(span); layout_block_flow_->GetNode()->appendChild(insert);
auto lines = MarkLineBoxesDirty();
EXPECT_FALSE(lines[0]->IsDirty());
EXPECT_TRUE(lines[1]->IsDirty());
}
// Test marking line boxes when appending a span on 2nd line.
TEST_P(NodeInsertTest, MarkLineBoxesDirtyOnAppendAfterBR) {
SetupHtml("container", R"HTML(
<style>
.abspos { position: absolute; }
.float { float: left; }
</style>
<div id=container style="font-size: 10px; width: 10ch">
<br>
<br>
</div>
)HTML");
Node* insert = (*GetParam())(GetDocument());
layout_block_flow_->GetNode()->appendChild(insert);
auto lines = MarkLineBoxesDirty(); auto lines = MarkLineBoxesDirty();
EXPECT_FALSE(lines[0]->IsDirty()); EXPECT_FALSE(lines[0]->IsDirty());
......
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