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