mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
layout: Fix logic for transforms with non-invertible matrix (#37147)
When the the current transformation matrix of a box isn't invertible, the box and its content shouldn't be displayed. However, the logic was broken: - It was only checking the `transform` property, but not individual transform properties like `scale`. - It was treating matrices with m₁₁=0 or m₂₂=0 and non-invertible, even when they can still be invertible and have a visible outcome. - When m₁₁=0 or m₂₂=0 weren't caused by `transform`, it was replacing the matrix with the identity. Testing: Adding new WPT Fixes: #37146 Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
fef1dd9ea0
commit
ac06b1cfcf
4 changed files with 114 additions and 24 deletions
|
@ -834,16 +834,6 @@ impl Fragment {
|
||||||
_ => text_decorations,
|
_ => text_decorations,
|
||||||
};
|
};
|
||||||
|
|
||||||
// If this fragment has a transform applied that makes it take up no space
|
|
||||||
// then we don't need to create any stacking contexts for it.
|
|
||||||
let has_non_invertible_transform = fragment
|
|
||||||
.has_non_invertible_transform_or_zero_scale(
|
|
||||||
&containing_block.rect.to_untyped(),
|
|
||||||
);
|
|
||||||
if has_non_invertible_transform {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment.build_stacking_context_tree(
|
fragment.build_stacking_context_tree(
|
||||||
fragment_clone,
|
fragment_clone,
|
||||||
stacking_context_tree,
|
stacking_context_tree,
|
||||||
|
@ -991,6 +981,13 @@ impl BoxFragment {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// <https://drafts.csswg.org/css-transforms/#transform-function-lists>
|
||||||
|
// > If a transform function causes the current transformation matrix of an object
|
||||||
|
// > to be non-invertible, the object and its content do not get displayed.
|
||||||
|
if !reference_frame_data.transform.is_invertible() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let new_spatial_id = stacking_context_tree.push_reference_frame(
|
let new_spatial_id = stacking_context_tree.push_reference_frame(
|
||||||
reference_frame_data.origin.to_webrender(),
|
reference_frame_data.origin.to_webrender(),
|
||||||
&containing_block.scroll_node_id,
|
&containing_block.scroll_node_id,
|
||||||
|
@ -1622,15 +1619,6 @@ impl BoxFragment {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the given style contains a transform that is not invertible.
|
|
||||||
fn has_non_invertible_transform_or_zero_scale(&self, containing_block: &Rect<Au>) -> bool {
|
|
||||||
let list = &self.style.get_box().transform;
|
|
||||||
match list.to_transform_3d_matrix(Some(&au_rect_to_length_rect(containing_block))) {
|
|
||||||
Ok(t) => !t.0.is_invertible() || t.0.m11 == 0. || t.0.m22 == 0.,
|
|
||||||
Err(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the 4D matrix representing this fragment's transform.
|
/// Returns the 4D matrix representing this fragment's transform.
|
||||||
pub fn calculate_transform_matrix(&self, border_rect: &Rect<Au>) -> Option<LayoutTransform> {
|
pub fn calculate_transform_matrix(&self, border_rect: &Rect<Au>) -> Option<LayoutTransform> {
|
||||||
let list = &self.style.get_box().transform;
|
let list = &self.style.get_box().transform;
|
||||||
|
@ -1660,11 +1648,6 @@ impl BoxFragment {
|
||||||
.then_rotate(rotate.0, rotate.1, rotate.2, angle)
|
.then_rotate(rotate.0, rotate.1, rotate.2, angle)
|
||||||
.then_scale(scale.0, scale.1, scale.2)
|
.then_scale(scale.0, scale.1, scale.2)
|
||||||
.then(&translation);
|
.then(&translation);
|
||||||
// WebRender will end up dividing by the scale value of this transform, so we
|
|
||||||
// want to ensure we don't feed it a divisor of 0.
|
|
||||||
if transform.m11 == 0. || transform.m22 == 0. {
|
|
||||||
return Some(LayoutTransform::identity());
|
|
||||||
}
|
|
||||||
|
|
||||||
let transform_origin = &self.style.get_box().transform_origin;
|
let transform_origin = &self.style.get_box().transform_origin;
|
||||||
let transform_origin_x = transform_origin
|
let transform_origin_x = transform_origin
|
||||||
|
|
26
tests/wpt/meta/MANIFEST.json
vendored
26
tests/wpt/meta/MANIFEST.json
vendored
|
@ -279684,6 +279684,19 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"individual-transform-3.html": [
|
||||||
|
"2c81cb12156134ee8deca6abf0043848ac8bc750",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"/css/reference/ref-filled-green-200px-square.html",
|
||||||
|
"=="
|
||||||
|
]
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"stacking-context-001.html": [
|
"stacking-context-001.html": [
|
||||||
"1a754c5d70b607396ffe2db1d04f2294aeca1041",
|
"1a754c5d70b607396ffe2db1d04f2294aeca1041",
|
||||||
[
|
[
|
||||||
|
@ -284738,6 +284751,19 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"transform-matrix-009.html": [
|
||||||
|
"81f04db0d103202180aba2625205643c376483af",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"/css/reference/ref-filled-green-200px-square.html",
|
||||||
|
"=="
|
||||||
|
]
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"transform-origin": {
|
"transform-origin": {
|
||||||
"svg-origin-length-001.html": [
|
"svg-origin-length-001.html": [
|
||||||
"2f2256361cadd4f69aa08fd775a4305219a41fdd",
|
"2f2256361cadd4f69aa08fd775a4305219a41fdd",
|
||||||
|
|
40
tests/wpt/tests/css/css-transforms/individual-transform/individual-transform-3.html
vendored
Normal file
40
tests/wpt/tests/css/css-transforms/individual-transform/individual-transform-3.html
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Individual transform: non-invertible matrix</title>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#individual-transforms">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#ctm">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-transforms-1/#transform-function-lists">
|
||||||
|
<link rel="help" href="https://github.com/servo/servo/issues/37146">
|
||||||
|
<link rel="match" href="../../reference/ref-filled-green-200px-square.html">
|
||||||
|
<meta name="assert" content="Tests that the element isn't rendered when
|
||||||
|
the current transformation matrix is non-invertible because of `scale`.">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
background: green;
|
||||||
|
}
|
||||||
|
.wrapper > div {
|
||||||
|
width: 200px;
|
||||||
|
height: 20px;
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
<div style="scale: 0"></div>
|
||||||
|
<div style="scale: 0 0"></div>
|
||||||
|
<div style="scale: 0 1"></div>
|
||||||
|
<div style="scale: 1 0"></div>
|
||||||
|
<div style="scale: 0 0 0"></div>
|
||||||
|
<div style="scale: 0 0 1"></div>
|
||||||
|
<div style="scale: 0 1 0"></div>
|
||||||
|
<div style="scale: 0 1 1"></div>
|
||||||
|
<div style="scale: 1 0 0"></div>
|
||||||
|
<div style="scale: 1 0 1"></div>
|
||||||
|
<div style="scale: 1 1 0"></div>
|
||||||
|
</div>
|
41
tests/wpt/tests/css/css-transforms/transform-matrix-009.html
vendored
Normal file
41
tests/wpt/tests/css/css-transforms/transform-matrix-009.html
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>CSS Test (Transforms): matrix() with zeros in the diagonal</title>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
|
||||||
|
<link rel="help" href="https://www.w3.org/TR/css-transforms-1/#funcdef-transform-matrix">
|
||||||
|
<link rel="help" href="https://www.w3.org/TR/css-transforms-1/#transform-function-lists">
|
||||||
|
<link rel="help" href="https://github.com/servo/servo/issues/37146">
|
||||||
|
<link rel="match" href="../reference/ref-filled-green-200px-square.html">
|
||||||
|
<meta name="assert" content="Tests that the element is still rendered,
|
||||||
|
even though the current transformation matrix has zeros in the diagonal,
|
||||||
|
as long as the matrix remains invertible.">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
.test {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
background: green;
|
||||||
|
transform: matrix(0,1, 1,0, 0,0);
|
||||||
|
/*
|
||||||
|
The resulting matrix is:
|
||||||
|
┌ ┐
|
||||||
|
│ 0 1 0 0 │
|
||||||
|
│ 1 0 0 0 │
|
||||||
|
│ 0 0 1 0 │
|
||||||
|
│ 0 0 0 1 │
|
||||||
|
└ ┘
|
||||||
|
It could result from e.g. `scaleX(-1) rotate(90deg)`.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="test"></div>
|
||||||
|
</div>
|
Loading…
Add table
Add a link
Reference in a new issue