Commit 86d41cbe authored by Ian Kilpatrick's avatar Ian Kilpatrick Committed by Commit Bot

[LayoutNG] Implement line-box avoidance of floats correctly.

This asks for the layout opporunties up front, then iterates through them
attempting to construct a line for each one, until something fits.

There are two checks:
1) For the inline-size of a linebox, directly after the line breaker has run.
2) For the block-size, after we've determined the line-height.

Bug: 	635619
Change-Id: Icb88791be2c4d267b1361c4a7ed47dec71b9a87d
Reviewed-on: https://chromium-review.googlesource.com/742041
Commit-Queue: Ian Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#513318}
parent 1ee958f6
......@@ -308,12 +308,6 @@ crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-bfc-
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-bfc-003-right-overflow.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-bfc-004.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-bfc-006.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-001l.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-001r.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-002l.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-002r.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-003l.xht [ Failure ]
crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-003r.xht [ Failure ]
crbug.com/723135 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-zero-height-wrap-001.xht [ Failure ]
crbug.com/723135 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-zero-height-wrap-002.xht [ Failure ]
......@@ -500,17 +494,9 @@ crbug.com/635619 virtual/layout_ng/fast/block/float/float-reparent-during-detach
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-and-text-indent-rl.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-and-text-indent.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-do-not-overhang-from-block-formatting-context.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-offset-image-quirk-line-height.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-offset-image-quirk.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-offset-image-strict-line-height.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-offset-image-strict.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-offset-inline-block-quirk-line-height.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-offset-inline-block-strict-line-height.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-with-margin-should-not-wrap.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-wrap-inside-inline-001.htm [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-wrap-inside-inline-002.htm [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-wrap-inside-inline-003.htm [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/floats-wrap-inside-inline-004.htm [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/independent-align-positioning.html [ Failure ]
crbug.com/635619 [ Mac ] virtual/layout_ng/fast/block/float/intruding-painted-twice.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/float/line-break-after-white-space-crash.html [ Pass Crash Timeout ]
......
......@@ -17,6 +17,33 @@ const char* kNGInlineItemTypeStrings[] = {
"Text", "Control", "AtomicInline", "OpenTag",
"CloseTag", "Floating", "OutOfFlowPositioned", "BidiControl"};
// Returns true if this item is "empty", i.e. if the node contains only empty
// items it will produce a single zero block-size line box.
static bool IsEmptyItem(NGInlineItem::NGInlineItemType type,
const ComputedStyle* style) {
if (type == NGInlineItem::kAtomicInline || type == NGInlineItem::kControl ||
type == NGInlineItem::kText)
return false;
if (type == NGInlineItem::kOpenTag) {
DCHECK(style);
if (!style->MarginStart().IsZero() || style->BorderStart().NonZero() ||
!style->PaddingStart().IsZero())
return false;
}
if (type == NGInlineItem::kCloseTag) {
DCHECK(style);
if (!style->MarginEnd().IsZero() || style->BorderEnd().NonZero() ||
!style->PaddingEnd().IsZero())
return false;
}
return true;
}
} // namespace
NGInlineItem::NGInlineItem(NGInlineItemType type,
......@@ -31,7 +58,8 @@ NGInlineItem::NGInlineItem(NGInlineItemType type,
layout_object_(layout_object),
type_(type),
bidi_level_(UBIDI_LTR),
shape_options_(kPreContext | kPostContext) {
shape_options_(kPreContext | kPostContext),
is_empty_item_(::blink::IsEmptyItem(type, style)) {
DCHECK_GE(end, start);
}
......
......@@ -65,6 +65,9 @@ class CORE_EXPORT NGInlineItem {
return static_cast<NGLayoutInlineShapeOptions>(shape_options_);
}
// If this item is "empty" for the purpose of empty block calculation.
bool IsEmptyItem() const { return is_empty_item_; }
unsigned StartOffset() const { return start_offset_; }
unsigned EndOffset() const { return end_offset_; }
unsigned Length() const { return end_offset_ - start_offset_; }
......@@ -111,6 +114,7 @@ class CORE_EXPORT NGInlineItem {
unsigned type_ : 4;
unsigned bidi_level_ : 8; // UBiDiLevel is defined as uint8_t.
unsigned shape_options_ : 2;
unsigned is_empty_item_ : 1;
friend class NGInlineNode;
};
......
......@@ -48,9 +48,11 @@ void NGLineInfo::SetLineStyle(const NGInlineNode& node,
}
void NGLineInfo::SetLineBfcOffset(NGBfcOffset line_bfc_offset,
LayoutUnit available_width) {
LayoutUnit available_width,
LayoutUnit width) {
line_bfc_offset_ = line_bfc_offset;
available_width_ = available_width;
width_ = width;
}
void NGLineInfo::SetLineEndShapeResult(
......
......@@ -131,8 +131,10 @@ class CORE_EXPORT NGLineInfo {
NGBfcOffset LineBfcOffset() const { return line_bfc_offset_; }
LayoutUnit AvailableWidth() const { return available_width_; }
LayoutUnit Width() const { return width_; }
void SetLineBfcOffset(NGBfcOffset line_bfc_offset,
LayoutUnit available_width);
LayoutUnit available_width,
LayoutUnit width);
// Start/end text offset of this line.
unsigned StartOffset() const { return start_offset_; }
......@@ -162,6 +164,7 @@ class CORE_EXPORT NGLineInfo {
NGBfcOffset line_bfc_offset_;
LayoutUnit available_width_;
LayoutUnit width_;
LayoutUnit text_indent_;
unsigned start_offset_;
......
......@@ -106,33 +106,6 @@ static bool ShouldRemoveNewline(const StringBuilder& before,
after_style);
}
// Returns true if this item is "empty", i.e. if the node contains only empty
// items it will produce a single zero block-size line box.
static bool IsItemEmpty(NGInlineItem::NGInlineItemType type,
const ComputedStyle* style) {
if (type == NGInlineItem::kAtomicInline || type == NGInlineItem::kControl ||
type == NGInlineItem::kText)
return false;
if (type == NGInlineItem::kOpenTag) {
DCHECK(style);
if (!style->MarginStart().IsZero() || style->BorderStart().NonZero() ||
!style->PaddingStart().IsZero())
return false;
}
if (type == NGInlineItem::kCloseTag) {
DCHECK(style);
if (!style->MarginEnd().IsZero() || style->BorderEnd().NonZero() ||
!style->PaddingEnd().IsZero())
return false;
}
return true;
}
static void AppendItem(Vector<NGInlineItem>* items,
NGInlineItem::NGInlineItemType type,
unsigned start,
......@@ -250,7 +223,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
if (last_collapsible_space_ == CollapsibleSpace::kSpace &&
!style->AutoWrap())
last_collapsible_space_ = CollapsibleSpace::kSpaceNoWrap;
is_empty_inline_ &= IsItemEmpty(NGInlineItem::kText, style);
is_empty_inline_ &= items_->back().IsEmptyItem();
}
}
......@@ -278,7 +251,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style,
layout_object);
is_empty_inline_ &= IsItemEmpty(NGInlineItem::kText, style);
is_empty_inline_ &= items_->back().IsEmptyItem();
start = end;
}
......@@ -339,7 +312,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Append(
unsigned end_offset = text_.length();
AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object);
is_empty_inline_ &= IsItemEmpty(type, style);
is_empty_inline_ &= items_->back().IsEmptyItem();
last_collapsible_space_ = CollapsibleSpace::kNone;
}
......@@ -363,7 +336,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendOpaque(
unsigned end_offset = text_.length();
AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object);
is_empty_inline_ &= IsItemEmpty(type, nullptr);
is_empty_inline_ &= items_->back().IsEmptyItem();
}
template <typename OffsetMappingBuilder>
......@@ -374,7 +347,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendOpaque(
unsigned end_offset = text_.length();
AppendItem(items_, type, end_offset, end_offset, style, layout_object);
is_empty_inline_ &= IsItemEmpty(type, style);
is_empty_inline_ &= items_->back().IsEmptyItem();
}
// Removes the collapsible newline at the end of |text_| if exists and the
......
......@@ -46,8 +46,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final
scoped_refptr<NGLayoutResult> Layout() override;
private:
scoped_refptr<NGLayoutResult> LayoutEmptyInline();
unsigned PositionLeadingItems(NGExclusionSpace*);
void PositionPendingFloats(LayoutUnit content_size, NGExclusionSpace*);
bool IsHorizontalWritingMode() const { return is_horizontal_writing_mode_; }
......@@ -96,7 +95,6 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final
unsigned is_horizontal_writing_mode_ : 1;
unsigned quirks_mode_ : 1;
std::unique_ptr<NGExclusionSpace> exclusion_space_;
Vector<NGPositionedFloat> positioned_floats_;
Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_;
};
......
......@@ -600,8 +600,13 @@ static LayoutUnit ComputeContentSize(NGInlineNode node,
LayoutUnit result;
while (!break_token || !break_token->IsFinished()) {
NGLineBreaker line_breaker(node, *space, &positioned_floats,
&unpositioned_floats, break_token.get());
if (!line_breaker.NextLine(empty_exclusion_space, &line_info))
&unpositioned_floats, &empty_exclusion_space, 0u,
break_token.get());
if (!line_breaker.NextLine(
NGLayoutOpportunity(
NGBfcOffset(),
NGLogicalSize({available_inline_size, NGSizeIndefinite})),
&line_info))
break;
break_token = line_breaker.CreateBreakToken(nullptr);
......
......@@ -9,6 +9,7 @@
#include "core/layout/ng/inline/ng_inline_node.h"
#include "core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "core/layout/ng/ng_exclusion_space.h"
#include "core/layout/ng/ng_fragment.h"
#include "core/layout/ng/ng_layout_result.h"
#include "core/layout/ng/ng_positioned_float.h"
......@@ -24,10 +25,31 @@ NGLineBoxFragmentBuilder::NGLineBoxFragmentBuilder(
NGLineBoxFragmentBuilder::~NGLineBoxFragmentBuilder() {}
void NGLineBoxFragmentBuilder::Reset() {
children_.clear();
offsets_.clear();
metrics_ = NGLineHeightMetrics();
inline_size_ = LayoutUnit();
}
NGLogicalSize NGLineBoxFragmentBuilder::Size() const {
return {inline_size_, metrics_.LineHeight().ClampNegativeToZero()};
}
LayoutUnit NGLineBoxFragmentBuilder::ComputeBlockSize() const {
LayoutUnit block_size;
NGWritingMode writing_mode(
FromPlatformWritingMode(node_.Style().GetWritingMode()));
for (size_t i = 0; i < children_.size(); ++i) {
block_size = std::max(
block_size, offsets_[i].block_offset +
NGFragment(writing_mode, *children_[i]).BlockSize());
}
return block_size;
}
const NGPhysicalFragment* NGLineBoxFragmentBuilder::Child::PhysicalFragment()
const {
return layout_result ? layout_result->PhysicalFragment().get()
......
......@@ -29,7 +29,10 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
TextDirection);
~NGLineBoxFragmentBuilder() override;
void Reset();
NGLogicalSize Size() const final;
LayoutUnit ComputeBlockSize() const;
void SetMetrics(const NGLineHeightMetrics&);
const NGLineHeightMetrics& Metrics() const { return metrics_; }
......
......@@ -34,19 +34,19 @@ class CORE_EXPORT NGLineBreaker {
const NGConstraintSpace&,
Vector<NGPositionedFloat>*,
Vector<scoped_refptr<NGUnpositionedFloat>>*,
NGExclusionSpace*,
unsigned handled_float_index,
const NGInlineBreakToken* = nullptr);
~NGLineBreaker() {}
// Compute the next line break point and produces NGInlineItemResults for
// the line.
bool NextLine(const NGExclusionSpace&, NGLineInfo*);
bool NextLine(const NGLayoutOpportunity&, NGLineInfo*);
// Create an NGInlineBreakToken for the last line returned by NextLine().
scoped_refptr<NGInlineBreakToken> CreateBreakToken(
std::unique_ptr<const NGInlineLayoutStateStack>) const;
NGExclusionSpace* ExclusionSpace() { return line_.exclusion_space.get(); }
private:
// This struct holds information for the current line.
struct LineData {
......@@ -59,7 +59,8 @@ class CORE_EXPORT NGLineBreaker {
// The current opportunity.
NGLayoutOpportunity opportunity;
std::unique_ptr<NGExclusionSpace> exclusion_space;
LayoutUnit line_left_bfc_offset;
LayoutUnit line_right_bfc_offset;
// We don't create "certain zero-height line boxes".
// https://drafts.csswg.org/css2/visuren.html#phantom-line-box
......@@ -72,7 +73,10 @@ class CORE_EXPORT NGLineBreaker {
// the next line.
bool is_after_forced_break = false;
LayoutUnit AvailableWidth() const { return opportunity.InlineSize(); }
LayoutUnit AvailableWidth() const {
DCHECK_GE(line_right_bfc_offset, line_left_bfc_offset);
return line_right_bfc_offset - line_left_bfc_offset;
}
bool CanFit() const { return position <= AvailableWidth(); }
bool CanFit(LayoutUnit extra) const {
return position + extra <= AvailableWidth();
......@@ -81,11 +85,7 @@ class CORE_EXPORT NGLineBreaker {
void BreakLine(NGLineInfo*);
void PrepareNextLine(const NGExclusionSpace&, NGLineInfo*);
bool HasFloatsAffectingCurrentLine() const;
void FindNextLayoutOpportunity();
void FindNextLayoutOpportunityWithMinimumInlineSize(LayoutUnit);
void PrepareNextLine(const NGLayoutOpportunity&, NGLineInfo*);
void ComputeLineLocation(NGLineInfo*) const;
......@@ -142,9 +142,12 @@ class CORE_EXPORT NGLineBreaker {
const NGConstraintSpace& constraint_space_;
Vector<NGPositionedFloat>* positioned_floats_;
Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats_;
NGExclusionSpace* exclusion_space_;
unsigned item_index_ = 0;
unsigned offset_ = 0;
bool previous_line_had_forced_break_ = false;
LayoutUnit bfc_line_offset_;
LayoutUnit bfc_block_offset_;
LazyLineBreakIterator break_iterator_;
HarfBuzzShaper shaper_;
......@@ -152,7 +155,7 @@ class CORE_EXPORT NGLineBreaker {
const Hyphenation* hyphenation_ = nullptr;
// Keep track of handled float items. See HandleFloat().
unsigned handled_floats_end_item_index_ = 0;
unsigned handled_floats_end_item_index_;
// The current base direction for the bidi algorithm.
// This is copied from NGInlineNode, then updated after each forced line break
......
......@@ -49,8 +49,13 @@ class NGLineBreakerTest : public NGBaseLayoutAlgorithmTest {
NGLineInfo line_info;
while (!break_token || !break_token->IsFinished()) {
NGLineBreaker line_breaker(node, *space, &positioned_floats,
&unpositioned_floats, break_token.get());
if (!line_breaker.NextLine(exclusion_space, &line_info))
&unpositioned_floats, &exclusion_space, 0u,
break_token.get());
if (!line_breaker.NextLine(
NGLayoutOpportunity(
NGBfcOffset(),
NGLogicalSize({available_width, NGSizeIndefinite})),
&line_info))
break;
break_token = line_breaker.CreateBreakToken(nullptr);
......
......@@ -51,6 +51,22 @@ NGLayoutOpportunity NGExclusionSpace::FindLayoutOpportunity(
return NGLayoutOpportunity();
}
Vector<NGLayoutOpportunity> NGExclusionSpace::AllLayoutOpportunities(
const NGBfcOffset& offset,
const NGLogicalSize& available_size) const {
Vector<NGLayoutOpportunity> opportunities;
NGLayoutOpportunityIterator opportunity_iter(*this, available_size, offset);
while (true) {
opportunities.push_back(opportunity_iter.Next());
if (opportunity_iter.IsAtEnd())
break;
}
return opportunities;
}
LayoutUnit NGExclusionSpace::ClearanceOffset(EClear clear_type) const {
switch (clear_type) {
case EClear::kNone:
......
......@@ -36,6 +36,10 @@ class CORE_EXPORT NGExclusionSpace {
const NGLogicalSize& available_size,
const NGLogicalSize& minimum_size) const;
Vector<NGLayoutOpportunity> AllLayoutOpportunities(
const NGBfcOffset& offset,
const NGLogicalSize& available_size) const;
// Returns the clearance offset based on the provided {@code clear_type}.
LayoutUnit ClearanceOffset(EClear clear_type) 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