Backed out 17 changesets (bug 1550554, bug 1549593, bug 1551991, bug 1529002) for failing multiple Android tests and Windows 2012 bustages CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Thu, 16 May 2019 13:17:10 +0300
changeset 474097 283b94c196a15367f287fd7f2bb6c9b4bcad0b45
parent 474096 fb8cc6cad30a0f94d5858cc35b20ce77acc605d2
child 474098 862aa43181c3bcb046b6159141c29a4c960680a6
push id36022
push userncsoregi@mozilla.com
push dateThu, 16 May 2019 21:55:16 +0000
treeherdermozilla-central@96802be91766 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1550554, 1549593, 1551991, 1529002
milestone68.0a1
backs out3bb3fafa62e2eea4b96404a1954f822fdaecbfd5
e12a979de502bf14147c43b34d437449bbd74552
d81e4aa6bf0c5701bf5369b4f764caa09cf803ea
c354e61f2a347779dbc2f31ca384046f8a85953a
37fd602bebc2f47062f1b8b22b3b71574aa4f072
6c1f00cc30ca74cf5db1463da06b0c2772e9737a
8a7a0329bdc3fe43cdcb40fb9f9749b34c1c9130
86159475ddd3a0ef6e54492be2dc563e71310d2c
35f91a9ea82af8ba15ea08996149bb871d6957f8
6798155e71dc6872757f2fad83a20d2242aded0f
b90c2cf5b8c558a93bb35df79ece66aa6df8446b
882ab9868c957394c82bffbf3f3fa7d61db2b0a2
b28a48e2ed212dc5dea20ddab26e37f66b00a2c0
2c31fe18eefd0a53bac3dec0c752fcc156d001e9
57f2362aa538b3374290652fe8605d05104c140f
45f171b26e95c6c7d12302743129ec5ccf5ac7e4
2e4b263c9410ca3e9e2077552c0afa30f688e749
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
Backed out 17 changesets (bug 1550554, bug 1549593, bug 1551991, bug 1529002) for failing multiple Android tests and Windows 2012 bustages CLOSED TREE Backed out changeset 3bb3fafa62e2 (bug 1551991) Backed out changeset e12a979de502 (bug 1551991) Backed out changeset d81e4aa6bf0c (bug 1551991) Backed out changeset c354e61f2a34 (bug 1551991) Backed out changeset 37fd602bebc2 (bug 1551991) Backed out changeset 6c1f00cc30ca (bug 1551991) Backed out changeset 8a7a0329bdc3 (bug 1551991) Backed out changeset 86159475ddd3 (bug 1551991) Backed out changeset 35f91a9ea82a (bug 1529002) Backed out changeset 6798155e71dc (bug 1529002) Backed out changeset b90c2cf5b8c5 (bug 1550554) Backed out changeset 882ab9868c95 (bug 1550554) Backed out changeset b28a48e2ed21 (bug 1550554) Backed out changeset 2c31fe18eefd (bug 1550554) Backed out changeset 57f2362aa538 (bug 1550554) Backed out changeset 45f171b26e95 (bug 1550554) Backed out changeset 2e4b263c9410 (bug 1549593)
Cargo.lock
build/moz.configure/bindgen.configure
gfx/layers/AnimationHelper.cpp
gfx/layers/composite/AsyncCompositionManager.cpp
layout/base/nsLayoutDebugger.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsQuoteList.cpp
layout/forms/nsButtonFrameRenderer.cpp
layout/forms/nsFieldSetFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
layout/painting/ActiveLayerTracker.cpp
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRendering.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/style/CounterStyleManager.cpp
layout/style/CounterStyleManager.h
layout/style/GeckoBindings.cpp
layout/style/GeckoBindings.h
layout/style/ServoBindings.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/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/nsStyleStructInlines.h
layout/style/nsStyleTransformMatrix.cpp
layout/style/nsStyleTransformMatrix.h
layout/svg/SVGImageContext.cpp
layout/svg/nsCSSFilterInstance.cpp
layout/svg/nsSVGUtils.cpp
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableFrame.cpp
layout/xul/nsTextBoxFrame.cpp
servo/components/style/counter_style/mod.rs
servo/components/style/gecko/arc_types.rs
servo/components/style/gecko/mod.rs
servo/components/style/gecko/rules.rs
servo/components/style/gecko/values.rs
servo/components/style/gecko_bindings/sugar/mod.rs
servo/components/style/gecko_bindings/sugar/ns_css_shadow_array.rs
servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
servo/components/style/gecko_bindings/sugar/ns_css_value.rs
servo/components/style/gecko_bindings/sugar/refptr.rs
servo/components/style/gecko_string_cache/mod.rs
servo/components/style/lib.rs
servo/components/style/properties/cascade.rs
servo/components/style/properties/data.py
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhands/box.mako.rs
servo/components/style/properties/longhands/effects.mako.rs
servo/components/style/properties/longhands/inherited_svg.mako.rs
servo/components/style/properties/longhands/inherited_text.mako.rs
servo/components/style/properties/longhands/ui.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/properties/shorthands/background.mako.rs
servo/components/style/properties/shorthands/box.mako.rs
servo/components/style/properties/shorthands/svg.mako.rs
servo/components/style/values/animated/effects.rs
servo/components/style/values/animated/transform.rs
servo/components/style/values/computed/angle.rs
servo/components/style/values/computed/list.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/transform.rs
servo/components/style/values/generics/effects.rs
servo/components/style/values/generics/transform.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/box.rs
servo/components/style/values/specified/list.rs
servo/components/style/values/specified/svg.rs
servo/components/style/values/specified/transform.rs
servo/components/style_traits/Cargo.toml
servo/components/style_traits/arc_slice.rs
servo/components/style_traits/lib.rs
servo/components/style_traits/owned_slice.rs
servo/components/style_traits/owned_str.rs
servo/components/style_traits/specified_value_info.rs
servo/components/style_traits/values.rs
servo/ports/geckolib/cbindgen.toml
servo/ports/geckolib/glue.rs
taskcluster/scripts/misc/build-cbindgen.sh
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2813,17 +2813,16 @@ dependencies = [
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.21.0",
  "servo_arc 0.1.1",
  "to_shmem 0.0.1",
  "to_shmem_derive 0.0.1",
 ]
 
--- a/build/moz.configure/bindgen.configure
+++ b/build/moz.configure/bindgen.configure
@@ -10,17 +10,17 @@ cbindgen_is_needed = depends(build_proje
 option(env='CBINDGEN', nargs=1, when=cbindgen_is_needed,
        help='Path to cbindgen')
 
 
 @imports(_from='textwrap', _import='dedent')
 def check_cbindgen_version(cbindgen, fatal=False):
     log.debug("trying cbindgen: %s" % cbindgen)
 
-    cbindgen_min_version = Version('0.8.7')
+    cbindgen_min_version = Version('0.8.6')
 
     # cbindgen x.y.z
     version = Version(check_cmd_output(cbindgen, '--version').strip().split(" ")[1])
     log.debug("%s has version %s" % (cbindgen, version))
     if version >= cbindgen_min_version:
         return True
     if not fatal:
         return False
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -590,59 +590,53 @@ bool AnimationHelper::SampleAnimations(C
   }
 
   return isAnimating;
 }
 
 gfx::Matrix4x4 AnimationHelper::ServoAnimationValueToMatrix4x4(
     const nsTArray<RefPtr<RawServoAnimationValue>>& aValues,
     const TransformData& aTransformData) {
-  // 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;
-
+  // FIXME: Bug 1457033: We should convert servo's animation value to matrix
+  // directly without nsCSSValueSharedList.
   // TODO: Bug 1429305: Support compositor animations for motion-path.
+  RefPtr<nsCSSValueSharedList> transform, translate, rotate, scale;
   for (const auto& value : aValues) {
     MOZ_ASSERT(value);
-    nsCSSPropertyID id = Servo_AnimationValue_GetPropertyId(value);
+    RefPtr<nsCSSValueSharedList> list;
+    nsCSSPropertyID id = Servo_AnimationValue_GetTransform(value, &list);
     switch (id) {
       case eCSSProperty_transform:
         MOZ_ASSERT(!transform);
-        transform = Servo_AnimationValue_GetTransform(value);
+        transform = list.forget();
         break;
       case eCSSProperty_translate:
         MOZ_ASSERT(!translate);
-        translate = Servo_AnimationValue_GetTranslate(value);
+        translate = list.forget();
         break;
       case eCSSProperty_rotate:
         MOZ_ASSERT(!rotate);
-        rotate = Servo_AnimationValue_GetRotate(value);
+        rotate = list.forget();
         break;
       case eCSSProperty_scale:
         MOZ_ASSERT(!scale);
-        scale = Servo_AnimationValue_GetScale(value);
+        scale = list.forget();
         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(
-      translate ? *translate : noneTranslate, rotate ? *rotate : noneRotate,
-      scale ? *scale : noneScale, transform ? *transform : noneTransform,
-      transformOrigin);
+      std::move(individualList), std::move(transform), 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,16 +41,18 @@
 #  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/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -164,23 +164,23 @@ static void PrintDisplayItemTo(nsDisplay
 
   for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
     const nsRect& r = iter.Get();
     aStream << nsPrintfCString(" (opaque %d,%d,%d,%d)", r.x, r.y, r.width,
                                r.height);
   }
 
   const auto& willChange = aItem->Frame()->StyleDisplay()->mWillChange;
-  if (!willChange.features.IsEmpty()) {
+  if (!willChange.IsEmpty()) {
     aStream << " (will-change=";
-    for (size_t i = 0; i < willChange.features.Length(); i++) {
+    for (size_t i = 0; i < willChange.Length(); i++) {
       if (i > 0) {
         aStream << ",";
       }
-      nsDependentAtomString buffer(willChange.features.AsSpan()[i].AsAtom());
+      nsDependentAtomString buffer(willChange[i]);
       aStream << NS_LossyConvertUTF16toASCII(buffer).get();
     }
     aStream << ")";
   }
 
   if (aItem->HasHitTestInfo()) {
     auto* hitTestInfoItem = static_cast<nsDisplayHitTestInfoItem*>(aItem);
 
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4360,33 +4360,29 @@ nsRect nsLayoutUtils::GetAllInFlowRectsU
   GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags);
   return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect
                                            : accumulator.mResultRect;
 }
 
 nsRect nsLayoutUtils::GetTextShadowRectsUnion(
     const nsRect& aTextAndDecorationsRect, nsIFrame* aFrame, uint32_t aFlags) {
   const nsStyleText* textStyle = aFrame->StyleText();
-  auto shadows = textStyle->mTextShadow.AsSpan();
-  if (shadows.IsEmpty()) {
-    return aTextAndDecorationsRect;
-  }
+  if (!textStyle->HasTextShadow()) return aTextAndDecorationsRect;
 
   nsRect resultRect = aTextAndDecorationsRect;
   int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
-  for (auto& shadow : shadows) {
-    nsMargin blur =
-        nsContextBoxBlur::GetBlurRadiusMargin(shadow.blur.ToAppUnits(), A2D);
+  for (uint32_t i = 0; i < textStyle->mTextShadow->Length(); ++i) {
+    nsCSSShadowItem* shadow = textStyle->mTextShadow->ShadowAt(i);
+    nsMargin blur = nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D);
     if ((aFlags & EXCLUDE_BLUR_SHADOWS) && blur != nsMargin(0, 0, 0, 0))
       continue;
 
     nsRect tmpRect(aTextAndDecorationsRect);
 
-    tmpRect.MoveBy(
-        nsPoint(shadow.horizontal.ToAppUnits(), shadow.vertical.ToAppUnits()));
+    tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
     tmpRect.Inflate(blur);
 
     resultRect.UnionRect(resultRect, tmpRect);
   }
   return resultRect;
 }
 
 enum ObjectDimensionType { eWidth, eHeight };
@@ -6098,46 +6094,44 @@ void nsLayoutUtils::DrawUniDirString(con
 }
 
 /* static */
 void nsLayoutUtils::PaintTextShadow(
     const nsIFrame* aFrame, gfxContext* aContext, const nsRect& aTextRect,
     const nsRect& aDirtyRect, const nscolor& aForegroundColor,
     TextShadowCallback aCallback, void* aCallbackData) {
   const nsStyleText* textStyle = aFrame->StyleText();
-  auto shadows = textStyle->mTextShadow.AsSpan();
-  if (shadows.IsEmpty()) {
-    return;
-  }
+  if (!textStyle->HasTextShadow()) return;
 
   // Text shadow happens with the last value being painted at the back,
   // ie. it is painted first.
   gfxContext* aDestCtx = aContext;
-  for (auto& shadow : Reversed(shadows)) {
-    nsPoint shadowOffset(shadow.horizontal.ToAppUnits(),
-                         shadow.vertical.ToAppUnits());
-    nscoord blurRadius = std::max(shadow.blur.ToAppUnits(), 0);
+  for (uint32_t i = textStyle->mTextShadow->Length(); i > 0; --i) {
+    nsCSSShadowItem* shadowDetails = textStyle->mTextShadow->ShadowAt(i - 1);
+    nsPoint shadowOffset(shadowDetails->mXOffset, shadowDetails->mYOffset);
+    nscoord blurRadius = std::max(shadowDetails->mRadius, 0);
 
     nsRect shadowRect(aTextRect);
     shadowRect.MoveBy(shadowOffset);
 
     nsPresContext* presCtx = aFrame->PresContext();
     nsContextBoxBlur contextBoxBlur;
 
-    nscolor shadowColor = shadow.color.CalcColor(aForegroundColor);
+    nscolor shadowColor = shadowDetails->mColor.CalcColor(aForegroundColor);
 
     // Webrender just needs the shadow details
     if (auto* textDrawer = aContext->GetTextDrawer()) {
       wr::Shadow wrShadow;
 
       wrShadow.offset = {
-          presCtx->AppUnitsToFloatDevPixels(shadow.horizontal.ToAppUnits()),
-          presCtx->AppUnitsToFloatDevPixels(shadow.vertical.ToAppUnits())};
-
-      wrShadow.blur_radius = presCtx->AppUnitsToFloatDevPixels(blurRadius);
+          presCtx->AppUnitsToFloatDevPixels(shadowDetails->mXOffset),
+          presCtx->AppUnitsToFloatDevPixels(shadowDetails->mYOffset)};
+
+      wrShadow.blur_radius =
+          presCtx->AppUnitsToFloatDevPixels(shadowDetails->mRadius);
       wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
 
       // Gecko already inflates the bounding rect of text shadows,
       // so tell WR not to inflate again.
       bool inflate = false;
       textDrawer->AppendShadow(wrShadow, inflate);
       continue;
     }
@@ -8292,18 +8286,18 @@ bool nsLayoutUtils::FontSizeInflationEna
     return false;
   }
   return presShell->FontSizeInflationEnabled();
 }
 
 /* static */
 nsRect nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame,
                                                const nsSize& aFrameSize) {
-  auto boxShadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
-  if (boxShadows.IsEmpty()) {
+  nsCSSShadowArray* boxShadows = aFrame->StyleEffects()->mBoxShadow;
+  if (!boxShadows) {
     return nsRect();
   }
 
   nsRect inputRect(nsPoint(0, 0), aFrameSize);
 
   // According to the CSS spec, box-shadow should be based on the border box.
   // However, that looks broken when the background extends outside the border
   // box, as can be the case with native theming.  To fix that we expand the
@@ -8318,29 +8312,27 @@ nsRect nsLayoutUtils::GetBoxShadowRectFo
       presContext->GetTheme()->GetWidgetOverflow(
           presContext->DeviceContext(), aFrame, styleDisplay->mAppearance,
           &inputRect);
     }
   }
 
   nsRect shadows;
   int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
-  for (auto& shadow : boxShadows) {
+  for (uint32_t i = 0; i < boxShadows->Length(); ++i) {
     nsRect tmpRect = inputRect;
+    nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
 
     // inset shadows are never painted outside the frame
-    if (shadow.inset) {
-      continue;
-    }
-
-    tmpRect.MoveBy(nsPoint(shadow.base.horizontal.ToAppUnits(),
-                           shadow.base.vertical.ToAppUnits()));
-    tmpRect.Inflate(shadow.spread.ToAppUnits());
-    tmpRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
-        shadow.base.blur.ToAppUnits(), A2D));
+    if (shadow->mInset) continue;
+
+    tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
+    tmpRect.Inflate(shadow->mSpread);
+    tmpRect.Inflate(
+        nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D));
     shadows.UnionRect(shadows, tmpRect);
   }
   return shadows;
 }
 
 /* static */
 bool nsLayoutUtils::GetContentViewerSize(nsPresContext* aPresContext,
                                          LayoutDeviceIntSize& aOutSize) {
--- a/layout/base/nsQuoteList.cpp
+++ b/layout/base/nsQuoteList.cpp
@@ -32,39 +32,20 @@ bool nsQuoteNode::InitTextFrame(nsGenCon
   }
   return dirty;
 }
 
 nsString nsQuoteNode::Text() {
   NS_ASSERTION(mType == StyleContentType::OpenQuote ||
                    mType == StyleContentType::CloseQuote,
                "should only be called when mText should be non-null");
+
   nsString result;
-  int32_t depth = Depth();
-  MOZ_ASSERT(depth >= -1);
-
-  Span<const StyleQuotePair> quotes =
-    mPseudoFrame->StyleList()->mQuotes._0.AsSpan();
-
-  // Reuse the last pair when the depth is greater than the number of
-  // pairs of quotes.  (Also make 'quotes: none' and close-quote from
-  // a depth of 0 equivalent for the next test.)
-  if (depth >= static_cast<int32_t>(quotes.Length())) {
-      depth = static_cast<int32_t>(quotes.Length()) - 1;
-  }
-
-  if (depth == -1) {
-    // close-quote from a depth of 0 or 'quotes: none'
-    return result;
-  }
-
-  const StyleQuotePair& pair = quotes[depth];
-  const StyleOwnedStr& quote =
-    mType == StyleContentType::OpenQuote ? pair.opening : pair.closing;
-  result.Assign(NS_ConvertUTF8toUTF16(quote.AsString()));
+  Servo_Quotes_GetQuote(mPseudoFrame->StyleList()->mQuotes.get(), Depth(),
+                        mType, &result);
   return result;
 }
 
 void nsQuoteList::Calc(nsQuoteNode* aNode) {
   if (aNode == FirstNode()) {
     aNode->mDepthBefore = 0;
   } else {
     aNode->mDepthBefore = Prev(aNode)->DepthAfter();
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -98,18 +98,18 @@ void nsDisplayButtonBoxShadowOuter::Pain
                                           gfxContext* aCtx) {
   nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
 
   nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
                                       frameRect, GetPaintRect());
 }
 
 bool nsDisplayButtonBoxShadowOuter::CanBuildWebRenderDisplayItems() {
-  // FIXME(emilio): Is this right? That doesn't make much sense.
-  if (mFrame->StyleEffects()->mBoxShadow.IsEmpty()) {
+  nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
+  if (!shadows) {
     return false;
   }
 
   bool hasBorderRadius;
   bool nativeTheme =
       nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
 
   // We don't support native themed things yet like box shadows around
@@ -154,36 +154,32 @@ bool nsDisplayButtonBoxShadowOuter::Crea
       borderRadius = wr::ToBorderRadius(
           LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
           LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
           LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
           LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
     }
   }
 
-  const Span<const StyleBoxShadow> shadows =
-      mFrame->StyleEffects()->mBoxShadow.AsSpan();
-  MOZ_ASSERT(!shadows.IsEmpty());
+  nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
+  MOZ_ASSERT(shadows);
 
-  for (const StyleBoxShadow& shadow : Reversed(shadows)) {
-    if (shadow.inset) {
+  for (uint32_t i = shadows->Length(); i > 0; i--) {
+    nsCSSShadowItem* shadow = shadows->ShadowAt(i - 1);
+    if (shadow->mInset) {
       continue;
     }
-    float blurRadius =
-        float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
+    float blurRadius = float(shadow->mRadius) / float(appUnitsPerDevPixel);
     gfx::Color shadowColor =
-        nsCSSRendering::GetShadowColor(shadow.base, mFrame, 1.0);
+        nsCSSRendering::GetShadowColor(shadow, mFrame, 1.0);
 
     LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
-        nsPoint(shadow.base.horizontal.ToAppUnits(),
-                shadow.base.vertical.ToAppUnits()),
-        appUnitsPerDevPixel);
+        nsPoint(shadow->mXOffset, shadow->mYOffset), appUnitsPerDevPixel);
 
-    float spreadRadius =
-        float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
+    float spreadRadius = float(shadow->mSpread) / float(appUnitsPerDevPixel);
 
     aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
                            deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
                            wr::ToColorF(shadowColor), blurRadius, spreadRadius,
                            borderRadius, wr::BoxShadowClipMode::Outset);
   }
   return true;
 }
@@ -384,17 +380,17 @@ bool nsDisplayButtonForeground::CreateWe
 
   br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
   return true;
 }
 
 nsresult nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
                                               nsDisplayList* aBackground,
                                               nsDisplayList* aForeground) {
-  if (!mFrame->StyleEffects()->mBoxShadow.IsEmpty()) {
+  if (mFrame->StyleEffects()->mBoxShadow) {
     aBackground->AppendNewToTop<nsDisplayButtonBoxShadowOuter>(aBuilder,
                                                                GetFrame());
   }
 
   nsRect buttonRect = mFrame->GetRectRelativeToSelf();
 
   nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, mFrame,
                                                        buttonRect, aBackground);
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -217,17 +217,17 @@ bool nsDisplayFieldSetBorder::CreateWebR
 void nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                        const nsDisplayListSet& aLists) {
   // Paint our background and border in a special way.
   // REVIEW: We don't really need to check frame emptiness here; if it's empty,
   // the background/border display item won't do anything, and if it isn't
   // empty, we need to paint the outline
   if (!(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
       IsVisibleForPainting()) {
-    if (!StyleEffects()->mBoxShadow.IsEmpty()) {
+    if (StyleEffects()->mBoxShadow) {
       aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowOuter>(
           aBuilder, this);
     }
 
     nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
         aBuilder, this, VisualBorderRectRelativeToSelf(),
         aLists.BorderBackground(),
         /* aAllowWillPaintBorderOptimization = */ false);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1209,20 +1209,18 @@ 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->mTransform != StyleDisplay()->mTransform) {
+          oldDisp->TransformChanged(*StyleDisplay())) {
         needAnchorSuppression = true;
       }
     }
 
     if (mInScrollAnchorChain && needAnchorSuppression) {
       ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
     }
   }
