Bug 505115 - Part 11a - Add nsCSSValueTriplet and optionally read a z component to -moz-transform-origin. r=dbaron
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 03 Aug 2011 15:04:23 +1200
changeset 73740 ea882f18d8ac5020db9af2bca4e7d252de2dc766
parent 73739 85cc7836c552230773782842fe5be67cc70a6298
child 73741 ad334152010a5d6f8f602d38a398ac7c4ec0f0c2
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersdbaron
bugs505115
milestone8.0a1
Bug 505115 - Part 11a - Add nsCSSValueTriplet and optionally read a z component to -moz-transform-origin. r=dbaron
layout/style/nsCSSParser.cpp
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleAnimation.cpp
layout/style/nsStyleAnimation.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -594,17 +594,16 @@ protected:
   PRBool ParseFunctionInternals(const PRInt32 aVariantMask[],
                                 PRUint16 aMinElems,
                                 PRUint16 aMaxElems,
                                 nsTArray<nsCSSValue>& aOutput);
 
   /* Functions for -moz-transform-origin Parsing */
   PRBool ParseMozTransformOrigin();
 
-
   /* 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);
 
@@ -7501,31 +7500,37 @@ PRBool CSSParserImpl::ParseMozTransform(
   }
   AppendValue(eCSSProperty__moz_transform, value);
   return PR_TRUE;
 }
 
 PRBool CSSParserImpl::ParseMozTransformOrigin()
 {
   nsCSSValuePair position;
-  if (!ParseBoxPositionValues(position, PR_TRUE) || !ExpectEndProperty())
+  nsCSSValue depth;
+  if (!ParseBoxPositionValues(position, PR_TRUE))
     return PR_FALSE;
 
   // 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);
   } else {
-    nsCSSValue pair;
-    pair.SetPairValue(&position);
-    AppendValue(eCSSProperty__moz_transform_origin, pair);
+    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);
   }
   return PR_TRUE;
 }
 
 PRBool
 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
 {
   if (!GetToken(PR_TRUE))
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -158,16 +158,20 @@ nsCSSValue::nsCSSValue(const nsCSSValue&
   else if (eCSSUnit_Gradient == mUnit) {
     mValue.mGradient = aCopy.mValue.mGradient;
     mValue.mGradient->AddRef();
   }
   else if (eCSSUnit_Pair == mUnit) {
     mValue.mPair = aCopy.mValue.mPair;
     mValue.mPair->AddRef();
   }
+  else if (eCSSUnit_Triplet == mUnit) {
+    mValue.mTriplet = aCopy.mValue.mTriplet;
+    mValue.mTriplet->AddRef();
+  }
   else if (eCSSUnit_Rect == mUnit) {
     mValue.mRect = aCopy.mValue.mRect;
     mValue.mRect->AddRef();
   }
   else if (eCSSUnit_List == mUnit) {
     mValue.mList = aCopy.mValue.mList;
     mValue.mList->AddRef();
   }
@@ -227,16 +231,19 @@ PRBool nsCSSValue::operator==(const nsCS
       return *mValue.mImage == *aOther.mValue.mImage;
     }
     else if (eCSSUnit_Gradient == mUnit) {
       return *mValue.mGradient == *aOther.mValue.mGradient;
     }
     else if (eCSSUnit_Pair == mUnit) {
       return *mValue.mPair == *aOther.mValue.mPair;
     }
+    else if (eCSSUnit_Triplet == mUnit) {
+      return *mValue.mTriplet == *aOther.mValue.mTriplet;
+    }
     else if (eCSSUnit_Rect == mUnit) {
       return *mValue.mRect == *aOther.mValue.mRect;
     }
     else if (eCSSUnit_List == mUnit) {
       return *mValue.mList == *aOther.mValue.mList;
     }
     else if (eCSSUnit_PairList == mUnit) {
       return *mValue.mPairList == *aOther.mValue.mPairList;
@@ -307,16 +314,18 @@ void nsCSSValue::DoReset()
   } else if (eCSSUnit_URL == mUnit) {
     mValue.mURL->Release();
   } else if (eCSSUnit_Image == mUnit) {
     mValue.mImage->Release();
   } else if (eCSSUnit_Gradient == mUnit) {
     mValue.mGradient->Release();
   } else if (eCSSUnit_Pair == mUnit) {
     mValue.mPair->Release();
+  } else if (eCSSUnit_Triplet == mUnit) {
+    mValue.mTriplet->Release();
   } else if (eCSSUnit_Rect == mUnit) {
     mValue.mRect->Release();
   } else if (eCSSUnit_List == mUnit) {
     mValue.mList->Release();
   } else if (eCSSUnit_PairList == mUnit) {
     mValue.mPairList->Release();
   }
   mUnit = eCSSUnit_Null;
@@ -436,16 +445,56 @@ void nsCSSValue::SetPairValue(const nsCS
                     yValue.GetUnit() != eCSSUnit_Initial,
                     "inappropriate pair value");
   Reset();
   mUnit = eCSSUnit_Pair;
   mValue.mPair = new nsCSSValuePair_heap(xValue, yValue);
   mValue.mPair->AddRef();
 }
 
+void nsCSSValue::SetTripletValue(const nsCSSValueTriplet* aValue)
+{
+    // triplet should not be used for null/inherit/initial values
+    // Only allow Null for the z component
+    NS_ABORT_IF_FALSE(aValue &&
+                      aValue->mXValue.GetUnit() != eCSSUnit_Null &&
+                      aValue->mYValue.GetUnit() != eCSSUnit_Null &&
+                      aValue->mXValue.GetUnit() != eCSSUnit_Inherit &&
+                      aValue->mYValue.GetUnit() != eCSSUnit_Inherit &&
+                      aValue->mZValue.GetUnit() != eCSSUnit_Inherit &&
+                      aValue->mXValue.GetUnit() != eCSSUnit_Initial &&
+                      aValue->mYValue.GetUnit() != eCSSUnit_Initial &&
+                      aValue->mZValue.GetUnit() != eCSSUnit_Initial,
+                      "missing or inappropriate triplet value");
+    Reset();
+    mUnit = eCSSUnit_Triplet;
+    mValue.mTriplet = new nsCSSValueTriplet_heap(aValue->mXValue, aValue->mYValue, aValue->mZValue);
+    mValue.mTriplet->AddRef();
+}
+
+void nsCSSValue::SetTripletValue(const nsCSSValue& xValue,
+                                 const nsCSSValue& yValue,
+                                 const nsCSSValue& zValue)
+{
+    // Only allow Null for the z component
+    NS_ABORT_IF_FALSE(xValue.GetUnit() != eCSSUnit_Null &&
+                      yValue.GetUnit() != eCSSUnit_Null &&
+                      xValue.GetUnit() != eCSSUnit_Inherit &&
+                      yValue.GetUnit() != eCSSUnit_Inherit &&
+                      zValue.GetUnit() != eCSSUnit_Inherit &&
+                      xValue.GetUnit() != eCSSUnit_Initial &&
+                      yValue.GetUnit() != eCSSUnit_Initial &&
+                      zValue.GetUnit() != eCSSUnit_Initial,
+                      "inappropriate triplet value");
+    Reset();
+    mUnit = eCSSUnit_Triplet;
+    mValue.mTriplet = new nsCSSValueTriplet_heap(xValue, yValue, zValue);
+    mValue.mTriplet->AddRef();
+}
+
 nsCSSRect& nsCSSValue::SetRectValue()
 {
   Reset();
   mUnit = eCSSUnit_Rect;
   mValue.mRect = new nsCSSRect_heap;
   mValue.mRect->AddRef();
   return *mValue.mRect;
 }
@@ -955,16 +1004,18 @@ nsCSSValue::AppendToString(nsCSSProperty
         break;
       }
       aResult.AppendLiteral(", ");
     }
 
     aResult.AppendLiteral(")");
   } else if (eCSSUnit_Pair == unit) {
     GetPairValue().AppendToString(aProperty, aResult);
+  } else if (eCSSUnit_Triplet == unit) {
+    GetTripletValue().AppendToString(aProperty, aResult);
   } else if (eCSSUnit_Rect == unit) {
     GetRectValue().AppendToString(aProperty, aResult);
   } else if (eCSSUnit_List == unit || eCSSUnit_ListDep == unit) {
     GetListValue()->AppendToString(aProperty, aResult);
   } else if (eCSSUnit_PairList == unit || eCSSUnit_PairListDep == unit) {
     GetPairListValue()->AppendToString(aProperty, aResult);
   }
 
@@ -1006,16 +1057,17 @@ nsCSSValue::AppendToString(nsCSSProperty
     case eCSSUnit_Integer:      break;
     case eCSSUnit_Enumerated:   break;
     case eCSSUnit_EnumColor:    break;
     case eCSSUnit_Color:        break;
     case eCSSUnit_Percent:      aResult.Append(PRUnichar('%'));    break;
     case eCSSUnit_Number:       break;
     case eCSSUnit_Gradient:     break;
     case eCSSUnit_Pair:         break;
+    case eCSSUnit_Triplet:      break;
     case eCSSUnit_Rect:         break;
     case eCSSUnit_List:         break;
     case eCSSUnit_ListDep:      break;
     case eCSSUnit_PairList:     break;
     case eCSSUnit_PairListDep:  break;
 
     case eCSSUnit_Inch:         aResult.AppendLiteral("in");   break;
     case eCSSUnit_Millimeter:   aResult.AppendLiteral("mm");   break;
@@ -1172,16 +1224,33 @@ nsCSSValuePair::AppendToString(nsCSSProp
 {
   mXValue.AppendToString(aProperty, aResult);
   if (mYValue.GetUnit() != eCSSUnit_Null) {
     aResult.Append(PRUnichar(' '));
     mYValue.AppendToString(aProperty, aResult);
   }
 }
 
+// --- nsCSSValueTriple -----------------
+
+void
+nsCSSValueTriplet::AppendToString(nsCSSProperty aProperty,
+                               nsAString& aResult) const
+{
+    mXValue.AppendToString(aProperty, aResult);
+    if (mYValue.GetUnit() != eCSSUnit_Null) {
+        aResult.Append(PRUnichar(' '));
+        mYValue.AppendToString(aProperty, aResult);
+        if (mZValue.GetUnit() != eCSSUnit_Null) {
+            aResult.Append(PRUnichar(' '));
+            mZValue.AppendToString(aProperty, aResult);
+        }
+    }
+}
+
 // --- nsCSSValuePairList -----------------
 
 nsCSSValuePairList::~nsCSSValuePairList()
 {
   MOZ_COUNT_DTOR(nsCSSValuePairList);
   NS_CSS_DELETE_LIST_MEMBER(nsCSSValuePairList, this, mNext);
 }
 
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -137,22 +137,23 @@ enum nsCSSUnit {
   eCSSUnit_Calc_Times_R = 34,     // (nsCSSValue::Array*) val * num within calc
   eCSSUnit_Calc_Divided = 35,     // (nsCSSValue::Array*) / within calc
 
   eCSSUnit_URL          = 40,     // (nsCSSValue::URL*) value
   eCSSUnit_Image        = 41,     // (nsCSSValue::Image*) value
   eCSSUnit_Gradient     = 42,     // (nsCSSValueGradient*) value
 
   eCSSUnit_Pair         = 50,     // (nsCSSValuePair*) pair of values
-  eCSSUnit_Rect         = 51,     // (nsCSSRect*) rectangle (four values)
-  eCSSUnit_List         = 52,     // (nsCSSValueList*) list of values
-  eCSSUnit_ListDep      = 53,     // (nsCSSValueList*) same as List
+  eCSSUnit_Triplet      = 51,     // (nsCSSValueTriplet*) triplet of values
+  eCSSUnit_Rect         = 52,     // (nsCSSRect*) rectangle (four values)
+  eCSSUnit_List         = 53,     // (nsCSSValueList*) list of values
+  eCSSUnit_ListDep      = 54,     // (nsCSSValueList*) same as List
                                   //   but does not own the list
-  eCSSUnit_PairList     = 54,     // (nsCSSValuePairList*) list of value pairs
-  eCSSUnit_PairListDep  = 55,     // (nsCSSValuePairList*) same as PairList
+  eCSSUnit_PairList     = 55,     // (nsCSSValuePairList*) list of value pairs
+  eCSSUnit_PairListDep  = 56,     // (nsCSSValuePairList*) same as PairList
                                   //   but does not own the list
 
   eCSSUnit_Integer      = 70,     // (int) simple value
   eCSSUnit_Enumerated   = 71,     // (int) value has enumerated meaning
 
   eCSSUnit_EnumColor    = 80,     // (int) enumerated color (kColorKTable)
   eCSSUnit_Color        = 81,     // (nscolor) an RGBA value
 
@@ -195,16 +196,18 @@ struct nsCSSValueGradient;
 struct nsCSSValuePair;
 struct nsCSSValuePair_heap;
 struct nsCSSRect;
 struct nsCSSRect_heap;
 struct nsCSSValueList;
 struct nsCSSValueList_heap;
 struct nsCSSValuePairList;
 struct nsCSSValuePairList_heap;
+struct nsCSSValueTriplet;
+struct nsCSSValueTriplet_heap;
 
 class nsCSSValue {
 public:
   struct Array;
   friend struct Array;
 
   struct URL;
   friend struct URL;
@@ -365,16 +368,19 @@ public:
   inline const nsCSSRect& GetRectValue() const;
 
   inline nsCSSValueList* GetListValue();
   inline const nsCSSValueList* GetListValue() const;
 
   inline nsCSSValuePairList* GetPairListValue();
   inline const nsCSSValuePairList* GetPairListValue() const;
 
+  inline nsCSSValueTriplet& GetTripletValue();
+  inline const nsCSSValueTriplet& GetTripletValue() const;
+
   URL* GetURLStructValue() const
   {
     // Not allowing this for Image values, because if the caller takes
     // a ref to them they won't be able to delete them properly.
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_URL, "not a URL value");
     return mValue.mURL;
   }
 
@@ -412,16 +418,18 @@ public:
   void SetArrayValue(nsCSSValue::Array* aArray, nsCSSUnit aUnit);
   void SetURLValue(nsCSSValue::URL* aURI);
   void SetImageValue(nsCSSValue::Image* aImage);
   void SetGradientValue(nsCSSValueGradient* aGradient);
   void SetPairValue(const nsCSSValuePair* aPair);
   void SetPairValue(const nsCSSValue& xValue, const nsCSSValue& yValue);
   void SetDependentListValue(nsCSSValueList* aList);
   void SetDependentPairListValue(nsCSSValuePairList* aList);
+  void SetTripletValue(const nsCSSValueTriplet* aTriplet);
+  void SetTripletValue(const nsCSSValue& xValue, const nsCSSValue& yValue, const nsCSSValue& zValue);
   void SetAutoValue();
   void SetInheritValue();
   void SetInitialValue();
   void SetNoneValue();
   void SetAllValue();
   void SetNormalValue();
   void SetSystemFontValue();
   void SetDummyValue();
@@ -526,16 +534,17 @@ protected:
     nsStringBuffer* mString;
     nscolor    mColor;
     Array*     mArray;
     URL*       mURL;
     Image*     mImage;
     nsCSSValueGradient* mGradient;
     nsCSSValuePair_heap* mPair;
     nsCSSRect_heap* mRect;
+    nsCSSValueTriplet_heap* mTriplet;
     nsCSSValueList_heap* mList;
     nsCSSValueList* mListDependent;
     nsCSSValuePairList_heap* mPairList;
     nsCSSValuePairList* mPairListDependent;
   }         mValue;
 };
 
 struct nsCSSValue::Array {
@@ -833,22 +842,98 @@ struct nsCSSValuePair {
   nsCSSValue mXValue;
   nsCSSValue mYValue;
 };
 
 // nsCSSValuePair_heap differs from nsCSSValuePair only in being
 // refcounted.  It should not be necessary to use this class directly;
 // it's an implementation detail of nsCSSValue.
 struct nsCSSValuePair_heap : public nsCSSValuePair {
+    // forward constructor
+    nsCSSValuePair_heap(const nsCSSValue& aXValue, const nsCSSValue& aYValue)
+        : nsCSSValuePair(aXValue, aYValue)
+    {}
+
+    NS_INLINE_DECL_REFCOUNTING(nsCSSValuePair_heap)
+};
+
+struct nsCSSValueTriplet {
+    nsCSSValueTriplet()
+    {
+        MOZ_COUNT_CTOR(nsCSSValueTriplet);
+    }
+    nsCSSValueTriplet(nsCSSUnit aUnit)
+        : mXValue(aUnit), mYValue(aUnit), mZValue(aUnit)
+    {
+        MOZ_COUNT_CTOR(nsCSSValueTriplet);
+    }
+    nsCSSValueTriplet(const nsCSSValue& aXValue, 
+                      const nsCSSValue& aYValue, 
+                      const nsCSSValue& aZValue)
+        : mXValue(aXValue), mYValue(aYValue), mZValue(aZValue)
+    {
+        MOZ_COUNT_CTOR(nsCSSValueTriplet);
+    }
+    nsCSSValueTriplet(const nsCSSValueTriplet& aCopy)
+        : mXValue(aCopy.mXValue), mYValue(aCopy.mYValue), mZValue(aCopy.mZValue)
+    {
+        MOZ_COUNT_CTOR(nsCSSValueTriplet);
+    }
+    ~nsCSSValueTriplet()
+    {
+        MOZ_COUNT_DTOR(nsCSSValueTriplet);
+    }
+
+    PRBool operator==(const nsCSSValueTriplet& aOther) const {
+        return mXValue == aOther.mXValue &&
+               mYValue == aOther.mYValue &&
+               mZValue == aOther.mZValue;
+    }
+
+    PRBool operator!=(const nsCSSValueTriplet& aOther) const {
+        return mXValue != aOther.mXValue ||
+               mYValue != aOther.mYValue ||
+               mZValue != aOther.mZValue;
+    }
+
+    void SetAllValuesTo(const nsCSSValue& aValue) {
+        mXValue = aValue;
+        mYValue = aValue;
+        mZValue = aValue;
+    }
+
+    void Reset() {
+        mXValue.Reset();
+        mYValue.Reset();
+        mZValue.Reset();
+    }
+
+    PRBool HasValue() const {
+        return mXValue.GetUnit() != eCSSUnit_Null ||
+               mYValue.GetUnit() != eCSSUnit_Null ||
+               mZValue.GetUnit() != eCSSUnit_Null;
+    }
+
+    void AppendToString(nsCSSProperty aProperty, nsAString& aResult) const;
+
+    nsCSSValue mXValue;
+    nsCSSValue mYValue;
+    nsCSSValue mZValue;
+};
+
+// nsCSSValueTriplet_heap differs from nsCSSValueTriplet only in being
+// refcounted.  It should not be necessary to use this class directly;
+// it's an implementation detail of nsCSSValue.
+struct nsCSSValueTriplet_heap : public nsCSSValueTriplet {
   // forward constructor
-  nsCSSValuePair_heap(const nsCSSValue& aXValue, const nsCSSValue& aYValue)
-    : nsCSSValuePair(aXValue, aYValue)
+  nsCSSValueTriplet_heap(const nsCSSValue& aXValue, const nsCSSValue& aYValue, const nsCSSValue& aZValue)
+    : nsCSSValueTriplet(aXValue, aYValue, aZValue)
   {}
 
-  NS_INLINE_DECL_REFCOUNTING(nsCSSValuePair_heap)
+  NS_INLINE_DECL_REFCOUNTING(nsCSSValueTriplet_heap)
 };
 
 // This has to be here so that the relationship between nsCSSValuePair
 // and nsCSSValuePair_heap is visible.
 inline nsCSSValuePair&
 nsCSSValue::GetPairValue()
 {
   NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Pair, "not a pair value");
@@ -857,16 +942,30 @@ nsCSSValue::GetPairValue()
 
 inline const nsCSSValuePair&
 nsCSSValue::GetPairValue() const
 {
   NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Pair, "not a pair value");
   return *mValue.mPair;
 }
 
+inline nsCSSValueTriplet&
+nsCSSValue::GetTripletValue()
+{
+    NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Triplet, "not a triplet value");
+    return *mValue.mTriplet;
+}
+
+inline const nsCSSValueTriplet&
+nsCSSValue::GetTripletValue() const
+{
+    NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Triplet, "not a triplet value");
+    return *mValue.mTriplet;
+}
+
 // Maybe should be replaced with nsCSSValueList and nsCSSValue::Array?
 struct nsCSSValuePairList {
   nsCSSValuePairList() : mNext(nsnull) { MOZ_COUNT_CTOR(nsCSSValuePairList); }
   ~nsCSSValuePairList();
 
   nsCSSValuePairList* Clone() const; // makes a deep copy
   void AppendToString(nsCSSProperty aProperty, nsAString& aResult) const;
 
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -933,16 +933,24 @@ nsComputedDOMStyle::DoGetMozTransformOri
                   &nsComputedDOMStyle::GetFrameBoundsWidthForTransform);
   valueList->AppendCSSValue(width);
 
   nsROCSSPrimitiveValue* height = GetROCSSPrimitiveValue();
   SetValueToCoord(height, display->mTransformOrigin[1], PR_FALSE,
                   &nsComputedDOMStyle::GetFrameBoundsHeightForTransform);
   valueList->AppendCSSValue(height);
 
+  if (display->mTransformOrigin[2].GetUnit() != eStyleUnit_Coord ||
+      display->mTransformOrigin[2].GetCoordValue() != 0) {
+    nsROCSSPrimitiveValue* depth = GetROCSSPrimitiveValue();
+    SetValueToCoord(depth, display->mTransformOrigin[2], PR_FALSE,
+                    nsnull);
+    valueList->AppendCSSValue(depth);
+  }
+
   return valueList;
 }
 
 nsIDOMCSSValue*
 nsComputedDOMStyle::DoGetMozPerspective()
 {
     nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
     if (GetStyleDisplay()->mChildPerspective.GetUnit() == eStyleUnit_Coord &&
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4465,28 +4465,55 @@ nsRuleNode::ComputeDisplayData(void* aSt
   default:
     NS_ABORT_IF_FALSE(false, "unrecognized transform unit");
   }
 
   /* Convert -moz-transform-origin. */
   const nsCSSValue* transformOriginValue =
     aRuleData->ValueForTransformOrigin();
   if (transformOriginValue->GetUnit() != eCSSUnit_Null) {
-#ifdef DEBUG
-    PRBool result =
-#endif
-      SetPairCoords(*transformOriginValue,
-                    display->mTransformOrigin[0],
-                    display->mTransformOrigin[1],
-                    parentDisplay->mTransformOrigin[0],
-                    parentDisplay->mTransformOrigin[1],
-                    SETCOORD_LPH | SETCOORD_INITIAL_HALF |
-                    SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC,
-                    aContext, mPresContext, canStoreInRuleTree);
-    NS_ASSERTION(result, "Malformed -moz-transform-origin parse!");
+    const nsCSSValue& valX =
+      transformOriginValue->GetUnit() == eCSSUnit_Triplet ?
+        transformOriginValue->GetTripletValue().mXValue : *transformOriginValue;
+    const nsCSSValue& valY =
+      transformOriginValue->GetUnit() == eCSSUnit_Triplet ?
+        transformOriginValue->GetTripletValue().mYValue : *transformOriginValue;
+    const nsCSSValue& valZ =
+      transformOriginValue->GetUnit() == eCSSUnit_Triplet ?
+        transformOriginValue->GetTripletValue().mZValue : *transformOriginValue;
+
+    mozilla::DebugOnly<PRBool> cX =
+       SetCoord(valX, display->mTransformOrigin[0],
+                parentDisplay->mTransformOrigin[0],
+                SETCOORD_LPH | SETCOORD_INITIAL_HALF |
+                  SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC,
+                aContext, mPresContext, canStoreInRuleTree);
+
+     mozilla::DebugOnly<PRBool> cY =
+       SetCoord(valY, display->mTransformOrigin[1],
+                parentDisplay->mTransformOrigin[1],
+                SETCOORD_LPH | SETCOORD_INITIAL_HALF |
+                  SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC,
+                aContext, mPresContext, canStoreInRuleTree);
+
+     if (valZ.GetUnit() == eCSSUnit_Null) {
+       // Null for the z component means a 0 translation, not
+       // unspecified, as we have already checked the triplet
+       // value for Null.
+       display->mTransformOrigin[2].SetCoordValue(0);
+     } else {
+       mozilla::DebugOnly<PRBool> cZ =
+         SetCoord(valZ, display->mTransformOrigin[2],
+                  parentDisplay->mTransformOrigin[2],
+                  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!");
   }
 
   SetCoord(*aRuleData->ValueForPerspective(), 
            display->mChildPerspective, parentDisplay->mChildPerspective,
            SETCOORD_LAH | SETCOORD_INITIAL_ZERO | SETCOORD_NONE,
            aContext, mPresContext, canStoreInRuleTree);
 
   SetDiscrete(*aRuleData->ValueForBackfaceVisibility(),
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -412,16 +412,71 @@ nsStyleAnimation::ComputeDistance(nsCSSP
             return PR_FALSE;
         }
         squareDistance += diffsquared;
       }
 
       aDistance = sqrt(squareDistance);
       return PR_TRUE;
     }
+    case eUnit_CSSValueTriplet: {
+      const nsCSSValueTriplet *triplet1 = aStartValue.GetCSSValueTripletValue();
+      const nsCSSValueTriplet *triplet2 = aEndValue.GetCSSValueTripletValue();
+      nsCSSUnit unit[3];
+      unit[0] = GetCommonUnit(aProperty, triplet1->mXValue.GetUnit(),
+                              triplet2->mXValue.GetUnit());
+      unit[1] = GetCommonUnit(aProperty, triplet1->mYValue.GetUnit(),
+                              triplet2->mYValue.GetUnit());
+      unit[2] = GetCommonUnit(aProperty, triplet1->mZValue.GetUnit(),
+                              triplet2->mZValue.GetUnit());
+      if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
+          unit[0] == eCSSUnit_URL) {
+        return PR_FALSE;
+      }
+
+      double squareDistance = 0.0;
+      static nsCSSValue nsCSSValueTriplet::* const pairValues[3] = {
+        &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
+      };
+      for (PRUint32 i = 0; i < 3; ++i) {
+        nsCSSValue nsCSSValueTriplet::*member = pairValues[i];
+        double diffsquared;
+        switch (unit[i]) {
+          case eCSSUnit_Pixel: {
+            float diff = (triplet1->*member).GetFloatValue() -
+                         (triplet2->*member).GetFloatValue();
+            diffsquared = diff * diff;
+            break;
+          }
+          case eCSSUnit_Percent: {
+            float diff = (triplet1->*member).GetPercentValue() -
+                         (triplet2->*member).GetPercentValue();
+             diffsquared = diff * diff;
+             break;
+          }
+          case eCSSUnit_Calc: {
+            CalcValue v1 = ExtractCalcValue(triplet1->*member);
+            CalcValue v2 = ExtractCalcValue(triplet2->*member);
+            float difflen = v2.mLength - v1.mLength;
+            float diffpct = v2.mPercent - v1.mPercent;
+            diffsquared = difflen * difflen + diffpct * diffpct;
+            break;
+          }
+          case eCSSUnit_Null:
+            break;
+          default:
+            NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
+            return PR_FALSE;
+        }
+        squareDistance += diffsquared;
+      }
+
+      aDistance = sqrt(squareDistance);
+      return PR_TRUE;
+    }
     case eUnit_CSSRect: {
       const nsCSSRect *rect1 = aStartValue.GetCSSRectValue();
       const nsCSSRect *rect2 = aEndValue.GetCSSRectValue();
       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
           rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
         // At least until we have calc()
@@ -1486,16 +1541,76 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
             return PR_FALSE;
         }
       }
 
       aResultValue.SetAndAdoptCSSValuePairValue(result.forget(),
                                                 eUnit_CSSValuePair);
       return PR_TRUE;
     }
