LibWeb/CSS: Align declaration block parsing with the spec

We have two different code paths that implement the "parse a CSS
declaration block" algorithm, for properties and descriptors. COmbining
them isn't straightforward, and doesn't seem especially useful.
This commit is contained in:
Sam Atkins 2025-04-14 15:26:43 +01:00
parent 9284530a9f
commit 2d8a997080
5 changed files with 52 additions and 38 deletions

View file

@ -234,7 +234,7 @@ WebIDL::ExceptionOr<void> CSSFontFaceDescriptors::set_css_text(StringView value)
// 3. Parse the given value and, if the return value is not the empty list, insert the items in the list into the
// declarations, in specified order.
auto descriptors = parse_css_list_of_descriptors(Parser::ParsingParams {}, AtRuleID::FontFace, value);
auto descriptors = parse_css_descriptor_declaration_block(Parser::ParsingParams {}, AtRuleID::FontFace, value);
if (!descriptors.is_empty())
m_descriptors = move(descriptors);

View file

@ -1211,7 +1211,7 @@ void CSSStyleProperties::set_declarations_from_text(StringView css_text)
auto parsing_params = owner_node().has_value()
? Parser::ParsingParams(owner_node()->element().document())
: Parser::ParsingParams();
auto style = parse_css_style_attribute(parsing_params, css_text);
auto style = parse_css_property_declaration_block(parsing_params, css_text);
set_the_declarations(style.properties, style.custom_properties);
}

View file

@ -57,18 +57,18 @@ GC::Ref<CSS::CSSStyleSheet> parse_css_stylesheet(CSS::Parser::ParsingParams cons
return style_sheet;
}
CSS::Parser::Parser::PropertiesAndCustomProperties parse_css_style_attribute(CSS::Parser::ParsingParams const& context, StringView css)
CSS::Parser::Parser::PropertiesAndCustomProperties parse_css_property_declaration_block(CSS::Parser::ParsingParams const& context, StringView css)
{
if (css.is_empty())
return {};
return CSS::Parser::Parser::create(context, css).parse_as_style_attribute();
return CSS::Parser::Parser::create(context, css).parse_as_property_declaration_block();
}
Vector<CSS::Descriptor> parse_css_list_of_descriptors(CSS::Parser::ParsingParams const& parsing_params, CSS::AtRuleID at_rule_id, StringView css)
Vector<CSS::Descriptor> parse_css_descriptor_declaration_block(CSS::Parser::ParsingParams const& parsing_params, CSS::AtRuleID at_rule_id, StringView css)
{
if (css.is_empty())
return {};
return CSS::Parser::Parser::create(parsing_params, css).parse_as_list_of_descriptors(at_rule_id);
return CSS::Parser::Parser::create(parsing_params, css).parse_as_descriptor_declaration_block(at_rule_id);
}
RefPtr<CSS::CSSStyleValue> parse_css_value(CSS::Parser::ParsingParams const& context, StringView string, CSS::PropertyID property_id)

View file

