ladybird/Libraries/LibWeb/CSS/CSSMathClamp.cpp
Andreas Kling 26b0801a78 LibWeb: Return Utf16String from CSS Typed OM serialization
Make CSS Typed OM to_string() implementations produce Utf16String
instead of building UTF-8 strings and converting at the JS binding
boundary. Use Utf16StringBuilder for numeric, math, and transform
serialization paths that already feed CSS Typed OM stringification.

Keep the older CSSOM StyleValue serializer on StringBuilder for now, and
convert only where Typed OM source values or parser re-entry still need
the existing UTF-8 representation.
2026-06-22 16:10:40 +02:00

156 lines
6.1 KiB
C++

/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSMathClamp.h"
#include <LibWeb/Bindings/CSSMathClamp.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSMathNegate.h>
#include <LibWeb/CSS/CSSNumericArray.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSMathClamp);
GC::Ref<CSSMathClamp> CSSMathClamp::create(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericValue> lower, GC::Ref<CSSNumericValue> value, GC::Ref<CSSNumericValue> upper)
{
return realm.create<CSSMathClamp>(realm, move(type), move(lower), move(value), move(upper));
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-cssmathclamp
WebIDL::ExceptionOr<GC::Ref<CSSMathClamp>> CSSMathClamp::construct_impl(JS::Realm& realm, CSSNumberish lower, CSSNumberish value, CSSNumberish upper)
{
// The CSSMathClamp(lower, value, upper) constructor must, when called, perform the following steps:
// 1. Replace lower, value, and upper with the result of rectifying a numberish value for each.
auto lower_rectified = rectify_a_numberish_value(realm, lower);
auto value_rectified = rectify_a_numberish_value(realm, value);
auto upper_rectified = rectify_a_numberish_value(realm, upper);
// 2. Let type be the result of adding the types of lower, value, and upper. If type is failure, throw a TypeError.
auto type = lower_rectified->type()
.added_to(value_rectified->type())
.map([&](auto&& type) { return type.added_to(upper_rectified->type()); });
if (!type.has_value()) {
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot create a CSSMathClamp with values of incompatible types"sv };
}
// 3. Return a new CSSMathClamp whose lower, value, and upper internal slots are set to lower, value, and upper, respectively.
return CSSMathClamp::create(realm, type->release_value(), move(lower_rectified), move(value_rectified), move(upper_rectified));
}
CSSMathClamp::CSSMathClamp(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericValue> lower, GC::Ref<CSSNumericValue> value, GC::Ref<CSSNumericValue> upper)
: CSSMathValue(realm, Bindings::CSSMathOperator::Clamp, move(type))
, m_lower(move(lower))
, m_value(move(value))
, m_upper(move(upper))
{
}
CSSMathClamp::~CSSMathClamp() = default;
void CSSMathClamp::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSMathClamp);
Base::initialize(realm);
}
void CSSMathClamp::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_lower);
visitor.visit(m_value);
visitor.visit(m_upper);
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathClamp::serialize_math_value(Utf16StringBuilder& s, Nested, Parens) const
{
// AD-HOC: The spec is missing serialization rules for CSSMathClamp: https://github.com/w3c/css-houdini-drafts/issues/1152
s.append_ascii("clamp("sv);
m_lower->serialize(s, { .nested = true, .parenless = true });
s.append_ascii(", "sv);
m_value->serialize(s, { .nested = true, .parenless = true });
s.append_ascii(", "sv);
m_upper->serialize(s, { .nested = true, .parenless = true });
s.append_ascii(')');
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-lower
GC::Ref<CSSNumericValue> CSSMathClamp::lower() const
{
// AD-HOC: No spec definition.
return m_lower;
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-value
GC::Ref<CSSNumericValue> CSSMathClamp::value() const
{
// AD-HOC: No spec definition.
return m_value;
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-upper
GC::Ref<CSSNumericValue> CSSMathClamp::upper() const
{
// AD-HOC: No spec definition.
return m_upper;
}
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathClamp::is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const
{
// AD-HOC: Spec doesn't handle clamp(). https://github.com/w3c/css-houdini-drafts/issues/1152
// 1. If value1 and value2 are not members of the same interface, return false.
auto* other_clamp = as_if<CSSMathClamp>(*other);
if (!other_clamp)
return false;
return m_lower->is_equal_numeric_value(other_clamp->m_lower)
&& m_value->is_equal_numeric_value(other_clamp->m_value)
&& m_upper->is_equal_numeric_value(other_clamp->m_upper);
}
// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value
Optional<SumValue> CSSMathClamp::create_a_sum_value() const
{
// AD-HOC: There is no spec for this. https://github.com/w3c/css-houdini-drafts/issues/1152
// So, basing it on the spec for CSSMathMin.
// Create sum values from lower, value, and upper.
auto lower = m_lower->create_a_sum_value();
auto value = m_value->create_a_sum_value();
auto upper = m_upper->create_a_sum_value();
// If any of those are failure, or has a length greater than one, return failure.
if (!lower.has_value() || lower->size() > 1
|| !value.has_value() || value->size() > 1
|| !upper.has_value() || upper->size() > 1)
return {};
// If not all their unit maps are identical, return failure.
if (lower->first().unit_map != value->first().unit_map || value->first().unit_map != upper->first().unit_map)
return {};
// Return value clamped between lower and upper.
return SumValue {
SumValueItem {
clamp(value->first().value, lower->first().value, upper->first().value),
value->first().unit_map,
}
};
}
WebIDL::ExceptionOr<NonnullRefPtr<CalculationNode const>> CSSMathClamp::create_calculation_node(CalculationContext const& context) const
{
auto lower = TRY(m_lower->create_calculation_node(context));
auto value = TRY(m_value->create_calculation_node(context));
auto upper = TRY(m_upper->create_calculation_node(context));
return ClampCalculationNode::create(move(lower), move(value), move(upper));
}
}