bug 650639 - cancel current font-downloaders when updating the user font set. r=dbaron
authorJonathan Kew <jfkthame@gmail.com>
Thu, 28 Apr 2011 06:04:45 +0100
changeset 68697 a9dd6038db6a1cc848554b4de07a098731a60006
parent 68696 02c2e04f09cf8206d4ab5308e107b681091477f6
child 68698 60ea7d2f4289fbf904ad9d3efdf0a93284cbaa81
push idunknown
push userunknown
push dateunknown
reviewersdbaron
bugs650639
milestone6.0a1
bug 650639 - cancel current font-downloaders when updating the user font set. r=dbaron
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/gfxUserFontSet.h
layout/style/nsFontFaceLoader.cpp
layout/style/nsFontFaceLoader.h
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -456,30 +456,20 @@ CacheLayoutTablesFromWOFF(const PRUint8*
     PreloadTableFromWOFF(aFontData, aLength, TRUETYPE_TAG('G','S','U','B'),
                          aFontEntry);
 }
 
 // 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().
 PRBool 
-gfxUserFontSet::OnLoadComplete(gfxFontEntry *aFontToLoad,
+gfxUserFontSet::OnLoadComplete(gfxProxyFontEntry *aProxy,
                                const PRUint8 *aFontData, PRUint32 aLength, 
                                nsresult aDownloadStatus)
 {
-    NS_ASSERTION(aFontToLoad->mIsProxy,
-                 "trying to load font data for wrong font entry type");
-
-    if (!aFontToLoad->mIsProxy) {
-        NS_Free((void*)aFontData);
-        return PR_FALSE;
-    }
-
-    gfxProxyFontEntry *pe = static_cast<gfxProxyFontEntry*> (aFontToLoad);
-
     // download successful, make platform font using font data
     if (NS_SUCCEEDED(aDownloadStatus)) {
         gfxFontEntry *fe = nsnull;
 
         // Unwrap/decompress/sanitize or otherwise munge the downloaded data
         // to make a usable sfnt structure.
 
         if (gfxPlatform::GetPlatform()->SanitizeDownloadedFonts()) {
@@ -491,24 +481,24 @@ gfxUserFontSet::OnLoadComplete(gfxFontEn
             PRUint32 saneLen;
             const PRUint8* saneData =
                 SanitizeOpenTypeData(aFontData, aLength, saneLen,
                                      fontType == GFX_USERFONT_WOFF);
 #ifdef DEBUG
             if (!saneData) {
                 char buf[1000];
                 sprintf(buf, "downloaded font rejected for \"%s\"",
-                        NS_ConvertUTF16toUTF8(pe->FamilyName()).get());
+                        NS_ConvertUTF16toUTF8(aProxy->FamilyName()).get());
                 NS_WARNING(buf);
             }
 #endif
             if (saneData) {
                 // Here ownership of saneData is passed to the platform,
                 // which will delete it when no longer required
-                fe = gfxPlatform::GetPlatform()->MakePlatformFont(pe,
+                fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy,
                                                                   saneData,
                                                                   saneLen);
                 if (fe) {
                     // if aFontData includes OpenType layout tables, we need to
                     // cache them in the font entry for harfbuzz to use,
                     // as they will have been dropped from the sanitized sfnt
                     // (temporary hack, see CacheLayoutTablesFromSFNT)
                     switch (fontType) {
@@ -532,17 +522,17 @@ gfxUserFontSet::OnLoadComplete(gfxFontEn
             // disable the sanitizer; the PrepareOpenTypeData and
             // ValidateSFNTHeaders functions will then be obsolete.
             aFontData = PrepareOpenTypeData(aFontData, &aLength);
 
             if (aFontData) {
                 if (gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
                     // Here ownership of aFontData is passed to the platform,
                     // which will delete it when no longer required
-                    fe = gfxPlatform::GetPlatform()->MakePlatformFont(pe,
+                    fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy,
                                                                       aFontData,
                                                                       aLength);
                     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)
                     NS_WARNING("failed to make platform font from download");
                 }
@@ -552,65 +542,67 @@ gfxUserFontSet::OnLoadComplete(gfxFontEn
         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(pe->mFeatureSettings);
-            fe->mLanguageOverride = pe->mLanguageOverride;
+            fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings);
+            fe->mLanguageOverride = aProxy->mLanguageOverride;
 
-            ReplaceFontEntry(pe, fe);
-            IncrementGeneration();
 #ifdef PR_LOGGING
+            // must do this before ReplaceFontEntry() because that will
+            // clear the proxy's mFamily pointer!
             if (LOG_ENABLED()) {
                 nsCAutoString fontURI;
-                pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI);
+                aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
                 LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
-                     this, pe->mSrcIndex, fontURI.get(),
-                     NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(),
+                     this, aProxy->mSrcIndex, fontURI.get(),
+                     NS_ConvertUTF16toUTF8(aProxy->mFamily->Name()).get(),
                      PRUint32(mGeneration)));
             }
 #endif
+            ReplaceFontEntry(aProxy, fe);
+            IncrementGeneration();
             return PR_TRUE;
         } else {
 #ifdef PR_LOGGING
             if (LOG_ENABLED()) {
                 nsCAutoString fontURI;
-                pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI);
+                aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
                 LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error making platform font\n",
-                     this, pe->mSrcIndex, fontURI.get(),
-                     NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get()));
+                     this, aProxy->mSrcIndex, fontURI.get(),
+                     NS_ConvertUTF16toUTF8(aProxy->mFamily->Name()).get()));
             }
 #endif
         }
     } else {
         // download failed
 #ifdef PR_LOGGING
         if (LOG_ENABLED()) {
             nsCAutoString fontURI;
-            pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI);
+            aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
             LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error %8.8x downloading font data\n",
-                 this, pe->mSrcIndex, fontURI.get(),
-                 NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(),
+                 this, aProxy->mSrcIndex, fontURI.get(),
+                 NS_ConvertUTF16toUTF8(aProxy->mFamily->Name()).get(),
                  aDownloadStatus));
         }
 #endif
     }
 
     if (aFontData) {
         NS_Free((void*)aFontData);
     }
 
     // error occurred, load next src
     LoadStatus status;
 
