Commit ec0c017a authored by kojii's avatar kojii Committed by Commit Bot

[LayoutNG] Inline margin/border/padding, inter-item breaking, and tests

This patch implements inline margin/border/padding. NGTextFragment is
placed according to these properties. The border is not reflected to
fragment tree yet, adding NGBoxFragments will be in following patches.

This patch also supports when inter-item is not breakable. Inline
margin/border/padding belong to different NGInlineItem than its
text, but between them are not breakable.

Includes a fix for LazyLineBreakIterator where offset 0 should not be
breakable.

This patch also adds tests when inter-items are not breakable.

BUG=636993
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_layout_tests_layout_ng

Review-Url: https://codereview.chromium.org/2865903002
Cr-Commit-Position: refs/heads/master@{#475799}
parent f14ac82c
......@@ -435,9 +435,6 @@ crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats-clear/margin-col
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats-clear/margin-collapse-clear-014.xht [ Failure ]
### virtual/layout_ng/external/wpt/css/CSS2/linebox
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-001.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-002.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-003.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Crash Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-001.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-002.xht [ Failure ]
......@@ -449,7 +446,6 @@ crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatti
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-010b.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-012.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-013.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-022.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-023.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-002.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-004.xht [ Failure ]
......@@ -485,8 +481,6 @@ crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-125
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-129.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-bleed-001.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-bleed-002.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-117a.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-118a.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-121.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-004a.xht [ Failure ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-005a.xht [ Failure ]
......
......@@ -1338,6 +1338,7 @@ source_set("unit_tests") {
"layout/ng/inline/ng_inline_items_builder_test.cc",
"layout/ng/inline/ng_inline_layout_algorithm_test.cc",
"layout/ng/inline/ng_inline_node_test.cc",
"layout/ng/inline/ng_line_breaker_test.cc",
"layout/ng/ng_absolute_utils_test.cc",
"layout/ng/ng_base_layout_algorithm_test.cc",
"layout/ng/ng_base_layout_algorithm_test.h",
......
......@@ -4,6 +4,7 @@
#include "core/layout/ng/inline/ng_inline_item.h"
#include "core/layout/LayoutInline.h"
#include "core/layout/LayoutObject.h"
#include "platform/fonts/CharacterRange.h"
#include "platform/fonts/shaping/ShapeResultBuffer.h"
......@@ -123,6 +124,18 @@ void NGInlineItem::GetFallbackFonts(
shape_result_->FallbackFonts(fallback_fonts);
}
bool NGInlineItem::HasStartEdge() const {
DCHECK(Type() == kOpenTag || Type() == kCloseTag);
// TODO(kojii): Should use break token when NG has its own tree building.
return !GetLayoutObject()->IsInlineElementContinuation();
}
bool NGInlineItem::HasEndEdge() const {
DCHECK(Type() == kOpenTag || Type() == kCloseTag);
// TODO(kojii): Should use break token when NG has its own tree building.
return !ToLayoutInline(GetLayoutObject())->Continuation();
}
NGInlineItemRange::NGInlineItemRange(Vector<NGInlineItem>* items,
unsigned start_index,
unsigned end_index)
......
......@@ -93,6 +93,9 @@ class NGInlineItem {
unsigned start,
unsigned end) const;
bool HasStartEdge() const;
bool HasEndEdge() const;
static void Split(Vector<NGInlineItem>&, unsigned index, unsigned offset);
static unsigned SetBidiLevel(Vector<NGInlineItem>&,
unsigned index,
......
......@@ -11,6 +11,10 @@ NGInlineItemResult::NGInlineItemResult() {}
NGInlineItemResult::NGInlineItemResult(unsigned index,
unsigned start,
unsigned end)
: item_index(index), start_offset(start), end_offset(end) {}
: item_index(index),
start_offset(start),
end_offset(end),
no_break_opportunities_inside(false),
prohibit_break_after(false) {}
} // namespace blink
......@@ -42,6 +42,14 @@ struct CORE_EXPORT NGInlineItemResult {
// NGBoxStrut for atomic inline items.
NGBoxStrut margins;
// Inside of this is not breakable.
// Used only during line breaking.
unsigned no_break_opportunities_inside : 1;
// Lines must not break after this.
// Used only during line breaking.
unsigned prohibit_break_after : 1;
NGInlineItemResult();
NGInlineItemResult(unsigned index, unsigned start, unsigned end);
};
......
......@@ -271,8 +271,10 @@ bool NGInlineLayoutAlgorithm::PlaceItems(
borders.BlockSum() + paddings.BlockSum());
}
} else if (item.Type() == NGInlineItem::kCloseTag) {
position += item_result.inline_size;
box = box_states_.OnCloseTag(item, &line_box, box, baseline_type_,
position);
continue;
} else if (item.Type() == NGInlineItem::kAtomicInline) {
box = PlaceAtomicInline(item, &item_result, position, &line_box,
&text_builder);
......
......@@ -93,6 +93,8 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
LayoutObject* start_inline_;
LayoutNGBlockFlow* block_;
Member<NGLayoutInputNode> next_sibling_;
friend class NGLineBreakerTest;
};
inline void NGInlineNode::AssertOffset(unsigned index, unsigned offset) const {
......
......@@ -8,11 +8,12 @@
#include "core/CoreExport.h"
#include "core/layout/ng/inline/ng_inline_item_result.h"
#include "platform/heap/Handle.h"
#include "platform/text/TextBreakIterator.h"
#include "platform/wtf/Allocator.h"
#include "platform/wtf/text/AtomicString.h"
namespace blink {
class LazyLineBreakIterator;
class NGInlineBreakToken;
class NGInlineItem;
class NGInlineNode;
......@@ -43,24 +44,50 @@ class CORE_EXPORT NGLineBreaker {
private:
void BreakLine(NGInlineItemResults*, NGInlineLayoutAlgorithm*);
bool HandleControlItem(const NGInlineItem&,
const String& text,
NGInlineItemResult*,
LayoutUnit position);
void LayoutAtomicInline(const NGInlineItem&, NGInlineItemResult*);
enum class LineBreakState {
// The current position is not breakable.
kNotBreakable,
// The current position is breakable.
kIsBreakable,
// Break by including trailing items (CloseTag).
kBreakAfterTrailings,
// Break immediately.
kForcedBreak
};
void HandleOverflow(NGInlineItemResults*, const LazyLineBreakIterator&);
LineBreakState HandleText(const NGInlineItem&, NGInlineItemResult*);
void BreakText(NGInlineItemResult*,
const NGInlineItem&,
LayoutUnit available_width);
LineBreakState HandleControlItem(const NGInlineItem&, NGInlineItemResult*);
LineBreakState HandleAtomicInline(const NGInlineItem&, NGInlineItemResult*);
void HandleFloat(const NGInlineItem&,
NGInlineItemResults*,
NGInlineLayoutAlgorithm*);
void HandleOpenTag(const NGInlineItem&, NGInlineItemResult*);
void HandleCloseTag(const NGInlineItem&, NGInlineItemResult*);
void HandleOverflow(NGInlineItemResults*);
void Rewind(NGInlineItemResults*, unsigned new_end);
void UpdateBreakIterator(const ComputedStyle&);
void MoveToNextOf(const NGInlineItem&);
void MoveToNextOf(const NGInlineItemResult&);
void SkipCollapsibleWhitespaces();
void AppendCloseTags(NGInlineItemResults*);
Persistent<NGInlineNode> node_;
const NGConstraintSpace* constraint_space_;
const AtomicString locale_;
unsigned item_index_;
unsigned offset_;
LayoutUnit available_width_;
LayoutUnit position_;
LazyLineBreakIterator break_iterator_;
unsigned auto_wrap_ : 1;
};
} // namespace blink
......
// Copyright 2017 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 "core/layout/ng/ng_base_layout_algorithm_test.h"
#include "core/layout/ng/inline/ng_inline_layout_algorithm.h"
#include "core/layout/ng/inline/ng_inline_node.h"
#include "core/layout/ng/inline/ng_line_breaker.h"
#include "core/layout/ng/layout_ng_block_flow.h"
#include "core/layout/ng/ng_constraint_space_builder.h"
#include "platform/wtf/text/StringBuilder.h"
namespace blink {
class NGLineBreakerTest : public NGBaseLayoutAlgorithmTest {
protected:
NGInlineNode* CreateInlineNode(const String& html_content) {
SetBodyInnerHTML(html_content);
LayoutNGBlockFlow* block_flow =
ToLayoutNGBlockFlow(GetLayoutObjectByElementId("container"));
NGInlineNode* inline_node =
new NGInlineNode(block_flow->FirstChild(), block_flow);
return inline_node;
}
// Break lines using the specified available width.
Vector<NGInlineItemResults> BreakLines(NGInlineNode* node,
LayoutUnit available_width) {
if (!node->IsPrepareLayoutFinished())
node->PrepareLayout();
RefPtr<NGConstraintSpace> space =
NGConstraintSpaceBuilder(NGWritingMode::kHorizontalTopBottom)
.SetAvailableSize({available_width, NGSizeIndefinite})
.ToConstraintSpace(NGWritingMode::kHorizontalTopBottom);
NGLineBreaker line_breaker(node, space.Get());
NGInlineLayoutAlgorithm algorithm(node, space.Get());
Vector<NGInlineItemResults> lines;
while (true) {
NGInlineItemResults item_results;
line_breaker.NextLine(&item_results, &algorithm);
if (item_results.IsEmpty())
break;
lines.push_back(item_results);
}
return lines;
}
};
namespace {
String ToString(NGInlineItemResults line, NGInlineNode* node) {
StringBuilder builder;
for (const auto& item_result : line) {
builder.Append(
node->Text(item_result.start_offset, item_result.end_offset));
}
return builder.ToString();
}
TEST_F(NGLineBreakerTest, SingleNode) {
LoadAhem();
NGInlineNode* node = CreateInlineNode(R"HTML(
<!DOCTYPE html>
<style>
#container {
font: 10px/1 Ahem;
}
</style>
<div id=container>123 456 789</div>
)HTML");
Vector<NGInlineItemResults> lines;
lines = BreakLines(node, LayoutUnit(80));
EXPECT_EQ(2u, lines.size());
EXPECT_EQ("123 456", ToString(lines[0], node));
EXPECT_EQ("789", ToString(lines[1], node));
lines = BreakLines(node, LayoutUnit(60));
EXPECT_EQ(3u, lines.size());
EXPECT_EQ("123", ToString(lines[0], node));
EXPECT_EQ("456", ToString(lines[1], node));
EXPECT_EQ("789", ToString(lines[2], node));
}
TEST_F(NGLineBreakerTest, OverflowWord) {
LoadAhem();
NGInlineNode* node = CreateInlineNode(R"HTML(
<!DOCTYPE html>
<style>
#container {
font: 10px/1 Ahem;
}
</style>
<div id=container>12345 678</div>
)HTML");
// The first line overflows, but the last line does not.
Vector<NGInlineItemResults> lines;
lines = BreakLines(node, LayoutUnit(40));
EXPECT_EQ(2u, lines.size());
EXPECT_EQ("12345", ToString(lines[0], node));
EXPECT_EQ("678", ToString(lines[1], node));
// Both lines overflow.
lines = BreakLines(node, LayoutUnit(20));
EXPECT_EQ(2u, lines.size());
EXPECT_EQ("12345", ToString(lines[0], node));
EXPECT_EQ("678", ToString(lines[1], node));
}
TEST_F(NGLineBreakerTest, OverflowAtomicInline) {
LoadAhem();
NGInlineNode* node = CreateInlineNode(R"HTML(
<!DOCTYPE html>
<style>
#container {
font: 10px/1 Ahem;
}
span {
display: inline-block;
width: 30px;
height: 10px;
}
</style>
<div id=container>12345<span></span>678</div>
)HTML");
Vector<NGInlineItemResults> lines;
lines = BreakLines(node, LayoutUnit(80));
EXPECT_EQ(2u, lines.size());
EXPECT_EQ(String(u"12345\uFFFC"), ToString(lines[0], node));
EXPECT_EQ("678", ToString(lines[1], node));
lines = BreakLines(node, LayoutUnit(70));
EXPECT_EQ(2u, lines.size());
EXPECT_EQ("12345", ToString(lines[0], node));
EXPECT_EQ(String(u"\uFFFC678"), ToString(lines[1], node));
lines = BreakLines(node, LayoutUnit(40));
EXPECT_EQ(3u, lines.size());
EXPECT_EQ("12345", ToString(lines[0], node));
EXPECT_EQ(String(u"\uFFFC"), ToString(lines[1], node));
EXPECT_EQ("678", ToString(lines[2], node));
lines = BreakLines(node, LayoutUnit(20));
EXPECT_EQ(3u, lines.size());
EXPECT_EQ("12345", ToString(lines[0], node));
EXPECT_EQ(String(u"\uFFFC"), ToString(lines[1], node));
EXPECT_EQ("678", ToString(lines[2], node));
}
TEST_F(NGLineBreakerTest, OverflowMargin) {
LoadAhem();
NGInlineNode* node = CreateInlineNode(R"HTML(
<!DOCTYPE html>
<style>
#container {
font: 10px/1 Ahem;
}
span {
margin-right: 4em;
}
</style>
<div id=container><span>123 456</span> 789</div>
)HTML");
const Vector<NGInlineItem>& items = node->Items();
// While "123 456" can fit in a line, "456" has a right margin that cannot
// fit. Since "456" and its right margin is not breakable, "456" should be on
// the next line.
Vector<NGInlineItemResults> lines;
lines = BreakLines(node, LayoutUnit(80));
EXPECT_EQ(3u, lines.size());
EXPECT_EQ("123", ToString(lines[0], node));
EXPECT_EQ("456", ToString(lines[1], node));
DCHECK_EQ(NGInlineItem::kCloseTag, items[lines[1].back().item_index].Type());
EXPECT_EQ("789", ToString(lines[2], node));
// Same as above, but this time "456" overflows the line because it is 70px.
lines = BreakLines(node, LayoutUnit(60));
EXPECT_EQ(3u, lines.size());
EXPECT_EQ("123", ToString(lines[0], node));
EXPECT_EQ("456", ToString(lines[1], node));
DCHECK_EQ(NGInlineItem::kCloseTag, items[lines[1].back().item_index].Type());
EXPECT_EQ("789", ToString(lines[2], node));
}
TEST_F(NGLineBreakerTest, BoundaryInWord) {
LoadAhem();
NGInlineNode* node = CreateInlineNode(R"HTML(
<!DOCTYPE html>
<style>
#container {
font: 10px/1 Ahem;
}
</style>
<div id=container><span>123 456</span>789 abc</div>
)HTML");
// The element boundary within "456789" should not cause a break.
// Since "789" does not fit, it should go to the next line along with "456".
Vector<NGInlineItemResults> lines;
lines = BreakLines(node, LayoutUnit(80));
EXPECT_EQ(3u, lines.size());
EXPECT_EQ("123", ToString(lines[0], node));
EXPECT_EQ("456789", ToString(lines[1], node));
EXPECT_EQ("abc", ToString(lines[2], node));
// Same as above, but this time "456789" overflows the line because it is
// 60px.
lines = BreakLines(node, LayoutUnit(50));
EXPECT_EQ(3u, lines.size());
EXPECT_EQ("123", ToString(lines[0], node));
EXPECT_EQ("456789", ToString(lines[1], node));
EXPECT_EQ("abc", ToString(lines[2], node));
}
TEST_F(NGLineBreakerTest, BoundaryInFirstWord) {
LoadAhem();
NGInlineNode* node = CreateInlineNode(R"HTML(
<!DOCTYPE html>
<style>
#container {
font: 10px/1 Ahem;
}
</style>
<div id=container><span>123</span>456 789</div>
)HTML");
Vector<NGInlineItemResults> lines;
lines = BreakLines(node, LayoutUnit(80));
EXPECT_EQ(2u, lines.size());
EXPECT_EQ("123456", ToString(lines[0], node));
EXPECT_EQ("789", ToString(lines[1], node));
lines = BreakLines(node, LayoutUnit(50));
EXPECT_EQ(2u, lines.size());
EXPECT_EQ("123456", ToString(lines[0], node));
EXPECT_EQ("789", ToString(lines[1], node));
lines = BreakLines(node, LayoutUnit(20));
EXPECT_EQ(2u, lines.size());
EXPECT_EQ("123456", ToString(lines[0], node));
EXPECT_EQ("789", ToString(lines[1], node));
}
} // namespace
} // namespace blink
......@@ -391,7 +391,7 @@ int LazyLineBreakIterator::NextBreakablePositionKeepAll(int pos) const {
}
unsigned LazyLineBreakIterator::NextBreakOpportunity(unsigned offset) const {
int next_break = 0;
int next_break = -1;
IsBreakable(offset, next_break);
return next_break;
}
......@@ -400,8 +400,7 @@ unsigned LazyLineBreakIterator::PreviousBreakOpportunity(unsigned offset,
unsigned min) const {
unsigned pos = std::min(offset, string_.length());
for (; pos > min; pos--) {
int next_break = 0;
if (IsBreakable(pos, next_break))
if (IsBreakable(pos))
return pos;
}
return min;
......
......@@ -217,6 +217,11 @@ class PLATFORM_EXPORT LazyLineBreakIterator final {
return IsBreakable(pos, next_breakable, break_type_);
}
inline bool IsBreakable(int pos) const {
int next_breakable = -1;
return IsBreakable(pos, next_breakable, break_type_);
}
// Returns the break opportunity at or after |offset|.
unsigned NextBreakOpportunity(unsigned offset) const;
......
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