mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 05:07: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:
parent
8c980cf75b
commit
8648355783
6 changed files with 44 additions and 89 deletions
|
@ -23,6 +23,9 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
struct FlexBasisContent { };
|
||||
using FlexBasis = Variant<FlexBasisContent, Size>;
|
||||
|
||||
struct AspectRatio {
|
||||
bool use_natural_aspect_ratio_if_available;
|
||||
Optional<Ratio> preferred_ratio;
|
||||
|
@ -59,6 +62,7 @@ public:
|
|||
static CSS::Visibility visibility() { return CSS::Visibility::Visible; }
|
||||
static CSS::FlexDirection flex_direction() { return CSS::FlexDirection::Row; }
|
||||
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::JustifyContent justify_content() { return CSS::JustifyContent::FlexStart; }
|
||||
static CSS::AlignContent align_content() { return CSS::AlignContent::Stretch; }
|
||||
|
@ -164,17 +168,6 @@ struct TransformOrigin {
|
|||
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 {
|
||||
Color color {};
|
||||
CSS::Length offset_x { Length::make_px(0) };
|
||||
|
@ -246,7 +239,7 @@ public:
|
|||
CSS::WhiteSpace white_space() const { return m_inherited.white_space; }
|
||||
CSS::FlexDirection flex_direction() const { return m_noninherited.flex_direction; }
|
||||
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_shrink() const { return m_noninherited.flex_shrink; }
|
||||
int order() const { return m_noninherited.order; }
|
||||
|
@ -398,7 +391,7 @@ protected:
|
|||
Vector<BackgroundLayerData> background_layers;
|
||||
CSS::FlexDirection flex_direction { InitialValues::flex_direction() };
|
||||
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_shrink { InitialValues::flex_shrink() };
|
||||
int order { InitialValues::order() };
|
||||
|
@ -492,7 +485,7 @@ public:
|
|||
BorderData& border_bottom() { return m_noninherited.border_bottom; }
|
||||
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_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_shrink(float value) { m_noninherited.flex_shrink = value; }
|
||||
void set_order(int value) { m_noninherited.order = value; }
|
||||
|
|
|
@ -559,18 +559,14 @@ ErrorOr<RefPtr<StyleValue const>> ResolvedCSSStyleDeclaration::style_value_for_p
|
|||
return NumberStyleValue::create(layout_node.computed_values().fill_opacity());
|
||||
case PropertyID::FillRule:
|
||||
return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().fill_rule()));
|
||||
case PropertyID::FlexBasis: {
|
||||
switch (layout_node.computed_values().flex_basis().type) {
|
||||
case FlexBasis::Content:
|
||||
case PropertyID::FlexBasis:
|
||||
return layout_node.computed_values().flex_basis().visit(
|
||||
[](CSS::FlexBasisContent const&) -> ErrorOr<RefPtr<StyleValue const>> {
|
||||
return IdentifierStyleValue::create(ValueID::Content);
|
||||
case FlexBasis::LengthPercentage:
|
||||
return style_value_for_length_percentage(*layout_node.computed_values().flex_basis().length_percentage);
|
||||
case FlexBasis::Auto:
|
||||
return IdentifierStyleValue::create(ValueID::Auto);
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
break;
|
||||
},
|
||||
[&](CSS::Size const& size) -> ErrorOr<RefPtr<StyleValue const>> {
|
||||
return style_value_for_size(size);
|
||||
});
|
||||
case PropertyID::FlexDirection:
|
||||
return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().flex_direction()));
|
||||
case PropertyID::FlexGrow:
|
||||
|
@ -839,7 +835,6 @@ ErrorOr<RefPtr<StyleValue const>> ResolvedCSSStyleDeclaration::style_value_for_p
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Optional<StyleProperty> ResolvedCSSStyleDeclaration::property(PropertyID property_id) const
|
||||
{
|
||||
|
|
|
@ -327,26 +327,14 @@ Optional<CSS::FlexWrap> StyleProperties::flex_wrap() const
|
|||
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);
|
||||
|
||||
if (value->is_identifier() && value->to_identifier() == CSS::ValueID::Content)
|
||||
return { { CSS::FlexBasis::Content, {} } };
|
||||
return CSS::FlexBasisContent {};
|
||||
|
||||
if (value->has_auto())
|
||||
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 {};
|
||||
return size_value(CSS::PropertyID::FlexBasis);
|
||||
}
|
||||
|
||||
float StyleProperties::flex_grow() const
|
||||
|
|
|
@ -69,7 +69,7 @@ public:
|
|||
Optional<CSS::ListStylePosition> list_style_position() const;
|
||||
Optional<CSS::FlexDirection> flex_direction() 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_shrink() const;
|
||||
int order() const;
|
||||
|
|
|
@ -27,18 +27,6 @@ template<typename T>
|
|||
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
|
||||
{
|
||||
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
|
||||
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();
|
||||
|
||||
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
|
||||
// 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.
|
||||
auto const& main_size = is_row_layout() ? item.box->computed_values().width() : item.box->computed_values().height();
|
||||
|
||||
if (main_size.is_auto()) {
|
||||
flex_basis.type = CSS::FlexBasis::Content;
|
||||
flex_basis = CSS::FlexBasisContent {};
|
||||
} else {
|
||||
flex_basis.type = CSS::FlexBasis::LengthPercentage;
|
||||
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;
|
||||
}
|
||||
flex_basis = main_size;
|
||||
}
|
||||
}
|
||||
|
||||
// For example, percentage values of flex-basis are resolved against the flex item’s containing block
|
||||
// (i.e. its flex container); and if that containing block’s size is indefinite,
|
||||
// the used value for flex-basis is content.
|
||||
if (flex_basis.type == CSS::FlexBasis::LengthPercentage
|
||||
&& flex_basis.length_percentage->is_percentage()
|
||||
if (flex_basis.has<CSS::Size>()
|
||||
&& flex_basis.get<CSS::Size>().is_percentage()
|
||||
&& !has_definite_main_size(flex_container())) {
|
||||
flex_basis.type = CSS::FlexBasis::Content;
|
||||
flex_basis = CSS::FlexBasisContent {};
|
||||
}
|
||||
|
||||
return flex_basis;
|
||||
|
@ -609,20 +586,21 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
|
|||
item.flex_base_size = [&] {
|
||||
item.used_flex_basis = used_flex_basis_for_item(item);
|
||||
|
||||
item.used_flex_basis_is_definite = [&](CSS::FlexBasisData const& flex_basis) -> bool {
|
||||
if (flex_basis.type != CSS::FlexBasis::LengthPercentage)
|
||||
item.used_flex_basis_is_definite = [&](CSS::FlexBasis const& flex_basis) -> bool {
|
||||
if (!flex_basis.has<CSS::Size>())
|
||||
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;
|
||||
if (flex_basis.length_percentage->is_length())
|
||||
if (size.is_length())
|
||||
return true;
|
||||
|
||||
bool can_resolve_percentages = is_row_layout()
|
||||
? m_flex_container_state.has_definite_width()
|
||||
: m_flex_container_state.has_definite_height();
|
||||
|
||||
if (flex_basis.length_percentage->is_calculated()) {
|
||||
auto const& calc_value = *flex_basis.length_percentage->calculated();
|
||||
if (size.is_calculated()) {
|
||||
auto const& calc_value = size.calculated();
|
||||
if (calc_value.resolves_to_percentage())
|
||||
return can_resolve_percentages;
|
||||
if (calc_value.resolves_to_length()) {
|
||||
|
@ -632,15 +610,16 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
|
|||
}
|
||||
return false;
|
||||
}
|
||||
VERIFY(flex_basis.length_percentage->is_percentage());
|
||||
VERIFY(size.is_percentage());
|
||||
return can_resolve_percentages;
|
||||
}(item.used_flex_basis);
|
||||
}(*item.used_flex_basis);
|
||||
|
||||
// A. If the item has a definite used flex basis, that’s the flex base size.
|
||||
if (item.used_flex_basis_is_definite) {
|
||||
auto const& size = item.used_flex_basis->get<CSS::Size>();
|
||||
if (is_row_layout())
|
||||
return get_pixel_width(child_box, to_css_size(item.used_flex_basis.length_percentage.value()));
|
||||
return get_pixel_height(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, size);
|
||||
}
|
||||
|
||||
// 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 definite cross size,
|
||||
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)) {
|
||||
// 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(
|
||||
|
@ -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
|
||||
// (e.g. when performing automatic table layout [CSS21]), size the item under that constraint.
|
||||
// The flex base size is the item’s 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())
|
||||
return calculate_min_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 item’s 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].
|
||||
// The flex base size is the item’s 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
|
||||
&& false && false) {
|
||||
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
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ private:
|
|||
|
||||
struct FlexItem {
|
||||
JS::NonnullGCPtr<Box> box;
|
||||
CSS::FlexBasisData used_flex_basis {};
|
||||
Optional<CSS::FlexBasis> used_flex_basis {};
|
||||
bool used_flex_basis_is_definite { false };
|
||||
CSSPixels flex_base_size { 0 };
|
||||
CSSPixels hypothetical_main_size { 0 };
|
||||
|
@ -214,7 +214,7 @@ private:
|
|||
|
||||
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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue