Bug 1161413 - Part 4: Move FontFaceSet ownership from nsPresContext to nsIDocument. r=smaug
authorCameron McCormack <cam@mcc.id.au>
Sat, 27 Jun 2015 11:39:54 +1000
changeset 281241 cd5c33b5e95594b3bd8b91c71bc13ae8e48abd99
parent 281240 a8b95bf6004f1a5d87e673d0e364a0edf59d8017
child 281242 09a4d8018a6fca4d85c30ce1de10d46413825d8a
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1161413
milestone41.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 1161413 - Part 4: Move FontFaceSet ownership from nsPresContext to nsIDocument. r=smaug
dom/base/nsDocument.cpp
dom/base/nsIDocument.h
dom/webidl/FontFaceSource.webidl
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.cpp
layout/style/FontFace.cpp
layout/style/FontFaceSet.cpp
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1557,16 +1557,19 @@ nsIDocument::nsIDocument()
     mRemovedFromDocShell(false),
     // mAllowDNSPrefetch starts true, so that we can always reliably && it
     // with various values that might disable it.  Since we never prefetch
     // unless we get a window, and in that case the docshell value will get
     // &&-ed in, this is safe.
     mAllowDNSPrefetch(true),
     mIsBeingUsedAsImage(false),
     mHasLinksToUpdate(false),
+    mFontFaceSetDirty(true),
+    mGetUserFontSetCalled(false),
+    mPostedFlushUserFontSet(false),
     mPartID(0),
     mDidFireDOMContentLoaded(true)
 {
   SetInDocument();
 
   PR_INIT_CLIST(&mDOMMediaQueryLists);  
 }
 
@@ -1943,16 +1946,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
     cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
   }
 
   // Traverse all nsIDocument pointer members.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
 
   // Traverse all nsDocument nsCOMPtrs.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
