LibWeb: Add CSS column-height property

This commit is contained in:
Psychpsyo 2025-09-04 21:50:06 +02:00 committed by Sam Atkins
parent b057bad102
commit 9ea8b5a0a3
14 changed files with 191 additions and 25 deletions

View File

@ -214,6 +214,7 @@ public:
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 column_height() { return CSS::Size::make_auto(); }
static Variant<LengthPercentage, NormalGap> row_gap() { return NormalGap {}; }
static CSS::BorderCollapse border_collapse() { return CSS::BorderCollapse::Separate; }
static CSS::EmptyCells empty_cells() { return CSS::EmptyCells::Show; }
@ -533,6 +534,7 @@ public:
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& column_height() const { return m_noninherited.column_height; }
Variant<LengthPercentage, NormalGap> const& row_gap() const { return m_noninherited.row_gap; }
CSS::BorderCollapse border_collapse() const { return m_inherited.border_collapse; }
CSS::EmptyCells empty_cells() const { return m_inherited.empty_cells; }
@ -803,6 +805,7 @@ protected:
Variant<LengthPercentage, NormalGap> column_gap { InitialValues::column_gap() };
CSS::ColumnSpan column_span { InitialValues::column_span() };
CSS::Size column_width { InitialValues::column_width() };
CSS::Size column_height { InitialValues::column_height() };
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() };
@ -996,6 +999,7 @@ public:
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_column_height(CSS::Size const& column_height) { m_noninherited.column_height = column_height; }
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_empty_cells(CSS::EmptyCells const empty_cells) { m_inherited.empty_cells = empty_cells; }

View File

