Files
ladybird/Tests/LibWeb/Layout/expected/abspos-pseudo-element-with-inline-as-abspos-containing-block.txt
Andreas Kling dc79259970 LibWeb: Support inline elements as abspos containing blocks
CSS allows inline elements with `position: relative` (or other
containing-block-establishing properties) to serve as the containing
block for their absolutely positioned descendants. However, our layout
system stores containing blocks as `Box*`, which cannot represent
inline elements (they are `InlineNode`, not `Box`).

This patch adds a workaround: when computing containing blocks, we
also check if there's an inline element between the abspos element
and its Box containing block that should actually be the CSS
containing block. If found, we store it in a new member called
`m_inline_containing_block_if_applicable` and use it during abspos
layout to:

1. Compute the inline's fragment bounding box as the containing
   block rectangle (including padding, per CSS spec)
2. Resolve percentage-based insets against the inline's dimensions
3. Position the abspos element relative to the inline's location

Some details to be aware of:

- The inline containing block search happens in the function
  `recompute_containing_block()` by walking DOM ancestors (not layout
  tree ancestors, since the layout tree restructures blocks inside
  inlines as siblings)

- For pseudo-elements like `::after`, we start the search from the
  generating element itself, since it may be the inline containing
  block

- Fragment offsets are relative to their block container, so we
  translate the computed rect to the abspos element's containing
  block coordinate system by accumulating offsets up the ancestor
  chain

- When the abspos element uses static position (auto insets), we
  don't apply the inline rect translation since static position is
  already computed in the correct coordinate system

Long term, we want to refactor our "containing block" concept to
map more cleanly to the spec concept. That means turning it into
a rectangle instead of the box this rectangle was derived from.
That's an invasive change for another day though.
2026-01-19 17:34:46 +01:00

25 lines
1.5 KiB
Plaintext

Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline
BlockContainer <html> at [0,0] [0+0+0 800 0+0+0] [0+0+0 34 0+0+0] [BFC] children: not-inline
BlockContainer <body> at [8,8] [8+0+0 784 0+0+8] [8+0+0 18 0+0+8] children: not-inline
BlockContainer <(anonymous)> at [8,8] [0+0+0 784 0+0+0] [0+0+0 18 0+0+0] children: inline
InlineNode <span> at [8,8] [0+0+0 74.125 16+0+0] [0+0+0 18 0+0+0]
frag 0 from TextNode start: 0, length: 8, rect: [8,8 74.125x18] baseline: 13.796875
"Features"
TextNode <#text> (not painted)
BlockContainer <(anonymous)> at [85.625,16.09375] positioned [0+0+0 8 0+0+0] [0+0+0 5 0+0+0] [BFC] children: inline
TextNode <#text> (not painted)
BlockContainer <(anonymous)> at [8,26] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: inline
TextNode <#text> (not painted)
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x34]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x18]
PaintableWithLines (BlockContainer(anonymous)) [8,8 784x18]
PaintableWithLines (InlineNode<SPAN>) [8,8 90.125x18]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [85.625,16.09375 8x5]
PaintableWithLines (BlockContainer(anonymous)) [8,26 784x0]
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
SC for BlockContainer<HTML> [0,0 800x34] [children: 0] (z-index: auto)