1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 04:37:34 +00:00

LibWeb: Modernize handling of the CSS flex-basis property

Instead of a custom struct, use an AK::Variant for flex-basis.
A flex-basis is either `content` or a CSS size value, so we don't need
anything custom for that.

By using a CSS size, we also avoid having to convert in and out of size
in various places, simplifying the code.

This finally gets rid of the "Unsupported main size for flex-basis"
debug spam. :^)
This commit is contained in:
Andreas Kling 2023-06-21 19:39:07 +02:00
parent 8c980cf75b
commit 8648355783
6 changed files with 44 additions and 89 deletions

View file

@ -23,6 +23,9 @@
namespace Web::CSS { namespace Web::CSS {
struct FlexBasisContent { };
using FlexBasis = Variant<FlexBasisContent, Size>;
struct AspectRatio { struct AspectRatio {
bool use_natural_aspect_ratio_if_available; bool use_natural_aspect_ratio_if_available;
Optional<Ratio> preferred_ratio; Optional<Ratio> preferred_ratio;
@ -59,6 +62,7 @@ public:
static CSS::Visibility visibility() { return CSS::Visibility::Visible; } static CSS::Visibility visibility() { return CSS::Visibility::Visible; }
static CSS::FlexDirection flex_direction() { return CSS::FlexDirection::Row; } static CSS::FlexDirection flex_direction() { return CSS::FlexDirection::Row; }
static CSS::FlexWrap flex_wrap() { return CSS::FlexWrap::Nowrap; } static CSS::FlexWrap flex_wrap() { return CSS::FlexWrap::Nowrap; }
static CSS::FlexBasis flex_basis() { return CSS::Size::make_auto(); }
static CSS::ImageRendering image_rendering() { return CSS::ImageRendering::Auto; } static CSS::ImageRendering image_rendering() { return CSS::ImageRendering::Auto; }
static CSS::JustifyContent justify_content() { return CSS::JustifyContent::FlexStart; } static CSS::JustifyContent justify_content() { return CSS::JustifyContent::FlexStart; }
static CSS::AlignContent align_content() { return CSS::AlignContent::Stretch; } static CSS::AlignContent align_content() { return CSS::AlignContent::Stretch; }
@ -164,17 +168,6 @@ struct TransformOrigin {
CSS::LengthPercentage y { Percentage(50) }; CSS::LengthPercentage y { Percentage(50) };
}; };
enum class FlexBasis {
Content,
LengthPercentage,
Auto,
};
struct FlexBasisData {
CSS::FlexBasis type { CSS::FlexBasis::Auto };
Optional<CSS::LengthPercentage> length_percentage;
};
struct ShadowData { struct ShadowData {
Color color {}; Color color {};
CSS::Length offset_x { Length::make_px(0) }; CSS::Length offset_x { Length::make_px(0) };
@ -246,7 +239,7 @@ public:
CSS::WhiteSpace white_space() const { return m_inherited.white_space; } CSS::WhiteSpace white_space() const { return m_inherited.white_space; }
CSS::FlexDirection flex_direction() const { return m_noninherited.flex_direction; } CSS::FlexDirection flex_direction() const { return m_noninherited.flex_direction; }
CSS::FlexWrap flex_wrap() const { return m_noninherited.flex_wrap; } CSS::FlexWrap flex_wrap() const { return m_noninherited.flex_wrap; }
FlexBasisData const& flex_basis() const { return m_noninherited.flex_basis; } FlexBasis const& flex_basis() const { return m_noninherited.flex_basis; }
float flex_grow() const { return m_noninherited.flex_grow; } float flex_grow() const { return m_noninherited.flex_grow; }
float flex_shrink() const { return m_noninherited.flex_shrink; } float flex_shrink() const { return m_noninherited.flex_shrink; }
int order() const { return m_noninherited.order; } int order() const { return m_noninherited.order; }
@ -398,7 +391,7 @@ protected:
Vector<BackgroundLayerData> background_layers; Vector<BackgroundLayerData> background_layers;
CSS::FlexDirection flex_direction { InitialValues::flex_direction() }; CSS::FlexDirection flex_direction { InitialValues::flex_direction() };
CSS::FlexWrap flex_wrap { InitialValues::flex_wrap() }; CSS::FlexWrap flex_wrap { InitialValues::flex_wrap() };
CSS::FlexBasisData flex_basis {}; CSS::FlexBasis flex_basis { InitialValues::flex_basis() };
float flex_grow { InitialValues::flex_grow() }; float flex_grow { InitialValues::flex_grow() };
float flex_shrink { InitialValues::flex_shrink() }; float flex_shrink { InitialValues::flex_shrink() };
int order { InitialValues::order() }; int order { InitialValues::order() };
@ -492,7 +485,7 @@ public:
BorderData& border_bottom() { return m_noninherited.border_bottom; } BorderData& border_bottom() { return m_noninherited.border_bottom; }
void set_flex_direction(CSS::FlexDirection value) { m_noninherited.flex_direction = value; } void set_flex_direction(CSS::FlexDirection value) { m_noninherited.flex_direction = value; }
void set_flex_wrap(CSS::FlexWrap value) { m_noninherited.flex_wrap = value; } void set_flex_wrap(CSS::FlexWrap value) { m_noninherited.flex_wrap = value; }
void set_flex_basis(FlexBasisData value) { m_noninherited.flex_basis = move(value); } void set_flex_basis(FlexBasis value) { m_noninherited.flex_basis = move(value); }
void set_flex_grow(float value) { m_noninherited.flex_grow = value; } void set_flex_grow(float value) { m_noninherited.flex_grow = value; }
void set_flex_shrink(float value) { m_noninherited.flex_shrink = value; } void set_flex_shrink(float value) { m_noninherited.flex_shrink = value; }
void set_order(int value) { m_noninherited.order = value; } void set_order(int value) { m_noninherited.order = value; }

View file

@ -559,18 +559,14 @@ ErrorOr<RefPtr<StyleValue const>> ResolvedCSSStyleDeclaration::style_value_for_p
return NumberStyleValue::create(layout_node.computed_values().fill_opacity()); return NumberStyleValue::create(layout_node.computed_values().fill_opacity());
case PropertyID::FillRule: case PropertyID::FillRule:
return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().fill_rule())); return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().fill_rule()));
case PropertyID::FlexBasis: { case PropertyID::FlexBasis:
switch (layout_node.computed_values().flex_basis().type) { return layout_node.computed_values().flex_basis().visit(
case FlexBasis::Content: [](CSS::FlexBasisContent const&) -> ErrorOr<RefPtr<StyleValue const>> {
return IdentifierStyleValue::create(ValueID::Content); return IdentifierStyleValue::create(ValueID::Content);
case FlexBasis::LengthPercentage: },
return style_value_for_length_percentage(*layout_node.computed_values().flex_basis().length_percentage); [&](CSS::Size const& size) -> ErrorOr<RefPtr<StyleValue const>> {
case FlexBasis::Auto: return style_value_for_size(size);
return IdentifierStyleValue::create(ValueID::Auto); });
default:
VERIFY_NOT_REACHED();
}
break;
case PropertyID::FlexDirection: case PropertyID::FlexDirection:
return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().flex_direction())); return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().flex_direction()));
case PropertyID::FlexGrow: case PropertyID::FlexGrow:
@ -838,7 +834,6 @@ ErrorOr<RefPtr<StyleValue const>> ResolvedCSSStyleDeclaration::style_value_for_p
dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Computed style for the '{}' property was requested", string_from_property_id(property_id)); dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Computed style for the '{}' property was requested", string_from_property_id(property_id));
return nullptr; return nullptr;
} }
}
} }
Optional<StyleProperty> ResolvedCSSStyleDeclaration::property(PropertyID property_id) const Optional<StyleProperty> ResolvedCSSStyleDeclaration::property(PropertyID property_id) const

