bug 816483 - cache instantiated user fonts and share them across pages that use the same resources. r=roc
authorJonathan Kew <jkew@mozilla.com>
Mon, 10 Dec 2012 09:31:07 +0000
changeset 124581 31b16bc75897d91e3ecaeedf3f0a29740192de50
parent 124580 0e9bc6febd7f0592e61556235d66caa53ae92ebe
child 124582 a4c7e07abd65e69904191cf4d33a66b588077ecc
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs816483
milestone20.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 816483 - cache instantiated user fonts and share them across pages that use the same resources. r=roc
gfx/thebes/gfxFT2FontList.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxPangoFonts.cpp
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/gfxUserFontSet.h
layout/style/nsFontFaceLoader.cpp
layout/style/nsFontFaceLoader.h
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -191,16 +191,17 @@ FT2FontEntry::CreateFontEntry(const gfxP
     // as it's not guaranteed that the face has valid names (bug 737315)
     FT2FontEntry* fe =
         FT2FontEntry::CreateFontEntry(face, nullptr, 0, aProxyEntry.Name(),
                                       aFontData);
     if (fe) {
         fe->mItalic = aProxyEntry.mItalic;
         fe->mWeight = aProxyEntry.mWeight;
         fe->mStretch = aProxyEntry.mStretch;
+        fe->mIsUserFont = true;
     }
     return fe;
 }
 
 class FTUserFontData {
 public:
     FTUserFontData(FT_Face aFace, const uint8_t* aData)
         : mFace(aFace), mFontData(aData)
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -77,18 +77,24 @@ gfxCharacterMap::NotifyReleased()
 {
     gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
     if (mShared) {
         fontlist->RemoveCmap(this);
     }
     delete this;
 }
 
-gfxFontEntry::~gfxFontEntry() 
-{
+gfxFontEntry::~gfxFontEntry()
+{
+    // For downloaded fonts, we need to tell the user font cache that this
+    // entry is being deleted.
+    if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) {
+        gfxUserFontSet::UserFontCache::ForgetFont(this);
+    }
+
     if (mSVGGlyphs) {
         delete mSVGGlyphs;
     }
     delete mUserFontData;
 }
 
 bool gfxFontEntry::IsSymbolFont() 
 {
@@ -1233,16 +1239,21 @@ gfxFontCache::gfxFontCache()
                                  SHAPED_WORD_TIMEOUT_SECONDS * 1000,
                                  nsITimer::TYPE_REPEATING_SLACK);
     }
 #endif
 }
 
 gfxFontCache::~gfxFontCache()
 {
+    // Ensure the user font cache releases its references to font entries,
+    // so they aren't kept alive after the font instances and font-list
+    // have been shut down.
+    gfxUserFontSet::UserFontCache::Shutdown();
+
     if (mWordCacheExpirationTimer) {
         mWordCacheExpirationTimer->Cancel();
         mWordCacheExpirationTimer = nullptr;
     }
 
     // Expire everything that has a zero refcount, so we don't leak them.
     AgeAllGenerations();
     // All fonts should be gone.
--- a/gfx/thebes/gfxPangoFonts.cpp
+++ b/gfx/thebes/gfxPangoFonts.cpp
@@ -1499,16 +1499,25 @@ gfxFcFontSet::SortPreferredFonts(bool &a
                 continue;
 
             entry->mKey = family; // initialize new entry
         }
 
         for (uint32_t f = 0; f < familyFonts->Length(); ++f) {
             FcPattern *font = familyFonts->ElementAt(f);
 
+            // Fix up the family name of user-font patterns, as the same
+            // font entry may be used (via the UserFontCache) for multiple
+            // CSS family names
+            if (isUserFont) {
+                font = FcPatternDuplicate(font);
+                FcPatternDel(font, FC_FAMILY);
+                FcPatternAddString(font, FC_FAMILY, family);
+            }
+
             // User fonts are already filtered by slant (but not size) in
             // mUserFontSet->FindFontEntry().
             if (!isUserFont && !SlantIsAcceptable(font, requestedSlant))
                 continue;
             if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
                 continue;
 
             for (uint32_t r = 0; r < requiredLangs.Length(); ++r) {
@@ -1519,17 +1528,22 @@ gfxFcFontSet::SortPreferredFonts(bool &a
                     requiredLangs.RemoveElementAt(r);
                     --r;
                 }
             }
 
             // FcFontSetDestroy will remove a reference but FcFontSetAdd
             // does _not_ take a reference!
             if (FcFontSetAdd(fontSet, font)) {
-                FcPatternReference(font);
+                // We don't add a reference here for user fonts, because we're
+                // using a local clone of the pattern (see above) in order to
+                // override the family name
+                if (!isUserFont) {
+                    FcPatternReference(font);
+                }
             }
         }
     }
 
     FcPattern *truncateMarker = NULL;
     for (uint32_t r = 0; r < requiredLangs.Length(); ++r) {
         const nsTArray< nsCountedRef<FcPattern> >& langFonts =
             utils->GetFontsForLang(requiredLangs[r].mLang);
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -10,16 +10,18 @@
 
 #include "gfxUserFontSet.h"
 #include "gfxPlatform.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "prlong.h"
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
+#include "nsIPrincipal.h"
+#include "mozilla/Telemetry.h"
 
 #include "woff.h"
 
 #include "opentype-sanitiser.h"
 #include "ots-memory-stream.h"
 
 using namespace mozilla;
 
@@ -401,16 +403,17 @@ StoreUserFontData(gfxFontEntry* aFontEnt
     }
     gfxUserFontData* userFontData = aFontEntry->mUserFontData;
     userFontData->mSrcIndex = aProxy->mSrcIndex;
     const gfxFontFaceSrc& src = aProxy->mSrcList[aProxy->mSrcIndex];
     if (src.mIsLocal) {
         userFontData->mLocalName = src.mLocalName;
     } else {
         userFontData->mURI = src.mURI;
+        userFontData->mPrincipal = aProxy->mPrincipal;
     }
     userFontData->mFormat = src.mFormatFlags;
     userFontData->mRealName = aOriginalName;
     if (aMetadata) {
         userFontData->mMetadata.SwapElements(*aMetadata);
         userFontData->mMetaOrigLen = aMetaOrigLen;
     }
 }
