Add separate computed Color value. r?birtles,manishearth draft
authorXidorn Quan <me@upsuper.org>
Wed, 07 Jun 2017 14:28:51 +1000
changeset 590141 d8d2d5b4c1b718833090979631655f70ea962001
parent 590140 7e533d521ab811e5a6c61ad0c1a315bc625af030
child 590142 faaaed30efb2e5506b86f908f4b4545d1d930af5
push id62604
push userxquan@mozilla.com
push dateWed, 07 Jun 2017 07:07:54 +0000
reviewersbirtles, manishearth
milestone55.0a1
Add separate computed Color value. r?birtles,manishearth MozReview-Commit-ID: 9hRcmGkhu4d
servo/Cargo.lock
servo/components/layout/Cargo.toml
servo/components/layout/lib.rs
servo/components/layout/table_cell.rs
servo/components/layout/table_row.rs
servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
servo/components/style/gecko_bindings/sugar/style_complex_color.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/background.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/color.mako.rs
servo/components/style/properties/longhand/column.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/outline.mako.rs
servo/components/style/properties/longhand/text.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/values/computed/color.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/color.rs
servo/components/style/values/specified/mod.rs
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -1413,17 +1413,16 @@ source = "registry+https://github.com/ru
 [[package]]
 name = "layout"
 version = "0.0.1"
 dependencies = [
  "app_units 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "canvas_traits 0.0.1",
- "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx 0.0.1",
  "gfx_traits 0.0.1",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "html5ever 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/components/layout/Cargo.toml
+++ b/servo/components/layout/Cargo.toml
@@ -9,17 +9,16 @@ publish = false
 name = "layout"
 path = "lib.rs"
 
 [dependencies]
 app_units = "0.4.1"
 atomic_refcell = "0.1"
 bitflags = "0.7"
 canvas_traits = {path = "../canvas_traits"}
-cssparser = "0.13.7"
 euclid = "0.13"
 fnv = "1.0"
 gfx = {path = "../gfx"}
 gfx_traits = {path = "../gfx_traits"}
 heapsize = "0.4"
 html5ever = "0.17"
 ipc-channel = "0.7"
 libc = "0.2"
--- a/servo/components/layout/lib.rs
+++ b/servo/components/layout/lib.rs
@@ -11,17 +11,16 @@
 #![feature(step_by)]
 
 extern crate app_units;
 extern crate atomic_refcell;
 #[macro_use]
 extern crate bitflags;
 extern crate canvas_traits;
 extern crate core;
-extern crate cssparser;
 extern crate euclid;
 extern crate fnv;
 extern crate gfx;
 extern crate gfx_traits;
 extern crate heapsize;
 #[macro_use] extern crate html5ever;
 extern crate ipc_channel;
 extern crate libc;
--- a/servo/components/layout/table_cell.rs
+++ b/servo/components/layout/table_cell.rs
@@ -4,29 +4,29 @@
 
 //! CSS table formatting contexts.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
 use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
 use context::LayoutContext;
-use cssparser::Color;
 use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
 use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
 use flow::{self, Flow, FlowClass, IS_ABSOLUTELY_POSITIONED, OpaqueFlow};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::MaybeAuto;
 use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
 use std::fmt;
 use style::computed_values::{border_collapse, border_top_style, vertical_align};
 use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMode};
 use style::properties::ServoComputedValues;
