Skip to content

Commit f25203f

Browse files
committed
LibWeb: Don't re-resolve "auto" flex item sizes after definitizing them
This is rather subtle and points to our architecture around definite sizes not being exactly right, but... At some points during flexbox layout, the spec tells us that the sizes of certain flex items are considered definite from this point on. We implement this by marking each item's associated UsedValues as "has-definite-width/height". However, this breaks code that tries to resolve computed "auto" sizes by taking the corresponding size from the containing block. The end result was that the 1st sizing pass in flexbox would find the right size for an "auto" sized item, but the 2nd pass would override the correct size with the containing block's content size in that axis instead. To work around the issue, FFC now remembers when it "definitizes" an item, and future attempts to resolve an "auto" computed size for that value will bypass the computed-auto-is-resolved-against-containing-block step of the algorithm. It's not perfect, and we'll need to think more about how to really represent these intermediate states relating to box sizes being definite..
1 parent b8aa6a4 commit f25203f

File tree

4 files changed

+45
-23
lines changed

4 files changed

+45
-23
lines changed

Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode)
7777
auto item_inner_cross_size = item_preferred_outer_cross_size - item.margins.cross_before - item.margins.cross_after - item.padding.cross_before - item.padding.cross_after - item.borders.cross_before - item.borders.cross_after;
7878
set_cross_size(item.box, item_inner_cross_size);
7979
set_has_definite_cross_size(item.box, true);
80+
item.has_assigned_definite_cross_size = true;
8081
}
8182
}
8283
}
@@ -172,6 +173,7 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode)
172173
for (auto& item : m_flex_items) {
173174
set_cross_size(item.box, item.cross_size);
174175
set_has_definite_cross_size(item.box, true);
176+
item.has_assigned_definite_cross_size = true;
175177
}
176178
}
177179
}
@@ -344,26 +346,18 @@ float FlexFormattingContext::specified_cross_size(Box const& box) const
344346
return is_row_layout() ? box_state.content_height() : box_state.content_width();
345347
}
346348

347-
float FlexFormattingContext::resolved_definite_cross_size(Box const& box) const
349+
float FlexFormattingContext::resolved_definite_cross_size(FlexItem const& item) const
348350
{
349-
auto const& cross_value = is_row_layout() ? box.computed_values().height() : box.computed_values().width();
350-
if (cross_value.is_auto())
351-
return specified_cross_size(flex_container());
352-
if (cross_value.is_length())
353-
return specified_cross_size(box);
354-
auto containing_block_size = !is_row_layout() ? containing_block_width_for(box) : containing_block_height_for(box);
355-
return cross_value.resolved(box, CSS::Length::make_px(containing_block_size)).to_px(box);
351+
if (item.has_assigned_definite_cross_size)
352+
return specified_cross_size(item.box);
353+
return !is_row_layout() ? m_state.resolved_definite_width(item.box) : m_state.resolved_definite_height(item.box);
356354
}
357355

358-
float FlexFormattingContext::resolved_definite_main_size(Box const& box) const
356+
float FlexFormattingContext::resolved_definite_main_size(FlexItem const& item) const
359357
{
360-
auto const& main_value = is_row_layout() ? box.computed_values().width() : box.computed_values().height();
361-
if (main_value.is_auto())
362-
return specified_main_size(flex_container());
363-
if (main_value.is_length())
364-
return specified_main_size(box);
365-
auto containing_block_size = is_row_layout() ? containing_block_width_for(box) : containing_block_height_for(box);
366-
return main_value.resolved(box, CSS::Length::make_px(containing_block_size)).to_px(box);
358+
if (item.has_assigned_definite_main_size)
359+
return specified_main_size(item.box);
360+
return is_row_layout() ? m_state.resolved_definite_width(item.box) : m_state.resolved_definite_height(item.box);
367361
}
368362

369363
bool FlexFormattingContext::has_main_min_size(Box const& box) const
@@ -524,7 +518,7 @@ void FlexFormattingContext::determine_available_main_and_cross_space(bool& main_
524518
auto containing_block_effective_main_size = [&](Box const& box) -> Optional<float> {
525519
auto& containing_block = *box.containing_block();
526520
if (has_definite_main_size(containing_block))
527-
return resolved_definite_main_size(containing_block);
521+
return is_row_layout() ? m_state.resolved_definite_width(box) : m_state.resolved_definite_height(box);
528522
return {};
529523
};
530524

@@ -688,7 +682,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
688682
&& flex_item.used_flex_basis.type == CSS::FlexBasis::Content
689683
&& has_definite_cross_size(flex_item.box)) {
690684
// flex_base_size is calculated from definite cross size and intrinsic aspect ratio
691-
return resolved_definite_cross_size(flex_item.box) * flex_item.box.intrinsic_aspect_ratio().value();
685+
return resolved_definite_cross_size(flex_item) * flex_item.box.intrinsic_aspect_ratio().value();
692686
}
693687

