tor-browser

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

parser.rs (8093B)


      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 context within which CSS code is parsed.
      6 
      7 use crate::context::QuirksMode;
      8 use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
      9 use crate::stylesheets::{CssRuleType, CssRuleTypes, Namespaces, Origin, UrlExtraData};
     10 use crate::use_counters::UseCounters;
     11 use cssparser::{Parser, SourceLocation, UnicodeRange};
     12 use selectors::parser::ParseRelative;
     13 use std::borrow::Cow;
     14 use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator};
     15 
     16 /// Nesting context for parsing rules.
     17 #[derive(Clone, Copy)]
     18 pub struct NestingContext {
     19    /// All rule types we've nested into, if any.
     20    pub rule_types: CssRuleTypes,
     21    /// Whether or not parsing relative selector syntax should be allowed.
     22    pub parse_relative: ParseRelative,
     23 }
     24 
     25 impl NestingContext {
     26    fn parse_relative_for(rule_type: CssRuleType) -> ParseRelative {
     27        match rule_type {
     28            CssRuleType::Scope => ParseRelative::ForScope,
     29            CssRuleType::Style => ParseRelative::ForNesting,
     30            _ => ParseRelative::No,
     31        }
     32    }
     33 
     34    /// Create a new nesting context.
     35    pub fn new(rule_types: CssRuleTypes, parse_nested_rule_type: Option<CssRuleType>) -> Self {
     36        Self {
     37            rule_types,
     38            parse_relative: parse_nested_rule_type
     39                .map_or(ParseRelative::No, Self::parse_relative_for),
     40        }
     41    }
     42 
     43    /// Create a new nesting context based on the given rule.
     44    pub fn new_from_rule(rule_type: Option<CssRuleType>) -> Self {
     45        Self {
     46            rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(),
     47            parse_relative: rule_type
     48                .map(Self::parse_relative_for)
     49                .unwrap_or(ParseRelative::No),
     50        }
     51    }
     52 
     53    /// Save the current nesting context.
     54    pub fn save(&mut self, rule_type: CssRuleType) -> Self {
     55        let old = *self;
     56        self.rule_types.insert(rule_type);
     57        let new_parse_relative = Self::parse_relative_for(rule_type);
     58        if new_parse_relative != ParseRelative::No {
     59            self.parse_relative = new_parse_relative;
     60        }
     61        old
     62    }
     63 
     64    /// Load the saved nesting context.
     65    pub fn restore(&mut self, saved: Self) {
     66        *self = saved;
     67    }
     68 }
     69 
     70 /// The data that the parser needs from outside in order to parse a stylesheet.
     71 pub struct ParserContext<'a> {
     72    /// The `Origin` of the stylesheet, whether it's a user, author or
     73    /// user-agent stylesheet.
     74    pub stylesheet_origin: Origin,
     75    /// The extra data we need for resolving url values.
     76    pub url_data: &'a UrlExtraData,
     77    /// The mode to use when parsing.
     78    pub parsing_mode: ParsingMode,
     79    /// The quirks mode of this stylesheet.
     80    pub quirks_mode: QuirksMode,
     81    /// The active error reporter, or none if error reporting is disabled.
     82    error_reporter: Option<&'a dyn ParseErrorReporter>,
     83    /// The currently active namespaces.
     84    pub namespaces: Cow<'a, Namespaces>,
     85    /// The use counters we want to record while parsing style rules, if any.
     86    pub use_counters: Option<&'a UseCounters>,
     87    /// Current nesting context.
     88    pub nesting_context: NestingContext,
     89 }
     90 
     91 impl<'a> ParserContext<'a> {
     92    /// Create a parser context.
     93    #[inline]
     94    pub fn new(
     95        stylesheet_origin: Origin,
     96        url_data: &'a UrlExtraData,
     97        rule_type: Option<CssRuleType>,
     98        parsing_mode: ParsingMode,
     99        quirks_mode: QuirksMode,
    100        namespaces: Cow<'a, Namespaces>,
    101        error_reporter: Option<&'a dyn ParseErrorReporter>,
    102        use_counters: Option<&'a UseCounters>,
    103    ) -> Self {
    104        Self {
    105            stylesheet_origin,
    106            url_data,
    107            parsing_mode,
    108            quirks_mode,
    109            error_reporter,
    110            namespaces,
    111            use_counters,
    112            nesting_context: NestingContext::new_from_rule(rule_type),
    113        }
    114    }
    115 
    116    /// Temporarily sets the rule_type and executes the callback function, returning its result.
    117    pub fn nest_for_rule<R>(
    118        &mut self,
    119        rule_type: CssRuleType,
    120        cb: impl FnOnce(&mut Self) -> R,
    121    ) -> R {
    122        let old = self.nesting_context.save(rule_type);
    123        let r = cb(self);
    124        self.nesting_context.restore(old);
    125        r
    126    }
    127 
    128    /// Whether we're in a @page rule.
    129    #[inline]
    130    pub fn in_page_rule(&self) -> bool {
    131        self.nesting_context.rule_types.contains(CssRuleType::Page)
    132    }
    133 
    134    /// Returns whether !important declarations are forbidden.
    135    #[inline]
    136    pub fn allows_important_declarations(&self) -> bool {
    137        !self
    138            .nesting_context
    139            .rule_types
    140            .intersects(CssRuleTypes::IMPORTANT_FORBIDDEN)
    141    }
    142 
    143    /// Get the rule type, which assumes that one is available.
    144    pub fn rule_types(&self) -> CssRuleTypes {
    145        self.nesting_context.rule_types
    146    }
    147 
    148    /// Returns whether CSS error reporting is enabled.
    149    #[inline]
    150    pub fn error_reporting_enabled(&self) -> bool {
    151        self.error_reporter.is_some()
    152    }
    153 
    154    /// Record a CSS parse error with this context’s error reporting.
    155    pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) {
    156        let error_reporter = match self.error_reporter {
    157            Some(r) => r,
    158            None => return,
    159        };
    160 
    161        error_reporter.report_error(self.url_data, location, error)
    162    }
    163 
    164    /// Whether we're in a user-agent stylesheet.
    165    #[inline]
    166    pub fn in_ua_sheet(&self) -> bool {
    167        self.stylesheet_origin == Origin::UserAgent
    168    }
    169 
    170    /// Returns whether chrome-only rules should be parsed.
    171    #[inline]
    172    pub fn chrome_rules_enabled(&self) -> bool {
    173        self.url_data.chrome_rules_enabled() || self.stylesheet_origin != Origin::Author
    174    }
    175 
    176    /// Whether the parsing mode allows units or functions that are not computationally independent.
    177    #[inline]
    178    pub fn allows_computational_dependence(&self) -> bool {
    179        self.parsing_mode.allows_computational_dependence()
    180    }
    181 }
    182 
    183 /// A trait to abstract parsing of a specified value given a `ParserContext` and
    184 /// CSS input.
    185 ///
    186 /// This can be derived on keywords with `#[derive(Parse)]`.
    187 ///
    188 /// The derive code understands the following attributes on each of the variants:
    189 ///
    190 ///  * `#[parse(aliases = "foo,bar")]` can be used to alias a value with another
    191 ///    at parse-time.
    192 ///
    193 ///  * `#[parse(condition = "function")]` can be used to make the parsing of the
    194 ///    value conditional on `function`, which needs to fulfill
    195 ///    `fn(&ParserContext) -> bool`.
    196 ///
    197 ///  * `#[parse(parse_fn = "function")]` can be used to specify a function other than Parser::parse
    198 ///    for a particular variant.
    199 pub trait Parse: Sized {
    200    /// Parse a value of this type.
    201    ///
    202    /// Returns an error on failure.
    203    fn parse<'i, 't>(
    204        context: &ParserContext,
    205        input: &mut Parser<'i, 't>,
    206    ) -> Result<Self, ParseError<'i>>;
    207 }
    208 
    209 impl<T> Parse for Vec<T>
    210 where
    211    T: Parse + OneOrMoreSeparated,
    212    <T as OneOrMoreSeparated>::S: Separator,
    213 {
    214    fn parse<'i, 't>(
    215        context: &ParserContext,
    216        input: &mut Parser<'i, 't>,
    217    ) -> Result<Self, ParseError<'i>> {
    218        <T as OneOrMoreSeparated>::S::parse(input, |i| T::parse(context, i))
    219    }
    220 }
    221 
    222 impl<T> Parse for Box<T>
    223 where
    224    T: Parse,
    225 {
    226    fn parse<'i, 't>(
    227        context: &ParserContext,
    228        input: &mut Parser<'i, 't>,
    229    ) -> Result<Self, ParseError<'i>> {
    230        T::parse(context, input).map(Box::new)
    231    }
    232 }
    233 
    234 impl Parse for crate::OwnedStr {
    235    fn parse<'i, 't>(
    236        _: &ParserContext,
    237        input: &mut Parser<'i, 't>,
    238    ) -> Result<Self, ParseError<'i>> {
    239        Ok(input.expect_string()?.as_ref().to_owned().into())
    240    }
    241 }
    242 
    243 impl Parse for UnicodeRange {
    244    fn parse<'i, 't>(
    245        _: &ParserContext,
    246        input: &mut Parser<'i, 't>,
    247    ) -> Result<Self, ParseError<'i>> {
    248        Ok(UnicodeRange::parse(input)?)
    249    }
    250 }