Bug 516091: Split CSS error reporting to its own file and class. Allows CSS scanner to become a stack object. r=dbaron
authorZack Weinberg <zackw@panix.com>
Thu, 15 Nov 2012 11:36:15 -0500
changeset 122036 03b607d3bca6b680ae59d780e064c880752e2e5d
parent 122035 84c7ef253748a99222f1bb071600b8cd059f4cf8
child 122037 0f01d41ab835d4c916f2b92f9db75b3d0ede92ef
push id273
push userlsblakk@mozilla.com
push dateThu, 14 Feb 2013 23:19:38 +0000
treeherdermozilla-release@c5e807a3f8b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs516091
milestone19.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 516091: Split CSS error reporting to its own file and class. Allows CSS scanner to become a stack object. r=dbaron
content/svg/content/src/nsSVGElement.cpp
layout/build/nsLayoutStatics.cpp
layout/style/ErrorReporter.cpp
layout/style/ErrorReporter.h
layout/style/Makefile.in
layout/style/nsCSSParser.cpp
layout/style/nsCSSParser.h
layout/style/nsCSSScanner.cpp
layout/style/nsCSSScanner.h
layout/style/nsStyleAnimation.cpp
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1102,25 +1102,16 @@ private:
 
 MappedAttrParser::MappedAttrParser(css::Loader* aLoader,
                                    nsIURI* aDocURI,
                                    already_AddRefed<nsIURI> aBaseURI,
                                    nsIPrincipal* aNodePrincipal)
   : mParser(aLoader), mDocURI(aDocURI), mBaseURI(aBaseURI),
     mNodePrincipal(aNodePrincipal), mDecl(nullptr)
 {
-  // SVG and CSS differ slightly in their interpretation of some of
-  // the attributes.  SVG allows attributes of the form: font-size="5"
-  // (style="font-size: 5" if using a style attribute)
-  // where CSS requires units: font-size="5pt" (style="font-size: 5pt")
-  // Set a flag to pass information to the parser so that we can use
-  // the CSS parser to parse the font-size attribute.  Note that this
-  // does *not* affect the use of CSS stylesheets, which will still
-  // require units.
-  mParser.SetSVGMode(true);
 }
 
 MappedAttrParser::~MappedAttrParser()
 {
   NS_ABORT_IF_FALSE(!mDecl,
                     "If mDecl was initialized, it should have been converted "
                     "into a style rule (and had its pointer cleared)");
 }
@@ -1136,17 +1127,17 @@ MappedAttrParser::ParseMappedAttrValue(n
 
   // Get the nsCSSProperty ID for our mapped attribute.
   nsCSSProperty propertyID =
     nsCSSProps::LookupProperty(nsDependentAtomString(aMappedAttrName),
                                nsCSSProps::eEnabled);
   if (propertyID != eCSSProperty_UNKNOWN) {
     bool changed; // outparam for ParseProperty. (ignored)
     mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI,
-                          mNodePrincipal, mDecl, &changed, false);
+                          mNodePrincipal, mDecl, &changed, false, true);
     return;
   }
   NS_ABORT_IF_FALSE(aMappedAttrName == nsGkAtoms::lang,
                     "Only 'lang' should be unrecognized!");
   // nsCSSParser doesn't know about 'lang', so we need to handle it specially.
   if (aMappedAttrName == nsGkAtoms::lang) {
     propertyID = eCSSProperty__x_lang;
     nsCSSExpandedDataBlock block;
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -9,23 +9,23 @@
 
 #include "nsAttrValue.h"
 #include "nsAutoCopyListener.h"
 #include "nsColorNames.h"
 #include "nsComputedDOMStyle.h"
 #include "nsContentDLF.h"
 #include "nsContentUtils.h"
 #include "nsCSSAnonBoxes.h"
+#include "mozilla/css/ErrorReporter.h"
 #include "nsCSSKeywords.h"
 #include "nsCSSParser.h"
 #include "nsCSSProps.h"
 #include "nsCSSPseudoClasses.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSRendering.h"
-#include "nsCSSScanner.h"
 #include "nsDOMAttribute.h"
 #include "nsDOMClassInfo.h"
 #include "nsEventListenerManager.h"
 #include "nsFrame.h"
 #include "nsGlobalWindow.h"
 #include "nsGkAtoms.h"
 #include "nsImageFrame.h"
 #include "nsLayoutStylesheetCache.h"
@@ -307,17 +307,17 @@ nsLayoutStatics::Shutdown()
   nsSprocketLayout::Shutdown();
 #endif
 
   nsMathMLOperators::ReleaseTable();
 
   nsFloatManager::Shutdown();
   nsImageFrame::ReleaseGlobals();
 
-  nsCSSScanner::ReleaseGlobals();
+  mozilla::css::ErrorReporter::ReleaseGlobals();
 
   nsTextFragment::Shutdown();
 
   nsAttrValue::Shutdown();
   nsContentUtils::Shutdown();
   nsNodeInfo::ClearCache();
   nsLayoutStylesheetCache::Shutdown();
   NS_NameSpaceManagerShutdown();
new file mode 100644
--- /dev/null
+++ b/layout/style/ErrorReporter.cpp
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* diagnostic reporting for CSS style sheet parser */
+
+#include "mozilla/css/ErrorReporter.h"
+#include "mozilla/css/Loader.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "nsCSSScanner.h"
+#include "nsCSSStyleSheet.h"
+#include "nsIConsoleService.h"
+#include "nsIDocument.h"
+#include "nsIFactory.h"
+#include "nsIScriptError.h"
+#include "nsIServiceManager.h"
+#include "nsIStringBundle.h"
+#include "nsThreadUtils.h"
+
+#ifdef CSS_REPORT_PARSE_ERRORS
+
+using mozilla::Preferences;
+namespace services = mozilla::services;
+
+namespace {
+class ShortTermURISpecCache : public nsRunnable {
+public:
+  ShortTermURISpecCache() : mPending(false) {}
+
+  nsString const& GetSpec(nsIURI* aURI) {
+    if (mURI != aURI) {
+      mURI = aURI;
+
+      nsAutoCString cSpec;
+      mURI->GetSpec(cSpec);
+      CopyUTF8toUTF16(cSpec, mSpec);
+    }
+    return mSpec;
+  }
+
+  bool IsInUse() const { return mURI != nullptr; }
+  bool IsPending() const { return mPending; }
+  void SetPending() { mPending = true; }
+
+  // When invoked as a runnable, zap the cache.
+  NS_IMETHOD Run() {
+    mURI = nullptr;
+    mSpec.Truncate();
+    mPending = false;
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIURI> mURI;
+  nsString mSpec;
+  bool mPending;
+};
+}
+
+static bool sReportErrors;
+static nsIConsoleService *sConsoleService;
+static nsIFactory *sScriptErrorFactory;
+static nsIStringBundle *sStringBundle;
+static ShortTermURISpecCache *sSpecCache;
+
+#define CSS_ERRORS_PREF "layout.css.report_errors"
+
+static bool
+InitGlobals()
+{
+  NS_ABORT_IF_FALSE(!sConsoleService && !sScriptErrorFactory && !sStringBundle,
+                    "should not have been called");
+
+  if (NS_FAILED(Preferences::AddBoolVarCache(&sReportErrors, CSS_ERRORS_PREF,
+                                             true))) {
+    return false;
+  }
+
+  nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+  if (!cs) {
+    return false;
+  }
+
+  nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
+  if (!sf) {
+    return false;
+  }
+
+  nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
+  if (!sbs) {
+    return false;
+  }
+
+  nsCOMPtr<nsIStringBundle> sb;
+  nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties",
+                                  getter_AddRefs(sb));
+  if (NS_FAILED(rv) || !sb) {
+    return false;
+  }
+
+  sConsoleService = cs.forget().get();
+  sScriptErrorFactory = sf.forget().get();
+  sStringBundle = sb.forget().get();
+
+  return true;
+}
+
+static inline bool
+ShouldReportErrors()
+{
+  if (!sConsoleService) {
+    if (!InitGlobals()) {
+      return false;
+    }
+  }
+  return sReportErrors;
+}
+
+namespace mozilla {
+namespace css {
+
+/* static */ void
+ErrorReporter::ReleaseGlobals()
+{
+  NS_IF_RELEASE(sConsoleService);
+  NS_IF_RELEASE(sScriptErrorFactory);
+  NS_IF_RELEASE(sStringBundle);
+  NS_IF_RELEASE(sSpecCache);
+}
+
+ErrorReporter::ErrorReporter(const nsCSSScanner& aScanner,
+                             const nsCSSStyleSheet* aSheet,
+                             const Loader* aLoader,
+                             nsIURI* aURI)
+  : mScanner(&aScanner), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
+    mInnerWindowID(0), mErrorLineNumber(0), mErrorColNumber(0)
+{
+}
+
+ErrorReporter::~ErrorReporter()
+{
+  // Schedule deferred cleanup for cached data. We want to strike a
+  // balance between performance and memory usage, so we only allow
+  // short-term caching.
+  if (sSpecCache && sSpecCache->IsInUse() && !sSpecCache->IsPending()) {
+    if (NS_FAILED(NS_DispatchToCurrentThread(sSpecCache))) {
+      // Peform the "deferred" cleanup immediately if the dispatch fails.
+      sSpecCache->Run();
+    } else {
+      sSpecCache->SetPending();
+    }
+  }
+}
+
+void
+ErrorReporter::OutputError()
+{
+  if (mError.IsEmpty()) {
+    return;
+  }
+  if (!ShouldReportErrors()) {
+    ClearError();
+    return;
+  }
+
+  if (mInnerWindowID == 0 && (mSheet || mLoader)) {
+    if (mSheet) {
+      mInnerWindowID = mSheet->FindOwningWindowInnerID();
+    }
+    if (mInnerWindowID == 0 && mLoader) {
+      nsIDocument* doc = mLoader->GetDocument();
+      if (doc) {
+        mInnerWindowID = doc->InnerWindowID();
+      }
+    }
+    // don't attempt this again, even if we failed
+    mSheet = nullptr;
+    mLoader = nullptr;
+  }
+
+  if (mFileName.IsEmpty()) {
+    if (mURI) {
+      if (!sSpecCache) {
+        sSpecCache = new ShortTermURISpecCache;
+        NS_ADDREF(sSpecCache);
+      }
+      mFileName = sSpecCache->GetSpec(mURI);
+      mURI = nullptr;
+    } else {
+      mFileName.AssignLiteral("from DOM");
+    }
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIScriptError> errorObject =
+    do_CreateInstance(sScriptErrorFactory, &rv);
+
+  if (NS_SUCCEEDED(rv)) {
+    rv = errorObject->InitWithWindowID(mError,
+                                       mFileName,
+                                       EmptyString(),
+                                       mErrorLineNumber,
+                                       mErrorColNumber,
+                                       nsIScriptError::warningFlag,
+                                       "CSS Parser",
+                                       mInnerWindowID);
+    if (NS_SUCCEEDED(rv)) {
+      sConsoleService->LogMessage(errorObject);
+    }
+  }
+
+  ClearError();
+}
+
+void
+ErrorReporter::ClearError()
+{
+  mError.Truncate();
+}
+
+void
+ErrorReporter::AddToError(const nsAString &aErrorText)
+{
+  if (!ShouldReportErrors()) return;
+
+  if (mError.IsEmpty()) {
+    mErrorLineNumber = mScanner->GetLineNumber();
+    mErrorColNumber = mScanner->GetColumnNumber();
+    mError = aErrorText;
+  } else {
+    mError.Append(NS_LITERAL_STRING("  ") + aErrorText);
+  }
+}
+
+void
+ErrorReporter::ReportUnexpected(const char *aMessage)
+{
+  if (!ShouldReportErrors()) return;
+
+  nsAutoString str;
+  sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
+                                   getter_Copies(str));
+  AddToError(str);
+}
+
+void
+ErrorReporter::ReportUnexpectedParams(const char *aMessage,
+                                      const PRUnichar **aParams,
+                                      uint32_t aParamsLength)
+{
+  if (!ShouldReportErrors()) return;
+
+  nsAutoString str;
+  sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
+                                      aParams, aParamsLength,
+                                      getter_Copies(str));
+  AddToError(str);
+}
+
+void
+ErrorReporter::ReportUnexpectedEOF(const char *aMessage)
+{
+  if (!ShouldReportErrors()) return;
+
+  nsAutoString innerStr;
+  sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
+                                   getter_Copies(innerStr));
+  const PRUnichar *params[] = {
+    innerStr.get()
+  };
+
+  nsAutoString str;
+  sStringBundle->FormatStringFromName(NS_LITERAL_STRING("PEUnexpEOF2").get(),
+                                      params, NS_ARRAY_LENGTH(params),
+                                      getter_Copies(str));
+  AddToError(str);
+}
+
+void
+ErrorReporter::ReportUnexpectedEOF(PRUnichar aExpected)
+{
+  if (!ShouldReportErrors()) return;
+
+  const PRUnichar expectedStr[] = {
+    PRUnichar('\''), aExpected, PRUnichar('\''), PRUnichar(0)
+  };
+  const PRUnichar *params[] = { expectedStr };
+
+  nsAutoString str;
+  sStringBundle->FormatStringFromName(NS_LITERAL_STRING("PEUnexpEOF2").get(),
+                                      params, NS_ARRAY_LENGTH(params),
+                                      getter_Copies(str));
+  AddToError(str);
+}
+
+void
+ErrorReporter::ReportUnexpectedToken(const char *aMessage,
+                                     const nsCSSToken &aToken)
+{
+  if (!ShouldReportErrors()) return;
+
+  nsAutoString tokenString;
+  aToken.AppendToString(tokenString);
+  const PRUnichar *params[] = {
+    tokenString.get()
+  };
+
+  nsAutoString str;
+  sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
+                                      params, NS_ARRAY_LENGTH(params),
+                                      getter_Copies(str));
+  AddToError(str);
+}
+
+void
+ErrorReporter::ReportUnexpectedTokenParams(const char *aMessage,
+                                           const nsCSSToken &aToken,
+                                           const PRUnichar **aParams,
+                                           uint32_t aParamsLength)
+{
+  NS_ABORT_IF_FALSE(aParams[0] == nullptr, "first param should be empty");
+
+  if (!ShouldReportErrors()) return;
+
+  nsAutoString tokenString;
+  aToken.AppendToString(tokenString);
+  aParams[0] = tokenString.get();
+
+  nsAutoString str;
+  sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
+                                      aParams, aParamsLength,
+                                      getter_Copies(str));
+  AddToError(str);
+
+}
+
+} // namespace css
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/layout/style/ErrorReporter.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* diagnostic reporting for CSS style sheet parser */
+
+#ifndef mozilla_css_ErrorReporter_h_
+#define mozilla_css_ErrorReporter_h_
+
+// XXX turn this off for minimo builds
+#define CSS_REPORT_PARSE_ERRORS
+
+#include "nsString.h"
+
+struct nsCSSToken;
+class nsCSSStyleSheet;
+class nsCSSScanner;
+class nsIURI;
+
+namespace mozilla {
+namespace css {
+
+class Loader;
+
+// If CSS_REPORT_PARSE_ERRORS is not defined, all of this class's
+// methods become inline stubs.
+class NS_STACK_CLASS ErrorReporter {
+public:
+  ErrorReporter(const nsCSSScanner &aScanner,
+                const nsCSSStyleSheet *aSheet,
+                const Loader *aLoader,
+                nsIURI *aURI);
+  ~ErrorReporter();
+
+  static void ReleaseGlobals();
+
+  void OutputError();
+  void ClearError();
+
+  // aMessage must take no parameters
+  void ReportUnexpected(const char *aMessage);
+  // aLookingFor is a plain string, not a format string
+  void ReportUnexpectedEOF(const char *aLookingFor);
+  // aLookingFor is a single character
+  void ReportUnexpectedEOF(PRUnichar aLookingFor);
+
+  // aMessage is a format string with 1 parameter (for the string
+  // representation of the unexpected token)
+  void ReportUnexpectedToken(const char *aMessage, const nsCSSToken &aToken);
+
+  // aMessage is a format string
+  template<uint32_t N>
+  void ReportUnexpectedParams(const char* aMessage,
+                              const PRUnichar* (&aParams)[N])
+  {
+    MOZ_STATIC_ASSERT(N > 0, "use ReportUnexpected instead");
+    ReportUnexpectedParams(aMessage, aParams, N);
+  }
+
+  // aMessage is a format string
+  // aParams's first entry must be null, and we'll fill in the token
+  template<uint32_t N>
+  void ReportUnexpectedTokenParams(const char* aMessage,
+                                   const nsCSSToken &aToken,
+                                   const PRUnichar* (&aParams)[N])
+  {
+    MOZ_STATIC_ASSERT(N > 1, "use ReportUnexpectedToken instead");
+    ReportUnexpectedTokenParams(aMessage, aToken, aParams, N);
+  }
+
+private:
+  void ReportUnexpectedParams(const char *aMessage,
+                              const PRUnichar **aParams,
+                              uint32_t aParamsLength);
+  void ReportUnexpectedTokenParams(const char *aMessage,
+                                   const nsCSSToken &aToken,
+                                   const PRUnichar **aParams,
+                                   uint32_t aParamsLength);
+
+  void AddToError(const nsAString &aErrorText);
+
+#ifdef CSS_REPORT_PARSE_ERRORS
+  nsAutoString mError;
+  nsString mFileName;
+  const nsCSSScanner *mScanner;
+  const nsCSSStyleSheet *mSheet;
+  const Loader *mLoader;
+  nsIURI *mURI;
+  uint64_t mInnerWindowID;
+  uint32_t mErrorLineNumber;
+  uint32_t mErrorColNumber;
+#endif
+};
+
+#ifndef CSS_REPORT_PARSE_ERRORS
+inline ErrorReporter::ErrorReporter(const nsCSSScanner&,
+                                    const nsCSSStyleSheet*,
+                                    const Loader*,
+                                    nsIURI*) {}
+inline ErrorReporter::~ErrorReporter() {}
+
+inline void ErrorReporter::ReleaseGlobals() {}
+
+inline void ErrorReporter::OutputError() {}
+inline void ErrorReporter::ClearError() {}
+
+inline void ErrorReporter::ReportUnexpected(const char *) {}
+inline void ErrorReporter::ReportUnexpectedParams(const char *,
+                                                  const PRUnichar **,
+                                                  uint32_t) {}
+inline void ErrorReporter::ReportUnexpectedEOF(const char *) {}
+inline void ErrorReporter::ReportUnexpectedEOF(PRUnichar) {}
+inline void ErrorReporter::ReportUnexpectedToken(const char *,
+                                                 const nsCSSToken &) {}
+inline void ErrorReporter::ReportUnexpectedTokenParams(const char *,
+                                                       const nsCSSToken &,
+                                                       const PRUnichar **,
+                                                       uint32_t) {}
+
+inline void ErrorReporter::AddToError(const nsAString &) {}
+#endif
+
+} // namespace css
+} // namespace mozilla
+
+#endif // mozilla_css_ErrorReporter_h_
--- a/layout/style/Makefile.in
+++ b/layout/style/Makefile.in
@@ -66,30 +66,32 @@ EXPORTS		= \
 		nsStyleStructInlines.h \
 		nsStyleStructList.h \
 		nsStyleTransformMatrix.h \
 		nsStyleUtil.h \
 		$(NULL)
 
 EXPORTS_mozilla/css = \
 		Declaration.h \
