LibWeb: Position absolute block-level boxes below previous siblings

Whether an absbox is positioned below or to the right of its previous
sibling in an `InlineFormattingContext` is determined by the
display-outside value before blockification, so we store the
pre-blockification `display` value in `ComputedValues` to access it in
`InlineFormattingContext` and position the box accordingly.
This commit is contained in:
Pascal Pomper 2025-09-09 00:35:59 +02:00 committed by Andreas Kling
parent 7867fef8d7
commit 5b1eba7ac8
9 changed files with 67 additions and 4 deletions

View File

@ -132,6 +132,16 @@ void ComputedProperties::revert_property(PropertyID id, ComputedProperties const
set_property_inherited(id, style_for_revert.is_property_inherited(id) ? Inherited::Yes : Inherited::No);
}
Display ComputedProperties::display_before_box_type_transformation() const
{
return m_display_before_box_type_transformation;
}
void ComputedProperties::set_display_before_box_type_transformation(Display value)
{
m_display_before_box_type_transformation = value;
}
void ComputedProperties::set_animated_property(PropertyID id, NonnullRefPtr<StyleValue const> value, Inherited inherited)
{
m_animated_property_values.set(id, move(value));

View File

@ -186,6 +186,9 @@ public:
MixBlendMode mix_blend_mode() const;
Optional<FlyString> view_transition_name() const;
Display display_before_box_type_transformation() const;
void set_display_before_box_type_transformation(Display value);
static Vector<Transformation> transformations_for_style_value(StyleValue const& value);
Vector<Transformation> transformations() const;
TransformBox transform_box() const;
@ -279,6 +282,8 @@ private:
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> m_animated_property_values;
Display m_display_before_box_type_transformation { InitialValues::display() };
int m_math_depth { InitialValues::math_depth() };
RefPtr<Gfx::FontCascadeList const> m_font_list;
RefPtr<Gfx::Font const> m_first_available_computed_font;

View File

@ -487,6 +487,7 @@ public:
CSS::ContentData content() const { return m_noninherited.content; }
CSS::PointerEvents pointer_events() const { return m_inherited.pointer_events; }
CSS::Display display() const { return m_noninherited.display; }
CSS::Display display_before_box_type_transformation() const { return m_noninherited.display_before_box_type_transformation; }
Optional<int> const& z_index() const { return m_noninherited.z_index; }
Variant<LengthOrCalculated, NumberOrCalculated> tab_size() const { return m_inherited.tab_size; }
CSS::TextAlign text_align() const { return m_inherited.text_align; }
@ -756,6 +757,7 @@ protected:
CSS::Clear clear { InitialValues::clear() };
CSS::Clip clip { InitialValues::clip() };
CSS::Display display { InitialValues::display() };
CSS::Display display_before_box_type_transformation { InitialValues::display() };
Optional<int> z_index;
// FIXME: Store this as flags in a u8.
Vector<CSS::TextDecorationLine> text_decoration_line { InitialValues::text_decoration_line() };
@ -955,6 +957,7 @@ public:
void set_list_style_type(CSS::ListStyleType value) { m_inherited.list_style_type = move(value); }
void set_list_style_position(CSS::ListStylePosition value) { m_inherited.list_style_position = move(value); }
void set_display(CSS::Display value) { m_noninherited.display = value; }
void set_display_before_box_type_transformation(CSS::Display value) { m_noninherited.display_before_box_type_transformation = value; }
void set_backdrop_filter(CSS::Filter const& backdrop_filter) { m_noninherited.backdrop_filter = backdrop_filter; }
void set_filter(CSS::Filter const& filter) { m_noninherited.filter = filter; }
void set_border_bottom_left_radius(CSS::BorderRadiusData value)

View File

@ -2154,6 +2154,8 @@ void StyleComputer::compute_property_values(ComputedProperties& style) const
style.set_property(property_id, absolutized_value, is_inherited);
});
style.set_display_before_box_type_transformation(style.display());
}
void StyleComputer::resolve_effective_overflow_values(ComputedProperties& style) const

View File

@ -503,8 +503,10 @@ StaticPositionRect InlineFormattingContext::calculate_static_position_rect(Box c
CSSPixelPoint position;
if (auto const* sibling = box.previous_sibling()) {
// 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.
// We're calculating the position for an absolutely positioned box with a previous sibling in an IFC.
// We need to position the box...
// ...below the last fragment of this sibling, if the display-outside value (before box type transformation) is block.
// ...at the top right corner of the last fragment of this sibling otherwise.
LineBoxFragment const* last_fragment = nullptr;
auto const& cb_state = m_state.get(*sibling->containing_block());
for (auto const& line_box : cb_state.line_boxes) {
@ -514,8 +516,15 @@ StaticPositionRect InlineFormattingContext::calculate_static_position_rect(Box c
}
}
if (last_fragment) {
position.set_x(last_fragment->offset().x() + last_fragment->width());
position.set_y(last_fragment->offset().y());
if (box.display_before_box_type_transformation().is_block_outside()) {
// Display-outside value is block => position below
position.set_x(0);
position.set_y(last_fragment->offset().y() + last_fragment->height());
} else {
// Display-outside value is not block => position to the right
position.set_x(last_fragment->offset().x() + last_fragment->width());
position.set_y(last_fragment->offset().y());
}
}
}

View File

@ -571,6 +571,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
border_top_right_radius.as_border_radius().vertical_radius() });
}
computed_values.set_display(computed_style.display());
computed_values.set_display_before_box_type_transformation(computed_style.display_before_box_type_transformation());
computed_values.set_flex_direction(computed_style.flex_direction());
computed_values.set_flex_wrap(computed_style.flex_wrap());
@ -968,6 +969,15 @@ CSS::Display Node::display() const
return computed_values().display();
}
CSS::Display Node::display_before_box_type_transformation() const
{
if (!has_style()) {
return CSS::Display(CSS::DisplayOutside::Inline, CSS::DisplayInside::Flow);
}
return computed_values().display_before_box_type_transformation();
}
bool Node::is_inline() const
{
return display().is_inline_outside();

View File

@ -90,6 +90,7 @@ public:
virtual bool can_have_children() const { return true; }
CSS::Display display() const;
CSS::Display display_before_box_type_transformation() const;
bool is_inline() const;
bool is_inline_block() const;

View File

@ -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 <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: inline
frag 0 from TextNode start: 0, length: 3, rect: [8,8 27.15625x18] baseline: 13.796875
"foo"
TextNode <#text> (not painted)
BlockContainer <div> at [8,26] 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: [8,26 27.640625x18] baseline: 13.796875
"bar"
TextNode <#text> (not painted)
TextNode <#text> (not painted)
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x34]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x18]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<DIV>) [8,26 27.640625x18]
TextPaintable (TextNode<#text>)
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
SC for BlockContainer<HTML> [0,0 800x34] [children: 0] (z-index: auto)

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
foo <div style="position:absolute">bar</div>