) [189.984375,18 300x180]
+ TextPaintable (TextNode<#text>)
+ PaintableWithLines (BlockContainer
) [494.984375,18 287.015625x180]
+ TextPaintable (TextNode<#text>)
+ PaintableWithLines (BlockContainer(anonymous)) [8,208 784x0]
\ No newline at end of file
diff --git a/Tests/LibWeb/Layout/input/grid/fit-content-1.html b/Tests/LibWeb/Layout/input/grid/fit-content-1.html
new file mode 100644
index 00000000000..22c108ce069
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/grid/fit-content-1.html
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/Tests/LibWeb/Layout/input/grid/fit-content-2.html b/Tests/LibWeb/Layout/input/grid/fit-content-2.html
new file mode 100644
index 00000000000..98a2975f908
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/grid/fit-content-2.html
@@ -0,0 +1,25 @@
+
+
Item as wide as the content.
+
+Item with more text in it. Because the contents of it are wider than the
+maximum width, it is clamped at 300 pixels.
+
+
Flexible item
+
+
diff --git a/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp b/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp
index c84775dc7be..4076d614f08 100644
--- a/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp
+++ b/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp
@@ -10,6 +10,13 @@
namespace Web::CSS {
+GridSize::GridSize(Type type, LengthPercentage length_percentage)
+ : m_value(move(length_percentage))
+{
+ VERIFY(type == Type::FitContent);
+ m_type = type;
+}
+
GridSize::GridSize(LengthPercentage length_percentage)
: m_type(Type::LengthPercentage)
, m_value(move(length_percentage))
@@ -63,7 +70,7 @@ bool GridSize::is_fixed(Layout::AvailableSize const& available_size) const
bool GridSize::is_intrinsic(Layout::AvailableSize const& available_size) const
{
- return is_auto(available_size) || is_max_content() || is_min_content();
+ return is_auto(available_size) || is_max_content() || is_min_content() || is_fit_content();
}
GridSize GridSize::make_auto()
@@ -73,7 +80,7 @@ GridSize GridSize::make_auto()
Size GridSize::css_size() const
{
- VERIFY(m_type == Type::LengthPercentage);
+ VERIFY(m_type == Type::LengthPercentage || m_type == Type::FitContent);
auto& length_percentage = m_value.get
();
if (length_percentage.is_auto())
return CSS::Size::make_auto();
@@ -88,6 +95,7 @@ String GridSize::to_string() const
{
switch (m_type) {
case Type::LengthPercentage:
+ case Type::FitContent:
return m_value.get().to_string();
case Type::FlexibleLength:
return m_value.get().to_string();
@@ -116,6 +124,21 @@ String GridMinMax::to_string() const
return MUST(builder.to_string());
}
+GridFitContent::GridFitContent(GridSize max_grid_size)
+ : m_max_grid_size(max_grid_size)
+{
+}
+
+GridFitContent::GridFitContent()
+ : m_max_grid_size(GridSize::make_auto())
+{
+}
+
+String GridFitContent::to_string() const
+{
+ return MUST(String::formatted("fit-content({})", m_max_grid_size.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)
@@ -156,6 +179,12 @@ String GridRepeat::to_string() const
return MUST(builder.to_string());
}
+ExplicitGridTrack::ExplicitGridTrack(CSS::GridFitContent grid_fit_content)
+ : m_type(Type::FitContent)
+ , m_grid_fit_content(grid_fit_content)
+{
+}
+
ExplicitGridTrack::ExplicitGridTrack(CSS::GridMinMax grid_minmax)
: m_type(Type::MinMax)
, m_grid_minmax(grid_minmax)
@@ -177,6 +206,8 @@ ExplicitGridTrack::ExplicitGridTrack(CSS::GridSize grid_size)
String ExplicitGridTrack::to_string() const
{
switch (m_type) {
+ case Type::FitContent:
+ return m_grid_fit_content.to_string();
case Type::MinMax:
return m_grid_minmax.to_string();
case Type::Repeat:
diff --git a/Userland/Libraries/LibWeb/CSS/GridTrackSize.h b/Userland/Libraries/LibWeb/CSS/GridTrackSize.h
index 0e23027ca9a..9fbc30dd2e6 100644
--- a/Userland/Libraries/LibWeb/CSS/GridTrackSize.h
+++ b/Userland/Libraries/LibWeb/CSS/GridTrackSize.h
@@ -17,10 +17,12 @@ public:
enum class Type {
LengthPercentage,
FlexibleLength,
+ FitContent,
MaxContent,
MinContent,
};
+ GridSize(Type, LengthPercentage);
GridSize(LengthPercentage);
GridSize(Flex);
GridSize(Type);
@@ -34,6 +36,7 @@ public:
bool is_auto(Layout::AvailableSize const&) const;
bool is_fixed(Layout::AvailableSize const&) const;
bool is_flexible_length() const { return m_type == Type::FlexibleLength; }
+ bool is_fit_content() const { return m_type == Type::FitContent; }
bool is_max_content() const { return m_type == Type::MaxContent; }
bool is_min_content() const { return m_type == Type::MinContent; }
@@ -42,7 +45,6 @@ public:
// https://www.w3.org/TR/css-grid-2/#layout-algorithm
// An intrinsic sizing function (min-content, max-content, auto, fit-content()).
- // FIXME: Add missing properties once implemented.
bool is_intrinsic(Layout::AvailableSize const&) const;
bool is_definite() const
@@ -64,6 +66,23 @@ private:
Variant m_value;
};
+class GridFitContent {
+public:
+ GridFitContent(GridSize);
+ GridFitContent();
+
+ GridSize max_grid_size() const& { return m_max_grid_size; }
+
+ String to_string() const;
+ bool operator==(GridFitContent const& other) const
+ {
+ return m_max_grid_size == other.m_max_grid_size;
+ }
+
+private:
+ GridSize m_max_grid_size;
+};
+
class GridMinMax {
public:
GridMinMax(CSS::GridSize min_grid_size, CSS::GridSize max_grid_size);
@@ -149,14 +168,23 @@ private:
class ExplicitGridTrack {
public:
enum class Type {
+ FitContent,
MinMax,
Repeat,
Default,
};
+ ExplicitGridTrack(CSS::GridFitContent);
ExplicitGridTrack(CSS::GridRepeat);
ExplicitGridTrack(CSS::GridMinMax);
ExplicitGridTrack(CSS::GridSize);
+ bool is_fit_content() const { return m_type == Type::FitContent; }
+ GridFitContent fit_content() const
+ {
+ VERIFY(is_fit_content());
+ return m_grid_fit_content;
+ }
+
bool is_repeat() const { return m_type == Type::Repeat; }
GridRepeat repeat() const
{
@@ -183,6 +211,8 @@ public:
String to_string() const;
bool operator==(ExplicitGridTrack const& other) const
{
+ if (is_fit_content() && other.is_fit_content())
+ return m_grid_fit_content == other.fit_content();
if (is_repeat() && other.is_repeat())
return m_grid_repeat == other.repeat();
if (is_minmax() && other.is_minmax())
@@ -194,6 +224,7 @@ public:
private:
Type m_type;
+ GridFitContent m_grid_fit_content;
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 547febc70b2..4215e474059 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -5910,6 +5910,21 @@ Optional Parser::parse_grid_size(ComponentValue const& component_
return {};
}
+Optional Parser::parse_fit_content(Vector const& component_values)
+{
+ // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-fit-content
+ // 'fit-content( )'
+ // Represents the formula max(minimum, min(limit, max-content)), where minimum represents an auto minimum (which is often, but not always,
+ // equal to a min-content minimum), and limit is the track sizing function passed as an argument to fit-content().
+ // This is essentially calculated as the smaller of minmax(auto, max-content) and minmax(auto, limit).
+ auto function_tokens = TokenStream(component_values);
+ function_tokens.skip_whitespace();
+ auto maybe_length_percentage = parse_length_percentage(function_tokens);
+ if (maybe_length_percentage.has_value())
+ return CSS::GridFitContent(CSS::GridSize(CSS::GridSize::Type::FitContent, maybe_length_percentage.value()));
+ return {};
+}
+
Optional Parser::parse_min_max(Vector const& component_values)
{
// https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax
@@ -6067,6 +6082,11 @@ Optional Parser::parse_track_sizing_function(ComponentVa
return CSS::ExplicitGridTrack(maybe_min_max_value.value());
else
return {};
+ } else if (function_token.name().equals_ignoring_ascii_case("fit-content"sv)) {
+ auto maybe_fit_content_value = parse_fit_content(function_token.values());
+ if (maybe_fit_content_value.has_value())
+ return CSS::ExplicitGridTrack(maybe_fit_content_value.value());
+ return {};
} else if (auto maybe_calculated = parse_calculated_value(token)) {
return CSS::ExplicitGridTrack(GridSize(LengthPercentage(maybe_calculated.release_nonnull())));
}
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
index 320ba396a26..00f60bf8269 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
@@ -250,6 +250,7 @@ private:
Optional parse_unicode_range(StringView);
Vector parse_unicode_ranges(TokenStream&);
Optional parse_grid_size(ComponentValue const&);
+ Optional parse_fit_content(Vector const&);
Optional parse_min_max(Vector const&);
Optional parse_repeat(Vector const&);
Optional parse_track_sizing_function(ComponentValue const&);
diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h
index e72ebfd9eda..5b9368cbab9 100644
--- a/Userland/Libraries/LibWeb/Forward.h
+++ b/Userland/Libraries/LibWeb/Forward.h
@@ -137,6 +137,7 @@ class FrequencyOrCalculated;
class FrequencyPercentage;
class FrequencyStyleValue;
class GridAutoFlowStyleValue;
+class GridFitContent;
class GridMinMax;
class GridRepeat;
class GridSize;
diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
index de99f8e65af..648e3274989 100644
--- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
@@ -17,6 +17,13 @@ GridFormattingContext::GridTrack GridFormattingContext::GridTrack::create_from_d
// NOTE: repeat() is expected to be expanded beforehand.
VERIFY(!definition.is_repeat());
+ if (definition.is_fit_content()) {
+ return GridTrack {
+ .min_track_sizing_function = CSS::GridSize::make_auto(),
+ .max_track_sizing_function = definition.fit_content().max_grid_size(),
+ };
+ }
+
if (definition.is_minmax()) {
return GridTrack {
.min_track_sizing_function = definition.minmax().min_grid_size(),
@@ -422,6 +429,7 @@ void GridFormattingContext::initialize_grid_tracks_from_definition(GridDimension
for (auto _ = 0; _ < repeat_count; _++) {
switch (track_definition.type()) {
case CSS::ExplicitGridTrack::Type::Default:
+ case CSS::ExplicitGridTrack::Type::FitContent:
case CSS::ExplicitGridTrack::Type::MinMax:
tracks.append(GridTrack::create_from_definition(track_definition));
break;
@@ -852,13 +860,18 @@ void GridFormattingContext::increase_sizes_to_accommodate_spanning_items_crossin
// 6. For max-content maximums: Lastly continue to increase the growth limit of tracks with a max track
// sizing function of max-content by distributing extra space as needed to account for these items' max-
- // content contributions.
+ // content contributions. However, limit the growth of any fit-content() tracks by their fit-content() argument.
auto item_max_content_contribution = calculate_max_content_contribution(item, dimension);
distribute_extra_space_across_spanned_tracks_growth_limit(item_max_content_contribution, spanned_tracks, [&](GridTrack const& track) {
- return track.max_track_sizing_function.is_max_content() || track.max_track_sizing_function.is_auto(available_size);
+ return track.max_track_sizing_function.is_max_content() || track.max_track_sizing_function.is_auto(available_size) || track.max_track_sizing_function.is_fit_content();
});
for (auto& track : spanned_tracks) {
- if (!track.growth_limit.has_value()) {
+ if (track.max_track_sizing_function.is_fit_content()) {
+ track.growth_limit = css_clamp(
+ track.planned_increase,
+ track.base_size,
+ track.max_track_sizing_function.css_size().to_px(grid_container(), available_size.to_px_or_zero()));
+ } else if (!track.growth_limit.has_value()) {
// If the affected size is an infinite growth limit, set it to the track’s base size plus the planned increase.
track.growth_limit = track.base_size + track.planned_increase;
} else {
@@ -2039,7 +2052,7 @@ void GridFormattingContext::init_grid_lines(GridDimension dimension)
line_names.extend(item.get().names);
} else if (item.has()) {
auto const& explicit_track = item.get();
- if (explicit_track.is_default() || explicit_track.is_minmax()) {
+ if (explicit_track.is_default() || explicit_track.is_minmax() || explicit_track.is_fit_content()) {
lines.append({ .names = line_names });
line_names.clear();
} else if (explicit_track.is_repeat()) {