mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 06:32:44 +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
 | ||||
|         //    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<Painting::ViewportPaintable&>(*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 <transform-list>, the resolved value is one <matrix()> function or one <matrix3d()> 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 <transform-function>s in <transform-list> to transform.
 | ||||
|         VERIFY(layout_node.paintable()); | ||||
|         auto const& paintable_box = verify_cast<Painting::PaintableBox const>(layout_node.paintable()); | ||||
|         VERIFY(paintable_box->stacking_context()); | ||||
|         auto const& paintable_box = verify_cast<Painting::PaintableBox const>(*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<StyleValue> 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 <matrix()> or <matrix3d()> serialization:
 | ||||
|         // -> 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
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Atkins
						Sam Atkins