tor-browser

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

motion.rs (10407B)


      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 that are related to motion path.
      6 
      7 use crate::derives::*;
      8 use crate::parser::{Parse, ParserContext};
      9 use crate::values::computed::motion::OffsetRotate as ComputedOffsetRotate;
     10 use crate::values::computed::{Context, ToComputedValue};
     11 use crate::values::generics::motion as generics;
     12 use crate::values::specified::basic_shape::BasicShape;
     13 use crate::values::specified::position::{HorizontalPosition, VerticalPosition};
     14 use crate::values::specified::url::SpecifiedUrl;
     15 use crate::values::specified::{Angle, Position};
     16 use crate::Zero;
     17 use cssparser::Parser;
     18 use style_traits::{ParseError, StyleParseErrorKind};
     19 
     20 /// The specified value of ray() function.
     21 pub type RayFunction = generics::GenericRayFunction<Angle, Position>;
     22 
     23 /// The specified value of <offset-path>.
     24 pub type OffsetPathFunction =
     25    generics::GenericOffsetPathFunction<BasicShape, RayFunction, SpecifiedUrl>;
     26 
     27 /// The specified value of `offset-path`.
     28 pub type OffsetPath = generics::GenericOffsetPath<OffsetPathFunction>;
     29 
     30 /// The specified value of `offset-position`.
     31 pub type OffsetPosition = generics::GenericOffsetPosition<HorizontalPosition, VerticalPosition>;
     32 
     33 /// The <coord-box> value, which defines the box that the <offset-path> sizes into.
     34 /// https://drafts.fxtf.org/motion-1/#valdef-offset-path-coord-box
     35 ///
     36 /// <coord-box> = content-box | padding-box | border-box | fill-box | stroke-box | view-box
     37 /// https://drafts.csswg.org/css-box-4/#typedef-coord-box
     38 #[allow(missing_docs)]
     39 #[derive(
     40    Animate,
     41    Clone,
     42    ComputeSquaredDistance,
     43    Copy,
     44    Debug,
     45    Deserialize,
     46    MallocSizeOf,
     47    Parse,
     48    PartialEq,
     49    Serialize,
     50    SpecifiedValueInfo,
     51    ToAnimatedValue,
     52    ToComputedValue,
     53    ToCss,
     54    ToResolvedValue,
     55    ToShmem,
     56 )]
     57 #[repr(u8)]
     58 pub enum CoordBox {
     59    ContentBox,
     60    PaddingBox,
     61    BorderBox,
     62    FillBox,
     63    StrokeBox,
     64    ViewBox,
     65 }
     66 
     67 impl CoordBox {
     68    /// Returns true if it is default value, border-box.
     69    #[inline]
     70    pub fn is_default(&self) -> bool {
     71        matches!(*self, Self::BorderBox)
     72    }
     73 }
     74 
     75 impl Parse for RayFunction {
     76    fn parse<'i, 't>(
     77        context: &ParserContext,
     78        input: &mut Parser<'i, 't>,
     79    ) -> Result<Self, ParseError<'i>> {
     80        input.expect_function_matching("ray")?;
     81        input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
     82    }
     83 }
     84 
     85 impl RayFunction {
     86    /// Parse the inner arguments of a `ray` function.
     87    fn parse_function_arguments<'i, 't>(
     88        context: &ParserContext,
     89        input: &mut Parser<'i, 't>,
     90    ) -> Result<Self, ParseError<'i>> {
     91        use crate::values::specified::PositionOrAuto;
     92 
     93        let mut angle = None;
     94        let mut size = None;
     95        let mut contain = false;
     96        let mut position = None;
     97        loop {
     98            if angle.is_none() {
     99                angle = input.try_parse(|i| Angle::parse(context, i)).ok();
    100            }
    101 
    102            if size.is_none() {
    103                size = input.try_parse(generics::RaySize::parse).ok();
    104                if size.is_some() {
    105                    continue;
    106                }
    107            }
    108 
    109            if !contain {
    110                contain = input
    111                    .try_parse(|i| i.expect_ident_matching("contain"))
    112                    .is_ok();
    113                if contain {
    114                    continue;
    115                }
    116            }
    117 
    118            if position.is_none() {
    119                if input.try_parse(|i| i.expect_ident_matching("at")).is_ok() {
    120                    let pos = Position::parse(context, input)?;
    121                    position = Some(PositionOrAuto::Position(pos));
    122                }
    123 
    124                if position.is_some() {
    125                    continue;
    126                }
    127            }
    128            break;
    129        }
    130 
    131        if angle.is_none() {
    132            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    133        }
    134 
    135        Ok(RayFunction {
    136            angle: angle.unwrap(),
    137            // If no <ray-size> is specified it defaults to closest-side.
    138            size: size.unwrap_or(generics::RaySize::ClosestSide),
    139            contain,
    140            position: position.unwrap_or(PositionOrAuto::auto()),
    141        })
    142    }
    143 }
    144 
    145 impl Parse for OffsetPathFunction {
    146    fn parse<'i, 't>(
    147        context: &ParserContext,
    148        input: &mut Parser<'i, 't>,
    149    ) -> Result<Self, ParseError<'i>> {
    150        use crate::values::specified::basic_shape::{AllowedBasicShapes, ShapeType};
    151 
    152        // <offset-path> = <ray()> | <url> | <basic-shape>
    153        // https://drafts.fxtf.org/motion-1/#typedef-offset-path
    154        if let Ok(ray) = input.try_parse(|i| RayFunction::parse(context, i)) {
    155            return Ok(OffsetPathFunction::Ray(ray));
    156        }
    157 
    158        if static_prefs::pref!("layout.css.motion-path-url.enabled") {
    159            if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
    160                return Ok(OffsetPathFunction::Url(url));
    161            }
    162        }
    163 
    164        BasicShape::parse(context, input, AllowedBasicShapes::ALL, ShapeType::Outline)
    165            .map(OffsetPathFunction::Shape)
    166    }
    167 }
    168 
    169 impl Parse for OffsetPath {
    170    fn parse<'i, 't>(
    171        context: &ParserContext,
    172        input: &mut Parser<'i, 't>,
    173    ) -> Result<Self, ParseError<'i>> {
    174        // Parse none.
    175        if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
    176            return Ok(OffsetPath::none());
    177        }
    178 
    179        let mut path = None;
    180        let mut coord_box = None;
    181        loop {
    182            if path.is_none() {
    183                path = input
    184                    .try_parse(|i| OffsetPathFunction::parse(context, i))
    185                    .ok();
    186            }
    187 
    188            if coord_box.is_none() {
    189                coord_box = input.try_parse(CoordBox::parse).ok();
    190                if coord_box.is_some() {
    191                    continue;
    192                }
    193            }
    194            break;
    195        }
    196 
    197        if let Some(p) = path {
    198            return Ok(OffsetPath::OffsetPath {
    199                path: Box::new(p),
    200                coord_box: coord_box.unwrap_or(CoordBox::BorderBox),
    201            });
    202        }
    203 
    204        match coord_box {
    205            Some(c) => Ok(OffsetPath::CoordBox(c)),
    206            None => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
    207        }
    208    }
    209 }
    210 
    211 /// The direction of offset-rotate.
    212 #[derive(
    213    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
    214 )]
    215 #[repr(u8)]
    216 pub enum OffsetRotateDirection {
    217    /// Unspecified direction keyword.
    218    #[css(skip)]
    219    None,
    220    /// 0deg offset (face forward).
    221    Auto,
    222    /// 180deg offset (face backward).
    223    Reverse,
    224 }
    225 
    226 impl OffsetRotateDirection {
    227    /// Returns true if it is none (i.e. the keyword is not specified).
    228    #[inline]
    229    fn is_none(&self) -> bool {
    230        *self == OffsetRotateDirection::None
    231    }
    232 }
    233 
    234 #[inline]
    235 fn direction_specified_and_angle_is_zero(direction: &OffsetRotateDirection, angle: &Angle) -> bool {
    236    !direction.is_none() && angle.is_zero()
    237 }
    238 
    239 /// The specified offset-rotate.
    240 /// The syntax is: "[ auto | reverse ] || <angle>"
    241 ///
    242 /// https://drafts.fxtf.org/motion-1/#offset-rotate-property
    243 #[derive(
    244    Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
    245 )]
    246 pub struct OffsetRotate {
    247    /// [auto | reverse].
    248    #[css(skip_if = "OffsetRotateDirection::is_none")]
    249    direction: OffsetRotateDirection,
    250    /// <angle>.
    251    /// If direction is None, this is a fixed angle which indicates a
    252    /// constant clockwise rotation transformation applied to it by this
    253    /// specified rotation angle. Otherwise, the angle will be added to
    254    /// the angle of the direction in layout.
    255    #[css(contextual_skip_if = "direction_specified_and_angle_is_zero")]
    256    angle: Angle,
    257 }
    258 
    259 impl OffsetRotate {
    260    /// Returns the initial value, auto.
    261    #[inline]
    262    pub fn auto() -> Self {
    263        OffsetRotate {
    264            direction: OffsetRotateDirection::Auto,
    265            angle: Angle::zero(),
    266        }
    267    }
    268 
    269    /// Returns true if self is auto 0deg.
    270    #[inline]
    271    pub fn is_auto(&self) -> bool {
    272        self.direction == OffsetRotateDirection::Auto && self.angle.is_zero()
    273    }
    274 }
    275 
    276 impl Parse for OffsetRotate {
    277    fn parse<'i, 't>(
    278        context: &ParserContext,
    279        input: &mut Parser<'i, 't>,
    280    ) -> Result<Self, ParseError<'i>> {
    281        let location = input.current_source_location();
    282        let mut direction = input.try_parse(OffsetRotateDirection::parse);
    283        let angle = input.try_parse(|i| Angle::parse(context, i));
    284        if direction.is_err() {
    285            // The direction and angle could be any order, so give it a change to parse
    286            // direction again.
    287            direction = input.try_parse(OffsetRotateDirection::parse);
    288        }
    289 
    290        if direction.is_err() && angle.is_err() {
    291            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    292        }
    293 
    294        Ok(OffsetRotate {
    295            direction: direction.unwrap_or(OffsetRotateDirection::None),
    296            angle: angle.unwrap_or(Zero::zero()),
    297        })
    298    }
    299 }
    300 
    301 impl ToComputedValue for OffsetRotate {
    302    type ComputedValue = ComputedOffsetRotate;
    303 
    304    #[inline]
    305    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
    306        use crate::values::computed::Angle as ComputedAngle;
    307 
    308        ComputedOffsetRotate {
    309            auto: !self.direction.is_none(),
    310            angle: if self.direction == OffsetRotateDirection::Reverse {
    311                // The computed value should always convert "reverse" into "auto".
    312                // e.g. "reverse calc(20deg + 10deg)" => "auto 210deg"
    313                self.angle.to_computed_value(context) + ComputedAngle::from_degrees(180.0)
    314            } else {
    315                self.angle.to_computed_value(context)
    316            },
    317        }
    318    }
    319 
    320    #[inline]
    321    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
    322        OffsetRotate {
    323            direction: if computed.auto {
    324                OffsetRotateDirection::Auto
    325            } else {
    326                OffsetRotateDirection::None
    327            },
    328            angle: ToComputedValue::from_computed_value(&computed.angle),
    329        }
    330    }
    331 }