1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5 //! Specified color values.
6
7 use super::AllowQuirks;
8 #[cfg(feature = "gecko")]
9 use crate::gecko_bindings::structs::nscolor;
10 use crate::parser::{Parse, ParserContext};
11 use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
12 use crate::values::generics::color::{Color as GenericColor, ColorOrAuto as GenericColorOrAuto};
13 use crate::values::specified::calc::CalcNode;
14 use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA};
15 use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind};
16 use itoa;
17 use std::fmt::{self, Write};
18 use std::io::Write as IoWrite;
19 use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
20 use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind};
21
22 /// Specified color value
23 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
24 pub enum Color {
25 /// The 'currentColor' keyword
26 CurrentColor,
27 /// A specific RGBA color
28 Numeric {
29 /// Parsed RGBA color
30 parsed: RGBA,
31 /// Authored representation
32 authored: Option<Box<str>>,
33 },
34 /// A complex color value from computed value
35 Complex(ComputedColor),
36 /// A system color
37 #[cfg(feature = "gecko")]
38 System(SystemColor),
39 /// Quirksmode-only rule for inheriting color from the body
40 #[cfg(feature = "gecko")]
41 InheritFromBodyQuirk,
42 }
43
44 /// System colors.
45 #[allow(missing_docs)]
46 #[cfg(feature = "gecko")]
47 #[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
48 #[repr(u8)]
49 pub enum SystemColor {
50 #[css(skip)]
51 WindowBackground,
52 #[css(skip)]
53 WindowForeground,
54 #[css(skip)]
55 WidgetBackground,
56 #[css(skip)]
57 WidgetForeground,
58 #[css(skip)]
59 WidgetSelectBackground,
60 #[css(skip)]
61 WidgetSelectForeground,
62 #[css(skip)]
63 Widget3DHighlight,
64 #[css(skip)]
65 Widget3DShadow,
66 #[css(skip)]
67 TextBackground,
68 #[css(skip)]
69 TextForeground,
70 #[css(skip)]
71 TextSelectBackground,
72 #[css(skip)]
73 TextSelectForeground,
74 #[css(skip)]
75 TextSelectForegroundCustom,
76 #[css(skip)]
77 TextSelectBackgroundDisabled,
78 #[css(skip)]
79 TextSelectBackgroundAttention,
80 #[css(skip)]
81 TextHighlightBackground,
82 #[css(skip)]
83 TextHighlightForeground,
84 #[css(skip)]
85 IMERawInputBackground,
86 #[css(skip)]
87 IMERawInputForeground,
88 #[css(skip)]
89 IMERawInputUnderline,
90 #[css(skip)]
91 IMESelectedRawTextBackground,
92 #[css(skip)]
93 IMESelectedRawTextForeground,
94 #[css(skip)]
95 IMESelectedRawTextUnderline,
96 #[css(skip)]
97 IMEConvertedTextBackground,
98 #[css(skip)]
99 IMEConvertedTextForeground,
100 #[css(skip)]
101 IMEConvertedTextUnderline,
102 #[css(skip)]
103 IMESelectedConvertedTextBackground,
104 #[css(skip)]
105 IMESelectedConvertedTextForeground,
106 #[css(skip)]
107 IMESelectedConvertedTextUnderline,
108 #[css(skip)]
109 SpellCheckerUnderline,
110 Activeborder,
111 Activecaption,
112 Appworkspace,
113 Background,
114 Buttonface,
115 Buttonhighlight,
116 Buttonshadow,
117 Buttontext,
118 Captiontext,
119 #[parse(aliases = "-moz-field")]
120 Field,
121 #[parse(aliases = "-moz-fieldtext")]
122 Fieldtext,
123 Graytext,
124 Highlight,
125 Highlighttext,
126 Inactiveborder,
127 Inactivecaption,
128 Inactivecaptiontext,
129 Infobackground,
130 Infotext,
131 Menu,
132 Menutext,
133 Scrollbar,
134 Threeddarkshadow,
135 Threedface,
136 Threedhighlight,
137 Threedlightshadow,
138 Threedshadow,
139 Window,
140 Windowframe,
141 Windowtext,
142 MozButtondefault,
143 #[parse(aliases = "-moz-default-color")]
144 Canvastext,
145 #[parse(aliases = "-moz-default-background-color")]
146 Canvas,
147 MozDialog,
148 MozDialogtext,
149 /// Used to highlight valid regions to drop something onto.
150 MozDragtargetzone,
151 /// Used for selected but not focused cell backgrounds.
152 MozCellhighlight,
153 /// Used for selected but not focused cell text.
154 MozCellhighlighttext,
155 /// Used for selected but not focused html cell backgrounds.
156 MozHtmlCellhighlight,
157 /// Used for selected but not focused html cell text.
158 MozHtmlCellhighlighttext,
159 /// Used to button text background when hovered.
160 MozButtonhoverface,
161 /// Used to button text color when hovered.
162 MozButtonhovertext,
163 /// Used for menu item backgrounds when hovered.
164 MozMenuhover,
165 /// Used for menu item text when hovered.
166 MozMenuhovertext,
167 /// Used for menubar item text.
168 MozMenubartext,
169 /// Used for menubar item text when hovered.
170 MozMenubarhovertext,
171
172 /// On platforms where these colors are the same as -moz-field, use
173 /// -moz-fieldtext as foreground color
174 MozEventreerow,
175 MozOddtreerow,
176
177 /// Used for button text when pressed.
178 #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
179 MozGtkButtonactivetext,
180
181 /// Used for button text when pressed.
182 MozMacButtonactivetext,
183 /// Background color of chrome toolbars in active windows.
184 MozMacChromeActive,
185 /// Background color of chrome toolbars in inactive windows.
186 MozMacChromeInactive,
187 /// Foreground color of default buttons.
188 MozMacDefaultbuttontext,
189 /// Ring color around text fields and lists.
190 MozMacFocusring,
191 /// Color used when mouse is over a menu item.
192 MozMacMenuselect,
193 /// Color used to do shadows on menu items.
194 MozMacMenushadow,
195 /// Color used to display text for disabled menu items.
196 MozMacMenutextdisable,
197 /// Color used to display text while mouse is over a menu item.
198 MozMacMenutextselect,
199 /// Text color of disabled text on toolbars.
200 MozMacDisabledtoolbartext,
201 /// Inactive light hightlight
202 MozMacSecondaryhighlight,
203
204 /// Font smoothing background colors needed by the Mac OS X theme, based on
205 /// -moz-appearance names.
206 MozMacVibrancyLight,
207 MozMacVibrancyDark,
208 MozMacVibrantTitlebarLight,
209 MozMacVibrantTitlebarDark,
210 MozMacMenupopup,
211 MozMacMenuitem,
212 MozMacActiveMenuitem,
213 MozMacSourceList,
214 MozMacSourceListSelection,
215 MozMacActiveSourceListSelection,
216 MozMacTooltip,
217
218 /// Accent color for title bar.
219 MozWinAccentcolor,
220 /// Color from drawing text over the accent color.
221 MozWinAccentcolortext,
222 /// Media rebar text.
223 MozWinMediatext,
224 /// Communications rebar text.
225 MozWinCommunicationstext,
226
227 /// Hyperlink color extracted from the system, not affected by the
228 /// browser.anchor_color user pref.
229 ///
230 /// There is no OS-specified safe background color for this text, but it is
231 /// used regularly within Windows and the Gnome DE on Dialog and Window
232 /// colors.
233 MozNativehyperlinktext,
234
235 #[parse(aliases = "-moz-hyperlinktext")]
236 Linktext,
237 #[parse(aliases = "-moz-activehyperlinktext")]
238 Activetext,
239 #[parse(aliases = "-moz-visitedhyperlinktext")]
240 Visitedtext,
241
242 /// Combobox widgets
243 MozComboboxtext,
244 MozCombobox,
245
246 MozGtkInfoBarText,
247
248 #[css(skip)]
249 End, // Just for array-indexing purposes.
250 }
251
252 #[cfg(feature = "gecko")]
253 impl SystemColor {
254 #[inline]
compute(&self, cx: &Context) -> ComputedColor255 fn compute(&self, cx: &Context) -> ComputedColor {
256 use crate::gecko_bindings::bindings;
257
258 let prefs = cx.device().pref_sheet_prefs();
259
260 convert_nscolor_to_computedcolor(match *self {
261 SystemColor::Canvastext => prefs.mDefaultColor,
262 SystemColor::Canvas => prefs.mDefaultBackgroundColor,
263 SystemColor::Linktext => prefs.mLinkColor,
264 SystemColor::Activetext => prefs.mActiveLinkColor,
265 SystemColor::Visitedtext => prefs.mVisitedLinkColor,
266
267 _ => unsafe {
268 bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document())
269 },
270 })
271 }
272 }
273
274 impl From<RGBA> for Color {
from(value: RGBA) -> Self275 fn from(value: RGBA) -> Self {
276 Color::rgba(value)
277 }
278 }
279
280 struct ColorComponentParser<'a, 'b: 'a>(&'a ParserContext<'b>);
281 impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponentParser<'a, 'b> {
282 type Error = StyleParseErrorKind<'i>;
283
parse_angle_or_number<'t>( &self, input: &mut Parser<'i, 't>, ) -> Result<AngleOrNumber, ParseError<'i>>284 fn parse_angle_or_number<'t>(
285 &self,
286 input: &mut Parser<'i, 't>,
287 ) -> Result<AngleOrNumber, ParseError<'i>> {
288 use crate::values::specified::Angle;
289
290 let location = input.current_source_location();
291 let token = input.next()?.clone();
292 match token {
293 Token::Dimension {
294 value, ref unit, ..
295 } => {
296 let angle = Angle::parse_dimension(value, unit, /* from_calc = */ false);
297
298 let degrees = match angle {
299 Ok(angle) => angle.degrees(),
300 Err(()) => return Err(location.new_unexpected_token_error(token.clone())),
301 };
302
303 Ok(AngleOrNumber::Angle { degrees })
304 },
305 Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }),
306 Token::Function(ref name) => {
307 let function = CalcNode::math_function(name, location)?;
308 CalcNode::parse_angle_or_number(self.0, input, function)
309 },
310 t => return Err(location.new_unexpected_token_error(t)),
311 }
312 }
313
parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>>314 fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> {
315 use crate::values::specified::Percentage;
316
317 Ok(Percentage::parse(self.0, input)?.get())
318 }
319
parse_number<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>>320 fn parse_number<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> {
321 use crate::values::specified::Number;
322
323 Ok(Number::parse(self.0, input)?.get())
324 }
325
parse_number_or_percentage<'t>( &self, input: &mut Parser<'i, 't>, ) -> Result<NumberOrPercentage, ParseError<'i>>326 fn parse_number_or_percentage<'t>(
327 &self,
328 input: &mut Parser<'i, 't>,
329 ) -> Result<NumberOrPercentage, ParseError<'i>> {
330 let location = input.current_source_location();
331
332 match *input.next()? {
333 Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }),
334 Token::Percentage { unit_value, .. } => {
335 Ok(NumberOrPercentage::Percentage { unit_value })
336 },
337 Token::Function(ref name) => {
338 let function = CalcNode::math_function(name, location)?;
339 CalcNode::parse_number_or_percentage(self.0, input, function)
340 },
341 ref t => return Err(location.new_unexpected_token_error(t.clone())),
342 }
343 }
344 }
345
346 impl Parse for Color {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>347 fn parse<'i, 't>(
348 context: &ParserContext,
349 input: &mut Parser<'i, 't>,
350 ) -> Result<Self, ParseError<'i>> {
351 // Currently we only store authored value for color keywords,
352 // because all browsers serialize those values as keywords for
353 // specified value.
354 let start = input.state();
355 let authored = input.expect_ident_cloned().ok();
356 input.reset(&start);
357
358 let compontent_parser = ColorComponentParser(&*context);
359 match input.try(|i| CSSParserColor::parse_with(&compontent_parser, i)) {
360 Ok(value) => Ok(match value {
361 CSSParserColor::CurrentColor => Color::CurrentColor,
362 CSSParserColor::RGBA(rgba) => Color::Numeric {
363 parsed: rgba,
364 authored: authored.map(|s| s.to_ascii_lowercase().into_boxed_str()),
365 },
366 }),
367 Err(e) => {
368 #[cfg(feature = "gecko")]
369 {
370 if let Ok(system) = input.try(|i| SystemColor::parse(context, i)) {
371 return Ok(Color::System(system));
372 }
373 }
374
375 match e.kind {
376 ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
377 Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
378 ValueParseErrorKind::InvalidColor(t),
379 )))
380 },
381 _ => Err(e),
382 }
383 },
384 }
385 }
386 }
387
388 impl ToCss for Color {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,389 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
390 where
391 W: Write,
392 {
393 match *self {
394 Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
395 Color::Numeric {
396 authored: Some(ref authored),
397 ..
398 } => dest.write_str(authored),
399 Color::Numeric {
400 parsed: ref rgba, ..
401 } => rgba.to_css(dest),
402 Color::Complex(_) => Ok(()),
403 #[cfg(feature = "gecko")]
404 Color::System(system) => system.to_css(dest),
405 #[cfg(feature = "gecko")]
406 Color::InheritFromBodyQuirk => Ok(()),
407 }
408 }
409 }
410
411 /// A wrapper of cssparser::Color::parse_hash.
412 ///
413 /// That function should never return CurrentColor, so it makes no sense to
414 /// handle a cssparser::Color here. This should really be done in cssparser
415 /// directly rather than here.
parse_hash_color(value: &[u8]) -> Result<RGBA, ()>416 fn parse_hash_color(value: &[u8]) -> Result<RGBA, ()> {
417 CSSParserColor::parse_hash(value).map(|color| match color {
418 CSSParserColor::RGBA(rgba) => rgba,
419 CSSParserColor::CurrentColor => unreachable!("parse_hash should never return currentcolor"),
420 })
421 }
422
423 impl Color {
424 /// Returns currentcolor value.
425 #[inline]
currentcolor() -> Color426 pub fn currentcolor() -> Color {
427 Color::CurrentColor
428 }
429
430 /// Returns transparent value.
431 #[inline]
transparent() -> Color432 pub fn transparent() -> Color {
433 // We should probably set authored to "transparent", but maybe it doesn't matter.
434 Color::rgba(RGBA::transparent())
435 }
436
437 /// Returns a numeric RGBA color value.
438 #[inline]
rgba(rgba: RGBA) -> Self439 pub fn rgba(rgba: RGBA) -> Self {
440 Color::Numeric {
441 parsed: rgba,
442 authored: None,
443 }
444 }
445
446 /// Parse a color, with quirks.
447 ///
448 /// <https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk>
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>449 pub fn parse_quirky<'i, 't>(
450 context: &ParserContext,
451 input: &mut Parser<'i, 't>,
452 allow_quirks: AllowQuirks,
453 ) -> Result<Self, ParseError<'i>> {
454 input.try(|i| Self::parse(context, i)).or_else(|e| {
455 if !allow_quirks.allowed(context.quirks_mode) {
456 return Err(e);
457 }
458 Color::parse_quirky_color(input)
459 .map(Color::rgba)
460 .map_err(|_| e)
461 })
462 }
463
464 /// Parse a <quirky-color> value.
465 ///
466 /// <https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk>
parse_quirky_color<'i, 't>(input: &mut Parser<'i, 't>) -> Result<RGBA, ParseError<'i>>467 fn parse_quirky_color<'i, 't>(input: &mut Parser<'i, 't>) -> Result<RGBA, ParseError<'i>> {
468 let location = input.current_source_location();
469 let (value, unit) = match *input.next()? {
470 Token::Number {
471 int_value: Some(integer),
472 ..
473 } => (integer, None),
474 Token::Dimension {
475 int_value: Some(integer),
476 ref unit,
477 ..
478 } => (integer, Some(unit)),
479 Token::Ident(ref ident) => {
480 if ident.len() != 3 && ident.len() != 6 {
481 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
482 }
483 return parse_hash_color(ident.as_bytes()).map_err(|()| {
484 location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
485 });
486 },
487 ref t => {
488 return Err(location.new_unexpected_token_error(t.clone()));
489 },
490 };
491 if value < 0 {
492 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
493 }
494 let length = if value <= 9 {
495 1
496 } else if value <= 99 {
497 2
498 } else if value <= 999 {
499 3
500 } else if value <= 9999 {
501 4
502 } else if value <= 99999 {
503 5
504 } else if value <= 999999 {
505 6
506 } else {
507 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
508 };
509 let total = length + unit.as_ref().map_or(0, |d| d.len());
510 if total > 6 {
511 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
512 }
513 let mut serialization = [b'0'; 6];
514 let space_padding = 6 - total;
515 let mut written = space_padding;
516 written += itoa::write(&mut serialization[written..], value).unwrap();
517 if let Some(unit) = unit {
518 written += (&mut serialization[written..])
519 .write(unit.as_bytes())
520 .unwrap();
521 }
522 debug_assert_eq!(written, 6);
523 parse_hash_color(&serialization)
524 .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
525 }
526
527 /// Returns true if the color is completely transparent, and false
528 /// otherwise.
is_transparent(&self) -> bool529 pub fn is_transparent(&self) -> bool {
530 match *self {
531 Color::Numeric { ref parsed, .. } => parsed.alpha == 0,
532 _ => false,
533 }
534 }
535 }
536
537 #[cfg(feature = "gecko")]
convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor538 fn convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor {
539 use crate::gecko::values::convert_nscolor_to_rgba;
540 ComputedColor::rgba(convert_nscolor_to_rgba(color))
541 }
542
543 impl Color {
544 /// Converts this Color into a ComputedColor.
545 ///
546 /// If `context` is `None`, and the specified color requires data from
547 /// the context to resolve, then `None` is returned.
to_computed_color(&self, _context: Option<&Context>) -> Option<ComputedColor>548 pub fn to_computed_color(&self, _context: Option<&Context>) -> Option<ComputedColor> {
549 Some(match *self {
550 Color::CurrentColor => ComputedColor::currentcolor(),
551 Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
552 Color::Complex(ref complex) => *complex,
553 #[cfg(feature = "gecko")]
554 Color::System(system) => system.compute(_context?),
555 #[cfg(feature = "gecko")]
556 Color::InheritFromBodyQuirk => {
557 ComputedColor::rgba(_context?.device().body_text_color())
558 },
559 })
560 }
561 }
562
563 impl ToComputedValue for Color {
564 type ComputedValue = ComputedColor;
565
to_computed_value(&self, context: &Context) -> ComputedColor566 fn to_computed_value(&self, context: &Context) -> ComputedColor {
567 self.to_computed_color(Some(context)).unwrap()
568 }
569
from_computed_value(computed: &ComputedColor) -> Self570 fn from_computed_value(computed: &ComputedColor) -> Self {
571 match *computed {
572 GenericColor::Numeric(color) => Color::rgba(color),
573 GenericColor::CurrentColor => Color::currentcolor(),
574 GenericColor::Complex { .. } => Color::Complex(*computed),
575 }
576 }
577 }
578
579 /// Specified color value for `-moz-font-smoothing-background-color`.
580 ///
581 /// This property does not support `currentcolor`. We could drop it at
582 /// parse-time, but it's not exposed to the web so it doesn't really matter.
583 ///
584 /// We resolve it to `transparent` instead.
585 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
586 pub struct MozFontSmoothingBackgroundColor(pub Color);
587
588 impl Parse for MozFontSmoothingBackgroundColor {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>589 fn parse<'i, 't>(
590 context: &ParserContext,
591 input: &mut Parser<'i, 't>,
592 ) -> Result<Self, ParseError<'i>> {
593 Color::parse(context, input).map(MozFontSmoothingBackgroundColor)
594 }
595 }
596
597 impl ToComputedValue for MozFontSmoothingBackgroundColor {
598 type ComputedValue = RGBA;
599
to_computed_value(&self, context: &Context) -> RGBA600 fn to_computed_value(&self, context: &Context) -> RGBA {
601 self.0
602 .to_computed_value(context)
603 .to_rgba(RGBA::transparent())
604 }
605
from_computed_value(computed: &RGBA) -> Self606 fn from_computed_value(computed: &RGBA) -> Self {
607 MozFontSmoothingBackgroundColor(Color::rgba(*computed))
608 }
609 }
610
611 impl SpecifiedValueInfo for Color {
612 const SUPPORTED_TYPES: u8 = CssType::COLOR;
613
collect_completion_keywords(f: KeywordsCollectFn)614 fn collect_completion_keywords(f: KeywordsCollectFn) {
615 // We are not going to insert all the color names here. Caller and
616 // devtools should take care of them. XXX Actually, transparent
617 // should probably be handled that way as well.
618 // XXX `currentColor` should really be `currentcolor`. But let's
619 // keep it consistent with the old system for now.
620 f(&["rgb", "rgba", "hsl", "hsla", "currentColor", "transparent"]);
621 }
622 }
623
624 /// Specified value for the "color" property, which resolves the `currentcolor`
625 /// keyword to the parent color instead of self's color.
626 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
627 #[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
628 pub struct ColorPropertyValue(pub Color);
629
630 impl ToComputedValue for ColorPropertyValue {
631 type ComputedValue = RGBA;
632
633 #[inline]
to_computed_value(&self, context: &Context) -> RGBA634 fn to_computed_value(&self, context: &Context) -> RGBA {
635 self.0
636 .to_computed_value(context)
637 .to_rgba(context.builder.get_parent_inherited_text().clone_color())
638 }
639
640 #[inline]
from_computed_value(computed: &RGBA) -> Self641 fn from_computed_value(computed: &RGBA) -> Self {
642 ColorPropertyValue(Color::rgba(*computed).into())
643 }
644 }
645
646 impl Parse for ColorPropertyValue {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>647 fn parse<'i, 't>(
648 context: &ParserContext,
649 input: &mut Parser<'i, 't>,
650 ) -> Result<Self, ParseError<'i>> {
651 Color::parse_quirky(context, input, AllowQuirks::Yes).map(ColorPropertyValue)
652 }
653 }
654
655 /// auto | <color>
656 pub type ColorOrAuto = GenericColorOrAuto<Color>;
657