From b2b677e9842775346d66a2697b29cfa96f11fdb0 Mon Sep 17 00:00:00 2001 From: martinfalisse Date: Sun, 30 Oct 2022 13:27:57 +0100 Subject: [PATCH] LibWeb: Refactor GridTrackSize classes Refactor various classes in the GridTrackSize file for the incoming named_tracks feature. Previously the ExplicitTrackSizing had mixed responsiblities with the newly-named GridRepeat class. This made it so it was not possible to have multiple repeats within a single 'GridTrackSizeList' definition. The MetaGridTrackSize class had both the responsibilities of being a container for minmax values as well as for simple GridSizes. By uniting the different possible values (repeat, minmax, default) into the ExplicitGridTrack class are able to be more expressive as to the different grid size modalities. The GridTrackSizeList will be useful as compared to a Vector since this way can keep track of the declared line names. These same line names are able to be declared within the values of a repeat function, hence the presence of a GridTrackSizeList inside the GridRepeat class. --- .../Libraries/LibWeb/CSS/ComputedValues.h | 16 +- .../Libraries/LibWeb/CSS/GridTrackSize.cpp | 168 ++++--- Userland/Libraries/LibWeb/CSS/GridTrackSize.h | 153 ++++-- .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 434 ++++++++++-------- Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 4 + .../Libraries/LibWeb/CSS/StyleProperties.cpp | 8 +- .../Libraries/LibWeb/CSS/StyleProperties.h | 4 +- Userland/Libraries/LibWeb/CSS/StyleValue.cpp | 19 +- Userland/Libraries/LibWeb/CSS/StyleValue.h | 23 +- Userland/Libraries/LibWeb/Forward.h | 8 +- .../LibWeb/Layout/GridFormattingContext.cpp | 178 +++++-- .../LibWeb/Layout/GridFormattingContext.h | 4 +- 12 files changed, 639 insertions(+), 380 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h index 65e43bf8fa..7fc77ee7d7 100644 --- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -64,8 +64,8 @@ public: static CSS::Size height() { return CSS::Size::make_auto(); } static CSS::Size min_height() { return CSS::Size::make_auto(); } static CSS::Size max_height() { return CSS::Size::make_none(); } - static CSS::ExplicitTrackSizing grid_template_columns() { return CSS::ExplicitTrackSizing::make_auto(); } - static CSS::ExplicitTrackSizing grid_template_rows() { return CSS::ExplicitTrackSizing::make_auto(); } + static CSS::GridTrackSizeList grid_template_columns() { return CSS::GridTrackSizeList::make_auto(); } + static CSS::GridTrackSizeList grid_template_rows() { return CSS::GridTrackSizeList::make_auto(); } static CSS::GridTrackPlacement grid_column_end() { return CSS::GridTrackPlacement::make_auto(); } static CSS::GridTrackPlacement grid_column_start() { return CSS::GridTrackPlacement::make_auto(); } static CSS::GridTrackPlacement grid_row_end() { return CSS::GridTrackPlacement::make_auto(); } @@ -182,8 +182,8 @@ public: CSS::Size const& min_height() const { return m_noninherited.min_height; } CSS::Size const& max_height() const { return m_noninherited.max_height; } Variant const& vertical_align() const { return m_noninherited.vertical_align; } - CSS::ExplicitTrackSizing const& grid_template_columns() const { return m_noninherited.grid_template_columns; } - CSS::ExplicitTrackSizing const& grid_template_rows() const { return m_noninherited.grid_template_rows; } + CSS::GridTrackSizeList const& grid_template_columns() const { return m_noninherited.grid_template_columns; } + CSS::GridTrackSizeList const& grid_template_rows() const { return m_noninherited.grid_template_rows; } CSS::GridTrackPlacement const& grid_column_end() const { return m_noninherited.grid_column_end; } CSS::GridTrackPlacement const& grid_column_start() const { return m_noninherited.grid_column_start; } CSS::GridTrackPlacement const& grid_row_end() const { return m_noninherited.grid_row_end; } @@ -304,8 +304,8 @@ protected: CSS::BoxSizing box_sizing { InitialValues::box_sizing() }; CSS::ContentData content; Variant vertical_align { InitialValues::vertical_align() }; - CSS::ExplicitTrackSizing grid_template_columns; - CSS::ExplicitTrackSizing grid_template_rows; + CSS::GridTrackSizeList grid_template_columns; + CSS::GridTrackSizeList grid_template_rows; CSS::GridTrackPlacement grid_column_end { InitialValues::grid_column_end() }; CSS::GridTrackPlacement grid_column_start { InitialValues::grid_column_start() }; CSS::GridTrackPlacement grid_row_end { InitialValues::grid_row_end() }; @@ -382,8 +382,8 @@ public: void set_box_sizing(CSS::BoxSizing value) { m_noninherited.box_sizing = value; } void set_vertical_align(Variant value) { m_noninherited.vertical_align = move(value); } void set_visibility(CSS::Visibility value) { m_inherited.visibility = value; } - void set_grid_template_columns(CSS::ExplicitTrackSizing value) { m_noninherited.grid_template_columns = move(value); } - void set_grid_template_rows(CSS::ExplicitTrackSizing value) { m_noninherited.grid_template_rows = move(value); } + void set_grid_template_columns(CSS::GridTrackSizeList value) { m_noninherited.grid_template_columns = move(value); } + void set_grid_template_rows(CSS::GridTrackSizeList value) { m_noninherited.grid_template_rows = move(value); } void set_grid_column_end(CSS::GridTrackPlacement value) { m_noninherited.grid_column_end = value; } void set_grid_column_start(CSS::GridTrackPlacement value) { m_noninherited.grid_column_start = value; } void set_grid_row_end(CSS::GridTrackPlacement value) { m_noninherited.grid_row_end = value; } diff --git a/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp b/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp index 0097d1e3d6..850788387c 100644 --- a/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp +++ b/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp @@ -12,34 +12,39 @@ namespace Web::CSS { -GridTrackSize::GridTrackSize(Length length) +GridSize::GridSize(Length length) : m_type(Type::Length) , m_length(length) { } -GridTrackSize::GridTrackSize(Percentage percentage) +GridSize::GridSize(Percentage percentage) : m_type(Type::Percentage) , m_length { Length::make_px(0) } , m_percentage(percentage) { } -GridTrackSize::GridTrackSize(float flexible_length) +GridSize::GridSize(float flexible_length) : m_type(Type::FlexibleLength) , m_length { Length::make_px(0) } , m_flexible_length(flexible_length) { } -GridTrackSize::~GridTrackSize() = default; - -GridTrackSize GridTrackSize::make_auto() +GridSize::GridSize() + : m_length { Length::make_auto() } { - return GridTrackSize(CSS::Length::make_auto()); } -String GridTrackSize::to_string() const +GridSize::~GridSize() = default; + +GridSize GridSize::make_auto() +{ + return GridSize(CSS::Length::make_auto()); +} + +String GridSize::to_string() const { switch (m_type) { case Type::Length: @@ -52,78 +57,123 @@ String GridTrackSize::to_string() const VERIFY_NOT_REACHED(); } -Length GridTrackSize::length() const +Length GridSize::length() const { return m_length; } -MetaGridTrackSize::MetaGridTrackSize(GridTrackSize grid_track_size) - : m_min_grid_track_size(grid_track_size) - , m_max_grid_track_size(grid_track_size) +GridMinMax::GridMinMax(GridSize min_grid_size, GridSize max_grid_size) + : m_min_grid_size(min_grid_size) + , m_max_grid_size(max_grid_size) { } -MetaGridTrackSize::MetaGridTrackSize(GridTrackSize min_grid_track_size, GridTrackSize max_grid_track_size) - : m_min_grid_track_size(min_grid_track_size) - , m_max_grid_track_size(max_grid_track_size) - , m_is_min_max(true) +String GridMinMax::to_string() const { + StringBuilder builder; + builder.append("minmax("sv); + builder.appendff("{}", m_min_grid_size.to_string()); + builder.append(", "sv); + builder.appendff("{}", m_max_grid_size.to_string()); + builder.append(")"sv); + return builder.to_string(); } -String MetaGridTrackSize::to_string() const -{ - if (m_is_min_max) { - StringBuilder builder; - builder.append("minmax("sv); - builder.appendff("{}", m_min_grid_track_size.to_string()); - builder.append(", "sv); - builder.appendff("{}", m_max_grid_track_size.to_string()); - builder.append(")"sv); - return builder.to_string(); - } - return String::formatted("{}", m_min_grid_track_size.to_string()); -} - -ExplicitTrackSizing::ExplicitTrackSizing() -{ -} - -ExplicitTrackSizing::ExplicitTrackSizing(Vector meta_grid_track_sizes) - : m_meta_grid_track_sizes(meta_grid_track_sizes) -{ -} - -ExplicitTrackSizing::ExplicitTrackSizing(Vector meta_grid_track_sizes, int repeat_count) - : m_meta_grid_track_sizes(meta_grid_track_sizes) - , m_is_repeat(true) +GridRepeat::GridRepeat(GridTrackSizeList grid_track_size_list, int repeat_count) + : m_type(Type::Default) + , m_grid_track_size_list(grid_track_size_list) , m_repeat_count(repeat_count) { } -ExplicitTrackSizing::ExplicitTrackSizing(Vector meta_grid_track_sizes, Type type) - : m_meta_grid_track_sizes(meta_grid_track_sizes) - , m_is_auto_fill(type == Type::AutoFill) - , m_is_auto_fit(type == Type::AutoFit) +GridRepeat::GridRepeat(GridTrackSizeList grid_track_size_list, Type type) + : m_type(type) + , m_grid_track_size_list(grid_track_size_list) { } -String ExplicitTrackSizing::to_string() const +GridRepeat::GridRepeat() +{ +} + +String GridRepeat::to_string() const { StringBuilder builder; - if (m_is_repeat) { - builder.append("repeat("sv); - builder.append(m_repeat_count); - builder.append(", "sv); + builder.append("repeat("sv); + switch (m_type) { + case Type::AutoFit: + builder.append("auto-fill"sv); + break; + case Type::AutoFill: + builder.append("auto-fit"sv); + break; + case Type::Default: + builder.appendff("{}", m_repeat_count); + break; + default: + VERIFY_NOT_REACHED(); } - for (int _ = 0; _ < m_repeat_count; ++_) { - for (size_t y = 0; y < m_meta_grid_track_sizes.size(); ++y) { - builder.append(m_meta_grid_track_sizes[y].to_string()); - if (y != m_meta_grid_track_sizes.size() - 1) - builder.append(" "sv); - } + builder.append(", "sv); + builder.appendff("{}", m_grid_track_size_list.to_string()); + builder.append(")"sv); + return builder.to_string(); +} + +ExplicitGridTrack::ExplicitGridTrack(CSS::GridMinMax grid_minmax) + : m_type(Type::MinMax) + , m_grid_minmax(grid_minmax) +{ +} + +ExplicitGridTrack::ExplicitGridTrack(CSS::GridRepeat grid_repeat) + : m_type(Type::Repeat) + , m_grid_repeat(grid_repeat) +{ +} + +ExplicitGridTrack::ExplicitGridTrack(CSS::GridSize grid_size) + : m_type(Type::Default) + , m_grid_size(grid_size) +{ +} + +String ExplicitGridTrack::to_string() const +{ + switch (m_type) { + case Type::MinMax: + return m_grid_minmax.to_string(); + case Type::Repeat: + return m_grid_repeat.to_string(); + case Type::Default: + return m_grid_size.to_string(); + default: + VERIFY_NOT_REACHED(); + } +} + +GridTrackSizeList::GridTrackSizeList(Vector track_list) + : m_track_list(track_list) +{ +} + +GridTrackSizeList::GridTrackSizeList() + : m_track_list({}) +{ +} + +GridTrackSizeList GridTrackSizeList::make_auto() +{ + return GridTrackSizeList(); +} + +String GridTrackSizeList::to_string() const +{ + StringBuilder builder; + for (size_t i = 0; i < m_track_list.size(); ++i) { + builder.append(m_track_list[i].to_string()); + if (i < m_track_list.size() - 1) + builder.append(" "sv); } - if (m_is_repeat) - builder.append(")"sv); return builder.to_string(); } diff --git a/Userland/Libraries/LibWeb/CSS/GridTrackSize.h b/Userland/Libraries/LibWeb/CSS/GridTrackSize.h index 7fc5f8e265..6a269ae568 100644 --- a/Userland/Libraries/LibWeb/CSS/GridTrackSize.h +++ b/Userland/Libraries/LibWeb/CSS/GridTrackSize.h @@ -12,7 +12,7 @@ namespace Web::CSS { -class GridTrackSize { +class GridSize { public: enum class Type { Length, @@ -21,12 +21,13 @@ public: // TODO: Max-Content }; - GridTrackSize(Length); - GridTrackSize(Percentage); - GridTrackSize(float); - ~GridTrackSize(); + GridSize(Length); + GridSize(Percentage); + GridSize(float); + GridSize(); + ~GridSize(); - static GridTrackSize make_auto(); + static GridSize make_auto(); Type type() const { return m_type; } @@ -52,7 +53,7 @@ public: } String to_string() const; - bool operator==(GridTrackSize const& other) const + bool operator==(GridSize const& other) const { return m_type == other.type() && m_length == other.length() @@ -69,63 +70,133 @@ private: float m_flexible_length { 0 }; }; -class MetaGridTrackSize { +class GridMinMax { public: - MetaGridTrackSize(CSS::GridTrackSize); - MetaGridTrackSize(CSS::GridTrackSize min_grid_track_size, CSS::GridTrackSize max_grid_track_size); + GridMinMax(CSS::GridSize min_grid_size, CSS::GridSize max_grid_size); + GridMinMax() = default; - bool is_min_max() const { return m_is_min_max; } - - GridTrackSize grid_track_size() const& { return m_min_grid_track_size; } - GridTrackSize min_grid_track_size() const& { return m_min_grid_track_size; } - GridTrackSize max_grid_track_size() const& { return m_max_grid_track_size; } + GridSize min_grid_size() const& { return m_min_grid_size; } + GridSize max_grid_size() const& { return m_max_grid_size; } String to_string() const; - bool operator==(MetaGridTrackSize const& other) const + bool operator==(GridMinMax const& other) const { - return m_min_grid_track_size == other.min_grid_track_size() - && m_max_grid_track_size == other.max_grid_track_size(); + return m_min_grid_size == other.min_grid_size() + && m_max_grid_size == other.max_grid_size(); } private: - GridTrackSize m_min_grid_track_size; - GridTrackSize m_max_grid_track_size; - bool m_is_min_max { false }; + GridSize m_min_grid_size; + GridSize m_max_grid_size; }; -class ExplicitTrackSizing { +class GridTrackSizeList { +public: + GridTrackSizeList(Vector track_list); + GridTrackSizeList(); + + static GridTrackSizeList make_auto(); + + Vector track_list() const { return m_track_list; } + String to_string() const; + bool operator==(GridTrackSizeList const& other) const + { + return m_track_list == other.track_list(); + } + +private: + Vector m_track_list; +}; + +class GridRepeat { public: enum class Type { AutoFit, AutoFill, + Default, }; + GridRepeat(GridTrackSizeList, int repeat_count); + GridRepeat(GridTrackSizeList, Type); + GridRepeat(); - ExplicitTrackSizing(); - ExplicitTrackSizing(Vector); - ExplicitTrackSizing(Vector, int repeat_count); - ExplicitTrackSizing(Vector, Type); - - static ExplicitTrackSizing make_auto() { return ExplicitTrackSizing(); }; - - bool is_repeat() const { return m_is_repeat; } - bool is_auto_fill() const { return m_is_auto_fill; } - bool is_auto_fit() const { return m_is_auto_fit; } - int repeat_count() const { return m_repeat_count; } - - Vector meta_grid_track_sizes() const& { return m_meta_grid_track_sizes; } + bool is_auto_fill() const { return m_type == Type::AutoFill; } + bool is_auto_fit() const { return m_type == Type::AutoFit; } + bool is_default() const { return m_type == Type::Default; } + int repeat_count() const + { + VERIFY(is_default()); + return m_repeat_count; + } + GridTrackSizeList grid_track_size_list() const& { return m_grid_track_size_list; } + Type type() const& { return m_type; } String to_string() const; - bool operator==(ExplicitTrackSizing const& other) const + bool operator==(GridRepeat const& other) const { - return m_meta_grid_track_sizes == other.meta_grid_track_sizes(); + if (m_type != other.type()) + return false; + if (m_type == Type::Default && m_repeat_count != other.repeat_count()) + return false; + return m_grid_track_size_list == other.grid_track_size_list(); } private: - Vector m_meta_grid_track_sizes; - bool m_is_repeat { false }; - bool m_is_auto_fill { false }; - bool m_is_auto_fit { false }; + Type m_type; + GridTrackSizeList m_grid_track_size_list; int m_repeat_count { 0 }; }; +class ExplicitGridTrack { +public: + enum class Type { + MinMax, + Repeat, + Default, + }; + ExplicitGridTrack(CSS::GridRepeat); + ExplicitGridTrack(CSS::GridMinMax); + ExplicitGridTrack(CSS::GridSize); + + bool is_repeat() const { return m_type == Type::Repeat; } + GridRepeat repeat() const + { + VERIFY(is_repeat()); + return m_grid_repeat; + } + + bool is_minmax() const { return m_type == Type::MinMax; } + GridMinMax minmax() const + { + VERIFY(is_minmax()); + return m_grid_minmax; + } + + bool is_default() const { return m_type == Type::Default; } + GridSize grid_size() const + { + VERIFY(is_default()); + return m_grid_size; + } + + Type type() const { return m_type; } + + String to_string() const; + bool operator==(ExplicitGridTrack const& other) const + { + if (is_repeat() && other.is_repeat()) + return m_grid_repeat == other.repeat(); + if (is_minmax() && other.is_minmax()) + return m_grid_minmax == other.minmax(); + if (is_default() && other.is_default()) + return m_grid_size == other.grid_size(); + return false; + } + +private: + Type m_type; + GridRepeat m_grid_repeat; + GridMinMax m_grid_minmax; + GridSize m_grid_size; +}; + } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index f43ac3eeee..ef47008ed9 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -5407,221 +5407,267 @@ RefPtr Parser::parse_as_css_value(PropertyID property_id) return parsed_value.release_value(); } +Optional Parser::parse_grid_size(ComponentValue const& component_value) +{ + // FIXME: Parse calc here if necessary + if (component_value.is_function()) + return {}; + auto token = component_value.token(); + if (token.is(Token::Type::Dimension) && token.dimension_unit().equals_ignoring_case("fr"sv)) { + float numeric_value = token.dimension_value(); + if (numeric_value) + return GridSize(numeric_value); + } + if (token.is(Token::Type::Ident) && token.ident().equals_ignoring_case("auto"sv)) + return GridSize::make_auto(); + auto dimension = parse_dimension(token); + if (!dimension.has_value()) + return {}; + if (dimension->is_length()) + return GridSize(dimension->length()); + else if (dimension->is_percentage()) + return GridSize(dimension->percentage()); + return {}; +} + +Optional Parser::parse_min_max(Vector const& component_values) +{ + // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax + // 'minmax(min, max)' + // Defines a size range greater than or equal to min and less than or equal to max. If the max is + // less than the min, then the max will be floored by the min (essentially yielding minmax(min, + // min)). As a maximum, a value sets the track’s flex factor; it is invalid as a minimum. + auto function_tokens = TokenStream(component_values); + auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens); + if (comma_separated_list.size() != 2) + return {}; + + TokenStream part_one_tokens { comma_separated_list[0] }; + part_one_tokens.skip_whitespace(); + if (!part_one_tokens.has_next_token()) + return {}; + auto current_token = part_one_tokens.next_token(); + auto min_grid_size = parse_grid_size(current_token); + + TokenStream part_two_tokens { comma_separated_list[1] }; + part_two_tokens.skip_whitespace(); + if (!part_two_tokens.has_next_token()) + return {}; + current_token = part_two_tokens.next_token(); + auto max_grid_size = parse_grid_size(current_token); + + if (min_grid_size.has_value() && max_grid_size.has_value()) { + // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax + // As a maximum, a value sets the track’s flex factor; it is invalid as a minimum. + if (min_grid_size.value().is_flexible_length()) + return {}; + return CSS::GridMinMax(min_grid_size.value(), max_grid_size.value()); + } + return {}; +} + +Optional Parser::parse_repeat(Vector const& component_values) +{ + // https://www.w3.org/TR/css-grid-2/#repeat-syntax + // 7.2.3.1. Syntax of repeat() + // The generic form of the repeat() syntax is, approximately, + // repeat( [ | auto-fill | auto-fit ] , ) + auto is_auto_fill = false; + auto is_auto_fit = false; + auto function_tokens = TokenStream(component_values); + auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens); + if (comma_separated_list.size() != 2) + return {}; + // The first argument specifies the number of repetitions. + TokenStream part_one_tokens { comma_separated_list[0] }; + part_one_tokens.skip_whitespace(); + if (!part_one_tokens.has_next_token()) + return {}; + auto current_token = part_one_tokens.next_token().token(); + + auto repeat_count = 0; + if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && current_token.number_value() > 0) + repeat_count = current_token.number_value(); + else if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto-fill"sv)) + is_auto_fill = true; + else if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto-fit"sv)) + is_auto_fit = true; + + // The second argument is a track list, which is repeated that number of times. + TokenStream part_two_tokens { comma_separated_list[1] }; + part_two_tokens.skip_whitespace(); + if (!part_two_tokens.has_next_token()) + return {}; + + Vector repeat_params; + while (part_two_tokens.has_next_token()) { + auto token = part_two_tokens.next_token(); + if (token.is_block()) { + part_two_tokens.skip_whitespace(); + } else { + auto track_sizing_function = parse_track_sizing_function(token); + if (!track_sizing_function.has_value()) + return {}; + // However, there are some restrictions: + // The repeat() notation can’t be nested. + if (track_sizing_function.value().is_repeat()) + return {}; + // Automatic repetitions (auto-fill or auto-fit) cannot be combined with intrinsic or flexible sizes. + if (track_sizing_function.value().is_default() && track_sizing_function.value().grid_size().is_flexible_length() && (is_auto_fill || is_auto_fit)) + return {}; + repeat_params.append(track_sizing_function.value()); + part_two_tokens.skip_whitespace(); + } + } + + // Thus the precise syntax of the repeat() notation has several forms: + // = repeat( [ ] , [ ? ]+ ? ) + // = repeat( [ auto-fill | auto-fit ] , [ ? ]+ ? ) + // = repeat( [ ] , [ ? ]+ ? ) + // = repeat( [ | auto-fill ], +) + + // The variant can represent the repetition of any , but is limited to a + // fixed number of repetitions. + + // The variant can repeat automatically to fill a space, but requires definite track + // sizes so that the number of repetitions can be calculated. It can only appear once in the track + // list, but the same track list can also contain s. + + // The variant is for adding line names to subgrids. It can only be used with the + // subgrid keyword and cannot specify track sizes, only line names. + + // If a repeat() function that is not a ends up placing two adjacent to + // each other, the name lists are merged. For example, repeat(2, [a] 1fr [b]) is equivalent to [a] + // 1fr [b a] 1fr [b]. + if (is_auto_fill) + return CSS::GridRepeat(CSS::GridTrackSizeList(repeat_params), CSS::GridRepeat::Type::AutoFill); + else if (is_auto_fit) + return CSS::GridRepeat(CSS::GridTrackSizeList(repeat_params), CSS::GridRepeat::Type::AutoFit); + else + return CSS::GridRepeat(CSS::GridTrackSizeList(repeat_params), repeat_count); +} + +Optional Parser::parse_track_sizing_function(ComponentValue const& token) +{ + if (token.is_function()) { + auto const& function_token = token.function(); + if (function_token.name().equals_ignoring_case("repeat"sv)) { + auto maybe_repeat = parse_repeat(function_token.values()); + if (maybe_repeat.has_value()) + return CSS::ExplicitGridTrack(maybe_repeat.value()); + else + return {}; + } else if (function_token.name().equals_ignoring_case("minmax"sv)) { + auto maybe_min_max_value = parse_min_max(function_token.values()); + if (maybe_min_max_value.has_value()) + return CSS::ExplicitGridTrack(maybe_min_max_value.value()); + else + return {}; + } + return {}; + } else if (token.is(Token::Type::Ident) && token.token().ident().equals_ignoring_case("auto"sv)) { + return CSS::ExplicitGridTrack(GridSize(Length::make_auto())); + } else if (token.is_block()) { + return {}; + } else { + auto grid_size = parse_grid_size(token); + if (!grid_size.has_value()) + return {}; + return CSS::ExplicitGridTrack(grid_size.value()); + } +} + RefPtr Parser::parse_grid_track_sizes(Vector const& component_values) { - auto parse_grid_track_size = [&](ComponentValue const& component_value) -> Optional { - // FIXME: Parse calc here if necessary - if (component_value.is_function()) - return {}; - auto token = component_value.token(); - if (token.is(Token::Type::Dimension) && token.dimension_unit().equals_ignoring_case("fr"sv)) { - float numeric_value = token.dimension_value(); - if (numeric_value) - return GridTrackSize(numeric_value); + Vector track_list; + TokenStream tokens { component_values }; + while (tokens.has_next_token()) { + auto token = tokens.next_token(); + if (token.is_block()) { + + } else { + auto track_sizing_function = parse_track_sizing_function(token); + if (!track_sizing_function.has_value()) + return GridTrackSizeStyleValue::make_auto(); + // FIXME: Handle multiple repeat values (should combine them here, or remove + // any other ones if the first one is auto-fill, etc.) + track_list.append(track_sizing_function.value()); } - if (token.is(Token::Type::Ident) && token.ident().equals_ignoring_case("auto"sv)) - return GridTrackSize::make_auto(); - auto dimension = parse_dimension(token); - if (!dimension.has_value()) - return {}; - if (dimension->is_length()) - return GridTrackSize(dimension->length()); - else if (dimension->is_percentage()) - return GridTrackSize(dimension->percentage()); - return {}; - }; - - auto parse_min_max = [&](Vector const& component_values) -> Optional { - // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax - // 'minmax(min, max)' - // Defines a size range greater than or equal to min and less than or equal to max. If the max is - // less than the min, then the max will be floored by the min (essentially yielding minmax(min, - // min)). As a maximum, a value sets the track’s flex factor; it is invalid as a minimum. - auto function_tokens = TokenStream(component_values); - auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens); - if (comma_separated_list.size() != 2) - return MetaGridTrackSize(GridTrackSize::make_auto()); - - TokenStream part_one_tokens { comma_separated_list[0] }; - part_one_tokens.skip_whitespace(); - if (!part_one_tokens.has_next_token()) - return MetaGridTrackSize(GridTrackSize::make_auto()); - auto current_token = part_one_tokens.next_token(); - auto min_grid_track_size = parse_grid_track_size(current_token); - - TokenStream part_two_tokens { comma_separated_list[1] }; - part_two_tokens.skip_whitespace(); - if (!part_two_tokens.has_next_token()) - return MetaGridTrackSize(GridTrackSize::make_auto()); - current_token = part_two_tokens.next_token(); - auto max_grid_track_size = parse_grid_track_size(current_token); - - if (min_grid_track_size.has_value() && max_grid_track_size.has_value()) { - // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax - // As a maximum, a value sets the track’s flex factor; it is invalid as a minimum. - if (min_grid_track_size.value().is_flexible_length()) - return {}; - return MetaGridTrackSize(min_grid_track_size.value(), max_grid_track_size.value()); - } - return MetaGridTrackSize(GridTrackSize::make_auto(), GridTrackSize::make_auto()); - }; - - Vector params; - for (auto const& component_value : component_values) { - if (component_value.is_function()) { - auto const& function_token = component_value.function(); - // https://www.w3.org/TR/css-grid-2/#repeat-syntax - // 7.2.3.1. Syntax of repeat() - // The generic form of the repeat() syntax is, approximately, - // repeat( [ | auto-fill | auto-fit ] , ) - if (function_token.name().equals_ignoring_case("repeat"sv)) { - auto is_auto_fill = false; - auto is_auto_fit = false; - auto function_tokens = TokenStream(function_token.values()); - auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens); - if (comma_separated_list.size() != 2) - continue; - // The first argument specifies the number of repetitions. - TokenStream part_one_tokens { comma_separated_list[0] }; - part_one_tokens.skip_whitespace(); - if (!part_one_tokens.has_next_token()) - continue; - auto current_token = part_one_tokens.next_token().token(); - - auto repeat_count = 0; - if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && current_token.number_value() > 0) - repeat_count = current_token.number_value(); - else if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto-fill"sv)) - is_auto_fill = true; - else if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto-fit"sv)) - is_auto_fit = true; - - // The second argument is a track list, which is repeated that number of times. - TokenStream part_two_tokens { comma_separated_list[1] }; - part_two_tokens.skip_whitespace(); - if (!part_two_tokens.has_next_token()) - continue; - - Vector repeat_params; - while (true) { - part_two_tokens.skip_whitespace(); - auto current_component_value = part_two_tokens.next_token(); - if (current_component_value.is_function()) { - // However, there are some restrictions: - // The repeat() notation can’t be nested. - if (current_component_value.function().name().equals_ignoring_case("repeat"sv)) - return GridTrackSizeStyleValue::create({}); - if (current_component_value.function().name().equals_ignoring_case("minmax"sv)) { - auto maybe_min_max_value = parse_min_max(current_component_value.function().values()); - if (maybe_min_max_value.has_value()) - repeat_params.append(maybe_min_max_value.value()); - else - return GridTrackSizeStyleValue::create({}); - } - // FIXME: Implement other function values possible within repeat - } else if (current_component_value.is_block()) { - // FIXME: Implement things like grid-template-columns: repeat(1, [col-start] 8); - } else { - auto grid_track_size = parse_grid_track_size(current_component_value); - if (!grid_track_size.has_value()) - return GridTrackSizeStyleValue::create({}); - // Automatic repetitions (auto-fill or auto-fit) cannot be combined with intrinsic or flexible sizes. - if (grid_track_size.value().is_flexible_length() && (is_auto_fill || is_auto_fit)) - return GridTrackSizeStyleValue::create({}); - repeat_params.append(grid_track_size.value()); - } - part_two_tokens.skip_whitespace(); - if (!part_two_tokens.has_next_token()) - break; - } - - // Thus the precise syntax of the repeat() notation has several forms: - // = repeat( [ ] , [ ? ]+ ? ) - // = repeat( [ auto-fill | auto-fit ] , [ ? ]+ ? ) - // = repeat( [ ] , [ ? ]+ ? ) - // = repeat( [ | auto-fill ], +) - - // The variant can represent the repetition of any , but is limited to a - // fixed number of repetitions. - - // The variant can repeat automatically to fill a space, but requires definite track - // sizes so that the number of repetitions can be calculated. It can only appear once in the track - // list, but the same track list can also contain s. - - // The variant is for adding line names to subgrids. It can only be used with the - // subgrid keyword and cannot specify track sizes, only line names. - - // If a repeat() function that is not a ends up placing two adjacent to - // each other, the name lists are merged. For example, repeat(2, [a] 1fr [b]) is equivalent to [a] - // 1fr [b a] 1fr [b]. - if (is_auto_fill) - return GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing(repeat_params, CSS::ExplicitTrackSizing::Type::AutoFill)); - if (is_auto_fit) - return GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing(repeat_params, CSS::ExplicitTrackSizing::Type::AutoFit)); - return GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing(repeat_params, repeat_count)); - } else if (function_token.name().equals_ignoring_case("minmax"sv)) { - auto maybe_min_max_value = parse_min_max(function_token.values()); - if (maybe_min_max_value.has_value()) - params.append(maybe_min_max_value.value()); - else - return GridTrackSizeStyleValue::create({}); - } - continue; - } - if (component_value.is_block()) { - params.append(GridTrackSize(Length::make_auto())); - continue; - } - if (component_value.is(Token::Type::Ident) && component_value.token().ident().equals_ignoring_case("auto"sv)) { - params.append(GridTrackSize(Length::make_auto())); - continue; - } - auto grid_track_size = parse_grid_track_size(component_value); - if (!grid_track_size.has_value()) - return GridTrackSizeStyleValue::create({}); - params.append(grid_track_size.value()); } - return GridTrackSizeStyleValue::create(params); + return GridTrackSizeStyleValue::create(CSS::GridTrackSizeList(track_list)); } RefPtr Parser::parse_grid_track_placement(Vector const& component_values) { + // https://www.w3.org/TR/css-grid-2/#line-placement + // Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties + // = + // auto | + // | + // [ && ? ] | + // [ span && [ || ] ] + auto is_auto = [](Token token) -> bool { + if (token.is(Token::Type::Ident) && token.ident().equals_ignoring_case("auto"sv)) + return true; + return false; + }; + auto is_span = [](Token token) -> bool { + if (token.is(Token::Type::Ident) && token.ident().equals_ignoring_case("span"sv)) + return true; + return false; + }; + auto is_valid_integer = [](Token token) -> bool { + // An value of zero makes the declaration invalid. + if (token.is(Token::Type::Number) && token.number().is_integer() && token.number_value() != 0) + return true; + return false; + }; auto tokens = TokenStream { component_values }; + tokens.skip_whitespace(); auto current_token = tokens.next_token().token(); if (!tokens.has_next_token()) { - if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto"sv)) + if (is_auto(current_token)) return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement()); - // https://drafts.csswg.org/css-grid/#grid-placement-span-int - // If the is omitted, it defaults to 1. - if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("span"sv)) + if (is_span(current_token)) return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(1, true)); - // https://drafts.csswg.org/css-grid/#grid-placement-int - // [ | ] && ? - // An value of zero makes the declaration invalid. - if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && current_token.number_value() != 0) + if (is_valid_integer(current_token)) return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(static_cast(current_token.number_value()))); return {}; } - auto is_span = false; - if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("span"sv)) { - is_span = true; + auto span_value = false; + auto span_or_position_value = 0; + while (true) { + if (is_auto(current_token)) + return {}; + if (is_span(current_token)) { + if (span_value == false) + span_value = true; + else + return {}; + } + if (is_valid_integer(current_token)) { + if (span_or_position_value == 0) + span_or_position_value = static_cast(current_token.number_value()); + else + return {}; + } tokens.skip_whitespace(); - current_token = tokens.next_token().token(); + if (tokens.has_next_token()) + current_token = tokens.next_token().token(); + else + break; } - // https://drafts.csswg.org/css-grid/#grid-placement-int - // [ | ] && ? - // An value of zero makes the declaration invalid. - if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && current_token.number_value() != 0 && !tokens.has_next_token()) { - // https://drafts.csswg.org/css-grid/#grid-placement-span-int - // Negative integers or zero are invalid. - if (is_span && static_cast(current_token.number_value()) < 1) - return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(1, is_span)); - return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(static_cast(current_token.number_value()), is_span)); - } - return {}; + // Negative integers or zero are invalid. + if (span_value && span_or_position_value < 1) + return {}; + + // If the is omitted, it defaults to 1. + if (span_or_position_value == 0) + span_or_position_value = 1; + return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(span_or_position_value, span_value)); } RefPtr Parser::parse_grid_track_placement_shorthand_value(Vector const& component_values) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 896127cb2f..7d31d8ad93 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -250,6 +250,10 @@ private: Optional parse_ratio(TokenStream&); Optional parse_unicode_range(TokenStream&); Optional parse_unicode_range(StringView); + Optional parse_grid_size(ComponentValue const&); + Optional parse_min_max(Vector const&); + Optional parse_repeat(Vector const&); + Optional parse_track_sizing_function(ComponentValue const&); enum class AllowedDataUrlType { None, diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 96ebd559e7..58bb482f93 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -710,16 +710,16 @@ Optional StyleProperties::font_variant() const return value_id_to_font_variant(value->to_identifier()); } -CSS::ExplicitTrackSizing StyleProperties::grid_template_columns() const +CSS::GridTrackSizeList StyleProperties::grid_template_columns() const { auto value = property(CSS::PropertyID::GridTemplateColumns); - return value->as_explicit_track_sizing().explicit_track_sizing(); + return value->as_grid_track_size_list().grid_track_size_list(); } -CSS::ExplicitTrackSizing StyleProperties::grid_template_rows() const +CSS::GridTrackSizeList StyleProperties::grid_template_rows() const { auto value = property(CSS::PropertyID::GridTemplateRows); - return value->as_explicit_track_sizing().explicit_track_sizing(); + return value->as_grid_track_size_list().grid_track_size_list(); } CSS::GridTrackPlacement StyleProperties::grid_column_end() const diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index 33be113427..55f51fd020 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -85,8 +85,8 @@ public: Optional pointer_events() const; Variant vertical_align() const; Optional font_variant() const; - CSS::ExplicitTrackSizing grid_template_columns() const; - CSS::ExplicitTrackSizing grid_template_rows() const; + CSS::GridTrackSizeList grid_template_columns() const; + CSS::GridTrackSizeList grid_template_rows() const; CSS::GridTrackPlacement grid_column_end() const; CSS::GridTrackPlacement grid_column_start() const; CSS::GridTrackPlacement grid_row_end() const; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 009850fca3..869cab95c5 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -168,9 +168,9 @@ LengthStyleValue const& StyleValue::as_length() const return static_cast(*this); } -GridTrackSizeStyleValue const& StyleValue::as_explicit_track_sizing() const +GridTrackSizeStyleValue const& StyleValue::as_grid_track_size_list() const { - VERIFY(is_explicit_track_sizing()); + VERIFY(is_grid_track_size_list()); return static_cast(*this); } @@ -1418,15 +1418,15 @@ bool GridTrackPlacementStyleValue::equals(StyleValue const& other) const String GridTrackSizeStyleValue::to_string() const { - return m_explicit_track_sizing.to_string(); + return m_grid_track_size_list.to_string(); } bool GridTrackSizeStyleValue::equals(StyleValue const& other) const { if (type() != other.type()) return false; - auto const& typed_other = other.as_explicit_track_sizing(); - return m_explicit_track_sizing == typed_other.explicit_track_sizing(); + auto const& typed_other = other.as_grid_track_size_list(); + return m_grid_track_size_list == typed_other.grid_track_size_list(); } String IdentifierStyleValue::to_string() const @@ -2145,9 +2145,14 @@ NonnullRefPtr GridTrackPlacementStyleValue::create return adopt_ref(*new GridTrackPlacementStyleValue(grid_track_placement)); } -NonnullRefPtr GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing explicit_track_sizing) +NonnullRefPtr GridTrackSizeStyleValue::create(CSS::GridTrackSizeList grid_track_size_list) { - return adopt_ref(*new GridTrackSizeStyleValue(explicit_track_sizing)); + return adopt_ref(*new GridTrackSizeStyleValue(grid_track_size_list)); +} + +NonnullRefPtr GridTrackSizeStyleValue::make_auto() +{ + return adopt_ref(*new GridTrackSizeStyleValue(CSS::GridTrackSizeList())); } NonnullRefPtr RectStyleValue::create(EdgeRect rect) diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index 4425544753..991b57a4cf 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -174,7 +174,6 @@ public: Calculated, Color, Content, - ExplicitTrackSizing, FilterValueList, Flex, FlexFlow, @@ -182,6 +181,7 @@ public: Frequency, GridTrackPlacement, GridTrackPlacementShorthand, + GridTrackSizeList, Identifier, Image, Inherit, @@ -226,7 +226,7 @@ public: bool is_frequency() const { return type() == Type::Frequency; } bool is_grid_track_placement() const { return type() == Type::GridTrackPlacement; } bool is_grid_track_placement_shorthand() const { return type() == Type::GridTrackPlacementShorthand; } - bool is_explicit_track_sizing() const { return type() == Type::ExplicitTrackSizing; } + bool is_grid_track_size_list() const { return type() == Type::GridTrackSizeList; } bool is_identifier() const { return type() == Type::Identifier; } bool is_image() const { return type() == Type::Image; } bool is_inherit() const { return type() == Type::Inherit; } @@ -269,7 +269,7 @@ public: FrequencyStyleValue const& as_frequency() const; GridTrackPlacementShorthandStyleValue const& as_grid_track_placement_shorthand() const; GridTrackPlacementStyleValue const& as_grid_track_placement() const; - GridTrackSizeStyleValue const& as_explicit_track_sizing() const; + GridTrackSizeStyleValue const& as_grid_track_size_list() const; IdentifierStyleValue const& as_identifier() const; ImageStyleValue const& as_image() const; InheritStyleValue const& as_inherit() const; @@ -310,7 +310,7 @@ public: FrequencyStyleValue& as_frequency() { return const_cast(const_cast(*this).as_frequency()); } GridTrackPlacementShorthandStyleValue& as_grid_track_placement_shorthand() { return const_cast(const_cast(*this).as_grid_track_placement_shorthand()); } GridTrackPlacementStyleValue& as_grid_track_placement() { return const_cast(const_cast(*this).as_grid_track_placement()); } - GridTrackSizeStyleValue& as_explicit_track_sizing() { return const_cast(const_cast(*this).as_explicit_track_sizing()); } + GridTrackSizeStyleValue& as_grid_track_size_list() { return const_cast(const_cast(*this).as_grid_track_size_list()); } IdentifierStyleValue& as_identifier() { return const_cast(const_cast(*this).as_identifier()); } ImageStyleValue& as_image() { return const_cast(const_cast(*this).as_image()); } InheritStyleValue& as_inherit() { return const_cast(const_cast(*this).as_inherit()); } @@ -1048,21 +1048,24 @@ private: class GridTrackSizeStyleValue final : public StyleValue { public: - static NonnullRefPtr create(CSS::ExplicitTrackSizing explicit_track_sizing); + static NonnullRefPtr create(CSS::GridTrackSizeList grid_track_size_list); virtual ~GridTrackSizeStyleValue() override = default; - CSS::ExplicitTrackSizing explicit_track_sizing() const { return m_explicit_track_sizing; } + static NonnullRefPtr make_auto(); + + CSS::GridTrackSizeList grid_track_size_list() const { return m_grid_track_size_list; } + virtual String to_string() const override; virtual bool equals(StyleValue const& other) const override; private: - explicit GridTrackSizeStyleValue(CSS::ExplicitTrackSizing explicit_track_sizing) - : StyleValue(Type::ExplicitTrackSizing) - , m_explicit_track_sizing(explicit_track_sizing) + explicit GridTrackSizeStyleValue(CSS::GridTrackSizeList grid_track_size_list) + : StyleValue(Type::GridTrackSizeList) + , m_grid_track_size_list(grid_track_size_list) { } - CSS::ExplicitTrackSizing m_explicit_track_sizing; + CSS::GridTrackSizeList m_grid_track_size_list; }; class IdentifierStyleValue final : public StyleValue { diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index d726199b08..e39ecebdee 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -50,7 +50,7 @@ class CSSStyleSheet; class CSSSupportsRule; class Display; class ElementInlineCSSStyleDeclaration; -class ExplicitTrackSizing; +class ExplicitGridTrack; class FilterValueListStyleValue; class FlexFlowStyleValue; class FlexStyleValue; @@ -59,10 +59,13 @@ class FontStyleValue; class Frequency; class FrequencyPercentage; class FrequencyStyleValue; +class GridMinMax; +class GridRepeat; +class GridSize; class GridTrackPlacement; class GridTrackPlacementShorthandStyleValue; class GridTrackPlacementStyleValue; -class GridTrackSize; +class GridTrackSizeList; class GridTrackSizeStyleValue; class IdentifierStyleValue; class ImageStyleValue; @@ -79,7 +82,6 @@ class MediaList; class MediaQuery; class MediaQueryList; class MediaQueryListEvent; -class MetaGridTrackSize; class Number; class NumericStyleValue; class OverflowStyleValue; diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp index f946b0e2e8..de7075bd85 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp @@ -20,6 +20,8 @@ GridFormattingContext::~GridFormattingContext() = default; void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const& available_space) { auto& box_state = m_state.get_mutable(box); + auto grid_template_columns = box.computed_values().grid_template_columns(); + auto grid_template_rows = box.computed_values().grid_template_rows(); auto should_skip_is_anonymous_text_run = [&](Box& child_box) -> bool { if (child_box.is_anonymous() && !child_box.first_child_of_type()) { bool contains_only_white_space = true; @@ -36,16 +38,16 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const return false; }; - auto resolve_definite_track_size = [&](CSS::GridTrackSize const& grid_track_size) -> float { - VERIFY(grid_track_size.is_definite()); - switch (grid_track_size.type()) { - case CSS::GridTrackSize::Type::Length: - if (grid_track_size.length().is_auto()) + auto resolve_definite_track_size = [&](CSS::GridSize const& grid_size) -> float { + VERIFY(grid_size.is_definite()); + switch (grid_size.type()) { + case CSS::GridSize::Type::Length: + if (grid_size.length().is_auto()) break; - return grid_track_size.length().to_px(box); + return grid_size.length().to_px(box); break; - case CSS::GridTrackSize::Type::Percentage: - return grid_track_size.percentage().as_fraction() * box_state.content_width(); + case CSS::GridSize::Type::Percentage: + return grid_size.percentage().as_fraction() * box_state.content_width(); break; default: VERIFY_NOT_REACHED(); @@ -75,8 +77,20 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const boxes_to_place.append(child_box); return IterationDecision::Continue; }); - auto column_repeat_count = box.computed_values().grid_template_columns().is_repeat() ? box.computed_values().grid_template_columns().repeat_count() : 1; - auto row_repeat_count = box.computed_values().grid_template_rows().is_repeat() ? box.computed_values().grid_template_rows().repeat_count() : 1; + auto column_count = 0; + for (auto const& explicit_grid_track : grid_template_columns.track_list()) { + if (explicit_grid_track.is_repeat() && explicit_grid_track.repeat().is_default()) + column_count += explicit_grid_track.repeat().repeat_count() * explicit_grid_track.repeat().grid_track_size_list().track_list().size(); + else + column_count += 1; + } + auto row_count = 0; + for (auto const& explicit_grid_track : grid_template_rows.track_list()) { + if (explicit_grid_track.is_repeat() && explicit_grid_track.repeat().is_default()) + row_count += explicit_grid_track.repeat().repeat_count() * explicit_grid_track.repeat().grid_track_size_list().track_list().size(); + else + row_count += 1; + } // https://www.w3.org/TR/css-grid-2/#auto-repeat // 7.2.3.2. Repeat-to-fill: auto-fill and auto-fit repetitions @@ -85,7 +99,9 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const // the span is already fulfilled). // Otherwise on a standalone axis, when auto-fill is given as the repetition number - if (box.computed_values().grid_template_columns().is_auto_fill() || box.computed_values().grid_template_columns().is_auto_fit()) { + if (grid_template_columns.track_list().size() == 1 + && grid_template_columns.track_list().first().is_repeat() + && (grid_template_columns.track_list().first().repeat().is_auto_fill() || grid_template_columns.track_list().first().repeat().is_auto_fit())) { // If the grid container has a definite size or max size in the relevant axis, then the number of // repetitions is the largest possible positive integer that does not cause the grid to overflow the // content box of its grid container @@ -95,21 +111,28 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const // function otherwise, flooring the max track sizing function by the min track sizing function if both // are definite, and taking gap into account) // FIXME: take gap into account - for (auto& meta_grid_track : box.computed_values().grid_template_columns().meta_grid_track_sizes()) { - if (meta_grid_track.max_grid_track_size().is_definite() && !meta_grid_track.min_grid_track_size().is_definite()) - sum_of_grid_track_sizes += resolve_definite_track_size(meta_grid_track.max_grid_track_size()); - else if (meta_grid_track.min_grid_track_size().is_definite() && !meta_grid_track.max_grid_track_size().is_definite()) - sum_of_grid_track_sizes += resolve_definite_track_size(meta_grid_track.min_grid_track_size()); - else if (meta_grid_track.min_grid_track_size().is_definite() && meta_grid_track.max_grid_track_size().is_definite()) - sum_of_grid_track_sizes += min(resolve_definite_track_size(meta_grid_track.min_grid_track_size()), resolve_definite_track_size(meta_grid_track.max_grid_track_size())); + for (auto& explicit_grid_track : grid_template_columns.track_list().first().repeat().grid_track_size_list().track_list()) { + auto track_sizing_function = explicit_grid_track; + if (track_sizing_function.is_minmax()) { + if (track_sizing_function.minmax().max_grid_size().is_definite() && !track_sizing_function.minmax().min_grid_size().is_definite()) + sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().max_grid_size()); + else if (track_sizing_function.minmax().min_grid_size().is_definite() && !track_sizing_function.minmax().max_grid_size().is_definite()) + sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().min_grid_size()); + else if (track_sizing_function.minmax().min_grid_size().is_definite() && track_sizing_function.minmax().max_grid_size().is_definite()) + sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.minmax().min_grid_size()), resolve_definite_track_size(track_sizing_function.minmax().max_grid_size())); + } else { + sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.grid_size()), resolve_definite_track_size(track_sizing_function.grid_size())); + } } - column_repeat_count = max(1, static_cast(get_free_space_x(box) / sum_of_grid_track_sizes)); + column_count = max(1, static_cast(get_free_space_x(box) / sum_of_grid_track_sizes)); // For the purpose of finding the number of auto-repeated tracks in a standalone axis, the UA must // floor the track size to a UA-specified value to avoid division by zero. It is suggested that this // floor be 1px. } - if (box.computed_values().grid_template_rows().is_auto_fill() || box.computed_values().grid_template_rows().is_auto_fit()) { + if (grid_template_rows.track_list().size() == 1 + && grid_template_rows.track_list().first().is_repeat() + && (grid_template_rows.track_list().first().repeat().is_auto_fill() || grid_template_rows.track_list().first().repeat().is_auto_fit())) { // If the grid container has a definite size or max size in the relevant axis, then the number of // repetitions is the largest possible positive integer that does not cause the grid to overflow the // content box of its grid container @@ -119,15 +142,20 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const // function otherwise, flooring the max track sizing function by the min track sizing function if both // are definite, and taking gap into account) // FIXME: take gap into account - for (auto& meta_grid_track : box.computed_values().grid_template_rows().meta_grid_track_sizes()) { - if (meta_grid_track.max_grid_track_size().is_definite() && !meta_grid_track.min_grid_track_size().is_definite()) - sum_of_grid_track_sizes += resolve_definite_track_size(meta_grid_track.max_grid_track_size()); - else if (meta_grid_track.min_grid_track_size().is_definite() && !meta_grid_track.max_grid_track_size().is_definite()) - sum_of_grid_track_sizes += resolve_definite_track_size(meta_grid_track.min_grid_track_size()); - else if (meta_grid_track.min_grid_track_size().is_definite() && meta_grid_track.max_grid_track_size().is_definite()) - sum_of_grid_track_sizes += min(resolve_definite_track_size(meta_grid_track.min_grid_track_size()), resolve_definite_track_size(meta_grid_track.max_grid_track_size())); + for (auto& explicit_grid_track : grid_template_rows.track_list().first().repeat().grid_track_size_list().track_list()) { + auto track_sizing_function = explicit_grid_track; + if (track_sizing_function.is_minmax()) { + if (track_sizing_function.minmax().max_grid_size().is_definite() && !track_sizing_function.minmax().min_grid_size().is_definite()) + sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().max_grid_size()); + else if (track_sizing_function.minmax().min_grid_size().is_definite() && !track_sizing_function.minmax().max_grid_size().is_definite()) + sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().min_grid_size()); + else if (track_sizing_function.minmax().min_grid_size().is_definite() && track_sizing_function.minmax().max_grid_size().is_definite()) + sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.minmax().min_grid_size()), resolve_definite_track_size(track_sizing_function.minmax().max_grid_size())); + } else { + sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.grid_size()), resolve_definite_track_size(track_sizing_function.grid_size())); + } } - row_repeat_count = max(1, static_cast(get_free_space_y(box) / sum_of_grid_track_sizes)); + row_count = max(1, static_cast(get_free_space_y(box) / sum_of_grid_track_sizes)); // The auto-fit keyword behaves the same as auto-fill, except that after grid item placement any // empty repeated tracks are collapsed. An empty track is one with no in-flow grid items placed into @@ -140,7 +168,7 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const // floor the track size to a UA-specified value to avoid division by zero. It is suggested that this // floor be 1px. } - auto occupation_grid = OccupationGrid(column_repeat_count * box.computed_values().grid_template_columns().meta_grid_track_sizes().size(), row_repeat_count * box.computed_values().grid_template_rows().meta_grid_track_sizes().size()); + auto occupation_grid = OccupationGrid(column_count, row_count); // https://drafts.csswg.org/css-grid/#auto-placement-algo // 8.5. Grid Item Placement Algorithm @@ -594,19 +622,67 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const // - A flexible sizing function (). // The grid sizing algorithm defines how to resolve these sizing constraints into used track sizes. - for (int x = 0; x < column_repeat_count; ++x) { - for (auto& meta_grid_track_size : box.computed_values().grid_template_columns().meta_grid_track_sizes()) - m_grid_columns.append({ meta_grid_track_size.min_grid_track_size(), meta_grid_track_size.max_grid_track_size() }); + for (auto const& track_in_list : grid_template_columns.track_list()) { + auto repeat_count = (track_in_list.is_repeat() && track_in_list.repeat().is_default()) ? track_in_list.repeat().repeat_count() : 1; + if (track_in_list.is_repeat()) { + if (track_in_list.repeat().is_auto_fill() || track_in_list.repeat().is_auto_fit()) + repeat_count = column_count; + } + for (auto _ = 0; _ < repeat_count; _++) { + switch (track_in_list.type()) { + case CSS::ExplicitGridTrack::Type::MinMax: + m_grid_columns.append({ track_in_list.minmax().min_grid_size(), track_in_list.minmax().max_grid_size() }); + break; + case CSS::ExplicitGridTrack::Type::Repeat: + for (auto& explicit_grid_track : track_in_list.repeat().grid_track_size_list().track_list()) { + auto track_sizing_function = explicit_grid_track; + if (track_sizing_function.is_minmax()) + m_grid_columns.append({ track_sizing_function.minmax().min_grid_size(), track_sizing_function.minmax().max_grid_size() }); + else + m_grid_columns.append({ track_sizing_function.grid_size(), track_sizing_function.grid_size() }); + } + break; + case CSS::ExplicitGridTrack::Type::Default: + m_grid_columns.append({ track_in_list.grid_size(), track_in_list.grid_size() }); + break; + default: + VERIFY_NOT_REACHED(); + } + } } - for (int x = 0; x < row_repeat_count; ++x) { - for (auto& meta_grid_track_size : box.computed_values().grid_template_rows().meta_grid_track_sizes()) - m_grid_rows.append({ meta_grid_track_size.min_grid_track_size(), meta_grid_track_size.max_grid_track_size() }); + for (auto const& track_in_list : grid_template_rows.track_list()) { + auto repeat_count = (track_in_list.is_repeat() && track_in_list.repeat().is_default()) ? track_in_list.repeat().repeat_count() : 1; + if (track_in_list.is_repeat()) { + if (track_in_list.repeat().is_auto_fill() || track_in_list.repeat().is_auto_fit()) + repeat_count = row_count; + } + for (auto _ = 0; _ < repeat_count; _++) { + switch (track_in_list.type()) { + case CSS::ExplicitGridTrack::Type::MinMax: + m_grid_rows.append({ track_in_list.minmax().min_grid_size(), track_in_list.minmax().max_grid_size() }); + break; + case CSS::ExplicitGridTrack::Type::Repeat: + for (auto& explicit_grid_track : track_in_list.repeat().grid_track_size_list().track_list()) { + auto track_sizing_function = explicit_grid_track; + if (track_sizing_function.is_minmax()) + m_grid_rows.append({ track_sizing_function.minmax().min_grid_size(), track_sizing_function.minmax().max_grid_size() }); + else + m_grid_rows.append({ track_sizing_function.grid_size(), track_sizing_function.grid_size() }); + } + break; + case CSS::ExplicitGridTrack::Type::Default: + m_grid_rows.append({ track_in_list.grid_size(), track_in_list.grid_size() }); + break; + default: + VERIFY_NOT_REACHED(); + } + } } for (int column_index = m_grid_columns.size(); column_index < occupation_grid.column_count(); column_index++) - m_grid_columns.append({ CSS::GridTrackSize::make_auto(), CSS::GridTrackSize::make_auto() }); + m_grid_columns.append({ CSS::GridSize::make_auto(), CSS::GridSize::make_auto() }); for (int row_index = m_grid_rows.size(); row_index < occupation_grid.row_count(); row_index++) - m_grid_rows.append({ CSS::GridTrackSize::make_auto(), CSS::GridTrackSize::make_auto() }); + m_grid_rows.append({ CSS::GridSize::make_auto(), CSS::GridSize::make_auto() }); // https://www.w3.org/TR/css-grid-2/#algo-overview // 12.1. Grid Sizing Algorithm @@ -695,16 +771,16 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const switch (grid_column.min_track_sizing_function.type()) { // - A fixed sizing function // Resolve to an absolute length and use that size as the track’s initial base size. - case CSS::GridTrackSize::Type::Length: + case CSS::GridSize::Type::Length: if (!grid_column.min_track_sizing_function.length().is_auto()) grid_column.base_size = grid_column.min_track_sizing_function.length().to_px(box); break; - case CSS::GridTrackSize::Type::Percentage: + case CSS::GridSize::Type::Percentage: grid_column.base_size = grid_column.min_track_sizing_function.percentage().as_fraction() * box_state.content_width(); break; // - An intrinsic sizing function // Use an initial base size of zero. - case CSS::GridTrackSize::Type::FlexibleLength: + case CSS::GridSize::Type::FlexibleLength: break; default: VERIFY_NOT_REACHED(); @@ -714,7 +790,7 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const switch (grid_column.max_track_sizing_function.type()) { // - A fixed sizing function // Resolve to an absolute length and use that size as the track’s initial growth limit. - case CSS::GridTrackSize::Type::Length: + case CSS::GridSize::Type::Length: if (!grid_column.max_track_sizing_function.length().is_auto()) grid_column.growth_limit = grid_column.max_track_sizing_function.length().to_px(box); else @@ -722,12 +798,12 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const // Use an initial growth limit of infinity. grid_column.growth_limit = -1; break; - case CSS::GridTrackSize::Type::Percentage: + case CSS::GridSize::Type::Percentage: grid_column.growth_limit = grid_column.max_track_sizing_function.percentage().as_fraction() * box_state.content_width(); break; // - A flexible sizing function // Use an initial growth limit of infinity. - case CSS::GridTrackSize::Type::FlexibleLength: + case CSS::GridSize::Type::FlexibleLength: grid_column.growth_limit = -1; break; default: @@ -746,16 +822,16 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const switch (grid_row.min_track_sizing_function.type()) { // - A fixed sizing function // Resolve to an absolute length and use that size as the track’s initial base size. - case CSS::GridTrackSize::Type::Length: + case CSS::GridSize::Type::Length: if (!grid_row.min_track_sizing_function.length().is_auto()) grid_row.base_size = grid_row.min_track_sizing_function.length().to_px(box); break; - case CSS::GridTrackSize::Type::Percentage: + case CSS::GridSize::Type::Percentage: grid_row.base_size = grid_row.min_track_sizing_function.percentage().as_fraction() * box_state.content_height(); break; // - An intrinsic sizing function // Use an initial base size of zero. - case CSS::GridTrackSize::Type::FlexibleLength: + case CSS::GridSize::Type::FlexibleLength: break; default: VERIFY_NOT_REACHED(); @@ -765,7 +841,7 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const switch (grid_row.max_track_sizing_function.type()) { // - A fixed sizing function // Resolve to an absolute length and use that size as the track’s initial growth limit. - case CSS::GridTrackSize::Type::Length: + case CSS::GridSize::Type::Length: if (!grid_row.max_track_sizing_function.length().is_auto()) grid_row.growth_limit = grid_row.max_track_sizing_function.length().to_px(box); else @@ -773,12 +849,12 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const // Use an initial growth limit of infinity. grid_row.growth_limit = -1; break; - case CSS::GridTrackSize::Type::Percentage: + case CSS::GridSize::Type::Percentage: grid_row.growth_limit = grid_row.max_track_sizing_function.percentage().as_fraction() * box_state.content_height(); break; // - A flexible sizing function // Use an initial growth limit of infinity. - case CSS::GridTrackSize::Type::FlexibleLength: + case CSS::GridSize::Type::FlexibleLength: grid_row.growth_limit = -1; break; default: @@ -940,7 +1016,9 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const // The auto-fit keyword behaves the same as auto-fill, except that after grid item placement any // empty repeated tracks are collapsed. An empty track is one with no in-flow grid items placed into // or spanning across it. (This can result in all tracks being collapsed, if they’re all empty.) - if (box.computed_values().grid_template_columns().is_auto_fit()) { + if (grid_template_columns.track_list().size() == 1 + && grid_template_columns.track_list().first().is_repeat() + && grid_template_columns.track_list().first().repeat().is_auto_fit()) { auto idx = 0; for (auto& grid_column : m_grid_columns) { // A collapsed track is treated as having a fixed track sizing function of 0px, and the gutters on diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h index 85a14b207b..2d43271e68 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h @@ -27,8 +27,8 @@ private: bool is_auto_positioned_track(CSS::GridTrackPlacement const&, CSS::GridTrackPlacement const&) const; struct GridTrackSizeConstraints { - CSS::GridTrackSize min_track_sizing_function; - CSS::GridTrackSize max_track_sizing_function; + CSS::GridSize min_track_sizing_function; + CSS::GridSize max_track_sizing_function; float base_size { 0 }; float growth_limit { 0 }; float space_to_distribute { 0 };