Bug 1316236 - Treat perspective(0) as perspective(infinity). r=dholbert
MozReview-Commit-ID: H9xlpjxrzht
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/transforms/green.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference: A green box</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<p>Pass if there is NO red below:</p>
+<div id="ref" style="width: 100px; height: 100px; background: green"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/transforms/perspective-zero.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: transform: perspective(0)</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#funcdef-perspective">
+<meta name="assert" content="perspective(0) should behave like identity transform function.">
+<link rel="match" href="green.html">
+<style>
+#cover-me, #test {
+ width: 100px;
+ height: 100px;
+}
+#cover-me {
+ background: red;
+ position: relative;
+ margin-bottom: -100px;
+}
+#test {
+ background: green;
+ /* This should be an identity transform, since perspective(0) must be
+ * treated as perspective(infinity), and consequently translateZ()
+ * doesn't have any effect, so that it covers up #cover-me.
+ * If perspective(0) is invalid, #test would not create a stacking
+ * context, and #cover-me would be placed on top of #test showing red.
+ * If perspective(0) is handled as perspective(epsilon), #test would
+ * be invisible. */
+ transform: perspective(0) translateZ(50px);
+}
+</style>
+<p>Pass if there is NO red below:</p>
+<div id="cover-me"></div><div id="test"></div>
--- a/layout/reftests/w3c-css/submitted/transforms/reftest.list
+++ b/layout/reftests/w3c-css/submitted/transforms/reftest.list
@@ -1,4 +1,5 @@
== transform-containing-block-dynamic-1a.html containing-block-dynamic-1-ref.html
== transform-containing-block-dynamic-1b.html containing-block-dynamic-1-ref.html
== perspective-containing-block-dynamic-1a.html containing-block-dynamic-1-ref.html
== perspective-containing-block-dynamic-1b.html containing-block-dynamic-1-ref.html
+== perspective-zero.html green.html
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -1370,24 +1370,21 @@ ComputeTransformDistance(nsCSSValue::Arr
// Do interpolation between perspective(100px) and perspective(1000px).
// 1) Convert them into matrix3d, and then do matrix decomposition:
// perspective vector 1: perspective(0, 0, -1/100, 1);
// perspective vector 2: perspective(0, 0, -1/1000, 1);
// 2) Do linear interpolation between these two vectors.
// Therefore, we use the same rule to get the distance as what we do for
// matrix3d.
- auto clampPerspectiveDepth = [](float aDepth) {
- // Perspective depth should be positive non-zero value.
- return std::max(aDepth, std::numeric_limits<float>::epsilon());
- };
+ using nsStyleTransformMatrix::ApplyPerspectiveToMatrix;
Matrix4x4 m1;
- m1.Perspective(clampPerspectiveDepth(a1->Item(1).GetFloatValue()));
+ ApplyPerspectiveToMatrix(m1, a1->Item(1).GetFloatValue());
Matrix4x4 m2;
- m2.Perspective(clampPerspectiveDepth(a2->Item(1).GetFloatValue()));
+ ApplyPerspectiveToMatrix(m2, a2->Item(1).GetFloatValue());
distance = ComputeTransform3DMatrixDistance(m1, m2);
break;
}
case eCSSKeyword_matrix: {
MOZ_ASSERT(a1->Count() == 7, "unexpected count");
MOZ_ASSERT(a2->Count() == 7, "unexpected count");
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -13,18 +13,16 @@
#include "nsPresContext.h"
#include "nsRuleNode.h"
#include "nsSVGUtils.h"
#include "nsCSSKeywords.h"
#include "mozilla/StyleAnimationValue.h"
#include "gfxMatrix.h"
#include "gfxQuaternion.h"
-#include <limits>
-
using namespace mozilla;
using namespace mozilla::gfx;
namespace nsStyleTransformMatrix {
/* Note on floating point precision: The transform matrix is an array
* of single precision 'float's, and so are most of the input values
* we get from the style system, but intermediate calculations
@@ -746,21 +744,19 @@ static void
ProcessPerspective(Matrix4x4& aMatrix,
const nsCSSValue::Array* aData,
nsStyleContext *aContext,
nsPresContext *aPresContext,
RuleNodeCacheConditions& aConditions)
{
NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
- float depth = std::max(ProcessTranslatePart(aData->Item(1), aContext,
- aPresContext, aConditions,
- nullptr),
- std::numeric_limits<float>::epsilon());
- aMatrix.Perspective(depth);
+ float depth = ProcessTranslatePart(aData->Item(1), aContext,
+ aPresContext, aConditions, nullptr);
+ ApplyPerspectiveToMatrix(aMatrix, depth);
}
/**
* SetToTransformFunction is essentially a giant switch statement that fans
* out to many smaller helper functions.
*/
static void
--- a/layout/style/nsStyleTransformMatrix.h
+++ b/layout/style/nsStyleTransformMatrix.h
@@ -8,30 +8,43 @@
*/
#ifndef nsStyleTransformMatrix_h_
#define nsStyleTransformMatrix_h_
#include "mozilla/EnumeratedArray.h"
#include "nsCSSValue.h"
+#include <limits>
+
class nsIFrame;
class nsStyleContext;
class nsPresContext;
struct gfxQuaternion;
struct nsRect;
namespace mozilla {
class RuleNodeCacheConditions;
} // namespace mozilla
/**
* A helper to generate gfxMatrixes from css transform functions.
*/
namespace nsStyleTransformMatrix {
+ // Function for applying perspective() transform function. We treat
+ // any value smaller than epsilon as perspective(infinity), which
+ // follows CSSWG's resolution on perspective(0). See bug 1316236.
+ inline void ApplyPerspectiveToMatrix(mozilla::gfx::Matrix4x4& aMatrix,
+ float aDepth)
+ {
+ if (aDepth >= std::numeric_limits<float>::epsilon()) {
+ aMatrix.Perspective(aDepth);
+ }
+ }
+
/**
* This class provides on-demand access to the 'reference box' for CSS
* transforms (needed to resolve percentage values in 'transform',
* 'transform-origin', etc.):
*
* http://dev.w3.org/csswg/css-transforms/#reference-box
*
* This class helps us to avoid calculating the reference box unless and