Bug 1028497 - Part 4: Move the nsUserFontSet object into FontFaceSet. r=jdaggett
authorCameron McCormack <cam@mcc.id.au>
Thu, 02 Oct 2014 12:32:05 +1000
changeset 232851 54ded49a16876d77c49567fb6d6419e167275775
parent 232850 1c827b73c45f272b36ec07e1f1660c07c07febd4
child 232852 399d1fef175824ee7fb0c1686e022f5942f12765
push id611
push userraliiev@mozilla.com
push dateMon, 05 Jan 2015 23:23:16 +0000
treeherdermozilla-release@345cd3b9c445 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs1028497
milestone35.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 1028497 - Part 4: Move the nsUserFontSet object into FontFaceSet. r=jdaggett This (a) moves ownership of the gfxUserFontSet into FontFaceSet, (b) moves nearly all of the functionality of nsUserFontSet into FontFaceSet, and (c) adds a class that inherits from gfxUserFontSet so that we can override some of its virtual functions and delegate them to the FontFaceSet.
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/gfxUserFontSet.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/inspector/nsFontFace.cpp
layout/style/FontFaceSet.cpp
layout/style/FontFaceSet.h
layout/style/nsFontFaceLoader.cpp
layout/style/nsFontFaceLoader.h
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -259,16 +259,34 @@ gfxUserFontEntry::StoreUserFontData(gfxF
     userFontData->mFormat = src.mFormatFlags;
     userFontData->mRealName = aOriginalName;
     if (aMetadata) {
         userFontData->mMetadata.SwapElements(*aMetadata);
         userFontData->mMetaOrigLen = aMetaOrigLen;
     }
 }
 
+void
+gfxUserFontEntry::GetFamilyNameAndURIForLogging(nsACString& aFamilyName,
+                                                nsACString& aURI)
+{
+  aFamilyName.Assign(NS_ConvertUTF16toUTF8(mFamilyName));
+
+  aURI.Truncate();
+  if (mSrcIndex == mSrcList.Length()) {
+    aURI.AppendLiteral("(end of source list)");
+  } else {
+    if (mSrcList[mSrcIndex].mURI) {
+      mSrcList[mSrcIndex].mURI->GetSpec(aURI);
+    } else {
+      aURI.AppendLiteral("(invalid URI)");
+    }
+  }
+}
+
 struct WOFFHeader {
     AutoSwap_PRUint32 signature;
     AutoSwap_PRUint32 flavor;
     AutoSwap_PRUint32 length;
     AutoSwap_PRUint16 numTables;
     AutoSwap_PRUint16 reserved;
     AutoSwap_PRUint32 totalSfntSize;
     AutoSwap_PRUint16 majorVersion;
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -519,16 +519,25 @@ public:
         return mUserFontLoadState == STATUS_LOADING &&
                mFontDataLoadingState < LOADING_SLOWLY;
     }
 
     // load the font - starts the loading of sources which continues until
     // a valid font resource is found or all sources fail
     void Load();
 
+    // methods to expose some information to FontFaceSet::UserFontSet
+    // since we can't make that class a friend
+    void SetLoader(nsFontFaceLoader* aLoader) { mLoader = aLoader; }
+    nsFontFaceLoader* GetLoader() { return mLoader; }
+    nsIPrincipal* GetPrincipal() { return mPrincipal; }
+    uint32_t GetSrcIndex() { return mSrcIndex; }
+    void GetFamilyNameAndURIForLogging(nsACString& aFamilyName,
+                                       nsACString& aURI);
+
 protected:
     const uint8_t* SanitizeOpenTypeData(const uint8_t* aData,
                                         uint32_t aLength,
                                         uint32_t& aSaneLength,
                                         bool aIsCompressed);
 
     // attempt to load the next resource in the src list.
     void LoadNextSrc();
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -55,17 +55,17 @@
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
 #include "nsRefreshDriver.h"
 #include "Layers.h"
 #include "nsIDOMEvent.h"
 #include "gfxPrefs.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsFrameLoader.h"
-
+#include "mozilla/dom/FontFaceSet.h"
 #include "nsContentUtils.h"
 #include "nsPIWindowRoot.h"
 #include "mozilla/Preferences.h"
 
 // Needed for Start/Stop of Image Animation
 #include "imgIContainer.h"
 #include "nsIImageLoadingContent.h"
 
@@ -232,18 +232,17 @@ nsPresContext::nsPresContext(nsIDocument
   if (!IsDynamic()) {
     mImageAnimationMode = imgIContainer::kDontAnimMode;
     mNeverAnimate = true;
   } else {
     mImageAnimationMode = imgIContainer::kNormalAnimMode;
     mNeverAnimate = false;
   }
   NS_ASSERTION(mDocument, "Null document");
-  mUserFontSet = nullptr;
-  mUserFontSetDirty = true;
+  mFontFaceSetDirty = true;
 
   mCounterStylesDirty = true;
 
   // if text perf logging enabled, init stats struct
   PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textperf);
   if (log && log->level >= PR_LOG_WARNING) {
     mTextPerf = new gfxTextPerfMetrics();
   }
