LibWeb: Don't distort replaced elements with natural size in flex layout
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

When layouting a replaced element with natural width and height (e.g. a
raster graphic), the replaced element would correctly end up with its
natural size in the main-axis dimension. For the cross axis dimension
however, the replaced element was stretched or squished to the flex
containers inner cross size, which is wrong. Instead, we need to respect
the replaced elements aspect ratio.

Since the touched code does not have a direct correspondence to any spec
text, I am not fully certain that the change is completely correct.
However, tests agree with it, so the new code seems more correct than
the old one at least.

This fixes 50 WPT subtests in `css/css-flexbox`, most of which are
already in-tree. I have also created a new test for a scenario that did
not seem to be covered by WPT.
This commit is contained in:
InvalidUsernameException 2025-07-10 13:27:53 +02:00 committed by Jelle Raaijmakers
commit 8002efe780
Notes: github-actions[bot] 2025-07-14 22:54:06 +00:00
11 changed files with 94 additions and 62 deletions

View file

@ -1137,7 +1137,7 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem&
}
if (item.box->has_preferred_aspect_ratio()) {
if (item.used_flex_basis_is_definite) {
if (item.used_flex_basis_is_definite || (item.box->has_natural_width() && item.box->has_natural_height())) {
item.hypothetical_cross_size = calculate_cross_size_from_main_size_and_aspect_ratio(item.main_size.value(), item.box->preferred_aspect_ratio().value());
return;
}

View file

@ -1,14 +1,14 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (1,1) content-size 798x69.984375 [BFC] children: not-inline
Box <body> at (10,10) content-size 780x51.984375 flex-container(row) [FFC] children: not-inline
ImageBox <img> at (11,10) content-size 66.65625x50 flex-item children: not-inline
ImageBox <img> at (11,11) content-size 66.65625x49.984375 flex-item children: not-inline
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
TextNode <#text>
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x71.984375]
PaintableBox (Box<BODY>) [9,9 782x53.984375]
ImagePaintable (ImageBox<IMG>) [10,9 68.65625x52]
ImagePaintable (ImageBox<IMG>) [10,10 68.65625x51.984375]
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
SC for BlockContainer<HTML> [1,1 798x69.984375] [children: 0] (z-index: auto)

View file

@ -0,0 +1,3 @@
Natural height of image (400px) should be same as computed height (400px).
Natural width of image (200px) should be same as computed width (200px).
Computed height of image (400px) should be larger than computed height of container (200px).

View file

@ -2,9 +2,8 @@ Harness status: OK
Found 4 tests
3 Pass
1 Fail
4 Pass
Pass img 1
Pass img 2
Pass img 3
Fail img 4
Pass img 4

View file

@ -2,20 +2,20 @@ Harness status: OK
Found 18 tests
5 Pass
13 Fail
Fail .flexbox > img 1
14 Pass
4 Fail
Pass .flexbox > img 1
Pass .flexbox > img 2
Pass .flexbox > img 3
Pass .flexbox > img 4
Fail .flexbox > img 5
Fail .flexbox > img 6
Fail .flexbox > img 7
Fail .flexbox > img 8
Fail .flexbox > img 9
Fail .flexbox > img 10
Fail .flexbox > img 11
Fail .flexbox > img 12
Pass .flexbox > img 5
Pass .flexbox > img 6
Pass .flexbox > img 7
Pass .flexbox > img 8
Pass .flexbox > img 9
Pass .flexbox > img 10
Pass .flexbox > img 11
Pass .flexbox > img 12
Fail .flexbox > img 13
Fail .flexbox > img 14
Pass .flexbox > img 15

View file

@ -2,20 +2,20 @@ Harness status: OK
Found 18 tests
5 Pass
13 Fail
Fail .flexbox > img 1
14 Pass
4 Fail
Pass .flexbox > img 1
Pass .flexbox > img 2
Pass .flexbox > img 3
Pass .flexbox > img 4
Fail .flexbox > img 5
Fail .flexbox > img 6
Fail .flexbox > img 7
Fail .flexbox > img 8
Fail .flexbox > img 9
Fail .flexbox > img 10
Fail .flexbox > img 11
Fail .flexbox > img 12
Pass .flexbox > img 5
Pass .flexbox > img 6
Pass .flexbox > img 7
Pass .flexbox > img 8
Pass .flexbox > img 9
Pass .flexbox > img 10
Pass .flexbox > img 11
Pass .flexbox > img 12
Fail .flexbox > img 13
Fail .flexbox > img 14
Pass .flexbox > img 15

View file

@ -2,20 +2,20 @@ Harness status: OK
Found 18 tests
5 Pass
13 Fail
Fail .flexbox > img 1
14 Pass
4 Fail
Pass .flexbox > img 1
Pass .flexbox > img 2
Pass .flexbox > img 3
Pass .flexbox > img 4
Fail .flexbox > img 5
Fail .flexbox > img 6
Fail .flexbox > img 7
Fail .flexbox > img 8
Fail .flexbox > img 9
Fail .flexbox > img 10
Fail .flexbox > img 11
Fail .flexbox > img 12
Pass .flexbox > img 5
Pass .flexbox > img 6
Pass .flexbox > img 7
Pass .flexbox > img 8
Pass .flexbox > img 9
Pass .flexbox > img 10
Pass .flexbox > img 11
Pass .flexbox > img 12
Fail .flexbox > img 13
Fail .flexbox > img 14
Fail .flexbox > img 15

View file

@ -2,20 +2,20 @@ Harness status: OK
Found 18 tests
5 Pass
13 Fail
Fail .flexbox > img 1
14 Pass
4 Fail
Pass .flexbox > img 1
Pass .flexbox > img 2
Pass .flexbox > img 3
Pass .flexbox > img 4
Fail .flexbox > img 5
Fail .flexbox > img 6
Fail .flexbox > img 7
Fail .flexbox > img 8
Fail .flexbox > img 9
Fail .flexbox > img 10
Fail .flexbox > img 11
Fail .flexbox > img 12
Pass .flexbox > img 5
Pass .flexbox > img 6
Pass .flexbox > img 7
Pass .flexbox > img 8
Pass .flexbox > img 9
Pass .flexbox > img 10
Pass .flexbox > img 11
Pass .flexbox > img 12
Fail .flexbox > img 13
Fail .flexbox > img 14
Fail .flexbox > img 15

View file

@ -2,20 +2,20 @@ Harness status: OK
Found 18 tests
5 Pass
13 Fail
Fail .flexbox > img 1
9 Pass
9 Fail
Pass .flexbox > img 1
Pass .flexbox > img 2
Pass .flexbox > img 3
Pass .flexbox > img 4
Fail .flexbox > img 5
Pass .flexbox > img 5
Fail .flexbox > img 6
Fail .flexbox > img 7
Fail .flexbox > img 8
Fail .flexbox > img 9
Pass .flexbox > img 9
Fail .flexbox > img 10
Fail .flexbox > img 11
Fail .flexbox > img 12
Pass .flexbox > img 12
Fail .flexbox > img 13
Fail .flexbox > img 14
Pass .flexbox > img 15

View file

@ -2,20 +2,20 @@ Harness status: OK
Found 18 tests
5 Pass
13 Fail
Fail .flexbox > img 1
9 Pass
9 Fail
Pass .flexbox > img 1
Pass .flexbox > img 2
Pass .flexbox > img 3
Pass .flexbox > img 4
Fail .flexbox > img 5
Pass .flexbox > img 5
Fail .flexbox > img 6
Fail .flexbox > img 7
Fail .flexbox > img 8
Fail .flexbox > img 9
Pass .flexbox > img 9
Fail .flexbox > img 10
Fail .flexbox > img 11
Fail .flexbox > img 12
Pass .flexbox > img 12
Fail .flexbox > img 13
Fail .flexbox > img 14
Pass .flexbox > img 15

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<script src="include.js"></script>
<style>
#flex {
display: flex;
align-items: center;
height: 200px;
/* Below properties exclusively so we can see things properly */
margin: 100px 0 100px 0;
background: tan;
}
</style>
<div id="flex">
<img id="img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAGQAQMAAADRPL3zAAAABlBMVEUwMDCAgIDHGf6uAAAAQ0lEQVRo3u3YMREAQAgDQRzg3+U7gDbl9+y1i4AMNdGrjBBCCCGEXJas84wQQgghhJwWW5kQQgghhPgnEkIIIYSQT1nvD3WWWfRskgAAAABJRU5ErkJggg==">
</div>
<script>
asyncTest(async (dome) => {
window.addEventListener('load', () => {
const image = document.getElementById("img");
const computedImageStyle = getComputedStyle(image);
const container = document.getElementById("flex");
const computedContainerHeight = getComputedStyle(container).height;
println(`Natural height of image (${image.naturalHeight}px) should be same as computed height (${computedImageStyle.height}).`);
println(`Natural width of image (${image.naturalWidth}px) should be same as computed width (${computedImageStyle.width}).`);
println(`Computed height of image (${computedImageStyle.height}) should be larger than computed height of container (${computedContainerHeight}).`);
dome();
});
});
</script>