@@ -2072,16 +2076,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSubImportLinks)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
 
   tmp->mParentDocument = nullptr;
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
 
 
   if (tmp->mBoxObjectTable) {
    tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
@@ -3810,16 +3815,19 @@ nsDocument::doCreateShell(nsPresContext*
   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   if (docShell && docShell->IsInvisible())
     shell->SetNeverPainting(true);
 
   mExternalResourceMap.ShowViewers();
 
   MaybeRescheduleAnimationFrameNotifications();
 
+  // Now that we have a shell, we might have @font-face rules.
+  RebuildUserFontSet();
+
   return shell.forget();
 }
 
 void
 nsDocument::MaybeRescheduleAnimationFrameNotifications()
 {
   if (!mPresShell || !IsEventHandlingEnabled()) {
     // bail out for now, until one of those conditions changes
@@ -3896,16 +3904,20 @@ nsDocument::DeleteShell()
     RevokeAnimationFrameNotifications();
   }
 
   // When our shell goes away, request that all our images be immediately
   // discarded, so we don't carry around decoded image data for a document we
   // no longer intend to paint.
   mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr);
 
+  // Now that we no longer have a shell, we need to forget about any FontFace
+  // objects for @font-face rules that came from the style set.
+  RebuildUserFontSet();
+
   mPresShell = nullptr;
 }
 
 void
 nsDocument::RevokeAnimationFrameNotifications()
 {
   if (!mFrameRequestCallbacks.IsEmpty()) {
     mPresShell->GetPresContext()->RefreshDriver()->
@@ -12951,25 +12963,127 @@ nsAutoSyncOperation::nsAutoSyncOperation
 nsAutoSyncOperation::~nsAutoSyncOperation()
 {
   for (int32_t i = 0; i < mDocuments.Count(); ++i) {
     mDocuments[i]->SetIsInSyncOperation(false);
   }
   nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);
 }
 
-FontFaceSet*
-nsIDocument::GetFonts(ErrorResult& aRv)
-{
-  nsIPresShell* shell = GetShell();
-  if (!shell) {
-    aRv.Throw(NS_ERROR_FAILURE);
+gfxUserFontSet*
+nsIDocument::GetUserFontSet()
+{
+  // We want to initialize the user font set lazily the first time the
+  // user asks for it, rather than building it too early and forcing
+  // rule cascade creation.  Thus we try to enforce the invariant that
+  // we *never* build the user font set until the first call to
+  // GetUserFontSet.  However, once it's been requested, we can't wait
+  // 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 (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 ||
+                 !GetShell() ||
+                 !GetShell()->IsReflowLocked(),
+                 "FlushUserFontSet should have been called first");
+    FlushUserFontSet();
+  }
+
+  if (!mFontFaceSet) {
     return nullptr;
   }
 
-  nsPresContext* presContext = shell->GetPresContext();
-  if (!presContext) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  return presContext->Fonts();
-}
+  return mFontFaceSet->GetUserFontSet();
+}
+
+void
+nsIDocument::FlushUserFontSet()
+{
+  if (!mGetUserFontSetCalled) {
+    return; // No one cares about this font set yet, but we want to be careful
+            // to not unset our mFontFaceSetDirty bit, so when someone really
+            // does we'll create it.
+  }
+
+  if (mFontFaceSetDirty) {
+    if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
+      nsTArray<nsFontFaceRuleContainer> rules;
+      nsIPresShell* shell = GetShell();
+      if (shell && !shell->StyleSet()->AppendFontFaceRules(rules)) {
+        return;
+      }
+
+      bool changed = false;
+
+      if (!mFontFaceSet && !rules.IsEmpty()) {
+        nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetScopeObject());
+        mFontFaceSet = new FontFaceSet(window, this);
+      }
+      if (mFontFaceSet) {
+        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 && shell) {
+        nsPresContext* presContext = shell->GetPresContext();
+        if (presContext) {
+          presContext->UserFontSetUpdated();
+        }
+      }
+    }
+
+    mFontFaceSetDirty = false;
+  }
+}
+
+void
+nsIDocument::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;
+  }
+
+  mFontFaceSetDirty = true;
+  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
+  // change reflow).
+  if (!mPostedFlushUserFontSet) {
+    nsCOMPtr<nsIRunnable> ev =
+      NS_NewRunnableMethod(this, &nsIDocument::HandleRebuildUserFontSet);
+    if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
+      mPostedFlushUserFontSet = true;
+    }
+  }
+}
+
+FontFaceSet*
+nsIDocument::Fonts()
+{
+  if (!mFontFaceSet) {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetScopeObject());
+    mFontFaceSet = new FontFaceSet(window, this);
+    GetUserFontSet();  // this will cause the user font set to be created/updated
+  }
+  return mFontFaceSet;
+}
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -29,16 +29,17 @@
 #include "Units.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "prclist.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/CORSMode.h"
 #include <bitset>                        // for member
 
+class gfxUserFontSet;
 class imgIRequest;
 class nsAString;
 class nsBindingManager;
 class nsIDocShell;
 class nsDocShell;
 class nsDOMNavigationTiming;
 class nsFrameLoader;
 class nsHTMLCSSStyleSheet;
@@ -146,18 +147,18 @@ template<typename> class Sequence;
 
 template<typename, typename> class CallbackObjectHolder;
 typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
 
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0x24fbaa06, 0x322b, 0x495f, \
-  {0x89, 0xcb, 0x33, 0xbc, 0x6f, 0x2d, 0xf6, 0x93} }
+{ 0x21bbd52a, 0xc2d2, 0x4b2f, \
+  { 0xbc, 0x6c, 0xc9, 0x52, 0xbe, 0x23, 0x6b, 0x19 } }
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
   DocumentFlavorSVG, // SVGDocument
   DocumentFlavorPlain, // Just a Document
 };
@@ -2555,18 +2556,23 @@ public:
 
     nsWeakPtr weakNode = do_GetWeakReference(node);
 
     if (weakNode) {
       mBlockedTrackingNodes.AppendElement(weakNode);
     }
   }
 
