Backed out changeset df9036657f25 (bug 1400438) for crashing chrome test mobile/android/components/extensions/test/mochitest/test_ext_options_ui.html on Android. r=backout on a CLOSED TREE
authorSebastian Hengst <archaeopteryx@coole-files.de>
Tue, 19 Sep 2017 16:33:05 +0200
changeset 381636 229b5c4c95cb7b431f51fffb27c9ae01fdb9a5a3
parent 381635 133b69a9a749d18d66d908626b0e8bf9d2f3920a
child 381637 7d8bac06b7275668e62e5dfb80ffcad69ec71035
push id32535
push userkwierso@gmail.com
push dateTue, 19 Sep 2017 21:06:08 +0000
treeherdermozilla-central@c0d1f9eb2a40 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1400438
milestone57.0a1
backs outdf9036657f25fe45b6afb21dfc26ce5d481fe8f8
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset df9036657f25 (bug 1400438) for crashing chrome test mobile/android/components/extensions/test/mochitest/test_ext_options_ui.html on Android. r=backout on a CLOSED TREE
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRenderingBorders.cpp
layout/painting/nsCSSRenderingBorders.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
layout/style/nsComputedDOMStyle.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -756,21 +756,23 @@ ConstructBorderRenderer(nsPresContext* a
   Float borderWidths[4] = { Float(border.top) / oneDevPixel,
                                    Float(border.right) / oneDevPixel,
                                    Float(border.bottom) / oneDevPixel,
                                    Float(border.left) / oneDevPixel };
   Rect dirtyRect = NSRectToRect(aDirtyRect, oneDevPixel);
 
   uint8_t borderStyles[4];
   nscolor borderColors[4];
-
-  // pull out styles, colors
+  nsBorderColors* compositeColors[4];
+
+  // pull out styles, colors, composite colors
   NS_FOR_CSS_SIDES (i) {
     borderStyles[i] = aStyleBorder.GetBorderStyle(i);
     borderColors[i] = ourColor->CalcComplexColor(aStyleBorder.mBorderColor[i]);
+    aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
   }
 
   PrintAsFormatString(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
 
   nsIDocument* document = nullptr;
   nsIContent* content = aForFrame->GetContent();
   if (content) {
     document = content->OwnerDoc();
@@ -780,17 +782,17 @@ ConstructBorderRenderer(nsPresContext* a
                              document,
                              aDrawTarget,
                              dirtyRect,
                              joinedBorderAreaPx,
                              borderStyles,
                              borderWidths,
                              bgRadii,
                              borderColors,
-                             aStyleBorder.mBorderColors.get(),
+                             compositeColors,
                              bgColor);
 }
 
 
 DrawResult
 nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
                                            gfxContext& aRenderingContext,
                                            nsIFrame* aForFrame,
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -85,16 +85,19 @@ static Color MakeBorderColor(nscolor aCo
 // border going inwards) and an array of line styles, calculate the
 // color that that stripe of the border should be rendered in.
 static Color ComputeColorForLine(uint32_t aLineIndex,
                                    const BorderColorStyle* aBorderColorStyle,
                                    uint32_t aBorderColorStyleCount,
                                    nscolor aBorderColor,
                                    nscolor aBackgroundColor);
 
+static Color ComputeCompositeColorForLine(uint32_t aLineIndex,
+                                          const nsBorderColors* aBorderColors);
+
 // little helper function to check if the array of 4 floats given are
 // equal to the given value
 static bool
 CheckFourFloatsEqual(const Float *vals, Float k)
 {
   return (vals[0] == k &&
           vals[1] == k &&
           vals[2] == k &&
@@ -170,35 +173,34 @@ nsCSSBorderRenderer::nsCSSBorderRenderer
                                          const nsIDocument* aDocument,
                                          DrawTarget* aDrawTarget,
                                          const Rect& aDirtyRect,
                                          Rect& aOuterRect,
                                          const uint8_t* aBorderStyles,
                                          const Float* aBorderWidths,
                                          RectCornerRadii& aBorderRadii,
                                          const nscolor* aBorderColors,
-                                         const nsBorderColors* aCompositeColors,
+                                         nsBorderColors* const* aCompositeColors,
                                          nscolor aBackgroundColor)
   : mPresContext(aPresContext),
     mDocument(aDocument),
     mDrawTarget(aDrawTarget),
     mDirtyRect(aDirtyRect),
     mOuterRect(aOuterRect),
     mBorderRadii(aBorderRadii),
     mBackgroundColor(aBackgroundColor)
 {
   PodCopy(mBorderStyles, aBorderStyles, 4);
   PodCopy(mBorderWidths, aBorderWidths, 4);
   PodCopy(mBorderColors, aBorderColors, 4);
-  NS_FOR_CSS_SIDES(side) {
-    if (aCompositeColors && !(*aCompositeColors)[side].IsEmpty()) {
-      mCompositeColors[side] = &(*aCompositeColors)[side];
-    } else {
-      mCompositeColors[side] = nullptr;
-    }
+  if (aCompositeColors) {
+    PodCopy(mCompositeColors, aCompositeColors, 4);
+  } else {
+    static nsBorderColors * const noColors[4] = { nullptr };
+    PodCopy(mCompositeColors, noColors, 4);
   }
 
   mInnerRect = mOuterRect;
   mInnerRect.Deflate(
       Margin(mBorderStyles[0] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[0] : 0,
              mBorderStyles[1] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[1] : 0,
              mBorderStyles[2] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[2] : 0,
              mBorderStyles[3] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[3] : 0));
@@ -312,21 +314,19 @@ nsCSSBorderRenderer::AreBorderSideFinalS
     }
 
     if (((1 << i) & aSides) == 0) {
       continue;
     }
 
     if (mBorderStyles[firstStyle] != mBorderStyles[i] ||
         mBorderColors[firstStyle] != mBorderColors[i] ||
-        !mCompositeColors[firstStyle] != !mCompositeColors[i] ||
-        (mCompositeColors[firstStyle] &&
-         *mCompositeColors[firstStyle] != *mCompositeColors[i])) {
+        !nsBorderColors::Equal(mCompositeColors[firstStyle],
+                               mCompositeColors[i]))
       return false;
-    }
   }
 
   /* Then if it's one of the two-tone styles and we're not
    * just comparing the TL or BR sides */
   switch (mBorderStyles[firstStyle]) {
     case NS_STYLE_BORDER_STYLE_GROOVE:
     case NS_STYLE_BORDER_STYLE_RIDGE:
     case NS_STYLE_BORDER_STYLE_INSET:
@@ -1260,36 +1260,46 @@ ComputeColorForLine(uint32_t aLineIndex,
                     nscolor aBackgroundColor)
 {
   NS_ASSERTION(aLineIndex < aBorderColorStyleCount, "Invalid lineIndex given");
 
   return MakeBorderColor(aBorderColor, aBackgroundColor,
                          aBorderColorStyle[aLineIndex]);
 }
 
+Color
+ComputeCompositeColorForLine(uint32_t aLineIndex,
+                             const nsBorderColors* aBorderColors)
+{
+  while (aLineIndex-- && aBorderColors->mNext)
+    aBorderColors = aBorderColors->mNext;
+
+  return Color::FromABGR(aBorderColors->mColor);
+}
+
 void
-nsCSSBorderRenderer::DrawBorderSidesCompositeColors(
-  int aSides, const nsTArray<nscolor>& aCompositeColors)
+nsCSSBorderRenderer::DrawBorderSidesCompositeColors(int aSides, const nsBorderColors *aCompositeColors)
 {
   RectCornerRadii radii = mBorderRadii;
 
   // the generic composite colors path; each border is 1px in size
   Rect soRect = mOuterRect;
   Float maxBorderWidth = 0;
   NS_FOR_CSS_SIDES (i) {
     maxBorderWidth = std::max(maxBorderWidth, Float(mBorderWidths[i]));
   }
 
   Float fakeBorderSizes[4];
 
   Point itl = mInnerRect.TopLeft();
   Point ibr = mInnerRect.BottomRight();
 
   for (uint32_t i = 0; i < uint32_t(maxBorderWidth); i++) {
-    ColorPattern color(ToDeviceColor(Color::FromABGR(aCompositeColors[i])));
+    ColorPattern color(ToDeviceColor(
+                         ComputeCompositeColorForLine(i, aCompositeColors)));
 
     Rect siRect = soRect;
     siRect.Deflate(1.0);
 
     // now cap the rects to the real mInnerRect
     Point tl = siRect.TopLeft();
     Point br = siRect.BottomRight();
 
@@ -1319,17 +1329,17 @@ nsCSSBorderRenderer::DrawBorderSides(int
 {
   if (aSides == 0 || (aSides & ~eSideBitsAll) != 0) {
     NS_WARNING("DrawBorderSides: invalid sides!");
     return;
   }
 
   uint8_t borderRenderStyle = NS_STYLE_BORDER_STYLE_NONE;
   nscolor borderRenderColor;
-  const nsTArray<nscolor>* compositeColors = nullptr;
+  const nsBorderColors *compositeColors = nullptr;
 
   uint32_t borderColorStyleCount = 0;
   BorderColorStyle borderColorStyleTopLeft[3], borderColorStyleBottomRight[3];
   BorderColorStyle *borderColorStyle = nullptr;
 
   NS_FOR_CSS_SIDES (i) {
     if ((aSides & (1 << i)) == 0)
       continue;
@@ -1377,17 +1387,17 @@ nsCSSBorderRenderer::DrawBorderSides(int
   // color is used for the remainder of the border's size.  Just
   // hand off to another function to do all that.
   if (compositeColors) {
     Float maxBorderWidth(0);
     NS_FOR_CSS_SIDES (i) {
       maxBorderWidth = std::max(maxBorderWidth, mBorderWidths[i]);
     }
     if (maxBorderWidth <= MAX_COMPOSITE_BORDER_WIDTH) {
-      DrawBorderSidesCompositeColors(aSides, *compositeColors);
+      DrawBorderSidesCompositeColors(aSides, compositeColors);
       return;
     }
     NS_WARNING("DrawBorderSides: too large border width for composite colors");
  }
 
   // We're not doing compositeColors, so we can calculate the
   // borderColorStyle based on the specified style.  The
   // borderColorStyle array goes from the outer to the inner style.
@@ -3130,73 +3140,80 @@ nsCSSBorderRenderer::DrawNoCompositeColo
                  firstColor, secondColor, skirtSize, skirtSlope);
     }
   }
 }
 
 void
 nsCSSBorderRenderer::DrawRectangularCompositeColors()
 {
-  nscolor currentColors[4];
-  NS_FOR_CSS_SIDES(side) {
-    currentColors[side] = mBorderColors[side];
-  }
+  nsBorderColors *currentColors[4];
+  memcpy(currentColors, mCompositeColors, sizeof(nsBorderColors*) * 4);
   Rect rect = mOuterRect;
   rect.Deflate(0.5);
 
   const twoFloats cornerAdjusts[4] = { { +0.5,  0   },
                                         {    0, +0.5 },
                                         { -0.5,  0   },
                                         {    0, -0.5 } };
 
   for (int i = 0; i < mBorderWidths[0]; i++) {
     NS_FOR_CSS_SIDES(side) {
-      // advance to the next composite color if one exists
-      if (mCompositeColors[side] &&
-          uint32_t(i) < mCompositeColors[side]->Length()) {
-        currentColors[side] = (*mCompositeColors[side])[i];
-      }
-    }
-    NS_FOR_CSS_SIDES(side) {
       int sideNext = (side + 1) % 4;
 
       Point firstCorner = rect.CCWCorner(side) + cornerAdjusts[side];
       Point secondCorner = rect.CWCorner(side) - cornerAdjusts[side];
 
-      Color currentColor = Color::FromABGR(currentColors[side]);
+      Color currentColor = Color::FromABGR(
+        currentColors[side] ? currentColors[side]->mColor
+                            : mBorderColors[side]);
 
       mDrawTarget->StrokeLine(firstCorner, secondCorner,
                               ColorPattern(ToDeviceColor(currentColor)));
 
       Point cornerTopLeft = rect.CWCorner(side) - Point(0.5, 0.5);
-      Color nextColor = Color::FromABGR(currentColors[sideNext]);
+      Color nextColor = Color::FromABGR(
+        currentColors[sideNext] ? currentColors[sideNext]->mColor
+                                : mBorderColors[sideNext]);
 
       Color cornerColor((currentColor.r + nextColor.r) / 2.f,
                         (currentColor.g + nextColor.g) / 2.f,
                         (currentColor.b + nextColor.b) / 2.f,
                         (currentColor.a + nextColor.a) / 2.f);
       mDrawTarget->FillRect(Rect(cornerTopLeft, Size(1, 1)),
                             ColorPattern(ToDeviceColor(cornerColor)));
+
+      if (side != 0) {
+        // We'll have to keep side 0 for the color averaging on side 3.
+        if (currentColors[side] && currentColors[side]->mNext) {
+          currentColors[side] = currentColors[side]->mNext;
+        }
+      }
+    }
+    // Now advance the color for side 0.
+    if (currentColors[0] && currentColors[0]->mNext) {
+      currentColors[0] = currentColors[0]->mNext;
     }
     rect.Deflate(1);
   }
 }
 
 void
 nsCSSBorderRenderer::DrawBorders()
 {
   bool forceSeparateCorners = false;
 
   if (mAllBordersSameStyle &&
       ((mCompositeColors[0] == nullptr &&
        (mBorderStyles[0] == NS_STYLE_BORDER_STYLE_NONE ||
         mBorderStyles[0] == NS_STYLE_BORDER_STYLE_HIDDEN ||
         mBorderColors[0] == NS_RGBA(0,0,0,0))) ||
-       (mCompositeColors[0] && mCompositeColors[0]->Length() == 1 &&
-        (*mCompositeColors[0])[0] == NS_RGBA(0,0,0,0))))
+       (mCompositeColors[0] &&
+        (mCompositeColors[0]->mColor == NS_RGBA(0,0,0,0) &&
+         !mCompositeColors[0]->mNext))))
   {
     // All borders are the same style, and the style is either none or hidden, or the color
     // is transparent.
     // This also checks if the first composite color is transparent, and there are
     // no others. It doesn't check if there are subsequent transparent ones, because
     // that would be very silly.
     return;
   }
--- a/layout/painting/nsCSSRenderingBorders.h
+++ b/layout/painting/nsCSSRenderingBorders.h
@@ -95,17 +95,17 @@ public:
                       const nsIDocument* aDocument,
                       DrawTarget* aDrawTarget,
                       const Rect& aDirtyRect,
                       Rect& aOuterRect,
                       const uint8_t* aBorderStyles,
                       const Float* aBorderWidths,
                       RectCornerRadii& aBorderRadii,
                       const nscolor* aBorderColors,
-                      const nsBorderColors* aCompositeColors,
+                      nsBorderColors* const* aCompositeColors,
                       nscolor aBackgroundColor);
 
   // draw the entire border
   void DrawBorders();
 
   bool CanCreateWebRenderCommands();
   void CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                mozilla::wr::IpcResourceUpdateQueue& aResources,
@@ -146,19 +146,17 @@ private:
   uint8_t mBorderStyles[4];
   Float mBorderWidths[4];
   RectCornerRadii mBorderRadii;
 
   // the colors for 'border-top-color' et. al.
   nscolor mBorderColors[4];
 
   // the lists of colors for '-moz-border-top-colors' et. al.
-  // the pointers here are either nullptr, or referring to a non-empty
-  // nsTArray, so no additional empty check is needed.
-  const nsTArray<nscolor>* mCompositeColors[4];
+  nsBorderColors* mCompositeColors[4];
 
   // the background color
   nscolor mBackgroundColor;
 
   // calculated values
   bool mAllBordersSameStyle;
   bool mAllBordersSameWidth;
   bool mOneUnitBorder;
@@ -232,18 +230,17 @@ private:
   // core rendering
   //
 
   // draw the border for the given sides, using the style of the first side
   // present in the bitmask
   void DrawBorderSides (int aSides);
 
   // function used by the above to handle -moz-border-colors
-  void DrawBorderSidesCompositeColors(
-    int aSides, const nsTArray<nscolor>& compositeColors);
+  void DrawBorderSidesCompositeColors(int aSides, const nsBorderColors *compositeColors);
 
   // Setup the stroke options for the given dashed/dotted side
   void SetupDashedOptions(StrokeOptions* aStrokeOptions,
                           Float aDash[2], mozilla::Side aSide,
                           Float aBorderLength, bool isCorner);
 
   // Draw the given dashed/dotte side
   void DrawDashedOrDottedSide(mozilla::Side aSide);
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1251,28 +1251,44 @@ Gecko_AtomEqualsUTF8IgnoreCase(nsIAtom* 
 }
 
 void
 Gecko_EnsureMozBorderColors(nsStyleBorder* aBorder)
 {
   aBorder->EnsureBorderColors();
 }
 
+void Gecko_ClearMozBorderColors(nsStyleBorder* aBorder, mozilla::Side aSide)
+{
+  aBorder->ClearBorderColors(aSide);
+}
+
+void
+Gecko_AppendMozBorderColors(nsStyleBorder* aBorder, mozilla::Side aSide,
+                            nscolor aColor)
+{
+  aBorder->AppendBorderColor(aSide, aColor);
+}
+
 void
 Gecko_CopyMozBorderColors(nsStyleBorder* aDest, const nsStyleBorder* aSrc,
                           mozilla::Side aSide)
 {
   if (aSrc->mBorderColors) {
-    aDest->EnsureBorderColors();
-    aDest->mBorderColors->mColors[aSide] = aSrc->mBorderColors->mColors[aSide];
-  } else {
-    aDest->ClearBorderColors(aSide);
+    aDest->CopyBorderColorsFrom(aSrc->mBorderColors[aSide], aSide);
   }
 }
 
+const nsBorderColors*
+Gecko_GetMozBorderColors(const nsStyleBorder* aBorder, mozilla::Side aSide)
+{
+  MOZ_ASSERT(aBorder);
+  return aBorder->mBorderColors ? aBorder->mBorderColors[aSide] : nullptr;
+}
+
 void
 Gecko_FontFamilyList_Clear(FontFamilyList* aList) {
   aList->Clear();
 }
 
 void
 Gecko_FontFamilyList_AppendNamed(FontFamilyList* aList, nsIAtom* aName, bool aQuoted)
 {
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -277,18 +277,23 @@ nsIAtom* Gecko_Atomize16(const nsAString
 void Gecko_AddRefAtom(nsIAtom* aAtom);
 void Gecko_ReleaseAtom(nsIAtom* aAtom);
 const uint16_t* Gecko_GetAtomAsUTF16(nsIAtom* aAtom, uint32_t* aLength);
 bool Gecko_AtomEqualsUTF8(nsIAtom* aAtom, const char* aString, uint32_t aLength);
 bool Gecko_AtomEqualsUTF8IgnoreCase(nsIAtom* aAtom, const char* aString, uint32_t aLength);
 
 // Border style
 void Gecko_EnsureMozBorderColors(nsStyleBorder* aBorder);
+void Gecko_ClearMozBorderColors(nsStyleBorder* aBorder, mozilla::Side aSide);
+void Gecko_AppendMozBorderColors(nsStyleBorder* aBorder, mozilla::Side aSide,
+                                 nscolor aColor);
 void Gecko_CopyMozBorderColors(nsStyleBorder* aDest, const nsStyleBorder* aSrc,
                                mozilla::Side aSide);
+const nsBorderColors* Gecko_GetMozBorderColors(const nsStyleBorder* aBorder,
+                                               mozilla::Side aSide);
 
 // Font style
 void Gecko_FontFamilyList_Clear(FontFamilyList* aList);
 void Gecko_FontFamilyList_AppendNamed(FontFamilyList* aList, nsIAtom* aName, bool aQuoted);
 void Gecko_FontFamilyList_AppendGeneric(FontFamilyList* list, FontFamilyType familyType);
 void Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src);
 // will not run destructors on dst, give it uninitialized memory
 // font_id is LookAndFeel::FontID
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -185,16 +185,17 @@ whitelist-types = [
     "gfxFontVariation",
     "GridNamedArea",
     "HalfCorner",
     "Image",
     "ImageURL",
     "Keyframe",
     "nsAttrName",
     "nsAttrValue",
+    "nsBorderColors",
     "nscolor",
     "nsChangeHint",
     "nsCSSCounterDesc",
     "nsCSSCounterStyleRule",
     "nsCSSFontFaceRule",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
@@ -437,16 +438,17 @@ structs-types = [
     "ServoElementSnapshotTable",
     "ServoStyleSetSizes",
     "SheetParsingMode",
     "StyleBasicShape",
     "StyleBasicShapeType",
     "StyleShapeSource",
     "StyleTransition",
     "gfxFontFeatureValueSet",
+    "nsBorderColors",
     "nsCSSCounterStyleRule",
     "nsCSSFontFaceRule",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
     "nsCSSShadowArray",
     "nsCSSUnit",
     "nsCSSValue",
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -5485,24 +5485,29 @@ nsComputedDOMStyle::GetLineHeightCoord(n
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::GetBorderColorsFor(mozilla::Side aSide)
 {
   const nsStyleBorder *border = StyleBorder();
 
   if (border->mBorderColors) {
-    const nsTArray<nscolor>& borderColors = (*border->mBorderColors)[aSide];
-    if (!borderColors.IsEmpty()) {
+    nsBorderColors* borderColors = border->mBorderColors[aSide];
+    if (borderColors) {
       RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
-      for (nscolor color : borderColors) {
+
+      do {
         RefPtr<nsROCSSPrimitiveValue> primitive = new nsROCSSPrimitiveValue;
-        SetToRGBAColor(primitive, color);
+
+        SetToRGBAColor(primitive, borderColors->mColor);
+
         valueList->AppendCSSValue(primitive.forget());
-      }
+        borderColors = borderColors->mNext;
+      } while (borderColors);
+
       return valueList.forget();
     }
   }
 
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetIdent(eCSSKeyword_none);
   return val.forget();
 }
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -7743,35 +7743,38 @@ nsRuleNode::ComputeBorderData(void* aSta
     case eCSSUnit_Unset:
     case eCSSUnit_None:
       border->ClearBorderColors(side);
       break;
 
     case eCSSUnit_Inherit: {
       conditions.SetUncacheable();
       border->ClearBorderColors(side);
-      if (parentBorder->mBorderColors) {
-        border->EnsureBorderColors();
-        border->mBorderColors->mColors[side] =
-          parentBorder->mBorderColors->mColors[side];
+      if (parentContext) {
+        nsBorderColors *parentColors;
+        parentBorder->GetCompositeColors(side, &parentColors);
+        if (parentColors) {
+          border->EnsureBorderColors();
+          border->mBorderColors[side] = parentColors->Clone();
+        }
       }
       break;
     }
 
     case eCSSUnit_List:
     case eCSSUnit_ListDep: {
       // Some composite border color information has been specified for this
       // border side.
       border->EnsureBorderColors();
       border->ClearBorderColors(side);
       const nsCSSValueList* list = value.GetListValue();
       while (list) {
         if (SetColor(list->mValue, unused, mPresContext,
                      aContext, borderColor, conditions))
-          border->mBorderColors->mColors[side].AppendElement(borderColor);
+          border->AppendBorderColor(side, borderColor);
         else {
           NS_NOTREACHED("unexpected item in -moz-border-*-colors list");
         }
         list = list->mNext;
       }
       break;
     }
 
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -315,17 +315,18 @@ nsStylePadding::CalcDifference(const nsS
   // FIXME: It would be good to return a weaker hint here that doesn't
   // force reflow of all descendants, but the hint would need to force
   // reflow of the frame's children (see how
   // ReflowInput::InitResizeFlags initializes the inline-resize flag).
   return NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics;
 }
 
 nsStyleBorder::nsStyleBorder(const nsPresContext* aContext)
-  : mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL)
+  : mBorderColors(nullptr)
+  , mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL)
   , mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH)
   , mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH)
   , mFloatEdge(StyleFloatEdge::ContentBox)
   , mBoxDecorationBreak(StyleBoxDecorationBreak::Slice)
   , mComputedBorder(0, 0, 0, 0)
 {
   MOZ_COUNT_CTOR(nsStyleBorder);
 
@@ -343,45 +344,72 @@ nsStyleBorder::nsStyleBorder(const nsPre
     mBorder.Side(side) = medium;
     mBorderStyle[side] = NS_STYLE_BORDER_STYLE_NONE;
     mBorderColor[side] = StyleComplexColor::CurrentColor();
   }
 
   mTwipsPerPixel = aContext->DevPixelsToAppUnits(1);
 }
 
+nsBorderColors::~nsBorderColors()
+{
+  NS_CSS_DELETE_LIST_MEMBER(nsBorderColors, this, mNext);
+}
+
+nsBorderColors*
+nsBorderColors::Clone(bool aDeep) const
+{
+  nsBorderColors* result = new nsBorderColors(mColor);
+  if (MOZ_UNLIKELY(!result)) {
+    return result;
+  }
+  if (aDeep) {
+    NS_CSS_CLONE_LIST_MEMBER(nsBorderColors, this, mNext, result, (false));
+  }
+  return result;
+}
+
 nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc)
-  : mBorderRadius(aSrc.mBorderRadius)
+  : mBorderColors(nullptr)
+  , mBorderRadius(aSrc.mBorderRadius)
   , mBorderImageSource(aSrc.mBorderImageSource)
   , mBorderImageSlice(aSrc.mBorderImageSlice)
   , mBorderImageWidth(aSrc.mBorderImageWidth)
   , mBorderImageOutset(aSrc.mBorderImageOutset)
   , mBorderImageFill(aSrc.mBorderImageFill)
   , mBorderImageRepeatH(aSrc.mBorderImageRepeatH)
   , mBorderImageRepeatV(aSrc.mBorderImageRepeatV)
   , mFloatEdge(aSrc.mFloatEdge)
   , mBoxDecorationBreak(aSrc.mBoxDecorationBreak)
   , mComputedBorder(aSrc.mComputedBorder)
   , mBorder(aSrc.mBorder)
   , mTwipsPerPixel(aSrc.mTwipsPerPixel)
 {
   MOZ_COUNT_CTOR(nsStyleBorder);
   if (aSrc.mBorderColors) {
-    mBorderColors.reset(new nsBorderColors(*aSrc.mBorderColors));
+    NS_FOR_CSS_SIDES(side) {
+      CopyBorderColorsFrom(aSrc.mBorderColors[side], side);
+    }
   }
 
   NS_FOR_CSS_SIDES(side) {
     mBorderStyle[side] = aSrc.mBorderStyle[side];
     mBorderColor[side] = aSrc.mBorderColor[side];
   }
 }
 
 nsStyleBorder::~nsStyleBorder()
 {
   MOZ_COUNT_DTOR(nsStyleBorder);
+  if (mBorderColors) {
+    for (int32_t i = 0; i < 4; i++) {
+      delete mBorderColors[i];
+    }
+    delete [] mBorderColors;
+  }
 }
 
 void
 nsStyleBorder::FinishStyle(nsPresContext* aPresContext)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
 
@@ -483,18 +511,19 @@ nsStyleBorder::CalcDifference(const nsSt
         mBorderImageWidth   != aNewData.mBorderImageWidth) {
       return nsChangeHint_RepaintFrame;
     }
   }
 
   // Note that at this point if mBorderColors is non-null so is
   // aNewData.mBorderColors
   if (mBorderColors) {
-    NS_FOR_CSS_SIDES(side) {
-      if ((*mBorderColors)[side] != (*aNewData.mBorderColors)[side]) {
+    NS_FOR_CSS_SIDES(ix) {
+      if (!nsBorderColors::Equal(mBorderColors[ix],
+                                 aNewData.mBorderColors[ix])) {
         return nsChangeHint_RepaintFrame;
       }
     }
   }
 
   // mBorder is the specified border value.  Changes to this don't
   // need any change processing, since we operate on the computed
   // border values instead.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -975,16 +975,48 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     NS_FOR_CSS_SIDES(side) {
       // Clamp negative calc() to 0.
       aPadding.Side(side) = std::max(mPadding.ToLength(side), 0);
     }
     return true;
   }
 };
 
+struct nsBorderColors
+{
+  nsBorderColors* mNext;
+  nscolor mColor;
+
+  nsBorderColors() : mNext(nullptr), mColor(NS_RGB(0,0,0)) {}
+  explicit nsBorderColors(const nscolor& aColor) : mNext(nullptr), mColor(aColor) {}
+  ~nsBorderColors();
+
+  nsBorderColors* Clone() const { return Clone(true); }
+
+  static bool Equal(const nsBorderColors* c1,
+                      const nsBorderColors* c2) {
+    if (c1 == c2) {
+      return true;
+    }
+    while (c1 && c2) {
+      if (c1->mColor != c2->mColor) {
+        return false;
+      }
+      c1 = c1->mNext;
+      c2 = c2->mNext;
+    }
+    // both should be nullptr if these are equal, otherwise one
+    // has more colors than another
+    return !c1 && !c2;
+  }
+
+private:
+  nsBorderColors* Clone(bool aDeep) const;
+};
+
 struct nsCSSShadowItem
 {
   nscoord mXOffset;
   nscoord mYOffset;
   nscoord mRadius;
   nscoord mSpread;
 
   nscolor      mColor;
@@ -1101,36 +1133,16 @@ private:
 
 // Returns if the given border style type is visible or not
 static bool IsVisibleBorderStyle(uint8_t aStyle)
 {
   return (aStyle != NS_STYLE_BORDER_STYLE_NONE &&
           aStyle != NS_STYLE_BORDER_STYLE_HIDDEN);
 }
 
-struct nsBorderColors
-{
-  nsBorderColors() = default;
-
-  // GCC cannot generate this copy constructor correctly, since nsTArray
-  // has explicit copy constructor, and we use array of nsTArray here.
-  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82235
-  nsBorderColors(const nsBorderColors& aOther) {
-    NS_FOR_CSS_SIDES(side) {
-      mColors[side] = aOther.mColors[side];
-    }
-  }
-
-  const nsTArray<nscolor>& operator[](mozilla::Side aSide) const {
-    return mColors[aSide];
-  }
-
-  nsTArray<nscolor> mColors[4];
-};
-
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder
 {
   explicit nsStyleBorder(const nsPresContext* aContext);
   nsStyleBorder(const nsStyleBorder& aBorder);
   ~nsStyleBorder();
 
   // Resolves and tracks mBorderImageSource.  Only called with a Servo-backed
   // style system, where those images must be resolved later than the OMT
@@ -1144,23 +1156,37 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBorder, sz);
   }
   void Destroy(nsPresContext* aContext);
 
   nsChangeHint CalcDifference(const nsStyleBorder& aNewData) const;
 
   void EnsureBorderColors() {
     if (!mBorderColors) {
-      mBorderColors.reset(new nsBorderColors);
+      mBorderColors = new nsBorderColors*[4];
+      if (mBorderColors) {
+        for (int32_t i = 0; i < 4; i++) {
+          mBorderColors[i] = nullptr;
+        }
+      }
     }
   }
 
   void ClearBorderColors(mozilla::Side aSide) {
-    if (mBorderColors) {
-      mBorderColors->mColors[aSide].Clear();
+    if (mBorderColors && mBorderColors[aSide]) {
+      delete mBorderColors[aSide];
+      mBorderColors[aSide] = nullptr;
+    }
+  }
+
+  void CopyBorderColorsFrom(const nsBorderColors* aSrcBorderColors, mozilla::Side aSide) {
+    if (aSrcBorderColors) {
+      EnsureBorderColors();
+      ClearBorderColors(aSide);
+      mBorderColors[aSide] = aSrcBorderColors->Clone();
     }
   }
 
   // Return whether aStyle is a visible style.  Invisible styles cause
   // the relevant computed border width to be 0.
   // Note that this does *not* consider the effects of 'border-image':
   // if border-style is none, but there is a loaded border image,
   // HasVisibleStyle will be false even though there *is* a border.
@@ -1225,27 +1251,50 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   {
     if (mBorderImageSource.GetType() == eStyleImageType_Image) {
       mBorderImageSource.ResolveImage(aContext);
     }
   }
 
   nsMargin GetImageOutset() const;
 
+  void GetCompositeColors(int32_t aIndex, nsBorderColors** aColors) const
+  {
+    if (!mBorderColors) {
+      *aColors = nullptr;
+    } else {
+      *aColors = mBorderColors[aIndex];
+    }
+  }
+
+  void AppendBorderColor(int32_t aIndex, nscolor aColor)
+  {
+    NS_ASSERTION(aIndex >= 0 && aIndex <= 3, "bad side for composite border color");
+    nsBorderColors* colorEntry = new nsBorderColors(aColor);
+    if (!mBorderColors[aIndex]) {
+      mBorderColors[aIndex] = colorEntry;
+    } else {
+      nsBorderColors* last = mBorderColors[aIndex];
+      while (last->mNext) {
+        last = last->mNext;
+      }
+      last->mNext = colorEntry;
+    }
+  }
+
   imgIRequest* GetBorderImageRequest() const
   {
     if (mBorderImageSource.GetType() == eStyleImageType_Image) {
       return mBorderImageSource.GetImageData();
     }
     return nullptr;
   }
 
 public:
-  // [reset] composite (stripe) colors
-  mozilla::UniquePtr<nsBorderColors> mBorderColors;
+  nsBorderColors** mBorderColors;     // [reset] composite (stripe) colors
   nsStyleCorners mBorderRadius;       // [reset] coord, percent
   nsStyleImage   mBorderImageSource;  // [reset]
   nsStyleSides   mBorderImageSlice;   // [reset] factor, percent
   nsStyleSides   mBorderImageWidth;   // [reset] length, factor, percent, auto
   nsStyleSides   mBorderImageOutset;  // [reset] length, factor
 
   uint8_t        mBorderImageFill;    // [reset]
   uint8_t        mBorderImageRepeatH; // [reset] see nsStyleConsts.h