Bug 1464400 - Keep track of CSS generics when resolving to actual font families and faces, and expose as a new CSSGeneric attribute on InspectorFontFace. r=jwatt
authorJonathan Kew <jkew@mozilla.com>
Fri, 25 May 2018 14:07:57 +0100
changeset 420924 9eabcc7a5eae3a16644bf06b5b0211e093251d66
parent 420923 f63e09b914570efce8e047f4f51aaf58ead46f75
child 420925 cbf0895981cdabbe208a3569f24a86091981257e
push id34083
push userapavel@mozilla.com
push dateSat, 02 Jun 2018 23:03:25 +0000
treeherdermozilla-central@1f62ecdf59b6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1464400
milestone62.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 1464400 - Keep track of CSS generics when resolving to actual font families and faces, and expose as a new CSSGeneric attribute on InspectorFontFace. r=jwatt
dom/chrome-webidl/InspectorUtils.webidl
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxDWriteFontList.h
gfx/thebes/gfxFcPlatformFontList.cpp
gfx/thebes/gfxFcPlatformFontList.h
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontEntry.h
gfx/thebes/gfxFontFamilyList.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/gfxTextRun.cpp
gfx/thebes/gfxTextRun.h
layout/inspector/InspectorFontFace.cpp
layout/inspector/InspectorFontFace.h
layout/inspector/tests/chrome/chrome.ini
layout/inspector/tests/chrome/test_fontFaceGeneric.xul
layout/style/ServoBindings.h
--- a/dom/chrome-webidl/InspectorUtils.webidl
+++ b/dom/chrome-webidl/InspectorUtils.webidl
@@ -129,16 +129,18 @@ interface InspectorFontFace {
   readonly attribute boolean fromLanguagePrefs;
   readonly attribute boolean fromSystemFallback;
 
   // available for all fonts
   readonly attribute DOMString name; // full font name as obtained from the font resource
   readonly attribute DOMString CSSFamilyName; // a family name that could be used in CSS font-family
                                               // (not necessarily the actual name that was used,
                                               // due to aliases, generics, localized names, etc)
+  readonly attribute DOMString CSSGeneric; // CSS generic (serif, sans-serif, etc) that was mapped
+                                           // to this font, if any (frequently empty!)
 
   [NewObject,Throws] sequence<InspectorVariationAxis> getVariationAxes();
   [NewObject,Throws] sequence<InspectorVariationInstance> getVariationInstances();
   [NewObject,Throws] sequence<InspectorFontFeature> getFeatures();
 
   // A list of Ranges of text rendered with this face.
   // This will list the first /maxRanges/ ranges found when InspectorUtils.getUsedFontFaces
   // was called (so it will be empty unless a non-zero maxRanges argument was passed).
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -1421,27 +1421,27 @@ gfxDWriteFontList::GetStandardFamilyName
         return true;
     }
 
     return false;
 }
 
 bool
 gfxDWriteFontList::FindAndAddFamilies(const nsAString& aFamily,
-                                      nsTArray<gfxFontFamily*>* aOutput,
+                                      nsTArray<FamilyAndGeneric>* aOutput,
                                       FindFamiliesFlags aFlags,
                                       gfxFontStyle* aStyle,
                                       gfxFloat aDevToCssSize)
 {
     nsAutoString keyName(aFamily);
     BuildKeyNameFromFontName(keyName);
 
     gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
     if (ff) {
-        aOutput->AppendElement(ff);
+        aOutput->AppendElement(FamilyAndGeneric(ff));
         return true;
     }
 
     if (mNonExistingFonts.Contains(keyName)) {
         return false;
     }
 
     return gfxPlatformFontList::FindAndAddFamilies(aFamily,
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -422,17 +422,17 @@ public:
     
     bool GetStandardFamilyName(const nsAString& aFontName,
                                  nsAString& aFamilyName);
 
     IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
     bool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
 
     bool FindAndAddFamilies(const nsAString& aFamily,
-                            nsTArray<gfxFontFamily*>* aOutput,
+                            nsTArray<FamilyAndGeneric>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     gfxFloat GetForceGDIClassicMaxFontSize() { return mForceGDIClassicMaxFontSize; }
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -1888,17 +1888,17 @@ gfxFcPlatformFontList::MakePlatformFont(
                                       aWeightForEntry,
                                       aStretchForEntry,
                                       aStyleForEntry,
                                       aFontData, aLength, face);
 }
 
 bool
 gfxFcPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
-                                          nsTArray<gfxFontFamily*>* aOutput,
+                                          nsTArray<FamilyAndGeneric>* aOutput,
                                           FindFamiliesFlags aFlags,
                                           gfxFontStyle* aStyle,
                                           gfxFloat aDevToCssSize)
 {
     nsAutoString familyName(aFamily);
     ToLowerCase(familyName);
     nsAtom* language = (aStyle ? aStyle->language.get() : nullptr);
 
@@ -1936,17 +1936,17 @@ gfxFcPlatformFontList::FindAndAddFamilie
     //   Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu Serif
     //
     // In this case fontconfig is including Tex Gyre Heros and
     // Nimbus Sans L as alternatives for Helvetica.
 
     // Because the FcConfigSubstitute call is quite expensive, we cache the
     // actual font families found via this process. So check the cache first:
     NS_ConvertUTF16toUTF8 familyToFind(familyName);
-    AutoTArray<gfxFontFamily*,10> cachedFamilies;
+    AutoTArray<FamilyAndGeneric,10> cachedFamilies;
     if (mFcSubstituteCache.Get(familyToFind, &cachedFamilies)) {
         if (cachedFamilies.IsEmpty()) {
             return false;
         }
         aOutput->AppendElements(cachedFamilies);
         return true;
     }
 
@@ -2085,17 +2085,17 @@ gfxFcPlatformFontList::GetStandardFamily
 
     // didn't find localized name, leave family name blank
     return true;
 }
 
 void
 gfxFcPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
                                        nsAtom* aLanguage,
-                                       nsTArray<gfxFontFamily*>& aFamilyList)
+                                       nsTArray<FamilyAndGeneric>& aFamilyList)
 {
     bool usePrefFontList = false;
 
     // treat -moz-fixed as monospace
     if (aGenericType == eFamily_moz_fixed) {
         aGenericType = eFamily_monospace;
     }
 
@@ -2142,17 +2142,20 @@ gfxFcPlatformFontList::AddGenericFonts(m
     if (usePrefFontList) {
         return gfxPlatformFontList::AddGenericFonts(aGenericType,
                                                     aLanguage,
                                                     aFamilyList);
     }
 
     PrefFontList* prefFonts = FindGenericFamilies(genericToLookup, aLanguage);
     NS_ASSERTION(prefFonts, "null generic font list");
-    aFamilyList.AppendElements(*prefFonts);
+    aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
+    for (auto& f : *prefFonts) {
+        aFamilyList.AppendElement(FamilyAndGeneric(f.get(), aGenericType));
+    }
 }
 
 void
 gfxFcPlatformFontList::ClearLangGroupPrefFonts()
 {
     ClearGenericMappings();
     gfxPlatformFontList::ClearLangGroupPrefFonts();
     mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
@@ -2253,24 +2256,24 @@ gfxFcPlatformFontList::FindGenericFamili
     bool foundFontWithLang = false;
     for (int i = 0; i < faces->nfont; i++) {
         FcPattern* font = faces->fonts[i];
         FcChar8* mappedGeneric = nullptr;
 
         FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
         if (mappedGeneric) {
             NS_ConvertUTF8toUTF16 mappedGenericName(ToCharPtr(mappedGeneric));
-            AutoTArray<gfxFontFamily*,1> genericFamilies;
+            AutoTArray<FamilyAndGeneric,1> genericFamilies;
             if (gfxPlatformFontList::FindAndAddFamilies(mappedGenericName,
                                                         &genericFamilies,
                                                         FindFamiliesFlags(0))) {
                 MOZ_ASSERT(genericFamilies.Length() == 1,
                            "expected a single family");
-                if (!prefFonts->Contains(genericFamilies[0])) {
-                    prefFonts->AppendElement(genericFamilies[0]);
+                if (!prefFonts->Contains(genericFamilies[0].mFamily)) {
+                    prefFonts->AppendElement(genericFamilies[0].mFamily);
                     bool foundLang =
                         !fcLang.IsEmpty() &&
                         PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
                     foundFontWithLang = foundFontWithLang || foundLang;
                     // check to see if the list is full
                     if (prefFonts->Length() >= limit) {
                         break;
                     }
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -300,30 +300,30 @@ public:
     MakePlatformFont(const nsAString& aFontName,
                      WeightRange aWeightForEntry,
                      StretchRange aStretchForEntry,
                      SlantStyleRange aStyleForEntry,
                      const uint8_t* aFontData,
                      uint32_t aLength) override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
-                            nsTArray<gfxFontFamily*>* aOutput,
+                            nsTArray<FamilyAndGeneric>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     bool GetStandardFamilyName(const nsAString& aFontName,
                                nsAString& aFamilyName) override;
 
     FcConfig* GetLastConfig() const { return mLastConfig; }
 
     // override to use fontconfig lookup for generics
     void AddGenericFonts(mozilla::FontFamilyType aGenericType,
                          nsAtom* aLanguage,
-                         nsTArray<gfxFontFamily*>& aFamilyList) override;
+                         nsTArray<FamilyAndGeneric>& aFamilyList) override;
 
     void ClearLangGroupPrefFonts() override;
 
     // clear out cached generic-lang ==> family-list mappings
     void ClearGenericMappings() {
         mGenericMappings.Clear();
     }
 
@@ -394,17 +394,17 @@ protected:
 
     // Caching family lookups as found by FindAndAddFamilies after resolving
     // substitutions. The gfxFontFamily objects cached here are owned by the
     // gfxFcPlatformFontList via its mFamilies table; note that if the main
     // font list is rebuilt (e.g. due to a fontconfig configuration change),
     // these pointers will be invalidated. InitFontList() flushes the cache
     // in this case.
     nsDataHashtable<nsCStringHashKey,
-                    nsTArray<gfxFontFamily*>> mFcSubstituteCache;
+                    nsTArray<FamilyAndGeneric>> mFcSubstituteCache;
 
     nsCOMPtr<nsITimer> mCheckFontUpdatesTimer;
     nsCountedRef<FcConfig> mLastConfig;
 
     // By default, font prefs under Linux are set to simply lookup
     // via fontconfig the appropriate font for serif/sans-serif/monospace.
     // Rather than check each time a font pref is used, check them all at startup
     // and set a boolean to flag the case that non-default user font prefs exist
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -626,22 +626,27 @@ public:
     };
 
 protected:
     // Protected destructor, to discourage deletion outside of Release():
     virtual ~gfxTextRunFactory();
 };
 
 struct gfxTextRange {
-    enum class MatchType : uint8_t {
+    enum class MatchType : uint16_t {
+        // The CSS generic that mapped to this font, if any. This field of
+        // the MatchType stores a FontFamilyType value as defined in the enum
+        // in gfxFontFamilyList.h.
+        kGenericMask    = 0x00ff,
+
         // Flags for recording the kind of font-matching that was used.
         // Note that multiple flags may be set on a single range.
-        kFontGroup      = 0x01,
-        kPrefsFallback  = 0x02,
-        kSystemFallback = 0x04
+        kFontGroup      = 0x0100,
+        kPrefsFallback  = 0x0200,
+        kSystemFallback = 0x0400
     };
     gfxTextRange(uint32_t aStart, uint32_t aEnd,
                  gfxFont* aFont, MatchType aMatchType,
                  mozilla::gfx::ShapedTextFlags aOrientation)
         : start(aStart),
           end(aEnd),
           font(aFont),
           matchType(aMatchType),
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -920,9 +920,34 @@ protected:
         kItalicFaceIndex     = 2,
         kBoldItalicFaceIndex = 3,
         // mask values for selecting face with bold and/or italic attributes
         kBoldMask   = 0x01,
         kItalicMask = 0x02
     };
 };
 
+// Struct used in the gfxFontGroup font list to keep track of a font family
+// together with the CSS generic (if any) that was mapped to it in this
+// particular case (so it can be reported to the DevTools font inspector).
+struct FamilyAndGeneric final {
+    FamilyAndGeneric()
+        : mFamily(nullptr)
+        , mGeneric(mozilla::FontFamilyType::eFamily_none)
+    {
+    }
+    FamilyAndGeneric(const FamilyAndGeneric& aOther)
+        : mFamily(aOther.mFamily)
+        , mGeneric(aOther.mGeneric)
+    {
+    }
+    explicit FamilyAndGeneric(gfxFontFamily* aFamily,
+                              mozilla::FontFamilyType aGeneric =
+                                  mozilla::FontFamilyType::eFamily_none)
+        : mFamily(aFamily)
+        , mGeneric(aGeneric)
+    {
+    }
+    gfxFontFamily* mFamily;
+    mozilla::FontFamilyType mGeneric;
+};
+
 #endif
--- a/gfx/thebes/gfxFontFamilyList.h
+++ b/gfx/thebes/gfxFontFamilyList.h
@@ -18,17 +18,17 @@
 namespace mozilla {
 
 /**
  * type of font family name, either a name (e.g. Helvetica) or a
  * generic (e.g. serif, sans-serif), with the ability to distinguish
  * between unquoted and quoted names for serializaiton
  */ 
 
-enum FontFamilyType : uint32_t {
+enum FontFamilyType : uint8_t {
   eFamily_none = 0,  // used when finding generics
 
   // explicitly named font family (e.g. Helvetica)
   eFamily_named,
   eFamily_named_quoted,
 
   // generics
   eFamily_serif,         // pref font code relies on this ordering!!!
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -877,27 +877,27 @@ gfxGDIFontList::MakePlatformFont(const n
       fe->mIsDataUserFont = true;
     }
 
     return fe;
 }
 
 bool
 gfxGDIFontList::FindAndAddFamilies(const nsAString& aFamily,
-                                   nsTArray<gfxFontFamily*>* aOutput,
+                                   nsTArray<FamilyAndGeneric>* aOutput,
                                    FindFamiliesFlags aFlags,
                                    gfxFontStyle* aStyle,
                                    gfxFloat aDevToCssSize)
 {
     nsAutoString keyName(aFamily);
     BuildKeyNameFromFontName(keyName);
 
     gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
     if (ff) {
-        aOutput->AppendElement(ff);
+        aOutput->AppendElement(FamilyAndGeneric(ff));
         return true;
     }
 
     if (mNonExistingFonts.Contains(keyName)) {
         return false;
     }
 
     return gfxPlatformFontList::FindAndAddFamilies(aFamily,
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -321,17 +321,17 @@ public:
     }
 
     // initialize font lists
     virtual nsresult InitFontListForPlatform() override;
 
     gfxFontFamily* CreateFontFamily(const nsAString& aName) const override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
-                            nsTArray<gfxFontFamily*>* aOutput,
+                            nsTArray<FamilyAndGeneric>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           WeightRange aWeightForEntry,
                                           StretchRange aStretchForEntry,
                                           SlantStyleRange aStyleForEntry);
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -151,17 +151,17 @@ public:
     gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                    WeightRange aWeightForEntry,
                                    StretchRange aStretchForEntry,
                                    SlantStyleRange aStyleForEntry,
                                    const uint8_t* aFontData,
                                    uint32_t aLength) override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
-                            nsTArray<gfxFontFamily*>* aOutput,
+                            nsTArray<FamilyAndGeneric>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     // 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,
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -1623,17 +1623,17 @@ gfxMacPlatformFontList::MakePlatformFont
 }
 
 // Webkit code uses a system font meta name, so mimic that here
 // WebCore/platform/graphics/mac/FontCacheMac.mm
 static const char kSystemFont_system[] = "-apple-system";
 
 bool
 gfxMacPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
-                                           nsTArray<gfxFontFamily*>* aOutput,
+                                           nsTArray<FamilyAndGeneric>* aOutput,
                                            FindFamiliesFlags aFlags,
                                            gfxFontStyle* aStyle,
                                            gfxFloat aDevToCssSize)
 {
     // search for special system font name, -apple-system
     if (aFamily.EqualsLiteral(kSystemFont_system)) {
         if (mUseSizeSensitiveSystemFont &&
             aStyle && (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -746,17 +746,17 @@ gfxPlatformFontList::CheckFamily(gfxFont
         return nullptr;
     }
 
     return aFamily;
 }
 
 bool
 gfxPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
-                                        nsTArray<gfxFontFamily*>* aOutput,
+                                        nsTArray<FamilyAndGeneric>* aOutput,
                                         FindFamiliesFlags aFlags,
                                         gfxFontStyle* aStyle,
                                         gfxFloat aDevToCssSize)
 {
     nsAutoString key;
     GenerateFontListKey(aFamily, key);
 
     NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
@@ -813,17 +813,17 @@ gfxPlatformFontList::FindAndAddFamilies(
             // the mOtherFamilyNames list; then retry the legacy-family search.
             if (base && base->CheckForLegacyFamilyNames(this)) {
                 familyEntry = mOtherFamilyNames.GetWeak(key);
             }
         }
     }
 
     if (familyEntry) {
-        aOutput->AppendElement(familyEntry);
+        aOutput->AppendElement(FamilyAndGeneric(familyEntry));
         return true;
     }
 
     return false;
 }
 
 gfxFontEntry*
 gfxPlatformFontList::FindFontForFamily(const nsAString& aFamily,
@@ -1006,22 +1006,22 @@ gfxPlatformFontList::GetFontFamiliesFrom
     nsAtom* aLangGroup,
     nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
 {
     // lookup and add platform fonts uniquely
     for (const nsString& genericFamily : aGenericNameFamilies) {
         gfxFontStyle style;
         style.language = aLangGroup;
         style.systemFont = false;
-        AutoTArray<gfxFontFamily*,10> families;
+        AutoTArray<FamilyAndGeneric,10> families;
         FindAndAddFamilies(genericFamily, &families, FindFamiliesFlags(0),
                            &style);
-        for (gfxFontFamily* f : families) {
-            if (!aGenericFamilies->Contains(f)) {
-                aGenericFamilies->AppendElement(f);
+        for (const FamilyAndGeneric& f : families) {
+            if (!aGenericFamilies->Contains(f.mFamily)) {
+                aGenericFamilies->AppendElement(f.mFamily);
             }
         }
     }
 }
 
 nsTArray<RefPtr<gfxFontFamily>>*
 gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
                                            eFontPrefLang aPrefLang)
@@ -1050,30 +1050,33 @@ gfxPlatformFontList::GetPrefFontsLangGro
         mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts);
     }
     return prefFonts;
 }
 
 void
 gfxPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
                                      nsAtom* aLanguage,
-                                     nsTArray<gfxFontFamily*>& aFamilyList)
+                                     nsTArray<FamilyAndGeneric>& aFamilyList)
 {
     // map lang ==> langGroup
     nsAtom* langGroup = GetLangGroup(aLanguage);
 
     // langGroup ==> prefLang
     eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
 
     // lookup pref fonts
     nsTArray<RefPtr<gfxFontFamily>>* prefFonts =
         GetPrefFontsLangGroup(aGenericType, prefLang);
 
     if (!prefFonts->IsEmpty()) {
-        aFamilyList.AppendElements(*prefFonts);
+        aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
+        for (auto& f : *prefFonts) {
+            aFamilyList.AppendElement(FamilyAndGeneric(f.get(), aGenericType));
+        }
     }
 }
 
 static nsAtom* PrefLangToLangGroups(uint32_t aIndex)
 {
     // static array here avoids static constructor
     static nsAtom* gPrefLangToLangGroups[] = {
         #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -161,17 +161,17 @@ public:
         eNoAddToNamesMissedWhenSearching = 1 << 2
     };
 
     // Find family(ies) matching aFamily and append to the aOutput array
     // (there may be multiple results in the case of fontconfig aliases, etc).
     // Return true if any match was found and appended, false if none.
     virtual bool
     FindAndAddFamilies(const nsAString& aFamily,
-                       nsTArray<gfxFontFamily*>* aOutput,
+                       nsTArray<FamilyAndGeneric>* aOutput,
                        FindFamiliesFlags aFlags,
                        gfxFontStyle* aStyle = nullptr,
                        gfxFloat aDevToCssSize = 1.0);
 
     gfxFontEntry* FindFontForFamily(const nsAString& aFamily,
                                     const gfxFontStyle* aStyle);
 
     // name lookup table methods
@@ -258,17 +258,17 @@ public:
     void GetFontlistInitInfo(uint32_t& aNumInits, uint32_t& aLoaderState) {
         aNumInits = mFontlistInitCount;
         aLoaderState = (uint32_t) mState;
     }
 
     virtual void
     AddGenericFonts(mozilla::FontFamilyType aGenericType,
                     nsAtom* aLanguage,
-                    nsTArray<gfxFontFamily*>& aFamilyList);
+                    nsTArray<FamilyAndGeneric>& aFamilyList);
 
     nsTArray<RefPtr<gfxFontFamily>>*
     GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
                           eFontPrefLang aPrefLang);
 
     // in some situations, need to make decisions about ambiguous characters, may need to look at multiple pref langs
     void GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
