Bug 773296 - Part 16: Add a ref-counted list nsCSSValue unit and use it for tranform lists; hold a strong reference to one on nsStyleDisplay. r=dbaron
authorCameron McCormack <cam@mcc.id.au>
Thu, 12 Dec 2013 13:09:44 +1100
changeset 160110 f5acf7d3cd8f8caaa3c17734d529ba78e5e5efc7
parent 160109 ec31b9795a5df73092f822b5d315a0c56ad7460e
child 160111 c576c10f4a172e56e5cb5048cc2cc14e5a93dad5
push id3938
push usercbook@mozilla.com
push dateThu, 12 Dec 2013 15:09:56 +0000
treeherderfx-team@0cf1107e2791 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs773296
milestone29.0a1
Bug 773296 - Part 16: Add a ref-counted list nsCSSValue unit and use it for tranform lists; hold a strong reference to one on nsStyleDisplay. r=dbaron This adds a new eCSSUnit_SharedList type for nsCSSValue, which is a reference counted object that contains an nsCSSValueList. We need this so that nsStyleDisplay::mSpecifiedTransform can hold a strong reference to a specified transform list value. When 'transform' is specified using a variable reference, the resulting nsCSSValue does not stick around in the Declaration object, so we wouldn't be guaranteed that it lives long enough for nsStyleDisplay to keep referencing it.
gfx/layers/composite/AsyncCompositionManager.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
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.h
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -35,17 +35,17 @@
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
 #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
 #if defined(MOZ_WIDGET_ANDROID)
 # include <android/log.h>
 # include "AndroidBridge.h"
 #endif
 #include "GeckoProfiler.h"
 
