tor-browser

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

basic_shape.rs (36741B)


      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 //! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
      6 //! types that are generic over their `ToCss` implementations.
      7 
      8 use crate::derives::*;
      9 use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};
     10 use crate::values::computed::Percentage;
     11 use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
     12 use crate::values::generics::{
     13    border::GenericBorderRadius,
     14    position::{GenericPosition, GenericPositionOrAuto},
     15    rect::Rect,
     16    NonNegative, Optional,
     17 };
     18 use crate::values::specified::svg_path::{PathCommand, SVGPathData};
     19 use crate::Zero;
     20 use std::fmt::{self, Write};
     21 use style_traits::{CssWriter, ToCss};
     22 
     23 /// A generic value for `<position>` in circle(), ellipse(), and shape().
     24 pub type ShapePosition<LengthPercentage> = GenericPosition<LengthPercentage, LengthPercentage>;
     25 
     26 /// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>
     27 #[allow(missing_docs)]
     28 #[derive(
     29    Animate,
     30    Clone,
     31    ComputeSquaredDistance,
     32    Copy,
     33    Debug,
     34    MallocSizeOf,
     35    PartialEq,
     36    Parse,
     37    SpecifiedValueInfo,
     38    ToAnimatedValue,
     39    ToComputedValue,
     40    ToCss,
     41    ToResolvedValue,
     42    ToShmem,
     43    ToTyped,
     44 )]
     45 #[repr(u8)]
     46 #[typed_value(derive_fields)]
     47 pub enum ShapeGeometryBox {
     48    /// Depending on which kind of element this style value applied on, the
     49    /// default value of the reference-box can be different.  For an HTML
     50    /// element, the default value of reference-box is border-box; for an SVG
     51    /// element, the default value is fill-box.  Since we can not determine the
     52    /// default value at parsing time, we keep this value to make a decision on
     53    /// it.
     54    #[css(skip)]
     55    ElementDependent,
     56    FillBox,
     57    StrokeBox,
     58    ViewBox,
     59    ShapeBox(ShapeBox),
     60 }
     61 
     62 impl Default for ShapeGeometryBox {
     63    fn default() -> Self {
     64        Self::ElementDependent
     65    }
     66 }
     67 
     68 /// Skip the serialization if the author omits the box or specifies border-box.
     69 #[inline]
     70 fn is_default_box_for_clip_path(b: &ShapeGeometryBox) -> bool {
     71    // Note: for clip-path, ElementDependent is always border-box, so we have to check both of them
     72    // for serialization.
     73    matches!(b, ShapeGeometryBox::ElementDependent)
     74        || matches!(b, ShapeGeometryBox::ShapeBox(ShapeBox::BorderBox))
     75 }
     76 
     77 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
     78 #[allow(missing_docs)]
     79 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
     80 #[derive(
     81    Animate,
     82    Clone,
     83    Copy,
     84    ComputeSquaredDistance,
     85    Debug,
     86    Eq,
     87    MallocSizeOf,
     88    Parse,
     89    PartialEq,
     90    SpecifiedValueInfo,
     91    ToAnimatedValue,
     92    ToComputedValue,
     93    ToCss,
     94    ToResolvedValue,
     95    ToShmem,
     96    ToTyped,
     97 )]
     98 #[repr(u8)]
     99 pub enum ShapeBox {
    100    MarginBox,
    101    BorderBox,
    102    PaddingBox,
    103    ContentBox,
    104 }
    105 
    106 impl Default for ShapeBox {
    107    fn default() -> Self {
    108        ShapeBox::MarginBox
    109    }
    110 }
    111 
    112 /// A value for the `clip-path` property.
    113 #[allow(missing_docs)]
    114 #[derive(
    115    Animate,
    116    Clone,
    117    ComputeSquaredDistance,
    118    Debug,
    119    MallocSizeOf,
    120    PartialEq,
    121    SpecifiedValueInfo,
    122    ToAnimatedValue,
    123    ToComputedValue,
    124    ToCss,
    125    ToResolvedValue,
    126    ToShmem,
    127    ToTyped,
    128 )]
    129 #[animation(no_bound(U))]
    130 #[repr(u8)]
    131 #[typed_value(derive_fields)]
    132 pub enum GenericClipPath<BasicShape, U> {
    133    #[animation(error)]
    134    None,
    135    #[animation(error)]
    136    // XXX This will likely change to skip since it seems Typed OM Level 1
    137    // won't be updated to cover this case even though there's some preparation
    138    // in WPT tests for this.
    139    #[typed_value(todo)]
    140    Url(U),
    141    #[typed_value(skip)]
    142    Shape(
    143        #[animation(field_bound)] Box<BasicShape>,
    144        #[css(skip_if = "is_default_box_for_clip_path")] ShapeGeometryBox,
    145    ),
    146    #[animation(error)]
    147    Box(ShapeGeometryBox),
    148 }
    149 
    150 pub use self::GenericClipPath as ClipPath;
    151 
    152 /// A value for the `shape-outside` property.
    153 #[allow(missing_docs)]
    154 #[derive(
    155    Animate,
    156    Clone,
    157    ComputeSquaredDistance,
    158    Debug,
    159    MallocSizeOf,
    160    PartialEq,
    161    SpecifiedValueInfo,
    162    ToAnimatedValue,
    163    ToComputedValue,
    164    ToCss,
    165    ToResolvedValue,
    166    ToShmem,
    167    ToTyped,
    168 )]
    169 #[animation(no_bound(I))]
    170 #[repr(u8)]
    171 pub enum GenericShapeOutside<BasicShape, I> {
    172    #[animation(error)]
    173    None,
    174    #[animation(error)]
    175    Image(I),
    176    Shape(Box<BasicShape>, #[css(skip_if = "is_default")] ShapeBox),
    177    #[animation(error)]
    178    Box(ShapeBox),
    179 }
    180 
    181 pub use self::GenericShapeOutside as ShapeOutside;
    182 
    183 /// The <basic-shape>.
    184 ///
    185 /// https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes
    186 #[derive(
    187    Animate,
    188    Clone,
    189    ComputeSquaredDistance,
    190    Debug,
    191    Deserialize,
    192    MallocSizeOf,
    193    PartialEq,
    194    Serialize,
    195    SpecifiedValueInfo,
    196    ToAnimatedValue,
    197    ToComputedValue,
    198    ToCss,
    199    ToResolvedValue,
    200    ToShmem,
    201 )]
    202 #[repr(C, u8)]
    203 pub enum GenericBasicShape<Angle, Position, LengthPercentage, BasicShapeRect> {
    204    /// The <basic-shape-rect>.
    205    Rect(BasicShapeRect),
    206    /// Defines a circle with a center and a radius.
    207    Circle(
    208        #[animation(field_bound)]
    209        #[css(field_bound)]
    210        #[shmem(field_bound)]
    211        Circle<LengthPercentage>,
    212    ),
    213    /// Defines an ellipse with a center and x-axis/y-axis radii.
    214    Ellipse(
    215        #[animation(field_bound)]
    216        #[css(field_bound)]
    217        #[shmem(field_bound)]
    218        Ellipse<LengthPercentage>,
    219    ),
    220    /// Defines a polygon with pair arguments.
    221    Polygon(GenericPolygon<LengthPercentage>),
    222    /// Defines a path() or shape().
    223    PathOrShape(
    224        #[animation(field_bound)]
    225        #[css(field_bound)]
    226        #[compute(field_bound)]
    227        GenericPathOrShapeFunction<Angle, Position, LengthPercentage>,
    228    ),
    229 }
    230 
    231 pub use self::GenericBasicShape as BasicShape;
    232 
    233 /// <https://drafts.csswg.org/css-shapes/#funcdef-inset>
    234 #[allow(missing_docs)]
    235 #[derive(
    236    Animate,
    237    Clone,
    238    ComputeSquaredDistance,
    239    Debug,
    240    Deserialize,
    241    MallocSizeOf,
    242    PartialEq,
    243    Serialize,
    244    SpecifiedValueInfo,
    245    ToAnimatedValue,
    246    ToComputedValue,
    247    ToResolvedValue,
    248    ToShmem,
    249 )]
    250 #[css(function = "inset")]
    251 #[repr(C)]
    252 pub struct GenericInsetRect<LengthPercentage> {
    253    pub rect: Rect<LengthPercentage>,
    254    #[shmem(field_bound)]
    255    #[animation(field_bound)]
    256    pub round: GenericBorderRadius<NonNegative<LengthPercentage>>,
    257 }
    258 
    259 pub use self::GenericInsetRect as InsetRect;
    260 
    261 /// <https://drafts.csswg.org/css-shapes/#funcdef-circle>
    262 #[allow(missing_docs)]
    263 #[derive(
    264    Animate,
    265    Clone,
    266    ComputeSquaredDistance,
    267    Copy,
    268    Debug,
    269    Deserialize,
    270    MallocSizeOf,
    271    PartialEq,
    272    Serialize,
    273    SpecifiedValueInfo,
    274    ToAnimatedValue,
    275    ToComputedValue,
    276    ToResolvedValue,
    277    ToShmem,
    278 )]
    279 #[css(function)]
    280 #[repr(C)]
    281 pub struct Circle<LengthPercentage> {
    282    pub position: GenericPositionOrAuto<ShapePosition<LengthPercentage>>,
    283    #[animation(field_bound)]
    284    pub radius: GenericShapeRadius<LengthPercentage>,
    285 }
    286 
    287 /// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse>
    288 #[allow(missing_docs)]
    289 #[derive(
    290    Animate,
    291    Clone,
    292    ComputeSquaredDistance,
    293    Copy,
    294    Debug,
    295    Deserialize,
    296    MallocSizeOf,
    297    PartialEq,
    298    Serialize,
    299    SpecifiedValueInfo,
    300    ToAnimatedValue,
    301    ToComputedValue,
    302    ToResolvedValue,
    303    ToShmem,
    304 )]
    305 #[css(function)]
    306 #[repr(C)]
    307 pub struct Ellipse<LengthPercentage> {
    308    pub position: GenericPositionOrAuto<ShapePosition<LengthPercentage>>,
    309    #[animation(field_bound)]
    310    pub semiaxis_x: GenericShapeRadius<LengthPercentage>,
    311    #[animation(field_bound)]
    312    pub semiaxis_y: GenericShapeRadius<LengthPercentage>,
    313 }
    314 
    315 /// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius>
    316 #[allow(missing_docs)]
    317 #[derive(
    318    Animate,
    319    Clone,
    320    ComputeSquaredDistance,
    321    Copy,
    322    Debug,
    323    Deserialize,
    324    MallocSizeOf,
    325    Parse,
    326    PartialEq,
    327    Serialize,
    328    SpecifiedValueInfo,
    329    ToAnimatedValue,
    330    ToComputedValue,
    331    ToCss,
    332    ToResolvedValue,
    333    ToShmem,
    334 )]
    335 #[repr(C, u8)]
    336 pub enum GenericShapeRadius<LengthPercentage> {
    337    Length(
    338        #[animation(field_bound)]
    339        #[parse(field_bound)]
    340        NonNegative<LengthPercentage>,
    341    ),
    342    #[animation(error)]
    343    ClosestSide,
    344    #[animation(error)]
    345    FarthestSide,
    346 }
    347 
    348 pub use self::GenericShapeRadius as ShapeRadius;
    349 
    350 /// A generic type for representing the `polygon()` function
    351 ///
    352 /// <https://drafts.csswg.org/css-shapes/#funcdef-polygon>
    353 #[derive(
    354    Clone,
    355    Debug,
    356    Deserialize,
    357    MallocSizeOf,
    358    PartialEq,
    359    Serialize,
    360    SpecifiedValueInfo,
    361    ToAnimatedValue,
    362    ToComputedValue,
    363    ToCss,
    364    ToResolvedValue,
    365    ToShmem,
    366 )]
    367 #[css(comma, function = "polygon")]
    368 #[repr(C)]
    369 pub struct GenericPolygon<LengthPercentage> {
    370    /// The filling rule for a polygon.
    371    #[css(skip_if = "is_default")]
    372    pub fill: FillRule,
    373    /// A collection of (x, y) coordinates to draw the polygon.
    374    #[css(iterable)]
    375    pub coordinates: crate::OwnedSlice<PolygonCoord<LengthPercentage>>,
    376 }
    377 
    378 pub use self::GenericPolygon as Polygon;
    379 
    380 /// Coordinates for Polygon.
    381 #[derive(
    382    Animate,
    383    Clone,
    384    ComputeSquaredDistance,
    385    Debug,
    386    Deserialize,
    387    MallocSizeOf,
    388    PartialEq,
    389    Serialize,
    390    SpecifiedValueInfo,
    391    ToAnimatedValue,
    392    ToComputedValue,
    393    ToCss,
    394    ToResolvedValue,
    395    ToShmem,
    396 )]
    397 #[repr(C)]
    398 pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage);
    399 
    400 /// path() function or shape() function.
    401 #[derive(
    402    Clone,
    403    ComputeSquaredDistance,
    404    Debug,
    405    Deserialize,
    406    MallocSizeOf,
    407    PartialEq,
    408    Serialize,
    409    SpecifiedValueInfo,
    410    ToAnimatedValue,
    411    ToComputedValue,
    412    ToCss,
    413    ToResolvedValue,
    414    ToShmem,
    415 )]
    416 #[repr(C, u8)]
    417 pub enum GenericPathOrShapeFunction<Angle, Position, LengthPercentage> {
    418    /// Defines a path with SVG path syntax.
    419    Path(Path),
    420    /// Defines a shape function, which is identical to path() but it uses the CSS syntax.
    421    Shape(
    422        #[css(field_bound)]
    423        #[compute(field_bound)]
    424        Shape<Angle, Position, LengthPercentage>,
    425    ),
    426 }
    427 
    428 // https://drafts.csswg.org/css-shapes/#typedef-fill-rule
    429 // NOTE: Basic shapes spec says that these are the only two values, however
    430 // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
    431 // says that it can also be `inherit`
    432 #[allow(missing_docs)]
    433 #[derive(
    434    Animate,
    435    Clone,
    436    ComputeSquaredDistance,
    437    Copy,
    438    Debug,
    439    Deserialize,
    440    Eq,
    441    MallocSizeOf,
    442    Parse,
    443    PartialEq,
    444    Serialize,
    445    SpecifiedValueInfo,
    446    ToAnimatedValue,
    447    ToComputedValue,
    448    ToCss,
    449    ToResolvedValue,
    450    ToShmem,
    451    ToTyped,
    452 )]
    453 #[repr(u8)]
    454 pub enum FillRule {
    455    Nonzero,
    456    Evenodd,
    457 }
    458 
    459 /// The path function.
    460 ///
    461 /// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-path
    462 #[derive(
    463    Animate,
    464    Clone,
    465    ComputeSquaredDistance,
    466    Debug,
    467    Deserialize,
    468    MallocSizeOf,
    469    PartialEq,
    470    Serialize,
    471    SpecifiedValueInfo,
    472    ToAnimatedValue,
    473    ToComputedValue,
    474    ToCss,
    475    ToResolvedValue,
    476    ToShmem,
    477 )]
    478 #[css(comma, function = "path")]
    479 #[repr(C)]
    480 pub struct Path {
    481    /// The filling rule for the svg path.
    482    #[css(skip_if = "is_default")]
    483    pub fill: FillRule,
    484    /// The svg path data.
    485    pub path: SVGPathData,
    486 }
    487 
    488 impl Path {
    489    /// Returns the slice of PathCommand.
    490    #[inline]
    491    pub fn commands(&self) -> &[PathCommand] {
    492        self.path.commands()
    493    }
    494 }
    495 
    496 impl<B, U> ToAnimatedZero for ClipPath<B, U> {
    497    fn to_animated_zero(&self) -> Result<Self, ()> {
    498        Err(())
    499    }
    500 }
    501 
    502 impl<B, U> ToAnimatedZero for ShapeOutside<B, U> {
    503    fn to_animated_zero(&self) -> Result<Self, ()> {
    504        Err(())
    505    }
    506 }
    507 
    508 impl<Length> ToCss for InsetRect<Length>
    509 where
    510    Length: ToCss + PartialEq + Zero,
    511 {
    512    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    513    where
    514        W: Write,
    515    {
    516        dest.write_str("inset(")?;
    517        self.rect.to_css(dest)?;
    518        if !self.round.is_zero() {
    519            dest.write_str(" round ")?;
    520            self.round.to_css(dest)?;
    521        }
    522        dest.write_char(')')
    523    }
    524 }
    525 
    526 impl<LengthPercentage> ToCss for Circle<LengthPercentage>
    527 where
    528    LengthPercentage: ToCss + PartialEq,
    529    ShapePosition<LengthPercentage>: ToCss,
    530 {
    531    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    532    where
    533        W: Write,
    534    {
    535        let has_radius = self.radius != Default::default();
    536 
    537        dest.write_str("circle(")?;
    538        if has_radius {
    539            self.radius.to_css(dest)?;
    540        }
    541 
    542        // Preserve the `at <position>` even if it specified the default value.
    543        // https://github.com/w3c/csswg-drafts/issues/8695
    544        if !matches!(self.position, GenericPositionOrAuto::Auto) {
    545            if has_radius {
    546                dest.write_char(' ')?;
    547            }
    548            dest.write_str("at ")?;
    549            self.position.to_css(dest)?;
    550        }
    551        dest.write_char(')')
    552    }
    553 }
    554 
    555 impl<LengthPercentage> ToCss for Ellipse<LengthPercentage>
    556 where
    557    LengthPercentage: ToCss + PartialEq,
    558    ShapePosition<LengthPercentage>: ToCss,
    559 {
    560    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    561    where
    562        W: Write,
    563    {
    564        let has_radii =
    565            self.semiaxis_x != Default::default() || self.semiaxis_y != Default::default();
    566 
    567        dest.write_str("ellipse(")?;
    568        if has_radii {
    569            self.semiaxis_x.to_css(dest)?;
    570            dest.write_char(' ')?;
    571            self.semiaxis_y.to_css(dest)?;
    572        }
    573 
    574        // Preserve the `at <position>` even if it specified the default value.
    575        // https://github.com/w3c/csswg-drafts/issues/8695
    576        if !matches!(self.position, GenericPositionOrAuto::Auto) {
    577            if has_radii {
    578                dest.write_char(' ')?;
    579            }
    580            dest.write_str("at ")?;
    581            self.position.to_css(dest)?;
    582        }
    583        dest.write_char(')')
    584    }
    585 }
    586 
    587 impl<L> Default for ShapeRadius<L> {
    588    #[inline]
    589    fn default() -> Self {
    590        ShapeRadius::ClosestSide
    591    }
    592 }
    593 
    594 impl<L> Animate for Polygon<L>
    595 where
    596    L: Animate,
    597 {
    598    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
    599        if self.fill != other.fill {
    600            return Err(());
    601        }
    602        let coordinates =
    603            lists::by_computed_value::animate(&self.coordinates, &other.coordinates, procedure)?;
    604        Ok(Polygon {
    605            fill: self.fill,
    606            coordinates,
    607        })
    608    }
    609 }
    610 
    611 impl<L> ComputeSquaredDistance for Polygon<L>
    612 where
    613    L: ComputeSquaredDistance,
    614 {
    615    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
    616        if self.fill != other.fill {
    617            return Err(());
    618        }
    619        lists::by_computed_value::squared_distance(&self.coordinates, &other.coordinates)
    620    }
    621 }
    622 
    623 impl Default for FillRule {
    624    #[inline]
    625    fn default() -> Self {
    626        FillRule::Nonzero
    627    }
    628 }
    629 
    630 #[inline]
    631 fn is_default<T: Default + PartialEq>(fill: &T) -> bool {
    632    *fill == Default::default()
    633 }
    634 
    635 /// The shape function defined in css-shape-2.
    636 /// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#)
    637 ///
    638 /// https://drafts.csswg.org/css-shapes-2/#shape-function
    639 #[derive(
    640    Clone,
    641    Debug,
    642    Deserialize,
    643    MallocSizeOf,
    644    PartialEq,
    645    Serialize,
    646    SpecifiedValueInfo,
    647    ToAnimatedValue,
    648    ToComputedValue,
    649    ToResolvedValue,
    650    ToShmem,
    651 )]
    652 #[repr(C)]
    653 pub struct Shape<Angle, Position, LengthPercentage> {
    654    /// The filling rule for this shape.
    655    pub fill: FillRule,
    656    /// The shape command data. Note that the starting point will be the first command in this
    657    /// slice.
    658    // Note: The first command is always GenericShapeCommand::Move.
    659    #[compute(field_bound)]
    660    pub commands: crate::OwnedSlice<GenericShapeCommand<Angle, Position, LengthPercentage>>,
    661 }
    662 
    663 impl<Angle, Position, LengthPercentage> Shape<Angle, Position, LengthPercentage> {
    664    /// Returns the slice of GenericShapeCommand<..>.
    665    #[inline]
    666    pub fn commands(&self) -> &[GenericShapeCommand<Angle, Position, LengthPercentage>] {
    667        &self.commands
    668    }
    669 }
    670 
    671 impl<Angle, Position, LengthPercentage> Animate for Shape<Angle, Position, LengthPercentage>
    672 where
    673    Angle: Animate,
    674    Position: Animate,
    675    LengthPercentage: Animate,
    676 {
    677    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
    678        if self.fill != other.fill {
    679            return Err(());
    680        }
    681        let commands =
    682            lists::by_computed_value::animate(&self.commands, &other.commands, procedure)?;
    683        Ok(Self {
    684            fill: self.fill,
    685            commands,
    686        })
    687    }
    688 }
    689 
    690 impl<Angle, Position, LengthPercentage> ComputeSquaredDistance
    691    for Shape<Angle, Position, LengthPercentage>
    692 where
    693    Angle: ComputeSquaredDistance,
    694    Position: ComputeSquaredDistance,
    695    LengthPercentage: ComputeSquaredDistance,
    696 {
    697    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
    698        if self.fill != other.fill {
    699            return Err(());
    700        }
    701        lists::by_computed_value::squared_distance(&self.commands, &other.commands)
    702    }
    703 }
    704 
    705 impl<Angle, Position, LengthPercentage> ToCss for Shape<Angle, Position, LengthPercentage>
    706 where
    707    Angle: ToCss + Zero,
    708    Position: ToCss,
    709    LengthPercentage: PartialEq + ToCss,
    710 {
    711    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    712    where
    713        W: Write,
    714    {
    715        use style_traits::values::SequenceWriter;
    716 
    717        // Per spec, we must have the first move command and at least one following command.
    718        debug_assert!(self.commands.len() > 1);
    719 
    720        dest.write_str("shape(")?;
    721        if !is_default(&self.fill) {
    722            self.fill.to_css(dest)?;
    723            dest.write_char(' ')?;
    724        }
    725        dest.write_str("from ")?;
    726        match &self.commands[0] {
    727            ShapeCommand::Move {
    728                point: CommandEndPoint::ToPosition(pos),
    729            } => pos.to_css(dest)?,
    730            ShapeCommand::Move {
    731                point: CommandEndPoint::ByCoordinate(coord),
    732            } => coord.to_css(dest)?,
    733            _ => unreachable!("The first command must be move"),
    734        }
    735        dest.write_str(", ")?;
    736        {
    737            let mut writer = SequenceWriter::new(dest, ", ");
    738            for command in self.commands.iter().skip(1) {
    739                writer.item(command)?;
    740            }
    741        }
    742        dest.write_char(')')
    743    }
    744 }
    745 
    746 /// This is a more general shape(path) command type, for both shape() and path().
    747 ///
    748 /// https://www.w3.org/TR/SVG11/paths.html#PathData
    749 /// https://drafts.csswg.org/css-shapes-2/#shape-function
    750 #[derive(
    751    Animate,
    752    Clone,
    753    ComputeSquaredDistance,
    754    Copy,
    755    Debug,
    756    Deserialize,
    757    MallocSizeOf,
    758    PartialEq,
    759    Serialize,
    760    SpecifiedValueInfo,
    761    ToAnimatedValue,
    762    ToAnimatedZero,
    763    ToComputedValue,
    764    ToResolvedValue,
    765    ToShmem,
    766 )]
    767 #[allow(missing_docs)]
    768 #[repr(C, u8)]
    769 pub enum GenericShapeCommand<Angle, Position, LengthPercentage> {
    770    /// The move command.
    771    Move {
    772        point: CommandEndPoint<Position, LengthPercentage>,
    773    },
    774    /// The line command.
    775    Line {
    776        point: CommandEndPoint<Position, LengthPercentage>,
    777    },
    778    /// The hline command.
    779    HLine {
    780        #[compute(field_bound)]
    781        x: AxisEndPoint<LengthPercentage>,
    782    },
    783    /// The vline command.
    784    VLine {
    785        #[compute(field_bound)]
    786        y: AxisEndPoint<LengthPercentage>,
    787    },
    788    /// The cubic Bézier curve command.
    789    CubicCurve {
    790        point: CommandEndPoint<Position, LengthPercentage>,
    791        control1: ControlPoint<Position, LengthPercentage>,
    792        control2: ControlPoint<Position, LengthPercentage>,
    793    },
    794    /// The quadratic Bézier curve command.
    795    QuadCurve {
    796        point: CommandEndPoint<Position, LengthPercentage>,
    797        control1: ControlPoint<Position, LengthPercentage>,
    798    },
    799    /// The smooth command.
    800    SmoothCubic {
    801        point: CommandEndPoint<Position, LengthPercentage>,
    802        control2: ControlPoint<Position, LengthPercentage>,
    803    },
    804    /// The smooth quadratic Bézier curve command.
    805    SmoothQuad {
    806        point: CommandEndPoint<Position, LengthPercentage>,
    807    },
    808    /// The arc command.
    809    Arc {
    810        point: CommandEndPoint<Position, LengthPercentage>,
    811        radii: ArcRadii<LengthPercentage>,
    812        arc_sweep: ArcSweep,
    813        arc_size: ArcSize,
    814        rotate: Angle,
    815    },
    816    /// The closepath command.
    817    Close,
    818 }
    819 
    820 pub use self::GenericShapeCommand as ShapeCommand;
    821 
    822 impl<Angle, Position, LengthPercentage> ToCss for ShapeCommand<Angle, Position, LengthPercentage>
    823 where
    824    Angle: ToCss + Zero,
    825    Position: ToCss,
    826    LengthPercentage: PartialEq + ToCss,
    827 {
    828    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    829    where
    830        W: fmt::Write,
    831    {
    832        use self::ShapeCommand::*;
    833        match *self {
    834            Move { ref point } => {
    835                dest.write_str("move ")?;
    836                point.to_css(dest)
    837            },
    838            Line { ref point } => {
    839                dest.write_str("line ")?;
    840                point.to_css(dest)
    841            },
    842            HLine { ref x } => {
    843                dest.write_str("hline ")?;
    844                x.to_css(dest)
    845            },
    846            VLine { ref y } => {
    847                dest.write_str("vline ")?;
    848                y.to_css(dest)
    849            },
    850            CubicCurve {
    851                ref point,
    852                ref control1,
    853                ref control2,
    854            } => {
    855                dest.write_str("curve ")?;
    856                point.to_css(dest)?;
    857                dest.write_str(" with ")?;
    858                control1.to_css(dest, point.is_abs())?;
    859                dest.write_char(' ')?;
    860                dest.write_char('/')?;
    861                dest.write_char(' ')?;
    862                control2.to_css(dest, point.is_abs())
    863            },
    864            QuadCurve {
    865                ref point,
    866                ref control1,
    867            } => {
    868                dest.write_str("curve ")?;
    869                point.to_css(dest)?;
    870                dest.write_str(" with ")?;
    871                control1.to_css(dest, point.is_abs())
    872            },
    873            SmoothCubic {
    874                ref point,
    875                ref control2,
    876            } => {
    877                dest.write_str("smooth ")?;
    878                point.to_css(dest)?;
    879                dest.write_str(" with ")?;
    880                control2.to_css(dest, point.is_abs())
    881            },
    882            SmoothQuad { ref point } => {
    883                dest.write_str("smooth ")?;
    884                point.to_css(dest)
    885            },
    886            Arc {
    887                ref point,
    888                ref radii,
    889                arc_sweep,
    890                arc_size,
    891                ref rotate,
    892            } => {
    893                dest.write_str("arc ")?;
    894                point.to_css(dest)?;
    895                dest.write_str(" of ")?;
    896                radii.to_css(dest)?;
    897 
    898                if matches!(arc_sweep, ArcSweep::Cw) {
    899                    dest.write_str(" cw")?;
    900                }
    901 
    902                if matches!(arc_size, ArcSize::Large) {
    903                    dest.write_str(" large")?;
    904                }
    905 
    906                if !rotate.is_zero() {
    907                    dest.write_str(" rotate ")?;
    908                    rotate.to_css(dest)?;
    909                }
    910                Ok(())
    911            },
    912            Close => dest.write_str("close"),
    913        }
    914    }
    915 }
    916 
    917 /// Defines the end point of the command, which can be specified in absolute or relative coordinates,
    918 /// determined by their "to" or "by" components respectively.
    919 /// https://drafts.csswg.org/css-shapes/#typedef-shape-command-end-point
    920 #[allow(missing_docs)]
    921 #[derive(
    922    Animate,
    923    Clone,
    924    ComputeSquaredDistance,
    925    Copy,
    926    Debug,
    927    Deserialize,
    928    MallocSizeOf,
    929    PartialEq,
    930    Serialize,
    931    SpecifiedValueInfo,
    932    ToAnimatedValue,
    933    ToAnimatedZero,
    934    ToComputedValue,
    935    ToResolvedValue,
    936    ToShmem,
    937 )]
    938 #[repr(C, u8)]
    939 pub enum CommandEndPoint<Position, LengthPercentage> {
    940    ToPosition(Position),
    941    ByCoordinate(CoordinatePair<LengthPercentage>),
    942 }
    943 
    944 impl<Position, LengthPercentage> CommandEndPoint<Position, LengthPercentage> {
    945    /// Return true if it is absolute, i.e. it is To.
    946    #[inline]
    947    pub fn is_abs(&self) -> bool {
    948        matches!(self, CommandEndPoint::ToPosition(_))
    949    }
    950 }
    951 
    952 impl<Position, LengthPercentage> CommandEndPoint<Position, LengthPercentage> {
    953    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    954    where
    955        W: Write,
    956        Position: ToCss,
    957        LengthPercentage: ToCss,
    958    {
    959        match self {
    960            CommandEndPoint::ToPosition(pos) => {
    961                dest.write_str("to ")?;
    962                pos.to_css(dest)
    963            },
    964            CommandEndPoint::ByCoordinate(coord) => {
    965                dest.write_str("by ")?;
    966                coord.to_css(dest)
    967            },
    968        }
    969    }
    970 }
    971 
    972 /// Defines the end point for the commands <horizontal-line-command> and <vertical-line-command>, which
    973 /// can be specified in absolute or relative values, determined by their "to" or "by" components respectively.
    974 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command
    975 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command
    976 #[allow(missing_docs)]
    977 #[derive(
    978    Animate,
    979    Clone,
    980    Copy,
    981    ComputeSquaredDistance,
    982    Debug,
    983    Deserialize,
    984    MallocSizeOf,
    985    PartialEq,
    986    Parse,
    987    Serialize,
    988    SpecifiedValueInfo,
    989    ToAnimatedValue,
    990    ToAnimatedZero,
    991    ToComputedValue,
    992    ToResolvedValue,
    993    ToShmem,
    994 )]
    995 #[repr(u8)]
    996 pub enum AxisEndPoint<LengthPercentage> {
    997    ToPosition(#[compute(field_bound)] AxisPosition<LengthPercentage>),
    998    ByCoordinate(LengthPercentage),
    999 }
   1000 
   1001 impl<LengthPercentage> AxisEndPoint<LengthPercentage> {
   1002    /// Return true if it is absolute, i.e. it is To.
   1003    #[inline]
   1004    pub fn is_abs(&self) -> bool {
   1005        matches!(self, AxisEndPoint::ToPosition(_))
   1006    }
   1007 }
   1008 
   1009 impl<LengthPercentage: ToCss> ToCss for AxisEndPoint<LengthPercentage> {
   1010    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1011    where
   1012        W: Write,
   1013    {
   1014        if self.is_abs() {
   1015            dest.write_str("to ")?;
   1016        } else {
   1017            dest.write_str("by ")?;
   1018        }
   1019        match self {
   1020            AxisEndPoint::ToPosition(pos) => pos.to_css(dest),
   1021            AxisEndPoint::ByCoordinate(coord) => coord.to_css(dest),
   1022        }
   1023    }
   1024 }
   1025 
   1026 /// Defines how the absolutely positioned end point for <horizontal-line-command> and
   1027 /// <vertical-line-command> is positioned.
   1028 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command
   1029 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command
   1030 #[allow(missing_docs)]
   1031 #[derive(
   1032    Animate,
   1033    Clone,
   1034    ComputeSquaredDistance,
   1035    Copy,
   1036    Debug,
   1037    Deserialize,
   1038    MallocSizeOf,
   1039    Parse,
   1040    PartialEq,
   1041    Serialize,
   1042    SpecifiedValueInfo,
   1043    ToAnimatedValue,
   1044    ToAnimatedZero,
   1045    ToCss,
   1046    ToResolvedValue,
   1047    ToShmem,
   1048 )]
   1049 #[repr(u8)]
   1050 pub enum AxisPosition<LengthPercentage> {
   1051    LengthPercent(LengthPercentage),
   1052    Keyword(AxisPositionKeyword),
   1053 }
   1054 
   1055 /// The set of position keywords used in <horizontal-line-command> and <vertical-line-command>
   1056 /// for absolute positioning. Note: this is the shared union list between hline and vline, so
   1057 /// not every value is valid for either. I.e. hline cannot be positioned with top or y-start.
   1058 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command
   1059 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command
   1060 #[allow(missing_docs)]
   1061 #[derive(
   1062    Animate,
   1063    Clone,
   1064    ComputeSquaredDistance,
   1065    Copy,
   1066    Debug,
   1067    Deserialize,
   1068    MallocSizeOf,
   1069    Parse,
   1070    PartialEq,
   1071    Serialize,
   1072    SpecifiedValueInfo,
   1073    ToAnimatedValue,
   1074    ToAnimatedZero,
   1075    ToCss,
   1076    ToResolvedValue,
   1077    ToShmem,
   1078 )]
   1079 #[repr(u8)]
   1080 pub enum AxisPositionKeyword {
   1081    Center,
   1082    Left,
   1083    Right,
   1084    Top,
   1085    Bottom,
   1086    XStart,
   1087    XEnd,
   1088    YStart,
   1089    YEnd,
   1090 }
   1091 
   1092 impl AxisPositionKeyword {
   1093    /// Returns the axis position keyword as its corresponding percentage.
   1094    #[inline]
   1095    pub fn as_percentage(&self) -> Percentage {
   1096        match self {
   1097            Self::Center => Percentage(0.5),
   1098            Self::Left | Self::Top | Self::XStart | Self::YStart => Percentage(0.),
   1099            Self::Right | Self::Bottom | Self::XEnd | Self::YEnd => Percentage(1.),
   1100        }
   1101    }
   1102 }
   1103 
   1104 /// Defines a pair of coordinates, representing a rightward and downward offset, respectively, from
   1105 /// a specified reference point. Percentages are resolved against the width or height,
   1106 /// respectively, of the reference box.
   1107 /// https://drafts.csswg.org/css-shapes-2/#typedef-shape-coordinate-pair
   1108 #[allow(missing_docs)]
   1109 #[derive(
   1110    AddAssign,
   1111    Animate,
   1112    Clone,
   1113    ComputeSquaredDistance,
   1114    Copy,
   1115    Debug,
   1116    Deserialize,
   1117    MallocSizeOf,
   1118    PartialEq,
   1119    Serialize,
   1120    SpecifiedValueInfo,
   1121    ToAnimatedValue,
   1122    ToAnimatedZero,
   1123    ToComputedValue,
   1124    ToCss,
   1125    ToResolvedValue,
   1126    ToShmem,
   1127 )]
   1128 #[repr(C)]
   1129 pub struct CoordinatePair<LengthPercentage> {
   1130    pub x: LengthPercentage,
   1131    pub y: LengthPercentage,
   1132 }
   1133 
   1134 impl<LengthPercentage> CoordinatePair<LengthPercentage> {
   1135    /// Create a CoordinatePair.
   1136    #[inline]
   1137    pub fn new(x: LengthPercentage, y: LengthPercentage) -> Self {
   1138        Self { x, y }
   1139    }
   1140 }
   1141 
   1142 /// Defines a control point for a quadratic or cubic Bézier curve, which can be specified
   1143 /// in absolute or relative coordinates.
   1144 /// https://drafts.csswg.org/css-shapes/#typedef-shape-control-point
   1145 #[allow(missing_docs)]
   1146 #[derive(
   1147    Animate,
   1148    Clone,
   1149    Copy,
   1150    ComputeSquaredDistance,
   1151    Debug,
   1152    Deserialize,
   1153    MallocSizeOf,
   1154    PartialEq,
   1155    Serialize,
   1156    SpecifiedValueInfo,
   1157    ToAnimatedValue,
   1158    ToAnimatedZero,
   1159    ToComputedValue,
   1160    ToResolvedValue,
   1161    ToShmem,
   1162 )]
   1163 #[repr(C, u8)]
   1164 pub enum ControlPoint<Position, LengthPercentage> {
   1165    Absolute(Position),
   1166    Relative(RelativeControlPoint<LengthPercentage>),
   1167 }
   1168 
   1169 impl<Position, LengthPercentage> ControlPoint<Position, LengthPercentage> {
   1170    /// Serialize <control-point>
   1171    pub fn to_css<W>(&self, dest: &mut CssWriter<W>, is_end_point_abs: bool) -> fmt::Result
   1172    where
   1173        W: Write,
   1174        Position: ToCss,
   1175        LengthPercentage: ToCss,
   1176    {
   1177        match self {
   1178            ControlPoint::Absolute(pos) => pos.to_css(dest),
   1179            ControlPoint::Relative(point) => point.to_css(dest, is_end_point_abs),
   1180        }
   1181    }
   1182 }
   1183 
   1184 /// Defines a relative control point to a quadratic or cubic Bézier curve, dependent on the
   1185 /// reference value. The default `None` is to be relative to the command’s starting point.
   1186 /// https://drafts.csswg.org/css-shapes/#typedef-shape-relative-control-point
   1187 #[allow(missing_docs)]
   1188 #[derive(
   1189    Animate,
   1190    Clone,
   1191    Copy,
   1192    Debug,
   1193    Deserialize,
   1194    MallocSizeOf,
   1195    PartialEq,
   1196    Serialize,
   1197    SpecifiedValueInfo,
   1198    ToAnimatedValue,
   1199    ToAnimatedZero,
   1200    ToComputedValue,
   1201    ToResolvedValue,
   1202    ToShmem,
   1203 )]
   1204 #[repr(C)]
   1205 pub struct RelativeControlPoint<LengthPercentage> {
   1206    pub coord: CoordinatePair<LengthPercentage>,
   1207    pub reference: ControlReference,
   1208 }
   1209 
   1210 impl<LengthPercentage: ToCss> RelativeControlPoint<LengthPercentage> {
   1211    fn to_css<W>(&self, dest: &mut CssWriter<W>, is_end_point_abs: bool) -> fmt::Result
   1212    where
   1213        W: Write,
   1214    {
   1215        self.coord.to_css(dest)?;
   1216        match self.reference {
   1217            ControlReference::Origin if is_end_point_abs => Ok(()),
   1218            ControlReference::Start if !is_end_point_abs => Ok(()),
   1219            other => {
   1220                dest.write_str(" from ")?;
   1221                other.to_css(dest)
   1222            },
   1223        }
   1224    }
   1225 }
   1226 
   1227 impl<LengthPercentage: ComputeSquaredDistance> ComputeSquaredDistance
   1228    for RelativeControlPoint<LengthPercentage>
   1229 {
   1230    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
   1231        self.coord.compute_squared_distance(&other.coord)
   1232    }
   1233 }
   1234 
   1235 /// Defines the point of reference for a <relative-control-point>.
   1236 ///
   1237 /// When a reference is not specified, depending on whether the associated
   1238 /// <command-end-point> is absolutely or relatively positioned, the default
   1239 /// will be `Origin` or `Start`, respectively.
   1240 /// https://drafts.csswg.org/css-shapes/#typedef-shape-relative-control-point
   1241 #[allow(missing_docs)]
   1242 #[derive(
   1243    Animate,
   1244    Clone,
   1245    Copy,
   1246    Debug,
   1247    Deserialize,
   1248    Eq,
   1249    MallocSizeOf,
   1250    PartialEq,
   1251    Parse,
   1252    Serialize,
   1253    SpecifiedValueInfo,
   1254    ToAnimatedValue,
   1255    ToAnimatedZero,
   1256    ToComputedValue,
   1257    ToCss,
   1258    ToResolvedValue,
   1259    ToShmem,
   1260 )]
   1261 #[repr(C)]
   1262 pub enum ControlReference {
   1263    Start,
   1264    End,
   1265    Origin,
   1266 }
   1267 
   1268 /// Defines the radiuses for an <arc-command>.
   1269 ///
   1270 /// The first <length-percentage> is the ellipse's horizontal radius, and the second is
   1271 /// the vertical radius. If only one value is provided, it is used for both radii, and any
   1272 /// <percentage> is resolved against the direction-agnostic size of the reference box.
   1273 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-arc-command
   1274 #[allow(missing_docs)]
   1275 #[derive(
   1276    Animate,
   1277    Clone,
   1278    ComputeSquaredDistance,
   1279    Copy,
   1280    Debug,
   1281    Deserialize,
   1282    MallocSizeOf,
   1283    PartialEq,
   1284    Serialize,
   1285    SpecifiedValueInfo,
   1286    ToAnimatedValue,
   1287    ToAnimatedZero,
   1288    ToComputedValue,
   1289    ToCss,
   1290    ToResolvedValue,
   1291    ToShmem,
   1292 )]
   1293 #[repr(C)]
   1294 pub struct ArcRadii<LengthPercentage> {
   1295    pub rx: LengthPercentage,
   1296    pub ry: Optional<LengthPercentage>,
   1297 }
   1298 
   1299 /// This indicates that the arc that is traced around the ellipse clockwise or counter-clockwise
   1300 /// from the center.
   1301 /// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-sweep
   1302 #[derive(
   1303    Clone,
   1304    Copy,
   1305    Debug,
   1306    Deserialize,
   1307    FromPrimitive,
   1308    MallocSizeOf,
   1309    Parse,
   1310    PartialEq,
   1311    Serialize,
   1312    SpecifiedValueInfo,
   1313    ToAnimatedValue,
   1314    ToAnimatedZero,
   1315    ToComputedValue,
   1316    ToCss,
   1317    ToResolvedValue,
   1318    ToShmem,
   1319 )]
   1320 #[repr(u8)]
   1321 pub enum ArcSweep {
   1322    /// Counter-clockwise. The default value. (This also represents 0 in the svg path.)
   1323    Ccw = 0,
   1324    /// Clockwise. (This also represents 1 in the svg path.)
   1325    Cw = 1,
   1326 }
   1327 
   1328 impl Animate for ArcSweep {
   1329    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
   1330        use num_traits::FromPrimitive;
   1331        // If an arc command has different <arc-sweep> between its starting and ending list, then
   1332        // the interpolated result uses cw for any progress value between 0 and 1.
   1333        // Note: we cast progress from f64->f32->f64 to drop tiny noise near 0.0.
   1334        let progress = procedure.weights().1 as f32 as f64;
   1335        let procedure = Procedure::Interpolate { progress };
   1336        (*self as i32 as f32)
   1337            .animate(&(*other as i32 as f32), procedure)
   1338            .map(|v| ArcSweep::from_u8((v > 0.) as u8).unwrap_or(ArcSweep::Ccw))
   1339    }
   1340 }
   1341 
   1342 impl ComputeSquaredDistance for ArcSweep {
   1343    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
   1344        (*self as i32).compute_squared_distance(&(*other as i32))
   1345    }
   1346 }
   1347 
   1348 /// This indicates that the larger or smaller, respectively, of the two possible arcs must be
   1349 /// chosen.
   1350 /// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-size
   1351 #[derive(
   1352    Clone,
   1353    Copy,
   1354    Debug,
   1355    Deserialize,
   1356    FromPrimitive,
   1357    MallocSizeOf,
   1358    Parse,
   1359    PartialEq,
   1360    Serialize,
   1361    SpecifiedValueInfo,
   1362    ToAnimatedValue,
   1363    ToAnimatedZero,
   1364    ToComputedValue,
   1365    ToCss,
   1366    ToResolvedValue,
   1367    ToShmem,
   1368 )]
   1369 #[repr(u8)]
   1370 pub enum ArcSize {
   1371    /// Choose the small one. The default value. (This also represents 0 in the svg path.)
   1372    Small = 0,
   1373    /// Choose the large one. (This also represents 1 in the svg path.)
   1374    Large = 1,
   1375 }
   1376 
   1377 impl Animate for ArcSize {
   1378    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
   1379        use num_traits::FromPrimitive;
   1380        // If it has different <arc-size> keywords, then the interpolated result uses large for any
   1381        // progress value between 0 and 1.
   1382        // Note: we cast progress from f64->f32->f64 to drop tiny noise near 0.0.
   1383        let progress = procedure.weights().1 as f32 as f64;
   1384        let procedure = Procedure::Interpolate { progress };
   1385        (*self as i32 as f32)
   1386            .animate(&(*other as i32 as f32), procedure)
   1387            .map(|v| ArcSize::from_u8((v > 0.) as u8).unwrap_or(ArcSize::Small))
   1388    }
   1389 }
   1390 
   1391 impl ComputeSquaredDistance for ArcSize {
   1392    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
   1393        (*self as i32).compute_squared_distance(&(*other as i32))
   1394    }
   1395 }