Commit 09a02c72 authored by Morten Stenshorne's avatar Morten Stenshorne Committed by Commit Bot

[LayoutNG] Fieldset min/max intrinsic size calculation.

Extra care is needed when the legend is an orthogonal writing mode root.
We need to do what we do in min/max calculation for such children in the
regular block layout algorithm.

Fieldset padding is also special, since it's included in the fieldset
content anonymous child, but we have to take care of it ourselves
anyway, in case there is no such child (empty fieldset, or fieldset with
just a legend).

Bug: 875235
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: If26d8a59ae1514085f40e5a9e1ecee007997e7ad
Reviewed-on: https://chromium-review.googlesource.com/1245363
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Reviewed-by: default avatarChristian Biesinger <cbiesinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#594514}
parent d596020a
......@@ -8,6 +8,11 @@
namespace blink {
void MinMaxSize::Encompass(const MinMaxSize& other) {
min_size = std::max(min_size, other.min_size);
max_size = std::max(max_size, other.max_size);
}
void MinMaxSize::Encompass(LayoutUnit value) {
min_size = std::max(min_size, value);
max_size = std::max(max_size, value);
......
......@@ -17,6 +17,9 @@ struct CORE_EXPORT MinMaxSize {
LayoutUnit min_size;
LayoutUnit max_size;
// Make sure that our min/max sizes are at least as large as |other|.
void Encompass(const MinMaxSize& other);
// Make sure that our min/max sizes are at least as large as |value|.
void Encompass(LayoutUnit value);
......@@ -36,6 +39,11 @@ struct CORE_EXPORT MinMaxSize {
}
void operator=(LayoutUnit value) { min_size = max_size = value; }
MinMaxSize& operator+=(MinMaxSize extra) {
min_size += extra.min_size;
max_size += extra.max_size;
return *this;
}
MinMaxSize& operator+=(const LayoutUnit);
MinMaxSize& operator-=(const LayoutUnit);
};
......
......@@ -14,6 +14,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
namespace blink {
......@@ -137,8 +138,56 @@ scoped_refptr<NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
base::Optional<MinMaxSize> NGFieldsetLayoutAlgorithm::ComputeMinMaxSize(
const MinMaxSizeInput& input) const {
// TODO(mstensho): Implement min/max
return MinMaxSize();
MinMaxSize sizes;
// Size-contained elements don't consider their contents for intrinsic sizing.
if (node_.ShouldApplySizeContainment())
return sizes;
if (NGBlockNode legend = Node().GetRenderedLegend()) {
// We'll need extrinsic sizing data when computing min/max for orthogonal
// flow roots, because calculating min/max in that case will involve doing
// layout. Note that we only need to do this for the legend, and not for the
// fieldset contents. The contents child is just an anonymous one, which
// inherits writing-mode from the fieldset, so it can never be a writing
// mode root.
NGConstraintSpace extrinsic_constraint_space;
const NGConstraintSpace* optional_constraint_space = nullptr;
if (!IsParallelWritingMode(Style().GetWritingMode(),
legend.Style().GetWritingMode())) {
NGBoxStrut border_padding = ComputeBorders(ConstraintSpace(), Node()) +
ComputePadding(ConstraintSpace(), Style());
// If there is a resolvable extrinsic block size, use that as input.
// Otherwise we'll fall back to the initial containing block size as a
// constraint.
LayoutUnit extrinsic_block_size = ComputeBlockSizeForFragment(
ConstraintSpace(), Style(), NGSizeIndefinite, border_padding);
if (extrinsic_block_size != NGSizeIndefinite) {
extrinsic_block_size -= border_padding.BlockSum();
extrinsic_block_size = extrinsic_block_size.ClampNegativeToZero();
}
extrinsic_constraint_space = CreateExtrinsicConstraintSpaceForChild(
ConstraintSpace(), extrinsic_block_size, legend);
optional_constraint_space = &extrinsic_constraint_space;
}
sizes = ComputeMinAndMaxContentContribution(
Style().GetWritingMode(), legend, input, optional_constraint_space);
sizes += ComputeMinMaxMargins(Style(), legend).InlineSum();
}
// The fieldset content includes the fieldset padding (and any scrollbars),
// while the legend is a regular child and doesn't. We may have a fieldset
// without any content or legend, so add the padding here, on the outside.
sizes += ComputePadding(ConstraintSpace(), node_.Style()).InlineSum();
if (NGBlockNode content = Node().GetFieldsetContent()) {
MinMaxSize content_minmax = ComputeMinAndMaxContentContribution(
Style().GetWritingMode(), content, input);
content_minmax += ComputeMinMaxMargins(Style(), content).InlineSum();
sizes.Encompass(content_minmax);
}
sizes += ComputeBorders(ConstraintSpace(), node_.Style()).InlineSum();
return sizes;
}
const NGConstraintSpace
......
......@@ -47,6 +47,24 @@ class NGFieldsetLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest {
return RunBlockLayoutAlgorithm(space, container);
}
MinMaxSize RunComputeMinAndMax(NGBlockNode node) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
NGLogicalSize(LayoutUnit(), LayoutUnit()));
NGFieldsetLayoutAlgorithm algorithm(node, space);
MinMaxSizeInput input;
auto min_max = algorithm.ComputeMinMaxSize(input);
EXPECT_TRUE(min_max.has_value());
return *min_max;
}
MinMaxSize RunComputeMinAndMax(const char* element_id) {
Element* element = GetDocument().getElementById(element_id);
NGBlockNode node(ToLayoutBox(element->GetLayoutObject()));
return RunComputeMinAndMax(node);
}
String DumpFragmentTree(const NGPhysicalBoxFragment* fragment) {
NGPhysicalFragment::DumpFlags flags =
NGPhysicalFragment::DumpHeaderText | NGPhysicalFragment::DumpSubtree |
......@@ -438,5 +456,71 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, LegendPercentHeightQuirks) {
EXPECT_EQ(expectation, dump);
}
TEST_F(NGFieldsetLayoutAlgorithmTest, MinMax) {
SetBodyInnerHTML(R"HTML(
<style>
fieldset { margin:123px; border:3px solid; padding:10px; width:100px; }
legend { margin:20px; border:11px solid; padding:7px; }
.float { float:left; width:50px; height:50px; }
</style>
<div id="container">
<fieldset id="fieldset1"></fieldset>
<fieldset id="fieldset2">
<legend></legend>
</fieldset>
<fieldset id="fieldset3">
<legend></legend>
<div class="float"></div>
<div class="float"></div>
</fieldset>
<fieldset id="fieldset4">
<legend>
<div class="float"></div>
<div class="float"></div>
</legend>
<div class="float"></div>
</fieldset>
<fieldset id="fieldset5">
<legend>
<div class="float"></div>
</legend>
<div class="float"></div>
<div class="float"></div>
<div class="float"></div>
</fieldset>
<fieldset id="fieldset6">
<div class="float"></div>
<div class="float"></div>
</fieldset>
</div>
)HTML");
MinMaxSize size;
size = RunComputeMinAndMax("fieldset1");
EXPECT_EQ(size.min_size, LayoutUnit(26));
EXPECT_EQ(size.max_size, LayoutUnit(26));
size = RunComputeMinAndMax("fieldset2");
EXPECT_EQ(size.min_size, LayoutUnit(102));
EXPECT_EQ(size.max_size, LayoutUnit(102));
size = RunComputeMinAndMax("fieldset3");
EXPECT_EQ(size.min_size, LayoutUnit(102));
EXPECT_EQ(size.max_size, LayoutUnit(126));
size = RunComputeMinAndMax("fieldset4");
EXPECT_EQ(size.min_size, LayoutUnit(152));
EXPECT_EQ(size.max_size, LayoutUnit(202));
size = RunComputeMinAndMax("fieldset5");
EXPECT_EQ(size.min_size, LayoutUnit(152));
EXPECT_EQ(size.max_size, LayoutUnit(176));
size = RunComputeMinAndMax("fieldset6");
EXPECT_EQ(size.min_size, LayoutUnit(76));
EXPECT_EQ(size.max_size, LayoutUnit(126));
}
} // anonymous namespace
} // 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