Bug 962440 - async font info loader infrastructure with OSX implementation. r=bas
authorJohn Daggett <jdaggett@mozilla.com>
Wed, 29 Jan 2014 16:39:01 +0900
changeset 181731 71c900e16cf9465298b05da2cb9a7812b3145120
parent 181730 3ee447cb0b7531f908b4db0738820261d16489a0
child 181732 a79047a5654f5c3d76372dd58a22fa23884d0b8e
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs962440
milestone29.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 962440 - async font info loader infrastructure with OSX implementation. r=bas
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxDWriteFontList.h
gfx/thebes/gfxFT2FontList.cpp
gfx/thebes/gfxFT2FontList.h
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontInfoLoader.cpp
gfx/thebes/gfxFontInfoLoader.h
gfx/thebes/gfxGDIFontList.cpp
gfx/thebes/gfxGDIFontList.h
gfx/thebes/gfxMacPlatformFontList.h
gfx/thebes/gfxMacPlatformFontList.mm
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -142,17 +142,17 @@ GetDirectWriteFaceName(IDWriteFont *aFon
         return hr;
     }
 
     aFontName.Assign(faceName.Elements());
     return S_OK;
 }
 
 void
-gfxDWriteFontFamily::FindStyleVariations()
+gfxDWriteFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
 {
     HRESULT hr;
     if (mHasStyles) {
         return;
     }
     mHasStyles = true;
 
     gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
@@ -491,40 +491,48 @@ gfxDWriteFontEntry::GetFontTable(uint32_
                               HB_MEMORY_MODE_READONLY,
                               ftr, DestroyBlobFunc);
     }
 
     return nullptr;
 }
 
 nsresult
-gfxDWriteFontEntry::ReadCMAP()
+gfxDWriteFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
 {
-    nsresult rv;
-
     // attempt this once, if errors occur leave a blank cmap
     if (mCharacterMap) {
         return NS_OK;
     }
 
-    nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+    nsRefPtr<gfxCharacterMap> charmap;
+    nsresult rv;
+    bool symbolFont;
+
+    if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
+                                                        mUVSOffset,
+                                                        symbolFont))) {
+        rv = NS_OK;
+    } else {
+        uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+        charmap = new gfxCharacterMap();
+        AutoTable cmapTable(this, kCMAP);
 
-    uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
-    AutoTable cmapTable(this, kCMAP);
-    if (cmapTable) {
-        bool unicodeFont = false, symbolFont = false; // currently ignored
-        uint32_t cmapLen;
-        const uint8_t* cmapData =
-            reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
-                                                              &cmapLen));
-        rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
-                                    *charmap, mUVSOffset,
-                                    unicodeFont, symbolFont);
-    } else {
-        rv = NS_ERROR_NOT_AVAILABLE;
+        if (cmapTable) {
+            bool unicodeFont = false, symbolFont = false; // currently ignored
+            uint32_t cmapLen;
+            const uint8_t* cmapData =
+                reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
+                                                                  &cmapLen));
+            rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
+                                        *charmap, mUVSOffset,
+                                        unicodeFont, symbolFont);
+        } else {
+            rv = NS_ERROR_NOT_AVAILABLE;
+        }
     }
 
     mHasCmapTable = NS_SUCCEEDED(rv);
     if (mHasCmapTable) {
         gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
         mCharacterMap = pfl->FindCharMap(charmap);
     } else {
         // if error occurred, initialize to null cmap
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -38,17 +38,17 @@ public:
      * \param aFamily IDWriteFontFamily object representing the directwrite
      * family object.
      */
     gfxDWriteFontFamily(const nsAString& aName, 
                         IDWriteFontFamily *aFamily)
       : gfxFontFamily(aName), mDWFamily(aFamily), mForceGDIClassic(false) {}
     virtual ~gfxDWriteFontFamily();
     
-    virtual void FindStyleVariations();
+    virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
 
     virtual void LocalizedName(nsAString& aLocalizedName);
 
     virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
                                bool aNeedFullnamePostscriptNames);
 
     void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
 
@@ -144,17 +144,17 @@ public:
     }
 
     virtual ~gfxDWriteFontEntry();
 
     virtual bool IsSymbolFont();
 
     virtual hb_blob_t* GetFontTable(uint32_t aTableTag) MOZ_OVERRIDE;
 
-    nsresult ReadCMAP();
+    nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
 
     bool IsCJKFont();
 
     void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
     bool GetForceGDIClassic() { return mForceGDIClassic; }
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -458,17 +458,17 @@ FT2FontEntry::CairoFontFace()
         FTUserFontData *userFontData = new FTUserFontData(face, face.FontData());
         cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
                                       userFontData, FTFontDestroyFunc);
     }
     return mFontFace;
 }
 
 nsresult
-FT2FontEntry::ReadCMAP()
+FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
 {
     if (mCharacterMap) {
         return NS_OK;
     }
 
     nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
 
     AutoFallibleTArray<uint8_t,16384> buffer;
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -69,17 +69,17 @@ public:
     // Create (if necessary) and return the cairo_font_face for this font.
     // This may fail and return null, so caller must be prepared to handle this.
     cairo_font_face_t *CairoFontFace();
 
     // Create a cairo_scaled_font for this face, with the given style.
     // This may fail and return null, so caller must be prepared to handle this.
     cairo_scaled_font_t *CreateScaledFont(const gfxFontStyle *aStyle);
 
-    nsresult ReadCMAP();
+    nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
 
     virtual hb_blob_t* GetFontTable(uint32_t aTableTag) MOZ_OVERRIDE;
 
     virtual nsresult CopyFontTable(uint32_t aTableTag,
                                    FallibleTArray<uint8_t>& aBuffer) MOZ_OVERRIDE;
 
     // Check for various kinds of brokenness, and set flags on the entry
     // accordingly so that we avoid using bad font tables
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -220,17 +220,17 @@ uint16_t gfxFontEntry::GetUVSGlyph(uint3
 
     if (mUVSData) {
         return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS);
     }
 
     return 0;
 }
 
-nsresult gfxFontEntry::ReadCMAP()
+nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
 {
     NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
     mCharacterMap = new gfxCharacterMap();
     return NS_OK;
 }
 
 nsString
 gfxFontEntry::RealFaceName()
@@ -559,16 +559,28 @@ gfxFontEntry::GetTableFromFontData(const
         return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
                                   dir->offset, dir->length,
                               HB_MEMORY_MODE_READONLY, nullptr, nullptr);
 
     }
     return nullptr;
 }
 
