Bug 1516454 - Use rust lengths for border corners. r=boris
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sun, 24 Feb 2019 10:47:53 -0800
changeset 461101 85f7a0580ce94b1d58e63485108fe7ebe54fea2b
parent 461100 1d2a9559d6d6c38d2ae46d8f618cba55237e48c4
child 461102 cb9df1982b8a0cd8f53adf46580e7a79af654af0
push id79014
push useremilio@crisal.io
push dateTue, 26 Feb 2019 14:02:03 +0000
treeherderautoland@85f7a0580ce9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersboris
bugs1516454, 15423
milestone67.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 1516454 - Use rust lengths for border corners. r=boris The test in https://github.com/web-platform-tests/wpt/pull/15423 hasn't been synced over yet, but it passes with this patch of course. Differential Revision: https://phabricator.services.mozilla.com/D20960
gfx/2d/Types.h
layout/base/ShapeUtils.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsStyleCoord.cpp
layout/style/nsStyleCoord.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
servo/components/style/cbindgen.toml
servo/components/style/gecko/conversions.rs
servo/components/style/gecko_bindings/sugar/ns_style_coord.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/values/computed/border.rs
servo/components/style/values/generics/border.rs
servo/components/style/values/generics/size.rs
servo/ports/geckolib/glue.rs
testing/web-platform/meta/css/css-shapes/shape-outside/values/shape-outside-inset-006.html.ini
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -513,17 +513,17 @@ constexpr int eCornerCount = 4;
 static inline Corner operator++(Corner& aCorner) {
   MOZ_ASSERT(aCorner >= eCornerTopLeft && aCorner <= eCornerBottomLeft,
              "Out of range corner!");
   aCorner = Corner(aCorner + 1);
   return aCorner;
 }
 
 // Indices into "half corner" arrays (nsStyleCorners e.g.)
