mirror of
https://github.com/RGBCube/serenity
synced 2025-05-18 18:35:06 +00:00
LibWeb: Produce resolved transform value according to spec algorithm
We now produce a `matrix3d()` value when appropriate. Some sites (such as gsap.com) request the resolved style for `transform` when there's no viewport paintable, but the element itself does already have a stacking context. This fixes crashes in that case, because we now do not access the stacking context at all. We also do not wrap the result as a StyleValueList any more. The returned StyleValue is only serialized and exposed to JS, so making it a StyleValueList has no effect.
This commit is contained in:
parent
c65d6964ea
commit
c9c99b3c42
3 changed files with 122 additions and 26 deletions
|
@ -0,0 +1,17 @@
|
||||||
|
none => none
|
||||||
|
matrix(1, 2, 3, 4, 5, 6) => matrix(1, 2, 3, 4, 5, 6)
|
||||||
|
matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) => matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
|
||||||
|
translate(1%, 2px) => matrix(1, 0, 0, 1, 7.84375, 2)
|
||||||
|
translate3d(1%, 2px, 3em) => matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 7.84375, 2, 48, 1)
|
||||||
|
translateX(1px) => matrix(1, 0, 0, 1, 1, 0)
|
||||||
|
translateY(1%) => matrix(1, 0, 0, 1, 0, 0)
|
||||||
|
scale(1, 2) => matrix(1, 0, 0, 2, 0, 0)
|
||||||
|
scaleX(2) => matrix(2, 0, 0, 1, 0, 0)
|
||||||
|
scaleY(2.5) => matrix(1, 0, 0, 2.5, 0, 0)
|
||||||
|
rotate(1deg) => matrix(0.999847, 0.017452, -0.017452, 0.999847, 0, 0)
|
||||||
|
rotateX(1rad) => matrix3d(1, 0, 0, 0, 0, 0.540302, 0.841470, 0, 0, -0.841470, 0.540302, 0, 0, 0, 0, 1)
|
||||||
|
rotateY(1grad) => matrix3d(0.999876, 0, -0.015707, 0, 0, 1, 0, 0, 0.015707, 0, 0.999876, 0, 0, 0, 0, 1)
|
||||||
|
rotateZ(1turn) => matrix(1, 0, -0, 1, 0, 0)
|
||||||
|
skew(1deg, 1rad) => matrix(1, 1.557407, 0.017455, 1, 0, 0)
|
||||||
|
skewX(1deg) => matrix(1, 0, 0.017455, 1, 0, 0)
|
||||||
|
skewY(1rad) => matrix(1, 1.557407, 0, 1, 0, 0)
|
35
Tests/LibWeb/Text/input/css/getComputedStyle-transform.html
Normal file
35
Tests/LibWeb/Text/input/css/getComputedStyle-transform.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<script>
|
||||||
|
test(() => {
|
||||||
|
const e = document.createElement("div");
|
||||||
|
document.body.appendChild(e);
|
||||||
|
function checkTransform(transform) {
|
||||||
|
e.style.transform = transform;
|
||||||
|
const computedStyle = getComputedStyle(e);
|
||||||
|
const serialized = computedStyle.transform;
|
||||||
|
println(transform + " => " + serialized);
|
||||||
|
}
|
||||||
|
for (transform of [
|
||||||
|
"none",
|
||||||
|
"matrix(1, 2, 3, 4, 5, 6)",
|
||||||
|
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)",
|
||||||
|
"translate(1%, 2px)",
|
||||||
|
"translate3d(1%, 2px, 3em)",
|
||||||
|
"translateX(1px)",
|
||||||
|
"translateY(1%)",
|
||||||
|
"scale(1, 2)",
|
||||||
|
"scaleX(2)",
|
||||||
|
"scaleY(2.5)",
|
||||||
|
"rotate(1deg)",
|
||||||
|
"rotateX(1rad)",
|
||||||
|
"rotateY(1grad)",
|
||||||
|
"rotateZ(1turn)",
|
||||||
|
"skew(1deg, 1rad)",
|
||||||
|
"skewX(1deg)",
|
||||||
|
"skewY(1rad)",
|
||||||
|
]) {
|
||||||
|
checkTransform(transform);
|
||||||
|
}
|
||||||
|
e.remove();
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -340,41 +340,85 @@ RefPtr<StyleValue const> ResolvedCSSStyleDeclaration::style_value_for_property(L
|
||||||
// -> A resolved value special case property defined in another specification
|
// -> A resolved value special case property defined in another specification
|
||||||
// As defined in the relevant specification.
|
// As defined in the relevant specification.
|
||||||
case PropertyID::Transform: {
|
case PropertyID::Transform: {
|
||||||
// NOTE: The computed value for `transform` serializes as a single `matrix(...)` value, instead of
|
|
||||||
// the original list of transform functions. So, we produce a StyleValue for that.
|
|
||||||
// https://www.w3.org/TR/css-transforms-1/#serialization-of-the-computed-value
|
|
||||||
// FIXME: Computing values should happen in the StyleComputer!
|
|
||||||
auto transformations = layout_node.computed_values().transformations();
|
auto transformations = layout_node.computed_values().transformations();
|
||||||
if (transformations.is_empty())
|
if (transformations.is_empty())
|
||||||
return IdentifierStyleValue::create(ValueID::None);
|
return IdentifierStyleValue::create(ValueID::None);
|
||||||
|
|
||||||
// The transform matrix is held by the StackingContext, so we need to make sure we have one first.
|
// https://drafts.csswg.org/css-transforms-2/#serialization-of-the-computed-value
|
||||||
auto const* viewport = layout_node.document().paintable();
|
// The transform property is a resolved value special case property. [CSSOM]
|
||||||
VERIFY(viewport);
|
// When the computed value is a <transform-list>, the resolved value is one <matrix()> function or one <matrix3d()> function computed by the following algorithm:
|
||||||
const_cast<Painting::ViewportPaintable&>(*viewport).build_stacking_context_tree_if_needed();
|
// 1. Let transform be a 4x4 matrix initialized to the identity matrix.
|
||||||
|
// The elements m11, m22, m33 and m44 of transform must be set to 1; all other elements of transform must be set to 0.
|
||||||
|
auto transform = FloatMatrix4x4::identity();
|
||||||
|
|
||||||
|
// 2. Post-multiply all <transform-function>s in <transform-list> to transform.
|
||||||
VERIFY(layout_node.paintable());
|
VERIFY(layout_node.paintable());
|
||||||
auto const& paintable_box = verify_cast<Painting::PaintableBox const>(layout_node.paintable());
|
auto const& paintable_box = verify_cast<Painting::PaintableBox const>(*layout_node.paintable());
|
||||||
VERIFY(paintable_box->stacking_context());
|
for (auto transformation : transformations) {
|
||||||
|
transform = transform * transformation.to_matrix(paintable_box);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This needs to serialize to matrix3d if the transformation matrix is a 3D matrix.
|
// https://drafts.csswg.org/css-transforms-1/#2d-matrix
|
||||||
// https://w3c.github.io/csswg-drafts/css-transforms-2/#serialization-of-the-computed-value
|
auto is_2d_matrix = [](Gfx::FloatMatrix4x4 const& matrix) -> bool {
|
||||||
auto affine_matrix = paintable_box->stacking_context()->affine_transform_matrix();
|
// A 3x2 transformation matrix,
|
||||||
|
// or a 4x4 matrix where the items m31, m32, m13, m23, m43, m14, m24, m34 are equal to 0
|
||||||
|
// and m33, m44 are equal to 1.
|
||||||
|
// NOTE: We only care about 4x4 matrices here.
|
||||||
|
// NOTE: Our elements are 0-indexed not 1-indexed, and in the opposite order.
|
||||||
|
if (matrix.elements()[0][2] != 0 // m31
|
||||||
|
|| matrix.elements()[1][2] != 0 // m32
|
||||||
|
|| matrix.elements()[2][0] != 0 // m13
|
||||||
|
|| matrix.elements()[2][1] != 0 // m23
|
||||||
|
|| matrix.elements()[2][3] != 0 // m43
|
||||||
|
|| matrix.elements()[3][0] != 0 // m14
|
||||||
|
|| matrix.elements()[3][1] != 0 // m24
|
||||||
|
|| matrix.elements()[3][2] != 0) // m34
|
||||||
|
return false;
|
||||||
|
|
||||||
StyleValueVector parameters;
|
if (matrix.elements()[2][2] != 1 // m33
|
||||||
parameters.ensure_capacity(6);
|
|| matrix.elements()[3][3] != 1) // m44
|
||||||
parameters.unchecked_append(NumberStyleValue::create(affine_matrix.a()));
|
return false;
|
||||||
parameters.unchecked_append(NumberStyleValue::create(affine_matrix.b()));
|
|
||||||
parameters.unchecked_append(NumberStyleValue::create(affine_matrix.c()));
|
|
||||||
parameters.unchecked_append(NumberStyleValue::create(affine_matrix.d()));
|
|
||||||
parameters.unchecked_append(NumberStyleValue::create(affine_matrix.e()));
|
|
||||||
parameters.unchecked_append(NumberStyleValue::create(affine_matrix.f()));
|
|
||||||
|
|
||||||
NonnullRefPtr<StyleValue> matrix_function = TransformationStyleValue::create(TransformFunction::Matrix, move(parameters));
|
return true;
|
||||||
// Elsewhere we always store the transform property's value as a StyleValueList of TransformationStyleValues,
|
};
|
||||||
// so this is just for consistency.
|
|
||||||
StyleValueVector matrix_functions { matrix_function };
|
// 3. Chose between <matrix()> or <matrix3d()> serialization:
|
||||||
return StyleValueList::create(move(matrix_functions), StyleValueList::Separator::Space);
|
// -> If transform is a 2D matrix
|
||||||
|
// Serialize transform to a <matrix()> function.
|
||||||
|
if (is_2d_matrix(transform)) {
|
||||||
|
StyleValueVector parameters {
|
||||||
|
NumberStyleValue::create(transform.elements()[0][0]),
|
||||||
|
NumberStyleValue::create(transform.elements()[1][0]),
|
||||||
|
NumberStyleValue::create(transform.elements()[0][1]),
|
||||||
|
NumberStyleValue::create(transform.elements()[1][1]),
|
||||||
|
NumberStyleValue::create(transform.elements()[0][3]),
|
||||||
|
NumberStyleValue::create(transform.elements()[1][3]),
|
||||||
|
};
|
||||||
|
return TransformationStyleValue::create(TransformFunction::Matrix, move(parameters));
|
||||||
|
}
|
||||||
|
// -> Otherwise
|
||||||
|
// Serialize transform to a <matrix3d()> function.
|
||||||
|
else {
|
||||||
|
StyleValueVector parameters {
|
||||||
|
NumberStyleValue::create(transform.elements()[0][0]),
|
||||||
|
NumberStyleValue::create(transform.elements()[1][0]),
|
||||||
|
NumberStyleValue::create(transform.elements()[2][0]),
|
||||||
|
NumberStyleValue::create(transform.elements()[3][0]),
|
||||||
|
NumberStyleValue::create(transform.elements()[0][1]),
|
||||||
|
NumberStyleValue::create(transform.elements()[1][1]),
|
||||||
|
NumberStyleValue::create(transform.elements()[2][1]),
|
||||||
|
NumberStyleValue::create(transform.elements()[3][1]),
|
||||||
|
NumberStyleValue::create(transform.elements()[0][2]),
|
||||||
|
NumberStyleValue::create(transform.elements()[1][2]),
|
||||||
|
NumberStyleValue::create(transform.elements()[2][2]),
|
||||||
|
NumberStyleValue::create(transform.elements()[3][2]),
|
||||||
|
NumberStyleValue::create(transform.elements()[0][3]),
|
||||||
|
NumberStyleValue::create(transform.elements()[1][3]),
|
||||||
|
NumberStyleValue::create(transform.elements()[2][3]),
|
||||||
|
NumberStyleValue::create(transform.elements()[3][3]),
|
||||||
|
};
|
||||||
|
return TransformationStyleValue::create(TransformFunction::Matrix3d, move(parameters));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -> Any other property
|
// -> Any other property
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue