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