+already_AddRefed<gfxCharacterMap>
+gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
+                                  uint32_t& aUVSOffset,
+                                  bool& aSymbolFont)
+{
+    if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
+        return nullptr;
+    }
+
+    return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont);
+}
+
 hb_blob_t *
 gfxFontEntry::GetFontTable(uint32_t aTag)
 {
     hb_blob_t *blob;
     if (GetExistingFontTable(aTag, &blob)) {
         return blob;
     }
 
@@ -900,16 +912,21 @@ gfxFontFamily::FindFontForStyle(const gf
     }
 
     return matchFE;
 }
 
 void
 gfxFontFamily::CheckForSimpleFamily()
 {
+    // already checked this family
+    if (mIsSimpleFamily) {
+        return;
+    };
+
     uint32_t count = mAvailableFonts.Length();
     if (count > 4 || count == 0) {
         return; // can't be "simple" if there are >4 faces;
                 // if none then the family is unusable anyway
     }
 
     if (count == 1) {
         mIsSimpleFamily = true;
@@ -1261,40 +1278,92 @@ gfxFontFamily::ReadOtherFamilyNames(gfxP
             continue;
         }
         ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
     }
 }
 
 void
 gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, 
-                             bool aNeedFullnamePostscriptNames)
+                             bool aNeedFullnamePostscriptNames,
+                             FontInfoData *aFontInfoData)
 {
     // if all needed names have already been read, skip
     if (mOtherFamilyNamesInitialized &&
         (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
         return;
 
-    FindStyleVariations();
+    if (!mOtherFamilyNamesInitialized &&
+        aFontInfoData &&
+        aFontInfoData->mLoadOtherNames)
+    {
+        nsAutoTArray<nsString,4> otherFamilyNames;
+        bool foundOtherNames =
+            aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames);
+        if (foundOtherNames) {
+            uint32_t i, n = otherFamilyNames.Length();
+            for (i = 0; i < n; i++) {
+                aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
+            }
+        }
+        mOtherFamilyNamesInitialized = true;
+    }
+
+    // if all needed data has been initialized, return
+    if (mOtherFamilyNamesInitialized &&
+        (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
+        return;
+    }
+
+    FindStyleVariations(aFontInfoData);
+
+    // check again, as style enumeration code may have loaded names
+    if (mOtherFamilyNamesInitialized &&
+        (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
+        return;
+    }
 
     uint32_t i, numFonts = mAvailableFonts.Length();
     const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
-    nsAutoString fullname, psname;
 
     bool firstTime = true, readAllFaces = false;
     for (i = 0; i < numFonts; ++i) {
         gfxFontEntry *fe = mAvailableFonts[i];
         if (!fe) {
             continue;
         }
+
+        nsAutoString fullname, psname;
+        bool foundFaceNames = false;
+        if (!mFaceNamesInitialized &&
+            aNeedFullnamePostscriptNames &&
+            aFontInfoData &&
+            aFontInfoData->mLoadFaceNames) {
+            aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
+            if (!fullname.IsEmpty()) {
+                aPlatformFontList->AddFullname(fe, fullname);
+            }
+            if (!psname.IsEmpty()) {
+                aPlatformFontList->AddPostscriptName(fe, psname);
+            }
+            foundFaceNames = true;
+
+            // found everything needed? skip to next font
+            if (mOtherFamilyNamesInitialized) {
+                continue;
+            }
+        }
+
+        // load directly from the name table
         gfxFontEntry::AutoTable nameTable(fe, kNAME);
         if (!nameTable) {
             continue;
         }
-        if (aNeedFullnamePostscriptNames) {
+
+        if (aNeedFullnamePostscriptNames && !foundFaceNames) {
             if (gfxFontUtils::ReadCanonicalName(
                     nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
             {
                 aPlatformFontList->AddFullname(fe, fullname);
             }
 
             if (gfxFontUtils::ReadCanonicalName(
                     nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
@@ -1336,26 +1405,28 @@ gfxFontFamily::FindFont(const nsAString&
         gfxFontEntry *fe = mAvailableFonts[i].get();
         if (fe && fe->Name() == aPostscriptName)
             return fe;
     }
     return nullptr;
 }
 
 void
-gfxFontFamily::ReadAllCMAPs()
-{
+gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData)
+{
+    FindStyleVariations(aFontInfoData);
+
     uint32_t i, numFonts = mAvailableFonts.Length();
     for (i = 0; i < numFonts; i++) {
         gfxFontEntry *fe = mAvailableFonts[i];
         // don't try to load cmaps for downloadable fonts not yet loaded
         if (!fe || fe->mIsProxy) {
             continue;
         }
-        fe->ReadCMAP();
+        fe->ReadCMAP(aFontInfoData);
         mFamilyCharacterMap.Union(*(fe->mCharacterMap));
     }
     mFamilyCharacterMap.Compact();
     mFamilyCharacterMapInitialized = true;
 }
 
 void
 gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -44,16 +44,17 @@ class gfxFont;
 class gfxFontFamily;
 class gfxFontGroup;
 class gfxUserFontSet;
 class gfxUserFontData;
 class gfxShapedText;
 class gfxShapedWord;
 class gfxSVGGlyphs;
 class gfxTextContextPaint;
+class FontInfoData;
 
 class nsILanguageAtomService;
 
 #define FONT_MAX_SIZE                  2000.0
 
 #define NO_FONT_LANGUAGE_OVERRIDE      0
 
 struct FontListSizes;
@@ -294,17 +295,17 @@ public:
     uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS);
 
     // All concrete gfxFontEntry subclasses (except gfxProxyFontEntry) need
     // to override this, otherwise the font will never be used as it will
     // be considered to support no characters.
     // ReadCMAP() must *always* set the mCharacterMap pointer to a valid
     // gfxCharacterMap, even if empty, as other code assumes this pointer
     // can be safely dereferenced.
-    virtual nsresult ReadCMAP();
+    virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
 
     bool TryGetSVGData(gfxFont* aFont);
     bool HasSVGGlyph(uint32_t aGlyphId);
     bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
                             gfxRect *aResult);
     bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode,
                         gfxTextContextPaint *aContextPaint);
     // Call this when glyph geometry or rendering has changed
@@ -486,16 +487,22 @@ protected:
     // Return a blob that wraps a table found within a buffer of font data.
     // The blob does NOT own its data; caller guarantees that the buffer
     // will remain valid at least as long as the blob.
     // Returns null if the specified table is not found.
     // This method assumes aFontData is valid 'sfnt' data; before using this,
     // caller is responsible to do any sanitization/validation necessary.
     hb_blob_t* GetTableFromFontData(const void* aFontData, uint32_t aTableTag);
 
+    // lookup the cmap in cached font data
+    virtual already_AddRefed<gfxCharacterMap>
+    GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
+                        uint32_t& aUVSOffset,
+                        bool& aSymbolFont);
+
     // Font's unitsPerEm from the 'head' table, if available (will be set to
     // kInvalidUPEM for non-sfnt font formats)
     uint16_t mUnitsPerEm;
 
     // Shaper-specific face objects, shared by all instantiations of the same
     // physical font, regardless of size.
     // Usually, only one of these will actually be created for any given font
     // entry, depending on the font tables that are present.
@@ -688,16 +695,17 @@ public:
         {
             aFontEntry->mIgnoreGDEF = true;
         }
         aFontEntry->mFamilyName = Name();
         mAvailableFonts.AppendElement(aFontEntry);
     }
 
     // note that the styles for this family have been added
+    bool HasStyles() { return mHasStyles; }
     void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; }
 
     // choose a specific face to match a style using CSS font matching
     // rules (weight matching occurs here).  may return a face that doesn't
     // precisely match (e.g. normal face when no italic face exists).
     // aNeedsSyntheticBold is set to true when synthetic bolding is
     // needed, false otherwise
     gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle, 
@@ -724,27 +732,28 @@ public:
     // set when other family names have been read in
     void SetOtherFamilyNamesInitialized() {
         mOtherFamilyNamesInitialized = true;
     }
 
     // read in other localized family names, fullnames and Postscript names
     // for all faces and append to lookup tables
     virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
-                               bool aNeedFullnamePostscriptNames);
+                               bool aNeedFullnamePostscriptNames,
+                               FontInfoData *aFontInfoData = nullptr);
 
     // find faces belonging to this family (platform implementations override this;
     // should be made pure virtual once all subclasses have been updated)
-    virtual void FindStyleVariations() { }
+    virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) { }
 
     // search for a specific face using the Postscript name
     gfxFontEntry* FindFont(const nsAString& aPostscriptName);
 
     // read in cmaps for all the faces
-    void ReadAllCMAPs();
+    void ReadAllCMAPs(FontInfoData *aFontInfoData = nullptr);
 
     bool TestCharacterMap(uint32_t aCh) {
         if (!mFamilyCharacterMapInitialized) {
             ReadAllCMAPs();
         }
         return mFamilyCharacterMap.test(aCh);
     }
 
--- a/gfx/thebes/gfxFontInfoLoader.cpp
+++ b/gfx/thebes/gfxFontInfoLoader.cpp
@@ -1,20 +1,96 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxFontInfoLoader.h"
 #include "nsCRT.h"
 #include "nsIObserverService.h"
+#include "nsThreadUtils.h"              // for nsRunnable
+#include "gfxPlatformFontList.h"
 
 using namespace mozilla;
 using mozilla::services::GetObserverService;
 
+void
+FontInfoData::Load()
+{
+    TimeStamp start = TimeStamp::Now();
+
+    uint32_t i, n = mFontFamiliesToLoad.Length();
+    mLoadStats.families = n;
+    for (i = 0; i < n; i++) {
+        LoadFontFamilyData(mFontFamiliesToLoad[i]);
+    }
+
+    mLoadTime = TimeStamp::Now() - start;
+}
+
+class FontInfoLoadCompleteEvent : public nsRunnable {
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    FontInfoLoadCompleteEvent(FontInfoData *aFontInfo) :
+        mFontInfo(aFontInfo)
+    {}
+    virtual ~FontInfoLoadCompleteEvent() {}
+
+    NS_IMETHOD Run();
+
+    nsRefPtr<FontInfoData> mFontInfo;
+};
+
+class AsyncFontInfoLoader : public nsRunnable {
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    AsyncFontInfoLoader(FontInfoData *aFontInfo) :
+        mFontInfo(aFontInfo)
+    {
+        mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo);
+    }
+    virtual ~AsyncFontInfoLoader() {}
+
+    NS_IMETHOD Run();
+
+    nsRefPtr<FontInfoData> mFontInfo;
+    nsRefPtr<FontInfoLoadCompleteEvent> mCompleteEvent;
+};
+
+// runs on main thread after async font info loading is done
+nsresult
+FontInfoLoadCompleteEvent::Run()
+{
+    gfxFontInfoLoader *loader =
+        static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList());
+
+    loader->FinalizeLoader(mFontInfo);
+
+    mFontInfo = nullptr;
+    return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(FontInfoLoadCompleteEvent, nsIRunnable);
+
+// runs on separate thread
+nsresult
+AsyncFontInfoLoader::Run()
+{
+    // load platform-specific font info
+    mFontInfo->Load();
+
+    // post a completion event that transfer the data to the fontlist
+    NS_DispatchToMainThread(mCompleteEvent, NS_DISPATCH_NORMAL);
+    mFontInfo = nullptr;
+
+    return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(AsyncFontInfoLoader, nsIRunnable);
+
 NS_IMPL_ISUPPORTS1(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
 
 NS_IMETHODIMP
 gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports *aSubject,
                                              const char *aTopic,
                                              const char16_t *someData)
 {
     if (!nsCRT::strcmp(aTopic, "quit-application")) {
@@ -26,88 +102,135 @@ gfxFontInfoLoader::ShutdownObserver::Obs
 }
 
 void
 gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval)
 {
     mInterval = aInterval;
 
     // sanity check
-    if (mState != stateInitial && mState != stateTimerOff) {
+    if (mState != stateInitial &&
+        mState != stateTimerOff &&
+        mState != stateTimerOnDelay) {
         CancelLoader();
     }
 
     // set up timer
     if (!mTimer) {
         mTimer = do_CreateInstance("@mozilla.org/timer;1");
         if (!mTimer) {
             NS_WARNING("Failure to create font info loader timer");
             return;
         }
     }
 
-    // need an initial delay?
-    uint32_t timerInterval;
+    AddShutdownObserver();
 
+    // delay? ==> start async thread after a delay
     if (aDelay) {
         mState = stateTimerOnDelay;
-        timerInterval = aDelay;
-    } else {
-        mState = stateTimerOnInterval;
-        timerInterval = mInterval;
+        mTimer->InitWithFuncCallback(DelayedStartCallback, this, aDelay,
+                                     nsITimer::TYPE_ONE_SHOT);
+        return;
+    }
+
+    mFontInfo = CreateFontInfoData();
+
+    // initialize
+    InitLoader();
+
+    // start async load
+    mState = stateAsyncLoad;
+    nsresult rv = NS_NewNamedThread("Font Loader",
+                                    getter_AddRefs(mFontLoaderThread),
+                                    nullptr);
+    if (NS_FAILED(rv)) {
+        return;
     }
 
-    InitLoader();
+    nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
+
+    mFontLoaderThread->Dispatch(loadEvent, NS_DISPATCH_NORMAL);
+}
 
-    // start timer
-    mTimer->InitWithFuncCallback(LoaderTimerCallback, this, timerInterval,
-                                 nsITimer::TYPE_REPEATING_SLACK);
+void
+gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo)
+{
+    // avoid loading data if loader has already been canceled
+    if (mState != stateAsyncLoad) {
+        return;
+    }
 
-    nsCOMPtr<nsIObserverService> obs = GetObserverService();
-    if (obs) {
-        mObserver = new ShutdownObserver(this);
-        obs->AddObserver(mObserver, "quit-application", false);
+    mLoadTime = mFontInfo->mLoadTime;
+
+    // try to load all font data immediately
+    if (LoadFontInfo()) {
+        CancelLoader();
+        return;
     }
+
+    // not all work completed ==> run load on interval
+    mState = stateTimerOnInterval;
+    mTimer->InitWithFuncCallback(LoadFontInfoCallback, this, mInterval,
+                                 nsITimer::TYPE_REPEATING_SLACK);
 }
 
 void
 gfxFontInfoLoader::CancelLoader()
 {
     if (mState == stateInitial) {
         return;
     }
     mState = stateTimerOff;
     if (mTimer) {
         mTimer->Cancel();
         mTimer = nullptr;
     }
+    if (mFontLoaderThread) {
+        mFontLoaderThread->Shutdown();
+        mFontLoaderThread = nullptr;
+    }
     RemoveShutdownObserver();
-    FinishLoader();
+    CleanupLoader();
 }
 
 void
-gfxFontInfoLoader::LoaderTimerFire()
+gfxFontInfoLoader::LoadFontInfoTimerFire()
 {
     if (mState == stateTimerOnDelay) {
         mState = stateTimerOnInterval;
         mTimer->SetDelay(mInterval);
     }
 
-    bool done = RunLoader();
+    bool done = LoadFontInfo();
     if (done) {
         CancelLoader();
     }
 }
 
 gfxFontInfoLoader::~gfxFontInfoLoader()
 {
     RemoveShutdownObserver();
 }
 
 void
+gfxFontInfoLoader::AddShutdownObserver()
+{
+    if (mObserver) {
+        return;
+    }
+
+    nsCOMPtr<nsIObserverService> obs = GetObserverService();
+    if (obs) {
+        mObserver = new ShutdownObserver(this);
+        obs->AddObserver(mObserver, "quit-application", false);
+    }
+}
+
+void
 gfxFontInfoLoader::RemoveShutdownObserver()
 {
     if (mObserver) {
         nsCOMPtr<nsIObserverService> obs = GetObserverService();
         if (obs) {
             obs->RemoveObserver(mObserver, "quit-application");
             mObserver = nullptr;
         }
--- a/gfx/thebes/gfxFontInfoLoader.h
+++ b/gfx/thebes/gfxFontInfoLoader.h
@@ -1,53 +1,191 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_FONT_INFO_LOADER_H
 #define GFX_FONT_INFO_LOADER_H
 
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsITimer.h"
+#include "nsIThread.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "gfxFont.h"
+#include "nsIRunnable.h"
+#include "mozilla/TimeStamp.h"
+#include "nsTraceRefcnt.h"
 
-// helper class for loading in font info spaced out at regular intervals
+// data retrieved for a given face
+
+struct FontFaceData {
+    FontFaceData() : mUVSOffset(0), mSymbolFont(false) {}
+
+    FontFaceData(const FontFaceData& aFontFaceData) {
+        mFullName = aFontFaceData.mFullName;
+        mPostscriptName = aFontFaceData.mPostscriptName;
+        mCharacterMap = aFontFaceData.mCharacterMap;
+        mUVSOffset = aFontFaceData.mUVSOffset;
+        mSymbolFont = aFontFaceData.mSymbolFont;
+    }
+
+    nsString mFullName;
+    nsString mPostscriptName;
+    nsRefPtr<gfxCharacterMap> mCharacterMap;
+    uint32_t mUVSOffset;
+    bool mSymbolFont;
+};
+
+// base class used to contain cached system-wide font info.
+// methods in this class are called on off-main threads so
+// all methods use only static methods or other thread-safe
+// font data access API's. specifically, no use is made of
+// gfxPlatformFontList, gfxFontFamily, gfxFamily or any
+// harfbuzz API methods within FontInfoData subclasses.
+
+class FontInfoData {
+public:
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FontInfoData)
+
+    FontInfoData(bool aLoadOtherNames,
+                 bool aLoadFaceNames,
+                 bool aLoadCmaps) :
+        mLoadOtherNames(aLoadOtherNames),
+        mLoadFaceNames(aLoadFaceNames),
+        mLoadCmaps(aLoadCmaps)
+   {
+        MOZ_COUNT_CTOR(FontInfoData);
+   }
+
+    virtual ~FontInfoData() {
+        MOZ_COUNT_DTOR(FontInfoData);
+    }
+
+    virtual void Load();
+
+    // loads font data for all fonts of a given family
+    // (called on async thread)
+    virtual void LoadFontFamilyData(const nsAString& aFamilyName) = 0;
+
+    // -- methods overriden by platform-specific versions --
+
+    // fetches cmap data for a particular font from cached font data
+    virtual already_AddRefed<gfxCharacterMap>
+    GetCMAP(const nsAString& aFontName,
+            uint32_t& aUVSOffset,
+            bool& aSymbolFont)
+    {
+        FontFaceData faceData;
+        if (!mFontFaceData.Get(aFontName, &faceData) ||
+            !faceData.mCharacterMap) {
+            return nullptr;
+        }
+
+        aUVSOffset = faceData.mUVSOffset;
+        aSymbolFont = faceData.mSymbolFont;
+        nsRefPtr<gfxCharacterMap> cmap = faceData.mCharacterMap;
+        return cmap.forget();
+    }
+
+    // fetches fullname/postscript names from cached font data
+    virtual void GetFaceNames(const nsAString& aFontName,
+                              nsAString& aFullName,
+                              nsAString& aPostscriptName)
+    {
+        FontFaceData faceData;
+        if (!mFontFaceData.Get(aFontName, &faceData)) {
+            return;
+        }
+
+        aFullName = faceData.mFullName;
+        aPostscriptName = faceData.mPostscriptName;
+    }
+
+    // fetches localized family name data from cached font data
+    virtual bool GetOtherFamilyNames(const nsAString& aFamilyName,
+                                     nsTArray<nsString>& aOtherFamilyNames)
+    {
+        return mOtherFamilyNames.Get(aFamilyName, &aOtherFamilyNames); 
+    }
+
+    nsTArray<nsString> mFontFamiliesToLoad;
+
+    // time spent on the loader thread
+    mozilla::TimeDuration mLoadTime;
+
+    struct FontCounts {
+        uint32_t families;
+        uint32_t fonts;
+        uint32_t cmaps;
+        uint32_t facenames;
+        uint32_t othernames;
+    };
+
+    FontCounts mLoadStats;
+
+    bool mLoadOtherNames;
+    bool mLoadFaceNames;
+    bool mLoadCmaps;
+
+    // face name ==> per-face data
+    nsDataHashtable<nsStringHashKey, FontFaceData> mFontFaceData;
+
+    // canonical family name ==> array of localized family names
+    nsDataHashtable<nsStringHashKey, nsTArray<nsString> > mOtherFamilyNames;
+};
+
+// gfxFontInfoLoader - helper class for loading font info on async thread
+// For large, "all fonts on system" data, data needed on a given platform
+// (e.g. localized names, face names, cmaps) are loaded async.
+
+// helper class for loading in font info on a separate async thread
+// once async thread completes, completion process is run on regular
+// intervals to prevent tying up the main thread
 
 class gfxFontInfoLoader {
 public:
 
     // state transitions:
     //   initial ---StartLoader with delay---> timer on delay
     //   initial ---StartLoader without delay---> timer on interval
     //   timer on delay ---LoaderTimerFire---> timer on interval
     //   timer on delay ---CancelLoader---> timer off
     //   timer on interval ---CancelLoader---> timer off
     //   timer off ---StartLoader with delay---> timer on delay
     //   timer off ---StartLoader without delay---> timer on interval
     typedef enum {
         stateInitial,
         stateTimerOnDelay,
+        stateAsyncLoad,
         stateTimerOnInterval,
         stateTimerOff
     } TimerState;
 
     gfxFontInfoLoader() :
         mInterval(0), mState(stateInitial)
     {
     }
 
     virtual ~gfxFontInfoLoader();
 
     // start timer with an initial delay, then call Run method at regular intervals
     void StartLoader(uint32_t aDelay, uint32_t aInterval);
 
+    // Finalize - async load complete, transfer data (on intervals if necessary)
+    virtual void FinalizeLoader(FontInfoData *aFontInfo);
+
     // cancel the timer and cleanup
     void CancelLoader();
 
+    uint32_t GetInterval() { return mInterval; }
+
 protected:
     class ShutdownObserver : public nsIObserver
     {
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIOBSERVER
 
         ShutdownObserver(gfxFontInfoLoader *aLoader)
@@ -56,34 +194,56 @@ protected:
 
         virtual ~ShutdownObserver()
         { }
 
     protected:
         gfxFontInfoLoader *mLoader;
     };
 
-    // Init - initialization at start time after initial delay
+    // CreateFontInfo - create platform-specific object used
+    //                  to load system-wide font info
+    virtual already_AddRefed<FontInfoData> CreateFontInfoData() {
+        return nullptr;
+    }
+
+    // Init - initialization before async loader thread runs
     virtual void InitLoader() = 0;
 
-    // Run - called at intervals, return true to indicate done
-    virtual bool RunLoader() = 0;
+    // LoadFontInfo - transfer font info data within a time limit, return
+    //                true when done
+    virtual bool LoadFontInfo() = 0;
 
-    // Finish - cleanup after done
-    virtual void FinishLoader() = 0;
+    // Cleanup - finish and cleanup after done, including possible reflows
+    virtual void CleanupLoader() {
+        mFontInfo = nullptr;
+    }
 
     // Timer interval callbacks
-    static void LoaderTimerCallback(nsITimer *aTimer, void *aThis) {
+    static void LoadFontInfoCallback(nsITimer *aTimer, void *aThis) {
         gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
-        loader->LoaderTimerFire();
+        loader->LoadFontInfoTimerFire();
     }
 
-    void LoaderTimerFire();
+    static void DelayedStartCallback(nsITimer *aTimer, void *aThis) {
+        gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
+        loader->StartLoader(0, loader->GetInterval());
+    }
 
+    void LoadFontInfoTimerFire();
+
+    void AddShutdownObserver();
     void RemoveShutdownObserver();
 
     nsCOMPtr<nsITimer> mTimer;
     nsCOMPtr<nsIObserver> mObserver;
+    nsCOMPtr<nsIThread> mFontLoaderThread;
     uint32_t mInterval;
     TimerState mState;
+
+    // after async font loader completes, data is stored here
+    nsRefPtr<FontInfoData> mFontInfo;
+
+    // time spent on the loader thread
+    mozilla::TimeDuration mLoadTime;
 };
 
 #endif /* GFX_FONT_INFO_LOADER_H */
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -141,49 +141,55 @@ GDIFontEntry::GDIFontEntry(const nsAStri
     if (IsType1())
         mForceGDI = true;
     mIsUserFont = aUserFontData != nullptr;
 
     InitLogFont(aFaceName, aFontType);
 }
 
 nsresult
-GDIFontEntry::ReadCMAP()
+GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
 {
     // attempt this once, if errors occur leave a blank cmap
     if (mCharacterMap) {
         return NS_OK;
     }
 
     // skip non-SFNT fonts completely
     if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE && 
         mFontType != GFX_FONT_TYPE_TT_OPENTYPE &&
         mFontType != GFX_FONT_TYPE_TRUETYPE) 
     {
         mCharacterMap = new gfxCharacterMap();
         mCharacterMap->mBuildOnTheFly = true;
         return NS_ERROR_FAILURE;
     }
 
-    nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
-
-    uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+    nsRefPtr<gfxCharacterMap> charmap;
     nsresult rv;
-
-    AutoFallibleTArray<uint8_t,16384> cmap;
-    rv = CopyFontTable(kCMAP, cmap);
+    bool unicodeFont = false, symbolFont = false;
 
-    bool unicodeFont = false, symbolFont = false; // currently ignored
+    if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
+                                                        mUVSOffset,
+                                                        symbolFont))) {
+        mSymbolFont = symbolFont;
+        rv = NS_OK;
+    } else {
+        uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+        charmap = new gfxCharacterMap();
+        AutoFallibleTArray<uint8_t,16384> cmap;
+        rv = CopyFontTable(kCMAP, cmap);
 
-    if (NS_SUCCEEDED(rv)) {
-        rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
-                                    *charmap, mUVSOffset,
-                                    unicodeFont, symbolFont);
+        if (NS_SUCCEEDED(rv)) {
+            rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
+                                        *charmap, mUVSOffset,
+                                        unicodeFont, symbolFont);
+        }
+        mSymbolFont = symbolFont;
     }
