Bug 752394 - time out font facename list initialization, reflow if needed after font loader thread completes. r=jfkthame
authorJohn Daggett <jdaggett@mozilla.com>
Wed, 23 Apr 2014 14:20:20 +0900
changeset 180092 0c4cbcc72ae4cd8f6bacda2b695b1deea2a4ebd1
parent 180091 2cd362a43c95739111bfada87366f3550212d947
child 180093 555db81b5607ffdedc12e1eb2eead34f93e232d8
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersjfkthame
bugs752394
milestone31.0a1
Bug 752394 - time out font facename list initialization, reflow if needed after font loader thread completes. r=jfkthame
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxGDIFontList.cpp
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -744,27 +744,21 @@ gfxDWriteFontList::GetDefaultFont(const 
 }
 
 gfxFontEntry *
 gfxDWriteFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                    const nsAString& aFullname)
 {
     gfxFontEntry *lookup;
 
-    // initialize name lookup tables if needed
-    if (!mFaceNamesInitialized) {
-        InitFaceNameLists();
+    lookup = LookupInFaceNameLists(aFullname);
+    if (!lookup) {
+        return nullptr;
     }
 
-    // lookup in name lookup tables, return null if not found
-    if (!(lookup = mExtraNames->mPostscriptNames.GetWeak(aFullname)) &&
-        !(lookup = mExtraNames->mFullnames.GetWeak(aFullname)))
-    {
-        return nullptr;
-    }
     gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
     gfxDWriteFontEntry *fe =
         new gfxDWriteFontEntry(lookup->Name(),
                                dwriteLookup->mFont,
                                aProxyEntry->Weight(),
                                aProxyEntry->Stretch(),
                                aProxyEntry->IsItalic());
     fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -721,25 +721,18 @@ gfxGDIFontList::EnumFontFamExProc(ENUMLO
 }
 
 gfxFontEntry* 
 gfxGDIFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                 const nsAString& aFullname)
 {
     gfxFontEntry *lookup;
 
-    // initialize name lookup tables if needed
-    if (!mFaceNamesInitialized) {
-        InitFaceNameLists();
-    }
-
-    // lookup in name lookup tables, return null if not found
-    if (!(lookup = mExtraNames->mPostscriptNames.GetWeak(aFullname)) &&
-        !(lookup = mExtraNames->mFullnames.GetWeak(aFullname)))
-    {
+    lookup = LookupInFaceNameLists(aFullname);
+    if (!lookup) {
         return nullptr;
     }
 
     bool isCFF = false; // jtdfix -- need to determine this
     
     // use the face name from the lookup font entry, which will be the localized
     // face name which GDI mapping tables use (e.g. with the system locale set to
     // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -122,17 +122,17 @@ gfxPlatformFontList::gfxPlatformFontList
       mPrefFonts(10), mBadUnderlineFamilyNames(10), mSharedCmaps(16),
       mStartIndex(0), mIncrement(1), mNumFamilies(0)
 {
     mOtherFamilyNamesInitialized = false;
 
     if (aNeedFullnamePostscriptNames) {
         mExtraNames = new ExtraNames();
     }
-    mFaceNamesInitialized = false;
+    mFaceNameListsInitialized = false;
 
     LoadBadUnderlineList();
 
     // pref changes notification setup
     NS_ASSERTION(!gFontListPrefObserver,
                  "There has been font list pref observer already");
     gFontListPrefObserver = new gfxFontListPrefObserver();
     NS_ADDREF(gFontListPrefObserver);
@@ -154,17 +154,17 @@ gfxPlatformFontList::InitFontList()
 {
     mFontFamilies.Clear();
     mOtherFamilyNames.Clear();
     mOtherFamilyNamesInitialized = false;
     if (mExtraNames) {
         mExtraNames->mFullnames.Clear();
         mExtraNames->mPostscriptNames.Clear();
     }
-    mFaceNamesInitialized = false;
+    mFaceNameListsInitialized = false;
     mPrefFonts.Clear();
     mReplacementCharFallbackFamily = nullptr;
     CancelLoader();
 
     // initialize ranges of characters for which system-wide font search should be skipped
     mCodepointsWithNoFonts.reset();
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
@@ -196,57 +196,144 @@ gfxPlatformFontList::InitOtherFamilyName
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED()) {
         TimeDuration elapsed = end - start;
         LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms",
                       elapsed.ToMilliseconds()));
     }
 #endif
 }
-                                                         
+
 PLDHashOperator
 gfxPlatformFontList::InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
                                               nsRefPtr<gfxFontFamily>& aFamilyEntry,
                                               void* userArg)
 {
     gfxPlatformFontList *fc = static_cast<gfxPlatformFontList*>(userArg);
     aFamilyEntry->ReadOtherFamilyNames(fc);
     return PL_DHASH_NEXT;
 }
 
