Add support for animation of nsRect values. (Bug 520488) r=bzbarsky
authorL. David Baron <dbaron@dbaron.org>
Mon, 21 Dec 2009 16:46:25 -0500
changeset 36525 bfe4de8f0428de92bdd54c994b8fa321e05fb613
parent 36524 833f5ebf46519109cc3168afe0e14851f05d6b9c
child 36526 3c8484e98d6290800a1896ecbfc108b6c3d7e56e
push id10857
push userdbaron@mozilla.com
push dateMon, 21 Dec 2009 21:46:59 +0000
treeherdermozilla-central@48175bb06cb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs520488
milestone1.9.3a1pre
Add support for animation of nsRect values. (Bug 520488) r=bzbarsky
layout/style/nsCSSPropList.h
layout/style/nsStyleAnimation.cpp
layout/style/nsStyleAnimation.h
layout/style/nsStyleStruct.h
layout/style/test/test_transitions_per_property.html
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -1185,18 +1185,18 @@ CSS_PROP_DISPLAY(
     clip,
     clip,
     Clip,
     0,
     Display,
     mClip,
     eCSSType_Rect,
     nsnull,
-    CSS_PROP_NO_OFFSET,
-    eStyleAnimType_None)
+    offsetof(nsStyleDisplay, mClip),
+    eStyleAnimType_Custom)
 CSS_PROP_COLOR(
     color,
     color,
     Color,
     CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
     Color,
     mColor,
@@ -1518,18 +1518,18 @@ CSS_PROP_LIST(
     -moz-image-region,
     image_region,
     MozImageRegion,
     0,
     List,
     mImageRegion,
     eCSSType_Rect,
     nsnull,
-    CSS_PROP_NO_OFFSET,
-    eStyleAnimType_None)
+    offsetof(nsStyleList, mImageRegion),
+    eStyleAnimType_Custom)
 CSS_PROP_UIRESET(
     ime-mode,
     ime_mode,
     ImeMode,
     0,
     UserInterface,
     mIMEMode,
     eCSSType_Value,
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -239,16 +239,52 @@ nsStyleAnimation::ComputeDistance(nsCSSP
             return PR_FALSE;
         }
         squareDistance += diff * diff;
       }
 
       aDistance = sqrt(squareDistance);
       break;
     }
+    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()
+        return PR_FALSE;
+      }
+
+      double squareDistance = 0.0;
+      for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(nsCSSRect::sides); ++i) {
+        nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
+        NS_ABORT_IF_FALSE((rect1->*member).GetUnit() ==
+                            (rect2->*member).GetUnit(),
+                          "should have returned above");
+        double diff;
+        switch ((rect1->*member).GetUnit()) {
+          case eCSSUnit_Pixel:
+            diff = (rect1->*member).GetFloatValue() -
+                   (rect2->*member).GetFloatValue();
+            break;
+          case eCSSUnit_Auto:
+            diff = 0;
+            break;
+          default:
+            NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
+            return PR_FALSE;
+        }
+        squareDistance += diff * diff;
+      }
+
+      aDistance = sqrt(squareDistance);
+      break;
+    }
     case eUnit_Dasharray: {
       // NOTE: This produces results on substantially different scales
       // for length values and percentage values, which might even be
       // mixed in the same property value.  This means the result isn't
       // particularly useful for paced animation.
 
       // Call AddWeighted to make us lists of the same length.
       Value normValue1, normValue2;
@@ -361,16 +397,23 @@ nsStyleAnimation::ComputeDistance(nsCSSP
     default:
       NS_NOTREACHED("Can't compute distance using the given common unit");
       success = PR_FALSE;
       break;
   }
   return success;
 }
 
+inline void
+nscoordToCSSValue(nscoord aCoord, nsCSSValue& aCSSValue)
+{
+  aCSSValue.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(aCoord),
+                          eCSSUnit_Pixel);
+}
+
 #define MAX_PACKED_COLOR_COMPONENT 255
 
 inline PRUint8 ClampColor(double aColor)
 {
   if (aColor >= MAX_PACKED_COLOR_COMPONENT)
     return MAX_PACKED_COLOR_COMPONENT;
   if (aColor <= 0.0)
     return 0;
@@ -581,16 +624,60 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
             return PR_FALSE;
         }
       }
 
       aResultValue.SetAndAdoptCSSValuePairValue(result.forget(),
                                                 eUnit_CSSValuePair);
       break;
     }
