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