@ -1913,17 +1913,33 @@ RefPtr<StyleValue const> Parser::parse_border_radius_shorthand_value(TokenStream
RefPtr<StyleValue const> Parser::parse_columns_value(TokenStream<ComponentValue>& tokens)
{
if (tokens.remaining_token_count() > 2)
return nullptr;
RefPtr<StyleValue const> column_count;
RefPtr<StyleValue const> column_width;
RefPtr<StyleValue const> column_height;
Vector<PropertyID> remaining_longhands { PropertyID::ColumnCount, PropertyID::ColumnWidth };
int found_autos = 0;
int tokens_read = 0;
auto transaction = tokens.begin_transaction();
while (tokens.has_next_token()) {
if (tokens.next_token().is_delim('/')) {
if (tokens_read == 0)
return nullptr;
tokens.discard_a_token();
auto value = parse_css_value_for_property(PropertyID::ColumnHeight, tokens);
if (!value)
return nullptr;
column_height = value.release_nonnull();
if (tokens.has_next_token())
return nullptr;
break;
}
if (tokens_read == 2)
return nullptr;
tokens_read++;
auto property_and_value = parse_css_value_for_properties(remaining_longhands, tokens);
if (!property_and_value.has_value())
return nullptr;
@ -1953,9 +1969,6 @@ RefPtr<StyleValue const> Parser::parse_columns_value(TokenStream<ComponentValue>
}
}
if (found_autos > 2)
return nullptr;
if (found_autos == 2) {
column_count = KeywordStyleValue::create(Keyword::Auto);
column_width = KeywordStyleValue::create(Keyword::Auto);
@ -1972,11 +1985,13 @@ RefPtr<StyleValue const> Parser::parse_columns_value(TokenStream<ComponentValue>
column_count = property_initial_value(PropertyID::ColumnCount);
if (!column_width)
column_width = property_initial_value(PropertyID::ColumnWidth);
if (!column_height)
column_height = property_initial_value(PropertyID::ColumnHeight);
transaction.commit();
return ShorthandStyleValue::create(PropertyID::Columns,
{ PropertyID::ColumnCount, PropertyID::ColumnWidth },
{ column_count.release_nonnull(), column_width.release_nonnull() });
{ PropertyID::ColumnCount, PropertyID::ColumnWidth, PropertyID::ColumnHeight },
{ column_count.release_nonnull(), column_width.release_nonnull(), column_height.release_nonnull() });
}
RefPtr<StyleValue const> Parser::parse_shadow_value(TokenStream<ComponentValue>& tokens, AllowInsetKeyword allow_inset_keyword)

View File

@ -1276,6 +1276,17 @@
],
"percentages-resolve-to": "length"
},
"column-height": {
"animation-type": "by-computed-value",
"inherited": false,
"initial": "auto",
"valid-types": [
"length [0,∞]"
],
"valid-identifiers": [
"auto"
]
},
"column-span": {
"animation-type": "discrete",
"inherited": false,
@ -1297,10 +1308,11 @@
},
"columns": {
"inherited": false,
"initial": "auto auto",
"initial": "auto auto / auto",
"longhands": [
"column-width",
"column-count"
"column-count",
"column-height"
]
},
"contain": {

View File

@ -321,15 +321,26 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const
case PropertyID::Columns: {
auto column_width = longhand(PropertyID::ColumnWidth)->to_string(mode);
auto column_count = longhand(PropertyID::ColumnCount)->to_string(mode);
auto column_height = longhand(PropertyID::ColumnHeight)->to_string(mode);
if (column_width == column_count)
return column_width;
if (column_width.equals_ignoring_ascii_case("auto"sv))
return column_count;
if (column_count.equals_ignoring_ascii_case("auto"sv))
return column_width;
StringBuilder builder;
return MUST(String::formatted("{} {}", column_width, column_count));
if (column_width == column_count) {
builder.append(column_width);
} else if (column_width.equals_ignoring_ascii_case("auto"sv)) {
builder.append(column_count);
} else if (column_count.equals_ignoring_ascii_case("auto"sv)) {
builder.append(column_width);
} else {
builder.append(MUST(String::formatted("{} {}", column_width, column_count)));
}
if (!column_height.equals_ignoring_ascii_case("auto"sv)) {
builder.append(" / "sv);
builder.append(column_height);
}
return builder.to_string_without_validation();
}
case PropertyID::Flex:
return MUST(String::formatted("{} {} {}", longhand(PropertyID::FlexGrow)->to_string(mode), longhand(PropertyID::FlexShrink)->to_string(mode), longhand(PropertyID::FlexBasis)->to_string(mode)));

View File

@ -840,6 +840,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
computed_values.set_column_span(computed_style.column_span());
computed_values.set_column_width(computed_style.size_value(CSS::PropertyID::ColumnWidth));
computed_values.set_column_height(computed_style.size_value(CSS::PropertyID::ColumnHeight));
computed_values.set_column_gap(computed_style.gap_value(CSS::PropertyID::ColumnGap));
computed_values.set_row_gap(computed_style.gap_value(CSS::PropertyID::RowGap));

View File

@ -140,6 +140,7 @@ All properties associated with getComputedStyle(document.body):
"clip-path",
"column-count",
"column-gap",
"column-height",
"column-span",
"column-width",
"contain",

View File

@ -348,6 +348,8 @@ All supported properties and their default values exposed from CSSStylePropertie
'column-count': 'auto'
'columnGap': 'normal'
'column-gap': 'normal'
'columnHeight': 'auto'
'column-height': 'auto'
'columnSpan': 'none'
'column-span': 'none'
'columnWidth': 'auto'

View File

@ -138,6 +138,7 @@ clip: auto
clip-path: none
column-count: auto
column-gap: normal
column-height: auto
column-span: none
column-width: auto
contain: none
@ -168,7 +169,7 @@ grid-row-start: auto
grid-template-areas: none
grid-template-columns: none
grid-template-rows: none
height: 2550px
height: 2565px
inline-size: 784px
inset-block-end: auto
inset-block-start: auto

View File

@ -0,0 +1,32 @@
Harness status: OK
Found 27 tests
27 Pass
Pass Property columns value 'auto'
Pass Property columns value '0.25em'
Pass Property columns value '2'
Pass Property columns value '0.25em 2'
Pass Property columns value '2 0.25em'
Pass Property columns value '0.25em auto'
Pass Property columns value 'auto 0.25em'
Pass Property columns value '2 auto'
Pass Property columns value 'auto 2'
Pass Property columns value 'auto / 2.5em'
Pass Property columns value '0.25em / 2.5em'
Pass Property columns value '2 / 2.5em'
Pass Property columns value '0.25em 2 / 2.5em'
Pass Property columns value '2 0.25em / 2.5em'
Pass Property columns value '0.25em auto / 2.5em'
Pass Property columns value 'auto 0.25em / 2.5em'
Pass Property columns value '2 auto / 2.5em'
Pass Property columns value 'auto 2 / 2.5em'
Pass Property columns value 'auto / auto'
Pass Property columns value '0.25em / auto'
Pass Property columns value '2 / auto'
Pass Property columns value '0.25em 2 / auto'
Pass Property columns value '2 0.25em / auto'
Pass Property columns value '0.25em auto / auto'
Pass Property columns value 'auto 0.25em / auto'
Pass Property columns value '2 auto / auto'
Pass Property columns value 'auto 2 / auto'

View File

@ -1,8 +1,8 @@
Harness status: OK
Found 9 tests
Found 17 tests
9 Pass
17 Pass
Pass e.style['columns'] = "none" should not set the property value
Pass e.style['columns'] = "10px 20px" should not set the property value
Pass e.style['columns'] = "10 20" should not set the property value
@ -11,4 +11,12 @@ Pass e.style['columns'] = "0 7px" should not set the property value
Pass e.style['columns'] = "auto auto auto" should not set the property value
Pass e.style['columns'] = "10em auto auto" should not set the property value
Pass e.style['columns'] = "initial initial" should not set the property value
Pass e.style['columns'] = "inherit inherit" should not set the property value
Pass e.style['columns'] = "inherit inherit" should not set the property value
Pass e.style['columns'] = "/ 100px" should not set the property value
Pass e.style['columns'] = "/ auto" should not set the property value
Pass e.style['columns'] = "/" should not set the property value
Pass e.style['columns'] = "auto /" should not set the property value
Pass e.style['columns'] = "100px /" should not set the property value
Pass e.style['columns'] = "2 /" should not set the property value
Pass e.style['columns'] = "2 100px /" should not set the property value
Pass e.style['columns'] = "100px 2 /" should not set the property value

View File

@ -1,8 +1,8 @@
Harness status: OK
Found 15 tests
Found 24 tests
15 Pass
24 Pass
Pass e.style['columns'] = "auto 3" should set the property value
Pass e.style['columns'] = "auto 10em" should set the property value
Pass e.style['columns'] = "3 auto" should set the property value
@ -16,5 +16,14 @@ Pass e.style['columns'] = "7em" should set the property value
Pass e.style['columns'] = "0 1" should set the property value
Pass e.style['columns'] = "1 0" should set the property value
Pass e.style['columns'] = "0px 1" should set the property value
Pass e.style['columns'] = "10px 2 / 100px" should set the property value
Pass e.style['columns'] = "2 10px / 100px" should set the property value
Pass e.style['columns'] = "10px / 100px" should set the property value
Pass e.style['columns'] = "auto / 100px" should set the property value
Pass e.style['columns'] = "10px 2 / auto" should set the property value
Pass e.style['columns'] = "2 10px / auto" should set the property value
Pass e.style['columns'] = "10px / auto" should set the property value
Pass e.style['columns'] = "2 / auto" should set the property value
Pass e.style['columns'] = "auto / auto" should set the property value
Pass e.style['columns'] = "initial" should set the property value
Pass e.style['columns'] = "inherit" should set the property value

View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<title>CSS Multi-column Layout: getComputedStyle().columns</title>
<link rel="help" href="https://drafts.csswg.org/css-multicol/#propdef-columns">
<link rel="help" href="https://drafts.csswg.org/css-multicol-2/#propdef-columns">
<meta name="assert" content="The 'columns' shorthand property computes to the correct serialization of its component values, with lengths made absolute.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/computed-testcommon.js"></script>
<div id="target"></div>
<style>
#target {
font-size: 40px;
}
</style>
<script>
test_computed_value("columns", "auto");
test_computed_value("columns", "0.25em", "10px");
test_computed_value("columns", "2", "2");
test_computed_value("columns", "0.25em 2", "10px 2");
test_computed_value("columns", "2 0.25em", "10px 2");
test_computed_value("columns", "0.25em auto", "10px");
test_computed_value("columns", "auto 0.25em", "10px");
test_computed_value("columns", "2 auto", "2");
test_computed_value("columns", "auto 2", "2");
test_computed_value("columns", "auto / 2.5em", "auto / 100px");
test_computed_value("columns", "0.25em / 2.5em", "10px / 100px");
test_computed_value("columns", "2 / 2.5em", "2 / 100px");
test_computed_value("columns", "0.25em 2 / 2.5em", "10px 2 / 100px");
test_computed_value("columns", "2 0.25em / 2.5em", "10px 2 / 100px");
test_computed_value("columns", "0.25em auto / 2.5em", "10px / 100px");
test_computed_value("columns", "auto 0.25em / 2.5em", "10px / 100px");
test_computed_value("columns", "2 auto / 2.5em", "2 / 100px");
test_computed_value("columns", "auto 2 / 2.5em", "2 / 100px");
test_computed_value("columns", "auto / auto", "auto");
test_computed_value("columns", "0.25em / auto", "10px");
test_computed_value("columns", "2 / auto", "2");
test_computed_value("columns", "0.25em 2 / auto", "10px 2");
test_computed_value("columns", "2 0.25em / auto", "10px 2");
test_computed_value("columns", "0.25em auto / auto", "10px");
test_computed_value("columns", "auto 0.25em / auto", "10px");
test_computed_value("columns", "2 auto / auto", "2");
test_computed_value("columns", "auto 2 / auto", "2");
</script>

View File

@ -4,7 +4,8 @@
<meta charset="utf-8">
<title>CSS Multi-column Layout: parsing columns with invalid values</title>
<link rel="help" href="https://drafts.csswg.org/css-multicol/#propdef-columns">
<meta name="assert" content="columns supports only the grammar '<column-width> || <column-count>'.">
<link rel="help" href="https://drafts.csswg.org/css-multicol-2/#propdef-columns">
<meta name="assert" content="columns supports only the grammar '[ <'column-width'> || <'column-count'> ] [ / <'column-height'> ]?'.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
@ -27,6 +28,17 @@ test_invalid_value("columns", "auto auto auto");
test_invalid_value("columns", "10em auto auto");
test_invalid_value("columns", "initial initial");
test_invalid_value("columns", "inherit inherit");
// With "/ <'column-height'>"
test_invalid_value("columns", "/ 100px");
test_invalid_value("columns", "/ auto");
test_invalid_value("columns", "/");
test_invalid_value("columns", "auto /");
test_invalid_value("columns", "100px /");
test_invalid_value("columns", "2 /");
test_invalid_value("columns", "2 100px /");
test_invalid_value("columns", "100px 2 /");
</script>
</body>
</html>

View File

@ -4,7 +4,8 @@
<meta charset="utf-8">
<title>CSS Multi-column Layout: parsing columns with valid values</title>
<link rel="help" href="https://drafts.csswg.org/css-multicol/#propdef-columns">
<meta name="assert" content="columns supports the full grammar '<column-width> || <column-count>'.">
<link rel="help" href="https://drafts.csswg.org/css-multicol-2/#propdef-columns">
<meta name="assert" content="columns supports the full grammar '[ <'column-width'> || <'column-count'> ] [ / <'column-height'> ]?'.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
@ -27,6 +28,17 @@ test_valid_value("columns", "0 1", "0px 1");
test_valid_value("columns", "1 0", "0px 1");
test_valid_value("columns", "0px 1");
// With "/ <'column-height'>"
test_valid_value("columns", "10px 2 / 100px");
test_valid_value("columns", "2 10px / 100px", "10px 2 / 100px");
test_valid_value("columns", "10px / 100px");
test_valid_value("columns", "auto / 100px");
test_valid_value("columns", "10px 2 / auto", "10px 2");
test_valid_value("columns", "2 10px / auto", "10px 2");
test_valid_value("columns", "10px / auto", "10px");
test_valid_value("columns", "2 / auto", "2");
test_valid_value("columns", "auto / auto", "auto");
// CSS-wide keywords.
test_valid_value("columns", "initial");
test_valid_value("columns", "inherit");