-struct nsCSSValueList;
+struct nsCSSValueSharedList;
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace layers {
 
 enum Op { Resolve, Detach };
 
@@ -343,17 +343,18 @@ SampleValue(float aPortion, Animation& a
                aEnd.GetUnit() == nsStyleAnimation::eUnit_None, "Must have same unit");
   nsStyleAnimation::Interpolate(aAnimation.property(), aStart, aEnd,
                                 aPortion, interpolatedValue);
   if (aAnimation.property() == eCSSProperty_opacity) {
     *aValue = interpolatedValue.GetFloatValue();
     return;
   }
 
-  nsCSSValueList* interpolatedList = interpolatedValue.GetCSSValueListValue();
+  nsCSSValueSharedList* interpolatedList =
+    interpolatedValue.GetCSSValueSharedListValue();
 
   TransformData& data = aAnimation.data().get_TransformData();
   nsPoint origin = data.origin();
   // we expect all our transform data to arrive in css pixels, so here we must
   // adjust to dev pixels.
   double cssPerDev = double(nsDeviceContext::AppUnitsPerCSSPixel())
                      / double(data.appUnitsPerDevPixel());
   gfxPoint3D transformOrigin = data.transformOrigin();
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -4054,17 +4054,17 @@ nsDisplayTransform::GetResultingTransfor
   gfx3DMatrix result;
   // Call IsSVGTransformed() regardless of the value of
   // disp->mSpecifiedTransform, since we still need any transformFromSVGParent.
   gfxMatrix svgTransform, transformFromSVGParent;
   bool hasSVGTransforms =
     frame && frame->IsSVGTransformed(&svgTransform, &transformFromSVGParent);
   /* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */
   if (aProperties.mTransformList) {
-    result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList,
+    result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead,
                                                     frame ? frame->StyleContext() : nullptr,
                                                     frame ? frame->PresContext() : nullptr,
                                                     dummy, bounds, aAppUnitsPerPixel);
   } else if (hasSVGTransforms) {
     // Correct the translation components for zoom:
     float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() /
                              aAppUnitsPerPixel;
     svgTransform.x0 *= pixelsPerCSSPx;
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -3032,29 +3032,29 @@ public:
    */
   static nsRect GetFrameBoundsForTransform(const nsIFrame* aFrame);
 
   struct FrameTransformProperties
   {
     FrameTransformProperties(const nsIFrame* aFrame,
                              float aAppUnitsPerPixel,
                              const nsRect* aBoundsOverride);
-    FrameTransformProperties(const nsCSSValueList* aTransformList,
+    FrameTransformProperties(nsCSSValueSharedList* aTransformList,
                              const gfxPoint3D& aToTransformOrigin,
                              const gfxPoint3D& aToPerspectiveOrigin,
                              nscoord aChildPerspective)
       : mFrame(nullptr)
       , mTransformList(aTransformList)
       , mToTransformOrigin(aToTransformOrigin)
       , mToPerspectiveOrigin(aToPerspectiveOrigin)
       , mChildPerspective(aChildPerspective)
     {}
 
     const nsIFrame* mFrame;
-    const nsCSSValueList* mTransformList;
+    nsRefPtr<nsCSSValueSharedList> mTransformList;
     const gfxPoint3D mToTransformOrigin;
     const gfxPoint3D mToPerspectiveOrigin;
     nscoord mChildPerspective;
   };
 
   /**
    * Given a frame with the -moz-transform property or an SVG transform,
    * returns the transformation matrix for that frame.
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -11174,17 +11174,20 @@ bool CSSParserImpl::ParseTransform(bool 
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
     // 'inherit', 'initial', 'unset' and 'none' must be alone
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
-    nsCSSValueList* cur = value.SetListValue();
+    nsCSSValueSharedList* list = new nsCSSValueSharedList;
+    value.SetSharedListValue(list);
+    list->mHead = new nsCSSValueList;
+    nsCSSValueList* cur = list->mHead;
     for (;;) {
       if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) {
         return false;
       }
       if (CheckEndProperty()) {
         break;
       }
       cur->mNext = new nsCSSValueList;
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -157,16 +157,20 @@ nsCSSValue::nsCSSValue(const nsCSSValue&
   }
   else if (eCSSUnit_List == mUnit) {
     mValue.mList = aCopy.mValue.mList;
     mValue.mList->AddRef();
   }
   else if (eCSSUnit_ListDep == mUnit) {
     mValue.mListDependent = aCopy.mValue.mListDependent;
   }
+  else if (eCSSUnit_SharedList == mUnit) {
+    mValue.mSharedList = aCopy.mValue.mSharedList;
+    mValue.mSharedList->AddRef();
+  }
   else if (eCSSUnit_PairList == mUnit) {
     mValue.mPairList = aCopy.mValue.mPairList;
     mValue.mPairList->AddRef();
   }
   else if (eCSSUnit_PairListDep == mUnit) {
     mValue.mPairListDependent = aCopy.mValue.mPairListDependent;
   }
   else {
@@ -227,16 +231,19 @@ bool nsCSSValue::operator==(const nsCSSV
       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_SharedList == mUnit) {
+      return *mValue.mSharedList == *aOther.mValue.mSharedList;
+    }
     else if (eCSSUnit_PairList == mUnit) {
       return *mValue.mPairList == *aOther.mValue.mPairList;
     }
     else {
       return mValue.mFloat == aOther.mValue.mFloat;
     }
   }
   return false;
@@ -310,16 +317,18 @@ void nsCSSValue::DoReset()
   } 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_SharedList == mUnit) {
+    mValue.mSharedList->Release();
   } else if (eCSSUnit_PairList == mUnit) {
     mValue.mPairList->Release();
   }
   mUnit = eCSSUnit_Null;
 }
 
 void nsCSSValue::SetIntValue(int32_t aValue, nsCSSUnit aUnit)
 {
@@ -508,16 +517,24 @@ nsCSSValueList* nsCSSValue::SetListValue
 {
   Reset();
   mUnit = eCSSUnit_List;
   mValue.mList = new nsCSSValueList_heap;
   mValue.mList->AddRef();
   return mValue.mList;
 }
 
+void nsCSSValue::SetSharedListValue(nsCSSValueSharedList* aList)
+{
+  Reset();
+  mUnit = eCSSUnit_SharedList;
+  mValue.mSharedList = aList;
+  mValue.mSharedList->AddRef();
+}
+
 void nsCSSValue::SetDependentListValue(nsCSSValueList* aList)
 {
   Reset();
   if (aList) {
     mUnit = eCSSUnit_ListDep;
     mValue.mListDependent = aList;
   }
 }
@@ -1193,16 +1210,18 @@ nsCSSValue::AppendToString(nsCSSProperty
       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_SharedList == unit) {
+    GetSharedListValue()->AppendToString(aProperty, aResult);
   } else if (eCSSUnit_PairList == unit || eCSSUnit_PairListDep == unit) {
     switch (aProperty) {
       case eCSSProperty_font_feature_settings:
         nsStyleUtil::AppendFontFeatureSettings(*this, aResult);
         break;
       default:
         GetPairListValue()->AppendToString(aProperty, aResult);
         break;
@@ -1253,16 +1272,17 @@ nsCSSValue::AppendToString(nsCSSProperty
     case eCSSUnit_Number:       break;
     case eCSSUnit_Gradient:     break;
     case eCSSUnit_TokenStream:  break;
     case eCSSUnit_Pair:         break;
     case eCSSUnit_Triplet:      break;
     case eCSSUnit_Rect:         break;
     case eCSSUnit_List:         break;
     case eCSSUnit_ListDep:      break;
+    case eCSSUnit_SharedList:   break;
     case eCSSUnit_PairList:     break;
     case eCSSUnit_PairListDep:  break;
 
     case eCSSUnit_Inch:         aResult.AppendLiteral("in");   break;
     case eCSSUnit_Millimeter:   aResult.AppendLiteral("mm");   break;
     case eCSSUnit_PhysicalMillimeter: aResult.AppendLiteral("mozmm");   break;
     case eCSSUnit_Centimeter:   aResult.AppendLiteral("cm");   break;
     case eCSSUnit_Point:        aResult.AppendLiteral("pt");   break;
@@ -1379,16 +1399,22 @@ nsCSSValue::SizeOfExcludingThis(mozilla:
     case eCSSUnit_List:
       n += mValue.mList->SizeOfIncludingThis(aMallocSizeOf);
       break;
 
     // ListDep: not measured because it's non-owning.
     case eCSSUnit_ListDep:
       break;
 
+    // SharedList
+    case eCSSUnit_SharedList:
+      // Makes more sense not to measure, since it most cases the list
+      // will be shared.
+      break;
+
     // PairList
     case eCSSUnit_PairList:
       n += mValue.mPairList->SizeOfIncludingThis(aMallocSizeOf);
       break;
 
     // PairListDep: not measured because it's non-owning.
     case eCSSUnit_PairListDep:
       break;
@@ -1517,16 +1543,51 @@ size_t
 nsCSSValueList_heap::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
   n += mValue.SizeOfExcludingThis(aMallocSizeOf);
   n += mNext ? mNext->SizeOfIncludingThis(aMallocSizeOf) : 0;
   return n;
 }
 
+// --- nsCSSValueSharedList -----------------
+
+nsCSSValueSharedList::~nsCSSValueSharedList()
+{
+  MOZ_COUNT_DTOR(nsCSSValueSharedList);
+  if (mHead) {
+    NS_CSS_DELETE_LIST_MEMBER(nsCSSValueList, mHead, mNext);
+    delete mHead;
+  }
+}
+
+void
+nsCSSValueSharedList::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const
+{
+  if (mHead) {
+    mHead->AppendToString(aProperty, aResult);
+  }
+}
+
+bool
+nsCSSValueSharedList::operator==(const nsCSSValueSharedList& aOther) const
+{
+  return !mHead == !aOther.mHead &&
+         (!mHead || *mHead == *aOther.mHead);
+}
+
+size_t
+nsCSSValueSharedList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = 0;
+  n += aMallocSizeOf(this);
+  n += mHead->SizeOfIncludingThis(aMallocSizeOf);
+  return n;
+}
+
 // --- nsCSSRect -----------------
 
 nsCSSRect::nsCSSRect(void)
 {
   MOZ_COUNT_CTOR(nsCSSRect);
 }
 
 nsCSSRect::nsCSSRect(const nsCSSRect& aCopy)
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -192,18 +192,20 @@ enum nsCSSUnit {
   eCSSUnit_TokenStream  = 43,     // (nsCSSValueTokenStream*) value
 
   eCSSUnit_Pair         = 50,     // (nsCSSValuePair*) pair of values
   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     = 55,     // (nsCSSValuePairList*) list of value pairs
-  eCSSUnit_PairListDep  = 56,     // (nsCSSValuePairList*) same as PairList
+  eCSSUnit_SharedList   = 55,     // (nsCSSValueSharedList*) same as list
+                                  //   but reference counted and shared
+  eCSSUnit_PairList     = 56,     // (nsCSSValuePairList*) list of value pairs
+  eCSSUnit_PairListDep  = 57,     // (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
 
@@ -252,16 +254,17 @@ enum nsCSSUnit {
 struct nsCSSValueGradient;
 struct nsCSSValuePair;
 struct nsCSSValuePair_heap;
 struct nsCSSValueTokenStream;
 struct nsCSSRect;
 struct nsCSSRect_heap;
 struct nsCSSValueList;
 struct nsCSSValueList_heap;
+struct nsCSSValueSharedList;
 struct nsCSSValuePairList;
 struct nsCSSValuePairList_heap;
 struct nsCSSValueTriplet;
 struct nsCSSValueTriplet_heap;
 
 class nsCSSValue {
 public:
   struct Array;
@@ -425,16 +428,22 @@ public:
   }
 
   nsCSSValueTokenStream* GetTokenStreamValue() const
   {
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_TokenStream, "not a token stream value");
     return mValue.mTokenStream;
   }
 
+  nsCSSValueSharedList* GetSharedListValue() const
+  {
+    NS_ABORT_IF_FALSE(mUnit == eCSSUnit_SharedList, "not a shared list value");
+    return mValue.mSharedList;
+  }
+
   // bodies of these are below
   inline nsCSSValuePair& GetPairValue();
   inline const nsCSSValuePair& GetPairValue() const;
 
   inline nsCSSRect& GetRectValue();
   inline const nsCSSRect& GetRectValue() const;
 
   inline nsCSSValueList* GetListValue();
@@ -493,16 +502,17 @@ public:
   void SetColorValue(nscolor aValue);
   void SetArrayValue(nsCSSValue::Array* aArray, nsCSSUnit aUnit);
   void SetURLValue(mozilla::css::URLValue* aURI);
   void SetImageValue(mozilla::css::ImageValue* aImage);
   void SetGradientValue(nsCSSValueGradient* aGradient);
   void SetTokenStreamValue(nsCSSValueTokenStream* aTokenStream);
   void SetPairValue(const nsCSSValuePair* aPair);
   void SetPairValue(const nsCSSValue& xValue, const nsCSSValue& yValue);
+  void SetSharedListValue(nsCSSValueSharedList* aList);
   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 SetUnsetValue();
@@ -552,16 +562,17 @@ protected:
     mozilla::css::ImageValue* mImage;
     nsCSSValueGradient* mGradient;
     nsCSSValueTokenStream* mTokenStream;
     nsCSSValuePair_heap* mPair;
     nsCSSRect_heap* mRect;
     nsCSSValueTriplet_heap* mTriplet;
     nsCSSValueList_heap* mList;
     nsCSSValueList* mListDependent;
+    nsCSSValueSharedList* mSharedList;
     nsCSSValuePairList_heap* mPairList;
     nsCSSValuePairList* mPairListDependent;
   } mValue;
 };
 
 struct nsCSSValue::Array {
 
   // return |Array| with reference count of zero
@@ -696,16 +707,48 @@ private:
 // refcounted.  It should not be necessary to use this class directly;
 // it's an implementation detail of nsCSSValue.
 struct nsCSSValueList_heap : public nsCSSValueList {
   NS_INLINE_DECL_REFCOUNTING(nsCSSValueList_heap)
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 };
 
+// This is a reference counted list value.  Note that the object is
+// a wrapper for the reference count and a pointer to the head of the
+// list, whereas the other list types (such as nsCSSValueList) do
+// not have such a wrapper.
+struct nsCSSValueSharedList {
+  nsCSSValueSharedList()
+  {
+    MOZ_COUNT_CTOR(nsCSSValueSharedList);
+  }
+
+  // Takes ownership of aList.
+  nsCSSValueSharedList(nsCSSValueList* aList)
+    : mHead(aList)
+  {
+    MOZ_COUNT_CTOR(nsCSSValueSharedList);
+  }
+
+  ~nsCSSValueSharedList();
+
+  NS_INLINE_DECL_REFCOUNTING(nsCSSValueSharedList)
+
+  void AppendToString(nsCSSProperty aProperty, nsAString& aResult) const;
+
+  bool operator==(nsCSSValueSharedList const& aOther) const;
+  bool operator!=(const nsCSSValueSharedList& aOther) const
+  { return !(*this == aOther); }
+
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+  nsCSSValueList* mHead;
+};
+
 // This has to be here so that the relationship between nsCSSValueList
 // and nsCSSValueList_heap is visible.
 inline nsCSSValueList*
 nsCSSValue::GetListValue()
 {
   if (mUnit == eCSSUnit_List)
     return mValue.mList;
   else {
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1227,17 +1227,17 @@ nsComputedDOMStyle::DoGetTransform()
    * use this approach.
    */
   nsRect bounds =
     (mInnerFrame ? nsDisplayTransform::GetFrameBoundsForTransform(mInnerFrame) :
      nsRect(0, 0, 0, 0));
 
    bool dummy;
    gfx3DMatrix matrix =
-     nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform,
+     nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform->mHead,
                                             mStyleContextHolder,
                                             mStyleContextHolder->PresContext(),
                                             dummy,
                                             bounds,
                                             float(mozilla::AppUnitsPerCSSPixel()));
 
   return MatrixToCSSValue(matrix);
 }
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -5349,25 +5349,26 @@ nsRuleNode::ComputeDisplayData(void* aSt
     display->mSpecifiedTransform = nullptr;
     break;
 
   case eCSSUnit_Inherit:
     display->mSpecifiedTransform = parentDisplay->mSpecifiedTransform;
     canStoreInRuleTree = false;
     break;
 
-  case eCSSUnit_List:
-  case eCSSUnit_ListDep: {
-    const nsCSSValueList* head = transformValue->GetListValue();
+  case eCSSUnit_SharedList: {
+    nsCSSValueSharedList* list = transformValue->GetSharedListValue();
+    nsCSSValueList* head = list->mHead;
+    MOZ_ASSERT(head, "transform list must have at least one item");
     // can get a _None in here from transform animation
     if (head->mValue.GetUnit() == eCSSUnit_None) {
       NS_ABORT_IF_FALSE(head->mNext == nullptr, "none must be alone");
       display->mSpecifiedTransform = nullptr;
     } else {
-      display->mSpecifiedTransform = head; // weak pointer, owned by rule
+      display->mSpecifiedTransform = list;
     }
     break;
   }
 
   default:
     NS_ABORT_IF_FALSE(false, "unrecognized transform unit");
   }
 
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -2272,18 +2272,21 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
                         "resultTail isn't pointing to the tail (may leak)");
 
       aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
                                                 eUnit_Filter);
       return true;
     }
 
     case eUnit_Transform: {
-      const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
-      const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
+      const nsCSSValueList* list1 = aValue1.GetCSSValueSharedListValue()->mHead;
+      const nsCSSValueList* list2 = aValue2.GetCSSValueSharedListValue()->mHead;
+
+      MOZ_ASSERT(list1);
+      MOZ_ASSERT(list2);
 
       // We want to avoid the matrix decomposition when we can, since
       // avoiding it can produce better results both for compound
       // transforms and for skew and skewY (see below).  We can do this
       // in two cases:
       //   (1) if one of the transforms is 'none'
       //   (2) if the lists have the same length and the transform
       //       functions match
@@ -2327,18 +2330,17 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
           if (match) {
             result = AddTransformLists(aCoeff1, list1, aCoeff2, list2);
           } else {
             result = AddDifferentTransformLists(aCoeff1, list1, aCoeff2, list2);
           }
         }
       }
 
