Commit 0cdbf1b7 authored by Alison Maher's avatar Alison Maher Committed by Commit Bot

[LayoutNG] Fieldset legend fragmentation

Add fragmentation support for fieldset legends.

Unit tests added to NGFieldsetLayoutAlgorithmTest.

The following work remains in order for legend fragmentation to be
considered complete and will be addressed in follow-up changes:

1. Making sure the fieldset border starts at the correct offset during
fragmentation when a legend is added.
2. Web tests will need to be added to test #1, as well as other
potential fragmentation scenarios.
3. Get fragmentation working in the case of a large border and small
legend, and add a unit test for this.
4. Refactor to reduce the amount of code in the Layout() function.
5. Handle fieldset content with a negative margin border start
correctly.

Bug: 875235
Change-Id: Idf1947af4ca9b26892bc6fc76e72cc1bbec4afa6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2090762
Commit-Queue: Alison Maher <almaher@microsoft.com>
Reviewed-by: default avatarMorten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749368}
parent 70e7732a
...@@ -30,7 +30,8 @@ NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm( ...@@ -30,7 +30,8 @@ NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm(
} }
scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
// TODO(almaher): Finish fragmentation for legends. // TODO(almaher): Make sure the border start is handled correctly during
// fragmentation.
// Layout of a fieldset container consists of two parts: Create a child // Layout of a fieldset container consists of two parts: Create a child
// fragment for the rendered legend (if any), and create a child fragment for // fragment for the rendered legend (if any), and create a child fragment for
...@@ -65,89 +66,131 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { ...@@ -65,89 +66,131 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
container_builder_.SetIsInitialColumnBalancingPass(); container_builder_.SetIsInitialColumnBalancingPass();
} }
scoped_refptr<const NGBlockBreakToken> legend_break_token;
scoped_refptr<const NGBlockBreakToken> content_break_token; scoped_refptr<const NGBlockBreakToken> content_break_token;
bool has_seen_all_children = false; bool has_seen_all_children = false;
if (const auto* token = BreakToken()) { if (const auto* token = BreakToken()) {
const auto child_tokens = token->ChildBreakTokens(); const auto child_tokens = token->ChildBreakTokens();
if (wtf_size_t break_token_count = child_tokens.size()) { if (wtf_size_t break_token_count = child_tokens.size()) {
scoped_refptr<const NGBlockBreakToken> child_token = DCHECK_LE(break_token_count, 2u);
To<NGBlockBreakToken>(child_tokens[0]); for (wtf_size_t break_token_idx = 0; break_token_idx < break_token_count;
if (child_token) { break_token_idx++) {
DCHECK(!child_token->InputNode().IsRenderedLegend()); scoped_refptr<const NGBlockBreakToken> child_token =
content_break_token = child_token; To<NGBlockBreakToken>(child_tokens[break_token_idx]);
if (child_token && child_token->InputNode().IsRenderedLegend()) {
DCHECK_EQ(break_token_idx, 0u);
legend_break_token = child_token;
} else {
content_break_token = child_token;
}
} }
// There shouldn't be any additional break tokens.
DCHECK_EQ(child_tokens.size(), 1u);
} }
if (token->HasSeenAllChildren()) { if (token->HasSeenAllChildren()) {
has_seen_all_children = true;
container_builder_.SetHasSeenAllChildren(); container_builder_.SetHasSeenAllChildren();
has_seen_all_children = true;
} }
} }
// TODO(vmpstr): Skip child (including legend) layout for fieldset elements.
NGBlockNode legend = Node().GetRenderedLegend(); NGBlockNode legend = Node().GetRenderedLegend();
if (!content_break_token && legend && !has_seen_all_children) { bool legend_break = false;
bool block_start_padding_edge_adjusted = false;
bool legend_needs_layout =
legend && (legend_break_token || !IsResumingLayout(BreakToken()));
if (legend_needs_layout) {
// Lay out the legend. While the fieldset container normally ignores its // Lay out the legend. While the fieldset container normally ignores its
// padding, the legend is laid out within what would have been the content // padding, the legend is laid out within what would have been the content
// box had the fieldset been a regular block with no weirdness. // box had the fieldset been a regular block with no weirdness.
LogicalSize content_box_size = LogicalSize content_box_size =
ShrinkAvailableSize(border_box_size, adjusted_border_padding); ShrinkAvailableSize(border_box_size, adjusted_border_padding);
auto legend_space = LogicalSize percentage_size = CalculateChildPercentageSize(
CreateConstraintSpaceForLegend(legend, content_box_size); ConstraintSpace(), Node(), content_box_size);
auto result = legend.Layout(legend_space, BreakToken()); NGBoxStrut legend_margins = ComputeMarginsFor(
legend.Style(), percentage_size.inline_size,
// TODO(layout-dev): Handle abortions caused by block fragmentation. ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction());
DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
if (legend_break_token)
legend_margins.block_start = LayoutUnit();
LogicalOffset legend_offset;
scoped_refptr<const NGLayoutResult> result;
LayoutUnit block_offset = legend_margins.block_start;
do {
auto legend_space = CreateConstraintSpaceForLegend(
legend, content_box_size, percentage_size, block_offset);
result = legend.Layout(legend_space, legend_break_token.get());
// TODO(layout-dev): Handle abortions caused by block fragmentation.
DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
const auto& physical_fragment = result->PhysicalFragment();
legend_break = physical_fragment.BreakToken();
// We have already adjusted the legend block offset, no need to adjust
// again.
if (block_offset != legend_margins.block_start)
break;
LayoutUnit legend_margin_box_block_size =
NGFragment(writing_mode, physical_fragment).BlockSize() +
legend_margins.BlockSum();
LayoutUnit space_left =
borders.block_start - legend_margin_box_block_size;
if (space_left > LayoutUnit()) {
// 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
// that needs to be pushed. We'll center the margin box in this case, to
// make sure that both margins remain within the area occupied by the
// border also after adjustment.
block_offset += space_left / 2;
if (ConstraintSpace().HasBlockFragmentation())
continue;
} else {
// If the legend is larger than the width of the fieldset block-start
// border, the actual padding edge of the fieldset will be moved
// accordingly. This will be the block-start offset for the fieldset
// contents anonymous box.
block_start_padding_edge = legend_margin_box_block_size;
block_start_padding_edge_adjusted = true;
}
break;
} while (true);
const auto& physical_fragment = result->PhysicalFragment();
NGBoxStrut legend_margins =
ComputeMarginsFor(legend_space, legend.Style(), ConstraintSpace());
// If the margin box of the legend is at least as tall as the fieldset // If the margin box of the legend is at least as tall as the fieldset
// block-start border width, it will start at the block-start border edge of // block-start border width, it will start at the block-start border edge
// the fieldset. As a paint effect, the block-start border will be pushed so // of the fieldset. As a paint effect, the block-start border will be
// that the center of the border will be flush with the center of the // pushed so that the center of the border will be flush with the center
// border-box of the legend. // of the border-box of the legend.
// TODO(mstensho): inline alignment // TODO(mstensho): inline alignment
LogicalOffset legend_offset = LogicalOffset( legend_offset = LogicalOffset(
adjusted_border_padding.inline_start + legend_margins.inline_start, adjusted_border_padding.inline_start + legend_margins.inline_start,
legend_margins.block_start); block_offset);
LayoutUnit legend_margin_box_block_size =
NGFragment(writing_mode, physical_fragment).BlockSize() +
legend_margins.BlockSum();
LayoutUnit space_left = borders.block_start - legend_margin_box_block_size;
if (space_left > LayoutUnit()) {
// 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 that
// needs to be pushed. We'll center the margin box in this case, to make
// sure that both margins remain within the area occupied by the border
// also after adjustment.
legend_offset.block_offset += space_left / 2;
} else {
// If the legend is larger than the width of the fieldset block-start
// border, the actual padding edge of the fieldset will be moved
// accordingly. This will be the block-start offset for the fieldset
// contents anonymous box.
block_start_padding_edge = legend_margin_box_block_size;
}
container_builder_.AddChild(physical_fragment, legend_offset); container_builder_.AddResult(*result, legend_offset);
DCHECK(!physical_fragment.BreakToken());
} }
NGBoxStrut borders_with_legend = borders; NGBoxStrut borders_with_legend = borders;
borders_with_legend.block_start = block_start_padding_edge; borders_with_legend.block_start = block_start_padding_edge;
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 (IsResumingLayout(BreakToken())) if (content_break_token ||
(!block_start_padding_edge_adjusted && IsResumingLayout(BreakToken()))) {
borders_with_legend.block_start = LayoutUnit(); borders_with_legend.block_start = LayoutUnit();
}
LayoutUnit intrinsic_block_size = borders_with_legend.BlockSum(); LayoutUnit intrinsic_block_size = borders_with_legend.BlockSum();
bool has_space_left = true;
if (ConstraintSpace().HasKnownFragmentainerBlockSize()) {
LayoutUnit space_left =
FragmentainerSpaceAtBfcStart(ConstraintSpace()) - intrinsic_block_size;
has_space_left = space_left > 0;
}
// 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 (has_space_left && fieldset_content &&
(content_break_token || !has_seen_all_children)) {
auto child_space = CreateConstraintSpaceForFieldsetContent( auto child_space = CreateConstraintSpaceForFieldsetContent(
fieldset_content, adjusted_padding_box_size, fieldset_content, adjusted_padding_box_size,
borders_with_legend.block_start); borders_with_legend.block_start);
...@@ -164,19 +207,20 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { ...@@ -164,19 +207,20 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
NGFragment(writing_mode, physical_fragment).BlockSize(); NGFragment(writing_mode, physical_fragment).BlockSize();
container_builder_.SetHasSeenAllChildren(); container_builder_.SetHasSeenAllChildren();
} }
LayoutUnit consumed_block_size =
BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit();
if (!fieldset_content) { if (!fieldset_content) {
// There was no anonymous child to provide the padding, so we have to add it
// ourselves.
container_builder_.SetHasSeenAllChildren(); container_builder_.SetHasSeenAllChildren();
// There was no anonymous child to provide the padding, so we have to add it
// ourselves. Subtract out the consumed block size to avoid over-calculating
// the block size when the fieldset's height is auto.
intrinsic_block_size += padding.BlockSum(); intrinsic_block_size += padding.BlockSum();
} }
intrinsic_block_size = ClampIntrinsicBlockSize( intrinsic_block_size = ClampIntrinsicBlockSize(
ConstraintSpace(), Node(), adjusted_border_padding, intrinsic_block_size); ConstraintSpace(), Node(), adjusted_border_padding, intrinsic_block_size);
LayoutUnit consumed_block_size =
BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit();
// 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 =
ComputeBlockSizeForFragment(ConstraintSpace(), Style(), border_padding_, ComputeBlockSizeForFragment(ConstraintSpace(), Style(), border_padding_,
...@@ -189,10 +233,17 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { ...@@ -189,10 +233,17 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
// contents, with the conjecture being that legend is part of the contents. // contents, with the conjecture being that legend is part of the contents.
// Thus, only do this adjustment if we do not contain size. // Thus, only do this adjustment if we do not contain size.
if (!Node().ShouldApplySizeContainment()) { if (!Node().ShouldApplySizeContainment()) {
LayoutUnit minimum_border_box_block_size = LayoutUnit minimum_border_box_block_size;
borders_with_legend.BlockSum() + padding.BlockSum(); if (legend_needs_layout) {
minimum_border_box_block_size =
borders_with_legend.BlockSum() + padding.BlockSum();
}
// Similar to how we add the consumed block size to the intrinsic
// block size when calculating border_box_size.block_size, we also need to
// do so when the fieldset is adjusted to encompass the legend.
border_box_size.block_size = border_box_size.block_size =
std::max(border_box_size.block_size, minimum_border_box_block_size); std::max(border_box_size.block_size,
minimum_border_box_block_size + consumed_block_size);
} }
// TODO(almaher): end border and padding may overflow the parent // TODO(almaher): end border and padding may overflow the parent
...@@ -254,17 +305,23 @@ base::Optional<MinMaxSizes> NGFieldsetLayoutAlgorithm::ComputeMinMaxSizes( ...@@ -254,17 +305,23 @@ base::Optional<MinMaxSizes> NGFieldsetLayoutAlgorithm::ComputeMinMaxSizes(
const NGConstraintSpace const NGConstraintSpace
NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForLegend( NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForLegend(
NGBlockNode legend, NGBlockNode legend,
LogicalSize available_size) { LogicalSize available_size,
LogicalSize percentage_size,
LayoutUnit block_offset) {
NGConstraintSpaceBuilder builder( NGConstraintSpaceBuilder builder(
ConstraintSpace(), legend.Style().GetWritingMode(), /* is_new_fc */ true); ConstraintSpace(), legend.Style().GetWritingMode(), /* is_new_fc */ true);
SetOrthogonalFallbackInlineSizeIfNeeded(Style(), legend, &builder); SetOrthogonalFallbackInlineSizeIfNeeded(Style(), legend, &builder);
builder.SetAvailableSize(available_size); builder.SetAvailableSize(available_size);
LogicalSize percentage_size =
CalculateChildPercentageSize(ConstraintSpace(), Node(), available_size);
builder.SetPercentageResolutionSize(percentage_size); builder.SetPercentageResolutionSize(percentage_size);
builder.SetIsShrinkToFit(legend.Style().LogicalWidth().IsAuto()); builder.SetIsShrinkToFit(legend.Style().LogicalWidth().IsAuto());
builder.SetTextDirection(legend.Style().Direction()); builder.SetTextDirection(legend.Style().Direction());
if (ConstraintSpace().HasBlockFragmentation()) {
SetupFragmentation(ConstraintSpace(), legend, block_offset, &builder,
/* is_new_fc */ true);
builder.SetEarlyBreakAppeal(container_builder_.BreakAppeal());
}
return builder.ToConstraintSpace(); return builder.ToConstraintSpace();
} }
......
...@@ -30,7 +30,9 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm ...@@ -30,7 +30,9 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm
private: private:
const NGConstraintSpace CreateConstraintSpaceForLegend( const NGConstraintSpace CreateConstraintSpaceForLegend(
NGBlockNode legend, NGBlockNode legend,
LogicalSize available_size); LogicalSize available_size,
LogicalSize percentage_size,
LayoutUnit block_offset);
const NGConstraintSpace CreateConstraintSpaceForFieldsetContent( const NGConstraintSpace CreateConstraintSpaceForFieldsetContent(
NGBlockNode fieldset_content, NGBlockNode fieldset_content,
LogicalSize padding_box_size, LogicalSize padding_box_size,
......
...@@ -574,6 +574,45 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SimpleFragmentation) { ...@@ -574,6 +574,45 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SimpleFragmentation) {
ASSERT_FALSE(fragment->BreakToken()); ASSERT_FALSE(fragment->BreakToken());
} }
// Tests that a fieldset with no content or padding will fragment if it reaches
// the fragmentation line.
TEST_F(NGFieldsetLayoutAlgorithmTest, FragmentationNoPadding) {
SetBodyInnerHTML(R"HTML(
<style>
#fieldset { margin:0; border:10px solid; padding:0px; width:100px; }
</style>
<fieldset id="fieldset"></fieldset>
)HTML");
LayoutUnit kFragmentainerSpaceAvailable(10);
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
scoped_refptr<const NGPhysicalBoxFragment> fragment =
NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
String dump = DumpFragmentTree(fragment.get());
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:120x10
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:120x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that a fieldset with auto height will fragment when its content reaches // Tests that a fieldset with auto height will fragment when its content reaches
// the fragmentation line. // the fragmentation line.
TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentationAutoHeight) { TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentationAutoHeight) {
...@@ -588,7 +627,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentationAutoHeight) { ...@@ -588,7 +627,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentationAutoHeight) {
} }
</style> </style>
<fieldset id="fieldset"> <fieldset id="fieldset">
<div id="child"></child> <div id="child"></div>
</fieldset> </fieldset>
)HTML"); )HTML");
...@@ -651,7 +690,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentation) { ...@@ -651,7 +690,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentation) {
} }
</style> </style>
<fieldset id="fieldset"> <fieldset id="fieldset">
<div id="child"></child> <div id="child"></div>
</fieldset> </fieldset>
)HTML"); )HTML");
...@@ -700,5 +739,336 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentation) { ...@@ -700,5 +739,336 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentation) {
EXPECT_EQ(expectation, dump); EXPECT_EQ(expectation, dump);
} }
// Tests that a fieldset with auto height will fragment when its legend reaches
// the fragmentation line.
TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentationAutoHeight) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
#fieldset {
border:3px solid; margin:0; padding:10px; width: 150px;
}
#legend {
padding:0px; margin:0; width: 50px; height: 500px;
}
</style>
<fieldset id="fieldset">
<legend id="legend"></legend>
</fieldset>
)HTML");
LayoutUnit kFragmentainerSpaceAvailable(200);
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
scoped_refptr<const NGPhysicalBoxFragment> fragment =
NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
String dump = DumpFragmentTree(fragment.get());
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x200
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x200
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x123
offset:13,0 size:50x100
offset:3,100 size:170x20
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that a fieldset with a set height will fragment when its legend
// reaches the fragmentation line. The used height should also be extended to
// encompass the legend.
TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentation) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
#fieldset {
border:3px solid; margin:0; padding:10px; width: 150px; height: 100px;
}
#legend {
padding:0px; margin:0; width: 50px; height: 500px;
}
</style>
<fieldset id="fieldset">
<legend id="legend"></legend>
</fieldset>
)HTML");
LayoutUnit kFragmentainerSpaceAvailable(200);
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
scoped_refptr<const NGPhysicalBoxFragment> fragment =
NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
String dump = DumpFragmentTree(fragment.get());
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x200
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x200
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x123
offset:13,0 size:50x100
offset:3,100 size:170x23
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that a fieldset with auto height will fragment when its legend/content
// reaches the fragmentation line.
TEST_F(NGFieldsetLayoutAlgorithmTest, LegendAndContentFragmentationAutoHeight) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
#fieldset {
border:3px solid; margin:0; padding:10px; width: 150px;
}
#legend {
padding:0px; margin:0; width: 50px; height: 500px;
}
#child {
margin:0; width: 100px; height: 200px;
}
</style>
<fieldset id="fieldset">
<legend id="legend"></legend>
<div id="child"></div>
</fieldset>
)HTML");
LayoutUnit kFragmentainerSpaceAvailable(200);
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
scoped_refptr<const NGPhysicalBoxFragment> fragment =
NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
String dump = DumpFragmentTree(fragment.get());
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x200
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x200
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x100
offset:3,100 size:170x100
offset:10,10 size:100x90
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x123
offset:3,0 size:170x120
offset:10,0 size:100x110
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests that a fieldset with a set height will fragment when its legend/content
// reaches the fragmentation line.
TEST_F(NGFieldsetLayoutAlgorithmTest, LegendAndContentFragmentation) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
#fieldset {
border:3px solid; margin:0; padding:10px; width: 150px; height: 100px;
}
#legend {
padding:0px; margin:0; width: 50px; height: 500px;
}
#child {
margin:0; width: 100px; height: 200px;
}
</style>
<fieldset id="fieldset">
<legend id="legend"></legend>
<div id="child"></div>
</fieldset>
)HTML");
LayoutUnit kFragmentainerSpaceAvailable(200);
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
scoped_refptr<const NGPhysicalBoxFragment> fragment =
NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
String dump = DumpFragmentTree(fragment.get());
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x200
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x200
offset:13,0 size:50x200
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x123
offset:13,0 size:50x100
offset:3,100 size:170x23
offset:10,10 size:100x90
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:176x0
offset:3,0 size:170x97
offset:10,0 size:100x110
)DUMP";
EXPECT_EQ(expectation, dump);
}
// Tests fragmentation when a legend's child content overflows.
TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentationWithOverflow) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
fieldset, legend { margin:0; border:none; padding:0; }
</style>
<fieldset id="fieldset">
<legend style="height:30px;">
<div style="width:55px; height:150px;"></div>
</legend>
<div style="width:44px; height:150px;"></div>
</div>
)HTML");
LayoutUnit kFragmentainerSpaceAvailable(100);
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
scoped_refptr<const NGPhysicalBoxFragment> fragment =
NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space);
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
String dump = DumpFragmentTree(fragment.get());
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x100
offset:0,0 size:55x30
offset:0,0 size:55x100
offset:0,30 size:1000x70
offset:0,0 size:44x70
)DUMP";
EXPECT_EQ(expectation, dump);
fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(
node, space, fragment->BreakToken());
ASSERT_FALSE(fragment->BreakToken());
dump = DumpFragmentTree(fragment.get());
expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x80
offset:0,0 size:55x0
offset:0,0 size:55x50
offset:0,0 size:1000x80
offset:0,0 size:44x80
)DUMP";
EXPECT_EQ(expectation, dump);
}
} // anonymous namespace } // anonymous namespace
} // namespace blink } // namespace blink
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