2026-01-13 21:44:21 +13:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2026, Callum Law <callumlaw1709@outlook.com>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "CSSFontFeatureValuesRule.h"
|
|
|
|
|
#include <AK/QuickSort.h>
|
|
|
|
|
#include <LibWeb/Bindings/CSSFontFeatureValuesRulePrototype.h>
|
|
|
|
|
#include <LibWeb/Bindings/Intrinsics.h>
|
2026-02-20 12:17:07 +13:00
|
|
|
#include <LibWeb/CSS/CSSStyleSheet.h>
|
|
|
|
|
#include <LibWeb/CSS/FontComputer.h>
|
2026-01-13 21:44:21 +13:00
|
|
|
#include <LibWeb/CSS/Serialize.h>
|
2026-02-20 12:17:07 +13:00
|
|
|
#include <LibWeb/DOM/Document.h>
|
2026-01-13 21:44:21 +13:00
|
|
|
#include <LibWeb/Infra/CharacterTypes.h>
|
|
|
|
|
|
|
|
|
|
namespace Web::CSS {
|
|
|
|
|
|
|
|
|
|
GC_DEFINE_ALLOCATOR(CSSFontFeatureValuesRule);
|
|
|
|
|
|
|
|
|
|
GC::Ref<CSSFontFeatureValuesRule> CSSFontFeatureValuesRule::create(JS::Realm& realm, Vector<FlyString> font_families)
|
|
|
|
|
{
|
|
|
|
|
return realm.create<CSSFontFeatureValuesRule>(realm, move(font_families));
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 17:06:59 +13:00
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 21:44:21 +13:00
|
|
|
CSSFontFeatureValuesRule::CSSFontFeatureValuesRule(JS::Realm& realm, Vector<FlyString> font_families)
|
|
|
|
|
: CSSRule(realm, CSSRule::Type::FontFeatureValues)
|
|
|
|
|
, m_font_families(move(font_families))
|
2026-02-20 12:17:07 +13:00
|
|
|
, m_annotation(realm.create<CSSFontFeatureValuesMap>(realm, 1, *this))
|
|
|
|
|
, m_ornaments(realm.create<CSSFontFeatureValuesMap>(realm, 1, *this))
|
|
|
|
|
, m_stylistic(realm.create<CSSFontFeatureValuesMap>(realm, 1, *this))
|
|
|
|
|
, m_swash(realm.create<CSSFontFeatureValuesMap>(realm, 1, *this))
|
|
|
|
|
, m_character_variant(realm.create<CSSFontFeatureValuesMap>(realm, 2, *this))
|
|
|
|
|
, m_styleset(realm.create<CSSFontFeatureValuesMap>(realm, AK::NumericLimits<size_t>::max(), *this))
|
|
|
|
|
, m_historical_forms(realm.create<CSSFontFeatureValuesMap>(realm, 1, *this))
|
2026-01-13 21:44:21 +13:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<FlyString> 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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 12:17:07 +13:00
|
|
|
void CSSFontFeatureValuesRule::clear_caches()
|
|
|
|
|
{
|
|
|
|
|
Base::clear_caches();
|
|
|
|
|
auto const* parent_style_sheet = this->parent_style_sheet();
|
|
|
|
|
|
|
|
|
|
if (!parent_style_sheet)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto document = parent_style_sheet->owning_document();
|
|
|
|
|
|
|
|
|
|
if (!document)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (auto const& family : m_font_families) {
|
|
|
|
|
document->font_computer().clear_computed_font_cache(family);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 21:44:21 +13:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|