Bug 754215 - Refactor user-font loading code in preparation for lazier downloading. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Wed, 09 Jul 2014 17:08:52 +1000
changeset 193238 6131c6ac70cbc72d7f6a183dd8aaebd8a0f372a9
parent 193237 b9c4f3960eb2cbaf9c484dcaa38092bb38f72ff3
child 193239 9ec5db6a1684276d922d3a640dc809c952959870
push id27112
push usercbook@mozilla.com
push dateThu, 10 Jul 2014 12:47:23 +0000
treeherdermozilla-central@6e9f72bdd32e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs754215
milestone33.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 754215 - Refactor user-font loading code in preparation for lazier downloading. r=jdaggett
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/gfxUserFontSet.h
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -30,34 +30,103 @@ gfxUserFontSet::GetUserFontsLog()
 {
     static PRLogModuleInfo *sLog;
     if (!sLog)
         sLog = PR_NewLogModule("userfonts");
     return sLog;
 }
 #endif /* PR_LOGGING */
 
-#define LOG(args) PR_LOG(GetUserFontsLog(), PR_LOG_DEBUG, args)
-#define LOG_ENABLED() PR_LOG_TEST(GetUserFontsLog(), PR_LOG_DEBUG)
+#define LOG(args) PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG, args)
+#define LOG_ENABLED() PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG)
 
 static uint64_t sFontSetGeneration = 0;
 
+// Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
+// adapted to use Mozilla allocators and to allow the final
+// memory buffer to be adopted by the client.
+class ExpandingMemoryStream : public ots::OTSStream {
+public:
+    ExpandingMemoryStream(size_t initial, size_t limit)
+        : mLength(initial), mLimit(limit), mOff(0) {
+        mPtr = NS_Alloc(mLength);
+    }
+
+    ~ExpandingMemoryStream() {
+        NS_Free(mPtr);
+    }
+
+    // return the buffer, and give up ownership of it
+    // so the caller becomes responsible to call NS_Free
+    // when finished with it
+    void* forget() {
+        void* p = mPtr;
+        mPtr = nullptr;
+        return p;
+    }
+
+    bool WriteRaw(const void *data, size_t length) {
+        if ((mOff + length > mLength) ||
+            (mLength > std::numeric_limits<size_t>::max() - mOff)) {
+            if (mLength == mLimit) {
+                return false;
+            }
+            size_t newLength = (mLength + 1) * 2;
+            if (newLength < mLength) {
+                return false;
+            }
+            if (newLength > mLimit) {
+                newLength = mLimit;
+            }
+            mPtr = NS_Realloc(mPtr, newLength);
+            mLength = newLength;
+            return WriteRaw(data, length);
+        }
+        std::memcpy(static_cast<char*>(mPtr) + mOff, data, length);
+        mOff += length;
+        return true;
+    }
+
+    bool Seek(off_t position) {
+        if (position < 0) {
+            return false;
+        }
+        if (static_cast<size_t>(position) > mLength) {
+            return false;
+        }
+        mOff = position;
+        return true;
+    }
+
+    off_t Tell() const {
+        return mOff;
+    }
+
+private:
+    void*        mPtr;
+    size_t       mLength;
+    const size_t mLimit;
+    off_t        mOff;
+};
+
 // TODO: support for unicode ranges not yet implemented
 
-gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+gfxProxyFontEntry::gfxProxyFontEntry(gfxUserFontSet *aFontSet,
+             const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
              uint32_t aWeight,
              int32_t aStretch,
              uint32_t aItalicStyle,
              const nsTArray<gfxFontFeature>& aFeatureSettings,
              uint32_t aLanguageOverride,
              gfxSparseBitSet *aUnicodeRanges)
     : gfxFontEntry(NS_LITERAL_STRING("Proxy")),
       mLoadingState(NOT_LOADING),
       mUnsupportedFormat(false),
