Bug 1397626 - Part 3: Use SharedFontList to store font-family specified and computed values. r=xidorn
authorCameron McCormack <cam@mcc.id.au>
Tue, 03 Oct 2017 12:27:45 +0800
changeset 427071 d49cf4aed3350dff668d64a2cf86ce64e6c7a2df
parent 427070 16c2166700f2bac39515b4c81f1b82aa8045fc22
child 427072 00f3a339b1976a9942a7ef04c9ac6a9d4204aee8
push id97
push userfmarier@mozilla.com
push dateSat, 14 Oct 2017 01:12:59 +0000
reviewersxidorn
bugs1397626
milestone58.0a1
Bug 1397626 - Part 3: Use SharedFontList to store font-family specified and computed values. r=xidorn MozReview-Commit-ID: J3MNO2un2ov
dom/events/ContentEventHandler.cpp
gfx/thebes/gfxFontFamilyList.h
gfx/thebes/gfxTextRun.cpp
gfx/thebes/gfxUserFontSet.cpp
js/src/devtools/rootAnalysis/analyzeHeapWrites.js
layout/build/nsLayoutStatics.cpp
layout/mathml/nsMathMLChar.cpp
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleUtil.cpp
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1040,17 +1040,17 @@ ContentEventHandler::GenerateFlatFontRan
         MOZ_ASSERT(baseOffset == 0);
         FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
         nsIFrame* frame = content->GetPrimaryFrame();
         if (frame) {
           const nsFont& font = frame->GetParent()->StyleFont()->mFont;
           const FontFamilyList& fontList = font.fontlist;
           const FontFamilyName& fontName = fontList.IsEmpty() ?
             FontFamilyName(fontList.GetDefaultFontType()) :
-            fontList.GetFontlist()[0];
+            fontList.GetFontlist()->mNames[0];
           fontName.AppendToString(fontRange->mFontName, false);
           fontRange->mFontSize =
             frame->PresContext()->AppUnitsToDevPixels(font.size);
         }
       }
       baseOffset += GetBRLength(aLineBreakType);
     }
   }
--- a/gfx/thebes/gfxFontFamilyList.h
+++ b/gfx/thebes/gfxFontFamilyList.h
@@ -7,16 +7,18 @@
 #define GFX_FONT_FAMILY_LIST_H
 
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsTArray.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/StaticPtr.h"
 
 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
  */ 
