1#![deny(missing_docs)]
8
9use crate::selector_parser::SelectorImpl;
10use crate::stylesheets::UrlExtraData;
11use cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation, Token};
12use selectors::parser::{Combinator, Component, RelativeSelector, Selector};
13use selectors::visitor::{SelectorListKind, SelectorVisitor};
14use selectors::SelectorList;
15use std::fmt;
16use style_traits::ParseError;
17
18#[derive(Debug)]
20pub enum ContextualParseError<'a> {
21 UnsupportedPropertyDeclaration(&'a str, ParseError<'a>, &'a [SelectorList<SelectorImpl>]),
23 UnsupportedPropertyDescriptor(&'a str, ParseError<'a>),
25 UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>),
27 UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError<'a>),
29 UnsupportedFontPaletteValuesDescriptor(&'a str, ParseError<'a>),
31 InvalidKeyframeRule(&'a str, ParseError<'a>),
33 InvalidFontFeatureValuesRule(&'a str, ParseError<'a>),
35 InvalidRule(&'a str, ParseError<'a>),
37 UnsupportedRule(&'a str, ParseError<'a>),
39 UnsupportedViewportDescriptorDeclaration(&'a str, ParseError<'a>),
41 UnsupportedCounterStyleDescriptorDeclaration(&'a str, ParseError<'a>),
43 InvalidCounterStyleWithoutSymbols(String),
45 InvalidCounterStyleNotEnoughSymbols(String),
47 InvalidCounterStyleWithoutAdditiveSymbols,
49 InvalidCounterStyleExtendsWithSymbols,
51 InvalidCounterStyleExtendsWithAdditiveSymbols,
53 InvalidMediaRule(&'a str, ParseError<'a>),
55 UnsupportedValue(&'a str, ParseError<'a>),
57 NeverMatchingHostSelector(String),
59 UnsupportedViewTransitionDescriptor(&'a str, ParseError<'a>),
61}
62
63impl<'a> fmt::Display for ContextualParseError<'a> {
64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 fn token_to_str(t: &Token, f: &mut fmt::Formatter) -> fmt::Result {
66 match *t {
67 Token::Ident(ref i) => write!(f, "identifier {}", i),
68 Token::AtKeyword(ref kw) => write!(f, "keyword @{}", kw),
69 Token::Hash(ref h) => write!(f, "hash #{}", h),
70 Token::IDHash(ref h) => write!(f, "id selector #{}", h),
71 Token::QuotedString(ref s) => write!(f, "quoted string \"{}\"", s),
72 Token::UnquotedUrl(ref u) => write!(f, "url {}", u),
73 Token::Delim(ref d) => write!(f, "delimiter {}", d),
74 Token::Number {
75 int_value: Some(i), ..
76 } => write!(f, "number {}", i),
77 Token::Number { value, .. } => write!(f, "number {}", value),
78 Token::Percentage {
79 int_value: Some(i), ..
80 } => write!(f, "percentage {}", i),
81 Token::Percentage { unit_value, .. } => {
82 write!(f, "percentage {}", unit_value * 100.)
83 },
84 Token::Dimension {
85 value, ref unit, ..
86 } => write!(f, "dimension {}{}", value, unit),
87 Token::WhiteSpace(_) => write!(f, "whitespace"),
88 Token::Comment(_) => write!(f, "comment"),
89 Token::Colon => write!(f, "colon (:)"),
90 Token::Semicolon => write!(f, "semicolon (;)"),
91 Token::Comma => write!(f, "comma (,)"),
92 Token::IncludeMatch => write!(f, "include match (~=)"),
93 Token::DashMatch => write!(f, "dash match (|=)"),
94 Token::PrefixMatch => write!(f, "prefix match (^=)"),
95 Token::SuffixMatch => write!(f, "suffix match ($=)"),
96 Token::SubstringMatch => write!(f, "substring match (*=)"),
97 Token::CDO => write!(f, "CDO (<!--)"),
98 Token::CDC => write!(f, "CDC (-->)"),
99 Token::Function(ref name) => write!(f, "function {}", name),
100 Token::ParenthesisBlock => write!(f, "parenthesis ("),
101 Token::SquareBracketBlock => write!(f, "square bracket ["),
102 Token::CurlyBracketBlock => write!(f, "curly bracket {{"),
103 Token::BadUrl(ref _u) => write!(f, "bad url parse error"),
104 Token::BadString(ref _s) => write!(f, "bad string parse error"),
105 Token::CloseParenthesis => write!(f, "unmatched close parenthesis"),
106 Token::CloseSquareBracket => write!(f, "unmatched close square bracket"),
107 Token::CloseCurlyBracket => write!(f, "unmatched close curly bracket"),
108 }
109 }
110
111 fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result {
112 match err.kind {
113 ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)) => {
114 write!(f, "found unexpected ")?;
115 token_to_str(t, f)
116 },
117 ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => {
118 write!(f, "unexpected end of input")
119 },
120 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(ref i)) => {
121 write!(f, "@ rule invalid: {}", i)
122 },
123 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => {
124 write!(f, "@ rule invalid")
125 },
126 ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => {
127 write!(f, "qualified rule invalid")
128 },
129 ParseErrorKind::Custom(ref err) => write!(f, "{:?}", err),
130 }
131 }
132
133 match *self {
134 ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err, _selectors) => {
135 write!(f, "Unsupported property declaration: '{}', ", decl)?;
136 parse_error_to_str(err, f)
137 },
138 ContextualParseError::UnsupportedPropertyDescriptor(decl, ref err) => {
139 write!(
140 f,
141 "Unsupported @property descriptor declaration: '{}', ",
142 decl
143 )?;
144 parse_error_to_str(err, f)
145 },
146 ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => {
147 write!(
148 f,
149 "Unsupported @font-face descriptor declaration: '{}', ",
150 decl
151 )?;
152 parse_error_to_str(err, f)
153 },
154 ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => {
155 write!(
156 f,
157 "Unsupported @font-feature-values descriptor declaration: '{}', ",
158 decl
159 )?;
160 parse_error_to_str(err, f)
161 },
162 ContextualParseError::UnsupportedFontPaletteValuesDescriptor(decl, ref err) => {
163 write!(
164 f,
165 "Unsupported @font-palette-values descriptor declaration: '{}', ",
166 decl
167 )?;
168 parse_error_to_str(err, f)
169 },
170 ContextualParseError::InvalidKeyframeRule(rule, ref err) => {
171 write!(f, "Invalid keyframe rule: '{}', ", rule)?;
172 parse_error_to_str(err, f)
173 },
174 ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => {
175 write!(f, "Invalid font feature value rule: '{}', ", rule)?;
176 parse_error_to_str(err, f)
177 },
178 ContextualParseError::InvalidRule(rule, ref err) => {
179 write!(f, "Invalid rule: '{}', ", rule)?;
180 parse_error_to_str(err, f)
181 },
182 ContextualParseError::UnsupportedRule(rule, ref err) => {
183 write!(f, "Unsupported rule: '{}', ", rule)?;
184 parse_error_to_str(err, f)
185 },
186 ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => {
187 write!(
188 f,
189 "Unsupported @viewport descriptor declaration: '{}', ",
190 decl
191 )?;
192 parse_error_to_str(err, f)
193 },
194 ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => {
195 write!(
196 f,
197 "Unsupported @counter-style descriptor declaration: '{}', ",
198 decl
199 )?;
200 parse_error_to_str(err, f)
201 },
202 ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!(
203 f,
204 "Invalid @counter-style rule: 'system: {}' without 'symbols'",
205 system
206 ),
207 ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!(
208 f,
209 "Invalid @counter-style rule: 'system: {}' less than two 'symbols'",
210 system
211 ),
212 ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!(
213 f,
214 "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'"
215 ),
216 ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!(
217 f,
218 "Invalid @counter-style rule: 'system: extends …' with 'symbols'"
219 ),
220 ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!(
221 f,
222 "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'"
223 ),
224 ContextualParseError::InvalidMediaRule(media_rule, ref err) => {
225 write!(f, "Invalid media rule: {}, ", media_rule)?;
226 parse_error_to_str(err, f)
227 },
228 ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
229 ContextualParseError::NeverMatchingHostSelector(ref selector) => {
230 write!(f, ":host selector is not featureless: {}", selector)
231 },
232 ContextualParseError::UnsupportedViewTransitionDescriptor(decl, ref err) => {
233 write!(
234 f,
235 "Unsupported @view-transition descriptor declaration: '{}', ",
236 decl
237 )?;
238 parse_error_to_str(err, f)
239 },
240 }
241 }
242}
243
244pub trait ParseErrorReporter {
246 fn report_error(
251 &self,
252 url: &UrlExtraData,
253 location: SourceLocation,
254 error: ContextualParseError,
255 );
256}
257
258#[cfg(feature = "servo")]
265pub struct RustLogReporter;
266
267#[cfg(feature = "servo")]
268impl ParseErrorReporter for RustLogReporter {
269 fn report_error(
270 &self,
271 url: &UrlExtraData,
272 location: SourceLocation,
273 error: ContextualParseError,
274 ) {
275 if log_enabled!(log::Level::Info) {
276 info!(
277 "Url:\t{}\n{}:{} {}",
278 url.as_str(),
279 location.line,
280 location.column,
281 error
282 )
283 }
284 }
285}
286
287#[repr(u8)]
290#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
291pub enum SelectorWarningKind {
292 UnconstraintedRelativeSelector,
295 SiblingCombinatorAfterScopeSelector,
298}
299
300impl SelectorWarningKind {
301 pub fn from_selector(selector: &Selector<SelectorImpl>) -> Vec<Self> {
303 let mut result = vec![];
304 if UnconstrainedRelativeSelectorVisitor::has_warning(selector, 0, false) {
305 result.push(SelectorWarningKind::UnconstraintedRelativeSelector);
306 }
307 if SiblingCombinatorAfterScopeSelectorVisitor::has_warning(selector) {
308 result.push(SelectorWarningKind::SiblingCombinatorAfterScopeSelector);
309 }
310 result
311 }
312}
313
314struct PerCompoundState {
316 relative_selector_found: bool,
318 constrained: bool,
320 in_relative_selector: bool,
322}
323
324impl PerCompoundState {
325 fn new(in_relative_selector: bool) -> Self {
326 Self {
327 relative_selector_found: false,
328 constrained: false,
329 in_relative_selector,
330 }
331 }
332}
333
334struct UnconstrainedRelativeSelectorVisitor {
336 compound_state: PerCompoundState,
337}
338
339impl UnconstrainedRelativeSelectorVisitor {
340 fn new(in_relative_selector: bool) -> Self {
341 Self {
342 compound_state: PerCompoundState::new(in_relative_selector),
343 }
344 }
345
346 fn has_warning(
347 selector: &Selector<SelectorImpl>,
348 offset: usize,
349 in_relative_selector: bool,
350 ) -> bool {
351 let relative_selector = matches!(
352 selector.iter_raw_parse_order_from(0).next().unwrap(),
353 Component::RelativeSelectorAnchor
354 );
355 debug_assert!(
356 !relative_selector || offset == 0,
357 "Checking relative selector from non-rightmost?"
358 );
359 let mut visitor = Self::new(in_relative_selector);
360 let mut iter = if relative_selector {
361 selector.iter_skip_relative_selector_anchor()
362 } else {
363 selector.iter_from(offset)
364 };
365 loop {
366 visitor.compound_state = PerCompoundState::new(in_relative_selector);
367
368 for s in &mut iter {
369 s.visit(&mut visitor);
370 }
371
372 if (visitor.compound_state.relative_selector_found
373 || visitor.compound_state.in_relative_selector)
374 && !visitor.compound_state.constrained
375 {
376 return true;
377 }
378
379 if iter.next_sequence().is_none() {
380 break;
381 }
382 }
383 false
384 }
385}
386
387impl SelectorVisitor for UnconstrainedRelativeSelectorVisitor {
388 type Impl = SelectorImpl;
389
390 fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool {
391 match c {
392 Component::Is(..)
394 | Component::Where(..)
395 | Component::Negation(..)
396 | Component::Has(..) => (),
397 Component::ExplicitUniversalType => (),
398 _ => self.compound_state.constrained |= true,
399 };
400 true
401 }
402
403 fn visit_selector_list(
404 &mut self,
405 _list_kind: SelectorListKind,
406 list: &[Selector<Self::Impl>],
407 ) -> bool {
408 let mut all_constrained = true;
409 for s in list {
410 let mut offset = 0;
411 if !self.compound_state.in_relative_selector {
413 let mut nested = Self::new(false);
414 let mut iter = s.iter();
415 loop {
416 for c in &mut iter {
417 c.visit(&mut nested);
418 offset += 1;
419 }
420
421 let c = iter.next_sequence();
422 offset += 1;
423 if c.map_or(true, |c| !c.is_pseudo_element()) {
424 break;
425 }
426 }
427 all_constrained &= nested.compound_state.constrained;
429 }
430
431 if offset >= s.len() {
432 continue;
433 }
434
435 if Self::has_warning(s, offset, self.compound_state.in_relative_selector) {
437 self.compound_state.constrained = false;
438 if !self.compound_state.in_relative_selector {
439 self.compound_state.relative_selector_found = true;
440 }
441 return false;
442 }
443 }
444 self.compound_state.constrained |= all_constrained;
445 true
446 }
447
448 fn visit_relative_selector_list(&mut self, list: &[RelativeSelector<Self::Impl>]) -> bool {
449 debug_assert!(
450 !self.compound_state.in_relative_selector,
451 "Nested relative selector"
452 );
453 self.compound_state.relative_selector_found = true;
454
455 for rs in list {
456 if Self::has_warning(&rs.selector, 0, true) {
458 self.compound_state.constrained = false;
459 return false;
460 }
461 }
462 true
463 }
464}
465
466struct SiblingCombinatorAfterScopeSelectorVisitor {
467 right_combinator_is_sibling: bool,
468 found: bool,
469}
470
471impl SiblingCombinatorAfterScopeSelectorVisitor {
472 fn new(right_combinator_is_sibling: bool) -> Self {
473 Self {
474 right_combinator_is_sibling,
475 found: false,
476 }
477 }
478 fn has_warning(selector: &Selector<SelectorImpl>) -> bool {
479 if !selector.has_scope_selector() {
480 return false;
481 }
482 let visitor = SiblingCombinatorAfterScopeSelectorVisitor::new(false);
483 visitor.find_never_matching_scope_selector(selector)
484 }
485
486 fn find_never_matching_scope_selector(mut self, selector: &Selector<SelectorImpl>) -> bool {
487 selector.visit(&mut self);
488 self.found
489 }
490}
491
492impl SelectorVisitor for SiblingCombinatorAfterScopeSelectorVisitor {
493 type Impl = SelectorImpl;
494
495 fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool {
496 if !matches!(c, Component::Scope | Component::ImplicitScope) {
497 return true;
498 }
499 if self.right_combinator_is_sibling {
501 self.found = true;
502 }
503 true
504 }
505
506 fn visit_selector_list(
507 &mut self,
508 _list_kind: SelectorListKind,
509 list: &[Selector<Self::Impl>],
510 ) -> bool {
511 for s in list {
512 let list_visitor = Self::new(self.right_combinator_is_sibling);
513 self.found |= list_visitor.find_never_matching_scope_selector(s);
514 }
515 true
516 }
517
518 fn visit_complex_selector(&mut self, combinator_to_right: Option<Combinator>) -> bool {
519 if let Some(c) = combinator_to_right {
520 self.right_combinator_is_sibling = c.is_sibling();
523 }
524 true
525 }
526
527 }