Bug 1266621 part 5 - Convert border-color to store complex color. r=heycam draft
authorXidorn Quan <me@upsuper.org>
Tue, 27 Sep 2016 20:16:35 +1000
changeset 418918 fba71e92cf7d23728a7c963ae12027e7aee7e986
parent 418917 9626d350649f4ba6dba597e3df05fe1829307610
child 419001 fac2841ecaeffe57c4d052c27e710ea7efd6c006
child 419195 c44415f0470b732d598eede2bbcc7335727c006e
push id30801
push userxquan@mozilla.com
push dateThu, 29 Sep 2016 09:22:37 +0000
reviewersheycam
bugs1266621
milestone52.0a1
Bug 1266621 part 5 - Convert border-color to store complex color. r=heycam MozReview-Commit-ID: 19sl9f3EVgt
layout/base/nsCSSRendering.cpp
layout/generic/nsColumnSetFrame.cpp
layout/generic/nsImageFrame.cpp
layout/style/StyleAnimationValue.cpp
layout/style/nsCSSPropList.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleContext.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/test_transitions_events.html
layout/style/test/test_transitions_per_property.html
layout/xul/tree/nsTreeBodyFrame.cpp
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -668,19 +668,19 @@ nsCSSRendering::PaintBorder(nsPresContex
 
   nsStyleBorder newStyleBorder(*styleBorder);
   // We could do something fancy to avoid the TrackImage/UntrackImage
   // work, but it doesn't seem worth it.  (We need to call TrackImage
   // since we're not going through nsRuleNode::ComputeBorderData.)
   newStyleBorder.TrackImage(aPresContext);
 
   NS_FOR_CSS_SIDES(side) {
-    newStyleBorder.SetBorderColor(side,
-      aStyleContext->GetVisitedDependentColor(
-        nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]));
+    nscolor color = aStyleContext->GetVisitedDependentColor(
+      nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]);
+    newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
   }
   DrawResult result =
     PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                                aDirtyRect, aBorderArea, newStyleBorder,
                                aStyleContext, aFlags, aSkipSides);
 
   // We could do something fancy to avoid the TrackImage/UntrackImage
   // work, but it doesn't seem worth it.  (We need to call UntrackImage
@@ -799,23 +799,19 @@ nsCSSRendering::PaintBorderWithStyleBord
   Rect dirtyRect = NSRectToRect(aDirtyRect, twipsPerPixel);
 
   uint8_t borderStyles[4];
   nscolor borderColors[4];
   nsBorderColors *compositeColors[4];
 
   // pull out styles, colors, composite colors
   NS_FOR_CSS_SIDES (i) {
-    bool foreground;
     borderStyles[i] = aStyleBorder.GetBorderStyle(i);
-    aStyleBorder.GetBorderColor(i, borderColors[i], foreground);
+    borderColors[i] = ourColor->CalcComplexColor(aStyleBorder.mBorderColor[i]);
     aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
-
-    if (foreground)
-      borderColors[i] = ourColor->mColor;
   }
 
   PrintAsFormatString(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
   //PrintAsFormatString ("bgRadii: %f %f %f %f\n", bgRadii[0], bgRadii[1], bgRadii[2], bgRadii[3]);
 
 #if 0
   // this will draw a transparent red backround underneath the border area
   ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 0.5f)));
