Auto merge of #17739 - jdm:no-vendor-prefixed-errors, r=emilio

Suppress CSS parser errors for vendor-prefixed properties.

This matches the behaviour of Gecko's CSS parser.

- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #17736
- [X] There are tests for these changes

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17739)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-07-17 07:10:11 -07:00 committed by GitHub
commit 38f4ae80c4
9 changed files with 84 additions and 31 deletions

View file

@ -133,7 +133,6 @@ use style::stylist::{ExtraStyleData, Stylist};
use style::thread_state; use style::thread_state;
use style::timer::Timer; use style::timer::Timer;
use style::traversal::{DomTraversal, TraversalDriver, TraversalFlags}; use style::traversal::{DomTraversal, TraversalDriver, TraversalFlags};
use style::values::CompactCowStr;
/// Information needed by the layout thread. /// Information needed by the layout thread.
pub struct LayoutThread { pub struct LayoutThread {
@ -714,7 +713,7 @@ impl LayoutThread {
Msg::RegisterPaint(name, mut properties, painter) => { Msg::RegisterPaint(name, mut properties, painter) => {
debug!("Registering the painter"); debug!("Registering the painter");
let properties = properties.drain(..) let properties = properties.drain(..)
.filter_map(|name| PropertyId::parse(CompactCowStr::from(&*name)).ok().map(|id| (name.clone(), id))) .filter_map(|name| PropertyId::parse(&*name).ok().map(|id| (name.clone(), id)))
.filter(|&(_, ref id)| id.as_shorthand().is_err()) .filter(|&(_, ref id)| id.as_shorthand().is_err())
.collect(); .collect();
let registered_painter = RegisteredPainter { let registered_painter = RegisteredPainter {

View file

@ -296,7 +296,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
fn GetPropertyValue(&self, property: DOMString) -> DOMString { fn GetPropertyValue(&self, property: DOMString) -> DOMString {
let id = if let Ok(id) = PropertyId::parse(property.into()) { let id = if let Ok(id) = PropertyId::parse(&property) {
id id
} else { } else {
// Unkwown property // Unkwown property
@ -307,7 +307,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
fn GetPropertyPriority(&self, property: DOMString) -> DOMString { fn GetPropertyPriority(&self, property: DOMString) -> DOMString {
let id = if let Ok(id) = PropertyId::parse(property.into()) { let id = if let Ok(id) = PropertyId::parse(&property) {
id id
} else { } else {
// Unkwown property // Unkwown property
@ -331,7 +331,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
priority: DOMString) priority: DOMString)
-> ErrorResult { -> ErrorResult {
// Step 3 // Step 3
let id = if let Ok(id) = PropertyId::parse(property.into()) { let id = if let Ok(id) = PropertyId::parse(&property) {
id id
} else { } else {
// Unknown property // Unknown property
@ -348,7 +348,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
} }
// Step 2 & 3 // Step 2 & 3
let id = match PropertyId::parse(property.into()) { let id = match PropertyId::parse(&property) {
Ok(id) => id, Ok(id) => id,
Err(..) => return Ok(()), // Unkwown property Err(..) => return Ok(()), // Unkwown property
}; };
@ -380,7 +380,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
return Err(Error::NoModificationAllowed); return Err(Error::NoModificationAllowed);
} }
let id = if let Ok(id) = PropertyId::parse(property.into()) { let id = if let Ok(id) = PropertyId::parse(&property) {
id id
} else { } else {
// Unkwown property, cannot be there to remove. // Unkwown property, cannot be there to remove.

View file

@ -8,7 +8,7 @@
use context::QuirksMode; use context::QuirksMode;
use cssparser::{DeclarationListParser, parse_important, ParserInput, CompactCowStr}; use cssparser::{DeclarationListParser, parse_important, ParserInput, CompactCowStr};
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter}; use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseError as CssParseError};
use error_reporting::{ParseErrorReporter, ContextualParseError}; use error_reporting::{ParseErrorReporter, ContextualParseError};
use parser::{ParserContext, log_css_error}; use parser::{ParserContext, log_css_error};
use properties::animated_properties::AnimationValue; use properties::animated_properties::AnimationValue;
@ -935,13 +935,28 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b> {
type Error = SelectorParseError<'i, StyleParseError<'i>>; type Error = SelectorParseError<'i, StyleParseError<'i>>;
} }
/// Based on NonMozillaVendorIdentifier from Gecko's CSS parser.
fn is_non_mozilla_vendor_identifier(name: &str) -> bool {
(name.starts_with("-") && !name.starts_with("-moz-")) ||
name.starts_with("_")
}
impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> { impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
type Declaration = Importance; type Declaration = Importance;
type Error = SelectorParseError<'i, StyleParseError<'i>>; type Error = SelectorParseError<'i, StyleParseError<'i>>;
fn parse_value<'t>(&mut self, name: CompactCowStr<'i>, input: &mut Parser<'i, 't>) fn parse_value<'t>(&mut self, name: CompactCowStr<'i>, input: &mut Parser<'i, 't>)
-> Result<Importance, ParseError<'i>> { -> Result<Importance, ParseError<'i>> {
let id = PropertyId::parse(name)?; let id = match PropertyId::parse(&name) {
Ok(id) => id,
Err(()) => {
return Err(if is_non_mozilla_vendor_identifier(&name) {
PropertyDeclarationParseError::UnknownVendorProperty
} else {
PropertyDeclarationParseError::UnknownProperty(name)
}.into());
}
};
input.parse_until_before(Delimiter::Bang, |input| { input.parse_until_before(Delimiter::Bang, |input| {
PropertyDeclaration::parse_into(self.declarations, id, self.context, input) PropertyDeclaration::parse_into(self.declarations, id, self.context, input)
.map_err(|e| e.into()) .map_err(|e| e.into())
@ -976,6 +991,15 @@ pub fn parse_property_declaration_list(context: &ParserContext,
} }
Err(err) => { Err(err) => {
iter.parser.declarations.clear(); iter.parser.declarations.clear();
// If the unrecognized property looks like a vendor-specific property,
// silently ignore it instead of polluting the error output.
if let CssParseError::Custom(SelectorParseError::Custom(
StyleParseError::PropertyDeclaration(
PropertyDeclarationParseError::UnknownVendorProperty))) = err.error {
continue;
}
let pos = err.span.start; let pos = err.span.start;
let error = ContextualParseError::UnsupportedPropertyDeclaration( let error = ContextualParseError::UnsupportedPropertyDeclaration(
iter.input.slice(err.span), err.error); iter.input.slice(err.span), err.error);

View file

@ -2871,7 +2871,7 @@ fn static_assert() {
Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr()); Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr());
} }
if let Ok(prop_id) = PropertyId::parse(feature.0.to_string().into()) { if let Ok(prop_id) = PropertyId::parse(&feature.0.to_string()) {
match prop_id.as_shorthand() { match prop_id.as_shorthand() {
Ok(shorthand) => { Ok(shorthand) => {
for longhand in shorthand.longhands() { for longhand in shorthand.longhands() {

View file

@ -20,7 +20,7 @@ use stylearc::{Arc, UniqueArc};
use app_units::Au; use app_units::Au;
#[cfg(feature = "servo")] use cssparser::RGBA; #[cfg(feature = "servo")] use cssparser::RGBA;
use cssparser::{Parser, TokenSerializationType, serialize_identifier}; use cssparser::{Parser, TokenSerializationType, serialize_identifier};
use cssparser::{ParserInput, CompactCowStr}; use cssparser::ParserInput;
#[cfg(feature = "servo")] use euclid::SideOffsets2D; #[cfg(feature = "servo")] use euclid::SideOffsets2D;
use computed_values; use computed_values;
use context::QuirksMode; use context::QuirksMode;
@ -510,7 +510,7 @@ impl LonghandId {
% if not property.derived_from: % if not property.derived_from:
longhands::${property.ident}::parse_declared(context, input) longhands::${property.ident}::parse_declared(context, input)
% else: % else:
Err(PropertyDeclarationParseError::UnknownProperty.into()) Err(PropertyDeclarationParseError::UnknownProperty("${property.ident}".into()).into())
% endif % endif
} }
% endfor % endfor
@ -992,8 +992,8 @@ impl PropertyId {
/// Returns a given property from the string `s`. /// Returns a given property from the string `s`.
/// ///
/// Returns Err(()) for unknown non-custom properties /// Returns Err(()) for unknown non-custom properties
pub fn parse<'i>(property_name: CompactCowStr<'i>) -> Result<Self, ParseError<'i>> { pub fn parse(property_name: &str) -> Result<Self, ()> {
if let Ok(name) = ::custom_properties::parse_name(&property_name) { if let Ok(name) = ::custom_properties::parse_name(property_name) {
return Ok(PropertyId::Custom(::custom_properties::Name::from(name))) return Ok(PropertyId::Custom(::custom_properties::Name::from(name)))
} }
@ -1014,10 +1014,10 @@ impl PropertyId {
% endfor % endfor
} }
} }
match static_id(&property_name) { match static_id(property_name) {
Some(&StaticId::Longhand(id)) => Ok(PropertyId::Longhand(id)), Some(&StaticId::Longhand(id)) => Ok(PropertyId::Longhand(id)),
Some(&StaticId::Shorthand(id)) => Ok(PropertyId::Shorthand(id)), Some(&StaticId::Shorthand(id)) => Ok(PropertyId::Shorthand(id)),
None => Err(StyleParseError::UnknownProperty(property_name).into()), None => Err(()),
} }
} }
@ -1101,7 +1101,7 @@ impl PropertyId {
} }
fn check_allowed_in(&self, rule_type: CssRuleType, stylesheet_origin: Origin) fn check_allowed_in(&self, rule_type: CssRuleType, stylesheet_origin: Origin)
-> Result<(), PropertyDeclarationParseError> { -> Result<(), PropertyDeclarationParseError<'static>> {
let id: NonCustomPropertyId; let id: NonCustomPropertyId;
match *self { match *self {
// Custom properties are allowed everywhere // Custom properties are allowed everywhere
@ -1186,7 +1186,7 @@ impl PropertyId {
return Err(PropertyDeclarationParseError::ExperimentalProperty); return Err(PropertyDeclarationParseError::ExperimentalProperty);
} }
} else { } else {
return Err(PropertyDeclarationParseError::UnknownProperty); return Err(PropertyDeclarationParseError::UnknownProperty(self.name().into()));
} }
} }
} else { } else {
@ -1462,9 +1462,9 @@ impl PropertyDeclaration {
/// This will not actually parse Importance values, and will always set things /// This will not actually parse Importance values, and will always set things
/// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser, /// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
/// we only set them here so that we don't have to reallocate /// we only set them here so that we don't have to reallocate
pub fn parse_into(declarations: &mut SourcePropertyDeclaration, pub fn parse_into<'i, 't>(declarations: &mut SourcePropertyDeclaration,
id: PropertyId, context: &ParserContext, input: &mut Parser) id: PropertyId, context: &ParserContext, input: &mut Parser<'i, 't>)
-> Result<(), PropertyDeclarationParseError> { -> Result<(), PropertyDeclarationParseError<'i>> {
assert!(declarations.is_empty()); assert!(declarations.is_empty());
let rule_type = context.rule_type(); let rule_type = context.rule_type();
debug_assert!(rule_type == CssRuleType::Keyframe || debug_assert!(rule_type == CssRuleType::Keyframe ||

View file

@ -17,6 +17,7 @@ use selectors::parser::SelectorParseError;
use shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard}; use shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
use std::fmt; use std::fmt;
use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, StyleParseError}; use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, StyleParseError};
use style_traits::PropertyDeclarationParseError;
use stylearc::Arc; use stylearc::Arc;
use stylesheets::{CssRuleType, StylesheetContents}; use stylesheets::{CssRuleType, StylesheetContents};
use stylesheets::rule_parser::VendorPrefix; use stylesheets::rule_parser::VendorPrefix;
@ -532,7 +533,8 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeDeclarationParser<'a, 'b> {
fn parse_value<'t>(&mut self, name: CompactCowStr<'i>, input: &mut Parser<'i, 't>) fn parse_value<'t>(&mut self, name: CompactCowStr<'i>, input: &mut Parser<'i, 't>)
-> Result<(), ParseError<'i>> { -> Result<(), ParseError<'i>> {
let id = PropertyId::parse(name.into())?; let id = PropertyId::parse(&name)
.map_err(|()| PropertyDeclarationParseError::UnknownProperty(name))?;
match PropertyDeclaration::parse_into(self.declarations, id, self.context, input) { match PropertyDeclaration::parse_into(self.declarations, id, self.context, input) {
Ok(()) => { Ok(()) => {
// In case there is still unparsed text in the declaration, we should roll back. // In case there is still unparsed text in the declaration, we should roll back.

View file

@ -91,7 +91,7 @@ pub enum StyleParseError<'i> {
/// Unexpected closing curly bracket in a DVB. /// Unexpected closing curly bracket in a DVB.
UnbalancedCloseCurlyBracketInDeclarationValueBlock, UnbalancedCloseCurlyBracketInDeclarationValueBlock,
/// A property declaration parsing error. /// A property declaration parsing error.
PropertyDeclaration(PropertyDeclarationParseError), PropertyDeclaration(PropertyDeclarationParseError<'i>),
/// A property declaration value had input remaining after successfully parsing. /// A property declaration value had input remaining after successfully parsing.
PropertyDeclarationValueNotExhausted, PropertyDeclarationValueNotExhausted,
/// An unexpected dimension token was encountered. /// An unexpected dimension token was encountered.
@ -112,15 +112,15 @@ pub enum StyleParseError<'i> {
UnspecifiedError, UnspecifiedError,
/// An unexpected token was found within a namespace rule. /// An unexpected token was found within a namespace rule.
UnexpectedTokenWithinNamespace(Token<'i>), UnexpectedTokenWithinNamespace(Token<'i>),
/// An unknown CSS property was encountered.
UnknownProperty(CompactCowStr<'i>),
} }
/// The result of parsing a property declaration. /// The result of parsing a property declaration.
#[derive(Eq, PartialEq, Clone, Debug)] #[derive(Eq, PartialEq, Clone, Debug)]
pub enum PropertyDeclarationParseError { pub enum PropertyDeclarationParseError<'i> {
/// The property declaration was for an unknown property. /// The property declaration was for an unknown property.
UnknownProperty, UnknownProperty(CompactCowStr<'i>),
/// An unknown vendor-specific identifier was encountered.
UnknownVendorProperty,
/// The property declaration was for a disabled experimental property. /// The property declaration was for a disabled experimental property.
ExperimentalProperty, ExperimentalProperty,
/// The property declaration contained an invalid value. /// The property declaration contained an invalid value.
@ -140,8 +140,8 @@ impl<'a> From<StyleParseError<'a>> for ParseError<'a> {
} }
} }
impl<'a> From<PropertyDeclarationParseError> for ParseError<'a> { impl<'a> From<PropertyDeclarationParseError<'a>> for ParseError<'a> {
fn from(this: PropertyDeclarationParseError) -> Self { fn from(this: PropertyDeclarationParseError<'a>) -> Self {
cssparser::ParseError::Custom(SelectorParseError::Custom(StyleParseError::PropertyDeclaration(this))) cssparser::ParseError::Custom(SelectorParseError::Custom(StyleParseError::PropertyDeclaration(this)))
} }
} }

View file

@ -233,7 +233,8 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
ErrorString::Ident(namespace), ErrorString::Ident(namespace),
(_, CssParseError::Custom(SelectorParseError::Custom( (_, CssParseError::Custom(SelectorParseError::Custom(
StyleParseError::UnknownProperty(property)))) => StyleParseError::PropertyDeclaration(
PropertyDeclarationParseError::UnknownProperty(property))))) =>
ErrorString::Ident(property), ErrorString::Ident(property),
(_, CssParseError::Custom(SelectorParseError::Custom( (_, CssParseError::Custom(SelectorParseError::Custom(

View file

@ -316,7 +316,7 @@ fn test_report_error_stylesheet() {
let error = errors.pop().unwrap(); let error = errors.pop().unwrap();
assert_eq!("Unsupported property declaration: 'invalid: true;', \ assert_eq!("Unsupported property declaration: 'invalid: true;', \
Custom(UnknownProperty(\"invalid\"))", error.message); Custom(PropertyDeclaration(UnknownProperty(\"invalid\")))", error.message);
assert_eq!(9, error.line); assert_eq!(9, error.line);
assert_eq!(8, error.column); assert_eq!(8, error.column);
@ -329,3 +329,30 @@ fn test_report_error_stylesheet() {
// testing for the url // testing for the url
assert_eq!(url, error.url); assert_eq!(url, error.url);
} }
#[test]
fn test_no_report_unrecognized_vendor_properties() {
let css = r"
div {
-o-background-color: red;
_background-color: red;
-moz-background-color: red;
}
";
let url = ServoUrl::parse("about::test").unwrap();
let error_reporter = CSSInvalidErrorReporterTest::new();
let errors = error_reporter.errors.clone();
let lock = SharedRwLock::new();
let media = Arc::new(lock.wrap(MediaList::empty()));
Stylesheet::from_str(css, url, Origin::UserAgent, media, lock,
None, &error_reporter, QuirksMode::NoQuirks, 0u64);
let mut errors = errors.lock().unwrap();
let error = errors.pop().unwrap();
assert_eq!("Unsupported property declaration: '-moz-background-color: red;', \
Custom(PropertyDeclaration(UnknownProperty(\"-moz-background-color\")))",
error.message);
assert!(errors.is_empty());
}