@@ -1089,20 +1088,20 @@ nsPresContext::Init(nsDeviceContext* aDe
   return NS_OK;
 }
 
 // Note: We don't hold a reference on the shell; it has a reference to
 // us
 void
 nsPresContext::SetShell(nsIPresShell* aShell)
 {
-  if (mUserFontSet) {
+  if (mFontFaceSet) {
     // Clear out user font set if we have one
-    mUserFontSet->Destroy();
-    NS_RELEASE(mUserFontSet);
+    mFontFaceSet->DestroyUserFontSet();
+    mFontFaceSet = nullptr;
   }
 
   if (mShell) {
     // Remove ourselves as the charset observer from the shell's doc, because
     // this shell may be going away for good.
     nsIDocument *doc = mShell->GetDocument();
     if (doc) {
       doc->RemoveCharSetObserver(this);
@@ -2063,29 +2062,33 @@ nsPresContext::GetUserFontSetInternal()
   // for somebody to call GetUserFontSet in order to rebuild it (see
   // comments below in RebuildUserFontSet for why).
 #ifdef DEBUG
   bool userFontSetGottenBefore = mGetUserFontSetCalled;
 #endif
   // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually
   // flush.
   mGetUserFontSetCalled = true;
-  if (mUserFontSetDirty) {
+  if (mFontFaceSetDirty) {
     // If this assertion fails, and there have actually been changes to
     // @font-face rules, then we will call StyleChangeReflow in
     // FlushUserFontSet.  If we're in the middle of reflow,
     // that's a bad thing to do, and the caller was responsible for
     // flushing first.  If we're not (e.g., in frame construction), it's
     // ok.
     NS_ASSERTION(!userFontSetGottenBefore || !mShell->IsReflowLocked(),
                  "FlushUserFontSet should have been called first");
     FlushUserFontSet();
   }
 
-  return mUserFontSet;
+  if (!mFontFaceSet) {
+    return nullptr;
+  }
+
+  return mFontFaceSet->GetUserFontSet();
 }
 
 gfxUserFontSet*
 nsPresContext::GetUserFontSetExternal()
 {
   return GetUserFontSetInternal();
 }
 
@@ -2093,71 +2096,69 @@ void
 nsPresContext::FlushUserFontSet()
 {
   if (!mShell) {
     return; // we've been torn down
   }
 
   if (!mGetUserFontSetCalled) {
     return; // No one cares about this font set yet, but we want to be careful
-            // to not unset our mUserFontSetDirty bit, so when someone really
+            // to not unset our mFontFaceSetDirty bit, so when someone really
             // does we'll create it.
   }
 
-  if (mUserFontSetDirty) {
+  if (mFontFaceSetDirty) {
     if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
       nsTArray<nsFontFaceRuleContainer> rules;
       if (!mShell->StyleSet()->AppendFontFaceRules(this, rules)) {
-        if (mUserFontSet) {
-          mUserFontSet->Destroy();
-          NS_RELEASE(mUserFontSet);
+        if (mFontFaceSet) {
+          mFontFaceSet->DestroyUserFontSet();
         }
         return;
       }
 
       bool changed = false;
 
       if (rules.Length() == 0) {
-        if (mUserFontSet) {
-          mUserFontSet->Destroy();
-          NS_RELEASE(mUserFontSet);
+        if (mFontFaceSet) {
+          mFontFaceSet->DestroyUserFontSet();
           changed = true;
         }
       } else {
-        if (!mUserFontSet) {
-          mUserFontSet = new nsUserFontSet(this);
-          NS_ADDREF(mUserFontSet);
+        if (!mFontFaceSet) {
+          mFontFaceSet = new FontFaceSet(mDocument->GetInnerWindow(), this);
         }
-        changed = mUserFontSet->UpdateRules(rules);
+        mFontFaceSet->EnsureUserFontSet(this);
+        changed = mFontFaceSet->UpdateRules(rules);
       }
 
       // We need to enqueue a style change reflow (for later) to
       // reflect that we're modifying @font-face rules.  (However,
       // without a reflow, nothing will happen to start any downloads
       // that are needed.)
       if (changed) {
         UserFontSetUpdated();
       }
     }
 
-    mUserFontSetDirty = false;
+    mFontFaceSetDirty = false;
   }
 }
 
 void
 nsPresContext::RebuildUserFontSet()
 {
   if (!mGetUserFontSetCalled) {
     // We want to lazily build the user font set the first time it's
     // requested (so we don't force creation of rule cascades too
     // early), so don't do anything now.
     return;
   }
 
-  mUserFontSetDirty = true;
+  mFontFaceSetDirty = true;
   mDocument->SetNeedStyleFlush();
 
   // Somebody has already asked for the user font set, so we need to
   // post an event to rebuild it.  Setting the user font set to be dirty
   // and lazily rebuilding it isn't sufficient, since it is only the act
   // of rebuilding it that will trigger the style change reflow that
   // calls GetUserFontSet.  (This reflow causes rebuilding of text runs,
   // which starts font loads, whose completion causes another style
@@ -2187,16 +2188,26 @@ nsPresContext::UserFontSetUpdated()
   //   2. Changing the value of the 'ex' and 'ch' units in style data,
   //      which also depend on font metrics.  Updating this information
   //      requires rebuilding the rule tree from the top, avoiding the
   //      reuse of cached data even when no style rules have changed.
 
   PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW);
 }
 
+FontFaceSet*
+nsPresContext::Fonts()
+{
+  if (!mFontFaceSet) {
+    mFontFaceSet = new FontFaceSet(mDocument->GetInnerWindow(), this);
+    GetUserFontSet();  // this will cause the user font set to be created/updated
+  }
+  return mFontFaceSet;
+}
+
 void
 nsPresContext::FlushCounterStyles()
 {
   if (!mShell) {
     return; // we've been torn down
   }
   if (mCounterStyleManager->IsInitial()) {
     // Still in its initial state, no need to clean.
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -53,30 +53,30 @@ class nsFrameManager;
 class nsILinkHandler;
 class nsIAtom;
 class nsICSSPseudoComparator;
 struct nsStyleBackground;
 struct nsStyleBorder;
 class nsIRunnable;
 class gfxUserFontSet;
 class gfxTextPerfMetrics;
-class nsUserFontSet;
 struct nsFontFaceRuleContainer;
 class nsPluginFrame;
 class nsTransitionManager;
 class nsAnimationManager;
 class nsRefreshDriver;
 class nsIWidget;
 class nsDeviceContext;
 
 namespace mozilla {
 class EventStateManager;
 class RestyleManager;
 class CounterStyleManager;
 namespace dom {
+class FontFaceSet;
 class MediaQueryList;
 }
 namespace layers {
 class ContainerLayer;
 }
 }
 
 // supported values for cached bool types
@@ -866,16 +866,18 @@ public:
   void FlushUserFontSet();
   void RebuildUserFontSet(); // asynchronously
 
   // Should be called whenever the set of fonts available in the user
   // font set changes (e.g., because a new font loads, or because the
   // user font set is changed and fonts become unavailable).
   void UserFontSetUpdated();
 
+  mozilla::dom::FontFaceSet* Fonts();
+
   void FlushCounterStyles();
   void RebuildCounterStyles(); // asynchronously
 
   // Ensure that it is safe to hand out CSS rules outside the layout
   // engine by ensuring that all CSS style sheets have unique inners
   // and, if necessary, synchronously rebuilding all style data.
   void EnsureSafeToHandOutCSSRules();
 
@@ -1242,17 +1244,17 @@ protected:
   nsCOMPtr<nsITimer>    mPrefChangedTimer;
 
   FramePropertyTable    mPropertyTable;
 
   nsInvalidateRequestList mInvalidateRequestsSinceLastPaint;
   nsInvalidateRequestList mUndeliveredInvalidateRequestsBeforeLastPaint;
 
   // container for per-context fonts (downloadable, SVG, etc.)
-  nsUserFontSet*        mUserFontSet;
+  nsRefPtr<mozilla::dom::FontFaceSet> mFontFaceSet;
 
   // text performance metrics
   nsAutoPtr<gfxTextPerfMetrics>   mTextPerf;
 
   nsRect                mVisibleArea;
   nsSize                mPageSize;
   float                 mPageScale;
   float                 mPPScale;
@@ -1323,18 +1325,18 @@ protected:
   // Does the associated document use root-em (rem) units?
   unsigned              mUsesRootEMUnits : 1;
   // Does the associated document use viewport units (vw/vh/vmin/vmax)?
   unsigned              mUsesViewportUnits : 1;
 
   // Has there been a change to the viewport's dimensions?
   unsigned              mPendingViewportChange : 1;
 
-  // Is the current mUserFontSet valid?
-  unsigned              mUserFontSetDirty : 1;
+  // Is the current mFontFaceSet valid?
+  unsigned              mFontFaceSetDirty : 1;
   // Has GetUserFontSet() been called?
   unsigned              mGetUserFontSetCalled : 1;
   // Do we currently have an event posted to call FlushUserFontSet?
   unsigned              mPostedFlushUserFontSet : 1;
 
   // Is the current mCounterStyleManager valid?
   unsigned              mCounterStylesDirty : 1;
   // Do we currently have an event posted to call FlushCounterStyles?
--- a/layout/inspector/nsFontFace.cpp
+++ b/layout/inspector/nsFontFace.cpp
@@ -5,16 +5,20 @@
 #include "nsFontFace.h"
 #include "nsIDOMCSSFontFaceRule.h"
 #include "nsCSSRules.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 #include "nsFontFaceLoader.h"
 #include "mozilla/gfx/2D.h"
 #include "zlib.h"
+#include "mozilla/dom/FontFaceSet.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
 
 nsFontFace::nsFontFace(gfxFontEntry*      aFontEntry,
                        gfxFontGroup*      aFontGroup,
                        uint8_t            aMatchType)
   : mFontEntry(aFontEntry),
     mFontGroup(aFontGroup),
     mMatchType(aMatchType)
 {
@@ -83,20 +87,23 @@ nsFontFace::GetCSSFamilyName(nsAString &
 /* readonly attribute nsIDOMCSSFontFaceRule rule; */
 NS_IMETHODIMP
 nsFontFace::GetRule(nsIDOMCSSFontFaceRule **aRule)
 {
   // check whether this font entry is associated with an @font-face rule
   // in the relevant font group's user font set
   nsCSSFontFaceRule* rule = nullptr;
   if (mFontEntry->IsUserFont()) {
-    nsUserFontSet* fontSet =
-      static_cast<nsUserFontSet*>(mFontGroup->GetUserFontSet());
+    FontFaceSet::UserFontSet* fontSet =
+      static_cast<FontFaceSet::UserFontSet*>(mFontGroup->GetUserFontSet());
     if (fontSet) {
-      rule = fontSet->FindRuleForEntry(mFontEntry);
+      FontFaceSet* fontFaceSet = fontSet->GetFontFaceSet();
+      if (fontFaceSet) {
+        rule = fontFaceSet->FindRuleForEntry(mFontEntry);
+      }
     }
   }
 
   NS_IF_ADDREF(*aRule = rule);
   return NS_OK;
 }
 
 /* readonly attribute long srcIndex; */
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -1,47 +1,99 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "FontFaceSet.h"
 
+#ifdef MOZ_LOGGING
+#define FORCE_PR_LOG /* Allow logging in the release build */
+#endif /* MOZ_LOGGING */
+#include "prlog.h"
+
 #include "mozilla/dom/FontFaceSetBinding.h"
 #include "mozilla/dom/Promise.h"
+#include "nsCrossSiteListenerProxy.h"
+#include "nsFontFaceLoader.h"
+#include "nsIChannelPolicy.h"
+#include "nsIConsoleService.h"
+#include "nsIContentPolicy.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIDocShell.h"
+#include "nsIDocument.h"
+#include "nsINetworkPredictor.h"
+#include "nsIPresShell.h"
+#include "nsIPrincipal.h"
+#include "nsISupportsPriority.h"
+#include "nsIWebNavigation.h"
+#include "nsNetUtil.h"
+#include "nsPresContext.h"
+#include "nsPrintfCString.h"
+#include "nsStyleSet.h"
 
+using namespace mozilla;
 using namespace mozilla::dom;
 
+#ifdef PR_LOGGING
+static PRLogModuleInfo*
+GetFontFaceSetLog()
+{
+  static PRLogModuleInfo* sLog;
+  if (!sLog)
+    sLog = PR_NewLogModule("fontfaceset");
+  return sLog;
+}
+#endif /* PR_LOGGING */
+
+#define LOG(args) PR_LOG(GetFontFaceSetLog(), PR_LOG_DEBUG, args)
+#define LOG_ENABLED() PR_LOG_TEST(GetFontFaceSetLog(), PR_LOG_DEBUG)
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED(FontFaceSet,
                                    DOMEventTargetHelper,
                                    mReady)
 
 NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FontFaceSet)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-FontFaceSet::FontFaceSet(nsPIDOMWindow* aWindow)
+FontFaceSet::FontFaceSet(nsPIDOMWindow* aWindow, nsPresContext* aPresContext)
   : DOMEventTargetHelper(aWindow)
+  , mPresContext(aPresContext)
 {
   MOZ_COUNT_CTOR(FontFaceSet);
+
+  MOZ_ASSERT(mPresContext);
 }
 
 FontFaceSet::~FontFaceSet()
 {
   MOZ_COUNT_DTOR(FontFaceSet);
+
+  NS_ASSERTION(mLoaders.Count() == 0, "mLoaders should have been emptied");
 }
 
 JSObject*
 FontFaceSet::WrapObject(JSContext* aContext)
 {
   return FontFaceSetBinding::Wrap(aContext, this);
 }
 
+FontFaceSet::UserFontSet*
+FontFaceSet::EnsureUserFontSet(nsPresContext* aPresContext)
+{
+  if (!mUserFontSet) {
+    mUserFontSet = new UserFontSet(this);
+    mPresContext = aPresContext;
+  }
+  return mUserFontSet;
+}
+
 already_AddRefed<Promise>
 FontFaceSet::Load(const nsAString& aFont,
                   const nsAString& aText,
                   ErrorResult& aRv)
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   return nullptr;
 }
@@ -102,8 +154,836 @@ FontFaceSet::IndexedGetter(uint32_t aInd
   return nullptr;
 }
 
 uint32_t
 FontFaceSet::Length()
 {
   return 0;
 }
+
+static PLDHashOperator DestroyIterator(nsPtrHashKey<nsFontFaceLoader>* aKey,
+                                       void* aUserArg)
+{
+  aKey->GetKey()->Cancel();
+  return PL_DHASH_REMOVE;
+}
+
+void
+FontFaceSet::DestroyUserFontSet()
+{
+  mPresContext = nullptr;
+  mLoaders.EnumerateEntries(DestroyIterator, nullptr);
+  mRules.Clear();
+  mUserFontSet = nullptr;
+}
+
+void
+FontFaceSet::RemoveLoader(nsFontFaceLoader* aLoader)
+{
+  mLoaders.RemoveEntry(aLoader);
+}
+
+nsresult
+FontFaceSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
+                       const gfxFontFaceSrc* aFontFaceSrc)
+{
+  nsresult rv;
+
+  nsIPresShell* ps = mPresContext->PresShell();
+  if (!ps)
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIStreamLoader> streamLoader;
+  nsCOMPtr<nsILoadGroup> loadGroup(ps->GetDocument()->GetDocumentLoadGroup());
+
+  nsCOMPtr<nsIChannel> channel;
+  // get Content Security Policy from principal to pass into channel
+  nsCOMPtr<nsIChannelPolicy> channelPolicy;
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  rv = aUserFontEntry->GetPrincipal()->GetCsp(getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (csp) {
+    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
+    channelPolicy->SetContentSecurityPolicy(csp);
+    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_FONT);
+  }
+  // Note we are calling NS_NewChannelInternal() with both a node and a
+  // principal.  This is because the document where the font is being loaded
+  // might have a different origin from the principal of the stylesheet
+  // that initiated the font load.
+  rv = NS_NewChannelInternal(getter_AddRefs(channel),
+                             aFontFaceSrc->mURI,
+                             ps->GetDocument(),
+                             aUserFontEntry->GetPrincipal(),
+                             nsILoadInfo::SEC_NORMAL,
+                             nsIContentPolicy::TYPE_FONT,
+                             channelPolicy,
+                             loadGroup);
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsFontFaceLoader> fontLoader =
+    new nsFontFaceLoader(aUserFontEntry, aFontFaceSrc->mURI, this, channel);
+
+  if (!fontLoader)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+#ifdef PR_LOGGING
+  if (LOG_ENABLED()) {
+    nsAutoCString fontURI, referrerURI;
+    aFontFaceSrc->mURI->GetSpec(fontURI);
+    if (aFontFaceSrc->mReferrer)
+      aFontFaceSrc->mReferrer->GetSpec(referrerURI);
+    LOG(("fontdownloader (%p) download start - font uri: (%s) "
+         "referrer uri: (%s)\n",
+         fontLoader.get(), fontURI.get(), referrerURI.get()));
+  }
+#endif
+
+  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+  if (httpChannel)
+    httpChannel->SetReferrer(aFontFaceSrc->mReferrer);
+  nsCOMPtr<nsISupportsPriority> priorityChannel(do_QueryInterface(channel));
+  if (priorityChannel) {
+    priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGH);
+  }
+
+  rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsIDocument *document = ps->GetDocument();
+  mozilla::net::PredictorLearn(aFontFaceSrc->mURI, document->GetDocumentURI(),
+                               nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
+                               loadGroup);
+
+  bool inherits = false;
+  rv = NS_URIChainHasFlags(aFontFaceSrc->mURI,
+                           nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
+                           &inherits);
+  if (NS_SUCCEEDED(rv) && inherits) {
+    // allow data, javascript, etc URI's
+    rv = channel->AsyncOpen(streamLoader, nullptr);
+  } else {
+    nsRefPtr<nsCORSListenerProxy> listener =
+      new nsCORSListenerProxy(streamLoader, aUserFontEntry->GetPrincipal(), false);
+    rv = listener->Init(channel);
+    if (NS_SUCCEEDED(rv)) {
+      rv = channel->AsyncOpen(listener, nullptr);
+    }
+    if (NS_FAILED(rv)) {
+      fontLoader->DropChannel();  // explicitly need to break ref cycle
+    }
+  }
+
+  if (NS_SUCCEEDED(rv)) {
+    mLoaders.PutEntry(fontLoader);
+    fontLoader->StartedLoading(streamLoader);
+    aUserFontEntry->SetLoader(fontLoader); // let the font entry remember the
+                                           // loader, in case we need to cancel it
+  }
+
+  return rv;
+}
+
+static PLDHashOperator DetachFontEntries(const nsAString& aKey,
+                                         nsRefPtr<gfxUserFontFamily>& aFamily,
+                                         void* aUserArg)
+{
+  aFamily->DetachFontEntries();
+  return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator RemoveIfEmpty(const nsAString& aKey,
+                                     nsRefPtr<gfxUserFontFamily>& aFamily,
+                                     void* aUserArg)
+{
+  return aFamily->GetFontList().Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
+}
+
+bool
+FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
+{
+  MOZ_ASSERT(mUserFontSet);
+
+  bool modified = false;
+
+  // The @font-face rules that make up the user font set have changed,
+  // so we need to update the set. However, we want to preserve existing
+  // font entries wherever possible, so that we don't discard and then
+  // re-download resources in the (common) case where at least some of the
+  // same rules are still present.
+
+  nsTArray<FontFaceRuleRecord> oldRules;
+  mRules.SwapElements(oldRules);
+
+  // Remove faces from the font family records; we need to re-insert them
+  // because we might end up with faces in a different order even if they're
+  // the same font entries as before. (The order can affect font selection
+  // where multiple faces match the requested style, perhaps with overlapping
+  // unicode-range coverage.)
+  mUserFontSet->mFontFamilies.Enumerate(DetachFontEntries, nullptr);
+
+  for (uint32_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
+    // Insert each rule into our list, migrating old font entries if possible
+    // rather than creating new ones; set  modified  to true if we detect
+    // that rule ordering has changed, or if a new entry is created.
+    InsertRule(aRules[i].mRule, aRules[i].mSheetType, oldRules, modified);
+  }
+
+  // Remove any residual families that have no font entries (i.e., they were
+  // not defined at all by the updated set of @font-face rules).
+  mUserFontSet->mFontFamilies.Enumerate(RemoveIfEmpty, nullptr);
+
+  // If any rules are left in the old list, note that the set has changed
+  // (even if the new set was built entirely by migrating old font entries).
+  if (oldRules.Length() > 0) {
+    modified = true;
+    // Any in-progress loaders for obsolete rules should be cancelled,
+    // as the resource being downloaded will no longer be required.
+    // We need to explicitly remove any loaders here, otherwise the loaders
+    // will keep their "orphaned" font entries alive until they complete,
+    // even after the oldRules array is deleted.
+    size_t count = oldRules.Length();
+    for (size_t i = 0; i < count; ++i) {
+      gfxUserFontEntry* userFontEntry = oldRules[i].mUserFontEntry;
+      nsFontFaceLoader* loader = userFontEntry->GetLoader();
+      if (loader) {
+        loader->Cancel();
+        RemoveLoader(loader);
+      }
+    }
+  }
+
+  if (modified) {
+    IncrementGeneration(true);
+  }
+
+  // local rules have been rebuilt, so clear the flag
+  mUserFontSet->mLocalRulesUsed = false;
+
+  return modified;
+}
+
+static bool
+HasLocalSrc(const nsCSSValue::Array *aSrcArr)
+{
+  size_t numSrc = aSrcArr->Count();
+  for (size_t i = 0; i < numSrc; i++) {
+    if (aSrcArr->Item(i).GetUnit() == eCSSUnit_Local_Font) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void
+FontFaceSet::IncrementGeneration(bool aIsRebuild)
+{
+  MOZ_ASSERT(mUserFontSet);
+  mUserFontSet->IncrementGeneration(aIsRebuild);
+}
+
+void
+FontFaceSet::InsertRule(nsCSSFontFaceRule* aRule, uint8_t aSheetType,
+                        nsTArray<FontFaceRuleRecord>& aOldRules,
+                        bool& aFontSetModified)
+{
+  NS_ABORT_IF_FALSE(aRule->GetType() == mozilla::css::Rule::FONT_FACE_RULE,
+                    "InsertRule passed a non-fontface CSS rule");
+
+  // set up family name
+  nsAutoString fontfamily;
+  nsCSSValue val;
+  uint32_t unit;
+
+  aRule->GetDesc(eCSSFontDesc_Family, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_String) {
+    val.GetStringValue(fontfamily);
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null,
+                 "@font-face family name has unexpected unit");
+  }
+  if (fontfamily.IsEmpty()) {
+    // If there is no family name, this rule cannot contribute a
+    // usable font, so there is no point in processing it further.
+    return;
+  }
+
+  // first, we check in oldRules; if the rule exists there, just move it
+  // to the new rule list, and put the entry into the appropriate family
+  for (uint32_t i = 0; i < aOldRules.Length(); ++i) {
+    const FontFaceRuleRecord& ruleRec = aOldRules[i];
+
+    if (ruleRec.mContainer.mRule == aRule &&
+        ruleRec.mContainer.mSheetType == aSheetType) {
+
+      // if local rules were used, don't use the old font entry
+      // for rules containing src local usage
+      if (mUserFontSet->mLocalRulesUsed) {
+        aRule->GetDesc(eCSSFontDesc_Src, val);
+        unit = val.GetUnit();
+        if (unit == eCSSUnit_Array && HasLocalSrc(val.GetArrayValue())) {
+          break;
+        }
+      }
+
+      mUserFontSet->AddFontFace(fontfamily, ruleRec.mUserFontEntry);
+      mRules.AppendElement(ruleRec);
+      aOldRules.RemoveElementAt(i);
+      // note the set has been modified if an old rule was skipped to find
+      // this one - something has been dropped, or ordering changed
+      if (i > 0) {
+        aFontSetModified = true;
+      }
+      return;
+    }
+  }
+
+  // this is a new rule:
+  FontFaceRuleRecord ruleRec;
+  ruleRec.mUserFontEntry =
+    FindOrCreateFontFaceFromRule(fontfamily, aRule, aSheetType);
+
+  if (!ruleRec.mUserFontEntry) {
+    return;
+  }
+
+  ruleRec.mContainer.mRule = aRule;
+  ruleRec.mContainer.mSheetType = aSheetType;
+
+  // Add the entry to the end of the list.  If an existing userfont entry was
+  // returned by FindOrCreateFontFaceFromRule that was already stored on the
+  // family, gfxUserFontFamily::AddFontEntry(), which AddFontFace calls,
+  // will automatically remove the earlier occurrence of the same userfont entry.
+  mUserFontSet->AddFontFace(fontfamily, ruleRec.mUserFontEntry);
+
+  mRules.AppendElement(ruleRec);
+
+  // this was a new rule and font entry, so note that the set was modified
+  aFontSetModified = true;
+}
+
+already_AddRefed<gfxUserFontEntry>
+FontFaceSet::FindOrCreateFontFaceFromRule(const nsAString& aFamilyName,
+                                          nsCSSFontFaceRule* aRule,
+                                          uint8_t aSheetType)
+{
+  nsCSSValue val;
+  uint32_t unit;
+
+  uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL;
+  int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
+  uint32_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
+  uint32_t languageOverride = NO_FONT_LANGUAGE_OVERRIDE;
+
+  // set up weight
+  aRule->GetDesc(eCSSFontDesc_Weight, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_Integer || unit == eCSSUnit_Enumerated) {
+    weight = val.GetIntValue();
+    if (weight == 0) {
+      weight = NS_STYLE_FONT_WEIGHT_NORMAL;
+    }
+  } else if (unit == eCSSUnit_Normal) {
+    weight = NS_STYLE_FONT_WEIGHT_NORMAL;
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null,
+                 "@font-face weight has unexpected unit");
+  }
+
+  // set up stretch
+  aRule->GetDesc(eCSSFontDesc_Stretch, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_Enumerated) {
+    stretch = val.GetIntValue();
+  } else if (unit == eCSSUnit_Normal) {
+    stretch = NS_STYLE_FONT_STRETCH_NORMAL;
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null,
+                 "@font-face stretch has unexpected unit");
+  }
+
+  // set up font style
+  aRule->GetDesc(eCSSFontDesc_Style, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_Enumerated) {
+    italicStyle = val.GetIntValue();
+  } else if (unit == eCSSUnit_Normal) {
+    italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null,
+                 "@font-face style has unexpected unit");
+  }
+
+  // set up font features
+  nsTArray<gfxFontFeature> featureSettings;
+  aRule->GetDesc(eCSSFontDesc_FontFeatureSettings, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_Normal) {
+    // empty list of features
+  } else if (unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep) {
+    nsRuleNode::ComputeFontFeatures(val.GetPairListValue(), featureSettings);
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null,
+                 "@font-face font-feature-settings has unexpected unit");
+  }
+
+  // set up font language override
+  aRule->GetDesc(eCSSFontDesc_FontLanguageOverride, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_Normal) {
+    // empty feature string
+  } else if (unit == eCSSUnit_String) {
+    nsString stringValue;
+    val.GetStringValue(stringValue);
+    languageOverride = gfxFontStyle::ParseFontLanguageOverride(stringValue);
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null,
+                 "@font-face font-language-override has unexpected unit");
+  }
+
+  // set up src array
+  nsTArray<gfxFontFaceSrc> srcArray;
+
+  aRule->GetDesc(eCSSFontDesc_Src, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_Array) {
+    nsCSSValue::Array* srcArr = val.GetArrayValue();
+    size_t numSrc = srcArr->Count();
+
+    for (size_t i = 0; i < numSrc; i++) {
+      val = srcArr->Item(i);
+      unit = val.GetUnit();
+      gfxFontFaceSrc* face = srcArray.AppendElements(1);
+      if (!face)
+        return nullptr;
+
+      switch (unit) {
+
+      case eCSSUnit_Local_Font:
+        val.GetStringValue(face->mLocalName);
+        face->mIsLocal = true;
+        face->mURI = nullptr;
+        face->mFormatFlags = 0;
+        break;
+      case eCSSUnit_URL:
+        face->mIsLocal = false;
+        face->mURI = val.GetURLValue();
+        face->mReferrer = val.GetURLStructValue()->mReferrer;
+        face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal;
+        NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
+
+        // agent and user stylesheets are treated slightly differently,
+        // the same-site origin check and access control headers are
+        // enforced against the sheet principal rather than the document
+        // principal to allow user stylesheets to include @font-face rules
+        face->mUseOriginPrincipal = (aSheetType == nsStyleSet::eUserSheet ||
+                                     aSheetType == nsStyleSet::eAgentSheet);
+
+        face->mLocalName.Truncate();
+        face->mFormatFlags = 0;
+        while (i + 1 < numSrc && (val = srcArr->Item(i+1),
+                 val.GetUnit() == eCSSUnit_Font_Format)) {
+          nsDependentString valueString(val.GetStringBufferValue());
+          if (valueString.LowerCaseEqualsASCII("woff")) {
+            face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF;
+          } else if (valueString.LowerCaseEqualsASCII("opentype")) {
+            face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_OPENTYPE;
+          } else if (valueString.LowerCaseEqualsASCII("truetype")) {
+            face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE;
+          } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
+            face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT;
+          } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
+            face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_EOT;
+          } else if (valueString.LowerCaseEqualsASCII("svg")) {
+            face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_SVG;
+          } else {
+            // unknown format specified, mark to distinguish from the
+            // case where no format hints are specified
+            face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_UNKNOWN;
+          }
+          i++;
+        }
+        if (!face->mURI) {
+          // if URI not valid, omit from src array
+          srcArray.RemoveElementAt(srcArray.Length() - 1);
+          NS_WARNING("null url in @font-face rule");
+          continue;
+        }
+        break;
+      default:
+        NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
+                     "strange unit type in font-face src array");
+        break;
+      }
+     }
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
+  }
+
+  if (srcArray.IsEmpty()) {
+    return nullptr;
+  }
+
+  nsRefPtr<gfxUserFontEntry> entry =
+    mUserFontSet->FindOrCreateFontFace(aFamilyName, srcArray, weight, stretch,
+                                       italicStyle, featureSettings,
+                                       languageOverride,
+                                       nullptr /* aUnicodeRanges */);
+  return entry.forget();
+}
+
+nsCSSFontFaceRule*
+FontFaceSet::FindRuleForEntry(gfxFontEntry* aFontEntry)
+{
+  NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
+  for (uint32_t i = 0; i < mRules.Length(); ++i) {
+    if (mRules[i].mUserFontEntry->GetPlatformFontEntry() == aFontEntry) {
+      return mRules[i].mContainer.mRule;
+    }
+  }
+  return nullptr;
+}
+
+nsCSSFontFaceRule*
+FontFaceSet::FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry)
+{
+  for (uint32_t i = 0; i < mRules.Length(); ++i) {
+    if (mRules[i].mUserFontEntry == aUserFontEntry) {
+      return mRules[i].mContainer.mRule;
+    }
+  }
+  return nullptr;
+}
+
+nsresult
+FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
+                        const char* aMessage,
+                        uint32_t aFlags,
+                        nsresult aStatus)
+{
+  nsCOMPtr<nsIConsoleService>
+    console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+  if (!console) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsAutoCString familyName;
+  nsAutoCString fontURI;
+  aUserFontEntry->GetFamilyNameAndURIForLogging(familyName, fontURI);
+
+  char weightKeywordBuf[8]; // plenty to sprintf() a uint16_t
+  const char* weightKeyword;
+  const nsAFlatCString& weightKeywordString =
+    nsCSSProps::ValueToKeyword(aUserFontEntry->Weight(),
+                               nsCSSProps::kFontWeightKTable);
+  if (weightKeywordString.Length() > 0) {
+    weightKeyword = weightKeywordString.get();
+  } else {
+    sprintf(weightKeywordBuf, "%u", aUserFontEntry->Weight());
+    weightKeyword = weightKeywordBuf;
+  }
+
+  nsPrintfCString message
+       ("downloadable font: %s "
+        "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
+        aMessage,
+        familyName.get(),
+        aUserFontEntry->IsItalic() ? "italic" : "normal",
+        weightKeyword,
+        nsCSSProps::ValueToKeyword(aUserFontEntry->Stretch(),
+                                   nsCSSProps::kFontStretchKTable).get(),
+        aUserFontEntry->GetSrcIndex());
+
+  if (NS_FAILED(aStatus)) {
+    message.AppendLiteral(": ");
+    switch (aStatus) {
+    case NS_ERROR_DOM_BAD_URI:
+      message.AppendLiteral("bad URI or cross-site access not allowed");
+      break;
+    case NS_ERROR_CONTENT_BLOCKED:
+      message.AppendLiteral("content blocked");
+      break;
+    default:
+      message.AppendLiteral("status=");
+      message.AppendInt(static_cast<uint32_t>(aStatus));
+      break;
+    }
+  }
+  message.AppendLiteral("\nsource: ");
+  message.Append(fontURI);
+
+#ifdef PR_LOGGING
+  if (PR_LOG_TEST(GetFontFaceSetLog(), PR_LOG_DEBUG)) {
+    PR_LOG(GetFontFaceSetLog(), PR_LOG_DEBUG,
+           ("userfonts (%p) %s", this, message.get()));
+  }
+#endif
+
+  // try to give the user an indication of where the rule came from
+  nsCSSFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
+  nsString href;
+  nsString text;
+  nsresult rv;
+  if (rule) {
+    rv = rule->GetCssText(text);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIDOMCSSStyleSheet> sheet;
+    rv = rule->GetParentStyleSheet(getter_AddRefs(sheet));
+    NS_ENSURE_SUCCESS(rv, rv);
+    // if the style sheet is removed while the font is loading can be null
+    if (sheet) {
+      rv = sheet->GetHref(href);
+      NS_ENSURE_SUCCESS(rv, rv);
+    } else {
+      NS_WARNING("null parent stylesheet for @font-face rule");
+      href.AssignLiteral("unknown");
+    }
+  }
+
+  nsCOMPtr<nsIScriptError> scriptError =
+    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint64_t innerWindowID = GetPresContext()->Document()->InnerWindowID();
+  rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
+                                     href,         // file
+                                     text,         // src line
+                                     0, 0,         // line & column number
+                                     aFlags,       // flags
+                                     "CSS Loader", // category (make separate?)
+                                     innerWindowID);
+  if (NS_SUCCEEDED(rv)) {
+    console->LogMessage(scriptError);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
+                           nsIPrincipal** aPrincipal,
+                           bool* aBypassCache)
+{
+  // check same-site origin
+  nsIPresShell* ps = mPresContext->PresShell();
+  if (!ps)
+    return NS_ERROR_FAILURE;
+
+  NS_ASSERTION(aFontFaceSrc && !aFontFaceSrc->mIsLocal,
+               "bad font face url passed to fontloader");
+  NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
+  if (!aFontFaceSrc->mURI)
+    return NS_ERROR_FAILURE;
+
+  // use document principal, original principal if flag set
+  // this enables user stylesheets to load font files via
+  // @font-face rules
+  *aPrincipal = ps->GetDocument()->NodePrincipal();
+
+  NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
+               "null origin principal in @font-face rule");
+  if (aFontFaceSrc->mUseOriginPrincipal) {
+    *aPrincipal = aFontFaceSrc->mOriginPrincipal;
+  }
+
+  nsresult rv = nsFontFaceLoader::CheckLoadAllowed(*aPrincipal,
+                                                   aFontFaceSrc->mURI,
+                                                   ps->GetDocument());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  *aBypassCache = false;
+
+  nsCOMPtr<nsIDocShell> docShell = ps->GetDocument()->GetDocShell();
+  if (docShell) {
+    uint32_t loadType;
+    if (NS_SUCCEEDED(docShell->GetLoadType(&loadType))) {
+      if ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE) {
+        *aBypassCache = true;
+      }
+    }
+  }
+
+  return rv;
+}
+
+nsresult
+FontFaceSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
+                              const gfxFontFaceSrc* aFontFaceSrc,
+                              uint8_t*& aBuffer,
+                              uint32_t& aBufferLength)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIChannel> channel;
+  // get Content Security Policy from principal to pass into channel
+  nsCOMPtr<nsIChannelPolicy> channelPolicy;
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  rv = aFontToLoad->GetPrincipal()->GetCsp(getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (csp) {
+    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
+    channelPolicy->SetContentSecurityPolicy(csp);
+    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_FONT);
+  }
+
+  nsIPresShell* ps = mPresContext->PresShell();
+  if (!ps) {
+    return NS_ERROR_FAILURE;
+  }
+  // Note we are calling NS_NewChannelInternal() with both a node and a
+  // principal.  This is because the document where the font is being loaded
+  // might have a different origin from the principal of the stylesheet
+  // that initiated the font load.
+  rv = NS_NewChannelInternal(getter_AddRefs(channel),
+                             aFontFaceSrc->mURI,
+                             ps->GetDocument(),
+                             aFontToLoad->GetPrincipal(),
+                             nsILoadInfo::SEC_NORMAL,
+                             nsIContentPolicy::TYPE_FONT,
+                             channelPolicy);
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // blocking stream is OK for data URIs
+  nsCOMPtr<nsIInputStream> stream;
+  rv = channel->Open(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint64_t bufferLength64;
+  rv = stream->Available(&bufferLength64);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (bufferLength64 == 0) {
+    return NS_ERROR_FAILURE;
+  }
+  if (bufferLength64 > UINT32_MAX) {
+    return NS_ERROR_FILE_TOO_BIG;
+  }
+  aBufferLength = static_cast<uint32_t>(bufferLength64);
+
+  // read all the decoded data
+  aBuffer = static_cast<uint8_t*> (NS_Alloc(sizeof(uint8_t) * aBufferLength));
+  if (!aBuffer) {
+    aBufferLength = 0;
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  uint32_t numRead, totalRead = 0;
+  while (NS_SUCCEEDED(rv =
+           stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
+                        aBufferLength - totalRead, &numRead)) &&
+         numRead != 0)
+  {
+    totalRead += numRead;
+    if (totalRead > aBufferLength) {
+      rv = NS_ERROR_FAILURE;
+      break;
+    }
+  }
+
+  // make sure there's a mime type
+  if (NS_SUCCEEDED(rv)) {
+    nsAutoCString mimeType;
+    rv = channel->GetContentType(mimeType);
+    aBufferLength = totalRead;
+  }
+
+  if (NS_FAILED(rv)) {
+    NS_Free(aBuffer);
+    aBuffer = nullptr;
+    aBufferLength = 0;
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+bool
+FontFaceSet::GetPrivateBrowsing()
+{
+  nsIPresShell* ps = mPresContext->PresShell();
+  if (!ps) {
+    return false;
+  }
+
+  nsCOMPtr<nsILoadContext> loadContext = ps->GetDocument()->GetLoadContext();
+  return loadContext && loadContext->UsePrivateBrowsing();
+}
+
+void
+FontFaceSet::DoRebuildUserFontSet()
+{
+  if (!mPresContext) {
+    // AFAICS, this can only happen if someone has already called Destroy() on
+    // this font-set, which means it is in the process of being torn down --
+    // so there's no point trying to update its rules.
+    return;
+  }
+
+  mPresContext->RebuildUserFontSet();
+}
+
+// -- FontFaceSet::UserFontSet ------------------------------------------------
+
+/* virtual */ nsresult
+FontFaceSet::UserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
+                                        nsIPrincipal** aPrincipal,
+                                        bool* aBypassCache)
+{
+  if (!mFontFaceSet) {
+    return NS_ERROR_FAILURE;
+  }
+  return mFontFaceSet->CheckFontLoad(aFontFaceSrc, aPrincipal, aBypassCache);
+}
+
+/* virtual */ nsresult
+FontFaceSet::UserFontSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
+                                    const gfxFontFaceSrc* aFontFaceSrc)
+{
+  if (!mFontFaceSet) {
+    return NS_ERROR_FAILURE;
+  }
+  return mFontFaceSet->StartLoad(aUserFontEntry, aFontFaceSrc);
+}
+
+/* virtual */ nsresult
+FontFaceSet::UserFontSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
+                                     const char* aMessage,
+                                     uint32_t aFlags,
+                                     nsresult aStatus)
+{
+  if (!mFontFaceSet) {
+    return NS_ERROR_FAILURE;
+  }
+  return mFontFaceSet->LogMessage(aUserFontEntry, aMessage, aFlags, aStatus);
+}
+
+/* virtual */ nsresult
+FontFaceSet::UserFontSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
+                                           const gfxFontFaceSrc* aFontFaceSrc,
+                                           uint8_t*& aBuffer,
+                                           uint32_t& aBufferLength)
+{
+  if (!mFontFaceSet) {
+    return NS_ERROR_FAILURE;
+  }
+  return mFontFaceSet->SyncLoadFontData(aFontToLoad, aFontFaceSrc,
+                                        aBuffer, aBufferLength);
+}
+
+/* virtual */ bool
+FontFaceSet::UserFontSet::GetPrivateBrowsing()
+{
+  return mFontFaceSet && mFontFaceSet->GetPrivateBrowsing();
+}
+
+/* virtual */ void
+FontFaceSet::UserFontSet::DoRebuildUserFontSet()
+{
+  if (!mFontFaceSet) {
+    return;
+  }
+  mFontFaceSet->DoRebuildUserFontSet();
+}
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -3,41 +3,113 @@
  * 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/. */
 
 #ifndef mozilla_dom_FontFaceSet_h
 #define mozilla_dom_FontFaceSet_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/FontFaceSetBinding.h"
