.flexrow) [10,10 780x21.46875] overflow: [11,11 778x21.46875]
+ PaintableBox (Box
.project) [11,11 46.03125x21.46875]
PaintableWithLines (BlockContainer(anonymous)) [12,12 44.03125x17.46875]
TextPaintable (TextNode<#text>)
diff --git a/Tests/LibWeb/Layout/expected/flex/flex-item-with-intrinsic-aspect-ratio-and-max-height.txt b/Tests/LibWeb/Layout/expected/flex/flex-item-with-intrinsic-aspect-ratio-and-max-height.txt
index 5ce0f4dc07..99f6d4198d 100644
--- a/Tests/LibWeb/Layout/expected/flex/flex-item-with-intrinsic-aspect-ratio-and-max-height.txt
+++ b/Tests/LibWeb/Layout/expected/flex/flex-item-with-intrinsic-aspect-ratio-and-max-height.txt
@@ -1,11 +1,11 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
- BlockContainer at (1,1) content-size 798x69.96875 [BFC] children: not-inline
- Box at (10,10) content-size 780x51.96875 flex-container(row) [FFC] children: not-inline
- ImageBox
![]()
at (11,11) content-size 66.65625x49.984375 flex-item children: not-inline
+ BlockContainer at (1,1) content-size 798x69.984375 [BFC] children: not-inline
+ Box at (10,10) content-size 780x51.984375 flex-container(row) [FFC] children: not-inline
+ ImageBox
![]()
at (11,11) content-size 64x49.984375 flex-item children: not-inline
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
TextNode <#text>
ViewportPaintable (Viewport<#document>) [0,0 800x600]
- PaintableWithLines (BlockContainer) [0,0 800x71.96875]
- PaintableBox (Box) [9,9 782x53.96875] overflow: [10,10 780x51.984375]
- ImagePaintable (ImageBox
![]()
) [10,10 68.65625x51.984375]
+ PaintableWithLines (BlockContainer) [0,0 800x71.984375]
+ PaintableBox (Box) [9,9 782x53.984375]
+ ImagePaintable (ImageBox
![]()
) [10,10 66x51.984375]
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
index f3940afe84..a5e57e88ee 100644
--- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
@@ -680,7 +680,8 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
place_block_level_element_in_normal_flow_horizontally(box, available_space);
- if (box.is_replaced_box())
+ // NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early.
+ if (box.is_replaced_box() || box.display().is_flex_inside())
compute_height(box, available_space);
if (independent_formatting_context) {
@@ -1247,15 +1248,4 @@ CSSPixels BlockFormattingContext::greatest_child_width(Box const& box) const
return max_width;
}
-void BlockFormattingContext::determine_width_of_child(Box const&, AvailableSpace const&)
-{
- // NOTE: We don't do anything here, since we'll have already determined the width of the child
- // before recursing into nested layout within the child.
-}
-
-void BlockFormattingContext::determine_height_of_child(Box const& box, AvailableSpace const& available_space)
-{
- compute_height(box, available_space);
-}
-
}
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
index eec02a897b..089d915789 100644
--- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
@@ -50,10 +50,6 @@ public:
void layout_block_level_box(Box const&, BlockContainer const&, LayoutMode, CSSPixels& bottom_of_lowest_margin_box, AvailableSpace const&);
- virtual bool can_determine_size_of_child() const override { return true; }
- virtual void determine_width_of_child(Box const&, AvailableSpace const&) override;
- virtual void determine_height_of_child(Box const&, AvailableSpace const&) override;
-
void resolve_vertical_box_model_metrics(Box const&);
enum class DidIntroduceClearance {
diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
index 7da85a1426..c05125b000 100644
--- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2023, Andreas Kling
+ * Copyright (c) 2021-2024, Andreas Kling
* Copyright (c) 2021, Tobias Christiansen
*
* SPDX-License-Identifier: BSD-2-Clause
@@ -56,35 +56,17 @@ CSSPixels FlexFormattingContext::automatic_content_height() const
return m_flex_container_state.content_height();
}
-void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace const& available_content_space)
+void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace const& available_space)
{
VERIFY(&run_box == &flex_container());
- // NOTE: The available space provided by the parent context is basically our *content box*.
- // FFC is currently written in a way that expects that to include padding and border as well,
- // so we pad out the available space here to accommodate that.
- // FIXME: Refactor the necessary parts of FFC so we don't need this hack!
-
- auto available_width = available_content_space.width;
- if (available_width.is_definite())
- available_width = AvailableSize::make_definite(available_width.to_px_or_zero() + m_flex_container_state.border_box_left() + m_flex_container_state.border_box_right());
- auto available_height = available_content_space.height;
- if (available_height.is_definite())
- available_height = AvailableSize::make_definite(available_height.to_px_or_zero() + m_flex_container_state.border_box_top() + m_flex_container_state.border_box_bottom());
-
- m_available_space_for_flex_container = AxisAgnosticAvailableSpace {
- .main = is_row_layout() ? available_width : available_height,
- .cross = !is_row_layout() ? available_width : available_height,
- .space = { available_width, available_height },
- };
-
// This implements https://www.w3.org/TR/css-flexbox-1/#layout-algorithm
// 1. Generate anonymous flex items
generate_anonymous_flex_items();
// 2. Determine the available main and cross space for the flex items
- determine_available_space_for_items(AvailableSpace(available_width, available_height));
+ determine_available_space_for_items(available_space);
{
// https://drafts.csswg.org/css-flexbox-1/#definite-sizes
@@ -114,12 +96,16 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace c
determine_flex_base_size_and_hypothetical_main_size(item);
}
- if (available_width.is_intrinsic_sizing_constraint() || available_height.is_intrinsic_sizing_constraint()) {
+ if (available_space.width.is_intrinsic_sizing_constraint() || available_space.height.is_intrinsic_sizing_constraint()) {
// We're computing intrinsic size for the flex container. This happens at the end of run().
} else {
-
// 4. Determine the main size of the flex container
- determine_main_size_of_flex_container();
+ // Determine the main size of the flex container using the rules of the formatting context in which it participates.
+ // NOTE: The automatic block size of a block-level flex container is its max-content size.
+
+ // NOTE: We've already handled this in the parent formatting context.
+ // Specifically, all formatting contexts will have assigned width & height to the flex container
+ // before this formatting context runs.
}
// 5. Collect flex items into flex lines:
@@ -186,7 +172,7 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace c
// 16. Align all flex lines (per align-content)
align_all_flex_lines();
- if (available_width.is_intrinsic_sizing_constraint() || available_height.is_intrinsic_sizing_constraint()) {
+ if (available_space.width.is_intrinsic_sizing_constraint() || available_space.height.is_intrinsic_sizing_constraint()) {
// We're computing intrinsic size for the flex container.
determine_intrinsic_size_of_flex_container();
} else {
@@ -195,7 +181,7 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace c
copy_dimensions_from_flex_items_to_boxes();
for (auto& item : m_flex_items) {
auto& box_state = m_state.get(item.box);
- if (auto independent_formatting_context = layout_inside(item.box, LayoutMode::Normal, box_state.available_inner_space_or_constraints_from(m_available_space_for_flex_container->space)))
+ if (auto independent_formatting_context = layout_inside(item.box, LayoutMode::Normal, box_state.available_inner_space_or_constraints_from(m_available_space_for_items->space)))
independent_formatting_context->parent_context_did_dimension_child_root_box();
compute_inset(item.box);
@@ -443,66 +429,17 @@ void FlexFormattingContext::set_main_axis_second_margin(FlexItem& item, CSSPixel
// https://drafts.csswg.org/css-flexbox-1/#algo-available
void FlexFormattingContext::determine_available_space_for_items(AvailableSpace const& available_space)
{
- // For each dimension, if that dimension of the flex container’s content box is a definite size, use that;
- // if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint;
- // otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value.
- // This might result in an infinite value.
-
- Optional available_width_for_items;
- if (m_flex_container_state.has_definite_width()) {
- available_width_for_items = AvailableSize::make_definite(m_flex_container_state.content_width());
- } else {
- if (available_space.width.is_intrinsic_sizing_constraint()) {
- available_width_for_items = available_space.width;
- } else {
- if (available_space.width.is_definite()) {
- auto remaining = available_space.width.to_px_or_zero()
- - m_flex_container_state.margin_left
- - m_flex_container_state.margin_right
- - m_flex_container_state.border_left
- - m_flex_container_state.padding_right
- - m_flex_container_state.padding_left
- - m_flex_container_state.padding_right;
- available_width_for_items = AvailableSize::make_definite(remaining);
- } else {
- available_width_for_items = AvailableSize::make_indefinite();
- }
- }
- }
-
- Optional available_height_for_items;
- if (m_flex_container_state.has_definite_height()) {
- available_height_for_items = AvailableSize::make_definite(m_flex_container_state.content_height());
- } else {
- if (available_space.height.is_intrinsic_sizing_constraint()) {
- available_height_for_items = available_space.height;
- } else {
- if (available_space.height.is_definite()) {
- auto remaining = available_space.height.to_px_or_zero()
- - m_flex_container_state.margin_top
- - m_flex_container_state.margin_bottom
- - m_flex_container_state.border_top
- - m_flex_container_state.padding_bottom
- - m_flex_container_state.padding_top
- - m_flex_container_state.padding_bottom;
- available_height_for_items = AvailableSize::make_definite(remaining);
- } else {
- available_height_for_items = AvailableSize::make_indefinite();
- }
- }
- }
-
if (is_row_layout()) {
m_available_space_for_items = AxisAgnosticAvailableSpace {
- .main = *available_width_for_items,
- .cross = *available_height_for_items,
- .space = { *available_width_for_items, *available_height_for_items },
+ .main = available_space.width,
+ .cross = available_space.height,
+ .space = { available_space.width, available_space.height },
};
} else {
m_available_space_for_items = AxisAgnosticAvailableSpace {
- .main = *available_height_for_items,
- .cross = *available_width_for_items,
- .space = { *available_width_for_items, *available_height_for_items },
+ .main = available_space.height,
+ .cross = available_space.width,
+ .space = { available_space.width, available_space.height },
};
}
}
@@ -785,59 +722,6 @@ CSSPixels FlexFormattingContext::content_based_minimum_size(FlexItem const& item
return unclamped_size;
}
-bool FlexFormattingContext::can_determine_size_of_child() const
-{
- return true;
-}
-
-void FlexFormattingContext::determine_width_of_child(Box const&, AvailableSpace const&)
-{
- // NOTE: For now, we simply do nothing here. If a child context is calling up to us
- // and asking us to determine its width, we've already done so as part of the
- // flex layout algorithm.
-}
-
-void FlexFormattingContext::determine_height_of_child(Box const&, AvailableSpace const&)
-{
- // NOTE: For now, we simply do nothing here. If a child context is calling up to us
- // and asking us to determine its height, we've already done so as part of the
- // flex layout algorithm.
-}
-
-// https://drafts.csswg.org/css-flexbox-1/#algo-main-container
-void FlexFormattingContext::determine_main_size_of_flex_container()
-{
- // Determine the main size of the flex container using the rules of the formatting context in which it participates.
- // NOTE: The automatic block size of a block-level flex container is its max-content size.
-
- // FIXME: The code below doesn't know how to size absolutely positioned flex containers at all.
- // We just leave it alone for now and let the parent context deal with it.
- if (flex_container().is_absolutely_positioned())
- return;
-
- // FIXME: Once all parent contexts now how to size a given child, we can remove
- // `can_determine_size_of_child()`.
- if (parent()->can_determine_size_of_child()) {
- if (is_row_layout()) {
- parent()->determine_width_of_child(flex_container(), m_available_space_for_flex_container->space);
- } else {
- parent()->determine_height_of_child(flex_container(), m_available_space_for_flex_container->space);
- }
- return;
- }
-
- if (is_row_layout()) {
- if (!flex_container().is_out_of_flow(*parent()) && m_state.get(*flex_container().containing_block()).has_definite_width()) {
- set_main_size(flex_container(), calculate_stretch_fit_width(flex_container(), m_available_space_for_flex_container->space.width));
- } else {
- set_main_size(flex_container(), calculate_max_content_width(flex_container()));
- }
- } else {
- if (!has_definite_main_size(flex_container()))
- set_main_size(flex_container(), calculate_max_content_height(flex_container(), m_available_space_for_flex_container->space.width.to_px_or_zero()));
- }
-}
-
// https://www.w3.org/TR/css-flexbox-1/#algo-line-break
void FlexFormattingContext::collect_flex_items_into_flex_lines()
{
@@ -1229,7 +1113,7 @@ void FlexFormattingContext::calculate_cross_size_of_each_flex_line()
// If the flex container is single-line, then clamp the line’s cross-size to be within the container’s computed min and max cross sizes.
// Note that if CSS 2.1’s definition of min/max-width/height applied more generally, this behavior would fall out automatically.
// AD-HOC: We don't do this when the flex container is being sized under a min-content or max-content constraint.
- if (is_single_line() && !m_available_space_for_flex_container->cross.is_intrinsic_sizing_constraint()) {
+ if (is_single_line() && !m_available_space_for_items->cross.is_intrinsic_sizing_constraint()) {
auto const& computed_min_size = this->computed_cross_min_size(flex_container());
auto const& computed_max_size = this->computed_cross_max_size(flex_container());
auto cross_min_size = (!computed_min_size.is_auto() && !computed_min_size.contains_percentage()) ? specified_cross_min_size(flex_container()) : 0;
@@ -1553,7 +1437,7 @@ void FlexFormattingContext::determine_flex_container_used_cross_size()
}
// AD-HOC: We don't apply min/max cross size constraints when sizing the flex container under an intrinsic sizing constraint.
- if (!m_available_space_for_flex_container->cross.is_intrinsic_sizing_constraint()) {
+ if (!m_available_space_for_items->cross.is_intrinsic_sizing_constraint()) {
auto const& computed_min_size = this->computed_cross_min_size(flex_container());
auto const& computed_max_size = this->computed_cross_max_size(flex_container());
auto cross_min_size = (!computed_min_size.is_auto() && !computed_min_size.contains_percentage()) ? specified_cross_min_size(flex_container()) : 0;
@@ -1689,7 +1573,7 @@ void FlexFormattingContext::copy_dimensions_from_flex_items_to_boxes()
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-sizes
void FlexFormattingContext::determine_intrinsic_size_of_flex_container()
{
- if (m_available_space_for_flex_container->main.is_intrinsic_sizing_constraint()) {
+ if (m_available_space_for_items->main.is_intrinsic_sizing_constraint()) {
CSSPixels main_size = calculate_intrinsic_main_size_of_flex_container();
set_main_size(flex_container(), main_size);
}
@@ -2051,7 +1935,7 @@ CSSPixels FlexFormattingContext::calculate_fit_content_cross_size(FlexItem const
CSSPixels FlexFormattingContext::calculate_min_content_cross_size(FlexItem const& item) const
{
if (is_row_layout()) {
- auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_flex_container->space);
+ auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_items->space);
if (available_space.width.is_indefinite()) {
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
}
@@ -2063,7 +1947,7 @@ CSSPixels FlexFormattingContext::calculate_min_content_cross_size(FlexItem const
CSSPixels FlexFormattingContext::calculate_max_content_cross_size(FlexItem const& item) const
{
if (is_row_layout()) {
- auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_flex_container->space);
+ auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_items->space);
if (available_space.width.is_indefinite()) {
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
}
diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
index 599300b32e..70408019db 100644
--- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, Andreas Kling
+ * Copyright (c) 2021-2024, Andreas Kling
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -24,10 +24,6 @@ public:
Box const& flex_container() const { return context_box(); }
- virtual bool can_determine_size_of_child() const override;
- virtual void determine_width_of_child(Box const&, AvailableSpace const&) override;
- virtual void determine_height_of_child(Box const&, AvailableSpace const&) override;
-
virtual CSSPixelPoint calculate_static_position(Box const&) const override;
private:
@@ -162,8 +158,6 @@ private:
void determine_flex_base_size_and_hypothetical_main_size(FlexItem&);
- void determine_main_size_of_flex_container();
-
void collect_flex_items_into_flex_lines();
void resolve_flexible_lengths();
@@ -229,7 +223,6 @@ private:
AvailableSpace space;
};
Optional m_available_space_for_items;
- Optional m_available_space_for_flex_container;
};
}
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
index 5a94155ec0..3d5f8d695d 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
@@ -89,10 +89,6 @@ public:
[[nodiscard]] CSSPixels calculate_stretch_fit_width(Box const&, AvailableSize const&) const;
[[nodiscard]] CSSPixels calculate_stretch_fit_height(Box const&, AvailableSize const&) const;
- virtual bool can_determine_size_of_child() const { return false; }
- virtual void determine_width_of_child(Box const&, AvailableSpace const&) { }
- virtual void determine_height_of_child(Box const&, AvailableSpace const&) { }
-
virtual CSSPixelPoint calculate_static_position(Box const&) const;
bool can_skip_is_anonymous_text_run(Box&);
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
index 534693991f..3e8093dc34 100644
--- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
@@ -172,6 +172,10 @@ void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode l
box_state.set_content_width(width);
+ // NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early.
+ if (box_state.has_definite_height() || box.display().is_flex_inside())
+ parent().compute_height(box, AvailableSpace(AvailableSize::make_definite(width), AvailableSize::make_definite(m_containing_block_state.content_height())));
+
auto independent_formatting_context = layout_inside(box, layout_mode, box_state.available_inner_space_or_constraints_from(*m_available_space));
auto const& height_value = box.computed_values().height();
@@ -389,21 +393,6 @@ bool InlineFormattingContext::can_fit_new_line_at_y(CSSPixels y) const
return true;
}
-bool InlineFormattingContext::can_determine_size_of_child() const
-{
- return parent().can_determine_size_of_child();
-}
-
-void InlineFormattingContext::determine_width_of_child(Box const& box, AvailableSpace const& available_space)
-{
- return parent().determine_width_of_child(box, available_space);
-}
-
-void InlineFormattingContext::determine_height_of_child(Box const& box, AvailableSpace const& available_space)
-{
- return parent().determine_height_of_child(box, available_space);
-}
-
CSSPixels InlineFormattingContext::vertical_float_clearance() const
{
return m_vertical_float_clearance;
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
index 1b21b669d7..72b4da4d4f 100644
--- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
@@ -34,10 +34,6 @@ public:
bool any_floats_intrude_at_y(CSSPixels y) const;
bool can_fit_new_line_at_y(CSSPixels y) const;
- virtual bool can_determine_size_of_child() const override;
- virtual void determine_width_of_child(Box const&, AvailableSpace const&) override;
- virtual void determine_height_of_child(Box const&, AvailableSpace const&) override;
-
CSSPixels vertical_float_clearance() const;
void set_vertical_float_clearance(CSSPixels);