Bug 1541546 - Use the rust color representation. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 04 Apr 2019 11:35:16 +0000
changeset 467999 bab32ba95bcc91e02e6458cbed32e612f3c38c3c
parent 467998 51298369fb0b21351379bb92835ebbe6e00edca4
child 468000 4fedc80c2609b5bae58ca322c84aa1ecc5bb0781
push id112673
push userccoroiu@mozilla.com
push dateThu, 04 Apr 2019 22:20:03 +0000
treeherdermozilla-inbound@f0fb97a222ff [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1541546
milestone68.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 1541546 - Use the rust color representation. r=heycam Differential Revision: https://phabricator.services.mozilla.com/D25976
gfx/src/X11UndefineNone.h
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRenderingGradients.cpp
layout/style/ComputedStyle.cpp
layout/style/ServoBindings.toml
layout/style/ServoStyleConstsForwards.h
layout/style/StyleComplexColor.cpp
layout/style/StyleComplexColor.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/xul/nsTextBoxFrame.cpp
layout/xul/tree/nsTreeBodyFrame.cpp
servo/components/style/gecko_bindings/sugar/mod.rs
servo/components/style/gecko_bindings/sugar/style_complex_color.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/values/computed/ui.rs
servo/components/style/values/generics/ui.rs
servo/ports/geckolib/cbindgen.toml
widget/cocoa/nsNativeThemeCocoa.mm
widget/windows/nsNativeThemeWin.cpp
--- a/gfx/src/X11UndefineNone.h
+++ b/gfx/src/X11UndefineNone.h
@@ -22,16 +22,21 @@
 #endif
 
 // X11 also defines Always, which conflicts with some style system enum variant
 // names, so get rid of that too, given we don't use it anywhere else.
 #ifdef Always
 #  undef Always
 #endif
 
+// And Complex...
+#ifdef Complex
+#  undef Complex
+#endif
+
 // X11/Xlib.h also defines True and False, get rid of those too for
 // the same reasons as above...
 #ifdef True
 #  undef True
 #  define X11True 1
 #endif
 #ifdef False
 #  undef False
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -801,17 +801,17 @@ static nsCSSBorderRenderer ConstructBord
   Rect dirtyRect = NSRectToRect(aDirtyRect, oneDevPixel);
 
   StyleBorderStyle borderStyles[4];
   nscolor borderColors[4];
 
   // pull out styles, colors
   NS_FOR_CSS_SIDES(i) {
     borderStyles[i] = aStyleBorder.GetBorderStyle(i);
-    borderColors[i] = aStyleBorder.BorderColorFor(i).CalcColor(aComputedStyle);
+    borderColors[i] = aStyleBorder.BorderColorFor(i).CalcColor(*aComputedStyle);
   }
 
   PrintAsFormatString(
       " borderStyles: %d %d %d %d\n", static_cast<int>(borderStyles[0]),
       static_cast<int>(borderStyles[1]), static_cast<int>(borderStyles[2]),
       static_cast<int>(borderStyles[3]));
 
   Document* document = nullptr;
@@ -2285,47 +2285,57 @@ static void DrawBackgroundColor(nsCSSRen
 
   RefPtr<Path> roundedRect =
       MakePathForRoundedRect(*drawTarget, bgAreaGfx, aClipState.mClippedRadii);
   aCtx->SetPath(roundedRect);
   aCtx->Fill();
   aCtx->Restore();
 }
 
+enum class ScrollbarColorKind {
+  Thumb,
+  Track,
+};
+
 static Maybe<nscolor> CalcScrollbarColor(nsIFrame* aFrame,
-                                         StyleComplexColor nsStyleUI::*aColor) {
+                                         ScrollbarColorKind aKind) {
   ComputedStyle* scrollbarStyle = nsLayoutUtils::StyleForScrollbar(aFrame);
-  auto color = scrollbarStyle->StyleUI()->*aColor;
-  if (color.IsAuto()) {
+  const auto& colors = scrollbarStyle->StyleUI()->mScrollbarColor;
+  if (colors.IsAuto()) {
     return Nothing();
   }
-  return Some(color.CalcColor(scrollbarStyle));
+  const auto& color = aKind == ScrollbarColorKind::Thumb
+                          ? colors.AsColors().thumb
+                          : colors.AsColors().track;
+  return Some(color.CalcColor(*scrollbarStyle));
 }
 
 static nscolor GetBackgroundColor(nsIFrame* aFrame,
                                   ComputedStyle* aComputedStyle) {
-  Maybe<nscolor> overrideColor = Nothing();
   switch (aComputedStyle->StyleDisplay()->mAppearance) {
     case StyleAppearance::ScrollbarthumbVertical:
-    case StyleAppearance::ScrollbarthumbHorizontal:
-      overrideColor =
-          CalcScrollbarColor(aFrame, &nsStyleUI::mScrollbarFaceColor);
+    case StyleAppearance::ScrollbarthumbHorizontal: {
+      if (Maybe<nscolor> overrideColor =
+              CalcScrollbarColor(aFrame, ScrollbarColorKind::Thumb)) {
+        return *overrideColor;
+      }
       break;
+    }
     case StyleAppearance::ScrollbarVertical:
     case StyleAppearance::ScrollbarHorizontal:
-    case StyleAppearance::Scrollcorner:
-      overrideColor =
-          CalcScrollbarColor(aFrame, &nsStyleUI::mScrollbarTrackColor);
+    case StyleAppearance::Scrollcorner: {
+      if (Maybe<nscolor> overrideColor =
+              CalcScrollbarColor(aFrame, ScrollbarColorKind::Track)) {
+        return *overrideColor;
+      }
       break;
+    }
     default:
       break;
   }
-  if (overrideColor.isSome()) {
-    return *overrideColor;
-  }
   return aComputedStyle->GetVisitedDependentColor(
       &nsStyleBackground::mBackgroundColor);
 }
 
 nscolor nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext,
                                                  ComputedStyle* aComputedStyle,
                                                  nsIFrame* aFrame,
                                                  bool& aDrawBackgroundImage,
--- a/layout/painting/nsCSSRenderingGradients.cpp
+++ b/layout/painting/nsCSSRenderingGradients.cpp
@@ -576,31 +576,31 @@ static nsTArray<ColorStop> ComputeColorS
     } else {
       // Other stops with no specified position get their position assigned
       // later by interpolation, see below.
       // Remember where the run of stops with no specified position starts,
       // if it starts here.
       if (firstUnsetPosition < 0) {
         firstUnsetPosition = i;
       }
-      auto stopColor = stop.mColor.CalcColor(aComputedStyle);
+      auto stopColor = stop.mColor.CalcColor(*aComputedStyle);
       stops.AppendElement(
           ColorStop(0, stop.mIsInterpolationHint, Color::FromABGR(stopColor)));
       continue;
     }
 
     if (i > 0) {
       // Prevent decreasing stop positions by advancing this position
       // to the previous stop position, if necessary
       double previousPosition = firstUnsetPosition > 0
                                     ? stops[firstUnsetPosition - 1].mPosition
                                     : stops[i - 1].mPosition;
       position = std::max(position, previousPosition);
     }
-    auto stopColor = stop.mColor.CalcColor(aComputedStyle);
+    auto stopColor = stop.mColor.CalcColor(*aComputedStyle);
     stops.AppendElement(ColorStop(position, stop.mIsInterpolationHint,
                                   Color::FromABGR(stopColor)));
     if (firstUnsetPosition > 0) {
       // Interpolate positions for all stops that didn't have a specified
       // position
       double p = stops[firstUnsetPosition - 1].mPosition;
       double d = (stops[i].mPosition - p) / (i - firstUnsetPosition + 1);
       for (uint32_t j = firstUnsetPosition; j < i; ++j) {
--- a/layout/style/ComputedStyle.cpp
+++ b/layout/style/ComputedStyle.cpp
@@ -272,43 +272,54 @@ static nscolor GetVisitedDependentColorI
   if (ComputedStyle* visitedStyle = aSc->GetStyleIfVisited()) {
     colors[1] = aColorFunc(visitedStyle);
     return ComputedStyle::CombineVisitedColors(colors,
                                                aSc->RelevantLinkVisited());
   }
   return colors[0];
 }
 
-static nscolor ExtractColor(ComputedStyle* aStyle, const nscolor& aColor) {
+static nscolor ExtractColor(const ComputedStyle& aStyle,
+                            const nscolor& aColor) {
   return aColor;
 }
 
-static nscolor ExtractColor(ComputedStyle* aStyle,
+static nscolor ExtractColor(const ComputedStyle& aStyle,
                             const StyleComplexColor& aColor) {
   return aColor.CalcColor(aStyle);
 }
 
-static nscolor ExtractColor(ComputedStyle* aStyle,
+// Currently caret-color, the only property in the list which is a ColorOrAuto,
+// always maps auto to currentcolor.
+static nscolor ExtractColor(const ComputedStyle& aStyle,
+                            const StyleColorOrAuto& aColor) {
+  if (aColor.IsAuto()) {
+    return ExtractColor(aStyle, StyleColor::CurrentColor());
+  }
+  return ExtractColor(aStyle, aColor.AsColor());
+}
+
+static nscolor ExtractColor(ComputedStyle& aStyle,
                             const nsStyleSVGPaint& aPaintServer) {
   return aPaintServer.Type() == eStyleSVGPaintType_Color
-             ? aPaintServer.GetColor(aStyle)
+             ? aPaintServer.GetColor(&aStyle)
              : NS_RGBA(0, 0, 0, 0);
 }
 
 #define STYLE_FIELD(struct_, field_) aField == &struct_::field_ ||
 #define STYLE_STRUCT(name_, fields_)                                           \
   template <>                                                                  \
   nscolor ComputedStyle::GetVisitedDependentColor(                             \
       decltype(nsStyle##name_::MOZ_ARG_1 fields_) nsStyle##name_::*aField) {   \
     MOZ_ASSERT(MOZ_FOR_EACH(STYLE_FIELD, (nsStyle##name_, ), fields_) false,   \
                "Getting visited-dependent color for a field in nsStyle" #name_ \
                " which is not listed in nsCSSVisitedDependentPropList.h");     \
     return GetVisitedDependentColorInternal(                                   \
         this, [aField](ComputedStyle* sc) {                                    \
-          return ExtractColor(sc, sc->Style##name_()->*aField);                \
+          return ExtractColor(*sc, sc->Style##name_()->*aField);               \
         });                                                                    \
   }
 #include "nsCSSVisitedDependentPropList.h"
 #undef STYLE_STRUCT
 #undef STYLE_FIELD
 
 struct ColorIndexSet {
   uint8_t colorIndex, alphaIndex;
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -90,17 +90,16 @@ rusty-enums = [
     "mozilla::StyleShapeRadius",
     "mozilla::StyleWindowDragging",
     "mozilla::StyleAnimationPlayState",
     "mozilla::StyleOrient",
     "mozilla::StyleBoxSizing",
     "mozilla::StyleClear",
     "mozilla::StyleColumnFill",
     "mozilla::StyleColumnSpan",
-    "mozilla::StyleComplexColor_Tag",
     "mozilla::StyleFloat",
     "mozilla::StyleImageOrientation",
     "mozilla::StyleUserModify",
     "mozilla::StyleUserInput",
     "mozilla::StyleBoxDirection",
     "mozilla::StyleTextJustify",
     "mozilla::StyleHyphens",
     "mozilla::StyleShapeSourceType",
@@ -454,16 +453,20 @@ cbindgen-types = [
     { gecko = "StyleWillChangeBits", servo = "values::specified::box_::WillChangeBits" },
     { gecko = "StyleTextDecorationLine", servo = "values::computed::TextDecorationLine" },
     { gecko = "StyleMozListReversed", servo = "values::computed::MozListReversed" },
     { gecko = "StyleOwned", servo = "gecko_bindings::sugar::ownership::Owned" },
     { gecko = "StyleOwnedOrNull", servo = "gecko_bindings::sugar::ownership::OwnedOrNull" },
     { gecko = "StyleStrong", servo = "gecko_bindings::sugar::ownership::Strong" },
     { gecko = "StyleGenericFontFamily", servo = "values::computed::font::GenericFontFamily" },
     { gecko = "StyleFontFamilyNameSyntax", servo = "values::computed::font::FontFamilyNameSyntax" },
+    { gecko = "StyleGenericColor", servo = "values::generics::color::Color" },
+    { gecko = "StyleGenericColorOrAuto", servo = "values::generics::color::ColorOrAuto" },
+    { gecko = "StyleGenericScrollbarColor", servo = "values::generics::ui::ScrollbarColor" },
+    { gecko = "StyleRGBA", servo = "cssparser::RGBA" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
--- a/layout/style/ServoStyleConstsForwards.h
+++ b/layout/style/ServoStyleConstsForwards.h
@@ -25,16 +25,17 @@
 #  include "mozilla/ServoBindingTypes.h"
 #  include "nsCSSPropertyID.h"
 #  include "nsCompatibility.h"
 
 struct RawServoAnimationValueTable;
 struct RawServoAnimationValueMap;
 
 class nsAtom;
+class nsIFrame;
 class nsINode;
 class nsCSSPropertyIDSet;
 class nsPresContext;
 class nsSimpleContentList;
 struct nsCSSValueSharedList;
 struct nsTimingFunction;
 
 class gfxFontFeatureValueSet;
@@ -53,16 +54,18 @@ namespace nsStyleTransformMatrix {
 enum class MatrixTransformOperator : uint8_t;
 }
 
 template <typename T>
 class nsMainThreadPtrHolder;
 
 namespace mozilla {
 
+class ComputedStyle;
+
 using Matrix4x4Components = float[16];
 using StyleMatrix4x4Components = Matrix4x4Components;
 
 struct Keyframe;
 struct PropertyStyleAnimationValuePair;
 
 using ComputedKeyframeValues = nsTArray<PropertyStyleAnimationValuePair>;
 
--- a/layout/style/StyleComplexColor.cpp
+++ b/layout/style/StyleComplexColor.cpp
@@ -41,47 +41,52 @@ static nscolor LinearBlendColors(nscolor
   }
 
   auto r = ClampColor((p1 * r1 + p2 * r2) / a);
   auto g = ClampColor((p1 * g1 + p2 * g2) / a);
   auto b = ClampColor((p1 * b1 + p2 * b2) / a);
   return NS_RGBA(r, g, b, NSToIntRound(a * 255));
 }
 
+template<>
 bool StyleComplexColor::MaybeTransparent() const {
   // We know that the color is opaque when it's a numeric color with
   // alpha == 255.
   // TODO(djg): Should we extend this to check Complex with bgRatio =
   // 0, and fgRatio * alpha >= 255?
-  return mTag != eNumeric || NS_GET_A(mColor) != 255;
+  return !IsNumeric() || AsNumeric().alpha != 255;
+}
+
+static nscolor RGBAToNSColor(const StyleRGBA& aRGBA) {
+  return NS_RGBA(aRGBA.red, aRGBA.green, aRGBA.blue, aRGBA.alpha);
 }
 
+template<>
 nscolor StyleComplexColor::CalcColor(nscolor aForegroundColor) const {
-  switch (mTag) {
-    case eNumeric:
-      return mColor;
-    case eForeground:
-    case eAuto:
-      return aForegroundColor;
-    case eComplex:
-      return LinearBlendColors(mColor, mBgRatio, aForegroundColor, mFgRatio);
-    default:
-      MOZ_ASSERT_UNREACHABLE("StyleComplexColor has invalid mTag");
-      return mColor;
+  if (IsNumeric()) {
+    return RGBAToNSColor(AsNumeric());
   }
+  if (IsCurrentColor()) {
+    return aForegroundColor;
+  }
+  MOZ_ASSERT(IsComplex());
+  const auto& complex = AsComplex();
+  return LinearBlendColors(RGBAToNSColor(complex.color), complex.ratios.bg,
+                           aForegroundColor, complex.ratios.fg);
 }
 
-nscolor StyleComplexColor::CalcColor(mozilla::ComputedStyle* aStyle) const {
+template<>
+nscolor StyleComplexColor::CalcColor(const ComputedStyle& aStyle) const {
   // Common case that is numeric color, which is pure background, we
   // can skip resolving StyleColor().
   // TODO(djg): Is this optimization worth it?
-  if (mTag == eNumeric) {
-    return mColor;
+  if (IsNumeric()) {
+    return RGBAToNSColor(AsNumeric());
   }
 
-  MOZ_ASSERT(aStyle);
-  auto fgColor = aStyle->StyleColor()->mColor;
+  auto fgColor = aStyle.StyleColor()->mColor;
   return CalcColor(fgColor);
 }
 
+template<>
 nscolor StyleComplexColor::CalcColor(const nsIFrame* aFrame) const {
-  return CalcColor(aFrame->Style());
+  return CalcColor(*aFrame->Style());
 }
--- a/layout/style/StyleComplexColor.h
+++ b/layout/style/StyleComplexColor.h
@@ -5,130 +5,47 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* represent a color combines a numeric color and currentcolor */
 
 #ifndef mozilla_StyleComplexColor_h_
 #define mozilla_StyleComplexColor_h_
 
 #include "nsColor.h"
-
-class nsIFrame;
+#include "mozilla/ServoStyleConsts.h"
 
 namespace mozilla {
 
-class ComputedStyle;
-
-/**
- * This struct represents a combined color from a numeric color and
- * the current foreground color (currentcolor keyword).
- * Conceptually, the formula is "color * q + currentcolor * p"
- * where p is mFgRatio and q is mBgRatio.
- *
- * It can also represent an "auto" value, which is valid for some
- * properties. See comment of `Tag::eAuto`.
- */
-class StyleComplexColor final {
- public:
-  static StyleComplexColor FromColor(nscolor aColor) {
-    return {aColor, 0, eNumeric};
-  }
-  static StyleComplexColor CurrentColor() {
-    return {NS_RGBA(0, 0, 0, 0), 1, eForeground};
-  }
-  static StyleComplexColor Auto() { return {NS_RGBA(0, 0, 0, 0), 1, eAuto}; }
+using StyleComplexColor = StyleColor;
 
-  static StyleComplexColor Black() {
-    return StyleComplexColor::FromColor(NS_RGB(0, 0, 0));
-  }
-  static StyleComplexColor White() {
-    return StyleComplexColor::FromColor(NS_RGB(255, 255, 255));
-  }
-  static StyleComplexColor Transparent() {
-    return StyleComplexColor::FromColor(NS_RGBA(0, 0, 0, 0));
-  }
-
-  bool IsAuto() const { return mTag == eAuto; }
-  bool IsCurrentColor() const { return mTag == eForeground; }
+template<>
+inline StyleColor StyleColor::FromColor(nscolor aColor) {
+  return StyleColor::Numeric({NS_GET_R(aColor), NS_GET_G(aColor),
+                              NS_GET_B(aColor), NS_GET_A(aColor)});
+}
 
-  bool operator==(const StyleComplexColor& aOther) const {
-    if (mTag != aOther.mTag) {
-      return false;
-    }
-
-    switch (mTag) {
-      case eAuto:
-      case eForeground:
-        return true;
-      case eNumeric:
-        return mColor == aOther.mColor;
-      case eComplex:
-        return (mBgRatio == aOther.mBgRatio && mFgRatio == aOther.mFgRatio &&
-                mColor == aOther.mColor);
-      default:
-        MOZ_ASSERT_UNREACHABLE("Unexpected StyleComplexColor type.");
-        return false;
-    }
-  }
+template<>
+inline StyleColor StyleColor::Black() {
+  return FromColor(NS_RGB(0, 0, 0));
+}
 
-  bool operator!=(const StyleComplexColor& aOther) const {
-    return !(*this == aOther);
-  }
-
-  /**
-   * Is it possible that this StyleComplexColor is transparent?
-   */
-  bool MaybeTransparent() const;
-
-  /**
-   * Compute the color for this StyleComplexColor, taking into account
-   * the foreground color, aForegroundColor.
-   */
-  nscolor CalcColor(nscolor aForegroundColor) const;
-
-  /**
-   * Compute the color for this StyleComplexColor, taking into account
-   * the foreground color from aStyle.
-   */
-  nscolor CalcColor(mozilla::ComputedStyle* aStyle) const;
-
-  /**
-   * Compute the color for this StyleComplexColor, taking into account
-   * the foreground color from aFrame's ComputedStyle.
-   */
-  nscolor CalcColor(const nsIFrame* aFrame) const;
+template<>
+inline StyleColor StyleColor::White() {
+  return FromColor(NS_RGB(255, 255, 255));
+}
 
- private:
-  enum Tag : uint8_t {
-    // This represents a computed-value time auto value. This
-    // indicates that this value should not be interpolatable with
-    // other colors. Other fields represent a currentcolor and
-    // properties can decide whether that should be used.
-    eAuto,
-    // This represents a numeric color; no currentcolor component.
-    eNumeric,
-    // This represents the current foreground color, currentcolor; no
-    // numeric color component.
-    eForeground,
-    // This represents a linear combination of numeric color and the
-    // foreground color: "mColor * mBgRatio + currentcolor *
-    // mFgRatio".
-    eComplex,
-  };
+template<>
+inline StyleColor StyleColor::Transparent() {
+  return FromColor(NS_RGBA(0, 0, 0, 0));
+}
 
-  StyleComplexColor(nscolor aColor, float aFgRatio, Tag aTag)
-      : mColor(aColor),
-        mBgRatio(1.f - aFgRatio),
-        mFgRatio(aFgRatio),
-        mTag(aTag) {
-    MOZ_ASSERT(mTag != eNumeric || aFgRatio == 0.);
-    MOZ_ASSERT(!(mTag == eAuto || mTag == eForeground) || aFgRatio == 1.);
-  }
+template<>
+nscolor StyleComplexColor::CalcColor(nscolor aForegroundColor) const;
 
-  nscolor mColor;
-  float mBgRatio;
-  float mFgRatio;
-  Tag mTag;
-};
+template<>
+nscolor StyleComplexColor::CalcColor(const ComputedStyle&) const;
+
+template<>
+nscolor StyleComplexColor::CalcColor(const nsIFrame*) const;
 
 }  // namespace mozilla
 
 #endif  // mozilla_StyleComplexColor_h_
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1037,17 +1037,17 @@ void nsComputedDOMStyle::SetToRGBAColor(
                                         nscolor aColor) {
   nsAutoString string;
   nsStyleUtil::GetSerializedColorValue(aColor, string);
   aValue->SetString(string);
 }
 
 void nsComputedDOMStyle::SetValueFromComplexColor(
     nsROCSSPrimitiveValue* aValue, const StyleComplexColor& aColor) {
-  SetToRGBAColor(aValue, aColor.CalcColor(mComputedStyle));
+  SetToRGBAColor(aValue, aColor.CalcColor(*mComputedStyle));
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetColor() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   SetToRGBAColor(val, StyleColor()->mColor);
   return val.forget();
 }
 
@@ -1853,34 +1853,31 @@ already_AddRefed<CSSValue> nsComputedDOM
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetScrollSnapPointsY() {
   return GetScrollSnapPoints(StyleDisplay()->mScrollSnapPointsY);
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetScrollbarColor() {
   const nsStyleUI* ui = StyleUI();
-  MOZ_ASSERT(
-      ui->mScrollbarFaceColor.IsAuto() == ui->mScrollbarTrackColor.IsAuto(),
-      "Whether the two colors are auto should be identical");
-
-  if (ui->mScrollbarFaceColor.IsAuto()) {
+  if (ui->mScrollbarColor.IsAuto()) {
     RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
     val->SetIdent(eCSSKeyword_auto);
     return val.forget();
   }
 
   RefPtr<nsDOMCSSValueList> list = GetROCSSValueList(false);
   auto put = [this, &list](const StyleComplexColor& color) {
     RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
     SetValueFromComplexColor(val, color);
     list->AppendCSSValue(val.forget());
   };
-  put(ui->mScrollbarFaceColor);
-  put(ui->mScrollbarTrackColor);
+  auto& colors = ui->mScrollbarColor.AsColors();
+  put(colors.thumb);
+  put(colors.track);
   return list.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetOutlineWidth() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetAppUnits(StyleOutline()->GetOutlineWidth());
   return val.forget();
 }
@@ -2148,17 +2145,21 @@ already_AddRefed<CSSValue> nsComputedDOM
   return val.forget();
 }
 
 static_assert(NS_STYLE_UNICODE_BIDI_NORMAL == 0,
               "unicode-bidi style constants not as expected");
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetCaretColor() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  SetValueFromComplexColor(val, StyleUI()->mCaretColor);
+  if (StyleUI()->mCaretColor.IsAuto()) {
+    SetToRGBAColor(val, StyleColor()->mColor);
+  } else {
+    SetValueFromComplexColor(val, StyleUI()->mCaretColor.AsColor());
+  }
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBoxFlex() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetNumber(StyleXUL()->mBoxFlex);
   return val.forget();
 }
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2841,19 +2841,18 @@ bool nsStyleBackground::HasFixedBackgrou
   }
   return false;
 }
 
 nscolor nsStyleBackground::BackgroundColor(const nsIFrame* aFrame) const {
   return mBackgroundColor.CalcColor(aFrame);
 }
 
-nscolor nsStyleBackground::BackgroundColor(
-    mozilla::ComputedStyle* aStyle) const {
-  return mBackgroundColor.CalcColor(aStyle);
+nscolor nsStyleBackground::BackgroundColor(ComputedStyle* aStyle) const {
+  return mBackgroundColor.CalcColor(*aStyle);
 }
 
 bool nsStyleBackground::IsTransparent(const nsIFrame* aFrame) const {
   return IsTransparent(aFrame->Style());
 }
 
 bool nsStyleBackground::IsTransparent(mozilla::ComputedStyle* aStyle) const {
   return BottomLayer().mImage.IsEmpty() && mImage.mImageCount == 1 &&
@@ -3913,32 +3912,30 @@ bool nsCursorImage::operator==(const nsC
 }
 
 nsStyleUI::nsStyleUI(const Document& aDocument)
     : mUserInput(StyleUserInput::Auto),
       mUserModify(StyleUserModify::ReadOnly),
       mUserFocus(StyleUserFocus::None),
       mPointerEvents(NS_STYLE_POINTER_EVENTS_AUTO),
       mCursor(StyleCursorKind::Auto),
-      mCaretColor(StyleComplexColor::Auto()),
-      mScrollbarFaceColor(StyleComplexColor::Auto()),
-      mScrollbarTrackColor(StyleComplexColor::Auto()) {
+      mCaretColor(StyleColorOrAuto::Auto()),
+      mScrollbarColor(StyleScrollbarColor::Auto()) {
   MOZ_COUNT_CTOR(nsStyleUI);
 }
 
 nsStyleUI::nsStyleUI(const nsStyleUI& aSource)
     : mUserInput(aSource.mUserInput),
       mUserModify(aSource.mUserModify),
       mUserFocus(aSource.mUserFocus),
       mPointerEvents(aSource.mPointerEvents),
       mCursor(aSource.mCursor),
       mCursorImages(aSource.mCursorImages),
       mCaretColor(aSource.mCaretColor),
-      mScrollbarFaceColor(aSource.mScrollbarFaceColor),
-      mScrollbarTrackColor(aSource.mScrollbarTrackColor) {
+      mScrollbarColor(aSource.mScrollbarColor) {
   MOZ_COUNT_CTOR(nsStyleUI);
 }
 
 nsStyleUI::~nsStyleUI() { MOZ_COUNT_DTOR(nsStyleUI); }
 
 void nsStyleUI::TriggerImageLoads(Document& aDocument,
                                   const nsStyleUI* aOldStyle) {
   MOZ_ASSERT(NS_IsMainThread());
@@ -3990,18 +3987,17 @@ nsChangeHint nsStyleUI::CalcDifference(c
     }
   }
 
   if (mUserFocus != aNewData.mUserFocus) {
     hint |= nsChangeHint_NeutralChange;
   }
 
   if (mCaretColor != aNewData.mCaretColor ||
-      mScrollbarFaceColor != aNewData.mScrollbarFaceColor ||
-      mScrollbarTrackColor != aNewData.mScrollbarTrackColor) {
+      mScrollbarColor != aNewData.mScrollbarColor) {
     hint |= nsChangeHint_RepaintFrame;
   }
 
   return hint;
 }
 
 //-----------------------
 // nsStyleUIReset
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2559,26 +2559,23 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   mozilla::StyleUserInput mUserInput;
   mozilla::StyleUserModify mUserModify;  // (modify-content)
   mozilla::StyleUserFocus mUserFocus;    // (auto-select)
   uint8_t mPointerEvents;                // NS_STYLE_POINTER_EVENTS_*
 
   mozilla::StyleCursorKind mCursor;
   nsTArray<nsCursorImage> mCursorImages;  // images and coords
-  mozilla::StyleComplexColor mCaretColor;
-
-  mozilla::StyleComplexColor mScrollbarFaceColor;
-  mozilla::StyleComplexColor mScrollbarTrackColor;
+
+  mozilla::StyleColorOrAuto mCaretColor;
+  mozilla::StyleScrollbarColor mScrollbarColor;
 
   inline uint8_t GetEffectivePointerEvents(nsIFrame* aFrame) const;
 
-  bool HasCustomScrollbars() const {
-    return !mScrollbarFaceColor.IsAuto() || !mScrollbarTrackColor.IsAuto();
-  }
+  bool HasCustomScrollbars() const { return !mScrollbarColor.IsAuto(); }
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleXUL {
   explicit nsStyleXUL(const mozilla::dom::Document&);
   nsStyleXUL(const nsStyleXUL& aSource);
   ~nsStyleXUL();
   void TriggerImageLoads(mozilla::dom::Document&, const nsStyleXUL*) {}
   const static bool kHasTriggerImageLoads = false;
@@ -2680,31 +2677,31 @@ class nsStyleSVGPaint {
                        mozilla::StyleComplexColor aFallbackColor);
   void SetContextValue(nsStyleSVGPaintType aType) {
     SetContextValue(aType, eStyleSVGFallbackType_NotSet,
                     mozilla::StyleComplexColor::Black());
   }
 
   nscolor GetColor(mozilla::ComputedStyle* aComputedStyle) const {
     MOZ_ASSERT(mType == eStyleSVGPaintType_Color);
-    return mPaint.mColor.CalcColor(aComputedStyle);
+    return mPaint.mColor.CalcColor(*aComputedStyle);
   }
 
   mozilla::css::URLValue* GetPaintServer() const {
     MOZ_ASSERT(mType == eStyleSVGPaintType_Server);
     return mPaint.mPaintServer;
   }
 
   nsStyleSVGFallbackType GetFallbackType() const { return mFallbackType; }
 
   nscolor GetFallbackColor(mozilla::ComputedStyle* aComputedStyle) const {
     MOZ_ASSERT(mType == eStyleSVGPaintType_Server ||
                mType == eStyleSVGPaintType_ContextFill ||
                mType == eStyleSVGPaintType_ContextStroke);
-    return mFallbackColor.CalcColor(aComputedStyle);
+    return mFallbackColor.CalcColor(*aComputedStyle);
   }
 
   bool operator==(const nsStyleSVGPaint& aOther) const;
   bool operator!=(const nsStyleSVGPaint& aOther) const {
     return !(*this == aOther);
   }
 
  private:
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -388,17 +388,17 @@ void nsTextBoxFrame::DrawText(gfxContext
     const nsStyleTextReset* styleText = context->StyleTextReset();
 
     if (decorMask &
         styleText->mTextDecorationLine) {  // a decoration defined here
       nscolor color;
       if (aOverrideColor) {
         color = *aOverrideColor;
       } else {
-        color = styleText->mTextDecorationColor.CalcColor(context);
+        color = styleText->mTextDecorationColor.CalcColor(*context);
       }
       uint8_t style = styleText->mTextDecorationStyle;
 
       if (StyleTextDecorationLine_UNDERLINE & decorMask &
           styleText->mTextDecorationLine) {
         underColor = color;
         underStyle = style;
         // TODO(emilio): Could add `operator~` or `remove()` to cbindgen.
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -2992,17 +2992,17 @@ ImgDrawResult nsTreeBodyFrame::PaintCell
                     twistyContext);
 
       nsMargin twistyMargin;
       twistyContext->StyleMargin()->GetMargin(twistyMargin);
       twistyRect.Inflate(twistyMargin);
 
       const nsStyleBorder* borderStyle = lineContext->StyleBorder();
       // Resolve currentcolor values against the treeline context
-      nscolor color = borderStyle->mBorderLeftColor.CalcColor(lineContext);
+      nscolor color = borderStyle->mBorderLeftColor.CalcColor(*lineContext);
       ColorPattern colorPatt(ToDeviceColor(color));
 
       StyleBorderStyle style = borderStyle->GetBorderStyle(eSideLeft);
       StrokeOptions strokeOptions;
       nsLayoutUtils::InitDashPattern(strokeOptions, style);
 
       nscoord srcX = currX + twistyRect.width - mIndentation / 2;
       nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight + aPt.y;
--- a/servo/components/style/gecko_bindings/sugar/mod.rs
+++ b/servo/components/style/gecko_bindings/sugar/mod.rs
@@ -10,9 +10,8 @@ mod ns_css_shadow_array;
 mod ns_css_shadow_item;
 pub mod ns_css_value;
 mod ns_style_auto_array;
 pub mod ns_style_coord;
 mod ns_t_array;
 pub mod origin_flags;
 pub mod ownership;
 pub mod refptr;
-mod style_complex_color;
deleted file mode 100644
--- a/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-/* 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 to interact with Gecko's StyleComplexColor.
-
-use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
-use crate::gecko_bindings::structs::StyleComplexColor;
-use crate::gecko_bindings::structs::StyleComplexColor_Tag as Tag;
-use crate::values::computed::{Color as ComputedColor, ColorOrAuto, RGBAColor as ComputedRGBA};
-use crate::values::generics::color::{
-    Color as GenericColor, ColorOrAuto as GenericColorOrAuto, ComplexColorRatios,
-};
-
-impl StyleComplexColor {
-    /// Create a `StyleComplexColor` value that represents `currentColor`.
-    pub fn current_color() -> Self {
-        StyleComplexColor {
-            mColor: 0,
-            mBgRatio: 0.,
-            mFgRatio: 1.,
-            mTag: Tag::eForeground,
-        }
-    }
-
-    /// Create a `StyleComplexColor` value that represents `auto`.
-    pub fn auto() -> Self {
-        StyleComplexColor {
-            mColor: 0,
-            mBgRatio: 0.,
-            mFgRatio: 1.,
-            mTag: Tag::eAuto,
-        }
-    }
-}
-
-impl From<ComputedRGBA> for StyleComplexColor {
-    fn from(other: ComputedRGBA) -> Self {
-        StyleComplexColor {
-            mColor: convert_rgba_to_nscolor(&other),
-            mBgRatio: 1.,
-            mFgRatio: 0.,
-            mTag: Tag::eNumeric,
-        }
-    }
-}
-
-impl From<ComputedColor> for StyleComplexColor {
-    fn from(other: ComputedColor) -> Self {
-        match other {
-            GenericColor::Numeric(color) => color.into(),
-            GenericColor::CurrentColor => Self::current_color(),
-            GenericColor::Complex { color, ratios } => {
-                debug_assert!(ratios != ComplexColorRatios::NUMERIC);
-                debug_assert!(ratios != ComplexColorRatios::FOREGROUND);
-                StyleComplexColor {
-                    mColor: convert_rgba_to_nscolor(&color).into(),
-                    mBgRatio: ratios.bg,
-                    mFgRatio: ratios.fg,
-                    mTag: Tag::eComplex,
-                }
-            },
-        }
-    }
-}
-
-impl From<StyleComplexColor> for ComputedColor {
-    fn from(other: StyleComplexColor) -> Self {
-        match other.mTag {
-            Tag::eNumeric => {
-                debug_assert!(other.mBgRatio == 1. && other.mFgRatio == 0.);
-                GenericColor::Numeric(convert_nscolor_to_rgba(other.mColor))
-            },
-            Tag::eForeground => {
-                debug_assert!(other.mBgRatio == 0. && other.mFgRatio == 1.);
-                GenericColor::CurrentColor
-            },
-            Tag::eComplex => {
-                debug_assert!(other.mBgRatio != 1. || other.mFgRatio != 0.);
-                debug_assert!(other.mBgRatio != 0. || other.mFgRatio != 1.);
-                GenericColor::Complex {
-                    color: convert_nscolor_to_rgba(other.mColor),
-                    ratios: ComplexColorRatios {
-                        bg: other.mBgRatio,
-                        fg: other.mFgRatio,
-                    },
-                }
-            },
-            Tag::eAuto => unreachable!("Unsupport StyleComplexColor with tag eAuto"),
-        }
-    }
-}
-
-impl From<ColorOrAuto> for StyleComplexColor {
-    fn from(other: ColorOrAuto) -> Self {
-        match other {
-            GenericColorOrAuto::Color(color) => color.into(),
-            GenericColorOrAuto::Auto => StyleComplexColor::auto(),
-        }
-    }
-}
-
-impl From<StyleComplexColor> for ColorOrAuto {
-    fn from(other: StyleComplexColor) -> Self {
-        if other.mTag != Tag::eAuto {
-            GenericColorOrAuto::Color(other.into())
-        } else {
-            GenericColorOrAuto::Auto
-        }
-    }
-}
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -392,44 +392,16 @@ def set_gecko_property(ffi_name, expr):
             % if keyword.gecko_inexhaustive:
             _ => panic!("Found unexpected value in style struct for ${ident} property"),
             % endif
         }
         % endif
     }
 </%def>
 
-<%def name="impl_color_setter(ident, gecko_ffi_name)">
-    #[allow(unreachable_code)]
-    #[allow(non_snake_case)]
-    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
-        ${set_gecko_property(gecko_ffi_name, "v.into()")}
-    }
-</%def>
-
-<%def name="impl_color_copy(ident, gecko_ffi_name)">
-    #[allow(non_snake_case)]
-    pub fn copy_${ident}_from(&mut self, other: &Self) {
-        let color = ${get_gecko_property(gecko_ffi_name, self_param = "other")};
-        ${set_gecko_property(gecko_ffi_name, "color")};
-    }
-
-    #[allow(non_snake_case)]
-    pub fn reset_${ident}(&mut self, other: &Self) {
-        self.copy_${ident}_from(other)
-    }
-</%def>
-
-<%def name="impl_color_clone(ident, gecko_ffi_name)">
-    #[allow(non_snake_case)]
-    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
-        ${get_gecko_property(gecko_ffi_name)}.into()
-    }
-</%def>
-
 <%def name="impl_keyword(ident, gecko_ffi_name, keyword, cast_type='u8', **kwargs)">
 <%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)"></%call>
 <%call expr="impl_simple_copy(ident, gecko_ffi_name, **kwargs)"></%call>
 <%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"></%call>
 </%def>
 
 <%def name="impl_simple(ident, gecko_ffi_name)">
 <%call expr="impl_simple_setter(ident, gecko_ffi_name)"></%call>
@@ -444,22 +416,16 @@ def set_gecko_property(ffi_name, expr):
     }
     <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
         Au(self.gecko.${gecko_ffi_name}).into()
     }
 </%def>
 
-<%def name="impl_color(ident, gecko_ffi_name)">
-<%call expr="impl_color_setter(ident, gecko_ffi_name)"></%call>
-<%call expr="impl_color_copy(ident, gecko_ffi_name)"></%call>
-<%call expr="impl_color_clone(ident, gecko_ffi_name)"></%call>
-</%def>
-
 <%def name="impl_rgba_color(ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         ${set_gecko_property(gecko_ffi_name, "convert_rgba_to_nscolor(&v)")}
     }
     <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
@@ -1233,26 +1199,23 @@ impl Clone for ${style_struct.gecko_stru
 <%def name="impl_trait(style_struct_name, skip_longhands='')">
 <%
     style_struct = next(x for x in data.style_structs if x.name == style_struct_name)
     longhands = [x for x in style_struct.longhands
                 if not (skip_longhands == "*" or x.name in skip_longhands.split())]
 
     # Types used with predefined_type()-defined properties that we can auto-generate.
     predefined_types = {
-        "Color": impl_color,
-        "ColorOrAuto": impl_color,
         "length::LengthOrAuto": impl_style_coord,
         "length::LengthOrNormal": impl_style_coord,
         "length::NonNegativeLengthOrAuto": impl_style_coord,
         "length::NonNegativeLengthPercentageOrNormal": impl_style_coord,
         "Length": impl_absolute_length,
         "LengthOrNormal": impl_style_coord,
         "LengthPercentageOrAuto": impl_style_coord,
-        "MozListReversed": impl_simple,
         "MozScriptMinSize": impl_absolute_length,
         "RGBAColor": impl_rgba_color,
         "SVGLength": impl_svg_length,
         "SVGOpacity": impl_svg_opacity,
         "SVGPaint": impl_svg_paint,
         "SVGWidth": impl_svg_length,
         "Transform": impl_transform,
         "url::UrlOrNone": impl_css_url,
@@ -1374,17 +1337,17 @@ fn static_assert() {
         self.copy_border_${side.ident}_style_from(other);
     }
 
     #[inline]
     pub fn clone_border_${side.ident}_style(&self) -> BorderStyle {
         self.gecko.mBorderStyle[${side.index}]
     }
 
-    <% impl_color("border_%s_color" % side.ident, "mBorder%sColor" % side.name) %>
+    <% impl_simple("border_%s_color" % side.ident, "mBorder%sColor" % side.name) %>
 
     <% impl_non_negative_length("border_%s_width" % side.ident,
                                 "mComputedBorder.%s" % side.ident,
                                 inherit_from="mBorder.%s" % side.ident,
                                 round_to_pixels=True) %>
 
     pub fn border_${side.ident}_has_nonzero_width(&self) -> bool {
         self.gecko.mComputedBorder.${side.ident} != 0
@@ -4381,33 +4344,21 @@ clip-path
     }
 
     #[allow(non_snake_case)]
     pub fn reset__moz_context_properties(&mut self, other: &Self) {
         self.copy__moz_context_properties_from(other)
     }
 </%self:impl_trait>
 
-<%self:impl_trait style_struct_name="Color"
-                  skip_longhands="*">
-    pub fn set_color(&mut self, v: longhands::color::computed_value::T) {
-        let result = convert_rgba_to_nscolor(&v);
-        ${set_gecko_property("mColor", "result")}
-    }
-
-    <%call expr="impl_simple_copy('color', 'mColor')"></%call>
-
-    pub fn clone_color(&self) -> longhands::color::computed_value::T {
-        let color = ${get_gecko_property("mColor")} as u32;
-        convert_nscolor_to_rgba(color)
-    }
+<%self:impl_trait style_struct_name="Color" skip_longhands="color">
+    ${impl_rgba_color("color", "mColor")}
 </%self:impl_trait>
 
-<%self:impl_trait style_struct_name="InheritedUI"
-                  skip_longhands="cursor scrollbar-color">
+<%self:impl_trait style_struct_name="InheritedUI" skip_longhands="cursor">
     pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
         self.gecko.mCursor = v.keyword;
         unsafe {
             Gecko_SetCursorArrayLength(&mut self.gecko, v.images.len());
         }
         for i in 0..v.images.len() {
             unsafe {
                 Gecko_SetCursorImageValue(
@@ -4459,58 +4410,16 @@ clip-path
                     None
                 };
 
             CursorImage { url, hotspot }
         }).collect::<Vec<_>>().into_boxed_slice();
 
         longhands::cursor::computed_value::T { images, keyword }
     }
-
-    pub fn set_scrollbar_color(&mut self, v: longhands::scrollbar_color::computed_value::T) {
-        use crate::gecko_bindings::structs::StyleComplexColor;
-        use crate::values::generics::ui::ScrollbarColor;
-        match v {
-            ScrollbarColor::Auto => {
-                self.gecko.mScrollbarFaceColor = StyleComplexColor::auto();
-                self.gecko.mScrollbarTrackColor = StyleComplexColor::auto();
-            }
-            ScrollbarColor::Colors { thumb, track } => {
-                self.gecko.mScrollbarFaceColor = thumb.into();
-                self.gecko.mScrollbarTrackColor = track.into();
-            }
-        }
-    }
-
-    pub fn copy_scrollbar_color_from(&mut self, other: &Self) {
-        self.gecko.mScrollbarFaceColor = other.gecko.mScrollbarFaceColor;
-        self.gecko.mScrollbarTrackColor = other.gecko.mScrollbarTrackColor;
-    }
-
-    pub fn reset_scrollbar_color(&mut self, other: &Self) {
-        self.copy_scrollbar_color_from(other);
-    }
-
-    pub fn clone_scrollbar_color(&self) -> longhands::scrollbar_color::computed_value::T {
-        use crate::gecko_bindings::structs::StyleComplexColor_Tag as Tag;
-        use crate::values::generics::ui::ScrollbarColor;
-        debug_assert!(
-            (self.gecko.mScrollbarFaceColor.mTag == Tag::eAuto) ==
-            (self.gecko.mScrollbarTrackColor.mTag == Tag::eAuto),
-            "Whether the two colors are `auto` should match",
-        );
-        if self.gecko.mScrollbarFaceColor.mTag == Tag::eAuto {
-            ScrollbarColor::Auto
-        } else {
-            ScrollbarColor::Colors {
-                thumb: self.gecko.mScrollbarFaceColor.into(),
-                track: self.gecko.mScrollbarTrackColor.into(),
-            }
-        }
-    }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Column"
                   skip_longhands="column-count column-rule-width column-rule-style">
 
     #[allow(unused_unsafe)]
     pub fn set_column_count(&mut self, v: longhands::column_count::computed_value::T) {
         use crate::gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount};
--- a/servo/components/style/values/computed/ui.rs
+++ b/servo/components/style/values/computed/ui.rs
@@ -14,9 +14,9 @@ pub use crate::values::specified::ui::{M
 
 /// A computed value for the `cursor` property.
 pub type Cursor = generics::Cursor<CursorImage>;
 
 /// A computed value for item of `image cursors`.
 pub type CursorImage = generics::CursorImage<ComputedImageUrl, Number>;
 
 /// A computed value for `scrollbar-color` property.
-pub type ScrollbarColor = generics::ScrollbarColor<Color>;
+pub type ScrollbarColor = generics::GenericScrollbarColor<Color>;
--- a/servo/components/style/values/generics/ui.rs
+++ b/servo/components/style/values/generics/ui.rs
@@ -81,26 +81,29 @@ impl<ImageUrl: ToCss, Number: ToCss> ToC
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToAnimatedZero,
     ToComputedValue,
     ToCss,
     ToShmem,
 )]
-pub enum ScrollbarColor<Color> {
+#[repr(C, u8)]
+pub enum GenericScrollbarColor<Color> {
     /// `auto`
     Auto,
     /// `<color>{2}`
     Colors {
         /// First `<color>`, for color of the scrollbar thumb.
         thumb: Color,
         /// Second `<color>`, for color of the scrollbar track.
         track: Color,
     },
 }
 
+pub use self::GenericScrollbarColor as ScrollbarColor;
+
 impl<Color> Default for ScrollbarColor<Color> {
     #[inline]
     fn default() -> Self {
         ScrollbarColor::Auto
     }
 }
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -101,16 +101,17 @@ include = [
   "RestyleHint",
   "TouchAction",
   "WillChangeBits",
   "TextDecorationLine",
   "MozListReversed",
   "Owned",
   "OwnedOrNull",
   "Strong",
+  "ScrollbarColor",
   "Color",
   "ColorOrAuto",
 ]
 item_types = ["enums", "structs", "typedefs", "functions"]
 renaming_overrides_prefixing = true
 
 # Prevent some renaming for Gecko types that cbindgen doesn't otherwise understand.
 [export.rename]
@@ -277,8 +278,30 @@ renaming_overrides_prefixing = true
 
 "Strong" = """
   already_AddRefed<GeckoType> Consume() {
     already_AddRefed<GeckoType> ret(const_cast<GeckoType*>(ptr));
     ptr = nullptr;
     return ret;
   }
 """
+
+"GenericColor" = """
+  static inline StyleGenericColor FromColor(nscolor);
+  static inline StyleGenericColor Black();
+  static inline StyleGenericColor White();
+  static inline StyleGenericColor Transparent();
+  bool MaybeTransparent() const;
+  /**
+   * Compute the color for this StyleComplexColor, taking into
+   * account the foreground color from the frame's ComputedStyle.
+   */
+  nscolor CalcColor(const nsIFrame*) const;
+  /**
+   * Compute the color for this StyleComplexColor, taking into
+   * account the foreground color the style.
+   */
+  nscolor CalcColor(const ComputedStyle&) const;
+  /**
+   * Compute the color for this StyleComplexColor, making the argument the foreground color.
+   */
+  nscolor CalcColor(nscolor) const;
+"""
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -2448,19 +2448,20 @@ nsNativeThemeCocoa::ScrollbarParams nsNa
   params.horizontal = aIsHorizontal;
   params.onDarkBackground = IsDarkBackground(aFrame);
   // Don't use custom scrollbars for overlay scrollbars since they are
   // generally good enough for use cases of custom scrollbars.
   if (!params.overlay) {
     ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
     const nsStyleUI* ui = style->StyleUI();
     if (ui->HasCustomScrollbars()) {
+      const auto& colors = ui->mScrollbarColor.AsColors();
       params.custom = true;
-      params.trackColor = ui->mScrollbarTrackColor.CalcColor(style);
-      params.faceColor = ui->mScrollbarFaceColor.CalcColor(style);
+      params.trackColor = colors.track.CalcColor(*style);
+      params.faceColor = colors.thumb.CalcColor(*style);
     }
   }
   return params;
 }
 
 void nsNativeThemeCocoa::DrawScrollbarThumb(CGContextRef cgContext, const CGRect& inBoxRect,
                                             ScrollbarParams aParams) {
   CGRect drawRect = inBoxRect;
@@ -4463,18 +4464,18 @@ nsITheme::Transparency nsNativeThemeCoco
     case StyleAppearance::ScrollbarSmall:
     case StyleAppearance::Scrollbar:
     case StyleAppearance::Scrollcorner: {
       // We don't use custom scrollbars when using overlay scrollbars.
       if (nsLookAndFeel::UseOverlayScrollbars()) {
         return eTransparent;
       }
       const nsStyleUI* ui = nsLayoutUtils::StyleForScrollbar(aFrame)->StyleUI();
-      StyleComplexColor trackColor = ui->mScrollbarTrackColor;
-      if (!trackColor.IsAuto() && trackColor.MaybeTransparent()) {
+      if (!ui->mScrollbarColor.IsAuto() &&
+          ui->mScrollbarColor.AsColors().track.MaybeTransparent()) {
         return eTransparent;
       }
       return eOpaque;
     }
 
     case StyleAppearance::Statusbar:
       // Knowing that scrollbars and statusbars are opaque improves
       // performance, because we create layers for them.
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -2655,17 +2655,19 @@ nsITheme::ThemeGeometryType nsNativeThem
   }
 }
 
 nsITheme::Transparency nsNativeThemeWin::GetWidgetTransparency(
     nsIFrame* aFrame, StyleAppearance aAppearance) {
   if (IsWidgetScrollbarPart(aAppearance)) {
     ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
     if (ShouldDrawCustomScrollbar(style)) {
-      if (style->StyleUI()->mScrollbarTrackColor.MaybeTransparent()) {
+      auto* ui = style->StyleUI();
+      if (ui->mScrollbarColor.IsAuto() ||
+          ui->mScrollbarColor.AsColors().track.MaybeTransparent()) {
         return eTransparent;
       }
       // DrawCustomScrollbarPart doesn't draw the track background for
       // widgets on it, and these widgets are thinner than the track,
       // so we need to return transparent for them.
       switch (aAppearance) {
         case StyleAppearance::ScrollbarthumbHorizontal:
         case StyleAppearance::ScrollbarthumbVertical:
@@ -4165,19 +4167,20 @@ nsresult nsNativeThemeWin::DrawCustomScr
   gfxFloat p2a = gfxFloat(aFrame->PresContext()->AppUnitsPerDevPixel());
   gfxRect clipRect = ThebesRect(
       LayoutDevicePixel::FromAppUnits(aClipRect, p2a).ToUnknownRect());
   ctx->Clip(clipRect);
   gfxRect rect =
       ThebesRect(LayoutDevicePixel::FromAppUnits(aRect, p2a).ToUnknownRect());
 
   const nsStyleUI* ui = aStyle->StyleUI();
-  nscolor trackColor = ui->mScrollbarTrackColor.IsAuto()
-                           ? NS_RGB(240, 240, 240)
-                           : ui->mScrollbarTrackColor.CalcColor(aStyle);
+  auto* customColors =
+      ui->mScrollbarColor.IsAuto() ? nullptr : &ui->mScrollbarColor.AsColors();
+  nscolor trackColor = customColors ? customColors->track.CalcColor(*aStyle)
+                                    : NS_RGB(240, 240, 240);
   switch (aAppearance) {
     case StyleAppearance::ScrollbarHorizontal:
     case StyleAppearance::ScrollbarVertical:
     case StyleAppearance::Scrollcorner: {
       ctx->SetColor(Color::FromABGR(trackColor));
       ctx->Rectangle(rect);
       ctx->Fill();
       return NS_OK;
@@ -4202,19 +4205,18 @@ nsresult nsNativeThemeWin::DrawCustomScr
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown widget type");
   }
 
   switch (aAppearance) {
     case StyleAppearance::ScrollbarthumbVertical:
     case StyleAppearance::ScrollbarthumbHorizontal: {
-      nscolor faceColor = ui->mScrollbarFaceColor.IsAuto()
-                              ? NS_RGB(205, 205, 205)
-                              : ui->mScrollbarFaceColor.CalcColor(aStyle);
+      nscolor faceColor = customColors ? customColors->thumb.CalcColor(*aStyle)
+                                       : NS_RGB(205, 205, 205);
       faceColor = AdjustScrollbarFaceColor(faceColor, eventStates);
       ctx->SetColor(Color::FromABGR(faceColor));
       ctx->Rectangle(bgRect);
       ctx->Fill();
       break;
     }
     case StyleAppearance::ScrollbarbuttonUp:
     case StyleAppearance::ScrollbarbuttonDown: