Bug 1465307 - P1: Extend StyleComplexColor to support additive blending. r?xidorn,hiro draft
authorDan Glastonbury <dan.glastonbury@gmail.com>
Wed, 23 May 2018 15:23:26 +1000
changeset 805075 2cd2b26ddd25d3bbe408c300e43cd437386da909
parent 805066 f7fd9b08c0156be5b5cd99de5ed0ed0b98d93051
child 805076 9a18f55ba8097a9ebf7ff9b3a550ac48442c14d5
push id112554
push userbmo:dglastonbury@mozilla.com
push dateThu, 07 Jun 2018 04:56:38 +0000
reviewersxidorn, hiro
bugs1465307
milestone62.0a1
Bug 1465307 - P1: Extend StyleComplexColor to support additive blending. r?xidorn,hiro Refactored StyleComplexColor to support "complex" blending between background (numeric) color and foreground color (currentColor). Made explicit the distinction between numeric, currentColor and a complex blend in Gecko and Stylo. This is to support SMIL animation, for example, of the form: <animate from="rgb(10,20,30)" by="currentColor" ... /> MozReview-Commit-ID: IUAK8P07gtm
layout/painting/nsCSSRendering.cpp
layout/style/ServoBindings.toml
layout/style/StyleComplexColor.cpp
layout/style/StyleComplexColor.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
servo/components/style/gecko_bindings/sugar/style_complex_color.rs
servo/components/style/values/animated/color.rs
servo/components/style/values/computed/color.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/color.rs
widget/windows/nsNativeThemeWin.cpp
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -2160,20 +2160,17 @@ IsOpaqueBorderEdge(const nsStyleBorder& 
   // 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;
 
   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 (!color.IsNumericColor()) {
-    return false;
-  }
-  return NS_GET_A(color.mColor) == 255;
+  return !color.MaybeTransparent();
 }
 
 /**
  * Returns true if all border edges are either missing or opaque.
  */
 static bool
 IsOpaqueBorder(const nsStyleBorder& aBorder)
 {
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -149,16 +149,17 @@ rusty-enums = [
     "mozilla::HalfCorner",
     "mozilla::StyleDisplay",
     "mozilla::StyleFloatEdge",
     "mozilla::StyleShapeRadius",
     "mozilla::StyleWindowDragging",
     "mozilla::StyleOrient",
     "mozilla::StyleBoxSizing",
     "mozilla::StyleClear",
+    "mozilla::StyleComplexColor_Tag",
     "mozilla::StyleFloat",
     "mozilla::StyleUserModify",
     "mozilla::StyleUserInput",
     "mozilla::StyleBoxDirection",
     "mozilla::StyleTextJustify",
     "mozilla::StyleHyphens",
     "mozilla::StyleFillRule",
     "mozilla::StyleShapeSourceType",
--- a/layout/style/StyleComplexColor.cpp
+++ b/layout/style/StyleComplexColor.cpp
@@ -8,70 +8,74 @@
 
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/ComputedStyleInlines.h"
 #include "nsIFrame.h"
 #include "nsStyleStruct.h"
 
 using namespace mozilla;
 
-static uint32_t
-BlendColorComponent(uint32_t aBg, uint32_t aFg, uint32_t aFgAlpha)
-{
-  return RoundingDivideBy255(aBg * (255 - aFgAlpha) + aFg * aFgAlpha);
-}
-
-// Blend one RGBA color with another based on a given ratio.
-// It is a linear interpolation on each channel with alpha premultipled.
+// Blend one RGBA color with another based on a given ratios.
+// It is a linear combination of each channel with alpha premultipled.
 static nscolor
-LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t aFgRatio)
+LinearBlendColors(nscolor aBg, float aBgRatio, nscolor aFg, float aFgRatio)
 {
-  // Common case that either pure background or pure foreground
-  if (aFgRatio == 0) {
-    return aBg;
-  }
-  if (aFgRatio == 255) {
-    return aFg;
-  }
-  // Common case that alpha channel is equal (usually both are opaque)
-  if (NS_GET_A(aBg) == NS_GET_A(aFg)) {
-    auto r = BlendColorComponent(NS_GET_R(aBg), NS_GET_R(aFg), aFgRatio);
-    auto g = BlendColorComponent(NS_GET_G(aBg), NS_GET_G(aFg), aFgRatio);
-    auto b = BlendColorComponent(NS_GET_B(aBg), NS_GET_B(aFg), aFgRatio);
-    return NS_RGBA(r, g, b, NS_GET_A(aFg));
-  }
-
   constexpr float kFactor = 1.0f / 255.0f;
 
-  float p1 = kFactor * (255 - aFgRatio);
+  float p1 = aBgRatio;
   float a1 = kFactor * NS_GET_A(aBg);
   float r1 = a1 * NS_GET_R(aBg);
   float g1 = a1 * NS_GET_G(aBg);
   float b1 = a1 * NS_GET_B(aBg);
 
-  float p2 = 1.0f - p1;
+  float p2 = aFgRatio;
   float a2 = kFactor * NS_GET_A(aFg);
   float r2 = a2 * NS_GET_R(aFg);
   float g2 = a2 * NS_GET_G(aFg);
   float b2 = a2 * NS_GET_B(aFg);
 
   float a = p1 * a1 + p2 * a2;
-  if (a == 0.0) {
+  if (a <= 0.f) {
     return NS_RGBA(0, 0, 0, 0);
   }
 
+  if (a > 1.f) {
+    a = 1.f;
+  }
+
   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));
 }
 
