Bug 1529002 - Use cbindgen to back CSS transforms. r=boris
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 16 May 2019 23:25:10 +0000
changeset 474260 02c806cb81d959f1fc08c7304f50266fb6a5e56c
parent 474259 29c5fc63a9bf79e53cc8991c47ed47fedafda4e1
child 474261 3c388ac5887ceec18ae5db997cc34065cad9ac2f
push id113144
push usershindli@mozilla.com
push dateFri, 17 May 2019 16:44:55 +0000
treeherdermozilla-inbound@f4c4b796f845 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersboris
bugs1529002
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529002 - Use cbindgen to back CSS transforms. r=boris This avoids the expensive conversion, and cleans up a bunch. Further cleanup is possible, just not done yet to avoid growing the patch even more. Differential Revision: https://phabricator.services.mozilla.com/D30748
gfx/layers/AnimationHelper.cpp
gfx/layers/composite/AsyncCompositionManager.cpp
layout/generic/nsFrame.cpp
layout/painting/ActiveLayerTracker.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/style/GeckoBindings.cpp
layout/style/GeckoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoStyleConstsInlines.h
layout/style/StyleAnimationValue.cpp
layout/style/StyleAnimationValue.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/nsStyleTransformMatrix.cpp
layout/style/nsStyleTransformMatrix.h
layout/svg/nsSVGUtils.cpp
servo/components/style/gecko_bindings/sugar/ns_css_value.rs
servo/components/style/gecko_bindings/sugar/refptr.rs
servo/components/style/properties/cascade.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhands/box.mako.rs
servo/components/style/properties/longhands/ui.mako.rs
servo/components/style/values/animated/transform.rs
servo/components/style/values/computed/angle.rs
servo/components/style/values/computed/transform.rs
servo/components/style/values/generics/transform.rs
servo/components/style/values/specified/transform.rs
servo/ports/geckolib/cbindgen.toml
servo/ports/geckolib/glue.rs
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -590,53 +590,59 @@ bool AnimationHelper::SampleAnimations(C
   }
 
   return isAnimating;
 }
 
 gfx::Matrix4x4 AnimationHelper::ServoAnimationValueToMatrix4x4(
     const nsTArray<RefPtr<RawServoAnimationValue>>& aValues,
     const TransformData& aTransformData) {
-  // FIXME: Bug 1457033: We should convert servo's animation value to matrix
-  // directly without nsCSSValueSharedList.
+  // This is a bit silly just to avoid the transform list copy from the
+  // animation transform list.
+  auto noneTranslate = StyleTranslate::None();
+  auto noneRotate = StyleRotate::None();
+  auto noneScale = StyleScale::None();
+  const StyleTransform noneTransform;
+
+  const StyleTranslate* translate = nullptr;
+  const StyleRotate* rotate = nullptr;
+  const StyleScale* scale = nullptr;
+  const StyleTransform* transform = nullptr;
+
   // TODO: Bug 1429305: Support compositor animations for motion-path.
-  RefPtr<nsCSSValueSharedList> transform, translate, rotate, scale;
   for (const auto& value : aValues) {
     MOZ_ASSERT(value);
-    RefPtr<nsCSSValueSharedList> list;
-    nsCSSPropertyID id = Servo_AnimationValue_GetTransform(value, &list);
+    nsCSSPropertyID id = Servo_AnimationValue_GetPropertyId(value);
     switch (id) {
       case eCSSProperty_transform:
         MOZ_ASSERT(!transform);
-        transform = list.forget();
+        transform = Servo_AnimationValue_GetTransform(value);
         break;
       case eCSSProperty_translate:
         MOZ_ASSERT(!translate);
-        translate = list.forget();
+        translate = Servo_AnimationValue_GetTranslate(value);
         break;
       case eCSSProperty_rotate:
         MOZ_ASSERT(!rotate);
-        rotate = list.forget();
+        rotate = Servo_AnimationValue_GetRotate(value);
         break;
       case eCSSProperty_scale:
         MOZ_ASSERT(!scale);
-        scale = list.forget();
+        scale = Servo_AnimationValue_GetScale(value);
         break;
       default:
         MOZ_ASSERT_UNREACHABLE("Unsupported transform-like property");
     }
   }
-  RefPtr<nsCSSValueSharedList> individualList =
-      nsStyleDisplay::GenerateCombinedIndividualTransform(translate, rotate,
-                                                          scale);
-
   // We expect all our transform data to arrive in device pixels
   gfx::Point3D transformOrigin = aTransformData.transformOrigin();
   nsDisplayTransform::FrameTransformProperties props(
-      std::move(individualList), std::move(transform), transformOrigin);
+      translate ? *translate : noneTranslate, rotate ? *rotate : noneRotate,
+      scale ? *scale : noneScale, transform ? *transform : noneTransform,
+      transformOrigin);
 
   return nsDisplayTransform::GetResultingTransformMatrix(
       props, aTransformData.origin(), aTransformData.appUnitsPerDevPixel(), 0,
       &aTransformData.bounds());
 }
 
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -41,18 +41,16 @@
 #  include "mozilla/layers/UiCompositorControllerParent.h"
 #  include "mozilla/widget/AndroidCompositorWidget.h"
 #endif
 #include "GeckoProfiler.h"
 #include "FrameUniformityData.h"
 #include "TreeTraversal.h"  // for ForEachNode, BreadthFirstSearch
 #include "VsyncSource.h"
 
-struct nsCSSValueSharedList;
-
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 static bool IsSameDimension(hal::ScreenOrientation o1,
                             hal::ScreenOrientation o2) {
   bool isO1portrait = (o1 == hal::eScreenOrientation_PortraitPrimary ||
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1209,18 +1209,20 @@ void nsFrame::DidSetComputedStyle(Comput
           oldPosition->mMinWidth != StylePosition()->mMinWidth ||
           oldPosition->mMaxWidth != StylePosition()->mMaxWidth ||
           oldPosition->mHeight != StylePosition()->mHeight ||
           oldPosition->mMinHeight != StylePosition()->mMinHeight ||
           oldPosition->mMaxHeight != StylePosition()->mMaxHeight) {
         needAnchorSuppression = true;
       }
 
+      // TODO(emilio): Should this do something about other transform-like
+      // properties?
       if (oldDisp->mPosition != StyleDisplay()->mPosition ||
-          oldDisp->TransformChanged(*StyleDisplay())) {
+          oldDisp->mTransform != StyleDisplay()->mTransform) {
         needAnchorSuppression = true;
       }
     }
 
     if (mInScrollAnchorChain && needAnchorSuppression) {
       ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
     }
   }
@@ -10662,28 +10664,27 @@ static bool IsFrameScrolledOutOfView(con
 
 bool nsIFrame::IsScrolledOutOfView() const {
   nsRect rect = GetVisualOverflowRectRelativeToSelf();
   return IsFrameScrolledOutOfView(this, rect, this);
 }
 
 gfx::Matrix nsIFrame::ComputeWidgetTransform() {
   const nsStyleUIReset* uiReset = StyleUIReset();
-  if (!uiReset->mSpecifiedWindowTransform) {
+  if (uiReset->mMozWindowTransform.IsNone()) {
     return gfx::Matrix();
   }
 
   nsStyleTransformMatrix::TransformReferenceBox refBox;
   refBox.Init(GetSize());
 
   nsPresContext* presContext = PresContext();
   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
   gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
-      uiReset->mSpecifiedWindowTransform->mHead, refBox,
-      float(appUnitsPerDevPixel));
+      uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
 
   // Apply the -moz-window-transform-origin translation to the matrix.
   const StyleTransformOrigin& origin = uiReset->mWindowTransformOrigin;
   Point transformOrigin = nsStyleTransformMatrix::Convert2DPosition(
       origin.horizontal, origin.vertical, refBox, appUnitsPerDevPixel);
   matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
 
   gfx::Matrix result2d;
--- a/layout/painting/ActiveLayerTracker.cpp
+++ b/layout/painting/ActiveLayerTracker.cpp
@@ -259,34 +259,31 @@ void ActiveLayerTracker::TransferActivit
   layerActivity->mFrame = aFrame;
   aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
   aFrame->SetProperty(LayerActivityProperty(), layerActivity);
 }
 
 static void IncrementScaleRestyleCountIfNeeded(nsIFrame* aFrame,
                                                LayerActivity* aActivity) {
   const nsStyleDisplay* display = aFrame->StyleDisplay();
-  if (!display->mSpecifiedTransform && !display->HasIndividualTransform() &&
+  if (!display->HasTransformProperty() && !display->HasIndividualTransform() &&
       !(display->mMotion && display->mMotion->HasPath())) {
     // The transform was removed.
     aActivity->mPreviousTransformScale = Nothing();
     IncrementMutationCount(
         &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]);
     return;
   }
 
   // Compute the new scale due to the CSS transform property.
   nsStyleTransformMatrix::TransformReferenceBox refBox(aFrame);
   Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms(
-      display->mIndividualTransform ? display->mIndividualTransform->mHead
-                                    : nullptr,
-      nsLayoutUtils::ResolveMotionPath(aFrame),
-      display->mSpecifiedTransform ? display->mSpecifiedTransform->mHead
-                                   : nullptr,
-      refBox, AppUnitsPerCSSPixel());
+      display->mTranslate, display->mRotate, display->mScale,
+      nsLayoutUtils::ResolveMotionPath(aFrame), display->mTransform, refBox,
+      AppUnitsPerCSSPixel());
   Matrix transform2D;
   if (!transform.Is2D(&transform2D)) {
     // We don't attempt to handle 3D transforms; just assume the scale changed.
     aActivity->mPreviousTransformScale = Nothing();
     IncrementMutationCount(
         &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]);
     return;
   }
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -179,267 +179,262 @@ nsCString ActiveScrolledRoot::ToString(
     str.AppendPrintf("<0x%p>", asr->mScrollableFrame);
     if (asr->mParent) {
       str.AppendLiteral(", ");
     }
   }
   return std::move(str);
 }
 
