tor-browser

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

mod.rs (16749B)


      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 //! Animated values.
      6 //!
      7 //! Some values, notably colors, cannot be interpolated directly with their
      8 //! computed values and need yet another intermediate representation. This
      9 //! module's raison d'ĂȘtre is to ultimately contain all these types.
     10 
     11 use crate::color::AbsoluteColor;
     12 use crate::properties::{ComputedValues, PropertyId};
     13 use crate::values::computed::url::ComputedUrl;
     14 use crate::values::computed::{Angle, Image, Length};
     15 use crate::values::generics::{ClampToNonNegative, NonNegative};
     16 use crate::values::specified::SVGPathData;
     17 use crate::values::CSSFloat;
     18 use app_units::Au;
     19 use smallvec::SmallVec;
     20 use std::cmp;
     21 
     22 pub mod color;
     23 pub mod effects;
     24 mod font;
     25 mod grid;
     26 pub mod lists;
     27 mod svg;
     28 pub mod transform;
     29 
     30 /// The category a property falls into for ordering purposes.
     31 ///
     32 /// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
     33 #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
     34 enum PropertyCategory {
     35    Custom,
     36    PhysicalLonghand,
     37    LogicalLonghand,
     38    Shorthand,
     39 }
     40 
     41 impl PropertyCategory {
     42    fn of(id: &PropertyId) -> Self {
     43        match *id {
     44            PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {
     45                Ok(id) => {
     46                    if id.is_logical() {
     47                        PropertyCategory::LogicalLonghand
     48                    } else {
     49                        PropertyCategory::PhysicalLonghand
     50                    }
     51                },
     52                Err(..) => PropertyCategory::Shorthand,
     53            },
     54            PropertyId::Custom(..) => PropertyCategory::Custom,
     55        }
     56    }
     57 }
     58 
     59 /// A comparator to sort PropertyIds such that physical longhands are sorted
     60 /// before logical longhands and shorthands, shorthands with fewer components
     61 /// are sorted before shorthands with more components, and otherwise shorthands
     62 /// are sorted by IDL name as defined by [Web Animations][property-order].
     63 ///
     64 /// Using this allows us to prioritize values specified by longhands (or smaller
     65 /// shorthand subsets) when longhands and shorthands are both specified on the
     66 /// one keyframe.
     67 ///
     68 /// [property-order] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
     69 pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {
     70    let a_category = PropertyCategory::of(a);
     71    let b_category = PropertyCategory::of(b);
     72 
     73    if a_category != b_category {
     74        return a_category.cmp(&b_category);
     75    }
     76 
     77    if a_category != PropertyCategory::Shorthand {
     78        return cmp::Ordering::Equal;
     79    }
     80 
     81    let a = a.as_shorthand().unwrap();
     82    let b = b.as_shorthand().unwrap();
     83    // Within shorthands, sort by the number of subproperties, then by IDL
     84    // name.
     85    let subprop_count_a = a.longhands().count();
     86    let subprop_count_b = b.longhands().count();
     87    subprop_count_a
     88        .cmp(&subprop_count_b)
     89        .then_with(|| a.idl_name_sort_order().cmp(&b.idl_name_sort_order()))
     90 }
     91 
     92 /// A helper function to animate two multiplicative factor.
     93 pub fn animate_multiplicative_factor(
     94    this: CSSFloat,
     95    other: CSSFloat,
     96    procedure: Procedure,
     97 ) -> Result<CSSFloat, ()> {
     98    Ok((this - 1.).animate(&(other - 1.), procedure)? + 1.)
     99 }
    100 
    101 /// Animate from one value to another.
    102 ///
    103 /// This trait is derivable with `#[derive(Animate)]`. The derived
    104 /// implementation uses a `match` expression with identical patterns for both
    105 /// `self` and `other`, calling `Animate::animate` on each fields of the values.
    106 /// If a field is annotated with `#[animation(constant)]`, the two values should
    107 /// be equal or an error is returned.
    108 ///
    109 /// If a variant is annotated with `#[animation(error)]`, the corresponding
    110 /// `match` arm returns an error.
    111 ///
    112 /// Trait bounds for type parameter `Foo` can be opted out of with
    113 /// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for
    114 /// fields can be opted into with `#[animation(field_bound)]` on the field.
    115 pub trait Animate: Sized {
    116    /// Animate a value towards another one, given an animation procedure.
    117    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>;
    118 }
    119 
    120 /// An animation procedure.
    121 ///
    122 /// <https://drafts.csswg.org/web-animations/#procedures-for-animating-properties>
    123 #[allow(missing_docs)]
    124 #[derive(Clone, Copy, Debug, PartialEq)]
    125 pub enum Procedure {
    126    /// <https://drafts.csswg.org/web-animations/#animation-interpolation>
    127    Interpolate { progress: f64 },
    128    /// <https://drafts.csswg.org/web-animations/#animation-addition>
    129    Add,
    130    /// <https://drafts.csswg.org/web-animations/#animation-accumulation>
    131    Accumulate { count: u64 },
    132 }
    133 
    134 /// The context needed to provide an animated value from a computed value.
    135 pub struct Context<'a> {
    136    /// The computed style we're taking the value from.
    137    pub style: &'a ComputedValues,
    138 }
    139 
    140 /// Conversion between computed values and intermediate values for animations.
    141 ///
    142 /// Notably, colors are represented as four floats during animations.
    143 ///
    144 /// This trait is derivable with `#[derive(ToAnimatedValue)]`.
    145 pub trait ToAnimatedValue {
    146    /// The type of the animated value.
    147    type AnimatedValue;
    148 
    149    /// Converts this value to an animated value.
    150    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue;
    151 
    152    /// Converts back an animated value into a computed value.
    153    fn from_animated_value(animated: Self::AnimatedValue) -> Self;
    154 }
    155 
    156 /// Returns a value similar to `self` that represents zero.
    157 ///
    158 /// This trait is derivable with `#[derive(ToAnimatedValue)]`. If a field is
    159 /// annotated with `#[animation(constant)]`, a clone of its value will be used
    160 /// instead of calling `ToAnimatedZero::to_animated_zero` on it.
    161 ///
    162 /// If a variant is annotated with `#[animation(error)]`, the corresponding
    163 /// `match` arm is not generated.
    164 ///
    165 /// Trait bounds for type parameter `Foo` can be opted out of with
    166 /// `#[animation(no_bound(Foo))]` on the type definition.
    167 pub trait ToAnimatedZero: Sized {
    168    /// Returns a value that, when added with an underlying value, will produce the underlying
    169    /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from
    170    /// the zero value to the 'by' value, and then adds the result to the underlying value.
    171    ///
    172    /// This is not the necessarily the same as the initial value of a property. For example, the
    173    /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the
    174    /// underlying value will not produce the underlying value.
    175    fn to_animated_zero(&self) -> Result<Self, ()>;
    176 }
    177 
    178 impl Procedure {
    179    /// Returns this procedure as a pair of weights.
    180    ///
    181    /// This is useful for animations that don't animate differently
    182    /// depending on the used procedure.
    183    #[inline]
    184    pub fn weights(self) -> (f64, f64) {
    185        match self {
    186            Procedure::Interpolate { progress } => (1. - progress, progress),
    187            Procedure::Add => (1., 1.),
    188            Procedure::Accumulate { count } => (count as f64, 1.),
    189        }
    190    }
    191 }
    192 
    193 /// <https://drafts.csswg.org/css-transitions/#animtype-number>
    194 impl Animate for i32 {
    195    #[inline]
    196    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
    197        Ok(((*self as f64).animate(&(*other as f64), procedure)? + 0.5).floor() as i32)
    198    }
    199 }
    200 
    201 /// <https://drafts.csswg.org/css-transitions/#animtype-number>
    202 impl Animate for f32 {
    203    #[inline]
    204    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
    205        let ret = (*self as f64).animate(&(*other as f64), procedure)?;
    206        Ok(ret.min(f32::MAX as f64).max(f32::MIN as f64) as f32)
    207    }
    208 }
    209 
    210 /// <https://drafts.csswg.org/css-transitions/#animtype-number>
    211 impl Animate for f64 {
    212    #[inline]
    213    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
    214        let (self_weight, other_weight) = procedure.weights();
    215 
    216        let ret = *self * self_weight + *other * other_weight;
    217        Ok(ret.min(f64::MAX).max(f64::MIN))
    218    }
    219 }
    220 
    221 impl<T> Animate for Option<T>
    222 where
    223    T: Animate,
    224 {
    225    #[inline]
    226    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
    227        match (self.as_ref(), other.as_ref()) {
    228            (Some(ref this), Some(ref other)) => Ok(Some(this.animate(other, procedure)?)),
    229            (None, None) => Ok(None),
    230            _ => Err(()),
    231        }
    232    }
    233 }
    234 
    235 impl<T: ToAnimatedValue + ClampToNonNegative> ToAnimatedValue for NonNegative<T> {
    236    type AnimatedValue = NonNegative<<T as ToAnimatedValue>::AnimatedValue>;
    237 
    238    #[inline]
    239    fn to_animated_value(self, cx: &crate::values::animated::Context) -> Self::AnimatedValue {
    240        NonNegative(self.0.to_animated_value(cx))
    241    }
    242 
    243    #[inline]
    244    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    245        Self(<T as ToAnimatedValue>::from_animated_value(animated.0).clamp_to_non_negative())
    246    }
    247 }
    248 
    249 impl ToAnimatedValue for Au {
    250    type AnimatedValue = Length;
    251 
    252    #[inline]
    253    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {
    254        Length::new(self.to_f32_px()).to_animated_value(context)
    255    }
    256 
    257    #[inline]
    258    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    259        Au::from_f32_px(Length::from_animated_value(animated).px())
    260    }
    261 }
    262 
    263 impl<T: Animate> Animate for Box<T> {
    264    #[inline]
    265    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
    266        Ok(Box::new((**self).animate(&other, procedure)?))
    267    }
    268 }
    269 
    270 impl<T> ToAnimatedValue for Option<T>
    271 where
    272    T: ToAnimatedValue,
    273 {
    274    type AnimatedValue = Option<<T as ToAnimatedValue>::AnimatedValue>;
    275 
    276    #[inline]
    277    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {
    278        self.map(|v| T::to_animated_value(v, context))
    279    }
    280 
    281    #[inline]
    282    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    283        animated.map(T::from_animated_value)
    284    }
    285 }
    286 
    287 impl<T> ToAnimatedValue for Vec<T>
    288 where
    289    T: ToAnimatedValue,
    290 {
    291    type AnimatedValue = Vec<<T as ToAnimatedValue>::AnimatedValue>;
    292 
    293    #[inline]
    294    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {
    295        self.into_iter()
    296            .map(|v| v.to_animated_value(context))
    297            .collect()
    298    }
    299 
    300    #[inline]
    301    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    302        animated.into_iter().map(T::from_animated_value).collect()
    303    }
    304 }
    305 
    306 impl<T> ToAnimatedValue for thin_vec::ThinVec<T>
    307 where
    308    T: ToAnimatedValue,
    309 {
    310    type AnimatedValue = thin_vec::ThinVec<<T as ToAnimatedValue>::AnimatedValue>;
    311 
    312    #[inline]
    313    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {
    314        self.into_iter()
    315            .map(|v| v.to_animated_value(context))
    316            .collect()
    317    }
    318 
    319    #[inline]
    320    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    321        animated.into_iter().map(T::from_animated_value).collect()
    322    }
    323 }
    324 
    325 impl<T> ToAnimatedValue for Box<T>
    326 where
    327    T: ToAnimatedValue,
    328 {
    329    type AnimatedValue = Box<<T as ToAnimatedValue>::AnimatedValue>;
    330 
    331    #[inline]
    332    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {
    333        Box::new((*self).to_animated_value(context))
    334    }
    335 
    336    #[inline]
    337    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    338        Box::new(T::from_animated_value(*animated))
    339    }
    340 }
    341 
    342 impl<T> ToAnimatedValue for Box<[T]>
    343 where
    344    T: ToAnimatedValue,
    345 {
    346    type AnimatedValue = Box<[<T as ToAnimatedValue>::AnimatedValue]>;
    347 
    348    #[inline]
    349    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {
    350        self.into_vec()
    351            .into_iter()
    352            .map(|v| v.to_animated_value(context))
    353            .collect()
    354    }
    355 
    356    #[inline]
    357    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    358        animated
    359            .into_vec()
    360            .into_iter()
    361            .map(T::from_animated_value)
    362            .collect()
    363    }
    364 }
    365 
    366 impl<T> ToAnimatedValue for crate::OwnedSlice<T>
    367 where
    368    T: ToAnimatedValue,
    369 {
    370    type AnimatedValue = crate::OwnedSlice<<T as ToAnimatedValue>::AnimatedValue>;
    371 
    372    #[inline]
    373    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {
    374        self.into_box().to_animated_value(context).into()
    375    }
    376 
    377    #[inline]
    378    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    379        Self::from(Box::from_animated_value(animated.into_box()))
    380    }
    381 }
    382 
    383 impl<T> ToAnimatedValue for SmallVec<[T; 1]>
    384 where
    385    T: ToAnimatedValue,
    386 {
    387    type AnimatedValue = SmallVec<[T::AnimatedValue; 1]>;
    388 
    389    #[inline]
    390    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {
    391        self.into_iter()
    392            .map(|v| v.to_animated_value(context))
    393            .collect()
    394    }
    395 
    396    #[inline]
    397    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    398        animated.into_iter().map(T::from_animated_value).collect()
    399    }
    400 }
    401 
    402 macro_rules! trivial_to_animated_value {
    403    ($ty:ty) => {
    404        impl $crate::values::animated::ToAnimatedValue for $ty {
    405            type AnimatedValue = Self;
    406 
    407            #[inline]
    408            fn to_animated_value(self, _: &Context) -> Self {
    409                self
    410            }
    411 
    412            #[inline]
    413            fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    414                animated
    415            }
    416        }
    417    };
    418 }
    419 
    420 trivial_to_animated_value!(crate::Atom);
    421 trivial_to_animated_value!(Angle);
    422 trivial_to_animated_value!(ComputedUrl);
    423 trivial_to_animated_value!(bool);
    424 trivial_to_animated_value!(f32);
    425 trivial_to_animated_value!(i32);
    426 trivial_to_animated_value!(u32);
    427 trivial_to_animated_value!(usize);
    428 trivial_to_animated_value!(AbsoluteColor);
    429 trivial_to_animated_value!(crate::values::generics::color::ColorMixFlags);
    430 // Note: This implementation is for ToAnimatedValue of ShapeSource.
    431 //
    432 // SVGPathData uses Box<[T]>. If we want to derive ToAnimatedValue for all the
    433 // types, we have to do "impl ToAnimatedValue for Box<[T]>" first.
    434 // However, the general version of "impl ToAnimatedValue for Box<[T]>" needs to
    435 // clone |T| and convert it into |T::AnimatedValue|. However, for SVGPathData
    436 // that is unnecessary--moving |T| is sufficient. So here, we implement this
    437 // trait manually.
    438 trivial_to_animated_value!(SVGPathData);
    439 // FIXME: Bug 1514342, Image is not animatable, but we still need to implement
    440 // this to avoid adding this derive to generic::Image and all its arms. We can
    441 // drop this after landing Bug 1514342.
    442 trivial_to_animated_value!(Image);
    443 
    444 impl ToAnimatedZero for Au {
    445    #[inline]
    446    fn to_animated_zero(&self) -> Result<Self, ()> {
    447        Ok(Au(0))
    448    }
    449 }
    450 
    451 impl ToAnimatedZero for f32 {
    452    #[inline]
    453    fn to_animated_zero(&self) -> Result<Self, ()> {
    454        Ok(0.)
    455    }
    456 }
    457 
    458 impl ToAnimatedZero for f64 {
    459    #[inline]
    460    fn to_animated_zero(&self) -> Result<Self, ()> {
    461        Ok(0.)
    462    }
    463 }
    464 
    465 impl ToAnimatedZero for i32 {
    466    #[inline]
    467    fn to_animated_zero(&self) -> Result<Self, ()> {
    468        Ok(0)
    469    }
    470 }
    471 
    472 impl<T> ToAnimatedZero for Box<T>
    473 where
    474    T: ToAnimatedZero,
    475 {
    476    #[inline]
    477    fn to_animated_zero(&self) -> Result<Self, ()> {
    478        Ok(Box::new((**self).to_animated_zero()?))
    479    }
    480 }
    481 
    482 impl<T> ToAnimatedZero for Option<T>
    483 where
    484    T: ToAnimatedZero,
    485 {
    486    #[inline]
    487    fn to_animated_zero(&self) -> Result<Self, ()> {
    488        match *self {
    489            Some(ref value) => Ok(Some(value.to_animated_zero()?)),
    490            None => Ok(None),
    491        }
    492    }
    493 }
    494 
    495 impl<T> ToAnimatedZero for Vec<T>
    496 where
    497    T: ToAnimatedZero,
    498 {
    499    #[inline]
    500    fn to_animated_zero(&self) -> Result<Self, ()> {
    501        self.iter().map(|v| v.to_animated_zero()).collect()
    502    }
    503 }
    504 
    505 impl<T> ToAnimatedZero for thin_vec::ThinVec<T>
    506 where
    507    T: ToAnimatedZero,
    508 {
    509    #[inline]
    510    fn to_animated_zero(&self) -> Result<Self, ()> {
    511        self.iter().map(|v| v.to_animated_zero()).collect()
    512    }
    513 }
    514 
    515 impl<T> ToAnimatedZero for Box<[T]>
    516 where
    517    T: ToAnimatedZero,
    518 {
    519    #[inline]
    520    fn to_animated_zero(&self) -> Result<Self, ()> {
    521        self.iter().map(|v| v.to_animated_zero()).collect()
    522    }
    523 }
    524 
    525 impl<T> ToAnimatedZero for crate::OwnedSlice<T>
    526 where
    527    T: ToAnimatedZero,
    528 {
    529    #[inline]
    530    fn to_animated_zero(&self) -> Result<Self, ()> {
    531        self.iter().map(|v| v.to_animated_zero()).collect()
    532    }
    533 }
    534 
    535 impl<T> ToAnimatedZero for crate::ArcSlice<T>
    536 where
    537    T: ToAnimatedZero,
    538 {
    539    #[inline]
    540    fn to_animated_zero(&self) -> Result<Self, ()> {
    541        let v = self
    542            .iter()
    543            .map(|v| v.to_animated_zero())
    544            .collect::<Result<Vec<_>, _>>()?;
    545        Ok(crate::ArcSlice::from_iter(v.into_iter()))
    546    }
    547 }