+    case eUnit_CSSRect: {
+      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() ||
+          rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
+        // At least until we have calc()
+        return PR_FALSE;
+      }
+
+      nsAutoPtr<nsCSSRect> result(new nsCSSRect);
+      if (!result) {
+        return PR_FALSE;
+      }
+      for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(nsCSSRect::sides); ++i) {
+        nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
+        NS_ABORT_IF_FALSE((rect1->*member).GetUnit() ==
+                            (rect2->*member).GetUnit(),
+                          "should have returned above");
+        switch ((rect1->*member).GetUnit()) {
+          case eCSSUnit_Pixel:
+            (result->*member).SetFloatValue(
+              aCoeff1 * (rect1->*member).GetFloatValue() +
+              aCoeff2 * (rect2->*member).GetFloatValue(),
+              eCSSUnit_Pixel);
+            break;
+          case eCSSUnit_Auto:
+            if (float(aCoeff1 + aCoeff2) != 1.0f) {
+              // Interpolating between two auto values makes sense;
+              // adding in other ratios does not.
+              return PR_FALSE;
+            }
+            (result->*member).SetAutoValue();
+            break;
+          default:
+            NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
+            return PR_FALSE;
+        }
+      }
+
+      aResultValue.SetAndAdoptCSSRectValue(result.forget(), eUnit_CSSRect);
+      break;
+    }
     case eUnit_Dasharray: {
       const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
       const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
 
       PRUint32 len1 = 0, len2 = 0;
       for (const nsCSSValueList *v = list1; v; v = v->mNext) {
         ++len1;
       }
@@ -891,20 +978,18 @@ nsStyleAnimation::UncomputeValue(nsCSSPr
       NS_ABORT_IF_FALSE(nsCSSProps::kTypeTable[aProperty] == eCSSType_Value,
                         "type mismatch");
       static_cast<nsCSSValue*>(aSpecifiedValue)->
         SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Integer);
       break;
     case eUnit_Coord: {
       NS_ABORT_IF_FALSE(nsCSSProps::kTypeTable[aProperty] == eCSSType_Value,
                         "type mismatch");
-      float pxVal = aPresContext->AppUnitsToFloatCSSPixels(
-                                    aComputedValue.GetCoordValue());
-      static_cast<nsCSSValue*>(aSpecifiedValue)->
-        SetFloatValue(pxVal, eCSSUnit_Pixel);
+      nscoordToCSSValue(aComputedValue.GetCoordValue(),
+                        *static_cast<nsCSSValue*>(aSpecifiedValue));
       break;
     }
     case eUnit_Percent:
       NS_ABORT_IF_FALSE(nsCSSProps::kTypeTable[aProperty] == eCSSType_Value,
                         "type mismatch");
       static_cast<nsCSSValue*>(aSpecifiedValue)->
         SetPercentValue(aComputedValue.GetPercentValue());
       break;
@@ -931,16 +1016,22 @@ nsStyleAnimation::UncomputeValue(nsCSSPr
       }
       break;
     case eUnit_CSSValuePair:
       NS_ABORT_IF_FALSE(nsCSSProps::kTypeTable[aProperty] ==
                           eCSSType_ValuePair, "type mismatch");
       *static_cast<nsCSSValuePair*>(aSpecifiedValue) =
         *aComputedValue.GetCSSValuePairValue();
       break;
+    case eUnit_CSSRect:
+      NS_ABORT_IF_FALSE(nsCSSProps::kTypeTable[aProperty] ==
+                          eCSSType_Rect, "type mismatch");
+      *static_cast<nsCSSRect*>(aSpecifiedValue) =
+        *aComputedValue.GetCSSRectValue();
+      break;
     case eUnit_Dasharray:
     case eUnit_Shadow:
       NS_ABORT_IF_FALSE(nsCSSProps::kTypeTable[aProperty] ==
                           eCSSType_ValueList, "type mismatch");
       *static_cast<nsCSSValueList**>(aSpecifiedValue) =
         aComputedValue.GetCSSValueListValue();
       break;
     default:
@@ -1058,19 +1149,17 @@ StyleCoordToValue(const nsStyleCoord& aC
   return PR_TRUE;
 }
 
 static void
 StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
 {
   switch (aCoord.GetUnit()) {
     case eStyleUnit_Coord:
-      aCSSValue.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(
-                                aCoord.GetCoordValue()),
-                              eCSSUnit_Pixel);
+      nscoordToCSSValue(aCoord.GetCoordValue(), aCSSValue);
       break;
     case eStyleUnit_Percent:
       aCSSValue.SetPercentValue(aCoord.GetPercentValue());
       break;
     default:
       NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
   }
 }