-void
-gfxPlatformFontList::InitFaceNameLists()
-{
-    mFaceNamesInitialized = true;
+struct ReadFaceNamesData {
+    ReadFaceNamesData(gfxPlatformFontList *aFontList, TimeStamp aStartTime)
+        : mFontList(aFontList), mStartTime(aStartTime), mTimedOut(false)
+    {}
+
+    gfxPlatformFontList *mFontList;
+    TimeStamp mStartTime;
+    bool mTimedOut;
+
+    // if mFirstChar is not empty, only load facenames for families
+    // that start with this character
+    nsString mFirstChar;
+};
 
+gfxFontEntry*
+gfxPlatformFontList::SearchFamiliesForFaceName(const nsAString& aFaceName)
+{
     TimeStamp start = TimeStamp::Now();
+    gfxFontEntry *lookup = nullptr;
 
-    // iterate over all font families and read in other family names
-    mFontFamilies.Enumerate(gfxPlatformFontList::InitFaceNameListsProc, this);
+    ReadFaceNamesData faceNameListsData(this, start);
+
+    // iterate over familes starting with the same letter
+    faceNameListsData.mFirstChar.Assign(aFaceName.CharAt(0));
+    ToLowerCase(faceNameListsData.mFirstChar);
+    mFontFamilies.Enumerate(gfxPlatformFontList::ReadFaceNamesProc,
+                            &faceNameListsData);
+    lookup = FindFaceName(aFaceName);
 
     TimeStamp end = TimeStamp::Now();
     Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS,
                                    start, end);
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED()) {
         TimeDuration elapsed = end - start;
-        LOG_FONTINIT(("(fontinit) InitFaceNameLists took %8.2f ms",
-                      elapsed.ToMilliseconds()));
+        LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
+                      elapsed.ToMilliseconds(),
+                      (lookup ? "found name" : ""),
+                      (faceNameListsData.mTimedOut ? "timeout" : "")));
     }
 #endif
+
+    return lookup;
 }
 
+// time limit for loading facename lists (ms)
+#define NAMELIST_TIMEOUT  200
+
 PLDHashOperator