-enum HalfCorner {
+enum HalfCorner : uint8_t {
   // This order is important!
   eCornerTopLeftX = 0,
   eCornerTopLeftY = 1,
   eCornerTopRightX = 2,
   eCornerTopRightY = 3,
   eCornerBottomRightX = 4,
   eCornerBottomRightY = 5,
   eCornerBottomLeftX = 6,
--- a/layout/base/ShapeUtils.cpp
+++ b/layout/base/ShapeUtils.cpp
@@ -135,17 +135,17 @@ nsSize ShapeUtils::ComputeEllipseRadii(c
   }
 
   return nsRect(x, y, width, height);
 }
 
 /* static */ bool ShapeUtils::ComputeInsetRadii(
     const StyleBasicShape& aBasicShape, const nsRect& aInsetRect,
     const nsRect& aRefBox, nscoord aRadii[8]) {
-  const nsStyleCorners& radius = aBasicShape.GetRadius();
+  const auto& radius = aBasicShape.GetRadius();
   return nsIFrame::ComputeBorderRadii(radius, aInsetRect.Size(), aRefBox.Size(),
                                       Sides(), aRadii);
 }
 
 /* static */ nsTArray<nsPoint> ShapeUtils::ComputePolygonVertices(
     const StyleBasicShape& aBasicShape, const nsRect& aRefBox) {
   MOZ_ASSERT(aBasicShape.GetShapeType() == StyleBasicShapeType::Polygon,
              "The basic shape must be polygon()!");
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6857,30 +6857,25 @@ static ImgDrawResult DrawImageInternal(
 
   if (aOrientation == StyleImageOrientation::FromImage) {
     img = ImageOps::Orient(img, img->GetOrientation());
   }
 
   return img.forget();
 }
 
-static bool NonZeroStyleCoord(const nsStyleCoord& aCoord) {
-  if (aCoord.IsCoordPercentCalcUnit()) {
-    // Since negative results are clamped to 0, check > 0.
-    return aCoord.ComputeCoordPercentCalc(nscoord_MAX) > 0 ||
-           aCoord.ComputeCoordPercentCalc(0) > 0;
-  }
-
-  return true;
+static bool NonZeroCorner(const LengthPercentage& aLength) {
+  // Since negative results are clamped to 0, check > 0.
+  return aLength.Resolve(nscoord_MAX) > 0 || aLength.Resolve(0) > 0;
 }
 
 /* static */ bool nsLayoutUtils::HasNonZeroCorner(
-    const nsStyleCorners& aCorners) {
+    const BorderRadius& aCorners) {
   NS_FOR_CSS_HALF_CORNERS(corner) {
-    if (NonZeroStyleCoord(aCorners.Get(corner))) return true;
+    if (NonZeroCorner(aCorners.Get(corner))) return true;
   }
   return false;
 }
 
 // aCorner is a "full corner" value, i.e. eCornerTopLeft etc.
 static bool IsCornerAdjacentToSide(uint8_t aCorner, Side aSide) {
   static_assert((int)eSideTop == eCornerTopLeft, "Check for Full Corner");
   static_assert((int)eSideRight == eCornerTopRight, "Check for Full Corner");
@@ -6895,17 +6890,17 @@ static bool IsCornerAdjacentToSide(uint8
                 "Check for Full Corner");
   static_assert((int)eSideLeft == ((eCornerTopLeft - 1) & 3),
                 "Check for Full Corner");
 
   return aSide == aCorner || aSide == ((aCorner - 1) & 3);
 }
 
 /* static */ bool nsLayoutUtils::HasNonZeroCornerOnSide(
-    const nsStyleCorners& aCorners, Side aSide) {
+    const BorderRadius& aCorners, Side aSide) {
   static_assert(eCornerTopLeftX / 2 == eCornerTopLeft,
                 "Check for Non Zero on side");
   static_assert(eCornerTopLeftY / 2 == eCornerTopLeft,
                 "Check for Non Zero on side");
   static_assert(eCornerTopRightX / 2 == eCornerTopRight,
                 "Check for Non Zero on side");
   static_assert(eCornerTopRightY / 2 == eCornerTopRight,
                 "Check for Non Zero on side");
@@ -6916,17 +6911,17 @@ static bool IsCornerAdjacentToSide(uint8
   static_assert(eCornerBottomLeftX / 2 == eCornerBottomLeft,
                 "Check for Non Zero on side");
   static_assert(eCornerBottomLeftY / 2 == eCornerBottomLeft,
                 "Check for Non Zero on side");
 
   NS_FOR_CSS_HALF_CORNERS(corner) {
     // corner is a "half corner" value, so dividing by two gives us a
     // "full corner" value.
-    if (NonZeroStyleCoord(aCorners.Get(corner)) &&
+    if (NonZeroCorner(aCorners.Get(corner)) &&
         IsCornerAdjacentToSide(corner / 2, aSide))
       return true;
   }
   return false;
 }
 
 /* static */ nsTransparencyMode nsLayoutUtils::GetFrameTransparency(
     nsIFrame* aBackgroundFrame, nsIFrame* aCSSRootFrame) {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -50,17 +50,16 @@ class nsDisplayItem;
 class nsFontMetrics;
 class nsFontFaceList;
 class nsIImageLoadingContent;
 class nsBlockFrame;
 class nsContainerFrame;
 class nsView;
 class nsIFrame;
 class nsStyleCoord;
-class nsStyleCorners;
 class nsPIDOMWindowOuter;
 class imgIRequest;
 struct nsStyleFont;
 struct nsOverflowAreas;
 
 namespace mozilla {
 class ComputedStyle;
 enum class PseudoStyleType : uint8_t;
@@ -1932,32 +1931,32 @@ class nsLayoutUtils {
    * @param aOrientation The desired orientation.
    */
   static already_AddRefed<imgIContainer> OrientImage(
       imgIContainer* aContainer,
       const mozilla::StyleImageOrientation& aOrientation);
 
   /**
    * Determine if any corner radius is of nonzero size
-   *   @param aCorners the |nsStyleCorners| object to check
+   *   @param aCorners the |BorderRadius| object to check
    *   @return true unless all the coordinates are 0%, 0 or null.
    *
    * A corner radius with one dimension zero and one nonzero is
    * treated as a nonzero-radius corner, even though it will end up
    * being rendered like a zero-radius corner.  This is because such
    * corners are not expected to appear outside of test cases, and it's
    * simpler to implement the test this way.
    */
-  static bool HasNonZeroCorner(const nsStyleCorners& aCorners);
+  static bool HasNonZeroCorner(const mozilla::BorderRadius& aCorners);
 
   /**
    * Determine if there is any corner radius on corners adjacent to the
    * given side.
    */
-  static bool HasNonZeroCornerOnSide(const nsStyleCorners& aCorners,
+  static bool HasNonZeroCornerOnSide(const mozilla::BorderRadius& aCorners,
                                      mozilla::Side aSide);
 
   /**
    * Determine if a widget is likely to require transparency or translucency.
    *   @param aBackgroundFrame The frame that the background is set on. For
    *                           <window>s, this will be the canvas frame.
    *   @param aCSSRootFrame    The frame that holds CSS properties affecting
    *                           the widget's transparency. For menupopups,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1639,35 +1639,25 @@ nsRect nsIFrame::GetContentRectRelativeT
   r.Deflate(bp);
   return r;
 }
 
 nsRect nsIFrame::GetContentRect() const {
   return GetContentRectRelativeToSelf() + GetPosition();
 }
 
-bool nsIFrame::ComputeBorderRadii(const nsStyleCorners& aBorderRadius,
+bool nsIFrame::ComputeBorderRadii(const BorderRadius& aBorderRadius,
                                   const nsSize& aFrameSize,
                                   const nsSize& aBorderArea, Sides aSkipSides,
                                   nscoord aRadii[8]) {
   // Percentages are relative to whichever side they're on.
   NS_FOR_CSS_HALF_CORNERS(i) {
-    const nsStyleCoord c = aBorderRadius.Get(i);
+    const LengthPercentage& c = aBorderRadius.Get(i);
     nscoord axis = HalfCornerIsX(i) ? aFrameSize.width : aFrameSize.height;
-
-    if (c.IsCoordPercentCalcUnit()) {
-      aRadii[i] = c.ComputeCoordPercentCalc(axis);
-      if (aRadii[i] < 0) {
-        // clamp calc()
-        aRadii[i] = 0;
-      }
-    } else {
-      MOZ_ASSERT_UNREACHABLE("ComputeBorderRadii: bad unit");
-      aRadii[i] = 0;
-    }
+    aRadii[i] = std::max(0, c.Resolve(axis));
   }
 
   if (aSkipSides.Top()) {
     aRadii[eCornerTopLeftX] = 0;
     aRadii[eCornerTopLeftY] = 0;
     aRadii[eCornerTopRightX] = 0;
     aRadii[eCornerTopRightY] = 0;
   }
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1358,17 +1358,17 @@ class nsIFrame : public nsQueryFrame {
    * aBorderArea is used for the adjustment of radii that might be too
    * large.
    * FIXME: In the long run, we can probably get away with only one of
    * these, especially if we change the way we handle outline-radius (by
    * removing it and inflating the border radius)
    *
    * Return whether any radii are nonzero.
    */
-  static bool ComputeBorderRadii(const nsStyleCorners& aBorderRadius,
+  static bool ComputeBorderRadii(const mozilla::BorderRadius&,
                                  const nsSize& aFrameSize,
                                  const nsSize& aBorderArea, Sides aSkipSides,
                                  nscoord aRadii[8]);
 
   /*
    * Given a set of border radii for one box (e.g., border box), convert
    * it to the equivalent set of radii for another box (e.g., in to
    * padding box, out to outline box) by reducing radii or increasing
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -508,16 +508,18 @@ RawServoDeclarationBlockStrong Servo_Par
     RawGeckoURLExtraData* data, mozilla::ParsingMode parsing_mode,
     nsCompatibility quirks_mode, mozilla::css::Loader* loader);
 
 bool Servo_ParseEasing(const nsAString* easing, RawGeckoURLExtraData* data,
                        nsTimingFunctionBorrowedMut output);
 
 void Servo_SerializeEasing(nsTimingFunctionBorrowed easing, nsAString* output);
 
+void Servo_SerializeBorderRadius(const mozilla::StyleBorderRadius*, nsAString*);
+
 void Servo_GetComputedKeyframeValues(
     RawGeckoKeyframeListBorrowed keyframes, RawGeckoElementBorrowed element,
     ComputedStyleBorrowed style, RawServoStyleSetBorrowed set,
     RawGeckoComputedKeyframeValuesListBorrowedMut result);
 
 RawServoAnimationValueStrong Servo_ComputedValues_ExtractAnimationValue(
     ComputedStyleBorrowed computed_values, nsCSSPropertyID property);
 
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -439,16 +439,17 @@ cbindgen-types = [
     { gecko = "StyleNonNegativeLength", servo = "values::computed::NonNegativeLength" },
     { gecko = "StyleNonNegativeNumber", servo = "values::computed::NonNegativeNumber" },
     { gecko = "StylePercentage", servo = "values::computed::Percentage" },
     { gecko = "StylePerspective", servo = "values::computed::Perspective" },
     { gecko = "StyleGenericPerspective", servo = "values::generics::box_::Perspective" },
     { gecko = "StyleZIndex", servo = "values::computed::ZIndex" },
     { gecko = "StyleGenericZIndex", servo = "values::generics::position::ZIndex" },
     { gecko = "StyleTransformOrigin", servo = "values::computed::TransformOrigin" },
+    { gecko = "StyleGenericBorderRadius", servo = "values::generics::border::BorderRadius" },
 ]
 
 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>" },
@@ -497,16 +498,17 @@ structs-types = [
     "mozilla::CORSMode",
     "mozilla::FontStretch",
     "mozilla::FontSlantStyle",
     "mozilla::FontWeight",
     "mozilla::MallocSizeOf",
     "mozilla::OriginFlags",
     "mozilla::StyleMotion",
     "mozilla::UniquePtr",
+    "mozilla::StyleBorderRadius",
     "mozilla::StyleDisplayMode",
     "mozilla::StylePrefersColorScheme",
     "mozilla::StyleIntersectionObserverRootMargin",
     "mozilla::StyleComputedFontStretchRange",
     "mozilla::StyleComputedFontStyleDescriptor",
     "mozilla::StyleComputedFontWeightRange",
     "mozilla::StyleFontDisplay",
     "mozilla::StyleUnicodeRange",
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1897,34 +1897,34 @@ already_AddRefed<CSSValue> nsComputedDOM
   return GetEllipseRadii(StyleOutline()->mOutlineRadius, eCornerTopLeft);
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetOutlineRadiusTopRight() {
   return GetEllipseRadii(StyleOutline()->mOutlineRadius, eCornerTopRight);
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetEllipseRadii(
-    const nsStyleCorners& aRadius, Corner aFullCorner) {
-  nsStyleCoord radiusX = aRadius.Get(FullToHalfCorner(aFullCorner, false));
-  nsStyleCoord radiusY = aRadius.Get(FullToHalfCorner(aFullCorner, true));
+    const BorderRadius& aRadius, Corner aFullCorner) {
+  const auto& radiusX = aRadius.Get(FullToHalfCorner(aFullCorner, false));
+  const auto& radiusY = aRadius.Get(FullToHalfCorner(aFullCorner, true));
 
   // for compatibility, return a single value if X and Y are equal
   if (radiusX == radiusY) {
     RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-    SetValueToCoord(val, radiusX, true);
+    SetValueToLengthPercentage(val, radiusX, true);
     return val.forget();
   }
 
   RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
 
   RefPtr<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
   RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
 
-  SetValueToCoord(valX, radiusX, true);
-  SetValueToCoord(valY, radiusY, true);
+  SetValueToLengthPercentage(valX, radiusX, true);
+  SetValueToLengthPercentage(valY, radiusY, true);
 
   valueList->AppendCSSValue(valX.forget());
   valueList->AppendCSSValue(valY.forget());
 
   return valueList.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetCSSShadowArray(
@@ -3268,32 +3268,19 @@ void nsComputedDOMStyle::BoxValuesToStri
       if (value2 != value4) {
         aString.Append(' ');
         aString.Append(value4);
       }
     }
   }
 }
 
-void nsComputedDOMStyle::BasicShapeRadiiToString(
-    nsAString& aCssText, const nsStyleCorners& aCorners) {
-  nsTArray<nsStyleCoord> horizontal, vertical;
-  nsAutoString horizontalString, verticalString;
-  NS_FOR_CSS_FULL_CORNERS(corner) {
-    horizontal.AppendElement(aCorners.Get(FullToHalfCorner(corner, false)));
-    vertical.AppendElement(aCorners.Get(FullToHalfCorner(corner, true)));
-  }
-  BoxValuesToString(horizontalString, horizontal, true);
-  BoxValuesToString(verticalString, vertical, true);
-  aCssText.Append(horizontalString);
-  if (horizontalString == verticalString) {
-    return;
-  }
-  aCssText.AppendLiteral(" / ");
-  aCssText.Append(verticalString);
+void nsComputedDOMStyle::BasicShapeRadiiToString(nsAString& aCssText,
+                                                 const BorderRadius& aCorners) {
+  Servo_SerializeBorderRadius(&aCorners, &aCssText);
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::CreatePrimitiveValueForBasicShape(
     const UniquePtr<StyleBasicShape>& aStyleBasicShape) {
   MOZ_ASSERT(aStyleBasicShape, "Expect a valid basic shape pointer!");
 
   StyleBasicShapeType type = aStyleBasicShape->GetShapeType();
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -36,17 +36,16 @@ struct ComputedGridTrackInfo;
 struct ComputedStyleMap;
 struct nsCSSKTableEntry;
 class nsIFrame;
 class nsIPresShell;
 class nsDOMCSSValueList;
 struct nsMargin;
 class nsROCSSPrimitiveValue;
 class nsStyleCoord;
-class nsStyleCorners;
 struct nsStyleFilter;
 class nsStyleGradient;
 struct nsStyleImage;
 class nsStyleSides;
 
 class nsComputedDOMStyle final : public nsDOMCSSDeclaration,
                                  public nsStubMutationObserver {
  private:
@@ -165,21 +164,20 @@ class nsComputedDOMStyle final : public 
 #undef STYLE_STRUCT
 
   /**
    * A method to get a percentage base for a percentage value.  Returns true
    * if a percentage base value was determined, false otherwise.
    */
   typedef bool (nsComputedDOMStyle::*PercentageBaseGetter)(nscoord&);
 
-  already_AddRefed<CSSValue> GetEllipseRadii(const nsStyleCorners& aRadius,
+  already_AddRefed<CSSValue> GetEllipseRadii(const mozilla::BorderRadius&,
                                              mozilla::Corner aFullCorner);
 
-  already_AddRefed<CSSValue> GetOffsetWidthFor(mozilla::Side aSide);
-
+  already_AddRefed<CSSValue> GetOffsetWidthFor(mozilla::Side);
   already_AddRefed<CSSValue> GetAbsoluteOffset(mozilla::Side);
   nscoord GetUsedAbsoluteOffset(mozilla::Side);
   already_AddRefed<CSSValue> GetNonStaticPositionOffset(
       mozilla::Side aSide, bool aResolveAuto, PercentageBaseGetter aWidthGetter,
       PercentageBaseGetter aHeightGetter);
 
   already_AddRefed<CSSValue> GetStaticOffset(mozilla::Side aSide);
 
@@ -291,16 +289,17 @@ class nsComputedDOMStyle final : public 
   already_AddRefed<CSSValue> DoGetBorderBottomWidth();
   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> DoGetBorderImageWidth();
 
   /* Box Shadow */
   already_AddRefed<CSSValue> DoGetBoxShadow();
 
   /* Margin Properties */
   already_AddRefed<CSSValue> DoGetMarginTopWidth();
@@ -310,16 +309,17 @@ class nsComputedDOMStyle final : public 
 
   /* Outline Properties */
   already_AddRefed<CSSValue> DoGetOutlineWidth();
   already_AddRefed<CSSValue> DoGetOutlineRadiusBottomLeft();
   already_AddRefed<CSSValue> DoGetOutlineRadiusBottomRight();
   already_AddRefed<CSSValue> DoGetOutlineRadiusTopLeft();
   already_AddRefed<CSSValue> DoGetOutlineRadiusTopRight();
 
+
   /* Text Properties */
   already_AddRefed<CSSValue> DoGetInitialLetter();
   already_AddRefed<CSSValue> DoGetLineHeight();
   already_AddRefed<CSSValue> DoGetTextDecoration();
   already_AddRefed<CSSValue> DoGetTextDecorationColor();
   already_AddRefed<CSSValue> DoGetTextDecorationLine();
   already_AddRefed<CSSValue> DoGetTextDecorationStyle();
   already_AddRefed<CSSValue> DoGetTextEmphasisPosition();
@@ -491,17 +491,17 @@ class nsComputedDOMStyle final : public 
 
   // Helper function for computing basic shape styles.
   already_AddRefed<CSSValue> CreatePrimitiveValueForBasicShape(
       const mozilla::UniquePtr<mozilla::StyleBasicShape>& aStyleBasicShape);
   void BoxValuesToString(nsAString& aString,
                          const nsTArray<nsStyleCoord>& aBoxValues,
                          bool aClampNegativeCalc);
   void BasicShapeRadiiToString(nsAString& aCssText,
-                               const nsStyleCorners& aCorners);
+                               const mozilla::BorderRadius&);
 
   // Find out if we can safely skip flushing for aDocument (i.e. pending
   // restyles does not affect mContent).
   bool NeedsToFlush(Document*) const;
 
   static ComputedStyleMap* GetComputedStyleMap();
 
   // We don't really have a good immutable representation of "presentation".
--- a/layout/style/nsStyleCoord.cpp
+++ b/layout/style/nsStyleCoord.cpp
@@ -198,52 +198,16 @@ bool nsStyleSides::operator==(const nsSt
   }
   return true;
 }
 
 void nsStyleSides::Reset() {
   NS_FOR_CSS_SIDES(i) { nsStyleCoord::Reset(mUnits[i], mValues[i]); }
 }
 
-nsStyleCorners::nsStyleCorners() {
-  NS_FOR_CSS_HALF_CORNERS(i) { mUnits[i] = eStyleUnit_Null; }
-  mozilla::PodArrayZero(mValues);
-}
-
-nsStyleCorners::nsStyleCorners(const nsStyleCorners& aOther) {
-  NS_FOR_CSS_HALF_CORNERS(i) { mUnits[i] = eStyleUnit_Null; }
-  *this = aOther;
-}
-
-nsStyleCorners::~nsStyleCorners() { Reset(); }
-
-nsStyleCorners& nsStyleCorners::operator=(const nsStyleCorners& aCopy) {
-  if (this != &aCopy) {
-    NS_FOR_CSS_HALF_CORNERS(i) {
-      nsStyleCoord::SetValue(mUnits[i], mValues[i], aCopy.mUnits[i],
-                             aCopy.mValues[i]);
-    }
-  }
-  return *this;
-}
-
-bool nsStyleCorners::operator==(const nsStyleCorners& aOther) const {
-  NS_FOR_CSS_HALF_CORNERS(i) {
-    if (nsStyleCoord(mValues[i], (nsStyleUnit)mUnits[i]) !=
-        nsStyleCoord(aOther.mValues[i], (nsStyleUnit)aOther.mUnits[i])) {
-      return false;
-    }
-  }
-  return true;
-}
-
-void nsStyleCorners::Reset() {
-  NS_FOR_CSS_HALF_CORNERS(i) { nsStyleCoord::Reset(mUnits[i], mValues[i]); }
-}
-
 // Validation of SideIsVertical.
 #define CASE(side, result) \
   static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
 CASE(eSideTop, false);
 CASE(eSideRight, true);
 CASE(eSideBottom, false);
 CASE(eSideLeft, true);
 #undef CASE
--- a/layout/style/nsStyleCoord.h
+++ b/layout/style/nsStyleCoord.h
@@ -39,16 +39,17 @@ enum LogicalCorner {
   eLogicalCornerBEndIStart = 3
 };
 
 using LengthPercentage = StyleLengthPercentage;
 using LengthPercentageOrAuto = StyleLengthPercentageOrAuto;
 using NonNegativeLengthPercentage = StyleNonNegativeLengthPercentage;
 using NonNegativeLengthPercentageOrAuto =
     StyleNonNegativeLengthPercentageOrAuto;
+using BorderRadius = StyleBorderRadius;
 
 nscoord StyleCSSPixelLength::ToAppUnits() const {
   // We want to resolve the length part of the calc() expression rounding 0.5
   // away from zero, instead of the default behavior of NSToCoordRoundWithClamp
   // which is floor(x + 0.5).
   //
   // This is what the rust code in the app_units crate does, and not doing this
   // would regress bug 1323735, for example.
@@ -246,16 +247,24 @@ bool StyleRect<T>::All(Predicate aPredic
 }
 
 template <typename T>
 template <typename Predicate>
 bool StyleRect<T>::Any(Predicate aPredicate) const {
   return aPredicate(_0) || aPredicate(_1) || aPredicate(_2) || aPredicate(_3);
 }
 
+template <>
+inline const LengthPercentage& BorderRadius::Get(HalfCorner aCorner) const {
+  static_assert(sizeof(BorderRadius) == sizeof(LengthPercentage) * 8, "");
+  static_assert(alignof(BorderRadius) == alignof(LengthPercentage), "");
+  auto* self = reinterpret_cast<const LengthPercentage*>(this);
+  return self[aCorner];
+}
+
 }  // namespace mozilla
 
 enum nsStyleUnit : uint8_t {
   eStyleUnit_Null = 0,           // (no value) value is not specified
   eStyleUnit_Normal = 1,         // (no value)
   eStyleUnit_Auto = 2,           // (no value)
   eStyleUnit_None = 3,           // (no value)
   eStyleUnit_Percent = 10,       // (float) 1.0 == 100%
@@ -584,55 +593,16 @@ class nsStyleSides {
     return true;
   }
 
  protected:
   nsStyleUnit mUnits[4];
   nsStyleUnion mValues[4];
 };
 
-/**
- * Class that represents a set of top-left/top-right/bottom-right/bottom-left
- * nsStyleCoord pairs.  This is used to hold the dimensions of the
- * corners of a box (for, e.g., border-radius and outline-radius).
- */
-/** <div rustbindgen private accessor="unsafe"></div> */
-class nsStyleCorners {
- public:
-  nsStyleCorners();
-  nsStyleCorners(const nsStyleCorners&);
-  ~nsStyleCorners();
-
-  // use compiler's version
-  nsStyleCorners& operator=(const nsStyleCorners& aCopy);
-  bool operator==(const nsStyleCorners& aOther) const;
-  bool operator!=(const nsStyleCorners& aOther) const;
-
-  // aHalfCorner is always one of enum HalfCorner in gfx/2d/Types.h.
-  inline nsStyleUnit GetUnit(uint8_t aHalfCorner) const;
-
-  inline nsStyleCoord Get(uint8_t aHalfCorner) const;
-
-  // Sets each corner to null and releases any refcounted objects.  Only use
-  // this if the object is initialized (i.e. don't use it in nsStyleCorners
-  // constructors).
-  void Reset();
-
-  inline void Set(uint8_t aHalfCorner, const nsStyleCoord& aCoord);
-
- protected:
-  // Stored as:
-  // top-left.x, top-left.y,
-  // top-right.x, top-right.y,
-  // bottom-right.x, bottom-right.y,
-  // bottom-left.x, bottom-left.y
-  nsStyleUnit mUnits[8];
-  nsStyleUnion mValues[8];
-};
-
 // -------------------------
 // nsStyleCoord inlines
 //
 inline nsStyleCoord::nsStyleCoord(nscoord aValue, CoordConstructorType)
     : mUnit(eStyleUnit_Coord) {
   mValue.mInt = aValue;
 }
 
@@ -825,29 +795,9 @@ inline void nsStyleSides::SetTop(const n
 
 inline void nsStyleSides::SetRight(const nsStyleCoord& aCoord) {
   Set(mozilla::eSideRight, aCoord);
 }
 
 inline void nsStyleSides::SetBottom(const nsStyleCoord& aCoord) {
   Set(mozilla::eSideBottom, aCoord);
 }
-
-// -------------------------
-// nsStyleCorners inlines
-//
-inline bool nsStyleCorners::operator!=(const nsStyleCorners& aOther) const {
-  return !((*this) == aOther);
-}
-
-inline nsStyleUnit nsStyleCorners::GetUnit(uint8_t aCorner) const {
-  return (nsStyleUnit)mUnits[aCorner];
-}
-
-inline nsStyleCoord nsStyleCorners::Get(uint8_t aCorner) const {
-  return nsStyleCoord(mValues[aCorner], nsStyleUnit(mUnits[aCorner]));
-}
-
-inline void nsStyleCorners::Set(uint8_t aCorner, const nsStyleCoord& aCoord) {
-  nsStyleCoord::SetValue(mUnits[aCorner], mValues[aCorner], aCoord);
-}
-
 #endif /* nsStyleCoord_h___ */
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -237,38 +237,40 @@ nsChangeHint nsStylePadding::CalcDiffere
   return NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics;
 }
 
 static nscoord TwipsPerPixel(const Document& aDocument) {
   auto* pc = aDocument.GetPresContext();
   return pc ? pc->AppUnitsPerDevPixel() : mozilla::AppUnitsPerCSSPixel();
 }
 
+static inline BorderRadius ZeroBorderRadius() {
+  auto zero = LengthPercentage::Zero();
+  return {{{zero, zero}}, {{zero, zero}}, {{zero, zero}}, {{zero, zero}}};
+}
+
 nsStyleBorder::nsStyleBorder(const Document& aDocument)
-    : mBorderImageOutset(
+    : mBorderRadius(ZeroBorderRadius()),
+      mBorderImageOutset(
           StyleRectWithAllSides(StyleNonNegativeLengthOrNumber::Number(0.))),
       mBorderImageSlice(
           {StyleRectWithAllSides(StyleNumberOrPercentage::Percentage({1.})),
            false}),
       mBorderImageRepeatH(StyleBorderImageRepeat::Stretch),
       mBorderImageRepeatV(StyleBorderImageRepeat::Stretch),
       mFloatEdge(StyleFloatEdge::ContentBox),
       mBoxDecorationBreak(StyleBoxDecorationBreak::Slice),
       mBorderTopColor(StyleComplexColor::CurrentColor()),
       mBorderRightColor(StyleComplexColor::CurrentColor()),
       mBorderBottomColor(StyleComplexColor::CurrentColor()),
       mBorderLeftColor(StyleComplexColor::CurrentColor()),
       mComputedBorder(0, 0, 0, 0),
       mTwipsPerPixel(TwipsPerPixel(aDocument)) {
   MOZ_COUNT_CTOR(nsStyleBorder);
 
-  NS_FOR_CSS_HALF_CORNERS(corner) {
-    mBorderRadius.Set(corner, nsStyleCoord(0, nsStyleCoord::CoordConstructor));
-  }
-
   nscoord medium = kMediumBorderWidth;
   NS_FOR_CSS_SIDES(side) {
     mBorderImageWidth.Set(side, nsStyleCoord(1.0f, eStyleUnit_Factor));
     mBorder.Side(side) = medium;
     mBorderStyle[side] = StyleBorderStyle::None;
   }
 }
 
@@ -395,26 +397,24 @@ nsChangeHint nsStyleBorder::CalcDifferen
       mBorderImageWidth != aNewData.mBorderImageWidth) {
     return nsChangeHint_NeutralChange;
   }
 
   return nsChangeHint(0);
 }
 
 nsStyleOutline::nsStyleOutline(const Document& aDocument)
-    : mOutlineWidth(kMediumBorderWidth),
+    : mOutlineRadius(ZeroBorderRadius()),
+      mOutlineWidth(kMediumBorderWidth),
       mOutlineOffset(0),
       mOutlineColor(StyleComplexColor::CurrentColor()),
       mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)),
       mActualOutlineWidth(0),
       mTwipsPerPixel(TwipsPerPixel(aDocument)) {
   MOZ_COUNT_CTOR(nsStyleOutline);
-  // spacing values not inherited
-  nsStyleCoord zero(0, nsStyleCoord::CoordConstructor);
-  NS_FOR_CSS_HALF_CORNERS(corner) { mOutlineRadius.Set(corner, zero); }
 }
 
 nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc)
     : mOutlineRadius(aSrc.mOutlineRadius),
       mOutlineWidth(aSrc.mOutlineWidth),
       mOutlineOffset(aSrc.mOutlineOffset),
       mOutlineColor(aSrc.mOutlineColor),
       mOutlineStyle(aSrc.mOutlineStyle),
@@ -777,16 +777,22 @@ nsChangeHint nsStyleSVG::CalcDifference(
   }
 
   return hint;
 }
 
 // --------------------
 // StyleBasicShape
 
+StyleBasicShape::StyleBasicShape(StyleBasicShapeType aType)
+    : mType(aType),
+      mFillRule(StyleFillRule::Nonzero),
+      mPosition(Position::FromPercentage(0.5f)),
+      mRadius(ZeroBorderRadius()) {}
+
 nsCSSKeyword StyleBasicShape::GetShapeTypeName() const {
   switch (mType) {
     case StyleBasicShapeType::Polygon:
       return eCSSKeyword_polygon;
     case StyleBasicShapeType::Circle:
       return eCSSKeyword_circle;
     case StyleBasicShapeType::Ellipse:
       return eCSSKeyword_ellipse;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -938,17 +938,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   imgIRequest* GetBorderImageRequest() const {
     if (mBorderImageSource.GetType() == eStyleImageType_Image) {
       return mBorderImageSource.GetImageData();
     }
     return nullptr;
   }
 
  public:
-  nsStyleCorners mBorderRadius;  // coord, percent
+  mozilla::StyleBorderRadius mBorderRadius;  // coord, percent
   nsStyleImage mBorderImageSource;
   nsStyleSides mBorderImageWidth;  // length, factor, percent, auto
   mozilla::StyleNonNegativeLengthOrNumberRect mBorderImageOutset;
   mozilla::StyleBorderImageSlice mBorderImageSlice;  // factor, percent
   mozilla::StyleBorderImageRepeat mBorderImageRepeatH;
   mozilla::StyleBorderImageRepeat mBorderImageRepeatV;
   mozilla::StyleFloatEdge mFloatEdge;
   mozilla::StyleBoxDecorationBreak mBoxDecorationBreak;
@@ -1042,17 +1042,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   explicit nsStyleOutline(const mozilla::dom::Document&);
   nsStyleOutline(const nsStyleOutline& aOutline);
   ~nsStyleOutline() { MOZ_COUNT_DTOR(nsStyleOutline); }
   void TriggerImageLoads(mozilla::dom::Document&, const nsStyleOutline*) {}
   const static bool kHasTriggerImageLoads = false;
 
   nsChangeHint CalcDifference(const nsStyleOutline& aNewData) const;
 
-  nsStyleCorners mOutlineRadius;  // coord, percent, calc
+  mozilla::StyleBorderRadius mOutlineRadius;
 
   // This is the specified value of outline-width, but with length values
   // computed to absolute.  mActualOutlineWidth stores the outline-width
   // value used by layout.  (We must store mOutlineWidth for the same
   // style struct resolution reasons that we do nsStyleBorder::mBorder;
   // see that field's comment.)
   nscoord mOutlineWidth;
   nscoord mOutlineOffset;
@@ -1286,19 +1286,19 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   // mSpecifiedJustifyItems, except when the latter is AUTO -- in that case,
   // mJustifyItems is set to NORMAL, or to the parent ComputedStyle's
   // mJustifyItems if it has the legacy flag.
   //
   // This last part happens in ComputedStyle::ApplyStyleFixups.
   uint8_t mSpecifiedJustifyItems;
   uint8_t mJustifyItems;
   uint8_t mJustifySelf;
-  mozilla::StyleFlexDirection mFlexDirection;  
-  uint8_t mFlexWrap;       // NS_STYLE_FLEX_WRAP_*
-  uint8_t mObjectFit;      // NS_STYLE_OBJECT_FIT_*
+  mozilla::StyleFlexDirection mFlexDirection;
+  uint8_t mFlexWrap;   // NS_STYLE_FLEX_WRAP_*
+  uint8_t mObjectFit;  // NS_STYLE_OBJECT_FIT_*
   int32_t mOrder;
   float mFlexGrow;
   float mFlexShrink;
   mozilla::StyleZIndex mZIndex;
   mozilla::UniquePtr<nsStyleGridTemplate> mGridTemplateColumns;
   mozilla::UniquePtr<nsStyleGridTemplate> mGridTemplateRows;
 
   // nullptr for 'none'
@@ -1630,46 +1630,42 @@ struct StyleAnimation {
   dom::PlaybackDirection mDirection;
   dom::FillMode mFillMode;
   StyleAnimationPlayState mPlayState;
   float mIterationCount;  // mozilla::PositiveInfinity<float>() means infinite
 };
 
 class StyleBasicShape final {
  public:
-  explicit StyleBasicShape(StyleBasicShapeType type)
-      : mType(type),
-        mFillRule(StyleFillRule::Nonzero),
-        mPosition(Position::FromPercentage(0.5f)) {}
+  explicit StyleBasicShape(StyleBasicShapeType);
 
   StyleBasicShapeType GetShapeType() const { return mType; }
   nsCSSKeyword GetShapeTypeName() const;
 
   StyleFillRule GetFillRule() const { return mFillRule; }
 
   const mozilla::Position& GetPosition() const {
     MOZ_ASSERT(mType == StyleBasicShapeType::Circle ||
                    mType == StyleBasicShapeType::Ellipse,
                "expected circle or ellipse");
     return mPosition;
   }
 
   bool HasRadius() const {
     MOZ_ASSERT(mType == StyleBasicShapeType::Inset, "expected inset");
-    nsStyleCoord zero;
-    zero.SetCoordValue(0);
     NS_FOR_CSS_HALF_CORNERS(corner) {
-      if (mRadius.Get(corner) != zero) {
+      auto& radius = mRadius.Get(corner);
+      if (radius.HasPercent() || radius.LengthInCSSPixels() != 0.0f) {
         return true;
       }
     }
     return false;
   }
 
-  const nsStyleCorners& GetRadius() const {
+  const mozilla::StyleBorderRadius& GetRadius() const {
     MOZ_ASSERT(mType == StyleBasicShapeType::Inset, "expected inset");
     return mRadius;
   }
 
   // mCoordinates has coordinates for polygon or radii for
   // ellipse and circle.
   const nsTArray<nsStyleCoord>& Coordinates() const { return mCoordinates; }
 
@@ -1688,17 +1684,17 @@ class StyleBasicShape final {
 
   // mCoordinates has coordinates for polygon or radii for
   // ellipse and circle.
   // (top, right, bottom, left) for inset
   nsTArray<nsStyleCoord> mCoordinates;
   // position of center for ellipse or circle
   mozilla::Position mPosition;
   // corner radii for inset (0 if not set)
-  nsStyleCorners mRadius;
+  mozilla::StyleBorderRadius mRadius;
 };
 
 struct StyleSVGPath final {
   const nsTArray<StylePathCommand>& Path() const { return mPath; }
 
   StyleFillRule FillRule() const { return mFillRule; }
 
   bool operator==(const StyleSVGPath& aOther) const {
--- a/servo/components/style/cbindgen.toml
+++ b/servo/components/style/cbindgen.toml
@@ -8,16 +8,17 @@ autogen_warning = """/* DO NOT MODIFY TH
  *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate style -o layout/style/ServoStyleConsts.h`
  */
 #include "nsCoord.h"
 #include "Units.h"
 #include "mozilla/gfx/Types.h"
 class nsAtom;
 namespace mozilla {
   class WritingMode;
+  enum HalfCorner : uint8_t;
   enum LogicalSide : uint8_t;
   namespace css {
     struct URLValue;
   }
 
   // Work-around weird cbindgen renaming.
   typedef css::URLValue StyleURLValue;
   typedef nsAtom StylensAtom;
@@ -83,16 +84,18 @@ include = [
   "Rect",
   "IntersectionObserverRootMargin",
   "Size",
   "MaxSize",
   "FlexBasis",
   "Position",
   "BackgroundSize",
   "BorderImageSlice",
+  "BorderSpacing",
+  "BorderRadius",
   "NonNegativeLengthOrNumberRect",
   "Perspective",
   "ZIndex",
   "TransformOrigin",
 ]
 item_types = ["enums", "structs", "typedefs"]
 
 [export.body]
@@ -180,8 +183,12 @@ item_types = ["enums", "structs", "typed
   // Defined in WritingModes.h
   inline const T& Get(mozilla::Side) const;
   inline const T& Get(mozilla::WritingMode, mozilla::LogicalSide) const;
   inline const T& GetIStart(mozilla::WritingMode) const;
   inline const T& GetBStart(mozilla::WritingMode) const;
   inline const T& GetIEnd(mozilla::WritingMode) const;
   inline const T& GetBEnd(mozilla::WritingMode) const;
 """
+
+"GenericBorderRadius" = """
+  inline const StyleLengthPercentage& Get(mozilla::HalfCorner) const;
+"""
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -570,38 +570,34 @@ impl nsStyleImage {
         })
     }
 }
 
 pub mod basic_shape {
     //! Conversions from and to CSS shape representations.
 
     use crate::gecko::values::GeckoStyleCoordConvertible;
-    use crate::gecko_bindings::structs::{nsStyleCoord, nsStyleCorners};
+    use crate::gecko_bindings::structs::nsStyleCoord;
     use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType};
     use crate::gecko_bindings::structs::{
         StyleGeometryBox, StyleShapeSource, StyleShapeSourceType,
     };
-    use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
     use crate::gecko_bindings::sugar::refptr::RefPtr;
     use crate::values::computed::basic_shape::{
         BasicShape, ClippingShape, FloatAreaShape, ShapeRadius,
     };
-    use crate::values::computed::border::{BorderCornerRadius, BorderRadius};
     use crate::values::computed::length::LengthPercentage;
     use crate::values::computed::motion::OffsetPath;
     use crate::values::computed::url::ComputedUrl;
     use crate::values::generics::basic_shape::{
         BasicShape as GenericBasicShape, InsetRect, Polygon,
     };
     use crate::values::generics::basic_shape::{Circle, Ellipse, Path, PolygonCoord};
     use crate::values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
-    use crate::values::generics::border::BorderRadius as GenericBorderRadius;
     use crate::values::generics::rect::Rect;
-    use crate::values::generics::NonNegative;
     use crate::values::specified::SVGPathData;
     use std::borrow::Borrow;
 
     impl StyleShapeSource {
         /// Convert StyleShapeSource to ShapeSource except URL and Image
         /// types.
         fn into_shape_source<ReferenceBox, ImageOrUrl>(
             &self,
@@ -701,17 +697,17 @@ pub mod basic_shape {
     impl<'a> From<&'a StyleBasicShape> for BasicShape {
         fn from(other: &'a StyleBasicShape) -> Self {
             match other.mType {
                 StyleBasicShapeType::Inset => {
                     let t = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[0]);
                     let r = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[1]);
                     let b = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[2]);
                     let l = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[3]);
-                    let round: BorderRadius = (&other.mRadius).into();
+                    let round = other.mRadius;
                     let round = if round.all_zero() { None } else { Some(round) };
                     let rect = Rect::new(
                         t.expect("inset() offset should be a length, percentage, or calc value"),
                         r.expect("inset() offset should be a length, percentage, or calc value"),
                         b.expect("inset() offset should be a length, percentage, or calc value"),
                         l.expect("inset() offset should be a length, percentage, or calc value"),
                     );
                     GenericBasicShape::Inset(InsetRect { rect, round })
@@ -747,75 +743,16 @@ pub mod basic_shape {
                         fill: other.mFillRule,
                         coordinates: coords,
                     })
                 },
             }
         }
     }
 
-    impl<'a> From<&'a nsStyleCorners> for BorderRadius {
-        fn from(other: &'a nsStyleCorners) -> Self {
-            let get_corner = |index| {
-                BorderCornerRadius::new(
-                    NonNegative(
-                        LengthPercentage::from_gecko_style_coord(&other.data_at(index)).expect(
-                            "<border-radius> should be a length, percentage, or calc value",
-                        ),
-                    ),
-                    NonNegative(
-                        LengthPercentage::from_gecko_style_coord(&other.data_at(index + 1)).expect(
-                            "<border-radius> should be a length, percentage, or calc value",
-                        ),
-                    ),
-                )
-            };
-
-            GenericBorderRadius {
-                top_left: get_corner(0),
-                top_right: get_corner(2),
-                bottom_right: get_corner(4),
-                bottom_left: get_corner(6),
-            }
-        }
-    }
-
-    // Can't be a From impl since we need to set an existing
-    // nsStyleCorners, not create a new one
-    impl BorderRadius {
-        /// Set this `BorderRadius` into a given `nsStyleCoord`.
-        pub fn set_corners(&self, other: &mut nsStyleCorners) {
-            let mut set_corner = |field: &BorderCornerRadius, index| {
-                field
-                    .0
-                    .width()
-                    .to_gecko_style_coord(&mut other.data_at_mut(index));
-                field
-                    .0
-                    .height()
-                    .to_gecko_style_coord(&mut other.data_at_mut(index + 1));
-            };
-            set_corner(&self.top_left, 0);
-            set_corner(&self.top_right, 2);
-            set_corner(&self.bottom_right, 4);
-            set_corner(&self.bottom_left, 6);
-        }
-    }
-
-    /// We use None for a nonexistant radius, but Gecko uses (0 0 0 0 / 0 0 0 0)
-    pub fn set_corners_from_radius(radius: Option<BorderRadius>, other: &mut nsStyleCorners) {
-        if let Some(radius) = radius {
-            radius.set_corners(other);
-        } else {
-            for i in 0..8 {
-                other.data_at_mut(i).set_value(CoordDataValue::Coord(0));
-            }
-        }
-    }
-
     impl<'a> From<&'a nsStyleCoord> for ShapeRadius {
         fn from(other: &'a nsStyleCoord) -> Self {
             let other = other.borrow();
             ShapeRadius::from_gecko_style_coord(other)
                 .expect("<shape-radius> should be a length, percentage, calc, or keyword value")
         }
     }
 
--- a/servo/components/style/gecko_bindings/sugar/ns_style_coord.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_style_coord.rs
@@ -1,17 +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 helpers for Gecko's `nsStyleCoord`.
 
 use crate::gecko_bindings::bindings;
 use crate::gecko_bindings::structs::{nsStyleCoord, nsStyleCoord_Calc, nsStyleCoord_CalcValue};
-use crate::gecko_bindings::structs::{nsStyleCorners, nsStyleSides};
+use crate::gecko_bindings::structs::nsStyleSides;
 use crate::gecko_bindings::structs::{nsStyleUnion, nsStyleUnit, nscoord};
 use std::mem;
 
 impl nsStyleCoord {
     #[inline]
     /// Get a `null` nsStyleCoord.
     pub fn null() -> Self {
         // Can't construct directly because it has private fields
@@ -118,74 +118,16 @@ unsafe impl<'a> CoordData for SidesDataM
 unsafe impl<'a> CoordDataMut for SidesDataMut<'a> {
     unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
         let unit = &mut self.sides.get_mUnits_mut()[self.index] as *mut _;
         let value = &mut self.sides.get_mValues_mut()[self.index] as *mut _;
         (&mut *unit, &mut *value)
     }
 }
 
-impl nsStyleCorners {
-    /// Get a `nsStyleCoord` like object representing the given index's value
-    /// and unit.
-    #[inline]
-    pub fn data_at(&self, index: usize) -> CornersData {
-        CornersData {
-            corners: self,
-            index: index,
-        }
-    }
-
-    /// Get a `nsStyleCoord` like object representing the mutable given index's
-    /// value and unit.
-    #[inline]
-    pub fn data_at_mut(&mut self, index: usize) -> CornersDataMut {
-        CornersDataMut {
-            corners: self,
-            index: index,
-        }
-    }
-}
-
-/// A `nsStyleCoord`-like struct on top of `nsStyleCorners`.
-pub struct CornersData<'a> {
-    corners: &'a nsStyleCorners,
-    index: usize,
-}
-
-/// A `nsStyleCoord`-like struct on top of a mutable `nsStyleCorners` reference.
-pub struct CornersDataMut<'a> {
-    corners: &'a mut nsStyleCorners,
-    index: usize,
-}
-
-unsafe impl<'a> CoordData for CornersData<'a> {
-    fn unit(&self) -> nsStyleUnit {
-        unsafe { self.corners.get_mUnits()[self.index] }
-    }
-    fn union(&self) -> nsStyleUnion {
-        unsafe { self.corners.get_mValues()[self.index] }
-    }
-}
-unsafe impl<'a> CoordData for CornersDataMut<'a> {
-    fn unit(&self) -> nsStyleUnit {
-        unsafe { self.corners.get_mUnits()[self.index] }
-    }
-    fn union(&self) -> nsStyleUnion {
-        unsafe { self.corners.get_mValues()[self.index] }
-    }
-}
-unsafe impl<'a> CoordDataMut for CornersDataMut<'a> {
-    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
-        let unit = &mut self.corners.get_mUnits_mut()[self.index] as *mut _;
-        let value = &mut self.corners.get_mValues_mut()[self.index] as *mut _;
-        (&mut *unit, &mut *value)
-    }
-}
-
 /// Enum representing the tagged union that is CoordData.
 ///
 /// In release mode this should never actually exist in the code, and will be
 /// optimized out by threading matches and inlining.
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub enum CoordDataValue {
     /// eStyleUnit_Null
     Null,
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -47,17 +47,17 @@ use crate::gecko::values::round_border_t
 use crate::logical_geometry::WritingMode;
 use crate::media_queries::Device;
 use crate::properties::computed_value_flags::*;
 use crate::properties::longhands;
 use crate::rule_tree::StrongRuleNode;
 use crate::selector_parser::PseudoElement;
 use servo_arc::{Arc, RawOffsetArc};
 use std::marker::PhantomData;
-use std::mem::{forget, uninitialized, transmute, zeroed};
+use std::mem::{forget, uninitialized, zeroed};
 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::{BoxShadow, Filter, SimpleShadow};
 use crate::values::generics::column::ColumnCount;
 use crate::values::generics::transform::TransformStyle;
@@ -812,44 +812,33 @@ def set_gecko_property(ffi_name, expr):
     }
 
     #[allow(non_snake_case)]
     pub fn reset_${ident}(&mut self, other: &Self) {
         self.copy_${ident}_from(other)
     }
 </%def>
 
-<%def name="impl_corner_style_coord(ident, gecko_ffi_name, x_index, y_index)">
+<%def name="impl_corner_style_coord(ident, gecko_ffi_name, corner)">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
-        v.0.width().to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${x_index}));
-        v.0.height().to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${y_index}));
+        self.gecko.${gecko_ffi_name}.${corner} = v;
     }
     #[allow(non_snake_case)]
     pub fn copy_${ident}_from(&mut self, other: &Self) {
-        self.gecko.${gecko_ffi_name}.data_at_mut(${x_index})
-                  .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${x_index}));
-        self.gecko.${gecko_ffi_name}.data_at_mut(${y_index})
-                  .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${y_index}));
+        self.gecko.${gecko_ffi_name}.${corner} =
+            other.gecko.${gecko_ffi_name}.${corner};
     }
     #[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) -> longhands::${ident}::computed_value::T {
