bug 494130 - log downloadable font errors to console. r=jdaggett
authorJonathan Kew <jfkthame@gmail.com>
Tue, 12 Jul 2011 12:24:36 +0100
changeset 73465 8e599595723343118290e0f7a42e96b73d952ef1
parent 73464 699ee4c1a49501a6e6a68f50dde49be723008af3
child 73466 5dd671d71bb214626cf387b790512c3d22be12da
child 75970 5e59a54938405c523d0a36b40a3e9b2f5e1442de
push id235
push userbzbarsky@mozilla.com
push dateTue, 27 Sep 2011 17:13:04 +0000
treeherdermozilla-beta@2d1e082d176a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs494130
milestone8.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 494130 - log downloadable font errors to console. r=jdaggett
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
@@ -50,21 +50,21 @@
 #include "woff.h"
 
 #include "opentype-sanitiser.h"
 #include "ots-memory-stream.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
-static PRLogModuleInfo *gUserFontsLog = PR_NewLogModule("userfonts");
+PRLogModuleInfo *gfxUserFontSet::sUserFontsLog = PR_NewLogModule("userfonts");
 #endif /* PR_LOGGING */
 
-#define LOG(args) PR_LOG(gUserFontsLog, PR_LOG_DEBUG, args)
-#define LOG_ENABLED() PR_LOG_TEST(gUserFontsLog, PR_LOG_DEBUG)
+#define LOG(args) PR_LOG(sUserFontsLog, PR_LOG_DEBUG, args)
+#define LOG_ENABLED() PR_LOG_TEST(sUserFontsLog, PR_LOG_DEBUG)
 
 static PRUint64 sFontSetGeneration = LL_INIT(0, 0);
 
 // TODO: support for unicode ranges not yet implemented
 
 gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
              gfxMixedFontFamily *aFamily,
              PRUint32 aWeight,
@@ -440,17 +440,17 @@ CopyWOFFMetadata(const PRUint8* aFontDat
     *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().
 PRBool 
 gfxUserFontSet::OnLoadComplete(gfxProxyFontEntry *aProxy,
-                               const PRUint8 *aFontData, PRUint32 aLength, 
+                               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);
@@ -477,38 +477,33 @@ gfxUserFontSet::OnLoadComplete(gfxProxyF
 
         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);
-#ifdef DEBUG
             if (!saneData) {
-                char buf[1000];
-                sprintf(buf, "downloaded font rejected for \"%s\"",
-                        NS_ConvertUTF16toUTF8(aProxy->FamilyName()).get());
-                NS_WARNING(buf);
+                LogMessage(aProxy, "rejected by sanitizer");
             }
-#endif
             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) {
-                    NS_WARNING("failed to make platform font from download");
+                    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);
 
@@ -518,21 +513,24 @@ gfxUserFontSet::OnLoadComplete(gfxProxyF
                     // 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)
-                    NS_WARNING("failed to make platform font from download");
+                    LogMessage(aProxy, "SFNT header or tables invalid");
                 }
             }
         }
 
         if (aFontData) {
             NS_Free((void*)aFontData);
             aFontData = nsnull;
         }
@@ -567,26 +565,18 @@ gfxUserFontSet::OnLoadComplete(gfxProxyF
                 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
-#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 %8.8x downloading font data\n",
-                 this, aProxy->mSrcIndex, fontURI.get(),
-                 NS_ConvertUTF16toUTF8(aProxy->mFamily->Name()).get(),
-                 aDownloadStatus));
-        }
-#endif
+        LogMessage(aProxy, "download failed", nsIScriptError::errorFlag,
+                   aDownloadStatus);
     }
 
     if (aFontData) {
         NS_Free((void*)aFontData);
     }
 
     // error occurred, load next src
     LoadStatus status;
@@ -601,104 +591,91 @@ gfxUserFontSet::OnLoadComplete(gfxProxyF
 }
 
 
 gfxUserFontSet::LoadStatus
 gfxUserFontSet::LoadNext(gfxProxyFontEntry *aProxyEntry)
 {
     PRUint32 numSrc = aProxyEntry->mSrcList.Length();
 
-    NS_ASSERTION(aProxyEntry->mSrcIndex < numSrc, "already at the end of the src list for user font");
+    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;
     } 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
+    // 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
 
         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, 
-                     NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(), 
-                     NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get(), 
+                LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
+                     this, aProxyEntry->mSrcIndex,
+                     NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
+                     NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get(),
                      PRUint32(mGeneration)));
                 fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings);
                 fe->mLanguageOverride = aProxyEntry->mLanguageOverride;
                 StoreUserFontData(fe, aProxyEntry, nsString(), nsnull, 0);
                 ReplaceFontEntry(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(aProxyEntry->mFamily->Name()).get()));            