@@ -554,58 +557,75 @@ gfxUserFontSet::LoadNext(gfxProxyFontEnt
             }
         }
 
         // src url ==> start the load process
         else {
             if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
                     currSrc.mFormatFlags)) {
 
-                nsresult rv;
-                bool loadDoesntSpin = false;
-                rv = NS_URIChainHasFlags(currSrc.mURI,
-                       nsIProtocolHandler::URI_SYNC_LOAD_IS_OK,
-                       &loadDoesntSpin);
-
-                if (NS_SUCCEEDED(rv) && loadDoesntSpin) {
-                    uint8_t *buffer = nullptr;
-                    uint32_t bufferLength = 0;
+                nsIPrincipal *principal = nullptr;
+                nsresult rv = CheckFontLoad(&currSrc, &principal);
 
-                    // sync load font immediately
-                    rv = SyncLoadFontData(aProxyEntry, &currSrc, buffer,
-                                          bufferLength);
-
-                    if (NS_SUCCEEDED(rv) &&
-                        LoadFont(aProxyEntry, buffer, bufferLength)) {
+                if (NS_SUCCEEDED(rv) && principal != nullptr) {
+                    // see if we have an existing entry for this source
+                    gfxFontEntry *fe =
+                        UserFontCache::GetFont(currSrc.mURI, principal,
+                                               aProxyEntry);
+                    if (fe) {
+                        ReplaceFontEntry(aProxyEntry, fe);
                         return STATUS_LOADED;
-                    } else {
-                        LogMessage(aProxyEntry, "font load failed",
-                                   nsIScriptError::errorFlag, rv);
                     }
 
-                } else {
-                    // otherwise load font async
-                    rv = StartLoad(aProxyEntry, &currSrc);
-                    bool loadOK = NS_SUCCEEDED(rv);
+                    // record the principal returned by CheckFontLoad,
+                    // for use when creating a channel
+                    // and when caching the loaded entry
+                    aProxyEntry->mPrincipal = principal;
+
+                    bool loadDoesntSpin = false;
+                    rv = NS_URIChainHasFlags(currSrc.mURI,
+                           nsIProtocolHandler::URI_SYNC_LOAD_IS_OK,
+                           &loadDoesntSpin);
+                    if (NS_SUCCEEDED(rv) && loadDoesntSpin) {
+                        uint8_t *buffer = nullptr;
+                        uint32_t bufferLength = 0;
 
-                    if (loadOK) {
+                        // sync load font immediately
+                        rv = SyncLoadFontData(aProxyEntry, &currSrc,
+                                              buffer, bufferLength);
+                        if (NS_SUCCEEDED(rv) &&
+                            (fe = LoadFont(aProxyEntry, buffer, bufferLength))) {
+                            UserFontCache::CacheFont(fe);
+                            return STATUS_LOADED;
+                        } else {
+                            LogMessage(aProxyEntry, "font load failed",
+                                       nsIScriptError::errorFlag, rv);
+                        }
+                    } else {
+                        // otherwise load font async
+                        rv = StartLoad(aProxyEntry, &currSrc);
+                        if (NS_SUCCEEDED(rv)) {
 #ifdef PR_LOGGING
-                        if (LOG_ENABLED()) {
-                            nsAutoCString fontURI;
-                            currSrc.mURI->GetSpec(fontURI);
-                            LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
-                                 this, aProxyEntry->mSrcIndex, fontURI.get(),
-                                 NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
+                            if (LOG_ENABLED()) {
+                                nsAutoCString fontURI;
+                                currSrc.mURI->GetSpec(fontURI);
+                                LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
+                                     this, aProxyEntry->mSrcIndex, fontURI.get(),
+                                     NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
+                            }
+#endif
+                            return STATUS_LOADING;
+                        } else {
+                            LogMessage(aProxyEntry, "download failed",
+                                       nsIScriptError::errorFlag, rv);
                         }
-#endif
-                        return STATUS_LOADING;
-                    } else {
-                        LogMessage(aProxyEntry, "download failed",
-                                   nsIScriptError::errorFlag, rv);
                     }
+                } else {
+                    LogMessage(aProxyEntry, "download not allowed",
+                               nsIScriptError::errorFlag, rv);
                 }
             } else {
                 // We don't log a warning to the web console yet,
                 // as another source may load successfully
                 aProxyEntry->mUnsupportedFormat = true;
             }
         }
 
@@ -747,16 +767,17 @@ gfxUserFontSet::LoadFont(gfxProxyFontEnt
             nsAutoCString fontURI;
             aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
             LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
                  this, aProxy->mSrcIndex, fontURI.get(),
                  NS_ConvertUTF16toUTF8(aProxy->mFamily->Name()).get(),
                  uint32_t(mGeneration)));
         }
 #endif
+        UserFontCache::CacheFont(fe);
         ReplaceFontEntry(aProxy, fe);
     } else {
 #ifdef PR_LOGGING
         if (LOG_ENABLED()) {
             nsAutoCString fontURI;
             aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
             LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
                  " error making platform font\n",
@@ -773,8 +794,99 @@ gfxMixedFontFamily*
 gfxUserFontSet::GetFamily(const nsAString& aFamilyName) const
 {
     nsAutoString key(aFamilyName);
     ToLowerCase(key);
 
     return mFontFamilies.GetWeak(key);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
+// across pages/fontsets rather than instantiating new platform fonts.
+//
+// Entries are added to this cache when a platform font is instantiated from
+// downloaded data, and removed when the platform font entry is destroyed.
+// We don't need to use a timed expiration scheme here because the gfxFontEntry
+// for a downloaded font will be kept alive by its corresponding gfxFont
+// instance(s) until they are deleted, and *that* happens using an expiration
+// tracker (gfxFontCache). The result is that the downloaded font instances
+// recorded here will persist between pages and can get reused (provided the
+// source URI and principal match, of course).
+///////////////////////////////////////////////////////////////////////////////
+
+nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
+    gfxUserFontSet::UserFontCache::sUserFonts = nullptr;
+
+bool
+gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
+{
+    bool equal;
+    if (NS_FAILED(mURI->Equals(aKey->mURI, &equal)) || !equal) {
+        return false;
+    }
+
+    if (NS_FAILED(mPrincipal->Equals(aKey->mPrincipal, &equal)) || !equal) {
+        return false;
+    }
+
+    const gfxFontEntry *fe = aKey->mFontEntry;
+    if (mFontEntry->mItalic           != fe->mItalic          ||
+        mFontEntry->mWeight           != fe->mWeight          ||
+        mFontEntry->mStretch          != fe->mStretch         ||
+        mFontEntry->mFeatureSettings  != fe->mFeatureSettings ||
+        mFontEntry->mLanguageOverride != fe->mLanguageOverride) {
+        return false;
+    }
+
+    return true;
+}
+
+void
+gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
+{
+    if (!sUserFonts) {
+        sUserFonts = new nsTHashtable<Entry>;
+        sUserFonts->Init();
+    }
+
+    gfxUserFontData *data = aFontEntry->mUserFontData;
+    sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry));
+}
+
+void
+gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry *aFontEntry)
+{
+    if (!sUserFonts) {
+        // if we've already deleted the cache (i.e. during shutdown),
+        // just ignore this
+        return;
+    }
+
+    gfxUserFontData *data = aFontEntry->mUserFontData;
+    sUserFonts->RemoveEntry(Key(data->mURI, data->mPrincipal, aFontEntry));
+}
+
+gfxFontEntry*
+gfxUserFontSet::UserFontCache::GetFont(nsIURI            *aSrcURI,
+                                       nsIPrincipal      *aPrincipal,
+                                       gfxProxyFontEntry *aProxy)
+{
+    if (!sUserFonts) {
+        return nullptr;
+    }
+
+    Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy));
+    if (entry) {
+        return entry->GetFontEntry();
+    }
+
+    return nullptr;
+}
+
+void
+gfxUserFontSet::UserFontCache::Shutdown()
+{
+    if (sUserFonts) {
+        delete sUserFonts;
+        sUserFonts = nullptr;
+    }
+}
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -9,20 +9,21 @@
 #include "gfxTypes.h"
 #include "gfxFont.h"
 #include "gfxFontUtils.h"
 #include "nsRefPtrHashtable.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 #include "nsIFile.h"