@@ -1770,26 +1766,23 @@ IsOpaqueBorderEdge(const nsStyleBorder& 
 
   // If we're using a border image, assume it's not fully opaque,
   // because we may not even have the image loaded at this point, and
   // even if we did, checking whether the relevant tile is fully
   // opaque would be too much work.
   if (aBorder.mBorderImageSource.GetType() != eStyleImageType_Null)
     return false;
 
-  nscolor color;
-  bool isForeground;
-  aBorder.GetBorderColor(aSide, color, isForeground);
-
+  StyleComplexColor color = aBorder.mBorderColor[aSide];
   // We don't know the foreground color here, so if it's being used
   // we must assume it might be transparent.
-  if (isForeground)
+  if (!color.IsNumericColor()) {
     return false;
-
-  return NS_GET_A(color) == 255;
+  }
+  return NS_GET_A(color.mColor) == 255;
 }
 
 /**
  * Returns true if all border edges are either missing or opaque.
  */
 static bool
 IsOpaqueBorder(const nsStyleBorder& aBorder)
 {
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -90,23 +90,23 @@ nsColumnSetFrame::PaintColumnRule(nsRend
   // the left border. PaintBorder() does all the rendering for us, so we not
   // only save an enormous amount of code but we'll support all the line styles that
   // we support on borders!
   nsStyleBorder border(presContext);
   Sides skipSides;
   if (isVertical) {
     border.SetBorderWidth(NS_SIDE_TOP, ruleWidth);
     border.SetBorderStyle(NS_SIDE_TOP, ruleStyle);
-    border.SetBorderColor(NS_SIDE_TOP, ruleColor);
+    border.mBorderTopColor = StyleComplexColor::FromColor(ruleColor);
     skipSides |= mozilla::eSideBitsLeftRight;
     skipSides |= mozilla::eSideBitsBottom;
   } else {
     border.SetBorderWidth(NS_SIDE_LEFT, ruleWidth);
     border.SetBorderStyle(NS_SIDE_LEFT, ruleStyle);
-    border.SetBorderColor(NS_SIDE_LEFT, ruleColor);
+    border.mBorderLeftColor = StyleComplexColor::FromColor(ruleColor);
     skipSides |= mozilla::eSideBitsTopBottom;
     skipSides |= mozilla::eSideBitsRight;
   }
 
   // Get our content rect as an absolute coordinate, not relative to
   // our parent (which is what the X and Y normally is)
   nsRect contentRect = GetContentRect() - GetRect().TopLeft() + aPt;
   nsSize ruleSize = isVertical ? nsSize(contentRect.width, ruleWidth)
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1260,19 +1260,17 @@ nsImageFrame::DisplayAltText(nsPresConte
   }
 }
 
 struct nsRecessedBorder : public nsStyleBorder {
   nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
     : nsStyleBorder(aPresContext)
   {
     NS_FOR_CSS_SIDES(side) {
-      // Note: use SetBorderColor here because we want to make sure
-      // the "special" flags are unset.
-      SetBorderColor(side, NS_RGB(0, 0, 0));
+      mBorderColor[side] = StyleComplexColor::FromColor(NS_RGB(0, 0, 0));
       mBorder.Side(side) = aBorderWidth;
       // Note: use SetBorderStyle here because we want to affect
       // mComputedBorder
       SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
     }
   }
 };
 
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -3414,32 +3414,16 @@ StyleAnimationValue::UncomputeValue(nsCS
 template<typename T>
 inline const T&
 StyleDataAtOffset(const void* aStyleStruct, ptrdiff_t aOffset)
 {
   return *reinterpret_cast<const T*>(
     reinterpret_cast<const uint8_t*>(aStyleStruct) + aOffset);
 }
 
-static void
-ExtractBorderColor(nsStyleContext* aStyleContext, const void* aStyleBorder,
-                   mozilla::css::Side aSide,
-                   StyleAnimationValue& aComputedValue)
-{
-  nscolor color;
-  bool foreground;
-  static_cast<const nsStyleBorder*>(aStyleBorder)->
-    GetBorderColor(aSide, color, foreground);
-  if (foreground) {
-    // FIXME: should add test for this
-    color = aStyleContext->StyleColor()->mColor;
-  }
-  aComputedValue.SetColorValue(color);
-}
-
 static bool
 StyleCoordToValue(const nsStyleCoord& aCoord, StyleAnimationValue& aValue)
 {
   switch (aCoord.GetUnit()) {
     case eStyleUnit_Normal:
       aValue.SetNormalValue();
       break;
     case eStyleUnit_Auto:
@@ -3830,33 +3814,16 @@ StyleAnimationValue::ExtractComputedValu
         #undef BORDER_WIDTH_CASE
 
         case eCSSProperty_column_rule_width:
           aComputedValue.SetCoordValue(
             static_cast<const nsStyleColumn*>(styleStruct)->
               GetComputedColumnRuleWidth());
           break;
 
-        case eCSSProperty_border_bottom_color:
-          ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_BOTTOM,
-                             aComputedValue);
-          break;
-        case eCSSProperty_border_left_color:
-          ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_LEFT,
-                             aComputedValue);
-          break;
-        case eCSSProperty_border_right_color:
-          ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_RIGHT,
-                             aComputedValue);
-          break;
-        case eCSSProperty_border_top_color:
-          ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_TOP,
-                             aComputedValue);
-          break;
-
         case eCSSProperty_column_count: {
           const nsStyleColumn *styleColumn =
             static_cast<const nsStyleColumn*>(styleStruct);
           if (styleColumn->mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO) {
             aComputedValue.SetAutoValue();
           } else {
             aComputedValue.SetIntValue(styleColumn->mColumnCount,
                                        eUnit_Integer);
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -785,18 +785,18 @@ CSS_PROP_BORDER(
     BorderBottomColor,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED |
         CSS_PROPERTY_HASHLESS_COLOR_QUIRK,
     "",
     VARIANT_HCK,
     kBorderColorKTable,
-    CSS_PROP_NO_OFFSET,
-    eStyleAnimType_Custom)
+    offsetof(nsStyleBorder, mBorderBottomColor),
+    eStyleAnimType_ComplexColor)
 CSS_PROP_BORDER(
     -moz-border-bottom-colors,
     border_bottom_colors,
     CSS_PROP_DOMPROP_PREFIXED(BorderBottomColors),
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
     "",
@@ -1051,18 +1051,18 @@ CSS_PROP_BORDER(
     BorderLeftColor,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_HASHLESS_COLOR_QUIRK |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
     "",
     VARIANT_HCK,
     kBorderColorKTable,
-    CSS_PROP_NO_OFFSET,
-    eStyleAnimType_Custom)
+    offsetof(nsStyleBorder, mBorderLeftColor),
+    eStyleAnimType_ComplexColor)
 CSS_PROP_BORDER(
     -moz-border-left-colors,
     border_left_colors,
     CSS_PROP_DOMPROP_PREFIXED(BorderLeftColors),
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
     "",
@@ -1113,18 +1113,18 @@ CSS_PROP_BORDER(
     BorderRightColor,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_HASHLESS_COLOR_QUIRK |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
     "",
     VARIANT_HCK,
     kBorderColorKTable,
-    CSS_PROP_NO_OFFSET,
-    eStyleAnimType_Custom)
+    offsetof(nsStyleBorder, mBorderRightColor),
+    eStyleAnimType_ComplexColor)
 CSS_PROP_BORDER(
     -moz-border-right-colors,
     border_right_colors,
     CSS_PROP_DOMPROP_PREFIXED(BorderRightColors),
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
     "",
@@ -1187,18 +1187,18 @@ CSS_PROP_BORDER(
     BorderTopColor,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED |
         CSS_PROPERTY_HASHLESS_COLOR_QUIRK,
     "",
     VARIANT_HCK,
     kBorderColorKTable,
-    CSS_PROP_NO_OFFSET,
-    eStyleAnimType_Custom)
+    offsetof(nsStyleBorder, mBorderTopColor),
+    eStyleAnimType_ComplexColor)
 CSS_PROP_BORDER(
     -moz-border-top-colors,
     border_top_colors,
     CSS_PROP_DOMPROP_PREFIXED(BorderTopColors),
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
     "",
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -5268,25 +5268,17 @@ nsComputedDOMStyle::GetBorderWidthFor(mo
 
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::GetBorderColorFor(mozilla::css::Side aSide)
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-
-  nscolor color;
-  bool foreground;
-  StyleBorder()->GetBorderColor(aSide, color, foreground);
-  if (foreground) {
-    color = StyleColor()->mColor;
-  }
-
-  SetToRGBAColor(val, color);
+  SetValueFromComplexColor(val, StyleBorder()->mBorderColor[aSide]);
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::GetMarginWidthFor(mozilla::css::Side aSide)
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -7592,55 +7592,22 @@ nsRuleNode::ComputeBorderData(void* aSta
       MOZ_ASSERT(false, "unrecognized border color unit");
     }
   }
 
   // border-color, border-*-color: color, string, enum, inherit
   {
     const nsCSSPropertyID* subprops =
       nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color);
-    bool foreground;
     NS_FOR_CSS_SIDES(side) {
-      const nsCSSValue& value = *aRuleData->ValueFor(subprops[side]);
-      if (eCSSUnit_Inherit == value.GetUnit()) {
-        conditions.SetUncacheable();
-        if (parentContext) {
-          parentBorder->GetBorderColor(side, borderColor, foreground);
-          if (foreground) {
-            // We want to inherit the color from the parent, not use the
-            // color on the element where this chunk of style data will be
-            // used.  We can ensure that the data for the parent are fully
-            // computed (unlike for the element where this will be used, for
-            // which the color could be specified on a more specific rule).
-            border->SetBorderColor(side, parentContext->StyleColor()->mColor);
-          } else
-            border->SetBorderColor(side, borderColor);
-        } else {
-          // We're the root
-          border->SetBorderToForeground(side);
-        }
-      }
-      else if (SetColor(value, unused, mPresContext, aContext, borderColor,
-                        conditions)) {
-        border->SetBorderColor(side, borderColor);
-      }
-      else if (eCSSUnit_Enumerated == value.GetUnit()) {
-        switch (value.GetIntValue()) {
-          case NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR:
-            border->SetBorderToForeground(side);
-            break;
-          default:
-            NS_NOTREACHED("Unexpected enumerated color");
-            break;
-        }
-      }
-      else if (eCSSUnit_Initial == value.GetUnit() ||
-               eCSSUnit_Unset == value.GetUnit()) {
-        border->SetBorderToForeground(side);
-      }
+      SetComplexColor<eUnsetInitial>(*aRuleData->ValueFor(subprops[side]),
+                                     parentBorder->mBorderColor[side],
+                                     StyleComplexColor::CurrentColor(),
+                                     mPresContext,
+                                     border->mBorderColor[side], conditions);
     }
   }
 
   // border-radius: length, percent, inherit
   {
     const nsCSSPropertyID* subprops =
       nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_radius);
     NS_FOR_CSS_FULL_CORNERS(corner) {
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -1170,24 +1170,18 @@ nsStyleContext::CalcStyleDifferenceInter
       }
     }
 
     // NB: Calling Peek on |this|, not |thisVis| (see above).
     if (!change && PeekStyleBorder()) {
       const nsStyleBorder *thisVisBorder = thisVis->StyleBorder();
       const nsStyleBorder *otherVisBorder = otherVis->StyleBorder();
       NS_FOR_CSS_SIDES(side) {
-        bool thisFG, otherFG;
-        // Dummy initialisations to keep Valgrind/Memcheck happy.
-        // See bug 1122375 comment 4.
-        nscolor thisColor = NS_RGBA(0, 0, 0, 0);
-        nscolor otherColor = NS_RGBA(0, 0, 0, 0);
-        thisVisBorder->GetBorderColor(side, thisColor, thisFG);
-        otherVisBorder->GetBorderColor(side, otherColor, otherFG);
-        if (thisFG != otherFG || (!thisFG && thisColor != otherColor)) {
+        if (thisVisBorder->mBorderColor[side] !=
+            otherVisBorder->mBorderColor[side]) {
           change = true;
           break;
         }
       }
     }
 
     // NB: Calling Peek on |this|, not |thisVis| (see above).
     if (!change && PeekStyleOutline()) {
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -457,18 +457,18 @@ nsStyleBorder::nsStyleBorder(StyleStruct
   nscoord medium =
     (StaticPresData::Get()->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM];
   NS_FOR_CSS_SIDES(side) {
     mBorderImageSlice.Set(side, nsStyleCoord(1.0f, eStyleUnit_Percent));
     mBorderImageWidth.Set(side, nsStyleCoord(1.0f, eStyleUnit_Factor));
     mBorderImageOutset.Set(side, nsStyleCoord(0.0f, eStyleUnit_Factor));
 
     mBorder.Side(side) = medium;
-    mBorderStyle[side] = NS_STYLE_BORDER_STYLE_NONE | BORDER_COLOR_FOREGROUND;
-    mBorderColor[side] = NS_RGB(0, 0, 0);
+    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);
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -877,20 +877,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   inline bool HasLocalBackground() const;
 
   const nsStyleImageLayers::Layer& BottomLayer() const { return mImage.BottomLayer(); }
 
   nsStyleImageLayers mImage;
   nscolor mBackgroundColor;       // [reset]
 };
 
-#define BORDER_COLOR_FOREGROUND   0x20
-#define BORDER_COLOR_SPECIAL      BORDER_COLOR_FOREGROUND
-#define BORDER_STYLE_MASK         0x1F
-
 #define NS_SPACING_MARGIN   0
 #define NS_SPACING_PADDING  1
 #define NS_SPACING_BORDER   2
 
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleMargin
 {
   explicit nsStyleMargin(StyleStructContext aContext);
@@ -1195,17 +1191,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   // 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.
   bool HasVisibleStyle(mozilla::css::Side aSide) const
   {
-    return IsVisibleBorderStyle(GetBorderStyle(aSide));
+    return IsVisibleBorderStyle(mBorderStyle[aSide]);
   }
 
   // aBorderWidth is in twips
   void SetBorderWidth(mozilla::css::Side aSide, nscoord aBorderWidth)
   {
     nscoord roundedWidth =
       NS_ROUND_BORDER_TO_PIXELS(aBorderWidth, mTwipsPerPixel);
     mBorder.Side(aSide) = roundedWidth;
@@ -1234,54 +1230,32 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nscoord GetComputedBorderWidth(mozilla::css::Side aSide) const
   {
     return GetComputedBorder().Side(aSide);
   }
 
   uint8_t GetBorderStyle(mozilla::css::Side aSide) const
   {
     NS_ASSERTION(aSide <= NS_SIDE_LEFT, "bad side");
-    return (mBorderStyle[aSide] & BORDER_STYLE_MASK);
+    return mBorderStyle[aSide];
   }
 
   void SetBorderStyle(mozilla::css::Side aSide, uint8_t aStyle)
   {
     NS_ASSERTION(aSide <= NS_SIDE_LEFT, "bad side");
-    mBorderStyle[aSide] &= ~BORDER_STYLE_MASK;
-    mBorderStyle[aSide] |= (aStyle & BORDER_STYLE_MASK);
+    mBorderStyle[aSide] = aStyle;
     mComputedBorder.Side(aSide) =
       (HasVisibleStyle(aSide) ? mBorder.Side(aSide) : 0);
   }
 
   inline bool IsBorderImageLoaded() const
   {
     return mBorderImageSource.IsLoaded();
   }
 
-  void GetBorderColor(mozilla::css::Side aSide, nscolor& aColor,
-                      bool& aForeground) const
-  {
-    aForeground = false;
-    NS_ASSERTION(aSide <= NS_SIDE_LEFT, "bad side");
-    if ((mBorderStyle[aSide] & BORDER_COLOR_SPECIAL) == 0) {
-      aColor = mBorderColor[aSide];
-    } else if (mBorderStyle[aSide] & BORDER_COLOR_FOREGROUND) {
-      aForeground = true;
-    } else {
-      NS_NOTREACHED("OUTLINE_COLOR_INITIAL should not be set here");
-    }
-  }
-
-  void SetBorderColor(mozilla::css::Side aSide, nscolor aColor)
-  {
-    NS_ASSERTION(aSide <= NS_SIDE_LEFT, "bad side");
-    mBorderColor[aSide] = aColor;
-    mBorderStyle[aSide] &= ~BORDER_COLOR_SPECIAL;
-  }
-
   void TrackImage(nsPresContext* aContext)
   {
     if (mBorderImageSource.GetType() == eStyleImageType_Image) {
       mBorderImageSource.TrackImage(aContext);
     }
   }
   void UntrackImage(nsPresContext* aContext)
   {
@@ -1309,24 +1283,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
       mBorderColors[aIndex] = colorEntry;
     } else {
       nsBorderColors* last = mBorderColors[aIndex];
       while (last->mNext) {
         last = last->mNext;
       }
       last->mNext = colorEntry;
     }
-    mBorderStyle[aIndex] &= ~BORDER_COLOR_SPECIAL;
-  }
-
-  void SetBorderToForeground(mozilla::css::Side aSide)
-  {
-    NS_ASSERTION(aSide <= NS_SIDE_LEFT, "bad side");
-    mBorderStyle[aSide] &= ~BORDER_COLOR_SPECIAL;
-    mBorderStyle[aSide] |= BORDER_COLOR_FOREGROUND;
   }
 
   imgIRequest* GetBorderImageRequest() const
   {
     if (mBorderImageSource.GetType() == eStyleImageType_Image) {
       return mBorderImageSource.GetImageData();
     }
     return nullptr;
@@ -1342,16 +1308,32 @@ public:
 
   uint8_t        mBorderImageFill;    // [reset]
   uint8_t        mBorderImageRepeatH; // [reset] see nsStyleConsts.h
   uint8_t        mBorderImageRepeatV; // [reset]
   mozilla::StyleFloatEdge mFloatEdge; // [reset]
   mozilla::StyleBoxDecorationBreak mBoxDecorationBreak; // [reset]
 
 protected:
+  uint8_t       mBorderStyle[4];  // [reset] See nsStyleConsts.h
+
+public:
+  // [reset] the colors to use for a simple border.
+  // not used for -moz-border-colors
+  union {
+    struct {
+      mozilla::StyleComplexColor mBorderTopColor;
+      mozilla::StyleComplexColor mBorderRightColor;
+      mozilla::StyleComplexColor mBorderBottomColor;
+      mozilla::StyleComplexColor mBorderLeftColor;
+    };
+    mozilla::StyleComplexColor mBorderColor[4];
+  };
+
+protected:
   // mComputedBorder holds the CSS2.1 computed border-width values.
   // In particular, these widths take into account the border-style
   // for the relevant side, and the values are rounded to the nearest
   // device pixel (which is not part of the definition of computed
   // values). The presence or absence of a border-image does not
   // affect border-width values.
   nsMargin      mComputedBorder;
 
@@ -1363,25 +1345,35 @@ protected:
   // case they have more specific rules setting the border style.
   //
   // Note that this isn't quite the CSS specified value, since this
   // has had the enumerated border widths converted to lengths, and
   // all lengths converted to twips.  But it's not quite the computed
   // value either. The values are rounded to the nearest device pixel.
   nsMargin      mBorder;
 
-  uint8_t       mBorderStyle[4];  // [reset] See nsStyleConsts.h
-  nscolor       mBorderColor[4];  // [reset] the colors to use for a simple
-                                  // border.  not used for -moz-border-colors
 private:
   nscoord       mTwipsPerPixel;
 
   nsStyleBorder& operator=(const nsStyleBorder& aOther) = delete;
 };
 
+#define ASSERT_BORDER_COLOR_FIELD(side_)                          \
+  static_assert(offsetof(nsStyleBorder, mBorder##side_##Color) == \
+                  offsetof(nsStyleBorder, mBorderColor) +         \
+                    size_t(mozilla::eSide##side_) *               \
+                    sizeof(mozilla::StyleComplexColor),           \
+                "mBorder" #side_ "Color must be at same offset "  \
+                "as mBorderColor[mozilla::eSide" #side_ "]")
+ASSERT_BORDER_COLOR_FIELD(Top);
+ASSERT_BORDER_COLOR_FIELD(Right);
+ASSERT_BORDER_COLOR_FIELD(Bottom);
+ASSERT_BORDER_COLOR_FIELD(Left);
+#undef ASSERT_BORDER_COLOR_FIELD
+
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleOutline
 {
   explicit nsStyleOutline(StyleStructContext aContext);
   nsStyleOutline(const nsStyleOutline& aOutline);
   ~nsStyleOutline() {
     MOZ_COUNT_DTOR(nsStyleOutline);
   }
--- a/layout/style/test/test_transitions_events.html
+++ b/layout/style/test/test_transitions_events.html
@@ -62,20 +62,16 @@ var gTestCount = 0;
 function started_test() { ++gTestCount; }
 function finished_test() { if (--gTestCount == 0) { SimpleTest.finish(); } }
 
 function $(id) { return document.getElementById(id); }
 function cs(id) { return getComputedStyle($(id), ""); }
 
 var got_one_root = false;
 var got_one_target = false;
-var got_one_target_bordertop = false;
-var got_one_target_borderright = false;
-var got_one_target_borderbottom = false;
-var got_one_target_borderleft = false;
 var got_two_target = false;
 var got_three_top = false;
 var got_three_right = false;
 var got_three_bottom = false;
 var got_three_left = false;
 var got_four_root = false;
 var got_body = false;
 var did_stops = false;
@@ -139,64 +135,30 @@ document.documentElement.addEventListene
          " element with id '" + event.target.id + "' " +
          "elapsedTime=" + event.elapsedTime +
          " propertyName='" + event.propertyName + "'");
     }
   }, false);
 
 $("one").addEventListener("transitionend",
   function(event) {
-    switch (event.propertyName) {
-      case "color":
-        ok(!got_one_target,
-           "transitionend on one on target (color)");
-        got_one_target = true;
-        event.stopPropagation();
-        break;
-      case "border-top-color":
-        ok(!got_one_target_bordertop,
-           "transitionend on one on target (border-top-color)");
-        got_one_target_bordertop = true;
-        event.stopPropagation();
-        break;
-      case "border-right-color":
-        ok(!got_one_target_borderright,
-           "transitionend on one on target (border-right-color)");
-        got_one_target_borderright = true;
-        // Let this event through to body
-        break;
-      case "border-bottom-color":
-        ok(!got_one_target_borderbottom,
-           "transitionend on one on target (border-bottom-color)");
-        got_one_target_borderbottom = true;
-        event.stopPropagation();
-        break;
-      case "border-left-color":
-        ok(!got_one_target_borderleft,
-           "transitionend on one on target (border-left-color)");
-        got_one_target_borderleft = true;
-        event.stopPropagation();
-        break;
-      default:
-        ok(false, "unexpected property name " + event.propertyName +
-                  " for transitionend on one on target");
-    }
+    is(event.propertyName, "color", "unexpected " +
+       "property name for transitionend on one on target");
+    ok(!got_one_target,
+        "transitionend on one on target (color)");
+    got_one_target = true;
+    event.stopPropagation();
     is(event.elapsedTime, 0.5,
        "elapsedTime for transitionend on one");
     is(cs("one").getPropertyValue(event.propertyName), "rgb(0, 255, 0)",
        "computed style of " + event.propertyName + " for transitionend on one");
     finished_test();
   }, false);
 
 started_test(); // color on #one
-started_test(); // border-top-color on #one
-started_test(); // border-right-color on #one
-started_test(); // border-right-color on #one (listener on root)
-started_test(); // border-bottom-color on #one
-started_test(); // border-left-color on #one
 $("one").style.color = "lime";
 
 
 $("two").addEventListener("transitionend",
   function(event) {
     event.stopPropagation();
 
     ok(!got_two_target, "transitionend on two on target");
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -104,35 +104,31 @@ var supported_properties = {
                              // an intermediate form.
                              /* test_length_percent_pair_unclamped */ ],
     "background-size": [ test_background_size_transition,
                          // FIXME: We don't currently test clamping,
                          // since background-size uses calc() as an
                          // intermediate form.
                          /* test_length_percent_pair_clamped */ ],
     "border-bottom-color": [ test_color_transition,
-                             test_currentcolor_transition,
-                             test_border_color_transition ],
+                             test_true_currentcolor_transition ],
     "border-bottom-width": [ test_length_transition,
                              test_length_clamped ],
     "border-left-color": [ test_color_transition,
-                           test_currentcolor_transition,
-                           test_border_color_transition ],
+                           test_true_currentcolor_transition ],
     "border-left-width": [ test_length_transition,
                            test_length_clamped ],
     "border-right-color": [ test_color_transition,
-                            test_currentcolor_transition,
-                            test_border_color_transition ],
+                            test_true_currentcolor_transition ],
     "border-right-width": [ test_length_transition,
                             test_length_clamped ],
     "border-spacing": [ test_length_pair_transition,
                         test_length_pair_transition_clamped ],
     "border-top-color": [ test_color_transition,
-                          test_currentcolor_transition,
-                          test_border_color_transition ],
+                          test_true_currentcolor_transition ],
     "border-top-width": [ test_length_transition,
                            test_length_clamped ],
     "bottom": [ test_length_transition, test_percent_transition,
                 test_length_percent_calc_transition,
                 test_length_unclamped, test_percent_unclamped ],
     "clip": [ test_rect_transition ],
     "clip-path": [ test_clip_path_transition ],
     "color": [ test_color_transition,
@@ -254,18 +250,17 @@ var supported_properties = {
                          // opacity is clamped in computed style
                          // (not parsing/interpolation)
                          test_float_zeroToOne_clamped ],
     // NOTE: when calc() is supported on 'stroke-width', we should add
     // test_length_percent_calc_transition.
     "stroke-width": [ test_length_transition_svg, test_percent_transition,
                       test_length_clamped_svg, test_percent_clamped ],
     "text-decoration": [ test_color_shorthand_transition,
-                         test_currentcolor_shorthand_transition,
-                         test_border_color_shorthand_transition ],
+                         test_true_currentcolor_shorthand_transition ],
     "text-decoration-color": [ test_color_transition,
                                test_true_currentcolor_transition ],
     "text-emphasis-color": [ test_color_transition,
                              test_true_currentcolor_transition ],
     "text-indent": [ test_length_transition, test_percent_transition,
                      test_length_percent_calc_transition,
                      test_length_unclamped, test_percent_unclamped ],
     "text-shadow": [ test_shadow_transition ],
@@ -1381,50 +1376,32 @@ function test_true_currentcolor_transiti
      msg_prefix + "interpolation of rgba color and currentcolor");
 
   // It is not possible to check distance, because there is a hidden
   // dimension for ratio of currentcolor.
 
   div.style.removeProperty("color");
 }
 