+  gfxUserFontSet* GetUserFontSet();
+  void FlushUserFontSet();
+  void RebuildUserFontSet(); // asynchronously
+  mozilla::dom::FontFaceSet* GetFonts() { return mFontFaceSet; }
+
   // FontFaceSource
-  mozilla::dom::FontFaceSet* GetFonts(mozilla::ErrorResult& aRv);
+  mozilla::dom::FontFaceSet* Fonts();
 
   bool DidFireDOMContentLoaded() const { return mDidFireDOMContentLoaded; }
 
 private:
   mutable std::bitset<eDeprecatedOperationCount> mDeprecationWarnedAbout;
   mutable std::bitset<eDocumentWarningCount> mDocWarningWarnedAbout;
   SelectorCache mSelectorCache;
 
@@ -2601,16 +2607,21 @@ protected:
 
   nsCString GetContentTypeInternal() const
   {
     return mContentType;
   }
 
   mozilla::dom::XPathEvaluator* XPathEvaluator();
 
+  void HandleRebuildUserFontSet() {
+    mPostedFlushUserFontSet = false;
+    FlushUserFontSet();
+  }
+
   nsCString mReferrer;
   nsString mLastModified;
 
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsIURI> mChromeXHRDocURI;
   nsCOMPtr<nsIURI> mDocumentBaseURI;
   nsCOMPtr<nsIURI> mChromeXHRDocBaseURI;
@@ -2657,16 +2668,19 @@ protected:
 
   // Table of element properties for this document.
   nsPropertyTable mPropertyTable;
   nsTArray<nsAutoPtr<nsPropertyTable> > mExtraPropertyTables;
 
   // Our cached .children collection
   nsCOMPtr<nsIHTMLCollection> mChildrenCollection;
 
+  // container for per-context fonts (downloadable, SVG, etc.)
+  nsRefPtr<mozilla::dom::FontFaceSet> mFontFaceSet;
+
   // Compatibility mode
   nsCompatibility mCompatMode;
 
   // Our readyState
   ReadyState mReadyState;
 
   // Our visibility state
   mozilla::dom::VisibilityState mVisibilityState;
@@ -2789,16 +2803,25 @@ protected:
 #ifdef DEBUG
   /**
    * This is true while FlushPendingLinkUpdates executes.  Calls to
    * [Un]RegisterPendingLinkUpdate will assert when this is true.
    */
   bool mIsLinkUpdateRegistrationsForbidden;
 #endif
 
+  // Is the current mFontFaceSet valid?
+  bool mFontFaceSetDirty;
+
+  // Has GetUserFontSet() been called?
+  bool mGetUserFontSetCalled;
+
+  // Do we currently have an event posted to call FlushUserFontSet?
+  bool mPostedFlushUserFontSet;
+
   enum Type {
     eUnknown, // should never be used
     eHTML,
     eXHTML,
     eGenericXML,
     eSVG,
     eXUL
   };
--- a/dom/webidl/FontFaceSource.webidl
+++ b/dom/webidl/FontFaceSource.webidl
@@ -8,11 +8,11 @@
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [NoInterfaceObject]
 interface FontFaceSource {
 
-  [Throws, Pref="layout.css.font-loading-api.enabled"]
+  [Pref="layout.css.font-loading-api.enabled"]
   readonly attribute FontFaceSet fonts;
 };
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -243,17 +243,16 @@ nsPresContext::nsPresContext(nsIDocument
   if (!IsDynamic()) {
     mImageAnimationMode = imgIContainer::kDontAnimMode;
     mNeverAnimate = true;
   } else {
     mImageAnimationMode = imgIContainer::kNormalAnimMode;
     mNeverAnimate = false;
   }
   NS_ASSERTION(mDocument, "Null document");
-  mFontFaceSetDirty = true;
 
   mCounterStylesDirty = true;
 
   // if text perf logging enabled, init stats struct
   PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textperf);
   if (MOZ_LOG_TEST(log, LogLevel::Warning)) {
     mTextPerf = new gfxTextPerfMetrics();
   }