-    mSymbolFont = symbolFont;
 
     mHasCmapTable = NS_SUCCEEDED(rv);
     if (mHasCmapTable) {
         gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
         mCharacterMap = pfl->FindCharMap(charmap);
     } else {
         // if error occurred, initialize to null cmap
         mCharacterMap = new gfxCharacterMap();
@@ -507,17 +513,17 @@ GDIFontFamily::FamilyAddStylesProc(const
              (logFont.lfItalic == 0xff) ? "italic" : "normal",
              logFont.lfWeight, fe->Stretch()));
     }
 #endif
     return 1;
 }
 
 void
-GDIFontFamily::FindStyleVariations()
+GDIFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
 {
     if (mHasStyles)
         return;
     mHasStyles = true;
 
     HDC hdc = GetDC(nullptr);
     SetGraphicsMode(hdc, GM_ADVANCED);
 
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -103,17 +103,17 @@ enum gfxWindowsFontType {
 // A single member of a font family (i.e. a single face, such as Times Italic)
 // represented as a LOGFONT that will resolve to the correct face.
 // This replaces FontEntry from gfxWindowsFonts.h/cpp.
 class GDIFontEntry : public gfxFontEntry
 {
 public:
     LPLOGFONTW GetLogFont() { return &mLogFont; }
 
-    nsresult ReadCMAP();
+    nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
 
     virtual bool IsSymbolFont();
 
     void FillLogFont(LOGFONTW *aLogFont, uint16_t aWeight, gfxFloat aSize,
                      bool aUseCleartype);
 
     static gfxWindowsFontType DetermineFontType(const NEWTEXTMETRICW& metrics, 
                                                 DWORD fontType)
@@ -289,17 +289,17 @@ protected:
 
 // a single font family, referencing one or more faces
 class GDIFontFamily : public gfxFontFamily
 {
 public:
     GDIFontFamily(nsAString &aName) :
         gfxFontFamily(aName) {}
 
-    virtual void FindStyleVariations();
+    virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
 
 private:
     static int CALLBACK FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
                                             const NEWTEXTMETRICEXW *nmetrics,
                                             DWORD fontType, LPARAM data);
 };
 
 class gfxGDIFontList : public gfxPlatformFontList {
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -43,17 +43,17 @@ public:
 
     // override gfxFontEntry table access function to bypass table cache,
     // use CGFontRef API to get direct access to system font data
     virtual hb_blob_t *GetFontTable(uint32_t aTag) MOZ_OVERRIDE;
 
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
-    nsresult ReadCMAP();
+    nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
 
     bool RequiresAATLayout() const { return mRequiresAAT; }
 
     bool IsCFF();
 
 protected:
     virtual gfxFont* CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold);
 
@@ -111,16 +111,18 @@ private:
     virtual gfxFontEntry* GlobalFontFallback(const uint32_t aCh,
                                              int32_t aRunScript,
                                              const gfxFontStyle* aMatchStyle,
                                              uint32_t& aCmapCount,
                                              gfxFontFamily** aMatchedFamily);
 
     virtual bool UsesSystemFallback() { return true; }
 
+    virtual already_AddRefed<FontInfoData> CreateFontInfoData();
+
     enum {
         kATSGenerationInitial = -1
     };
 
     // default font for use with system-wide font fallback
     CTFontRef mDefaultFont;
 };
 
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -217,40 +217,48 @@ SupportsScriptInGSUB(gfxFontEntry* aFont
         hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'),
                                          aScriptTags, &index, &chosenScript);
     hb_face_destroy(face);
 
     return found && chosenScript != TRUETYPE_TAG('D','F','L','T');
 }
 
 nsresult
