Bug 1469957: Move the error reporter into ParserContext. r=xidorn
authorEmilio Cobos Álvarez <emilio@crisal.io>
Wed, 20 Jun 2018 21:07:45 +0200
changeset 477480 4072740124c8aa15d5439983862e61281d437364
parent 477447 a79434a2511b80f92554c15317e2d6271849ace9
child 477481 82874b00bab05e115287d1fbe8549c11c523d5de
push id9385
push userdluca@mozilla.com
push dateFri, 22 Jun 2018 15:47:18 +0000
treeherdermozilla-beta@82a9a1027e2b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersxidorn
bugs1469957, 1452143
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1469957: Move the error reporter into ParserContext. r=xidorn Summary: This should make it easier to report errors, and also reduce codesize. The reason this was so generic is that error reporting was unconditionally enabled and was super-hot, but now that's no longer the case after bug 1452143, so we can afford the virtual call in the "error reporting enabled" case. This opens the possibility of simplifying a lot the error setup as well, though this patch doesn't do it. Test Plan: No behavior change, so no new tests. Reviewers: xidorn Bug #: 1469957 Differential Revision: https://phabricator.services.mozilla.com/D1734 MozReview-Commit-ID: F3wTdhX9MB5
servo/components/style/counter_style/mod.rs
servo/components/style/error_reporting.rs
servo/components/style/font_face.rs
servo/components/style/media_queries/media_list.rs
servo/components/style/parser.rs
servo/components/style/properties/declaration_block.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/stylesheets/font_feature_values_rule.rs
servo/components/style/stylesheets/keyframes_rule.rs
servo/components/style/stylesheets/mod.rs
servo/components/style/stylesheets/rule_parser.rs
servo/components/style/stylesheets/stylesheet.rs
servo/components/style/stylesheets/viewport_rule.rs
servo/ports/geckolib/error_reporter.rs
servo/ports/geckolib/glue.rs
servo/ports/geckolib/stylesheet_loader.rs
--- a/servo/components/style/counter_style/mod.rs
+++ b/servo/components/style/counter_style/mod.rs
@@ -4,18 +4,18 @@
 
 //! The [`@counter-style`][counter-style] at-rule.
 //!
 //! [counter-style]: https://drafts.csswg.org/css-counter-styles/
 
 use Atom;
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser};
 use cssparser::{CowRcStr, Parser, SourceLocation, Token};
-use error_reporting::{ContextualParseError, ParseErrorReporter};
-use parser::{Parse, ParserContext, ParserErrorContext};
+use error_reporting::ContextualParseError;
+use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use std::mem;
 use std::num::Wrapping;
 use std::ops::Range;
 use str::CssStringWriter;
 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
@@ -68,42 +68,38 @@ pub fn parse_counter_style_name_definiti
             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         } else {
             Ok(ident)
         }
     })
 }
 
 /// Parse the body (inside `{}`) of an @counter-style rule
-pub fn parse_counter_style_body<'i, 't, R>(
+pub fn parse_counter_style_body<'i, 't>(
     name: CustomIdent,
     context: &ParserContext,
-    error_context: &ParserErrorContext<R>,
     input: &mut Parser<'i, 't>,
     location: SourceLocation,
-) -> Result<CounterStyleRuleData, ParseError<'i>>
-where
-    R: ParseErrorReporter,
-{
+) -> Result<CounterStyleRuleData, ParseError<'i>> {
     let start = input.current_source_location();
     let mut rule = CounterStyleRuleData::empty(name, location);
     {
         let parser = CounterStyleRuleParser {
             context: context,
             rule: &mut rule,
         };
         let mut iter = DeclarationListParser::new(input, parser);
         while let Some(declaration) = iter.next() {
             if let Err((error, slice)) = declaration {
                 let location = error.location;
                 let error = ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(
                     slice,
                     error,
                 );
-                context.log_css_error(error_context, location, error)
+                context.log_css_error(location, error)
             }
         }
     }
     let error = match *rule.resolved_system() {
         ref system @ System::Cyclic |
         ref system @ System::Fixed { .. } |
         ref system @ System::Symbolic |
         ref system @ System::Alphabetic |
@@ -129,17 +125,17 @@ where
             Some(ContextualParseError::InvalidCounterStyleExtendsWithSymbols)
         },
         System::Extends(_) if rule.additive_symbols.is_some() => {
             Some(ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols)
         },
         _ => None,
     };
     if let Some(error) = error {
-        context.log_css_error(error_context, start, error);
+        context.log_css_error(start, error);
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     } else {
         Ok(rule)
     }
 }
 
 struct CounterStyleRuleParser<'a, 'b: 'a> {
     context: &'a ParserContext<'b>,
--- a/servo/components/style/error_reporting.rs
+++ b/servo/components/style/error_reporting.rs
@@ -244,22 +244,8 @@ impl ParseErrorReporter for RustLogRepor
                 url.as_str(),
                 location.line,
                 location.column,
                 error
             )
         }
     }
 }
-
-/// Error reporter which silently forgets errors
-pub struct NullReporter;
-
-impl ParseErrorReporter for NullReporter {
-    fn report_error(
-        &self,
-        _url: &UrlExtraData,
-        _location: SourceLocation,
-        _error: ContextualParseError,
-    ) {
-        // do nothing
-    }
-}
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -5,18 +5,18 @@
 //! The [`@font-face`][ff] at-rule.
 //!
 //! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule
 
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{CowRcStr, SourceLocation};
 #[cfg(feature = "gecko")]
 use cssparser::UnicodeRange;
-use error_reporting::{ContextualParseError, ParseErrorReporter};
-use parser::{Parse, ParserContext, ParserErrorContext};
+use error_reporting::ContextualParseError;
+use parser::{Parse, ParserContext};
 #[cfg(feature = "gecko")]
 use properties::longhands::font_language_override;
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use str::CssStringWriter;
 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
 use style_traits::{StyleParseErrorKind, ToCss};
@@ -181,37 +181,33 @@ impl ToCss for FontStyle {
             }
         }
     }
 }
 
 /// Parse the block inside a `@font-face` rule.
 ///
 /// Note that the prelude parsing code lives in the `stylesheets` module.
