Bug 1121643 - Add "font.system.whitelist" pref to resist font-based fingerprinting. r=jfkthame
authorArthur Edelstein <arthuredelstein@gmail.com>
Tue, 23 Aug 2016 00:06:07 +0000
changeset 358506 e688328c81ec31ebf184d9b9079c9a3582cc8fd3
parent 358505 1e11d10bd648150440e784bb7ce0b120319ef5b2
child 358507 c89ddd6aaf2aad484b4a0eacdcbad5f5bba7cbdb
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1121643
milestone52.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 1121643 - Add "font.system.whitelist" pref to resist font-based fingerprinting. r=jfkthame
gfx/tests/mochitest/mochitest.ini
gfx/tests/mochitest/test_font_whitelist.html
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxDWriteFontList.h
gfx/thebes/gfxFT2FontList.cpp
gfx/thebes/gfxFT2FontList.h
gfx/thebes/gfxFcPlatformFontList.cpp
gfx/thebes/gfxFcPlatformFontList.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
gfx/thebes/gfxUserFontSet.cpp
--- a/gfx/tests/mochitest/mochitest.ini
+++ b/gfx/tests/mochitest/mochitest.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 
 [test_acceleration.html]
 subsuite = gpu
 fail-if = (os == "win" && os_version == "5.1" && e10s) # Bug 1253862
 [test_bug509244.html]
 [test_bug513439.html]
+[test_font_whitelist.html]
 [test_overdraw.html]
 # Disable test until bug 1064136 is fixed
 skip-if = true
new file mode 100644
--- /dev/null
+++ b/gfx/tests/mochitest/test_font_whitelist.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1121643
+-->
+<head>
+  <title>Test for Bug 1121643</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121643">Mozilla Bug 1121643</a>
+<span id="mono" style="font-family: monospace; font-size: 64px;">M</span>
+<span id="sans" style="font-family: sans-serif; font-size: 64px;">M</span>
+<span id="serif" style="font-family: serif; font-size: 64px;">M</span>
+<div id="content" style="display: none">
+
+</div>
+<script class="testbody" type="application/javascript;version=1.7">
+
+/** Test for Bug 1121643 **/
+
+const DOMUtils =  SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+                               .getService(SpecialPowers.Ci.inIDOMUtils);
+
+// Given an element id, returns the first font face name encountered.
+let fontUsed = id => {
+  let element = document.getElementById(id),
+      range = document.createRange();
+  range.selectNode(element);
+  return DOMUtils.getUsedFontFaces(range).item(0).CSSFamilyName;
+}
+
+// A map of the default mono, sans and serif fonts, obtained when
+// whitelisting is disabled.
+const fonts = { mono : fontUsed("mono"),
+                sans : fontUsed("sans"),
+                serif : fontUsed("serif") };
+
+// Set the font whitelist to contain none, some, or all of the
+// default mono, sans, and serif fonts. Check that the rendering
+// of our three test elements uses only fonts present in the
+// whitelist.
+let testFontWhitelist = function* (useMono, useSans, useSerif) {
+  let whitelist = [];
+  if (useMono) {
+    whitelist.push(fonts.mono);
+  }
+  if (useSans) {
+    whitelist.push(fonts.sans);
+  }
+  if (useSerif) {
+    whitelist.push(fonts.serif);
+  }
+  yield SpecialPowers.pushPrefEnv({"set": [["font.system.whitelist",
+                                            whitelist.join(", ")]]});
+  // If whitelist is empty, then whitelisting is considered disabled
+  // and all fonts are allowed.
+  info("font whitelist: " + JSON.stringify(whitelist));
+  let whitelistEmpty = whitelist.length === 0;
+  is(useMono || whitelistEmpty, fontUsed("mono") === fonts.mono,
+     "Correct mono whitelisting state; got " + fontUsed("mono") + ", requested " + fonts.mono);
+  is(useSans || whitelistEmpty, fontUsed("sans") === fonts.sans,
+     "Correct sans whitelisting state; got " + fontUsed("sans") + ", requested " + fonts.sans);
+  is(useSerif || whitelistEmpty, fontUsed("serif") === fonts.serif,
+     "Correct serif whitelisting state; got " + fontUsed("serif") + ", requested " + fonts.serif);
+}
+
+// Run tests to confirm that only whitelisting fonts are present in a
+// rendered page. Try turning mono, sans, and serif off and on in
+// every combination.
+add_task(function* () {
+  for (let useMono of [false, true]) {
+    for (let useSans of [false, true]) {
+      for (let useSerif of [false, true]) {
+        yield testFontWhitelist(useMono, useSans, useSerif);
+      }
+    }
+  }
+});
+
+</script>
+</body>
+</html>
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -734,17 +734,17 @@ gfxDWriteFontList::gfxDWriteFontList()
 {
 }
 
 // bug 602792 - CJK systems default to large CJK fonts which cause excessive
 //   I/O strain during cold startup due to dwrite caching bugs.  Default to
 //   Arial to avoid this.
 
 gfxFontFamily *