@@ -1637,17 +1635,17 @@ bool nsIFrame::ChildrenHavePerspective(
 }
 
 bool nsIFrame::HasOpacityInternal(float aThreshold,
                                   const nsStyleDisplay* aStyleDisplay,
                                   const nsStyleEffects* aStyleEffects,
                                   EffectSet* aEffectSet) const {
   MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
   if (aStyleEffects->mOpacity < aThreshold ||
-      (aStyleDisplay->mWillChange.bits & StyleWillChangeBits_OPACITY)) {
+      (aStyleDisplay->mWillChangeBitField & StyleWillChangeBits_OPACITY)) {
     return true;
   }
 
   if (!mMayHaveOpacityAnimation) {
     return false;
   }
 
   return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
@@ -2412,26 +2410,26 @@ void nsFrame::DisplayBorderBackgroundOut
                                              bool aForceBackground) {
   // The visibility check belongs here since child elements have the
   // opportunity to override the visibility property and display even if
   // their parent is hidden.
   if (!IsVisibleForPainting()) {
     return;
   }
 
-  const auto* effects = StyleEffects();
-  if (effects->HasBoxShadowWithInset(false)) {
+  nsCSSShadowArray* shadows = StyleEffects()->mBoxShadow;
+  if (shadows && shadows->HasShadowWithInset(false)) {
     aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowOuter>(aBuilder,
                                                                        this);
   }
 
   bool bgIsThemed =
       DisplayBackgroundUnconditional(aBuilder, aLists, aForceBackground);
 
-  if (effects->HasBoxShadowWithInset(true)) {
+  if (shadows && shadows->HasShadowWithInset(true)) {
     aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowInner>(aBuilder,
                                                                        this);
   }
 
   // If there's a themed background, we should not create a border item.
   // It won't be rendered.
   if (!bgIsThemed && StyleBorder()->HasBorder()) {
     aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
@@ -2867,27 +2865,27 @@ void nsIFrame::BuildDisplayListForStacki
   // we're painting, and we're not animating opacity. Don't do this
   // if we're going to compute plugin geometry, since opacity-0 plugins
   // need to have display items built for them.
   bool needHitTestInfo = aBuilder->BuildCompositorHitTestInfo() &&
                          StyleUI()->GetEffectivePointerEvents(this) !=
                              NS_STYLE_POINTER_EVENTS_NONE;
   bool opacityItemForEventsAndPluginsOnly = false;
   if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() &&
-      !(disp->mWillChange.bits & StyleWillChangeBits_OPACITY) &&
+      !(disp->mWillChangeBitField & StyleWillChangeBits_OPACITY) &&
       !nsLayoutUtils::HasAnimationOfPropertySet(
           this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
     if (needHitTestInfo || aBuilder->WillComputePluginGeometry()) {
       opacityItemForEventsAndPluginsOnly = true;
     } else {
       return;
     }
   }
 
-  if (disp->mWillChange.bits) {
+  if (disp->mWillChangeBitField) {
     aBuilder->AddToWillChangeBudget(this, GetSize());
   }
 
   // For preserves3d, use the dirty rect already installed on the
   // builder, since aDirtyRect maybe distorted for transforms along
   // the chain.
   nsRect visibleRect = aBuilder->GetVisibleRect();
   nsRect dirtyRect = aBuilder->GetDirtyRect();
@@ -10601,17 +10599,17 @@ bool nsIFrame::IsStackingContext(const n
            aStyleDisplay->IsContainLayout())) ||
          // strictly speaking, 'perspective' doesn't require visual atomicity,
          // but the spec says it acts like the rest of these
          ChildrenHavePerspective(aStyleDisplay) ||
          aStyleEffects->mMixBlendMode != NS_STYLE_BLEND_NORMAL ||
          nsSVGIntegrationUtils::UsingEffectsForFrame(this) ||
          (aIsPositioned && (aStyleDisplay->IsPositionForcingStackingContext() ||
                             aStylePosition->mZIndex.IsInteger())) ||
-         (aStyleDisplay->mWillChange.bits &
+         (aStyleDisplay->mWillChangeBitField &
           StyleWillChangeBits_STACKING_CONTEXT) ||
          aStyleDisplay->mIsolation != NS_STYLE_ISOLATION_AUTO;
 }
 
 bool nsIFrame::IsStackingContext() {
   const nsStyleDisplay* disp = StyleDisplay();
   const bool isPositioned = disp->IsAbsPosContainingBlock(this);
   return IsStackingContext(disp, StylePosition(), StyleEffects(), isPositioned);
@@ -10664,27 +10662,28 @@ 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->mMozWindowTransform.IsNone()) {
+  if (!uiReset->mSpecifiedWindowTransform) {
     return gfx::Matrix();
   }
 
   nsStyleTransformMatrix::TransformReferenceBox refBox;
   refBox.Init(GetSize());
 
   nsPresContext* presContext = PresContext();
   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
   gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
-      uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
+      uiReset->mSpecifiedWindowTransform->mHead, 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/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3450,17 +3450,17 @@ void ScrollFrameHelper::BuildDisplayList
   // borders and underneath borders and backgrounds of later elements
   // in the tree.
   // Note that this does not apply for overlay scrollbars; those are drawn
   // in the positioned-elements layer on top of everything else by the call
   // to AppendScrollPartsTo(..., true) further down.
   AppendScrollPartsTo(aBuilder, aLists, createLayersForScrollbars, false);
 
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
-  if (disp->mWillChange.bits & StyleWillChangeBits_SCROLL) {
+  if (disp->mWillChangeBitField & StyleWillChangeBits_SCROLL) {
     aBuilder->AddToWillChangeBudget(mOuter, GetVisualViewportSize());
   }
 
   mScrollParentID = aBuilder->GetCurrentScrollParentId();
 
   Maybe<nsRect> contentBoxClip;
   Maybe<const DisplayItemClipChain*> extraContentBoxClipForNonCaretContent;
   if (MOZ_UNLIKELY(
@@ -5412,30 +5412,30 @@ bool ScrollFrameHelper::IsScrollbarOnRig
       return true;
     case 3:  // Always left
       return false;
   }
 }
 
 bool ScrollFrameHelper::IsMaybeScrollingActive() const {
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
-  if (disp->mWillChange.bits & StyleWillChangeBits_SCROLL) {
+  if (disp->mWillChangeBitField & StyleWillChangeBits_SCROLL) {
     return true;
   }
 
   nsIContent* content = mOuter->GetContent();
   return mHasBeenScrolledRecently || IsAlwaysActive() ||
          nsLayoutUtils::HasDisplayPort(content) ||
          nsContentUtils::HasScrollgrab(content);
 }
 
 bool ScrollFrameHelper::IsScrollingActive(
     nsDisplayListBuilder* aBuilder) const {
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
-  if (disp->mWillChange.bits & StyleWillChangeBits_SCROLL &&
+  if (disp->mWillChangeBitField & StyleWillChangeBits_SCROLL &&
       aBuilder->IsInWillChangeBudget(mOuter, GetVisualViewportSize())) {
     return true;
   }
 
   nsIContent* content = mOuter->GetContent();
   return mHasBeenScrolledRecently || IsAlwaysActive() ||
          nsLayoutUtils::HasDisplayPort(content) ||
          nsContentUtils::HasScrollgrab(content);
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -348,17 +348,17 @@ class nsTextPaintStyle {
 
   // if this returns false, we don't need to draw underline.
   static bool GetSelectionUnderline(nsPresContext* aPresContext, int32_t aIndex,
                                     nscolor* aLineColor, float* aRelativeSize,
                                     uint8_t* aStyle);
 
   // if this returns false, no text-shadow was specified for the selection
   // and the *aShadow parameter was not modified.
-  bool GetSelectionShadow(Span<const StyleSimpleShadow>* aShadows);
+  bool GetSelectionShadow(nsCSSShadowArray** aShadow);
 
   nsPresContext* PresContext() const { return mPresContext; }
 
   enum {
     eIndexRawInput = 0,
     eIndexSelRawText,
     eIndexConvText,
     eIndexSelConvText,
@@ -393,17 +393,18 @@ class nsTextPaintStyle {
   bool mInitCommonColors;
   bool mInitSelectionColorsAndShadow;
   bool mResolveColors;
 
   // Selection data
 
   nscolor mSelectionTextColor;
   nscolor mSelectionBGColor;
-  RefPtr<ComputedStyle> mSelectionPseudoStyle;
+  RefPtr<nsCSSShadowArray> mSelectionShadow;
+  bool mHasSelectionShadow;
 
   // Common data
 
   int32_t mSufficientContrast;
   nscolor mFrameBackgroundColor;
   nscolor mSystemFieldForegroundColor;
   nscolor mSystemFieldBackgroundColor;
 
@@ -1816,62 +1817,62 @@ bool BuildTextRunsScanner::ContinueTextR
       HasTerminalNewline(aFrame1)) {
     return false;
   }
 
   if (aFrame1->GetParent()->GetContent() !=
       aFrame2->GetParent()->GetContent()) {
     // Does aFrame, or any ancestor between it and aAncestor, have a property
     // that should inhibit cross-element-boundary shaping on aSide?
-    auto PreventCrossBoundaryShaping = [](const nsIFrame* aFrame,
-                                          const nsIFrame* aAncestor,
-                                          Side aSide) {
-      while (aFrame != aAncestor) {
-        ComputedStyle* ctx = aFrame->Style();
-        // According to https://drafts.csswg.org/css-text/#boundary-shaping:
-        //
-        // Text shaping must be broken at inline box boundaries when any of
-        // the following are true for any box whose boundary separates the
-        // two typographic character units:
-        //
-        // 1. Any of margin/border/padding separating the two typographic
-        //    character units in the inline axis is non-zero.
-        const auto& margin = ctx->StyleMargin()->mMargin.Get(aSide);
-        if (!margin.ConvertsToLength() ||
-            margin.AsLengthPercentage().ToLength() != 0) {
-          return true;
-        }
-        const auto& padding = ctx->StylePadding()->mPadding.Get(aSide);
-        if (!padding.ConvertsToLength() || padding.ToLength() != 0) {
-          return true;
-        }
-        if (ctx->StyleBorder()->GetComputedBorderWidth(aSide) != 0) {
-          return true;
-        }
-
-        // 2. vertical-align is not baseline.
-        //
-        // FIXME: Should this use VerticalAlignEnum()?
-        const auto& verticalAlign = ctx->StyleDisplay()->mVerticalAlign;
-        if (!verticalAlign.IsKeyword() ||
-            verticalAlign.AsKeyword() != StyleVerticalAlignKeyword::Baseline) {
-          return true;
-        }
-
-        // 3. The boundary is a bidi isolation boundary.
-        const uint8_t unicodeBidi = ctx->StyleTextReset()->mUnicodeBidi;
-        if (unicodeBidi == NS_STYLE_UNICODE_BIDI_ISOLATE ||
-            unicodeBidi == NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE) {
-          return true;
-        }
-
-        aFrame = aFrame->GetParent();
-      }
-      return false;
-    };
+    auto PreventCrossBoundaryShaping =
+        [](const nsIFrame* aFrame, const nsIFrame* aAncestor, Side aSide) {
+          while (aFrame != aAncestor) {
+            ComputedStyle* ctx = aFrame->Style();
+            // According to https://drafts.csswg.org/css-text/#boundary-shaping:
+            //
+            // Text shaping must be broken at inline box boundaries when any of
+            // the following are true for any box whose boundary separates the
+            // two typographic character units:
+            //
+            // 1. Any of margin/border/padding separating the two typographic
+            //    character units in the inline axis is non-zero.
+            const auto& margin = ctx->StyleMargin()->mMargin.Get(aSide);
+            if (!margin.ConvertsToLength() ||
+                margin.AsLengthPercentage().ToLength() != 0) {
+              return true;
+            }
+            const auto& padding = ctx->StylePadding()->mPadding.Get(aSide);
+            if (!padding.ConvertsToLength() || padding.ToLength() != 0) {
+              return true;
+            }
+            if (ctx->StyleBorder()->GetComputedBorderWidth(aSide) != 0) {
+              return true;
+            }
+
+            // 2. vertical-align is not baseline.
+            //
+            // FIXME: Should this use VerticalAlignEnum()?
+            const auto& verticalAlign = ctx->StyleDisplay()->mVerticalAlign;
+            if (!verticalAlign.IsKeyword() ||
+                verticalAlign.AsKeyword() !=
+                    StyleVerticalAlignKeyword::Baseline) {
+              return true;
+            }
+
+            // 3. The boundary is a bidi isolation boundary.
+            const uint8_t unicodeBidi = ctx->StyleTextReset()->mUnicodeBidi;
+            if (unicodeBidi == NS_STYLE_UNICODE_BIDI_ISOLATE ||
+                unicodeBidi == NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE) {
+              return true;
+            }
+
+            aFrame = aFrame->GetParent();
+          }
+          return false;
+        };
 
     const nsIFrame* ancestor =
         nsLayoutUtils::FindNearestCommonAncestorFrame(aFrame1, aFrame2);
     MOZ_ASSERT(ancestor);
 
     // Map inline-end and inline-start to physical sides for checking presence
     // of non-zero margin/border/padding.
     Side side1 = wm.PhysicalSide(eLogicalSideIEnd);
@@ -2582,17 +2583,18 @@ void BuildTextRunsScanner::SetupBreakSin
   // whitespace...
   gfxSkipCharsIterator iter(aTextRun->GetSkipChars());
 
   for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
     MappedFlow* mappedFlow = &mMappedFlows[i];
     // The CSS word-break value may change within a word, so we reset it for
     // each MappedFlow. The line-breaker will flush its text if the property
     // actually changes.
-    auto wordBreak = mappedFlow->mStartFrame->StyleText()->EffectiveWordBreak();
+    auto wordBreak =
+      mappedFlow->mStartFrame->StyleText()->EffectiveWordBreak();
     switch (wordBreak) {
       case StyleWordBreak::BreakAll:
         mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_BreakAll);
         break;
       case StyleWordBreak::KeepAll:
         mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_KeepAll);
         break;
       case StyleWordBreak::Normal:
@@ -3781,16 +3783,17 @@ static nscolor EnsureDifferentColors(nsc
 nsTextPaintStyle::nsTextPaintStyle(nsTextFrame* aFrame)
     : mFrame(aFrame),
       mPresContext(aFrame->PresContext()),
       mInitCommonColors(false),
       mInitSelectionColorsAndShadow(false),
       mResolveColors(true),
       mSelectionTextColor(NS_RGBA(0, 0, 0, 0)),
       mSelectionBGColor(NS_RGBA(0, 0, 0, 0)),
+      mHasSelectionShadow(false),
       mSufficientContrast(0),
       mFrameBackgroundColor(NS_RGBA(0, 0, 0, 0)),
       mSystemFieldForegroundColor(NS_RGBA(0, 0, 0, 0)),
       mSystemFieldBackgroundColor(NS_RGBA(0, 0, 0, 0)) {
   for (uint32_t i = 0; i < ArrayLength(mSelectionStyle); i++)
     mSelectionStyle[i].mInit = false;
 }
 
@@ -4054,17 +4057,18 @@ bool nsTextPaintStyle::InitSelectionColo
 
   // Use ::selection pseudo class if applicable.
   if (RefPtr<ComputedStyle> style =
           mFrame->ComputeSelectionStyle(selectionStatus)) {
     mSelectionBGColor =
         style->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
     mSelectionTextColor =
         style->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
-    mSelectionPseudoStyle = style.forget();
+    mHasSelectionShadow = true;
+    mSelectionShadow = style->StyleText()->mTextShadow;
     return true;
   }
 
   nscolor selectionBGColor =
       LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
 
   if (selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
     mSelectionBGColor = LookAndFeel::GetColor(
@@ -4231,24 +4235,23 @@ bool nsTextPaintStyle::GetSelectionUnder
   }
   *aRelativeSize = size;
   *aStyle = style;
 
   return style != NS_STYLE_TEXT_DECORATION_STYLE_NONE &&
          color != NS_TRANSPARENT && size > 0.0f;
 }
 
-bool nsTextPaintStyle::GetSelectionShadow(
-    Span<const StyleSimpleShadow>* aShadows) {
+bool nsTextPaintStyle::GetSelectionShadow(nsCSSShadowArray** aShadow) {
   if (!InitSelectionColorsAndShadow()) {
     return false;
   }
 
-  if (mSelectionPseudoStyle) {
-    *aShadows = mSelectionPseudoStyle->StyleText()->mTextShadow.AsSpan();
+  if (mHasSelectionShadow) {
+    *aShadow = mSelectionShadow;
     return true;
   }
 
   return false;
 }
 
 inline nscolor Get40PercentColor(nscolor aForeColor, nscolor aBackColor) {
   nscolor foreColor = NS_RGBA(NS_GET_R(aForeColor), NS_GET_G(aForeColor),
@@ -5693,28 +5696,31 @@ bool nsTextFrame::GetSelectionTextColors
     default:
       *aForeground = aTextPaintStyle.GetTextColor();
       *aBackground = NS_RGBA(0, 0, 0, 0);
       return false;
   }
 }
 
 /**
- * This sets *aShadows to the appropriate shadows, if any, for the given
- * type of selection.
- * If text-shadow was not specified, *aShadows is left untouched.
+ * This sets *aShadow to the appropriate shadow, if any, for the given
+ * type of selection. Returns true if *aShadow was set.
+ * If text-shadow was not specified, *aShadow is left untouched
+ * (NOT reset to null), and the function returns false.
  */
-static void GetSelectionTextShadow(nsIFrame* aFrame,
+static bool GetSelectionTextShadow(nsIFrame* aFrame,
                                    SelectionType aSelectionType,
                                    nsTextPaintStyle& aTextPaintStyle,
-                                   Span<const StyleSimpleShadow>* aShadows) {
-  if (aSelectionType != SelectionType::eNormal) {
-    return;
-  }
-  aTextPaintStyle.GetSelectionShadow(aShadows);
+                                   nsCSSShadowArray** aShadow) {
+  switch (aSelectionType) {
+    case SelectionType::eNormal:
+      return aTextPaintStyle.GetSelectionShadow(aShadow);
+    default:
+      return false;
+  }
 }
 
 /**
  * This class lets us iterate over chunks of text in a uniform selection state,
  * observing cluster boundaries, in content order, maintaining the current
  * x-offset as we go, and telling whether the text chunk has a hyphen after
  * it or not. The caller is responsible for actually computing the advance
  * width of each chunk.
@@ -5835,31 +5841,32 @@ static void AddHyphenToMetrics(nsTextFra
       hyphenTextRun->MeasureText(aBoundingBoxType, aDrawTarget);
   if (aTextFrame->GetWritingMode().IsLineInverted()) {
     hyphenMetrics.mBoundingBox.y = -hyphenMetrics.mBoundingBox.YMost();
   }
   aMetrics->CombineWith(hyphenMetrics, aBaseTextRun->IsRightToLeft());
 }
 
 void nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams,
-                                 const StyleSimpleShadow& aShadowDetails,
+                                 nsCSSShadowItem* aShadowDetails,
                                  gfxRect& aBoundingBox, uint32_t aBlurFlags) {
   AUTO_PROFILER_LABEL("nsTextFrame::PaintOneShadow", GRAPHICS);
 
-  nsPoint shadowOffset(aShadowDetails.horizontal.ToAppUnits(),
-                       aShadowDetails.vertical.ToAppUnits());
-  nscoord blurRadius = std::max(aShadowDetails.blur.ToAppUnits(), 0);
-
-  nscolor shadowColor = aShadowDetails.color.CalcColor(aParams.foregroundColor);
+  gfx::Point shadowOffset(aShadowDetails->mXOffset, aShadowDetails->mYOffset);
+  nscoord blurRadius = std::max(aShadowDetails->mRadius, 0);
+
+  nscolor shadowColor =
+      aShadowDetails->mColor.CalcColor(aParams.foregroundColor);
 
   if (auto* textDrawer = aParams.context->GetTextDrawer()) {
     wr::Shadow wrShadow;
 
-    wrShadow.offset = {PresContext()->AppUnitsToFloatDevPixels(shadowOffset.x),
-                       PresContext()->AppUnitsToFloatDevPixels(shadowOffset.y)};
+    wrShadow.offset = {
+        PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mXOffset),
+        PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mYOffset)};
 
     wrShadow.blur_radius = PresContext()->AppUnitsToFloatDevPixels(blurRadius);
     wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
 
     bool inflate = true;
     textDrawer->AppendShadow(wrShadow, inflate);
     return;
   }
@@ -5879,18 +5886,17 @@ void nsTextFrame::PaintOneShadow(const P
     }
     shadowGfxRect += gfxPoint(aParams.textBaselinePt.x,
                               aParams.framePt.y + aParams.leftSideOffset);
   } else {
     shadowGfxRect =
         aBoundingBox + gfxPoint(aParams.framePt.x + aParams.leftSideOffset,
                                 aParams.textBaselinePt.y);
   }
-  Point shadowGfxOffset(shadowOffset.x, shadowOffset.y);
-  shadowGfxRect += gfxPoint(shadowGfxOffset.x, shadowOffset.y);
+  shadowGfxRect += gfxPoint(shadowOffset.x, shadowOffset.y);
 
   nsRect shadowRect(NSToCoordRound(shadowGfxRect.X()),
                     NSToCoordRound(shadowGfxRect.Y()),
                     NSToCoordRound(shadowGfxRect.Width()),
                     NSToCoordRound(shadowGfxRect.Height()));
 
   nsContextBoxBlur contextBoxBlur;
   const auto A2D = PresContext()->AppUnitsPerDevPixel();
@@ -5906,27 +5912,27 @@ void nsTextFrame::PaintOneShadow(const P
   // Draw the text onto our alpha-only surface to capture the alpha values.
   // Remember that the box blur context has a device offset on it, so we don't
   // need to translate any coordinates to fit on the surface.
   gfxFloat advanceWidth;
   nsTextPaintStyle textPaintStyle(this);
   DrawTextParams params(shadowContext);
   params.advanceWidth = &advanceWidth;
   params.dirtyRect = aParams.dirtyRect;
-  params.framePt = aParams.framePt + shadowGfxOffset;
+  params.framePt = aParams.framePt + shadowOffset;
   params.provider = aParams.provider;
   params.textStyle = &textPaintStyle;
   params.textColor =
       aParams.context == shadowContext ? shadowColor : NS_RGB(0, 0, 0);
   params.clipEdges = aParams.clipEdges;
   params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
   // Multi-color shadow is not allowed, so we use the same color of the text
   // color.
   params.decorationOverrideColor = &params.textColor;
-  DrawText(aParams.range, aParams.textBaselinePt + shadowGfxOffset, params);
+  DrawText(aParams.range, aParams.textBaselinePt + shadowOffset, params);
 
   contextBoxBlur.DoPaint();
   aParams.context->Restore();
 }
 
 // Paints selection backgrounds and text in the correct colors. Also computes
 // aAllTypes, the union of all selection types that are applying to this text.
 bool nsTextFrame::PaintTextWithSelectionColors(
@@ -6070,30 +6076,30 @@ bool nsTextFrame::PaintTextWithSelection
 
     gfx::Point textBaselinePt =
         vertical
             ? gfx::Point(aParams.textBaselinePt.x, aParams.framePt.y + iOffset)
             : gfx::Point(aParams.framePt.x + iOffset, aParams.textBaselinePt.y);
 
     // Determine what shadow, if any, to draw - either from textStyle
     // or from the ::-moz-selection pseudo-class if specified there
-    Span<const StyleSimpleShadow> shadows = textStyle->mTextShadow.AsSpan();
+    nsCSSShadowArray* shadow = textStyle->GetTextShadow();
     GetSelectionTextShadow(this, selectionType, *aParams.textPaintStyle,
-                           &shadows);
-    if (!shadows.IsEmpty()) {
+                           &shadow);
+    if (shadow) {
       nscoord startEdge = iOffset;
       if (mTextRun->IsInlineReversed()) {
         startEdge -=
             hyphenWidth + mTextRun->GetAdvanceWidth(range, aParams.provider);
       }
       shadowParams.range = range;
       shadowParams.textBaselinePt = textBaselinePt;
       shadowParams.foregroundColor = foreground;
       shadowParams.leftSideOffset = startEdge;
-      PaintShadows(shadows, shadowParams);
+      PaintShadows(shadow, shadowParams);
     }
 
     // Draw text segment
     params.textColor = foreground;
     params.textStrokeColor = aParams.textPaintStyle->GetWebkitTextStrokeColor();
     params.textStrokeWidth = aParams.textPaintStyle->GetWebkitTextStrokeWidth();
     params.drawSoftHyphen = hyphenWidth > 0;
     DrawText(range, textBaselinePt, params);
@@ -6443,19 +6449,19 @@ bool nsTextFrame::MeasureCharClippedText
     maxLength = offset - *aStartOffset;
     nscoord* snappedEndEdge = rtl ? aSnappedStartEdge : aSnappedEndEdge;
     *snappedEndEdge = NSToCoordFloor(gfxFloat(frameISize) - advanceWidth);
   }
   *aMaxLength = maxLength;
   return maxLength != 0;
 }
 
-void nsTextFrame::PaintShadows(Span<const StyleSimpleShadow> aShadows,
+void nsTextFrame::PaintShadows(nsCSSShadowArray* aShadow,
                                const PaintShadowParams& aParams) {
-  if (aShadows.IsEmpty()) {
+  if (!aShadow) {
     return;
   }
 
   gfxTextRun::Metrics shadowMetrics = mTextRun->MeasureText(
       aParams.range, gfxFont::LOOSE_INK_EXTENTS, nullptr, aParams.provider);
   if (GetWritingMode().IsLineInverted()) {
     Swap(shadowMetrics.mAscent, shadowMetrics.mDescent);
     shadowMetrics.mBoundingBox.y = -shadowMetrics.mBoundingBox.YMost();
@@ -6486,18 +6492,19 @@ void nsTextFrame::PaintShadows(Span<cons
     run++;
   }
 
   if (mTextRun->IsVertical()) {
     Swap(shadowMetrics.mBoundingBox.x, shadowMetrics.mBoundingBox.y);
     Swap(shadowMetrics.mBoundingBox.width, shadowMetrics.mBoundingBox.height);
   }
 
-  for (const auto& shadow : Reversed(aShadows)) {
-    PaintOneShadow(aParams, shadow, shadowMetrics.mBoundingBox, blurFlags);
+  for (uint32_t i = aShadow->Length(); i > 0; --i) {
+    PaintOneShadow(aParams, aShadow->ShadowAt(i - 1),
+                   shadowMetrics.mBoundingBox, blurFlags);
   }
 }
 
 void nsTextFrame::PaintText(const PaintTextParams& aParams,
                             const nscoord aVisIStartEdge,
                             const nscoord aVisIEndEdge,
                             const nsPoint& aToReferenceFrame,
                             const bool aIsSelected,
@@ -6597,17 +6604,17 @@ void nsTextFrame::PaintText(const PaintT
     const nsStyleText* textStyle = StyleText();
     PaintShadowParams shadowParams(aParams);
     shadowParams.range = range;
     shadowParams.textBaselinePt = textBaselinePt;
     shadowParams.leftSideOffset = snappedStartEdge;
     shadowParams.provider = &provider;
     shadowParams.foregroundColor = foregroundColor;
     shadowParams.clipEdges = &clipEdges;
-    PaintShadows(textStyle->mTextShadow.AsSpan(), shadowParams);
+    PaintShadows(textStyle->mTextShadow, shadowParams);
   }
 
   gfxFloat advanceWidth;
   DrawTextParams params(aParams.context);
   params.dirtyRect = aParams.dirtyRect;
   params.framePt = aParams.framePt;
   params.provider = &provider;
   params.advanceWidth = &advanceWidth;
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -671,20 +671,20 @@ class nsTextFrame : public nsFrame {
     nscoord leftSideOffset = 0;
     explicit PaintShadowParams(const PaintTextParams& aParams)
         : dirtyRect(aParams.dirtyRect),
           framePt(aParams.framePt),
           context(aParams.context) {}
   };
 
   void PaintOneShadow(const PaintShadowParams& aParams,
-                      const mozilla::StyleSimpleShadow& aShadowDetails,
-                      gfxRect& aBoundingBox, uint32_t aBlurFlags);
+                      nsCSSShadowItem* aShadowDetails, gfxRect& aBoundingBox,
+                      uint32_t aBlurFlags);
 
-  void PaintShadows(mozilla::Span<const mozilla::StyleSimpleShadow>,
+  void PaintShadows(nsCSSShadowArray* aShadow,
                     const PaintShadowParams& aParams);
 
   struct LineDecoration {
     nsIFrame* mFrame;
 
     // This is represents the offset from our baseline to mFrame's baseline;
     // positive offsets are *above* the baseline and negative offsets below
     nscoord mBaselineOffset;
--- a/layout/painting/ActiveLayerTracker.cpp
+++ b/layout/painting/ActiveLayerTracker.cpp
@@ -259,31 +259,34 @@ 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->HasTransformProperty() && !display->HasIndividualTransform() &&
+  if (!display->mSpecifiedTransform && !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->mTranslate, display->mRotate, display->mScale,
-      nsLayoutUtils::ResolveMotionPath(aFrame), display->mTransform, refBox,
-      AppUnitsPerCSSPixel());
+      display->mIndividualTransform ? display->mIndividualTransform->mHead
+                                    : nullptr,
+      nsLayoutUtils::ResolveMotionPath(aFrame),
+      display->mSpecifiedTransform ? display->mSpecifiedTransform->mHead
+                                   : nullptr,
+      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;
   }
@@ -475,24 +478,24 @@ bool ActiveLayerTracker::IsStyleAnimated
   // For display:table content, transforms are applied to the table wrapper
   // (primary frame) but their will-change style will be specified on the style
   // frame and, unlike other transform properties, not inherited.
   // As a result, for transform properties only we need to be careful to look up
   // the will-change style on the _style_ frame.
   const nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aFrame);
   const nsCSSPropertyIDSet transformSet =
       nsCSSPropertyIDSet::TransformLikeProperties();
-  if ((styleFrame && (styleFrame->StyleDisplay()->mWillChange.bits &
+  if ((styleFrame && (styleFrame->StyleDisplay()->mWillChangeBitField &
                       StyleWillChangeBits_TRANSFORM)) &&
       aPropertySet.Intersects(transformSet) &&
       (!aBuilder ||
        aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
     return true;
   }
-  if ((aFrame->StyleDisplay()->mWillChange.bits &
+  if ((aFrame->StyleDisplay()->mWillChangeBitField &
        StyleWillChangeBits_OPACITY) &&
       aPropertySet.Intersects(nsCSSPropertyIDSet::OpacityProperties()) &&
       (!aBuilder ||
        aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
     return true;
   }
 
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -1349,20 +1349,20 @@ bool nsCSSRendering::HasBoxShadowNativeT
     // border-box path with border-radius disabled.
     return transparency != nsITheme::eOpaque;
   }
 
   aMaybeHasBorderRadius = true;
   return false;
 }
 
-gfx::Color nsCSSRendering::GetShadowColor(const StyleSimpleShadow& aShadow,
+gfx::Color nsCSSRendering::GetShadowColor(nsCSSShadowItem* aShadow,
                                           nsIFrame* aFrame, float aOpacity) {
   // Get the shadow color; if not specified, use the foreground color
-  nscolor shadowColor = aShadow.color.CalcColor(aFrame);
+  nscolor shadowColor = aShadow->mColor.CalcColor(aFrame);
   Color color = Color::FromABGR(shadowColor);
   color.a *= aOpacity;
   return color;
 }
 
 nsRect nsCSSRendering::GetShadowRect(const nsRect& aFrameArea,
                                      bool aNativeTheme, nsIFrame* aForFrame) {
   nsRect frameRect = aNativeTheme
@@ -1396,20 +1396,18 @@ bool nsCSSRendering::GetBorderRadii(cons
 
 void nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
                                          gfxContext& aRenderingContext,
                                          nsIFrame* aForFrame,
                                          const nsRect& aFrameArea,
                                          const nsRect& aDirtyRect,
                                          float aOpacity) {
   DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
-  auto shadows = aForFrame->StyleEffects()->mBoxShadow.AsSpan();
-  if (shadows.IsEmpty()) {
-    return;
-  }
+  nsCSSShadowArray* shadows = aForFrame->StyleEffects()->mBoxShadow;
+  if (!shadows) return;
 
   bool hasBorderRadius;
   // mutually exclusive with hasBorderRadius
   bool nativeTheme = HasBoxShadowNativeTheme(aForFrame, hasBorderRadius);
   const nsStyleDisplay* styleDisplay = aForFrame->StyleDisplay();
 
   nsRect frameRect = GetShadowRect(aFrameArea, nativeTheme, aForFrame);
 
@@ -1446,54 +1444,51 @@ void nsCSSRendering::PaintBoxShadowOuter
         aForFrame->GetPaddingRectRelativeToSelf() + aFrameArea.TopLeft();
     skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, oneDevPixel);
   } else if (hasBorderRadius) {
     skipGfxRect.Deflate(gfxMargin(
         std::max(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
         std::max(borderRadii[C_BL].height, borderRadii[C_BR].height), 0));
   }
 
-  for (const StyleBoxShadow& shadow : Reversed(shadows)) {
-    if (shadow.inset) {
-      continue;
-    }
+  for (uint32_t i = shadows->Length(); i > 0; --i) {
+    nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
+    if (shadowItem->mInset) continue;
 
     nsRect shadowRect = frameRect;
-    nsPoint shadowOffset(shadow.base.horizontal.ToAppUnits(),
-                         shadow.base.vertical.ToAppUnits());
-    shadowRect.MoveBy(shadowOffset);
-    nscoord shadowSpread = shadow.spread.ToAppUnits();
+    shadowRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
     if (!nativeTheme) {
-      shadowRect.Inflate(shadowSpread);
+      shadowRect.Inflate(shadowItem->mSpread);
     }
 
     // shadowRect won't include the blur, so make an extra rect here that
     // includes the blur for use in the even-odd rule below.
     nsRect shadowRectPlusBlur = shadowRect;
-    nscoord blurRadius = shadow.base.blur.ToAppUnits();
+    nscoord blurRadius = shadowItem->mRadius;
     shadowRectPlusBlur.Inflate(
         nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, oneDevPixel));
 
     Rect shadowGfxRectPlusBlur = NSRectToRect(shadowRectPlusBlur, oneDevPixel);
     shadowGfxRectPlusBlur.RoundOut();
     MaybeSnapToDevicePixels(shadowGfxRectPlusBlur, aDrawTarget, true);
 
-    Color gfxShadowColor = GetShadowColor(shadow.base, aForFrame, aOpacity);
+    Color gfxShadowColor = GetShadowColor(shadowItem, aForFrame, aOpacity);
 
     if (nativeTheme) {
       nsContextBoxBlur blurringArea;
 
       // When getting the widget shape from the native theme, we're going
       // to draw the widget into the shadow surface to create a mask.
       // We need to ensure that there actually *is* a shadow surface
       // and that we're not going to draw directly into aRenderingContext.
-      gfxContext* shadowContext = blurringArea.Init(
-          shadowRect, shadowSpread, blurRadius, oneDevPixel, &aRenderingContext,
-          aDirtyRect, useSkipGfxRect ? &skipGfxRect : nullptr,
-          nsContextBoxBlur::FORCE_MASK);
+      gfxContext* shadowContext =
+          blurringArea.Init(shadowRect, shadowItem->mSpread, blurRadius,
+                            oneDevPixel, &aRenderingContext, aDirtyRect,
+                            useSkipGfxRect ? &skipGfxRect : nullptr,
+                            nsContextBoxBlur::FORCE_MASK);
       if (!shadowContext) continue;
 
       MOZ_ASSERT(shadowContext == blurringArea.GetContext());
 
       aRenderingContext.Save();
       aRenderingContext.SetColor(gfxShadowColor);
 
       // Draw the shape of the frame so it can be blurred. Recall how
@@ -1504,22 +1499,23 @@ void nsCSSRendering::PaintBoxShadowOuter
       // this.
 
       // We don't clip the border-box from the shadow, nor any other box.
       // We assume that the native theme is going to paint over the shadow.
 
       // Draw the widget shape
       gfxContextMatrixAutoSaveRestore save(shadowContext);
       gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(
-          shadowOffset, aPresContext->AppUnitsPerDevPixel());
+          nsPoint(shadowItem->mXOffset, shadowItem->mYOffset),
+          aPresContext->AppUnitsPerDevPixel());
       shadowContext->SetMatrixDouble(
           shadowContext->CurrentMatrixDouble().PreTranslate(devPixelOffset));
 
       nsRect nativeRect = aDirtyRect;
-      nativeRect.MoveBy(-shadowOffset);
+      nativeRect.MoveBy(-nsPoint(shadowItem->mXOffset, shadowItem->mYOffset));
       nativeRect.IntersectRect(frameRect, nativeRect);
       aPresContext->GetTheme()->DrawWidgetBackground(shadowContext, aForFrame,
                                                      styleDisplay->mAppearance,
                                                      aFrameArea, nativeRect);
 
       blurringArea.DoPaint();
       aRenderingContext.Restore();
     } else {
@@ -1576,17 +1572,17 @@ void nsCSSRendering::PaintBoxShadowOuter
       }
       fragmentClip = fragmentClip.Intersect(aDirtyRect);
       aRenderingContext.Clip(NSRectToSnappedRect(
           fragmentClip, aForFrame->PresContext()->AppUnitsPerDevPixel(),
           aDrawTarget));
 
       RectCornerRadii clipRectRadii;
       if (hasBorderRadius) {
-        Float spreadDistance = Float(shadowSpread / oneDevPixel);
+        Float spreadDistance = Float(shadowItem->mSpread) / oneDevPixel;
 
         Float borderSizes[4];
 
         borderSizes[eSideLeft] = spreadDistance;
         borderSizes[eSideTop] = spreadDistance;
         borderSizes[eSideRight] = spreadDistance;
         borderSizes[eSideBottom] = spreadDistance;
 
@@ -1609,21 +1605,18 @@ nsRect nsCSSRendering::GetBoxShadowInner
 
   nsRect paddingRect = frameRect;
   nsMargin border = aFrame->GetUsedBorder();
   paddingRect.Deflate(border);
   return paddingRect;
 }
 
 bool nsCSSRendering::ShouldPaintBoxShadowInner(nsIFrame* aFrame) {
-  const Span<const StyleBoxShadow> shadows =
-      aFrame->StyleEffects()->mBoxShadow.AsSpan();
-  if (shadows.IsEmpty()) {
-    return false;
-  }
+  nsCSSShadowArray* shadows = aFrame->StyleEffects()->mBoxShadow;
+  if (!shadows) return false;
 
   if (aFrame->IsThemed() && aFrame->GetContent() &&
       !nsContentUtils::IsChromeDoc(aFrame->GetContent()->GetComposedDoc())) {
     // There's no way of getting hold of a shape corresponding to a
     // "padding-box" for native-themed widgets, so just don't draw
     // inner box-shadows for them. But we allow chrome to paint inner
     // box shadows since chrome can be aware of the platform theme.
     return false;
@@ -1666,55 +1659,52 @@ bool nsCSSRendering::GetShadowInnerRadii
 void nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
                                          gfxContext& aRenderingContext,
                                          nsIFrame* aForFrame,
                                          const nsRect& aFrameArea) {
   if (!ShouldPaintBoxShadowInner(aForFrame)) {
     return;
   }
 
-  const Span<const StyleBoxShadow> shadows =
-      aForFrame->StyleEffects()->mBoxShadow.AsSpan();
+  nsCSSShadowArray* shadows = aForFrame->StyleEffects()->mBoxShadow;
   NS_ASSERTION(
       aForFrame->IsFieldSetFrame() || aFrameArea.Size() == aForFrame->GetSize(),
       "unexpected size");
 
   nsRect paddingRect = GetBoxShadowInnerPaddingRect(aForFrame, aFrameArea);
 
   RectCornerRadii innerRadii;
   bool hasBorderRadius = GetShadowInnerRadii(aForFrame, aFrameArea, innerRadii);
 
   const nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
 
-  for (const StyleBoxShadow& shadow : Reversed(shadows)) {
-    if (!shadow.inset) {
-      continue;
-    }
+  for (uint32_t i = shadows->Length(); i > 0; --i) {
+    nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
+    if (!shadowItem->mInset) continue;
 
     // shadowPaintRect: the area to paint on the temp surface
     // shadowClipRect: the area on the temporary surface within shadowPaintRect
     //                 that we will NOT paint in
-    nscoord blurRadius = shadow.base.blur.ToAppUnits();
+    nscoord blurRadius = shadowItem->mRadius;
     nsMargin blurMargin =
         nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, oneDevPixel);
     nsRect shadowPaintRect = paddingRect;
     shadowPaintRect.Inflate(blurMargin);
 
     // Round the spread radius to device pixels (by truncation).
     // This mostly matches what we do for borders, except that we don't round
     // up values between zero and one device pixels to one device pixel.
     // This way of rounding is symmetric around zero, which makes sense for
     // the spread radius.
-    int32_t spreadDistance = shadow.spread.ToAppUnits() / oneDevPixel;
+    int32_t spreadDistance = shadowItem->mSpread / oneDevPixel;
     nscoord spreadDistanceAppUnits =
         aPresContext->DevPixelsToAppUnits(spreadDistance);
 
     nsRect shadowClipRect = paddingRect;
-    shadowClipRect.MoveBy(shadow.base.horizontal.ToAppUnits(),
-                          shadow.base.vertical.ToAppUnits());
+    shadowClipRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
     shadowClipRect.Deflate(spreadDistanceAppUnits, spreadDistanceAppUnits);
 
     Rect shadowClipGfxRect = NSRectToRect(shadowClipRect, oneDevPixel);
     shadowClipGfxRect.Round();
 
     RectCornerRadii clipRectRadii;
     if (hasBorderRadius) {
       // Calculate the radii the inner clipping rect will have
@@ -1759,34 +1749,34 @@ void nsCSSRendering::PaintBoxShadowInner
     DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
 
     // Clip the context to the area of the frame's padding rect, so no part of
     // the shadow is painted outside. Also cut out anything beyond where the
     // inset shadow will be.
     Rect shadowGfxRect = NSRectToRect(paddingRect, oneDevPixel);
     shadowGfxRect.Round();
 
-    Color shadowColor = GetShadowColor(shadow.base, aForFrame, 1.0);
+    Color shadowColor = GetShadowColor(shadowItem, aForFrame, 1.0);
     aRenderingContext.Save();
 
     // This clips the outside border radius.
     // clipRectRadii is the border radius inside the inset shadow.
     if (hasBorderRadius) {
       RefPtr<Path> roundedRect =
           MakePathForRoundedRect(*drawTarget, shadowGfxRect, innerRadii);
       aRenderingContext.Clip(roundedRect);
     } else {
       aRenderingContext.Clip(shadowGfxRect);
     }
 
     nsContextBoxBlur insetBoxBlur;
     gfxRect destRect =
         nsLayoutUtils::RectToGfxRect(shadowPaintRect, oneDevPixel);
-    Point shadowOffset(shadow.base.horizontal.ToAppUnits() / oneDevPixel,
-                       shadow.base.vertical.ToAppUnits() / oneDevPixel);
+    Point shadowOffset(shadowItem->mXOffset / oneDevPixel,
+                       shadowItem->mYOffset / oneDevPixel);
 
     insetBoxBlur.InsetBoxBlur(
         &aRenderingContext, ToRect(destRect), shadowClipGfxRect, shadowColor,
         blurRadius, spreadDistanceAppUnits, oneDevPixel, hasBorderRadius,
         clipRectRadii, ToRect(skipGfxRect), shadowOffset);
     aRenderingContext.Restore();
   }
 }
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -136,17 +136,17 @@ struct nsCSSRendering {
                                   nsIFrame* aForFrame,
                                   const nsRect& aFrameArea);
 
   static bool GetBorderRadii(const nsRect& aFrameRect,
                              const nsRect& aBorderRect, nsIFrame* aFrame,
                              RectCornerRadii& aOutRadii);
   static nsRect GetShadowRect(const nsRect& aFrameArea, bool aNativeTheme,
                               nsIFrame* aForFrame);
-  static mozilla::gfx::Color GetShadowColor(const mozilla::StyleSimpleShadow&,
+  static mozilla::gfx::Color GetShadowColor(nsCSSShadowItem* aShadow,
                                             nsIFrame* aFrame, float aOpacity);
   // Returns if the frame has a themed frame.
   // aMaybeHasBorderRadius will return false if we can early detect
   // that we don't have a border radius.
   static bool HasBoxShadowNativeTheme(nsIFrame* aFrame,
                                       bool& aMaybeHasBorderRadius);
   static void PaintBoxShadowOuter(nsPresContext* aPresContext,
                                   gfxContext& aRenderingContext,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -173,262 +173,267 @@ nsCString ActiveScrolledRoot::ToString(
     str.AppendPrintf("<0x%p>", asr->mScrollableFrame);
     if (asr->mParent) {
       str.AppendLiteral(", ");
     }
   }
   return std::move(str);
 }
 
-static inline CSSAngle MakeCSSAngle(const StyleAngle& aValue) {
-  return CSSAngle(aValue.ToDegrees(), eCSSUnit_Degree);
-}
-
-static Rotate GetRotate(const StyleRotate& aValue) {
+static inline CSSAngle MakeCSSAngle(const nsCSSValue& aValue) {
+  return CSSAngle(aValue.GetAngleValue(), aValue.GetUnit());
+}
+
+static Rotate GetRotate(const nsCSSValue& aValue) {
   Rotate result = null_t();
-  switch (aValue.tag) {
-    case StyleRotate::Tag::None:
+  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))));
       break;
-    case StyleRotate::Tag::Rotate:
-      result = Rotate(Rotation(MakeCSSAngle(aValue.AsRotate())));
+    case eCSSKeyword_rotate3d:
+      result = Rotate(Rotation3D(
+          array->Item(1).GetFloatValue(), array->Item(2).GetFloatValue(),
+          array->Item(3).GetFloatValue(), MakeCSSAngle(array->Item(4))));
       break;
-    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 StyleScale& aValue) {
+static Scale GetScale(const nsCSSValue& aValue) {
   Scale result(1., 1., 1.);
-  switch (aValue.tag) {
-    case StyleScale::Tag::None:
+  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();
       break;
-    case StyleScale::Tag::Scale: {
-      auto& scale = aValue.AsScale();
-      result.x() = scale._0;
-      result.y() = scale._1;
+    case eCSSKeyword_scalez:
+      result.z() = array->Item(1).GetFloatValue();
       break;
-    }
-    case StyleScale::Tag::Scale3D: {
-      auto& scale = aValue.AsScale3D();
-      result.x() = scale._0;
-      result.y() = scale._1;
-      result.z() = scale._2;
+    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();
       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(
-    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,
+static Translation GetTranslate(const nsCSSValue& aValue,
                                 TransformReferenceBox& aRefBox) {
   Translation result(0, 0, 0);
-  switch (aValue.tag) {
-    case StyleTranslate::Tag::None:
+  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);
       break;
-    case StyleTranslate::Tag::Translate: {
-      auto& translate = aValue.AsTranslate();
-      result = GetTranslate(aRefBox, translate._0, translate._1);
+    case eCSSKeyword_translatey:
+      result.y() = nsStyleTransformMatrix::ProcessTranslatePart(
+          array->Item(1), &aRefBox, &TransformReferenceBox::Height);
       break;
-    }
-    case StyleTranslate::Tag::Translate3D: {
-      auto& translate = aValue.AsTranslate3D();
-      result = GetTranslate(aRefBox, translate._0, translate._1, translate._2);
+    case eCSSKeyword_translatez:
+      result.z() =
+          nsStyleTransformMatrix::ProcessTranslatePart(array->Item(1), nullptr);
       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 StyleTransform& aTransform, TransformReferenceBox& aRefBox,
+    const nsCSSValueList* aList, mozilla::ComputedStyle* aStyle,
+    nsPresContext* aPresContext, TransformReferenceBox& aRefBox,
     InfallibleTArray<TransformFunction>& aFunctions) {
-  for (const StyleTransformOperation& op : aTransform.Operations()) {
-    switch (op.tag) {
-      case StyleTransformOperation::Tag::RotateX: {
-        CSSAngle theta = MakeCSSAngle(op.AsRotateX());
+  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));
         aFunctions.AppendElement(RotationX(theta));
         break;
       }
-      case StyleTransformOperation::Tag::RotateY: {
-        CSSAngle theta = MakeCSSAngle(op.AsRotateY());
+      case eCSSKeyword_rotatey: {
+        CSSAngle theta = MakeCSSAngle(array->Item(1));
         aFunctions.AppendElement(RotationY(theta));
         break;
       }
-      case StyleTransformOperation::Tag::RotateZ: {
-        CSSAngle theta = MakeCSSAngle(op.AsRotateZ());
+      case eCSSKeyword_rotatez: {
+        CSSAngle theta = MakeCSSAngle(array->Item(1));
         aFunctions.AppendElement(RotationZ(theta));
         break;
       }
-      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));
+      case eCSSKeyword_rotate:
+        aFunctions.AppendElement(GetRotate(currElem).get_Rotation());
         break;
-      }
-      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.));
+      case eCSSKeyword_rotate3d:
+        aFunctions.AppendElement(GetRotate(currElem).get_Rotation3D());
         break;
-      }
-      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()));
+      case eCSSKeyword_scalex:
+      case eCSSKeyword_scaley:
+      case eCSSKeyword_scalez:
+      case eCSSKeyword_scale:
+      case eCSSKeyword_scale3d:
+        aFunctions.AppendElement(GetScale(currElem));
         break;
-      }
-      case StyleTransformOperation::Tag::TranslateZ: {
-        aFunctions.AppendElement(GetTranslate(aRefBox, LengthPercentage::Zero(),
-                                              LengthPercentage::Zero(),
-                                              op.AsTranslateZ()));
+      case eCSSKeyword_translatex:
+      case eCSSKeyword_translatey:
+      case eCSSKeyword_translatez:
+      case eCSSKeyword_translate:
+      case eCSSKeyword_translate3d:
+        aFunctions.AppendElement(GetTranslate(currElem, aRefBox));
         break;
-      }
-      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());
+      case eCSSKeyword_skewx: {
+        CSSAngle x = MakeCSSAngle(array->Item(1));
         aFunctions.AppendElement(SkewX(x));
         break;
       }
-      case StyleTransformOperation::Tag::SkewY: {
-        CSSAngle y = MakeCSSAngle(op.AsSkewY());
+      case eCSSKeyword_skewy: {
+        CSSAngle y = MakeCSSAngle(array->Item(1));
         aFunctions.AppendElement(SkewY(y));
         break;
       }
-      case StyleTransformOperation::Tag::Skew: {
-        const auto& skew = op.AsSkew();
-        aFunctions.AppendElement(
-            Skew(MakeCSSAngle(skew._0), MakeCSSAngle(skew._1)));
+      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));
         break;
       }
-      case StyleTransformOperation::Tag::Matrix: {
+      case eCSSKeyword_matrix: {
         gfx::Matrix4x4 matrix;
-        const auto& m = op.AsMatrix();
-        matrix._11 = m.a;
-        matrix._12 = m.b;
+        matrix._11 = array->Item(1).GetFloatValue();
+        matrix._12 = array->Item(2).GetFloatValue();
         matrix._13 = 0;
         matrix._14 = 0;
-        matrix._21 = m.c;
-        matrix._22 = m.d;
+        matrix._21 = array->Item(3).GetFloatValue();
+        matrix._22 = array->Item(4).GetFloatValue();
         matrix._23 = 0;
         matrix._24 = 0;
         matrix._31 = 0;
         matrix._32 = 0;
         matrix._33 = 1;
         matrix._34 = 0;
-        matrix._41 = m.e;
-        matrix._42 = m.f;
+        matrix._41 = ProcessTranslatePart(array->Item(5), &aRefBox,
+                                          &TransformReferenceBox::Width);
+        matrix._42 = ProcessTranslatePart(array->Item(6), &aRefBox,
+                                          &TransformReferenceBox::Height);
         matrix._43 = 0;
         matrix._44 = 1;
         aFunctions.AppendElement(TransformMatrix(matrix));
         break;
       }
-      case StyleTransformOperation::Tag::Matrix3D: {
-        const auto& m = op.AsMatrix3D();
+      case eCSSKeyword_matrix3d: {
         gfx::Matrix4x4 matrix;
-
-        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;
+        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();
         aFunctions.AppendElement(TransformMatrix(matrix));
         break;
       }
-      case StyleTransformOperation::Tag::InterpolateMatrix: {
+      case eCSSKeyword_interpolatematrix: {
         Matrix4x4 matrix;
-        nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, op, aRefBox);
+        nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, array,
+                                                         aRefBox);
+        aFunctions.AppendElement(TransformMatrix(matrix));
+        break;
+      }
+      case eCSSKeyword_accumulatematrix: {
+        Matrix4x4 matrix;
+        nsStyleTransformMatrix::ProcessAccumulateMatrix(matrix, array, aRefBox);
         aFunctions.AppendElement(TransformMatrix(matrix));
         break;
       }
-      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()));
+      case eCSSKeyword_perspective: {
+        aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue()));
         break;
       }
       default:
-        MOZ_ASSERT_UNREACHABLE("Function not handled yet!");
-    }
-  }
+        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());
 }
 
 static TimingFunction ToTimingFunction(
     const Maybe<ComputedTimingFunction>& aCTF) {
   if (aCTF.isNothing()) {
     return TimingFunction(null_t());
   }
 
@@ -461,32 +466,44 @@ static void SetAnimatable(nsCSSPropertyI
           aFrame->Style()->GetVisitedDependentColor(&nsStyleColor::mColor);
       aAnimatable = aAnimationValue.GetColor(foreground);
       break;
     }
     case eCSSProperty_opacity:
       aAnimatable = aAnimationValue.GetOpacity();
       break;
     case eCSSProperty_rotate: {
-      aAnimatable = GetRotate(aAnimationValue.GetRotateProperty());
+      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);
       break;
     }
     case eCSSProperty_scale: {
-      aAnimatable = GetScale(aAnimationValue.GetScaleProperty());
+      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);
       break;
     }
     case eCSSProperty_translate: {
-      aAnimatable =
-          GetTranslate(aAnimationValue.GetTranslateProperty(), aRefBox);
+      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);
       break;
     }
     case eCSSProperty_transform: {
       aAnimatable = InfallibleTArray<TransformFunction>();
-      AddTransformFunctions(aAnimationValue.GetTransformProperty(), aRefBox,
-                            aAnimatable.get_ArrayOfTransformFunction());
+      RefPtr<const nsCSSValueSharedList> list =
+          aAnimationValue.GetTransformList();
+      AddTransformFunctions(list, aFrame, aRefBox, aAnimatable);
       break;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported property");
   }
 }
 
 enum class Send {
@@ -3719,17 +3736,18 @@ bool nsDisplayBackgroundImage::AppendBac
 
   if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
                               color)) {
     return false;
   }
 
   const nsStyleBorder* borderStyle = aFrame->StyleBorder();
   const nsStyleEffects* effectsStyle = aFrame->StyleEffects();
-  bool hasInsetShadow = effectsStyle->HasBoxShadowWithInset(true);
+  bool hasInsetShadow = effectsStyle->mBoxShadow &&
+                        effectsStyle->mBoxShadow->HasShadowWithInset(true);
   bool willPaintBorder = aAllowWillPaintBorderOptimization && !isThemed &&
                          !hasInsetShadow && borderStyle->HasBorder();
 
   nsPoint toRef = aBuilder->ToReferenceFrame(aFrame);
 
   // An auxiliary list is necessary in case we have background blending; if that
   // is the case, background items need to be wrapped by a blend container to
   // isolate blending to the background
@@ -5357,18 +5375,18 @@ bool nsDisplayBoxShadowOuter::ComputeVis
     return false;
   }
 
   mVisibleRegion.And(*aVisibleRegion, GetPaintRect());
   return true;
 }
 
 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() {
-  auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
-  if (shadows.IsEmpty()) {
+  nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
+  if (!shadows) {
     return false;
   }
 
   bool hasBorderRadius;
   bool nativeTheme =
       nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
 
   // We don't support native themed things yet like box shadows around
@@ -5413,36 +5431,34 @@ bool nsDisplayBoxShadowOuter::CreateWebR
     hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
                                                      mFrame, borderRadii);
   }
 
   // Everything here is in app units, change to device units.
   for (uint32_t i = 0; i < rects.Length(); ++i) {
     LayoutDeviceRect clipRect =
         LayoutDeviceRect::FromAppUnits(rects[i], appUnitsPerDevPixel);
-    auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
-    MOZ_ASSERT(!shadows.IsEmpty());
-
-    for (auto& shadow : Reversed(shadows)) {
-      if (shadow.inset) {
+    nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
+    MOZ_ASSERT(shadows);
+
+    for (uint32_t j = shadows->Length(); j > 0; j--) {
+      nsCSSShadowItem* shadow = shadows->ShadowAt(j - 1);
+      if (shadow->mInset) {
         continue;
       }
 
-      float blurRadius =
-          float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
+      float blurRadius = float(shadow->mRadius) / float(appUnitsPerDevPixel);
       gfx::Color shadowColor =
-          nsCSSRendering::GetShadowColor(shadow.base, mFrame, mOpacity);
+          nsCSSRendering::GetShadowColor(shadow, mFrame, mOpacity);
 
       // We don't move the shadow rect here since WR does it for us
       // Now translate everything to device pixels.
       const nsRect& shadowRect = frameRect;
       LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
-          nsPoint(shadow.base.horizontal.ToAppUnits(),
-                  shadow.base.vertical.ToAppUnits()),
-          appUnitsPerDevPixel);
+          nsPoint(shadow->mXOffset, shadow->mYOffset), appUnitsPerDevPixel);
 
       LayoutDeviceRect deviceBox =
           LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
       wr::LayoutRect deviceBoxRect = wr::ToRoundedLayoutRect(deviceBox);
       wr::LayoutRect deviceClipRect = wr::ToRoundedLayoutRect(clipRect);
 
       LayoutDeviceSize zeroSize;
       wr::BorderRadius borderRadius =
@@ -5450,18 +5466,17 @@ bool nsDisplayBoxShadowOuter::CreateWebR
       if (hasBorderRadius) {
         borderRadius = wr::ToBorderRadius(
             LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
             LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
             LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
             LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
       }
 
-      float spreadRadius =
-          float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
+      float spreadRadius = float(shadow->mSpread) / float(appUnitsPerDevPixel);
 
       aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
                              deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
                              wr::ToColorF(shadowColor), blurRadius,
                              spreadRadius, borderRadius,
                              wr::BoxShadowClipMode::Outset);
     }
   }
@@ -5514,18 +5529,18 @@ void nsDisplayBoxShadowInner::Paint(nsDi
     nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
     gfx->Restore();
   }
 }
 
 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
     const nsPoint& aReferenceOffset) {
-  auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
-  if (shadows.IsEmpty()) {
+  nsCSSShadowArray* shadows = aFrame->StyleEffects()->mBoxShadow;
+  if (!shadows) {
     // Means we don't have to paint anything
     return true;
   }
 
   bool hasBorderRadius;
   bool nativeTheme =
       nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
 
@@ -5546,55 +5561,55 @@ void nsDisplayBoxShadowInner::CreateInse
     return;
   }
 
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
 
   AutoTArray<nsRect, 10> rects;
   ComputeDisjointRectangles(aVisibleRegion, &rects);
 
-  auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
+  nsCSSShadowArray* shadows = aFrame->StyleEffects()->mBoxShadow;
 
   for (uint32_t i = 0; i < rects.Length(); ++i) {
     LayoutDeviceRect clipRect =
         LayoutDeviceRect::FromAppUnits(rects[i], appUnitsPerDevPixel);
 
-    for (auto& shadow : Reversed(shadows)) {
-      if (!shadow.inset) {
+    for (uint32_t i = shadows->Length(); i > 0; --i) {
+      nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
+      if (!shadowItem->mInset) {
         continue;
       }
 
       nsRect shadowRect =
           nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
       RectCornerRadii innerRadii;
       nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
 
       // Now translate everything to device pixels.
       LayoutDeviceRect deviceBoxRect =
           LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
       wr::LayoutRect deviceClipRect = wr::ToRoundedLayoutRect(clipRect);
       Color shadowColor =
-          nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
+          nsCSSRendering::GetShadowColor(shadowItem, aFrame, 1.0);
 
       LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
-          nsPoint(shadow.base.horizontal.ToAppUnits(),
-                  shadow.base.vertical.ToAppUnits()),
+          nsPoint(shadowItem->mXOffset, shadowItem->mYOffset),
           appUnitsPerDevPixel);
 
       float blurRadius =
-          float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
+          float(shadowItem->mRadius) / float(appUnitsPerDevPixel);
 
       wr::BorderRadius borderRadius = wr::ToBorderRadius(
           LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
           LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
           LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
           LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
       // NOTE: Any spread radius > 0 will render nothing. WR Bug.
       float spreadRadius =
-          float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
+          float(shadowItem->mSpread) / float(appUnitsPerDevPixel);
 
       aBuilder.PushBoxShadow(
           wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
           !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
           wr::ToLayoutVector2D(shadowOffset), wr::ToColorF(shadowColor),
           blurRadius, spreadRadius, borderRadius, wr::BoxShadowClipMode::Inset);
     }
   }
@@ -7728,21 +7743,19 @@ 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),
-      mTranslate(aFrame->StyleDisplay()->mTranslate),
-      mRotate(aFrame->StyleDisplay()->mRotate),
-      mScale(aFrame->StyleDisplay()->mScale),
-      mTransform(aFrame->StyleDisplay()->mTransform),
+      mIndividualTransformList(aFrame->StyleDisplay()->mIndividualTransform),
       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(
@@ -7800,18 +7813,23 @@ 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.mTranslate, aProperties.mRotate, aProperties.mScale,
-        aProperties.mMotion, aProperties.mTransform, refBox, aAppUnitsPerPixel);
+        aProperties.mIndividualTransformList
+            ? aProperties.mIndividualTransformList->mHead
+            : nullptr,
+        aProperties.mMotion,
+        aProperties.mTransformList ? aProperties.mTransformList->mHead
+                                   : nullptr,
+        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);
   }
 
@@ -8938,17 +8956,17 @@ nsDisplayText::nsDisplayText(nsDisplayLi
 
 bool nsDisplayText::CanApplyOpacity() const {
   if (IsSelected()) {
     return false;
   }
 
   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
   const nsStyleText* textStyle = f->StyleText();
-  if (textStyle->HasTextShadow()) {
+  if (textStyle->mTextShadow) {
     return false;
   }
 
   nsTextFrame::TextDecorations decorations;
   f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
                         decorations);
   if (decorations.HasDecorationLines()) {
     return false;
@@ -8986,17 +9004,17 @@ bool nsDisplayText::CreateWebRenderComma
           .ToUnknownPoint();
 
   // Clipping the bounds to the PaintRect (factoring in what's covered by parent
   // frames) let's us early reject a bunch of things, but it can produce
   // incorrect results for shadows, because they can translate things back into
   // view. Also if we're selected we might have some shadows from the
   // ::selected and ::inctive-selected pseudo-selectors. So don't do this
   // optimization if we have shadows or a selection.
-  if (!(IsSelected() || f->StyleText()->HasTextShadow())) {
+  if (!(IsSelected() || f->StyleText()->GetTextShadow())) {
     nsRect visible = GetPaintRect();
     visible.Inflate(3 * appUnitsPerDevPixel);
     bounds = bounds.Intersect(visible);
   }
 
   RefPtr<gfxContext> textDrawer = aBuilder.GetTextContext(
       aResources, aSc, aManager, this, bounds, deviceOffset);
 
@@ -9917,28 +9935,33 @@ bool nsDisplayFilters::CreateWebRenderCS
             ClampStdDeviation(NSAppUnitsToFloatPixels(
                 filter.GetFilterParameter().GetCoordValue(),
                 appUnitsPerDevPixel))));
         break;
       }
       case NS_STYLE_FILTER_DROP_SHADOW: {
         float appUnitsPerDevPixel =
             mFrame->PresContext()->AppUnitsPerDevPixel();
-        const StyleSimpleShadow& shadow = filter.GetDropShadow();
-        nscolor color = shadow.color.CalcColor(mFrame);
+        nsCSSShadowArray* shadows = filter.GetDropShadow();
+        if (!shadows || shadows->Length() != 1) {
+          MOZ_ASSERT_UNREACHABLE(
+              "Exactly one drop shadow should have been "
+              "parsed.");
+          return false;
+        }
+
+        nsCSSShadowItem* shadow = shadows->ShadowAt(0);
+        nscolor color = shadow->mColor.CalcColor(mFrame);
 
         wr::Shadow wrShadow;
         wrShadow.offset = {
-            NSAppUnitsToFloatPixels(shadow.horizontal.ToAppUnits(),
-                                    appUnitsPerDevPixel),
-            NSAppUnitsToFloatPixels(shadow.vertical.ToAppUnits(),
-                                    appUnitsPerDevPixel)};
+          NSAppUnitsToFloatPixels(shadow->mXOffset, appUnitsPerDevPixel),
+          NSAppUnitsToFloatPixels(shadow->mYOffset, appUnitsPerDevPixel)};
         wrShadow.blur_radius =
-            NSAppUnitsToFloatPixels(shadow.blur.ToAppUnits(),
-                                    appUnitsPerDevPixel);
+          NSAppUnitsToFloatPixels(shadow->mRadius, appUnitsPerDevPixel);
         wrShadow.color = {NS_GET_R(color) / 255.0f, NS_GET_G(color) / 255.0f,
                           NS_GET_B(color) / 255.0f, NS_GET_A(color) / 255.0f};
         auto filterOp = wr::FilterOp::DropShadow(wrShadow);
 
         wrFilters.filters.AppendElement(filterOp);
         break;
       }
       default:
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -6874,50 +6874,44 @@ 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 MOZ_STACK_CLASS FrameTransformProperties {
+  struct 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(const mozilla::StyleTranslate& aTranslate,
-                             const mozilla::StyleRotate& aRotate,
-                             const mozilla::StyleScale& aScale,
-                             const mozilla::StyleTransform& aTransform,
-                             const Point3D& aToTransformOrigin)
+    FrameTransformProperties(
+        RefPtr<const nsCSSValueSharedList>&& aIndividualTransform,
+        RefPtr<const nsCSSValueSharedList>&& aTransformList,
+        const Point3D& aToTransformOrigin)
         : mFrame(nullptr),
-          mTranslate(aTranslate),
-          mRotate(aRotate),
-          mScale(aScale),
-          mTransform(aTransform),
+          mIndividualTransformList(std::move(aIndividualTransform)),
+          mTransformList(std::move(aTransformList)),
           mToTransformOrigin(aToTransformOrigin) {}
 
     bool HasTransform() const {
-      return !mTranslate.IsNone() || !mRotate.IsNone() || !mScale.IsNone() ||
-             !mTransform.IsNone() || mMotion.isSome();
+      return mIndividualTransformList || mTransformList || mMotion.isSome();
     }
 
     const nsIFrame* mFrame;
-    const mozilla::StyleTranslate& mTranslate;
-    const mozilla::StyleRotate& mRotate;
-    const mozilla::StyleScale& mScale;
-    const mozilla::StyleTransform& mTransform;
+    const RefPtr<const nsCSSValueSharedList> mIndividualTransformList;
     const mozilla::Maybe<mozilla::MotionPathData> mMotion;
+    const RefPtr<const nsCSSValueSharedList> mTransformList;
     const Point3D mToTransformOrigin;
   };
 
   /**
-   * Given a frame with the transform property or an SVG transform,
+   * Given a frame with the -moz-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/CounterStyleManager.cpp
+++ b/layout/style/CounterStyleManager.cpp
@@ -19,17 +19,20 @@
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsUnicodeProperties.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoStyleSet.h"
 
 namespace mozilla {
 
-using AdditiveSymbol = StyleAdditiveSymbol;
+struct AdditiveSymbol {
+  CounterValue weight;
+  nsString symbol;
+};
 
 struct NegativeType {
   nsString before, after;
 };
 
 struct PadType {
   int32_t width;
   nsString symbol;
@@ -41,38 +44,38 @@ struct PadType {
 // than this, the whole counter text will be dropped as well.
 // The spec requires user agents to support at least 60 Unicode code-
 // points for counter text. However, this constant only limits the
 // length in 16-bit units. So it has to be at least 120, since code-
 // points outside the BMP will need 2 16-bit units.
 #define LENGTH_LIMIT 150
 
 static bool GetCyclicCounterText(CounterValue aOrdinal, nsAString& aResult,
-                                 Span<const nsString> aSymbols) {
+                                 const nsTArray<nsString>& aSymbols) {
   MOZ_ASSERT(aSymbols.Length() >= 1, "No symbol available for cyclic counter.");
   auto n = aSymbols.Length();
   CounterValue index = (aOrdinal - 1) % n;
   aResult = aSymbols[index >= 0 ? index : index + n];
   return true;
 }
 
 static bool GetFixedCounterText(CounterValue aOrdinal, nsAString& aResult,
                                 CounterValue aStart,
-                                Span<const nsString> aSymbols) {
+                                const nsTArray<nsString>& aSymbols) {
   CounterValue index = aOrdinal - aStart;
   if (index >= 0 && index < CounterValue(aSymbols.Length())) {
     aResult = aSymbols[index];
     return true;
   } else {
     return false;
   }
 }
 
 static bool GetSymbolicCounterText(CounterValue aOrdinal, nsAString& aResult,
-                                   Span<const nsString> aSymbols) {
+                                   const nsTArray<nsString>& aSymbols) {
   MOZ_ASSERT(aSymbols.Length() >= 1,
              "No symbol available for symbolic counter.");
   MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal.");
   if (aOrdinal == 0) {
     return false;
   }
 
   aResult.Truncate();
@@ -88,17 +91,17 @@ static bool GetSymbolicCounterText(Count
     for (size_t i = 0; i < len; ++i) {
       aResult.Append(symbol);
     }
   }
   return true;
 }
 
 static bool GetAlphabeticCounterText(CounterValue aOrdinal, nsAString& aResult,
-                                     Span<const nsString> aSymbols) {
+                                     const nsTArray<nsString>& aSymbols) {
   MOZ_ASSERT(aSymbols.Length() >= 2, "Too few symbols for alphabetic counter.");
   MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal.");
   if (aOrdinal == 0) {
     return false;
   }
 
   auto n = aSymbols.Length();
   // The precise length of this array should be
@@ -114,17 +117,17 @@ static bool GetAlphabeticCounterText(Cou
   aResult.Truncate();
   for (auto i = indexes.Length(); i > 0; --i) {
     aResult.Append(aSymbols[indexes[i - 1]]);
   }
   return true;
 }
 
 static bool GetNumericCounterText(CounterValue aOrdinal, nsAString& aResult,
-                                  Span<const nsString> aSymbols) {
+                                  const nsTArray<nsString>& aSymbols) {
   MOZ_ASSERT(aSymbols.Length() >= 2, "Too few symbols for numeric counter.");
   MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal.");
 
   if (aOrdinal == 0) {
     aResult = aSymbols[0];
     return true;
   }
 
@@ -138,21 +141,21 @@ static bool GetNumericCounterText(Counte
   aResult.Truncate();
   for (auto i = indexes.Length(); i > 0; --i) {
     aResult.Append(aSymbols[indexes[i - 1]]);
   }
   return true;
 }
 
 static bool GetAdditiveCounterText(CounterValue aOrdinal, nsAString& aResult,
-                                   Span<const AdditiveSymbol> aSymbols) {
+                                   const nsTArray<AdditiveSymbol>& aSymbols) {
   MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal.");
 
   if (aOrdinal == 0) {
-    const AdditiveSymbol& last = aSymbols[aSymbols.Length() - 1];
+    const AdditiveSymbol& last = aSymbols.LastElement();
     if (last.weight == 0) {
       aResult = last.symbol;
       return true;
     }
     return false;
   }
 
   aResult.Truncate();
@@ -984,18 +987,24 @@ class CustomCounterStyle final : public 
     PresShell* presShell = mManager->PresContext()->PresShell();
     this->~CustomCounterStyle();
     presShell->FreeByObjectID(eArenaObjectID_CustomCounterStyle, this);
   }
 
  private:
   ~CustomCounterStyle() {}
 
-  Span<const nsString> GetSymbols();
-  Span<const AdditiveSymbol> GetAdditiveSymbols();
+  nsCSSValue GetDesc(nsCSSCounterDesc aDesc) const {
+    nsCSSValue value;
+    Servo_CounterStyleRule_GetDescriptor(mRule, aDesc, &value);
+    return value;
+  }
+
+  const nsTArray<nsString>& GetSymbols();
+  const nsTArray<AdditiveSymbol>& GetAdditiveSymbols();
 
   // The speak-as values of counter styles may form a loop, and the
   // loops may have complex interaction with the loop formed by
   // extending. To solve this problem, the computation of speak-as is
   // divided into two phases:
   // 1. figure out the raw value, by ComputeRawSpeakAs, and
   // 2. eliminate loop, by ComputeSpeakAs.
   // See comments before the definitions of these methods for details.
@@ -1031,18 +1040,18 @@ class CustomCounterStyle final : public 
     FLAG_PREFIX_INITED = 1 << 5,
     FLAG_SUFFIX_INITED = 1 << 6,
     FLAG_PAD_INITED = 1 << 7,
     FLAG_SPEAKAS_INITED = 1 << 8,
   };
   uint16_t mFlags;
 
   // Fields below will be initialized when necessary.
-  StyleOwnedSlice<nsString> mSymbols;
-  StyleOwnedSlice<AdditiveSymbol> mAdditiveSymbols;
+  nsTArray<nsString> mSymbols;
+  nsTArray<AdditiveSymbol> mAdditiveSymbols;
   NegativeType mNegative;
   nsString mPrefix, mSuffix;
   PadType mPad;
 
   // CounterStyleManager will guarantee that none of the pointers below
   // refers to a freed CounterStyle. There are two possible cases where
   // the manager will release its reference to a CounterStyle: 1. the
   // manager itself is released, 2. a rule is invalidated. In the first
@@ -1088,38 +1097,40 @@ void CustomCounterStyle::ResetDependentD
   }
 }
 
 /* virtual */
 void CustomCounterStyle::GetPrefix(nsAString& aResult) {
   if (!(mFlags & FLAG_PREFIX_INITED)) {
     mFlags |= FLAG_PREFIX_INITED;
 
-    if (!Servo_CounterStyleRule_GetPrefix(mRule, &mPrefix)) {
-      if (IsExtendsSystem()) {
-        GetExtends()->GetPrefix(mPrefix);
-      } else {
-        mPrefix.Truncate();
-      }
+    nsCSSValue value = GetDesc(eCSSCounterDesc_Prefix);
+    if (value.UnitHasStringValue()) {
+      value.GetStringValue(mPrefix);
+    } else if (IsExtendsSystem()) {
+      GetExtends()->GetPrefix(mPrefix);
+    } else {
+      mPrefix.Truncate();
     }
   }
   aResult = mPrefix;
 }
 
 /* virtual */
 void CustomCounterStyle::GetSuffix(nsAString& aResult) {
   if (!(mFlags & FLAG_SUFFIX_INITED)) {
     mFlags |= FLAG_SUFFIX_INITED;
 
-    if (!Servo_CounterStyleRule_GetSuffix(mRule, &mSuffix)) {
-      if (IsExtendsSystem()) {
-        GetExtends()->GetSuffix(mSuffix);
-      } else {
-        mSuffix.AssignLiteral(u". ");
-      }
+    nsCSSValue value = GetDesc(eCSSCounterDesc_Suffix);
+    if (value.UnitHasStringValue()) {
+      value.GetStringValue(mSuffix);
+    } else if (IsExtendsSystem()) {
+      GetExtends()->GetSuffix(mSuffix);
+    } else {
+      mSuffix.AssignLiteral(u". ");
     }
   }
   aResult = mSuffix;
 }
 
 /* virtual */
 void CustomCounterStyle::GetSpokenCounterText(CounterValue aOrdinal,
                                               WritingMode aWritingMode,
@@ -1148,52 +1159,66 @@ bool CustomCounterStyle::IsBullet() {
       return false;
   }
 }
 
 /* virtual */
 void CustomCounterStyle::GetNegative(NegativeType& aResult) {
   if (!(mFlags & FLAG_NEGATIVE_INITED)) {
     mFlags |= FLAG_NEGATIVE_INITED;
-    if (!Servo_CounterStyleRule_GetNegative(mRule,
-                                            &mNegative.before,
-                                            &mNegative.after)) {
-      if (IsExtendsSystem()) {
-        GetExtends()->GetNegative(mNegative);
-      } else {
-        mNegative.before.AssignLiteral(u"-");
+    nsCSSValue value = GetDesc(eCSSCounterDesc_Negative);
+    switch (value.GetUnit()) {
+      case eCSSUnit_Ident:
+      case eCSSUnit_String:
+        value.GetStringValue(mNegative.before);
         mNegative.after.Truncate();
+        break;
+      case eCSSUnit_Pair: {
+        const nsCSSValuePair& pair = value.GetPairValue();
+        pair.mXValue.GetStringValue(mNegative.before);
+        pair.mYValue.GetStringValue(mNegative.after);
+        break;
+      }
+      default: {
+        if (IsExtendsSystem()) {
+          GetExtends()->GetNegative(mNegative);
+        } else {
+          mNegative.before.AssignLiteral(u"-");
+          mNegative.after.Truncate();
+        }
       }
     }
   }
   aResult = mNegative;
 }
 
 static inline bool IsRangeValueInfinite(const nsCSSValue& aValue) {
   return aValue.GetUnit() == eCSSUnit_Enumerated &&
          aValue.GetIntValue() == NS_STYLE_COUNTER_RANGE_INFINITE;
 }
 
 /* virtual */
 bool CustomCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) {
-  auto inRange = Servo_CounterStyleRule_IsInRange(mRule, aOrdinal);
-  switch (inRange) {
-    case StyleIsOrdinalInRange::InRange:
-      return true;
-    case StyleIsOrdinalInRange::NotInRange:
-      return false;
-    case StyleIsOrdinalInRange::NoOrdinalSpecified:
-      if (IsExtendsSystem()) {
-        return GetExtends()->IsOrdinalInRange(aOrdinal);
+  nsCSSValue value = GetDesc(eCSSCounterDesc_Range);
+  if (value.GetUnit() == eCSSUnit_PairList) {
+    for (const nsCSSValuePairList* item = value.GetPairListValue();
+         item != nullptr; item = item->mNext) {
+      const nsCSSValue& lowerBound = item->mXValue;
+      const nsCSSValue& upperBound = item->mYValue;
+      if ((IsRangeValueInfinite(lowerBound) ||
+           aOrdinal >= lowerBound.GetIntValue()) &&
+          (IsRangeValueInfinite(upperBound) ||
+           aOrdinal <= upperBound.GetIntValue())) {
+        return true;
       }
-      break;
-    case StyleIsOrdinalInRange::Auto:
-      break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unkown result from IsInRange?");
+    }
+    return false;
+  } else if (IsExtendsSystem() && value.GetUnit() == eCSSUnit_None) {
+    // Only use the range of extended style when 'range' is not specified.
+    return GetExtends()->IsOrdinalInRange(aOrdinal);
   }
   return IsOrdinalInAutoRange(aOrdinal);
 }
 
 /* virtual */
 bool CustomCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) {
   switch (mSystem) {
     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
@@ -1212,23 +1237,26 @@ bool CustomCounterStyle::IsOrdinalInAuto
       return false;
   }
 }
 
 /* virtual */
 void CustomCounterStyle::GetPad(PadType& aResult) {
   if (!(mFlags & FLAG_PAD_INITED)) {
     mFlags |= FLAG_PAD_INITED;
-    if (!Servo_CounterStyleRule_GetPad(mRule, &mPad.width, &mPad.symbol)) {
-      if (IsExtendsSystem()) {
-        GetExtends()->GetPad(mPad);
-      } else {
-        mPad.width = 0;
-        mPad.symbol.Truncate();
-      }
+    nsCSSValue value = GetDesc(eCSSCounterDesc_Pad);
+    if (value.GetUnit() == eCSSUnit_Pair) {
+      const nsCSSValuePair& pair = value.GetPairValue();
+      mPad.width = pair.mXValue.GetIntValue();
+      pair.mYValue.GetStringValue(mPad.symbol);
+    } else if (IsExtendsSystem()) {
+      GetExtends()->GetPad(mPad);
+    } else {
+      mPad.width = 0;
+      mPad.symbol.Truncate();
     }
   }
   aResult = mPad;
 }
 
 /* virtual */
 CounterStyle* CustomCounterStyle::GetFallback() {
   if (!mFallback) {
@@ -1294,28 +1322,41 @@ bool CustomCounterStyle::GetInitialCount
       return GetExtendsRoot()->GetInitialCounterText(aOrdinal, aWritingMode,
                                                      aResult, aIsRTL);
     default:
       MOZ_ASSERT_UNREACHABLE("Invalid system.");
       return false;
   }
 }
 
-Span<const nsString> CustomCounterStyle::GetSymbols() {
+const nsTArray<nsString>& CustomCounterStyle::GetSymbols() {
   if (mSymbols.IsEmpty()) {
-    Servo_CounterStyleRule_GetSymbols(mRule, &mSymbols);
+    nsCSSValue values = GetDesc(eCSSCounterDesc_Symbols);
+    for (const nsCSSValueList* item = values.GetListValue(); item;
+         item = item->mNext) {
+      nsString* symbol = mSymbols.AppendElement();
+      item->mValue.GetStringValue(*symbol);
+    }
+    mSymbols.Compact();
   }
-  return mSymbols.AsSpan();
+  return mSymbols;
 }
 
-Span<const AdditiveSymbol> CustomCounterStyle::GetAdditiveSymbols() {
+const nsTArray<AdditiveSymbol>& CustomCounterStyle::GetAdditiveSymbols() {
   if (mAdditiveSymbols.IsEmpty()) {
-    Servo_CounterStyleRule_GetAdditiveSymbols(mRule, &mAdditiveSymbols);
+    nsCSSValue values = GetDesc(eCSSCounterDesc_AdditiveSymbols);
+    for (const nsCSSValuePairList* item = values.GetPairListValue(); item;
+         item = item->mNext) {
+      AdditiveSymbol* symbol = mAdditiveSymbols.AppendElement();
+      symbol->weight = item->mXValue.GetIntValue();
+      item->mYValue.GetStringValue(symbol->symbol);
+    }
+    mAdditiveSymbols.Compact();
   }
-  return mAdditiveSymbols.AsSpan();
+  return mAdditiveSymbols;
 }
 
 // This method is used to provide the computed value for 'auto'.
 uint8_t CustomCounterStyle::GetSpeakAsAutoValue() {
   uint8_t system = mSystem;
   if (IsExtendsSystem()) {
     CounterStyle* root = GetExtendsRoot();
     if (!root->IsCustomStyle()) {
@@ -1333,35 +1374,29 @@ uint8_t CustomCounterStyle::GetSpeakAsAu
 // out the raw value. To keep things clear, this method is designed to
 // have no side effects (but functions it calls may still affect other
 // fields in the style.)
 void CustomCounterStyle::ComputeRawSpeakAs(uint8_t& aSpeakAs,
                                            CounterStyle*& aSpeakAsCounter) {
   NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_INITED),
                "ComputeRawSpeakAs is called with speak-as inited.");
 
-  auto speakAs = Servo_CounterStyleRule_GetSpeakAs(mRule);
-  switch (speakAs.tag) {
-    case StyleCounterSpeakAs::Tag::Auto:
+  nsCSSValue value = GetDesc(eCSSCounterDesc_SpeakAs);
+  switch (value.GetUnit()) {
+    case eCSSUnit_Auto:
       aSpeakAs = GetSpeakAsAutoValue();
       break;
-    case StyleCounterSpeakAs::Tag::Bullets:
-      aSpeakAs = NS_STYLE_COUNTER_SPEAKAS_BULLETS;
-      break;
-    case StyleCounterSpeakAs::Tag::Numbers:
-      aSpeakAs = NS_STYLE_COUNTER_SPEAKAS_NUMBERS;
+    case eCSSUnit_Enumerated:
+      aSpeakAs = value.GetIntValue();
       break;
-    case StyleCounterSpeakAs::Tag::Words:
-      aSpeakAs = NS_STYLE_COUNTER_SPEAKAS_WORDS;
+    case eCSSUnit_AtomIdent:
+      aSpeakAs = NS_STYLE_COUNTER_SPEAKAS_OTHER;
+      aSpeakAsCounter = mManager->ResolveCounterStyle(value.GetAtomValue());
       break;
-    case StyleCounterSpeakAs::Tag::Ident:
-      aSpeakAs = NS_STYLE_COUNTER_SPEAKAS_OTHER;
-      aSpeakAsCounter = mManager->ResolveCounterStyle(speakAs.AsIdent());
-      break;
-    case StyleCounterSpeakAs::Tag::None: {
+    case eCSSUnit_Null: {
       if (!IsExtendsSystem()) {
         aSpeakAs = GetSpeakAsAutoValue();
       } else {
         CounterStyle* extended = GetExtends();
         if (!extended->IsCustomStyle()) {
           // It is safe to call GetSpeakAs on non-custom style.
           aSpeakAs = extended->GetSpeakAs();
         } else {
--- a/layout/style/CounterStyleManager.h
+++ b/layout/style/CounterStyleManager.h
@@ -113,17 +113,17 @@ class AnonymousCounterStyle final : publ
   virtual bool GetInitialCounterText(CounterValue aOrdinal,
                                      WritingMode aWritingMode,
                                      nsAString& aResult, bool& aIsRTL) override;
 
   virtual AnonymousCounterStyle* AsAnonymous() override { return this; }
 
   bool IsSingleString() const { return mSingleString; }
   uint8_t GetSystem() const { return mSystem; }
-  Span<const nsString> GetSymbols() const { return MakeSpan(mSymbols); }
+  const nsTArray<nsString>& GetSymbols() const { return mSymbols; }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousCounterStyle)
 
  private:
   ~AnonymousCounterStyle() {}
 
   bool mSingleString;
   uint8_t mSystem;
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1422,16 +1422,31 @@ void Gecko_EnsureStyleAnimationArrayLeng
   EnsureStyleAutoArrayLength(base, aLen);
 }
 
 void Gecko_EnsureStyleTransitionArrayLength(void* aArray, size_t aLen) {
   auto base = reinterpret_cast<nsStyleAutoArray<StyleTransition>*>(aArray);
   EnsureStyleAutoArrayLength(base, aLen);
 }
 
+void Gecko_ClearWillChange(nsStyleDisplay* aDisplay, size_t aLength) {
+  aDisplay->mWillChange.Clear();
+  aDisplay->mWillChange.SetCapacity(aLength);
+}
+
+void Gecko_AppendWillChange(nsStyleDisplay* aDisplay, nsAtom* aAtom) {
+  aDisplay->mWillChange.AppendElement(aAtom);
+}
+
+void Gecko_CopyWillChangeFrom(nsStyleDisplay* aDest,
+                              const nsStyleDisplay* aSrc) {
+  aDest->mWillChange.Clear();
+  aDest->mWillChange.AppendElements(aSrc->mWillChange);
+}
+
 enum class KeyframeSearchDirection {
   Forwards,
   Backwards,
 };
 
 enum class KeyframeInsertPosition {
   Prepend,
   LastForOffset,
@@ -1599,16 +1614,28 @@ void Gecko_nsStyleSVG_SetDashArrayLength
   aSvg->mStrokeDasharray.Clear();
   aSvg->mStrokeDasharray.SetLength(aLen);
 }
 
 void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* aDst, const nsStyleSVG* aSrc) {
   aDst->mStrokeDasharray = aSrc->mStrokeDasharray;
 }
 
+void Gecko_nsStyleSVG_SetContextPropertiesLength(nsStyleSVG* aSvg,
+                                                 uint32_t aLen) {
+  aSvg->mContextProps.Clear();
+  aSvg->mContextProps.SetLength(aLen);
+}
+
+void Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* aDst,
+                                            const nsStyleSVG* aSrc) {
+  aDst->mContextProps = aSrc->mContextProps;
+  aDst->mContextPropsBits = aSrc->mContextPropsBits;
+}
+
 URLValue* Gecko_URLValue_Create(StyleStrong<RawServoCssUrlData> aCssUrl,
                                 CORSMode aCORSMode) {
   RefPtr<URLValue> url = new URLValue(aCssUrl.Consume(), aCORSMode);
   return url.forget().take();
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(GeckoURLValueMallocSizeOf)
 
@@ -1685,16 +1712,176 @@ 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);
 
+nsCSSShadowArray* Gecko_NewCSSShadowArray(uint32_t aLen) {
+  RefPtr<nsCSSShadowArray> arr = new (aLen) nsCSSShadowArray(aLen);
+  return arr.forget().take();
+}
+
+NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray);
+
+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();
+}
+
+void Gecko_CSSValue_SetKeyword(nsCSSValue* aCSSValue, nsCSSKeyword aKeyword) {
+  aCSSValue->SetEnumValue(aKeyword);
+}
+
+nsCSSKeyword Gecko_CSSValue_GetKeyword(const nsCSSValue* aCSSValue) {
+  return aCSSValue->GetKeywordValue();
+}
+
+void Gecko_CSSValue_SetPercentage(nsCSSValue* aCSSValue, float aPercent) {
+  aCSSValue->SetPercentValue(aPercent);
+}
+
+float Gecko_CSSValue_GetPercentage(const nsCSSValue* aCSSValue) {
+  return aCSSValue->GetPercentValue();
+}
+
+void Gecko_CSSValue_SetPixelLength(nsCSSValue* aCSSValue, float aLen) {
+  MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null ||
+             aCSSValue->GetUnit() == eCSSUnit_Pixel);
+  aCSSValue->SetFloatValue(aLen, eCSSUnit_Pixel);
+}
+
+void Gecko_CSSValue_SetCalc(nsCSSValue* aCSSValue,
+                            nsStyleCoord::CalcValue aCalc) {
+  aCSSValue->SetCalcValue(aCalc);
+}
+
+nsStyleCoord::CalcValue Gecko_CSSValue_GetCalc(const nsCSSValue* aCSSValue) {
+  return aCSSValue->GetCalcValue();
+}
+
+void Gecko_CSSValue_SetFunction(nsCSSValue* aCSSValue, int32_t aLen) {
+  nsCSSValue::Array* arr = nsCSSValue::Array::Create(aLen);
+  aCSSValue->SetArrayValue(arr, eCSSUnit_Function);
+}
+
+void Gecko_CSSValue_SetString(nsCSSValue* aCSSValue, const uint8_t* aString,
+                              uint32_t aLength, nsCSSUnit aUnit) {
+  MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null);
+  nsString string;
+  nsDependentCSubstring slice(reinterpret_cast<const char*>(aString), aLength);
+  AppendUTF8toUTF16(slice, string);
+  aCSSValue->SetStringValue(string, aUnit);
+}
+
+void Gecko_CSSValue_SetStringFromAtom(nsCSSValue* aCSSValue, nsAtom* aAtom,
+                                      nsCSSUnit aUnit) {
+  aCSSValue->SetStringValue(nsDependentAtomString(aAtom), aUnit);
+}
+
+void Gecko_CSSValue_SetAtomIdent(nsCSSValue* aCSSValue, nsAtom* aAtom) {
+  aCSSValue->SetAtomIdentValue(already_AddRefed<nsAtom>(aAtom));
+}
+
+void Gecko_CSSValue_SetArray(nsCSSValue* aCSSValue, int32_t aLength) {
+  MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null);
+  RefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(aLength);
+  aCSSValue->SetArrayValue(array, eCSSUnit_Array);
+}
+
+void Gecko_CSSValue_SetInt(nsCSSValue* aCSSValue, int32_t aInteger,
+                           nsCSSUnit aUnit) {
+  aCSSValue->SetIntValue(aInteger, aUnit);
+}
+
+void Gecko_CSSValue_SetFloat(nsCSSValue* aCSSValue, float aValue,
+                             nsCSSUnit aUnit) {
+  aCSSValue->SetFloatValue(aValue, aUnit);
+}
+
+nsCSSValue* Gecko_CSSValue_GetArrayItem(nsCSSValue* aCSSValue, int32_t aIndex) {
+  return &aCSSValue->GetArrayValue()->Item(aIndex);
+}
+
+const nsCSSValue* Gecko_CSSValue_GetArrayItemConst(const nsCSSValue* aCSSValue,
+                                                   int32_t aIndex) {
+  return &aCSSValue->GetArrayValue()->Item(aIndex);
+}
+
+void Gecko_CSSValue_SetPair(nsCSSValue* aCSSValue, const nsCSSValue* aXValue,
+                            const nsCSSValue* aYValue) {
+  MOZ_ASSERT(NS_IsMainThread());
+  aCSSValue->SetPairValue(*aXValue, *aYValue);
+}
+
+void Gecko_CSSValue_SetList(nsCSSValue* aCSSValue, uint32_t aLen) {
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCSSValueList* item = aCSSValue->SetListValue();
+  for (uint32_t i = 1; i < aLen; ++i) {
+    item->mNext = new nsCSSValueList;
+    item = item->mNext;
+  }
+}
+
+void Gecko_CSSValue_SetPairList(nsCSSValue* aCSSValue, uint32_t aLen) {
+  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,
                                     const nsStyleFont* aSource) {
   aFont->mLanguage = aSource->mLanguage;
@@ -1954,16 +2141,18 @@ 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
@@ -13,16 +13,17 @@
 
 #include "mozilla/ServoTypes.h"
 #include "mozilla/ServoBindingTypes.h"
 #include "mozilla/css/DocumentMatchingFunction.h"
 #include "mozilla/css/SheetLoadData.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/ComputedTimingFunction.h"
 #include "mozilla/PreferenceSheet.h"
+#include "nsCSSValue.h"
 #include "nsStyleStruct.h"
 
 class nsAtom;
 class nsIURI;
 class nsSimpleContentList;
 struct nsFont;
 
 namespace mozilla {
@@ -463,16 +464,19 @@ void Gecko_CopyCounterSetsFrom(nsStyleCo
 void Gecko_CopyCounterIncrementsFrom(nsStyleContent* content,
                                      const nsStyleContent* other);
 
 void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len,
                                    nsStyleImageLayers::LayerType layer_type);
 
 void Gecko_EnsureStyleAnimationArrayLength(void* array, size_t len);
 void Gecko_EnsureStyleTransitionArrayLength(void* array, size_t len);
+void Gecko_ClearWillChange(nsStyleDisplay* display, size_t length);
+void Gecko_AppendWillChange(nsStyleDisplay* display, nsAtom* atom);
+void Gecko_CopyWillChangeFrom(nsStyleDisplay* dest, const nsStyleDisplay* src);
 
 // Searches from the beginning of |keyframes| for a Keyframe object with the
 // specified offset and timing function. If none is found, a new Keyframe object
 // with the specified |offset| and |timingFunction| will be prepended to
 // |keyframes|.
 //
 // @param keyframes  An array of Keyframe objects, sorted by offset.
 //                   The first Keyframe in the array, if any, MUST have an
@@ -580,16 +584,76 @@ 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);
 
+nsCSSShadowArray* Gecko_NewCSSShadowArray(uint32_t len);
+
+NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray);
+
+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*);
+float Gecko_CSSValue_GetNumber(const nsCSSValue* css_value);
+float Gecko_CSSValue_GetPercentage(const nsCSSValue* css_value);
+nsStyleCoord::CalcValue Gecko_CSSValue_GetCalc(const nsCSSValue* aCSSValue);
+void Gecko_CSSValue_SetNumber(nsCSSValue* css_value, float number);
+
+void Gecko_CSSValue_SetKeyword(nsCSSValue* css_value, nsCSSKeyword keyword);
+
+void Gecko_CSSValue_SetPercentage(nsCSSValue* css_value, float percent);
+
+void Gecko_CSSValue_SetPixelLength(nsCSSValue* aCSSValue, float aLen);
+
+void Gecko_CSSValue_SetCalc(nsCSSValue* css_value,
+                            nsStyleCoord::CalcValue calc);
+
+void Gecko_CSSValue_SetFunction(nsCSSValue* css_value, int32_t len);
+
+void Gecko_CSSValue_SetString(nsCSSValue* css_value, const uint8_t* string,
+                              uint32_t len, nsCSSUnit unit);
+
+void Gecko_CSSValue_SetStringFromAtom(nsCSSValue* css_value, nsAtom* atom,
+                                      nsCSSUnit unit);
+
+// Take an addrefed nsAtom and set it to the nsCSSValue
+void Gecko_CSSValue_SetAtomIdent(nsCSSValue* css_value, nsAtom* atom);
+void Gecko_CSSValue_SetArray(nsCSSValue* css_value, int32_t len);
+
+void Gecko_CSSValue_SetInt(nsCSSValue* css_value, int32_t integer,
+                           nsCSSUnit unit);
+
+void Gecko_CSSValue_SetFloat(nsCSSValue* css_value, float value,
+                             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.h
+++ b/layout/style/ServoBindings.h
@@ -54,16 +54,22 @@ GROUP_RULE_FUNCS(Supports)
 BASIC_RULE_FUNCS(FontFeatureValues)
 BASIC_RULE_FUNCS(FontFace)
 BASIC_RULE_FUNCS(CounterStyle)
 
 #undef GROUP_RULE_FUNCS
 #undef BASIC_RULE_FUNCS
 #undef BASIC_RULE_FUNCS_WITHOUT_GETTER
 
+// TODO(emilio): Move CounterStyleRule outside of nsCSSValue, then remove this
+// while at it.
+void Servo_CounterStyleRule_GetDescriptor(const RawServoCounterStyleRule* rule,
+                                          nsCSSCounterDesc desc,
+                                          nsCSSValue* result);
+
 void Servo_CounterStyleRule_GetDescriptorCssText(
     const RawServoCounterStyleRule* rule, nsCSSCounterDesc desc,
     nsAString* result);
 
 bool Servo_CounterStyleRule_SetDescriptor(const RawServoCounterStyleRule* rule,
                                           nsCSSCounterDesc desc,
                                           const nsACString* value);
 
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -47,18 +47,16 @@ 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",
@@ -457,17 +455,17 @@ cbindgen-types = [
     { gecko = "StyleGenericZIndex", servo = "values::generics::position::ZIndex" },
     { gecko = "StyleTransformOrigin", servo = "values::computed::TransformOrigin" },
     { gecko = "StyleGenericBorderRadius", servo = "values::generics::border::BorderRadius" },
     { gecko = "StyleLetterSpacing", servo = "values::computed::text::LetterSpacing" },
     { gecko = "StyleGenericLineHeight", servo = "values::generics::text::LineHeight" },
     { gecko = "StyleContain", servo = "values::computed::Contain" },
     { gecko = "StyleRestyleHint", servo = "invalidation::element::restyle_hints::RestyleHint" },
     { gecko = "StyleTouchAction", servo = "values::computed::TouchAction" },
-    { gecko = "StyleWillChange", servo = "values::specified::box_::WillChange" },
+    { gecko = "StyleWillChangeBits", servo = "values::specified::box_::WillChangeBits" },
     { gecko = "StyleTextDecorationLine", servo = "values::computed::TextDecorationLine" },
     { gecko = "StyleTextTransform", servo = "values::computed::TextTransform" },
     { gecko = "StyleMozListReversed", servo = "values::computed::MozListReversed" },
     { gecko = "StyleOwned", servo = "gecko_bindings::sugar::ownership::Owned" },
     { gecko = "StyleOwnedOrNull", servo = "gecko_bindings::sugar::ownership::OwnedOrNull" },
     { gecko = "StyleStrong", servo = "gecko_bindings::sugar::ownership::Strong" },
     { gecko = "StyleGenericFontFamily", servo = "values::computed::font::GenericFontFamily" },
     { gecko = "StyleFontFamilyNameSyntax", servo = "values::computed::font::FontFamilyNameSyntax" },
@@ -477,28 +475,16 @@ cbindgen-types = [
     { gecko = "StyleRGBA", servo = "cssparser::RGBA" },
     { gecko = "StyleOrigin", servo = "stylesheets::Origin" },
     { gecko = "StyleGenericGradientItem", servo = "values::generics::image::GradientItem" },
     { gecko = "StyleGenericVerticalAlign", servo = "values::generics::box_::VerticalAlign" },
     { gecko = "StyleVerticalAlignKeyword", servo = "values::generics::box_::VerticalAlignKeyword" },
     { gecko = "StyleGenericBasicShape", servo = "values::generics::basic_shape::BasicShape" },
     { 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
@@ -5,82 +5,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Some inline functions declared in cbindgen.toml */
 
 #ifndef mozilla_ServoStyleConstsInlines_h
 #define mozilla_ServoStyleConstsInlines_h
 
 #include "mozilla/ServoStyleConsts.h"
-#include "nsGkAtoms.h"
 #include <type_traits>
-#include <new>
 
 // TODO(emilio): there are quite a few other implementations scattered around
 // that should move here.
 
 namespace mozilla {
 
-template <typename T>
-inline StyleOwnedSlice<T>::StyleOwnedSlice(const StyleOwnedSlice& aOther) {
-  len = aOther.len;
-  if (!len) {
-    ptr = (T*)alignof(T);
-  } else {
-    ptr = (T*)malloc(len * sizeof(T));
-    size_t i = 0;
-    for (const T& elem : aOther.AsSpan()) {
-      new (ptr + i++) T(elem);
-    }
-  }
-}
-
-template <typename T>
-inline StyleOwnedSlice<T>::StyleOwnedSlice(StyleOwnedSlice&& aOther) {
-  len = aOther.len;
-  ptr = aOther.ptr;
-  aOther.ptr = (T*)alignof(T);
-  aOther.len = 0;
-}
-
-template <typename T>
-inline void StyleOwnedSlice<T>::Clear() {
-  if (!len) {
-    return;
-  }
-  for (size_t i : IntegerRange(len)) {
-    ptr[i].~T();
-  }
-  free(ptr);
-  ptr = (T*)alignof(T);
-  len = 0;
-}
-
-template <typename T>
-inline StyleOwnedSlice<T>::~StyleOwnedSlice() {
-  Clear();
-}
-
 // This code is basically a C++ port of the Arc::clone() implementation in
 // servo/components/servo_arc/lib.rs.
 static constexpr const size_t kStaticRefcount =
     std::numeric_limits<size_t>::max();
 static constexpr const size_t kMaxRefcount =
     std::numeric_limits<intptr_t>::max();
 static constexpr const uint32_t kArcSliceCanary = 0xf3f3f3f3;
 
 #define ASSERT_CANARY \
   MOZ_DIAGNOSTIC_ASSERT(_0.ptr->data.header.header == kArcSliceCanary, "Uh?");
 
 template <typename T>
-inline StyleArcSlice<T>::StyleArcSlice() {
-  _0.ptr = reinterpret_cast<decltype(_0.ptr)>(Servo_StyleArcSlice_EmptyPtr());
-}
-
-template <typename T>
 inline StyleArcSlice<T>::StyleArcSlice(const StyleArcSlice& aOther) {
   MOZ_DIAGNOSTIC_ASSERT(aOther._0.ptr);
   _0.ptr = aOther._0.ptr;
   if (_0.ptr->count.load(std::memory_order_relaxed) != kStaticRefcount) {
     auto old_size = _0.ptr->count.fetch_add(1, std::memory_order_relaxed);
     if (MOZ_UNLIKELY(old_size > kMaxRefcount)) {
       ::abort();
     }
@@ -98,22 +51,16 @@ inline StyleArcSlice<T>::StyleArcSlice(
 
 template <typename T>
 inline size_t StyleArcSlice<T>::Length() const {
   ASSERT_CANARY
   return _0.ptr->data.header.length;
 }
 
 template <typename T>
-inline bool StyleArcSlice<T>::IsEmpty() const {
-  ASSERT_CANARY
-  return Length() == 0;
-}
-
-template <typename T>
 inline Span<const T> StyleArcSlice<T>::AsSpan() const {
   ASSERT_CANARY
   return MakeSpan(_0.ptr->data.slice, Length());
 }
 
 template <typename T>
 inline bool StyleArcSlice<T>::operator==(const StyleArcSlice& aOther) const {
   ASSERT_CANARY
@@ -139,58 +86,11 @@ inline StyleArcSlice<T>::~StyleArcSlice(
   for (T& elem : MakeSpan(_0.ptr->data.slice, Length())) {
     elem.~T();
   }
   free(_0.ptr);  // Drop the allocation now.
 }
 
 #undef ASSERT_CANARY
 
-inline bool StyleAtom::IsStatic() const { return !!(_0 & 1); }
-
-inline nsAtom* StyleAtom::AsAtom() const {
-  if (IsStatic()) {
-    return const_cast<nsStaticAtom*>(&detail::gGkAtoms.mAtoms[(_0 & ~1) >> 1]);
-  }
-  return reinterpret_cast<nsAtom*>(_0);
-}
-
-inline StyleAtom::~StyleAtom() {
-  if (!IsStatic()) {
-    AsAtom()->Release();
-  }
-}
-
-inline StyleAtom::StyleAtom(const StyleAtom& aOther) : _0(aOther._0) {
-  if (!IsStatic()) {
-    reinterpret_cast<nsAtom*>(_0)->AddRef();
-  }
-}
-
-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,117 +37,206 @@
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using nsStyleTransformMatrix::Decompose2DMatrix;
 using nsStyleTransformMatrix::Decompose3DMatrix;
 using nsStyleTransformMatrix::ShearType;
 
-// 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) {
+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;
+  }
+
+  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 StyleAngle{0.0};
+    return Err(BogusAnimation{});
   }
-  return StyleAngle{aAngle.value()};
+  return Ok();
 }
 
-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));
+static Result<nsCSSValueSharedList*, BogusAnimation> CreateCSSValueList(
+    const InfallibleTArray<layers::TransformFunction>& aFunctions) {
+  nsAutoPtr<nsCSSValueList> result;
+  nsCSSValueList** resultTail = getter_Transfers(result);
+  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?");
     }
-    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) {
-    result.AppendElement(OperationFromLayers(function));
+  if (aFunctions.Length() == 0) {
+    result = new nsCSSValueList();
+    result->mValue.SetNoneValue();
   }
-  return result;
+  return new nsCSSValueSharedList(result.forget());
 }
 
 // AnimationValue Implementation
 
 bool AnimationValue::operator==(const AnimationValue& aOther) const {
   if (mServo && aOther.mServo) {
     return Servo_AnimationValue_DeepEqual(mServo, aOther.mServo);
   }
@@ -166,76 +255,27 @@ float AnimationValue::GetOpacity() const
   return Servo_AnimationValue_GetOpacity(mServo);
 }
 
 nscolor AnimationValue::GetColor(nscolor aForegroundColor) const {
   MOZ_ASSERT(mServo);
   return Servo_AnimationValue_GetColor(mServo, aForegroundColor);
 }
 
-const StyleTranslate& AnimationValue::GetTranslateProperty() const {
-  MOZ_ASSERT(mServo);
-  return *Servo_AnimationValue_GetTranslate(mServo);
-}
-
-const StyleRotate& AnimationValue::GetRotateProperty() const {
+already_AddRefed<const nsCSSValueSharedList> AnimationValue::GetTransformList()
+    const {
   MOZ_ASSERT(mServo);
-  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);
+  RefPtr<nsCSSValueSharedList> transform;
+  Servo_AnimationValue_GetTransform(mServo, &transform);
+  return transform.forget();
 }
 
 Size AnimationValue::GetScaleValue(const nsIFrame* aFrame) const {
-  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);
+  RefPtr<const nsCSSValueSharedList> list = GetTransformList();
+  return nsStyleTransformMatrix::GetScaleValue(list, aFrame);
 }
 
 void AnimationValue::SerializeSpecifiedValue(nsCSSPropertyID aProperty,
                                              nsAString& aString) const {
   MOZ_ASSERT(mServo);
   Servo_AnimationValue_Serialize(mServo, aProperty, &aString);
 }
 
@@ -296,68 +336,106 @@ 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: {
-      nsTArray<StyleTransformOperation> ops =
-          CreateTransformList(aAnimatable.get_ArrayOfTransformFunction());
-      ;
-      return Servo_AnimationValue_Transform(ops.Elements(), ops.Length())
-          .Consume();
+      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;
     }
     case layers::Animatable::Tfloat:
-      return Servo_AnimationValue_Opacity(aAnimatable.get_float()).Consume();
+      result = Servo_AnimationValue_Opacity(aAnimatable.get_float()).Consume();
+      break;
     case layers::Animatable::Tnscolor:
-      return Servo_AnimationValue_Color(aProperty, aAnimatable.get_nscolor())
-          .Consume();
+      result = Servo_AnimationValue_Color(aProperty, aAnimatable.get_nscolor())
+                   .Consume();
+      break;
     case layers::Animatable::TRotate: {
-      auto rotate = RotateFromLayers(aAnimatable.get_Rotate());
-      return Servo_AnimationValue_Rotate(&rotate).Consume();
+      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;
     }
     case layers::Animatable::TScale: {
-      const layers::Scale& s = aAnimatable.get_Scale();
-      auto scale = StyleScale::Scale3D(s.x(), s.y(), s.z());
-      return Servo_AnimationValue_Scale(&scale).Consume();
+      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;
     }
     case layers::Animatable::TTranslation: {
-      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();
+      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;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported type");
   }
-  return nullptr;
+  return result.forget();
 }
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -72,21 +72,18 @@ 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 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 transform list as a RefPtr.
+  already_AddRefed<const nsCSSValueSharedList> GetTransformList() 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->mTransform);
+  return GetTransformValue(display->mSpecifiedTransform);
 }
 
 /* static */
 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::MatrixToCSSValue(
     const mozilla::gfx::Matrix4x4& matrix) {
   bool is3D = !matrix.Is2D();
 
   nsAutoString resultString(NS_LITERAL_STRING("matrix"));
@@ -2497,22 +2497,24 @@ 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(
-    const StyleTransform& aTransform) {
+    nsCSSValueSharedList* aSpecifiedTransform) {
   /* If there are no transforms, then we should construct a single-element
    * entry and hand it back.
    */
-  if (aTransform.IsNone()) {
+  if (!aSpecifiedTransform) {
     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.
    */
 
@@ -2526,17 +2528,18 @@ 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(
-      aTransform, refBox, float(mozilla::AppUnitsPerCSSPixel()));
+      aSpecifiedTransform->mHead, 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,17 +183,18 @@ 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(const mozilla::StyleTransform&);
+  already_AddRefed<CSSValue> GetTransformValue(
+      nsCSSValueSharedList* aSpecifiedTransform);
 
   // 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/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -677,16 +677,22 @@ enum class StyleWhiteSpace : uint8_t {
 #define NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE 0
 #define NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER 1
 #define NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE 2
 #define NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER 3
 #define NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE 4
 
 // See nsStyleSVG
 
+// -moz-context-properties
+#define NS_STYLE_CONTEXT_PROPERTY_FILL (1 << 0)
+#define NS_STYLE_CONTEXT_PROPERTY_STROKE (1 << 1)
+#define NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY (1 << 2)
+#define NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY (1 << 3)
+
 /*
  * -moz-window-shadow
  * Also used in widget code
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE 0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT 1
 #define NS_STYLE_WINDOW_SHADOW_MENU 2
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -80,16 +80,18 @@ static bool DefinitelyEqualImages(const 
 
   if (!aRequest1 || !aRequest2) {
     return false;
   }
 
   return aRequest1->DefinitelyEquals(*aRequest2);
 }
 
+static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs);
+
 // --------------------
 // nsStyleFont
 //
 nsStyleFont::nsStyleFont(const nsStyleFont& aSrc)
     : mFont(aSrc.mFont),
       mSize(aSrc.mSize),
       mFontSizeFactor(aSrc.mFontSizeFactor),
       mFontSizeOffset(aSrc.mFontSizeOffset),
@@ -473,22 +475,22 @@ nsChangeHint nsStyleOutline::CalcDiffere
   return nsChangeHint(0);
 }
 
 // --------------------
 // nsStyleList
 //
 nsStyleList::nsStyleList(const Document& aDocument)
     : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE),
-      mQuotes{StyleArcSlice<StyleQuotePair>(Servo_Quotes_GetInitialValue())},
       mMozListReversed(StyleMozListReversed::False) {
   MOZ_COUNT_CTOR(nsStyleList);
   MOZ_ASSERT(NS_IsMainThread());
 
   mCounterStyle = nsGkAtoms::disc;
+  mQuotes = Servo_Quotes_GetInitialValue().Consume();
 }
 
 nsStyleList::~nsStyleList() { MOZ_COUNT_DTOR(nsStyleList); }
 
 nsStyleList::nsStyleList(const nsStyleList& aSource)
     : mListStylePosition(aSource.mListStylePosition),
       mListStyleImage(aSource.mListStyleImage),
       mCounterStyle(aSource.mCounterStyle),
@@ -507,17 +509,18 @@ void nsStyleList::TriggerImageLoads(Docu
         aDocument, aOldStyle ? aOldStyle->mListStyleImage.get() : nullptr);
   }
 }
 
 nsChangeHint nsStyleList::CalcDifference(
     const nsStyleList& aNewData, const nsStyleDisplay& aOldDisplay) const {
   // If the quotes implementation is ever going to change we might not need
   // a framechange here and a reflow should be sufficient.  See bug 35768.
-  if (mQuotes != aNewData.mQuotes) {
+  if (mQuotes != aNewData.mQuotes &&
+      !Servo_Quotes_Equal(mQuotes.get(), aNewData.mQuotes.get())) {
     return nsChangeHint_ReconstructFrame;
   }
   nsChangeHint hint = nsChangeHint(0);
   // Only elements whose display value is list-item can be affected by
   // list-style-position and list-style-type. If the old display struct
   // doesn't exist, assume it isn't affected by display value at all,
   // and thus these properties should not affect it either. This also
   // relies on that when the display value changes from something else
@@ -666,61 +669,62 @@ nsChangeHint nsStyleColumn::CalcDifferen
 }
 
 // --------------------
 // nsStyleSVG
 //
 nsStyleSVG::nsStyleSVG(const Document& aDocument)
     : mFill(eStyleSVGPaintType_Color),  // Will be initialized to NS_RGB(0,0,0)
       mStroke(eStyleSVGPaintType_None),
-      mMozContextProperties{{}, {0}},
       mStrokeDashoffset(LengthPercentage::Zero()),
       mStrokeWidth(LengthPercentage::FromPixels(1.0f)),
       mFillOpacity(1.0f),
       mStrokeMiterlimit(4.0f),
       mStrokeOpacity(1.0f),
       mClipRule(StyleFillRule::Nonzero),
       mColorInterpolation(NS_STYLE_COLOR_INTERPOLATION_SRGB),
       mColorInterpolationFilters(NS_STYLE_COLOR_INTERPOLATION_LINEARRGB),
       mFillRule(StyleFillRule::Nonzero),
       mPaintOrder(NS_STYLE_PAINT_ORDER_NORMAL),
       mShapeRendering(NS_STYLE_SHAPE_RENDERING_AUTO),
       mStrokeLinecap(NS_STYLE_STROKE_LINECAP_BUTT),
       mStrokeLinejoin(NS_STYLE_STROKE_LINEJOIN_MITER),
       mTextAnchor(NS_STYLE_TEXT_ANCHOR_START),
+      mContextPropsBits(0),
       mContextFlags(
           (eStyleSVGOpacitySource_Normal << FILL_OPACITY_SOURCE_SHIFT) |
           (eStyleSVGOpacitySource_Normal << STROKE_OPACITY_SOURCE_SHIFT)) {
   MOZ_COUNT_CTOR(nsStyleSVG);
 }
 
 nsStyleSVG::~nsStyleSVG() { MOZ_COUNT_DTOR(nsStyleSVG); }
 
 nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource)
     : mFill(aSource.mFill),
       mStroke(aSource.mStroke),
       mMarkerEnd(aSource.mMarkerEnd),
       mMarkerMid(aSource.mMarkerMid),
       mMarkerStart(aSource.mMarkerStart),
       mStrokeDasharray(aSource.mStrokeDasharray),
-      mMozContextProperties(aSource.mMozContextProperties),
+      mContextProps(aSource.mContextProps),
       mStrokeDashoffset(aSource.mStrokeDashoffset),
       mStrokeWidth(aSource.mStrokeWidth),
       mFillOpacity(aSource.mFillOpacity),
       mStrokeMiterlimit(aSource.mStrokeMiterlimit),
       mStrokeOpacity(aSource.mStrokeOpacity),
       mClipRule(aSource.mClipRule),
       mColorInterpolation(aSource.mColorInterpolation),
       mColorInterpolationFilters(aSource.mColorInterpolationFilters),
       mFillRule(aSource.mFillRule),
       mPaintOrder(aSource.mPaintOrder),
       mShapeRendering(aSource.mShapeRendering),
       mStrokeLinecap(aSource.mStrokeLinecap),
       mStrokeLinejoin(aSource.mStrokeLinejoin),
       mTextAnchor(aSource.mTextAnchor),
+      mContextPropsBits(aSource.mContextPropsBits),
       mContextFlags(aSource.mContextFlags) {
   MOZ_COUNT_CTOR(nsStyleSVG);
 }
 
 static bool PaintURIChanged(const nsStyleSVGPaint& aPaint1,
                             const nsStyleSVGPaint& aPaint2) {
   if (aPaint1.Type() != aPaint2.Type()) {
     return aPaint1.Type() == eStyleSVGPaintType_Server ||
@@ -788,22 +792,22 @@ nsChangeHint nsStyleSVG::CalcDifference(
   if (mStrokeDashoffset != aNewData.mStrokeDashoffset ||
       mClipRule != aNewData.mClipRule ||
       mColorInterpolation != aNewData.mColorInterpolation ||
       mColorInterpolationFilters != aNewData.mColorInterpolationFilters ||
       mFillRule != aNewData.mFillRule || mPaintOrder != aNewData.mPaintOrder ||
       mShapeRendering != aNewData.mShapeRendering ||
       mStrokeDasharray != aNewData.mStrokeDasharray ||
       mContextFlags != aNewData.mContextFlags ||
-      mMozContextProperties.bits != aNewData.mMozContextProperties.bits) {
+      mContextPropsBits != aNewData.mContextPropsBits) {
     return hint | nsChangeHint_RepaintFrame;
   }
 
   if (!hint) {
-    if (mMozContextProperties.idents != aNewData.mMozContextProperties.idents) {
+    if (mContextProps != aNewData.mContextProps) {
       hint = nsChangeHint_NeutralChange;
     }
   }
 
   return hint;
 }
 
 // --------------------
@@ -965,22 +969,23 @@ void StyleShapeSource::DoDestroy() {
       break;
   }
   mType = StyleShapeSourceType::None;
 }
 
 // --------------------
 // nsStyleFilter
 //
-nsStyleFilter::nsStyleFilter() : mType(NS_STYLE_FILTER_NONE), mURL(nullptr) {
+nsStyleFilter::nsStyleFilter()
+    : mType(NS_STYLE_FILTER_NONE), mDropShadow(nullptr) {
   MOZ_COUNT_CTOR(nsStyleFilter);
 }
 
 nsStyleFilter::nsStyleFilter(const nsStyleFilter& aSource)
-    : mType(NS_STYLE_FILTER_NONE), mURL(nullptr) {
+    : mType(NS_STYLE_FILTER_NONE), mDropShadow(nullptr) {
   MOZ_COUNT_CTOR(nsStyleFilter);
   if (aSource.mType == NS_STYLE_FILTER_URL) {
     SetURL(aSource.mURL);
   } else if (aSource.mType == NS_STYLE_FILTER_DROP_SHADOW) {
     SetDropShadow(aSource.mDropShadow);
   } else if (aSource.mType != NS_STYLE_FILTER_NONE) {
     SetFilterParameter(aSource.mFilterParameter, aSource.mType);
   }
@@ -1013,27 +1018,28 @@ nsStyleFilter& nsStyleFilter::operator=(
 bool nsStyleFilter::operator==(const nsStyleFilter& aOther) const {
   if (mType != aOther.mType) {
     return false;
   }
 
   if (mType == NS_STYLE_FILTER_URL) {
     return DefinitelyEqualURIs(mURL, aOther.mURL);
   } else if (mType == NS_STYLE_FILTER_DROP_SHADOW) {
-    return mDropShadow == aOther.mDropShadow;
+    return *mDropShadow == *aOther.mDropShadow;
   } else if (mType != NS_STYLE_FILTER_NONE) {
     return mFilterParameter == aOther.mFilterParameter;
   }
 
   return true;
 }
 
 void nsStyleFilter::ReleaseRef() {
   if (mType == NS_STYLE_FILTER_DROP_SHADOW) {
-    mDropShadow.~StyleSimpleShadow();
+    NS_ASSERTION(mDropShadow, "expected pointer");
+    mDropShadow->Release();
   } else if (mType == NS_STYLE_FILTER_URL) {
     NS_ASSERTION(mURL, "expected pointer");
     mURL->Release();
   }
   mURL = nullptr;
 }
 
 void nsStyleFilter::SetFilterParameter(const nsStyleCoord& aFilterParameter,
@@ -1046,19 +1052,21 @@ void nsStyleFilter::SetFilterParameter(c
 bool nsStyleFilter::SetURL(css::URLValue* aURL) {
   ReleaseRef();
   mURL = aURL;
   mURL->AddRef();
   mType = NS_STYLE_FILTER_URL;
   return true;
 }
 
-void nsStyleFilter::SetDropShadow(const StyleSimpleShadow& aSrc) {
+void nsStyleFilter::SetDropShadow(nsCSSShadowArray* aDropShadow) {
+  NS_ASSERTION(aDropShadow, "expected pointer");
   ReleaseRef();
-  new (&mDropShadow) StyleSimpleShadow(aSrc);
+  mDropShadow = aDropShadow;
+  mDropShadow->AddRef();
   mType = NS_STYLE_FILTER_DROP_SHADOW;
 }
 
 // --------------------
 // nsStyleSVGReset
 //
 nsStyleSVGReset::nsStyleSVGReset(const Document& aDocument)
     : mMask(nsStyleImageLayers::LayerType::Mask),
@@ -2911,17 +2919,17 @@ nsStyleDisplay::nsStyleDisplay(const Doc
       mOverflowX(StyleOverflow::Visible),
       mOverflowY(StyleOverflow::Visible),
       mOverflowClipBoxBlock(StyleOverflowClipBox::PaddingBox),
       mOverflowClipBoxInline(StyleOverflowClipBox::PaddingBox),
       mResize(StyleResize::None),
       mOrient(StyleOrient::Inline),
       mIsolation(NS_STYLE_ISOLATION_AUTO),
       mTopLayer(NS_STYLE_TOP_LAYER_NONE),
-      mWillChange{{}, {0}},
+      mWillChangeBitField({0}),
       mTouchAction(StyleTouchAction_AUTO),
       mScrollBehavior(NS_STYLE_SCROLL_BEHAVIOR_AUTO),
       mOverscrollBehaviorX(StyleOverscrollBehavior::Auto),
       mOverscrollBehaviorY(StyleOverscrollBehavior::Auto),
       mOverflowAnchor(StyleOverflowAnchor::Auto),
       mScrollSnapType(
           {StyleScrollSnapAxis::Both, StyleScrollSnapStrictness::None}),
       mScrollSnapPointsX(eStyleUnit_None),
@@ -2978,34 +2986,35 @@ nsStyleDisplay::nsStyleDisplay(const nsS
       mOverflowX(aSource.mOverflowX),
       mOverflowY(aSource.mOverflowY),
       mOverflowClipBoxBlock(aSource.mOverflowClipBoxBlock),
       mOverflowClipBoxInline(aSource.mOverflowClipBoxInline),
       mResize(aSource.mResize),
       mOrient(aSource.mOrient),
       mIsolation(aSource.mIsolation),
       mTopLayer(aSource.mTopLayer),
+      mWillChangeBitField(aSource.mWillChangeBitField),
       mWillChange(aSource.mWillChange),
       mTouchAction(aSource.mTouchAction),
       mScrollBehavior(aSource.mScrollBehavior),
       mOverscrollBehaviorX(aSource.mOverscrollBehaviorX),
       mOverscrollBehaviorY(aSource.mOverscrollBehaviorY),
       mScrollSnapType(aSource.mScrollSnapType),
       mScrollSnapPointsX(aSource.mScrollSnapPointsX),
       mScrollSnapPointsY(aSource.mScrollSnapPointsY),
       mScrollSnapDestination(aSource.mScrollSnapDestination),
       mScrollSnapCoordinate(aSource.mScrollSnapCoordinate),
       mLineClamp(aSource.mLineClamp),
       mBackfaceVisibility(aSource.mBackfaceVisibility),
       mTransformStyle(aSource.mTransformStyle),
       mTransformBox(aSource.mTransformBox),
-      mTransform(aSource.mTransform),
-      mRotate(aSource.mRotate),
-      mTranslate(aSource.mTranslate),
-      mScale(aSource.mScale),
+      mSpecifiedTransform(aSource.mSpecifiedTransform),
+      mSpecifiedRotate(aSource.mSpecifiedRotate),
+      mSpecifiedTranslate(aSource.mSpecifiedTranslate),
+      mSpecifiedScale(aSource.mSpecifiedScale),
       // 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,38 +3034,77 @@ 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);
 }
 
-template <typename TransformLike>
+static inline bool TransformListChanged(
+    const RefPtr<nsCSSValueSharedList>& aList,
+    const RefPtr<nsCSSValueSharedList>& aNewList) {
+  return !aList != !aNewList || (aList && *aList != *aNewList);
+}
+
 static inline nsChangeHint CompareTransformValues(
-    const TransformLike& aOldTransform, const TransformLike& aNewTransform) {
+    const RefPtr<nsCSSValueSharedList>& aList,
+    const RefPtr<nsCSSValueSharedList>& aNewList) {
   nsChangeHint result = nsChangeHint(0);
 
   // Note: If we add a new change hint for transform changes here, we have to
   // modify KeyframeEffect::CalculateCumulativeChangeHint too!
-  if (aOldTransform != aNewTransform) {
+  if (!aList != !aNewList || (aList && *aList != *aNewList)) {
     result |= nsChangeHint_UpdateTransformLayer;
-    if (!aOldTransform.IsNone() && !aNewTransform.IsNone()) {
+    if (aList && aNewList) {
       result |= nsChangeHint_UpdatePostTransformOverflow;
     } else {
       result |= nsChangeHint_UpdateOverflow;
     }
   }
 
   return result;
 }
@@ -3195,20 +3243,24 @@ 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(mTransform, aNewData.mTransform);
-    transformHint |= CompareTransformValues(mRotate, aNewData.mRotate);
-    transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate);
-    transformHint |= CompareTransformValues(mScale, aNewData.mScale);
+    transformHint |= CompareTransformValues(mSpecifiedTransform,
+                                            aNewData.mSpecifiedTransform);
+    transformHint |=
+        CompareTransformValues(mSpecifiedRotate, aNewData.mSpecifiedRotate);
+    transformHint |= CompareTransformValues(mSpecifiedTranslate,
+                                            aNewData.mSpecifiedTranslate);
+    transformHint |=
+        CompareTransformValues(mSpecifiedScale, aNewData.mSpecifiedScale);
     transformHint |= CompareMotionValues(mMotion.get(), aNewData.mMotion.get());
 
     if (mTransformOrigin != aNewData.mTransformOrigin) {
       transformHint |= nsChangeHint_UpdateTransformLayer |
                        nsChangeHint_UpdatePostTransformOverflow;
     }
 
     if (mPerspectiveOrigin != aNewData.mPerspectiveOrigin ||
@@ -3241,17 +3293,17 @@ nsChangeHint nsStyleDisplay::CalcDiffere
   // Note that the HasTransformStyle() != aNewData.HasTransformStyle()
   // test above handles relevant changes in the StyleWillChangeBit_TRANSFORM
   // bit, which in turn handles frame reconstruction for changes in the
   // containing block of fixed-positioned elements.
   //
   // TODO(emilio): Should add xor to the generated cbindgen type.
   auto willChangeBitsChanged =
       StyleWillChangeBits{static_cast<decltype(StyleWillChangeBits::bits)>(
-          mWillChange.bits.bits ^ aNewData.mWillChange.bits.bits)};
+          mWillChangeBitField.bits ^ aNewData.mWillChangeBitField.bits)};
 
   if (willChangeBitsChanged &
       (StyleWillChangeBits_STACKING_CONTEXT | StyleWillChangeBits_SCROLL |
        StyleWillChangeBits_OPACITY)) {
     hint |= nsChangeHint_RepaintFrame;
   }
 
   if (willChangeBitsChanged &
@@ -3311,16 +3363,78 @@ 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),
@@ -3576,16 +3690,34 @@ nsChangeHint nsStyleTextReset::CalcDiffe
 
   if (mTextOverflow != aNewData.mTextOverflow) {
     return nsChangeHint_RepaintFrame;
   }
 
   return nsChangeHint(0);
 }
 
+// Returns true if the given shadow-arrays are equal.
+static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs) {
+  if (lhs == rhs) {
+    return true;
+  }
+
+  if (!lhs || !rhs || lhs->Length() != rhs->Length()) {
+    return false;
+  }
+
+  for (uint32_t i = 0; i < lhs->Length(); ++i) {
+    if (*lhs->ShadowAt(i) != *rhs->ShadowAt(i)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 // --------------------
 // nsStyleText
 //
 
 nsStyleText::nsStyleText(const Document& aDocument)
     : mTextTransform(StyleTextTransform::None()),
       mTextAlign(NS_STYLE_TEXT_ALIGN_START),
       mTextAlignLast(NS_STYLE_TEXT_ALIGN_AUTO),
@@ -3604,17 +3736,18 @@ nsStyleText::nsStyleText(const Document&
       mWebkitTextFillColor(StyleColor::CurrentColor()),
       mWebkitTextStrokeColor(StyleColor::CurrentColor()),
       mMozTabSize(
           StyleNonNegativeLengthOrNumber::Number(NS_STYLE_TABSIZE_INITIAL)),
       mWordSpacing(LengthPercentage::Zero()),
       mLetterSpacing({0.}),
       mLineHeight(StyleLineHeight::Normal()),
       mTextIndent(LengthPercentage::Zero()),
-      mWebkitTextStrokeWidth(0) {
+      mWebkitTextStrokeWidth(0),
+      mTextShadow(nullptr) {
   MOZ_COUNT_CTOR(nsStyleText);
   RefPtr<nsAtom> language = aDocument.GetContentLanguageAsAtomForStyle();
   mTextEmphasisPosition =
       language && nsStyleUtil::MatchesLanguagePrefix(language, u"zh")
           ? NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH
           : NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT;
 }
 
@@ -3693,17 +3826,17 @@ nsChangeHint nsStyleText::CalcDifference
   // text-rendering changes require a reflow since they change SVG
   // frames' rects.
   if (mTextRendering != aNewData.mTextRendering) {
     hint |= nsChangeHint_NeedReflow |
             nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
             nsChangeHint_RepaintFrame;
   }
 
-  if (mTextShadow != aNewData.mTextShadow ||
+  if (!AreShadowArraysEqual(mTextShadow, aNewData.mTextShadow) ||
       mTextEmphasisStyle != aNewData.mTextEmphasisStyle ||
       mTextEmphasisStyleString != aNewData.mTextEmphasisStyleString ||
       mWebkitTextStrokeWidth != aNewData.mWebkitTextStrokeWidth) {
     hint |= nsChangeHint_UpdateSubtreeOverflow | nsChangeHint_SchedulePaint |
             nsChangeHint_RepaintFrame;
 
     // We don't add any other hints below.
     return hint;
@@ -3875,37 +4008,41 @@ 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),
-      mMozWindowTransform(aSource.mMozWindowTransform),
+      mSpecifiedWindowTransform(aSource.mSpecifiedWindowTransform),
       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;
@@ -3926,33 +4063,37 @@ nsChangeHint nsStyleUIReset::CalcDiffere
     hint |= NS_STYLE_HINT_VISUAL;
   }
 
   if (mWindowDragging != aNewData.mWindowDragging) {
     hint |= nsChangeHint_SchedulePaint;
   }
 
   if (mWindowOpacity != aNewData.mWindowOpacity ||
-      mMozWindowTransform != aNewData.mMozWindowTransform) {
+      !mSpecifiedWindowTransform != !aNewData.mSpecifiedWindowTransform ||
+      (mSpecifiedWindowTransform &&
+       *mSpecifiedWindowTransform != *aNewData.mSpecifiedWindowTransform) ||
+      mWindowTransformOrigin != aNewData.mWindowTransformOrigin) {
     hint |= nsChangeHint_UpdateWidgetProperties;
   }
 
   if (!hint && mIMEMode != aNewData.mIMEMode) {
     hint |= nsChangeHint_NeutralChange;
   }
 
   return hint;
 }
 
 //-----------------------
 // nsStyleEffects
 //
 
 nsStyleEffects::nsStyleEffects(const Document&)
-    : mClip(0, 0, 0, 0),
+    : mBoxShadow(nullptr),
+      mClip(0, 0, 0, 0),
       mOpacity(1.0f),
       mClipFlags(NS_STYLE_CLIP_AUTO),
       mMixBlendMode(NS_STYLE_BLEND_NORMAL) {
   MOZ_COUNT_CTOR(nsStyleEffects);
 }
 
 nsStyleEffects::nsStyleEffects(const nsStyleEffects& aSource)
     : mFilters(aSource.mFilters),
@@ -3965,17 +4106,17 @@ nsStyleEffects::nsStyleEffects(const nsS
 }
 
 nsStyleEffects::~nsStyleEffects() { MOZ_COUNT_DTOR(nsStyleEffects); }
 
 nsChangeHint nsStyleEffects::CalcDifference(
     const nsStyleEffects& aNewData) const {
   nsChangeHint hint = nsChangeHint(0);
 
-  if (mBoxShadow != aNewData.mBoxShadow) {
+  if (!AreShadowArraysEqual(mBoxShadow, aNewData.mBoxShadow)) {
     // Update overflow regions & trigger DLBI to be sure it's noticed.
     // Also request a repaint, since it's possible that only the color
     // of the shadow is changing (and UpdateOverflow/SchedulePaint won't
     // repaint for that, since they won't know what needs invalidating.)
     hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
             nsChangeHint_RepaintFrame;
   }
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -742,16 +742,119 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     NS_FOR_CSS_SIDES(side) {
       // Clamp negative calc() to 0.
       aPadding.Side(side) = std::max(mPadding.Get(side).ToLength(), 0);
     }
     return true;
   }
 };
 
+struct nsCSSShadowItem {
+  nscoord mXOffset;
+  nscoord mYOffset;
+  nscoord mRadius;
+  nscoord mSpread;
+
+  mozilla::StyleColor mColor;
+  bool mInset;
+
+  nsCSSShadowItem()
+      : mXOffset(0),
+        mYOffset(0),
+        mRadius(0),
+        mSpread(0),
+        mColor(mozilla::StyleColor::CurrentColor()),
+        mInset(false) {
+    MOZ_COUNT_CTOR(nsCSSShadowItem);
+  }
+  ~nsCSSShadowItem() { MOZ_COUNT_DTOR(nsCSSShadowItem); }
+
+  bool operator==(const nsCSSShadowItem& aOther) const {
+    return (mXOffset == aOther.mXOffset && mYOffset == aOther.mYOffset &&
+            mRadius == aOther.mRadius && mSpread == aOther.mSpread &&
+            mInset == aOther.mInset && mColor == aOther.mColor);
+  }
+  bool operator!=(const nsCSSShadowItem& aOther) const {
+    return !(*this == aOther);
+  }
+};
+
+class nsCSSShadowArray final {
+ public:
+  void* operator new(size_t aBaseSize, uint32_t aArrayLen) {
+    // We can allocate both this nsCSSShadowArray and the
+    // actual array in one allocation. The amount of memory to
+    // allocate is equal to the class's size + the number of bytes for all
+    // but the first array item (because aBaseSize includes one
+    // item, see the private declarations)
+    return ::operator new(aBaseSize +
+                          (aArrayLen - 1) * sizeof(nsCSSShadowItem));
+  }
+
+  void operator delete(void* aPtr) { ::operator delete(aPtr); }
+
+  explicit nsCSSShadowArray(uint32_t aArrayLen) : mLength(aArrayLen) {
+    for (uint32_t i = 1; i < mLength; ++i) {
+      // Make sure we call the constructors of each nsCSSShadowItem
+      // (the first one is called for us because we declared it under private)
+      new (&mArray[i]) nsCSSShadowItem();
+    }
+  }
+
+ private:
+  // Private destructor, to discourage deletion outside of Release():
+  ~nsCSSShadowArray() {
+    for (uint32_t i = 1; i < mLength; ++i) {
+      mArray[i].~nsCSSShadowItem();
+    }
+  }
+
+ public:
+  uint32_t Length() const { return mLength; }
+  nsCSSShadowItem* ShadowAt(uint32_t i) {
+    MOZ_ASSERT(i < mLength,
+               "Accessing too high an index in the text shadow array!");
+    return &mArray[i];
+  }
+  const nsCSSShadowItem* ShadowAt(uint32_t i) const {
+    MOZ_ASSERT(i < mLength,
+               "Accessing too high an index in the text shadow array!");
+    return &mArray[i];
+  }
+
+  bool HasShadowWithInset(bool aInset) {
+    for (uint32_t i = 0; i < mLength; ++i) {
+      if (mArray[i].mInset == aInset) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool operator==(const nsCSSShadowArray& aOther) const {
+    if (mLength != aOther.Length()) {
+      return false;
+    }
+
+    for (uint32_t i = 0; i < mLength; ++i) {
+      if (ShadowAt(i) != aOther.ShadowAt(i)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsCSSShadowArray)
+
+ private:
+  uint32_t mLength;
+  nsCSSShadowItem mArray[1];  // This MUST be the last item
+};
+
 // Border widths are rounded to the nearest-below integer number of pixels,
 // but values between zero and one device pixels are always rounded up to
 // one device pixel.
 #define NS_ROUND_BORDER_TO_PIXELS(l, tpp) \
   ((l) == 0) ? 0 : std::max((tpp), (l) / (tpp) * (tpp))
 
 // Returns if the given border style type is visible or not
 static bool IsVisibleBorderStyle(mozilla::StyleBorderStyle aStyle) {
@@ -995,17 +1098,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   RefPtr<nsStyleImageRequest> mListStyleImage;
 
   mozilla::CounterStylePtr mCounterStyle;
 
  private:
   nsStyleList& operator=(const nsStyleList& aOther) = delete;
 
  public:
-  mozilla::StyleQuotes mQuotes;
+  RefPtr<RawServoQuotes> mQuotes;
   nsRect mImageRegion;  // the rect to use within an image
   mozilla::StyleMozListReversed
       mMozListReversed;  // true in an <ol reversed> scope
 };
 
 struct nsStyleGridLine {
   // http://dev.w3.org/csswg/css-grid/#typedef-grid-line
   // XXXmats we could optimize memory size here
@@ -1378,17 +1481,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   mozilla::StyleNonNegativeLengthOrNumber mMozTabSize;
   mozilla::LengthPercentage mWordSpacing;
   mozilla::StyleLetterSpacing mLetterSpacing;
   mozilla::StyleLineHeight mLineHeight;
   mozilla::LengthPercentage mTextIndent;
   nscoord mWebkitTextStrokeWidth;  // coord
 
-  mozilla::StyleArcSlice<mozilla::StyleSimpleShadow> mTextShadow;
+  RefPtr<nsCSSShadowArray> mTextShadow;  // nullptr in case of a zero-length
 
   nsString mTextEmphasisStyleString;
 
   mozilla::StyleWordBreak EffectiveWordBreak() const {
     if (mWordBreak == mozilla::StyleWordBreak::BreakWord) {
       return mozilla::StyleWordBreak::Normal;
     }
     return mWordBreak;
@@ -1439,17 +1542,19 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     return owrap == mozilla::StyleOverflowWrap::BreakWord ||
            owrap == mozilla::StyleOverflowWrap::Anywhere;
   }
 
   bool HasTextEmphasis() const { return !mTextEmphasisStyleString.IsEmpty(); }
 
   bool HasWebkitTextStroke() const { return mWebkitTextStrokeWidth > 0; }
 
-  bool HasTextShadow() const { return !mTextShadow.IsEmpty(); }
+  // These are defined in nsStyleStructInlines.h.
+  inline bool HasTextShadow() const;
+  inline nsCSSShadowArray* GetTextShadow() const;
 
   // The aContextFrame argument on each of these is the frame this
   // style struct is for.  If the frame is for SVG text or inside ruby,
   // the return value will be massaged to be something that makes sense
   // for those cases.
   inline bool NewlineIsSignificant(const nsTextFrame* aContextFrame) const;
   inline bool WhiteSpaceCanWrap(const nsIFrame* aContextFrame) const;
   inline bool WordCanWrap(const nsIFrame* aContextFrame) const;
@@ -1556,19 +1661,22 @@ struct StyleAnimation {
   dom::FillMode mFillMode;
   StyleAnimationPlayState mPlayState;
   float mIterationCount;  // mozilla::PositiveInfinity<float>() means infinite
 };
 
 struct StyleSVGPath final {
   StyleSVGPath(StyleForgottenArcSlicePtr<StylePathCommand> aPath,
                StyleFillRule aFill)
-      : mPath(aPath), mFillRule(aFill) {}
-
-  Span<const StylePathCommand> Path() const { return mPath.AsSpan(); }
+    : mPath(aPath),
+      mFillRule(aFill) {}
+
+  Span<const StylePathCommand> Path() const {
+    return mPath.AsSpan();
+  }
 
   StyleFillRule FillRule() const { return mFillRule; }
 
   bool operator==(const StyleSVGPath& aOther) const {
     return mPath == aOther.mPath && mFillRule == aOther.mFillRule;
   }
 
   bool operator!=(const StyleSVGPath& aOther) const {
@@ -1694,16 +1802,18 @@ 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
@@ -1723,17 +1833,21 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   mozilla::StyleOverflow mOverflowX;
   mozilla::StyleOverflow mOverflowY;
   mozilla::StyleOverflowClipBox mOverflowClipBoxBlock;
   mozilla::StyleOverflowClipBox mOverflowClipBoxInline;
   mozilla::StyleResize mResize;
   mozilla::StyleOrient mOrient;
   uint8_t mIsolation;  // NS_STYLE_ISOLATION_*
   uint8_t mTopLayer;   // NS_STYLE_TOP_LAYER_*
-  mozilla::StyleWillChange mWillChange;
+  // Stores a bitfield representation of the properties that are frequently
+  // queried. This should match mWillChange. Also tracks if any of the
+  // properties in the will-change list require a stacking context.
+  mozilla::StyleWillChangeBits mWillChangeBitField;
+  nsTArray<RefPtr<nsAtom>> mWillChange;
 
   mozilla::StyleTouchAction mTouchAction;
   uint8_t mScrollBehavior;  // NS_STYLE_SCROLL_BEHAVIOR_*
   mozilla::StyleOverscrollBehavior mOverscrollBehaviorX;
   mozilla::StyleOverscrollBehavior mOverscrollBehaviorY;
   mozilla::StyleOverflowAnchor mOverflowAnchor;
   mozilla::StyleScrollSnapAlign mScrollSnapAlign;
   mozilla::StyleScrollSnapType mScrollSnapType;
@@ -1745,22 +1859,23 @@ 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;
-
-  mozilla::StyleTransform mTransform;
-  mozilla::StyleRotate mRotate;
-  mozilla::StyleTranslate mTranslate;
-  mozilla::StyleScale mScale;
-
+  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::UniquePtr<mozilla::StyleMotion> mMotion;
 
   mozilla::StyleTransformOrigin mTransformOrigin;
   mozilla::StylePerspective mChildPerspective;
   mozilla::Position mPerspectiveOrigin;
 
   mozilla::StyleVerticalAlign mVerticalAlign;
 
@@ -1991,29 +2106,28 @@ 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 transform property or a related
-   * property. */
+  /* Returns whether the element has the -moz-transform property
+   * or a related property. */
   bool HasTransformStyle() const {
-    return HasTransformProperty() || HasIndividualTransform() ||
+    return mSpecifiedTransform || mSpecifiedRotate || mSpecifiedTranslate ||
+           mSpecifiedScale ||
            mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
-           (mWillChange.bits & mozilla::StyleWillChangeBits_TRANSFORM) ||
+           (mWillChangeBitField & mozilla::StyleWillChangeBits_TRANSFORM) ||
            (mMotion && mMotion->HasPath());
   }
 
-  bool HasTransformProperty() const { return !mTransform._0.IsEmpty(); }
-
   bool HasIndividualTransform() const {
-    return !mRotate.IsNone() || !mTranslate.IsNone() || !mScale.IsNone();
+    return mSpecifiedRotate || mSpecifiedTranslate || mSpecifiedScale;
   }
 
   bool HasPerspectiveStyle() const { return !mChildPerspective.IsNone(); }
 
   bool BackfaceIsHidden() const {
     return mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN;
   }
 
@@ -2110,16 +2224,33 @@ 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*) {}
@@ -2353,17 +2484,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;
-  mozilla::StyleTransform mMozWindowTransform;
+  RefPtr<nsCSSValueSharedList> mSpecifiedWindowTransform;
   mozilla::StyleTransformOrigin mWindowTransformOrigin;
 };
 
 struct nsCursorImage {
   bool mHaveHotspot;
   float mHotspotX, mHotspotY;
   RefPtr<nsStyleImageRequest> mImage;
 
@@ -2562,40 +2693,40 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsChangeHint CalcDifference(const nsStyleSVG& aNewData) const;
 
   nsStyleSVGPaint mFill;
   nsStyleSVGPaint mStroke;
   RefPtr<mozilla::css::URLValue> mMarkerEnd;
   RefPtr<mozilla::css::URLValue> mMarkerMid;
   RefPtr<mozilla::css::URLValue> mMarkerStart;
   nsTArray<mozilla::NonNegativeLengthPercentage> mStrokeDasharray;
-  mozilla::StyleMozContextProperties mMozContextProperties;
+  nsTArray<RefPtr<nsAtom>> mContextProps;
 
   mozilla::LengthPercentage mStrokeDashoffset;
   mozilla::NonNegativeLengthPercentage mStrokeWidth;
 
   float mFillOpacity;
   float mStrokeMiterlimit;
   float mStrokeOpacity;
 
   mozilla::StyleFillRule mClipRule;
   uint8_t mColorInterpolation;         // NS_STYLE_COLOR_INTERPOLATION_*
   uint8_t mColorInterpolationFilters;  // NS_STYLE_COLOR_INTERPOLATION_*
   mozilla::StyleFillRule mFillRule;
-  uint8_t mPaintOrder;      // bitfield of NS_STYLE_PAINT_ORDER_* values
-  uint8_t mShapeRendering;  // NS_STYLE_SHAPE_RENDERING_*
-  uint8_t mStrokeLinecap;   // NS_STYLE_STROKE_LINECAP_*
-  uint8_t mStrokeLinejoin;  // NS_STYLE_STROKE_LINEJOIN_*
-  uint8_t mTextAnchor;      // NS_STYLE_TEXT_ANCHOR_*
+  uint8_t mPaintOrder;        // bitfield of NS_STYLE_PAINT_ORDER_* values
+  uint8_t mShapeRendering;    // NS_STYLE_SHAPE_RENDERING_*
+  uint8_t mStrokeLinecap;     // NS_STYLE_STROKE_LINECAP_*
+  uint8_t mStrokeLinejoin;    // NS_STYLE_STROKE_LINEJOIN_*
+  uint8_t mTextAnchor;        // NS_STYLE_TEXT_ANCHOR_*
+  uint8_t mContextPropsBits;  // bitfield of
+                              // NS_STYLE_CONTEXT_PROPERTY_FILL_* values
 
   /// Returns true if style has been set to expose the computed values of
   /// certain properties (such as 'fill') to the contents of any linked images.
-  bool ExposesContextProperties() const {
-    return bool(mMozContextProperties.bits);
-  }
+  bool ExposesContextProperties() const { return bool(mContextPropsBits); }
 
   nsStyleSVGOpacitySource FillOpacitySource() const {
     uint8_t value =
         (mContextFlags & FILL_OPACITY_SOURCE_MASK) >> FILL_OPACITY_SOURCE_SHIFT;
     return nsStyleSVGOpacitySource(value);
   }
   nsStyleSVGOpacitySource StrokeOpacitySource() const {
     uint8_t value = (mContextFlags & STROKE_OPACITY_SOURCE_MASK) >>
@@ -2678,30 +2809,30 @@ struct nsStyleFilter {
 
   mozilla::css::URLValue* GetURL() const {
     MOZ_ASSERT(mType == NS_STYLE_FILTER_URL, "wrong filter type");
     return mURL;
   }
 
   bool SetURL(mozilla::css::URLValue* aValue);
 
-  const mozilla::StyleSimpleShadow& GetDropShadow() const {
+  nsCSSShadowArray* GetDropShadow() const {
     NS_ASSERTION(mType == NS_STYLE_FILTER_DROP_SHADOW, "wrong filter type");
     return mDropShadow;
   }
-  void SetDropShadow(const mozilla::StyleSimpleShadow&);
+  void SetDropShadow(nsCSSShadowArray* aDropShadow);
 
  private:
   void ReleaseRef();
 
   uint32_t mType;                 // NS_STYLE_FILTER_*
   nsStyleCoord mFilterParameter;  // coord, percent, factor, angle
   union {
     mozilla::css::URLValue* mURL;
-    mozilla::StyleSimpleShadow mDropShadow;
+    nsCSSShadowArray* mDropShadow;
   };
 };
 
 template <>
 struct nsTArray_CopyChooser<nsStyleFilter> {
   typedef nsTArray_CopyWithConstructors<nsStyleFilter> Type;
 };
 
@@ -2748,28 +2879,19 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   ~nsStyleEffects();
   void TriggerImageLoads(mozilla::dom::Document&, const nsStyleEffects*) {}
   const static bool kHasTriggerImageLoads = false;
 
   nsChangeHint CalcDifference(const nsStyleEffects& aNewData) const;
 
   bool HasFilters() const { return !mFilters.IsEmpty(); }
 
-  bool HasBoxShadowWithInset(bool aInset) const {
-    for (auto& shadow : mBoxShadow.AsSpan()) {
-      if (shadow.inset == aInset) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   nsTArray<nsStyleFilter> mFilters;
-  mozilla::StyleOwnedSlice<mozilla::StyleBoxShadow> mBoxShadow;
-  nsRect mClip;  // offsets from UL border edge
+  RefPtr<nsCSSShadowArray> mBoxShadow;  // nullptr for 'none'
+  nsRect mClip;                         // offsets from UL border edge
   float mOpacity;
   uint8_t mClipFlags;     // bitfield of NS_STYLE_CLIP_* values
   uint8_t mMixBlendMode;  // NS_STYLE_BLEND_*
 };
 
 #define STATIC_ASSERT_TYPE_LAYOUTS_MATCH(T1, T2)           \
   static_assert(sizeof(T1) == sizeof(T2),                  \
                 "Size mismatch between " #T1 " and " #T2); \
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -30,16 +30,20 @@ inline void nsStyleImage::SetSubImage(ui
   EnsureCachedBIData();
   mCachedBIData->SetSubImage(aIndex, aSubImage);
 }
 
 inline imgIContainer* nsStyleImage::GetSubImage(uint8_t aIndex) const {
   return (mCachedBIData) ? mCachedBIData->GetSubImage(aIndex) : nullptr;
 }
 
+bool nsStyleText::HasTextShadow() const { return mTextShadow; }
+
+nsCSSShadowArray* nsStyleText::GetTextShadow() const { return mTextShadow; }
+
 bool nsStyleText::NewlineIsSignificant(const nsTextFrame* aContextFrame) const {
   NS_ASSERTION(aContextFrame->StyleText() == this, "unexpected aContextFrame");
   return NewlineIsSignificantStyle() &&
          !aContextFrame->ShouldSuppressLineBreak() &&
          !aContextFrame->Style()->IsTextCombined();
 }
 
 bool nsStyleText::WhiteSpaceCanWrap(const nsIFrame* aContextFrame) const {
@@ -126,17 +130,17 @@ bool nsStyleDisplay::HasPerspective(cons
 }
 
 bool nsStyleDisplay::IsFixedPosContainingBlockForNonSVGTextFrames(
     const mozilla::ComputedStyle& aStyle) const {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
   NS_ASSERTION(aStyle.StyleDisplay() == this, "unexpected aStyle");
 
-  if (mWillChange.bits & mozilla::StyleWillChangeBits_FIXPOS_CB) {
+  if (mWillChangeBitField & mozilla::StyleWillChangeBits_FIXPOS_CB) {
     return true;
   }
 
   return aStyle.StyleEffects()->HasFilters();
 }
 
 bool nsStyleDisplay::
     IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() const {
@@ -171,17 +175,17 @@ bool nsStyleDisplay::IsFixedPosContainin
              "Any fixed-pos CB should also be an abs-pos CB");
   return true;
 }
 
 bool nsStyleDisplay::IsAbsPosContainingBlockForNonSVGTextFrames() const {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
   return IsAbsolutelyPositionedStyle() || IsRelativelyPositionedStyle() ||
-         (mWillChange.bits & mozilla::StyleWillChangeBits_ABSPOS_CB);
+         (mWillChangeBitField & mozilla::StyleWillChangeBits_ABSPOS_CB);
 }
 
 bool nsStyleDisplay::IsAbsPosContainingBlock(
     const nsIFrame* aContextFrame) const {
   mozilla::ComputedStyle* style = aContextFrame->Style();
   NS_ASSERTION(style->StyleDisplay() == this, "unexpected aContextFrame");
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -4,19 +4,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * A class used for intermediate representations of the -moz-transform property.
  */
 
 #include "nsStyleTransformMatrix.h"
+#include "nsCSSValue.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsSVGUtils.h"
+#include "nsCSSKeywords.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "gfxMatrix.h"
 #include "gfxQuaternion.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
@@ -120,70 +122,108 @@ void TransformReferenceBox::Init(const n
   mX = 0;
   mY = 0;
   mWidth = aDimensions.width;
   mHeight = aDimensions.height;
   mIsCached = true;
 }
 
 float ProcessTranslatePart(
-    const LengthPercentage& aValue, TransformReferenceBox* aRefBox,
+    const nsCSSValue& aValue, TransformReferenceBox* aRefBox,
     TransformReferenceBox::DimensionGetter aDimensionGetter) {
-  return aValue.ResolveToCSSPixelsWith([&] {
-    return aRefBox && !aRefBox->IsEmpty()
-               ? CSSPixel::FromAppUnits((aRefBox->*aDimensionGetter)())
-               : CSSCoord(0);
-  });
+  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;
 }
 
 /**
  * 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 StyleTransformOperation& aOp) {
-  const auto& matrix = aOp.AsMatrix();
+static void ProcessMatrix(Matrix4x4& aMatrix, const nsCSSValue::Array* aData,
+                          TransformReferenceBox& aRefBox) {
+  MOZ_ASSERT(aData->Count() == 7, "Invalid array!");
+
   gfxMatrix result;
 
-  result._11 = matrix.a;
-  result._12 = matrix.b;
-  result._21 = matrix.c;
-  result._22 = matrix.d;
-  result._31 = matrix.e;
-  result._32 = matrix.f;
+  /* 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);
 
   aMatrix = result * aMatrix;
 }
 
-static void ProcessMatrix3D(Matrix4x4& aMatrix,
-                            const StyleTransformOperation& aOp) {
+static void ProcessMatrix3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData,
+                            TransformReferenceBox& aRefBox) {
+  MOZ_ASSERT(aData->Count() == 17, "Invalid array!");
+
   Matrix4x4 temp;
 
-  const auto& matrix = aOp.AsMatrix3D();
+  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();
 
-  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;
+  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);
 
   aMatrix = temp * aMatrix;
 }
 
 // For accumulation for transform functions, |aOne| corresponds to |aB| and
 // |aTwo| corresponds to |aA| for StyleAnimationValue::Accumulate().
 class Accumulate {
  public:
@@ -271,299 +311,533 @@ class Interpolate {
     Matrix4x4 result;
     Servo_MatrixTransform_Operate(MatrixTransformOperator::Interpolate,
                                   &aMatrix1.components, &aMatrix2.components,
                                   aProgress, &result.components);
     return result;
   }
 };
 
+/**
+ * Calculate 2 matrices by decomposing them with Operator.
+ *
+ * @param aMatrix1   First matrix, using CSS pixel units.
+ * @param aMatrix2   Second matrix, using CSS pixel units.
+ * @param aProgress  Coefficient for the Operator.
+ */
+template <typename Operator>
+static Matrix4x4 OperateTransformMatrix(const Matrix4x4& aMatrix1,
+                                        const Matrix4x4& aMatrix2,
+                                        double aProgress) {
+  // Decompose both matrices
+
+  Point3D scale1(1, 1, 1), translate1;
+  Point4D perspective1(0, 0, 0, 1);
+  gfxQuaternion rotate1;
+  nsStyleTransformMatrix::ShearArray shear1{0.0f, 0.0f, 0.0f};
+
+  Point3D scale2(1, 1, 1), translate2;
+  Point4D perspective2(0, 0, 0, 1);
+  gfxQuaternion rotate2;
+  nsStyleTransformMatrix::ShearArray shear2{0.0f, 0.0f, 0.0f};
+
+  // Check if both matrices are decomposable.
+  bool wasDecomposed;
+  Matrix matrix2d1, matrix2d2;
+  if (aMatrix1.Is2D(&matrix2d1) && aMatrix2.Is2D(&matrix2d2)) {
+    wasDecomposed =
+        Decompose2DMatrix(matrix2d1, scale1, shear1, rotate1, translate1) &&
+        Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2);
+  } else {
+    wasDecomposed = Decompose3DMatrix(aMatrix1, scale1, shear1, rotate1,
+                                      translate1, perspective1) &&
+                    Decompose3DMatrix(aMatrix2, scale2, shear2, rotate2,
+                                      translate2, perspective2);
+  }
+
+  // Fallback to discrete operation if one of the matrices is not decomposable.
+  if (!wasDecomposed) {
+    return Operator::operateForFallback(aMatrix1, aMatrix2, aProgress);
+  }
+
+  Matrix4x4 result;
+
+  // Operate each of the pieces in response to |Operator|.
+  Point4D perspective =
+      Operator::operateForPerspective(perspective1, perspective2, aProgress);
+  result.SetTransposedVector(3, perspective);
+
+  Point3D translate = Operator::operate(translate1, translate2, aProgress);
+  result.PreTranslate(translate.x, translate.y, translate.z);
+
+  Matrix4x4 rotate = Operator::operateForRotate(rotate1, rotate2, aProgress);
+  if (!rotate.IsIdentity()) {
+    result = rotate * result;
+  }
+
+  // TODO: Would it be better to operate these as angles?
+  //       How do we convert back to angles?
+  float yzshear = Operator::operate(shear1[ShearType::YZ],
+                                    shear2[ShearType::YZ], aProgress);
+  if (yzshear != 0.0) {
+    result.SkewYZ(yzshear);
+  }
+
+  float xzshear = Operator::operate(shear1[ShearType::XZ],
+                                    shear2[ShearType::XZ], aProgress);
+  if (xzshear != 0.0) {
+    result.SkewXZ(xzshear);
+  }
+
+  float xyshear = Operator::operate(shear1[ShearType::XY],
+                                    shear2[ShearType::XY], aProgress);
+  if (xyshear != 0.0) {
+    result.SkewXY(xyshear);
+  }
+
+  Point3D scale = Operator::operateForScale(scale1, scale2, aProgress);
+  if (scale != Point3D(1.0, 1.0, 1.0)) {
+    result.PreScale(scale.x, scale.y, scale.z);
+  }
+
+  return result;
+}
+
+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 StyleTransform& aFrom,
-                                  const StyleTransform& aTo, float aProgress,
+                                  const nsCSSValue::Array* aData,
                                   TransformReferenceBox& aRefBox) {
-  float appUnitPerCSSPixel = AppUnitsPerCSSPixel();
-  Matrix4x4 matrix1 = ReadTransforms(aFrom, aRefBox, appUnitPerCSSPixel);
-  Matrix4x4 matrix2 = ReadTransforms(aTo, aRefBox, appUnitPerCSSPixel);
-  aMatrix = Operator::operateByServo(matrix1, matrix2, aProgress) * aMatrix;
+  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;
+    }
+
+    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) {
+    aMatrix =
+        OperateTransformMatrixByServo<Operator>(matrix1, matrix2, progress) *
+        aMatrix;
+    return;
+  }
+
+  aMatrix =
+      OperateTransformMatrix<Operator>(matrix1, matrix2, progress) * aMatrix;
 }
 
 /* Helper function to process two matrices that we need to interpolate between
  */
 void ProcessInterpolateMatrix(Matrix4x4& aMatrix,
-                              const StyleTransformOperation& aOp,
+                              const nsCSSValue::Array* aData,
                               TransformReferenceBox& aRefBox) {
-  const auto& args = aOp.AsInterpolateMatrix();
-  ProcessMatrixOperator<Interpolate>(aMatrix, args.from_list, args.to_list,
-                                     args.progress._0, aRefBox);
+  ProcessMatrixOperator<Interpolate>(aMatrix, aData, aRefBox);
 }
 
-void ProcessAccumulateMatrix(Matrix4x4& aMatrix,
-                             const StyleTransformOperation& aOp,
+void ProcessAccumulateMatrix(Matrix4x4& aMatrix, const nsCSSValue::Array* aData,
                              TransformReferenceBox& aRefBox) {
-  const auto& args = aOp.AsAccumulateMatrix();
-  ProcessMatrixOperator<Accumulate>(aMatrix, args.from_list, args.to_list,
-                                    args.count, aRefBox);
+  ProcessMatrixOperator<Accumulate>(aMatrix, aData, aRefBox);
 }
 
 /* Helper function to process a translatex function. */
 static void ProcessTranslateX(Matrix4x4& aMatrix,
-                              const LengthPercentage& aLength,
+                              const nsCSSValue::Array* aData,
                               TransformReferenceBox& aRefBox) {
+  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
+
   Point3D temp;
-  temp.x =
-      ProcessTranslatePart(aLength, &aRefBox, &TransformReferenceBox::Width);
+
+  temp.x = ProcessTranslatePart(aData->Item(1), &aRefBox,
+                                &TransformReferenceBox::Width);
   aMatrix.PreTranslate(temp);
 }
 
 /* Helper function to process a translatey function. */
 static void ProcessTranslateY(Matrix4x4& aMatrix,
-                              const LengthPercentage& aLength,
+                              const nsCSSValue::Array* aData,
                               TransformReferenceBox& aRefBox) {
+  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
+
   Point3D temp;
-  temp.y =
-      ProcessTranslatePart(aLength, &aRefBox, &TransformReferenceBox::Height);
+
+  temp.y = ProcessTranslatePart(aData->Item(1), &aRefBox,
+                                &TransformReferenceBox::Height);
   aMatrix.PreTranslate(temp);
 }
 
-static void ProcessTranslateZ(Matrix4x4& aMatrix, const Length& aLength) {
+static void ProcessTranslateZ(Matrix4x4& aMatrix,
+                              const nsCSSValue::Array* aData) {
+  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
+
   Point3D temp;
-  temp.z = aLength.ToCSSPixels();
+
+  temp.z = ProcessTranslatePart(aData->Item(1), nullptr);
   aMatrix.PreTranslate(temp);
 }
 
 /* Helper function to process a translate function. */
-static void ProcessTranslate(Matrix4x4& aMatrix, const LengthPercentage& aX,
-                             const LengthPercentage& aY,
+static void ProcessTranslate(Matrix4x4& aMatrix, const nsCSSValue::Array* aData,
                              TransformReferenceBox& aRefBox) {
+  MOZ_ASSERT(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");
+
   Point3D temp;
-  temp.x = ProcessTranslatePart(aX, &aRefBox, &TransformReferenceBox::Width);
-  temp.y = ProcessTranslatePart(aY, &aRefBox, &TransformReferenceBox::Height);
+
+  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);
+  }
   aMatrix.PreTranslate(temp);
 }
 
-static void ProcessTranslate3D(Matrix4x4& aMatrix, const LengthPercentage& aX,
-                               const LengthPercentage& aY, const Length& aZ,
+static void ProcessTranslate3D(Matrix4x4& aMatrix,
+                               const nsCSSValue::Array* aData,
                                TransformReferenceBox& aRefBox) {
+  MOZ_ASSERT(aData->Count() == 4, "Invalid array!");
+
   Point3D temp;
 
-  temp.x = ProcessTranslatePart(aX, &aRefBox, &TransformReferenceBox::Width);
-  temp.y = ProcessTranslatePart(aY, &aRefBox, &TransformReferenceBox::Height);
-  temp.z = aZ.ToCSSPixels();
+  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);
 
   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);
 }
 
-static void ProcessScale3D(Matrix4x4& aMatrix,
-                           const StyleTransformOperation& aOp) {
-  const auto& scale = aOp.AsScale3D();
-  ProcessScaleHelper(aMatrix, scale._0, scale._1, scale._2);
+/* 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);
 }
 
 /* Helper function that, given a set of angles, constructs the appropriate
  * skew matrix.
  */
-static void ProcessSkewHelper(Matrix4x4& aMatrix, const StyleAngle& aXAngle,
-                              const StyleAngle& aYAngle) {
-  aMatrix.SkewXY(aXAngle.ToRadians(), aYAngle.ToRadians());
+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 ProcessRotate3D(Matrix4x4& aMatrix, float aX, float aY, float aZ,
-                            const StyleAngle& aAngle) {
+/* 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();
+
   Matrix4x4 temp;
-  temp.SetRotateAxisAngle(aX, aY, aZ, aAngle.ToRadians());
+  temp.SetRotateAxisAngle(x, y, z, theta);
+
   aMatrix = temp * aMatrix;
 }
 
-static void ProcessPerspective(Matrix4x4& aMatrix, const Length& aLength) {
-  float depth = aLength.ToCSSPixels();
+static void ProcessPerspective(Matrix4x4& aMatrix,
+                               const nsCSSValue::Array* aData) {
+  MOZ_ASSERT(aData->Count() == 2, "Invalid array!");
+
+  float depth = ProcessTranslatePart(aData->Item(1), nullptr);
   ApplyPerspectiveToMatrix(aMatrix, depth);
 }
 
+/**
+ * SetToTransformFunction is essentially a giant switch statement that fans
+ * out to many smaller helper functions.
+ */
 static void MatrixForTransformFunction(Matrix4x4& aMatrix,
-                                       const StyleTransformOperation& aOp,
+                                       const nsCSSValue::Array* aData,
                                        TransformReferenceBox& aRefBox) {
+  MOZ_ASSERT(aData, "Why did you want to get data from a null array?");
+
   /* Get the keyword for the transform. */
-  switch (aOp.tag) {
-    case StyleTransformOperation::Tag::TranslateX:
-      ProcessTranslateX(aMatrix, aOp.AsTranslateX(), aRefBox);
+  switch (TransformFunctionOf(aData)) {
+    case eCSSKeyword_translatex:
+      ProcessTranslateX(aMatrix, aData, aRefBox);
       break;
-    case StyleTransformOperation::Tag::TranslateY:
-      ProcessTranslateY(aMatrix, aOp.AsTranslateY(), aRefBox);
-      break;
-    case StyleTransformOperation::Tag::TranslateZ:
-      ProcessTranslateZ(aMatrix, aOp.AsTranslateZ());
-      break;
-    case StyleTransformOperation::Tag::Translate:
-      ProcessTranslate(aMatrix, aOp.AsTranslate()._0, aOp.AsTranslate()._1,
-                       aRefBox);
+    case eCSSKeyword_translatey:
+      ProcessTranslateY(aMatrix, aData, aRefBox);
       break;
-    case StyleTransformOperation::Tag::Translate3D:
-      return ProcessTranslate3D(aMatrix, aOp.AsTranslate3D()._0,
-                                aOp.AsTranslate3D()._1, aOp.AsTranslate3D()._2,
-                                aRefBox);
+    case eCSSKeyword_translatez:
+      ProcessTranslateZ(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::ScaleX:
-      ProcessScaleHelper(aMatrix, aOp.AsScaleX(), 1.0f, 1.0f);
+    case eCSSKeyword_translate:
+      ProcessTranslate(aMatrix, aData, aRefBox);
+      break;
+    case eCSSKeyword_translate3d:
+      ProcessTranslate3D(aMatrix, aData, aRefBox);
       break;
-    case StyleTransformOperation::Tag::ScaleY:
-      ProcessScaleHelper(aMatrix, 1.0f, aOp.AsScaleY(), 1.0f);
+    case eCSSKeyword_scalex:
+      ProcessScaleX(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::ScaleZ:
-      ProcessScaleHelper(aMatrix, 1.0f, 1.0f, aOp.AsScaleZ());
+    case eCSSKeyword_scaley:
+      ProcessScaleY(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::Scale:
-      ProcessScaleHelper(aMatrix, aOp.AsScale()._0, aOp.AsScale()._1, 1.0f);
+    case eCSSKeyword_scalez:
+      ProcessScaleZ(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::Scale3D:
-      ProcessScale3D(aMatrix, aOp);
+    case eCSSKeyword_scale:
+      ProcessScale(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::SkewX:
-      ProcessSkewHelper(aMatrix, aOp.AsSkewX(), StyleAngle::Zero());
+    case eCSSKeyword_scale3d:
+      ProcessScale3D(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::SkewY:
-      ProcessSkewHelper(aMatrix, StyleAngle::Zero(), aOp.AsSkewY());
+    case eCSSKeyword_skewx:
+      ProcessSkewX(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::Skew:
-      ProcessSkewHelper(aMatrix, aOp.AsSkew()._0, aOp.AsSkew()._1);
+    case eCSSKeyword_skewy:
+      ProcessSkewY(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::RotateX:
-      aMatrix.RotateX(aOp.AsRotateX().ToRadians());
+    case eCSSKeyword_skew:
+      ProcessSkew(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::RotateY:
-      aMatrix.RotateY(aOp.AsRotateY().ToRadians());
+    case eCSSKeyword_rotatex:
+      ProcessRotateX(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::RotateZ:
-      aMatrix.RotateZ(aOp.AsRotateZ().ToRadians());
+    case eCSSKeyword_rotatey:
+      ProcessRotateY(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::Rotate:
-      aMatrix.RotateZ(aOp.AsRotate().ToRadians());
+    case eCSSKeyword_rotatez:
+      MOZ_FALLTHROUGH;
+    case eCSSKeyword_rotate:
+      ProcessRotateZ(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::Rotate3D:
-      ProcessRotate3D(aMatrix, aOp.AsRotate3D()._0, aOp.AsRotate3D()._1,
-                      aOp.AsRotate3D()._2, aOp.AsRotate3D()._3);
+    case eCSSKeyword_rotate3d:
+      ProcessRotate3D(aMatrix, aData);
       break;
-    case StyleTransformOperation::Tag::Matrix:
-      ProcessMatrix(aMatrix, aOp);
+    case eCSSKeyword_matrix:
+      ProcessMatrix(aMatrix, aData, aRefBox);
       break;
-    case StyleTransformOperation::Tag::Matrix3D:
-      ProcessMatrix3D(aMatrix, aOp);
+    case eCSSKeyword_matrix3d:
+      ProcessMatrix3D(aMatrix, aData, aRefBox);
       break;
-    case StyleTransformOperation::Tag::InterpolateMatrix:
-      ProcessInterpolateMatrix(aMatrix, aOp, aRefBox);
+    case eCSSKeyword_interpolatematrix:
+      ProcessMatrixOperator<Interpolate>(aMatrix, aData, aRefBox);
       break;
-    case StyleTransformOperation::Tag::AccumulateMatrix:
-      ProcessAccumulateMatrix(aMatrix, aOp, aRefBox);
+    case eCSSKeyword_accumulatematrix:
+      ProcessMatrixOperator<Accumulate>(aMatrix, aData, aRefBox);
       break;
-    case StyleTransformOperation::Tag::Perspective:
-      ProcessPerspective(aMatrix, aOp.AsPerspective());
+    case eCSSKeyword_perspective:
+      ProcessPerspective(aMatrix, aData);
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown transform function!");
   }
 }
 
-Matrix4x4 ReadTransforms(const StyleTransform& aTransform,
+/**
+ * Return the transform function, as an nsCSSKeyword, for the given
+ * nsCSSValue::Array from a transform list.
+ */
+nsCSSKeyword TransformFunctionOf(const nsCSSValue::Array* aData) {
+  MOZ_ASSERT(aData->Item(0).GetUnit() == eCSSUnit_Enumerated);
+  return aData->Item(0).GetKeywordValue();
+}
+
+void SetIdentityMatrix(nsCSSValue::Array* aMatrix) {
+  MOZ_ASSERT(aMatrix, "aMatrix should be non-null");
+
+  nsCSSKeyword tfunc = TransformFunctionOf(aMatrix);
+  MOZ_ASSERT(tfunc == eCSSKeyword_matrix || tfunc == eCSSKeyword_matrix3d,
+             "Only accept matrix and matrix3d");
+
+  if (tfunc == eCSSKeyword_matrix) {
+    MOZ_ASSERT(aMatrix->Count() == 7, "Invalid matrix");
+    Matrix m;
+    for (size_t i = 0; i < 6; ++i) {
+      aMatrix->Item(i + 1).SetFloatValue(m.components[i], eCSSUnit_Number);
+    }
+    return;
+  }
+
+  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,
                          TransformReferenceBox& aRefBox,
                          float aAppUnitsPerMatrixUnit) {
   Matrix4x4 result;
-
-  for (const StyleTransformOperation& op : aTransform.Operations()) {
-    MatrixForTransformFunction(result, op, aRefBox);
-  }
+  ReadTransformsImpl(result, aList, aRefBox);
 
   float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
   result.PreScale(1 / scale, 1 / scale, 1 / scale);
   result.PostScale(scale, scale, scale);
 
   return result;
 }
 
-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,
+Matrix4x4 ReadTransforms(const nsCSSValueList* aIndividualTransforms,
                          const Maybe<MotionPathData>& aMotion,
-                         const StyleTransform& aTransform,
+                         const nsCSSValueList* aTransform,
                          TransformReferenceBox& aRefBox,
                          float aAppUnitsPerMatrixUnit) {
   Matrix4x4 result;
 
-  ProcessTranslate(result, aTranslate, aRefBox);
-  ProcessRotate(result, aRotate, aRefBox);
-  ProcessScale(result, aScale, aRefBox);
+  if (aIndividualTransforms) {
+    ReadTransformsImpl(result, aIndividualTransforms, 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);
     }
   }
 
-  for (const StyleTransformOperation& op : aTransform.Operations()) {
-    MatrixForTransformFunction(result, op, aRefBox);
+  if (aTransform) {
+    ReadTransformsImpl(result, aTransform, aRefBox);
   }
 
   float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
   result.PreScale(1 / scale, 1 / scale, 1 / scale);
   result.PostScale(scale, scale, scale);
 
   return result;
 }
@@ -770,17 +1044,16 @@ 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;
@@ -856,9 +1129,46 @@ 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
@@ -9,16 +9,17 @@
  */
 
 #ifndef nsStyleTransformMatrix_h_
 #define nsStyleTransformMatrix_h_
 
 #include "gfxPoint.h"
 #include "mozilla/gfx/Matrix.h"
 #include "mozilla/EnumeratedArray.h"
+#include "nsCSSValue.h"
 #include "nsSize.h"
 
 #include <limits>
 
 class nsIFrame;
 class nsPresContext;
 struct gfxQuaternion;
 struct nsRect;
@@ -138,47 +139,56 @@ class MOZ_STACK_CLASS TransformReference
 
   void EnsureDimensionsAreCached();
 
   const nsIFrame* mFrame;
   nscoord mX, mY, mWidth, mHeight;
   bool mIsCached;
 };
 
+/**
+ * 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 mozilla::LengthPercentage& aValue, TransformReferenceBox* aRefBox,
+    const nsCSSValue& aValue, TransformReferenceBox* aRefBox,
     TransformReferenceBox::DimensionGetter aDimensionGetter = nullptr);
 
 void ProcessInterpolateMatrix(mozilla::gfx::Matrix4x4& aMatrix,
-                              const mozilla::StyleTransformOperation& aOp,
+                              const nsCSSValue::Array* aData,
                               TransformReferenceBox& aBounds);
 
 void ProcessAccumulateMatrix(mozilla::gfx::Matrix4x4& aMatrix,
-                             const mozilla::StyleTransformOperation& aOp,
+                             const nsCSSValue::Array* aData,
                              TransformReferenceBox& aBounds);
 
 /**
- * Given a StyleTransform containing transform functions, returns a matrix
- * containing the value of those functions.
+ * Given an nsCSSValueList containing -moz-transform functions,
+ * returns a matrix containing the value of those functions.
  *
- * @param aList the transform operation list.
+ * @param aData The nsCSSValueList containing the transform functions
  * @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 mozilla::StyleTransform& aList,
+mozilla::gfx::Matrix4x4 ReadTransforms(const nsCSSValueList* 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 mozilla::StyleTranslate&, const mozilla::StyleRotate&,
-    const mozilla::StyleScale&,
+    const nsCSSValueList* aIndividualTransforms,
     const mozilla::Maybe<mozilla::MotionPathData>& aMotion,
-    const mozilla::StyleTransform&, TransformReferenceBox& aRefBox,
+    const nsCSSValueList* aTransform, 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,
@@ -208,11 +218,16 @@ 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/SVGImageContext.cpp
+++ b/layout/svg/SVGImageContext.cpp
@@ -40,31 +40,31 @@ void SVGImageContext::MaybeStoreContextP
     return;
   }
 
   bool haveContextPaint = false;
 
   RefPtr<SVGEmbeddingContextPaint> contextPaint =
       new SVGEmbeddingContextPaint();
 
-  if ((style->mMozContextProperties.bits & StyleContextPropertyBits_FILL) &&
+  if ((style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_FILL) &&
       style->mFill.Type() == eStyleSVGPaintType_Color) {
     haveContextPaint = true;
     contextPaint->SetFill(style->mFill.GetColor(aFromComputedStyle));
   }
-  if ((style->mMozContextProperties.bits & StyleContextPropertyBits_STROKE) &&
+  if ((style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_STROKE) &&
       style->mStroke.Type() == eStyleSVGPaintType_Color) {
     haveContextPaint = true;
     contextPaint->SetStroke(style->mStroke.GetColor(aFromComputedStyle));
   }
-  if (style->mMozContextProperties.bits & StyleContextPropertyBits_FILL_OPACITY) {
+  if (style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY) {
     haveContextPaint = true;
     contextPaint->SetFillOpacity(style->mFillOpacity);
   }
-  if (style->mMozContextProperties.bits & StyleContextPropertyBits_STROKE_OPACITY) {
+  if (style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY) {
     haveContextPaint = true;
     contextPaint->SetStrokeOpacity(style->mStrokeOpacity);
   }
 
   if (haveContextPaint) {
     if (!aContext) {
       aContext.emplace();
     }
--- a/layout/svg/nsCSSFilterInstance.cpp
+++ b/layout/svg/nsCSSFilterInstance.cpp
@@ -164,31 +164,36 @@ nsresult nsCSSFilterInstance::SetAttribu
   atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForDropShadow(
     FilterPrimitiveDescription& aDescr) {
-  const auto& shadow = mFilter.GetDropShadow();
+  nsCSSShadowArray* shadows = mFilter.GetDropShadow();
+  if (!shadows || shadows->Length() != 1) {
+    MOZ_ASSERT_UNREACHABLE("Exactly one drop shadow should have been parsed.");
+    return NS_ERROR_FAILURE;
+  }
 
   DropShadowAttributes atts;
+  nsCSSShadowItem* shadow = shadows->ShadowAt(0);
 
   // Set drop shadow blur radius.
-  Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow.blur.ToAppUnits());
+  Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow->mRadius);
   atts.mStdDeviation = radiusInFilterSpace;
 
   // Set offset.
-  IntPoint offsetInFilterSpace = OffsetToFilterSpace(
-      shadow.horizontal.ToAppUnits(), shadow.vertical.ToAppUnits());
+  IntPoint offsetInFilterSpace =
+      OffsetToFilterSpace(shadow->mXOffset, shadow->mYOffset);
   atts.mOffset = offsetInFilterSpace;
 
   // Set color. If unspecified, use the CSS color property.
-  nscolor shadowColor = shadow.color.CalcColor(mShadowFallbackColor);
+  nscolor shadowColor = shadow->mColor.CalcColor(mShadowFallbackColor);
   atts.mColor = ToAttributeColor(shadowColor);
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForGrayscale(
     FilterPrimitiveDescription& aDescr) {
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1719,19 +1719,22 @@ gfxMatrix nsSVGUtils::GetTransformMatrix
       properties.mToTransformOrigin.z};
 
   Matrix svgTransform;
   Matrix4x4 trans;
   (void)aFrame->IsSVGTransformed(&svgTransform);
 
   if (properties.HasTransform()) {
     trans = nsStyleTransformMatrix::ReadTransforms(
-        properties.mTranslate, properties.mRotate, properties.mScale,
-        properties.mMotion, properties.mTransform, refBox,
-        AppUnitsPerCSSPixel());
+        properties.mIndividualTransformList
+            ? properties.mIndividualTransformList->mHead
+            : nullptr,
+        properties.mMotion,
+        properties.mTransformList ? properties.mTransformList->mHead : nullptr,
+        refBox, AppUnitsPerCSSPixel());
   } else {
     trans = Matrix4x4::From2D(svgTransform);
   }
 
   trans.ChangeBasis(svgTransformOrigin);
 
   Matrix mm;
   trans.ProjectTo2D();
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -439,17 +439,17 @@ bool nsTableCellFrame::ShouldPaintBackgr
   return ShouldPaintBordersAndBackgrounds();
 }
 
 void nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                         const nsDisplayListSet& aLists) {
   DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
   if (ShouldPaintBordersAndBackgrounds()) {
     // display outset box-shadows if we need to.
-    bool hasBoxShadow = !StyleEffects()->mBoxShadow.IsEmpty();
+    bool hasBoxShadow = !!StyleEffects()->mBoxShadow;
     if (hasBoxShadow) {
       aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowOuter>(
           aBuilder, this);
     }
 
     // display background if we need to.
     if (aBuilder->IsForEventDelivery() ||
         !StyleBackground()->IsTransparent(this) ||
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1405,17 +1405,17 @@ void nsTableFrame::DisplayGenericTablePa
   }
 
   if (isVisible) {
     // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
     // just because we're visible?  Or should it depend on the cell visibility
     // when we're not the whole table?
 
     // Paint the outset box-shadows for the table frames
-    if (!aFrame->StyleEffects()->mBoxShadow.IsEmpty()) {
+    if (aFrame->StyleEffects()->mBoxShadow) {
       aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowOuter>(
           aBuilder, aFrame);
     }
   }
 
   // Background visibility for rows, rowgroups, columns, colgroups depends on
   // the visibility of the _cell_, not of the row/col(group).
   if (aFrame->IsTableRowGroupFrame()) {
@@ -1478,17 +1478,17 @@ void nsTableFrame::DisplayGenericTablePa
   }
 
   if (isVisible) {
     // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
     // just because we're visible?  Or should it depend on the cell visibility
     // when we're not the whole table?
 
     // Paint the inset box-shadows for the table frames
-    if (!aFrame->StyleEffects()->mBoxShadow.IsEmpty()) {
+    if (aFrame->StyleEffects()->mBoxShadow) {
       aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowInner>(
           aBuilder, aFrame);
     }
   }
 
   aFrame->DisplayOutline(aBuilder, aLists);
 
   aTraversal(aBuilder, aFrame, aLists);
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -943,29 +943,29 @@ nsTextBoxFrame::DoXULLayout(nsBoxLayoutS
   textRect = tr.GetPhysicalRect(wm, GetSize());
 
   // Our scrollable overflow is our bounds; our visual overflow may
   // extend beyond that.
   nsRect visualBounds;
   visualBounds.UnionRect(scrollBounds, textRect);
   nsOverflowAreas overflow(visualBounds, scrollBounds);
 
-  if (textStyle->HasTextShadow()) {
+  if (textStyle->mTextShadow) {
     // text-shadow extends our visual but not scrollable bounds
     nsRect& vis = overflow.VisualOverflow();
     vis.UnionRect(vis,
                   nsLayoutUtils::GetTextShadowRectsUnion(mTextDrawRect, this));
   }
   FinishAndStoreOverflow(overflow, GetSize());
 
   return rv;
 }
 
 nsRect nsTextBoxFrame::GetComponentAlphaBounds() const {
-  if (StyleText()->HasTextShadow()) {
+  if (StyleText()->mTextShadow) {
     return GetVisualOverflowRectRelativeToSelf();
   }
   return mTextDrawRect;
 }
 
 bool nsTextBoxFrame::ComputesOwnOverflowArea() { return true; }
 
 /* virtual */
--- a/servo/components/style/counter_style/mod.rs
+++ b/servo/components/style/counter_style/mod.rs
@@ -14,16 +14,17 @@ use crate::values::specified::Integer;
 use crate::values::CustomIdent;
 use crate::Atom;
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser};
 use cssparser::{CowRcStr, Parser, SourceLocation, Token};
 use selectors::parser::SelectorParseErrorKind;
 use std::fmt::{self, Write};
 use std::mem;
 use std::num::Wrapping;
+use std::ops::Range;
 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
 use style_traits::{StyleParseErrorKind, ToCss};
 
 /// Parse a counter style name reference.
 ///
 /// This allows the reserved counter style names "decimal" and "disc".
 pub fn parse_counter_style_name<'i, 't>(
     input: &mut Parser<'i, 't>,
@@ -255,17 +256,17 @@ counter_style_descriptors! {
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-prefix>
     "prefix" prefix / set_prefix [_]: Symbol,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-suffix>
     "suffix" suffix / set_suffix [_]: Symbol,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
-    "range" range / set_range [_]: CounterRanges,
+    "range" range / set_range [_]: Ranges,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-pad>
     "pad" pad / set_pad [_]: Pad,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-fallback>
     "fallback" fallback / set_fallback [_]: Fallback,
 
     /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
@@ -365,17 +366,17 @@ impl Parse for System {
         try_match_ident_ignore_ascii_case! { input,
             "cyclic" => Ok(System::Cyclic),
             "numeric" => Ok(System::Numeric),
             "alphabetic" => Ok(System::Alphabetic),
             "symbolic" => Ok(System::Symbolic),
             "additive" => Ok(System::Additive),
             "fixed" => {
                 let first_symbol_value = input.try(|i| Integer::parse(context, i)).ok();
-                Ok(System::Fixed { first_symbol_value })
+                Ok(System::Fixed { first_symbol_value: first_symbol_value })
             }
             "extends" => {
                 let other = parse_counter_style_name(input)?;
                 Ok(System::Extends(other))
             }
         }
     }
 }
@@ -403,35 +404,36 @@ impl ToCss for System {
                 dest.write_str("extends ")?;
                 other.to_css(dest)
             },
         }
     }
 }
 
 /// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol>
-#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss, ToShmem, MallocSizeOf)]
+#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
+#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss, ToShmem)]
 pub enum Symbol {
     /// <string>
-    String(crate::OwnedStr),
+    String(String),
     /// <custom-ident>
     Ident(CustomIdent),
     // Not implemented:
     // /// <image>
     // Image(Image),
 }
 
 impl Parse for Symbol {
     fn parse<'i, 't>(
         _context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let location = input.current_source_location();
         match *input.next()? {
-            Token::QuotedString(ref s) => Ok(Symbol::String(s.as_ref().to_owned().into())),
+            Token::QuotedString(ref s) => Ok(Symbol::String(s.as_ref().to_owned())),
             Token::Ident(ref s) => Ok(Symbol::Ident(CustomIdent::from_ident(location, s, &[])?)),
             ref t => Err(location.new_unexpected_token_error(t.clone())),
         }
     }
 }
 
 impl Symbol {
     /// Returns whether this symbol is allowed in symbols() function.
@@ -456,85 +458,100 @@ impl Parse for Negative {
         Ok(Negative(
             Symbol::parse(context, input)?,
             input.try(|input| Symbol::parse(context, input)).ok(),
         ))
     }
 }
 
 /// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
-#[derive(Clone, Debug, ToShmem, ToCss)]
-pub struct CounterRange {
-    /// The start of the range.
-    pub start: CounterBound,
-    /// The end of the range.
-    pub end: CounterBound,
-}
+///
+/// Empty Vec represents 'auto'
+#[derive(Clone, Debug, ToShmem)]
+pub struct Ranges(pub Vec<Range<CounterBound>>);
 
-/// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
-///
-/// Empty represents 'auto'
-#[derive(Clone, Debug, ToShmem, ToCss)]
-#[css(comma)]
-pub struct CounterRanges(
-    #[css(iterable, if_empty = "auto")]
-    pub crate::OwnedSlice<CounterRange>,
-);
-
-/// A bound found in `CounterRanges`.
+/// A bound found in `Ranges`.
 #[derive(Clone, Copy, Debug, ToCss, ToShmem)]
 pub enum CounterBound {
     /// An integer bound.
     Integer(Integer),
     /// The infinite bound.
     Infinite,
 }
 
-impl Parse for CounterRanges {
+impl Parse for Ranges {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         if input
             .try(|input| input.expect_ident_matching("auto"))
             .is_ok()
         {
-            return Ok(CounterRanges(Default::default()));
+            Ok(Ranges(Vec::new()))
+        } else {
+            input
+                .parse_comma_separated(|input| {
+                    let opt_start = parse_bound(context, input)?;
+                    let opt_end = parse_bound(context, input)?;
+                    if let (CounterBound::Integer(start), CounterBound::Integer(end)) =
+                        (opt_start, opt_end)
+                    {
+                        if start > end {
+                            return Err(
+                                input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
+                            );
+                        }
+                    }
+                    Ok(opt_start..opt_end)
+                })
+                .map(Ranges)
         }
-
-        let ranges = input.parse_comma_separated(|input| {
-            let start = parse_bound(context, input)?;
-            let end = parse_bound(context, input)?;
-            if let (CounterBound::Integer(start), CounterBound::Integer(end)) =
-                (start, end)
-            {
-                if start > end {
-                    return Err(
-                        input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
-                    );
-                }
-            }
-            Ok(CounterRange { start, end })
-        })?;
-
-        Ok(CounterRanges(ranges.into()))
     }
 }
 
 fn parse_bound<'i, 't>(
     context: &ParserContext,
     input: &mut Parser<'i, 't>,
 ) -> Result<CounterBound, ParseError<'i>> {
     if let Ok(integer) = input.try(|input| Integer::parse(context, input)) {
         return Ok(CounterBound::Integer(integer));
     }
     input.expect_ident_matching("infinite")?;
     Ok(CounterBound::Infinite)
 }
 
+impl ToCss for Ranges {
+    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+    where
+        W: Write,
+    {
+        let mut iter = self.0.iter();
+        if let Some(first) = iter.next() {
+            range_to_css(first, dest)?;
+            for item in iter {
+                dest.write_str(", ")?;
+                range_to_css(item, dest)?;
+            }
+            Ok(())
+        } else {
+            dest.write_str("auto")
+        }
+    }
+}
+
+fn range_to_css<W>(range: &Range<CounterBound>, dest: &mut CssWriter<W>) -> fmt::Result
+where
+    W: Write,
+{
+    range.start.to_css(dest)?;
+    dest.write_char(' ')?;
+    range.end.to_css(dest)
+}
+
 /// <https://drafts.csswg.org/css-counter-styles/#counter-style-pad>
 #[derive(Clone, Debug, ToCss, ToShmem)]
 pub struct Pad(pub Integer, pub Symbol);
 
 impl Parse for Pad {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
@@ -550,59 +567,63 @@ impl Parse for Pad {
 #[derive(Clone, Debug, ToCss, ToShmem)]
 pub struct Fallback(pub CustomIdent);
 
 impl Parse for Fallback {
     fn parse<'i, 't>(
         _context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
-        Ok(Fallback(parse_counter_style_name(input)?))
+        parse_counter_style_name(input).map(Fallback)
     }
 }
 
 /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
-#[derive(Clone, Debug, Eq, PartialEq, MallocSizeOf, ToComputedValue, ToCss, ToShmem)]
-pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>);
+#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
+#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss, ToShmem)]
+pub struct Symbols(#[css(iterable)] pub Vec<Symbol>);
 
 impl Parse for Symbols {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let mut symbols = Vec::new();
-        while let Ok(s) = input.try(|input| Symbol::parse(context, input)) {
-            symbols.push(s);
+        loop {
+            if let Ok(s) = input.try(|input| Symbol::parse(context, input)) {
+                symbols.push(s)
+            } else {
+                if symbols.is_empty() {
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+                } else {
+                    return Ok(Symbols(symbols));
+                }
+            }
         }
-        if symbols.is_empty() {
-            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
-        }
-        Ok(Symbols(symbols.into()))
     }
 }
 
 /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols>
 #[derive(Clone, Debug, ToCss, ToShmem)]
-#[css(comma)]
-pub struct AdditiveSymbols(#[css(iterable)] pub crate::OwnedSlice<AdditiveTuple>);
+pub struct AdditiveSymbols(pub Vec<AdditiveTuple>);
 
 impl Parse for AdditiveSymbols {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let tuples = Vec::<AdditiveTuple>::parse(context, input)?;
         // FIXME maybe? https://github.com/w3c/csswg-drafts/issues/1220
         if tuples
             .windows(2)
             .any(|window| window[0].weight <= window[1].weight)
         {
             return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
-        Ok(AdditiveSymbols(tuples.into()))
+        Ok(AdditiveSymbols(tuples))
     }
 }
 
 /// <integer> && <symbol>
 #[derive(Clone, Debug, ToCss, ToShmem)]
 pub struct AdditiveTuple {
     /// <integer>
     pub weight: Integer,
@@ -617,17 +638,20 @@ impl OneOrMoreSeparated for AdditiveTupl
 impl Parse for AdditiveTuple {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let symbol = input.try(|input| Symbol::parse(context, input));
         let weight = Integer::parse_non_negative(context, input)?;
         let symbol = symbol.or_else(|_| Symbol::parse(context, input))?;
-        Ok(Self { weight, symbol })
+        Ok(AdditiveTuple {
+            weight: weight,
+            symbol: symbol,
+        })
     }
 }
 
 /// <https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as>
 #[derive(Clone, Debug, ToCss, ToShmem)]
 pub enum SpeakAs {
     /// auto
     Auto,
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -18,32 +18,34 @@ use crate::gecko_bindings::structs::RawS
 use crate::gecko_bindings::structs::RawServoImportRule;
 use crate::gecko_bindings::structs::RawServoKeyframe;
 use crate::gecko_bindings::structs::RawServoKeyframesRule;
 use crate::gecko_bindings::structs::RawServoMediaList;
 use crate::gecko_bindings::structs::RawServoMediaRule;
 use crate::gecko_bindings::structs::RawServoMozDocumentRule;
 use crate::gecko_bindings::structs::RawServoNamespaceRule;
 use crate::gecko_bindings::structs::RawServoPageRule;
+use crate::gecko_bindings::structs::RawServoQuotes;
 use crate::gecko_bindings::structs::RawServoStyleRule;
 use crate::gecko_bindings::structs::RawServoStyleSheetContents;
 use crate::gecko_bindings::structs::RawServoSupportsRule;
 use crate::gecko_bindings::structs::ServoCssRules;
 use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong};
 use crate::media_queries::MediaList;
 use crate::properties::animated_properties::AnimationValue;
 use crate::properties::{ComputedValues, PropertyDeclarationBlock};
 use crate::shared_lock::Locked;
 use crate::stylesheets::keyframes_rule::Keyframe;
 use crate::stylesheets::{CounterStyleRule, CssRules, FontFaceRule, FontFeatureValuesRule};
 use crate::stylesheets::{DocumentRule, ImportRule, KeyframesRule, MediaRule};
 use crate::stylesheets::{NamespaceRule, PageRule};
 use crate::stylesheets::{StyleRule, StylesheetContents, SupportsRule};
 use servo_arc::{Arc, ArcBorrow};
 use std::{mem, ptr};
+use values::computed::QuotePair;
 
 macro_rules! impl_arc_ffi {
     ($servo_type:ty => $gecko_type:ty[$addref:ident, $release:ident]) => {
         unsafe impl HasFFI for $servo_type {
             type FFIType = $gecko_type;
         }
         unsafe impl HasArcFFI for $servo_type {}
 
@@ -108,16 +110,19 @@ impl_arc_ffi!(Locked<FontFaceRule> => Ra
               [Servo_FontFaceRule_AddRef, Servo_FontFaceRule_Release]);
 
 impl_arc_ffi!(Locked<CounterStyleRule> => RawServoCounterStyleRule
               [Servo_CounterStyleRule_AddRef, Servo_CounterStyleRule_Release]);
 
 impl_arc_ffi!(CssUrlData => RawServoCssUrlData
               [Servo_CssUrlData_AddRef, Servo_CssUrlData_Release]);
 
+impl_arc_ffi!(Box<[QuotePair]> => RawServoQuotes
+              [Servo_Quotes_AddRef, Servo_Quotes_Release]);
+
 // ComputedStyle is not an opaque type on any side of FFI.
 // This means that doing the HasArcFFI type trick is actually unsound,
 // since it gives us a way to construct an Arc<ComputedStyle> from
 // an &ComputedStyle, which in general is not allowed. So we
 // implement the restricted set of arc type functionality we need.
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_ComputedStyle_AddRef(obj: &ComputedValues) {
--- a/servo/components/style/gecko/mod.rs
+++ b/servo/components/style/gecko/mod.rs
@@ -12,15 +12,16 @@ pub mod boxed_types;
 pub mod conversions;
 pub mod data;
 pub mod media_features;
 pub mod media_queries;
 #[cfg(feature = "gecko_profiler")]
 pub mod profiler;
 pub mod pseudo_element;
 pub mod restyle_damage;
+pub mod rules;
 pub mod selector_parser;
 pub mod snapshot;
 pub mod snapshot_helpers;
 pub mod traversal;
 pub mod url;
 pub mod values;
 pub mod wrapper;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/gecko/rules.rs
@@ -0,0 +1,135 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Bindings for CSS Rule objects
+
+use crate::counter_style::{self, CounterBound};
+use crate::gecko_bindings::structs::{self, nsCSSValue};
+use crate::gecko_bindings::sugar::ns_css_value::ToNsCssValue;
+
+impl<'a> ToNsCssValue for &'a counter_style::System {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        use crate::counter_style::System::*;
+        match *self {
+            Cyclic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC as i32),
+            Numeric => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC as i32),
+            Alphabetic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC as i32),
+            Symbolic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC as i32),
+            Additive => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_ADDITIVE as i32),
+            Fixed {
+                ref first_symbol_value,
+            } => {
+                let mut a = nsCSSValue::null();
+                let mut b = nsCSSValue::null();
+                a.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_FIXED as i32);
+                b.set_integer(first_symbol_value.map_or(1, |v| v.value()));
+                nscssvalue.set_pair(&a, &b);
+            },
+            Extends(ref other) => {
+                let mut a = nsCSSValue::null();
+                let mut b = nsCSSValue::null();
+                a.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_EXTENDS as i32);
+                b.set_atom_ident(other.0.clone());
+                nscssvalue.set_pair(&a, &b);
+            },
+        }
+    }
+}
+
+impl<'a> ToNsCssValue for &'a counter_style::Negative {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        if let Some(ref second) = self.1 {
+            let mut a = nsCSSValue::null();
+            let mut b = nsCSSValue::null();
+            a.set_from(&self.0);
+            b.set_from(second);
+            nscssvalue.set_pair(&a, &b);
+        } else {
+            nscssvalue.set_from(&self.0)
+        }
+    }
+}
+
+impl<'a> ToNsCssValue for &'a counter_style::Symbol {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        match *self {
+            counter_style::Symbol::String(ref s) => nscssvalue.set_string(s),
+            counter_style::Symbol::Ident(ref s) => nscssvalue.set_ident_from_atom(&s.0),
+        }
+    }
+}
+
+impl<'a> ToNsCssValue for &'a counter_style::Ranges {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        if self.0.is_empty() {
+            nscssvalue.set_auto();
+        } else {
+            nscssvalue.set_pair_list(self.0.iter().map(|range| {
+                fn set_bound(bound: CounterBound, nscssvalue: &mut nsCSSValue) {
+                    if let CounterBound::Integer(finite) = bound {
+                        nscssvalue.set_integer(finite.value())
+                    } else {
+                        nscssvalue.set_enum(structs::NS_STYLE_COUNTER_RANGE_INFINITE as i32)
+                    }
+                }
+                let mut start = nsCSSValue::null();
+                let mut end = nsCSSValue::null();
+                set_bound(range.start, &mut start);
+                set_bound(range.end, &mut end);
+                (start, end)
+            }));
+        }
+    }
+}
+
+impl<'a> ToNsCssValue for &'a counter_style::Pad {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        let mut min_length = nsCSSValue::null();
+        let mut pad_with = nsCSSValue::null();
+        min_length.set_integer(self.0.value());
+        pad_with.set_from(&self.1);
+        nscssvalue.set_pair(&min_length, &pad_with);
+    }
+}
+
+impl<'a> ToNsCssValue for &'a counter_style::Fallback {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        nscssvalue.set_atom_ident(self.0 .0.clone())
+    }
+}
+
+impl<'a> ToNsCssValue for &'a counter_style::Symbols {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        nscssvalue.set_list(self.0.iter().map(|item| {
+            let mut value = nsCSSValue::null();
+            value.set_from(item);
+            value
+        }));
+    }
+}
+
+impl<'a> ToNsCssValue for &'a counter_style::AdditiveSymbols {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        nscssvalue.set_pair_list(self.0.iter().map(|tuple| {
+            let mut weight = nsCSSValue::null();
+            let mut symbol = nsCSSValue::null();
+            weight.set_integer(tuple.weight.value());
+            symbol.set_from(&tuple.symbol);
+            (weight, symbol)
+        }));
+    }
+}
+
+impl<'a> ToNsCssValue for &'a counter_style::SpeakAs {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        use crate::counter_style::SpeakAs::*;
+        match *self {
+            Auto => nscssvalue.set_auto(),
+            Bullets => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_BULLETS as i32),
+            Numbers => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_NUMBERS as i32),
+            Words => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_WORDS as i32),
+            Other(ref other) => nscssvalue.set_atom_ident(other.0.clone()),
+        }
+    }
+}
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -283,17 +283,17 @@ impl CounterStyleOrNone {
             CounterStyleOrNone::Name(name) => unsafe {
                 set_name(gecko_value, name.0.into_addrefed());
             },
             CounterStyleOrNone::Symbols(symbols_type, symbols) => {
                 let symbols: Vec<_> = symbols
                     .0
                     .iter()
                     .map(|symbol| match *symbol {
-                        Symbol::String(ref s) => nsCStr::from(&**s),
+                        Symbol::String(ref s) => nsCStr::from(s),
                         Symbol::Ident(_) => unreachable!("Should not have identifier in symbols()"),
                     })
                     .collect();
                 let symbols: Vec<_> = symbols
                     .iter()
                     .map(|symbol| symbol as &nsACString as *const _)
                     .collect();
                 unsafe {
@@ -328,15 +328,15 @@ impl CounterStyleOrNone {
             let symbols = &anonymous.mSymbols;
             if anonymous.mSingleString {
                 debug_assert_eq!(symbols.len(), 1);
                 Either::Second(symbols[0].to_string())
             } else {
                 let symbol_type = SymbolsType::from_gecko_keyword(anonymous.mSystem as u32);
                 let symbols = symbols
                     .iter()
-                    .map(|gecko_symbol| Symbol::String(gecko_symbol.to_string().into()))
+                    .map(|gecko_symbol| Symbol::String(gecko_symbol.to_string()))
                     .collect();
                 Either::First(CounterStyleOrNone::Symbols(symbol_type, Symbols(symbols)))
             }
         }
     }
 }
--- a/servo/components/style/gecko_bindings/sugar/mod.rs
+++ b/servo/components/style/gecko_bindings/sugar/mod.rs
@@ -1,14 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Rust sugar and convenience methods for Gecko types.
 
 mod ns_com_ptr;
 mod ns_compatibility;
+mod ns_css_shadow_array;
+mod ns_css_shadow_item;
+pub mod ns_css_value;
 mod ns_style_auto_array;
 pub mod ns_style_coord;
 mod ns_t_array;
 pub mod origin_flags;
 pub mod ownership;
 pub mod refptr;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_shadow_array.rs
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Rust helpers for Gecko's `nsCSSShadowArray`.
+
+use crate::gecko_bindings::bindings::Gecko_AddRefCSSShadowArrayArbitraryThread;
+use crate::gecko_bindings::bindings::Gecko_NewCSSShadowArray;
+use crate::gecko_bindings::bindings::Gecko_ReleaseCSSShadowArrayArbitraryThread;
+use crate::gecko_bindings::structs::{nsCSSShadowArray, nsCSSShadowItem, RefPtr};
+use std::ops::{Deref, DerefMut};
+use std::{ptr, slice};
+
+impl RefPtr<nsCSSShadowArray> {
+    /// Replaces the current `nsCSSShadowArray` with a new one of len `len`.
+    pub fn replace_with_new(&mut self, len: u32) {
+        unsafe {
+            if !self.mRawPtr.is_null() {
+                Gecko_ReleaseCSSShadowArrayArbitraryThread(self.mRawPtr);
+            }
+
+            self.mRawPtr = if len == 0 {
+                ptr::null_mut()
+            } else {
+                Gecko_NewCSSShadowArray(len)
+            }
+        }
+    }
+
+    /// Sets the value to other `nsCSSShadowArray`, bumping and decreasing
+    /// refcounts as needed.
+    ///
+    /// TODO(emilio): Seems like this could move to `refptr.rs`, and be more
+    /// generic.
+    pub fn copy_from(&mut self, other: &Self) {
+        unsafe {
+            if !self.mRawPtr.is_null() {
+                Gecko_ReleaseCSSShadowArrayArbitraryThread(self.mRawPtr);
+            }
+            if !other.mRawPtr.is_null() {
+                Gecko_AddRefCSSShadowArrayArbitraryThread(other.mRawPtr);
+            }
+
+            self.mRawPtr = other.mRawPtr;
+        }
+    }
+}
+
+impl Deref for RefPtr<nsCSSShadowArray> {
+    type Target = [nsCSSShadowItem];
+    fn deref(&self) -> &[nsCSSShadowItem] {
+        if self.mRawPtr.is_null() {
+            &[]
+        } else {
+            unsafe {
+                slice::from_raw_parts(
+                    (*self.mRawPtr).mArray.as_ptr(),
+                    (*self.mRawPtr).mLength as usize,
+                )
+            }
+        }
+    }
+}
+
+impl DerefMut for RefPtr<nsCSSShadowArray> {
+    fn deref_mut(&mut self) -> &mut [nsCSSShadowItem] {
+        if self.mRawPtr.is_null() {
+            &mut []
+        } else {
+            unsafe {
+                slice::from_raw_parts_mut(
+                    (*self.mRawPtr).mArray.as_mut_ptr(),
+                    (*self.mRawPtr).mLength as usize,
+                )
+            }
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Rust helpers for Gecko's `nsCSSShadowItem`.
+
+use crate::gecko_bindings::structs::nsCSSShadowItem;
+use crate::values::computed::effects::{BoxShadow, SimpleShadow};
+use app_units::Au;
+
+impl nsCSSShadowItem {
+    /// Sets this item from the given box shadow.
+    #[inline]
+    pub fn set_from_box_shadow(&mut self, shadow: BoxShadow) {
+        self.set_from_simple_shadow(shadow.base);
+        self.mSpread = shadow.spread.to_i32_au();
+        self.mInset = shadow.inset;
+    }
+
+    /// Returns this item as a box shadow.
+    #[inline]
+    pub fn to_box_shadow(&self) -> BoxShadow {
+        BoxShadow {
+            base: self.extract_simple_shadow(),
+            spread: Au(self.mSpread).into(),
+            inset: self.mInset,
+        }
+    }
+
+    /// Sets this item from the given simple shadow.
+    #[inline]
+    pub fn set_from_simple_shadow(&mut self, shadow: SimpleShadow) {
+        self.mXOffset = shadow.horizontal.to_i32_au();
+        self.mYOffset = shadow.vertical.to_i32_au();
+        self.mRadius = shadow.blur.0.to_i32_au();
+        self.mSpread = 0;
+        self.mInset = false;
+        self.mColor = shadow.color.into();
+    }
+
+    /// Gets a simple shadow from this item.
+    #[inline]
+    fn extract_simple_shadow(&self) -> SimpleShadow {
+        SimpleShadow {
+            color: self.mColor.into(),
+            horizontal: Au(self.mXOffset).into(),
+            vertical: Au(self.mYOffset).into(),
+            blur: Au(self.mRadius).into(),
+        }
+    }
+
+    /// Returns this item as a simple shadow.
+    #[inline]
+    pub fn to_simple_shadow(&self) -> SimpleShadow {
+        debug_assert_eq!(self.mSpread, 0);
+        debug_assert_eq!(self.mInset, false);
+        self.extract_simple_shadow()
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -0,0 +1,424 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Little helpers for `nsCSSValue`.
+
+use crate::gecko_bindings::bindings;
+use crate::gecko_bindings::structs;
+use crate::gecko_bindings::structs::{nsCSSUnit, nsCSSValue};
+use crate::gecko_bindings::structs::{nsCSSValueList, nsCSSValue_Array};
+use crate::gecko_string_cache::Atom;
+use crate::values::computed::{Angle, Length, LengthPercentage, Percentage};
+use crate::Zero;
+use std::marker::PhantomData;
+use std::mem;
+use std::ops::{Index, IndexMut};
+use std::slice;
+
+impl nsCSSValue {
+    /// Create a CSSValue with null unit, useful to be used as a return value.
+    #[inline]
+    pub fn null() -> Self {
+        unsafe { mem::zeroed() }
+    }
+
+    /// Returns true if this nsCSSValue is none.
+    #[inline]
+    pub fn is_none(&self) -> bool {
+        self.mUnit == nsCSSUnit::eCSSUnit_None
+    }
+
+    /// Returns this nsCSSValue value as an integer, unchecked in release
+    /// builds.
+    pub fn integer_unchecked(&self) -> i32 {
+        debug_assert!(
+            self.mUnit == nsCSSUnit::eCSSUnit_Integer ||
+                self.mUnit == nsCSSUnit::eCSSUnit_Enumerated
+        );
+        unsafe { *self.mValue.mInt.as_ref() }
+    }
+
+    /// Checks if it is an integer and returns it if so
+    pub fn integer(&self) -> Option<i32> {
+        if self.mUnit == nsCSSUnit::eCSSUnit_Integer || self.mUnit == nsCSSUnit::eCSSUnit_Enumerated
+        {
+            Some(unsafe { *self.mValue.mInt.as_ref() })
+        } else {
+            None
+        }
+    }
+
+    /// Returns this nsCSSValue value as a floating point value, unchecked in
+    /// release builds.
+    pub fn float_unchecked(&self) -> f32 {
+        debug_assert!(nsCSSUnit::eCSSUnit_Number as u32 <= self.mUnit as u32);
+        unsafe { *self.mValue.mFloat.as_ref() }
+    }
+
+    /// Returns this nsCSSValue as a nsCSSValue::Array, unchecked in release
+    /// builds.
+    pub unsafe fn array_unchecked(&self) -> &nsCSSValue_Array {
+        debug_assert!(
+            nsCSSUnit::eCSSUnit_Array as u32 <= self.mUnit as u32 &&
+                self.mUnit as u32 <= nsCSSUnit::eCSSUnit_Calc_Plus as u32
+        );
+        let array = *self.mValue.mArray.as_ref();
+        debug_assert!(!array.is_null());
+        &*array
+    }
+
+    /// Sets LengthPercentage value to this nsCSSValue.
+    pub unsafe fn set_length_percentage(&mut self, lp: LengthPercentage) {
+        if lp.was_calc {
+            return bindings::Gecko_CSSValue_SetCalc(self, lp.into());
+        }
+        debug_assert!(!lp.has_percentage || lp.unclamped_length() == Length::zero());
+        if lp.has_percentage {
+            return self.set_percentage(lp.percentage());
+        }
+        self.set_px(lp.unclamped_length().px());
+    }
+
+    /// Sets a px value to this nsCSSValue.
+    pub unsafe fn set_px(&mut self, px: f32) {
+        bindings::Gecko_CSSValue_SetPixelLength(self, px)
+    }
+
+    /// Sets a percentage value to this nsCSSValue.
+    pub unsafe fn set_percentage(&mut self, unit_value: f32) {
+        bindings::Gecko_CSSValue_SetPercentage(self, unit_value)
+    }
+
+    /// Returns LengthPercentage value.
+    pub unsafe fn get_length_percentage(&self) -> LengthPercentage {
+        match self.mUnit {
+            nsCSSUnit::eCSSUnit_Pixel => {
+                LengthPercentage::new(Length::new(bindings::Gecko_CSSValue_GetNumber(self)), None)
+            },
+            nsCSSUnit::eCSSUnit_Percent => LengthPercentage::new_percent(Percentage(
+                bindings::Gecko_CSSValue_GetPercentage(self),
+            )),
+            nsCSSUnit::eCSSUnit_Calc => bindings::Gecko_CSSValue_GetCalc(self).into(),
+            _ => panic!("Unexpected unit"),
+        }
+    }
+
+    /// Returns Length  value.
+    pub unsafe fn get_length(&self) -> Length {
+        match self.mUnit {
+            nsCSSUnit::eCSSUnit_Pixel => Length::new(bindings::Gecko_CSSValue_GetNumber(self)),
+            _ => panic!("Unexpected unit"),
+        }
+    }
+
+    fn set_valueless_unit(&mut self, unit: nsCSSUnit) {
+        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
+        debug_assert!(
+            unit as u32 <= nsCSSUnit::eCSSUnit_DummyInherit as u32,
+            "Not a valueless unit"
+        );
+        self.mUnit = unit;
+    }
+
+    /// Set to an auto value
+    ///
+    /// This method requires the current value to be null.
+    pub fn set_auto(&mut self) {
+        self.set_valueless_unit(nsCSSUnit::eCSSUnit_Auto);
+    }
+
+    /// Set to a normal value
+    ///
+    /// This method requires the current value to be null.
+    pub fn set_normal(&mut self) {
+        self.set_valueless_unit(nsCSSUnit::eCSSUnit_Normal);
+    }
+
+    fn set_string_internal(&mut self, s: &str, unit: nsCSSUnit) {
+        unsafe { bindings::Gecko_CSSValue_SetString(self, s.as_ptr(), s.len() as u32, unit) }
+    }
+
+    fn set_string_from_atom_internal(&mut self, s: &Atom, unit: nsCSSUnit) {
+        unsafe { bindings::Gecko_CSSValue_SetStringFromAtom(self, s.as_ptr(), unit) }
+    }
+
+    /// Set to a string value
+    pub fn set_string(&mut self, s: &str) {
+        self.set_string_internal(s, nsCSSUnit::eCSSUnit_String)
+    }
+
+    /// Set to a string value from the given atom
+    pub fn set_string_from_atom(&mut self, s: &Atom) {
+        self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_String)
+    }
+
+    /// Set to a ident value from the given atom
+    pub fn set_ident_from_atom(&mut self, s: &Atom) {
+        self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_Ident)
+    }
+
+    /// Set to an identifier value
+    pub fn set_ident(&mut self, s: &str) {
+        self.set_string_internal(s, nsCSSUnit::eCSSUnit_Ident)
+    }
+
+    /// Set to an atom identifier value
+    pub fn set_atom_ident(&mut self, s: Atom) {
+        unsafe { bindings::Gecko_CSSValue_SetAtomIdent(self, s.into_addrefed()) }
+    }
+
+    fn set_int_internal(&mut self, value: i32, unit: nsCSSUnit) {
+        unsafe { bindings::Gecko_CSSValue_SetInt(self, value, unit) }
+    }
+
+    /// Set to an integer value
+    pub fn set_integer(&mut self, value: i32) {
+        self.set_int_internal(value, nsCSSUnit::eCSSUnit_Integer)
+    }
+
+    /// Set to an enumerated value
+    pub fn set_enum<T: Into<i32>>(&mut self, value: T) {
+        self.set_int_internal(value.into(), nsCSSUnit::eCSSUnit_Enumerated);
+    }
+
+    /// Set to a number value
+    pub fn set_number(&mut self, number: f32) {
+        unsafe { bindings::Gecko_CSSValue_SetFloat(self, number, nsCSSUnit::eCSSUnit_Number) }
+    }
+
+    /// Set to an array of given length
+    pub fn set_array(&mut self, len: i32) -> &mut nsCSSValue_Array {
+        unsafe { bindings::Gecko_CSSValue_SetArray(self, len) }
+        unsafe { self.mValue.mArray.as_mut().as_mut() }.unwrap()
+    }
+
+    /// Generic set from any value that implements the ToNsCssValue trait.
+    pub fn set_from<T: ToNsCssValue>(&mut self, value: T) {
+        value.convert(self)
+    }
+
+    /// Returns an `Angle` value from this `nsCSSValue`.
+    ///
+    /// Panics if the unit is not `eCSSUnit_Degree`.
+    #[inline]
+    pub fn get_angle(&self) -> Angle {
+        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Degree);
+        Angle::from_degrees(self.float_unchecked())
+    }
+
+    /// Sets Angle value to this nsCSSValue.
+    pub fn set_angle(&mut self, angle: Angle) {
+        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
+        self.mUnit = nsCSSUnit::eCSSUnit_Degree;
+        unsafe {
+            *self.mValue.mFloat.as_mut() = angle.degrees();
+        }
+    }
+
+    /// Set to a pair value
+    ///
+    /// This is only supported on the main thread.
+    pub fn set_pair(&mut self, x: &nsCSSValue, y: &nsCSSValue) {
+        unsafe { bindings::Gecko_CSSValue_SetPair(self, x, y) }
+    }
+
+    /// Set to a list value
+    ///
+    /// This is only supported on the main thread.
+    pub fn set_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_SetList(self, values.len() as u32);
+        }
+        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_List);
+        let list: &mut structs::nsCSSValueList = &mut unsafe {
+            self.mValue
+                .mList
+                .as_ref() // &*nsCSSValueList_heap
+                .as_mut()
+                .expect("List pointer should be non-null")
+        }
+        ._base;
+        for (item, new_value) in list.into_iter().zip(values) {
+            *item = new_value;
+        }
+    }
+
+    /// Set to a pair list value
+    ///
+    /// This is only supported on the main thread.
+    pub fn set_pair_list<I>(&mut self, mut values: I)
+    where
+        I: ExactSizeIterator<Item = (nsCSSValue, nsCSSValue)>,
+    {
+        debug_assert!(values.len() > 0, "Empty list is not supported");
+        unsafe {
+            bindings::Gecko_CSSValue_SetPairList(self, values.len() as u32);
+        }
+        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_PairList);
+        let mut item_ptr = &mut unsafe {
+            self.mValue
+                .mPairList
+                .as_ref() // &*nsCSSValuePairList_heap
+                .as_mut()
+                .expect("List pointer should be non-null")
+        }
+        ._base as *mut structs::nsCSSValuePairList;
+        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) };
+    }
+}
+
+/// Iterator of nsCSSValueList.
+#[allow(non_camel_case_types)]
+pub struct nsCSSValueListIterator<'a> {
+    current: Option<&'a nsCSSValueList>,
+}
+
+impl<'a> Iterator for nsCSSValueListIterator<'a> {
+    type Item = &'a nsCSSValue;
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.current {
+            Some(item) => {
+                self.current = unsafe { item.mNext.as_ref() };
+                Some(&item.mValue)
+            },
+            None => None,
+        }
+    }
+}
+
+impl<'a> IntoIterator for &'a nsCSSValueList {
+    type Item = &'a nsCSSValue;
+    type IntoIter = nsCSSValueListIterator<'a>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        nsCSSValueListIterator {
+            current: Some(self),
+        }
+    }
+}
+
+/// Mutable Iterator of nsCSSValueList.
+#[allow(non_camel_case_types)]
+pub struct nsCSSValueListMutIterator<'a> {
+    current: *mut nsCSSValueList,
+    phantom: PhantomData<&'a mut nsCSSValue>,
+}
+
+impl<'a> Iterator for nsCSSValueListMutIterator<'a> {
+    type Item = &'a mut nsCSSValue;
+    fn next(&mut self) -> Option<Self::Item> {
+        match unsafe { self.current.as_mut() } {
+            Some(item) => {
+                self.current = item.mNext;
+                Some(&mut item.mValue)
+            },
+            None => None,
+        }
+    }
+}
+
+impl<'a> IntoIterator for &'a mut nsCSSValueList {
+    type Item = &'a mut nsCSSValue;
+    type IntoIter = nsCSSValueListMutIterator<'a>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        nsCSSValueListMutIterator {
+            current: self as *mut nsCSSValueList,
+            phantom: PhantomData,
+        }
+    }
+}
+
+impl nsCSSValue_Array {
+    /// Return the length of this `nsCSSValue::Array`
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.mCount
+    }
+
+    #[inline]
+    fn buffer(&self) -> *const nsCSSValue {
+        self.mArray.as_ptr()
+    }
+
+    /// Get the array as a slice of nsCSSValues.
+    #[inline]
+    pub fn as_slice(&self) -> &[nsCSSValue] {
+        unsafe { slice::from_raw_parts(self.buffer(), self.len()) }
+    }
+
+    /// Get the array as a mutable slice of nsCSSValues.
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [nsCSSValue] {
+        unsafe { slice::from_raw_parts_mut(self.buffer() as *mut _, self.len()) }
+    }
+}
+
+impl Index<usize> for nsCSSValue_Array {
+    type Output = nsCSSValue;
+    #[inline]
+    fn index(&self, i: usize) -> &nsCSSValue {
+        &self.as_slice()[i]
+    }
+}
+
+impl IndexMut<usize> for nsCSSValue_Array {
+    #[inline]
+    fn index_mut(&mut self, i: usize) -> &mut nsCSSValue {
+        &mut self.as_mut_slice()[i]
+    }
+}
+
+/// Generic conversion to nsCSSValue
+pub trait ToNsCssValue {
+    /// Convert
+    fn convert(self, nscssvalue: &mut nsCSSValue);
+}
+
+impl<T: ToNsCssValue> From<T> for nsCSSValue {
+    fn from(value: T) -> nsCSSValue {
+        let mut result = nsCSSValue::null();
+        value.convert(&mut result);
+        result
+    }
+}
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -285,16 +285,21 @@ 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/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -48,17 +48,16 @@ macro_rules! local_name {
     };
 }
 
 /// A handle to a Gecko atom.
 ///
 /// This is either a strong reference to a dynamic atom (an nsAtom pointer),
 /// or an offset from gGkAtoms to the nsStaticAtom object.
 #[derive(Eq, PartialEq)]
-#[repr(C)]
 pub struct Atom(usize);
 
 /// An atom *without* a strong reference.
 ///
 /// Only usable as `&'a WeakAtom`,
 /// where `'a` is the lifetime of something that holds a strong reference to that atom.
 pub struct WeakAtom(nsAtom);
 
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -185,17 +185,16 @@ pub use html5ever::LocalName;
 pub use html5ever::Namespace;
 #[cfg(feature = "servo")]
 pub use html5ever::Prefix;
 #[cfg(feature = "servo")]
 pub use servo_atoms::Atom;
 
 pub use style_traits::arc_slice::ArcSlice;
 pub use style_traits::owned_slice::OwnedSlice;
-pub use style_traits::owned_str::OwnedStr;
 
 /// The CSS properties supported by the style system.
 /// Generated from the properties.mako.rs template by build.rs
 #[macro_use]
 #[allow(unsafe_code)]
 #[deny(missing_docs)]
 pub mod properties {
     include!(concat!(env!("OUT_DIR"), "/properties.rs"));
--- a/servo/components/style/properties/cascade.rs
+++ b/servo/components/style/properties/cascade.rs
@@ -623,16 +623,20 @@ 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/data.py
+++ b/servo/components/style/properties/data.py
@@ -169,17 +169,16 @@ class Longhand(object):
     def __init__(self, style_struct, name, spec=None, animation_value_type=None, keyword=None,
                  predefined_type=None, servo_pref=None, gecko_pref=None,
                  enabled_in="content", need_index=False,
                  gecko_ffi_name=None,
                  allowed_in_keyframe_block=True, cast_type='u8',
                  logical=False, logical_group=None, alias=None, extra_prefixes=None, boxed=False,
                  flags=None, allowed_in_page_rule=False, allow_quirks=False,
                  ignored_when_colors_disabled=False,
-                 simple_vector_bindings=False,
                  vector=False, servo_restyle_damage="repaint"):
         self.name = name
         if not spec:
             raise TypeError("Spec should be specified for %s" % name)
         self.spec = spec
         self.keyword = keyword
         self.predefined_type = predefined_type
         self.ident = to_rust_ident(name)
@@ -206,17 +205,16 @@ class Longhand(object):
         self.alias = parse_property_aliases(alias)
         self.extra_prefixes = parse_property_aliases(extra_prefixes)
         self.boxed = arg_to_bool(boxed)
         self.flags = flags.split() if flags else []
         self.allowed_in_page_rule = arg_to_bool(allowed_in_page_rule)
         self.allow_quirks = allow_quirks
         self.ignored_when_colors_disabled = ignored_when_colors_disabled
         self.is_vector = vector
-        self.simple_vector_bindings = simple_vector_bindings
 
         # https://drafts.csswg.org/css-animations/#keyframes
         # > The <declaration-list> inside of <keyframe-block> accepts any CSS property
         # > except those defined in this specification,
         # > but does accept the `animation-play-state` property and interprets it specially.
         self.allowed_in_keyframe_block = allowed_in_keyframe_block \
             and allowed_in_keyframe_block != "False"
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -23,16 +23,17 @@ use crate::gecko_bindings::bindings::Gec
 use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
 use crate::gecko_bindings::bindings::Gecko_CopyCursorArrayFrom;
 use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
 use crate::gecko_bindings::bindings::Gecko_CopyImageValueFrom;
 use crate::gecko_bindings::bindings::Gecko_CopyListStyleImageFrom;
 use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
 use crate::gecko_bindings::bindings::Gecko_SetCursorArrayLength;
 use crate::gecko_bindings::bindings::Gecko_SetCursorImageValue;
+use crate::gecko_bindings::bindings::Gecko_NewCSSShadowArray;
 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
 use crate::gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use crate::gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
 use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
 use crate::gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use crate::gecko_bindings::structs;
 use crate::gecko_bindings::structs::nsCSSPropertyID;
@@ -50,17 +51,17 @@ use crate::selector_parser::PseudoElemen
 use servo_arc::{Arc, RawOffsetArc};
 use std::marker::PhantomData;
 use std::mem::{forget, uninitialized, zeroed, ManuallyDrop};
 use std::{cmp, ops, ptr};
 use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
 use crate::values::computed::{NonNegativeLength, Percentage, TransitionProperty};
 use crate::values::computed::BorderStyle;
 use crate::values::computed::font::FontSize;
-use crate::values::computed::effects::Filter;
+use crate::values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 use crate::values::generics::column::ColumnCount;
 use crate::values::generics::transform::TransformStyle;
 use crate::values::generics::url::UrlOrNone;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
 
@@ -297,24 +298,24 @@ impl ${style_struct.gecko_struct_name} {
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         ${set_gecko_property(gecko_ffi_name, "From::from(v)")}
     }
 </%def>
 
 <%def name="impl_simple_clone(ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
-        From::from(self.gecko.${gecko_ffi_name}.clone())
+        From::from(self.gecko.${gecko_ffi_name})
     }
 </%def>
 
 <%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)">
     #[allow(non_snake_case)]
     pub fn copy_${ident}_from(&mut self, other: &Self) {
-        self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}.clone();
+        self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
     }
 
     #[allow(non_snake_case)]
     pub fn reset_${ident}(&mut self, other: &Self) {
         self.copy_${ident}_from(other)
     }
 </%def>
 
@@ -806,16 +807,291 @@ 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> {
@@ -911,16 +1187,17 @@ 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:
@@ -2230,22 +2507,28 @@ 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 scroll-snap-points-x
-                          scroll-snap-points-y scroll-snap-coordinate
-                          -moz-binding offset-path shape-outside
-                          -webkit-line-clamp""" %>
+                          transform-style
+                          rotate scroll-snap-points-x scroll-snap-points-y
+                          scroll-snap-coordinate -moz-binding will-change
+                          offset-path shape-outside
+                          translate scale -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;
@@ -2538,16 +2821,80 @@ 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')}
+
+    pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) {
+        use crate::gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange};
+        use crate::values::specified::box_::{WillChangeBits, WillChange};
+
+        match v {
+            WillChange::AnimateableFeatures { features, bits } => {
+                unsafe {
+                    Gecko_ClearWillChange(&mut *self.gecko, features.len());
+                }
+
+                for feature in features.iter() {
+                    unsafe {
+                        Gecko_AppendWillChange(&mut *self.gecko, feature.0.as_ptr())
+                    }
+                }
+
+                self.gecko.mWillChangeBitField = bits;
+            },
+            WillChange::Auto => {
+                unsafe {
+                    Gecko_ClearWillChange(&mut *self.gecko, 0);
+                }
+                self.gecko.mWillChangeBitField = WillChangeBits::empty();
+            },
+        };
+    }
+
+    pub fn copy_will_change_from(&mut self, other: &Self) {
+        use crate::gecko_bindings::bindings::Gecko_CopyWillChangeFrom;
+
+        self.gecko.mWillChangeBitField = other.gecko.mWillChangeBitField;
+        unsafe {
+            Gecko_CopyWillChangeFrom(&mut *self.gecko, &*other.gecko);
+        }
+    }
+
+    pub fn reset_will_change(&mut self, other: &Self) {
+        self.copy_will_change_from(other)
+    }
+
+    pub fn clone_will_change(&self) -> longhands::will_change::computed_value::T {
+        use crate::values::CustomIdent;
+        use crate::values::specified::box_::WillChange;
+
+        if self.gecko.mWillChange.len() == 0 {
+            return WillChange::Auto
+        }
+
+        let custom_idents: Vec<CustomIdent> = self.gecko.mWillChange.iter().map(|gecko_atom| {
+            unsafe {
+                CustomIdent(Atom::from_raw(gecko_atom.mRawPtr))
+            }
+        }).collect();
+
+        WillChange::AnimateableFeatures {
+            features: custom_idents.into_boxed_slice(),
+            bits: self.gecko.mWillChangeBitField,
+        }
+    }
+
     <% 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;
 
@@ -2950,17 +3297,17 @@ fn static_assert() {
                   skip_longhands="${skip_background_longhands}">
 
     <% impl_common_image_layer_properties("background") %>
     <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
     <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="List"
-                  skip_longhands="list-style-image list-style-type -moz-image-region">
+                  skip_longhands="list-style-image list-style-type quotes -moz-image-region">
 
     pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) {
         match image {
             UrlOrNone::None => {
                 unsafe {
                     Gecko_SetListStyleImageNone(&mut *self.gecko);
                 }
             }
@@ -3026,16 +3373,38 @@ fn static_assert() {
 
         let result = CounterStyleOrNone::from_gecko_value(&self.gecko.mCounterStyle);
         match result {
             Either::First(counter_style) => T::CounterStyle(counter_style),
             Either::Second(string) => T::String(string),
         }
     }
 
+    pub fn set_quotes(&mut self, other: longhands::quotes::computed_value::T) {
+        self.gecko.mQuotes.set_arc(other.0.clone());
+    }
+
+    pub fn copy_quotes_from(&mut self, other: &Self) {
+        self.set_quotes(other.clone_quotes());
+    }
+
+    pub fn reset_quotes(&mut self, other: &Self) {
+        self.copy_quotes_from(other)
+    }
+
+    pub fn clone_quotes(&self) -> longhands::quotes::computed_value::T {
+        use gecko_bindings::sugar::ownership::HasArcFFI;
+        use values::computed::QuotePair;
+
+        let quote_pairs = unsafe { &*self.gecko.mQuotes.mRawPtr };
+        longhands::quotes::computed_value::T(
+            Box::<[QuotePair]>::as_arc(&quote_pairs).clone_arc()
+        )
+    }
+
     #[allow(non_snake_case)]
     pub fn set__moz_image_region(&mut self, v: longhands::_moz_image_region::computed_value::T) {
         use crate::values::Either;
         use crate::values::generics::length::LengthPercentageOrAuto::*;
 
         match v {
             Either::Second(_auto) => {
                 self.gecko.mImageRegion.x = 0;
@@ -3103,17 +3472,41 @@ fn static_assert() {
             self.gecko.mSpan
         )
     }
 
     ${impl_simple_copy('_x_span', 'mSpan')}
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Effects"
-                  skip_longhands="clip filter">
+                  skip_longhands="box-shadow clip filter">
+    pub fn set_box_shadow<I>(&mut self, v: I)
+        where I: IntoIterator<Item = BoxShadow>,
+              I::IntoIter: ExactSizeIterator
+    {
+        let v = v.into_iter();
+        self.gecko.mBoxShadow.replace_with_new(v.len() as u32);
+        for (servo, gecko_shadow) in v.zip(self.gecko.mBoxShadow.iter_mut()) {
+            gecko_shadow.set_from_box_shadow(servo);
+        }
+    }
+
+    pub fn copy_box_shadow_from(&mut self, other: &Self) {
+        self.gecko.mBoxShadow.copy_from(&other.gecko.mBoxShadow);
+    }
+
+    pub fn reset_box_shadow(&mut self, other: &Self) {
+        self.copy_box_shadow_from(other)
+    }
+
+    pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T {
+        let buf = self.gecko.mBoxShadow.iter().map(|v| v.to_box_shadow()).collect();
+        longhands::box_shadow::computed_value::List(buf)
+    }
+
     pub fn set_clip(&mut self, v: longhands::clip::computed_value::T) {
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_AUTO;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_RECT;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_LEFT_AUTO;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO;
         use crate::values::generics::length::LengthPercentageOrAuto::*;
@@ -3227,16 +3620,17 @@ fn static_assert() {
      %>
 
     pub fn set_filter<I>(&mut self, v: I)
     where
         I: IntoIterator<Item = Filter>,
         I::IntoIter: ExactSizeIterator,
     {
         use crate::values::generics::effects::Filter::*;
+        use crate::gecko_bindings::structs::nsCSSShadowArray;
         use crate::gecko_bindings::structs::nsStyleFilter;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_SATURATE;
@@ -3267,20 +3661,29 @@ fn static_assert() {
                                             gecko_filter),
 
                 HueRotate(angle) => fill_filter(NS_STYLE_FILTER_HUE_ROTATE,
                                                 CoordDataValue::from(angle),
                                                 gecko_filter),
 
                 DropShadow(shadow) => {
                     gecko_filter.mType = NS_STYLE_FILTER_DROP_SHADOW;
-                    unsafe {
-                        let ref mut union = gecko_filter.__bindgen_anon_1;
-                        ptr::write(union.mDropShadow.as_mut(), shadow);
+
+                    fn init_shadow(filter: &mut nsStyleFilter) -> &mut nsCSSShadowArray {
+                        unsafe {
+                            let ref mut union = filter.__bindgen_anon_1;
+                            let shadow_array: &mut *mut nsCSSShadowArray = union.mDropShadow.as_mut();
+                            *shadow_array = Gecko_NewCSSShadowArray(1);
+
+                            &mut **shadow_array
+                        }
                     }
+
+                    let gecko_shadow = init_shadow(gecko_filter);
+                    gecko_shadow.mArray[0].set_from_simple_shadow(shadow);
                 },
                 Url(ref url) => {
                     unsafe {
                         bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.url_value_ptr());
                     }
                 },
             }
         }
@@ -3306,51 +3709,52 @@ fn static_assert() {
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_SATURATE;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_SEPIA;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_HUE_ROTATE;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW;
         use crate::gecko_bindings::structs::NS_STYLE_FILTER_URL;
 
-        longhands::filter::computed_value::List(self.gecko.mFilters.iter().map(|filter| {
+        let mut filters = Vec::new();
+        for filter in self.gecko.mFilters.iter(){
             match filter.mType {
                 % for func in FILTER_FUNCTIONS:
                 NS_STYLE_FILTER_${func.upper()} => {
-                    Filter::${func}(
+                    filters.push(Filter::${func}(
                         GeckoStyleCoordConvertible::from_gecko_style_coord(
-                            &filter.mFilterParameter
-                        ).unwrap()
-                    )
+                            &filter.mFilterParameter).unwrap()));
                 },
                 % endfor
                 NS_STYLE_FILTER_BLUR => {
-                    Filter::Blur(NonNegativeLength::from_gecko_style_coord(
-                        &filter.mFilterParameter
-                    ).unwrap())
+                    filters.push(Filter::Blur(NonNegativeLength::from_gecko_style_coord(
+                        &filter.mFilterParameter).unwrap()));
                 },
                 NS_STYLE_FILTER_HUE_ROTATE => {
-                    Filter::HueRotate(GeckoStyleCoordConvertible::from_gecko_style_coord(
-                        &filter.mFilterParameter,
-                    ).unwrap())
+                    filters.push(Filter::HueRotate(
+                        GeckoStyleCoordConvertible::from_gecko_style_coord(
+                            &filter.mFilterParameter).unwrap()));
                 },
                 NS_STYLE_FILTER_DROP_SHADOW => {
-                    Filter::DropShadow(unsafe {
-                        (*filter.__bindgen_anon_1.mDropShadow.as_ref()).clone()
-                    })
+                    filters.push(unsafe {
+                        Filter::DropShadow(
+                            (**filter.__bindgen_anon_1.mDropShadow.as_ref()).mArray[0].to_simple_shadow(),
+                        )
+                    });
                 },
                 NS_STYLE_FILTER_URL => {
-                    Filter::Url(unsafe {
+                    filters.push(Filter::Url(unsafe {
                         let url = RefPtr::new(*filter.__bindgen_anon_1.mURL.as_ref());
                         ComputedUrl::from_url_value(url)
-                    })
+                    }));
                 }
-                _ => unreachable!("Unknown filter function?"),
+                _ => {},
             }
-        }).collect())
+        }
+        longhands::filter::computed_value::List(filters)
     }
 
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedBox">
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedTable"
@@ -3375,24 +3779,48 @@ fn static_assert() {
             Au(self.gecko.mBorderSpacingCol).into(),
             Au(self.gecko.mBorderSpacingRow).into()
         )
     }
 </%self:impl_trait>
 
 
 <%self:impl_trait style_struct_name="InheritedText"
-                  skip_longhands="text-align text-emphasis-style
+                  skip_longhands="text-align text-emphasis-style text-shadow
                                   -webkit-text-stroke-width text-emphasis-position">
 
     <% text_align_keyword = Keyword("text-align",
                                     "start end left right center justify -moz-center -moz-left -moz-right char",
                                     gecko_strip_moz_prefix=False) %>
     ${impl_keyword('text_align', 'mTextAlign', text_align_keyword)}
 
+    pub fn set_text_shadow<I>(&mut self, v: I)
+        where I: IntoIterator<Item = SimpleShadow>,
+              I::IntoIter: ExactSizeIterator
+    {
+        let v = v.into_iter();
+        self.gecko.mTextShadow.replace_with_new(v.len() as u32);
+        for (servo, gecko_shadow) in v.zip(self.gecko.mTextShadow.iter_mut()) {
+            gecko_shadow.set_from_simple_shadow(servo);
+        }
+    }
+
+    pub fn copy_text_shadow_from(&mut self, other: &Self) {
+        self.gecko.mTextShadow.copy_from(&other.gecko.mTextShadow);
+    }
+
+    pub fn reset_text_shadow(&mut self, other: &Self) {
+        self.copy_text_shadow_from(other)
+    }
+
+    pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T {
+        let buf = self.gecko.mTextShadow.iter().map(|v| v.to_simple_shadow()).collect();
+        longhands::text_shadow::computed_value::List(buf)
+    }
+
     fn clear_text_emphasis_style_if_string(&mut self) {
         if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
             self.gecko.mTextEmphasisStyleString.truncate();
             self.gecko.mTextEmphasisStyle = structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE as u8;
         }
     }
 
     ${impl_simple_type_with_conversion("text_emphasis_position")}
@@ -3686,17 +4114,17 @@ clip-path
 
     <% impl_common_image_layer_properties("mask") %>
     <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
     <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
     <% impl_shape_source("clip_path", "mClipPath") %>
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedSVG"
-                  skip_longhands="paint-order stroke-dasharray">
+                  skip_longhands="paint-order stroke-dasharray -moz-context-properties">
     pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) {
         self.gecko.mPaintOrder = v.0;
     }
 
     ${impl_simple_copy('paint_order', 'mPaintOrder')}
 
     pub fn clone_paint_order(&self) -> longhands::paint_order::computed_value::T {
         use crate::properties::longhands::paint_order::computed_value::T;
@@ -3746,16 +4174,72 @@ clip-path
         use crate::values::generics::svg::SVGStrokeDashArray;
 
         if self.gecko.mContextFlags & CONTEXT_VALUE != 0 {
             debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0);
             return SVGStrokeDashArray::ContextValue;
         }
         SVGStrokeDashArray::Values(self.gecko.mStrokeDasharray.iter().cloned().collect())
     }
+
+    #[allow(non_snake_case)]
+    pub fn _moz_context_properties_count(&self) -> usize {
+        self.gecko.mContextProps.len()
+    }
+
+    #[allow(non_snake_case)]
+    pub fn _moz_context_properties_at(
+        &self,
+        index: usize,
+    ) -> longhands::_moz_context_properties::computed_value::single_value::T {
+        longhands::_moz_context_properties::computed_value::single_value::T(
+            CustomIdent(unsafe {
+                Atom::from_raw(self.gecko.mContextProps[index].mRawPtr)
+            })
+        )
+    }
+
+    #[allow(non_snake_case)]
+    pub fn set__moz_context_properties<I>(&mut self, v: I)
+    where
+        I: IntoIterator<Item = longhands::_moz_context_properties::computed_value::single_value::T>,
+        I::IntoIter: ExactSizeIterator
+    {
+        let v = v.into_iter();
+        unsafe {
+            bindings::Gecko_nsStyleSVG_SetContextPropertiesLength(&mut *self.gecko, v.len() as u32);
+        }
+
+        let s = &mut *self.gecko;
+        s.mContextPropsBits = 0;
+        for (gecko, servo) in s.mContextProps.iter_mut().zip(v) {
+            if (servo.0).0 == atom!("fill") {
+                s.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_FILL as u8;
+            } else if (servo.0).0 == atom!("stroke") {
+                s.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_STROKE as u8;
+            } else if (servo.0).0 == atom!("fill-opacity") {
+                s.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY as u8;
+            } else if (servo.0).0 == atom!("stroke-opacity") {
+                s.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY as u8;
+            }
+            gecko.mRawPtr = (servo.0).0.into_addrefed();
+        }
+    }
+
+    #[allow(non_snake_case)]
+    pub fn copy__moz_context_properties_from(&mut self, other: &Self) {
+        unsafe {
+            bindings::Gecko_nsStyleSVG_CopyContextProperties(&mut *self.gecko, &*other.gecko);
+        }
+    }
+
+    #[allow(non_snake_case)]
+    pub fn reset__moz_context_properties(&mut self, other: &Self) {
+        self.copy__moz_context_properties_from(other)
+    }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Color">
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedUI" skip_longhands="cursor">
     pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
         self.gecko.mCursor = v.keyword;
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -75,36 +75,22 @@
 <%doc>
     To be used in cases where we have a grammar like "<thing> [ , <thing> ]*".
 
     Setting allow_empty to False allows for cases where the vector
     is empty. The grammar for these is usually "none | <thing> [ , <thing> ]*".
     We assume that the default/initial value is an empty vector for these.
     `initial_value` need not be defined for these.
 </%doc>
-
-// The setup here is roughly:
-//
-//  * UnderlyingList is the list that is stored in the computed value. This may
-//    be a shared ArcSlice if the property is inherited.
-//  * UnderlyingOwnedList is the list that is used for animation.
-//  * Specified values always use OwnedSlice, since it's more compact.
-//  * computed_value::List is just a convenient alias that you can use for the
-//    computed value list, since this is in the computed_value module.
-//
-// If simple_vector_bindings is true, then we don't use the complex iterator
-// machinery and set_foo_from, and just compute the value like any other
-// longhand.
 <%def name="vector_longhand(name, animation_value_type=None,
                             vector_animation_type=None, allow_empty=False,
-                            simple_vector_bindings=False,
                             separator='Comma',
                             **kwargs)">
     <%call expr="longhand(name, animation_value_type=animation_value_type, vector=True,
-                          simple_vector_bindings=simple_vector_bindings, **kwargs)">
+                          **kwargs)">
         #[allow(unused_imports)]
         use smallvec::SmallVec;
 
         pub mod single_value {
             #[allow(unused_imports)]
             use cssparser::{Parser, BasicParseError};
             #[allow(unused_imports)]
             use crate::parser::{Parse, ParserContext};
@@ -120,282 +106,178 @@
             use crate::values::{computed, specified};
             #[allow(unused_imports)]
             use crate::values::{Auto, Either, None_};
             ${caller.body()}
         }
 
         /// The definition of the computed value for ${name}.
         pub mod computed_value {
-            #[allow(unused_imports)]
-            use crate::values::animated::ToAnimatedValue;
-            #[allow(unused_imports)]
-            use crate::values::resolved::ToResolvedValue;
             pub use super::single_value::computed_value as single_value;
             pub use self::single_value::T as SingleComputedValue;
-            % if not allow_empty or allow_empty == "NotInitial":
-            use smallvec::SmallVec;
+            % if allow_empty and allow_empty != "NotInitial":
+            use std::vec::IntoIter;
+            % else:
+            use smallvec::{IntoIter, SmallVec};
             % endif
             use crate::values::computed::ComputedVecIter;
 
-            <% is_shared_list = allow_empty and allow_empty != "NotInitial" and data.longhands_by_name[name].style_struct.inherited %>
-
-            // FIXME(emilio): Add an OwnedNonEmptySlice type, and figure out
-            // something for transition-name, which is the only remaining user
-            // of NotInitial.
-            pub type UnderlyingList<T> =
-                % if allow_empty and allow_empty != "NotInitial":
-                % if data.longhands_by_name[name].style_struct.inherited:
-                    crate::ArcSlice<T>;
-                % else:
-                    crate::OwnedSlice<T>;
-                % endif
-                % else:
-                    SmallVec<[T; 1]>;
-                % endif
-
-            pub type UnderlyingOwnedList<T> =
-                % if allow_empty and allow_empty != "NotInitial":
-                    crate::OwnedSlice<T>;
-                % else:
-                    SmallVec<[T; 1]>;
-                % endif
-
-
-            /// The generic type defining the animated and resolved values for
-            /// this property.
+            /// The generic type defining the value for this property.
             ///
             /// Making this type generic allows the compiler to figure out the
             /// animated value for us, instead of having to implement it
             /// manually for every type we care about.
             % if separator == "Comma":
             #[css(comma)]
             % endif
             #[derive(
                 Clone,
                 Debug,
                 MallocSizeOf,
                 PartialEq,
                 ToAnimatedValue,
                 ToResolvedValue,
                 ToCss,
             )]
-            pub struct OwnedList<T>(
-                % if not allow_empty:
-                #[css(iterable)]
-                % else:
-                #[css(if_empty = "none", iterable)]
-                % endif
-                pub UnderlyingOwnedList<T>,
-            );
-
-            /// The computed value for this property.
-            % if not is_shared_list:
-            pub type ComputedList = OwnedList<single_value::T>;
-            pub use self::OwnedList as List;
-            % else:
-            pub use self::ComputedList as List;
-
-            % if separator == "Comma":
-            #[css(comma)]
-            % endif
-            #[derive(
-                Clone,
-                Debug,
-                MallocSizeOf,
-                PartialEq,
-                ToCss,
-            )]
-            pub struct ComputedList(
+            pub struct List<T>(
                 % if not allow_empty:
                 #[css(iterable)]
                 % else:
                 #[css(if_empty = "none", iterable)]
                 % endif
-                % if is_shared_list:
-                #[ignore_malloc_size_of = "Arc"]
+                % if allow_empty and allow_empty != "NotInitial":
+                pub Vec<T>,
+                % else:
+                pub SmallVec<[T; 1]>,
                 % endif
-                pub UnderlyingList<single_value::T>,
             );
 
-            type ResolvedList = OwnedList<<single_value::T as ToResolvedValue>::ResolvedValue>;
-            impl ToResolvedValue for ComputedList {
-                type ResolvedValue = ResolvedList;
 
-                fn to_resolved_value(self, context: &crate::values::resolved::Context) -> Self::ResolvedValue {
-                    OwnedList(
-                        self.0
-                            .iter()
-                            .cloned()
-                            .map(|v| v.to_resolved_value(context))
-                            .collect()
-                    )
-                }
-
-                fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
-                    % if not is_shared_list:
-                    use std::iter::FromIterator;
-                    % endif
-                    let iter =
-                        resolved.0.into_iter().map(ToResolvedValue::from_resolved_value);
-                    ComputedList(UnderlyingList::from_iter(iter))
-                }
-            }
-            % endif
-
-            % if simple_vector_bindings:
-            impl From<ComputedList> for UnderlyingList<single_value::T> {
-                #[inline]
-                fn from(l: ComputedList) -> Self {
-                    l.0
-                }
-            }
-            impl From<UnderlyingList<single_value::T>> for ComputedList {
-                #[inline]
-                fn from(l: UnderlyingList<single_value::T>) -> Self {
-                    List(l)
-                }
-            }
-            % endif
-
+            /// The computed value, effectively a list of single values.
             % if vector_animation_type:
             % if not animation_value_type:
                 Sorry, this is stupid but needed for now.
             % endif
 
             use crate::properties::animated_properties::ListAnimation;
-            use crate::values::animated::{Animate, ToAnimatedZero, Procedure};
+            use crate::values::animated::{Animate, ToAnimatedValue, ToAnimatedZero, Procedure};
             use crate::values::distance::{SquaredDistance, ComputeSquaredDistance};
 
             // FIXME(emilio): For some reason rust thinks that this alias is
             // unused, even though it's clearly used below?
             #[allow(unused)]
-            type AnimatedList = OwnedList<<single_value::T as ToAnimatedValue>::AnimatedValue>;
-
-            % if is_shared_list:
-            impl ToAnimatedValue for ComputedList {
-                type AnimatedValue = AnimatedList;
-
-                fn to_animated_value(self) -> Self::AnimatedValue {
-                    OwnedList(
-                        self.0.iter().map(|v| v.clone().to_animated_value()).collect()
-                    )
-                }
-
-                fn from_animated_value(animated: Self::AnimatedValue) -> Self {
-                    let iter =
-                        animated.0.into_iter().map(ToAnimatedValue::from_animated_value);
-                    ComputedList(UnderlyingList::from_iter(iter))
-                }
-            }
-            % endif
+            type AnimatedList = <List<single_value::T> as ToAnimatedValue>::AnimatedValue;
 
             impl ToAnimatedZero for AnimatedList {
                 fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
             }
 
             impl Animate for AnimatedList {
                 fn animate(
                     &self,
                     other: &Self,
                     procedure: Procedure,
                 ) -> Result<Self, ()> {
-                    Ok(OwnedList(
+                    Ok(List(
                         self.0.animate_${vector_animation_type}(&other.0, procedure)?
                     ))
                 }
             }
             impl ComputeSquaredDistance for AnimatedList {
                 fn compute_squared_distance(
                     &self,
                     other: &Self,
                 ) -> Result<SquaredDistance, ()> {
                     self.0.squared_distance_${vector_animation_type}(&other.0)
                 }
             }
             % endif
 
-            /// The computed value, effectively a list of single values.
-            pub use self::ComputedList as T;
+            pub type T = List<single_value::T>;
 
             pub type Iter<'a, 'cx, 'cx_a> = ComputedVecIter<'a, 'cx, 'cx_a, super::single_value::SpecifiedValue>;
+
+            impl IntoIterator for T {
+                type Item = single_value::T;
+                % if allow_empty and allow_empty != "NotInitial":
+                type IntoIter = IntoIter<single_value::T>;
+                % else:
+                type IntoIter = IntoIter<[single_value::T; 1]>;
+                % endif
+                fn into_iter(self) -> Self::IntoIter {
+                    self.0.into_iter()
+                }
+            }
         }
 
         /// The specified value of ${name}.
         % if separator == "Comma":
         #[css(comma)]
         % endif
         #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
         pub struct SpecifiedValue(
             % if not allow_empty:
             #[css(iterable)]
             % else:
             #[css(if_empty = "none", iterable)]
             % endif
-            pub crate::OwnedSlice<single_value::SpecifiedValue>,
+            pub Vec<single_value::SpecifiedValue>,
         );
 
         pub fn get_initial_value() -> computed_value::T {
             % if allow_empty and allow_empty != "NotInitial":
-                computed_value::List(Default::default())
+                computed_value::List(vec![])
             % else:
                 let mut v = SmallVec::new();
                 v.push(single_value::get_initial_value());
                 computed_value::List(v)
             % endif
         }
 
         pub fn parse<'i, 't>(
             context: &ParserContext,
             input: &mut Parser<'i, 't>,
         ) -> Result<SpecifiedValue, ParseError<'i>> {
             use style_traits::Separator;
 
             % if allow_empty:
-            if input.try(|input| input.expect_ident_matching("none")).is_ok() {
-                return Ok(SpecifiedValue(Default::default()))
-            }
+                if input.try(|input| input.expect_ident_matching("none")).is_ok() {
+                    return Ok(SpecifiedValue(Vec::new()))
+                }
             % endif
 
-            let v = style_traits::${separator}::parse(input, |parser| {
+            style_traits::${separator}::parse(input, |parser| {
                 single_value::parse(context, parser)
-            })?;
-            Ok(SpecifiedValue(v.into()))
+            }).map(SpecifiedValue)
         }
 
         pub use self::single_value::SpecifiedValue as SingleSpecifiedValue;
 
-        % if not simple_vector_bindings:
         impl SpecifiedValue {
-            fn compute_iter<'a, 'cx, 'cx_a>(
+            pub fn compute_iter<'a, 'cx, 'cx_a>(
                 &'a self,
                 context: &'cx Context<'cx_a>,
             ) -> computed_value::Iter<'a, 'cx, 'cx_a> {
                 computed_value::Iter::new(context, &self.0)
             }
         }
-        % endif
 
         impl ToComputedValue for SpecifiedValue {
             type ComputedValue = computed_value::T;
 
             #[inline]
             fn to_computed_value(&self, context: &Context) -> computed_value::T {
-                % if not is_shared_list:
-                use std::iter::FromIterator;
-                % endif
-                computed_value::List(computed_value::UnderlyingList::from_iter(
-                    self.0.iter().map(|i| i.to_computed_value(context))
-                ))
+                computed_value::List(self.compute_iter(context).collect())
             }
 
             #[inline]
             fn from_computed_value(computed: &computed_value::T) -> Self {
-                let iter = computed.0.iter().map(ToComputedValue::from_computed_value);
-                SpecifiedValue(iter.collect())
+                SpecifiedValue(computed.0.iter()
+                                    .map(ToComputedValue::from_computed_value)
+                                    .collect())
             }
         }
     </%call>
 </%def>
 <%def name="longhand(*args, **kwargs)">
     <%
         property = data.declare_longhand(*args, **kwargs)
         if property is None:
@@ -488,17 +370,17 @@
                 }
             % endif
 
             % if not property.style_struct.inherited and property.logical:
                 context.rule_cache_conditions.borrow_mut()
                     .set_writing_mode_dependency(context.builder.writing_mode);
             % endif
 
-            % if property.is_vector and not property.simple_vector_bindings:
+            % if property.is_vector:
                 // In the case of a vector property we want to pass down an
                 // iterator so that this can be computed without allocation.
                 //
                 // However, computing requires a context, but the style struct
                 // being mutated is on the context. We temporarily remove it,
                 // mutate it, and then put it back. Vector longhands cannot
                 // touch their own style struct whilst computing, else this will
                 // panic.
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -768,17 +768,16 @@ macro_rules! animated_list_impl {
                         },
                     }
                 }).sum()
             }
         }
     }
 }
 
-animated_list_impl!(<T> for crate::OwnedSlice<T>);
 animated_list_impl!(<T> for SmallVec<[T; 1]>);
 animated_list_impl!(<T> for Vec<T>);
 
 /// <https://drafts.csswg.org/css-transitions/#animtype-visibility>
 impl Animate for Visibility {
     #[inline]
     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         let (this_weight, other_weight) = procedure.weights();
--- a/servo/components/style/properties/longhands/box.mako.rs
+++ b/servo/components/style/properties/longhands/box.mako.rs
@@ -347,16 +347,17 @@
 <% 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/effects.mako.rs
+++ b/servo/components/style/properties/longhands/effects.mako.rs
@@ -18,17 +18,16 @@
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type(
     "box-shadow",
     "BoxShadow",
     None,
     vector=True,
-    simple_vector_bindings=True,
     animation_value_type="AnimatedBoxShadowList",
     vector_animation_type="with_zero",
     extra_prefixes="webkit",
     ignored_when_colors_disabled=True,
     flags="APPLIES_TO_FIRST_LETTER",
     spec="https://drafts.csswg.org/css-backgrounds/#box-shadow",
 )}
 
--- a/servo/components/style/properties/longhands/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_svg.mako.rs
@@ -189,13 +189,16 @@
     products="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder",
 )}
 
 ${helpers.predefined_type(
     "-moz-context-properties",
     "MozContextProperties",
-    "computed::MozContextProperties::default()",
-    products="gecko",
+    initial_value=None,
+    vector=True,
+    need_index=True,
     animation_value_type="none",
+    products="gecko",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)",
+    allow_empty=True,
 )}
--- a/servo/components/style/properties/longhands/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_text.mako.rs
@@ -213,17 +213,16 @@
 ${helpers.predefined_type(
     "text-shadow",
     "SimpleShadow",
     None,
     vector=True,
     vector_animation_type="with_zero",
     animation_value_type="AnimatedTextShadowList",
     ignored_when_colors_disabled=True,
-    simple_vector_bindings=True,
     flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
     spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
 )}
 
 ${helpers.predefined_type(
     "text-emphasis-style",
     "TextEmphasisStyle",
     None,
--- a/servo/components/style/properties/longhands/ui.mako.rs
+++ b/servo/components/style/properties/longhands/ui.mako.rs
@@ -76,16 +76,17 @@
     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/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -3439,17 +3439,17 @@ impl<'a> StyleBuilder<'a> {
                 reset_struct,
                 % if property.logical:
                 self.writing_mode,
                 % endif
             );
     }
     % endif
 
-    % if not property.is_vector or property.simple_vector_bindings:
+    % if not property.is_vector:
     /// Set the `${property.ident}` to the computed value `value`.
     #[allow(non_snake_case)]
     pub fn set_${property.ident}(
         &mut self,
         value: longhands::${property.ident}::computed_value::T
     ) {
         % if not property.style_struct.inherited:
         self.modified_reset = true;
--- a/servo/components/style/properties/shorthands/background.mako.rs
+++ b/servo/components/style/properties/shorthands/background.mako.rs
@@ -35,21 +35,21 @@
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         let mut background_color = None;
 
         % for name in "image position_x position_y repeat size attachment origin clip".split():
-        // Vec grows from 0 to 4 by default on first push().  So allocate with
-        // capacity 1, so in the common case of only one item we don't way
-        // overallocate, then shrink.  Note that we always push at least one
-        // item if parsing succeeds.
-        let mut background_${name} = Vec::with_capacity(1);
+            // Vec grows from 0 to 4 by default on first push().  So allocate
+            // with capacity 1, so in the common case of only one item we don't
+            // way overallocate.  Note that we always push at least one item if
+            // parsing succeeds.
+            let mut background_${name} = background_${name}::SpecifiedValue(Vec::with_capacity(1));
         % endfor
         input.parse_comma_separated(|input| {
             // background-color can only be in the last element, so if it
             // is parsed anywhere before, the value is invalid.
             if background_color.is_some() {
                 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
 
@@ -94,41 +94,46 @@
             }
             let mut any = false;
             % for name in "image position repeat size attachment origin clip".split():
                 any = any || ${name}.is_some();
             % endfor
             any = any || background_color.is_some();
             if any {
                 if let Some(position) = position {
-                    background_position_x.push(position.horizontal);
-                    background_position_y.push(position.vertical);
+                    background_position_x.0.push(position.horizontal);
+                    background_position_y.0.push(position.vertical);
                 } else {
-                    background_position_x.push(PositionComponent::zero());
-                    background_position_y.push(PositionComponent::zero());
+                    background_position_x.0.push(PositionComponent::zero());
+                    background_position_y.0.push(PositionComponent::zero());
                 }
                 % for name in "image repeat size attachment origin clip".split():
                     if let Some(bg_${name}) = ${name} {
-                        background_${name}.push(bg_${name});
+                        background_${name}.0.push(bg_${name});
                     } else {
-                        background_${name}.push(background_${name}::single_value
+                        background_${name}.0.push(background_${name}::single_value
                                                                     ::get_initial_specified_value());
                     }
                 % endfor
                 Ok(())
             } else {
                 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         })?;
 
         Ok(expanded! {
              background_color: background_color.unwrap_or(Color::transparent()),
-             % for name in "image position_x position_y repeat size attachment origin clip".split():
-             background_${name}: background_${name}::SpecifiedValue(background_${name}.into()),
-             % endfor
+             background_image: background_image,
+             background_position_x: background_position_x,
+             background_position_y: background_position_y,
+             background_repeat: background_repeat,
+             background_attachment: background_attachment,
+             background_size: background_size,
+             background_origin: background_origin,
+             background_clip: background_clip,
          })
     }
 
     impl<'a> ToCss for LonghandsToSerialize<'a>  {