tor-browser

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

image.rs (20956B)


      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 //! Generic types for the handling of [images].
      6 //!
      7 //! [images]: https://drafts.csswg.org/css-images/#image-values
      8 
      9 use crate::color::mix::ColorInterpolationMethod;
     10 use crate::custom_properties;
     11 use crate::derives::*;
     12 use crate::values::generics::NonNegative;
     13 use crate::values::generics::{color::GenericLightDark, position::PositionComponent, Optional};
     14 use crate::values::serialize_atom_identifier;
     15 use crate::{Atom, Zero};
     16 use servo_arc::Arc;
     17 use std::fmt::{self, Write};
     18 use style_traits::{CssWriter, ToCss};
     19 /// An `<image> | none` value.
     20 ///
     21 /// https://drafts.csswg.org/css-images/#image-values
     22 #[derive(Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToResolvedValue, ToShmem, ToTyped)]
     23 #[repr(C, u8)]
     24 pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
     25    /// `none` variant.
     26    None,
     27 
     28    /// A `<url()>` image.
     29    Url(ImageUrl),
     30 
     31    /// A `<gradient>` image.  Gradients are rather large, and not nearly as
     32    /// common as urls, so we box them here to keep the size of this enum sane.
     33    Gradient(Box<G>),
     34 
     35    /// A `-moz-element(# <element-id>)`
     36    #[cfg(feature = "gecko")]
     37    #[css(function = "-moz-element")]
     38    Element(Atom),
     39 
     40    /// A `-moz-symbolic-icon(<icon-id>)`
     41    /// NOTE(emilio): #[css(skip)] only really affects SpecifiedValueInfo, which we want because
     42    /// this is chrome-only.
     43    #[cfg(feature = "gecko")]
     44    #[css(function, skip)]
     45    MozSymbolicIcon(Atom),
     46 
     47    /// A paint worklet image.
     48    /// <https://drafts.css-houdini.org/css-paint-api/>
     49    #[cfg(feature = "servo")]
     50    PaintWorklet(Box<PaintWorklet>),
     51 
     52    /// A `<cross-fade()>` image. Storing this directly inside of
     53    /// GenericImage increases the size by 8 bytes so we box it here
     54    /// and store images directly inside of cross-fade instead of
     55    /// boxing them there.
     56    CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
     57 
     58    /// An `image-set()` function.
     59    ImageSet(Box<GenericImageSet<Self, Resolution>>),
     60 
     61    /// A `light-dark()` function.
     62    /// NOTE(emilio): #[css(skip)] only affects SpecifiedValueInfo. Remove or make conditional
     63    /// if/when shipping light-dark() for content.
     64    LightDark(#[css(skip)] Box<GenericLightDark<Self>>),
     65 }
     66 
     67 pub use self::GenericImage as Image;
     68 
     69 /// <https://drafts.csswg.org/css-images-4/#cross-fade-function>
     70 #[derive(
     71    Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
     72 )]
     73 #[css(comma, function = "cross-fade")]
     74 #[repr(C)]
     75 pub struct GenericCrossFade<Image, Color, Percentage> {
     76    /// All of the image percent pairings passed as arguments to
     77    /// cross-fade.
     78    #[css(iterable)]
     79    pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
     80 }
     81 
     82 /// An optional percent and a cross fade image.
     83 #[derive(
     84    Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
     85 )]
     86 #[repr(C)]
     87 pub struct GenericCrossFadeElement<Image, Color, Percentage> {
     88    /// The percent of the final image that `image` will be.
     89    pub percent: Optional<Percentage>,
     90    /// A color or image that will be blended when cross-fade is
     91    /// evaluated.
     92    pub image: GenericCrossFadeImage<Image, Color>,
     93 }
     94 
     95 /// An image or a color. `cross-fade` takes either when blending
     96 /// images together.
     97 #[derive(
     98    Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
     99 )]
    100 #[repr(C, u8)]
    101 pub enum GenericCrossFadeImage<I, C> {
    102    /// A boxed image value. Boxing provides indirection so images can
    103    /// be cross-fades and cross-fades can be images.
    104    Image(I),
    105    /// A color value.
    106    Color(C),
    107 }
    108 
    109 pub use self::GenericCrossFade as CrossFade;
    110 pub use self::GenericCrossFadeElement as CrossFadeElement;
    111 pub use self::GenericCrossFadeImage as CrossFadeImage;
    112 
    113 /// https://drafts.csswg.org/css-images-4/#image-set-notation
    114 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
    115 #[css(comma, function = "image-set")]
    116 #[repr(C)]
    117 pub struct GenericImageSet<Image, Resolution> {
    118    /// The index of the selected candidate. usize::MAX for specified values or invalid images.
    119    #[css(skip)]
    120    pub selected_index: usize,
    121 
    122    /// All of the image and resolution pairs.
    123    #[css(iterable)]
    124    pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
    125 }
    126 
    127 /// An optional percent and a cross fade image.
    128 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
    129 #[repr(C)]
    130 pub struct GenericImageSetItem<Image, Resolution> {
    131    /// `<image>`. `<string>` is converted to `Image::Url` at parse time.
    132    pub image: Image,
    133    /// The `<resolution>`.
    134    ///
    135    /// TODO: Skip serialization if it is 1x.
    136    pub resolution: Resolution,
    137 
    138    /// The `type(<string>)`
    139    /// (Optional) Specify the image's MIME type
    140    pub mime_type: crate::OwnedStr,
    141 
    142    /// True if mime_type has been specified
    143    pub has_mime_type: bool,
    144 }
    145 
    146 impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
    147    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    148    where
    149        W: fmt::Write,
    150    {
    151        self.image.to_css(dest)?;
    152        dest.write_char(' ')?;
    153        self.resolution.to_css(dest)?;
    154 
    155        if self.has_mime_type {
    156            dest.write_char(' ')?;
    157            dest.write_str("type(")?;
    158            self.mime_type.to_css(dest)?;
    159            dest.write_char(')')?;
    160        }
    161        Ok(())
    162    }
    163 }
    164 
    165 pub use self::GenericImageSet as ImageSet;
    166 pub use self::GenericImageSetItem as ImageSetItem;
    167 
    168 /// State flags stored on each variant of a Gradient.
    169 #[derive(
    170    Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
    171 )]
    172 #[repr(C)]
    173 pub struct GradientFlags(u8);
    174 bitflags! {
    175    impl GradientFlags: u8 {
    176        /// Set if this is a repeating gradient.
    177        const REPEATING = 1 << 0;
    178        /// Set if the color interpolation method matches the default for the items.
    179        const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;
    180    }
    181 }
    182 
    183 /// A CSS gradient.
    184 /// <https://drafts.csswg.org/css-images/#gradients>
    185 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
    186 #[repr(C)]
    187 pub enum GenericGradient<
    188    LineDirection,
    189    Length,
    190    LengthPercentage,
    191    Position,
    192    Angle,
    193    AngleOrPercentage,
    194    Color,
    195 > {
    196    /// A linear gradient.
    197    Linear {
    198        /// Line direction
    199        direction: LineDirection,
    200        /// Method to use for color interpolation.
    201        color_interpolation_method: ColorInterpolationMethod,
    202        /// The color stops and interpolation hints.
    203        items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
    204        /// State flags for the gradient.
    205        flags: GradientFlags,
    206        /// Compatibility mode.
    207        compat_mode: GradientCompatMode,
    208    },
    209    /// A radial gradient.
    210    Radial {
    211        /// Shape of gradient
    212        shape: GenericEndingShape<NonNegative<Length>, NonNegative<LengthPercentage>>,
    213        /// Center of gradient
    214        position: Position,
    215        /// Method to use for color interpolation.
    216        color_interpolation_method: ColorInterpolationMethod,
    217        /// The color stops and interpolation hints.
    218        items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
    219        /// State flags for the gradient.
    220        flags: GradientFlags,
    221        /// Compatibility mode.
    222        compat_mode: GradientCompatMode,
    223    },
    224    /// A conic gradient.
    225    Conic {
    226        /// Start angle of gradient
    227        angle: Angle,
    228        /// Center of gradient
    229        position: Position,
    230        /// Method to use for color interpolation.
    231        color_interpolation_method: ColorInterpolationMethod,
    232        /// The color stops and interpolation hints.
    233        items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
    234        /// State flags for the gradient.
    235        flags: GradientFlags,
    236    },
    237 }
    238 
    239 pub use self::GenericGradient as Gradient;
    240 
    241 #[derive(
    242    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
    243 )]
    244 #[repr(u8)]
    245 /// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
    246 pub enum GradientCompatMode {
    247    /// Modern syntax.
    248    Modern,
    249    /// `-webkit` prefix.
    250    WebKit,
    251    /// `-moz` prefix
    252    Moz,
    253 }
    254 
    255 /// A radial gradient's ending shape.
    256 #[derive(
    257    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
    258 )]
    259 #[repr(C, u8)]
    260 pub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {
    261    /// A circular gradient.
    262    Circle(GenericCircle<NonNegativeLength>),
    263    /// An elliptic gradient.
    264    Ellipse(GenericEllipse<NonNegativeLengthPercentage>),
    265 }
    266 
    267 pub use self::GenericEndingShape as EndingShape;
    268 
    269 /// A circle shape.
    270 #[derive(
    271    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
    272 )]
    273 #[repr(C, u8)]
    274 pub enum GenericCircle<NonNegativeLength> {
    275    /// A circle radius.
    276    Radius(NonNegativeLength),
    277    /// A circle extent.
    278    Extent(ShapeExtent),
    279 }
    280 
    281 pub use self::GenericCircle as Circle;
    282 
    283 /// An ellipse shape.
    284 #[derive(
    285    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
    286 )]
    287 #[repr(C, u8)]
    288 pub enum GenericEllipse<NonNegativeLengthPercentage> {
    289    /// An ellipse pair of radii.
    290    Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),
    291    /// An ellipse extent.
    292    Extent(ShapeExtent),
    293 }
    294 
    295 pub use self::GenericEllipse as Ellipse;
    296 
    297 /// <https://drafts.csswg.org/css-images/#typedef-extent-keyword>
    298 #[allow(missing_docs)]
    299 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    300 #[derive(
    301    Clone,
    302    Copy,
    303    Debug,
    304    Eq,
    305    MallocSizeOf,
    306    Parse,
    307    PartialEq,
    308    ToComputedValue,
    309    ToCss,
    310    ToResolvedValue,
    311    ToShmem,
    312 )]
    313 #[repr(u8)]
    314 pub enum ShapeExtent {
    315    ClosestSide,
    316    FarthestSide,
    317    ClosestCorner,
    318    FarthestCorner,
    319    Contain,
    320    Cover,
    321 }
    322 
    323 /// A gradient item.
    324 /// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>
    325 #[derive(
    326    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
    327 )]
    328 #[repr(C, u8)]
    329 pub enum GenericGradientItem<Color, T> {
    330    /// A simple color stop, without position.
    331    SimpleColorStop(Color),
    332    /// A complex color stop, with a position.
    333    ComplexColorStop {
    334        /// The color for the stop.
    335        color: Color,
    336        /// The position for the stop.
    337        position: T,
    338    },
    339    /// An interpolation hint.
    340    InterpolationHint(T),
    341 }
    342 
    343 pub use self::GenericGradientItem as GradientItem;
    344 
    345 /// A color stop.
    346 /// <https://drafts.csswg.org/css-images/#typedef-color-stop-list>
    347 #[derive(
    348    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
    349 )]
    350 pub struct ColorStop<Color, T> {
    351    /// The color of this stop.
    352    pub color: Color,
    353    /// The position of this stop.
    354    pub position: Option<T>,
    355 }
    356 
    357 impl<Color, T> ColorStop<Color, T> {
    358    /// Convert the color stop into an appropriate `GradientItem`.
    359    #[inline]
    360    pub fn into_item(self) -> GradientItem<Color, T> {
    361        match self.position {
    362            Some(position) => GradientItem::ComplexColorStop {
    363                color: self.color,
    364                position,
    365            },
    366            None => GradientItem::SimpleColorStop(self.color),
    367        }
    368    }
    369 }
    370 
    371 /// Specified values for a paint worklet.
    372 /// <https://drafts.css-houdini.org/css-paint-api/>
    373 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
    374 #[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
    375 pub struct PaintWorklet {
    376    /// The name the worklet was registered with.
    377    pub name: Atom,
    378    /// The arguments for the worklet.
    379    /// TODO: store a parsed representation of the arguments.
    380    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
    381    #[compute(no_field_bound)]
    382    #[resolve(no_field_bound)]
    383    pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
    384 }
    385 
    386 impl ::style_traits::SpecifiedValueInfo for PaintWorklet {}
    387 
    388 impl ToCss for PaintWorklet {
    389    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    390    where
    391        W: Write,
    392    {
    393        dest.write_str("paint(")?;
    394        serialize_atom_identifier(&self.name, dest)?;
    395        for argument in &self.arguments {
    396            dest.write_str(", ")?;
    397            argument.to_css(dest)?;
    398        }
    399        dest.write_char(')')
    400    }
    401 }
    402 
    403 impl<G, U, C, P, Resolution> fmt::Debug for Image<G, U, C, P, Resolution>
    404 where
    405    Image<G, U, C, P, Resolution>: ToCss,
    406 {
    407    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    408        self.to_css(&mut CssWriter::new(f))
    409    }
    410 }
    411 
    412 impl<G, U, C, P, Resolution> ToCss for Image<G, U, C, P, Resolution>
    413 where
    414    G: ToCss,
    415    U: ToCss,
    416    C: ToCss,
    417    P: ToCss,
    418    Resolution: ToCss,
    419 {
    420    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    421    where
    422        W: Write,
    423    {
    424        match *self {
    425            Image::None => dest.write_str("none"),
    426            Image::Url(ref url) => url.to_css(dest),
    427            Image::Gradient(ref gradient) => gradient.to_css(dest),
    428            #[cfg(feature = "servo")]
    429            Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
    430            #[cfg(feature = "gecko")]
    431            Image::Element(ref selector) => {
    432                dest.write_str("-moz-element(#")?;
    433                serialize_atom_identifier(selector, dest)?;
    434                dest.write_char(')')
    435            },
    436            #[cfg(feature = "gecko")]
    437            Image::MozSymbolicIcon(ref id) => {
    438                dest.write_str("-moz-symbolic-icon(")?;
    439                serialize_atom_identifier(id, dest)?;
    440                dest.write_char(')')
    441            },
    442            Image::ImageSet(ref is) => is.to_css(dest),
    443            Image::CrossFade(ref cf) => cf.to_css(dest),
    444            Image::LightDark(ref ld) => ld.to_css(dest),
    445        }
    446    }
    447 }
    448 
    449 impl<D, L, LP, P, A: Zero, AoP, C> ToCss for Gradient<D, L, LP, P, A, AoP, C>
    450 where
    451    D: LineDirection,
    452    L: ToCss,
    453    LP: ToCss,
    454    P: PositionComponent + ToCss,
    455    A: ToCss,
    456    AoP: ToCss,
    457    C: ToCss,
    458 {
    459    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    460    where
    461        W: Write,
    462    {
    463        let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {
    464            Gradient::Linear {
    465                compat_mode, flags, ..
    466            }
    467            | Gradient::Radial {
    468                compat_mode, flags, ..
    469            } => (
    470                compat_mode,
    471                flags.contains(GradientFlags::REPEATING),
    472                flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
    473            ),
    474            Gradient::Conic { flags, .. } => (
    475                GradientCompatMode::Modern,
    476                flags.contains(GradientFlags::REPEATING),
    477                flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
    478            ),
    479        };
    480 
    481        match compat_mode {
    482            GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
    483            GradientCompatMode::Moz => dest.write_str("-moz-")?,
    484            _ => {},
    485        }
    486 
    487        if repeating {
    488            dest.write_str("repeating-")?;
    489        }
    490 
    491        match *self {
    492            Gradient::Linear {
    493                ref direction,
    494                ref color_interpolation_method,
    495                ref items,
    496                compat_mode,
    497                ..
    498            } => {
    499                dest.write_str("linear-gradient(")?;
    500                let mut skip_comma = true;
    501                if !direction.points_downwards(compat_mode) {
    502                    direction.to_css(dest, compat_mode)?;
    503                    skip_comma = false;
    504                }
    505                if !has_default_color_interpolation_method {
    506                    if !skip_comma {
    507                        dest.write_char(' ')?;
    508                    }
    509                    color_interpolation_method.to_css(dest)?;
    510                    skip_comma = false;
    511                }
    512                for item in &**items {
    513                    if !skip_comma {
    514                        dest.write_str(", ")?;
    515                    }
    516                    skip_comma = false;
    517                    item.to_css(dest)?;
    518                }
    519            },
    520            Gradient::Radial {
    521                ref shape,
    522                ref position,
    523                ref color_interpolation_method,
    524                ref items,
    525                compat_mode,
    526                ..
    527            } => {
    528                dest.write_str("radial-gradient(")?;
    529                let omit_shape = match *shape {
    530                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover))
    531                    | EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
    532                    _ => false,
    533                };
    534                let omit_position = position.is_center();
    535                if compat_mode == GradientCompatMode::Modern {
    536                    if !omit_shape {
    537                        shape.to_css(dest)?;
    538                        if !omit_position {
    539                            dest.write_char(' ')?;
    540                        }
    541                    }
    542                    if !omit_position {
    543                        dest.write_str("at ")?;
    544                        position.to_css(dest)?;
    545                    }
    546                } else {
    547                    if !omit_position {
    548                        position.to_css(dest)?;
    549                        if !omit_shape {
    550                            dest.write_str(", ")?;
    551                        }
    552                    }
    553                    if !omit_shape {
    554                        shape.to_css(dest)?;
    555                    }
    556                }
    557                if !has_default_color_interpolation_method {
    558                    if !omit_shape || !omit_position {
    559                        dest.write_char(' ')?;
    560                    }
    561                    color_interpolation_method.to_css(dest)?;
    562                }
    563 
    564                let mut skip_comma =
    565                    omit_shape && omit_position && has_default_color_interpolation_method;
    566                for item in &**items {
    567                    if !skip_comma {
    568                        dest.write_str(", ")?;
    569                    }
    570                    skip_comma = false;
    571                    item.to_css(dest)?;
    572                }
    573            },
    574            Gradient::Conic {
    575                ref angle,
    576                ref position,
    577                ref color_interpolation_method,
    578                ref items,
    579                ..
    580            } => {
    581                dest.write_str("conic-gradient(")?;
    582                let omit_angle = angle.is_zero();
    583                let omit_position = position.is_center();
    584                if !omit_angle {
    585                    dest.write_str("from ")?;
    586                    angle.to_css(dest)?;
    587                    if !omit_position {
    588                        dest.write_char(' ')?;
    589                    }
    590                }
    591                if !omit_position {
    592                    dest.write_str("at ")?;
    593                    position.to_css(dest)?;
    594                }
    595                if !has_default_color_interpolation_method {
    596                    if !omit_angle || !omit_position {
    597                        dest.write_char(' ')?;
    598                    }
    599                    color_interpolation_method.to_css(dest)?;
    600                }
    601                let mut skip_comma =
    602                    omit_angle && omit_position && has_default_color_interpolation_method;
    603                for item in &**items {
    604                    if !skip_comma {
    605                        dest.write_str(", ")?;
    606                    }
    607                    skip_comma = false;
    608                    item.to_css(dest)?;
    609                }
    610            },
    611        }
    612        dest.write_char(')')
    613    }
    614 }
    615 
    616 /// The direction of a linear gradient.
    617 pub trait LineDirection {
    618    /// Whether this direction points towards, and thus can be omitted.
    619    fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
    620 
    621    /// Serialises this direction according to the compatibility mode.
    622    fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
    623    where
    624        W: Write;
    625 }
    626 
    627 impl<L> ToCss for Circle<L>
    628 where
    629    L: ToCss,
    630 {
    631    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    632    where
    633        W: Write,
    634    {
    635        match *self {
    636            Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
    637                dest.write_str("circle")
    638            },
    639            Circle::Extent(keyword) => {
    640                dest.write_str("circle ")?;
    641                keyword.to_css(dest)
    642            },
    643            Circle::Radius(ref length) => length.to_css(dest),
    644        }
    645    }
    646 }