-gfxDWriteFontList::GetDefaultFont(const gfxFontStyle *aStyle)
+gfxDWriteFontList::GetDefaultFontForPlatform(const gfxFontStyle *aStyle)
 {
     nsAutoString resolvedName;
 
     // try Arial first
     gfxFontFamily *ff;
     if ((ff = FindFamily(NS_LITERAL_STRING("Arial")))) {
         return ff;
     }
@@ -850,17 +850,17 @@ gfxDWriteFontList::MakePlatformFont(cons
 
 enum DWriteInitError {
     errGDIInterop = 1,
     errSystemFontCollection = 2,
     errNoFonts = 3
 };
 
 nsresult
-gfxDWriteFontList::InitFontList()
+gfxDWriteFontList::InitFontListForPlatform()
 {
     LARGE_INTEGER frequency;          // ticks per second
     LARGE_INTEGER t1, t2, t3, t4, t5; // ticks
     double elapsedTime, upTime;
     char nowTime[256], nowDate[256];
 
     if (LOG_FONTINIT_ENABLED()) {
         GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT,
@@ -871,18 +871,16 @@ gfxDWriteFontList::InitFontList()
     QueryPerformanceFrequency(&frequency);
     QueryPerformanceCounter(&t1); // start
 
     HRESULT hr;
     mGDIFontTableAccess =
         Preferences::GetBool("gfx.font_rendering.directwrite.use_gdi_table_loading",
                              false);
 
-    gfxPlatformFontList::InitFontList();
-
     mFontSubstitutes.Clear();
     mNonExistingFonts.Clear();
 
     hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
         GetGdiInterop(getter_AddRefs(mGDIInterop));
     if (FAILED(hr)) {
         Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
                               uint32_t(errGDIInterop));
@@ -1404,17 +1402,18 @@ IFACEMETHODIMP DWriteFontFallbackRendere
 
 gfxFontEntry*
 gfxDWriteFontList::GlobalFontFallback(const uint32_t aCh,
                                       Script aRunScript,
                                       const gfxFontStyle* aMatchStyle,
                                       uint32_t& aCmapCount,
                                       gfxFontFamily** aMatchedFamily)
 {
-    bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+    bool useCmaps = IsFontFamilyWhitelistActive() ||
+                    gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
 
     if (useCmaps) {
         return gfxPlatformFontList::GlobalFontFallback(aCh,
                                                        aRunScript,
                                                        aMatchStyle,
                                                        aCmapCount,
                                                        aMatchedFamily);
     }
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -352,19 +352,17 @@ class gfxDWriteFontList : public gfxPlat
 public:
     gfxDWriteFontList();
 
     static gfxDWriteFontList* PlatformFontList() {
         return static_cast<gfxDWriteFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
-    virtual nsresult InitFontList();
-
-    virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
+    virtual nsresult InitFontListForPlatform() override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
                                           uint8_t aStyle);
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            uint16_t aWeight,
@@ -386,16 +384,20 @@ public:
 
     gfxFloat GetForceGDIClassicMaxFontSize() { return mForceGDIClassicMaxFontSize; }
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
+protected:
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
 private:
     friend class gfxDWriteFontFamily;
 
     nsresult GetFontSubstitutes();
 
     void GetDirectWriteSubstitutes();
 
     // search fonts system-wide for a given character, null otherwise
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1048,27 +1048,27 @@ gfxFT2FontList::AddFaceToList(const nsCS
 {
     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 =
+    RefPtr<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 = fontFamilies.GetWeak(name);
+        RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
         if (!family) {
             family = new FT2FontFamily(name);
             fontFamilies.Put(name, family);
             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
                 family->SetSkipSpaceFeatureCheck(true);
             }
             if (mBadUnderlineFamilyNames.Contains(name)) {
                 family->SetBadUnderlineFamily();
@@ -1363,17 +1363,17 @@ gfxFT2FontList::AppendFaceFromFontListEn
                                             StandardFile aStdFile)
 {
     FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
     if (fe) {
         auto& fontFamilies =
             aFLE.isHidden() ? mHiddenFontFamilies : mFontFamilies;
         fe->mStandardFace = (aStdFile == kStandard);
         nsAutoString name(aFLE.familyName());
-        gfxFontFamily *family = fontFamilies.GetWeak(name);
+        RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
         if (!family) {
             family = new FT2FontFamily(name);
             fontFamilies.Put(name, family);
             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
                 family->SetSkipSpaceFeatureCheck(true);
             }
             if (mBadUnderlineFamilyNames.Contains(name)) {
                 family->SetBadUnderlineFamily();
@@ -1465,20 +1465,19 @@ PreloadAsUserFontFaces(nsStringHashKey::
 
         // Stash it persistently in the user-font cache.
         gfxUserFontSet::UserFontCache::CacheFont(
             fe, gfxUserFontSet::UserFontCache::kPersistent);
     }
 }
 
 nsresult
-gfxFT2FontList::InitFontList()
+gfxFT2FontList::InitFontListForPlatform()
 {
-    // reset font lists
-    gfxPlatformFontList::InitFontList();
+    // reset hidden font list
     mHiddenFontFamilies.Clear();
 
     LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
 
     FindFonts();
 
     for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         nsStringHashKey::KeyType key = iter.Key();
@@ -1557,17 +1556,17 @@ searchDone:
         fe->mStretch = aStretch;
         fe->mIsLocalUserFont = true;
     }
 
     return fe;
 }
 
 gfxFontFamily*
-gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle)
+gfxFT2FontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     gfxFontFamily *ff = nullptr;
 #ifdef MOZ_WIDGET_GONK
     ff = FindFamily(NS_LITERAL_STRING("Fira Sans"));
 #elif defined(MOZ_WIDGET_ANDROID)
     ff = FindFamily(NS_LITERAL_STRING("Roboto"));
     if (!ff) {
         ff = FindFamily(NS_LITERAL_STRING("Droid Sans"));
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -116,18 +116,16 @@ public:
 };
 
 class gfxFT2FontList : public gfxPlatformFontList
 {
 public:
     gfxFT2FontList();
     virtual ~gfxFT2FontList();
 
-    virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
-
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
                                           uint8_t aStyle);
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            uint16_t aWeight,
                                            int16_t aStretch,
@@ -146,17 +144,18 @@ public:
     void WillShutdown();
 
 protected:
     typedef enum {
         kUnknown,
         kStandard
     } StandardFile;
 
-    virtual nsresult InitFontList();
+    // initialize font lists
+    virtual nsresult InitFontListForPlatform() override;
 
     void AppendFaceFromFontListEntry(const FontListEntry& aFLE,
                                      StandardFile aStdFile);
 
     void AppendFacesFromFontFile(const nsCString& aFileName,
                                  FontNameCache *aCache,
                                  StandardFile aStdFile,
                                  FT2FontFamily::Visibility aVisibility);
@@ -180,16 +179,19 @@ protected:
 
     void FindFonts();
 
     void FindFontsInOmnijar(FontNameCache *aCache);
 
     void FindFontsInDir(const nsCString& aDir, FontNameCache* aFNC,
                         FT2FontFamily::Visibility aVisibility);
 
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
     nsTHashtable<nsStringHashKey> mSkipSpaceLookupCheckFamilies;
 
 private:
     FontFamilyTable mHiddenFontFamilies;
 
     mozilla::UniquePtr<FontNameCache> mFontNameCache;
     int64_t mJarModifiedTime;
     nsCOMPtr<nsIObserver> mObserver;
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -980,17 +980,17 @@ gfxFcPlatformFontList::AddFontSetFamilie
     // Chrome Skia/Webkit code does also.
 
     if (!aFontSet) {
         NS_WARNING("AddFontSetFamilies called with a null font set.");
         return;
     }
 
     FcChar8* lastFamilyName = (FcChar8*)"";
-    gfxFontconfigFontFamily* fontFamily = nullptr;
+    RefPtr<gfxFontconfigFontFamily> fontFamily;
     nsAutoString familyName;
     for (int f = 0; f < aFontSet->nfont; f++) {
         FcPattern* font = aFontSet->fonts[f];
 
         // not scalable? skip...
         FcBool scalable;
         if (FcPatternGetBool(font, FC_SCALABLE, 0, &scalable) != FcResultMatch ||
             !scalable) {
@@ -1056,23 +1056,20 @@ gfxFcPlatformFontList::AddFontSetFamilie
         if (!fullname.IsEmpty()) {
             ToLowerCase(fullname);
             mLocalNames.Put(fullname, font);
         }
     }
 }
 
 nsresult
-gfxFcPlatformFontList::InitFontList()
+gfxFcPlatformFontList::InitFontListForPlatform()
 {
     mLastConfig = FcConfigGetCurrent();
 
-    // reset font lists
-    gfxPlatformFontList::InitFontList();
-
     mLocalNames.Clear();
     mFcSubstituteCache.Clear();
 
     // iterate over available fonts
     FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
     AddFontSetFamilies(systemFonts, /* aAppFonts = */ false);
     mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
 
@@ -1177,17 +1174,17 @@ gfxFcPlatformFontList::GetFontList(nsIAt
         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
     if (sansSerif)
         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
     if (serif)
         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
 }
 
 gfxFontFamily*
-gfxFcPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+gfxFcPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     // Get the default font by using a fake name to retrieve the first
     // scalable font that fontconfig suggests for the given language.
     PrefFontList* prefFonts =
         FindGenericFamilies(NS_LITERAL_STRING("-moz-default"), aStyle->language);
     NS_ASSERTION(prefFonts, "null list of generic fonts");
     if (prefFonts && !prefFonts->IsEmpty()) {
         return (*prefFonts)[0];
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -213,26 +213,23 @@ class gfxFcPlatformFontList : public gfx
 public:
     gfxFcPlatformFontList();
 
     static gfxFcPlatformFontList* PlatformFontList() {
         return static_cast<gfxFcPlatformFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
-    nsresult InitFontList() override;
+    virtual nsresult InitFontListForPlatform() override;
 
     void GetFontList(nsIAtom *aLangGroup,
                      const nsACString& aGenericFamily,
                      nsTArray<nsString>& aListOfFonts) override;
 
 
-    gfxFontFamily*
-    GetDefaultFont(const gfxFontStyle* aStyle) override;
-
     gfxFontEntry*
     LookupLocalFont(const nsAString& aFontName, uint16_t aWeight,
                     int16_t aStretch, uint8_t aStyle) override;
 
     gfxFontEntry*
     MakePlatformFont(const nsAString& aFontName, uint16_t aWeight,
                      int16_t aStretch,
                      uint8_t aStyle,
@@ -275,16 +272,19 @@ protected:
     PrefFontList* FindGenericFamilies(const nsAString& aGeneric,
                                       nsIAtom* aLanguage);
 
     // are all pref font settings set to use fontconfig generics?
     bool PrefFontListsUseOnlyGenerics();
 
     static void CheckFontUpdates(nsITimer *aTimer, void *aThis);
 
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
 #ifdef MOZ_BUNDLED_FONTS
     void ActivateBundledFonts();
     nsCString mBundledFontsPath;
     bool mBundledFontsInitialized;
 #endif
 
     // to avoid enumerating all fonts, maintain a mapping of local font
     // names to family
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -647,23 +647,20 @@ gfxGDIFontList::GetFontSubstitutes()
         if (ff) {
             mFontSubstitutes.Put(substituteName, ff);
         }
     }
     return NS_OK;
 }
 
 nsresult
-gfxGDIFontList::InitFontList()
+gfxGDIFontList::InitFontListForPlatform()
 {
     Telemetry::AutoTimer<Telemetry::GDI_INITFONTLIST_TOTAL> timer;
 
-    // reset font lists
-    gfxPlatformFontList::InitFontList();
-    
     mFontSubstitutes.Clear();
     mNonExistingFonts.Clear();
 
     // iterate over available families
     LOGFONTW logfont;
     memset(&logfont, 0, sizeof(logfont));
     logfont.lfCharSet = DEFAULT_CHARSET;
 
@@ -915,17 +912,17 @@ gfxGDIFontList::FindAndAddFamilies(const
         return false;
     }
 
     return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
                                                    aDevToCssSize);
 }
 
 gfxFontFamily*
-gfxGDIFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+gfxGDIFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     gfxFontFamily *ff = nullptr;
 
     // this really shouldn't fail to find a font....
     NONCLIENTMETRICSW ncm;
     ncm.cbSize = sizeof(ncm);
     BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 
                                           sizeof(ncm), &ncm, 0);
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -296,19 +296,17 @@ private:
 
 class gfxGDIFontList : public gfxPlatformFontList {
 public:
     static gfxGDIFontList* PlatformFontList() {
         return static_cast<gfxGDIFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
-    virtual nsresult InitFontList();
-
-    virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
+    virtual nsresult InitFontListForPlatform() override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
@@ -322,16 +320,20 @@ public:
                                            const uint8_t* aFontData,
                                            uint32_t aLength);
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
+protected:
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
 private:
     friend class gfxWindowsPlatform;
 
     gfxGDIFontList();
 
     nsresult GetFontSubstitutes();
 
     static int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXW *lpelfe,
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -77,18 +77,16 @@ protected:
 class gfxMacPlatformFontList : public gfxPlatformFontList {
 public:
     static gfxMacPlatformFontList* PlatformFontList() {
         return static_cast<gfxMacPlatformFontList*>(sPlatformFontList);
     }
 
     static int32_t AppleWeightToCSSWeight(int32_t aAppleWeight);
 
-    gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle) override;
-
     bool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) override;
 
     gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                   uint16_t aWeight,
                                   int16_t aStretch,
                                   uint8_t aStyle) override;
 
     gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
@@ -105,24 +103,28 @@ public:
 
     // lookup the system font for a particular system font type and set
     // the name and style characteristics
     void LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,
                           nsAString& aSystemFontName,
                           gfxFontStyle &aFontStyle,
                           float aDevPixPerCSSPixel);
 
+protected:
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
 private:
     friend class gfxPlatformMac;
 
     gfxMacPlatformFontList();
     virtual ~gfxMacPlatformFontList();
 
     // initialize font lists
-    nsresult InitFontList() override;
+    virtual nsresult InitFontListForPlatform() override;
 
     // special case font faces treated as font families (set via prefs)
     void InitSingleFaceList();
 
     // initialize system fonts
     void InitSystemFontNames();
 
     // helper function to lookup in both hidden system fonts and normal fonts
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -698,34 +698,33 @@ gfxMacPlatformFontList::AddFamily(CFStri
     if (hiddenSystemFont && mUseSizeSensitiveSystemFont &&
         mSystemDisplayFontFamilyName.Equals(familyName)) {
         sizeHint = 128.0;
     }
 
     nsAutoString key;
     ToLowerCase(familyName, key);
 
-    gfxFontFamily* familyEntry = new gfxMacFontFamily(familyName, sizeHint);
+    RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(familyName, sizeHint);
     table.Put(key, familyEntry);
 
     // check the bad underline blacklist
     if (mBadUnderlineFamilyNames.Contains(key)) {
         familyEntry->SetBadUnderlineFamily();
     }
 }
 
 nsresult
-gfxMacPlatformFontList::InitFontList()
+gfxMacPlatformFontList::InitFontListForPlatform()
 {
     nsAutoreleasePool localPool;
 
     Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
 
-    // reset font lists
-    gfxPlatformFontList::InitFontList();
+    // reset system font list
     mSystemFontFamilies.Clear();
     
     // iterate over available families
 
     InitSystemFontNames();
 
     CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
 
@@ -775,17 +774,17 @@ gfxMacPlatformFontList::InitSingleFaceLi
             nsAutoString key;
             GenerateFontListKey(familyName, key);
             LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
                           NS_ConvertUTF16toUTF8(familyName).get(),
                           NS_ConvertUTF16toUTF8(key).get()));
 
             // add only if doesn't exist already
             if (!mFontFamilies.GetWeak(key)) {
-                gfxFontFamily *familyEntry =
+                RefPtr<gfxFontFamily> familyEntry =
                     new gfxSingleFaceMacFontFamily(familyName);
                 // LookupLocalFont sets this, need to clear
                 fontEntry->mIsLocalUserFont = false;
                 familyEntry->AddFontEntry(fontEntry);
                 familyEntry->SetHasStyles(true);
                 mFontFamilies.Put(key, familyEntry);
                 LOG_FONTLIST(("(fontlist-singleface) added new family\n",
                               NS_ConvertUTF16toUTF8(familyName).get(),
@@ -922,17 +921,18 @@ gfxMacPlatformFontList::RegisteredFontsC
 
 gfxFontEntry*
 gfxMacPlatformFontList::GlobalFontFallback(const uint32_t aCh,
                                            Script aRunScript,
                                            const gfxFontStyle* aMatchStyle,
                                            uint32_t& aCmapCount,
                                            gfxFontFamily** aMatchedFamily)
 {
-    bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+    bool useCmaps = IsFontFamilyWhitelistActive() ||
+                    gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
 
     if (useCmaps) {
         return gfxPlatformFontList::GlobalFontFallback(aCh,
                                                        aRunScript,
                                                        aMatchStyle,
                                                        aCmapCount,
                                                        aMatchedFamily);
     }
@@ -1012,17 +1012,17 @@ gfxMacPlatformFontList::GlobalFontFallba
     }
 
     ::CFRelease(str);
 
     return fontEntry;
 }
 
 gfxFontFamily*
-gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+gfxMacPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     nsAutoreleasePool localPool;
 
     NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
     nsAutoString familyName;
 
     GetStringForNSString(defaultFamily, familyName);
     return FindFamily(familyName);
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -95,16 +95,18 @@ const gfxFontEntry::ScriptRange gfxPlatf
 
 static const char* kObservedPrefs[] = {
     "font.",
     "font.name-list.",
     "intl.accept_languages",  // hmmmm...
     nullptr
 };
 
+static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
+
 // xxx - this can probably be eliminated by reworking pref font handling code
 static const char *gPrefLangNames[] = {
     #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
     #include "gfxFontPrefLangList.h"
     #undef FONT_PREF_LANG
 };
 
 static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
@@ -168,17 +170,18 @@ gfxPlatformFontList::MemoryReporter::Col
     }
 
     return NS_OK;
 }
 
 gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
     : mFontFamilies(64), mOtherFamilyNames(16),
       mBadUnderlineFamilyNames(8), mSharedCmaps(8),
-      mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0)
+      mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0),
+      mFontFamilyWhitelistActive(false)
 {
     mOtherFamilyNamesInitialized = false;
 
     if (aNeedFullnamePostscriptNames) {
         mExtraNames = MakeUnique<ExtraNames>();
     }
     mFaceNameListsInitialized = false;
 
@@ -186,31 +189,65 @@ gfxPlatformFontList::gfxPlatformFontList
 
     // pref changes notification setup
     NS_ASSERTION(!gFontListPrefObserver,
                  "There has been font list pref observer already");
     gFontListPrefObserver = new gfxFontListPrefObserver();
     NS_ADDREF(gFontListPrefObserver);
     Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
 
+    Preferences::RegisterCallback(FontWhitelistPrefChanged,
+                                  kFontSystemWhitelistPref);
+
     RegisterStrongMemoryReporter(new MemoryReporter());
 }
 
 gfxPlatformFontList::~gfxPlatformFontList()
 {
     mSharedCmaps.Clear();
     ClearLangGroupPrefFonts();
     NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
     Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
+    Preferences::UnregisterCallback(FontWhitelistPrefChanged,
+                                    kFontSystemWhitelistPref);
     NS_RELEASE(gFontListPrefObserver);
 }
 
 // number of CSS generic font families
 const uint32_t kNumGenerics = 5;
 
+void
+gfxPlatformFontList::ApplyWhitelist()
+{
+    nsTArray<nsString> list;
+    gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, list);
+    uint32_t numFonts = list.Length();
+    mFontFamilyWhitelistActive = (numFonts > 0);
+    if (!mFontFamilyWhitelistActive) {
+        return;
+    }
+    nsTHashtable<nsStringHashKey> familyNamesWhitelist;
+    for (uint32_t i = 0; i < numFonts; i++) {
+        nsString key;
+        ToLowerCase(list[i], key);
+        familyNamesWhitelist.PutEntry(key);
+    }
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        // Don't continue if we only have one font left.
+        if (mFontFamilies.Count() == 1) {
+            break;
+        }
+        nsString fontFamilyName(iter.Key());
+        ToLowerCase(fontFamilyName);
+        if (!familyNamesWhitelist.Contains(fontFamilyName)) {
+            iter.Remove();
+        }
+    }
+}
+
 nsresult
 gfxPlatformFontList::InitFontList()
 {
     mFontlistInitCount++;
 
     if (LOG_FONTINIT_ENABLED()) {
         LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
     }
@@ -238,16 +275,22 @@ gfxPlatformFontList::InitFontList()
 
     // 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
 
     sPlatformFontList = this;
 
+    nsresult rv = InitFontListForPlatform();
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+
+    ApplyWhitelist();
     return NS_OK;
 }
 
 void
 gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
 {
     aResult = aKeyName;
     ToLowerCase(aResult);
@@ -1142,16 +1185,31 @@ gfxPlatformFontList::GetDefaultGeneric(e
     }
 
     if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
         return mDefaultGenericsLangGroup[uint32_t(aLang)];
     }
     return eFamily_serif;
 }
 
