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

[LayoutNG] Make box fragment generation consistent

This patch fixes the logic whether to generate box fragments for
an inline box or not consistent across wrapped lines.

Not all inline boxes need to have corresponding box fragments.
NGInlineLayoutAlgorithm determines this and avoid creating them
when not necessary.

NGInlineLayoutAlgorithm used to determine it for each wrapped line,
but this made traversing fragments harder. With this change, if
traversal finds a box fragment for a LayoutInline, it knows that
it can find all other fragments too.

wpt/css/CSS2/normal-flow/block-in-inline-empty-00[14] no longer
pass with this change. These tests don't pass in Blink/WebKit, and
that the fix priority is not high at this moment.

Bug: 636993
Change-Id: I58b0f3c5fd95ee743a5ab404c8e8a4b284909616
Reviewed-on: https://chromium-review.googlesource.com/895231
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#534079}
parent fbe04e51
...@@ -1235,8 +1235,6 @@ crbug.com/591099 external/wpt/css/CSS2/floats/floats-wrap-bfc-003-right-table.xh ...@@ -1235,8 +1235,6 @@ crbug.com/591099 external/wpt/css/CSS2/floats/floats-wrap-bfc-003-right-table.xh
crbug.com/591099 external/wpt/css/CSS2/floats/floats-wrap-bfc-004.xht [ Failure ] crbug.com/591099 external/wpt/css/CSS2/floats/floats-wrap-bfc-004.xht [ Failure ]
crbug.com/591099 external/wpt/css/CSS2/linebox/inline-box-002.xht [ Failure ] crbug.com/591099 external/wpt/css/CSS2/linebox/inline-box-002.xht [ Failure ]
crbug.com/714962 external/wpt/css/CSS2/linebox/vertical-align-baseline-005a.xht [ Failure ] crbug.com/714962 external/wpt/css/CSS2/linebox/vertical-align-baseline-005a.xht [ Failure ]
crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-empty-001.xht [ Pass ]
crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-empty-004.xht [ Pass ]
crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001e.xht [ Pass ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001e.xht [ Pass ]
crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001h.xht [ Pass ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001h.xht [ Pass ]
crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-insert-002e.xht [ Pass ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-insert-002e.xht [ Pass ]
......
...@@ -274,6 +274,8 @@ crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-in ...@@ -274,6 +274,8 @@ crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-in
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/max-height-percentage-002.xht [ Failure ] crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/max-height-percentage-002.xht [ Failure ]
# Inline: border in continuations. Fail in Blink. # Inline: border in continuations. Fail in Blink.
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-empty-001.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-empty-004.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001f.xht [ Failure ] crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001f.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-002f.xht [ Failure ] crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-002f.xht [ Failure ]
......
...@@ -16,19 +16,23 @@ ...@@ -16,19 +16,23 @@
namespace blink { namespace blink {
void NGInlineBoxState::ComputeTextMetrics(const ComputedStyle& style, void NGInlineBoxState::ComputeTextMetrics(const ComputedStyle& style,
FontBaseline baseline_type, FontBaseline baseline_type) {
bool line_height_quirk) {
text_metrics = NGLineHeightMetrics(style, baseline_type); text_metrics = NGLineHeightMetrics(style, baseline_type);
text_top = -text_metrics.ascent; text_top = -text_metrics.ascent;
text_height = text_metrics.LineHeight(); text_height = text_metrics.LineHeight();
text_metrics.AddLeading(style.ComputedLineHeightAsFixed()); text_metrics.AddLeading(style.ComputedLineHeightAsFixed());
if (!line_height_quirk) metrics.Unite(text_metrics);
metrics.Unite(text_metrics);
include_used_fonts = style.LineHeight().IsNegative(); include_used_fonts = style.LineHeight().IsNegative();
} }
void NGInlineBoxState::EnsureTextMetrics(const ComputedStyle& style,
FontBaseline baseline_type) {
if (text_metrics.IsEmpty())
ComputeTextMetrics(style, baseline_type);
}
void NGInlineBoxState::AccumulateUsedFonts(const ShapeResult* shape_result, void NGInlineBoxState::AccumulateUsedFonts(const ShapeResult* shape_result,
FontBaseline baseline_type) { FontBaseline baseline_type) {
HashSet<const SimpleFontData*> fallback_fonts; HashSet<const SimpleFontData*> fallback_fonts;
...@@ -83,7 +87,7 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( ...@@ -83,7 +87,7 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems(
if (!line_height_quirk) if (!line_height_quirk)
box.metrics = box.text_metrics; box.metrics = box.text_metrics;
else else
box.metrics = NGLineHeightMetrics(); box.metrics = box.text_metrics = NGLineHeightMetrics();
if (box.needs_box_fragment) { if (box.needs_box_fragment) {
// Existing box states are wrapped before they were closed, and hence // Existing box states are wrapped before they were closed, and hence
// they do not have start edges. // they do not have start edges.
...@@ -99,12 +103,15 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( ...@@ -99,12 +103,15 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems(
// Initialize the box state for the line box. // Initialize the box state for the line box.
NGInlineBoxState& line_box = LineBoxState(); NGInlineBoxState& line_box = LineBoxState();
line_box.style = line_style; if (line_box.style != line_style) {
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. // Use a "strut" (a zero-width inline box with the element's font and
// https://drafts.csswg.org/css2/visudet.html#strut // line height properties) as the initial metrics for the line box.
line_box.ComputeTextMetrics(*line_style, baseline_type, line_height_quirk); // https://drafts.csswg.org/css2/visudet.html#strut
if (!line_height_quirk)
line_box.ComputeTextMetrics(*line_style, baseline_type);
}
return &stack_.back(); return &stack_.back();
} }
...@@ -181,12 +188,9 @@ void NGInlineLayoutStateStack::EndBoxState( ...@@ -181,12 +188,9 @@ void NGInlineLayoutStateStack::EndBoxState(
} }
} }
void NGInlineBoxState::SetNeedsBoxFragment(bool when_empty) { void NGInlineBoxState::SetNeedsBoxFragment() {
needs_box_fragment_when_empty = when_empty; DCHECK(item);
if (!needs_box_fragment) { needs_box_fragment = true;
DCHECK(item);
needs_box_fragment = true;
}
} }
void NGInlineBoxState::SetLineRightForBoxFragment( void NGInlineBoxState::SetLineRightForBoxFragment(
...@@ -213,16 +217,6 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder( ...@@ -213,16 +217,6 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder(
NGLineBoxFragmentBuilder::ChildList* line_box, NGLineBoxFragmentBuilder::ChildList* line_box,
FontBaseline baseline_type) { FontBaseline baseline_type) {
DCHECK(box->needs_box_fragment); DCHECK(box->needs_box_fragment);
unsigned fragment_end = line_box->size();
if (box->fragment_start == fragment_end &&
!box->needs_box_fragment_when_empty) {
// Don't create a box if the inline box is "empty".
// Inline boxes with inline margins/borders/paddings are not "empty",
// but background doesn't make difference in this context.
// Whether to create this box or not affects layout when the line contains
// only this box, since this box participates the line height.
return;
}
// The inline box should have the height of the font metrics without the // The inline box should have the height of the font metrics without the
// line-height property. Compute from style because |box->metrics| includes // line-height property. Compute from style because |box->metrics| includes
...@@ -238,6 +232,8 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder( ...@@ -238,6 +232,8 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder(
NGLogicalSize size(LayoutUnit(), metrics.LineHeight() + NGLogicalSize size(LayoutUnit(), metrics.LineHeight() +
box->border_padding_block_start + box->border_padding_block_start +
box->border_padding_block_end); box->border_padding_block_end);
unsigned fragment_end = line_box->size();
DCHECK(box->item); DCHECK(box->item);
box_data_list_.push_back( box_data_list_.push_back(
BoxData{box->fragment_start, fragment_end, box->item, size}); BoxData{box->fragment_start, fragment_end, box->item, size});
......
...@@ -79,20 +79,16 @@ struct NGInlineBoxState { ...@@ -79,20 +79,16 @@ struct NGInlineBoxState {
} }
// Compute text metrics for a box. All text in a box share the same // Compute text metrics for a box. All text in a box share the same
// metrics. When line_height_quirk is set, text metrics won't // metrics.
// influence box height until ActivateTextMetrics() is called. // The computed metrics is included into the line height of the current box.
void ComputeTextMetrics(const ComputedStyle& style, void ComputeTextMetrics(const ComputedStyle& style,
FontBaseline baseline_type, FontBaseline baseline_type);
bool line_height_quirk); void EnsureTextMetrics(const ComputedStyle&, FontBaseline);
void AccumulateUsedFonts(const ShapeResult*, FontBaseline);
// Activate text metrics. Used by the line height quirk when the void AccumulateUsedFonts(const ShapeResult*, FontBaseline);
// box gets text content or has border, padding or margin in the
// inline layout direction.
void ActivateTextMetrics() { metrics.Unite(text_metrics); }
// Create a box fragment for this box. // Create a box fragment for this box.
void SetNeedsBoxFragment(bool when_empty); void SetNeedsBoxFragment();
void SetLineRightForBoxFragment(const NGInlineItem&, void SetLineRightForBoxFragment(const NGInlineItem&,
const NGInlineItemResult&); const NGInlineItemResult&);
......
...@@ -17,38 +17,23 @@ const char* kNGInlineItemTypeStrings[] = { ...@@ -17,38 +17,23 @@ const char* kNGInlineItemTypeStrings[] = {
"Text", "Control", "AtomicInline", "OpenTag", "Text", "Control", "AtomicInline", "OpenTag",
"CloseTag", "Floating", "OutOfFlowPositioned", "BidiControl"}; "CloseTag", "Floating", "OutOfFlowPositioned", "BidiControl"};
// Returns true if this item is "empty", i.e. if the node contains only empty // Returns true if this inline box is "empty", i.e. if the node contains only
// items it will produce a single zero block-size line box. // empty items it will produce a single zero block-size line box.
static bool IsEmptyItem(NGInlineItem::NGInlineItemType type, //
const ComputedStyle* style, // While the spec defines "non-zero margins, padding, or borders" prevents
LayoutObject* layout_object) { // line boxes to be zero-height, tests indicate that only inline direction
if (type == NGInlineItem::kAtomicInline || type == NGInlineItem::kControl || // of them do so. https://drafts.csswg.org/css2/visuren.html
type == NGInlineItem::kText) bool IsInlineBoxEmpty(const ComputedStyle& style,
const LayoutObject& layout_object) {
if (style.BorderStart().NonZero() || !style.PaddingStart().IsZero() ||
style.BorderEnd().NonZero() || !style.PaddingEnd().IsZero())
return false; return false;
if (type == NGInlineItem::kOpenTag) { // Non-zero margin can prevent "empty" only in non-quirks mode.
DCHECK(style && layout_object); // https://quirks.spec.whatwg.org/#the-line-height-calculation-quirk
if ((!style.MarginStart().IsZero() || !style.MarginEnd().IsZero()) &&
if (style->BorderStart().NonZero() || !style->PaddingStart().IsZero()) !layout_object.GetDocument().InLineHeightQuirksMode())
return false; return false;
// Non-zero margin can prevent "empty" only in non-quirks mode.
// https://quirks.spec.whatwg.org/#the-line-height-calculation-quirk
if (!style->MarginStart().IsZero() &&
!layout_object->GetDocument().InLineHeightQuirksMode())
return false;
}
if (type == NGInlineItem::kCloseTag) {
DCHECK(style && layout_object);
if (style->BorderEnd().NonZero() || !style->PaddingEnd().IsZero())
return false;
if (!style->MarginEnd().IsZero() &&
!layout_object->GetDocument().InLineHeightQuirksMode())
return false;
}
return true; return true;
} }
...@@ -68,12 +53,41 @@ NGInlineItem::NGInlineItem(NGInlineItemType type, ...@@ -68,12 +53,41 @@ NGInlineItem::NGInlineItem(NGInlineItemType type,
type_(type), type_(type),
bidi_level_(UBIDI_LTR), bidi_level_(UBIDI_LTR),
shape_options_(kPreContext | kPostContext), shape_options_(kPreContext | kPostContext),
is_empty_item_(::blink::IsEmptyItem(type, style, layout_object)) { is_empty_item_(false),
should_create_box_fragment_(false) {
DCHECK_GE(end, start); DCHECK_GE(end, start);
ComputeBoxProperties();
} }
NGInlineItem::~NGInlineItem() = default; NGInlineItem::~NGInlineItem() = default;
void NGInlineItem::ComputeBoxProperties() {
DCHECK(!is_empty_item_);
DCHECK(!should_create_box_fragment_);
if (type_ == NGInlineItem::kText || type_ == NGInlineItem::kAtomicInline ||
type_ == NGInlineItem::kControl)
return;
if (type_ == NGInlineItem::kOpenTag) {
DCHECK(style_ && layout_object_ && layout_object_->IsLayoutInline());
if (layout_object_->HasBoxDecorationBackground() || style_->HasPadding() ||
style_->HasMargin()) {
is_empty_item_ = IsInlineBoxEmpty(*style_, *layout_object_);
should_create_box_fragment_ = true;
} else {
is_empty_item_ = true;
should_create_box_fragment_ =
ToLayoutBoxModelObject(layout_object_)->HasSelfPaintingLayer() ||
style_->HasOutline() || style_->CanContainAbsolutePositionObjects() ||
style_->CanContainFixedPositionObjects();
}
return;
}
is_empty_item_ = true;
}
const char* NGInlineItem::NGInlineItemTypeToString(int val) const { const char* NGInlineItem::NGInlineItemTypeToString(int val) const {
return kNGInlineItemTypeStrings[val]; return kNGInlineItemTypeStrings[val];
} }
......
...@@ -68,6 +68,10 @@ class CORE_EXPORT NGInlineItem { ...@@ -68,6 +68,10 @@ class CORE_EXPORT NGInlineItem {
// If this item is "empty" for the purpose of empty block calculation. // If this item is "empty" for the purpose of empty block calculation.
bool IsEmptyItem() const { return is_empty_item_; } bool IsEmptyItem() const { return is_empty_item_; }
// If this item should create a box fragment. Box fragments can be omitted for
// optimization if this is false.
bool ShouldCreateBoxFragment() const { return should_create_box_fragment_; }
unsigned StartOffset() const { return start_offset_; } unsigned StartOffset() const { return start_offset_; }
unsigned EndOffset() const { return end_offset_; } unsigned EndOffset() const { return end_offset_; }
unsigned Length() const { return end_offset_ - start_offset_; } unsigned Length() const { return end_offset_ - start_offset_; }
...@@ -101,6 +105,8 @@ class CORE_EXPORT NGInlineItem { ...@@ -101,6 +105,8 @@ class CORE_EXPORT NGInlineItem {
String ToString() const; String ToString() const;
private: private:
void ComputeBoxProperties();
unsigned start_offset_; unsigned start_offset_;
unsigned end_offset_; unsigned end_offset_;
UScriptCode script_; UScriptCode script_;
...@@ -112,6 +118,7 @@ class CORE_EXPORT NGInlineItem { ...@@ -112,6 +118,7 @@ class CORE_EXPORT NGInlineItem {
unsigned bidi_level_ : 8; // UBiDiLevel is defined as uint8_t. unsigned bidi_level_ : 8; // UBiDiLevel is defined as uint8_t.
unsigned shape_options_ : 2; unsigned shape_options_ : 2;
unsigned is_empty_item_ : 1; unsigned is_empty_item_ : 1;
unsigned should_create_box_fragment_ : 1;
friend class NGInlineNode; friend class NGInlineNode;
}; };
......
...@@ -64,9 +64,6 @@ struct CORE_EXPORT NGInlineItemResult { ...@@ -64,9 +64,6 @@ struct CORE_EXPORT NGInlineItemResult {
// Has start/end edge for open/close tags. // Has start/end edge for open/close tags.
bool has_edge = false; bool has_edge = false;
// Create a box when the box is empty, for open/close tags.
bool needs_box_when_empty = false;
// Inside of this may be breakable. False means there are no break // Inside of this may be breakable. False means there are no break
// opportunities, or has CSS properties that prohibit breaking. // opportunities, or has CSS properties that prohibit breaking.
// Used only during line breaking. // Used only during line breaking.
......
...@@ -35,15 +35,6 @@ ...@@ -35,15 +35,6 @@
namespace blink { namespace blink {
namespace { namespace {
inline bool ShouldCreateBoxFragment(const NGInlineItem& item,
const NGInlineItemResult& item_result) {
DCHECK(item.Style());
const ComputedStyle& style = *item.Style();
// TODO(kojii): We might need more conditions to create box fragments.
return style.HasBoxDecorationBackground() || style.HasOutline() ||
item_result.needs_box_when_empty;
}
// Represents a data struct that are needed for 'text-align' and justifications. // Represents a data struct that are needed for 'text-align' and justifications.
struct NGLineAlign { struct NGLineAlign {
STACK_ALLOCATED(); STACK_ALLOCATED();
...@@ -144,19 +135,18 @@ void NGInlineLayoutAlgorithm::CreateLine(NGLineInfo* line_info, ...@@ -144,19 +135,18 @@ void NGInlineLayoutAlgorithm::CreateLine(NGLineInfo* line_info,
} }
DCHECK(item.GetLayoutObject()->IsText() || DCHECK(item.GetLayoutObject()->IsText() ||
item.GetLayoutObject()->IsLayoutNGListItem()); item.GetLayoutObject()->IsLayoutNGListItem());
DCHECK(!box->text_metrics.IsEmpty());
if (item_result.shape_result) { if (item_result.shape_result) {
if (quirks_mode_) if (quirks_mode_)
box->ActivateTextMetrics(); box->EnsureTextMetrics(*item.Style(), baseline_type_);
// Take all used fonts into account if 'line-height: normal'. // Take all used fonts into account if 'line-height: normal'.
if (box->include_used_fonts && item.Type() == NGInlineItem::kText) { if (box->include_used_fonts && item.Type() == NGInlineItem::kText) {
box->AccumulateUsedFonts(item_result.shape_result.get(), box->AccumulateUsedFonts(item_result.shape_result.get(),
baseline_type_); baseline_type_);
} }
} else { } else {
if (quirks_mode_ && !box->HasMetrics())
box->ActivateTextMetrics();
DCHECK(!item.TextShapeResult()); // kControl or unit tests. DCHECK(!item.TextShapeResult()); // kControl or unit tests.
if (quirks_mode_ && !box->HasMetrics())
box->EnsureTextMetrics(*item.Style(), baseline_type_);
} }
text_builder.SetItem(&item_result, box->text_height); text_builder.SetItem(&item_result, box->text_height);
...@@ -169,20 +159,20 @@ void NGInlineLayoutAlgorithm::CreateLine(NGLineInfo* line_info, ...@@ -169,20 +159,20 @@ void NGInlineLayoutAlgorithm::CreateLine(NGLineInfo* line_info,
} else if (item.Type() == NGInlineItem::kOpenTag) { } else if (item.Type() == NGInlineItem::kOpenTag) {
box = box_states_->OnOpenTag(item, item_result, line_box_); box = box_states_->OnOpenTag(item, item_result, line_box_);
// Compute text metrics for all inline boxes since even empty inlines // Compute text metrics for all inline boxes since even empty inlines
// influence the line height. // influence the line height, except when quirks mode and the box is empty
// for the purpose of empty block calculation.
// https://drafts.csswg.org/css2/visudet.html#line-height // https://drafts.csswg.org/css2/visudet.html#line-height
box->ComputeTextMetrics(*item.Style(), baseline_type_, quirks_mode_); if (!(item.IsEmptyItem() && quirks_mode_))
if (quirks_mode_ && item_result.needs_box_when_empty) box->ComputeTextMetrics(*item.Style(), baseline_type_);
box->ActivateTextMetrics(); if (item.ShouldCreateBoxFragment())
if (ShouldCreateBoxFragment(item, item_result)) box->SetNeedsBoxFragment();
box->SetNeedsBoxFragment(item_result.needs_box_when_empty);
} else if (item.Type() == NGInlineItem::kCloseTag) { } else if (item.Type() == NGInlineItem::kCloseTag) {
if (box->needs_box_fragment || item_result.needs_box_when_empty) { if (!box->needs_box_fragment && item_result.inline_size)
if (item_result.needs_box_when_empty) box->SetNeedsBoxFragment();
box->SetNeedsBoxFragment(true); if (box->needs_box_fragment) {
box->SetLineRightForBoxFragment(item, item_result); box->SetLineRightForBoxFragment(item, item_result);
if (quirks_mode_) if (quirks_mode_)
box->ActivateTextMetrics(); box->EnsureTextMetrics(*item.Style(), baseline_type_);
} }
box = box_states_->OnCloseTag(&line_box_, box, baseline_type_); box = box_states_->OnCloseTag(&line_box_, box, baseline_type_);
} else if (item.Type() == NGInlineItem::kAtomicInline) { } else if (item.Type() == NGInlineItem::kAtomicInline) {
...@@ -277,13 +267,15 @@ void NGInlineLayoutAlgorithm::PlaceGeneratedContent( ...@@ -277,13 +267,15 @@ void NGInlineLayoutAlgorithm::PlaceGeneratedContent(
NGInlineBoxState* box, NGInlineBoxState* box,
NGTextFragmentBuilder* text_builder) { NGTextFragmentBuilder* text_builder) {
if (box->CanAddTextOfStyle(*style)) { if (box->CanAddTextOfStyle(*style)) {
if (quirks_mode_)
box->EnsureTextMetrics(*style, baseline_type_);
PlaceText(std::move(shape_result), std::move(style), 0, box, text_builder); PlaceText(std::move(shape_result), std::move(style), 0, box, text_builder);
} else { } else {
scoped_refptr<ComputedStyle> text_style = scoped_refptr<ComputedStyle> text_style =
ComputedStyle::CreateAnonymousStyleWithDisplay(*style, ComputedStyle::CreateAnonymousStyleWithDisplay(*style,
EDisplay::kInline); EDisplay::kInline);
NGInlineBoxState* box = box_states_->OnOpenTag(*text_style, line_box_); NGInlineBoxState* box = box_states_->OnOpenTag(*text_style, line_box_);
box->ComputeTextMetrics(*text_style, baseline_type_, false); box->ComputeTextMetrics(*text_style, baseline_type_);
PlaceText(std::move(shape_result), std::move(style), 0, box, text_builder); PlaceText(std::move(shape_result), std::move(style), 0, box, text_builder);
box_states_->OnCloseTag(&line_box_, box, baseline_type_); box_states_->OnCloseTag(&line_box_, box, baseline_type_);
} }
...@@ -298,6 +290,7 @@ void NGInlineLayoutAlgorithm::PlaceText( ...@@ -298,6 +290,7 @@ void NGInlineLayoutAlgorithm::PlaceText(
unsigned start_offset = shape_result->StartIndexForResult(); unsigned start_offset = shape_result->StartIndexForResult();
unsigned end_offset = shape_result->EndIndexForResult(); unsigned end_offset = shape_result->EndIndexForResult();
LayoutUnit inline_size = shape_result->SnappedWidth(); LayoutUnit inline_size = shape_result->SnappedWidth();
DCHECK(!box->text_metrics.IsEmpty());
text_builder->SetText(std::move(style), std::move(shape_result), text_builder->SetText(std::move(style), std::move(shape_result),
{inline_size, box->text_height}); {inline_size, box->text_height});
scoped_refptr<NGPhysicalTextFragment> text_fragment = scoped_refptr<NGPhysicalTextFragment> text_fragment =
...@@ -402,8 +395,10 @@ bool NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects( ...@@ -402,8 +395,10 @@ bool NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects(
void NGInlineLayoutAlgorithm::PlaceListMarker(const NGInlineItem& item, void NGInlineLayoutAlgorithm::PlaceListMarker(const NGInlineItem& item,
NGInlineItemResult* item_result, NGInlineItemResult* item_result,
const NGLineInfo& line_info) { const NGLineInfo& line_info) {
if (quirks_mode_) if (quirks_mode_) {
box_states_->LineBoxState().ActivateTextMetrics(); box_states_->LineBoxState().EnsureTextMetrics(*item.Style(),
baseline_type_);
}
item_result->layout_result = item_result->layout_result =
NGBlockNode(ToLayoutBox(item.GetLayoutObject())) NGBlockNode(ToLayoutBox(item.GetLayoutObject()))
......
...@@ -69,6 +69,41 @@ TEST_F(NGInlineLayoutAlgorithmTest, BreakToken) { ...@@ -69,6 +69,41 @@ TEST_F(NGInlineLayoutAlgorithmTest, BreakToken) {
EXPECT_TRUE(line3->BreakToken()->IsFinished()); EXPECT_TRUE(line3->BreakToken()->IsFinished());
} }
// This test ensures that if an inline box generates (or does not generate) box
// fragments for a wrapped line, it should consistently do so for other lines
// too, when the inline box is fragmented to multiple lines.
TEST_F(NGInlineLayoutAlgorithmTest, BoxForEndMargin) {
LoadAhem();
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
html, body { margin: 0; }
#container {
font: 10px/1 Ahem;
width: 50px;
}
span {
border-right: 10px solid blue;
}
</style>
<!-- This line wraps, and only 2nd line has a border. -->
<div id=container>12 <span>3 45</span> 6</div>
)HTML");
LayoutBlockFlow* block_flow =
ToLayoutBlockFlow(GetLayoutObjectByElementId("container"));
const NGPhysicalBoxFragment* block_box = block_flow->CurrentFragment();
ASSERT_TRUE(block_box);
EXPECT_EQ(2u, block_box->Children().size());
const NGPhysicalLineBoxFragment& line_box1 =
ToNGPhysicalLineBoxFragment(*block_box->Children()[0]);
EXPECT_EQ(2u, line_box1.Children().size());
// The <span> generates a box fragment for the 2nd line because it has a
// right border. It should also generate a box fragment for the 1st line even
// though there's no borders on the 1st line.
EXPECT_EQ(NGPhysicalFragment::kFragmentBox, line_box1.Children()[1]->Type());
}
// A block with inline children generates fragment tree as follows: // A block with inline children generates fragment tree as follows:
// - A box fragment created by NGBlockNode // - A box fragment created by NGBlockNode
// - A wrapper box fragment created by NGInlineNode // - A wrapper box fragment created by NGInlineNode
......
...@@ -689,10 +689,10 @@ void NGLineBreaker::HandleOpenTag(const NGInlineItem& item, ...@@ -689,10 +689,10 @@ void NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
DCHECK(item.Style()); DCHECK(item.Style());
const ComputedStyle& style = *item.Style(); const ComputedStyle& style = *item.Style();
item_result->needs_box_when_empty = false;
item_result->has_edge = item.HasStartEdge(); item_result->has_edge = item.HasStartEdge();
if (style.HasBorder() || style.HasPadding() || if (item.ShouldCreateBoxFragment() &&
(style.HasMargin() && item_result->has_edge)) { (style.HasBorder() || style.HasPadding() ||
(style.HasMargin() && item_result->has_edge))) {
NGBoxStrut borders = ComputeBorders(constraint_space_, style); NGBoxStrut borders = ComputeBorders(constraint_space_, style);
NGBoxStrut paddings = ComputePadding(constraint_space_, style); NGBoxStrut paddings = ComputePadding(constraint_space_, style);
item_result->padding = paddings; item_result->padding = paddings;
...@@ -710,15 +710,12 @@ void NGLineBreaker::HandleOpenTag(const NGInlineItem& item, ...@@ -710,15 +710,12 @@ void NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
// line boxes to be zero-height, tests indicate that only inline direction // line boxes to be zero-height, tests indicate that only inline direction
// of them do so. See should_create_line_box_. // of them do so. See should_create_line_box_.
// Force to create a box, because such inline boxes affect line heights. // Force to create a box, because such inline boxes affect line heights.
item_result->needs_box_when_empty = if (!line_.should_create_line_box &&
item_result->inline_size || (item_result->inline_size ||
(item_result->margins.inline_start && !in_line_height_quirks_mode_); (item_result->margins.inline_start && !in_line_height_quirks_mode_)))
line_.should_create_line_box |= item_result->needs_box_when_empty; line_.should_create_line_box = true;
} }
} }
item_result->needs_box_when_empty |=
style.CanContainAbsolutePositionObjects() ||
style.CanContainFixedPositionObjects();
SetCurrentStyle(style); SetCurrentStyle(style);
MoveToNextOf(item); MoveToNextOf(item);
} }
...@@ -726,7 +723,6 @@ void NGLineBreaker::HandleOpenTag(const NGInlineItem& item, ...@@ -726,7 +723,6 @@ void NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
void NGLineBreaker::HandleCloseTag(const NGInlineItem& item, void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
NGInlineItemResults* item_results) { NGInlineItemResults* item_results) {
NGInlineItemResult* item_result = AddItem(item, item_results); NGInlineItemResult* item_result = AddItem(item, item_results);
item_result->needs_box_when_empty = false;
item_result->has_edge = item.HasEndEdge(); item_result->has_edge = item.HasEndEdge();
if (item_result->has_edge) { if (item_result->has_edge) {
DCHECK(item.Style()); DCHECK(item.Style());
...@@ -738,10 +734,10 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item, ...@@ -738,10 +734,10 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
borders.inline_end + paddings.inline_end; borders.inline_end + paddings.inline_end;
line_.position += item_result->inline_size; line_.position += item_result->inline_size;
item_result->needs_box_when_empty = if (!line_.should_create_line_box &&
item_result->inline_size || (item_result->inline_size ||
(item_result->margins.inline_end && !in_line_height_quirks_mode_); (item_result->margins.inline_end && !in_line_height_quirks_mode_)))
line_.should_create_line_box |= item_result->needs_box_when_empty; line_.should_create_line_box = true;
} }
DCHECK(item.GetLayoutObject() && item.GetLayoutObject()->Parent()); DCHECK(item.GetLayoutObject() && item.GetLayoutObject()->Parent());
bool was_auto_wrap = auto_wrap_; bool was_auto_wrap = auto_wrap_;
......
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