error_reporter.rs (23567B)
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 //! Wrapper around Gecko's CSS error reporting mechanism. 6 7 #![allow(unsafe_code)] 8 9 use cssparser::{serialize_identifier, CowRcStr, ToCss}; 10 use cssparser::{BasicParseErrorKind, ParseError, ParseErrorKind, SourceLocation, Token}; 11 use selectors::parser::SelectorParseErrorKind; 12 use selectors::SelectorList; 13 use std::ffi::CStr; 14 use std::ptr; 15 use style::error_reporting::{ContextualParseError, ParseErrorReporter}; 16 use style::gecko_bindings::bindings; 17 use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData; 18 use style::gecko_bindings::structs::{nsIURI, Loader, StyleSheet as DomStyleSheet}; 19 use style::selector_parser::SelectorImpl; 20 use style::stylesheets::UrlExtraData; 21 use style_traits::{PropertySyntaxParseError, StyleParseErrorKind, ValueParseErrorKind}; 22 23 pub type ErrorKind<'i> = ParseErrorKind<'i, StyleParseErrorKind<'i>>; 24 25 /// An error reporter with all the data we need to report errors. 26 pub struct ErrorReporter { 27 window_id: u64, 28 uri: *mut nsIURI, 29 } 30 31 impl ErrorReporter { 32 /// Create a new instance of the Gecko error reporter, if error reporting is 33 /// enabled. 34 pub fn new( 35 sheet: *mut DomStyleSheet, 36 loader: *mut Loader, 37 extra_data: *mut RawUrlExtraData, 38 ) -> Option<Self> { 39 let mut window_id = 0; 40 41 let enabled = 42 unsafe { bindings::Gecko_ErrorReportingEnabled(sheet, loader, &mut window_id) }; 43 44 if !enabled { 45 return None; 46 } 47 48 let uri = unsafe { 49 extra_data 50 .as_ref() 51 .map(|d| d.mBaseURI.raw()) 52 .unwrap_or(ptr::null_mut()) 53 }; 54 55 Some(ErrorReporter { window_id, uri }) 56 } 57 } 58 59 enum ErrorString<'a> { 60 Snippet(CowRcStr<'a>), 61 Ident(CowRcStr<'a>), 62 UnexpectedToken(Token<'a>), 63 } 64 65 impl<'a> ErrorString<'a> { 66 fn into_str(self) -> CowRcStr<'a> { 67 match self { 68 ErrorString::Snippet(s) => s, 69 ErrorString::UnexpectedToken(t) => t.to_css_string().into(), 70 ErrorString::Ident(i) => { 71 let mut s = String::new(); 72 serialize_identifier(&i, &mut s).unwrap(); 73 s.into() 74 }, 75 } 76 } 77 } 78 79 #[derive(Debug)] 80 enum Action { 81 Nothing, 82 Skip, 83 Drop, 84 } 85 86 trait ErrorHelpers<'a> { 87 fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>); 88 fn error_params(self) -> ErrorParams<'a>; 89 fn selectors(&self) -> &'a [SelectorList<SelectorImpl>]; 90 fn to_gecko_message(&self) -> (Option<&'static CStr>, &'static CStr, Action); 91 } 92 93 fn extract_error_param<'a>(err: ErrorKind<'a>) -> Option<ErrorString<'a>> { 94 Some(match err { 95 ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => { 96 ErrorString::UnexpectedToken(t) 97 }, 98 99 ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(i)) => { 100 let mut s = String::from("@"); 101 serialize_identifier(&i, &mut s).unwrap(); 102 ErrorString::Snippet(s.into()) 103 }, 104 105 ParseErrorKind::Custom(StyleParseErrorKind::OtherInvalidValue(property)) => { 106 ErrorString::Snippet(property) 107 }, 108 109 ParseErrorKind::Custom(StyleParseErrorKind::SelectorError( 110 SelectorParseErrorKind::UnexpectedIdent(ident), 111 )) => ErrorString::Ident(ident), 112 113 ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty(property)) => { 114 ErrorString::Ident(property) 115 }, 116 117 ParseErrorKind::Custom(StyleParseErrorKind::UnexpectedTokenWithinNamespace(token)) => { 118 ErrorString::UnexpectedToken(token) 119 }, 120 121 _ => return None, 122 }) 123 } 124 125 #[derive(Default)] 126 struct ErrorParams<'a> { 127 prefix_param: Option<ErrorString<'a>>, 128 main_param: Option<ErrorString<'a>>, 129 } 130 131 /// If an error parameter is present in the given error, return it. Additionally return 132 /// a second parameter if it exists, for use in the prefix for the eventual error message. 133 fn extract_error_params<'a>(err: ErrorKind<'a>) -> Option<ErrorParams<'a>> { 134 let (main, prefix) = match err { 135 ParseErrorKind::Custom(StyleParseErrorKind::InvalidColor(property, token)) 136 | ParseErrorKind::Custom(StyleParseErrorKind::InvalidFilter(property, token)) => ( 137 Some(ErrorString::Snippet(property.into())), 138 Some(ErrorString::UnexpectedToken(token)), 139 ), 140 141 ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName(ident)) => { 142 (Some(ErrorString::Ident(ident)), None) 143 }, 144 145 ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(token)) 146 | ParseErrorKind::Custom(StyleParseErrorKind::ValueError( 147 ValueParseErrorKind::InvalidColor(token), 148 )) => (Some(ErrorString::UnexpectedToken(token)), None), 149 150 ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(err)) => match err { 151 SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t) 152 | SelectorParseErrorKind::BadValueInAttr(t) 153 | SelectorParseErrorKind::ExpectedBarInAttr(t) 154 | SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t) 155 | SelectorParseErrorKind::InvalidQualNameInAttr(t) 156 | SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t) 157 | SelectorParseErrorKind::PseudoElementExpectedIdent(t) 158 | SelectorParseErrorKind::NoIdentForPseudo(t) 159 | SelectorParseErrorKind::ClassNeedsIdent(t) 160 | SelectorParseErrorKind::PseudoElementExpectedColon(t) => { 161 (None, Some(ErrorString::UnexpectedToken(t))) 162 }, 163 SelectorParseErrorKind::ExpectedNamespace(namespace) => { 164 (None, Some(ErrorString::Ident(namespace))) 165 }, 166 SelectorParseErrorKind::UnsupportedPseudoClassOrElement(p) => { 167 (None, Some(ErrorString::Ident(p))) 168 }, 169 SelectorParseErrorKind::EmptySelector | SelectorParseErrorKind::DanglingCombinator => { 170 (None, None) 171 }, 172 err => ( 173 Some(extract_error_param(ParseErrorKind::Custom( 174 StyleParseErrorKind::SelectorError(err), 175 ))?), 176 None, 177 ), 178 }, 179 err => (Some(extract_error_param(err)?), None), 180 }; 181 Some(ErrorParams { 182 main_param: main, 183 prefix_param: prefix, 184 }) 185 } 186 187 impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> { 188 fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>) { 189 match self { 190 ContextualParseError::UnsupportedPropertyDeclaration(s, err, _) 191 | ContextualParseError::UnsupportedPropertyDescriptor(s, err) 192 | ContextualParseError::UnsupportedFontFaceDescriptor(s, err) 193 | ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) 194 | ContextualParseError::UnsupportedFontPaletteValuesDescriptor(s, err) 195 | ContextualParseError::InvalidKeyframeRule(s, err) 196 | ContextualParseError::InvalidFontFeatureValuesRule(s, err) 197 | ContextualParseError::InvalidRule(s, err) 198 | ContextualParseError::UnsupportedRule(s, err) 199 | ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) 200 | ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) 201 | ContextualParseError::InvalidMediaRule(s, err) 202 | ContextualParseError::UnsupportedValue(s, err) => (s.into(), err.kind), 203 ContextualParseError::NeverMatchingHostSelector(s) 204 | ContextualParseError::InvalidCounterStyleWithoutSymbols(s) 205 | ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) => ( 206 s.into(), 207 ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()), 208 ), 209 ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols 210 | ContextualParseError::InvalidCounterStyleExtendsWithSymbols 211 | ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => ( 212 "".into(), 213 ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()), 214 ), 215 } 216 } 217 218 fn error_params(self) -> ErrorParams<'a> { 219 let (s, error) = self.error_data(); 220 let mut params = extract_error_params(error).unwrap_or_default(); 221 params 222 .main_param 223 .get_or_insert_with(|| ErrorString::Snippet(s)); 224 params 225 } 226 227 fn selectors(&self) -> &'a [SelectorList<SelectorImpl>] { 228 match *self { 229 ContextualParseError::UnsupportedPropertyDeclaration(_, _, selectors) => selectors, 230 _ => &[], 231 } 232 } 233 234 fn to_gecko_message(&self) -> (Option<&'static CStr>, &'static CStr, Action) { 235 let (msg, action): (&CStr, Action) = match *self { 236 ContextualParseError::UnsupportedPropertyDeclaration( 237 _, 238 ParseError { 239 kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(_)), 240 .. 241 }, 242 _, 243 ) 244 | ContextualParseError::UnsupportedPropertyDeclaration( 245 _, 246 ParseError { 247 kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)), 248 .. 249 }, 250 _, 251 ) => (cstr!("PEParseDeclarationDeclExpected"), Action::Skip), 252 ContextualParseError::UnsupportedPropertyDeclaration( 253 _, 254 ParseError { 255 kind: ParseErrorKind::Custom(ref err), 256 .. 257 }, 258 _, 259 ) => match *err { 260 StyleParseErrorKind::InvalidColor(_, _) => { 261 return ( 262 Some(cstr!("PEColorNotColor")), 263 cstr!("PEValueParsingError"), 264 Action::Drop, 265 ); 266 }, 267 StyleParseErrorKind::InvalidFilter(_, _) => { 268 return ( 269 Some(cstr!("PEExpectedNoneOrURLOrFilterFunction")), 270 cstr!("PEValueParsingError"), 271 Action::Drop, 272 ); 273 }, 274 StyleParseErrorKind::OtherInvalidValue(_) => { 275 (cstr!("PEValueParsingError"), Action::Drop) 276 }, 277 StyleParseErrorKind::UnexpectedImportantDeclaration => { 278 (cstr!("PEImportantDeclError"), Action::Drop) 279 }, 280 _ => (cstr!("PEUnknownProperty"), Action::Drop), 281 }, 282 ContextualParseError::UnsupportedPropertyDeclaration(..) => { 283 (cstr!("PEUnknownProperty"), Action::Drop) 284 }, 285 ContextualParseError::UnsupportedFontFaceDescriptor(..) => { 286 (cstr!("PEUnknownFontDesc"), Action::Skip) 287 }, 288 ContextualParseError::InvalidKeyframeRule(..) => { 289 (cstr!("PEKeyframeBadName"), Action::Nothing) 290 }, 291 ContextualParseError::InvalidRule( 292 _, 293 ParseError { 294 kind: 295 ParseErrorKind::Custom(StyleParseErrorKind::UnexpectedTokenWithinNamespace(_)), 296 .. 297 }, 298 ) => (cstr!("PEAtNSUnexpected"), Action::Nothing), 299 ContextualParseError::InvalidRule( 300 _, 301 ParseError { 302 kind: ParseErrorKind::Custom(StyleParseErrorKind::DisallowedImportRule), 303 .. 304 }, 305 ) => (cstr!("PEDisallowedImportRule"), Action::Nothing), 306 ContextualParseError::InvalidRule( 307 _, 308 ParseError { 309 kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)), 310 .. 311 }, 312 ) => (cstr!("PEUnknownAtRule"), Action::Nothing), 313 ContextualParseError::InvalidRule(_, ref err) => { 314 let prefix = match err.kind { 315 ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(ref err)) => { 316 match *err { 317 SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(_) => { 318 Some(cstr!("PEAttSelUnexpected")) 319 }, 320 SelectorParseErrorKind::ExpectedBarInAttr(_) => { 321 Some(cstr!("PEAttSelNoBar")) 322 }, 323 SelectorParseErrorKind::BadValueInAttr(_) => { 324 Some(cstr!("PEAttSelBadValue")) 325 }, 326 SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(_) => { 327 Some(cstr!("PEAttributeNameOrNamespaceExpected")) 328 }, 329 SelectorParseErrorKind::InvalidQualNameInAttr(_) => { 330 Some(cstr!("PEAttributeNameExpected")) 331 }, 332 SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(_) => { 333 Some(cstr!("PETypeSelNotType")) 334 }, 335 SelectorParseErrorKind::ExpectedNamespace(_) => { 336 Some(cstr!("PEUnknownNamespacePrefix")) 337 }, 338 SelectorParseErrorKind::EmptySelector => { 339 Some(cstr!("PESelectorGroupNoSelector")) 340 }, 341 SelectorParseErrorKind::DanglingCombinator => { 342 Some(cstr!("PESelectorGroupExtraCombinator")) 343 }, 344 SelectorParseErrorKind::UnsupportedPseudoClassOrElement(_) => { 345 Some(cstr!("PEPseudoSelUnknown")) 346 }, 347 SelectorParseErrorKind::PseudoElementExpectedColon(_) => { 348 Some(cstr!("PEPseudoSelEndOrUserActionPC")) 349 }, 350 SelectorParseErrorKind::NoIdentForPseudo(_) => { 351 Some(cstr!("PEPseudoClassArgNotIdent")) 352 }, 353 SelectorParseErrorKind::PseudoElementExpectedIdent(_) => { 354 Some(cstr!("PEPseudoSelBadName")) 355 }, 356 SelectorParseErrorKind::ClassNeedsIdent(_) => { 357 Some(cstr!("PEClassSelNotIdent")) 358 }, 359 _ => None, 360 } 361 }, 362 ParseErrorKind::Custom( 363 StyleParseErrorKind::PropertySyntaxField(_) 364 | StyleParseErrorKind::PropertyInheritsField(_), 365 ) => { 366 // Keeps PEBadSelectorRSIgnored from being reported when a syntax descriptor 367 // error or inherits descriptor error was already reported. 368 return (None, cstr!(""), Action::Nothing); 369 }, 370 _ => None, 371 }; 372 return (prefix, cstr!("PEBadSelectorRSIgnored"), Action::Nothing); 373 }, 374 ContextualParseError::InvalidMediaRule(_, ref err) => { 375 let err: &CStr = match err.kind { 376 ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName( 377 .., 378 )) => cstr!("PEMQExpectedFeatureName"), 379 ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureValue) => { 380 cstr!("PEMQExpectedFeatureValue") 381 }, 382 ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryUnexpectedOperator) => { 383 cstr!("PEMQUnexpectedOperator") 384 }, 385 ParseErrorKind::Custom(StyleParseErrorKind::RangedExpressionWithNoValue) => { 386 cstr!("PEMQNoMinMaxWithoutValue") 387 }, 388 _ => cstr!("PEMQUnexpectedToken"), 389 }; 390 (err, Action::Nothing) 391 }, 392 ContextualParseError::UnsupportedRule(..) => (cstr!("PEDeclDropped"), Action::Nothing), 393 ContextualParseError::NeverMatchingHostSelector(..) => { 394 (cstr!("PENeverMatchingHostSelector"), Action::Nothing) 395 }, 396 ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) 397 | ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) 398 | ContextualParseError::InvalidCounterStyleWithoutSymbols(..) 399 | ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) 400 | ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols 401 | ContextualParseError::InvalidCounterStyleExtendsWithSymbols 402 | ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols 403 | ContextualParseError::UnsupportedPropertyDescriptor(..) 404 | ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) 405 | ContextualParseError::UnsupportedFontPaletteValuesDescriptor(..) 406 | ContextualParseError::InvalidFontFeatureValuesRule(..) => { 407 (cstr!("PEUnknownAtRule"), Action::Skip) 408 }, 409 ContextualParseError::UnsupportedValue(_, ParseError { ref kind, .. }) => { 410 match *kind { 411 ParseErrorKind::Custom(StyleParseErrorKind::ValueError( 412 ValueParseErrorKind::InvalidColor(..), 413 )) => (cstr!("PEColorNotColor"), Action::Nothing), 414 ParseErrorKind::Custom(StyleParseErrorKind::PropertySyntaxField(ref kind)) => { 415 let name = match kind { 416 PropertySyntaxParseError::NoSyntax => { 417 cstr!("PEPRSyntaxFieldMissing") 418 }, 419 PropertySyntaxParseError::EmptyInput => { 420 cstr!("PEPRSyntaxFieldEmptyInput") 421 }, 422 PropertySyntaxParseError::ExpectedPipeBetweenComponents => { 423 cstr!("PEPRSyntaxFieldExpectedPipe") 424 }, 425 PropertySyntaxParseError::InvalidNameStart => { 426 cstr!("PEPRSyntaxFieldInvalidNameStart") 427 }, 428 PropertySyntaxParseError::InvalidName => { 429 cstr!("PEPRSyntaxFieldInvalidName") 430 }, 431 PropertySyntaxParseError::UnclosedDataTypeName => { 432 cstr!("PEPRSyntaxFieldUnclosedDataTypeName") 433 }, 434 PropertySyntaxParseError::UnexpectedEOF => { 435 cstr!("PEPRSyntaxFieldUnexpectedEOF") 436 }, 437 PropertySyntaxParseError::UnknownDataTypeName => { 438 cstr!("PEPRSyntaxFieldUnknownDataTypeName") 439 }, 440 }; 441 (name, Action::Nothing) 442 }, 443 ParseErrorKind::Custom(StyleParseErrorKind::PropertyInheritsField( 444 ref kind, 445 )) => { 446 let name = match kind { 447 style_traits::PropertyInheritsParseError::NoInherits => { 448 cstr!("PEPRInheritsFieldMissing") 449 }, 450 style_traits::PropertyInheritsParseError::InvalidInherits => { 451 cstr!("PEPRInheritsFieldInvalid") 452 }, 453 }; 454 (name, Action::Nothing) 455 }, 456 _ => { 457 // Not the best error message, since we weren't parsing 458 // a declaration, just a value. But we don't produce 459 // UnsupportedValue errors other than InvalidColors 460 // currently. 461 debug_assert!(false, "should use a more specific error message"); 462 (cstr!("PEDeclDropped"), Action::Nothing) 463 }, 464 } 465 }, 466 }; 467 (None, msg, action) 468 } 469 } 470 471 impl ErrorReporter { 472 pub fn report(&self, location: SourceLocation, error: ContextualParseError) { 473 let (pre, name, action) = error.to_gecko_message(); 474 let suffix = match action { 475 Action::Nothing => ptr::null(), 476 Action::Skip => cstr!("PEDeclSkipped").as_ptr(), 477 Action::Drop => cstr!("PEDeclDropped").as_ptr(), 478 }; 479 let selectors = error.selectors(); 480 let desugared_selector_list = match selectors.len() { 481 0 => None, 482 1 => Some(selectors[0].to_css_string()), 483 _ => { 484 let mut desugared = selectors.last().unwrap().clone(); 485 for parent in selectors.iter().rev().skip(1) { 486 desugared = desugared.replace_parent_selector(&parent); 487 } 488 Some(desugared.to_css_string()) 489 }, 490 }; 491 let selector_list_ptr = desugared_selector_list 492 .as_ref() 493 .map_or(ptr::null(), |s| s.as_ptr()) as *const _; 494 let params = error.error_params(); 495 let param = params.main_param; 496 let pre_param = params.prefix_param; 497 let param = param.map(|p| p.into_str()); 498 let pre_param = pre_param.map(|p| p.into_str()); 499 let param_ptr = param.as_ref().map_or(ptr::null(), |p| p.as_ptr()); 500 let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr()); 501 unsafe { 502 bindings::Gecko_ReportUnexpectedCSSError( 503 self.window_id, 504 self.uri, 505 name.as_ptr() as *const _, 506 param_ptr as *const _, 507 param.as_ref().map_or(0, |p| p.len()) as u32, 508 pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _, 509 pre_param_ptr as *const _, 510 pre_param.as_ref().map_or(0, |p| p.len()) as u32, 511 suffix as *const _, 512 selector_list_ptr, 513 desugared_selector_list 514 .as_ref() 515 .map_or(0, |string| string.len()) as u32, 516 location.line, 517 location.column, 518 ); 519 } 520 } 521 } 522 523 impl ParseErrorReporter for ErrorReporter { 524 fn report_error( 525 &self, 526 _url: &UrlExtraData, 527 location: SourceLocation, 528 error: ContextualParseError, 529 ) { 530 self.report(location, error) 531 } 532 }