ladybird/Libraries/LibWeb/CSS/GridTrackSize.h
Andreas Kling 19e5b57672 LibWeb: Parse CSS grid subgrid track listings
Parse subgrid track listings for grid-template rows and columns,
including fixed and auto-fill name-repeat line-name lists. Preserve
the subgrid keyword through computed values and shorthand serialization,
and make interpolation discrete instead of routing subgrid lists through
the explicit track interpolation path.

Import WPT coverage for subgrid grid-template parsing and computed
values, and add text coverage for discrete subgrid track-list animation.
2026-05-25 18:11:38 +02:00

215 lines
6.6 KiB
C++

/*
* Copyright (c) 2022, Martin Falisse <mfalisse@outlook.com>
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <AK/HashMap.h>
#include <AK/Vector.h>
#include <LibWeb/CSS/PercentageOr.h>
#include <LibWeb/CSS/Size.h>
#include <LibWeb/Layout/AvailableSpace.h>
namespace Web::CSS {
class GridSize {
public:
GridSize(NonnullRefPtr<StyleValue const>);
~GridSize();
static GridSize make_auto();
bool is_auto(Layout::AvailableSize const&) const;
bool is_fixed(Layout::AvailableSize const&) const;
bool is_flexible_length() const;
bool is_fit_content() const;
bool is_max_content() const;
bool is_min_content() const;
Size css_size() const { return Size::from_style_value(m_value); }
double flex_factor() const { return Flex::from_style_value(m_value).to_fr(); }
NonnullRefPtr<StyleValue const> style_value() const { return m_value; }
// https://www.w3.org/TR/css-grid-2/#layout-algorithm
// An intrinsic sizing function (min-content, max-content, auto, fit-content()).
bool is_intrinsic(Layout::AvailableSize const&) const;
bool is_definite() const;
void serialize(StringBuilder&, SerializationMode) const;
String to_string(SerializationMode) const;
GridSize absolutized(ComputationContext const&) const;
bool operator==(GridSize const& other) const = default;
bool is_computationally_independent() const { return m_value->is_computationally_independent(); }
private:
ValueComparingNonnullRefPtr<StyleValue const> m_value;
};
class GridMinMax {
public:
GridMinMax(CSS::GridSize min_grid_size, CSS::GridSize max_grid_size);
GridSize const& min_grid_size() const& { return m_min_grid_size; }
GridSize const& max_grid_size() const& { return m_max_grid_size; }
void serialize(StringBuilder&, SerializationMode) const;
String to_string(SerializationMode) const;
GridMinMax absolutized(ComputationContext const&) const;
bool operator==(GridMinMax const& other) const = default;
bool is_computationally_independent() const
{
return m_min_grid_size.is_computationally_independent() && m_max_grid_size.is_computationally_independent();
}
private:
GridSize m_min_grid_size;
GridSize m_max_grid_size;
};
struct GridLineName {
FlyString name;
bool implicit { false };
bool adopted_from_parent_grid { false };
bool operator==(GridLineName const& other) const = default;
};
struct GridArea {
size_t row_start { 0 };
size_t row_end { 1 };
size_t column_start { 0 };
size_t column_end { 1 };
bool operator==(GridArea const& other) const = default;
};
struct GridTemplateAreas {
HashMap<String, GridArea> areas;
size_t row_count { 0 };
size_t column_count { 0 };
bool is_empty() const { return row_count == 0; }
bool operator==(GridTemplateAreas const& other) const = default;
};
class GridLineNames {
public:
void append(FlyString const& name) { m_names.append({ name }); }
bool is_empty() const { return m_names.is_empty(); }
auto const& names() const& { return m_names; }
void serialize(StringBuilder&) const;
String to_string() const;
bool operator==(GridLineNames const& other) const = default;
private:
Vector<GridLineName> m_names;
};
class GridTrackSizeList {
public:
static GridTrackSizeList make_none();
static GridTrackSizeList make_line_name_list();
static GridTrackSizeList make_subgrid();
Vector<CSS::ExplicitGridTrack> track_list() const;
auto const& list() const { return m_list; }
bool is_subgrid() const { return m_is_subgrid; }
void serialize(StringBuilder&, SerializationMode) const;
String to_string(SerializationMode) const;
bool operator==(GridTrackSizeList const& other) const;
bool is_empty() const { return !m_is_subgrid && m_list.is_empty(); }
void append(GridLineNames&&);
void append(ExplicitGridTrack&&);
GridTrackSizeList absolutized(ComputationContext const&) const;
bool is_computationally_independent() const;
private:
bool m_is_subgrid { false };
bool m_preserve_line_name_sets { false };
Vector<Variant<ExplicitGridTrack, GridLineNames>> m_list;
};
enum class GridRepeatType {
AutoFit,
AutoFill,
Fixed,
};
struct GridRepeatParams {
GridRepeatType type;
RefPtr<StyleValue const> count { nullptr };
};
class GridRepeat {
public:
GridRepeat(GridRepeatType, GridTrackSizeList&&, RefPtr<StyleValue const> repeat_count);
GridRepeat(GridTrackSizeList&&, GridRepeatParams const&);
bool is_auto_fill() const { return m_type == GridRepeatType::AutoFill; }
bool is_auto_fit() const { return m_type == GridRepeatType::AutoFit; }
bool is_fixed() const { return m_type == GridRepeatType::Fixed; }
size_t repeat_count() const
{
VERIFY(is_fixed());
return int_from_style_value(*m_repeat_count);
}
GridTrackSizeList const& grid_track_size_list() const& { return m_grid_track_size_list; }
GridRepeatType type() const& { return m_type; }
void serialize(StringBuilder&, SerializationMode) const;
String to_string(SerializationMode) const;
GridRepeat absolutized(ComputationContext const&) const;
bool operator==(GridRepeat const& other) const = default;
bool is_computationally_independent() const { return m_grid_track_size_list.is_computationally_independent() && (!m_repeat_count || m_repeat_count->is_computationally_independent()); }
private:
GridRepeatType m_type;
GridTrackSizeList m_grid_track_size_list;
ValueComparingRefPtr<StyleValue const> m_repeat_count;
};
class ExplicitGridTrack {
public:
ExplicitGridTrack(Variant<GridRepeat, GridMinMax, GridSize>&& value);
bool is_repeat() const { return m_value.has<GridRepeat>(); }
GridRepeat const& repeat() const { return m_value.get<GridRepeat>(); }
bool is_minmax() const { return m_value.has<GridMinMax>(); }
GridMinMax const& minmax() const { return m_value.get<GridMinMax>(); }
bool is_default() const { return m_value.has<GridSize>(); }
GridSize const& grid_size() const { return m_value.get<GridSize>(); }
void serialize(StringBuilder&, SerializationMode) const;
String to_string(SerializationMode) const;
ExplicitGridTrack absolutized(ComputationContext const&) const;
bool operator==(ExplicitGridTrack const& other) const = default;
bool is_computationally_independent() const
{
return m_value.visit([](auto const& value) { return value.is_computationally_independent(); });
}
private:
Variant<GridRepeat, GridMinMax, GridSize> m_value;
};
}