Skip to main content

style/
parser.rs

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