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 }