+
+gfxFontFamily*
+gfxPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+{
+    gfxFontFamily* family = GetDefaultFontForPlatform(aStyle);
+    if (family) {
+        return family;
+    }
+    // Something has gone wrong and we were unable to retrieve a default font
+    // from the platform. (Likely the whitelist has blocked all potential
+    // default fonts.) As a last resort, we return the first font listed in
+    // mFontFamilies.
+    return mFontFamilies.Iter().Data();
+}
+
 void
 gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
 {
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         RefPtr<gfxFontFamily>& family = iter.Data();
         aFontFamilyNames.AppendElement(family->Name());
     }
 }
@@ -1593,10 +1651,16 @@ gfxPlatformFontList::AddSizeOfExcludingT
 void
 gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
                                             FontListSizes* aSizes) const
 {
     aSizes->mFontListSize += aMallocSizeOf(this);
     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
 }
 
+bool
+gfxPlatformFontList::IsFontFamilyWhitelistActive()
+{
+    return mFontFamilyWhitelistActive;
+}
+
 #undef LOG
 #undef LOG_ENABLED
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -109,17 +109,17 @@ public:
     static void Shutdown() {
         delete sPlatformFontList;
         sPlatformFontList = nullptr;
     }
 
     virtual ~gfxPlatformFontList();
 
     // initialize font lists
