tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxFcPlatformFontList.cpp (107947B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "mozilla/Logging.h"
      7 
      8 #include "gfxFcPlatformFontList.h"
      9 #include "gfxFont.h"
     10 #include "gfxFontConstants.h"
     11 #include "gfxFT2Utils.h"
     12 #include "gfxPlatform.h"
     13 #include "nsPresContext.h"
     14 #include "mozilla/dom/ContentChild.h"
     15 #include "mozilla/dom/ContentParent.h"
     16 #include "mozilla/Preferences.h"
     17 #include "mozilla/Sprintf.h"
     18 #include "mozilla/StaticPrefs_gfx.h"
     19 #include "mozilla/StaticPrefs_mathml.h"
     20 #include "mozilla/glean/GfxMetrics.h"
     21 #include "mozilla/TimeStamp.h"
     22 #include "nsGkAtoms.h"
     23 #include "nsIConsoleService.h"
     24 #include "nsIGfxInfo.h"
     25 #include "mozilla/Components.h"
     26 #include "nsString.h"
     27 #include "nsStringFwd.h"
     28 #include "nsUnicodeProperties.h"
     29 #include "nsDirectoryServiceUtils.h"
     30 #include "nsDirectoryServiceDefs.h"
     31 #include "nsAppDirectoryServiceDefs.h"
     32 #include "nsCharSeparatedTokenizer.h"
     33 #include "nsXULAppAPI.h"
     34 #include "SharedFontList-impl.h"
     35 #ifndef BASE_BROWSER_VERSION
     36 #  define StandardFonts
     37 #  include "StandardFonts-linux.inc"
     38 #  undef StandardFonts
     39 #endif
     40 #include "mozilla/intl/Locale.h"
     41 
     42 #include "mozilla/gfx/HelpersCairo.h"
     43 
     44 #include <cairo-ft.h>
     45 #include <fontconfig/fcfreetype.h>
     46 #include <fontconfig/fontconfig.h>
     47 #include <harfbuzz/hb.h>
     48 #include <dlfcn.h>
     49 #include <unistd.h>
     50 
     51 #ifdef MOZ_WIDGET_GTK
     52 #  include <gdk/gdk.h>
     53 #  include <gtk/gtk.h>
     54 #  include "gfxPlatformGtk.h"
     55 #  include "mozilla/WidgetUtilsGtk.h"
     56 #endif
     57 
     58 #ifdef MOZ_X11
     59 #  include "mozilla/X11Util.h"
     60 #endif
     61 
     62 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
     63 #  include "mozilla/SandboxBrokerPolicyFactory.h"
     64 #  include "mozilla/SandboxSettings.h"
     65 #endif
     66 
     67 #include FT_MULTIPLE_MASTERS_H
     68 
     69 using namespace mozilla;
     70 using namespace mozilla::gfx;
     71 using namespace mozilla::unicode;
     72 using namespace mozilla::intl;
     73 
     74 #ifndef FC_POSTSCRIPT_NAME
     75 #  define FC_POSTSCRIPT_NAME "postscriptname" /* String */
     76 #endif
     77 #ifndef FC_VARIABLE
     78 #  define FC_VARIABLE "variable" /* Bool */
     79 #endif
     80 #ifndef FC_NAMED_INSTANCE
     81 #  define FC_NAMED_INSTANCE "namedinstance" /* Bool */
     82 #endif
     83 
     84 #define PRINTING_FC_PROPERTY "gfx.printing"
     85 
     86 #define LOG_FONTLIST(args) \
     87  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
     88 #define LOG_FONTLIST_ENABLED() \
     89  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
     90 #define LOG_CMAPDATA_ENABLED() \
     91  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
     92 
     93 static const FcChar8* ToFcChar8Ptr(const char* aStr) {
     94  return reinterpret_cast<const FcChar8*>(aStr);
     95 }
     96 
     97 static const char* ToCharPtr(const FcChar8* aStr) {
     98  return reinterpret_cast<const char*>(aStr);
     99 }
    100 
    101 // canonical name ==> first en name or first name if no en name
    102 // This is the required logic for fullname lookups as per CSS3 Fonts spec.
    103 static uint32_t FindCanonicalNameIndex(FcPattern* aFont,
    104                                       const char* aLangField) {
    105  uint32_t n = 0, en = 0;
    106  FcChar8* lang;
    107  while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
    108    // look for 'en' or variants, en-US, en-JP etc.
    109    uint32_t len = strlen(ToCharPtr(lang));
    110    bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
    111    if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
    112      en = n;
    113      break;
    114    }
    115    n++;
    116  }
    117  return en;
    118 }
    119 
    120 static void GetFaceNames(FcPattern* aFont, const nsACString& aFamilyName,
    121                         nsACString& aPostscriptName, nsACString& aFullname) {
    122  // get the Postscript name
    123  FcChar8* psname;
    124  if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) ==
    125      FcResultMatch) {
    126    aPostscriptName = ToCharPtr(psname);
    127  }
    128 
    129  // get the canonical fullname (i.e. en name or first name)
    130  uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
    131  FcChar8* fullname;
    132  if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
    133    aFullname = ToCharPtr(fullname);
    134  }
    135 
    136  // if have fullname, done
    137  if (!aFullname.IsEmpty()) {
    138    return;
    139  }
    140 
    141  // otherwise, set the fullname to family + style name [en] and use that
    142  aFullname = aFamilyName;
    143 
    144  // figure out the en style name
    145  en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
    146  nsAutoCString style;
    147  FcChar8* stylename = nullptr;
    148  FcPatternGetString(aFont, FC_STYLE, en, &stylename);
    149  if (stylename) {
    150    style = ToCharPtr(stylename);
    151  }
    152 
    153  if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
    154    aFullname.Append(' ');
    155    aFullname.Append(style);
    156  }
    157 }
    158 
    159 static FontWeight MapFcWeight(int aFcWeight) {
    160  if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
    161    return FontWeight::FromInt(100);
    162  }
    163  if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
    164    return FontWeight::FromInt(200);
    165  }
    166  if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
    167    return FontWeight::FromInt(300);
    168  }
    169  if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
    170    // This includes FC_WEIGHT_BOOK
    171    return FontWeight::FromInt(400);
    172  }
    173  if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
    174    return FontWeight::FromInt(500);
    175  }
    176  if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
    177    return FontWeight::FromInt(600);
    178  }
    179  if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
    180    return FontWeight::FromInt(700);
    181  }
    182  if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
    183    return FontWeight::FromInt(800);
    184  }
    185  if (aFcWeight <= FC_WEIGHT_BLACK) {
    186    return FontWeight::FromInt(900);
    187  }
    188 
    189  // including FC_WEIGHT_EXTRABLACK
    190  return FontWeight::FromInt(901);
    191 }
    192 
    193 // TODO(emilio, jfkthame): I think this can now be more fine-grained.
    194 static FontStretch MapFcWidth(int aFcWidth) {
    195  if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
    196    return FontStretch::ULTRA_CONDENSED;
    197  }
    198  if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
    199    return FontStretch::EXTRA_CONDENSED;
    200  }
    201  if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
    202    return FontStretch::CONDENSED;
    203  }
    204  if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
    205    return FontStretch::SEMI_CONDENSED;
    206  }
    207  if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
    208    return FontStretch::NORMAL;
    209  }
    210  if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
    211    return FontStretch::SEMI_EXPANDED;
    212  }
    213  if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
    214    return FontStretch::EXPANDED;
    215  }
    216  if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
    217    return FontStretch::EXTRA_EXPANDED;
    218  }
    219  return FontStretch::ULTRA_EXPANDED;
    220 }
    221 
    222 static void GetFontProperties(FcPattern* aFontPattern, WeightRange* aWeight,
    223                              StretchRange* aStretch,
    224                              SlantStyleRange* aSlantStyle,
    225                              uint16_t* aSize = nullptr) {
    226  // weight
    227  int weight;
    228  if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) !=
    229      FcResultMatch) {
    230    weight = FC_WEIGHT_REGULAR;
    231  }
    232  *aWeight = WeightRange(MapFcWeight(weight));
    233 
    234  // width
    235  int width;
    236  if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
    237    width = FC_WIDTH_NORMAL;
    238  }
    239  *aStretch = StretchRange(MapFcWidth(width));
    240 
    241  // italic
    242  int slant;
    243  if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
    244    slant = FC_SLANT_ROMAN;
    245  }
    246  if (slant == FC_SLANT_OBLIQUE) {
    247    *aSlantStyle = SlantStyleRange(FontSlantStyle::OBLIQUE);
    248  } else if (slant > 0) {
    249    *aSlantStyle = SlantStyleRange(FontSlantStyle::ITALIC);
    250  }
    251 
    252  if (aSize) {
    253    // pixel size, or zero if scalable
    254    FcBool scalable;
    255    if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
    256            FcResultMatch &&
    257        scalable) {
    258      *aSize = 0;
    259    } else {
    260      double size;
    261      if (FcPatternGetDouble(aFontPattern, FC_PIXEL_SIZE, 0, &size) ==
    262          FcResultMatch) {
    263        *aSize = uint16_t(NS_round(size));
    264      } else {
    265        *aSize = 0;
    266      }
    267    }
    268  }
    269 }
    270 
    271 void gfxFontconfigFontEntry::GetUserFontFeatures(FcPattern* aPattern) {
    272  int fontFeaturesNum = 0;
    273  char* s;
    274  hb_feature_t tmpFeature;
    275  while (FcResultMatch == FcPatternGetString(aPattern, "fontfeatures",
    276                                             fontFeaturesNum, (FcChar8**)&s)) {
    277    bool ret = hb_feature_from_string(s, -1, &tmpFeature);
    278    if (ret) {
    279      mFeatureSettings.AppendElement(
    280          (gfxFontFeature){tmpFeature.tag, tmpFeature.value});
    281    }
    282    fontFeaturesNum++;
    283  }
    284 }
    285 
    286 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
    287                                               FcPattern* aFontPattern,
    288                                               bool aIgnoreFcCharmap)
    289    : gfxFT2FontEntryBase(aFaceName),
    290      mFontPattern(aFontPattern),
    291      mFTFaceInitialized(false),
    292      mIgnoreFcCharmap(aIgnoreFcCharmap) {
    293  GetFontProperties(aFontPattern, &mWeightRange, &mStretchRange, &mStyleRange);
    294  GetUserFontFeatures(mFontPattern);
    295 }
    296 
    297 gfxFontEntry* gfxFontconfigFontEntry::Clone() const {
    298  MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
    299  return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
    300 }
    301 
    302 static already_AddRefed<FcPattern> CreatePatternForFace(FT_Face aFace) {
    303  // Use fontconfig to fill out the pattern from the FTFace.
    304  // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
    305  // least). The dummy file passed here is removed below.
    306  //
    307  // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
    308  // is passed as the "blanks" argument, which provides that unexpectedly
    309  // blank glyphs are elided.  Here, however, we pass nullptr for
    310  // "blanks", effectively assuming that, if the font has a blank glyph,
    311  // then the author intends any associated character to be rendered
    312  // blank.
    313  RefPtr<FcPattern> pattern =
    314      dont_AddRef(FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr));
    315  // given that we have a FT_Face, not really sure this is possible...
    316  if (!pattern) {
    317    pattern = dont_AddRef(FcPatternCreate());
    318  }
    319  FcPatternDel(pattern, FC_FILE);
    320  FcPatternDel(pattern, FC_INDEX);
    321 
    322  // Make a new pattern and store the face in it so that cairo uses
    323  // that when creating a cairo font face.
    324  FcPatternAddFTFace(pattern, FC_FT_FACE, aFace);
    325 
    326  return pattern.forget();
    327 }
    328 
    329 static already_AddRefed<SharedFTFace> CreateFaceForPattern(
    330    FcPattern* aPattern) {
    331  FcChar8* filename;
    332  if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) {
    333    return nullptr;
    334  }
    335  int index;
    336  if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
    337    index = 0;  // default to 0 if not found in pattern
    338  }
    339  return Factory::NewSharedFTFace(nullptr, ToCharPtr(filename), index);
    340 }
    341 
    342 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
    343                                               WeightRange aWeight,
    344                                               StretchRange aStretch,
    345                                               SlantStyleRange aStyle,
    346                                               RefPtr<SharedFTFace>&& aFace)
    347    : gfxFT2FontEntryBase(aFaceName),
    348      mFontPattern(CreatePatternForFace(aFace->GetFace())),
    349      mFTFace(aFace.forget().take()),
    350      mFTFaceInitialized(true),
    351      mIgnoreFcCharmap(true) {
    352  mWeightRange = aWeight;
    353  mStyleRange = aStyle;
    354  mStretchRange = aStretch;
    355  mIsDataUserFont = true;
    356 }
    357 
    358 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
    359                                               FcPattern* aFontPattern,
    360                                               WeightRange aWeight,
    361                                               StretchRange aStretch,
    362                                               SlantStyleRange aStyle)
    363    : gfxFT2FontEntryBase(aFaceName),
    364      mFontPattern(aFontPattern),
    365      mFTFaceInitialized(false) {
    366  mWeightRange = aWeight;
    367  mStyleRange = aStyle;
    368  mStretchRange = aStretch;
    369  mIsLocalUserFont = true;
    370 
    371  // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
    372  // via src:local()...
    373  // If the local font happens to come from the application fontset,
    374  // we want to set it to true so that color/svg fonts will work even
    375  // if the default glyphs are blank; but if the local font is a non-
    376  // sfnt face (e.g. legacy type 1) then we need to set it to false
    377  // because our cmap-reading code will fail and we depend on FT+Fc to
    378  // determine the coverage.
    379  // We set the flag here, but may flip it the first time TestCharacterMap
    380  // is called, at which point we'll look to see whether a 'cmap' is
    381  // actually present in the font.
    382  mIgnoreFcCharmap = true;
    383 
    384  GetUserFontFeatures(mFontPattern);
    385 }
    386 
    387 typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
    388 typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
    389 static GetVarFunc sGetVar;
    390 static DoneVarFunc sDoneVar;
    391 static bool sInitializedVarFuncs = false;
    392 
    393 static void InitializeVarFuncs() {
    394  if (sInitializedVarFuncs) {
    395    return;
    396  }
    397  sInitializedVarFuncs = true;
    398 #if MOZ_TREE_FREETYPE
    399  sGetVar = &FT_Get_MM_Var;
    400  sDoneVar = &FT_Done_MM_Var;
    401 #else
    402  sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
    403  sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
    404 #endif
    405 }
    406 
    407 gfxFontconfigFontEntry::~gfxFontconfigFontEntry() {
    408  if (mMMVar) {
    409    // Prior to freetype 2.9, there was no specific function to free the
    410    // FT_MM_Var record, and the docs just said to use free().
    411    // InitializeVarFuncs must have been called in order for mMMVar to be
    412    // non-null here, so we don't need to do it again.
    413    if (sDoneVar) {
    414      auto ftFace = GetFTFace();
    415      MOZ_ASSERT(ftFace, "How did mMMVar get set without a face?");
    416      (*sDoneVar)(ftFace->GetFace()->glyph->library, mMMVar);
    417    } else {
    418      free(mMMVar);
    419    }
    420  }
    421  if (mFTFaceInitialized) {
    422    auto face = mFTFace.exchange(nullptr);
    423    NS_IF_RELEASE(face);
    424  }
    425 }
    426 
    427 nsresult gfxFontconfigFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
    428  // attempt this once, if errors occur leave a blank cmap
    429  if (mCharacterMap) {
    430    return NS_OK;
    431  }
    432 
    433  RefPtr<gfxCharacterMap> charmap;
    434  nsresult rv;
    435 
    436  uint32_t uvsOffset = 0;
    437  if (aFontInfoData &&
    438      (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) {
    439    rv = NS_OK;
    440  } else {
    441    uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
    442    charmap = new gfxCharacterMap(256);
    443    AutoTable cmapTable(this, kCMAP);
    444 
    445    if (cmapTable) {
    446      uint32_t cmapLen;
    447      const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
    448          hb_blob_get_data(cmapTable, &cmapLen));
    449      rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, uvsOffset);
    450    } else {
    451      rv = NS_ERROR_NOT_AVAILABLE;
    452    }
    453  }
    454  mUVSOffset.exchange(uvsOffset);
    455 
    456  bool setCharMap = true;
    457  if (NS_SUCCEEDED(rv)) {
    458    gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
    459    fontlist::FontList* sharedFontList = pfl->SharedFontList();
    460    if (!IsUserFont() && mShmemFace) {
    461      mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily);
    462      if (TrySetShmemCharacterMap()) {
    463        setCharMap = false;
    464      }
    465    } else {
    466      charmap = pfl->FindCharMap(charmap);
    467    }
    468    mHasCmapTable = true;
    469  } else {
    470    // if error occurred, initialize to null cmap
    471    charmap = new gfxCharacterMap(0);
    472    mHasCmapTable = false;
    473  }
    474  if (setCharMap) {
    475    if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
    476      charmap.get()->AddRef();
    477    }
    478  }
    479 
    480  LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
    481                mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
    482                charmap->mHash, mCharacterMap == charmap ? " new" : ""));
    483  if (LOG_CMAPDATA_ENABLED()) {
    484    char prefix[256];
    485    SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
    486    charmap->Dump(prefix, eGfxLog_cmapdata);
    487  }
    488 
    489  return rv;
    490 }
    491 
    492 static bool HasChar(FcPattern* aFont, FcChar32 aCh) {
    493  FcCharSet* charset = nullptr;
    494  FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
    495  return charset && FcCharSetHasChar(charset, aCh);
    496 }
    497 
    498 bool gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh) {
    499  // For user fonts, or for fonts bundled with the app (which might include
    500  // color/svg glyphs where the default glyphs may be blank, and thus confuse
    501  // fontconfig/freetype's char map checking), we instead check the cmap
    502  // directly for character coverage.
    503  if (mIgnoreFcCharmap) {
    504    // If it does not actually have a cmap, switch our strategy to use
    505    // fontconfig's charmap after all (except for data fonts, which must
    506    // always have a cmap to have passed OTS validation).
    507    if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'))) {
    508      mIgnoreFcCharmap = false;
    509      // ...and continue with HasChar() below.
    510    } else {
    511      return gfxFontEntry::TestCharacterMap(aCh);
    512    }
    513  }
    514  // otherwise (for system fonts), use the charmap in the pattern
    515  return HasChar(mFontPattern, aCh);
    516 }
    517 
    518 bool gfxFontconfigFontEntry::HasFontTable(uint32_t aTableTag) {
    519  if (FTUserFontData* ufd = GetUserFontData()) {
    520    if (ufd->FontData()) {
    521      return !!gfxFontUtils::FindTableDirEntry(ufd->FontData(), aTableTag);
    522    }
    523  }
    524  return gfxFT2FontEntryBase::FaceHasTable(GetFTFace(), aTableTag);
    525 }
    526 
    527 hb_blob_t* gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag) {
    528  // for data fonts, read directly from the font data
    529  if (FTUserFontData* ufd = GetUserFontData()) {
    530    if (ufd->FontData()) {
    531      return gfxFontUtils::GetTableFromFontData(ufd->FontData(), aTableTag);
    532    }
    533  }
    534 
    535  return gfxFontEntry::GetFontTable(aTableTag);
    536 }
    537 
    538 double gfxFontconfigFontEntry::GetAspect(uint8_t aSizeAdjustBasis) {
    539  using FontSizeAdjust = gfxFont::FontSizeAdjust;
    540  if (FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::ExHeight ||
    541      FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::CapHeight) {
    542    // try to compute aspect from OS/2 metrics if available
    543    AutoTable os2Table(this, TRUETYPE_TAG('O', 'S', '/', '2'));
    544    if (os2Table) {
    545      uint16_t upem = UnitsPerEm();
    546      if (upem != kInvalidUPEM) {
    547        uint32_t len;
    548        const auto* os2 =
    549            reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
    550        if (uint16_t(os2->version) >= 2) {
    551          // XXX(jfkthame) Other implementations don't have the check for
    552          // values <= 0.1em; should we drop that here? Just require it to be
    553          // a positive number?
    554          if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
    555              FontSizeAdjust::Tag::ExHeight) {
    556            if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
    557                int16_t(os2->sxHeight) > 0.1 * upem) {
    558              return double(int16_t(os2->sxHeight)) / upem;
    559            }
    560          }
    561          if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
    562              FontSizeAdjust::Tag::CapHeight) {
    563            if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
    564                int16_t(os2->sCapHeight) > 0.1 * upem) {
    565              return double(int16_t(os2->sCapHeight)) / upem;
    566            }
    567          }
    568        }
    569      }
    570    }
    571  }
    572 
    573  // create a font to calculate the requested aspect
    574  gfxFontStyle s;
    575  s.size = 256.0;  // pick large size to reduce hinting artifacts
    576  RefPtr<gfxFont> font = FindOrMakeFont(&s);
    577  if (font) {
    578    const gfxFont::Metrics& metrics =
    579        font->GetMetrics(nsFontMetrics::eHorizontal);
    580    if (metrics.emHeight == 0) {
    581      return 0;
    582    }
    583    switch (FontSizeAdjust::Tag(aSizeAdjustBasis)) {
    584      case FontSizeAdjust::Tag::ExHeight:
    585        return metrics.xHeight / metrics.emHeight;
    586      case FontSizeAdjust::Tag::CapHeight:
    587        return metrics.capHeight / metrics.emHeight;
    588      case FontSizeAdjust::Tag::ChWidth:
    589        return metrics.zeroWidth > 0 ? metrics.zeroWidth / metrics.emHeight
    590                                     : 0.5;
    591      case FontSizeAdjust::Tag::IcWidth:
    592      case FontSizeAdjust::Tag::IcHeight: {
    593        bool vertical = FontSizeAdjust::Tag(aSizeAdjustBasis) ==
    594                        FontSizeAdjust::Tag::IcHeight;
    595        gfxFloat advance =
    596            font->GetCharAdvance(gfxFont::kWaterIdeograph, vertical);
    597        return advance > 0 ? advance / metrics.emHeight : 1.0;
    598      }
    599      default:
    600        break;
    601    }
    602  }
    603 
    604  MOZ_ASSERT_UNREACHABLE("failed to compute size-adjust aspect");
    605  return 0.5;
    606 }
    607 
    608 static void PrepareFontOptions(FcPattern* aPattern, int* aOutLoadFlags,
    609                               unsigned int* aOutSynthFlags) {
    610  int loadFlags = FT_LOAD_DEFAULT;
    611  unsigned int synthFlags = 0;
    612 
    613  // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
    614 
    615  FcBool printing;
    616  if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
    617      FcResultMatch) {
    618    printing = FcFalse;
    619  }
    620 
    621  // Font options are set explicitly here to improve cairo's caching
    622  // behavior and to record the relevant parts of the pattern so that
    623  // the pattern can be released.
    624  //
    625  // Most font_options have already been set as defaults on the FcPattern
    626  // with cairo_ft_font_options_substitute(), then user and system
    627  // fontconfig configurations were applied.  The resulting font_options
    628  // have been recorded on the face during
    629  // cairo_ft_font_face_create_for_pattern().
    630  //
    631  // None of the settings here cause this scaled_font to behave any
    632  // differently from how it would behave if it were created from the same
    633  // face with default font_options.
    634  //
    635  // We set options explicitly so that the same scaled_font will be found in
    636  // the cairo_scaled_font_map when cairo loads glyphs from a context with
    637  // the same font_face, font_matrix, ctm, and surface font_options.
    638  //
    639  // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
    640  // font_options on the cairo_ft_font_face, and doesn't consider default
    641  // option values to not match any explicit values.
    642  //
    643  // Even after cairo_set_scaled_font is used to set font_options for the
    644  // cairo context, when cairo looks for a scaled_font for the context, it
    645  // will look for a font with some option values from the target surface if
    646  // any values are left default on the context font_options.  If this
    647  // scaled_font is created with default font_options, cairo will not find
    648  // it.
    649  //
    650  // The one option not recorded in the pattern is hint_metrics, which will
    651  // affect glyph metrics.  The default behaves as CAIRO_HINT_METRICS_ON.
    652  // We should be considering the font_options of the surface on which this
    653  // font will be used, but currently we don't have different gfxFonts for
    654  // different surface font_options, so we'll create a font suitable for the
    655  // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
    656 
    657  // The remaining options have been recorded on the pattern and the face.
    658  // _cairo_ft_options_merge has some logic to decide which options from the
    659  // scaled_font or from the cairo_ft_font_face take priority in the way the
    660  // font behaves.
    661  //
    662  // In the majority of cases, _cairo_ft_options_merge uses the options from
    663  // the cairo_ft_font_face, so sometimes it is not so important which
    664  // values are set here so long as they are not defaults, but we'll set
    665  // them to the exact values that we expect from the font, to be consistent
    666  // and to protect against changes in cairo.
    667  //
    668  // In some cases, _cairo_ft_options_merge uses some options from the
    669  // scaled_font's font_options rather than options on the
    670  // cairo_ft_font_face (from fontconfig).
    671  // https://bugs.freedesktop.org/show_bug.cgi?id=11838
    672  //
    673  // Surface font options were set on the pattern in
    674  // cairo_ft_font_options_substitute.  If fontconfig has changed the
    675  // hint_style then that is what the user (or distribution) wants, so we
    676  // use the setting from the FcPattern.
    677  //
    678  // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
    679  FcBool hinting = FcFalse;
    680  if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
    681    hinting = FcTrue;
    682  }
    683 
    684  int fc_hintstyle = FC_HINT_NONE;
    685  if (!printing && hinting &&
    686      FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &fc_hintstyle) !=
    687          FcResultMatch) {
    688    fc_hintstyle = FC_HINT_FULL;
    689  }
    690  switch (fc_hintstyle) {
    691    case FC_HINT_NONE:
    692      loadFlags = FT_LOAD_NO_HINTING;
    693      break;
    694    case FC_HINT_SLIGHT:
    695      loadFlags = FT_LOAD_TARGET_LIGHT;
    696      break;
    697  }
    698 
    699  FcBool fc_antialias;
    700  if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &fc_antialias) !=
    701      FcResultMatch) {
    702    fc_antialias = FcTrue;
    703  }
    704  if (!fc_antialias) {
    705    if (fc_hintstyle != FC_HINT_NONE) {
    706      loadFlags = FT_LOAD_TARGET_MONO;
    707    }
    708    loadFlags |= FT_LOAD_MONOCHROME;
    709  } else if (fc_hintstyle == FC_HINT_FULL) {
    710    int fc_rgba;
    711    if (FcPatternGetInteger(aPattern, FC_RGBA, 0, &fc_rgba) != FcResultMatch) {
    712      fc_rgba = FC_RGBA_UNKNOWN;
    713    }
    714    switch (fc_rgba) {
    715      case FC_RGBA_RGB:
    716      case FC_RGBA_BGR:
    717        loadFlags = FT_LOAD_TARGET_LCD;
    718        break;
    719      case FC_RGBA_VRGB:
    720      case FC_RGBA_VBGR:
    721        loadFlags = FT_LOAD_TARGET_LCD_V;
    722        break;
    723    }
    724  }
    725 
    726  if (!FcPatternAllowsBitmaps(aPattern, fc_antialias != FcFalse,
    727                              fc_hintstyle != FC_HINT_NONE)) {
    728    loadFlags |= FT_LOAD_NO_BITMAP;
    729  }
    730 
    731  FcBool autohint;
    732  if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch &&
    733      autohint) {
    734    loadFlags |= FT_LOAD_FORCE_AUTOHINT;
    735  }
    736 
    737  FcBool embolden;
    738  if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch &&
    739      embolden) {
    740    synthFlags |= CAIRO_FT_SYNTHESIZE_BOLD;
    741  }
    742 
    743  *aOutLoadFlags = loadFlags;
    744  *aOutSynthFlags = synthFlags;
    745 }
    746 
    747 #ifdef MOZ_X11
    748 static bool GetXftInt(Display* aDisplay, const char* aName, int* aResult) {
    749  if (!aDisplay) {
    750    return false;
    751  }
    752  char* value = XGetDefault(aDisplay, "Xft", aName);
    753  if (!value) {
    754    return false;
    755  }
    756  if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
    757    return true;
    758  }
    759  char* end;
    760  *aResult = strtol(value, &end, 0);
    761  if (end != value) {
    762    return true;
    763  }
    764  return false;
    765 }
    766 #endif
    767 
    768 static void PreparePattern(FcPattern* aPattern, bool aIsPrinterFont) {
    769  FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
    770 
    771  // This gets cairo_font_options_t for the Screen.  We should have
    772  // different font options for printing (no hinting) but we are not told
    773  // what we are measuring for.
    774  //
    775  // If cairo adds support for lcd_filter, gdk will not provide the default
    776  // setting for that option.  We could get the default setting by creating
    777  // an xlib surface once, recording its font_options, and then merging the
    778  // gdk options.
    779  //
    780  // Using an xlib surface would also be an option to get Screen font
    781  // options for non-GTK X11 toolkits, but less efficient than using GDK to
    782  // pick up dynamic changes.
    783  if (aIsPrinterFont) {
    784    cairo_font_options_t* options = cairo_font_options_create();
    785    cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
    786    cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
    787    cairo_ft_font_options_substitute(options, aPattern);
    788    cairo_font_options_destroy(options);
    789    FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
    790 #ifdef MOZ_WIDGET_GTK
    791  } else {
    792    gfxFcPlatformFontList::PlatformFontList()->SubstituteSystemFontOptions(
    793        aPattern);
    794 #endif  // MOZ_WIDGET_GTK
    795  }
    796 
    797  FcDefaultSubstitute(aPattern);
    798 }
    799 
    800 void gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
    801  if (aIndex > 0) {
    802    ThreadSafeWeakPtr<UnscaledFontFontconfig> front =
    803        std::move(mUnscaledFonts[aIndex]);
    804    for (size_t i = aIndex; i > 0; i--) {
    805      mUnscaledFonts[i] = std::move(mUnscaledFonts[i - 1]);
    806    }
    807    mUnscaledFonts[0] = std::move(front);
    808  }
    809 }
    810 
    811 already_AddRefed<UnscaledFontFontconfig>
    812 gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const std::string& aFile,
    813                                                  uint32_t aIndex) {
    814  for (size_t i = 0; i < kNumEntries; i++) {
    815    RefPtr<UnscaledFontFontconfig> entry(mUnscaledFonts[i]);
    816    if (entry && entry->GetFile() == aFile && entry->GetIndex() == aIndex) {
    817      MoveToFront(i);
    818      return entry.forget();
    819    }
    820  }
    821  return nullptr;
    822 }
    823 
    824 static inline gfxFloat SizeForStyle(gfxFontconfigFontEntry* aEntry,
    825                                    const gfxFontStyle& aStyle) {
    826  return StyleFontSizeAdjust::Tag(aStyle.sizeAdjustBasis) !=
    827                 StyleFontSizeAdjust::Tag::None
    828             ? aStyle.GetAdjustedSize(aEntry->GetAspect(aStyle.sizeAdjustBasis))
    829             : aStyle.size * aEntry->mSizeAdjust;
    830 }
    831 
    832 static double ChooseFontSize(gfxFontconfigFontEntry* aEntry,
    833                             const gfxFontStyle& aStyle) {
    834  double requestedSize = SizeForStyle(aEntry, aStyle);
    835  double bestDist = -1.0;
    836  double bestSize = requestedSize;
    837  double size;
    838  int v = 0;
    839  while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
    840         FcResultMatch) {
    841    ++v;
    842    double dist = fabs(size - requestedSize);
    843    if (bestDist < 0.0 || dist < bestDist) {
    844      bestDist = dist;
    845      bestSize = size;
    846    }
    847  }
    848  // If the font has bitmaps but wants to be scaled, then let it scale.
    849  if (bestSize >= 0.0) {
    850    FcBool scalable;
    851    if (FcPatternGetBool(aEntry->GetPattern(), FC_SCALABLE, 0, &scalable) ==
    852            FcResultMatch &&
    853        scalable) {
    854      return requestedSize;
    855    }
    856  }
    857  return bestSize;
    858 }
    859 
    860 gfxFont* gfxFontconfigFontEntry::CreateFontInstance(
    861    const gfxFontStyle* aFontStyle) {
    862  RefPtr<FcPattern> pattern = dont_AddRef(FcPatternCreate());
    863  if (!pattern) {
    864    NS_WARNING("Failed to create Fontconfig pattern for font instance");
    865    return nullptr;
    866  }
    867 
    868  double size = ChooseFontSize(this, *aFontStyle);
    869  FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
    870 
    871  RefPtr<SharedFTFace> face = GetFTFace();
    872  if (!face) {
    873    NS_WARNING("Failed to get FreeType face for pattern");
    874    return nullptr;
    875  }
    876  if (HasVariations()) {
    877    // For variation fonts, we create a new FT_Face here so that
    878    // variation coordinates from the style can be applied without
    879    // affecting other font instances created from the same entry
    880    // (font resource).
    881    // For user fonts: create a new FT_Face from the font data, and then make
    882    // a pattern from that.
    883    // For system fonts: create a new FT_Face and store it in a copy of the
    884    // original mFontPattern.
    885    RefPtr<SharedFTFace> varFace = face->GetData()
    886                                       ? face->GetData()->CloneFace()
    887                                       : CreateFaceForPattern(mFontPattern);
    888    if (varFace) {
    889      AutoTArray<gfxFontVariation, 8> settings;
    890      GetVariationsForStyle(settings, *aFontStyle);
    891      gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, varFace->GetFace());
    892      face = std::move(varFace);
    893    }
    894  }
    895 
    896  PreparePattern(pattern, aFontStyle->printerFont);
    897  RefPtr<FcPattern> renderPattern =
    898      dont_AddRef(FcFontRenderPrepare(nullptr, pattern, mFontPattern));
    899  if (!renderPattern) {
    900    NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
    901    return nullptr;
    902  }
    903 
    904  if (aFontStyle->NeedsSyntheticBold(this)) {
    905    FcPatternAddBool(renderPattern, FC_EMBOLDEN, FcTrue);
    906  }
    907 
    908  // will synthetic oblique be applied using a transform?
    909  if (IsUpright() && !aFontStyle->style.IsNormal() &&
    910      aFontStyle->synthesisStyle != StyleFontSynthesisStyle::None) {
    911    // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
    912    FcPatternDel(renderPattern, FC_EMBEDDED_BITMAP);
    913    FcPatternAddBool(renderPattern, FC_EMBEDDED_BITMAP, FcFalse);
    914  }
    915 
    916  int loadFlags;
    917  unsigned int synthFlags;
    918  PrepareFontOptions(renderPattern, &loadFlags, &synthFlags);
    919 
    920  std::string file;
    921  int index = 0;
    922  if (!face->GetData()) {
    923    const FcChar8* fcFile;
    924    if (FcPatternGetString(renderPattern, FC_FILE, 0,
    925                           const_cast<FcChar8**>(&fcFile)) != FcResultMatch ||
    926        FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) !=
    927            FcResultMatch) {
    928      NS_WARNING("No file in Fontconfig pattern for font instance");
    929      return nullptr;
    930    }
    931    file = ToCharPtr(fcFile);
    932  }
    933 
    934  RefPtr<UnscaledFontFontconfig> unscaledFont;
    935  {
    936    AutoReadLock lock(mLock);
    937    unscaledFont = mUnscaledFontCache.Lookup(file, index);
    938  }
    939 
    940  if (!unscaledFont) {
    941    AutoWriteLock lock(mLock);
    942    // Here, we use the original mFTFace, not a potential clone with variation
    943    // settings applied.
    944    auto ftFace = GetFTFace();
    945    unscaledFont = ftFace->GetData() ? new UnscaledFontFontconfig(ftFace)
    946                                     : new UnscaledFontFontconfig(
    947                                           std::move(file), index, ftFace);
    948    mUnscaledFontCache.Add(unscaledFont);
    949  }
    950 
    951  gfxFont* newFont = new gfxFontconfigFont(
    952      unscaledFont, std::move(face), renderPattern, size, this, aFontStyle,
    953      loadFlags, (synthFlags & CAIRO_FT_SYNTHESIZE_BOLD) != 0);
    954 
    955  return newFont;
    956 }
    957 
    958 SharedFTFace* gfxFontconfigFontEntry::GetFTFace() {
    959  if (!mFTFaceInitialized) {
    960    RefPtr<SharedFTFace> face = CreateFaceForPattern(mFontPattern);
    961    if (face) {
    962      if (mFTFace.compareExchange(nullptr, face.get())) {
    963        face.forget().leak();  // The reference is now owned by mFTFace.
    964        mFTFaceInitialized = true;
    965      } else {
    966        // We lost a race to set mFTFace! Just discard our new face.
    967      }
    968    }
    969  }
    970  return mFTFace;
    971 }
    972 
    973 FTUserFontData* gfxFontconfigFontEntry::GetUserFontData() {
    974  auto face = GetFTFace();
    975  if (face && face->GetData()) {
    976    return static_cast<FTUserFontData*>(face->GetData());
    977  }
    978  return nullptr;
    979 }
    980 
    981 bool gfxFontconfigFontEntry::HasVariations() {
    982  // If the answer is already cached, just return it.
    983  switch (mHasVariations) {
    984    case HasVariationsState::No:
    985      return false;
    986    case HasVariationsState::Yes:
    987      return true;
    988    case HasVariationsState::Uninitialized:
    989      break;
    990  }
    991 
    992  // Figure out whether we have variations, and record in mHasVariations.
    993  // (It doesn't matter if we race with another thread to set this; the result
    994  // will be the same.)
    995 
    996  if (!gfxPlatform::HasVariationFontSupport()) {
    997    mHasVariations = HasVariationsState::No;
    998    return false;
    999  }
   1000 
   1001  // For installed fonts, query the fontconfig pattern rather than paying
   1002  // the cost of loading a FT_Face that we otherwise might never need.
   1003  if (!IsUserFont() || IsLocalUserFont()) {
   1004    FcBool variable;
   1005    if ((FcPatternGetBool(mFontPattern, FC_VARIABLE, 0, &variable) ==
   1006         FcResultMatch) &&
   1007        variable) {
   1008      mHasVariations = HasVariationsState::Yes;
   1009      return true;
   1010    }
   1011  } else {
   1012    if (auto ftFace = GetFTFace()) {
   1013      if (ftFace->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
   1014        mHasVariations = HasVariationsState::Yes;
   1015        return true;
   1016      }
   1017    }
   1018  }
   1019 
   1020  mHasVariations = HasVariationsState::No;
   1021  return false;
   1022 }
   1023 
   1024 FT_MM_Var* gfxFontconfigFontEntry::GetMMVar() {
   1025  {
   1026    AutoReadLock lock(mLock);
   1027    if (mMMVarInitialized) {
   1028      return mMMVar;
   1029    }
   1030  }
   1031 
   1032  AutoWriteLock lock(mLock);
   1033 
   1034  mMMVarInitialized = true;
   1035  InitializeVarFuncs();
   1036  if (!sGetVar) {
   1037    return nullptr;
   1038  }
   1039  auto ftFace = GetFTFace();
   1040  if (!ftFace) {
   1041    return nullptr;
   1042  }
   1043  if (FT_Err_Ok != (*sGetVar)(ftFace->GetFace(), &mMMVar)) {
   1044    mMMVar = nullptr;
   1045  }
   1046  return mMMVar;
   1047 }
   1048 
   1049 void gfxFontconfigFontEntry::GetVariationAxes(
   1050    nsTArray<gfxFontVariationAxis>& aAxes) {
   1051  if (!HasVariations()) {
   1052    return;
   1053  }
   1054  gfxFT2Utils::GetVariationAxes(GetMMVar(), aAxes);
   1055 }
   1056 
   1057 void gfxFontconfigFontEntry::GetVariationInstances(
   1058    nsTArray<gfxFontVariationInstance>& aInstances) {
   1059  if (!HasVariations()) {
   1060    return;
   1061  }
   1062  gfxFT2Utils::GetVariationInstances(this, GetMMVar(), aInstances);
   1063 }
   1064 
   1065 nsresult gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
   1066                                               nsTArray<uint8_t>& aBuffer) {
   1067  NS_ASSERTION(!mIsDataUserFont,
   1068               "data fonts should be reading tables directly from memory");
   1069  return gfxFT2FontEntryBase::CopyFaceTable(GetFTFace(), aTableTag, aBuffer);
   1070 }
   1071 
   1072 void gfxFontconfigFontFamily::FindStyleVariationsLocked(
   1073    FontInfoData* aFontInfoData) {
   1074  if (mHasStyles) {
   1075    return;
   1076  }
   1077 
   1078  // add font entries for each of the faces
   1079  uint32_t numFonts = mFontPatterns.Length();
   1080  NS_ASSERTION(numFonts, "font family containing no faces!!");
   1081  uint32_t numRegularFaces = 0;
   1082  for (uint32_t i = 0; i < numFonts; i++) {
   1083    FcPattern* face = mFontPatterns[i];
   1084 
   1085    // figure out the psname/fullname and choose which to use as the facename
   1086    nsAutoCString psname, fullname;
   1087    GetFaceNames(face, mName, psname, fullname);
   1088    const nsAutoCString& faceName = !psname.IsEmpty() ? psname : fullname;
   1089 
   1090    gfxFontconfigFontEntry* fontEntry =
   1091        new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
   1092 
   1093    if (gfxPlatform::HasVariationFontSupport()) {
   1094      fontEntry->SetupVariationRanges();
   1095    }
   1096 
   1097    AddFontEntryLocked(fontEntry);
   1098 
   1099    if (fontEntry->IsNormalStyle()) {
   1100      numRegularFaces++;
   1101    }
   1102 
   1103    if (LOG_FONTLIST_ENABLED()) {
   1104      nsAutoCString weightString;
   1105      fontEntry->Weight().ToString(weightString);
   1106      nsAutoCString stretchString;
   1107      fontEntry->Stretch().ToString(stretchString);
   1108      nsAutoCString styleString;
   1109      fontEntry->SlantStyle().ToString(styleString);
   1110      LOG_FONTLIST(
   1111          ("(fontlist) added (%s) to family (%s)"
   1112           " with style: %s weight: %s stretch: %s"
   1113           " psname: %s fullname: %s",
   1114           fontEntry->Name().get(), Name().get(), styleString.get(),
   1115           weightString.get(), stretchString.get(), psname.get(),
   1116           fullname.get()));
   1117    }
   1118  }
   1119 
   1120  // somewhat arbitrary, but define a family with two or more regular
   1121  // faces as a family for which intra-family fallback should be used
   1122  if (numRegularFaces > 1) {
   1123    mCheckForFallbackFaces = true;
   1124  }
   1125  mFaceNamesInitialized = true;
   1126  mFontPatterns.Clear();
   1127  SetHasStyles(true);
   1128 
   1129  CheckForSimpleFamily();
   1130 }
   1131 
   1132 void gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern,
   1133                                             bool aSingleName) {
   1134  NS_ASSERTION(
   1135      !mHasStyles,
   1136      "font patterns must not be added to already enumerated families");
   1137 
   1138  FcBool outline;
   1139  if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) !=
   1140          FcResultMatch ||
   1141      !outline) {
   1142    mHasNonScalableFaces = true;
   1143 
   1144    FcBool scalable;
   1145    if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
   1146            FcResultMatch &&
   1147        scalable) {
   1148      mForceScalable = true;
   1149    }
   1150  }
   1151 
   1152  if (aSingleName) {
   1153    mFontPatterns.InsertElementAt(mUniqueNameFaceCount++, aFontPattern);
   1154  } else {
   1155    mFontPatterns.AppendElement(aFontPattern);
   1156  }
   1157 }
   1158 
   1159 static const double kRejectDistance = 10000.0;
   1160 
   1161 // Calculate a distance score representing the size disparity between the
   1162 // requested style's size and the font entry's size.
   1163 static double SizeDistance(gfxFontconfigFontEntry* aEntry,
   1164                           const gfxFontStyle& aStyle, bool aForceScalable) {
   1165  double requestedSize = SizeForStyle(aEntry, aStyle);
   1166  double bestDist = -1.0;
   1167  double size;
   1168  int v = 0;
   1169  while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
   1170         FcResultMatch) {
   1171    ++v;
   1172    double dist = fabs(size - requestedSize);
   1173    if (bestDist < 0.0 || dist < bestDist) {
   1174      bestDist = dist;
   1175    }
   1176  }
   1177  if (bestDist < 0.0) {
   1178    // No size means scalable
   1179    return -1.0;
   1180  } else if (aForceScalable || 5.0 * bestDist < requestedSize) {
   1181    // fontconfig prefers a matching family or lang to pixelsize of bitmap
   1182    // fonts. CSS suggests a tolerance of 20% on pixelsize.
   1183    return bestDist;
   1184  } else {
   1185    // Reject any non-scalable fonts that are not within tolerance.
   1186    return kRejectDistance;
   1187  }
   1188 }
   1189 
   1190 void gfxFontconfigFontFamily::FindAllFontsForStyle(
   1191    const gfxFontStyle& aFontStyle, nsTArray<gfxFontEntry*>& aFontEntryList,
   1192    bool aIgnoreSizeTolerance) {
   1193  gfxFontFamily::FindAllFontsForStyle(aFontStyle, aFontEntryList,
   1194                                      aIgnoreSizeTolerance);
   1195 
   1196  if (!mHasNonScalableFaces) {
   1197    return;
   1198  }
   1199 
   1200  // Iterate over the the available fonts while compacting any groups
   1201  // of unscalable fonts with matching styles into a single entry
   1202  // corresponding to the closest available size. If the closest
   1203  // available size is rejected for being outside tolerance, then the
   1204  // entire group will be skipped.
   1205  size_t skipped = 0;
   1206  gfxFontconfigFontEntry* bestEntry = nullptr;
   1207  double bestDist = -1.0;
   1208  for (size_t i = 0; i < aFontEntryList.Length(); i++) {
   1209    gfxFontconfigFontEntry* entry =
   1210        static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
   1211    double dist =
   1212        SizeDistance(entry, aFontStyle, mForceScalable || aIgnoreSizeTolerance);
   1213    // If the entry is scalable or has a style that does not match
   1214    // the group of unscalable fonts, then start a new group.
   1215    if (dist < 0.0 || !bestEntry || bestEntry->Stretch() != entry->Stretch() ||
   1216        bestEntry->Weight() != entry->Weight() ||
   1217        bestEntry->SlantStyle() != entry->SlantStyle()) {
   1218      // If the best entry in this group is still outside the tolerance,
   1219      // then skip the entire group.
   1220      if (bestDist >= kRejectDistance) {
   1221        skipped++;
   1222      }
   1223      // Remove any compacted entries from the previous group.
   1224      if (skipped) {
   1225        i -= skipped;
   1226        aFontEntryList.RemoveElementsAt(i, skipped);
   1227        skipped = 0;
   1228      }
   1229      // Mark the start of the new group.
   1230      bestEntry = entry;
   1231      bestDist = dist;
   1232    } else {
   1233      // If this entry more closely matches the requested size than the
   1234      // current best in the group, then take this entry instead.
   1235      if (dist < bestDist) {
   1236        aFontEntryList[i - 1 - skipped] = entry;
   1237        bestEntry = entry;
   1238        bestDist = dist;
   1239      }
   1240      skipped++;
   1241    }
   1242  }
   1243  // If the best entry in this group is still outside the tolerance,
   1244  // then skip the entire group.
   1245  if (bestDist >= kRejectDistance) {
   1246    skipped++;
   1247  }
   1248  // Remove any compacted entries from the current group.
   1249  if (skipped) {
   1250    aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
   1251  }
   1252 }
   1253 
   1254 static bool PatternHasLang(const FcPattern* aPattern, const FcChar8* aLang) {
   1255  FcLangSet* langset;
   1256 
   1257  if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
   1258    return false;
   1259  }
   1260 
   1261  if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
   1262    return true;
   1263  }
   1264  return false;
   1265 }
   1266 
   1267 bool gfxFontconfigFontFamily::SupportsLangGroup(nsAtom* aLangGroup) const {
   1268  if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
   1269    return true;
   1270  }
   1271 
   1272  nsAutoCString fcLang;
   1273  gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
   1274  pfl->GetSampleLangForGroup(aLangGroup, fcLang);
   1275  if (fcLang.IsEmpty()) {
   1276    return true;
   1277  }
   1278 
   1279  // Before FindStyleVariations has been called, mFontPatterns will contain
   1280  // the font patterns.  Afterward, it'll be empty, but mAvailableFonts
   1281  // will contain the font entries, each of which holds a reference to its
   1282  // pattern.  We only check the first pattern in each list, because support
   1283  // for langs is considered to be consistent across all faces in a family.
   1284  AutoReadLock lock(mLock);
   1285  FcPattern* fontPattern;
   1286  if (mFontPatterns.Length()) {
   1287    fontPattern = mFontPatterns[0];
   1288  } else if (mAvailableFonts.Length()) {
   1289    fontPattern = static_cast<gfxFontconfigFontEntry*>(mAvailableFonts[0].get())
   1290                      ->GetPattern();
   1291  } else {
   1292    return true;
   1293  }
   1294 
   1295  // is lang included in the underlying pattern?
   1296  return PatternHasLang(fontPattern, ToFcChar8Ptr(fcLang.get()));
   1297 }
   1298 
   1299 /* virtual */
   1300 gfxFontconfigFontFamily::~gfxFontconfigFontFamily() {
   1301  // Should not be dropped by stylo
   1302  MOZ_ASSERT(NS_IsMainThread());
   1303 }
   1304 
   1305 template <typename Func>
   1306 void gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc) {
   1307  AutoReadLock lock(mLock);
   1308  if (HasStyles()) {
   1309    for (auto& fe : mAvailableFonts) {
   1310      if (!fe) {
   1311        continue;
   1312      }
   1313      auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get());
   1314      aAddPatternFunc(fce->GetPattern(), mContainsAppFonts);
   1315    }
   1316  } else {
   1317    for (auto& pat : mFontPatterns) {
   1318      aAddPatternFunc(pat, mContainsAppFonts);
   1319    }
   1320  }
   1321 }
   1322 
   1323 gfxFontconfigFont::gfxFontconfigFont(
   1324    const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
   1325    RefPtr<SharedFTFace>&& aFTFace, FcPattern* aPattern, gfxFloat aAdjustedSize,
   1326    gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle, int aLoadFlags,
   1327    bool aEmbolden)
   1328    : gfxFT2FontBase(aUnscaledFont, std::move(aFTFace), aFontEntry, aFontStyle,
   1329                     aLoadFlags, aEmbolden),
   1330      mPattern(aPattern) {
   1331  mAdjustedSize = aAdjustedSize;
   1332  InitMetrics();
   1333 }
   1334 
   1335 gfxFontconfigFont::~gfxFontconfigFont() = default;
   1336 
   1337 already_AddRefed<ScaledFont> gfxFontconfigFont::GetScaledFont(
   1338    const TextRunDrawParams& aRunParams) {
   1339  if (ScaledFont* scaledFont = mAzureScaledFont) {
   1340    return do_AddRef(scaledFont);
   1341  }
   1342 
   1343  RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForFontconfigFont(
   1344      GetUnscaledFont(), GetAdjustedSize(), mFTFace, GetPattern());
   1345  if (!newScaledFont) {
   1346    return nullptr;
   1347  }
   1348 
   1349  InitializeScaledFont(newScaledFont);
   1350 
   1351  if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
   1352    newScaledFont.forget().leak();
   1353  }
   1354  ScaledFont* scaledFont = mAzureScaledFont;
   1355  return do_AddRef(scaledFont);
   1356 }
   1357 
   1358 bool gfxFontconfigFont::ShouldHintMetrics() const {
   1359  return !GetStyle()->printerFont;
   1360 }
   1361 
   1362 gfxFcPlatformFontList::gfxFcPlatformFontList()
   1363    : mLocalNames(64),
   1364      mGenericMappings(32),
   1365      mFcSubstituteCache(64),
   1366      mLastConfig(nullptr),
   1367      mAlwaysUseFontconfigGenerics(true) {
   1368 #ifndef BASE_BROWSER_VERSION
   1369  CheckFamilyList(kBaseFonts_Ubuntu_22_04);
   1370  CheckFamilyList(kLangFonts_Ubuntu_22_04);
   1371  CheckFamilyList(kBaseFonts_Ubuntu_20_04);
   1372  CheckFamilyList(kLangFonts_Ubuntu_20_04);
   1373  CheckFamilyList(kBaseFonts_Fedora_39);
   1374  CheckFamilyList(kBaseFonts_Fedora_38);
   1375 #endif
   1376  mLastConfig = FcConfigGetCurrent();
   1377  if (XRE_IsParentProcess()) {
   1378    // if the rescan interval is set, start the timer
   1379    int rescanInterval = FcConfigGetRescanInterval(nullptr);
   1380    if (rescanInterval) {
   1381      NS_NewTimerWithFuncCallback(
   1382          getter_AddRefs(mCheckFontUpdatesTimer), CheckFontUpdates, this,
   1383          (rescanInterval + 1) * 1000, nsITimer::TYPE_REPEATING_SLACK,
   1384          "gfxFcPlatformFontList::gfxFcPlatformFontList"_ns);
   1385      if (!mCheckFontUpdatesTimer) {
   1386        NS_WARNING("Failure to create font updates timer");
   1387      }
   1388    }
   1389  }
   1390 
   1391 #ifdef MOZ_BUNDLED_FONTS
   1392  mBundledFontsInitialized = false;
   1393 #endif
   1394 }
   1395 
   1396 gfxFcPlatformFontList::~gfxFcPlatformFontList() {
   1397  AutoLock lock(mLock);
   1398 
   1399  if (mCheckFontUpdatesTimer) {
   1400    mCheckFontUpdatesTimer->Cancel();
   1401    mCheckFontUpdatesTimer = nullptr;
   1402  }
   1403 #ifdef MOZ_WIDGET_GTK
   1404  ClearSystemFontOptions();
   1405 #endif
   1406 }
   1407 
   1408 void gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet,
   1409                                               const SandboxPolicy* aPolicy,
   1410                                               bool aAppFonts) {
   1411  // This iterates over the fonts in a font set and adds in gfxFontFamily
   1412  // objects for each family. Individual gfxFontEntry objects for each face
   1413  // are not created here; the patterns are just stored in the family. When
   1414  // a family is actually used, it will be populated with gfxFontEntry
   1415  // records and the patterns moved to those.
   1416 
   1417  if (NS_WARN_IF(!aFontSet)) {
   1418    return;
   1419  }
   1420 
   1421  FcChar8* lastFamilyName = (FcChar8*)"";
   1422  RefPtr<gfxFontconfigFontFamily> fontFamily;
   1423  nsAutoCString familyName;
   1424  for (int f = 0; f < aFontSet->nfont; f++) {
   1425    FcPattern* pattern = aFontSet->fonts[f];
   1426 
   1427    // Skip any fonts that aren't readable for us (e.g. due to restrictive
   1428    // file ownership/permissions).
   1429    FcChar8* path;
   1430    if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
   1431      continue;
   1432    }
   1433    if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
   1434      continue;
   1435    }
   1436 
   1437 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
   1438    // Skip any fonts that will be blocked by the content-process sandbox
   1439    // policy.
   1440    if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
   1441                     SandboxBroker::Perms::MAY_READ)) {
   1442      continue;
   1443    }
   1444 #endif
   1445 
   1446    AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
   1447                         aAppFonts);
   1448  }
   1449 }
   1450 
   1451 // Check whether a pattern refers to a non-variable font, or a specific named
   1452 // instance of a variable font. Only such patterns are eligible for src:local()
   1453 // name lookups, as local() is defined to reference a specific face.
   1454 static bool IsNonVariableOrNamedInstance(FcPattern* aPattern) {
   1455  FcBool value;
   1456  if (FcPatternGetBool(aPattern, FC_VARIABLE, 0, &value) == FcResultMatch &&
   1457      value) {
   1458    // It's a variable font resource; check if this is a named instance.
   1459    if (FcPatternGetBool(aPattern, FC_NAMED_INSTANCE, 0, &value) ==
   1460            FcResultMatch &&
   1461        value) {
   1462      // Yes, named instance: ok to use.
   1463      return true;
   1464    }
   1465    // It's a variable font; we don't want it.
   1466    return false;
   1467  }
   1468  // Non-variable: no problem.
   1469  return true;
   1470 }
   1471 
   1472 void gfxFcPlatformFontList::AddPatternToFontList(
   1473    FcPattern* aFont, FcChar8*& aLastFamilyName, nsACString& aFamilyName,
   1474    RefPtr<gfxFontconfigFontFamily>& aFontFamily, bool aAppFonts) {
   1475  // get canonical name
   1476  uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG);
   1477  FcChar8* canonical = nullptr;
   1478  FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical);
   1479  if (!canonical) {
   1480    return;
   1481  }
   1482 
   1483  // same as the last one? no need to add a new family, skip
   1484  if (FcStrCmp(canonical, aLastFamilyName) != 0) {
   1485    aLastFamilyName = canonical;
   1486 
   1487    // add new family if one doesn't already exist
   1488    aFamilyName.Truncate();
   1489    aFamilyName = ToCharPtr(canonical);
   1490    nsAutoCString keyName(aFamilyName);
   1491    ToLowerCase(keyName);
   1492 
   1493    aFontFamily = static_cast<gfxFontconfigFontFamily*>(
   1494        mFontFamilies
   1495            .LookupOrInsertWith(keyName,
   1496                                [&] {
   1497                                  FontVisibility visibility =
   1498                                      aAppFonts
   1499                                          ? FontVisibility::Base
   1500                                          : GetVisibilityForFamily(keyName);
   1501                                  return MakeRefPtr<gfxFontconfigFontFamily>(
   1502                                      aFamilyName, visibility);
   1503                                })
   1504            .get());
   1505    // Record if the family contains fonts from the app font set
   1506    // (in which case we won't rely on fontconfig's charmap, due to
   1507    // bug 1276594).
   1508    if (aAppFonts) {
   1509      aFontFamily->SetFamilyContainsAppFonts(true);
   1510    }
   1511  }
   1512 
   1513  // Add pointers to other localized family names. Most fonts
   1514  // only have a single name, so the first call to GetString
   1515  // will usually not match
   1516  FcChar8* otherName;
   1517  int n = (cIndex == 0 ? 1 : 0);
   1518  AutoTArray<nsCString, 4> otherFamilyNames;
   1519  while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) == FcResultMatch) {
   1520    otherFamilyNames.AppendElement(nsCString(ToCharPtr(otherName)));
   1521    n++;
   1522    if (n == int(cIndex)) {
   1523      n++;  // skip over canonical name
   1524    }
   1525  }
   1526  if (!otherFamilyNames.IsEmpty()) {
   1527    AddOtherFamilyNames(aFontFamily, otherFamilyNames);
   1528  }
   1529 
   1530  const bool singleName = n == 1;
   1531 
   1532  MOZ_ASSERT(aFontFamily, "font must belong to a font family");
   1533  aFontFamily->AddFontPattern(aFont, singleName);
   1534 
   1535  if (IsNonVariableOrNamedInstance(aFont)) {
   1536    // map the psname, fullname ==> font family for local font lookups
   1537    nsAutoCString psname, fullname;
   1538    GetFaceNames(aFont, aFamilyName, psname, fullname);
   1539    if (!psname.IsEmpty()) {
   1540      ToLowerCase(psname);
   1541      mLocalNames.InsertOrUpdate(psname, RefPtr{aFont});
   1542    }
   1543    if (!fullname.IsEmpty()) {
   1544      ToLowerCase(fullname);
   1545      mLocalNames.WithEntryHandle(fullname, [&](auto&& entry) {
   1546        if (entry && !singleName) {
   1547          return;
   1548        }
   1549        entry.InsertOrUpdate(RefPtr{aFont});
   1550      });
   1551    }
   1552  }
   1553 }
   1554 
   1555 nsresult gfxFcPlatformFontList::InitFontListForPlatform() {
   1556 #ifdef MOZ_BUNDLED_FONTS
   1557  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
   1558    ActivateBundledFonts();
   1559  }
   1560 #endif
   1561 
   1562  mLocalNames.Clear();
   1563  mFcSubstituteCache.Clear();
   1564 
   1565  ClearSystemFontOptions();
   1566 
   1567  mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
   1568  mOtherFamilyNamesInitialized = true;
   1569 
   1570  mLastConfig = FcConfigGetCurrent();
   1571 
   1572  if (XRE_IsContentProcess()) {
   1573    // Content process: use the font list passed from the chrome process,
   1574    // because we can't rely on fontconfig in the presence of sandboxing;
   1575    // it may report fonts that we can't actually access.
   1576 
   1577    FcChar8* lastFamilyName = (FcChar8*)"";
   1578    RefPtr<gfxFontconfigFontFamily> fontFamily;
   1579    nsAutoCString familyName;
   1580 
   1581    // Get font list that was passed during XPCOM startup
   1582    // or in an UpdateFontList message.
   1583    auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
   1584 
   1585 #ifdef MOZ_WIDGET_GTK
   1586    UpdateSystemFontOptionsFromIpc(fontList.options());
   1587 #endif
   1588 
   1589    // For fontconfig versions between 2.10.94 and 2.11.1 inclusive,
   1590    // we need to escape any leading space in the charset element,
   1591    // otherwise FcNameParse will fail. :(
   1592    //
   1593    // The bug was introduced on 2013-05-24 by
   1594    //   https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7
   1595    //   "Bug 64906 - FcNameParse() should ignore leading whitespace in
   1596    //   parameters"
   1597    // because ignoring a leading space in the encoded value of charset
   1598    // causes erroneous decoding of the whole element.
   1599    // This first shipped in version 2.10.94, and was eventually fixed as
   1600    // a side-effect of switching to the "human-readable" representation of
   1601    // charsets on 2014-07-03 in
   1602    //   https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730
   1603    //   "Change charset parse/unparse format to be human readable"
   1604    // (with a followup fix next day) which means a leading space is no
   1605    // longer significant. This fix landed after 2.11.1 had been shipped,
   1606    // so the first version tag without the bug is 2.11.91.
   1607    int fcVersion = FcGetVersion();
   1608    bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
   1609 
   1610    for (FontPatternListEntry& fpe : fontList.entries()) {
   1611      nsCString& patternStr = fpe.pattern();
   1612      if (fcCharsetParseBug) {
   1613        int32_t index = patternStr.Find(":charset= ");
   1614        if (index != kNotFound) {
   1615          // insert backslash after the =, before the space
   1616          patternStr.Insert('\\', index + 9);
   1617        }
   1618      }
   1619      FcPattern* pattern = FcNameParse((const FcChar8*)patternStr.get());
   1620      AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
   1621                           fpe.appFontFamily());
   1622      FcPatternDestroy(pattern);
   1623    }
   1624 
   1625    LOG_FONTLIST(
   1626        ("got font list from chrome process: "
   1627         "%u faces in %u families",
   1628         (unsigned)fontList.entries().Length(), mFontFamilies.Count()));
   1629 
   1630    fontList.entries().Clear();
   1631    return NS_OK;
   1632  }
   1633 
   1634  UpdateSystemFontOptions();
   1635 
   1636  UniquePtr<SandboxPolicy> policy;
   1637 
   1638 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
   1639  // If read sandboxing is enabled, create a temporary SandboxPolicy to
   1640  // check font paths; use a fake PID to avoid picking up any PID-specific
   1641  // rules by accident.
   1642  SandboxBrokerPolicyFactory policyFactory;
   1643  if (GetEffectiveContentSandboxLevel() > 2 &&
   1644      !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
   1645    policy = policyFactory.GetContentPolicy(-1, false);
   1646  }
   1647 #endif
   1648 
   1649 #ifdef MOZ_BUNDLED_FONTS
   1650  // https://bugzilla.mozilla.org/show_bug.cgi?id=1745715:
   1651  // It's important to do this *before* processing the standard system fonts,
   1652  // so that if a family is present in both font sets, we'll treat it as app-
   1653  // bundled and therefore always visible.
   1654  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
   1655    FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
   1656    AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
   1657  }
   1658 #endif
   1659 
   1660  // iterate over available fonts
   1661  FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
   1662  AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
   1663 
   1664  return NS_OK;
   1665 }
   1666 
   1667 void gfxFcPlatformFontList::ReadSystemFontList(dom::SystemFontList* retValue) {
   1668  AutoLock lock(mLock);
   1669 
   1670  // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
   1671  // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
   1672  // an older version, we manually append it to the unparsed pattern.
   1673 #ifdef MOZ_WIDGET_GTK
   1674  SystemFontOptionsToIpc(retValue->options());
   1675 #endif
   1676 
   1677  if (FcGetVersion() < 20900) {
   1678    for (const auto& entry : mFontFamilies) {
   1679      auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
   1680      family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
   1681        char* s = (char*)FcNameUnparse(aPat);
   1682        nsDependentCString patternStr(s);
   1683        char* file = nullptr;
   1684        if (FcResultMatch ==
   1685            FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&file)) {
   1686          patternStr.Append(":file=");
   1687          patternStr.Append(file);
   1688        }
   1689        retValue->entries().AppendElement(
   1690            FontPatternListEntry(patternStr, aAppFonts));
   1691        free(s);
   1692      });
   1693    }
   1694  } else {
   1695    for (const auto& entry : mFontFamilies) {
   1696      auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
   1697      family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
   1698        char* s = (char*)FcNameUnparse(aPat);
   1699        nsDependentCString patternStr(s);
   1700        retValue->entries().AppendElement(
   1701            FontPatternListEntry(patternStr, aAppFonts));
   1702        free(s);
   1703      });
   1704    }
   1705  }
   1706 }
   1707 
   1708 using Device = nsIGfxInfo::FontVisibilityDeviceDetermination;
   1709 static Device sFontVisibilityDevice = Device::Unassigned;
   1710 
   1711 void AssignFontVisibilityDevice() {
   1712  sFontVisibilityDevice = Device::Linux_Unknown;
   1713 #ifndef BASE_BROWSER_VERSION
   1714  if (sFontVisibilityDevice == Device::Unassigned) {
   1715    nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
   1716    NS_ENSURE_SUCCESS_VOID(
   1717        gfxInfo->GetFontVisibilityDetermination(&sFontVisibilityDevice));
   1718  }
   1719 #endif
   1720 }
   1721 
   1722 // Per family array of faces.
   1723 class FacesData {
   1724  using FaceInitArray = AutoTArray<fontlist::Face::InitData, 8>;
   1725 
   1726  FaceInitArray mFaces;
   1727 
   1728  // Number of faces that have a single name. Faces that have multiple names are
   1729  // sorted last.
   1730  uint32_t mUniqueNameFaceCount = 0;
   1731 
   1732 public:
   1733  void Add(fontlist::Face::InitData&& aData, bool aSingleName) {
   1734    if (aSingleName) {
   1735      mFaces.InsertElementAt(mUniqueNameFaceCount++, std::move(aData));
   1736    } else {
   1737      mFaces.AppendElement(std::move(aData));
   1738    }
   1739  }
   1740 
   1741  const FaceInitArray& Get() const { return mFaces; }
   1742 };
   1743 
   1744 void gfxFcPlatformFontList::InitSharedFontListForPlatform() {
   1745  mLocalNames.Clear();
   1746  mFcSubstituteCache.Clear();
   1747 
   1748  mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
   1749  mOtherFamilyNamesInitialized = true;
   1750 
   1751  mLastConfig = FcConfigGetCurrent();
   1752 
   1753  if (!XRE_IsParentProcess()) {
   1754 #ifdef MOZ_WIDGET_GTK
   1755    auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
   1756    UpdateSystemFontOptionsFromIpc(fontList.options());
   1757 #endif
   1758    // Content processes will access the shared-memory data created by the
   1759    // parent, so they do not need to query fontconfig for the available
   1760    // fonts themselves.
   1761    return;
   1762  }
   1763 
   1764 #ifdef MOZ_WIDGET_GTK
   1765  UpdateSystemFontOptions();
   1766 #endif
   1767 
   1768 #ifdef MOZ_BUNDLED_FONTS
   1769  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
   1770    auto timerId = glean::fontlist::bundledfonts_activate.Start();
   1771    ActivateBundledFonts();
   1772    glean::fontlist::bundledfonts_activate.StopAndAccumulate(
   1773        std::move(timerId));
   1774  }
   1775 #endif
   1776 
   1777  UniquePtr<SandboxPolicy> policy;
   1778 
   1779 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
   1780  // If read sandboxing is enabled, create a temporary SandboxPolicy to
   1781  // check font paths; use a fake PID to avoid picking up any PID-specific
   1782  // rules by accident.
   1783  SandboxBrokerPolicyFactory policyFactory;
   1784  if (GetEffectiveContentSandboxLevel() > 2 &&
   1785      !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
   1786    policy = policyFactory.GetContentPolicy(-1, false);
   1787  }
   1788 #endif
   1789 
   1790  nsTArray<fontlist::Family::InitData> families;
   1791 
   1792  nsClassHashtable<nsCStringHashKey, FacesData> faces;
   1793 
   1794  // Do we need to work around the fontconfig FcNameParse/FcNameUnparse bug
   1795  // (present in versions between 2.10.94 and 2.11.1 inclusive)? See comment
   1796  // in InitFontListForPlatform for details.
   1797  int fcVersion = FcGetVersion();
   1798  bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
   1799 
   1800  // Returns true if the font was added with FontVisibility::Base.
   1801  // This enables us to count how many known Base fonts are present.
   1802  auto addPattern = [this, fcCharsetParseBug, &families, &faces](
   1803                        FcPattern* aPattern, FcChar8*& aLastFamilyName,
   1804                        nsCString& aFamilyName, bool aAppFont) -> bool {
   1805    // get canonical name
   1806    uint32_t cIndex = FindCanonicalNameIndex(aPattern, FC_FAMILYLANG);
   1807    FcChar8* canonical = nullptr;
   1808    FcPatternGetString(aPattern, FC_FAMILY, cIndex, &canonical);
   1809    if (!canonical) {
   1810      return false;
   1811    }
   1812 
   1813    nsAutoCString keyName;
   1814    keyName = ToCharPtr(canonical);
   1815    ToLowerCase(keyName);
   1816 
   1817    aLastFamilyName = canonical;
   1818    aFamilyName = ToCharPtr(canonical);
   1819 
   1820    const FontVisibility visibility =
   1821        aAppFont ? FontVisibility::Base : GetVisibilityForFamily(keyName);
   1822 
   1823    // Same canonical family name as the last one? Definitely no need to add a
   1824    // new family record.
   1825    auto* faceList =
   1826        faces
   1827            .LookupOrInsertWith(
   1828                keyName,
   1829                [&] {
   1830                  families.AppendElement(fontlist::Family::InitData(
   1831                      keyName, aFamilyName, fontlist::Family::kNoIndex,
   1832                      visibility,
   1833                      /*bundled*/ aAppFont, /*badUnderline*/ false));
   1834                  return MakeUnique<FacesData>();
   1835                })
   1836            .get();
   1837 
   1838    char* s = (char*)FcNameUnparse(aPattern);
   1839    nsAutoCString descriptor(s);
   1840    free(s);
   1841 
   1842    if (fcCharsetParseBug) {
   1843      // Escape any leading space in charset to work around FcNameParse bug.
   1844      int32_t index = descriptor.Find(":charset= ");
   1845      if (index != kNotFound) {
   1846        // insert backslash after the =, before the space
   1847        descriptor.Insert('\\', index + 9);
   1848      }
   1849    }
   1850 
   1851    WeightRange weight(FontWeight::NORMAL);
   1852    StretchRange stretch(FontStretch::NORMAL);
   1853    SlantStyleRange style(FontSlantStyle::NORMAL);
   1854    uint16_t size;
   1855    GetFontProperties(aPattern, &weight, &stretch, &style, &size);
   1856 
   1857    auto initData = fontlist::Face::InitData{descriptor, 0,       size, false,
   1858                                             weight,     stretch, style};
   1859 
   1860    // Add entries for any other localized family names. (Most fonts only have
   1861    // a single family name, so the first call to GetString will usually fail).
   1862    // These get the same visibility level as we looked up for the first name.
   1863    FcChar8* otherName;
   1864    int n = (cIndex == 0 ? 1 : 0);
   1865    while (FcPatternGetString(aPattern, FC_FAMILY, n, &otherName) ==
   1866           FcResultMatch) {
   1867      nsAutoCString otherFamilyName(ToCharPtr(otherName));
   1868      keyName = otherFamilyName;
   1869      ToLowerCase(keyName);
   1870 
   1871      faces
   1872          .LookupOrInsertWith(
   1873              keyName,
   1874              [&] {
   1875                families.AppendElement(fontlist::Family::InitData(
   1876                    keyName, otherFamilyName, fontlist::Family::kNoIndex,
   1877                    visibility,
   1878                    /*bundled*/ aAppFont, /*badUnderline*/ false));
   1879 
   1880                return MakeUnique<FacesData>();
   1881              })
   1882          .get()
   1883          ->Add(fontlist::Face::InitData(initData), /* singleName = */ false);
   1884 
   1885      n++;
   1886      if (n == int(cIndex)) {
   1887        n++;  // skip over canonical name
   1888      }
   1889    }
   1890 
   1891    const bool singleName = n == 1;
   1892    faceList->Add(std::move(initData), singleName);
   1893 
   1894    if (IsNonVariableOrNamedInstance(aPattern)) {
   1895      // map the psname, fullname ==> font family for local font lookups
   1896      nsAutoCString psname, fullname;
   1897      GetFaceNames(aPattern, aFamilyName, psname, fullname);
   1898      MOZ_PUSH_IGNORE_THREAD_SAFETY
   1899      if (!psname.IsEmpty()) {
   1900        ToLowerCase(psname);
   1901        MaybeAddToLocalNameTable(
   1902            psname, fontlist::LocalFaceRec::InitData(keyName, descriptor));
   1903      }
   1904      if (!fullname.IsEmpty()) {
   1905        ToLowerCase(fullname);
   1906        if (fullname != psname) {
   1907          // We only consider overriding an existing entry if this is the only
   1908          // way to name this family. This prevents dubious aliases from
   1909          // clobbering the local name table.
   1910          if (singleName || !mLocalNameTable.Contains(fullname)) {
   1911            MaybeAddToLocalNameTable(fullname, fontlist::LocalFaceRec::InitData(
   1912                                                   keyName, descriptor));
   1913          }
   1914        }
   1915      }
   1916      MOZ_POP_THREAD_SAFETY
   1917    }
   1918 
   1919    return visibility == FontVisibility::Base;
   1920  };
   1921 
   1922  // Returns the number of families with FontVisibility::Base that were found.
   1923  auto addFontSetFamilies = [&addPattern](FcFontSet* aFontSet,
   1924                                          SandboxPolicy* aPolicy,
   1925                                          bool aAppFonts) -> size_t {
   1926    size_t count = 0;
   1927    if (NS_WARN_IF(!aFontSet)) {
   1928      return count;
   1929    }
   1930    FcChar8* lastFamilyName = (FcChar8*)"";
   1931    RefPtr<gfxFontconfigFontFamily> fontFamily;
   1932    nsAutoCString familyName;
   1933    for (int f = 0; f < aFontSet->nfont; f++) {
   1934      FcPattern* pattern = aFontSet->fonts[f];
   1935 
   1936      // Skip any fonts that aren't readable for us (e.g. due to restrictive
   1937      // file ownership/permissions).
   1938      FcChar8* path;
   1939      if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
   1940        continue;
   1941      }
   1942      if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
   1943        continue;
   1944      }
   1945 
   1946 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
   1947      // Skip any fonts that will be blocked by the content-process sandbox
   1948      // policy.
   1949      if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
   1950                       SandboxBroker::Perms::MAY_READ)) {
   1951        continue;
   1952      }
   1953 #endif
   1954 
   1955      // Clone the pattern, because we can't operate on the one belonging to
   1956      // the FcFontSet directly.
   1957      FcPattern* clone = FcPatternDuplicate(pattern);
   1958 
   1959      // Pick up any configuration options applicable to the font (e.g. custom
   1960      // fontfeatures settings).
   1961      if (!FcConfigSubstitute(nullptr, clone, FcMatchFont)) {
   1962        // Out of memory?! We're probably doomed, but just skip this font.
   1963        FcPatternDestroy(clone);
   1964        continue;
   1965      }
   1966      // But ignore hinting settings from FcConfigSubstitute, as we don't want
   1967      // to bake them into the pattern in the font list.
   1968      FcPatternDel(clone, FC_HINT_STYLE);
   1969      FcPatternDel(clone, FC_HINTING);
   1970 
   1971      // If this is a TrueType or OpenType font, discard the FC_CHARSET object
   1972      // (which may be very large), because we'll read the 'cmap' directly.
   1973      // This substantially reduces the pressure on shared memory (bug 1664151)
   1974      // due to the large font descriptors (serialized patterns).
   1975      FcChar8* fontFormat;
   1976      MOZ_PUSH_IGNORE_THREAD_SAFETY
   1977      if (FcPatternGetString(clone, FC_FONTFORMAT, 0, &fontFormat) ==
   1978              FcResultMatch &&
   1979          (!FcStrCmp(fontFormat, (const FcChar8*)"TrueType") ||
   1980           !FcStrCmp(fontFormat, (const FcChar8*)"CFF"))) {
   1981        FcPatternDel(clone, FC_CHARSET);
   1982        if (addPattern(clone, lastFamilyName, familyName, aAppFonts)) {
   1983          ++count;
   1984        }
   1985      } else {
   1986        if (addPattern(clone, lastFamilyName, familyName, aAppFonts)) {
   1987          ++count;
   1988        }
   1989      }
   1990      MOZ_POP_THREAD_SAFETY
   1991 
   1992      FcPatternDestroy(clone);
   1993    }
   1994    return count;
   1995  };
   1996 
   1997 #ifdef MOZ_BUNDLED_FONTS
   1998  // Add bundled fonts before system fonts, to set correct visibility status
   1999  // for any families that appear in both.
   2000  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
   2001    FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
   2002    addFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
   2003  }
   2004 #endif
   2005 
   2006  // iterate over available fonts
   2007  FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
   2008  auto numBaseFamilies = addFontSetFamilies(systemFonts, policy.get(),
   2009                                            /* aAppFonts = */ false);
   2010  AssignFontVisibilityDevice();
   2011  if (numBaseFamilies < 3 && sFontVisibilityDevice != Device::Linux_Unknown) {
   2012    // If we found fewer than 3 known FontVisibility::Base families in the
   2013    // system (ignoring app-bundled fonts), we must be dealing with a very
   2014    // non-standard configuration; disable the distro-specific font
   2015    // fingerprinting protection by marking all fonts as Unknown.
   2016    for (auto& f : families) {
   2017      f.mVisibility = FontVisibility::Unknown;
   2018    }
   2019    // Issue a warning that we're disabling this protection.
   2020    nsCOMPtr<nsIConsoleService> console(
   2021        do_GetService("@mozilla.org/consoleservice;1"));
   2022    if (console) {
   2023      console->LogStringMessage(
   2024          u"Font-fingerprinting protection disabled; not enough standard "
   2025          u"distro fonts installed.");
   2026    }
   2027  }
   2028 
   2029  mozilla::fontlist::FontList* list = SharedFontList();
   2030  list->SetFamilyNames(families);
   2031 
   2032  for (uint32_t i = 0; i < families.Length(); i++) {
   2033    list->Families()[i].AddFaces(list, faces.Get(families[i].mKey)->Get());
   2034  }
   2035 }
   2036 
   2037 FontVisibility gfxFcPlatformFontList::GetVisibilityForFamily(
   2038    const nsACString& aName) const {
   2039  AssignFontVisibilityDevice();
   2040 
   2041  switch (sFontVisibilityDevice) {
   2042 #ifndef BASE_BROWSER_VERSION
   2043    case Device::Linux_Ubuntu_any:
   2044    case Device::Linux_Ubuntu_22:
   2045      if (FamilyInList(aName, kBaseFonts_Ubuntu_22_04)) {
   2046        return FontVisibility::Base;
   2047      }
   2048      if (FamilyInList(aName, kLangFonts_Ubuntu_22_04)) {
   2049        return FontVisibility::LangPack;
   2050      }
   2051      if (sFontVisibilityDevice == Device::Linux_Ubuntu_22) {
   2052        return FontVisibility::User;
   2053      }
   2054      // For Ubuntu_any, we fall through to also check the 20_04 lists.
   2055      [[fallthrough]];
   2056 
   2057    case Device::Linux_Ubuntu_20:
   2058      if (FamilyInList(aName, kBaseFonts_Ubuntu_20_04)) {
   2059        return FontVisibility::Base;
   2060      }
   2061      if (FamilyInList(aName, kLangFonts_Ubuntu_20_04)) {
   2062        return FontVisibility::LangPack;
   2063      }
   2064      return FontVisibility::User;
   2065 
   2066    case Device::Linux_Fedora_any:
   2067    case Device::Linux_Fedora_39:
   2068      if (FamilyInList(aName, kBaseFonts_Fedora_39)) {
   2069        return FontVisibility::Base;
   2070      }
   2071      if (sFontVisibilityDevice == Device::Linux_Fedora_39) {
   2072        return FontVisibility::User;
   2073      }
   2074      // For Fedora_any, fall through to also check Fedora 38 list.
   2075      [[fallthrough]];
   2076 
   2077    case Device::Linux_Fedora_38:
   2078      if (FamilyInList(aName, kBaseFonts_Fedora_38)) {
   2079        return FontVisibility::Base;
   2080      }
   2081      return FontVisibility::User;
   2082 #endif
   2083    default:
   2084      // We don't know how to categorize fonts on this system
   2085      return FontVisibility::Unknown;
   2086  }
   2087 }
   2088 
   2089 nsTArray<std::pair<const char**, uint32_t>>
   2090 gfxFcPlatformFontList::GetFilteredPlatformFontLists() {
   2091  AssignFontVisibilityDevice();
   2092 
   2093  nsTArray<std::pair<const char**, uint32_t>> fontLists;
   2094 
   2095 #ifndef BASE_BROWSER_VERSION
   2096  switch (sFontVisibilityDevice) {
   2097    case Device::Linux_Ubuntu_any:
   2098    case Device::Linux_Ubuntu_22:
   2099      fontLists.AppendElement(std::make_pair(
   2100          kBaseFonts_Ubuntu_22_04, std::size(kBaseFonts_Ubuntu_22_04)));
   2101      fontLists.AppendElement(std::make_pair(
   2102          kLangFonts_Ubuntu_22_04, std::size(kLangFonts_Ubuntu_22_04)));
   2103      // For Ubuntu_any, we fall through to also check the 20_04 lists.
   2104      [[fallthrough]];
   2105 
   2106    case Device::Linux_Ubuntu_20:
   2107      fontLists.AppendElement(std::make_pair(
   2108          kBaseFonts_Ubuntu_20_04, std::size(kBaseFonts_Ubuntu_20_04)));
   2109      fontLists.AppendElement(std::make_pair(
   2110          kLangFonts_Ubuntu_20_04, std::size(kLangFonts_Ubuntu_20_04)));
   2111      break;
   2112 
   2113    case Device::Linux_Fedora_any:
   2114    case Device::Linux_Fedora_39:
   2115      fontLists.AppendElement(std::make_pair(kBaseFonts_Fedora_39,
   2116                                             std::size(kBaseFonts_Fedora_39)));
   2117      // For Fedora_any, fall through to also check Fedora 38 list.
   2118      [[fallthrough]];
   2119 
   2120    case Device::Linux_Fedora_38:
   2121      fontLists.AppendElement(std::make_pair(kBaseFonts_Fedora_38,
   2122                                             std::size(kBaseFonts_Fedora_38)));
   2123      break;
   2124 
   2125    default:
   2126      // We don't know how to categorize fonts on this system
   2127      break;
   2128  }
   2129 #endif
   2130 
   2131  return fontLists;
   2132 }
   2133 
   2134 gfxFontEntry* gfxFcPlatformFontList::CreateFontEntry(
   2135    fontlist::Face* aFace, const fontlist::Family* aFamily) {
   2136  nsAutoCString desc(aFace->mDescriptor.AsString(SharedFontList()));
   2137  FcPattern* pattern = FcNameParse((const FcChar8*)desc.get());
   2138  auto* fe = new gfxFontconfigFontEntry(desc, pattern, true);
   2139  FcPatternDestroy(pattern);
   2140  fe->InitializeFrom(aFace, aFamily);
   2141  return fe;
   2142 }
   2143 
   2144 // For displaying the fontlist in UI, use explicit call to FcFontList. Using
   2145 // FcFontList results in the list containing the localized names as dictated
   2146 // by system defaults.
   2147 static void GetSystemFontList(nsTArray<nsString>& aListOfFonts,
   2148                              nsAtom* aLangGroup) {
   2149  aListOfFonts.Clear();
   2150 
   2151  RefPtr<FcPattern> pat = dont_AddRef(FcPatternCreate());
   2152  if (!pat) {
   2153    return;
   2154  }
   2155 
   2156  UniquePtr<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
   2157  if (!os) {
   2158    return;
   2159  }
   2160 
   2161  // add the lang to the pattern
   2162  nsAutoCString fcLang;
   2163  gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
   2164  pfl->GetSampleLangForGroup(aLangGroup, fcLang);
   2165  if (!fcLang.IsEmpty()) {
   2166    FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
   2167  }
   2168 
   2169  UniquePtr<FcFontSet> fs(FcFontList(nullptr, pat, os.get()));
   2170  if (!fs) {
   2171    return;
   2172  }
   2173 
   2174  for (int i = 0; i < fs->nfont; i++) {
   2175    char* family;
   2176 
   2177    if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, (FcChar8**)&family) !=
   2178        FcResultMatch) {
   2179      continue;
   2180    }
   2181 
   2182    // Remove duplicates...
   2183    nsAutoString strFamily;
   2184    AppendUTF8toUTF16(MakeStringSpan(family), strFamily);
   2185    if (aListOfFonts.Contains(strFamily)) {
   2186      continue;
   2187    }
   2188 
   2189    aListOfFonts.AppendElement(strFamily);
   2190  }
   2191 
   2192  aListOfFonts.Sort();
   2193 }
   2194 
   2195 void gfxFcPlatformFontList::GetFontList(nsAtom* aLangGroup,
   2196                                        const nsACString& aGenericFamily,
   2197                                        nsTArray<nsString>& aListOfFonts) {
   2198  // Get the list of font family names using fontconfig
   2199  GetSystemFontList(aListOfFonts, aLangGroup);
   2200 
   2201  // Under Linux, the generics "serif", "sans-serif" and "monospace"
   2202  // are included in the pref fontlist. These map to whatever fontconfig
   2203  // decides they should be for a given language, rather than one of the
   2204  // fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*)
   2205  bool serif = false, sansSerif = false, monospace = false;
   2206  if (aGenericFamily.IsEmpty())
   2207    serif = sansSerif = monospace = true;
   2208  else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
   2209    serif = true;
   2210  else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
   2211    sansSerif = true;
   2212  else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
   2213    monospace = true;
   2214  else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
   2215           aGenericFamily.LowerCaseEqualsLiteral("fantasy") ||
   2216           aGenericFamily.LowerCaseEqualsLiteral("math"))
   2217    serif = sansSerif = true;
   2218  else
   2219    MOZ_ASSERT_UNREACHABLE("unexpected CSS generic font family");
   2220 
   2221  // The first in the list becomes the default in
   2222  // FontBuilder.readFontSelection() if the preference-selected font is not
   2223  // available, so put system configured defaults first.
   2224  if (monospace) aListOfFonts.InsertElementAt(0, u"monospace"_ns);
   2225  if (sansSerif) aListOfFonts.InsertElementAt(0, u"sans-serif"_ns);
   2226  if (serif) aListOfFonts.InsertElementAt(0, u"serif"_ns);
   2227 }
   2228 
   2229 FontFamily gfxFcPlatformFontList::GetDefaultFontForPlatform(
   2230    FontVisibilityProvider* aFontVisibilityProvider, const gfxFontStyle* aStyle,
   2231    nsAtom* aLanguage) {
   2232  // We hardcode Arimo also in preferences, and using the original code that
   2233  // tried to resolve a non-existing font did not play well with our fontconfig
   2234  // configuration. See
   2235  // https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/41043
   2236  PrefFontList* prefFonts =
   2237      FindGenericFamilies(aFontVisibilityProvider, "Arimo"_ns,
   2238                          aLanguage ? aLanguage : nsGkAtoms::x_western);
   2239  NS_ASSERTION(prefFonts, "null list of generic fonts");
   2240  if (prefFonts && !prefFonts->IsEmpty()) {
   2241    return (*prefFonts)[0];
   2242  }
   2243  return FontFamily();
   2244 }
   2245 
   2246 gfxFontEntry* gfxFcPlatformFontList::LookupLocalFont(
   2247    FontVisibilityProvider* aFontVisibilityProvider,
   2248    const nsACString& aFontName, WeightRange aWeightForEntry,
   2249    StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
   2250  AutoLock lock(mLock);
   2251 
   2252  nsAutoCString keyName(aFontName);
   2253  ToLowerCase(keyName);
   2254 
   2255  if (SharedFontList()) {
   2256    return LookupInSharedFaceNameList(aFontVisibilityProvider, aFontName,
   2257                                      aWeightForEntry, aStretchForEntry,
   2258                                      aStyleForEntry);
   2259  }
   2260 
   2261  // if name is not in the global list, done
   2262  const auto fontPattern = mLocalNames.Lookup(keyName);
   2263  if (!fontPattern) {
   2264    return nullptr;
   2265  }
   2266 
   2267  return new gfxFontconfigFontEntry(aFontName, *fontPattern, aWeightForEntry,
   2268                                    aStretchForEntry, aStyleForEntry);
   2269 }
   2270 
   2271 gfxFontEntry* gfxFcPlatformFontList::MakePlatformFont(
   2272    const nsACString& aFontName, WeightRange aWeightForEntry,
   2273    StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
   2274    const uint8_t* aFontData, uint32_t aLength) {
   2275  RefPtr<FTUserFontData> ufd = new FTUserFontData(aFontData, aLength);
   2276  RefPtr<SharedFTFace> face = ufd->CloneFace();
   2277  if (!face) {
   2278    return nullptr;
   2279  }
   2280  return new gfxFontconfigFontEntry(aFontName, aWeightForEntry,
   2281                                    aStretchForEntry, aStyleForEntry,
   2282                                    std::move(face));
   2283 }
   2284 
   2285 static bool UseCustomFontconfigLookupsForLocale(const Locale& aLocale) {
   2286  return aLocale.Script().EqualTo("Hans") || aLocale.Script().EqualTo("Hant") ||
   2287         aLocale.Script().EqualTo("Jpan") || aLocale.Script().EqualTo("Kore") ||
   2288         aLocale.Script().EqualTo("Arab");
   2289 }
   2290 
   2291 bool gfxFcPlatformFontList::FindAndAddFamiliesLocked(
   2292    FontVisibilityProvider* aFontVisibilityProvider,
   2293    StyleGenericFontFamily aGeneric, const nsACString& aFamily,
   2294    nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags,
   2295    gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) {
   2296  nsAutoCString familyName(aFamily);
   2297  ToLowerCase(familyName);
   2298 
   2299  if (!(aFlags & FindFamiliesFlags::eQuotedFamilyName)) {
   2300    // deprecated generic names are explicitly converted to standard generics
   2301    bool isDeprecatedGeneric = false;
   2302    if (familyName.EqualsLiteral("sans") ||
   2303        familyName.EqualsLiteral("sans serif")) {
   2304      familyName.AssignLiteral("sans-serif");
   2305      isDeprecatedGeneric = true;
   2306    } else if (familyName.EqualsLiteral("mono")) {
   2307      familyName.AssignLiteral("monospace");
   2308      isDeprecatedGeneric = true;
   2309    }
   2310 
   2311    // fontconfig generics? use fontconfig to determine the family for lang
   2312    if (isDeprecatedGeneric ||
   2313        mozilla::StyleSingleFontFamily::Parse(familyName).IsGeneric()) {
   2314      PrefFontList* prefFonts =
   2315          FindGenericFamilies(aFontVisibilityProvider, familyName, aLanguage);
   2316      if (prefFonts && !prefFonts->IsEmpty()) {
   2317        aOutput->AppendElements(*prefFonts);
   2318        return true;
   2319      }
   2320      return false;
   2321    }
   2322  }
   2323 
   2324  // fontconfig allows conditional substitutions in such a way that it's
   2325  // difficult to distinguish an explicit substitution from other suggested
   2326  // choices. To sniff out explicit substitutions, compare the substitutions
   2327  // for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the
   2328  // substitutions
   2329  //
   2330  // Example:
   2331  //
   2332  //   serif ==> DejaVu Serif, ...
   2333  //   Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu
   2334  //   Serif
   2335  //
   2336  // In this case fontconfig is including Tex Gyre Heros and
   2337  // Nimbus Sans L as alternatives for Helvetica.
   2338 
   2339  // Because the FcConfigSubstitute call is quite expensive, we cache the
   2340  // actual font families found via this process.
   2341  nsAutoCString cacheKey;
   2342 
   2343  // For languages that use CJK or Arabic script, we include the language as
   2344  // part of the cache key because fontconfig may have lang-specific rules that
   2345  // specify different substitutions. (In theory, this could apply to *any*
   2346  // language, but it's highly unlikely to matter for non-CJK/Arabic scripts,
   2347  // and it gets really expensive to do separate lookups for 300+ distinct lang
   2348  // tags e.g. on wikipedia.org, when they all end up mapping to the same font
   2349  // list.)
   2350  // We remember the most recently checked aLanguage atom so that when the same
   2351  // language is used in many successive calls, we can avoid repeating the
   2352  // locale code processing every time.
   2353  if (aLanguage != mPrevLanguage) {
   2354    GetSampleLangForGroup(aLanguage, mSampleLang);
   2355    ToLowerCase(mSampleLang);
   2356    Locale locale;
   2357    mUseCustomLookups = LocaleParser::TryParse(mSampleLang, locale).isOk() &&
   2358                        locale.AddLikelySubtags().isOk() &&
   2359                        UseCustomFontconfigLookupsForLocale(locale);
   2360    mPrevLanguage = aLanguage;
   2361  }
   2362  if (mUseCustomLookups) {
   2363    cacheKey = mSampleLang;
   2364    cacheKey.Append(':');
   2365  }
   2366 
   2367  // Include the generic family in the cache key, to maintain the distinction
   2368  // between fonts explicitly requested by name and the results of resolving
   2369  // CSS generics.
   2370  cacheKey.AppendInt(int(aGeneric));
   2371  cacheKey.Append(':');
   2372 
   2373  cacheKey.Append(familyName);
   2374  auto vis = aFontVisibilityProvider
   2375                 ? aFontVisibilityProvider->GetFontVisibility()
   2376                 : FontVisibility::User;
   2377  cacheKey.Append(':');
   2378  cacheKey.AppendInt(int(vis));
   2379  if (const auto& cached = mFcSubstituteCache.Lookup(cacheKey)) {
   2380    if (cached->IsEmpty()) {
   2381      return false;
   2382    }
   2383    aOutput->AppendElements(*cached);
   2384    return true;
   2385  }
   2386 
   2387  // It wasn't in the cache, so we need to ask fontconfig...
   2388  const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
   2389  const FcChar8* terminator = nullptr;
   2390  RefPtr<FcPattern> sentinelSubst = dont_AddRef(FcPatternCreate());
   2391  FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
   2392  if (!mSampleLang.IsEmpty()) {
   2393    FcPatternAddString(sentinelSubst, FC_LANG, ToFcChar8Ptr(mSampleLang.get()));
   2394  }
   2395  FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
   2396 
   2397  // If the sentinel name is still present, we'll use that as the terminator
   2398  // for the family names we collect; this means that if fontconfig prepends
   2399  // additional family names (e.g. an emoji font, or lang-specific preferred
   2400  // font) to all patterns, it won't simply mask all actual requested names.
   2401  // If the sentinel has been deleted/replaced altogether, then we'll take
   2402  // the first substitute name as the new terminator.
   2403  FcChar8* substName;
   2404  for (int i = 0; FcPatternGetString(sentinelSubst, FC_FAMILY, i, &substName) ==
   2405                  FcResultMatch;
   2406       i++) {
   2407    if (FcStrCmp(substName, kSentinelName) == 0) {
   2408      terminator = kSentinelName;
   2409      break;
   2410    }
   2411    if (!terminator) {
   2412      terminator = substName;
   2413    }
   2414  }
   2415 
   2416  // substitutions for font, -moz-sentinel pattern
   2417  RefPtr<FcPattern> fontWithSentinel = dont_AddRef(FcPatternCreate());
   2418  FcPatternAddString(fontWithSentinel, FC_FAMILY,
   2419                     ToFcChar8Ptr(familyName.get()));
   2420  FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
   2421  if (!mSampleLang.IsEmpty()) {
   2422    FcPatternAddString(sentinelSubst, FC_LANG, ToFcChar8Ptr(mSampleLang.get()));
   2423  }
   2424  FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
   2425 
   2426  // Add all font family matches until reaching the terminator.
   2427  AutoTArray<FamilyAndGeneric, 10> cachedFamilies;
   2428  for (int i = 0; FcPatternGetString(fontWithSentinel, FC_FAMILY, i,
   2429                                     &substName) == FcResultMatch;
   2430       i++) {
   2431    if (terminator && FcStrCmp(substName, terminator) == 0) {
   2432      break;
   2433    }
   2434    gfxPlatformFontList::FindAndAddFamiliesLocked(
   2435        aFontVisibilityProvider, aGeneric,
   2436        nsDependentCString(ToCharPtr(substName)), &cachedFamilies, aFlags,
   2437        aStyle, aLanguage);
   2438  }
   2439 
   2440  const auto& insertedCachedFamilies =
   2441      mFcSubstituteCache.InsertOrUpdate(cacheKey, std::move(cachedFamilies));
   2442 
   2443  if (insertedCachedFamilies.IsEmpty()) {
   2444    return false;
   2445  }
   2446  aOutput->AppendElements(insertedCachedFamilies);
   2447  return true;
   2448 }
   2449 
   2450 bool gfxFcPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
   2451                                                  nsACString& aFamilyName) {
   2452  aFamilyName.Truncate();
   2453 
   2454  // The fontconfig list of fonts includes generic family names in the
   2455  // font list. For these, just use the generic name.
   2456  if (aFontName.EqualsLiteral("serif") ||
   2457      aFontName.EqualsLiteral("sans-serif") ||
   2458      aFontName.EqualsLiteral("monospace")) {
   2459    aFamilyName.Assign(aFontName);
   2460    return true;
   2461  }
   2462 
   2463  RefPtr<FcPattern> pat = dont_AddRef(FcPatternCreate());
   2464  if (!pat) {
   2465    return true;
   2466  }
   2467 
   2468  UniquePtr<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
   2469  if (!os) {
   2470    return true;
   2471  }
   2472 
   2473  // add the family name to the pattern
   2474  FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(aFontName.get()));
   2475 
   2476  UniquePtr<FcFontSet> givenFS(FcFontList(nullptr, pat, os.get()));
   2477  if (!givenFS) {
   2478    return true;
   2479  }
   2480 
   2481  // See if there is a font face with first family equal to the given family
   2482  // (needs to be in sync with names coming from GetFontList())
   2483  nsTArray<nsCString> candidates;
   2484  for (int i = 0; i < givenFS->nfont; i++) {
   2485    char* firstFamily;
   2486 
   2487    if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
   2488                           (FcChar8**)&firstFamily) != FcResultMatch) {
   2489      continue;
   2490    }
   2491 
   2492    nsDependentCString first(firstFamily);
   2493    if (!candidates.Contains(first)) {
   2494      candidates.AppendElement(first);
   2495 
   2496      if (aFontName.Equals(first)) {
   2497        aFamilyName.Assign(aFontName);
   2498        return true;
   2499      }
   2500    }
   2501  }
   2502 
   2503  // Because fontconfig conflates different family name types, need to
   2504  // double check that the candidate name is not simply a different
   2505  // name type. For example, if a font with nameID=16 "Minion Pro" and
   2506  // nameID=21 "Minion Pro Caption" exists, calling FcFontList with
   2507  // family="Minion Pro" will return a set of patterns some of which
   2508  // will have a first family of "Minion Pro Caption". Ignore these
   2509  // patterns and use the first candidate that maps to a font set with
   2510  // the same number of faces and an identical set of patterns.
   2511  for (uint32_t j = 0; j < candidates.Length(); ++j) {
   2512    FcPatternDel(pat, FC_FAMILY);
   2513    FcPatternAddString(pat, FC_FAMILY, (FcChar8*)candidates[j].get());
   2514 
   2515    UniquePtr<FcFontSet> candidateFS(FcFontList(nullptr, pat, os.get()));
   2516    if (!candidateFS) {
   2517      return true;
   2518    }
   2519 
   2520    if (candidateFS->nfont != givenFS->nfont) {
   2521      continue;
   2522    }
   2523 
   2524    bool equal = true;
   2525    for (int i = 0; i < givenFS->nfont; ++i) {
   2526      if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
   2527        equal = false;
   2528        break;
   2529      }
   2530    }
   2531    if (equal) {
   2532      aFamilyName = candidates[j];
   2533      return true;
   2534    }
   2535  }
   2536 
   2537  // didn't find localized name, leave family name blank
   2538  return true;
   2539 }
   2540 
   2541 void gfxFcPlatformFontList::AddGenericFonts(
   2542    FontVisibilityProvider* aFontVisibilityProvider,
   2543    StyleGenericFontFamily aGenericType, nsAtom* aLanguage,
   2544    nsTArray<FamilyAndGeneric>& aFamilyList) {
   2545  // TODO(eri): For now the math generic language uses the legacy
   2546  // "serif.x-math". See `gfxPlatformFontList::AddGenericFonts`.
   2547  if (StaticPrefs::mathml_font_family_math_enabled() &&
   2548      aGenericType == StyleGenericFontFamily::Math) {
   2549    aGenericType = StyleGenericFontFamily::Serif;
   2550    aLanguage = nsGkAtoms::x_math;
   2551  }
   2552 
   2553  const char* generic = GetGenericName(aGenericType);
   2554  NS_ASSERTION(generic, "weird generic font type");
   2555  if (!generic) {
   2556    return;
   2557  }
   2558 
   2559  // By default, most font prefs on Linux map to "use fontconfig"
   2560  // keywords. So only need to explicitly lookup font pref if
   2561  // non-default settings exist, or if we are the system-ui font, which we deal
   2562  // with in the base class.
   2563  const bool isSystemUi = aGenericType == StyleGenericFontFamily::SystemUi;
   2564  bool usePrefFontList = isSystemUi;
   2565 
   2566  nsAutoCString genericToLookup(generic);
   2567  if ((!mAlwaysUseFontconfigGenerics && aLanguage) ||
   2568      aLanguage == nsGkAtoms::x_math) {
   2569    nsAtom* langGroup = GetLangGroup(aLanguage);
   2570    nsAutoCString fontlistValue;
   2571    mFontPrefs->LookupName(PrefName(generic, langGroup), fontlistValue);
   2572    if (fontlistValue.IsEmpty()) {
   2573      // The font name list may have two or more family names as comma
   2574      // separated list.  In such case, not matching with generic font
   2575      // name is fine because if the list prefers specific font, we
   2576      // should try to use the pref with complicated path.
   2577      mFontPrefs->LookupNameList(PrefName(generic, langGroup), fontlistValue);
   2578    }
   2579    if (!fontlistValue.IsEmpty()) {
   2580      if (!fontlistValue.EqualsLiteral("serif") &&
   2581          !fontlistValue.EqualsLiteral("sans-serif") &&
   2582          !fontlistValue.EqualsLiteral("monospace")) {
   2583        usePrefFontList = true;
   2584      } else {
   2585        // serif, sans-serif or monospace was specified
   2586        genericToLookup = fontlistValue;
   2587      }
   2588    }
   2589  }
   2590 
   2591  // when pref fonts exist, use standard pref font lookup
   2592  if (usePrefFontList) {
   2593    gfxPlatformFontList::AddGenericFonts(aFontVisibilityProvider, aGenericType,
   2594                                         aLanguage, aFamilyList);
   2595    if (!isSystemUi) {
   2596      return;
   2597    }
   2598  }
   2599 
   2600  AutoLock lock(mLock);
   2601  PrefFontList* prefFonts =
   2602      FindGenericFamilies(aFontVisibilityProvider, genericToLookup, aLanguage);
   2603  NS_ASSERTION(prefFonts, "null generic font list");
   2604  aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
   2605  for (auto& f : *prefFonts) {
   2606    aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
   2607  }
   2608 }
   2609 
   2610 void gfxFcPlatformFontList::ClearLangGroupPrefFontsLocked() {
   2611  ClearGenericMappingsLocked();
   2612  gfxPlatformFontList::ClearLangGroupPrefFontsLocked();
   2613  mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
   2614 }
   2615 
   2616 gfxPlatformFontList::PrefFontList* gfxFcPlatformFontList::FindGenericFamilies(
   2617    FontVisibilityProvider* aFontVisibilityProvider, const nsCString& aGeneric,
   2618    nsAtom* aLanguage) {
   2619  // set up name
   2620  nsAutoCString fcLang;
   2621  GetSampleLangForGroup(aLanguage, fcLang);
   2622  ToLowerCase(fcLang);
   2623 
   2624  nsAutoCString cacheKey(aGeneric);
   2625  if (fcLang.Length() > 0) {
   2626    cacheKey.Append('-');
   2627    // If the script is CJK or Arabic, we cache by lang so that different fonts
   2628    // various locales can be supported; but otherwise, we cache by script
   2629    // subtag, to avoid a proliferation of entries for Western & similar
   2630    // languages.
   2631    // In theory, this means we could fail to respect custom fontconfig rules
   2632    // for individual (non-CJK/Arab) languages that share the same script, but
   2633    // such setups are probably vanishingly rare.
   2634    Locale locale;
   2635    if (LocaleParser::TryParse(fcLang, locale).isOk() &&
   2636        locale.AddLikelySubtags().isOk()) {
   2637      if (UseCustomFontconfigLookupsForLocale(locale)) {
   2638        cacheKey.Append(fcLang);
   2639      } else {
   2640        cacheKey.Append(locale.Script().Span());
   2641      }
   2642    } else {
   2643      cacheKey.Append(fcLang);
   2644    }
   2645  }
   2646 
   2647  // try to get the family from the cache
   2648  return mGenericMappings.WithEntryHandle(
   2649      cacheKey, [&](auto&& entry) -> PrefFontList* {
   2650        if (!entry) {
   2651          // if not found, ask fontconfig to pick the appropriate font
   2652          RefPtr<FcPattern> genericPattern = dont_AddRef(FcPatternCreate());
   2653          FcPatternAddString(genericPattern, FC_FAMILY,
   2654                             ToFcChar8Ptr(aGeneric.get()));
   2655 
   2656          // -- prefer scalable fonts
   2657          FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
   2658 
   2659          // -- add the lang to the pattern
   2660          if (!fcLang.IsEmpty()) {
   2661            FcPatternAddString(genericPattern, FC_LANG,
   2662                               ToFcChar8Ptr(fcLang.get()));
   2663          }
   2664 
   2665          // -- perform substitutions
   2666          FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
   2667          FcDefaultSubstitute(genericPattern);
   2668 
   2669          // -- sort to get the closest matches
   2670          FcResult result;
   2671          UniquePtr<FcFontSet> faces(
   2672              FcFontSort(nullptr, genericPattern, FcFalse, nullptr, &result));
   2673 
   2674          if (!faces) {
   2675            return nullptr;
   2676          }
   2677 
   2678          // -- select the fonts to be used for the generic
   2679          auto prefFonts = MakeUnique<PrefFontList>();  // can be empty but in
   2680                                                        // practice won't happen
   2681          uint32_t limit = StaticPrefs::
   2682              gfx_font_rendering_fontconfig_max_generic_substitutions();
   2683          bool foundFontWithLang = false;
   2684          for (int i = 0; i < faces->nfont; i++) {
   2685            FcPattern* font = faces->fonts[i];
   2686            FcChar8* mappedGeneric = nullptr;
   2687 
   2688            FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
   2689            if (mappedGeneric) {
   2690              mLock.AssertCurrentThreadIn();
   2691              nsAutoCString mappedGenericName(ToCharPtr(mappedGeneric));
   2692              AutoTArray<FamilyAndGeneric, 1> genericFamilies;
   2693              if (gfxPlatformFontList::FindAndAddFamiliesLocked(
   2694                      aFontVisibilityProvider, StyleGenericFontFamily::None,
   2695                      mappedGenericName, &genericFamilies,
   2696                      FindFamiliesFlags(0))) {
   2697                MOZ_ASSERT(genericFamilies.Length() == 1,
   2698                           "expected a single family");
   2699                if (!prefFonts->Contains(genericFamilies[0].mFamily)) {
   2700                  prefFonts->AppendElement(genericFamilies[0].mFamily);
   2701                  bool foundLang =
   2702                      !fcLang.IsEmpty() &&
   2703                      PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
   2704                  foundFontWithLang = foundFontWithLang || foundLang;
   2705                  // check to see if the list is full
   2706                  if (prefFonts->Length() >= limit) {
   2707                    break;
   2708                  }
   2709                }
   2710              }
   2711            }
   2712          }
   2713 
   2714          // if no font in the list matches the lang, trim all but the first one
   2715          if (!prefFonts->IsEmpty() && !foundFontWithLang) {
   2716            prefFonts->TruncateLength(1);
   2717          }
   2718 
   2719          entry.Insert(std::move(prefFonts));
   2720        }
   2721        return entry->get();
   2722      });
   2723 }
   2724 
   2725 bool gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics() {
   2726  for (auto iter = mFontPrefs->NameIter(); !iter.Done(); iter.Next()) {
   2727    // Check whether all font.name prefs map to generic keywords
   2728    // and that the pref name and keyword match.
   2729    //   Ex: font.name.serif.ar ==> "serif" (ok)
   2730    //   Ex: font.name.serif.ar ==> "monospace" (return false)
   2731    //   Ex: font.name.serif.ar ==> "DejaVu Serif" (return false)
   2732    //   Ex: font.name.serif.ar ==> "" and
   2733    //       font.name-list.serif.ar ==> "serif" (ok)
   2734    //   Ex: font.name.serif.ar ==> "" and
   2735    //       font.name-list.serif.ar ==> "Something, serif"
   2736    //                                           (return false)
   2737    const nsACString* prefValue = &iter.Data();
   2738    nsAutoCString listValue;
   2739    if (iter.Data().IsEmpty()) {
   2740      // The font name list may have two or more family names as comma
   2741      // separated list.  In such case, not matching with generic font
   2742      // name is fine because if the list prefers specific font, this
   2743      // should return false.
   2744      mFontPrefs->LookupNameList(iter.Key(), listValue);
   2745      prefValue = &listValue;
   2746    }
   2747 
   2748    nsCCharSeparatedTokenizer tokenizer(iter.Key(), '.');
   2749    const nsDependentCSubstring& generic = tokenizer.nextToken();
   2750    const nsDependentCSubstring& langGroup = tokenizer.nextToken();
   2751 
   2752    if (!langGroup.EqualsLiteral("x-math") && !generic.Equals(*prefValue)) {
   2753      return false;
   2754    }
   2755  }
   2756  return true;
   2757 }
   2758 
   2759 /* static */
   2760 void gfxFcPlatformFontList::CheckFontUpdates(nsITimer* aTimer, void* aThis) {
   2761  // A content process is not supposed to check this directly;
   2762  // it will be notified by the parent when the font list changes.
   2763  MOZ_ASSERT(XRE_IsParentProcess());
   2764 
   2765  // check for font updates
   2766  FcInitBringUptoDate();
   2767 
   2768  // update fontlist if current config changed
   2769  gfxFcPlatformFontList* pfl = static_cast<gfxFcPlatformFontList*>(aThis);
   2770  FcConfig* current = FcConfigGetCurrent();
   2771  if (current != pfl->GetLastConfig()) {
   2772    pfl->UpdateFontList();
   2773    gfxPlatform::GlobalReflowFlags flags =
   2774        gfxPlatform::GlobalReflowFlags::NeedsReframe |
   2775        gfxPlatform::GlobalReflowFlags::FontsChanged |
   2776        gfxPlatform::GlobalReflowFlags::BroadcastToChildren;
   2777    gfxPlatform::ForceGlobalReflow(flags);
   2778    mozilla::dom::ContentParent::NotifyUpdatedFonts(true);
   2779  }
   2780 }
   2781 
   2782 gfxFontFamily* gfxFcPlatformFontList::CreateFontFamily(
   2783    const nsACString& aName, FontVisibility aVisibility) const {
   2784  return new gfxFontconfigFontFamily(aName, aVisibility);
   2785 }
   2786 
   2787 // mapping of moz lang groups ==> default lang
   2788 struct MozLangGroupData {
   2789  nsAtom* const& mozLangGroup;
   2790  const char* defaultLang;
   2791 };
   2792 
   2793 const MozLangGroupData MozLangGroups[] = {
   2794    {nsGkAtoms::x_western, "en"},    {nsGkAtoms::x_cyrillic, "ru"},
   2795    {nsGkAtoms::x_devanagari, "hi"}, {nsGkAtoms::x_tamil, "ta"},
   2796    {nsGkAtoms::x_armn, "hy"},       {nsGkAtoms::x_beng, "bn"},
   2797    {nsGkAtoms::x_cans, "iu"},       {nsGkAtoms::x_ethi, "am"},
   2798    {nsGkAtoms::x_geor, "ka"},       {nsGkAtoms::x_gujr, "gu"},
   2799    {nsGkAtoms::x_guru, "pa"},       {nsGkAtoms::x_khmr, "km"},
   2800    {nsGkAtoms::x_knda, "kn"},       {nsGkAtoms::x_mlym, "ml"},
   2801    {nsGkAtoms::x_orya, "or"},       {nsGkAtoms::x_sinh, "si"},
   2802    {nsGkAtoms::x_tamil, "ta"},      {nsGkAtoms::x_telu, "te"},
   2803    {nsGkAtoms::x_tibt, "bo"},       {nsGkAtoms::Unicode, 0}};
   2804 
   2805 bool gfxFcPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
   2806                                            nsAtom* aLangGroup,
   2807                                            nsACString& aFcLang) {
   2808  // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
   2809  // aOSLang is in the form "language[_territory][.codeset][@modifier]".
   2810  // fontconfig takes languages in the form "language-territory".
   2811  // nsLanguageAtomService takes languages in the form language-subtag,
   2812  // where subtag may be a territory.  fontconfig and nsLanguageAtomService
   2813  // handle case-conversion for us.
   2814  const char *pos, *end;
   2815  aOSLang.BeginReading(pos);
   2816  aOSLang.EndReading(end);
   2817  aFcLang.Truncate();
   2818  while (pos < end) {
   2819    switch (*pos) {
   2820      case '.':
   2821      case '@':
   2822        end = pos;
   2823        break;
   2824      case '_':
   2825        aFcLang.Append('-');
   2826        break;
   2827      default:
   2828        aFcLang.Append(*pos);
   2829    }
   2830    ++pos;
   2831  }
   2832 
   2833  nsAtom* atom = mLangService->LookupLanguage(aFcLang);
   2834  return atom == aLangGroup;
   2835 }
   2836 
   2837 void gfxFcPlatformFontList::GetSampleLangForGroup(nsAtom* aLanguage,
   2838                                                  nsACString& aLangStr) {
   2839  aLangStr.Truncate();
   2840  if (!aLanguage) {
   2841    return;
   2842  }
   2843 
   2844  // set up lang string
   2845  const MozLangGroupData* mozLangGroup = nullptr;
   2846 
   2847  // -- look it up in the list of moz lang groups
   2848  for (unsigned int i = 0; i < std::size(MozLangGroups); ++i) {
   2849    if (aLanguage == MozLangGroups[i].mozLangGroup) {
   2850      mozLangGroup = &MozLangGroups[i];
   2851      break;
   2852    }
   2853  }
   2854 
   2855  // -- not a mozilla lang group? Just return the BCP47 string
   2856  //    representation of the lang group
   2857  if (!mozLangGroup) {
   2858    // Not a special mozilla language group.
   2859    // Use aLanguage as a language code.
   2860    aLanguage->ToUTF8String(aLangStr);
   2861    return;
   2862  }
   2863 
   2864  // -- check the environment for the user's preferred language that
   2865  //    corresponds to this mozilla lang group.
   2866  const char* languages = getenv("LANGUAGE");
   2867  if (languages) {
   2868    const char separator = ':';
   2869 
   2870    for (const char* pos = languages; true; ++pos) {
   2871      if (*pos == '\0' || *pos == separator) {
   2872        if (languages < pos &&
   2873            TryLangForGroup(Substring(languages, pos), aLanguage, aLangStr)) {
   2874          return;
   2875        }
   2876 
   2877        if (*pos == '\0') {
   2878          break;
   2879        }
   2880 
   2881        languages = pos + 1;
   2882      }
   2883    }
   2884  }
   2885  const char* ctype = setlocale(LC_CTYPE, nullptr);
   2886  if (ctype &&
   2887      TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr)) {
   2888    return;
   2889  }
   2890 
   2891  if (mozLangGroup->defaultLang) {
   2892    aLangStr.Assign(mozLangGroup->defaultLang);
   2893  } else {
   2894    aLangStr.Truncate();
   2895  }
   2896 }
   2897 
   2898 #ifdef MOZ_BUNDLED_FONTS
   2899 void gfxFcPlatformFontList::ActivateBundledFonts() {
   2900  if (!mBundledFontsInitialized) {
   2901    mBundledFontsInitialized = true;
   2902    nsCOMPtr<nsIFile> localDir;
   2903    nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
   2904    if (NS_FAILED(rv)) {
   2905      return;
   2906    }
   2907    if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
   2908      return;
   2909    }
   2910    bool isDir;
   2911    if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
   2912      return;
   2913    }
   2914    if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
   2915      return;
   2916    }
   2917  }
   2918  if (!mBundledFontsPath.IsEmpty()) {
   2919    FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get()));
   2920  }
   2921 }
   2922 #endif
   2923 
   2924 #ifdef MOZ_WIDGET_GTK
   2925 /***************************************************************************
   2926 *
   2927 * These functions must be last in the file because it uses the system cairo
   2928 * library.  Above this point the cairo library used is the tree cairo.
   2929 */
   2930 
   2931 // Tree cairo symbols have different names.  Disable their activation through
   2932 // preprocessor macros.
   2933 #  undef cairo_ft_font_options_substitute
   2934 
   2935 #  undef cairo_font_options_create
   2936 #  undef cairo_font_options_destroy
   2937 #  undef cairo_font_options_copy
   2938 #  undef cairo_font_options_equal
   2939 
   2940 #  undef cairo_font_options_get_antialias
   2941 #  undef cairo_font_options_set_antialias
   2942 #  undef cairo_font_options_get_hint_style
   2943 #  undef cairo_font_options_set_hint_style
   2944 #  undef cairo_font_options_get_lcd_filter
   2945 #  undef cairo_font_options_set_lcd_filter
   2946 #  undef cairo_font_options_get_subpixel_order
   2947 #  undef cairo_font_options_set_subpixel_order
   2948 
   2949 // The system cairo functions are not declared because the include paths cause
   2950 // the gdk headers to pick up the tree cairo.h.
   2951 extern "C" {
   2952 NS_VISIBILITY_DEFAULT void cairo_ft_font_options_substitute(
   2953    const cairo_font_options_t* options, FcPattern* pattern);
   2954 
   2955 NS_VISIBILITY_DEFAULT cairo_font_options_t* cairo_font_options_copy(
   2956    const cairo_font_options_t*);
   2957 NS_VISIBILITY_DEFAULT cairo_font_options_t* cairo_font_options_create();
   2958 NS_VISIBILITY_DEFAULT void cairo_font_options_destroy(cairo_font_options_t*);
   2959 NS_VISIBILITY_DEFAULT cairo_bool_t cairo_font_options_equal(
   2960    const cairo_font_options_t*, const cairo_font_options_t*);
   2961 
   2962 NS_VISIBILITY_DEFAULT cairo_antialias_t
   2963 cairo_font_options_get_antialias(const cairo_font_options_t*);
   2964 NS_VISIBILITY_DEFAULT void cairo_font_options_set_antialias(
   2965    cairo_font_options_t*, cairo_antialias_t);
   2966 NS_VISIBILITY_DEFAULT cairo_hint_style_t
   2967 cairo_font_options_get_hint_style(const cairo_font_options_t*);
   2968 NS_VISIBILITY_DEFAULT void cairo_font_options_set_hint_style(
   2969    cairo_font_options_t*, cairo_hint_style_t);
   2970 NS_VISIBILITY_DEFAULT cairo_subpixel_order_t
   2971 cairo_font_options_get_subpixel_order(const cairo_font_options_t*);
   2972 NS_VISIBILITY_DEFAULT void cairo_font_options_set_subpixel_order(
   2973    cairo_font_options_t*, cairo_subpixel_order_t);
   2974 }
   2975 
   2976 void gfxFcPlatformFontList::ClearSystemFontOptions() {
   2977  if (mSystemFontOptions) {
   2978    cairo_font_options_destroy(mSystemFontOptions);
   2979    mSystemFontOptions = nullptr;
   2980  }
   2981  Factory::SetSubpixelOrder(SubpixelOrder::UNKNOWN);
   2982 }
   2983 
   2984 static void SetSubpixelOrderFromCairo(const cairo_font_options_t* aOptions) {
   2985  SubpixelOrder subpixelOrder = SubpixelOrder::UNKNOWN;
   2986  switch (cairo_font_options_get_subpixel_order(aOptions)) {
   2987    case CAIRO_SUBPIXEL_ORDER_RGB:
   2988      subpixelOrder = SubpixelOrder::RGB;
   2989      break;
   2990    case CAIRO_SUBPIXEL_ORDER_BGR:
   2991      subpixelOrder = SubpixelOrder::BGR;
   2992      break;
   2993    case CAIRO_SUBPIXEL_ORDER_VRGB:
   2994      subpixelOrder = SubpixelOrder::VRGB;
   2995      break;
   2996    case CAIRO_SUBPIXEL_ORDER_VBGR:
   2997      subpixelOrder = SubpixelOrder::VBGR;
   2998      break;
   2999    default:
   3000      break;
   3001  }
   3002  Factory::SetSubpixelOrder(subpixelOrder);
   3003 }
   3004 
   3005 bool gfxFcPlatformFontList::UpdateSystemFontOptions() {
   3006  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
   3007 
   3008  if (gfxPlatform::IsHeadless()) {
   3009    return false;
   3010  }
   3011 
   3012 #  ifdef MOZ_X11
   3013  {
   3014    // This one shouldn't change during the X session.
   3015    int lcdfilter;
   3016    GdkDisplay* dpy = gdk_display_get_default();
   3017    if (mozilla::widget::GdkIsX11Display(dpy) &&
   3018        GetXftInt(GDK_DISPLAY_XDISPLAY(dpy), "lcdfilter", &lcdfilter)) {
   3019      mFreetypeLcdSetting = lcdfilter;
   3020    }
   3021  }
   3022 #  endif  // MOZ_X11
   3023 
   3024  const cairo_font_options_t* options =
   3025      gdk_screen_get_font_options(gdk_screen_get_default());
   3026  if (!options) {
   3027    bool changed = !!mSystemFontOptions;
   3028    ClearSystemFontOptions();
   3029    return changed;
   3030  }
   3031 
   3032  cairo_font_options_t* newOptions = cairo_font_options_copy(options);
   3033 
   3034  if (mSystemFontOptions &&
   3035      cairo_font_options_equal(mSystemFontOptions, options)) {
   3036    cairo_font_options_destroy(newOptions);
   3037    return false;
   3038  }
   3039 
   3040  SetSubpixelOrderFromCairo(options);
   3041 
   3042  ClearSystemFontOptions();
   3043  mSystemFontOptions = newOptions;
   3044  return true;
   3045 }
   3046 
   3047 void gfxFcPlatformFontList::SystemFontOptionsToIpc(
   3048    dom::SystemFontOptions& aOptions) {
   3049  aOptions.antialias() =
   3050      mSystemFontOptions ? cairo_font_options_get_antialias(mSystemFontOptions)
   3051                         : CAIRO_ANTIALIAS_DEFAULT;
   3052  aOptions.subpixelOrder() =
   3053      mSystemFontOptions
   3054          ? cairo_font_options_get_subpixel_order(mSystemFontOptions)
   3055          : CAIRO_SUBPIXEL_ORDER_DEFAULT;
   3056  aOptions.hintStyle() =
   3057      mSystemFontOptions ? cairo_font_options_get_hint_style(mSystemFontOptions)
   3058                         : CAIRO_HINT_STYLE_DEFAULT;
   3059  aOptions.lcdFilter() = mFreetypeLcdSetting;
   3060 }
   3061 
   3062 void gfxFcPlatformFontList::UpdateSystemFontOptionsFromIpc(
   3063    const dom::SystemFontOptions& aOptions) {
   3064  ClearSystemFontOptions();
   3065  mSystemFontOptions = cairo_font_options_create();
   3066  cairo_font_options_set_antialias(mSystemFontOptions,
   3067                                   cairo_antialias_t(aOptions.antialias()));
   3068  cairo_font_options_set_hint_style(mSystemFontOptions,
   3069                                    cairo_hint_style_t(aOptions.hintStyle()));
   3070  cairo_font_options_set_subpixel_order(
   3071      mSystemFontOptions, cairo_subpixel_order_t(aOptions.subpixelOrder()));
   3072  mFreetypeLcdSetting = aOptions.lcdFilter();
   3073  SetSubpixelOrderFromCairo(mSystemFontOptions);
   3074 }
   3075 
   3076 void gfxFcPlatformFontList::SubstituteSystemFontOptions(FcPattern* aPattern) {
   3077  if (mSystemFontOptions) {
   3078    cairo_ft_font_options_substitute(mSystemFontOptions, aPattern);
   3079  }
   3080 
   3081  if (mFreetypeLcdSetting != -1) {
   3082    FcValue value;
   3083    if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) {
   3084      FcPatternAddInteger(aPattern, FC_LCD_FILTER, mFreetypeLcdSetting);
   3085    }
   3086  }
   3087 }
   3088 
   3089 #endif  // MOZ_WIDGET_GTK