+#include "gfxUserFontSet.h"
+#include "nsCSSRules.h"
 #include "nsPIDOMWindow.h"
 
+struct gfxFontFaceSrc;
+class gfxUserFontEntry;
+class nsFontFaceLoader;
+class nsIPrincipal;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 class FontFace;
 class Promise;
 }
 }
 
 namespace mozilla {
 namespace dom {
 
 class FontFaceSet MOZ_FINAL : public DOMEventTargetHelper
 {
+  friend class UserFontSet;
+
 public:
+  /**
+   * A gfxUserFontSet that integrates with the layout and style systems to
+   * manage @font-face rules and handle network requests for font loading.
+   *
+   * We would combine this class and FontFaceSet into the one class if it were
+   * possible; it's not because FontFaceSet is cycle collected and
+   * gfxUserFontSet isn't (and can't be, as gfx classes don't use the cycle
+   * collector).  So UserFontSet exists just to override the needed virtual
+   * methods from gfxUserFontSet and to forward them on FontFaceSet.
+   */
+  class UserFontSet MOZ_FINAL : public gfxUserFontSet
+  {
+    friend class FontFaceSet;
+
+  public:
+    UserFontSet(FontFaceSet* aFontFaceSet)
+      : mFontFaceSet(aFontFaceSet)
+    {
+    }
+
+    FontFaceSet* GetFontFaceSet() { return mFontFaceSet; }
+
+    virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
+                                   nsIPrincipal** aPrincipal,
+                                   bool* aBypassCache) MOZ_OVERRIDE;
+    virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
+                               const gfxFontFaceSrc* aFontFaceSrc) MOZ_OVERRIDE;
+
+  protected:
+    virtual bool GetPrivateBrowsing() MOZ_OVERRIDE;
+    virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
+                                      const gfxFontFaceSrc* aFontFaceSrc,
+                                      uint8_t*& aBuffer,
+                                      uint32_t& aBufferLength) MOZ_OVERRIDE;
+    virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
+                                const char* aMessage,
+                                uint32_t aFlags = nsIScriptError::errorFlag,
+                                nsresult aStatus = NS_OK) MOZ_OVERRIDE;
+    virtual void DoRebuildUserFontSet() MOZ_OVERRIDE;
+
+  private:
+    nsRefPtr<FontFaceSet> mFontFaceSet;
+  };
+
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FontFaceSet, DOMEventTargetHelper)
 
