LibWeb/CSS: Use Size in GridSize

Reduces a bunch of duplicate logic here for holding different sizing
functions, and also removes a user of `auto` Length.
This commit is contained in:
Sam Atkins 2025-09-04 11:38:27 +01:00
commit ae40c7ed95
Notes: github-actions[bot] 2025-09-04 12:34:21 +00:00
5 changed files with 92 additions and 102 deletions

View file

@ -11,41 +11,26 @@
namespace Web::CSS {
GridSize::GridSize(Type type, LengthPercentage length_percentage)
: m_value(move(length_percentage))
{
VERIFY(type == Type::FitContent);
m_type = type;
}
GridSize::GridSize(LengthPercentage length_percentage)
: m_type(Type::LengthPercentage)
, m_value(move(length_percentage))
GridSize::GridSize(Size size)
: m_value(move(size))
{
}
GridSize::GridSize(Flex flex_factor)
: m_type(Type::FlexibleLength)
, m_value(move(flex_factor))
: m_value(move(flex_factor))
{
}
GridSize::GridSize(Type type)
: m_value { Empty() }
{
VERIFY(type == Type::MinContent || type == Type::MaxContent);
m_type = type;
}
GridSize::~GridSize() = default;
bool GridSize::is_auto(Layout::AvailableSize const& available_size) const
{
if (m_type == Type::LengthPercentage) {
auto& length_percentage = m_value.get<LengthPercentage>();
if (length_percentage.contains_percentage())
if (auto const* size = m_value.get_pointer<Size>()) {
if (size->is_auto())
return true;
if (size->contains_percentage())
return !available_size.is_definite();
return length_percentage.is_auto();
return false;
}
return false;
@ -53,59 +38,83 @@ bool GridSize::is_auto(Layout::AvailableSize const& available_size) const
bool GridSize::is_fixed(Layout::AvailableSize const& available_size) const
{
if (m_type == Type::LengthPercentage) {
auto& length_percentage = m_value.get<LengthPercentage>();
if (length_percentage.contains_percentage())
if (auto const* size = m_value.get_pointer<Size>()) {
if (!size->is_length_percentage())
return false;
if (size->contains_percentage())
return available_size.is_definite();
return !length_percentage.is_auto();
return true;
}
return false;
}
bool GridSize::is_flexible_length() const
{
return m_value.has<Flex>();
}
bool GridSize::is_fit_content() const
{
if (auto const* size = m_value.get_pointer<Size>())
return size->is_fit_content();
return false;
}
bool GridSize::is_max_content() const
{
if (auto const* size = m_value.get_pointer<Size>())
return size->is_max_content();
return false;
}
bool GridSize::is_min_content() const
{
if (auto const* size = m_value.get_pointer<Size>())
return size->is_min_content();
return false;
}
bool GridSize::is_intrinsic(Layout::AvailableSize const& available_size) const
{
return is_auto(available_size) || is_max_content() || is_min_content() || is_fit_content();
return m_value.visit(
[&available_size](Size const& size) {
return size.is_auto()
|| size.is_max_content()
|| size.is_min_content()
|| size.is_fit_content()
|| (size.contains_percentage() && !available_size.is_definite());
},
[](Flex const&) {
return false;
});
}
bool GridSize::is_definite() const
{
return m_value.visit(
[](Size const& size) {
return (size.is_length() && !size.length().is_auto()) || size.is_percentage() || size.is_calculated();
},
[](Flex const&) { return false; });
}
GridSize GridSize::make_auto()
{
return GridSize(CSS::Length::make_auto());
}
Size GridSize::css_size() const
{
VERIFY(m_type == Type::LengthPercentage || m_type == Type::FitContent);
auto& length_percentage = m_value.get<LengthPercentage>();
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());
return GridSize(Size::make_auto());
}
String GridSize::to_string(SerializationMode mode) const
{
switch (m_type) {
case Type::LengthPercentage:
return m_value.get<LengthPercentage>().to_string(mode);
case Type::FitContent:
return MUST(String::formatted("fit-content({})", m_value.get<LengthPercentage>().to_string(mode)));
case Type::FlexibleLength:
return m_value.get<Flex>().to_string();
case Type::MaxContent:
return "max-content"_string;
case Type::MinContent:
return "min-content"_string;
}
VERIFY_NOT_REACHED();
return m_value.visit([mode](auto const& it) { return it.to_string(mode); });
}
GridMinMax::GridMinMax(GridSize min_grid_size, GridSize max_grid_size)
: m_min_grid_size(min_grid_size)
, m_max_grid_size(max_grid_size)
: m_min_grid_size(move(min_grid_size))
, m_max_grid_size(move(max_grid_size))
{
}

View file

@ -10,57 +10,40 @@
#include <AK/FlyString.h>
#include <AK/Vector.h>
#include <LibWeb/CSS/PercentageOr.h>
#include <LibWeb/CSS/Size.h>
#include <LibWeb/Layout/AvailableSpace.h>
namespace Web::CSS {
class GridSize {
public:
enum class Type {
LengthPercentage,
FlexibleLength,
FitContent,
MaxContent,
MinContent,
};
GridSize(Type, LengthPercentage);
GridSize(LengthPercentage);
GridSize(Size);
GridSize(Flex);
GridSize(Type);
~GridSize();
static GridSize make_auto();
Type type() const { return m_type; }
bool is_auto(Layout::AvailableSize const&) const;
bool is_fixed(Layout::AvailableSize const&) const;
bool is_flexible_length() const { return m_type == Type::FlexibleLength; }
bool is_fit_content() const { return m_type == Type::FitContent; }
bool is_max_content() const { return m_type == Type::MaxContent; }
bool is_min_content() const { return m_type == Type::MinContent; }
bool is_flexible_length() const;
bool is_fit_content() const;
bool is_max_content() const;
bool is_min_content() const;
LengthPercentage const& length_percentage() const { return m_value.get<LengthPercentage>(); }
Size css_size() const { return m_value.get<Size>(); }
double flex_factor() const { return m_value.get<Flex>().to_fr(); }
// https://www.w3.org/TR/css-grid-2/#layout-algorithm
// An intrinsic sizing function (min-content, max-content, auto, fit-content()).
bool is_intrinsic(Layout::AvailableSize const&) const;
bool is_definite() const
{
return type() == Type::LengthPercentage && !length_percentage().is_auto();
}
Size css_size() const;
bool is_definite() const;
String to_string(SerializationMode) const;
bool operator==(GridSize const& other) const = default;
private:
Type m_type;
Variant<Empty, LengthPercentage, Flex> m_value;
Variant<Size, Flex> m_value;
};
class GridMinMax {

View file

@ -298,7 +298,7 @@ private:
Optional<GridSize> parse_grid_track_breadth(TokenStream<ComponentValue>&);
Optional<GridSize> parse_grid_inflexible_breadth(TokenStream<ComponentValue>&);
Optional<GridSize> parse_grid_fixed_breadth(TokenStream<ComponentValue>&);
Optional<LengthPercentage> parse_grid_fixed_breadth(TokenStream<ComponentValue>&);
Optional<GridLineNames> parse_grid_line_names(TokenStream<ComponentValue>&);

View file

@ -3582,7 +3582,7 @@ Optional<GridSize> Parser::parse_grid_inflexible_breadth(TokenStream<ComponentVa
// <inflexible-breadth> = <length-percentage [0,∞]> | min-content | max-content | auto
if (auto fixed_breadth = parse_grid_fixed_breadth(tokens); fixed_breadth.has_value())
return fixed_breadth;
return GridSize { Size::make_length_percentage(fixed_breadth.value()) };
auto transaction = tokens.begin_transaction();
if (!tokens.has_next_token())
@ -3591,11 +3591,11 @@ Optional<GridSize> Parser::parse_grid_inflexible_breadth(TokenStream<ComponentVa
auto const& token = tokens.consume_a_token();
if (token.is_ident("max-content"sv)) {
transaction.commit();
return GridSize(GridSize::Type::MaxContent);
return GridSize(Size::make_max_content());
}
if (token.is_ident("min-content"sv)) {
transaction.commit();
return GridSize(GridSize::Type::MinContent);
return GridSize(Size::make_min_content());
}
if (token.is_ident("auto"sv)) {
transaction.commit();
@ -3606,7 +3606,7 @@ Optional<GridSize> Parser::parse_grid_inflexible_breadth(TokenStream<ComponentVa
}
// https://www.w3.org/TR/css-grid-2/#typedef-fixed-breadth
Optional<GridSize> Parser::parse_grid_fixed_breadth(TokenStream<ComponentValue>& tokens)
Optional<LengthPercentage> Parser::parse_grid_fixed_breadth(TokenStream<ComponentValue>& tokens)
{
// <fixed-breadth> = <length-percentage [0,∞]>
@ -3619,7 +3619,7 @@ Optional<GridSize> Parser::parse_grid_fixed_breadth(TokenStream<ComponentValue>&
if (length_percentage->is_percentage() && length_percentage->percentage().value() < 0)
return {};
transaction.commit();
return GridSize(length_percentage.release_value());
return length_percentage.release_value();
}
// https://www.w3.org/TR/css-grid-2/#typedef-line-names
@ -3871,7 +3871,7 @@ Optional<ExplicitGridTrack> Parser::parse_grid_track_size(TokenStream<ComponentV
if (function_tokens.has_next_token())
return {};
transaction.commit();
return ExplicitGridTrack(GridSize(GridSize::Type::FitContent, maybe_length_percentage->length_percentage()));
return ExplicitGridTrack(GridSize(Size::make_fit_content(maybe_length_percentage.release_value())));
}
}
@ -3895,14 +3895,14 @@ Optional<ExplicitGridTrack> Parser::parse_grid_fixed_size(TokenStream<ComponentV
auto const& function_token = token.function();
if (function_token.name.equals_ignoring_ascii_case("minmax"sv)) {
{
GridMinMaxParamParser parse_min = [this](auto& tokens) { return parse_grid_fixed_breadth(tokens); };
GridMinMaxParamParser parse_min = [this](auto& tokens) { return parse_grid_fixed_breadth(tokens).map([](auto& it) { return GridSize(Size::make_length_percentage(it)); }); };
GridMinMaxParamParser parse_max = [this](auto& tokens) { return parse_grid_track_breadth(tokens); };
if (auto result = parse_grid_minmax(tokens, parse_min, parse_max); result.has_value())
return result;
}
{
GridMinMaxParamParser parse_min = [this](auto& tokens) { return parse_grid_inflexible_breadth(tokens); };
GridMinMaxParamParser parse_max = [this](auto& tokens) { return parse_grid_fixed_breadth(tokens); };
GridMinMaxParamParser parse_max = [this](auto& tokens) { return parse_grid_fixed_breadth(tokens).map([](auto& it) { return GridSize(Size::make_length_percentage(it)); }); };
if (auto result = parse_grid_minmax(tokens, parse_min, parse_max); result.has_value())
return result;
}
@ -3912,7 +3912,7 @@ Optional<ExplicitGridTrack> Parser::parse_grid_fixed_size(TokenStream<ComponentV
}
if (auto fixed_breadth = parse_grid_fixed_breadth(tokens); fixed_breadth.has_value()) {
return ExplicitGridTrack(fixed_breadth.value());
return ExplicitGridTrack(GridSize { Size::make_length_percentage(fixed_breadth.release_value()) });
}
return {};

View file

@ -189,8 +189,8 @@ GridFormattingContext::GridTrack GridFormattingContext::GridTrack::create_auto()
GridFormattingContext::GridTrack GridFormattingContext::GridTrack::create_gap(CSSPixels size)
{
return GridTrack {
.min_track_sizing_function = CSS::GridSize(CSS::Length::make_px(size)),
.max_track_sizing_function = CSS::GridSize(CSS::Length::make_px(size)),
.min_track_sizing_function = CSS::GridSize(CSS::Size::make_px(size)),
.max_track_sizing_function = CSS::GridSize(CSS::Size::make_px(size)),
.base_size = size,
.is_gap = true,
};
@ -207,9 +207,7 @@ GridFormattingContext::~GridFormattingContext() = default;
CSSPixels GridFormattingContext::resolve_definite_track_size(CSS::GridSize const& grid_size, AvailableSpace const& available_space) const
{
VERIFY(grid_size.is_definite());
if (grid_size.type() == CSS::GridSize::Type::LengthPercentage)
return grid_size.css_size().to_px(grid_container(), available_space.width.to_px_or_zero());
VERIFY_NOT_REACHED();
}
int GridFormattingContext::count_of_repeated_auto_fill_or_fit_tracks(GridDimension dimension, CSS::ExplicitGridTrack const& repeated_track)
@ -1856,8 +1854,8 @@ void GridFormattingContext::collapse_auto_fit_tracks_if_needed(GridDimension dim
continue;
// NOTE: A collapsed track is treated as having a fixed track sizing function of 0px
tracks[track_index].min_track_sizing_function = CSS::GridSize(CSS::Length::make_px(0));
tracks[track_index].max_track_sizing_function = CSS::GridSize(CSS::Length::make_px(0));
tracks[track_index].min_track_sizing_function = CSS::GridSize(CSS::Size::make_px(0));
tracks[track_index].max_track_sizing_function = CSS::GridSize(CSS::Size::make_px(0));
}
}
@ -2114,7 +2112,7 @@ void GridFormattingContext::run(AvailableSpace const& available_space)
if (i < tracks.size()) {
auto const& track = tracks[i];
result.append(CSS::ExplicitGridTrack { CSS::GridSize { CSS::LengthPercentage(CSS::Length::make_px(track.base_size)) } });
result.append(CSS::ExplicitGridTrack { CSS::GridSize { CSS::Size::make_px(track.base_size) } });
}
}
return result;
@ -2646,7 +2644,7 @@ CSSPixels GridFormattingContext::content_based_minimum_size(GridItem const& item
spans_only_tracks_with_limited_max_track_sizing_function = false;
break;
}
sum_of_max_sizing_functions += track.max_track_sizing_function.length_percentage().to_px(item.box, m_available_space->width.to_px_or_zero());
sum_of_max_sizing_functions += track.max_track_sizing_function.css_size().to_px(item.box, m_available_space->width.to_px_or_zero());
}
if (spans_only_tracks_with_limited_max_track_sizing_function) {
result = min(result, sum_of_max_sizing_functions);