tor-browser

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

svg.rs (12693B)


      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 SVG properties.
      6 
      7 use crate::derives::*;
      8 use crate::parser::{Parse, ParserContext};
      9 use crate::values::generics::svg as generic;
     10 use crate::values::specified::color::Color;
     11 use crate::values::specified::url::SpecifiedUrl;
     12 use crate::values::specified::AllowQuirks;
     13 use crate::values::specified::LengthPercentage;
     14 use crate::values::specified::SVGPathData;
     15 use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
     16 use crate::values::CustomIdent;
     17 use cssparser::{Parser, Token};
     18 use std::fmt::{self, Write};
     19 use style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};
     20 use style_traits::{StyleParseErrorKind, ToCss};
     21 
     22 /// Specified SVG Paint value
     23 pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>;
     24 
     25 /// <length> | <percentage> | <number> | context-value
     26 pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
     27 
     28 /// A non-negative version of SVGLength.
     29 pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
     30 
     31 /// [ <length> | <percentage> | <number> ]# | context-value
     32 pub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;
     33 
     34 /// Whether the `context-value` value is enabled.
     35 #[cfg(feature = "gecko")]
     36 pub fn is_context_value_enabled() -> bool {
     37    static_prefs::pref!("gfx.font_rendering.opentype_svg.enabled")
     38 }
     39 
     40 /// Whether the `context-value` value is enabled.
     41 #[cfg(not(feature = "gecko"))]
     42 pub fn is_context_value_enabled() -> bool {
     43    false
     44 }
     45 
     46 macro_rules! parse_svg_length {
     47    ($ty:ty, $lp:ty) => {
     48        impl Parse for $ty {
     49            fn parse<'i, 't>(
     50                context: &ParserContext,
     51                input: &mut Parser<'i, 't>,
     52            ) -> Result<Self, ParseError<'i>> {
     53                if let Ok(lp) =
     54                    input.try_parse(|i| <$lp>::parse_quirky(context, i, AllowQuirks::Always))
     55                {
     56                    return Ok(generic::SVGLength::LengthPercentage(lp));
     57                }
     58 
     59                try_match_ident_ignore_ascii_case! { input,
     60                    "context-value" if is_context_value_enabled() => {
     61                        Ok(generic::SVGLength::ContextValue)
     62                    },
     63                }
     64            }
     65        }
     66    };
     67 }
     68 
     69 parse_svg_length!(SVGLength, LengthPercentage);
     70 parse_svg_length!(SVGWidth, NonNegativeLengthPercentage);
     71 
     72 impl Parse for SVGStrokeDashArray {
     73    fn parse<'i, 't>(
     74        context: &ParserContext,
     75        input: &mut Parser<'i, 't>,
     76    ) -> Result<Self, ParseError<'i>> {
     77        if let Ok(values) = input.try_parse(|i| {
     78            CommaWithSpace::parse(i, |i| {
     79                NonNegativeLengthPercentage::parse_quirky(context, i, AllowQuirks::Always)
     80            })
     81        }) {
     82            return Ok(generic::SVGStrokeDashArray::Values(values.into()));
     83        }
     84 
     85        try_match_ident_ignore_ascii_case! { input,
     86            "context-value" if is_context_value_enabled() => {
     87                Ok(generic::SVGStrokeDashArray::ContextValue)
     88            },
     89            "none" => Ok(generic::SVGStrokeDashArray::Values(Default::default())),
     90        }
     91    }
     92 }
     93 
     94 /// <opacity-value> | context-fill-opacity | context-stroke-opacity
     95 pub type SVGOpacity = generic::SVGOpacity<Opacity>;
     96 
     97 /// The specified value for a single CSS paint-order property.
     98 #[repr(u8)]
     99 #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)]
    100 pub enum PaintOrder {
    101    /// `normal` variant
    102    Normal = 0,
    103    /// `fill` variant
    104    Fill = 1,
    105    /// `stroke` variant
    106    Stroke = 2,
    107    /// `markers` variant
    108    Markers = 3,
    109 }
    110 
    111 /// Number of non-normal components
    112 pub const PAINT_ORDER_COUNT: u8 = 3;
    113 
    114 /// Number of bits for each component
    115 pub const PAINT_ORDER_SHIFT: u8 = 2;
    116 
    117 /// Mask with above bits set
    118 pub const PAINT_ORDER_MASK: u8 = 0b11;
    119 
    120 /// The specified value is tree `PaintOrder` values packed into the
    121 /// bitfields below, as a six-bit field, of 3 two-bit pairs
    122 ///
    123 /// Each pair can be set to FILL, STROKE, or MARKERS
    124 /// Lowest significant bit pairs are highest priority.
    125 ///  `normal` is the empty bitfield. The three pairs are
    126 /// never zero in any case other than `normal`.
    127 ///
    128 /// Higher priority values, i.e. the values specified first,
    129 /// will be painted first (and may be covered by paintings of lower priority)
    130 #[derive(
    131    Clone,
    132    Copy,
    133    Debug,
    134    MallocSizeOf,
    135    PartialEq,
    136    SpecifiedValueInfo,
    137    ToComputedValue,
    138    ToResolvedValue,
    139    ToShmem,
    140    ToTyped,
    141 )]
    142 #[repr(transparent)]
    143 pub struct SVGPaintOrder(pub u8);
    144 
    145 impl SVGPaintOrder {
    146    /// Get default `paint-order` with `0`
    147    pub fn normal() -> Self {
    148        SVGPaintOrder(0)
    149    }
    150 
    151    /// Get variant of `paint-order`
    152    pub fn order_at(&self, pos: u8) -> PaintOrder {
    153        match (self.0 >> pos * PAINT_ORDER_SHIFT) & PAINT_ORDER_MASK {
    154            0 => PaintOrder::Normal,
    155            1 => PaintOrder::Fill,
    156            2 => PaintOrder::Stroke,
    157            3 => PaintOrder::Markers,
    158            _ => unreachable!("this cannot happen"),
    159        }
    160    }
    161 }
    162 
    163 impl Parse for SVGPaintOrder {
    164    fn parse<'i, 't>(
    165        _context: &ParserContext,
    166        input: &mut Parser<'i, 't>,
    167    ) -> Result<SVGPaintOrder, ParseError<'i>> {
    168        if let Ok(()) = input.try_parse(|i| i.expect_ident_matching("normal")) {
    169            return Ok(SVGPaintOrder::normal());
    170        }
    171 
    172        let mut value = 0;
    173        // bitfield representing what we've seen so far
    174        // bit 1 is fill, bit 2 is stroke, bit 3 is markers
    175        let mut seen = 0;
    176        let mut pos = 0;
    177 
    178        loop {
    179            let result: Result<_, ParseError> = input.try_parse(|input| {
    180                try_match_ident_ignore_ascii_case! { input,
    181                    "fill" => Ok(PaintOrder::Fill),
    182                    "stroke" => Ok(PaintOrder::Stroke),
    183                    "markers" => Ok(PaintOrder::Markers),
    184                }
    185            });
    186 
    187            match result {
    188                Ok(val) => {
    189                    if (seen & (1 << val as u8)) != 0 {
    190                        // don't parse the same ident twice
    191                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    192                    }
    193 
    194                    value |= (val as u8) << (pos * PAINT_ORDER_SHIFT);
    195                    seen |= 1 << (val as u8);
    196                    pos += 1;
    197                },
    198                Err(_) => break,
    199            }
    200        }
    201 
    202        if value == 0 {
    203            // Couldn't find any keyword
    204            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    205        }
    206 
    207        // fill in rest
    208        for i in pos..PAINT_ORDER_COUNT {
    209            for paint in 1..(PAINT_ORDER_COUNT + 1) {
    210                // if not seen, set bit at position, mark as seen
    211                if (seen & (1 << paint)) == 0 {
    212                    seen |= 1 << paint;
    213                    value |= paint << (i * PAINT_ORDER_SHIFT);
    214                    break;
    215                }
    216            }
    217        }
    218 
    219        Ok(SVGPaintOrder(value))
    220    }
    221 }
    222 
    223 impl ToCss for SVGPaintOrder {
    224    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    225    where
    226        W: Write,
    227    {
    228        if self.0 == 0 {
    229            return dest.write_str("normal");
    230        }
    231 
    232        let mut last_pos_to_serialize = 0;
    233        for i in (1..PAINT_ORDER_COUNT).rev() {
    234            let component = self.order_at(i);
    235            let earlier_component = self.order_at(i - 1);
    236            if component < earlier_component {
    237                last_pos_to_serialize = i - 1;
    238                break;
    239            }
    240        }
    241 
    242        for pos in 0..last_pos_to_serialize + 1 {
    243            if pos != 0 {
    244                dest.write_char(' ')?
    245            }
    246            self.order_at(pos).to_css(dest)?;
    247        }
    248        Ok(())
    249    }
    250 }
    251 
    252 /// The context properties we understand.
    253 #[derive(
    254    Clone,
    255    Copy,
    256    Eq,
    257    Debug,
    258    Default,
    259    MallocSizeOf,
    260    PartialEq,
    261    SpecifiedValueInfo,
    262    ToComputedValue,
    263    ToResolvedValue,
    264    ToShmem,
    265 )]
    266 #[repr(C)]
    267 pub struct ContextPropertyBits(u8);
    268 bitflags! {
    269    impl ContextPropertyBits: u8 {
    270        /// `fill`
    271        const FILL = 1 << 0;
    272        /// `stroke`
    273        const STROKE = 1 << 1;
    274        /// `fill-opacity`
    275        const FILL_OPACITY = 1 << 2;
    276        /// `stroke-opacity`
    277        const STROKE_OPACITY = 1 << 3;
    278    }
    279 }
    280 
    281 /// Specified MozContextProperties value.
    282 /// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)
    283 #[derive(
    284    Clone,
    285    Debug,
    286    Default,
    287    MallocSizeOf,
    288    PartialEq,
    289    SpecifiedValueInfo,
    290    ToComputedValue,
    291    ToCss,
    292    ToResolvedValue,
    293    ToShmem,
    294    ToTyped,
    295 )]
    296 #[repr(C)]
    297 pub struct MozContextProperties {
    298    #[css(iterable, if_empty = "none")]
    299    #[ignore_malloc_size_of = "Arc"]
    300    idents: crate::ArcSlice<CustomIdent>,
    301    #[css(skip)]
    302    bits: ContextPropertyBits,
    303 }
    304 
    305 impl Parse for MozContextProperties {
    306    fn parse<'i, 't>(
    307        _context: &ParserContext,
    308        input: &mut Parser<'i, 't>,
    309    ) -> Result<MozContextProperties, ParseError<'i>> {
    310        let mut values = vec![];
    311        let mut bits = ContextPropertyBits::empty();
    312        loop {
    313            {
    314                let location = input.current_source_location();
    315                let ident = input.expect_ident()?;
    316 
    317                if ident.eq_ignore_ascii_case("none") && values.is_empty() {
    318                    return Ok(Self::default());
    319                }
    320 
    321                let ident = CustomIdent::from_ident(location, ident, &["all", "none", "auto"])?;
    322 
    323                if ident.0 == atom!("fill") {
    324                    bits.insert(ContextPropertyBits::FILL);
    325                } else if ident.0 == atom!("stroke") {
    326                    bits.insert(ContextPropertyBits::STROKE);
    327                } else if ident.0 == atom!("fill-opacity") {
    328                    bits.insert(ContextPropertyBits::FILL_OPACITY);
    329                } else if ident.0 == atom!("stroke-opacity") {
    330                    bits.insert(ContextPropertyBits::STROKE_OPACITY);
    331                }
    332 
    333                values.push(ident);
    334            }
    335 
    336            let location = input.current_source_location();
    337            match input.next() {
    338                Ok(&Token::Comma) => continue,
    339                Err(..) => break,
    340                Ok(other) => return Err(location.new_unexpected_token_error(other.clone())),
    341            }
    342        }
    343 
    344        if values.is_empty() {
    345            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    346        }
    347 
    348        Ok(MozContextProperties {
    349            idents: crate::ArcSlice::from_iter(values.into_iter()),
    350            bits,
    351        })
    352    }
    353 }
    354 
    355 /// The svg d property type.
    356 ///
    357 /// https://svgwg.org/svg2-draft/paths.html#TheDProperty
    358 #[derive(
    359    Animate,
    360    Clone,
    361    ComputeSquaredDistance,
    362    Debug,
    363    Deserialize,
    364    MallocSizeOf,
    365    PartialEq,
    366    Serialize,
    367    SpecifiedValueInfo,
    368    ToAnimatedValue,
    369    ToAnimatedZero,
    370    ToComputedValue,
    371    ToCss,
    372    ToResolvedValue,
    373    ToShmem,
    374    ToTyped,
    375 )]
    376 #[repr(C, u8)]
    377 pub enum DProperty {
    378    /// Path value for path(<string>) or just a <string>.
    379    #[css(function)]
    380    Path(SVGPathData),
    381    /// None value.
    382    #[animation(error)]
    383    None,
    384 }
    385 
    386 impl DProperty {
    387    /// return none.
    388    #[inline]
    389    pub fn none() -> Self {
    390        DProperty::None
    391    }
    392 }
    393 
    394 impl Parse for DProperty {
    395    fn parse<'i, 't>(
    396        context: &ParserContext,
    397        input: &mut Parser<'i, 't>,
    398    ) -> Result<Self, ParseError<'i>> {
    399        // Parse none.
    400        if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
    401            return Ok(DProperty::none());
    402        }
    403 
    404        // Parse possible functions.
    405        input.expect_function_matching("path")?;
    406        let path_data = input.parse_nested_block(|i| Parse::parse(context, i))?;
    407        Ok(DProperty::Path(path_data))
    408    }
    409 }
    410 
    411 #[derive(
    412    Clone,
    413    Copy,
    414    Debug,
    415    Default,
    416    Eq,
    417    MallocSizeOf,
    418    Parse,
    419    PartialEq,
    420    SpecifiedValueInfo,
    421    ToComputedValue,
    422    ToCss,
    423    ToResolvedValue,
    424    ToShmem,
    425    ToTyped,
    426 )]
    427 #[css(bitflags(single = "none", mixed = "non-scaling-stroke"))]
    428 #[repr(C)]
    429 /// https://svgwg.org/svg2-draft/coords.html#VectorEffects
    430 pub struct VectorEffect(u8);
    431 bitflags! {
    432    impl VectorEffect: u8 {
    433        /// `none`
    434        const NONE = 0;
    435        /// `non-scaling-stroke`
    436        const NON_SCALING_STROKE = 1 << 0;
    437    }
    438 }
    439 
    440 impl VectorEffect {
    441    /// Returns the initial value of vector-effect
    442    #[inline]
    443    pub fn none() -> Self {
    444        Self::NONE
    445    }
    446 }