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, Parser};
35use style_traits::{ParseError, StyleParseErrorKind};
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(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
95 Ok(Self(FilterFactor(
96 NonNegativeNumberOrPercentage::parse(context, input)?.0,
97 )))
98 }
99}
100
101type ZeroToOneFactor = ZeroToOne<FilterFactor>;
102impl ZeroToOneFactor {
103 fn one() -> Self {
104 Self(FilterFactor(NumberOrPercentage::Number(Number::new(1.))))
105 }
106}
107
108impl Parse for ZeroToOneFactor {
109 #[inline]
110 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
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(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
122 let mut lengths = None;
123 let mut color = None;
124 let mut inset = false;
125
126 loop {
127 if !inset
128 && input
129 .try_parse(|input| input.expect_ident_matching("inset"))
130 .is_ok()
131 {
132 inset = true;
133 continue;
134 }
135 if lengths.is_none() {
136 let value = input.try_parse::<_, _, ParseError>(|i| {
137 let horizontal = Length::parse(context, i)?;
138 let vertical = Length::parse(context, i)?;
139 let (blur, spread) =
140 match i.try_parse(|i| Length::parse_non_negative(context, i)) {
141 Ok(blur) => {
142 let spread = i.try_parse(|i| Length::parse(context, i)).ok();
143 (Some(blur.into()), spread)
144 },
145 Err(_) => (None, None),
146 };
147 Ok((horizontal, vertical, blur, spread))
148 });
149 if let Ok(value) = value {
150 lengths = Some(value);
151 continue;
152 }
153 }
154 if color.is_none() {
155 if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) {
156 color = Some(value);
157 continue;
158 }
159 }
160 break;
161 }
162
163 let lengths = lengths.ok_or(ParseError::custom(StyleParseErrorKind::UnspecifiedError))?;
164 Ok(BoxShadow {
165 base: SimpleShadow {
166 color,
167 horizontal: lengths.0,
168 vertical: lengths.1,
169 blur: lengths.2,
170 },
171 spread: lengths.3,
172 inset,
173 })
174 }
175}
176
177impl ToComputedValue for BoxShadow {
178 type ComputedValue = ComputedBoxShadow;
179
180 #[inline]
181 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
182 ComputedBoxShadow {
183 base: self.base.to_computed_value(context),
184 spread: self
185 .spread
186 .as_ref()
187 .unwrap_or(&Length::zero())
188 .to_computed_value(context),
189 inset: self.inset,
190 }
191 }
192
193 #[inline]
194 fn from_computed_value(computed: &ComputedBoxShadow) -> Self {
195 BoxShadow {
196 base: ToComputedValue::from_computed_value(&computed.base),
197 spread: Some(ToComputedValue::from_computed_value(&computed.spread)),
198 inset: computed.inset,
199 }
200 }
201}
202
203impl Filter {
207 pub fn to_computed_value_without_context(&self) -> Result<ComputedFilter, ()> {
209 match *self {
210 Filter::Blur(ref length) => Ok(ComputedFilter::Blur(ComputedNonNegativeLength::new(
211 length.0.to_computed_pixel_length_without_context()?,
212 ))),
213 Filter::Brightness(ref factor) => {
214 Ok(ComputedFilter::Brightness(ComputedNonNegativeNumber::from(
215 factor.0.to_computed_value_without_context()?.value(),
216 )))
217 },
218 Filter::Contrast(ref factor) => {
219 Ok(ComputedFilter::Contrast(ComputedNonNegativeNumber::from(
220 factor.0.to_computed_value_without_context()?.value(),
221 )))
222 },
223 Filter::Grayscale(ref factor) => {
224 Ok(ComputedFilter::Grayscale(ComputedZeroToOneNumber::from(
225 factor.0.to_computed_value_without_context()?.value(),
226 )))
227 },
228 Filter::HueRotate(ref angle) => Ok(ComputedFilter::HueRotate(
229 ComputedAngle::from_degrees(angle.degrees().ok_or(())?),
230 )),
231 Filter::Invert(ref factor) => {
232 Ok(ComputedFilter::Invert(ComputedZeroToOneNumber::from(
233 factor.0.to_computed_value_without_context()?.value(),
234 )))
235 },
236 Filter::Opacity(ref factor) => {
237 Ok(ComputedFilter::Opacity(ComputedZeroToOneNumber::from(
238 factor.0.to_computed_value_without_context()?.value(),
239 )))
240 },
241 Filter::Saturate(ref factor) => {
242 Ok(ComputedFilter::Saturate(ComputedNonNegativeNumber::from(
243 factor.0.to_computed_value_without_context()?.value(),
244 )))
245 },
246 Filter::Sepia(ref factor) => Ok(ComputedFilter::Sepia(ComputedZeroToOneNumber::from(
247 factor.0.to_computed_value_without_context()?.value(),
248 ))),
249 Filter::DropShadow(ref shadow) => {
250 if cfg!(feature = "gecko") {
251 let color = shadow
252 .color
253 .as_ref()
254 .unwrap_or(&Color::currentcolor())
255 .to_computed_color(None)?;
256
257 let horizontal = ComputedCSSPixelLength::new(
258 shadow
259 .horizontal
260 .to_computed_pixel_length_without_context()?,
261 );
262 let vertical = ComputedCSSPixelLength::new(
263 shadow.vertical.to_computed_pixel_length_without_context()?,
264 );
265 let blur = ComputedNonNegativeLength::new(
266 shadow
267 .blur
268 .as_ref()
269 .unwrap_or(&NonNegativeLength::zero())
270 .0
271 .to_computed_pixel_length_without_context()?,
272 );
273
274 Ok(ComputedFilter::DropShadow(ComputedSimpleShadow {
275 color,
276 horizontal,
277 vertical,
278 blur,
279 }))
280 } else {
281 Err(())
282 }
283 },
284 #[cfg(feature = "gecko")]
285 Filter::Url(ref url) => Ok(ComputedFilter::Url(ComputedUrl(url.clone()))),
286 #[cfg(feature = "servo")]
287 Filter::Url(_) => Err(()),
288 }
289 }
290}
291
292impl Parse for Filter {
293 #[inline]
294 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
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 function = match input.expect_function() {
302 Ok(f) => f.clone(),
303 Err(e) => return Err(e.into()),
304 };
305 input.parse_nested_block(|i| {
306 match_ignore_ascii_case! { &*function,
307 "blur" => Ok(GenericFilter::Blur(
308 i.try_parse(|i| NonNegativeLength::parse(context, i))
309 .unwrap_or(Zero::zero()),
310 )),
311 "brightness" => Ok(GenericFilter::Brightness(
312 i.try_parse(|i| NonNegativeFactor::parse(context, i))
313 .unwrap_or(NonNegativeFactor::one()),
314 )),
315 "contrast" => Ok(GenericFilter::Contrast(
316 i.try_parse(|i| NonNegativeFactor::parse(context, i))
317 .unwrap_or(NonNegativeFactor::one()),
318 )),
319 "grayscale" => {
320 Ok(GenericFilter::Grayscale(
323 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
324 .unwrap_or(ZeroToOneFactor::one()),
325 ))
326 },
327 "hue-rotate" => {
328 Ok(GenericFilter::HueRotate(
331 i.try_parse(|i| Angle::parse_with_unitless(context, i))
332 .unwrap_or(Zero::zero()),
333 ))
334 },
335 "invert" => {
336 Ok(GenericFilter::Invert(
339 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
340 .unwrap_or(ZeroToOneFactor::one()),
341 ))
342 },
343 "opacity" => {
344 Ok(GenericFilter::Opacity(
347 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
348 .unwrap_or(ZeroToOneFactor::one()),
349 ))
350 },
351 "saturate" => Ok(GenericFilter::Saturate(
352 i.try_parse(|i| NonNegativeFactor::parse(context, i))
353 .unwrap_or(NonNegativeFactor::one()),
354 )),
355 "sepia" => {
356 Ok(GenericFilter::Sepia(
359 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
360 .unwrap_or(ZeroToOneFactor::one()),
361 ))
362 },
363 "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),
364 _ => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
365 }
366 })
367 }
368}
369
370impl Parse for SimpleShadow {
371 #[inline]
372 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
373 let color = input.try_parse(|i| Color::parse(context, i)).ok();
374 let horizontal = Length::parse(context, input)?;
375 let vertical = Length::parse(context, input)?;
376 let blur = input
377 .try_parse(|i| Length::parse_non_negative(context, i))
378 .ok();
379 let blur = blur.map(NonNegative::<Length>);
380 let color = color.or_else(|| input.try_parse(|i| Color::parse(context, i)).ok());
381
382 Ok(SimpleShadow {
383 color,
384 horizontal,
385 vertical,
386 blur,
387 })
388 }
389}
390
391impl ToComputedValue for SimpleShadow {
392 type ComputedValue = ComputedSimpleShadow;
393
394 #[inline]
395 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
396 ComputedSimpleShadow {
397 color: self
398 .color
399 .as_ref()
400 .unwrap_or(&Color::currentcolor())
401 .to_computed_value(context),
402 horizontal: self.horizontal.to_computed_value(context),
403 vertical: self.vertical.to_computed_value(context),
404 blur: self
405 .blur
406 .as_ref()
407 .unwrap_or(&NonNegativeLength::zero())
408 .to_computed_value(context),
409 }
410 }
411
412 #[inline]
413 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
414 SimpleShadow {
415 color: Some(ToComputedValue::from_computed_value(&computed.color)),
416 horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
417 vertical: ToComputedValue::from_computed_value(&computed.vertical),
418 blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
419 }
420 }
421}