694688
// C. If the used flex basis is content or depends on its available space,
@@ -721,7 +715,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
721715
// FIXME: This is probably too naive.
722716
// FIXME: Care about FlexBasis::Auto
723717
if (has_definite_main_size(child_box))
724-
return resolved_definite_main_size(child_box);
718+
return resolved_definite_main_size(flex_item);
725719

726720
// NOTE: To avoid repeated layout work, we keep a cache of flex item main sizes on the
727721
// root LayoutState object. It's available through a full layout cycle.
@@ -774,7 +768,7 @@ Optional<float> FlexFormattingContext::transferred_size_suggestion(FlexItem cons
774768
if (item.box.has_intrinsic_aspect_ratio() && has_definite_cross_size(item.box)) {
775769
auto aspect_ratio = item.box.intrinsic_aspect_ratio().value();
776770
// FIXME: Clamp cross size to min/max cross size before this conversion.
777-
return resolved_definite_cross_size(item.box) * aspect_ratio;
771+
return resolved_definite_cross_size(item) * aspect_ratio;
778772
}
779773

780774
// It is otherwise undefined.
@@ -1033,6 +1027,7 @@ void FlexFormattingContext::resolve_flexible_lengths()
10331027
// 2. If a flex-item’s flex basis is definite, then its post-flexing main size is also definite.
10341028
if (has_definite_main_size(flex_container()) || flex_item->used_flex_basis_is_definite) {
10351029
set_has_definite_main_size(flex_item->box, true);
1030+
flex_item->has_assigned_definite_main_size = true;
10361031
}
10371032
}
10381033

@@ -1055,7 +1050,7 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem&
10551050

10561051
// If we have a definite cross size, this is easy! No need to perform layout, we can just use it as-is.
10571052
if (has_definite_cross_size(item.box)) {
1058-
item.hypothetical_cross_size = css_clamp(resolved_definite_cross_size(item.box), clamp_min, clamp_max);
1053+
item.hypothetical_cross_size = css_clamp(resolved_definite_cross_size(item), clamp_min, clamp_max);
10591054
return;
10601055
}
10611056

Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class FlexFormattingContext final : public FormattingContext {
6262
DirectionAgnosticMargins padding {};
6363
bool is_min_violation { false };
6464
bool is_max_violation { false };
65+
bool has_assigned_definite_main_size { false };
66+
bool has_assigned_definite_cross_size { false };
6567

6668
float add_main_margin_box_sizes(float content_size) const
6769
{
@@ -85,8 +87,8 @@ class FlexFormattingContext final : public FormattingContext {
8587
bool has_definite_cross_size(Box const&) const;
8688
float specified_main_size(Box const&) const;
8789
float specified_cross_size(Box const&) const;
88-
float resolved_definite_main_size(Box const&) const;
89-
float resolved_definite_cross_size(Box const&) const;
90+
float resolved_definite_main_size(FlexItem const&) const;
91+
float resolved_definite_cross_size(FlexItem const&) const;
9092
bool has_main_min_size(Box const&) const;
9193
bool has_cross_min_size(Box const&) const;
9294
float specified_main_max_size(Box const&) const;

Userland/Libraries/LibWeb/Layout/LayoutState.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,26 @@ void LayoutState::UsedValues::set_content_height(float height)
232232
m_content_height = height;
233233
}
234234

235+
float LayoutState::resolved_definite_width(Box const& box) const
236+
{
237+
auto const& computed_value = box.computed_values().width();
238+
if (computed_value.is_auto())
239+
return get(*box.containing_block()).content_width();
240+
if (computed_value.is_length())
241+
return get(box).content_width();
242+
auto containing_block_size = get(*box.containing_block()).content_width();
243+
return computed_value.resolved(box, CSS::Length::make_px(containing_block_size)).to_px(box);
244+
}
245+
246+
float LayoutState::resolved_definite_height(Box const& box) const
247+
{
248+
auto const& computed_value = box.computed_values().height();
249+
if (computed_value.is_auto())
250+
return get(*box.containing_block()).content_height();
251+
if (computed_value.is_length())
252+
return get(box).content_height();
253+
auto containing_block_size = get(*box.containing_block()).content_height();
254+
return computed_value.resolved(box, CSS::Length::make_px(containing_block_size)).to_px(box);
255+
}
256+
235257
}

Userland/Libraries/LibWeb/Layout/LayoutState.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ struct LayoutState {
124124
HashTable<Box const*> m_floating_descendants;
125125
};
126126

127+
float resolved_definite_width(Box const&) const;
128+
float resolved_definite_height(Box const&) const;
129+
127130
void commit();
128131

129132
// NOTE: get_mutable() will CoW the UsedValues if it's inherited from an ancestor state;

0 commit comments

Comments
 (0)