Bug 1035091 part 1: change CSS parser and loader APIs to distinguish UA, user, and author sheets instead of just UA vs everyone else. r=heycam
☠☠ backed out by 3602ae56a243 ☠ ☠
authorZack Weinberg <zackw@panix.com>
Wed, 02 Sep 2015 13:52:49 -0400
changeset 260615 f6e98029d1cb
parent 260614 ac45060fbda2
child 260616 1fc07bdd9aa8
push id29318
push usercbook@mozilla.com
push dateThu, 03 Sep 2015 11:15:07 +0000
treeherdermozilla-central@74fbd245369c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1035091
milestone43.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 1035091 part 1: change CSS parser and loader APIs to distinguish UA, user, and author sheets instead of just UA vs everyone else. r=heycam
dom/base/nsDocument.cpp
dom/base/nsTreeSanitizer.cpp
dom/svg/SVGDocument.cpp
editor/libeditor/nsHTMLEditor.cpp
layout/base/nsStyleSheetService.cpp
layout/style/CSSStyleSheet.cpp
layout/style/Loader.cpp
layout/style/Loader.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSParser.h
layout/style/nsLayoutStylesheetCache.cpp
layout/style/nsLayoutStylesheetCache.h
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4404,30 +4404,49 @@ FindSheet(const nsCOMArray<nsIStyleSheet
     if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
       return i;
   }
 
   return -1;
 }
 
 nsresult
-nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
+nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType,
+                                     nsIURI* aSheetURI)
 {
   NS_PRECONDITION(aSheetURI, "null arg");
 
   // Checking if we have loaded this one already.
   if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
     return NS_ERROR_INVALID_ARG;
 
   // Loading the sheet sync.
-  nsRefPtr<mozilla::css::Loader> loader = new mozilla::css::Loader();
+  nsRefPtr<css::Loader> loader = new css::Loader();
+
+  css::SheetParsingMode parsingMode;
+  switch (aType) {
+    case nsIDocument::eAgentSheet:
+      parsingMode = css::eAgentSheetFeatures;
+      break;
+
+    case nsIDocument::eUserSheet:
+      parsingMode = css::eUserSheetFeatures;
+      break;
+
+    case nsIDocument::eAuthorSheet:
+      parsingMode = css::eAuthorSheetFeatures;
+      break;
+
+    default:
+      MOZ_CRASH("impossible value for aType");
+  }
 
   nsRefPtr<CSSStyleSheet> sheet;
-  nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet,
-    true, getter_AddRefs(sheet));
+  nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true,
+                                      getter_AddRefs(sheet));
   NS_ENSURE_SUCCESS(rv, rv);
 
   sheet->SetOwningDocument(this);
   MOZ_ASSERT(sheet->IsApplicable());
 
   return AddAdditionalStyleSheet(aType, sheet);
 }
 
@@ -9930,17 +9949,20 @@ nsDocument::PreloadStyle(nsIURI* uri, co
                          Element::StringToCORSMode(aCrossOriginAttr),
                          aReferrerPolicy, aIntegrity);
 }
 
 nsresult
 nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
                                 CSSStyleSheet** sheet)
 {
-  return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet);
+  css::SheetParsingMode mode =
+    isAgentSheet ? css::eAgentSheetFeatures
+                 : css::eAuthorSheetFeatures;
+  return CSSLoader()->LoadSheetSync(uri, mode, isAgentSheet, sheet);
 }
 
 class nsDelayedEventDispatcher : public nsRunnable
 {
 public:
   explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments)
   {
     mDocuments.SwapElements(aDocuments);
--- a/dom/base/nsTreeSanitizer.cpp
+++ b/dom/base/nsTreeSanitizer.cpp
@@ -1098,17 +1098,18 @@ nsTreeSanitizer::SanitizeStyleSheet(cons
   bool didSanitize = false;
   // Create a sheet to hold the parsed CSS
   nsRefPtr<CSSStyleSheet> sheet = new CSSStyleSheet(CORS_NONE, aDocument->GetReferrerPolicy());
   sheet->SetURIs(aDocument->GetDocumentURI(), nullptr, aBaseURI);
   sheet->SetPrincipal(aDocument->NodePrincipal());
   // Create the CSS parser, and parse the CSS text.
   nsCSSParser parser(nullptr, sheet);
   rv = parser.ParseSheet(aOriginal, aDocument->GetDocumentURI(), aBaseURI,
-                         aDocument->NodePrincipal(), 0, false);
+                         aDocument->NodePrincipal(), 0,
+                         mozilla::css::eAuthorSheetFeatures);
   NS_ENSURE_SUCCESS(rv, true);
   // Mark the sheet as complete.
   MOZ_ASSERT(!sheet->IsModified(),
              "should not get marked modified during parsing");
   sheet->SetComplete();
   // Loop through all the rules found in the CSS text
   int32_t ruleCount = sheet->StyleRuleCount();
   for (int32_t i = 0; i < ruleCount; ++i) {
--- a/dom/svg/SVGDocument.cpp
+++ b/dom/svg/SVGDocument.cpp
@@ -143,17 +143,19 @@ SVGDocument::EnsureNonSVGUserAgentStyleS
                                    getter_Copies(spec));
 
           mozilla::css::Loader* cssLoader = CSSLoader();
           if (cssLoader->GetEnabled()) {
             nsCOMPtr<nsIURI> uri;
             NS_NewURI(getter_AddRefs(uri), spec);
             if (uri) {
               nsRefPtr<CSSStyleSheet> cssSheet;
-              cssLoader->LoadSheetSync(uri, true, true, getter_AddRefs(cssSheet));
+              cssLoader->LoadSheetSync(uri,
+                                       mozilla::css::eAgentSheetFeatures,
+                                       true, getter_AddRefs(cssSheet));
               if (cssSheet) {
                 EnsureOnDemandBuiltInUASheet(cssSheet);
               }
             }
           }
         }
       }
     }
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -2835,17 +2835,18 @@ nsHTMLEditor::AddOverrideStyleSheet(cons
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We MUST ONLY load synchronous local files (no @import)
   // XXXbz Except this will actually try to load remote files
   // synchronously, of course..
   nsRefPtr<CSSStyleSheet> sheet;
   // Editor override style sheets may want to style Gecko anonymous boxes
   rv = ps->GetDocument()->CSSLoader()->
-    LoadSheetSync(uaURI, true, true, getter_AddRefs(sheet));
+    LoadSheetSync(uaURI, mozilla::css::eAgentSheetFeatures, true,
+                  getter_AddRefs(sheet));
 
   // Synchronous loads should ALWAYS return completed
   NS_ENSURE_TRUE(sheet, NS_ERROR_NULL_POINTER);
 
   // Add the override style sheet
   // (This checks if already exists)
   ps->AddOverrideStyleSheet(sheet);
 
--- a/layout/base/nsStyleSheetService.cpp
+++ b/layout/base/nsStyleSheetService.cpp
@@ -175,27 +175,42 @@ nsStyleSheetService::LoadAndRegisterShee
   }
   return rv;
 }
 
 nsresult
 nsStyleSheetService::LoadAndRegisterSheetInternal(nsIURI *aSheetURI,
                                                   uint32_t aSheetType)
 {
-  NS_ENSURE_ARG(aSheetType == AGENT_SHEET ||
-                aSheetType == USER_SHEET ||
-                aSheetType == AUTHOR_SHEET);
   NS_ENSURE_ARG_POINTER(aSheetURI);
 
+  css::SheetParsingMode parsingMode;
+  switch (aSheetType) {
+    case AGENT_SHEET:
+      parsingMode = css::eAgentSheetFeatures;
+      break;
+
+    case USER_SHEET:
+      parsingMode = css::eUserSheetFeatures;
+      break;
+
+    case AUTHOR_SHEET:
+      parsingMode = css::eAuthorSheetFeatures;
+      break;
+
+    default:
+      NS_WARNING("invalid sheet type argument");
+      return NS_ERROR_INVALID_ARG;
+  }
+
   nsRefPtr<css::Loader> loader = new css::Loader();
 
   nsRefPtr<CSSStyleSheet> sheet;
-  // Allow UA sheets, but not user sheets, to use unsafe rules
-  nsresult rv = loader->LoadSheetSync(aSheetURI, aSheetType == AGENT_SHEET,
-                                      true, getter_AddRefs(sheet));
+  nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true,
+                                      getter_AddRefs(sheet));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!mSheets[aSheetType].AppendObject(sheet)) {
     rv = NS_ERROR_OUT_OF_MEMORY;
   }
 
   return rv;
 }
