LibWeb: Restrict CSS gap properties to values allowed by the spec

Gap values are now represented by Variant<LengthPercentage, NormalGap>.
NormalGap is just an empty struct to represent the `normal` keyword.

This fixes a long-standing issue where we were incorrectly storing gaps
as CSS::Size, which led to us allowing a bunch of invalid gap values.
This commit is contained in:
Andreas Kling 2024-11-09 17:38:09 +01:00 committed by Andreas Kling
parent 2a741f81c7
commit 107b20e84d
Notes: github-actions[bot] 2024-11-09 18:23:29 +00:00
17 changed files with 157 additions and 120 deletions

View file

@ -96,7 +96,7 @@ clear: none
clip: auto
clip-path: none
column-count: auto
column-gap: auto
column-gap: normal
column-span: none
column-width: auto
content: normal
@ -172,7 +172,7 @@ position: static
r: 0px
right: auto
rotate: none
row-gap: auto
row-gap: normal
rx: auto
ry: auto
scrollbar-gutter: auto

View file

@ -6,24 +6,24 @@ Rerun
Found 18 tests
5 Pass
13 Fail
16 Pass
2 Fail
Details
Result Test Name MessageFail Default column-gap is 'normal'
Result Test Name MessagePass Default column-gap is 'normal'
Pass column-gap accepts pixels
Pass column-gap accepts em
Pass column-gap accepts vw
Pass column-gap accepts percentage
Fail column-gap accepts calc()
Fail column-gap accepts calc() mixing fixed and percentage values
Fail Initial column-gap is 'normal'
Fail Initial column-gap is 'normal' 2
Fail Initial inherited column-gap is 'normal'
Pass Initial column-gap is 'normal'
Pass Initial column-gap is 'normal' 2
Pass Initial inherited column-gap is 'normal'
Pass column-gap is inheritable
Fail Negative column-gap is invalid
Fail 'max-content' column-gap is invalid
Fail 'none' column-gap is invalid
Fail column-gap with multiple values is invalid
Fail Angle column-gap is invalid
Fail Resolution column-gap is invalid
Fail Time column-gap is invalid
Pass Negative column-gap is invalid
Pass 'max-content' column-gap is invalid
Pass 'none' column-gap is invalid
Pass column-gap with multiple values is invalid
Pass Angle column-gap is invalid
Pass Resolution column-gap is invalid
Pass Time column-gap is invalid

View file

@ -6,11 +6,11 @@ Rerun
Found 6 tests
6 Fail
6 Pass
Details
Result Test Name MessageFail colum-gap:normal computes to normal on multicol elements
Fail row-gap:normal computes to normal on multicol elements
Fail colum-gap:normal computes to normal on grid
Fail row-gap:normal computes to normal on grid
Fail colum-gap:normal (main axis) computes to normal on flexbox
Fail row-gap:normal (cross axis) computes to normal on flexbox
Result Test Name MessagePass colum-gap:normal computes to normal on multicol elements
Pass row-gap:normal computes to normal on multicol elements
Pass colum-gap:normal computes to normal on grid
Pass row-gap:normal computes to normal on grid
Pass colum-gap:normal (main axis) computes to normal on flexbox
Pass row-gap:normal (cross axis) computes to normal on flexbox

View file

@ -6,10 +6,10 @@ Rerun
Found 26 tests
10 Pass
16 Fail
21 Pass
5 Fail
Details
Result Test Name MessageFail Default gap is 'normal'
Result Test Name MessagePass Default gap is 'normal'
Pass gap accepts pixels
Pass gap accepts pixels 2
Pass gap accepts pixels combined with percentage
@ -22,16 +22,16 @@ Pass gap accepts percentage 2
Fail gap accepts calc()
Fail gap accepts calc() mixing fixed and percentage values
Fail gap accepts calc() 2
Fail Initial gap is 'normal'
Fail Initial gap is 'normal' 2
Fail Initial inherited gap is 'normal'
Pass Initial gap is 'normal'
Pass Initial gap is 'normal' 2
Pass Initial inherited gap is 'normal'
Pass gap is inheritable
Fail Negative gap is invalid
Fail 'max-content' gap is invalid
Fail 'none' gap is invalid
Fail Angle gap is invalid
Fail Resolution gap is invalid
Fail Time gap is invalid
Fail gap with three values is invalid
Pass Negative gap is invalid
Pass 'max-content' gap is invalid
Pass 'none' gap is invalid
Pass Angle gap is invalid
Pass Resolution gap is invalid
Pass Time gap is invalid
Pass gap with three values is invalid
Fail gap with slash is invalid
Fail gap with one wrong value is invalid

