tor-browser

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

font_feature_values_rule.rs (18228B)


      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 //! The [`@font-feature-values`][font-feature-values] at-rule.
      6 //!
      7 //! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
      8 
      9 use crate::derives::*;
     10 use crate::error_reporting::ContextualParseError;
     11 #[cfg(feature = "gecko")]
     12 use crate::gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;
     13 #[cfg(feature = "gecko")]
     14 use crate::gecko_bindings::structs::{self, gfxFontFeatureValueSet};
     15 use crate::parser::{Parse, ParserContext};
     16 use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
     17 use crate::stylesheets::CssRuleType;
     18 use crate::values::computed::font::FamilyName;
     19 use crate::values::serialize_atom_identifier;
     20 use crate::Atom;
     21 use cssparser::{
     22    match_ignore_ascii_case, AtRuleParser, BasicParseErrorKind, CowRcStr, DeclarationParser,
     23    Parser, ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
     24    Token,
     25 };
     26 use std::fmt::{self, Write};
     27 use style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};
     28 #[cfg(feature = "gecko")]
     29 use thin_vec::ThinVec;
     30 
     31 /// A @font-feature-values block declaration.
     32 /// It is `<ident>: <integer>+`.
     33 /// This struct can take 3 value types.
     34 /// - `SingleValue` is to keep just one unsigned integer value.
     35 /// - `PairValues` is to keep one or two unsigned integer values.
     36 /// - `VectorValues` is to keep a list of unsigned integer values.
     37 #[derive(Clone, Debug, PartialEq, ToShmem)]
     38 pub struct FFVDeclaration<T> {
     39    /// An `<ident>` for declaration name.
     40    pub name: Atom,
     41    /// An `<integer>+` for declaration value.
     42    pub value: T,
     43 }
     44 
     45 impl<T: ToCss> ToCss for FFVDeclaration<T> {
     46    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     47    where
     48        W: Write,
     49    {
     50        serialize_atom_identifier(&self.name, dest)?;
     51        dest.write_str(": ")?;
     52        self.value.to_css(dest)?;
     53        dest.write_char(';')
     54    }
     55 }
     56 
     57 /// A trait for @font-feature-values rule to gecko values conversion.
     58 #[cfg(feature = "gecko")]
     59 pub trait ToGeckoFontFeatureValues {
     60    /// Sets the equivalent of declaration to gecko `ThinVec<u32>` array.
     61    fn to_gecko_font_feature_values(&self) -> ThinVec<u32>;
     62 }
     63 
     64 /// A @font-feature-values block declaration value that keeps one value.
     65 #[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
     66 pub struct SingleValue(pub u32);
     67 
     68 impl Parse for SingleValue {
     69    fn parse<'i, 't>(
     70        _context: &ParserContext,
     71        input: &mut Parser<'i, 't>,
     72    ) -> Result<SingleValue, ParseError<'i>> {
     73        let location = input.current_source_location();
     74        match *input.next()? {
     75            Token::Number {
     76                int_value: Some(v), ..
     77            } if v >= 0 => Ok(SingleValue(v as u32)),
     78            ref t => Err(location.new_unexpected_token_error(t.clone())),
     79        }
     80    }
     81 }
     82 
     83 #[cfg(feature = "gecko")]
     84 impl ToGeckoFontFeatureValues for SingleValue {
     85    fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {
     86        thin_vec::thin_vec![self.0 as u32]
     87    }
     88 }
     89 
     90 /// A @font-feature-values block declaration value that keeps one or two values.
     91 #[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
     92 pub struct PairValues(pub u32, pub Option<u32>);
     93 
     94 impl Parse for PairValues {
     95    fn parse<'i, 't>(
     96        _context: &ParserContext,
     97        input: &mut Parser<'i, 't>,
     98    ) -> Result<PairValues, ParseError<'i>> {
     99        let location = input.current_source_location();
    100        let first = match *input.next()? {
    101            Token::Number {
    102                int_value: Some(a), ..
    103            } if a >= 0 => a as u32,
    104            ref t => return Err(location.new_unexpected_token_error(t.clone())),
    105        };
    106        let location = input.current_source_location();
    107        match input.next() {
    108            Ok(&Token::Number {
    109                int_value: Some(b), ..
    110            }) if b >= 0 => Ok(PairValues(first, Some(b as u32))),
    111            // It can't be anything other than number.
    112            Ok(t) => Err(location.new_unexpected_token_error(t.clone())),
    113            // It can be just one value.
    114            Err(_) => Ok(PairValues(first, None)),
    115        }
    116    }
    117 }
    118 
    119 #[cfg(feature = "gecko")]
    120 impl ToGeckoFontFeatureValues for PairValues {
    121    fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {
    122        let mut result = thin_vec::thin_vec![self.0 as u32];
    123        if let Some(second) = self.1 {
    124            result.push(second as u32);
    125        }
    126        result
    127    }
    128 }
    129 
    130 /// A @font-feature-values block declaration value that keeps a list of values.
    131 #[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
    132 pub struct VectorValues(#[css(iterable)] pub Vec<u32>);
    133 
    134 impl Parse for VectorValues {
    135    fn parse<'i, 't>(
    136        _context: &ParserContext,
    137        input: &mut Parser<'i, 't>,
    138    ) -> Result<VectorValues, ParseError<'i>> {
    139        let mut vec = vec![];
    140        loop {
    141            let location = input.current_source_location();
    142            match input.next() {
    143                Ok(&Token::Number {
    144                    int_value: Some(a), ..
    145                }) if a >= 0 => {
    146                    vec.push(a as u32);
    147                },
    148                // It can't be anything other than number.
    149                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
    150                Err(_) => break,
    151            }
    152        }
    153 
    154        if vec.len() == 0 {
    155            return Err(input.new_error(BasicParseErrorKind::EndOfInput));
    156        }
    157 
    158        Ok(VectorValues(vec))
    159    }
    160 }
    161 
    162 #[cfg(feature = "gecko")]
    163 impl ToGeckoFontFeatureValues for VectorValues {
    164    fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {
    165        self.0.iter().copied().collect()
    166    }
    167 }
    168 
    169 /// Parses a list of `FamilyName`s.
    170 pub fn parse_family_name_list<'i, 't>(
    171    context: &ParserContext,
    172    input: &mut Parser<'i, 't>,
    173 ) -> Result<Vec<FamilyName>, ParseError<'i>> {
    174    input
    175        .parse_comma_separated(|i| FamilyName::parse(context, i))
    176        .map_err(|e| e.into())
    177 }
    178 
    179 /// @font-feature-values inside block parser. Parses a list of `FFVDeclaration`.
    180 /// (`<ident>: <integer>+`)
    181 struct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> {
    182    context: &'a ParserContext<'b>,
    183    declarations: &'a mut Vec<FFVDeclaration<T>>,
    184 }
    185 
    186 /// Default methods reject all at rules.
    187 impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
    188    type Prelude = ();
    189    type AtRule = ();
    190    type Error = StyleParseErrorKind<'i>;
    191 }
    192 
    193 impl<'a, 'b, 'i, T> QualifiedRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
    194    type Prelude = ();
    195    type QualifiedRule = ();
    196    type Error = StyleParseErrorKind<'i>;
    197 }
    198 
    199 impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T>
    200 where
    201    T: Parse,
    202 {
    203    type Declaration = ();
    204    type Error = StyleParseErrorKind<'i>;
    205 
    206    fn parse_value<'t>(
    207        &mut self,
    208        name: CowRcStr<'i>,
    209        input: &mut Parser<'i, 't>,
    210        _declaration_start: &ParserState,
    211    ) -> Result<(), ParseError<'i>> {
    212        let value = input.parse_entirely(|i| T::parse(self.context, i))?;
    213        let new = FFVDeclaration {
    214            name: Atom::from(&*name),
    215            value,
    216        };
    217        update_or_push(&mut self.declarations, new);
    218        Ok(())
    219    }
    220 }
    221 
    222 impl<'a, 'b, 'i, T> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
    223    for FFVDeclarationsParser<'a, 'b, T>
    224 where
    225    T: Parse,
    226 {
    227    fn parse_declarations(&self) -> bool {
    228        true
    229    }
    230    fn parse_qualified(&self) -> bool {
    231        false
    232    }
    233 }
    234 
    235 macro_rules! font_feature_values_blocks {
    236    (
    237        blocks = [
    238            $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident / $gecko_enum: ident: $ty: ty, )*
    239        ]
    240    ) => {
    241        /// The [`@font-feature-values`][font-feature-values] at-rule.
    242        ///
    243        /// [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
    244        #[derive(Clone, Debug, PartialEq, ToShmem)]
    245        pub struct FontFeatureValuesRule {
    246            /// Font family list for @font-feature-values rule.
    247            /// Family names cannot contain generic families. FamilyName
    248            /// also accepts only non-generic names.
    249            pub family_names: Vec<FamilyName>,
    250            $(
    251                #[$doc]
    252                pub $ident: Vec<FFVDeclaration<$ty>>,
    253            )*
    254            /// The line and column of the rule's source code.
    255            pub source_location: SourceLocation,
    256        }
    257 
    258        impl FontFeatureValuesRule {
    259            /// Creates an empty FontFeatureValuesRule with given location and family name list.
    260            fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self {
    261                FontFeatureValuesRule {
    262                    family_names: family_names,
    263                    $(
    264                        $ident: vec![],
    265                    )*
    266                    source_location: location,
    267                }
    268            }
    269 
    270            /// Parses a `FontFeatureValuesRule`.
    271            pub fn parse(
    272                context: &ParserContext,
    273                input: &mut Parser,
    274                family_names: Vec<FamilyName>,
    275                location: SourceLocation,
    276            ) -> Self {
    277                let mut rule = FontFeatureValuesRule::new(family_names, location);
    278                let mut parser = FontFeatureValuesRuleParser {
    279                    context,
    280                    rule: &mut rule,
    281                };
    282                let mut iter = RuleBodyParser::new(input, &mut parser);
    283                while let Some(result) = iter.next() {
    284                    if let Err((error, slice)) = result {
    285                        let location = error.location;
    286                        let error = ContextualParseError::UnsupportedRule(slice, error);
    287                        context.log_css_error(location, error);
    288                    }
    289                }
    290                rule
    291            }
    292 
    293            /// Prints inside of `@font-feature-values` block.
    294            pub fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    295            where
    296                W: Write,
    297            {
    298                $(
    299                    if self.$ident.len() > 0 {
    300                        dest.write_str(concat!("@", $name, " {\n"))?;
    301                        let iter = self.$ident.iter();
    302                        for val in iter {
    303                            val.to_css(dest)?;
    304                            dest.write_str("\n")?
    305                        }
    306                        dest.write_str("}\n")?
    307                    }
    308                )*
    309                Ok(())
    310            }
    311 
    312            /// Returns length of all at-rules.
    313            pub fn len(&self) -> usize {
    314                let mut len = 0;
    315                $(
    316                    len += self.$ident.len();
    317                )*
    318                len
    319            }
    320 
    321            /// Convert to Gecko gfxFontFeatureValueSet.
    322            #[cfg(feature = "gecko")]
    323            pub fn set_at_rules(&self, dest: *mut gfxFontFeatureValueSet) {
    324                for ref family in self.family_names.iter() {
    325                    let family = family.name.to_ascii_lowercase();
    326                    $(
    327                        if self.$ident.len() > 0 {
    328                            for val in self.$ident.iter() {
    329                                let array = unsafe {
    330                                    Gecko_AppendFeatureValueHashEntry(
    331                                        dest,
    332                                        family.as_ptr(),
    333                                        structs::$gecko_enum,
    334                                        val.name.as_ptr()
    335                                    )
    336                                };
    337                                unsafe {
    338                                    *array = val.value.to_gecko_font_feature_values();
    339                                }
    340                            }
    341                        }
    342                    )*
    343                }
    344            }
    345        }
    346 
    347        impl ToCssWithGuard for FontFeatureValuesRule {
    348            fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
    349                dest.write_str("@font-feature-values ")?;
    350                self.family_names.to_css(&mut CssWriter::new(dest))?;
    351                dest.write_str(" {\n")?;
    352                self.value_to_css(&mut CssWriter::new(dest))?;
    353                dest.write_char('}')
    354            }
    355        }
    356 
    357        /// Updates with new value if same `ident` exists, otherwise pushes to the vector.
    358        fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) {
    359            if let Some(item) = vec.iter_mut().find(|item| item.name == element.name) {
    360                item.value = element.value;
    361            } else {
    362                vec.push(element);
    363            }
    364        }
    365 
    366        /// Keeps the information about block type like @swash, @styleset etc.
    367        enum BlockType {
    368            $(
    369                $ident_camel,
    370            )*
    371        }
    372 
    373        /// Parser for `FontFeatureValuesRule`. Parses all blocks
    374        /// <feature-type> {
    375        ///   <feature-value-declaration-list>
    376        /// }
    377        /// <feature-type> = @stylistic | @historical-forms | @styleset |
    378        /// @character-variant | @swash | @ornaments | @annotation
    379        struct FontFeatureValuesRuleParser<'a> {
    380            context: &'a ParserContext<'a>,
    381            rule: &'a mut FontFeatureValuesRule,
    382        }
    383 
    384        /// Default methods reject all qualified rules.
    385        impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
    386            type Prelude = ();
    387            type QualifiedRule = ();
    388            type Error = StyleParseErrorKind<'i>;
    389        }
    390 
    391        impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
    392            type Prelude = BlockType;
    393            type AtRule = ();
    394            type Error = StyleParseErrorKind<'i>;
    395 
    396            fn parse_prelude<'t>(
    397                &mut self,
    398                name: CowRcStr<'i>,
    399                input: &mut Parser<'i, 't>,
    400            ) -> Result<BlockType, ParseError<'i>> {
    401                match_ignore_ascii_case! { &*name,
    402                    $(
    403                        $name => Ok(BlockType::$ident_camel),
    404                    )*
    405                    _ => Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
    406                }
    407            }
    408 
    409            fn parse_block<'t>(
    410                &mut self,
    411                prelude: BlockType,
    412                _: &ParserState,
    413                input: &mut Parser<'i, 't>
    414            ) -> Result<Self::AtRule, ParseError<'i>> {
    415                debug_assert!(self.context.rule_types().contains(CssRuleType::FontFeatureValues));
    416                match prelude {
    417                    $(
    418                        BlockType::$ident_camel => {
    419                            let mut parser = FFVDeclarationsParser {
    420                                context: &self.context,
    421                                declarations: &mut self.rule.$ident,
    422                            };
    423 
    424                            let mut iter = RuleBodyParser::new(input, &mut parser);
    425                            while let Some(declaration) = iter.next() {
    426                                if let Err((error, slice)) = declaration {
    427                                    let location = error.location;
    428                                    // TODO(emilio): Maybe add a more specific error kind for
    429                                    // font-feature-values descriptors.
    430                                    let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, &[]);
    431                                    self.context.log_css_error(location, error);
    432                                }
    433                            }
    434                        },
    435                    )*
    436                }
    437 
    438                Ok(())
    439            }
    440        }
    441 
    442        impl<'a, 'i> DeclarationParser<'i> for FontFeatureValuesRuleParser<'a> {
    443            type Declaration = ();
    444            type Error = StyleParseErrorKind<'i>;
    445        }
    446 
    447        impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> for FontFeatureValuesRuleParser<'a> {
    448            fn parse_declarations(&self) -> bool { false }
    449            fn parse_qualified(&self) -> bool { true }
    450        }
    451    }
    452 }
    453 
    454 font_feature_values_blocks! {
    455    blocks = [
    456        #[doc = "A @swash blocksck. \
    457                 Specifies a feature name that will work with the swash() \
    458                 functional notation of font-variant-alternates."]
    459        "swash" swash / Swash / NS_FONT_VARIANT_ALTERNATES_SWASH: SingleValue,
    460 
    461        #[doc = "A @stylistic block. \
    462                 Specifies a feature name that will work with the annotation() \
    463                 functional notation of font-variant-alternates."]
    464        "stylistic" stylistic / Stylistic / NS_FONT_VARIANT_ALTERNATES_STYLISTIC: SingleValue,
    465 
    466        #[doc = "A @ornaments block. \
    467                 Specifies a feature name that will work with the ornaments() ] \
    468                 functional notation of font-variant-alternates."]
    469        "ornaments" ornaments / Ornaments / NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: SingleValue,
    470 
    471        #[doc = "A @annotation block. \
    472                 Specifies a feature name that will work with the stylistic() \
    473                 functional notation of font-variant-alternates."]
    474        "annotation" annotation / Annotation / NS_FONT_VARIANT_ALTERNATES_ANNOTATION: SingleValue,
    475 
    476        #[doc = "A @character-variant block. \
    477                 Specifies a feature name that will work with the styleset() \
    478                 functional notation of font-variant-alternates. The value can be a pair."]
    479        "character-variant" character_variant / CharacterVariant / NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT:
    480            PairValues,
    481 
    482        #[doc = "A @styleset block. \
    483                 Specifies a feature name that will work with the character-variant() \
    484                 functional notation of font-variant-alternates. The value can be a list."]
    485        "styleset" styleset / Styleset / NS_FONT_VARIANT_ALTERNATES_STYLESET: VectorValues,
    486    ]
    487 }