View file

@ -327,26 +327,14 @@ Optional<CSS::FlexWrap> StyleProperties::flex_wrap() const
return value_id_to_flex_wrap(value->to_identifier()); return value_id_to_flex_wrap(value->to_identifier());
} }
Optional<CSS::FlexBasisData> StyleProperties::flex_basis() const Optional<CSS::FlexBasis> StyleProperties::flex_basis() const
{ {
auto value = property(CSS::PropertyID::FlexBasis); auto value = property(CSS::PropertyID::FlexBasis);
if (value->is_identifier() && value->to_identifier() == CSS::ValueID::Content) if (value->is_identifier() && value->to_identifier() == CSS::ValueID::Content)
return { { CSS::FlexBasis::Content, {} } }; return CSS::FlexBasisContent {};
if (value->has_auto()) return size_value(CSS::PropertyID::FlexBasis);
return { { CSS::FlexBasis::Auto, {} } };
if (value->is_percentage())
return { { CSS::FlexBasis::LengthPercentage, value->as_percentage().percentage() } };
if (value->is_length())
return { { CSS::FlexBasis::LengthPercentage, value->as_length().length() } };
if (value->is_calculated())
return { { CSS::FlexBasis::LengthPercentage, CSS::LengthPercentage { value->as_calculated() } } };
return {};
} }
float StyleProperties::flex_grow() const float StyleProperties::flex_grow() const