+#include "nsIPrincipal.h"
 #include "nsISupportsImpl.h"
 #include "nsIScriptError.h"
+#include "nsURIHashKey.h"
 
-class nsIURI;
 class gfxMixedFontFamily;
 class nsFontFaceLoader;
 
 // parsed CSS @font-face rule information
 // lifetime: from when @font-face rule processed until font is loaded
 struct gfxFontFaceSrc {
     bool                   mIsLocal;       // url or local
 
@@ -30,20 +31,19 @@ struct gfxFontFaceSrc {
     bool                   mUseOriginPrincipal;
 
     // format hint flags, union of all possible formats
     // (e.g. TrueType, EOT, SVG, etc.)
     // see FLAG_FORMAT_* enum values below
     uint32_t               mFormatFlags;
 
     nsString               mLocalName;     // full font name if local
-    nsCOMPtr<nsIURI>       mURI;           // uri if url 
+    nsCOMPtr<nsIURI>       mURI;           // uri if url
     nsCOMPtr<nsIURI>       mReferrer;      // referrer url if url
-    nsCOMPtr<nsISupports>  mOriginPrincipal; // principal if url 
-    
+    nsCOMPtr<nsIPrincipal> mOriginPrincipal; // principal if url
 };
 
 // Subclassed to store platform-specific code cleaned out when font entry is
 // deleted.
 // Lifetime: from when platform font is created until it is deactivated.
 // If the platform does not need to add any platform-specific code/data here,
 // then the gfxUserFontSet will allocate a base gfxUserFontData and attach
 // to the entry to track the basic user font info fields here.
@@ -51,16 +51,17 @@ class gfxUserFontData {
 public:
     gfxUserFontData()
         : mSrcIndex(0), mFormat(0), mMetaOrigLen(0)
     { }
     virtual ~gfxUserFontData() { }
 
     nsTArray<uint8_t> mMetadata;  // woff metadata block (compressed), if any
     nsCOMPtr<nsIURI>  mURI;       // URI of the source, if it was url()
+    nsCOMPtr<nsIPrincipal> mPrincipal; // principal for the download, if url()
     nsString          mLocalName; // font name used for the source, if local()
     nsString          mRealName;  // original fullname from the font resource
     uint32_t          mSrcIndex;  // index in the rule's source list
     uint32_t          mFormat;    // format hint for the source used, if any
     uint32_t          mMetaOrigLen; // length needed to decompress metadata
 };
 
 // initially contains a set of proxy font entry objects, replaced with
@@ -197,20 +198,24 @@ public:
     }
 
     // lookup a font entry for a given style, returns null if not loaded
     gfxFontEntry *FindFontEntry(const nsAString& aName,
                                 const gfxFontStyle& aFontStyle,
                                 bool& aFoundFamily,
                                 bool& aNeedsBold,
                                 bool& aWaitForUserFont);