-gfxPlatformFontList::InitFaceNameListsProc(nsStringHashKey::KeyType aKey,
-                                           nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                           void* userArg)
+gfxPlatformFontList::ReadFaceNamesProc(nsStringHashKey::KeyType aKey,
+                                       nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                                       void* userArg)
+{
+    ReadFaceNamesData *data = static_cast<ReadFaceNamesData*>(userArg);
+    gfxPlatformFontList *fc = data->mFontList;
+
+    // when filtering, skip names that don't start with the filter character
+    if (!(data->mFirstChar.IsEmpty())) {
+        char16_t firstChar = aKey.CharAt(0);
+        nsAutoString firstCharStr(&firstChar, 1);
+        ToLowerCase(firstCharStr);
+        if (!firstCharStr.Equals(data->mFirstChar)) {
+            return PL_DHASH_NEXT;
+        }
+    }
+    aFamilyEntry->ReadFaceNames(fc, fc->NeedFullnamePostscriptNames());
+
+    TimeDuration elapsed = TimeStamp::Now() - data->mStartTime;
+    if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
+        data->mTimedOut = true;
+        return PL_DHASH_STOP;
+    }
+    return PL_DHASH_NEXT;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::FindFaceName(const nsAString& aFaceName)
 {
-    gfxPlatformFontList *fc = static_cast<gfxPlatformFontList*>(userArg);
-    aFamilyEntry->ReadFaceNames(fc, fc->NeedFullnamePostscriptNames());
-    return PL_DHASH_NEXT;
+    gfxFontEntry *lookup;
+
+    // lookup in name lookup tables, return null if not found
+    if (mExtraNames &&
+        ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
+         (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
+        return lookup;
+    }
+
+    return nullptr;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::LookupInFaceNameLists(const nsAString& aFaceName)
+{
+    gfxFontEntry *lookup = nullptr;
+
+    // initialize facename lookup tables if needed
+    // note: this can terminate early or time out, in which case
+    //       mFaceNameListsInitialized remains false
+    if (!mFaceNameListsInitialized) {
+        lookup = SearchFamiliesForFaceName(aFaceName);
+        if (lookup) {
+            return lookup;
+        }
+    }
+
+    // lookup in name lookup tables, return null if not found
+    if (!(lookup = FindFaceName(aFaceName))) {
+        // names not completely initialized, so keep track of lookup misses
+        if (!mFaceNameListsInitialized) {
+            if (!mFaceNamesMissed) {
+                mFaceNamesMissed = new nsTHashtable<nsStringHashKey>(4);
+            }
+            mFaceNamesMissed->PutEntry(aFaceName);
+        }
+    }
+
+    return lookup;
 }
 
 void
 gfxPlatformFontList::PreloadNamesList()
 {
     nsAutoTArray<nsString, 10> preloadFonts;
     gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
 
@@ -810,39 +897,75 @@ gfxPlatformFontList::LoadFontInfo()
         TimeDuration elapsed = TimeStamp::Now() - start;
         LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
                       elapsed.ToMilliseconds(), (done ? "true" : "false")));
     }
 #endif
 
     if (done) {
         mOtherFamilyNamesInitialized = true;
-        mFaceNamesInitialized = true;
+        mFaceNameListsInitialized = true;
     }
 
     return done;
 }
 
+struct LookupMissedFaceNamesData {
+    LookupMissedFaceNamesData(gfxPlatformFontList *aFontList)
+        : mFontList(aFontList), mFoundName(false) {}
+
+    gfxPlatformFontList *mFontList;
+    bool mFoundName;
+};
+
+/*static*/ PLDHashOperator
+gfxPlatformFontList::LookupMissedFaceNamesProc(nsStringHashKey *aKey,
+                                               void *aUserArg)
+{
+    LookupMissedFaceNamesData *data =
+        reinterpret_cast<LookupMissedFaceNamesData*>(aUserArg);
+
+    if (data->mFontList->FindFaceName(aKey->GetKey())) {
+        data->mFoundName = true;
+        return PL_DHASH_STOP;
+    }
+    return PL_DHASH_NEXT;
+}
+
 void 
 gfxPlatformFontList::CleanupLoader()
 {
     mFontFamiliesToLoad.Clear();
     mNumFamilies = 0;
+    bool rebuilt = false;
+
+    // if had missed face names that are now available, force reflow all
+    if (mFaceNamesMissed &&
+        mFaceNamesMissed->Count() != 0) {
+        LookupMissedFaceNamesData namedata(this);
+        mFaceNamesMissed->EnumerateEntries(LookupMissedFaceNamesProc, &namedata);
+        if (namedata.mFoundName) {
+            rebuilt = true;
+            mUserFontSetList.EnumerateEntries(RebuildLocalFonts, nullptr);
+        }
+        mFaceNamesMissed = nullptr;
+    }
 
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED() && mFontInfo) {
         LOG_FONTINIT(("(fontinit) fontloader load thread took %8.2f ms "
                       "%d families %d fonts %d cmaps "
-                      "%d facenames %d othernames",
+                      "%d facenames %d othernames %s",
                       mLoadTime.ToMilliseconds(),
                       mFontInfo->mLoadStats.families,
                       mFontInfo->mLoadStats.fonts,
                       mFontInfo->mLoadStats.cmaps,
                       mFontInfo->mLoadStats.facenames,
-                      mFontInfo->mLoadStats.othernames));
+                      mFontInfo->mLoadStats.othernames,
+                      (rebuilt ? "(userfont sets rebuilt)" : "")));
     }
 #endif
 
     gfxFontInfoLoader::CleanupLoader();
 }
 
 void
 gfxPlatformFontList::GetPrefsAndStartLoader()
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -223,26 +223,40 @@ protected:
     virtual bool UsesSystemFallback() { return false; }
 
     // verifies that a family contains a non-zero font count
     gfxFontFamily* CheckFamily(gfxFontFamily *aFamily);
 
     // separate initialization for reading in name tables, since this is expensive
     void InitOtherFamilyNames();
 
