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::properties::PropertyIdRef;
11use crate::stylesheets::{CssRuleType, CssRuleTypes, Namespaces, Origin, UrlExtraData};
12use crate::use_counters::UseCounters;
13use cssparser::{Parser, SourceLocation, UnicodeRange};
14use selectors::parser::ParseRelative;
15use std::borrow::Cow;
16use std::cell::Cell;
17use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator};
18
19/// Nesting context for parsing rules.
20#[derive(Clone, Copy)]
21pub struct NestingContext {
22    /// All rule types we've nested into, if any.
23    pub rule_types: CssRuleTypes,
24    /// Whether or not parsing relative selector syntax should be allowed.
25    pub parse_relative: ParseRelative,
26}
27
28impl NestingContext {
29    fn parse_relative_for(rule_type: CssRuleType) -> ParseRelative {
30        match rule_type {
31            CssRuleType::Scope => ParseRelative::ForScope,
32            CssRuleType::Style => ParseRelative::ForNesting,
33            _ => ParseRelative::No,
34        }
35    }
36
37    /// Create a new nesting context.
38    pub fn new(rule_types: CssRuleTypes, parse_nested_rule_type: Option<CssRuleType>) -> Self {
39        Self {
40            rule_types,
41            parse_relative: parse_nested_rule_type
42                .map_or(ParseRelative::No, Self::parse_relative_for),
43        }
44    }
45
46    /// Create a new nesting context based on the given rule.
47    pub fn new_from_rule(rule_type: Option<CssRuleType>) -> Self {
48        Self {
49            rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(),
50            parse_relative: rule_type
51                .map(Self::parse_relative_for)
52                .unwrap_or(ParseRelative::No),
53        }
54    }
55
56    /// Save the current nesting context.
57    pub fn save(&mut self, rule_type: CssRuleType) -> Self {
58        let old = *self;
59        self.rule_types.insert(rule_type);
60        let new_parse_relative = Self::parse_relative_for(rule_type);
61        if new_parse_relative != ParseRelative::No {
62            self.parse_relative = new_parse_relative;
63        }
64        old
65    }
66
67    /// Load the saved nesting context.
68    pub fn restore(&mut self, saved: Self) {
69        *self = saved;
70    }
71}
72
73/// Context for the property declaration that is currently being parsed.
74#[derive(Debug, Default)]
75pub struct PropertyDeclarationContext<'a> {
76    /// Reference to the current property's name/ID.
77    property_id: Option<PropertyIdRef<'a>>,
78    /// Current count of random() functions that have been parsed so far
79    /// in the current property declaration. Used when a random() function
80    /// has a `property-index-scoped` key.
81    random_count: Cell<i32>,
82}
83
84impl<'a> PropertyDeclarationContext<'a> {
85    fn set(&mut self, property_id: PropertyIdRef<'a>) {
86        debug_assert!(
87            self.property_id.is_none(),
88            "Previous declaration should be empty"
89        );
90        self.property_id = Some(property_id);
91        self.random_count.set(0);
92    }
93
94    fn clear(&mut self) {
95        self.property_id = None;
96        self.random_count.set(0);
97    }
98
99    /// The declaration being parsed, if any.
100    pub fn property_id(&self) -> Option<PropertyIdRef<'a>> {
101        self.property_id
102    }
103
104    /// The index of the current random() function in this declaration.
105    pub fn current_random_index(&self) -> i32 {
106        self.random_count.get()
107    }
108
109    /// Claims the next index for a random() function in this declaration. The
110    /// index is 1-based and follows the order the functions appear in the
111    /// parsed value.
112    pub fn increment_random_count(&self) -> i32 {
113        let index = self.random_count.get() + 1;
114        self.random_count.set(index);
115        index
116    }
117}
118
119/// The data that the parser needs from outside in order to parse a stylesheet.
120pub struct ParserContext<'a> {
121    /// The `Origin` of the stylesheet, whether it's a user, author or
122    /// user-agent stylesheet.
123    pub stylesheet_origin: Origin,
124    /// The extra data we need for resolving url values.
125    pub url_data: &'a UrlExtraData,
126    /// The mode to use when parsing.
127    pub parsing_mode: ParsingMode,
128    /// The quirks mode of this stylesheet.
129    pub quirks_mode: QuirksMode,
130    /// The active error reporter, or none if error reporting is disabled.
131    error_reporter: Option<&'a dyn ParseErrorReporter>,
132    /// The currently active namespaces.
133    pub namespaces: Cow<'a, Namespaces>,
134    /// The use counters we want to record while parsing style rules, if any.
135    pub use_counters: Option<&'a UseCounters>,
136    /// Current nesting context.
137    pub nesting_context: NestingContext,
138    /// The relevant regions in the input that have been tainted by attr() if relevant.
139    pub attr_tainted_regions: AttrTaint,
140    /// Current property declaration context.
141    pub property_declaration_context: PropertyDeclarationContext<'a>,
142}
143
144impl<'a> ParserContext<'a> {
145    /// Create a parser context.
146    #[inline]
147    pub fn new(
148        stylesheet_origin: Origin,
149        url_data: &'a UrlExtraData,
150        rule_type: Option<CssRuleType>,
151        parsing_mode: ParsingMode,
152        quirks_mode: QuirksMode,
153        namespaces: Cow<'a, Namespaces>,
154        error_reporter: Option<&'a dyn ParseErrorReporter>,
155        use_counters: Option<&'a UseCounters>,
156        attr_tainted_regions: AttrTaint,
157    ) -> Self {
158        Self {
159            stylesheet_origin,
160            url_data,
161            parsing_mode,
162            quirks_mode,
163            error_reporter,
164            namespaces,
165            use_counters,
166            nesting_context: NestingContext::new_from_rule(rule_type),
167            attr_tainted_regions,
168            property_declaration_context: Default::default(),
169        }
170    }
171
172    /// Temporarily sets the property declaration context and executes the callback function,
173    /// returning its result.
174    pub fn with_property_declaration<R>(
175        &mut self,
176        property_id: PropertyIdRef<'a>,
177        cb: impl FnOnce(&Self) -> R,
178    ) -> R {
179        self.property_declaration_context.set(property_id);
180        let r = cb(self);
181        self.property_declaration_context.clear();
182        r
183    }
184
185    /// Temporarily sets the rule_type and executes the callback function, returning its result.
186    pub fn nest_for_rule<R>(
187        &mut self,
188        rule_type: CssRuleType,
189        cb: impl FnOnce(&mut Self) -> R,
190    ) -> R {
191        let old = self.nesting_context.save(rule_type);
192        let r = cb(self);
193        self.nesting_context.restore(old);
194        r
195    }
196
197    /// Temporarily adds a parsing mode flag and executes the callback, returning its result.
198    pub fn with_parsing_mode<R>(&mut self, mode: ParsingMode, cb: impl FnOnce(&Self) -> R) -> R {
199        let old = self.parsing_mode;
200        self.parsing_mode |= mode;
201        let r = cb(self);
202        self.parsing_mode = old;
203        r
204    }
205
206    /// Whether we're in a @page rule.
207    #[inline]
208    pub fn in_page_rule(&self) -> bool {
209        self.nesting_context.rule_types.contains(CssRuleType::Page)
210    }
211
212    /// Returns whether !important declarations are forbidden.
213    #[inline]
214    pub fn allows_important_declarations(&self) -> bool {
215        !self
216            .nesting_context
217            .rule_types
218            .intersects(CssRuleTypes::IMPORTANT_FORBIDDEN)
219    }
220
221    /// Returns whether we can parse element-dependent values.
222    #[inline]
223    pub fn has_element_context(&self) -> bool {
224        if self
225            .nesting_context
226            .rule_types
227            .intersects(CssRuleTypes::WITHOUT_ELEMENT_CONTEXT)
228        {
229            return false;
230        }
231
232        if self
233            .parsing_mode
234            .intersects(ParsingMode::MEDIA_QUERY_CONDITION)
235        {
236            return false;
237        }
238
239        if !self.allows_computational_dependence() {
240            return false;
241        }
242
243        true
244    }
245
246    /// Get the rule type, which assumes that one is available.
247    pub fn rule_types(&self) -> CssRuleTypes {
248        self.nesting_context.rule_types
249    }
250
251    /// Returns whether CSS error reporting is enabled.
252    #[inline]
253    pub fn error_reporting_enabled(&self) -> bool {
254        self.error_reporter.is_some()
255    }
256
257    /// Record a CSS parse error with this context’s error reporting.
258    pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) {
259        let error_reporter = match self.error_reporter {
260            Some(r) => r,
261            None => return,
262        };
263
264        error_reporter.report_error(self.url_data, location, error)
265    }
266
267    /// Whether we're in a user-agent stylesheet.
268    #[inline]
269    pub fn in_ua_sheet(&self) -> bool {
270        self.stylesheet_origin == Origin::UserAgent
271    }
272
273    /// Returns whether chrome-only rules should be parsed.
274    #[inline]
275    pub fn chrome_rules_enabled(&self) -> bool {
276        self.url_data.chrome_rules_enabled() || self.stylesheet_origin != Origin::Author
277    }
278
279    /// Whether the parsing mode allows units or functions that are not computationally independent.
280    #[inline]
281    pub fn allows_computational_dependence(&self) -> bool {
282        self.parsing_mode.allows_computational_dependence()
283    }
284
285    /// Whether any `<url>` over this `range` is disallowed due to attr()-tainting.
286    pub fn disallow_urls_in_range(&self, range: &AttrTaintedRange) -> bool {
287        self.attr_tainted_regions
288            .should_disallow_urls_in_range(range)
289    }
290}
291
292/// A trait to abstract parsing of a specified value given a `ParserContext` and
293/// CSS input.
294///
295/// This can be derived on keywords with `#[derive(Parse)]`.
296///
297/// The derive code understands the following attributes on each of the variants:
298///
299///  * `#[parse(aliases = "foo,bar")]` can be used to alias a value with another
300///    at parse-time.
301///
302///  * `#[parse(condition = "function")]` can be used to make the parsing of the
303///    value conditional on `function`, which needs to fulfill
304///    `fn(&ParserContext) -> bool`.
305///
306///  * `#[parse(parse_fn = "function")]` can be used to specify a function other than Parser::parse
307///    for a particular variant.
308pub trait Parse: Sized {
309    /// Parse a value of this type.
310    ///
311    /// Returns an error on failure.
312    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError>;
313}
314
315impl<T> Parse for Vec<T>
316where
317    T: Parse + OneOrMoreSeparated,
318    <T as OneOrMoreSeparated>::S: Separator,
319{
320    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
321        <T as OneOrMoreSeparated>::S::parse(input, |i| T::parse(context, i))
322    }
323}
324
325impl<T> Parse for Box<T>
326where
327    T: Parse,
328{
329    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
330        T::parse(context, input).map(Box::new)
331    }
332}
333
334impl Parse for crate::OwnedStr {
335    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
336        Ok(input.expect_string()?.as_ref().to_owned().into())
337    }
338}
339
340impl Parse for UnicodeRange {
341    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
342        Ok(UnicodeRange::parse(input)?)
343    }
344}