@@ -1112,21 +1111,16 @@ 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 (mFontFaceSet) {
-    // Clear out user font set if we have one
-    mFontFaceSet->DestroyUserFontSet();
-    mFontFaceSet = nullptr;
-  }
   if (mCounterStyleManager) {
     mCounterStyleManager->Disconnect();
     mCounterStyleManager = nullptr;
   }
 
   if (mShell) {
     // Remove ourselves as the charset observer from the shell's doc, because
     // this shell may be going away for good.
@@ -1681,17 +1675,17 @@ nsPresContext::SetBidi(uint32_t aSource,
     nsIDocument* doc = mShell->GetDocument();
     if (doc) {
       SetVisualMode(IsVisualCharset(doc->GetDocumentCharacterSet()));
     }
   }
   if (aForceRestyle && mShell) {
     // Reconstruct the root document element's frame and its children,
     // because we need to trigger frame reconstruction for direction change.
-    RebuildUserFontSet();
+    mDocument->RebuildUserFontSet();
     mShell->ReconstructFrames();
   }
 }
 
 uint32_t
 nsPresContext::GetBidi() const
 {
   return Document()->GetBidiOptions();
@@ -1925,17 +1919,17 @@ nsPresContext::RebuildAllStyleData(nsCha
   if (!mShell) {
     // We must have been torn down. Nothing to do here.
     return;
   }
 
   mUsesRootEMUnits = false;
   mUsesExChUnits = false;
   mUsesViewportUnits = false;
-  RebuildUserFontSet();
+  mDocument->RebuildUserFontSet();
   RebuildCounterStyles();
 
   RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint);
 }
 
 void
 nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                             nsRestyleHint aRestyleHint)
