Skip to content

Commit

Permalink
NG/DL: Implement size containment (and display lock) for NG fieldset …
Browse files Browse the repository at this point in the history
…algo

This patch adds size containment implementation for fieldsets in
LayoutNG.

Tested with LayoutNGFieldset feature flag.

Along with the new tests,
external/wpt/css/css-contain/contain-size-fieldset-001.html and
external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-fieldset-001.html
are also passing.

R=chrishtr@chromium.org, ikilpatrick@chromium.org, mstensho@chromium.org

Bug: 958975
Change-Id: I5e735adc65ed9450f84e4c1ab68cba69e3fd85ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1613844
Reviewed-by: Morten Stenshorne <mstensho@chromium.org>
Commit-Queue: vmpstr <vmpstr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#660866}
  • Loading branch information
vmpstr authored and Commit Bot committed May 17, 2019
1 parent bccc229 commit e90c6ef
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 16 deletions.
Expand Up @@ -49,6 +49,7 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
LayoutUnit block_start_padding_edge =
container_builder_.Borders().block_start;

// TODO(vmpstr): Skip child (including legend) layout for fieldset elements.
if (NGBlockNode legend = Node().GetRenderedLegend()) {
// Lay out the legend. While the fieldset container normally ignores its
// padding, the legend is laid out within what would have been the content
Expand Down Expand Up @@ -116,17 +117,26 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
intrinsic_block_size += padding.BlockSum();
}

intrinsic_block_size =
ClampIntrinsicBlockSize(Node(), border_padding_, intrinsic_block_size);

// Recompute the block-axis size now that we know our content size.
border_box_size.block_size = ComputeBlockSizeForFragment(
ConstraintSpace(), Style(), border_padding_, intrinsic_block_size);

// The above computation utility knows nothing about fieldset weirdness. The
// legend may eat from the available content box block size. Make room for
// that if necessary.
LayoutUnit minimum_border_box_block_size =
borders_with_legend.BlockSum() + padding.BlockSum();
border_box_size.block_size =
std::max(border_box_size.block_size, minimum_border_box_block_size);
// Note that in size containment, we have to consider sizing as if we have no
// contents, with the conjecture being that legend is part of the contents.
// Thus, only do this adjustment if we do not contain size.
if (!Node().ShouldApplySizeContainment() &&
!Node().DisplayLockInducesSizeContainment()) {
LayoutUnit minimum_border_box_block_size =
borders_with_legend.BlockSum() + padding.BlockSum();
border_box_size.block_size =
std::max(border_box_size.block_size, minimum_border_box_block_size);
}

container_builder_.SetIsFieldsetContainer();
container_builder_.SetIntrinsicBlockSize(intrinsic_block_size);
Expand All @@ -143,25 +153,38 @@ base::Optional<MinMaxSize> NGFieldsetLayoutAlgorithm::ComputeMinMaxSize(
const MinMaxSizeInput& input) const {
MinMaxSize sizes;

// Size-contained elements don't consider their contents for intrinsic sizing.
// TODO(crbug.com/958975): Display locking needs to handle this.
if (node_.ShouldApplySizeContainment())
return sizes;
bool apply_size_containment = node_.ShouldApplySizeContainment();
if (apply_size_containment) {
if (input.size_type == NGMinMaxSizeType::kContentBoxSize)
return sizes;
} else if (node_.DisplayLockInducesSizeContainment()) {
sizes = node_.GetDisplayLockContext().GetLockedContentLogicalWidth();
if (input.size_type == NGMinMaxSizeType::kContentBoxSize)
return sizes;
apply_size_containment = true;
}

if (NGBlockNode legend = Node().GetRenderedLegend()) {
sizes = ComputeMinAndMaxContentContribution(Style(), legend, input);
sizes += ComputeMinMaxMargins(Style(), legend).InlineSum();
// Size containment does not consider the legend for sizing.
if (!apply_size_containment) {
if (NGBlockNode legend = Node().GetRenderedLegend()) {
sizes = ComputeMinAndMaxContentContribution(Style(), legend, input);
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(), content, input);
content_minmax += ComputeMinMaxMargins(Style(), content).InlineSum();
sizes.Encompass(content_minmax);
// Size containment does not consider the content for sizing.
if (!apply_size_containment) {
if (NGBlockNode content = Node().GetFieldsetContent()) {
MinMaxSize content_minmax =
ComputeMinAndMaxContentContribution(Style(), content, input);
content_minmax += ComputeMinMaxMargins(Style(), content).InlineSum();
sizes.Encompass(content_minmax);
}
}

sizes += ComputeBorders(ConstraintSpace(), node_).InlineSum();
Expand Down
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Containment Test: Size containment on fieldset</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-size">
<link rel="match" href="reference/contain-size-fieldset-002-ref.html">
<meta name=assert content="Size containment does apply to fieldsets, thus their size is the same than if they don't have contents.">
<style>
#border {
border: 1px solid black;
width: min-content;
}
fieldset {
contain: size;
visibility: hidden;
height: 1px;
}
</style>

<p>This test passes if it has the same output as the reference. You should see a black border box below.</p>
<div id="border">
<fieldset>
<legend>legend</legend>
Fieldset contents are here.
</fieldset>
</div>
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Containment Test: Reference file</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<style>
#border {
border: 1px solid black;
width: min-content;
}
fieldset {
visibility: hidden;
height: 1px;
}
</style>

<p>This test passes if it has the same output as the reference. You should see a black border box below.</p>
<div id="border">
<fieldset></fieldset>
</div>
@@ -0,0 +1,38 @@
<!doctype HTML>
<html class="reftest-wait">
<meta charset="utf8">
<title>Display Locking: fieldset with legend and size containment, sized by display lock</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://github.com/WICG/display-locking">
<link rel="match" href="fieldset-empty-ref.html">
<script src="/common/reftest-wait.js"></script>

<style>
#border {
border: 1px solid black;
width: min-content;
}
#container {
contain: style layout size;
background: lightblue;
}
</style>