-        use crate::values::computed::border::BorderCornerRadius;
-        let width = GeckoStyleCoordConvertible::from_gecko_style_coord(
-                        &self.gecko.${gecko_ffi_name}.data_at(${x_index}))
-                        .expect("Failed to clone ${ident}");
-        let height = GeckoStyleCoordConvertible::from_gecko_style_coord(
-                        &self.gecko.${gecko_ffi_name}.data_at(${y_index}))
-                        .expect("Failed to clone ${ident}");
-        BorderCornerRadius::new(width, height)
+        self.gecko.${gecko_ffi_name}.${corner}
     }
 </%def>
 
 <%def name="impl_css_url(ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         match v {
             UrlOrNone::Url(ref url) => {
@@ -1382,55 +1371,40 @@ impl ${style_struct.gecko_struct_name} {
 
 <%!
 class Side(object):
     def __init__(self, name, index):
         self.name = name
         self.ident = name.lower()
         self.index = index
 
-class Corner(object):
-    def __init__(self, vert, horiz, index):
-        self.x_name = "HalfCorner::eCorner" + vert + horiz + "X"
-        self.y_name = "HalfCorner::eCorner" + vert + horiz + "Y"
-        self.ident = (vert + "_" + horiz).lower()
-        self.x_index = 2 * index
-        self.y_index = 2 * index + 1
-
 class GridLine(object):
     def __init__(self, name):
         self.ident = "grid-" + name.lower()
         self.name = self.ident.replace('-', '_')
         self.gecko = "m" + to_camel_case(self.ident)
 
 SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
-CORNERS = [Corner("Top", "Left", 0), Corner("Top", "Right", 1),
-           Corner("Bottom", "Right", 2), Corner("Bottom", "Left", 3)]
+CORNERS = ["top_left", "top_right", "bottom_right", "bottom_left"]
 GRID_LINES = map(GridLine, ["row-start", "row-end", "column-start", "column-end"])
 %>
 
 #[allow(dead_code)]
 fn static_assert() {
-    unsafe {
-        % for corner in CORNERS:
-        transmute::<_, [u32; ${corner.x_index}]>([1; structs::${corner.x_name} as usize]);
-        transmute::<_, [u32; ${corner.y_index}]>([1; structs::${corner.y_name} as usize]);
-        % endfor
-    }
     // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.
     % for side in SIDES:
     { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }
     % endfor
 }
 
 
 <% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y)
                                      for x in SIDES
                                      for y in ["color", "style", "width"]] +