+		ErrorReporter.h \
 		GroupRule.h \
 		ImageLoader.h \
 		ImportRule.h \
 		Loader.h \
 		NameSpaceRule.h \
 		Rule.h \
 		StyleRule.h \
 		$(NULL)
 
 CPPSRCS		= \
 		AnimationCommon.cpp \
 		nsCSSAnonBoxes.cpp \
 		nsCSSDataBlock.cpp \
 		Declaration.cpp \
+		ErrorReporter.cpp \
 		nsCSSKeywords.cpp \
 		ImageLoader.cpp \
 		Loader.cpp \
 		nsAnimationManager.cpp \
 		nsCSSParser.cpp \
 		nsCSSProps.cpp \
 		nsCSSPseudoClasses.cpp \
 		nsCSSPseudoElements.cpp \
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
 
 #include "nsCSSParser.h"
 #include "nsCSSProps.h"
 #include "nsCSSKeywords.h"
 #include "nsCSSScanner.h"
+#include "mozilla/css/ErrorReporter.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/css/StyleRule.h"
 #include "mozilla/css/ImportRule.h"
 #include "nsCSSRules.h"
 #include "mozilla/css/NameSpaceRule.h"
 #include "nsTArray.h"
 #include "nsCSSStyleSheet.h"
 #include "mozilla/css/Declaration.h"
