From 80c8e787a825da323b2419db962de65efbae1bcd Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sun, 17 Aug 2025 16:57:31 +0200 Subject: [PATCH] LibWeb: Fix abspos layout when box is contained by grid area Before this change, `layout_absolutely_positioned_element()` in GFC had an assumption that all contained by grid container abspos boxes were also direct children of the grid container. This change adds handling for the cases when it's not true and, in order to identify grid area abspos box belongs to, we have to find ancestor grid item. --- .../LibWeb/Layout/GridFormattingContext.cpp | 45 ++++++++++++++----- .../abspos-item-contained-by-grid-area.txt | 20 +++++++++ .../abspos-item-contained-by-grid-area.html | 19 ++++++++ 3 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 Tests/LibWeb/Layout/expected/grid/abspos-item-contained-by-grid-area.txt create mode 100644 Tests/LibWeb/Layout/input/grid/abspos-item-contained-by-grid-area.html diff --git a/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Libraries/LibWeb/Layout/GridFormattingContext.cpp index 55d9177547a..4899f56807b 100644 --- a/Libraries/LibWeb/Layout/GridFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/GridFormattingContext.cpp @@ -2109,27 +2109,48 @@ void GridFormattingContext::run(AvailableSpace const& available_space) m_grid_container_used_values.set_grid_template_rows(CSS::GridTrackSizeListStyleValue::create(move(grid_track_rows))); } +// https://www.w3.org/TR/css-grid-2/#abspos-items void GridFormattingContext::layout_absolutely_positioned_element(Box const& box) { auto& box_state = m_state.get_mutable(box); auto const& computed_values = box.computed_values(); - auto is_auto_row = is_auto_positioned_track(computed_values.grid_row_start(), computed_values.grid_row_end()); - auto is_auto_column = is_auto_positioned_track(computed_values.grid_column_start(), computed_values.grid_column_end()); + auto grid_area_rect = [&] -> CSSPixelRect { + // NOTE: Grid areas form containing blocks for abspos elements, but + // `Node::containing_block()` is not aware of that. Therefore, we need to + // find the closest grid item ancestor in order to identify grid area it belongs to. + NodeWithStyle const* containing_grid_item = &box; + while (containing_grid_item->parent() && !containing_grid_item->parent()->display().is_grid_inside()) + containing_grid_item = containing_grid_item->parent(); + + auto const& computed_values = containing_grid_item->computed_values(); + VERIFY(containing_grid_item); + + // NOTE: If abspos box is contained by in-flow grid item its grid position is already determined. + if (containing_grid_item->is_grid_item()) { + auto item = *m_grid_items.find_if([containing_grid_item](GridItem const& grid_item) { + return grid_item.box == containing_grid_item; + }); + return get_grid_area_rect(item); + } + + GridItem item { as(*containing_grid_item), box_state, {}, {}, {}, {} }; + auto is_auto_row = is_auto_positioned_track(computed_values.grid_row_start(), computed_values.grid_row_end()); + auto is_auto_column = is_auto_positioned_track(computed_values.grid_column_start(), computed_values.grid_column_end()); - GridItem item { box, box_state, {}, {}, {}, {} }; - if (!is_auto_row) { auto row_placement_position = resolve_grid_position(box, GridDimension::Row); - item.row = row_placement_position.start; - item.row_span = row_placement_position.span; - } - if (!is_auto_column) { auto column_placement_position = resolve_grid_position(box, GridDimension::Column); - item.column = column_placement_position.start; - item.column_span = column_placement_position.span; - } + if (!is_auto_row) { + item.row = row_placement_position.start; + item.row_span = row_placement_position.span; + } + if (!is_auto_column) { + item.column = column_placement_position.start; + item.column_span = column_placement_position.span; + } + return get_grid_area_rect(item); + }(); - auto grid_area_rect = get_grid_area_rect(item); auto available_width = AvailableSize::make_definite(grid_area_rect.width()); auto available_height = AvailableSize::make_definite(grid_area_rect.height()); AvailableSpace available_space { available_width, available_height }; diff --git a/Tests/LibWeb/Layout/expected/grid/abspos-item-contained-by-grid-area.txt b/Tests/LibWeb/Layout/expected/grid/abspos-item-contained-by-grid-area.txt new file mode 100644 index 00000000000..6c278febf34 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/abspos-item-contained-by-grid-area.txt @@ -0,0 +1,20 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x16 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x0 children: not-inline + Box at (8,8) content-size 800x600 positioned [GFC] children: not-inline + BlockContainer at (408,8) content-size 400x600 [BFC] children: not-inline + BlockContainer at (408,8) content-size 100x18 positioned [BFC] children: inline + frag 0 from TextNode start: 0, length: 5, rect: [408,8 39.78125x18] baseline: 13.796875 + "Hello" + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] overflow: [0,0 808x608] + PaintableWithLines (BlockContainer) [0,0 800x16] + PaintableWithLines (BlockContainer) [8,8 784x0] + PaintableBox (Box
.gfc) [8,8 800x600] + PaintableWithLines (BlockContainer
.grid-item) [408,8 400x600] + PaintableWithLines (BlockContainer
.abspos) [408,8 100x18] + TextPaintable (TextNode<#text>) + +SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto) + SC for BlockContainer [0,0 800x16] [children: 0] (z-index: auto) diff --git a/Tests/LibWeb/Layout/input/grid/abspos-item-contained-by-grid-area.html b/Tests/LibWeb/Layout/input/grid/abspos-item-contained-by-grid-area.html new file mode 100644 index 00000000000..a63948adab2 --- /dev/null +++ b/Tests/LibWeb/Layout/input/grid/abspos-item-contained-by-grid-area.html @@ -0,0 +1,19 @@ +
Hello
\ No newline at end of file