Bug 1496008 - Serialize a bunch of image properties with Servo. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 05 Oct 2018 20:08:55 +0000
changeset 495583 f36371c06b5ea5af8133de9bde6a431b5060cc6c
parent 495582 a3d2029e7b26c3ae312b17f7ab11916eec5de355
child 495584 3e75d7c78e42350683289433ff419c975d63fafc
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1496008
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1496008 - Serialize a bunch of image properties with Servo. r=heycam I had to fix the conversion for BackgroundSize too, hopefully we can simplify all this using cbindgen in the future instead of CalcValue. Differential Revision: https://phabricator.services.mozilla.com/D7580
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoCSSPropList.mako.py
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/test/test_computed_style.html
layout/style/test/test_transitions_per_property.html
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/url.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/values/computed/length.rs
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -2056,16 +2056,23 @@ void
 Gecko_GetComputedURLSpec(const URLValueData* aURL, nsCString* aOut)
 {
   MOZ_ASSERT(aURL);
   MOZ_ASSERT(aOut);
   if (aURL->IsLocalRef()) {
     aOut->Assign(aURL->GetString());
     return;
   }
+  Gecko_GetComputedImageURLSpec(aURL, aOut);
+}
+
+void
+Gecko_GetComputedImageURLSpec(const URLValueData* aURL, nsCString* aOut)
+{
+  // Image URIs don't serialize local refs as local.
   if (nsIURI* uri = aURL->GetURI()) {
     nsresult rv = uri->GetSpec(*aOut);
     if (NS_SUCCEEDED(rv)) {
       return;
     }
   }
 
   aOut->AssignLiteral("about:invalid");
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -545,16 +545,17 @@ void Gecko_nsStyleSVGPaint_Reset(nsStyle
 void Gecko_nsStyleSVG_SetDashArrayLength(nsStyleSVG* svg, uint32_t len);
 void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* dst, const nsStyleSVG* src);
 void Gecko_nsStyleSVG_SetContextPropertiesLength(nsStyleSVG* svg, uint32_t len);
 void Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* dst, const nsStyleSVG* src);
 
 mozilla::css::URLValue* Gecko_NewURLValue(ServoBundledURI uri);
 size_t Gecko_URLValue_SizeOfIncludingThis(mozilla::css::URLValue* url);
 void Gecko_GetComputedURLSpec(const mozilla::css::URLValueData* url, nsCString* spec);
+void Gecko_GetComputedImageURLSpec(const mozilla::css::URLValueData* url, nsCString* spec);
 void Gecko_nsIURI_Debug(nsIURI*, nsCString* spec);
 
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::URLValue, CSSURLValue);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(RawGeckoURLExtraData, URLExtraData);
 
 void Gecko_FillAllImageLayers(nsStyleImageLayers* layers, uint32_t max_len);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
 
--- a/layout/style/ServoCSSPropList.mako.py
+++ b/layout/style/ServoCSSPropList.mako.py
@@ -62,16 +62,18 @@ def method(prop):
 
 # Colors, integers and lengths are easy as well.
 #
 # TODO(emilio): This will go away once the rest of the longhands have been
 # moved or perhaps using a blacklist for the ones with non-layout-dependence
 # but other non-trivial dependence like scrollbar colors.
 SERIALIZED_PREDEFINED_TYPES = [
     "Appearance",
+    "BackgroundRepeat",
+    "BackgroundSize",
     "Clear",
     "ClipRectOrAuto",
     "Color",
     "Content",
     "CounterIncrement",
     "CounterReset",
     "Float",
     "FontFamily",
@@ -85,26 +87,31 @@ SERIALIZED_PREDEFINED_TYPES = [
     "FontVariant",
     "FontVariantAlternates",
     "FontVariantEastAsian",
     "FontVariantLigatures",
     "FontVariantNumeric",
     "FontVariationSettings",
     "FontWeight",
     "Integer",
+    "ImageLayer",
     "Length",
     "LengthOrPercentage",
     "NonNegativeLength",
     "NonNegativeLengthOrPercentage",
     "ListStyleType",
     "OffsetPath",
     "Opacity",
     "Resize",
+    "TransformStyle",
+    "background::BackgroundSize",
     "basic_shape::ClippingShape",
     "basic_shape::FloatAreaShape",
+    "position::HorizontalPosition",
+    "position::VerticalPosition",
     "url::ImageUrlOrNone",
 ]
 
 def serialized_by_servo(prop):
     # If the property requires layout information, no such luck.
     if "GETCS_NEEDS_LAYOUT_FLUSH" in prop.flags:
         return False
     # No shorthands yet.
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -185,32 +185,16 @@ nsCSSProps::GetStringValue(nsCSSCounterD
   } else {
     static nsDependentCString sNullStr("");
     return sNullStr;
   }
 }
 
 /***************************************************************************/
 