+use style::values::computed::Color;
 use table::InternalTable;
 use table_row::{CollapsedBorder, CollapsedBorderProvenance};
 
 /// A table formatting context.
 #[derive(Serialize)]
 pub struct TableCellFlow {
     /// Data common to all block flows.
     pub block_flow: BlockFlow,
--- a/servo/components/layout/table_row.rs
+++ b/servo/components/layout/table_row.rs
@@ -4,34 +4,33 @@
 
 //! CSS table formatting contexts.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
 use block::{BlockFlow, ISizeAndMarginsComputer};
 use context::LayoutContext;
-use cssparser::{Color, RGBA};
 use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
 use euclid::Point2D;
 use flow::{self, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
 use flow_list::MutFlowListIterator;
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::MaybeAuto;
 use serde::{Serialize, Serializer};
 use std::cmp::max;
 use std::fmt;
 use std::iter::{Enumerate, IntoIterator, Peekable};
 use style::computed_values::{border_collapse, border_spacing, border_top_style};
 use style::logical_geometry::{LogicalSize, PhysicalSide, WritingMode};
 use style::properties::ServoComputedValues;
 use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW};
-use style::values::computed::LengthOrPercentageOrAuto;
+use style::values::computed::{Color, LengthOrPercentageOrAuto};
 use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, VecExt};
 use table_cell::{CollapsedBordersForCell, TableCellFlow};
 
 /// A single row of a table.
 pub struct TableRowFlow {
     /// Fields common to all block flows.
     pub block_flow: BlockFlow,
 
@@ -601,17 +600,17 @@ pub enum CollapsedBorderProvenance {
 }
 
 impl CollapsedBorder {
     /// Creates a collapsible border style for no border.
     pub fn new() -> CollapsedBorder {
         CollapsedBorder {
             style: border_top_style::T::none,
             width: Au(0),
-            color: Color::RGBA(RGBA::transparent()),
+            color: Color::transparent(),
             provenance: CollapsedBorderProvenance::FromTable,
         }
     }
 
     /// Creates a collapsed border from the block-start border described in the given CSS style
     /// object.
     fn top(css_style: &ServoComputedValues, provenance: CollapsedBorderProvenance)
            -> CollapsedBorder {
--- a/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
@@ -1,43 +1,42 @@
 /* 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 for Gecko's `nsCSSShadowItem`.
 
 use app_units::Au;
-use cssparser::Color;
 use gecko::values::{convert_rgba_to_nscolor, convert_nscolor_to_rgba};
 use gecko_bindings::structs::nsCSSShadowItem;
-use values::computed::Shadow;
+use values::computed::{Color, Shadow};
 
 impl nsCSSShadowItem {
     /// Set this item to the given shadow value.
     pub fn set_from_shadow(&mut self, other: Shadow) {
         self.mXOffset = other.offset_x.0;
         self.mYOffset = other.offset_y.0;
         self.mRadius = other.blur_radius.0;
         self.mSpread = other.spread_radius.0;
         self.mInset = other.inset;
-        self.mColor = match other.color {
-            Color::RGBA(rgba) => {
-                self.mHasColor = true;
-                convert_rgba_to_nscolor(&rgba)
-            },
+        if other.color.is_currentcolor() {
             // TODO handle currentColor
             // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
-            Color::CurrentColor => 0,
+            self.mHasColor = false;
+            self.mColor = 0;
+        } else {
+            self.mHasColor = true;
+            self.mColor = convert_rgba_to_nscolor(&other.color.color);
         }
     }
 
     /// Generate shadow value from this shadow item.
     pub fn to_shadow(&self) -> Shadow {
         Shadow {
             offset_x: Au(self.mXOffset),
             offset_y: Au(self.mYOffset),
             blur_radius: Au(self.mRadius),
             spread_radius: Au(self.mSpread),
             inset: self.mInset,
-            color: Color::RGBA(convert_nscolor_to_rgba(self.mColor)),
+            color: Color::rgba(convert_nscolor_to_rgba(self.mColor)),
         }
     }
 }
--- a/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
+++ b/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
@@ -1,18 +1,18 @@
 /* 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 cssparser;
 use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
 use gecko_bindings::structs::{nscolor, StyleComplexColor};
 use values;
+use values::computed::Color as ComputedColor;
 
 impl From<nscolor> for StyleComplexColor {
     fn from(other: nscolor) -> Self {
         StyleComplexColor {
             mColor: other,
             mForegroundRatio: 0,
             mIsAuto: false,
         }
@@ -34,45 +34,39 @@ impl StyleComplexColor {
         StyleComplexColor {
             mColor: 0,
             mForegroundRatio: 255,
             mIsAuto: true,
         }
     }
 }
 
-impl From<cssparser::Color> for StyleComplexColor {
-    fn from(other: cssparser::Color) -> Self {
-        use cssparser::Color;
-
-        match other {
-            Color::RGBA(rgba) => convert_rgba_to_nscolor(&rgba).into(),
-            Color::CurrentColor => StyleComplexColor::current_color(),
+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,
         }
     }
 }
 
 impl From<StyleComplexColor> for values::computed::ColorOrAuto {
     fn from(color: StyleComplexColor) -> Self {
         use values::{Auto, Either};
 
         if color.mIsAuto {
             return Either::Second(Auto);
         }
 
         Either::First(color.into())
     }
 }
 
-impl From<StyleComplexColor> for cssparser::Color {
+impl From<StyleComplexColor> for ComputedColor {
     fn from(other: StyleComplexColor) -> Self {
-        use cssparser::Color;
-
-        if other.mForegroundRatio == 0 {
-            Color::RGBA(convert_nscolor_to_rgba(other.mColor))
-        } else if other.mForegroundRatio == 255 {
-            Color::CurrentColor
-        } else {
-            // FIXME #13546 handle interpolation values
-            Color::CurrentColor
+        debug_assert!(!other.mIsAuto);
+        ComputedColor {
+            color: convert_nscolor_to_rgba(other.mColor),
+            foreground_ratio: other.mForegroundRatio,
         }
     }
 }
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -2,17 +2,17 @@
  * 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% from data import SYSTEM_FONT_LONGHANDS %>
 
 use app_units::Au;
-use cssparser::{Color as CSSParserColor, Parser, RGBA, serialize_identifier};
+use cssparser::{Parser, RGBA, serialize_identifier};
 use euclid::{Point2D, Size2D};
 #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
 #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
 #[cfg(feature = "gecko")] use gecko_string_cache::Atom;
 use properties::{CSSWideKeyword, PropertyDeclaration};
 use properties::longhands;
 use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
@@ -32,17 +32,17 @@ use std::cmp;
 #[cfg(feature = "gecko")] use std::collections::HashMap;
 use std::fmt;
 use style_traits::ToCss;
 use super::ComputedValues;
 use values::CSSFloat;
 use values::{Auto, Either};
 use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 use values::computed::{BorderCornerRadius, ClipRect};
-use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage};
+use values::computed::{CalcLengthOrPercentage, Color, Context, LengthOrPercentage};
 use values::computed::{MaxLength, MozLength, Shadow};
 use values::computed::ToComputedValue;
 use values::generics::{SVGPaint, SVGPaintKind};
 use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
 use values::generics::position as generic_position;
 
 
 /// A given transition property, that is either `All`, or an animatable
@@ -2616,113 +2616,165 @@ impl Animatable for IntermediateRGBA {
                                .fold(0.0f64, |n, (&a, &b)| {
                                    let diff = (a - b) as f64;
                                    n + diff * diff
                                });
         Ok(diff)
     }
 }
 
-impl From<Either<CSSParserColor, Auto>> for Either<IntermediateColor, Auto> {
-    fn from(from: Either<CSSParserColor, Auto>) -> Either<IntermediateColor, Auto> {
+impl From<Either<Color, Auto>> for Either<IntermediateColor, Auto> {
+    fn from(from: Either<Color, Auto>) -> Either<IntermediateColor, Auto> {
         match from {
-            Either::First(from) =>
-                match from {
-                    CSSParserColor::RGBA(color) =>
-                        Either::First(IntermediateColor::IntermediateRGBA(
-                            IntermediateRGBA::new(color.red_f32(),
-                                                  color.green_f32(),
-                                                  color.blue_f32(),
-                                                  color.alpha_f32()))),
-                    CSSParserColor::CurrentColor =>
-                        Either::First(IntermediateColor::CurrentColor),
-                },
+            Either::First(from) => Either::First(from.into()),
             Either::Second(Auto) => Either::Second(Auto),
         }
     }
 }
 
-impl From<Either<IntermediateColor, Auto>> for Either<CSSParserColor, Auto> {
-    fn from(from: Either<IntermediateColor, Auto>) -> Either<CSSParserColor, Auto> {
+impl From<Either<IntermediateColor, Auto>> for Either<Color, Auto> {
+    fn from(from: Either<IntermediateColor, Auto>) -> Either<Color, Auto> {
         match from {
-            Either::First(from) =>
-                match from {
-                    IntermediateColor::IntermediateRGBA(color) =>
-                        Either::First(CSSParserColor::RGBA(RGBA::from_floats(color.red,
-                                                                             color.green,
-                                                                             color.blue,
-                                                                             color.alpha))),
-                    IntermediateColor::CurrentColor =>
-                        Either::First(CSSParserColor::CurrentColor),
-                },
+            Either::First(from) => Either::First(from.into()),
             Either::Second(Auto) => Either::Second(Auto),
         }
     }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
-pub enum IntermediateColor {
-    CurrentColor,
-    IntermediateRGBA(IntermediateRGBA),
+pub struct IntermediateColor {
+    color: IntermediateRGBA,
+    foreground_ratio: f32,
+}
+
+impl IntermediateColor {
+    fn currentcolor() -> Self {
+        IntermediateColor {
+            color: IntermediateRGBA::transparent(),
+            foreground_ratio: 1.,
+        }
+    }
+
+    fn transparent() -> Self {
+        IntermediateColor {
+            color: IntermediateRGBA::transparent(),
+            foreground_ratio: 0.,
+        }
+    }
+
+    fn is_currentcolor(&self) -> bool {
+        self.foreground_ratio >= 1.
+    }
+
+    fn is_numeric(&self) -> bool {
+        self.foreground_ratio <= 0.
+    }
+
+    fn effective_intermediate_rgba(&self) -> IntermediateRGBA {
+        IntermediateRGBA {
+            alpha: self.color.alpha * (1. - self.foreground_ratio),
+            .. self.color
+        }
+    }
 }
 
 impl Animatable for IntermediateColor {
     #[inline]
     fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        match (*self, *other) {
-            (IntermediateColor::IntermediateRGBA(ref this),
-             IntermediateColor::IntermediateRGBA(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(IntermediateColor::IntermediateRGBA)
+        // 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.
+        if self.foreground_ratio == other.foreground_ratio {
+            if self.is_currentcolor() {
+                Ok(IntermediateColor::currentcolor())
+            } else {
+                Ok(IntermediateColor {
+                    color: self.color.add_weighted(&other.color, self_portion, other_portion)?,
+                    foreground_ratio: self.foreground_ratio,
+                })
             }
-            // FIXME: Bug 1345709: Implement currentColor animations.
-            _ => Err(()),
+        } else if self.is_currentcolor() && other.is_numeric() {
+            Ok(IntermediateColor {
+                color: other.color,
+                foreground_ratio: self_portion as f32,
+            })
+        } else if self.is_numeric() && other.is_currentcolor() {
+            Ok(IntermediateColor {
+                color: self.color,
+                foreground_ratio: other_portion 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.add_weighted(&other_color, self_portion, other_portion)?;
+            // Then we compute the final foreground ratio, and derive
+            // the final alpha value from the effective alpha value.
+            let foreground_ratio = self.foreground_ratio
+                .add_weighted(&other.foreground_ratio, self_portion, other_portion)?;
+            let alpha = color.alpha / (1. - foreground_ratio);
+            Ok(IntermediateColor {
+                color: IntermediateRGBA {
+                    alpha: alpha,
+                    .. color
+                },
+                foreground_ratio: foreground_ratio,
+            })
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sq| sq.sqrt())
     }
 
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
-        match (*self, *other) {
-            (IntermediateColor::IntermediateRGBA(ref this), IntermediateColor::IntermediateRGBA(ref other)) => {
-                this.compute_squared_distance(other)
-            },
-            _ => Ok(0.0),
+        // All comments in add_weighted also applies here.
+        if self.foreground_ratio == other.foreground_ratio {
+            if self.is_currentcolor() {
+                Ok(0.)
+            } else {
+                self.color.compute_squared_distance(&other.color)
+            }
+        } else if self.is_currentcolor() && other.is_numeric() {
+            Ok(IntermediateRGBA::transparent().compute_squared_distance(&other.color)? + 1.)
+        } else if self.is_numeric() && other.is_currentcolor() {
+            Ok(self.color.compute_squared_distance(&IntermediateRGBA::transparent())? + 1.)
+        } else {
+            let self_color = self.effective_intermediate_rgba();
+            let other_color = other.effective_intermediate_rgba();
+            let dist = self_color.compute_squared_distance(&other_color)?;
+            let ratio_diff = (self.foreground_ratio - other.foreground_ratio) as f64;
+            Ok(dist + ratio_diff * ratio_diff)
         }
     }
 }
 
-impl From<CSSParserColor> for IntermediateColor {
-    fn from(color: CSSParserColor) -> IntermediateColor {
-        match color {
-            CSSParserColor::RGBA(color) =>
-                IntermediateColor::IntermediateRGBA(IntermediateRGBA::new(color.red_f32(),
-                                                                          color.green_f32(),
-                                                                          color.blue_f32(),
-                                                                          color.alpha_f32())),
-            CSSParserColor::CurrentColor => IntermediateColor::CurrentColor,
+impl From<Color> for IntermediateColor {
+    fn from(color: Color) -> IntermediateColor {
+        IntermediateColor {
+            color: color.color.into(),
+            foreground_ratio: color.foreground_ratio as f32 * (1. / 255.),
         }
     }
 }
 
-impl From<IntermediateColor> for CSSParserColor {
-    fn from(color: IntermediateColor) -> CSSParserColor {
-        match color {
-            IntermediateColor::IntermediateRGBA(color) =>
-                CSSParserColor::RGBA(RGBA::from_floats(color.red,
-                                                       color.green,
-                                                       color.blue,
-                                                       color.alpha)),
-            IntermediateColor::CurrentColor => CSSParserColor::CurrentColor,
+impl From<IntermediateColor> for Color {
+    fn from(color: IntermediateColor) -> Color {
+        Color {
+            color: color.color.into(),
+            foreground_ratio: (color.foreground_ratio * 255.).round() as u8,
         }
     }
 }
 
 /// Animatable SVGPaint
 pub type IntermediateSVGPaint = SVGPaint<IntermediateRGBA>;
 /// Animatable SVGPaintKind
 pub type IntermediateSVGPaintKind = SVGPaintKind<IntermediateRGBA>;
@@ -2919,17 +2971,17 @@ impl Animatable for IntermediateShadowLi
     #[inline]
     fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
         // The inset value must change
         let mut zero = IntermediateShadow {
             offset_x: Au(0),
             offset_y: Au(0),
             blur_radius: Au(0),
             spread_radius: Au(0),
-            color: IntermediateColor::IntermediateRGBA(IntermediateRGBA::transparent()),
+            color: IntermediateColor::transparent(),
             inset: false,
         };
 
         let max_len = cmp::max(self.0.len(), other.0.len());
 
         let mut result = if max_len > 1 {
             SmallVec::from_vec(Vec::with_capacity(max_len))
         } else {
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -2,17 +2,17 @@
  * 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Background", inherited=False) %>
 
 ${helpers.predefined_type("background-color", "Color",
-    "::cssparser::Color::RGBA(::cssparser::RGBA::transparent())",
+    "computed_value::T::transparent()",
     initial_specified_value="SpecifiedValue::transparent()",
     spec="https://drafts.csswg.org/css-backgrounds/#background-color",
     animation_value_type="IntermediateColor",
     ignored_when_colors_disabled=True,
     allow_quirks=True)}
 
 ${helpers.predefined_type("background-image", "ImageLayer",
     initial_value="Either::First(None_)",
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -12,17 +12,17 @@
     def maybe_logical_spec(side, kind):
         if side[1]: # if it is logical
             return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
         else:
             return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind)
 %>
 % for side in ALL_SIDES:
     ${helpers.predefined_type("border-%s-color" % side[0], "Color",
-                              "::cssparser::Color::CurrentColor",
+                              "computed_value::T::currentcolor()",
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
                               spec=maybe_logical_spec(side, "color"),
                               animation_value_type="IntermediateColor",
                               logical=side[1],
                               allow_quirks=not side[1],
                               ignored_when_colors_disabled=True)}
 
     ${helpers.predefined_type("border-%s-style" % side[0], "BorderStyle",
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -7,29 +7,26 @@
 <% data.new_style_struct("Color", inherited=True) %>
 
 <% from data import to_rust_ident %>
 
 <%helpers:longhand name="color" need_clone="True"
                    animation_value_type="IntermediateRGBA"
                    ignored_when_colors_disabled="True"
                    spec="https://drafts.csswg.org/css-color/#color">
-    use cssparser::{Color as CSSParserColor, RGBA};
+    use cssparser::RGBA;
     use values::specified::{AllowQuirks, Color};
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
-            match self.0.to_computed_value(context) {
-                CSSParserColor::RGBA(rgba) => rgba,
-                CSSParserColor::CurrentColor =>
-                    context.inherited_style.get_color().clone_color(),
-            }
+            self.0.to_computed_value(context)
+                .to_rgba(context.inherited_style.get_color().clone_color())
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(Color::rgba(*computed).into())
         }
     }
 
--- a/servo/components/style/properties/longhand/column.mako.rs
+++ b/servo/components/style/properties/longhand/column.mako.rs
@@ -47,17 +47,17 @@
                           computed_type="::app_units::Au",
                           products="gecko",
                           spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
                           animation_value_type="ComputedValue",
                           extra_prefixes="moz")}
 
 // https://drafts.csswg.org/css-multicol-1/#crc
 ${helpers.predefined_type("column-rule-color", "Color",
-                          "::cssparser::Color::CurrentColor",
+                          "computed_value::T::currentcolor()",
                           initial_specified_value="specified::Color::currentcolor()",
                           products="gecko", animation_value_type="IntermediateColor", extra_prefixes="moz",
                           need_clone=True, ignored_when_colors_disabled=True,
                           spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color")}
 
 ${helpers.single_keyword("column-span", "none all",
                          products="gecko", animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-span")}
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -681,17 +681,17 @@
                 };
                 SpecifiedValue(horiz, vert)
             }
         }
     % endif
 </%helpers:longhand>
 
 ${helpers.predefined_type("text-emphasis-color", "Color",
-                          "::cssparser::Color::CurrentColor",
+                          "computed_value::T::currentcolor()",
                           initial_specified_value="specified::Color::currentcolor()",
                           products="gecko", animation_value_type="IntermediateColor",
                           need_clone=True, ignored_when_colors_disabled=True,
                           spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color")}
 
 
 ${helpers.predefined_type(
     "-moz-tab-size", "LengthOrNumber",
@@ -700,24 +700,24 @@
     products="gecko", animation_value_type="none",
     spec="https://drafts.csswg.org/css-text-3/#tab-size-property")}
 
 
 // CSS Compatibility
 // https://compat.spec.whatwg.org
 ${helpers.predefined_type(
     "-webkit-text-fill-color", "Color",
-    "CSSParserColor::CurrentColor",
+    "computed_value::T::currentcolor()",
     products="gecko", animation_value_type="IntermediateColor",
     need_clone=True, ignored_when_colors_disabled=True,
     spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color")}
 
 ${helpers.predefined_type(
     "-webkit-text-stroke-color", "Color",
-    "CSSParserColor::CurrentColor",
+    "computed_value::T::currentcolor()",
     initial_specified_value="specified::Color::currentcolor()",
     products="gecko", animation_value_type="IntermediateColor",
     need_clone=True, ignored_when_colors_disabled=True,
     spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color")}
 
 ${helpers.predefined_type("-webkit-text-stroke-width",
                           "BorderSideWidth",
                           "Au::from_px(0)",
--- a/servo/components/style/properties/longhand/outline.mako.rs
+++ b/servo/components/style/properties/longhand/outline.mako.rs
@@ -5,17 +5,17 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import Method %>
 
 <% data.new_style_struct("Outline",
                          inherited=False,
                          additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
 
 // TODO(pcwalton): `invert`
-${helpers.predefined_type("outline-color", "Color", "computed::Color::CurrentColor",
+${helpers.predefined_type("outline-color", "Color", "computed_value::T::currentcolor()",
                           initial_specified_value="specified::Color::currentcolor()",
                           animation_value_type="IntermediateColor", need_clone=True,
                           ignored_when_colors_disabled=True,
                           spec="https://drafts.csswg.org/css-ui/#propdef-outline-color")}
 
 <%helpers:longhand name="outline-style" need_clone="True" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
     use values::specified::BorderStyle;
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -274,17 +274,17 @@
 ${helpers.single_keyword("text-decoration-style",
                          "solid double dotted dashed wavy -moz-none",
                          products="gecko",
                          animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}
 
 ${helpers.predefined_type(
     "text-decoration-color", "Color",
-    "computed::Color::CurrentColor",
+    "computed_value::T::currentcolor()",
     initial_specified_value="specified::Color::currentcolor()",
     products="gecko",
     animation_value_type="IntermediateColor",
     ignored_when_colors_disabled=True,
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
 
 <%helpers:longhand name="initial-letter"
                    animation_value_type="none"
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -13,17 +13,17 @@
 use std::borrow::Cow;
 use std::collections::HashSet;
 use std::fmt;
 use std::mem;
 use std::ops::Deref;
 use stylearc::{Arc, UniqueArc};
 
 use app_units::Au;
-#[cfg(feature = "servo")] use cssparser::{Color as CSSParserColor, RGBA};
+#[cfg(feature = "servo")] use cssparser::RGBA;
 use cssparser::{Parser, TokenSerializationType, serialize_identifier};
 use error_reporting::ParseErrorReporter;
 #[cfg(feature = "servo")] use euclid::side_offsets::SideOffsets2D;
 use computed_values;
 use context::QuirksMode;
 use font_metrics::FontMetricsProvider;
 #[cfg(feature = "gecko")] use gecko_bindings::bindings;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
@@ -1947,21 +1947,18 @@ impl ComputedValues {
     /// Resolves the currentColor keyword.
     ///
     /// Any color value from computed values (except for the 'color' property
     /// itself) should go through this method.
     ///
     /// Usage example:
     /// let top_color = style.resolve_color(style.Border.border_top_color);
     #[inline]
-    pub fn resolve_color(&self, color: CSSParserColor) -> RGBA {
-        match color {
-            CSSParserColor::RGBA(rgba) => rgba,
-            CSSParserColor::CurrentColor => self.get_color().color,
-        }
+    pub fn resolve_color(&self, color: computed::Color) -> RGBA {
+        color.to_rgba(self.get_color().color)
     }
 
     /// Get the logical computed inline size.
     #[inline]
     pub fn content_inline_size(&self) -> computed::LengthOrPercentageOrAuto {
         let position_style = self.get_position();
         if self.writing_mode.is_vertical() {
             position_style.height
--- a/servo/components/style/values/computed/color.rs
+++ b/servo/components/style/values/computed/color.rs
@@ -1,10 +1,144 @@
 /* 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/. */
 
 //! Computed color values.
 
-use cssparser::RGBA;
+use cssparser::{Color as CSSParserColor, RGBA};
+use std::fmt;
+use style_traits::ToCss;
+
+/// 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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct Color {
+    /// RGBA color.
+    pub color: RGBA,
+    /// The ratio of currentcolor in complex color.
+    pub foreground_ratio: u8,
+}
+
+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
+}
+
+impl Color {
+    /// Returns a numeric color representing the given RGBA value.
+    pub fn rgba(rgba: RGBA) -> Color {
+        Color {
+            color: rgba,
+            foreground_ratio: 0,
+        }
+    }
+
+    /// 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(),
+        }
+    }
+
+    /// Whether it is a numeric color (no currentcolor component).
+    pub fn is_numeric(&self) -> bool {
+        self.foreground_ratio == 0
+    }
+
+    /// Whether it is a currentcolor value (no numeric color component).
+    pub fn is_currentcolor(&self) -> bool {
+        self.foreground_ratio == u8::max_value()
+    }
+
+    /// 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();
+        }
+        // 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);
+        }
+
+        // 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) +
+        //          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 p2 = 1. - p1;
+        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 {
+            return RGBA::transparent();
+        }
+
+        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,
+        }
+    }
+}
+
+impl ToCss for Color {
+    fn to_css<W>(&self, dest: &mut 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(())
+        }
+    }
+}
 
 /// Computed value type for the specified RGBAColor.
 pub type RGBAColor = RGBA;
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -18,21 +18,20 @@ use std::f32::consts::PI;
 use std::fmt;
 use style_traits::ToCss;
 use super::{CSSFloat, CSSInteger, RGBA};
 use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
 use super::generics::grid::TrackList as GenericTrackList;
 use super::specified;
 
 pub use app_units::Au;