@@ -162,17 +164,19 @@ struct FontFamilyName final {
 };
 
 inline bool
 operator==(const FontFamilyName& a, const FontFamilyName& b) {
     return a.mType == b.mType && a.mName == b.mName;
 }
 
 /**
- * A refcounted array of FontFamilyNames.
+ * A refcounted array of FontFamilyNames.  We use this to store the specified
+ * value (in Servo) and the computed value (in both Gecko and Servo) of the
+ * font-family property.
  */
 class SharedFontList
 {
 public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedFontList);
 
     SharedFontList()
     {
@@ -246,161 +250,165 @@ private:
 /**
  * font family list, array of font families and a default font type.
  * font family names are either named strings or generics. the default
  * font type is used to preserve the variable font fallback behavior
  */
 class FontFamilyList {
 public:
     FontFamilyList()
-        : mDefaultFontType(eFamily_none)
+        : mFontlist(WrapNotNull(SharedFontList::sEmpty.get()))
+        , mDefaultFontType(eFamily_none)
     {
     }
 
     explicit FontFamilyList(FontFamilyType aGenericType)
-        : mDefaultFontType(eFamily_none)
+        : mFontlist(WrapNotNull(new SharedFontList(aGenericType)))
+        , mDefaultFontType(eFamily_none)
     {
-        Append(FontFamilyName(aGenericType));
     }
 
     FontFamilyList(const nsAString& aFamilyName,
                    QuotedName aQuoted)
-        : mDefaultFontType(eFamily_none)
+        : mFontlist(WrapNotNull(new SharedFontList(aFamilyName, aQuoted)))
+        , mDefaultFontType(eFamily_none)
     {
-        Append(FontFamilyName(aFamilyName, aQuoted));
+    }
+
+    explicit FontFamilyList(const FontFamilyName& aName)
+        : mFontlist(WrapNotNull(new SharedFontList(aName)))
+        , mDefaultFontType(eFamily_none)
+    {
+    }
+
+    explicit FontFamilyList(nsTArray<FontFamilyName>&& aNames)
+        : mFontlist(WrapNotNull(new SharedFontList(Move(aNames))))
+    {
     }
 
     FontFamilyList(const FontFamilyList& aOther)
         : mFontlist(aOther.mFontlist)
         , mDefaultFontType(aOther.mDefaultFontType)
     {
     }
 
-    FontFamilyList(const SharedFontList* aFontlist)
-        : mFontlist(aFontlist->mNames)
+    FontFamilyList(NotNull<SharedFontList*> aFontList)
+        : mFontlist(aFontList)
         , mDefaultFontType(eFamily_none)
     {
     }
 
-    void Append(const FontFamilyName& aFamilyName) {
-        mFontlist.AppendElement(aFamilyName);
+    void SetFontlist(nsTArray<FontFamilyName>&& aNames)
+    {
+        mFontlist = WrapNotNull(new SharedFontList(Move(aNames)));
     }
 
-    void Append(const nsTArray<nsString>& aFamilyNameList) {
-        uint32_t len = aFamilyNameList.Length();
-        for (uint32_t i = 0; i < len; i++) {
-            mFontlist.AppendElement(FontFamilyName(aFamilyNameList[i],
-                                                   eUnquotedName));
-        }
-    }
-
-    void Clear() {
-        mFontlist.Clear();
+    void SetFontlist(NotNull<SharedFontList*> aFontlist)
+    {
+        mFontlist = aFontlist;
     }
 
     uint32_t Length() const {
-        return mFontlist.Length();
+        return mFontlist->mNames.Length();
     }
 
     bool IsEmpty() const {
-      return mFontlist.IsEmpty();
+        return mFontlist->mNames.IsEmpty();
     }
 
-    const nsTArray<FontFamilyName>& GetFontlist() const {
+    NotNull<SharedFontList*> GetFontlist() const {
         return mFontlist;
     }
 
     bool Equals(const FontFamilyList& aFontlist) const {
-        return mFontlist == aFontlist.mFontlist &&
+        return (mFontlist == aFontlist.mFontlist ||
+                mFontlist->mNames == aFontlist.mFontlist->mNames) &&
                mDefaultFontType == aFontlist.mDefaultFontType;
     }
 
-    FontFamilyType FirstGeneric() const
-    {
-        for (const FontFamilyName& name : mFontlist) {
-            if (name.IsGeneric()) {
-                return name.mType;
-            }
-        }
-        return eFamily_none;
+    FontFamilyType FirstGeneric() const {
+        return mFontlist->FirstGeneric();
     }
 
     bool HasGeneric() const
     {
-        return FirstGeneric() != eFamily_none;
+        return mFontlist->HasGeneric();
     }
 
     bool HasDefaultGeneric() const {
-        uint32_t len = mFontlist.Length();
-        for (uint32_t i = 0; i < len; i++) {
-            const FontFamilyName& name = mFontlist[i];
+        for (const FontFamilyName& name : mFontlist->mNames) {
             if (name.mType == mDefaultFontType) {
                 return true;
             }
         }
         return false;
     }
 
     // Find the first generic (but ignoring cursive and fantasy, as they are
     // rarely configured in any useful way) in the list.
     // If found, move it to the start and return true; else return false.
     bool PrioritizeFirstGeneric() {
-        uint32_t len = mFontlist.Length();
+        uint32_t len = mFontlist->mNames.Length();
         for (uint32_t i = 0; i < len; i++) {
-            const FontFamilyName name = mFontlist[i];
+            const FontFamilyName name = mFontlist->mNames[i];
             if (name.IsGeneric()) {
                 if (name.mType == eFamily_cursive ||
                     name.mType == eFamily_fantasy) {
                     continue;
                 }
                 if (i > 0) {
-                    mFontlist.RemoveElementAt(i);
-                    mFontlist.InsertElementAt(0, name);
+                    nsTArray<FontFamilyName> names;
+                    names.AppendElements(mFontlist->mNames);
+                    names.RemoveElementAt(i);
+                    names.InsertElementAt(0, name);
+                    SetFontlist(Move(names));
                 }
                 return true;
             }
         }
         return false;
     }
 
     void PrependGeneric(FontFamilyType aType) {
-        mFontlist.InsertElementAt(0, FontFamilyName(aType));
+        nsTArray<FontFamilyName> names;
+        names.AppendElements(mFontlist->mNames);
+        names.InsertElementAt(0, FontFamilyName(aType));
+        SetFontlist(Move(names));
     }
 
     void ToString(nsAString& aFamilyList,
                   bool aQuotes = true,
                   bool aIncludeDefault = false) const {
+        const nsTArray<FontFamilyName>& names = mFontlist->mNames;
         aFamilyList.Truncate();
-        uint32_t len = mFontlist.Length();
+        uint32_t len = names.Length();
         for (uint32_t i = 0; i < len; i++) {
             if (i != 0) {
                 aFamilyList.Append(',');
             }
-            const FontFamilyName& name = mFontlist[i];
+            const FontFamilyName& name = names[i];
             name.AppendToString(aFamilyList, aQuotes);
         }
         if (aIncludeDefault && mDefaultFontType != eFamily_none) {
             if (!aFamilyList.IsEmpty()) {
                 aFamilyList.Append(',');
             }
             if (mDefaultFontType == eFamily_serif) {
                 aFamilyList.AppendLiteral("serif");
             } else {
                 aFamilyList.AppendLiteral("sans-serif");
             }
         }
     }
 
     // searches for a specific non-generic name, lowercase comparison
     bool Contains(const nsAString& aFamilyName) const {
-        uint32_t len = mFontlist.Length();
         nsAutoString fam(aFamilyName);
         ToLowerCase(fam);
-        for (uint32_t i = 0; i < len; i++) {
-            const FontFamilyName& name = mFontlist[i];
+        for (const FontFamilyName& name : mFontlist->mNames) {
             if (name.mType != eFamily_named &&
                 name.mType != eFamily_named_quoted) {
                 continue;
             }
             nsAutoString listname(name.mName);
             ToLowerCase(listname);
             if (listname.Equals(fam)) {
                 return true;
@@ -415,29 +423,26 @@ public:
                      aType == eFamily_sans_serif,
                      "default font type must be either serif or sans-serif");
         mDefaultFontType = aType;
     }
 
     // memory reporting
     size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
         size_t n = 0;
-        n += mFontlist.ShallowSizeOfExcludingThis(aMallocSizeOf);
-        for (size_t i = 0; i < mFontlist.Length(); i++) {
-            n += mFontlist[i].SizeOfExcludingThis(aMallocSizeOf);
-        }
+        n += mFontlist->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
         return n;
     }
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
         return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     }
 
-private:
-    nsTArray<FontFamilyName>   mFontlist;
+protected:
+    NotNull<RefPtr<SharedFontList>> mFontlist;
     FontFamilyType             mDefaultFontType; // none, serif or sans-serif
 };
 
 inline bool
 operator==(const FontFamilyList& a, const FontFamilyList& b) {
     return a.Equals(b);
 }
 
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1830,17 +1830,17 @@ gfxFontGroup::~gfxFontGroup()
 void
 gfxFontGroup::BuildFontList()
 {
     // initialize fonts in the font family list
     AutoTArray<gfxFontFamily*,10> fonts;
     gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
 
     // lookup fonts in the fontlist
-    for (const FontFamilyName& name : mFamilyList.GetFontlist()) {
+    for (const FontFamilyName& name : mFamilyList.GetFontlist()->mNames) {
         if (name.IsNamed()) {
             AddPlatformFont(name.mName, fonts);
         } else {
             pfl->AddGenericFonts(name.mType, mStyle.language, fonts);
             if (mTextPerf) {
                 mTextPerf->current.genericLookups++;
             }
         }
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -1069,17 +1069,17 @@ gfxUserFontSet::LookupFamily(const nsASt
     ToLowerCase(key);
 
     return mFontFamilies.GetWeak(key);
 }
 
 bool
 gfxUserFontSet::ContainsUserFontSetFonts(const FontFamilyList& aFontList) const
 {
-    for (const FontFamilyName& name : aFontList.GetFontlist()) {
+    for (const FontFamilyName& name : aFontList.GetFontlist()->mNames) {
         if (name.mType != eFamily_named &&
             name.mType != eFamily_named_quoted) {
             continue;
         }
         if (LookupFamily(name.mName)) {
             return true;
         }
     }
--- a/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
+++ b/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
@@ -233,16 +233,18 @@ function treatAsSafeArgument(entry, varN
         ["Gecko_nsStyleFont_PrefillDefaultForGeneric", "aFont", null],
         ["Gecko_nsStyleSVG_SetContextPropertiesLength", "aSvg", null],
         ["Gecko_ClearAlternateValues", "aFont", null],
         ["Gecko_AppendAlternateValues", "aFont", null],
         ["Gecko_CopyAlternateValuesFrom", "aDest", null],
         ["Gecko_CounterStyle_GetName", "aResult", null],
         ["Gecko_CounterStyle_GetSingleString", "aResult", null],
         ["Gecko_EnsureMozBorderColors", "aBorder", null],
+        ["Gecko_nsTArray_FontFamilyName_AppendNamed", "aNames", null],
+        ["Gecko_nsTArray_FontFamilyName_AppendGeneric", "aNames", null],
     ];
     for (var [entryMatch, varMatch, csuMatch] of whitelist) {
         assert(entryMatch || varMatch || csuMatch);
         if (entryMatch && !nameMatches(entry.name, entryMatch))
             continue;
         if (varMatch && !nameMatches(varName, varMatch))
             continue;
         if (csuMatch && (!csuName || !nameMatches(csuName, csuMatch)))
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -187,16 +187,17 @@ nsLayoutStatics::Initialize()
   rv = nsTextFragment::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsTextFragment");
     return rv;
   }
 
   nsCellMap::Init();
 
+  mozilla::SharedFontList::Initialize();
   StaticPresData::Init();
   nsCSSRendering::Init();
 
   rv = nsHTMLDNSPrefetch::Initialize();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize HTML DNS prefetch");
     return rv;
   }
@@ -413,16 +414,17 @@ nsLayoutStatics::Shutdown()
 
   nsRegion::ShutdownStatic();
 
   mozilla::EventDispatcher::Shutdown();
 
   HTMLInputElement::DestroyUploadLastDir();
 
   nsLayoutUtils::Shutdown();
+  mozilla::SharedFontList::Shutdown();
 
   nsHyphenationManager::Shutdown();
   nsDOMMutationObserver::Shutdown();
 
   DateTimeFormat::Shutdown();
 
   ContentParent::ShutDown();
 
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -53,17 +53,21 @@ using namespace mozilla::image;
 static const float kMaxScaleFactor = 20.0;
 static const float kLargeOpFactor = float(M_SQRT2);
 static const float kIntegralFactor = 2.0;
 
 static void
 NormalizeDefaultFont(nsFont& aFont, float aFontSizeInflation)
 {
   if (aFont.fontlist.GetDefaultFontType() != eFamily_none) {
-    aFont.fontlist.Append(FontFamilyName(aFont.fontlist.GetDefaultFontType()));
+    nsTArray<FontFamilyName> names;
+    names.AppendElements(aFont.fontlist.GetFontlist()->mNames);
+    names.AppendElement(FontFamilyName(aFont.fontlist.GetDefaultFontType()));
+
+    aFont.fontlist.SetFontlist(Move(names));
     aFont.fontlist.SetDefaultFontType(eFamily_none);
   }
   aFont.size = NSToCoordRound(aFont.size * aFontSizeInflation);
 }
 
 // -----------------------------------------------------------------------------
 static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
 
@@ -969,17 +973,19 @@ nsMathMLChar::SetFontFamily(nsPresContex
                             const nsGlyphCode&      aGlyphCode,
                             const FontFamilyList&   aDefaultFamilyList,
                             nsFont&                 aFont,
                             RefPtr<gfxFontGroup>* aFontGroup)
 {
   FontFamilyList glyphCodeFont;
 
   if (aGlyphCode.font) {
-    glyphCodeFont.Append(aGlyphTable->FontNameFor(aGlyphCode));
+    nsTArray<FontFamilyName> names;
+    names.AppendElement(aGlyphTable->FontNameFor(aGlyphCode));
+    glyphCodeFont.SetFontlist(Move(names));
   }
 
   const FontFamilyList& familyList =
     aGlyphCode.font ? glyphCodeFont : aDefaultFamilyList;
 
   if (!*aFontGroup || !(aFont.fontlist == familyList)) {
     nsFont font = aFont;
     font.fontlist = familyList;
@@ -989,19 +995,18 @@ nsMathMLChar::SetFontFamily(nsPresContex
     params.explicitLanguage = styleFont->mExplicitLanguage;
     params.userFontSet = aPresContext->GetUserFontSet();
     params.textPerf = aPresContext->GetTextPerfMetrics();
     RefPtr<nsFontMetrics> fm =
       aPresContext->DeviceContext()->GetMetricsFor(font, params);
     // Set the font if it is an unicode table
     // or if the same family name has been found
     gfxFont *firstFont = fm->GetThebesFontGroup()->GetFirstValidFont();
-    FontFamilyList firstFontList;
-    firstFontList.Append(
-      FontFamilyName(firstFont->GetFontEntry()->FamilyName(), eUnquotedName));
+    FontFamilyList firstFontList(
+      firstFont->GetFontEntry()->FamilyName(), eUnquotedName);
     if (aGlyphTable == &gGlyphTableList->mUnicodeTable ||
         firstFontList == familyList) {
       aFont.fontlist = familyList;
       *aFontGroup = fm->GetThebesFontGroup();
     } else {
       return false; // We did not set the font
     }
   }
@@ -1413,18 +1418,17 @@ nsMathMLChar::StretchEnumContext::EnumCa
   }
 
   // Check font family if it is not a generic one
   // We test with the kNullGlyph
   nsStyleContext *sc = context->mChar->mStyleContext;
   nsFont font = sc->StyleFont()->mFont;
   NormalizeDefaultFont(font, context->mFontSizeInflation);
   RefPtr<gfxFontGroup> fontGroup;
-  FontFamilyList family;
-  family.Append(unquotedFamilyName);
+  FontFamilyList family(unquotedFamilyName);
   if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
                                                   nullptr, kNullGlyph, family,
                                                   font, &fontGroup))
      return true; // Could not set the family
 
   // Determine the glyph table to use for this font.
   nsAutoPtr<nsOpenTypeTable> openTypeTable;
   nsGlyphTable* glyphTable;
@@ -1464,40 +1468,46 @@ nsMathMLChar::StretchEnumContext::EnumCa
      (context->mTryParts && context->TryParts(glyphTable,
                                               &fontGroup,
                                               familyList)))
     return false; // no need to continue
 
   return true; // true means continue
 }
 
+static void
+AppendFallbacks(nsTArray<FontFamilyName>& aNames,
+                const nsTArray<nsString>& aFallbacks)
+{
+  for (const nsString& fallback : aFallbacks) {
+    aNames.AppendElement(FontFamilyName(fallback, eUnquotedName));
+  }
+}
+
 // insert math fallback families just before the first generic or at the end
 // when no generic present
 static void
 InsertMathFallbacks(FontFamilyList& aFamilyList,
                     nsTArray<nsString>& aFallbacks)
 {
-  FontFamilyList aMergedList;
+  nsTArray<FontFamilyName> mergedList;
 
   bool inserted = false;
-  const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
-  uint32_t i, num = fontlist.Length();
-  for (i = 0; i < num; i++) {
-    const FontFamilyName& name = fontlist[i];
+  for (const FontFamilyName& name : aFamilyList.GetFontlist()->mNames) {
     if (!inserted && name.IsGeneric()) {
       inserted = true;
-      aMergedList.Append(aFallbacks);
+      AppendFallbacks(mergedList, aFallbacks);
     }
-    aMergedList.Append(name);
+    mergedList.AppendElement(name);
   }
 
   if (!inserted) {
-    aMergedList.Append(aFallbacks);
+    AppendFallbacks(mergedList, aFallbacks);
   }
-  aFamilyList = aMergedList;
+  aFamilyList.SetFontlist(Move(mergedList));
 }
 
 nsresult
 nsMathMLChar::StretchInternal(nsIFrame*                aForFrame,
                               DrawTarget*              aDrawTarget,
                               float                    aFontSizeInflation,
                               nsStretchDirection&      aStretchDirection,
                               const nsBoundingMetrics& aContainerSize,
@@ -1651,17 +1661,17 @@ nsMathMLChar::StretchInternal(nsIFrame* 
            NS_ConvertUTF16toUTF8(fontlistStr).get(), mData[0], mData[0]&0x00FF);
 #endif
     StretchEnumContext enumData(this, presContext, aDrawTarget,
                                 aFontSizeInflation,
                                 aStretchDirection, targetSize, aStretchHint,
                                 aDesiredStretchSize, font.fontlist, glyphFound);
     enumData.mTryParts = !largeopOnly;
 
-    const nsTArray<FontFamilyName>& fontlist = font.fontlist.GetFontlist();
+    const nsTArray<FontFamilyName>& fontlist = font.fontlist.GetFontlist()->mNames;
     uint32_t i, num = fontlist.Length();
     bool next = true;
     for (i = 0; i < num && next; i++) {
       const FontFamilyName& name = fontlist[i];
       next = StretchEnumContext::EnumCallback(name, name.IsGeneric(), &enumData);
     }
   }
 
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1251,38 +1251,61 @@ Gecko_AtomEqualsUTF8IgnoreCase(nsIAtom* 
 
 void
 Gecko_EnsureMozBorderColors(nsStyleBorder* aBorder)
 {
   aBorder->EnsureBorderColors();
 }
 
 void
-Gecko_FontFamilyList_Clear(FontFamilyList* aList) {
-  aList->Clear();
-}
-
-void
-Gecko_FontFamilyList_AppendNamed(FontFamilyList* aList, nsIAtom* aName, bool aQuoted)
+Gecko_nsTArray_FontFamilyName_AppendNamed(nsTArray<FontFamilyName>* aNames,
+                                          nsIAtom* aName,
+                                          bool aQuoted)
 {
   FontFamilyName family;
   aName->ToString(family.mName);
   if (aQuoted) {
     family.mType = eFamily_named_quoted;
   }
 
-  aList->Append(family);
+  aNames->AppendElement(family);
 }
 
 void
-Gecko_FontFamilyList_AppendGeneric(FontFamilyList* aList, FontFamilyType aType)
+Gecko_nsTArray_FontFamilyName_AppendGeneric(nsTArray<FontFamilyName>* aNames,
+                                            FontFamilyType aType)
+{
+  aNames->AppendElement(FontFamilyName(aType));
+}
+
+SharedFontList*
+Gecko_SharedFontList_Create()
 {
-  aList->Append(FontFamilyName(aType));
+  RefPtr<SharedFontList> fontlist = new SharedFontList();
+  return fontlist.forget().take();
 }
 
+MOZ_DEFINE_MALLOC_SIZE_OF(GeckoSharedFontListMallocSizeOf)
+
+size_t
+Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(SharedFontList* aFontlist)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return aFontlist->SizeOfIncludingThisIfUnshared(GeckoSharedFontListMallocSizeOf);
+}
+
+size_t
+Gecko_SharedFontList_SizeOfIncludingThis(SharedFontList* aFontlist)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return aFontlist->SizeOfIncludingThis(GeckoSharedFontListMallocSizeOf);
+}
+
+NS_IMPL_THREADSAFE_FFI_REFCOUNTING(mozilla::SharedFontList, SharedFontList);
+
 void
 Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src)
 {
   dst->fontlist = src->fontlist;
 }
 
 void
 Gecko_nsFont_InitSystem(nsFont* aDest, int32_t aFontId,
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -30,17 +30,19 @@
  */
 
 class nsIAtom;
 class nsIPrincipal;
 class nsIURI;
 struct nsFont;
 namespace mozilla {
   class FontFamilyList;
+  struct FontFamilyName;
   enum FontFamilyType : uint32_t;
+  class SharedFontList;
   enum class CSSPseudoElementType : uint8_t;
   struct Keyframe;
   enum Side;
   struct StyleTransition;
   namespace css {
     struct URLValue;
     struct ImageValue;
     class LoaderReusableStyleSheets;
@@ -51,18 +53,20 @@ namespace mozilla {
   enum class UpdateAnimationsTasks : uint8_t;
   struct LangGroupFontPrefs;
   class SeenPtrs;
   class ServoStyleContext;
   class ServoStyleSheet;
   class ServoElementSnapshotTable;
 }
 using mozilla::FontFamilyList;
+using mozilla::FontFamilyName;
 using mozilla::FontFamilyType;
 using mozilla::ServoElementSnapshot;
+using mozilla::SharedFontList;
 class nsCSSCounterStyleRule;
 class nsCSSFontFaceRule;
 struct nsMediaFeature;
 struct nsStyleList;
 struct nsStyleImage;
 struct nsStyleGradientStop;
 class nsStyleGradient;
 class nsStyleCoord;
@@ -279,20 +283,24 @@ void Gecko_ReleaseAtom(nsIAtom* aAtom);
 const uint16_t* Gecko_GetAtomAsUTF16(nsIAtom* aAtom, uint32_t* aLength);
 bool Gecko_AtomEqualsUTF8(nsIAtom* aAtom, const char* aString, uint32_t aLength);
 bool Gecko_AtomEqualsUTF8IgnoreCase(nsIAtom* aAtom, const char* aString, uint32_t aLength);
 
 // Border style
 void Gecko_EnsureMozBorderColors(nsStyleBorder* aBorder);
 
 // Font style
-void Gecko_FontFamilyList_Clear(FontFamilyList* aList);
-void Gecko_FontFamilyList_AppendNamed(FontFamilyList* aList, nsIAtom* aName, bool aQuoted);
-void Gecko_FontFamilyList_AppendGeneric(FontFamilyList* list, FontFamilyType familyType);
 void Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src);
+void Gecko_nsTArray_FontFamilyName_AppendNamed(nsTArray<FontFamilyName>* aNames, nsIAtom* aName, bool aQuoted);
+void Gecko_nsTArray_FontFamilyName_AppendGeneric(nsTArray<FontFamilyName>* aNames, FontFamilyType aType);
+// Returns an already-AddRefed SharedFontList with an empty mNames array.
+SharedFontList* Gecko_SharedFontList_Create();
+size_t Gecko_SharedFontList_SizeOfIncludingThis(SharedFontList* fontlist);
+size_t Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(SharedFontList* fontlist);
+NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::SharedFontList, SharedFontList);
 // will not run destructors on dst, give it uninitialized memory
 // font_id is LookAndFeel::FontID
 void Gecko_nsFont_InitSystem(nsFont* dst, int32_t font_id,
                              const nsStyleFont* font, RawGeckoPresContextBorrowed pres_context);
 void Gecko_nsFont_Destroy(nsFont* dst);
 
 // The gfxFontFeatureValueSet returned from this function has zero reference.
 gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet();
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -422,17 +422,16 @@ structs-types = [
     "RawGeckoXBLBinding",
     "RefPtr",
     "RustString",
     "CSSPseudoClassType",
     "CSSPseudoElementType",
     "ServoTraversalFlags",
     "ComputedTimingFunction_BeforeFlag",
     "CounterStylePtr",
-    "FontFamilyList",
     "FontFamilyType",
     "FontSizePrefs",
     "GeckoFontMetrics",
     "IterationCompositeOperation",
     "Keyframe",
     "PropertyValuePair",
     "SeenPtrs",
     "ServoBundledURI",
@@ -518,16 +517,18 @@ structs-types = [
     "EffectCompositor_CascadeLevel",
     "UpdateAnimationsTasks",
     "ParsingMode",
     "InheritTarget",
     "URLMatchingFunction",
     "StyleRuleInclusion",
     "nsStyleTransformMatrix::MatrixTransformOperator",
     "RawGeckoGfxMatrix4x4",
+    "FontFamilyName",
+    "mozilla::SharedFontList",
 ]
 array-types = [
     { cpp-type = "uintptr_t", rust-type = "usize" },
 ]
 servo-owned-types = [
     { name = "RawServoStyleSet", opaque = true },
     { name = "ServoElementSnapshot", opaque = false },
     { name = "RawServoAnimationValueMap", opaque = true },
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -2203,16 +2203,19 @@ nsCSSValue::SizeOfExcludingThis(mozilla:
       break;
 
     // GridTemplateAreas
     case eCSSUnit_GridTemplateAreas:
       n += mValue.mGridTemplateAreas->SizeOfIncludingThis(aMallocSizeOf);
       break;
 
     case eCSSUnit_FontFamilyList:
+      // The SharedFontList is a refcounted object, but is unique per
+      // declaration. We don't measure the references from computed
+      // values.
       n += mValue.mFontFamilyList->SizeOfIncludingThis(aMallocSizeOf);
       break;
 
     // Atom is always shared, and thus should not be counted.
     case eCSSUnit_AtomIdent:
       break;
 
     // Int: nothing extra to measure.
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -813,23 +813,23 @@ public:
   }
 
   nsCSSValueSharedList* GetSharedListValue() const
   {
     MOZ_ASSERT(mUnit == eCSSUnit_SharedList, "not a shared list value");
     return mValue.mSharedList;
   }
 
-  mozilla::SharedFontList* GetFontFamilyListValue() const
+  mozilla::NotNull<mozilla::SharedFontList*> GetFontFamilyListValue() const
   {
     MOZ_ASSERT(mUnit == eCSSUnit_FontFamilyList,
                "not a font family list value");
     NS_ASSERTION(mValue.mFontFamilyList != nullptr,
                  "font family list value should never be null");
-    return mValue.mFontFamilyList;
+    return mozilla::WrapNotNull(mValue.mFontFamilyList);
   }
 
   // bodies of these are below
   inline nsCSSValuePair& GetPairValue();
   inline const nsCSSValuePair& GetPairValue() const;
 
   inline nsCSSRect& GetRectValue();
   inline const nsCSSRect& GetRectValue() const;
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4371,17 +4371,17 @@ nsRuleNode::ComputeFontData(void* aStart
   // using the 'font' shorthand).
 
   // Figure out if we are a generic font
   uint8_t generic = kGenericFont_NONE;
   // XXXldb What if we would have had a string if we hadn't been doing
   // the optimization with a non-null aStartStruct?
   const nsCSSValue* familyValue = aRuleData->ValueForFontFamily();
   if (eCSSUnit_FontFamilyList == familyValue->GetUnit()) {
-    const SharedFontList* fontlist = familyValue->GetFontFamilyListValue();
+    NotNull<SharedFontList*> fontlist = familyValue->GetFontFamilyListValue();
     font->mFont.fontlist = FontFamilyList(fontlist);
 
     // if only a single generic, set the generic type
     if (fontlist->mNames.Length() == 1) {
       // extract the first generic in the fontlist, if exists
       switch (font->mFont.fontlist.FirstGeneric()) {
         case eFamily_serif:
           generic = kGenericFont_serif;
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -240,18 +240,17 @@ nsStyleUtil::AppendEscapedCSSFontFamilyL
     if (defaultGeneric != eFamily_none) {
       FontFamilyName(defaultGeneric).AppendToString(aResult);
     } else {
       NS_NOTREACHED("No fonts to serialize");
     }
     return;
   }
 
-  const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
-  AppendEscapedCSSFontFamilyList(fontlist, aResult);
+  AppendEscapedCSSFontFamilyList(aFamilyList.GetFontlist().get(), aResult);
 }
 
 
 /* static */ void
 nsStyleUtil::AppendBitmaskCSSValue(nsCSSPropertyID aProperty,
                                    int32_t aMaskedValue,
                                    int32_t aFirstMask,
                                    int32_t aLastMask,