-static inline CSSAngle MakeCSSAngle(const nsCSSValue& aValue) {
-  return CSSAngle(aValue.GetAngleValue(), aValue.GetUnit());
-}
-
-static Rotate GetRotate(const nsCSSValue& aValue) {
+static inline CSSAngle MakeCSSAngle(const StyleAngle& aValue) {
+  return CSSAngle(aValue.ToDegrees(), eCSSUnit_Degree);
+}
+
+static Rotate GetRotate(const StyleRotate& aValue) {
   Rotate result = null_t();
-  if (aValue.GetUnit() == eCSSUnit_None) {
-    return result;
-  }
-
-  const nsCSSValue::Array* array = aValue.GetArrayValue();
-  switch (nsStyleTransformMatrix::TransformFunctionOf(array)) {
-    case eCSSKeyword_rotate:
-      result = Rotate(Rotation(MakeCSSAngle(array->Item(1))));
+  switch (aValue.tag) {
+    case StyleRotate::Tag::None:
+      break;
+    case StyleRotate::Tag::Rotate:
+      result = Rotate(Rotation(MakeCSSAngle(aValue.AsRotate())));
       break;
-    case eCSSKeyword_rotate3d:
-      result = Rotate(Rotation3D(
-          array->Item(1).GetFloatValue(), array->Item(2).GetFloatValue(),
-          array->Item(3).GetFloatValue(), MakeCSSAngle(array->Item(4))));
+    case StyleRotate::Tag::Rotate3D: {
+      const auto& rotate = aValue.AsRotate3D();
+      result = Rotate(
+          Rotation3D(rotate._0, rotate._1, rotate._2, MakeCSSAngle(rotate._3)));
       break;
+    }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported rotate");
   }
   return result;
 }
 
-static Scale GetScale(const nsCSSValue& aValue) {
+static Scale GetScale(const StyleScale& aValue) {
   Scale result(1., 1., 1.);
-  if (aValue.GetUnit() == eCSSUnit_None) {
-    // Use (1, 1, 1) to replace the none case.
-    return result;
-  }
-
-  const nsCSSValue::Array* array = aValue.GetArrayValue();
-  switch (nsStyleTransformMatrix::TransformFunctionOf(array)) {
-    case eCSSKeyword_scalex:
-      result.x() = array->Item(1).GetFloatValue();
-      break;
-    case eCSSKeyword_scaley:
-      result.y() = array->Item(1).GetFloatValue();
+  switch (aValue.tag) {
+    case StyleScale::Tag::None:
       break;
-    case eCSSKeyword_scalez:
-      result.z() = array->Item(1).GetFloatValue();
+    case StyleScale::Tag::Scale: {
+      auto& scale = aValue.AsScale();
+      result.x() = scale._0;
+      result.y() = scale._1;
       break;
-    case eCSSKeyword_scale:
-      result.x() = array->Item(1).GetFloatValue();
-      // scale(x) is shorthand for scale(x, x);
-      result.y() =
-          array->Count() == 2 ? result.x() : array->Item(2).GetFloatValue();
+    }
+    case StyleScale::Tag::Scale3D: {
+      auto& scale = aValue.AsScale3D();
+      result.x() = scale._0;
+      result.y() = scale._1;
+      result.z() = scale._2;
       break;
-    case eCSSKeyword_scale3d:
-      result.x() = array->Item(1).GetFloatValue();
-      result.y() = array->Item(2).GetFloatValue();
-      result.z() = array->Item(3).GetFloatValue();
-      break;
+    }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported scale");
   }
   return result;
 }
 
-static Translation GetTranslate(const nsCSSValue& aValue,
+static Translation GetTranslate(
+    TransformReferenceBox& aRefBox, const LengthPercentage& aX,
+    const LengthPercentage& aY = LengthPercentage::Zero(),
+    const Length& aZ = Length{0}) {
+  Translation result(0, 0, 0);
+  result.x() = nsStyleTransformMatrix::ProcessTranslatePart(
+      aX, &aRefBox, &TransformReferenceBox::Width);
+  result.y() = nsStyleTransformMatrix::ProcessTranslatePart(
+      aY, &aRefBox, &TransformReferenceBox::Height);
+  result.z() = aZ.ToCSSPixels();
+  return result;
+}
+
+static Translation GetTranslate(const StyleTranslate& aValue,
                                 TransformReferenceBox& aRefBox) {
   Translation result(0, 0, 0);
-  if (aValue.GetUnit() == eCSSUnit_None) {
-    // Use (0, 0, 0) to replace the none case.
-    return result;
-  }
-
-  const nsCSSValue::Array* array = aValue.GetArrayValue();
-  switch (nsStyleTransformMatrix::TransformFunctionOf(array)) {
-    case eCSSKeyword_translatex:
-      result.x() = nsStyleTransformMatrix::ProcessTranslatePart(
-          array->Item(1), &aRefBox, &TransformReferenceBox::Width);
+  switch (aValue.tag) {
+    case StyleTranslate::Tag::None:
       break;
-    case eCSSKeyword_translatey:
-      result.y() = nsStyleTransformMatrix::ProcessTranslatePart(
-          array->Item(1), &aRefBox, &TransformReferenceBox::Height);
+    case StyleTranslate::Tag::Translate: {
+      auto& translate = aValue.AsTranslate();
+      result = GetTranslate(aRefBox, translate._0, translate._1);
       break;
-    case eCSSKeyword_translatez:
-      result.z() =
-          nsStyleTransformMatrix::ProcessTranslatePart(array->Item(1), nullptr);
+    }
+    case StyleTranslate::Tag::Translate3D: {
+      auto& translate = aValue.AsTranslate3D();
+      result = GetTranslate(aRefBox, translate._0, translate._1, translate._2);
       break;
-    case eCSSKeyword_translate:
-      result.x() = nsStyleTransformMatrix::ProcessTranslatePart(
-          array->Item(1), &aRefBox, &TransformReferenceBox::Width);
-      // translate(x) is shorthand for translate(x, 0)
-      if (array->Count() == 3) {
-        result.y() = nsStyleTransformMatrix::ProcessTranslatePart(
-            array->Item(2), &aRefBox, &TransformReferenceBox::Height);
-      }
-      break;
-    case eCSSKeyword_translate3d:
-      result.x() = nsStyleTransformMatrix::ProcessTranslatePart(
-          array->Item(1), &aRefBox, &TransformReferenceBox::Width);
-      result.y() = nsStyleTransformMatrix::ProcessTranslatePart(
-          array->Item(2), &aRefBox, &TransformReferenceBox::Height);
-      result.z() =
-          nsStyleTransformMatrix::ProcessTranslatePart(array->Item(3), nullptr);
-      break;
+    }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported translate");
   }
   return result;
 }
 
 static void AddTransformFunctions(
-    const nsCSSValueList* aList, mozilla::ComputedStyle* aStyle,
-    nsPresContext* aPresContext, TransformReferenceBox& aRefBox,
+    const StyleTransform& aTransform, TransformReferenceBox& aRefBox,
     InfallibleTArray<TransformFunction>& aFunctions) {
-  if (aList->mValue.GetUnit() == eCSSUnit_None) {
-    return;
-  }
-
-  for (const nsCSSValueList* curr = aList; curr; curr = curr->mNext) {
-    const nsCSSValue& currElem = curr->mValue;
-    NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function,
-                 "Stream should consist solely of functions!");
-    nsCSSValue::Array* array = currElem.GetArrayValue();
-    switch (nsStyleTransformMatrix::TransformFunctionOf(array)) {
-      case eCSSKeyword_rotatex: {
-        CSSAngle theta = MakeCSSAngle(array->Item(1));
+  for (const StyleTransformOperation& op : aTransform.Operations()) {
+    switch (op.tag) {
+      case StyleTransformOperation::Tag::RotateX: {
+        CSSAngle theta = MakeCSSAngle(op.AsRotateX());
         aFunctions.AppendElement(RotationX(theta));
         break;
       }
-      case eCSSKeyword_rotatey: {
-        CSSAngle theta = MakeCSSAngle(array->Item(1));
+      case StyleTransformOperation::Tag::RotateY: {
+        CSSAngle theta = MakeCSSAngle(op.AsRotateY());
         aFunctions.AppendElement(RotationY(theta));
         break;
       }
-      case eCSSKeyword_rotatez: {
-        CSSAngle theta = MakeCSSAngle(array->Item(1));
+      case StyleTransformOperation::Tag::RotateZ: {
+        CSSAngle theta = MakeCSSAngle(op.AsRotateZ());
         aFunctions.AppendElement(RotationZ(theta));
         break;
       }
-      case eCSSKeyword_rotate:
-        aFunctions.AppendElement(GetRotate(currElem).get_Rotation());
+      case StyleTransformOperation::Tag::Rotate: {
+        CSSAngle theta = MakeCSSAngle(op.AsRotate());
+        aFunctions.AppendElement(Rotation(theta));
+        break;
+      }
+      case StyleTransformOperation::Tag::Rotate3D: {
+        const auto& rotate = op.AsRotate3D();
+        CSSAngle theta = MakeCSSAngle(rotate._3);
+        aFunctions.AppendElement(
+            Rotation3D(rotate._0, rotate._1, rotate._2, theta));
         break;
-      case eCSSKeyword_rotate3d:
-        aFunctions.AppendElement(GetRotate(currElem).get_Rotation3D());
+      }
+      case StyleTransformOperation::Tag::ScaleX: {
+        aFunctions.AppendElement(Scale(op.AsScaleX(), 1., 1.));
+        break;
+      }
+      case StyleTransformOperation::Tag::ScaleY: {
+        aFunctions.AppendElement(Scale(1., op.AsScaleY(), 1.));
+        break;
+      }
+      case StyleTransformOperation::Tag::ScaleZ: {
+        aFunctions.AppendElement(Scale(1., 1., op.AsScaleZ()));
+        break;
+      }
+      case StyleTransformOperation::Tag::Scale: {
+        const auto& scale = op.AsScale();
+        aFunctions.AppendElement(Scale(scale._0, scale._1, 1.));
         break;
-      case eCSSKeyword_scalex:
-      case eCSSKeyword_scaley:
-      case eCSSKeyword_scalez:
-      case eCSSKeyword_scale:
-      case eCSSKeyword_scale3d:
-        aFunctions.AppendElement(GetScale(currElem));
+      }
+      case StyleTransformOperation::Tag::Scale3D: {
+        const auto& scale = op.AsScale3D();
+        aFunctions.AppendElement(Scale(scale._0, scale._1, scale._2));
+        break;
+      }
+      case StyleTransformOperation::Tag::TranslateX: {
+        aFunctions.AppendElement(GetTranslate(aRefBox, op.AsTranslateX()));
+        break;
+      }
+      case StyleTransformOperation::Tag::TranslateY: {
+        aFunctions.AppendElement(
+            GetTranslate(aRefBox, LengthPercentage::Zero(), op.AsTranslateY()));
         break;
-      case eCSSKeyword_translatex:
-      case eCSSKeyword_translatey:
-      case eCSSKeyword_translatez:
-      case eCSSKeyword_translate:
-      case eCSSKeyword_translate3d:
-        aFunctions.AppendElement(GetTranslate(currElem, aRefBox));
+      }
+      case StyleTransformOperation::Tag::TranslateZ: {
+        aFunctions.AppendElement(GetTranslate(aRefBox, LengthPercentage::Zero(),
+                                              LengthPercentage::Zero(),
+                                              op.AsTranslateZ()));
         break;
-      case eCSSKeyword_skewx: {
-        CSSAngle x = MakeCSSAngle(array->Item(1));
+      }
+      case StyleTransformOperation::Tag::Translate: {
+        const auto& translate = op.AsTranslate();
+        aFunctions.AppendElement(
+            GetTranslate(aRefBox, translate._0, translate._1));
+        break;
+      }
+      case StyleTransformOperation::Tag::Translate3D: {
+        const auto& translate = op.AsTranslate3D();
+        aFunctions.AppendElement(
+            GetTranslate(aRefBox, translate._0, translate._1, translate._2));
+        break;
+      }
+      case StyleTransformOperation::Tag::SkewX: {
+        CSSAngle x = MakeCSSAngle(op.AsSkewX());
         aFunctions.AppendElement(SkewX(x));
         break;
       }
-      case eCSSKeyword_skewy: {
-        CSSAngle y = MakeCSSAngle(array->Item(1));
+      case StyleTransformOperation::Tag::SkewY: {
+        CSSAngle y = MakeCSSAngle(op.AsSkewY());
         aFunctions.AppendElement(SkewY(y));
         break;
       }
-      case eCSSKeyword_skew: {
-        CSSAngle x = MakeCSSAngle(array->Item(1));
-        // skew(x) is shorthand for skew(x, 0)
-        CSSAngle y(0.0f, eCSSUnit_Degree);
-        if (array->Count() == 3) {
-          y = MakeCSSAngle(array->Item(2));
-        }
-        aFunctions.AppendElement(Skew(x, y));
+      case StyleTransformOperation::Tag::Skew: {
+        const auto& skew = op.AsSkew();
+        aFunctions.AppendElement(
+            Skew(MakeCSSAngle(skew._0), MakeCSSAngle(skew._1)));
         break;
       }
-      case eCSSKeyword_matrix: {
+      case StyleTransformOperation::Tag::Matrix: {
         gfx::Matrix4x4 matrix;
-        matrix._11 = array->Item(1).GetFloatValue();
-        matrix._12 = array->Item(2).GetFloatValue();
+        const auto& m = op.AsMatrix();
+        matrix._11 = m.a;
+        matrix._12 = m.b;
         matrix._13 = 0;
         matrix._14 = 0;
-        matrix._21 = array->Item(3).GetFloatValue();
-        matrix._22 = array->Item(4).GetFloatValue();
+        matrix._21 = m.c;
+        matrix._22 = m.d;
         matrix._23 = 0;
         matrix._24 = 0;
         matrix._31 = 0;
         matrix._32 = 0;
         matrix._33 = 1;
         matrix._34 = 0;
-        matrix._41 = ProcessTranslatePart(array->Item(5), &aRefBox,
-                                          &TransformReferenceBox::Width);
-        matrix._42 = ProcessTranslatePart(array->Item(6), &aRefBox,
-                                          &TransformReferenceBox::Height);
+        matrix._41 = m.e;
+        matrix._42 = m.f;
         matrix._43 = 0;
         matrix._44 = 1;
         aFunctions.AppendElement(TransformMatrix(matrix));
         break;
       }
-      case eCSSKeyword_matrix3d: {
+      case StyleTransformOperation::Tag::Matrix3D: {
+        const auto& m = op.AsMatrix3D();
         gfx::Matrix4x4 matrix;
-        matrix._11 = array->Item(1).GetFloatValue();
-        matrix._12 = array->Item(2).GetFloatValue();
-        matrix._13 = array->Item(3).GetFloatValue();
-        matrix._14 = array->Item(4).GetFloatValue();
-        matrix._21 = array->Item(5).GetFloatValue();
-        matrix._22 = array->Item(6).GetFloatValue();
-        matrix._23 = array->Item(7).GetFloatValue();
-        matrix._24 = array->Item(8).GetFloatValue();
-        matrix._31 = array->Item(9).GetFloatValue();
-        matrix._32 = array->Item(10).GetFloatValue();
-        matrix._33 = array->Item(11).GetFloatValue();
-        matrix._34 = array->Item(12).GetFloatValue();
-        matrix._41 = ProcessTranslatePart(array->Item(13), &aRefBox,
-                                          &TransformReferenceBox::Width);
-        matrix._42 = ProcessTranslatePart(array->Item(14), &aRefBox,
-                                          &TransformReferenceBox::Height);
-        matrix._43 = ProcessTranslatePart(array->Item(15), &aRefBox, nullptr);
-        matrix._44 = array->Item(16).GetFloatValue();
+
+        matrix._11 = m.m11;
+        matrix._12 = m.m12;
+        matrix._13 = m.m13;
+        matrix._14 = m.m14;
+        matrix._21 = m.m21;
+        matrix._22 = m.m22;
+        matrix._23 = m.m23;
+        matrix._24 = m.m24;
+        matrix._31 = m.m31;
+        matrix._32 = m.m32;
+        matrix._33 = m.m33;
+        matrix._34 = m.m34;
+
+        matrix._41 = m.m41;
+        matrix._42 = m.m42;
+        matrix._43 = m.m43;
+        matrix._44 = m.m44;
         aFunctions.AppendElement(TransformMatrix(matrix));
         break;
       }
-      case eCSSKeyword_interpolatematrix: {
+      case StyleTransformOperation::Tag::InterpolateMatrix: {
         Matrix4x4 matrix;
-        nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, array,
-                                                         aRefBox);
-        aFunctions.AppendElement(TransformMatrix(matrix));
-        break;
-      }
-      case eCSSKeyword_accumulatematrix: {
-        Matrix4x4 matrix;
-        nsStyleTransformMatrix::ProcessAccumulateMatrix(matrix, array, aRefBox);
+        nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, op, aRefBox);
         aFunctions.AppendElement(TransformMatrix(matrix));
         break;
       }
-      case eCSSKeyword_perspective: {
-        aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue()));
+      case StyleTransformOperation::Tag::AccumulateMatrix: {
+        Matrix4x4 matrix;
+        nsStyleTransformMatrix::ProcessAccumulateMatrix(matrix, op, aRefBox);
+        aFunctions.AppendElement(TransformMatrix(matrix));
+        break;
+      }
+      case StyleTransformOperation::Tag::Perspective: {
+        aFunctions.AppendElement(Perspective(op.AsPerspective().ToCSSPixels()));
         break;
       }
       default:
-        NS_ERROR("Function not handled yet!");
-    }
-  }
-}
-
-static void AddTransformFunctions(const nsCSSValueSharedList* aList,
-                                  const nsIFrame* aFrame,
-                                  TransformReferenceBox& aRefBox,
-                                  layers::Animatable& aAnimatable) {
-  MOZ_ASSERT(aList->mHead);
-  AddTransformFunctions(aList->mHead, aFrame->Style(), aFrame->PresContext(),
-                        aRefBox, aAnimatable.get_ArrayOfTransformFunction());
+        MOZ_ASSERT_UNREACHABLE("Function not handled yet!");
+    }
+  }
 }
 
 static TimingFunction ToTimingFunction(
     const Maybe<ComputedTimingFunction>& aCTF) {
   if (aCTF.isNothing()) {
     return TimingFunction(null_t());
   }
 
@@ -472,44 +467,32 @@ static void SetAnimatable(nsCSSPropertyI
           aFrame->Style()->GetVisitedDependentColor(&nsStyleColor::mColor);
       aAnimatable = aAnimationValue.GetColor(foreground);
       break;
     }
     case eCSSProperty_opacity:
       aAnimatable = aAnimationValue.GetOpacity();
       break;
     case eCSSProperty_rotate: {
-      RefPtr<const nsCSSValueSharedList> list =
-          aAnimationValue.GetTransformList();
-      MOZ_ASSERT(list && list->mHead && !list->mHead->mNext,
-                 "should have only one nsCSSValueList for rotate");
-      aAnimatable = GetRotate(list->mHead->mValue);
+      aAnimatable = GetRotate(aAnimationValue.GetRotateProperty());
       break;
     }
     case eCSSProperty_scale: {
-      RefPtr<const nsCSSValueSharedList> list =
-          aAnimationValue.GetTransformList();
-      MOZ_ASSERT(list && list->mHead && !list->mHead->mNext,
-                 "should have only one nsCSSValueList for scale");
-      aAnimatable = GetScale(list->mHead->mValue);
+      aAnimatable = GetScale(aAnimationValue.GetScaleProperty());
       break;
     }
     case eCSSProperty_translate: {
-      RefPtr<const nsCSSValueSharedList> list =
-          aAnimationValue.GetTransformList();
-      MOZ_ASSERT(list && list->mHead && !list->mHead->mNext,
-                 "should have only one nsCSSValueList for translate");
-      aAnimatable = GetTranslate(list->mHead->mValue, aRefBox);
+      aAnimatable =
+          GetTranslate(aAnimationValue.GetTranslateProperty(), aRefBox);
       break;
     }
     case eCSSProperty_transform: {
       aAnimatable = InfallibleTArray<TransformFunction>();
-      RefPtr<const nsCSSValueSharedList> list =
-          aAnimationValue.GetTransformList();
-      AddTransformFunctions(list, aFrame, aRefBox, aAnimatable);
+      AddTransformFunctions(aAnimationValue.GetTransformProperty(), aRefBox,
+                            aAnimatable.get_ArrayOfTransformFunction());
       break;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported property");
   }
 }
 
 enum class Send {
@@ -7753,19 +7736,21 @@ bool nsDisplayTransform::ComputePerspect
   aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
   return true;
 }
 
 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
     const nsIFrame* aFrame, float aAppUnitsPerPixel,
     const nsRect* aBoundsOverride)
     : mFrame(aFrame),
-      mIndividualTransformList(aFrame->StyleDisplay()->mIndividualTransform),
+      mTranslate(aFrame->StyleDisplay()->mTranslate),
+      mRotate(aFrame->StyleDisplay()->mRotate),
+      mScale(aFrame->StyleDisplay()->mScale),
+      mTransform(aFrame->StyleDisplay()->mTransform),
       mMotion(nsLayoutUtils::ResolveMotionPath(aFrame)),
-      mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform),
       mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel,
                                                    aBoundsOverride)) {}
 
 /* Wraps up the transform matrix in a change-of-basis matrix pair that
  * translates from local coordinate space to transform coordinate space, then
  * hands it back.
  */
 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
@@ -7823,23 +7808,18 @@ Matrix4x4 nsDisplayTransform::GetResulti
       frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
 
   bool shouldRound = ShouldRoundTransformOrigin(frame);
 
   /* Transformed frames always have a transform, or are preserving 3d (and might
    * still have perspective!) */
   if (aProperties.HasTransform()) {
     result = nsStyleTransformMatrix::ReadTransforms(
-        aProperties.mIndividualTransformList
-            ? aProperties.mIndividualTransformList->mHead
-            : nullptr,
-        aProperties.mMotion,
-        aProperties.mTransformList ? aProperties.mTransformList->mHead
-                                   : nullptr,
-        refBox, aAppUnitsPerPixel);
+        aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
+        aProperties.mMotion, aProperties.mTransform, refBox, aAppUnitsPerPixel);
   } else if (hasSVGTransforms) {
     // Correct the translation components for zoom:
     float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
     svgTransform._31 *= pixelsPerCSSPx;
     svgTransform._32 *= pixelsPerCSSPx;
     result = Matrix4x4::From2D(svgTransform);
   }
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -6885,44 +6885,50 @@ class nsDisplayTransform : public nsDisp
    * space).
    * aOutMatrix is assumed to be the identity matrix, and isn't explicitly
    * cleared.
    */
   static bool ComputePerspectiveMatrix(const nsIFrame* aFrame,
                                        float aAppUnitsPerPixel,
                                        Matrix4x4& aOutMatrix);
 
-  struct FrameTransformProperties {
+  struct MOZ_STACK_CLASS FrameTransformProperties {
     FrameTransformProperties(const nsIFrame* aFrame, float aAppUnitsPerPixel,
                              const nsRect* aBoundsOverride);
     // This constructor is used on the compositor (for animations).
     // FIXME: Bug 1186329: if we want to support compositor animations for
     // motion path, we need to update this. For now, let mMotion be Nothing().
-    FrameTransformProperties(
-        RefPtr<const nsCSSValueSharedList>&& aIndividualTransform,
-        RefPtr<const nsCSSValueSharedList>&& aTransformList,
-        const Point3D& aToTransformOrigin)
+    FrameTransformProperties(const mozilla::StyleTranslate& aTranslate,
+                             const mozilla::StyleRotate& aRotate,
+                             const mozilla::StyleScale& aScale,
+                             const mozilla::StyleTransform& aTransform,
+                             const Point3D& aToTransformOrigin)
         : mFrame(nullptr),
-          mIndividualTransformList(std::move(aIndividualTransform)),
-          mTransformList(std::move(aTransformList)),
+          mTranslate(aTranslate),
+          mRotate(aRotate),
+          mScale(aScale),
+          mTransform(aTransform),
           mToTransformOrigin(aToTransformOrigin) {}
 
     bool HasTransform() const {
-      return mIndividualTransformList || mTransformList || mMotion.isSome();
+      return !mTranslate.IsNone() || !mRotate.IsNone() || !mScale.IsNone() ||
+             !mTransform.IsNone() || mMotion.isSome();
     }
 
     const nsIFrame* mFrame;
-    const RefPtr<const nsCSSValueSharedList> mIndividualTransformList;
+    const mozilla::StyleTranslate& mTranslate;
+    const mozilla::StyleRotate& mRotate;
+    const mozilla::StyleScale& mScale;
+    const mozilla::StyleTransform& mTransform;
     const mozilla::Maybe<mozilla::MotionPathData> mMotion;
-    const RefPtr<const nsCSSValueSharedList> mTransformList;
     const Point3D mToTransformOrigin;
   };
 
   /**
-   * Given a frame with the -moz-transform property or an SVG transform,
+   * Given a frame with the transform property or an SVG transform,
    * returns the transformation matrix for that frame.
    *
    * @param aFrame The frame to get the matrix from.
    * @param aOrigin Relative to which point this transform should be applied.
    * @param aAppUnitsPerPixel The number of app units per graphics unit.
    * @param aBoundsOverride [optional] If this is nullptr (the default), the
    *        computation will use the value of TransformReferenceBox(aFrame).
    *        Otherwise, it will use the value of aBoundsOverride.  This is
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1685,43 +1685,16 @@ void Gecko_Snapshot_DebugListAttributes(
 }
 
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLValue, CSSURLValue);
 
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData);
 
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
 
-nsCSSValueSharedList* Gecko_NewCSSValueSharedList(uint32_t aLen) {
-  RefPtr<nsCSSValueSharedList> list = new nsCSSValueSharedList;
-  if (aLen == 0) {
-    return list.forget().take();
-  }
-
-  list->mHead = new nsCSSValueList;
-  nsCSSValueList* cur = list->mHead;
-  for (uint32_t i = 0; i < aLen - 1; i++) {
-    cur->mNext = new nsCSSValueList;
-    cur = cur->mNext;
-  }
-
-  return list.forget().take();
-}
-
-nsCSSValueSharedList* Gecko_NewNoneTransform() {
-  RefPtr<nsCSSValueSharedList> list = new nsCSSValueSharedList;
-  list->mHead = new nsCSSValueList;
-  list->mHead->mValue.SetNoneValue();
-  return list.forget().take();
-}
-
-void Gecko_StyleDisplay_GenerateCombinedTransform(nsStyleDisplay* aDisplay) {
-  aDisplay->GenerateCombinedIndividualTransform();
-}
-
 void Gecko_CSSValue_SetNumber(nsCSSValue* aCSSValue, float aNumber) {
   aCSSValue->SetFloatValue(aNumber, eCSSUnit_Number);
 }
 
 float Gecko_CSSValue_GetNumber(const nsCSSValue* aCSSValue) {
   return aCSSValue->GetFloatValue();
 }
 
@@ -1823,29 +1796,16 @@ void Gecko_CSSValue_SetPairList(nsCSSVal
   MOZ_ASSERT(NS_IsMainThread());
   nsCSSValuePairList* item = aCSSValue->SetPairListValue();
   for (uint32_t i = 1; i < aLen; ++i) {
     item->mNext = new nsCSSValuePairList;
     item = item->mNext;
   }
 }
 
-void Gecko_CSSValue_InitSharedList(nsCSSValue* aCSSValue, uint32_t aLen) {
-  MOZ_ASSERT(aLen > 0, "Must create at least one nsCSSValueList (mHead)");
-
-  nsCSSValueSharedList* list = new nsCSSValueSharedList;
-  aCSSValue->SetSharedListValue(list);
-  list->mHead = new nsCSSValueList;
-  nsCSSValueList* cur = list->mHead;
-  for (uint32_t i = 1; i < aLen; ++i) {
-    cur->mNext = new nsCSSValueList;
-    cur = cur->mNext;
-  }
-}
-
 void Gecko_CSSValue_Drop(nsCSSValue* aCSSValue) { aCSSValue->~nsCSSValue(); }
 
 void Gecko_nsStyleFont_SetLang(nsStyleFont* aFont, nsAtom* aAtom) {
   aFont->mLanguage = dont_AddRef(aAtom);
   aFont->mExplicitLanguage = true;
 }
 
 void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont,
@@ -2107,18 +2067,16 @@ const char* Gecko_CSSKeywordString(nsCSS
   return value.get();
 }
 
 void Gecko_AddPropertyToSet(nsCSSPropertyIDSet* aPropertySet,
                             nsCSSPropertyID aProperty) {
   aPropertySet->AddProperty(aProperty);
 }
 
-NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
-
 #define STYLE_STRUCT(name)                                             \
                                                                        \
   void Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr,       \
                                              const Document* doc) {    \
     new (ptr) nsStyle##name(*doc);                                     \
   }                                                                    \
                                                                        \
   void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr,           \
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -581,20 +581,16 @@ void Gecko_nsIURI_Debug(nsIURI*, nsCStri
 
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::URLValue, CSSURLValue);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::URLExtraData, URLExtraData);
 
 void Gecko_FillAllImageLayers(nsStyleImageLayers* layers, uint32_t max_len);
 
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
 
-nsCSSValueSharedList* Gecko_NewCSSValueSharedList(uint32_t len);
-nsCSSValueSharedList* Gecko_NewNoneTransform();
-void Gecko_StyleDisplay_GenerateCombinedTransform(nsStyleDisplay*);
-
 // Getter for nsCSSValue
 nsCSSValue* Gecko_CSSValue_GetArrayItem(nsCSSValue*, int32_t index);
 
 // const version of the above function.
 const nsCSSValue* Gecko_CSSValue_GetArrayItemConst(const nsCSSValue*,
                                                    int32_t index);
 
 nsCSSKeyword Gecko_CSSValue_GetKeyword(const nsCSSValue*);
@@ -631,22 +627,18 @@ void Gecko_CSSValue_SetFloat(nsCSSValue*
                              nsCSSUnit unit);
 
 void Gecko_CSSValue_SetPair(nsCSSValue* css_value, const nsCSSValue* xvalue,
                             const nsCSSValue* yvalue);
 
 void Gecko_CSSValue_SetList(nsCSSValue* css_value, uint32_t len);
 void Gecko_CSSValue_SetPairList(nsCSSValue* css_value, uint32_t len);
 
-void Gecko_CSSValue_InitSharedList(nsCSSValue* css_value, uint32_t len);
-
 void Gecko_CSSValue_Drop(nsCSSValue* css_value);
 
-NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
-
 float Gecko_FontStretch_ToFloat(mozilla::FontStretch aStretch);
 
 void Gecko_FontStretch_SetFloat(mozilla::FontStretch* aStretch,
                                 float aFloatValue);
 
 float Gecko_FontSlantStyle_ToFloat(mozilla::FontSlantStyle aStyle);
 void Gecko_FontSlantStyle_SetNormal(mozilla::FontSlantStyle*);
 void Gecko_FontSlantStyle_SetItalic(mozilla::FontSlantStyle*);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -47,16 +47,18 @@ raw-lines = [
     "use atomic_refcell::AtomicRefCell;",
     "use data::ElementData;",
 ]
 hide-types = [
     ".*char_traits",
     ".*incompatible_char_type",
     # https://github.com/rust-lang/rust-bindgen/issues/1503
     "mozilla::StyleTimingFunction.*",
+    # https://github.com/rust-lang/rust-bindgen/issues/1559
+    "mozilla::StyleGenericTransformOperation_.*",
 ]
 bitfield-enums = [
     "nsChangeHint",
     "mozilla::OriginFlags",
 ]
 rusty-enums = [
     "nsCompatibility",
     "mozilla::EffectCompositor_CascadeLevel",
@@ -481,16 +483,22 @@ cbindgen-types = [
     { gecko = "StyleArcSlice", servo = "style_traits::arc_slice::ArcSlice" },
     { gecko = "StyleForgottenArcSlicePtr", servo = "style_traits::arc_slice::ForgottenArcSlicePtr" },
     { gecko = "StyleOwnedSlice", servo = "style_traits::owned_slice::OwnedSlice" },
     { gecko = "StyleMozContextProperties", servo = "values::specified::svg::MozContextProperties" },
     { gecko = "StyleQuotes", servo = "values::specified::list::Quotes" },
     { gecko = "StyleOwnedStr", servo = "style_traits::owned_str::OwnedStr" },
     { gecko = "StyleGenericBoxShadow", servo = "values::generics::effects::BoxShadow" },
     { gecko = "StyleGenericSimpleShadow", servo = "values::generics::effects::SimpleShadow" },
+    { gecko = "StyleGenericTransformOperation", servo = "values::generics::transform::TransformOperation" },
+    { gecko = "StyleGenericTransform", servo = "values::generics::transform::Transform" },
+    { gecko = "StyleGenericScale", servo = "values::generics::transform::Scale" },
+    { gecko = "StyleGenericRotate", servo = "values::generics::transform::Rotate" },
+    { gecko = "StyleGenericTranslate", servo = "values::generics::transform::Translate" },
+    { gecko = "StyleAngle", servo = "values::computed::Angle" }
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
--- a/layout/style/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -155,11 +155,29 @@ inline StyleAtom::StyleAtom(const StyleA
 inline nsAtom* StyleCustomIdent::AsAtom() const { return _0.AsAtom(); }
 
 inline nsDependentCSubstring StyleOwnedStr::AsString() const {
   Span<const uint8_t> s = _0.AsSpan();
   return nsDependentCSubstring(reinterpret_cast<const char*>(s.Elements()),
                                s.Length());
 }
 
+template <typename T>
+inline Span<const T> StyleGenericTransform<T>::Operations() const {
+  return _0.AsSpan();
+}
+
+template <typename T>
+inline bool StyleGenericTransform<T>::IsNone() const {
+  return Operations().IsEmpty();
+}
+
+inline StyleAngle StyleAngle::Zero() { return {0.0f}; }
+
+inline float StyleAngle::ToDegrees() const { return _0; }
+
+inline double StyleAngle::ToRadians() const {
+  return double(ToDegrees()) * M_PI / 180.0;
+}
+
 }  // namespace mozilla
 
 #endif
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -37,206 +37,117 @@
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using nsStyleTransformMatrix::Decompose2DMatrix;
 using nsStyleTransformMatrix::Decompose3DMatrix;
 using nsStyleTransformMatrix::ShearType;
 
-static already_AddRefed<nsCSSValue::Array> AppendFunction(
-    nsCSSKeyword aTransformFunction) {
-  uint32_t nargs;
-  switch (aTransformFunction) {
-    case eCSSKeyword_matrix3d:
-      nargs = 16;
-      break;
-    case eCSSKeyword_matrix:
-      nargs = 6;
-      break;
-    case eCSSKeyword_rotate3d:
-      nargs = 4;
-      break;
-    case eCSSKeyword_interpolatematrix:
-    case eCSSKeyword_accumulatematrix:
-    case eCSSKeyword_translate3d:
-    case eCSSKeyword_scale3d:
-      nargs = 3;
-      break;
-    case eCSSKeyword_translate:
-    case eCSSKeyword_skew:
-    case eCSSKeyword_scale:
-      nargs = 2;
-      break;
-    default:
-      NS_ERROR("must be a transform function");
-      MOZ_FALLTHROUGH;
-    case eCSSKeyword_translatex:
-    case eCSSKeyword_translatey:
-    case eCSSKeyword_translatez:
-    case eCSSKeyword_scalex:
-    case eCSSKeyword_scaley:
-    case eCSSKeyword_scalez:
-    case eCSSKeyword_skewx:
-    case eCSSKeyword_skewy:
-    case eCSSKeyword_rotate:
-    case eCSSKeyword_rotatex:
-    case eCSSKeyword_rotatey:
-    case eCSSKeyword_rotatez:
-    case eCSSKeyword_perspective:
-      nargs = 1;
-      break;
+// TODO(emilio): Remove angle unit in a followup, should always be degrees.
+static inline StyleAngle GetCSSAngle(const layers::CSSAngle& aAngle) {
+  if (aAngle.unit() != eCSSUnit_Degree) {
+    NS_ERROR("Bogus animation from IPC");
+    return StyleAngle{0.0};
   }
-
-  RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(nargs + 1);
-  arr->Item(0).SetIntValue(aTransformFunction, eCSSUnit_Enumerated);
-
-  return arr.forget();
-}
-
-static already_AddRefed<nsCSSValue::Array> AppendTransformFunction(
-    nsCSSKeyword aTransformFunction, nsCSSValueList**& aListTail) {
-  RefPtr<nsCSSValue::Array> arr = AppendFunction(aTransformFunction);
-  nsCSSValueList* item = new nsCSSValueList;
-  item->mValue.SetArrayValue(arr, eCSSUnit_Function);
-
-  *aListTail = item;
-  aListTail = &item->mNext;
-
-  return arr.forget();
-}
-
-struct BogusAnimation {};
-
-static inline Result<Ok, BogusAnimation> SetCSSAngle(
-    const layers::CSSAngle& aAngle, nsCSSValue& aValue) {
-  aValue.SetFloatValue(aAngle.value(), nsCSSUnit(aAngle.unit()));
-  if (!aValue.IsAngularUnit()) {
-    NS_ERROR("Bogus animation from IPC");
-    return Err(BogusAnimation{});
-  }
-  return Ok();
+  return StyleAngle{aAngle.value()};
 }
 
-static Result<nsCSSValueSharedList*, BogusAnimation> CreateCSSValueList(
-    const InfallibleTArray<layers::TransformFunction>& aFunctions) {
-  nsAutoPtr<nsCSSValueList> result;
-  nsCSSValueList** resultTail = getter_Transfers(result);
+static StyleTransformOperation OperationFromLayers(
+    const layers::TransformFunction& aFunction) {
+  switch (aFunction.type()) {
+    case layers::TransformFunction::TRotationX: {
+      const layers::CSSAngle& angle = aFunction.get_RotationX().angle();
+      return StyleTransformOperation::RotateX(GetCSSAngle(angle));
+    }
+    case layers::TransformFunction::TRotationY: {
+      const layers::CSSAngle& angle = aFunction.get_RotationY().angle();
+      return StyleTransformOperation::RotateY(GetCSSAngle(angle));
+    }
+    case layers::TransformFunction::TRotationZ: {
+      const layers::CSSAngle& angle = aFunction.get_RotationZ().angle();
+      return StyleTransformOperation::RotateZ(GetCSSAngle(angle));
+    }
+    case layers::TransformFunction::TRotation: {
+      const layers::CSSAngle& angle = aFunction.get_Rotation().angle();
+      return StyleTransformOperation::Rotate(GetCSSAngle(angle));
+    }
+    case layers::TransformFunction::TRotation3D: {
+      float x = aFunction.get_Rotation3D().x();
+      float y = aFunction.get_Rotation3D().y();
+      float z = aFunction.get_Rotation3D().z();
+      const layers::CSSAngle& angle = aFunction.get_Rotation3D().angle();
+      return StyleTransformOperation::Rotate3D(x, y, z, GetCSSAngle(angle));
+    }
+    case layers::TransformFunction::TScale: {
+      float x = aFunction.get_Scale().x();
+      float y = aFunction.get_Scale().y();
+      float z = aFunction.get_Scale().z();
+      return StyleTransformOperation::Scale3D(x, y, z);
+    }
+    case layers::TransformFunction::TTranslation: {
+      float x = aFunction.get_Translation().x();
+      float y = aFunction.get_Translation().y();
+      float z = aFunction.get_Translation().z();
+      return StyleTransformOperation::Translate3D(
+          LengthPercentage::FromPixels(x), LengthPercentage::FromPixels(y),
+          Length{z});
+    }
+    case layers::TransformFunction::TSkewX: {
+      const layers::CSSAngle& x = aFunction.get_SkewX().x();
+      return StyleTransformOperation::SkewX(GetCSSAngle(x));
+    }
+    case layers::TransformFunction::TSkewY: {
+      const layers::CSSAngle& y = aFunction.get_SkewY().y();
+      return StyleTransformOperation::SkewY(GetCSSAngle(y));
+    }
+    case layers::TransformFunction::TSkew: {
+      const layers::CSSAngle& x = aFunction.get_Skew().x();
+      const layers::CSSAngle& y = aFunction.get_Skew().y();
+      return StyleTransformOperation::Skew(GetCSSAngle(x), GetCSSAngle(y));
+    }
+    case layers::TransformFunction::TTransformMatrix: {
+      const gfx::Matrix4x4& matrix = aFunction.get_TransformMatrix().value();
+      return StyleTransformOperation::Matrix3D({
+          matrix._11,
+          matrix._12,
+          matrix._13,
+          matrix._14,
+          matrix._21,
+          matrix._22,
+          matrix._23,
+          matrix._24,
+          matrix._31,
+          matrix._32,
+          matrix._33,
+          matrix._34,
+          matrix._41,
+          matrix._42,
+          matrix._43,
+          matrix._44,
+      });
+    }
+    case layers::TransformFunction::TPerspective: {
+      float perspective = aFunction.get_Perspective().value();
+      return StyleTransformOperation::Perspective(Length{perspective});
+    }
+    default:
+      MOZ_ASSERT_UNREACHABLE("All functions should be implemented?");
+      return StyleTransformOperation::TranslateX(LengthPercentage::Zero());
+  }
+}
+
+static nsTArray<StyleTransformOperation> CreateTransformList(
+    const nsTArray<layers::TransformFunction>& aFunctions) {
+  nsTArray<StyleTransformOperation> result;
+  result.SetCapacity(aFunctions.Length());
   for (const layers::TransformFunction& function : aFunctions) {
-    RefPtr<nsCSSValue::Array> arr;
-    switch (function.type()) {
-      case layers::TransformFunction::TRotationX: {
-        const layers::CSSAngle& angle = function.get_RotationX().angle();
-        arr = AppendTransformFunction(eCSSKeyword_rotatex, resultTail);
-        MOZ_TRY(SetCSSAngle(angle, arr->Item(1)));
-        break;
-      }
-      case layers::TransformFunction::TRotationY: {
-        const layers::CSSAngle& angle = function.get_RotationY().angle();
-        arr = AppendTransformFunction(eCSSKeyword_rotatey, resultTail);
-        MOZ_TRY(SetCSSAngle(angle, arr->Item(1)));
-        break;
-      }
-      case layers::TransformFunction::TRotationZ: {
-        const layers::CSSAngle& angle = function.get_RotationZ().angle();
-        arr = AppendTransformFunction(eCSSKeyword_rotatez, resultTail);
-        MOZ_TRY(SetCSSAngle(angle, arr->Item(1)));
-        break;
-      }
-      case layers::TransformFunction::TRotation: {
-        const layers::CSSAngle& angle = function.get_Rotation().angle();
-        arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail);
-        MOZ_TRY(SetCSSAngle(angle, arr->Item(1)));
-        break;
-      }
-      case layers::TransformFunction::TRotation3D: {
-        float x = function.get_Rotation3D().x();
-        float y = function.get_Rotation3D().y();
-        float z = function.get_Rotation3D().z();
-        const layers::CSSAngle& angle = function.get_Rotation3D().angle();
-        arr = AppendTransformFunction(eCSSKeyword_rotate3d, resultTail);
-        arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
-        arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
-        arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
-        MOZ_TRY(SetCSSAngle(angle, arr->Item(4)));
-        break;
-      }
-      case layers::TransformFunction::TScale: {
-        arr = AppendTransformFunction(eCSSKeyword_scale3d, resultTail);
-        arr->Item(1).SetFloatValue(function.get_Scale().x(), eCSSUnit_Number);
-        arr->Item(2).SetFloatValue(function.get_Scale().y(), eCSSUnit_Number);
-        arr->Item(3).SetFloatValue(function.get_Scale().z(), eCSSUnit_Number);
-        break;
-      }
-      case layers::TransformFunction::TTranslation: {
-        arr = AppendTransformFunction(eCSSKeyword_translate3d, resultTail);
-        arr->Item(1).SetFloatValue(function.get_Translation().x(),
-                                   eCSSUnit_Pixel);
-        arr->Item(2).SetFloatValue(function.get_Translation().y(),
-                                   eCSSUnit_Pixel);
-        arr->Item(3).SetFloatValue(function.get_Translation().z(),
-                                   eCSSUnit_Pixel);
-        break;
-      }
-      case layers::TransformFunction::TSkewX: {
-        const layers::CSSAngle& x = function.get_SkewX().x();
-        arr = AppendTransformFunction(eCSSKeyword_skewx, resultTail);
-        MOZ_TRY(SetCSSAngle(x, arr->Item(1)));
-        break;
-      }
-      case layers::TransformFunction::TSkewY: {
-        const layers::CSSAngle& y = function.get_SkewY().y();
-        arr = AppendTransformFunction(eCSSKeyword_skewy, resultTail);
-        MOZ_TRY(SetCSSAngle(y, arr->Item(1)));
-        break;
-      }
-      case layers::TransformFunction::TSkew: {
-        const layers::CSSAngle& x = function.get_Skew().x();
-        const layers::CSSAngle& y = function.get_Skew().y();
-        arr = AppendTransformFunction(eCSSKeyword_skew, resultTail);
-        MOZ_TRY(SetCSSAngle(x, arr->Item(1)));
-        MOZ_TRY(SetCSSAngle(y, arr->Item(2)));
-        break;
-      }
-      case layers::TransformFunction::TTransformMatrix: {
-        arr = AppendTransformFunction(eCSSKeyword_matrix3d, resultTail);
-        const gfx::Matrix4x4& matrix = function.get_TransformMatrix().value();
-        arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
-        arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
-        arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number);
-        arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number);
-        arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number);
-        arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number);
-        arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number);
-        arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number);
-        arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number);
-        arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number);
-        arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number);
-        arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number);
-        arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number);
-        arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number);
-        arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number);
-        arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number);
-        break;
-      }
-      case layers::TransformFunction::TPerspective: {
-        float perspective = function.get_Perspective().value();
-        arr = AppendTransformFunction(eCSSKeyword_perspective, resultTail);
-        arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel);
-        break;
-      }
-      default:
-        NS_ASSERTION(false, "All functions should be implemented?");
-    }
+    result.AppendElement(OperationFromLayers(function));
   }
-  if (aFunctions.Length() == 0) {
-    result = new nsCSSValueList();
-    result->mValue.SetNoneValue();
-  }
-  return new nsCSSValueSharedList(result.forget());
+  return result;
 }
 
 // AnimationValue Implementation
 
 bool AnimationValue::operator==(const AnimationValue& aOther) const {
   if (mServo && aOther.mServo) {
     return Servo_AnimationValue_DeepEqual(mServo, aOther.mServo);
   }
@@ -255,27 +166,76 @@ float AnimationValue::GetOpacity() const
   return Servo_AnimationValue_GetOpacity(mServo);
 }
 
 nscolor AnimationValue::GetColor(nscolor aForegroundColor) const {
   MOZ_ASSERT(mServo);
   return Servo_AnimationValue_GetColor(mServo, aForegroundColor);
 }
 
-already_AddRefed<const nsCSSValueSharedList> AnimationValue::GetTransformList()
-    const {
+const StyleTranslate& AnimationValue::GetTranslateProperty() const {
+  MOZ_ASSERT(mServo);
+  return *Servo_AnimationValue_GetTranslate(mServo);
+}
+
+const StyleRotate& AnimationValue::GetRotateProperty() const {
   MOZ_ASSERT(mServo);
-  RefPtr<nsCSSValueSharedList> transform;
-  Servo_AnimationValue_GetTransform(mServo, &transform);
-  return transform.forget();
+  return *Servo_AnimationValue_GetRotate(mServo);
+}
+
+const StyleScale& AnimationValue::GetScaleProperty() const {
+  MOZ_ASSERT(mServo);
+  return *Servo_AnimationValue_GetScale(mServo);
+}
+
+const StyleTransform& AnimationValue::GetTransformProperty() const {
+  MOZ_ASSERT(mServo);
+  return *Servo_AnimationValue_GetTransform(mServo);
 }
 
 Size AnimationValue::GetScaleValue(const nsIFrame* aFrame) const {
-  RefPtr<const nsCSSValueSharedList> list = GetTransformList();
-  return nsStyleTransformMatrix::GetScaleValue(list, aFrame);
+  using namespace nsStyleTransformMatrix;
+
+  const StyleTranslate* translate = nullptr;
+  const StyleRotate* rotate = nullptr;
+  const StyleScale* scale = nullptr;
+  const StyleTransform* transform = nullptr;
+
+  switch (Servo_AnimationValue_GetPropertyId(mServo)) {
+    case eCSSProperty_scale:
+      scale = &GetScaleProperty();
+      break;
+    case eCSSProperty_translate:
+      translate = &GetTranslateProperty();
+      break;
+    case eCSSProperty_rotate:
+      rotate = &GetRotateProperty();
+      break;
+    case eCSSProperty_transform:
+      transform = &GetTransformProperty();
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE(
+          "Should only need to check in transform properties");
+      return Size(1.0, 1.0);
+  }
+
+  TransformReferenceBox refBox(aFrame);
+  Matrix4x4 t =
+      ReadTransforms(translate ? *translate : StyleTranslate::None(),
+                     rotate ? *rotate : StyleRotate::None(),
+                     scale ? *scale : StyleScale::None(), Nothing(),
+                     transform ? *transform : StyleTransform(), refBox,
+                     aFrame->PresContext()->AppUnitsPerDevPixel());
+  Matrix transform2d;
+  bool canDraw2D = t.CanDraw2D(&transform2d);
+  if (!canDraw2D) {
+    return Size();
+  }
+  return transform2d.ScaleFactors(true);
 }
 
 void AnimationValue::SerializeSpecifiedValue(nsCSSPropertyID aProperty,
                                              nsAString& aString) const {
   MOZ_ASSERT(mServo);
   Servo_AnimationValue_Serialize(mServo, aProperty, &aString);
 }
 
@@ -336,106 +296,68 @@ AnimationValue AnimationValue::FromStrin
     return result;
   }
 
   result.mServo = presShell->StyleSet()->ComputeAnimationValue(
       aElement, declarations, computedStyle);
   return result;
 }
 
+StyleRotate RotateFromLayers(const layers::Rotate& aRotate) {
+  switch (aRotate.type()) {
+    case layers::Rotate::Tnull_t:
+      return StyleRotate::None();
+    case layers::Rotate::TRotation: {
+      const layers::CSSAngle& angle = aRotate.get_Rotation().angle();
+      return StyleRotate::Rotate(GetCSSAngle(angle));
+    }
+    case layers::Rotate::TRotation3D: {
+      float x = aRotate.get_Rotation3D().x();
+      float y = aRotate.get_Rotation3D().y();
+      float z = aRotate.get_Rotation3D().z();
+      const layers::CSSAngle& angle = aRotate.get_Rotation3D().angle();
+      return StyleRotate::Rotate3D(x, y, z, GetCSSAngle(angle));
+    }
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unknown rotate value?");
+      return StyleRotate::None();
+  }
+}
+
 /* static */
 already_AddRefed<RawServoAnimationValue> AnimationValue::FromAnimatable(
     nsCSSPropertyID aProperty, const layers::Animatable& aAnimatable) {
-  RefPtr<RawServoAnimationValue> result;
-
   switch (aAnimatable.type()) {
     case layers::Animatable::Tnull_t:
       break;
     case layers::Animatable::TArrayOfTransformFunction: {
-      const InfallibleTArray<layers::TransformFunction>& transforms =
-          aAnimatable.get_ArrayOfTransformFunction();
-      auto listOrError = CreateCSSValueList(transforms);
-      if (listOrError.isOk()) {
-        RefPtr<nsCSSValueSharedList> list = listOrError.unwrap();
-        MOZ_ASSERT(list, "Transform list should be non null");
-        result = Servo_AnimationValue_Transform(eCSSProperty_transform, list)
-                     .Consume();
-      }
-      break;
+      nsTArray<StyleTransformOperation> ops =
+          CreateTransformList(aAnimatable.get_ArrayOfTransformFunction());
+      ;
+      return Servo_AnimationValue_Transform(ops.Elements(), ops.Length())
+          .Consume();
     }
     case layers::Animatable::Tfloat:
-      result = Servo_AnimationValue_Opacity(aAnimatable.get_float()).Consume();
-      break;
+      return Servo_AnimationValue_Opacity(aAnimatable.get_float()).Consume();
     case layers::Animatable::Tnscolor:
-      result = Servo_AnimationValue_Color(aProperty, aAnimatable.get_nscolor())
-                   .Consume();
-      break;
+      return Servo_AnimationValue_Color(aProperty, aAnimatable.get_nscolor())
+          .Consume();
     case layers::Animatable::TRotate: {
-      RefPtr<nsCSSValueSharedList> list = new nsCSSValueSharedList;
-      list->mHead = new nsCSSValueList;
-
-      const layers::Rotate& r = aAnimatable.get_Rotate();
-      if (r.type() == layers::Rotate::Tnull_t) {
-        list->mHead->mValue.SetNoneValue();
-      } else {
-        RefPtr<nsCSSValue::Array> arr;
-        if (r.type() == layers::Rotate::TRotation) {
-          const layers::CSSAngle& angle = r.get_Rotation().angle();
-          arr = AppendFunction(eCSSKeyword_rotate);
-          auto rv = SetCSSAngle(angle, arr->Item(1));
-          if (rv.isErr()) {
-            arr->Item(1).SetFloatValue(0.0, eCSSUnit_Degree);
-          }
-        } else {
-          MOZ_ASSERT(r.type() == layers::Rotate::TRotation3D,
-                     "Should be rotate3D");
-          float x = r.get_Rotation3D().x();
-          float y = r.get_Rotation3D().y();
-          float z = r.get_Rotation3D().z();
-          const layers::CSSAngle& angle = r.get_Rotation3D().angle();
-          arr = AppendFunction(eCSSKeyword_rotate3d);
-          arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
-          arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
-          arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
-          auto rv = SetCSSAngle(angle, arr->Item(4));
-          if (rv.isErr()) {
-            arr->Item(4).SetFloatValue(0.0, eCSSUnit_Degree);
-          }
-        }
-        list->mHead->mValue.SetArrayValue(arr, eCSSUnit_Function);
-      }
-      result =
-          Servo_AnimationValue_Transform(eCSSProperty_rotate, list).Consume();
-      break;
+      auto rotate = RotateFromLayers(aAnimatable.get_Rotate());
+      return Servo_AnimationValue_Rotate(&rotate).Consume();
     }
     case layers::Animatable::TScale: {
-      const layers::Scale& scale = aAnimatable.get_Scale();
-      RefPtr<nsCSSValue::Array> arr = AppendFunction(eCSSKeyword_scale3d);
-      arr->Item(1).SetFloatValue(scale.x(), eCSSUnit_Number);
-      arr->Item(2).SetFloatValue(scale.y(), eCSSUnit_Number);
-      arr->Item(3).SetFloatValue(scale.z(), eCSSUnit_Number);
-
-      RefPtr<nsCSSValueSharedList> list = new nsCSSValueSharedList;
-      list->mHead = new nsCSSValueList;
-      list->mHead->mValue.SetArrayValue(arr, eCSSUnit_Function);
-      result =
-          Servo_AnimationValue_Transform(eCSSProperty_scale, list).Consume();
-      break;
+      const layers::Scale& s = aAnimatable.get_Scale();
+      auto scale = StyleScale::Scale3D(s.x(), s.y(), s.z());
+      return Servo_AnimationValue_Scale(&scale).Consume();
     }
     case layers::Animatable::TTranslation: {
-      const layers::Translation& translate = aAnimatable.get_Translation();
-      RefPtr<nsCSSValue::Array> arr = AppendFunction(eCSSKeyword_translate3d);
-      arr->Item(1).SetFloatValue(translate.x(), eCSSUnit_Pixel);
-      arr->Item(2).SetFloatValue(translate.y(), eCSSUnit_Pixel);
-      arr->Item(3).SetFloatValue(translate.z(), eCSSUnit_Pixel);
-
-      RefPtr<nsCSSValueSharedList> list = new nsCSSValueSharedList;
-      list->mHead = new nsCSSValueList;
-      list->mHead->mValue.SetArrayValue(arr, eCSSUnit_Function);
-      result = Servo_AnimationValue_Transform(eCSSProperty_translate, list)
-                   .Consume();
-      break;
+      const layers::Translation& t = aAnimatable.get_Translation();
+      auto translate = StyleTranslate::Translate3D(
+          LengthPercentage::FromPixels(t.x()),
+          LengthPercentage::FromPixels(t.y()), Length{t.z()});
+      return Servo_AnimationValue_Translate(&translate).Consume();
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported type");
   }
-  return result.forget();
+  return nullptr;
 }
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -72,18 +72,21 @@ struct AnimationValue {
   bool IsNull() const { return !mServo; }
 
   float GetOpacity() const;
 
   // Returns nscolor value in this AnimationValue.
   // Currently only background-color is supported.
   nscolor GetColor(nscolor aForegroundColor) const;
 
-  // Return the transform list as a RefPtr.
-  already_AddRefed<const nsCSSValueSharedList> GetTransformList() const;
+  // Return a transform list for the transform property.
+  const mozilla::StyleTransform& GetTransformProperty() const;
+  const mozilla::StyleScale& GetScaleProperty() const;
+  const mozilla::StyleTranslate& GetTranslateProperty() const;
+  const mozilla::StyleRotate& GetRotateProperty() const;
 
   // Return the scale for mServo, which is calculated with reference to aFrame.
   mozilla::gfx::Size GetScaleValue(const nsIFrame* aFrame) const;
 
   // Uncompute this AnimationValue and then serialize it.
   void SerializeSpecifiedValue(nsCSSPropertyID aProperty,
                                nsAString& aString) const;
 
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1094,17 +1094,17 @@ already_AddRefed<CSSValue> nsComputedDOM
   auto position = MaybeResolvePositionForTransform(
       origin.horizontal, origin.vertical, mInnerFrame);
   SetValueToPosition(position, valueList);
   return valueList.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransform() {
   const nsStyleDisplay* display = StyleDisplay();
-  return GetTransformValue(display->mSpecifiedTransform);
+  return GetTransformValue(display->mTransform);
 }
 
 /* static */
 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::MatrixToCSSValue(
     const mozilla::gfx::Matrix4x4& matrix) {
   bool is3D = !matrix.Is2D();
 
   nsAutoString resultString(NS_LITERAL_STRING("matrix"));
@@ -2497,24 +2497,22 @@ bool nsComputedDOMStyle::GetFrameBorderR
   return true;
 }
 
 /* If the property is "none", hand back "none" wrapped in a value.
  * Otherwise, compute the aggregate transform matrix and hands it back in a
  * "matrix" wrapper.
  */
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetTransformValue(
-    nsCSSValueSharedList* aSpecifiedTransform) {
+    const StyleTransform& aTransform) {
   /* If there are no transforms, then we should construct a single-element
    * entry and hand it back.
    */
-  if (!aSpecifiedTransform) {
+  if (aTransform.IsNone()) {
     RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-
-    /* Set it to "none." */
     val->SetIdent(eCSSKeyword_none);
     return val.forget();
   }
 
   /* Otherwise, we need to compute the current value of the transform matrix,
    * store it in a string, and hand it back to the caller.
    */
 
@@ -2528,18 +2526,17 @@ already_AddRefed<CSSValue> nsComputedDOM
    * problem, because only two of these values can be explicitly referenced
    * using the named transforms.  Until a real solution is found, we'll just
    * use this approach.
    */
   nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame,
                                                        nsSize(0, 0));
 
   gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
-      aSpecifiedTransform->mHead, refBox,
-      float(mozilla::AppUnitsPerCSSPixel()));
+      aTransform, refBox, float(mozilla::AppUnitsPerCSSPixel()));
 
   return MatrixToCSSValue(matrix);
 }
 
 void nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText,
                                            const nsStyleCoord& aCoord,
                                            bool aClampNegativeCalc) {
   RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -183,18 +183,17 @@ class nsComputedDOMStyle final : public 
   already_AddRefed<CSSValue> GetBorderStyleFor(mozilla::Side aSide);
 
   already_AddRefed<CSSValue> GetBorderWidthFor(mozilla::Side aSide);
 
   already_AddRefed<CSSValue> GetBorderColorFor(mozilla::Side aSide);
 
   already_AddRefed<CSSValue> GetMarginWidthFor(mozilla::Side aSide);
 
-  already_AddRefed<CSSValue> GetTransformValue(
-      nsCSSValueSharedList* aSpecifiedTransform);
+  already_AddRefed<CSSValue> GetTransformValue(const mozilla::StyleTransform&);
 
   // Appends all aLineNames (may be empty) space-separated to aResult.
   void AppendGridLineNames(nsString& aResult,
                            const nsTArray<nsString>& aLineNames);
   // Appends aLineNames as a CSSValue* to aValueList.  If aLineNames is empty
   // a value ("[]") is only appended if aSuppressEmptyList is false.
   void AppendGridLineNames(nsDOMCSSValueList* aValueList,
                            const nsTArray<nsString>& aLineNames,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2992,20 +2992,20 @@ nsStyleDisplay::nsStyleDisplay(const nsS
       mScrollSnapPointsX(aSource.mScrollSnapPointsX),
       mScrollSnapPointsY(aSource.mScrollSnapPointsY),
       mScrollSnapDestination(aSource.mScrollSnapDestination),
       mScrollSnapCoordinate(aSource.mScrollSnapCoordinate),
       mLineClamp(aSource.mLineClamp),
       mBackfaceVisibility(aSource.mBackfaceVisibility),
       mTransformStyle(aSource.mTransformStyle),
       mTransformBox(aSource.mTransformBox),
-      mSpecifiedTransform(aSource.mSpecifiedTransform),
-      mSpecifiedRotate(aSource.mSpecifiedRotate),
-      mSpecifiedTranslate(aSource.mSpecifiedTranslate),
-      mSpecifiedScale(aSource.mSpecifiedScale),
+      mTransform(aSource.mTransform),
+      mRotate(aSource.mRotate),
+      mTranslate(aSource.mTranslate),
+      mScale(aSource.mScale),
       // We intentionally leave mIndividualTransform as null, is the caller's
       // responsibility to call GenerateCombinedIndividualTransform when
       // appropriate.
       mMotion(aSource.mMotion ? MakeUnique<StyleMotion>(*aSource.mMotion)
                               : nullptr),
       mTransformOrigin(aSource.mTransformOrigin),
       mChildPerspective(aSource.mChildPerspective),
       mPerspectiveOrigin(aSource.mPerspectiveOrigin),
@@ -3025,77 +3025,38 @@ nsStyleDisplay::nsStyleDisplay(const nsS
       mAnimationPlayStateCount(aSource.mAnimationPlayStateCount),
       mAnimationIterationCountCount(aSource.mAnimationIterationCountCount),
       mShapeImageThreshold(aSource.mShapeImageThreshold),
       mShapeMargin(aSource.mShapeMargin),
       mShapeOutside(aSource.mShapeOutside) {
   MOZ_COUNT_CTOR(nsStyleDisplay);
 }
 
-static void ReleaseSharedListOnMainThread(const char* aName,
-                                          RefPtr<nsCSSValueSharedList>& aList) {
-  // We don't allow releasing nsCSSValues with refcounted data in the Servo
-  // traversal, since the refcounts aren't threadsafe. Since Servo may trigger
-  // the deallocation of style structs during styling, we need to handle it
-  // here.
-  if (aList && ServoStyleSet::IsInServoTraversal()) {
-    // The default behavior of NS_ReleaseOnMainThreadSystemGroup is to only
-    // proxy the release if we're not already on the main thread. This is a nice
-    // optimization for the cases we happen to be doing a sequential traversal
-    // (i.e. a single-core machine), but it trips our assertions which check
-    // whether we're in a Servo traversal, parallel or not. So we
-    // unconditionally proxy in debug builds.
-    bool alwaysProxy =
-#ifdef DEBUG
-        true;
-#else
-        false;
-#endif
-    NS_ReleaseOnMainThreadSystemGroup(aName, aList.forget(), alwaysProxy);
-  }
-}
-
 nsStyleDisplay::~nsStyleDisplay() {
-  ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedTransform",
-                                mSpecifiedTransform);
-  ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedRotate",
-                                mSpecifiedRotate);
-  ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedTranslate",
-                                mSpecifiedTranslate);
-  ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedScale",
-                                mSpecifiedScale);
-  ReleaseSharedListOnMainThread("nsStyleDisplay::mIndividualTransform",
-                                mIndividualTransform);
   MOZ_COUNT_DTOR(nsStyleDisplay);
 }
 
 void nsStyleDisplay::TriggerImageLoads(Document& aDocument,
                                        const nsStyleDisplay* aOldStyle) {
   MOZ_ASSERT(NS_IsMainThread());
 
   mShapeOutside.TriggerImageLoads(
       aDocument, aOldStyle ? &aOldStyle->mShapeOutside : nullptr);
 }
 
-static inline bool TransformListChanged(
-    const RefPtr<nsCSSValueSharedList>& aList,
-    const RefPtr<nsCSSValueSharedList>& aNewList) {
-  return !aList != !aNewList || (aList && *aList != *aNewList);
-}
-
+template <typename TransformLike>
 static inline nsChangeHint CompareTransformValues(
-    const RefPtr<nsCSSValueSharedList>& aList,
-    const RefPtr<nsCSSValueSharedList>& aNewList) {
+    const TransformLike& aOldTransform, const TransformLike& aNewTransform) {
   nsChangeHint result = nsChangeHint(0);
 
   // Note: If we add a new change hint for transform changes here, we have to
   // modify KeyframeEffect::CalculateCumulativeChangeHint too!
-  if (!aList != !aNewList || (aList && *aList != *aNewList)) {
+  if (aOldTransform != aNewTransform) {
     result |= nsChangeHint_UpdateTransformLayer;
-    if (aList && aNewList) {
+    if (!aOldTransform.IsNone() && !aNewTransform.IsNone()) {
       result |= nsChangeHint_UpdatePostTransformOverflow;
     } else {
       result |= nsChangeHint_UpdateOverflow;
     }
   }
 
   return result;
 }
@@ -3234,24 +3195,20 @@ nsChangeHint nsStyleDisplay::CalcDiffere
      * overflow rect.
      *
      * If the property isn't present in either style struct, we still do the
      * comparisons but turn all the resulting change hints into
      * nsChangeHint_NeutralChange.
      */
     nsChangeHint transformHint = nsChangeHint(0);
 
-    transformHint |= CompareTransformValues(mSpecifiedTransform,
-                                            aNewData.mSpecifiedTransform);
-    transformHint |=
-        CompareTransformValues(mSpecifiedRotate, aNewData.mSpecifiedRotate);
-    transformHint |= CompareTransformValues(mSpecifiedTranslate,
-                                            aNewData.mSpecifiedTranslate);
-    transformHint |=
-        CompareTransformValues(mSpecifiedScale, aNewData.mSpecifiedScale);
+    transformHint |= CompareTransformValues(mTransform, aNewData.mTransform);
+    transformHint |= CompareTransformValues(mRotate, aNewData.mRotate);
+    transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate);
+    transformHint |= CompareTransformValues(mScale, aNewData.mScale);
     transformHint |= CompareMotionValues(mMotion.get(), aNewData.mMotion.get());
 
     if (mTransformOrigin != aNewData.mTransformOrigin) {
       transformHint |= nsChangeHint_UpdateTransformLayer |
                        nsChangeHint_UpdatePostTransformOverflow;
     }
 
     if (mPerspectiveOrigin != aNewData.mPerspectiveOrigin ||
@@ -3354,78 +3311,16 @@ nsChangeHint nsStyleDisplay::CalcDiffere
                 mScrollSnapCoordinate != aNewData.mScrollSnapCoordinate ||
                 mWillChange != aNewData.mWillChange)) {
     hint |= nsChangeHint_NeutralChange;
   }
 
   return hint;
 }
 
-bool nsStyleDisplay::TransformChanged(const nsStyleDisplay& aNewData) const {
-  return TransformListChanged(mSpecifiedTransform,
-                              aNewData.mSpecifiedTransform);
-}
-
-/* static */
-already_AddRefed<nsCSSValueSharedList>
-nsStyleDisplay::GenerateCombinedIndividualTransform(
-    nsCSSValueSharedList* aTranslate, nsCSSValueSharedList* aRotate,
-    nsCSSValueSharedList* aScale) {
-  // Follow the order defined in the spec to append transform functions.
-  // https://drafts.csswg.org/css-transforms-2/#ctm
-  AutoTArray<nsCSSValueSharedList*, 3> shareLists;
-  if (aTranslate) {
-    shareLists.AppendElement(aTranslate);
-  }
-
-  if (aRotate) {
-    shareLists.AppendElement(aRotate);
-  }
-
-  if (aScale) {
-    shareLists.AppendElement(aScale);
-  }
-
-  if (shareLists.IsEmpty()) {
-    return nullptr;
-  }
-
-  if (shareLists.Length() == 1) {
-    return RefPtr<nsCSSValueSharedList>(shareLists[0]).forget();
-  }
-
-  // In common, we may have 3 transform functions:
-  // 1. one rotate function in aRotate,
-  // 2. one translate function in aTranslate,
-  // 3. one scale function in aScale.
-  AutoTArray<nsCSSValueList*, 3> valueLists;
-  for (auto list : shareLists) {
-    if (list) {
-      valueLists.AppendElement(list->mHead->Clone());
-    }
-  }
-
-  // Check we have at least one list or else valueLists.Length() - 1 below will
-  // underflow.
-  MOZ_ASSERT(!valueLists.IsEmpty());
-
-  for (uint32_t i = 0; i < valueLists.Length() - 1; i++) {
-    valueLists[i]->mNext = valueLists[i + 1];
-  }
-
-  RefPtr<nsCSSValueSharedList> list = new nsCSSValueSharedList(valueLists[0]);
-  return list.forget();
-}
-
-void nsStyleDisplay::GenerateCombinedIndividualTransform() {
-  MOZ_ASSERT(!mIndividualTransform);
-  mIndividualTransform = GenerateCombinedIndividualTransform(
-      mSpecifiedTranslate, mSpecifiedRotate, mSpecifiedScale);
-}
-
 // --------------------
 // nsStyleVisibility
 //
 
 nsStyleVisibility::nsStyleVisibility(const Document& aDocument)
     : mDirection(aDocument.GetBidiOptions() == IBMBIDI_TEXTDIRECTION_RTL
                      ? NS_STYLE_DIRECTION_RTL
                      : NS_STYLE_DIRECTION_LTR),
@@ -3980,41 +3875,37 @@ nsChangeHint nsStyleUI::CalcDifference(c
 nsStyleUIReset::nsStyleUIReset(const Document& aDocument)
     : mUserSelect(StyleUserSelect::Auto),
       mScrollbarWidth(StyleScrollbarWidth::Auto),
       mForceBrokenImageIcon(0),
       mIMEMode(NS_STYLE_IME_MODE_AUTO),
       mWindowDragging(StyleWindowDragging::Default),
       mWindowShadow(NS_STYLE_WINDOW_SHADOW_DEFAULT),
       mWindowOpacity(1.0),
-      mSpecifiedWindowTransform(nullptr),
       mWindowTransformOrigin{LengthPercentage::FromPercentage(0.5),
                              LengthPercentage::FromPercentage(0.5),
                              {0.}} {
   MOZ_COUNT_CTOR(nsStyleUIReset);
 }
 
 nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
     : mUserSelect(aSource.mUserSelect),
       mScrollbarWidth(aSource.mScrollbarWidth),
       mForceBrokenImageIcon(aSource.mForceBrokenImageIcon),
       mIMEMode(aSource.mIMEMode),
       mWindowDragging(aSource.mWindowDragging),
       mWindowShadow(aSource.mWindowShadow),
       mWindowOpacity(aSource.mWindowOpacity),
-      mSpecifiedWindowTransform(aSource.mSpecifiedWindowTransform),
+      mMozWindowTransform(aSource.mMozWindowTransform),
       mWindowTransformOrigin(aSource.mWindowTransformOrigin) {
   MOZ_COUNT_CTOR(nsStyleUIReset);
 }
 
 nsStyleUIReset::~nsStyleUIReset() {
   MOZ_COUNT_DTOR(nsStyleUIReset);
-
-  ReleaseSharedListOnMainThread("nsStyleUIReset::mSpecifiedWindowTransform",
-                                mSpecifiedWindowTransform);
 }
 
 nsChangeHint nsStyleUIReset::CalcDifference(
     const nsStyleUIReset& aNewData) const {
   nsChangeHint hint = nsChangeHint(0);
 
   if (mForceBrokenImageIcon != aNewData.mForceBrokenImageIcon) {
     hint |= nsChangeHint_ReconstructFrame;
@@ -4035,20 +3926,17 @@ nsChangeHint nsStyleUIReset::CalcDiffere
     hint |= NS_STYLE_HINT_VISUAL;
   }
 
   if (mWindowDragging != aNewData.mWindowDragging) {
     hint |= nsChangeHint_SchedulePaint;
   }
 
   if (mWindowOpacity != aNewData.mWindowOpacity ||
-      !mSpecifiedWindowTransform != !aNewData.mSpecifiedWindowTransform ||
-      (mSpecifiedWindowTransform &&
-       *mSpecifiedWindowTransform != *aNewData.mSpecifiedWindowTransform) ||
-      mWindowTransformOrigin != aNewData.mWindowTransformOrigin) {
+      mMozWindowTransform != aNewData.mMozWindowTransform) {
     hint |= nsChangeHint_UpdateWidgetProperties;
   }
 
   if (!hint && mIMEMode != aNewData.mIMEMode) {
     hint |= nsChangeHint_NeutralChange;
   }
 
   return hint;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1694,18 +1694,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsStyleDisplay(const nsStyleDisplay& aOther);
   ~nsStyleDisplay();
 
   void TriggerImageLoads(mozilla::dom::Document&, const nsStyleDisplay*);
   const static bool kHasTriggerImageLoads = true;
 
   nsChangeHint CalcDifference(const nsStyleDisplay& aNewData) const;
 
-  bool TransformChanged(const nsStyleDisplay& aNewData) const;
-
   // We guarantee that if mBinding is non-null, so are mBinding->GetURI() and
   // mBinding->mOriginPrincipal.
   RefPtr<mozilla::css::URLValue> mBinding;
   mozilla::StyleDisplay mDisplay;
   mozilla::StyleDisplay mOriginalDisplay;  // saved mDisplay for
                                            //         position:absolute/fixed
                                            //         and float:left/right;
                                            //         otherwise equal to
@@ -1747,23 +1745,22 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   // 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.)
   uint8_t mBackfaceVisibility;
   uint8_t mTransformStyle;
   StyleGeometryBox mTransformBox;
-  RefPtr<nsCSSValueSharedList> mSpecifiedTransform;
-  RefPtr<nsCSSValueSharedList> mSpecifiedRotate;
-  RefPtr<nsCSSValueSharedList> mSpecifiedTranslate;
-  RefPtr<nsCSSValueSharedList> mSpecifiedScale;
-  // Used to store the final combination of mSpecifiedRotate,
-  // mSpecifiedTranslate, and mSpecifiedScale.
-  RefPtr<nsCSSValueSharedList> mIndividualTransform;
+
+  mozilla::StyleTransform mTransform;
+  mozilla::StyleRotate mRotate;
+  mozilla::StyleTranslate mTranslate;
+  mozilla::StyleScale mScale;
+
   mozilla::UniquePtr<mozilla::StyleMotion> mMotion;
 
   mozilla::StyleTransformOrigin mTransformOrigin;
   mozilla::StylePerspective mChildPerspective;
   mozilla::Position mPerspectiveOrigin;
 
   mozilla::StyleVerticalAlign mVerticalAlign;
 
@@ -1994,28 +1991,29 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     // responsible for checking if the box in question is
     // non-atomic and inline-level, and creating an
     // exemption as necessary.
     return (mContain & mozilla::StyleContain_SIZE) &&
            !IsInternalRubyDisplayType() &&
            (mozilla::StyleDisplay::Table != mDisplay) && !IsInnerTableStyle();
   }
 
-  /* Returns whether the element has the -moz-transform property
-   * or a related property. */
+  /* Returns whether the element has the transform property or a related
+   * property. */
   bool HasTransformStyle() const {
-    return mSpecifiedTransform || mSpecifiedRotate || mSpecifiedTranslate ||
-           mSpecifiedScale ||
+    return HasTransformProperty() || HasIndividualTransform() ||
            mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
            (mWillChange.bits & mozilla::StyleWillChangeBits_TRANSFORM) ||
            (mMotion && mMotion->HasPath());
   }
 
+  bool HasTransformProperty() const { return !mTransform._0.IsEmpty(); }
+
   bool HasIndividualTransform() const {
-    return mSpecifiedRotate || mSpecifiedTranslate || mSpecifiedScale;
+    return !mRotate.IsNone() || !mTranslate.IsNone() || !mScale.IsNone();
   }
 
   bool HasPerspectiveStyle() const { return !mChildPerspective.IsNone(); }
 
   bool BackfaceIsHidden() const {
     return mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN;
   }
 
@@ -2112,33 +2110,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
    * frame (i.e., when calculating style changes).
    */
   inline bool IsFixedPosContainingBlockForNonSVGTextFrames(
       const mozilla::ComputedStyle&) const;
   inline bool
   IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() const;
   inline bool IsFixedPosContainingBlockForTransformSupportingFrames() const;
 
-  /**
-   * Returns the final combined individual transform.
-   **/
-  already_AddRefed<nsCSSValueSharedList> GetCombinedTransform() const {
-    return mIndividualTransform ? do_AddRef(mIndividualTransform) : nullptr;
-  }
-
-  /**
-   * Returns the combined transform list based on translate, rotate, scale
-   * individual transforms. The combination order is defined in
-   * https://drafts.csswg.org/css-transforms-2/#ctm
-   */
-  static already_AddRefed<nsCSSValueSharedList>
-  GenerateCombinedIndividualTransform(nsCSSValueSharedList* aTranslate,
-                                      nsCSSValueSharedList* aRotate,
-                                      nsCSSValueSharedList* aScale);
-
   void GenerateCombinedIndividualTransform();
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable {
   explicit nsStyleTable(const mozilla::dom::Document&);
   nsStyleTable(const nsStyleTable& aOther);
   ~nsStyleTable();
   void TriggerImageLoads(mozilla::dom::Document&, const nsStyleTable*) {}
@@ -2372,17 +2353,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   mozilla::StyleUserSelect mUserSelect;  // [reset](selection-style)
   mozilla::StyleScrollbarWidth mScrollbarWidth;
   uint8_t mForceBrokenImageIcon;  // (0 if not forcing, otherwise forcing)
   uint8_t mIMEMode;
   mozilla::StyleWindowDragging mWindowDragging;
   uint8_t mWindowShadow;
   float mWindowOpacity;
-  RefPtr<nsCSSValueSharedList> mSpecifiedWindowTransform;
+  mozilla::StyleTransform mMozWindowTransform;
   mozilla::StyleTransformOrigin mWindowTransformOrigin;
 };
 
 struct nsCursorImage {
   bool mHaveHotspot;
   float mHotspotX, mHotspotY;
   RefPtr<nsStyleImageRequest> mImage;
 
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -122,108 +122,70 @@ void TransformReferenceBox::Init(const n
   mX = 0;
   mY = 0;
   mWidth = aDimensions.width;
   mHeight = aDimensions.height;
   mIsCached = true;
 }
 
 float ProcessTranslatePart(
-    const nsCSSValue& aValue, TransformReferenceBox* aRefBox,
+    const LengthPercentage& aValue, TransformReferenceBox* aRefBox,
     TransformReferenceBox::DimensionGetter aDimensionGetter) {
-  nscoord offset = 0;
-  float percent = 0.0f;
-
-  if (aValue.GetUnit() == eCSSUnit_Percent) {
-    percent = aValue.GetPercentValue();
-  } else if (aValue.GetUnit() == eCSSUnit_Pixel ||
-             aValue.GetUnit() == eCSSUnit_Number) {
-    // Raw numbers are treated as being pixels.
-    return aValue.GetFloatValue();
-  } else if (aValue.IsCalcUnit()) {
-    // We can retrieve the Calc value directly because it has been computed
-    // from the Servo side and set by nsCSSValue::SetCalcValue().
-    nsStyleCoord::CalcValue calc = aValue.GetCalcValue();
-    percent = calc.mPercent;
-    offset = calc.mLength;
-  } else {
-    // Note: The unit of nsCSSValue passed from Servo side would be number,
-    //       pixel, percent, or eCSSUnit_Calc, so it is impossible to go into
-    //       this branch.
-    MOZ_CRASH("unexpected unit in ProcessTranslatePart");
-  }
-
-  float translation = NSAppUnitsToFloatPixels(offset, AppUnitsPerCSSPixel());
-  // We want to avoid calling aDimensionGetter if there's no percentage to be
-  // resolved (for performance reasons - see TransformReferenceBox).
-  if (percent != 0.0f && aRefBox && !aRefBox->IsEmpty()) {
-    translation +=
-        percent * NSAppUnitsToFloatPixels((aRefBox->*aDimensionGetter)(),
-                                          AppUnitsPerCSSPixel());
-  }
-  return translation;
+  return aValue.ResolveToCSSPixelsWith([&] {
+    return aRefBox && !aRefBox->IsEmpty()
+               ? CSSPixel::FromAppUnits((aRefBox->*aDimensionGetter)())
+               : CSSCoord(0);
+  });
 }
 
 /**
  * Helper functions to process all the transformation function types.
  *
  * These take a matrix parameter to accumulate the current matrix.
  */
 
 /* Helper function to process a matrix entry. */
-static void ProcessMatrix(Matrix4x4& aMatrix, const nsCSSValue::Array* aData,
-                          TransformReferenceBox& aRefBox) {
-  MOZ_ASSERT(aData->Count() == 7, "Invalid array!");
-
+static void ProcessMatrix(Matrix4x4& aMatrix,
+                          const StyleTransformOperation& aOp) {
+  const auto& matrix = aOp.AsMatrix();
   gfxMatrix result;
 
-  /* Take the first four elements out of the array as floats and store
-   * them.
-   */
-  result._11 = aData->Item(1).GetFloatValue();
-  result._12 = aData->Item(2).GetFloatValue();
-  result._21 = aData->Item(3).GetFloatValue();
-  result._22 = aData->Item(4).GetFloatValue();
-
-  /* The last two elements have their length parts stored in aDelta
-   * and their percent parts stored in aX[0] and aY[1].
-   */
-  result._31 = ProcessTranslatePart(aData->Item(5), &aRefBox,
-                                    &TransformReferenceBox::Width);
-  result._32 = ProcessTranslatePart(aData->Item(6), &aRefBox,
-                                    &TransformReferenceBox::Height);
+  result._11 = matrix.a;
+  result._12 = matrix.b;
+  result._21 = matrix.c;
+  result._22 = matrix.d;
+  result._31 = matrix.e;
+  result._32 = matrix.f;
 
   aMatrix = result * aMatrix;
 }
 
-static void ProcessMatrix3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData,
-                            TransformReferenceBox& aRefBox) {
-  MOZ_ASSERT(aData->Count() == 17, "Invalid array!");
-
+static void ProcessMatrix3D(Matrix4x4& aMatrix,
+                            const StyleTransformOperation& aOp) {
   Matrix4x4 temp;
 
-  temp._11 = aData->Item(1).GetFloatValue();
-  temp._12 = aData->Item(2).GetFloatValue();
-  temp._13 = aData->Item(3).GetFloatValue();
-  temp._14 = aData->Item(4).GetFloatValue();
-  temp._21 = aData->Item(5).GetFloatValue();
-  temp._22 = aData->Item(6).GetFloatValue();
-  temp._23 = aData->Item(7).GetFloatValue();
-  temp._24 = aData->Item(8).GetFloatValue();
-  temp._31 = aData->Item(9).GetFloatValue();
-  temp._32 = aData->Item(10).GetFloatValue();
-  temp._33 = aData->Item(11).GetFloatValue();
-  temp._34 = aData->Item(12).GetFloatValue();
-  temp._44 = aData->Item(16).GetFloatValue();
+  const auto& matrix = aOp.AsMatrix3D();
 
-  temp._41 = ProcessTranslatePart(aData->Item(13), &aRefBox,
-                                  &TransformReferenceBox::Width);
-  temp._42 = ProcessTranslatePart(aData->Item(14), &aRefBox,
-                                  &TransformReferenceBox::Height);
-  temp._43 = ProcessTranslatePart(aData->Item(15), nullptr);
+  temp._11 = matrix.m11;
+  temp._12 = matrix.m12;
+  temp._13 = matrix.m13;
+  temp._14 = matrix.m14;
+  temp._21 = matrix.m21;
+  temp._22 = matrix.m22;
+  temp._23 = matrix.m23;
+  temp._24 = matrix.m24;
+  temp._31 = matrix.m31;
+  temp._32 = matrix.m32;
+  temp._33 = matrix.m33;
+  temp._34 = matrix.m34;
+
+  temp._41 = matrix.m41;
+  temp._42 = matrix.m42;
+  temp._43 = matrix.m43;
+  temp._44 = matrix.m44;
 
   aMatrix = temp * aMatrix;
 }
 
 // For accumulation for transform functions, |aOne| corresponds to |aB| and
 // |aTwo| corresponds to |aA| for StyleAnimationValue::Accumulate().
 class Accumulate {
  public:
@@ -405,346 +367,212 @@ template <typename Operator>
 static Matrix4x4 OperateTransformMatrixByServo(const Matrix4x4& aMatrix1,
                                                const Matrix4x4& aMatrix2,
                                                double aProgress) {
   return Operator::operateByServo(aMatrix1, aMatrix2, aProgress);
 }
 
 template <typename Operator>
 static void ProcessMatrixOperator(Matrix4x4& aMatrix,
-                                  const nsCSSValue::Array* aData,
+                                  const StyleTransform& aFrom,
+                                  const StyleTransform& aTo, float aProgress,
                                   TransformReferenceBox& aRefBox) {
-  MOZ_ASSERT(aData->Count() == 4, "Invalid array!");
-
-  auto readTransform = [&](const nsCSSValue& aValue) -> Matrix4x4 {
-    const nsCSSValueList* list = nullptr;
-    switch (aValue.GetUnit()) {
-      case eCSSUnit_List:
-        // For Gecko style backend.
-        list = aValue.GetListValue();
-        break;
-      case eCSSUnit_SharedList:
-        // For Servo style backend. The transform lists of interpolatematrix
-        // are not created on the main thread (i.e. during parallel traversal),
-        // and nsCSSValueList_heap is not thread safe. Therefore, we use
-        // nsCSSValueSharedList as a workaround.
-        list = aValue.GetSharedListValue()->mHead;
-        break;
-      default:
-        list = nullptr;
-    }
+  float appUnitPerCSSPixel = AppUnitsPerCSSPixel();
+  Matrix4x4 matrix1 = ReadTransforms(aFrom, aRefBox, appUnitPerCSSPixel);
+  Matrix4x4 matrix2 = ReadTransforms(aTo, aRefBox, appUnitPerCSSPixel);
 
-    Matrix4x4 matrix;
-    if (!list) {
-      return matrix;
-    }
-
-    float appUnitPerCSSPixel = AppUnitsPerCSSPixel();
-    matrix = nsStyleTransformMatrix::ReadTransforms(list, aRefBox,
-                                                    appUnitPerCSSPixel);
-    return matrix;
-  };
-
-  Matrix4x4 matrix1 = readTransform(aData->Item(1));
-  Matrix4x4 matrix2 = readTransform(aData->Item(2));
-  double progress = aData->Item(3).GetPercentValue();
-
-  // We cannot use GeckoComputedStyle to check if we use Servo backend because
-  // it could be null in Gecko. Instead, use the unit of the nsCSSValue because
-  // we use eCSSUnit_SharedList for Servo backend.
-  if (aData->Item(1).GetUnit() == eCSSUnit_SharedList) {
+  // TODO(emilio): I think the legacy decomposition code couldn't be reached
+  // before, probably just remove it?
+  const bool kUseLegacyDecomposition = false;
+  if (kUseLegacyDecomposition) {
     aMatrix =
-        OperateTransformMatrixByServo<Operator>(matrix1, matrix2, progress) *
-        aMatrix;
+        OperateTransformMatrix<Operator>(matrix1, matrix2, aProgress) * aMatrix;
     return;
   }
 
   aMatrix =
-      OperateTransformMatrix<Operator>(matrix1, matrix2, progress) * aMatrix;
+      OperateTransformMatrixByServo<Operator>(matrix1, matrix2, aProgress) *
+      aMatrix;
 }
 
 /* Helper function to process two matrices that we need to interpolate between
  */
 void ProcessInterpolateMatrix(Matrix4x4& aMatrix,
-                              const nsCSSValue::Array* aData,
+                              const StyleTransformOperation& aOp,
                               TransformReferenceBox& aRefBox) {
-  ProcessMatrixOperator<Interpolate>(aMatrix, aData, aRefBox);
+  const auto& args = aOp.AsInterpolateMatrix();
+  ProcessMatrixOperator<Interpolate>(aMatrix, args.from_list, args.to_list,
+                                     args.progress._0, aRefBox);
 }
 
-void ProcessAccumulateMatrix(Matrix4x4& aMatrix, const nsCSSValue::Array* aData,
+void ProcessAccumulateMatrix(Matrix4x4& aMatrix,
+                             const StyleTransformOperation& aOp,
                              TransformReferenceBox& aRefBox) {
-  ProcessMatrixOperator<Accumulate>(aMatrix, aData, aRefBox);
+  const auto& args = aOp.AsAccumulateMatrix();
+  ProcessMatrixOperator<Accumulate>(aMatrix, args.from_list, args.to_list,
+                                    args.count, aRefBox);
 }
 
 /* Helper function to process a translatex function. */
 static void ProcessTranslateX(Matrix4x4& aMatrix,
-                              const nsCSSValue::Array* aData,
+                              const LengthPercentage& aLength,
                               TransformReferenceBox& aRefBox) {
-  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
-
   Point3D temp;
-
-  temp.x = ProcessTranslatePart(aData->Item(1), &aRefBox,
-                                &TransformReferenceBox::Width);
+  temp.x =
+      ProcessTranslatePart(aLength, &aRefBox, &TransformReferenceBox::Width);
   aMatrix.PreTranslate(temp);
 }
 
 /* Helper function to process a translatey function. */
 static void ProcessTranslateY(Matrix4x4& aMatrix,
-                              const nsCSSValue::Array* aData,
+                              const LengthPercentage& aLength,
                               TransformReferenceBox& aRefBox) {
-  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
-
   Point3D temp;
-
-  temp.y = ProcessTranslatePart(aData->Item(1), &aRefBox,
-                                &TransformReferenceBox::Height);
+  temp.y =
+      ProcessTranslatePart(aLength, &aRefBox, &TransformReferenceBox::Height);
   aMatrix.PreTranslate(temp);
 }
 
-static void ProcessTranslateZ(Matrix4x4& aMatrix,
-                              const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
-
+static void ProcessTranslateZ(Matrix4x4& aMatrix, const Length& aLength) {
   Point3D temp;
-
-  temp.z = ProcessTranslatePart(aData->Item(1), nullptr);
+  temp.z = aLength.ToCSSPixels();
   aMatrix.PreTranslate(temp);
 }
 
 /* Helper function to process a translate function. */
-static void ProcessTranslate(Matrix4x4& aMatrix, const nsCSSValue::Array* aData,
+static void ProcessTranslate(Matrix4x4& aMatrix, const LengthPercentage& aX,
+                             const LengthPercentage& aY,
                              TransformReferenceBox& aRefBox) {
-  MOZ_ASSERT(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");
-
   Point3D temp;
-
-  temp.x = ProcessTranslatePart(aData->Item(1), &aRefBox,
-                                &TransformReferenceBox::Width);
-
-  /* If we read in a Y component, set it appropriately */
-  if (aData->Count() == 3) {
-    temp.y = ProcessTranslatePart(aData->Item(2), &aRefBox,
-                                  &TransformReferenceBox::Height);
-  }
+  temp.x = ProcessTranslatePart(aX, &aRefBox, &TransformReferenceBox::Width);
+  temp.y = ProcessTranslatePart(aY, &aRefBox, &TransformReferenceBox::Height);
   aMatrix.PreTranslate(temp);
 }
 
-static void ProcessTranslate3D(Matrix4x4& aMatrix,
-                               const nsCSSValue::Array* aData,
+static void ProcessTranslate3D(Matrix4x4& aMatrix, const LengthPercentage& aX,
+                               const LengthPercentage& aY, const Length& aZ,
                                TransformReferenceBox& aRefBox) {
-  MOZ_ASSERT(aData->Count() == 4, "Invalid array!");
-
   Point3D temp;
 
-  temp.x = ProcessTranslatePart(aData->Item(1), &aRefBox,
-                                &TransformReferenceBox::Width);
-
-  temp.y = ProcessTranslatePart(aData->Item(2), &aRefBox,
-                                &TransformReferenceBox::Height);
-
-  temp.z = ProcessTranslatePart(aData->Item(3), nullptr);
+  temp.x = ProcessTranslatePart(aX, &aRefBox, &TransformReferenceBox::Width);
+  temp.y = ProcessTranslatePart(aY, &aRefBox, &TransformReferenceBox::Height);
+  temp.z = aZ.ToCSSPixels();
 
   aMatrix.PreTranslate(temp);
 }
 
 /* Helper function to set up a scale matrix. */
 static void ProcessScaleHelper(Matrix4x4& aMatrix, float aXScale, float aYScale,
                                float aZScale) {
   aMatrix.PreScale(aXScale, aYScale, aZScale);
 }
 
-/* Process a scalex function. */
-static void ProcessScaleX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2, "Bad array!");
-  ProcessScaleHelper(aMatrix, aData->Item(1).GetFloatValue(), 1.0f, 1.0f);
-}
-
-/* Process a scaley function. */
-static void ProcessScaleY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2, "Bad array!");
-  ProcessScaleHelper(aMatrix, 1.0f, aData->Item(1).GetFloatValue(), 1.0f);
-}
-
-static void ProcessScaleZ(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2, "Bad array!");
-  ProcessScaleHelper(aMatrix, 1.0f, 1.0f, aData->Item(1).GetFloatValue());
-}
-
-static void ProcessScale3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 4, "Bad array!");
-  ProcessScaleHelper(aMatrix, aData->Item(1).GetFloatValue(),
-                     aData->Item(2).GetFloatValue(),
-                     aData->Item(3).GetFloatValue());
-}
-
-/* Process a scale function. */
-static void ProcessScale(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
-  /* We either have one element or two.  If we have one, it's for both X and Y.
-   * Otherwise it's one for each.
-   */
-  const nsCSSValue& scaleX = aData->Item(1);
-  const nsCSSValue& scaleY = (aData->Count() == 2 ? scaleX : aData->Item(2));
-
-  ProcessScaleHelper(aMatrix, scaleX.GetFloatValue(), scaleY.GetFloatValue(),
-                     1.0f);
+static void ProcessScale3D(Matrix4x4& aMatrix,
+                           const StyleTransformOperation& aOp) {
+  const auto& scale = aOp.AsScale3D();
+  ProcessScaleHelper(aMatrix, scale._0, scale._1, scale._2);
 }
 
 /* Helper function that, given a set of angles, constructs the appropriate
  * skew matrix.
  */
-static void ProcessSkewHelper(Matrix4x4& aMatrix, double aXAngle,
-                              double aYAngle) {
-  aMatrix.SkewXY(aXAngle, aYAngle);
-}
-
-/* Function that converts a skewx transform into a matrix. */
-static void ProcessSkewX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  NS_ASSERTION(aData->Count() == 2, "Bad array!");
-  ProcessSkewHelper(aMatrix, aData->Item(1).GetAngleValueInRadians(), 0.0);
-}
-
-/* Function that converts a skewy transform into a matrix. */
-static void ProcessSkewY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  NS_ASSERTION(aData->Count() == 2, "Bad array!");
-  ProcessSkewHelper(aMatrix, 0.0, aData->Item(1).GetAngleValueInRadians());
-}
-
-/* Function that converts a skew transform into a matrix. */
-static void ProcessSkew(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  NS_ASSERTION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
-
-  double xSkew = aData->Item(1).GetAngleValueInRadians();
-  double ySkew =
-      (aData->Count() == 2 ? 0.0 : aData->Item(2).GetAngleValueInRadians());
-
-  ProcessSkewHelper(aMatrix, xSkew, ySkew);
+static void ProcessSkewHelper(Matrix4x4& aMatrix, const StyleAngle& aXAngle,
+                              const StyleAngle& aYAngle) {
+  aMatrix.SkewXY(aXAngle.ToRadians(), aYAngle.ToRadians());
 }
 
-/* Function that converts a rotate transform into a matrix. */
-static void ProcessRotateZ(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
-  double theta = aData->Item(1).GetAngleValueInRadians();
-  aMatrix.RotateZ(theta);
-}
-
-static void ProcessRotateX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
-  double theta = aData->Item(1).GetAngleValueInRadians();
-  aMatrix.RotateX(theta);
-}
-
-static void ProcessRotateY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
-  double theta = aData->Item(1).GetAngleValueInRadians();
-  aMatrix.RotateY(theta);
-}
-
-static void ProcessRotate3D(Matrix4x4& aMatrix,
-                            const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 5, "Invalid array!");
-
-  double theta = aData->Item(4).GetAngleValueInRadians();
-  float x = aData->Item(1).GetFloatValue();
-  float y = aData->Item(2).GetFloatValue();
-  float z = aData->Item(3).GetFloatValue();
-
+static void ProcessRotate3D(Matrix4x4& aMatrix, float aX, float aY, float aZ,
+                            const StyleAngle& aAngle) {
   Matrix4x4 temp;
-  temp.SetRotateAxisAngle(x, y, z, theta);
-
+  temp.SetRotateAxisAngle(aX, aY, aZ, aAngle.ToRadians());
   aMatrix = temp * aMatrix;
 }
 
-static void ProcessPerspective(Matrix4x4& aMatrix,
-                               const nsCSSValue::Array* aData) {
-  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
-
-  float depth = ProcessTranslatePart(aData->Item(1), nullptr);
+static void ProcessPerspective(Matrix4x4& aMatrix, const Length& aLength) {
+  float depth = aLength.ToCSSPixels();
   ApplyPerspectiveToMatrix(aMatrix, depth);
 }
 
-/**
- * SetToTransformFunction is essentially a giant switch statement that fans
- * out to many smaller helper functions.
- */
 static void MatrixForTransformFunction(Matrix4x4& aMatrix,
-                                       const nsCSSValue::Array* aData,
+                                       const StyleTransformOperation& aOp,
                                        TransformReferenceBox& aRefBox) {
-  MOZ_ASSERT(aData, "Why did you want to get data from a null array?");
-
   /* Get the keyword for the transform. */
-  switch (TransformFunctionOf(aData)) {
-    case eCSSKeyword_translatex:
-      ProcessTranslateX(aMatrix, aData, aRefBox);
+  switch (aOp.tag) {
+    case StyleTransformOperation::Tag::TranslateX:
+      ProcessTranslateX(aMatrix, aOp.AsTranslateX(), aRefBox);
+      break;
+    case StyleTransformOperation::Tag::TranslateY:
+      ProcessTranslateY(aMatrix, aOp.AsTranslateY(), aRefBox);
       break;
-    case eCSSKeyword_translatey:
-      ProcessTranslateY(aMatrix, aData, aRefBox);
+    case StyleTransformOperation::Tag::TranslateZ:
+      ProcessTranslateZ(aMatrix, aOp.AsTranslateZ());
+      break;
+    case StyleTransformOperation::Tag::Translate:
+      ProcessTranslate(aMatrix, aOp.AsTranslate()._0, aOp.AsTranslate()._1,
+                       aRefBox);
       break;
-    case eCSSKeyword_translatez:
-      ProcessTranslateZ(aMatrix, aData);
+    case StyleTransformOperation::Tag::Translate3D:
+      return ProcessTranslate3D(aMatrix, aOp.AsTranslate3D()._0,
+                                aOp.AsTranslate3D()._1, aOp.AsTranslate3D()._2,
+                                aRefBox);
       break;
-    case eCSSKeyword_translate:
-      ProcessTranslate(aMatrix, aData, aRefBox);
-      break;
-    case eCSSKeyword_translate3d:
-      ProcessTranslate3D(aMatrix, aData, aRefBox);
+    case StyleTransformOperation::Tag::ScaleX:
+      ProcessScaleHelper(aMatrix, aOp.AsScaleX(), 1.0f, 1.0f);
       break;
-    case eCSSKeyword_scalex:
-      ProcessScaleX(aMatrix, aData);
+    case StyleTransformOperation::Tag::ScaleY:
+      ProcessScaleHelper(aMatrix, 1.0f, aOp.AsScaleY(), 1.0f);
       break;
-    case eCSSKeyword_scaley:
-      ProcessScaleY(aMatrix, aData);
+    case StyleTransformOperation::Tag::ScaleZ:
+      ProcessScaleHelper(aMatrix, 1.0f, 1.0f, aOp.AsScaleZ());
       break;
-    case eCSSKeyword_scalez:
-      ProcessScaleZ(aMatrix, aData);
+    case StyleTransformOperation::Tag::Scale:
+      ProcessScaleHelper(aMatrix, aOp.AsScale()._0, aOp.AsScale()._1, 1.0f);
       break;
-    case eCSSKeyword_scale:
-      ProcessScale(aMatrix, aData);
+    case StyleTransformOperation::Tag::Scale3D:
+      ProcessScale3D(aMatrix, aOp);
       break;
-    case eCSSKeyword_scale3d:
-      ProcessScale3D(aMatrix, aData);
+    case StyleTransformOperation::Tag::SkewX:
+      ProcessSkewHelper(aMatrix, aOp.AsSkewX(), StyleAngle::Zero());
       break;
-    case eCSSKeyword_skewx:
-      ProcessSkewX(aMatrix, aData);
+    case StyleTransformOperation::Tag::SkewY:
+      ProcessSkewHelper(aMatrix, StyleAngle::Zero(), aOp.AsSkewY());
       break;
-    case eCSSKeyword_skewy:
-      ProcessSkewY(aMatrix, aData);
+    case StyleTransformOperation::Tag::Skew:
+      ProcessSkewHelper(aMatrix, aOp.AsSkew()._0, aOp.AsSkew()._1);
       break;
-    case eCSSKeyword_skew:
-      ProcessSkew(aMatrix, aData);
+    case StyleTransformOperation::Tag::RotateX:
+      aMatrix.RotateX(aOp.AsRotateX().ToRadians());
       break;
-    case eCSSKeyword_rotatex:
-      ProcessRotateX(aMatrix, aData);
+    case StyleTransformOperation::Tag::RotateY:
+      aMatrix.RotateY(aOp.AsRotateY().ToRadians());
       break;
-    case eCSSKeyword_rotatey:
-      ProcessRotateY(aMatrix, aData);
+    case StyleTransformOperation::Tag::RotateZ:
+      aMatrix.RotateZ(aOp.AsRotateZ().ToRadians());
       break;
-    case eCSSKeyword_rotatez:
-      MOZ_FALLTHROUGH;
-    case eCSSKeyword_rotate:
-      ProcessRotateZ(aMatrix, aData);
+    case StyleTransformOperation::Tag::Rotate:
+      aMatrix.RotateZ(aOp.AsRotate().ToRadians());
       break;
-    case eCSSKeyword_rotate3d:
-      ProcessRotate3D(aMatrix, aData);
+    case StyleTransformOperation::Tag::Rotate3D:
+      ProcessRotate3D(aMatrix, aOp.AsRotate3D()._0, aOp.AsRotate3D()._1,
+                      aOp.AsRotate3D()._2, aOp.AsRotate3D()._3);
       break;
-    case eCSSKeyword_matrix:
-      ProcessMatrix(aMatrix, aData, aRefBox);
+    case StyleTransformOperation::Tag::Matrix:
+      ProcessMatrix(aMatrix, aOp);
       break;
-    case eCSSKeyword_matrix3d:
-      ProcessMatrix3D(aMatrix, aData, aRefBox);
+    case StyleTransformOperation::Tag::Matrix3D:
+      ProcessMatrix3D(aMatrix, aOp);
       break;
-    case eCSSKeyword_interpolatematrix:
-      ProcessMatrixOperator<Interpolate>(aMatrix, aData, aRefBox);
+    case StyleTransformOperation::Tag::InterpolateMatrix:
+      ProcessInterpolateMatrix(aMatrix, aOp, aRefBox);
       break;
-    case eCSSKeyword_accumulatematrix:
-      ProcessMatrixOperator<Accumulate>(aMatrix, aData, aRefBox);
+    case StyleTransformOperation::Tag::AccumulateMatrix:
+      ProcessAccumulateMatrix(aMatrix, aOp, aRefBox);
       break;
-    case eCSSKeyword_perspective:
-      ProcessPerspective(aMatrix, aData);
+    case StyleTransformOperation::Tag::Perspective:
+      ProcessPerspective(aMatrix, aOp.AsPerspective());
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown transform function!");
   }
 }
 
 /**
  * Return the transform function, as an nsCSSKeyword, for the given
@@ -773,71 +601,107 @@ void SetIdentityMatrix(nsCSSValue::Array
 
   MOZ_ASSERT(aMatrix->Count() == 17, "Invalid matrix3d");
   Matrix4x4 m;
   for (size_t i = 0; i < 16; ++i) {
     aMatrix->Item(i + 1).SetFloatValue(m.components[i], eCSSUnit_Number);
   }
 }
 
-static void ReadTransformsImpl(Matrix4x4& aMatrix, const nsCSSValueList* aList,
-                               TransformReferenceBox& aRefBox) {
-  for (const nsCSSValueList* curr = aList; curr != nullptr;
-       curr = curr->mNext) {
-    const nsCSSValue& currElem = curr->mValue;
-    if (currElem.GetUnit() != eCSSUnit_Function) {
-      NS_ASSERTION(currElem.GetUnit() == eCSSUnit_None && !aList->mNext,
-                   "stream should either be a list of functions or a "
-                   "lone None");
-      continue;
-    }
-    NS_ASSERTION(currElem.GetArrayValue()->Count() >= 1,
-                 "Incoming function is too short!");
-
-    /* Read in a single transform matrix. */
-    MatrixForTransformFunction(aMatrix, currElem.GetArrayValue(), aRefBox);
-  }
-}
-
-Matrix4x4 ReadTransforms(const nsCSSValueList* aList,
+Matrix4x4 ReadTransforms(const StyleTransform& aTransform,
                          TransformReferenceBox& aRefBox,
                          float aAppUnitsPerMatrixUnit) {
   Matrix4x4 result;
-  ReadTransformsImpl(result, aList, aRefBox);
+
+  for (const StyleTransformOperation& op : aTransform.Operations()) {
+    MatrixForTransformFunction(result, op, aRefBox);
+  }
 
   float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
   result.PreScale(1 / scale, 1 / scale, 1 / scale);
   result.PostScale(scale, scale, scale);
 
   return result;
 }
 
-Matrix4x4 ReadTransforms(const nsCSSValueList* aIndividualTransforms,
+static void ProcessTranslate(Matrix4x4& aMatrix,
+                             const StyleTranslate& aTranslate,
+                             TransformReferenceBox& aRefBox) {
+  switch (aTranslate.tag) {
+    case StyleTranslate::Tag::None:
+      return;
+    case StyleTranslate::Tag::Translate:
+      return ProcessTranslate(aMatrix, aTranslate.AsTranslate()._0,
+                              aTranslate.AsTranslate()._1, aRefBox);
+    case StyleTranslate::Tag::Translate3D:
+      return ProcessTranslate3D(aMatrix, aTranslate.AsTranslate3D()._0,
+                                aTranslate.AsTranslate3D()._1,
+                                aTranslate.AsTranslate3D()._2, aRefBox);
+    default:
+      MOZ_ASSERT_UNREACHABLE("Huh?");
+  }
+}
+
+static void ProcessRotate(Matrix4x4& aMatrix, const StyleRotate& aRotate,
+                          TransformReferenceBox& aRefBox) {
+  switch (aRotate.tag) {
+    case StyleRotate::Tag::None:
+      return;
+    case StyleRotate::Tag::Rotate:
+      aMatrix.RotateZ(aRotate.AsRotate().ToRadians());
+      return;
+    case StyleRotate::Tag::Rotate3D:
+      return ProcessRotate3D(aMatrix, aRotate.AsRotate3D()._0,
+                             aRotate.AsRotate3D()._1, aRotate.AsRotate3D()._2,
+                             aRotate.AsRotate3D()._3);
+    default:
+      MOZ_ASSERT_UNREACHABLE("Huh?");
+  }
+}
+
+static void ProcessScale(Matrix4x4& aMatrix, const StyleScale& aScale,
+                         TransformReferenceBox& aRefBox) {
+  switch (aScale.tag) {
+    case StyleScale::Tag::None:
+      return;
+    case StyleScale::Tag::Scale:
+      return ProcessScaleHelper(aMatrix, aScale.AsScale()._0,
+                                aScale.AsScale()._1, 1.0f);
+    case StyleScale::Tag::Scale3D:
+      return ProcessScaleHelper(aMatrix, aScale.AsScale3D()._0,
+                                aScale.AsScale3D()._1, aScale.AsScale3D()._2);
+    default:
+      MOZ_ASSERT_UNREACHABLE("Huh?");
+  }
+}
+
+Matrix4x4 ReadTransforms(const StyleTranslate& aTranslate,
+                         const StyleRotate& aRotate, const StyleScale& aScale,
                          const Maybe<MotionPathData>& aMotion,
-                         const nsCSSValueList* aTransform,
+                         const StyleTransform& aTransform,
                          TransformReferenceBox& aRefBox,
                          float aAppUnitsPerMatrixUnit) {
   Matrix4x4 result;
 
-  if (aIndividualTransforms) {
-    ReadTransformsImpl(result, aIndividualTransforms, aRefBox);
-  }
+  ProcessTranslate(result, aTranslate, aRefBox);
+  ProcessRotate(result, aRotate, aRefBox);
+  ProcessScale(result, aScale, aRefBox);
 
   if (aMotion.isSome()) {
     // Create the equivalent translate and rotate function, according to the
     // order in spec. We combine the translate and then the rotate.
     // https://drafts.fxtf.org/motion-1/#calculating-path-transform
     result.PreTranslate(aMotion->mTranslate.x, aMotion->mTranslate.y, 0.0);
     if (aMotion->mRotate != 0.0) {
       result.RotateZ(aMotion->mRotate);
     }
   }
 
-  if (aTransform) {
-    ReadTransformsImpl(result, aTransform, aRefBox);
+  for (const StyleTransformOperation& op : aTransform.Operations()) {
+    MatrixForTransformFunction(result, op, aRefBox);
   }
 
   float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
   result.PreScale(1 / scale, 1 / scale, 1 / scale);
   result.PostScale(scale, scale, scale);
 
   return result;
 }
@@ -1044,16 +908,17 @@ bool Decompose2DMatrix(const Matrix& aMa
 bool Decompose3DMatrix(const Matrix4x4& aMatrix, Point3D& aScale,
                        ShearArray& aShear, gfxQuaternion& aRotate,
                        Point3D& aTranslate, Point4D& aPerspective) {
   Matrix4x4 local = aMatrix;
 
   if (local[3][3] == 0) {
     return false;
   }
+
   /* Normalize the matrix */
   local.Normalize();
 
   /**
    * perspective is used to solve for perspective, but it also provides
    * an easy way to test for singularity of the upper 3x3 component.
    */
   Matrix4x4 perspective = local;
@@ -1129,46 +994,9 @@ bool Decompose3DMatrix(const Matrix4x4& 
   }
 
   /* Now, get the rotations out */
   aRotate = gfxQuaternion(local);
 
   return true;
 }
 
-Matrix CSSValueArrayTo2DMatrix(nsCSSValue::Array* aArray) {
-  MOZ_ASSERT(aArray && TransformFunctionOf(aArray) == eCSSKeyword_matrix &&
-             aArray->Count() == 7);
-  Matrix m(aArray->Item(1).GetFloatValue(), aArray->Item(2).GetFloatValue(),
-           aArray->Item(3).GetFloatValue(), aArray->Item(4).GetFloatValue(),
-           aArray->Item(5).GetFloatValue(), aArray->Item(6).GetFloatValue());
-  return m;
-}
-
-Matrix4x4 CSSValueArrayTo3DMatrix(nsCSSValue::Array* aArray) {
-  MOZ_ASSERT(aArray && TransformFunctionOf(aArray) == eCSSKeyword_matrix3d &&
-             aArray->Count() == 17);
-  gfx::Float array[16];
-  for (size_t i = 0; i < 16; ++i) {
-    array[i] = aArray->Item(i + 1).GetFloatValue();
-  }
-  Matrix4x4 m(array);
-  return m;
-}
-
-Size GetScaleValue(const nsCSSValueSharedList* aList,
-                   const nsIFrame* aForFrame) {
-  MOZ_ASSERT(aList && aList->mHead);
-  MOZ_ASSERT(aForFrame);
-
-  TransformReferenceBox refBox(aForFrame);
-  Matrix4x4 transform = ReadTransforms(
-      aList->mHead, refBox, aForFrame->PresContext()->AppUnitsPerDevPixel());
-  Matrix transform2d;
-  bool canDraw2D = transform.CanDraw2D(&transform2d);
-  if (!canDraw2D) {
-    return Size();
-  }
-
-  return transform2d.ScaleFactors(true);
-}
-
 }  // namespace nsStyleTransformMatrix
--- a/layout/style/nsStyleTransformMatrix.h
+++ b/layout/style/nsStyleTransformMatrix.h
@@ -148,47 +148,46 @@ class MOZ_STACK_CLASS TransformReference
  * Return the transform function, as an nsCSSKeyword, for the given
  * nsCSSValue::Array from a transform list.
  */
 nsCSSKeyword TransformFunctionOf(const nsCSSValue::Array* aData);
 
 void SetIdentityMatrix(nsCSSValue::Array* aMatrix);
 
 float ProcessTranslatePart(
-    const nsCSSValue& aValue, TransformReferenceBox* aRefBox,
+    const mozilla::LengthPercentage& aValue, TransformReferenceBox* aRefBox,
     TransformReferenceBox::DimensionGetter aDimensionGetter = nullptr);
 
 void ProcessInterpolateMatrix(mozilla::gfx::Matrix4x4& aMatrix,
-                              const nsCSSValue::Array* aData,
+                              const mozilla::StyleTransformOperation& aOp,
                               TransformReferenceBox& aBounds);
 
 void ProcessAccumulateMatrix(mozilla::gfx::Matrix4x4& aMatrix,
-                             const nsCSSValue::Array* aData,
+                             const mozilla::StyleTransformOperation& aOp,
                              TransformReferenceBox& aBounds);
 
 /**
- * Given an nsCSSValueList containing -moz-transform functions,
- * returns a matrix containing the value of those functions.
+ * Given a StyleTransform containing transform functions, returns a matrix
+ * containing the value of those functions.
  *
- * @param aData The nsCSSValueList containing the transform functions
+ * @param aList the transform operation list.
  * @param aBounds The frame's bounding rectangle.
  * @param aAppUnitsPerMatrixUnit The number of app units per device pixel.
- *
- * eCSSUnit_Pixel (as they are in an StyleAnimationValue)
  */
-mozilla::gfx::Matrix4x4 ReadTransforms(const nsCSSValueList* aList,
+mozilla::gfx::Matrix4x4 ReadTransforms(const mozilla::StyleTransform& aList,
                                        TransformReferenceBox& aBounds,
                                        float aAppUnitsPerMatrixUnit);
 
 // Generate the gfx::Matrix for CSS Transform Module Level 2.
 // https://drafts.csswg.org/css-transforms-2/#ctm
 mozilla::gfx::Matrix4x4 ReadTransforms(
-    const nsCSSValueList* aIndividualTransforms,
+    const mozilla::StyleTranslate&, const mozilla::StyleRotate&,
+    const mozilla::StyleScale&,
     const mozilla::Maybe<mozilla::MotionPathData>& aMotion,
-    const nsCSSValueList* aTransform, TransformReferenceBox& aRefBox,
+    const mozilla::StyleTransform&, TransformReferenceBox& aRefBox,
     float aAppUnitsPerMatrixUnit);
 
 /**
  * Given the x and y values, compute the 2d position with respect to the given
  * TransformReferenceBox that these values describe, in CSS pixels.
  */
 mozilla::CSSPoint Convert2DPosition(const mozilla::LengthPercentage& aX,
                                     const mozilla::LengthPercentage& aY,
@@ -218,16 +217,11 @@ bool Decompose2DMatrix(const mozilla::gf
  * Implements the 3d transform matrix decomposition algorithm.
  */
 bool Decompose3DMatrix(const mozilla::gfx::Matrix4x4& aMatrix,
                        mozilla::gfx::Point3D& aScale, ShearArray& aShear,
                        gfxQuaternion& aRotate,
                        mozilla::gfx::Point3D& aTranslate,
                        mozilla::gfx::Point4D& aPerspective);
 
-mozilla::gfx::Matrix CSSValueArrayTo2DMatrix(nsCSSValue::Array* aArray);
-mozilla::gfx::Matrix4x4 CSSValueArrayTo3DMatrix(nsCSSValue::Array* aArray);
-
-mozilla::gfx::Size GetScaleValue(const nsCSSValueSharedList* aList,
-                                 const nsIFrame* aForFrame);
 }  // namespace nsStyleTransformMatrix
 
 #endif
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1719,22 +1719,19 @@ gfxMatrix nsSVGUtils::GetTransformMatrix
       properties.mToTransformOrigin.z};
 
   Matrix svgTransform;
   Matrix4x4 trans;
   (void)aFrame->IsSVGTransformed(&svgTransform);
 
   if (properties.HasTransform()) {
     trans = nsStyleTransformMatrix::ReadTransforms(
-        properties.mIndividualTransformList
-            ? properties.mIndividualTransformList->mHead
-            : nullptr,
-        properties.mMotion,
-        properties.mTransformList ? properties.mTransformList->mHead : nullptr,
-        refBox, AppUnitsPerCSSPixel());
+        properties.mTranslate, properties.mRotate, properties.mScale,
+        properties.mMotion, properties.mTransform, refBox,
+        AppUnitsPerCSSPixel());
   } else {
     trans = Matrix4x4::From2D(svgTransform);
   }
 
   trans.ChangeBasis(svgTransformOrigin);
 
   Matrix mm;
   trans.ProjectTo2D();
--- a/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -271,39 +271,16 @@ impl nsCSSValue {
         while let Some(item) = unsafe { item_ptr.as_mut() } {
             let value = values.next().expect("Values shouldn't have been exhausted");
             item.mXValue = value.0;
             item.mYValue = value.1;
             item_ptr = item.mNext;
         }
         debug_assert!(values.next().is_none(), "Values should have been exhausted");
     }
-
-    /// Set a shared list
-    pub fn set_shared_list<I>(&mut self, values: I)
-    where
-        I: ExactSizeIterator<Item = nsCSSValue>,
-    {
-        debug_assert!(values.len() > 0, "Empty list is not supported");
-        unsafe { bindings::Gecko_CSSValue_InitSharedList(self, values.len() as u32) };
-        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_SharedList);
-        let list = unsafe {
-            self.mValue
-                .mSharedList
-                .as_ref()
-                .as_mut()
-                .expect("List pointer should be non-null")
-                .mHead
-                .as_mut()
-        };
-        debug_assert!(list.is_some(), "New created shared list shouldn't be null");
-        for (item, new_value) in list.unwrap().into_iter().zip(values) {
-            *item = new_value;
-        }
-    }
 }
 
 impl Drop for nsCSSValue {
     fn drop(&mut self) {
         unsafe { bindings::Gecko_CSSValue_Drop(self) };
     }
 }
 
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -285,21 +285,16 @@ macro_rules! impl_threadsafe_refcount {
 }
 
 impl_threadsafe_refcount!(
     structs::mozilla::URLExtraData,
     bindings::Gecko_AddRefURLExtraDataArbitraryThread,
     bindings::Gecko_ReleaseURLExtraDataArbitraryThread
 );
 impl_threadsafe_refcount!(
-    structs::nsCSSValueSharedList,
-    bindings::Gecko_AddRefCSSValueSharedListArbitraryThread,
-    bindings::Gecko_ReleaseCSSValueSharedListArbitraryThread
-);
-impl_threadsafe_refcount!(
     structs::mozilla::css::URLValue,
     bindings::Gecko_AddRefCSSURLValueArbitraryThread,
     bindings::Gecko_ReleaseCSSURLValueArbitraryThread
 );
 impl_threadsafe_refcount!(
     structs::mozilla::css::GridTemplateAreasValue,
     bindings::Gecko_AddRefGridTemplateAreasValueArbitraryThread,
     bindings::Gecko_ReleaseGridTemplateAreasValueArbitraryThread
--- a/servo/components/style/properties/cascade.rs
+++ b/servo/components/style/properties/cascade.rs
@@ -623,20 +623,16 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
         self.context.builder.visited_style = Some(style);
     }
 
     fn finished_applying_properties(&mut self) {
         let builder = &mut self.context.builder;
 
         #[cfg(feature = "gecko")]
         {
-            if let Some(display) = builder.get_box_if_mutated() {
-                display.generate_combined_transform();
-            }
-
             if let Some(bg) = builder.get_background_if_mutated() {
                 bg.fill_arrays();
             }
 
             if let Some(svg) = builder.get_svg_if_mutated() {
                 svg.fill_arrays();
             }
         }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -806,291 +806,16 @@ def set_gecko_property(ffi_name, expr):
         }
 
         UrlOrNone::Url(unsafe {
             ComputedUrl::from_url_value(self.gecko.${gecko_ffi_name}.to_safe())
         })
     }
 </%def>
 
-<%
-transform_functions = [
-    ("Matrix3D", "matrix3d", ["number"] * 16),
-    ("Matrix", "matrix", ["number"] * 6),
-    ("Translate", "translate", ["lp", "lp"]),
-    ("Translate3D", "translate3d", ["lp", "lp", "length"]),
-    ("TranslateX", "translatex", ["lp"]),
-    ("TranslateY", "translatey", ["lp"]),
-    ("TranslateZ", "translatez", ["length"]),
-    ("Scale3D", "scale3d", ["number"] * 3),
-    ("Scale", "scale", ["number", "number"]),
-    ("ScaleX", "scalex", ["number"]),
-    ("ScaleY", "scaley", ["number"]),
-    ("ScaleZ", "scalez", ["number"]),
-    ("Rotate", "rotate", ["angle"]),
-    ("Rotate3D", "rotate3d", ["number"] * 3 + ["angle"]),
-    ("RotateX", "rotatex", ["angle"]),
-    ("RotateY", "rotatey", ["angle"]),
-    ("RotateZ", "rotatez", ["angle"]),
-    ("Skew", "skew", ["angle", "angle"]),
-    ("SkewX", "skewx", ["angle"]),
-    ("SkewY", "skewy", ["angle"]),
-    ("Perspective", "perspective", ["length"]),
-    ("InterpolateMatrix", "interpolatematrix", ["list"] * 2 + ["percentage"]),
-    ("AccumulateMatrix", "accumulatematrix", ["list"] * 2 + ["integer_to_percentage"])
-]
-%>
-
-<%def name="transform_function_arm(name, keyword, items)">
-    <%
-        pattern = None
-        if keyword == "matrix3d":
-            # m11: number1, m12: number2, ..
-            single_patterns = ["m%s: %s" % (str(a / 4 + 1) + str(a % 4 + 1), b + str(a + 1)) for (a, b)
-                                in enumerate(items)]
-            pattern = "(Matrix3D { %s })" % ", ".join(single_patterns)
-        elif keyword == "matrix":
-            # a: number1, b: number2, ..
-            single_patterns = ["%s: %s" % (chr(ord('a') + a), b + str(a + 1)) for (a, b)
-                                in enumerate(items)]
-            pattern = "(Matrix { %s })" % ", ".join(single_patterns)
-        elif keyword == "interpolatematrix":
-            pattern = " { from_list: ref list1, to_list: ref list2, progress: percentage3 }"
-        elif keyword == "accumulatematrix":
-            pattern = " { from_list: ref list1, to_list: ref list2, count: integer_to_percentage3 }"
-        else:
-            # Generate contents of pattern from items
-            pattern = "(%s)" % ", ".join([b + str(a+1) for (a,b) in enumerate(items)])
-
-        # First %s substituted with the call to GetArrayItem, the second
-        # %s substituted with the corresponding variable
-        css_value_setters = {
-            "length" : "bindings::Gecko_CSSValue_SetPixelLength(%s, %s.px())",
-            "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s.0)",
-            # Note: This is an integer type, but we use it as a percentage value in Gecko, so
-            #       need to cast it to f32.
-            "integer_to_percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s as f32)",
-            "lp" : "%s.set_length_percentage(%s)",
-            "angle" : "%s.set_angle(%s)",
-            "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
-            # Note: We use nsCSSValueSharedList here, instead of nsCSSValueList_heap
-            #       because this function is not called on the main thread and
-            #       nsCSSValueList_heap is not thread safe.
-            "list" : "%s.set_shared_list(%s.0.iter().map(&convert_to_ns_css_value));",
-        }
-    %>
-    crate::values::generics::transform::TransformOperation::${name}${pattern} => {
-        let len = ${len(items) + 1};
-        bindings::Gecko_CSSValue_SetFunction(gecko_value, len);
-        bindings::Gecko_CSSValue_SetKeyword(
-            bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
-            structs::nsCSSKeyword::eCSSKeyword_${keyword}
-        );
-        % for index, item in enumerate(items):
-            % if item == "list":
-                debug_assert!(!${item}${index + 1}.0.is_empty());
-            % endif
-            ${css_value_setters[item] % (
-                "(&mut *bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d))" % (index + 1),
-                item + str(index + 1)
-            )};
-        % endfor
-    }
-</%def>
-
-<%def name="computed_operation_arm(name, keyword, items)">
-    <%
-        # %s is substituted with the call to GetArrayItem.
-        css_value_getters = {
-            "length" : "Length::new(bindings::Gecko_CSSValue_GetNumber(%s))",
-            "lp" : "%s.get_length_percentage()",
-            "angle" : "%s.get_angle()",
-            "number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
-            "percentage" : "Percentage(bindings::Gecko_CSSValue_GetPercentage(%s))",
-            "integer_to_percentage" : "bindings::Gecko_CSSValue_GetPercentage(%s) as i32",
-            "list" : "Transform(convert_shared_list_to_operations(%s))",
-        }
-        pre_symbols = "("
-        post_symbols = ")"
-        if keyword == "interpolatematrix" or keyword == "accumulatematrix":
-            # We generate this like: "TransformOperation::InterpolateMatrix {", so the space is
-            # between "InterpolateMatrix"/"AccumulateMatrix" and '{'
-            pre_symbols = " {"
-            post_symbols = "}"
-        elif keyword == "matrix3d":
-            pre_symbols = "(Matrix3D {"
-            post_symbols = "})"
-        elif keyword == "matrix":
-            pre_symbols = "(Matrix {"
-            post_symbols = "})"
-        field_names = None
-        if keyword == "interpolatematrix":
-            field_names = ["from_list", "to_list", "progress"]
-        elif keyword == "accumulatematrix":
-            field_names = ["from_list", "to_list", "count"]
-
-    %>
-    structs::nsCSSKeyword::eCSSKeyword_${keyword} => {
-        crate::values::generics::transform::TransformOperation::${name}${pre_symbols}
-        % for index, item in enumerate(items):
-            % if keyword == "matrix3d":
-                m${index / 4 + 1}${index % 4 + 1}:
-            % elif keyword == "matrix":
-                ${chr(ord('a') + index)}:
-            % elif keyword == "interpolatematrix" or keyword == "accumulatematrix":
-                ${field_names[index]}:
-            % endif
-            <%
-                getter = css_value_getters[item] % (
-                    "(&*bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d))" % (index + 1)
-                )
-            %>
-            ${getter},
-        % endfor
-        ${post_symbols}
-    },
-</%def>
-
-#[allow(unused_parens)]
-fn set_single_transform_function(
-    servo_value: &values::computed::TransformOperation,
-    gecko_value: &mut structs::nsCSSValue /* output */
-) {
-    use crate::values::computed::TransformOperation;
-    use crate::values::generics::transform::{Matrix, Matrix3D};
-
-    let convert_to_ns_css_value = |item: &TransformOperation| -> structs::nsCSSValue {
-        let mut value = structs::nsCSSValue::null();
-        set_single_transform_function(item, &mut value);
-        value
-    };
-
-    unsafe {
-        match *servo_value {
-            % for servo, gecko, format in transform_functions:
-                ${transform_function_arm(servo, gecko, format)}
-            % endfor
-        }
-    }
-}
-
-pub fn convert_transform(
-    input: &[values::computed::TransformOperation],
-    output: &mut structs::root::RefPtr<structs::root::nsCSSValueSharedList>
-) {
-    use crate::gecko_bindings::sugar::refptr::RefPtr;
-
-    unsafe { output.clear() };
-
-    let list = unsafe {
-        RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(input.len() as u32))
-    };
-    let value_list = unsafe { list.mHead.as_mut() };
-    if let Some(value_list) = value_list {
-        for (gecko, servo) in value_list.into_iter().zip(input.into_iter()) {
-            set_single_transform_function(servo, gecko);
-        }
-    }
-    output.set_move(list);
-}
-
-#[allow(unused_parens)]
-fn clone_single_transform_function(
-    gecko_value: &structs::nsCSSValue
-) -> values::computed::TransformOperation {
-    use crate::values::computed::{Length, Percentage, TransformOperation};
-    use crate::values::generics::transform::{Matrix, Matrix3D};
-    use crate::values::generics::transform::Transform;
-
-    let convert_shared_list_to_operations = |value: &structs::nsCSSValue|
-                                            -> Vec<TransformOperation> {
-        debug_assert_eq!(value.mUnit, structs::nsCSSUnit::eCSSUnit_SharedList);
-        let value_list = unsafe {
-            value.mValue.mSharedList.as_ref()
-                    .as_mut().expect("List pointer should be non-null").mHead.as_ref()
-        };
-        debug_assert!(value_list.is_some(), "An empty shared list is not allowed");
-        value_list.unwrap().into_iter()
-                            .map(|item| clone_single_transform_function(item))
-                            .collect()
-    };
-
-    let transform_function = unsafe {
-        bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0))
-    };
-
-    unsafe {
-        match transform_function {
-            % for servo, gecko, format in transform_functions:
-                ${computed_operation_arm(servo, gecko, format)}
-            % endfor
-            _ => panic!("unacceptable transform function"),
-        }
-    }
-}
-
-pub fn clone_transform_from_list(
-    list: Option< &structs::root::nsCSSValueList>
-) -> values::computed::Transform {
-    use crate::values::generics::transform::Transform;
-
-    let result = match list {
-        Some(list) => {
-            list.into_iter()
-                .filter_map(|value| {
-                    // Handle none transform.
-                    if value.is_none() {
-                        None
-                    } else {
-                        Some(clone_single_transform_function(value))
-                    }
-                })
-                .collect::<Vec<_>>()
-        },
-        _ => vec![],
-    };
-    Transform(result)
-}
-
-<%def name="impl_transform(ident, gecko_ffi_name)">
-    #[allow(non_snake_case)]
-    pub fn set_${ident}(&mut self, other: values::computed::Transform) {
-        use crate::gecko_properties::convert_transform;
-        if other.0.is_empty() {
-            unsafe {
-                self.gecko.${gecko_ffi_name}.clear();
-            }
-            return;
-        };
-        convert_transform(&other.0, &mut self.gecko.${gecko_ffi_name});
-    }
-
-    #[allow(non_snake_case)]
-    pub fn copy_${ident}_from(&mut self, other: &Self) {
-        unsafe { self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name}); }
-    }
-
-    #[allow(non_snake_case)]
-    pub fn reset_${ident}(&mut self, other: &Self) {
-        self.copy_${ident}_from(other)
-    }
-
-    #[allow(non_snake_case)]
-    pub fn clone_${ident}(&self) -> values::computed::Transform {
-        use crate::gecko_properties::clone_transform_from_list;
-        use crate::values::generics::transform::Transform;
-
-        if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() {
-            return Transform(vec!());
-        }
-        let list = unsafe { (*self.gecko.${gecko_ffi_name}.to_safe().get()).mHead.as_ref() };
-        clone_transform_from_list(list)
-    }
-</%def>
-
 <%def name="impl_logical(name, **kwargs)">
     ${helpers.logical_setter(name)}
 </%def>
 
 <%def name="impl_style_struct(style_struct)">
 impl ${style_struct.gecko_struct_name} {
     #[allow(dead_code, unused_variables)]
     pub fn default(document: &structs::Document) -> Arc<Self> {
@@ -1186,17 +911,16 @@ impl Clone for ${style_struct.gecko_stru
 
     # Types used with predefined_type()-defined properties that we can auto-generate.
     predefined_types = {
         "MozScriptMinSize": impl_absolute_length,
         "SVGLength": impl_svg_length,
         "SVGOpacity": impl_svg_opacity,
         "SVGPaint": impl_svg_paint,
         "SVGWidth": impl_svg_length,
-        "Transform": impl_transform,
         "url::UrlOrNone": impl_css_url,
     }
 
     def longhand_method(longhand):
         args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
 
         # get the method and pass additional keyword or type-specific arguments
         if longhand.logical:
@@ -2506,28 +2230,22 @@ fn static_assert() {
 </%def>
 
 <% skip_box_longhands= """display
                           animation-name animation-delay animation-duration
                           animation-direction animation-fill-mode animation-play-state
                           animation-iteration-count animation-timing-function
                           clear transition-duration transition-delay
                           transition-timing-function transition-property
-                          transform-style
-                          rotate scroll-snap-points-x scroll-snap-points-y
-                          scroll-snap-coordinate -moz-binding
-                          offset-path shape-outside
-                          translate scale -webkit-line-clamp""" %>
+                          transform-style scroll-snap-points-x
+                          scroll-snap-points-y scroll-snap-coordinate
+                          -moz-binding offset-path shape-outside
+                          -webkit-line-clamp""" %>
 <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
     #[inline]
-    pub fn generate_combined_transform(&mut self) {
-        unsafe { bindings::Gecko_StyleDisplay_GenerateCombinedTransform(&mut *self.gecko) };
-    }
-
-    #[inline]
     pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
         self.gecko.mDisplay = v;
         self.gecko.mOriginalDisplay = v;
     }
 
     #[inline]
     pub fn copy_display_from(&mut self, other: &Self) {
         self.gecko.mDisplay = other.gecko.mDisplay;
@@ -2820,20 +2538,16 @@ fn static_assert() {
         }
     }
 
     ${impl_animation_count('iteration_count', 'IterationCount')}
     ${impl_copy_animation_value('iteration_count', 'IterationCount')}
 
     ${impl_animation_timing_function()}
 
-    ${impl_individual_transform('rotate', 'Rotate', 'mSpecifiedRotate')}
-    ${impl_individual_transform('translate', 'Translate', 'mSpecifiedTranslate')}
-    ${impl_individual_transform('scale', 'Scale', 'mSpecifiedScale')}
-
     <% impl_shape_source("shape_outside", "mShapeOutside") %>
 
     pub fn set_offset_path(&mut self, v: longhands::offset_path::computed_value::T) {
         use crate::gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_SetStyleMotion};
         use crate::gecko_bindings::structs::StyleShapeSourceType;
         use crate::values::generics::basic_shape::FillRule;
         use crate::values::specified::OffsetPath;
 
--- a/servo/components/style/properties/longhands/box.mako.rs
+++ b/servo/components/style/properties/longhands/box.mako.rs
@@ -347,17 +347,16 @@
 <% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %>
 
 ${helpers.predefined_type(
     "transform",
     "Transform",
     "generics::transform::Transform::none()",
     extra_prefixes=transform_extra_prefixes,
     animation_value_type="ComputedValue",
-    gecko_ffi_name="mSpecifiedTransform",
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB \
            GETCS_NEEDS_LAYOUT_FLUSH CAN_ANIMATE_ON_COMPOSITOR",
     spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
     servo_restyle_damage="reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "rotate",
--- a/servo/components/style/properties/longhands/ui.mako.rs
+++ b/servo/components/style/properties/longhands/ui.mako.rs
@@ -76,17 +76,16 @@
     enabled_in="chrome",
 )}
 
 ${helpers.predefined_type(
     "-moz-window-transform",
     "Transform",
     "generics::transform::Transform::none()",
     products="gecko",
-    gecko_ffi_name="mSpecifiedWindowTransform",
     flags="GETCS_NEEDS_LAYOUT_FLUSH",
     animation_value_type="ComputedValue",
     spec="None (Nonstandard internal property)",
     enabled_in="chrome",
 )}
 
 ${helpers.predefined_type(
     "-moz-window-transform-origin",
--- a/servo/components/style/values/animated/transform.rs
+++ b/servo/components/style/values/animated/transform.rs
@@ -856,17 +856,17 @@ impl Animate for ComputedTransform {
     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         use std::borrow::Cow;
 
         // Addition for transforms simply means appending to the list of
         // transform functions. This is different to how we handle the other
         // animation procedures so we treat it separately here rather than
         // handling it in TransformOperation.
         if procedure == Procedure::Add {
-            let result = self.0.iter().chain(&other.0).cloned().collect::<Vec<_>>();
+            let result = self.0.iter().chain(&*other.0).cloned().collect();
             return Ok(Transform(result));
         }
 
         let this = Cow::Borrowed(&self.0);
         let other = Cow::Borrowed(&other.0);
 
         // Interpolate the common prefix
         let mut result = this
@@ -893,25 +893,25 @@ impl Animate for ComputedTransform {
             // => Add the remainders to a suitable ___Matrix function.
             (Some(this_remainder), Some(other_remainder)) => match procedure {
                 Procedure::Add => {
                     debug_assert!(false, "Should have already dealt with add by the point");
                     return Err(());
                 },
                 Procedure::Interpolate { progress } => {
                     result.push(TransformOperation::InterpolateMatrix {
-                        from_list: Transform(this_remainder.to_vec()),
-                        to_list: Transform(other_remainder.to_vec()),
+                        from_list: Transform(this_remainder.to_vec().into()),
+                        to_list: Transform(other_remainder.to_vec().into()),
                         progress: Percentage(progress as f32),
                     });
                 },
                 Procedure::Accumulate { count } => {
                     result.push(TransformOperation::AccumulateMatrix {
-                        from_list: Transform(this_remainder.to_vec()),
-                        to_list: Transform(other_remainder.to_vec()),
+                        from_list: Transform(this_remainder.to_vec().into()),
+                        to_list: Transform(other_remainder.to_vec().into()),
                         count: cmp::min(count, i32::max_value() as u64) as i32,
                     });
                 },
             },
             // If there is a remainder from just one list, then one list must be shorter but
             // completely match the type of the corresponding functions in the longer list.
             // => Interpolate the remainder with identity transforms.
             (Some(remainder), None) | (None, Some(remainder)) => {
@@ -922,18 +922,18 @@ impl Animate for ComputedTransform {
                         .map(|transform| {
                             let identity = transform.to_animated_zero().unwrap();
 
                             match transform {
                                 // We can't interpolate/accumulate ___Matrix types directly with a
                                 // matrix. Instead we need to wrap it in another ___Matrix type.
                                 TransformOperation::AccumulateMatrix { .. } |
                                 TransformOperation::InterpolateMatrix { .. } => {
-                                    let transform_list = Transform(vec![transform.clone()]);
-                                    let identity_list = Transform(vec![identity]);
+                                    let transform_list = Transform(vec![transform.clone()].into());
+                                    let identity_list = Transform(vec![identity].into());
                                     let (from_list, to_list) = if fill_right {
                                         (transform_list, identity_list)
                                     } else {
                                         (identity_list, transform_list)
                                     };
 
                                     match procedure {
                                         Procedure::Add => Err(()),
@@ -965,17 +965,17 @@ impl Animate for ComputedTransform {
                             }
                         })
                         .collect::<Result<Vec<_>, _>>()?,
                 );
             },
             (None, None) => {},
         }
 
-        Ok(Transform(result))
+        Ok(Transform(result.into()))
     }
 }
 
 impl ComputeSquaredDistance for ComputedTransform {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         let squared_dist = self.0.squared_distance_with_zero(&other.0);
 
--- a/servo/components/style/values/computed/angle.rs
+++ b/servo/components/style/values/computed/angle.rs
@@ -21,16 +21,17 @@ use style_traits::{CssWriter, ToCss};
     Copy,
     Debug,
     MallocSizeOf,
     PartialEq,
     PartialOrd,
     ToAnimatedZero,
     ToResolvedValue,
 )]
+#[repr(C)]
 pub struct Angle(CSSFloat);
 
 impl ToCss for Angle {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         self.degrees().to_css(dest)?;
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -11,19 +11,19 @@ use crate::values::computed::{Angle, Int
 use crate::values::generics::transform as generic;
 use crate::Zero;
 use euclid::{Transform3D, Vector3D};
 
 pub use crate::values::generics::transform::TransformStyle;
 
 /// A single operation in a computed CSS `transform`
 pub type TransformOperation =
-    generic::TransformOperation<Angle, Number, Length, Integer, LengthPercentage>;
+    generic::GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>;
 /// A computed CSS `transform`
-pub type Transform = generic::Transform<TransformOperation>;
+pub type Transform = generic::GenericTransform<TransformOperation>;
 
 /// The computed value of a CSS `<transform-origin>`
 pub type TransformOrigin =
     generic::GenericTransformOrigin<LengthPercentage, LengthPercentage, Length>;
 
 /// A vector to represent the direction vector (rotate axis) for Rotate3D.
 pub type DirectionVector = Vector3D<CSSFloat>;
 
@@ -535,23 +535,23 @@ impl ToAnimatedZero for TransformOperati
 
 impl ToAnimatedZero for Transform {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Ok(generic::Transform(
             self.0
                 .iter()
                 .map(|op| op.to_animated_zero())
-                .collect::<Result<Vec<_>, _>>()?,
+                .collect::<Result<crate::OwnedSlice<_>, _>>()?,
         ))
     }
 }
 
 /// A computed CSS `rotate`
-pub type Rotate = generic::Rotate<Number, Angle>;
+pub type Rotate = generic::GenericRotate<Number, Angle>;
 
 impl Rotate {
     /// Convert TransformOperation to Rotate.
     pub fn to_transform_operation(&self) -> Option<TransformOperation> {
         match *self {
             generic::Rotate::None => None,
             generic::Rotate::Rotate(angle) => Some(generic::TransformOperation::Rotate(angle)),
             generic::Rotate::Rotate3D(rx, ry, rz, angle) => {
@@ -568,17 +568,17 @@ impl Rotate {
                 generic::Rotate::Rotate3D(rx, ry, rz, angle)
             },
             _ => unreachable!("Found unexpected value for rotate property"),
         }
     }
 }
 
 /// A computed CSS `translate`
-pub type Translate = generic::Translate<LengthPercentage, Length>;
+pub type Translate = generic::GenericTranslate<LengthPercentage, Length>;
 
 impl Translate {
     /// Convert TransformOperation to Translate.
     pub fn to_transform_operation(&self) -> Option<TransformOperation> {
         match *self {
             generic::Translate::None => None,
             generic::Translate::Translate(tx, ty) => {
                 Some(generic::TransformOperation::Translate(tx, ty))
@@ -597,17 +597,17 @@ impl Translate {
                 generic::Translate::Translate3D(tx, ty, tz)
             },
             _ => unreachable!("Found unexpected value for translate"),
         }
     }
 }
 
 /// A computed CSS `scale`
-pub type Scale = generic::Scale<Number>;
+pub type Scale = generic::GenericScale<Number>;
 
 impl Scale {
     /// Convert TransformOperation to Scale.
     pub fn to_transform_operation(&self) -> Option<TransformOperation> {
         match *self {
             generic::Scale::None => None,
             generic::Scale::Scale(sx, sy) => Some(generic::TransformOperation::Scale(sx, sy)),
             generic::Scale::Scale3D(sx, sy, sz) => {
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -25,48 +25,54 @@ use style_traits::{CssWriter, ToCss};
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
-#[css(comma, function)]
-pub struct Matrix<T> {
+#[css(comma, function = "matrix")]
+#[repr(C)]
+pub struct GenericMatrix<T> {
     pub a: T,
     pub b: T,
     pub c: T,
     pub d: T,
     pub e: T,
     pub f: T,
 }
 
+pub use self::GenericMatrix as Matrix;
+
 #[allow(missing_docs)]
 #[cfg_attr(rustfmt, rustfmt_skip)]
 #[css(comma, function = "matrix3d")]
 #[derive(
     Clone,
     Copy,
     Debug,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
-pub struct Matrix3D<T> {
+#[repr(C)]
+pub struct GenericMatrix3D<T> {
     pub m11: T, pub m12: T, pub m13: T, pub m14: T,
     pub m21: T, pub m22: T, pub m23: T, pub m24: T,
     pub m31: T, pub m32: T, pub m33: T, pub m34: T,
     pub m41: T, pub m42: T, pub m43: T, pub m44: T,
 }
 
+pub use self::GenericMatrix3D as Matrix3D;
+
 #[cfg_attr(rustfmt, rustfmt_skip)]
 impl<T: Into<f64>> From<Matrix<T>> for Transform3D<f64> {
     #[inline]
     fn from(m: Matrix<T>) -> Self {
         Transform3D::row_major(
             m.a.into(), m.b.into(), 0.0, 0.0,
             m.c.into(), m.d.into(), 0.0, 0.0,
             0.0,        0.0,        1.0, 0.0,
@@ -137,27 +143,29 @@ fn is_same<N: PartialEq>(x: &N, y: &N) -
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
+#[repr(C, u8)]
 /// A single operation in the list of a `transform` value
-pub enum TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
+/// cbindgen:derive-tagged-enum-copy-constructor=true
+pub enum GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>
 where
     Angle: Zero,
     LengthPercentage: Zero,
     Number: PartialEq,
 {
     /// Represents a 2D 2x3 matrix.
-    Matrix(Matrix<Number>),
+    Matrix(GenericMatrix<Number>),
     /// Represents a 3D 4x4 matrix.
-    Matrix3D(Matrix3D<Number>),
+    Matrix3D(GenericMatrix3D<Number>),
     /// A 2D skew.
     ///
     /// If the second angle is not provided it is assumed zero.
     ///
     /// Syntax can be skew(angle) or skew(angle, angle)
     #[css(comma, function)]
     Skew(Angle, #[css(skip_if = "Zero::is_zero")] Angle),
     /// skewX(angle)
@@ -227,43 +235,48 @@ where
     ///
     /// The value must be greater than or equal to zero.
     #[css(function)]
     Perspective(Length),
     /// A intermediate type for interpolation of mismatched transform lists.
     #[allow(missing_docs)]
     #[css(comma, function = "interpolatematrix")]
     InterpolateMatrix {
-        from_list: Transform<TransformOperation<Angle, Number, Length, Integer, LengthPercentage>>,
-        to_list: Transform<TransformOperation<Angle, Number, Length, Integer, LengthPercentage>>,
+        from_list: GenericTransform<GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>>,
+        to_list: GenericTransform<GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>>,
         progress: computed::Percentage,
     },
     /// A intermediate type for accumulation of mismatched transform lists.
     #[allow(missing_docs)]
     #[css(comma, function = "accumulatematrix")]
     AccumulateMatrix {
-        from_list: Transform<TransformOperation<Angle, Number, Length, Integer, LengthPercentage>>,
-        to_list: Transform<TransformOperation<Angle, Number, Length, Integer, LengthPercentage>>,
+        from_list: GenericTransform<GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>>,
+        to_list: GenericTransform<GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>>,
         count: Integer,
     },
 }
 
+pub use self::GenericTransformOperation as TransformOperation;
+
 #[derive(
     Clone,
     Debug,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
+#[repr(C)]
 /// A value of the `transform` property
-pub struct Transform<T>(#[css(if_empty = "none", iterable)] pub Vec<T>);
+pub struct GenericTransform<T>(#[css(if_empty = "none", iterable)] pub crate::OwnedSlice<T>);
+
+pub use self::GenericTransform as Transform;
 
 impl<Angle, Number, Length, Integer, LengthPercentage>
     TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
 where
     Angle: Zero,
     LengthPercentage: Zero,
     Number: PartialEq,
 {
@@ -492,17 +505,17 @@ where
         };
         Ok(matrix)
     }
 }
 
 impl<T> Transform<T> {
     /// `none`
     pub fn none() -> Self {
-        Transform(vec![])
+        Transform(Default::default())
     }
 }
 
 impl<T: ToMatrix> Transform<T> {
     /// Return the equivalent 3d matrix of this transform list.
     /// We return a pair: the first one is the transform matrix, and the second one
     /// indicates if there is any 3d transform function in this transform list.
     #[cfg_attr(rustfmt, rustfmt_skip)]
@@ -524,17 +537,17 @@ impl<T: ToMatrix> Transform<T> {
         // We intentionally use Transform3D<f64> during computation to avoid error propagation
         // because using f32 to compute triangle functions (e.g. in create_rotation()) is not
         // accurate enough. In Gecko, we also use "double" to compute the triangle functions.
         // Therefore, let's use Transform3D<f64> during matrix computation and cast it into f32
         // in the end.
         let mut transform = Transform3D::<f64>::identity();
         let mut contain_3d = false;
 
-        for operation in &self.0 {
+        for operation in &*self.0 {
             let matrix = operation.to_3d_matrix(reference_box)?;
             contain_3d |= operation.is_3d();
             transform = transform.pre_mul(&matrix);
         }
 
         Ok((cast_3d_transform(transform), contain_3d))
     }
 }
@@ -584,28 +597,31 @@ pub fn get_normalized_vector_and_angle<T
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedZero,
     ToComputedValue,
     ToResolvedValue,
     ToShmem,
 )]
+#[repr(C, u8)]
 /// A value of the `Rotate` property
 ///
 /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
-pub enum Rotate<Number, Angle> {
+pub enum GenericRotate<Number, Angle> {
     /// 'none'
     None,
     /// '<angle>'
     Rotate(Angle),
     /// '<number>{3} <angle>'
     Rotate3D(Number, Number, Number, Angle),
 }
 
+pub use self::GenericRotate as Rotate;
+
 /// A trait to check if the current 3D vector is parallel to the DirectionVector.
 /// This is especially for serialization on Rotate.
 pub trait IsParallelTo {
     /// Returns true if this is parallel to the vector.
     fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
 }
 
 impl<Number, Angle> ToCss for Rotate<Number, Angle>
@@ -655,28 +671,31 @@ where
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedZero,
     ToComputedValue,
     ToResolvedValue,
     ToShmem,
 )]
+#[repr(C, u8)]
 /// A value of the `Scale` property
 ///
 /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
-pub enum Scale<Number> {
+pub enum GenericScale<Number> {
     /// 'none'
     None,
     /// '<number>{1,2}'
     Scale(Number, Number),
     /// '<number>{3}'
     Scale3D(Number, Number, Number),
 }
 
+pub use self::GenericScale as Scale;
+
 impl<Number: ToCss + PartialEq> ToCss for Scale<Number> {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: fmt::Write,
     {
         match *self {
             Scale::None => dest.write_str("none"),
             Scale::Scale(ref x, ref y) => {
@@ -705,45 +724,48 @@ impl<Number: ToCss + PartialEq> ToCss fo
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedZero,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
+#[repr(C, u8)]
 /// A value of the `translate` property
 ///
 /// https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization:
 ///
 /// If a 2d translation is specified, the property must serialize with only one
 /// or two values (per usual, if the second value is 0px, the default, it must
 /// be omitted when serializing).
 ///
 /// If a 3d translation is specified, all three values must be serialized.
 ///
 /// We don't omit the 3rd component even if it is 0px for now, and the
 /// related spec issue is https://github.com/w3c/csswg-drafts/issues/3305
 ///
 /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
-pub enum Translate<LengthPercentage, Length>
+pub enum GenericTranslate<LengthPercentage, Length>
 where
     LengthPercentage: Zero,
 {
     /// 'none'
     None,
     /// '<length-percentage>' or '<length-percentage> <length-percentage>'
     Translate(
         LengthPercentage,
         #[css(skip_if = "Zero::is_zero")] LengthPercentage,
     ),
     /// '<length-percentage> <length-percentage> <length>'
     Translate3D(LengthPercentage, LengthPercentage, Length),
 }
 
+pub use self::GenericTranslate as Translate;
+
 #[allow(missing_docs)]
 #[derive(
     Clone,
     Copy,
     Debug,
     MallocSizeOf,
     Parse,
     PartialEq,
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -37,17 +37,17 @@ impl Transform {
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         use style_traits::{Separator, Space};
 
         if input
             .try(|input| input.expect_ident_matching("none"))
             .is_ok()
         {
-            return Ok(generic::Transform(Vec::new()));
+            return Ok(generic::Transform::none());
         }
 
         Ok(generic::Transform(Space::parse(input, |input| {
             let function = input.expect_function()?.clone();
             input.parse_nested_block(|input| {
                 let location = input.current_source_location();
                 let result = match_ignore_ascii_case! { &function,
                     "matrix" => {
@@ -213,17 +213,17 @@ impl Transform {
                     },
                     _ => Err(()),
                 };
                 result.map_err(|()| {
                     location
                         .new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))
                 })
             })
-        })?))
+        })?.into()))
     }
 }
 
 impl Parse for Transform {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -126,16 +126,20 @@ include = [
   "ShapeRadius",
   "ArcSlice",
   "ForgottenArcSlicePtr",
   "HeaderWithLength",
   "MozContextProperties",
   "Quotes",
   "BoxShadow",
   "SimpleShadow",
+  "Transform",
+  "Rotate",
+  "Scale",
+  "Translate",
 ]
 item_types = ["enums", "structs", "typedefs", "functions"]
 renaming_overrides_prefixing = true
 
 # Prevent some renaming for Gecko types that cbindgen doesn't otherwise understand.
 [export.rename]
 "nscolor" = "nscolor"
 "nsAtom" = "nsAtom"
@@ -182,16 +186,17 @@ renaming_overrides_prefixing = true
 "OriginFlags" = "OriginFlags"
 "ServoTraversalFlags" = "ServoTraversalFlags"
 "ServoStyleSetSizes" = "ServoStyleSetSizes"
 
 [export.body]
 "CSSPixelLength" = """
   inline nscoord ToAppUnits() const;
   inline bool IsZero() const;
+  float ToCSSPixels() const { return _0; }
 """
 
 "LengthPercentage" = """
   // Defined in nsStyleCoord.h
   static constexpr inline StyleLengthPercentage Zero();
   static inline StyleLengthPercentage FromAppUnits(nscoord);
   static inline StyleLengthPercentage FromPixels(CSSCoord);
   static inline StyleLengthPercentage FromPercentage(float);
@@ -399,8 +404,29 @@ renaming_overrides_prefixing = true
 
   inline StyleAtom(const StyleAtom& aOther);
   inline ~StyleAtom();
 """
 
 "OwnedStr" = """
   inline nsDependentCSubstring AsString() const;
 """
+
+"GenericTransform" = """
+  inline Span<const T> Operations() const;
+  inline bool IsNone() const;
+"""
+
+"GenericTransformOperation" = """
+ private:
+  // Private default constructor without initialization so that the helper
+  // constructor functions still work as expected. They take care of
+  // initializing the fields properly.
+  StyleGenericTransformOperation() {}
+ public:
+"""
+
+
+"Angle" = """
+  inline static StyleAngle Zero();
+  inline float ToDegrees() const;
+  inline double ToRadians() const;
+"""
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -42,17 +42,16 @@ use style::gecko_bindings::bindings::nsA
 use style::gecko_bindings::bindings::nsAString;
 use style::gecko_bindings::bindings::Gecko_AddPropertyToSet;
 use style::gecko_bindings::bindings::Gecko_AppendPropertyValuePair;
 use style::gecko_bindings::bindings::Gecko_ConstructFontFeatureValueSet;
 use style::gecko_bindings::bindings::Gecko_GetOrCreateFinalKeyframe;
 use style::gecko_bindings::bindings::Gecko_GetOrCreateInitialKeyframe;
 use style::gecko_bindings::bindings::Gecko_GetOrCreateKeyframeAtStart;
 use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
-use style::gecko_bindings::bindings::Gecko_NewNoneTransform;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{Element as RawGeckoElement, nsINode as RawGeckoNode};
 use style::gecko_bindings::structs::{
     RawServoStyleSet, RawServoAuthorStyles,
     RawServoCssUrlData, RawServoDeclarationBlock, RawServoMediaList,
     RawServoCounterStyleRule, RawServoAnimationValue, RawServoSupportsRule,
     RawServoKeyframesRule, ServoCssRules, RawServoStyleSheetContents,
     RawServoPageRule, RawServoNamespaceRule, RawServoMozDocumentRule,
@@ -60,17 +59,16 @@ use style::gecko_bindings::structs::{
     RawServoFontFaceRule, RawServoFontFeatureValuesRule,
     RawServoSharedMemoryBuilder
 };
 use style::gecko_bindings::structs::gfxFontFeatureValueSet;
 use style::gecko_bindings::structs::nsAtom;
 use style::gecko_bindings::structs::nsCSSCounterDesc;
 use style::gecko_bindings::structs::nsCSSFontDesc;
 use style::gecko_bindings::structs::nsCSSPropertyID;
-use style::gecko_bindings::structs::nsCSSValueSharedList;
 use style::gecko_bindings::structs::nsChangeHint;
 use style::gecko_bindings::structs::nsCompatibility;
 use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
 use style::gecko_bindings::structs::nsTArray;
 use style::gecko_bindings::structs::nsTimingFunction;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::structs::AtomArray;
 use style::gecko_bindings::structs::PseudoStyleType;
@@ -94,17 +92,16 @@ use style::gecko_bindings::structs::Shee
 use style::gecko_bindings::structs::SheetLoadDataHolder;
 use style::gecko_bindings::structs::SheetParsingMode;
 use style::gecko_bindings::structs::StyleRuleInclusion;
 use style::gecko_bindings::structs::StyleSheet as DomStyleSheet;
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, HasBoxFFI, Strong, Owned, OwnedOrNull};
 use style::gecko_bindings::sugar::refptr::RefPtr;
-use style::gecko_properties;
 use style::global_style_data::{GlobalStyleData, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
 use style::invalidation::element::restyle_hints::RestyleHint;
 use style::media_queries::MediaList;
 use style::parser::{self, Parse, ParserContext};
 use style::properties::animated_properties::AnimationValue;
 use style::properties::{parse_one_declaration_into, parse_style_attribute};
 use style::properties::{ComputedValues, Importance, NonCustomPropertyId};
 use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId};
@@ -786,97 +783,85 @@ pub extern "C" fn Servo_AnimationValue_C
         LonghandId::BackgroundColor => {
             Arc::new(AnimationValue::BackgroundColor(animatedRGBA.into())).into_strong()
         },
         _ => panic!("Should be background-color property"),
     }
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn Servo_AnimationValue_GetTransform(
+pub unsafe extern "C" fn Servo_AnimationValue_GetScale(
+    value: &RawServoAnimationValue,
+) -> *const computed::Scale {
+    let value = AnimationValue::as_arc(&value);
+    match **value {
+        AnimationValue::Scale(ref value) => value,
+        _ => unreachable!("Expected scale"),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetTranslate(
     value: &RawServoAnimationValue,
-    list: *mut structs::RefPtr<nsCSSValueSharedList>,
-) -> nsCSSPropertyID {
-    let list = &mut *list;
+) -> *const computed::Translate {
+    let value = AnimationValue::as_arc(&value);
+    match **value {
+        AnimationValue::Translate(ref value) => value,
+        _ => unreachable!("Expected translate"),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetRotate(
+    value: &RawServoAnimationValue,
+) -> *const computed::Rotate {
     let value = AnimationValue::as_arc(&value);
     match **value {
-        AnimationValue::Transform(ref servo_list) => {
-            if servo_list.0.is_empty() {
-                list.set_move(RefPtr::from_addrefed(Gecko_NewNoneTransform()));
-            } else {
-                gecko_properties::convert_transform(&servo_list.0, list);
-            }
-            nsCSSPropertyID::eCSSProperty_transform
-        },
-        AnimationValue::Translate(ref v) => {
-            if let Some(v) = v.to_transform_operation() {
-                gecko_properties::convert_transform(&[v], list);
-            } else {
-                list.set_move(RefPtr::from_addrefed(Gecko_NewNoneTransform()));
-            }
-            nsCSSPropertyID::eCSSProperty_translate
-        },
-        AnimationValue::Rotate(ref v) => {
-            if let Some(v) = v.to_transform_operation() {
-                gecko_properties::convert_transform(&[v], list);
-            } else {
-                list.set_move(RefPtr::from_addrefed(Gecko_NewNoneTransform()));
-            }
-            nsCSSPropertyID::eCSSProperty_rotate
-        },
-        AnimationValue::Scale(ref v) => {
-            if let Some(v) = v.to_transform_operation() {
-                gecko_properties::convert_transform(&[v], list);
-            } else {
-                list.set_move(RefPtr::from_addrefed(Gecko_NewNoneTransform()));
-            }
-            nsCSSPropertyID::eCSSProperty_scale
-        },
-        _ => unreachable!("Unsupported transform-like animation value"),
+        AnimationValue::Rotate(ref value) => value,
+        _ => unreachable!("Expected rotate"),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetTransform(
+    value: &RawServoAnimationValue,
+) -> *const computed::Transform {
+    let value = AnimationValue::as_arc(&value);
+    match **value {
+        AnimationValue::Transform(ref value) => value,
+        _ => unreachable!("Unsupported transform animation value"),
     }
 }
 
 #[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_Rotate(r: &computed::Rotate) -> Strong<RawServoAnimationValue> {
+    Arc::new(AnimationValue::Rotate(r.clone())).into_strong()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_Translate(t: &computed::Translate) -> Strong<RawServoAnimationValue> {
+    Arc::new(AnimationValue::Translate(t.clone())).into_strong()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_Scale(s: &computed::Scale) -> Strong<RawServoAnimationValue> {
+    Arc::new(AnimationValue::Scale(s.clone())).into_strong()
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn Servo_AnimationValue_Transform(
-    property: nsCSSPropertyID,
-    list: *const nsCSSValueSharedList,
+    list: *const computed::TransformOperation,
+    len: usize,
 ) -> Strong<RawServoAnimationValue> {
-    use style::values::computed::transform::{Rotate, Scale, Translate};
-
-    let list = (&*list).mHead.as_ref();
-
-    let property = LonghandId::from_nscsspropertyid(property)
-        .expect("We don't have shorthand property animation value");
-    let transform = gecko_properties::clone_transform_from_list(list);
-    match property {
-        LonghandId::Rotate => {
-            let rotate = if transform.0.is_empty() {
-                style::values::generics::transform::Rotate::None
-            } else {
-                debug_assert_eq!(transform.0.len(), 1);
-                Rotate::from_transform_operation(&(transform.0)[0])
-            };
-            Arc::new(AnimationValue::Rotate(rotate)).into_strong()
-        },
-        LonghandId::Scale => {
-            debug_assert_eq!(transform.0.len(), 1);
-            Arc::new(AnimationValue::Scale(Scale::from_transform_operation(&(transform.0)[0])))
-                .into_strong()
-        },
-        LonghandId::Translate => {
-            debug_assert_eq!(transform.0.len(), 1);
-            Arc::new(AnimationValue::Translate(
-                Translate::from_transform_operation(&(transform.0)[0])
-            )).into_strong()
-        },
-        LonghandId::Transform => {
-            Arc::new(AnimationValue::Transform(transform)).into_strong()
-        },
-        _ => unreachable!("Unsupported transform-like animation value"),
-    }
+    use style::values::generics::transform::Transform;
+
+    let slice = std::slice::from_raw_parts(list, len);
+    Arc::new(AnimationValue::Transform(
+        Transform(slice.iter().cloned().collect())
+    )).into_strong()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_AnimationValue_DeepEqual(
     this: &RawServoAnimationValue,
     other: &RawServoAnimationValue,
 ) -> bool {
     let this_value = AnimationValue::as_arc(&this);
@@ -5204,16 +5189,22 @@ pub extern "C" fn Servo_GetAnimationValu
     );
     for (index, anim) in iter.enumerate() {
         unsafe { animation_values.set_len((index + 1) as u32) };
         animation_values[index].set_arc_leaky(Arc::new(anim));
     }
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_AnimationValue_GetPropertyId(value: &RawServoAnimationValue) -> nsCSSPropertyID {
+    let value = AnimationValue::as_arc(&value);
+    value.id().to_nscsspropertyid()
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_AnimationValue_Compute(
     element: &RawGeckoElement,
     declarations: &RawServoDeclarationBlock,
     style: &ComputedValues,
     raw_data: &RawServoStyleSet,
 ) -> Strong<RawServoAnimationValue> {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
     let metrics = get_metrics_provider_for_product();