LibWeb/Layout: Improve grid item sizing for replaced boxes

With this change we no longer stretch "width: auto" for replaced
elements and also use "width calculation rules for block-level replaced
elements", like suggested by the spec.
This commit is contained in:
Aliaksandr Kalenik 2025-03-20 00:44:06 +01:00 committed by Alexander Kalenik
parent 2dd657f530
commit 1f8e7c3cca
Notes: github-actions[bot] 2025-03-20 02:37:49 +00:00
13 changed files with 83 additions and 54 deletions

View file

@ -1549,6 +1549,7 @@ void GridFormattingContext::resolve_grid_item_widths()
{ {
for (auto& item : m_grid_items) { for (auto& item : m_grid_items) {
CSSPixels containing_block_width = containing_block_size_for_item(item, GridDimension::Column); CSSPixels containing_block_width = containing_block_size_for_item(item, GridDimension::Column);
CSS::JustifyItems justification = justification_for_item(item.box);
auto const& computed_values = item.box->computed_values(); auto const& computed_values = item.box->computed_values();
auto const& computed_width = computed_values.width(); auto const& computed_width = computed_values.width();
@ -1559,15 +1560,14 @@ void GridFormattingContext::resolve_grid_item_widths()
CSSPixels width; CSSPixels width;
}; };
ItemAlignment initial { auto try_compute_width = [&item, containing_block_width, justification](CSSPixels a_width, CSS::Size const& computed_width) -> ItemAlignment {
.margin_left = item.used_values.margin_left, auto const& computed_values = item.box->computed_values();
.margin_right = item.used_values.margin_right,
.width = item.used_values.content_width()
};
auto try_compute_width = [&](CSSPixels a_width, CSS::Size const& computed_width) -> ItemAlignment { ItemAlignment result = {
ItemAlignment result = initial; .margin_left = item.used_values.margin_left,
result.width = a_width; .margin_right = item.used_values.margin_right,
.width = a_width
};
// Auto margins absorb positive free space prior to alignment via the box alignment properties. // Auto margins absorb positive free space prior to alignment via the box alignment properties.
auto free_space_left_for_margins = containing_block_width - result.width - item.used_values.border_left - item.used_values.border_right - item.used_values.padding_left - item.used_values.padding_right - item.used_values.margin_left - item.used_values.margin_right; auto free_space_left_for_margins = containing_block_width - result.width - item.used_values.border_left - item.used_values.border_right - item.used_values.padding_left - item.used_values.padding_right - item.used_values.margin_left - item.used_values.margin_right;
@ -1578,13 +1578,14 @@ void GridFormattingContext::resolve_grid_item_widths()
result.margin_left = free_space_left_for_margins; result.margin_left = free_space_left_for_margins;
} else if (computed_values.margin().right().is_auto()) { } else if (computed_values.margin().right().is_auto()) {
result.margin_right = free_space_left_for_margins; result.margin_right = free_space_left_for_margins;
} else if (computed_width.is_auto()) { } else if (computed_width.is_auto() && !item.box->is_replaced_box()) {
result.width += free_space_left_for_margins; result.width += free_space_left_for_margins;
} }
auto free_space_left_for_alignment = containing_block_width - a_width - item.used_values.border_left - item.used_values.border_right - item.used_values.padding_left - item.used_values.padding_right - item.used_values.margin_left - item.used_values.margin_right; auto free_space_left_for_alignment = containing_block_width - a_width - item.used_values.border_left - item.used_values.border_right - item.used_values.padding_left - item.used_values.padding_right - item.used_values.margin_left - item.used_values.margin_right;
switch (justification_for_item(item.box)) { switch (justification) {
case CSS::JustifyItems::Normal: case CSS::JustifyItems::Normal:
break;
case CSS::JustifyItems::Stretch: case CSS::JustifyItems::Stretch:
break; break;
case CSS::JustifyItems::Center: case CSS::JustifyItems::Center:
@ -1613,13 +1614,18 @@ void GridFormattingContext::resolve_grid_item_widths()
ItemAlignment used_alignment; ItemAlignment used_alignment;
AvailableSpace available_space { AvailableSize::make_definite(containing_block_width), AvailableSize::make_indefinite() }; AvailableSpace available_space { AvailableSize::make_definite(containing_block_width), AvailableSize::make_indefinite() };
if (computed_width.is_auto()) { if (item.box->is_replaced_box() && item.box->has_natural_width()) {
used_alignment = try_compute_width(calculate_fit_content_width(item.box, available_space), computed_width); auto width = tentative_width_for_replaced_element(item.box, computed_values.width(), available_space);
} else if (computed_width.is_fit_content()) { used_alignment = try_compute_width(width, computed_width);
used_alignment = try_compute_width(calculate_fit_content_width(item.box, available_space), computed_width);
} else { } else {
auto width_px = calculate_inner_width(item.box, available_space.width, computed_width); if (computed_width.is_auto() || computed_width.is_fit_content()) {
used_alignment = try_compute_width(width_px, computed_width); auto fit_content_width = calculate_fit_content_width(item.box, available_space);
used_alignment = try_compute_width(fit_content_width, computed_width);
used_alignment = try_compute_width(calculate_fit_content_width(item.box, available_space), computed_width);
} else {
auto width_px = calculate_inner_width(item.box, available_space.width, computed_width);
used_alignment = try_compute_width(width_px, computed_width);
}
} }
if (!should_treat_max_width_as_none(item.box, m_available_space->width)) { if (!should_treat_max_width_as_none(item.box, m_available_space->width)) {
@ -1648,6 +1654,7 @@ void GridFormattingContext::resolve_grid_item_heights()
{ {
for (auto& item : m_grid_items) { for (auto& item : m_grid_items) {
CSSPixels containing_block_height = containing_block_size_for_item(item, GridDimension::Row); CSSPixels containing_block_height = containing_block_size_for_item(item, GridDimension::Row);
CSS::AlignItems alignment = alignment_for_item(item.box);
auto const& computed_values = item.box->computed_values(); auto const& computed_values = item.box->computed_values();
auto const& computed_height = computed_values.height(); auto const& computed_height = computed_values.height();
@ -1658,15 +1665,14 @@ void GridFormattingContext::resolve_grid_item_heights()
CSSPixels height; CSSPixels height;
}; };
ItemAlignment initial { auto try_compute_height = [&item, containing_block_height, alignment](CSSPixels a_height) -> ItemAlignment {
.margin_top = item.used_values.margin_top, auto const& computed_values = item.box->computed_values();
.margin_bottom = item.used_values.margin_bottom,
.height = item.used_values.content_height()
};
auto try_compute_height = [&](CSSPixels a_height) -> ItemAlignment { ItemAlignment result = {
ItemAlignment result = initial; .margin_top = item.used_values.margin_top,
result.height = a_height; .margin_bottom = item.used_values.margin_bottom,
.height = a_height
};
CSSPixels height = a_height; CSSPixels height = a_height;
auto underflow_px = containing_block_height - height - item.used_values.border_top - item.used_values.border_bottom - item.used_values.padding_top - item.used_values.padding_bottom - item.used_values.margin_top - item.used_values.margin_bottom; auto underflow_px = containing_block_height - height - item.used_values.border_top - item.used_values.border_bottom - item.used_values.padding_top - item.used_values.padding_bottom - item.used_values.margin_top - item.used_values.margin_bottom;
@ -1678,13 +1684,14 @@ void GridFormattingContext::resolve_grid_item_heights()
result.margin_top = underflow_px; result.margin_top = underflow_px;
} else if (computed_values.margin().bottom().is_auto()) { } else if (computed_values.margin().bottom().is_auto()) {
result.margin_bottom = underflow_px; result.margin_bottom = underflow_px;
} else if (computed_values.height().is_auto()) { } else if (computed_values.height().is_auto() && !item.box->is_replaced_box()) {
height += underflow_px; height += underflow_px;
} }
switch (alignment_for_item(item.box)) { switch (alignment) {
case CSS::AlignItems::Baseline: case CSS::AlignItems::Baseline:
// FIXME: Not implemented // FIXME: Not implemented
break;
case CSS::AlignItems::Stretch: case CSS::AlignItems::Stretch:
case CSS::AlignItems::Normal: case CSS::AlignItems::Normal:
result.height = height; result.height = height;

View file

@ -0,0 +1,11 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x116 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 100x100 children: not-inline
Box <div.img-wrapper> at (8,8) content-size 100x100 [GFC] children: not-inline
ImageBox <img> at (8,8) content-size 10x10 children: not-inline
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x116]
PaintableWithLines (BlockContainer<BODY>) [8,8 100x100]
PaintableBox (Box<DIV>.img-wrapper) [8,8 100x100]
ImagePaintable (ImageBox<IMG>) [8,8 10x10]

View file

@ -0,0 +1,12 @@
<!DOCTYPE html><style>
body {
width: 100px;
height: 100px;
}
.img-wrapper {
display: grid;
width: 100%;
height: 100%;
background-color: darkorchid;
}
</style><div class="img-wrapper"><img src="data:image/webp;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAADUlEQVR4nGNgGAWkAwABNgABVtF/yAAAAABJRU5ErkJggg=="></div>

View file

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests Found 2 tests
2 Fail 1 Pass
1 Fail
Fail .before 1 Fail .before 1
Fail .after 2 Pass .after 2

View file

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests Found 2 tests
2 Fail 1 Pass
Fail .before 1 1 Fail
Pass .before 1
Fail .after 2 Fail .after 2

View file

@ -2,7 +2,6 @@ Harness status: OK
Found 2 tests Found 2 tests
1 Pass 2 Pass
1 Fail
Pass .before 1 Pass .before 1
Fail .after 2 Pass .after 2

View file

@ -2,7 +2,6 @@ Harness status: OK
Found 2 tests Found 2 tests
1 Pass 2 Pass
1 Fail Pass .before 1
Fail .before 1
Pass .after 2 Pass .after 2

View file

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests Found 2 tests
2 Fail 1 Pass
1 Fail
Fail .before 1 Fail .before 1
Fail .after 2 Pass .after 2

View file

@ -4,5 +4,5 @@ Found 2 tests
1 Pass 1 Pass
1 Fail 1 Fail
Fail .before 1 Pass .before 1
Pass .after 2 Fail .after 2

View file

@ -2,7 +2,6 @@ Harness status: OK
Found 2 tests Found 2 tests
1 Pass 2 Pass
1 Fail
Pass .before 1 Pass .before 1
Fail .after 2 Pass .after 2

View file

@ -2,7 +2,6 @@ Harness status: OK
Found 2 tests Found 2 tests
1 Pass 2 Pass
1 Fail Pass .before 1
Fail .before 1
Pass .after 2 Pass .after 2

View file

@ -2,11 +2,11 @@ Harness status: OK
Found 6 tests Found 6 tests
1 Pass 3 Pass
5 Fail 3 Fail
Fail .before 1 Fail .before 1
Fail .before 2 Pass .before 2
Fail .before 3 Fail .before 3
Fail .after 4 Fail .after 4
Pass .after 5 Pass .after 5
Fail .after 6 Pass .after 6

View file

@ -2,11 +2,11 @@ Harness status: OK
Found 6 tests Found 6 tests
1 Pass 3 Pass
5 Fail 3 Fail
Fail .before 1 Fail .before 1
Pass .before 2 Pass .before 2
Fail .before 3 Pass .before 3
Fail .after 4 Fail .after 4
Fail .after 5 Pass .after 5
Fail .after 6 Fail .after 6