diff options
author | martinfalisse <martinmotteditfalisse@gmail.com> | 2022-10-30 13:27:57 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-11-01 11:19:41 +0100 |
commit | b2b677e9842775346d66a2697b29cfa96f11fdb0 (patch) | |
tree | 3cfeffb2b531abe67a80e8ef1ab8f4b73ddfe1a1 | |
parent | 9441515312c47187815ad2c8d23f1ded8026394a (diff) | |
download | serenity-b2b677e9842775346d66a2697b29cfa96f11fdb0.zip |
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<ExplicitGridTrack> 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.
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/ComputedValues.h | 16 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp | 150 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/GridTrackSize.h | 147 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp | 418 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleProperties.cpp | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleProperties.h | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleValue.cpp | 19 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleValue.h | 23 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Forward.h | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp | 178 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/GridFormattingContext.h | 4 |
12 files changed, 619 insertions, 360 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<CSS::VerticalAlign, CSS::LengthPercentage> 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<CSS::VerticalAlign, CSS::LengthPercentage> 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<CSS::VerticalAlign, CSS::LengthPercentage> 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; +GridSize::GridSize() + : m_length { Length::make_auto() } +{ +} -GridTrackSize GridTrackSize::make_auto() +GridSize::~GridSize() = default; + +GridSize GridSize::make_auto() { - return GridTrackSize(CSS::Length::make_auto()); + return GridSize(CSS::Length::make_auto()); } -String GridTrackSize::to_string() const +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) +{ +} + +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(); +} + +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) +{ +} + +GridRepeat::GridRepeat(GridTrackSizeList grid_track_size_list, Type type) + : m_type(type) + , m_grid_track_size_list(grid_track_size_list) { } -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) +GridRepeat::GridRepeat() { } -String MetaGridTrackSize::to_string() const +String GridRepeat::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(); + StringBuilder builder; + 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(); } - return String::formatted("{}", m_min_grid_track_size.to_string()); + builder.append(", "sv); + builder.appendff("{}", m_grid_track_size_list.to_string()); + builder.append(")"sv); + return builder.to_string(); } -ExplicitTrackSizing::ExplicitTrackSizing() +ExplicitGridTrack::ExplicitGridTrack(CSS::GridMinMax grid_minmax) + : m_type(Type::MinMax) + , m_grid_minmax(grid_minmax) { } -ExplicitTrackSizing::ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize> meta_grid_track_sizes) - : m_meta_grid_track_sizes(meta_grid_track_sizes) +ExplicitGridTrack::ExplicitGridTrack(CSS::GridRepeat grid_repeat) + : m_type(Type::Repeat) + , m_grid_repeat(grid_repeat) { } -ExplicitTrackSizing::ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize> meta_grid_track_sizes, int repeat_count) - : m_meta_grid_track_sizes(meta_grid_track_sizes) - , m_is_repeat(true) - , m_repeat_count(repeat_count) +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(); + } } -ExplicitTrackSizing::ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize> 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) +GridTrackSizeList::GridTrackSizeList(Vector<CSS::ExplicitGridTrack> track_list) + : m_track_list(track_list) { } -String ExplicitTrackSizing::to_string() const +GridTrackSizeList::GridTrackSizeList() + : m_track_list({}) +{ +} + +GridTrackSizeList GridTrackSizeList::make_auto() +{ + return GridTrackSizeList(); +} + +String GridTrackSizeList::to_string() const { StringBuilder builder; - if (m_is_repeat) { - builder.append("repeat("sv); - builder.append(m_repeat_count); - builder.append(", "sv); - } - 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); - } + 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; } + GridSize min_grid_size() const& { return m_min_grid_size; } + GridSize max_grid_size() const& { return m_max_grid_size; } - 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; } + String to_string() const; + bool operator==(GridMinMax const& other) const + { + return m_min_grid_size == other.min_grid_size() + && m_max_grid_size == other.max_grid_size(); + } + +private: + GridSize m_min_grid_size; + GridSize m_max_grid_size; +}; + +class GridTrackSizeList { +public: + GridTrackSizeList(Vector<CSS::ExplicitGridTrack> track_list); + GridTrackSizeList(); + + static GridTrackSizeList make_auto(); + Vector<CSS::ExplicitGridTrack> track_list() const { return m_track_list; } String to_string() const; - bool operator==(MetaGridTrackSize const& other) const + bool operator==(GridTrackSizeList 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_track_list == other.track_list(); } private: - GridTrackSize m_min_grid_track_size; - GridTrackSize m_max_grid_track_size; - bool m_is_min_max { false }; + Vector<CSS::ExplicitGridTrack> m_track_list; }; -class ExplicitTrackSizing { +class GridRepeat { public: enum class Type { AutoFit, AutoFill, + Default, }; + GridRepeat(GridTrackSizeList, int repeat_count); + GridRepeat(GridTrackSizeList, Type); + GridRepeat(); + + 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; } - ExplicitTrackSizing(); - ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize>); - ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize>, int repeat_count); - ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize>, Type); + String to_string() const; + bool operator==(GridRepeat const& other) const + { + 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(); + } - static ExplicitTrackSizing make_auto() { return ExplicitTrackSizing(); }; +private: + 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_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; } + 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; + } - Vector<CSS::MetaGridTrackSize> meta_grid_track_sizes() const& { return m_meta_grid_track_sizes; } + Type type() const { return m_type; } String to_string() const; - bool operator==(ExplicitTrackSizing const& other) const + bool operator==(ExplicitGridTrack const& other) const { - return m_meta_grid_track_sizes == other.meta_grid_track_sizes(); + 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: - Vector<CSS::MetaGridTrackSize> m_meta_grid_track_sizes; - bool m_is_repeat { false }; - bool m_is_auto_fill { false }; - bool m_is_auto_fit { false }; - int m_repeat_count { 0 }; + 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<StyleValue> Parser::parse_as_css_value(PropertyID property_id) return parsed_value.release_value(); } -RefPtr<StyleValue> Parser::parse_grid_track_sizes(Vector<ComponentValue> const& component_values) +Optional<CSS::GridSize> Parser::parse_grid_size(ComponentValue const& component_value) { - auto parse_grid_track_size = [&](ComponentValue const& component_value) -> Optional<GridTrackSize> { - // 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); - } - 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()); + // 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 {}; +} - auto parse_min_max = [&](Vector<ComponentValue> const& component_values) -> Optional<MetaGridTrackSize> { +Optional<CSS::GridMinMax> Parser::parse_min_max(Vector<ComponentValue> 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 <flex> 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 - // '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 <flex> 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 <flex> value sets the track’s flex factor; it is invalid as a minimum. - if (min_grid_track_size.value().is_flexible_length()) + // As a maximum, a <flex> 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<CSS::GridRepeat> Parser::parse_repeat(Vector<ComponentValue> 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( [ <integer [1,∞]> | auto-fill | auto-fit ] , <track-list> ) + 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<CSS::ExplicitGridTrack> 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 {}; - return MetaGridTrackSize(min_grid_track_size.value(), max_grid_track_size.value()); + // 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(); } - return MetaGridTrackSize(GridTrackSize::make_auto(), GridTrackSize::make_auto()); - }; + } - Vector<CSS::MetaGridTrackSize> 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( [ <integer [1,∞]> | auto-fill | auto-fit ] , <track-list> ) - 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; + // Thus the precise syntax of the repeat() notation has several forms: + // <track-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <track-size> ]+ <line-names>? ) + // <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? ) + // <fixed-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <fixed-size> ]+ <line-names>? ) + // <name-repeat> = repeat( [ <integer [1,∞]> | auto-fill ], <line-names>+) - Vector<CSS::MetaGridTrackSize> 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; - } + // The <track-repeat> variant can represent the repetition of any <track-size>, but is limited to a + // fixed number of repetitions. - // Thus the precise syntax of the repeat() notation has several forms: - // <track-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <track-size> ]+ <line-names>? ) - // <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? ) - // <fixed-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <fixed-size> ]+ <line-names>? ) - // <name-repeat> = repeat( [ <integer [1,∞]> | auto-fill ], <line-names>+) - - // The <track-repeat> variant can represent the repetition of any <track-size>, but is limited to a - // fixed number of repetitions. - - // The <auto-repeat> 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 <fixed-repeat>s. - - // The <name-repeat> 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 <name-repeat> ends up placing two <line-names> 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; + // The <auto-repeat> 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 <fixed-repeat>s. + + // The <name-repeat> 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 <name-repeat> ends up placing two <line-names> 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<CSS::ExplicitGridTrack> 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 {}; } - if (component_value.is(Token::Type::Ident) && component_value.token().ident().equals_ignoring_case("auto"sv)) { - params.append(GridTrackSize(Length::make_auto())); - continue; + 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<StyleValue> Parser::parse_grid_track_sizes(Vector<ComponentValue> const& component_values) +{ + Vector<CSS::ExplicitGridTrack> 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()); } - 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<StyleValue> Parser::parse_grid_track_placement(Vector<ComponentValue> 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 + // <grid-line> = + // auto | + // <custom-ident> | + // [ <integer> && <custom-ident>? ] | + // [ span && [ <integer> || <custom-ident> ] ] + 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 <integer> 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 <integer> 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 - // [ <integer [−∞,−1]> | <integer [1,∞]> ] && <custom-ident>? - // An <integer> 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<int>(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<int>(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 - // [ <integer [−∞,−1]> | <integer [1,∞]> ] && <custom-ident>? - // An <integer> 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<int>(current_token.number_value()) < 1) - return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(1, is_span)); - return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(static_cast<int>(current_token.number_value()), is_span)); - } - return {}; + // Negative integers or zero are invalid. + if (span_value && span_or_position_value < 1) + return {}; + + // If the <integer> 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<StyleValue> Parser::parse_grid_track_placement_shorthand_value(Vector<ComponentValue> 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<Ratio> parse_ratio(TokenStream<ComponentValue>&); Optional<UnicodeRange> parse_unicode_range(TokenStream<ComponentValue>&); Optional<UnicodeRange> parse_unicode_range(StringView); + Optional<GridSize> parse_grid_size(ComponentValue const&); + Optional<GridMinMax> parse_min_max(Vector<ComponentValue> const&); + Optional<GridRepeat> parse_repeat(Vector<ComponentValue> const&); + Optional<ExplicitGridTrack> 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<CSS::FontVariant> 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<CSS::PointerEvents> pointer_events() const; Variant<CSS::VerticalAlign, CSS::LengthPercentage> vertical_align() const; Optional<CSS::FontVariant> 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<LengthStyleValue const&>(*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<GridTrackSizeStyleValue const&>(*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> GridTrackPlacementStyleValue::create return adopt_ref(*new GridTrackPlacementStyleValue(grid_track_placement)); } -NonnullRefPtr<GridTrackSizeStyleValue> GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing explicit_track_sizing) +NonnullRefPtr<GridTrackSizeStyleValue> 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> GridTrackSizeStyleValue::make_auto() +{ + return adopt_ref(*new GridTrackSizeStyleValue(CSS::GridTrackSizeList())); } NonnullRefPtr<RectStyleValue> 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<FrequencyStyleValue&>(const_cast<StyleValue const&>(*this).as_frequency()); } GridTrackPlacementShorthandStyleValue& as_grid_track_placement_shorthand() { return const_cast<GridTrackPlacementShorthandStyleValue&>(const_cast<StyleValue const&>(*this).as_grid_track_placement_shorthand()); } GridTrackPlacementStyleValue& as_grid_track_placement() { return const_cast<GridTrackPlacementStyleValue&>(const_cast<StyleValue const&>(*this).as_grid_track_placement()); } - GridTrackSizeStyleValue& as_explicit_track_sizing() { return const_cast<GridTrackSizeStyleValue&>(const_cast<StyleValue const&>(*this).as_explicit_track_sizing()); } + GridTrackSizeStyleValue& as_grid_track_size_list() { return const_cast<GridTrackSizeStyleValue&>(const_cast<StyleValue const&>(*this).as_grid_track_size_list()); } IdentifierStyleValue& as_identifier() { return const_cast<IdentifierStyleValue&>(const_cast<StyleValue const&>(*this).as_identifier()); } ImageStyleValue& as_image() { return const_cast<ImageStyleValue&>(const_cast<StyleValue const&>(*this).as_image()); } InheritStyleValue& as_inherit() { return const_cast<InheritStyleValue&>(const_cast<StyleValue const&>(*this).as_inherit()); } @@ -1048,21 +1048,24 @@ private: class GridTrackSizeStyleValue final : public StyleValue { public: - static NonnullRefPtr<GridTrackSizeStyleValue> create(CSS::ExplicitTrackSizing explicit_track_sizing); + static NonnullRefPtr<GridTrackSizeStyleValue> create(CSS::GridTrackSizeList grid_track_size_list); virtual ~GridTrackSizeStyleValue() override = default; - CSS::ExplicitTrackSizing explicit_track_sizing() const { return m_explicit_track_sizing; } + static NonnullRefPtr<GridTrackSizeStyleValue> 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<BlockContainer>()) { 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<int>(get_free_space_x(box) / sum_of_grid_track_sizes)); + column_count = max(1, static_cast<int>(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<int>(get_free_space_y(box) / sum_of_grid_track_sizes)); + row_count = max(1, static_cast<int>(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 (<flex>). // 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 }; |