-MacOSFontEntry::ReadCMAP()
+MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
 {
     // attempt this once, if errors occur leave a blank cmap
     if (mCharacterMap) {
         return NS_OK;
     }
 
-    nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+    nsRefPtr<gfxCharacterMap> charmap;
+    nsresult rv;
+    bool symbolFont;
 
-    uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
-
-    AutoTable cmapTable(this, kCMAP);
-    nsresult rv;
+    if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
+                                                        mUVSOffset,
+                                                        symbolFont))) {
+        rv = NS_OK;
+    } else {
+        uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+        charmap = new gfxCharacterMap();
+        AutoTable cmapTable(this, kCMAP);
 
-    if (cmapTable) {
-        bool unicodeFont = false, symbolFont = false; // currently ignored
-
-        uint32_t cmapLen;
-        const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
-        rv = gfxFontUtils::ReadCMAP((const uint8_t*)cmapData, cmapLen,
-                                    *charmap, mUVSOffset,
-                                    unicodeFont, symbolFont);
-    } else {
-        rv = NS_ERROR_NOT_AVAILABLE;
+        if (cmapTable) {
+            bool unicodeFont = false, symbolFont = false; // currently ignored
+            uint32_t cmapLen;
+            const uint8_t* cmapData =
+                reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
+                                                                  &cmapLen));
+            rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
+                                        *charmap, mUVSOffset,
+                                        unicodeFont, symbolFont);
+        } else {
+            rv = NS_ERROR_NOT_AVAILABLE;
+        }
     }
 
     if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
         // We assume a Graphite font knows what it's doing,
         // and provides whatever shaping is needed for the
         // characters it supports, so only check/clear the
         // complex-script ranges for non-Graphite fonts
 
@@ -441,17 +449,17 @@ public:
     gfxMacFontFamily(nsAString& aName) :
         gfxFontFamily(aName)
     {}
 
     virtual ~gfxMacFontFamily() {}
 
     virtual void LocalizedName(nsAString& aLocalizedName);
 
-    virtual void FindStyleVariations();
+    virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
 };
 
 void
 gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
 {
     nsAutoreleasePool localPool;
 
     if (!HasOtherFamilyNames()) {
@@ -469,17 +477,17 @@ gfxMacFontFamily::LocalizedName(nsAStrin
         return;
     }
 
     // failed to get localized name, just use the canonical one
     aLocalizedName = mName;
 }
 
 void
-gfxMacFontFamily::FindStyleVariations()
+gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
 {
     if (mHasStyles)
         return;
 
     nsAutoreleasePool localPool;
 
     NSString *family = GetNSStringForString(mName);
 
@@ -1009,8 +1017,144 @@ gfxMacPlatformFontList::MakePlatformFont
     // if something is funky about this font, delete immediately
 
 #if DEBUG
     NS_WARNING("downloaded font not loaded properly");
 #endif
 
     return nullptr;
 }
+
+// used to load system-wide font info on off-main thread
+class MacFontInfo : public FontInfoData {
+public:
+    MacFontInfo(bool aLoadOtherNames,
+                bool aLoadFaceNames,
+                bool aLoadCmaps) :
+        FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
+    {}
+
+    virtual ~MacFontInfo() {}
+
+    virtual void Load() {
+        nsAutoreleasePool localPool;
+        FontInfoData::Load();
+    }
+
+    // loads font data for all members of a given family
+    virtual void LoadFontFamilyData(const nsAString& aFamilyName);
+};
+
+void
+MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
+{
+    // family name ==> CTFontDescriptor
+    NSString *famName = GetNSStringForString(aFamilyName);
+    CFStringRef family = CFStringRef(famName);
+
+    CFMutableDictionaryRef attr =
+        CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
+                                  &kCFTypeDictionaryValueCallBacks);
+    CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
+    CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr);
+    CFRelease(attr);
+    CFArrayRef matchingFonts =
+        CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
+    CFRelease(fd);
+    if (!matchingFonts) {
+        return;
+    }
+
+    nsTArray<nsString> otherFamilyNames;
+    bool hasOtherFamilyNames = true;
+
+    // iterate over faces in the family
+    int f, numFaces = (int) CFArrayGetCount(matchingFonts);
+    for (f = 0; f < numFaces; f++) {
+        mLoadStats.fonts++;
+
+        CTFontDescriptorRef faceDesc =
+            (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
+        if (!faceDesc) {
+            continue;
+        }
+        CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc,
+                                                           0.0, nullptr);
+
+        if (mLoadCmaps) {
+            // face name
+            CFStringRef faceName = (CFStringRef)
+                CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
+
+            nsAutoTArray<UniChar, 1024> buffer;
+            CFIndex len = CFStringGetLength(faceName);
+            buffer.SetLength(len+1);
+            CFStringGetCharacters(faceName, ::CFRangeMake(0, len),
+                                    buffer.Elements());
+            buffer[len] = 0;
+            nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()),
+                                  len);
+
+            // load the cmap data
+            FontFaceData fontData;
+            CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap,
+                                                 kCTFontTableOptionNoOptions);
+            if (cmapTable) {
+                bool unicodeFont = false, symbolFont = false; // ignored
+                const uint8_t *cmapData =
+                    (const uint8_t*)CFDataGetBytePtr(cmapTable);
+                uint32_t cmapLen = CFDataGetLength(cmapTable);
+                nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+                uint32_t offset;
+                nsresult rv;
+
+                rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset,
+                                            unicodeFont, symbolFont);
+                if (NS_SUCCEEDED(rv)) {
+                    fontData.mCharacterMap = charmap;
+                    fontData.mUVSOffset = offset;
+                    fontData.mSymbolFont = symbolFont;
+                    mLoadStats.cmaps++;
+                }
+                CFRelease(cmapTable);
+            }
+
+            mFontFaceData.Put(fontName, fontData);
+            CFRelease(faceName);
+        }
+
+        if (mLoadOtherNames && hasOtherFamilyNames) {
+            CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName,
+                                                  kCTFontTableOptionNoOptions);
+            if (nameTable) {
+                const char *nameData = (const char*)CFDataGetBytePtr(nameTable);
+                uint32_t nameLen = CFDataGetLength(nameTable);
+                gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName,
+                                                           nameData, nameLen,
+                                                           otherFamilyNames,
+                                                           false);
+                hasOtherFamilyNames = otherFamilyNames.Length() != 0;
+                CFRelease(nameTable);
+            }
+        }
+
+        CFRelease(fontRef);
+    }
+    CFRelease(matchingFonts);
+
+    // if found other names, insert them in the hash table
+    if (otherFamilyNames.Length() != 0) {
+        mOtherFamilyNames.Put(aFamilyName, otherFamilyNames);
+        mLoadStats.othernames += otherFamilyNames.Length();
+    }
+}
+
+already_AddRefed<FontInfoData>
+gfxMacPlatformFontList::CreateFontInfoData()
+{
+    bool loadCmaps = !UsesSystemFallback() ||
+        gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+    nsRefPtr<MacFontInfo> fi =
+        new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
+    return fi.forget();
+}
+
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -553,44 +553,63 @@ static void LogRegistryEvent(const wchar
   wsprintfW(buf, L" log %s", msg);
   hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey);
   if (SUCCEEDED(hr)) {
     RegCloseKey(dummyKey);
   }
 }
 #endif
 
