/* * Copyright (c) 2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #include "CSSMathClamp.h" #include #include #include #include #include #include #include namespace Web::CSS { GC_DEFINE_ALLOCATOR(CSSMathClamp); GC::Ref CSSMathClamp::create(JS::Realm& realm, NumericType type, GC::Ref lower, GC::Ref value, GC::Ref upper) { return realm.create(realm, move(type), move(lower), move(value), move(upper)); } // https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-cssmathclamp WebIDL::ExceptionOr> 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 lower, GC::Ref value, GC::Ref 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 String CSSMathClamp::serialize_math_value(Nested, Parens) const { // AD-HOC: The spec is missing serialization rules for CSSMathClamp: https://github.com/w3c/css-houdini-drafts/issues/1152 StringBuilder s; s.append("clamp("sv); s.append(m_lower->to_string({ .nested = true, .parenless = true })); s.append(", "sv); s.append(m_value->to_string({ .nested = true, .parenless = true })); s.append(", "sv); s.append(m_upper->to_string({ .nested = true, .parenless = true })); s.append(")"sv); return s.to_string_without_validation(); } // https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathclamp-lower GC::Ref 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 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 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 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(*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 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> 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)); } }