-      mLoader(nullptr)
+      mLoader(nullptr),
+      mFontSet(aFontSet)
 {
     mIsProxy = true;
     mSrcList = aFontFaceSrcList;
     mSrcIndex = 0;
     mWeight = aWeight;
     mStretch = aStretch;
     // XXX Currently, we don't distinguish 'italic' and 'oblique' styles;
     // we need to fix this. (Bug 543715)
@@ -96,16 +165,408 @@ gfxProxyFontEntry::Matches(const nsTArra
 
 gfxFont*
 gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
 {
     // cannot create an actual font for a proxy entry
     return nullptr;
 }
 
+static ots::TableAction
+OTSTableAction(uint32_t aTag, void *aUserData)
+{
+    // preserve Graphite, color glyph and SVG tables
+    if (aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') ||
+        aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') ||
+        aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') ||
+        aTag == TRUETYPE_TAG('G', 'l', 'a', 't') ||
+        aTag == TRUETYPE_TAG('F', 'e', 'a', 't') ||
+        aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
+        aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
+        aTag == TRUETYPE_TAG('C', 'P', 'A', 'L')) {
+        return ots::TABLE_ACTION_PASSTHRU;
+    }
+    return ots::TABLE_ACTION_DEFAULT;
+}
+
+struct OTSCallbackUserData {
+    gfxMixedFontFamily *mFamily;
+    gfxProxyFontEntry  *mProxy;
+};
+
+/* static */ bool
+gfxProxyFontEntry::OTSMessage(void *aUserData, const char *format, ...)
+{
+    va_list va;
+    va_start(va, format);
+
+    // buf should be more than adequate for any message OTS generates,
+    // so we don't worry about checking the result of vsnprintf()
+    char buf[512];
+    (void)vsnprintf(buf, sizeof(buf), format, va);
+
+    va_end(va);
+
+    OTSCallbackUserData *d = static_cast<OTSCallbackUserData*>(aUserData);
+    d->mProxy->mFontSet->LogMessage(d->mFamily, d->mProxy, buf);
+
+    return false;
+}
+
+// Call the OTS library to sanitize an sfnt before attempting to use it.
+// Returns a newly-allocated block, or nullptr in case of fatal errors.
+const uint8_t*
+gfxProxyFontEntry::SanitizeOpenTypeData(gfxMixedFontFamily *aFamily,
+                                        const uint8_t* aData,
+                                        uint32_t       aLength,
+                                        uint32_t&      aSaneLength,
+                                        bool           aIsCompressed)
+{
+    // limit output/expansion to 256MB
+    ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength,
+                                 1024 * 1024 * 256);
+
+    OTSCallbackUserData userData;
+    userData.mFamily = aFamily;
+    userData.mProxy = this;
+
+    ots::OTSContext otsContext;
+    otsContext.SetTableActionCallback(&OTSTableAction, nullptr);
+    otsContext.SetMessageCallback(&OTSMessage, &userData);
+
+    if (otsContext.Process(&output, aData, aLength)) {
+        aSaneLength = output.Tell();
+        return static_cast<uint8_t*>(output.forget());
+    } else {
+        aSaneLength = 0;
+        return nullptr;
+    }
+}
+
+void
+gfxProxyFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry,
+                                     bool aPrivate,
+                                     const nsAString& aOriginalName,
+                                     FallibleTArray<uint8_t>* aMetadata,
+                                     uint32_t aMetaOrigLen)
+{
+    if (!aFontEntry->mUserFontData) {
+        aFontEntry->mUserFontData = new gfxUserFontData;
+    }
+    gfxUserFontData* userFontData = aFontEntry->mUserFontData;
+    userFontData->mSrcIndex = mSrcIndex;
+    const gfxFontFaceSrc& src = mSrcList[mSrcIndex];
+    if (src.mIsLocal) {
+        userFontData->mLocalName = src.mLocalName;
+    } else {
+        userFontData->mURI = src.mURI;
+        userFontData->mPrincipal = mPrincipal;
+    }
+    userFontData->mPrivate = aPrivate;
+    userFontData->mFormat = src.mFormatFlags;
+    userFontData->mRealName = aOriginalName;
+    if (aMetadata) {
+        userFontData->mMetadata.SwapElements(*aMetadata);
+        userFontData->mMetaOrigLen = aMetaOrigLen;
+    }
+}
+
+struct WOFFHeader {
+    AutoSwap_PRUint32 signature;
+    AutoSwap_PRUint32 flavor;
+    AutoSwap_PRUint32 length;
+    AutoSwap_PRUint16 numTables;
+    AutoSwap_PRUint16 reserved;
+    AutoSwap_PRUint32 totalSfntSize;
+    AutoSwap_PRUint16 majorVersion;
+    AutoSwap_PRUint16 minorVersion;
+    AutoSwap_PRUint32 metaOffset;
+    AutoSwap_PRUint32 metaCompLen;
+    AutoSwap_PRUint32 metaOrigLen;
+    AutoSwap_PRUint32 privOffset;
+    AutoSwap_PRUint32 privLen;
+};
+
+static void
+CopyWOFFMetadata(const uint8_t* aFontData,
+                 uint32_t aLength,
+                 FallibleTArray<uint8_t>* aMetadata,
+                 uint32_t* aMetaOrigLen)
+{
+    // This function may be called with arbitrary, unvalidated "font" data
+    // from @font-face, so it needs to be careful to bounds-check, etc.,
+    // before trying to read anything.
+    // This just saves a copy of the compressed data block; it does NOT check
+    // that the block can be successfully decompressed, or that it contains
+    // well-formed/valid XML metadata.
+    if (aLength < sizeof(WOFFHeader)) {
+        return;
+    }
+    const WOFFHeader* woff = reinterpret_cast<const WOFFHeader*>(aFontData);
+    uint32_t metaOffset = woff->metaOffset;
+    uint32_t metaCompLen = woff->metaCompLen;
+    if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
+        return;
+    }
+    if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
+        return;
+    }
+    if (!aMetadata->SetLength(woff->metaCompLen)) {
+        return;
+    }
+    memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
+    *aMetaOrigLen = woff->metaOrigLen;
+}
+
+gfxProxyFontEntry::LoadStatus
+gfxProxyFontEntry::LoadNext(gfxMixedFontFamily *aFamily,
+                            bool& aLocalRulesUsed)
+{
+    uint32_t numSrc = mSrcList.Length();
+
+    NS_ASSERTION(mSrcIndex < numSrc,
+                 "already at the end of the src list for user font");
+
+    if (mLoadingState == NOT_LOADING) {
+        mLoadingState = LOADING_STARTED;
+        mUnsupportedFormat = false;
+    } else {
+        // we were already loading; move to the next source,
+        // but don't reset state - if we've already timed out,
+        // that counts against the new download
+        mSrcIndex++;
+    }
+
+    // load each src entry in turn, until a local face is found
+    // or a download begins successfully
+    while (mSrcIndex < numSrc) {
+        const gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
+
+        // src local ==> lookup and load immediately
+
+        if (currSrc.mIsLocal) {
+            gfxFontEntry *fe =
+                gfxPlatform::GetPlatform()->LookupLocalFont(this,
+                                                            currSrc.mLocalName);
+            aLocalRulesUsed = true;
+            if (fe) {
+                LOG(("fontset (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
+                     mFontSet, mSrcIndex,
+                     NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
+                     NS_ConvertUTF16toUTF8(aFamily->Name()).get(),
+                     uint32_t(mFontSet->mGeneration)));
+                fe->mFeatureSettings.AppendElements(mFeatureSettings);
+                fe->mLanguageOverride = mLanguageOverride;
+                // For src:local(), we don't care whether the request is from
+                // a private window as there's no issue of caching resources;
+                // local fonts are just available all the time.
+                StoreUserFontData(fe, false, nsString(), nullptr, 0);
+                mFontSet->ReplaceFontEntry(aFamily, this, fe);
+                return STATUS_LOADED;
+            } else {
+                LOG(("fontset (%p) [src %d] failed local: (%s) for (%s)\n",
+                     mFontSet, mSrcIndex,
+                     NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
+                     NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
+            }
+        }
+
+        // src url ==> start the load process
+        else {
+            if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
+                    currSrc.mFormatFlags)) {
+
+                nsIPrincipal *principal = nullptr;
+                bool bypassCache;
+                nsresult rv = mFontSet->CheckFontLoad(&currSrc, &principal,
+                                                      &bypassCache);
+
+                if (NS_SUCCEEDED(rv) && principal != nullptr) {
+                    if (!bypassCache) {
+                        // see if we have an existing entry for this source
+                        gfxFontEntry *fe = gfxUserFontSet::
+                            UserFontCache::GetFont(currSrc.mURI,
+                                                   principal,
+                                                   this,
+                                                   mFontSet->GetPrivateBrowsing());
+                        if (fe) {
+                            mFontSet->ReplaceFontEntry(aFamily, this, fe);
+                            return STATUS_LOADED;
+                        }
+                    }
+
+                    // record the principal returned by CheckFontLoad,
+                    // for use when creating a channel
+                    // and when caching the loaded entry
+                    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;
+
+                        // sync load font immediately
+                        rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer,
+                                                        bufferLength);
+
+                        if (NS_SUCCEEDED(rv) &&
+                            LoadFont(aFamily, buffer, bufferLength)) {
+                            return STATUS_LOADED;
+                        } else {
+                            mFontSet->LogMessage(aFamily, this,
+                                                 "font load failed",
+                                                 nsIScriptError::errorFlag,
+                                                 rv);
+                        }
+
+                    } else {
+                        // otherwise load font async
+                        rv = mFontSet->StartLoad(aFamily, this, &currSrc);
+                        bool loadOK = NS_SUCCEEDED(rv);
+
+                        if (loadOK) {
+#ifdef PR_LOGGING
+                            if (LOG_ENABLED()) {
+                                nsAutoCString fontURI;
+                                currSrc.mURI->GetSpec(fontURI);
+                                LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
+                                     mFontSet, mSrcIndex, fontURI.get(),
+                                     NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
+                            }
+#endif
+                            return STATUS_LOADING;
+                        } else {
+                            mFontSet->LogMessage(aFamily, this,
+                                                 "download failed",
+                                                 nsIScriptError::errorFlag,
+                                                 rv);
+                        }
+                    }
+                } else {
+                    mFontSet->LogMessage(aFamily, this, "download not allowed",
+                                         nsIScriptError::errorFlag, rv);
+                }
+            } else {
+                // We don't log a warning to the web console yet,
+                // as another source may load successfully
+                mUnsupportedFormat = true;
+            }
+        }
+
+        mSrcIndex++;
+    }
+
+    if (mUnsupportedFormat) {
+        mFontSet->LogMessage(aFamily, this, "no supported format found",
+                             nsIScriptError::warningFlag);
+    }
+
+    // all src's failed; mark this entry as unusable (so fallback will occur)
+    LOG(("userfonts (%p) failed all src for (%s)\n",
+        mFontSet, NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
+    mLoadingState = LOADING_FAILED;
+
+    return STATUS_END_OF_LIST;
+}
+
+gfxFontEntry*
+gfxProxyFontEntry::LoadFont(gfxMixedFontFamily *aFamily,
+                            const uint8_t *aFontData, uint32_t &aLength)
+{
+    gfxFontEntry *fe = nullptr;
+
+    gfxUserFontType fontType =
+        gfxFontUtils::DetermineFontDataType(aFontData, aLength);
+
+    // Unwrap/decompress/sanitize or otherwise munge the downloaded data
+    // to make a usable sfnt structure.
+
+    // Because platform font activation code may replace the name table
+    // in the font with a synthetic one, we save the original name so that
+    // it can be reported via the nsIDOMFontFace API.
+    nsAutoString originalFullName;
+
+    // Call the OTS sanitizer; this will also decode WOFF to sfnt
+    // if necessary. The original data in aFontData is left unchanged.
+    uint32_t saneLen;
+    const uint8_t* saneData =
+        SanitizeOpenTypeData(aFamily, aFontData, aLength, saneLen,
+                             fontType == GFX_USERFONT_WOFF);
+    if (!saneData) {
+        mFontSet->LogMessage(aFamily, this, "rejected by sanitizer");
+    }
+    if (saneData) {
+        // The sanitizer ensures that we have a valid sfnt and a usable
+        // name table, so this should never fail unless we're out of
+        // memory, and GetFullNameFromSFNT is not directly exposed to
+        // arbitrary/malicious data from the web.
+        gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
+                                          originalFullName);
+        // Here ownership of saneData is passed to the platform,
+        // which will delete it when no longer required
+        fe = gfxPlatform::GetPlatform()->MakePlatformFont(this,
+                                                          saneData,
+                                                          saneLen);
+        if (!fe) {
+            mFontSet->LogMessage(aFamily, this, "not usable by platform");
+        }
+    }
+
+    if (fe) {
+        // Save a copy of the metadata block (if present) for nsIDOMFontFace
+        // to use if required. Ownership of the metadata block will be passed
+        // to the gfxUserFontData record below.
+        FallibleTArray<uint8_t> metadata;
+        uint32_t metaOrigLen = 0;
+        if (fontType == GFX_USERFONT_WOFF) {
+            CopyWOFFMetadata(aFontData, aLength, &metadata, &metaOrigLen);
+        }
+
+        // copy OpenType feature/language settings from the proxy to the
+        // newly-created font entry
+        fe->mFeatureSettings.AppendElements(mFeatureSettings);
+        fe->mLanguageOverride = mLanguageOverride;
+        StoreUserFontData(fe, mFontSet->GetPrivateBrowsing(), originalFullName,
+                          &metadata, metaOrigLen);
+#ifdef PR_LOGGING
+        if (LOG_ENABLED()) {
+            nsAutoCString fontURI;
+            mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
+            LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
+                 this, mSrcIndex, fontURI.get(),
+                 NS_ConvertUTF16toUTF8(aFamily->Name()).get(),
+                 uint32_t(mFontSet->mGeneration)));
+        }
+#endif
+        mFontSet->ReplaceFontEntry(aFamily, this, fe);
+        gfxUserFontSet::UserFontCache::CacheFont(fe);
+    } else {
+#ifdef PR_LOGGING
+        if (LOG_ENABLED()) {
+            nsAutoCString fontURI;
+            mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
+            LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
+                 " error making platform font\n",
+                 this, mSrcIndex, fontURI.get(),
+                 NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
+        }
+#endif
+    }
+
+    // The downloaded data can now be discarded; the font entry is using the
+    // sanitized copy
+    NS_Free((void*)aFontData);
+
+    return fe;
+}
+
 gfxUserFontSet::gfxUserFontSet()
     : mFontFamilies(5), mLocalRulesUsed(false)
 {
     IncrementGeneration();
     gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
     if (fp) {
         fp->AddUserFontSet(this);
     }
@@ -170,17 +631,18 @@ gfxUserFontSet::AddFontFace(const nsAStr
         // it to the end of the list. gfxMixedFontFamily::AddFontEntry() will
         // automatically remove any earlier occurrence of the same proxy.
         family->AddFontEntry(existingProxyEntry);
         return existingProxyEntry;
     }
 
     // construct a new face and add it into the family
     gfxProxyFontEntry *proxyEntry =
-        new gfxProxyFontEntry(aFontFaceSrcList, aWeight, aStretch,
+        new gfxProxyFontEntry(this, aFontFaceSrcList,
+                              aWeight, aStretch,
                               aItalicStyle,
                               aFeatureSettings,
                               aLanguageOverride,
                               aUnicodeRanges);
     family->AddFontEntry(proxyEntry);
 #ifdef PR_LOGGING
     if (LOG_ENABLED()) {
         LOG(("userfonts (%p) added (%s) with style: %s weight: %d stretch: %d",
@@ -233,265 +695,53 @@ gfxUserFontSet::FindFontEntry(gfxFontFam
     // if currently loading, return null for now
     if (proxyEntry->mLoadingState > gfxProxyFontEntry::NOT_LOADING) {
         aWaitForUserFont =
             (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY);
         return nullptr;
     }
 
     // hasn't been loaded yet, start the load process
-    LoadStatus status;
+    gfxProxyFontEntry::LoadStatus status;
 
     // NOTE that if all sources in the entry fail, this will delete proxyEntry,
     // so we cannot use it again if status==STATUS_END_OF_LIST
-    status = LoadNext(family, proxyEntry);
+    status = proxyEntry->LoadNext(family, mLocalRulesUsed);
 
     // if the load succeeded immediately, the font entry was replaced so
     // search again
-    if (status == STATUS_LOADED) {
+    if (status == gfxProxyFontEntry::STATUS_LOADED) {
         return family->FindFontForStyle(aFontStyle, aNeedsBold);
     }
 
     // check whether we should wait for load to complete before painting
     // a fallback font -- but not if all sources failed (bug 633500)
-    aWaitForUserFont = (status != STATUS_END_OF_LIST) &&
+    aWaitForUserFont = (status != gfxProxyFontEntry::STATUS_END_OF_LIST) &&
         (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY);
 
     // if either loading or an error occurred, return null
     return nullptr;
 }
 
-// Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
-// adapted to use Mozilla allocators and to allow the final
-// memory buffer to be adopted by the client.
-class ExpandingMemoryStream : public ots::OTSStream {
-public:
-    ExpandingMemoryStream(size_t initial, size_t limit)
-        : mLength(initial), mLimit(limit), mOff(0) {
-        mPtr = NS_Alloc(mLength);
-    }
-
-    ~ExpandingMemoryStream() {
-        NS_Free(mPtr);
-    }
-
-    // return the buffer, and give up ownership of it
-    // so the caller becomes responsible to call NS_Free
-    // when finished with it
-    void* forget() {
-        void* p = mPtr;
-        mPtr = nullptr;
-        return p;
-    }
-
-    bool WriteRaw(const void *data, size_t length) {
-        if ((mOff + length > mLength) ||
-            (mLength > std::numeric_limits<size_t>::max() - mOff)) {
-            if (mLength == mLimit) {
-                return false;
-            }
-            size_t newLength = (mLength + 1) * 2;
-            if (newLength < mLength) {
-                return false;
-            }
-            if (newLength > mLimit) {
-                newLength = mLimit;
-            }
-            mPtr = NS_Realloc(mPtr, newLength);
-            mLength = newLength;
-            return WriteRaw(data, length);
-        }
-        std::memcpy(static_cast<char*>(mPtr) + mOff, data, length);
-        mOff += length;
-        return true;
-    }
-
-    bool Seek(off_t position) {
-        if (position < 0) {
-            return false;
-        }
-        if (static_cast<size_t>(position) > mLength) {
-            return false;
-        }
-        mOff = position;
-        return true;
-    }
-
-    off_t Tell() const {
-        return mOff;
-    }
-
-private:
-    void*        mPtr;
-    size_t       mLength;
-    const size_t mLimit;
-    off_t        mOff;
-};
-
-static ots::TableAction
-OTSTableAction(uint32_t aTag, void *aUserData)
-{
-    // preserve Graphite, color glyph and SVG tables
-    if (aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') ||
-        aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') ||
-        aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') ||
-        aTag == TRUETYPE_TAG('G', 'l', 'a', 't') ||
-        aTag == TRUETYPE_TAG('F', 'e', 'a', 't') ||
-        aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
-        aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
-        aTag == TRUETYPE_TAG('C', 'P', 'A', 'L')) {
-        return ots::TABLE_ACTION_PASSTHRU;
-    }
-    return ots::TABLE_ACTION_DEFAULT;
-}
-
-struct OTSCallbackUserData {
-    gfxUserFontSet     *mFontSet;
-    gfxMixedFontFamily *mFamily;
-    gfxProxyFontEntry  *mProxy;
-};
-
-/* static */ bool
-gfxUserFontSet::OTSMessage(void *aUserData, const char *format, ...)
-{
-    va_list va;
-    va_start(va, format);
-
-    // buf should be more than adequate for any message OTS generates,
-    // so we don't worry about checking the result of vsnprintf()
-    char buf[512];
-    (void)vsnprintf(buf, sizeof(buf), format, va);
-
-    va_end(va);
-
-    OTSCallbackUserData *d = static_cast<OTSCallbackUserData*>(aUserData);
-    d->mFontSet->LogMessage(d->mFamily, d->mProxy, buf);
-
-    return false;
-}
-
-// Call the OTS library to sanitize an sfnt before attempting to use it.
-// Returns a newly-allocated block, or nullptr in case of fatal errors.
-const uint8_t*
-gfxUserFontSet::SanitizeOpenTypeData(gfxMixedFontFamily *aFamily,
-                                     gfxProxyFontEntry *aProxy,
-                                     const uint8_t* aData, uint32_t aLength,
-                                     uint32_t& aSaneLength, bool aIsCompressed)
-{
-    // limit output/expansion to 256MB
-    ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength,
-                                 1024 * 1024 * 256);
-
-    OTSCallbackUserData userData;
-    userData.mFontSet = this;
-    userData.mFamily = aFamily;
-    userData.mProxy = aProxy;
-
-    ots::OTSContext otsContext;
-    otsContext.SetTableActionCallback(&OTSTableAction, nullptr);
-    otsContext.SetMessageCallback(&gfxUserFontSet::OTSMessage, &userData);
-
-    if (otsContext.Process(&output, aData, aLength)) {
-        aSaneLength = output.Tell();
-        return static_cast<uint8_t*>(output.forget());
-    } else {
-        aSaneLength = 0;
-        return nullptr;
-    }
-}
-
-static void
-StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy,
-                  bool aPrivate, const nsAString& aOriginalName,
-                  FallibleTArray<uint8_t>* aMetadata, uint32_t aMetaOrigLen)
-{
-    if (!aFontEntry->mUserFontData) {
-        aFontEntry->mUserFontData = new gfxUserFontData;
-    }
-    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->mPrivate = aPrivate;
-    userFontData->mFormat = src.mFormatFlags;
-    userFontData->mRealName = aOriginalName;
-    if (aMetadata) {
-        userFontData->mMetadata.SwapElements(*aMetadata);
-        userFontData->mMetaOrigLen = aMetaOrigLen;
-    }
-}
-
-struct WOFFHeader {
-    AutoSwap_PRUint32 signature;
-    AutoSwap_PRUint32 flavor;
-    AutoSwap_PRUint32 length;
-    AutoSwap_PRUint16 numTables;
-    AutoSwap_PRUint16 reserved;
-    AutoSwap_PRUint32 totalSfntSize;
-    AutoSwap_PRUint16 majorVersion;
-    AutoSwap_PRUint16 minorVersion;
-    AutoSwap_PRUint32 metaOffset;
-    AutoSwap_PRUint32 metaCompLen;
-    AutoSwap_PRUint32 metaOrigLen;
-    AutoSwap_PRUint32 privOffset;
-    AutoSwap_PRUint32 privLen;
-};
-
-void
-gfxUserFontSet::CopyWOFFMetadata(const uint8_t* aFontData,
-                                 uint32_t aLength,
-                                 FallibleTArray<uint8_t>* aMetadata,
-                                 uint32_t* aMetaOrigLen)
-{
-    // This function may be called with arbitrary, unvalidated "font" data
-    // from @font-face, so it needs to be careful to bounds-check, etc.,
-    // before trying to read anything.
-    // This just saves a copy of the compressed data block; it does NOT check
-    // that the block can be successfully decompressed, or that it contains
-    // well-formed/valid XML metadata.
-    if (aLength < sizeof(WOFFHeader)) {
-        return;
-    }
-    const WOFFHeader* woff = reinterpret_cast<const WOFFHeader*>(aFontData);
-    uint32_t metaOffset = woff->metaOffset;
-    uint32_t metaCompLen = woff->metaCompLen;
-    if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
-        return;
-    }
-    if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
-        return;
-    }
-    if (!aMetadata->SetLength(woff->metaCompLen)) {
-        return;
-    }
-    memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
-    *aMetaOrigLen = woff->metaOrigLen;
-}
-
 // This is called when a font download finishes.
 // Ownership of aFontData passes in here, and the font set must
 // ensure that it is eventually deleted via NS_Free().
 bool
 gfxUserFontSet::OnLoadComplete(gfxMixedFontFamily *aFamily,
                                gfxProxyFontEntry *aProxy,
                                const uint8_t *aFontData, uint32_t aLength,
                                nsresult aDownloadStatus)
 {
     // forget about the loader, as we no longer potentially need to cancel it
     // if the entry is obsoleted
     aProxy->mLoader = nullptr;
 
     // download successful, make platform font using font data
     if (NS_SUCCEEDED(aDownloadStatus)) {
-        gfxFontEntry *fe = LoadFont(aFamily, aProxy, aFontData, aLength);
+        gfxFontEntry *fe = aProxy->LoadFont(aFamily, aFontData, aLength);
         aFontData = nullptr;
 
         if (fe) {
             IncrementGeneration();
             return true;
         }
 
     } else {
@@ -501,173 +751,26 @@ gfxUserFontSet::OnLoadComplete(gfxMixedF
                    aDownloadStatus);
     }
 
     if (aFontData) {
         NS_Free((void*)aFontData);
     }
 
     // error occurred, load next src
-    (void)LoadNext(aFamily, aProxy);
+    (void)aProxy->LoadNext(aFamily, mLocalRulesUsed);
 
     // We ignore the status returned by LoadNext();
     // even if loading failed, we need to bump the font-set generation
     // and return true in order to trigger reflow, so that fallback
     // will be used where the text was "masked" by the pending download
     IncrementGeneration();
     return true;
 }
 
-
-gfxUserFontSet::LoadStatus
-gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
-                         gfxProxyFontEntry *aProxyEntry)
-{
-    uint32_t numSrc = aProxyEntry->mSrcList.Length();
-
-    NS_ASSERTION(aProxyEntry->mSrcIndex < numSrc,
-                 "already at the end of the src list for user font");
-
-    if (aProxyEntry->mLoadingState == gfxProxyFontEntry::NOT_LOADING) {
-        aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_STARTED;
-        aProxyEntry->mUnsupportedFormat = false;
-    } else {
-        // we were already loading; move to the next source,
-        // but don't reset state - if we've already timed out,
-        // that counts against the new download
-        aProxyEntry->mSrcIndex++;
-    }
-
-    // load each src entry in turn, until a local face is found
-    // or a download begins successfully
-    while (aProxyEntry->mSrcIndex < numSrc) {
-        const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[aProxyEntry->mSrcIndex];
-
-        // src local ==> lookup and load immediately
-
-        if (currSrc.mIsLocal) {
-            gfxFontEntry *fe =
-                gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry,
-                                                            currSrc.mLocalName);
-            mLocalRulesUsed = true;
-            if (fe) {
-                LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
-                     this, aProxyEntry->mSrcIndex,
-                     NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
-                     NS_ConvertUTF16toUTF8(aFamily->Name()).get(),
-                     uint32_t(mGeneration)));
-                fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings);
-                fe->mLanguageOverride = aProxyEntry->mLanguageOverride;
-                // For src:local(), we don't care whether the request is from
-                // a private window as there's no issue of caching resources;
-                // local fonts are just available all the time.
-                StoreUserFontData(fe, aProxyEntry, false, nsString(), nullptr, 0);
-                ReplaceFontEntry(aFamily, aProxyEntry, fe);
-                return STATUS_LOADED;
-            } else {
-                LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
-                     this, aProxyEntry->mSrcIndex,
-                     NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
-                     NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
-            }
-        }
-
-        // src url ==> start the load process
-        else {
-            if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
-                    currSrc.mFormatFlags)) {
-
-                nsIPrincipal *principal = nullptr;
-                bool bypassCache;
-                nsresult rv = CheckFontLoad(&currSrc, &principal, &bypassCache);
-
-                if (NS_SUCCEEDED(rv) && principal != nullptr) {
-                    if (!bypassCache) {
-                        // see if we have an existing entry for this source
-                        gfxFontEntry *fe =
-                            UserFontCache::GetFont(currSrc.mURI, principal,
-                                                   aProxyEntry,
-                                                   GetPrivateBrowsing());
-                        if (fe) {
-                            ReplaceFontEntry(aFamily, aProxyEntry, fe);
-                            return STATUS_LOADED;
-                        }
-                    }
-
-                    // 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;
-
-                        // sync load font immediately
-                        rv = SyncLoadFontData(aProxyEntry, &currSrc,
-                                              buffer, bufferLength);
-                        if (NS_SUCCEEDED(rv) && LoadFont(aFamily, aProxyEntry,
-                                                         buffer, bufferLength)) {
-                            return STATUS_LOADED;
-                        } else {
-                            LogMessage(aFamily, aProxyEntry,
-                                       "font load failed",
-                                       nsIScriptError::errorFlag, rv);
-                        }
-                    } else {
-                        // otherwise load font async
-                        rv = StartLoad(aFamily, 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(aFamily->Name()).get()));
-                            }
-#endif
-                            return STATUS_LOADING;
-                        } else {
-                            LogMessage(aFamily, aProxyEntry,
-                                       "download failed",
-                                       nsIScriptError::errorFlag, rv);
-                        }
-                    }
-                } else {
-                    LogMessage(aFamily, 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;
-            }
-        }
-
-        aProxyEntry->mSrcIndex++;
-    }
-
-    if (aProxyEntry->mUnsupportedFormat) {
-        LogMessage(aFamily, aProxyEntry, "no supported format found",
-                   nsIScriptError::warningFlag);
-    }
-
-    // all src's failed; mark this entry as unusable (so fallback will occur)
-    LOG(("userfonts (%p) failed all src for (%s)\n",
-        this, NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
-    aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_FAILED;
-
-    return STATUS_END_OF_LIST;
-}
-
 void
 gfxUserFontSet::IncrementGeneration()
 {
     // add one, increment again if zero
     ++sFontSetGeneration;
     if (sFontSetGeneration == 0)
        ++sFontSetGeneration;
     mGeneration = sFontSetGeneration;
@@ -676,108 +779,16 @@ gfxUserFontSet::IncrementGeneration()
 void
 gfxUserFontSet::RebuildLocalRules()
 {
     if (mLocalRulesUsed) {
         DoRebuildUserFontSet();
     }
 }
 
-gfxFontEntry*
-gfxUserFontSet::LoadFont(gfxMixedFontFamily *aFamily,
-                         gfxProxyFontEntry *aProxy,
-                         const uint8_t *aFontData, uint32_t &aLength)
-{
-    gfxFontEntry *fe = nullptr;
-
-    gfxUserFontType fontType =
-        gfxFontUtils::DetermineFontDataType(aFontData, aLength);
-
-    // Unwrap/decompress/sanitize or otherwise munge the downloaded data
-    // to make a usable sfnt structure.
-
-    // Because platform font activation code may replace the name table
-    // in the font with a synthetic one, we save the original name so that
-    // it can be reported via the nsIDOMFontFace API.
-    nsAutoString originalFullName;
-
-    // Call the OTS sanitizer; this will also decode WOFF to sfnt
-    // if necessary. The original data in aFontData is left unchanged.
-    uint32_t saneLen;
-    const uint8_t* saneData =
-        SanitizeOpenTypeData(aFamily, aProxy, aFontData, aLength, saneLen,
-                             fontType == GFX_USERFONT_WOFF);
-    if (!saneData) {
-        LogMessage(aFamily, aProxy, "rejected by sanitizer");
-    }
-    if (saneData) {
-        // The sanitizer ensures that we have a valid sfnt and a usable
-        // name table, so this should never fail unless we're out of
-        // memory, and GetFullNameFromSFNT is not directly exposed to
-        // arbitrary/malicious data from the web.
-        gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
-                                          originalFullName);
-        // Here ownership of saneData is passed to the platform,
-        // which will delete it when no longer required
-        fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy,
-                                                          saneData,
-                                                          saneLen);
-        if (!fe) {
-            LogMessage(aFamily, aProxy, "not usable by platform");
-        }
-    }
-
-    if (fe) {
-        // Save a copy of the metadata block (if present) for nsIDOMFontFace
-        // to use if required. Ownership of the metadata block will be passed
-        // to the gfxUserFontData record below.
-        FallibleTArray<uint8_t> metadata;
-        uint32_t metaOrigLen = 0;
-        if (fontType == GFX_USERFONT_WOFF) {
-            CopyWOFFMetadata(aFontData, aLength, &metadata, &metaOrigLen);
-        }
-
-        // copy OpenType feature/language settings from the proxy to the
-        // newly-created font entry
-        fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings);
-        fe->mLanguageOverride = aProxy->mLanguageOverride;
-        StoreUserFontData(fe, aProxy, GetPrivateBrowsing(),
-                          originalFullName, &metadata, metaOrigLen);
-#ifdef PR_LOGGING
-        if (LOG_ENABLED()) {
-            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(aFamily->Name()).get(),
-                 uint32_t(mGeneration)));
-        }
-#endif
-        ReplaceFontEntry(aFamily, aProxy, fe);
-        UserFontCache::CacheFont(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",
-                 this, aProxy->mSrcIndex, fontURI.get(),
-                 NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
-        }
-#endif
-    }
-
-    // The downloaded data can now be discarded; the font entry is using the
-    // sanitized copy
-    NS_Free((void*)aFontData);
-
-    return fe;
-}
-
 gfxFontFamily*
 gfxUserFontSet::GetFamily(const nsAString& aFamilyName) const
 {
     nsAutoString key(aFamilyName);
     ToLowerCase(key);
 
     return mFontFamilies.GetWeak(key);
 }
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -130,16 +130,17 @@ public:
     void DetachFontEntries() {
         mAvailableFonts.Clear();
     }
 };
 
 class gfxProxyFontEntry;
 
 class gfxUserFontSet {
+    friend class gfxProxyFontEntry;
 
 public:
 
     NS_INLINE_DECL_REFCOUNTING(gfxUserFontSet)
 
     gfxUserFontSet();
 
     enum {
@@ -152,24 +153,16 @@ public:
         FLAG_FORMAT_EOT            = 1 << 4,
         FLAG_FORMAT_SVG            = 1 << 5,
         FLAG_FORMAT_WOFF           = 1 << 6,
 
         // mask of all unused bits, update when adding new formats
         FLAG_FORMAT_NOT_USED       = ~((1 << 7)-1)
     };
 
-    enum LoadStatus {
-        STATUS_LOADING = 0,
-        STATUS_LOADED,
-        STATUS_FORMAT_NOT_SUPPORTED,
-        STATUS_ERROR,
-        STATUS_END_OF_LIST
-    };
-
 
     // add in a font face
     // weight - [100, 900] (multiples of 100)
     // stretch = [NS_FONT_STRETCH_ULTRA_CONDENSED, NS_FONT_STRETCH_ULTRA_EXPANDED]
     // italic style = constants in gfxFontConstants.h, e.g. NS_FONT_STYLE_NORMAL
     // language override = result of calling gfxFontStyle::ParseFontLanguageOverride
     // TODO: support for unicode ranges not yet implemented
     gfxFontEntry *AddFontFace(const nsAString& aFamilyName,
@@ -408,78 +401,61 @@ public:
 
 protected:
     // Protected destructor, to discourage deletion outside of Release():
     virtual ~gfxUserFontSet();
 
     // Return whether the font set is associated with a private-browsing tab.
     virtual bool GetPrivateBrowsing() = 0;
 
-    // for a given proxy font entry, attempt to load the next resource
-    // in the src list
-    LoadStatus LoadNext(gfxMixedFontFamily *aFamily,
-                        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(gfxMixedFontFamily *aFamily,
-                           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) = 0;
 
     // report a problem of some kind (implemented in nsUserFontSet)
     virtual nsresult LogMessage(gfxMixedFontFamily *aFamily,
                                 gfxProxyFontEntry *aProxy,
                                 const char *aMessage,
                                 uint32_t aFlags = nsIScriptError::errorFlag,
                                 nsresult aStatus = NS_OK) = 0;
 
-    const uint8_t* SanitizeOpenTypeData(gfxMixedFontFamily *aFamily,
-                                        gfxProxyFontEntry *aProxy,
-                                        const uint8_t* aData,
-                                        uint32_t aLength,
-                                        uint32_t& aSaneLength,
-                                        bool aIsCompressed);
-
-    static bool OTSMessage(void *aUserData, const char *format, ...);
-
     // helper method for performing the actual userfont set rebuild
     virtual void DoRebuildUserFontSet() = 0;
 
     // font families defined by @font-face rules
     nsRefPtrHashtable<nsStringHashKey, gfxMixedFontFamily> mFontFamilies;
 
     uint64_t        mGeneration;
 
     // true when local names have been looked up, false otherwise
     bool mLocalRulesUsed;
 
     static PRLogModuleInfo* GetUserFontsLog();
-
-private:
-    static void CopyWOFFMetadata(const uint8_t* aFontData,
-                                 uint32_t aLength,
-                                 FallibleTArray<uint8_t>* aMetadata,
-                                 uint32_t* aMetaOrigLen);
 };
 
 // acts a placeholder until the real font is downloaded
 
 class gfxProxyFontEntry : public gfxFontEntry {
     friend class gfxUserFontSet;
+    friend class nsUserFontSet;
+    friend class nsFontFaceLoader;
 
 public:
-    gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
+    enum LoadStatus {
+        STATUS_LOADING = 0,
+        STATUS_LOADED,
+        STATUS_FORMAT_NOT_SUPPORTED,
+        STATUS_ERROR,
+        STATUS_END_OF_LIST
+    };
+
+    gfxProxyFontEntry(gfxUserFontSet *aFontSet,
+                      const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                       uint32_t aWeight,
                       int32_t aStretch,
                       uint32_t aItalicStyle,
                       const nsTArray<gfxFontFeature>& aFeatureSettings,
                       uint32_t aLanguageOverride,
                       gfxSparseBitSet *aUnicodeRanges);
 
     virtual ~gfxProxyFontEntry();
@@ -490,29 +466,59 @@ public:
                  int32_t aStretch,
                  uint32_t aItalicStyle,
                  const nsTArray<gfxFontFeature>& aFeatureSettings,
                  uint32_t aLanguageOverride,
                  gfxSparseBitSet *aUnicodeRanges);
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold);
 
+protected:
+    const uint8_t* SanitizeOpenTypeData(gfxMixedFontFamily *aFamily,
+                                        const uint8_t* aData,
+                                        uint32_t aLength,
+                                        uint32_t& aSaneLength,
+                                        bool aIsCompressed);
+
+    // Attempt to load the next resource in the src list.
+    // aLocalRules is set to true if an attempt was made to load a
+    // local() font was loaded, and left as it is otherwise.
+    LoadStatus LoadNext(gfxMixedFontFamily *aFamily,
+                        bool& aLocalRulesUsed);
+
+    // helper method for creating a platform font
+    // returns font entry if platform font creation successful
+    // Ownership of aFontData is passed in here; the font must
+    // ensure that it is eventually deleted with NS_Free().
+    gfxFontEntry* LoadFont(gfxMixedFontFamily *aFamily,
+                           const uint8_t *aFontData, uint32_t &aLength);
+
+    // store metadata and src details for current src into aFontEntry
+    void StoreUserFontData(gfxFontEntry*      aFontEntry,
+                           bool               aPrivate,
+                           const nsAString&   aOriginalName,
+                           FallibleTArray<uint8_t>* aMetadata,
+                           uint32_t           aMetaOrigLen);
+
+    static bool OTSMessage(void *aUserData, const char *format, ...);
+
     // note that code depends on the ordering of these values!
     enum LoadingState {
         NOT_LOADING = 0,     // not started to load any font resources yet
         LOADING_STARTED,     // loading has started; hide fallback font
         LOADING_ALMOST_DONE, // timeout happened but we're nearly done,
                              // so keep hiding fallback font
         LOADING_SLOWLY,      // timeout happened and we're not nearly done,
                              // so use the fallback font
         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
+    gfxUserFontSet          *mFontSet; // font-set to which the proxy belongs
     nsCOMPtr<nsIPrincipal>   mPrincipal;
 };
 
 
 #endif /* GFX_USER_FONT_SET_H */