Bug 1514869 - patch 4 - Implement Linux/fontconfig backend for the shared font list. r=jwatt
authorJonathan Kew <jkew@mozilla.com>
Sat, 27 Apr 2019 15:38:28 +0000
changeset 471765 d51c979e9930e3f20d601149c0cab64d748b872c
parent 471764 c154853c599ae8fe77062a06475f6b7c5e6da226
child 471766 ba0672dcd82d09031cb5899716a5f90916438c42
push id84266
push userjkew@mozilla.com
push dateMon, 29 Apr 2019 15:21:39 +0000
treeherderautoland@7de7d6a0be86 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1514869
milestone68.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 1514869 - patch 4 - Implement Linux/fontconfig backend for the shared font list. r=jwatt Differential Revision: https://phabricator.services.mozilla.com/D22940
gfx/thebes/gfxFcPlatformFontList.cpp
gfx/thebes/gfxFcPlatformFontList.h
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/TimeStamp.h"
 #include "nsGkAtoms.h"
 #include "nsUnicodeProperties.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsXULAppAPI.h"
+#include "SharedFontList-impl.h"
 
 #include "mozilla/gfx/HelpersCairo.h"
 
 #include <fontconfig/fcfreetype.h>
 #include <dlfcn.h>
 #include <unistd.h>
 
 #ifdef MOZ_WIDGET_GTK
@@ -210,53 +211,59 @@ static FontStretch MapFcWidth(int aFcWid
     return FontStretch::Expanded();
   }
   if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
     return FontStretch::ExtraExpanded();
   }
   return FontStretch::UltraExpanded();
 }
 
+static void GetFontProperties(FcPattern* aFontPattern, WeightRange* aWeight,
+                              StretchRange* aStretch,
+                              SlantStyleRange* aSlantStyle) {
+  // weight
+  int weight;
+  if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) !=
+      FcResultMatch) {
+    weight = FC_WEIGHT_REGULAR;
+  }
+  *aWeight = WeightRange(MapFcWeight(weight));
+
+  // width
+  int width;
+  if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
+    width = FC_WIDTH_NORMAL;
+  }
+  *aStretch = StretchRange(MapFcWidth(width));
+
+  // italic
+  int slant;
+  if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
+    slant = FC_SLANT_ROMAN;
+  }
+  if (slant == FC_SLANT_OBLIQUE) {
+    *aSlantStyle = SlantStyleRange(FontSlantStyle::Oblique());
+  } else if (slant > 0) {
+    *aSlantStyle = SlantStyleRange(FontSlantStyle::Italic());
+  }
+}
+
 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
                                                FcPattern* aFontPattern,
                                                bool aIgnoreFcCharmap)
     : gfxFontEntry(aFaceName),
       mFontPattern(aFontPattern),
       mFTFace(nullptr),
       mFTFaceInitialized(false),
       mIgnoreFcCharmap(aIgnoreFcCharmap),
       mHasVariationsInitialized(false),
       mAspect(0.0),
       mFontData(nullptr),
       mLength(0) {
-  // italic
-  int slant;
-  if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
-    slant = FC_SLANT_ROMAN;
-  }
-  if (slant == FC_SLANT_OBLIQUE) {
-    mStyleRange = SlantStyleRange(FontSlantStyle::Oblique());
-  } else if (slant > 0) {
-    mStyleRange = SlantStyleRange(FontSlantStyle::Italic());
-  }
-
-  // weight
-  int weight;
-  if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) !=
-      FcResultMatch) {
-    weight = FC_WEIGHT_REGULAR;
-  }
-  mWeightRange = WeightRange(MapFcWeight(weight));
-
-  // width
-  int width;
-  if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
-    width = FC_WIDTH_NORMAL;
-  }
-  mStretchRange = StretchRange(MapFcWidth(width));
+  GetFontProperties(aFontPattern, &mWeightRange, &mStretchRange, &mStyleRange);
 }
 
 gfxFontEntry* gfxFontconfigFontEntry::Clone() const {
   MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
   return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
 }
 
 static FcPattern* CreatePatternForFace(FT_Face aFace) {
@@ -413,17 +420,27 @@ nsresult gfxFontconfigFontEntry::ReadCMA
     } else {
       rv = NS_ERROR_NOT_AVAILABLE;
     }
   }
 
   mHasCmapTable = NS_SUCCEEDED(rv);
   if (mHasCmapTable) {
     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
-    mCharacterMap = pfl->FindCharMap(charmap);
+    fontlist::FontList* sharedFontList = pfl->SharedFontList();
+    if (!IsUserFont() && mShmemFace) {
+      mShmemFace->SetCharacterMap(sharedFontList, charmap);  // async
+      if (!TrySetShmemCharacterMap()) {
+        // Temporarily retain charmap, until the shared version is
+        // ready for use.
+        mCharacterMap = charmap;
+      }
+    } else {
+      mCharacterMap = pfl->FindCharMap(charmap);
+    }
   } else {
     // if error occurred, initialize to null cmap
     mCharacterMap = new gfxCharacterMap();
   }
 
   LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
                 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
                 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
