Commit 6f64a55f authored by Alison Maher's avatar Alison Maher Committed by Commit Bot

[LayoutNG] Large legend small border fragmentation

When the legend is larger than the fieldset border-top, the fieldset's
border box gets pushed down. In the case of fragmentation, allow
this adjustment if the legend fits inside the first fragment.
Otherwise, don't adjust the fieldset border block-start offset.

This is similar to how fragmentation was handled in the case where the
legend is smaller than the border-top:
https://chromium-review.googlesource.com/c/chromium/src/+/2125230

Tests were not added at this time for the following reasons:
1. It currently isn't possible to test where the fieldset border
starts using unit tests.
2. Paint for legend fragmentation isn't currently working, so testing
this via web tests was also not possible.

Bug: 875235
Change-Id: I9594ab35bce23a4e9dd1a3aa2e7d913900ec14b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2131348Reviewed-by: default avatarMorten Stenshorne <mstensho@chromium.org>
Commit-Queue: Alison Maher <almaher@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#756351}
parent dda040e5
...@@ -34,7 +34,6 @@ NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm( ...@@ -34,7 +34,6 @@ NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm(
borders_ = container_builder_.Borders(); borders_ = container_builder_.Borders();
padding_ = container_builder_.Padding(); padding_ = container_builder_.Padding();
border_box_size_ = container_builder_.InitialBorderBoxSize(); border_box_size_ = container_builder_.InitialBorderBoxSize();
block_start_padding_edge_ = borders_.block_start;
// Leading border and padding should only apply to the first fragment. We // Leading border and padding should only apply to the first fragment. We
// don't adjust the value of border_padding_ itself so that it can be used // don't adjust the value of border_padding_ itself so that it can be used
...@@ -69,15 +68,21 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { ...@@ -69,15 +68,21 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
container_builder_.SetIsInitialColumnBalancingPass(); container_builder_.SetIsInitialColumnBalancingPass();
} }
// TODO(almaher): Instead of setting this to 0 when resuming layout, this
// should equal the amount of the border block-start that is remaining from
// the previous fragment(s).
if (!IsResumingLayout(BreakToken()))
intrinsic_block_size_ = borders_.block_start;
NGBreakStatus break_status = LayoutChildren(); NGBreakStatus break_status = LayoutChildren();
if (break_status == NGBreakStatus::kNeedsEarlierBreak) { if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
// We need to abort the layout. No fragment will be generated. // We need to abort the layout. No fragment will be generated.
return container_builder_.Abort(NGLayoutResult::kNeedsEarlierBreak); return container_builder_.Abort(NGLayoutResult::kNeedsEarlierBreak);
} }
intrinsic_block_size_ = intrinsic_block_size_ = ClampIntrinsicBlockSize(
ClampIntrinsicBlockSize(ConstraintSpace(), Node(), ConstraintSpace(), Node(), adjusted_border_padding_,
adjusted_border_padding_, intrinsic_block_size_); intrinsic_block_size_ + borders_.block_end);
// Recompute the block-axis size now that we know our content size. // Recompute the block-axis size now that we know our content size.
border_box_size_.block_size = border_box_size_.block_size =
...@@ -113,7 +118,7 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { ...@@ -113,7 +118,7 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
container_builder_.SetBlockSize(block_size); container_builder_.SetBlockSize(block_size);
} }
NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders_with_legend_, NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders_,
&container_builder_) &container_builder_)
.Run(); .Run();
...@@ -154,62 +159,53 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutChildren() { ...@@ -154,62 +159,53 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutChildren() {
NGBreakStatus break_status = LayoutLegend(legend, legend_break_token); NGBreakStatus break_status = LayoutLegend(legend, legend_break_token);
if (break_status != NGBreakStatus::kContinue) if (break_status != NGBreakStatus::kContinue)
return break_status; return break_status;
}
borders_with_legend_ = borders_; // The legend may eat from the available content box block size. Calculate
borders_with_legend_.block_start = block_start_padding_edge_; // the minimum block size needed to encompass the legend.
if (!Node().ShouldApplySizeContainment()) {
// The legend may eat from the available content box block size. If the minimum_border_box_block_size_ =
// border_box_size_ is expanded to encompass the legend, then update the intrinsic_block_size_ + padding_.BlockSum() + borders_.block_end;
// border_box_size_ here, as well, to ensure the fieldset content gets the
// correct size.
if (!Node().ShouldApplySizeContainment() && legend_needs_layout) {
minimum_border_box_block_size_ =
borders_with_legend_.BlockSum() + padding_.BlockSum();
if (border_box_size_.block_size != kIndefiniteSize) {
border_box_size_.block_size =
std::max(border_box_size_.block_size, minimum_border_box_block_size_);
} }
} }
NGBoxStrut borders_with_legend = borders_;
borders_with_legend.block_start = intrinsic_block_size_;
LogicalSize adjusted_padding_box_size = LogicalSize adjusted_padding_box_size =
ShrinkAvailableSize(border_box_size_, borders_with_legend_); ShrinkAvailableSize(border_box_size_, borders_with_legend);
// If the legend has been laid out in previous fragments, if (adjusted_padding_box_size.block_size != kIndefiniteSize) {
// adjusted_padding_box_size will need to be adjusted further to account for // If intrinsic_block_size_ does not include the border block-start, exclude
// block size taken up by the legend. // it from adjusted_padding_box_size, as well.
if (legend && adjusted_padding_box_size.block_size != kIndefiniteSize) { if (intrinsic_block_size_ == LayoutUnit())
LayoutUnit content_consumed_block_size = adjusted_padding_box_size.block_size -= borders_.block_start;
content_break_token ? content_break_token->ConsumedBlockSize()
: LayoutUnit(); // If the legend has been laid out in previous fragments,
LayoutUnit legend_block_size = // adjusted_padding_box_size will need to be adjusted further to account for
consumed_block_size_ - content_consumed_block_size; // block size taken up by the legend.
adjusted_padding_box_size.block_size = if (legend) {
std::max(padding_.BlockSum(), LayoutUnit content_consumed_block_size =
adjusted_padding_box_size.block_size - legend_block_size); content_break_token ? content_break_token->ConsumedBlockSize()
} : LayoutUnit();
LayoutUnit legend_block_size =
if ((IsResumingLayout(content_break_token.get())) || consumed_block_size_ - content_consumed_block_size;
(!block_start_padding_edge_adjusted_ && IsResumingLayout(BreakToken()))) { adjusted_padding_box_size.block_size =
borders_with_legend_.block_start = LayoutUnit(); std::max(padding_.BlockSum(),
adjusted_padding_box_size.block_size - legend_block_size);
}
} }
intrinsic_block_size_ = borders_with_legend_.BlockSum();
// Proceed with normal fieldset children (excluding the rendered legend). They // Proceed with normal fieldset children (excluding the rendered legend). They
// all live inside an anonymous child box of the fieldset container. // all live inside an anonymous child box of the fieldset container.
auto fieldset_content = Node().GetFieldsetContent(); auto fieldset_content = Node().GetFieldsetContent();
if (fieldset_content && (content_break_token || !has_seen_all_children)) { if (fieldset_content && (content_break_token || !has_seen_all_children)) {
LayoutUnit fragmentainer_block_offset; if (ConstraintSpace().HasBlockFragmentation() && legend_broke_ &&
if (ConstraintSpace().HasBlockFragmentation()) { IsFragmentainerOutOfSpace(ConstraintSpace().FragmentainerOffsetAtBfc() +
fragmentainer_block_offset = intrinsic_block_size_))
ConstraintSpace().FragmentainerOffsetAtBfc() + intrinsic_block_size_; return NGBreakStatus::kContinue;
if (legend_broke_ &&
IsFragmentainerOutOfSpace(fragmentainer_block_offset)) NGBreakStatus break_status =
return NGBreakStatus::kContinue; LayoutFieldsetContent(fieldset_content, content_break_token,
} adjusted_padding_box_size, !!legend);
NGBreakStatus break_status = LayoutFieldsetContent(
fieldset_content, content_break_token, adjusted_padding_box_size,
fragmentainer_block_offset, !!legend);
if (break_status == NGBreakStatus::kNeedsEarlierBreak) if (break_status == NGBreakStatus::kNeedsEarlierBreak)
return break_status; return break_status;
} }
...@@ -284,11 +280,18 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend( ...@@ -284,11 +280,18 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend(
NGFragment(writing_mode_, physical_fragment).BlockSize() + NGFragment(writing_mode_, physical_fragment).BlockSize() +
legend_margins.BlockSum(); legend_margins.BlockSum();
LayoutUnit space_left = borders_.block_start - legend_margin_box_block_size; LayoutUnit space_left = borders_.block_start - legend_margin_box_block_size;
if (space_left > LayoutUnit()) {
// Don't adjust the block_offset if the legend broke.
if (legend_break_token || legend_broke_)
break;
// If the border is smaller, intrinsic_block_size_ should now be based on
// the size of the legend instead of the border.
if (space_left <= LayoutUnit())
intrinsic_block_size_ = legend_margin_box_block_size;
// Don't adjust the block-start offset of the legend or fragment border if
// the legend broke.
if (legend_break_token || legend_broke_)
break;
if (space_left > LayoutUnit()) {
// If the border is the larger one, though, it will stay put at the // If the border is the larger one, though, it will stay put at the
// border-box block-start edge of the fieldset. Then it's the legend // border-box block-start edge of the fieldset. Then it's the legend
// that needs to be pushed. We'll center the margin box in this case, to // that needs to be pushed. We'll center the margin box in this case, to
...@@ -306,8 +309,7 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend( ...@@ -306,8 +309,7 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend(
// border, the actual padding edge of the fieldset will be moved // border, the actual padding edge of the fieldset will be moved
// accordingly. This will be the block-start offset for the fieldset // accordingly. This will be the block-start offset for the fieldset
// contents anonymous box. // contents anonymous box.
block_start_padding_edge_ = legend_margin_box_block_size; borders_.block_start = legend_margin_box_block_size;
block_start_padding_edge_adjusted_ = true;
} }
break; break;
} while (true); } while (true);
...@@ -330,11 +332,9 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent( ...@@ -330,11 +332,9 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent(
NGBlockNode& fieldset_content, NGBlockNode& fieldset_content,
scoped_refptr<const NGBlockBreakToken> content_break_token, scoped_refptr<const NGBlockBreakToken> content_break_token,
LogicalSize adjusted_padding_box_size, LogicalSize adjusted_padding_box_size,
LayoutUnit fragmentainer_block_offset,
bool has_legend) { bool has_legend) {
auto child_space = CreateConstraintSpaceForFieldsetContent( auto child_space = CreateConstraintSpaceForFieldsetContent(
fieldset_content, adjusted_padding_box_size, fieldset_content, adjusted_padding_box_size, intrinsic_block_size_);
borders_with_legend_.block_start);
auto result = fieldset_content.Layout(child_space, content_break_token.get()); auto result = fieldset_content.Layout(child_space, content_break_token.get());
// TODO(layout-dev): Handle abortions caused by block fragmentation. // TODO(layout-dev): Handle abortions caused by block fragmentation.
...@@ -345,7 +345,7 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent( ...@@ -345,7 +345,7 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent(
// TODO(almaher): The legend should be treated as out-of-flow. // TODO(almaher): The legend should be treated as out-of-flow.
break_status = BreakBeforeChildIfNeeded( break_status = BreakBeforeChildIfNeeded(
ConstraintSpace(), fieldset_content, *result.get(), ConstraintSpace(), fieldset_content, *result.get(),
fragmentainer_block_offset, ConstraintSpace().FragmentainerOffsetAtBfc() + intrinsic_block_size_,
/*has_container_separation*/ has_legend, &container_builder_); /*has_container_separation*/ has_legend, &container_builder_);
EBreakBetween break_after = JoinFragmentainerBreakValues( EBreakBetween break_after = JoinFragmentainerBreakValues(
result->FinalBreakAfter(), fieldset_content.Style().BreakAfter()); result->FinalBreakAfter(), fieldset_content.Style().BreakAfter());
...@@ -353,7 +353,8 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent( ...@@ -353,7 +353,8 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent(
} }
if (break_status == NGBreakStatus::kContinue) { if (break_status == NGBreakStatus::kContinue) {
container_builder_.AddResult(*result, borders_with_legend_.StartOffset()); LogicalOffset offset(borders_.inline_start, intrinsic_block_size_);
container_builder_.AddResult(*result, offset);
intrinsic_block_size_ += intrinsic_block_size_ +=
NGFragment(writing_mode_, result->PhysicalFragment()).BlockSize(); NGFragment(writing_mode_, result->PhysicalFragment()).BlockSize();
container_builder_.SetHasSeenAllChildren(); container_builder_.SetHasSeenAllChildren();
......
...@@ -37,7 +37,6 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm ...@@ -37,7 +37,6 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm
NGBlockNode& fieldset_content, NGBlockNode& fieldset_content,
scoped_refptr<const NGBlockBreakToken> content_break_token, scoped_refptr<const NGBlockBreakToken> content_break_token,
LogicalSize adjusted_padding_box_size, LogicalSize adjusted_padding_box_size,
LayoutUnit fragmentainer_block_offset,
bool has_legend); bool has_legend);
const NGConstraintSpace CreateConstraintSpaceForLegend( const NGConstraintSpace CreateConstraintSpaceForLegend(
...@@ -62,10 +61,6 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm ...@@ -62,10 +61,6 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm
// and padding are only applied to the first fragment. // and padding are only applied to the first fragment.
NGBoxStrut adjusted_border_padding_; NGBoxStrut adjusted_border_padding_;
// The result of borders_ after positioning the fieldset's legend element.
NGBoxStrut borders_with_legend_;
LayoutUnit block_start_padding_edge_;
LayoutUnit intrinsic_block_size_; LayoutUnit intrinsic_block_size_;
const LayoutUnit consumed_block_size_; const LayoutUnit consumed_block_size_;
LogicalSize border_box_size_; LogicalSize border_box_size_;
...@@ -75,10 +70,6 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm ...@@ -75,10 +70,6 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm
// the legend. // the legend.
LayoutUnit minimum_border_box_block_size_; LayoutUnit minimum_border_box_block_size_;
// If true, this indicates the block_start_padding_edge_ had changed from its
// initial value during the current layout pass.
bool block_start_padding_edge_adjusted_ = false;
// If true, this indicates that the legend broke during the current layout // If true, this indicates that the legend broke during the current layout
// pass. // pass.
bool legend_broke_ = false; bool legend_broke_ = false;
......
...@@ -1811,10 +1811,12 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallLegendLargeBorderFragmentation) { ...@@ -1811,10 +1811,12 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallLegendLargeBorderFragmentation) {
node, space, fragment->BreakToken()); node, space, fragment->BreakToken());
ASSERT_TRUE(fragment->BreakToken()); ASSERT_TRUE(fragment->BreakToken());
// TODO(almaher): The fieldset content should start at offset 60,20.
dump = DumpFragmentTree(fragment.get()); dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:220x40 offset:unplaced size:220x40
offset:60,0 size:10x10 offset:60,0 size:10x10
offset:60,0 size:100x0
)DUMP"; )DUMP";
EXPECT_EQ(expectation, dump); EXPECT_EQ(expectation, dump);
...@@ -1830,9 +1832,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallLegendLargeBorderFragmentation) { ...@@ -1830,9 +1832,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallLegendLargeBorderFragmentation) {
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken()); node, space, fragment->BreakToken());
// TODO(almaher): There should be no break token here. In this case the bottom ASSERT_FALSE(fragment->BreakToken());
// border never reduces in size, causing fragmentation to continue infinitely.
ASSERT_TRUE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get()); dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
...@@ -1879,9 +1879,11 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderFragmentation) { ...@@ -1879,9 +1879,11 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderFragmentation) {
node, space, fragment->BreakToken()); node, space, fragment->BreakToken());
ASSERT_TRUE(fragment->BreakToken()); ASSERT_TRUE(fragment->BreakToken());
// TODO(almaher): The fieldset content should start at offset 60,20.
dump = DumpFragmentTree(fragment.get()); dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:220x40 offset:unplaced size:220x40
offset:60,0 size:100x0
)DUMP"; )DUMP";
EXPECT_EQ(expectation, dump); EXPECT_EQ(expectation, dump);
...@@ -1897,9 +1899,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderFragmentation) { ...@@ -1897,9 +1899,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderFragmentation) {
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken()); node, space, fragment->BreakToken());
// TODO(almaher): There should be no break token here. In this case the bottom ASSERT_FALSE(fragment->BreakToken());
// border never reduces in size, causing fragmentation to continue infinitely.
ASSERT_TRUE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get()); dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
...@@ -1946,9 +1946,11 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderWithBreak) { ...@@ -1946,9 +1946,11 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderWithBreak) {
node, space, fragment->BreakToken()); node, space, fragment->BreakToken());
ASSERT_TRUE(fragment->BreakToken()); ASSERT_TRUE(fragment->BreakToken());
// TODO(almaher): The fieldset content should start at offset 60,20.
dump = DumpFragmentTree(fragment.get()); dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:220x40 offset:unplaced size:220x40
offset:60,0 size:100x0
)DUMP"; )DUMP";
EXPECT_EQ(expectation, dump); EXPECT_EQ(expectation, dump);
...@@ -1964,9 +1966,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderWithBreak) { ...@@ -1964,9 +1966,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderWithBreak) {
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken()); node, space, fragment->BreakToken());
// TODO(almaher): There should be no break token here. In this case the bottom ASSERT_FALSE(fragment->BreakToken());
// border never reduces in size, causing fragmentation to continue infinitely.
ASSERT_TRUE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get()); dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
......
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