Bug 505115 - Part 12a - Implement -moz-perspective-origin style property. r=dbaron
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 03 Aug 2011 15:04:24 +1200
changeset 73742 2d3b6382054d41f104cab872860644a59cb07ed2
parent 73741 ad334152010a5d6f8f602d38a398ac7c4ec0f0c2
child 73743 166f4f247772f60a74f438277508211390333e6a
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersdbaron
bugs505115
milestone8.0a1
Bug 505115 - Part 12a - Implement -moz-perspective-origin style property. r=dbaron
dom/interfaces/css/nsIDOMCSS2Properties.idl
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleAnimation.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/style/test/test_transitions_per_property.html
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl
@@ -46,17 +46,17 @@
  * The nsIDOMCSS2Properties interface is a datatype for additional
  * reflection of data already provided in nsIDOMCSSStyleDeclaration in
  * the Document Object Model.
  *
  * For more information on this interface please see
  * http://www.w3.org/TR/DOM-Level-2-Style
  */
 
-[scriptable, uuid(63a0afd0-b322-11e0-aff2-0800200c9a66)]
+[scriptable, uuid(10f43750-b379-11e0-aff2-0800200c9a66)]
 interface nsIDOMCSS2Properties : nsISupports
 {
            attribute DOMString        background;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundAttachment;
                                         // raises(DOMException) on setting
 
@@ -679,16 +679,19 @@ interface nsIDOMCSS2Properties : nsISupp
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTransformOrigin;
                                         // raises(DOMException) on setting 
 
            attribute DOMString        MozPerspective;
                                         // raises(DOMException) on setting
 
+           attribute DOMString        MozPerspectiveOrigin;
+                                        // raises(DOMException) on setting
+
            attribute DOMString        MozBackfaceVisibility;
                                         // raises(DOMException) on setting 
 
            attribute DOMString        MozWindowShadow;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundSize;
                                         // raises(DOMException) on setting
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -591,18 +591,18 @@ protected:
   PRBool ParseFunction(const nsString &aFunction, const PRInt32 aAllowedTypes[],
                        PRUint16 aMinElems, PRUint16 aMaxElems,
                        nsCSSValue &aValue);
   PRBool ParseFunctionInternals(const PRInt32 aVariantMask[],
                                 PRUint16 aMinElems,
                                 PRUint16 aMaxElems,
                                 nsTArray<nsCSSValue>& aOutput);
 
-  /* Functions for -moz-transform-origin Parsing */
-  PRBool ParseMozTransformOrigin();
+  /* Functions for -moz-transform-origin/-moz-perspective-origin Parsing */
+  PRBool ParseMozTransformOrigin(PRBool aPerspective);
 
   /* Find and return the namespace ID associated with aPrefix.
      If aPrefix has not been declared in an @namespace rule, returns
      kNameSpaceID_Unknown and sets mFoundUnresolvablePrefix to true. */
   PRInt32 GetNamespaceIdForPrefix(const nsString& aPrefix);
 
   /* Find the correct default namespace, and set it on aSelector. */
   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
@@ -5543,17 +5543,19 @@ CSSParserImpl::ParsePropertyByFunction(n
     return ParseQuotes();
   case eCSSProperty_size:
     return ParseSize();
   case eCSSProperty_text_decoration:
     return ParseTextDecoration();
   case eCSSProperty__moz_transform:
     return ParseMozTransform();
   case eCSSProperty__moz_transform_origin:
-    return ParseMozTransformOrigin();
+    return ParseMozTransformOrigin(PR_FALSE);
+  case eCSSProperty_perspective_origin:
+    return ParseMozTransformOrigin(PR_TRUE);
   case eCSSProperty_transition:
     return ParseTransition();
   case eCSSProperty_animation:
     return ParseAnimation();
   case eCSSProperty_transition_property:
     return ParseTransitionProperty();
   case eCSSProperty_fill:
   case eCSSProperty_stroke:
@@ -7497,40 +7499,52 @@ PRBool CSSParserImpl::ParseMozTransform(
       cur->mNext = new nsCSSValueList;
       cur = cur->mNext;
     }
   }
   AppendValue(eCSSProperty__moz_transform, value);
   return PR_TRUE;
 }
 
