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