#grid-item-abspos) [306.78125,258 101.21875x25]
+ TextPaintable (TextNode<#text>)
+ PaintableWithLines (BlockContainer(anonymous)) [8,183 400x0]
+ PaintableWithLines (BlockContainer
#fill) [8,183 100x100]
+ TextPaintable (TextNode<#text>)
+ PaintableWithLines (BlockContainer(anonymous)) [8,283 400x0]
diff --git a/Tests/LibWeb/Layout/input/flex/abspos-flex-child-with-percentage-height.html b/Tests/LibWeb/Layout/input/flex/abspos-flex-child-with-percentage-height.html
new file mode 100644
index 00000000000..8b77cc451f2
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/flex/abspos-flex-child-with-percentage-height.html
@@ -0,0 +1,26 @@
+
+
+
diff --git a/Tests/LibWeb/Layout/input/grid/abspos-with-grid-container-as-parent.html b/Tests/LibWeb/Layout/input/grid/abspos-with-grid-container-as-parent.html
new file mode 100644
index 00000000000..78fbdc60966
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/grid/abspos-with-grid-container-as-parent.html
@@ -0,0 +1,32 @@
+
+
hello
+
+
top left
+
top right
+
bottom left
+
bottom right
+
+
hello
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp
index 1d0a6785c9f..d3c63bbd2f8 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Document.cpp
@@ -1131,6 +1131,26 @@ void Document::update_layout()
}
}
+ // Assign each box that establishes a formatting context a list of absolutely positioned children it should take care of during layout
+ m_layout_root->for_each_in_inclusive_subtree([&](auto& child) {
+ if (!child.is_absolutely_positioned())
+ return TraversalDecision::Continue;
+ if (auto* containing_block = child.containing_block()) {
+ auto* closest_box_that_establishes_formatting_context = containing_block;
+ while (closest_box_that_establishes_formatting_context) {
+ if (closest_box_that_establishes_formatting_context == m_layout_root)
+ break;
+ if (Layout::FormattingContext::formatting_context_type_created_by_box(*closest_box_that_establishes_formatting_context).has_value()) {
+ break;
+ }
+ closest_box_that_establishes_formatting_context = closest_box_that_establishes_formatting_context->containing_block();
+ }
+ VERIFY(closest_box_that_establishes_formatting_context);
+ closest_box_that_establishes_formatting_context->add_contained_abspos_child(child);
+ }
+ return TraversalDecision::Continue;
+ });
+
Layout::LayoutState layout_state;
{
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
index 485c17d2998..6dccbf6358c 100644
--- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
@@ -110,8 +110,9 @@ void BlockFormattingContext::parent_context_did_dimension_child_root_box()
if (m_layout_mode == LayoutMode::Normal) {
// We can also layout absolutely positioned boxes within this BFC.
- for (auto& box : m_absolutely_positioned_boxes) {
- auto& cb_state = m_state.get(*box->containing_block());
+ for (auto& child : root().contained_abspos_children()) {
+ auto& box = verify_cast
(*child);
+ auto& cb_state = m_state.get(*box.containing_block());
auto available_width = AvailableSize::make_definite(cb_state.content_width() + cb_state.padding_left + cb_state.padding_right);
auto available_height = AvailableSize::make_definite(cb_state.content_height() + cb_state.padding_top + cb_state.padding_bottom);
layout_absolutely_positioned_element(box, AvailableSpace(available_width, available_height));
@@ -585,7 +586,7 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
if (box.is_absolutely_positioned()) {
box_state.vertical_offset_of_parent_block_container = m_y_offset_of_current_block_container.value();
- m_absolutely_positioned_boxes.append(box);
+ box_state.set_static_position_rect(calculate_static_position_rect(box));
return;
}
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
index 09d3a1a0a71..0b183451252 100644
--- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
@@ -39,8 +39,6 @@ public:
void compute_height(Box const&, AvailableSpace const&);
- void add_absolutely_positioned_box(Box const& box) { m_absolutely_positioned_boxes.append(box); }
-
SpaceUsedAndContainingMarginForFloats space_used_and_containing_margin_for_floats(CSSPixels y) const;
[[nodiscard]] SpaceUsedByFloats intrusion_by_floats_into_box(Box const&, CSSPixels y_in_box) const;
[[nodiscard]] SpaceUsedByFloats intrusion_by_floats_into_box(LayoutState::UsedValues const&, CSSPixels y_in_box) const;
@@ -170,8 +168,6 @@ private:
FloatSideData m_left_floats;
FloatSideData m_right_floats;
- Vector> m_absolutely_positioned_boxes;
-
bool m_was_notified_after_parent_dimensioned_my_root_box { false };
};
diff --git a/Userland/Libraries/LibWeb/Layout/Box.cpp b/Userland/Libraries/LibWeb/Layout/Box.cpp
index b1aeb1a691b..844cd423f38 100644
--- a/Userland/Libraries/LibWeb/Layout/Box.cpp
+++ b/Userland/Libraries/LibWeb/Layout/Box.cpp
@@ -28,7 +28,13 @@ Box::~Box()
{
}
-// https://www.w3.org/TR/css-overflow-3/#overflow-control
+void Box::visit_edges(Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(m_contained_abspos_children);
+}
+
+// https://www.w37.org/TR/css-overflow-3/#overflow-control
static bool overflow_value_makes_box_a_scroll_container(CSS::Overflow overflow)
{
switch (overflow) {
diff --git a/Userland/Libraries/LibWeb/Layout/Box.h b/Userland/Libraries/LibWeb/Layout/Box.h
index 7bfedc0d963..7278505de70 100644
--- a/Userland/Libraries/LibWeb/Layout/Box.h
+++ b/Userland/Libraries/LibWeb/Layout/Box.h
@@ -54,6 +54,11 @@ public:
bool is_user_scrollable() const;
+ void add_contained_abspos_child(JS::NonnullGCPtr child) { m_contained_abspos_children.append(child); }
+ Vector> const& contained_abspos_children() const { return m_contained_abspos_children; }
+
+ virtual void visit_edges(Cell::Visitor&) override;
+
protected:
Box(DOM::Document&, DOM::Node*, NonnullRefPtr);
Box(DOM::Document&, DOM::Node*, NonnullOwnPtr);
@@ -64,6 +69,8 @@ private:
Optional m_natural_width;
Optional m_natural_height;
Optional m_natural_aspect_ratio;
+
+ Vector> m_contained_abspos_children;
};
template<>
diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
index 5f9e163caa1..c22f13d69e5 100644
--- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
@@ -186,13 +186,18 @@ void FlexFormattingContext::parent_context_did_dimension_child_root_box()
flex_container().for_each_child_of_type([&](Layout::Box& box) {
if (box.is_absolutely_positioned()) {
- auto& cb_state = m_state.get(*box.containing_block());
- auto available_width = AvailableSize::make_definite(cb_state.content_width() + cb_state.padding_left + cb_state.padding_right);
- auto available_height = AvailableSize::make_definite(cb_state.content_height() + cb_state.padding_top + cb_state.padding_bottom);
- layout_absolutely_positioned_element(box, AvailableSpace(available_width, available_height));
+ m_state.get_mutable(box).set_static_position_rect(calculate_static_position_rect(box));
}
return IterationDecision::Continue;
});
+
+ for (auto& child : flex_container().contained_abspos_children()) {
+ auto& box = verify_cast(*child);
+ auto& cb_state = m_state.get(*box.containing_block());
+ auto available_width = AvailableSize::make_definite(cb_state.content_width() + cb_state.padding_left + cb_state.padding_right);
+ auto available_height = AvailableSize::make_definite(cb_state.content_height() + cb_state.padding_top + cb_state.padding_bottom);
+ layout_absolutely_positioned_element(box, AvailableSpace(available_width, available_height));
+ }
}
// https://www.w3.org/TR/css-flexbox-1/#flex-direction-property
@@ -2129,35 +2134,12 @@ void FlexFormattingContext::handle_align_content_stretch()
}
// https://drafts.csswg.org/css-flexbox-1/#abspos-items
-CSSPixelPoint FlexFormattingContext::calculate_static_position(Box const& box) const
+StaticPositionRect FlexFormattingContext::calculate_static_position_rect(Box const& box) const
{
// The cross-axis edges of the static-position rectangle of an absolutely-positioned child
// of a flex container are the content edges of the flex container.
- CSSPixels cross_offset = 0;
- CSSPixels half_line_size = inner_cross_size(m_flex_container_state) / 2;
-
- auto cross_to_px = [&](CSS::LengthPercentage const& length_percentage) -> CSSPixels {
- return length_percentage.to_px(box, m_flex_container_state.content_width());
- };
-
- auto main_to_px = [&](CSS::LengthPercentage const& length_percentage) -> CSSPixels {
- return length_percentage.to_px(box, m_flex_container_state.content_width());
- };
-
- auto const& box_state = m_state.get(box);
- CSSPixels cross_margin_before = is_row_layout() ? cross_to_px(box.computed_values().margin().top()) : cross_to_px(box.computed_values().margin().left());
- CSSPixels cross_margin_after = is_row_layout() ? cross_to_px(box.computed_values().margin().bottom()) : cross_to_px(box.computed_values().margin().right());
- CSSPixels cross_border_before = is_row_layout() ? box.computed_values().border_top().width : box.computed_values().border_left().width;
- CSSPixels cross_border_after = is_row_layout() ? box.computed_values().border_bottom().width : box.computed_values().border_right().width;
- CSSPixels cross_padding_before = is_row_layout() ? cross_to_px(box.computed_values().padding().top()) : cross_to_px(box.computed_values().padding().left());
- CSSPixels cross_padding_after = is_row_layout() ? cross_to_px(box.computed_values().padding().bottom()) : cross_to_px(box.computed_values().padding().right());
- CSSPixels main_margin_before = is_row_layout() ? main_to_px(box.computed_values().margin().left()) : main_to_px(box.computed_values().margin().top());
- CSSPixels main_margin_after = is_row_layout() ? main_to_px(box.computed_values().margin().right()) : main_to_px(box.computed_values().margin().bottom());
- CSSPixels main_border_before = is_row_layout() ? box.computed_values().border_left().width : box.computed_values().border_top().width;
- CSSPixels main_border_after = is_row_layout() ? box.computed_values().border_right().width : box.computed_values().border_bottom().width;
- CSSPixels main_padding_before = is_row_layout() ? main_to_px(box.computed_values().padding().left()) : main_to_px(box.computed_values().padding().top());
- CSSPixels main_padding_after = is_row_layout() ? main_to_px(box.computed_values().padding().right()) : main_to_px(box.computed_values().padding().bottom());
+ StaticPositionRect::Alignment cross_axis_alignment = StaticPositionRect::Alignment::Start;
switch (alignment_for_item(box)) {
case CSS::AlignItems::Baseline:
// FIXME: Implement this
@@ -2167,67 +2149,64 @@ CSSPixelPoint FlexFormattingContext::calculate_static_position(Box const& box) c
case CSS::AlignItems::SelfStart:
case CSS::AlignItems::Stretch:
case CSS::AlignItems::Normal:
- cross_offset = -half_line_size;
+ cross_axis_alignment = StaticPositionRect::Alignment::Start;
break;
case CSS::AlignItems::End:
case CSS::AlignItems::SelfEnd:
case CSS::AlignItems::FlexEnd:
- cross_offset = half_line_size - inner_cross_size(box_state) - (cross_margin_before + cross_margin_after) - (cross_border_before + cross_border_after) - (cross_padding_before + cross_padding_after);
+ cross_axis_alignment = StaticPositionRect::Alignment::End;
break;
case CSS::AlignItems::Center:
- cross_offset = -((inner_cross_size(box_state) + cross_margin_after + cross_margin_before + cross_border_before + cross_border_after + cross_padding_before + cross_padding_after) / 2);
+ cross_axis_alignment = StaticPositionRect::Alignment::Center;
break;
default:
break;
}
- cross_offset += inner_cross_size(m_flex_container_state) / 2;
-
// The main-axis edges of the static-position rectangle are where the margin edges of the child
// would be positioned if it were the sole flex item in the flex container,
// assuming both the child and the flex container were fixed-size boxes of their used size.
// (For this purpose, auto margins are treated as zero.
- bool pack_from_end = true;
- CSSPixels main_offset = 0;
+ StaticPositionRect::Alignment main_axis_alignment = StaticPositionRect::Alignment::Start;
switch (flex_container().computed_values().justify_content()) {
case CSS::JustifyContent::Start:
case CSS::JustifyContent::Left:
- pack_from_end = false;
+ main_axis_alignment = StaticPositionRect::Alignment::Start;
break;
case CSS::JustifyContent::Stretch:
case CSS::JustifyContent::Normal:
case CSS::JustifyContent::FlexStart:
case CSS::JustifyContent::SpaceBetween:
- pack_from_end = is_direction_reverse();
+ main_axis_alignment = is_direction_reverse() ? StaticPositionRect::Alignment::End : StaticPositionRect::Alignment::Start;
break;
case CSS::JustifyContent::End:
- pack_from_end = true;
+ main_axis_alignment = StaticPositionRect::Alignment::End;
break;
case CSS::JustifyContent::Right:
- pack_from_end = is_row_layout();
+ main_axis_alignment = StaticPositionRect::Alignment::End;
break;
case CSS::JustifyContent::FlexEnd:
- pack_from_end = !is_direction_reverse();
+ main_axis_alignment = !is_direction_reverse() ? StaticPositionRect::Alignment::End : StaticPositionRect::Alignment::Start;
break;
case CSS::JustifyContent::Center:
case CSS::JustifyContent::SpaceAround:
case CSS::JustifyContent::SpaceEvenly:
- pack_from_end = false;
- main_offset = (inner_main_size(m_flex_container_state) - inner_main_size(box_state) - main_margin_before - main_margin_after - main_border_before - main_border_after - main_padding_before - main_padding_after) / 2;
+ main_axis_alignment = StaticPositionRect::Alignment::Center;
break;
}
- if (pack_from_end)
- main_offset += inner_main_size(m_flex_container_state) - inner_main_size(box_state) - main_margin_before - main_margin_after - main_border_before - main_border_after - main_padding_before - main_padding_after;
-
- auto static_position_offset = is_row_layout() ? CSSPixelPoint { main_offset, cross_offset } : CSSPixelPoint { cross_offset, main_offset };
-
auto absolute_position_of_flex_container = absolute_content_rect(flex_container()).location();
auto absolute_position_of_abspos_containing_block = absolute_content_rect(*box.containing_block()).location();
- auto diff = absolute_position_of_flex_container - absolute_position_of_abspos_containing_block;
- return static_position_offset + diff;
+ auto flex_container_width = is_row_layout() ? inner_main_size(m_flex_container_state) : inner_cross_size(m_flex_container_state);
+ auto flex_container_height = is_row_layout() ? inner_cross_size(m_flex_container_state) : inner_main_size(m_flex_container_state);
+
+ StaticPositionRect static_position_rect;
+ static_position_rect.rect = { absolute_position_of_flex_container - absolute_position_of_abspos_containing_block, { flex_container_width, flex_container_height } };
+ static_position_rect.horizontal_alignment = is_row_layout() ? main_axis_alignment : cross_axis_alignment;
+ static_position_rect.vertical_alignment = is_row_layout() ? cross_axis_alignment : main_axis_alignment;
+ return static_position_rect;
}
double FlexFormattingContext::FlexLine::sum_of_flex_factor_of_unfrozen_items() const
diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
index 041e6353ad2..24a77e833b8 100644
--- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
@@ -24,7 +24,7 @@ public:
Box const& flex_container() const { return context_box(); }
- virtual CSSPixelPoint calculate_static_position(Box const&) const override;
+ virtual StaticPositionRect calculate_static_position_rect(Box const&) const override;
private:
[[nodiscard]] bool should_treat_main_size_as_auto(Box const&) const;
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
index 6b9eb1d145b..0b485b46239 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
@@ -716,7 +716,7 @@ void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_ele
width = CSS::Length::make_px(content_width);
m_state.get_mutable(box).set_content_width(content_width);
- auto static_position = calculate_static_position(box);
+ auto static_position = m_state.get(box).static_position();
left = static_position.x();
right = solve_for_right();
@@ -774,7 +774,7 @@ void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_ele
// Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
else if (computed_left.is_auto() && computed_right.is_auto() && !width.is_auto()) {
// FIXME: Check direction
- auto static_position = calculate_static_position(box);
+ auto static_position = m_state.get(box).static_position();
left = static_position.x();
right = solve_for_right();
}
@@ -863,7 +863,7 @@ void FormattingContext::compute_width_for_absolutely_positioned_replaced_element
auto margin_left = computed_values.margin().left();
auto right = computed_values.inset().right();
auto margin_right = computed_values.margin().right();
- auto static_position = calculate_static_position(box);
+ auto static_position = m_state.get(box).static_position();
auto to_px = [&](const CSS::LengthPercentage& l) {
return l.to_px(box, width_of_containing_block);
@@ -1034,7 +1034,7 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
auto constrained_height = apply_min_max_height_constraints(height);
m_state.get_mutable(box).set_content_height(constrained_height.to_px(box));
- auto static_position = calculate_static_position(box);
+ auto static_position = m_state.get(box).static_position();
top = CSS::Length::make_px(static_position.y());
solve_for_bottom();
@@ -1089,7 +1089,7 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
// 2. If top and bottom are auto and height is not auto,
else if (top.is_auto() && bottom.is_auto() && !height.is_auto()) {
// then set top to the static position,
- top = CSS::Length::make_px(calculate_static_position(box).y());
+ top = CSS::Length::make_px(m_state.get(box).static_position().y());
// then solve for bottom.
solve_for_bottom();
@@ -1176,7 +1176,7 @@ CSSPixelRect FormattingContext::content_box_rect_in_static_position_ancestor_coo
}
// https://www.w3.org/TR/css-position-3/#staticpos-rect
-CSSPixelPoint FormattingContext::calculate_static_position(Box const& box) const
+StaticPositionRect FormattingContext::calculate_static_position_rect(Box const& box) const
{
// NOTE: This is very ad-hoc.
// The purpose of this function is to calculate the approximate position that `box`
@@ -1216,7 +1216,9 @@ CSSPixelPoint FormattingContext::calculate_static_position(Box const& box) const
y = box_state.vertical_offset_of_parent_block_container;
}
auto offset_to_static_parent = content_box_rect_in_static_position_ancestor_coordinate_space(box, *box.containing_block());
- return offset_to_static_parent.location().translated(x, y);
+ StaticPositionRect static_position_rect;
+ static_position_rect.rect = { offset_to_static_parent.location().translated(x, y), { 0, 0 } };
+ return static_position_rect;
}
void FormattingContext::layout_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space)
@@ -1259,7 +1261,7 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box, Ava
CSSPixelPoint used_offset;
- auto static_position = calculate_static_position(box);
+ auto static_position = m_state.get(box).static_position();
if (box.computed_values().inset().top().is_auto() && box.computed_values().inset().bottom().is_auto()) {
used_offset.set_y(static_position.y());
@@ -1300,7 +1302,7 @@ void FormattingContext::compute_height_for_absolutely_positioned_replaced_elemen
auto margin_top = computed_values.margin().top();
auto bottom = computed_values.inset().bottom();
auto margin_bottom = computed_values.margin().bottom();
- auto static_position = calculate_static_position(box);
+ auto static_position = m_state.get(box).static_position();
auto to_px = [&](const CSS::LengthPercentage& l) {
return l.to_px(box, height_of_containing_block);
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
index cf64a952d16..a95d15ad692 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
@@ -107,7 +107,7 @@ public:
[[nodiscard]] CSSPixels calculate_stretch_fit_width(Box const&, AvailableSize const&) const;
[[nodiscard]] CSSPixels calculate_stretch_fit_height(Box const&, AvailableSize const&) const;
- virtual CSSPixelPoint calculate_static_position(Box const&) const;
+ virtual StaticPositionRect calculate_static_position_rect(Box const&) const;
bool can_skip_is_anonymous_text_run(Box&);
void compute_inset(NodeWithStyleAndBoxModelMetrics const&);
diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
index 09102dec19d..3ce8a998377 100644
--- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
@@ -2010,13 +2010,18 @@ void GridFormattingContext::parent_context_did_dimension_child_root_box()
grid_container().for_each_child_of_type([&](Layout::Box& box) {
if (box.is_absolutely_positioned()) {
- auto& cb_state = m_state.get(*box.containing_block());
- auto available_width = AvailableSize::make_definite(cb_state.content_width() + cb_state.padding_left + cb_state.padding_right);
- auto available_height = AvailableSize::make_definite(cb_state.content_height() + cb_state.padding_top + cb_state.padding_bottom);
- layout_absolutely_positioned_element(box, AvailableSpace(available_width, available_height));
+ m_state.get_mutable(box).set_static_position_rect(calculate_static_position_rect(box));
}
return IterationDecision::Continue;
});
+
+ for (auto& child : grid_container().contained_abspos_children()) {
+ auto& box = verify_cast(*child);
+ auto& cb_state = m_state.get(*box.containing_block());
+ auto available_width = AvailableSize::make_definite(cb_state.content_width() + cb_state.padding_left + cb_state.padding_right);
+ auto available_height = AvailableSize::make_definite(cb_state.content_height() + cb_state.padding_top + cb_state.padding_bottom);
+ layout_absolutely_positioned_element(box, AvailableSpace(available_width, available_height));
+ }
}
void GridFormattingContext::determine_intrinsic_size_of_grid_container(AvailableSpace const& available_space)
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
index d681fe3bd5a..2fcc4153d72 100644
--- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
@@ -305,8 +305,11 @@ void InlineFormattingContext::generate_line_boxes()
break;
}
case InlineLevelIterator::Item::Type::AbsolutelyPositionedElement:
- if (is(*item.node))
- parent().add_absolutely_positioned_box(static_cast(*item.node));
+ if (is(*item.node)) {
+ auto const& box = static_cast(*item.node);
+ auto& box_state = m_state.get_mutable(box);
+ box_state.set_static_position_rect(calculate_static_position_rect(box));
+ }
break;
case InlineLevelIterator::Item::Type::FloatingElement:
diff --git a/Userland/Libraries/LibWeb/Layout/LayoutState.h b/Userland/Libraries/LibWeb/Layout/LayoutState.h
index 0bab0f50ef3..80d2db7475c 100644
--- a/Userland/Libraries/LibWeb/Layout/LayoutState.h
+++ b/Userland/Libraries/LibWeb/Layout/LayoutState.h
@@ -25,6 +25,35 @@ enum class SizeConstraint {
class AvailableSize;
class AvailableSpace;
+// https://www.w3.org/TR/css-position-3/#static-position-rectangle
+struct StaticPositionRect {
+ enum class Alignment {
+ Start,
+ Center,
+ End,
+ };
+
+ CSSPixelRect rect;
+ Alignment horizontal_alignment { Alignment::Start };
+ Alignment vertical_alignment { Alignment::Start };
+
+ CSSPixelPoint aligned_position_for_box_with_size(CSSPixelSize const& size) const
+ {
+ CSSPixelPoint position = rect.location();
+ if (horizontal_alignment == Alignment::Center)
+ position.set_x(position.x() + (rect.width() - size.width()) / 2);
+ else if (horizontal_alignment == Alignment::End)
+ position.set_x(position.x() + rect.width() - size.width());
+
+ if (vertical_alignment == Alignment::Center)
+ position.set_y(position.y() + (rect.height() - size.height()) / 2);
+ else if (vertical_alignment == Alignment::End)
+ position.set_y(position.y() + rect.height() - size.height());
+
+ return position;
+ }
+};
+
struct LayoutState {
LayoutState()
: m_root(*this)
@@ -145,6 +174,15 @@ struct LayoutState {
void set_grid_template_rows(RefPtr used_values_for_grid_template_rows) { m_grid_template_rows = move(used_values_for_grid_template_rows); }
auto const& grid_template_rows() const { return m_grid_template_rows; }
+ void set_static_position_rect(StaticPositionRect const& static_position_rect) { m_static_position_rect = static_position_rect; }
+ CSSPixelPoint static_position() const
+ {
+ CSSPixelSize size;
+ size.set_width(content_width() + padding_left + padding_right + border_left + border_right + margin_left + margin_right);
+ size.set_height(content_height() + padding_top + padding_bottom + border_top + border_bottom + margin_top + margin_bottom);
+ return m_static_position_rect->aligned_position_for_box_with_size(size);
+ }
+
private:
AvailableSize available_width_inside() const;
AvailableSize available_height_inside() const;
@@ -175,6 +213,8 @@ struct LayoutState {
RefPtr m_grid_template_columns;
RefPtr m_grid_template_rows;
+
+ Optional m_static_position_rect;
};
// Commits the used values produced by layout and builds a paintable tree.