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 //! Types used to report parsing errors.
6 
7 #![deny(missing_docs)]
8 
9 use crate::selector_parser::SelectorImpl;
10 use crate::stylesheets::UrlExtraData;
11 use cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation, Token};
12 use selectors::SelectorList;
13 use std::fmt;
14 use style_traits::ParseError;
15 
16 /// Errors that can be encountered while parsing CSS.
17 #[derive(Debug)]
18 pub enum ContextualParseError<'a> {
19     /// A property declaration was not recognized.
20     UnsupportedPropertyDeclaration(
21         &'a str,
22         ParseError<'a>,
23         Option<&'a SelectorList<SelectorImpl>>,
24     ),
25     /// A font face descriptor was not recognized.
26     UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>),
27     /// A font feature values descriptor was not recognized.
28     UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError<'a>),
29     /// A keyframe rule was not valid.
30     InvalidKeyframeRule(&'a str, ParseError<'a>),
31     /// A font feature values rule was not valid.
32     InvalidFontFeatureValuesRule(&'a str, ParseError<'a>),
33     /// A keyframe property declaration was not recognized.
34     UnsupportedKeyframePropertyDeclaration(&'a str, ParseError<'a>),
35     /// A rule was invalid for some reason.
36     InvalidRule(&'a str, ParseError<'a>),
37     /// A rule was not recognized.
38     UnsupportedRule(&'a str, ParseError<'a>),
39     /// A viewport descriptor declaration was not recognized.
40     UnsupportedViewportDescriptorDeclaration(&'a str, ParseError<'a>),
41     /// A counter style descriptor declaration was not recognized.
42     UnsupportedCounterStyleDescriptorDeclaration(&'a str, ParseError<'a>),
43     /// A counter style rule had no symbols.
44     InvalidCounterStyleWithoutSymbols(String),
45     /// A counter style rule had less than two symbols.
46     InvalidCounterStyleNotEnoughSymbols(String),
47     /// A counter style rule did not have additive-symbols.
48     InvalidCounterStyleWithoutAdditiveSymbols,
49     /// A counter style rule had extends with symbols.
50     InvalidCounterStyleExtendsWithSymbols,
51     /// A counter style rule had extends with additive-symbols.
52     InvalidCounterStyleExtendsWithAdditiveSymbols,
53     /// A media rule was invalid for some reason.
54     InvalidMediaRule(&'a str, ParseError<'a>),
55     /// A value was not recognized.
56     UnsupportedValue(&'a str, ParseError<'a>),
57     /// A never-matching `:host` selector was found.
58     NeverMatchingHostSelector(String),
59 }
60 
61 impl<'a> fmt::Display for ContextualParseError<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result62     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::UnsupportedFontFaceDescriptor(decl, ref err) => {
137                 write!(
138                     f,
139                     "Unsupported @font-face descriptor declaration: '{}', ",
140                     decl
141                 )?;
142                 parse_error_to_str(err, f)
143             },
144             ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => {
145                 write!(
146                     f,
147                     "Unsupported @font-feature-values descriptor declaration: '{}', ",
148                     decl
149                 )?;
150                 parse_error_to_str(err, f)
151             },
152             ContextualParseError::InvalidKeyframeRule(rule, ref err) => {
153                 write!(f, "Invalid keyframe rule: '{}', ", rule)?;
154                 parse_error_to_str(err, f)
155             },
156             ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => {
157                 write!(f, "Invalid font feature value rule: '{}', ", rule)?;
158                 parse_error_to_str(err, f)
159             },
160             ContextualParseError::UnsupportedKeyframePropertyDeclaration(decl, ref err) => {
161                 write!(f, "Unsupported keyframe property declaration: '{}', ", decl)?;
162                 parse_error_to_str(err, f)
163             },
164             ContextualParseError::InvalidRule(rule, ref err) => {
165                 write!(f, "Invalid rule: '{}', ", rule)?;
166                 parse_error_to_str(err, f)
167             },
168             ContextualParseError::UnsupportedRule(rule, ref err) => {
169                 write!(f, "Unsupported rule: '{}', ", rule)?;
170                 parse_error_to_str(err, f)
171             },
172             ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => {
173                 write!(
174                     f,
175                     "Unsupported @viewport descriptor declaration: '{}', ",
176                     decl
177                 )?;
178                 parse_error_to_str(err, f)
179             },
180             ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => {
181                 write!(
182                     f,
183                     "Unsupported @counter-style descriptor declaration: '{}', ",
184                     decl
185                 )?;
186                 parse_error_to_str(err, f)
187             },
188             ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!(
189                 f,
190                 "Invalid @counter-style rule: 'system: {}' without 'symbols'",
191                 system
192             ),
193             ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!(
194                 f,
195                 "Invalid @counter-style rule: 'system: {}' less than two 'symbols'",
196                 system
197             ),
198             ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!(
199                 f,
200                 "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'"
201             ),
202             ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!(
203                 f,
204                 "Invalid @counter-style rule: 'system: extends …' with 'symbols'"
205             ),
206             ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!(
207                 f,
208                 "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'"
209             ),
210             ContextualParseError::InvalidMediaRule(media_rule, ref err) => {
211                 write!(f, "Invalid media rule: {}, ", media_rule)?;
212                 parse_error_to_str(err, f)
213             },
214             ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
215             ContextualParseError::NeverMatchingHostSelector(ref selector) => {
216                 write!(f, ":host selector is not featureless: {}", selector)
217             },
218         }
219     }
220 }
221 
222 /// A generic trait for an error reporter.
223 pub trait ParseErrorReporter {
224     /// Called when the style engine detects an error.
225     ///
226     /// Returns the current input being parsed, the source location it was
227     /// reported from, and a message.
report_error( &self, url: &UrlExtraData, location: SourceLocation, error: ContextualParseError, )228     fn report_error(
229         &self,
230         url: &UrlExtraData,
231         location: SourceLocation,
232         error: ContextualParseError,
233     );
234 }
235 
236 /// An error reporter that uses [the `log` crate](https://github.com/rust-lang-nursery/log)
237 /// at `info` level.
238 ///
239 /// This logging is silent by default, and can be enabled with a `RUST_LOG=style=info`
240 /// environment variable.
241 /// (See [`env_logger`](https://rust-lang-nursery.github.io/log/env_logger/).)
242 #[cfg(feature = "servo")]
243 pub struct RustLogReporter;
244 
245 #[cfg(feature = "servo")]
246 impl ParseErrorReporter for RustLogReporter {
report_error( &self, url: &UrlExtraData, location: SourceLocation, error: ContextualParseError, )247     fn report_error(
248         &self,
249         url: &UrlExtraData,
250         location: SourceLocation,
251         error: ContextualParseError,
252     ) {
253         if log_enabled!(log::Level::Info) {
254             info!(
255                 "Url:\t{}\n{}:{} {}",
256                 url.as_str(),
257                 location.line,
258                 location.column,
259                 error
260             )
261         }
262     }
263 }
264