+                LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
+                     this, aProxyEntry->mSrcIndex,
+                     NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
+                     NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
             }
-        } 
+        }
 
         // src url ==> start the load process
         else {
-            if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI, 
+            if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
                     currSrc.mFormatFlags)) {
                 nsresult 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(), 
+                        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;                  
+                    return STATUS_LOADING;
                 } else {
-#ifdef PR_LOGGING
-                    if (LOG_ENABLED()) {
-                        nsCAutoString fontURI;
-                        currSrc.mURI->GetSpec(fontURI);
-                        LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) download failed\n", 
-                             this, aProxyEntry->mSrcIndex, fontURI.get(), 
-                             NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
-                    }
-#endif
+                    LogMessage(aProxyEntry, "download failed",
+                               nsIScriptError::errorFlag, rv);
                 }
             } else {
-#ifdef PR_LOGGING
-                if (LOG_ENABLED()) {
-                    nsCAutoString fontURI;
-                    currSrc.mURI->GetSpec(fontURI);
-                    LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) format not supported\n", 
-                         this, aProxyEntry->mSrcIndex, fontURI.get(), 
-                         NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
-                }
-#endif
+                LogMessage(aProxyEntry, "format not supported",
+                           nsIScriptError::warningFlag);
             }
         }
 
         aProxyEntry->mSrcIndex++;
     }
 
     // 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(aProxyEntry->mFamily->Name()).get()));            