+gfxFontFamily*
+gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily)
+{
+    if (aFamily && !aFamily->HasStyles()) {
+        aFamily->FindStyleVariations();
+        aFamily->CheckForSimpleFamily();
+    }
+
+    if (aFamily && aFamily->GetFontList().Length() == 0) {
+        // failed to load any faces for this family, so discard it
+        nsAutoString key;
+        GenerateFontListKey(aFamily->Name(), key);
+        mFontFamilies.Remove(key);
+        return nullptr;
+    }
+
+    return aFamily;
+}
+
 gfxFontFamily* 
 gfxPlatformFontList::FindFamily(const nsAString& aFamily)
 {
     nsAutoString key;
     gfxFontFamily *familyEntry;
     GenerateFontListKey(aFamily, key);
 
     NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
 
     // lookup in canonical (i.e. English) family name list
     if ((familyEntry = mFontFamilies.GetWeak(key))) {
-        return familyEntry;
+        return CheckFamily(familyEntry);
     }
 
     // lookup in other family names list (mostly localized names)
     if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) {
-        return familyEntry;
+        return CheckFamily(familyEntry);
     }
 
     // name not found and other family names not yet fully initialized so
     // initialize the rest of the list and try again.  this is done lazily
     // since reading name table entries is expensive.
     // although ASCII localized family names are possible they don't occur
     // in practice so avoid pulling in names at startup
     if (!mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
         InitOtherFamilyNames();
         if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) {
-            return familyEntry;
+            return CheckFamily(familyEntry);
         }
     }
 
     return nullptr;
 }
 
 gfxFontEntry*
 gfxPlatformFontList::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold)