@@ -300,16 +300,18 @@ public:
     // Returns true if the font family whitelist is not empty.
     bool IsFontFamilyWhitelistActive();
 
     static void FontWhitelistPrefChanged(const char *aPref, void *aClosure);
 
     bool AddWithLegacyFamilyName(const nsAString& aLegacyName,
                                  gfxFontEntry* aFontEntry);
 
+    static const char* GetGenericName(mozilla::FontFamilyType aGenericType);
+
 protected:
     class InitOtherFamilyNamesRunnable : public mozilla::CancelableRunnable
     {
     public:
         InitOtherFamilyNamesRunnable()
             : CancelableRunnable("gfxPlatformFontList::InitOtherFamilyNamesRunnable")
             , mIsCanceled(false)
         {
@@ -398,22 +400,23 @@ protected:
     // Convenience method to return the first matching family (if any) as found
     // by FindAndAddFamilies().
     gfxFontFamily*
     FindFamily(const nsAString& aFamily,
                FindFamiliesFlags aFlags = FindFamiliesFlags(0),
                gfxFontStyle* aStyle = nullptr,
                gfxFloat aDevToCssSize = 1.0)
     {
-        AutoTArray<gfxFontFamily*,1> families;
+        AutoTArray<FamilyAndGeneric,1> families;
         return FindAndAddFamilies(aFamily,
                                   &families,
                                   aFlags,
                                   aStyle,
-                                  aDevToCssSize) ? families[0] : nullptr;
+                                  aDevToCssSize)
+               ? families[0].mFamily : nullptr;
     }
 
     // Lookup family name in global family list without substitutions or
     // localized family name lookup. Used for common font fallback families.
     gfxFontFamily* FindFamilyByCanonicalName(const nsAString& aFamily) {
         nsAutoString key;
         gfxFontFamily *familyEntry;
         GenerateFontListKey(aFamily, key);
@@ -483,18 +486,16 @@ protected:
 
     void GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult);
 
     virtual void GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames);
 
     // helper function to map lang to lang group
     nsAtom* GetLangGroup(nsAtom* aLanguage);
 