-  FontFaceSet(nsPIDOMWindow* aWindow);
+  FontFaceSet(nsPIDOMWindow* aWindow, nsPresContext* aPresContext);
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
-  // Web IDL
+  UserFontSet* EnsureUserFontSet(nsPresContext* aPresContext);
+  UserFontSet* GetUserFontSet() { return mUserFontSet; }
+
+  // Called when this font set is no longer associated with a presentation.
+  void DestroyUserFontSet();
+
+  // Called by nsFontFaceLoader when the loader has completed normally.
+  // It's removed from the mLoaders set.
+  void RemoveLoader(nsFontFaceLoader* aLoader);
+
+  bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
+
+  nsPresContext* GetPresContext() { return mPresContext; }
+
+  // search for @font-face rule that matches a platform font entry
+  nsCSSFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
+
+  void IncrementGeneration(bool aIsRebuild = false);
+
+  // -- Web IDL --------------------------------------------------------------
+
   IMPL_EVENT_HANDLER(loading)
   IMPL_EVENT_HANDLER(loadingdone)
   IMPL_EVENT_HANDLER(loadingerror)
   already_AddRefed<mozilla::dom::Promise> Load(const nsAString& aFont,
                                                const nsAString& aText,
                                                mozilla::ErrorResult& aRv);
   bool Check(const nsAString& aFont,
              const nsAString& aText,
@@ -50,15 +122,62 @@ public:
   bool Delete(FontFace& aFontFace, mozilla::ErrorResult& aRv);
   bool Has(FontFace& aFontFace);
   FontFace* IndexedGetter(uint32_t aIndex, bool& aFound);
   uint32_t Length();
 
 private:
   ~FontFaceSet();
 
+  // The font-set keeps track of the collection of rules, and their
+  // corresponding font entries (whether proxies or real entries),
+  // so that we can update the set without having to throw away
+  // all the existing fonts.
+  struct FontFaceRuleRecord {
+    nsRefPtr<gfxUserFontEntry>   mUserFontEntry;
+    nsFontFaceRuleContainer      mContainer;
+  };
+
+  void InsertRule(nsCSSFontFaceRule* aRule, uint8_t aSheetType,
+                  nsTArray<FontFaceRuleRecord>& oldRules,
+                  bool& aFontSetModified);
+
+  already_AddRefed<gfxUserFontEntry> FindOrCreateFontFaceFromRule(
+                                                   const nsAString& aFamilyName,
+                                                   nsCSSFontFaceRule* aRule,
+                                                   uint8_t aSheetType);
+
+  // search for @font-face rule that matches a userfont font entry
+  nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
+
+  nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
+                     const gfxFontFaceSrc* aFontFaceSrc);
+  nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
+                         nsIPrincipal** aPrincipal,
+                         bool* aBypassCache);
+  bool GetPrivateBrowsing();
+  nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
+                            const gfxFontFaceSrc* aFontFaceSrc,
+                            uint8_t*& aBuffer,
+                            uint32_t& aBufferLength);
+  nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
+                      const char* aMessage,
+                      uint32_t aFlags,
+                      nsresult aStatus);
+  void DoRebuildUserFontSet();
+
+  nsRefPtr<UserFontSet> mUserFontSet;
+  nsPresContext* mPresContext;
+
   nsRefPtr<mozilla::dom::Promise> mReady;