+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;
+}
+
 nscolor
 StyleComplexColor::CalcColor(mozilla::ComputedStyle* aStyle) const {
+  // Common case that is numeric color, which is pure background, we
+  // can skip resolving StyleColor().
+  if (mTag == eNumeric) {
+    return mColor;
+  }
+
   MOZ_ASSERT(aStyle);
-  auto foregroundColor = aStyle->StyleColor()->mColor;
-  return LinearBlendColors(mColor, foregroundColor, mForegroundRatio);
+  auto fgColor = aStyle->StyleColor()->mColor;
+
+  if (mTag == eComplex) {
+    return LinearBlendColors(mColor, mBgRatio, fgColor, mFgRatio);
+  }
+
+  // eForeground and eAuto return the currentcolor.
+  return fgColor;
 }
 
 nscolor
 StyleComplexColor::CalcColor(const nsIFrame* aFrame) const {
   return CalcColor(aFrame->Style());
 }
--- a/layout/style/StyleComplexColor.h
+++ b/layout/style/StyleComplexColor.h
@@ -15,64 +15,111 @@ class nsIFrame;
 
 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 * (1 - p) + currentcolor * p"
- * where p is mForegroundRatio. See mozilla::LinearBlendColors for
- * the actual algorithm.
+ * 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 mIsAuto.
+ * properties. See comment of `Tag::eAuto`.
  */
-struct StyleComplexColor
+class StyleComplexColor final
 {
-  nscolor mColor;
-  uint8_t mForegroundRatio;
-  // Whether the complex color represents a computed-value time auto
-  // value. This is a flag indicating that this value should not be
-  // interpolatable with other colors. When this flag is set, other
-  // fields represent a currentcolor. Properties can decide whether
-  // that should be used.
-  bool mIsAuto;
-
+public:
   static StyleComplexColor FromColor(nscolor aColor) {
-    return {aColor, 0, false};
+    return {aColor, 0, eNumeric};
   }
   static StyleComplexColor CurrentColor() {
-    return {NS_RGBA(0, 0, 0, 0), 255, false};
+    return {NS_RGBA(0, 0, 0, 0), 1, eForeground};
   }
   static StyleComplexColor Auto() {
-    return {NS_RGBA(0, 0, 0, 0), 255, true};
+    return {NS_RGBA(0, 0, 0, 0), 1, eAuto};
   }
 
-  bool IsNumericColor() const { return mForegroundRatio == 0; }
-  bool IsCurrentColor() const { return mForegroundRatio == 255; }
+  bool IsAuto() const { return mTag == eAuto; }
+  bool IsCurrentColor() const { return mTag == eForeground; }
 
   bool operator==(const StyleComplexColor& aOther) const {
-    return mForegroundRatio == aOther.mForegroundRatio &&
-           (IsCurrentColor() || mColor == aOther.mColor) &&
-           mIsAuto == aOther.mIsAuto;
+    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;
+    }
   }
+
   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 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;
+
+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,
+  };
+
+  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.);
+  }
+
+  nscolor mColor;
+  float mBgRatio;
+  float mFgRatio;
+  Tag mTag;
 };
 
-}
+} // namespace mozilla
 
 #endif // mozilla_StyleComplexColor_h_
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1180,17 +1180,17 @@ nsComputedDOMStyle::SetValueFromComplexC
   SetToRGBAColor(aValue, aColor.CalcColor(mComputedStyle));
 }
 
 void
 nsComputedDOMStyle::SetValueForWidgetColor(nsROCSSPrimitiveValue* aValue,
                                            const StyleComplexColor& aColor,
                                            uint8_t aWidgetType)
 {
-  if (!aColor.mIsAuto) {
+  if (!aColor.IsAuto()) {
     SetToRGBAColor(aValue, aColor.CalcColor(mComputedStyle));
     return;
   }
   nsPresContext* presContext = mPresShell->GetPresContext();
   MOZ_ASSERT(presContext);
   if (nsContentUtils::ShouldResistFingerprinting(presContext->GetDocShell())) {
     // Return transparent when resisting fingerprinting.
     SetToRGBAColor(aValue, NS_RGBA(0, 0, 0, 0));
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -4256,30 +4256,37 @@ nsStyleContent::CalcDifference(const nsS
   return nsChangeHint(0);
 }
 
 // --------------------
 // nsStyleTextReset
 //
 
 nsStyleTextReset::nsStyleTextReset(const nsPresContext* aContext)
-  : mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE)
+  : mTextOverflow()
+  , mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE)
   , mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID)
   , mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL)
   , mInitialLetterSink(0)
   , mInitialLetterSize(0.0f)
   , mTextDecorationColor(StyleComplexColor::CurrentColor())
 {
   MOZ_COUNT_CTOR(nsStyleTextReset);
 }
 
 nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource)