-    status = LoadNext(pe);
+    status = LoadNext(aProxy);
 
     // 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 PR_TRUE;
 }
 
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -198,26 +198,26 @@ public:
     gfxFontEntry *FindFontEntry(const nsAString& aName,
                                 const gfxFontStyle& aFontStyle,
                                 PRBool& aFoundFamily,
                                 PRBool& aNeedsBold,
                                 PRBool& aWaitForUserFont);
                                 
     // initialize the process that loads external font data, which upon 
     // completion will call OnLoadComplete method
-    virtual nsresult StartLoad(gfxFontEntry *aFontToLoad, 
+    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().
-    PRBool OnLoadComplete(gfxFontEntry *aFontToLoad,
+    PRBool OnLoadComplete(gfxProxyFontEntry *aProxy,
                           const PRUint8 *aFontData, PRUint32 aLength,
                           nsresult aDownloadStatus);
 
     // Replace a proxy with a real fontEntry; this is implemented in
     // nsUserFontSet in order to keep track of the entry corresponding
     // to each @font-face rule.
     virtual void ReplaceFontEntry(gfxProxyFontEntry *aProxy,
                                   gfxFontEntry *aFontEntry) = 0;
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -78,19 +78,19 @@
 #ifdef PR_LOGGING
 static PRLogModuleInfo *gFontDownloaderLog = PR_NewLogModule("fontdownloader");
 #endif /* PR_LOGGING */
 
 #define LOG(args) PR_LOG(gFontDownloaderLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gFontDownloaderLog, PR_LOG_DEBUG)
 
 
-nsFontFaceLoader::nsFontFaceLoader(gfxFontEntry *aFontToLoad, nsIURI *aFontURI,
+nsFontFaceLoader::nsFontFaceLoader(gfxProxyFontEntry *aProxy, nsIURI *aFontURI,
                                    nsUserFontSet *aFontSet, nsIChannel *aChannel)
-  : mFontEntry(aFontToLoad), mFontURI(aFontURI), mFontSet(aFontSet),
+  : mFontEntry(aProxy), mFontURI(aFontURI), mFontSet(aFontSet),
     mChannel(aChannel)
 {
 }
 
 nsFontFaceLoader::~nsFontFaceLoader()
 {
   if (mLoadTimer) {
     mLoadTimer->Cancel();
@@ -113,34 +113,27 @@ nsFontFaceLoader::StartedLoading(nsIStre
     mLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
     if (mLoadTimer) {
       mLoadTimer->InitWithFuncCallback(LoadTimerCallback,
                                        static_cast<void*>(this),
                                        loadTimeout,
                                        nsITimer::TYPE_ONE_SHOT);
     }
   } else {
-    gfxProxyFontEntry *pe =
-      static_cast<gfxProxyFontEntry*>(mFontEntry.get());
-    pe->mLoadingState = gfxProxyFontEntry::LOADING_SLOWLY;
+    mFontEntry->mLoadingState = gfxProxyFontEntry::LOADING_SLOWLY;
   }
   mStreamLoader = aStreamLoader;
 }
 
 void
 nsFontFaceLoader::LoadTimerCallback(nsITimer *aTimer, void *aClosure)
 {
   nsFontFaceLoader *loader = static_cast<nsFontFaceLoader*>(aClosure);
 
-  if (!loader->mFontEntry->mIsProxy) {
-    return;
-  }
-
-  gfxProxyFontEntry *pe =
-    static_cast<gfxProxyFontEntry*>(loader->mFontEntry.get());
+  gfxProxyFontEntry *pe = loader->mFontEntry.get();
   bool updateUserFontSet = true;
 
   // If the entry is loading, check whether it's >75% done; if so,
   // we allow another timeout period before showing a fallback font.
   if (pe->mLoadingState == gfxProxyFontEntry::LOADING_STARTED) {
     PRInt32 contentLength;
     loader->mChannel->GetContentLength(&contentLength);
     PRUint32 numBytesRead;
@@ -236,16 +229,17 @@ nsFontFaceLoader::OnStreamComplete(nsISt
   }
 
   return NS_SUCCESS_ADOPTED_DATA;
 }
 
 void
 nsFontFaceLoader::Cancel()
 {
+  mFontEntry->mLoadingState = gfxProxyFontEntry::NOT_LOADING;
   mFontSet = nsnull;
   if (mLoadTimer) {
     mLoadTimer->Cancel();
     mLoadTimer = nsnull;
   }
   mChannel->Cancel(NS_BINDING_ABORTED);
 }
 
@@ -314,18 +308,18 @@ nsUserFontSet::Destroy()
 
 void
 nsUserFontSet::RemoveLoader(nsFontFaceLoader *aLoader)
 {
   mLoaders.RemoveEntry(aLoader);
 }
 
 nsresult 
-nsUserFontSet::StartLoad(gfxFontEntry *aFontToLoad, 
-                          const gfxFontFaceSrc *aFontFaceSrc)
+nsUserFontSet::StartLoad(gfxProxyFontEntry *aProxy,
+                         const gfxFontFaceSrc *aFontFaceSrc)
 {
   nsresult rv;
   
   // check same-site origin
   nsIPresShell *ps = mPresContext->PresShell();
   if (!ps)
     return NS_ERROR_FAILURE;
     
@@ -383,17 +377,17 @@ nsUserFontSet::StartLoad(gfxFontEntry *a
                      loadGroup,
                      nsnull,
                      nsIRequest::LOAD_NORMAL,
                      channelPolicy);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<nsFontFaceLoader> fontLoader =
-    new nsFontFaceLoader(aFontToLoad, aFontFaceSrc->mURI, this, channel);
+    new nsFontFaceLoader(aProxy, aFontFaceSrc->mURI, this, channel);
 
   if (!fontLoader)
     return NS_ERROR_OUT_OF_MEMORY;
 
 #ifdef PR_LOGGING
   if (LOG_ENABLED()) {
     nsCAutoString fontURI, referrerURI;
     aFontFaceSrc->mURI->GetSpec(fontURI);
@@ -437,16 +431,20 @@ nsUserFontSet::StartLoad(gfxFontEntry *a
   }
 
   return rv;
 }
 
 PRBool
 nsUserFontSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
 {
+  // destroy any current loaders, as the entries they refer to
+  // may be about to get replaced
+  mLoaders.EnumerateEntries(DestroyIterator, nsnull);
+
   nsTArray<FontFaceRuleRecord> oldRules;
   mRules.SwapElements(oldRules);
 
   // destroy the font family records; we need to re-create them
   // because we might end up with faces in a different order,
   // even if they're the same font entries as before
   mFontFamilies.Clear();
 
--- a/layout/style/nsFontFaceLoader.h
+++ b/layout/style/nsFontFaceLoader.h
@@ -67,17 +67,17 @@ public:
   nsUserFontSet(nsPresContext *aContext);
   ~nsUserFontSet();
 
   // Called when this font set is no longer associated with a presentation.
   void Destroy();
 
   // starts loading process, creating and initializing a nsFontFaceLoader obj
   // returns whether load process successfully started or not
-  nsresult StartLoad(gfxFontEntry *aFontToLoad, 
+  nsresult StartLoad(gfxProxyFontEntry *aFontToLoad, 
                      const gfxFontFaceSrc *aFontFaceSrc);
 
   // Called by nsFontFaceLoader when the loader has completed normally.
   // It's removed from the mLoaders set.
   void RemoveLoader(nsFontFaceLoader *aLoader);
 
   PRBool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
 
@@ -109,17 +109,17 @@ protected:
 
   nsTArray<FontFaceRuleRecord>   mRules;
 };
 
 class nsFontFaceLoader : public nsIStreamLoaderObserver
 {
 public:
 
-  nsFontFaceLoader(gfxFontEntry *aFontToLoad, nsIURI *aFontURI, 
+  nsFontFaceLoader(gfxProxyFontEntry *aFontToLoad, nsIURI *aFontURI, 
                    nsUserFontSet *aFontSet, nsIChannel *aChannel);
   virtual ~nsFontFaceLoader();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLOADEROBSERVER 
 
   // initiate the load
   nsresult Init();
@@ -132,17 +132,17 @@ public:
 
   static void LoadTimerCallback(nsITimer *aTimer, void *aClosure);
 
   static nsresult CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
                                    nsIURI* aTargetURI,
                                    nsISupports* aContext);
 
 private:
-  nsRefPtr<gfxFontEntry>  mFontEntry;
+  nsRefPtr<gfxProxyFontEntry>  mFontEntry;
   nsCOMPtr<nsIURI>        mFontURI;
   nsRefPtr<nsUserFontSet> mFontSet;
   nsCOMPtr<nsIChannel>    mChannel;
   nsCOMPtr<nsITimer>      mLoadTimer;
 
   nsIStreamLoader        *mStreamLoader;
 };