@@ -214,28 +229,42 @@ nsStyleSheetService::SheetRegistered(nsI
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStyleSheetService::PreloadSheet(nsIURI *aSheetURI, uint32_t aSheetType,
                                   nsIDOMStyleSheet **aSheet)
 {
-  NS_ENSURE_ARG(aSheetType == AGENT_SHEET ||
-                aSheetType == USER_SHEET ||
-                aSheetType == AUTHOR_SHEET);
+  NS_PRECONDITION(aSheet, "Null out param");
   NS_ENSURE_ARG_POINTER(aSheetURI);
-  NS_PRECONDITION(aSheet, "Null out param");
+  css::SheetParsingMode parsingMode;
+  switch (aSheetType) {
+    case AGENT_SHEET:
+      parsingMode = css::eAgentSheetFeatures;
+      break;
+
+    case USER_SHEET:
+      parsingMode = css::eUserSheetFeatures;
+      break;
+
+    case AUTHOR_SHEET:
+      parsingMode = css::eAuthorSheetFeatures;
+      break;
+
+    default:
+      NS_WARNING("invalid sheet type argument");
+      return NS_ERROR_INVALID_ARG;
+  }
 
   nsRefPtr<css::Loader> loader = new css::Loader();
 
-  // Allow UA sheets, but not user sheets, to use unsafe rules
   nsRefPtr<CSSStyleSheet> sheet;
-  nsresult rv = loader->LoadSheetSync(aSheetURI, aSheetType == AGENT_SHEET,
-                                      true, getter_AddRefs(sheet));
+  nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true,
+                                      getter_AddRefs(sheet));
   NS_ENSURE_SUCCESS(rv, rv);
   sheet.forget(aSheet);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStyleSheetService::UnregisterSheet(nsIURI *aSheetURI, uint32_t aSheetType)
 {
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -2304,21 +2304,24 @@ CSSStyleSheet::ParseSheet(const nsAStrin
     NS_ASSERTION(child->mParent == this, "Child sheet is not parented to this!");
     child->mParent = nullptr;
     child->mDocument = nullptr;
   }
   mInner->mFirstChild = nullptr;
   mInner->mNameSpaceMap = nullptr;
 
   // allow unsafe rules if the style sheet's principal is the system principal
-  bool allowUnsafeRules = nsContentUtils::IsSystemPrincipal(mInner->mPrincipal);
+  css::SheetParsingMode parsingMode =
+    nsContentUtils::IsSystemPrincipal(mInner->mPrincipal)
+      ? css::eAgentSheetFeatures
+      : css::eAuthorSheetFeatures;
 
   nsCSSParser parser(loader, this);
   nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI,
-                                  mInner->mPrincipal, 1, allowUnsafeRules);
+                                  mInner->mPrincipal, 1, parsingMode);
   DidDirty(); // we are always 'dirty' here since we always remove rules first
   NS_ENSURE_SUCCESS(rv, rv);
 
   // notify document of all new rules
   if (mDocument) {
     for (int32_t index = 0; index < mInner->mOrderedRules.Count(); ++index) {
       nsRefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(index);
       if (rule->GetType() == css::Rule::IMPORT_RULE &&
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -99,16 +99,22 @@ using namespace mozilla::dom;
 
 namespace mozilla {
 namespace css {
 
 /*********************************************
  * Data needed to properly load a stylesheet *
  *********************************************/
 
+static_assert(eAuthorSheetFeatures == 0 &&
+              eUserSheetFeatures == 1 &&
+              eAgentSheetFeatures == 2,
+              "sheet parsing mode constants won't fit "
+              "in SheetLoadData::mParsingMode");
+
 class SheetLoadData final : public nsIRunnable,
                             public nsIUnicharStreamLoaderObserver,
                             public nsIThreadObserver
 {
 protected:
   virtual ~SheetLoadData(void);
 
 public:
@@ -132,17 +138,17 @@ public:
                 nsIPrincipal* aLoaderPrincipal,
                 nsINode* aRequestingNode);
 
   // Data for loading a non-document sheet
   SheetLoadData(Loader* aLoader,
                 nsIURI* aURI,
                 CSSStyleSheet* aSheet,
                 bool aSyncLoad,
-                bool aAllowUnsafeRules,
+                SheetParsingMode aParsingMode,
                 bool aUseSystemPrincipal,
                 const nsCString& aCharset,
                 nsICSSLoaderObserver* aObserver,
                 nsIPrincipal* aLoaderPrincipal,
                 nsINode* aRequestingNode);
 
   already_AddRefed<nsIURI> GetReferrerURI();
 
@@ -210,19 +216,22 @@ public:
   // are fired for any SheetLoadData that has a non-null
   // mOwningElement.
   bool                       mMustNotify : 1;
 
   // mWasAlternate is true if the sheet was an alternate when the load data was
   // created.
   bool                       mWasAlternate : 1;
 
-  // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed
-  // in the loaded sheet.
-  bool                       mAllowUnsafeRules : 1;
+  // mParsingMode controls access to nonstandard style constructs that
+  // are not safe for use on the public Web but necessary in UA sheets
+  // and/or useful in user sheets.  The only values stored in this
+  // field are 0, 1, and 2; three bits are allocated to avoid issues
+  // should the enum type be signed.
+  SheetParsingMode           mParsingMode : 3;
 
   // mUseSystemPrincipal is true if the system principal should be used for
   // this sheet, no matter what the channel principal is.  Only true for sync
   // loads.
   bool                       mUseSystemPrincipal : 1;
 
   // If true, this SheetLoadData is being used as a way to handle
   // async observer notification for an already-complete sheet.
@@ -328,17 +337,17 @@ SheetLoadData::SheetLoadData(Loader* aLo
     mNext(nullptr),
     mPendingChildren(0),
     mSyncLoad(false),
     mIsNonDocumentSheet(false),
     mIsLoading(false),
     mIsCancelled(false),
     mMustNotify(false),
     mWasAlternate(aIsAlternate),
-    mAllowUnsafeRules(false),
+    mParsingMode(eAuthorSheetFeatures),
     mUseSystemPrincipal(false),
     mSheetAlreadyComplete(false),
     mOwningElement(aOwningElement),
     mObserver(aObserver),
     mLoaderPrincipal(aLoaderPrincipal),
     mRequestingNode(aRequestingNode)
 {
   NS_PRECONDITION(mLoader, "Must have a loader!");
@@ -359,42 +368,42 @@ SheetLoadData::SheetLoadData(Loader* aLo
     mParentData(aParentData),
     mPendingChildren(0),
     mSyncLoad(false),
     mIsNonDocumentSheet(false),
     mIsLoading(false),
     mIsCancelled(false),
     mMustNotify(false),
     mWasAlternate(false),
-    mAllowUnsafeRules(false),
+    mParsingMode(eAuthorSheetFeatures),
     mUseSystemPrincipal(false),
     mSheetAlreadyComplete(false),
     mOwningElement(nullptr),
     mObserver(aObserver),
     mLoaderPrincipal(aLoaderPrincipal),
     mRequestingNode(aRequestingNode)
 {
   NS_PRECONDITION(mLoader, "Must have a loader!");
   if (mParentData) {
     mSyncLoad = mParentData->mSyncLoad;
     mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
-    mAllowUnsafeRules = mParentData->mAllowUnsafeRules;
+    mParsingMode = mParentData->mParsingMode;
     mUseSystemPrincipal = mParentData->mUseSystemPrincipal;
     ++(mParentData->mPendingChildren);
   }
 
   NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
                    "Shouldn't use system principal for async loads");
 }
 
 SheetLoadData::SheetLoadData(Loader* aLoader,
                              nsIURI* aURI,
                              CSSStyleSheet* aSheet,
                              bool aSyncLoad,
-                             bool aAllowUnsafeRules,
+                             SheetParsingMode aParsingMode,
                              bool aUseSystemPrincipal,
                              const nsCString& aCharset,
                              nsICSSLoaderObserver* aObserver,
                              nsIPrincipal* aLoaderPrincipal,
                              nsINode* aRequestingNode)
   : mLoader(aLoader),
     mURI(aURI),
     mLineNumber(1),
@@ -402,26 +411,30 @@ SheetLoadData::SheetLoadData(Loader* aLo
     mNext(nullptr),
     mPendingChildren(0),
     mSyncLoad(aSyncLoad),
     mIsNonDocumentSheet(true),
     mIsLoading(false),
     mIsCancelled(false),
     mMustNotify(false),
     mWasAlternate(false),
-    mAllowUnsafeRules(aAllowUnsafeRules),
+    mParsingMode(aParsingMode),
     mUseSystemPrincipal(aUseSystemPrincipal),
     mSheetAlreadyComplete(false),
     mOwningElement(nullptr),
     mObserver(aObserver),
     mLoaderPrincipal(aLoaderPrincipal),
     mRequestingNode(aRequestingNode),
     mCharsetHint(aCharset)
 {
   NS_PRECONDITION(mLoader, "Must have a loader!");
+  NS_PRECONDITION(aParsingMode == eAuthorSheetFeatures ||
+                  aParsingMode == eUserSheetFeatures ||
+                  aParsingMode == eAgentSheetFeatures,
+                  "Unrecognized sheet parsing mode");
 
   NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
                    "Shouldn't use system principal for async loads");
 }
 
 SheetLoadData::~SheetLoadData()
 {
   NS_IF_RELEASE(mNext);
@@ -1740,17 +1753,17 @@ Loader::ParseSheet(const nsAString& aInp
 
   // Push our load data on the stack so any kids can pick it up
   mParsingDatas.AppendElement(aLoadData);
   nsIURI* sheetURI = aLoadData->mSheet->GetSheetURI();
   nsIURI* baseURI = aLoadData->mSheet->GetBaseURI();
   nsresult rv = parser.ParseSheet(aInput, sheetURI, baseURI,
                                   aLoadData->mSheet->Principal(),
                                   aLoadData->mLineNumber,
-                                  aLoadData->mAllowUnsafeRules);
+                                  aLoadData->mParsingMode);
   mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
 
   if (NS_FAILED(rv)) {
     LOG_ERROR(("  Low-level error in parser!"));
     SheetComplete(aLoadData, rv);
     return rv;
   }
 
@@ -2248,59 +2261,60 @@ Loader::LoadChildSheet(CSSStyleSheet* aP
   // If syncLoad is true, |data| will be deleted by now.
   if (!syncLoad) {
     data->mMustNotify = true;
   }
   return rv;
 }
 
 nsresult
-Loader::LoadSheetSync(nsIURI* aURL, bool aAllowUnsafeRules,
+Loader::LoadSheetSync(nsIURI* aURL,
+                      SheetParsingMode aParsingMode,
                       bool aUseSystemPrincipal,
                       CSSStyleSheet** aSheet)
 {
   LOG(("css::Loader::LoadSheetSync"));
-  return InternalLoadNonDocumentSheet(aURL, aAllowUnsafeRules,
+  return InternalLoadNonDocumentSheet(aURL, aParsingMode,
                                       aUseSystemPrincipal, nullptr,
                                       EmptyCString(), aSheet, nullptr);
 }
 
 nsresult
 Loader::LoadSheet(nsIURI* aURL,
                   nsIPrincipal* aOriginPrincipal,
                   const nsCString& aCharset,
                   nsICSSLoaderObserver* aObserver,
                   CSSStyleSheet** aSheet)
 {
   LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
   NS_PRECONDITION(aSheet, "aSheet is null");
-  return InternalLoadNonDocumentSheet(aURL, false, false,
+  return InternalLoadNonDocumentSheet(aURL, eAuthorSheetFeatures, false,
                                       aOriginPrincipal, aCharset,
                                       aSheet, aObserver);
 }
 
 nsresult
 Loader::LoadSheet(nsIURI* aURL,
                   nsIPrincipal* aOriginPrincipal,
                   const nsCString& aCharset,
                   nsICSSLoaderObserver* aObserver,
                   CORSMode aCORSMode,
                   ReferrerPolicy aReferrerPolicy,
                   const nsAString& aIntegrity)
 {
   LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
-  return InternalLoadNonDocumentSheet(aURL, false, false,
+  return InternalLoadNonDocumentSheet(aURL, eAuthorSheetFeatures, false,
                                       aOriginPrincipal, aCharset,
                                       nullptr, aObserver, aCORSMode,
                                       aReferrerPolicy, aIntegrity);
 }
 
 nsresult
 Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
-                                     bool aAllowUnsafeRules,
+                                     SheetParsingMode aParsingMode,
                                      bool aUseSystemPrincipal,
                                      nsIPrincipal* aOriginPrincipal,
                                      const nsCString& aCharset,
                                      CSSStyleSheet** aSheet,
                                      nsICSSLoaderObserver* aObserver,
                                      CORSMode aCORSMode,
                                      ReferrerPolicy aReferrerPolicy,
                                      const nsAString& aIntegrity)
@@ -2347,17 +2361,17 @@ Loader::InternalLoadNonDocumentSheet(nsI
     }
     if (aSheet) {
       sheet.swap(*aSheet);
     }
     return rv;
   }
 
   SheetLoadData* data =
-    new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules,
+    new SheetLoadData(this, aURL, sheet, syncLoad, aParsingMode,
                       aUseSystemPrincipal, aCharset, aObserver,
                       aOriginPrincipal, mDocument);
 
   NS_ADDREF(data);
   rv = LoadSheet(data, state);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aSheet) {
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -137,16 +137,43 @@ class ImportRule;
 enum StyleSheetState {
   eSheetStateUnknown = 0,
   eSheetNeedsParser,
   eSheetPending,
   eSheetLoading,
   eSheetComplete
 };
 
+/**
+ * Enum defining the mode in which a sheet is to be parsed.  This is
+ * usually, but not always, the same as the cascade level at which the
+ * sheet will apply (see nsStyleSet.h).  Most of the Loader APIs only
+ * support loading of author sheets.
+ *
+ * Author sheets are the normal case: styles embedded in or linked
+ * from HTML pages.  They are also the most restricted.
+ *
+ * User sheets can do anything author sheets can do, and also get
+ * access to a few CSS extensions that are not yet suitable for
+ * exposure on the public Web, but are very useful for expressing
+ * user style overrides, such as @-moz-document rules.
+ *
+ * Agent sheets have access to all author- and user-sheet features
+ * plus more extensions that are necessary for internal use but,
+ * again, not yet suitable for exposure on the public Web.  Some of
+ * these are outright unsafe to expose; in particular, incorrect
+ * styling of anonymous box pseudo-elements can violate layout
+ * invariants.
+ */
+enum SheetParsingMode {
+  eAuthorSheetFeatures = 0,
+  eUserSheetFeatures,
+  eAgentSheetFeatures
+};
+
 class Loader final {
   typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
 
 public:
   Loader();
   explicit Loader(nsIDocument*);
 
  private:
@@ -251,51 +278,49 @@ public:
   /**
    * Synchronously load and return the stylesheet at aURL.  Any child sheets
    * will also be loaded synchronously.  Note that synchronous loads over some
    * protocols may involve spinning up a new event loop, so use of this method
    * does NOT guarantee not receiving any events before the sheet loads.  This
    * method can be used to load sheets not associated with a document.
    *
    * @param aURL the URL of the sheet to load
-   * @param aEnableUnsafeRules whether unsafe rules are enabled for this
-   * sheet load
-   * Unsafe rules are rules that can violate key Gecko invariants if misused.
-   * In particular, most anonymous box pseudoelements must be very carefully
-   * styled or we will have severe problems. Therefore unsafe rules should
-   * never be enabled for stylesheets controlled by untrusted sites; preferably
-   * unsafe rules should only be enabled for agent sheets.
+   * @param aParsingMode the mode in which to parse the sheet
+   *        (see comments at enum SheetParsingMode, above).
    * @param aUseSystemPrincipal if true, give the resulting sheet the system
    * principal no matter where it's being loaded from.
    * @param [out] aSheet the loaded, complete sheet.
    *
    * NOTE: At the moment, this method assumes the sheet will be UTF-8, but
    * ideally it would allow arbitrary encodings.  Callers should NOT depend on
    * non-UTF8 sheets being treated as UTF-8 by this method.
    *
    * NOTE: A successful return from this method doesn't indicate anything about
    * whether the data could be parsed as CSS and doesn't indicate anything
    * about the status of child sheets of the returned sheet.
    */
-  nsresult LoadSheetSync(nsIURI* aURL, bool aEnableUnsafeRules,
+  nsresult LoadSheetSync(nsIURI* aURL,
+                         SheetParsingMode aParsingMode,
                          bool aUseSystemPrincipal,
                          CSSStyleSheet** aSheet);
 
   /**
-   * As above, but aUseSystemPrincipal and aEnableUnsafeRules are assumed false.
+   * As above, but defaults aParsingMode to eAuthorSheetFeatures and
+   * aUseSystemPrincipal to false.
    */
   nsresult LoadSheetSync(nsIURI* aURL, CSSStyleSheet** aSheet) {
-    return LoadSheetSync(aURL, false, false, aSheet);
+    return LoadSheetSync(aURL, eAuthorSheetFeatures, false, aSheet);
   }
 
   /**
    * Asynchronously load the stylesheet at aURL.  If a successful result is
    * returned, aObserver is guaranteed to be notified asynchronously once the
    * sheet is loaded and marked complete.  This method can be used to load
-   * sheets not associated with a document.
+   * sheets not associated with a document.  This method cannot be used to
+   * load user or agent sheets.
    *
    * @param aURL the URL of the sheet to load
    * @param aOriginPrincipal the principal to use for security checks.  This
    *                         can be null to indicate that these checks should
    *                         be skipped.
    * @param aCharset the encoding to use for converting the sheet data
    *        from bytes to Unicode.  May be empty to indicate that the
    *        charset of the CSSLoader's document should be used.  This
@@ -441,17 +466,17 @@ private:
                             nsIContent* aLinkingContent,
                             nsIDocument* aDocument);
 
   nsresult InsertChildSheet(CSSStyleSheet* aSheet,
                             CSSStyleSheet* aParentSheet,
                             ImportRule* aParentRule);
 
   nsresult InternalLoadNonDocumentSheet(nsIURI* aURL,
-                                        bool aAllowUnsafeRules,
+                                        SheetParsingMode aParsingMode,
                                         bool aUseSystemPrincipal,
                                         nsIPrincipal* aOriginPrincipal,
                                         const nsCString& aCharset,
                                         CSSStyleSheet** aSheet,
                                         nsICSSLoaderObserver* aObserver,
                                         CORSMode aCORSMode = CORS_NONE,
                                         ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Default,
                                         const nsAString& aIntegrity = EmptyString());
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -100,16 +100,22 @@ static void AssignRuleToPointer(css::Rul
 static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
 
 struct CSSParserInputState {
   nsCSSScannerPosition mPosition;
   nsCSSToken mToken;
   bool mHavePushBack;
 };
 
+static_assert(eAuthorSheetFeatures == 0 &&
+              eUserSheetFeatures == 1 &&
+              eAgentSheetFeatures == 2,
+              "sheet parsing mode constants won't fit "
+              "in CSSParserImpl::mParsingMode");
+
 // Your basic top-down recursive descent style parser
 // The exposed methods and members of this class are precisely those
 // needed by nsCSSParser, far below.
 class CSSParserImpl {
 public:
   CSSParserImpl();
   ~CSSParserImpl();
 
@@ -124,17 +130,17 @@ public:
   // Clears everything set by the above Set*() functions.
   void Reset();
 
   nsresult ParseSheet(const nsAString& aInput,
                       nsIURI*          aSheetURI,
                       nsIURI*          aBaseURI,
                       nsIPrincipal*    aSheetPrincipal,
                       uint32_t         aLineNumber,
-                      bool             aAllowUnsafeRules);
+                      SheetParsingMode aParsingMode);
 
   nsresult ParseStyleAttribute(const nsAString&  aAttributeValue,
                                nsIURI*           aDocURL,
                                nsIURI*           aBaseURL,
                                nsIPrincipal*     aNodePrincipal,
                                css::StyleRule**  aResult);
 
   nsresult ParseDeclarations(const nsAString&  aBuffer,
@@ -308,22 +314,30 @@ public:
                                            nsRuleData* aRuleData,
                                            nsIURI* aDocURL,
                                            nsIURI* aBaseURL,
                                            nsIPrincipal* aDocPrincipal,
                                            CSSStyleSheet* aSheet,
                                            uint32_t aLineNumber,
                                            uint32_t aLineOffset);
 
+  bool AgentRulesEnabled() const {
+    return mParsingMode == eAgentSheetFeatures;
+  }
+  bool UserRulesEnabled() const {
+    return mParsingMode == eAgentSheetFeatures ||
+           mParsingMode == eUserSheetFeatures;
+  }
+
   nsCSSProps::EnabledState PropertyEnabledState() const {
     static_assert(nsCSSProps::eEnabledForAllContent == 0,
                   "nsCSSProps::eEnabledForAllContent should be zero for "
                   "this bitfield to work");
     nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent;
-    if (mUnsafeRulesEnabled) {
+    if (AgentRulesEnabled()) {
       enabledState |= nsCSSProps::eEnabledInUASheets;
     }
     if (mIsChromeOrCertifiedApp) {
       enabledState |= nsCSSProps::eEnabledInChromeOrCertifiedApp;
     }
     return enabledState;
   }
 
@@ -1199,18 +1213,22 @@ protected:
   bool          mNavQuirkMode : 1;
 
   // True when the hashless color quirk applies.
   bool mHashlessColorQuirk : 1;
 
   // True when the unitless length quirk applies.
   bool mUnitlessLengthQuirk : 1;
 
-  // True if unsafe rules should be allowed
-  bool mUnsafeRulesEnabled : 1;
+  // Controls access to nonstandard style constructs that are not safe
+  // for use on the public Web but necessary in UA sheets and/or
+  // useful in user sheets.  The only values stored in this field are
+  // 0, 1, and 2; three bits are allocated to avoid issues should the
+  // enum type be signed.
+  SheetParsingMode mParsingMode : 3;
 
   // True if we are in parsing rules for Chrome or Certified App content,
   // in which case CSS properties with the
   // CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP
   // flag should be allowed.
   bool mIsChromeOrCertifiedApp : 1;
 
   // True if viewport units should be allowed.
@@ -1329,17 +1347,17 @@ CSSParserImpl::CSSParserImpl()
     mReporter(nullptr),
     mChildLoader(nullptr),
     mSection(eCSSSection_Charset),
     mNameSpaceMap(nullptr),
     mHavePushBack(false),
     mNavQuirkMode(false),
     mHashlessColorQuirk(false),
     mUnitlessLengthQuirk(false),
-    mUnsafeRulesEnabled(false),
+    mParsingMode(eAuthorSheetFeatures),
     mIsChromeOrCertifiedApp(false),
     mViewportUnitsEnabled(true),
     mHTMLMediaMode(false),
     mParsingCompoundProperty(false),
     mInSupportsCondition(false),
     mInFailingSupportsRule(false),
     mSuppressErrors(false),
     mSheetPrincipalRequired(true),
@@ -1436,17 +1454,17 @@ CSSParserImpl::ReleaseScanner()
 }
 
 nsresult
 CSSParserImpl::ParseSheet(const nsAString& aInput,
                           nsIURI*          aSheetURI,
                           nsIURI*          aBaseURI,
                           nsIPrincipal*    aSheetPrincipal,
                           uint32_t         aLineNumber,
-                          bool             aAllowUnsafeRules)
+                          SheetParsingMode aParsingMode)
 {
   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
   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
@@ -1481,17 +1499,17 @@ CSSParserImpl::ParseSheet(const nsAStrin
           break;
       }
     }
   }
   else {
     mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
   }
 
-  mUnsafeRulesEnabled = aAllowUnsafeRules;
+  mParsingMode = aParsingMode;
   mIsChromeOrCertifiedApp =
     dom::IsChromeURI(aSheetURI) ||
     aSheetPrincipal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
 
   nsCSSToken* tk = &mToken;
   for (;;) {
     // Get next non-whitespace token
     if (!GetToken(true)) {
@@ -1507,17 +1525,17 @@ CSSParserImpl::ParseSheet(const nsAStrin
     }
     UngetToken();
     if (ParseRuleSet(AppendRuleToSheet, this)) {
       mSection = eCSSSection_General;
     }
   }
   ReleaseScanner();
 
-  mUnsafeRulesEnabled = false;
+  mParsingMode = eAuthorSheetFeatures;
   mIsChromeOrCertifiedApp = false;
 
   // XXX check for low level errors
   return NS_OK;
 }
 
 /**
  * Determines whether the identifier contained in the given string is a
@@ -1685,17 +1703,17 @@ CSSParserImpl::ParseProperty(const nsCSS
   mSection = eCSSSection_General;
   scanner.SetSVGMode(aIsSVGMode);
 
   *aChanged = false;
 
   // Check for unknown or preffed off properties
   if (eCSSProperty_UNKNOWN == aPropID ||
       !(nsCSSProps::IsEnabled(aPropID) ||
-        (mUnsafeRulesEnabled &&
+        (AgentRulesEnabled() &&
          nsCSSProps::PropHasFlags(aPropID,
                                   CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)))) {
     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
     REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
     REPORT_UNEXPECTED(PEDeclDropped);
     OUTPUT_ERROR();
     ReleaseScanner();
     return;
@@ -5585,17 +5603,17 @@ CSSParserImpl::ParsePseudoSelector(int32
   bool isTreePseudo = false;
   nsCSSPseudoElements::Type pseudoElementType =
     nsCSSPseudoElements::GetPseudoType(pseudo);
   nsCSSPseudoClasses::Type pseudoClassType =
     nsCSSPseudoClasses::GetPseudoType(pseudo);
   bool pseudoClassIsUserAction =
     nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
 
-  if (!mUnsafeRulesEnabled &&
+  if (!AgentRulesEnabled() &&
       ((pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount &&
         nsCSSPseudoElements::PseudoElementIsUASheetOnly(pseudoElementType)) ||
        (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass &&
         nsCSSPseudoClasses::PseudoClassIsUASheetOnly(pseudoClassType)))) {
     // This pseudo-element or pseudo-class is not exposed to content.
     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
     UngetToken();
     return eSelectorParsingStatus_Error;
@@ -5621,20 +5639,20 @@ CSSParserImpl::ParsePseudoSelector(int32
   // be false, and it will still pass that check.  So the tree
   // pseudo-elements are allowed to be either functions or not, as
   // desired.
   bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
 #endif
   bool isPseudoElement =
     (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount);
   // anonymous boxes are only allowed if they're the tree boxes or we have
-  // enabled unsafe rules
+  // enabled agent rules
   bool isAnonBox = isTreePseudo ||
     (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox &&
-     mUnsafeRulesEnabled);
+     AgentRulesEnabled());
   bool isPseudoClass =
     (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass);
 
   NS_ASSERTION(!isPseudoClass ||
                pseudoElementType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
                "Why is this atom both a pseudo-class and a pseudo-element?");
   NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
                "Shouldn't be more than one of these");
@@ -10456,21 +10474,21 @@ CSSParserImpl::ParseSingleValueProperty(
   }
 
   uint32_t variant = nsCSSProps::ParserVariant(aPropID);
   if (variant == 0) {
     MOZ_ASSERT(false, "not a single value property");
     return false;
   }
 
-  // We only allow 'script-level' when unsafe rules are enabled, because
+  // We only allow 'script-level' when agent rules are enabled, because
   // otherwise it could interfere with rulenode optimizations if used in
   // a non-MathML-enabled document. We also only allow math-display when
-  // unsafe rules are enabled.
-  if (!mUnsafeRulesEnabled &&
+  // agent rules are enabled.
+  if (!AgentRulesEnabled() &&
       (aPropID == eCSSProperty_script_level ||
        aPropID == eCSSProperty_math_display))
     return false;
 
   const KTableValue* kwtable = nsCSSProps::kKeywordTableTable[aPropID];
   uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID);
   return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions);
 }
@@ -15776,21 +15794,21 @@ nsCSSParser::SetChildLoader(mozilla::css
 }
 
 nsresult
 nsCSSParser::ParseSheet(const nsAString& aInput,
                         nsIURI*          aSheetURI,
                         nsIURI*          aBaseURI,
                         nsIPrincipal*    aSheetPrincipal,
                         uint32_t         aLineNumber,
-                        bool             aAllowUnsafeRules)
+                        SheetParsingMode aParsingMode)
 {
   return static_cast<CSSParserImpl*>(mImpl)->
     ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
-               aAllowUnsafeRules);
+               aParsingMode);
 }
 
 nsresult
 nsCSSParser::ParseStyleAttribute(const nsAString&  aAttributeValue,
                                  nsIURI*           aDocURI,
                                  nsIURI*           aBaseURI,
                                  nsIPrincipal*     aNodePrincipal,
                                  css::StyleRule**  aResult)
--- a/layout/style/nsCSSParser.h
+++ b/layout/style/nsCSSParser.h
@@ -4,16 +4,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 */
 
 #ifndef nsCSSParser_h___
 #define nsCSSParser_h___
 
 #include "mozilla/Attributes.h"
+#include "mozilla/css/Loader.h"
 
 #include "nsCSSProperty.h"
 #include "nsCSSScanner.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsStringFwd.h"
 #include "nsTArrayForwardDeclare.h"
 
@@ -27,17 +28,16 @@ class nsCSSValue;
 struct nsRuleData;
 
 namespace mozilla {
 class CSSStyleSheet;
 class CSSVariableValues;
 namespace css {
 class Rule;
 class Declaration;
-class Loader;
 class StyleRule;
 } // namespace css
 } // namespace mozilla
 
 // Interface to the css parser.
 
 class MOZ_STACK_CLASS nsCSSParser {
 public:
@@ -72,25 +72,24 @@ public:
    * @param aInput the data to parse
    * @param aSheetURL the URI to use as the sheet URI (for error reporting).
    *                  This must match the URI of the sheet passed to
    *                  SetStyleSheet.
    * @param aBaseURI the URI to use for relative URI resolution
    * @param aSheetPrincipal the principal of the stylesheet.  This must match
    *                        the principal of the sheet passed to SetStyleSheet.
    * @param aLineNumber the line number of the first line of the sheet.
-   * @param aAllowUnsafeRules see aEnableUnsafeRules in
-   *                          mozilla::css::Loader::LoadSheetSync
+   * @param aParsingMode  see SheetParsingMode in css/Loader.h
    */
   nsresult ParseSheet(const nsAString& aInput,
                       nsIURI*          aSheetURL,
                       nsIURI*          aBaseURI,
                       nsIPrincipal*    aSheetPrincipal,
                       uint32_t         aLineNumber,
-                      bool             aAllowUnsafeRules);
+                      mozilla::css::SheetParsingMode aParsingMode);
 
   // Parse HTML style attribute or its equivalent in other markup
   // languages.  aBaseURL is the base url to use for relative links in
   // the declaration.
   nsresult ParseStyleAttribute(const nsAString&  aAttributeValue,
                                nsIURI*           aDocURL,
                                nsIURI*           aBaseURL,
                                nsIPrincipal*     aNodePrincipal,
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -14,16 +14,17 @@
 #include "nsIFile.h"
 #include "nsNetUtil.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsPrintfCString.h"
 
 using namespace mozilla;
+using namespace mozilla::css;
 
 static bool sNumberControlEnabled;
 
 #define NUMBER_CONTROL_PREF "dom.forms.number"
 
 NS_IMPL_ISUPPORTS(
   nsLayoutStylesheetCache, nsIObserver, nsIMemoryReporter)
 
@@ -54,48 +55,48 @@ nsLayoutStylesheetCache::Observe(nsISupp
 CSSStyleSheet*
 nsLayoutStylesheetCache::ScrollbarsSheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mScrollbarsSheet) {
     // Scrollbars don't need access to unsafe rules
     LoadSheetURL("chrome://global/skin/scrollbars.css",
-                 gStyleCache->mScrollbarsSheet, false);
+                 gStyleCache->mScrollbarsSheet, eAuthorSheetFeatures);
   }
 
   return gStyleCache->mScrollbarsSheet;
 }
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::FormsSheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mFormsSheet) {
     // forms.css needs access to unsafe rules
     LoadSheetURL("resource://gre-resources/forms.css",
-                 gStyleCache->mFormsSheet, true);
+                 gStyleCache->mFormsSheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mFormsSheet;
 }
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::NumberControlSheet()
 {
   EnsureGlobal();
 
   if (!sNumberControlEnabled) {
     return nullptr;
   }
 
   if (!gStyleCache->mNumberControlSheet) {
     LoadSheetURL("resource://gre-resources/number-control.css",
-                 gStyleCache->mNumberControlSheet, true);
+                 gStyleCache->mNumberControlSheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mNumberControlSheet;
 }
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::UserContentSheet()
 {
@@ -112,30 +113,30 @@ nsLayoutStylesheetCache::UserChromeSheet
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::UASheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mUASheet) {
     LoadSheetURL("resource://gre-resources/ua.css",
-                 gStyleCache->mUASheet, true);
+                 gStyleCache->mUASheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mUASheet;
 }
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::HTMLSheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mHTMLSheet) {
     LoadSheetURL("resource://gre-resources/html.css",
-                 gStyleCache->mHTMLSheet, true);
+                 gStyleCache->mHTMLSheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mHTMLSheet;
 }
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::MinimalXULSheet()
 {
@@ -173,17 +174,17 @@ nsLayoutStylesheetCache::SVGSheet()
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::MathMLSheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mMathMLSheet) {
     LoadSheetURL("resource://gre-resources/mathml.css",
-                 gStyleCache->mMathMLSheet, true);
+                 gStyleCache->mMathMLSheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mMathMLSheet;
 }
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::CounterStylesSheet()
 {
@@ -194,30 +195,30 @@ nsLayoutStylesheetCache::CounterStylesSh
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::NoScriptSheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mNoScriptSheet) {
     LoadSheetURL("resource://gre-resources/noscript.css",
-                 gStyleCache->mNoScriptSheet, true);
+                 gStyleCache->mNoScriptSheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mNoScriptSheet;
 }
 
 CSSStyleSheet*
 nsLayoutStylesheetCache::NoFramesSheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mNoFramesSheet) {
     LoadSheetURL("resource://gre-resources/noframes.css",
-                 gStyleCache->mNoFramesSheet, true);
+                 gStyleCache->mNoFramesSheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mNoFramesSheet;
 }
 
 /* static */ CSSStyleSheet*
 nsLayoutStylesheetCache::ChromePreferenceSheet(nsPresContext* aPresContext)
 {
@@ -246,30 +247,30 @@ nsLayoutStylesheetCache::ContentPreferen
 
 /* static */ CSSStyleSheet*
 nsLayoutStylesheetCache::ContentEditableSheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mContentEditableSheet) {
     LoadSheetURL("resource://gre/res/contenteditable.css",
-                 gStyleCache->mContentEditableSheet, true);
+                 gStyleCache->mContentEditableSheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mContentEditableSheet;
 }
 
 /* static */ CSSStyleSheet*
 nsLayoutStylesheetCache::DesignModeSheet()
 {
   EnsureGlobal();
 
   if (!gStyleCache->mDesignModeSheet) {
     LoadSheetURL("resource://gre/res/designmode.css",
-                 gStyleCache->mDesignModeSheet, true);
+                 gStyleCache->mDesignModeSheet, eAgentSheetFeatures);
   }
 
   return gStyleCache->mDesignModeSheet;
 }
 
 void
 nsLayoutStylesheetCache::Shutdown()
 {
@@ -338,27 +339,27 @@ nsLayoutStylesheetCache::nsLayoutStylesh
     obsSvc->AddObserver(this, "chrome-flush-caches", false);
   }
 
   InitFromProfile();
 
   // And make sure that we load our UA sheets.  No need to do this
   // per-profile, since they're profile-invariant.
   LoadSheetURL("resource://gre-resources/counterstyles.css",
-               mCounterStylesSheet, true);
+               mCounterStylesSheet, eAgentSheetFeatures);
   LoadSheetURL("resource://gre-resources/full-screen-override.css",
-               mFullScreenOverrideSheet, true);
+               mFullScreenOverrideSheet, eAgentSheetFeatures);
   LoadSheetURL("chrome://global/content/minimal-xul.css",
-               mMinimalXULSheet, true);
+               mMinimalXULSheet, eAgentSheetFeatures);
   LoadSheetURL("resource://gre-resources/quirk.css",
-               mQuirkSheet, true);
+               mQuirkSheet, eAgentSheetFeatures);
   LoadSheetURL("resource://gre/res/svg.css",
-               mSVGSheet, true);
+               mSVGSheet, eAgentSheetFeatures);
   LoadSheetURL("chrome://global/content/xul.css",
-               mXULSheet, true);
+               mXULSheet, eAgentSheetFeatures);
 
   // The remaining sheets are created on-demand do to their use being rarer
   // (which helps save memory for Firefox OS apps) or because they need to
   // be re-loadable in DependentPrefChanged.
 }
 
 nsLayoutStylesheetCache::~nsLayoutStylesheetCache()
 {
@@ -415,79 +416,81 @@ nsLayoutStylesheetCache::InitFromProfile
   }
 
   contentFile->Clone(getter_AddRefs(chromeFile));
   if (!chromeFile) return;
 
   contentFile->Append(NS_LITERAL_STRING("userContent.css"));
   chromeFile->Append(NS_LITERAL_STRING("userChrome.css"));
 
-  LoadSheetFile(contentFile, mUserContentSheet);
-  LoadSheetFile(chromeFile, mUserChromeSheet);
+  LoadSheetFile(contentFile, mUserContentSheet, eUserSheetFeatures);
+  LoadSheetFile(chromeFile, mUserChromeSheet, eUserSheetFeatures);
 }
 
 /* static */ void
 nsLayoutStylesheetCache::LoadSheetURL(const char* aURL,
                                       nsRefPtr<CSSStyleSheet>& aSheet,
-                                      bool aEnableUnsafeRules)
+                                      SheetParsingMode aParsingMode)
 {
   nsCOMPtr<nsIURI> uri;
   NS_NewURI(getter_AddRefs(uri), aURL);
-  LoadSheet(uri, aSheet, aEnableUnsafeRules);
+  LoadSheet(uri, aSheet, aParsingMode);
   if (!aSheet) {
     NS_ERROR(nsPrintfCString("Could not load %s", aURL).get());
   }
 }
 
 void
