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 = match shadow
246 .color
247 .as_ref()
248 .unwrap_or(&Color::currentcolor())
249 .to_computed_color(None)
250 {
251 Some(c) => c,
252 None => return Err(()),
253 };
254
255 let horizontal = ComputedCSSPixelLength::new(
256 shadow
257 .horizontal
258 .to_computed_pixel_length_without_context()?,
259 );
260 let vertical = ComputedCSSPixelLength::new(
261 shadow.vertical.to_computed_pixel_length_without_context()?,
262 );
263 let blur = ComputedNonNegativeLength::new(
264 shadow
265 .blur
266 .as_ref()
267 .unwrap_or(&NonNegativeLength::zero())
268 .0
269 .to_computed_pixel_length_without_context()?,
270 );
271
272 Ok(ComputedFilter::DropShadow(ComputedSimpleShadow {
273 color,
274 horizontal,
275 vertical,
276 blur,
277 }))
278 } else {
279 Err(())
280 }
281 },
282 #[cfg(feature = "gecko")]
283 Filter::Url(ref url) => Ok(ComputedFilter::Url(ComputedUrl(url.clone()))),
284 #[cfg(feature = "servo")]
285 Filter::Url(_) => Err(()),
286 }
287 }
288}
289
290impl Parse for Filter {
291 #[inline]
292 fn parse<'i, 't>(
293 context: &ParserContext,
294 input: &mut Parser<'i, 't>,
295 ) -> Result<Self, ParseError<'i>> {
296 #[cfg(feature = "gecko")]
297 {
298 if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
299 return Ok(GenericFilter::Url(url));
300 }
301 }
302 let location = input.current_source_location();
303 let function = match input.expect_function() {
304 Ok(f) => f.clone(),
305 Err(cssparser::BasicParseError {
306 kind: BasicParseErrorKind::UnexpectedToken(t),
307 location,
308 }) => return Err(location.new_custom_error(ValueParseErrorKind::InvalidFilter(t))),
309 Err(e) => return Err(e.into()),
310 };
311 input.parse_nested_block(|i| {
312 match_ignore_ascii_case! { &*function,
313 "blur" => Ok(GenericFilter::Blur(
314 i.try_parse(|i| NonNegativeLength::parse(context, i))
315 .unwrap_or(Zero::zero()),
316 )),
317 "brightness" => Ok(GenericFilter::Brightness(
318 i.try_parse(|i| NonNegativeFactor::parse(context, i))
319 .unwrap_or(NonNegativeFactor::one()),
320 )),
321 "contrast" => Ok(GenericFilter::Contrast(
322 i.try_parse(|i| NonNegativeFactor::parse(context, i))
323 .unwrap_or(NonNegativeFactor::one()),
324 )),
325 "grayscale" => {
326 Ok(GenericFilter::Grayscale(
329 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
330 .unwrap_or(ZeroToOneFactor::one()),
331 ))
332 },
333 "hue-rotate" => {
334 Ok(GenericFilter::HueRotate(
337 i.try_parse(|i| Angle::parse_with_unitless(context, i))
338 .unwrap_or(Zero::zero()),
339 ))
340 },
341 "invert" => {
342 Ok(GenericFilter::Invert(
345 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
346 .unwrap_or(ZeroToOneFactor::one()),
347 ))
348 },
349 "opacity" => {
350 Ok(GenericFilter::Opacity(
353 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
354 .unwrap_or(ZeroToOneFactor::one()),
355 ))
356 },
357 "saturate" => Ok(GenericFilter::Saturate(
358 i.try_parse(|i| NonNegativeFactor::parse(context, i))
359 .unwrap_or(NonNegativeFactor::one()),
360 )),
361 "sepia" => {
362 Ok(GenericFilter::Sepia(
365 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
366 .unwrap_or(ZeroToOneFactor::one()),
367 ))
368 },
369 "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),
370 _ => Err(location.new_custom_error(
371 ValueParseErrorKind::InvalidFilter(Token::Function(function.clone()))
372 )),
373 }
374 })
375 }
376}
377
378impl Parse for SimpleShadow {
379 #[inline]
380 fn parse<'i, 't>(
381 context: &ParserContext,
382 input: &mut Parser<'i, 't>,
383 ) -> Result<Self, ParseError<'i>> {
384 let color = input.try_parse(|i| Color::parse(context, i)).ok();
385 let horizontal = Length::parse(context, input)?;
386 let vertical = Length::parse(context, input)?;
387 let blur = input
388 .try_parse(|i| Length::parse_non_negative(context, i))
389 .ok();
390 let blur = blur.map(NonNegative::<Length>);
391 let color = color.or_else(|| input.try_parse(|i| Color::parse(context, i)).ok());
392
393 Ok(SimpleShadow {
394 color,
395 horizontal,
396 vertical,
397 blur,
398 })
399 }
400}
401
402impl ToComputedValue for SimpleShadow {
403 type ComputedValue = ComputedSimpleShadow;
404
405 #[inline]
406 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
407 ComputedSimpleShadow {
408 color: self
409 .color
410 .as_ref()
411 .unwrap_or(&Color::currentcolor())
412 .to_computed_value(context),
413 horizontal: self.horizontal.to_computed_value(context),
414 vertical: self.vertical.to_computed_value(context),
415 blur: self
416 .blur
417 .as_ref()
418 .unwrap_or(&NonNegativeLength::zero())
419 .to_computed_value(context),
420 }
421 }
422
423 #[inline]
424 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
425 SimpleShadow {
426 color: Some(ToComputedValue::from_computed_value(&computed.color)),
427 horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
428 vertical: ToComputedValue::from_computed_value(&computed.vertical),
429 blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
430 }
431 }
432}