-pub fn parse_font_face_block<R>(
+pub fn parse_font_face_block(
     context: &ParserContext,
-    error_context: &ParserErrorContext<R>,
     input: &mut Parser,
     location: SourceLocation,
-) -> FontFaceRuleData
-where
-    R: ParseErrorReporter,
-{
+) -> FontFaceRuleData {
     let mut rule = FontFaceRuleData::empty(location);
     {
         let parser = FontFaceRuleParser {
             context: context,
             rule: &mut rule,
         };
         let mut iter = DeclarationListParser::new(input, parser);
         while let Some(declaration) = iter.next() {
             if let Err((error, slice)) = declaration {
                 let location = error.location;
                 let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
-                context.log_css_error(error_context, location, error)
+                context.log_css_error(location, error)
             }
         }
     }
     rule
 }
 
 /// A @font-face rule that is known to have font-family and src declarations.
 #[cfg(feature = "servo")]
--- a/servo/components/style/media_queries/media_list.rs
+++ b/servo/components/style/media_queries/media_list.rs
@@ -4,18 +4,18 @@
 
 //! A media query list:
 //!
 //! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list
 
 use context::QuirksMode;
 use cssparser::{Delimiter, Parser};
 use cssparser::{ParserInput, Token};
-use error_reporting::{ContextualParseError, ParseErrorReporter};
-use parser::{ParserContext, ParserErrorContext};
+use error_reporting::ContextualParseError;
+use parser::ParserContext;
 use super::{Device, MediaQuery, Qualifier};
 
 /// A type that encapsulates a media query list.
 #[css(comma)]
 #[derive(Clone, Debug, MallocSizeOf, ToCss)]
 pub struct MediaList {
     /// The list of media queries.
     #[css(iterable)]
@@ -25,42 +25,37 @@ pub struct MediaList {
 impl MediaList {
     /// Parse a media query list from CSS.
     ///
     /// Always returns a media query list. If any invalid media query is
     /// found, the media query list is only filled with the equivalent of
     /// "not all", see:
     ///
     /// <https://drafts.csswg.org/mediaqueries/#error-handling>
-    pub fn parse<R>(
+    pub fn parse(
         context: &ParserContext,
         input: &mut Parser,
-        error_reporter: &R,
-    ) -> MediaList
-    where
-        R: ParseErrorReporter,
-    {
+    ) -> Self {
         if input.is_exhausted() {
             return Self::empty();
         }
 
         let mut media_queries = vec![];
         loop {
             let start_position = input.position();
             match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) {
                 Ok(mq) => {
                     media_queries.push(mq);
                 },
                 Err(err) => {
                     media_queries.push(MediaQuery::never_matching());
                     let location = err.location;
                     let error =
                         ContextualParseError::InvalidMediaRule(input.slice_from(start_position), err);
-                    let error_context = ParserErrorContext { error_reporter };
-                    context.log_css_error(&error_context, location, error);
+                    context.log_css_error(location, error);
                 },
             }
 
             match input.next() {
                 Ok(&Token::Comma) => {},
                 Ok(_) => unreachable!(),
                 Err(_) => break,
             }
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -31,73 +31,73 @@ pub fn assert_parsing_mode_match() {
 
     check_parsing_modes! {
         ParsingMode_Default => ParsingMode::DEFAULT,
         ParsingMode_AllowUnitlessLength => ParsingMode::ALLOW_UNITLESS_LENGTH,
         ParsingMode_AllowAllNumericValues => ParsingMode::ALLOW_ALL_NUMERIC_VALUES,
     }
 }
 
-/// The context required to report a parse error.
-pub struct ParserErrorContext<'a, R: 'a> {
-    /// An error reporter to report syntax errors.
-    pub error_reporter: &'a R,
-}
-
 /// The data that the parser needs from outside in order to parse a stylesheet.
 pub struct ParserContext<'a> {
     /// The `Origin` of the stylesheet, whether it's a user, author or
     /// user-agent stylesheet.
     pub stylesheet_origin: Origin,
     /// The extra data we need for resolving url values.
     pub url_data: &'a UrlExtraData,
     /// The current rule type, if any.
     pub rule_type: Option<CssRuleType>,
     /// The mode to use when parsing.
     pub parsing_mode: ParsingMode,
     /// The quirks mode of this stylesheet.
     pub quirks_mode: QuirksMode,
+    /// The active error reporter, or none if error reporting is disabled.
+    error_reporter: Option<&'a ParseErrorReporter>,
     /// The currently active namespaces.
     pub namespaces: Option<&'a Namespaces>,
 }
 
 impl<'a> ParserContext<'a> {
     /// Create a parser context.
     #[inline]
     pub fn new(
         stylesheet_origin: Origin,
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
+        error_reporter: Option<&'a ParseErrorReporter>,
     ) -> Self {
         ParserContext {
             stylesheet_origin,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
+            error_reporter,
             namespaces: None,
         }
     }
 
     /// Create a parser context for on-the-fly parsing in CSSOM
     #[inline]
     pub fn new_for_cssom(
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
+        error_reporter: Option<&'a ParseErrorReporter>,
     ) -> Self {
         Self::new(
             Origin::Author,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
+            error_reporter,
         )
     }
 
     /// Create a parser context based on a previous context, but with a modified rule type.
     #[inline]
     pub fn new_with_rule_type(
         context: &'a ParserContext,
         rule_type: CssRuleType,
@@ -105,16 +105,17 @@ impl<'a> ParserContext<'a> {
     ) -> ParserContext<'a> {
         ParserContext {
             stylesheet_origin: context.stylesheet_origin,
             url_data: context.url_data,
             rule_type: Some(rule_type),
             parsing_mode: context.parsing_mode,
             quirks_mode: context.quirks_mode,
             namespaces: Some(namespaces),
+            error_reporter: context.error_reporter,
         }
     }
 
     /// Whether we're in a @page rule.
     #[inline]
     pub fn in_page_rule(&self) -> bool {
         self.rule_type
             .map_or(false, |rule_type| rule_type == CssRuleType::Page)
@@ -122,31 +123,27 @@ impl<'a> ParserContext<'a> {
 
     /// Get the rule type, which assumes that one is available.
     pub fn rule_type(&self) -> CssRuleType {
         self.rule_type
             .expect("Rule type expected, but none was found.")
     }
 
     /// Record a CSS parse error with this context’s error reporting.
-    pub fn log_css_error<R>(
+    pub fn log_css_error(
         &self,
-        context: &ParserErrorContext<R>,
         location: SourceLocation,
         error: ContextualParseError,
-    ) where
-        R: ParseErrorReporter,
-    {
-        let location = SourceLocation {
-            line: location.line,
-            column: location.column,
+    ) {
+        let error_reporter = match self.error_reporter {
+            Some(r) => r,
+            None => return,
         };
-        context
-            .error_reporter
-            .report_error(self.url_data, location, error)
+
+        error_reporter.report_error(self.url_data, location, error)
     }
 
     /// Returns whether chrome-only rules should be parsed.
     pub fn chrome_rules_enabled(&self) -> bool {
         self.url_data.is_chrome() || self.stylesheet_origin == Origin::User
     }
 }
 
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -6,17 +6,17 @@
 
 #![deny(missing_docs)]
 
 use context::QuirksMode;
 use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
 use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
 use custom_properties::CustomPropertiesBuilder;
 use error_reporting::{ParseErrorReporter, ContextualParseError};
-use parser::{ParserContext, ParserErrorContext};
+use parser::ParserContext;
 use properties::animated_properties::AnimationValue;
 use shared_lock::Locked;
 use smallbitvec::{self, SmallBitVec};
 use smallvec::SmallVec;
 use std::fmt::{self, Write};
 use std::iter::{DoubleEndedIterator, Zip};
 use std::slice::Iter;
 use str::{CssString, CssStringBorrow, CssStringWriter};
@@ -1072,73 +1072,73 @@ where
         dest.write_str(" !important")?;
     }
 
     dest.write_char(';')
 }
 
 /// A helper to parse the style attribute of an element, in order for this to be
 /// shared between Servo and Gecko.
-pub fn parse_style_attribute<R>(
+///
+/// Inline because we call this cross-crate.
+#[inline]
+pub fn parse_style_attribute(
     input: &str,
     url_data: &UrlExtraData,
-    error_reporter: &R,
+    error_reporter: Option<&ParseErrorReporter>,
     quirks_mode: QuirksMode,
-) -> PropertyDeclarationBlock
-where
-    R: ParseErrorReporter
-{
+) -> PropertyDeclarationBlock {
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         quirks_mode,
+        error_reporter,
     );
 
-    let error_context = ParserErrorContext { error_reporter: error_reporter };
     let mut input = ParserInput::new(input);
-    parse_property_declaration_list(&context, &error_context, &mut Parser::new(&mut input))
+    parse_property_declaration_list(&context, &mut Parser::new(&mut input))
 }
 
 /// Parse a given property declaration. Can result in multiple
 /// `PropertyDeclaration`s when expanding a shorthand, for example.
 ///
 /// This does not attempt to parse !important at all.
-pub fn parse_one_declaration_into<R>(
+#[inline]
+pub fn parse_one_declaration_into(
     declarations: &mut SourcePropertyDeclaration,
     id: PropertyId,
     input: &str,
     url_data: &UrlExtraData,
-    error_reporter: &R,
+    error_reporter: Option<&ParseErrorReporter>,
     parsing_mode: ParsingMode,
     quirks_mode: QuirksMode
-) -> Result<(), ()>
-where
-    R: ParseErrorReporter
-{
+) -> Result<(), ()> {
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         parsing_mode,
         quirks_mode,
+        error_reporter,
     );
 
     let mut input = ParserInput::new(input);
     let mut parser = Parser::new(&mut input);
     let start_position = parser.position();
     parser.parse_entirely(|parser| {
         PropertyDeclaration::parse_into(declarations, id, &context, parser)
     }).map_err(|err| {
         let location = err.location;
         let error = ContextualParseError::UnsupportedPropertyDeclaration(
-            parser.slice_from(start_position), err);
-        let error_context = ParserErrorContext { error_reporter: error_reporter };
-        context.log_css_error(&error_context, location, error);
+            parser.slice_from(start_position),
+            err,
+        );
+        context.log_css_error(location, error);
     })
 }
 
 /// A struct to parse property declarations.
 struct PropertyDeclarationParser<'a, 'b: 'a> {
     context: &'a ParserContext<'b>,
     declarations: &'a mut SourcePropertyDeclaration,
 }
@@ -1188,24 +1188,20 @@ impl<'a, 'b, 'i> DeclarationParser<'i> f
         input.expect_exhausted()?;
         Ok(importance)
     }
 }
 
 
 /// Parse a list of property declarations and return a property declaration
 /// block.
-pub fn parse_property_declaration_list<R>(
+pub fn parse_property_declaration_list(
     context: &ParserContext,
-    error_context: &ParserErrorContext<R>,
     input: &mut Parser,
-) -> PropertyDeclarationBlock
-where
-    R: ParseErrorReporter
-{
+) -> PropertyDeclarationBlock {
     let mut declarations = SourcePropertyDeclaration::new();
     let mut block = PropertyDeclarationBlock::new();
     let parser = PropertyDeclarationParser {
         context: context,
         declarations: &mut declarations,
     };
     let mut iter = DeclarationListParser::new(input, parser);
     while let Some(declaration) = iter.next() {
@@ -1223,14 +1219,14 @@ where
                 // If the unrecognized property looks like a vendor-specific property,
                 // silently ignore it instead of polluting the error output.
                 if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownVendorProperty) = error.kind {
                     continue;
                 }
 
                 let location = error.location;
                 let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error);
-                context.log_css_error(error_context, location, error);
+                context.log_css_error(location, error);
             }
         }
     }
     block
 }
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -1437,16 +1437,17 @@ impl UnparsedValue {
             //
             // FIXME(emilio): ParsingMode is slightly fishy...
             let context = ParserContext::new(
                 Origin::Author,
                 &self.url_data,
                 None,
                 ParsingMode::DEFAULT,
                 quirks_mode,
+                None,
             );
 
             let mut input = ParserInput::new(&css);
             Parser::new(&mut input).parse_entirely(|input| {
                 match self.from_shorthand {
                     None => longhand_id.parse_value(&context, input),
                     Some(ShorthandId::All) => {
                         // No need to parse the 'all' shorthand as anything other than a CSS-wide
--- a/servo/components/style/stylesheets/font_feature_values_rule.rs
+++ b/servo/components/style/stylesheets/font_feature_values_rule.rs
@@ -5,22 +5,22 @@
 //! The [`@font-feature-values`][font-feature-values] at-rule.
 //!
 //! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
 
 use Atom;
 use cssparser::{AtRuleParser, AtRuleType, BasicParseErrorKind, CowRcStr};
 use cssparser::{DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{QualifiedRuleParser, RuleListParser, SourceLocation, Token};
-use error_reporting::{ContextualParseError, ParseErrorReporter};
+use error_reporting::ContextualParseError;
 #[cfg(feature = "gecko")]
 use gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::{self, gfxFontFeatureValueSet, nsTArray};
-use parser::{Parse, ParserContext, ParserErrorContext};
+use parser::{Parse, ParserContext};
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use str::CssStringWriter;
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 use stylesheets::CssRuleType;
 use values::computed::font::FamilyName;
 use values::serialize_atom_identifier;
 
@@ -262,37 +262,34 @@ macro_rules! font_feature_values_blocks 
                     $(
                         $ident: vec![],
                     )*
                     source_location: location,
                 }
             }
 
             /// Parses a `FontFeatureValuesRule`.
-            pub fn parse<R>(context: &ParserContext,
-                            error_context: &ParserErrorContext<R>,
-                            input: &mut Parser,
-                            family_names: Vec<FamilyName>,
-                            location: SourceLocation)
-                            -> FontFeatureValuesRule
-                where R: ParseErrorReporter
-            {
+            pub fn parse(
+                context: &ParserContext,
+                input: &mut Parser,
+                family_names: Vec<FamilyName>,
+                location: SourceLocation,
+            ) -> Self {
                 let mut rule = FontFeatureValuesRule::new(family_names, location);
 
                 {
                     let mut iter = RuleListParser::new_for_nested_rule(input, FontFeatureValuesRuleParser {
                         context: context,
-                        error_context: error_context,
                         rule: &mut rule,
                     });
                     while let Some(result) = iter.next() {
                         if let Err((error, slice)) = result {
                             let location = error.location;
                             let error = ContextualParseError::UnsupportedRule(slice, error);
-                            context.log_css_error(error_context, location, error);
+                            context.log_css_error(location, error);
                         }
                     }
                 }
                 rule
             }
 
             /// Prints font family names.
             pub fn font_family_to_css<W>(
@@ -393,30 +390,29 @@ macro_rules! font_feature_values_blocks 
         }
 
         /// Parser for `FontFeatureValuesRule`. Parses all blocks
         /// <feature-type> {
         ///   <feature-value-declaration-list>
         /// }
         /// <feature-type> = @stylistic | @historical-forms | @styleset |
         /// @character-variant | @swash | @ornaments | @annotation
-        struct FontFeatureValuesRuleParser<'a, R: 'a> {
+        struct FontFeatureValuesRuleParser<'a> {
             context: &'a ParserContext<'a>,
-            error_context: &'a ParserErrorContext<'a, R>,
             rule: &'a mut FontFeatureValuesRule,
         }
 
         /// Default methods reject all qualified rules.
-        impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
+        impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
             type Prelude = ();
             type QualifiedRule = ();
             type Error = StyleParseErrorKind<'i>;
         }
 
-        impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
+        impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
             type PreludeNoBlock = ();
             type PreludeBlock = BlockType;
             type AtRule = ();
             type Error = StyleParseErrorKind<'i>;
 
             fn parse_prelude<'t>(&mut self,
                                  name: CowRcStr<'i>,
                                  input: &mut Parser<'i, 't>)
@@ -445,17 +441,17 @@ macro_rules! font_feature_values_blocks 
 
                             let mut iter = DeclarationListParser::new(input, parser);
                             while let Some(declaration) = iter.next() {
                                 if let Err((error, slice)) = declaration {
                                     let location = error.location;
                                     let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(
                                         slice, error
                                     );
-                                    self.context.log_css_error(self.error_context, location, error);
+                                    self.context.log_css_error(location, error);
                                 }
                             }
                         },
                     )*
                 }
 
                 Ok(())
             }
