layout: Properly handle intrinsic min/max block sizes on replaced element (#37457)

This change aligns Servo with both Blink and WebKit in common cases.

When the `aspect-ratio` property is set to a different value than the
natural ratio, then Blink and WebKit disagree, we match Blink.

Gecko doesn't support intrinsic min/max block sizes at all.

Note this patch doesn't fix the intrinsic contributions, they will need
to be addressed in a follow-up patch.

Testing: Covered by WPT
Fixes: #37433

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2025-06-14 11:24:10 +02:00 committed by GitHub
parent a27f2bb84d
commit f963f2731d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 136 additions and 69 deletions

View file

@ -509,55 +509,60 @@ impl ReplacedContents {
.to_definite()
.map(|block_size| Au::zero().max(block_size - pbm_sums.block));
let resolve_inline_size = |get_block_size: &dyn Fn() -> SizeConstraint| {
let get_inline_content_size = || {
self.content_size(
Direction::Inline,
preferred_aspect_ratio,
get_block_size,
&get_inline_fallback_size,
)
.into()
};
sizes.inline.resolve(
Direction::Inline,
automatic_size.inline,
Au::zero,
Some(inline_stretch_size),
get_inline_content_size,
false, /* is_table */
)
};
let resolve_block_size = |get_inline_size: &dyn Fn() -> SizeConstraint| {
let get_block_content_size = || -> ContentSizes {
self.content_size(
Direction::Block,
preferred_aspect_ratio,
get_inline_size,
&get_block_fallback_size,
)
.into()
};
sizes.block.resolve(
Direction::Block,
automatic_size.block,
Au::zero,
block_stretch_size,
get_block_content_size,
false, /* is_table */
)
};
// First, compute the inline size. Intrinsic values depend on the block sizing properties
// through the aspect ratio, but these can also be intrinsic and depend on the inline size.
// Therefore, we tentatively treat intrinsic block sizing properties as their initial value.
let get_inline_content_size = || {
let get_block_size = || {
sizes
.block
.resolve_extrinsic(automatic_size.block, Au::zero(), block_stretch_size)
};
self.content_size(
Direction::Inline,
preferred_aspect_ratio,
&get_block_size,
&get_inline_fallback_size,
)
.into()
};
let inline_size = sizes.inline.resolve(
Direction::Inline,
automatic_size.inline,
Au::zero,
Some(inline_stretch_size),
get_inline_content_size,
false, /* is_table */
);
// Now we can compute the block size, using the inline size from above.
let get_block_content_size = || -> ContentSizes {
let get_inline_size = || SizeConstraint::Definite(inline_size);
self.content_size(
Direction::Block,
preferred_aspect_ratio,
&get_inline_size,
&get_block_fallback_size,
)
.into()
};
let block_size = sizes.block.resolve(
Direction::Block,
automatic_size.block,
Au::zero,
block_stretch_size,
get_block_content_size,
false, /* is_table */
);
// Therefore, when there is an aspect ratio, we may need to:
// 1. Tentatively resolve the inline size, ignoring block sizing properties.
// 2. Tentatively resolve the block size, resolving intrinsic keywords by transferring (1).
// 3. Resolve the final inline size, resolving intrinsic keywords by transferring (2).
// 4. Resolve the final block size, resolving intrinsic keywords by transferring (3).
let inline_size = resolve_inline_size(&|| {
SizeConstraint::Definite(resolve_block_size(&|| {
SizeConstraint::Definite(resolve_inline_size(&|| SizeConstraint::default()))
}))
});
LogicalVec2 {
inline: inline_size,
block: block_size,
block: resolve_block_size(&|| SizeConstraint::Definite(inline_size)),
}
}

View file

@ -249947,6 +249947,32 @@
{}
]
],
"replaced-element-047.tentative.html": [
"ec0d6394088436ce3caa0b5d990c2b2fe017f583",
[
null,
[
[
"/css/reference/ref-filled-green-100px-square.xht",
"=="
]
],
{}
]
],
"replaced-element-048.tentative.html": [
"66057fbdd4df4062e48f81de9417b7cc89fe54f1",
[
null,
[
[
"/css/reference/ref-filled-green-100px-square.xht",
"=="
]
],
{}
]
],
"replaced-element-dynamic-aspect-ratio.html": [
"d4b83d3673cbfba940baec1c88f3e6630c760eb4",
[

View file

@ -1,24 +0,0 @@
[keyword-sizes-on-replaced-element.html]
[.test 68]
expected: FAIL
[.test 69]
expected: FAIL
[.test 71]
expected: FAIL
[.test 75]
expected: FAIL
[.test 77]
expected: FAIL
[.test 78]
expected: FAIL
[.test 80]
expected: FAIL
[.test 84]
expected: FAIL

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#valdef-width-max-content">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12333">
<link rel="help" href="https://github.com/servo/servo/issues/37433">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
<style>
.red {
position: absolute;
z-index: -1;
width: 100px;
height: 100px;
background: red;
}
canvas {
display: block;
width: max-content;
height: 0px;
min-height: max-content;
aspect-ratio: 1;
background: green;
}
</style>
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div class="red"></div>
<canvas width="100" height="50"></canvas>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#valdef-width-max-content">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12333">
<link rel="help" href="https://github.com/servo/servo/issues/37433">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
<style>
.red {
position: absolute;
z-index: -1;
width: 100px;
height: 100px;
background: red;
}
canvas {
display: block;
width: max-content;
height: 500px;
max-height: max-content;
aspect-ratio: 1;
background: green;
}
</style>
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div class="red"></div>
<canvas width="100" height="50"></canvas>