1use crate::Zero;
8use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10#[cfg(feature = "servo")]
11use crate::values::Impossible;
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::NumberOrPercentage as ComputedNumberOrPercentage;
19use crate::values::computed::ZeroToOneNumber as ComputedZeroToOneNumber;
20use crate::values::computed::effects::BoxShadow as ComputedBoxShadow;
21use crate::values::computed::effects::SimpleShadow as ComputedSimpleShadow;
22#[cfg(feature = "gecko")]
23use crate::values::computed::url::ComputedUrl;
24use crate::values::computed::{Context, ToComputedValue};
25use crate::values::generics::effects::BoxShadow as GenericBoxShadow;
26use crate::values::generics::effects::Filter as GenericFilter;
27use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
28use crate::values::generics::{NonNegative, ZeroToOne};
29use crate::values::specified::color::Color;
30use crate::values::specified::length::{Length, NonNegativeLength};
31#[cfg(feature = "gecko")]
32use crate::values::specified::url::SpecifiedUrl;
33use crate::values::specified::{Angle, NonNegativeNumberOrPercentage, Number, NumberOrPercentage};
34use cssparser::{Parser, match_ignore_ascii_case};
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 && let Ok(value) = input.try_parse(|i| Color::parse(context, i))
156 {
157 color = Some(value);
158 continue;
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 let color = shadow
251 .color
252 .as_ref()
253 .unwrap_or(&Color::currentcolor())
254 .to_computed_color(None)?;
255
256 let horizontal = ComputedCSSPixelLength::new(
257 shadow
258 .horizontal
259 .to_computed_pixel_length_without_context()?,
260 );
261 let vertical = ComputedCSSPixelLength::new(
262 shadow.vertical.to_computed_pixel_length_without_context()?,
263 );
264 let blur = ComputedNonNegativeLength::new(
265 shadow
266 .blur
267 .as_ref()
268 .unwrap_or(&NonNegativeLength::zero())
269 .0
270 .to_computed_pixel_length_without_context()?,
271 );
272
273 Ok(ComputedFilter::DropShadow(ComputedSimpleShadow {
274 color,
275 horizontal,
276 vertical,
277 blur,
278 }))
279 },
280 #[cfg(feature = "gecko")]
281 Filter::Url(ref url) => Ok(ComputedFilter::Url(ComputedUrl(url.clone()))),
282 #[cfg(feature = "servo")]
283 Filter::Url(_) => Err(()),
284 }
285 }
286}
287
288impl Parse for Filter {
289 #[inline]
290 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
291 #[cfg(feature = "gecko")]
292 {
293 if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
294 return Ok(GenericFilter::Url(url));
295 }
296 }
297 let function = {
298 let f = input.expect_function()?;
299 f.clone()
300 };
301 input.parse_nested_block(|i| {
302 match_ignore_ascii_case! { &*function,
303 "blur" => Ok(GenericFilter::Blur(
304 i.try_parse(|i| NonNegativeLength::parse(context, i))
305 .unwrap_or(Zero::zero()),
306 )),
307 "brightness" => Ok(GenericFilter::Brightness(
308 i.try_parse(|i| NonNegativeFactor::parse(context, i))
309 .unwrap_or(NonNegativeFactor::one()),
310 )),
311 "contrast" => Ok(GenericFilter::Contrast(
312 i.try_parse(|i| NonNegativeFactor::parse(context, i))
313 .unwrap_or(NonNegativeFactor::one()),
314 )),
315 "grayscale" => {
316 Ok(GenericFilter::Grayscale(
319 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
320 .unwrap_or(ZeroToOneFactor::one()),
321 ))
322 },
323 "hue-rotate" => {
324 Ok(GenericFilter::HueRotate(
327 i.try_parse(|i| Angle::parse_with_unitless(context, i))
328 .unwrap_or(Zero::zero()),
329 ))
330 },
331 "invert" => {
332 Ok(GenericFilter::Invert(
335 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
336 .unwrap_or(ZeroToOneFactor::one()),
337 ))
338 },
339 "opacity" => {
340 Ok(GenericFilter::Opacity(
343 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
344 .unwrap_or(ZeroToOneFactor::one()),
345 ))
346 },
347 "saturate" => Ok(GenericFilter::Saturate(
348 i.try_parse(|i| NonNegativeFactor::parse(context, i))
349 .unwrap_or(NonNegativeFactor::one()),
350 )),
351 "sepia" => {
352 Ok(GenericFilter::Sepia(
355 i.try_parse(|i| ZeroToOneFactor::parse(context, i))
356 .unwrap_or(ZeroToOneFactor::one()),
357 ))
358 },
359 "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),
360 _ => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
361 }
362 })
363 }
364}
365
366impl Parse for SimpleShadow {
367 #[inline]
368 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
369 let color = input.try_parse(|i| Color::parse(context, i)).ok();
370 let horizontal = Length::parse(context, input)?;
371 let vertical = Length::parse(context, input)?;
372 let blur = input
373 .try_parse(|i| Length::parse_non_negative(context, i))
374 .ok();
375 let blur = blur.map(NonNegative::<Length>);
376 let color = color.or_else(|| input.try_parse(|i| Color::parse(context, i)).ok());
377
378 Ok(SimpleShadow {
379 color,
380 horizontal,
381 vertical,
382 blur,
383 })
384 }
385}
386
387impl ToComputedValue for SimpleShadow {
388 type ComputedValue = ComputedSimpleShadow;
389
390 #[inline]
391 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
392 ComputedSimpleShadow {
393 color: self
394 .color
395 .as_ref()
396 .unwrap_or(&Color::currentcolor())
397 .to_computed_value(context),
398 horizontal: self.horizontal.to_computed_value(context),
399 vertical: self.vertical.to_computed_value(context),
400 blur: self
401 .blur
402 .as_ref()
403 .unwrap_or(&NonNegativeLength::zero())
404 .to_computed_value(context),
405 }
406 }
407
408 #[inline]
409 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
410 SimpleShadow {
411 color: Some(ToComputedValue::from_computed_value(&computed.color)),
412 horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
413 vertical: ToComputedValue::from_computed_value(&computed.vertical),
414 blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
415 }
416 }
417}
418
419#[allow(missing_docs)]
421#[derive(
422 Clone,
423 Copy,
424 Debug,
425 Deserialize,
426 Eq,
427 FromPrimitive,
428 Hash,
429 MallocSizeOf,
430 Parse,
431 PartialEq,
432 Serialize,
433 SpecifiedValueInfo,
434 ToComputedValue,
435 ToCss,
436 ToResolvedValue,
437 ToShmem,
438 ToTyped,
439)]
440#[repr(u8)]
441pub enum Blend {
442 Normal,
443 Multiply,
444 Screen,
445 Overlay,
446 Darken,
447 Lighten,
448 ColorDodge,
449 ColorBurn,
450 HardLight,
451 SoftLight,
452 Difference,
453 Exclusion,
454 Hue,
455 Saturation,
456 Color,
457 Luminosity,
458 PlusLighter,
459}