@@ -919,27 +936,31 @@ static double ChooseFontSize(gfxFontconf
       return requestedSize;
     }
   }
   return bestSize;
 }
 
 gfxFont* gfxFontconfigFontEntry::CreateFontInstance(
     const gfxFontStyle* aFontStyle) {
+  FcPattern* fontPattern = mFontPattern;
+  if (!fontPattern) {
+    return nullptr;
+  }
+
   nsAutoRef<FcPattern> pattern(FcPatternCreate());
   if (!pattern) {
     NS_WARNING("Failed to create Fontconfig pattern for font instance");
     return nullptr;
   }
 
   double size = ChooseFontSize(this, *aFontStyle);
   FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
 
   FT_Face face = mFTFace;
-  FcPattern* fontPattern = mFontPattern;
   if (face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
     // For variation fonts, we create a new FT_Face and FcPattern here
     // so that variation coordinates from the style can be applied
     // without affecting other font instances created from the same
     // entry (font resource).
     if (mFontData) {
       // For user fonts: create a new FT_Face from the font data, and then
       // make a pattern from that.
@@ -1623,39 +1644,225 @@ void gfxFcPlatformFontList::ReadSystemFo
   // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
   // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
   // an older version, we manually append it to the unparsed pattern.
   if (FcGetVersion() < 20900) {
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
       auto family = static_cast<gfxFontconfigFontFamily*>(iter.Data().get());
       family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
         char* s = (char*)FcNameUnparse(aPat);
-        nsAutoCString patternStr(s);
-        free(s);
+        nsDependentCString patternStr(s);
         if (FcResultMatch ==
             FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&s)) {
           patternStr.Append(":file=");
           patternStr.Append(s);
         }
         retValue->AppendElement(FontPatternListEntry(patternStr, aAppFonts));
+        free(s);
       });
     }
   } else {
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
       auto family = static_cast<gfxFontconfigFontFamily*>(iter.Data().get());
       family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
         char* s = (char*)FcNameUnparse(aPat);
         nsDependentCString patternStr(s);
         retValue->AppendElement(FontPatternListEntry(patternStr, aAppFonts));
         free(s);
       });
     }
   }
 }
 