-                                
+
+    // check whether the given source is allowed to be loaded
+    virtual nsresult CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc,
+                                   nsIPrincipal **aPrincipal) = 0;
+
     // initialize the process that loads external font data, which upon 
     // completion will call OnLoadComplete method
-    virtual nsresult StartLoad(gfxProxyFontEntry *aProxy, 
+    virtual nsresult StartLoad(gfxProxyFontEntry *aProxy,
                                const gfxFontFaceSrc *aFontFaceSrc) = 0;
 
     // when download has been completed, pass back data here
     // aDownloadStatus == NS_OK ==> download succeeded, error otherwise
     // returns true if platform font creation sucessful (or local()
     // reference was next in line)
     // Ownership of aFontData is passed in here; the font set must
     // ensure that it is eventually deleted with NS_Free().
@@ -226,37 +231,131 @@ public:
 
     // generation - each time a face is loaded, generation is
     // incremented so that the change can be recognized 
     uint64_t GetGeneration() { return mGeneration; }
 
     // increment the generation on font load
     void IncrementGeneration();
 
+    class UserFontCache {
+    public:
+        // Record a loaded user-font in the cache. This requires that the
+        // font-entry's userFontData has been set up already, as it relies
+        // on the URI and Principal recorded there.
+        static void CacheFont(gfxFontEntry *aFontEntry);
+
+        // The given gfxFontEntry is being destroyed, so remove any record that
+        // refers to it.
+        static void ForgetFont(gfxFontEntry *aFontEntry);
+
+        // Return the gfxFontEntry corresponding to a given URI and principal,
+        // and the features of the given proxy, or nullptr if none is available
+        static gfxFontEntry* GetFont(nsIURI            *aSrcURI,
+                                     nsIPrincipal      *aPrincipal,
+                                     gfxProxyFontEntry *aProxy);
+
+        // Clear everything so that we don't leak URIs and Principals.
+        static void Shutdown();
+
+    private:
+        // Key used to look up entries in the user-font cache.
+        // Note that key comparison does *not* use the mFontEntry field
+        // as a whole; it only compares specific fields within the entry
+        // (weight/width/style/features) that could affect font selection
+        // or rendering, and that must match between a font-set's proxy
+        // entry and the corresponding "real" font entry.
+        struct Key {
+            nsCOMPtr<nsIURI>        mURI;
+            nsCOMPtr<nsIPrincipal>  mPrincipal;
+            gfxFontEntry           *mFontEntry;
+
+            Key(nsIURI* aURI, nsIPrincipal* aPrincipal,
+                gfxFontEntry* aFontEntry)
+                : mURI(aURI),
+                  mPrincipal(aPrincipal),
+                  mFontEntry(aFontEntry)
+            { }
+        };
+
+        class Entry : public PLDHashEntryHdr {
+        public:
+            typedef const Key& KeyType;
+            typedef const Key* KeyTypePointer;
+
+            Entry(KeyTypePointer aKey)
+                : mURI(aKey->mURI),
+                  mPrincipal(aKey->mPrincipal),
+                  mFontEntry(aKey->mFontEntry)
+            { }
+
+            Entry(const Entry& aOther)
+                : mURI(aOther.mURI),
+                  mPrincipal(aOther.mPrincipal),
+                  mFontEntry(aOther.mFontEntry)
+            { }
+
+            ~Entry() { }
+
+            bool KeyEquals(const KeyTypePointer aKey) const;
+
+            static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+
+            static PLDHashNumber HashKey(const KeyTypePointer aKey) {
+                uint32_t principalHash;
+                aKey->mPrincipal->GetHashValue(&principalHash);
+                return mozilla::HashGeneric(principalHash,
+                                            nsURIHashKey::HashKey(aKey->mURI),
+                                            HashFeatures(aKey->mFontEntry->mFeatureSettings),
+                                            ( aKey->mFontEntry->mItalic |
+                                             (aKey->mFontEntry->mWeight << 1) |
+                                             (aKey->mFontEntry->mStretch << 10) ) ^
+                                             aKey->mFontEntry->mLanguageOverride);
+            }
+
+            enum { ALLOW_MEMMOVE = false };
+
+            gfxFontEntry* GetFontEntry() const { return mFontEntry; }
+
+        private:
+            static uint32_t
+            HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) {
+                return mozilla::HashBytes(aFeatures.Elements(),
+                                          aFeatures.Length() * sizeof(gfxFontFeature));
+            }
+
+            nsCOMPtr<nsIURI>       mURI;
+            nsCOMPtr<nsIPrincipal> mPrincipal;
+
+            // The "real" font entry corresponding to this downloaded font.
+            // The font entry MUST notify the cache when it is destroyed
+            // (by calling Forget()).
+            gfxFontEntry          *mFontEntry;
+        };
+
+        static nsTHashtable<Entry> *sUserFonts;
+    };
+
 protected:
     // for a given proxy font entry, attempt to load the next resource
     // in the src list
     LoadStatus LoadNext(gfxProxyFontEntry *aProxyEntry);
 
     // helper method for creating a platform font
     // returns font entry if platform font creation successful
     // Ownership of aFontData is passed in here; the font set must
     // ensure that it is eventually deleted with NS_Free().
     gfxFontEntry* LoadFont(gfxProxyFontEntry *aProxy,
                            const uint8_t *aFontData, uint32_t &aLength);
 
     // parse data for a data URL
     virtual nsresult SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
                                       const gfxFontFaceSrc *aFontFaceSrc,
                                       uint8_t* &aBuffer,