@ -1321,7 +1321,8 @@ Vector<Vector<ComponentValue>> Parser::parse_a_comma_separated_list_of_component
return groups;
}
Parser::PropertiesAndCustomProperties Parser::parse_as_style_attribute()
// https://drafts.csswg.org/cssom/#parse-a-css-declaration-block
Parser::PropertiesAndCustomProperties Parser::parse_as_property_declaration_block()
{
auto expand_shorthands = [&](Vector<StyleProperty>& properties) -> Vector<StyleProperty> {
Vector<StyleProperty> expanded_properties;
@ -1341,16 +1342,36 @@ Parser::PropertiesAndCustomProperties Parser::parse_as_style_attribute()
return expanded_properties;
};
// 1. Let declarations be the returned declarations from invoking parse a blocks contents with string.
m_rule_context.append(ContextType::Style);
auto declarations_and_at_rules = parse_a_blocks_contents(m_token_stream);
m_rule_context.take_last();
auto properties = extract_properties(declarations_and_at_rules);
properties.properties = expand_shorthands(properties.properties);
return properties;
// 2. Let parsed declarations be a new empty list.
PropertiesAndCustomProperties parsed_declarations;
// 3. For each item declaration in declarations, follow these substeps:
for (auto const& rule_or_list : declarations_and_at_rules) {
if (rule_or_list.has<Rule>())
continue;
auto& rule_declarations = rule_or_list.get<Vector<Declaration>>();
for (auto const& declaration : rule_declarations) {
// 1. Let parsed declaration be the result of parsing declaration according to the appropriate CSS
// specifications, dropping parts that are said to be ignored. If the whole declaration is dropped, let
// parsed declaration be null.
// 2. If parsed declaration is not null, append it to parsed declarations.
extract_property(declaration, parsed_declarations);
}
}
parsed_declarations.properties = expand_shorthands(parsed_declarations.properties);
// 4. Return parsed declarations.
return parsed_declarations;
}
Vector<Descriptor> Parser::parse_as_list_of_descriptors(AtRuleID at_rule_id)
// https://drafts.csswg.org/cssom/#parse-a-css-declaration-block
Vector<Descriptor> Parser::parse_as_descriptor_declaration_block(AtRuleID at_rule_id)
{
auto context_type = [at_rule_id] {
switch (at_rule_id) {
@ -1362,22 +1383,32 @@ Vector<Descriptor> Parser::parse_as_list_of_descriptors(AtRuleID at_rule_id)
VERIFY_NOT_REACHED();
}();
// 1. Let declarations be the returned declarations from invoking parse a blocks contents with string.
m_rule_context.append(context_type);
auto declarations_and_at_rules = parse_a_blocks_contents(m_token_stream);
m_rule_context.take_last();
Vector<Descriptor> descriptors;
// 2. Let parsed declarations be a new empty list.
Vector<Descriptor> parsed_declarations;
// 3. For each item declaration in declarations, follow these substeps:
for (auto const& rule_or_list : declarations_and_at_rules) {
if (rule_or_list.has<Rule>())
continue;
auto& declarations = rule_or_list.get<Vector<Declaration>>();
for (auto const& declaration : declarations) {
if (auto descriptor = convert_to_descriptor(at_rule_id, declaration); descriptor.has_value())
descriptors.append(descriptor.release_value());
auto& rule_declarations = rule_or_list.get<Vector<Declaration>>();
for (auto const& declaration : rule_declarations) {
// 1. Let parsed declaration be the result of parsing declaration according to the appropriate CSS
// specifications, dropping parts that are said to be ignored. If the whole declaration is dropped, let
// parsed declaration be null.
// 2. If parsed declaration is not null, append it to parsed declarations.
if (auto parsed_declaration = convert_to_descriptor(at_rule_id, declaration); parsed_declaration.has_value())
parsed_declarations.append(parsed_declaration.release_value());
}
}
return descriptors;
// 4. Return parsed declarations.
return parsed_declarations;
}
bool Parser::is_valid_in_the_current_context(Declaration const&) const
@ -1502,22 +1533,6 @@ bool Parser::is_valid_in_the_current_context(QualifiedRule const&) const
VERIFY_NOT_REACHED();
}
Parser::PropertiesAndCustomProperties Parser::extract_properties(Vector<RuleOrListOfDeclarations> const& rules_and_lists_of_declarations)
{
PropertiesAndCustomProperties result;
for (auto const& rule_or_list : rules_and_lists_of_declarations) {
if (rule_or_list.has<Rule>())
continue;
auto& declarations = rule_or_list.get<Vector<Declaration>>();
PropertiesAndCustomProperties& dest = result;
for (auto const& declaration : declarations) {
extract_property(declaration, dest);
}
}
return result;
}
void Parser::extract_property(Declaration const& declaration, PropertiesAndCustomProperties& dest)
{
if (auto maybe_property = convert_to_style_property(declaration); maybe_property.has_value()) {

View file

@ -96,8 +96,8 @@ public:
Vector<StyleProperty> properties;
HashMap<FlyString, StyleProperty> custom_properties;
};
PropertiesAndCustomProperties parse_as_style_attribute();
Vector<Descriptor> parse_as_list_of_descriptors(AtRuleID);
PropertiesAndCustomProperties parse_as_property_declaration_block();
Vector<Descriptor> parse_as_descriptor_declaration_block(AtRuleID);
CSSRule* parse_as_css_rule();
Optional<StyleProperty> parse_as_supports_condition();
@ -464,7 +464,6 @@ private:
static bool has_ignored_vendor_prefix(StringView);
PropertiesAndCustomProperties extract_properties(Vector<RuleOrListOfDeclarations> const&);
void extract_property(Declaration const&, Parser::PropertiesAndCustomProperties&);
DOM::Document const* document() const;
@ -521,8 +520,8 @@ private:
namespace Web {
GC::Ref<CSS::CSSStyleSheet> parse_css_stylesheet(CSS::Parser::ParsingParams const&, StringView, Optional<::URL::URL> location = {}, Vector<NonnullRefPtr<CSS::MediaQuery>> = {});
CSS::Parser::Parser::PropertiesAndCustomProperties parse_css_style_attribute(CSS::Parser::ParsingParams const&, StringView);
Vector<CSS::Descriptor> parse_css_list_of_descriptors(CSS::Parser::ParsingParams const&, CSS::AtRuleID, StringView);
CSS::Parser::Parser::PropertiesAndCustomProperties parse_css_property_declaration_block(CSS::Parser::ParsingParams const&, StringView);
Vector<CSS::Descriptor> parse_css_descriptor_declaration_block(CSS::Parser::ParsingParams const&, CSS::AtRuleID, StringView);
RefPtr<CSS::CSSStyleValue> parse_css_value(CSS::Parser::ParsingParams const&, StringView, CSS::PropertyID property_id = CSS::PropertyID::Invalid);
RefPtr<CSS::CSSStyleValue> parse_css_descriptor(CSS::Parser::ParsingParams const&, CSS::AtRuleID, CSS::DescriptorID, StringView);
Optional<CSS::SelectorList> parse_selector(CSS::Parser::ParsingParams const&, StringView);