Bug 1756130 [wpt PR 32898] - [CSP] Enhance unsafe-eval test to check both realms...
[gecko.git] / servo / components / style / parser.rs
blob837b97400241d0e534a7bcb76de9948c283762ce
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/. */
5 //! The context within which CSS code is parsed.
7 use crate::context::QuirksMode;
8 use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
9 use crate::stylesheets::{CssRuleType, Namespaces, Origin, UrlExtraData};
10 use crate::use_counters::UseCounters;
11 use cssparser::{Parser, SourceLocation, UnicodeRange};
12 use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator};
14 /// Asserts that all ParsingMode flags have a matching ParsingMode value in gecko.
15 #[cfg(feature = "gecko")]
16 #[inline]
17 pub fn assert_parsing_mode_match() {
18     use crate::gecko_bindings::structs;
20     macro_rules! check_parsing_modes {
21         ( $( $a:ident => $b:path ),*, ) => {
22             if cfg!(debug_assertions) {
23                 let mut modes = ParsingMode::all();
24                 $(
25                     assert_eq!(structs::$a as usize, $b.bits() as usize, stringify!($b));
26                     modes.remove($b);
27                 )*
28                 assert_eq!(modes, ParsingMode::empty(), "all ParsingMode bits should have an assertion");
29             }
30         }
31     }
33     check_parsing_modes! {
34         ParsingMode_Default => ParsingMode::DEFAULT,
35         ParsingMode_AllowUnitlessLength => ParsingMode::ALLOW_UNITLESS_LENGTH,
36         ParsingMode_AllowAllNumericValues => ParsingMode::ALLOW_ALL_NUMERIC_VALUES,
37     }
40 /// The data that the parser needs from outside in order to parse a stylesheet.
41 pub struct ParserContext<'a> {
42     /// The `Origin` of the stylesheet, whether it's a user, author or
43     /// user-agent stylesheet.
44     pub stylesheet_origin: Origin,
45     /// The extra data we need for resolving url values.
46     pub url_data: &'a UrlExtraData,
47     /// The current rule type, if any.
48     pub rule_type: Option<CssRuleType>,
49     /// The mode to use when parsing.
50     pub parsing_mode: ParsingMode,
51     /// The quirks mode of this stylesheet.
52     pub quirks_mode: QuirksMode,
53     /// The active error reporter, or none if error reporting is disabled.
54     error_reporter: Option<&'a dyn ParseErrorReporter>,
55     /// The currently active namespaces.
56     pub namespaces: Option<&'a Namespaces>,
57     /// The use counters we want to record while parsing style rules, if any.
58     pub use_counters: Option<&'a UseCounters>,
61 impl<'a> ParserContext<'a> {
62     /// Create a parser context.
63     #[inline]
64     pub fn new(
65         stylesheet_origin: Origin,
66         url_data: &'a UrlExtraData,
67         rule_type: Option<CssRuleType>,
68         parsing_mode: ParsingMode,
69         quirks_mode: QuirksMode,
70         error_reporter: Option<&'a dyn ParseErrorReporter>,
71         use_counters: Option<&'a UseCounters>,
72     ) -> Self {
73         Self {
74             stylesheet_origin,
75             url_data,
76             rule_type,
77             parsing_mode,
78             quirks_mode,
79             error_reporter,
80             namespaces: None,
81             use_counters,
82         }
83     }
85     /// Create a parser context based on a previous context, but with a modified
86     /// rule type.
87     #[inline]
88     pub fn new_with_rule_type(
89         context: &'a ParserContext,
90         rule_type: CssRuleType,
91         namespaces: &'a Namespaces,
92     ) -> ParserContext<'a> {
93         Self {
94             stylesheet_origin: context.stylesheet_origin,
95             url_data: context.url_data,
96             rule_type: Some(rule_type),
97             parsing_mode: context.parsing_mode,
98             quirks_mode: context.quirks_mode,
99             namespaces: Some(namespaces),
100             error_reporter: context.error_reporter,
101             use_counters: context.use_counters,
102         }
103     }
105     /// Whether we're in a @page rule.
106     #[inline]
107     pub fn in_page_rule(&self) -> bool {
108         self.rule_type
109             .map_or(false, |rule_type| rule_type == CssRuleType::Page)
110     }
112     /// Get the rule type, which assumes that one is available.
113     pub fn rule_type(&self) -> CssRuleType {
114         self.rule_type
115             .expect("Rule type expected, but none was found.")
116     }
118     /// Returns whether CSS error reporting is enabled.
119     #[inline]
120     pub fn error_reporting_enabled(&self) -> bool {
121         self.error_reporter.is_some()
122     }
124     /// Record a CSS parse error with this context’s error reporting.
125     pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) {
126         let error_reporter = match self.error_reporter {
127             Some(r) => r,
128             None => return,
129         };
131         error_reporter.report_error(self.url_data, location, error)
132     }
134     /// Whether we're in a user-agent stylesheet.
135     #[inline]
136     pub fn in_ua_sheet(&self) -> bool {
137         self.stylesheet_origin == Origin::UserAgent
138     }
140     /// Returns whether chrome-only rules should be parsed.
141     #[inline]
142     pub fn chrome_rules_enabled(&self) -> bool {
143         self.url_data.chrome_rules_enabled() || self.stylesheet_origin == Origin::User
144     }
146     /// Whether we're in a user-agent stylesheet or chrome rules are enabled.
147     #[inline]
148     pub fn in_ua_or_chrome_sheet(&self) -> bool {
149         self.in_ua_sheet() || self.chrome_rules_enabled()
150     }
153 /// A trait to abstract parsing of a specified value given a `ParserContext` and
154 /// CSS input.
156 /// This can be derived on keywords with `#[derive(Parse)]`.
158 /// The derive code understands the following attributes on each of the variants:
160 ///  * `#[parse(aliases = "foo,bar")]` can be used to alias a value with another
161 ///    at parse-time.
163 ///  * `#[parse(condition = "function")]` can be used to make the parsing of the
164 ///    value conditional on `function`, which needs to fulfill
165 ///    `fn(&ParserContext) -> bool`.
166 pub trait Parse: Sized {
167     /// Parse a value of this type.
168     ///
169     /// Returns an error on failure.
170     fn parse<'i, 't>(
171         context: &ParserContext,
172         input: &mut Parser<'i, 't>,
173     ) -> Result<Self, ParseError<'i>>;
176 impl<T> Parse for Vec<T>
177 where
178     T: Parse + OneOrMoreSeparated,
179     <T as OneOrMoreSeparated>::S: Separator,
181     fn parse<'i, 't>(
182         context: &ParserContext,
183         input: &mut Parser<'i, 't>,
184     ) -> Result<Self, ParseError<'i>> {
185         <T as OneOrMoreSeparated>::S::parse(input, |i| T::parse(context, i))
186     }
189 impl<T> Parse for Box<T>
190 where
191     T: Parse,
193     fn parse<'i, 't>(
194         context: &ParserContext,
195         input: &mut Parser<'i, 't>,
196     ) -> Result<Self, ParseError<'i>> {
197         T::parse(context, input).map(Box::new)
198     }
201 impl Parse for crate::OwnedStr {
202     fn parse<'i, 't>(
203         _: &ParserContext,
204         input: &mut Parser<'i, 't>,
205     ) -> Result<Self, ParseError<'i>> {
206         Ok(input.expect_string()?.as_ref().to_owned().into())
207     }
210 impl Parse for UnicodeRange {
211     fn parse<'i, 't>(
212         _: &ParserContext,
213         input: &mut Parser<'i, 't>,
214     ) -> Result<Self, ParseError<'i>> {
215         Ok(UnicodeRange::parse(input)?)
216     }