Commit 8ee75745 authored by Ian Kilpatrick's avatar Ian Kilpatrick Committed by Commit Bot

[layout] Track inflow-bounds within box fragment builder.

This patch tracks the bounds of any inflow children (no floats, or
out-of-flow positioned objects).

This is only done for scrollable containers.
This is used to correctly determine the layout-overflow of a fragment.

Bug: 1066616
Change-Id: I334176cdf61f6eda92573e169719dbf7a2f62995
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2432225Reviewed-by: default avatarMorten Stenshorne <mstensho@chromium.org>
Commit-Queue: Ian Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812667}
parent f866eb74
...@@ -33,6 +33,10 @@ void LogicalRect::Unite(const LogicalRect& other) { ...@@ -33,6 +33,10 @@ void LogicalRect::Unite(const LogicalRect& other) {
return; return;
} }
UniteEvenIfEmpty(other);
}
void LogicalRect::UniteEvenIfEmpty(const LogicalRect& other) {
LogicalOffset new_end_offset(Max(EndOffset(), other.EndOffset())); LogicalOffset new_end_offset(Max(EndOffset(), other.EndOffset()));
LogicalOffset new_start_offset(Min(offset, other.offset)); LogicalOffset new_start_offset(Min(offset, other.offset));
size = new_end_offset - new_start_offset; size = new_end_offset - new_start_offset;
......
...@@ -65,6 +65,7 @@ struct CORE_EXPORT LogicalRect { ...@@ -65,6 +65,7 @@ struct CORE_EXPORT LogicalRect {
} }
void Unite(const LogicalRect&); void Unite(const LogicalRect&);
void UniteEvenIfEmpty(const LogicalRect&);
String ToString() const; String ToString() const;
}; };
......
...@@ -47,6 +47,13 @@ struct CORE_EXPORT NGBoxStrut { ...@@ -47,6 +47,13 @@ struct CORE_EXPORT NGBoxStrut {
bool IsEmpty() const { return *this == NGBoxStrut(); } bool IsEmpty() const { return *this == NGBoxStrut(); }
void ClampNegativeToZero() {
inline_start = inline_start.ClampNegativeToZero();
inline_end = inline_end.ClampNegativeToZero();
block_start = block_start.ClampNegativeToZero();
block_end = block_end.ClampNegativeToZero();
}
inline NGPhysicalBoxStrut ConvertToPhysical(WritingMode, TextDirection) const; inline NGPhysicalBoxStrut ConvertToPhysical(WritingMode, TextDirection) const;
// The following two operators exist primarily to have an easy way to access // The following two operators exist primarily to have an easy way to access
......
...@@ -194,18 +194,24 @@ void NGBoxFragmentBuilder::AddResult(const NGLayoutResult& child_layout_result, ...@@ -194,18 +194,24 @@ void NGBoxFragmentBuilder::AddResult(const NGLayoutResult& child_layout_result,
// maybe OOF objects. Investigate how to handle them. // maybe OOF objects. Investigate how to handle them.
} }
} }
AddChild(fragment, offset);
NGMarginStrut end_margin_strut = child_layout_result.EndMarginStrut();
// No margins should pierce outside formatting-context roots.
DCHECK(!fragment.IsFormattingContextRoot() || end_margin_strut.IsEmpty());
AddChild(fragment, offset, /* inline_container */ nullptr, &end_margin_strut);
if (fragment.IsBox()) if (fragment.IsBox())
PropagateBreak(child_layout_result); PropagateBreak(child_layout_result);
} }
void NGBoxFragmentBuilder::AddChild(const NGPhysicalContainerFragment& child, void NGBoxFragmentBuilder::AddChild(const NGPhysicalContainerFragment& child,
const LogicalOffset& child_offset, const LogicalOffset& child_offset,
const LayoutInline* inline_container) { const LayoutInline* inline_container,
const NGMarginStrut* margin_strut) {
LogicalOffset adjusted_offset = child_offset; LogicalOffset adjusted_offset = child_offset;
if (child.IsCSSBox() && if (box_type_ != NGPhysicalBoxFragment::NGBoxType::kInlineBox) {
box_type_ != NGPhysicalBoxFragment::NGBoxType::kInlineBox) { if (child.IsCSSBox()) {
// Apply the relative position offset. // Apply the relative position offset.
const auto& box_child = To<NGPhysicalBoxFragment>(child); const auto& box_child = To<NGPhysicalBoxFragment>(child);
if (box_child.Style().GetPosition() == EPosition::kRelative) { if (box_child.Style().GetPosition() == EPosition::kRelative) {
...@@ -214,8 +220,8 @@ void NGBoxFragmentBuilder::AddChild(const NGPhysicalContainerFragment& child, ...@@ -214,8 +220,8 @@ void NGBoxFragmentBuilder::AddChild(const NGPhysicalContainerFragment& child,
} }
// The |may_have_descendant_above_block_start_| flag is used to determine // The |may_have_descendant_above_block_start_| flag is used to determine
// if a fragment can be re-used when preceding floats are present. This is // if a fragment can be re-used when preceding floats are present. This
// relatively rare, and is true if: // is relatively rare, and is true if:
// - An inflow child is positioned above our block-start edge. // - An inflow child is positioned above our block-start edge.
// - Any inflow descendants (within the same formatting-context) which // - Any inflow descendants (within the same formatting-context) which
// *may* have a child positioned above our block-start edge. // *may* have a child positioned above our block-start edge.
...@@ -226,6 +232,51 @@ void NGBoxFragmentBuilder::AddChild(const NGPhysicalContainerFragment& child, ...@@ -226,6 +232,51 @@ void NGBoxFragmentBuilder::AddChild(const NGPhysicalContainerFragment& child,
may_have_descendant_above_block_start_ = true; may_have_descendant_above_block_start_ = true;
} }
// If we are a scroll container, we need to track the maximum bounds of any
// inflow children (including line-boxes) to calculate the layout-overflow.
//
// This is used for determining the "padding-box" of the scroll container
// which is *sometimes* considered as part of the scrollable area. Inflow
// children contribute to this area, out-of-flow positioned children don't.
//
// Out-of-flow positioned children still contribute to the layout-overflow,
// but just don't influence where this padding is.
if (Node().IsScrollContainer() && !child.IsOutOfFlowPositioned()) {
NGBoxStrut margins;
if (child.IsCSSBox()) {
margins =
ComputeMarginsFor(child.Style(), child_available_size_.inline_size,
GetWritingMode(), Direction());
}
// If we are in block-flow layout we use the end *margin-strut* as the
// block-end "margin" (instead of just the block-end margin).
if (margin_strut) {
NGMarginStrut end_margin_strut = *margin_strut;
end_margin_strut.Append(margins.block_end, /* is_quirky */ false);
margins.block_end = end_margin_strut.Sum();
}
NGFragment fragment(GetWritingMode(), child);
// Use the original offset (*without* relative-positioning applied), and
// clamp any negative margins to zero.
margins.ClampNegativeToZero();
LogicalRect bounds = {
LogicalOffset(child_offset.inline_offset - margins.inline_start,
child_offset.block_offset - margins.block_start),
LogicalSize(
margins.inline_start + fragment.InlineSize() + margins.inline_end,
margins.block_start + fragment.BlockSize() + margins.block_end)};
// Even an empty (0x0) fragment contributes to the inflow-bounds.
if (!inflow_bounds_)
inflow_bounds_ = bounds;
else
inflow_bounds_->UniteEvenIfEmpty(bounds);
}
}
PropagateChildData(child, adjusted_offset, inline_container); PropagateChildData(child, adjusted_offset, inline_container);
AddChildInternal(&child, adjusted_offset); AddChildInternal(&child, adjusted_offset);
} }
......
...@@ -207,7 +207,8 @@ class CORE_EXPORT NGBoxFragmentBuilder final ...@@ -207,7 +207,8 @@ class CORE_EXPORT NGBoxFragmentBuilder final
void AddChild(const NGPhysicalContainerFragment&, void AddChild(const NGPhysicalContainerFragment&,
const LogicalOffset&, const LogicalOffset&,
const LayoutInline* inline_container = nullptr); const LayoutInline* inline_container = nullptr,
const NGMarginStrut* margin_strut = nullptr);
// Manually add a break token to the builder. Note that we're assuming that // Manually add a break token to the builder. Note that we're assuming that
// this break token is for content in the same flow as this parent. // this break token is for content in the same flow as this parent.
...@@ -546,6 +547,7 @@ class CORE_EXPORT NGBoxFragmentBuilder final ...@@ -546,6 +547,7 @@ class CORE_EXPORT NGBoxFragmentBuilder final
LogicalSize child_available_size_; LogicalSize child_available_size_;
LayoutUnit overflow_block_size_ = kIndefiniteSize; LayoutUnit overflow_block_size_ = kIndefiniteSize;
LayoutUnit intrinsic_block_size_; LayoutUnit intrinsic_block_size_;
base::Optional<LogicalRect> inflow_bounds_;
NGFragmentItemsBuilder* items_builder_ = nullptr; NGFragmentItemsBuilder* items_builder_ = nullptr;
......
...@@ -181,6 +181,10 @@ class CORE_EXPORT NGLayoutInputNode { ...@@ -181,6 +181,10 @@ class CORE_EXPORT NGLayoutInputNode {
return box_->GetNGPaginationBreakability() == LayoutBox::kForbidBreaks; return box_->GetNGPaginationBreakability() == LayoutBox::kForbidBreaks;
} }
bool IsScrollContainer() const {
return IsBlock() && box_->IsScrollContainer();
}
bool CreatesNewFormattingContext() const { bool CreatesNewFormattingContext() const {
return IsBlock() && box_->CreatesNewFormattingContext(); return IsBlock() && box_->CreatesNewFormattingContext();
} }
......
...@@ -50,7 +50,15 @@ scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create( ...@@ -50,7 +50,15 @@ scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create(
builder->initial_fragment_geometry_->padding.ConvertToPhysical( builder->initial_fragment_geometry_->padding.ConvertToPhysical(
builder->GetWritingMode(), builder->Direction()); builder->GetWritingMode(), builder->Direction());
bool has_padding = !padding.IsZero(); bool has_padding = !padding.IsZero();
const PhysicalSize physical_size =
ToPhysicalSize(builder->Size(), builder->GetWritingMode());
WritingModeConverter converter(builder->GetWritingDirection(), physical_size);
base::Optional<PhysicalRect> inflow_bounds; base::Optional<PhysicalRect> inflow_bounds;
if (builder->inflow_bounds_)
inflow_bounds = converter.ToPhysical(*builder->inflow_bounds_);
const PhysicalRect layout_overflow; const PhysicalRect layout_overflow;
bool has_layout_overflow = false; bool has_layout_overflow = false;
bool has_rare_data = bool has_rare_data =
......
<!DOCTYPE html>
<meta name="assert" content="This ensures that floats block-end margin contributes to the scrollable overflow.">
<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#scrollable" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<body onload="checkLayout('#target')">
<div id="target" style="width: 100px; height: 100px; overflow: scroll;" data-expected-scroll-height="220">
<div style="float: left; width: 50px; height: 200px; margin: 10px; background: lime;"></div>
</div>
<div id=log></div>
</body>
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