-      aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
-                                                eUnit_Transform);
+      aResultValue.SetTransformValue(new nsCSSValueSharedList(result.forget()));
       return true;
     }
     case eUnit_BackgroundPosition: {
       const nsCSSValueList *position1 = aValue1.GetCSSValueListValue();
       const nsCSSValueList *position2 = aValue2.GetCSSValueListValue();
       nsAutoPtr<nsCSSValueList> result;
       nsCSSValueList **resultTail = getter_Transfers(result);
       while (position1 && position2) {
@@ -2644,21 +2646,24 @@ nsStyleAnimation::UncomputeValue(nsCSSPr
     } break;
     case eUnit_CSSRect: {
       nsCSSRect& rect = aSpecifiedValue.SetRectValue();
       rect = *aComputedValue.GetCSSRectValue();
     } break;
     case eUnit_Dasharray:
     case eUnit_Shadow:
     case eUnit_Filter:
-    case eUnit_Transform:
     case eUnit_BackgroundPosition:
       aSpecifiedValue.
         SetDependentListValue(aComputedValue.GetCSSValueListValue());
       break;
+    case eUnit_Transform:
+      aSpecifiedValue.
+        SetSharedListValue(aComputedValue.GetCSSValueSharedListValue());
+      break;
     case eUnit_CSSValuePairList:
       aSpecifiedValue.
         SetDependentPairListValue(aComputedValue.GetCSSValuePairListValue());
       break;
     default:
       return false;
   }
   return true;
@@ -3303,31 +3308,31 @@ nsStyleAnimation::ExtractComputedValue(n
 
         case eCSSProperty_transform: {
           const nsStyleDisplay *display =
             static_cast<const nsStyleDisplay*>(styleStruct);
           nsAutoPtr<nsCSSValueList> result;
           if (display->mSpecifiedTransform) {
             // Clone, and convert all lengths (not percents) to pixels.
             nsCSSValueList **resultTail = getter_Transfers(result);
-            for (const nsCSSValueList *l = display->mSpecifiedTransform;
+            for (const nsCSSValueList *l = display->mSpecifiedTransform->mHead;
                  l; l = l->mNext) {
               nsCSSValueList *clone = new nsCSSValueList;
               *resultTail = clone;
               resultTail = &clone->mNext;
 
               SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
             }
           } else {
             result = new nsCSSValueList();
             result->mValue.SetNoneValue();
           }
 
-          aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
-                                                      eUnit_Transform);
+          aComputedValue.SetTransformValue(
+              new nsCSSValueSharedList(result.forget()));
           break;
         }
 
         default:
           NS_ABORT_IF_FALSE(false, "missing property implementation");
           return false;
       };
       return true;
@@ -3563,30 +3568,33 @@ nsStyleAnimation::Value::operator=(const
       mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect);
       if (!mValue.mCSSRect) {
         mUnit = eUnit_Null;
       }
       break;
     case eUnit_Filter:
     case eUnit_Dasharray:
     case eUnit_Shadow:
-    case eUnit_Transform:
     case eUnit_BackgroundPosition:
       NS_ABORT_IF_FALSE(mUnit == eUnit_Shadow || mUnit == eUnit_Filter ||
                         aOther.mValue.mCSSValueList,
                         "value lists other than shadows and filters may not be null");
       if (aOther.mValue.mCSSValueList) {
         mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
         if (!mValue.mCSSValueList) {
           mUnit = eUnit_Null;
         }
       } else {
         mValue.mCSSValueList = nullptr;
       }
       break;
+    case eUnit_Transform:
+      mValue.mCSSValueSharedList = aOther.mValue.mCSSValueSharedList;
+      mValue.mCSSValueSharedList->AddRef();
+      break;
     case eUnit_CSSValuePairList:
       NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePairList,
                         "value pair lists may not be null");
       mValue.mCSSValuePairList = aOther.mValue.mCSSValuePairList->Clone();
       if (!mValue.mCSSValuePairList) {
         mUnit = eUnit_Null;
       }
       break;