-    virtual nsresult InitFontList();
+    nsresult InitFontList();
 
     virtual void GetFontList(nsIAtom *aLangGroup,
                              const nsACString& aGenericFamily,
                              nsTArray<nsString>& aListOfFonts);
 
     void UpdateFontList();
 
     virtual void ClearLangGroupPrefFonts();
@@ -150,17 +150,17 @@ public:
 
     void AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName);
 
     bool NeedFullnamePostscriptNames() { return mExtraNames != nullptr; }
 
     // pure virtual functions, to be provided by concrete subclasses
 
     // get the system default font family
-    virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle) = 0;
+    gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
 
     // look up a font by name on the host platform
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
                                           uint8_t aStyle) = 0;
 
     // create a new platform font from downloaded data (@font-face)
@@ -243,16 +243,23 @@ public:
     // default serif/sans-serif choice based on font.default.xxx prefs
     mozilla::FontFamilyType
     GetDefaultGeneric(eFontPrefLang aLang);
 
     // map lang group ==> lang string
     void GetSampleLangForGroup(nsIAtom* aLanguage, nsACString& aLangStr,
                                bool aCheckEnvironment = true);
 
+    // Returns true if the font family whitelist is not empty.
+    bool IsFontFamilyWhitelistActive();
+
+    static void FontWhitelistPrefChanged(const char *aPref, void *aClosure) {
+        gfxPlatformFontList::PlatformFontList()->UpdateFontList();
+    }
+
 protected:
     class MemoryReporter final : public nsIMemoryReporter
     {
         ~MemoryReporter() {}
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIMEMORYREPORTER
     };