-    static PLDHashOperator InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
-                                                    nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                                    void* userArg);
+    static PLDHashOperator
+    InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
+                             nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                             void* userArg);
+
+    // search through font families, looking for a given name, initializing
+    // facename lists along the way. first checks all families with names
+    // close to face name, then searchs all families if not found.
+    gfxFontEntry* SearchFamiliesForFaceName(const nsAString& aFaceName);
 
-    // read in all fullname/Postscript names for all font faces
-    void InitFaceNameLists();
+    static PLDHashOperator
+    ReadFaceNamesProc(nsStringHashKey::KeyType aKey,
+                      nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                      void* userArg);
 
-    static PLDHashOperator InitFaceNameListsProc(nsStringHashKey::KeyType aKey,
-                                                 nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                                 void* userArg);
+    // helper method for finding fullname/postscript names in facename lists
+    gfxFontEntry* FindFaceName(const nsAString& aFaceName);
+
+    // look up a font by name, for cases where platform font list
+    // maintains explicit mappings of fullname/psname ==> font
+    virtual gfxFontEntry* LookupInFaceNameLists(const nsAString& aFontName);
+
+    static PLDHashOperator LookupMissedFaceNamesProc(nsStringHashKey *aKey,
+                                                     void *aUserArg);
 
     // commonly used fonts for which the name table should be loaded at startup
     virtual void PreloadNamesList();
 
     // load the bad underline blacklist from pref.
     void LoadBadUnderlineList();
 
     // explicitly set fixed-pitch flag for all faces
@@ -281,27 +295,30 @@ protected:
     // other family name ==> family entry (not unique, can have multiple names per
     // family entry, only names *other* than the canonical names are stored here)
     nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mOtherFamilyNames;
 
     // flag set after InitOtherFamilyNames is called upon first name lookup miss
     bool mOtherFamilyNamesInitialized;
 
     // flag set after fullname and Postcript name lists are populated
-    bool mFaceNamesInitialized;
+    bool mFaceNameListsInitialized;
 
     struct ExtraNames {
       ExtraNames() : mFullnames(100), mPostscriptNames(100) {}
       // fullname ==> font entry (unique, one name per font entry)
       nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> mFullnames;
       // Postscript name ==> font entry (unique, one name per font entry)
       nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> mPostscriptNames;
     };
     nsAutoPtr<ExtraNames> mExtraNames;
 
+    // face names missed when face name loading takes a long time
+    nsAutoPtr<nsTHashtable<nsStringHashKey> > mFaceNamesMissed;
+
     // cached pref font lists
     // maps list of family names ==> array of family entries, one per lang group
     nsDataHashtable<nsUint32HashKey, nsTArray<nsRefPtr<gfxFontFamily> > > mPrefFonts;
 
     // when system-wide font lookup fails for a character, cache it to skip future searches
     gfxSparseBitSet mCodepointsWithNoFonts;
 
     // the family to use for U+FFFD fallback, to avoid expensive search every time