-function test_border_color_transition(prop, get_color=(x => x), is_shorthand=false) {
-  div.style.setProperty("transition-property", "none", "");
-  div.style.setProperty(prop, "rgb(128, 64, 0)", "");
-  div.style.setProperty("color", "rgb(0, 0, 128)", "");
-  is(get_color(cs.getPropertyValue(prop)), "rgb(128, 64, 0)",
-     "color-valued property " + prop + ": computed value before transition");
-  div.style.setProperty("transition-property", prop, "");
-  div.style.removeProperty(prop);
-  is(get_color(cs.getPropertyValue(prop)), "rgb(96, 48, 32)",
-     "color-valued property " + prop + ": interpolation of initial value");
-
-  if (!is_shorthand) {
-    check_distance(prop, "rgb(128, 64, 0)", "rgb(96, 48, 32)", "initial");
-  }
-
-  div.style.removeProperty("color");
-}
-
 function get_color_from_shorthand_value(value) {
-  var m = value.match(/rgb\([^, ]*, [^, ]*, [^, ]*\)/);
+  var m = value.match(/rgba?\([^, ]*, [^, ]*, [^, ]*(?:, [^, ]*)?\)/);
   isnot(m, null, "shorthand property value should contain color");
   return m[0];
 }
 
 function test_color_shorthand_transition(prop) {
   test_color_transition(prop, get_color_from_shorthand_value, true);
 }
 
 function test_currentcolor_shorthand_transition(prop) {
   test_currentcolor_transition(prop, get_color_from_shorthand_value, true);
 }
 