-PRBool CSSParserImpl::ParseMozTransformOrigin()
+PRBool CSSParserImpl::ParseMozTransformOrigin(PRBool aPerspective)
 {
   nsCSSValuePair position;
-  nsCSSValue depth;
   if (!ParseBoxPositionValues(position, PR_TRUE))
     return PR_FALSE;
 
+  nsCSSProperty prop = eCSSProperty__moz_transform_origin;
+  if (aPerspective) {
+    if (!ExpectEndProperty()) {
+      return PR_FALSE;
+    }
+    prop = eCSSProperty_perspective_origin;
+  }
+
   // Unlike many other uses of pairs, this position should always be stored
   // as a pair, even if the values are the same, so it always serializes as
   // a pair, and to keep the computation code simple.
   if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
       position.mXValue.GetUnit() == eCSSUnit_Initial) {
     NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
                       "inherit/initial only half?");
-    AppendValue(eCSSProperty__moz_transform_origin, position.mXValue);
+    AppendValue(prop, position.mXValue);
   } else {
-    if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nsnull) || 
-        !nsLayoutUtils::Are3DTransformsEnabled()) {
-      depth.Reset();
-    }
-
-    nsCSSValue triplet;
-    triplet.SetTripletValue(position.mXValue, position.mYValue, depth);
-    AppendValue(eCSSProperty__moz_transform_origin, triplet);
+    nsCSSValue value;
+    if (aPerspective) {
+      value.SetPairValue(position.mXValue, position.mYValue);
+    } else {
+      nsCSSValue depth;
+      if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nsnull) ||
+          !nsLayoutUtils::Are3DTransformsEnabled()) {
+        depth.Reset();
+      }
+      value.SetTripletValue(position.mXValue, position.mYValue, depth);
+    }
+
+    AppendValue(prop, value);
   }
   return PR_TRUE;
 }
 
 PRBool
 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
 {
   if (!GetToken(PR_TRUE))
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2254,16 +2254,26 @@ CSS_PROP_DISPLAY(
     CSS_PROP_DOMPROP_PREFIXED(TransformOrigin),
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_STORES_CALC,
     0,
     kBackgroundPositionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_Custom)
 CSS_PROP_DISPLAY(
+    -moz-perspective-origin,
+    perspective_origin,
+    CSS_PROP_DOMPROP_PREFIXED(PerspectiveOrigin),
+    CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_STORES_CALC,
+    0,
+    kBackgroundPositionKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_Custom)
+CSS_PROP_DISPLAY(
     -moz-perspective,
     perspective,
     CSS_PROP_DOMPROP_PREFIXED(Perspective),
     CSS_PROPERTY_PARSE_VALUE,
     VARIANT_NONE | VARIANT_INHERIT | VARIANT_LENGTH,
     nsnull,
     offsetof(nsStyleDisplay, mChildPerspective),
     eStyleAnimType_Coord)
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -944,16 +944,45 @@ nsComputedDOMStyle::DoGetMozTransformOri
     SetValueToCoord(depth, display->mTransformOrigin[2], PR_FALSE,
                     nsnull);
     valueList->AppendCSSValue(depth);
   }
 
   return valueList;
 }
 
