ladybird/Libraries/LibWeb/CSS/CSSFontFeatureValuesMap.cpp

107 lines
4 KiB
C++
Raw Permalink Normal View History

/*
* Copyright (c) 2026, Callum Law <callumlaw1709@outlook.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSFontFeatureValuesMap.h"
#include <LibJS/Runtime/Array.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSFontFeatureValuesRule.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSFontFeatureValuesMap);
GC::Ref<CSSFontFeatureValuesMap> CSSFontFeatureValuesMap::create(JS::Realm& realm, size_t max_value_count, GC::Ref<CSSFontFeatureValuesRule> parent_rule)
{
return realm.create<CSSFontFeatureValuesMap>(realm, max_value_count, parent_rule);
}
CSSFontFeatureValuesMap::CSSFontFeatureValuesMap(JS::Realm& realm, size_t max_value_count, GC::Ref<CSSFontFeatureValuesRule> parent_rule)
: Bindings::PlatformObject(realm)
, m_map_entries(JS::Map::create(realm))
, m_max_value_count(max_value_count)
, m_parent_rule(parent_rule)
{
}
WebIDL::ExceptionOr<void> CSSFontFeatureValuesMap::set(String const& feature_value_name, Variant<u32, Vector<u32>> const& values)
{
// https://drafts.csswg.org/css-fonts-4/#cssfontfeaturevaluesmap
// The CSSFontFeatureValuesMap interface uses the default map class methods but the set method has different
// behavior. It takes a sequence of unsigned integers and associates it with a given featureValueName. The method
// behaves the same as the default map class method except that
// a single unsigned long value is treated as a sequence of a single value.
Vector<u32> value_vector = values.visit(
[](u32 single_value) { return Vector<u32> { single_value }; },
[](Vector<u32> value_vector) { return value_vector; });
// The method throws an exception if an invalid number of values is passed in.
if (value_vector.is_empty())
return WebIDL::InvalidAccessError::create(realm(), "CSSFontFeatureValuesMap.set requires at least one value."_utf16);
// If the associated feature value block only allows a limited number of values, the set method throws an
// InvalidAccessError exception when the input sequence to set contains more than the limited number of values. See
// the description of multi-valued feature value definitions for details on the maximum number of values allowed for
// a given type of feature value block.
if (value_vector.size() > m_max_value_count)
return WebIDL::InvalidAccessError::create(realm(), Utf16String::formatted("CSSFontFeatureValuesMap.set only allows a maximum of {} values for the associated feature", m_max_value_count));
Vector<JS::Value> wrapped_values;
wrapped_values.ensure_capacity(value_vector.size());
for (auto const& value : value_vector)
wrapped_values.append(JS::Value { value });
m_map_entries->map_set(JS::PrimitiveString::create(vm(), feature_value_name), JS::Array::create_from(realm(), wrapped_values.span()));
m_parent_rule->clear_caches();
return {};
}
void CSSFontFeatureValuesMap::on_map_modified_from_js(Badge<Bindings::CSSFontFeatureValuesMapPrototype>)
{
m_parent_rule->clear_caches();
}
OrderedHashMap<FlyString, Vector<u32>> CSSFontFeatureValuesMap::to_ordered_hash_map() const
{
OrderedHashMap<FlyString, Vector<u32>> result;
for (auto const& entry : *m_map_entries) {
auto key = MUST(entry.key.to_string(vm()));
auto const& array = as<JS::Array>(entry.value.as_object());
auto array_length = MUST(MUST(array.get(vm().names.length)).to_length(vm()));
Vector<u32> values;
values.ensure_capacity(array_length);
for (size_t i = 0; i < array_length; ++i)
values.append(MUST(array.get_without_side_effects(JS::PropertyKey { i }).to_u32(vm())));
result.set(key, values);
}
return result;
}
void CSSFontFeatureValuesMap::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSFontFeatureValuesMap);
Base::initialize(realm);
}
void CSSFontFeatureValuesMap::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_map_entries);
visitor.visit(m_parent_rule);
}
}