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
☠☠ backed out by 538a689e3487 ☠ ☠
authorJonathan Kew <jkew@mozilla.com>
Fri, 25 May 2018 14:07:57 +0100
changeset 1528986 f8dbb1d2d07cb5645d8370b1f766b21da538d392
parent 1528985 36687c035662d43b744ddb1ead29e65d139fae66
child 1528987 040aa41f523e4abecdd1d7546e90ff179407693d
push id276055
push usersurkov.alexander@gmail.com
push dateFri, 01 Jun 2018 15:22:50 +0000
treeherdertry@b0e8b387f142 [default view] [failures only]
reviewersjwatt
bugs1464400
milestone62.0a1
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
@@ -130,16 +130,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;