+/* Convert the stored representation into a list of two values and then hand
+ * it back.
+ */
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetMozPerspectiveOrigin()
+{
+  /* We need to build up a list of two values.  We'll call them
+   * width and height.
+   */
+
+  /* Store things as a value list */
+  nsDOMCSSValueList* valueList = GetROCSSValueList(PR_FALSE);
+
+  /* Now, get the values. */
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsROCSSPrimitiveValue* width = GetROCSSPrimitiveValue();
+  SetValueToCoord(width, display->mPerspectiveOrigin[0], PR_FALSE,
+                  &nsComputedDOMStyle::GetFrameBoundsWidthForTransform);
+  valueList->AppendCSSValue(width);
+
+  nsROCSSPrimitiveValue* height = GetROCSSPrimitiveValue();
+  SetValueToCoord(height, display->mPerspectiveOrigin[1], PR_FALSE,
+                  &nsComputedDOMStyle::GetFrameBoundsHeightForTransform);
+  valueList->AppendCSSValue(height);
+
+  return valueList;
+}
+
 nsIDOMCSSValue*
 nsComputedDOMStyle::DoGetMozPerspective()
 {
     nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
     if (GetStyleDisplay()->mChildPerspective.GetUnit() == eStyleUnit_Coord &&
         GetStyleDisplay()->mChildPerspective.GetCoordValue() == 0.0) {
         val->SetIdent(eCSSKeyword_none);
     } else {
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -344,16 +344,17 @@ private:
   nsIDOMCSSValue* DoGetOverflowY();
   nsIDOMCSSValue* DoGetResize();
   nsIDOMCSSValue* DoGetPageBreakAfter();
   nsIDOMCSSValue* DoGetPageBreakBefore();
   nsIDOMCSSValue* DoGetMozTransform();
   nsIDOMCSSValue* DoGetMozTransformOrigin();
   nsIDOMCSSValue* DoGetMozPerspective();
   nsIDOMCSSValue* DoGetMozBackfaceVisibility();
+  nsIDOMCSSValue* DoGetMozPerspectiveOrigin();
   nsIDOMCSSValue* DoGetOrient();
 
   /* User interface properties */
   nsIDOMCSSValue* DoGetCursor();
   nsIDOMCSSValue* DoGetForceBrokenImageIcon();
   nsIDOMCSSValue* DoGetIMEMode();
   nsIDOMCSSValue* DoGetUserFocus();
   nsIDOMCSSValue* DoGetUserInput();
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4506,16 +4506,31 @@ nsRuleNode::ComputeDisplayData(void* aSt
                   SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC,
                   aContext, mPresContext, canStoreInRuleTree);
        NS_ABORT_IF_FALSE(cY == cZ, "changed one but not the other");
      }
      NS_ABORT_IF_FALSE(cX == cY, "changed one but not the other");
      NS_ASSERTION(cX, "Malformed -moz-transform-origin parse!");
   }
 
+  const nsCSSValue* perspectiveOriginValue =
+    aRuleData->ValueForPerspectiveOrigin();
+  if (perspectiveOriginValue->GetUnit() != eCSSUnit_Null) {
+    mozilla::DebugOnly<PRBool> result =
+      SetPairCoords(*perspectiveOriginValue,
+                    display->mPerspectiveOrigin[0],
+                    display->mPerspectiveOrigin[1],
+                    parentDisplay->mPerspectiveOrigin[0],
+                    parentDisplay->mPerspectiveOrigin[1],
+                    SETCOORD_LPH | SETCOORD_INITIAL_HALF |
+                    SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC,
+                    aContext, mPresContext, canStoreInRuleTree);
+    NS_ASSERTION(result, "Malformed -moz-perspective-origin parse!");
+  }
+
   SetCoord(*aRuleData->ValueForPerspective(), 
            display->mChildPerspective, parentDisplay->mChildPerspective,
            SETCOORD_LAH | SETCOORD_INITIAL_ZERO | SETCOORD_NONE,
            aContext, mPresContext, canStoreInRuleTree);
 
   SetDiscrete(*aRuleData->ValueForBackfaceVisibility(),
               display->mBackfaceVisibility, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentDisplay->mBackfaceVisibility,
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -2409,16 +2409,32 @@ nsStyleAnimation::ExtractComputedValue(n
               triplet->mZValue.GetFloatValue() == 0.0f) {
             triplet->mZValue.Reset();
           }
           aComputedValue.SetAndAdoptCSSValueTripletValue(triplet.forget(),
                                                          eUnit_CSSValueTriplet);
           break;
         }
 