+void gfxFcPlatformFontList::InitSharedFontListForPlatform() {
+  mLocalNames.Clear();
+  mFcSubstituteCache.Clear();
+
+  mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
+  mOtherFamilyNamesInitialized = true;
+
+  if (!XRE_IsParentProcess()) {
+    // Content processes will access the shared-memory data created by the
+    // parent, so they do not need to query fontconfig for the available
+    // fonts themselves.
+    return;
+  }
+
+#ifdef MOZ_BUNDLED_FONTS
+  ActivateBundledFonts();
+#endif
+
+  mLastConfig = FcConfigGetCurrent();
+
+  UniquePtr<SandboxPolicy> policy;
+
+#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
+  // If read sandboxing is enabled, create a temporary SandboxPolicy to
+  // check font paths; use a fake PID to avoid picking up any PID-specific
+  // rules by accident.
+  SandboxBrokerPolicyFactory policyFactory;
+  if (GetEffectiveContentSandboxLevel() > 2 &&
+      !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
+    policy = policyFactory.GetContentPolicy(-1, false);
+  }
+#endif
+
+  nsTArray<fontlist::Family::InitData> families;
+
+  nsClassHashtable<nsCStringHashKey, nsTArray<fontlist::Face::InitData>> faces;
+
+  auto addPattern = [this, &families, &faces](
+                        FcPattern* aPattern, FcChar8*& aLastFamilyName,
+                        nsCString& aFamilyName, bool aAppFont) -> void {
+    // get canonical name
+    uint32_t cIndex = FindCanonicalNameIndex(aPattern, FC_FAMILYLANG);
+    FcChar8* canonical = nullptr;
+    FcPatternGetString(aPattern, FC_FAMILY, cIndex, &canonical);
+    if (!canonical) {
+      return;
+    }
+
+    // same as the last one? definitely no need to add a new family
+    if (FcStrCmp(canonical, aLastFamilyName) != 0) {
+      aLastFamilyName = canonical;
+
+      // add new family if one doesn't already exist
+      aFamilyName = ToCharPtr(canonical);
+      nsAutoCString keyName(aFamilyName);
+      ToLowerCase(keyName);
+
+      auto faceList = faces.Get(keyName);
+      if (!faceList) {
+        faceList = new nsTArray<fontlist::Face::InitData>;
+        faces.Put(keyName, faceList);
+
+        /* TODO:
+        // Add pointers to other localized family names. Most fonts
+        // only have a single name, so the first call to GetString
+        // will usually not match
+        FcChar8* otherName;
+        int n = (cIndex == 0 ? 1 : 0);
+        while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) ==
+               FcResultMatch) {
+          nsAutoCString otherFamilyName(ToCharPtr(otherName));
+          AddOtherFamilyName(aFontFamily, otherFamilyName);
+          n++;
+          if (n == int(cIndex)) {
+            n++;  // skip over canonical name
+          }
+        }
+        */
+
+        families.AppendElement(fontlist::Family::InitData(
+            keyName, aFamilyName, /*index*/ 0, /*hidden*/ false,
+            /*bundled*/ aAppFont, /*badUnderline*/ false));
+      }
+    }
+
+    char* s = (char*)FcNameUnparse(aPattern);
+    nsAutoCString descriptor(s);
+    free(s);
+
+    WeightRange weight(FontWeight::Normal());
+    StretchRange stretch(FontStretch::Normal());
+    SlantStyleRange style(FontSlantStyle::Normal());
+    GetFontProperties(aPattern, &weight, &stretch, &style);
+
+    nsAutoCString keyName(aFamilyName);
+    ToLowerCase(keyName);
+    auto faceList = faces.Get(keyName);
+    uint32_t faceIndex = faceList->Length();
+    faceList->AppendElement(
+        fontlist::Face::InitData{descriptor, 0, false, weight, stretch, style});
+    // map the psname, fullname ==> font family for local font lookups
+    nsAutoCString psname, fullname;
+    GetFaceNames(aPattern, aFamilyName, psname, fullname);
+    if (!psname.IsEmpty()) {
+      ToLowerCase(psname);
+      mLocalNameTable.Put(psname,
+                          fontlist::LocalFaceRec::InitData(keyName, faceIndex));
+    }
+    if (!fullname.IsEmpty()) {
+      ToLowerCase(fullname);
+      if (fullname != psname) {
+        mLocalNameTable.Put(
+            fullname, fontlist::LocalFaceRec::InitData(keyName, faceIndex));
+      }
+    }
+  };
+
+  auto addFontSetFamilies = [&addPattern](FcFontSet* aFontSet,
+                                          SandboxPolicy* aPolicy,
+                                          bool aAppFonts) -> void {
+    FcChar8* lastFamilyName = (FcChar8*)"";
+    RefPtr<gfxFontconfigFontFamily> fontFamily;
+    nsAutoCString familyName;
+    for (int f = 0; f < aFontSet->nfont; f++) {
+      FcPattern* pattern = aFontSet->fonts[f];
+
+      // Skip any fonts that aren't readable for us (e.g. due to restrictive
+      // file ownership/permissions).
+      FcChar8* path;
+      if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
+        continue;
+      }
+      if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
+        continue;
+      }
+
+#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
+      // Skip any fonts that will be blocked by the content-process sandbox
+      // policy.
+      if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
+                       SandboxBroker::Perms::MAY_READ)) {
+        continue;
+      }
+#endif
+
+      addPattern(pattern, lastFamilyName, familyName, aAppFonts);
+    }
+  };
+
+  // iterate over available fonts
+  FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
+  addFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
+
+#ifdef MOZ_BUNDLED_FONTS
+  FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
+  addFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
+#endif
+
+  ApplyWhitelist(families);
+  families.Sort();
+
+  mozilla::fontlist::FontList* list = SharedFontList();
+  list->SetFamilyNames(families);
+
+  for (uint32_t i = 0; i < families.Length(); i++) {
+    list->Families()[i].AddFaces(list, *faces.Get(families[i].mKey));
+  }
+}
+
+gfxFontEntry* gfxFcPlatformFontList::CreateFontEntry(
+    fontlist::Face* aFace, const fontlist::Family* aFamily) {
+  fontlist::FontList* list = SharedFontList();
+  nsAutoCString desc(aFace->mDescriptor.AsString(list));
+  FcPattern* pattern = FcNameParse((const FcChar8*)desc.get());
+  gfxFontEntry* fe = new gfxFontconfigFontEntry(desc, pattern, true);
+  FcPatternDestroy(pattern);
+  fe->mStyleRange = aFace->mStyle;
+  fe->mWeightRange = aFace->mWeight;
+  fe->mStretchRange = aFace->mStretch;
+  fe->mFixedPitch = aFace->mFixedPitch;
+  fe->mIsBadUnderlineFont = aFamily->IsBadUnderlineFamily();
+  fe->mShmemFace = aFace;
+  fe->mFamilyName = aFamily->DisplayName().AsString(SharedFontList());
+  return fe;
+}
+
 // For displaying the fontlist in UI, use explicit call to FcFontList. Using
 // FcFontList results in the list containing the localized names as dictated
 // by system defaults.
 static void GetSystemFontList(nsTArray<nsString>& aListOfFonts,
                               nsAtom* aLangGroup) {
   aListOfFonts.Clear();
 
   nsAutoRef<FcPattern> pat(FcPatternCreate());
@@ -1752,16 +1959,21 @@ FontFamily gfxFcPlatformFontList::GetDef
 }
 
 gfxFontEntry* gfxFcPlatformFontList::LookupLocalFont(
     const nsACString& aFontName, WeightRange aWeightForEntry,
     StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
   nsAutoCString keyName(aFontName);
   ToLowerCase(keyName);
 
+  if (SharedFontList()) {
+    return LookupInSharedFaceNameList(aFontName, aWeightForEntry,
+                                      aStretchForEntry, aStyleForEntry);
+  }
+
   // if name is not in the global list, done
   FcPattern* fontPattern = mLocalNames.Get(keyName);
   if (!fontPattern) {
     return nullptr;
   }
 
   return new gfxFontconfigFontEntry(aFontName, fontPattern, aWeightForEntry,
                                     aStretchForEntry, aStyleForEntry);
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -265,23 +265,28 @@ class gfxFcPlatformFontList : public gfx
   gfxFcPlatformFontList();
 
   static gfxFcPlatformFontList* PlatformFontList() {
     return static_cast<gfxFcPlatformFontList*>(sPlatformFontList);
   }
 
   // initialize font lists
   nsresult InitFontListForPlatform() override;
+  void InitSharedFontListForPlatform() override;
 
   void GetFontList(nsAtom* aLangGroup, const nsACString& aGenericFamily,
                    nsTArray<nsString>& aListOfFonts) override;
 
   void ReadSystemFontList(
       InfallibleTArray<mozilla::dom::SystemFontListEntry>* retValue);
 
+  gfxFontEntry* CreateFontEntry(
+      mozilla::fontlist::Face* aFace,
+      const mozilla::fontlist::Family* aFamily) override;
+
   gfxFontEntry* LookupLocalFont(const nsACString& aFontName,
                                 WeightRange aWeightForEntry,
                                 StretchRange aStretchForEntry,
                                 SlantStyleRange aStyleForEntry) override;
 
   gfxFontEntry* MakePlatformFont(const nsACString& aFontName,
                                  WeightRange aWeightForEntry,
                                  StretchRange aStretchForEntry,