1use std::fmt::Write;
8
9use super::{
10 parsing::{rcs_enabled, ChannelKeyword},
11 AbsoluteColor,
12};
13use crate::derives::*;
14use crate::{
15 parser::ParserContext,
16 values::{
17 animated::ToAnimatedValue,
18 computed,
19 generics::calc::CalcUnits,
20 specified::calc::{CalcNode, CalcParseFlags, Leaf},
21 },
22};
23use cssparser::{color::OPAQUE, Parser, Token};
24use style_traits::{ParseError, StyleParseErrorKind, ToCss};
25
26#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
28#[repr(u8)]
29pub enum ColorComponent<ValueType> {
30 None,
32 Value(ValueType),
34 ChannelKeyword(ChannelKeyword),
36 Calc(Box<CalcNode>),
38 AlphaOmitted,
40}
41
42impl<ValueType> ColorComponent<ValueType> {
43 #[inline]
45 pub fn is_none(&self) -> bool {
46 matches!(self, Self::None)
47 }
48}
49
50pub trait ColorComponentType: Sized + Clone {
53 fn from_value(value: f32) -> Self;
58
59 fn units() -> CalcUnits;
61
62 fn try_from_token(token: &Token) -> Result<Self, ()>;
64
65 fn try_from_leaf(leaf: &Leaf) -> Result<Self, ()>;
68}
69
70impl<ValueType: ColorComponentType> ColorComponent<ValueType> {
71 pub fn parse<'i, 't>(
73 context: &ParserContext,
74 input: &mut Parser<'i, 't>,
75 allow_none: bool,
76 allowed_channel_keywords: ChannelKeyword,
77 ) -> Result<Self, ParseError<'i>> {
78 let location = input.current_source_location();
79
80 match *input.next()? {
81 Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => {
82 Ok(ColorComponent::None)
83 },
84 ref t @ Token::Ident(ref ident) => Ok(match ChannelKeyword::from_ident(ident) {
85 Ok(channel_keyword) if allowed_channel_keywords.contains(channel_keyword) => {
86 ColorComponent::ChannelKeyword(channel_keyword)
87 },
88 _ => return Err(location.new_unexpected_token_error(t.clone())),
89 }),
90 Token::Function(ref name) => {
91 let function = CalcNode::math_function(context, name, location)?;
92 let mut flags = CalcParseFlags::new(ValueType::units());
93 flags.color_components = if rcs_enabled() {
94 allowed_channel_keywords
95 } else {
96 ChannelKeyword::empty()
97 };
98 let mut node = CalcNode::parse(context, input, function, flags)?;
99 node.simplify_and_sort();
100 if node.unit().is_err() {
101 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
102 }
103 Ok(Self::Calc(Box::new(node)))
104 },
105 ref t => ValueType::try_from_token(t)
106 .map(Self::Value)
107 .map_err(|_| location.new_unexpected_token_error(t.clone())),
108 }
109 }
110
111 pub fn to_computed_value(
118 &self,
119 context: Option<&computed::Context>,
120 origin_color: Option<&AbsoluteColor>,
121 ) -> Self {
122 match self {
123 Self::None => Self::None,
124 Self::Value(v) => Self::Value(v.clone()),
125 Self::ChannelKeyword(channel_keyword) => match origin_color {
126 Some(origin_color) => {
127 match origin_color.get_component_by_channel_keyword(*channel_keyword) {
128 Ok(value) => Self::Value(ValueType::from_value(value.unwrap_or(0.0))),
129 Err(()) => Self::ChannelKeyword(*channel_keyword),
130 }
131 },
132 None => Self::ChannelKeyword(*channel_keyword),
133 },
134 Self::Calc(node) => {
135 if let Ok(value) = node
139 .resolve_map(|leaf| Ok(leaf.to_computed_value(context, origin_color)))
140 .and_then(|leaf| ValueType::try_from_leaf(&leaf))
141 {
142 Self::Value(value)
143 } else {
144 Self::Calc(Box::new(node.to_computed_value(context, origin_color)))
145 }
146 },
147 Self::AlphaOmitted => match origin_color {
148 Some(origin_color) => match origin_color.alpha() {
153 Some(alpha) => Self::Value(ValueType::from_value(alpha)),
154 None => Self::None,
155 },
156 None => Self::AlphaOmitted,
157 },
158 }
159 }
160
161 pub fn resolve(&self) -> Result<Option<ValueType>, ()> {
166 Ok(match self {
167 Self::None => None,
168 Self::Value(value) => Some(value.clone()),
169 Self::ChannelKeyword(_) => return Err(()),
172 Self::Calc(node) => Some(ValueType::try_from_leaf(&node.resolve()?)?),
173 Self::AlphaOmitted => Some(ValueType::from_value(OPAQUE)),
174 })
175 }
176}
177
178impl<ValueType: ToCss> ToCss for ColorComponent<ValueType> {
179 fn to_css<W>(&self, dest: &mut style_traits::CssWriter<W>) -> std::fmt::Result
180 where
181 W: Write,
182 {
183 match self {
184 ColorComponent::None => dest.write_str("none")?,
185 ColorComponent::Value(value) => value.to_css(dest)?,
186 ColorComponent::ChannelKeyword(channel_keyword) => channel_keyword.to_css(dest)?,
187 ColorComponent::Calc(node) => {
188 node.to_css(dest)?;
193 },
194 ColorComponent::AlphaOmitted => {
195 debug_assert!(false, "can't serialize an omitted alpha component");
196 },
197 }
198
199 Ok(())
200 }
201}
202
203impl<ValueType> ToAnimatedValue for ColorComponent<ValueType> {
204 type AnimatedValue = Self;
205
206 fn to_animated_value(self, _context: &crate::values::animated::Context) -> Self::AnimatedValue {
207 self
208 }
209
210 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
211 animated
212 }
213}