-                                    ["border-{0}-radius".format(x.ident.replace("_", "-"))
+                                    ["border-{0}-radius".format(x.replace("_", "-"))
                                      for x in CORNERS]) %>
 
 <%self:impl_trait style_struct_name="Border"
                   skip_longhands="${skip_border_longhands} border-image-source
                                   border-image-repeat border-image-width">
     % for side in SIDES:
     pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) {
         self.gecko.mBorderStyle[${side.index}] = v;
@@ -1489,20 +1463,19 @@ fn static_assert() {
                                 round_to_pixels=True) %>
 
     pub fn border_${side.ident}_has_nonzero_width(&self) -> bool {
         self.gecko.mComputedBorder.${side.ident} != 0
     }
     % endfor
 
     % for corner in CORNERS:
-    <% impl_corner_style_coord("border_%s_radius" % corner.ident,
+    <% impl_corner_style_coord("border_%s_radius" % corner,
                                "mBorderRadius",
-                               corner.x_index,
-                               corner.y_index) %>
+                               corner) %>
     % endfor
 
     pub fn set_border_image_source(&mut self, image: longhands::border_image_source::computed_value::T) {
         unsafe {
             // Prevent leaking of the last elements we did set
             Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource);
         }
 
@@ -2022,17 +1995,17 @@ fn static_assert() {
         };
 
         Either::First(TemplateAreasArc(Arc::new(TemplateAreas{ areas, strings, width })))
     }
 
 </%self:impl_trait>
 
 <% skip_outline_longhands = " ".join("outline-style outline-width".split() +
-                                     ["-moz-outline-radius-{0}".format(x.ident.replace("_", ""))
+                                     ["-moz-outline-radius-{0}".format(x.replace("_", ""))
                                       for x in CORNERS]) %>
 <%self:impl_trait style_struct_name="Outline"
                   skip_longhands="${skip_outline_longhands}">
 
     pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
         self.gecko.mOutlineStyle = v;
         // NB: This is needed to correctly handling the initial value of
         // outline-width when outline-style changes, see the
@@ -2054,20 +2027,19 @@ fn static_assert() {
         self.gecko.mOutlineStyle.clone()
     }
 
     <% impl_non_negative_length("outline_width", "mActualOutlineWidth",
                                 inherit_from="mOutlineWidth",
                                 round_to_pixels=True) %>
 
     % for corner in CORNERS:
-    <% impl_corner_style_coord("_moz_outline_radius_%s" % corner.ident.replace("_", ""),
+    <% impl_corner_style_coord("_moz_outline_radius_%s" % corner.replace("_", ""),
                                "mOutlineRadius",
-                               corner.x_index,
-                               corner.y_index) %>
+                               corner) %>
     % endfor
 
     pub fn outline_has_nonzero_width(&self) -> bool {
         self.gecko.mActualOutlineWidth != 0
     }
 </%self:impl_trait>
 
 <%
@@ -4588,17 +4560,16 @@ fn set_style_svg_path(
     gecko_path.mFillRule = fill;
 }
 
 <%def name="impl_shape_source(ident, gecko_ffi_name)">
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         use crate::gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource};
         use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType};
         use crate::gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource};
