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 };