+    LOG(("userfonts (%p) failed all src for (%s)\n",
+        this, NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
     aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_FAILED;
 
     return STATUS_END_OF_LIST;
 }
 
 void
 gfxUserFontSet::IncrementGeneration()
 {
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -42,16 +42,17 @@
 #include "gfxFont.h"
 #include "gfxFontUtils.h"
 #include "nsRefPtrHashtable.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 #include "nsIFile.h"
 #include "nsISupportsImpl.h"
+#include "nsIScriptError.h"
 
 class nsIURI;
 class gfxMixedFontFamily;
 
 // parsed CSS @font-face rule information
 // lifetime: from when @font-face rule processed until font is loaded
 struct gfxFontFaceSrc {
     PRPackedBool           mIsLocal;       // url or local
@@ -247,20 +248,28 @@ public:
 
 protected:
     // for a given proxy font entry, attempt to load the next resource
     // in the src list
     LoadStatus LoadNext(gfxProxyFontEntry *aProxyEntry);
 
     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;
+
     // font families defined by @font-face rules
     nsRefPtrHashtable<nsStringHashKey, gfxMixedFontFamily> mFontFamilies;
 
     PRUint64        mGeneration;
+
+    static PRLogModuleInfo *sUserFontsLog;
 };
 
 // acts a placeholder until the real font is downloaded
 
 class gfxProxyFontEntry : public gfxFontEntry {
     friend class gfxUserFontSet;
 
 public:
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -68,16 +68,18 @@
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentErrors.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIChannelPolicy.h"
 #include "nsChannelPolicy.h"
 
+#include "nsIConsoleService.h"
+
 #include "nsStyleSet.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *gFontDownloaderLog = PR_NewLogModule("fontdownloader");
 #endif /* PR_LOGGING */
 
@@ -337,27 +339,17 @@ nsUserFontSet::StartLoad(gfxProxyFontEnt
                "null origin principal in @font-face rule");
   if (aFontFaceSrc->mUseOriginPrincipal) {
     principal = do_QueryInterface(aFontFaceSrc->mOriginPrincipal);
   }
   
   rv = nsFontFaceLoader::CheckLoadAllowed(principal, aFontFaceSrc->mURI, 
                                           ps->GetDocument());
   if (NS_FAILED(rv)) {
-#ifdef PR_LOGGING
-    if (LOG_ENABLED()) {
-      nsCAutoString fontURI, referrerURI;
-      aFontFaceSrc->mURI->GetSpec(fontURI);
-      if (aFontFaceSrc->mReferrer)
-        aFontFaceSrc->mReferrer->GetSpec(referrerURI);
-      LOG(("fontdownloader download blocked - font uri: (%s) "
-           "referrer uri: (%s) err: %8.8x\n", 
-          fontURI.get(), referrerURI.get(), rv));
-    }
-#endif    
+    LogMessage(aProxy, "download not allowed", nsIScriptError::errorFlag, rv);
     return rv;
   }
 
   nsCOMPtr<nsIStreamLoader> streamLoader;
   nsCOMPtr<nsILoadGroup> loadGroup(ps->GetDocument()->GetDocumentLoadGroup());
 
   nsCOMPtr<nsIChannel> channel;
   // get Content Security Policy from principal to pass into channel
@@ -692,8 +684,110 @@ nsUserFontSet::FindRuleForEntry(gfxFontE
 {
   for (PRUint32 i = 0; i < mRules.Length(); ++i) {
     if (mRules[i].mFontEntry == aFontEntry) {
       return mRules[i].mContainer.mRule;
     }
   }
   return nsnull;
 }
+
+nsresult
+nsUserFontSet::LogMessage(gfxProxyFontEntry *aProxy,
+                          const char        *aMessage,
+                          PRUint32          aFlags,
+                          nsresult          aStatus)
+{
+  nsCOMPtr<nsIConsoleService>
+    console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+  if (!console) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  NS_ConvertUTF16toUTF8 familyName(aProxy->FamilyName());
+  nsCAutoString fontURI;
+  aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
+
+  char weightKeywordBuf[8]; // plenty to sprintf() a PRUint16
+  const char *weightKeyword;
+  const nsAFlatCString& weightKeywordString =
+    nsCSSProps::ValueToKeyword(aProxy->Weight(),
+                               nsCSSProps::kFontWeightKTable);
+  if (weightKeywordString.Length() > 0) {
+    weightKeyword = weightKeywordString.get();
+  } else {
+    sprintf(weightKeywordBuf, "%u", aProxy->Weight());
+    weightKeyword = weightKeywordBuf;
+  }
+
+  nsPrintfCString
+    msg(1024,
+        "downloadable font: %s "
+        "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
+        aMessage,
+        familyName.get(),
+        aProxy->IsItalic() ? "italic" : "normal",
+        weightKeyword,
+        nsCSSProps::ValueToKeyword(aProxy->Stretch(),
+                                   nsCSSProps::kFontStretchKTable).get(),
+        aProxy->mSrcIndex);
+
+  if (aStatus != 0) {
+    msg.Append(": ");
+    switch (aStatus) {
+    case NS_ERROR_DOM_BAD_URI:
+      msg.Append("bad URI or cross-site access not allowed");
+      break;
+    case NS_ERROR_CONTENT_BLOCKED:
+      msg.Append("content blocked");
+      break;
+    default:
+      msg.Append("status=");
+      msg.AppendInt(aStatus);
+      break;
+    }
+  }
+  msg.Append("\nsource: ");
+  msg.Append(fontURI);
+
+#ifdef PR_LOGGING
+  if (PR_LOG_TEST(sUserFontsLog, PR_LOG_DEBUG)) {
+    PR_LOG(sUserFontsLog, PR_LOG_DEBUG,
+           ("userfonts (%p) %s", this, msg.get()));
+  }
+#endif
+
+  // try to give the user an indication of where the rule came from
+  nsCSSFontFaceRule* rule = FindRuleForEntry(aProxy);
+  nsString href;
+  nsString text;
+  nsresult rv;
+  if (rule) {
+    rv = rule->GetCssText(text);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIDOMCSSStyleSheet> sheet;
+    rv = rule->GetParentStyleSheet(getter_AddRefs(sheet));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = sheet->GetHref(href);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIScriptError2> scriptError =
+    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint64 windowID = GetPresContext()->Document()->OuterWindowID();
+  rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(msg).get(),
+                                     href.get(),   // file
+                                     text.get(),   // src line
+                                     0, 0,         // line & column number
+                                     aFlags,       // flags
+                                     "CSS Loader", // category (make separate?)
+                                     windowID);
+  if (NS_SUCCEEDED(rv)){
+    nsCOMPtr<nsIScriptError> logError = do_QueryInterface(scriptError);
+    if (logError) {
+      console->LogMessage(logError);
+    }
+  }
+
+  return NS_OK;
+}
--- a/layout/style/nsFontFaceLoader.h
+++ b/layout/style/nsFontFaceLoader.h
@@ -97,16 +97,21 @@ protected:
     nsRefPtr<gfxFontEntry>       mFontEntry;
     nsFontFaceRuleContainer      mContainer;
   };
 
   void InsertRule(nsCSSFontFaceRule *aRule, PRUint8 aSheetType,
                   nsTArray<FontFaceRuleRecord>& oldRules,
                   PRBool& aFontSetModified);
 
+  virtual nsresult LogMessage(gfxProxyFontEntry *aProxy,
+                              const char *aMessage,
+                              PRUint32 aFlags = nsIScriptError::errorFlag,
+                              nsresult aStatus = 0);
+
   nsPresContext *mPresContext;  // weak reference
 
   // Set of all loaders pointing to us. These are not strong pointers,
   // but that's OK because nsFontFaceLoader always calls RemoveLoader on
   // us before it dies (unless we die first).
   nsTHashtable< nsPtrHashKey<nsFontFaceLoader> > mLoaders;
 
   nsTArray<FontFaceRuleRecord>   mRules;