[LayoutNG] Fix empty inlines to influence the used line height
This patch matches NGInlineLayoutAlgorithm to CSS2 10.8 Line height
calculations: the 'line-height' and 'vertical-align' properties[1]
defining empty inline elements should influence line height.
Also, since the introduction of NGInlineBoxState, we had two places to
compute the union of boxes; NGInlineBoxState.stack_[0] and
NGLineBoxFragmentBuilder. Some code were uniting boxes to wrong one.
This patch unifies them to NGInlineBoxState.
[1] https://drafts.csswg.org/css2/visudet.html#line-height
BUG=636993
Review-Url: https://codereview.chromium.org/2845493002
Cr-Commit-Position: refs/heads/master@{#467278}
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index ce194f76..faa6b25 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -826,6 +826,7 @@
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-replaced-width-006.xht [ Skip ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-replaced-width-008.xht [ Skip ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-replaced-width-009.xht [ Skip ]
+crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-replaced-width-011.xht [ Skip ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-replaced-width-012.xht [ Skip ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-replaced-width-013.xht [ Skip ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-replaced-width-014.xht [ Skip ]
@@ -1287,7 +1288,6 @@
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-002.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-003.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Skip ]
-crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-003.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-001.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-002.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-003.xht [ Skip ]
@@ -1305,7 +1305,6 @@
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-002.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-004.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-005.xht [ Skip ]
-crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-012.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-013.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-015.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-016.xht [ Skip ]
@@ -1335,7 +1334,6 @@
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-104.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-125.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-126.xht [ Skip ]
-crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-127.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-128.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-129.xht [ Skip ]
crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-bleed-001.xht [ Skip ]
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc
index ffe34569..084904c0 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc
@@ -11,9 +11,8 @@
namespace blink {
-void NGInlineBoxState::ComputeTextMetrics(const NGInlineItem& item,
+void NGInlineBoxState::ComputeTextMetrics(const ComputedStyle& style,
FontBaseline baseline_type) {
- const ComputedStyle& style = *item.Style();
text_metrics = NGLineHeightMetrics(style, baseline_type);
text_top = -text_metrics.ascent;
text_metrics.AddLeading(style.ComputedLineHeightAsFixed());
@@ -22,23 +21,47 @@
include_used_fonts = style.LineHeight().IsNegative();
}
+void NGInlineBoxState::AccumulateUsedFonts(const NGInlineItem& item,
+ unsigned start,
+ unsigned end,
+ FontBaseline baseline_type) {
+ HashSet<const SimpleFontData*> fallback_fonts;
+ item.GetFallbackFonts(&fallback_fonts, start, end);
+ for (const auto& fallback_font : fallback_fonts) {
+ NGLineHeightMetrics fallback_metrics(fallback_font->GetFontMetrics(),
+ baseline_type);
+ fallback_metrics.AddLeading(
+ fallback_font->GetFontMetrics().FixedLineSpacing());
+ metrics.Unite(fallback_metrics);
+ }
+}
+
NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems(
- const ComputedStyle* line_style) {
+ const ComputedStyle* line_style,
+ FontBaseline baseline_type) {
if (stack_.IsEmpty()) {
// For the first line, push a box state for the line itself.
stack_.Resize(1);
NGInlineBoxState* box = &stack_.back();
box->fragment_start = 0;
- box->style = line_style;
- return box;
+ } else {
+ // For the following lines, clear states that are not shared across lines.
+ for (auto& box : stack_) {
+ box.fragment_start = 0;
+ box.metrics = NGLineHeightMetrics();
+ DCHECK(box.pending_descendants.IsEmpty());
+ }
}
- // For the following lines, clear states that are not shared across lines.
- for (auto& box : stack_) {
- box.fragment_start = 0;
- box.metrics = NGLineHeightMetrics();
- DCHECK(box.pending_descendants.IsEmpty());
- }
+ // Initialize the box state for the line box.
+ NGInlineBoxState& line_box = LineBoxState();
+ line_box.style = line_style;
+
+ // Use a "strut" (a zero-width inline box with the element's font and
+ // line height properties) as the initial metrics for the line box.
+ // https://drafts.csswg.org/css2/visudet.html#strut
+ line_box.ComputeTextMetrics(*line_style, baseline_type);
+
return &stack_.back();
}
@@ -72,7 +95,9 @@
NGInlineBoxState* box = &(*it);
EndBoxState(box, line_box);
}
- line_box->UniteMetrics(stack_.front().metrics);
+
+ DCHECK(!LineBoxState().metrics.IsEmpty());
+ line_box->SetMetrics(LineBoxState().metrics);
}
void NGInlineLayoutStateStack::EndBoxState(NGInlineBoxState* box,
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.h
index e3675e1..9a52e39 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.h
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.h
@@ -40,7 +40,11 @@
bool include_used_fonts = false;
// Compute text metrics for a box. All text in a box share the same metrics.
- void ComputeTextMetrics(const NGInlineItem&, FontBaseline);
+ void ComputeTextMetrics(const ComputedStyle& style, FontBaseline);
+ void AccumulateUsedFonts(const NGInlineItem&,
+ unsigned start,
+ unsigned end,
+ FontBaseline);
};
// Represents the inline tree structure. This class provides:
@@ -49,9 +53,12 @@
// 3) Cache common values for a box.
class NGInlineLayoutStateStack {
public:
+ // The box state for the line box.
+ NGInlineBoxState& LineBoxState() { return stack_.front(); }
+
// Initialize the box state stack for a new line.
// @return The initial box state for the line.
- NGInlineBoxState* OnBeginPlaceItems(const ComputedStyle*);
+ NGInlineBoxState* OnBeginPlaceItems(const ComputedStyle*, FontBaseline);
// Push a box state stack.
NGInlineBoxState* OnOpenTag(const NGInlineItem&,
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 5e92c1ca..470da16 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -394,35 +394,40 @@
const Vector<LineItemChunk, 32>& line_item_chunks) {
const Vector<NGInlineItem>& items = Node()->Items();
- // Use a "strut" (a zero-width inline box with the element's font and
- // line height properties) as the initial metrics for the line box.
- // https://drafts.csswg.org/css2/visudet.html#strut
const ComputedStyle& line_style = LineStyle();
NGLineHeightMetrics line_metrics(line_style, baseline_type_);
NGLineHeightMetrics line_metrics_with_leading = line_metrics;
line_metrics_with_leading.AddLeading(line_style.ComputedLineHeightAsFixed());
- NGLineBoxFragmentBuilder line_box(Node(), line_metrics_with_leading);
+ NGLineBoxFragmentBuilder line_box(Node());
// Compute heights of all inline items by placing the dominant baseline at 0.
// The baseline is adjusted after the height of the line box is computed.
NGTextFragmentBuilder text_builder(Node());
- NGInlineBoxState* box = box_states_.OnBeginPlaceItems(&LineStyle());
+ NGInlineBoxState* box =
+ box_states_.OnBeginPlaceItems(&LineStyle(), baseline_type_);
LayoutUnit inline_size;
for (const auto& line_item_chunk : line_item_chunks) {
const NGInlineItem& item = items[line_item_chunk.index];
LayoutUnit line_top;
if (item.Type() == NGInlineItem::kText) {
DCHECK(item.GetLayoutObject()->IsText());
- if (box->text_metrics.IsEmpty())
- box->ComputeTextMetrics(item, baseline_type_);
+ DCHECK(!box->text_metrics.IsEmpty());
line_top = box->text_top;
text_builder.SetSize(
{line_item_chunk.inline_size, box->text_metrics.LineHeight()});
// Take all used fonts into account if 'line-height: normal'.
- if (box->include_used_fonts)
- AccumulateUsedFonts(item, line_item_chunk, &line_box);
+ if (box->include_used_fonts) {
+ box->AccumulateUsedFonts(item, line_item_chunk.start_offset,
+ line_item_chunk.end_offset, baseline_type_);
+ }
} else if (item.Type() == NGInlineItem::kOpenTag) {
box = box_states_.OnOpenTag(item, &line_box, &text_builder);
+ // Compute text metrics for all inline boxes since even empty inlines
+ // influence the line height.
+ // https://drafts.csswg.org/css2/visudet.html#line-height
+ // TODO(kojii): Review if atomic inline level should have open/close.
+ if (!item.GetLayoutObject()->IsAtomicInlineLevel())
+ box->ComputeTextMetrics(*item.Style(), baseline_type_);
continue;
} else if (item.Type() == NGInlineItem::kCloseTag) {
box = box_states_.OnCloseTag(item, &line_box, box);
@@ -491,29 +496,15 @@
// the line box to the line top.
line_box.MoveChildrenInBlockDirection(baseline);
line_box.SetInlineSize(inline_size);
- container_builder_.AddChild(line_box.ToLineBoxFragment(),
- {LayoutUnit(), baseline - line_metrics.ascent});
+ container_builder_.AddChild(
+ line_box.ToLineBoxFragment(),
+ {LayoutUnit(), baseline + box_states_.LineBoxState().text_top});
max_inline_size_ = std::max(max_inline_size_, inline_size);
content_size_ = line_bottom;
return true;
}
-void NGInlineLayoutAlgorithm::AccumulateUsedFonts(
- const NGInlineItem& item,
- const LineItemChunk& line_item_chunk,
- NGLineBoxFragmentBuilder* line_box) {
- HashSet<const SimpleFontData*> fallback_fonts;
- item.GetFallbackFonts(&fallback_fonts, line_item_chunk.start_offset,
- line_item_chunk.end_offset);
- for (const auto& fallback_font : fallback_fonts) {
- NGLineHeightMetrics metrics(fallback_font->GetFontMetrics(),
- baseline_type_);
- metrics.AddLeading(fallback_font->GetFontMetrics().FixedLineSpacing());
- line_box->UniteMetrics(metrics);
- }
-}
-
LayoutUnit NGInlineLayoutAlgorithm::PlaceAtomicInline(
const NGInlineItem& item,
NGLineBoxFragmentBuilder* line_box,
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.cc
index baeab067..e972c2c 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -13,10 +13,8 @@
namespace blink {
-NGLineBoxFragmentBuilder::NGLineBoxFragmentBuilder(
- NGInlineNode* node,
- const NGLineHeightMetrics& metrics)
- : direction_(TextDirection::kLtr), node_(node), metrics_(metrics) {}
+NGLineBoxFragmentBuilder::NGLineBoxFragmentBuilder(NGInlineNode* node)
+ : direction_(TextDirection::kLtr), node_(node) {}
NGLineBoxFragmentBuilder& NGLineBoxFragmentBuilder::SetDirection(
TextDirection direction) {
@@ -51,9 +49,8 @@
offsets_[index].block_offset += delta;
}
-void NGLineBoxFragmentBuilder::UniteMetrics(
- const NGLineHeightMetrics& metrics) {
- metrics_.Unite(metrics);
+void NGLineBoxFragmentBuilder::SetMetrics(const NGLineHeightMetrics& metrics) {
+ metrics_ = metrics;
}
void NGLineBoxFragmentBuilder::SetBreakToken(
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.h
index c1e0c89..e7e16de 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -20,7 +20,7 @@
STACK_ALLOCATED();
public:
- NGLineBoxFragmentBuilder(NGInlineNode*, const NGLineHeightMetrics&);
+ explicit NGLineBoxFragmentBuilder(NGInlineNode*);
NGLineBoxFragmentBuilder& SetDirection(TextDirection);
@@ -35,7 +35,7 @@
return children_;
}
- void UniteMetrics(const NGLineHeightMetrics&);
+ void SetMetrics(const NGLineHeightMetrics&);
const NGLineHeightMetrics& Metrics() const { return metrics_; }
// Set the break token for the fragment to build.