diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-transform.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-transform.txt new file mode 100644 index 0000000000..d520b6ca2d --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-transform.txt @@ -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) diff --git a/Tests/LibWeb/Text/input/css/getComputedStyle-transform.html b/Tests/LibWeb/Text/input/css/getComputedStyle-transform.html new file mode 100644 index 0000000000..3418ba45db --- /dev/null +++ b/Tests/LibWeb/Text/input/css/getComputedStyle-transform.html @@ -0,0 +1,35 @@ + + diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp index 3f99653111..8ddda3d302 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp @@ -340,41 +340,85 @@ RefPtr ResolvedCSSStyleDeclaration::style_value_for_property(L // -> A resolved value special case property defined in another specification // As defined in the relevant specification. 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(); if (transformations.is_empty()) return IdentifierStyleValue::create(ValueID::None); - // The transform matrix is held by the StackingContext, so we need to make sure we have one first. - auto const* viewport = layout_node.document().paintable(); - VERIFY(viewport); - const_cast(*viewport).build_stacking_context_tree_if_needed(); + // https://drafts.csswg.org/css-transforms-2/#serialization-of-the-computed-value + // The transform property is a resolved value special case property. [CSSOM] + // When the computed value is a , the resolved value is one function or one function computed by the following algorithm: + // 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 s in to transform. VERIFY(layout_node.paintable()); - auto const& paintable_box = verify_cast(layout_node.paintable()); - VERIFY(paintable_box->stacking_context()); + auto const& paintable_box = verify_cast(*layout_node.paintable()); + 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://w3c.github.io/csswg-drafts/css-transforms-2/#serialization-of-the-computed-value - auto affine_matrix = paintable_box->stacking_context()->affine_transform_matrix(); + // https://drafts.csswg.org/css-transforms-1/#2d-matrix + auto is_2d_matrix = [](Gfx::FloatMatrix4x4 const& matrix) -> bool { + // 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; - parameters.ensure_capacity(6); - parameters.unchecked_append(NumberStyleValue::create(affine_matrix.a())); - 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())); + if (matrix.elements()[2][2] != 1 // m33 + || matrix.elements()[3][3] != 1) // m44 + return false; - NonnullRefPtr matrix_function = TransformationStyleValue::create(TransformFunction::Matrix, move(parameters)); - // 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 }; - return StyleValueList::create(move(matrix_functions), StyleValueList::Separator::Space); + return true; + }; + + // 3. Chose between or serialization: + // -> If transform is a 2D matrix + // Serialize transform to a 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 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