+        case eCSSProperty_perspective_origin: {
+          const nsStyleDisplay *styleDisplay =
+            static_cast<const nsStyleDisplay*>(styleStruct);
+          nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
+          if (!pair ||
+              !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[0],
+                                    pair->mXValue) ||
+              !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[1],
+                                    pair->mYValue)) {
+            return PR_FALSE;
+          }
+          aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
+                                                      eUnit_CSSValuePair);
+          break;
+        }
+
         case eCSSProperty_stroke_dasharray: {
           const nsStyleSVG *svg = static_cast<const nsStyleSVG*>(styleStruct);
           NS_ABORT_IF_FALSE((svg->mStrokeDasharray != nsnull) ==
                             (svg->mStrokeDasharrayLength != 0),
                             "pointer/length mismatch");
           nsAutoPtr<nsCSSValueList> result;
           if (svg->mStrokeDasharray) {
             NS_ABORT_IF_FALSE(svg->mStrokeDasharrayLength > 0,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2029,16 +2029,18 @@ nsStyleDisplay::nsStyleDisplay()
   mResize = NS_STYLE_RESIZE_NONE;
   mClipFlags = NS_STYLE_CLIP_AUTO;
   mClip.SetRect(0,0,0,0);
   mOpacity = 1.0f;
   mSpecifiedTransform = nsnull;
   mTransformOrigin[0].SetPercentValue(0.5f); // Transform is centered on origin
   mTransformOrigin[1].SetPercentValue(0.5f);
   mTransformOrigin[2].SetCoordValue(0);
+  mPerspectiveOrigin[0].SetPercentValue(0.5f);
+  mPerspectiveOrigin[1].SetPercentValue(0.5f);
   mChildPerspective.SetCoordValue(0);
   mBackfaceVisibility = NS_STYLE_BACKFACE_VISIBILITY_VISIBLE;
   mOrient = NS_STYLE_ORIENT_HORIZONTAL;
 
   mTransitions.AppendElement();
   NS_ABORT_IF_FALSE(mTransitions.Length() == 1,
                     "appending within auto buffer should never fail");
   mTransitions[0].SetInitialValues();
@@ -2097,16 +2099,18 @@ nsStyleDisplay::nsStyleDisplay(const nsS
 
   /* Copy over the transformation information. */
   mSpecifiedTransform = aSource.mSpecifiedTransform;
   
   /* Copy over transform origin. */
   mTransformOrigin[0] = aSource.mTransformOrigin[0];
   mTransformOrigin[1] = aSource.mTransformOrigin[1];
   mTransformOrigin[2] = aSource.mTransformOrigin[2];
+  mPerspectiveOrigin[0] = aSource.mPerspectiveOrigin[0];
+  mPerspectiveOrigin[1] = aSource.mPerspectiveOrigin[1];
   mChildPerspective = aSource.mChildPerspective;
   mBackfaceVisibility = aSource.mBackfaceVisibility;
 }
 
 nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
@@ -2161,16 +2165,23 @@ nsChangeHint nsStyleDisplay::CalcDiffere
     
     for (PRUint8 index = 0; index < 3; ++index)
       if (mTransformOrigin[index] != aOther.mTransformOrigin[index]) {
         NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
                                            nsChangeHint_RepaintFrame));
         break;
       }
     
+    for (PRUint8 index = 0; index < 2; ++index)
+      if (mPerspectiveOrigin[index] != aOther.mPerspectiveOrigin[index]) {
+        NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
+                                           nsChangeHint_RepaintFrame));
+        break;
+      }
+
     if (mChildPerspective != aOther.mChildPerspective)
       NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
                                          nsChangeHint_RepaintFrame));
 
     if (mBackfaceVisibility != aOther.mBackfaceVisibility)
       NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   }
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1511,16 +1511,17 @@ struct nsStyleDisplay {
 
   // mSpecifiedTransform is the list of transform functions as
   // specified, or null to indicate there is no transform.  (inherit or
   // initial are replaced by an actual list of transform functions, or
   // null, as appropriate.) (owned by the style rule)
   const nsCSSValueList *mSpecifiedTransform; // [reset]
   nsStyleCoord mTransformOrigin[3]; // [reset] percent, coord, calc, 3rd param is coord, calc only
   nsStyleCoord mChildPerspective; // [reset] coord
+  nsStyleCoord mPerspectiveOrigin[2]; // [reset] percent, coord, calc
   PRUint8 mBackfaceVisibility;
 
   nsAutoTArray<nsTransition, 1> mTransitions; // [reset]
   // The number of elements in mTransitions that are not from repeating
   // a list due to another property being longer.
   PRUint32 mTransitionTimingFunctionCount,
            mTransitionDurationCount,
            mTransitionDelayCount,
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -962,17 +962,44 @@ var gCSSProperties = {
 			"-moz-calc(20px + 50%) -moz-calc(50% - 10px)",
 			"-moz-calc(-20px) -moz-calc(-50%)",
 			"-moz-calc(-20%) -moz-calc(-50%)"
 		],
 		invalid_values: ["red", "auto", "none", "0.5 0.5", "40px #0000ff",
 						 "border", "center red", "right diagonal",
 						 "#00ffff bottom"]
 	},