<div id="border">
<fieldset id="container">
<legend>This is a legend</legend>
This is some content.
</fieldset>
</div>

<script>
async function runTest() {
const container = document.getElementById("container");
await container.displayLock.acquire({ timeout: Infinity, size: [123, 234] });
takeScreenshot();
}

window.onload = runTest;
</script>

</html>
@@ -0,0 +1,21 @@
<!doctype HTML>
<html>
<meta charset="utf8">
<title>Display Locking: empty fieldset (reference)</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://github.com/WICG/display-locking">

<style>
#border {
border: 1px solid black;
width: min-content;
}
fieldset {
visibility: hidden;
}
</style>

<div id="border">
<fieldset></fieldset>
</div>
</html>
@@ -0,0 +1,35 @@
<!doctype HTML>
<html class="reftest-wait">
<meta charset="utf8">
<title>Display Locking: empty fieldset</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://github.com/WICG/display-locking">
<link rel="match" href="fieldset-empty-ref.html">
<script src="/common/reftest-wait.js"></script>

<style>
#border {
border: 1px solid black;
width: min-content;
}
#container {
contain: style layout;
background: lightblue;
}
</style>

<div id="border">
<fieldset id="container"></fieldset>
</div>

<script>
async function runTest() {
const container = document.getElementById("container");
await container.displayLock.acquire({ timeout: Infinity, size: [0, 0] });
takeScreenshot();
}

window.onload = runTest;
</script>

</html>
@@ -0,0 +1,27 @@
<!doctype HTML>
<html>
<meta charset="utf8">
<title>Display Locking: fieldset with legend, sized by display lock (reference)</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://github.com/WICG/display-locking">

<style>
#border {
border: 1px solid black;
width: min-content;
}
fieldset {
visibility: hidden;
}
#child {
width: 123px;
height: 234px;
}
</style>

<div id="border">
<fieldset>
<div id="child"></div>
</fieldset>
</div>
</html>
@@ -0,0 +1,38 @@
<!doctype HTML>
<html class="reftest-wait">
<meta charset="utf8">
<title>Display Locking: fieldset with legend, sized by display lock</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://github.com/WICG/display-locking">
<link rel="match" href="fieldset-with-legend-sized-ref.html">
<script src="/common/reftest-wait.js"></script>

<style>
#border {
border: 1px solid black;
width: min-content;
}
#container {
contain: style layout;
background: lightblue;
}
</style>

<div id="border">
<fieldset id="container">
<legend>This is a legend</legend>
This is some content.
</fieldset>
</div>

<script>
async function runTest() {
const container = document.getElementById("container");
await container.displayLock.acquire({ timeout: Infinity, size: [123, 234] });
takeScreenshot();
}

window.onload = runTest;
</script>

</html>
@@ -0,0 +1,38 @@
<!doctype HTML>
<html class="reftest-wait">
<meta charset="utf8">
<title>Display Locking: fieldset with legend</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://github.com/WICG/display-locking">
<!-- The legend should be ignored when sizing, resulting in the same ref as empty -->
<link rel="match" href="fieldset-empty-ref.html">
<script src="/common/reftest-wait.js"></script>

<style>
#border {
border: 1px solid black;
width: min-content;
}
#container {
contain: style layout;
background: lightblue;
}
</style>

<div id="border">
<fieldset id="container">
<legend>This is a legend</legend>
</fieldset>
</div>

<script>
async function runTest() {
const container = document.getElementById("container");
await container.displayLock.acquire({ timeout: Infinity, size: [0, 0] });
takeScreenshot();
}

window.onload = runTest;
</script>

</html>

0 comments on commit e90c6ef

Please sign in to comment.