style: Refactor specified::Color parsing/computation functions. draft
authorCameron McCormack <cam@mcc.id.au>
Mon, 16 Oct 2017 08:51:55 +0800
changeset 680685 de0cc06f009d2d22289881adfbfc61997f4f1fcd
parent 680630 2ba9ba4fa63b942d8d9401f6ff6e40f5730adcd1
child 680686 dd9b35638d1f6c30b7aa05b04e6793c975a69f4c
child 680750 e6ae671737e6d726c76d05b52f93d239d09b5049
push id84585
push userbmo:cam@mcc.id.au
push dateMon, 16 Oct 2017 03:56:44 +0000
milestone58.0a1
style: Refactor specified::Color parsing/computation functions. So they can be called without a context. MozReview-Commit-ID: BZSZw1VwpYZ
servo/components/style/values/specified/color.rs
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -62,52 +62,17 @@ mod gecko {
 impl From<RGBA> for Color {
     fn from(value: RGBA) -> Self {
         Color::rgba(value)
     }
 }
 
 impl Parse for Color {
     fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
-        // Currently we only store authored value for color keywords,
-        // because all browsers serialize those values as keywords for
-        // specified value.
-        let start = input.state();
-        let authored = match input.next() {
-            Ok(&Token::Ident(ref s)) => Some(s.to_lowercase().into_boxed_str()),
-            _ => None,
-        };
-        input.reset(&start);
-        match input.try(CSSParserColor::parse) {
-            Ok(value) =>
-                Ok(match value {
-                    CSSParserColor::CurrentColor => Color::CurrentColor,
-                    CSSParserColor::RGBA(rgba) => Color::Numeric {
-                        parsed: rgba,
-                        authored: authored,
-                    },
-                }),
-            Err(e) => {
-                #[cfg(feature = "gecko")] {
-                    if let Ok(system) = input.try(SystemColor::parse) {
-                        return Ok(Color::System(system));
-                    } else if let Ok(c) = gecko::SpecialColorKeyword::parse(input) {
-                        return Ok(Color::Special(c));
-                    }
-                }
-                match e {
-                    BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location } => {
-                        Err(location.new_custom_error(
-                            StyleParseErrorKind::ValueError(ValueParseErrorKind::InvalidColor(t))
-                        ))
-                    }
-                    e => Err(e.into())
-                }
-            }
-        }
+        Color::parse_color(input)
     }
 }
 
 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),
@@ -239,70 +204,128 @@ impl Color {
     /// Returns false if the color is completely transparent, and
     /// true otherwise.
     pub fn is_non_transparent(&self) -> bool {
         match *self {
             Color::Numeric { ref parsed, .. } => parsed.alpha != 0,
             _ => true,
         }
     }
+
+    /// Parse a <color> value.
+    pub fn parse_color<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+        // Currently we only store authored value for color keywords,
+        // because all browsers serialize those values as keywords for
+        // specified value.
+        let start = input.state();
+        let authored = match input.next() {
+            Ok(&Token::Ident(ref s)) => Some(s.to_lowercase().into_boxed_str()),
+            _ => None,
+        };
+        input.reset(&start);
+        match input.try(CSSParserColor::parse) {
+            Ok(value) =>
+                Ok(match value {
+                    CSSParserColor::CurrentColor => Color::CurrentColor,
+                    CSSParserColor::RGBA(rgba) => Color::Numeric {
+                        parsed: rgba,
+                        authored: authored,
+                    },
+                }),
+            Err(e) => {
+                #[cfg(feature = "gecko")] {
+                    if let Ok(system) = input.try(SystemColor::parse) {
+                        return Ok(Color::System(system));
+                    } else if let Ok(c) = gecko::SpecialColorKeyword::parse(input) {
+                        return Ok(Color::Special(c));
+                    }
+                }
+                match e {
+                    BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location } => {
+                        Err(location.new_custom_error(
+                            StyleParseErrorKind::ValueError(ValueParseErrorKind::InvalidColor(t))
+                        ))
+                    }
+                    e => Err(e.into())
+                }
+            }
+        }
+    }
 }
 
 #[cfg(feature = "gecko")]
 fn convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor {
     use gecko::values::convert_nscolor_to_rgba;
     ComputedColor::rgba(convert_nscolor_to_rgba(color))
 }
 
+impl Color {
+    /// Converts this Color into a ComputedColor.
+    ///
+    /// If `context` is `None`, and the specified color requires data from
+    /// the context to resolve, then `None` is returned.
+    pub fn to_computed_color(
+        &self,
+        context: Option<&Context>,
+    ) -> Option<ComputedColor> {
+        match *self {
+            Color::CurrentColor => {
+                Some(ComputedColor::currentcolor())
+            }
+            Color::Numeric { ref parsed, .. } => {
+                Some(ComputedColor::rgba(*parsed))
+            }
+            Color::Complex(ref complex) => {
+                Some(*complex)
+            }
+            #[cfg(feature = "gecko")]
+            Color::System(system) => {
+                context.map(|context| {
+                    convert_nscolor_to_computedcolor(
+                        system.to_computed_value(context)
+                    )
+                })
+            }
+            #[cfg(feature = "gecko")]
+            Color::Special(special) => {
+                use self::gecko::SpecialColorKeyword as Keyword;
+                context.map(|context| {
+                    let pres_context = context.device().pres_context();
+                    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 {
-        match *self {
-            Color::CurrentColor => {
-                if let Some(longhand) = context.for_non_inherited_property {
-                    if longhand.stores_complex_colors_lossily() {
-                        context.rule_cache_conditions.borrow_mut()
-                            .set_uncacheable();
-                    }
-                }
-                ComputedColor::currentcolor()
-            }
-            Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
-            Color::Complex(ref complex) => {
-                if complex.foreground_ratio != 0 {
-                    if let Some(longhand) = context.for_non_inherited_property {
-                        if longhand.stores_complex_colors_lossily() {
-                            context.rule_cache_conditions.borrow_mut()
-                                .set_uncacheable();
-                        }
-                    }
+        let result = self.to_computed_color(Some(context)).unwrap();
+        if result.foreground_ratio != 0 {
+            if let Some(longhand) = context.for_non_inherited_property {
+                if longhand.stores_complex_colors_lossily() {
+                    context.rule_cache_conditions.borrow_mut().set_uncacheable();
                 }
-                *complex
             }
-            #[cfg(feature = "gecko")]
-            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 = context.device().pres_context();
-                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 => {
-                ComputedColor::rgba(context.device().body_text_color())
-            },
         }
+        result
     }
 
     fn from_computed_value(computed: &ComputedColor) -> Self {
         if computed.is_numeric() {
             Color::rgba(computed.color)
         } else if computed.is_currentcolor() {
             Color::currentcolor()
         } else {