--- a/servo/components/style/stylesheets/keyframes_rule.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -1,18 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Keyframes: https://drafts.csswg.org/css-animations/#keyframes
 
 use cssparser::{AtRuleParser, CowRcStr, Parser, ParserInput, QualifiedRuleParser, RuleListParser};
 use cssparser::{parse_one_rule, DeclarationListParser, DeclarationParser, SourceLocation, Token};
-use error_reporting::{ContextualParseError, NullReporter, ParseErrorReporter};
-use parser::{ParserContext, ParserErrorContext};
+use error_reporting::ContextualParseError;
+use parser::ParserContext;
 use properties::{DeclarationSource, Importance, PropertyDeclaration};
 use properties::{LonghandId, PropertyDeclarationBlock, PropertyId};
 use properties::{PropertyDeclarationId, SourcePropertyDeclaration};
 use properties::LonghandIdSet;
 use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
 use shared_lock::{Locked, ToCssWithGuard};
@@ -206,36 +206,32 @@ impl ToCssWithGuard for Keyframe {
 impl Keyframe {
     /// Parse a CSS keyframe.
     pub fn parse<'i>(
         css: &'i str,
         parent_stylesheet_contents: &StylesheetContents,
         lock: &SharedRwLock,
     ) -> Result<Arc<Locked<Self>>, ParseError<'i>> {
         let url_data = parent_stylesheet_contents.url_data.read();
-        let error_reporter = NullReporter;
         let namespaces = parent_stylesheet_contents.namespaces.read();
         let mut context = ParserContext::new(
             parent_stylesheet_contents.origin,
             &url_data,
             Some(CssRuleType::Keyframe),
             ParsingMode::DEFAULT,
             parent_stylesheet_contents.quirks_mode,
+            None,
         );
-        let error_context = ParserErrorContext {
-            error_reporter: &error_reporter,
-        };
         context.namespaces = Some(&*namespaces);
         let mut input = ParserInput::new(css);
         let mut input = Parser::new(&mut input);
 
         let mut declarations = SourcePropertyDeclaration::new();
         let mut rule_parser = KeyframeListParser {
             context: &context,
-            error_context: &error_context,
             shared_lock: &lock,
             declarations: &mut declarations,
         };
         parse_one_rule(&mut input, &mut rule_parser)
     }
 }
 
 impl DeepCloneWithLock for Keyframe {
@@ -472,65 +468,59 @@ impl KeyframesAnimation {
 /// Parses a keyframes list, like:
 /// 0%, 50% {
 ///     width: 50%;
 /// }
 ///
 /// 40%, 60%, 100% {
 ///     width: 100%;
 /// }
-struct KeyframeListParser<'a, R: 'a> {
+struct KeyframeListParser<'a> {
     context: &'a ParserContext<'a>,
-    error_context: &'a ParserErrorContext<'a, R>,
     shared_lock: &'a SharedRwLock,
     declarations: &'a mut SourcePropertyDeclaration,
 }
 
 /// Parses a keyframe list from CSS input.
-pub fn parse_keyframe_list<R>(
+pub fn parse_keyframe_list(
     context: &ParserContext,
-    error_context: &ParserErrorContext<R>,
     input: &mut Parser,
     shared_lock: &SharedRwLock,
-) -> Vec<Arc<Locked<Keyframe>>>
-where
-    R: ParseErrorReporter,
-{
+) -> Vec<Arc<Locked<Keyframe>>> {
     debug_assert!(
         context.namespaces.is_some(),
         "Parsing a keyframe list from a context without namespaces?"
     );
 
     let mut declarations = SourcePropertyDeclaration::new();
     RuleListParser::new_for_nested_rule(
         input,
         KeyframeListParser {
             context: context,
-            error_context: error_context,
             shared_lock: shared_lock,
             declarations: &mut declarations,
         },
     ).filter_map(Result::ok)
         .collect()
 }
 
-impl<'a, 'i, R> AtRuleParser<'i> for KeyframeListParser<'a, R> {
+impl<'a, 'i> AtRuleParser<'i> for KeyframeListParser<'a> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = Arc<Locked<Keyframe>>;
     type Error = StyleParseErrorKind<'i>;
 }
 
 /// A wrapper to wraps the KeyframeSelector with its source location
 struct KeyframeSelectorParserPrelude {
     selector: KeyframeSelector,
     source_location: SourceLocation,
 }
 
-impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for KeyframeListParser<'a, R> {
+impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
     type Prelude = KeyframeSelectorParserPrelude;
     type QualifiedRule = Arc<Locked<Keyframe>>;
     type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self::Prelude, ParseError<'i>> {
@@ -542,18 +532,17 @@ impl<'a, 'i, R: ParseErrorReporter> Qual
                 source_location: start_location,
             }),
             Err(e) => {
                 let location = e.location;
                 let error = ContextualParseError::InvalidKeyframeRule(
                     input.slice_from(start_position),
                     e.clone(),
                 );
-                self.context
-                    .log_css_error(self.error_context, location, error);
+                self.context.log_css_error(location, error);
                 Err(e)
             },
         }
     }
 
     fn parse_block<'t>(
         &mut self,
         prelude: Self::Prelude,
