/* * Copyright (c) 2026, Callum Law * * SPDX-License-Identifier: BSD-2-Clause */ #include "CSSFontFeatureValuesRule.h" #include #include #include #include #include namespace Web::CSS { GC_DEFINE_ALLOCATOR(CSSFontFeatureValuesRule); GC::Ref CSSFontFeatureValuesRule::create(JS::Realm& realm, Vector font_families) { return realm.create(realm, move(font_families)); } bool CSSFontFeatureValuesRule::is_font_feature_value_type_at_keyword(FlyString const& keyword) { return first_is_one_of(keyword, "stylistic", "historical-forms", "styleset", "character-variant", "swash", "ornaments", "annotation"); } CSSFontFeatureValuesRule::CSSFontFeatureValuesRule(JS::Realm& realm, Vector font_families) : CSSRule(realm, CSSRule::Type::FontFeatureValues) , m_font_families(move(font_families)) , m_annotation(realm.create(realm, 1)) , m_ornaments(realm.create(realm, 1)) , m_stylistic(realm.create(realm, 1)) , m_swash(realm.create(realm, 1)) , m_character_variant(realm.create(realm, 2)) , m_styleset(realm.create(realm, AK::NumericLimits::max())) , m_historical_forms(realm.create(realm, 1)) { } FlyString CSSFontFeatureValuesRule::font_family() const { StringBuilder builder; for (auto const& family : m_font_families) { if (builder.length() > 0) builder.append(", "sv); if (family.code_points().contains_any_of(Infra::ASCII_WHITESPACE_CODE_POINTS)) serialize_a_string(builder, family); else serialize_an_identifier(builder, family); } return MUST(builder.to_string()); } void CSSFontFeatureValuesRule::set_font_family(FlyString const& value) { Vector family_names; for (auto const& family_name : value.bytes_as_string_view().split_view(',')) family_names.append(MUST(FlyString::from_utf8(family_name.trim_whitespace()))); m_font_families = move(family_names); } String CSSFontFeatureValuesRule::serialized() const { StringBuilder builder; auto serialize_font_feature_values_map = [&](CSSFontFeatureValuesMap const& map, StringView const& at_rule_name) { if (auto entries = map.to_ordered_hash_map(); !entries.is_empty()) { builder.appendff(" @{} {{"sv, at_rule_name); for (auto const& [key, value] : entries) { builder.append(' '); serialize_an_identifier(builder, key); builder.append(':'); for (size_t i = 0; i < value.size(); ++i) builder.appendff(" {}", value[i]); builder.append(";"sv); } builder.append(" }"sv); } }; builder.appendff("@font-feature-values {} {{"sv, font_family()); serialize_font_feature_values_map(m_annotation, "annotation"sv); serialize_font_feature_values_map(m_ornaments, "ornaments"sv); serialize_font_feature_values_map(m_stylistic, "stylistic"sv); serialize_font_feature_values_map(m_swash, "swash"sv); serialize_font_feature_values_map(m_character_variant, "character-variant"sv); serialize_font_feature_values_map(m_styleset, "styleset"sv); serialize_font_feature_values_map(m_historical_forms, "historical-forms"sv); builder.append(" }"sv); return builder.to_string_without_validation(); } void CSSFontFeatureValuesRule::initialize(JS::Realm& realm) { WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSFontFeatureValuesRule); Base::initialize(realm); } void CSSFontFeatureValuesRule::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(m_annotation); visitor.visit(m_ornaments); visitor.visit(m_stylistic); visitor.visit(m_swash); visitor.visit(m_character_variant); visitor.visit(m_styleset); visitor.visit(m_historical_forms); } }