From e7243f009076588c158f2eb3ebeb6624a76907b3 Mon Sep 17 00:00:00 2001 From: Callum Law Date: Thu, 5 Mar 2026 00:03:57 +1300 Subject: [PATCH] LibWeb: Support custom descriptors Some at-rules (i.e. `@function`) require us to support custom descriptors (e.g. `--foo`). We do this by adding `DescriptorID::Custom` and using a new `DescriptorNameAndID` class in a bunch of places where we previously just used `DescriptorID` --- Documentation/CSSGeneratedFiles.md | 18 ++++- Libraries/LibWeb/CSS/CSSCounterStyleRule.cpp | 20 ++--- Libraries/LibWeb/CSS/CSSDescriptors.cpp | 80 +++++++++---------- Libraries/LibWeb/CSS/CSSDescriptors.h | 10 +-- Libraries/LibWeb/CSS/CSSFontFaceRule.cpp | 20 ++--- Libraries/LibWeb/CSS/Descriptor.h | 4 +- Libraries/LibWeb/CSS/DescriptorNameAndID.h | 72 +++++++++++++++++ Libraries/LibWeb/CSS/FontFace.cpp | 50 ++++++------ Libraries/LibWeb/CSS/ParsedFontFace.cpp | 28 +++---- .../LibWeb/CSS/Parser/DescriptorParsing.cpp | 20 ++--- Libraries/LibWeb/CSS/Parser/Helpers.cpp | 4 +- Libraries/LibWeb/CSS/Parser/Parser.cpp | 4 +- Libraries/LibWeb/CSS/Parser/Parser.h | 6 +- Libraries/LibWeb/CSS/Parser/RuleParsing.cpp | 20 ++--- Libraries/LibWeb/Dump.cpp | 2 +- .../LibWeb/GenerateCSSDescriptors.cpp | 66 +++++++++++---- 16 files changed, 272 insertions(+), 152 deletions(-) create mode 100644 Libraries/LibWeb/CSS/DescriptorNameAndID.h diff --git a/Documentation/CSSGeneratedFiles.md b/Documentation/CSSGeneratedFiles.md index bf3db2465e..a2a383eac7 100644 --- a/Documentation/CSSGeneratedFiles.md +++ b/Documentation/CSSGeneratedFiles.md @@ -173,10 +173,11 @@ The generated code provides: Each at-rule object has the following fields. Both are required. -| Field | Description | -|---------------|---------------------------------------------------------------------------------------------------| -| `spec` | String. URL to the spec that defines this at-rule. | -| `descriptors` | Object, with keys being descriptor names and values being objects of their properties. See below. | +| Field | Description | +|----------------------|----------------------------------------------------------------------------------------------------------------------| +| `spec` | String. URL to the spec that defines this at-rule. | +| `descriptors` | Object, with keys being descriptor names and values being objects of their properties. See below. | +| `custom-descriptors` | Object, the configuration for custom descriptors (i.e. --foo). Omission means that custom descriptors are disallowed | ### Descriptor fields @@ -189,6 +190,15 @@ Each descriptor object can have the following fields: | `syntax` | Yes | Array of strings. Each string is one option, taken from the spec. | | `FIXME` or `NOTE` | No | Strings, for when you want to leave a note. | +### Custom descriptor fields + +Each custom descriptor object has the following fields + +| Field | Required | Description | +|--------------------|----------|----------------------------------------------| +| `syntax` | Yes | Array of strings. Each string is one option. | +| `FIXME` or `NOTE` | No | Strings, for when you want to leave a note. | + ## Keywords.json This is a single JSON array of strings, each of which is a CSS keyword, for example `auto`, `none`, `medium`, or `currentcolor`. diff --git a/Libraries/LibWeb/CSS/CSSCounterStyleRule.cpp b/Libraries/LibWeb/CSS/CSSCounterStyleRule.cpp index 2d6f1f552b..b3a8658fb4 100644 --- a/Libraries/LibWeb/CSS/CSSCounterStyleRule.cpp +++ b/Libraries/LibWeb/CSS/CSSCounterStyleRule.cpp @@ -137,7 +137,7 @@ void CSSCounterStyleRule::set_system(FlyString const& system) { // 1. parse the given value as the descriptor associated with the attribute. Parser::ParsingParams parsing_params { realm() }; - auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::System, system); + auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::System), system); // 2. If the result is invalid according to the given descriptor’s grammar, or would cause the @counter-style rule // to not define a counter style, do nothing and abort these steps. (For example, some systems require the @@ -169,7 +169,7 @@ void CSSCounterStyleRule::set_negative(FlyString const& negative) { Parser::ParsingParams parsing_params { realm() }; - if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::Negative, negative)) + if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::Negative), negative)) m_negative = value; } @@ -185,7 +185,7 @@ void CSSCounterStyleRule::set_prefix(FlyString const& prefix) { Parser::ParsingParams parsing_params { realm() }; - if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::Prefix, prefix)) + if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::Prefix), prefix)) m_prefix = value; } @@ -201,7 +201,7 @@ void CSSCounterStyleRule::set_suffix(FlyString const& suffix) { Parser::ParsingParams parsing_params { realm() }; - if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::Suffix, suffix)) + if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::Suffix), suffix)) m_suffix = value; } @@ -217,7 +217,7 @@ void CSSCounterStyleRule::set_range(FlyString const& range) { Parser::ParsingParams parsing_params { realm() }; - if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::Range, range)) + if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::Range), range)) m_range = value; } @@ -233,7 +233,7 @@ void CSSCounterStyleRule::set_pad(FlyString const& pad) { Parser::ParsingParams parsing_params { realm() }; - if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::Pad, pad)) + if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::Pad), pad)) m_pad = value; } @@ -249,7 +249,7 @@ void CSSCounterStyleRule::set_fallback(FlyString const& fallback) { Parser::ParsingParams parsing_params { realm() }; - if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::Fallback, fallback)) + if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::Fallback), fallback)) m_fallback = value; } @@ -269,7 +269,7 @@ void CSSCounterStyleRule::set_symbols(FlyString const& symbols) // 1. parse the given value as the descriptor associated with the attribute. Parser::ParsingParams parsing_params { realm() }; - auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::Symbols, symbols); + auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::Symbols), symbols); // 2. If the result is invalid according to the given descriptor’s grammar, or would cause the @counter-style rule // to not define a counter style, do nothing and abort these steps. (For example, some systems require the @@ -300,7 +300,7 @@ void CSSCounterStyleRule::set_additive_symbols(FlyString const& additive_symbols // 1. parse the given value as the descriptor associated with the attribute. Parser::ParsingParams parsing_params { realm() }; - auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::AdditiveSymbols, additive_symbols); + auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::AdditiveSymbols), additive_symbols); // 2. If the result is invalid according to the given descriptor’s grammar, or would cause the @counter-style rule // to not define a counter style, do nothing and abort these steps. (For example, some systems require the @@ -327,7 +327,7 @@ void CSSCounterStyleRule::set_speak_as(FlyString const& speak_as) { Parser::ParsingParams parsing_params { realm() }; - if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, CSS::DescriptorID::SpeakAs, speak_as)) + if (auto value = parse_css_descriptor(parsing_params, CSS::AtRuleID::CounterStyle, DescriptorNameAndID::from_id(CSS::DescriptorID::SpeakAs), speak_as)) m_speak_as = value; } diff --git a/Libraries/LibWeb/CSS/CSSDescriptors.cpp b/Libraries/LibWeb/CSS/CSSDescriptors.cpp index 3668c94321..02f31c3ad8 100644 --- a/Libraries/LibWeb/CSS/CSSDescriptors.cpp +++ b/Libraries/LibWeb/CSS/CSSDescriptors.cpp @@ -38,16 +38,16 @@ String CSSDescriptors::item(size_t index) const if (index >= length()) return {}; - return to_string(m_descriptors[index].descriptor_id).to_string(); + return m_descriptors[index].descriptor_name_and_id.name().to_string(); } // https://drafts.csswg.org/cssom/#set-a-css-declaration -bool CSSDescriptors::set_a_css_declaration(DescriptorID descriptor_id, NonnullRefPtr value, Important) +bool CSSDescriptors::set_a_css_declaration(DescriptorNameAndID const& descriptor_name_and_id, NonnullRefPtr value, Important) { VERIFY(!is_computed()); for (auto& descriptor : m_descriptors) { - if (descriptor.descriptor_id == descriptor_id) { + if (descriptor.descriptor_name_and_id == descriptor_name_and_id) { if (*descriptor.value == *value) return false; descriptor.value = move(value); @@ -56,7 +56,7 @@ bool CSSDescriptors::set_a_css_declaration(DescriptorID descriptor_id, NonnullRe } m_descriptors.append(Descriptor { - .descriptor_id = descriptor_id, + .descriptor_name_and_id = descriptor_name_and_id, .value = move(value), }); return true; @@ -70,12 +70,12 @@ WebIDL::ExceptionOr CSSDescriptors::set_property(FlyString const& property return WebIDL::NoModificationAllowedError::create(realm(), "Cannot modify properties of readonly CSSStyleDeclaration"_utf16); // 2. If property is not a custom property, follow these substeps: - Optional descriptor_id; + Optional descriptor_name_and_id; { // 1. Let property be property converted to ASCII lowercase. // 2. If property is not a case-sensitive match for a supported CSS property, then return. - descriptor_id = descriptor_id_from_string(m_at_rule_id, property); - if (!descriptor_id.has_value()) + descriptor_name_and_id = DescriptorNameAndID::from_name(m_at_rule_id, property); + if (!descriptor_name_and_id.has_value()) return {}; } @@ -90,7 +90,7 @@ WebIDL::ExceptionOr CSSDescriptors::set_property(FlyString const& property return {}; // 5. Let component value list be the result of parsing value for property property. - RefPtr component_value_list = parse_css_descriptor(Parser::ParsingParams {}, m_at_rule_id, *descriptor_id, value); + RefPtr component_value_list = parse_css_descriptor(Parser::ParsingParams {}, m_at_rule_id, *descriptor_name_and_id, value); // 6. If component value list is null, then return. if (!component_value_list) @@ -100,14 +100,14 @@ WebIDL::ExceptionOr CSSDescriptors::set_property(FlyString const& property auto updated = false; // 8. If property is a shorthand property, then for each longhand property longhand that property maps to, in canonical order, follow these substeps: - if (is_shorthand(m_at_rule_id, *descriptor_id)) { - for_each_expanded_longhand(m_at_rule_id, *descriptor_id, component_value_list, [this, &updated, priority](DescriptorID longhand_id, auto longhand_value) { + if (is_shorthand(m_at_rule_id, *descriptor_name_and_id)) { + for_each_expanded_longhand(m_at_rule_id, *descriptor_name_and_id, component_value_list, [this, &updated, priority](DescriptorNameAndID const& longhand_name_and_id, auto longhand_value) { VERIFY(longhand_value); // 1. Let longhand result be the result of set the CSS declaration longhand with the appropriate value(s) // from component value list, with the important flag set if priority is not the empty string, and unset // otherwise, and with the list of declarations being the declarations. - auto longhand_result = set_a_css_declaration(longhand_id, longhand_value.release_nonnull(), priority.is_empty() ? Important::No : Important::Yes); + auto longhand_result = set_a_css_declaration(longhand_name_and_id, longhand_value.release_nonnull(), priority.is_empty() ? Important::No : Important::Yes); // 2. If longhand result is true, let updated be true. if (longhand_result) @@ -118,7 +118,7 @@ WebIDL::ExceptionOr CSSDescriptors::set_property(FlyString const& property // with the important flag set if priority is not the empty string, and unset otherwise, and with the list of // declarations being the declarations. else { - updated = set_a_css_declaration(*descriptor_id, *component_value_list, !priority.is_empty() ? Important::Yes : Important::No); + updated = set_a_css_declaration(*descriptor_name_and_id, *component_value_list, !priority.is_empty() ? Important::Yes : Important::No); } // 10. If updated is true, update style attribute for the CSS declaration block. @@ -143,22 +143,22 @@ WebIDL::ExceptionOr CSSDescriptors::remove_property(FlyString const& pro // 4. Let removed be false. bool removed = false; - auto descriptor_id = descriptor_id_from_string(m_at_rule_id, property); + auto descriptor_name_and_id = DescriptorNameAndID::from_name(m_at_rule_id, property); // 5. If property is a shorthand property, for each longhand property longhand that property maps to: - if (descriptor_id.has_value() && is_shorthand(m_at_rule_id, *descriptor_id)) { - for_each_expanded_longhand(m_at_rule_id, *descriptor_id, nullptr, [this, &removed](DescriptorID longhand_id, auto const&) { + if (descriptor_name_and_id.has_value() && is_shorthand(m_at_rule_id, *descriptor_name_and_id)) { + for_each_expanded_longhand(m_at_rule_id, *descriptor_name_and_id, nullptr, [this, &removed](DescriptorNameAndID const& longhand_name_and_id, auto const&) { // 1. If longhand is not a property name of a CSS declaration in the declarations, continue. // 2. Remove that CSS declaration and let removed be true. - if (m_descriptors.remove_first_matching([longhand_id](auto& entry) { return entry.descriptor_id == longhand_id; })) { + if (m_descriptors.remove_first_matching([longhand_name_and_id](Descriptor const& entry) { return entry.descriptor_name_and_id == longhand_name_and_id; })) { removed = true; } }); } // 6. Otherwise, if property is a case-sensitive match for a property name of a CSS declaration in the // declarations, remove that CSS declaration and let removed be true. - else if (descriptor_id.has_value()) { - removed = m_descriptors.remove_first_matching([descriptor_id](auto& entry) { return entry.descriptor_id == *descriptor_id; }); + else if (descriptor_name_and_id.has_value()) { + removed = m_descriptors.remove_first_matching([descriptor_name_and_id](Descriptor const& entry) { return entry.descriptor_name_and_id == descriptor_name_and_id; }); } // 7. If removed is true, Update style attribute for the CSS declaration block. @@ -177,9 +177,9 @@ String CSSDescriptors::get_property_value(FlyString const& property) const // 2. If property is a case-sensitive match for a property name of a CSS declaration in the declarations, then // return the result of invoking serialize a CSS value of that declaration. - auto descriptor_id = descriptor_id_from_string(m_at_rule_id, property); - if (descriptor_id.has_value()) { - auto match = m_descriptors.first_matching([descriptor_id](auto& entry) { return entry.descriptor_id == *descriptor_id; }); + auto descriptor_name_and_id = DescriptorNameAndID::from_name(m_at_rule_id, property); + if (descriptor_name_and_id.has_value()) { + auto match = m_descriptors.first_matching([descriptor_name_and_id](Descriptor const& entry) { return entry.descriptor_name_and_id == descriptor_name_and_id; }); if (match.has_value()) return match->value->to_string(SerializationMode::Normal); } @@ -208,7 +208,7 @@ String CSSDescriptors::serialized() const // 3. Declaration loop: For each CSS declaration declaration in declaration block’s declarations, follow these substeps: for (auto const& descriptor : m_descriptors) { // 1. Let property be declaration’s property name. - auto property = to_string(descriptor.descriptor_id); + auto property = descriptor.descriptor_name_and_id.name(); // 2. If property is in already serialized, continue with the steps labeled declaration loop. // AD-HOC: Not needed as we don't have shorthands. @@ -264,49 +264,49 @@ void CSSDescriptors::visit_edges(Visitor& visitor) } } -RefPtr CSSDescriptors::descriptor(DescriptorID descriptor_id) const +RefPtr CSSDescriptors::descriptor(DescriptorNameAndID const& descriptor_name_and_id) const { - auto match = m_descriptors.first_matching([descriptor_id](Descriptor const& descriptor) { - return descriptor.descriptor_id == descriptor_id; + auto match = m_descriptors.first_matching([descriptor_name_and_id](Descriptor const& descriptor) { + return descriptor.descriptor_name_and_id == descriptor_name_and_id; }); if (match.has_value()) return match->value; return nullptr; } -RefPtr CSSDescriptors::descriptor_or_initial_value(DescriptorID descriptor_id) const +RefPtr CSSDescriptors::descriptor_or_initial_value(DescriptorNameAndID const& descriptor_name_and_id) const { - if (auto value = descriptor(descriptor_id)) + if (auto value = descriptor(descriptor_name_and_id)) return value.release_nonnull(); - return descriptor_initial_value(m_at_rule_id, descriptor_id); + return descriptor_initial_value(m_at_rule_id, descriptor_name_and_id.id()); } -bool is_shorthand(AtRuleID at_rule, DescriptorID descriptor) +bool is_shorthand(AtRuleID at_rule, DescriptorNameAndID const& descriptor) { - if (at_rule == AtRuleID::Page && descriptor == DescriptorID::Margin) + if (at_rule == AtRuleID::Page && descriptor.id() == DescriptorID::Margin) return true; return false; } -void for_each_expanded_longhand(AtRuleID at_rule, DescriptorID descriptor, RefPtr value, Function)> callback) +void for_each_expanded_longhand(AtRuleID at_rule, DescriptorNameAndID const& descriptor, RefPtr value, Function)> callback) { - if (at_rule == AtRuleID::Page && descriptor == DescriptorID::Margin) { + if (at_rule == AtRuleID::Page && descriptor.id() == DescriptorID::Margin) { if (!value) { - callback(DescriptorID::MarginTop, nullptr); - callback(DescriptorID::MarginRight, nullptr); - callback(DescriptorID::MarginBottom, nullptr); - callback(DescriptorID::MarginLeft, nullptr); + callback(DescriptorNameAndID::from_id(DescriptorID::MarginTop), nullptr); + callback(DescriptorNameAndID::from_id(DescriptorID::MarginRight), nullptr); + callback(DescriptorNameAndID::from_id(DescriptorID::MarginBottom), nullptr); + callback(DescriptorNameAndID::from_id(DescriptorID::MarginLeft), nullptr); return; } auto const& shorthand_value = value->as_shorthand(); - 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)); + callback(DescriptorNameAndID::from_id(DescriptorID::MarginTop), shorthand_value.longhand(PropertyID::MarginTop)); + callback(DescriptorNameAndID::from_id(DescriptorID::MarginRight), shorthand_value.longhand(PropertyID::MarginRight)); + callback(DescriptorNameAndID::from_id(DescriptorID::MarginBottom), shorthand_value.longhand(PropertyID::MarginBottom)); + callback(DescriptorNameAndID::from_id(DescriptorID::MarginLeft), shorthand_value.longhand(PropertyID::MarginLeft)); } } diff --git a/Libraries/LibWeb/CSS/CSSDescriptors.h b/Libraries/LibWeb/CSS/CSSDescriptors.h index c99ab946cd..81adb403ed 100644 --- a/Libraries/LibWeb/CSS/CSSDescriptors.h +++ b/Libraries/LibWeb/CSS/CSSDescriptors.h @@ -27,8 +27,8 @@ public: virtual StringView get_property_priority(FlyString const& property) const override; Vector const& descriptors() const { return m_descriptors; } - RefPtr descriptor(DescriptorID) const; - RefPtr descriptor_or_initial_value(DescriptorID) const; + RefPtr descriptor(DescriptorNameAndID const&) const; + RefPtr descriptor_or_initial_value(DescriptorNameAndID const&) const; virtual String serialized() const override; virtual WebIDL::ExceptionOr set_css_text(StringView) override; @@ -37,7 +37,7 @@ protected: CSSDescriptors(JS::Realm&, AtRuleID, Vector); private: - bool set_a_css_declaration(DescriptorID, NonnullRefPtr, Important); + bool set_a_css_declaration(DescriptorNameAndID const&, NonnullRefPtr, Important); virtual void visit_edges(Visitor&) override; @@ -45,7 +45,7 @@ private: Vector m_descriptors; }; -bool is_shorthand(AtRuleID, DescriptorID); -void for_each_expanded_longhand(AtRuleID, DescriptorID, RefPtr, Function)>); +bool is_shorthand(AtRuleID, DescriptorNameAndID const&); +void for_each_expanded_longhand(AtRuleID, DescriptorNameAndID const&, RefPtr, Function)>); } diff --git a/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp b/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp index 5f1aa14ae9..07fc63112c 100644 --- a/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp +++ b/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp @@ -45,8 +45,8 @@ bool CSSFontFaceRule::is_valid() const // @font-face rules require a font-family and src descriptor; if either of these are missing, the @font-face rule // must not be considered when performing the font matching algorithm. // https://drafts.csswg.org/css-fonts-4/#font-face-rule - return !m_style->descriptor(DescriptorID::FontFamily).is_null() - && !m_style->descriptor(DescriptorID::Src).is_null(); + return !m_style->descriptor(DescriptorNameAndID::from_id(DescriptorID::FontFamily)).is_null() + && !m_style->descriptor(DescriptorNameAndID::from_id(DescriptorID::Src)).is_null(); } ParsedFontFace CSSFontFaceRule::font_face() const @@ -69,21 +69,21 @@ String CSSFontFaceRule::serialized() const // AD-HOC: We don't necessary always have a font-family descriptor as the spec assumes, // see https://github.com/w3c/csswg-drafts/issues/13323 - if (auto font_family = descriptors.descriptor(DescriptorID::FontFamily); !font_family.is_null()) { + if (auto font_family = descriptors.descriptor(DescriptorNameAndID::from_id(DescriptorID::FontFamily)); !font_family.is_null()) { builder.append(' '); // 2. The string "font-family:", followed by a single SPACE (U+0020). builder.append("font-family: "sv); // 3. The result of performing serialize a string on the rule’s font family name. - descriptors.descriptor(DescriptorID::FontFamily)->serialize(builder, SerializationMode::Normal); + descriptors.descriptor(DescriptorNameAndID::from_id(DescriptorID::FontFamily))->serialize(builder, SerializationMode::Normal); // 4. The string ";", i.e., SEMICOLON (U+003B). builder.append(';'); } // 5. If the rule’s associated source list is not empty, follow these substeps: - if (auto sources = descriptors.descriptor(DescriptorID::Src)) { + if (auto sources = descriptors.descriptor(DescriptorNameAndID::from_id(DescriptorID::Src))) { // 1. A single SPACE (U+0020), followed by the string "src:", followed by a single SPACE (U+0020). builder.append(" src: "sv); @@ -95,7 +95,7 @@ String CSSFontFaceRule::serialized() const } // 6. If rule’s associated unicode-range descriptor is present, a single SPACE (U+0020), followed by the string "unicode-range:", followed by a single SPACE (U+0020), followed by the result of performing serialize a <'unicode-range'>, followed by the string ";", i.e., SEMICOLON (U+003B). - if (auto unicode_range = descriptors.descriptor(DescriptorID::UnicodeRange)) { + if (auto unicode_range = descriptors.descriptor(DescriptorNameAndID::from_id(DescriptorID::UnicodeRange))) { builder.append(" unicode-range: "sv); unicode_range->serialize(builder, SerializationMode::Normal); builder.append(';'); @@ -110,7 +110,7 @@ String CSSFontFaceRule::serialized() const // followed by the string "font-feature-settings:", followed by a single SPACE (U+0020), // followed by the result of performing serialize a <'font-feature-settings'>, // followed by the string ";", i.e., SEMICOLON (U+003B). - if (auto font_feature_settings = descriptors.descriptor(DescriptorID::FontFeatureSettings)) { + if (auto font_feature_settings = descriptors.descriptor(DescriptorNameAndID::from_id(DescriptorID::FontFeatureSettings))) { builder.append(" font-feature-settings: "sv); font_feature_settings->serialize(builder, SerializationMode::Normal); builder.append(";"sv); @@ -121,7 +121,7 @@ String CSSFontFaceRule::serialized() const // followed by the result of performing serialize a <'font-stretch'>, // followed by the string ";", i.e., SEMICOLON (U+003B). // NOTE: font-stretch is now an alias for font-width, so we use that instead. - if (auto font_width = descriptors.descriptor(DescriptorID::FontWidth)) { + if (auto font_width = descriptors.descriptor(DescriptorNameAndID::from_id(DescriptorID::FontWidth))) { builder.append(" font-stretch: "sv); font_width->serialize(builder, SerializationMode::Normal); builder.append(";"sv); @@ -131,7 +131,7 @@ String CSSFontFaceRule::serialized() const // followed by the string "font-weight:", followed by a single SPACE (U+0020), // followed by the result of performing serialize a <'font-weight'>, // followed by the string ";", i.e., SEMICOLON (U+003B). - if (auto font_weight = descriptors.descriptor(DescriptorID::FontWeight)) { + if (auto font_weight = descriptors.descriptor(DescriptorNameAndID::from_id(DescriptorID::FontWeight))) { builder.append(" font-weight: "sv); font_weight->serialize(builder, SerializationMode::Normal); builder.append(";"sv); @@ -141,7 +141,7 @@ String CSSFontFaceRule::serialized() const // followed by the string "font-style:", followed by a single SPACE (U+0020), // followed by the result of performing serialize a <'font-style'>, // followed by the string ";", i.e., SEMICOLON (U+003B). - if (auto font_style = descriptors.descriptor(DescriptorID::FontStyle)) { + if (auto font_style = descriptors.descriptor(DescriptorNameAndID::from_id(DescriptorID::FontStyle))) { builder.append(" font-style: "sv); font_style->serialize(builder, SerializationMode::Normal); builder.append(";"sv); diff --git a/Libraries/LibWeb/CSS/Descriptor.h b/Libraries/LibWeb/CSS/Descriptor.h index 223f1d0af1..cde44a4a14 100644 --- a/Libraries/LibWeb/CSS/Descriptor.h +++ b/Libraries/LibWeb/CSS/Descriptor.h @@ -7,7 +7,7 @@ #pragma once #include -#include +#include #include namespace Web::CSS { @@ -15,7 +15,7 @@ namespace Web::CSS { struct Descriptor { ~Descriptor(); - DescriptorID descriptor_id; + DescriptorNameAndID descriptor_name_and_id; NonnullRefPtr value; }; diff --git a/Libraries/LibWeb/CSS/DescriptorNameAndID.h b/Libraries/LibWeb/CSS/DescriptorNameAndID.h new file mode 100644 index 0000000000..5e33a8d7a4 --- /dev/null +++ b/Libraries/LibWeb/CSS/DescriptorNameAndID.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Callum Law + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::CSS { + +class DescriptorNameAndID { +public: + static Optional from_name(AtRuleID at_rule_id, FlyString name) + { + if (is_a_custom_property_name_string(name)) + return DescriptorNameAndID(move(name), DescriptorID::Custom); + + if (auto descriptor_id = descriptor_id_from_string(at_rule_id, name); descriptor_id.has_value()) { + // NB: We use the serialized ID as the name here instead of the input name to ensure that legacy alias + // mapping is reflected + return DescriptorNameAndID(CSS::to_string(descriptor_id.value()), descriptor_id.value()); + } + + return {}; + } + + static DescriptorNameAndID from_id(DescriptorID descriptor_id) + { + VERIFY(descriptor_id != DescriptorID::Custom); + return DescriptorNameAndID(CSS::to_string(descriptor_id), descriptor_id); + } + + DescriptorID id() const { return m_id; } + + FlyString const& name() const + { + return m_name; + } + + bool operator==(DescriptorNameAndID const& other) const + { + return m_id == other.m_id && m_name == other.m_name; + } + +private: + DescriptorNameAndID(FlyString name, DescriptorID id) + : m_name(move(name)) + , m_id(id) + { + } + + FlyString m_name; + DescriptorID m_id; +}; + +} + +namespace AK { + +template<> +struct Traits : public DefaultTraits { + static unsigned hash(Web::CSS::DescriptorNameAndID const& descriptor_name_and_id) + { + return pair_int_hash(to_underlying(descriptor_name_and_id.id()), descriptor_name_and_id.name().hash()); + } +}; + +} diff --git a/Libraries/LibWeb/CSS/FontFace.cpp b/Libraries/LibWeb/CSS/FontFace.cpp index 07ede8422b..564ffc13ee 100644 --- a/Libraries/LibWeb/CSS/FontFace.cpp +++ b/Libraries/LibWeb/CSS/FontFace.cpp @@ -96,7 +96,7 @@ GC::Ref FontFace::construct_impl(JS::Realm& realm, String family, Font Parser::ParsingParams parsing_params { realm }; auto try_parse_descriptor = [&parsing_params, &font_face, &realm](DescriptorID descriptor_id, String const& string) -> String { - auto result = parse_css_descriptor(parsing_params, AtRuleID::FontFace, descriptor_id, string); + auto result = parse_css_descriptor(parsing_params, AtRuleID::FontFace, DescriptorNameAndID::from_id(descriptor_id), string); if (!result) { font_face->reject_status_promise(WebIDL::SyntaxError::create(realm, Utf16String::formatted("FontFace constructor: Invalid {}", to_string(descriptor_id)))); return {}; @@ -120,7 +120,7 @@ GC::Ref FontFace::construct_impl(JS::Realm& realm, String family, Font font_face->m_line_gap_override = try_parse_descriptor(DescriptorID::LineGapOverride, descriptors.line_gap_override); RefPtr parsed_source; if (auto* source_string = source.get_pointer()) { - parsed_source = parse_css_descriptor(parsing_params, AtRuleID::FontFace, DescriptorID::Src, *source_string); + parsed_source = parse_css_descriptor(parsing_params, AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::Src), *source_string); if (!parsed_source) { font_face->reject_status_promise(WebIDL::SyntaxError::create(realm, Utf16String::formatted("FontFace constructor: Invalid {}", to_string(DescriptorID::Src)))); } @@ -238,7 +238,7 @@ GC::Ref FontFace::create_css_connected(JS::Realm& realm, CSSFontFaceRu font_face->m_css_font_face_rule = &rule; font_face->reparse_connected_css_font_face_rule_descriptors(); - if (auto src_value = rule.descriptors()->descriptor(DescriptorID::Src)) { + if (auto src_value = rule.descriptors()->descriptor(DescriptorNameAndID::from_id(DescriptorID::Src))) { font_face->m_urls = ParsedFontFace::sources_from_style_value(*src_value); font_face->m_urls.remove_all_matching(is_unsupported_source); } @@ -252,17 +252,17 @@ void FontFace::reparse_connected_css_font_face_rule_descriptors() { auto const& descriptors = m_css_font_face_rule->descriptors(); - set_family_impl(*descriptors->descriptor(DescriptorID::FontFamily)); - set_style_impl(*descriptors->descriptor_or_initial_value(DescriptorID::FontStyle)); - set_weight_impl(*descriptors->descriptor_or_initial_value(DescriptorID::FontWeight)); - set_stretch_impl(*descriptors->descriptor_or_initial_value(DescriptorID::FontWidth)); - set_unicode_range_impl(*descriptors->descriptor_or_initial_value(DescriptorID::UnicodeRange)); - set_feature_settings_impl(*descriptors->descriptor_or_initial_value(DescriptorID::FontFeatureSettings)); - set_variation_settings_impl(*descriptors->descriptor_or_initial_value(DescriptorID::FontVariationSettings)); - set_display_impl(*descriptors->descriptor_or_initial_value(DescriptorID::FontDisplay)); - set_ascent_override_impl(*descriptors->descriptor_or_initial_value(DescriptorID::AscentOverride)); - set_descent_override_impl(*descriptors->descriptor_or_initial_value(DescriptorID::DescentOverride)); - set_line_gap_override_impl(*descriptors->descriptor_or_initial_value(DescriptorID::LineGapOverride)); + set_family_impl(*descriptors->descriptor(DescriptorNameAndID::from_id(DescriptorID::FontFamily))); + set_style_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontStyle))); + set_weight_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontWeight))); + set_stretch_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontWidth))); + set_unicode_range_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::UnicodeRange))); + set_feature_settings_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontFeatureSettings))); + set_variation_settings_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontVariationSettings))); + set_display_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontDisplay))); + set_ascent_override_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::AscentOverride))); + set_descent_override_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::DescentOverride))); + set_line_gap_override_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::LineGapOverride))); } ParsedFontFace FontFace::parsed_font_face() const @@ -342,7 +342,7 @@ WebIDL::ExceptionOr FontFace::set_family(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::FontFamily, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::FontFamily), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.family setter: Invalid descriptor value"_utf16); @@ -366,7 +366,7 @@ WebIDL::ExceptionOr FontFace::set_style(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::FontStyle, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::FontStyle), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.style setter: Invalid descriptor value"_utf16); @@ -390,7 +390,7 @@ WebIDL::ExceptionOr FontFace::set_weight(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::FontWeight, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::FontWeight), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.weight setter: Invalid descriptor value"_utf16); @@ -415,7 +415,7 @@ WebIDL::ExceptionOr FontFace::set_stretch(String const& string) // parsed value. // NOTE: font-stretch is now an alias for font-width - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::FontWidth, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::FontWidth), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.stretch setter: Invalid descriptor value"_utf16); @@ -439,7 +439,7 @@ WebIDL::ExceptionOr FontFace::set_unicode_range(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::UnicodeRange, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::UnicodeRange), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.unicodeRange setter: Invalid descriptor value"_utf16); @@ -463,7 +463,7 @@ WebIDL::ExceptionOr FontFace::set_feature_settings(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::FontFeatureSettings, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::FontFeatureSettings), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.featureSettings setter: Invalid descriptor value"_utf16); @@ -487,7 +487,7 @@ WebIDL::ExceptionOr FontFace::set_variation_settings(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::FontVariationSettings, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::FontVariationSettings), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.variationSettings setter: Invalid descriptor value"_utf16); @@ -511,7 +511,7 @@ WebIDL::ExceptionOr FontFace::set_display(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::FontDisplay, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::FontDisplay), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.display setter: Invalid descriptor value"_utf16); @@ -535,7 +535,7 @@ WebIDL::ExceptionOr FontFace::set_ascent_override(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::AscentOverride, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::AscentOverride), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.ascentOverride setter: Invalid descriptor value"_utf16); @@ -559,7 +559,7 @@ WebIDL::ExceptionOr FontFace::set_descent_override(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::DescentOverride, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::DescentOverride), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.descentOverride setter: Invalid descriptor value"_utf16); @@ -583,7 +583,7 @@ WebIDL::ExceptionOr FontFace::set_line_gap_override(String const& string) // If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute to the serialization of the // parsed value. - auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorID::LineGapOverride, string); + auto property = parse_css_descriptor(Parser::ParsingParams(), AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::LineGapOverride), string); if (!property) return WebIDL::SyntaxError::create(realm(), "FontFace.lineGapOverride setter: Invalid descriptor value"_utf16); diff --git a/Libraries/LibWeb/CSS/ParsedFontFace.cpp b/Libraries/LibWeb/CSS/ParsedFontFace.cpp index fee04e0b5c..9db4f09df4 100644 --- a/Libraries/LibWeb/CSS/ParsedFontFace.cpp +++ b/Libraries/LibWeb/CSS/ParsedFontFace.cpp @@ -65,7 +65,7 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de }; FlyString font_family; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontFamily)) + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontFamily))) font_family = string_from_style_value(*value); ComputationContext computation_context { @@ -73,7 +73,7 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de }; Optional weight; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontWeight)) { + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontWeight))) { // https://drafts.csswg.org/css-fonts-4/#font-prop-desc // The auto values for these three descriptors have the following effects: // - For font selection purposes, the font is selected as if the appropriate normal value (normal, normal or normal) is chosen @@ -102,7 +102,7 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de } Optional slope; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontStyle)) { + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontStyle))) { // https://drafts.csswg.org/css-fonts-4/#font-prop-desc // The auto values for these three descriptors have the following effects: // - For font selection purposes, the font is selected as if the appropriate normal value (normal, normal or normal) is chosen @@ -115,7 +115,7 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de } Optional width; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontWidth)) { + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontWidth))) { // https://drafts.csswg.org/css-fonts-4/#font-prop-desc // The auto values for these three descriptors have the following effects: // - For font selection purposes, the font is selected as if the appropriate normal value (normal, normal or normal) is chosen @@ -128,45 +128,45 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de } Vector sources; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::Src)) + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::Src))) sources = sources_from_style_value(*value); Vector unicode_ranges; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::UnicodeRange)) { + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::UnicodeRange))) { for (auto const& range : value->as_value_list().values()) unicode_ranges.append(range->as_unicode_range().unicode_range()); } Optional ascent_override; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::AscentOverride)) + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::AscentOverride))) ascent_override = extract_percentage_or_normal(*value); Optional descent_override; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::DescentOverride)) + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::DescentOverride))) descent_override = extract_percentage_or_normal(*value); Optional line_gap_override; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::LineGapOverride)) + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::LineGapOverride))) line_gap_override = extract_percentage_or_normal(*value); FontDisplay font_display; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontDisplay)) + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontDisplay))) font_display = keyword_to_font_display(value->to_keyword()).value_or(FontDisplay::Auto); Optional font_named_instance; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontNamedInstance)) { + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontNamedInstance))) { if (value->is_string()) font_named_instance = value->as_string().string_value(); } Optional font_language_override; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontLanguageOverride)) { + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontLanguageOverride))) { if (value->is_string()) font_language_override = value->as_string().string_value(); } Optional> font_feature_settings; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontFeatureSettings)) { + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontFeatureSettings))) { if (value->to_keyword() == Keyword::Normal) { font_feature_settings.clear(); } else if (value->is_value_list()) { @@ -184,7 +184,7 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de } Optional> font_variation_settings; - if (auto value = descriptors.descriptor_or_initial_value(DescriptorID::FontVariationSettings)) { + if (auto value = descriptors.descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontVariationSettings))) { if (value->to_keyword() == Keyword::Normal) { font_variation_settings.clear(); } else if (value->is_value_list()) { diff --git a/Libraries/LibWeb/CSS/Parser/DescriptorParsing.cpp b/Libraries/LibWeb/CSS/Parser/DescriptorParsing.cpp index 845ddea6ac..c9a42b21de 100644 --- a/Libraries/LibWeb/CSS/Parser/DescriptorParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/DescriptorParsing.cpp @@ -22,17 +22,17 @@ namespace Web::CSS::Parser { -Parser::ParseErrorOr> Parser::parse_descriptor_value(AtRuleID at_rule_id, DescriptorID descriptor_id, TokenStream& unprocessed_tokens) +Parser::ParseErrorOr> Parser::parse_descriptor_value(AtRuleID at_rule_id, DescriptorNameAndID const& descriptor_name_and_id, TokenStream& unprocessed_tokens) { - if (!at_rule_supports_descriptor(at_rule_id, descriptor_id)) { + if (!at_rule_supports_descriptor(at_rule_id, descriptor_name_and_id.id())) { ErrorReporter::the().report(UnknownPropertyError { .rule_name = to_string(at_rule_id), - .property_name = to_string(descriptor_id), + .property_name = descriptor_name_and_id.name(), }); return ParseError::SyntaxError; } - auto context_guard = push_temporary_value_parsing_context(DescriptorContext { at_rule_id, descriptor_id }); + auto context_guard = push_temporary_value_parsing_context(DescriptorContext { at_rule_id, descriptor_name_and_id.id() }); Vector component_values; while (unprocessed_tokens.has_next_token()) { @@ -48,7 +48,7 @@ Parser::ParseErrorOr> Parser::parse_descriptor_v : Optional {}; TokenStream tokens { component_values }; - auto metadata = get_descriptor_metadata(at_rule_id, descriptor_id); + auto metadata = get_descriptor_metadata(at_rule_id, descriptor_name_and_id.id()); for (auto const& option : metadata.syntax) { auto transaction = tokens.begin_transaction(); auto parsed_style_value = option.visit( @@ -415,7 +415,7 @@ Parser::ParseErrorOr> Parser::parse_descriptor_v ErrorReporter::the().report(InvalidPropertyError { .rule_name = to_string(at_rule_id), - .property_name = to_string(descriptor_id), + .property_name = descriptor_name_and_id.name(), .value_string = tokens.dump_string(), .description = "Failed to parse."_string, }); @@ -425,16 +425,16 @@ Parser::ParseErrorOr> Parser::parse_descriptor_v Optional Parser::convert_to_descriptor(AtRuleID at_rule_id, Declaration const& declaration) { - auto descriptor_id = descriptor_id_from_string(at_rule_id, declaration.name); - if (!descriptor_id.has_value()) + auto descriptor_name_and_id = DescriptorNameAndID::from_name(at_rule_id, declaration.name); + if (!descriptor_name_and_id.has_value()) return {}; auto value_token_stream = TokenStream(declaration.value); - auto value = parse_descriptor_value(at_rule_id, descriptor_id.value(), value_token_stream); + auto value = parse_descriptor_value(at_rule_id, descriptor_name_and_id.value(), value_token_stream); if (value.is_error()) return {}; - return Descriptor { *descriptor_id, value.release_value() }; + return Descriptor { descriptor_name_and_id.value(), value.release_value() }; } } diff --git a/Libraries/LibWeb/CSS/Parser/Helpers.cpp b/Libraries/LibWeb/CSS/Parser/Helpers.cpp index 6647d7495b..de3b2099b8 100644 --- a/Libraries/LibWeb/CSS/Parser/Helpers.cpp +++ b/Libraries/LibWeb/CSS/Parser/Helpers.cpp @@ -89,11 +89,11 @@ RefPtr parse_css_type(CSS::Parser::ParsingParams const& c return CSS::Parser::Parser::create(context, string).parse_as_type(value_type); } -RefPtr parse_css_descriptor(CSS::Parser::ParsingParams const& parsing_params, CSS::AtRuleID at_rule_id, CSS::DescriptorID descriptor_id, StringView string) +RefPtr parse_css_descriptor(CSS::Parser::ParsingParams const& parsing_params, CSS::AtRuleID at_rule_id, CSS::DescriptorNameAndID const& descriptor_name_and_id, StringView string) { if (string.is_empty()) return nullptr; - return CSS::Parser::Parser::create(parsing_params, string).parse_as_descriptor_value(at_rule_id, descriptor_id); + return CSS::Parser::Parser::create(parsing_params, string).parse_as_descriptor_value(at_rule_id, descriptor_name_and_id); } CSS::CSSRule* parse_css_rule(CSS::Parser::ParsingParams const& context, StringView css_text) diff --git a/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Libraries/LibWeb/CSS/Parser/Parser.cpp index d4a5befc46..8ff77ada2d 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -1807,11 +1807,11 @@ RefPtr Parser::parse_as_css_value(PropertyID property_id) return parsed_value.release_value(); } -RefPtr Parser::parse_as_descriptor_value(AtRuleID at_rule_id, DescriptorID descriptor_id) +RefPtr Parser::parse_as_descriptor_value(AtRuleID at_rule_id, DescriptorNameAndID const& descriptor_name_and_id) { auto component_values = parse_a_list_of_component_values(m_token_stream); auto tokens = TokenStream(component_values); - auto parsed_value = parse_descriptor_value(at_rule_id, descriptor_id, tokens); + auto parsed_value = parse_descriptor_value(at_rule_id, descriptor_name_and_id, tokens); if (parsed_value.is_error()) return nullptr; return parsed_value.release_value(); diff --git a/Libraries/LibWeb/CSS/Parser/Parser.h b/Libraries/LibWeb/CSS/Parser/Parser.h index 5bba99750c..f7cb8dffd4 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Libraries/LibWeb/CSS/Parser/Parser.h @@ -160,7 +160,7 @@ public: RefPtr parse_as_supports(); RefPtr parse_as_css_value(PropertyID); - RefPtr parse_as_descriptor_value(AtRuleID, DescriptorID); + RefPtr parse_as_descriptor_value(AtRuleID, DescriptorNameAndID const&); RefPtr parse_as_type(ValueType); Optional parse_as_component_value(); @@ -382,7 +382,7 @@ private: RefPtr parse_radial_gradient_function(TokenStream&); ParseErrorOr> parse_css_value(PropertyID, TokenStream&, Optional original_source_text = {}); - ParseErrorOr> parse_descriptor_value(AtRuleID, DescriptorID, TokenStream&); + ParseErrorOr> parse_descriptor_value(AtRuleID, DescriptorNameAndID const&, TokenStream&); RefPtr parse_positional_value_list_shorthand(PropertyID, TokenStream&); RefPtr parse_css_value_for_property(PropertyID, TokenStream&); struct PropertyAndValue { @@ -645,7 +645,7 @@ CSS::Parser::Parser::PropertiesAndCustomProperties parse_css_property_declaratio Vector parse_css_descriptor_declaration_block(CSS::Parser::ParsingParams const&, CSS::AtRuleID, StringView); RefPtr parse_css_value(CSS::Parser::ParsingParams const&, StringView, CSS::PropertyID); RefPtr parse_css_type(CSS::Parser::ParsingParams const&, StringView, CSS::ValueType); -RefPtr parse_css_descriptor(CSS::Parser::ParsingParams const&, CSS::AtRuleID, CSS::DescriptorID, StringView); +RefPtr parse_css_descriptor(CSS::Parser::ParsingParams const&, CSS::AtRuleID, CSS::DescriptorNameAndID const&, StringView); Optional parse_selector(CSS::Parser::ParsingParams const&, StringView); Optional parse_selector_for_nested_style_rule(CSS::Parser::ParsingParams const&, StringView); Optional parse_page_selector_list(CSS::Parser::ParsingParams const&, StringView); diff --git a/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp b/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp index 5f88ac38a4..6836e3fd64 100644 --- a/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp @@ -56,8 +56,8 @@ public: void append(Descriptor&& descriptor) { - if (is_shorthand(m_at_rule, descriptor.descriptor_id)) { - for_each_expanded_longhand(m_at_rule, descriptor.descriptor_id, descriptor.value, [this](auto longhand_id, auto longhand_value) { + if (is_shorthand(m_at_rule, descriptor.descriptor_name_and_id)) { + for_each_expanded_longhand(m_at_rule, descriptor.descriptor_name_and_id, descriptor.value, [this](auto longhand_id, auto longhand_value) { append_internal(Descriptor { longhand_id, longhand_value.release_nonnull() }); }); return; @@ -74,19 +74,19 @@ public: private: void append_internal(Descriptor&& descriptor) { - if (m_seen_descriptor_ids.contains(descriptor.descriptor_id)) { + if (m_seen_descriptor_ids.contains(descriptor.descriptor_name_and_id)) { m_descriptors.remove_first_matching([&descriptor](Descriptor const& existing) { - return existing.descriptor_id == descriptor.descriptor_id; + return existing.descriptor_name_and_id == descriptor.descriptor_name_and_id; }); } else { - m_seen_descriptor_ids.set(descriptor.descriptor_id); + m_seen_descriptor_ids.set(descriptor.descriptor_name_and_id); } m_descriptors.append(move(descriptor)); } AtRuleID m_at_rule; Vector m_descriptors; - HashTable m_seen_descriptor_ids; + HashTable m_seen_descriptor_ids; }; GC::Ptr Parser::convert_to_rule(Rule const& rule, Nested nested) @@ -756,12 +756,12 @@ GC::Ptr Parser::convert_to_property_rule(AtRule const& rule) rule.for_each_as_declaration_list([&](auto& declaration) { if (auto descriptor = convert_to_descriptor(AtRuleID::Property, declaration); descriptor.has_value()) { - if (descriptor->descriptor_id == DescriptorID::Syntax) { + if (descriptor->descriptor_name_and_id.id() == DescriptorID::Syntax) { if (descriptor->value->is_string()) syntax_maybe = descriptor->value->as_string().string_value(); return; } - if (descriptor->descriptor_id == DescriptorID::Inherits) { + if (descriptor->descriptor_name_and_id.id() == DescriptorID::Inherits) { switch (descriptor->value->to_keyword()) { case Keyword::True: inherits_maybe = true; @@ -774,7 +774,7 @@ GC::Ptr Parser::convert_to_property_rule(AtRule const& rule) } return; } - if (descriptor->descriptor_id == DescriptorID::InitialValue) { + if (descriptor->descriptor_name_and_id.id() == DescriptorID::InitialValue) { initial_value_maybe = *descriptor->value; return; } @@ -893,7 +893,7 @@ GC::Ptr Parser::convert_to_counter_style_rule(AtRule const& if (!descriptor.has_value()) return; - switch (descriptor->descriptor_id) { + switch (descriptor->descriptor_name_and_id.id()) { case DescriptorID::System: system = descriptor->value; break; diff --git a/Libraries/LibWeb/Dump.cpp b/Libraries/LibWeb/Dump.cpp index 98168b7eb9..ce9d99e594 100644 --- a/Libraries/LibWeb/Dump.cpp +++ b/Libraries/LibWeb/Dump.cpp @@ -685,7 +685,7 @@ void dump_descriptors(StringBuilder& builder, CSS::CSSDescriptors const& descrip builder.appendff("Declarations ({}):\n", descriptors.length()); for (auto const& descriptor : descriptors.descriptors()) { dump_indent(builder, indent_levels); - builder.appendff(" {}: '{}'", CSS::to_string(descriptor.descriptor_id), descriptor.value->to_string(CSS::SerializationMode::Normal)); + builder.appendff(" {}: '{}'", descriptor.descriptor_name_and_id.name(), descriptor.value->to_string(CSS::SerializationMode::Normal)); builder.append('\n'); } } diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSDescriptors.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSDescriptors.cpp index f5c9737b02..b18bb3e656 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSDescriptors.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSDescriptors.cpp @@ -106,6 +106,7 @@ enum class DescriptorID : @descriptor_id_underlying_type@ { member_generator.appendln(" @name:titlecase@,"); } generator.append(R"~~~( + Custom, }; Optional descriptor_id_from_string(AtRuleID, StringView); @@ -197,6 +198,13 @@ Optional descriptor_id_from_string(AtRuleID at_rule_id, StringView case AtRuleID::@at_rule:titlecase@: )~~~"); + if (at_rule.has_object("custom-descriptors"sv)) { + at_rule_generator.append(R"~~~( + if (is_a_custom_property_name_string(string)) + return DescriptorID::Custom; +)~~~"); + } + auto const& descriptors = at_rule.get_object("descriptors"sv).value(); descriptors.for_each_member([&](auto const& descriptor_name, JsonValue const& descriptor_value) { @@ -242,6 +250,8 @@ FlyString to_string(DescriptorID descriptor_id) } generator.append(R"~~~( + case DescriptorID::Custom: + VERIFY_NOT_REACHED(); } VERIFY_NOT_REACHED(); } @@ -271,6 +281,12 @@ bool at_rule_supports_descriptor(AtRuleID at_rule_id, DescriptorID descriptor_id descriptor_generator.appendln(" case DescriptorID::@descriptor:titlecase@:"); }); + if (at_rule.has_object("custom-descriptors"sv)) { + at_rule_generator.append(R"~~~( + case DescriptorID::Custom: +)~~~"); + } + at_rule_generator.append(R"~~~( return true; default: @@ -323,7 +339,7 @@ RefPtr descriptor_initial_value(AtRuleID at_rule_id, Descripto descriptor_generator.set("initial_value_string", initial_value.value()); descriptor_generator.append(R"~~~( case DescriptorID::@descriptor:titlecase@: { - auto parsed_value = parse_css_descriptor(parsing_params, AtRuleID::@at_rule:titlecase@, DescriptorID::@descriptor:titlecase@, "@initial_value_string@"sv); + auto parsed_value = parse_css_descriptor(parsing_params, AtRuleID::@at_rule:titlecase@, DescriptorNameAndID::from_id(DescriptorID::@descriptor:titlecase@), "@initial_value_string@"sv); VERIFY(!parsed_value.is_null()); auto initial_value = parsed_value.release_nonnull(); initial_values[to_underlying(at_rule_id)][to_underlying(descriptor_id)] = initial_value; @@ -365,19 +381,7 @@ DescriptorMetadata get_descriptor_metadata(AtRuleID at_rule_id, DescriptorID des switch (descriptor_id) { )~~~"); - auto const& descriptors = at_rule.get_object("descriptors"sv).value(); - descriptors.for_each_member([&](auto const& descriptor_name, JsonValue const& descriptor_value) { - auto const& descriptor = descriptor_value.as_object(); - if (is_legacy_alias(descriptor)) - return; - - auto descriptor_generator = at_rule_generator.fork(); - descriptor_generator.set("descriptor:titlecase", title_casify(descriptor_name)); - descriptor_generator.append(R"~~~( - case DescriptorID::@descriptor:titlecase@: { - DescriptorMetadata metadata; -)~~~"); - auto const& syntax = descriptor.get_array("syntax"sv).value(); + auto const generate_syntax_list = [&](SourceGenerator& descriptor_generator, JsonArray const& syntax) { for (auto const& entry : syntax.values()) { auto option_generator = descriptor_generator.fork(); auto const& syntax_string = entry.as_string(); @@ -446,12 +450,46 @@ DescriptorMetadata get_descriptor_metadata(AtRuleID at_rule_id, DescriptorID des )~~~"); } } + }; + + auto const& descriptors = at_rule.get_object("descriptors"sv).value(); + descriptors.for_each_member([&](auto const& descriptor_name, JsonValue const& descriptor_value) { + auto const& descriptor = descriptor_value.as_object(); + if (is_legacy_alias(descriptor)) + return; + + auto descriptor_generator = at_rule_generator.fork(); + descriptor_generator.set("descriptor:titlecase", title_casify(descriptor_name)); + descriptor_generator.append(R"~~~( + case DescriptorID::@descriptor:titlecase@: { + DescriptorMetadata metadata; +)~~~"); + auto const& syntax = descriptor.get_array("syntax"sv).value(); + + generate_syntax_list(descriptor_generator, syntax); + descriptor_generator.append(R"~~~( return metadata; } )~~~"); }); + if (at_rule.has_object("custom-descriptors"sv)) { + auto const& custom_descriptors = at_rule.get_object("custom-descriptors"sv).value(); + + auto custom_descriptor_generator = at_rule_generator.fork(); + custom_descriptor_generator.append(R"~~~( + case DescriptorID::Custom: { + DescriptorMetadata metadata; +)~~~"); + auto const& syntax = custom_descriptors.get_array("syntax"sv).value(); + generate_syntax_list(custom_descriptor_generator, syntax); + custom_descriptor_generator.append(R"~~~( + return metadata; + } +)~~~"); + } + at_rule_generator.append(R"~~~( default: VERIFY_NOT_REACHED();