tor-browser

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

feature.rs (7376B)


      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 //! Query features.
      6 
      7 use crate::derives::*;
      8 use crate::parser::ParserContext;
      9 use crate::values::computed::{self, CSSPixelLength, Ratio, Resolution};
     10 use crate::Atom;
     11 use cssparser::Parser;
     12 use selectors::kleene_value::KleeneValue;
     13 use std::fmt;
     14 use style_traits::ParseError;
     15 
     16 /// A generic discriminant for an enum value.
     17 pub type KeywordDiscriminant = u8;
     18 
     19 type QueryFeatureGetter<T> = fn(device: &computed::Context) -> T;
     20 
     21 /// Serializes a given discriminant.
     22 ///
     23 /// FIXME(emilio): we could prevent this allocation if the ToCss code would
     24 /// generate a method for keywords to get the static string or something.
     25 pub type KeywordSerializer = fn(KeywordDiscriminant) -> String;
     26 
     27 /// Parses a given identifier.
     28 pub type KeywordParser = for<'a, 'i, 't> fn(
     29    context: &'a ParserContext,
     30    input: &'a mut Parser<'i, 't>,
     31 ) -> Result<KeywordDiscriminant, ParseError<'i>>;
     32 
     33 /// An evaluator for a given feature.
     34 ///
     35 /// This determines the kind of values that get parsed, too.
     36 #[allow(missing_docs)]
     37 pub enum Evaluator {
     38    Length(QueryFeatureGetter<CSSPixelLength>),
     39    OptionalLength(QueryFeatureGetter<Option<CSSPixelLength>>),
     40    Integer(QueryFeatureGetter<i32>),
     41    Float(QueryFeatureGetter<f32>),
     42    BoolInteger(QueryFeatureGetter<bool>),
     43    /// A non-negative number ratio, such as the one from device-pixel-ratio.
     44    NumberRatio(QueryFeatureGetter<Ratio>),
     45    OptionalNumberRatio(QueryFeatureGetter<Option<Ratio>>),
     46    /// A resolution.
     47    Resolution(QueryFeatureGetter<Resolution>),
     48    /// A keyword value.
     49    Enumerated {
     50        /// The parser to get a discriminant given a string.
     51        parser: KeywordParser,
     52        /// The serializer to get a string from a discriminant.
     53        ///
     54        /// This is guaranteed to be called with a keyword that `parser` has
     55        /// produced.
     56        serializer: KeywordSerializer,
     57        /// The evaluator itself. This is guaranteed to be called with a
     58        /// keyword that `parser` has produced.
     59        evaluator: fn(&computed::Context, Option<KeywordDiscriminant>) -> KleeneValue,
     60    },
     61 }
     62 
     63 /// A simple helper macro to create a keyword evaluator.
     64 ///
     65 /// This assumes that keyword feature expressions don't accept ranges, and
     66 /// asserts if that's not true. As of today there's nothing like that (does that
     67 /// even make sense?).
     68 macro_rules! keyword_evaluator {
     69    ($actual_evaluator:ident, $keyword_type:ty) => {{
     70        fn __parse<'i, 't>(
     71            context: &$crate::parser::ParserContext,
     72            input: &mut $crate::cssparser::Parser<'i, 't>,
     73        ) -> Result<$crate::queries::feature::KeywordDiscriminant, ::style_traits::ParseError<'i>>
     74        {
     75            let kw = <$keyword_type as $crate::parser::Parse>::parse(context, input)?;
     76            Ok(kw as $crate::queries::feature::KeywordDiscriminant)
     77        }
     78 
     79        fn __serialize(kw: $crate::queries::feature::KeywordDiscriminant) -> String {
     80            // This unwrap is ok because the only discriminants that get
     81            // back to us is the ones that `parse` produces.
     82            let value: $keyword_type = ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap();
     83            <$keyword_type as ::style_traits::ToCss>::to_css_string(&value)
     84        }
     85 
     86        fn __evaluate(
     87            context: &$crate::values::computed::Context,
     88            value: Option<$crate::queries::feature::KeywordDiscriminant>,
     89        ) -> selectors::kleene_value::KleeneValue {
     90            // This unwrap is ok because the only discriminants that get
     91            // back to us is the ones that `parse` produces.
     92            let value: Option<$keyword_type> =
     93                value.map(|kw| ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap());
     94            selectors::kleene_value::KleeneValue::from($actual_evaluator(context, value))
     95        }
     96 
     97        $crate::queries::feature::Evaluator::Enumerated {
     98            parser: __parse,
     99            serializer: __serialize,
    100            evaluator: __evaluate,
    101        }
    102    }};
    103 }
    104 
    105 /// Different flags or toggles that change how a expression is parsed or
    106 /// evaluated.
    107 #[derive(Clone, Copy, Debug, ToShmem)]
    108 pub struct FeatureFlags(u8);
    109 bitflags! {
    110    impl FeatureFlags : u8 {
    111        /// The feature should only be parsed in chrome and ua sheets.
    112        const CHROME_AND_UA_ONLY = 1 << 0;
    113        /// The feature requires a -webkit- prefix.
    114        const WEBKIT_PREFIX = 1 << 1;
    115        /// The feature requires the inline-axis containment.
    116        const CONTAINER_REQUIRES_INLINE_AXIS = 1 << 2;
    117        /// The feature requires the block-axis containment.
    118        const CONTAINER_REQUIRES_BLOCK_AXIS = 1 << 3;
    119        /// The feature requires containment in the physical width axis.
    120        const CONTAINER_REQUIRES_WIDTH_AXIS = 1 << 4;
    121        /// The feature requires containment in the physical height axis.
    122        const CONTAINER_REQUIRES_HEIGHT_AXIS = 1 << 5;
    123        /// The feature evaluation depends on the viewport size.
    124        const VIEWPORT_DEPENDENT = 1 << 6;
    125        /// The feature evaluation depends on style queries.
    126        const STYLE = 1 << 7;
    127    }
    128 }
    129 
    130 impl FeatureFlags {
    131    /// Returns parsing requirement flags.
    132    pub fn parsing_requirements(self) -> Self {
    133        self.intersection(Self::CHROME_AND_UA_ONLY | Self::WEBKIT_PREFIX)
    134    }
    135 
    136    /// Returns all the container axis flags.
    137    pub fn all_container_axes() -> Self {
    138        Self::CONTAINER_REQUIRES_INLINE_AXIS
    139            | Self::CONTAINER_REQUIRES_BLOCK_AXIS
    140            | Self::CONTAINER_REQUIRES_WIDTH_AXIS
    141            | Self::CONTAINER_REQUIRES_HEIGHT_AXIS
    142    }
    143 
    144    /// Returns our subset of container axis flags.
    145    pub fn container_axes(self) -> Self {
    146        self.intersection(Self::all_container_axes())
    147    }
    148 }
    149 
    150 /// Whether a feature allows ranges or not.
    151 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    152 #[allow(missing_docs)]
    153 pub enum AllowsRanges {
    154    Yes,
    155    No,
    156 }
    157 
    158 /// A description of a feature.
    159 pub struct QueryFeatureDescription {
    160    /// The feature name, in ascii lowercase.
    161    pub name: Atom,
    162    /// Whether min- / max- prefixes are allowed or not.
    163    pub allows_ranges: AllowsRanges,
    164    /// The evaluator, which we also use to determine which kind of value to
    165    /// parse.
    166    pub evaluator: Evaluator,
    167    /// Different feature-specific flags.
    168    pub flags: FeatureFlags,
    169 }
    170 
    171 impl QueryFeatureDescription {
    172    /// Whether this feature allows ranges.
    173    #[inline]
    174    pub fn allows_ranges(&self) -> bool {
    175        self.allows_ranges == AllowsRanges::Yes
    176    }
    177 }
    178 
    179 /// A simple helper to construct a `QueryFeatureDescription`.
    180 macro_rules! feature {
    181    ($name:expr, $allows_ranges:expr, $evaluator:expr, $flags:expr,) => {
    182        $crate::queries::feature::QueryFeatureDescription {
    183            name: $name,
    184            allows_ranges: $allows_ranges,
    185            evaluator: $evaluator,
    186            flags: $flags,
    187        }
    188    };
    189 }
    190 
    191 impl fmt::Debug for QueryFeatureDescription {
    192    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    193        f.debug_struct("QueryFeatureDescription")
    194            .field("name", &self.name)
    195            .field("allows_ranges", &self.allows_ranges)
    196            .field("flags", &self.flags)
    197            .finish()
    198    }
    199 }