-    static const char* GetGenericName(mozilla::FontFamilyType aGenericType);
-
     // gfxFontInfoLoader overrides, used to load in font cmaps
     virtual void InitLoader() override;
     virtual bool LoadFontInfo() override;
     virtual void CleanupLoader() override;
 
     // read the loader initialization prefs, and start it
     void GetPrefsAndStartLoader();
 
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1791,17 +1791,17 @@ gfxFontGroup::~gfxFontGroup()
     // Should not be dropped by stylo
     MOZ_ASSERT(NS_IsMainThread());
 }
 
 void
 gfxFontGroup::BuildFontList()
 {
     // initialize fonts in the font family list
-    AutoTArray<gfxFontFamily*,10> fonts;
+    AutoTArray<FamilyAndGeneric,10> fonts;
     gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
 
     // lookup fonts in the fontlist
     for (const FontFamilyName& name : mFamilyList.GetFontlist()->mNames) {
         if (name.IsNamed()) {
             AddPlatformFont(name.mName, fonts);
         } else {
             pfl->AddGenericFonts(name.mType, mStyle.language, fonts);
@@ -1817,24 +1817,24 @@ gfxFontGroup::BuildFontList()
         pfl->AddGenericFonts(mFamilyList.GetDefaultFontType(),
                              mStyle.language, fonts);
         if (mTextPerf) {
             mTextPerf->current.genericLookups++;
         }
     }
 
     // build the fontlist from the specified families
-    for (gfxFontFamily* fontFamily : fonts) {
-        AddFamilyToFontList(fontFamily);
+    for (const auto& f : fonts) {
+        AddFamilyToFontList(f.mFamily, f.mGeneric);
     }
 }
 
 void
 gfxFontGroup::AddPlatformFont(const nsAString& aName,
-                              nsTArray<gfxFontFamily*>& aFamilyList)
+                              nsTArray<FamilyAndGeneric>& aFamilyList)
 {
     // First, look up in the user font set...
     // If the fontSet matches the family, we must not look for a platform
     // font of the same name, even if we fail to actually get a fontEntry
     // here; we'll fall back to the next name in the CSS font-family list.
     if (mUserFontSet) {
         // Add userfonts to the fontlist whether already loaded
         // or not. Loading is initiated during font matching.
@@ -1848,25 +1848,26 @@ gfxFontGroup::AddPlatformFont(const nsAS
     // Not known in the user font set ==> check system fonts
     gfxPlatformFontList::PlatformFontList()
         ->FindAndAddFamilies(aName, &aFamilyList,
                              gfxPlatformFontList::FindFamiliesFlags(0),
                              &mStyle, mDevToCssSize);
 }
 
 void
-gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily)
+gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily,
+                                  FontFamilyType aGeneric)
 {
     NS_ASSERTION(aFamily, "trying to add a null font family to fontlist");
     AutoTArray<gfxFontEntry*,4> fontEntryList;
     aFamily->FindAllFontsForStyle(mStyle, fontEntryList);
     // add these to the fontlist
     for (gfxFontEntry* fe : fontEntryList) {
         if (!HasFont(fe)) {
-            FamilyFace ff(aFamily, fe);
+            FamilyFace ff(aFamily, fe, aGeneric);
             if (fe->mIsUserFontContainer) {
                 ff.CheckState(mSkipDrawing);
             }
             mFonts.AppendElement(ff);
         }
     }
     // for a family marked as "check fallback faces", only mark the last
     // entry so that fallbacks for a family are only checked once
@@ -2059,28 +2060,31 @@ gfxFontGroup::GetDefaultFont()
                        NS_ConvertUTF16toUTF8(familiesString).get());
         MOZ_CRASH_UNSAFE_OOL(msg);
     }
 
     return mDefaultFont.get();
 }
 
 gfxFont*