@@ -3724,32 +3732,43 @@ nsStyleAnimation::Value::SetAndAdoptCSSV
   NS_ABORT_IF_FALSE(aUnit != eUnit_Dasharray || aUnit != eUnit_Filter ||
                     aValueList != nullptr,
                     "dasharrays and filters may not be null");
   mUnit = aUnit;
   mValue.mCSSValueList = aValueList; // take ownership
 }
 
 void
+nsStyleAnimation::Value::SetTransformValue(nsCSSValueSharedList* aList)
+{
+  FreeValue();
+  mUnit = eUnit_Transform;
+  mValue.mCSSValueSharedList = aList;
+  mValue.mCSSValueSharedList->AddRef();
+}
+
+void
 nsStyleAnimation::Value::SetAndAdoptCSSValuePairListValue(
                            nsCSSValuePairList *aValuePairList)
 {
   FreeValue();
   NS_ABORT_IF_FALSE(aValuePairList, "may not be null");
   mUnit = eUnit_CSSValuePairList;
   mValue.mCSSValuePairList = aValuePairList; // take ownership
 }
 
 void
 nsStyleAnimation::Value::FreeValue()
 {
   if (IsCSSValueUnit(mUnit)) {
     delete mValue.mCSSValue;
   } else if (IsCSSValueListUnit(mUnit)) {
     delete mValue.mCSSValueList;
+  } else if (IsCSSValueSharedListValue(mUnit)) {
+    mValue.mCSSValueSharedList->Release();
   } 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;
@@ -3789,19 +3808,20 @@ nsStyleAnimation::Value::operator==(cons
       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_Filter:
     case eUnit_Shadow:
-    case eUnit_Transform:
     case eUnit_BackgroundPosition:
       return *mValue.mCSSValueList == *aOther.mValue.mCSSValueList;
+    case eUnit_Transform:
+      return *mValue.mCSSValueSharedList == *aOther.mValue.mCSSValueSharedList;
     case eUnit_CSSValuePairList:
       return *mValue.mCSSValuePairList == *aOther.mValue.mCSSValuePairList;
     case eUnit_UnparsedString:
       return (NS_strcmp(GetStringBufferValue(),
                         aOther.GetStringBufferValue()) == 0);
   }
 
   NS_NOTREACHED("incomplete case");
--- a/layout/style/nsStyleAnimation.h
+++ b/layout/style/nsStyleAnimation.h
@@ -237,16 +237,17 @@ public:
       nscoord mCoord;
       float mFloat;
       nscolor mColor;
       nsCSSValue* mCSSValue;
       nsCSSValuePair* mCSSValuePair;
       nsCSSValueTriplet* mCSSValueTriplet;
       nsCSSRect* mCSSRect;
       nsCSSValueList* mCSSValueList;
+      nsCSSValueSharedList* mCSSValueSharedList;
       nsCSSValuePairList* mCSSValuePairList;
       nsStringBuffer* mString;
     } mValue;
   public:
     Unit GetUnit() const {
       NS_ASSERTION(mUnit != eUnit_Null, "uninitialized");
       return mUnit;
     }
