mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb/CSS: Implement CSSTranslate
Equivalent to the translate() transform functions. +34 WPT subtests.
This commit is contained in:
parent
8e86bf2dd0
commit
c7d22d8cfd
Notes:
github-actions[bot]
2025-09-24 11:29:48 +00:00
Author: https://github.com/AtkinsSJ
Commit: c7d22d8cfd
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6227
12 changed files with 285 additions and 40 deletions
|
|
@ -144,6 +144,7 @@ set(SOURCES
|
|||
CSS/CSSSupportsRule.cpp
|
||||
CSS/CSSTransformComponent.cpp
|
||||
CSS/CSSTransition.cpp
|
||||
CSS/CSSTranslate.cpp
|
||||
CSS/CSSUnitValue.cpp
|
||||
CSS/CSSUnparsedValue.cpp
|
||||
CSS/CSSVariableReferenceValue.cpp
|
||||
|
|
|
|||
181
Libraries/LibWeb/CSS/CSSTranslate.cpp
Normal file
181
Libraries/LibWeb/CSS/CSSTranslate.cpp
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "CSSTranslate.h"
|
||||
#include <LibWeb/Bindings/CSSTranslatePrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/CSS/CSSNumericValue.h>
|
||||
#include <LibWeb/CSS/CSSUnitValue.h>
|
||||
#include <LibWeb/Geometry/DOMMatrix.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(CSSTranslate);
|
||||
|
||||
GC::Ref<CSSTranslate> CSSTranslate::create(JS::Realm& realm, Is2D is_2d, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z)
|
||||
{
|
||||
return realm.create<CSSTranslate>(realm, is_2d, x, y, z);
|
||||
}
|
||||
|
||||
// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstranslate-csstranslate
|
||||
WebIDL::ExceptionOr<GC::Ref<CSSTranslate>> CSSTranslate::construct_impl(JS::Realm& realm, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ptr<CSSNumericValue> z)
|
||||
{
|
||||
// The CSSTranslate(x, y, z) constructor must, when invoked, perform the following steps:
|
||||
|
||||
// 1. If x or y don’t match <length-percentage>, throw a TypeError.
|
||||
if (!x->type().matches_length_percentage({}))
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "CSSTranslate x component doesn't match <length-percentage>"sv };
|
||||
|
||||
if (!y->type().matches_length_percentage({}))
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "CSSTranslate y component doesn't match <length-percentage>"sv };
|
||||
|
||||
// 2. If z was passed, but doesn’t match <length>, throw a TypeError.
|
||||
if (z && !z->type().matches_length({}))
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "CSSTranslate z component doesn't match <length>"sv };
|
||||
|
||||
// 3. Let this be a new CSSTranslate object, with its x and y internal slots set to x and y.
|
||||
// 4. If z was passed, set this’s z internal slot to z, and set this’s is2D internal slot to false.
|
||||
// 5. If z was not passed, set this’s z internal slot to a new unit value of (0, "px"), and set this’s is2D internal slot to true.
|
||||
Is2D is_2d = Is2D::No;
|
||||
if (!z) {
|
||||
is_2d = Is2D::Yes;
|
||||
z = CSSUnitValue::create(realm, 0, "px"_fly_string);
|
||||
}
|
||||
auto this_ = realm.create<CSSTranslate>(realm, is_2d, x, y, z.as_nonnull());
|
||||
|
||||
// 6. Return this.
|
||||
return this_;
|
||||
}
|
||||
|
||||
CSSTranslate::CSSTranslate(JS::Realm& realm, Is2D is_2d, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z)
|
||||
: CSSTransformComponent(realm, is_2d)
|
||||
, m_x(x)
|
||||
, m_y(y)
|
||||
, m_z(z)
|
||||
{
|
||||
}
|
||||
|
||||
CSSTranslate::~CSSTranslate() = default;
|
||||
|
||||
void CSSTranslate::initialize(JS::Realm& realm)
|
||||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSTranslate);
|
||||
Base::initialize(realm);
|
||||
}
|
||||
|
||||
void CSSTranslate::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_x);
|
||||
visitor.visit(m_y);
|
||||
visitor.visit(m_z);
|
||||
}
|
||||
|
||||
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-csstranslate
|
||||
WebIDL::ExceptionOr<Utf16String> CSSTranslate::to_string() const
|
||||
{
|
||||
// 1. Let s initially be the empty string.
|
||||
StringBuilder builder { StringBuilder::Mode::UTF16 };
|
||||
|
||||
// 2. If this’s is2D internal slot is false:
|
||||
if (!is_2d()) {
|
||||
// 1. Append "translate3d(" to s.
|
||||
builder.append("translate3d("sv);
|
||||
|
||||
// 2. Serialize this’s x internal slot, and append it to s.
|
||||
builder.append(m_x->to_string());
|
||||
|
||||
// 3. Append ", " to s.
|
||||
builder.append(", "sv);
|
||||
|
||||
// 4. Serialize this’s y internal slot, and append it to s.
|
||||
builder.append(m_y->to_string());
|
||||
|
||||
// 5. Append ", " to s.
|
||||
builder.append(", "sv);
|
||||
|
||||
// 6. Serialize this’s z internal slot, and append it to s.
|
||||
builder.append(m_z->to_string());
|
||||
|
||||
// 7. Append ")" to s, and return s.
|
||||
builder.append(")"sv);
|
||||
return builder.to_utf16_string();
|
||||
}
|
||||
// 3. Otherwise:
|
||||
else {
|
||||
// 1. Append "translate(" to s.
|
||||
builder.append("translate("sv);
|
||||
|
||||
// 2. Serialize this’s x internal slot, and append it to s.
|
||||
builder.append(m_x->to_string());
|
||||
|
||||
// 3. Append ", " to s.
|
||||
builder.append(", "sv);
|
||||
|
||||
// 4. Serialize this’s y internal slot, and append it to s.
|
||||
builder.append(m_y->to_string());
|
||||
|
||||
// 5. Append ")" to s, and return s.
|
||||
builder.append(")"sv);
|
||||
return builder.to_utf16_string();
|
||||
}
|
||||
}
|
||||
|
||||
// https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformcomponent-tomatrix
|
||||
WebIDL::ExceptionOr<GC::Ref<Geometry::DOMMatrix>> CSSTranslate::to_matrix() const
|
||||
{
|
||||
// 1. Let matrix be a new DOMMatrix object, initialized to this’s equivalent 4x4 transform matrix, as defined in
|
||||
// CSS Transforms 1 § 12. Mathematical Description of Transform Functions, and with its is2D internal slot set
|
||||
// to the same value as this’s is2D internal slot.
|
||||
// NOTE: Recall that the is2D flag affects what transform, and thus what equivalent matrix, a
|
||||
// CSSTransformComponent represents.
|
||||
// As the entries of such a matrix are defined relative to the px unit, if any <length>s in this involved in
|
||||
// generating the matrix are not compatible units with px (such as relative lengths or percentages), throw a
|
||||
// TypeError.
|
||||
auto matrix = Geometry::DOMMatrix::create(realm());
|
||||
|
||||
// NB: to() throws a TypeError if the conversion can't be done.
|
||||
matrix->set_m41(TRY(m_x->to("px"_fly_string))->value());
|
||||
matrix->set_m42(TRY(m_y->to("px"_fly_string))->value());
|
||||
if (!is_2d())
|
||||
matrix->set_m43(TRY(m_z->to("px"_fly_string))->value());
|
||||
|
||||
// 2. Return matrix.
|
||||
return matrix;
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> CSSTranslate::set_x(GC::Ref<CSSNumericValue> x)
|
||||
{
|
||||
// AD-HOC: Not specced. https://github.com/w3c/css-houdini-drafts/issues/1153
|
||||
// WPT expects this to throw for invalid values.
|
||||
if (!x->type().matches_length_percentage({}))
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "CSSTranslate x component doesn't match <length-percentage>"sv };
|
||||
m_x = x;
|
||||
return {};
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> CSSTranslate::set_y(GC::Ref<CSSNumericValue> y)
|
||||
{
|
||||
// AD-HOC: Not specced. https://github.com/w3c/css-houdini-drafts/issues/1153
|
||||
// WPT expects this to throw for invalid values.
|
||||
if (!y->type().matches_length_percentage({}))
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "CSSTranslate y component doesn't match <length-percentage>"sv };
|
||||
m_y = y;
|
||||
return {};
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> CSSTranslate::set_z(GC::Ref<CSSNumericValue> z)
|
||||
{
|
||||
// AD-HOC: Not specced. https://github.com/w3c/css-houdini-drafts/issues/1153
|
||||
// WPT expects this to throw for invalid values.
|
||||
if (!z->type().matches_length({}))
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "CSSTranslate z component doesn't match <length>"sv };
|
||||
m_z = z;
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
46
Libraries/LibWeb/CSS/CSSTranslate.h
Normal file
46
Libraries/LibWeb/CSS/CSSTranslate.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/CSS/CSSTransformComponent.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
// https://drafts.css-houdini.org/css-typed-om-1/#csstranslate
|
||||
class CSSTranslate final : public CSSTransformComponent {
|
||||
WEB_PLATFORM_OBJECT(CSSTranslate, CSSTransformComponent);
|
||||
GC_DECLARE_ALLOCATOR(CSSTranslate);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static GC::Ref<CSSTranslate> create(JS::Realm&, Is2D, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z);
|
||||
static WebIDL::ExceptionOr<GC::Ref<CSSTranslate>> construct_impl(JS::Realm&, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ptr<CSSNumericValue> z = {});
|
||||
|
||||
virtual ~CSSTranslate() override;
|
||||
|
||||
virtual WebIDL::ExceptionOr<Utf16String> to_string() const override;
|
||||
|
||||
virtual WebIDL::ExceptionOr<GC::Ref<Geometry::DOMMatrix>> to_matrix() const override;
|
||||
|
||||
GC::Ref<CSSNumericValue> x() const { return m_x; }
|
||||
GC::Ref<CSSNumericValue> y() const { return m_y; }
|
||||
GC::Ref<CSSNumericValue> z() const { return m_z; }
|
||||
WebIDL::ExceptionOr<void> set_x(GC::Ref<CSSNumericValue> value);
|
||||
WebIDL::ExceptionOr<void> set_y(GC::Ref<CSSNumericValue> value);
|
||||
WebIDL::ExceptionOr<void> set_z(GC::Ref<CSSNumericValue> value);
|
||||
|
||||
private:
|
||||
explicit CSSTranslate(JS::Realm&, Is2D, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
GC::Ref<CSSNumericValue> m_x;
|
||||
GC::Ref<CSSNumericValue> m_y;
|
||||
GC::Ref<CSSNumericValue> m_z;
|
||||
};
|
||||
|
||||
}
|
||||
11
Libraries/LibWeb/CSS/CSSTranslate.idl
Normal file
11
Libraries/LibWeb/CSS/CSSTranslate.idl
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#import <CSS/CSSNumericValue.idl>
|
||||
#import <CSS/CSSTransformComponent.idl>
|
||||
|
||||
// https://drafts.css-houdini.org/css-typed-om-1/#csstranslate
|
||||
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
|
||||
interface CSSTranslate : CSSTransformComponent {
|
||||
constructor(CSSNumericValue x, CSSNumericValue y, optional CSSNumericValue z);
|
||||
attribute CSSNumericValue x;
|
||||
attribute CSSNumericValue y;
|
||||
attribute CSSNumericValue z;
|
||||
};
|
||||
|
|
@ -268,6 +268,7 @@ class CSSStyleSheet;
|
|||
class CSSStyleValue;
|
||||
class CSSSupportsRule;
|
||||
class CSSTransformComponent;
|
||||
class CSSTranslate;
|
||||
class CSSUnitValue;
|
||||
class CSSUnparsedValue;
|
||||
class CSSVariableReferenceValue;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ libweb_js_bindings(CSS/CSSStyleValue)
|
|||
libweb_js_bindings(CSS/CSSSupportsRule)
|
||||
libweb_js_bindings(CSS/CSSTransformComponent)
|
||||
libweb_js_bindings(CSS/CSSTransition)
|
||||
libweb_js_bindings(CSS/CSSTranslate)
|
||||
libweb_js_bindings(CSS/CSSUnitValue)
|
||||
libweb_js_bindings(CSS/CSSUnparsedValue)
|
||||
libweb_js_bindings(CSS/CSSVariableReferenceValue)
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ CSSStyleValue
|
|||
CSSSupportsRule
|
||||
CSSTransformComponent
|
||||
CSSTransition
|
||||
CSSTranslate
|
||||
CSSUnitValue
|
||||
CSSUnparsedValue
|
||||
CSSVariableReferenceValue
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
|||
|
||||
Found 545 tests
|
||||
|
||||
296 Pass
|
||||
249 Fail
|
||||
305 Pass
|
||||
240 Fail
|
||||
Pass idl_test setup
|
||||
Pass idl_test validation
|
||||
Pass Partial interface Element: original interface defined
|
||||
|
|
@ -276,15 +276,15 @@ Pass CSSTransformComponent interface: existence and properties of interface prot
|
|||
Pass CSSTransformComponent interface: stringifier
|
||||
Pass CSSTransformComponent interface: attribute is2D
|
||||
Pass CSSTransformComponent interface: operation toMatrix()
|
||||
Fail CSSTranslate interface: existence and properties of interface object
|
||||
Fail CSSTranslate interface object length
|
||||
Fail CSSTranslate interface object name
|
||||
Fail CSSTranslate interface: existence and properties of interface prototype object
|
||||
Fail CSSTranslate interface: existence and properties of interface prototype object's "constructor" property
|
||||
Fail CSSTranslate interface: existence and properties of interface prototype object's @@unscopables property
|
||||
Fail CSSTranslate interface: attribute x
|
||||
Fail CSSTranslate interface: attribute y
|
||||
Fail CSSTranslate interface: attribute z
|
||||
Pass CSSTranslate interface: existence and properties of interface object
|
||||
Pass CSSTranslate interface object length
|
||||
Pass CSSTranslate interface object name
|
||||
Pass CSSTranslate interface: existence and properties of interface prototype object
|
||||
Pass CSSTranslate interface: existence and properties of interface prototype object's "constructor" property
|
||||
Pass CSSTranslate interface: existence and properties of interface prototype object's @@unscopables property
|
||||
Pass CSSTranslate interface: attribute x
|
||||
Pass CSSTranslate interface: attribute y
|
||||
Pass CSSTranslate interface: attribute z
|
||||
Fail CSSTranslate must be primary interface of transformValue[0]
|
||||
Fail Stringification of transformValue[0]
|
||||
Fail CSSTranslate interface: transformValue[0] must inherit property "x" with the proper type
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ Harness status: OK
|
|||
|
||||
Found 4 tests
|
||||
|
||||
4 Fail
|
||||
Fail CSSTranslate.toMatrix() flattens when told it is 2d
|
||||
1 Pass
|
||||
3 Fail
|
||||
Pass CSSTranslate.toMatrix() flattens when told it is 2d
|
||||
Fail CSSRotate.toMatrix() flattens when told it is 2d
|
||||
Fail CSSScale.toMatrix() flattens when told it is 2d
|
||||
Fail CSSMatrixComponent.toMatrix() flattens when told it is 2d
|
||||
|
|
@ -2,6 +2,7 @@ Harness status: OK
|
|||
|
||||
Found 2 tests
|
||||
|
||||
2 Fail
|
||||
Fail CSSTranslate.toMatrix() containing relative units throws TypeError
|
||||
1 Pass
|
||||
1 Fail
|
||||
Pass CSSTranslate.toMatrix() containing relative units throws TypeError
|
||||
Fail CSSPerspective.toMatrix() containing relative units throws TypeError
|
||||
|
|
@ -2,8 +2,9 @@ Harness status: OK
|
|||
|
||||
Found 8 tests
|
||||
|
||||
8 Fail
|
||||
Fail CSSTranslate.toMatrix() returns correct matrix
|
||||
1 Pass
|
||||
7 Fail
|
||||
Pass CSSTranslate.toMatrix() returns correct matrix
|
||||
Fail CSSRotate.toMatrix() returns correct matrix
|
||||
Fail CSSScale.toMatrix() returns correct matrix
|
||||
Fail CSSSkew.toMatrix() returns correct matrix
|
||||
|
|
|
|||
|
|
@ -2,26 +2,26 @@ Harness status: OK
|
|||
|
||||
Found 22 tests
|
||||
|
||||
22 Fail
|
||||
Fail Constructing a CSSTranslate with a CSSUnitValue with type other than length or percent for the coordinates throws a TypeError
|
||||
Fail Constructing a CSSTranslate with a CSSMathValue that doesn't match <length-percentage> for the coordinates throws a TypeError
|
||||
Fail Constructing a CSSTranslate with a percent for the Z coordinate throws a TypeError
|
||||
Fail Updating CSSTranslate.x to a CSSUnitValue with type other than length or percent throws a TypeError
|
||||
Fail Updating CSSTranslate.x to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
|
||||
Fail Updating CSSTranslate.y to a CSSUnitValue with type other than length or percent throws a TypeError
|
||||
Fail Updating CSSTranslate.y to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
|
||||
Fail Updating CSSTranslate.z to a CSSUnitValue with type other than length or percent throws a TypeError
|
||||
Fail Updating CSSTranslate.z to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
|
||||
Fail Updating CSSTranslate.z to a percent throws a TypeError
|
||||
Fail CSSTranslate can be constructed from two length or percent coordinates
|
||||
Fail CSSTranslate can be constructed from three length or percent coordinates
|
||||
Fail CSSTranslate can be constructed from CSSMathValues
|
||||
Fail CSSTranslate.x can be updated to a length
|
||||
Fail CSSTranslate.x can be updated to a percent
|
||||
Fail CSSTranslate.x can be updated to a CSSMathValue
|
||||
Fail CSSTranslate.y can be updated to a length
|
||||
Fail CSSTranslate.y can be updated to a percent
|
||||
Fail CSSTranslate.y can be updated to a CSSMathValue
|
||||
Fail CSSTranslate.z can be updated to a length
|
||||
Fail CSSTranslate.z can be updated to a CSSMathValue
|
||||
Fail Modifying CSSTranslate.is2D can be updated to true or false
|
||||
22 Pass
|
||||
Pass Constructing a CSSTranslate with a CSSUnitValue with type other than length or percent for the coordinates throws a TypeError
|
||||
Pass Constructing a CSSTranslate with a CSSMathValue that doesn't match <length-percentage> for the coordinates throws a TypeError
|
||||
Pass Constructing a CSSTranslate with a percent for the Z coordinate throws a TypeError
|
||||
Pass Updating CSSTranslate.x to a CSSUnitValue with type other than length or percent throws a TypeError
|
||||
Pass Updating CSSTranslate.x to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
|
||||
Pass Updating CSSTranslate.y to a CSSUnitValue with type other than length or percent throws a TypeError
|
||||
Pass Updating CSSTranslate.y to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
|
||||
Pass Updating CSSTranslate.z to a CSSUnitValue with type other than length or percent throws a TypeError
|
||||
Pass Updating CSSTranslate.z to a CSSMathValue that doesn't match <length-percentage> throws a TypeError
|
||||
Pass Updating CSSTranslate.z to a percent throws a TypeError
|
||||
Pass CSSTranslate can be constructed from two length or percent coordinates
|
||||
Pass CSSTranslate can be constructed from three length or percent coordinates
|
||||
Pass CSSTranslate can be constructed from CSSMathValues
|
||||
Pass CSSTranslate.x can be updated to a length
|
||||
Pass CSSTranslate.x can be updated to a percent
|
||||
Pass CSSTranslate.x can be updated to a CSSMathValue
|
||||
Pass CSSTranslate.y can be updated to a length
|
||||
Pass CSSTranslate.y can be updated to a percent
|
||||
Pass CSSTranslate.y can be updated to a CSSMathValue
|
||||
Pass CSSTranslate.z can be updated to a length
|
||||
Pass CSSTranslate.z can be updated to a CSSMathValue
|
||||
Pass Modifying CSSTranslate.is2D can be updated to true or false
|
||||
Loading…
Add table
Add a link
Reference in a new issue