LibWeb: Make FC of containing block responsible for abspos layout

Before this change, a formatting context was responsible for layout of
absolutely positioned boxes whose FC root box was their parent (either
directly or indirectly). This only worked correctly when the containing
block of the absolutely positioned child did not escape the FC root.
This is because the width and height of an absolutely positioned box are
resolved based on the size of its containing block, so we needed to
ensure that the containing block's layout was completed before laying
out an absolutely positioned box.

With this change, the layout of absolutely positioned boxes is delayed
until the FC responsible for the containing block's layout is complete.
This has affected the way we calculate the static position. It is no
longer possible to ask the FC for a box's static position, as this FC's
state might be gone by the time the layout for absolutely positioned
elements occurs. Instead, the "static position rectangle" (a concept
from the spec) is saved in the layout state, along with information on
how to align the box within this rectangle when its width and height are
resolved.
This commit is contained in:
Aliaksandr Kalenik 2024-09-10 19:02:03 +02:00 committed by Andreas Kling
parent bea7eec518
commit 863416e3ac
Notes: github-actions[bot] 2024-09-12 05:37:23 +00:00
18 changed files with 299 additions and 86 deletions

View file

@ -1,12 +1,12 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (1,1) content-size 798x0 [BFC] children: not-inline
BlockContainer <body> at (10,10) content-size 500x100 positioned [BFC] children: not-inline
BlockContainer <div.image-container> 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 <img> at (262,12) content-size 248x26.46875 children: not-inline
BlockContainer <div.image-container> 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 <img> at (262,13) content-size 248x26.46875 children: not-inline
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x2] overflow: [9,9 502x102]
PaintableWithLines (BlockContainer<BODY>) [9,9 502x102]
PaintableWithLines (BlockContainer<DIV>.image-container) [260,10 250x30.46875] overflow: [261,11 249x28.46875]
ImagePaintable (ImageBox<IMG>) [261,11 250x28.46875]
PaintableWithLines (BlockContainer<DIV>.image-container) [260,11 250x30.46875] overflow: [261,12 249x28.46875]
ImagePaintable (ImageBox<IMG>) [261,12 250x28.46875]

View file

@ -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 <div.column.outer.right> at (11,1809) content-size 300x60 flex-container(column) [FFC] children: not-inline
BlockContainer <div> 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 <div> 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 <div.column.reverse.outer.right> at (11,2429) content-size 300x60 flex-container(column-reverse) [FFC] children: not-inline
BlockContainer <div> 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 <div> 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<DIV>.column.outer.right) [10,1808 302x62]
PaintableWithLines (BlockContainer<DIV>) [11,1809 152x52]
PaintableWithLines (BlockContainer<DIV>) [11,1817 152x52]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [10,1870 780x0]
PaintableBox (Box<DIV>.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<DIV>.column.reverse.outer.right) [10,2428 302x62]
PaintableWithLines (BlockContainer<DIV>) [11,2429 152x52]
PaintableWithLines (BlockContainer<DIV>) [11,2437 152x52]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [10,2490 780x0]

View file

