diff --git a/Tests/LibWeb/Layout/expected/abspos-box-with-replaced-element.txt b/Tests/LibWeb/Layout/expected/abspos-box-with-replaced-element.txt index 76806becf02..1471d95229e 100644 --- a/Tests/LibWeb/Layout/expected/abspos-box-with-replaced-element.txt +++ b/Tests/LibWeb/Layout/expected/abspos-box-with-replaced-element.txt @@ -1,12 +1,12 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer at (1,1) content-size 798x0 [BFC] children: not-inline BlockContainer at (10,10) content-size 500x100 positioned [BFC] children: not-inline - BlockContainer at (261,11) content-size 248x28.46875 positioned [BFC] children: inline - frag 0 from ImageBox start: 0, length: 0, rect: [262,12 248x26.46875] baseline: 28.46875 - ImageBox at (262,12) content-size 248x26.46875 children: not-inline + BlockContainer at (261,12) content-size 248x28.46875 positioned [BFC] children: inline + frag 0 from ImageBox start: 0, length: 0, rect: [262,13 248x26.46875] baseline: 28.46875 + ImageBox at (262,13) content-size 248x26.46875 children: not-inline ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer) [0,0 800x2] overflow: [9,9 502x102] PaintableWithLines (BlockContainer) [9,9 502x102] - PaintableWithLines (BlockContainer
.image-container) [260,10 250x30.46875] overflow: [261,11 249x28.46875] - ImagePaintable (ImageBox) [261,11 250x28.46875] + PaintableWithLines (BlockContainer
.image-container) [260,11 250x30.46875] overflow: [261,12 249x28.46875] + ImagePaintable (ImageBox) [261,12 250x28.46875] diff --git a/Tests/LibWeb/Layout/expected/flex/abspos-flex-child-static-position-with-justify-content.txt b/Tests/LibWeb/Layout/expected/flex/abspos-flex-child-static-position-with-justify-content.txt index 9123ce5e909..9aa4843cfc3 100644 --- a/Tests/LibWeb/Layout/expected/flex/abspos-flex-child-static-position-with-justify-content.txt +++ b/Tests/LibWeb/Layout/expected/flex/abspos-flex-child-static-position-with-justify-content.txt @@ -207,8 +207,8 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer <(anonymous)> at (10,1808) content-size 780x0 children: inline TextNode <#text> Box at (11,1809) content-size 300x60 flex-container(column) [FFC] children: not-inline - BlockContainer
at (12,1810) content-size 150x50 positioned [BFC] children: inline - frag 0 from TextNode start: 0, length: 5, rect: [12,1810 37.109375x17] baseline: 13.296875 + BlockContainer
at (12,1818) content-size 150x50 positioned [BFC] children: inline + frag 0 from TextNode start: 0, length: 5, rect: [12,1818 37.109375x17] baseline: 13.296875 "right" TextNode <#text> BlockContainer <(anonymous)> at (10,1870) content-size 780x0 children: inline @@ -277,8 +277,8 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer <(anonymous)> at (10,2428) content-size 780x0 children: inline TextNode <#text> Box at (11,2429) content-size 300x60 flex-container(column-reverse) [FFC] children: not-inline - BlockContainer
at (12,2430) content-size 150x50 positioned [BFC] children: inline - frag 0 from TextNode start: 0, length: 5, rect: [12,2430 37.109375x17] baseline: 13.296875 + BlockContainer
at (12,2438) content-size 150x50 positioned [BFC] children: inline + frag 0 from TextNode start: 0, length: 5, rect: [12,2438 37.109375x17] baseline: 13.296875 "right" TextNode <#text> BlockContainer <(anonymous)> at (10,2490) content-size 780x0 children: inline @@ -405,7 +405,7 @@ ViewportPaintable (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x2500] TextPaintable (TextNode<#text>) PaintableWithLines (BlockContainer(anonymous)) [10,1808 780x0] PaintableBox (Box
.column.outer.right) [10,1808 302x62] - PaintableWithLines (BlockContainer
) [11,1809 152x52] + PaintableWithLines (BlockContainer
) [11,1817 152x52] TextPaintable (TextNode<#text>) PaintableWithLines (BlockContainer(anonymous)) [10,1870 780x0] PaintableBox (Box
.column.reverse.outer.start) [10,1870 302x62] @@ -445,6 +445,6 @@ ViewportPaintable (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x2500] TextPaintable (TextNode<#text>) PaintableWithLines (BlockContainer(anonymous)) [10,2428 780x0] PaintableBox (Box
.column.reverse.outer.right) [10,2428 302x62] - PaintableWithLines (BlockContainer
) [11,2429 152x52] + PaintableWithLines (BlockContainer
) [11,2437 152x52] TextPaintable (TextNode<#text>) PaintableWithLines (BlockContainer(anonymous)) [10,2490 780x0] diff --git a/Tests/LibWeb/Layout/expected/flex/abspos-flex-child-with-percentage-height.txt b/Tests/LibWeb/Layout/expected/flex/abspos-flex-child-with-percentage-height.txt new file mode 100644 index 00000000000..2fde36f68c0 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/flex/abspos-flex-child-with-percentage-height.txt @@ -0,0 +1,37 @@ +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: inline + BlockContainer at (8,8) content-size 200x300 positioned [BFC] children: not-inline + BlockContainer <(anonymous)> at (8,8) content-size 200x0 children: inline + TextNode <#text> + Box at (8,8) content-size 200x0 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (83,8) content-size 50x150 positioned [BFC] children: not-inline + BlockContainer <(anonymous)> at (83,8) content-size 50x0 children: inline + TextNode <#text> + BlockContainer at (83,8) content-size 50x0 children: not-inline + BlockContainer <(anonymous)> at (83,8) content-size 50x0 children: inline + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,8) content-size 200x0 children: inline + TextNode <#text> + BlockContainer
at (8,8) content-size 300x300 children: not-inline + BlockContainer <(anonymous)> at (8,308) content-size 200x0 children: inline + TextNode <#text> + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x16] + PaintableWithLines (BlockContainer) [8,8 784x0] + PaintableWithLines (BlockContainer
#containing-block) [8,8 200x300] overflow: [8,8 300x300] + PaintableWithLines (BlockContainer(anonymous)) [8,8 200x0] + PaintableBox (Box
#inner-flex) [8,8 200x0] overflow: [83,8 50x150] + PaintableWithLines (BlockContainer
) [83,8 50x150] + PaintableWithLines (BlockContainer(anonymous)) [83,8 50x0] + PaintableWithLines (BlockContainer) [83,8 50x0] + PaintableWithLines (BlockContainer(anonymous)) [83,8 50x0] + PaintableWithLines (BlockContainer(anonymous)) [8,8 200x0] + PaintableWithLines (BlockContainer
) [8,8 300x300] + PaintableWithLines (BlockContainer(anonymous)) [8,308 200x0] diff --git a/Tests/LibWeb/Layout/expected/grid/abspos-with-grid-container-as-parent.txt b/Tests/LibWeb/Layout/expected/grid/abspos-with-grid-container-as-parent.txt new file mode 100644 index 00000000000..68b12a5bf00 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/abspos-with-grid-container-as-parent.txt @@ -0,0 +1,59 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x0 [BFC] children: not-inline + BlockContainer at (8,8) content-size 400x275 positioned [BFC] children: not-inline + BlockContainer at (8,8) content-size 100x100 children: inline + frag 0 from TextNode start: 0, length: 5, rect: [8,8 36.84375x17] baseline: 13.296875 + "hello" + TextNode <#text> + BlockContainer <(anonymous)> at (8,108) content-size 400x0 children: inline + TextNode <#text> + BlockContainer at (8,108) content-size 400x75 [BFC] children: inline + TextNode <#text> + BlockContainer at (8,8) content-size 60.890625x25 positioned [BFC] children: inline + frag 0 from TextNode start: 0, length: 8, rect: [8,8 60.890625x17] baseline: 13.296875 + "top left" + TextNode <#text> + TextNode <#text> + BlockContainer at (336.25,8) content-size 71.75x25 positioned [BFC] children: inline + frag 0 from TextNode start: 0, length: 9, rect: [336.25,8 71.75x17] baseline: 13.296875 + "top right" + TextNode <#text> + TextNode <#text> + BlockContainer at (8,258) content-size 90.359375x25 positioned [BFC] children: inline + frag 0 from TextNode start: 0, length: 11, rect: [8,258 90.359375x17] baseline: 13.296875 + "bottom left" + TextNode <#text> + TextNode <#text> + BlockContainer at (306.78125,258) content-size 101.21875x25 positioned [BFC] children: inline + frag 0 from TextNode start: 0, length: 12, rect: [306.78125,258 101.21875x17] baseline: 13.296875 + "bottom right" + TextNode <#text> + TextNode <#text> + BlockContainer <(anonymous)> at (8,183) content-size 400x0 children: inline + TextNode <#text> + BlockContainer at (8,183) content-size 100x100 children: inline + frag 0 from TextNode start: 0, length: 5, rect: [8,183 36.84375x17] baseline: 13.296875 + "hello" + TextNode <#text> + BlockContainer <(anonymous)> at (8,283) content-size 400x0 children: inline + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x0] overflow: [8,8 400x275] + PaintableWithLines (BlockContainer) [8,8 400x275] + PaintableWithLines (BlockContainer
#fill) [8,8 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [8,108 400x0] + PaintableWithLines (BlockContainer
#grid) [8,108 400x75] + PaintableWithLines (BlockContainer
#grid-item-abspos) [8,8 60.890625x25] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#grid-item-abspos) [336.25,8 71.75x25] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#grid-item-abspos) [8,258 90.359375x25] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#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.