View file

@ -6,22 +6,22 @@ Rerun
Found 16 tests
2 Pass
14 Fail
6 Pass
10 Fail
Details
Result Test Name MessageFail e.style['gap'] = "normal" should set the property value
Fail e.style['gap'] = "10px" should set the property value
Fail e.style['gap'] = "normal normal" should set the property value
Fail e.style['gap'] = "10px 10px" should set the property value
Fail e.style['column-gap'] = "normal" should set the property value
Pass e.style['column-gap'] = "normal" should set the property value
Pass e.style['column-gap'] = "10px" should set the property value
Fail e.style['row-gap'] = "normal" should set the property value
Pass e.style['row-gap'] = "normal" should set the property value
Pass e.style['row-gap'] = "10px" should set the property value
Fail 'row-gap: normal; column-gap: normal;' is serialized to 'gap: normal;'
Fail getPropertyValue for 'row-gap: normal; column-gap: normal;' returns 'normal'
Fail 'row-gap: 10px; column-gap: 10px;' is serialized to 'gap: 10px;'
Fail getPropertyValue for 'row-gap: 10px; column-gap: 10px;' returns '10px'
Fail 'row-gap: 10px; column-gap: normal;' is serialized to 'gap: 10px normal;'
Fail getPropertyValue for 'row-gap: 10px; column-gap: normal;' returns '10px normal'
Pass getPropertyValue for 'row-gap: 10px; column-gap: normal;' returns '10px normal'
Fail 'column-gap: normal; row-gap: 10px;' is serialized to 'gap: 10px normal;'
Fail getPropertyValue for 'column-gap: normal; row-gap: 10px;' returns '10px normal'
Pass getPropertyValue for 'column-gap: normal; row-gap: 10px;' returns '10px normal'

View file

@ -6,24 +6,24 @@ Rerun
Found 18 tests
5 Pass
13 Fail
16 Pass
2 Fail
Details
Result Test Name MessageFail Default grid-column-gap is 'normal'
Result Test Name MessagePass Default grid-column-gap is 'normal'
Pass grid-column-gap accepts pixels
Pass grid-column-gap accepts em
Pass grid-column-gap accepts vw
Pass grid-column-gap accepts percentage
Fail grid-column-gap accepts calc()
Fail grid-column-gap accepts calc() mixing fixed and percentage values
Fail Initial grid-column-gap is 'normal'
Fail Initial grid-column-gap is 'normal' 2
Fail Initial inherited grid-column-gap is 'normal'
Pass Initial grid-column-gap is 'normal'
Pass Initial grid-column-gap is 'normal' 2
Pass Initial inherited grid-column-gap is 'normal'
Pass grid-column-gap is inheritable
Fail Negative grid-column-gap is invalid
Fail 'max-content' grid-column-gap is invalid
Fail 'none' grid-column-gap is invalid
Fail grid-column-gap with multiple values is invalid
Fail Angle grid-column-gap is invalid
Fail Resolution grid-column-gap is invalid
Fail Time grid-column-gap is invalid
Pass Negative grid-column-gap is invalid
Pass 'max-content' grid-column-gap is invalid
Pass 'none' grid-column-gap is invalid
Pass grid-column-gap with multiple values is invalid
Pass Angle grid-column-gap is invalid
Pass Resolution grid-column-gap is invalid
Pass Time grid-column-gap is invalid

View file