-gfxFontGroup::GetFirstValidFont(uint32_t aCh)
+gfxFontGroup::GetFirstValidFont(uint32_t aCh, FontFamilyType* aGeneric)
 {
     uint32_t count = mFonts.Length();
     for (uint32_t i = 0; i < count; ++i) {
         FamilyFace& ff = mFonts[i];
         if (ff.IsInvalid()) {
             continue;
         }
 
         // already have a font?
         gfxFont* font = ff.Font();
         if (font) {
+            if (aGeneric) {
+                *aGeneric = ff.Generic();
+            }
             return font;
         }
 
         // Need to build a font, loading userfont if not loaded. In
         // cases where unicode range might apply, use the character
         // provided.
         if (ff.IsUserFontContainer()) {
             gfxUserFontEntry* ufe =
@@ -2094,19 +2098,25 @@ gfxFontGroup::GetFirstValidFont(uint32_t
             if (ufe->LoadState() != gfxUserFontEntry::STATUS_LOADED ||
                 !inRange) {
                 continue;
             }
         }
 
         font = GetFontAt(i, aCh);
         if (font) {
+            if (aGeneric) {
+                *aGeneric = mFonts[i].Generic();
+            }
             return font;
         }
     }
+    if (aGeneric) {
+        *aGeneric = eFamily_none;
+    }
     return GetDefaultFont();
 }
 
 gfxFont *
 gfxFontGroup::GetFirstMathFont()
 {
     uint32_t count = mFonts.Length();
     for (uint32_t i = 0; i < count; ++i) {
@@ -2884,32 +2894,34 @@ gfxFontGroup::FindFontForChar(uint32_t a
     bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
     bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
     bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
 
     if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
         gfxFont* firstFont = GetFontAt(0, aCh);
         if (firstFont) {
             if (firstFont->HasCharacter(aCh)) {
-                *aMatchType = gfxTextRange::MatchType::kFontGroup;
+                *aMatchType = gfxTextRange::MatchType::kFontGroup |
+                              gfxTextRange::MatchType(mFonts[0].Generic());
                 return firstFont;
             }
 
             gfxFont* font = nullptr;
             if (mFonts[0].CheckForFallbackFaces()) {
                 font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
             } else if (!firstFont->GetFontEntry()->IsUserFont()) {
                 // For platform fonts (but not userfonts), we may need to do
                 // fallback within the family to handle cases where some faces
                 // such as Italic or Black have reduced character sets compared
                 // to the family's Regular face.
                 font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
             }
             if (font) {
-                *aMatchType = gfxTextRange::MatchType::kFontGroup;
+                *aMatchType = gfxTextRange::MatchType::kFontGroup |
+                              gfxTextRange::MatchType(mFonts[0].Generic());
                 return font;
             }
         }
 
         // we don't need to check the first font again below
         ++nextIndex;
     }
 
@@ -2949,16 +2961,18 @@ gfxFontGroup::FindFontForChar(uint32_t a
         if (ff.IsInvalid() || ff.IsLoading()) {
             continue;
         }
 
         // if available, use already made gfxFont and check for character
         gfxFont* font = ff.Font();
         if (font) {
             if (font->HasCharacter(aCh)) {
+                *aMatchType = gfxTextRange::MatchType::kFontGroup |
+                              gfxTextRange::MatchType(ff.Generic());
                 return font;
             }
             continue;
         }
 
         // don't have a gfxFont yet, test before building
         gfxFontEntry *fe = ff.FontEntry();
         if (fe->mIsUserFontContainer) {
@@ -2977,50 +2991,54 @@ gfxFontGroup::FindFontForChar(uint32_t a
                 !FontLoadingForFamily(ff.Family(), aCh)) {
                 ufe->Load();
                 ff.CheckState(mSkipDrawing);
             }
             gfxFontEntry* pfe = ufe->GetPlatformFontEntry();
             if (pfe && pfe->HasCharacter(aCh)) {
                 font = GetFontAt(i, aCh);
                 if (font) {
-                    *aMatchType = gfxTextRange::MatchType::kFontGroup;
+                    *aMatchType = gfxTextRange::MatchType::kFontGroup |
+                                  gfxTextRange::MatchType(mFonts[i].Generic());
                     return font;
                 }
             }
         } else if (fe->HasCharacter(aCh)) {
             // for normal platform fonts, after checking the cmap
             // build the font via GetFontAt
             font = GetFontAt(i, aCh);
             if (font) {
-                *aMatchType = gfxTextRange::MatchType::kFontGroup;
+                *aMatchType = gfxTextRange::MatchType::kFontGroup |
+                              gfxTextRange::MatchType(mFonts[i].Generic());
                 return font;
             }
         }
 
         // check other family faces if needed
         if (ff.CheckForFallbackFaces()) {
             NS_ASSERTION(i == 0 ? true :
                          !mFonts[i-1].CheckForFallbackFaces() ||
                          !mFonts[i-1].Family()->Name().Equals(ff.Family()->Name()),
                          "should only do fallback once per font family");
             font = FindFallbackFaceForChar(ff.Family(), aCh);
             if (font) {
-                *aMatchType = gfxTextRange::MatchType::kFontGroup;
+                *aMatchType = gfxTextRange::MatchType::kFontGroup |
+                              gfxTextRange::MatchType(ff.Generic());
                 return font;
             }
         } else {
             // For platform fonts, but not user fonts, consider intra-family
             // fallback to handle styles with reduced character sets (see
             // also above).
             fe = ff.FontEntry();
             if (!fe->mIsUserFontContainer && !fe->IsUserFont()) {
                 font = FindFallbackFaceForChar(ff.Family(), aCh);
                 if (font) {
-                    *aMatchType = gfxTextRange::MatchType::kFontGroup;
+                    *aMatchType = gfxTextRange::MatchType::kFontGroup |
+                                  gfxTextRange::MatchType(ff.Generic());
                     return font;
                 }
             }
         }
     }
 
     if (fontListLength == 0) {
         gfxFont* defaultFont = GetDefaultFont();
@@ -3079,21 +3097,23 @@ void gfxFontGroup::ComputeRanges(nsTArra
             nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]);
         }
     }
     int32_t lastRangeIndex = -1;
 
     // initialize prevFont to the group's primary font, so that this will be
     // used for string-initial control chars, etc rather than risk hitting font
     // fallback for these (bug 716229)
-    gfxFont *prevFont = GetFirstValidFont();
+    FontFamilyType generic = eFamily_none;
+    gfxFont *prevFont = GetFirstValidFont(' ', &generic);
 
     // if we use the initial value of prevFont, we treat this as a match from
     // the font group; fixes bug 978313
-    gfxTextRange::MatchType matchType = gfxTextRange::MatchType::kFontGroup;
+    gfxTextRange::MatchType matchType = gfxTextRange::MatchType::kFontGroup |
+                                        gfxTextRange::MatchType(generic);
 
     for (uint32_t i = 0; i < aLength; i++) {
 
         const uint32_t origI = i; // save off in case we increase for surrogate
 
         // set up current ch
         uint32_t ch = nextCh;
 
@@ -3133,27 +3153,28 @@ void gfxFontGroup::ComputeRanges(nsTArra
         if ((font = GetFontAt(0, ch)) != nullptr
             && font->HasCharacter(ch)
             && (sizeof(T) == sizeof(uint8_t)
                 || (!IsClusterExtender(ch)
                     && ch != NARROW_NO_BREAK_SPACE
                     && !gfxFontUtils::IsJoinControl(ch)
                     && !gfxFontUtils::IsJoinCauser(prevCh)
                     && !gfxFontUtils::IsVarSelector(ch)))) {
-            matchType = gfxTextRange::MatchType::kFontGroup;
+            matchType = gfxTextRange::MatchType::kFontGroup |
+                        gfxTextRange::MatchType(mFonts[0].Generic());
         } else {
             font = FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont,
                                    &matchType);
         }
 
 #ifndef RELEASE_OR_BETA
         if (MOZ_UNLIKELY(mTextPerf)) {
-            if (matchType == gfxTextRange::MatchType::kPrefsFallback) {
+            if (matchType & gfxTextRange::MatchType::kPrefsFallback) {
                 mTextPerf->current.fallbackPrefs++;
-            } else if (matchType == gfxTextRange::MatchType::kSystemFallback) {
+            } else if (matchType & gfxTextRange::MatchType::kSystemFallback) {
                 mTextPerf->current.fallbackSystem++;
             }
         }
 #endif
 
         prevCh = ch;
 
         ShapedTextFlags orient = aOrientation;
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -859,18 +859,21 @@ public:
                  const gfxFontStyle* aStyle,
                  gfxTextPerfMetrics* aTextPerf,
                  gfxUserFontSet* aUserFontSet,
                  gfxFloat aDevToCssSize);
 
     virtual ~gfxFontGroup();
 
     // Returns first valid font in the fontlist or default font.
-    // Initiates userfont loads if userfont not loaded
-    gfxFont* GetFirstValidFont(uint32_t aCh = 0x20);
+    // Initiates userfont loads if userfont not loaded.
+    // aGeneric: if non-null, returns the CSS generic type that was mapped to
+    //           this font
+    gfxFont* GetFirstValidFont(uint32_t aCh = 0x20,
+                               mozilla::FontFamilyType* aGeneric = nullptr);
 
     // Returns the first font in the font-group that has an OpenType MATH table,
     // or null if no such font is available. The GetMathConstant methods may be
     // called on the returned font.
     gfxFont *GetFirstMathFont();
 
     const gfxFontStyle *GetStyle() const { return &mStyle; }
 
@@ -1020,47 +1023,51 @@ protected:
     void ComputeRanges(nsTArray<gfxTextRange>& mRanges,
                        const T *aString, uint32_t aLength,
                        Script aRunScript,
                        mozilla::gfx::ShapedTextFlags aOrientation);
 
     class FamilyFace {
     public:
         FamilyFace() : mFamily(nullptr), mFontEntry(nullptr),
+                       mGeneric(mozilla::eFamily_none),
                        mFontCreated(false),
                        mLoading(false), mInvalid(false),
                        mCheckForFallbackFaces(false)
         { }
 
-        FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont)
-            : mFamily(aFamily), mFontCreated(true),
+        FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont,
+                   mozilla::FontFamilyType aGeneric)
+            : mFamily(aFamily), mGeneric(aGeneric), mFontCreated(true),
               mLoading(false), mInvalid(false), mCheckForFallbackFaces(false)
         {
             NS_ASSERTION(aFont, "font pointer must not be null");
             NS_ASSERTION(!aFamily ||
                          aFamily->ContainsFace(aFont->GetFontEntry()),
                          "font is not a member of the given family");
             mFont = aFont;
             NS_ADDREF(aFont);
         }
 
-        FamilyFace(gfxFontFamily* aFamily, gfxFontEntry* aFontEntry)
-            : mFamily(aFamily), mFontCreated(false),
+        FamilyFace(gfxFontFamily* aFamily, gfxFontEntry* aFontEntry,
+                   mozilla::FontFamilyType aGeneric)
+            : mFamily(aFamily), mGeneric(aGeneric), mFontCreated(false),
               mLoading(false), mInvalid(false), mCheckForFallbackFaces(false)
         {
             NS_ASSERTION(aFontEntry, "font entry pointer must not be null");
             NS_ASSERTION(!aFamily ||
                          aFamily->ContainsFace(aFontEntry),
                          "font is not a member of the given family");
             mFontEntry = aFontEntry;
             NS_ADDREF(aFontEntry);
         }
 
         FamilyFace(const FamilyFace& aOtherFamilyFace)
             : mFamily(aOtherFamilyFace.mFamily),
+              mGeneric(aOtherFamilyFace.mGeneric),
               mFontCreated(aOtherFamilyFace.mFontCreated),
               mLoading(aOtherFamilyFace.mLoading),
               mInvalid(aOtherFamilyFace.mInvalid),
               mCheckForFallbackFaces(aOtherFamilyFace.mCheckForFallbackFaces)
         {
             if (mFontCreated) {
                 mFont = aOtherFamilyFace.mFont;
                 NS_ADDREF(mFont);
@@ -1083,16 +1090,17 @@ protected:
         {
             if (mFontCreated) {
                 NS_RELEASE(mFont);
             } else {
                 NS_IF_RELEASE(mFontEntry);
             }
 
             mFamily = aOther.mFamily;
+            mGeneric = aOther.mGeneric;
             mFontCreated = aOther.mFontCreated;
             mLoading = aOther.mLoading;
             mInvalid = aOther.mInvalid;
 
             if (mFontCreated) {
                 mFont = aOther.mFont;
                 NS_ADDREF(mFont);
             } else {
@@ -1107,16 +1115,18 @@ protected:
         gfxFont* Font() const {
             return mFontCreated ? mFont : nullptr;
         }
 
         gfxFontEntry* FontEntry() const {
             return mFontCreated ? mFont->GetFontEntry() : mFontEntry;
         }
 
+        mozilla::FontFamilyType Generic() const { return mGeneric; }
+
         bool IsUserFontContainer() const {
             return FontEntry()->mIsUserFontContainer;
         }
         bool IsLoading() const { return mLoading; }
         bool IsInvalid() const { return mInvalid; }
         void CheckState(bool& aSkipDrawing);
         void SetLoading(bool aIsLoading) { mLoading = aIsLoading; }
         void SetInvalid() { mInvalid = true; }
@@ -1143,16 +1153,17 @@ protected:
         RefPtr<gfxFontFamily> mFamily;
         // either a font or a font entry exists
         union {
             // Whichever of these fields is actually present will be a strong
             // reference, with refcounting handled manually.
             gfxFont* MOZ_OWNING_REF      mFont;
             gfxFontEntry* MOZ_OWNING_REF mFontEntry;
         };
+        mozilla::FontFamilyType mGeneric;
         bool                    mFontCreated : 1;
         bool                    mLoading     : 1;
         bool                    mInvalid     : 1;
         bool                    mCheckForFallbackFaces : 1;
     };
 
     // List of font families, either named or generic.
     // Generic names map to system pref fonts based on language.
@@ -1254,20 +1265,21 @@ protected:
     // whether the family might have a font for a given character
     gfxFont*
     FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh);
 
    // helper methods for looking up fonts
 
     // lookup and add a font with a given name (i.e. *not* a generic!)
     void AddPlatformFont(const nsAString& aName,
-                         nsTArray<gfxFontFamily*>& aFamilyList);
+                         nsTArray<FamilyAndGeneric>& aFamilyList);
 
     // do style selection and add entries to list
-    void AddFamilyToFontList(gfxFontFamily* aFamily);
+    void AddFamilyToFontList(gfxFontFamily* aFamily,
+                             mozilla::FontFamilyType aGeneric);
 };
 
 // A "missing font recorder" is to be used during text-run creation to keep
 // a record of any scripts encountered for which font coverage was lacking;
 // when Flush() is called, it sends a notification that front-end code can use
 // to download fonts on demand (or whatever else it wants to do).
 
 #define GFX_MISSING_FONTS_NOTIFY_PREF "gfx.missing_fonts.notify"
--- a/layout/inspector/InspectorFontFace.cpp
+++ b/layout/inspector/InspectorFontFace.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "InspectorFontFace.h"
 
+#include "gfxPlatformFontList.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 #include "nsFontFaceLoader.h"
 #include "mozilla/gfx/2D.h"
 #include "brotli/decode.h"
 #include "zlib.h"
 #include "mozilla/dom/FontFaceSet.h"
 #include "mozilla/ServoBindings.h"
@@ -50,16 +51,29 @@ InspectorFontFace::GetName(nsAString& aN
 }
 
 void
 InspectorFontFace::GetCSSFamilyName(nsAString& aCSSFamilyName)
 {
   aCSSFamilyName = mFontEntry->FamilyName();
 }
 
+void
+InspectorFontFace::GetCSSGeneric(nsAString& aName)
+{
+  auto genericType =
+    FontFamilyType(mMatchType & gfxTextRange::MatchType::kGenericMask);
+  if (genericType >= FontFamilyType::eFamily_generic_first &&
+      genericType <= FontFamilyType::eFamily_generic_last) {
+    aName.AssignASCII(gfxPlatformFontList::GetGenericName(genericType));
+  } else {
+    aName.Truncate(0);
+  }
+}
+
 ServoFontFaceRule*
 InspectorFontFace::GetRule()
 {
   if (!mRule) {
     // check whether this font entry is associated with an @font-face rule
     // in the relevant font group's user font set
     RawServoFontFaceRule* rule = nullptr;
     if (mFontEntry->IsUserFont()) {
--- a/layout/inspector/InspectorFontFace.h
+++ b/layout/inspector/InspectorFontFace.h
@@ -51,16 +51,17 @@ public:
   }
 
   // Web IDL
   bool FromFontGroup();
   bool FromLanguagePrefs();
   bool FromSystemFallback();
   void GetName(nsAString& aName);
   void GetCSSFamilyName(nsAString& aCSSFamilyName);
+  void GetCSSGeneric(nsAString& aGeneric);
   ServoFontFaceRule* GetRule();
   int32_t SrcIndex();
   void GetURI(nsAString& aURI);
   void GetLocalName(nsAString& aLocalName);
   void GetFormat(nsAString& aFormat);
   void GetMetadata(nsAString& aMetadata);
 
   void GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult,
--- a/layout/inspector/tests/chrome/chrome.ini
+++ b/layout/inspector/tests/chrome/chrome.ini
@@ -23,8 +23,9 @@ support-files =
 [test_fontFeaturesAPI.xul]
 support-files =
   test_fontFeaturesAPI.css
   DejaVuSans.ttf
 [test_fontVariationsAPI.xul]
 skip-if = (os == 'win' || os == 'linux' || os == 'mac') # bug 1433438, bug 1456855, bug 1456856
 support-files =
   test_fontVariationsAPI.css
+[test_fontFaceGeneric.xul]
new file mode 100644
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_fontFaceGeneric.xul
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="CSSGeneric attribute of InspectorFontFace"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="RunTest();">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function RunTest() {
+  var rng = document.createRange();
+  var elem, fonts;
+
+  elem = document.getElementById("test1");
+  rng.selectNode(elem);
+  fonts = InspectorUtils.getUsedFontFaces(rng);
+  is(fonts.length, 1, "one font found");
+  is(fonts[0].CSSGeneric, "serif", "font " + fonts[0].name + " was specified as CSS generic");
+  var serifFamily = fonts[0].CSSFamilyName;
+
+  elem = document.getElementById("test2");
+  elem.style.fontFamily = serifFamily + ", serif";
+  rng.selectNode(elem);
+  fonts = InspectorUtils.getUsedFontFaces(rng);
+  is(fonts.length, 1, "one font found");
+  is(fonts[0].CSSFamilyName, serifFamily, "used the expected family (" + serifFamily + ")");
+  is(fonts[0].CSSGeneric, "", "font " + fonts[0].name + " was specified by name");
+
+  elem = document.getElementById("test3");
+  elem.getElementsByTagName("span")[0].style.fontFamily = serifFamily + ", serif";
+  rng.selectNode(elem);
+  fonts = InspectorUtils.getUsedFontFaces(rng);
+  is(fonts.length, 2, "two fonts found");
+  var checked = 0;
+  fonts.forEach(function(f) {
+    if (f.CSSFamilyName == serifFamily) {
+      is(f.CSSGeneric, "", "serif font " + f.name + " was specified by name");
+      checked++;
+    } else {
+      is(f.CSSGeneric, "monospace", "monospace font " + f.name + " was specified as generic");
+      checked++;
+    }
+  });
+  is(checked, 2, "two fonts checked");
+
+  SimpleTest.finish();
+}
+  ]]>
+  </script>
+
+  <!-- html:body contains elements the test will inspect -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <div id="test1" style="font-family: serif">test one</div>
+  <div id="test2" style="font-family: monospace">test two</div>
+  <div id="test3" style="font-family: monospace">test <span>three</span></div>
+  </body>
+
+</window>
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -31,17 +31,17 @@
 
 class nsAtom;
 class nsIPrincipal;
 class nsIURI;
 struct nsFont;
 namespace mozilla {
   class FontFamilyList;
   struct FontFamilyName;
-  enum FontFamilyType : uint32_t;
+  enum FontFamilyType : uint8_t;
   class SharedFontList;
   enum class CSSPseudoElementType : uint8_t;
   struct Keyframe;
   struct StyleTransition;
   namespace css {
     class ErrorReporter;
     struct URLValue;
     struct ImageValue;