+
+  // Set of all loaders pointing to us. These are not strong pointers,
+  // but that's OK because nsFontFaceLoader always calls RemoveLoader on
+  // us before it dies (unless we die first).
+  nsTHashtable< nsPtrHashKey<nsFontFaceLoader> > mLoaders;
+
+  nsTArray<FontFaceRuleRecord>   mRules;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // !defined(mozilla_dom_FontFaceSet_h)
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -9,38 +9,26 @@
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG /* Allow logging in the release build */
 #endif /* MOZ_LOGGING */
 #include "prlog.h"
 
 #include "nsFontFaceLoader.h"
 
 #include "nsError.h"
-#include "nsNetUtil.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
-
+#include "FontFaceSet.h"
 #include "nsPresContext.h"
-#include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
-
+#include "nsIHttpChannel.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
-#include "nsCrossSiteListenerProxy.h"
-#include "nsIContentSecurityPolicy.h"
-#include "nsIDocShell.h"
-#include "nsIWebNavigation.h"
-#include "nsISupportsPriority.h"
-#include "nsINetworkPredictor.h"
 
-#include "nsIConsoleService.h"
-
-#include "nsStyleSet.h"
-#include "nsPrintfCString.h"
 #include "mozilla/gfx/2D.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* 
 GetFontDownloaderLog()
 {
@@ -52,36 +40,36 @@ GetFontDownloaderLog()
 #endif /* PR_LOGGING */
 
 #define LOG(args) PR_LOG(GetFontDownloaderLog(), PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(GetFontDownloaderLog(), PR_LOG_DEBUG)
 
 
 nsFontFaceLoader::nsFontFaceLoader(gfxUserFontEntry* aUserFontEntry,
                                    nsIURI* aFontURI,
-                                   nsUserFontSet* aFontSet,
+                                   mozilla::dom::FontFaceSet* aFontFaceSet,
                                    nsIChannel* aChannel)
   : mUserFontEntry(aUserFontEntry),
     mFontURI(aFontURI),
-    mFontSet(aFontSet),
+    mFontFaceSet(aFontFaceSet),
     mChannel(aChannel)
 {
 }
 
 nsFontFaceLoader::~nsFontFaceLoader()
 {
   if (mUserFontEntry) {
     mUserFontEntry->mLoader = nullptr;
   }
   if (mLoadTimer) {
     mLoadTimer->Cancel();
     mLoadTimer = nullptr;
   }
-  if (mFontSet) {
-    mFontSet->RemoveLoader(this);
+  if (mFontFaceSet) {
+    mFontFaceSet->RemoveLoader(this);
   }
 }
 
 void
 nsFontFaceLoader::StartedLoading(nsIStreamLoader* aStreamLoader)
 {
   int32_t loadTimeout =
     Preferences::GetInt("gfx.downloadable_fonts.fallback_delay", 3000);
@@ -99,17 +87,17 @@ nsFontFaceLoader::StartedLoading(nsIStre
   mStreamLoader = aStreamLoader;
 }
 
 void
 nsFontFaceLoader::LoadTimerCallback(nsITimer* aTimer, void* aClosure)
 {
   nsFontFaceLoader* loader = static_cast<nsFontFaceLoader*>(aClosure);
 
-  if (!loader->mFontSet) {
+  if (!loader->mFontFaceSet) {
     // We've been canceled
     return;
   }
 
   gfxUserFontEntry* ufe = loader->mUserFontEntry.get();
   bool updateUserFontSet = true;
 
   // If the entry is loading, check whether it's >75% done; if so,
@@ -138,58 +126,57 @@ nsFontFaceLoader::LoadTimerCallback(nsIT
     }
   }
 
   // If the font is not 75% loaded, or if we've already timed out once
   // before, we mark this entry as "loading slowly", so the fallback
   // font will be used in the meantime, and tell the context to refresh.
   if (updateUserFontSet) {
     ufe->mFontDataLoadingState = gfxUserFontEntry::LOADING_SLOWLY;
-    gfxUserFontSet* fontSet = loader->mFontSet;
-    nsPresContext* ctx = loader->mFontSet->GetPresContext();
+    nsPresContext* ctx = loader->mFontFaceSet->GetPresContext();
     NS_ASSERTION(ctx, "userfontset doesn't have a presContext?");
     if (ctx) {
-      fontSet->IncrementGeneration();
+      loader->mFontFaceSet->IncrementGeneration();
       ctx->UserFontSetUpdated();
       LOG(("fontdownloader (%p) timeout reflow\n", loader));
     }
   }
 }
 
 NS_IMPL_ISUPPORTS(nsFontFaceLoader, nsIStreamLoaderObserver)
 
 NS_IMETHODIMP
 nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
                                    nsISupports* aContext,
                                    nsresult aStatus,
                                    uint32_t aStringLen,
                                    const uint8_t* aString)
 {
-  if (!mFontSet) {
+  if (!mFontFaceSet) {
     // We've been canceled
     return aStatus;
   }
 
-  mFontSet->RemoveLoader(this);
+  mFontFaceSet->RemoveLoader(this);
 
 #ifdef PR_LOGGING
   if (LOG_ENABLED()) {
     nsAutoCString fontURI;
     mFontURI->GetSpec(fontURI);
     if (NS_SUCCEEDED(aStatus)) {
       LOG(("fontdownloader (%p) download completed - font uri: (%s)\n", 
            this, fontURI.get()));
     } else {
       LOG(("fontdownloader (%p) download failed - font uri: (%s) error: %8.8x\n", 
            this, fontURI.get(), aStatus));
     }
   }
 #endif
 
-  nsPresContext* ctx = mFontSet->GetPresContext();
+  nsPresContext* ctx = mFontFaceSet->GetPresContext();
   NS_ASSERTION(ctx && !ctx->PresShell()->IsDestroying(),
                "We should have been canceled already");
 
   if (NS_SUCCEEDED(aStatus)) {
     // for HTTP requests, check whether the request _actually_ succeeded;
     // the "request status" in aStatus does not necessarily indicate this,
     // because HTTP responses such as 404 (Not Found) will still result in
     // a success code and potentially an HTML error page from the server
@@ -220,31 +207,31 @@ nsFontFaceLoader::OnStreamComplete(nsISt
   if (fontUpdate) {
     // Update layout for the presence of the new font.  Since this is
     // asynchronous, reflows will coalesce.
     ctx->UserFontSetUpdated();
     LOG(("fontdownloader (%p) reflow\n", this));
   }
 
   // done with font set
-  mFontSet = nullptr;
+  mFontFaceSet = nullptr;
   if (mLoadTimer) {
     mLoadTimer->Cancel();
     mLoadTimer = nullptr;
   }
 
   return NS_SUCCESS_ADOPTED_DATA;
 }
 
 void
 nsFontFaceLoader::Cancel()
 {
   mUserFontEntry->mFontDataLoadingState = gfxUserFontEntry::NOT_LOADING;
   mUserFontEntry->mLoader = nullptr;
-  mFontSet = nullptr;
+  mFontFaceSet = nullptr;
   if (mLoadTimer) {
     mLoadTimer->Cancel();
     mLoadTimer = nullptr;
   }
   mChannel->Cancel(NS_BINDING_ABORTED);
 }
 
 nsresult
@@ -278,781 +265,8 @@ nsFontFaceLoader::CheckLoadAllowed(nsIPr
                                  nsContentUtils::GetSecurityManager());
 
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     return NS_ERROR_CONTENT_BLOCKED;
   }
 
   return NS_OK;
 }
-
-nsUserFontSet::nsUserFontSet(nsPresContext* aContext)
-  : mPresContext(aContext)
-{
-  NS_ASSERTION(mPresContext, "null context passed to nsUserFontSet");
-}
-
-nsUserFontSet::~nsUserFontSet()
-{
-  NS_ASSERTION(mLoaders.Count() == 0, "mLoaders should have been emptied");
-}
-
-static PLDHashOperator DestroyIterator(nsPtrHashKey<nsFontFaceLoader>* aKey,
-                                       void* aUserArg)
-{
-  aKey->GetKey()->Cancel();
-  return PL_DHASH_REMOVE;
-}
-
-void
-nsUserFontSet::Destroy()
-{
-  mPresContext = nullptr;
-  mLoaders.EnumerateEntries(DestroyIterator, nullptr);
-  mRules.Clear();
-}
-
-void
-nsUserFontSet::RemoveLoader(nsFontFaceLoader* aLoader)
-{
-  mLoaders.RemoveEntry(aLoader);
-}
-
-nsresult
-nsUserFontSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
-                         const gfxFontFaceSrc* aFontFaceSrc)
-{
-  nsresult rv;
-
-  nsIPresShell* ps = mPresContext->PresShell();
-  if (!ps)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIStreamLoader> streamLoader;
-  nsCOMPtr<nsILoadGroup> loadGroup(ps->GetDocument()->GetDocumentLoadGroup());
-
-  nsCOMPtr<nsIChannel> channel;
-  // get Content Security Policy from principal to pass into channel
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = aUserFontEntry->mPrincipal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_FONT);
-  }
-  // Note we are calling NS_NewChannelInternal() with both a node and a
-  // principal.  This is because the document where the font is being loaded
-  // might have a different origin from the principal of the stylesheet
-  // that initiated the font load.
-  rv = NS_NewChannelInternal(getter_AddRefs(channel),
-                             aFontFaceSrc->mURI,
-                             ps->GetDocument(),
-                             aUserFontEntry->mPrincipal,
-                             nsILoadInfo::SEC_NORMAL,
-                             nsIContentPolicy::TYPE_FONT,
-                             channelPolicy,
-                             loadGroup);
-
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<nsFontFaceLoader> fontLoader =
-    new nsFontFaceLoader(aUserFontEntry, aFontFaceSrc->mURI, this, channel);
-
-  if (!fontLoader)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-#ifdef PR_LOGGING
-  if (LOG_ENABLED()) {
-    nsAutoCString fontURI, referrerURI;
-    aFontFaceSrc->mURI->GetSpec(fontURI);
-    if (aFontFaceSrc->mReferrer)
-      aFontFaceSrc->mReferrer->GetSpec(referrerURI);
-    LOG(("fontdownloader (%p) download start - font uri: (%s) "
-         "referrer uri: (%s)\n",
-         fontLoader.get(), fontURI.get(), referrerURI.get()));
-  }
-#endif
-
-  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
-  if (httpChannel)
-    httpChannel->SetReferrer(aFontFaceSrc->mReferrer);
-  nsCOMPtr<nsISupportsPriority> priorityChannel(do_QueryInterface(channel));
-  if (priorityChannel) {
-    priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGH);
-  }
-
-  rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsIDocument *document = ps->GetDocument();
-  mozilla::net::PredictorLearn(aFontFaceSrc->mURI, document->GetDocumentURI(),
-                               nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
-                               loadGroup);
-
-  bool inherits = false;
-  rv = NS_URIChainHasFlags(aFontFaceSrc->mURI,
-                           nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
-                           &inherits);
-  if (NS_SUCCEEDED(rv) && inherits) {
-    // allow data, javascript, etc URI's
-    rv = channel->AsyncOpen(streamLoader, nullptr);
-  } else {
-    nsRefPtr<nsCORSListenerProxy> listener =
-      new nsCORSListenerProxy(streamLoader, aUserFontEntry->mPrincipal, false);
-    rv = listener->Init(channel);
-    if (NS_SUCCEEDED(rv)) {
-      rv = channel->AsyncOpen(listener, nullptr);
-    }
-    if (NS_FAILED(rv)) {
-      fontLoader->DropChannel();  // explicitly need to break ref cycle
-    }
-  }
-
-  if (NS_SUCCEEDED(rv)) {
-    mLoaders.PutEntry(fontLoader);
-    fontLoader->StartedLoading(streamLoader);
-    aUserFontEntry->mLoader = fontLoader; // let the font entry remember the
-                                          // loader, in case we need to cancel it
-  }
-
-  return rv;
-}
-
-static PLDHashOperator DetachFontEntries(const nsAString& aKey,
-                                         nsRefPtr<gfxUserFontFamily>& aFamily,
-                                         void* aUserArg)
-{
-  aFamily->DetachFontEntries();
-  return PL_DHASH_NEXT;
-}
-
-static PLDHashOperator RemoveIfEmpty(const nsAString& aKey,
-                                     nsRefPtr<gfxUserFontFamily>& aFamily,
-                                     void* aUserArg)
-{
-  return aFamily->GetFontList().Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
-}
-
-bool
-nsUserFontSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
-{
-  bool modified = false;
-
-  // The @font-face rules that make up the user font set have changed,
-  // so we need to update the set. However, we want to preserve existing
-  // font entries wherever possible, so that we don't discard and then
-  // re-download resources in the (common) case where at least some of the
-  // same rules are still present.
-
-  nsTArray<FontFaceRuleRecord> oldRules;
-  mRules.SwapElements(oldRules);
-
-  // Remove faces from the font family records; we need to re-insert them
-  // because we might end up with faces in a different order even if they're
-  // the same font entries as before. (The order can affect font selection
-  // where multiple faces match the requested style, perhaps with overlapping
-  // unicode-range coverage.)
-  mFontFamilies.Enumerate(DetachFontEntries, nullptr);
-
-  for (uint32_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
-    // Insert each rule into our list, migrating old font entries if possible
-    // rather than creating new ones; set  modified  to true if we detect
-    // that rule ordering has changed, or if a new entry is created.
-    InsertRule(aRules[i].mRule, aRules[i].mSheetType, oldRules, modified);
-  }
-
-  // Remove any residual families that have no font entries (i.e., they were
-  // not defined at all by the updated set of @font-face rules).
-  mFontFamilies.Enumerate(RemoveIfEmpty, nullptr);
-
-  // If any rules are left in the old list, note that the set has changed
-  // (even if the new set was built entirely by migrating old font entries).
-  if (oldRules.Length() > 0) {
-    modified = true;
-    // Any in-progress loaders for obsolete rules should be cancelled,
-    // as the resource being downloaded will no longer be required.
-    // We need to explicitly remove any loaders here, otherwise the loaders
-    // will keep their "orphaned" font entries alive until they complete,
-    // even after the oldRules array is deleted.
-    size_t count = oldRules.Length();
-    for (size_t i = 0; i < count; ++i) {
-      gfxUserFontEntry* userFontEntry = oldRules[i].mUserFontEntry;
-      nsFontFaceLoader* loader = userFontEntry->mLoader;
-      if (loader) {
-        loader->Cancel();
-        RemoveLoader(loader);
-      }
-    }
-  }
-
-  if (modified) {
-    IncrementGeneration(true);
-  }
-
-  // local rules have been rebuilt, so clear the flag
-  mLocalRulesUsed = false;
-
-  return modified;
-}
-
-static bool
-HasLocalSrc(const nsCSSValue::Array *aSrcArr)
-{
-  size_t numSrc = aSrcArr->Count();
-  for (size_t i = 0; i < numSrc; i++) {
-    if (aSrcArr->Item(i).GetUnit() == eCSSUnit_Local_Font) {
-      return true;
-    }
-  }
-  return false;
-}
-
-void
-nsUserFontSet::InsertRule(nsCSSFontFaceRule* aRule, uint8_t aSheetType,
-                          nsTArray<FontFaceRuleRecord>& aOldRules,
-                          bool& aFontSetModified)
-{
-  NS_ABORT_IF_FALSE(aRule->GetType() == mozilla::css::Rule::FONT_FACE_RULE,
-                    "InsertRule passed a non-fontface CSS rule");
-
-  // set up family name
-  nsAutoString fontfamily;
-  nsCSSValue val;
-  uint32_t unit;
-
-  aRule->GetDesc(eCSSFontDesc_Family, val);
-  unit = val.GetUnit();
-  if (unit == eCSSUnit_String) {
-    val.GetStringValue(fontfamily);
-  } else {
-    NS_ASSERTION(unit == eCSSUnit_Null,
-                 "@font-face family name has unexpected unit");
-  }
-  if (fontfamily.IsEmpty()) {
-    // If there is no family name, this rule cannot contribute a
-    // usable font, so there is no point in processing it further.
-    return;
-  }
-
-  // first, we check in oldRules; if the rule exists there, just move it
-  // to the new rule list, and put the entry into the appropriate family
-  for (uint32_t i = 0; i < aOldRules.Length(); ++i) {
-    const FontFaceRuleRecord& ruleRec = aOldRules[i];
-
-    if (ruleRec.mContainer.mRule == aRule &&
-        ruleRec.mContainer.mSheetType == aSheetType) {
-
-      // if local rules were used, don't use the old font entry
-      // for rules containing src local usage
-      if (mLocalRulesUsed) {
-        aRule->GetDesc(eCSSFontDesc_Src, val);
-        unit = val.GetUnit();
-        if (unit == eCSSUnit_Array && HasLocalSrc(val.GetArrayValue())) {
-          break;
-        }
-      }
-
-      AddFontFace(fontfamily, ruleRec.mUserFontEntry);
-      mRules.AppendElement(ruleRec);
-      aOldRules.RemoveElementAt(i);
-      // note the set has been modified if an old rule was skipped to find
-      // this one - something has been dropped, or ordering changed
-      if (i > 0) {
-        aFontSetModified = true;
-      }
-      return;
-    }
-  }
-
-  // this is a new rule:
-  FontFaceRuleRecord ruleRec;
-  ruleRec.mUserFontEntry =
-    FindOrCreateFontFaceFromRule(fontfamily, aRule, aSheetType);
-
-  if (!ruleRec.mUserFontEntry) {
-    return;
-  }
-
-  ruleRec.mContainer.mRule = aRule;
-  ruleRec.mContainer.mSheetType = aSheetType;
-
-  // Add the entry to the end of the list.  If an existing userfont entry was
-  // returned by FindOrCreateFontFaceFromRule that was already stored on the
-  // family, gfxUserFontFamily::AddFontEntry(), which AddFontFace calls,
-  // will automatically remove the earlier occurrence of the same userfont entry.
-  AddFontFace(fontfamily, ruleRec.mUserFontEntry);
-
-  mRules.AppendElement(ruleRec);
-
-  // this was a new rule and font entry, so note that the set was modified
-  aFontSetModified = true;
-}
-
-already_AddRefed<gfxUserFontEntry>
-nsUserFontSet::FindOrCreateFontFaceFromRule(const nsAString& aFamilyName,
-                                            nsCSSFontFaceRule* aRule,
-                                            uint8_t aSheetType)
-{
-  nsCSSValue val;
-  uint32_t unit;
-
-  uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL;
-  int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
-  uint32_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
-  uint32_t languageOverride = NO_FONT_LANGUAGE_OVERRIDE;
-
-  // set up weight
-  aRule->GetDesc(eCSSFontDesc_Weight, val);
-  unit = val.GetUnit();
-  if (unit == eCSSUnit_Integer || unit == eCSSUnit_Enumerated) {
-    weight = val.GetIntValue();
-    if (weight == 0) {
-      weight = NS_STYLE_FONT_WEIGHT_NORMAL;
-    }
-  } else if (unit == eCSSUnit_Normal) {
-    weight = NS_STYLE_FONT_WEIGHT_NORMAL;
-  } else {
-    NS_ASSERTION(unit == eCSSUnit_Null,
-                 "@font-face weight has unexpected unit");
-  }
-
-  // set up stretch
-  aRule->GetDesc(eCSSFontDesc_Stretch, val);
-  unit = val.GetUnit();
-  if (unit == eCSSUnit_Enumerated) {
-    stretch = val.GetIntValue();
-  } else if (unit == eCSSUnit_Normal) {
-    stretch = NS_STYLE_FONT_STRETCH_NORMAL;
-  } else {
-    NS_ASSERTION(unit == eCSSUnit_Null,
-                 "@font-face stretch has unexpected unit");
-  }
-
-  // set up font style
-  aRule->GetDesc(eCSSFontDesc_Style, val);
-  unit = val.GetUnit();
-  if (unit == eCSSUnit_Enumerated) {
-    italicStyle = val.GetIntValue();
-  } else if (unit == eCSSUnit_Normal) {
-    italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
-  } else {
-    NS_ASSERTION(unit == eCSSUnit_Null,
-                 "@font-face style has unexpected unit");
-  }
-
-  // set up font features
-  nsTArray<gfxFontFeature> featureSettings;
-  aRule->GetDesc(eCSSFontDesc_FontFeatureSettings, val);
-  unit = val.GetUnit();
-  if (unit == eCSSUnit_Normal) {
-    // empty list of features
-  } else if (unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep) {
-    nsRuleNode::ComputeFontFeatures(val.GetPairListValue(), featureSettings);
-  } else {
-    NS_ASSERTION(unit == eCSSUnit_Null,
-                 "@font-face font-feature-settings has unexpected unit");
-  }
-
-  // set up font language override
-  aRule->GetDesc(eCSSFontDesc_FontLanguageOverride, val);
-  unit = val.GetUnit();
-  if (unit == eCSSUnit_Normal) {
-    // empty feature string
-  } else if (unit == eCSSUnit_String) {
-    nsString stringValue;
-    val.GetStringValue(stringValue);
-    languageOverride = gfxFontStyle::ParseFontLanguageOverride(stringValue);
-  } else {
-    NS_ASSERTION(unit == eCSSUnit_Null,
-                 "@font-face font-language-override has unexpected unit");
-  }
-
-  // set up src array
-  nsTArray<gfxFontFaceSrc> srcArray;
-
-  aRule->GetDesc(eCSSFontDesc_Src, val);
-  unit = val.GetUnit();
-  if (unit == eCSSUnit_Array) {
-    nsCSSValue::Array* srcArr = val.GetArrayValue();
-    size_t numSrc = srcArr->Count();
-    
-    for (size_t i = 0; i < numSrc; i++) {
-      val = srcArr->Item(i);
-      unit = val.GetUnit();
-      gfxFontFaceSrc* face = srcArray.AppendElements(1);
-      if (!face)
-        return nullptr;
-
-      switch (unit) {
-
-      case eCSSUnit_Local_Font:
-        val.GetStringValue(face->mLocalName);
-        face->mIsLocal = true;
-        face->mURI = nullptr;
-        face->mFormatFlags = 0;
-        break;
-      case eCSSUnit_URL:
-        face->mIsLocal = false;
-        face->mURI = val.GetURLValue();
-        face->mReferrer = val.GetURLStructValue()->mReferrer;
-        face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal;
-        NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
-
-        // agent and user stylesheets are treated slightly differently,
-        // the same-site origin check and access control headers are
-        // enforced against the sheet principal rather than the document
-        // principal to allow user stylesheets to include @font-face rules
-        face->mUseOriginPrincipal = (aSheetType == nsStyleSet::eUserSheet ||
-                                     aSheetType == nsStyleSet::eAgentSheet);
-
-        face->mLocalName.Truncate();
-        face->mFormatFlags = 0;
-        while (i + 1 < numSrc && (val = srcArr->Item(i+1), 
-                 val.GetUnit() == eCSSUnit_Font_Format)) {
-          nsDependentString valueString(val.GetStringBufferValue());
-          if (valueString.LowerCaseEqualsASCII("woff")) {
-            face->mFormatFlags |= FLAG_FORMAT_WOFF;
-          } else if (valueString.LowerCaseEqualsASCII("opentype")) {
-            face->mFormatFlags |= FLAG_FORMAT_OPENTYPE;
-          } else if (valueString.LowerCaseEqualsASCII("truetype")) {
-            face->mFormatFlags |= FLAG_FORMAT_TRUETYPE;
-          } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
-            face->mFormatFlags |= FLAG_FORMAT_TRUETYPE_AAT;
-          } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
-            face->mFormatFlags |= FLAG_FORMAT_EOT;
-          } else if (valueString.LowerCaseEqualsASCII("svg")) {
-            face->mFormatFlags |= FLAG_FORMAT_SVG;
-          } else {
-            // unknown format specified, mark to distinguish from the
-            // case where no format hints are specified
-            face->mFormatFlags |= FLAG_FORMAT_UNKNOWN;
-          }
-          i++;
-        }
-        if (!face->mURI) {
-          // if URI not valid, omit from src array
-          srcArray.RemoveElementAt(srcArray.Length() - 1);
-          NS_WARNING("null url in @font-face rule");
-          continue;
-        }
-        break;
-      default:
-        NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
-                     "strange unit type in font-face src array");
-        break;
-      }
-     }
-  } else {
-    NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
-  }
-
-  if (srcArray.IsEmpty()) {
-    return nullptr;
-  }
-
-  nsRefPtr<gfxUserFontEntry> entry =
-    FindOrCreateFontFace(aFamilyName, srcArray, weight, stretch, italicStyle,
-                         featureSettings, languageOverride,
-                         nullptr /* aUnicodeRanges */);
-  return entry.forget();
-}
-
-nsCSSFontFaceRule*
-nsUserFontSet::FindRuleForEntry(gfxFontEntry* aFontEntry)
-{
-  NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
-  for (uint32_t i = 0; i < mRules.Length(); ++i) {
-    if (mRules[i].mUserFontEntry->GetPlatformFontEntry() == aFontEntry) {
-      return mRules[i].mContainer.mRule;
-    }
-  }
-  return nullptr;
-}
-
-nsCSSFontFaceRule*
-nsUserFontSet::FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry)
-{
-  for (uint32_t i = 0; i < mRules.Length(); ++i) {
-    if (mRules[i].mUserFontEntry == aUserFontEntry) {
-      return mRules[i].mContainer.mRule;
-    }
-  }
-  return nullptr;
-}
-
-nsresult
-nsUserFontSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
-                          const char* aMessage,
-                          uint32_t aFlags,
-                          nsresult aStatus)
-{
-  nsCOMPtr<nsIConsoleService>
-    console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
-  if (!console) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  NS_ConvertUTF16toUTF8 familyName(aUserFontEntry->mFamilyName);
-  nsAutoCString fontURI;
-  if (aUserFontEntry->mSrcIndex == aUserFontEntry->mSrcList.Length()) {
-    fontURI.AppendLiteral("(end of source list)");
-  } else {
-    if (aUserFontEntry->mSrcList[aUserFontEntry->mSrcIndex].mURI) {
-      aUserFontEntry->mSrcList[aUserFontEntry->mSrcIndex].mURI->GetSpec(fontURI);
-    } else {
-      fontURI.AppendLiteral("(invalid URI)");
-    }
-  }
-
-  char weightKeywordBuf[8]; // plenty to sprintf() a uint16_t
-  const char* weightKeyword;
-  const nsAFlatCString& weightKeywordString =
-    nsCSSProps::ValueToKeyword(aUserFontEntry->Weight(),
-                               nsCSSProps::kFontWeightKTable);
-  if (weightKeywordString.Length() > 0) {
-    weightKeyword = weightKeywordString.get();
-  } else {
-    sprintf(weightKeywordBuf, "%u", aUserFontEntry->Weight());
-    weightKeyword = weightKeywordBuf;
-  }
-
-  nsPrintfCString message
-       ("downloadable font: %s "
-        "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
-        aMessage,
-        familyName.get(),
-        aUserFontEntry->IsItalic() ? "italic" : "normal",
-        weightKeyword,
-        nsCSSProps::ValueToKeyword(aUserFontEntry->Stretch(),
-                                   nsCSSProps::kFontStretchKTable).get(),
-        aUserFontEntry->mSrcIndex);
-
-  if (NS_FAILED(aStatus)) {
-    message.AppendLiteral(": ");
-    switch (aStatus) {
-    case NS_ERROR_DOM_BAD_URI:
-      message.AppendLiteral("bad URI or cross-site access not allowed");
-      break;
-    case NS_ERROR_CONTENT_BLOCKED:
-      message.AppendLiteral("content blocked");
-      break;
-    default:
-      message.AppendLiteral("status=");
-      message.AppendInt(static_cast<uint32_t>(aStatus));
-      break;
-    }
-  }
-  message.AppendLiteral("\nsource: ");
-  message.Append(fontURI);
-
-#ifdef PR_LOGGING
-  if (PR_LOG_TEST(GetUserFontsLog(), PR_LOG_DEBUG)) {
-    PR_LOG(GetUserFontsLog(), PR_LOG_DEBUG,
-           ("userfonts (%p) %s", this, message.get()));
-  }
-#endif
-
-  // try to give the user an indication of where the rule came from
-  nsCSSFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
-  nsString href;
-  nsString text;
-  nsresult rv;
-  if (rule) {
-    rv = rule->GetCssText(text);
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIDOMCSSStyleSheet> sheet;
-    rv = rule->GetParentStyleSheet(getter_AddRefs(sheet));
-    NS_ENSURE_SUCCESS(rv, rv);
-    // if the style sheet is removed while the font is loading can be null
-    if (sheet) {
-      rv = sheet->GetHref(href);
-      NS_ENSURE_SUCCESS(rv, rv);
-    } else {
-      NS_WARNING("null parent stylesheet for @font-face rule");
-      href.AssignLiteral("unknown");
-    }
-  }
-
-  nsCOMPtr<nsIScriptError> scriptError =
-    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint64_t innerWindowID = GetPresContext()->Document()->InnerWindowID();
-  rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
-                                     href,         // file
-                                     text,         // src line
-                                     0, 0,         // line & column number
-                                     aFlags,       // flags
-                                     "CSS Loader", // category (make separate?)
-                                     innerWindowID);
-  if (NS_SUCCEEDED(rv)) {
-    console->LogMessage(scriptError);
-  }
-
-  return NS_OK;
-}
-
-nsresult
-nsUserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
-                             nsIPrincipal** aPrincipal,
-                             bool* aBypassCache)
-{
-  // check same-site origin
-  nsIPresShell* ps = mPresContext->PresShell();
-  if (!ps)
-    return NS_ERROR_FAILURE;
-
-  NS_ASSERTION(aFontFaceSrc && !aFontFaceSrc->mIsLocal,
-               "bad font face url passed to fontloader");
-  NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
-  if (!aFontFaceSrc->mURI)
-    return NS_ERROR_FAILURE;
-
-  // use document principal, original principal if flag set
-  // this enables user stylesheets to load font files via
-  // @font-face rules
-  *aPrincipal = ps->GetDocument()->NodePrincipal();
-
-  NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
-               "null origin principal in @font-face rule");
-  if (aFontFaceSrc->mUseOriginPrincipal) {
-    *aPrincipal = aFontFaceSrc->mOriginPrincipal;
-  }
-
-  nsresult rv = nsFontFaceLoader::CheckLoadAllowed(*aPrincipal,
-                                                   aFontFaceSrc->mURI,
-                                                   ps->GetDocument());
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aBypassCache = false;
-
-  nsCOMPtr<nsIDocShell> docShell = ps->GetDocument()->GetDocShell();
-  if (docShell) {
-    uint32_t loadType;
-    if (NS_SUCCEEDED(docShell->GetLoadType(&loadType))) {
-      if ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE) {
-        *aBypassCache = true;
-      }
-    }
-  }
-
-  return rv;
-}
-
-nsresult
-nsUserFontSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
-                                const gfxFontFaceSrc* aFontFaceSrc,
-                                uint8_t*& aBuffer,
-                                uint32_t& aBufferLength)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsIChannel> channel;
-  // get Content Security Policy from principal to pass into channel
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = aFontToLoad->mPrincipal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_FONT);
-  }
-
-  nsIPresShell* ps = mPresContext->PresShell();
-  if (!ps) {
-    return NS_ERROR_FAILURE;
-  }
-  // Note we are calling NS_NewChannelInternal() with both a node and a
-  // principal.  This is because the document where the font is being loaded
-  // might have a different origin from the principal of the stylesheet
-  // that initiated the font load.
-  rv = NS_NewChannelInternal(getter_AddRefs(channel),
-                             aFontFaceSrc->mURI,
-                             ps->GetDocument(),
-                             aFontToLoad->mPrincipal,
-                             nsILoadInfo::SEC_NORMAL,
-                             nsIContentPolicy::TYPE_FONT,
-                             channelPolicy);
-
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // blocking stream is OK for data URIs
-  nsCOMPtr<nsIInputStream> stream;
-  rv = channel->Open(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint64_t bufferLength64;
-  rv = stream->Available(&bufferLength64);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (bufferLength64 == 0) {
-    return NS_ERROR_FAILURE;
-  }
-  if (bufferLength64 > UINT32_MAX) {
-    return NS_ERROR_FILE_TOO_BIG;
-  }
-  aBufferLength = static_cast<uint32_t>(bufferLength64);
-
-  // read all the decoded data
-  aBuffer = static_cast<uint8_t*> (NS_Alloc(sizeof(uint8_t) * aBufferLength));
-  if (!aBuffer) {
-    aBufferLength = 0;
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  uint32_t numRead, totalRead = 0;
-  while (NS_SUCCEEDED(rv =
-           stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
-                        aBufferLength - totalRead, &numRead)) &&
-         numRead != 0)
-  {
-    totalRead += numRead;
-    if (totalRead > aBufferLength) {
-      rv = NS_ERROR_FAILURE;
-      break;
-    }
-  }
-
-  // make sure there's a mime type
-  if (NS_SUCCEEDED(rv)) {
-    nsAutoCString mimeType;
-    rv = channel->GetContentType(mimeType);
-    aBufferLength = totalRead;
-  }
-
-  if (NS_FAILED(rv)) {
-    NS_Free(aBuffer);
-    aBuffer = nullptr;
-    aBufferLength = 0;
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-bool
-nsUserFontSet::GetPrivateBrowsing()
-{
-  nsIPresShell* ps = mPresContext->PresShell();
-  if (!ps) {
-    return false;
-  }
-
-  nsCOMPtr<nsILoadContext> loadContext = ps->GetDocument()->GetLoadContext();
-  return loadContext && loadContext->UsePrivateBrowsing();
-}
-
-void
-nsUserFontSet::DoRebuildUserFontSet()
-{
-  if (!mPresContext) {
-    // AFAICS, this can only happen if someone has already called Destroy() on
-    // this font-set, which means it is in the process of being torn down --
-    // so there's no point trying to update its rules.
-    return;
-  }
-
-  mPresContext->RebuildUserFontSet();
-}
--- a/layout/style/nsFontFaceLoader.h
+++ b/layout/style/nsFontFaceLoader.h
@@ -16,109 +16,29 @@
 #include "gfxUserFontSet.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 #include "nsCSSRules.h"
 
 class nsPresContext;
 class nsIPrincipal;
 
-class nsFontFaceLoader;
-
-// nsUserFontSet - defines the loading mechanism for downloadable fonts
-class nsUserFontSet : public gfxUserFontSet
-{
-public:
-  explicit nsUserFontSet(nsPresContext* aContext);
-
-  // Called when this font set is no longer associated with a presentation.
-  void Destroy();
-
-  // starts loading process, creating and initializing a nsFontFaceLoader obj
-  // returns whether load process successfully started or not
-  nsresult StartLoad(gfxUserFontEntry* aFontToLoad,
-                     const gfxFontFaceSrc* aFontFaceSrc) MOZ_OVERRIDE;
-
-  // Called by nsFontFaceLoader when the loader has completed normally.
-  // It's removed from the mLoaders set.
-  void RemoveLoader(nsFontFaceLoader* aLoader);
-
-  bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
-
-  nsPresContext* GetPresContext() { return mPresContext; }
-
-  // search for @font-face rule that matches a platform font entry
-  nsCSSFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
-
-protected:
-  // Protected destructor, to discourage deletion outside of Release()
-  // (since we inherit from refcounted class gfxUserFontSet):
-  ~nsUserFontSet();
-
-  // The font-set keeps track of the collection of rules, and their
-  // corresponding font entries (whether proxies or real entries),
-  // so that we can update the set without having to throw away
-  // all the existing fonts.
-  struct FontFaceRuleRecord {
-    nsRefPtr<gfxUserFontEntry>   mUserFontEntry;
-    nsFontFaceRuleContainer      mContainer;
-  };
-
-  void InsertRule(nsCSSFontFaceRule* aRule, uint8_t aSheetType,
-                  nsTArray<FontFaceRuleRecord>& oldRules,
-                  bool& aFontSetModified);
-
-  already_AddRefed<gfxUserFontEntry> FindOrCreateFontFaceFromRule(
-                                                   const nsAString& aFamilyName,
-                                                   nsCSSFontFaceRule* aRule,
-                                                   uint8_t aSheetType);
-
-  virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
-                              const char* aMessage,
-                              uint32_t aFlags = nsIScriptError::errorFlag,
-                              nsresult aStatus = NS_OK) MOZ_OVERRIDE;
-
-  virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
-                                 nsIPrincipal** aPrincipal,
-                                 bool* aBypassCache) MOZ_OVERRIDE;
-
-  virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
-                                    const gfxFontFaceSrc* aFontFaceSrc,
-                                    uint8_t*& aBuffer,
-                                    uint32_t& aBufferLength) MOZ_OVERRIDE;
-
-  virtual bool GetPrivateBrowsing() MOZ_OVERRIDE;
-
-  virtual void DoRebuildUserFontSet() MOZ_OVERRIDE;
-
-  // search for @font-face rule that matches a userfont font entry
-  nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
-
-  nsPresContext* mPresContext;  // weak reference
-
-  // Set of all loaders pointing to us. These are not strong pointers,
-  // but that's OK because nsFontFaceLoader always calls RemoveLoader on
-  // us before it dies (unless we die first).
-  nsTHashtable< nsPtrHashKey<nsFontFaceLoader> > mLoaders;
-
-  nsTArray<FontFaceRuleRecord>   mRules;
-};
-
 class nsFontFaceLoader : public nsIStreamLoaderObserver
 {
 public:
   nsFontFaceLoader(gfxUserFontEntry* aFontToLoad, nsIURI* aFontURI,
-                   nsUserFontSet* aFontSet, nsIChannel* aChannel);
+                   mozilla::dom::FontFaceSet* aFontFaceSet,
+                   nsIChannel* aChannel);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLOADEROBSERVER 
 
   // initiate the load
   nsresult Init();
-  // cancel the load and remove its reference to mFontSet
+  // cancel the load and remove its reference to mFontFaceSet
   void Cancel();
 
   void DropChannel() { mChannel = nullptr; }
 
   void StartedLoading(nsIStreamLoader* aStreamLoader);
 
   static void LoadTimerCallback(nsITimer* aTimer, void* aClosure);
 
@@ -127,16 +47,16 @@ public:
                                    nsISupports* aContext);
 
 protected:
   virtual ~nsFontFaceLoader();
 
 private:
   nsRefPtr<gfxUserFontEntry>  mUserFontEntry;
   nsCOMPtr<nsIURI>        mFontURI;
-  nsRefPtr<nsUserFontSet> mFontSet;
+  nsRefPtr<mozilla::dom::FontFaceSet> mFontFaceSet;
   nsCOMPtr<nsIChannel>    mChannel;
   nsCOMPtr<nsITimer>      mLoadTimer;
 
   nsIStreamLoader*        mStreamLoader;
 };
 
 #endif /* !defined(nsFontFaceLoader_h_) */