style: Extend StyleComplexColor to support additive blending.

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" ... />

Bug: 1465307
Reviewed-by: hiro,xidorn
MozReview-Commit-ID: IUAK8P07gtm
This commit is contained in:
Dan Glastonbury 2018-05-23 15:23:26 +10:00 committed by Emilio Cobos Álvarez
parent 3816143a1d
commit 255fe05d40
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
5 changed files with 248 additions and 204 deletions

View file

@ -5,28 +5,21 @@
//! Rust helpers to interact with Gecko's StyleComplexColor. //! Rust helpers to interact with Gecko's StyleComplexColor.
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor}; 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::{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; use values::computed::ui::ColorOrAuto;
impl From<nscolor> for StyleComplexColor {
fn from(other: nscolor) -> Self {
StyleComplexColor {
mColor: other,
mForegroundRatio: 0,
mIsAuto: false,
}
}
}
impl StyleComplexColor { impl StyleComplexColor {
/// Create a `StyleComplexColor` value that represents `currentColor`. /// Create a `StyleComplexColor` value that represents `currentColor`.
pub fn current_color() -> Self { pub fn current_color() -> Self {
StyleComplexColor { StyleComplexColor {
mColor: 0, mColor: 0,
mForegroundRatio: 255, mBgRatio: 0.,
mIsAuto: false, mFgRatio: 1.,
mTag: Tag::eForeground,
} }
} }
@ -34,28 +27,66 @@ impl StyleComplexColor {
pub fn auto() -> Self { pub fn auto() -> Self {
StyleComplexColor { StyleComplexColor {
mColor: 0, mColor: 0,
mForegroundRatio: 255, mBgRatio: 0.,
mIsAuto: true, 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 { impl From<ComputedColor> for StyleComplexColor {
fn from(other: ComputedColor) -> Self { fn from(other: ComputedColor) -> Self {
StyleComplexColor { match other {
mColor: convert_rgba_to_nscolor(&other.color).into(), ComputedColor::Numeric(color) => color.into(),
mForegroundRatio: other.foreground_ratio, ComputedColor::Foreground => Self::current_color(),
mIsAuto: false, 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 { impl From<StyleComplexColor> for ComputedColor {
fn from(other: StyleComplexColor) -> Self { fn from(other: StyleComplexColor) -> Self {
debug_assert!(!other.mIsAuto); match other.mTag {
ComputedColor { Tag::eNumeric => {
color: convert_nscolor_to_rgba(other.mColor), debug_assert!(other.mBgRatio == 1. && other.mFgRatio == 0.);
foreground_ratio: other.mForegroundRatio, 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"),
} }
} }
} }
@ -71,7 +102,7 @@ impl From<ColorOrAuto> for StyleComplexColor {
impl From<StyleComplexColor> for ColorOrAuto { impl From<StyleComplexColor> for ColorOrAuto {
fn from(other: StyleComplexColor) -> Self { fn from(other: StyleComplexColor) -> Self {
if !other.mIsAuto { if other.mTag != Tag::eAuto {
Either::First(other.into()) Either::First(other.into())
} else { } else {
Either::Second(Auto) Either::Second(Auto)

View file

@ -6,6 +6,7 @@
use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::animated::{Animate, Procedure, ToAnimatedZero};
use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::computed::ComplexColorRatios;
/// An animated RGBA color. /// An animated RGBA color.
/// ///
@ -91,42 +92,51 @@ impl ComputeSquaredDistance for RGBA {
} }
} }
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)] #[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))] #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Color { pub enum Color {
pub color: RGBA, Numeric(RGBA),
pub foreground_ratio: f32, Foreground,
Complex(RGBA, ComplexColorRatios),
} }
impl Color { impl Color {
fn currentcolor() -> Self { fn currentcolor() -> Self {
Color { Color::Foreground
color: RGBA::transparent(),
foreground_ratio: 1.,
}
} }
/// Returns a transparent intermediate color. /// Returns a transparent intermediate color.
pub fn transparent() -> Self { pub fn transparent() -> Self {
Color { Color::Numeric(RGBA::transparent())
color: RGBA::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) -> RGBA { fn effective_intermediate_rgba(&self) -> RGBA {
RGBA { match *self {
alpha: self.color.alpha * (1. - self.foreground_ratio), Color::Numeric(color) => color,
..self.color Color::Foreground => RGBA::transparent(),
Color::Complex(color, ratios) => RGBA {
alpha: color.alpha * ratios.bg,
..color.clone()
},
}
}
fn effective_ratios(&self) -> ComplexColorRatios {
match *self {
Color::Numeric(..) => ComplexColorRatios::NUMERIC,
Color::Foreground => ComplexColorRatios::FOREGROUND,
Color::Complex(.., ratios) => ratios,
} }
} }
} }
@ -136,50 +146,66 @@ impl Animate for Color {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
// Common cases are interpolating between two numeric colors, // Common cases are interpolating between two numeric colors,
// two currentcolors, and a numeric color and a currentcolor. // 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(); let (this_weight, other_weight) = procedure.weights();
if self.foreground_ratio == other.foreground_ratio {
if self.is_currentcolor() { Ok(match (*self, *other, procedure) {
Ok(Color::currentcolor()) // Any interpolation of currentColor with currentColor returns currentColor.
} else { (Color::Foreground, Color::Foreground, Procedure::Interpolate { .. }) => {
Ok(Color { Color::currentcolor()
color: self.color.animate(&other.color, procedure)?,
foreground_ratio: self.foreground_ratio,
})
} }
} else if self.is_currentcolor() && other.is_numeric() { // Animating two numeric colors.
Ok(Color { (Color::Numeric(c1), Color::Numeric(c2), _) => {
color: other.color, Color::Numeric(c1.animate(&c2, procedure)?)
foreground_ratio: this_weight as f32, }
}) // Combinations of numeric color and currentColor
} else if self.is_numeric() && other.is_currentcolor() { (Color::Foreground, Color::Numeric(color), _) => Color::Complex(
Ok(Color { color,
color: self.color, ComplexColorRatios {
foreground_ratio: other_weight as f32, bg: other_weight as f32,
}) fg: this_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
}, },
foreground_ratio: foreground_ratio, ),
}) (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,
},
),
// 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)
}
}
})
} }
} }
@ -187,27 +213,26 @@ impl ComputeSquaredDistance for Color {
#[inline] #[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
// All comments from the Animate impl also applies here. // All comments from the Animate impl also applies here.
if self.foreground_ratio == other.foreground_ratio { Ok(match (*self, *other) {
if self.is_currentcolor() { (Color::Foreground, Color::Foreground) => SquaredDistance::from_sqrt(0.),
Ok(SquaredDistance::from_sqrt(0.)) (Color::Numeric(c1), Color::Numeric(c2)) => c1.compute_squared_distance(&c2)?,
} else { (Color::Foreground, Color::Numeric(color))
self.color.compute_squared_distance(&other.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( let self_color = self.effective_intermediate_rgba();
RGBA::transparent().compute_squared_distance(&other.color)? + let other_color = other.effective_intermediate_rgba();
SquaredDistance::from_sqrt(1.), let self_ratios = self.effective_ratios();
) let other_ratios = other.effective_ratios();
} else if self.is_numeric() && other.is_currentcolor() {
Ok(self.color.compute_squared_distance(&RGBA::transparent())? + self_color.compute_squared_distance(&other_color)?
SquaredDistance::from_sqrt(1.)) + self_ratios.bg.compute_squared_distance(&other_ratios.bg)?
} else { + self_ratios.fg.compute_squared_distance(&other_ratios.fg)?
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)?)
}
} }
} }

View file

@ -10,17 +10,36 @@ use style_traits::{CssWriter, ToCss};
use values::animated::ToAnimatedValue; use values::animated::ToAnimatedValue;
use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA}; use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
/// This struct represents a combined color from a numeric color and /// Ratios representing the contribution of color and currentcolor to
/// the current foreground color (currentcolor keyword). /// the final color value.
/// Conceptually, the formula is "color * (1 - p) + currentcolor * p" #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
/// where p is foreground_ratio. pub struct ComplexColorRatios {
#[derive(Clone, Copy, Debug, MallocSizeOf)] /// Numeric color contribution.
pub struct Color { pub bg: f32,
/// RGBA color. /// Foreground color, aka currentcolor, contribution.
pub color: RGBA, pub fg: f32,
}
/// The ratio of currentcolor in complex color. impl ComplexColorRatios {
pub foreground_ratio: u8, /// 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. /// Computed value type for the specified RGBAColor.
@ -31,11 +50,8 @@ pub type ColorPropertyValue = RGBA;
impl Color { impl Color {
/// Returns a numeric color representing the given RGBA value. /// Returns a numeric color representing the given RGBA value.
pub fn rgba(rgba: RGBA) -> Color { pub fn rgba(color: RGBA) -> Color {
Color { Color::Numeric(color)
color: rgba,
foreground_ratio: 0,
}
} }
/// Returns a complex color value representing transparent. /// Returns a complex color value representing transparent.
@ -45,73 +61,53 @@ impl Color {
/// Returns a complex color value representing currentcolor. /// Returns a complex color value representing currentcolor.
pub fn currentcolor() -> Color { pub fn currentcolor() -> Color {
Color { Color::Foreground
color: RGBA::transparent(),
foreground_ratio: u8::max_value(),
}
} }
/// Whether it is a numeric color (no currentcolor component). /// Whether it is a numeric color (no currentcolor component).
pub fn is_numeric(&self) -> bool { pub fn is_numeric(&self) -> bool {
self.foreground_ratio == 0 matches!(*self, Color::Numeric { .. })
} }
/// Whether it is a currentcolor value (no numeric color component). /// Whether it is a currentcolor value (no numeric color component).
pub fn is_currentcolor(&self) -> bool { 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 /// Combine this complex color with the given foreground color into
/// a numeric RGBA color. It currently uses linear blending. /// a numeric RGBA color. It currently uses linear blending.
pub fn to_rgba(&self, fg_color: RGBA) -> RGBA { pub fn to_rgba(&self, fg_color: RGBA) -> RGBA {
// Common cases that the complex color is either pure numeric let (color, ratios) = match *self {
// color or pure currentcolor. // Common cases that the complex color is either pure numeric
if self.is_numeric() { // color or pure currentcolor.
return self.color; Color::Numeric(color) => return color,
} Color::Foreground => return fg_color,
if self.is_currentcolor() { Color::Complex(color, ratios) => (color, ratios),
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);
}
// For the more complicated case that the alpha value differs, // For the more complicated case that the alpha value differs,
// we use the following formula to compute the components: // we use the following formula to compute the components:
// alpha = self_alpha * (1 - fg_ratio) + fg_alpha * fg_ratio // alpha = self_alpha * bg_ratio + fg_alpha * fg_ratio
// color = (self_color * self_alpha * (1 - fg_ratio) + // color = (self_color * self_alpha * bg_ratio +
// fg_color * fg_alpha * fg_ratio) / alpha // fg_color * fg_alpha * fg_ratio) / alpha
let p1 = (1. / 255.) * (255 - fg_ratio) as f32; let p1 = ratios.bg;
let a1 = self.color.alpha_f32(); let a1 = color.alpha_f32();
let r1 = a1 * self.color.red_f32(); let r1 = a1 * color.red_f32();
let g1 = a1 * self.color.green_f32(); let g1 = a1 * color.green_f32();
let b1 = a1 * self.color.blue_f32(); let b1 = a1 * color.blue_f32();
let p2 = 1. - p1; let p2 = ratios.fg;
let a2 = fg_color.alpha_f32(); let a2 = fg_color.alpha_f32();
let r2 = a2 * fg_color.red_f32(); let r2 = a2 * fg_color.red_f32();
let g2 = a2 * fg_color.green_f32(); let g2 = a2 * fg_color.green_f32();
let b2 = a2 * fg_color.blue_f32(); let b2 = a2 * fg_color.blue_f32();
let a = p1 * a1 + p2 * a2; let a = p1 * a1 + p2 * a2;
if a == 0.0 { if a <= 0. {
return RGBA::transparent(); return RGBA::transparent();
} }
let a = f32::min(a, 1.);
let inverse_a = 1. / a; let inverse_a = 1. / a;
let r = (p1 * r1 + p2 * r2) * inverse_a; let r = (p1 * r1 + p2 * r2) * inverse_a;
@ -121,19 +117,9 @@ impl Color {
} }
} }
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 { impl From<RGBA> for Color {
fn from(color: RGBA) -> Color { fn from(color: RGBA) -> Color {
Color { Color::Numeric(color)
color: color,
foreground_ratio: 0,
}
} }
} }
@ -142,12 +128,10 @@ impl ToCss for Color {
where where
W: fmt::Write, W: fmt::Write,
{ {
if self.is_numeric() { match *self {
self.color.to_css(dest) Color::Numeric(color) => color.to_css(dest),
} else if self.is_currentcolor() { Color::Foreground => CSSParserColor::CurrentColor.to_css(dest),
CSSParserColor::CurrentColor.to_css(dest) _ => Ok(()),
} else {
Ok(())
} }
} }
} }
@ -157,17 +141,23 @@ impl ToAnimatedValue for Color {
#[inline] #[inline]
fn to_animated_value(self) -> Self::AnimatedValue { fn to_animated_value(self) -> Self::AnimatedValue {
AnimatedColor { match self {
color: self.color.to_animated_value(), Color::Numeric(color) => AnimatedColor::Numeric(color.to_animated_value()),
foreground_ratio: self.foreground_ratio as f32 * (1. / 255.), Color::Foreground => AnimatedColor::Foreground,
Color::Complex(color, ratios) => {
AnimatedColor::Complex(color.to_animated_value(), ratios)
}
} }
} }
#[inline] #[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self { fn from_animated_value(animated: Self::AnimatedValue) -> Self {
Color { match animated {
color: RGBA::from_animated_value(animated.color), AnimatedColor::Numeric(color) => Color::Numeric(RGBA::from_animated_value(color)),
foreground_ratio: (animated.foreground_ratio * 255.).round() as u8, AnimatedColor::Foreground => Color::Foreground,
AnimatedColor::Complex(color, ratios) => {
Color::Complex(RGBA::from_animated_value(color), ratios)
}
} }
} }
} }

View file

@ -45,7 +45,7 @@ pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier,
pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display, TransitionProperty}; pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display, TransitionProperty};
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective};
pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange}; 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::column::ColumnCount;
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset}; pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
pub use self::effects::{BoxShadow, Filter, SimpleShadow}; pub use self::effects::{BoxShadow, Filter, SimpleShadow};