View file

@ -69,7 +69,7 @@ public:
Optional<CSS::ListStylePosition> list_style_position() const; Optional<CSS::ListStylePosition> list_style_position() const;
Optional<CSS::FlexDirection> flex_direction() const; Optional<CSS::FlexDirection> flex_direction() const;
Optional<CSS::FlexWrap> flex_wrap() const; Optional<CSS::FlexWrap> flex_wrap() const;
Optional<CSS::FlexBasisData> flex_basis() const; Optional<CSS::FlexBasis> flex_basis() const;
float flex_grow() const; float flex_grow() const;
float flex_shrink() const; float flex_shrink() const;
int order() const; int order() const;

View file

@ -27,18 +27,6 @@ template<typename T>
return ::max(min, ::min(value, max)); return ::max(min, ::min(value, max));
} }
// FIXME: This is a hack helper, remove it when no longer needed.
static CSS::Size to_css_size(CSS::LengthPercentage const& length_percentage)
{
if (length_percentage.is_auto())
return CSS::Size::make_auto();
if (length_percentage.is_length())
return CSS::Size::make_length(length_percentage.length());
if (length_percentage.is_calculated())
return CSS::Size::make_calculated(length_percentage.calculated());
return CSS::Size::make_percentage(length_percentage.percentage());
}
CSSPixels FlexFormattingContext::get_pixel_width(Box const& box, CSS::Size const& size) const CSSPixels FlexFormattingContext::get_pixel_width(Box const& box, CSS::Size const& size) const
{ {
auto containing_block_width = containing_block_width_for(box); auto containing_block_width = containing_block_width_for(box);
@ -537,41 +525,30 @@ void FlexFormattingContext::determine_available_space_for_items(AvailableSpace c
} }
// https://drafts.csswg.org/css-flexbox-1/#propdef-flex-basis // https://drafts.csswg.org/css-flexbox-1/#propdef-flex-basis
CSS::FlexBasisData FlexFormattingContext::used_flex_basis_for_item(FlexItem const& item) const CSS::FlexBasis FlexFormattingContext::used_flex_basis_for_item(FlexItem const& item) const
{ {
auto flex_basis = item.box->computed_values().flex_basis(); auto flex_basis = item.box->computed_values().flex_basis();
if (flex_basis.type == CSS::FlexBasis::Auto) { if (flex_basis.has<CSS::Size>() && flex_basis.get<CSS::Size>().is_auto()) {
// https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto // https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto
// When specified on a flex item, the auto keyword retrieves the value of the main size property as the used flex-basis. // When specified on a flex item, the auto keyword retrieves the value of the main size property as the used flex-basis.
// If that value is itself auto, then the used value is content. // If that value is itself auto, then the used value is content.
auto const& main_size = is_row_layout() ? item.box->computed_values().width() : item.box->computed_values().height(); auto const& main_size = is_row_layout() ? item.box->computed_values().width() : item.box->computed_values().height();
if (main_size.is_auto()) { if (main_size.is_auto()) {
flex_basis.type = CSS::FlexBasis::Content; flex_basis = CSS::FlexBasisContent {};
} else { } else {
flex_basis.type = CSS::FlexBasis::LengthPercentage; flex_basis = main_size;
if (main_size.is_length()) {
flex_basis.length_percentage = main_size.length();
} else if (main_size.is_percentage()) {
flex_basis.length_percentage = main_size.percentage();
} else if (main_size.is_calculated()) {
flex_basis.length_percentage = CSS::LengthPercentage { main_size.calculated() };
} else {
// FIXME: Support other size values!
dbgln("FIXME: Unsupported main size for flex-basis: {}", main_size);
flex_basis.type = CSS::FlexBasis::Content;
}
} }
} }
// For example, percentage values of flex-basis are resolved against the flex items containing block // For example, percentage values of flex-basis are resolved against the flex items containing block
// (i.e. its flex container); and if that containing blocks size is indefinite, // (i.e. its flex container); and if that containing blocks size is indefinite,
// the used value for flex-basis is content. // the used value for flex-basis is content.
if (flex_basis.type == CSS::FlexBasis::LengthPercentage if (flex_basis.has<CSS::Size>()
&& flex_basis.length_percentage->is_percentage() && flex_basis.get<CSS::Size>().is_percentage()
&& !has_definite_main_size(flex_container())) { && !has_definite_main_size(flex_container())) {
flex_basis.type = CSS::FlexBasis::Content; flex_basis = CSS::FlexBasisContent {};
} }
return flex_basis; return flex_basis;
@ -609,20 +586,21 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
item.flex_base_size = [&] { item.flex_base_size = [&] {
item.used_flex_basis = used_flex_basis_for_item(item); item.used_flex_basis = used_flex_basis_for_item(item);
item.used_flex_basis_is_definite = [&](CSS::FlexBasisData const& flex_basis) -> bool { item.used_flex_basis_is_definite = [&](CSS::FlexBasis const& flex_basis) -> bool {
if (flex_basis.type != CSS::FlexBasis::LengthPercentage) if (!flex_basis.has<CSS::Size>())
return false; return false;
if (flex_basis.length_percentage->is_auto()) auto const& size = flex_basis.get<CSS::Size>();
if (size.is_auto() || size.is_min_content() || size.is_max_content() || size.is_fit_content())
return false; return false;
if (flex_basis.length_percentage->is_length()) if (size.is_length())
return true; return true;
bool can_resolve_percentages = is_row_layout() bool can_resolve_percentages = is_row_layout()
? m_flex_container_state.has_definite_width() ? m_flex_container_state.has_definite_width()
: m_flex_container_state.has_definite_height(); : m_flex_container_state.has_definite_height();
if (flex_basis.length_percentage->is_calculated()) { if (size.is_calculated()) {
auto const& calc_value = *flex_basis.length_percentage->calculated(); auto const& calc_value = size.calculated();
if (calc_value.resolves_to_percentage()) if (calc_value.resolves_to_percentage())
return can_resolve_percentages; return can_resolve_percentages;
if (calc_value.resolves_to_length()) { if (calc_value.resolves_to_length()) {
@ -632,15 +610,16 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
} }
return false; return false;
} }
VERIFY(flex_basis.length_percentage->is_percentage()); VERIFY(size.is_percentage());
return can_resolve_percentages; return can_resolve_percentages;
}(item.used_flex_basis); }(*item.used_flex_basis);
// A. If the item has a definite used flex basis, thats the flex base size. // A. If the item has a definite used flex basis, thats the flex base size.
if (item.used_flex_basis_is_definite) { if (item.used_flex_basis_is_definite) {
auto const& size = item.used_flex_basis->get<CSS::Size>();
if (is_row_layout()) if (is_row_layout())
return get_pixel_width(child_box, to_css_size(item.used_flex_basis.length_percentage.value())); return get_pixel_width(child_box, size);
return get_pixel_height(child_box, to_css_size(item.used_flex_basis.length_percentage.value())); return get_pixel_height(child_box, size);
} }
// B. If the flex item has ... // B. If the flex item has ...
@ -648,7 +627,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
// - a used flex basis of content, and // - a used flex basis of content, and
// - a definite cross size, // - a definite cross size,
if (item.box->has_preferred_aspect_ratio() if (item.box->has_preferred_aspect_ratio()
&& item.used_flex_basis.type == CSS::FlexBasis::Content && item.used_flex_basis->has<CSS::FlexBasisContent>()
&& has_definite_cross_size(item.box)) { && has_definite_cross_size(item.box)) {
// flex_base_size is calculated from definite cross size and intrinsic aspect ratio // flex_base_size is calculated from definite cross size and intrinsic aspect ratio
return adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints( return adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(
@ -662,7 +641,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
// and the flex container is being sized under a min-content or max-content constraint // and the flex container is being sized under a min-content or max-content constraint
// (e.g. when performing automatic table layout [CSS21]), size the item under that constraint. // (e.g. when performing automatic table layout [CSS21]), size the item under that constraint.
// The flex base size is the items resulting main size. // The flex base size is the items resulting main size.
if (item.used_flex_basis.type == CSS::FlexBasis::Content && m_available_space_for_items->main.is_intrinsic_sizing_constraint()) { if (item.used_flex_basis->has<CSS::FlexBasisContent>() && m_available_space_for_items->main.is_intrinsic_sizing_constraint()) {
if (m_available_space_for_items->main.is_min_content()) if (m_available_space_for_items->main.is_min_content())
return calculate_min_content_main_size(item); return calculate_min_content_main_size(item);
return calculate_max_content_main_size(item); return calculate_max_content_main_size(item);
@ -672,7 +651,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
// the available main size is infinite, and the flex items inline axis is parallel to the main axis, // the available main size is infinite, and the flex items inline axis is parallel to the main axis,
// lay the item out using the rules for a box in an orthogonal flow [CSS3-WRITING-MODES]. // lay the item out using the rules for a box in an orthogonal flow [CSS3-WRITING-MODES].
// The flex base size is the items max-content main size. // The flex base size is the items max-content main size.
if (item.used_flex_basis.type == CSS::FlexBasis::Content if (item.used_flex_basis->has<CSS::FlexBasisContent>()
// FIXME: && main_size is infinite && inline axis is parallel to the main axis // FIXME: && main_size is infinite && inline axis is parallel to the main axis
&& false && false) { && false && false) {
TODO(); TODO();
@ -704,7 +683,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
// This means that *all* intrinsic heights computed within a flex formatting context will // This means that *all* intrinsic heights computed within a flex formatting context will
// automatically use the fit-content width in case a used width is not known yet. // automatically use the fit-content width in case a used width is not known yet.
if (item.used_flex_basis.type == CSS::FlexBasis::Content) { if (item.used_flex_basis->has<CSS::FlexBasisContent>()) {
return calculate_max_content_main_size(item); return calculate_max_content_main_size(item);
} }

View file

@ -53,7 +53,7 @@ private:
struct FlexItem { struct FlexItem {
JS::NonnullGCPtr<Box> box; JS::NonnullGCPtr<Box> box;
CSS::FlexBasisData used_flex_basis {}; Optional<CSS::FlexBasis> used_flex_basis {};
bool used_flex_basis_is_definite { false }; bool used_flex_basis_is_definite { false };
CSSPixels flex_base_size { 0 }; CSSPixels flex_base_size { 0 };
CSSPixels hypothetical_main_size { 0 }; CSSPixels hypothetical_main_size { 0 };
@ -214,7 +214,7 @@ private:
virtual void parent_context_did_dimension_child_root_box() override; virtual void parent_context_did_dimension_child_root_box() override;
CSS::FlexBasisData used_flex_basis_for_item(FlexItem const&) const; CSS::FlexBasis used_flex_basis_for_item(FlexItem const&) const;
LayoutState::UsedValues& m_flex_container_state; LayoutState::UsedValues& m_flex_container_state;