Bug 512566. Load font data url's synchronously, gfx changes. r=jfkthame
authorJohn Daggett <jdaggett@mozilla.com>
Fri, 16 Mar 2012 12:31:01 +0900
changeset 92876 f06bc45e46ae007004f424fa9094c238a0a19dfd
parent 92875 86a7d913b1be60ca68f02747e9b82e14ad701586
child 92877 2c023d5f75846ba126c4872af5c203f81c35d1c2
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs512566
milestone14.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 512566. Load font data url's synchronously, gfx changes. r=jfkthame
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/gfxUserFontSet.h
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -41,16 +41,18 @@
 #endif /* MOZ_LOGGING */
 #include "prlog.h"
 
 #include "gfxUserFontSet.h"
 #include "gfxPlatform.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "prlong.h"
+#include "nsNetUtil.h"
+#include "nsIProtocolHandler.h"
 
 #include "woff.h"
 
 #include "opentype-sanitiser.h"
 #include "ots-memory-stream.h"
 
 using namespace mozilla;
 
@@ -447,141 +449,30 @@ gfxUserFontSet::CopyWOFFMetadata(const P
     }
     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 
+bool
 gfxUserFontSet::OnLoadComplete(gfxProxyFontEntry *aProxy,
                                const PRUint8 *aFontData, PRUint32 aLength,
                                nsresult aDownloadStatus)
 {
     // download successful, make platform font using font data
     if (NS_SUCCEEDED(aDownloadStatus)) {
-        gfxFontEntry *fe = nsnull;
-
-        gfxUserFontType fontType =
-            gfxFontUtils::DetermineFontDataType(aFontData, aLength);
+        gfxFontEntry *fe = LoadFont(aProxy, aFontData, aLength);
 
-        // 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.
-        // NOTE: after the non-OTS codepath using PrepareOpenTypeData is
-        // removed, we should defer this until after we've created the new
-        // fontEntry.
-        nsTArray<PRUint8> metadata;
-        PRUint32 metaOrigLen = 0;
-        if (fontType == GFX_USERFONT_WOFF) {
-            CopyWOFFMetadata(aFontData, aLength, &metadata, &metaOrigLen);
+        if (fe) {
+            IncrementGeneration();
+            return true;
         }
 
-        // 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;
-
-        if (gfxPlatform::GetPlatform()->SanitizeDownloadedFonts()) {
-           // Call the OTS sanitizer; this will also decode WOFF to sfnt
-            // if necessary. The original data in aFontData is left unchanged.
-            PRUint32 saneLen;
-            const PRUint8* saneData =
-                SanitizeOpenTypeData(aFontData, aLength, saneLen,
-                                     fontType == GFX_USERFONT_WOFF);
-            if (!saneData) {
-                LogMessage(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(aProxy, "not usable by platform");
-                }
-            }
-        } else {
-            // FIXME: this code can be removed once we remove the pref to
-            // disable the sanitizer; the PrepareOpenTypeData and
-            // ValidateSFNTHeaders functions will then be obsolete.
-            aFontData = PrepareOpenTypeData(aFontData, &aLength);
-
-            if (aFontData) {
-                if (gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
-                    // ValidateSFNTHeaders has checked that we have a valid
-                    // sfnt structure and a usable 'name' table
-                    gfxFontUtils::GetFullNameFromSFNT(aFontData, aLength,
-                                                      originalFullName);
-                    // Here ownership of aFontData is passed to the platform,
-                    // which will delete it when no longer required
-                    fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy,
-                                                                      aFontData,
-                                                                      aLength);
-                    if (!fe) {
-                        LogMessage(aProxy, "not usable by platform");
-                    }
-                    aFontData = nsnull; // we must NOT free this below!
-                } else {
-                    // the data was unusable, so just discard it
-                    // (error will be reported below, if logging is enabled)
-                    LogMessage(aProxy, "SFNT header or tables invalid");
-                }
-            }
-        }
-
-        if (aFontData) {
-            NS_Free((void*)aFontData);
-            aFontData = nsnull;
-        }
-
-        if (fe) {
-            // 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, originalFullName,
-                              &metadata, metaOrigLen);
-#ifdef PR_LOGGING
-            // must do this before ReplaceFontEntry() because that will
-            // clear the proxy's mFamily pointer!
-            if (LOG_ENABLED()) {
-                nsCAutoString 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(),
-                     PRUint32(mGeneration)));
-            }
-#endif
-            ReplaceFontEntry(aProxy, fe);
-            IncrementGeneration();
-            return true;
-        } else {
-#ifdef PR_LOGGING
-            if (LOG_ENABLED()) {
-                nsCAutoString 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(aProxy->mFamily->Name()).get()));
-            }
-#endif
-        }
     } else {
         // download failed
         LogMessage(aProxy, "download failed", nsIScriptError::errorFlag,
                    aDownloadStatus);
     }
 
     if (aFontData) {
         NS_Free((void*)aFontData);
@@ -617,17 +508,17 @@ gfxUserFontSet::LoadNext(gfxProxyFontEnt
         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
+        // src local ==> lookup and load immediately
 
         if (currSrc.mIsLocal) {
             gfxFontEntry *fe =
                 gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry,
                                                             currSrc.mLocalName);
             if (fe) {
                 LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
                      this, aProxyEntry->mSrcIndex,
@@ -646,32 +537,61 @@ gfxUserFontSet::LoadNext(gfxProxyFontEnt
                      NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
             }
         }
 
         // src url ==> start the load process
         else {
             if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
                     currSrc.mFormatFlags)) {
-                nsresult rv = StartLoad(aProxyEntry, &currSrc);
-                bool loadOK = NS_SUCCEEDED(rv);
-                if (loadOK) {
+
+                nsresult rv;
+                bool loadDoesntSpin = false;
+                rv = NS_URIChainHasFlags(currSrc.mURI,
+                       nsIProtocolHandler::URI_SYNC_LOAD_DOESNT_SPIN_EVENT_LOOP,
+                       &loadDoesntSpin);
+
+                if (NS_SUCCEEDED(rv) && loadDoesntSpin)
+                {
+                    PRUint8 *buffer = nsnull;
+                    PRUint32 bufferLength = 0;
+
+                    // sync load font immediately
+                    rv = SyncLoadFontData(aProxyEntry, &currSrc, buffer,
+                                          bufferLength);
+
+                    const PRUint8 *buf2 = buffer;
+                    if (NS_SUCCEEDED(rv) &&
+                        LoadFont(aProxyEntry, buf2, bufferLength)) {
+                        return STATUS_LOADED;
+                    } else {
+                        LogMessage(aProxyEntry, "font load failed",
+                                   nsIScriptError::errorFlag, rv);
+                    }
+
+                } else {
+                    // otherwise load font async
+                    rv = StartLoad(aProxyEntry, &currSrc);
+                    PRBool loadOK = NS_SUCCEEDED(rv);
+
+                    if (loadOK) {
 #ifdef PR_LOGGING
-                    if (LOG_ENABLED()) {
-                        nsCAutoString 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()) {
+                            nsCAutoString 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 {
                 // We don't log a warning to the web console yet,
                 // as another source may load successfully
                 aProxyEntry->mUnsupportedFormat = true;
             }
         }
 
@@ -697,16 +617,145 @@ gfxUserFontSet::IncrementGeneration()
     // add one, increment again if zero
     LL_ADD(sFontSetGeneration, sFontSetGeneration, 1);
     if (LL_IS_ZERO(sFontSetGeneration))
         LL_ADD(sFontSetGeneration, sFontSetGeneration, 1);
     mGeneration = sFontSetGeneration;
 }
 
 
+gfxFontEntry*
+gfxUserFontSet::LoadFont(gfxProxyFontEntry *aProxy,
+                         const PRUint8* &aFontData, PRUint32 &aLength)
+{
+    gfxFontEntry *fe = nsnull;
+
+    gfxUserFontType fontType =
+        gfxFontUtils::DetermineFontDataType(aFontData, aLength);
+
+    // 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.
+    // NOTE: after the non-OTS codepath using PrepareOpenTypeData is
+    // removed, we should defer this until after we've created the new
+    // fontEntry.
+    nsTArray<PRUint8> metadata;
+    PRUint32 metaOrigLen = 0;
+    if (fontType == GFX_USERFONT_WOFF) {
+        CopyWOFFMetadata(aFontData, aLength, &metadata, &metaOrigLen);
+    }
+
+    // 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;
+
+    if (gfxPlatform::GetPlatform()->SanitizeDownloadedFonts()) {
+       // Call the OTS sanitizer; this will also decode WOFF to sfnt
+        // if necessary. The original data in aFontData is left unchanged.
+        PRUint32 saneLen;
+        const PRUint8* saneData =
+            SanitizeOpenTypeData(aFontData, aLength, saneLen,
+                                 fontType == GFX_USERFONT_WOFF);
+        if (!saneData) {
+            LogMessage(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(aProxy, "not usable by platform");
+            }
+        }
+    } else {
+        // FIXME: this code can be removed once we remove the pref to
+        // disable the sanitizer; the PrepareOpenTypeData and
+        // ValidateSFNTHeaders functions will then be obsolete.
+        aFontData = PrepareOpenTypeData(aFontData, &aLength);
+
+        if (aFontData) {
+            if (gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
+                // ValidateSFNTHeaders has checked that we have a valid
+                // sfnt structure and a usable 'name' table
+                gfxFontUtils::GetFullNameFromSFNT(aFontData, aLength,
+                                                  originalFullName);
+                // Here ownership of aFontData is passed to the platform,
+                // which will delete it when no longer required
+                fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy,
+                                                                  aFontData,
+                                                                  aLength);
+                if (!fe) {
+                    LogMessage(aProxy, "not usable by platform");
+                }
+                aFontData = nsnull; // we must NOT free this!
+            } else {
+                // the data was unusable, so just discard it
+                // (error will be reported below, if logging is enabled)
+                LogMessage(aProxy, "SFNT header or tables invalid");
+            }
+        }
+    }
+
+    if (aFontData) {
+        NS_Free((void*)aFontData);
+        aFontData = nsnull;
+    }
+
+    if (fe) {
+        // 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, originalFullName,
+                          &metadata, metaOrigLen);
+#ifdef PR_LOGGING
+        // must do this before ReplaceFontEntry() because that will
+        // clear the proxy's mFamily pointer!
+        if (LOG_ENABLED()) {
+            nsCAutoString 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(),
+                 PRUint32(mGeneration)));
+        }
+#endif
+        ReplaceFontEntry(aProxy, fe);
+
+    } else {
+
+#ifdef PR_LOGGING
+        if (LOG_ENABLED()) {
+            nsCAutoString 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(aProxy->mFamily->Name()).get()));
+        }
+#endif
+
+    }
+
+    return fe;
+}
+
 gfxMixedFontFamily*
 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
@@ -262,16 +262,33 @@ public:
     // increment the generation on font load
     void IncrementGeneration();
 
 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 PRUint8* &aFontData, PRUint32 &aLength);
+
+    // parse data for a data URL
+    virtual nsresult SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
+                                      const gfxFontFaceSrc *aFontFaceSrc,
+                                      PRUint8* &aBuffer,
+                                      PRUint32 &aBufferLength)
+    {
+        // implemented in nsUserFontSet
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
+
     gfxMixedFontFamily *GetFamily(const nsAString& aName) const;
 
     // report a problem of some kind (implemented in nsUserFontSet)
     virtual nsresult LogMessage(gfxProxyFontEntry *aProxy,
                                 const char *aMessage,
                                 PRUint32 aFlags = nsIScriptError::errorFlag,
                                 nsresult aStatus = 0) = 0;