-                                      uint32_t &aBufferLength)
-    {
-        // implemented in nsUserFontSet
-        return NS_ERROR_NOT_IMPLEMENTED;
-    }
+                                      uint32_t &aBufferLength) = 0;
 
     gfxMixedFontFamily *GetFamily(const nsAString& aName) const;
 
     // report a problem of some kind (implemented in nsUserFontSet)
     virtual nsresult LogMessage(gfxProxyFontEntry *aProxy,
                                 const char *aMessage,
                                 uint32_t aFlags = nsIScriptError::errorFlag,
                                 nsresult aStatus = NS_OK) = 0;
@@ -315,12 +414,13 @@ public:
         LOADING_FAILED       // failed to load any source: use fallback
     };
     LoadingState             mLoadingState;
     bool                     mUnsupportedFormat;
 
     nsTArray<gfxFontFaceSrc> mSrcList;
     uint32_t                 mSrcIndex; // index of loading src item
     nsFontFaceLoader        *mLoader; // current loader for this entry, if any
+    nsCOMPtr<nsIPrincipal>   mPrincipal;
 };
 
 
 #endif /* GFX_USER_FONT_SET_H */
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -311,33 +311,29 @@ nsUserFontSet::RemoveLoader(nsFontFaceLo
   mLoaders.RemoveEntry(aLoader);
 }
 
 nsresult
 nsUserFontSet::StartLoad(gfxProxyFontEntry *aProxy,
                          const gfxFontFaceSrc *aFontFaceSrc)
 {
   nsresult rv;
-  nsIPrincipal *principal = nullptr;
-
-  rv = CheckFontLoad(aProxy, aFontFaceSrc, &principal);
-  NS_ENSURE_SUCCESS(rv, 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 = principal->GetCsp(getter_AddRefs(csp));
+  rv = aProxy->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);
   }
   rv = NS_NewChannel(getter_AddRefs(channel),
                      aFontFaceSrc->mURI,
@@ -377,18 +373,17 @@ nsUserFontSet::StartLoad(gfxProxyFontEnt
   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, principal,
-                              false);
+      new nsCORSListenerProxy(streamLoader, aProxy->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
     }
   }
