mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-11-04 07:10:57 +00:00 
			
		
		
		
	
		
			
	
	
		
			159 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			159 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						|||
| 
								 | 
							
								 * Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * SPDX-License-Identifier: BSD-2-Clause
							 | 
						|||
| 
								 | 
							
								 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#include "CSSTransformValue.h"
							 | 
						|||
| 
								 | 
							
								#include <LibWeb/Bindings/CSSTransformValuePrototype.h>
							 | 
						|||
| 
								 | 
							
								#include <LibWeb/Bindings/Intrinsics.h>
							 | 
						|||
| 
								 | 
							
								#include <LibWeb/CSS/CSSTransformComponent.h>
							 | 
						|||
| 
								 | 
							
								#include <LibWeb/Geometry/DOMMatrix.h>
							 | 
						|||
| 
								 | 
							
								#include <LibWeb/WebIDL/ExceptionOr.h>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								namespace Web::CSS {
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								GC_DEFINE_ALLOCATOR(CSSTransformValue);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								GC::Ref<CSSTransformValue> CSSTransformValue::create(JS::Realm& realm, Vector<GC::Ref<CSSTransformComponent>> transforms)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    return realm.create<CSSTransformValue>(realm, move(transforms));
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformvalue-csstransformvalue
							 | 
						|||
| 
								 | 
							
								WebIDL::ExceptionOr<GC::Ref<CSSTransformValue>> CSSTransformValue::construct_impl(JS::Realm& realm, GC::RootVector<GC::Root<CSSTransformComponent>> transforms)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // The CSSTransformValue(transforms) constructor must, when called, perform the following steps:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    // 1. If transforms is empty, throw a TypeError.
							 | 
						|||
| 
								 | 
							
								    if (transforms.is_empty())
							 | 
						|||
| 
								 | 
							
								        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "CSSTransformValue's transforms list cannot be empty."sv };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    // 2. Return a new CSSTransformValue whose values to iterate over is transforms.
							 | 
						|||
| 
								 | 
							
								    Vector<GC::Ref<CSSTransformComponent>> converted_transforms;
							 | 
						|||
| 
								 | 
							
								    converted_transforms.ensure_capacity(transforms.size());
							 | 
						|||
| 
								 | 
							
								    for (auto const& transform : transforms)
							 | 
						|||
| 
								 | 
							
								        converted_transforms.append(*transform);
							 | 
						|||
| 
								 | 
							
								    return CSSTransformValue::create(realm, move(converted_transforms));
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								CSSTransformValue::CSSTransformValue(JS::Realm& realm, Vector<GC::Ref<CSSTransformComponent>> transforms)
							 | 
						|||
| 
								 | 
							
								    : CSSStyleValue(realm)
							 | 
						|||
| 
								 | 
							
								    , m_transforms(move(transforms))
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    m_legacy_platform_object_flags = LegacyPlatformObjectFlags {
							 | 
						|||
| 
								 | 
							
								        .supports_indexed_properties = true,
							 | 
						|||
| 
								 | 
							
								        .has_indexed_property_setter = true,
							 | 
						|||
| 
								 | 
							
								    };
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								CSSTransformValue::~CSSTransformValue() = default;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void CSSTransformValue::initialize(JS::Realm& realm)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSTransformValue);
							 | 
						|||
| 
								 | 
							
								    Base::initialize(realm);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void CSSTransformValue::visit_edges(Visitor& visitor)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    Base::visit_edges(visitor);
							 | 
						|||
| 
								 | 
							
								    visitor.visit(m_transforms);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformvalue-length
							 | 
						|||
| 
								 | 
							
								WebIDL::UnsignedLong CSSTransformValue::length() const
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // The length attribute indicates how many transform components are contained within the CSSTransformValue.
							 | 
						|||
| 
								 | 
							
								    return m_transforms.size();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// https://drafts.css-houdini.org/css-typed-om-1/#ref-for-dfn-determine-the-value-of-an-indexed-property%E2%91%A0
							 | 
						|||
| 
								 | 
							
								Optional<JS::Value> CSSTransformValue::item_value(size_t index) const
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // To determine the value of an indexed property of a CSSTransformValue this and an index n, let values be thisβs
							 | 
						|||
| 
								 | 
							
								    // [[values]] internal slot, and return values[n].
							 | 
						|||
| 
								 | 
							
								    if (index >= m_transforms.size())
							 | 
						|||
| 
								 | 
							
								        return {};
							 | 
						|||
