layout: Never stretch indefinite intrinsic keywords other than auto (#34672)

Consider:
```html
<div style="position: relative; width: 50px; height: 50px; border: solid; margin: 5px">
  <div style="position: absolute; top: 0; bottom: 0; height: max-content">
    <canvas width="25" height="25" style="background: cyan; height: 100%"></canvas>
  </div>
</div>
```

In order to determine the inline min/max-content sizes, we need a
tentative block size as the input, which only takes extrinsic values
into account.

In this case `height: max-content` is intrinsic, so we were treating it
as `height: initial`, which would behave as a definite `height: stretch`.
Therefore, the canvas was able to resolve its percentage.

However, it seems weird to treat an explicitly intrinsic keyword in an
extrinsic way, and Blink doesn't do it. So now we treat the tentative
block size as indefinite, therefore the percentage behaves as auto.

This adds a new test, we were previously failing 6 subtests, now only 3.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2024-12-18 23:21:47 +01:00 committed by GitHub
parent 017b12a627
commit ab270f3d52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 329 additions and 6 deletions

View file

@ -627,7 +627,7 @@ impl ToLogicalWithContainingBlock<LogicalRect<Au>> for PhysicalRect<Au> {
/// The possible values accepted by the sizing properties.
/// <https://drafts.csswg.org/css-sizing/#sizing-properties>
#[derive(Clone, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Size<T> {
/// Represents an `auto` value for the preferred and minimum size properties,
/// or `none` for the maximum size properties.

View file

@ -799,7 +799,6 @@ impl<'a> AbsoluteAxisSolver<'a> {
// A LazyCell will only invoke it once if needed, and then reuse the result.
let content_size = get_content_size.map(LazyCell::new);
let solve_size = |initial_behavior, stretch_size: Au| -> SizeConstraint {
let initial_is_stretch = initial_behavior == Size::Stretch;
let stretch_size = stretch_size.max(Au::zero());
if let Some(ref content_size) = content_size {
let preferred_size = Some(self.computed_size.resolve(
@ -816,10 +815,12 @@ impl<'a> AbsoluteAxisSolver<'a> {
.resolve_non_initial(stretch_size, content_size);
SizeConstraint::new(preferred_size, min_size, max_size)
} else {
let preferred_size = self
.computed_size
.maybe_resolve_extrinsic(Some(stretch_size))
.or(initial_is_stretch.then_some(stretch_size));
let preferred_size = if self.computed_size.is_initial() {
initial_behavior
} else {
self.computed_size
}
.maybe_resolve_extrinsic(Some(stretch_size));
let min_size = self
.computed_min_size
.maybe_resolve_extrinsic(Some(stretch_size))

View file

@ -577577,6 +577577,13 @@
{}
]
],
"keyword-sizes-on-abspos.html": [
"2bc4c463e0b34084af70f3c8dca97cadb90d36cf",
[
null,
{}
]
],
"keyword-sizes-on-floated-element.html": [
"e3da8bee7eb7b613e457d00eb88a677c35698d08",
[

View file

@ -0,0 +1,9 @@
[keyword-sizes-on-abspos.html]
[.test 17]
expected: FAIL
[.test 22]
expected: FAIL
[.test 27]
expected: FAIL

View file

@ -0,0 +1,306 @@
<!DOCTYPE html>
<title>Keyword sizes on absolutely positioned box</title>
<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#sizing-values">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#sizing-values">
<link rel="help" href="https://drafts.csswg.org/css-position/#abspos-layout">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/11387">
<meta assert="The various keyword sizes work as expected on absolutely positioned boxes.">
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
<style>
.cb {
position: relative;
width: 100px;
height: 100px
}
.test {
position: absolute;
inset: 0;
margin: 5px;
border: 3px solid;
padding: 2px;
font: 20px/1 Ahem;
}
/* Set the preferred size to small amount, to test that the min size works */
.test.min-width { width: 0px }
.test.min-height { height: 0px }
/* Set the preferred size to big amount, to test that the max size works */
.test.max-width { width: 500px }
.test.max-height { height: 500px }
/* stretch isn't widely supported, fall back to vendor-prefixed alternatives */
.width.stretch { width: -moz-available; width: -webkit-fill-available; width: stretch }
.min-width.stretch { min-width: -moz-available; min-width: -webkit-fill-available; min-width: stretch }
.max-width.stretch { max-width: -moz-available; max-width: -webkit-fill-available; max-width: stretch }
.height.stretch { height: -moz-available; height: -webkit-fill-available; height: stretch }
.min-height.stretch { min-height: -moz-available; min-height: -webkit-fill-available; min-height: stretch }
.max-height.stretch { max-height: -moz-available; max-height: -webkit-fill-available; max-height: stretch }
canvas { display: block; background: currentcolor }
.test.width canvas, .test.min-width canvas, .test.max-width canvas { width: 100% }
.test.height canvas, .test.min-height canvas, .test.max-height canvas { height: 100% }
</style>
<div id="log"></div>
<div class="cb">
<div class="test width" style="width: initial" data-expected-width="90">XX XX</div>
</div>
<div class="cb">
<div class="test width" style="width: min-content" data-expected-width="50">XX XX</div>
</div>
<div class="cb">
<div class="test width" style="width: fit-content" data-expected-width="90">XX XX</div>
</div>
<div class="cb">
<div class="test width" style="width: max-content" data-expected-width="110">XX XX</div>
</div>
<div class="cb">
<div class="test width stretch" data-expected-width="90">XX XX</div>
</div>
<div class="cb">
<div class="test min-width" style="min-width: initial" data-expected-width="10">XX XX</div>
</div>
<div class="cb">
<div class="test min-width" style="min-width: min-content" data-expected-width="50">XX XX</div>
</div>
<div class="cb">
<div class="test min-width" style="min-width: fit-content" data-expected-width="90">XX XX</div>
</div>
<div class="cb">
<div class="test min-width" style="min-width: max-content" data-expected-width="110">XX XX</div>
</div>
<div class="cb">
<div class="test min-width stretch" data-expected-width="90">XX XX</div>
</div>
<div class="cb">
<div class="test max-width" style="max-width: initial" data-expected-width="510">XX XX</div>
</div>
<div class="cb">
<div class="test max-width" style="max-width: min-content" data-expected-width="50">XX XX</div>
</div>
<div class="cb">
<div class="test max-width" style="max-width: fit-content" data-expected-width="90">XX XX</div>
</div>
<div class="cb">
<div class="test max-width" style="max-width: max-content" data-expected-width="110">XX XX</div>
</div>
<div class="cb">
<div class="test max-width stretch" data-expected-width="90">XX XX</div>
</div>
<div class="cb">
<div class="test width" style="width: initial" data-expected-width="90">
<canvas width="20" height="10" data-expected-width="80" data-expected-height="40"></canvas>
</div>
</div>
<div class="cb">
<div class="test width" style="width: min-content" data-expected-width="10">
<canvas width="20" height="10" data-expected-width="0" data-expected-height="0"></canvas>
</div>
</div>
<div class="cb">
<div class="test width" style="width: fit-content" data-expected-width="30">
<canvas width="20" height="10" data-expected-width="20" data-expected-height="10"></canvas>
</div>
</div>
<div class="cb">
<div class="test width" style="width: max-content" data-expected-width="30">
<canvas width="20" height="10" data-expected-width="20" data-expected-height="10"></canvas>
</div>
</div>
<div class="cb">
<div class="test width stretch" data-expected-width="90">
<canvas width="20" height="10" data-expected-width="80" data-expected-height="40"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-width" style="min-width: initial" data-expected-width="10">
<canvas width="20" height="10" data-expected-width="0" data-expected-height="0"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-width" style="min-width: min-content" data-expected-width="10">
<canvas width="20" height="10" data-expected-width="0" data-expected-height="0"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-width" style="min-width: fit-content" data-expected-width="30">
<canvas width="20" height="10" data-expected-width="20" data-expected-height="10"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-width" style="min-width: max-content" data-expected-width="30">
<canvas width="20" height="10" data-expected-width="20" data-expected-height="10"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-width stretch" data-expected-width="90">
<canvas width="20" height="10" data-expected-width="80" data-expected-height="40"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-width" style="max-width: initial" data-expected-width="510">
<canvas width="20" height="10" data-expected-width="500" data-expected-height="250"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-width" style="max-width: min-content" data-expected-width="10">
<canvas width="20" height="10" data-expected-width="0" data-expected-height="0"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-width" style="max-width: fit-content" data-expected-width="30">
<canvas width="20" height="10" data-expected-width="20" data-expected-height="10"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-width" style="max-width: max-content" data-expected-width="30">
<canvas width="20" height="10" data-expected-width="20" data-expected-height="10"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-width stretch" data-expected-width="90">
<canvas width="20" height="10" data-expected-width="80" data-expected-height="40"></canvas>
</div>
</div>
<div class="cb">
<div class="test height" style="height: initial" data-expected-height="90">XX XX</div>
</div>
<div class="cb">
<div class="test height" style="height: min-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test height" style="height: fit-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test height" style="height: max-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test height stretch" data-expected-height="90">XX XX</div>
</div>
<div class="cb">
<div class="test min-height" style="min-height: initial" data-expected-height="10">XX XX</div>
</div>
<div class="cb">
<div class="test min-height" style="min-height: min-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test min-height" style="min-height: fit-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test min-height" style="min-height: max-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test min-height stretch" data-expected-height="90">XX XX</div>
</div>
<div class="cb">
<div class="test max-height" style="max-height: initial" data-expected-height="510">XX XX</div>
</div>
<div class="cb">
<div class="test max-height" style="max-height: min-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test max-height" style="max-height: fit-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test max-height" style="max-height: max-content" data-expected-height="50">XX XX</div>
</div>
<div class="cb">
<div class="test max-height stretch" data-expected-height="90">XX XX</div>
</div>
<div class="cb">
<div class="test height" style="height: initial" data-expected-height="90">
<canvas width="10" height="20" data-expected-width="40" data-expected-height="80"></canvas>
</div>
</div>
<div class="cb">
<div class="test height" style="height: min-content" data-expected-height="30">
<canvas width="10" height="20" data-expected-width="10" data-expected-height="20"></canvas>
</div>
</div>
<div class="cb">
<div class="test height" style="height: fit-content" data-expected-height="30">
<canvas width="10" height="20" data-expected-width="10" data-expected-height="20"></canvas>
</div>
</div>
<div class="cb">
<div class="test height" style="height: max-content" data-expected-height="30">
<canvas width="10" height="20" data-expected-width="10" data-expected-height="20"></canvas>
</div>
</div>
<div class="cb">
<div class="test height stretch" data-expected-height="90">
<canvas width="10" height="20" data-expected-width="40" data-expected-height="80"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-height" style="min-height: initial" data-expected-height="10">
<canvas width="10" height="20" data-expected-width="0" data-expected-height="0"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-height" style="min-height: min-content" data-expected-height="10">
<canvas width="10" height="20" data-expected-width="0" data-expected-height="0"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-height" style="min-height: fit-content" data-expected-height="10">
<canvas width="10" height="20" data-expected-width="0" data-expected-height="0"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-height" style="min-height: max-content" data-expected-height="10">
<canvas width="10" height="20" data-expected-width="0" data-expected-height="0"></canvas>
</div>
</div>
<div class="cb">
<div class="test min-height stretch" data-expected-height="90">
<canvas width="10" height="20" data-expected-width="40" data-expected-height="80"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-height" style="max-height: initial" data-expected-height="510">
<canvas width="10" height="20" data-expected-width="250" data-expected-height="500"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-height" style="max-height: min-content" data-expected-height="510">
<canvas width="10" height="20" data-expected-width="250" data-expected-height="500"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-height" style="max-height: fit-content" data-expected-height="510">
<canvas width="10" height="20" data-expected-width="250" data-expected-height="500"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-height" style="max-height: max-content" data-expected-height="510">
<canvas width="10" height="20" data-expected-width="250" data-expected-height="500"></canvas>
</div>
</div>
<div class="cb">
<div class="test max-height stretch" data-expected-height="90">
<canvas width="10" height="20" data-expected-width="40" data-expected-height="80"></canvas>
</div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<script>
document.fonts.ready.then(() => checkLayout(".test"));
</script>