Source code

Revision control

Other Tools

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, Namespaces, Origin, UrlExtraData};
10
use crate::use_counters::UseCounters;
11
use cssparser::{Parser, SourceLocation, UnicodeRange};
12
use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator};
13
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;
19
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
}
32
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
}
38
}
39
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>,
59
}
60
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
}
84
85
/// Create a parser context for on-the-fly parsing in CSSOM
86
#[inline]
87
pub fn new_for_cssom(
88
url_data: &'a UrlExtraData,
89
rule_type: Option<CssRuleType>,
90
parsing_mode: ParsingMode,
91
quirks_mode: QuirksMode,
92
error_reporter: Option<&'a dyn ParseErrorReporter>,
93
use_counters: Option<&'a UseCounters>,
94
) -> Self {
95
Self::new(
96
Origin::Author,
97
url_data,
98
rule_type,
99
parsing_mode,
100
quirks_mode,
101
error_reporter,
102
use_counters,
103
)
104
}
105
106
/// Create a parser context based on a previous context, but with a modified
107
/// rule type.
108
#[inline]
109
pub fn new_with_rule_type(
110
context: &'a ParserContext,
111
rule_type: CssRuleType,
112
namespaces: &'a Namespaces,
113
) -> ParserContext<'a> {
114
Self {
115
stylesheet_origin: context.stylesheet_origin,
116
url_data: context.url_data,
117
rule_type: Some(rule_type),
118
parsing_mode: context.parsing_mode,
119
quirks_mode: context.quirks_mode,
120
namespaces: Some(namespaces),
121
error_reporter: context.error_reporter,
122
use_counters: context.use_counters,
123
}
124
}
125
126
/// Whether we're in a @page rule.
127
#[inline]
128
pub fn in_page_rule(&self) -> bool {
129
self.rule_type
130
.map_or(false, |rule_type| rule_type == CssRuleType::Page)
131
}
132
133
/// Get the rule type, which assumes that one is available.
134
pub fn rule_type(&self) -> CssRuleType {
135
self.rule_type
136
.expect("Rule type expected, but none was found.")
137
}
138
139
/// Returns whether CSS error reporting is enabled.
140
#[inline]
141
pub fn error_reporting_enabled(&self) -> bool {
142
self.error_reporter.is_some()
143
}
144
145
/// Record a CSS parse error with this context’s error reporting.
146
pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) {
147
let error_reporter = match self.error_reporter {
148
Some(r) => r,
149
None => return,
150
};
151
152
error_reporter.report_error(self.url_data, location, error)
153
}
154
155
/// Whether we're in a user-agent stylesheet.
156
#[inline]
157
pub fn in_ua_sheet(&self) -> bool {
158
self.stylesheet_origin == Origin::UserAgent
159
}
160
161
/// Returns whether chrome-only rules should be parsed.
162
#[inline]
163
pub fn chrome_rules_enabled(&self) -> bool {
164
self.url_data.is_chrome() || self.stylesheet_origin == Origin::User
165
}
166
167
/// Whether we're in a user-agent stylesheet or chrome rules are enabled.
168
#[inline]
169
pub fn in_ua_or_chrome_sheet(&self) -> bool {
170
self.in_ua_sheet() || self.chrome_rules_enabled()
171
}
172
}
173
174
/// A trait to abstract parsing of a specified value given a `ParserContext` and
175
/// CSS input.
176
///
177
/// This can be derived on keywords with `#[derive(Parse)]`.
178
///
179
/// The derive code understands the following attributes on each of the variants:
180
///
181
/// * `#[parse(aliases = "foo,bar")]` can be used to alias a value with another
182
/// at parse-time.
183
///
184
/// * `#[parse(condition = "function")]` can be used to make the parsing of the
185
/// value conditional on `function`, which needs to fulfill
186
/// `fn(&ParserContext) -> bool`.
187
pub trait Parse: Sized {
188
/// Parse a value of this type.
189
///
190
/// Returns an error on failure.
191
fn parse<'i, 't>(
192
context: &ParserContext,
193
input: &mut Parser<'i, 't>,
194
) -> Result<Self, ParseError<'i>>;
195
}
196
197
impl<T> Parse for Vec<T>
198
where
199
T: Parse + OneOrMoreSeparated,
200
<T as OneOrMoreSeparated>::S: Separator,
201
{
202
fn parse<'i, 't>(
203
context: &ParserContext,
204
input: &mut Parser<'i, 't>,
205
) -> Result<Self, ParseError<'i>> {
206
<T as OneOrMoreSeparated>::S::parse(input, |i| T::parse(context, i))
207
}
208
}
209
210
impl Parse for UnicodeRange {
211
fn parse<'i, 't>(
212
_context: &ParserContext,
213
input: &mut Parser<'i, 't>,
214
) -> Result<Self, ParseError<'i>> {
215
UnicodeRange::parse(input).map_err(|e| e.into())
216
}
217
}