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 435157 d49cf4aed3350dff668d64a2cf86ce64e6c7a2df
parent 435156 16c2166700f2bac39515b4c81f1b82aa8045fc22
child 435158 00f3a339b1976a9942a7ef04c9ac6a9d4204aee8
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersxidorn
bugs1397626
milestone58.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 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,