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- ...@@ -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-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-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-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-001.xht [ Failure ]
crbug.com/723135 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-zero-height-wrap-002.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 ...@@ -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-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-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-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-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-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-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 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 [ 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 ] 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[] = { ...@@ -17,6 +17,33 @@ 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
// 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 } // namespace
NGInlineItem::NGInlineItem(NGInlineItemType type, NGInlineItem::NGInlineItem(NGInlineItemType type,
...@@ -31,7 +58,8 @@ NGInlineItem::NGInlineItem(NGInlineItemType type, ...@@ -31,7 +58,8 @@ NGInlineItem::NGInlineItem(NGInlineItemType type,
layout_object_(layout_object), layout_object_(layout_object),
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)) {
DCHECK_GE(end, start); DCHECK_GE(end, start);
} }
......
...@@ -65,6 +65,9 @@ class CORE_EXPORT NGInlineItem { ...@@ -65,6 +65,9 @@ class CORE_EXPORT NGInlineItem {
return static_cast<NGLayoutInlineShapeOptions>(shape_options_); 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 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_; }
...@@ -111,6 +114,7 @@ class CORE_EXPORT NGInlineItem { ...@@ -111,6 +114,7 @@ class CORE_EXPORT NGInlineItem {
unsigned type_ : 4; unsigned type_ : 4;
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;
friend class NGInlineNode; friend class NGInlineNode;
}; };
......
...@@ -48,9 +48,11 @@ void NGLineInfo::SetLineStyle(const NGInlineNode& node, ...@@ -48,9 +48,11 @@ void NGLineInfo::SetLineStyle(const NGInlineNode& node,
} }
void NGLineInfo::SetLineBfcOffset(NGBfcOffset line_bfc_offset, void NGLineInfo::SetLineBfcOffset(NGBfcOffset line_bfc_offset,
LayoutUnit available_width) { LayoutUnit available_width,
LayoutUnit width) {
line_bfc_offset_ = line_bfc_offset; line_bfc_offset_ = line_bfc_offset;
available_width_ = available_width; available_width_ = available_width;
width_ = width;
} }
void NGLineInfo::SetLineEndShapeResult( void NGLineInfo::SetLineEndShapeResult(
......
...@@ -131,8 +131,10 @@ class CORE_EXPORT NGLineInfo { ...@@ -131,8 +131,10 @@ class CORE_EXPORT NGLineInfo {
NGBfcOffset LineBfcOffset() const { return line_bfc_offset_; } NGBfcOffset LineBfcOffset() const { return line_bfc_offset_; }
LayoutUnit AvailableWidth() const { return available_width_; } LayoutUnit AvailableWidth() const { return available_width_; }
LayoutUnit Width() const { return width_; }
void SetLineBfcOffset(NGBfcOffset line_bfc_offset, void SetLineBfcOffset(NGBfcOffset line_bfc_offset,
LayoutUnit available_width); LayoutUnit available_width,
LayoutUnit width);
// Start/end text offset of this line. // Start/end text offset of this line.
unsigned StartOffset() const { return start_offset_; } unsigned StartOffset() const { return start_offset_; }
...@@ -162,6 +164,7 @@ class CORE_EXPORT NGLineInfo { ...@@ -162,6 +164,7 @@ class CORE_EXPORT NGLineInfo {
NGBfcOffset line_bfc_offset_; NGBfcOffset line_bfc_offset_;
LayoutUnit available_width_; LayoutUnit available_width_;
LayoutUnit width_;
LayoutUnit text_indent_; LayoutUnit text_indent_;
unsigned start_offset_; unsigned start_offset_;
......
...@@ -106,33 +106,6 @@ static bool ShouldRemoveNewline(const StringBuilder& before, ...@@ -106,33 +106,6 @@ static bool ShouldRemoveNewline(const StringBuilder& before,
after_style); 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, static void AppendItem(Vector<NGInlineItem>* items,
NGInlineItem::NGInlineItemType type, NGInlineItem::NGInlineItemType type,
unsigned start, unsigned start,
...@@ -250,7 +223,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>:: ...@@ -250,7 +223,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
if (last_collapsible_space_ == CollapsibleSpace::kSpace && if (last_collapsible_space_ == CollapsibleSpace::kSpace &&
!style->AutoWrap()) !style->AutoWrap())
last_collapsible_space_ = CollapsibleSpace::kSpaceNoWrap; 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>:: ...@@ -278,7 +251,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style, AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style,
layout_object); layout_object);
is_empty_inline_ &= IsItemEmpty(NGInlineItem::kText, style); is_empty_inline_ &= items_->back().IsEmptyItem();
start = end; start = end;
} }
...@@ -339,7 +312,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Append( ...@@ -339,7 +312,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Append(
unsigned end_offset = text_.length(); unsigned end_offset = text_.length();
AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object); 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; last_collapsible_space_ = CollapsibleSpace::kNone;
} }
...@@ -363,7 +336,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendOpaque( ...@@ -363,7 +336,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendOpaque(
unsigned end_offset = text_.length(); unsigned end_offset = text_.length();
AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object); 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> template <typename OffsetMappingBuilder>
...@@ -374,7 +347,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendOpaque( ...@@ -374,7 +347,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendOpaque(
unsigned end_offset = text_.length(); unsigned end_offset = text_.length();
AppendItem(items_, type, end_offset, end_offset, style, layout_object); 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 // Removes the collapsible newline at the end of |text_| if exists and the
......
...@@ -523,54 +523,122 @@ LayoutUnit NGInlineLayoutAlgorithm::ComputeContentSize( ...@@ -523,54 +523,122 @@ LayoutUnit NGInlineLayoutAlgorithm::ComputeContentSize(
} }
scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
if (Node().IsEmptyInline()) std::unique_ptr<NGExclusionSpace> initial_exclusion_space(
return LayoutEmptyInline(); WTF::MakeUnique<NGExclusionSpace>(ConstraintSpace().ExclusionSpace()));
DCHECK(ConstraintSpace().UnpositionedFloats().IsEmpty()); bool is_empty_inline = Node().IsEmptyInline();
DCHECK(ConstraintSpace().MarginStrut().IsEmpty());
container_builder_.SetBfcOffset(ConstraintSpace().BfcOffset());
scoped_refptr<NGInlineBreakToken> break_token = BreakToken(); if (!is_empty_inline) {
DCHECK(ConstraintSpace().UnpositionedFloats().IsEmpty());
DCHECK(ConstraintSpace().MarginStrut().IsEmpty());
container_builder_.SetBfcOffset(ConstraintSpace().BfcOffset());
}
// Copy the state stack from the unfinished break token if provided. This // In order to get the correct list of layout opportunities, we need to
// enforces the layout inputs immutability constraint. If we weren't provided // position any "leading" items (floats) within the exclusion space first.
// with a break token we just create an empty state stack. unsigned handled_item_index =
box_states_ = PositionLeadingItems(initial_exclusion_space.get());
break_token
? WTF::MakeUnique<NGInlineLayoutStateStack>(break_token->StateStack())
: WTF::MakeUnique<NGInlineLayoutStateStack>();
std::unique_ptr<NGExclusionSpace> exclusion_space( // If we are an empty inline, we don't have to run the full algorithm, we can
WTF::MakeUnique<NGExclusionSpace>(ConstraintSpace().ExclusionSpace())); // return now as we should have positioned all of our floats.
NGLineInfo line_info; if (is_empty_inline) {
DCHECK_EQ(handled_item_index, Node().Items().size());
container_builder_.SwapPositionedFloats(&positioned_floats_);
container_builder_.SwapUnpositionedFloats(&unpositioned_floats_);
container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut());
container_builder_.SetExclusionSpace(std::move(initial_exclusion_space));
Vector<NGOutOfFlowPositionedDescendant> descendant_candidates;
container_builder_.GetAndClearOutOfFlowDescendantCandidates(
&descendant_candidates);
for (auto& descendant : descendant_candidates)
container_builder_.AddOutOfFlowDescendant(descendant);
NGLineBreaker line_breaker(Node(), constraint_space_, &positioned_floats_, return container_builder_.ToLineBoxFragment();
&unpositioned_floats_, break_token.get()); }
// TODO(ikilpatrick): Does this always succeed when we aren't an empty inline?
if (line_breaker.NextLine(*exclusion_space, &line_info)) { DCHECK(container_builder_.BfcOffset());
CreateLine(&line_info, line_breaker.ExclusionSpace());
// We query all the layout opportunities on the initial exclusion space up
// front, as if the line breaker may add floats and change the opportunities.
Vector<NGLayoutOpportunity> opportunities =
initial_exclusion_space->AllLayoutOpportunities(
ConstraintSpace().BfcOffset(), ConstraintSpace().AvailableSize());
Vector<NGPositionedFloat> positioned_floats;
DCHECK(unpositioned_floats_.IsEmpty());
std::unique_ptr<NGExclusionSpace> exclusion_space;
NGInlineBreakToken* break_token = BreakToken();
for (const auto& opportunity : opportunities) {
// Copy the state stack from the unfinished break token if provided. This
// enforces the layout inputs immutability constraint. If we weren't
// provided with a break token we just create an empty state stack.
box_states_ = break_token ? WTF::MakeUnique<NGInlineLayoutStateStack>(
break_token->StateStack())
: WTF::MakeUnique<NGInlineLayoutStateStack>();
// Reset any state that may have been modified in a previous pass.
positioned_floats.clear();
unpositioned_floats_.clear();
container_builder_.Reset();
exclusion_space =
WTF::MakeUnique<NGExclusionSpace>(*initial_exclusion_space);
NGLineInfo line_info;
NGLineBreaker line_breaker(Node(), constraint_space_, &positioned_floats,
&unpositioned_floats_, exclusion_space.get(),
handled_item_index, break_token);
// TODO(ikilpatrick): Does this always succeed when we aren't an empty
// inline?
if (!line_breaker.NextLine(opportunity, &line_info))
break;
// If this fragment will be larger than the inline-size of the opportunity,
// *and* the opportunity is smaller than the available inline-size,
// continue to the next opportunity.
if (line_info.Width() > opportunity.InlineSize() &&
opportunity.InlineSize() !=
ConstraintSpace().AvailableSize().inline_size)
continue;
CreateLine(&line_info, exclusion_space.get());
// We now can check the block-size of the fragment, and it fits within the
// opportunity.
LayoutUnit block_size = container_builder_.ComputeBlockSize();
if (block_size > opportunity.BlockSize())
continue;
LayoutUnit line_height =
container_builder_.Metrics().LineHeight().ClampNegativeToZero();
// Success!
positioned_floats_.AppendVector(positioned_floats);
container_builder_.SetBreakToken( container_builder_.SetBreakToken(
line_breaker.CreateBreakToken(std::move(box_states_))); line_breaker.CreateBreakToken(std::move(box_states_)));
exclusion_space = // Place any remaining floats which couldn't fit on the line.
WTF::MakeUnique<NGExclusionSpace>(*line_breaker.ExclusionSpace()); PositionPendingFloats(line_height, exclusion_space.get());
}
// Place any remaining floats which couldn't fit on the line. // A <br clear=both> will strech the line-box height, such that the
LayoutUnit content_size = // block-end edge will clear any floats.
container_builder_.Metrics().LineHeight().ClampNegativeToZero(); // TODO(ikilpatrick): Move this into ng_block_layout_algorithm.
PositionPendingFloats(content_size, exclusion_space.get()); container_builder_.SetBlockSize(
ComputeContentSize(line_info, *exclusion_space, line_height));
// A <br clear=both> will strech the line-box height, such that the block-end break;
// edge will clear any floats. }
container_builder_.SetBlockSize(ComputeContentSize(
line_info, *exclusion_space,
container_builder_.Metrics().LineHeight().ClampNegativeToZero()));
// We shouldn't have any unpositioned floats if we aren't empty. // We shouldn't have any unpositioned floats if we aren't empty.
DCHECK(unpositioned_floats_.IsEmpty()); DCHECK(unpositioned_floats_.IsEmpty());
container_builder_.SwapPositionedFloats(&positioned_floats_); container_builder_.SwapPositionedFloats(&positioned_floats_);
container_builder_.SetExclusionSpace(std::move(exclusion_space)); container_builder_.SetExclusionSpace(
exclusion_space ? std::move(exclusion_space)
: std::move(initial_exclusion_space));
Vector<NGOutOfFlowPositionedDescendant> descendant_candidates; Vector<NGOutOfFlowPositionedDescendant> descendant_candidates;
container_builder_.GetAndClearOutOfFlowDescendantCandidates( container_builder_.GetAndClearOutOfFlowDescendantCandidates(
...@@ -580,11 +648,19 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { ...@@ -580,11 +648,19 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
return container_builder_.ToLineBoxFragment(); return container_builder_.ToLineBoxFragment();
} }
scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::LayoutEmptyInline() { // This positions any "leading" floats within the given exclusion space.
// If we are also an empty inline, it will add any out-of-flow descendants.
// TODO(ikilpatrick): Do we need to always add the OOFs here?
unsigned NGInlineLayoutAlgorithm::PositionLeadingItems(
NGExclusionSpace* exclusion_space) {
const Vector<NGInlineItem>& items = Node().Items(); const Vector<NGInlineItem>& items = Node().Items();
bool is_empty_inline = Node().IsEmptyInline();
LayoutUnit bfc_line_offset = ConstraintSpace().BfcOffset().line_offset; LayoutUnit bfc_line_offset = ConstraintSpace().BfcOffset().line_offset;
for (const auto& item : items) { unsigned index = BreakToken() ? BreakToken()->ItemIndex() : 0;
for (; index < items.size(); ++index) {
const auto& item = items[index];
if (item.Type() == NGInlineItem::kFloating) { if (item.Type() == NGInlineItem::kFloating) {
NGBlockNode node(ToLayoutBox(item.GetLayoutObject())); NGBlockNode node(ToLayoutBox(item.GetLayoutObject()));
NGBoxStrut margins = NGBoxStrut margins =
...@@ -594,36 +670,24 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::LayoutEmptyInline() { ...@@ -594,36 +670,24 @@ scoped_refptr<NGLayoutResult> NGInlineLayoutAlgorithm::LayoutEmptyInline() {
ConstraintSpace().AvailableSize(), ConstraintSpace().AvailableSize(),
ConstraintSpace().PercentageResolutionSize(), bfc_line_offset, ConstraintSpace().PercentageResolutionSize(), bfc_line_offset,
bfc_line_offset, margins, node, /* break_token */ nullptr)); bfc_line_offset, margins, node, /* break_token */ nullptr));
} else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) { } else if (is_empty_inline &&
item.Type() == NGInlineItem::kOutOfFlowPositioned) {
NGBlockNode node(ToLayoutBox(item.GetLayoutObject())); NGBlockNode node(ToLayoutBox(item.GetLayoutObject()));
container_builder_.AddOutOfFlowChildCandidate( container_builder_.AddOutOfFlowChildCandidate(
node, NGLogicalOffset(), CurrentDirection(node.Style().Direction())); node, NGLogicalOffset(), CurrentDirection(node.Style().Direction()));
} else {
DCHECK_NE(item.Type(), NGInlineItem::kAtomicInline);
DCHECK_NE(item.Type(), NGInlineItem::kControl);
DCHECK_NE(item.Type(), NGInlineItem::kText);
} }
}
std::unique_ptr<NGExclusionSpace> exclusion_space(
WTF::MakeUnique<NGExclusionSpace>(ConstraintSpace().ExclusionSpace()));
// The content_size is zero for an empty inline. // Abort if we've found something that makes this a non-empty inline.
if (ConstraintSpace().FloatsBfcOffset()) if (!item.IsEmptyItem()) {
PositionPendingFloats(/* content_size */ LayoutUnit(), DCHECK(!is_empty_inline);
exclusion_space.get()); break;
}
}
container_builder_.SwapPositionedFloats(&positioned_floats_); if (ConstraintSpace().FloatsBfcOffset() || container_builder_.BfcOffset())
container_builder_.SwapUnpositionedFloats(&unpositioned_floats_); PositionPendingFloats(/* content_size */ LayoutUnit(), exclusion_space);
container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut());
container_builder_.SetExclusionSpace(std::move(exclusion_space));
Vector<NGOutOfFlowPositionedDescendant> descendant_candidates; return index;
container_builder_.GetAndClearOutOfFlowDescendantCandidates(
&descendant_candidates);
for (auto& descendant : descendant_candidates)
container_builder_.AddOutOfFlowDescendant(descendant);
return container_builder_.ToLineBoxFragment();
} }
void NGInlineLayoutAlgorithm::PositionPendingFloats( void NGInlineLayoutAlgorithm::PositionPendingFloats(
......
...@@ -46,8 +46,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final ...@@ -46,8 +46,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final
scoped_refptr<NGLayoutResult> Layout() override; scoped_refptr<NGLayoutResult> Layout() override;
private: private:
scoped_refptr<NGLayoutResult> LayoutEmptyInline(); unsigned PositionLeadingItems(NGExclusionSpace*);
void PositionPendingFloats(LayoutUnit content_size, NGExclusionSpace*); void PositionPendingFloats(LayoutUnit content_size, NGExclusionSpace*);
bool IsHorizontalWritingMode() const { return is_horizontal_writing_mode_; } bool IsHorizontalWritingMode() const { return is_horizontal_writing_mode_; }
...@@ -96,7 +95,6 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final ...@@ -96,7 +95,6 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final
unsigned is_horizontal_writing_mode_ : 1; unsigned is_horizontal_writing_mode_ : 1;
unsigned quirks_mode_ : 1; unsigned quirks_mode_ : 1;
std::unique_ptr<NGExclusionSpace> exclusion_space_;
Vector<NGPositionedFloat> positioned_floats_; Vector<NGPositionedFloat> positioned_floats_;
Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_; Vector<scoped_refptr<NGUnpositionedFloat>> unpositioned_floats_;
}; };
......
...@@ -600,8 +600,13 @@ static LayoutUnit ComputeContentSize(NGInlineNode node, ...@@ -600,8 +600,13 @@ static LayoutUnit ComputeContentSize(NGInlineNode node,
LayoutUnit result; LayoutUnit result;
while (!break_token || !break_token->IsFinished()) { while (!break_token || !break_token->IsFinished()) {
NGLineBreaker line_breaker(node, *space, &positioned_floats, NGLineBreaker line_breaker(node, *space, &positioned_floats,
&unpositioned_floats, break_token.get()); &unpositioned_floats, &empty_exclusion_space, 0u,
if (!line_breaker.NextLine(empty_exclusion_space, &line_info)) break_token.get());
if (!line_breaker.NextLine(
NGLayoutOpportunity(
NGBfcOffset(),
NGLogicalSize({available_inline_size, NGSizeIndefinite})),
&line_info))
break; break;
break_token = line_breaker.CreateBreakToken(nullptr); break_token = line_breaker.CreateBreakToken(nullptr);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "core/layout/ng/inline/ng_inline_node.h" #include "core/layout/ng/inline/ng_inline_node.h"
#include "core/layout/ng/inline/ng_physical_line_box_fragment.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_exclusion_space.h"
#include "core/layout/ng/ng_fragment.h"
#include "core/layout/ng/ng_layout_result.h" #include "core/layout/ng/ng_layout_result.h"
#include "core/layout/ng/ng_positioned_float.h" #include "core/layout/ng/ng_positioned_float.h"
...@@ -24,10 +25,31 @@ NGLineBoxFragmentBuilder::NGLineBoxFragmentBuilder( ...@@ -24,10 +25,31 @@ NGLineBoxFragmentBuilder::NGLineBoxFragmentBuilder(
NGLineBoxFragmentBuilder::~NGLineBoxFragmentBuilder() {} NGLineBoxFragmentBuilder::~NGLineBoxFragmentBuilder() {}
void NGLineBoxFragmentBuilder::Reset() {
children_.clear();
offsets_.clear();
metrics_ = NGLineHeightMetrics();
inline_size_ = LayoutUnit();
}
NGLogicalSize NGLineBoxFragmentBuilder::Size() const { NGLogicalSize NGLineBoxFragmentBuilder::Size() const {
return {inline_size_, metrics_.LineHeight().ClampNegativeToZero()}; 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 NGPhysicalFragment* NGLineBoxFragmentBuilder::Child::PhysicalFragment()
const { const {
return layout_result ? layout_result->PhysicalFragment().get() return layout_result ? layout_result->PhysicalFragment().get()
......
...@@ -29,7 +29,10 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final ...@@ -29,7 +29,10 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
TextDirection); TextDirection);
~NGLineBoxFragmentBuilder() override; ~NGLineBoxFragmentBuilder() override;
void Reset();
NGLogicalSize Size() const final; NGLogicalSize Size() const final;
LayoutUnit ComputeBlockSize() const;
void SetMetrics(const NGLineHeightMetrics&); void SetMetrics(const NGLineHeightMetrics&);
const NGLineHeightMetrics& Metrics() const { return metrics_; } const NGLineHeightMetrics& Metrics() const { return metrics_; }
......
...@@ -23,14 +23,18 @@ NGLineBreaker::NGLineBreaker( ...@@ -23,14 +23,18 @@ NGLineBreaker::NGLineBreaker(
const NGConstraintSpace& space, const NGConstraintSpace& space,
Vector<NGPositionedFloat>* positioned_floats, Vector<NGPositionedFloat>* positioned_floats,
Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats, Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats,
NGExclusionSpace* exclusion_space,
unsigned handled_float_index,
const NGInlineBreakToken* break_token) const NGInlineBreakToken* break_token)
: node_(node), : node_(node),
constraint_space_(space), constraint_space_(space),
positioned_floats_(positioned_floats), positioned_floats_(positioned_floats),
unpositioned_floats_(unpositioned_floats), unpositioned_floats_(unpositioned_floats),
exclusion_space_(exclusion_space),
break_iterator_(node.Text()), break_iterator_(node.Text()),
shaper_(node.Text().Characters16(), node.Text().length()), shaper_(node.Text().Characters16(), node.Text().length()),
spacing_(node.Text()), spacing_(node.Text()),
handled_floats_end_item_index_(handled_float_index),
base_direction_(node_.BaseDirection()) { base_direction_(node_.BaseDirection()) {
if (break_token) { if (break_token) {
item_index_ = break_token->ItemIndex(); item_index_ = break_token->ItemIndex();
...@@ -65,7 +69,7 @@ void NGLineBreaker::ComputeBaseDirection() { ...@@ -65,7 +69,7 @@ void NGLineBreaker::ComputeBaseDirection() {
} }
// Initialize internal states for the next line. // Initialize internal states for the next line.
void NGLineBreaker::PrepareNextLine(const NGExclusionSpace& exclusion_space, void NGLineBreaker::PrepareNextLine(const NGLayoutOpportunity& opportunity,
NGLineInfo* line_info) { NGLineInfo* line_info) {
NGInlineItemResults* item_results = &line_info->Results(); NGInlineItemResults* item_results = &line_info->Results();
item_results->clear(); item_results->clear();
...@@ -83,18 +87,17 @@ void NGLineBreaker::PrepareNextLine(const NGExclusionSpace& exclusion_space, ...@@ -83,18 +87,17 @@ void NGLineBreaker::PrepareNextLine(const NGExclusionSpace& exclusion_space,
// regardless of 'text-indent'. // regardless of 'text-indent'.
line_.position = line_info->TextIndent(); line_.position = line_info->TextIndent();
line_.exclusion_space = WTF::MakeUnique<NGExclusionSpace>(exclusion_space); line_.opportunity = opportunity;
line_.line_left_bfc_offset = opportunity.LineStartOffset();
// We are only able to calculate our available_width if our container has line_.line_right_bfc_offset = opportunity.LineEndOffset();
// been positioned in the BFC coordinate space yet. bfc_block_offset_ = opportunity.BlockStartOffset();
FindNextLayoutOpportunity();
} }
bool NGLineBreaker::NextLine(const NGExclusionSpace& exclusion_space, bool NGLineBreaker::NextLine(const NGLayoutOpportunity& opportunity,
NGLineInfo* line_info) { NGLineInfo* line_info) {
bfc_block_offset_ = constraint_space_.BfcOffset().block_offset; bfc_block_offset_ = constraint_space_.BfcOffset().block_offset;
PrepareNextLine(exclusion_space, line_info); PrepareNextLine(opportunity, line_info);
BreakLine(line_info); BreakLine(line_info);
line_info->SetEndOffset(offset_); line_info->SetEndOffset(offset_);
...@@ -190,52 +193,8 @@ void NGLineBreaker::BreakLine(NGLineInfo* line_info) { ...@@ -190,52 +193,8 @@ void NGLineBreaker::BreakLine(NGLineInfo* line_info) {
line_info->SetIsLastLine(true); line_info->SetIsLastLine(true);
} }
// @return if there are floats that affect current line.
// This is different from the clearance offset in that floats outside of the
// current layout opportunities, such as floats in margin/padding, or floats
// below such floats, are not included.
bool NGLineBreaker::HasFloatsAffectingCurrentLine() const {
return line_.opportunity.InlineSize() !=
constraint_space_.AvailableSize().inline_size;
}
// Update the inline size of the first layout opportunity from the given
// content_offset.
void NGLineBreaker::FindNextLayoutOpportunity() {
NGBfcOffset origin_offset = {constraint_space_.BfcOffset().line_offset,
bfc_block_offset_};
line_.opportunity = line_.exclusion_space->FindLayoutOpportunity(
origin_offset, constraint_space_.AvailableSize(),
/* minimum_size */ NGLogicalSize());
// When floats/exclusions occupies the entire line (e.g., float: left; width:
// 100%), zero-inline-size opportunities are not included in the iterator.
// Instead, the block offset of the first opportunity is pushed down to avoid
// such floats/exclusions. Set the line box location to it.
bfc_block_offset_ = line_.opportunity.BlockStartOffset();
}
// Finds a layout opportunity that has the given minimum inline size, or the one
// without floats/exclusions (and that there will not be wider oppotunities than
// that,) and moves |bfc_block_offset_| down to it.
//
// Used to move lines down when no break opportunities can fit in a line that
// has floats.
void NGLineBreaker::FindNextLayoutOpportunityWithMinimumInlineSize(
LayoutUnit min_inline_size) {
NGBfcOffset origin_offset = {constraint_space_.BfcOffset().line_offset,
bfc_block_offset_};
NGLogicalSize minimum_size(min_inline_size, LayoutUnit());
line_.opportunity = line_.exclusion_space->FindLayoutOpportunity(
origin_offset, constraint_space_.AvailableSize(), minimum_size);
bfc_block_offset_ = line_.opportunity.BlockStartOffset();
}
void NGLineBreaker::ComputeLineLocation(NGLineInfo* line_info) const { void NGLineBreaker::ComputeLineLocation(NGLineInfo* line_info) const {
LayoutUnit line_bfc_offset = line_.opportunity.LineStartOffset(); LayoutUnit bfc_line_offset = line_.line_left_bfc_offset;
LayoutUnit available_width = line_.AvailableWidth(); LayoutUnit available_width = line_.AvailableWidth();
// Indenting should move the current position to compute the size of // Indenting should move the current position to compute the size of
...@@ -245,12 +204,12 @@ void NGLineBreaker::ComputeLineLocation(NGLineInfo* line_info) const { ...@@ -245,12 +204,12 @@ void NGLineBreaker::ComputeLineLocation(NGLineInfo* line_info) const {
// Move the line box by indent. Negative indents are ink overflow, let the // Move the line box by indent. Negative indents are ink overflow, let the
// line box overflow from the container box. // line box overflow from the container box.
if (IsLtr(line_info->BaseDirection())) if (IsLtr(line_info->BaseDirection()))
line_bfc_offset += text_indent; bfc_line_offset += text_indent;
available_width -= text_indent; available_width -= text_indent;
} }
line_info->SetLineBfcOffset({line_bfc_offset, bfc_block_offset_}, line_info->SetLineBfcOffset({bfc_line_offset, bfc_block_offset_},
available_width); available_width, line_.position);
} }
bool NGLineBreaker::IsFirstBreakOpportunity(unsigned offset, bool NGLineBreaker::IsFirstBreakOpportunity(unsigned offset,
...@@ -499,13 +458,10 @@ NGLineBreaker::LineBreakState NGLineBreaker::HandleFloat( ...@@ -499,13 +458,10 @@ NGLineBreaker::LineBreakState NGLineBreaker::HandleFloat(
// twice. // twice.
// Ideally rewind can take floats out of floats list, but the difference is // Ideally rewind can take floats out of floats list, but the difference is
// sutble compared to the complexity. // sutble compared to the complexity.
// TODO(kojii): Keep a list of floats in a separate vector, then "commit" them
// inside NGLineLayoutAlgorithm.
if (item_index_ < handled_floats_end_item_index_) { if (item_index_ < handled_floats_end_item_index_) {
MoveToNextOf(item); MoveToNextOf(item);
return ComputeIsBreakableAfter(item_result); return ComputeIsBreakableAfter(item_result);
} }
handled_floats_end_item_index_ = item_index_ + 1;
NGBlockNode node(ToLayoutBox(item.GetLayoutObject())); NGBlockNode node(ToLayoutBox(item.GetLayoutObject()));
...@@ -523,30 +479,52 @@ NGLineBreaker::LineBreakState NGLineBreaker::HandleFloat( ...@@ -523,30 +479,52 @@ NGLineBreaker::LineBreakState NGLineBreaker::HandleFloat(
margins, node, margins, node,
/* break_token */ nullptr); /* break_token */ nullptr);
LayoutUnit inline_size = ComputeInlineSizeForUnpositionedFloat( LayoutUnit inline_margin_size =
constraint_space_, unpositioned_float.get()); (ComputeInlineSizeForUnpositionedFloat(constraint_space_,
unpositioned_float.get()) +
// We can only determine if our float will fit if we have an available_width margins.InlineSum())
// I.e. we may not have come across any text yet, in order to be able to .ClampNegativeToZero();
// resolve the BFC position.
bool float_does_not_fit = !line_.CanFit(inline_size + margins.InlineSum()); // The float should be positioned after the current line if:
// - It can't fit.
// - It will be moved down due to block-start edge alignment.
// - It will be moved down due to clearance.
bool float_after_line =
!line_.CanFit(inline_margin_size) ||
exclusion_space_->LastFloatBlockStart() > bfc_block_offset_ ||
exclusion_space_->ClearanceOffset(float_style.Clear()) >
bfc_block_offset_;
// Check if we already have a pending float. That's because a float cannot be // Check if we already have a pending float. That's because a float cannot be
// higher than any block or floated box generated before. // higher than any block or floated box generated before.
if (!unpositioned_floats_->IsEmpty() || float_does_not_fit) { if (!unpositioned_floats_->IsEmpty() || float_after_line) {
unpositioned_floats_->push_back(std::move(unpositioned_float)); unpositioned_floats_->push_back(std::move(unpositioned_float));
} else { } else {
LayoutUnit origin_block_offset = bfc_block_offset_; LayoutUnit origin_block_offset = bfc_block_offset_;
NGPositionedFloat positioned_float = PositionFloat( NGPositionedFloat positioned_float = PositionFloat(
origin_block_offset, constraint_space_.BfcOffset().block_offset, origin_block_offset, constraint_space_.BfcOffset().block_offset,
unpositioned_float.get(), constraint_space_, unpositioned_float.get(), constraint_space_, exclusion_space_);
line_.exclusion_space.get());
positioned_floats_->push_back(positioned_float); positioned_floats_->push_back(positioned_float);
// We need to recalculate the available_width as the float probably DCHECK_EQ(positioned_float.bfc_offset.block_offset,
// consumed space on the line. bfc_block_offset_ + margins.block_start);
FindNextLayoutOpportunity();
if (float_style.Floating() == EFloat::kLeft) {
line_.line_left_bfc_offset = std::max(
line_.line_left_bfc_offset,
positioned_float.bfc_offset.line_offset + inline_margin_size -
margins.LineLeft(TextDirection::kLtr));
} else {
line_.line_right_bfc_offset =
std::min(line_.line_right_bfc_offset,
positioned_float.bfc_offset.line_offset -
margins.LineLeft(TextDirection::kLtr));
}
DCHECK_GE(line_.line_left_bfc_offset, LayoutUnit());
DCHECK_GE(line_.line_right_bfc_offset, LayoutUnit());
DCHECK_GE(line_.AvailableWidth(), LayoutUnit());
} }
MoveToNextOf(item); MoveToNextOf(item);
...@@ -697,23 +675,6 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info, ...@@ -697,23 +675,6 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info,
// Reaching here means that the rewind point was not found. // Reaching here means that the rewind point was not found.
// When the first break opportunity overflows and if the current line has
// floats or intruding floats, we need to find the next layout opportunity
// which will fit the first break opportunity.
// Doing so will move the line down to where there are narrower floats (and
// thus wider available width,) or no floats.
if (HasFloatsAffectingCurrentLine()) {
FindNextLayoutOpportunityWithMinimumInlineSize(line_.position);
// Moving the line down widened the available width. Need to rewind items
// that depend on old available width, but it's not trivial to rewind all
// the states. For the simplicity, rewind to the beginning of the line.
Rewind(line_info, 0);
SetCurrentStyle(line_info->LineStyle());
line_.position = line_info->TextIndent();
BreakLine(line_info);
return;
}
// Let this line overflow. // Let this line overflow.
// If there was a break opporunity, the overflow should stop there. // If there was a break opporunity, the overflow should stop there.
if (break_before) if (break_before)
...@@ -726,7 +687,7 @@ void NGLineBreaker::Rewind(NGLineInfo* line_info, unsigned new_end) { ...@@ -726,7 +687,7 @@ void NGLineBreaker::Rewind(NGLineInfo* line_info, unsigned new_end) {
NGInlineItemResults* item_results = &line_info->Results(); NGInlineItemResults* item_results = &line_info->Results();
DCHECK_LT(new_end, item_results->size()); DCHECK_LT(new_end, item_results->size());
// TODO(ikilpatrick): Add rewinding of exclusions. // TODO(ikilpatrick): Add DCHECK that we never rewind past any floats.
if (new_end) { if (new_end) {
// Use |results[new_end - 1].end_offset| because it may have been truncated // Use |results[new_end - 1].end_offset| because it may have been truncated
...@@ -741,8 +702,6 @@ void NGLineBreaker::Rewind(NGLineInfo* line_info, unsigned new_end) { ...@@ -741,8 +702,6 @@ void NGLineBreaker::Rewind(NGLineInfo* line_info, unsigned new_end) {
// TODO(kojii): Should we keep results for the next line? We don't need to // TODO(kojii): Should we keep results for the next line? We don't need to
// re-layout atomic inlines. // re-layout atomic inlines.
// TODO(kojii): Removing processed floats is likely a problematic. Keep
// floats in this line, or keep it for the next line.
item_results->Shrink(new_end); item_results->Shrink(new_end);
line_info->SetIsLastLine(false); line_info->SetIsLastLine(false);
......
...@@ -34,19 +34,19 @@ class CORE_EXPORT NGLineBreaker { ...@@ -34,19 +34,19 @@ class CORE_EXPORT NGLineBreaker {
const NGConstraintSpace&, const NGConstraintSpace&,
Vector<NGPositionedFloat>*, Vector<NGPositionedFloat>*,
Vector<scoped_refptr<NGUnpositionedFloat>>*, Vector<scoped_refptr<NGUnpositionedFloat>>*,
NGExclusionSpace*,
unsigned handled_float_index,
const NGInlineBreakToken* = nullptr); const NGInlineBreakToken* = nullptr);
~NGLineBreaker() {} ~NGLineBreaker() {}
// Compute the next line break point and produces NGInlineItemResults for // Compute the next line break point and produces NGInlineItemResults for
// the line. // the line.
bool NextLine(const NGExclusionSpace&, NGLineInfo*); bool NextLine(const NGLayoutOpportunity&, NGLineInfo*);
// Create an NGInlineBreakToken for the last line returned by NextLine(). // Create an NGInlineBreakToken for the last line returned by NextLine().
scoped_refptr<NGInlineBreakToken> CreateBreakToken( scoped_refptr<NGInlineBreakToken> CreateBreakToken(
std::unique_ptr<const NGInlineLayoutStateStack>) const; std::unique_ptr<const NGInlineLayoutStateStack>) const;
NGExclusionSpace* ExclusionSpace() { return line_.exclusion_space.get(); }
private: private:
// This struct holds information for the current line. // This struct holds information for the current line.
struct LineData { struct LineData {
...@@ -59,7 +59,8 @@ class CORE_EXPORT NGLineBreaker { ...@@ -59,7 +59,8 @@ class CORE_EXPORT NGLineBreaker {
// The current opportunity. // The current opportunity.
NGLayoutOpportunity 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". // We don't create "certain zero-height line boxes".
// https://drafts.csswg.org/css2/visuren.html#phantom-line-box // https://drafts.csswg.org/css2/visuren.html#phantom-line-box
...@@ -72,7 +73,10 @@ class CORE_EXPORT NGLineBreaker { ...@@ -72,7 +73,10 @@ class CORE_EXPORT NGLineBreaker {
// the next line. // the next line.
bool is_after_forced_break = false; 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() const { return position <= AvailableWidth(); }
bool CanFit(LayoutUnit extra) const { bool CanFit(LayoutUnit extra) const {
return position + extra <= AvailableWidth(); return position + extra <= AvailableWidth();
...@@ -81,11 +85,7 @@ class CORE_EXPORT NGLineBreaker { ...@@ -81,11 +85,7 @@ class CORE_EXPORT NGLineBreaker {
void BreakLine(NGLineInfo*); void BreakLine(NGLineInfo*);
void PrepareNextLine(const NGExclusionSpace&, NGLineInfo*); void PrepareNextLine(const NGLayoutOpportunity&, NGLineInfo*);
bool HasFloatsAffectingCurrentLine() const;
void FindNextLayoutOpportunity();
void FindNextLayoutOpportunityWithMinimumInlineSize(LayoutUnit);
void ComputeLineLocation(NGLineInfo*) const; void ComputeLineLocation(NGLineInfo*) const;
...@@ -142,9 +142,12 @@ class CORE_EXPORT NGLineBreaker { ...@@ -142,9 +142,12 @@ class CORE_EXPORT NGLineBreaker {
const NGConstraintSpace& constraint_space_; const NGConstraintSpace& constraint_space_;
Vector<NGPositionedFloat>* positioned_floats_; Vector<NGPositionedFloat>* positioned_floats_;
Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats_; Vector<scoped_refptr<NGUnpositionedFloat>>* unpositioned_floats_;
NGExclusionSpace* exclusion_space_;
unsigned item_index_ = 0; unsigned item_index_ = 0;
unsigned offset_ = 0; unsigned offset_ = 0;
bool previous_line_had_forced_break_ = false; bool previous_line_had_forced_break_ = false;
LayoutUnit bfc_line_offset_;
LayoutUnit bfc_block_offset_; LayoutUnit bfc_block_offset_;
LazyLineBreakIterator break_iterator_; LazyLineBreakIterator break_iterator_;
HarfBuzzShaper shaper_; HarfBuzzShaper shaper_;
...@@ -152,7 +155,7 @@ class CORE_EXPORT NGLineBreaker { ...@@ -152,7 +155,7 @@ class CORE_EXPORT NGLineBreaker {
const Hyphenation* hyphenation_ = nullptr; const Hyphenation* hyphenation_ = nullptr;
// Keep track of handled float items. See HandleFloat(). // 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. // The current base direction for the bidi algorithm.
// This is copied from NGInlineNode, then updated after each forced line break // This is copied from NGInlineNode, then updated after each forced line break
......
...@@ -49,8 +49,13 @@ class NGLineBreakerTest : public NGBaseLayoutAlgorithmTest { ...@@ -49,8 +49,13 @@ class NGLineBreakerTest : public NGBaseLayoutAlgorithmTest {
NGLineInfo line_info; NGLineInfo line_info;
while (!break_token || !break_token->IsFinished()) { while (!break_token || !break_token->IsFinished()) {
NGLineBreaker line_breaker(node, *space, &positioned_floats, NGLineBreaker line_breaker(node, *space, &positioned_floats,
&unpositioned_floats, break_token.get()); &unpositioned_floats, &exclusion_space, 0u,
if (!line_breaker.NextLine(exclusion_space, &line_info)) break_token.get());
if (!line_breaker.NextLine(
NGLayoutOpportunity(
NGBfcOffset(),
NGLogicalSize({available_width, NGSizeIndefinite})),
&line_info))
break; break;
break_token = line_breaker.CreateBreakToken(nullptr); break_token = line_breaker.CreateBreakToken(nullptr);
......
...@@ -51,6 +51,22 @@ NGLayoutOpportunity NGExclusionSpace::FindLayoutOpportunity( ...@@ -51,6 +51,22 @@ NGLayoutOpportunity NGExclusionSpace::FindLayoutOpportunity(
return NGLayoutOpportunity(); 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 { LayoutUnit NGExclusionSpace::ClearanceOffset(EClear clear_type) const {
switch (clear_type) { switch (clear_type) {
case EClear::kNone: case EClear::kNone:
......
...@@ -36,6 +36,10 @@ class CORE_EXPORT NGExclusionSpace { ...@@ -36,6 +36,10 @@ class CORE_EXPORT NGExclusionSpace {
const NGLogicalSize& available_size, const NGLogicalSize& available_size,
const NGLogicalSize& minimum_size) const; 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}. // Returns the clearance offset based on the provided {@code clear_type}.
LayoutUnit ClearanceOffset(EClear clear_type) const; 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