-        use crate::gecko::conversions::basic_shape::set_corners_from_radius;
         use crate::gecko::values::GeckoStyleCoordConvertible;
         use crate::values::generics::basic_shape::{BasicShape, ShapeSource};
 
         let ref mut ${ident} = self.gecko.${gecko_ffi_name};
 
         // clean up existing struct
         unsafe { Gecko_DestroyShapeSource(${ident}) };
         ${ident}.mType = StyleShapeSourceType::None;
@@ -4653,18 +4624,20 @@ fn set_style_svg_path(
                         shape.mCoordinates[0].leaky_set_null();
                         inset.rect.0.to_gecko_style_coord(&mut shape.mCoordinates[0]);
                         shape.mCoordinates[1].leaky_set_null();
                         inset.rect.1.to_gecko_style_coord(&mut shape.mCoordinates[1]);
                         shape.mCoordinates[2].leaky_set_null();
                         inset.rect.2.to_gecko_style_coord(&mut shape.mCoordinates[2]);
                         shape.mCoordinates[3].leaky_set_null();
                         inset.rect.3.to_gecko_style_coord(&mut shape.mCoordinates[3]);
-
-                        set_corners_from_radius(inset.round, &mut shape.mRadius);
+                        shape.mRadius = match inset.round {
+                            Some(radius) => radius,
+                            None => crate::values::computed::BorderRadius::zero(),
+                        };
                     }
                     BasicShape::Circle(circ) => {
                         let shape = init_shape(${ident}, StyleBasicShapeType::Circle);
                         unsafe { shape.mCoordinates.set_len(1) };
                         shape.mCoordinates[0].leaky_set_null();
                         circ.radius.to_gecko_style_coord(&mut shape.mCoordinates[0]);
 
                         shape.mPosition = circ.position.into();
--- a/servo/components/style/values/computed/border.rs
+++ b/servo/components/style/values/computed/border.rs
@@ -82,16 +82,26 @@ impl BorderCornerRadius {
         GenericBorderCornerRadius(Size2D::new(
             NonNegativeLengthPercentage::zero(),
             NonNegativeLengthPercentage::zero(),
         ))
     }
 }
 
 impl BorderRadius {
+    /// Returns a `0` border radius.
+    pub fn zero() -> Self {
+        Self {
+            top_left: BorderCornerRadius::zero(),
+            top_right: BorderCornerRadius::zero(),
+            bottom_right: BorderCornerRadius::zero(),
+            bottom_left: BorderCornerRadius::zero(),
+        }
+    }
+
     /// Returns whether all the values are `0px`.
     pub fn all_zero(&self) -> bool {
         fn all(corner: &BorderCornerRadius) -> bool {
             fn is_zero(l: &NonNegativeLengthPercentage) -> bool {
                 *l == NonNegativeLengthPercentage::zero()
             }
             is_zero(corner.0.width()) && is_zero(corner.0.height())
         }
--- a/servo/components/style/values/generics/border.rs
+++ b/servo/components/style/values/generics/border.rs
@@ -48,17 +48,20 @@ pub use self::GenericBorderImageSlice as
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToAnimatedZero,
     ToComputedValue,
     ToCss,
 )]
