bug 1030829 part 2 - preload 'hidden' fonts on FirefoxOS into the user-font cache. r=roc
authorJonathan Kew <jkew@mozilla.com>
Mon, 30 Jun 2014 19:05:29 +0100
changeset 191482 7790d48f1451ce31aa7a4db3bd99b55f79dcc22b
parent 191481 57eebbb377ec3f6a32f98e49de8120f9cd348e42
child 191483 afab5eedc9ae2e40c7ed992be1f195fe1b6a76f2
push id45585
push userjkew@mozilla.com
push dateMon, 30 Jun 2014 18:05:37 +0000
treeherdermozilla-inbound@7790d48f1451 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1030829
milestone33.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 1030829 part 2 - preload 'hidden' fonts on FirefoxOS into the user-font cache. r=roc
dom/ipc/PContent.ipdl
gfx/thebes/gfxFT2FontList.cpp
gfx/thebes/gfxFT2FontList.h
gfx/thebes/gfxUserFontSet.h
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -61,16 +61,17 @@ namespace dom {
 struct FontListEntry {
     nsString  familyName;
     nsString  faceName;
     nsCString filepath;
     uint16_t  weight;
     int16_t   stretch;
     uint8_t   italic;
     uint8_t   index;
+    bool      isHidden;
 };
 
 struct DeviceStorageFreeSpaceParams
 {
   nsString type;
   nsString storageName;
 };
 
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1,14 +1,15 @@
 /* -*- 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 "mozilla/ArrayUtils.h"
+#include "mozilla/Base64.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "gfxAndroidPlatform.h"
 #include "mozilla/Omnijar.h"
 #include "nsAutoPtr.h"
 #include "nsIInputStream.h"
 #include "nsNetUtil.h"
@@ -38,16 +39,18 @@
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIMemory.h"
 #include "gfxFontConstants.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/scache/StartupCache.h"
+#include <fcntl.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *
 GetFontInfoLog()
 {
@@ -577,30 +580,32 @@ FT2FontEntry::AddSizeOfIncludingThis(Mal
 
 /*
  * FT2FontFamily
  * A standard gfxFontFamily; just adds a method used to support sending
  * the font list from chrome to content via IPC.
  */
 
 void
-FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList)
+FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
+                                  Visibility aVisibility)
 {
     for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
         const FT2FontEntry *fe =
             static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
         if (!fe) {
             continue;
         }
         
         aFontList->AppendElement(FontListEntry(Name(), fe->Name(),
                                                fe->mFilename,
                                                fe->Weight(), fe->Stretch(),
                                                fe->IsItalic(),
-                                               fe->mFTFontIndex));
+                                               fe->mFTFontIndex,
+                                               aVisibility == kHidden));
     }
 }
 
 /*
  * Startup cache support for the font list:
  * We store the list of families and faces, with their style attributes and the
  * corresponding font files, in the startup cache.
  * This allows us to recreate the gfxFT2FontList collection of families and
@@ -822,19 +827,21 @@ private:
 // For Mobile, we use gfxFT2Fonts, and we build the font list by directly
 // scanning the system's Fonts directory for OpenType and TrueType files.
 
 gfxFT2FontList::gfxFT2FontList()
 {
 }
 
 void
-gfxFT2FontList::AppendFacesFromCachedFaceList(const nsCString& aFileName,
-                                              bool aStdFile,
-                                              const nsCString& aFaceList)
+gfxFT2FontList::AppendFacesFromCachedFaceList(
+    const nsCString& aFileName,
+    const nsCString& aFaceList,
+    StandardFile aStdFile,
+    FT2FontFamily::Visibility aVisibility)
 {
     const char *beginning = aFaceList.get();
     const char *end = strchr(beginning, ',');
     while (end) {
         nsString familyName =
             NS_ConvertUTF8toUTF16(beginning, end - beginning);
         ToLowerCase(familyName);
         beginning = end + 1;
@@ -860,17 +867,18 @@ gfxFT2FontList::AppendFacesFromCachedFac
         uint32_t weight = strtoul(beginning, nullptr, 10);
         beginning = end + 1;
         if (!(end = strchr(beginning, ','))) {
             break;
         }
         int32_t stretch = strtol(beginning, nullptr, 10);
 
         FontListEntry fle(familyName, faceName, aFileName,
-                          weight, stretch, italic, index);
+                          weight, stretch, italic, index,
+                          aVisibility == FT2FontFamily::kHidden);
         AppendFaceFromFontListEntry(fle, aStdFile);
 
         beginning = end + 1;
         end = strchr(beginning, ',');
     }
 }
 
 static void
@@ -918,48 +926,50 @@ FT2FontEntry::CheckForBrokenFont(gfxFont
                 mIgnoreGSUB = true;
             }
         }
     }
 }
 
 void
 gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
-                                        bool aStdFile,
-                                        FontNameCache *aCache)
+                                        FontNameCache *aCache,
+                                        StandardFile aStdFile,
+                                        FT2FontFamily::Visibility aVisibility)
 {
     nsCString faceList;
     uint32_t filesize = 0, timestamp = 0;
     if (aCache) {
         aCache->GetInfoForFile(aFileName, faceList, &timestamp, &filesize);
     }
 
     struct stat s;
     int statRetval = stat(aFileName.get(), &s);
     if (!faceList.IsEmpty() && 0 == statRetval &&
         s.st_mtime == timestamp && s.st_size == filesize)
     {
         LOG(("using cached font info for %s", aFileName.get()));
-        AppendFacesFromCachedFaceList(aFileName, aStdFile, faceList);
+        AppendFacesFromCachedFaceList(aFileName, faceList, aStdFile,
+                                      aVisibility);
         return;
     }
 
     FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
     FT_Face dummy;
     if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) {
         LOG(("reading font info via FreeType for %s", aFileName.get()));
         nsCString faceList;
         timestamp = s.st_mtime;
         filesize = s.st_size;
         for (FT_Long i = 0; i < dummy->num_faces; i++) {
             FT_Face face;
             if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
                 continue;
             }
-            AddFaceToList(aFileName, i, aStdFile, face, faceList);
+            AddFaceToList(aFileName, i, aStdFile, aVisibility, face, faceList);
             FT_Done_Face(face);
         }
         FT_Done_Face(dummy);
         if (aCache && 0 == statRetval && !faceList.IsEmpty()) {
             aCache->CacheFileInfo(aFileName, faceList, timestamp, filesize);
         }
     }
 }
@@ -1012,44 +1022,50 @@ gfxFT2FontList::FindFontsInOmnijar(FontN
                          sizeof(jarModifiedTime));
     }
 }
 
 // Given the freetype face corresponding to an entryName and face index,
 // add the face to the available font list and to the faceList string
 void
 gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
-                              bool aStdFile, FT_Face aFace,
+                              StandardFile aStdFile,
+                              FT2FontFamily::Visibility aVisibility,
+                              FT_Face aFace,
                               nsCString& aFaceList)
 {
     if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
         // ignore faces that don't support a Unicode charmap
         return;
     }
 
     // build the font entry name and create an FT2FontEntry,
     // but do -not- keep a reference to the FT_Face
     FT2FontEntry* fe =
         CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
 
+    auto& fontFamilies =
+        (aVisibility == FT2FontFamily::kHidden) ? mHiddenFontFamilies :
+                                                  mFontFamilies;
+
     if (fe) {
         NS_ConvertUTF8toUTF16 name(aFace->family_name);
         BuildKeyNameFromFontName(name);
-        gfxFontFamily *family = mFontFamilies.GetWeak(name);
+        gfxFontFamily *family = fontFamilies.GetWeak(name);
         if (!family) {
             family = new FT2FontFamily(name);
-            mFontFamilies.Put(name, family);
+            fontFamilies.Put(name, family);
             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
                 family->SetSkipSpaceFeatureCheck(true);
             }
             if (mBadUnderlineFamilyNames.Contains(name)) {
                 family->SetBadUnderlineFamily();
             }
         }
-        fe->mStandardFace = aStdFile;
+        fe->mStandardFace = (aStdFile == kStandard);
         family->AddFontEntry(fe);
 
         fe->CheckForBrokenFont(family);
 
         AppendToFaceList(aFaceList, name, fe);
 #ifdef PR_LOGGING
         if (LOG_ENABLED()) {
             LOG(("(fontinit) added (%s) to family (%s)"
@@ -1069,17 +1085,17 @@ gfxFT2FontList::AppendFacesFromOmnijarEn
                                             FontNameCache *aCache,
                                             bool aJarChanged)
 {
     nsCString faceList;
     if (aCache && !aJarChanged) {
         uint32_t filesize, timestamp;
         aCache->GetInfoForFile(aEntryName, faceList, &timestamp, &filesize);
         if (faceList.Length() > 0) {
-            AppendFacesFromCachedFaceList(aEntryName, true, faceList);
+            AppendFacesFromCachedFaceList(aEntryName, faceList);
             return;
         }
     }
 
     nsZipItem *item = aArchive->GetItem(aEntryName.get());
     NS_ASSERTION(item, "failed to find zip entry");
 
     uint32_t bufSize = item->RealSize();
@@ -1106,17 +1122,18 @@ gfxFT2FontList::AppendFacesFromOmnijarEn
         return;
     }
 
     for (FT_Long i = 0; i < dummy->num_faces; i++) {
         FT_Face face;
         if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf, bufSize, i, &face)) {
             continue;
         }
-        AddFaceToList(aEntryName, i, true, face, faceList);
+        AddFaceToList(aEntryName, i, kStandard, FT2FontFamily::kVisible,
+                      face, faceList);
         FT_Done_Face(face);
     }
 
     FT_Done_Face(dummy);
 
     if (aCache && !faceList.IsEmpty()) {
         aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
     }
@@ -1155,24 +1172,29 @@ gfxFT2FontList::FindFonts()
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
 
     if (XRE_GetProcessType() != GeckoProcessType_Default) {
         // Content process: ask the Chrome process to give us the list
         InfallibleTArray<FontListEntry> fonts;
         mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts);
         for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
-            AppendFaceFromFontListEntry(fonts[i], false);
+            // We don't need to identify "standard" font files here,
+            // as the faces are already sorted.
+            AppendFaceFromFontListEntry(fonts[i], kUnknown);
         }
         // Passing null for userdata tells Finalize that it does not need
         // to sort faces (because they were already sorted by chrome,
         // so we just maintain the existing order)
         mFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr);
-        LOG(("got font list from chrome process: %d faces in %d families",
-            fonts.Length(), mFontFamilies.Count()));
+        mHiddenFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr);
+        LOG(("got font list from chrome process: %d faces in %d families "
+             "and %d in hidden families",
+            fonts.Length(), mFontFamilies.Count(),
+            mHiddenFontFamilies.Count()));
         return;
     }
 
     // Chrome process: get the cached list (if any)
     FontNameCache fnc;
 
     // ANDROID_ROOT is the root of the android system, typically /system;
     // font files are in /$ANDROID_ROOT/fonts/
@@ -1180,23 +1202,30 @@ gfxFT2FontList::FindFonts()
     char *androidRoot = PR_GetEnv("ANDROID_ROOT");
     if (androidRoot) {
         root = androidRoot;
     } else {
         root = NS_LITERAL_CSTRING("/system");
     }
     root.AppendLiteral("/fonts");
 
-    FindFontsInDir(root, &fnc);
+    FindFontsInDir(root, &fnc, FT2FontFamily::kVisible);
 
     if (mFontFamilies.Count() == 0) {
         // if we can't find/read the font directory, we are doomed!
         NS_RUNTIMEABORT("Could not read the system fonts directory");
     }
 
+#ifdef MOZ_WIDGET_GONK
+    // Look for fonts in /system/fonts/hidden and preload them to the
+    // user-font cache as data: URIs
+    root.AppendLiteral("/hidden");
+    FindFontsInDir(root, &fnc, FT2FontFamily::kHidden);
+#endif
+
     // Look for fonts stored in omnijar, unless we're on a low-memory
     // device where we don't want to spend the RAM to decompress them.
     // (Prefs may disable this, or force-enable it even with low memory.)
     bool lowmem;
     nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService();
     if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem &&
          Preferences::GetBool("gfx.bundled_fonts.enabled")) ||
         Preferences::GetBool("gfx.bundled_fonts.force-enabled")) {
@@ -1207,28 +1236,31 @@ gfxFT2FontList::FindFonts()
     nsCOMPtr<nsIFile> localDir;
     nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
                                          getter_AddRefs(localDir));
     if (NS_SUCCEEDED(rv) &&
         NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
         nsCString localPath;
         rv = localDir->GetNativePath(localPath);
         if (NS_SUCCEEDED(rv)) {
-            FindFontsInDir(localPath, &fnc);
+            FindFontsInDir(localPath, &fnc, FT2FontFamily::kVisible);
         }
     }
 
     // Finalize the families by sorting faces into standard order
     // and marking "simple" families.
     // Passing non-null userData here says that we want faces to be sorted.
     mFontFamilies.Enumerate(FinalizeFamilyMemberList, this);
+    mHiddenFontFamilies.Enumerate(FinalizeFamilyMemberList, this);
 }
 
 void
-gfxFT2FontList::FindFontsInDir(const nsCString& aDir, FontNameCache *aFNC)
+gfxFT2FontList::FindFontsInDir(const nsCString& aDir,
+                               FontNameCache *aFNC,
+                               FT2FontFamily::Visibility aVisibility)
 {
     static const char* sStandardFonts[] = {
         "DroidSans.ttf",
         "DroidSans-Bold.ttf",
         "DroidSerif-Regular.ttf",
         "DroidSerif-Bold.ttf",
         "DroidSerif-Italic.ttf",
         "DroidSerif-BoldItalic.ttf",
@@ -1267,35 +1299,38 @@ gfxFT2FontList::FindFontsInDir(const nsC
             nsCString s(aDir);
             s.Append('/');
             s.Append(ent->d_name);
 
             // Add the face(s) from this file to our font list;
             // note that if we have cached info for this file in fnc,
             // and the file is unchanged, we won't actually need to read it.
             // If the file is new/changed, this will update the FontNameCache.
-            AppendFacesFromFontFile(s, isStdFont, aFNC);
+            AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown,
+                                    aVisibility);
         }
     }
 
     closedir(d);
 }
 
 void
 gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
-                                            bool aStdFile)
+                                            StandardFile aStdFile)
 {
     FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
     if (fe) {
-        fe->mStandardFace = aStdFile;
+        auto& fontFamilies =
+            aFLE.isHidden() ? mHiddenFontFamilies : mFontFamilies;
+        fe->mStandardFace = (aStdFile == kStandard);
         nsAutoString name(aFLE.familyName());
-        gfxFontFamily *family = mFontFamilies.GetWeak(name);
+        gfxFontFamily *family = fontFamilies.GetWeak(name);
         if (!family) {
             family = new FT2FontFamily(name);
-            mFontFamilies.Put(name, family);
+            fontFamilies.Put(name, family);
             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
                 family->SetSkipSpaceFeatureCheck(true);
             }
             if (mBadUnderlineFamilyNames.Contains(name)) {
                 family->SetBadUnderlineFamily();
             }
         }
         family->AddFontEntry(fe);
@@ -1308,51 +1343,143 @@ static PLDHashOperator
 AddFamilyToFontList(nsStringHashKey::KeyType aKey,
                     nsRefPtr<gfxFontFamily>& aFamily,
                     void* aUserArg)
 {
     InfallibleTArray<FontListEntry>* fontlist =
         reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
 
     FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get());
-    family->AddFacesToFontList(fontlist);
+    family->AddFacesToFontList(fontlist, FT2FontFamily::kVisible);
+
+    return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
+AddHiddenFamilyToFontList(nsStringHashKey::KeyType aKey,
+                          nsRefPtr<gfxFontFamily>& aFamily,
+                          void* aUserArg)
+{
+    InfallibleTArray<FontListEntry>* fontlist =
+        reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
+
+    FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get());
+    family->AddFacesToFontList(fontlist, FT2FontFamily::kHidden);
 
     return PL_DHASH_NEXT;
 }
 
 void
 gfxFT2FontList::GetFontList(InfallibleTArray<FontListEntry>* retValue)
 {
     mFontFamilies.Enumerate(AddFamilyToFontList, retValue);
+    mHiddenFontFamilies.Enumerate(AddHiddenFamilyToFontList, retValue);
 }
 
 static void
 LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
 {
     nsAutoTArray<nsString, 5> skiplist;
     gfxFontUtils::GetPrefsFontList(
         "font.whitelist.skip_default_features_space_check",
         skiplist);
     uint32_t numFonts = skiplist.Length();
     for (uint32_t i = 0; i < numFonts; i++) {
         ToLowerCase(skiplist[i]);
         aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
     }
 }
 
+static PLDHashOperator
+PreloadAsUserFontFaces(nsStringHashKey::KeyType aKey,
+                       nsRefPtr<gfxFontFamily>& aFamily,
+                       void* aUserArg)
+{
+    gfxFontFamily *family = aFamily.get();
+
+    auto& faces = family->GetFontList();
+    size_t count = faces.Length();
+    for (size_t i = 0; i < count; ++i) {
+        FT2FontEntry* fe = static_cast<FT2FontEntry*>(faces[i].get());
+        if (fe->mFTFontIndex != 0) {
+            NS_NOTREACHED("don't try to preload a multi-face font");
+            continue;
+        }
+
+        // XXX Should we move the i/o here off the main thread?
+
+        // Map the font data in fe->mFilename, so we can generate a data: URI.
+        int fd = open(fe->mFilename.get(), O_RDONLY);
+        if (fd < 0) {
+            continue;
+        }
+        struct stat buf;
+        if (fstat(fd, &buf) != 0 || buf.st_size < 12) {
+            close(fd);
+            continue;
+        }
+        char* data = static_cast<char*>(
+            mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
+        close(fd);
+        if (data == MAP_FAILED) {
+            continue;
+        }
+
+        // First byte is sufficient to distinguish WOFF from uncompressed
+        // OpenType (either TrueType or CFF).
+        bool isWoff = (data[0] == 'w');
+
+        // Generate a corresponding data: URI that apps could use.
+        nsCString encodedData;
+        nsresult rv = Base64Encode(Substring(data, buf.st_size), encodedData);
+        munmap(data, buf.st_size);
+        if (NS_FAILED(rv)) {
+            continue;
+        }
+        nsCString spec("data:font/");
+        spec.Append(isWoff ? "woff" : "opentype");
+        spec.Append(";base64,");
+        spec.Append(encodedData);
+#if 0
+        ALOG("\n**** Preloading family [%s] face [%s]:\n%s\n\n",
+             NS_ConvertUTF16toUTF8(family->Name()).get(),
+             fe->mFilename.get(),
+             spec.get());
+#endif
+
+        // Record the URI in gfxUserFontData on the entry.
+        nsCOMPtr<nsIURI> uri;
+        if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), spec))) {
+            continue;
+        }
+        fe->mUserFontData = new gfxUserFontData;
+        fe->mUserFontData->mURI = uri;
+        fe->mUserFontData->mRealName = fe->Name();
+
+        // Stash it persistently in the user-font cache.
+        gfxUserFontSet::UserFontCache::CacheFont(
+            fe, gfxUserFontSet::UserFontCache::kPersistent);
+    }
+
+    return PL_DHASH_NEXT;
+}
+
 nsresult
 gfxFT2FontList::InitFontList()
 {
     // reset font lists
     gfxPlatformFontList::InitFontList();
+    mHiddenFontFamilies.Clear();
     
     LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
 
     FindFonts();
 
+    mHiddenFontFamilies.Enumerate(PreloadAsUserFontFaces, this);
+
     return NS_OK;
 }
 
 struct FullFontNameSearch {
     FullFontNameSearch(const nsAString& aFullName)
         : mFullName(aFullName), mFontEntry(nullptr)
     { }
 
@@ -1397,16 +1524,18 @@ FindFullName(nsStringHashKey::KeyType aK
 
 gfxFontEntry* 
 gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                 const nsAString& aFontName)
 {
     // walk over list of names
     FullFontNameSearch data(aFontName);
 
+    // Note that we only check mFontFamilies here, not mHiddenFontFamilies;
+    // hence @font-face { src:local(...) } will not find hidden fonts.
     mFontFamilies.Enumerate(FindFullName, &data);
 
     if (!data.mFontEntry) {
         return nullptr;
     }
 
     // Clone the font entry so that we can then set its style descriptors
     // from the proxy rather than the actual font.
@@ -1454,8 +1583,26 @@ gfxFT2FontList::MakePlatformFont(const g
                                  uint32_t aLength)
 {
     // The FT2 font needs the font data to persist, so we do NOT free it here
     // but instead pass ownership to the font entry.
     // Deallocation will happen later, when the font face is destroyed.
     return FT2FontEntry::CreateFontEntry(*aProxyEntry, aFontData, aLength);
 }
 
+static PLDHashOperator
+AppendFamily(nsStringHashKey::KeyType aKey,
+             nsRefPtr<gfxFontFamily>& aFamily,
+             void* aUserArg)
+{
+    nsTArray<nsRefPtr<gfxFontFamily> > * familyArray =
+        reinterpret_cast<nsTArray<nsRefPtr<gfxFontFamily>>*>(aUserArg);
+
+    familyArray->AppendElement(aFamily);
+    return PL_DHASH_NEXT;
+}
+
+void
+gfxFT2FontList::GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray)
+{
+    mFontFamilies.Enumerate(AppendFamily, &aFamilyArray);
+    mHiddenFontFamilies.Enumerate(AppendFamily, &aFamilyArray);
+}
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -90,21 +90,30 @@ public:
 
     nsCString mFilename;
     uint8_t   mFTFontIndex;
 };
 
 class FT2FontFamily : public gfxFontFamily
 {
 public:
+    // Flags to indicate whether a font should be "visible" in the global
+    // font list (available for use in font-family), or "hidden" (available
+    // only to support a matching data: URI used in @font-face).
+    typedef enum {
+        kVisible,
+        kHidden
+    } Visibility;
+
     FT2FontFamily(const nsAString& aName) :
         gfxFontFamily(aName) { }
 
     // Append this family's faces to the IPC fontlist
-    void AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList);
+    void AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
+                            Visibility aVisibility);
 };
 
 class gfxFT2FontList : public gfxPlatformFontList
 {
 public:
     gfxFT2FontList();
 
     virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
@@ -117,40 +126,57 @@ public:
                                            uint32_t aLength);
 
     void GetFontList(InfallibleTArray<FontListEntry>* retValue);
 
     static gfxFT2FontList* PlatformFontList() {
         return static_cast<gfxFT2FontList*>(gfxPlatformFontList::PlatformFontList());
     }
 
+    virtual void GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray);
+
 protected:
+    typedef enum {
+        kUnknown,
+        kStandard
+    } StandardFile;
+
     virtual nsresult InitFontList();
 
     void AppendFaceFromFontListEntry(const FontListEntry& aFLE,
-                                     bool isStdFile);
+                                     StandardFile aStdFile);
 
     void AppendFacesFromFontFile(const nsCString& aFileName,
-                                 bool isStdFile = false,
-                                 FontNameCache *aCache = nullptr);
+                                 FontNameCache *aCache,
+                                 StandardFile aStdFile,
+                                 FT2FontFamily::Visibility aVisibility);
 
     void AppendFacesFromOmnijarEntry(nsZipArchive *aReader,
                                      const nsCString& aEntryName,
                                      FontNameCache *aCache,
                                      bool aJarChanged);
 
+    // the defaults here are suitable for reading bundled fonts from omnijar
     void AppendFacesFromCachedFaceList(const nsCString& aFileName,
-                                       bool isStdFile,
-                                       const nsCString& aFaceList);
+                                       const nsCString& aFaceList,
+                                       StandardFile aStdFile = kStandard,
+                                       FT2FontFamily::Visibility aVisibility =
+                                           FT2FontFamily::kVisible);
 
     void AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
-                       bool aStdFile, FT_Face aFace, nsCString& aFaceList);
+                       StandardFile aStdFile,
+                       FT2FontFamily::Visibility aVisibility,
+                       FT_Face aFace, nsCString& aFaceList);
 
     void FindFonts();
 
     void FindFontsInOmnijar(FontNameCache *aCache);
 
-    void FindFontsInDir(const nsCString& aDir, FontNameCache* aFNC);
+    void FindFontsInDir(const nsCString& aDir, FontNameCache* aFNC,
+                        FT2FontFamily::Visibility aVisibility);
 
     nsTHashtable<nsStringHashKey> mSkipSpaceLookupCheckFamilies;
+
+private:
+    nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mHiddenFontFamilies;
 };
 
 #endif /* GFX_FT2FONTLIST_H */
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -56,17 +56,17 @@ operator==(const gfxFontFaceSrc& a, cons
 // deleted.
 // Lifetime: from when platform font is created until it is deactivated.
 // If the platform does not need to add any platform-specific code/data here,
 // then the gfxUserFontSet will allocate a base gfxUserFontData and attach
 // to the entry to track the basic user font info fields here.
 class gfxUserFontData {
 public:
     gfxUserFontData()
-        : mSrcIndex(0), mFormat(0), mMetaOrigLen(0)
+        : mSrcIndex(0), mFormat(0), mMetaOrigLen(0), mPrivate(false)
     { }
     virtual ~gfxUserFontData() { }
 
     nsTArray<uint8_t> mMetadata;  // woff metadata block (compressed), if any
     nsCOMPtr<nsIURI>  mURI;       // URI of the source, if it was url()
     nsCOMPtr<nsIPrincipal> mPrincipal; // principal for the download, if url()
     nsString          mLocalName; // font name used for the source, if local()
     nsString          mRealName;  // original fullname from the font resource