Skip to content

Commit b24b00f

Browse files
committed
LibWeb/CSS: Reify CSSTransformValues
We have a slightly odd setup here. TransformationStyleValue reifies as a single CSSTransformComponent. It's StyleValueList that actually reifies as a CSSTransformValue - but only if it only contains TransformationStyleValues. +79 WPT subtests.
1 parent d5977b9 commit b24b00f

File tree

6 files changed

+272
-86
lines changed

6 files changed

+272
-86
lines changed

Libraries/LibWeb/CSS/StyleValues/StyleValueList.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
*/
99

1010
#include "StyleValueList.h"
11+
#include <LibGC/RootVector.h>
12+
#include <LibJS/Runtime/Realm.h>
13+
#include <LibWeb/CSS/CSSTransformComponent.h>
14+
#include <LibWeb/CSS/CSSTransformValue.h>
1115
#include <LibWeb/CSS/Parser/ComponentValue.h>
16+
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
1217

1318
namespace Web::CSS {
1419

@@ -83,4 +88,26 @@ Vector<Parser::ComponentValue> StyleValueList::tokenize() const
8388
return component_values;
8489
}
8590

91+
// https://drafts.css-houdini.org/css-typed-om-1/#reify-a-transform-list
92+
static GC::Ref<CSSStyleValue> reify_a_transform_list(JS::Realm& realm, StyleValueVector const& values)
93+
{
94+
GC::RootVector<GC::Ref<CSSTransformComponent>> transform_components { realm.heap() };
95+
for (auto const& transform : values) {
96+
transform_components.append(transform->as_transformation().reify_a_transform_function(realm));
97+
}
98+
return CSSTransformValue::create(realm, static_cast<Vector<GC::Ref<CSSTransformComponent>>>(move(transform_components)));
99+
}
100+
101+
GC::Ref<CSSStyleValue> StyleValueList::reify(JS::Realm& realm, String const& associated_property) const
102+
{
103+
// NB: <transform-list> is a StyleValueList that contains TransformStyleValues. If that's what we are, follow the
104+
// steps for reifying that.
105+
if (all_of(m_properties.values, [](auto const& it) { return it->is_transformation(); })) {
106+
return reify_a_transform_list(realm, m_properties.values);
107+
}
108+
109+
// NB: Otherwise, there isn't an equivalent CSSStyleValue for StyleValueList, so just use the default.
110+
return Base::reify(realm, associated_property);
111+
}
112+
86113
}

Libraries/LibWeb/CSS/StyleValues/StyleValueList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class StyleValueList final : public StyleValueWithDefaultOperators<StyleValueLis
3535

3636
virtual String to_string(SerializationMode) const override;
3737
virtual Vector<Parser::ComponentValue> tokenize() const override;
38+
virtual GC::Ref<CSSStyleValue> reify(JS::Realm&, String const& associated_property) const override;
3839

3940
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const override;
4041

Libraries/LibWeb/CSS/StyleValues/TransformationStyleValue.cpp

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,23 @@
1010

1111
#include "TransformationStyleValue.h"
1212
#include <AK/StringBuilder.h>
13+
#include <LibWeb/CSS/CSSMatrixComponent.h>
14+
#include <LibWeb/CSS/CSSPerspective.h>
15+
#include <LibWeb/CSS/CSSRotate.h>
16+
#include <LibWeb/CSS/CSSScale.h>
17+
#include <LibWeb/CSS/CSSSkew.h>
18+
#include <LibWeb/CSS/CSSSkewX.h>
19+
#include <LibWeb/CSS/CSSSkewY.h>
20+
#include <LibWeb/CSS/CSSTransformComponent.h>
21+
#include <LibWeb/CSS/CSSTranslate.h>
22+
#include <LibWeb/CSS/CSSUnitValue.h>
1323
#include <LibWeb/CSS/Serialize.h>
1424
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
1525
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
1626
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
1727
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
1828
#include <LibWeb/CSS/Transformation.h>
29+
#include <LibWeb/Geometry/DOMMatrix.h>
1930

2031
namespace Web::CSS {
2132

@@ -218,6 +229,152 @@ String TransformationStyleValue::to_string(SerializationMode mode) const
218229
return MUST(builder.to_string());
219230
}
220231

