1use crate::parser::{Parse, ParserContext};
8use crate::values::computed::effects::BoxShadow as ComputedBoxShadow;
9use crate::values::computed::effects::SimpleShadow as ComputedSimpleShadow;
10#[cfg(feature = "gecko")]
11use crate::values::computed::url::ComputedUrl;
12use crate::values::computed::Angle as ComputedAngle;
13use crate::values::computed::CSSPixelLength as ComputedCSSPixelLength;
14use crate::values::computed::Filter as ComputedFilter;
15use crate::values::computed::NonNegativeLength as ComputedNonNegativeLength;
16use crate::values::computed::NonNegativeNumber as ComputedNonNegativeNumber;
17use crate::values::computed::ZeroToOneNumber as ComputedZeroToOneNumber;
18use crate::values::computed::{Context, ToComputedValue};
19use crate::values::generics::effects::BoxShadow as GenericBoxShadow;
20use crate::values::generics::effects::Filter as GenericFilter;
21use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
22use crate::values::generics::NonNegative;
23use crate::values::specified::color::Color;
24use crate::values::specified::length::{Length, NonNegativeLength};
25#[cfg(feature = "gecko")]
26use crate::values::specified::url::SpecifiedUrl;
27use crate::values::specified::{Angle, Number, NumberOrPercentage};
28#[cfg(feature = "servo")]
29use crate::values::Impossible;
30use crate::Zero;
31use cssparser::{BasicParseErrorKind, Parser, Token};
32use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};
33
34pub type BoxShadow =
36 GenericBoxShadow<Option<Color>, Length, Option<NonNegativeLength>, Option<Length>>;
37
38#[cfg(feature = "gecko")]
40pub type SpecifiedFilter = GenericFilter<
41 Angle,
42 NonNegativeFactor,
43 ZeroToOneFactor,
44 NonNegativeLength,
45 SimpleShadow,
46 SpecifiedUrl,
47>;
48
49#[cfg(feature = "servo")]
51pub type SpecifiedFilter = GenericFilter<
52 Angle,
53 NonNegativeFactor,
54 ZeroToOneFactor,
55 NonNegativeLength,
56 SimpleShadow,
57 Impossible,
58>;
59
60pub use self::SpecifiedFilter as Filter;
61
62#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
64pub struct NonNegativeFactor(NumberOrPercentage);
65
66#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
68pub struct ZeroToOneFactor(NumberOrPercentage);
69
70#[inline]
72fn clamp_to_one(number: NumberOrPercentage) -> NumberOrPercentage {
73 match number {
74 NumberOrPercentage::Percentage(percent) => {
75 NumberOrPercentage::Percentage(percent.clamp_to_hundred())
76 },
77 NumberOrPercentage::Number(number) => NumberOrPercentage::Number(number.clamp_to_one()),
78 }
79}
80
81macro_rules! factor_impl_common {
82 ($ty:ty, $computed_ty:ty) => {
83 impl $ty {
84 #[inline]
85 fn one() -> Self {
86 Self(NumberOrPercentage::Number(Number::new(1.)))
87 }
88 }
89
90 impl ToComputedValue for $ty {
91 type ComputedValue = $computed_ty;
92
93 #[inline]
94 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
95 use crate::values::computed::NumberOrPercentage;
96 match self.0.to_computed_value(context) {
97 NumberOrPercentage::Number(n) => n.into(),
98 NumberOrPercentage::Percentage(p) => p.0.into(),
99 }
100 }
101
102 #[inline]
103 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
104 Self(NumberOrPercentage::Number(
105 ToComputedValue::from_computed_value(&computed.0),
106 ))
107 }
108 }
109 };
110}
111factor_impl_common!(NonNegativeFactor, ComputedNonNegativeNumber);
112factor_impl_common!(ZeroToOneFactor, ComputedZeroToOneNumber);
113
114impl Parse for NonNegativeFactor {
115 #[inline]
116 fn parse<'i, 't>(
117 context: &ParserContext,
118 input: &mut Parser<'i, 't>,
119 ) -> Result<Self, ParseError<'i>> {
120 NumberOrPercentage::parse_non_negative(context, input).map(Self)
121 }
122}
123
124impl Parse for ZeroToOneFactor {
125 #[inline]
126 fn parse<'i, 't>(
127 context: &ParserContext,
128 input: &mut Parser<'i, 't>,
129 ) -> Result<Self, ParseError<'i>> {
130 NumberOrPercentage::parse_non_negative(context, input)
131 .map(clamp_to_one)
132 .map(Self)
133 }
134}
135
136pub type SimpleShadow = GenericSimpleShadow<Option<Color>, Length, Option<NonNegativeLength>>;
138
139impl Parse for BoxShadow {
140 fn parse<'i, 't>(
141 context: &ParserContext,
142 input: &mut Parser<'i, 't>,
143 ) -> Result<Self, ParseError<'i>> {
144 let mut lengths = None;
145 let mut color = None;
146 let mut inset = false;
147
148 loop {
149 if !inset {
150 if input
151 .try_parse(|input| input.expect_ident_matching("inset"))
152 .is_ok()
153 {
154 inset = true;
155 continue;
156 }
157 }
158 if lengths.is_none() {
159 let value = input.try_parse::<_, _, ParseError>(|i| {
160 let horizontal = Length::parse(context, i)?;
161 let vertical = Length::parse(context, i)?;
162 let (blur, spread) =
163 match i.try_parse(|i| Length::parse_non_negative(context, i)) {
164 Ok(blur) => {
165 let spread = i.try_parse(|i| Length::parse(context, i)).ok();
166 (Some(blur.into()), spread)
167 },
168 Err(_) => (None, None),
169 };
170 Ok((horizontal, vertical, blur, spread))
171 });
172 if let Ok(value) = value {
173 lengths = Some(value);
174 continue;
175 }
176 }
177 if color.is_none() {
178 if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) {
179 color = Some(value);
180 continue;
181 }
182 }
183 break;
184 }
185
186 let lengths =
187 lengths.ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
188 Ok(BoxShadow {
189 base: SimpleShadow {
190 color: color,
191 horizontal: lengths.0,
192 vertical: lengths.1,
193 blur: lengths.2,
194 },
195 spread: lengths.3,
196 inset: inset,
197 })
198 }
199}
200
201impl ToComputedValue for BoxShadow {
202 type ComputedValue = ComputedBoxShadow;
203
204 #[inline]
205 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
206 ComputedBoxShadow {
207 base: self.base.to_computed_value(context),
208 spread: self
209 .spread
210 .as_ref()
211 .unwrap_or(&Length::zero())
212 .to_computed_value(context),
213 inset: self.inset,
214 }
215 }
216
217 #[inline]
218 fn from_computed_value(computed: &ComputedBoxShadow) -> Self {
219 BoxShadow {
220 base: ToComputedValue::from_computed_value(&computed.base),
221 spread: Some(ToComputedValue::from_computed_value(&computed.spread)),
222 inset: computed.inset,
223 }
224 }
225}
226
227impl Filter {
231 pub fn to_computed_value_without_context(&self) -> Result<ComputedFilter, ()> {
233 match *self {
234 Filter::Blur(ref length) => Ok(ComputedFilter::Blur(ComputedNonNegativeLength::new(
235 length.0.to_computed_pixel_length_without_context()?,
236 ))),
237 Filter::Brightness(ref factor) => Ok(ComputedFilter::Brightness(
238 ComputedNonNegativeNumber::from(factor.0.to_number().get()),
239 )),
240 Filter::Contrast(ref factor) => Ok(ComputedFilter::Contrast(
241 ComputedNonNegativeNumber::from(factor.0.to_number().get()),
242 )),
243 Filter::Grayscale(ref factor) => Ok(ComputedFilter::Grayscale(
244 ComputedZeroToOneNumber::from(factor.0.to_number().get()),
245 )),
246 Filter::HueRotate(ref angle) => Ok(ComputedFilter::HueRotate(
247 ComputedAngle::from_degrees(angle.degrees()),
248 )),
249 Filter::Invert(ref factor) => Ok(ComputedFilter::Invert(
250 ComputedZeroToOneNumber::from(factor.0.to_number().get()),
251 )),
252 Filter::Opacity(ref factor) => Ok(ComputedFilter::Opacity(
253 ComputedZeroToOneNumber::from(factor.0.to_number().get()),
254 )),
255 Filter::Saturate(ref factor) => Ok(ComputedFilter::Saturate(
256 ComputedNonNegativeNumber::from(factor.0.to_number().get()),
257 )),
258 Filter::Sepia(ref factor) => Ok(ComputedFilter::Sepia(ComputedZeroToOneNumber::from(
259 factor.0.to_number().get(),
260 ))),
261 Filter::DropShadow(ref shadow) => {
262 if cfg!(feature = "gecko") {
263 let color = match shadow
264 .color
265 .as_ref()
266 .unwrap_or(&Color::currentcolor())
267 .to_computed_color(None)
268 {
269 Some(c) => c,
270 None => return Err(()),
271 };
272
273 let horizontal = ComputedCSSPixelLength::new(
274 shadow
275 .horizontal
276 .to_computed_pixel_length_without_context()?,
277 );
278 let vertical = ComputedCSSPixelLength::new(
279 shadow.vertical.to_computed_pixel_length_without_context()?,
280 );
281 let blur = ComputedNonNegativeLength::new(
282 shadow
283 .blur
284 .as_ref()
285 .unwrap_or(&NonNegativeLength::zero())
286 .0
287 .to_computed_pixel_length_without_context()?,
288 );
289
290 Ok(ComputedFilter::DropShadow(ComputedSimpleShadow {
291 color,
292 horizontal,
293 vertical,
294 blur,
295 }))
296 } else {
297 Err(())
298 }
299 },
300 #[cfg(feature = "gecko")]
301 Filter::Url(ref url) => Ok(ComputedFilter::Url(ComputedUrl(url.clone()))),
302 #[cfg(feature = "servo")]
303 Filter::Url(_) => Err(()),
304 }
305 }
306}
307
308impl Parse for Filter {
309 #[inline]
310 fn parse<'i, 't>(
311 context: &ParserContext,
312 input: &mut Parser<'i, 't>,
313 ) -> Result<Self, ParseError<'i>> {
314 #[cfg(feature = "gecko")]
315 {
316 if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
317 return Ok(GenericFilter::Url(url));
318 }
319 }
320 let location = input.current_source_location();
321 let function = match input.expect_function() {
322 Ok(f) => f.clone(),
323 Err(cssparser::BasicParseError {
324 kind: BasicParseErrorKind::UnexpectedToken(t),
325 location,
326 }) => return Err(location.new_custom_error(ValueParseErrorKind::InvalidFilter(t))),
327 Err(e) => return Err(e.into()),
328 };
329 input.parse_nested_block(|i| {
330 match_ignore_ascii_case! { &*function,
331 "blur" => Ok(GenericFilter::Blur(
332 i.try_parse(|i| NonNegativeLength::parse(context, i))
333 .unwrap_or(Zero::zero()),
334 )),
335 "brightness" => Ok(GenericFilter::Brightness(
336 i.try_parse(|i| NonNegativeFactor::parse(context, i))
337 .unwrap_or(NonNegativeFactor::one()),
338 )),
339 "contrast" => Ok(GenericFilter::Contrast(
340 i.try_parse(|i| NonNegativeFactor::parse(context, i))
341 .unwrap_or(NonNegativeFactor::one()),
342 )),
343 "grayscale" => {
344 Ok(GenericFilter::Grayscale(
347 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
348 .unwrap_or(ZeroToOneFactor::one()),
349 ))
350 },
351 "hue-rotate" => {
352 Ok(GenericFilter::HueRotate(
355 i.try_parse(|i| Angle::parse_with_unitless(context, i))
356 .unwrap_or(Zero::zero()),
357 ))
358 },
359 "invert" => {
360 Ok(GenericFilter::Invert(
363 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
364 .unwrap_or(ZeroToOneFactor::one()),
365 ))
366 },
367 "opacity" => {
368 Ok(GenericFilter::Opacity(
371 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
372 .unwrap_or(ZeroToOneFactor::one()),
373 ))
374 },
375 "saturate" => Ok(GenericFilter::Saturate(
376 i.try_parse(|i| NonNegativeFactor::parse(context, i))
377 .unwrap_or(NonNegativeFactor::one()),
378 )),
379 "sepia" => {
380 Ok(GenericFilter::Sepia(
383 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
384 .unwrap_or(ZeroToOneFactor::one()),
385 ))
386 },
387 "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),
388 _ => Err(location.new_custom_error(
389 ValueParseErrorKind::InvalidFilter(Token::Function(function.clone()))
390 )),
391 }
392 })
393 }
394}
395
396impl Parse for SimpleShadow {
397 #[inline]
398 fn parse<'i, 't>(
399 context: &ParserContext,
400 input: &mut Parser<'i, 't>,
401 ) -> Result<Self, ParseError<'i>> {
402 let color = input.try_parse(|i| Color::parse(context, i)).ok();
403 let horizontal = Length::parse(context, input)?;
404 let vertical = Length::parse(context, input)?;
405 let blur = input
406 .try_parse(|i| Length::parse_non_negative(context, i))
407 .ok();
408 let blur = blur.map(NonNegative::<Length>);
409 let color = color.or_else(|| input.try_parse(|i| Color::parse(context, i)).ok());
410
411 Ok(SimpleShadow {
412 color,
413 horizontal,
414 vertical,
415 blur,
416 })
417 }
418}
419
420impl ToComputedValue for SimpleShadow {
421 type ComputedValue = ComputedSimpleShadow;
422
423 #[inline]
424 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
425 ComputedSimpleShadow {
426 color: self
427 .color
428 .as_ref()
429 .unwrap_or(&Color::currentcolor())
430 .to_computed_value(context),
431 horizontal: self.horizontal.to_computed_value(context),
432 vertical: self.vertical.to_computed_value(context),
433 blur: self
434 .blur
435 .as_ref()
436 .unwrap_or(&NonNegativeLength::zero())
437 .to_computed_value(context),
438 }
439 }
440
441 #[inline]
442 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
443 SimpleShadow {
444 color: Some(ToComputedValue::from_computed_value(&computed.color)),
445 horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
446 vertical: ToComputedValue::from_computed_value(&computed.vertical),
447 blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
448 }
449 }
450}