-function test_border_color_shorthand_transition(prop) {
-  test_border_color_transition(prop, get_color_from_shorthand_value, true);
+function test_true_currentcolor_shorthand_transition(prop) {
+  test_true_currentcolor_transition(prop, get_color_from_shorthand_value, true);
 }
 
 function test_clip_path_equals(computedValStr, expectedList)
 {
   // Check simple case "none"
   if (computedValStr == "none" && computedValStr == expectedList[0]) {
     return true;
   }
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -3257,27 +3257,22 @@ nsTreeBodyFrame::PaintCell(int32_t      
 
       nsMargin twistyMargin;
       twistyContext->StyleMargin()->GetMargin(twistyMargin);
       twistyRect.Inflate(twistyMargin);
 
       aRenderingContext.ThebesContext()->Save();
 
       const nsStyleBorder* borderStyle = lineContext->StyleBorder();
-      nscolor color;
-      bool useForegroundColor;
-      borderStyle->GetBorderColor(NS_SIDE_LEFT, color, useForegroundColor);
-      if (useForegroundColor) {
-        // GetBorderColor didn't touch color, thus grab it from the treeline context
-        color = lineContext->StyleColor()->mColor;
-      }
+      // Resolve currentcolor values against the treeline context
+      nscolor color = lineContext->StyleColor()->
+        CalcComplexColor(borderStyle->mBorderLeftColor);
       ColorPattern colorPatt(ToDeviceColor(color));
 
-      uint8_t style;
-      style = borderStyle->GetBorderStyle(NS_SIDE_LEFT);
+      uint8_t style = borderStyle->GetBorderStyle(NS_SIDE_LEFT);
       StrokeOptions strokeOptions;
       nsLayoutUtils::InitDashPattern(strokeOptions, style);
 
       nscoord srcX = currX + twistyRect.width - mIndentation / 2;
       nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight + aPt.y;
 
       DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
       nsPresContext* pc = PresContext();