232+
// https://drafts.css-houdini.org/css-typed-om-1/#reify-a-transform-function
233+
GC::Ref<CSSTransformComponent> TransformationStyleValue::reify_a_transform_function(JS::Realm& realm) const
234+
{
235+
auto reify_numeric_argument = [&](size_t index) {
236+
return GC::Ref { as<CSSNumericValue>(*m_properties.values[index]->reify(realm, {})) };
237+
};
238+
auto reify_0 = [&] { return CSSUnitValue::create(realm, 0, "number"_fly_string); };
239+
auto reify_1 = [&] { return CSSUnitValue::create(realm, 1, "number"_fly_string); };
240+
auto reify_0px = [&] { return CSSUnitValue::create(realm, 0, "px"_fly_string); };
241+
auto reify_0deg = [&] { return CSSUnitValue::create(realm, 0, "deg"_fly_string); };
242+
243+
// To reify a <transform-function> func, perform the appropriate set of steps below, based on func:
244+
switch (m_properties.transform_function) {
245+
// -> matrix()
246+
// -> matrix3d()
247+
// 1. Return a new CSSMatrixComponent object, whose matrix internal slot is set to a 4x4 matrix representing the
248+
// same information as func, and whose is2D internal slot is true if func is matrix(), and false otherwise.
249+
case TransformFunction::Matrix:
250+
case TransformFunction::Matrix3d: {
251+
auto transform_as_matrix = MUST(to_transformation().to_matrix({}));
252+
auto matrix = Geometry::DOMMatrix::create(realm);
253+
matrix->set_m11(transform_as_matrix[0, 0]);
254+
matrix->set_m12(transform_as_matrix[1, 0]);
255+
matrix->set_m13(transform_as_matrix[2, 0]);
256+
matrix->set_m14(transform_as_matrix[3, 0]);
257+
matrix->set_m21(transform_as_matrix[0, 1]);
258+
matrix->set_m22(transform_as_matrix[1, 1]);
259+
matrix->set_m23(transform_as_matrix[2, 1]);
260+
matrix->set_m24(transform_as_matrix[3, 1]);
261+
matrix->set_m31(transform_as_matrix[0, 2]);
262+
matrix->set_m32(transform_as_matrix[1, 2]);
263+
matrix->set_m33(transform_as_matrix[2, 2]);
264+
matrix->set_m34(transform_as_matrix[3, 2]);
265+
matrix->set_m41(transform_as_matrix[0, 3]);
266+
matrix->set_m42(transform_as_matrix[1, 3]);
267+
matrix->set_m43(transform_as_matrix[2, 3]);
268+
matrix->set_m44(transform_as_matrix[3, 3]);
269+
270+
auto is_2d = m_properties.transform_function == TransformFunction::Matrix ? CSSTransformComponent::Is2D::Yes : CSSTransformComponent::Is2D::No;
271+
return CSSMatrixComponent::create(realm, is_2d, matrix);
272+
}
273+
274+
// -> translate()
275+
// -> translateX()
276+
// -> translateY()
277+
// -> translate3d()
278+
// -> translateZ()
279+
// 1. Return a new CSSTranslate object, whose x, y, and z internal slots are set to the reification of the
280+
// specified x/y/z offsets, or the reification of 0px if not specified in func, and whose is2D internal slot
281+
// is true if func is translate(), translateX(), or translateY(), and false otherwise.
282+
case TransformFunction::Translate: {
283+
// NB: Default y to 0px if it's not specified.
284+
auto y = m_properties.values.size() > 1 ? reify_numeric_argument(1) : reify_0px();
285+
return CSSTranslate::create(realm, CSSTransformComponent::Is2D::Yes, reify_numeric_argument(0), y, reify_0px());
286+
}
287+
case TransformFunction::TranslateX:
288+
return CSSTranslate::create(realm, CSSTransformComponent::Is2D::Yes, reify_numeric_argument(0), reify_0px(), reify_0px());
289+
case TransformFunction::TranslateY:
290+
return CSSTranslate::create(realm, CSSTransformComponent::Is2D::Yes, reify_0px(), reify_numeric_argument(0), reify_0px());
291+
case TransformFunction::Translate3d:
292+
return CSSTranslate::create(realm, CSSTransformComponent::Is2D::No, reify_numeric_argument(0), reify_numeric_argument(1), reify_numeric_argument(2));
293+
case TransformFunction::TranslateZ:
294+
return CSSTranslate::create(realm, CSSTransformComponent::Is2D::No, reify_0px(), reify_0px(), reify_numeric_argument(0));
295+
296+
// -> scale()
297+
// -> scaleX()
298+
// -> scaleY()
299+
// -> scale3d()
300+
// -> scaleZ()
301+
// 1. Return a new CSSScale object, whose x, y, and z internal slots are set to the specified x/y/z scales, or
302+
// to 1 if not specified in func and whose is2D internal slot is true if func is scale(), scaleX(), or
303+
// scaleY(), and false otherwise.
304+
case TransformFunction::Scale: {
305+
// NB: Default y to a copy of x if it's not specified.
306+
auto y = m_properties.values.size() > 1 ? reify_numeric_argument(1) : reify_numeric_argument(0);
307+
return CSSScale::create(realm, CSSTransformComponent::Is2D::Yes, reify_numeric_argument(0), y, reify_1());
308+
}
309+
case TransformFunction::ScaleX:
310+
return CSSScale::create(realm, CSSTransformComponent::Is2D::Yes, reify_numeric_argument(0), reify_1(), reify_1());
311+
case TransformFunction::ScaleY:
312+
return CSSScale::create(realm, CSSTransformComponent::Is2D::Yes, reify_1(), reify_numeric_argument(0), reify_1());
313+
case TransformFunction::Scale3d:
314+
return CSSScale::create(realm, CSSTransformComponent::Is2D::No, reify_numeric_argument(0), reify_numeric_argument(1), reify_numeric_argument(2));
315+
case TransformFunction::ScaleZ:
316+
return CSSScale::create(realm, CSSTransformComponent::Is2D::No, reify_1(), reify_1(), reify_numeric_argument(0));
317+
318+
// -> rotate()
319+
// -> rotate3d()
320+
// -> rotateX()
321+
// -> rotateY()
322+
// -> rotateZ()
323+
// 1. Return a new CSSRotate object, whose angle internal slot is set to the reification of the specified angle,
324+
// and whose x, y, and z internal slots are set to the specified rotation axis coordinates, or the implicit
325+
// axis coordinates if not specified in func and whose is2D internal slot is true if func is rotate(), and
326+
// false otherwise.
327+
case TransformFunction::Rotate:
328+
return CSSRotate::create(realm, CSSTransformComponent::Is2D::Yes, reify_0(), reify_0(), reify_1(), reify_numeric_argument(0));
329+
case TransformFunction::Rotate3d:
330+
return CSSRotate::create(realm, CSSTransformComponent::Is2D::No, reify_numeric_argument(0), reify_numeric_argument(1), reify_numeric_argument(2), reify_numeric_argument(3));
331+
case TransformFunction::RotateX:
332+
return CSSRotate::create(realm, CSSTransformComponent::Is2D::No, reify_1(), reify_0(), reify_0(), reify_numeric_argument(0));
333+
case TransformFunction::RotateY:
334+
return CSSRotate::create(realm, CSSTransformComponent::Is2D::No, reify_0(), reify_1(), reify_0(), reify_numeric_argument(0));
335+
case TransformFunction::RotateZ:
336+
return CSSRotate::create(realm, CSSTransformComponent::Is2D::No, reify_0(), reify_0(), reify_1(), reify_numeric_argument(0));
337+
338+
// -> skew()
339+
// 1. Return a new CSSSkew object, whose ax and ay internal slots are set to the reification of the specified x
340+
// and y angles, or the reification of 0deg if not specified in func, and whose is2D internal slot is true.
341+
case TransformFunction::Skew: {
342+
// NB: Default y to 0deg if it's not specified.
343+
auto y = m_properties.values.size() > 1 ? reify_numeric_argument(1) : reify_0deg();
344+
return CSSSkew::create(realm, reify_numeric_argument(0), y);
345+
}
346+
347+
// -> skewX()
348+
// 1. Return a new CSSSkewX object, whose ax internal slot is set to the reification of the specified x angle,
349+
// or the reification of 0deg if not specified in func, and whose is2D internal slot is true.
350+
case TransformFunction::SkewX:
351+
return CSSSkewX::create(realm, reify_numeric_argument(0));
352+
353+
// -> skewY()
354+
// 1. Return a new CSSSkewY object, whose ay internal slot is set to the reification of the specified y angle,
355+
// or the reification of 0deg if not specified in func, and whose is2D internal slot is true.
356+
case TransformFunction::SkewY:
357+
return CSSSkewY::create(realm, reify_numeric_argument(0));
358+
359+
// -> perspective()
360+
// 1. Return a new CSSPerspective object, whose length internal slot is set to the reification of the specified
361+
// length (see reify a numeric value if it is a length, and reify an identifier if it is the keyword none)
362+
// and whose is2D internal slot is false.
363+
case TransformFunction::Perspective: {
364+
CSSPerspectiveValueInternal length = [&]() -> CSSPerspectiveValueInternal {
365+
auto reified = m_properties.values[0]->reify(realm, {});
366+
if (auto* keyword = as_if<CSSKeywordValue>(*reified))
367+
return GC::Ref { *keyword };
368+
if (auto* numeric = as_if<CSSNumericValue>(*reified))
369+
return GC::Ref { *numeric };
370+
VERIFY_NOT_REACHED();
371+
}();
372+
return CSSPerspective::create(realm, length);
373+
}
374+
}
375+
VERIFY_NOT_REACHED();
376+
}
377+
221378
bool TransformationStyleValue::Properties::operator==(Properties const& other) const
222379
{
223380
return property == other.property

Libraries/LibWeb/CSS/StyleValues/TransformationStyleValue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class TransformationStyleValue final : public StyleValueWithDefaultOperators<Tra
2828
Transformation to_transformation() const;
2929

3030
virtual String to_string(SerializationMode) const override;
31+
GC::Ref<CSSTransformComponent> reify_a_transform_function(JS::Realm&) const;
3132

3233
bool properties_equal(TransformationStyleValue const& other) const { return m_properties == other.m_properties; }
3334

0 commit comments

Comments
 (0)