tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

effects.rs (17010B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! Specified types for CSS values related to effects.
      6 
      7 use crate::derives::*;
      8 use crate::parser::{Parse, ParserContext};
      9 use crate::values::computed::effects::BoxShadow as ComputedBoxShadow;
     10 use crate::values::computed::effects::SimpleShadow as ComputedSimpleShadow;
     11 #[cfg(feature = "gecko")]
     12 use crate::values::computed::url::ComputedUrl;
     13 use crate::values::computed::Angle as ComputedAngle;
     14 use crate::values::computed::CSSPixelLength as ComputedCSSPixelLength;
     15 use crate::values::computed::Filter as ComputedFilter;
     16 use crate::values::computed::NonNegativeLength as ComputedNonNegativeLength;
     17 use crate::values::computed::NonNegativeNumber as ComputedNonNegativeNumber;
     18 use crate::values::computed::Number as ComputedNumber;
     19 use crate::values::computed::ZeroToOneNumber as ComputedZeroToOneNumber;
     20 use crate::values::computed::{Context, ToComputedValue};
     21 use crate::values::generics::effects::BoxShadow as GenericBoxShadow;
     22 use crate::values::generics::effects::Filter as GenericFilter;
     23 use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
     24 use crate::values::generics::{NonNegative, ZeroToOne};
     25 use crate::values::specified::color::Color;
     26 use crate::values::specified::length::{Length, NonNegativeLength};
     27 #[cfg(feature = "gecko")]
     28 use crate::values::specified::url::SpecifiedUrl;
     29 use crate::values::specified::{Angle, NonNegativeNumberOrPercentage, Number, NumberOrPercentage};
     30 #[cfg(feature = "servo")]
     31 use crate::values::Impossible;
     32 use crate::Zero;
     33 use cssparser::{match_ignore_ascii_case, BasicParseErrorKind, Parser, Token};
     34 use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};
     35 
     36 /// A specified value for a single shadow of the `box-shadow` property.
     37 pub type BoxShadow =
     38    GenericBoxShadow<Option<Color>, Length, Option<NonNegativeLength>, Option<Length>>;
     39 
     40 /// A specified value for a single `filter`.
     41 #[cfg(feature = "gecko")]
     42 pub type SpecifiedFilter = GenericFilter<Angle, FilterFactor, Length, SimpleShadow, SpecifiedUrl>;
     43 
     44 /// A specified value for a single `filter`.
     45 #[cfg(feature = "servo")]
     46 pub type SpecifiedFilter = GenericFilter<Angle, FilterFactor, Length, SimpleShadow, Impossible>;
     47 
     48 pub use self::SpecifiedFilter as Filter;
     49 
     50 /// The factor for a filter function.
     51 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
     52 pub struct FilterFactor(NumberOrPercentage);
     53 
     54 impl FilterFactor {
     55    fn to_number(&self) -> Number {
     56        self.0.to_number()
     57    }
     58 }
     59 
     60 impl 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 /// Clamp the value to 1 if the value is over 100%.
     71 #[inline]
     72 fn 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 
     81 type NonNegativeFactor = NonNegative<FilterFactor>;
     82 impl NonNegativeFactor {
     83    fn one() -> Self {
     84        Self(FilterFactor(NumberOrPercentage::Number(Number::new(1.))))
     85    }
     86 }
     87 
     88 impl 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 
     99 type ZeroToOneFactor = ZeroToOne<FilterFactor>;
    100 impl ZeroToOneFactor {
    101    fn one() -> Self {
    102        Self(FilterFactor(NumberOrPercentage::Number(Number::new(1.))))
    103    }
    104 }
    105 
    106 impl 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 
    118 /// A specified value for the `drop-shadow()` filter.
    119 pub type SimpleShadow = GenericSimpleShadow<Option<Color>, Length, Option<NonNegativeLength>>;
    120 
    121 impl 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 
    183 impl 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 
    209 // We need this for converting the specified Filter into computed Filter without Context (for
    210 // some FFIs in glue.rs). This can fail because in some circumstances, we still need Context to
    211 // determine the computed value.
    212 impl Filter {
    213    /// Generate the ComputedFilter without Context.
    214    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 
    290 impl 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                    // Values of amount over 100% are allowed but UAs must clamp the values to 1.
    327                    // https://drafts.fxtf.org/filter-effects/#funcdef-filter-grayscale
    328                    Ok(GenericFilter::Grayscale(
    329                        i.try_parse(|i| ZeroToOneFactor::parse(context, i))
    330                         .unwrap_or(ZeroToOneFactor::one()),
    331                    ))
    332                },
    333                "hue-rotate" => {
    334                    // We allow unitless zero here, see:
    335                    // https://github.com/w3c/fxtf-drafts/issues/228
    336                    Ok(GenericFilter::HueRotate(
    337                        i.try_parse(|i| Angle::parse_with_unitless(context, i))
    338                         .unwrap_or(Zero::zero()),
    339                    ))
    340                },
    341                "invert" => {
    342                    // Values of amount over 100% are allowed but UAs must clamp the values to 1.
    343                    // https://drafts.fxtf.org/filter-effects/#funcdef-filter-invert
    344                    Ok(GenericFilter::Invert(
    345                        i.try_parse(|i| ZeroToOneFactor::parse(context, i))
    346                         .unwrap_or(ZeroToOneFactor::one()),
    347                    ))
    348                },
    349                "opacity" => {
    350                    // Values of amount over 100% are allowed but UAs must clamp the values to 1.
    351                    // https://drafts.fxtf.org/filter-effects/#funcdef-filter-opacity
    352                    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                    // Values of amount over 100% are allowed but UAs must clamp the values to 1.
    363                    // https://drafts.fxtf.org/filter-effects/#funcdef-filter-sepia
    364                    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 
    378 impl 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 
    402 impl 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 }