@@ -700,78 +719,115 @@ gfxPlatformFontList::RemoveCmap(const gf
     // cmap needs to match the entry *and* be the same ptr before removing
     CharMapHashKey *found =
         mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
     if (found && found->GetKey() == aCharMap) {
         mSharedCmaps.RemoveEntry(const_cast<gfxCharacterMap*>(aCharMap));
     }
 }
 
+static PLDHashOperator AppendFamilyToList(nsStringHashKey::KeyType aKey,
+                                          nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                                          void *aUserArg)
+{
+    nsTArray<nsString> *familyNames = static_cast<nsTArray<nsString> *>(aUserArg);
+    familyNames->AppendElement(aFamilyEntry->Name());
+    return PL_DHASH_NEXT;
+}
+
+void
+gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
+{
+    mFontFamilies.Enumerate(AppendFamilyToList, &aFontFamilyNames);
+}
+
 void 
 gfxPlatformFontList::InitLoader()
 {
-    GetFontFamilyList(mFontFamiliesToLoad);
+    GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
     mStartIndex = 0;
-    mNumFamilies = mFontFamiliesToLoad.Length();
+    mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
+    memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
 }
 
 #define FONT_LOADER_MAX_TIMESLICE 100  // max time for one pass through RunLoader = 100ms
 
 bool
