tor-browser

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

gfxPlatformFontList.cpp (116856B)


      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 #include "mozilla/gfx/Logging.h"
      8 #include "mozilla/intl/Locale.h"
      9 #include "mozilla/intl/LocaleService.h"
     10 #include "mozilla/intl/OSPreferences.h"
     11 
     12 #include "gfxPlatformFontList.h"
     13 #include "gfxTextRun.h"
     14 #include "gfxUserFontSet.h"
     15 #include "SharedFontList-impl.h"
     16 
     17 #include "GeckoProfiler.h"
     18 #include "nsCRT.h"
     19 #include "nsGkAtoms.h"
     20 #include "nsPresContext.h"
     21 #include "nsServiceManagerUtils.h"
     22 #include "nsUnicharUtils.h"
     23 #include "nsUnicodeProperties.h"
     24 #include "nsXULAppAPI.h"
     25 
     26 #include "mozilla/AppShutdown.h"
     27 #include "mozilla/BinarySearch.h"
     28 #include "mozilla/Likely.h"
     29 #include "mozilla/MemoryReporting.h"
     30 #include "mozilla/Mutex.h"
     31 #include "mozilla/Preferences.h"
     32 #include "mozilla/StaticPrefs_gfx.h"
     33 #include "mozilla/StaticPrefs_layout.h"
     34 #include "mozilla/StaticPrefs_mathml.h"
     35 #include "mozilla/glean/GfxMetrics.h"
     36 #include "mozilla/TimeStamp.h"
     37 #include "mozilla/dom/BlobImpl.h"
     38 #include "mozilla/dom/ContentChild.h"
     39 #include "mozilla/dom/ContentParent.h"
     40 #include "mozilla/dom/ContentProcessMessageManager.h"
     41 #include "mozilla/dom/Document.h"
     42 #include "mozilla/gfx/2D.h"
     43 #include "mozilla/ipc/FileDescriptorUtils.h"
     44 #include "mozilla/TextUtils.h"
     45 
     46 #include "base/eintr_wrapper.h"
     47 
     48 #include <locale.h>
     49 #include <numeric>
     50 
     51 using namespace mozilla;
     52 using mozilla::intl::Locale;
     53 using mozilla::intl::LocaleParser;
     54 using mozilla::intl::LocaleService;
     55 using mozilla::intl::OSPreferences;
     56 
     57 #define LOG_FONTLIST(args) \
     58  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
     59 #define LOG_FONTLIST_ENABLED() \
     60  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
     61 #define LOG_FONTINIT(args) \
     62  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
     63 #define LOG_FONTINIT_ENABLED() \
     64  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
     65 
     66 gfxPlatformFontList* gfxPlatformFontList::sPlatformFontList = nullptr;
     67 
     68 // Character ranges that require complex-script shaping support in the font,
     69 // and so should be masked out by ReadCMAP if the necessary layout tables
     70 // are not present.
     71 // Currently used by the Mac and FT2 implementations only, but probably should
     72 // be supported on Windows as well.
     73 const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = {
     74    // Actually, now that harfbuzz supports presentation-forms shaping for
     75    // Arabic, we can render it without layout tables. So maybe we don't
     76    // want to mask the basic Arabic block here?
     77    // This affects the arabic-fallback-*.html reftests, which rely on
     78    // loading a font that *doesn't* have any GSUB table.
     79    {0x0600, 0x060B, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
     80    // skip 060C Arabic comma, also used by N'Ko etc
     81    {0x060D, 0x061A, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
     82    // skip 061B Arabic semicolon, also used by N'Ko etc
     83    {0x061C, 0x061E, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
     84    // skip 061F Arabic question mark, also used by N'Ko etc
     85    {0x0620, 0x063F, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
     86    // skip 0640 Arabic tatweel (for syriac, adlam, etc)
     87    {0x0641, 0x06D3, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
     88    // skip 06D4 Arabic full stop (for hanifi rohingya)
     89    {0x06D5, 0x06FF, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
     90    {0x0700, 0x074F, 1, {TRUETYPE_TAG('s', 'y', 'r', 'c'), 0, 0}},
     91    {0x0750, 0x077F, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
     92    {0x08A0, 0x08FF, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
     93    {0x0900,
     94     0x0963,
     95     2,
     96     {TRUETYPE_TAG('d', 'e', 'v', '2'), TRUETYPE_TAG('d', 'e', 'v', 'a'), 0}},
     97    // skip 0964 DEVANAGARI DANDA and 0965 DEVANAGARI DOUBLE DANDA, shared by
     98    // various other Indic writing systems
     99    {0x0966,
    100     0x097F,
    101     2,
    102     {TRUETYPE_TAG('d', 'e', 'v', '2'), TRUETYPE_TAG('d', 'e', 'v', 'a'), 0}},
    103    {0x0980,
    104     0x09FF,
    105     2,
    106     {TRUETYPE_TAG('b', 'n', 'g', '2'), TRUETYPE_TAG('b', 'e', 'n', 'g'), 0}},
    107    {0x0A00,
    108     0x0A7F,
    109     2,
    110     {TRUETYPE_TAG('g', 'u', 'r', '2'), TRUETYPE_TAG('g', 'u', 'r', 'u'), 0}},
    111    {0x0A80,
    112     0x0AFF,
    113     2,
    114     {TRUETYPE_TAG('g', 'j', 'r', '2'), TRUETYPE_TAG('g', 'u', 'j', 'r'), 0}},
    115    {0x0B00,
    116     0x0B7F,
    117     2,
    118     {TRUETYPE_TAG('o', 'r', 'y', '2'), TRUETYPE_TAG('o', 'r', 'y', 'a'), 0}},
    119    {0x0B80,
    120     0x0BFF,
    121     2,
    122     {TRUETYPE_TAG('t', 'm', 'l', '2'), TRUETYPE_TAG('t', 'a', 'm', 'l'), 0}},
    123    {0x0C00,
    124     0x0C7F,
    125     2,
    126     {TRUETYPE_TAG('t', 'e', 'l', '2'), TRUETYPE_TAG('t', 'e', 'l', 'u'), 0}},
    127    {0x0C80,
    128     0x0CFF,
    129     2,
    130     {TRUETYPE_TAG('k', 'n', 'd', '2'), TRUETYPE_TAG('k', 'n', 'd', 'a'), 0}},
    131    {0x0D00,
    132     0x0D7F,
    133     2,
    134     {TRUETYPE_TAG('m', 'l', 'm', '2'), TRUETYPE_TAG('m', 'l', 'y', 'm'), 0}},
    135    {0x0D80, 0x0DFF, 1, {TRUETYPE_TAG('s', 'i', 'n', 'h'), 0, 0}},
    136    {0x0E80, 0x0EFF, 1, {TRUETYPE_TAG('l', 'a', 'o', ' '), 0, 0}},
    137    {0x0F00, 0x0FFF, 1, {TRUETYPE_TAG('t', 'i', 'b', 't'), 0, 0}},
    138    {0x1000,
    139     0x109f,
    140     2,
    141     {TRUETYPE_TAG('m', 'y', 'm', 'r'), TRUETYPE_TAG('m', 'y', 'm', '2'), 0}},
    142    {0x1780, 0x17ff, 1, {TRUETYPE_TAG('k', 'h', 'm', 'r'), 0, 0}},
    143    // Khmer Symbols (19e0..19ff) don't seem to need any special shaping
    144    {0xaa60,
    145     0xaa7f,
    146     2,
    147     {TRUETYPE_TAG('m', 'y', 'm', 'r'), TRUETYPE_TAG('m', 'y', 'm', '2'), 0}},
    148    // Thai seems to be "renderable" without AAT morphing tables
    149    {0, 0, 0, {0, 0, 0}}  // terminator
    150 };
    151 
    152 static const char* kObservedPrefs[] = {
    153    "font.", "font.name-list.", "intl.accept_languages",  // hmmmm...
    154    "browser.display.use_document_fonts.icon_font_allowlist", nullptr};
    155 
    156 static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
    157 
    158 static const char kCJKFallbackOrderPref[] = "font.cjk_pref_fallback_order";
    159 
    160 // Pref for the list of icon font families that still get to override the
    161 // default font from prefs, even when use_document_fonts is disabled.
    162 // (This is to enable ligature-based icon fonts to keep working.)
    163 static const char kIconFontsPref[] =
    164    "browser.display.use_document_fonts.icon_font_allowlist";
    165 
    166 // xxx - this can probably be eliminated by reworking pref font handling code
    167 static const char* gPrefLangNames[] = {
    168 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
    169 #include "gfxFontPrefLangList.h"
    170 #undef FONT_PREF_LANG
    171 };
    172 
    173 static_assert(std::size(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
    174              "size of pref lang name array doesn't match pref lang enum size");
    175 
    176 class gfxFontListPrefObserver final : public nsIObserver {
    177  ~gfxFontListPrefObserver() = default;
    178 
    179 public:
    180  NS_DECL_ISUPPORTS
    181  NS_DECL_NSIOBSERVER
    182 };
    183 
    184 static void FontListPrefChanged(const char* aPref, void* aData = nullptr) {
    185  // XXX this could be made to only clear out the cache for the prefs that were
    186  // changed but it probably isn't that big a deal.
    187  gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
    188  gfxPlatformFontList::PlatformFontList()->LoadIconFontOverrideList();
    189  gfxFontCache::GetCache()->Flush();
    190 }
    191 
    192 static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
    193 
    194 NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
    195 
    196 #define LOCALES_CHANGED_TOPIC "intl:system-locales-changed"
    197 
    198 NS_IMETHODIMP
    199 gfxFontListPrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
    200                                 const char16_t* aData) {
    201  NS_ASSERTION(!strcmp(aTopic, LOCALES_CHANGED_TOPIC), "invalid topic");
    202  FontListPrefChanged(nullptr);
    203 
    204  if (XRE_IsParentProcess()) {
    205    gfxPlatform::GlobalReflowFlags flags =
    206        gfxPlatform::GlobalReflowFlags::BroadcastToChildren |
    207        gfxPlatform::GlobalReflowFlags::FontsChanged;
    208    gfxPlatform::ForceGlobalReflow(flags);
    209  }
    210  return NS_OK;
    211 }
    212 
    213 MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
    214 
    215 NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
    216 
    217 NS_IMETHODIMP
    218 gfxPlatformFontList::MemoryReporter::CollectReports(
    219    nsIHandleReportCallback* aHandleReport, nsISupports* aData,
    220    bool aAnonymize) {
    221  FontListSizes sizes;
    222  sizes.mFontListSize = 0;
    223  sizes.mFontTableCacheSize = 0;
    224  sizes.mCharMapsSize = 0;
    225  sizes.mLoaderSize = 0;
    226  sizes.mSharedSize = 0;
    227 
    228  gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(
    229      &FontListMallocSizeOf, &sizes);
    230 
    231  MOZ_COLLECT_REPORT(
    232      "explicit/gfx/font-list", KIND_HEAP, UNITS_BYTES, sizes.mFontListSize,
    233      "Memory used to manage the list of font families and faces.");
    234 
    235  MOZ_COLLECT_REPORT(
    236      "explicit/gfx/font-charmaps", KIND_HEAP, UNITS_BYTES, sizes.mCharMapsSize,
    237      "Memory used to record the character coverage of individual fonts.");
    238 
    239  if (sizes.mFontTableCacheSize) {
    240    MOZ_COLLECT_REPORT(
    241        "explicit/gfx/font-tables", KIND_HEAP, UNITS_BYTES,
    242        sizes.mFontTableCacheSize,
    243        "Memory used for cached font metrics and layout tables.");
    244  }
    245 
    246  if (sizes.mLoaderSize) {
    247    MOZ_COLLECT_REPORT("explicit/gfx/font-loader", KIND_HEAP, UNITS_BYTES,
    248                       sizes.mLoaderSize,
    249                       "Memory used for (platform-specific) font loader.");
    250  }
    251 
    252  if (sizes.mSharedSize) {
    253    MOZ_COLLECT_REPORT(
    254        "font-list-shmem", KIND_NONHEAP, UNITS_BYTES, sizes.mSharedSize,
    255        "Shared memory for system font list and character coverage data.");
    256  }
    257 
    258  return NS_OK;
    259 }
    260 
    261 PRThread* gfxPlatformFontList::sInitFontListThread = nullptr;
    262 
    263 static void InitFontListCallback(void* aFontList) {
    264  AUTO_PROFILER_REGISTER_THREAD("InitFontList");
    265  PR_SetCurrentThreadName("InitFontList");
    266 
    267  if (!static_cast<gfxPlatformFontList*>(aFontList)->InitFontList()) {
    268    gfxPlatformFontList::Shutdown();
    269  }
    270 }
    271 
    272 /* static */
    273 bool gfxPlatformFontList::Initialize(gfxPlatformFontList* aList) {
    274  sPlatformFontList = aList;
    275  if (XRE_IsParentProcess() &&
    276      StaticPrefs::gfx_font_list_omt_enabled_AtStartup() &&
    277      StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
    278      !gfxPlatform::InSafeMode()) {
    279    // We call nsRFPService::CalculateFontLocaleAllowlist so that it reads
    280    // intl.accept_languages while we are still on the main thread.
    281    nsRFPService::CalculateFontLocaleAllowlist();
    282    sInitFontListThread = PR_CreateThread(
    283        PR_USER_THREAD, InitFontListCallback, aList, PR_PRIORITY_NORMAL,
    284        PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
    285    return true;
    286  }
    287  if (aList->InitFontList()) {
    288    return true;
    289  }
    290  Shutdown();
    291  return false;
    292 }
    293 
    294 gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
    295    : mLock("gfxPlatformFontList lock"),
    296      mFontFamilies(64),
    297      mOtherFamilyNames(16),
    298      mSharedCmaps(8) {
    299  if (aNeedFullnamePostscriptNames) {
    300    mExtraNames = MakeUnique<ExtraNames>();
    301  }
    302 
    303  mLangService = nsLanguageAtomService::GetService();
    304 
    305  LoadBadUnderlineList();
    306  LoadIconFontOverrideList();
    307 
    308  mFontPrefs = MakeUnique<FontPrefs>();
    309 
    310  gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
    311  mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
    312 
    313  // pref changes notification setup
    314  NS_ASSERTION(!gFontListPrefObserver,
    315               "There has been font list pref observer already");
    316  gFontListPrefObserver = new gfxFontListPrefObserver();
    317  NS_ADDREF(gFontListPrefObserver);
    318 
    319  Preferences::RegisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
    320 
    321  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    322  if (obs) {
    323    obs->AddObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC, false);
    324  }
    325 
    326  // Only the parent process listens for whitelist changes; it will then
    327  // notify its children to rebuild their font lists.
    328  if (XRE_IsParentProcess()) {
    329    Preferences::RegisterCallback(FontWhitelistPrefChanged,
    330                                  kFontSystemWhitelistPref);
    331  }
    332 
    333  RegisterStrongMemoryReporter(new MemoryReporter());
    334 
    335  // initialize lang group pref font defaults (i.e. serif/sans-serif)
    336  mDefaultGenericsLangGroup.AppendElements(std::size(gPrefLangNames));
    337  for (uint32_t i = 0; i < std::size(gPrefLangNames); i++) {
    338    nsAutoCString prefDefaultFontType("font.default.");
    339    prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
    340    nsAutoCString serifOrSans;
    341    Preferences::GetCString(prefDefaultFontType.get(), serifOrSans);
    342    if (serifOrSans.EqualsLiteral("sans-serif")) {
    343      mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::SansSerif;
    344    } else {
    345      mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::Serif;
    346    }
    347  }
    348 }
    349 
    350 gfxPlatformFontList::~gfxPlatformFontList() {
    351  // Note that gfxPlatformFontList::Shutdown() ensures that the init-font-list
    352  // thread is finished before we come here.
    353 
    354  AutoLock lock(mLock);
    355 
    356  // We can't just do mSharedCmaps.Clear() here because removing each item from
    357  // the table would drop its last reference, and its Release() method would
    358  // then call back to MaybeRemoveCmap to search for it, which we can't do
    359  // while in the middle of clearing the table.
    360  // So we first clear the "shared" flag in each entry, so Release() won't try
    361  // to re-find them in the table.
    362  for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
    363    iter.Get()->mCharMap->ClearSharedFlag();
    364  }
    365  mSharedCmaps.Clear();
    366 
    367  ClearLangGroupPrefFontsLocked();
    368 
    369  NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
    370 
    371  Preferences::UnregisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
    372 
    373  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    374  if (obs) {
    375    obs->RemoveObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC);
    376  }
    377 
    378  if (XRE_IsParentProcess()) {
    379    Preferences::UnregisterCallback(FontWhitelistPrefChanged,
    380                                    kFontSystemWhitelistPref);
    381  }
    382  NS_RELEASE(gFontListPrefObserver);
    383 }
    384 
    385 FontVisibility gfxPlatformFontList::GetFontVisibility(nsCString& aFont,
    386                                                      bool& aFound) {
    387  AutoLock lock(mLock);
    388 
    389  GenerateFontListKey(aFont);
    390  if (SharedFontList()) {
    391    auto* font = SharedFontList()->FindFamily(aFont);
    392    if (font) {
    393      aFound = true;
    394      return font->Visibility();
    395    }
    396    aFound = false;
    397    return FontVisibility::Unknown;
    398  }
    399 
    400  {
    401    auto* font = mFontFamilies.GetWeak(aFont);
    402    if (font) {
    403      aFound = true;
    404      return font->Visibility();
    405    }
    406  }
    407 
    408  {
    409    auto* font = mOtherFamilyNames.GetWeak(aFont);
    410    if (font) {
    411      aFound = true;
    412      return font->Visibility();
    413    }
    414  }
    415 
    416  aFound = false;
    417  return FontVisibility::Unknown;
    418 }
    419 
    420 bool gfxPlatformFontList::GetMissingFonts(nsTArray<nsCString>& aMissingFonts) {
    421  AutoLock lock(mLock);
    422 
    423  auto fontLists = GetFilteredPlatformFontLists();
    424 
    425  if (!fontLists.Length()) {
    426    return false;
    427  }
    428 
    429  for (unsigned int i = 0; i < fontLists.Length(); i++) {
    430    for (unsigned int j = 0; j < fontLists[i].second; j++) {
    431      nsCString key(fontLists[i].first[j]);
    432      GenerateFontListKey(key);
    433 
    434      bool found = false;
    435      GetFontVisibility(key, found);
    436      if (!found) {
    437        aMissingFonts.AppendElement(key);
    438      }
    439    }
    440  }
    441  return true;
    442 }
    443 
    444 void gfxPlatformFontList::GetMissingFonts(nsCString& aMissingFonts) {
    445  nsTArray<nsCString> missingFonts;
    446  bool fontlistExists = GetMissingFonts(missingFonts);
    447 
    448  if (!fontlistExists) {
    449    aMissingFonts.AssignLiteral("No font list available for this device.");
    450    return;
    451  }
    452 
    453  if (missingFonts.IsEmpty()) {
    454    aMissingFonts.Append("All fonts are available.");
    455    return;
    456  }
    457 
    458  missingFonts.Sort();
    459  aMissingFonts.Append(StringJoin("|"_ns, missingFonts));
    460 }
    461 
    462 /* static */
    463 void gfxPlatformFontList::FontWhitelistPrefChanged(const char* aPref,
    464                                                   void* aClosure) {
    465  MOZ_ASSERT(XRE_IsParentProcess());
    466  auto* pfl = gfxPlatformFontList::PlatformFontList();
    467  pfl->UpdateFontList(true);
    468  dom::ContentParent::NotifyUpdatedFonts(true);
    469 }
    470 
    471 void gfxPlatformFontList::ApplyWhitelist() {
    472  uint32_t numFonts = mEnabledFontsList.Length();
    473  if (!mFontFamilyWhitelistActive) {
    474    return;
    475  }
    476  nsTHashSet<nsCString> familyNamesWhitelist;
    477  for (uint32_t i = 0; i < numFonts; i++) {
    478    nsAutoCString key;
    479    ToLowerCase(mEnabledFontsList[i], key);
    480    familyNamesWhitelist.Insert(key);
    481  }
    482  AutoTArray<RefPtr<gfxFontFamily>, 128> accepted;
    483  bool whitelistedFontFound = false;
    484  for (const auto& entry : mFontFamilies) {
    485    nsAutoCString fontFamilyName(entry.GetKey());
    486    ToLowerCase(fontFamilyName);
    487    if (familyNamesWhitelist.Contains(fontFamilyName)) {
    488      accepted.AppendElement(entry.GetData());
    489      whitelistedFontFound = true;
    490    }
    491  }
    492  if (!whitelistedFontFound) {
    493    // No whitelisted fonts found! Ignore the whitelist.
    494    return;
    495  }
    496  // Replace the original full list with the accepted subset.
    497  mFontFamilies.Clear();
    498  for (auto& f : accepted) {
    499    nsAutoCString fontFamilyName(f->Name());
    500    ToLowerCase(fontFamilyName);
    501    mFontFamilies.InsertOrUpdate(fontFamilyName, std::move(f));
    502  }
    503 }
    504 
    505 void gfxPlatformFontList::ApplyWhitelist(
    506    nsTArray<fontlist::Family::InitData>& aFamilies) {
    507  mLock.AssertCurrentThreadIn();
    508  if (!mFontFamilyWhitelistActive) {
    509    return;
    510  }
    511  nsTHashSet<nsCString> familyNamesWhitelist;
    512  for (const auto& item : mEnabledFontsList) {
    513    nsAutoCString key;
    514    ToLowerCase(item, key);
    515    familyNamesWhitelist.Insert(key);
    516  }
    517  AutoTArray<fontlist::Family::InitData, 128> accepted;
    518  bool keptNonHidden = false;
    519  for (auto& f : aFamilies) {
    520    if (familyNamesWhitelist.Contains(f.mKey)) {
    521      accepted.AppendElement(f);
    522      if (f.mVisibility != FontVisibility::Hidden) {
    523        keptNonHidden = true;
    524      }
    525    }
    526  }
    527  if (!keptNonHidden) {
    528    // No (visible) families were whitelisted: ignore the whitelist
    529    // and just leave the fontlist unchanged.
    530    return;
    531  }
    532  aFamilies = std::move(accepted);
    533 }
    534 
    535 bool gfxPlatformFontList::FamilyInList(const nsACString& aName,
    536                                       const char* aList[], size_t aCount) {
    537  size_t result;
    538  return BinarySearchIf(
    539      aList, 0, aCount,
    540      [&](const char* const aVal) -> int {
    541        return nsCaseInsensitiveUTF8StringComparator(
    542            aName.BeginReading(), aVal, aName.Length(), strlen(aVal));
    543      },
    544      &result);
    545 }
    546 
    547 void gfxPlatformFontList::CheckFamilyList(const char* aList[], size_t aCount) {
    548 #ifdef DEBUG
    549  MOZ_ASSERT(aCount > 0, "empty font family list?");
    550  const char* a = aList[0];
    551  uint32_t aLen = strlen(a);
    552  for (size_t i = 1; i < aCount; ++i) {
    553    const char* b = aList[i];
    554    uint32_t bLen = strlen(b);
    555    if (nsCaseInsensitiveUTF8StringComparator(a, b, aLen, bLen) >= 0) {
    556      MOZ_CRASH_UNSAFE_PRINTF("incorrectly sorted font family list: %s >= %s",
    557                              a, b);
    558    }
    559    a = b;
    560    aLen = bLen;
    561  }
    562 #endif
    563 }
    564 
    565 bool gfxPlatformFontList::AddWithLegacyFamilyName(const nsACString& aLegacyName,
    566                                                  gfxFontEntry* aFontEntry,
    567                                                  FontVisibility aVisibility) {
    568  mLock.AssertCurrentThreadIn();
    569  bool added = false;
    570  nsAutoCString key;
    571  ToLowerCase(aLegacyName, key);
    572  mOtherFamilyNames
    573      .LookupOrInsertWith(key,
    574                          [&] {
    575                            RefPtr<gfxFontFamily> family =
    576                                CreateFontFamily(aLegacyName, aVisibility);
    577                            // We don't want the family to search for faces,
    578                            // we're adding them directly here.
    579                            family->SetHasStyles(true);
    580                            // And we don't want it to attempt to search for
    581                            // legacy names, because we've already done that
    582                            // (and this is the result).
    583                            family->SetCheckedForLegacyFamilyNames(true);
    584                            added = true;
    585                            return family;
    586                          })
    587      ->AddFontEntry(aFontEntry->Clone());
    588  return added;
    589 }
    590 
    591 bool gfxPlatformFontList::InitFontList() {
    592  // If the startup font-list-init thread is still running, we need to wait
    593  // for it to finish before trying to reinitialize here.
    594  if (sInitFontListThread && !IsInitFontListThread()) {
    595    PR_JoinThread(sInitFontListThread);
    596    sInitFontListThread = nullptr;
    597  }
    598 
    599  AutoLock lock(mLock);
    600 
    601  if (LOG_FONTINIT_ENABLED()) {
    602    LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
    603  }
    604 
    605  if (IsInitialized()) {
    606    // Font-list reinitialization always occurs on the main thread, in response
    607    // to a change notification; it's only the initial creation during startup
    608    // that may be on another thread.
    609    MOZ_ASSERT(NS_IsMainThread());
    610 
    611    // Rebuilding fontlist so clear out font/word caches.
    612    gfxFontCache* fontCache = gfxFontCache::GetCache();
    613    if (fontCache) {
    614      fontCache->FlushShapedWordCaches();
    615      fontCache->Flush();
    616    }
    617 
    618    gfxPlatform::PurgeSkiaFontCache();
    619 
    620    // There's no need to broadcast this reflow request to child processes, as
    621    // ContentParent::NotifyUpdatedFonts deals with it by re-entering into this
    622    // function on child processes.
    623    gfxPlatform::GlobalReflowFlags flags =
    624        gfxPlatform::GlobalReflowFlags::NeedsReframe |
    625        gfxPlatform::GlobalReflowFlags::FontsChanged;
    626    ForceGlobalReflowLocked(flags);
    627 
    628    mAliasTable.Clear();
    629    mLocalNameTable.Clear();
    630    mIconFontsSet.Clear();
    631 
    632    CancelLoadCmapsTask();
    633    mStartedLoadingCmapsFrom = 0xffffffffu;
    634 
    635    CancelInitOtherFamilyNamesTask();
    636    mFontFamilies.Clear();
    637    mOtherFamilyNames.Clear();
    638    mOtherFamilyNamesInitialized = false;
    639 
    640    if (mExtraNames) {
    641      mExtraNames->mFullnames.Clear();
    642      mExtraNames->mPostscriptNames.Clear();
    643    }
    644    mFaceNameListsInitialized = false;
    645    ClearLangGroupPrefFontsLocked();
    646    CancelLoader();
    647 
    648    // Clear cached family records that will no longer be valid.
    649    for (auto& f : mReplacementCharFallbackFamily) {
    650      f = FontFamily();
    651    }
    652 
    653    gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
    654    mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
    655 
    656    LoadIconFontOverrideList();
    657  }
    658 
    659  // From here, gfxPlatformFontList::IsInitialized will return true,
    660  // unless InitFontListForPlatform() fails and we reset it below.
    661  mFontlistInitCount++;
    662 
    663  InitializeCodepointsWithNoFonts();
    664 
    665  // Try to initialize the cross-process shared font list if enabled by prefs.
    666  if (StaticPrefs::gfx_e10s_font_list_shared_AtStartup()) {
    667    for (const auto& entry : mFontEntries.Values()) {
    668      if (!entry) {
    669        continue;
    670      }
    671      AutoWriteLock lock(entry->mLock);
    672      entry->mShmemCharacterMap = nullptr;
    673      entry->mShmemFace = nullptr;
    674      entry->mFamilyName.Truncate();
    675    }
    676    mFontEntries.Clear();
    677    mShmemCharMaps.Clear();
    678    bool oldSharedList = mSharedFontList != nullptr;
    679    mSharedFontList.reset(new fontlist::FontList(mFontlistInitCount));
    680    InitSharedFontListForPlatform();
    681    if (mSharedFontList && mSharedFontList->Initialized()) {
    682      if (mLocalNameTable.Count()) {
    683        SharedFontList()->SetLocalNames(mLocalNameTable);
    684        mLocalNameTable.Clear();
    685      }
    686    } else {
    687      // something went wrong, fall back to in-process list
    688      gfxCriticalNote << "Failed to initialize shared font list, "
    689                         "falling back to in-process list.";
    690      mSharedFontList.reset(nullptr);
    691    }
    692    if (oldSharedList && XRE_IsParentProcess()) {
    693      // notify all children of the change
    694      if (NS_IsMainThread()) {
    695        dom::ContentParent::NotifyUpdatedFonts(true);
    696      } else {
    697        NS_DispatchToMainThread(NS_NewRunnableFunction(
    698            "NotifyUpdatedFonts callback",
    699            [] { dom::ContentParent::NotifyUpdatedFonts(true); }));
    700      }
    701    }
    702  }
    703 
    704  if (!SharedFontList()) {
    705    if (NS_FAILED(InitFontListForPlatform())) {
    706      mFontlistInitCount = 0;
    707      return false;
    708    }
    709    ApplyWhitelist();
    710  }
    711 
    712  // Set up mDefaultFontEntry as a "last resort" default that we can use
    713  // to avoid crashing if the font list is otherwise unusable.
    714  gfxFontStyle defStyle;
    715  FontFamily fam = GetDefaultFontLocked(nullptr, &defStyle);
    716  gfxFontEntry* fe;
    717  if (fam.mShared) {
    718    auto face = fam.mShared->FindFaceForStyle(SharedFontList(), defStyle);
    719    fe = face ? GetOrCreateFontEntryLocked(face, fam.mShared) : nullptr;
    720  } else {
    721    fe = fam.mUnshared->FindFontForStyle(defStyle);
    722  }
    723  mDefaultFontEntry = fe;
    724 
    725  if (XRE_IsParentProcess() && NS_IsMainThread()) {
    726    if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) {
    727      obsvc->NotifyObservers(nullptr, "font-list-initialized", nullptr);
    728    }
    729  }
    730 
    731  return true;
    732 }
    733 
    734 void gfxPlatformFontList::LoadIconFontOverrideList() {
    735  mIconFontsSet.Clear();
    736  AutoTArray<nsCString, 20> iconFontsList;
    737  gfxFontUtils::GetPrefsFontList(kIconFontsPref, iconFontsList);
    738  for (auto& name : iconFontsList) {
    739    ToLowerCase(name);
    740    mIconFontsSet.Insert(name);
    741  }
    742 }
    743 
    744 void gfxPlatformFontList::InitializeCodepointsWithNoFonts() {
    745  auto& first = mCodepointsWithNoFonts[FontVisibility(0)];
    746  for (auto& bitset : mCodepointsWithNoFonts) {
    747    if (&bitset == &first) {
    748      bitset.reset();
    749      bitset.SetRange(0, 0x1f);            // C0 controls
    750      bitset.SetRange(0x7f, 0x9f);         // C1 controls
    751      bitset.SetRange(0xE000, 0xF8FF);     // PUA
    752      bitset.SetRange(0xF0000, 0x10FFFD);  // Supplementary PUA
    753      bitset.SetRange(0xfdd0, 0xfdef);     // noncharacters
    754      for (unsigned i = 0; i <= 0x100000; i += 0x10000) {
    755        bitset.SetRange(i + 0xfffe, i + 0xffff);  // noncharacters
    756      }
    757      bitset.Compact();
    758    } else {
    759      bitset = first;
    760    }
    761  }
    762 }
    763 
    764 void gfxPlatformFontList::GenerateFontListKey(const nsACString& aKeyName,
    765                                              nsACString& aResult) {
    766  aResult = aKeyName;
    767  ToLowerCase(aResult);
    768 }
    769 
    770 void gfxPlatformFontList::GenerateFontListKey(nsACString& aKeyName) {
    771  ToLowerCase(aKeyName);
    772 }
    773 
    774 // Used if a stylo thread wants to trigger InitOtherFamilyNames in the main
    775 // process: we can't do IPC from the stylo thread so we post this to the main
    776 // thread instead.
    777 class InitOtherFamilyNamesForStylo : public mozilla::Runnable {
    778 public:
    779  explicit InitOtherFamilyNamesForStylo(bool aDeferOtherFamilyNamesLoading)
    780      : Runnable("gfxPlatformFontList::InitOtherFamilyNamesForStylo"),
    781        mDefer(aDeferOtherFamilyNamesLoading) {}
    782 
    783  NS_IMETHOD Run() override {
    784    auto pfl = gfxPlatformFontList::PlatformFontList();
    785    auto list = pfl->SharedFontList();
    786    if (!list) {
    787      return NS_OK;
    788    }
    789    bool initialized = false;
    790    dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
    791        list->GetGeneration(), mDefer, &initialized);
    792    pfl->mOtherFamilyNamesInitialized.compareExchange(false, initialized);
    793    return NS_OK;
    794  }
    795 
    796 private:
    797  bool mDefer;
    798 };
    799 
    800 #define OTHERNAMES_TIMEOUT 200
    801 
    802 bool gfxPlatformFontList::InitOtherFamilyNames(
    803    bool aDeferOtherFamilyNamesLoading) {
    804  if (mOtherFamilyNamesInitialized) {
    805    return true;
    806  }
    807 
    808  if (SharedFontList() && !XRE_IsParentProcess()) {
    809    if (NS_IsMainThread()) {
    810      bool initialized;
    811      dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
    812          SharedFontList()->GetGeneration(), aDeferOtherFamilyNamesLoading,
    813          &initialized);
    814      mOtherFamilyNamesInitialized.compareExchange(false, initialized);
    815    } else {
    816      NS_DispatchToMainThread(
    817          new InitOtherFamilyNamesForStylo(aDeferOtherFamilyNamesLoading));
    818    }
    819    return mOtherFamilyNamesInitialized;
    820  }
    821 
    822  // If the font loader delay has been set to zero, we don't defer loading
    823  // additional family names (regardless of the aDefer... parameter), as we
    824  // take this to mean availability of font info is to be prioritized over
    825  // potential startup perf or main-thread jank.
    826  // (This is used so we can reliably run reftests that depend on localized
    827  // font-family names being available.)
    828  if (aDeferOtherFamilyNamesLoading &&
    829      StaticPrefs::gfx_font_loader_delay() > 0) {
    830    if (!mPendingOtherFamilyNameTask) {
    831      RefPtr<mozilla::CancelableRunnable> task =
    832          new InitOtherFamilyNamesRunnable();
    833      mPendingOtherFamilyNameTask = task;
    834      NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
    835    }
    836  } else {
    837    InitOtherFamilyNamesInternal(false);
    838  }
    839  return mOtherFamilyNamesInitialized;
    840 }
    841 
    842 // time limit for loading facename lists (ms)
    843 #define NAMELIST_TIMEOUT 200
    844 
    845 gfxFontEntry* gfxPlatformFontList::SearchFamiliesForFaceName(
    846    const nsACString& aFaceName) {
    847  TimeStamp start = TimeStamp::Now();
    848  bool timedOut = false;
    849  // if mFirstChar is not 0, only load facenames for families
    850  // that start with this character
    851  char16_t firstChar = 0;
    852  gfxFontEntry* lookup = nullptr;
    853 
    854  // iterate over familes starting with the same letter
    855  firstChar = ToLowerCase(aFaceName.CharAt(0));
    856 
    857  for (const auto& entry : mFontFamilies) {
    858    nsCStringHashKey::KeyType key = entry.GetKey();
    859    const RefPtr<gfxFontFamily>& family = entry.GetData();
    860 
    861    // when filtering, skip names that don't start with the filter character
    862    if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
    863      continue;
    864    }
    865 
    866    family->ReadFaceNames(this, NeedFullnamePostscriptNames());
    867 
    868    TimeDuration elapsed = TimeStamp::Now() - start;
    869    if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
    870      timedOut = true;
    871      break;
    872    }
    873  }
    874 
    875  lookup = FindFaceName(aFaceName);
    876 
    877  TimeDuration elapsed = TimeStamp::Now() - start;
    878  glean::fontlist::initfacenamelists.AccumulateRawDuration(elapsed);
    879  if (LOG_FONTINIT_ENABLED()) {
    880    LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
    881                  elapsed.ToMilliseconds(), (lookup ? "found name" : ""),
    882                  (timedOut ? "timeout" : "")));
    883  }
    884 
    885  return lookup;
    886 }
    887 
    888 gfxFontEntry* gfxPlatformFontList::FindFaceName(const nsACString& aFaceName) {
    889  gfxFontEntry* lookup;
    890 
    891  // lookup in name lookup tables, return null if not found
    892  if (mExtraNames &&
    893      ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
    894       (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
    895    return lookup;
    896  }
    897 
    898  return nullptr;
    899 }
    900 
    901 gfxFontEntry* gfxPlatformFontList::LookupInFaceNameLists(
    902    const nsACString& aFaceName) {
    903  gfxFontEntry* lookup = nullptr;
    904 
    905  // initialize facename lookup tables if needed
    906  // note: this can terminate early or time out, in which case
    907  //       mFaceNameListsInitialized remains false
    908  if (!mFaceNameListsInitialized) {
    909    lookup = SearchFamiliesForFaceName(aFaceName);
    910    if (lookup) {
    911      return lookup;
    912    }
    913  }
    914 
    915  // lookup in name lookup tables, return null if not found
    916  if (!(lookup = FindFaceName(aFaceName))) {
    917    // names not completely initialized, so keep track of lookup misses
    918    if (!mFaceNameListsInitialized) {
    919      if (!mFaceNamesMissed) {
    920        mFaceNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
    921      }
    922      mFaceNamesMissed->Insert(aFaceName);
    923    }
    924  }
    925 
    926  return lookup;
    927 }
    928 
    929 gfxFontEntry* gfxPlatformFontList::LookupInSharedFaceNameList(
    930    FontVisibilityProvider* aFontVisibilityProvider,
    931    const nsACString& aFaceName, WeightRange aWeightForEntry,
    932    StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
    933  nsAutoCString keyName(aFaceName);
    934  ToLowerCase(keyName);
    935  fontlist::FontList* list = SharedFontList();
    936  fontlist::Family* family = nullptr;
    937  fontlist::Face* face = nullptr;
    938  if (list->NumLocalFaces()) {
    939    fontlist::LocalFaceRec* rec = list->FindLocalFace(keyName);
    940    if (rec) {
    941      auto* families = list->Families();
    942      if (families) {
    943        family = &families[rec->mFamilyIndex];
    944        face = family->Faces(list)[rec->mFaceIndex].ToPtr<fontlist::Face>(list);
    945      }
    946    }
    947  } else {
    948    list->SearchForLocalFace(keyName, &family, &face);
    949  }
    950  if (!face || !family) {
    951    return nullptr;
    952  }
    953  FontVisibility level = aFontVisibilityProvider
    954                             ? aFontVisibilityProvider->GetFontVisibility()
    955                             : FontVisibility::User;
    956  if (!IsVisibleToCSS(*family, level)) {
    957    if (aFontVisibilityProvider) {
    958      aFontVisibilityProvider->ReportBlockedFontFamily(*family);
    959    }
    960    return nullptr;
    961  }
    962  gfxFontEntry* fe = CreateFontEntry(face, family);
    963  if (fe) {
    964    fe->mIsLocalUserFont = true;
    965    fe->mWeightRange = aWeightForEntry;
    966    fe->mStretchRange = aStretchForEntry;
    967    fe->mStyleRange = aStyleForEntry;
    968  }
    969  return fe;
    970 }
    971 
    972 void gfxPlatformFontList::MaybeAddToLocalNameTable(
    973    const nsACString& aName, const fontlist::LocalFaceRec::InitData& aData) {
    974  // Compute a measure of the similarity between aName (which will be a PSName
    975  // or FullName) and aReference (a font family name).
    976  auto nameSimilarity = [](const nsACString& aName,
    977                           const nsACString& aReference) -> uint32_t {
    978    uint32_t nameIdx = 0, refIdx = 0, matchCount = 0;
    979    while (nameIdx < aName.Length() && refIdx < aReference.Length()) {
    980      // Ignore non-alphanumerics in the ASCII range, so that a PSname like
    981      // "TimesNewRomanPSMT" is a good match for family "Times New Roman".
    982      while (nameIdx < aName.Length() && IsAscii(aName[nameIdx]) &&
    983             !IsAsciiAlphanumeric(aName[nameIdx])) {
    984        ++nameIdx;
    985      }
    986      while (refIdx < aReference.Length() && IsAscii(aReference[refIdx]) &&
    987             !IsAsciiAlphanumeric(aReference[refIdx])) {
    988        ++refIdx;
    989      }
    990      if (nameIdx == aName.Length() || refIdx == aReference.Length() ||
    991          aName[nameIdx] != aReference[refIdx]) {
    992        break;
    993      }
    994      ++nameIdx;
    995      ++refIdx;
    996      ++matchCount;
    997    }
    998    return matchCount;
    999  };
   1000 
   1001  mLocalNameTable.WithEntryHandle(aName, [&](auto entry) -> void {
   1002    if (entry) {
   1003      if (nameSimilarity(aName, aData.mFamilyName) >
   1004          nameSimilarity(aName, entry.Data().mFamilyName)) {
   1005        entry.Update(aData);
   1006      }
   1007    } else {
   1008      entry.OrInsert(aData);
   1009    }
   1010  });
   1011 }
   1012 
   1013 void gfxPlatformFontList::LoadBadUnderlineList() {
   1014  gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset",
   1015                                 mBadUnderlineFamilyNames);
   1016  for (auto& fam : mBadUnderlineFamilyNames) {
   1017    ToLowerCase(fam);
   1018  }
   1019  mBadUnderlineFamilyNames.Compact();
   1020  mBadUnderlineFamilyNames.Sort();
   1021 }
   1022 
   1023 void gfxPlatformFontList::UpdateFontList(bool aFullRebuild) {
   1024  MOZ_ASSERT(NS_IsMainThread());
   1025  if (aFullRebuild) {
   1026    InitFontList();
   1027    AutoLock lock(mLock);
   1028    RebuildLocalFonts();
   1029  } else {
   1030    // The font list isn't being fully rebuilt, we're just being notified that
   1031    // character maps have been updated and so font fallback needs to be re-
   1032    // done. We only care about this if we have previously encountered a
   1033    // fallback that required cmaps that were not yet available, and so we
   1034    // asked for the async cmap loader to run.
   1035    AutoLock lock(mLock);
   1036    if (mStartedLoadingCmapsFrom != 0xffffffffu) {
   1037      InitializeCodepointsWithNoFonts();
   1038      mStartedLoadingCmapsFrom = 0xffffffffu;
   1039      gfxPlatform::GlobalReflowFlags flags =
   1040          gfxPlatform::GlobalReflowFlags::FontsChanged |
   1041          gfxPlatform::GlobalReflowFlags::BroadcastToChildren;
   1042      ForceGlobalReflowLocked(flags);
   1043    }
   1044  }
   1045 }
   1046 
   1047 bool gfxPlatformFontList::IsVisibleToCSS(const gfxFontFamily& aFamily,
   1048                                         FontVisibility aVisibility) const {
   1049  return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
   1050 }
   1051 
   1052 bool gfxPlatformFontList::IsVisibleToCSS(const fontlist::Family& aFamily,
   1053                                         FontVisibility aVisibility) const {
   1054  return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
   1055 }
   1056 
   1057 void gfxPlatformFontList::GetFontList(nsAtom* aLangGroup,
   1058                                      const nsACString& aGenericFamily,
   1059                                      nsTArray<nsString>& aListOfFonts) {
   1060  AutoLock lock(mLock);
   1061 
   1062  if (SharedFontList()) {
   1063    fontlist::FontList* list = SharedFontList();
   1064    const fontlist::Family* families = list->Families();
   1065    if (families) {
   1066      for (uint32_t i = 0; i < list->NumFamilies(); i++) {
   1067        auto& f = families[i];
   1068        if (!IsVisibleToCSS(f, FontVisibility::User) || f.IsAltLocaleFamily()) {
   1069          continue;
   1070        }
   1071        // XXX TODO: filter families for aGenericFamily, if supported by
   1072        // platform
   1073        aListOfFonts.AppendElement(
   1074            NS_ConvertUTF8toUTF16(list->LocalizedFamilyName(&f)));
   1075      }
   1076    }
   1077    return;
   1078  }
   1079 
   1080  for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
   1081    if (!IsVisibleToCSS(*family, FontVisibility::User)) {
   1082      continue;
   1083    }
   1084    if (family->FilterForFontList(aLangGroup, aGenericFamily)) {
   1085      nsAutoCString localizedFamilyName;
   1086      family->LocalizedName(localizedFamilyName);
   1087      aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(localizedFamilyName));
   1088    }
   1089  }
   1090 
   1091  aListOfFonts.Sort();
   1092  aListOfFonts.Compact();
   1093 }
   1094 
   1095 void gfxPlatformFontList::GetFontFamilyList(
   1096    nsTArray<RefPtr<gfxFontFamily>>& aFamilyArray) {
   1097  AutoLock lock(mLock);
   1098  MOZ_ASSERT(aFamilyArray.IsEmpty());
   1099  // This doesn't use ToArray, because the caller passes an AutoTArray.
   1100  aFamilyArray.SetCapacity(mFontFamilies.Count());
   1101  for (const auto& family : mFontFamilies.Values()) {
   1102    aFamilyArray.AppendElement(family);
   1103  }
   1104 }
   1105 
   1106 already_AddRefed<gfxFont> gfxPlatformFontList::SystemFindFontForChar(
   1107    FontVisibilityProvider* aFontVisibilityProvider, uint32_t aCh,
   1108    uint32_t aNextCh, Script aRunScript, FontPresentation aPresentation,
   1109    const gfxFontStyle* aStyle, FontVisibility* aVisibility) {
   1110  AutoLock lock(mLock);
   1111  FontVisibility level = aFontVisibilityProvider
   1112                             ? aFontVisibilityProvider->GetFontVisibility()
   1113                             : FontVisibility::User;
   1114  MOZ_ASSERT(!mCodepointsWithNoFonts[level].test(aCh),
   1115             "don't call for codepoints already known to be unsupported");
   1116 
   1117  // Try to short-circuit font fallback for U+FFFD, used to represent
   1118  // encoding errors: just use cached family from last time U+FFFD was seen.
   1119  // This helps speed up pages with lots of encoding errors, binary-as-text,
   1120  // etc.
   1121  if (aCh == 0xFFFD) {
   1122    gfxFontEntry* fontEntry = nullptr;
   1123    auto& fallbackFamily = mReplacementCharFallbackFamily[level];
   1124    if (fallbackFamily.mShared) {
   1125      fontlist::Face* face =
   1126          fallbackFamily.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
   1127      if (face) {
   1128        fontEntry = GetOrCreateFontEntryLocked(face, fallbackFamily.mShared);
   1129        *aVisibility = fallbackFamily.mShared->Visibility();
   1130      }
   1131    } else if (fallbackFamily.mUnshared) {
   1132      fontEntry = fallbackFamily.mUnshared->FindFontForStyle(*aStyle);
   1133      *aVisibility = fallbackFamily.mUnshared->Visibility();
   1134    }
   1135 
   1136    // this should never fail, as we must have found U+FFFD in order to set
   1137    // mReplacementCharFallbackFamily[...] at all, but better play it safe
   1138    if (fontEntry && fontEntry->HasCharacter(aCh)) {
   1139      return fontEntry->FindOrMakeFont(aStyle);
   1140    }
   1141  }
   1142 
   1143  TimeStamp start = TimeStamp::Now();
   1144 
   1145  // search commonly available fonts
   1146  bool common = true;
   1147  FontFamily fallbackFamily;
   1148  RefPtr<gfxFont> candidate =
   1149      CommonFontFallback(aFontVisibilityProvider, aCh, aNextCh, aRunScript,
   1150                         aPresentation, aStyle, fallbackFamily);
   1151  RefPtr<gfxFont> font;
   1152  if (candidate) {
   1153    if (aPresentation == FontPresentation::Any) {
   1154      font = std::move(candidate);
   1155    } else {
   1156      bool hasColorGlyph = candidate->HasColorGlyphFor(aCh, aNextCh);
   1157      if (hasColorGlyph == PrefersColor(aPresentation)) {
   1158        font = std::move(candidate);
   1159      }
   1160    }
   1161  }
   1162 
   1163  // If we didn't find a common font, or it was not the preferred type (color
   1164  // or monochrome), do system-wide fallback (except for specials).
   1165  uint32_t cmapCount = 0;
   1166  if (!font) {
   1167    common = false;
   1168    font = GlobalFontFallback(aFontVisibilityProvider, aCh, aNextCh, aRunScript,
   1169                              aPresentation, aStyle, cmapCount, fallbackFamily);
   1170    // If the font we found doesn't match the requested type, and we also found
   1171    // a candidate above, prefer that one.
   1172    if (font && aPresentation != FontPresentation::Any && candidate) {
   1173      bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
   1174      if (hasColorGlyph != PrefersColor(aPresentation)) {
   1175        font = std::move(candidate);
   1176      }
   1177    }
   1178  }
   1179  TimeDuration elapsed = TimeStamp::Now() - start;
   1180 
   1181  LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
   1182 
   1183  if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
   1184    Script script = intl::UnicodeProperties::GetScriptCode(aCh);
   1185    MOZ_LOG(log, LogLevel::Warning,
   1186            ("(textrun-systemfallback-%s) char: u+%6.6x "
   1187             "script: %d match: [%s]"
   1188             " time: %dus cmaps: %d\n",
   1189             (common ? "common" : "global"), aCh, static_cast<int>(script),
   1190             (font ? font->GetFontEntry()->Name().get() : "<none>"),
   1191             int32_t(elapsed.ToMicroseconds()), cmapCount));
   1192  }
   1193 
   1194  // no match? add to set of non-matching codepoints
   1195  if (!font) {
   1196    mCodepointsWithNoFonts[level].set(aCh);
   1197  } else {
   1198    *aVisibility = fallbackFamily.mShared
   1199                       ? fallbackFamily.mShared->Visibility()
   1200                       : fallbackFamily.mUnshared->Visibility();
   1201    if (aCh == 0xFFFD) {
   1202      mReplacementCharFallbackFamily[level] = fallbackFamily;
   1203    }
   1204  }
   1205 
   1206  // track system fallback time
   1207  static bool first = true;
   1208  if (first)
   1209    glean::fontlist::system_font_fallback_first.AccumulateRawDuration(elapsed);
   1210  else
   1211    glean::fontlist::system_font_fallback.AccumulateRawDuration(elapsed);
   1212  first = false;
   1213 
   1214  return font.forget();
   1215 }
   1216 
   1217 #define NUM_FALLBACK_FONTS 8
   1218 
   1219 already_AddRefed<gfxFont> gfxPlatformFontList::CommonFontFallback(
   1220    FontVisibilityProvider* aFontVisibilityProvider, uint32_t aCh,
   1221    uint32_t aNextCh, Script aRunScript, FontPresentation aPresentation,
   1222    const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
   1223  AutoTArray<const char*, NUM_FALLBACK_FONTS> defaultFallbacks;
   1224  gfxPlatform::GetPlatform()->GetCommonFallbackFonts(
   1225      aCh, aRunScript, aPresentation, defaultFallbacks);
   1226  GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
   1227  FontVisibility level = aFontVisibilityProvider
   1228                             ? aFontVisibilityProvider->GetFontVisibility()
   1229                             : FontVisibility::User;
   1230 
   1231  // If a color-emoji presentation is requested, we will check any font found
   1232  // to see if it can provide this; if not, we'll remember it as a possible
   1233  // candidate but search the remainder of the list for a better choice.
   1234  RefPtr<gfxFont> candidateFont;
   1235  FontFamily candidateFamily;
   1236  auto check = [&](gfxFontEntry* aFontEntry,
   1237                   FontFamily aFamily) -> already_AddRefed<gfxFont> {
   1238    RefPtr<gfxFont> font = aFontEntry->FindOrMakeFont(aMatchStyle);
   1239    if (aPresentation < FontPresentation::EmojiDefault ||
   1240        font->HasColorGlyphFor(aCh, aNextCh)) {
   1241      aMatchedFamily = aFamily;
   1242      return font.forget();
   1243    }
   1244    // We want a color glyph but this font only has monochrome; remember it
   1245    // (unless we already have a candidate) but continue to search.
   1246    if (!candidateFont) {
   1247      candidateFont = std::move(font);
   1248      candidateFamily = aFamily;
   1249    }
   1250    return nullptr;
   1251  };
   1252 
   1253  if (SharedFontList()) {
   1254    for (const auto name : defaultFallbacks) {
   1255      fontlist::Family* family =
   1256          FindSharedFamily(aFontVisibilityProvider, nsDependentCString(name));
   1257      if (!family || !IsVisibleToCSS(*family, level)) {
   1258        continue;
   1259      }
   1260      // XXX(jfkthame) Should we fire the async cmap-loader here, or let it
   1261      // always do a potential sync initialization of the family?
   1262      family->SearchAllFontsForChar(SharedFontList(), &data);
   1263      if (data.mBestMatch) {
   1264        RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(family));
   1265        if (font) {
   1266          return font.forget();
   1267        }
   1268      }
   1269    }
   1270  } else {
   1271    for (const auto name : defaultFallbacks) {
   1272      gfxFontFamily* fallback =
   1273          FindFamilyByCanonicalName(nsDependentCString(name));
   1274      if (!fallback || !IsVisibleToCSS(*fallback, level)) {
   1275        continue;
   1276      }
   1277      fallback->FindFontForChar(&data);
   1278      if (data.mBestMatch) {
   1279        RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(fallback));
   1280        if (font) {
   1281          return font.forget();
   1282        }
   1283      }
   1284    }
   1285  }
   1286 
   1287  // If we had a candidate that supports the character, but doesn't have the
   1288  // desired emoji-style glyph, we'll return it anyhow as nothing better was
   1289  // found.
   1290  if (candidateFont) {
   1291    aMatchedFamily = candidateFamily;
   1292    return candidateFont.forget();
   1293  }
   1294 
   1295  return nullptr;
   1296 }
   1297 
   1298 already_AddRefed<gfxFont> gfxPlatformFontList::GlobalFontFallback(
   1299    FontVisibilityProvider* aFontVisibilityProvider, uint32_t aCh,
   1300    uint32_t aNextCh, Script aRunScript, FontPresentation aPresentation,
   1301    const gfxFontStyle* aMatchStyle, uint32_t& aCmapCount,
   1302    FontFamily& aMatchedFamily) {
   1303  bool useCmaps = IsFontFamilyWhitelistActive() ||
   1304                  gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
   1305  FontVisibility level = aFontVisibilityProvider
   1306                             ? aFontVisibilityProvider->GetFontVisibility()
   1307                             : FontVisibility::User;
   1308  if (!useCmaps) {
   1309    // Allow platform-specific fallback code to try and find a usable font
   1310    gfxFontEntry* fe = PlatformGlobalFontFallback(
   1311        aFontVisibilityProvider, aCh, aRunScript, aMatchStyle, aMatchedFamily);
   1312    if (fe) {
   1313      if (aMatchedFamily.mShared) {
   1314        if (IsVisibleToCSS(*aMatchedFamily.mShared, level)) {
   1315          RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle);
   1316          if (font) {
   1317            if (aPresentation == FontPresentation::Any) {
   1318              return font.forget();
   1319            }
   1320            bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
   1321            if (hasColorGlyph == PrefersColor(aPresentation)) {
   1322              return font.forget();
   1323            }
   1324          }
   1325        }
   1326      } else {
   1327        if (IsVisibleToCSS(*aMatchedFamily.mUnshared, level)) {
   1328          RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle);
   1329          if (font) {
   1330            if (aPresentation == FontPresentation::Any) {
   1331              return font.forget();
   1332            }
   1333            bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
   1334            if (hasColorGlyph == PrefersColor(aPresentation)) {
   1335              return font.forget();
   1336            }
   1337          }
   1338        }
   1339      }
   1340    }
   1341  }
   1342 
   1343  // otherwise, try to find it among local fonts
   1344  GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
   1345  if (SharedFontList()) {
   1346    fontlist::Family* families = SharedFontList()->Families();
   1347    if (families) {
   1348      for (uint32_t i = 0; i < SharedFontList()->NumFamilies(); i++) {
   1349        fontlist::Family& family = families[i];
   1350        if (!IsVisibleToCSS(family, level)) {
   1351          continue;
   1352        }
   1353        if (!family.IsFullyInitialized() &&
   1354            StaticPrefs::gfx_font_rendering_fallback_async() &&
   1355            !XRE_IsParentProcess()) {
   1356          // Start loading all the missing charmaps; but this is async,
   1357          // so for now we just continue, ignoring this family.
   1358          StartCmapLoadingFromFamily(i);
   1359        } else {
   1360          family.SearchAllFontsForChar(SharedFontList(), &data);
   1361          if (data.mMatchDistance == 0.0) {
   1362            // no better style match is possible, so stop searching
   1363            break;
   1364          }
   1365        }
   1366      }
   1367      if (data.mBestMatch) {
   1368        aMatchedFamily = FontFamily(data.mMatchedSharedFamily);
   1369        return data.mBestMatch->FindOrMakeFont(aMatchStyle);
   1370      }
   1371    }
   1372  } else {
   1373    // iterate over all font families to find a font that support the
   1374    // character
   1375    for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
   1376      if (!IsVisibleToCSS(*family, level)) {
   1377        continue;
   1378      }
   1379      // evaluate all fonts in this family for a match
   1380      family->FindFontForChar(&data);
   1381      if (data.mMatchDistance == 0.0) {
   1382        // no better style match is possible, so stop searching
   1383        break;
   1384      }
   1385    }
   1386 
   1387    aCmapCount = data.mCmapsTested;
   1388    if (data.mBestMatch) {
   1389      aMatchedFamily = FontFamily(data.mMatchedFamily);
   1390      return data.mBestMatch->FindOrMakeFont(aMatchStyle);
   1391    }
   1392  }
   1393 
   1394  return nullptr;
   1395 }
   1396 
   1397 class StartCmapLoadingRunnable : public mozilla::Runnable {
   1398 public:
   1399  explicit StartCmapLoadingRunnable(uint32_t aStartIndex)
   1400      : Runnable("gfxPlatformFontList::StartCmapLoadingRunnable"),
   1401        mStartIndex(aStartIndex) {}
   1402 
   1403  NS_IMETHOD Run() override {
   1404    auto* pfl = gfxPlatformFontList::PlatformFontList();
   1405    auto* list = pfl->SharedFontList();
   1406    if (!list) {
   1407      return NS_OK;
   1408    }
   1409    if (mStartIndex >= list->NumFamilies()) {
   1410      return NS_OK;
   1411    }
   1412    if (XRE_IsParentProcess()) {
   1413      pfl->StartCmapLoading(list->GetGeneration(), mStartIndex);
   1414    } else {
   1415      dom::ContentChild::GetSingleton()->SendStartCmapLoading(
   1416          list->GetGeneration(), mStartIndex);
   1417    }
   1418    return NS_OK;
   1419  }
   1420 
   1421 private:
   1422  uint32_t mStartIndex;
   1423 };
   1424 
   1425 void gfxPlatformFontList::StartCmapLoadingFromFamily(uint32_t aStartIndex) {
   1426  AutoLock lock(mLock);
   1427  if (aStartIndex >= mStartedLoadingCmapsFrom) {
   1428    // We already initiated cmap-loading from here or earlier in the list;
   1429    // no need to do it again here.
   1430    return;
   1431  }
   1432  mStartedLoadingCmapsFrom = aStartIndex;
   1433 
   1434  // If we're already on the main thread, don't bother dispatching a runnable
   1435  // here to kick off the loading process, just do it directly.
   1436  if (NS_IsMainThread()) {
   1437    auto* list = SharedFontList();
   1438    if (XRE_IsParentProcess()) {
   1439      StartCmapLoading(list->GetGeneration(), aStartIndex);
   1440    } else {
   1441      dom::ContentChild::GetSingleton()->SendStartCmapLoading(
   1442          list->GetGeneration(), aStartIndex);
   1443    }
   1444  } else {
   1445    NS_DispatchToMainThread(new StartCmapLoadingRunnable(aStartIndex));
   1446  }
   1447 }
   1448 
   1449 class LoadCmapsRunnable final : public IdleRunnable,
   1450                                public nsIObserver,
   1451                                public nsSupportsWeakReference {
   1452  NS_DECL_ISUPPORTS_INHERITED
   1453  NS_DECL_NSIOBSERVER
   1454 
   1455 private:
   1456  virtual ~LoadCmapsRunnable() {
   1457    if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
   1458      obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
   1459    }
   1460  }
   1461 
   1462 public:
   1463  LoadCmapsRunnable(uint32_t aGeneration, uint32_t aFamilyIndex)
   1464      : IdleRunnable("gfxPlatformFontList::LoadCmapsRunnable"),
   1465        mGeneration(aGeneration),
   1466        mStartIndex(aFamilyIndex),
   1467        mIndex(aFamilyIndex) {}
   1468 
   1469  void SetDeadline(TimeStamp aDeadline) override { mDeadline = aDeadline; }
   1470 
   1471  // Reset the current family index, if the value passed is earlier than our
   1472  // original starting position. We don't "reset" if it would move the current
   1473  // position forward, or back into the already-scanned range.
   1474  // We could optimize further by remembering the current position reached,
   1475  // and then skipping ahead from the original start, but it doesn't seem worth
   1476  // extra complexity for a task that usually only happens once, and already-
   1477  // processed families will be skipped pretty quickly in Run() anyhow.
   1478  void MaybeResetIndex(uint32_t aFamilyIndex) {
   1479    if (aFamilyIndex < mStartIndex) {
   1480      mStartIndex = aFamilyIndex;
   1481      mIndex = aFamilyIndex;
   1482    }
   1483  }
   1484 
   1485  void Cancel() { mIsCanceled = true; }
   1486 
   1487  NS_IMETHOD Run() override {
   1488    if (mIsCanceled) {
   1489      return NS_OK;
   1490    }
   1491    auto* pfl = gfxPlatformFontList::PlatformFontList();
   1492    auto* list = pfl->SharedFontList();
   1493    MOZ_ASSERT(list);
   1494    if (!list) {
   1495      return NS_OK;
   1496    }
   1497    if (mGeneration != list->GetGeneration()) {
   1498      return NS_OK;
   1499    }
   1500    uint32_t numFamilies = list->NumFamilies();
   1501    if (mIndex >= numFamilies) {
   1502      return NS_OK;
   1503    }
   1504    auto* families = list->Families();
   1505    while (mIndex < numFamilies) {
   1506      auto& family = families[mIndex++];
   1507      if (family.IsFullyInitialized()) {
   1508        // Skip any families that are already initialized.
   1509        continue;
   1510      }
   1511      // Fully initialize this family.
   1512      (void)pfl->InitializeFamily(&family, true);
   1513      // TODO(emilio): It'd make sense to use mDeadline here to determine
   1514      // whether we can do more work, but that is surprisingly a performance
   1515      // regression in practice, see bug 1936489. Investigate if we can be
   1516      // smarter about this.
   1517      break;
   1518    }
   1519    // If there are more families to initialize, post ourselves back to the
   1520    // idle queue handle the next ones; otherwise we're finished and we need
   1521    // to notify content processes to update their rendering.
   1522    if (mIndex < numFamilies) {
   1523      mDeadline = TimeStamp();
   1524      NS_DispatchToMainThreadQueue(do_AddRef(this), EventQueuePriority::Idle);
   1525    } else {
   1526      pfl->Lock();
   1527      pfl->CancelLoadCmapsTask();
   1528      pfl->InitializeCodepointsWithNoFonts();
   1529      dom::ContentParent::NotifyUpdatedFonts(false);
   1530      pfl->Unlock();
   1531    }
   1532    return NS_OK;
   1533  }
   1534 
   1535 private:
   1536  uint32_t mGeneration;
   1537  uint32_t mStartIndex;
   1538  uint32_t mIndex;
   1539  TimeStamp mDeadline;
   1540  bool mIsCanceled = false;
   1541 };
   1542 
   1543 NS_IMPL_ISUPPORTS_INHERITED(LoadCmapsRunnable, IdleRunnable, nsIObserver,
   1544                            nsISupportsWeakReference);
   1545 
   1546 NS_IMETHODIMP
   1547 LoadCmapsRunnable::Observe(nsISupports* aSubject, const char* aTopic,
   1548                           const char16_t* aData) {
   1549  MOZ_ASSERT(!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID),
   1550             "unexpected topic");
   1551  Cancel();
   1552  return NS_OK;
   1553 }
   1554 
   1555 void gfxPlatformFontList::CancelLoadCmapsTask() {
   1556  if (mLoadCmapsRunnable) {
   1557    mLoadCmapsRunnable->Cancel();
   1558    mLoadCmapsRunnable = nullptr;
   1559  }
   1560 }
   1561 
   1562 void gfxPlatformFontList::StartCmapLoading(uint32_t aGeneration,
   1563                                           uint32_t aStartIndex) {
   1564  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
   1565  if (aGeneration != SharedFontList()->GetGeneration()) {
   1566    return;
   1567  }
   1568  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   1569    return;
   1570  }
   1571  if (mLoadCmapsRunnable) {
   1572    // We already have a runnable; just make sure it covers the full range of
   1573    // families needed.
   1574    mLoadCmapsRunnable->MaybeResetIndex(aStartIndex);
   1575    return;
   1576  }
   1577  mLoadCmapsRunnable = new LoadCmapsRunnable(aGeneration, aStartIndex);
   1578  if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
   1579    obs->AddObserver(mLoadCmapsRunnable, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
   1580                     /* ownsWeak = */ true);
   1581  }
   1582  NS_DispatchToMainThreadQueue(do_AddRef(mLoadCmapsRunnable),
   1583                               EventQueuePriority::Idle);
   1584 }
   1585 
   1586 gfxFontFamily* gfxPlatformFontList::CheckFamily(gfxFontFamily* aFamily) {
   1587  if (aFamily && !aFamily->HasStyles()) {
   1588    aFamily->FindStyleVariations();
   1589  }
   1590 
   1591  if (aFamily && aFamily->FontListLength() == 0) {
   1592    // Failed to load any faces for this family, so discard it.
   1593    nsAutoCString key;
   1594    GenerateFontListKey(aFamily->Name(), key);
   1595    mFontFamilies.Remove(key);
   1596    return nullptr;
   1597  }
   1598 
   1599  return aFamily;
   1600 }
   1601 
   1602 bool gfxPlatformFontList::FindAndAddFamilies(
   1603    FontVisibilityProvider* aFontVisibilityProvider,
   1604    StyleGenericFontFamily aGeneric, const nsACString& aFamily,
   1605    nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags,
   1606    gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) {
   1607  AutoLock lock(mLock);
   1608 
   1609 #ifdef DEBUG
   1610  auto initialLength = aOutput->Length();
   1611 #endif
   1612 
   1613  bool didFind = FindAndAddFamiliesLocked(aFontVisibilityProvider, aGeneric,
   1614                                          aFamily, aOutput, aFlags, aStyle,
   1615                                          aLanguage, aDevToCssSize);
   1616 #ifdef DEBUG
   1617  auto finalLength = aOutput->Length();
   1618  // Validate the expectation that the output-array grows if we return true,
   1619  // or remains the same (probably empty) if we return false.
   1620  MOZ_ASSERT_IF(didFind, finalLength > initialLength);
   1621  MOZ_ASSERT_IF(!didFind, finalLength == initialLength);
   1622 #endif
   1623 
   1624  return didFind;
   1625 }
   1626 
   1627 bool gfxPlatformFontList::FindAndAddFamiliesLocked(
   1628    FontVisibilityProvider* aFontVisibilityProvider,
   1629    StyleGenericFontFamily aGeneric, const nsACString& aFamily,
   1630    nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags,
   1631    gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) {
   1632  nsAutoCString key;
   1633  GenerateFontListKey(aFamily, key);
   1634 
   1635  bool allowHidden = bool(aFlags & FindFamiliesFlags::eSearchHiddenFamilies);
   1636  FontVisibility visibilityLevel =
   1637      aFontVisibilityProvider ? aFontVisibilityProvider->GetFontVisibility()
   1638                              : FontVisibility::User;
   1639 
   1640  // If this font lookup is the result of resolving a CSS generic (not a direct
   1641  // font-family request by the page), and RFP settings allow generics to be
   1642  // unrestricted, bump the effective visibility level applied here so as to
   1643  // allow user-installed fonts to be used.
   1644  if (visibilityLevel < FontVisibility::User &&
   1645      aGeneric != StyleGenericFontFamily::None &&
   1646      !aFontVisibilityProvider->ShouldResistFingerprinting(
   1647          RFPTarget::FontVisibilityRestrictGenerics)) {
   1648    visibilityLevel = FontVisibility::User;
   1649  }
   1650 
   1651  if (SharedFontList()) {
   1652    fontlist::Family* family = SharedFontList()->FindFamily(key);
   1653    // If not found, and other family names have not yet been initialized,
   1654    // initialize the rest of the list and try again. This is done lazily
   1655    // since reading name table entries is expensive.
   1656    // Although ASCII localized family names are possible they don't occur
   1657    // in practice, so avoid pulling in names at startup.
   1658    if (!family && !mOtherFamilyNamesInitialized) {
   1659      bool triggerLoading = true;
   1660      bool mayDefer =
   1661          !(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading);
   1662      if (IsAscii(key)) {
   1663        // If `key` is an ASCII name, only trigger loading if it includes a
   1664        // space, and the "base" name (up to the last space) exists as a known
   1665        // family, so that this might be a legacy styled-family name.
   1666        const char* data = key.BeginReading();
   1667        int32_t index = key.Length();
   1668        while (--index > 0) {
   1669          if (data[index] == ' ') {
   1670            break;
   1671          }
   1672        }
   1673        if (index <= 0 ||
   1674            !SharedFontList()->FindFamily(nsAutoCString(key.get(), index))) {
   1675          triggerLoading = false;
   1676        }
   1677      }
   1678      if (triggerLoading) {
   1679        if (InitOtherFamilyNames(mayDefer)) {
   1680          family = SharedFontList()->FindFamily(key);
   1681        }
   1682      }
   1683      if (!family && !mOtherFamilyNamesInitialized &&
   1684          !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
   1685        AddToMissedNames(key);
   1686      }
   1687    }
   1688    // Check whether the family we found is actually allowed to be looked up,
   1689    // according to current font-visibility prefs.
   1690    if (family) {
   1691      bool visible = IsVisibleToCSS(*family, visibilityLevel);
   1692      if (visible || (allowHidden && family->IsHidden())) {
   1693        aOutput->AppendElement(FamilyAndGeneric(family, aGeneric));
   1694        return true;
   1695      }
   1696      if (aFontVisibilityProvider) {
   1697        aFontVisibilityProvider->ReportBlockedFontFamily(*family);
   1698      }
   1699    }
   1700    return false;
   1701  }
   1702 
   1703  NS_ASSERTION(mFontFamilies.Count() != 0,
   1704               "system font list was not initialized correctly");
   1705 
   1706  auto isBlockedByVisibilityLevel = [=](gfxFontFamily* aFamily) -> bool {
   1707    bool visible = IsVisibleToCSS(*aFamily, visibilityLevel);
   1708    if (visible || (allowHidden && aFamily->IsHidden())) {
   1709      return false;
   1710    }
   1711    if (aFontVisibilityProvider) {
   1712      aFontVisibilityProvider->ReportBlockedFontFamily(*aFamily);
   1713    }
   1714    return true;
   1715  };
   1716 
   1717  // lookup in canonical (i.e. English) family name list
   1718  gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
   1719  if (familyEntry) {
   1720    if (isBlockedByVisibilityLevel(familyEntry)) {
   1721      return false;
   1722    }
   1723  }
   1724 
   1725  // if not found, lookup in other family names list (mostly localized names)
   1726  if (!familyEntry) {
   1727    familyEntry = mOtherFamilyNames.GetWeak(key);
   1728  }
   1729  if (familyEntry) {
   1730    if (isBlockedByVisibilityLevel(familyEntry)) {
   1731      return false;
   1732    }
   1733  }
   1734 
   1735  // if still not found and other family names not yet fully initialized,
   1736  // initialize the rest of the list and try again.  this is done lazily
   1737  // since reading name table entries is expensive.
   1738  // although ASCII localized family names are possible they don't occur
   1739  // in practice so avoid pulling in names at startup
   1740  if (!familyEntry && !mOtherFamilyNamesInitialized && !IsAscii(aFamily)) {
   1741    InitOtherFamilyNames(
   1742        !(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading));
   1743    familyEntry = mOtherFamilyNames.GetWeak(key);
   1744    if (!familyEntry && !mOtherFamilyNamesInitialized &&
   1745        !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
   1746      // localized family names load timed out, add name to list of
   1747      // names to check after localized names are loaded
   1748      AddToMissedNames(key);
   1749    }
   1750    if (familyEntry) {
   1751      if (isBlockedByVisibilityLevel(familyEntry)) {
   1752        return false;
   1753      }
   1754    }
   1755  }
   1756 
   1757  familyEntry = CheckFamily(familyEntry);
   1758 
   1759  // If we failed to find the requested family, check for a space in the
   1760  // name; if found, and if the "base" name (up to the last space) exists
   1761  // as a family, then this might be a legacy GDI-style family name for
   1762  // an additional weight/width. Try searching the faces of the base family
   1763  // and create any corresponding legacy families.
   1764  if (!familyEntry &&
   1765      !(aFlags & FindFamiliesFlags::eNoSearchForLegacyFamilyNames)) {
   1766    // We don't have nsAString::RFindChar, so look for a space manually
   1767    const char* data = aFamily.BeginReading();
   1768    int32_t index = aFamily.Length();
   1769    while (--index > 0) {
   1770      if (data[index] == ' ') {
   1771        break;
   1772      }
   1773    }
   1774    if (index > 0) {
   1775      gfxFontFamily* base = FindUnsharedFamily(
   1776          aFontVisibilityProvider, Substring(aFamily, 0, index),
   1777          FindFamiliesFlags::eNoSearchForLegacyFamilyNames);
   1778      // If we found the "base" family name, and if it has members with
   1779      // legacy names, this will add corresponding font-family entries to
   1780      // the mOtherFamilyNames list; then retry the legacy-family search.
   1781      if (base && base->CheckForLegacyFamilyNames(this)) {
   1782        familyEntry = mOtherFamilyNames.GetWeak(key);
   1783      }
   1784      if (familyEntry) {
   1785        if (isBlockedByVisibilityLevel(familyEntry)) {
   1786          return false;
   1787        }
   1788      }
   1789    }
   1790  }
   1791 
   1792  if (familyEntry) {
   1793    aOutput->AppendElement(FamilyAndGeneric(familyEntry, aGeneric));
   1794    return true;
   1795  }
   1796 
   1797  return false;
   1798 }
   1799 
   1800 void gfxPlatformFontList::AddToMissedNames(const nsCString& aKey) {
   1801  if (!mOtherNamesMissed) {
   1802    mOtherNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
   1803  }
   1804  mOtherNamesMissed->Insert(aKey);
   1805 }
   1806 
   1807 fontlist::Family* gfxPlatformFontList::FindSharedFamily(
   1808    FontVisibilityProvider* aFontVisibilityProvider, const nsACString& aFamily,
   1809    FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
   1810    gfxFloat aDevToCss) {
   1811  if (!SharedFontList()) {
   1812    return nullptr;
   1813  }
   1814  AutoTArray<FamilyAndGeneric, 1> families;
   1815  if (!FindAndAddFamiliesLocked(
   1816          aFontVisibilityProvider, StyleGenericFontFamily::None, aFamily,
   1817          &families, aFlags, aStyle, aLanguage, aDevToCss) ||
   1818      !families[0].mFamily.mShared) {
   1819    return nullptr;
   1820  }
   1821  fontlist::Family* family = families[0].mFamily.mShared;
   1822  if (!family->IsInitialized()) {
   1823    if (!InitializeFamily(family)) {
   1824      return nullptr;
   1825    }
   1826  }
   1827  return family;
   1828 }
   1829 
   1830 class InitializeFamilyRunnable : public mozilla::Runnable {
   1831 public:
   1832  explicit InitializeFamilyRunnable(uint32_t aFamilyIndex, bool aLoadCmaps)
   1833      : Runnable("gfxPlatformFontList::InitializeFamilyRunnable"),
   1834        mIndex(aFamilyIndex),
   1835        mLoadCmaps(aLoadCmaps) {}
   1836 
   1837  NS_IMETHOD Run() override {
   1838    auto list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
   1839    if (!list) {
   1840      return NS_OK;
   1841    }
   1842    if (mIndex >= list->NumFamilies()) {
   1843      // Out of range? Maybe the list got reinitialized since this request
   1844      // was posted - just ignore it.
   1845      return NS_OK;
   1846    }
   1847    dom::ContentChild::GetSingleton()->SendInitializeFamily(
   1848        list->GetGeneration(), mIndex, mLoadCmaps);
   1849    return NS_OK;
   1850  }
   1851 
   1852 private:
   1853  uint32_t mIndex;
   1854  bool mLoadCmaps;
   1855 };
   1856 
   1857 bool gfxPlatformFontList::InitializeFamily(fontlist::Family* aFamily,
   1858                                           bool aLoadCmaps) {
   1859  MOZ_ASSERT(SharedFontList());
   1860  auto list = SharedFontList();
   1861  if (!XRE_IsParentProcess()) {
   1862    auto* families = list->Families();
   1863    if (!families) {
   1864      return false;
   1865    }
   1866    uint32_t index = aFamily - families;
   1867    if (index >= list->NumFamilies()) {
   1868      return false;
   1869    }
   1870    if (NS_IsMainThread()) {
   1871      dom::ContentChild::GetSingleton()->SendInitializeFamily(
   1872          list->GetGeneration(), index, aLoadCmaps);
   1873    } else {
   1874      NS_DispatchToMainThread(new InitializeFamilyRunnable(index, aLoadCmaps));
   1875    }
   1876    return aFamily->IsInitialized();
   1877  }
   1878 
   1879  if (!aFamily->IsInitialized()) {
   1880    // The usual case: we're being asked to populate the face list.
   1881    AutoTArray<fontlist::Face::InitData, 16> faceList;
   1882    GetFacesInitDataForFamily(aFamily, faceList, aLoadCmaps);
   1883    aFamily->AddFaces(list, faceList);
   1884  } else {
   1885    // The family's face list was already initialized, but if aLoadCmaps is
   1886    // true we also want to eagerly load character maps. This is used when a
   1887    // child process is doing SearchAllFontsForChar, to have the parent load
   1888    // all the cmaps at once and reduce IPC traffic (and content-process file
   1889    // access overhead, which is crippling for DirectWrite on Windows).
   1890    if (aLoadCmaps) {
   1891      auto* faces = aFamily->Faces(list);
   1892      if (faces) {
   1893        for (size_t i = 0; i < aFamily->NumFaces(); i++) {
   1894          auto* face = faces[i].ToPtr<fontlist::Face>(list);
   1895          if (face && face->mCharacterMap.IsNull()) {
   1896            // We don't want to cache this font entry, as the parent will most
   1897            // likely never use it again; it's just to populate the charmap for
   1898            // the benefit of the child process.
   1899            RefPtr<gfxFontEntry> fe = CreateFontEntry(face, aFamily);
   1900            if (fe) {
   1901              fe->ReadCMAP();
   1902            }
   1903          }
   1904        }
   1905      }
   1906    }
   1907  }
   1908 
   1909  if (aLoadCmaps && aFamily->IsInitialized()) {
   1910    aFamily->SetupFamilyCharMap(list);
   1911  }
   1912 
   1913  return aFamily->IsInitialized();
   1914 }
   1915 
   1916 gfxFontEntry* gfxPlatformFontList::FindFontForFamily(
   1917    FontVisibilityProvider* aFontVisibilityProvider, const nsACString& aFamily,
   1918    const gfxFontStyle* aStyle) {
   1919  AutoLock lock(mLock);
   1920 
   1921  nsAutoCString key;
   1922  GenerateFontListKey(aFamily, key);
   1923 
   1924  FontFamily family = FindFamily(aFontVisibilityProvider, key);
   1925  if (family.IsNull()) {
   1926    return nullptr;
   1927  }
   1928  if (family.mShared) {
   1929    auto face = family.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
   1930    if (!face) {
   1931      return nullptr;
   1932    }
   1933    return GetOrCreateFontEntryLocked(face, family.mShared);
   1934  }
   1935  return family.mUnshared->FindFontForStyle(*aStyle);
   1936 }
   1937 
   1938 gfxFontEntry* gfxPlatformFontList::GetOrCreateFontEntryLocked(
   1939    fontlist::Face* aFace, const fontlist::Family* aFamily) {
   1940  return mFontEntries
   1941      .LookupOrInsertWith(aFace,
   1942                          [=] { return CreateFontEntry(aFace, aFamily); })
   1943      .get();
   1944 }
   1945 
   1946 void gfxPlatformFontList::AddOtherFamilyNames(
   1947    gfxFontFamily* aFamilyEntry, const nsTArray<nsCString>& aOtherFamilyNames) {
   1948  AutoLock lock(mLock);
   1949 
   1950  for (const auto& name : aOtherFamilyNames) {
   1951    nsAutoCString key;
   1952    GenerateFontListKey(name, key);
   1953 
   1954    mOtherFamilyNames.LookupOrInsertWith(key, [&] {
   1955      LOG_FONTLIST(
   1956          ("(fontlist-otherfamily) canonical family: %s, other family: %s\n",
   1957           aFamilyEntry->Name().get(), name.get()));
   1958      if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
   1959        aFamilyEntry->SetBadUnderlineFamily();
   1960      }
   1961      return RefPtr{aFamilyEntry};
   1962    });
   1963  }
   1964 }
   1965 
   1966 void gfxPlatformFontList::AddFullnameLocked(gfxFontEntry* aFontEntry,
   1967                                            const nsCString& aFullname) {
   1968  mExtraNames->mFullnames.LookupOrInsertWith(aFullname, [&] {
   1969    LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
   1970                  aFontEntry->Name().get(), aFullname.get()));
   1971    return RefPtr{aFontEntry};
   1972  });
   1973 }
   1974 
   1975 void gfxPlatformFontList::AddPostscriptNameLocked(
   1976    gfxFontEntry* aFontEntry, const nsCString& aPostscriptName) {
   1977  mExtraNames->mPostscriptNames.LookupOrInsertWith(aPostscriptName, [&] {
   1978    LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
   1979                  aFontEntry->Name().get(), aPostscriptName.get()));
   1980    return RefPtr{aFontEntry};
   1981  });
   1982 }
   1983 
   1984 bool gfxPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
   1985                                                nsACString& aFamilyName) {
   1986  AutoLock lock(mLock);
   1987  FontFamily family = FindFamily(nullptr, aFontName);
   1988  if (family.IsNull()) {
   1989    return false;
   1990  }
   1991  return GetLocalizedFamilyName(family, aFamilyName);
   1992 }
   1993 
   1994 bool gfxPlatformFontList::GetLocalizedFamilyName(const FontFamily& aFamily,
   1995                                                 nsACString& aFamilyName) {
   1996  if (aFamily.mShared) {
   1997    aFamilyName = SharedFontList()->LocalizedFamilyName(aFamily.mShared);
   1998    return true;
   1999  }
   2000  if (aFamily.mUnshared) {
   2001    aFamily.mUnshared->LocalizedName(aFamilyName);
   2002    return true;
   2003  }
   2004  return false;  // leaving the aFamilyName outparam untouched
   2005 }
   2006 
   2007 FamilyAndGeneric gfxPlatformFontList::GetDefaultFontFamily(
   2008    const nsACString& aLangGroup, const nsACString& aGenericFamily) {
   2009  if (NS_WARN_IF(aLangGroup.IsEmpty()) ||
   2010      NS_WARN_IF(aGenericFamily.IsEmpty())) {
   2011    return FamilyAndGeneric();
   2012  }
   2013 
   2014  AutoLock lock(mLock);
   2015 
   2016  nsAutoCString value;
   2017  AutoTArray<nsCString, 4> names;
   2018  if (mFontPrefs->LookupNameList(PrefName(aGenericFamily, aLangGroup), value)) {
   2019    gfxFontUtils::ParseFontList(value, names);
   2020  }
   2021 
   2022  for (const nsCString& name : names) {
   2023    FontFamily family = FindFamily(nullptr, name);
   2024    if (!family.IsNull()) {
   2025      return FamilyAndGeneric(family);
   2026    }
   2027  }
   2028 
   2029  return FamilyAndGeneric();
   2030 }
   2031 
   2032 ShmemCharMapHashEntry::ShmemCharMapHashEntry(const gfxSparseBitSet* aCharMap)
   2033    : mList(gfxPlatformFontList::PlatformFontList()->SharedFontList()),
   2034      mHash(aCharMap->GetChecksum()) {
   2035  size_t len = SharedBitSet::RequiredSize(*aCharMap);
   2036  mCharMap = mList->Alloc(len);
   2037  SharedBitSet::Create(mCharMap.ToPtr(mList, len), len, *aCharMap);
   2038 }
   2039 
   2040 fontlist::Pointer gfxPlatformFontList::GetShmemCharMapLocked(
   2041    const gfxSparseBitSet* aCmap) {
   2042  auto* entry = mShmemCharMaps.GetEntry(aCmap);
   2043  if (!entry) {
   2044    entry = mShmemCharMaps.PutEntry(aCmap);
   2045  }
   2046  return entry->GetCharMap();
   2047 }
   2048 
   2049 // Lookup aCmap in the shared cmap set, adding if not already present.
   2050 // This is the only way for a reference to a gfxCharacterMap to be acquired
   2051 // by another thread than its original creator.
   2052 already_AddRefed<gfxCharacterMap> gfxPlatformFontList::FindCharMap(
   2053    gfxCharacterMap* aCmap) {
   2054  // Lock to prevent potentially racing against MaybeRemoveCmap.
   2055  AutoLock lock(mLock);
   2056 
   2057  // Find existing entry or insert a new one (which will add a reference).
   2058  aCmap->CalcHash();
   2059  aCmap->mShared = true;  // Set the shared flag in preparation for adding
   2060                          // to the global table.
   2061  RefPtr cmap = mSharedCmaps.PutEntry(aCmap)->GetKey();
   2062 
   2063  // If we ended up finding a different, pre-existing entry, clear the
   2064  // shared flag on this one so that it'll get deleted on Release().
   2065  if (cmap.get() != aCmap) {
   2066    aCmap->mShared = false;
   2067  }
   2068 
   2069  return cmap.forget();
   2070 }
   2071 
   2072 // Potentially remove the charmap from the shared cmap set. This is called
   2073 // when a user of the charmap drops a reference and the refcount goes to 1;
   2074 // in that case, it is possible our shared set is the only remaining user
   2075 // of the object, and we should remove it.
   2076 // Note that aCharMap might have already been freed, so we must not try to
   2077 // dereference it until we have checked that it's still present in our table.
   2078 void gfxPlatformFontList::MaybeRemoveCmap(gfxCharacterMap* aCharMap) {
   2079  // Lock so that nobody else can get a reference via FindCharMap while we're
   2080  // checking here.
   2081  AutoLock lock(mLock);
   2082 
   2083  // Skip lookups during teardown.
   2084  if (!mSharedCmaps.Count()) {
   2085    return;
   2086  }
   2087 
   2088  // aCharMap needs to match the entry and be the same ptr and still have a
   2089  // refcount of exactly 1 (i.e. we hold the only reference) before removing.
   2090  // If we're racing another thread, it might already have been removed, in
   2091  // which case GetEntry will not find it and we won't try to dereference the
   2092  // already-freed pointer.
   2093  CharMapHashKey* found =
   2094      mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
   2095  if (found && found->GetKey() == aCharMap && aCharMap->RefCount() == 1) {
   2096    // Forget our reference to the object that's being deleted, without
   2097    // calling Release() on it.
   2098    found->mCharMap.forget().leak();
   2099 
   2100    // Do the deletion.
   2101    delete aCharMap;
   2102 
   2103    // Log this as a "Release" to keep leak-checking correct.
   2104    NS_LOG_RELEASE(aCharMap, 0, "gfxCharacterMap");
   2105 
   2106    mSharedCmaps.RemoveEntry(found);
   2107  }
   2108 }
   2109 
   2110 static void GetSystemUIFontFamilies(
   2111    FontVisibilityProvider* aFontVisibilityProvider,
   2112    [[maybe_unused]] nsAtom* aLangGroup, nsTArray<nsCString>& aFamilies) {
   2113  // TODO: On macOS, use CTCreateUIFontForLanguage or such thing (though the
   2114  // code below ends up using [NSFont systemFontOfSize: 0.0].
   2115  nsFont systemFont;
   2116  gfxFontStyle fontStyle;
   2117  nsAutoString systemFontName;
   2118  if (aFontVisibilityProvider
   2119          ? aFontVisibilityProvider->ShouldResistFingerprinting(
   2120                RFPTarget::FontVisibilityRestrictGenerics)
   2121          : nsContentUtils::ShouldResistFingerprinting(
   2122                "aFontVisibilityProvider not available",
   2123                RFPTarget::FontVisibilityRestrictGenerics)) {
   2124 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_UIKIT)
   2125    *aFamilies.AppendElement() = "-apple-system"_ns;
   2126    return;
   2127 #elif defined(MOZ_WIDGET_GTK)
   2128    // tor-browser#43141: Hardcode Arimo in case our custom fontconfig is
   2129    // missing.
   2130    *aFamilies.AppendElement() = "Arimo"_ns;
   2131    return;
   2132 #elif !defined(MOZ_WIDGET_ANDROID)
   2133    *aFamilies.AppendElement() = "sans-serif"_ns;
   2134    return;
   2135 #endif
   2136  }
   2137  if (!LookAndFeel::GetFont(StyleSystemFont::Menu, systemFontName, fontStyle)) {
   2138    return;
   2139  }
   2140  systemFontName.Trim("\"'");
   2141  CopyUTF16toUTF8(systemFontName, *aFamilies.AppendElement());
   2142 }
   2143 
   2144 void gfxPlatformFontList::ResolveGenericFontNames(
   2145    FontVisibilityProvider* aFontVisibilityProvider,
   2146    StyleGenericFontFamily aGenericType, eFontPrefLang aPrefLang,
   2147    PrefFontList* aGenericFamilies) {
   2148  const char* langGroupStr = GetPrefLangName(aPrefLang);
   2149  const char* generic = GetGenericName(aGenericType);
   2150 
   2151  if (!generic) {
   2152    return;
   2153  }
   2154 
   2155  AutoTArray<nsCString, 4> genericFamilies;
   2156 
   2157  // load family for "font.name.generic.lang"
   2158  PrefName prefName(generic, langGroupStr);
   2159  nsAutoCString value;
   2160  if (mFontPrefs->LookupName(prefName, value)) {
   2161    gfxFontUtils::ParseFontList(value, genericFamilies);
   2162  }
   2163 
   2164  // load fonts for "font.name-list.generic.lang"
   2165  if (mFontPrefs->LookupNameList(prefName, value)) {
   2166    gfxFontUtils::ParseFontList(value, genericFamilies);
   2167  }
   2168 
   2169  nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
   2170  MOZ_ASSERT(langGroup, "null lang group for pref lang");
   2171 
   2172  if (aGenericType == StyleGenericFontFamily::SystemUi) {
   2173    GetSystemUIFontFamilies(aFontVisibilityProvider, langGroup,
   2174                            genericFamilies);
   2175  }
   2176 
   2177  GetFontFamiliesFromGenericFamilies(aFontVisibilityProvider, aGenericType,
   2178                                     genericFamilies, langGroup,
   2179                                     aGenericFamilies);
   2180 
   2181 #if 0  // dump out generic mappings
   2182    printf("%s ===> ", NamePref(generic, langGroupStr).get());
   2183    for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
   2184        if (k > 0) printf(", ");
   2185        printf("%s", (*aGenericFamilies)[k].mIsShared
   2186            ? (*aGenericFamilies)[k].mShared->DisplayName().AsString(SharedFontList()).get()
   2187            : (*aGenericFamilies)[k].mUnshared->Name().get());
   2188    }
   2189    printf("\n");
   2190 #endif
   2191 }
   2192 
   2193 void gfxPlatformFontList::ResolveEmojiFontNames(
   2194    FontVisibilityProvider* aFontVisibilityProvider,
   2195    PrefFontList* aGenericFamilies) {
   2196  // emoji preference has no lang name
   2197  AutoTArray<nsCString, 4> genericFamilies;
   2198 
   2199  nsAutoCString value;
   2200  if (mFontPrefs->LookupNameList(PrefName("emoji", ""), value)) {
   2201    gfxFontUtils::ParseFontList(value, genericFamilies);
   2202  }
   2203 
   2204  GetFontFamiliesFromGenericFamilies(
   2205      aFontVisibilityProvider, StyleGenericFontFamily::MozEmoji,
   2206      genericFamilies, nullptr, aGenericFamilies);
   2207 }
   2208 
   2209 void gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(
   2210    FontVisibilityProvider* aFontVisibilityProvider,
   2211    StyleGenericFontFamily aGenericType,
   2212    nsTArray<nsCString>& aGenericNameFamilies, nsAtom* aLangGroup,
   2213    PrefFontList* aGenericFamilies) {
   2214  // lookup and add platform fonts uniquely
   2215  for (const nsCString& genericFamily : aGenericNameFamilies) {
   2216    AutoTArray<FamilyAndGeneric, 10> families;
   2217    FindAndAddFamiliesLocked(aFontVisibilityProvider, aGenericType,
   2218                             genericFamily, &families, FindFamiliesFlags(0),
   2219                             nullptr, aLangGroup);
   2220    for (const FamilyAndGeneric& f : families) {
   2221      if (!aGenericFamilies->Contains(f.mFamily)) {
   2222        aGenericFamilies->AppendElement(f.mFamily);
   2223      }
   2224    }
   2225  }
   2226 }
   2227 
   2228 gfxPlatformFontList::PrefFontList*
   2229 gfxPlatformFontList::GetPrefFontsLangGroupLocked(
   2230    FontVisibilityProvider* aFontVisibilityProvider,
   2231    StyleGenericFontFamily aGenericType, eFontPrefLang aPrefLang) {
   2232  if (aGenericType == StyleGenericFontFamily::MozEmoji ||
   2233      aPrefLang == eFontPrefLang_Emoji) {
   2234    // Emoji font has no lang
   2235    PrefFontList* prefFonts = mEmojiPrefFont.get();
   2236    if (MOZ_UNLIKELY(!prefFonts)) {
   2237      prefFonts = new PrefFontList;
   2238      ResolveEmojiFontNames(aFontVisibilityProvider, prefFonts);
   2239      mEmojiPrefFont.reset(prefFonts);
   2240    }
   2241    return prefFonts;
   2242  }
   2243 
   2244  auto index = static_cast<size_t>(aGenericType);
   2245  PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][index].get();
   2246  if (MOZ_UNLIKELY(!prefFonts)) {
   2247    prefFonts = new PrefFontList;
   2248    ResolveGenericFontNames(aFontVisibilityProvider, aGenericType, aPrefLang,
   2249                            prefFonts);
   2250    mLangGroupPrefFonts[aPrefLang][index].reset(prefFonts);
   2251  }
   2252  return prefFonts;
   2253 }
   2254 
   2255 void gfxPlatformFontList::AddGenericFonts(
   2256    FontVisibilityProvider* aFontVisibilityProvider,
   2257    StyleGenericFontFamily aGenericType, nsAtom* aLanguage,
   2258    nsTArray<FamilyAndGeneric>& aFamilyList) {
   2259  AutoLock lock(mLock);
   2260 
   2261  // TODO(eri): For now the math generic language uses the legacy
   2262  // "serif.x-math" configuration font list to avoid excesive
   2263  // repetition until a better font preference system can be found.
   2264  if (StaticPrefs::mathml_font_family_math_enabled() &&
   2265      aGenericType == StyleGenericFontFamily::Math) {
   2266    aGenericType = StyleGenericFontFamily::Serif;
   2267    aLanguage = nsGkAtoms::x_math;
   2268  }
   2269 
   2270  // map lang ==> langGroup
   2271  nsAtom* langGroup = GetLangGroup(aLanguage);
   2272 
   2273  // langGroup ==> prefLang
   2274  eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
   2275 
   2276  // lookup pref fonts
   2277  PrefFontList* prefFonts = GetPrefFontsLangGroupLocked(aFontVisibilityProvider,
   2278                                                        aGenericType, prefLang);
   2279 
   2280  if (!prefFonts->IsEmpty()) {
   2281    aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
   2282    for (auto& f : *prefFonts) {
   2283      aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
   2284    }
   2285  }
   2286 }
   2287 
   2288 static nsAtom* PrefLangToLangGroups(uint32_t aIndex) {
   2289  // static array here avoids static constructor
   2290  static nsAtom* gPrefLangToLangGroups[] = {
   2291 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
   2292 #include "gfxFontPrefLangList.h"
   2293 #undef FONT_PREF_LANG
   2294  };
   2295 
   2296  return aIndex < std::size(gPrefLangToLangGroups)
   2297             ? gPrefLangToLangGroups[aIndex]
   2298             : nsGkAtoms::Unicode;
   2299 }
   2300 
   2301 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(const char* aLang) {
   2302  if (!aLang || !aLang[0]) {
   2303    return eFontPrefLang_Others;
   2304  }
   2305  for (uint32_t i = 0; i < std::size(gPrefLangNames); ++i) {
   2306    if (!nsCRT::strcasecmp(gPrefLangNames[i], aLang)) {
   2307      return eFontPrefLang(i);
   2308    }
   2309    // If the pref-lang is a two-character lang tag, try ignoring any trailing
   2310    // subtags in aLang and see if the base lang matches.
   2311    if (strlen(gPrefLangNames[i]) == 2 && strlen(aLang) > 3 &&
   2312        aLang[2] == '-' && !nsCRT::strncasecmp(gPrefLangNames[i], aLang, 2)) {
   2313      return eFontPrefLang(i);
   2314    }
   2315  }
   2316  return eFontPrefLang_Others;
   2317 }
   2318 
   2319 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(nsAtom* aLang) {
   2320  if (!aLang) return eFontPrefLang_Others;
   2321  nsAutoCString lang;
   2322  aLang->ToUTF8String(lang);
   2323  return GetFontPrefLangFor(lang.get());
   2324 }
   2325 
   2326 nsAtom* gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang) {
   2327  // the special CJK set pref lang should be resolved into separate
   2328  // calls to individual CJK pref langs before getting here
   2329  NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
   2330 
   2331  return PrefLangToLangGroups(uint32_t(aLang));
   2332 }
   2333 
   2334 const char* gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang) {
   2335  if (uint32_t(aLang) < std::size(gPrefLangNames)) {
   2336    return gPrefLangNames[uint32_t(aLang)];
   2337  }
   2338  return nullptr;
   2339 }
   2340 
   2341 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(uint32_t aCh) {
   2342  switch (ublock_getCode(aCh)) {
   2343    case UBLOCK_BASIC_LATIN:
   2344    case UBLOCK_LATIN_1_SUPPLEMENT:
   2345    case UBLOCK_LATIN_EXTENDED_A:
   2346    case UBLOCK_LATIN_EXTENDED_B:
   2347    case UBLOCK_IPA_EXTENSIONS:
   2348    case UBLOCK_SPACING_MODIFIER_LETTERS:
   2349    case UBLOCK_LATIN_EXTENDED_ADDITIONAL:
   2350    case UBLOCK_LATIN_EXTENDED_C:
   2351    case UBLOCK_LATIN_EXTENDED_D:
   2352    case UBLOCK_LATIN_EXTENDED_E:
   2353    case UBLOCK_PHONETIC_EXTENSIONS:
   2354      return eFontPrefLang_Western;
   2355    case UBLOCK_GREEK:
   2356    case UBLOCK_GREEK_EXTENDED:
   2357      return eFontPrefLang_Greek;
   2358    case UBLOCK_CYRILLIC:
   2359    case UBLOCK_CYRILLIC_SUPPLEMENT:
   2360    case UBLOCK_CYRILLIC_EXTENDED_A:
   2361    case UBLOCK_CYRILLIC_EXTENDED_B:
   2362    case UBLOCK_CYRILLIC_EXTENDED_C:
   2363      return eFontPrefLang_Cyrillic;
   2364    case UBLOCK_ARMENIAN:
   2365      return eFontPrefLang_Armenian;
   2366    case UBLOCK_HEBREW:
   2367      return eFontPrefLang_Hebrew;
   2368    case UBLOCK_ARABIC:
   2369    case UBLOCK_ARABIC_PRESENTATION_FORMS_A:
   2370    case UBLOCK_ARABIC_PRESENTATION_FORMS_B:
   2371    case UBLOCK_ARABIC_SUPPLEMENT:
   2372    case UBLOCK_ARABIC_EXTENDED_A:
   2373    case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS:
   2374      return eFontPrefLang_Arabic;
   2375    case UBLOCK_DEVANAGARI:
   2376    case UBLOCK_DEVANAGARI_EXTENDED:
   2377      return eFontPrefLang_Devanagari;
   2378    case UBLOCK_BENGALI:
   2379      return eFontPrefLang_Bengali;
   2380    case UBLOCK_GURMUKHI:
   2381      return eFontPrefLang_Gurmukhi;
   2382    case UBLOCK_GUJARATI:
   2383      return eFontPrefLang_Gujarati;
   2384    case UBLOCK_ORIYA:
   2385      return eFontPrefLang_Oriya;
   2386    case UBLOCK_TAMIL:
   2387      return eFontPrefLang_Tamil;
   2388    case UBLOCK_TELUGU:
   2389      return eFontPrefLang_Telugu;
   2390    case UBLOCK_KANNADA:
   2391      return eFontPrefLang_Kannada;
   2392    case UBLOCK_MALAYALAM:
   2393      return eFontPrefLang_Malayalam;
   2394    case UBLOCK_SINHALA:
   2395    case UBLOCK_SINHALA_ARCHAIC_NUMBERS:
   2396      return eFontPrefLang_Sinhala;
   2397    case UBLOCK_THAI:
   2398      return eFontPrefLang_Thai;
   2399    case UBLOCK_TIBETAN:
   2400      return eFontPrefLang_Tibetan;
   2401    case UBLOCK_GEORGIAN:
   2402    case UBLOCK_GEORGIAN_SUPPLEMENT:
   2403    case UBLOCK_GEORGIAN_EXTENDED:
   2404      return eFontPrefLang_Georgian;
   2405    case UBLOCK_HANGUL_JAMO:
   2406    case UBLOCK_HANGUL_COMPATIBILITY_JAMO:
   2407    case UBLOCK_HANGUL_SYLLABLES:
   2408    case UBLOCK_HANGUL_JAMO_EXTENDED_A:
   2409    case UBLOCK_HANGUL_JAMO_EXTENDED_B:
   2410      return eFontPrefLang_Korean;
   2411    case UBLOCK_ETHIOPIC:
   2412    case UBLOCK_ETHIOPIC_EXTENDED:
   2413    case UBLOCK_ETHIOPIC_SUPPLEMENT:
   2414    case UBLOCK_ETHIOPIC_EXTENDED_A:
   2415      return eFontPrefLang_Ethiopic;
   2416    case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS:
   2417    case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED:
   2418      return eFontPrefLang_Canadian;
   2419    case UBLOCK_KHMER:
   2420    case UBLOCK_KHMER_SYMBOLS:
   2421      return eFontPrefLang_Khmer;
   2422    case UBLOCK_CJK_RADICALS_SUPPLEMENT:
   2423    case UBLOCK_KANGXI_RADICALS:
   2424    case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS:
   2425    case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
   2426    case UBLOCK_HIRAGANA:
   2427    case UBLOCK_KATAKANA:
   2428    case UBLOCK_BOPOMOFO:
   2429    case UBLOCK_KANBUN:
   2430    case UBLOCK_BOPOMOFO_EXTENDED:
   2431    case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS:
   2432    case UBLOCK_CJK_COMPATIBILITY:
   2433    case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:
   2434    case UBLOCK_CJK_UNIFIED_IDEOGRAPHS:
   2435    case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS:
   2436    case UBLOCK_CJK_COMPATIBILITY_FORMS:
   2437    case UBLOCK_SMALL_FORM_VARIANTS:
   2438    case UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS:
   2439    case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:
   2440    case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:
   2441    case UBLOCK_KATAKANA_PHONETIC_EXTENSIONS:
   2442    case UBLOCK_CJK_STROKES:
   2443    case UBLOCK_VERTICAL_FORMS:
   2444    case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C:
   2445    case UBLOCK_KANA_SUPPLEMENT:
   2446    case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D:
   2447    case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E:
   2448    case UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION:
   2449    case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F:
   2450    case UBLOCK_KANA_EXTENDED_A:
   2451      return eFontPrefLang_CJKSet;
   2452    case UBLOCK_MATHEMATICAL_OPERATORS:
   2453    case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS:
   2454    case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A:
   2455    case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B:
   2456    case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS:
   2457      return eFontPrefLang_Mathematics;
   2458    default:
   2459      return eFontPrefLang_Others;
   2460  }
   2461 }
   2462 
   2463 bool gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang) {
   2464  switch (aLang) {
   2465    case eFontPrefLang_Japanese:
   2466    case eFontPrefLang_ChineseTW:
   2467    case eFontPrefLang_ChineseCN:
   2468    case eFontPrefLang_ChineseHK:
   2469    case eFontPrefLang_Korean:
   2470    case eFontPrefLang_CJKSet:
   2471      return true;
   2472    default:
   2473      return false;
   2474  }
   2475 }
   2476 
   2477 void gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[],
   2478                                       uint32_t& aLen, eFontPrefLang aCharLang,
   2479                                       eFontPrefLang aPageLang) {
   2480  AutoLock lock(mLock);
   2481  if (IsLangCJK(aCharLang)) {
   2482    AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
   2483  } else {
   2484    AppendPrefLang(aPrefLangs, aLen, aCharLang);
   2485  }
   2486 
   2487  AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
   2488 }
   2489 
   2490 void gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[],
   2491                                             uint32_t& aLen,
   2492                                             eFontPrefLang aCharLang,
   2493                                             eFontPrefLang aPageLang) {
   2494  // prefer the lang specified by the page *if* CJK
   2495  if (IsLangCJK(aPageLang)) {
   2496    AppendPrefLang(aPrefLangs, aLen, aPageLang);
   2497  }
   2498 
   2499  // append in cached CJK langs
   2500  for (const auto lang : GetFontPrefs()->CJKPrefLangs()) {
   2501    AppendPrefLang(aPrefLangs, aLen, eFontPrefLang(lang));
   2502  }
   2503 }
   2504 
   2505 void gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[],
   2506                                         uint32_t& aLen,
   2507                                         eFontPrefLang aAddLang) {
   2508  if (aLen >= kMaxLenPrefLangList) {
   2509    return;
   2510  }
   2511 
   2512  // If the lang is already present, just ignore the addition.
   2513  for (const auto lang : Span<eFontPrefLang>(aPrefLangs, aLen)) {
   2514    if (lang == aAddLang) {
   2515      return;
   2516    }
   2517  }
   2518 
   2519  aPrefLangs[aLen++] = aAddLang;
   2520 }
   2521 
   2522 StyleGenericFontFamily gfxPlatformFontList::GetDefaultGeneric(
   2523    eFontPrefLang aLang) {
   2524  if (aLang == eFontPrefLang_Emoji) {
   2525    return StyleGenericFontFamily::MozEmoji;
   2526  }
   2527 
   2528  AutoLock lock(mLock);
   2529 
   2530  if (uint32_t(aLang) < std::size(gPrefLangNames)) {
   2531    return mDefaultGenericsLangGroup[uint32_t(aLang)];
   2532  }
   2533  return StyleGenericFontFamily::Serif;
   2534 }
   2535 
   2536 FontFamily gfxPlatformFontList::GetDefaultFont(
   2537    FontVisibilityProvider* aFontVisibilityProvider,
   2538    const gfxFontStyle* aStyle) {
   2539  AutoLock lock(mLock);
   2540  return GetDefaultFontLocked(aFontVisibilityProvider, aStyle);
   2541 }
   2542 
   2543 FontFamily gfxPlatformFontList::GetDefaultFontLocked(
   2544    FontVisibilityProvider* aFontVisibilityProvider,
   2545    const gfxFontStyle* aStyle) {
   2546  FontFamily family =
   2547      GetDefaultFontForPlatform(aFontVisibilityProvider, aStyle);
   2548  if (!family.IsNull()) {
   2549    return family;
   2550  }
   2551  // Something has gone wrong and we were unable to retrieve a default font
   2552  // from the platform. (Likely the whitelist has blocked all potential
   2553  // default fonts.) As a last resort, we return the first font in our list.
   2554  if (SharedFontList()) {
   2555    MOZ_RELEASE_ASSERT(SharedFontList()->NumFamilies() > 0);
   2556    return FontFamily(SharedFontList()->Families());
   2557  }
   2558  MOZ_RELEASE_ASSERT(mFontFamilies.Count() > 0);
   2559  return FontFamily(mFontFamilies.ConstIter().Data());
   2560 }
   2561 
   2562 void gfxPlatformFontList::GetFontFamilyNames(
   2563    nsTArray<nsCString>& aFontFamilyNames) {
   2564  if (SharedFontList()) {
   2565    fontlist::FontList* list = SharedFontList();
   2566    const fontlist::Family* families = list->Families();
   2567    if (families) {
   2568      for (uint32_t i = 0, n = list->NumFamilies(); i < n; i++) {
   2569        const fontlist::Family& family = families[i];
   2570        if (!family.IsHidden()) {
   2571          aFontFamilyNames.AppendElement(family.DisplayName().AsString(list));
   2572        }
   2573      }
   2574    }
   2575  } else {
   2576    for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
   2577      if (!family->IsHidden()) {
   2578        aFontFamilyNames.AppendElement(family->Name());
   2579      }
   2580    }
   2581  }
   2582 }
   2583 
   2584 nsAtom* gfxPlatformFontList::GetLangGroup(nsAtom* aLanguage) {
   2585  // map lang ==> langGroup
   2586  nsAtom* langGroup = nullptr;
   2587  if (aLanguage) {
   2588    langGroup = mLangService->GetLanguageGroup(aLanguage);
   2589  }
   2590  if (!langGroup) {
   2591    langGroup = nsGkAtoms::Unicode;
   2592  }
   2593  return langGroup;
   2594 }
   2595 
   2596 /* static */ const char* gfxPlatformFontList::GetGenericName(
   2597    StyleGenericFontFamily aGenericType) {
   2598  // type should be standard generic type at this point
   2599  // map generic type to string
   2600  switch (aGenericType) {
   2601    case StyleGenericFontFamily::Serif:
   2602      return "serif";
   2603    case StyleGenericFontFamily::SansSerif:
   2604      return "sans-serif";
   2605    case StyleGenericFontFamily::Monospace:
   2606      return "monospace";
   2607    case StyleGenericFontFamily::Cursive:
   2608      return "cursive";
   2609    case StyleGenericFontFamily::Fantasy:
   2610      return "fantasy";
   2611    case StyleGenericFontFamily::Math:
   2612      return "math";
   2613    case StyleGenericFontFamily::SystemUi:
   2614      return "system-ui";
   2615    case StyleGenericFontFamily::MozEmoji:
   2616      return "-moz-emoji";
   2617    case StyleGenericFontFamily::None:
   2618      break;
   2619  }
   2620  MOZ_ASSERT_UNREACHABLE("Unknown generic");
   2621  return nullptr;
   2622 }
   2623 
   2624 void gfxPlatformFontList::InitLoader() {
   2625  GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
   2626  mStartIndex = 0;
   2627  mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
   2628  memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
   2629 }
   2630 
   2631 #define FONT_LOADER_MAX_TIMESLICE \
   2632  20  // max time for one pass through RunLoader = 20ms
   2633 
   2634 bool gfxPlatformFontList::LoadFontInfo() {
   2635  AutoLock lock(mLock);
   2636  TimeStamp start = TimeStamp::Now();
   2637  uint32_t i, endIndex = mNumFamilies;
   2638  fontlist::FontList* list = SharedFontList();
   2639  bool loadCmaps =
   2640      !list && (!UsesSystemFallback() ||
   2641                gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback());
   2642 
   2643  // for each font family, load in various font info
   2644  for (i = mStartIndex; i < endIndex; i++) {
   2645    nsAutoCString key;
   2646    GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
   2647 
   2648    if (list) {
   2649      fontlist::Family* family = list->FindFamily(key);
   2650      if (!family) {
   2651        continue;
   2652      }
   2653      ReadFaceNamesForFamily(family, NeedFullnamePostscriptNames());
   2654    } else {
   2655      // lookup in canonical (i.e. English) family name list
   2656      gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
   2657      if (!familyEntry) {
   2658        continue;
   2659      }
   2660 
   2661      // read in face names
   2662      familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(),
   2663                                 mFontInfo);
   2664 
   2665      // load the cmaps if needed
   2666      if (loadCmaps) {
   2667        familyEntry->ReadAllCMAPs(mFontInfo);
   2668      }
   2669    }
   2670 
   2671    // Limit the time spent reading fonts in one pass, unless the font-loader
   2672    // delay was set to zero, in which case we run to completion even if it
   2673    // causes some jank.
   2674    if (StaticPrefs::gfx_font_loader_delay() > 0) {
   2675      TimeDuration elapsed = TimeStamp::Now() - start;
   2676      if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
   2677          i + 1 != endIndex) {
   2678        endIndex = i + 1;
   2679        break;
   2680      }
   2681    }
   2682  }
   2683 
   2684  mStartIndex = endIndex;
   2685  bool done = mStartIndex >= mNumFamilies;
   2686 
   2687  if (LOG_FONTINIT_ENABLED()) {
   2688    TimeDuration elapsed = TimeStamp::Now() - start;
   2689    LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
   2690                  elapsed.ToMilliseconds(), (done ? "true" : "false")));
   2691  }
   2692 
   2693  if (done) {
   2694    mOtherFamilyNamesInitialized = true;
   2695    CancelInitOtherFamilyNamesTask();
   2696    mFaceNameListsInitialized = true;
   2697  }
   2698 
   2699  return done;
   2700 }
   2701 
   2702 void gfxPlatformFontList::CleanupLoader() {
   2703  AutoLock lock(mLock);
   2704 
   2705  mFontFamiliesToLoad.Clear();
   2706  mNumFamilies = 0;
   2707  bool rebuilt = false, forceReflow = false;
   2708 
   2709  // if had missed face names that are now available, force reflow all
   2710  if (mFaceNamesMissed) {
   2711    rebuilt = std::any_of(mFaceNamesMissed->cbegin(), mFaceNamesMissed->cend(),
   2712                          [&](const auto& key) {
   2713                            mLock.AssertCurrentThreadIn();
   2714                            return FindFaceName(key);
   2715                          });
   2716    if (rebuilt) {
   2717      RebuildLocalFonts();
   2718    }
   2719 
   2720    mFaceNamesMissed = nullptr;
   2721  }
   2722 
   2723  if (mOtherNamesMissed) {
   2724    forceReflow = std::any_of(
   2725        mOtherNamesMissed->cbegin(), mOtherNamesMissed->cend(),
   2726        [&](const auto& key) {
   2727          mLock.AssertCurrentThreadIn();
   2728          return FindUnsharedFamily(
   2729              nullptr, key,
   2730              (FindFamiliesFlags::eForceOtherFamilyNamesLoading |
   2731               FindFamiliesFlags::eNoAddToNamesMissedWhenSearching));
   2732        });
   2733    if (forceReflow) {
   2734      gfxPlatform::GlobalReflowFlags flags =
   2735          gfxPlatform::GlobalReflowFlags::FontsChanged;
   2736      ForceGlobalReflowLocked(flags);
   2737    }
   2738 
   2739    mOtherNamesMissed = nullptr;
   2740  }
   2741 
   2742  if (LOG_FONTINIT_ENABLED() && mFontInfo) {
   2743    LOG_FONTINIT(
   2744        ("(fontinit) fontloader load thread took %8.2f ms "
   2745         "%d families %d fonts %d cmaps "
   2746         "%d facenames %d othernames %s %s",
   2747         mLoadTime.ToMilliseconds(), mFontInfo->mLoadStats.families,
   2748         mFontInfo->mLoadStats.fonts, mFontInfo->mLoadStats.cmaps,
   2749         mFontInfo->mLoadStats.facenames, mFontInfo->mLoadStats.othernames,
   2750         (rebuilt ? "(userfont sets rebuilt)" : ""),
   2751         (forceReflow ? "(global reflow)" : "")));
   2752  }
   2753 
   2754  gfxFontInfoLoader::CleanupLoader();
   2755 }
   2756 
   2757 void gfxPlatformFontList::ForceGlobalReflow(
   2758    gfxPlatform::GlobalReflowFlags aFlags) {
   2759  if (!NS_IsMainThread()) {
   2760    NS_DispatchToMainThread(NS_NewRunnableFunction(
   2761        "gfxPlatformFontList::ForceGlobalReflow",
   2762        [this, aFlags] { this->ForceGlobalReflow(aFlags); }));
   2763    return;
   2764  }
   2765 
   2766  if (aFlags & gfxPlatform::GlobalReflowFlags::FontsChanged) {
   2767    AutoLock lock(mLock);
   2768    InitializeCodepointsWithNoFonts();
   2769    if (SharedFontList()) {
   2770      // If we're using a shared local face-name list, this may have changed
   2771      // such that existing font entries held by user font sets are no longer
   2772      // safe to use: ensure they all get flushed.
   2773      RebuildLocalFonts(/*aForgetLocalFaces*/ true);
   2774    }
   2775  }
   2776 
   2777  gfxPlatform::ForceGlobalReflow(aFlags);
   2778 }
   2779 
   2780 void gfxPlatformFontList::ForceGlobalReflowLocked(
   2781    gfxPlatform::GlobalReflowFlags aFlags) {
   2782  if (!NS_IsMainThread()) {
   2783    NS_DispatchToMainThread(NS_NewRunnableFunction(
   2784        "gfxPlatformFontList::ForceGlobalReflow",
   2785        [this, aFlags] { this->ForceGlobalReflow(aFlags); }));
   2786    return;
   2787  }
   2788 
   2789  if (aFlags & gfxPlatform::GlobalReflowFlags::FontsChanged) {
   2790    InitializeCodepointsWithNoFonts();
   2791    if (SharedFontList()) {
   2792      RebuildLocalFonts(/*aForgetLocalFaces*/ true);
   2793    }
   2794  }
   2795 
   2796  AutoUnlock unlock(mLock);
   2797  gfxPlatform::ForceGlobalReflow(aFlags);
   2798 }
   2799 
   2800 void gfxPlatformFontList::GetPrefsAndStartLoader() {
   2801  // If we're already in shutdown, there's no point in starting this, and it
   2802  // could trigger an assertion if we try to use the Thread Manager too late.
   2803  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   2804    return;
   2805  }
   2806  uint32_t delay = std::max(1u, StaticPrefs::gfx_font_loader_delay());
   2807  if (NS_IsMainThread()) {
   2808    StartLoader(delay);
   2809  } else {
   2810    NS_DispatchToMainThread(NS_NewRunnableFunction(
   2811        "StartLoader callback", [delay, fontList = this] {
   2812          fontList->Lock();
   2813          fontList->StartLoader(delay);
   2814          fontList->Unlock();
   2815        }));
   2816  }
   2817 }
   2818 
   2819 void gfxPlatformFontList::RebuildLocalFonts(bool aForgetLocalFaces) {
   2820  // Make a local copy of the list of font sets we need to process.
   2821  AutoTArray<RefPtr<gfxUserFontSet>, 16> fontSets;
   2822  fontSets.SetCapacity(mUserFontSetList.Count());
   2823  for (auto* fontset : mUserFontSetList) {
   2824    fontSets.AppendElement(fontset);
   2825  }
   2826  // Drop our lock before calling ForgetLocalFaces and RebuildLocalRules
   2827  // for each set, to avoid possible deadlocks.
   2828  AutoUnlock unlock(mLock);
   2829  for (auto fontset : fontSets) {
   2830    if (aForgetLocalFaces) {
   2831      fontset->ForgetLocalFaces();
   2832    }
   2833    fontset->RebuildLocalRules();
   2834  }
   2835 }
   2836 
   2837 void gfxPlatformFontList::ClearLangGroupPrefFontsLocked() {
   2838  for (uint32_t i = eFontPrefLang_First;
   2839       i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
   2840    auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
   2841    for (auto& pref : prefFontsLangGroup) {
   2842      pref = nullptr;
   2843    }
   2844  }
   2845  mEmojiPrefFont = nullptr;
   2846 
   2847  // Create a new FontPrefs and replace the existing one.
   2848  mFontPrefs = MakeUnique<FontPrefs>();
   2849 }
   2850 
   2851 // Support for memory reporting
   2852 
   2853 // this is also used by subclasses that hold additional font tables
   2854 /*static*/
   2855 size_t gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis(
   2856    const FontFamilyTable& aTable, MallocSizeOf aMallocSizeOf) {
   2857  return std::accumulate(
   2858      aTable.Keys().cbegin(), aTable.Keys().cend(),
   2859      aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
   2860      [&](size_t oldValue, const nsACString& key) {
   2861        // We don't count the size of the family here, because this is an
   2862        // *extra* reference to a family that will have already been counted in
   2863        // the main list.
   2864        return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   2865      });
   2866 }
   2867 
   2868 /*static*/
   2869 size_t gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
   2870    const FontEntryTable& aTable, MallocSizeOf aMallocSizeOf) {
   2871  return std::accumulate(
   2872      aTable.Keys().cbegin(), aTable.Keys().cend(),
   2873      aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
   2874      [&](size_t oldValue, const nsACString& key) {
   2875        // The font itself is counted by its owning family; here we only care
   2876        // about the names stored in the hashtable keys.
   2877 
   2878        return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   2879      });
   2880 }
   2881 
   2882 void gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
   2883                                                 FontListSizes* aSizes) const {
   2884  AutoLock lock(mLock);
   2885 
   2886  aSizes->mFontListSize +=
   2887      mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
   2888  for (const auto& entry : mFontFamilies) {
   2889    aSizes->mFontListSize +=
   2890        entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   2891    entry.GetData()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
   2892  }
   2893 
   2894  aSizes->mFontListSize +=
   2895      SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
   2896 
   2897  if (mExtraNames) {
   2898    aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
   2899        mExtraNames->mFullnames, aMallocSizeOf);
   2900    aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
   2901        mExtraNames->mPostscriptNames, aMallocSizeOf);
   2902  }
   2903 
   2904  for (uint32_t i = eFontPrefLang_First;
   2905       i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
   2906    auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
   2907    for (const UniquePtr<PrefFontList>& pf : prefFontsLangGroup) {
   2908      if (pf) {
   2909        aSizes->mFontListSize += pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
   2910      }
   2911    }
   2912  }
   2913 
   2914  for (const auto& bitset : mCodepointsWithNoFonts) {
   2915    aSizes->mFontListSize += bitset.SizeOfExcludingThis(aMallocSizeOf);
   2916  }
   2917  aSizes->mFontListSize +=
   2918      mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
   2919 
   2920  aSizes->mFontListSize +=
   2921      mBadUnderlineFamilyNames.ShallowSizeOfExcludingThis(aMallocSizeOf);
   2922  for (const auto& i : mBadUnderlineFamilyNames) {
   2923    aSizes->mFontListSize += i.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   2924  }
   2925 
   2926  aSizes->mFontListSize +=
   2927      mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
   2928  for (const auto& entry : mSharedCmaps) {
   2929    aSizes->mCharMapsSize += entry.GetKey()->SizeOfIncludingThis(aMallocSizeOf);
   2930  }
   2931 
   2932  aSizes->mFontListSize +=
   2933      mFontEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
   2934  for (const auto& entry : mFontEntries.Values()) {
   2935    if (entry) {
   2936      entry->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
   2937    }
   2938  }
   2939 
   2940  if (SharedFontList()) {
   2941    aSizes->mFontListSize +=
   2942        SharedFontList()->SizeOfIncludingThis(aMallocSizeOf);
   2943    if (XRE_IsParentProcess()) {
   2944      aSizes->mSharedSize += SharedFontList()->AllocatedShmemSize();
   2945    }
   2946  }
   2947 }
   2948 
   2949 void gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   2950                                                 FontListSizes* aSizes) const {
   2951  aSizes->mFontListSize += aMallocSizeOf(this);
   2952  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   2953 }
   2954 
   2955 void gfxPlatformFontList::InitOtherFamilyNamesInternal(
   2956    bool aDeferOtherFamilyNamesLoading) {
   2957  if (mOtherFamilyNamesInitialized) {
   2958    return;
   2959  }
   2960 
   2961  AutoLock lock(mLock);
   2962 
   2963  if (aDeferOtherFamilyNamesLoading) {
   2964    TimeStamp start = TimeStamp::Now();
   2965    bool timedOut = false;
   2966 
   2967    auto list = SharedFontList();
   2968    if (list) {
   2969      // If the gfxFontInfoLoader task is not yet running, kick it off now so
   2970      // that it will load remaining names etc as soon as idle time permits.
   2971      if (mState == stateInitial || mState == stateTimerOnDelay) {
   2972        StartLoader(0);
   2973        timedOut = true;
   2974      }
   2975    } else {
   2976      for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
   2977        family->ReadOtherFamilyNames(this);
   2978        TimeDuration elapsed = TimeStamp::Now() - start;
   2979        if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
   2980          timedOut = true;
   2981          break;
   2982        }
   2983      }
   2984    }
   2985 
   2986    if (!timedOut) {
   2987      mOtherFamilyNamesInitialized = true;
   2988      CancelInitOtherFamilyNamesTask();
   2989    }
   2990    TimeDuration elapsed = TimeStamp::Now() - start;
   2991    glean::fontlist::initotherfamilynames.AccumulateRawDuration(elapsed);
   2992 
   2993    if (LOG_FONTINIT_ENABLED()) {
   2994      LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
   2995                    elapsed.ToMilliseconds(), (timedOut ? "timeout" : "")));
   2996    }
   2997  } else {
   2998    TimeStamp start = TimeStamp::Now();
   2999 
   3000    auto list = SharedFontList();
   3001    if (list) {
   3002      for (auto& f : mozilla::Range<fontlist::Family>(list->Families(),
   3003                                                      list->NumFamilies())) {
   3004        ReadFaceNamesForFamily(&f, false);
   3005      }
   3006    } else {
   3007      for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
   3008        family->ReadOtherFamilyNames(this);
   3009      }
   3010    }
   3011 
   3012    mOtherFamilyNamesInitialized = true;
   3013    CancelInitOtherFamilyNamesTask();
   3014 
   3015    TimeDuration elapsed = TimeStamp::Now() - start;
   3016    glean::fontlist::initotherfamilynames_no_deferring.AccumulateRawDuration(
   3017        elapsed);
   3018 
   3019    if (LOG_FONTINIT_ENABLED()) {
   3020      LOG_FONTINIT(
   3021          ("(fontinit) InitOtherFamilyNames without deferring took %8.2f ms",
   3022           elapsed.ToMilliseconds()));
   3023    }
   3024  }
   3025 }
   3026 
   3027 void gfxPlatformFontList::CancelInitOtherFamilyNamesTask() {
   3028  if (mPendingOtherFamilyNameTask) {
   3029    mPendingOtherFamilyNameTask->Cancel();
   3030    mPendingOtherFamilyNameTask = nullptr;
   3031  }
   3032  auto list = SharedFontList();
   3033  if (list && XRE_IsParentProcess()) {
   3034    bool forceReflow = false;
   3035    if (!mAliasTable.IsEmpty()) {
   3036      list->SetAliases(mAliasTable);
   3037      mAliasTable.Clear();
   3038      forceReflow = true;
   3039    }
   3040    if (mLocalNameTable.Count()) {
   3041      list->SetLocalNames(mLocalNameTable);
   3042      mLocalNameTable.Clear();
   3043      forceReflow = true;
   3044    }
   3045    // If there's a LoadCmapsRunnable alive, we ignore forceReflow because the
   3046    // runnable will trigger a reflow when it completes, and we don't want to
   3047    // reflow more times than necessary.
   3048    if (forceReflow && !mLoadCmapsRunnable) {
   3049      gfxPlatform::GlobalReflowFlags flags =
   3050          gfxPlatform::GlobalReflowFlags::BroadcastToChildren |
   3051          gfxPlatform::GlobalReflowFlags::FontsChanged;
   3052      gfxPlatform::ForceGlobalReflow(flags);
   3053    }
   3054  }
   3055 }
   3056 
   3057 void gfxPlatformFontList::ShareFontListShmBlockToProcess(
   3058    uint32_t aGeneration, uint32_t aIndex, base::ProcessId aPid,
   3059    mozilla::ipc::ReadOnlySharedMemoryHandle* aOut) {
   3060  auto list = SharedFontList();
   3061  if (!list) {
   3062    return;
   3063  }
   3064  if (!aGeneration || list->GetGeneration() == aGeneration) {
   3065    list->ShareShmBlockToProcess(aIndex, aPid, aOut);
   3066  } else {
   3067    *aOut = nullptr;
   3068  }
   3069 }
   3070 
   3071 void gfxPlatformFontList::ShareFontListToProcess(
   3072    nsTArray<mozilla::ipc::ReadOnlySharedMemoryHandle>* aBlocks,
   3073    base::ProcessId aPid) {
   3074  auto list = SharedFontList();
   3075  if (list) {
   3076    list->ShareBlocksToProcess(aBlocks, aPid);
   3077  }
   3078 }
   3079 
   3080 mozilla::ipc::ReadOnlySharedMemoryHandle
   3081 gfxPlatformFontList::ShareShmBlockToProcess(uint32_t aIndex,
   3082                                            base::ProcessId aPid) {
   3083  MOZ_RELEASE_ASSERT(SharedFontList());
   3084  return SharedFontList()->ShareBlockToProcess(aIndex, aPid);
   3085 }
   3086 
   3087 void gfxPlatformFontList::ShmBlockAdded(
   3088    uint32_t aGeneration, uint32_t aIndex,
   3089    mozilla::ipc::ReadOnlySharedMemoryHandle aHandle) {
   3090  if (SharedFontList()) {
   3091    AutoLock lock(mLock);
   3092    SharedFontList()->ShmBlockAdded(aGeneration, aIndex, std::move(aHandle));
   3093  }
   3094 }
   3095 
   3096 void gfxPlatformFontList::InitializeFamily(uint32_t aGeneration,
   3097                                           uint32_t aFamilyIndex,
   3098                                           bool aLoadCmaps) {
   3099  auto list = SharedFontList();
   3100  MOZ_ASSERT(list);
   3101  if (!list) {
   3102    return;
   3103  }
   3104  if (list->GetGeneration() != aGeneration) {
   3105    return;
   3106  }
   3107  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   3108    return;
   3109  }
   3110  if (aFamilyIndex >= list->NumFamilies()) {
   3111    return;
   3112  }
   3113  fontlist::Family* family = list->Families() + aFamilyIndex;
   3114  if (!family->IsInitialized() || aLoadCmaps) {
   3115    (void)InitializeFamily(family, aLoadCmaps);
   3116  }
   3117 }
   3118 
   3119 void gfxPlatformFontList::SetCharacterMap(uint32_t aGeneration,
   3120                                          uint32_t aFamilyIndex, bool aAlias,
   3121                                          uint32_t aFaceIndex,
   3122                                          const gfxSparseBitSet& aMap) {
   3123  MOZ_ASSERT(XRE_IsParentProcess());
   3124  auto list = SharedFontList();
   3125  MOZ_ASSERT(list);
   3126  if (!list) {
   3127    return;
   3128  }
   3129  if (list->GetGeneration() != aGeneration) {
   3130    return;
   3131  }
   3132  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   3133    return;
   3134  }
   3135 
   3136  const fontlist::Family* family;
   3137  if (aAlias) {
   3138    if (aFamilyIndex >= list->NumAliases()) {
   3139      MOZ_ASSERT(false, "AliasFamily index out of range");
   3140      return;
   3141    }
   3142    family = list->AliasFamilies() + aFamilyIndex;
   3143  } else {
   3144    if (aFamilyIndex >= list->NumFamilies()) {
   3145      MOZ_ASSERT(false, "Family index out of range");
   3146      return;
   3147    }
   3148    family = list->Families() + aFamilyIndex;
   3149  }
   3150 
   3151  if (aFaceIndex >= family->NumFaces()) {
   3152    MOZ_ASSERT(false, "Face index out of range");
   3153    return;
   3154  }
   3155 
   3156  if (auto* face =
   3157          family->Faces(list)[aFaceIndex].ToPtr<fontlist::Face>(list)) {
   3158    face->mCharacterMap = GetShmemCharMap(&aMap);
   3159  }
   3160 }
   3161 
   3162 void gfxPlatformFontList::SetupFamilyCharMap(uint32_t aGeneration,
   3163                                             uint32_t aIndex, bool aAlias) {
   3164  MOZ_ASSERT(XRE_IsParentProcess());
   3165  auto list = SharedFontList();
   3166  MOZ_ASSERT(list);
   3167  if (!list) {
   3168    return;
   3169  }
   3170  if (list->GetGeneration() != aGeneration) {
   3171    return;
   3172  }
   3173  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   3174    return;
   3175  }
   3176 
   3177  if (aAlias) {
   3178    if (aIndex >= list->NumAliases()) {
   3179      MOZ_ASSERT(false, "AliasFamily index out of range");
   3180      return;
   3181    }
   3182    list->AliasFamilies()[aIndex].SetupFamilyCharMap(list);
   3183    return;
   3184  }
   3185 
   3186  if (aIndex >= list->NumFamilies()) {
   3187    MOZ_ASSERT(false, "Family index out of range");
   3188    return;
   3189  }
   3190  list->Families()[aIndex].SetupFamilyCharMap(list);
   3191 }
   3192 
   3193 bool gfxPlatformFontList::InitOtherFamilyNames(uint32_t aGeneration,
   3194                                               bool aDefer) {
   3195  auto list = SharedFontList();
   3196  MOZ_ASSERT(list);
   3197  if (!list) {
   3198    return false;
   3199  }
   3200  if (list->GetGeneration() != aGeneration) {
   3201    return false;
   3202  }
   3203  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   3204    return false;
   3205  }
   3206  return InitOtherFamilyNames(aDefer);
   3207 }
   3208 
   3209 uint32_t gfxPlatformFontList::GetGeneration() const {
   3210  return SharedFontList() ? SharedFontList()->GetGeneration() : 0;
   3211 }
   3212 
   3213 gfxPlatformFontList::FontPrefs::FontPrefs() {
   3214  // This must be created on the main thread, so that we can safely use the
   3215  // Preferences service. Once created, it can be read from any thread.
   3216  MOZ_ASSERT(NS_IsMainThread());
   3217  Init();
   3218 }
   3219 
   3220 void gfxPlatformFontList::FontPrefs::Init() {
   3221  if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
   3222    return;
   3223  }
   3224  nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch();
   3225  if (!prefRootBranch) {
   3226    return;
   3227  }
   3228  nsTArray<nsCString> prefNames;
   3229  if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNamePrefix, prefNames))) {
   3230    for (auto& prefName : prefNames) {
   3231      nsAutoCString value;
   3232      if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
   3233        nsAutoCString pref(Substring(prefName, sizeof(kNamePrefix) - 1));
   3234        mFontName.InsertOrUpdate(pref, value);
   3235      }
   3236    }
   3237  }
   3238  if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNameListPrefix, prefNames))) {
   3239    for (auto& prefName : prefNames) {
   3240      nsAutoCString value;
   3241      if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
   3242        nsAutoCString pref(Substring(prefName, sizeof(kNameListPrefix) - 1));
   3243        mFontNameList.InsertOrUpdate(pref, value);
   3244      }
   3245    }
   3246  }
   3247  mEmojiHasUserValue = Preferences::HasUserValue("font.name-list.emoji");
   3248 
   3249  // Record the default CJK order, based on accept-lang settings and locale.
   3250  eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
   3251  uint32_t tempLen = 0;
   3252 
   3253  // Add the CJK pref fonts from accept languages, the order should be same
   3254  // order.
   3255  nsAutoCString acceptLang;
   3256  nsresult rv = LocaleService::GetInstance()->GetAcceptLanguages(acceptLang);
   3257 
   3258  // We use gfxFontUtils::ParseFontList to read the list even
   3259  // though it's not actually a list of fonts but of locale codes;
   3260  // the format is the same.
   3261  AutoTArray<nsCString, 5> list;
   3262  if (NS_SUCCEEDED(rv)) {
   3263    gfxFontUtils::ParseFontList(acceptLang, list);
   3264  }
   3265 
   3266  for (const auto& lang : list) {
   3267    eFontPrefLang fpl = GetFontPrefLangFor(lang.get());
   3268    switch (fpl) {
   3269      case eFontPrefLang_Japanese:
   3270      case eFontPrefLang_Korean:
   3271      case eFontPrefLang_ChineseCN:
   3272      case eFontPrefLang_ChineseHK:
   3273      case eFontPrefLang_ChineseTW:
   3274        AppendPrefLang(tempPrefLangs, tempLen, fpl);
   3275        break;
   3276      default:
   3277        break;
   3278    }
   3279  }
   3280 
   3281  // Try using app's locale
   3282  nsAutoCString localeStr;
   3283  LocaleService::GetInstance()->GetAppLocaleAsBCP47(localeStr);
   3284 
   3285  {
   3286    Locale locale;
   3287    if (LocaleParser::TryParse(localeStr, locale).isOk() &&
   3288        locale.Canonicalize().isOk()) {
   3289      if (locale.Language().EqualTo("ja")) {
   3290        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
   3291      } else if (locale.Language().EqualTo("zh")) {
   3292        if (locale.Region().EqualTo("CN")) {
   3293          AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
   3294        } else if (locale.Region().EqualTo("TW")) {
   3295          AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
   3296        } else if (locale.Region().EqualTo("HK")) {
   3297          AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
   3298        }
   3299      } else if (locale.Language().EqualTo("ko")) {
   3300        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
   3301      }
   3302    }
   3303  }
   3304 
   3305  // Then add the known CJK prefs in order of system preferred locales
   3306  AutoTArray<nsCString, 5> prefLocales;
   3307  prefLocales.AppendElement("ja"_ns);
   3308  prefLocales.AppendElement("zh-CN"_ns);
   3309  prefLocales.AppendElement("zh-TW"_ns);
   3310  prefLocales.AppendElement("zh-HK"_ns);
   3311  prefLocales.AppendElement("ko"_ns);
   3312 
   3313  AutoTArray<nsCString, 16> sysLocales;
   3314  AutoTArray<nsCString, 16> negLocales;
   3315  if (NS_SUCCEEDED(
   3316          OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) {
   3317    LocaleService::GetInstance()->NegotiateLanguages(
   3318        sysLocales, prefLocales, ""_ns,
   3319        LocaleService::kLangNegStrategyFiltering, negLocales);
   3320    for (const auto& localeStr : negLocales) {
   3321      Locale locale;
   3322      if (LocaleParser::TryParse(localeStr, locale).isOk() &&
   3323          locale.Canonicalize().isOk()) {
   3324        if (locale.Language().EqualTo("ja")) {
   3325          AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
   3326        } else if (locale.Language().EqualTo("zh")) {
   3327          if (locale.Region().EqualTo("CN")) {
   3328            AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
   3329          } else if (locale.Region().EqualTo("TW")) {
   3330            AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
   3331          } else if (locale.Region().EqualTo("HK")) {
   3332            AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
   3333          }
   3334        } else if (locale.Language().EqualTo("ko")) {
   3335          AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
   3336        }
   3337      }
   3338    }
   3339  }
   3340 
   3341  // Last resort... set up CJK font prefs in the order listed by the user-
   3342  // configurable ordering pref.
   3343  gfxFontUtils::GetPrefsFontList(kCJKFallbackOrderPref, list);
   3344  for (const auto& item : list) {
   3345    eFontPrefLang fpl = GetFontPrefLangFor(item.get());
   3346    switch (fpl) {
   3347      case eFontPrefLang_Japanese:
   3348      case eFontPrefLang_Korean:
   3349      case eFontPrefLang_ChineseCN:
   3350      case eFontPrefLang_ChineseHK:
   3351      case eFontPrefLang_ChineseTW:
   3352        AppendPrefLang(tempPrefLangs, tempLen, fpl);
   3353        break;
   3354      default:
   3355        break;
   3356    }
   3357  }
   3358 
   3359  // Truly-last resort... try Chinese font prefs before Japanese because
   3360  // they tend to have more complete character coverage, and therefore less
   3361  // risk of "ransom-note" effects.
   3362  // (If the kCJKFallbackOrderPref was fully populated, as it is by default,
   3363  // this will do nothing as all these values are already present.)
   3364  AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
   3365  AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
   3366  AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
   3367  AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
   3368  AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
   3369 
   3370  // copy into the cached array
   3371  for (const auto lang : Span<eFontPrefLang>(tempPrefLangs, tempLen)) {
   3372    mCJKPrefLangs.AppendElement(lang);
   3373  }
   3374 }
   3375 
   3376 bool gfxPlatformFontList::FontPrefs::LookupName(const nsACString& aPref,
   3377                                                nsACString& aValue) const {
   3378  if (const auto& value = mFontName.Lookup(aPref)) {
   3379    aValue = *value;
   3380    return true;
   3381  }
   3382  return false;
   3383 }
   3384 
   3385 bool gfxPlatformFontList::FontPrefs::LookupNameList(const nsACString& aPref,
   3386                                                    nsACString& aValue) const {
   3387  if (const auto& value = mFontNameList.Lookup(aPref)) {
   3388    aValue = *value;
   3389    return true;
   3390  }
   3391  return false;
   3392 }
   3393 
   3394 bool gfxPlatformFontList::IsKnownIconFontFamily(
   3395    const nsAtom* aFamilyName) const {
   3396  nsAtomCString fam(aFamilyName);
   3397  ToLowerCase(fam);
   3398  return mIconFontsSet.Contains(fam);
   3399 }
   3400 
   3401 #undef LOG
   3402 #undef LOG_ENABLED