/* * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace Web::CSS { GC_DEFINE_ALLOCATOR(CSSCounterStyleRule); GC::Ref CSSCounterStyleRule::create(JS::Realm& realm, FlyString name, RefPtr system, RefPtr negative) { return realm.create(realm, name, move(system), move(negative)); } CSSCounterStyleRule::CSSCounterStyleRule(JS::Realm& realm, FlyString name, RefPtr system, RefPtr negative) : CSSRule(realm, Type::CounterStyle) , m_name(move(name)) , m_system(move(system)) , m_negative(move(negative)) { } String CSSCounterStyleRule::serialized() const { StringBuilder builder; builder.appendff("@counter-style {} {{", serialize_an_identifier(m_name)); if (m_system) { builder.append(" system: "sv); m_system->serialize(builder, SerializationMode::Normal); builder.append(';'); } if (m_negative) { builder.append(" negative: "sv); m_negative->serialize(builder, SerializationMode::Normal); builder.append(';'); } builder.append(" }"sv); return MUST(builder.to_string()); } // https://drafts.csswg.org/css-counter-styles-3/#dom-csscounterstylerule-name void CSSCounterStyleRule::set_name(FlyString name) { // On setting the name attribute, run the following steps: // 1. If the value is an ASCII case-insensitive match for "none" or one of the non-overridable counter-style names, do nothing and return. if (name.equals_ignoring_ascii_case("none"sv) || matches_non_overridable_counter_style_name(name)) return; // 2. If the value is an ASCII case-insensitive match for any of the predefined counter styles, lowercase it. if (auto keyword = keyword_from_string(name); keyword.has_value() && keyword_to_counter_style_name_keyword(keyword.release_value()).has_value()) name = name.to_ascii_lowercase(); // 3. Replace the associated rule’s name with an identifier equal to the value. m_name = move(name); } FlyString CSSCounterStyleRule::system() const { if (!m_system) return ""_fly_string; return m_system->to_string(SerializationMode::Normal); } // https://drafts.csswg.org/css-counter-styles-3/#dom-csscounterstylerule-system 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); // 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 // symbols descriptor to contain two values.) // NB: Since we only allow changing parameters of the system, not the algorithm itself (see below), we know this // change can't cause the @counter-style to not define a counter style. if (!value) return; // 3. If the attribute being set is system, and the new value would change the algorithm used, do nothing and abort // these steps. // Note: It’s okay to change an aspect of the algorithm, like the first symbol value of a fixed system. if (!m_system || m_system->as_counter_style_system().algorithm_differs_from(value->as_counter_style_system())) return; // 4. Set the descriptor to the value. m_system = value; } FlyString CSSCounterStyleRule::negative() const { if (!m_negative) return ""_fly_string; return m_negative->to_string(SerializationMode::Normal); } 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)) m_negative = value; } void CSSCounterStyleRule::initialize(JS::Realm& realm) { WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSCounterStyleRule); Base::initialize(realm); } }