+    case eUnit_CSSValueTriplet: {
+      nsCSSValueTriplet triplet1(*aValue1.GetCSSValueTripletValue());
+      nsCSSValueTriplet triplet2(*aValue2.GetCSSValueTripletValue());
+
+      if (triplet1.mZValue.GetUnit() == eCSSUnit_Null) {
+        triplet1.mZValue.SetFloatValue(0.0, eCSSUnit_Pixel);
+      }
+      if (triplet2.mZValue.GetUnit() == eCSSUnit_Null) {
+          triplet2.mZValue.SetFloatValue(0.0, eCSSUnit_Pixel);
+      }
+
+      nsCSSUnit unit[3];
+      unit[0] = GetCommonUnit(aProperty, triplet1.mXValue.GetUnit(),
+                              triplet2.mXValue.GetUnit());
+      unit[1] = GetCommonUnit(aProperty, triplet1.mYValue.GetUnit(),
+                               triplet2.mYValue.GetUnit());
+      unit[2] = GetCommonUnit(aProperty, triplet1.mZValue.GetUnit(),
+                              triplet2.mZValue.GetUnit());
+      if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
+          unit[0] == eCSSUnit_Null || unit[0] == eCSSUnit_URL) {
+        return PR_FALSE;
+      }
+
+      nsAutoPtr<nsCSSValueTriplet> result(new nsCSSValueTriplet);
+      static nsCSSValue nsCSSValueTriplet::* const tripletValues[3] = {
+        &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
+      };
+      PRUint32 restrictions = nsCSSProps::ValueRestrictions(aProperty);
+      for (PRUint32 i = 0; i < 3; ++i) {
+        nsCSSValue nsCSSValueTriplet::*member = tripletValues[i];
+        switch (unit[i]) {
+          case eCSSUnit_Pixel:
+            AddCSSValuePixel(aCoeff1, &triplet1->*member, aCoeff2, &triplet2->*member,
+                             result->*member, restrictions);
+            break;
+          case eCSSUnit_Percent:
+            AddCSSValuePercent(aCoeff1, &triplet1->*member,
+                               aCoeff2, &triplet2->*member,
+                               result->*member, restrictions);
+            break;
+          case eCSSUnit_Calc:
+            AddCSSValueCanonicalCalc(aCoeff1, &triplet1->*member,
+                                     aCoeff2, &triplet2->*member,
+                                     result->*member);
+            break;
+          default:
+            NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
+            return PR_FALSE;
+        }
+      }
+
+      if (result->mZValue.GetUnit() == eCSSUnit_Pixel &&
+          result->mZValue.GetFloatValue() == 0.0f) {
+        result->mZValue.Reset();
+      }
+
+      aResultValue.SetAndAdoptCSSValueTripletValue(result.forget(),
+                                                   eUnit_CSSValueTriplet);
+      return PR_TRUE;
+    }
     case eUnit_CSSRect: {
       NS_ABORT_IF_FALSE(nsCSSProps::ValueRestrictions(aProperty) == 0,
                         "must add code for handling value restrictions");
       const nsCSSRect *rect1 = aValue1.GetCSSRectValue();
       const nsCSSRect *rect2 = aValue2.GetCSSRectValue();
       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
@@ -1953,16 +2068,28 @@ nsStyleAnimation::UncomputeValue(nsCSSPr
       // use pairs do expect collapsing.
       const nsCSSValuePair* pair = aComputedValue.GetCSSValuePairValue();
       if (pair->mXValue == pair->mYValue) {
         aSpecifiedValue = pair->mXValue;
       } else {
         aSpecifiedValue.SetPairValue(pair);
       }
     } break;
+    case eUnit_CSSValueTriplet: {
+      // Rule node processing expects triplet values to be collapsed to a
+      // single value if both halves would be equal, for most but not
+      // all properties.  At present, all animatable properties that
+      // use pairs do expect collapsing.
+      const nsCSSValueTriplet* triplet = aComputedValue.GetCSSValueTripletValue();
+      if (triplet->mXValue == triplet->mYValue && triplet->mYValue == triplet->mZValue) {
+        aSpecifiedValue = triplet->mXValue;
+      } else {
+        aSpecifiedValue.SetTripletValue(triplet);
+      }
+    } break;
     case eUnit_CSSRect: {
       nsCSSRect& rect = aSpecifiedValue.SetRectValue();
       rect = *aComputedValue.GetCSSRectValue();
     } break;
     case eUnit_Dasharray:
     case eUnit_Shadow:
     case eUnit_Transform:
       aSpecifiedValue.
@@ -2263,26 +2390,32 @@ nsStyleAnimation::ExtractComputedValue(n
           aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
                                                       eUnit_CSSValuePair);
           break;
         }
 
         case eCSSProperty__moz_transform_origin: {
           const nsStyleDisplay *styleDisplay =
             static_cast<const nsStyleDisplay*>(styleStruct);
-          nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
-          if (!pair ||
+          nsAutoPtr<nsCSSValueTriplet> triplet(new nsCSSValueTriplet);
+          if (!triplet ||
               !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0],
-                                    pair->mXValue) ||
+                                    triplet->mXValue) ||
               !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1],
-                                    pair->mYValue)) {
+                                    triplet->mYValue) ||
+              !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[2],
+                                    triplet->mZValue)) {
             return PR_FALSE;
           }
-          aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
-                                                      eUnit_CSSValuePair);
+          if (triplet->mZValue.GetUnit() == eCSSUnit_Pixel &&
+              triplet->mZValue.GetFloatValue() == 0.0f) {
+            triplet->mZValue.Reset();
+          }
+          aComputedValue.SetAndAdoptCSSValueTripletValue(triplet.forget(),
+                                                         eUnit_CSSValueTriplet);
           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");
@@ -2771,16 +2904,24 @@ nsStyleAnimation::Value::operator=(const
     case eUnit_CSSValuePair:
       NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePair,
                         "value pairs may not be null");
       mValue.mCSSValuePair = new nsCSSValuePair(*aOther.mValue.mCSSValuePair);
       if (!mValue.mCSSValuePair) {
         mUnit = eUnit_Null;
       }
       break;
+    case eUnit_CSSValueTriplet:
+      NS_ABORT_IF_FALSE(aOther.mValue.mCSSValueTriplet,
+                        "value triplets may not be null");
+      mValue.mCSSValueTriplet = new nsCSSValueTriplet(*aOther.mValue.mCSSValueTriplet);
+      if (!mValue.mCSSValueTriplet) {
+        mUnit = eUnit_Null;
+      }
+      break;
     case eUnit_CSSRect:
       NS_ABORT_IF_FALSE(aOther.mValue.mCSSRect, "rects may not be null");
       mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect);
       if (!mValue.mCSSRect) {
         mUnit = eUnit_Null;
       }
       break;
     case eUnit_Dasharray:
@@ -2908,16 +3049,27 @@ nsStyleAnimation::Value::SetAndAdoptCSSV
   FreeValue();
   NS_ABORT_IF_FALSE(IsCSSValuePairUnit(aUnit), "bad unit");
   NS_ABORT_IF_FALSE(aValuePair != nsnull, "value pairs may not be null");
   mUnit = aUnit;
   mValue.mCSSValuePair = aValuePair; // take ownership
 }
 
 void
+nsStyleAnimation::Value::SetAndAdoptCSSValueTripletValue(
+                           nsCSSValueTriplet *aValueTriplet, Unit aUnit)
+{
+    FreeValue();
+    NS_ABORT_IF_FALSE(IsCSSValueTripletUnit(aUnit), "bad unit");
+    NS_ABORT_IF_FALSE(aValueTriplet != nsnull, "value pairs may not be null");
+    mUnit = aUnit;
+    mValue.mCSSValueTriplet = aValueTriplet; // take ownership
+}
+
+void
 nsStyleAnimation::Value::SetAndAdoptCSSRectValue(nsCSSRect *aRect, Unit aUnit)
 {
   FreeValue();
   NS_ABORT_IF_FALSE(IsCSSRectUnit(aUnit), "bad unit");
   NS_ABORT_IF_FALSE(aRect != nsnull, "value pairs may not be null");
   mUnit = aUnit;
   mValue.mCSSRect = aRect; // take ownership
 }
