From c8d24d496625d9a10374a774caaa50e8065fe8f5 Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Thu, 28 Aug 2025 00:03:09 +0200 Subject: [PATCH] LibWeb: Correctly position absolute inline boxes in last line If we were calculating the static position for an absolutely positioned inline box that resides in the last line of its containing block, we would not have yet provided the fragments in that line with their final positions. Additionally, we would always move the box beneath the fragment, which was incorrect. Fixes #5867. --- .../LibWeb/Layout/InlineFormattingContext.cpp | 30 +++++++------------ .../block-and-inline/abspos-inline.txt | 21 +++++++++++++ .../input/block-and-inline/abspos-inline.html | 2 ++ 3 files changed, 33 insertions(+), 20 deletions(-) create mode 100644 Tests/LibWeb/Layout/expected/block-and-inline/abspos-inline.txt create mode 100644 Tests/LibWeb/Layout/input/block-and-inline/abspos-inline.html diff --git a/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Libraries/LibWeb/Layout/InlineFormattingContext.cpp index 5920c4db77b..9ddd9edc241 100644 --- a/Libraries/LibWeb/Layout/InlineFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/InlineFormattingContext.cpp @@ -443,12 +443,12 @@ void InlineFormattingContext::generate_line_boxes() } } + line_builder.update_last_line(); + for (auto* box : absolute_boxes) { auto& box_state = m_state.get_mutable(*box); box_state.set_static_position_rect(calculate_static_position_rect(*box)); } - - line_builder.update_last_line(); } bool InlineFormattingContext::any_floats_intrude_at_block_offset(CSSPixels block_offset) const @@ -499,20 +499,13 @@ void InlineFormattingContext::set_vertical_float_clearance(CSSPixels vertical_fl StaticPositionRect InlineFormattingContext::calculate_static_position_rect(Box const& box) const { - CSSPixels x = 0; - CSSPixels y = 0; - VERIFY(box.parent()); VERIFY(box.parent()->children_are_inline()); - // We're an abspos box with inline siblings. This is gonna get messy! + + CSSPixelPoint position; if (auto const* sibling = box.previous_sibling()) { - // Hard case: there's a previous sibling. This means there's already inline content - // preceding the hypothetical static position of `box` within its containing block. - // If we had been position:static, that inline content would have been wrapped in - // anonymous block box, so now we get to imagine what the world might have looked like - // in that scenario.. - // Basically, we find its last associated line box fragment and place `box` under it. - // FIXME: I'm 100% sure this can be smarter, better and faster. + // We're calculating the position for an absolutely positioned box with a previous sibling in an IFC. We need to + // position the box at the top right corner of the last fragment of this sibling. LineBoxFragment const* last_fragment = nullptr; auto const& cb_state = m_state.get(*sibling->containing_block()); for (auto const& line_box : cb_state.line_boxes) { @@ -522,15 +515,12 @@ StaticPositionRect InlineFormattingContext::calculate_static_position_rect(Box c } } if (last_fragment) { - x = last_fragment->offset().x() + last_fragment->width(); - y = last_fragment->offset().y() + last_fragment->height(); + position.set_x(last_fragment->offset().x() + last_fragment->width()); + position.set_y(last_fragment->offset().y()); } - } else { - // Easy case: no previous sibling, we're at the top of the containing block. } - StaticPositionRect static_position_rect; - static_position_rect.rect = { { x, y }, { 0, 0 } }; - return static_position_rect; + + return { .rect = { position, { 0, 0 } } }; } } diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/abspos-inline.txt b/Tests/LibWeb/Layout/expected/block-and-inline/abspos-inline.txt new file mode 100644 index 00000000000..a2a7159c7da --- /dev/null +++ b/Tests/LibWeb/Layout/expected/block-and-inline/abspos-inline.txt @@ -0,0 +1,21 @@ +Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 34 0+0+0] [BFC] children: not-inline + BlockContainer at [8,8] [8+0+0 784 0+0+8] [8+0+0 18 0+0+8] children: inline + frag 0 from TextNode start: 0, length: 3, rect: [8,8 27.15625x18] baseline: 13.796875 + "foo" + TextNode <#text> (not painted) + BlockContainer at [35.15625,8] positioned [0+0+0 27.640625 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline + frag 0 from TextNode start: 0, length: 3, rect: [35.15625,8 27.640625x18] baseline: 13.796875 + "bar" + TextNode <#text> (not painted) + TextNode <#text> (not painted) + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x34] + PaintableWithLines (BlockContainer) [8,8 784x18] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer) [35.15625,8 27.640625x18] + TextPaintable (TextNode<#text>) + +SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto) + SC for BlockContainer [0,0 800x34] [children: 0] (z-index: auto) diff --git a/Tests/LibWeb/Layout/input/block-and-inline/abspos-inline.html b/Tests/LibWeb/Layout/input/block-and-inline/abspos-inline.html new file mode 100644 index 00000000000..63c0241aa86 --- /dev/null +++ b/Tests/LibWeb/Layout/input/block-and-inline/abspos-inline.html @@ -0,0 +1,2 @@ + +foo bar