-nsLayoutStylesheetCache::LoadSheetFile(nsIFile* aFile, nsRefPtr<CSSStyleSheet>& aSheet)
+nsLayoutStylesheetCache::LoadSheetFile(nsIFile* aFile,
+                                       nsRefPtr<CSSStyleSheet>& aSheet,
+                                       SheetParsingMode aParsingMode)
 {
   bool exists = false;
   aFile->Exists(&exists);
 
   if (!exists) return;
 
   nsCOMPtr<nsIURI> uri;
   NS_NewFileURI(getter_AddRefs(uri), aFile);
 
-  LoadSheet(uri, aSheet, false);
+  LoadSheet(uri, aSheet, aParsingMode);
 }
 
 static void
 ErrorLoadingBuiltinSheet(nsIURI* aURI, const char* aMsg)
 {
   nsAutoCString spec;
   if (aURI) {
     aURI->GetSpec(spec);
   }
   NS_RUNTIMEABORT(nsPrintfCString("%s loading built-in stylesheet '%s'",
                                   aMsg, spec.get()).get());
 }
 
 void
 nsLayoutStylesheetCache::LoadSheet(nsIURI* aURI,
                                    nsRefPtr<CSSStyleSheet>& aSheet,
-                                   bool aEnableUnsafeRules)
+                                   SheetParsingMode aParsingMode)
 {
   if (!aURI) {
     ErrorLoadingBuiltinSheet(aURI, "null URI");
     return;
   }
 
   if (!gCSSLoader) {
     gCSSLoader = new mozilla::css::Loader();
     NS_IF_ADDREF(gCSSLoader);
     if (!gCSSLoader) {
       ErrorLoadingBuiltinSheet(aURI, "no Loader");
       return;
     }
   }
 
 
-  nsresult rv = gCSSLoader->LoadSheetSync(aURI, aEnableUnsafeRules, true,
+  nsresult rv = gCSSLoader->LoadSheetSync(aURI, aParsingMode, true,
                                           getter_AddRefs(aSheet));
   if (NS_FAILED(rv)) {
     ErrorLoadingBuiltinSheet(aURI,
       nsPrintfCString("LoadSheetSync failed with error %x", rv).get());
   }
 }
 
 /* static */ void
--- a/layout/style/nsLayoutStylesheetCache.h
+++ b/layout/style/nsLayoutStylesheetCache.h
@@ -8,25 +8,23 @@
 #define nsLayoutStylesheetCache_h__
 
 #include "nsIMemoryReporter.h"
 #include "nsIObserver.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/css/Loader.h"
 
 class nsIFile;
 class nsIURI;
 
 namespace mozilla {
 class CSSStyleSheet;
-namespace css {
-class Loader;
-} // namespace css
 } // namespace mozilla
 
 class nsLayoutStylesheetCache final
  : public nsIObserver
  , public nsIMemoryReporter
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -65,21 +63,22 @@ private:
   nsLayoutStylesheetCache();
   ~nsLayoutStylesheetCache();
 
   static void EnsureGlobal();
   void InitFromProfile();
   void InitMemoryReporter();
   static void LoadSheetURL(const char* aURL,
                            nsRefPtr<mozilla::CSSStyleSheet>& aSheet,
-                           bool aEnableUnsafeRules);
+                           mozilla::css::SheetParsingMode aParsingMode);
   static void LoadSheetFile(nsIFile* aFile,
-                            nsRefPtr<mozilla::CSSStyleSheet>& aSheet);
+                            nsRefPtr<mozilla::CSSStyleSheet>& aSheet,
+                            mozilla::css::SheetParsingMode aParsingMode);
   static void LoadSheet(nsIURI* aURI, nsRefPtr<mozilla::CSSStyleSheet>& aSheet,
-                        bool aEnableUnsafeRules);
+                        mozilla::css::SheetParsingMode aParsingMode);
   static void InvalidateSheet(nsRefPtr<mozilla::CSSStyleSheet>& aSheet);
   static void DependentPrefChanged(const char* aPref, void* aData);
   void BuildPreferenceSheet(nsRefPtr<mozilla::CSSStyleSheet>& aSheet,
                             nsPresContext* aPresContext);
   static void AppendPreferenceRule(mozilla::CSSStyleSheet* aSheet,
                                    const nsAString& aRule);
   static void AppendPreferenceColorRule(mozilla::CSSStyleSheet* aSheet,
                                         const char* aString, nscolor aColor);