@ -6,10 +6,10 @@ Rerun
Found 26 tests
10 Pass
16 Fail
21 Pass
5 Fail
Details
Result Test Name MessageFail Default grid-gap is 'normal'
Result Test Name MessagePass Default grid-gap is 'normal'
Pass grid-gap accepts pixels
Pass grid-gap accepts pixels 2
Pass grid-gap accepts pixels combined with percentage
@ -22,16 +22,16 @@ Pass grid-gap accepts percentage 2
Fail grid-gap accepts calc()
Fail grid-gap accepts calc() mixing fixed and percentage values
Fail grid-gap accepts calc() 2
Fail Initial grid-gap is 'normal'
Fail Initial grid-gap is 'normal' 2
Fail Initial inherited grid-gap is 'normal'
Pass Initial grid-gap is 'normal'
Pass Initial grid-gap is 'normal' 2
Pass Initial inherited grid-gap is 'normal'
Pass grid-gap is inheritable
Fail Negative grid-gap is invalid
Fail 'max-content' grid-gap is invalid
Fail 'none' grid-gap is invalid
Fail Angle grid-gap is invalid
Fail Resolution grid-gap is invalid
Fail Time grid-gap is invalid
Fail grid-gap with three values is invalid
Pass Negative grid-gap is invalid
Pass 'max-content' grid-gap is invalid
Pass 'none' grid-gap is invalid
Pass Angle grid-gap is invalid
Pass Resolution grid-gap is invalid
Pass Time grid-gap is invalid
Pass grid-gap with three values is invalid
Fail grid-gap with slash is invalid
Fail grid-gap with one wrong value is invalid

View file

@ -6,24 +6,24 @@ Rerun
Found 18 tests
5 Pass
13 Fail
16 Pass
2 Fail
Details
Result Test Name MessageFail Default grid-row-gap is 'normal'
Result Test Name MessagePass Default grid-row-gap is 'normal'
Pass grid-row-gap accepts pixels
Pass grid-row-gap accepts em
Pass grid-row-gap accepts vw
Pass grid-row-gap accepts percentage
Fail grid-row-gap accepts calc()
Fail grid-row-gap accepts calc() mixing fixed and percentage values
Fail Initial grid-row-gap is 'normal'
Fail Initial grid-row-gap is 'normal' 2
Fail Initial inherited grid-row-gap is 'normal'
Pass Initial grid-row-gap is 'normal'
Pass Initial grid-row-gap is 'normal' 2
Pass Initial inherited grid-row-gap is 'normal'
Pass grid-row-gap is inheritable
Fail Negative grid-row-gap is invalid
Fail 'max-content' grid-row-gap is invalid
Fail 'none' grid-row-gap is invalid
Fail grid-row-gap with multiple values is invalid
Fail Angle grid-row-gap is invalid
Fail Resolution grid-row-gap is invalid
Fail Time grid-row-gap is invalid
Pass Negative grid-row-gap is invalid
Pass 'max-content' grid-row-gap is invalid
Pass 'none' grid-row-gap is invalid
Pass grid-row-gap with multiple values is invalid
Pass Angle grid-row-gap is invalid
Pass Resolution grid-row-gap is invalid
Pass Time grid-row-gap is invalid

View file

@ -6,24 +6,24 @@ Rerun
Found 18 tests
5 Pass
13 Fail
16 Pass
2 Fail
Details
Result Test Name MessageFail Default row-gap is 'normal'
Result Test Name MessagePass Default row-gap is 'normal'
Pass row-gap accepts pixels
Pass row-gap accepts em
Pass row-gap accepts vw
Pass row-gap accepts percentage
Fail row-gap accepts calc()
Fail row-gap accepts calc() mixing fixed and percentage values
Fail Initial row-gap is 'normal'
Fail Initial row-gap is 'normal' 2
Fail Initial inherited row-gap is 'normal'
Pass Initial row-gap is 'normal'
Pass Initial row-gap is 'normal' 2
Pass Initial inherited row-gap is 'normal'
Pass row-gap is inheritable
Fail Negative row-gap is invalid
Fail 'max-content' row-gap is invalid
Fail 'none' row-gap is invalid
Fail row-gap with multiple values is invalid
Fail Angle row-gap is invalid
Fail Resolution row-gap is invalid
Fail Time row-gap is invalid
Pass Negative row-gap is invalid
Pass 'max-content' row-gap is invalid
Pass 'none' row-gap is invalid
Pass row-gap with multiple values is invalid
Pass Angle row-gap is invalid
Pass Resolution row-gap is invalid
Pass Time row-gap is invalid

View file