| 
								 | 
							
								    return m_transforms[index];
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								static WebIDL::ExceptionOr<GC::Ref<CSSTransformComponent>> transform_component_from_js_value(JS::Value& value)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    if (value.is_object()) {
							 | 
						|||
| 
								 | 
							
								        if (auto* transform_component = as_if<CSSTransformComponent>(value.as_object()))
							 | 
						|||
| 
								 | 
							
								            return GC::Ref { *transform_component };
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Value must be a CSSTransformComponent"sv };
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// https://drafts.css-houdini.org/css-typed-om-1/#ref-for-dfn-set-the-value-of-an-existing-indexed-property%E2%91%A0
							 | 
						|||
| 
								 | 
							
								WebIDL::ExceptionOr<void> CSSTransformValue::set_value_of_existing_indexed_property(u32 n, JS::Value new_value)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // To set the value of an existing indexed property of a CSSTransformValue this, an index n, and a value new value,
							 | 
						|||
| 
								 | 
							
								    // let values be thisβs [[values]] internal slot, and set values[n] to new value.
							 | 
						|||
| 
								 | 
							
								    m_transforms[n] = TRY(transform_component_from_js_value(new_value));
							 | 
						|||
| 
								 | 
							
								    return {};
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// https://drafts.css-houdini.org/css-typed-om-1/#ref-for-dfn-set-the-value-of-a-new-indexed-propertyβ 
							 | 
						|||
| 
								 | 
							
								WebIDL::ExceptionOr<void> CSSTransformValue::set_value_of_new_indexed_property(u32 n, JS::Value new_value)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // To set the value of a new indexed property of a CSSTransformValue this, an index n, and a value new value, let
							 | 
						|||
| 
								 | 
							
								    // values be thisβs [[values]] internal slot. If n is not equal to the size of values, throw a RangeError.
							 | 
						|||
| 
								 | 
							
								    // Otherwise, append new value to values.
							 | 
						|||
| 
								 | 
							
								    if (n != m_transforms.size())
							 | 
						|||
| 
								 | 
							
								        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "Index out of range"sv };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    m_transforms.append(TRY(transform_component_from_js_value(new_value)));
							 | 
						|||
| 
								 | 
							
								    return {};
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformvalue-is2d
							 | 
						|||
| 
								 | 
							
								bool CSSTransformValue::is_2d() const
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // The is2D attribute of a CSSTransformValue this must, on getting, return true if, for each func in thisβs values
							 | 
						|||
| 
								 | 
							
								    // to iterate over, the funcβs is2D attribute would return true; otherwise, the attribute returns false.
							 | 
						|||
| 
								 | 
							
								    return all_of(m_transforms, [](auto& transform) { return transform->is_2d(); });
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformvalue-tomatrix
							 | 
						|||
| 
								 | 
							
								WebIDL::ExceptionOr<GC::Ref<Geometry::DOMMatrix>> CSSTransformValue::to_matrix() const
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // The toMatrix() method of a CSSTransformValue this must, when called, perform the following steps:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    // 1. Let matrix be a new DOMMatrix, initialized to the identity matrix, with its is2D internal slot set to true.
							 | 
						|||
| 
								 | 
							
								    auto matrix = Geometry::DOMMatrix::create(realm());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    // 2. For each func in thisβs values to iterate over:
							 | 
						|||
| 
								 | 
							
								    for (auto const& function : m_transforms) {
							 | 
						|||
| 
								 | 
							
								        // 1. Let funcMatrix be the DOMMatrix returned by calling toMatrix() on func.
							 | 
						|||
| 
								 | 
							
								        // AD-HOC: This can throw exceptions.
							 | 
						|||
| 
								 | 
							
								        auto function_matrix = TRY(function->to_matrix());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        // 2. Set matrix to the result of multiplying matrix and the matrix represented by funcMatrix.
							 | 
						|||
| 
								 | 
							
								        TRY(matrix->multiply_self(*function_matrix));
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    // 3. Return matrix.
							 | 
						|||
| 
								 | 
							
								    return matrix;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-csstransformvalue
							 | 
						|||
| 
								 | 
							
								WebIDL::ExceptionOr<String> CSSTransformValue::to_string() const
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // 1. Return the result of serializing each item in thisβs values to iterate over, then concatenating them
							 | 
						|||
| 
								 | 
							
								    //    separated by " ".
							 | 
						|||
| 
								 | 
							
								    StringBuilder builder;
							 | 
						|||
| 
								 | 
							
								    bool first = true;
							 | 
						|||
| 
								 | 
							
								    for (auto const& transform : m_transforms) {
							 | 
						|||
| 
								 | 
							
								        if (!first)
							 | 
						|||
| 
								 | 
							
								            builder.append(" "sv);
							 | 
						|||
| 
								 | 
							
								        first = false;
							 | 
						|||
| 
								 | 
							
								        builder.append(TRY(transform->to_string()));
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    return builder.to_string_without_validation();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								}
							 |