-const KTableEntry nsCSSProps::kTransformStyleKTable[] = {
-  { eCSSKeyword_flat, NS_STYLE_TRANSFORM_STYLE_FLAT },
-  { eCSSKeyword_preserve_3d, NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D },
-  { eCSSKeyword_UNKNOWN, -1 }
-};
-
-const KTableEntry nsCSSProps::kImageLayerRepeatKTable[] = {
-  { eCSSKeyword_no_repeat,  StyleImageLayerRepeat::NoRepeat },
-  { eCSSKeyword_repeat,     StyleImageLayerRepeat::Repeat },
-  { eCSSKeyword_repeat_x,   StyleImageLayerRepeat::RepeatX },
-  { eCSSKeyword_repeat_y,   StyleImageLayerRepeat::RepeatY },
-  { eCSSKeyword_round,      StyleImageLayerRepeat::Round},
-  { eCSSKeyword_space,      StyleImageLayerRepeat::Space},
-  { eCSSKeyword_UNKNOWN, -1 }
-};
-
 const KTableEntry nsCSSProps::kBorderImageRepeatKTable[] = {
   { eCSSKeyword_stretch, StyleBorderImageRepeat::Stretch },
   { eCSSKeyword_repeat, StyleBorderImageRepeat::Repeat },
   { eCSSKeyword_round, StyleBorderImageRepeat::Round },
   { eCSSKeyword_space, StyleBorderImageRepeat::Space },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
@@ -561,24 +545,16 @@ const KTableEntry nsCSSProps::kOverflowS
   { eCSSKeyword_visible, NS_STYLE_OVERFLOW_VISIBLE },
   { eCSSKeyword_hidden, NS_STYLE_OVERFLOW_HIDDEN },
   { eCSSKeyword_scroll, NS_STYLE_OVERFLOW_SCROLL },
   // Deprecated:
   { eCSSKeyword__moz_hidden_unscrollable, NS_STYLE_OVERFLOW_CLIP },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
-const KTableEntry nsCSSProps::kRadialGradientSizeKTable[] = {
-  { eCSSKeyword_closest_side,    NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE },
-  { eCSSKeyword_closest_corner,  NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER },
-  { eCSSKeyword_farthest_side,   NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE },
-  { eCSSKeyword_farthest_corner, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER },
-  { eCSSKeyword_UNKNOWN,         -1 }
-};
-
 const KTableEntry nsCSSProps::kOverscrollBehaviorKTable[] = {
   { eCSSKeyword_auto,       StyleOverscrollBehavior::Auto },
   { eCSSKeyword_contain,    StyleOverscrollBehavior::Contain },
   { eCSSKeyword_none,       StyleOverscrollBehavior::None },
   { eCSSKeyword_UNKNOWN,    -1 }
 };
 
 const KTableEntry nsCSSProps::kScrollSnapTypeKTable[] = {
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -302,18 +302,16 @@ public:
 #define CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(it_, prop_, enabledstate_)   \
   for (const nsCSSPropertyID *it_ = nsCSSProps::SubpropertyEntryFor(prop_), \
                             es_ = (nsCSSPropertyID)((enabledstate_) |       \
                                                   CSSEnabledState(0));    \
        *it_ != eCSSProperty_UNKNOWN; ++it_)                               \
     if (nsCSSProps::IsEnabled(*it_, (mozilla::CSSEnabledState) es_))
 
   // Keyword/Enum value tables
-  static const KTableEntry kTransformStyleKTable[];
-  static const KTableEntry kImageLayerRepeatKTable[];
   // Not const because we modify its entries when the pref
   // "layout.css.background-clip.text" changes:
   static const KTableEntry kBorderImageRepeatKTable[];
   static const KTableEntry kBorderStyleKTable[];
   static const KTableEntry kShapeRadiusKTable[];
   static const KTableEntry kFilterFunctionKTable[];
   static const KTableEntry kBoxShadowTypeKTable[];
   static const KTableEntry kCursorKTable[];
@@ -340,17 +338,16 @@ public:
   static const KTableEntry kGridAutoFlowKTable[];
   static const KTableEntry kGridTrackBreadthKTable[];
   static const KTableEntry kLineHeightKTable[];
   static const KTableEntry kContainKTable[];
   static const KTableEntry kOutlineStyleKTable[];
   static const KTableEntry kOverflowKTable[];
   static const KTableEntry kOverflowSubKTable[];
   static const KTableEntry kOverflowClipBoxKTable[];
-  static const KTableEntry kRadialGradientSizeKTable[];
   static const KTableEntry kOverscrollBehaviorKTable[];
   static const KTableEntry kScrollSnapTypeKTable[];
   // Not const because we modify its entries when the pref
   // "layout.css.text-align-unsafe-value.enabled" changes:
   static KTableEntry kTextAlignKTable[];
   static const KTableEntry kTextDecorationLineKTable[];
   static const KTableEntry kTextDecorationStyleKTable[];
   static const KTableEntry kTextEmphasisStyleShapeKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1289,26 +1289,16 @@ already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetPerspective()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   SetValueToCoord(val, StyleDisplay()->mChildPerspective, false);
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetTransformStyle()
-{
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  val->SetIdent(
-      nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mTransformStyle,
-                                     nsCSSProps::kTransformStyleKTable));
-  return val.forget();
-}
-
-already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetTransform()
 {
   const nsStyleDisplay* display = StyleDisplay();
   return GetTransformValue(display->mSpecifiedTransform);
 }
 
 static already_AddRefed<CSSValue>
 ReadIndividualTransformValue(nsCSSValueSharedList* aList,
@@ -1649,322 +1639,16 @@ SetValueToCalc(const nsStyleCoord::CalcV
     result.Append(tmp);
   }
 
   result.Append(')');
 
   aValue->SetString(result); // not really SetString
 }
 
-static void
-AppendCSSGradientLength(const nsStyleCoord&    aValue,
-                        nsROCSSPrimitiveValue* aPrimitive,
-                        nsAString&             aString)
-{
-  nsAutoString tokenString;
-  if (aValue.IsCalcUnit())
-    SetValueToCalc(aValue.GetCalcValue(), aPrimitive);
-  else if (aValue.GetUnit() == eStyleUnit_Coord)
-    aPrimitive->SetAppUnits(aValue.GetCoordValue());
-  else
-    aPrimitive->SetPercent(aValue.GetPercentValue());
-  aPrimitive->GetCssText(tokenString);
-  aString.Append(tokenString);
-}
-
-static void
-AppendCSSGradientToBoxPosition(const nsStyleGradient* aGradient,
-                               nsAString&             aString,
-                               bool&                  aNeedSep)
-{
-  // This function only supports box position keywords. Make sure we're not
-  // calling it with inputs that would have coordinates that aren't
-  // representable with box-position keywords.
-  MOZ_ASSERT(aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR &&
-             !(aGradient->mLegacySyntax && aGradient->mMozLegacySyntax),
-             "Only call me for linear-gradient and -webkit-linear-gradient");
-
-  float xValue = aGradient->mBgPosX.GetPercentValue();
-  float yValue = aGradient->mBgPosY.GetPercentValue();
-
-  if (xValue == 0.5f &&
-      yValue == (aGradient->mLegacySyntax ? 0.0f : 1.0f)) {
-    // omit "to bottom" in modern syntax, "top" in legacy syntax
-    return;
-  }
-  NS_ASSERTION(yValue != 0.5f || xValue != 0.5f, "invalid box position");
-
-  if (!aGradient->mLegacySyntax) {
-    // Modern syntax explicitly includes the word "to". Old syntax does not
-    // (and is implicitly "from" the given position instead).
-    aString.AppendLiteral("to ");
-  }
-
-  if (xValue == 0.0f) {
-    aString.AppendLiteral("left");
-  } else if (xValue == 1.0f) {
-    aString.AppendLiteral("right");
-  } else if (xValue != 0.5f) { // do not write "center" keyword
-    MOZ_ASSERT_UNREACHABLE("invalid box position");
-  }
-
-  if (xValue != 0.5f && yValue != 0.5f) {
-    // We're appending both an x-keyword and a y-keyword.
-    // Add a space between them here.
-    aString.AppendLiteral(" ");
-  }
-
-  if (yValue == 0.0f) {
-    aString.AppendLiteral("top");
-  } else if (yValue == 1.0f) {
-    aString.AppendLiteral("bottom");
-  } else if (yValue != 0.5f) { // do not write "center" keyword
-    MOZ_ASSERT_UNREACHABLE("invalid box position");
-  }
-
-
-  aNeedSep = true;
-}
-
-void
-nsComputedDOMStyle::GetCSSGradientString(const nsStyleGradient* aGradient,
-                                         nsAString& aString)
-{
-  if (!aGradient->mLegacySyntax) {
-    aString.Truncate();
-  } else {
-    if (aGradient->mMozLegacySyntax) {
-      aString.AssignLiteral("-moz-");
-    } else {
-      aString.AssignLiteral("-webkit-");
-    }
-  }
-  if (aGradient->mRepeating) {
-    aString.AppendLiteral("repeating-");
-  }
-  bool isRadial = aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR;
-  if (isRadial) {
-    aString.AppendLiteral("radial-gradient(");
-  } else {
-    aString.AppendLiteral("linear-gradient(");
-  }
-
-  bool needSep = false;
-  nsAutoString tokenString;
-  RefPtr<nsROCSSPrimitiveValue> tmpVal = new nsROCSSPrimitiveValue;
-
-  if (isRadial && !aGradient->mLegacySyntax) {
-    if (aGradient->mSize != NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE) {
-      if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
-        aString.AppendLiteral("circle");
-        needSep = true;
-      }
-      if (aGradient->mSize != NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER) {
-        if (needSep) {
-          aString.Append(' ');
-        }
-        AppendASCIItoUTF16(nsCSSProps::
-                           ValueToKeyword(aGradient->mSize,
-                                          nsCSSProps::kRadialGradientSizeKTable),
-                           aString);
-        needSep = true;
-      }
-    } else {
-      AppendCSSGradientLength(aGradient->mRadiusX, tmpVal, aString);
-      if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
-        aString.Append(' ');
-        AppendCSSGradientLength(aGradient->mRadiusY, tmpVal, aString);
-      }
-      needSep = true;
-    }
-  }
-  if (aGradient->mBgPosX.GetUnit() != eStyleUnit_None) {
-    MOZ_ASSERT(aGradient->mBgPosY.GetUnit() != eStyleUnit_None);
-    if (!isRadial &&
-        !(aGradient->mLegacySyntax && aGradient->mMozLegacySyntax)) {
-      // linear-gradient() or -webkit-linear-gradient()
-      AppendCSSGradientToBoxPosition(aGradient, aString, needSep);
-    } else if (aGradient->mBgPosX.GetUnit() != eStyleUnit_Percent ||
-               aGradient->mBgPosX.GetPercentValue() != 0.5f ||
-               aGradient->mBgPosY.GetUnit() != eStyleUnit_Percent ||
-               aGradient->mBgPosY.GetPercentValue() != (isRadial ? 0.5f : 0.0f)) {
-      // [-vendor-]radial-gradient or -moz-linear-gradient, with
-      // non-default box position, which we output here.
-      if (isRadial && !aGradient->mLegacySyntax) {
-        if (needSep) {
-          aString.Append(' ');
-        }
-        aString.AppendLiteral("at ");
-        needSep = false;
-      }
-      AppendCSSGradientLength(aGradient->mBgPosX, tmpVal, aString);
-      if (aGradient->mBgPosY.GetUnit() != eStyleUnit_None) {
-        aString.Append(' ');
-        AppendCSSGradientLength(aGradient->mBgPosY, tmpVal, aString);
-      }
-      needSep = true;
-    }
-  }
-  if (aGradient->mAngle.GetUnit() != eStyleUnit_None) {
-    MOZ_ASSERT(!isRadial || aGradient->mLegacySyntax);
-    if (needSep) {
-      aString.Append(' ');
-    }
-    nsStyleUtil::AppendAngleValue(aGradient->mAngle, aString);
-    needSep = true;
-  }
-
-  if (isRadial && aGradient->mLegacySyntax &&
-      (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR ||
-       aGradient->mSize != NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER)) {
-    MOZ_ASSERT(aGradient->mSize != NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE);
-    if (needSep) {
-      aString.AppendLiteral(", ");
-      needSep = false;
-    }
-    if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
-      aString.AppendLiteral("circle");
-      needSep = true;
-    }
-    if (aGradient->mSize != NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER) {
-      if (needSep) {
-        aString.Append(' ');
-      }
-      AppendASCIItoUTF16(nsCSSProps::
-                         ValueToKeyword(aGradient->mSize,
-                                        nsCSSProps::kRadialGradientSizeKTable),
-                         aString);
-    }
-    needSep = true;
-  }
-
-
-  // color stops
-  for (uint32_t i = 0; i < aGradient->mStops.Length(); ++i) {
-    if (needSep) {
-      aString.AppendLiteral(", ");
-    }
-
-    const auto& stop = aGradient->mStops[i];
-    if (!stop.mIsInterpolationHint) {
-      SetValueFromComplexColor(tmpVal, stop.mColor);
-      tmpVal->GetCssText(tokenString);
-      aString.Append(tokenString);
-    }
-
-    if (stop.mLocation.GetUnit() != eStyleUnit_None) {
-      if (!stop.mIsInterpolationHint) {
-        aString.Append(' ');
-      }
-      AppendCSSGradientLength(stop.mLocation, tmpVal, aString);
-    }
-    needSep = true;
-  }
-
-  aString.Append(')');
-}
-
-// -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
-void
-nsComputedDOMStyle::GetImageRectString(nsIURI* aURI,
-                                       const nsStyleSides& aCropRect,
-                                       nsString& aString)
-{
-  RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
-
-  // <uri>
-  RefPtr<nsROCSSPrimitiveValue> valURI = new nsROCSSPrimitiveValue;
-  valURI->SetURI(aURI);
-  valueList->AppendCSSValue(valURI.forget());
-
-  // <top>, <right>, <bottom>, <left>
-  NS_FOR_CSS_SIDES(side) {
-    RefPtr<nsROCSSPrimitiveValue> valSide = new nsROCSSPrimitiveValue;
-    SetValueToCoord(valSide, aCropRect.Get(side), false);
-    valueList->AppendCSSValue(valSide.forget());
-  }
-
-  nsAutoString argumentString;
-  valueList->GetCssText(argumentString);
-
-  aString = NS_LITERAL_STRING("-moz-image-rect(") +
-            argumentString +
-            NS_LITERAL_STRING(")");
-}
-
-void
-nsComputedDOMStyle::SetValueToStyleImage(const nsStyleImage& aStyleImage,
-                                         nsROCSSPrimitiveValue* aValue)
-{
-  switch (aStyleImage.GetType()) {
-    case eStyleImageType_Image:
-    {
-      nsCOMPtr<nsIURI> uri = aStyleImage.GetImageURI();
-      if (!uri) {
-        aValue->SetIdent(eCSSKeyword_none);
-        break;
-      }
-
-      const UniquePtr<nsStyleSides>& cropRect = aStyleImage.GetCropRect();
-      if (cropRect) {
-        nsAutoString imageRectString;
-        GetImageRectString(uri, *cropRect, imageRectString);
-        aValue->SetString(imageRectString);
-      } else {
-        aValue->SetURI(uri);
-      }
-      break;
-    }
-    case eStyleImageType_Gradient:
-    {
-      nsAutoString gradientString;
-      GetCSSGradientString(aStyleImage.GetGradientData(),
-                           gradientString);
-      aValue->SetString(gradientString);
-      break;
-    }
-    case eStyleImageType_Element:
-    {
-      nsAutoString elementId;
-      nsStyleUtil::AppendEscapedCSSIdent(
-        nsDependentAtomString(aStyleImage.GetElementId()),
-                              elementId);
-      nsAutoString elementString = NS_LITERAL_STRING("-moz-element(#") +
-                                   elementId +
-                                   NS_LITERAL_STRING(")");
-      aValue->SetString(elementString);
-      break;
-    }
-    case eStyleImageType_Null:
-      aValue->SetIdent(eCSSKeyword_none);
-      break;
-    case eStyleImageType_URL:
-      SetValueToURLValue(aStyleImage.GetURLValue(), aValue);
-      break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("unexpected image type");
-      break;
-  }
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetImageLayerImage(const nsStyleImageLayers& aLayers)
-{
-  RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
-
-  for (uint32_t i = 0, i_end = aLayers.mImageCount; i < i_end; ++i) {
-    RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-
-    SetValueToStyleImage(aLayers.mLayers[i].mImage, val);
-    valueList->AppendCSSValue(val.forget());
-  }
-
-  return valueList.forget();
-}
-
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetImageLayerPosition(const nsStyleImageLayers& aLayers)
 {
   if (aLayers.mPositionXCount != aLayers.mPositionYCount) {
     // No value to return.  We can't express this combination of
     // values as a shorthand.
     return nullptr;
   }
@@ -1975,176 +1659,16 @@ nsComputedDOMStyle::DoGetImageLayerPosit
 
     SetValueToPosition(aLayers.mLayers[i].mPosition, itemList);
     valueList->AppendCSSValue(itemList.forget());
   }
 
   return valueList.forget();
 }
 
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetImageLayerPositionX(const nsStyleImageLayers& aLayers)
-{
-  RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
-  for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) {
-    RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-    SetValueToPositionCoord(aLayers.mLayers[i].mPosition.mXPosition, val);
-    valueList->AppendCSSValue(val.forget());
-  }
-
-  return valueList.forget();
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetImageLayerPositionY(const nsStyleImageLayers& aLayers)
-{
-  RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
-  for (uint32_t i = 0, i_end = aLayers.mPositionYCount; i < i_end; ++i) {
-    RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-    SetValueToPositionCoord(aLayers.mLayers[i].mPosition.mYPosition, val);
-    valueList->AppendCSSValue(val.forget());
-  }
-
-  return valueList.forget();
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetImageLayerRepeat(const nsStyleImageLayers& aLayers)
-{
-  RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
-
-  for (uint32_t i = 0, i_end = aLayers.mRepeatCount; i < i_end; ++i) {
-    RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
-    RefPtr<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
-
-    const StyleImageLayerRepeat xRepeat = aLayers.mLayers[i].mRepeat.mXRepeat;
-    const StyleImageLayerRepeat yRepeat = aLayers.mLayers[i].mRepeat.mYRepeat;
-
-    bool hasContraction = true;
-    unsigned contraction;
-    if (xRepeat == yRepeat) {
-      contraction = uint8_t(xRepeat);
-    } else if (xRepeat == StyleImageLayerRepeat::Repeat &&
-               yRepeat == StyleImageLayerRepeat::NoRepeat) {
-      contraction = uint8_t(StyleImageLayerRepeat::RepeatX);
-    } else if (xRepeat == StyleImageLayerRepeat::NoRepeat &&
-               yRepeat == StyleImageLayerRepeat::Repeat) {
-      contraction = uint8_t(StyleImageLayerRepeat::RepeatY);
-    } else {
-      hasContraction = false;
-    }
-
-    RefPtr<nsROCSSPrimitiveValue> valY;
-    if (hasContraction) {
-      valX->SetIdent(nsCSSProps::ValueToKeywordEnum(contraction,
-                                         nsCSSProps::kImageLayerRepeatKTable));
-    } else {
-      valY = new nsROCSSPrimitiveValue;
-
-      valX->SetIdent(nsCSSProps::ValueToKeywordEnum(xRepeat,
-                                          nsCSSProps::kImageLayerRepeatKTable));
-      valY->SetIdent(nsCSSProps::ValueToKeywordEnum(yRepeat,
-                                          nsCSSProps::kImageLayerRepeatKTable));
-    }
-    itemList->AppendCSSValue(valX.forget());
-    if (valY) {
-      itemList->AppendCSSValue(valY.forget());
-    }
-    valueList->AppendCSSValue(itemList.forget());
-  }
-
-  return valueList.forget();
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetImageLayerSize(const nsStyleImageLayers& aLayers)
-{
-  RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
-
-  for (uint32_t i = 0, i_end = aLayers.mSizeCount; i < i_end; ++i) {
-    const nsStyleImageLayers::Size &size = aLayers.mLayers[i].mSize;
-
-    switch (size.mWidthType) {
-      case nsStyleImageLayers::Size::eContain:
-      case nsStyleImageLayers::Size::eCover: {
-        MOZ_ASSERT(size.mWidthType == size.mHeightType,
-                   "unsynced types");
-        nsCSSKeyword keyword = size.mWidthType == nsStyleImageLayers::Size::eContain
-                             ? eCSSKeyword_contain
-                             : eCSSKeyword_cover;
-        RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-        val->SetIdent(keyword);
-        valueList->AppendCSSValue(val.forget());
-        break;
-      }
-      default: {
-        RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
-
-        RefPtr<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
-        RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
-
-        if (size.mWidthType == nsStyleImageLayers::Size::eAuto) {
-          valX->SetIdent(eCSSKeyword_auto);
-        } else {
-          MOZ_ASSERT(size.mWidthType ==
-                       nsStyleImageLayers::Size::eLengthPercentage,
-                     "bad mWidthType");
-          if (!size.mWidth.mHasPercent &&
-              // negative values must have come from calc()
-              size.mWidth.mLength >= 0) {
-            MOZ_ASSERT(size.mWidth.mPercent == 0.0f,
-                       "Shouldn't have mPercent");
-            valX->SetAppUnits(size.mWidth.mLength);
-          } else if (size.mWidth.mLength == 0 &&
-                     // negative values must have come from calc()
-                     size.mWidth.mPercent >= 0.0f) {
-            valX->SetPercent(size.mWidth.mPercent);
-          } else {
-            SetValueToCalc(&size.mWidth, valX);
-          }
-        }
-
-        if (size.mHeightType == nsStyleImageLayers::Size::eAuto) {
-          valY->SetIdent(eCSSKeyword_auto);
-        } else {
-          MOZ_ASSERT(size.mHeightType ==
-                       nsStyleImageLayers::Size::eLengthPercentage,
-                     "bad mHeightType");
-          if (!size.mHeight.mHasPercent &&
-              // negative values must have come from calc()
-              size.mHeight.mLength >= 0) {
-            MOZ_ASSERT(size.mHeight.mPercent == 0.0f,
-                       "Shouldn't have mPercent");
-            valY->SetAppUnits(size.mHeight.mLength);
-          } else if (size.mHeight.mLength == 0 &&
-                     // negative values must have come from calc()
-                     size.mHeight.mPercent >= 0.0f) {
-            valY->SetPercent(size.mHeight.mPercent);
-          } else {
-            SetValueToCalc(&size.mHeight, valY);
-          }
-        }
-        itemList->AppendCSSValue(valX.forget());
-        itemList->AppendCSSValue(valY.forget());
-        valueList->AppendCSSValue(itemList.forget());
-        break;
-      }
-    }
-  }
-
-  return valueList.forget();
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBackgroundImage()
-{
-  const nsStyleImageLayers& layers = StyleBackground()->mImage;
-  return DoGetImageLayerImage(layers);
-}
-
 void
 nsComputedDOMStyle::SetValueToPositionCoord(
     const Position::Coord& aCoord,
     nsROCSSPrimitiveValue* aValue)
 {
   if (!aCoord.mHasPercent) {
     MOZ_ASSERT(aCoord.mPercent == 0.0f,
                "Shouldn't have mPercent!");
@@ -2203,44 +1727,16 @@ nsComputedDOMStyle::SetValueToURLValue(c
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetBackgroundPosition()
 {
   const nsStyleImageLayers& layers = StyleBackground()->mImage;
   return DoGetImageLayerPosition(layers);
 }
 
 already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBackgroundPositionX()
-{
-  const nsStyleImageLayers& layers = StyleBackground()->mImage;
-  return DoGetImageLayerPositionX(layers);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBackgroundPositionY()
-{
-  const nsStyleImageLayers& layers = StyleBackground()->mImage;
-  return DoGetImageLayerPositionY(layers);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBackgroundRepeat()
-{
-  const nsStyleImageLayers& layers = StyleBackground()->mImage;
-  return DoGetImageLayerRepeat(layers);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBackgroundSize()
-{
-  const nsStyleImageLayers& layers = StyleBackground()->mImage;
-  return DoGetImageLayerSize(layers);
-}
-
-already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetGridTemplateAreas()
 {
   const css::GridTemplateAreasValue* areas =
     StylePosition()->mGridTemplateAreas;
   if (!areas) {
     RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
     val->SetIdent(eCSSKeyword_none);
     return val.forget();
@@ -3497,28 +2993,16 @@ nsComputedDOMStyle::DoGetBoxFlex()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetNumber(StyleXUL()->mBoxFlex);
   return val.forget();
 }
 
 /* Border image properties */
 
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBorderImageSource()
-{
-  const nsStyleBorder* border = StyleBorder();
-
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  const nsStyleImage& image = border->mBorderImageSource;
-  SetValueToStyleImage(image, val);
-
-  return val.forget();
-}
-
 void
 nsComputedDOMStyle::AppendFourSideCoordValues(nsDOMCSSValueList* aList,
                                               const nsStyleSides& aValues)
 {
   const nsStyleCoord& top = aValues.Get(eSideTop);
   const nsStyleCoord& right = aValues.Get(eSideRight);
   const nsStyleCoord& bottom = aValues.Get(eSideBottom);
   const nsStyleCoord& left = aValues.Get(eSideLeft);
@@ -5050,58 +4534,23 @@ nsComputedDOMStyle::DoGetMask()
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
   SetValueToURLValue(firstLayer.mImage.GetURLValue(), val);
 
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetMaskImage()
-{
-  const nsStyleImageLayers& layers = StyleSVGReset()->mMask;
-  return DoGetImageLayerImage(layers);
-}
-
-already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetMaskPosition()
 {
   const nsStyleImageLayers& layers = StyleSVGReset()->mMask;
   return DoGetImageLayerPosition(layers);
 }
 
 already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetMaskPositionX()
-{
-  const nsStyleImageLayers& layers = StyleSVGReset()->mMask;
-  return DoGetImageLayerPositionX(layers);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetMaskPositionY()
-{
-  const nsStyleImageLayers& layers = StyleSVGReset()->mMask;
-  return DoGetImageLayerPositionY(layers);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetMaskRepeat()
-{
-  const nsStyleImageLayers& layers = StyleSVGReset()->mMask;
-  return DoGetImageLayerRepeat(layers);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetMaskSize()
-{
-  const nsStyleImageLayers& layers = StyleSVGReset()->mMask;
-  return DoGetImageLayerSize(layers);
-}
-
-already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetPaintOrder()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   nsAutoString string;
   uint8_t paintOrder = StyleSVG()->mPaintOrder;
   nsStyleUtil::AppendPaintOrderValue(paintOrder, string);
   val->SetString(string);
   return val.forget();
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -276,39 +276,24 @@ private:
   already_AddRefed<CSSValue> DoGetGridTemplateColumns();
   already_AddRefed<CSSValue> DoGetGridTemplateRows();
   already_AddRefed<CSSValue> DoGetGridColumnStart();
   already_AddRefed<CSSValue> DoGetGridColumnEnd();
   already_AddRefed<CSSValue> DoGetGridRowStart();
   already_AddRefed<CSSValue> DoGetGridRowEnd();
 
   /* StyleImageLayer properties */
-  already_AddRefed<CSSValue> DoGetImageLayerImage(const nsStyleImageLayers& aLayers);
   already_AddRefed<CSSValue> DoGetImageLayerPosition(const nsStyleImageLayers& aLayers);
-  already_AddRefed<CSSValue> DoGetImageLayerPositionX(const nsStyleImageLayers& aLayers);
-  already_AddRefed<CSSValue> DoGetImageLayerPositionY(const nsStyleImageLayers& aLayers);
-  already_AddRefed<CSSValue> DoGetImageLayerRepeat(const nsStyleImageLayers& aLayers);
-  already_AddRefed<CSSValue> DoGetImageLayerSize(const nsStyleImageLayers& aLayers);
 
   /* Background properties */
-  already_AddRefed<CSSValue> DoGetBackgroundImage();
   already_AddRefed<CSSValue> DoGetBackgroundPosition();
-  already_AddRefed<CSSValue> DoGetBackgroundPositionX();
-  already_AddRefed<CSSValue> DoGetBackgroundPositionY();
-  already_AddRefed<CSSValue> DoGetBackgroundRepeat();
-  already_AddRefed<CSSValue> DoGetBackgroundSize();
 
   /* Mask properties */
   already_AddRefed<CSSValue> DoGetMask();
-  already_AddRefed<CSSValue> DoGetMaskImage();
   already_AddRefed<CSSValue> DoGetMaskPosition();
-  already_AddRefed<CSSValue> DoGetMaskPositionX();
-  already_AddRefed<CSSValue> DoGetMaskPositionY();
-  already_AddRefed<CSSValue> DoGetMaskRepeat();
-  already_AddRefed<CSSValue> DoGetMaskSize();
 
   /* Padding properties */
   already_AddRefed<CSSValue> DoGetPaddingTop();
   already_AddRefed<CSSValue> DoGetPaddingBottom();
   already_AddRefed<CSSValue> DoGetPaddingLeft();
   already_AddRefed<CSSValue> DoGetPaddingRight();
 
   /* Table Properties */
@@ -325,17 +310,16 @@ private:
   already_AddRefed<CSSValue> DoGetBorderLeftWidth();
   already_AddRefed<CSSValue> DoGetBorderRightWidth();
   already_AddRefed<CSSValue> DoGetBorderBottomLeftRadius();
   already_AddRefed<CSSValue> DoGetBorderBottomRightRadius();
   already_AddRefed<CSSValue> DoGetBorderTopLeftRadius();
   already_AddRefed<CSSValue> DoGetBorderTopRightRadius();
 
   /* Border Image */
-  already_AddRefed<CSSValue> DoGetBorderImageSource();
   already_AddRefed<CSSValue> DoGetBorderImageSlice();
   already_AddRefed<CSSValue> DoGetBorderImageWidth();
   already_AddRefed<CSSValue> DoGetBorderImageOutset();
   already_AddRefed<CSSValue> DoGetBorderImageRepeat();
 
   /* Box Shadow */
   already_AddRefed<CSSValue> DoGetBoxShadow();
 
@@ -394,17 +378,16 @@ private:
   already_AddRefed<CSSValue> DoGetTouchAction();
   already_AddRefed<CSSValue> DoGetTransform();
   already_AddRefed<CSSValue> DoGetTranslate();
   already_AddRefed<CSSValue> DoGetRotate();
   already_AddRefed<CSSValue> DoGetScale();
   already_AddRefed<CSSValue> DoGetTransformOrigin();
   already_AddRefed<CSSValue> DoGetPerspective();
   already_AddRefed<CSSValue> DoGetPerspectiveOrigin();
-  already_AddRefed<CSSValue> DoGetTransformStyle();
   already_AddRefed<CSSValue> DoGetOverscrollBehaviorX();
   already_AddRefed<CSSValue> DoGetOverscrollBehaviorY();
   already_AddRefed<CSSValue> DoGetScrollSnapTypeX();
   already_AddRefed<CSSValue> DoGetScrollSnapTypeY();
   already_AddRefed<CSSValue> DoGetScrollSnapPointsX();
   already_AddRefed<CSSValue> DoGetScrollSnapPointsY();
   already_AddRefed<CSSValue> DoGetScrollSnapDestination();
   already_AddRefed<CSSValue> DoGetScrollSnapCoordinate();
@@ -478,18 +461,16 @@ private:
 
   /* Helper functions */
   void SetToRGBAColor(nsROCSSPrimitiveValue* aValue, nscolor aColor);
   void SetValueFromComplexColor(nsROCSSPrimitiveValue* aValue,
                                 const mozilla::StyleComplexColor& aColor);
   void SetValueForWidgetColor(nsROCSSPrimitiveValue* aValue,
                               const mozilla::StyleComplexColor& aColor,
                               mozilla::StyleAppearance aWidgetType);
-  void SetValueToStyleImage(const nsStyleImage& aStyleImage,
-                            nsROCSSPrimitiveValue* aValue);
   void SetValueToPositionCoord(const mozilla::Position::Coord& aCoord,
                                nsROCSSPrimitiveValue* aValue);
   void SetValueToPosition(const mozilla::Position& aPosition,
                           nsDOMCSSValueList* aValueList);
   void SetValueToURLValue(const mozilla::css::URLValueData* aURL,
                           nsROCSSPrimitiveValue* aValue);
 
   /**
--- a/layout/style/test/test_computed_style.html
+++ b/layout/style/test/test_computed_style.html
@@ -173,19 +173,19 @@ var noframe_container = document.getElem
     [ "0px 0px", "0px 0px", "0 with units" ],
     [ "0% 0%", "0% 0%", "0%" ],
     [ "calc(0px) 0", "0px 0px", "0 calc with units horizontal" ],
     [ "0 calc(0px)", "0px 0px", "0 calc with units vertical" ],
     [ "calc(3px - 3px) 0", "0px 0px", "computed 0 calc with units horizontal" ],
     [ "0 calc(3px - 3px)", "0px 0px", "computed 0 calc with units vertical" ],
     [ "calc(0%) 0", "0% 0px", "0% calc horizontal"],
     [ "0 calc(0%)", "0px 0%", "0% calc vertical"],
-    [ "calc(3px + 2% - 2%) 0", "calc(3px + 0%) 0px",
+    [ "calc(3px + 2% - 2%) 0", "calc(0% + 3px) 0px",
                       "computed 0% calc horizontal"],
-    [ "0 calc(3px + 2% - 2%)", "0px calc(3px + 0%)",
+    [ "0 calc(3px + 2% - 2%)", "0px calc(0% + 3px)",
                       "computed 0% calc vertical"],
     [ "calc(3px - 5px) calc(6px - 9px)",
       "calc(-2px) calc(-3px)", "negative pixel width" ],
     [ "", "auto auto", "initial value" ],
   ];
 
   var p = document.createElement("p");
   var cs = getComputedStyle(p, "");
@@ -202,20 +202,20 @@ var noframe_container = document.getElem
 
 (function test_bug_716628() {
   // Test that various gradient styles round-trip correctly
   var backgroundImages = [
     [ "radial-gradient(at 10% bottom, #ffffff, black)",
       "radial-gradient(at 10% 100%, rgb(255, 255, 255), rgb(0, 0, 0))",
       "radial gradient 1" ],
     [ "radial-gradient(#ffffff, black)",
-      "radial-gradient(rgb(255, 255, 255), rgb(0, 0, 0))",
+      "radial-gradient(at 50% 50%, rgb(255, 255, 255), rgb(0, 0, 0))",
       "radial gradient 2" ],
     [ "radial-gradient(farthest-corner, #ffffff, black)",
-      "radial-gradient(rgb(255, 255, 255), rgb(0, 0, 0))",
+      "radial-gradient(at 50% 50%, rgb(255, 255, 255), rgb(0, 0, 0))",
       "radial gradient 3" ],
     [ "linear-gradient(red, blue)",
       "linear-gradient(rgb(255, 0, 0), rgb(0, 0, 255))",
       "linear gradient 1" ],
     [ "linear-gradient(to bottom, red, blue)",
       "linear-gradient(rgb(255, 0, 0), rgb(0, 0, 255))",
       "linear gradient 2" ],
     [ "linear-gradient(to right, red, blue)",
@@ -624,17 +624,17 @@ var noframe_container = document.getElem
 
     // Linear gradient with default keyword (should be serialized without keyword):
     [ "-webkit-linear-gradient(top, red, blue)",
       "-webkit-linear-gradient(rgb(255, 0, 0), rgb(0, 0, 255))",
       "-webkit-linear-gradient with legacy default direction keyword" ],
 
     // Radial gradients (should be serialized using modern unprefixed style):
     [ "-webkit-radial-gradient(contain, red, blue)",
-      "radial-gradient(closest-side, rgb(255, 0, 0), rgb(0, 0, 255))",
+      "radial-gradient(closest-side at 50% 50%, rgb(255, 0, 0), rgb(0, 0, 255))",
       "-webkit-radial-gradient with legacy 'contain' keyword" ],
   ];
 
   var p = document.createElement("p");
   var cs = getComputedStyle(p, "");
   frame_container.appendChild(p);
 
   for (var i = 0; i < backgroundImages.length; ++i) {
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -2521,21 +2521,21 @@ function test_background_position_coord_
   is(cs.getPropertyValue(prop), "62.5%",
      "property " + prop + ": interpolation of edge keywords & percents");
   check_distance(prop, "center", "62.5%", endEdge);
 
   // Test interpolation between edge keyword *with an offset* and non-keyword
   // values.
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, `${endEdge} 20px`, "");
-  is(cs.getPropertyValue(prop), "calc(-20px + 100%)",
+  is(cs.getPropertyValue(prop), "calc(100% - 20px)",
      "property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "calc(40px + 20%)", "");
-  is(cs.getPropertyValue(prop), "calc(-5px + 80%)",
+  is(cs.getPropertyValue(prop), "calc(80% - 5px)",
      "property " + prop + ": interpolation of edge keywords w/ offsets & calc");
   check_distance(prop, `${endEdge} 20px`,
                        "calc(-5px + 80%)",
                        "calc(40px + 20%)");
 
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "10px, 50px, 30px", "");
   is(cs.getPropertyValue(prop), "10px, 50px, 30px",
@@ -2561,29 +2561,29 @@ function test_background_position_coord_
                        "50px, 70%, 30%, 25px");
 
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "20%, 8px", "");
   is(cs.getPropertyValue(prop), "20%, 8px",
      "property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "12px, 40%", "");
-  is(cs.getPropertyValue(prop), "calc(3px + 15%), calc(6px + 10%)",
+  is(cs.getPropertyValue(prop), "calc(15% + 3px), calc(10% + 6px)",
      "property " + prop + ": interpolation that computes to calc()");
   check_distance(prop, "20%, 8px",
                        "calc(3px + 15%), calc(6px + 10%)",
                        "12px, 40%");
 
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "calc(20% + 40px), 8px, calc(20px + 12%)", "");
-  is(cs.getPropertyValue(prop), "calc(40px + 20%), 8px, calc(20px + 12%)",
+  is(cs.getPropertyValue(prop), "calc(20% + 40px), 8px, calc(12% + 20px)",
      "property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "12px, calc(20%), calc(8px + 20%)", "");
-  is(cs.getPropertyValue(prop), "calc(33px + 15%), calc(6px + 5%), calc(17px + 14%)",
+  is(cs.getPropertyValue(prop), "calc(15% + 33px), calc(5% + 6px), calc(14% + 17px)",
      "property " + prop + ": interpolation that computes to calc()");
   check_distance(prop, "calc(20% + 40px), 8px, calc(20px + 12%)",
                        "calc(33px + 15%), calc(6px + 5%), calc(17px + 14%)",
                        "12px, calc(20%), calc(8px + 20%)");
 }
 
 /**
  * Common tests for 'background-position', 'background-size', and other
@@ -2629,54 +2629,58 @@ function test_background_position_size_c
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "50px 0", "");
   is(cs.getPropertyValue(prop), "20px 30px",
      "property " + prop + ": interpolation of lengths");
   if (doesPropHaveDistanceComputation) {
     check_distance(prop, "10px 40px", "20px 30px", "50px 0");
   }
 
+  const serializesCalcAccordingToTheSpec = prop == "mask-size" || prop == "background-size";
+
   // Test interpolation that computes to to calc() (transition from % to px)
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "20% 40%", "");
   is(cs.getPropertyValue(prop), "20% 40%",
      "property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "12px 20px", "");
-  is(cs.getPropertyValue(prop), "calc(3px + 15%) calc(5px + 30%)",
+  is(cs.getPropertyValue(prop),
+     serializesCalcAccordingToTheSpec ? "calc(15% + 3px) calc(30% + 5px)" : "calc(3px + 15%) calc(5px + 30%)",
      "property " + prop + ": interpolation that computes to calc()");
   if (doesPropHaveDistanceComputation) {
     check_distance(prop, "20% 40%",
                          "calc(3px + 15%) calc(5px + 30%)",
                          "12px 20px");
   }
 
   // Test interpolation that computes to to calc() (transition from px to %)
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "12px 20px", "");
   is(cs.getPropertyValue(prop), "12px 20px",
      "property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "20% 40%", "");
-  is(cs.getPropertyValue(prop), "calc(9px + 5%) calc(15px + 10%)",
+  is(cs.getPropertyValue(prop),
+     serializesCalcAccordingToTheSpec ? "calc(5% + 9px) calc(10% + 15px)" : "calc(9px + 5%) calc(15px + 10%)",
      "property " + prop + ": interpolation that computes to calc()");
   if (doesPropHaveDistanceComputation) {
     check_distance(prop, "12px 20px",
                          "calc(9px + 5%) calc(15px + 10%)",
                          "20% 40%");
   }
 
   // Test interpolation between calc() and non-calc()
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "calc(40px + 10%) 16px", "");
-  is(cs.getPropertyValue(prop), "calc(40px + 10%) 16px",
+  is(cs.getPropertyValue(prop), serializesCalcAccordingToTheSpec ? "calc(10% + 40px) 16px" : "calc(40px + 10%) 16px",
      "property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "30% calc(8px + 60%)", "");
-  is(cs.getPropertyValue(prop), "calc(30px + 15%) calc(14px + 15%)",
+  is(cs.getPropertyValue(prop), serializesCalcAccordingToTheSpec ? "calc(15% + 30px) calc(15% + 14px)" : "calc(30px + 15%) calc(14px + 15%)",
      "property " + prop + ": interpolation between calc() and non-calc()");
   if (doesPropHaveDistanceComputation) {
     check_distance(prop, "calc(40px + 10%) 16px",
                          "calc(30px + 15%) calc(14px + 15%)",
                          "30% calc(8px + 60%)");
   }
 
   // Test list values, if appropriate
@@ -2708,30 +2712,36 @@ function test_background_position_size_c
                            "50px 20%, 70% 50px, 30% 40%, 25px 50px");
     }
     div.style.setProperty("transition-property", "none", "");
     div.style.setProperty(prop, "20% 40%, 8px 12px", "");
     is(cs.getPropertyValue(prop), "20% 40%, 8px 12px",
        "property " + prop + ": computed value before transition");
     div.style.setProperty("transition-property", prop, "");
     div.style.setProperty(prop, "12px 20px, 40% 16%", "");
-    is(cs.getPropertyValue(prop), "calc(3px + 15%) calc(5px + 30%), calc(6px + 10%) calc(9px + 4%)",
+    is(cs.getPropertyValue(prop), serializesCalcAccordingToTheSpec
+        ? "calc(15% + 3px) calc(30% + 5px), calc(10% + 6px) calc(4% + 9px)"
+        : "calc(3px + 15%) calc(5px + 30%), calc(6px + 10%) calc(9px + 4%)",
        "property " + prop + ": interpolation that computes to calc()");
     if (doesPropHaveDistanceComputation) {
       check_distance(prop, "20% 40%, 8px 12px",
                            "calc(3px + 15%) calc(5px + 30%), calc(6px + 10%) calc(9px + 4%)",
                            "12px 20px, 40% 16%");
     }
     div.style.setProperty("transition-property", "none", "");
     div.style.setProperty(prop, "calc(20% + 40px) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)", "");
-    is(cs.getPropertyValue(prop), "calc(40px + 20%) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)",
+    is(cs.getPropertyValue(prop), serializesCalcAccordingToTheSpec
+        ? "calc(20% + 40px) calc(40% + 40px), 8px 12%, calc(12% + 20px) calc(8% + 24px)"
+        : "calc(40px + 20%) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)",
        "property " + prop + ": computed value before transition");
     div.style.setProperty("transition-property", prop, "");
     div.style.setProperty(prop, "12px 20%, calc(20%) calc(16px + 60%), calc(8px + 20%) calc(40px + 16%)", "");
-    is(cs.getPropertyValue(prop), "calc(33px + 15%) calc(30px + 35%), calc(6px + 5%) calc(4px + 24%), calc(17px + 14%) calc(28px + 10%)",
+    is(cs.getPropertyValue(prop), serializesCalcAccordingToTheSpec
+        ? "calc(15% + 33px) calc(35% + 30px), calc(5% + 6px) calc(24% + 4px), calc(14% + 17px) calc(10% + 28px)"
+        : "calc(33px + 15%) calc(30px + 35%), calc(6px + 5%) calc(4px + 24%), calc(17px + 14%) calc(28px + 10%)",
        "property " + prop + ": interpolation that computes to calc()");
     if (doesPropHaveDistanceComputation) {
       check_distance(prop, "calc(20% + 40px) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)",
                            "calc(33px + 15%) calc(30px + 35%), calc(6px + 5%) calc(4px + 24%), calc(17px + 14%) calc(28px + 10%)",
                            "12px 20%, calc(20%) calc(16px + 60%), calc(8px + 20%) calc(40px + 16%)");
     }
   }
 }
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -12,17 +12,17 @@ use app_units::Au;
 use gecko::values::GeckoStyleCoordConvertible;
 use gecko_bindings::bindings;
 use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
 use gecko_bindings::structs::{nsresult, SheetType, nsStyleImage};
 use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
 use std::f32::consts::PI;
 use stylesheets::{Origin, RulesMutateError};
 use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
-use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto};
+use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto, NonNegativeLengthOrPercentageOrAuto};
 use values::computed::{Percentage, TextAlign};
 use values::computed::image::LineDirection;
 use values::computed::url::ComputedImageUrl;
 use values::generics::box_::VerticalAlign;
 use values::generics::grid::{TrackListValue, TrackSize};
 use values::generics::image::{CompatMode, GradientItem, Image as GenericImage};
 use values::generics::rect::Rect;
 
@@ -101,16 +101,36 @@ impl From<nsStyleCoord_CalcValue> for Le
         match (other.mHasPercent, other.mLength) {
             (false, _) => LengthOrPercentageOrAuto::Length(Au(other.mLength).into()),
             (true, 0) => LengthOrPercentageOrAuto::Percentage(Percentage(other.mPercent)),
             _ => LengthOrPercentageOrAuto::Calc(other.into()),
         }
     }
 }
 
+// FIXME(emilio): A lot of these impl From should probably become explicit or
+// disappear as we move more stuff to cbindgen.
+impl From<nsStyleCoord_CalcValue> for NonNegativeLengthOrPercentageOrAuto {
+    fn from(other: nsStyleCoord_CalcValue) -> Self {
+        use values::generics::NonNegative;
+        use style_traits::values::specified::AllowedNumericType;
+        NonNegative(if other.mLength < 0 || other.mPercent < 0. {
+            LengthOrPercentageOrAuto::Calc(
+                CalcLengthOrPercentage::with_clamping_mode(
+                    Au(other.mLength).into(),
+                    if other.mHasPercent { Some(Percentage(other.mPercent)) } else { None },
+                    AllowedNumericType::NonNegative,
+                )
+            )
+        } else {
+            other.into()
+        })
+    }
+}
+
 impl From<Angle> for CoordDataValue {
     fn from(reference: Angle) -> Self {
         match reference {
             Angle::Deg(val) => CoordDataValue::Degree(val),
             Angle::Grad(val) => CoordDataValue::Grad(val),
             Angle::Rad(val) => CoordDataValue::Radian(val),
             Angle::Turn(val) => CoordDataValue::Turn(val),
         }
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -268,24 +268,28 @@ impl ToComputedValue for SpecifiedImageU
     }
 
     #[inline]
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         computed.0.clone()
     }
 }
 
-fn serialize_computed_url<W>(url_value_data: &URLValueData, dest: &mut CssWriter<W>) -> fmt::Result
+fn serialize_computed_url<W>(
+    url_value_data: &URLValueData,
+    dest: &mut CssWriter<W>,
+    get_url: unsafe extern "C" fn(*const URLValueData, *mut nsCString) -> (),
+) -> fmt::Result
 where
     W: Write,
 {
     dest.write_str("url(")?;
     unsafe {
         let mut string = nsCString::new();
-        bindings::Gecko_GetComputedURLSpec(url_value_data, &mut string);
+        get_url(url_value_data, &mut string);
         string.as_str_unchecked().to_css(dest)?;
     }
     dest.write_char(')')
 }
 
 /// The computed value of a CSS `url()`.
 ///
 /// The only difference between specified and computed URLs is the
@@ -293,17 +297,21 @@ where
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
 pub struct ComputedUrl(pub SpecifiedUrl);
 
 impl ToCss for ComputedUrl {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
-        serialize_computed_url(&self.0.url_value._base, dest)
+        serialize_computed_url(
+            &self.0.url_value._base,
+            dest,
+            bindings::Gecko_GetComputedURLSpec,
+        )
     }
 }
 
 impl ComputedUrl {
     /// Convert from RefPtr<URLValue> to ComputedUrl.
     pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
         let url = CssUrl::from_url_value_data(&url_value._base);
         ComputedUrl(SpecifiedUrl { url, url_value })
@@ -314,17 +322,21 @@ impl ComputedUrl {
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
 pub struct ComputedImageUrl(pub SpecifiedImageUrl);
 
 impl ToCss for ComputedImageUrl {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
-        serialize_computed_url(&self.0.image_value._base, dest)
+        serialize_computed_url(
+            &self.0.image_value._base,
+            dest,
+            bindings::Gecko_GetComputedImageURLSpec,
+        )
     }
 }
 
 impl ComputedImageUrl {
     /// Convert from nsStyleImageReques to ComputedImageUrl.
     pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
         let image_value = image_request.mImageValue.to_safe();
         let url = CssUrl::from_url_value_data(&image_value._base);
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -4000,26 +4000,25 @@ fn static_assert() {
             mWidthType: w_type as u8,
             mHeightType: h_type as u8,
         }
     </%self:simple_image_array_property>
 
     pub fn clone_${shorthand}_size(&self) -> longhands::${shorthand}_size::computed_value::T {
         use gecko_bindings::structs::nsStyleCoord_CalcValue as CalcValue;
         use gecko_bindings::structs::nsStyleImageLayers_Size_DimensionType as DimensionType;
-        use values::generics::NonNegative;
         use values::computed::NonNegativeLengthOrPercentageOrAuto;
         use values::generics::background::BackgroundSize;
 
         fn to_servo(value: CalcValue, ty: u8) -> NonNegativeLengthOrPercentageOrAuto {
             if ty == DimensionType::eAuto as u8 {
                 NonNegativeLengthOrPercentageOrAuto::auto()
             } else {
                 debug_assert_eq!(ty, DimensionType::eLengthPercentage as u8);
-                NonNegative(value.into())
+                value.into()
             }
         }
 
         longhands::${shorthand}_size::computed_value::List(
             self.gecko.${image_layers_field}.mLayers.iter().map(|ref layer| {
                 if DimensionType::eCover as u8 == layer.mSize.mWidthType {
                     debug_assert_eq!(layer.mSize.mHeightType, DimensionType::eCover as u8);
                     return BackgroundSize::Cover
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -197,31 +197,44 @@ impl From<LengthOrPercentageOrNone> for 
 
 impl ToCss for CalcLengthOrPercentage {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         use num_traits::Zero;
 
-        let (length, percentage) = match (self.length, self.percentage) {
-            (l, None) => return l.to_css(dest),
-            (l, Some(p)) if l.px() == 0. => return p.to_css(dest),
-            (l, Some(p)) => (l, p),
-        };
+        let length = self.unclamped_length();
+        match self.percentage {
+            Some(p) => {
+                if length.px() == 0. && self.clamping_mode.clamp(p.0) == p.0 {
+                    return p.to_css(dest);
+                }
+            }
+            None => {
+                if self.clamping_mode.clamp(length.px()) == length.px() {
+                    return length.to_css(dest);
+                }
+            }
+        }
 
         dest.write_str("calc(")?;
-        percentage.to_css(dest)?;
-
-        dest.write_str(if length.px() < Zero::zero() {
-            " - "
+        if let Some(percentage) = self.percentage {
+            percentage.to_css(dest)?;
+            if length.px() != 0. {
+                dest.write_str(if length.px() < Zero::zero() {
+                    " - "
+                } else {
+                    " + "
+                })?;
+                length.abs().to_css(dest)?;
+            }
         } else {
-            " + "
-        })?;
-        length.abs().to_css(dest)?;
+            length.to_css(dest)?;
+        }
 
         dest.write_str(")")
     }
 }
 
 impl specified::CalcLengthOrPercentage {
     /// Compute the value, zooming any absolute units by the zoom function.
     fn to_computed_value_with_zoom<F>(