@@ -1159,24 +1248,18 @@ nsStyleAnimation::ExtractComputedValue(n
 
         case eCSSProperty_border_spacing: {
           const nsStyleTableBorder *styleTableBorder =
             static_cast<const nsStyleTableBorder*>(styleStruct);
           nsCSSValuePair *pair = new nsCSSValuePair;
           if (!pair) {
             return PR_FALSE;
           }
-          pair->mXValue.SetFloatValue(
-            nsPresContext::AppUnitsToFloatCSSPixels(
-              styleTableBorder->mBorderSpacingX),
-            eCSSUnit_Pixel);
-          pair->mYValue.SetFloatValue(
-            nsPresContext::AppUnitsToFloatCSSPixels(
-              styleTableBorder->mBorderSpacingY),
-            eCSSUnit_Pixel);
+          nscoordToCSSValue(styleTableBorder->mBorderSpacingX, pair->mXValue);
+          nscoordToCSSValue(styleTableBorder->mBorderSpacingY, pair->mYValue);
           aComputedValue.SetAndAdoptCSSValuePairValue(pair,
                                                       eUnit_CSSValuePair);
           break;
         }
 
         case eCSSProperty__moz_transform_origin: {
           const nsStyleDisplay *styleDisplay =
             static_cast<const nsStyleDisplay*>(styleStruct);
@@ -1265,16 +1348,74 @@ nsStyleAnimation::ExtractComputedValue(n
             static_cast<const nsStyleFont*>(styleStruct)->mFont.weight;
           if (weight % 100 != 0) {
             return PR_FALSE;
           }
           aComputedValue.SetIntValue(weight, eUnit_Integer);
           return PR_TRUE;
         }
 
+        case eCSSProperty_image_region: {
+          const nsStyleList *list =
+            static_cast<const nsStyleList*>(styleStruct);
+          const nsRect &srect = list->mImageRegion;
+          if (srect.IsEmpty()) {
+            aComputedValue.SetAutoValue();
+            break;
+          }
+
+          nsCSSRect *vrect = new nsCSSRect;
+          if (!vrect) {
+            return PR_FALSE;
+          }
+          nscoordToCSSValue(srect.x, vrect->mLeft);
+          nscoordToCSSValue(srect.y, vrect->mTop);
+          nscoordToCSSValue(srect.XMost(), vrect->mRight);
+          nscoordToCSSValue(srect.YMost(), vrect->mBottom);
+          aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
+          break;
+        }
+
+        case eCSSProperty_clip: {
+          const nsStyleDisplay *display =
+            static_cast<const nsStyleDisplay*>(styleStruct);
+          if (!(display->mClipFlags & NS_STYLE_CLIP_RECT)) {
+            aComputedValue.SetAutoValue();
+            break;
+          }
+
+          nsCSSRect *vrect = new nsCSSRect;
+          if (!vrect) {
+            return PR_FALSE;
+          }
+          const nsRect &srect = display->mClip;
+          if (display->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) {
+            vrect->mTop.SetAutoValue();
+          } else {
+            nscoordToCSSValue(srect.y, vrect->mTop);
+          }
+          if (display->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) {
+            vrect->mRight.SetAutoValue();
+          } else {
+            nscoordToCSSValue(srect.XMost(), vrect->mRight);
+          }
+          if (display->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) {
+            vrect->mBottom.SetAutoValue();
+          } else {
+            nscoordToCSSValue(srect.YMost(), vrect->mBottom);
+          }
+          if (display->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) {
+            vrect->mLeft.SetAutoValue();
+          } else {
+            nscoordToCSSValue(srect.x, vrect->mLeft);
+          }
+          aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
+          break;
+        }
+
         default:
           NS_ABORT_IF_FALSE(PR_FALSE, "missing property implementation");
           return PR_FALSE;
       };
       return PR_TRUE;
     case eStyleAnimType_Coord:
       return StyleCoordToValue(*static_cast<const nsStyleCoord*>(
         StyleDataAtOffset(styleStruct, ssOffset)), aComputedValue);
@@ -1371,30 +1512,22 @@ nsStyleAnimation::ExtractComputedValue(n
         return PR_TRUE;
       }
       nsAutoPtr<nsCSSValueList> result;
       nsCSSValueList **resultTail = getter_Transfers(result);
       for (PRUint32 i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
         const nsCSSShadowItem *shadow = shadowArray->ShadowAt(i);
         // X, Y, Radius, Spread, Color, Inset
         nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(6);
-        arr->Item(0).SetFloatValue(
-          nsPresContext::AppUnitsToFloatCSSPixels(shadow->mXOffset),
-          eCSSUnit_Pixel);
-        arr->Item(1).SetFloatValue(
-          nsPresContext::AppUnitsToFloatCSSPixels(shadow->mYOffset),
-          eCSSUnit_Pixel);
-        arr->Item(2).SetFloatValue(
-          nsPresContext::AppUnitsToFloatCSSPixels(shadow->mRadius),
-          eCSSUnit_Pixel);
+        nscoordToCSSValue(shadow->mXOffset, arr->Item(0));
+        nscoordToCSSValue(shadow->mYOffset, arr->Item(1));
+        nscoordToCSSValue(shadow->mRadius, arr->Item(2));
         // NOTE: This code sometimes stores mSpread: 0 even when
         // the parser would be required to leave it null.
-        arr->Item(3).SetFloatValue(
-          nsPresContext::AppUnitsToFloatCSSPixels(shadow->mSpread),
-          eCSSUnit_Pixel);
+        nscoordToCSSValue(shadow->mSpread, arr->Item(3));
         if (shadow->mHasColor) {
           arr->Item(4).SetColorValue(shadow->mColor);
         }
         if (shadow->mInset) {
           arr->Item(5).SetIntValue(NS_STYLE_BOX_SHADOW_INSET,
                                    eCSSUnit_Enumerated);
         }
 
@@ -1477,16 +1610,23 @@ 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_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:
     case eUnit_Shadow:
       NS_ABORT_IF_FALSE(mUnit != eUnit_Dasharray || aOther.mValue.mCSSValueList,
                         "dasharrays may not be null");
       if (aOther.mValue.mCSSValueList) {
         mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
         if (!mValue.mCSSValueList) {
           mUnit = eUnit_Null;
@@ -1587,16 +1727,26 @@ 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::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
+}
+
+void
 nsStyleAnimation::Value::SetAndAdoptCSSValueListValue(
                            nsCSSValueList *aValueList, Unit aUnit)
 {
   FreeValue();
   NS_ABORT_IF_FALSE(IsCSSValueListUnit(aUnit), "bad unit");
   NS_ABORT_IF_FALSE(aUnit != eUnit_Dasharray || aValueList != nsnull,
                     "dasharrays may not be null");
   mUnit = aUnit;
@@ -1605,16 +1755,18 @@ nsStyleAnimation::Value::SetAndAdoptCSSV
 
 void
 nsStyleAnimation::Value::FreeValue()
 {
   if (IsCSSValueListUnit(mUnit)) {
     delete mValue.mCSSValueList;
   } else if (IsCSSValuePairUnit(mUnit)) {
     delete mValue.mCSSValuePair;
+  } else if (IsCSSRectUnit(mUnit)) {
+    delete mValue.mCSSRect;
   } else if (IsStringUnit(mUnit)) {
     NS_ABORT_IF_FALSE(mValue.mString, "expecting non-null string");
     mValue.mString->Release();
   }
 }
 
 PRBool
 nsStyleAnimation::Value::operator==(const Value& aOther) const
@@ -1636,16 +1788,18 @@ nsStyleAnimation::Value::operator==(cons
       return mValue.mCoord == aOther.mValue.mCoord;
     case eUnit_Percent:
     case eUnit_Float:
       return mValue.mFloat == aOther.mValue.mFloat;
     case eUnit_Color:
       return mValue.mColor == aOther.mValue.mColor;
     case eUnit_CSSValuePair:
       return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
+    case eUnit_CSSRect:
+      return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
     case eUnit_Dasharray:
     case eUnit_Shadow:
       return nsCSSValueList::Equal(mValue.mCSSValueList,
                                    aOther.mValue.mCSSValueList);
     case eUnit_UnparsedString:
       return (NS_strcmp(GetStringBufferValue(),
                         aOther.GetStringBufferValue()) == 0);
   }
--- a/layout/style/nsStyleAnimation.h
+++ b/layout/style/nsStyleAnimation.h
@@ -51,16 +51,17 @@
 #include "nsColor.h"
 
 class nsCSSDeclaration;
 class nsIContent;
 class nsPresContext;
 class nsStyleContext;
 struct nsCSSValueList;
 struct nsCSSValuePair;
+struct nsCSSRect;
 
 /**
  * Utility class to handle animated style values
  */
 class nsStyleAnimation {
 public:
   class Value;
 
@@ -229,30 +230,32 @@ public:
     eUnit_None,
     eUnit_Enumerated,
     eUnit_Integer,
     eUnit_Coord,
     eUnit_Percent,
     eUnit_Float,
     eUnit_Color,
     eUnit_CSSValuePair, // nsCSSValuePair* (never null)
+    eUnit_CSSRect, // nsCSSRect* (never null)
     eUnit_Dasharray, // nsCSSValueList* (never null)
     eUnit_Shadow, // nsCSSValueList* (may be null)
     eUnit_UnparsedString // nsStringBuffer* (never null)
   };
 
   class Value {
   private:
     Unit mUnit;
     union {
       PRInt32 mInt;
       nscoord mCoord;
       float mFloat;
       nscolor mColor;
       nsCSSValuePair* mCSSValuePair;
+      nsCSSRect* mCSSRect;
       nsCSSValueList* mCSSValueList;
       nsStringBuffer* mString;
     } mValue;
   public:
     Unit GetUnit() const {
       NS_ASSERTION(mUnit != eUnit_Null, "uninitialized");
       return mUnit;
     }
@@ -282,16 +285,20 @@ public:
     nscolor GetColorValue() const {
       NS_ASSERTION(mUnit == eUnit_Color, "unit mismatch");
       return mValue.mColor;
     }
     nsCSSValuePair* GetCSSValuePairValue() const {
       NS_ASSERTION(IsCSSValuePairUnit(mUnit), "unit mismatch");
       return mValue.mCSSValuePair;
     }
+    nsCSSRect* GetCSSRectValue() const {
+      NS_ASSERTION(IsCSSRectUnit(mUnit), "unit mismatch");
+      return mValue.mCSSRect;
+    }
     nsCSSValueList* GetCSSValueListValue() const {
       NS_ASSERTION(IsCSSValueListUnit(mUnit), "unit mismatch");
       return mValue.mCSSValueList;
     }
     const PRUnichar* GetStringBufferValue() const {
       NS_ASSERTION(IsStringUnit(mUnit), "unit mismatch");
       return GetBufferValue(mValue.mString);
     }
@@ -331,16 +338,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 SetAndAdoptCSSValueListValue(nsCSSValueList *aValue, Unit aUnit);
     void SetAndAdoptCSSValuePairValue(nsCSSValuePair *aValue, Unit aUnit);
+    void SetAndAdoptCSSRectValue(nsCSSRect *aValue, Unit aUnit);
 
     Value& operator=(const Value& aOther);
 
     PRBool operator==(const Value& aOther) const;
     PRBool operator!=(const Value& aOther) const
       { return !(*this == aOther); }
 
   private:
@@ -351,16 +359,19 @@ public:
     }
 
     static PRBool IsIntUnit(Unit aUnit) {
       return aUnit == eUnit_Enumerated || aUnit == eUnit_Integer;
     }
     static PRBool IsCSSValuePairUnit(Unit aUnit) {
       return aUnit == eUnit_CSSValuePair;
     }
+    static PRBool IsCSSRectUnit(Unit aUnit) {
+      return aUnit == eUnit_CSSRect;
+    }
     static PRBool IsCSSValueListUnit(Unit aUnit) {
       return aUnit == eUnit_Dasharray || aUnit == eUnit_Shadow;
     }
     static PRBool IsStringUnit(Unit aUnit) {
       return aUnit == eUnit_UnparsedString;
     }
   };
 };
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1297,24 +1297,17 @@ struct nsStyleDisplay {
 #ifdef DEBUG
   static nsChangeHint MaxDifference();
 #endif
   static PRBool ForceCompare() { return PR_TRUE; }
 
   // We guarantee that if mBinding is non-null, so are mBinding->mURI and
   // mBinding->mOriginPrincipal.
   nsRefPtr<nsCSSValue::URL> mBinding;    // [reset]
-#if 0
-  // XXX This is how it is defined in the CSS2 spec, but the errata
-  // changed it to be consistent with the positioning draft and how
-  // Nav and IE implement it
-  nsMargin  mClip;              // [reset] offsets from respective edge
-#else
   nsRect    mClip;              // [reset] offsets from upper-left border edge
-#endif
   float   mOpacity;             // [reset]
   PRUint8 mDisplay;             // [reset] see nsStyleConsts.h NS_STYLE_DISPLAY_*
   PRUint8 mOriginalDisplay;     // [reset] saved mDisplay for position:absolute/fixed
   PRUint8 mAppearance;          // [reset]
   PRUint8 mPosition;            // [reset] see nsStyleConsts.h
   PRUint8 mFloats;              // [reset] see nsStyleConsts.h NS_STYLE_FLOAT_*
   PRUint8 mBreakType;           // [reset] see nsStyleConsts.h NS_STYLE_CLEAR_*
   PRPackedBool mBreakBefore;    // [reset] 
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -54,16 +54,17 @@ var supported_properties = {
     "-moz-box-flex": [ test_float_zeroToOne_transition,
                        test_float_aboveOne_transition ],
     "-moz-box-shadow": [ test_shadow_transition ],
     "-moz-column-count": [ test_pos_integer_or_auto_transition ],
     "-moz-column-gap": [ test_length_transition ],
     "-moz-column-rule-color": [ test_color_transition ],
     "-moz-column-rule-width": [ test_length_transition ],
     "-moz-column-width": [ test_length_transition ],
+    "-moz-image-region": [ test_rect_transition ],
     "-moz-outline-radius-bottomleft": [ test_radius_transition ],
     "-moz-outline-radius-bottomright": [ test_radius_transition ],
     "-moz-outline-radius-topleft": [ test_radius_transition ],
     "-moz-outline-radius-topright": [ test_radius_transition ],
     "-moz-transform-origin": [ test_length_pair_transition,
                                test_length_percent_pair_transition ],
     "background-color": [ test_color_transition ],
     "border-bottom-color": [ test_color_transition ],
@@ -71,16 +72,17 @@ var supported_properties = {
     "border-left-color": [ test_color_transition ],
     "border-left-width": [ test_length_transition ],
     "border-right-color": [ test_color_transition ],
     "border-right-width": [ test_length_transition ],
     "border-spacing": [ test_length_pair_transition ],
     "border-top-color": [ test_color_transition ],
     "border-top-width": [ test_length_transition ],
     "bottom": [ test_length_transition, test_percent_transition ],
+    "clip": [ test_rect_transition ],
     "color": [ test_color_transition ],
     "fill": [ test_color_transition ],
     "fill-opacity" : [ test_float_zeroToOne_transition ],
     "flood-color": [ test_color_transition ],
     "flood-opacity" : [ test_float_zeroToOne_transition ],
     "font-size": [ test_length_transition, test_percent_transition ],
     "font-size-adjust": [ test_float_zeroToOne_transition,
                           test_float_aboveOne_transition ],
@@ -521,12 +523,32 @@ function test_length_percent_pair_transi
   is(cs.getPropertyValue(prop), "4px 50%",
      "length-valued property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "12px 70%", "");
   is(cs.getPropertyValue(prop), "8px 60%",
      "length-valued property " + prop + ": interpolation of lengths");
 }
 
+function test_rect_transition(prop) {
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "rect(4px, 16px, 12px, 8px)", "");
+  is(cs.getPropertyValue(prop), "rect(4px, 16px, 12px, 8px)",
+     "rect-valued property " + prop + ": computed value before transition");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "rect(0px, 6px, 4px, 2px)", "");
+  is(cs.getPropertyValue(prop), "rect(2px, 11px, 8px, 5px)",
+     "rect-valued property " + prop + ": interpolation of rects");
+  if (prop == "clip") {
+    div.style.setProperty(prop, "rect(0px, 6px, 4px, auto)", "");
+    is(cs.getPropertyValue(prop), "rect(0px, 6px, 4px, auto)",
+       "rect-valued property " + prop + ": can't interpolate auto components");
+    div.style.setProperty(prop, "rect(0px, 6px, 4px, 2px)", "");
+  }
+  div.style.setProperty(prop, "auto", "");
+  is(cs.getPropertyValue(prop), "auto",
+     "rect-valued property " + prop + ": can't interpolate auto components");
+}
+
 </script>
 </pre>
 </body>
 </html>