@@ -580,17 +569,17 @@ impl<'a, 'i, R: ParseErrorReporter> Qual
                         DeclarationSource::Parsing,
                     );
                 },
                 Err((error, slice)) => {
                     iter.parser.declarations.clear();
                     let location = error.location;
                     let error =
                         ContextualParseError::UnsupportedKeyframePropertyDeclaration(slice, error);
-                    context.log_css_error(self.error_context, location, error);
+                    context.log_css_error(location, error);
                 },
             }
             // `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
         }
         Ok(Arc::new(self.shared_lock.wrap(Keyframe {
             selector: prelude.selector,
             block: Arc::new(self.shared_lock.wrap(block)),
             source_location: prelude.source_location,
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -19,20 +19,19 @@ mod rule_list;
 mod rule_parser;
 mod rules_iterator;
 mod style_rule;
 mod stylesheet;
 pub mod supports_rule;
 pub mod viewport_rule;
 
 use cssparser::{parse_one_rule, Parser, ParserInput};
-use error_reporting::NullReporter;
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use parser::{ParserContext, ParserErrorContext};
+use parser::ParserContext;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
 use shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt;
 use str::CssStringWriter;
 use style_traits::ParsingMode;
 
 pub use self::counter_style_rule::CounterStyleRule;
@@ -223,37 +222,34 @@ impl CssRule {
         css: &str,
         insert_rule_context: InsertRuleContext,
         parent_stylesheet_contents: &StylesheetContents,
         shared_lock: &SharedRwLock,
         state: State,
         loader: Option<&StylesheetLoader>,
     ) -> Result<Self, RulesMutateError> {
         let url_data = parent_stylesheet_contents.url_data.read();
-        let error_reporter = NullReporter;
         let context = ParserContext::new(
             parent_stylesheet_contents.origin,
             &url_data,
             None,
             ParsingMode::DEFAULT,
             parent_stylesheet_contents.quirks_mode,
+            None,
         );
 
         let mut input = ParserInput::new(css);
         let mut input = Parser::new(&mut input);
 
         let mut guard = parent_stylesheet_contents.namespaces.write();
 
         // nested rules are in the body state
         let mut rule_parser = TopLevelRuleParser {
             stylesheet_origin: parent_stylesheet_contents.origin,
             context,
-            error_context: ParserErrorContext {
-                error_reporter: &error_reporter,
-            },
             shared_lock: &shared_lock,
             loader,
             state,
             dom_error: None,
             namespaces: &mut *guard,
             insert_rule_context: Some(insert_rule_context),
         };
 
--- a/servo/components/style/stylesheets/rule_parser.rs
+++ b/servo/components/style/stylesheets/rule_parser.rs
@@ -3,20 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Parsing of the stylesheet contents.
 
 use {Namespace, Prefix};
 use counter_style::{parse_counter_style_body, parse_counter_style_name_definition};
 use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListParser};
 use cssparser::{BasicParseError, BasicParseErrorKind, CowRcStr, SourceLocation};
-use error_reporting::{ContextualParseError, ParseErrorReporter};
+use error_reporting::ContextualParseError;
 use font_face::parse_font_face_block;
 use media_queries::MediaList;
-use parser::{Parse, ParserContext, ParserErrorContext};
+use parser::{Parse, ParserContext};
 use properties::parse_property_declaration_list;
 use selector_parser::{SelectorImpl, SelectorParser};
 use selectors::SelectorList;
 use servo_arc::Arc;
 use shared_lock::{Locked, SharedRwLock};
 use str::starts_with_ignore_ascii_case;
 use style_traits::{ParseError, StyleParseErrorKind};
 use stylesheets::{CssRule, CssRuleType, CssRules, Origin, RulesMutateError, StylesheetLoader};
@@ -35,51 +35,48 @@ use values::computed::font::FamilyName;
 pub struct InsertRuleContext<'a> {
     /// The rule list we're about to insert into.
     pub rule_list: &'a [CssRule],
     /// The index we're about to get inserted at.
     pub index: usize,
 }
 
 /// The parser for the top-level rules in a stylesheet.
-pub struct TopLevelRuleParser<'a, R: 'a> {
+pub struct TopLevelRuleParser<'a> {
     /// The origin of the stylesheet we're parsing.
     pub stylesheet_origin: Origin,
     /// A reference to the lock we need to use to create rules.
     pub shared_lock: &'a SharedRwLock,
     /// A reference to a stylesheet loader if applicable, for `@import` rules.
     pub loader: Option<&'a StylesheetLoader>,
     /// The top-level parser context.
     ///
     /// This won't contain any namespaces, and only nested parsers created with
     /// `ParserContext::new_with_rule_type` will.
     pub context: ParserContext<'a>,
-    /// The context required for reporting parse errors.
-    pub error_context: ParserErrorContext<'a, R>,
     /// The current state of the parser.
     pub state: State,
     /// Whether we have tried to parse was invalid due to being in the wrong
     /// place (e.g. an @import rule was found while in the `Body` state). Reset
     /// to `false` when `take_had_hierarchy_error` is called.
     pub dom_error: Option<RulesMutateError>,
     /// The namespace map we use for parsing. Needs to start as `Some()`, and
     /// will be taken out after parsing namespace rules, and that reference will
     /// be moved to `ParserContext`.
     pub namespaces: &'a mut Namespaces,
     /// The info we need insert a rule in a list.
     pub insert_rule_context: Option<InsertRuleContext<'a>>,
 }
 
-impl<'b, R> TopLevelRuleParser<'b, R> {
-    fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b, R> {
+impl<'b> TopLevelRuleParser<'b> {
+    fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b> {
         NestedRuleParser {
             stylesheet_origin: self.stylesheet_origin,
             shared_lock: self.shared_lock,
             context: &self.context,
-            error_context: &self.error_context,
             namespaces: &self.namespaces,
         }
     }
 
     /// Returns the current state of the parser.
     pub fn state(&self) -> State {
         self.state
     }
@@ -171,17 +168,17 @@ pub enum AtRuleBlockPrelude {
 /// A rule prelude for at-rule without block.
 pub enum AtRuleNonBlockPrelude {
     /// A @import rule prelude.
     Import(CssUrl, Arc<Locked<MediaList>>, SourceLocation),
     /// A @namespace rule prelude.
     Namespace(Option<Prefix>, Namespace, SourceLocation),
 }
 
-impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, R> {
+impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
     type PreludeNoBlock = AtRuleNonBlockPrelude;
     type PreludeBlock = AtRuleBlockPrelude;
     type AtRule = CssRule;
     type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         name: CowRcStr<'i>,
@@ -192,21 +189,17 @@ impl<'a, 'i, R: ParseErrorReporter> AtRu
             "import" => {
                 if !self.check_state(State::Imports) {
                     return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
                 }
 
                 let url_string = input.expect_url_or_string()?.as_ref().to_owned();
                 let url = CssUrl::parse_from_string(url_string, &self.context);
 
-                let media = MediaList::parse(
-                    &self.context,
-                    input,
-                    self.error_context.error_reporter,
-                );
+                let media = MediaList::parse(&self.context, input);
                 let media = Arc::new(self.shared_lock.wrap(media));
 
                 let prelude = AtRuleNonBlockPrelude::Import(url, media, location);
                 return Ok(AtRuleType::WithoutBlock(prelude));
             },
             "namespace" => {
                 if !self.check_state(State::Namespaces) {
                     return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule))
@@ -291,17 +284,17 @@ impl<'a, 'i, R: ParseErrorReporter> AtRu
     }
 }
 
 pub struct QualifiedRuleParserPrelude {
     selectors: SelectorList<SelectorImpl>,
     source_location: SourceLocation,
 }
 
-impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, R> {
+impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
     type Prelude = QualifiedRuleParserPrelude;
     type QualifiedRule = CssRule;
     type Error = StyleParseErrorKind<'i>;
 
     #[inline]
     fn parse_prelude<'t>(
         &mut self,
         input: &mut Parser<'i, 't>,
@@ -322,77 +315,74 @@ impl<'a, 'i, R: ParseErrorReporter> Qual
         QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input).map(|result| {
             self.state = State::Body;
             result
         })
     }
 }
 
 #[derive(Clone)] // shallow, relatively cheap .clone
-struct NestedRuleParser<'a, 'b: 'a, R: 'b> {
+struct NestedRuleParser<'a, 'b: 'a> {
     stylesheet_origin: Origin,
     shared_lock: &'a SharedRwLock,
     context: &'a ParserContext<'b>,
-    error_context: &'a ParserErrorContext<'b, R>,
     namespaces: &'a Namespaces,
 }
 
-impl<'a, 'b, R: ParseErrorReporter> NestedRuleParser<'a, 'b, R> {
+impl<'a, 'b> NestedRuleParser<'a, 'b> {
     fn parse_nested_rules(
         &mut self,
         input: &mut Parser,
         rule_type: CssRuleType,
     ) -> Arc<Locked<CssRules>> {
-        let context = ParserContext::new_with_rule_type(self.context, rule_type, self.namespaces);
+        let context = ParserContext::new_with_rule_type(
+            self.context,
+            rule_type,
+            self.namespaces,
+        );
 
         let nested_parser = NestedRuleParser {
             stylesheet_origin: self.stylesheet_origin,
             shared_lock: self.shared_lock,
             context: &context,
-            error_context: &self.error_context,
             namespaces: self.namespaces,
         };
 
         let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
         let mut rules = Vec::new();
         while let Some(result) = iter.next() {
             match result {
                 Ok(rule) => rules.push(rule),
                 Err((error, slice)) => {
                     let location = error.location;
                     let error = ContextualParseError::InvalidRule(slice, error);
-                    self.context
-                        .log_css_error(self.error_context, location, error);
+                    self.context.log_css_error(location, error);
                 },
             }
         }
         CssRules::new(rules, self.shared_lock)
     }
 }
 
-impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a, 'b, R> {
+impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
     type PreludeNoBlock = AtRuleNonBlockPrelude;
     type PreludeBlock = AtRuleBlockPrelude;
     type AtRule = CssRule;
     type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         name: CowRcStr<'i>,
         input: &mut Parser<'i, 't>,
     ) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
         let location = input.current_source_location();
 
         match_ignore_ascii_case! { &*name,
             "media" => {
-                let media_queries = MediaList::parse(
-                    self.context,
-                    input,
-                    self.error_context.error_reporter,
-                );
+                let media_queries = MediaList::parse(self.context, input);
                 let arc = Arc::new(self.shared_lock.wrap(media_queries));
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
             },
             "supports" => {
                 let cond = SupportsCondition::parse(input)?;
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond, location)))
             },
             "font-face" => {
@@ -468,30 +458,29 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
             AtRuleBlockPrelude::FontFace(location) => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::FontFace,
                     self.namespaces,
                 );
 
                 Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
-                    parse_font_face_block(&context, self.error_context, input, location).into(),
+                    parse_font_face_block(&context, input, location).into(),
                 ))))
             },
             AtRuleBlockPrelude::FontFeatureValues(family_names, location) => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::FontFeatureValues,
                     self.namespaces,
                 );
 
                 Ok(CssRule::FontFeatureValues(Arc::new(self.shared_lock.wrap(
                     FontFeatureValuesRule::parse(
                         &context,
-                        self.error_context,
                         input,
                         family_names,
                         location,
                     ),
                 ))))
             },
             AtRuleBlockPrelude::CounterStyle(name, location) => {
                 let context = ParserContext::new_with_rule_type(
@@ -500,17 +489,16 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
                     self.namespaces,
                 );
 
                 Ok(CssRule::CounterStyle(Arc::new(
                     self.shared_lock.wrap(
                         parse_counter_style_body(
                             name,
                             &context,
-                            self.error_context,
                             input,
                             location,
                         )?.into(),
                     ),
                 )))
             },
             AtRuleBlockPrelude::Media(media_queries, source_location) => {
                 Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
@@ -539,49 +527,48 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
             AtRuleBlockPrelude::Viewport => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::Viewport,
                     self.namespaces,
                 );
 
                 Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