@@ -2109,125 +2103,19 @@ nsPresContext::HasAuthorSpecifiedRules(n
 {
   return
     nsRuleNode::HasAuthorSpecifiedRules(aFrame->StyleContext(),
                                         ruleTypeMask,
                                         UseDocumentColors());
 }
 
 gfxUserFontSet*
-nsPresContext::GetUserFontSetInternal()
-{
-  // We want to initialize the user font set lazily the first time the
-  // user asks for it, rather than building it too early and forcing
-  // rule cascade creation.  Thus we try to enforce the invariant that
-  // we *never* build the user font set until the first call to
-  // GetUserFontSet.  However, once it's been requested, we can't wait
-  // 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 (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();
-  }
-
-  if (!mFontFaceSet) {
-    return nullptr;
-  }
-
-  return mFontFaceSet->GetUserFontSet();
-}
-
-gfxUserFontSet*
-nsPresContext::GetUserFontSetExternal()
-{
-  return GetUserFontSetInternal();
-}
-
-void
-nsPresContext::FlushUserFontSet()
+nsPresContext::GetUserFontSet()
 {
-  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 mFontFaceSetDirty bit, so when someone really
-            // does we'll create it.
-  }
-
-  if (mFontFaceSetDirty) {
-    if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
-      nsTArray<nsFontFaceRuleContainer> rules;
-      if (!mShell->StyleSet()->AppendFontFaceRules(rules)) {
-        return;
-      }
-
-      bool changed = false;
-
-      if (!mFontFaceSet && !rules.IsEmpty()) {
-        mFontFaceSet = new FontFaceSet(mDocument->GetInnerWindow(), mDocument);
-      }
-      if (mFontFaceSet) {
-        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();
-      }
-    }
-
-    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;
-  }
-
-  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
-  // change reflow).
-  if (!mPostedFlushUserFontSet) {
-    nsCOMPtr<nsIRunnable> ev =
-      NS_NewRunnableMethod(this, &nsPresContext::HandleRebuildUserFontSet);
-    if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
-      mPostedFlushUserFontSet = true;
-    }
-  }
+  return mDocument->GetUserFontSet();
 }
 
 void
 nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont)
 {
   if (!mShell)
     return;
 
@@ -2265,26 +2153,16 @@ nsPresContext::UserFontSetUpdated(gfxUse
   // given the weight, width, and slant from the nsStyleFont). If it does,
   // mark that frame dirty and skip inspecting its descendants.
   nsIFrame* root = mShell->GetRootFrame();
   if (root) {
     nsFontFaceUtils::MarkDirtyForFontChange(root, aUpdatedFont);
   }
 }
 
-FontFaceSet*
-nsPresContext::Fonts()
-{
-  if (!mFontFaceSet) {
-    mFontFaceSet = new FontFaceSet(mDocument->GetInnerWindow(), mDocument);
-    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.
@@ -2744,18 +2622,19 @@ nsPresContext::HavePendingInputEvent()
       return false;
     }
   }
 }
 
 void
 nsPresContext::NotifyFontFaceSetOnRefresh()
 {
-  if (mFontFaceSet) {
-    mFontFaceSet->DidRefresh();
+  FontFaceSet* set = mDocument->GetFonts();
+  if (set) {
+    set->DidRefresh();
   }
 }
 
 bool
 nsPresContext::HasPendingRestyleOrReflow()
 {
   return (mRestyleManager && mRestyleManager->HasPendingRestyles()) ||
          PresShell()->HasPendingReflow();
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -62,19 +62,16 @@ class nsRefreshDriver;
 class nsIWidget;
 class nsDeviceContext;
 class gfxMissingFontRecorder;
 
 namespace mozilla {
 class EventStateManager;
 class RestyleManager;
 class CounterStyleManager;
-namespace dom {
-class FontFaceSet;
-}
 namespace layers {
 class ContainerLayer;
 class LayerManager;
 }
 }
 
 // supported values for cached bool types
 enum nsPresContext_CachedBoolPrefType {
@@ -873,37 +870,26 @@ public:
   }
 
   // This method should be used instead of directly accessing mPaintFlashing,
   // as that value may be out of date when mPaintFlashingInitialized is false.
   bool GetPaintFlashing() const;
 
   bool             SuppressingResizeReflow() const { return mSuppressResizeReflow; }
 
-  virtual gfxUserFontSet* GetUserFontSetExternal();
-  gfxUserFontSet* GetUserFontSetInternal();
-#ifdef MOZILLA_INTERNAL_API
-  gfxUserFontSet* GetUserFontSet() { return GetUserFontSetInternal(); }
-#else
-  gfxUserFontSet* GetUserFontSet() { return GetUserFontSetExternal(); }
-#endif
-
-  void FlushUserFontSet();
-  void RebuildUserFontSet(); // asynchronously
+  gfxUserFontSet* GetUserFontSet();
 
   // 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(gfxUserFontEntry* aUpdatedFont = nullptr);
 
   gfxMissingFontRecorder *MissingFontRecorder() { return mMissingFonts; }
   void NotifyMissingFonts();
 
-  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();
 
@@ -1190,21 +1176,16 @@ public:
   void InvalidatePaintedLayers();
 
 protected:
   // May be called multiple times (unlink, destructor)
   void Destroy();
 
   void AppUnitsPerDevPixelChanged();
 
-  void HandleRebuildUserFontSet() {
-    mPostedFlushUserFontSet = false;
-    FlushUserFontSet();
-  }
-
   void HandleRebuildCounterStyles() {
     mPostedFlushCounterStyles = false;
     FlushCounterStyles();
   }
 
   bool HavePendingInputEvent();
 
   // Can't be inline because we can't include nsStyleSet.h.
@@ -1273,19 +1254,16 @@ protected:
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   nsCOMPtr<nsITimer>    mPrefChangedTimer;
 
   FramePropertyTable    mPropertyTable;
 
   nsInvalidateRequestList mInvalidateRequestsSinceLastPaint;
   nsInvalidateRequestList mUndeliveredInvalidateRequestsBeforeLastPaint;
 
-  // container for per-context fonts (downloadable, SVG, etc.)
-  nsRefPtr<mozilla::dom::FontFaceSet> mFontFaceSet;
-
   // text performance metrics
   nsAutoPtr<gfxTextPerfMetrics>   mTextPerf;
 
   nsAutoPtr<gfxMissingFontRecorder> mMissingFonts;
 
   nsRect                mVisibleArea;
   nsSize                mPageSize;
   float                 mPageScale;
@@ -1364,23 +1342,16 @@ protected:
   // Does the associated document use ex or ch units?
   unsigned              mUsesExChUnits : 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 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?
   unsigned              mPostedFlushCounterStyles: 1;
 
   // resize reflow is suppressed when the only change has been to zoom
   // the document rather than to change the document's dimensions
   unsigned              mSuppressResizeReflow : 1;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -4018,17 +4018,17 @@ PresShell::FlushPendingNotifications(moz
     // up-to-date style data.
     if (!mIsDestroying) {
       mViewManager->FlushDelayedResize(false);
       mPresContext->FlushPendingMediaFeatureValuesChanged();
 
       // Flush any pending update of the user font set, since that could
       // cause style changes (for updating ex/ch units, and to cause a
       // reflow).
-      mPresContext->FlushUserFontSet();
+      mDocument->FlushUserFontSet();
 
       mPresContext->FlushCounterStyles();
 
       // Flush any requested SMIL samples.
       if (mDocument->HasAnimationController()) {
         mDocument->GetAnimationController()->FlushResampleRequests();
       }
 
@@ -4429,18 +4429,19 @@ nsIPresShell::ReconstructStyleDataIntern
 
   mStylesHaveChanged = false;
 
   if (mIsDestroying) {
     // We don't want to mess with restyles at this point
     return;
   }
 
+  mDocument->RebuildUserFontSet();
+
   if (mPresContext) {
-    mPresContext->RebuildUserFontSet();
     mPresContext->RebuildCounterStyles();
   }
 
   Element* root = mDocument->GetRootElement();
   if (!mDidInitialize) {
     // Nothing to do here, since we have no frames yet
     return;
   }
@@ -8807,17 +8808,17 @@ PresShell::DidCauseReflow()
   nsContentUtils::RemoveScriptBlocker();
 
   return NS_OK;
 }
 
 void
 PresShell::WillDoReflow()
 {
-  mPresContext->FlushUserFontSet();
+  mDocument->FlushUserFontSet();
 
   mPresContext->FlushCounterStyles();
 
   mFrameConstructor->BeginUpdate();
 
   mLastReflowStart = GetPerformanceNow();
 }
 
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -183,22 +183,17 @@ FontFace::Constructor(const GlobalObject
   nsISupports* global = aGlobal.GetAsSupports();
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
   nsIDocument* doc = window->GetDoc();
   if (!doc) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  FontFaceSet* set = doc->GetFonts(aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  nsRefPtr<FontFace> obj = new FontFace(global, set);
+  nsRefPtr<FontFace> obj = new FontFace(global, doc->Fonts());
   obj->mFontFaceSet->AddUnavailableFontFace(obj);
   if (!obj->SetDescriptors(aFamily, aDescriptors)) {
     return obj.forget();
   }
 
   obj->InitializeSource(aSource);
   return obj.forget();
 }
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -1563,34 +1563,27 @@ FontFaceSet::StyleSheetLoaded(mozilla::C
 {
   CheckLoadingFinished();
   return NS_OK;
 }
 
 void
 FontFaceSet::FlushUserFontSet()
 {
-  nsPresContext* presContext = GetPresContext();
-  if (presContext) {
-    presContext->FlushUserFontSet();
+  if (mDocument) {
+    mDocument->FlushUserFontSet();
   }
 }
 
 void
 FontFaceSet::RebuildUserFontSet()
 {
-  nsPresContext* presContext = GetPresContext();
-  if (!presContext) {
-    // 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;
+  if (mDocument) {
+    mDocument->RebuildUserFontSet();
   }
-
-  presContext->RebuildUserFontSet();
 }
 
 nsPresContext*
 FontFaceSet::GetPresContext()
 {
   if (!mDocument) {
     return nullptr;
   }