summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc156
1 files changed, 95 insertions, 61 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 1964973a226..45a4869ac14 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
@@ -93,7 +94,7 @@ inline void ComputeCanBreakAfter(NGInlineItemResult* item_result,
bool auto_wrap,
const LazyLineBreakIterator& break_iterator) {
item_result->can_break_after =
- auto_wrap && break_iterator.IsBreakable(item_result->end_offset);
+ auto_wrap && break_iterator.IsBreakable(item_result->EndOffset());
}
inline void RemoveLastItem(NGLineInfo* line_info) {
@@ -235,8 +236,9 @@ inline NGInlineItemResult* NGLineBreaker::AddItem(const NGInlineItem& item,
DCHECK_LE(end_offset, item.EndOffset());
NGInlineItemResults* item_results = line_info->MutableResults();
return &item_results->emplace_back(
- &item, item_index_, offset_, end_offset, break_anywhere_if_overflow_,
- ShouldCreateLineBox(*item_results), HasUnpositionedFloats(*item_results));
+ &item, item_index_, NGTextOffset(offset_, end_offset),
+ break_anywhere_if_overflow_, ShouldCreateLineBox(*item_results),
+ HasUnpositionedFloats(*item_results));
}
inline NGInlineItemResult* NGLineBreaker::AddItem(const NGInlineItem& item,
@@ -350,7 +352,8 @@ void NGLineBreaker::NextLine(
// line boxes. These cases need to be reviewed.
bool should_create_line_box = ShouldCreateLineBox(item_results) ||
(has_list_marker_ && line_info->IsLastLine()) ||
- mode_ != NGLineBreakerMode::kContent;
+ mode_ != NGLineBreakerMode::kContent ||
+ node_.HasLineEvenIfEmpty();
if (!should_create_line_box)
line_info->SetIsEmptyLine();
@@ -443,7 +446,7 @@ void NGLineBreaker::BreakLine(
// determine the break opportunity.
NGInlineItemResult* item_result = AddItem(item, line_info);
item_result->can_break_after =
- break_iterator_.IsBreakable(item_result->end_offset);
+ break_iterator_.IsBreakable(item_result->EndOffset());
MoveToNextOf(item);
} else if (item.Type() == NGInlineItem::kListMarker) {
NGInlineItemResult* item_result = AddItem(item, line_info);
@@ -480,23 +483,23 @@ bool NGLineBreaker::ShouldForceCanBreakAfter(
DCHECK(auto_wrap_);
DCHECK_EQ(item_result.item->Type(), NGInlineItem::kText);
const String& text = Text();
- DCHECK_GE(text.length(), item_result.end_offset);
- if (text.length() <= item_result.end_offset ||
- text[item_result.end_offset] != kObjectReplacementCharacter)
+ DCHECK_GE(text.length(), item_result.EndOffset());
+ if (text.length() <= item_result.EndOffset() ||
+ text[item_result.EndOffset()] != kObjectReplacementCharacter)
return false;
// This kObjectReplacementCharacter can be any objects, such as a floating or
// an OOF object. Check if it's really an atomic inline.
const Vector<NGInlineItem>& items = Items();
for (const NGInlineItem* item = std::next(item_result.item);
item != items.end(); ++item) {
- DCHECK_EQ(item->StartOffset(), item_result.end_offset);
+ DCHECK_EQ(item->StartOffset(), item_result.EndOffset());
if (item->Type() == NGInlineItem::kAtomicInline) {
// Except when sticky images quirk was applied.
if (UNLIKELY(text[item->StartOffset()] == kNoBreakSpaceCharacter))
return false;
- return true;
+ return !item->IsRubyRun();
}
- if (item->EndOffset() > item_result.end_offset)
+ if (item->EndOffset() > item_result.EndOffset())
break;
}
return false;
@@ -556,6 +559,12 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
NGInlineItemResult* item_result = AddItem(item, line_info);
item_result->should_create_line_box = true;
+ // Try to commit |pending_end_overhang_| of a prior NGInlineItemResult.
+ // |pending_end_overhang_| doesn't work well with bidi reordering. It's
+ // difficult to compute overhang after bidi reordering because it affect
+ // line breaking.
+ if (maybe_have_end_overhang_)
+ position_ -= CommitPendingEndOverhang(line_info);
if (auto_wrap_) {
if (mode_ == NGLineBreakerMode::kMinContent &&
@@ -582,7 +591,7 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// If the break is at the middle of a text item, we know no trailable
// items follow, only trailable spaces if any. This is very common that
// shortcut to handling trailing spaces.
- if (item_result->end_offset < item.EndOffset())
+ if (item_result->EndOffset() < item.EndOffset())
return HandleTrailingSpaces(item, shape_result, line_info);
// The break point found at the end of this text item. Continue looking
@@ -608,8 +617,8 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// If this is all trailable spaces, this item is trailable, and next item
// maybe too. Don't go to |HandleOverflow()| yet.
- if (IsAllBreakableSpaces(Text(), item_result->start_offset,
- item_result->end_offset))
+ if (IsAllBreakableSpaces(Text(), item_result->StartOffset(),
+ item_result->EndOffset()))
return;
HandleOverflow(line_info);
@@ -618,8 +627,8 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// Add until the end of the item if !auto_wrap. In most cases, it's the whole
// item.
- DCHECK_EQ(item_result->end_offset, item.EndOffset());
- if (item_result->start_offset == item.StartOffset()) {
+ DCHECK_EQ(item_result->EndOffset(), item.EndOffset());
+ if (item_result->StartOffset() == item.StartOffset()) {
item_result->inline_size =
shape_result.SnappedWidth().ClampNegativeToZero();
item_result->shape_result = ShapeResultView::Create(&shape_result);
@@ -627,9 +636,9 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// <wbr> can wrap even if !auto_wrap. Spaces after that will be leading
// spaces and thus be collapsed.
DCHECK(trailing_whitespace_ == WhitespaceState::kLeading &&
- item_result->start_offset >= item.StartOffset());
+ item_result->StartOffset() >= item.StartOffset());
item_result->shape_result = ShapeResultView::Create(
- &shape_result, item_result->start_offset, item_result->end_offset);
+ &shape_result, item_result->StartOffset(), item_result->EndOffset());
item_result->inline_size =
item_result->shape_result->SnappedWidth().ClampNegativeToZero();
}
@@ -652,7 +661,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
(item.Type() == NGInlineItem::kControl &&
Text()[item.StartOffset()] == kTabulationCharacter));
DCHECK(&item_shape_result);
- item.AssertOffset(item_result->start_offset);
+ item.AssertOffset(item_result->StartOffset());
DCHECK_EQ(item_shape_result.StartIndex(), item.StartOffset());
DCHECK_EQ(item_shape_result.EndIndex(), item.EndOffset());
@@ -676,7 +685,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
// Use kStartShouldBeSafe if at the beginning of a line.
unsigned options = ShapingLineBreaker::kDefaultOptions;
- if (item_result->start_offset != line_info->StartOffset())
+ if (item_result->StartOffset() != line_info->StartOffset())
options |= ShapingLineBreaker::kDontReshapeStart;
// Reshaping between the last character and trailing spaces is needed only
@@ -702,7 +711,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
DCHECK_LE(try_count, 2u);
#endif
scoped_refptr<const ShapeResultView> shape_result = breaker.ShapeLine(
- item_result->start_offset, available_width.ClampNegativeToZero(),
+ item_result->StartOffset(), available_width.ClampNegativeToZero(),
options, &result);
// If this item overflows and 'break-word' is set, this line will be
@@ -710,14 +719,15 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
if (!shape_result) {
DCHECK(options & ShapingLineBreaker::kNoResultIfOverflow);
item_result->inline_size = available_width_with_hyphens + 1;
- item_result->end_offset = item.EndOffset();
+ item_result->text_offset.end = item.EndOffset();
+ item_result->text_offset.AssertNotEmpty();
return kOverflow;
}
DCHECK_EQ(shape_result->NumCharacters(),
- result.break_offset - item_result->start_offset);
+ result.break_offset - item_result->StartOffset());
// It is critical to move the offset forward, or NGLineBreaker may keep
// adding NGInlineItemResult until all the memory is consumed.
- CHECK_GT(result.break_offset, item_result->start_offset);
+ CHECK_GT(result.break_offset, item_result->StartOffset());
inline_size = shape_result->SnappedWidth().ClampNegativeToZero();
if (UNLIKELY(result.is_hyphenated)) {
@@ -741,7 +751,8 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
item_result->hyphen_string = String();
}
item_result->inline_size = inline_size;
- item_result->end_offset = result.break_offset;
+ item_result->text_offset.end = result.break_offset;
+ item_result->text_offset.AssertNotEmpty();
item_result->shape_result = std::move(shape_result);
break;
}
@@ -754,7 +765,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
// * If width > available_width: The first break opportunity does not fit.
// offset is the first break opportunity, either inside, at the end, or
// beyond the end.
- if (item_result->end_offset < item.EndOffset()) {
+ if (item_result->EndOffset() < item.EndOffset()) {
item_result->can_break_after = true;
if (UNLIKELY(break_iterator_.BreakType() ==
@@ -764,9 +775,9 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
trailing_whitespace_ = WhitespaceState::kNone;
}
} else {
- DCHECK_EQ(item_result->end_offset, item.EndOffset());
+ DCHECK_EQ(item_result->EndOffset(), item.EndOffset());
item_result->can_break_after =
- break_iterator_.IsBreakable(item_result->end_offset);
+ break_iterator_.IsBreakable(item_result->EndOffset());
if (!item_result->can_break_after && item.Type() == NGInlineItem::kText &&
ShouldForceCanBreakAfter(*item_result))
item_result->can_break_after = true;
@@ -783,7 +794,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
}
// Breaks the text item at the previous break opportunity from
-// |item_result->end_offset|. Returns false if there were no previous break
+// |item_result->text_offset.end|. Returns false if there were no previous break
// opportunities.
bool NGLineBreaker::BreakTextAtPreviousBreakOpportunity(
NGInlineItemResult* item_result) {
@@ -794,13 +805,14 @@ bool NGLineBreaker::BreakTextAtPreviousBreakOpportunity(
DCHECK(item.Style() && item.Style()->AutoWrap());
unsigned break_opportunity = break_iterator_.PreviousBreakOpportunity(
- item_result->end_offset - 1, item_result->start_offset);
- if (break_opportunity <= item_result->start_offset)
+ item_result->EndOffset() - 1, item_result->StartOffset());
+ if (break_opportunity <= item_result->StartOffset())
return false;
- item_result->end_offset = break_opportunity;
- item_result->shape_result =
- ShapeResultView::Create(item.TextShapeResult(), item_result->start_offset,
- item_result->end_offset);
+ item_result->text_offset.end = break_opportunity;
+ item_result->text_offset.AssertNotEmpty();
+ item_result->shape_result = ShapeResultView::Create(
+ item.TextShapeResult(), item_result->StartOffset(),
+ item_result->EndOffset());
item_result->inline_size =
item_result->shape_result->SnappedWidth().ClampNegativeToZero();
item_result->can_break_after = true;
@@ -832,7 +844,7 @@ bool NGLineBreaker::HandleTextForFastMinContent(NGInlineItemResult* item_result,
// If this is the first part of the text, it may form a word with the previous
// item. Fallback to |HandleText()|.
- unsigned start_offset = item_result->start_offset;
+ unsigned start_offset = item_result->StartOffset();
DCHECK_LT(start_offset, item.EndOffset());
if (start_offset != line_info->StartOffset() &&
start_offset == item.StartOffset())
@@ -895,7 +907,8 @@ bool NGLineBreaker::HandleTextForFastMinContent(NGInlineItemResult* item_result,
return false;
// Create an NGInlineItemResult that has the max of widths of all words.
- item_result->end_offset = last_end_offset;
+ item_result->text_offset.end = last_end_offset;
+ item_result->text_offset.AssertNotEmpty();
item_result->inline_size = LayoutUnit::FromFloatCeil(min_width);
item_result->can_break_after = true;
@@ -945,7 +958,7 @@ scoped_refptr<ShapeResultView> NGLineBreaker::TruncateLineEndResult(
const NGInlineItem& item = *item_result.item;
// Check given offsets require to truncate |item_result.shape_result|.
- const unsigned start_offset = item_result.start_offset;
+ const unsigned start_offset = item_result.StartOffset();
const ShapeResultView* source_result = item_result.shape_result.get();
DCHECK(source_result);
DCHECK_GE(start_offset, source_result->StartIndex());
@@ -978,7 +991,7 @@ void NGLineBreaker::UpdateShapeResult(const NGLineInfo& line_info,
NGInlineItemResult* item_result) {
DCHECK(item_result);
item_result->shape_result =
- TruncateLineEndResult(line_info, *item_result, item_result->end_offset);
+ TruncateLineEndResult(line_info, *item_result, item_result->EndOffset());
DCHECK(item_result->shape_result);
item_result->inline_size = item_result->shape_result->SnappedWidth();
}
@@ -1038,8 +1051,8 @@ void NGLineBreaker::HandleTrailingSpaces(const NGInlineItem& item,
NGInlineItemResult* item_result = AddItem(item, end, line_info);
item_result->has_only_trailing_spaces = true;
item_result->shape_result = ShapeResultView::Create(&shape_result);
- if (item_result->start_offset == item.StartOffset() &&
- item_result->end_offset == item.EndOffset())
+ if (item_result->StartOffset() == item.StartOffset() &&
+ item_result->EndOffset() == item.EndOffset())
item_result->inline_size = item_result->shape_result->SnappedWidth();
else
UpdateShapeResult(*line_info, item_result);
@@ -1079,7 +1092,7 @@ void NGLineBreaker::RemoveTrailingCollapsibleSpace(NGLineInfo* line_info) {
if (end_index < item_results.size()) {
const NGInlineItemResult& end_item_result = item_results[end_index];
unsigned end_item_index = end_item_result.item_index;
- unsigned end_offset = end_item_result.start_offset;
+ unsigned end_offset = end_item_result.StartOffset();
ResetRewindLoopDetector();
Rewind(end_index, line_info);
item_index_ = end_item_index;
@@ -1100,8 +1113,8 @@ void NGLineBreaker::RemoveTrailingCollapsibleSpace(NGLineInfo* line_info) {
position_ -= item_result->inline_size;
if (scoped_refptr<const ShapeResultView>& collapsed_shape_result =
trailing_collapsible_space_->collapsed_shape_result) {
- DCHECK_GE(item_result->end_offset, item_result->start_offset + 2);
- --item_result->end_offset;
+ --item_result->text_offset.end;
+ item_result->text_offset.AssertNotEmpty();
item_result->shape_result = collapsed_shape_result;
item_result->inline_size = item_result->shape_result->SnappedWidth();
position_ += item_result->inline_size;
@@ -1152,9 +1165,9 @@ void NGLineBreaker::ComputeTrailingCollapsibleSpace(NGLineInfo* line_info) {
if (item.EndCollapseType() == NGInlineItem::kOpaqueToCollapsing)
continue;
if (item.Type() == NGInlineItem::kText) {
- DCHECK_GT(item_result.end_offset, 0u);
+ DCHECK_GT(item_result.EndOffset(), 0u);
DCHECK(item.Style());
- if (!IsBreakableSpace(text[item_result.end_offset - 1]))
+ if (!IsBreakableSpace(text[item_result.EndOffset() - 1]))
break;
if (!item.Style()->CollapseWhiteSpace()) {
trailing_whitespace_ = WhitespaceState::kPreserved;
@@ -1169,10 +1182,10 @@ void NGLineBreaker::ComputeTrailingCollapsibleSpace(NGLineInfo* line_info) {
trailing_collapsible_space_->item_result != &item_result) {
trailing_collapsible_space_.emplace();
trailing_collapsible_space_->item_result = &item_result;
- if (item_result.end_offset - 1 > item_result.start_offset) {
+ if (item_result.EndOffset() - 1 > item_result.StartOffset()) {
trailing_collapsible_space_->collapsed_shape_result =
TruncateLineEndResult(*line_info, item_result,
- item_result.end_offset - 1);
+ item_result.EndOffset() - 1);
}
}
trailing_whitespace_ = WhitespaceState::kCollapsible;
@@ -1377,7 +1390,8 @@ void NGLineBreaker::HandleAtomicInline(
} else {
DCHECK(mode_ == NGLineBreakerMode::kMinContent || !max_size_cache_);
NGBlockNode child(ToLayoutBox(item.GetLayoutObject()));
- MinMaxSizesInput input(percentage_resolution_block_size_for_min_max);
+ MinMaxSizesInput input(percentage_resolution_block_size_for_min_max,
+ MinMaxSizesType::kContent);
MinMaxSizesResult result =
ComputeMinAndMaxContentContribution(node_.Style(), child, input);
if (mode_ == NGLineBreakerMode::kMinContent) {
@@ -1405,6 +1419,25 @@ void NGLineBreaker::HandleAtomicInline(
auto_wrap_ && !(sticky_images_quirk_ && item.IsImage());
position_ += item_result->inline_size;
+
+ if (item.IsRubyRun()) {
+ // Overrides can_break_after.
+ ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_);
+
+ NGAnnotationOverhang overhang = GetOverhang(*item_result);
+ if (overhang.end > LayoutUnit()) {
+ item_result->pending_end_overhang = overhang.end;
+ maybe_have_end_overhang_ = true;
+ }
+
+ if (CanApplyStartOverhang(*line_info, overhang.start)) {
+ DCHECK_EQ(item_result->margins.inline_start, LayoutUnit());
+ item_result->margins.inline_start = -overhang.start;
+ item_result->inline_size -= overhang.start;
+ position_ -= overhang.start;
+ }
+ }
+
trailing_whitespace_ = WhitespaceState::kNone;
MoveToNextOf(item);
}
@@ -1640,8 +1673,8 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
// iterator cannot compute this because it considers break opportunities are
// before a run of spaces.
const String& text = Text();
- if (item_result->end_offset < text.length() &&
- IsBreakableSpace(text[item_result->end_offset])) {
+ if (item_result->EndOffset() < text.length() &&
+ IsBreakableSpace(text[item_result->EndOffset()])) {
item_result->can_break_after = true;
return;
}
@@ -1720,7 +1753,7 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
BreakText(item_result, item, *item.TextShapeResult(),
std::min(item_available_width, min_available_width),
item_available_width, line_info);
- DCHECK_LE(item_result->end_offset, item_result_before.end_offset);
+ DCHECK_LE(item_result->EndOffset(), item_result_before.EndOffset());
#if DCHECK_IS_ON()
item_result->CheckConsistency(true);
#endif
@@ -1728,8 +1761,8 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
// If BreakText() changed this item small enough to fit, break here.
if (item_result->can_break_after &&
item_result->inline_size <= item_available_width &&
- item_result->end_offset < item_result_before.end_offset) {
- DCHECK_LT(item_result->end_offset, item.EndOffset());
+ item_result->EndOffset() < item_result_before.EndOffset()) {
+ DCHECK_LT(item_result->EndOffset(), item.EndOffset());
// If this is the last item, adjust it to accommodate the change.
const unsigned new_end = i + 1;
@@ -1739,7 +1772,7 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
available_width + width_to_rewind + item_result->inline_size;
DCHECK_EQ(position_, line_info->ComputeWidth());
item_index_ = item_result->item_index;
- offset_ = item_result->end_offset;
+ offset_ = item_result->EndOffset();
items_data_.AssertOffset(item_index_, offset_);
HandleTrailingSpaces(item, line_info);
return;
@@ -1818,11 +1851,11 @@ void NGLineBreaker::RewindOverflow(unsigned new_end, NGLineInfo* line_info) {
const EWhiteSpace white_space = item.Style()->WhiteSpace();
if (ComputedStyle::AutoWrap(white_space) &&
white_space != EWhiteSpace::kBreakSpaces &&
- IsBreakableSpace(text[item_result.start_offset])) {
+ IsBreakableSpace(text[item_result.StartOffset()])) {
// If all characters are trailable spaces, check the next item.
if (item_result.shape_result &&
- IsAllBreakableSpaces(text, item_result.start_offset + 1,
- item_result.end_offset)) {
+ IsAllBreakableSpaces(text, item_result.StartOffset() + 1,
+ item_result.EndOffset())) {
continue;
}
// If this item starts with spaces followed by non-space characters,
@@ -1842,7 +1875,7 @@ void NGLineBreaker::RewindOverflow(unsigned new_end, NGLineInfo* line_info) {
// All control characters except newline are trailable if auto_wrap. We
// should not have rewound if there was a newline, so safe to assume all
// controls are trailable.
- DCHECK_NE(text[item_result.start_offset], kNewlineCharacter);
+ DCHECK_NE(text[item_result.StartOffset()], kNewlineCharacter);
DCHECK(item.Style());
EWhiteSpace white_space = item.Style()->WhiteSpace();
if (ComputedStyle::AutoWrap(white_space) &&
@@ -1953,8 +1986,9 @@ void NGLineBreaker::Rewind(unsigned new_end, NGLineInfo* line_info) {
// When rewinding all items, use |results[0].start_offset|.
const NGInlineItemResult& first_remove = item_results[new_end];
item_index_ = first_remove.item_index;
- offset_ = first_remove.start_offset;
+ offset_ = first_remove.StartOffset();
trailing_whitespace_ = WhitespaceState::kLeading;
+ maybe_have_end_overhang_ = false;
}
SetCurrentStyle(ComputeCurrentStyle(new_end, line_info));
@@ -2077,7 +2111,7 @@ void NGLineBreaker::MoveToNextOf(const NGInlineItem& item) {
}
void NGLineBreaker::MoveToNextOf(const NGInlineItemResult& item_result) {
- offset_ = item_result.end_offset;
+ offset_ = item_result.EndOffset();
item_index_ = item_result.item_index;
DCHECK(item_result.item);
if (offset_ == item_result.item->EndOffset())