-gfxPlatformFontList::RunLoader()
+gfxPlatformFontList::LoadFontInfo()
 {
     TimeStamp start = TimeStamp::Now();
-    uint32_t i, endIndex = (mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies);
+    uint32_t i, endIndex = mNumFamilies;
     bool loadCmaps = !UsesSystemFallback() ||
         gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
 
     // for each font family, load in various font info
     for (i = mStartIndex; i < endIndex; i++) {
-        gfxFontFamily* familyEntry = mFontFamiliesToLoad[i];
+        nsAutoString key;
+        gfxFontFamily *familyEntry;
+        GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
 
-        // find all faces that are members of this family
-        familyEntry->FindStyleVariations();
-        if (familyEntry->GetFontList().Length() == 0) {
-            // failed to load any faces for this family, so discard it
-            nsAutoString key;
-            GenerateFontListKey(familyEntry->Name(), key);
-            mFontFamilies.Remove(key);
+        // lookup in canonical (i.e. English) family name list
+        if (!(familyEntry = mFontFamilies.GetWeak(key))) {
             continue;
         }
 
+        // read in face names
+        familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(), mFontInfo);
+
         // load the cmaps if needed
         if (loadCmaps) {
-            familyEntry->ReadAllCMAPs();
+            familyEntry->ReadAllCMAPs(mFontInfo);
         }
 
-        // read in face names
-        familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames());
-
-        // check whether the family can be considered "simple" for style matching
-        familyEntry->CheckForSimpleFamily();
-
         // limit the time spent reading fonts in one pass
         TimeDuration elapsed = TimeStamp::Now() - start;
         if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
                 i + 1 != endIndex) {
             endIndex = i + 1;
             break;
         }
     }
 
     mStartIndex = endIndex;
+    bool done = mStartIndex >= mNumFamilies;
 
-    return (mStartIndex >= mNumFamilies);
+#ifdef PR_LOGGING
+    if (LOG_FONTINIT_ENABLED()) {
+        TimeDuration elapsed = TimeStamp::Now() - start;
+        LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
+                      elapsed.ToMilliseconds(), (done ? "true" : "false")));
+    }
+#endif
+
+    return done;
 }
 
 void 
-gfxPlatformFontList::FinishLoader()
+gfxPlatformFontList::CleanupLoader()
 {
     mFontFamiliesToLoad.Clear();
     mNumFamilies = 0;
+
+#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",
+                      mLoadTime.ToMilliseconds(),
+                      mFontInfo->mLoadStats.families,
+                      mFontInfo->mLoadStats.fonts,
+                      mFontInfo->mLoadStats.cmaps,
+                      mFontInfo->mLoadStats.facenames,
+                      mFontInfo->mLoadStats.othernames));
+    }
+#endif
+
+    mOtherFamilyNamesInitialized = true;
+    mFaceNamesInitialized = true;
+    gfxFontInfoLoader::CleanupLoader();
 }
 
 void
 gfxPlatformFontList::GetPrefsAndStartLoader()
 {
     mIncrement =
         std::max(1u, Preferences::GetUint(FONT_LOADER_FAMILIES_PER_SLICE_PREF));
 
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -77,17 +77,17 @@ protected:
 struct FontListSizes {
     uint32_t mFontListSize; // size of the font list and dependent objects
                             // (font family and face names, etc), but NOT
                             // including the font table cache and the cmaps
     uint32_t mFontTableCacheSize; // memory used for the gfxFontEntry table caches
     uint32_t mCharMapsSize; // memory used for cmap coverage info
 };
 
-class gfxPlatformFontList : protected gfxFontInfoLoader
+class gfxPlatformFontList : public gfxFontInfoLoader
 {
 public:
     static gfxPlatformFontList* PlatformFontList() {
         return sPlatformFontList;
     }
 
     static nsresult Init() {
         NS_ASSERTION(!sPlatformFontList, "What's this doing here?");
@@ -206,16 +206,19 @@ protected:
                                              const gfxFontStyle* aMatchStyle,
                                              uint32_t& aCmapCount,
                                              gfxFontFamily** aMatchedFamily);
 
     // whether system-based font fallback is used or not
     // if system fallback is used, no need to load all cmaps
     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);
 
     // read in all fullname/Postscript names for all font faces
@@ -236,20 +239,22 @@ protected:
 
     void GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult);
 
     static PLDHashOperator
         HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey,
                                 nsRefPtr<gfxFontFamily>& aFamilyEntry,
                                 void* aUserArg);
 
+    virtual void GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames);
+
     // gfxFontInfoLoader overrides, used to load in font cmaps
     virtual void InitLoader();
-    virtual bool RunLoader();
-    virtual void FinishLoader();
+    virtual bool LoadFontInfo();
+    virtual void CleanupLoader();
 
     // read the loader initialization prefs, and start it
     void GetPrefsAndStartLoader();
 
     // used by memory reporter to accumulate sizes of family names in the hash
     static size_t
     SizeOfFamilyNameEntryExcludingThis(const nsAString&               aKey,
                                        const nsRefPtr<gfxFontFamily>& aFamily,