-	"-moz-perspective": {
+    "-moz-perspective-origin": {
+        domProp: "MozPerspectiveOrigin",
+        inherited: false,
+        type: CSS_TYPE_LONGHAND,
+        /* no subproperties */
+        prerequisites: { "width": "10px", "height": "10px", "display": "block"},
+        initial_values: [ "50% 50%", "center", "center center" ],
+        other_values: [ "25% 25%", "5px 5px", "20% 3em", "0 0", "0in 1in",
+                        "top", "bottom","top left", "top right",
+                        "top center", "center left", "center right",
+                        "bottom left", "bottom right", "bottom center",
+                        "20% center", "5px center", "13in bottom",
+                        "left 50px", "right 13%", "center 40px",
+                        "-moz-calc(20px)",
+                        "-moz-calc(20px) 10px",
+                        "10px -moz-calc(20px)",
+                        "-moz-calc(20px) 25%",
+                        "25% -moz-calc(20px)",
+                        "-moz-calc(20px) -moz-calc(20px)",
+                        "-moz-calc(20px + 1em) -moz-calc(20px / 2)",
+                        "-moz-calc(20px + 50%) -moz-calc(50% - 10px)",
+                        "-moz-calc(-20px) -moz-calc(-50%)",
+                        "-moz-calc(-20%) -moz-calc(-50%)" ],
+        invalid_values: [ "red", "auto", "none", "0.5 0.5", "40px #0000ff",
+                          "border", "center red", "right diagonal",
+                          "#00ffff bottom"]
+    },
+    "-moz-perspective": {
 		domProp: "MozPerspective",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none", "0" ],
 		other_values: [ "1000px", "500.2px", "-100px", "-27.2em" ],
 		invalid_values: [ "pants", "200" ]
 	},
     "-moz-backface-visibility": {
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -76,16 +76,19 @@ var supported_properties = {
     "-moz-outline-radius-topleft": [ test_radius_transition ],
     "-moz-outline-radius-topright": [ test_radius_transition ],
     "-moz-text-decoration-color": [ test_color_transition,
                                     test_border_color_transition ],
     "-moz-transform": [ test_transform_transition ],
     "-moz-transform-origin": [ test_length_pair_transition,
                                test_length_percent_pair_transition,
                                test_length_percent_pair_unclamped ],
+    "-moz-perspective-origin": [ test_length_pair_transition,
+                                 test_length_percent_pair_transition,
+                                 test_length_percent_pair_unclamped ],
     "background-color": [ test_color_transition ],
     "background-position": [ test_background_position_transition,
                              // FIXME: We don't currently test clamping,
                              // since background-position uses calc() as
                              // an intermediate form.
                              /* test_length_percent_pair_unclamped */ ],
     "background-size": [ test_background_size_transition,
                          // FIXME: We don't currently test clamping,