-                    ViewportRule::parse(&context, self.error_context, input)?,
+                    ViewportRule::parse(&context, input)?,
                 ))))
             },
             AtRuleBlockPrelude::Keyframes(name, vendor_prefix, source_location) => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::Keyframes,
                     self.namespaces,
                 );
 
                 Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(
                     KeyframesRule {
                         name,
                         keyframes: parse_keyframe_list(
                             &context,
-                            self.error_context,
                             input,
                             self.shared_lock,
                         ),
                         vendor_prefix,
                         source_location,
                     },
                 ))))
             },
             AtRuleBlockPrelude::Page(source_location) => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::Page,
                     self.namespaces,
                 );
 
                 let declarations =
-                    parse_property_declaration_list(&context, self.error_context, input);
+                    parse_property_declaration_list(&context, input);
                 Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
                     block: Arc::new(self.shared_lock.wrap(declarations)),
                     source_location,
                 }))))
             },
             AtRuleBlockPrelude::Document(condition, source_location) => {
                 if !cfg!(feature = "gecko") {
                     unreachable!()
@@ -593,17 +580,17 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
                         source_location,
                     },
                 ))))
             },
         }
     }
 }
 
-impl<'a, 'b, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b, R> {
+impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
     type Prelude = QualifiedRuleParserPrelude;
     type QualifiedRule = CssRule;
     type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         input: &mut Parser<'i, 't>,
     ) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
@@ -622,16 +609,16 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
     fn parse_block<'t>(
         &mut self,
         prelude: QualifiedRuleParserPrelude,
         input: &mut Parser<'i, 't>,
     ) -> Result<CssRule, ParseError<'i>> {
         let context =
             ParserContext::new_with_rule_type(self.context, CssRuleType::Style, self.namespaces);
 
-        let declarations = parse_property_declaration_list(&context, self.error_context, input);
+        let declarations = parse_property_declaration_list(&context, input);
         Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
             selectors: prelude.selectors,
             block: Arc::new(self.shared_lock.wrap(declarations)),
             source_location: prelude.source_location,
         }))))
     }
 }
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -8,17 +8,17 @@ use cssparser::{Parser, ParserInput, Rul
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use fallible::FallibleVec;
 use fnv::FnvHashMap;
 use invalidation::media_queries::{MediaListKey, ToMediaListKey};
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
 use media_queries::{Device, MediaList};
 use parking_lot::RwLock;
-use parser::{ParserContext, ParserErrorContext};
+use parser::ParserContext;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard};
 use std::mem;
 use std::sync::atomic::{AtomicBool, Ordering};
 use style_traits::ParsingMode;
 use stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
 use stylesheets::loader::StylesheetLoader;
 use stylesheets::rule_parser::{State, TopLevelRuleParser};