@@ -797,22 +792,19 @@ nsUserFontSet::LogMessage(gfxProxyFontEn
   if (NS_SUCCEEDED(rv)) {
     console->LogMessage(scriptError);
   }
 
   return NS_OK;
 }
 
 nsresult
-nsUserFontSet::CheckFontLoad(gfxProxyFontEntry *aFontToLoad,
-                             const gfxFontFaceSrc *aFontFaceSrc,
+nsUserFontSet::CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc,
                              nsIPrincipal **aPrincipal)
 {
-  nsresult rv;
-
   // 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");
@@ -822,47 +814,36 @@ nsUserFontSet::CheckFontLoad(gfxProxyFon
   // 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) {
-    nsCOMPtr<nsIPrincipal> p = do_QueryInterface(aFontFaceSrc->mOriginPrincipal);
-    *aPrincipal = p;
+    *aPrincipal = aFontFaceSrc->mOriginPrincipal;
   }
 
-  rv = nsFontFaceLoader::CheckLoadAllowed(*aPrincipal, aFontFaceSrc->mURI,
-                                          ps->GetDocument());
-  if (NS_FAILED(rv)) {
-    LogMessage(aFontToLoad, "download not allowed",
-               nsIScriptError::errorFlag, rv);
-  }
-
-  return rv;
+  return nsFontFaceLoader::CheckLoadAllowed(*aPrincipal, aFontFaceSrc->mURI,
+                                            ps->GetDocument());
 }
 
 nsresult
 nsUserFontSet::SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
                                 const gfxFontFaceSrc *aFontFaceSrc,
                                 uint8_t* &aBuffer,
                                 uint32_t &aBufferLength)
 {
   nsresult rv;
-  nsIPrincipal *principal = nullptr;
-
-  rv = CheckFontLoad(aFontToLoad, aFontFaceSrc, &principal);
-  NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIChannel> channel;
   // get Content Security Policy from principal to pass into channel
   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(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);
   }
   rv = NS_NewChannel(getter_AddRefs(channel),
                      aFontFaceSrc->mURI,
--- a/layout/style/nsFontFaceLoader.h
+++ b/layout/style/nsFontFaceLoader.h
@@ -69,19 +69,18 @@ protected:
                   nsTArray<FontFaceRuleRecord>& oldRules,
                   bool& aFontSetModified);
 
   virtual nsresult LogMessage(gfxProxyFontEntry *aProxy,
                               const char *aMessage,
                               uint32_t aFlags = nsIScriptError::errorFlag,
                               nsresult aStatus = NS_OK);
 
-  nsresult CheckFontLoad(gfxProxyFontEntry *aFontToLoad,
-                         const gfxFontFaceSrc *aFontFaceSrc,
-                         nsIPrincipal **aPrincipal);
+  virtual nsresult CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc,
+                                 nsIPrincipal **aPrincipal);
 
   virtual nsresult SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
                                     const gfxFontFaceSrc *aFontFaceSrc,
                                     uint8_t* &aBuffer,
                                     uint32_t &aBufferLength);
 
   nsPresContext *mPresContext;  // weak reference