@@ -156,18 +157,16 @@ class CSSParserImpl {
 public:
   CSSParserImpl();
   ~CSSParserImpl();
 
   nsresult SetStyleSheet(nsCSSStyleSheet* aSheet);
 
   nsresult SetQuirkMode(bool aQuirkMode);
 
-  nsresult SetSVGMode(bool aSVGMode);
-
   nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
 
   // Clears everything set by the above Set*() functions.
   void Reset();
 
   nsresult ParseSheet(const nsAString& aInput,
                       nsIURI*          aSheetURI,
                       nsIURI*          aBaseURI,
@@ -196,17 +195,18 @@ public:
 
   nsresult ParseProperty(const nsCSSProperty aPropID,
                          const nsAString& aPropValue,
                          nsIURI* aSheetURL,
                          nsIURI* aBaseURL,
                          nsIPrincipal* aSheetPrincipal,
                          css::Declaration* aDeclaration,
                          bool* aChanged,
-                         bool aIsImportant);
+                         bool aIsImportant,
+                         bool aIsSVGMode);
 
   nsresult ParseMediaList(const nsSubstring& aBuffer,
                           nsIURI* aURL, // for error reporting
                           uint32_t aLineNumber, // for error reporting
                           nsMediaList* aMediaList,
                           bool aHTMLMode);
 
   bool ParseColorString(const nsSubstring& aBuffer,
@@ -254,41 +254,37 @@ protected:
       {
         mParser->SetParsingCompoundProperty(false);
       }
     private:
       CSSParserImpl* mParser;
   };
 
   // the caller must hold on to aString until parsing is done
-  void InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
-                   uint32_t aLineNumber, nsIURI* aBaseURI,
+  void InitScanner(nsCSSScanner& aScanner,
+                   css::ErrorReporter& aReporter,
+                   nsIURI* aSheetURI, nsIURI* aBaseURI,
                    nsIPrincipal* aSheetPrincipal);
   void ReleaseScanner(void);
   bool IsSVGMode() const {
-    return mScanner.IsSVGMode();
+    return mScanner->IsSVGMode();
   }
 
   bool GetToken(bool aSkipWS);
   void UngetToken();
 
   // get the part in paretheses of the url() function, which is really a
   // part of a token in the CSS grammar, but we're using a combination
   // of the parser and the scanner to do it to handle the backtracking
   // required by the error handling of the tokenization (since if we
   // fail to scan the full token, we should fall back to tokenizing as
   // FUNCTION ... ')').
   // Note that this function WILL WRITE TO aURL IN SOME FAILURE CASES.
   bool GetURLInParens(nsString& aURL);
 
-  void AssertInitialState() {
-    NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
-    NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
-  }
-
   bool ExpectSymbol(PRUnichar aSymbol, bool aSkipWS);
   bool ExpectEndProperty();
   bool CheckEndProperty();
   nsSubstring* NextIdent();
   void SkipUntil(PRUnichar aStopSymbol);
   void SkipUntilOneOf(const PRUnichar* aStopSymbolChars);
   void SkipRuleSet(bool aInsideBraces);
   bool SkipAtRule(bool aInsideBlock);
@@ -630,17 +626,20 @@ protected:
   /* Find the correct default namespace, and set it on aSelector. */
   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
 
   // Current token. The value is valid after calling GetToken and invalidated
   // by UngetToken.
   nsCSSToken mToken;
 
   // Our scanner.
-  nsCSSScanner mScanner;
+  nsCSSScanner* mScanner;
+
+  // Our error reporter.
+  css::ErrorReporter* mReporter;
 
   // The URI to be used as a base for relative URIs.
   nsCOMPtr<nsIURI> mBaseURI;
 
   // The URI to be used as an HTTP "Referer" and for error reporting.
   nsCOMPtr<nsIURI> mSheetURI;
 
   // The principal of the sheet involved
@@ -685,20 +684,16 @@ protected:
   // True for parsing media lists for HTML attributes, where we have to
   // ignore CSS comments.
   bool mHTMLMediaMode : 1;
 
   // This flag is set when parsing a non-box shorthand; it's used to not apply
   // some quirks during shorthand parsing
   bool          mParsingCompoundProperty : 1;
 
-#ifdef DEBUG
-  bool mScannerInited : 1;
-#endif
-
   // Stack of rule groups; used for @media and such.
   InfallibleTArray<nsRefPtr<css::GroupRule> > mGroupStack;
 
   // During the parsing of a property (which may be a shorthand), the data
   // are stored in |mTempData|.  (It is needed to ensure that parser
   // errors cause the data to be ignored, and to ensure that a
   // non-'!important' declaration does not override an '!important'
   // one.)
@@ -718,73 +713,54 @@ static void AppendRuleToArray(css::Rule*
 }
 
 static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
 {
   CSSParserImpl* parser = (CSSParserImpl*) aParser;
   parser->AppendRule(aRule);
 }
 
-#ifdef CSS_REPORT_PARSE_ERRORS
-
 #define REPORT_UNEXPECTED(msg_) \
-  mScanner.ReportUnexpected(#msg_)
+  mReporter->ReportUnexpected(#msg_)
 
 #define REPORT_UNEXPECTED_P(msg_, params_) \
-  mScanner.ReportUnexpectedParams(#msg_, params_)
+  mReporter->ReportUnexpectedParams(#msg_, params_)
 
 #define REPORT_UNEXPECTED_EOF(lf_) \
-  mScanner.ReportUnexpectedEOF(#lf_)
+  mReporter->ReportUnexpectedEOF(#lf_)
 
 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
-  mScanner.ReportUnexpectedEOF(ch_)
+  mReporter->ReportUnexpectedEOF(ch_)
 
 #define REPORT_UNEXPECTED_TOKEN(msg_) \
-  mScanner.ReportUnexpectedToken(mToken, #msg_)
+  mReporter->ReportUnexpectedToken(#msg_, mToken)
 
 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_) \
-  mScanner.ReportUnexpectedTokenParams(mToken, #msg_, \
-                                       params_, ArrayLength(params_))
-
+  mReporter->ReportUnexpectedTokenParams(#msg_, mToken, params_)
 
 #define OUTPUT_ERROR() \
-  mScanner.OutputError()
+  mReporter->OutputError()
 
 #define CLEAR_ERROR() \
-  mScanner.ClearError()
-
-#else
-
-#define REPORT_UNEXPECTED(msg_)
-#define REPORT_UNEXPECTED_P(msg_, params_)
-#define REPORT_UNEXPECTED_EOF(lf_)
-#define REPORT_UNEXPECTED_EOF_CHAR(ch_)
-#define REPORT_UNEXPECTED_TOKEN(msg_)
-#define REPORT_UNEXPECTED_TOKEN_P(msg_, params_)
-#define OUTPUT_ERROR()
-#define CLEAR_ERROR()
-
-#endif
+  mReporter->ClearError()
 
 CSSParserImpl::CSSParserImpl()
   : mToken(),
-    mScanner(),
+    mScanner(nullptr),
+    mReporter(nullptr),
     mChildLoader(nullptr),
     mSection(eCSSSection_Charset),
     mNameSpaceMap(nullptr),
     mHavePushBack(false),
     mNavQuirkMode(false),
     mHashlessColorQuirk(false),
     mUnitlessLengthQuirk(false),
     mUnsafeRulesEnabled(false),
     mHTMLMediaMode(false),
     mParsingCompoundProperty(false),
-#ifdef DEBUG
-    mScannerInited(false),
-#endif
     mNextFree(nullptr)
 {
 }
 
 CSSParserImpl::~CSSParserImpl()
 {
   mData.AssertInitialState();
   mTempData.AssertInitialState();
@@ -812,101 +788,89 @@ CSSParserImpl::SetStyleSheet(nsCSSStyleS
 nsresult
 CSSParserImpl::SetQuirkMode(bool aQuirkMode)
 {
   mNavQuirkMode = aQuirkMode;
   return NS_OK;
 }
 
 nsresult
-CSSParserImpl::SetSVGMode(bool aSVGMode)
-{
-  mScanner.SetSVGMode(aSVGMode);
-  return NS_OK;
-}
-
-nsresult
 CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
 {
   mChildLoader = aChildLoader;  // not ref counted, it owns us
   return NS_OK;
 }
 
 void
 CSSParserImpl::Reset()
 {
-  NS_ASSERTION(! mScannerInited, "resetting with scanner active");
+  NS_ASSERTION(!mScanner, "resetting with scanner active");
   SetStyleSheet(nullptr);
   SetQuirkMode(false);
-  SetSVGMode(false);
   SetChildLoader(nullptr);
 }
 
 void
-CSSParserImpl::InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
-                           uint32_t aLineNumber, nsIURI* aBaseURI,
+CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
+                           css::ErrorReporter& aReporter,
+                           nsIURI* aSheetURI, nsIURI* aBaseURI,
                            nsIPrincipal* aSheetPrincipal)
 {
-  // Having it not own the string is OK since the caller will hold on to
-  // the stream until we're done parsing.
-  NS_ASSERTION(! mScannerInited, "already have scanner");
-
-  mScanner.Init(aString, aSheetURI, aLineNumber, mSheet, mChildLoader);
-
-#ifdef DEBUG
-  mScannerInited = true;
-#endif
+  NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
+  NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
+  NS_PRECONDITION(!mScanner, "already have scanner");
+
+  mScanner = &aScanner;
+  mReporter = &aReporter;
+  mScanner->SetErrorReporter(mReporter);
+
   mBaseURI = aBaseURI;
   mSheetURI = aSheetURI;
   mSheetPrincipal = aSheetPrincipal;
-
   mHavePushBack = false;
 }
 
 void
-CSSParserImpl::ReleaseScanner(void)
-{
-  mScanner.Close();
-#ifdef DEBUG
-  mScannerInited = false;
-#endif
+CSSParserImpl::ReleaseScanner()
+{
+  mScanner = nullptr;
+  mReporter = nullptr;
   mBaseURI = nullptr;
   mSheetURI = nullptr;
   mSheetPrincipal = nullptr;
 }
 
 nsresult
 CSSParserImpl::ParseSheet(const nsAString& aInput,
                           nsIURI*          aSheetURI,
                           nsIURI*          aBaseURI,
                           nsIPrincipal*    aSheetPrincipal,
                           uint32_t         aLineNumber,
                           bool             aAllowUnsafeRules)
 {
   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
-
-  NS_ASSERTION(nullptr != aBaseURI, "need base URI");
-  NS_ASSERTION(nullptr != aSheetURI, "need sheet URI");
-  AssertInitialState();
-
+  NS_PRECONDITION(aBaseURI, "need base URI");
+  NS_PRECONDITION(aSheetURI, "need sheet URI");
   NS_PRECONDITION(mSheet, "Must have sheet to parse into");
   NS_ENSURE_STATE(mSheet);
 
 #ifdef DEBUG
   nsIURI* uri = mSheet->GetSheetURI();
   bool equal;
   NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
                "Sheet URI does not match passed URI");
   NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
                                                         &equal)) &&
                equal,
                "Sheet principal does not match passed principal");
 #endif
 
-  InitScanner(aInput, aSheetURI, aLineNumber, aBaseURI, aSheetPrincipal);
+  nsCSSScanner scanner(aInput, aLineNumber);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
+  InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
 
   int32_t ruleCount = mSheet->StyleRuleCount();
   if (0 < ruleCount) {
     css::Rule* lastRule = nullptr;
     mSheet->GetStyleRuleAt(ruleCount - 1, lastRule);
     if (lastRule) {
       switch (lastRule->GetType()) {
         case css::Rule::CHARSET_RULE:
@@ -972,22 +936,22 @@ NonMozillaVendorIdentifier(const nsAStri
 nsresult
 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
                                    nsIURI*          aDocURI,
                                    nsIURI*          aBaseURI,
                                    nsIPrincipal*    aNodePrincipal,
                                    css::StyleRule** aResult)
 {
   NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
-  AssertInitialState();
-
-  NS_ASSERTION(nullptr != aBaseURI, "need base URI");
+  NS_PRECONDITION(aBaseURI, "need base URI");
 
   // XXX line number?
-  InitScanner(aAttributeValue, aDocURI, 0, aBaseURI, aNodePrincipal);
+  nsCSSScanner scanner(aAttributeValue, 0);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
+  InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
 
   mSection = eCSSSection_General;
 
   // In quirks mode, allow style declarations to have braces or not
   // (bug 99554).
   bool haveBraces;
   if (mNavQuirkMode && GetToken(true)) {
     haveBraces = eCSSToken_Symbol == mToken.mType &&
@@ -1020,22 +984,23 @@ CSSParserImpl::ParseStyleAttribute(const
 nsresult
 CSSParserImpl::ParseDeclarations(const nsAString&  aBuffer,
                                  nsIURI*           aSheetURI,
                                  nsIURI*           aBaseURI,
                                  nsIPrincipal*     aSheetPrincipal,
                                  css::Declaration* aDeclaration,
                                  bool*           aChanged)
 {
+  *aChanged = false;
+
   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
-  AssertInitialState();
-
-  *aChanged = false;
-
-  InitScanner(aBuffer, aSheetURI, 0, aBaseURI, aSheetPrincipal);
+
+  nsCSSScanner scanner(aBuffer, 0);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
+  InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
 
   mSection = eCSSSection_General;
 
   mData.AssertInitialState();
   aDeclaration->ClearData();
   // We could check if it was already empty, but...
   *aChanged = true;
 
@@ -1058,21 +1023,21 @@ CSSParserImpl::ParseDeclarations(const n
 nsresult
 CSSParserImpl::ParseRule(const nsAString&        aRule,
                          nsIURI*                 aSheetURI,
                          nsIURI*                 aBaseURI,
                          nsIPrincipal*           aSheetPrincipal,
                          nsCOMArray<css::Rule>&  aResult)
 {
   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
-  AssertInitialState();
-
-  NS_ASSERTION(nullptr != aBaseURI, "need base URI");
-
-  InitScanner(aRule, aSheetURI, 0, aBaseURI, aSheetPrincipal);
+  NS_PRECONDITION(aBaseURI, "need base URI");
+
+  nsCSSScanner scanner(aRule, 0);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
+  InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
 
   mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
 
   nsCSSToken* tk = &mToken;
   // Get first non-whitespace token
   if (!GetToken(true)) {
     REPORT_UNEXPECTED(PEParseRuleWSOnly);
     OUTPUT_ERROR();
@@ -1096,28 +1061,32 @@ CSSParserImpl::ParseRule(const nsAString
 nsresult
 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
                              const nsAString& aPropValue,
                              nsIURI* aSheetURI,
                              nsIURI* aBaseURI,
                              nsIPrincipal* aSheetPrincipal,
                              css::Declaration* aDeclaration,
                              bool* aChanged,
-                             bool aIsImportant)
+                             bool aIsImportant,
+                             bool aIsSVGMode)
 {
   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
   NS_PRECONDITION(aBaseURI, "need base URI");
   NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
-  AssertInitialState();
+
   mData.AssertInitialState();
   mTempData.AssertInitialState();
   aDeclaration->AssertMutable();
 
-  InitScanner(aPropValue, aSheetURI, 0, aBaseURI, aSheetPrincipal);
+  nsCSSScanner scanner(aPropValue, 0);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
+  InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
   mSection = eCSSSection_General;
+  scanner.SetSVGMode(aIsSVGMode);
 
   *aChanged = false;
 
   // Check for unknown or preffed off properties
   if (eCSSProperty_UNKNOWN == aPropID || !nsCSSProps::IsEnabled(aPropID)) {
     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
     const PRUnichar *params[] = {
       propName.get()
@@ -1180,19 +1149,20 @@ CSSParserImpl::ParseMediaList(const nsSu
                               nsMediaList* aMediaList,
                               bool aHTMLMode)
 {
   // XXX Are there cases where the caller wants to keep what it already
   // has in case of parser error?
   aMediaList->Clear();
 
   // fake base URI since media lists don't have URIs in them
-  InitScanner(aBuffer, aURI, aLineNumber, aURI, nullptr);
-
-  AssertInitialState();
+  nsCSSScanner scanner(aBuffer, aLineNumber);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
+  InitScanner(scanner, reporter, aURI, aURI, nullptr);
+
   mHTMLMediaMode = aHTMLMode;
 
     // XXXldb We need to make the scanner not skip CSS comments!  (Or
     // should we?)
 
   // For aHTMLMode, we used to follow the parsing rules in
   // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
   // which wouldn't work for media queries since they remove all but the
@@ -1212,35 +1182,36 @@ CSSParserImpl::ParseMediaList(const nsSu
 }
 
 bool
 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
                                 nsIURI* aURI, // for error reporting
                                 uint32_t aLineNumber, // for error reporting
                                 nsCSSValue& aValue)
 {
-  AssertInitialState();
-  InitScanner(aBuffer, aURI, aLineNumber, aURI, nullptr);
+  nsCSSScanner scanner(aBuffer, aLineNumber);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
+  InitScanner(scanner, reporter, aURI, aURI, nullptr);
 
   // Parse a color, and check that there's nothing else after it.
   bool colorParsed = ParseColor(aValue) && !GetToken(true);
   OUTPUT_ERROR();
   ReleaseScanner();
   return colorParsed;
 }
 
 nsresult
 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
                                    nsIURI* aURI, // for error reporting
                                    uint32_t aLineNumber, // for error reporting
                                    nsCSSSelectorList **aSelectorList)
 {
-  InitScanner(aSelectorString, aURI, aLineNumber, aURI, nullptr);
-
-  AssertInitialState();
+  nsCSSScanner scanner(aSelectorString, aLineNumber);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
+  InitScanner(scanner, reporter, aURI, aURI, nullptr);
 
   bool success = ParseSelectorList(*aSelectorList, PRUnichar(0));
 
   // We deliberately do not call OUTPUT_ERROR here, because all our
   // callers map a failure return to a JS exception, and if that JS
   // exception is caught, people don't want to see parser diagnostics;
   // see e.g. http://bugs.jquery.com/ticket/7535
   // It would be nice to be able to save the parser diagnostics into
@@ -1261,19 +1232,19 @@ CSSParserImpl::ParseSelectorString(const
 }
 
 
 already_AddRefed<nsCSSKeyframeRule>
 CSSParserImpl::ParseKeyframeRule(const nsSubstring&  aBuffer,
                                  nsIURI*             aURI,
                                  uint32_t            aLineNumber)
 {
-  InitScanner(aBuffer, aURI, aLineNumber, aURI, nullptr);
-
-  AssertInitialState();
+  nsCSSScanner scanner(aBuffer, aLineNumber);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
+  InitScanner(scanner, reporter, aURI, aURI, nullptr);
 
   nsRefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
   if (GetToken(true)) {
     // extra garbage at the end
     result = nullptr;
   }
 
   OUTPUT_ERROR();
@@ -1285,19 +1256,19 @@ CSSParserImpl::ParseKeyframeRule(const n
 bool
 CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
                                            nsIURI* aURI, // for error reporting
                                            uint32_t aLineNumber, // for error reporting
                                            InfallibleTArray<float>& aSelectorList)
 {
   NS_ABORT_IF_FALSE(aSelectorList.IsEmpty(), "given list should start empty");
 
-  InitScanner(aSelectorString, aURI, aLineNumber, aURI, nullptr);
-
-  AssertInitialState();
+  nsCSSScanner scanner(aSelectorString, aLineNumber);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
+  InitScanner(scanner, reporter, aURI, aURI, nullptr);
 
   bool success = ParseKeyframeSelectorList(aSelectorList) &&
                  // must consume entire input string
                  !GetToken(true);
 
   OUTPUT_ERROR();
   ReleaseScanner();
 
@@ -1312,34 +1283,34 @@ CSSParserImpl::ParseKeyframeSelectorStri
 
 //----------------------------------------------------------------------
 
 bool
 CSSParserImpl::GetToken(bool aSkipWS)
 {
   for (;;) {
     if (!mHavePushBack) {
-      if (!mScanner.Next(mToken)) {
+      if (!mScanner->Next(mToken)) {
         break;
       }
     }
     mHavePushBack = false;
     if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
       continue;
     }
     return true;
   }
   return false;
 }
 
 bool
 CSSParserImpl::GetURLInParens(nsString& aURL)
 {
   NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
-  if (! mScanner.NextURL(mToken)) {
+  if (! mScanner->NextURL(mToken)) {
     // EOF
     return false;
   }
 
   aURL = mToken.mIdent;
 
   if (eCSSToken_URL != mToken.mType) {
     // In the failure case (which gives a token of type
@@ -2386,32 +2357,32 @@ CSSParserImpl::ParseKeyframeSelectorList
 //   : "@supports" supports_condition group_rule_body
 //   ;
 bool
 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
 {
   bool conditionMet = false;
   nsString condition;
 
-  mScanner.StartRecording();
+  mScanner->StartRecording();
   bool parsed = ParseSupportsCondition(conditionMet);
 
   if (!parsed) {
-    mScanner.StopRecording();
+    mScanner->StopRecording();
     return false;
   }
 
   if (!ExpectSymbol('{', true)) {
     REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
-    mScanner.StopRecording();
+    mScanner->StopRecording();
     return false;
   }
 
   UngetToken();
-  mScanner.StopRecording(condition);
+  mScanner->StopRecording(condition);
 
   // Remove the "{" that would follow the condition.
   if (condition.Length() != 0) {
     condition.Truncate(condition.Length() - 1);
   }
 
   // Remove spaces from the start and end of the recorded supports condition.
   condition.Trim(" ", true, true, false);
@@ -2772,17 +2743,17 @@ CSSParserImpl::AppendRule(css::Rule* aRu
 }
 
 bool
 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
                             bool aInsideBraces)
 {
   // First get the list of selectors for the rule
   nsCSSSelectorList* slist = nullptr;
-  uint32_t linenum = mScanner.GetLineNumber();
+  uint32_t linenum = mScanner->GetLineNumber();
   if (! ParseSelectorList(slist, PRUnichar('{'))) {
     REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
     OUTPUT_ERROR();
     SkipRuleSet(aInsideBraces);
     return false;
   }
   NS_ASSERTION(nullptr != slist, "null selector list");
   CLEAR_ERROR();
@@ -3677,17 +3648,17 @@ CSSParserImpl::ParsePseudoClassWithNthPa
     uint32_t truncAt = 0;
     if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
       truncAt = 1;
     } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-"))) {
       truncAt = 2;
     }
     if (truncAt != 0) {
       for (uint32_t i = mToken.mIdent.Length() - 1; i >= truncAt; --i) {
-        mScanner.Pushback(mToken.mIdent[i]);
+        mScanner->Pushback(mToken.mIdent[i]);
       }
       mToken.mIdent.Truncate(truncAt);
     }
   }
 
   if (eCSSToken_Ident == mToken.mType) {
     if (mToken.mIdent.LowerCaseEqualsLiteral("odd")) {
       numbers[0] = 2;
@@ -10044,23 +10015,16 @@ nsCSSParser::SetStyleSheet(nsCSSStyleShe
 nsresult
 nsCSSParser::SetQuirkMode(bool aQuirkMode)
 {
   return static_cast<CSSParserImpl*>(mImpl)->
     SetQuirkMode(aQuirkMode);
 }
 
 nsresult
-nsCSSParser::SetSVGMode(bool aSVGMode)
-{
-  return static_cast<CSSParserImpl*>(mImpl)->
-    SetSVGMode(aSVGMode);
-}
-
-nsresult
 nsCSSParser::SetChildLoader(mozilla::css::Loader* aChildLoader)
 {
   return static_cast<CSSParserImpl*>(mImpl)->
     SetChildLoader(aChildLoader);
 }
 
 nsresult
 nsCSSParser::ParseSheet(const nsAString& aInput,
@@ -10113,22 +10077,24 @@ nsCSSParser::ParseRule(const nsAString& 
 
 nsresult
 nsCSSParser::ParseProperty(const nsCSSProperty aPropID,
                            const nsAString&    aPropValue,
                            nsIURI*             aSheetURI,
                            nsIURI*             aBaseURI,
                            nsIPrincipal*       aSheetPrincipal,
                            css::Declaration*   aDeclaration,
-                           bool*             aChanged,
-                           bool                aIsImportant)
+                           bool*               aChanged,
+                           bool                aIsImportant,
+                           bool                aIsSVGMode)
 {
   return static_cast<CSSParserImpl*>(mImpl)->
     ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
-                  aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
+                  aSheetPrincipal, aDeclaration, aChanged,
+                  aIsImportant, aIsSVGMode);
 }
 
 nsresult
 nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
                             nsIURI*            aURI,
                             uint32_t           aLineNumber,
                             nsMediaList*       aMediaList,
                             bool               aHTMLMode)
--- a/layout/style/nsCSSParser.h
+++ b/layout/style/nsCSSParser.h
@@ -52,19 +52,16 @@ public:
   // Set a style sheet for the parser to fill in. The style sheet must
   // implement the nsCSSStyleSheet interface.  Null can be passed in to clear
   // out an existing stylesheet reference.
   nsresult SetStyleSheet(nsCSSStyleSheet* aSheet);
 
   // Set whether or not to emulate Nav quirks
   nsresult SetQuirkMode(bool aQuirkMode);
 
-  // Set whether or not we are in an SVG element
-  nsresult SetSVGMode(bool aSVGMode);
-
   // Set loader to use for child sheets
   nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
 
   /**
    * Parse aInput into the stylesheet that was previously set by calling
    * SetStyleSheet.  Calling this method without calling SetStyleSheet first is
    * an error.
    *
@@ -108,24 +105,33 @@ public:
                              bool*           aChanged);
 
   nsresult ParseRule(const nsAString&        aRule,
                      nsIURI*                 aSheetURL,
                      nsIURI*                 aBaseURL,
                      nsIPrincipal*           aSheetPrincipal,
                      nsCOMArray<mozilla::css::Rule>& aResult);
 
+  // Parse the value of a single CSS property, and add or replace that
+  // property in aDeclaration.
+  //
+  // SVG "mapped attributes" (which correspond directly to CSS
+  // properties) are parsed slightly differently from regular CSS; in
+  // particular, units may be omitted from <length>.  The 'aIsSVGMode'
+  // argument controls this quirk.  Note that this *only* applies to
+  // mapped attributes, not inline styles or full style sheets in SVG.
   nsresult ParseProperty(const nsCSSProperty aPropID,
                          const nsAString&    aPropValue,
                          nsIURI*             aSheetURL,
                          nsIURI*             aBaseURL,
                          nsIPrincipal*       aSheetPrincipal,
                          mozilla::css::Declaration* aDeclaration,
-                         bool*             aChanged,
-                         bool                aIsImportant);
+                         bool*               aChanged,
+                         bool                aIsImportant,
+                         bool                aIsSVGMode = false);
 
   /**
    * Parse aBuffer into a media list |aMediaList|, which must be
    * non-null, replacing its current contents.  If aHTMLMode is true,
    * parse according to HTML rules, with commas as the most important
    * delimiter.  Otherwise, parse according to CSS rules, with
    * parentheses and strings more important than commas.  |aURL| and
    * |aLineNumber| are used for error reporting.
--- a/layout/style/nsCSSScanner.cpp
+++ b/layout/style/nsCSSScanner.cpp
@@ -4,44 +4,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 /* tokenization of CSS style sheets */
 
 #include <math.h> // must be first due to symbol conflicts
 
 #include "nsCSSScanner.h"
-#include "nsString.h"
-#include "nsCRT.h"
+#include "mozilla/css/ErrorReporter.h"
+#include "mozilla/Likely.h"
 #include "mozilla/Util.h"
 
-// for #ifdef CSS_REPORT_PARSE_ERRORS
-#include "nsCOMPtr.h"
-#include "nsIServiceManager.h"
-#include "nsIComponentManager.h"
-#include "nsReadableUtils.h"
-#include "nsIURI.h"
-#include "nsIConsoleService.h"
-#include "nsIScriptError.h"
-#include "nsIStringBundle.h"
-#include "nsIDocument.h"
-#include "mozilla/Services.h"
-#include "mozilla/css/Loader.h"
-#include "nsCSSStyleSheet.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/Likely.h"
-
-using namespace mozilla;
-
-#ifdef CSS_REPORT_PARSE_ERRORS
-static bool gReportErrors = true;
-static nsIConsoleService *gConsoleService;
-static nsIFactory *gScriptErrorFactory;
-static nsIStringBundle *gStringBundle;
-#endif
+using mozilla::ArrayLength;
 
 static const uint8_t IS_HEX_DIGIT  = 0x01;
 static const uint8_t START_IDENT   = 0x02;
 static const uint8_t IS_IDENT      = 0x04;
 static const uint8_t IS_WHITESPACE = 0x08;
 static const uint8_t IS_URL_CHAR   = 0x10;
 
 #define W    IS_WHITESPACE
@@ -159,17 +136,17 @@ HexDigitValue(int32_t ch)
 }
 
 nsCSSToken::nsCSSToken()
 {
   mType = eCSSToken_Symbol;
 }
 
 void
-nsCSSToken::AppendToString(nsString& aBuffer)
+nsCSSToken::AppendToString(nsString& aBuffer) const
 {
   switch (mType) {
     case eCSSToken_AtKeyword:
       aBuffer.Append(PRUnichar('@')); // fall through intentional
     case eCSSToken_Ident:
     case eCSSToken_WhiteSpace:
     case eCSSToken_Function:
     case eCSSToken_HTMLComment:
@@ -245,400 +222,41 @@ nsCSSToken::AppendToString(nsString& aBu
       aBuffer.Append(mIdent);
       break;
     default:
       NS_ERROR("invalid token type");
       break;
   }
 }
 
-class DeferredCleanupRunnable : public nsRunnable
-{
-public:
-  DeferredCleanupRunnable(nsCSSScanner* aToClean)
-    : mToClean(aToClean)
-  {}
-
-  NS_IMETHOD Run() {
-    if (mToClean) {
-      mToClean->PerformDeferredCleanup();
-    }
-
-    return NS_OK;
-  }
-
-  void Revoke() {
-    mToClean = nullptr;
-  }
-
-private:
-  nsCSSScanner* mToClean;
-};
-
-nsCSSScanner::nsCSSScanner()
-  : mReadPointer(nullptr)
+nsCSSScanner::nsCSSScanner(const nsAString& aBuffer, uint32_t aLineNumber)
+  : mReadPointer(aBuffer.BeginReading())
+  , mOffset(0)
+  , mCount(aBuffer.Length())
+  , mPushback(mLocalPushback)
+  , mPushbackCount(0)
+  , mPushbackSize(ArrayLength(mLocalPushback))
+  , mLineNumber(aLineNumber)
+  , mLineOffset(0)
+  , mRecordStartOffset(0)
+  , mReporter(nullptr)
   , mSVGMode(false)
-#ifdef CSS_REPORT_PARSE_ERRORS
-  , mError(mErrorBuf, ArrayLength(mErrorBuf), 0)
-  , mInnerWindowID(0)
-  , mWindowIDCached(false)
-  , mSheet(nullptr)
-  , mLoader(nullptr)
-#endif
+  , mRecording(false)
 {
   MOZ_COUNT_CTOR(nsCSSScanner);
-  mPushback = mLocalPushback;
-  mPushbackSize = ArrayLength(mLocalPushback);
-  // No need to init the other members, since they represent state
-  // which can get cleared.  We'll init them every time Init() is
-  // called.
 }
 
 nsCSSScanner::~nsCSSScanner()
 {
   MOZ_COUNT_DTOR(nsCSSScanner);
-  Reset();
   if (mLocalPushback != mPushback) {
     delete [] mPushback;
   }
 }
 
-#ifdef CSS_REPORT_PARSE_ERRORS
-void
-nsCSSScanner::PerformDeferredCleanup()
-{
-  // Clean up all short term caches.
-  mCachedURI = nullptr;
-  mCachedFileName.Truncate();
-
-  // Release our DeferredCleanupRunnable.
-  mDeferredCleaner.Forget();
-}
-
-#define CSS_ERRORS_PREF "layout.css.report_errors"
-
-static int
-CSSErrorsPrefChanged(const char *aPref, void *aClosure)
-{
-  gReportErrors = Preferences::GetBool(CSS_ERRORS_PREF, true);
-  return 0;
-}
-#endif
-
-/* static */ bool
-nsCSSScanner::InitGlobals()
-{
-#ifdef CSS_REPORT_PARSE_ERRORS
-  if (gConsoleService && gScriptErrorFactory)
-    return true;
-  
-  nsresult rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &gConsoleService);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  rv = CallGetClassObject(NS_SCRIPTERROR_CONTRACTID, &gScriptErrorFactory);
-  NS_ENSURE_SUCCESS(rv, false);
-  NS_ASSERTION(gConsoleService && gScriptErrorFactory,
-               "unexpected null pointer without failure");
-
-  Preferences::RegisterCallback(CSSErrorsPrefChanged, CSS_ERRORS_PREF);
-  CSSErrorsPrefChanged(CSS_ERRORS_PREF, nullptr);
-#endif
-  return true;
-}
-
-/* static */ void
-nsCSSScanner::ReleaseGlobals()
-{
-#ifdef CSS_REPORT_PARSE_ERRORS
-  Preferences::UnregisterCallback(CSSErrorsPrefChanged, CSS_ERRORS_PREF);
-  NS_IF_RELEASE(gConsoleService);
-  NS_IF_RELEASE(gScriptErrorFactory);
-  NS_IF_RELEASE(gStringBundle);
-#endif
-}
-
-void
-nsCSSScanner::Init(const nsAString& aBuffer,
-                   nsIURI* aURI, uint32_t aLineNumber,
-                   nsCSSStyleSheet* aSheet, mozilla::css::Loader* aLoader)
-{
-  NS_PRECONDITION(!mReadPointer, "Should not have an existing input buffer!");
-
-  mReadPointer = aBuffer.BeginReading();
-  mCount = aBuffer.Length();
-
-#ifdef CSS_REPORT_PARSE_ERRORS
-  // If aURI is different from mCachedURI, invalidate the filename cache.
-  if (aURI != mCachedURI) {
-    mCachedURI = aURI;
-    mCachedFileName.Truncate();
-  }
-#endif // CSS_REPORT_PARSE_ERRORS
-
-  mLineNumber = aLineNumber;
-
-  // Reset variables that we use to keep track of our progress through the input
-  mOffset = 0;
-  mPushbackCount = 0;
-  mRecording = false;
-
-#ifdef CSS_REPORT_PARSE_ERRORS
-  mColNumber = 0;
-  mSheet = aSheet;
-  mLoader = aLoader;
-#endif
-}
-
-#ifdef CSS_REPORT_PARSE_ERRORS
-
-// @see REPORT_UNEXPECTED_EOF in nsCSSParser.cpp
-#define REPORT_UNEXPECTED_EOF(lf_) \
-  ReportUnexpectedEOF(#lf_)
-
-void
-nsCSSScanner::AddToError(const nsSubstring& aErrorText)
-{
-  if (mError.IsEmpty()) {
-    mErrorLineNumber = mLineNumber;
-    mErrorColNumber = mColNumber;
-    mError = aErrorText;
-  } else {
-    mError.Append(NS_LITERAL_STRING("  ") + aErrorText);
-  }
-}
-
-void
-nsCSSScanner::ClearError()
-{
-  mError.Truncate();
-}
-
-void
-nsCSSScanner::OutputError()
-{
-  if (mError.IsEmpty()) return;
- 
-  // Log it to the Error console
-
-  if (InitGlobals() && gReportErrors) {
-    if (!mWindowIDCached) {
-      if (mSheet) {
-        mInnerWindowID = mSheet->FindOwningWindowInnerID();
-      }
-      if (mInnerWindowID == 0 && mLoader) {
-        nsIDocument* doc = mLoader->GetDocument();
-        if (doc) {
-          mInnerWindowID = doc->InnerWindowID();
-        }
-      }
-      mWindowIDCached = true;
-    }
-
-    nsresult rv;
-    nsCOMPtr<nsIScriptError> errorObject =
-      do_CreateInstance(gScriptErrorFactory, &rv);
-
-    if (NS_SUCCEEDED(rv)) {
-      // Update the cached filename if needed.
-      if (mCachedFileName.IsEmpty()) {
-        if (mCachedURI) {
-          nsAutoCString cFileName;
-          mCachedURI->GetSpec(cFileName);
-          CopyUTF8toUTF16(cFileName, mCachedFileName);
-        } else {
-          mCachedFileName.AssignLiteral("from DOM");
-        }
-      }
-
-      rv = errorObject->InitWithWindowID(mError,
-                                         mCachedFileName,
-                                         EmptyString(),
-                                         mErrorLineNumber,
-                                         mErrorColNumber,
-                                         nsIScriptError::warningFlag,
-                                         "CSS Parser",
-                                         mInnerWindowID);
-      if (NS_SUCCEEDED(rv)) {
-        gConsoleService->LogMessage(errorObject);
-      }
-    }
-  }
-  ClearError();
-}
-
-static bool
-InitStringBundle()
-{
-  if (gStringBundle)
-    return true;
-
-  nsCOMPtr<nsIStringBundleService> sbs =
-    mozilla::services::GetStringBundleService();
-  if (!sbs)
-    return false;
-
-  nsresult rv = 
-    sbs->CreateBundle("chrome://global/locale/css.properties", &gStringBundle);
-  if (NS_FAILED(rv)) {
-    gStringBundle = nullptr;
-    return false;
-  }
-
-  return true;
-}
-
-#define ENSURE_STRINGBUNDLE \
-  PR_BEGIN_MACRO if (!InitStringBundle()) return; PR_END_MACRO
-
-// aMessage must take no parameters
-void nsCSSScanner::ReportUnexpected(const char* aMessage)
-{
-  ENSURE_STRINGBUNDLE;
-
-  nsXPIDLString str;
-  gStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
-                                   getter_Copies(str));
-  AddToError(str);
-}
-  
-void
-nsCSSScanner::ReportUnexpectedParams(const char* aMessage,
-                                     const PRUnichar **aParams,
-                                     uint32_t aParamsLength)
-{
-  NS_PRECONDITION(aParamsLength > 0, "use the non-params version");
-  ENSURE_STRINGBUNDLE;
-
-  nsXPIDLString str;
-  gStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
-                                      aParams, aParamsLength,
-                                      getter_Copies(str));
-  AddToError(str);
-}
-
-// aLookingFor is a plain string, not a format string
-void
-nsCSSScanner::ReportUnexpectedEOF(const char* aLookingFor)
-{
-  ENSURE_STRINGBUNDLE;
-
-  nsXPIDLString innerStr;
-  gStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aLookingFor).get(),
-                                   getter_Copies(innerStr));
-
-  const PRUnichar *params[] = {
-    innerStr.get()
-  };
-  nsXPIDLString str;
-  gStringBundle->FormatStringFromName(NS_LITERAL_STRING("PEUnexpEOF2").get(),
-                                      params, ArrayLength(params),
-                                      getter_Copies(str));
-  AddToError(str);
-}
-
-// aLookingFor is a single character
-void
-nsCSSScanner::ReportUnexpectedEOF(PRUnichar aLookingFor)
-{
-  ENSURE_STRINGBUNDLE;
-
-  const PRUnichar lookingForStr[] = {
-    PRUnichar('\''), aLookingFor, PRUnichar('\''), PRUnichar(0)
-  };
-  const PRUnichar *params[] = { lookingForStr };
-  nsXPIDLString str;
-  gStringBundle->FormatStringFromName(NS_LITERAL_STRING("PEUnexpEOF2").get(),
-                                      params, ArrayLength(params),
-                                      getter_Copies(str));
-  AddToError(str);
-}
-
-// aMessage must take 1 parameter (for the string representation of the
-// unexpected token)
-void
-nsCSSScanner::ReportUnexpectedToken(nsCSSToken& tok,
-                                    const char *aMessage)
-{
-  ENSURE_STRINGBUNDLE;
-  
-  nsAutoString tokenString;
-  tok.AppendToString(tokenString);
-
-  const PRUnichar *params[] = {
-    tokenString.get()
-  };
-
-  ReportUnexpectedParams(aMessage, params);
-}
-
-// aParams's first entry must be null, and we'll fill in the token
-void
-nsCSSScanner::ReportUnexpectedTokenParams(nsCSSToken& tok,
-                                          const char* aMessage,
-                                          const PRUnichar **aParams,
-                                          uint32_t aParamsLength)
-{
-  NS_PRECONDITION(aParamsLength > 1, "use the non-params version");
-  NS_PRECONDITION(aParams[0] == nullptr, "first param should be empty");
-
-  ENSURE_STRINGBUNDLE;
-  
-  nsAutoString tokenString;
-  tok.AppendToString(tokenString);
-  aParams[0] = tokenString.get();
-
-  ReportUnexpectedParams(aMessage, aParams, aParamsLength);
-}
-
-#else
-
-#define REPORT_UNEXPECTED_EOF(lf_)
-
-#endif // CSS_REPORT_PARSE_ERRORS
-
-void
-nsCSSScanner::Close()
-{
-  Reset();
-
-  // Schedule deferred cleanup for cached data. We want to strike a balance
-  // between performance and memory usage, so we only allow short-term caching.
-#ifdef CSS_REPORT_PARSE_ERRORS
-  if (!mDeferredCleaner.IsPending()) {
-    mDeferredCleaner = new DeferredCleanupRunnable(this);
-    if (NS_FAILED(NS_DispatchToCurrentThread(mDeferredCleaner.get()))) {
-      // Peform the "deferred" cleanup immediately if the dispatch fails.
-      // This will also have the effect of clearing mDeferredCleaner.
-      nsCSSScanner::PerformDeferredCleanup();
-    }
-  }
-#endif
-}
-
-void
-nsCSSScanner::Reset()
-{
-  mReadPointer = nullptr;
-
-  // Clean things up so we don't hold on to memory if our parser gets recycled.
-#ifdef CSS_REPORT_PARSE_ERRORS
-  mError.Truncate();
-  mInnerWindowID = 0;
-  mWindowIDCached = false;
-  mSheet = nullptr;
-  mLoader = nullptr;
-#endif
-
-  if (mPushback != mLocalPushback) {
-    delete [] mPushback;
-    mPushback = mLocalPushback;
-    mPushbackSize = ArrayLength(mLocalPushback);
-  }
-}
-
 // Returns -1 on error or eof
 int32_t
 nsCSSScanner::Read()
 {
   int32_t rv;
   if (0 < mPushbackCount) {
     rv = int32_t(mPushback[--mPushbackCount]);
   } else {
@@ -655,21 +273,17 @@ nsCSSScanner::Read()
       rv = '\n';
     } else if (rv == '\f') {
       rv = '\n';
     }
     if (rv == '\n') {
       // 0 is a magical line number meaning that we don't know (i.e., script)
       if (mLineNumber != 0)
         ++mLineNumber;
-#ifdef CSS_REPORT_PARSE_ERRORS
-      mColNumber = 0;
-    } else {
-      mColNumber++;
-#endif
+      mLineOffset = 0;
     }
   }
   return rv;
 }
 
 int32_t
 nsCSSScanner::Peek()
 {
@@ -1038,18 +652,18 @@ nsCSSScanner::ParseAndAppendEscape(nsStr
       AppendUCS4ToUTF16(ENSURE_VALID_CHAR(rv), aOutput);
     } else {
       while (i--)
         aOutput.Append('0');
       if (IsWhitespace(ch))
         Pushback(ch);
     }
     return true;
-  } 
-  // "Any character except a hexidecimal digit can be escaped to
+  }
+  // "Any character except a hexadecimal digit can be escaped to
   // remove its special meaning by putting a backslash in front"
   // -- CSS1 spec section 7.1
   if (ch == '\n') {
     if (!aInString) {
       // Outside of strings (which includes url() that contains a
       // string), escaped newlines aren't special, and just tokenize as
       // eCSSToken_Symbol (DELIM).
       Pushback(ch);
@@ -1092,19 +706,16 @@ nsCSSScanner::GatherIdent(int32_t aChar,
       // See how much we can consume and append in one go
       uint32_t n = mOffset;
       // Count number of Ident characters that can be processed
       while (n < mCount && IsIdent(mReadPointer[n])) {
         ++n;
       }
       // Add to the token what we have so far
       if (n > mOffset) {
-#ifdef CSS_REPORT_PARSE_ERRORS
-        mColNumber += n - mOffset;
-#endif
         aIdent.Append(&mReadPointer[mOffset], n - mOffset);
         mOffset = n;
       }
     }
 
     aChar = Read();
     if (aChar < 0) break;
     if (aChar == '\\') {
@@ -1339,17 +950,17 @@ nsCSSScanner::SkipCComment()
     if (ch < 0) break;
     if (ch == '*') {
       if (LookAhead('/')) {
         return true;
       }
     }
   }
 
-  REPORT_UNEXPECTED_EOF(PECommentEOF);
+  mReporter->ReportUnexpectedEOF("PECommentEOF");
   return false;
 }
 
 bool
 nsCSSScanner::ParseString(int32_t aStop, nsCSSToken& aToken)
 {
   aToken.mIdent.SetLength(0);
   aToken.mType = eCSSToken_String;
@@ -1361,51 +972,44 @@ nsCSSScanner::ParseString(int32_t aStop,
       uint32_t n = mOffset;
       // Count number of characters that can be processed
       for (;n < mCount; ++n) {
         PRUnichar nextChar = mReadPointer[n];
         if ((nextChar == aStop) || (nextChar == '\\') ||
             (nextChar == '\n') || (nextChar == '\r') || (nextChar == '\f')) {
           break;
         }
-#ifdef CSS_REPORT_PARSE_ERRORS
-        ++mColNumber;
-#endif
       }
       // Add to the token what we have so far
       if (n > mOffset) {
         aToken.mIdent.Append(&mReadPointer[mOffset], n - mOffset);
         mOffset = n;
       }
     }
     int32_t ch = Read();
     if (ch < 0 || ch == aStop) {
       break;
     }
     if (ch == '\n') {
       aToken.mType = eCSSToken_Bad_String;
-#ifdef CSS_REPORT_PARSE_ERRORS
-      ReportUnexpectedToken(aToken, "SEUnterminatedString");
-#endif
+      mReporter->ReportUnexpectedToken("SEUnterminatedString", aToken);
       break;
     }
     if (ch == '\\') {
       if (!ParseAndAppendEscape(aToken.mIdent, true)) {
         aToken.mType = eCSSToken_Bad_String;
         Pushback(ch);
-#ifdef CSS_REPORT_PARSE_ERRORS
         // For strings, the only case where ParseAndAppendEscape will
         // return false is when there's a backslash to start an escape
         // immediately followed by end-of-stream.  In that case, the
         // correct tokenization is badstring *followed* by a DELIM for
         // the backslash, but as far as the author is concerned, it
         // works pretty much the same as an unterminated string, so we
         // use the same error message.
-        ReportUnexpectedToken(aToken, "SEUnterminatedString");
-#endif
+        mReporter->ReportUnexpectedToken("SEUnterminatedString", aToken);
         break;
       }
     } else {
       aToken.mIdent.Append(ch);
     }
   }
   return true;
 }
--- a/layout/style/nsCSSScanner.h
+++ b/layout/style/nsCSSScanner.h
@@ -4,27 +4,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* tokenization of CSS style sheets */
 
 #ifndef nsCSSScanner_h___
 #define nsCSSScanner_h___
 
 #include "nsString.h"
-#include "nsCOMPtr.h"
-#include "mozilla/css/Loader.h"
-#include "nsCSSStyleSheet.h"
 
-// XXX turn this off for minimo builds
-#define CSS_REPORT_PARSE_ERRORS
-
-// for #ifdef CSS_REPORT_PARSE_ERRORS
-#include "nsXPIDLString.h"
-#include "nsThreadUtils.h"
-class nsIURI;
+namespace mozilla {
+namespace css {
+class ErrorReporter;
+}
+}
 
 // Token types
 enum nsCSSTokenType {
   // A css identifier (e.g. foo)
   eCSSToken_Ident,          // mIdent
 
   // A css at keyword (e.g. @foo)
   eCSSToken_AtKeyword,      // mIdent
@@ -84,89 +79,43 @@ struct nsCSSToken {
   bool            mHasSign; // for number, percentage, and dimension
 
   nsCSSToken();
 
   bool IsSymbol(PRUnichar aSymbol) {
     return bool((eCSSToken_Symbol == mType) && (mSymbol == aSymbol));
   }
 
-  void AppendToString(nsString& aBuffer);
+  void AppendToString(nsString& aBuffer) const;
 };
 
-class DeferredCleanupRunnable; 
-
 // CSS Scanner API. Used to tokenize an input stream using the CSS
 // forward compatible tokenization rules. This implementation is
 // private to this package and is only used internally by the css
 // parser.
 class nsCSSScanner {
   public:
-  nsCSSScanner();
-  ~nsCSSScanner();
-
-  // Init the scanner.
   // |aLineNumber == 1| is the beginning of a file, use |aLineNumber == 0|
   // when the line number is unknown.
-  void Init(const nsAString& aBuffer,
-            nsIURI* aURI, uint32_t aLineNumber,
-            nsCSSStyleSheet* aSheet, mozilla::css::Loader* aLoader);
-  void Close();
+  nsCSSScanner(const nsAString& aBuffer, uint32_t aLineNumber);
+  ~nsCSSScanner();
 
-  static bool InitGlobals();
-  static void ReleaseGlobals();
-
+  void SetErrorReporter(mozilla::css::ErrorReporter* aReporter) {
+    mReporter = aReporter;
+  }
   // Set whether or not we are processing SVG
   void SetSVGMode(bool aSVGMode) {
     mSVGMode = aSVGMode;
   }
   bool IsSVGMode() const {
     return mSVGMode;
   }
 
-#ifdef CSS_REPORT_PARSE_ERRORS
-  // Clean up any reclaimable cached resources.
-  void PerformDeferredCleanup();
-
-  void AddToError(const nsSubstring& aErrorText);
-  void OutputError();
-  void ClearError();
-
-  // aMessage must take no parameters
-  void ReportUnexpected(const char* aMessage);
-  
-private:
-  void Reset();
-
-  void ReportUnexpectedParams(const char* aMessage,
-                              const PRUnichar** aParams,
-                              uint32_t aParamsLength);
-
-public:
-  template<uint32_t N>                           
-  void ReportUnexpectedParams(const char* aMessage,
-                              const PRUnichar* (&aParams)[N])
-    {
-      return ReportUnexpectedParams(aMessage, aParams, N);
-    }
-  // aLookingFor is a plain string, not a format string
-  void ReportUnexpectedEOF(const char* aLookingFor);
-  // aLookingFor is a single character
-  void ReportUnexpectedEOF(PRUnichar aLookingFor);
-  // aMessage must take 1 parameter (for the string representation of the
-  // unexpected token)
-  void ReportUnexpectedToken(nsCSSToken& tok, const char *aMessage);
-  // aParams's first entry must be null, and we'll fill in the token
-  void ReportUnexpectedTokenParams(nsCSSToken& tok,
-                                   const char* aMessage,
-                                   const PRUnichar **aParams,
-                                   uint32_t aParamsLength);
-#endif
-
-  uint32_t GetLineNumber() { return mLineNumber; }
+  uint32_t GetLineNumber() const { return mLineNumber; }
+  uint32_t GetColumnNumber() const { return mOffset - mLineOffset + 1; }
 
   // Get the next token. Return false on EOF. aTokenResult
   // is filled in with the data for the token.
   bool Next(nsCSSToken& aTokenResult);
 
   // Get the next token that may be a string or unquoted URL
   bool NextURL(nsCSSToken& aTokenResult);
 
@@ -202,34 +151,25 @@ protected:
   bool ParseURange(int32_t aChar, nsCSSToken& aResult);
   bool SkipCComment();
 
   bool GatherIdent(int32_t aChar, nsString& aIdent);
 
   const PRUnichar *mReadPointer;
   uint32_t mOffset;
   uint32_t mCount;
+
   PRUnichar* mPushback;
   int32_t mPushbackCount;
   int32_t mPushbackSize;
   PRUnichar mLocalPushback[4];
 
   uint32_t mLineNumber;
+  uint32_t mLineOffset;
+  uint32_t mRecordStartOffset;
+
+  mozilla::css::ErrorReporter *mReporter;
   // True if we are in SVG mode; false in "normal" CSS
   bool mSVGMode;
   bool mRecording;
-  uint32_t mRecordStartOffset;
-
-#ifdef CSS_REPORT_PARSE_ERRORS
-  nsRevocableEventPtr<DeferredCleanupRunnable> mDeferredCleaner;
-  nsCOMPtr<nsIURI> mCachedURI;  // Used to invalidate the cached filename.
-  nsString mCachedFileName;
-  uint32_t mErrorLineNumber, mColNumber, mErrorColNumber;
-  nsFixedString mError;
-  PRUnichar mErrorBuf[200];
-  uint64_t mInnerWindowID;
-  bool mWindowIDCached;
-  nsCSSStyleSheet* mSheet;
-  mozilla::css::Loader* mLoader;
-#endif
 };
 
 #endif /* nsCSSScanner_h___ */
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -2212,29 +2212,26 @@ BuildStyleRule(nsCSSProperty aProperty,
   nsAutoPtr<css::Declaration> declaration(new css::Declaration());
   declaration->InitializeEmpty();
 
   bool changed; // ignored, but needed as outparam for ParseProperty
   nsIDocument* doc = aTargetElement->OwnerDoc();
   nsCOMPtr<nsIURI> baseURI = aTargetElement->GetBaseURI();
   nsCSSParser parser(doc->CSSLoader());
 
-  if (aUseSVGMode) {
-    parser.SetSVGMode(true);
-  }
-
   nsCSSProperty propertyToCheck = nsCSSProps::IsShorthand(aProperty) ?
     nsCSSProps::SubpropertyEntryFor(aProperty)[0] : aProperty;
 
   // Get a parser, parse the property, and check for CSS parsing errors.
   // If any of these steps fails, we bail out and delete the declaration.
   if (NS_FAILED(parser.ParseProperty(aProperty, aSpecifiedValue,
                                      doc->GetDocumentURI(), baseURI,
                                      aTargetElement->NodePrincipal(),
-                                     declaration, &changed, false)) ||
+                                     declaration, &changed, false,
+                                     aUseSVGMode)) ||
       // check whether property parsed without CSS parsing errors
       !declaration->HasNonImportantValueFor(propertyToCheck)) {
     NS_WARNING("failure in BuildStyleRule");
     return nullptr;
   }
 
   nsRefPtr<css::StyleRule> rule = new css::StyleRule(nullptr, declaration.forget());
   return rule.forget();