Add support for calc() expressions in translate functions of -moz-transform. (Bug 531344) r=bzbarsky
authorL. David Baron <dbaron@dbaron.org>
Fri, 02 Jul 2010 21:18:56 -0700
changeset 47193 aa23d71ab2305d7d1b41b0a385752034a696f8fa
parent 47192 15e9cd490443e35b24ba2a1eec2abe2b9dd2475f
child 47194 e419e47c09bfaef6ef8cc0a3d8778cef746983d3
push id14265
push userdbaron@mozilla.com
push dateSat, 03 Jul 2010 04:19:19 +0000
treeherdermozilla-central@f2b02ba56bdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs531344
milestone2.0b2pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Add support for calc() expressions in translate functions of -moz-transform. (Bug 531344) r=bzbarsky
layout/style/nsCSSParser.cpp
layout/style/nsStyleTransformMatrix.cpp
layout/style/test/property_database.js
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -149,16 +149,18 @@
 #define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO)
 #define VARIANT_HPN  (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER)
 #define VARIANT_HN   (VARIANT_INHERIT | VARIANT_NUMBER)
 #define VARIANT_HON  (VARIANT_HN | VARIANT_NONE)
 #define VARIANT_HOS  (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
 #define VARIANT_TIMING_FUNCTION (VARIANT_KEYWORD | VARIANT_CUBIC_BEZIER)
 #define VARIANT_UK   (VARIANT_URL | VARIANT_KEYWORD)
 #define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE)
+#define VARIANT_TRANSFORM_LPCALC (VARIANT_LP | VARIANT_CALC | \
+                                  VARIANT_CALC_NO_MIN_MAX)
 
 //----------------------------------------------------------------------
 
 namespace {
 
 // Rule processing function
 typedef void (* RuleAppendFunc) (nsICSSRule* aRule, void* aData);
 static void AppendRuleToArray(nsICSSRule* aRule, void* aArray);
@@ -8091,52 +8093,47 @@ static PRBool GetFunctionParseInformatio
                                           PRUint16 &aMinElems,
                                           PRUint16 &aMaxElems,
                                           const PRInt32 *& aVariantMask)
 {
 /* These types represent the common variant masks that will be used to
    * parse out the individual functions.  The order in the enumeration
    * must match the order in which the masks are declared.
    */
-  enum { eLengthPercent,
-         eTwoLengthPercents,
+  enum { eLengthPercentCalc,
+         eTwoLengthPercentCalcs,
          eAngle,
          eTwoAngles,
          eNumber,
          eTwoNumbers,
          eMatrix,
          eNumVariantMasks };
   static const PRInt32 kMaxElemsPerFunction = 6;
   static const PRInt32 kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
-    {VARIANT_LENGTH | VARIANT_PERCENT},
-    {VARIANT_LENGTH | VARIANT_PERCENT, VARIANT_LENGTH | VARIANT_PERCENT},
+    {VARIANT_TRANSFORM_LPCALC},
+    {VARIANT_TRANSFORM_LPCALC, VARIANT_TRANSFORM_LPCALC},
     {VARIANT_ANGLE_OR_ZERO},
     {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
     {VARIANT_NUMBER},
     {VARIANT_NUMBER, VARIANT_NUMBER},
     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
-     VARIANT_LENGTH | VARIANT_PERCENT, VARIANT_LENGTH | VARIANT_PERCENT}};
+     VARIANT_TRANSFORM_LPCALC, VARIANT_TRANSFORM_LPCALC}};
 
 #ifdef DEBUG
   static const PRUint8 kVariantMaskLengths[eNumVariantMasks] =
     {1, 2, 1, 2, 1, 2, 6};
 #endif
 
   PRInt32 variantIndex = eNumVariantMasks;
 
   switch (aToken) {
   case eCSSKeyword_translatex:
-    /* Exactly one length or percent. */
-    variantIndex = eLengthPercent;
-    aMinElems = 1U;
-    aMaxElems = 1U;
-    break;
   case eCSSKeyword_translatey:
     /* Exactly one length or percent. */
-    variantIndex = eLengthPercent;
+    variantIndex = eLengthPercentCalc;
     aMinElems = 1U;
     aMaxElems = 1U;
     break;
   case eCSSKeyword_scalex:
     /* Exactly one scale factor. */
     variantIndex = eNumber;
     aMinElems = 1U;
     aMaxElems = 1U;
@@ -8150,17 +8147,17 @@ static PRBool GetFunctionParseInformatio
   case eCSSKeyword_rotate:
     /* Exactly one angle. */
     variantIndex = eAngle;
     aMinElems = 1U;
     aMaxElems = 1U;
     break;
   case eCSSKeyword_translate:
     /* One or two lengths or percents. */
-    variantIndex = eTwoLengthPercents;
+    variantIndex = eTwoLengthPercentCalcs;
     aMinElems = 1U;
     aMaxElems = 2U;
     break;
   case eCSSKeyword_skew:
     /* Exactly one or two angles. */
     variantIndex = eTwoAngles;
     aMinElems = 1U;
     aMaxElems = 2U;
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -41,16 +41,19 @@
 #include "nsStyleTransformMatrix.h"
 #include "nsAutoPtr.h"
 #include "nsCSSValue.h"
 #include "nsStyleContext.h"
 #include "nsPresContext.h"
 #include "nsRuleNode.h"
 #include "nsCSSKeywords.h"
 #include "nsMathUtils.h"
+#include "CSSCalc.h"
+
+namespace css = mozilla::css;
 
 /* 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
  * involving angles need to be done in 'double'.
  */
 
 /* Force small values to zero.  We do this to avoid having sin(360deg)
@@ -247,24 +250,105 @@ static void ProcessMatrix(float aMain[4]
    */
   if (aData->Item(6).GetUnit() == eCSSUnit_Percent)
     aY[1] = aData->Item(6).GetPercentValue();
   else
     aDelta[1] = CalcLength(aData->Item(6), aContext, aPresContext,
                            aCanStoreInRuleTree);
 }
 
+struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps
+{
+  struct result_type {
+    nscoord mLength;
+    float mPercent;
+
+    result_type(nscoord aLength, float aPercent)
+      : mLength(aLength), mPercent(aPercent) {}
+  };
+
+  LengthPercentPairCalcOps(nsStyleContext* aContext,
+                           nsPresContext* aPresContext,
+                           PRBool& aCanStoreInRuleTree)
+    : mContext(aContext),
+      mPresContext(aPresContext),
+      mCanStoreInRuleTree(aCanStoreInRuleTree) {}
+
+  nsStyleContext* mContext;
+  nsPresContext* mPresContext;
+  PRBool& mCanStoreInRuleTree;
+
+  result_type ComputeLeafValue(const nsCSSValue& aValue)
+  {
+    if (aValue.GetUnit() == eCSSUnit_Percent) {
+      return result_type(0, aValue.GetPercentValue());
+    } else {
+      return result_type(CalcLength(aValue, mContext, mPresContext,
+                                    mCanStoreInRuleTree),
+                         0.0f);
+    }
+  }
+
+  result_type
+  MergeAdditive(nsCSSUnit aCalcFunction,
+                result_type aValue1, result_type aValue2)
+  {
+    if (aCalcFunction == eCSSUnit_Calc_Plus) {
+      return result_type(NSCoordSaturatingAdd(aValue1.mLength,
+                                              aValue2.mLength),
+                         aValue1.mPercent + aValue2.mPercent);
+    }
+    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
+                      "min() and max() are not allowed in calc() on "
+                      "transform");
+    return result_type(NSCoordSaturatingSubtract(aValue1.mLength,
+                                                 aValue2.mLength, 0),
+                       aValue1.mPercent - aValue2.mPercent);
+  }
+
+  result_type
+  MergeMultiplicativeL(nsCSSUnit aCalcFunction,
+                       float aValue1, result_type aValue2)
+  {
+    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
+                      "unexpected unit");
+    return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1),
+                       aValue1 * aValue2.mPercent);
+  }
+
+  result_type
+  MergeMultiplicativeR(nsCSSUnit aCalcFunction,
+                       result_type aValue1, float aValue2)
+  {
+    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R ||
+                      aCalcFunction == eCSSUnit_Calc_Divided,
+                      "unexpected unit");
+    if (aCalcFunction == eCSSUnit_Calc_Divided) {
+      aValue2 = 1.0f / aValue2;
+    }
+    return result_type(NSCoordSaturatingMultiply(aValue1.mLength, aValue2),
+                       aValue1.mPercent * aValue2);
+  }
+
+};
+
 static void ProcessTranslatePart(nscoord& aOffset, float& aPercent,
                                  const nsCSSValue& aValue,
                                  nsStyleContext* aContext,
                                  nsPresContext* aPresContext,
                                  PRBool& aCanStoreInRuleTree)
 {
   if (aValue.GetUnit() == eCSSUnit_Percent) {
     aPercent = aValue.GetPercentValue();
+  } else if (aValue.IsCalcUnit()) {
+    LengthPercentPairCalcOps ops(aContext, aPresContext, aCanStoreInRuleTree);
+    LengthPercentPairCalcOps::result_type result =
+      css::ComputeCalc(aValue, ops);
+    aPercent = result.mPercent;
+    aOffset = result.mLength;
   } else {
     aOffset = CalcLength(aValue, aContext, aPresContext,
                          aCanStoreInRuleTree);
   }
 }
 
 /* Helper function to process a translatex function. */
 static void ProcessTranslateX(nscoord aDelta[2], float aX[2],
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -682,18 +682,33 @@ var gCSSProperties = {
 		other_values: [ "0", "3", "99", "12000" ],
 		invalid_values: [ "-1", "-808", "3.0", "17.5" ]
 	},
 	"-moz-transform": {
 		domProp: "MozTransform",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none" ],
-		other_values: [ "translatex(1px)", "translatex(4em)", "translatex(-4px)", "translatex(3px)", "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)", "translatey(4em)", "translate(3px)", "translate(10px, -3px)", "rotate(45deg)", "rotate(45grad)", "rotate(45rad)", "rotate(0)", "scalex(10)", "scaley(10)", "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)", "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "matrix(1, 2, 3, 4, 5px, 6em)", "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)", "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)", "translatex(50%)", "translatey(50%)", "translate(50%)", "translate(3%, 5px)", "translate(5px, 3%)", "matrix(1, 2, 3, 4, 5px, 6%)", "matrix(1, 2, 3, 4, 5%, 6px)", "matrix(1, 2, 3, 4, 5%, 6%)"],
-		invalid_values: ["1px", "#0000ff", "red", "auto", "translatex(1px 1px)", "translatex(translatex(1px))", "translatex(#0000ff)", "translatex(red)", "translatey()", "matrix(1, 2, 3, 4, 5, 6)", "matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)", "skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)", "matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)", "matrix(0, 1, 2, 3%, 4%, 5%)"]
+		other_values: [ "translatex(1px)", "translatex(4em)", "translatex(-4px)", "translatex(3px)", "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)", "translatey(4em)", "translate(3px)", "translate(10px, -3px)", "rotate(45deg)", "rotate(45grad)", "rotate(45rad)", "rotate(0)", "scalex(10)", "scaley(10)", "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)", "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "matrix(1, 2, 3, 4, 5px, 6em)", "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)", "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)", "translatex(50%)", "translatey(50%)", "translate(50%)", "translate(3%, 5px)", "translate(5px, 3%)", "matrix(1, 2, 3, 4, 5px, 6%)", "matrix(1, 2, 3, 4, 5%, 6px)", "matrix(1, 2, 3, 4, 5%, 6%)",
+			/* valid calc() values */
+			"translatex(-moz-calc(5px + 10%))",
+			"translatey(-moz-calc(0.25 * 5px + 10% / 3))",
+			"translate(-moz-calc(5px - 10% * 3))",
+			"translate(-moz-calc(5px - 3 * 10%), 50px)",
+			"translate(-50px, -moz-calc(5px - 10% * 3))",
+			"matrix(1, 0, 0, 1, -moz-calc(5px * 3), -moz-calc(10% - 3px))"
+		],
+		invalid_values: ["1px", "#0000ff", "red", "auto", "translatex(1px 1px)", "translatex(translatex(1px))", "translatex(#0000ff)", "translatex(red)", "translatey()", "matrix(1, 2, 3, 4, 5, 6)", "matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)", "skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)", "matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)", "matrix(0, 1, 2, 3%, 4%, 5%)",
+			/* invalid (specially for this) calc() values */
+			"translatey(-moz-min(5px,10%))",
+			"translatex(-moz-max(5px,10%))",
+			"translate(10px, -moz-calc(min(5px,10%)))",
+			"translate(-moz-calc(max(5px,10%)), 10%)",
+			"matrix(1, 0, 0, 1, -moz-max(5px * 3), -moz-calc(10% - 3px))"
+		]
 	},
 	"-moz-transform-origin": {
 		domProp: "MozTransformOrigin",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		/* no subproperties */
 		prerequisites: { "width": "10px", "height": "10px", "display": "block"},
 		initial_values: [ "50% 50%", "center", "center center" ],