Commit 3fc184d5 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Allow negative margins to stay on the line after overflow

This patch allows atomic inlines to stay on the line even
when it appears after the line overflowed, if its negative
margins can bring the position back to inside of the line.

This is not a well-defined behavior in the CSS spec, but
legacy/WebKit/Gecko do this at some levels, though not really
interoperable.

Bug: 1001000
Change-Id: If8cfc5d51de14c8de6a9caa78fa10abaf6f1b5b7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1859424
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#705848}
parent aa74a703
......@@ -96,6 +96,12 @@ inline void ComputeCanBreakAfter(NGInlineItemResult* item_result,
auto_wrap && break_iterator.IsBreakable(item_result->end_offset);
}
inline void RemoveLastItem(NGLineInfo* line_info) {
NGInlineItemResults* item_results = line_info->MutableResults();
DCHECK_GT(item_results->size(), 0u);
item_results->Shrink(item_results->size() - 1);
}
// To correctly determine if a float is allowed to be on the same line as its
// content, we need to determine if it has any ancestors with inline-end
// padding, border, or margin.
......@@ -227,6 +233,8 @@ NGLineBreaker::~NGLineBreaker() = default;
inline NGInlineItemResult* NGLineBreaker::AddItem(const NGInlineItem& item,
unsigned end_offset,
NGLineInfo* line_info) {
DCHECK_GE(offset_, item.StartOffset());
DCHECK_GE(end_offset, offset_);
DCHECK_LE(end_offset, item.EndOffset());
NGInlineItemResults* item_results = line_info->MutableResults();
return &item_results->emplace_back(
......@@ -1338,11 +1346,22 @@ void NGLineBreaker::HandleAtomicInline(
DCHECK(item.Style());
const ComputedStyle& style = *item.Style();
if (HandleOverflowIfNeeded(line_info))
// Compute margins before computing overflow, because even when the current
// position is beyond the end, negative margins can bring this item back to on
// the current line.
NGInlineItemResult* item_result = AddItem(item, line_info);
item_result->margins =
ComputeLineMarginsForVisualContainer(constraint_space_, style);
LayoutUnit inline_margins = item_result->margins.InlineSum();
LayoutUnit remaining_width = RemainingAvailableWidth();
bool is_overflow_before =
state_ == LineBreakState::kContinue && remaining_width < 0;
if (UNLIKELY(is_overflow_before && inline_margins > remaining_width)) {
RemoveLastItem(line_info);
HandleOverflow(line_info);
return;
}
NGInlineItemResult* item_result = AddItem(item, line_info);
item_result->should_create_line_box = true;
// When we're just computing min/max content sizes, we can skip the full
// layout and just compute those sizes. On the other hand, for regular
// layout we need to do the full layout and get the layout result.
......@@ -1358,20 +1377,16 @@ void NGLineBreaker::HandleAtomicInline(
NGFragment(constraint_space_.GetWritingMode(),
item_result->layout_result->PhysicalFragment())
.InlineSize();
item_result->margins =
ComputeLineMarginsForVisualContainer(constraint_space_, style);
item_result->inline_size += item_result->margins.InlineSum();
item_result->inline_size += inline_margins;
} else if (mode_ == NGLineBreakerMode::kMaxContent && max_size_cache_) {
unsigned item_index = &item - Items().begin();
item_result->inline_size = (*max_size_cache_)[item_index];
} else {
DCHECK(mode_ == NGLineBreakerMode::kMinContent || !max_size_cache_);
NGBlockNode child(ToLayoutBox(item.GetLayoutObject()));
MinMaxSizeInput input(percentage_resolution_block_size_for_min_max);
MinMaxSize sizes =
ComputeMinAndMaxContentContribution(node_.Style(), child, input);
LayoutUnit inline_margins =
ComputeLineMarginsForVisualContainer(constraint_space_, style)
.InlineSum();
if (mode_ == NGLineBreakerMode::kMinContent) {
item_result->inline_size = sizes.min_size + inline_margins;
if (max_size_cache_) {
......@@ -1385,13 +1400,14 @@ void NGLineBreaker::HandleAtomicInline(
}
}
trailing_whitespace_ = WhitespaceState::kNone;
position_ += item_result->inline_size;
item_result->should_create_line_box = true;
ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_);
if (!item_result->can_break_after && auto_wrap_ &&
IsAtomicInlineBeforeNoBreakSpace(*item_result))
item_result->can_break_after = true;
position_ += item_result->inline_size;
trailing_whitespace_ = WhitespaceState::kNone;
MoveToNextOf(item);
}
......
......@@ -18,6 +18,8 @@
inline-block {
display: inline-block;
}
.w1 { width: 1ch; }
.w2 { width: 2ch; }
.w4 { width: 4ch; }
.w5 { width: 5ch; }
span {
......@@ -37,6 +39,10 @@ img {
<div class="w4" data-expected-height=10><img class="w4"><span style="margin-left: -4ch">123</span></div>
<div class="w4" data-expected-height=10><img class="w5"><span style="margin-left: -5ch">123</span></div>
<div class="w4" data-expected-height=20><img class="w5"><span style="margin-left: -3ch">123</span></div>
<div class="w4" data-expected-height=10><img class="w4"><img class="w1" style="margin-left: -1ch"></div>
<div class="w4" data-expected-height=20><img class="w4"><img class="w2" style="margin-left: -1ch"></div>
<div class="w4" data-expected-height=10><img class="w5"><img class="w1" style="margin-left: -2ch"></div>
<div class="w4" data-expected-height=20><img class="w5"><img class="w2" style="margin-left: -2ch"></div>
</section>
<script>checkLayout('[data-expected-height]');</script>
</body>
......@@ -10,5 +10,11 @@ FAIL [data-expected-height] 6 assert_equals:
<div class="w4" data-expected-height="10"><img class="w5"><span style="margin-left: -5ch">123</span></div>
height expected 10 but got 20
PASS [data-expected-height] 7
PASS [data-expected-height] 8
PASS [data-expected-height] 9
FAIL [data-expected-height] 10 assert_equals:
<div class="w4" data-expected-height="10"><img class="w5"><img class="w1" style="margin-left: -2ch"></div>
height expected 10 but got 20
PASS [data-expected-height] 11
Harness: the test ran to completion.
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