-pub use cssparser::Color;
 pub use self::background::BackgroundSize;
 pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
 pub use self::border::{BorderRadius, BorderCornerRadius};
-pub use self::color::RGBAColor;
+pub use self::color::{Color, RGBAColor};
 pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect};
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
 #[cfg(feature = "gecko")]
 pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use super::specified::{BorderStyle, Percentage, UrlOrNone};
 pub use super::generics::grid::GridLine;
 pub use super::specified::url::SpecifiedUrl;
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -10,31 +10,33 @@ use gecko_bindings::structs::nscolor;
 use itoa;
 use parser::{ParserContext, Parse};
 #[cfg(feature = "gecko")]
 use properties::longhands::color::SystemColor;
 use std::fmt;
 use std::io::Write;
 use style_traits::ToCss;
 use super::AllowQuirks;
-use values::computed::{Context, ToComputedValue};
+use values::computed::{Color as ComputedColor, Context, ToComputedValue};
 
 /// Specified color value
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Color {
     /// The 'currentColor' keyword
     CurrentColor,
     /// A specific RGBA color
     Numeric {
         /// Parsed RGBA color
         parsed: RGBA,
         /// Authored representation
         authored: Option<Box<str>>,
     },
+    /// A complex color value from computed value
+    Complex(ComputedColor),
 
     /// A system color
     #[cfg(feature = "gecko")]
     System(SystemColor),
     /// A special color keyword value used in Gecko
     #[cfg(feature = "gecko")]
     Special(gecko::SpecialColorKeyword),
     /// Quirksmode-only rule for inheriting color from the body
@@ -92,16 +94,17 @@ impl Parse for Color {
 }
 
 impl ToCss for Color {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
             Color::Numeric { authored: Some(ref authored), .. } => dest.write_str(authored),
             Color::Numeric { parsed: ref rgba, .. } => rgba.to_css(dest),
+            Color::Complex(_) => Ok(()),
             #[cfg(feature = "gecko")]
             Color::System(system) => system.to_css(dest),
             #[cfg(feature = "gecko")]
             Color::Special(special) => special.to_css(dest),
             #[cfg(feature = "gecko")]
             Color::InheritFromBodyQuirk => Ok(()),
         }
     }