@@ -64,23 +64,23 @@ pub struct StylesheetContents {
     pub source_map_url: RwLock<Option<String>>,
     /// This stylesheet's source URL.
     pub source_url: RwLock<Option<String>>,
 }
 
 impl StylesheetContents {
     /// Parse a given CSS string, with a given url-data, origin, and
     /// quirks mode.
-    pub fn from_str<R: ParseErrorReporter>(
+    pub fn from_str(
         css: &str,
         url_data: UrlExtraData,
         origin: Origin,
         shared_lock: &SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &R,
+        error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
     ) -> Self {
         let namespaces = RwLock::new(Namespaces::default());
         let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
             css,
             &url_data,
             origin,
@@ -301,28 +301,26 @@ impl StylesheetInDocument for DocumentSt
     #[inline]
     fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
         self.0.rules(guard)
     }
 }
 
 impl Stylesheet {
     /// Updates an empty stylesheet from a given string of text.
-    pub fn update_from_str<R>(
+    pub fn update_from_str(
         existing: &Stylesheet,
         css: &str,
         url_data: UrlExtraData,
         stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &R,
+        error_reporter: Option<&ParseErrorReporter>,
         line_number_offset: u32,
-    ) where
-        R: ParseErrorReporter,
-    {
+    ) {
         let namespaces = RwLock::new(Namespaces::default());
-        let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
+        let (rules, source_map_url, source_url) = Self::parse_rules(
             css,
             &url_data,
             existing.contents.origin,
             &mut *namespaces.write(),
             &existing.shared_lock,
             stylesheet_loader,
             error_reporter,
             existing.contents.quirks_mode,
@@ -337,41 +335,45 @@ impl Stylesheet {
 
         // Acquire the lock *after* parsing, to minimize the exclusive section.
         let mut guard = existing.shared_lock.write();
         *existing.contents.rules.write_with(&mut guard) = CssRules(rules);
         *existing.contents.source_map_url.write() = source_map_url;
         *existing.contents.source_url.write() = source_url;
     }
 
-    fn parse_rules<R: ParseErrorReporter>(
+    fn parse_rules(
         css: &str,
         url_data: &UrlExtraData,
         origin: Origin,
         namespaces: &mut Namespaces,
         shared_lock: &SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &R,
+        error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
     ) -> (Vec<CssRule>, Option<String>, Option<String>) {
         let mut rules = Vec::new();
         let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset);
         let mut input = Parser::new(&mut input);
 
-        let context = ParserContext::new(origin, url_data, None, ParsingMode::DEFAULT, quirks_mode);
-
-        let error_context = ParserErrorContext { error_reporter };
+        let context = ParserContext::new(
+            origin,
+            url_data,
+            None,
+            ParsingMode::DEFAULT,
+            quirks_mode,
+            error_reporter,
+        );
 
         let rule_parser = TopLevelRuleParser {
             stylesheet_origin: origin,
             shared_lock,
             loader: stylesheet_loader,
             context,
-            error_context,
             state: State::Start,
             dom_error: None,
             insert_rule_context: None,
             namespaces,
         };
 
         {
             let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
@@ -385,17 +387,16 @@ impl Stylesheet {
                         if rules.try_push(rule).is_err() {
                             break;
                         }
                     },
                     Err((error, slice)) => {
                         let location = error.location;
                         let error = ContextualParseError::InvalidRule(slice, error);
                         iter.parser.context.log_css_error(
-                            &iter.parser.error_context,
                             location,
                             error,
                         );
                     },
                 }
             }
         }
 
@@ -404,27 +405,27 @@ impl Stylesheet {
         (rules, source_map_url, source_url)
     }
 
     /// Creates an empty stylesheet and parses it with a given base url, origin
     /// and media.
     ///
     /// Effectively creates a new stylesheet and forwards the hard work to
     /// `Stylesheet::update_from_str`.
-    pub fn from_str<R: ParseErrorReporter>(
+    pub fn from_str(
         css: &str,
         url_data: UrlExtraData,
         origin: Origin,
         media: Arc<Locked<MediaList>>,
         shared_lock: SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &R,
+        error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
-    ) -> Stylesheet {
+    ) -> Self {
         let contents = StylesheetContents::from_str(
             css,
             url_data,
             origin,
             &shared_lock,
             stylesheet_loader,
             error_reporter,
             quirks_mode,
--- a/servo/components/style/stylesheets/viewport_rule.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -6,21 +6,21 @@
 //!
 //! [at]: https://drafts.csswg.org/css-device-adapt/#atviewport-rule
 //! [meta]: https://drafts.csswg.org/css-device-adapt/#viewport-meta
 
 use app_units::Au;
 use context::QuirksMode;
 use cssparser::{parse_important, AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::CowRcStr;
-use error_reporting::{ContextualParseError, ParseErrorReporter};
+use error_reporting::ContextualParseError;
 use euclid::TypedSize2D;
 use font_metrics::get_metrics_provider_for_product;
 use media_queries::Device;
-use parser::{ParserContext, ParserErrorContext};
+use parser::ParserContext;
 use properties::StyleBuilder;
 use rule_cache::RuleCacheConditions;
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard};
 use std::borrow::Cow;
 use std::cell::RefCell;
 use std::fmt::{self, Write};
 use std::iter::Enumerate;
@@ -350,42 +350,40 @@ const SEPARATOR: &'static [char] = &['\x
 
 #[inline]
 fn is_whitespace_separator_or_equals(c: &char) -> bool {
     WHITESPACE.contains(c) || SEPARATOR.contains(c) || *c == '='
 }
 
 impl ViewportRule {
     /// Parse a single @viewport rule.
-    pub fn parse<'i, 't, R>(
+    ///
+    /// TODO(emilio): This could use the `Parse` trait now.
+    pub fn parse<'i, 't>(
         context: &ParserContext,
-        error_context: &ParserErrorContext<R>,
         input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>>
-    where
-        R: ParseErrorReporter,
-    {
-        let parser = ViewportRuleParser { context: context };
+    ) -> Result<Self, ParseError<'i>> {
+        let parser = ViewportRuleParser { context };
 
         let mut cascade = Cascade::new();
         let mut parser = DeclarationListParser::new(input, parser);
         while let Some(result) = parser.next() {
             match result {
                 Ok(declarations) => {
                     for declarations in declarations {
                         cascade.add(Cow::Owned(declarations))
                     }
                 },
                 Err((error, slice)) => {
                     let location = error.location;
                     let error = ContextualParseError::UnsupportedViewportDescriptorDeclaration(
                         slice,
                         error,
                     );
-                    context.log_css_error(error_context, location, error);
+                    context.log_css_error(location, error);
                 },
             }
         }
         Ok(ViewportRule {
             declarations: cascade.finish(),
         })
     }
 }
--- a/servo/ports/geckolib/error_reporter.rs
+++ b/servo/ports/geckolib/error_reporter.rs
@@ -4,55 +4,57 @@
 
 //! Wrapper around Gecko's CSS error reporting mechanism.
 
 #![allow(unsafe_code)]
 
 use cssparser::{CowRcStr, serialize_identifier, ToCss};
 use cssparser::{SourceLocation, ParseError, ParseErrorKind, Token, BasicParseErrorKind};
 use selectors::parser::SelectorParseErrorKind;
-use std::cell::Cell;
 use std::ffi::CStr;
 use std::ptr;
 use style::error_reporting::{ParseErrorReporter, ContextualParseError};
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::structs::{Loader, StyleSheet as DomStyleSheet, nsIURI};
 use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
 use style::stylesheets::UrlExtraData;
 use style_traits::{StyleParseErrorKind, ValueParseErrorKind};
 
 pub type ErrorKind<'i> = ParseErrorKind<'i, StyleParseErrorKind<'i>>;
 
 /// An error reporter with all the data we need to report errors.
 pub struct ErrorReporter {
     sheet: *const DomStyleSheet,
     loader: *const Loader,
     uri: *mut nsIURI,
-    cached_error_reporting_enabled: Cell<Option<bool>>,
 }
 
 impl ErrorReporter {
-    /// Create a new instance of the Gecko error reporter.
+    /// Create a new instance of the Gecko error reporter, if error reporting is
+    /// enabled.
     pub fn new(
         sheet: *mut DomStyleSheet,
         loader: *mut Loader,
         extra_data: *mut RawUrlExtraData,
-    ) -> Self {
+    ) -> Option<Self> {
+        if !Self::reporting_enabled(sheet, loader) {
+            return None;
+        }
+
         let uri = unsafe {
             extra_data.as_ref()
                 .map(|d| d.mBaseURI.raw::<nsIURI>())
                 .unwrap_or(ptr::null_mut())
         };
 
-        ErrorReporter {
+        Some(ErrorReporter {
             sheet,
             loader,
             uri,
-            cached_error_reporting_enabled: Cell::new(None),
-        }
+        })
     }
 }
 
 enum ErrorString<'a> {
     Snippet(CowRcStr<'a>),
     Ident(CowRcStr<'a>),
     UnexpectedToken(Token<'a>),
 }
@@ -388,31 +390,25 @@ impl<'a> ErrorHelpers<'a> for Contextual
                 }
             }
         };
         (None, msg, action)
     }
 }
 
 impl ErrorReporter {
-    fn reporting_enabled(&self) -> bool {
-        if let Some(enabled) = self.cached_error_reporting_enabled.get() {
-            return enabled;
-        }
-        let enabled = unsafe {
-            bindings::Gecko_ErrorReportingEnabled(self.sheet, self.loader)
-        };
-        self.cached_error_reporting_enabled.set(Some(enabled));
-        enabled
+    fn reporting_enabled(
+        sheet: *const DomStyleSheet,
+        loader: *const Loader,
+    ) -> bool {
+        unsafe { bindings::Gecko_ErrorReportingEnabled(sheet, loader) }
     }
 
     pub fn report(&self, location: SourceLocation, error: ContextualParseError) {
-        if !self.reporting_enabled() {
-            return;
-        }
+        debug_assert!(Self::reporting_enabled(self.sheet, self.loader));
 
         let (pre, name, action) = error.to_gecko_message();
         let suffix = match action {
             Action::Nothing => ptr::null(),
             Action::Skip => cstr!("PEDeclSkipped").as_ptr(),
             Action::Drop => cstr!("PEDeclDropped").as_ptr(),
         };
         let params = error.error_params();
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -21,17 +21,17 @@ use style::applicable_declarations::Appl
 use style::author_styles::AuthorStyles;
 use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
 use style::context::ThreadLocalStyleContext;
 use style::counter_style;
 use style::data::{ElementStyles, self};
 use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
 use style::driver;
 use style::element_state::{DocumentState, ElementState};
-use style::error_reporting::{ContextualParseError, NullReporter, ParseErrorReporter};
+use style::error_reporting::{ContextualParseError, ParseErrorReporter};
 use style::font_metrics::{FontMetricsProvider, get_metrics_provider_for_product};
 use style::gecko::data::{GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl};
 use style::gecko::global_style_data::{GLOBAL_STYLE_DATA, GlobalStyleData, STYLE_THREAD_POOL};
 use style::gecko::restyle_damage::GeckoRestyleDamage;
 use style::gecko::selector_parser::{NonTSPseudoClass, PseudoElement};
 use style::gecko::traversal::RecalcStyleOnly;
 use style::gecko::wrapper::{GeckoElement, GeckoNode};
 use style::gecko_bindings::bindings;
@@ -1150,17 +1150,17 @@ pub extern "C" fn Servo_StyleSheet_Empty
     let shared_lock = &global_style_data.shared_lock;
     Arc::new(
         StylesheetContents::from_str(
             "",
             unsafe { dummy_url_data() }.clone(),
             origin,
             shared_lock,
             /* loader = */ None,
-            &NullReporter,
+            None,
             QuirksMode::NoQuirks,
             0
         )
     ).into_strong()
 }
 
 /// Note: The load_data corresponds to this sheet, and is passed as the parent
 /// load data for child sheet loads. It may be null for certain cases where we
@@ -1191,20 +1191,25 @@ pub extern "C" fn Servo_StyleSheet_FromU
     // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
     let loader: Option<&StyleStylesheetLoader> = match loader {
         None => None,
         Some(ref s) => Some(s),
     };
 
 
     Arc::new(StylesheetContents::from_str(
-        input, url_data.clone(), mode_to_origin(mode),
-        &global_style_data.shared_lock, loader, &reporter,
-        quirks_mode.into(), line_number_offset)
-    ).into_strong()
+        input,
+        url_data.clone(),
+        mode_to_origin(mode),
+        &global_style_data.shared_lock,
+        loader,
+        reporter.as_ref().map(|r| r as &ParseErrorReporter),
+        quirks_mode.into(),
+        line_number_offset,
+    )).into_strong()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
     load_data: *mut SheetLoadDataHolder,
     extra_data: *mut URLExtraData,
     bytes: *const nsACString,
     mode: SheetParsingMode,
@@ -2510,16 +2515,17 @@ pub unsafe extern "C" fn Servo_FontFaceR
     let mut parser = Parser::new(&mut input);
     let url_data = RefPtr::from_ptr_ref(&data);
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::FontFace),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
 
     write_locked_arc(rule, |rule: &mut FontFaceRule| {
         macro_rules! to_css_text {
             (
                 valid: [$($v_enum_name:ident => $field:ident,)*]
                 invalid: [$($i_enum_name:ident,)*]
             ) => {
@@ -2719,16 +2725,17 @@ macro_rules! counter_style_descriptors {
             let mut parser = Parser::new(&mut input);
             let url_data = dummy_url_data();
             let context = ParserContext::new(
                 Origin::Author,
                 url_data,
                 Some(CssRuleType::CounterStyle),
                 ParsingMode::DEFAULT,
                 QuirksMode::NoQuirks,
+                None,
             );
 
             write_locked_arc(rule, |rule: &mut CounterStyleRule| {
                 match desc {
                     $(nsCSSCounterDesc::$desc => {
                         match parser.parse_entirely(|i| Parse::parse(&context, i)) {
                             Ok(value) => rule.$setter(value),
                             Err(_) => false,
@@ -3201,28 +3208,25 @@ pub extern "C" fn Servo_StyleSet_Drop(da
 #[no_mangle]
 pub unsafe extern "C" fn Servo_StyleSet_CompatModeChanged(raw_data: RawServoStyleSetBorrowed) {
     let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
     let doc =
         &*data.stylist.device().pres_context().mDocument.raw::<nsIDocument>();
     data.stylist.set_quirks_mode(QuirksMode::from(doc.mCompatMode));
 }
 
-fn parse_property_into<R>(
+fn parse_property_into(
     declarations: &mut SourcePropertyDeclaration,
     property_id: PropertyId,
     value: *const nsACString,
     data: *mut URLExtraData,
     parsing_mode: structs::ParsingMode,
     quirks_mode: QuirksMode,
-    reporter: &R
-) -> Result<(), ()>
-where
-    R: ParseErrorReporter
-{
+    reporter: Option<&ParseErrorReporter>,
+) -> Result<(), ()> {
     use style_traits::ParsingMode;
     let value = unsafe { value.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
     let parsing_mode = ParsingMode::from_bits_truncate(parsing_mode);
 
     parse_one_declaration_into(
         declarations,
         property_id,
@@ -3241,18 +3245,27 @@ pub extern "C" fn Servo_ParseProperty(
     parsing_mode: structs::ParsingMode,
     quirks_mode: nsCompatibility,
     loader: *mut Loader,
 ) -> RawServoDeclarationBlockStrong {
     let id = get_property_id_from_nscsspropertyid!(property,
                                                    RawServoDeclarationBlockStrong::null());
     let mut declarations = SourcePropertyDeclaration::new();
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
-    match parse_property_into(&mut declarations, id, value, data,
-                              parsing_mode, quirks_mode.into(), &reporter) {
+    let result = parse_property_into(
+        &mut declarations,
+        id,
+        value,
+        data,
+        parsing_mode,
+        quirks_mode.into(),
+        reporter.as_ref().map(|r| r as &ParseErrorReporter),
+    );
+
+    match result {
         Ok(()) => {
             let global_style_data = &*GLOBAL_STYLE_DATA;
             let mut block = PropertyDeclarationBlock::new();
             block.extend(
                 declarations.drain(),
                 Importance::Normal,
                 DeclarationSource::CssOm,
             );
@@ -3273,16 +3286,17 @@ pub extern "C" fn Servo_ParseEasing(
     // FIXME Dummy URL data would work fine here.
     let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
     let easing = unsafe { (*easing).to_string() };
     let mut input = ParserInput::new(&easing);
     let mut parser = Parser::new(&mut input);
     let result =
         parser.parse_entirely(|p| transition_timing_function::single_value::parse(&context, p));
     match result {
         Ok(parsed_easing) => {
@@ -3363,17 +3377,17 @@ pub extern "C" fn Servo_ParseStyleAttrib
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let value = unsafe { data.as_ref().unwrap().as_str_unchecked() };
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
     let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) };
     Arc::new(global_style_data.shared_lock.wrap(
         parse_style_attribute(
             value,
             url_data,
-            &reporter,
+            reporter.as_ref().map(|r| r as &ParseErrorReporter),
             quirks_mode.into(),
         )
     )).into_strong()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_CreateEmpty() -> RawServoDeclarationBlockStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
@@ -3548,17 +3562,17 @@ fn set_property(
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
     let result = parse_property_into(
         &mut source_declarations,
         property_id,
         value,
         data,
         parsing_mode,
         quirks_mode,
-        &reporter,
+        reporter.as_ref().map(|r| r as &ParseErrorReporter),
     );
 
     if result.is_err() {
         return false;
     }
 
     before_change_closure.invoke();
 
@@ -3752,24 +3766,22 @@ pub unsafe extern "C" fn Servo_MediaList
     };
 
     let context = ParserContext::new(
         origin,
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        // TODO(emilio): Looks like error reporting could be useful here?
+        None,
     );
 
     write_locked_arc(list, |list: &mut MediaList| {
-        *list = MediaList::parse(
-            &context,
-            &mut parser,
-            &NullReporter,
-        );
+        *list = MediaList::parse(&context, &mut parser);
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_GetLength(list: RawServoMediaListBorrowed) -> u32 {
     read_locked_arc(list, |list: &MediaList| list.media_queries.len() as u32)
 }
 
@@ -3796,16 +3808,17 @@ pub extern "C" fn Servo_MediaList_Append
 ) {
     let new_medium = unsafe { new_medium.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new_for_cssom(
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
     write_locked_arc(list, |list: &mut MediaList| {
         list.append_medium(&context, new_medium);
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_DeleteMedium(
@@ -3814,16 +3827,17 @@ pub extern "C" fn Servo_MediaList_Delete
 ) -> bool {
     let old_medium = unsafe { old_medium.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new_for_cssom(
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
     write_locked_arc(list, |list: &mut MediaList| list.delete_medium(&context, old_medium))
 }
 
 macro_rules! get_longhand_from_id {
     ($id:expr) => {
         match PropertyId::from_nscsspropertyid($id) {
             Ok(PropertyId::Longhand(long)) => long,
@@ -4205,16 +4219,17 @@ pub extern "C" fn Servo_DeclarationBlock
     let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) };
     let string = unsafe { (*value).to_string() };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
     let url = SpecifiedImageUrl::parse_from_string(string.into(), &context);
     let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(
         vec![Either::Second(Image::Url(url))]
     ));
     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
         decls.push(decl, Importance::Normal, DeclarationSource::CssOm);
     });
@@ -4245,17 +4260,17 @@ pub unsafe extern "C" fn Servo_CSSSuppor
     let mut declarations = SourcePropertyDeclaration::new();
     parse_property_into(
         &mut declarations,
         id,
         value,
         DUMMY_URL_DATA,
         structs::ParsingMode_Default,
         QuirksMode::NoQuirks,
-        &NullReporter,
+        None,
     ).is_ok()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_CSSSupports(cond: *const nsACString) -> bool {
     let condition = unsafe { cond.as_ref().unwrap().as_str_unchecked() };
     let mut input = ParserInput::new(&condition);
     let mut input = Parser::new(&mut input);
@@ -4264,16 +4279,17 @@ pub extern "C" fn Servo_CSSSupports(cond
         let url_data = unsafe { dummy_url_data() };
         // NOTE(emilio): The supports API is not associated to any stylesheet,
         // so the fact that there are no namespace map here is fine.
         let context = ParserContext::new_for_cssom(
             url_data,
             Some(CssRuleType::Style),
             ParsingMode::DEFAULT,
             QuirksMode::NoQuirks,
+            None,
         );
 
         cond.eval(&context)
     } else {
         false
     }
 }
 
@@ -5323,40 +5339,41 @@ pub unsafe extern "C" fn Servo_SelectorL
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_SelectorList_Drop(list: RawServoSelectorListOwned) {
     let _ = list.into_box::<::selectors::SelectorList<SelectorImpl>>();
 }
 
 fn parse_color(
     value: &str,
-    error_reporter: Option<&ErrorReporter>,
+    error_reporter: Option<&ParseErrorReporter>,
 ) -> Result<specified::Color, ()> {
     let mut input = ParserInput::new(value);
     let mut parser = Parser::new(&mut input);
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        error_reporter,
     );
 
     let start_position = parser.position();
     parser.parse_entirely(|i| specified::Color::parse(&context, i)).map_err(|err| {
-        if let Some(error_reporter) = error_reporter {
+        if error_reporter.is_some() {
             match err.kind {
                 ParseErrorKind::Custom(StyleParseErrorKind::ValueError(..)) => {
                     let location = err.location.clone();
                     let error = ContextualParseError::UnsupportedValue(
                         parser.slice_from(start_position),
                         err,
                     );
-                    error_reporter.report(location, error);
+                    context.log_css_error(location, error);
                 }
                 // Ignore other kinds of errors that might be reported, such as
                 // ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken),
                 // since Gecko doesn't report those to the error console.
                 _ => {}
             }
         }
     })
@@ -5380,22 +5397,22 @@ pub extern "C" fn Servo_ComputeColor(
     loader: *mut Loader,
 ) -> bool {
     use style::gecko;
 
     let current_color = gecko::values::convert_nscolor_to_rgba(current_color);
     let value = unsafe { (*value).to_string() };
     let result_color = unsafe { result_color.as_mut().unwrap() };
 
-    let reporter = unsafe { loader.as_mut() }.map(|loader| {
+    let reporter = unsafe { loader.as_mut() }.and_then(|loader| {
         // Make an ErrorReporter that will report errors as being "from DOM".
         ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
     });
 
-    match parse_color(&value, reporter.as_ref()) {
+    match parse_color(&value, reporter.as_ref().map(|r| r as &ParseErrorReporter)) {
         Ok(specified_color) => {
             let computed_color = match raw_data {
                 Some(raw_data) => {
                     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
                     let device = data.stylist.device();
                     let quirks_mode = data.stylist.quirks_mode();
                     Context::for_media_query_evaluation(device, quirks_mode, |context| {
                         specified_color.to_computed_color(Some(&context))
@@ -5437,16 +5454,17 @@ pub unsafe extern "C" fn Servo_Intersect
 
     let url_data = dummy_url_data();
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
 
     let margin = parser.parse_entirely(|p| {
         IntersectionObserverRootMargin::parse(&context, p)
     });
     match margin {
         Ok(margin) => {
             margin.0.to_gecko_rect(result);
@@ -5479,17 +5497,18 @@ pub extern "C" fn Servo_ParseTransformIn
     let string = unsafe { (*value).to_string() };
     let mut input = ParserInput::new(&string);
     let mut parser = Parser::new(&mut input);
     let context = ParserContext::new(
         Origin::Author,
         unsafe { dummy_url_data() },
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
-        QuirksMode::NoQuirks
+        QuirksMode::NoQuirks,
+        None,
     );
 
     let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
         Ok(t) => t,
         Err(..) => return false,
     };
 
     let (m, is_3d) = match transform.to_transform_3d_matrix(None) {
@@ -5522,16 +5541,17 @@ pub extern "C" fn Servo_ParseFontShortha
     let mut parser = Parser::new(&mut input);
     let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::FontFace),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
 
     let font = match parser.parse_entirely(|f| font::parse_value(&context, f)) {
         Ok(f) => f,
         Err(..) => return false,
     };
 
     // The system font is not acceptable, so we return false.
@@ -5579,16 +5599,17 @@ pub unsafe extern "C" fn Servo_SourceSiz
     let mut parser = Parser::new(&mut input);
 
     let context = ParserContext::new(
         Origin::Author,
         dummy_url_data(),
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
 
     // NB: Intentionally not calling parse_entirely.
     let list = SourceSizeList::parse(&context, &mut parser);
     Box::into_raw(Box::new(list)) as *mut _
 }
 
 #[no_mangle]
--- a/servo/ports/geckolib/stylesheet_loader.rs
+++ b/servo/ports/geckolib/stylesheet_loader.rs
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::SourceLocation;
 use nsstring::nsCString;
 use servo_arc::Arc;
 use style::context::QuirksMode;
-use style::error_reporting::NullReporter;
 use style::gecko::data::GeckoStyleSheet;
 use style::gecko::global_style_data::GLOBAL_STYLE_DATA;
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::Gecko_LoadStyleSheet;
 use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
 use style::gecko_bindings::structs::{StyleSheet as DomStyleSheet, SheetLoadData, SheetLoadDataHolder};
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::sugar::ownership::FFIArcHelpers;
@@ -99,20 +98,25 @@ impl AsyncStylesheetParser {
     pub fn parse(self) {
         let global_style_data = &*GLOBAL_STYLE_DATA;
         let input: &str = unsafe { (*self.bytes).as_str_unchecked() };
 
         // Note: Parallel CSS parsing doesn't report CSS errors. When errors
         // are being logged, Gecko prevents the parallel parsing path from
         // running.
         let sheet = Arc::new(StylesheetContents::from_str(
-            input, self.extra_data.clone(), self.origin,
-            &global_style_data.shared_lock, Some(&self), &NullReporter,
-            self.quirks_mode.into(), self.line_number_offset)
-        );
+            input,
+            self.extra_data.clone(),
+            self.origin,
+            &global_style_data.shared_lock,
+            Some(&self),
+            None,
+            self.quirks_mode.into(),
+            self.line_number_offset,
+        ));
 
         unsafe {
             bindings::Gecko_StyleSheet_FinishAsyncParse(self.load_data.get(), sheet.into_strong());
         }
     }
 }
 
 impl StyleStylesheetLoader for AsyncStylesheetParser {