Bug 1316236 - Treat perspective(0) as perspective(infinity). r=dholbert draft
authorXidorn Quan <me@upsuper.org>
Fri, 20 Jan 2017 12:45:33 +1100
changeset 464810 5060ae56163446a9dd6ae816c548ecd987a6e33e
parent 463578 26f064614428e7552ab0063de71a684671a6a640
child 543000 83ff9dc17e0d63bda3c1a46783e6f1a84da179c2
push id42440
push userxquan@mozilla.com
push dateMon, 23 Jan 2017 00:09:44 +0000
reviewersdholbert
bugs1316236
milestone53.0a1
Bug 1316236 - Treat perspective(0) as perspective(infinity). r=dholbert MozReview-Commit-ID: H9xlpjxrzht
layout/reftests/w3c-css/submitted/transforms/green.html
layout/reftests/w3c-css/submitted/transforms/perspective-zero.html
layout/reftests/w3c-css/submitted/transforms/reftest.list
layout/style/StyleAnimationValue.cpp
layout/style/nsStyleTransformMatrix.cpp
layout/style/nsStyleTransformMatrix.h
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