-pub struct BorderCornerRadius<L>(#[css(field_bound)] pub Size2D<L>);
+#[repr(C)]
+pub struct GenericBorderCornerRadius<L>(#[css(field_bound)] pub Size2D<L>);
+
+pub use self::GenericBorderCornerRadius as BorderCornerRadius;
 
 impl<L> BorderCornerRadius<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderCornerRadius(Size2D::new(w, h))
     }
 }
 
@@ -72,16 +75,17 @@ impl<L> BorderCornerRadius<L> {
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToAnimatedZero,
     ToComputedValue,
     ToCss,
 )]
+#[repr(transparent)]
 pub struct BorderSpacing<L>(#[css(field_bound)] pub Size2D<L>);
 
 impl<L> BorderSpacing<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderSpacing(Size2D::new(w, h))
     }
 }
@@ -96,27 +100,30 @@ impl<L> BorderSpacing<L> {
     Copy,
     Debug,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToComputedValue,
 )]
-pub struct BorderRadius<LengthPercentage> {
+#[repr(C)]
+pub struct GenericBorderRadius<LengthPercentage> {
     /// The top left radius.
-    pub top_left: BorderCornerRadius<LengthPercentage>,
+    pub top_left: GenericBorderCornerRadius<LengthPercentage>,
     /// The top right radius.
-    pub top_right: BorderCornerRadius<LengthPercentage>,
+    pub top_right: GenericBorderCornerRadius<LengthPercentage>,
     /// The bottom right radius.
-    pub bottom_right: BorderCornerRadius<LengthPercentage>,
+    pub bottom_right: GenericBorderCornerRadius<LengthPercentage>,
     /// The bottom left radius.
-    pub bottom_left: BorderCornerRadius<LengthPercentage>,
+    pub bottom_left: GenericBorderCornerRadius<LengthPercentage>,
 }
 
+pub use self::GenericBorderRadius as BorderRadius;
+
 impl<L> BorderRadius<L> {
     /// Returns a new `BorderRadius<L>`.
     #[inline]
     pub fn new(
         tl: BorderCornerRadius<L>,
         tr: BorderCornerRadius<L>,
         br: BorderCornerRadius<L>,
         bl: BorderCornerRadius<L>,
--- a/servo/components/style/values/generics/size.rs
+++ b/servo/components/style/values/generics/size.rs
@@ -20,16 +20,17 @@ use style_traits::{CssWriter, ParseError
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedZero,
     ToAnimatedValue,
     ToComputedValue,
 )]
 #[allow(missing_docs)]
+#[repr(C)]
 pub struct Size2D<L> {
     pub width: L,
     pub height: L,
 }
 
 impl<L> Size2D<L> {
     #[inline]
     /// Create a new `Size2D` for an area of given width and height.
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -3698,16 +3698,25 @@ pub unsafe extern "C" fn Servo_Serialize
     output: *mut nsAString,
 ) {
     easing
         .mTiming
         .to_css(&mut CssWriter::new(&mut *output))
         .unwrap();
 }
 
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SerializeBorderRadius(
+    radius: *const computed::BorderRadius,
+    output: *mut nsAString,
+) {
+    (*radius).to_css(&mut CssWriter::new(&mut *output)).unwrap();
+}
+
 #[no_mangle]
 pub extern "C" fn Servo_GetProperties_Overriding_Animation(
     element: RawGeckoElementBorrowed,
     list: RawGeckoCSSPropertyIDListBorrowed,
     set: nsCSSPropertyIDSetBorrowedMut,
 ) {
     let element = GeckoElement(element);
     let element_data = match element.borrow_data() {
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-shapes/shape-outside/values/shape-outside-inset-006.html.ini
+++ /dev/null
@@ -1,22 +0,0 @@
-[shape-outside-inset-006.html]
-  [inset(10px round 10.1200px 20.34px 30.56px 40.780px) - computed]
-    expected: FAIL
-
-  [inset(10px round 10.123px 20.00px 30.10px 40.5678px) - computed]
-    expected: FAIL
-
-  [inset(10px round +10.1200px +20.340px +30.56px +40.780px) - computed]
-    expected: FAIL
-
-  [inset(10px round 10.1200px 20.34px 30.56px 40.780px / 10.1200px 20.34px 30.56px 40.780px) - computed]
-    expected: FAIL
-
-  [inset(10px round 10.123px 20.00px 30.10px 40.5678px / 10.123px 20.00px 30.10px 40.5678px) - computed]
-    expected: FAIL
-
-  [inset(10px round +10.1200px +20.340px +30.56px +40.780px / +10.1200px +20.340px +30.56px +40.780px) - computed]
-    expected: FAIL
-
-  [inset(10px round +10.123px +20.00px +30.10px +40.5678px / 10.123px +20.00px +30.10px +40.5678px) - computed]
-    expected: FAIL
-