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 }
58 
59 impl<'a> fmt::Display for ContextualParseError<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result60     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61         fn token_to_str(t: &Token, f: &mut fmt::Formatter) -> fmt::Result {
62             match *t {
63                 Token::Ident(ref i) => write!(f, "identifier {}", i),
64                 Token::AtKeyword(ref kw) => write!(f, "keyword @{}", kw),
65                 Token::Hash(ref h) => write!(f, "hash #{}", h),
66                 Token::IDHash(ref h) => write!(f, "id selector #{}", h),
67                 Token::QuotedString(ref s) => write!(f, "quoted string \"{}\"", s),
68                 Token::UnquotedUrl(ref u) => write!(f, "url {}", u),
69                 Token::Delim(ref d) => write!(f, "delimiter {}", d),
70                 Token::Number {
71                     int_value: Some(i), ..
72                 } => write!(f, "number {}", i),
73                 Token::Number { value, .. } => write!(f, "number {}", value),
74                 Token::Percentage {
75                     int_value: Some(i), ..
76                 } => write!(f, "percentage {}", i),
77                 Token::Percentage { unit_value, .. } => {
78                     write!(f, "percentage {}", unit_value * 100.)
79                 },
80                 Token::Dimension {
81                     value, ref unit, ..
82                 } => write!(f, "dimension {}{}", value, unit),
83                 Token::WhiteSpace(_) => write!(f, "whitespace"),
84                 Token::Comment(_) => write!(f, "comment"),
85                 Token::Colon => write!(f, "colon (:)"),
86                 Token::Semicolon => write!(f, "semicolon (;)"),
87                 Token::Comma => write!(f, "comma (,)"),
88                 Token::IncludeMatch => write!(f, "include match (~=)"),
89                 Token::DashMatch => write!(f, "dash match (|=)"),
90                 Token::PrefixMatch => write!(f, "prefix match (^=)"),
91                 Token::SuffixMatch => write!(f, "suffix match ($=)"),
92                 Token::SubstringMatch => write!(f, "substring match (*=)"),
93                 Token::CDO => write!(f, "CDO (<!--)"),
94                 Token::CDC => write!(f, "CDC (-->)"),
95                 Token::Function(ref name) => write!(f, "function {}", name),
96                 Token::ParenthesisBlock => write!(f, "parenthesis ("),
97                 Token::SquareBracketBlock => write!(f, "square bracket ["),
98                 Token::CurlyBracketBlock => write!(f, "curly bracket {{"),
99                 Token::BadUrl(ref _u) => write!(f, "bad url parse error"),
100                 Token::BadString(ref _s) => write!(f, "bad string parse error"),
101                 Token::CloseParenthesis => write!(f, "unmatched close parenthesis"),
102                 Token::CloseSquareBracket => write!(f, "unmatched close square bracket"),
103                 Token::CloseCurlyBracket => write!(f, "unmatched close curly bracket"),
104             }
105         }
106 
107         fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result {
108             match err.kind {
109                 ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)) => {
110                     write!(f, "found unexpected ")?;
111                     token_to_str(t, f)
112                 },
113                 ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => {
114                     write!(f, "unexpected end of input")
115                 },
116                 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(ref i)) => {
117                     write!(f, "@ rule invalid: {}", i)
118                 },
119                 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => {
120                     write!(f, "@ rule invalid")
121                 },
122                 ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => {
123                     write!(f, "qualified rule invalid")
124                 },
125                 ParseErrorKind::Custom(ref err) => write!(f, "{:?}", err),
126             }
127         }
128 
129         match *self {
130             ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err, _selectors) => {
131                 write!(f, "Unsupported property declaration: '{}', ", decl)?;
132                 parse_error_to_str(err, f)
133             },
134             ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => {
135                 write!(
136                     f,
137                     "Unsupported @font-face descriptor declaration: '{}', ",
138                     decl
139                 )?;
140                 parse_error_to_str(err, f)
141             },
142             ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => {
143                 write!(
144                     f,
145                     "Unsupported @font-feature-values descriptor declaration: '{}', ",
146                     decl
147                 )?;
148                 parse_error_to_str(err, f)
149             },
150             ContextualParseError::InvalidKeyframeRule(rule, ref err) => {
151                 write!(f, "Invalid keyframe rule: '{}', ", rule)?;
152                 parse_error_to_str(err, f)
153             },
154             ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => {
155                 write!(f, "Invalid font feature value rule: '{}', ", rule)?;
156                 parse_error_to_str(err, f)
157             },
158             ContextualParseError::UnsupportedKeyframePropertyDeclaration(decl, ref err) => {
159                 write!(f, "Unsupported keyframe property declaration: '{}', ", decl)?;
160                 parse_error_to_str(err, f)
161             },
162             ContextualParseError::InvalidRule(rule, ref err) => {
163                 write!(f, "Invalid rule: '{}', ", rule)?;
164                 parse_error_to_str(err, f)
165             },
166             ContextualParseError::UnsupportedRule(rule, ref err) => {
167                 write!(f, "Unsupported rule: '{}', ", rule)?;
168                 parse_error_to_str(err, f)
169             },
170             ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => {
171                 write!(
172                     f,
173                     "Unsupported @viewport descriptor declaration: '{}', ",
174                     decl
175                 )?;
176                 parse_error_to_str(err, f)
177             },
178             ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => {
179                 write!(
180                     f,
181                     "Unsupported @counter-style descriptor declaration: '{}', ",
182                     decl
183                 )?;
184                 parse_error_to_str(err, f)
185             },
186             ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!(
187                 f,
188                 "Invalid @counter-style rule: 'system: {}' without 'symbols'",
189                 system
190             ),
191             ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!(
192                 f,
193                 "Invalid @counter-style rule: 'system: {}' less than two 'symbols'",
194                 system
195             ),
196             ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!(
197                 f,
198                 "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'"
199             ),
200             ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!(
201                 f,
202                 "Invalid @counter-style rule: 'system: extends …' with 'symbols'"
203             ),
204             ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!(
205                 f,
206                 "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'"
207             ),
208             ContextualParseError::InvalidMediaRule(media_rule, ref err) => {
209                 write!(f, "Invalid media rule: {}, ", media_rule)?;
210                 parse_error_to_str(err, f)
211             },
212             ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
213         }
214     }
215 }
216 
217 /// A generic trait for an error reporter.
218 pub trait ParseErrorReporter {
219     /// Called when the style engine detects an error.
220     ///
221     /// Returns the current input being parsed, the source location it was
222     /// reported from, and a message.
report_error( &self, url: &UrlExtraData, location: SourceLocation, error: ContextualParseError, )223     fn report_error(
224         &self,
225         url: &UrlExtraData,
226         location: SourceLocation,
227         error: ContextualParseError,
228     );
229 }
230 
231 /// An error reporter that uses [the `log` crate](https://github.com/rust-lang-nursery/log)
232 /// at `info` level.
233 ///
234 /// This logging is silent by default, and can be enabled with a `RUST_LOG=style=info`
235 /// environment variable.
236 /// (See [`env_logger`](https://rust-lang-nursery.github.io/log/env_logger/).)
237 #[cfg(feature = "servo")]
238 pub struct RustLogReporter;
239 
240 #[cfg(feature = "servo")]
241 impl ParseErrorReporter for RustLogReporter {
report_error( &self, url: &UrlExtraData, location: SourceLocation, error: ContextualParseError, )242     fn report_error(
243         &self,
244         url: &UrlExtraData,
245         location: SourceLocation,
246         error: ContextualParseError,
247     ) {
248         if log_enabled!(log::Level::Info) {
249             info!(
250                 "Url:\t{}\n{}:{} {}",
251                 url.as_str(),
252                 location.line,
253                 location.column,
254                 error
255             )
256         }
257     }
258 }
259