@ -0,0 +1,37 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x16 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x0 children: inline
BlockContainer <div#containing-block> at (8,8) content-size 200x300 positioned [BFC] children: not-inline
BlockContainer <(anonymous)> at (8,8) content-size 200x0 children: inline
TextNode <#text>
Box <div#inner-flex> at (8,8) content-size 200x0 flex-container(row) [FFC] children: not-inline
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
TextNode <#text>
BlockContainer <div> at (83,8) content-size 50x150 positioned [BFC] children: not-inline
BlockContainer <(anonymous)> at (83,8) content-size 50x0 children: inline
TextNode <#text>
BlockContainer <span> 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 <div> 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<HTML>) [0,0 800x16]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x0]
PaintableWithLines (BlockContainer<DIV>#containing-block) [8,8 200x300] overflow: [8,8 300x300]
PaintableWithLines (BlockContainer(anonymous)) [8,8 200x0]
PaintableBox (Box<DIV>#inner-flex) [8,8 200x0] overflow: [83,8 50x150]
PaintableWithLines (BlockContainer<DIV>) [83,8 50x150]
PaintableWithLines (BlockContainer(anonymous)) [83,8 50x0]
PaintableWithLines (BlockContainer<SPAN>) [83,8 50x0]
PaintableWithLines (BlockContainer(anonymous)) [83,8 50x0]
PaintableWithLines (BlockContainer(anonymous)) [8,8 200x0]
PaintableWithLines (BlockContainer<DIV>) [8,8 300x300]
PaintableWithLines (BlockContainer(anonymous)) [8,308 200x0]

View file

@ -0,0 +1,59 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x0 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 400x275 positioned [BFC] children: not-inline
BlockContainer <div#fill> 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 <div#grid> at (8,108) content-size 400x75 [BFC] children: inline
TextNode <#text>
BlockContainer <div#grid-item-abspos> 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 <div#grid-item-abspos> 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 <div#grid-item-abspos> 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 <div#grid-item-abspos> 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 <div#fill> 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<HTML>) [0,0 800x0] overflow: [8,8 400x275]
PaintableWithLines (BlockContainer<BODY>) [8,8 400x275]
PaintableWithLines (BlockContainer<DIV>#fill) [8,8 100x100]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [8,108 400x0]
PaintableWithLines (BlockContainer<DIV>#grid) [8,108 400x75]
PaintableWithLines (BlockContainer<DIV>#grid-item-abspos) [8,8 60.890625x25]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<DIV>#grid-item-abspos) [336.25,8 71.75x25]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<DIV>#grid-item-abspos) [8,258 90.359375x25]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<DIV>#grid-item-abspos) [306.78125,258 101.21875x25]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [8,183 400x0]
PaintableWithLines (BlockContainer<DIV>#fill) [8,183 100x100]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [8,283 400x0]

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<style>
#containing-block {
width: 200px;
background: red;
position: absolute;
}
#inner-flex {
display: flex;
justify-content: center;
}
span {
display: block;
width: 50px;
}
</style>
<div id="containing-block">
<div id="inner-flex">
<div style="position: absolute; height: 50%; background: green;">
<span></span>
</div>
</div>
<div style="background-color: blueviolet; width: 300px; height: 300px"></div>
</div>

View file

@ -0,0 +1,32 @@
<!doctype html><style>
* {
outline: 1px solid black;
}
body {
position: absolute;
width: 400px;
}
#grid {
display: flow-root;
background-color: magenta;
height: 75px;
}
#grid-item-abspos {
position: absolute;
height: 25px;
background-color: yellowgreen;
}
#fill {
width: 100px;
height: 100px;
background-color: rebeccapurple;
}
</style>
<div id="fill">hello</div>
<div id="grid">
<div id="grid-item-abspos" style="top: 0; left: 0">top left</div>
<div id="grid-item-abspos" style="top: 0; right: 0">top right</div>
<div id="grid-item-abspos" style="bottom: 0; left: 0">bottom left</div>
<div id="grid-item-abspos" style="bottom: 0; right: 0">bottom right</div>
</div>
<div id="fill">hello</div>

View file

@ -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;
{

View file

@ -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<Box>(*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;
}

View file

@ -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<JS::NonnullGCPtr<Box const>> m_absolutely_positioned_boxes;
bool m_was_notified_after_parent_dimensioned_my_root_box { false };
};

View file

@ -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) {

View file

@ -54,6 +54,11 @@ public:
bool is_user_scrollable() const;
void add_contained_abspos_child(JS::NonnullGCPtr<Node> child) { m_contained_abspos_children.append(child); }
Vector<JS::NonnullGCPtr<Node>> const& contained_abspos_children() const { return m_contained_abspos_children; }
virtual void visit_edges(Cell::Visitor&) override;
protected:
Box(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
Box(DOM::Document&, DOM::Node*, NonnullOwnPtr<CSS::ComputedValues>);
@ -64,6 +69,8 @@ private:
Optional<CSSPixels> m_natural_width;
Optional<CSSPixels> m_natural_height;
Optional<CSSPixelFraction> m_natural_aspect_ratio;
Vector<JS::NonnullGCPtr<Node>> m_contained_abspos_children;
};
template<>

View file

@ -186,13 +186,18 @@ void FlexFormattingContext::parent_context_did_dimension_child_root_box()
flex_container().for_each_child_of_type<Box>([&](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<Box>(*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

View file

@ -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;

View file

@ -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);

View file

@ -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&);

View file

@ -2010,13 +2010,18 @@ void GridFormattingContext::parent_context_did_dimension_child_root_box()
grid_container().for_each_child_of_type<Box>([&](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<Box>(*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)

View file

@ -305,8 +305,11 @@ void InlineFormattingContext::generate_line_boxes()
break;
}
case InlineLevelIterator::Item::Type::AbsolutelyPositionedElement:
if (is<Box>(*item.node))
parent().add_absolutely_positioned_box(static_cast<Layout::Box const&>(*item.node));
if (is<Box>(*item.node)) {
auto const& box = static_cast<Layout::Box const&>(*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:

View file

@ -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<CSS::GridTrackSizeListStyleValue> 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<CSS::GridTrackSizeListStyleValue> m_grid_template_columns;
RefPtr<CSS::GridTrackSizeListStyleValue> m_grid_template_rows;
Optional<StaticPositionRect> m_static_position_rect;
};
// Commits the used values produced by layout and builds a paintable tree.