@@ -292,16 +293,20 @@ public:
     nsCSSRect* GetCSSRectValue() const {
       NS_ASSERTION(IsCSSRectUnit(mUnit), "unit mismatch");
       return mValue.mCSSRect;
     }
     nsCSSValueList* GetCSSValueListValue() const {
       NS_ASSERTION(IsCSSValueListUnit(mUnit), "unit mismatch");
       return mValue.mCSSValueList;
     }
+    nsCSSValueSharedList* GetCSSValueSharedListValue() const {
+      NS_ASSERTION(IsCSSValueSharedListValue(mUnit), "unit mismatch");
+      return mValue.mCSSValueSharedList;
+    }
     nsCSSValuePairList* GetCSSValuePairListValue() const {
       NS_ASSERTION(IsCSSValuePairListUnit(mUnit), "unit mismatch");
       return mValue.mCSSValuePairList;
     }
     const PRUnichar* GetStringBufferValue() const {
       NS_ASSERTION(IsStringUnit(mUnit), "unit mismatch");
       return GetBufferValue(mValue.mString);
     }
@@ -346,16 +351,18 @@ public:
     // "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);
 
+    void SetTransformValue(nsCSSValueSharedList* aList);
+
     Value& operator=(const Value& aOther);
 
     bool operator==(const Value& aOther) const;
     bool operator!=(const Value& aOther) const
       { return !(*this == aOther); }
 
   private:
     void FreeValue();
