Skip to content

Commit 50b1cc4

Browse files
dbaronChromium LUCI CQ
authored and
Chromium LUCI CQ
committed
Correct handling of [0px,1px) values of perspective and perspective().
This makes a number of fixes to handling small values of the perspective CSS property and the perspective() transform function to match the css-transforms-2 specification (the latest updates to which come from the resolutions in w3c/csswg-drafts#413): * Accept zero values of the perspective property at parse time. (They were already accepted for the perspective function.) Zero values are currently accepted by Gecko, but it treats them as the identity matrix (that is, as infinite perspective) rather than clamping to 1px. * Use -1.0 rather than 0.0 as the internal representation of perspective: none. * For rendering of both the perspective property and the perspective() transform function, treat values smaller than 1px as 1px. * For interpolation of the perspective() transform function, treat values smaller than 1px as 1px. This is an additional clarification to the resolution that I proposed in w3c/csswg-drafts#6320. * When handling the perspective() transform function when finding the resolved value of the transform property (which is a matrix() or matrix3d() value), treat values smaller than 1px as 1px. (Resolved values are the results of getComputedStyle().) This is an additional clarification that I proposed in w3c/csswg-drafts#6346. Note that interpolation and resolved values of the perspective property since both interpolation and resolved values match the specified values. In the case of interpolation that was resolved specifically in w3c/csswg-drafts#3084. It also substantially simplifies PerspectiveTransformOperation::Blend, although I *believe* the only substantive change is the clamping of its inputs to be 1px or larger. Parts of this are somewhat risky, since previously transform: perspective(0) was treated as the identity matrix and perspective: 0 was a syntax error, whereas this makes both be treated as very substantial transform (perspective from 1px away). The old behavior of transform: perspective(0) was interoperable across browsers. The old behavior of perspective: 0 was different in Gecko (where it was valid syntax, but like transform: perspective(0) was treated as the identity matrix), but the old behaviors across browsers still had in common that they all led to the identity matrix (whether valid or invalid syntax), which is not true of the new behavior. The risk for handling of values in (0px, 1px) is probably less substantial since those were already treated as extreme transforms, and this makes them less extreme. There are thus three possible less-risky alternatives, from more risk (but less than this) to lowest risk: * Use this patch, but omit the changes to perspective: 0 and perspective(0) except for the change that makes perspective: 0 valid, but treat perspective: 0 as an identity transform like Gecko does. * Use this patch, but omit all the changes to perspective: 0px and perspective(0). * Change the behavior only when DBL_TRUE_MIN <= perspective < DBL_MIN, by treating perspective (property or function) as DBL_MIN in those cases. However, it's worth trying this riskier alternative and following the CSS Working Group's decision because that decision was made for good reasons. Taking this approach has two advantages: (1) It eliminates the only case where the valid values of a CSS property are an open range (a range exclusive of its endpoint), which creates difficulties for defining clamping of values to the valid range, which is important to CSS both for calc() and for animations (e.g., when the timing function result is outside of [0, 1]). (2) It eliminates a discontinuity in behavior at zero. Discontinuities in behavior cause animations that cross the discontinuity to behave poorly. Fixed: 1205161 Change-Id: Ie11a3d27d32e6ce16c39d670f6423a6710ba0971 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2924023 Commit-Queue: David Baron Reviewed-by: Xianzhu Wang Reviewed-by: Rune Lillesveen Cr-Commit-Position: refs/heads/master@{#889344}
1 parent ffb37ee commit 50b1cc4

24 files changed

+131
-279
lines changed

third_party/blink/renderer/core/css/css_properties.json5

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3323,7 +3323,7 @@
33233323
interpolable: true,
33243324
field_group: "*",
33253325
field_template: "primitive",
3326-
default_value: "0.0",
3326+
default_value: "-1.0",
33273327
type_name: "float",
33283328
converter: "ConvertPerspective",
33293329
keywords: ["none"],

third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5374,20 +5374,18 @@ const CSSValue* Perspective::ParseSingleValue(
53745374
if (range.Peek().Id() == CSSValueID::kNone)
53755375
return css_parsing_utils::ConsumeIdent(range);
53765376
CSSPrimitiveValue* parsed_value =
5377-
css_parsing_utils::ConsumeLength(range, context, kValueRangeAll);
5377+
css_parsing_utils::ConsumeLength(range, context, kValueRangeNonNegative);
53785378
bool use_legacy_parsing = localContext.UseAliasParsing();
53795379
if (!parsed_value && use_legacy_parsing) {
53805380
double perspective;
5381-
if (!css_parsing_utils::ConsumeNumberRaw(range, context, perspective))
5381+
if (!css_parsing_utils::ConsumeNumberRaw(range, context, perspective) ||
5382+
perspective < 0.0)
53825383
return nullptr;
53835384
context.Count(WebFeature::kUnitlessPerspectiveInPerspectiveProperty);
53845385
parsed_value = CSSNumericLiteralValue::Create(
53855386
perspective, CSSPrimitiveValue::UnitType::kPixels);
53865387
}
5387-
if (parsed_value &&
5388-
(parsed_value->IsCalculated() || parsed_value->GetDoubleValue() > 0))
5389-
return parsed_value;
5390-
return nullptr;
5388+
return parsed_value;
53915389
}
53925390

53935391
const CSSValue* Perspective::CSSValueFromComputedStyleInternal(

third_party/blink/renderer/core/layout/layout_box.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1995,7 +1995,7 @@ bool LayoutBox::MapVisualRectToContainer(
19951995

19961996
TransformationMatrix perspective_matrix;
19971997
perspective_matrix.ApplyPerspective(
1998-
container_object->StyleRef().Perspective());
1998+
container_object->StyleRef().UsedPerspective());
19991999
perspective_matrix.ApplyTransformOrigin(perspective_origin.X(),
20002000
perspective_origin.Y(), 0);
20012001

third_party/blink/renderer/core/layout/layout_object.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3339,7 +3339,7 @@ void LayoutObject::GetTransformFromContainer(
33393339

33403340
TransformationMatrix perspective_matrix;
33413341
perspective_matrix.ApplyPerspective(
3342-
container_object->StyleRef().Perspective());
3342+
container_object->StyleRef().UsedPerspective());
33433343
perspective_matrix.ApplyTransformOrigin(perspective_origin.X(),
33443344
perspective_origin.Y(), 0);
33453345

third_party/blink/renderer/core/paint/paint_property_tree_builder.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1917,7 +1917,7 @@ void FragmentPaintPropertyTreeBuilder::UpdatePerspective() {
19171917
// most transform nodes do.
19181918
TransformPaintPropertyNode::State state{
19191919
TransformPaintPropertyNode::TransformAndOrigin(
1920-
TransformationMatrix().ApplyPerspective(style.Perspective()),
1920+
TransformationMatrix().ApplyPerspective(style.UsedPerspective()),
19211921
PerspectiveOrigin(To(object_)) +
19221922
FloatSize(context_.current.paint_offset))};
19231923
state.flags.flattens_inherited_transform =

third_party/blink/renderer/core/style/computed_style.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_COMPUTED_STYLE_H_
2727
#define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_COMPUTED_STYLE_H_
2828

29+
#include <algorithm>
2930
#include <memory>
3031
#include "base/types/pass_key.h"
3132
#include "third_party/blink/renderer/core/core_export.h"
@@ -1931,7 +1932,12 @@ class ComputedStyle : public ComputedStyleBase,
19311932
}
19321933

19331934
// Perspective utility functions.
1934-
bool HasPerspective() const { return Perspective() > 0; }
1935+
bool HasPerspective() const { return Perspective() >= 0; }
1936+
1937+
float UsedPerspective() const {
1938+
DCHECK(HasPerspective());
1939+
return std::max(1.0f, Perspective());
1940+
}
19351941

19361942
// Outline utility functions.
19371943
// HasOutline is insufficient to determine whether Node has an outline.

third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "third_party/blink/renderer/platform/transforms/perspective_transform_operation.h"
2727

28+
#include <algorithm>
2829
#include "third_party/blink/renderer/platform/geometry/blend.h"
2930
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
3031

@@ -33,18 +34,15 @@ namespace blink {
3334
scoped_refptr PerspectiveTransformOperation::Accumulate(
3435
const TransformOperation& other) {
3536
DCHECK(other.IsSameType(*this));
36-
double other_p = To(other).p_;
37-
38-
if (p_ == 0 && other_p == 0)
39-
return nullptr;
37+
double other_p = To(other).UsedPerspective();
38+
double p = UsedPerspective();
4039

4140
// We want to solve:
4241
// -1/p + -1/p' == -1/p'', where we know p and p'.
4342
//
4443
// This can be rewritten as:
4544
// p'' == (p * p') / (p + p')
46-
double p = (p_ * other_p) / (p_ + other_p);
47-
return PerspectiveTransformOperation::Create(p);
45+
return PerspectiveTransformOperation::Create((p * other_p) / (p + other_p));
4846
}
4947

5048
scoped_refptr PerspectiveTransformOperation::Blend(
@@ -54,34 +52,27 @@ scoped_refptr PerspectiveTransformOperation::Blend(
5452
if (from && !from->IsSameType(*this))
5553
return this;
5654

55+
// https://drafts.csswg.org/css-transforms-2/#interpolation-of-transform-functions
56+
// says that we should run matrix decomposition and then run the rules for
57+
// interpolation of matrices, but we know what those rules are going to
58+
// yield, so just do that directly.
59+
double from_p_inverse, to_p_inverse;
5760
if (blend_to_identity) {
58-
// FIXME: this seems wrong. https://bugs.webkit.org/show_bug.cgi?id=52700
59-
double p = blink::Blend(p_, 1., progress);
60-
return PerspectiveTransformOperation::Create(clampTo<int>(p, 0));
61-
}
62-
63-
const PerspectiveTransformOperation* from_op =
64-
static_cast<const PerspectiveTransformOperation*>(from);
65-
66-
TransformationMatrix from_t;
67-
TransformationMatrix to_t;
68-
from_t.ApplyPerspective(from_op ? from_op->p_ : 0);
69-
to_t.ApplyPerspective(p_);
70-
to_t.Blend(from_t, progress);
71-
72-
TransformationMatrix::DecomposedType decomp;
73-
if (!to_t.Decompose(decomp)) {
74-
// If we can't decompose, bail out of interpolation.
75-
const PerspectiveTransformOperation* used_operation =
76-
progress > 0.5 ? this : from_op;
77-
return PerspectiveTransformOperation::Create(used_operation->Perspective());
78-
}
79-
80-
if (decomp.perspective_z) {
81-
double val = -1.0 / decomp.perspective_z;
82-
return PerspectiveTransformOperation::Create(clampTo<double>(val, 0));
61+
from_p_inverse = 1.0 / UsedPerspective();
62+
to_p_inverse = 0.0;
63+
} else {
64+
if (from) {
65+
const PerspectiveTransformOperation* from_op =
66+
static_cast<const PerspectiveTransformOperation*>(from);
67+
from_p_inverse = 1.0 / from_op->UsedPerspective();
68+
} else {
69+
from_p_inverse = 0.0;
70+
}
71+
to_p_inverse = 1.0 / UsedPerspective();
8372
}
84-
return PerspectiveTransformOperation::Create(0);
73+
double p =
74+
1.0 / std::max(0.0, blink::Blend(from_p_inverse, to_p_inverse, progress));
75+
return PerspectiveTransformOperation::Create(clampTo<double>(p, 0));
8576
}
8677

8778
scoped_refptr PerspectiveTransformOperation::Zoom(

third_party/blink/renderer/platform/transforms/perspective_transform_operation.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_PERSPECTIVE_TRANSFORM_OPERATION_H_
2727
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_PERSPECTIVE_TRANSFORM_OPERATION_H_
2828

29+
#include <algorithm>
2930
#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
3031
#include "third_party/blink/renderer/platform/wtf/casting.h"
3132

@@ -40,6 +41,8 @@ class PLATFORM_EXPORT PerspectiveTransformOperation final
4041

4142
double Perspective() const { return p_; }
4243

44+
double UsedPerspective() const { return std::max(1.0, p_); }
45+
4346
static bool IsMatchingOperationType(OperationType type) {
4447
return type == kPerspective;
4548
}
@@ -56,7 +59,7 @@ class PLATFORM_EXPORT PerspectiveTransformOperation final
5659
}
5760

5861
void Apply(TransformationMatrix& transform, const FloatSize&) const override {
59-
transform.ApplyPerspective(p_);
62+
transform.ApplyPerspective(UsedPerspective());
6063
}
6164

6265
scoped_refptr Accumulate(

third_party/blink/web_tests/TestExpectations

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5446,11 +5446,9 @@ crbug.com/1008483 virtual/transform-interop/transforms/3d/point-mapping/3d-point
54465446

54475447
crbug.com/943503 external/wpt/css/css-transforms/transform3d-perspective-003.html [ Failure ]
54485448
crbug.com/943503 external/wpt/css/css-transforms/transform3d-perspective-004.html [ Failure ]
5449-
crbug.com/943503 external/wpt/css/css-transforms/transform3d-perspective-005.html [ Failure ]
54505449

54515450
crbug.com/943503 virtual/transform-interop/external/wpt/css/css-transforms/transform3d-perspective-003.html [ Pass ]
54525451
crbug.com/943503 virtual/transform-interop/external/wpt/css/css-transforms/transform3d-perspective-004.html [ Pass ]
5453-
crbug.com/943503 virtual/transform-interop/external/wpt/css/css-transforms/transform3d-perspective-005.html [ Pass ]
54545452

54555453
# Swiftshader issue.
54565454
crbug.com/1048149 external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-non-integer-innerwidth.html [ Crash Timeout ]

third_party/blink/web_tests/animations/interpolation/webkit-perspective-interpolation.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from: '50px',
3333
to: '100px'
3434
}, [
35-
{at: -20, is: 'none'}, // perspective does not accept 0 or negative values
35+
{at: -20, is: '0px'},
3636
{at: -0.3, is: '35px'},
3737
{at: 0, is: '50px'},
3838
{at: 0.3, is: '65px'},

third_party/blink/web_tests/animations/responsive/animations-responsive-perspective.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@
2626

2727
player.currentTime = 5;
2828
element.style.fontSize = '40px';
29-
assert_equals(getComputedStyle(element).perspective, 'none');
29+
assert_equals(getComputedStyle(element).perspective, '0px');
3030

3131
player.currentTime = 7.5;
32-
assert_equals(getComputedStyle(element).perspective, 'none');
33-
}, 'perspective clamped to none');
32+
assert_equals(getComputedStyle(element).perspective, '0px');
33+
}, 'perspective clamped to 0px');
3434

3535
test(function() {
3636
container.style.perspective = 'none';

0 commit comments

Comments
 (0)