@@ -2948,16 +3100,18 @@ void
 nsStyleAnimation::Value::FreeValue()
 {
   if (IsCSSValueUnit(mUnit)) {
     delete mValue.mCSSValue;
   } else if (IsCSSValueListUnit(mUnit)) {
     delete mValue.mCSSValueList;
   } else if (IsCSSValuePairUnit(mUnit)) {
     delete mValue.mCSSValuePair;
+  } else if (IsCSSValueTripletUnit(mUnit)) {
+    delete mValue.mCSSValueTriplet;
   } else if (IsCSSRectUnit(mUnit)) {
     delete mValue.mCSSRect;
   } else if (IsCSSValuePairListUnit(mUnit)) {
     delete mValue.mCSSValuePairList;
   } else if (IsStringUnit(mUnit)) {
     NS_ABORT_IF_FALSE(mValue.mString, "expecting non-null string");
     mValue.mString->Release();
   }
@@ -2986,16 +3140,18 @@ nsStyleAnimation::Value::operator==(cons
     case eUnit_Float:
       return mValue.mFloat == aOther.mValue.mFloat;
     case eUnit_Color:
       return mValue.mColor == aOther.mValue.mColor;
     case eUnit_Calc:
       return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
     case eUnit_CSSValuePair:
       return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
+    case eUnit_CSSValueTriplet:
+      return *mValue.mCSSValueTriplet == *aOther.mValue.mCSSValueTriplet;
     case eUnit_CSSRect:
       return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
     case eUnit_Dasharray:
     case eUnit_Shadow:
     case eUnit_Transform:
       return *mValue.mCSSValueList == *aOther.mValue.mCSSValueList;
     case eUnit_CSSValuePairList:
       return *mValue.mCSSValuePairList == *aOther.mValue.mCSSValuePairList;
--- a/layout/style/nsStyleAnimation.h
+++ b/layout/style/nsStyleAnimation.h
@@ -50,16 +50,17 @@
 #include "nsCoord.h"
 #include "nsColor.h"
 
 class nsPresContext;
 class nsStyleContext;
 class nsCSSValue;
 struct nsCSSValueList;
 struct nsCSSValuePair;
+struct nsCSSValueTriplet;
 struct nsCSSValuePairList;
 struct nsCSSRect;
 struct gfxMatrix;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
@@ -244,16 +245,17 @@ public:
     eUnit_Integer,
     eUnit_Coord,
     eUnit_Percent,
     eUnit_Float,
     eUnit_Color,
     eUnit_Calc, // nsCSSValue* (never null), always with a single
                 // calc() expression that's either length or length+percent
     eUnit_CSSValuePair, // nsCSSValuePair* (never null)
+    eUnit_CSSValueTriplet, // nsCSSValueTriplet* (never null)
     eUnit_CSSRect, // nsCSSRect* (never null)
     eUnit_Dasharray, // nsCSSValueList* (never null)
     eUnit_Shadow, // nsCSSValueList* (may be null)
     eUnit_Transform, // nsCSSValueList* (never null)
     eUnit_CSSValuePairList, // nsCSSValuePairList* (never null)
     eUnit_UnparsedString // nsStringBuffer* (never null)
   };
 
@@ -262,16 +264,17 @@ public:
     Unit mUnit;
     union {
       PRInt32 mInt;
       nscoord mCoord;
       float mFloat;
       nscolor mColor;
       nsCSSValue* mCSSValue;
       nsCSSValuePair* mCSSValuePair;
+      nsCSSValueTriplet* mCSSValueTriplet;
       nsCSSRect* mCSSRect;
       nsCSSValueList* mCSSValueList;
       nsCSSValuePairList* mCSSValuePairList;
       nsStringBuffer* mString;
     } mValue;
   public:
     Unit GetUnit() const {
       NS_ASSERTION(mUnit != eUnit_Null, "uninitialized");
@@ -307,16 +310,20 @@ public:
     nsCSSValue* GetCSSValueValue() const {
       NS_ASSERTION(IsCSSValueUnit(mUnit), "unit mismatch");
       return mValue.mCSSValue;
     }
     nsCSSValuePair* GetCSSValuePairValue() const {
       NS_ASSERTION(IsCSSValuePairUnit(mUnit), "unit mismatch");
       return mValue.mCSSValuePair;
     }
+    nsCSSValueTriplet* GetCSSValueTripletValue() const {
+      NS_ASSERTION(IsCSSValueTripletUnit(mUnit), "unit mismatch");
+      return mValue.mCSSValueTriplet;
+    }
     nsCSSRect* GetCSSRectValue() const {
       NS_ASSERTION(IsCSSRectUnit(mUnit), "unit mismatch");
       return mValue.mCSSRect;
     }
     nsCSSValueList* GetCSSValueListValue() const {
       NS_ASSERTION(IsCSSValueListUnit(mUnit), "unit mismatch");
       return mValue.mCSSValueList;
     }
@@ -364,16 +371,17 @@ public:
     void SetFloatValue(float aFloat);
     void SetColorValue(nscolor aColor);
     void SetUnparsedStringValue(const nsString& aString);
 
     // These setters take ownership of |aValue|, and are therefore named
     // "SetAndAdopt*".
     void SetAndAdoptCSSValueValue(nsCSSValue *aValue, Unit aUnit);
     void SetAndAdoptCSSValuePairValue(nsCSSValuePair *aValue, Unit aUnit);
+    void SetAndAdoptCSSValueTripletValue(nsCSSValueTriplet *aValue, Unit aUnit);
     void SetAndAdoptCSSRectValue(nsCSSRect *aValue, Unit aUnit);
     void SetAndAdoptCSSValueListValue(nsCSSValueList *aValue, Unit aUnit);
     void SetAndAdoptCSSValuePairListValue(nsCSSValuePairList *aValue);
 
     Value& operator=(const Value& aOther);
 
     PRBool operator==(const Value& aOther) const;
     PRBool operator!=(const Value& aOther) const
@@ -391,16 +399,19 @@ public:
              aUnit == eUnit_Integer;
     }
     static PRBool IsCSSValueUnit(Unit aUnit) {
       return aUnit == eUnit_Calc;
     }
     static PRBool IsCSSValuePairUnit(Unit aUnit) {
       return aUnit == eUnit_CSSValuePair;
     }
+    static PRBool IsCSSValueTripletUnit(Unit aUnit) {
+      return aUnit == eUnit_CSSValueTriplet;
+    }
     static PRBool IsCSSRectUnit(Unit aUnit) {
       return aUnit == eUnit_CSSRect;
     }
     static PRBool IsCSSValueListUnit(Unit aUnit) {
       return aUnit == eUnit_Dasharray || aUnit == eUnit_Shadow ||
              aUnit == eUnit_Transform;
     }
     static PRBool IsCSSValuePairListUnit(Unit aUnit) {
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2027,17 +2027,18 @@ nsStyleDisplay::nsStyleDisplay()
   mOverflowX = NS_STYLE_OVERFLOW_VISIBLE;
   mOverflowY = NS_STYLE_OVERFLOW_VISIBLE;
   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[1].SetPercentValue(0.5f);
+  mTransformOrigin[2].SetCoordValue(0);
   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();
@@ -2095,16 +2096,17 @@ nsStyleDisplay::nsStyleDisplay(const nsS
   mOrient = aSource.mOrient;
 
   /* 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];
   mChildPerspective = aSource.mChildPerspective;
   mBackfaceVisibility = aSource.mBackfaceVisibility;
 }
 
 nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
@@ -2152,17 +2154,17 @@ nsChangeHint nsStyleDisplay::CalcDiffere
      * the overflow rect (which probably changed if the transform changed)
      * and to redraw within the bounds of that new overflow rect.
      */
     if (!mSpecifiedTransform != !aOther.mSpecifiedTransform ||
         (mSpecifiedTransform && *mSpecifiedTransform != *aOther.mSpecifiedTransform))
       NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
                                          nsChangeHint_UpdateTransformLayer));
     
-    for (PRUint8 index = 0; index < 2; ++index)
+    for (PRUint8 index = 0; index < 3; ++index)
       if (mTransformOrigin[index] != aOther.mTransformOrigin[index]) {
         NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
                                            nsChangeHint_RepaintFrame));
         break;
       }
     
     if (mChildPerspective != aOther.mChildPerspective)
       NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1509,17 +1509,17 @@ struct nsStyleDisplay {
   PRUint8   mClipFlags;         // [reset] see nsStyleConsts.h
   PRUint8 mOrient;              // [reset] see nsStyleConsts.h
 
   // 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[2]; // [reset] percent, coord, calc
+  nsStyleCoord mTransformOrigin[3]; // [reset] percent, coord, calc, 3rd param is coord, calc only
   nsStyleCoord mChildPerspective; // [reset] coord
   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,