View file

@ -88,11 +88,11 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen
}; };
Ok(AngleOrNumber::Angle { degrees }) Ok(AngleOrNumber::Angle { degrees })
}, }
Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }), Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
input.parse_nested_block(|i| CalcNode::parse_angle_or_number(self.0, i)) input.parse_nested_block(|i| CalcNode::parse_angle_or_number(self.0, i))
}, }
t => return Err(location.new_unexpected_token_error(t)), t => return Err(location.new_unexpected_token_error(t)),
} }
} }
@ -119,10 +119,10 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen
Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }), Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }),
Token::Percentage { unit_value, .. } => { Token::Percentage { unit_value, .. } => {
Ok(NumberOrPercentage::Percentage { unit_value }) Ok(NumberOrPercentage::Percentage { unit_value })
}, }
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
input.parse_nested_block(|i| CalcNode::parse_number_or_percentage(self.0, i)) input.parse_nested_block(|i| CalcNode::parse_number_or_percentage(self.0, i))
}, }
t => return Err(location.new_unexpected_token_error(t)), t => return Err(location.new_unexpected_token_error(t)),
} }
} }
@ -168,10 +168,10 @@ impl Parse for Color {
Err(e.location.new_custom_error(StyleParseErrorKind::ValueError( Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
ValueParseErrorKind::InvalidColor(t), ValueParseErrorKind::InvalidColor(t),
))) )))
}, }
_ => Err(e), _ => Err(e),
} }
}, }
} }
} }
} }
@ -275,10 +275,10 @@ impl Color {
} }
return parse_hash_color(ident.as_bytes()) return parse_hash_color(ident.as_bytes())
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}, }
ref t => { ref t => {
return Err(location.new_unexpected_token_error(t.clone())); return Err(location.new_unexpected_token_error(t.clone()));
}, }
}; };
if value < 0 { if value < 0 {
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
@ -358,11 +358,11 @@ impl Color {
Keyword::MozVisitedhyperlinktext => pres_context.mVisitedLinkColor, Keyword::MozVisitedhyperlinktext => pres_context.mVisitedLinkColor,
}) })
}) })
}, }
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Color::InheritFromBodyQuirk => { Color::InheritFromBodyQuirk => {
_context.map(|context| ComputedColor::rgba(context.device().body_text_color())) _context.map(|context| ComputedColor::rgba(context.device().body_text_color()))
}, }
} }
} }
} }
@ -372,7 +372,7 @@ impl ToComputedValue for Color {
fn to_computed_value(&self, context: &Context) -> ComputedColor { fn to_computed_value(&self, context: &Context) -> ComputedColor {
let result = self.to_computed_color(Some(context)).unwrap(); 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 let Some(longhand) = context.for_non_inherited_property {
if longhand.stores_complex_colors_lossily() { if longhand.stores_complex_colors_lossily() {
context.rule_cache_conditions.borrow_mut().set_uncacheable(); context.rule_cache_conditions.borrow_mut().set_uncacheable();
@ -383,12 +383,10 @@ impl ToComputedValue for Color {
} }
fn from_computed_value(computed: &ComputedColor) -> Self { fn from_computed_value(computed: &ComputedColor) -> Self {
if computed.is_numeric() { match *computed {
Color::rgba(computed.color) ComputedColor::Numeric(color) => Color::rgba(color),
} else if computed.is_currentcolor() { ComputedColor::Foreground => Color::currentcolor(),
Color::currentcolor() ComputedColor::Complex(..) => Color::Complex(*computed),
} else {
Color::Complex(*computed)
} }
} }
} }