summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2023-01-06 21:05:18 +0100
committerAndreas Kling <kling@serenityos.org>2023-01-06 21:12:55 +0100
commit80ce0419b6085c670171f6394c2ca68f67c25ea2 (patch)
treeb849b52796fe0bf91f22e288a747695791e22872
parent6d54e5ce9af5223f7b055383b666944856a3f2df (diff)
downloadserenity-80ce0419b6085c670171f6394c2ca68f67c25ea2.zip
LibWeb: Fix abspos flex container with height:auto getting zero height
When laying out abspos boxes, we compute the height twice: before and after the inside of the box has been laid out. The first pass allows percentage vertical values inside the box to be resolved against the box's height. The second pass resolves the final used value for the height of the box itself. In cases where the box height depends on the results of inside layout, we were incorrectly setting the box to having a definite zero height. This led to incorrect results when sizing an abspos flex container, since the FFC sizes containers (in row layouts) based on whether the container has a definite height. To avoid this problem, this patch adds an enum so we can differentiate between the two abspos height computation passes. If the first pass discovers a dependency on the inside layout, we simply bail out of computing the height, leaving it as indefinite. This allows the FFC to size its container correctly, and the correct height gets set by the second pass.
-rw-r--r--Base/res/html/tests/abspos-flexbox-with-auto-height.html12
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.cpp31
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.h11
3 files changed, 44 insertions, 10 deletions
diff --git a/Base/res/html/tests/abspos-flexbox-with-auto-height.html b/Base/res/html/tests/abspos-flexbox-with-auto-height.html
new file mode 100644
index 0000000000..0eb5ce9ce3
--- /dev/null
+++ b/Base/res/html/tests/abspos-flexbox-with-auto-height.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html><html><head><style>
+ * {
+ border: 1px solid black;
+ }
+ nav {
+ position: absolute;
+ display: flex;
+ }
+ .item {
+ background: lime;
+ }
+</style></head><body><nav><div class=item>This should have a green background.</div></nav></body></html>
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
index 61b8a0fcf0..b735980240 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
@@ -386,12 +386,12 @@ void FormattingContext::compute_width_for_absolutely_positioned_element(Box cons
compute_width_for_absolutely_positioned_non_replaced_element(box, available_space);
}
-void FormattingContext::compute_height_for_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space)
+void FormattingContext::compute_height_for_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space, BeforeOrAfterInsideLayout before_or_after_inside_layout)
{
if (is<ReplacedBox>(box))
- compute_height_for_absolutely_positioned_replaced_element(static_cast<ReplacedBox const&>(box), available_space);
+ compute_height_for_absolutely_positioned_replaced_element(static_cast<ReplacedBox const&>(box), available_space, before_or_after_inside_layout);
else
- compute_height_for_absolutely_positioned_non_replaced_element(box, available_space);
+ compute_height_for_absolutely_positioned_non_replaced_element(box, available_space, before_or_after_inside_layout);
}
CSSPixels FormattingContext::compute_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box, AvailableSpace const& available_space)
@@ -636,13 +636,17 @@ void FormattingContext::compute_width_for_absolutely_positioned_replaced_element
}
// https://drafts.csswg.org/css-position-3/#abs-non-replaced-height
-void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box const& box, AvailableSpace const& available_space)
+void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box const& box, AvailableSpace const& available_space, BeforeOrAfterInsideLayout before_or_after_inside_layout)
{
// 5.3. The Height Of Absolutely Positioned, Non-Replaced Elements
// For absolutely positioned elements, the used values of the vertical dimensions must satisfy this constraint:
// top + margin-top + border-top-width + padding-top + height + padding-bottom + border-bottom-width + margin-bottom + bottom = height of containing block
+ // NOTE: This function is called twice: both before and after inside layout.
+ // In the before pass, if it turns out we need the automatic height of the box, we abort these steps.
+ // This allows the box to retain an indefinite height from the perspective of inside layout.
+
auto margin_top = box.computed_values().margin().top();
auto margin_bottom = box.computed_values().margin().bottom();
auto top = box.computed_values().inset().top();
@@ -697,6 +701,10 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
// If all three of top, height, and bottom are auto:
if (top.is_auto() && height.is_auto() && bottom.is_auto()) {
+ // (If we haven't done inside layout yet, we can't compute the auto height.)
+ if (before_or_after_inside_layout == BeforeOrAfterInsideLayout::Before)
+ return;
+
// First set any auto values for margin-top and margin-bottom to 0,
if (margin_top.is_auto())
margin_top = CSS::Length::make_px(0);
@@ -748,6 +756,10 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
// 1. If top and height are auto and bottom is not auto,
if (top.is_auto() && height.is_auto() && !bottom.is_auto()) {
+ // (If we haven't done inside layout yet, we can't compute the auto height.)
+ if (before_or_after_inside_layout == BeforeOrAfterInsideLayout::Before)
+ return;
+
// then the height is based on the Auto heights for block formatting context roots,
height = CSS::Size::make_px(compute_auto_height_for_block_formatting_context_root(verify_cast<BlockContainer>(box)));
@@ -766,6 +778,10 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
// 3. If height and bottom are auto and top is not auto,
else if (height.is_auto() && bottom.is_auto() && !top.is_auto()) {
+ // (If we haven't done inside layout yet, we can't compute the auto height.)
+ if (before_or_after_inside_layout == BeforeOrAfterInsideLayout::Before)
+ return;
+
// then the height is based on the Auto heights for block formatting context roots,
height = CSS::Size::make_px(compute_auto_height_for_block_formatting_context_root(verify_cast<BlockContainer>(box)));
@@ -893,10 +909,11 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box, Ava
// NOTE: We compute height before *and* after doing inside layout.
// This is done so that inside layout can resolve percentage heights.
// In some situations, e.g with non-auto top & bottom values, the height can be determined early.
- compute_height_for_absolutely_positioned_element(box, available_space);
+ compute_height_for_absolutely_positioned_element(box, available_space, BeforeOrAfterInsideLayout::Before);
auto independent_formatting_context = layout_inside(box, LayoutMode::Normal, box_state.available_inner_space_or_constraints_from(available_space));
- compute_height_for_absolutely_positioned_element(box, available_space);
+
+ compute_height_for_absolutely_positioned_element(box, available_space, BeforeOrAfterInsideLayout::After);
box_state.margin_left = box.computed_values().margin().left().resolved(box, width_of_containing_block_as_length).to_px(box);
box_state.margin_top = box.computed_values().margin().top().resolved(box, width_of_containing_block_as_length).to_px(box);
@@ -966,7 +983,7 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box, Ava
independent_formatting_context->parent_context_did_dimension_child_root_box();
}
-void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space)
+void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space, BeforeOrAfterInsideLayout)
{
// 10.6.5 Absolutely positioned, replaced elements
// The used value of 'height' is determined as for inline replaced elements.
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
index 1b74d88650..7e1692c8e0 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
@@ -110,9 +110,14 @@ protected:
void compute_width_for_absolutely_positioned_element(Box const&, AvailableSpace const&);
void compute_width_for_absolutely_positioned_non_replaced_element(Box const&, AvailableSpace const&);
void compute_width_for_absolutely_positioned_replaced_element(ReplacedBox const&, AvailableSpace const&);
- void compute_height_for_absolutely_positioned_element(Box const&, AvailableSpace const&);
- void compute_height_for_absolutely_positioned_non_replaced_element(Box const&, AvailableSpace const&);
- void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const&, AvailableSpace const&);
+
+ enum class BeforeOrAfterInsideLayout {
+ Before,
+ After,
+ };
+ void compute_height_for_absolutely_positioned_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout);
+ void compute_height_for_absolutely_positioned_non_replaced_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout);
+ void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const&, AvailableSpace const&, BeforeOrAfterInsideLayout);
Type m_type {};