@ -46,6 +46,8 @@ struct GridAutoFlow {
bool dense { false };
};
struct NormalGap { };
struct QuotesData {
enum class Type {
None,
@ -173,10 +175,10 @@ public:
static CSS::GridTrackPlacement grid_row_start() { return CSS::GridTrackPlacement::make_auto(); }
static CSS::GridAutoFlow grid_auto_flow() { return CSS::GridAutoFlow {}; }
static ColumnCount column_count() { return ColumnCount::make_auto(); }
static CSS::Size column_gap() { return CSS::Size::make_auto(); }
static Variant<LengthPercentage, NormalGap> column_gap() { return NormalGap {}; }
static CSS::ColumnSpan column_span() { return CSS::ColumnSpan::None; }
static CSS::Size column_width() { return CSS::Size::make_auto(); }
static CSS::Size row_gap() { return CSS::Size::make_auto(); }
static Variant<LengthPercentage, NormalGap> row_gap() { return NormalGap {}; }
static CSS::BorderCollapse border_collapse() { return CSS::BorderCollapse::Separate; }
static Vector<Vector<String>> grid_template_areas() { return {}; }
static CSS::Time transition_delay() { return CSS::Time::make_seconds(0); }
@ -434,10 +436,10 @@ public:
CSS::GridTrackPlacement const& grid_row_end() const { return m_noninherited.grid_row_end; }
CSS::GridTrackPlacement const& grid_row_start() const { return m_noninherited.grid_row_start; }
CSS::ColumnCount column_count() const { return m_noninherited.column_count; }
CSS::Size const& column_gap() const { return m_noninherited.column_gap; }
Variant<LengthPercentage, NormalGap> const& column_gap() const { return m_noninherited.column_gap; }
CSS::ColumnSpan const& column_span() const { return m_noninherited.column_span; }
CSS::Size const& column_width() const { return m_noninherited.column_width; }
CSS::Size const& row_gap() const { return m_noninherited.row_gap; }
Variant<LengthPercentage, NormalGap> const& row_gap() const { return m_noninherited.row_gap; }
CSS::BorderCollapse border_collapse() const { return m_inherited.border_collapse; }
Vector<Vector<String>> const& grid_template_areas() const { return m_noninherited.grid_template_areas; }
CSS::ObjectFit object_fit() const { return m_noninherited.object_fit; }
@ -658,10 +660,10 @@ protected:
CSS::GridTrackPlacement grid_row_end { InitialValues::grid_row_end() };
CSS::GridTrackPlacement grid_row_start { InitialValues::grid_row_start() };
CSS::ColumnCount column_count { InitialValues::column_count() };
CSS::Size column_gap { InitialValues::column_gap() };
Variant<LengthPercentage, NormalGap> column_gap { InitialValues::column_gap() };
CSS::ColumnSpan column_span { InitialValues::column_span() };
CSS::Size column_width { InitialValues::column_width() };
CSS::Size row_gap { InitialValues::row_gap() };
Variant<LengthPercentage, NormalGap> row_gap { InitialValues::row_gap() };
Vector<Vector<String>> grid_template_areas { InitialValues::grid_template_areas() };
Gfx::Color stop_color { InitialValues::stop_color() };
float stop_opacity { InitialValues::stop_opacity() };
@ -802,10 +804,10 @@ public:
void set_grid_row_end(CSS::GridTrackPlacement value) { m_noninherited.grid_row_end = value; }
void set_grid_row_start(CSS::GridTrackPlacement value) { m_noninherited.grid_row_start = value; }
void set_column_count(CSS::ColumnCount value) { m_noninherited.column_count = value; }
void set_column_gap(CSS::Size const& column_gap) { m_noninherited.column_gap = column_gap; }
void set_column_gap(Variant<LengthPercentage, NormalGap> const& column_gap) { m_noninherited.column_gap = column_gap; }
void set_column_span(CSS::ColumnSpan const& column_span) { m_noninherited.column_span = column_span; }
void set_column_width(CSS::Size const& column_width) { m_noninherited.column_width = column_width; }
void set_row_gap(CSS::Size const& row_gap) { m_noninherited.row_gap = row_gap; }
void set_row_gap(Variant<LengthPercentage, NormalGap> const& row_gap) { m_noninherited.row_gap = row_gap; }
void set_border_collapse(CSS::BorderCollapse const& border_collapse) { m_inherited.border_collapse = border_collapse; }
void set_grid_template_areas(Vector<Vector<String>> const& grid_template_areas) { m_noninherited.grid_template_areas = grid_template_areas; }
void set_grid_auto_flow(CSS::GridAutoFlow grid_auto_flow) { m_noninherited.grid_auto_flow = grid_auto_flow; }

View file

@ -874,13 +874,13 @@
"column-gap": {
"animation-type": "by-computed-value",
"inherited": false,
"initial": "auto",
"initial": "normal",
"valid-types": [
"length [0,∞]",
"percentage [0,∞]"
],
"valid-identifiers": [
"auto"
"normal"
],
"percentages-resolve-to": "length"
},
@ -1284,14 +1284,14 @@
},
"gap": {
"inherited": false,
"initial": "auto",
"initial": "normal",
"valid-types": [
"length [0,∞]",
"percentage [0,∞]"
],
"max-values": 2,
"valid-identifiers": [
"auto"
"normal"
],
"percentages-resolve-to": "length",
"longhands": [
@ -2311,13 +2311,13 @@
"row-gap": {
"animation-type": "by-computed-value",
"inherited": false,
"initial": "auto",
"initial": "normal",
"valid-types": [
"length [0,∞]",
"percentage [0,∞]"
],
"valid-identifiers": [
"auto"
"normal"
],
"percentages-resolve-to": "length"
},

View file

@ -127,6 +127,26 @@ CSSStyleValue const* StyleProperties::maybe_null_property(CSS::PropertyID proper
return m_data->m_property_values[to_underlying(property_id)];
}
Variant<LengthPercentage, NormalGap> StyleProperties::gap_value(CSS::PropertyID id) const
{
auto const& value = property(id);
if (value.is_keyword()) {
VERIFY(value.as_keyword().keyword() == CSS::Keyword::Normal);
return NormalGap {};
}
if (value.is_math())
return LengthPercentage { const_cast<CSSMathValue&>(value.as_math()) };
if (value.is_percentage())
return LengthPercentage { value.as_percentage().percentage() };
if (value.is_length())
return LengthPercentage { value.as_length().length() };
VERIFY_NOT_REACHED();
}
CSS::Size StyleProperties::size_value(CSS::PropertyID id) const
{
auto const& value = property(id);

View file

@ -87,6 +87,7 @@ public:
void set_transition_property_source(JS::GCPtr<CSS::CSSStyleDeclaration const> declaration) { m_data->m_transition_property_source = declaration; }
CSS::Size size_value(CSS::PropertyID) const;
[[nodiscard]] Variant<LengthPercentage, NormalGap> gap_value(CSS::PropertyID) const;
LengthPercentage length_percentage_or_fallback(CSS::PropertyID, LengthPercentage const& fallback) const;
Optional<LengthPercentage> length_percentage(CSS::PropertyID) const;
LengthBox length_box(CSS::PropertyID left_id, CSS::PropertyID top_id, CSS::PropertyID right_id, CSS::PropertyID bottom_id, const CSS::Length& default_value) const;

View file

@ -2292,18 +2292,25 @@ double FlexFormattingContext::FlexLine::sum_of_scaled_flex_shrink_factor_of_unfr
return sum;
}
static CSSPixels gap_to_px(Variant<CSS::LengthPercentage, CSS::NormalGap> const& gap, Layout::Node const& grid_container, CSSPixels reference_value)
{
return gap.visit(
[](CSS::NormalGap) { return CSSPixels(0); },
[&](auto const& gap) { return gap.to_px(grid_container, reference_value); });
}
CSSPixels FlexFormattingContext::main_gap() const
{
auto const& computed_values = flex_container().computed_values();
auto gap = is_row_layout() ? computed_values.column_gap() : computed_values.row_gap();
return gap.to_px(flex_container(), inner_main_size(m_flex_container_state));
auto const& gap = is_row_layout() ? computed_values.column_gap() : computed_values.row_gap();
return gap_to_px(gap, flex_container(), inner_main_size(m_flex_container_state));
}
CSSPixels FlexFormattingContext::cross_gap() const
{
auto const& computed_values = flex_container().computed_values();
auto gap = is_row_layout() ? computed_values.row_gap() : computed_values.column_gap();
return gap.to_px(flex_container(), inner_cross_size(m_flex_container_state));
return gap_to_px(gap, flex_container(), inner_cross_size(m_flex_container_state));
}
}

View file

@ -12,6 +12,13 @@
namespace Web::Layout {
static CSSPixels gap_to_px(Variant<CSS::LengthPercentage, CSS::NormalGap> const& gap, Layout::Node const& grid_container, CSSPixels reference_value)
{
return gap.visit(
[](CSS::NormalGap) { return CSSPixels(0); },
[&](auto const& gap) { return gap.to_px(grid_container, reference_value); });
}
static Alignment to_alignment(CSS::JustifyContent value)
{
switch (value) {
@ -192,7 +199,7 @@ int GridFormattingContext::count_of_repeated_auto_fill_or_fit_tracks(GridDimensi
auto const& available_size = dimension == GridDimension::Column ? m_available_space->width : m_available_space->height;
auto free_space = get_free_space(*m_available_space, dimension).to_px_or_zero();
auto const& gap = dimension == GridDimension::Column ? grid_computed_values.column_gap() : grid_computed_values.row_gap();
auto gap_px = gap.to_px(grid_container(), available_size.to_px_or_zero());
auto gap_px = gap_to_px(gap, grid_container(), available_size.to_px_or_zero());
auto size_of_repeated_tracks_with_gap = size_of_repeated_tracks + repeat_track_list.size() * gap_px;
// If any number of repetitions would overflow, then 1 repetition.
if (free_space <= size_of_repeated_tracks_with_gap) {
@ -564,8 +571,8 @@ void GridFormattingContext::initialize_gap_tracks(AvailableSpace const& availabl
// line.
if (m_grid_columns.size() > 0) {
CSSPixels column_gap_width = 0;
if (!grid_container().computed_values().column_gap().is_auto()) {
column_gap_width = grid_container().computed_values().column_gap().to_px(grid_container(), available_space.width.to_px_or_zero());
if (!grid_container().computed_values().column_gap().has<CSS::NormalGap>()) {
column_gap_width = gap_to_px(grid_container().computed_values().column_gap(), grid_container(), available_space.width.to_px_or_zero());
}
m_column_gap_tracks.ensure_capacity(m_grid_columns.size() - 1);
@ -582,8 +589,8 @@ void GridFormattingContext::initialize_gap_tracks(AvailableSpace const& availabl
if (m_grid_rows.size() > 0) {
CSSPixels row_gap_height = 0;
if (!grid_container().computed_values().row_gap().is_auto()) {
row_gap_height = grid_container().computed_values().row_gap().to_px(grid_container(), available_space.height.to_px_or_zero());
if (!grid_container().computed_values().row_gap().has<CSS::NormalGap>()) {
row_gap_height = gap_to_px(grid_container().computed_values().row_gap(), grid_container(), available_space.height.to_px_or_zero());
}
m_row_gap_tracks.ensure_capacity(m_grid_rows.size() - 1);
@ -1775,7 +1782,7 @@ void GridFormattingContext::resolve_track_spacing(GridDimension const dimension)
auto const& computed_gap = is_column_dimension ? grid_container().computed_values().column_gap() : grid_container().computed_values().row_gap();
auto const& available_size = is_column_dimension ? m_available_space->width.to_px_or_zero() : m_available_space->height.to_px_or_zero();
space_between_tracks = max(space_between_tracks, computed_gap.to_px(grid_container(), available_size));
space_between_tracks = max(space_between_tracks, gap_to_px(computed_gap, grid_container(), available_size));
auto& gap_tracks = is_column_dimension ? m_column_gap_tracks : m_row_gap_tracks;
for (auto& track : gap_tracks) {

View file

@ -185,9 +185,9 @@ private:
bool has_gaps(GridDimension const dimension) const
{
if (dimension == GridDimension::Column) {
return !grid_container().computed_values().column_gap().is_auto();
return !grid_container().computed_values().column_gap().has<CSS::NormalGap>();
} else {
return !grid_container().computed_values().row_gap().is_auto();
return !grid_container().computed_values().row_gap().has<CSS::NormalGap>();
}
}

View file

@ -869,8 +869,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
computed_values.set_column_width(computed_style.size_value(CSS::PropertyID::ColumnWidth));
computed_values.set_column_gap(computed_style.size_value(CSS::PropertyID::ColumnGap));
computed_values.set_row_gap(computed_style.size_value(CSS::PropertyID::RowGap));
computed_values.set_column_gap(computed_style.gap_value(CSS::PropertyID::ColumnGap));
computed_values.set_row_gap(computed_style.gap_value(CSS::PropertyID::RowGap));
if (auto border_collapse = computed_style.border_collapse(); border_collapse.has_value())
computed_values.set_border_collapse(border_collapse.value());