@@ -356,27 +363,35 @@ protected:
 
     void RebuildLocalFonts();
 
     void
     ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
                             eFontPrefLang aPrefLang,
                             nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies);
 
+    virtual nsresult InitFontListForPlatform() = 0;
+
+    void ApplyWhitelist();
+
     typedef nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> FontFamilyTable;
     typedef nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> FontEntryTable;
 
     // used by memory reporter to accumulate sizes of family names in the table
     static size_t
     SizeOfFontFamilyTableExcludingThis(const FontFamilyTable& aTable,
                                        mozilla::MallocSizeOf aMallocSizeOf);
     static size_t
     SizeOfFontEntryTableExcludingThis(const FontEntryTable& aTable,
                                       mozilla::MallocSizeOf aMallocSizeOf);
 
+    // Platform-specific helper for GetDefaultFont(...).
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) = 0;
+
     // canonical family name ==> family entry (unique, one name per family entry)
     FontFamilyTable mFontFamilies;
 
     // other family name ==> family entry (not unique, can have multiple names per
     // family entry, only names *other* than the canonical names are stored here)
     FontFamilyTable mOtherFamilyNames;
 
     // flag set after InitOtherFamilyNames is called upon first name lookup miss
@@ -432,11 +447,13 @@ protected:
     // see bugs 636957, 1070983, 1189129
     uint32_t mFontlistInitCount; // num times InitFontList called
 
     nsTHashtable<nsPtrHashKey<gfxUserFontSet> > mUserFontSetList;
 
     nsCOMPtr<nsILanguageAtomService> mLangService;
     nsTArray<uint32_t> mCJKPrefLangs;
     nsTArray<mozilla::FontFamilyType> mDefaultGenericsLangGroup;
+
+    bool mFontFamilyWhitelistActive;
 };
 
 #endif /* GFXPLATFORMFONTLIST_H_ */
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -417,17 +417,20 @@ gfxUserFontEntry::LoadNextSrc()
     // load each src entry in turn, until a local face is found
     // or a download begins successfully
     while (mSrcIndex < numSrc) {
         gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
 
         // src local ==> lookup and load immediately
 
         if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
-            gfxFontEntry* fe =
+            // Don't look up local fonts if the font whitelist is being used.
+            gfxFontEntry* fe = gfxPlatformFontList::PlatformFontList()->
+                                 IsFontFamilyWhitelistActive() ?
+                nullptr :
                 gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
                                                             mWeight,
                                                             mStretch,
                                                             mStyle);
             nsTArray<gfxUserFontSet*> fontSets;
             GetUserFontSets(fontSets);
             for (gfxUserFontSet* fontSet : fontSets) {
                 // We need to note on each gfxUserFontSet that contains the user