1#![deny(missing_docs)]
8
9use crate::selector_parser::SelectorImpl;
10use crate::stylesheets::UrlExtraData;
11use cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation};
12use selectors::SelectorList;
13use selectors::parser::{Combinator, Component, RelativeSelector, Selector};
14use selectors::visitor::{SelectorListKind, SelectorVisitor};
15use std::fmt;
16use style_traits::ParseError;
17
18#[derive(Debug)]
20pub enum ContextualParseError<'a> {
21 UnsupportedPropertyDeclaration(&'a str, ParseError, &'a [SelectorList<SelectorImpl>]),
23 UnsupportedPropertyDescriptor(&'a str, ParseError),
25 UnsupportedFontFaceDescriptor(&'a str, ParseError),
27 UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError),
29 UnsupportedFontPaletteValuesDescriptor(&'a str, ParseError),
31 InvalidKeyframeRule(&'a str, ParseError),
33 InvalidFontFeatureValuesRule(&'a str, ParseError),
35 InvalidRule(&'a str, ParseError),
37 UnsupportedRule(&'a str, ParseError),
39 UnsupportedViewportDescriptorDeclaration(&'a str, ParseError),
41 UnsupportedCounterStyleDescriptorDeclaration(&'a str, ParseError),
43 InvalidCounterStyleWithoutSymbols(String),
45 InvalidCounterStyleNotEnoughSymbols(String),
47 InvalidCounterStyleWithoutAdditiveSymbols,
49 InvalidCounterStyleExtendsWithSymbols,
51 InvalidCounterStyleExtendsWithAdditiveSymbols,
53 InvalidMediaRule(&'a str, ParseError),
55 UnsupportedValue(&'a str, ParseError),
57 NeverMatchingHostSelector(String),
59 UnsupportedViewTransitionDescriptor(&'a str, ParseError),
61}
62
63impl<'a> fmt::Display for ContextualParseError<'a> {
64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result {
66 match err.kind {
67 ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken) => {
68 write!(f, "found unexpected token")
69 },
70 ParseErrorKind::Basic(BasicParseErrorKind::TooManyNestedBlocks) => {
71 write!(f, "too many nested blocks")
72 },
73 ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => {
74 write!(f, "unexpected end of input")
75 },
76 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid) => {
77 write!(f, "@ rule invalid")
78 },
79 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => {
80 write!(f, "@ rule invalid")
81 },
82 ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => {
83 write!(f, "qualified rule invalid")
84 },
85 ParseErrorKind::Custom(ref err) => write!(f, "{:?}", err),
86 }
87 }
88
89 match *self {
90 ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err, _selectors) => {
91 write!(f, "Unsupported property declaration: '{}', ", decl)?;
92 parse_error_to_str(err, f)
93 },
94 ContextualParseError::UnsupportedPropertyDescriptor(decl, ref err) => {
95 write!(
96 f,
97 "Unsupported @property descriptor declaration: '{}', ",
98 decl
99 )?;
100 parse_error_to_str(err, f)
101 },
102 ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => {
103 write!(
104 f,
105 "Unsupported @font-face descriptor declaration: '{}', ",
106 decl
107 )?;
108 parse_error_to_str(err, f)
109 },
110 ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => {
111 write!(
112 f,
113 "Unsupported @font-feature-values descriptor declaration: '{}', ",
114 decl
115 )?;
116 parse_error_to_str(err, f)
117 },
118 ContextualParseError::UnsupportedFontPaletteValuesDescriptor(decl, ref err) => {
119 write!(
120 f,
121 "Unsupported @font-palette-values descriptor declaration: '{}', ",
122 decl
123 )?;
124 parse_error_to_str(err, f)
125 },
126 ContextualParseError::InvalidKeyframeRule(rule, ref err) => {
127 write!(f, "Invalid keyframe rule: '{}', ", rule)?;
128 parse_error_to_str(err, f)
129 },
130 ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => {
131 write!(f, "Invalid font feature value rule: '{}', ", rule)?;
132 parse_error_to_str(err, f)
133 },
134 ContextualParseError::InvalidRule(rule, ref err) => {
135 write!(f, "Invalid rule: '{}', ", rule)?;
136 parse_error_to_str(err, f)
137 },
138 ContextualParseError::UnsupportedRule(rule, ref err) => {
139 write!(f, "Unsupported rule: '{}', ", rule)?;
140 parse_error_to_str(err, f)
141 },
142 ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => {
143 write!(
144 f,
145 "Unsupported @viewport descriptor declaration: '{}', ",
146 decl
147 )?;
148 parse_error_to_str(err, f)
149 },
150 ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => {
151 write!(
152 f,
153 "Unsupported @counter-style descriptor declaration: '{}', ",
154 decl
155 )?;
156 parse_error_to_str(err, f)
157 },
158 ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!(
159 f,
160 "Invalid @counter-style rule: 'system: {}' without 'symbols'",
161 system
162 ),
163 ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!(
164 f,
165 "Invalid @counter-style rule: 'system: {}' less than two 'symbols'",
166 system
167 ),
168 ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!(
169 f,
170 "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'"
171 ),
172 ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!(
173 f,
174 "Invalid @counter-style rule: 'system: extends …' with 'symbols'"
175 ),
176 ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!(
177 f,
178 "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'"
179 ),
180 ContextualParseError::InvalidMediaRule(media_rule, ref err) => {
181 write!(f, "Invalid media rule: {}, ", media_rule)?;
182 parse_error_to_str(err, f)
183 },
184 ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
185 ContextualParseError::NeverMatchingHostSelector(ref selector) => {
186 write!(f, ":host selector is not featureless: {}", selector)
187 },
188 ContextualParseError::UnsupportedViewTransitionDescriptor(decl, ref err) => {
189 write!(
190 f,
191 "Unsupported @view-transition descriptor declaration: '{}', ",
192 decl
193 )?;
194 parse_error_to_str(err, f)
195 },
196 }
197 }
198}
199
200pub trait ParseErrorReporter {
202 fn report_error(
207 &self,
208 url: &UrlExtraData,
209 location: SourceLocation,
210 error: ContextualParseError,
211 );
212}
213
214#[cfg(feature = "servo")]
221pub struct RustLogReporter;
222
223#[cfg(feature = "servo")]
224impl ParseErrorReporter for RustLogReporter {
225 fn report_error(
226 &self,
227 url: &UrlExtraData,
228 location: SourceLocation,
229 error: ContextualParseError,
230 ) {
231 if log_enabled!(log::Level::Info) {
232 info!(
233 "Url:\t{}\n{}:{} {}",
234 url.as_str(),
235 location.line,
236 location.column,
237 error
238 )
239 }
240 }
241}
242
243#[repr(u8)]
246#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
247pub enum SelectorWarningKind {
248 UnconstraintedRelativeSelector,
251 SiblingCombinatorAfterScopeSelector,
254}
255
256impl SelectorWarningKind {
257 pub fn from_selector(selector: &Selector<SelectorImpl>) -> Vec<Self> {
259 let mut result = vec![];
260 if UnconstrainedRelativeSelectorVisitor::has_warning(selector, 0, false) {
261 result.push(SelectorWarningKind::UnconstraintedRelativeSelector);
262 }
263 if SiblingCombinatorAfterScopeSelectorVisitor::has_warning(selector) {
264 result.push(SelectorWarningKind::SiblingCombinatorAfterScopeSelector);
265 }
266 result
267 }
268}
269
270struct PerCompoundState {
272 relative_selector_found: bool,
274 constrained: bool,
276 in_relative_selector: bool,
278}
279
280impl PerCompoundState {
281 fn new(in_relative_selector: bool) -> Self {
282 Self {
283 relative_selector_found: false,
284 constrained: false,
285 in_relative_selector,
286 }
287 }
288}
289
290struct UnconstrainedRelativeSelectorVisitor {
292 compound_state: PerCompoundState,
293}
294
295impl UnconstrainedRelativeSelectorVisitor {
296 fn new(in_relative_selector: bool) -> Self {
297 Self {
298 compound_state: PerCompoundState::new(in_relative_selector),
299 }
300 }
301
302 fn has_warning(
303 selector: &Selector<SelectorImpl>,
304 offset: usize,
305 in_relative_selector: bool,
306 ) -> bool {
307 let relative_selector = matches!(
308 selector.iter_raw_parse_order_from(0).next().unwrap(),
309 Component::RelativeSelectorAnchor
310 );
311 debug_assert!(
312 !relative_selector || offset == 0,
313 "Checking relative selector from non-rightmost?"
314 );
315 let mut visitor = Self::new(in_relative_selector);
316 let mut iter = if relative_selector {
317 selector.iter_skip_relative_selector_anchor()
318 } else {
319 selector.iter_from(offset)
320 };
321 loop {
322 visitor.compound_state = PerCompoundState::new(in_relative_selector);
323
324 for s in &mut iter {
325 s.visit(&mut visitor);
326 }
327
328 if (visitor.compound_state.relative_selector_found
329 || visitor.compound_state.in_relative_selector)
330 && !visitor.compound_state.constrained
331 {
332 return true;
333 }
334
335 if iter.next_sequence().is_none() {
336 break;
337 }
338 }
339 false
340 }
341}
342
343impl SelectorVisitor for UnconstrainedRelativeSelectorVisitor {
344 type Impl = SelectorImpl;
345
346 fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool {
347 match c {
348 Component::Is(..)
350 | Component::Where(..)
351 | Component::Negation(..)
352 | Component::Has(..) => (),
353 Component::ExplicitUniversalType => (),
354 _ => self.compound_state.constrained |= true,
355 };
356 true
357 }
358
359 fn visit_selector_list(
360 &mut self,
361 _list_kind: SelectorListKind,
362 list: &[Selector<Self::Impl>],
363 ) -> bool {
364 let mut all_constrained = true;
365 for s in list {
366 let mut offset = 0;
367 if !self.compound_state.in_relative_selector {
369 let mut nested = Self::new(false);
370 let mut iter = s.iter();
371 loop {
372 for c in &mut iter {
373 c.visit(&mut nested);
374 offset += 1;
375 }
376
377 let c = iter.next_sequence();
378 offset += 1;
379 if c.is_none_or(|c| !c.is_pseudo_element()) {
380 break;
381 }
382 }
383 all_constrained &= nested.compound_state.constrained;
385 }
386
387 if offset >= s.len() {
388 continue;
389 }
390
391 if Self::has_warning(s, offset, self.compound_state.in_relative_selector) {
393 self.compound_state.constrained = false;
394 if !self.compound_state.in_relative_selector {
395 self.compound_state.relative_selector_found = true;
396 }
397 return false;
398 }
399 }
400 self.compound_state.constrained |= all_constrained;
401 true
402 }
403
404 fn visit_relative_selector_list(&mut self, list: &[RelativeSelector<Self::Impl>]) -> bool {
405 debug_assert!(
406 !self.compound_state.in_relative_selector,
407 "Nested relative selector"
408 );
409 self.compound_state.relative_selector_found = true;
410
411 for rs in list {
412 if Self::has_warning(&rs.selector, 0, true) {
414 self.compound_state.constrained = false;
415 return false;
416 }
417 }
418 true
419 }
420}
421
422struct SiblingCombinatorAfterScopeSelectorVisitor {
423 right_combinator_is_sibling: bool,
424 found: bool,
425}
426
427impl SiblingCombinatorAfterScopeSelectorVisitor {
428 fn new(right_combinator_is_sibling: bool) -> Self {
429 Self {
430 right_combinator_is_sibling,
431 found: false,
432 }
433 }
434 fn has_warning(selector: &Selector<SelectorImpl>) -> bool {
435 if !selector.has_scope_selector() {
436 return false;
437 }
438 let visitor = SiblingCombinatorAfterScopeSelectorVisitor::new(false);
439 visitor.find_never_matching_scope_selector(selector)
440 }
441
442 fn find_never_matching_scope_selector(mut self, selector: &Selector<SelectorImpl>) -> bool {
443 selector.visit(&mut self);
444 self.found
445 }
446}
447
448impl SelectorVisitor for SiblingCombinatorAfterScopeSelectorVisitor {
449 type Impl = SelectorImpl;
450
451 fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool {
452 if !matches!(c, Component::Scope | Component::ImplicitScope) {
453 return true;
454 }
455 if self.right_combinator_is_sibling {
457 self.found = true;
458 }
459 true
460 }
461
462 fn visit_selector_list(
463 &mut self,
464 _list_kind: SelectorListKind,
465 list: &[Selector<Self::Impl>],
466 ) -> bool {
467 for s in list {
468 let list_visitor = Self::new(self.right_combinator_is_sibling);
469 self.found |= list_visitor.find_never_matching_scope_selector(s);
470 }
471 true
472 }
473
474 fn visit_complex_selector(&mut self, combinator_to_right: Option<Combinator>) -> bool {
475 if let Some(c) = combinator_to_right {
476 self.right_combinator_is_sibling = c.is_sibling();
479 }
480 true
481 }
482
483 }