@@ -221,35 +224,37 @@ impl Color {
         match *self {
             Color::Numeric { ref parsed, .. } => parsed.alpha != 0,
             _ => true,
         }
     }
 }
 
 #[cfg(feature = "gecko")]
-fn to_rgba(color: nscolor) -> CSSParserColor {
+fn convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor {
     use gecko::values::convert_nscolor_to_rgba;
-    CSSParserColor::RGBA(convert_nscolor_to_rgba(color))
+    ComputedColor::rgba(convert_nscolor_to_rgba(color))
 }
 
 impl ToComputedValue for Color {
-    type ComputedValue = CSSParserColor;
+    type ComputedValue = ComputedColor;
 
-    fn to_computed_value(&self, _context: &Context) -> CSSParserColor {
+    fn to_computed_value(&self, _context: &Context) -> ComputedColor {
         match *self {
-            Color::CurrentColor => CSSParserColor::CurrentColor,
-            Color::Numeric { ref parsed, .. } => CSSParserColor::RGBA(*parsed),
+            Color::CurrentColor => ComputedColor::currentcolor(),
+            Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
+            Color::Complex(ref complex) => *complex,
             #[cfg(feature = "gecko")]
-            Color::System(system) => to_rgba(system.to_computed_value(_context)),
+            Color::System(system) =>
+                convert_nscolor_to_computedcolor(system.to_computed_value(_context)),
             #[cfg(feature = "gecko")]
             Color::Special(special) => {
                 use self::gecko::SpecialColorKeyword as Keyword;
                 let pres_context = unsafe { &*_context.device.pres_context };
-                to_rgba(match special {
+                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")]
@@ -259,31 +264,34 @@ impl ToComputedValue for Color {
                 use gecko_bindings::bindings::Gecko_GetBody;
                 let pres_context = unsafe { &*_context.device.pres_context };
                 let body = unsafe {
                     Gecko_GetBody(pres_context)
                 };
                 if let Some(body) = body {
                     let wrap = GeckoElement(body);
                     let borrow = wrap.borrow_data();
-                    CSSParserColor::RGBA(borrow.as_ref().unwrap()
-                                               .styles().primary.values()
-                                               .get_color()
-                                               .clone_color())
+                    ComputedColor::rgba(borrow.as_ref().unwrap()
+                                              .styles().primary.values()
+                                              .get_color()
+                                              .clone_color())
                 } else {
-                    to_rgba(pres_context.mDefaultColor)
+                    convert_nscolor_to_computedcolor(pres_context.mDefaultColor)
                 }
             },
         }
     }
 
-    fn from_computed_value(computed: &CSSParserColor) -> Self {
-        match *computed {
-            CSSParserColor::RGBA(rgba) => Color::rgba(rgba),
-            CSSParserColor::CurrentColor => Color::currentcolor(),
+    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)
         }
     }
 }
 
 /// Specified color value, but resolved to just RGBA for computed value
 /// with value from color property at the same context.
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -302,20 +310,18 @@ impl ToCss for RGBAColor {
         self.0.to_css(dest)
     }
 }
 
 impl ToComputedValue for RGBAColor {
     type ComputedValue = RGBA;
 
     fn to_computed_value(&self, context: &Context) -> RGBA {
-        match self.0.to_computed_value(context) {
-            CSSParserColor::RGBA(rgba) => rgba,
-            CSSParserColor::CurrentColor => context.style.get_color().clone_color(),
-        }
+        self.0.to_computed_value(context)
+            .to_rgba(context.style.get_color().clone_color())
     }
 
     fn from_computed_value(computed: &RGBA) -> Self {
         RGBAColor(Color::rgba(*computed))
     }
 }
 
 impl From<Color> for RGBAColor {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified values.
 //!
 //! TODO(emilio): Enhance docs.
 
 use Namespace;
 use context::QuirksMode;
-use cssparser::{self, Parser, Token, serialize_identifier};
+use cssparser::{Parser, Token, serialize_identifier};
 use parser::{ParserContext, Parse};
 use self::grid::TrackSizeOrRepeat;
 use self::url::SpecifiedUrl;
 use std::ascii::AsciiExt;
 use std::f32;
 use std::fmt;
 use style_traits::ToCss;
 use style_traits::values::specified::AllowedNumericType;
@@ -685,20 +685,18 @@ impl ToComputedValue for Shadow {
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         ComputedShadow {
             offset_x: self.offset_x.to_computed_value(context),
             offset_y: self.offset_y.to_computed_value(context),
             blur_radius: self.blur_radius.to_computed_value(context),
             spread_radius: self.spread_radius.to_computed_value(context),
-            color: self.color
-                        .as_ref()
-                        .map(|color| color.to_computed_value(context))
-                        .unwrap_or(cssparser::Color::CurrentColor),
+            color: self.color.as_ref().unwrap_or(&Color::CurrentColor)
+                             .to_computed_value(context),
             inset: self.inset,
         }
     }
 
     #[inline]
     fn from_computed_value(computed: &ComputedShadow) -> Self {
         Shadow {
             offset_x: ToComputedValue::from_computed_value(&computed.offset_x),