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 179686 0c4cbcc72ae4cd8f6bacda2b695b1deea2a4ebd1
parent 179685 2cd362a43c95739111bfada87366f3550212d947
child 179687 555db81b5607ffdedc12e1eb2eead34f93e232d8
push id42586
push userjdaggett@mozilla.com
push dateWed, 23 Apr 2014 05:21:10 +0000
treeherdermozilla-inbound@555db81b5607 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs752394
milestone31.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 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