@@ -377,19 +384,22 @@ public:
     static bool IsCSSValueTripletUnit(Unit aUnit) {
       return aUnit == eUnit_CSSValueTriplet;
     }
     static bool IsCSSRectUnit(Unit aUnit) {
       return aUnit == eUnit_CSSRect;
     }
     static bool IsCSSValueListUnit(Unit aUnit) {
       return aUnit == eUnit_Dasharray || aUnit == eUnit_Filter ||
-             aUnit == eUnit_Shadow || aUnit == eUnit_Transform ||
+             aUnit == eUnit_Shadow ||
              aUnit == eUnit_BackgroundPosition;
     }
+    static bool IsCSSValueSharedListValue(Unit aUnit) {
+      return aUnit == eUnit_Transform;
+    }
     static bool IsCSSValuePairListUnit(Unit aUnit) {
       return aUnit == eUnit_CSSValuePairList;
     }
     static bool IsStringUnit(Unit aUnit) {
       return aUnit == eUnit_UnparsedString;
     }
   };
 };
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1719,20 +1719,20 @@ struct nsStyleDisplay {
   uint8_t mResize;              // [reset] see nsStyleConsts.h
   uint8_t mClipFlags;           // [reset] see nsStyleConsts.h
   uint8_t mOrient;              // [reset] see nsStyleConsts.h
   uint8_t mMixBlendMode;        // [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)
+  // null, as appropriate.)
   uint8_t mBackfaceVisibility;
   uint8_t mTransformStyle;
-  const nsCSSValueList *mSpecifiedTransform; // [reset]
+  nsRefPtr<nsCSSValueSharedList> 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
 
   nsAutoTArray<nsTransition, 1> mTransitions; // [reset]
   // The number of elements in mTransitions that are not from repeating
   // a list due to another property being longer.
   uint32_t mTransitionTimingFunctionCount,