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