+  : mTextOverflow(aSource.mTextOverflow)
+  , mTextDecorationLine(aSource.mTextDecorationLine)
+  , mTextDecorationStyle(aSource.mTextDecorationStyle)
+  , mUnicodeBidi(aSource.mUnicodeBidi)
+  , mInitialLetterSink(aSource.mInitialLetterSink)
+  , mInitialLetterSize(aSource.mInitialLetterSize)
+  , mTextDecorationColor(aSource.mTextDecorationColor)
 {
   MOZ_COUNT_CTOR(nsStyleTextReset);
-  *this = aSource;
 }
 
 nsStyleTextReset::~nsStyleTextReset()
 {
   MOZ_COUNT_DTOR(nsStyleTextReset);
 }
 
 nsChangeHint
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2856,17 +2856,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   mozilla::StyleComplexColor mScrollbarFaceColor;   // [inherited]
   mozilla::StyleComplexColor mScrollbarTrackColor;  // [inherited]
 
   inline uint8_t GetEffectivePointerEvents(nsIFrame* aFrame) const;
 
   bool HasCustomScrollbars() const
   {
-    return !mScrollbarFaceColor.mIsAuto || !mScrollbarTrackColor.mIsAuto;
+    return !mScrollbarFaceColor.IsAuto() || !mScrollbarTrackColor.IsAuto();
   }
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleXUL
 {
   explicit nsStyleXUL(const nsPresContext* aContext);
   nsStyleXUL(const nsStyleXUL& aSource);
   ~nsStyleXUL();
--- a/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
+++ b/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
@@ -1,80 +1,111 @@
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 //! Rust helpers to interact with Gecko's StyleComplexColor.
 
 use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
-use gecko_bindings::structs::{nscolor, StyleComplexColor};
+use gecko_bindings::structs::StyleComplexColor;
+use gecko_bindings::structs::StyleComplexColor_Tag as Tag;
 use values::{Auto, Either};
-use values::computed::Color as ComputedColor;
+use values::computed::{Color as ComputedColor, RGBAColor as ComputedRGBA};
+use values::computed::ComplexColorRatios;
 use values::computed::ui::ColorOrAuto;
 
-impl From<nscolor> for StyleComplexColor {
-    fn from(other: nscolor) -> Self {
-        StyleComplexColor {
-            mColor: other,
-            mForegroundRatio: 0,
-            mIsAuto: false,
-        }
-    }
-}
-
 impl StyleComplexColor {
     /// Create a `StyleComplexColor` value that represents `currentColor`.
     pub fn current_color() -> Self {
         StyleComplexColor {
             mColor: 0,
-            mForegroundRatio: 255,
-            mIsAuto: false,
+            mBgRatio: 0.,
+            mFgRatio: 1.,
+            mTag: Tag::eForeground,
         }
     }
 
     /// Create a `StyleComplexColor` value that represents `auto`.
     pub fn auto() -> Self {
         StyleComplexColor {
             mColor: 0,
-            mForegroundRatio: 255,
-            mIsAuto: true,
+            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 {
-        StyleComplexColor {
-            mColor: convert_rgba_to_nscolor(&other.color).into(),
-            mForegroundRatio: other.foreground_ratio,
-            mIsAuto: false,
+        match other {
+            ComputedColor::Numeric(color) => color.into(),
+            ComputedColor::Foreground => Self::current_color(),
+            ComputedColor::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 {
-        debug_assert!(!other.mIsAuto);
-        ComputedColor {
-            color: convert_nscolor_to_rgba(other.mColor),
-            foreground_ratio: other.mForegroundRatio,
+        match other.mTag {
+            Tag::eNumeric => {
+                debug_assert!(other.mBgRatio == 1. && other.mFgRatio == 0.);
+                ComputedColor::Numeric(convert_nscolor_to_rgba(other.mColor))
+            }
+            Tag::eForeground => {
+                debug_assert!(other.mBgRatio == 0. && other.mFgRatio == 1.);
+                ComputedColor::Foreground
+            }
+            Tag::eComplex => {
+                debug_assert!(other.mBgRatio != 1. || other.mFgRatio != 0.);
+                debug_assert!(other.mBgRatio != 0. || other.mFgRatio != 1.);
+                ComputedColor::Complex(
+                    convert_nscolor_to_rgba(other.mColor),
+                    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 {
             Either::First(color) => color.into(),
             Either::Second(_) => StyleComplexColor::auto(),
         }
     }
 }
 
 impl From<StyleComplexColor> for ColorOrAuto {
     fn from(other: StyleComplexColor) -> Self {
-        if !other.mIsAuto {
+        if other.mTag != Tag::eAuto {
             Either::First(other.into())
         } else {
             Either::Second(Auto)
         }
     }
 }
--- a/servo/components/style/values/animated/color.rs
+++ b/servo/components/style/values/animated/color.rs
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Animated types for CSS colors.
 
 use values::animated::{Animate, Procedure, ToAnimatedZero};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
+use values::computed::ComplexColorRatios;
 
 /// An animated RGBA color.
 ///
 /// Unlike in computed values, each component value may exceed the
 /// range `[0.0, 1.0]`.
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 #[derive(Clone, Copy, Debug, PartialEq, ToAnimatedZero)]
 pub struct RGBA {
@@ -86,133 +87,157 @@ impl ComputeSquaredDistance for RGBA {
         start
             .iter()
             .zip(&end)
             .map(|(this, other)| this.compute_squared_distance(other))
             .sum()
     }
 }
 
+impl Animate for ComplexColorRatios {
+    #[inline]
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        let bg = self.bg.animate(&other.bg, procedure)?;
+        let fg = self.fg.animate(&other.fg, procedure)?;
+
+        Ok(ComplexColorRatios { bg, fg })
+    }
+}
+
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 #[derive(Clone, Copy, Debug, PartialEq)]
-pub struct Color {
-    pub color: RGBA,
-    pub foreground_ratio: f32,
+pub enum Color {
+    Numeric(RGBA),
+    Foreground,
+    Complex(RGBA, ComplexColorRatios),
 }
 
 impl Color {
     fn currentcolor() -> Self {
-        Color {
-            color: RGBA::transparent(),
-            foreground_ratio: 1.,
-        }
+        Color::Foreground
     }
 
     /// Returns a transparent intermediate color.
     pub fn transparent() -> Self {
-        Color {
-            color: RGBA::transparent(),
-            foreground_ratio: 0.,
+        Color::Numeric(RGBA::transparent())
+    }
+
+    fn effective_intermediate_rgba(&self) -> RGBA {
+        match *self {
+            Color::Numeric(color) => color,
+            Color::Foreground => RGBA::transparent(),
+            Color::Complex(color, ratios) => RGBA {
+                alpha: color.alpha * ratios.bg,
+                ..color.clone()
+            },
         }
     }
 
-    fn is_currentcolor(&self) -> bool {
-        self.foreground_ratio >= 1.
-    }
-
-    fn is_numeric(&self) -> bool {
-        self.foreground_ratio <= 0.
-    }
-
-    fn effective_intermediate_rgba(&self) -> RGBA {
-        RGBA {
-            alpha: self.color.alpha * (1. - self.foreground_ratio),
-            ..self.color
+    fn effective_ratios(&self) -> ComplexColorRatios {
+        match *self {
+            Color::Numeric(..) => ComplexColorRatios::NUMERIC,
+            Color::Foreground => ComplexColorRatios::FOREGROUND,
+            Color::Complex(.., ratios) => ratios,
         }
     }
 }
 
 impl Animate for Color {
     #[inline]
     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         // Common cases are interpolating between two numeric colors,
         // two currentcolors, and a numeric color and a currentcolor.
-        //
-        // Note: this algorithm assumes self_portion + other_portion
-        // equals to one, so it may be broken for additive operation.
-        // To properly support additive color interpolation, we would
-        // need two ratio fields in computed color types.
         let (this_weight, other_weight) = procedure.weights();
-        if self.foreground_ratio == other.foreground_ratio {
-            if self.is_currentcolor() {
-                Ok(Color::currentcolor())
-            } else {
-                Ok(Color {
-                    color: self.color.animate(&other.color, procedure)?,
-                    foreground_ratio: self.foreground_ratio,
-                })
+
+        Ok(match (*self, *other, procedure) {
+            // Any interpolation of currentColor with currentColor returns currentColor.
+            (Color::Foreground, Color::Foreground, Procedure::Interpolate { .. }) => {
+                Color::currentcolor()
+            }
+            // Animating two numeric colors.
+            (Color::Numeric(c1), Color::Numeric(c2), _) => {
+                Color::Numeric(c1.animate(&c2, procedure)?)
             }
-        } else if self.is_currentcolor() && other.is_numeric() {
-            Ok(Color {
-                color: other.color,
-                foreground_ratio: this_weight as f32,
-            })
-        } else if self.is_numeric() && other.is_currentcolor() {
-            Ok(Color {
-                color: self.color,
-                foreground_ratio: other_weight as f32,
-            })
-        } else {
-            // For interpolating between two complex colors, we need to
-            // generate colors with effective alpha value.
-            let self_color = self.effective_intermediate_rgba();
-            let other_color = other.effective_intermediate_rgba();
-            let color = self_color.animate(&other_color, procedure)?;
-            // Then we compute the final foreground ratio, and derive
-            // the final alpha value from the effective alpha value.
-            let foreground_ratio = self.foreground_ratio
-                .animate(&other.foreground_ratio, procedure)?;
-            let alpha = color.alpha / (1. - foreground_ratio);
-            Ok(Color {
-                color: RGBA {
-                    alpha: alpha,
-                    ..color
+            // Combinations of numeric color and currentColor
+            (Color::Foreground, Color::Numeric(color), _) => Color::Complex(
+                color,
+                ComplexColorRatios {
+                    bg: other_weight as f32,
+                    fg: this_weight as f32,
+                },
+            ),
+            (Color::Numeric(color), Color::Foreground, _) => Color::Complex(
+                color,
+                ComplexColorRatios {
+                    bg: this_weight as f32,
+                    fg: other_weight as f32,
+                },
+            ),
+
+            // Any other animation of currentColor with currentColor is complex.
+            (Color::Foreground, Color::Foreground, _) => Color::Complex(
+                RGBA::transparent(),
+                ComplexColorRatios {
+                    bg: 0.,
+                    fg: (this_weight + other_weight) as f32,
                 },
-                foreground_ratio: foreground_ratio,
-            })
-        }
+            ),
+
+            // Defer to complex calculations
+            _ => {
+                // For interpolating between two complex colors, we need to
+                // generate colors with effective alpha value.
+                let self_color = self.effective_intermediate_rgba();
+                let other_color = other.effective_intermediate_rgba();
+                let color = self_color.animate(&other_color, procedure)?;
+                // Then we compute the final background ratio, and derive
+                // the final alpha value from the effective alpha value.
+                let self_ratios = self.effective_ratios();
+                let other_ratios = other.effective_ratios();
+                let ratios = self_ratios.animate(&other_ratios, procedure)?;
+                let alpha = color.alpha / ratios.bg;
+                let color = RGBA { alpha, ..color };
+
+                if ratios == ComplexColorRatios::NUMERIC {
+                    Color::Numeric(color)
+                } else if ratios == ComplexColorRatios::FOREGROUND {
+                    Color::Foreground
+                } else {
+                    Color::Complex(color, ratios)
+                }
+            }
+        })
     }
 }
 
 impl ComputeSquaredDistance for Color {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         // All comments from the Animate impl also applies here.
-        if self.foreground_ratio == other.foreground_ratio {
-            if self.is_currentcolor() {
-                Ok(SquaredDistance::from_sqrt(0.))
-            } else {
-                self.color.compute_squared_distance(&other.color)
+        Ok(match (*self, *other) {
+            (Color::Foreground, Color::Foreground) => SquaredDistance::from_sqrt(0.),
+            (Color::Numeric(c1), Color::Numeric(c2)) => c1.compute_squared_distance(&c2)?,
+            (Color::Foreground, Color::Numeric(color))
+            | (Color::Numeric(color), Color::Foreground) => {
+                // `computed_squared_distance` is symmetic.
+                color.compute_squared_distance(&RGBA::transparent())?
+                    + SquaredDistance::from_sqrt(1.)
             }
-        } else if self.is_currentcolor() && other.is_numeric() {
-            Ok(
-                RGBA::transparent().compute_squared_distance(&other.color)? +
-                    SquaredDistance::from_sqrt(1.),
-            )
-        } else if self.is_numeric() && other.is_currentcolor() {
-            Ok(self.color.compute_squared_distance(&RGBA::transparent())? +
-                SquaredDistance::from_sqrt(1.))
-        } else {
-            let self_color = self.effective_intermediate_rgba();
-            let other_color = other.effective_intermediate_rgba();
-            Ok(self_color.compute_squared_distance(&other_color)? +
-                self.foreground_ratio
-                    .compute_squared_distance(&other.foreground_ratio)?)
-        }
+            (_, _) => {
+                let self_color = self.effective_intermediate_rgba();
+                let other_color = other.effective_intermediate_rgba();
+                let self_ratios = self.effective_ratios();
+                let other_ratios = other.effective_ratios();
+
+                self_color.compute_squared_distance(&other_color)?
+                    + self_ratios.bg.compute_squared_distance(&other_ratios.bg)?
+                    + self_ratios.fg.compute_squared_distance(&other_ratios.fg)?
+            }
+        })
     }
 }
 
 impl ToAnimatedZero for Color {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         // FIXME(nox): This does not look correct to me.
         Err(())
--- a/servo/components/style/values/computed/color.rs
+++ b/servo/components/style/values/computed/color.rs
@@ -5,174 +5,164 @@
 //! Computed color values.
 
 use cssparser::{Color as CSSParserColor, RGBA};
 use std::fmt;
 use style_traits::{CssWriter, ToCss};
 use values::animated::ToAnimatedValue;
 use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
 
-/// This struct represents a combined color from a numeric color and
-/// the current foreground color (currentcolor keyword).
-/// Conceptually, the formula is "color * (1 - p) + currentcolor * p"
-/// where p is foreground_ratio.
-#[derive(Clone, Copy, Debug, MallocSizeOf)]
-pub struct Color {
-    /// RGBA color.
-    pub color: RGBA,
+/// Ratios representing the contribution of color and currentcolor to
+/// the final color value.
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
+pub struct ComplexColorRatios {
+    /// Numeric color contribution.
+    pub bg: f32,
+    /// Foreground color, aka currentcolor, contribution.
+    pub fg: f32,
+}
 
-    /// The ratio of currentcolor in complex color.
-    pub foreground_ratio: u8,
+impl ComplexColorRatios {
+    /// Ratios representing pure numeric color.
+    pub const NUMERIC: ComplexColorRatios = ComplexColorRatios { bg: 1., fg: 0. };
+    /// Ratios representing pure foreground color.
+    pub const FOREGROUND: ComplexColorRatios = ComplexColorRatios { bg: 0., fg: 1. };
+}
+
+/// This enum represents a combined color from a numeric color and
+/// the current foreground color (currentColor keyword).
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
+pub enum Color {
+    ///  Numeric RGBA color.
+    Numeric(RGBA),
+
+    /// The current foreground color.
+    Foreground,
+
+    /// A linear combination of numeric color and currentColor.
+    /// The formula is: `color * bg_ratio + currentColor * fg_ratio`.
+    Complex(RGBA, ComplexColorRatios),
 }
 
 /// Computed value type for the specified RGBAColor.
 pub type RGBAColor = RGBA;
 
 /// The computed value of the `color` property.
 pub type ColorPropertyValue = RGBA;
 
 impl Color {
     /// Returns a numeric color representing the given RGBA value.
-    pub fn rgba(rgba: RGBA) -> Color {
-        Color {
-            color: rgba,
-            foreground_ratio: 0,
-        }
+    pub fn rgba(color: RGBA) -> Color {
+        Color::Numeric(color)
     }
 
     /// Returns a complex color value representing transparent.
     pub fn transparent() -> Color {
         Color::rgba(RGBA::transparent())
     }
 
     /// Returns a complex color value representing currentcolor.
     pub fn currentcolor() -> Color {
-        Color {
-            color: RGBA::transparent(),
-            foreground_ratio: u8::max_value(),
-        }
+        Color::Foreground
     }
 
     /// Whether it is a numeric color (no currentcolor component).
     pub fn is_numeric(&self) -> bool {
-        self.foreground_ratio == 0
+        matches!(*self, Color::Numeric { .. })
     }
 
     /// Whether it is a currentcolor value (no numeric color component).
     pub fn is_currentcolor(&self) -> bool {
-        self.foreground_ratio == u8::max_value()
+        matches!(*self, Color::Foreground)
     }
 
     /// Combine this complex color with the given foreground color into
     /// a numeric RGBA color. It currently uses linear blending.
     pub fn to_rgba(&self, fg_color: RGBA) -> RGBA {
-        // Common cases that the complex color is either pure numeric
-        // color or pure currentcolor.
-        if self.is_numeric() {
-            return self.color;
-        }
-        if self.is_currentcolor() {
-            return fg_color.clone();
-        }
-
-        fn blend_color_component(bg: u8, fg: u8, fg_alpha: u8) -> u8 {
-            let bg_ratio = (u8::max_value() - fg_alpha) as u32;
-            let fg_ratio = fg_alpha as u32;
-            let color = bg as u32 * bg_ratio + fg as u32 * fg_ratio;
-            // Rounding divide the number by 255
-            ((color + 127) / 255) as u8
-        }
-
-        // Common case that alpha channel is equal (usually both are opaque).
-        let fg_ratio = self.foreground_ratio;
-        if self.color.alpha == fg_color.alpha {
-            let r = blend_color_component(self.color.red, fg_color.red, fg_ratio);
-            let g = blend_color_component(self.color.green, fg_color.green, fg_ratio);
-            let b = blend_color_component(self.color.blue, fg_color.blue, fg_ratio);
-            return RGBA::new(r, g, b, fg_color.alpha);
-        }
+        let (color, ratios) = match *self {
+            // Common cases that the complex color is either pure numeric
+            // color or pure currentcolor.
+            Color::Numeric(color) => return color,
+            Color::Foreground => return fg_color,
+            Color::Complex(color, ratios) => (color, ratios),
+        };
 
         // For the more complicated case that the alpha value differs,
         // we use the following formula to compute the components:
-        // alpha = self_alpha * (1 - fg_ratio) + fg_alpha * fg_ratio
-        // color = (self_color * self_alpha * (1 - fg_ratio) +
+        // alpha = self_alpha * bg_ratio + fg_alpha * fg_ratio
+        // color = (self_color * self_alpha * bg_ratio +
         //          fg_color * fg_alpha * fg_ratio) / alpha
 
-        let p1 = (1. / 255.) * (255 - fg_ratio) as f32;
-        let a1 = self.color.alpha_f32();
-        let r1 = a1 * self.color.red_f32();
-        let g1 = a1 * self.color.green_f32();
-        let b1 = a1 * self.color.blue_f32();
+        let p1 = ratios.bg;
+        let a1 = color.alpha_f32();
+        let r1 = a1 * color.red_f32();
+        let g1 = a1 * color.green_f32();
+        let b1 = a1 * color.blue_f32();
 
-        let p2 = 1. - p1;
+        let p2 = ratios.fg;
         let a2 = fg_color.alpha_f32();
         let r2 = a2 * fg_color.red_f32();
         let g2 = a2 * fg_color.green_f32();
         let b2 = a2 * fg_color.blue_f32();
 
         let a = p1 * a1 + p2 * a2;
-        if a == 0.0 {
+        if a <= 0. {
             return RGBA::transparent();
         }
+        let a = f32::min(a, 1.);
 
         let inverse_a = 1. / a;
         let r = (p1 * r1 + p2 * r2) * inverse_a;
         let g = (p1 * g1 + p2 * g2) * inverse_a;
         let b = (p1 * b1 + p2 * b2) * inverse_a;
         return RGBA::from_floats(r, g, b, a);
     }
 }
 
-impl PartialEq for Color {
-    fn eq(&self, other: &Color) -> bool {
-        self.foreground_ratio == other.foreground_ratio &&
-            (self.is_currentcolor() || self.color == other.color)
-    }
-}
-
 impl From<RGBA> for Color {
     fn from(color: RGBA) -> Color {
-        Color {
-            color: color,
-            foreground_ratio: 0,
-        }
+        Color::Numeric(color)
     }
 }
 
 impl ToCss for Color {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: fmt::Write,
     {
-        if self.is_numeric() {
-            self.color.to_css(dest)
-        } else if self.is_currentcolor() {
-            CSSParserColor::CurrentColor.to_css(dest)
-        } else {
-            Ok(())
+        match *self {
+            Color::Numeric(color) => color.to_css(dest),
+            Color::Foreground => CSSParserColor::CurrentColor.to_css(dest),
+            _ => Ok(()),
         }
     }
 }
 
 impl ToAnimatedValue for Color {
     type AnimatedValue = AnimatedColor;
 
     #[inline]
     fn to_animated_value(self) -> Self::AnimatedValue {
-        AnimatedColor {
-            color: self.color.to_animated_value(),
-            foreground_ratio: self.foreground_ratio as f32 * (1. / 255.),
+        match self {
+            Color::Numeric(color) => AnimatedColor::Numeric(color.to_animated_value()),
+            Color::Foreground => AnimatedColor::Foreground,
+            Color::Complex(color, ratios) => {
+                AnimatedColor::Complex(color.to_animated_value(), ratios)
+            }
         }
     }
 
     #[inline]
     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
-        Color {
-            color: RGBA::from_animated_value(animated.color),
-            foreground_ratio: (animated.foreground_ratio * 255.).round() as u8,
+        match animated {
+            AnimatedColor::Numeric(color) => Color::Numeric(RGBA::from_animated_value(color)),
+            AnimatedColor::Foreground => Color::Foreground,
+            AnimatedColor::Complex(color, ratios) => {
+                Color::Complex(RGBA::from_animated_value(color), ratios)
+            }
         }
     }
 }
 
 impl ToAnimatedValue for RGBA {
     type AnimatedValue = AnimatedRGBA;
 
     #[inline]
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -40,17 +40,17 @@ pub use self::border::{BorderImageRepeat
 pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
 pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontVariantAlternates, FontWeight};
 pub use self::font::{FontFamily, FontLanguageOverride, FontStyle, FontVariantEastAsian, FontVariationSettings};
 pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
 pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
 pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display, TransitionProperty};
 pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective};
 pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange};
-pub use self::color::{Color, ColorPropertyValue, RGBAColor};
+pub use self::color::{Color, ColorPropertyValue, ComplexColorRatios, RGBAColor};
 pub use self::column::ColumnCount;
 pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 pub use self::flex::FlexBasis;
 pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
 pub use self::inherited_box::{ImageOrientation, Orientation};
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -83,21 +83,21 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::Co
                 let angle = Angle::parse_dimension(value, unit, /* from_calc = */ false);
 
                 let degrees = match angle {
                     Ok(angle) => angle.degrees(),
                     Err(()) => return Err(location.new_unexpected_token_error(token.clone())),
                 };
 
                 Ok(AngleOrNumber::Angle { degrees })
-            },
+            }
             Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
                 input.parse_nested_block(|i| CalcNode::parse_angle_or_number(self.0, i))
-            },
+            }
             t => return Err(location.new_unexpected_token_error(t)),
         }
     }
 
     fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> {
         use values::specified::Percentage;
 
         Ok(Percentage::parse(self.0, input)?.get())
@@ -114,20 +114,20 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::Co
         input: &mut Parser<'i, 't>,
     ) -> Result<NumberOrPercentage, ParseError<'i>> {
         let location = input.current_source_location();
 
         match input.next()?.clone() {
             Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }),
             Token::Percentage { unit_value, .. } => {
                 Ok(NumberOrPercentage::Percentage { unit_value })
-            },
+            }
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
                 input.parse_nested_block(|i| CalcNode::parse_number_or_percentage(self.0, i))
-            },
+            }
             t => return Err(location.new_unexpected_token_error(t)),
         }
     }
 }
 
 impl Parse for Color {
     fn parse<'i, 't>(
         context: &ParserContext,
@@ -163,20 +163,20 @@ impl Parse for Color {
                     }
                 }
 
                 match e.kind {
                     ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
                         Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
                             ValueParseErrorKind::InvalidColor(t),
                         )))
-                    },
+                    }
                     _ => Err(e),
                 }
-            },
+            }
         }
     }
 }
 
 impl ToCss for Color {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
@@ -270,20 +270,20 @@ impl Color {
                 ..
             } => (integer, Some(unit)),
             Token::Ident(ref ident) => {
                 if ident.len() != 3 && ident.len() != 6 {
                     return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
                 return parse_hash_color(ident.as_bytes())
                     .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
-            },
+            }
             ref t => {
                 return Err(location.new_unexpected_token_error(t.clone()));
-            },
+            }
         };
         if value < 0 {
             return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         let length = if value <= 9 {
             1
         } else if value <= 99 {
             2
@@ -353,47 +353,45 @@ impl Color {
                     convert_nscolor_to_computedcolor(match special {
                         Keyword::MozDefaultColor => pres_context.mDefaultColor,
                         Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
                         Keyword::MozHyperlinktext => pres_context.mLinkColor,
                         Keyword::MozActivehyperlinktext => pres_context.mActiveLinkColor,
                         Keyword::MozVisitedhyperlinktext => pres_context.mVisitedLinkColor,
                     })
                 })
-            },
+            }
             #[cfg(feature = "gecko")]
             Color::InheritFromBodyQuirk => {
                 _context.map(|context| ComputedColor::rgba(context.device().body_text_color()))
-            },
+            }
         }
     }
 }
 
 impl ToComputedValue for Color {
     type ComputedValue = ComputedColor;
 
     fn to_computed_value(&self, context: &Context) -> ComputedColor {
         let result = self.to_computed_color(Some(context)).unwrap();
-        if result.foreground_ratio != 0 {
+        if !result.is_numeric() {
             if let Some(longhand) = context.for_non_inherited_property {
                 if longhand.stores_complex_colors_lossily() {
                     context.rule_cache_conditions.borrow_mut().set_uncacheable();
                 }
             }
         }
         result
     }
 
     fn from_computed_value(computed: &ComputedColor) -> Self {
-        if computed.is_numeric() {
-            Color::rgba(computed.color)
-        } else if computed.is_currentcolor() {
-            Color::currentcolor()
-        } else {
-            Color::Complex(*computed)
+        match *computed {
+            ComputedColor::Numeric(color) => Color::rgba(color),
+            ComputedColor::Foreground => Color::currentcolor(),
+            ComputedColor::Complex(..) => Color::Complex(*computed),
         }
     }
 }
 
 /// Specified color value, but resolved to just RGBA for computed value
 /// with value from color property at the same context.
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub struct RGBAColor(pub Color);
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -4131,34 +4131,34 @@ GetOpaqueBackgroundColor(ComputedStyle* 
   return NS_ComposeColors(NS_RGB(255, 255, 255), color);
 }
 
 static nscolor
 GetScrollbarFaceColor(ComputedStyle* aStyle)
 {
   StyleComplexColor complexColor =
     aStyle->StyleUserInterface()->mScrollbarFaceColor;
-  if (complexColor.mIsAuto) {
+  if (complexColor.IsAuto()) {
     return GetScrollbarFaceColorForAuto();
   }
   nscolor color = complexColor.CalcColor(aStyle);
   if (NS_GET_A(color) == 255) {
     return color;
   }
   nscolor bgColor = GetOpaqueBackgroundColor(aStyle);
   return NS_ComposeColors(bgColor, color);
 }
 
 static nscolor
 GetScrollbarTrackColor(ComputedStyle* aStyle)
 {
   StyleComplexColor complexColor =
     aStyle->StyleUserInterface()->mScrollbarTrackColor;
   nscolor color;
-  if (complexColor.mIsAuto) {
+  if (complexColor.IsAuto()) {
     color = GetScrollbarTrackColorForAuto(aStyle);
   } else {
     color = complexColor.CalcColor(aStyle);
   }
   if (NS_GET_A(color) == 255) {
     return color;
   }
   nscolor bgColor = GetOpaqueBackgroundColor(aStyle);
@@ -4258,18 +4258,18 @@ GetScrollbarArrowColor(nscolor aTrackCol
 nsresult
 nsNativeThemeWin::DrawCustomScrollbarPart(gfxContext* aContext,
                                           nsIFrame* aFrame,
                                           ComputedStyle* aStyle,
                                           uint8_t aWidgetType,
                                           const nsRect& aRect,
                                           const nsRect& aClipRect)
 {
-  MOZ_ASSERT(!aStyle->StyleUserInterface()->mScrollbarFaceColor.mIsAuto ||
-             !aStyle->StyleUserInterface()->mScrollbarTrackColor.mIsAuto);
+  MOZ_ASSERT(!aStyle->StyleUserInterface()->mScrollbarFaceColor.IsAuto() ||
+             !aStyle->StyleUserInterface()->mScrollbarTrackColor.IsAuto());
 
   gfxRect tr(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()),
           dr(aClipRect.X(), aClipRect.Y(),
              aClipRect.Width(), aClipRect.Height());
 
   nscolor trackColor = GetScrollbarTrackColor(aStyle);
   HBRUSH dcBrush = (HBRUSH) GetStockObject(DC_BRUSH);