mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-22 17:29:01 +00:00
LibWeb: Add generic logic for parsing "positional-value-list-shorthands"
Continues the work started in #5386 to simplify handling of positional value list shorthand properties. Previously we would parse these as `StyleValueList`s and then rely on `StyleComputer::for_each_property_expanding_shorthands` to expand them into longhands. This required a bit of work to handle `ShorthandStyleValue`s for the `@page` `margin` descriptor.
This commit is contained in:
parent
b1e77b3522
commit
a7e5ded188
Notes:
github-actions[bot]
2025-09-09 09:47:05 +00:00
Author: https://github.com/Calme1709
Commit: a7e5ded188
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6136
Reviewed-by: https://github.com/gmta ✅
6 changed files with 57 additions and 199 deletions
|
@ -7,7 +7,7 @@
|
|||
#include <LibWeb/CSS/CSSDescriptors.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/Serialize.h>
|
||||
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||
#include <LibWeb/CSS/StyleValues/ShorthandStyleValue.h>
|
||||
#include <LibWeb/Infra/Strings.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
|
@ -316,36 +316,12 @@ void for_each_expanded_longhand(AtRuleID at_rule, DescriptorID descriptor, RefPt
|
|||
return;
|
||||
}
|
||||
|
||||
if (value->is_value_list()) {
|
||||
auto& values = value->as_value_list().values();
|
||||
if (values.size() == 4) {
|
||||
callback(DescriptorID::MarginTop, values[0]);
|
||||
callback(DescriptorID::MarginRight, values[1]);
|
||||
callback(DescriptorID::MarginBottom, values[2]);
|
||||
callback(DescriptorID::MarginLeft, values[3]);
|
||||
} else if (values.size() == 3) {
|
||||
callback(DescriptorID::MarginTop, values[0]);
|
||||
callback(DescriptorID::MarginRight, values[1]);
|
||||
callback(DescriptorID::MarginBottom, values[2]);
|
||||
callback(DescriptorID::MarginLeft, values[1]);
|
||||
} else if (values.size() == 2) {
|
||||
callback(DescriptorID::MarginTop, values[0]);
|
||||
callback(DescriptorID::MarginRight, values[1]);
|
||||
callback(DescriptorID::MarginBottom, values[0]);
|
||||
callback(DescriptorID::MarginLeft, values[1]);
|
||||
} else if (values.size() == 1) {
|
||||
callback(DescriptorID::MarginTop, values[0]);
|
||||
callback(DescriptorID::MarginRight, values[0]);
|
||||
callback(DescriptorID::MarginBottom, values[0]);
|
||||
callback(DescriptorID::MarginLeft, values[0]);
|
||||
}
|
||||
auto const& shorthand_value = value->as_shorthand();
|
||||
|
||||
} else {
|
||||
callback(DescriptorID::MarginTop, *value);
|
||||
callback(DescriptorID::MarginRight, *value);
|
||||
callback(DescriptorID::MarginBottom, *value);
|
||||
callback(DescriptorID::MarginLeft, *value);
|
||||
}
|
||||
callback(DescriptorID::MarginTop, shorthand_value.longhand(PropertyID::MarginTop));
|
||||
callback(DescriptorID::MarginRight, shorthand_value.longhand(PropertyID::MarginRight));
|
||||
callback(DescriptorID::MarginBottom, shorthand_value.longhand(PropertyID::MarginBottom));
|
||||
callback(DescriptorID::MarginLeft, shorthand_value.longhand(PropertyID::MarginLeft));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,9 +53,8 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_descriptor_v
|
|||
auto value_for_property = value_or_error.release_value();
|
||||
// Descriptors don't accept the following, which properties do:
|
||||
// - CSS-wide keywords
|
||||
// - Shorthands
|
||||
// - Arbitrary substitution functions (so, UnresolvedStyleValue)
|
||||
if (value_for_property->is_css_wide_keyword() || value_for_property->is_shorthand() || value_for_property->is_unresolved())
|
||||
if (value_for_property->is_css_wide_keyword() || value_for_property->is_unresolved())
|
||||
return nullptr;
|
||||
return value_for_property;
|
||||
},
|
||||
|
|
|
@ -345,6 +345,7 @@ private:
|
|||
|
||||
ParseErrorOr<NonnullRefPtr<StyleValue const>> parse_css_value(PropertyID, TokenStream<ComponentValue>&, Optional<String> original_source_text = {});
|
||||
ParseErrorOr<NonnullRefPtr<StyleValue const>> parse_descriptor_value(AtRuleID, DescriptorID, TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue const> parse_positional_value_list_shorthand(PropertyID, TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue const> parse_css_value_for_property(PropertyID, TokenStream<ComponentValue>&);
|
||||
struct PropertyAndValue {
|
||||
PropertyID property;
|
||||
|
|
|
@ -810,6 +810,13 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_css_value(Pr
|
|||
break;
|
||||
}
|
||||
|
||||
if (property_is_positional_value_list_shorthand(property_id)) {
|
||||
if (auto parsed_value = parse_positional_value_list_shorthand(property_id, tokens); parsed_value && !tokens.has_next_token())
|
||||
return parsed_value.release_nonnull();
|
||||
|
||||
return ParseError::SyntaxError;
|
||||
}
|
||||
|
||||
// If there's only 1 ComponentValue, we can only produce a single StyleValue.
|
||||
if (component_values.size() == 1) {
|
||||
auto stream = TokenStream { component_values };
|
||||
|
@ -891,6 +898,48 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_css_value(Pr
|
|||
return { ShorthandStyleValue::create(property_id, move(longhand_properties), move(longhand_values)) };
|
||||
}
|
||||
|
||||
RefPtr<StyleValue const> Parser::parse_positional_value_list_shorthand(PropertyID property_id, TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
auto const& longhands = longhands_for_shorthand(property_id);
|
||||
|
||||
Vector<ValueComparingNonnullRefPtr<StyleValue const>> parsed_values;
|
||||
|
||||
while (auto parsed_value = parse_css_value_for_property(property_id, tokens))
|
||||
parsed_values.append(parsed_value.release_nonnull());
|
||||
|
||||
if (parsed_values.size() == 0 || parsed_values.size() > longhands.size())
|
||||
return nullptr;
|
||||
|
||||
switch (longhands.size()) {
|
||||
case 2: {
|
||||
switch (parsed_values.size()) {
|
||||
case 1:
|
||||
return ShorthandStyleValue::create(property_id, longhands, { parsed_values[0], parsed_values[0] });
|
||||
case 2:
|
||||
return ShorthandStyleValue::create(property_id, longhands, parsed_values);
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
case 4: {
|
||||
switch (parsed_values.size()) {
|
||||
case 1:
|
||||
return ShorthandStyleValue::create(property_id, longhands, { parsed_values[0], parsed_values[0], parsed_values[0], parsed_values[0] });
|
||||
case 2:
|
||||
return ShorthandStyleValue::create(property_id, longhands, { parsed_values[0], parsed_values[1], parsed_values[0], parsed_values[1] });
|
||||
case 3:
|
||||
return ShorthandStyleValue::create(property_id, longhands, { parsed_values[0], parsed_values[1], parsed_values[2], parsed_values[1] });
|
||||
case 4:
|
||||
return ShorthandStyleValue::create(property_id, longhands, parsed_values);
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
default:
|
||||
TODO();
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<StyleValue const> Parser::parse_color_scheme_value(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
// normal | [ light | dark | <custom-ident> ]+ && only?
|
||||
|
|
|
@ -517,7 +517,6 @@
|
|||
"border-block-start-color",
|
||||
"border-block-end-color"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"color"
|
||||
]
|
||||
|
@ -590,7 +589,6 @@
|
|||
"border-block-start-style",
|
||||
"border-block-end-style"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"line-style"
|
||||
]
|
||||
|
@ -603,7 +601,6 @@
|
|||
"border-block-start-width",
|
||||
"border-block-end-width"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"length [0,∞]",
|
||||
"line-width"
|
||||
|
@ -692,7 +689,6 @@
|
|||
"border-bottom-color",
|
||||
"border-left-color"
|
||||
],
|
||||
"max-values": 4,
|
||||
"valid-types": [
|
||||
"color"
|
||||
],
|
||||
|
@ -803,7 +799,6 @@
|
|||
"border-inline-start-color",
|
||||
"border-inline-end-color"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"color"
|
||||
]
|
||||
|
@ -876,7 +871,6 @@
|
|||
"border-inline-start-style",
|
||||
"border-inline-end-style"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"line-style"
|
||||
]
|
||||
|
@ -889,7 +883,6 @@
|
|||
"border-inline-start-width",
|
||||
"border-inline-end-width"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"length [0,∞]",
|
||||
"line-width"
|
||||
|
@ -1029,7 +1022,6 @@
|
|||
"border-bottom-style",
|
||||
"border-left-style"
|
||||
],
|
||||
"max-values": 4,
|
||||
"valid-types": [
|
||||
"line-style"
|
||||
]
|
||||
|
@ -1108,7 +1100,6 @@
|
|||
"border-bottom-width",
|
||||
"border-left-width"
|
||||
],
|
||||
"max-values": 4,
|
||||
"valid-types": [
|
||||
"length [0,∞]",
|
||||
"line-width"
|
||||
|
@ -1800,7 +1791,6 @@
|
|||
"length [0,∞]",
|
||||
"percentage [0,∞]"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-identifiers": [
|
||||
"normal"
|
||||
],
|
||||
|
@ -2064,7 +2054,6 @@
|
|||
"bottom",
|
||||
"left"
|
||||
],
|
||||
"max-values": 4,
|
||||
"valid-types": [
|
||||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
|
@ -2081,7 +2070,6 @@
|
|||
"inset-block-start",
|
||||
"inset-block-end"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
|
@ -2112,7 +2100,6 @@
|
|||
"inset-inline-start",
|
||||
"inset-inline-end"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
|
@ -2262,7 +2249,6 @@
|
|||
"margin-bottom",
|
||||
"margin-left"
|
||||
],
|
||||
"max-values": 4,
|
||||
"valid-types": [
|
||||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
|
@ -2282,7 +2268,6 @@
|
|||
"margin-block-start",
|
||||
"margin-block-end"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
|
@ -2327,7 +2312,6 @@
|
|||
"margin-inline-start",
|
||||
"margin-inline-end"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
|
@ -2750,7 +2734,6 @@
|
|||
"positional-value-list-shorthand": true,
|
||||
"inherited": false,
|
||||
"initial": "visible",
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"overflow"
|
||||
]
|
||||
|
@ -2787,7 +2770,6 @@
|
|||
"padding-bottom",
|
||||
"padding-left"
|
||||
],
|
||||
"max-values": 4,
|
||||
"valid-types": [
|
||||
"length [0,∞]",
|
||||
"percentage [0,∞]"
|
||||
|
@ -2804,7 +2786,6 @@
|
|||
"padding-block-start",
|
||||
"padding-block-end"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"length [0,∞]",
|
||||
"percentage [0,∞]"
|
||||
|
@ -2843,7 +2824,6 @@
|
|||
"padding-inline-start",
|
||||
"padding-inline-end"
|
||||
],
|
||||
"max-values": 2,
|
||||
"valid-types": [
|
||||
"length [0,∞]",
|
||||
"percentage [0,∞]"
|
||||
|
|
|
@ -675,96 +675,6 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i
|
|||
return;
|
||||
}
|
||||
|
||||
// FIXME: We should add logic in parse_css_value to parse "positional-value-list-shorthand"s as
|
||||
// ShorthandStyleValues to avoid the need for this (and assign_start_and_end_values).
|
||||
auto assign_edge_values = [&](PropertyID top_property, PropertyID right_property, PropertyID bottom_property, PropertyID left_property, StyleValue const& value) {
|
||||
if (value.is_value_list()) {
|
||||
auto values = value.as_value_list().values();
|
||||
|
||||
if (values.size() == 4) {
|
||||
set_longhand_property(top_property, values[0]);
|
||||
set_longhand_property(right_property, values[1]);
|
||||
set_longhand_property(bottom_property, values[2]);
|
||||
set_longhand_property(left_property, values[3]);
|
||||
} else if (values.size() == 3) {
|
||||
set_longhand_property(top_property, values[0]);
|
||||
set_longhand_property(right_property, values[1]);
|
||||
set_longhand_property(bottom_property, values[2]);
|
||||
set_longhand_property(left_property, values[1]);
|
||||
} else if (values.size() == 2) {
|
||||
set_longhand_property(top_property, values[0]);
|
||||
set_longhand_property(right_property, values[1]);
|
||||
set_longhand_property(bottom_property, values[0]);
|
||||
set_longhand_property(left_property, values[1]);
|
||||
} else if (values.size() == 1) {
|
||||
set_longhand_property(top_property, values[0]);
|
||||
set_longhand_property(right_property, values[0]);
|
||||
set_longhand_property(bottom_property, values[0]);
|
||||
set_longhand_property(left_property, values[0]);
|
||||
}
|
||||
} else {
|
||||
set_longhand_property(top_property, value);
|
||||
set_longhand_property(right_property, value);
|
||||
set_longhand_property(bottom_property, value);
|
||||
set_longhand_property(left_property, value);
|
||||
}
|
||||
};
|
||||
|
||||
auto assign_start_and_end_values = [&](PropertyID start_property, PropertyID end_property, auto const& values) {
|
||||
if (values.is_value_list()) {
|
||||
set_longhand_property(start_property, value.as_value_list().values()[0]);
|
||||
set_longhand_property(end_property, value.as_value_list().values()[1]);
|
||||
} else {
|
||||
set_longhand_property(start_property, value);
|
||||
set_longhand_property(end_property, value);
|
||||
}
|
||||
};
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderStyle) {
|
||||
assign_edge_values(PropertyID::BorderTopStyle, PropertyID::BorderRightStyle, PropertyID::BorderBottomStyle, PropertyID::BorderLeftStyle, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderBlockStyle) {
|
||||
assign_start_and_end_values(PropertyID::BorderBlockStartStyle, PropertyID::BorderBlockEndStyle, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderInlineStyle) {
|
||||
assign_start_and_end_values(PropertyID::BorderInlineStartStyle, PropertyID::BorderInlineEndStyle, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderWidth) {
|
||||
assign_edge_values(PropertyID::BorderTopWidth, PropertyID::BorderRightWidth, PropertyID::BorderBottomWidth, PropertyID::BorderLeftWidth, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderBlockWidth) {
|
||||
assign_start_and_end_values(PropertyID::BorderBlockStartWidth, PropertyID::BorderBlockEndWidth, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderInlineWidth) {
|
||||
assign_start_and_end_values(PropertyID::BorderInlineStartWidth, PropertyID::BorderInlineEndWidth, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderColor) {
|
||||
assign_edge_values(PropertyID::BorderTopColor, PropertyID::BorderRightColor, PropertyID::BorderBottomColor, PropertyID::BorderLeftColor, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderBlockColor) {
|
||||
assign_start_and_end_values(PropertyID::BorderBlockStartColor, PropertyID::BorderBlockEndColor, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BorderInlineColor) {
|
||||
assign_start_and_end_values(PropertyID::BorderInlineStartColor, PropertyID::BorderInlineEndColor, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::BackgroundPosition) {
|
||||
if (value.is_position()) {
|
||||
auto const& position = value.as_position();
|
||||
|
@ -797,63 +707,6 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i
|
|||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::Inset) {
|
||||
assign_edge_values(PropertyID::Top, PropertyID::Right, PropertyID::Bottom, PropertyID::Left, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::InsetBlock) {
|
||||
assign_start_and_end_values(PropertyID::InsetBlockStart, PropertyID::InsetBlockEnd, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::InsetInline) {
|
||||
assign_start_and_end_values(PropertyID::InsetInlineStart, PropertyID::InsetInlineEnd, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::Margin) {
|
||||
assign_edge_values(PropertyID::MarginTop, PropertyID::MarginRight, PropertyID::MarginBottom, PropertyID::MarginLeft, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::MarginBlock) {
|
||||
assign_start_and_end_values(PropertyID::MarginBlockStart, PropertyID::MarginBlockEnd, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::MarginInline) {
|
||||
assign_start_and_end_values(PropertyID::MarginInlineStart, PropertyID::MarginInlineEnd, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::Padding) {
|
||||
assign_edge_values(PropertyID::PaddingTop, PropertyID::PaddingRight, PropertyID::PaddingBottom, PropertyID::PaddingLeft, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::PaddingBlock) {
|
||||
assign_start_and_end_values(PropertyID::PaddingBlockStart, PropertyID::PaddingBlockEnd, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::PaddingInline) {
|
||||
assign_start_and_end_values(PropertyID::PaddingInlineStart, PropertyID::PaddingInlineEnd, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::Gap) {
|
||||
if (value.is_value_list()) {
|
||||
auto const& values_list = value.as_value_list();
|
||||
set_longhand_property(CSS::PropertyID::RowGap, values_list.values()[0]);
|
||||
set_longhand_property(CSS::PropertyID::ColumnGap, values_list.values()[1]);
|
||||
return;
|
||||
}
|
||||
set_longhand_property(CSS::PropertyID::RowGap, value);
|
||||
set_longhand_property(CSS::PropertyID::ColumnGap, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::Transition) {
|
||||
if (value.to_keyword() == Keyword::None) {
|
||||
// Handle `none` as a shorthand for `all 0s ease 0s`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue