LibWeb/CSS: Implement converting CSSTransformValues to StyleValues

This commit is contained in:
Sam Atkins 2025-10-13 16:38:36 +01:00
parent 5178d1ebe3
commit 3716db1c61
Notes: github-actions[bot] 2025-10-14 12:42:50 +00:00
20 changed files with 198 additions and 5 deletions

View file

@ -7,6 +7,9 @@
#include "CSSMatrixComponent.h" #include "CSSMatrixComponent.h"
#include <LibWeb/Bindings/CSSMatrixComponentPrototype.h> #include <LibWeb/Bindings/CSSMatrixComponentPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -82,4 +85,39 @@ WebIDL::ExceptionOr<void> CSSMatrixComponent::set_matrix(GC::Ref<Geometry::DOMMa
return {}; return {};
} }
WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> CSSMatrixComponent::create_style_value(PropertyNameAndID const& property) const
{
if (is_2d()) {
return TransformationStyleValue::create(property.id(), TransformFunction::Matrix,
{
NumberStyleValue::create(m_matrix->a()),
NumberStyleValue::create(m_matrix->b()),
NumberStyleValue::create(m_matrix->c()),
NumberStyleValue::create(m_matrix->d()),
NumberStyleValue::create(m_matrix->e()),
NumberStyleValue::create(m_matrix->f()),
});
}
return TransformationStyleValue::create(property.id(), TransformFunction::Matrix3d,
{
NumberStyleValue::create(m_matrix->m11()),
NumberStyleValue::create(m_matrix->m12()),
NumberStyleValue::create(m_matrix->m13()),
NumberStyleValue::create(m_matrix->m14()),
NumberStyleValue::create(m_matrix->m21()),
NumberStyleValue::create(m_matrix->m22()),
NumberStyleValue::create(m_matrix->m23()),
NumberStyleValue::create(m_matrix->m24()),
NumberStyleValue::create(m_matrix->m31()),
NumberStyleValue::create(m_matrix->m32()),
NumberStyleValue::create(m_matrix->m33()),
NumberStyleValue::create(m_matrix->m34()),
NumberStyleValue::create(m_matrix->m41()),
NumberStyleValue::create(m_matrix->m42()),
NumberStyleValue::create(m_matrix->m43()),
NumberStyleValue::create(m_matrix->m44()),
});
}
} }

View file

@ -33,6 +33,8 @@ public:
GC::Ref<Geometry::DOMMatrix> matrix() const { return m_matrix; } GC::Ref<Geometry::DOMMatrix> matrix() const { return m_matrix; }
WebIDL::ExceptionOr<void> set_matrix(GC::Ref<Geometry::DOMMatrix> matrix); WebIDL::ExceptionOr<void> set_matrix(GC::Ref<Geometry::DOMMatrix> matrix);
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const override;
private: private:
explicit CSSMatrixComponent(JS::Realm&, Is2D, GC::Ref<Geometry::DOMMatrix>); explicit CSSMatrixComponent(JS::Realm&, Is2D, GC::Ref<Geometry::DOMMatrix>);

View file

@ -9,6 +9,8 @@
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSNumericValue.h> #include <LibWeb/CSS/CSSNumericValue.h>
#include <LibWeb/CSS/CSSUnitValue.h> #include <LibWeb/CSS/CSSUnitValue.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -157,4 +159,12 @@ void CSSPerspective::set_is_2d(bool)
// The is2D attribute of a CSSPerspective object must, on setting, do nothing. // The is2D attribute of a CSSPerspective object must, on setting, do nothing.
} }
WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> CSSPerspective::create_style_value(PropertyNameAndID const& property) const
{
auto length = TRY(m_length.visit([&](auto const& value) {
return value->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No);
}));
return TransformationStyleValue::create(property.id(), TransformFunction::Perspective, { move(length) });
}
} }

View file

@ -37,6 +37,8 @@ public:
virtual void set_is_2d(bool value) override; virtual void set_is_2d(bool value) override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const override;
private: private:
explicit CSSPerspective(JS::Realm&, CSSPerspectiveValueInternal); explicit CSSPerspective(JS::Realm&, CSSPerspectiveValueInternal);

View file

@ -8,6 +8,8 @@
#include <LibWeb/Bindings/CSSRotatePrototype.h> #include <LibWeb/Bindings/CSSRotatePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSUnitValue.h> #include <LibWeb/CSS/CSSUnitValue.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -218,4 +220,22 @@ WebIDL::ExceptionOr<void> CSSRotate::set_angle(GC::Ref<CSSNumericValue> value)
return {}; return {};
} }
WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> CSSRotate::create_style_value(PropertyNameAndID const& property) const
{
if (is_2d()) {
return TransformationStyleValue::create(property.id(), TransformFunction::Rotate,
{
TRY(m_angle->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
return TransformationStyleValue::create(property.id(), TransformFunction::Rotate3d,
{
TRY(m_x->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_y->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_z->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_angle->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
} }

View file

@ -36,6 +36,8 @@ public:
WebIDL::ExceptionOr<void> set_z(CSSNumberish value); WebIDL::ExceptionOr<void> set_z(CSSNumberish value);
WebIDL::ExceptionOr<void> set_angle(GC::Ref<CSSNumericValue> value); WebIDL::ExceptionOr<void> set_angle(GC::Ref<CSSNumericValue> value);
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const override;
private: private:
explicit CSSRotate(JS::Realm&, Is2D, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z, GC::Ref<CSSNumericValue> angle); explicit CSSRotate(JS::Realm&, Is2D, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z, GC::Ref<CSSNumericValue> angle);

View file

@ -9,6 +9,8 @@
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSNumericValue.h> #include <LibWeb/CSS/CSSNumericValue.h>
#include <LibWeb/CSS/CSSUnitValue.h> #include <LibWeb/CSS/CSSUnitValue.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -199,4 +201,22 @@ WebIDL::ExceptionOr<void> CSSScale::set_z(CSSNumberish value)
return {}; return {};
} }
WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> CSSScale::create_style_value(PropertyNameAndID const& property) const
{
if (is_2d()) {
return TransformationStyleValue::create(property.id(), TransformFunction::Scale,
{
TRY(m_x->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_y->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
return TransformationStyleValue::create(property.id(), TransformFunction::Scale3d,
{
TRY(m_x->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_y->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_z->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
} }

View file

@ -33,6 +33,8 @@ public:
WebIDL::ExceptionOr<void> set_y(CSSNumberish value); WebIDL::ExceptionOr<void> set_y(CSSNumberish value);
WebIDL::ExceptionOr<void> set_z(CSSNumberish value); WebIDL::ExceptionOr<void> set_z(CSSNumberish value);
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const override;
private: private:
explicit CSSScale(JS::Realm&, Is2D, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z); explicit CSSScale(JS::Realm&, Is2D, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z);

View file

@ -9,6 +9,8 @@
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSNumericValue.h> #include <LibWeb/CSS/CSSNumericValue.h>
#include <LibWeb/CSS/CSSUnitValue.h> #include <LibWeb/CSS/CSSUnitValue.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -136,4 +138,13 @@ void CSSSkew::set_is_2d(bool)
// The is2D attribute of a CSSSkew, CSSSkewX, or CSSSkewY object must, on setting, do nothing. // The is2D attribute of a CSSSkew, CSSSkewX, or CSSSkewY object must, on setting, do nothing.
} }
WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> CSSSkew::create_style_value(PropertyNameAndID const& property) const
{
return TransformationStyleValue::create(property.id(), TransformFunction::Skew,
{
TRY(m_ax->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_ay->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
} }

View file

@ -32,6 +32,8 @@ public:
virtual void set_is_2d(bool value) override; virtual void set_is_2d(bool value) override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const override;
private: private:
explicit CSSSkew(JS::Realm&, GC::Ref<CSSNumericValue> ax, GC::Ref<CSSNumericValue> ay); explicit CSSSkew(JS::Realm&, GC::Ref<CSSNumericValue> ax, GC::Ref<CSSNumericValue> ay);

View file

@ -9,6 +9,8 @@
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSNumericValue.h> #include <LibWeb/CSS/CSSNumericValue.h>
#include <LibWeb/CSS/CSSUnitValue.h> #include <LibWeb/CSS/CSSUnitValue.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -106,4 +108,12 @@ void CSSSkewX::set_is_2d(bool)
// The is2D attribute of a CSSSkewX, CSSSkewXX, or CSSSkewXY object must, on setting, do nothing. // The is2D attribute of a CSSSkewX, CSSSkewXX, or CSSSkewXY object must, on setting, do nothing.
} }
WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> CSSSkewX::create_style_value(PropertyNameAndID const& property) const
{
return TransformationStyleValue::create(property.id(), TransformFunction::SkewX,
{
TRY(m_ax->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
} }

View file

@ -30,6 +30,8 @@ public:
virtual void set_is_2d(bool value) override; virtual void set_is_2d(bool value) override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const override;
private: private:
explicit CSSSkewX(JS::Realm&, GC::Ref<CSSNumericValue> ax); explicit CSSSkewX(JS::Realm&, GC::Ref<CSSNumericValue> ax);

View file

@ -9,6 +9,8 @@
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSNumericValue.h> #include <LibWeb/CSS/CSSNumericValue.h>
#include <LibWeb/CSS/CSSUnitValue.h> #include <LibWeb/CSS/CSSUnitValue.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -106,4 +108,12 @@ void CSSSkewY::set_is_2d(bool)
// The is2D attribute of a CSSSkewY, CSSSkewYX, or CSSSkewYY object must, on setting, do nothing. // The is2D attribute of a CSSSkewY, CSSSkewYX, or CSSSkewYY object must, on setting, do nothing.
} }
WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> CSSSkewY::create_style_value(PropertyNameAndID const& property) const
{
return TransformationStyleValue::create(property.id(), TransformFunction::SkewY,
{
TRY(m_ay->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
} }

View file

@ -30,6 +30,8 @@ public:
virtual void set_is_2d(bool value) override; virtual void set_is_2d(bool value) override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const override;
private: private:
explicit CSSSkewY(JS::Realm&, GC::Ref<CSSNumericValue> ay); explicit CSSSkewY(JS::Realm&, GC::Ref<CSSNumericValue> ay);

View file

@ -31,6 +31,8 @@ public:
virtual WebIDL::ExceptionOr<GC::Ref<Geometry::DOMMatrix>> to_matrix() const = 0; virtual WebIDL::ExceptionOr<GC::Ref<Geometry::DOMMatrix>> to_matrix() const = 0;
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const = 0;
protected: protected:
explicit CSSTransformComponent(JS::Realm&, Is2D is_2d); explicit CSSTransformComponent(JS::Realm&, Is2D is_2d);

View file

@ -8,6 +8,9 @@
#include <LibWeb/Bindings/CSSTransformValuePrototype.h> #include <LibWeb/Bindings/CSSTransformValuePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSTransformComponent.h> #include <LibWeb/CSS/CSSTransformComponent.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -155,4 +158,35 @@ WebIDL::ExceptionOr<String> CSSTransformValue::to_string() const
return builder.to_string_without_validation(); return builder.to_string_without_validation();
} }
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSTransformValue::create_an_internal_representation(PropertyNameAndID const& property, PerformTypeCheck perform_type_check) const
{
// NB: This can become <transform-function> or <transform-list>, and we don't know which is wanted without performing the type checking.
// We can worry about that if and when we ever do have a CSSTransformValue that isn't top-level.
VERIFY(perform_type_check == PerformTypeCheck::Yes);
// If value is a CSSStyleValue subclass,
// If value does not match the grammar of a list-valued property iteration of property, throw a TypeError.
//
// If any component of propertys CSS grammar has a limited numeric range, and the corresponding part of value
// is a CSSUnitValue that is outside of that range, replace that value with the result of wrapping it in a
// fresh CSSMathSum whose values internal slot contains only that part of value.
//
// Return the value.
// NB: We match <transform-function> if we have 1 transform. We match <transform-list> always.
if (m_transforms.size() == 1 && property_accepts_type(property.id(), ValueType::TransformFunction))
return TRY(m_transforms.first()->create_style_value(property));
if (property_accepts_type(property.id(), ValueType::TransformList)) {
StyleValueVector transforms;
transforms.ensure_capacity(m_transforms.size());
for (auto const transform : m_transforms)
transforms.unchecked_append(TRY(transform->create_style_value(property)));
return StyleValueList::create(move(transforms), StyleValueList::Separator::Space);
}
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Property does not accept values of this type."sv };
}
} }

View file

@ -32,6 +32,8 @@ public:
virtual WebIDL::ExceptionOr<String> to_string() const override; virtual WebIDL::ExceptionOr<String> to_string() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;
private: private:
explicit CSSTransformValue(JS::Realm&, Vector<GC::Ref<CSSTransformComponent>>); explicit CSSTransformValue(JS::Realm&, Vector<GC::Ref<CSSTransformComponent>>);

View file

@ -9,6 +9,8 @@
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSNumericValue.h> #include <LibWeb/CSS/CSSNumericValue.h>
#include <LibWeb/CSS/CSSUnitValue.h> #include <LibWeb/CSS/CSSUnitValue.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/Geometry/DOMMatrix.h> #include <LibWeb/Geometry/DOMMatrix.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -178,4 +180,22 @@ WebIDL::ExceptionOr<void> CSSTranslate::set_z(GC::Ref<CSSNumericValue> z)
return {}; return {};
} }
WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> CSSTranslate::create_style_value(PropertyNameAndID const& property) const
{
if (is_2d()) {
return TransformationStyleValue::create(property.id(), TransformFunction::Translate,
{
TRY(m_x->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_y->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
return TransformationStyleValue::create(property.id(), TransformFunction::Translate3d,
{
TRY(m_x->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_y->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
TRY(m_z->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::No)),
});
}
} }

View file

@ -32,6 +32,8 @@ public:
WebIDL::ExceptionOr<void> set_y(GC::Ref<CSSNumericValue> value); WebIDL::ExceptionOr<void> set_y(GC::Ref<CSSNumericValue> value);
WebIDL::ExceptionOr<void> set_z(GC::Ref<CSSNumericValue> value); WebIDL::ExceptionOr<void> set_z(GC::Ref<CSSNumericValue> value);
virtual WebIDL::ExceptionOr<NonnullRefPtr<TransformationStyleValue const>> create_style_value(PropertyNameAndID const&) const override;
private: private:
explicit CSSTranslate(JS::Realm&, Is2D, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z); explicit CSSTranslate(JS::Realm&, Is2D, GC::Ref<CSSNumericValue> x, GC::Ref<CSSNumericValue> y, GC::Ref<CSSNumericValue> z);

View file

@ -2,17 +2,17 @@ Harness status: OK
Found 33 tests Found 33 tests
28 Pass 31 Pass
5 Fail 2 Fail
Pass Can set 'transform' to CSS-wide keywords: initial Pass Can set 'transform' to CSS-wide keywords: initial
Pass Can set 'transform' to CSS-wide keywords: inherit Pass Can set 'transform' to CSS-wide keywords: inherit
Pass Can set 'transform' to CSS-wide keywords: unset Pass Can set 'transform' to CSS-wide keywords: unset
Pass Can set 'transform' to CSS-wide keywords: revert Pass Can set 'transform' to CSS-wide keywords: revert
Fail Can set 'transform' to var() references: var(--A) Fail Can set 'transform' to var() references: var(--A)
Pass Can set 'transform' to the 'none' keyword: none Pass Can set 'transform' to the 'none' keyword: none
Fail Can set 'transform' to a transform: translate(50%, 50%) Pass Can set 'transform' to a transform: translate(50%, 50%)
Fail Can set 'transform' to a transform: perspective(10em) Pass Can set 'transform' to a transform: perspective(10em)
Fail Can set 'transform' to a transform: translate3d(0px, 1px, 2px) translate(0px, 1px) rotate3d(1, 2, 3, 45deg) rotate(45deg) scale3d(1, 2, 3) scale(1, 2) skew(1deg, 1deg) skewX(1deg) skewY(45deg) perspective(1px) matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) matrix(1, 2, 3, 4, 5, 6) Pass Can set 'transform' to a transform: translate3d(0px, 1px, 2px) translate(0px, 1px) rotate3d(1, 2, 3, 45deg) rotate(45deg) scale3d(1, 2, 3) scale(1, 2) skew(1deg, 1deg) skewX(1deg) skewY(45deg) perspective(1px) matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) matrix(1, 2, 3, 4, 5, 6)
Pass Setting 'transform' to a length: 0px throws TypeError Pass Setting 'transform' to a length: 0px throws TypeError
Pass Setting 'transform' to a length: -3.14em throws TypeError Pass Setting 'transform' to a length: -3.14em throws TypeError
Pass Setting 'transform' to a length: 3.14cm throws TypeError Pass Setting 'transform' to a length: 3.14cm throws TypeError