Bug 1876318 - set shipping-product for push-bundle tasks. r=bhearsum,releng-reviewers
[gecko.git] / gfx / thebes / gfxPlatformFontList.cpp
blobbc0e123f85871c6a4903d579fede248f6cb38c16
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/. */
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"
12 #include "gfxPlatformFontList.h"
13 #include "gfxTextRun.h"
14 #include "gfxUserFontSet.h"
15 #include "SharedFontList-impl.h"
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"
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/dom/Document.h"
42 #include "mozilla/gfx/2D.h"
43 #include "mozilla/ipc/FileDescriptorUtils.h"
44 #include "mozilla/ResultExtensions.h"
45 #include "mozilla/TextUtils.h"
46 #include "mozilla/Unused.h"
48 #include "base/eintr_wrapper.h"
50 #include <locale.h>
51 #include <numeric>
53 using namespace mozilla;
54 using mozilla::intl::Locale;
55 using mozilla::intl::LocaleParser;
56 using mozilla::intl::LocaleService;
57 using mozilla::intl::OSPreferences;
59 #define LOG_FONTLIST(args) \
60 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
61 #define LOG_FONTLIST_ENABLED() \
62 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
63 #define LOG_FONTINIT(args) \
64 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
65 #define LOG_FONTINIT_ENABLED() \
66 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
68 gfxPlatformFontList* gfxPlatformFontList::sPlatformFontList = nullptr;
70 // Character ranges that require complex-script shaping support in the font,
71 // and so should be masked out by ReadCMAP if the necessary layout tables
72 // are not present.
73 // Currently used by the Mac and FT2 implementations only, but probably should
74 // be supported on Windows as well.
75 const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = {
76 // Actually, now that harfbuzz supports presentation-forms shaping for
77 // Arabic, we can render it without layout tables. So maybe we don't
78 // want to mask the basic Arabic block here?
79 // This affects the arabic-fallback-*.html reftests, which rely on
80 // loading a font that *doesn't* have any GSUB table.
81 {0x0600, 0x060B, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
82 // skip 060C Arabic comma, also used by N'Ko etc
83 {0x060D, 0x061A, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
84 // skip 061B Arabic semicolon, also used by N'Ko etc
85 {0x061C, 0x061E, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
86 // skip 061F Arabic question mark, also used by N'Ko etc
87 {0x0620, 0x063F, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
88 // skip 0640 Arabic tatweel (for syriac, adlam, etc)
89 {0x0641, 0x06D3, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
90 // skip 06D4 Arabic full stop (for hanifi rohingya)
91 {0x06D5, 0x06FF, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
92 {0x0700, 0x074F, 1, {TRUETYPE_TAG('s', 'y', 'r', 'c'), 0, 0}},
93 {0x0750, 0x077F, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
94 {0x08A0, 0x08FF, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
95 {0x0900,
96 0x0963,
98 {TRUETYPE_TAG('d', 'e', 'v', '2'), TRUETYPE_TAG('d', 'e', 'v', 'a'), 0}},
99 // skip 0964 DEVANAGARI DANDA and 0965 DEVANAGARI DOUBLE DANDA, shared by
100 // various other Indic writing systems
101 {0x0966,
102 0x097F,
104 {TRUETYPE_TAG('d', 'e', 'v', '2'), TRUETYPE_TAG('d', 'e', 'v', 'a'), 0}},
105 {0x0980,
106 0x09FF,
108 {TRUETYPE_TAG('b', 'n', 'g', '2'), TRUETYPE_TAG('b', 'e', 'n', 'g'), 0}},
109 {0x0A00,
110 0x0A7F,
112 {TRUETYPE_TAG('g', 'u', 'r', '2'), TRUETYPE_TAG('g', 'u', 'r', 'u'), 0}},
113 {0x0A80,
114 0x0AFF,
116 {TRUETYPE_TAG('g', 'j', 'r', '2'), TRUETYPE_TAG('g', 'u', 'j', 'r'), 0}},
117 {0x0B00,
118 0x0B7F,
120 {TRUETYPE_TAG('o', 'r', 'y', '2'), TRUETYPE_TAG('o', 'r', 'y', 'a'), 0}},
121 {0x0B80,
122 0x0BFF,
124 {TRUETYPE_TAG('t', 'm', 'l', '2'), TRUETYPE_TAG('t', 'a', 'm', 'l'), 0}},
125 {0x0C00,
126 0x0C7F,
128 {TRUETYPE_TAG('t', 'e', 'l', '2'), TRUETYPE_TAG('t', 'e', 'l', 'u'), 0}},
129 {0x0C80,
130 0x0CFF,
132 {TRUETYPE_TAG('k', 'n', 'd', '2'), TRUETYPE_TAG('k', 'n', 'd', 'a'), 0}},
133 {0x0D00,
134 0x0D7F,
136 {TRUETYPE_TAG('m', 'l', 'm', '2'), TRUETYPE_TAG('m', 'l', 'y', 'm'), 0}},
137 {0x0D80, 0x0DFF, 1, {TRUETYPE_TAG('s', 'i', 'n', 'h'), 0, 0}},
138 {0x0E80, 0x0EFF, 1, {TRUETYPE_TAG('l', 'a', 'o', ' '), 0, 0}},
139 {0x0F00, 0x0FFF, 1, {TRUETYPE_TAG('t', 'i', 'b', 't'), 0, 0}},
140 {0x1000,
141 0x109f,
143 {TRUETYPE_TAG('m', 'y', 'm', 'r'), TRUETYPE_TAG('m', 'y', 'm', '2'), 0}},
144 {0x1780, 0x17ff, 1, {TRUETYPE_TAG('k', 'h', 'm', 'r'), 0, 0}},
145 // Khmer Symbols (19e0..19ff) don't seem to need any special shaping
146 {0xaa60,
147 0xaa7f,
149 {TRUETYPE_TAG('m', 'y', 'm', 'r'), TRUETYPE_TAG('m', 'y', 'm', '2'), 0}},
150 // Thai seems to be "renderable" without AAT morphing tables
151 {0, 0, 0, {0, 0, 0}} // terminator
154 static const char* kObservedPrefs[] = {
155 "font.", "font.name-list.", "intl.accept_languages", // hmmmm...
156 "browser.display.use_document_fonts.icon_font_allowlist", nullptr};
158 static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
160 static const char kCJKFallbackOrderPref[] = "font.cjk_pref_fallback_order";
162 // Pref for the list of icon font families that still get to override the
163 // default font from prefs, even when use_document_fonts is disabled.
164 // (This is to enable ligature-based icon fonts to keep working.)
165 static const char kIconFontsPref[] =
166 "browser.display.use_document_fonts.icon_font_allowlist";
168 // xxx - this can probably be eliminated by reworking pref font handling code
169 static const char* gPrefLangNames[] = {
170 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
171 #include "gfxFontPrefLangList.h"
172 #undef FONT_PREF_LANG
175 static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
176 "size of pref lang name array doesn't match pref lang enum size");
178 class gfxFontListPrefObserver final : public nsIObserver {
179 ~gfxFontListPrefObserver() = default;
181 public:
182 NS_DECL_ISUPPORTS
183 NS_DECL_NSIOBSERVER
186 static void FontListPrefChanged(const char* aPref, void* aData = nullptr) {
187 // XXX this could be made to only clear out the cache for the prefs that were
188 // changed but it probably isn't that big a deal.
189 gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
190 gfxPlatformFontList::PlatformFontList()->LoadIconFontOverrideList();
191 gfxFontCache::GetCache()->Flush();
194 static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
196 NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
198 #define LOCALES_CHANGED_TOPIC "intl:system-locales-changed"
200 NS_IMETHODIMP
201 gfxFontListPrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
202 const char16_t* aData) {
203 NS_ASSERTION(!strcmp(aTopic, LOCALES_CHANGED_TOPIC), "invalid topic");
204 FontListPrefChanged(nullptr);
206 if (XRE_IsParentProcess()) {
207 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::No);
209 return NS_OK;
212 MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
214 NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
216 NS_IMETHODIMP
217 gfxPlatformFontList::MemoryReporter::CollectReports(
218 nsIHandleReportCallback* aHandleReport, nsISupports* aData,
219 bool aAnonymize) {
220 FontListSizes sizes;
221 sizes.mFontListSize = 0;
222 sizes.mFontTableCacheSize = 0;
223 sizes.mCharMapsSize = 0;
224 sizes.mLoaderSize = 0;
225 sizes.mSharedSize = 0;
227 gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(
228 &FontListMallocSizeOf, &sizes);
230 MOZ_COLLECT_REPORT(
231 "explicit/gfx/font-list", KIND_HEAP, UNITS_BYTES, sizes.mFontListSize,
232 "Memory used to manage the list of font families and faces.");
234 MOZ_COLLECT_REPORT(
235 "explicit/gfx/font-charmaps", KIND_HEAP, UNITS_BYTES, sizes.mCharMapsSize,
236 "Memory used to record the character coverage of individual fonts.");
238 if (sizes.mFontTableCacheSize) {
239 MOZ_COLLECT_REPORT(
240 "explicit/gfx/font-tables", KIND_HEAP, UNITS_BYTES,
241 sizes.mFontTableCacheSize,
242 "Memory used for cached font metrics and layout tables.");
245 if (sizes.mLoaderSize) {
246 MOZ_COLLECT_REPORT("explicit/gfx/font-loader", KIND_HEAP, UNITS_BYTES,
247 sizes.mLoaderSize,
248 "Memory used for (platform-specific) font loader.");
251 if (sizes.mSharedSize) {
252 MOZ_COLLECT_REPORT(
253 "font-list-shmem", KIND_NONHEAP, UNITS_BYTES, sizes.mSharedSize,
254 "Shared memory for system font list and character coverage data.");
257 return NS_OK;
260 PRThread* gfxPlatformFontList::sInitFontListThread = nullptr;
262 static void InitFontListCallback(void* aFontList) {
263 AUTO_PROFILER_REGISTER_THREAD("InitFontList");
264 PR_SetCurrentThreadName("InitFontList");
266 if (!static_cast<gfxPlatformFontList*>(aFontList)->InitFontList()) {
267 gfxPlatformFontList::Shutdown();
271 /* static */
272 bool gfxPlatformFontList::Initialize(gfxPlatformFontList* aList) {
273 sPlatformFontList = aList;
274 if (XRE_IsParentProcess() &&
275 StaticPrefs::gfx_font_list_omt_enabled_AtStartup() &&
276 StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
277 !gfxPlatform::InSafeMode()) {
278 sInitFontListThread = PR_CreateThread(
279 PR_USER_THREAD, InitFontListCallback, aList, PR_PRIORITY_NORMAL,
280 PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
281 return true;
283 if (aList->InitFontList()) {
284 return true;
286 Shutdown();
287 return false;
290 gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
291 : mLock("gfxPlatformFontList lock"),
292 mFontFamilies(64),
293 mOtherFamilyNames(16),
294 mSharedCmaps(8) {
295 if (aNeedFullnamePostscriptNames) {
296 mExtraNames = MakeUnique<ExtraNames>();
299 mLangService = nsLanguageAtomService::GetService();
301 LoadBadUnderlineList();
302 LoadIconFontOverrideList();
304 mFontPrefs = MakeUnique<FontPrefs>();
306 gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
307 mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
309 // pref changes notification setup
310 NS_ASSERTION(!gFontListPrefObserver,
311 "There has been font list pref observer already");
312 gFontListPrefObserver = new gfxFontListPrefObserver();
313 NS_ADDREF(gFontListPrefObserver);
315 Preferences::RegisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
317 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
318 if (obs) {
319 obs->AddObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC, false);
322 // Only the parent process listens for whitelist changes; it will then
323 // notify its children to rebuild their font lists.
324 if (XRE_IsParentProcess()) {
325 Preferences::RegisterCallback(FontWhitelistPrefChanged,
326 kFontSystemWhitelistPref);
329 RegisterStrongMemoryReporter(new MemoryReporter());
331 // initialize lang group pref font defaults (i.e. serif/sans-serif)
332 mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
333 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
334 nsAutoCString prefDefaultFontType("font.default.");
335 prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
336 nsAutoCString serifOrSans;
337 Preferences::GetCString(prefDefaultFontType.get(), serifOrSans);
338 if (serifOrSans.EqualsLiteral("sans-serif")) {
339 mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::SansSerif;
340 } else {
341 mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::Serif;
346 gfxPlatformFontList::~gfxPlatformFontList() {
347 // Note that gfxPlatformFontList::Shutdown() ensures that the init-font-list
348 // thread is finished before we come here.
350 AutoLock lock(mLock);
352 // We can't just do mSharedCmaps.Clear() here because removing each item from
353 // the table would drop its last reference, and its Release() method would
354 // then call back to MaybeRemoveCmap to search for it, which we can't do
355 // while in the middle of clearing the table.
356 // So we first clear the "shared" flag in each entry, so Release() won't try
357 // to re-find them in the table.
358 for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
359 iter.Get()->mCharMap->ClearSharedFlag();
361 mSharedCmaps.Clear();
363 ClearLangGroupPrefFontsLocked();
365 NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
367 Preferences::UnregisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
369 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
370 if (obs) {
371 obs->RemoveObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC);
374 if (XRE_IsParentProcess()) {
375 Preferences::UnregisterCallback(FontWhitelistPrefChanged,
376 kFontSystemWhitelistPref);
378 NS_RELEASE(gFontListPrefObserver);
381 void gfxPlatformFontList::GetMissingFonts(nsCString& aMissingFonts) {
382 AutoLock lock(mLock);
384 auto fontLists = GetFilteredPlatformFontLists();
386 if (!fontLists.Length()) {
387 aMissingFonts.Append("No font list available for this device.");
390 for (unsigned int i = 0; i < fontLists.Length(); i++) {
391 for (unsigned int j = 0; j < fontLists[i].second; j++) {
392 nsCString key(fontLists[i].first[j]);
393 GenerateFontListKey(key);
395 if (SharedFontList()) {
396 fontlist::Family* family = SharedFontList()->FindFamily(key);
397 if (!family) {
398 aMissingFonts.Append(fontLists[i].first[j]);
399 aMissingFonts.Append("|");
401 } else {
402 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
403 if (!familyEntry) {
404 familyEntry = mOtherFamilyNames.GetWeak(key);
406 if (!familyEntry) {
407 aMissingFonts.Append(fontLists[i].first[j]);
408 aMissingFonts.Append("|");
415 /* static */
416 void gfxPlatformFontList::FontWhitelistPrefChanged(const char* aPref,
417 void* aClosure) {
418 MOZ_ASSERT(XRE_IsParentProcess());
419 auto* pfl = gfxPlatformFontList::PlatformFontList();
420 pfl->UpdateFontList(true);
421 dom::ContentParent::NotifyUpdatedFonts(true);
424 void gfxPlatformFontList::ApplyWhitelist() {
425 uint32_t numFonts = mEnabledFontsList.Length();
426 if (!mFontFamilyWhitelistActive) {
427 return;
429 nsTHashSet<nsCString> familyNamesWhitelist;
430 for (uint32_t i = 0; i < numFonts; i++) {
431 nsAutoCString key;
432 ToLowerCase(mEnabledFontsList[i], key);
433 familyNamesWhitelist.Insert(key);
435 AutoTArray<RefPtr<gfxFontFamily>, 128> accepted;
436 bool whitelistedFontFound = false;
437 for (const auto& entry : mFontFamilies) {
438 if (entry.GetData()->IsHidden()) {
439 // Hidden system fonts are exempt from whitelisting, but don't count
440 // towards determining whether we "kept" any (user-visible) fonts
441 accepted.AppendElement(entry.GetData());
442 continue;
444 nsAutoCString fontFamilyName(entry.GetKey());
445 ToLowerCase(fontFamilyName);
446 if (familyNamesWhitelist.Contains(fontFamilyName)) {
447 accepted.AppendElement(entry.GetData());
448 whitelistedFontFound = true;
451 if (!whitelistedFontFound) {
452 // No whitelisted fonts found! Ignore the whitelist.
453 return;
455 // Replace the original full list with the accepted subset.
456 mFontFamilies.Clear();
457 for (auto& f : accepted) {
458 nsAutoCString fontFamilyName(f->Name());
459 ToLowerCase(fontFamilyName);
460 mFontFamilies.InsertOrUpdate(fontFamilyName, std::move(f));
464 void gfxPlatformFontList::ApplyWhitelist(
465 nsTArray<fontlist::Family::InitData>& aFamilies) {
466 mLock.AssertCurrentThreadIn();
467 if (!mFontFamilyWhitelistActive) {
468 return;
470 nsTHashSet<nsCString> familyNamesWhitelist;
471 for (const auto& item : mEnabledFontsList) {
472 nsAutoCString key;
473 ToLowerCase(item, key);
474 familyNamesWhitelist.Insert(key);
476 AutoTArray<fontlist::Family::InitData, 128> accepted;
477 bool keptNonHidden = false;
478 for (auto& f : aFamilies) {
479 if (f.mVisibility == FontVisibility::Hidden ||
480 familyNamesWhitelist.Contains(f.mKey)) {
481 accepted.AppendElement(f);
482 if (f.mVisibility != FontVisibility::Hidden) {
483 keptNonHidden = true;
487 if (!keptNonHidden) {
488 // No (visible) families were whitelisted: ignore the whitelist
489 // and just leave the fontlist unchanged.
490 return;
492 aFamilies = std::move(accepted);
495 bool gfxPlatformFontList::FamilyInList(const nsACString& aName,
496 const char* aList[], size_t aCount) {
497 size_t result;
498 return BinarySearchIf(
499 aList, 0, aCount,
500 [&](const char* const aVal) -> int {
501 return nsCaseInsensitiveUTF8StringComparator(
502 aName.BeginReading(), aVal, aName.Length(), strlen(aVal));
504 &result);
507 void gfxPlatformFontList::CheckFamilyList(const char* aList[], size_t aCount) {
508 #ifdef DEBUG
509 MOZ_ASSERT(aCount > 0, "empty font family list?");
510 const char* a = aList[0];
511 uint32_t aLen = strlen(a);
512 for (size_t i = 1; i < aCount; ++i) {
513 const char* b = aList[i];
514 uint32_t bLen = strlen(b);
515 if (nsCaseInsensitiveUTF8StringComparator(a, b, aLen, bLen) >= 0) {
516 MOZ_CRASH_UNSAFE_PRINTF("incorrectly sorted font family list: %s >= %s",
517 a, b);
519 a = b;
520 aLen = bLen;
522 #endif
525 bool gfxPlatformFontList::AddWithLegacyFamilyName(const nsACString& aLegacyName,
526 gfxFontEntry* aFontEntry,
527 FontVisibility aVisibility) {
528 mLock.AssertCurrentThreadIn();
529 bool added = false;
530 nsAutoCString key;
531 ToLowerCase(aLegacyName, key);
532 mOtherFamilyNames
533 .LookupOrInsertWith(key,
534 [&] {
535 RefPtr<gfxFontFamily> family =
536 CreateFontFamily(aLegacyName, aVisibility);
537 // We don't want the family to search for faces,
538 // we're adding them directly here.
539 family->SetHasStyles(true);
540 // And we don't want it to attempt to search for
541 // legacy names, because we've already done that
542 // (and this is the result).
543 family->SetCheckedForLegacyFamilyNames(true);
544 added = true;
545 return family;
547 ->AddFontEntry(aFontEntry->Clone());
548 return added;
551 bool gfxPlatformFontList::InitFontList() {
552 // If the startup font-list-init thread is still running, we need to wait
553 // for it to finish before trying to reinitialize here.
554 if (sInitFontListThread && !IsInitFontListThread()) {
555 PR_JoinThread(sInitFontListThread);
556 sInitFontListThread = nullptr;
559 AutoLock lock(mLock);
561 if (LOG_FONTINIT_ENABLED()) {
562 LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
565 if (IsInitialized()) {
566 // Font-list reinitialization always occurs on the main thread, in response
567 // to a change notification; it's only the initial creation during startup
568 // that may be on another thread.
569 MOZ_ASSERT(NS_IsMainThread());
571 // Rebuilding fontlist so clear out font/word caches.
572 gfxFontCache* fontCache = gfxFontCache::GetCache();
573 if (fontCache) {
574 fontCache->FlushShapedWordCaches();
575 fontCache->Flush();
578 gfxPlatform::PurgeSkiaFontCache();
580 // There's no need to broadcast this reflow request to child processes, as
581 // ContentParent::NotifyUpdatedFonts deals with it by re-entering into this
582 // function on child processes.
583 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::Yes,
584 gfxPlatform::BroadcastToChildren::No);
586 mAliasTable.Clear();
587 mLocalNameTable.Clear();
588 mIconFontsSet.Clear();
590 CancelLoadCmapsTask();
591 mStartedLoadingCmapsFrom = 0xffffffffu;
593 CancelInitOtherFamilyNamesTask();
594 mFontFamilies.Clear();
595 mOtherFamilyNames.Clear();
596 mOtherFamilyNamesInitialized = false;
598 if (mExtraNames) {
599 mExtraNames->mFullnames.Clear();
600 mExtraNames->mPostscriptNames.Clear();
602 mFaceNameListsInitialized = false;
603 ClearLangGroupPrefFontsLocked();
604 CancelLoader();
606 // Clear cached family records that will no longer be valid.
607 for (auto& f : mReplacementCharFallbackFamily) {
608 f = FontFamily();
611 gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
612 mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
614 LoadIconFontOverrideList();
617 // From here, gfxPlatformFontList::IsInitialized will return true,
618 // unless InitFontListForPlatform() fails and we reset it below.
619 mFontlistInitCount++;
621 InitializeCodepointsWithNoFonts();
623 // Try to initialize the cross-process shared font list if enabled by prefs,
624 // but not if we're running in Safe Mode.
625 if (StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
626 !gfxPlatform::InSafeMode()) {
627 for (const auto& entry : mFontEntries.Values()) {
628 if (!entry) {
629 continue;
631 AutoWriteLock lock(entry->mLock);
632 entry->mShmemCharacterMap = nullptr;
633 entry->mShmemFace = nullptr;
634 entry->mFamilyName.Truncate();
636 mFontEntries.Clear();
637 mShmemCharMaps.Clear();
638 bool oldSharedList = mSharedFontList != nullptr;
639 mSharedFontList.reset(new fontlist::FontList(mFontlistInitCount));
640 InitSharedFontListForPlatform();
641 if (mSharedFontList && mSharedFontList->Initialized()) {
642 if (mLocalNameTable.Count()) {
643 SharedFontList()->SetLocalNames(mLocalNameTable);
644 mLocalNameTable.Clear();
646 } else {
647 // something went wrong, fall back to in-process list
648 gfxCriticalNote << "Failed to initialize shared font list, "
649 "falling back to in-process list.";
650 mSharedFontList.reset(nullptr);
652 if (oldSharedList && XRE_IsParentProcess()) {
653 // notify all children of the change
654 if (NS_IsMainThread()) {
655 dom::ContentParent::NotifyUpdatedFonts(true);
656 } else {
657 NS_DispatchToMainThread(NS_NewRunnableFunction(
658 "NotifyUpdatedFonts callback",
659 [] { dom::ContentParent::NotifyUpdatedFonts(true); }));
664 if (!SharedFontList()) {
665 if (NS_FAILED(InitFontListForPlatform())) {
666 mFontlistInitCount = 0;
667 return false;
669 ApplyWhitelist();
672 // Set up mDefaultFontEntry as a "last resort" default that we can use
673 // to avoid crashing if the font list is otherwise unusable.
674 gfxFontStyle defStyle;
675 FontFamily fam = GetDefaultFontLocked(nullptr, &defStyle);
676 gfxFontEntry* fe;
677 if (fam.mShared) {
678 auto face = fam.mShared->FindFaceForStyle(SharedFontList(), defStyle);
679 fe = face ? GetOrCreateFontEntryLocked(face, fam.mShared) : nullptr;
680 } else {
681 fe = fam.mUnshared->FindFontForStyle(defStyle);
683 mDefaultFontEntry = fe;
685 return true;
688 void gfxPlatformFontList::LoadIconFontOverrideList() {
689 mIconFontsSet.Clear();
690 AutoTArray<nsCString, 20> iconFontsList;
691 gfxFontUtils::GetPrefsFontList(kIconFontsPref, iconFontsList);
692 for (auto& name : iconFontsList) {
693 ToLowerCase(name);
694 mIconFontsSet.Insert(name);
698 void gfxPlatformFontList::InitializeCodepointsWithNoFonts() {
699 auto& first = mCodepointsWithNoFonts[FontVisibility(0)];
700 for (auto& bitset : mCodepointsWithNoFonts) {
701 if (&bitset == &first) {
702 bitset.reset();
703 bitset.SetRange(0, 0x1f); // C0 controls
704 bitset.SetRange(0x7f, 0x9f); // C1 controls
705 bitset.SetRange(0xE000, 0xF8FF); // PUA
706 bitset.SetRange(0xF0000, 0x10FFFD); // Supplementary PUA
707 bitset.SetRange(0xfdd0, 0xfdef); // noncharacters
708 for (unsigned i = 0; i <= 0x100000; i += 0x10000) {
709 bitset.SetRange(i + 0xfffe, i + 0xffff); // noncharacters
711 bitset.Compact();
712 } else {
713 bitset = first;
718 void gfxPlatformFontList::FontListChanged() {
719 MOZ_ASSERT(!XRE_IsParentProcess());
720 AutoLock lock(mLock);
721 InitializeCodepointsWithNoFonts();
722 if (SharedFontList()) {
723 // If we're using a shared local face-name list, this may have changed
724 // such that existing font entries held by user font sets are no longer
725 // safe to use: ensure they all get flushed.
726 RebuildLocalFonts(/*aForgetLocalFaces*/ true);
728 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::Yes);
731 void gfxPlatformFontList::GenerateFontListKey(const nsACString& aKeyName,
732 nsACString& aResult) {
733 aResult = aKeyName;
734 ToLowerCase(aResult);
737 void gfxPlatformFontList::GenerateFontListKey(nsACString& aKeyName) {
738 ToLowerCase(aKeyName);
741 // Used if a stylo thread wants to trigger InitOtherFamilyNames in the main
742 // process: we can't do IPC from the stylo thread so we post this to the main
743 // thread instead.
744 class InitOtherFamilyNamesForStylo : public mozilla::Runnable {
745 public:
746 explicit InitOtherFamilyNamesForStylo(bool aDeferOtherFamilyNamesLoading)
747 : Runnable("gfxPlatformFontList::InitOtherFamilyNamesForStylo"),
748 mDefer(aDeferOtherFamilyNamesLoading) {}
750 NS_IMETHOD Run() override {
751 auto pfl = gfxPlatformFontList::PlatformFontList();
752 auto list = pfl->SharedFontList();
753 if (!list) {
754 return NS_OK;
756 bool initialized = false;
757 dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
758 list->GetGeneration(), mDefer, &initialized);
759 pfl->mOtherFamilyNamesInitialized.compareExchange(false, initialized);
760 return NS_OK;
763 private:
764 bool mDefer;
767 #define OTHERNAMES_TIMEOUT 200
769 bool gfxPlatformFontList::InitOtherFamilyNames(
770 bool aDeferOtherFamilyNamesLoading) {
771 if (mOtherFamilyNamesInitialized) {
772 return true;
775 if (SharedFontList() && !XRE_IsParentProcess()) {
776 if (NS_IsMainThread()) {
777 bool initialized;
778 dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
779 SharedFontList()->GetGeneration(), aDeferOtherFamilyNamesLoading,
780 &initialized);
781 mOtherFamilyNamesInitialized.compareExchange(false, initialized);
782 } else {
783 NS_DispatchToMainThread(
784 new InitOtherFamilyNamesForStylo(aDeferOtherFamilyNamesLoading));
786 return mOtherFamilyNamesInitialized;
789 // If the font loader delay has been set to zero, we don't defer loading
790 // additional family names (regardless of the aDefer... parameter), as we
791 // take this to mean availability of font info is to be prioritized over
792 // potential startup perf or main-thread jank.
793 // (This is used so we can reliably run reftests that depend on localized
794 // font-family names being available.)
795 if (aDeferOtherFamilyNamesLoading &&
796 StaticPrefs::gfx_font_loader_delay() > 0) {
797 if (!mPendingOtherFamilyNameTask) {
798 RefPtr<mozilla::CancelableRunnable> task =
799 new InitOtherFamilyNamesRunnable();
800 mPendingOtherFamilyNameTask = task;
801 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
803 } else {
804 InitOtherFamilyNamesInternal(false);
806 return mOtherFamilyNamesInitialized;
809 // time limit for loading facename lists (ms)
810 #define NAMELIST_TIMEOUT 200
812 gfxFontEntry* gfxPlatformFontList::SearchFamiliesForFaceName(
813 const nsACString& aFaceName) {
814 TimeStamp start = TimeStamp::Now();
815 bool timedOut = false;
816 // if mFirstChar is not 0, only load facenames for families
817 // that start with this character
818 char16_t firstChar = 0;
819 gfxFontEntry* lookup = nullptr;
821 // iterate over familes starting with the same letter
822 firstChar = ToLowerCase(aFaceName.CharAt(0));
824 for (const auto& entry : mFontFamilies) {
825 nsCStringHashKey::KeyType key = entry.GetKey();
826 const RefPtr<gfxFontFamily>& family = entry.GetData();
828 // when filtering, skip names that don't start with the filter character
829 if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
830 continue;
833 family->ReadFaceNames(this, NeedFullnamePostscriptNames());
835 TimeDuration elapsed = TimeStamp::Now() - start;
836 if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
837 timedOut = true;
838 break;
842 lookup = FindFaceName(aFaceName);
844 TimeStamp end = TimeStamp::Now();
845 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS, start,
846 end);
847 if (LOG_FONTINIT_ENABLED()) {
848 TimeDuration elapsed = end - start;
849 LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
850 elapsed.ToMilliseconds(), (lookup ? "found name" : ""),
851 (timedOut ? "timeout" : "")));
854 return lookup;
857 gfxFontEntry* gfxPlatformFontList::FindFaceName(const nsACString& aFaceName) {
858 gfxFontEntry* lookup;
860 // lookup in name lookup tables, return null if not found
861 if (mExtraNames &&
862 ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
863 (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
864 return lookup;
867 return nullptr;
870 gfxFontEntry* gfxPlatformFontList::LookupInFaceNameLists(
871 const nsACString& aFaceName) {
872 gfxFontEntry* lookup = nullptr;
874 // initialize facename lookup tables if needed
875 // note: this can terminate early or time out, in which case
876 // mFaceNameListsInitialized remains false
877 if (!mFaceNameListsInitialized) {
878 lookup = SearchFamiliesForFaceName(aFaceName);
879 if (lookup) {
880 return lookup;
884 // lookup in name lookup tables, return null if not found
885 if (!(lookup = FindFaceName(aFaceName))) {
886 // names not completely initialized, so keep track of lookup misses
887 if (!mFaceNameListsInitialized) {
888 if (!mFaceNamesMissed) {
889 mFaceNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
891 mFaceNamesMissed->Insert(aFaceName);
895 return lookup;
898 gfxFontEntry* gfxPlatformFontList::LookupInSharedFaceNameList(
899 nsPresContext* aPresContext, const nsACString& aFaceName,
900 WeightRange aWeightForEntry, StretchRange aStretchForEntry,
901 SlantStyleRange aStyleForEntry) {
902 nsAutoCString keyName(aFaceName);
903 ToLowerCase(keyName);
904 fontlist::FontList* list = SharedFontList();
905 fontlist::Family* family = nullptr;
906 fontlist::Face* face = nullptr;
907 if (list->NumLocalFaces()) {
908 fontlist::LocalFaceRec* rec = list->FindLocalFace(keyName);
909 if (rec) {
910 auto* families = list->Families();
911 if (families) {
912 family = &families[rec->mFamilyIndex];
913 face = family->Faces(list)[rec->mFaceIndex].ToPtr<fontlist::Face>(list);
916 } else {
917 list->SearchForLocalFace(keyName, &family, &face);
919 if (!face || !family) {
920 return nullptr;
922 FontVisibility level =
923 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
924 if (!IsVisibleToCSS(*family, level)) {
925 if (aPresContext) {
926 aPresContext->ReportBlockedFontFamily(*family);
928 return nullptr;
930 gfxFontEntry* fe = CreateFontEntry(face, family);
931 if (fe) {
932 fe->mIsLocalUserFont = true;
933 fe->mWeightRange = aWeightForEntry;
934 fe->mStretchRange = aStretchForEntry;
935 fe->mStyleRange = aStyleForEntry;
937 return fe;
940 void gfxPlatformFontList::LoadBadUnderlineList() {
941 gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset",
942 mBadUnderlineFamilyNames);
943 for (auto& fam : mBadUnderlineFamilyNames) {
944 ToLowerCase(fam);
946 mBadUnderlineFamilyNames.Compact();
947 mBadUnderlineFamilyNames.Sort();
950 void gfxPlatformFontList::UpdateFontList(bool aFullRebuild) {
951 MOZ_ASSERT(NS_IsMainThread());
952 if (aFullRebuild) {
953 InitFontList();
954 AutoLock lock(mLock);
955 RebuildLocalFonts();
956 } else {
957 // The font list isn't being fully rebuilt, we're just being notified that
958 // character maps have been updated and so font fallback needs to be re-
959 // done. We only care about this if we have previously encountered a
960 // fallback that required cmaps that were not yet available, and so we
961 // asked for the async cmap loader to run.
962 AutoLock lock(mLock);
963 if (mStartedLoadingCmapsFrom != 0xffffffffu) {
964 InitializeCodepointsWithNoFonts();
965 mStartedLoadingCmapsFrom = 0xffffffffu;
966 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::No);
971 bool gfxPlatformFontList::IsVisibleToCSS(const gfxFontFamily& aFamily,
972 FontVisibility aVisibility) const {
973 return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
976 bool gfxPlatformFontList::IsVisibleToCSS(const fontlist::Family& aFamily,
977 FontVisibility aVisibility) const {
978 return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
981 void gfxPlatformFontList::GetFontList(nsAtom* aLangGroup,
982 const nsACString& aGenericFamily,
983 nsTArray<nsString>& aListOfFonts) {
984 AutoLock lock(mLock);
986 if (SharedFontList()) {
987 fontlist::FontList* list = SharedFontList();
988 const fontlist::Family* families = list->Families();
989 if (families) {
990 for (uint32_t i = 0; i < list->NumFamilies(); i++) {
991 auto& f = families[i];
992 if (!IsVisibleToCSS(f, FontVisibility::User) || f.IsAltLocaleFamily()) {
993 continue;
995 // XXX TODO: filter families for aGenericFamily, if supported by
996 // platform
997 aListOfFonts.AppendElement(
998 NS_ConvertUTF8toUTF16(list->LocalizedFamilyName(&f)));
1001 return;
1004 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
1005 if (!IsVisibleToCSS(*family, FontVisibility::User)) {
1006 continue;
1008 if (family->FilterForFontList(aLangGroup, aGenericFamily)) {
1009 nsAutoCString localizedFamilyName;
1010 family->LocalizedName(localizedFamilyName);
1011 aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(localizedFamilyName));
1015 aListOfFonts.Sort();
1016 aListOfFonts.Compact();
1019 void gfxPlatformFontList::GetFontFamilyList(
1020 nsTArray<RefPtr<gfxFontFamily>>& aFamilyArray) {
1021 AutoLock lock(mLock);
1022 MOZ_ASSERT(aFamilyArray.IsEmpty());
1023 // This doesn't use ToArray, because the caller passes an AutoTArray.
1024 aFamilyArray.SetCapacity(mFontFamilies.Count());
1025 for (const auto& family : mFontFamilies.Values()) {
1026 aFamilyArray.AppendElement(family);
1030 already_AddRefed<gfxFont> gfxPlatformFontList::SystemFindFontForChar(
1031 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
1032 Script aRunScript, eFontPresentation aPresentation,
1033 const gfxFontStyle* aStyle, FontVisibility* aVisibility) {
1034 AutoLock lock(mLock);
1035 FontVisibility level =
1036 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1037 MOZ_ASSERT(!mCodepointsWithNoFonts[level].test(aCh),
1038 "don't call for codepoints already known to be unsupported");
1040 // Try to short-circuit font fallback for U+FFFD, used to represent
1041 // encoding errors: just use cached family from last time U+FFFD was seen.
1042 // This helps speed up pages with lots of encoding errors, binary-as-text,
1043 // etc.
1044 if (aCh == 0xFFFD) {
1045 gfxFontEntry* fontEntry = nullptr;
1046 auto& fallbackFamily = mReplacementCharFallbackFamily[level];
1047 if (fallbackFamily.mShared) {
1048 fontlist::Face* face =
1049 fallbackFamily.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
1050 if (face) {
1051 fontEntry = GetOrCreateFontEntryLocked(face, fallbackFamily.mShared);
1052 *aVisibility = fallbackFamily.mShared->Visibility();
1054 } else if (fallbackFamily.mUnshared) {
1055 fontEntry = fallbackFamily.mUnshared->FindFontForStyle(*aStyle);
1056 *aVisibility = fallbackFamily.mUnshared->Visibility();
1059 // this should never fail, as we must have found U+FFFD in order to set
1060 // mReplacementCharFallbackFamily[...] at all, but better play it safe
1061 if (fontEntry && fontEntry->HasCharacter(aCh)) {
1062 return fontEntry->FindOrMakeFont(aStyle);
1066 TimeStamp start = TimeStamp::Now();
1068 // search commonly available fonts
1069 bool common = true;
1070 FontFamily fallbackFamily;
1071 RefPtr<gfxFont> candidate =
1072 CommonFontFallback(aPresContext, aCh, aNextCh, aRunScript, aPresentation,
1073 aStyle, fallbackFamily);
1074 RefPtr<gfxFont> font;
1075 if (candidate) {
1076 if (aPresentation == eFontPresentation::Any) {
1077 font = std::move(candidate);
1078 } else {
1079 bool hasColorGlyph = candidate->HasColorGlyphFor(aCh, aNextCh);
1080 if (hasColorGlyph == PrefersColor(aPresentation)) {
1081 font = std::move(candidate);
1086 // If we didn't find a common font, or it was not the preferred type (color
1087 // or monochrome), do system-wide fallback (except for specials).
1088 uint32_t cmapCount = 0;
1089 if (!font) {
1090 common = false;
1091 font = GlobalFontFallback(aPresContext, aCh, aNextCh, aRunScript,
1092 aPresentation, aStyle, cmapCount, fallbackFamily);
1093 // If the font we found doesn't match the requested type, and we also found
1094 // a candidate above, prefer that one.
1095 if (font && aPresentation != eFontPresentation::Any && candidate) {
1096 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1097 if (hasColorGlyph != PrefersColor(aPresentation)) {
1098 font = std::move(candidate);
1102 TimeDuration elapsed = TimeStamp::Now() - start;
1104 LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
1106 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
1107 Script script = intl::UnicodeProperties::GetScriptCode(aCh);
1108 MOZ_LOG(log, LogLevel::Warning,
1109 ("(textrun-systemfallback-%s) char: u+%6.6x "
1110 "script: %d match: [%s]"
1111 " time: %dus cmaps: %d\n",
1112 (common ? "common" : "global"), aCh, static_cast<int>(script),
1113 (font ? font->GetFontEntry()->Name().get() : "<none>"),
1114 int32_t(elapsed.ToMicroseconds()), cmapCount));
1117 // no match? add to set of non-matching codepoints
1118 if (!font) {
1119 mCodepointsWithNoFonts[level].set(aCh);
1120 } else {
1121 *aVisibility = fallbackFamily.mShared
1122 ? fallbackFamily.mShared->Visibility()
1123 : fallbackFamily.mUnshared->Visibility();
1124 if (aCh == 0xFFFD) {
1125 mReplacementCharFallbackFamily[level] = fallbackFamily;
1129 // track system fallback time
1130 static bool first = true;
1131 int32_t intElapsed =
1132 int32_t(first ? elapsed.ToMilliseconds() : elapsed.ToMicroseconds());
1133 Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST
1134 : Telemetry::SYSTEM_FONT_FALLBACK),
1135 intElapsed);
1136 first = false;
1138 // track the script for which fallback occurred (incremented one make it
1139 // 1-based)
1140 Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT,
1141 int(aRunScript) + 1);
1143 return font.forget();
1146 #define NUM_FALLBACK_FONTS 8
1148 already_AddRefed<gfxFont> gfxPlatformFontList::CommonFontFallback(
1149 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
1150 Script aRunScript, eFontPresentation aPresentation,
1151 const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
1152 AutoTArray<const char*, NUM_FALLBACK_FONTS> defaultFallbacks;
1153 gfxPlatform::GetPlatform()->GetCommonFallbackFonts(
1154 aCh, aRunScript, aPresentation, defaultFallbacks);
1155 GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
1156 FontVisibility level =
1157 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1159 // If a color-emoji presentation is requested, we will check any font found
1160 // to see if it can provide this; if not, we'll remember it as a possible
1161 // candidate but search the remainder of the list for a better choice.
1162 RefPtr<gfxFont> candidateFont;
1163 FontFamily candidateFamily;
1164 auto check = [&](gfxFontEntry* aFontEntry,
1165 FontFamily aFamily) -> already_AddRefed<gfxFont> {
1166 RefPtr<gfxFont> font = aFontEntry->FindOrMakeFont(aMatchStyle);
1167 if (aPresentation < eFontPresentation::EmojiDefault ||
1168 font->HasColorGlyphFor(aCh, aNextCh)) {
1169 aMatchedFamily = aFamily;
1170 return font.forget();
1172 // We want a color glyph but this font only has monochrome; remember it
1173 // (unless we already have a candidate) but continue to search.
1174 if (!candidateFont) {
1175 candidateFont = std::move(font);
1176 candidateFamily = aFamily;
1178 return nullptr;
1181 if (SharedFontList()) {
1182 for (const auto name : defaultFallbacks) {
1183 fontlist::Family* family =
1184 FindSharedFamily(aPresContext, nsDependentCString(name));
1185 if (!family || !IsVisibleToCSS(*family, level)) {
1186 continue;
1188 // XXX(jfkthame) Should we fire the async cmap-loader here, or let it
1189 // always do a potential sync initialization of the family?
1190 family->SearchAllFontsForChar(SharedFontList(), &data);
1191 if (data.mBestMatch) {
1192 RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(family));
1193 if (font) {
1194 return font.forget();
1198 } else {
1199 for (const auto name : defaultFallbacks) {
1200 gfxFontFamily* fallback =
1201 FindFamilyByCanonicalName(nsDependentCString(name));
1202 if (!fallback || !IsVisibleToCSS(*fallback, level)) {
1203 continue;
1205 fallback->FindFontForChar(&data);
1206 if (data.mBestMatch) {
1207 RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(fallback));
1208 if (font) {
1209 return font.forget();
1215 // If we had a candidate that supports the character, but doesn't have the
1216 // desired emoji-style glyph, we'll return it anyhow as nothing better was
1217 // found.
1218 if (candidateFont) {
1219 aMatchedFamily = candidateFamily;
1220 return candidateFont.forget();
1223 return nullptr;
1226 already_AddRefed<gfxFont> gfxPlatformFontList::GlobalFontFallback(
1227 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
1228 Script aRunScript, eFontPresentation aPresentation,
1229 const gfxFontStyle* aMatchStyle, uint32_t& aCmapCount,
1230 FontFamily& aMatchedFamily) {
1231 bool useCmaps = IsFontFamilyWhitelistActive() ||
1232 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1233 FontVisibility level =
1234 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1235 if (!useCmaps) {
1236 // Allow platform-specific fallback code to try and find a usable font
1237 gfxFontEntry* fe = PlatformGlobalFontFallback(aPresContext, aCh, aRunScript,
1238 aMatchStyle, aMatchedFamily);
1239 if (fe) {
1240 if (aMatchedFamily.mShared) {
1241 if (IsVisibleToCSS(*aMatchedFamily.mShared, level)) {
1242 RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle);
1243 if (font) {
1244 if (aPresentation == eFontPresentation::Any) {
1245 return font.forget();
1247 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1248 if (hasColorGlyph == PrefersColor(aPresentation)) {
1249 return font.forget();
1253 } else {
1254 if (IsVisibleToCSS(*aMatchedFamily.mUnshared, level)) {
1255 RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle);
1256 if (font) {
1257 if (aPresentation == eFontPresentation::Any) {
1258 return font.forget();
1260 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1261 if (hasColorGlyph == PrefersColor(aPresentation)) {
1262 return font.forget();
1270 // otherwise, try to find it among local fonts
1271 GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
1272 if (SharedFontList()) {
1273 fontlist::Family* families = SharedFontList()->Families();
1274 if (families) {
1275 for (uint32_t i = 0; i < SharedFontList()->NumFamilies(); i++) {
1276 fontlist::Family& family = families[i];
1277 if (!IsVisibleToCSS(family, level)) {
1278 continue;
1280 if (!family.IsFullyInitialized() &&
1281 StaticPrefs::gfx_font_rendering_fallback_async() &&
1282 !XRE_IsParentProcess()) {
1283 // Start loading all the missing charmaps; but this is async,
1284 // so for now we just continue, ignoring this family.
1285 StartCmapLoadingFromFamily(i);
1286 } else {
1287 family.SearchAllFontsForChar(SharedFontList(), &data);
1288 if (data.mMatchDistance == 0.0) {
1289 // no better style match is possible, so stop searching
1290 break;
1294 if (data.mBestMatch) {
1295 aMatchedFamily = FontFamily(data.mMatchedSharedFamily);
1296 return data.mBestMatch->FindOrMakeFont(aMatchStyle);
1299 } else {
1300 // iterate over all font families to find a font that support the
1301 // character
1302 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
1303 if (!IsVisibleToCSS(*family, level)) {
1304 continue;
1306 // evaluate all fonts in this family for a match
1307 family->FindFontForChar(&data);
1308 if (data.mMatchDistance == 0.0) {
1309 // no better style match is possible, so stop searching
1310 break;
1314 aCmapCount = data.mCmapsTested;
1315 if (data.mBestMatch) {
1316 aMatchedFamily = FontFamily(data.mMatchedFamily);
1317 return data.mBestMatch->FindOrMakeFont(aMatchStyle);
1321 return nullptr;
1324 class StartCmapLoadingRunnable : public mozilla::Runnable {
1325 public:
1326 explicit StartCmapLoadingRunnable(uint32_t aStartIndex)
1327 : Runnable("gfxPlatformFontList::StartCmapLoadingRunnable"),
1328 mStartIndex(aStartIndex) {}
1330 NS_IMETHOD Run() override {
1331 auto* pfl = gfxPlatformFontList::PlatformFontList();
1332 auto* list = pfl->SharedFontList();
1333 if (!list) {
1334 return NS_OK;
1336 if (mStartIndex >= list->NumFamilies()) {
1337 return NS_OK;
1339 if (XRE_IsParentProcess()) {
1340 pfl->StartCmapLoading(list->GetGeneration(), mStartIndex);
1341 } else {
1342 dom::ContentChild::GetSingleton()->SendStartCmapLoading(
1343 list->GetGeneration(), mStartIndex);
1345 return NS_OK;
1348 private:
1349 uint32_t mStartIndex;
1352 void gfxPlatformFontList::StartCmapLoadingFromFamily(uint32_t aStartIndex) {
1353 AutoLock lock(mLock);
1354 if (aStartIndex >= mStartedLoadingCmapsFrom) {
1355 // We already initiated cmap-loading from here or earlier in the list;
1356 // no need to do it again here.
1357 return;
1359 mStartedLoadingCmapsFrom = aStartIndex;
1361 // If we're already on the main thread, don't bother dispatching a runnable
1362 // here to kick off the loading process, just do it directly.
1363 if (NS_IsMainThread()) {
1364 auto* list = SharedFontList();
1365 if (XRE_IsParentProcess()) {
1366 StartCmapLoading(list->GetGeneration(), aStartIndex);
1367 } else {
1368 dom::ContentChild::GetSingleton()->SendStartCmapLoading(
1369 list->GetGeneration(), aStartIndex);
1371 } else {
1372 NS_DispatchToMainThread(new StartCmapLoadingRunnable(aStartIndex));
1376 class LoadCmapsRunnable : public CancelableRunnable {
1377 class WillShutdownObserver : public nsIObserver {
1378 public:
1379 NS_DECL_ISUPPORTS
1380 NS_DECL_NSIOBSERVER
1382 explicit WillShutdownObserver(LoadCmapsRunnable* aRunnable)
1383 : mRunnable(aRunnable) {}
1385 void Remove() {
1386 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1387 if (obs) {
1388 obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
1390 mRunnable = nullptr;
1393 protected:
1394 virtual ~WillShutdownObserver() = default;
1396 LoadCmapsRunnable* mRunnable;
1399 public:
1400 explicit LoadCmapsRunnable(uint32_t aGeneration, uint32_t aFamilyIndex)
1401 : CancelableRunnable("gfxPlatformFontList::LoadCmapsRunnable"),
1402 mGeneration(aGeneration),
1403 mStartIndex(aFamilyIndex),
1404 mIndex(aFamilyIndex) {
1405 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1406 if (obs) {
1407 mObserver = new WillShutdownObserver(this);
1408 obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
1412 virtual ~LoadCmapsRunnable() {
1413 if (mObserver) {
1414 mObserver->Remove();
1418 // Reset the current family index, if the value passed is earlier than our
1419 // original starting position. We don't "reset" if it would move the current
1420 // position forward, or back into the already-scanned range.
1421 // We could optimize further by remembering the current position reached,
1422 // and then skipping ahead from the original start, but it doesn't seem worth
1423 // extra complexity for a task that usually only happens once, and already-
1424 // processed families will be skipped pretty quickly in Run() anyhow.
1425 void MaybeResetIndex(uint32_t aFamilyIndex) {
1426 if (aFamilyIndex < mStartIndex) {
1427 mStartIndex = aFamilyIndex;
1428 mIndex = aFamilyIndex;
1432 nsresult Cancel() override {
1433 mIsCanceled = true;
1434 return NS_OK;
1437 NS_IMETHOD Run() override {
1438 if (mIsCanceled) {
1439 return NS_OK;
1441 auto* pfl = gfxPlatformFontList::PlatformFontList();
1442 auto* list = pfl->SharedFontList();
1443 MOZ_ASSERT(list);
1444 if (!list) {
1445 return NS_OK;
1447 if (mGeneration != list->GetGeneration()) {
1448 return NS_OK;
1450 uint32_t numFamilies = list->NumFamilies();
1451 if (mIndex >= numFamilies) {
1452 return NS_OK;
1454 auto* families = list->Families();
1455 // Skip any families that are already initialized.
1456 while (mIndex < numFamilies && families[mIndex].IsFullyInitialized()) {
1457 ++mIndex;
1459 // Fully process one family, and advance index.
1460 if (mIndex < numFamilies) {
1461 Unused << pfl->InitializeFamily(&families[mIndex], true);
1462 ++mIndex;
1464 // If there are more families to initialize, post ourselves back to the
1465 // idle queue to handle the next one; otherwise we're finished and we need
1466 // to notify content processes to update their rendering.
1467 if (mIndex < numFamilies) {
1468 RefPtr<CancelableRunnable> task = this;
1469 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
1470 } else {
1471 pfl->Lock();
1472 pfl->CancelLoadCmapsTask();
1473 pfl->InitializeCodepointsWithNoFonts();
1474 dom::ContentParent::NotifyUpdatedFonts(false);
1475 pfl->Unlock();
1477 return NS_OK;
1480 private:
1481 uint32_t mGeneration;
1482 uint32_t mStartIndex;
1483 uint32_t mIndex;
1484 bool mIsCanceled = false;
1486 RefPtr<WillShutdownObserver> mObserver;
1489 NS_IMPL_ISUPPORTS(LoadCmapsRunnable::WillShutdownObserver, nsIObserver)
1491 NS_IMETHODIMP
1492 LoadCmapsRunnable::WillShutdownObserver::Observe(nsISupports* aSubject,
1493 const char* aTopic,
1494 const char16_t* aData) {
1495 if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
1496 if (mRunnable) {
1497 mRunnable->Cancel();
1499 } else {
1500 MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
1502 return NS_OK;
1505 void gfxPlatformFontList::StartCmapLoading(uint32_t aGeneration,
1506 uint32_t aStartIndex) {
1507 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
1508 if (aGeneration != SharedFontList()->GetGeneration()) {
1509 return;
1511 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1512 return;
1514 if (mLoadCmapsRunnable) {
1515 // We already have a runnable; just make sure it covers the full range of
1516 // families needed.
1517 static_cast<LoadCmapsRunnable*>(mLoadCmapsRunnable.get())
1518 ->MaybeResetIndex(aStartIndex);
1519 return;
1521 mLoadCmapsRunnable = new LoadCmapsRunnable(aGeneration, aStartIndex);
1522 RefPtr<CancelableRunnable> task = mLoadCmapsRunnable;
1523 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
1526 gfxFontFamily* gfxPlatformFontList::CheckFamily(gfxFontFamily* aFamily) {
1527 if (aFamily && !aFamily->HasStyles()) {
1528 aFamily->FindStyleVariations();
1531 if (aFamily && aFamily->FontListLength() == 0) {
1532 // Failed to load any faces for this family, so discard it.
1533 nsAutoCString key;
1534 GenerateFontListKey(aFamily->Name(), key);
1535 mFontFamilies.Remove(key);
1536 return nullptr;
1539 return aFamily;
1542 bool gfxPlatformFontList::FindAndAddFamilies(
1543 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
1544 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
1545 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1546 gfxFloat aDevToCssSize) {
1547 AutoLock lock(mLock);
1549 #ifdef DEBUG
1550 auto initialLength = aOutput->Length();
1551 #endif
1553 bool didFind =
1554 FindAndAddFamiliesLocked(aPresContext, aGeneric, aFamily, aOutput, aFlags,
1555 aStyle, aLanguage, aDevToCssSize);
1556 #ifdef DEBUG
1557 auto finalLength = aOutput->Length();
1558 // Validate the expectation that the output-array grows if we return true,
1559 // or remains the same (probably empty) if we return false.
1560 MOZ_ASSERT_IF(didFind, finalLength > initialLength);
1561 MOZ_ASSERT_IF(!didFind, finalLength == initialLength);
1562 #endif
1564 return didFind;
1567 bool gfxPlatformFontList::FindAndAddFamiliesLocked(
1568 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
1569 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
1570 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1571 gfxFloat aDevToCssSize) {
1572 nsAutoCString key;
1573 GenerateFontListKey(aFamily, key);
1575 bool allowHidden = bool(aFlags & FindFamiliesFlags::eSearchHiddenFamilies);
1576 FontVisibility visibilityLevel =
1577 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1579 // If this font lookup is the result of resolving a CSS generic (not a direct
1580 // font-family request by the page), and RFP settings allow generics to be
1581 // unrestricted, bump the effective visibility level applied here so as to
1582 // allow user-installed fonts to be used.
1583 if (visibilityLevel < FontVisibility::User &&
1584 aGeneric != StyleGenericFontFamily::None &&
1585 !aPresContext->Document()->ShouldResistFingerprinting(
1586 RFPTarget::FontVisibilityRestrictGenerics)) {
1587 visibilityLevel = FontVisibility::User;
1590 if (SharedFontList()) {
1591 fontlist::Family* family = SharedFontList()->FindFamily(key);
1592 // If not found, and other family names have not yet been initialized,
1593 // initialize the rest of the list and try again. This is done lazily
1594 // since reading name table entries is expensive.
1595 // Although ASCII localized family names are possible they don't occur
1596 // in practice, so avoid pulling in names at startup.
1597 if (!family && !mOtherFamilyNamesInitialized) {
1598 bool triggerLoading = true;
1599 bool mayDefer =
1600 !(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading);
1601 if (IsAscii(key)) {
1602 // If `key` is an ASCII name, only trigger loading if it includes a
1603 // space, and the "base" name (up to the last space) exists as a known
1604 // family, so that this might be a legacy styled-family name.
1605 const char* data = key.BeginReading();
1606 int32_t index = key.Length();
1607 while (--index > 0) {
1608 if (data[index] == ' ') {
1609 break;
1612 if (index <= 0 ||
1613 !SharedFontList()->FindFamily(nsAutoCString(key.get(), index))) {
1614 triggerLoading = false;
1617 if (triggerLoading) {
1618 if (InitOtherFamilyNames(mayDefer)) {
1619 family = SharedFontList()->FindFamily(key);
1622 if (!family && !mOtherFamilyNamesInitialized &&
1623 !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
1624 AddToMissedNames(key);
1627 // Check whether the family we found is actually allowed to be looked up,
1628 // according to current font-visibility prefs.
1629 if (family) {
1630 bool visible = IsVisibleToCSS(*family, visibilityLevel);
1631 if (visible || (allowHidden && family->IsHidden())) {
1632 aOutput->AppendElement(FamilyAndGeneric(family, aGeneric));
1633 return true;
1635 if (aPresContext) {
1636 aPresContext->ReportBlockedFontFamily(*family);
1639 return false;
1642 NS_ASSERTION(mFontFamilies.Count() != 0,
1643 "system font list was not initialized correctly");
1645 auto isBlockedByVisibilityLevel = [=](gfxFontFamily* aFamily) -> bool {
1646 bool visible = IsVisibleToCSS(*aFamily, visibilityLevel);
1647 if (visible || (allowHidden && aFamily->IsHidden())) {
1648 return false;
1650 if (aPresContext) {
1651 aPresContext->ReportBlockedFontFamily(*aFamily);
1653 return true;
1656 // lookup in canonical (i.e. English) family name list
1657 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
1658 if (familyEntry) {
1659 if (isBlockedByVisibilityLevel(familyEntry)) {
1660 return false;
1664 // if not found, lookup in other family names list (mostly localized names)
1665 if (!familyEntry) {
1666 familyEntry = mOtherFamilyNames.GetWeak(key);
1668 if (familyEntry) {
1669 if (isBlockedByVisibilityLevel(familyEntry)) {
1670 return false;
1674 // if still not found and other family names not yet fully initialized,
1675 // initialize the rest of the list and try again. this is done lazily
1676 // since reading name table entries is expensive.
1677 // although ASCII localized family names are possible they don't occur
1678 // in practice so avoid pulling in names at startup
1679 if (!familyEntry && !mOtherFamilyNamesInitialized && !IsAscii(aFamily)) {
1680 InitOtherFamilyNames(
1681 !(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading));
1682 familyEntry = mOtherFamilyNames.GetWeak(key);
1683 if (!familyEntry && !mOtherFamilyNamesInitialized &&
1684 !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
1685 // localized family names load timed out, add name to list of
1686 // names to check after localized names are loaded
1687 AddToMissedNames(key);
1689 if (familyEntry) {
1690 if (isBlockedByVisibilityLevel(familyEntry)) {
1691 return false;
1696 familyEntry = CheckFamily(familyEntry);
1698 // If we failed to find the requested family, check for a space in the
1699 // name; if found, and if the "base" name (up to the last space) exists
1700 // as a family, then this might be a legacy GDI-style family name for
1701 // an additional weight/width. Try searching the faces of the base family
1702 // and create any corresponding legacy families.
1703 if (!familyEntry &&
1704 !(aFlags & FindFamiliesFlags::eNoSearchForLegacyFamilyNames)) {
1705 // We don't have nsAString::RFindChar, so look for a space manually
1706 const char* data = aFamily.BeginReading();
1707 int32_t index = aFamily.Length();
1708 while (--index > 0) {
1709 if (data[index] == ' ') {
1710 break;
1713 if (index > 0) {
1714 gfxFontFamily* base =
1715 FindUnsharedFamily(aPresContext, Substring(aFamily, 0, index),
1716 FindFamiliesFlags::eNoSearchForLegacyFamilyNames);
1717 // If we found the "base" family name, and if it has members with
1718 // legacy names, this will add corresponding font-family entries to
1719 // the mOtherFamilyNames list; then retry the legacy-family search.
1720 if (base && base->CheckForLegacyFamilyNames(this)) {
1721 familyEntry = mOtherFamilyNames.GetWeak(key);
1723 if (familyEntry) {
1724 if (isBlockedByVisibilityLevel(familyEntry)) {
1725 return false;
1731 if (familyEntry) {
1732 aOutput->AppendElement(FamilyAndGeneric(familyEntry, aGeneric));
1733 return true;
1736 return false;
1739 void gfxPlatformFontList::AddToMissedNames(const nsCString& aKey) {
1740 if (!mOtherNamesMissed) {
1741 mOtherNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
1743 mOtherNamesMissed->Insert(aKey);
1746 fontlist::Family* gfxPlatformFontList::FindSharedFamily(
1747 nsPresContext* aPresContext, const nsACString& aFamily,
1748 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1749 gfxFloat aDevToCss) {
1750 if (!SharedFontList()) {
1751 return nullptr;
1753 AutoTArray<FamilyAndGeneric, 1> families;
1754 if (!FindAndAddFamiliesLocked(aPresContext, StyleGenericFontFamily::None,
1755 aFamily, &families, aFlags, aStyle, aLanguage,
1756 aDevToCss) ||
1757 !families[0].mFamily.mShared) {
1758 return nullptr;
1760 fontlist::Family* family = families[0].mFamily.mShared;
1761 if (!family->IsInitialized()) {
1762 if (!InitializeFamily(family)) {
1763 return nullptr;
1766 return family;
1769 class InitializeFamilyRunnable : public mozilla::Runnable {
1770 public:
1771 explicit InitializeFamilyRunnable(uint32_t aFamilyIndex, bool aLoadCmaps)
1772 : Runnable("gfxPlatformFontList::InitializeFamilyRunnable"),
1773 mIndex(aFamilyIndex),
1774 mLoadCmaps(aLoadCmaps) {}
1776 NS_IMETHOD Run() override {
1777 auto list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
1778 if (!list) {
1779 return NS_OK;
1781 if (mIndex >= list->NumFamilies()) {
1782 // Out of range? Maybe the list got reinitialized since this request
1783 // was posted - just ignore it.
1784 return NS_OK;
1786 dom::ContentChild::GetSingleton()->SendInitializeFamily(
1787 list->GetGeneration(), mIndex, mLoadCmaps);
1788 return NS_OK;
1791 private:
1792 uint32_t mIndex;
1793 bool mLoadCmaps;
1796 bool gfxPlatformFontList::InitializeFamily(fontlist::Family* aFamily,
1797 bool aLoadCmaps) {
1798 MOZ_ASSERT(SharedFontList());
1799 auto list = SharedFontList();
1800 if (!XRE_IsParentProcess()) {
1801 auto* families = list->Families();
1802 if (!families) {
1803 return false;
1805 uint32_t index = aFamily - families;
1806 if (index >= list->NumFamilies()) {
1807 return false;
1809 if (NS_IsMainThread()) {
1810 dom::ContentChild::GetSingleton()->SendInitializeFamily(
1811 list->GetGeneration(), index, aLoadCmaps);
1812 } else {
1813 NS_DispatchToMainThread(new InitializeFamilyRunnable(index, aLoadCmaps));
1815 return aFamily->IsInitialized();
1818 if (!aFamily->IsInitialized()) {
1819 // The usual case: we're being asked to populate the face list.
1820 AutoTArray<fontlist::Face::InitData, 16> faceList;
1821 GetFacesInitDataForFamily(aFamily, faceList, aLoadCmaps);
1822 aFamily->AddFaces(list, faceList);
1823 } else {
1824 // The family's face list was already initialized, but if aLoadCmaps is
1825 // true we also want to eagerly load character maps. This is used when a
1826 // child process is doing SearchAllFontsForChar, to have the parent load
1827 // all the cmaps at once and reduce IPC traffic (and content-process file
1828 // access overhead, which is crippling for DirectWrite on Windows).
1829 if (aLoadCmaps) {
1830 auto* faces = aFamily->Faces(list);
1831 if (faces) {
1832 for (size_t i = 0; i < aFamily->NumFaces(); i++) {
1833 auto* face = faces[i].ToPtr<fontlist::Face>(list);
1834 if (face && face->mCharacterMap.IsNull()) {
1835 // We don't want to cache this font entry, as the parent will most
1836 // likely never use it again; it's just to populate the charmap for
1837 // the benefit of the child process.
1838 RefPtr<gfxFontEntry> fe = CreateFontEntry(face, aFamily);
1839 if (fe) {
1840 fe->ReadCMAP();
1848 if (aLoadCmaps && aFamily->IsInitialized()) {
1849 aFamily->SetupFamilyCharMap(list);
1852 return aFamily->IsInitialized();
1855 gfxFontEntry* gfxPlatformFontList::FindFontForFamily(
1856 nsPresContext* aPresContext, const nsACString& aFamily,
1857 const gfxFontStyle* aStyle) {
1858 AutoLock lock(mLock);
1860 nsAutoCString key;
1861 GenerateFontListKey(aFamily, key);
1863 FontFamily family = FindFamily(aPresContext, key);
1864 if (family.IsNull()) {
1865 return nullptr;
1867 if (family.mShared) {
1868 auto face = family.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
1869 if (!face) {
1870 return nullptr;
1872 return GetOrCreateFontEntryLocked(face, family.mShared);
1874 return family.mUnshared->FindFontForStyle(*aStyle);
1877 gfxFontEntry* gfxPlatformFontList::GetOrCreateFontEntryLocked(
1878 fontlist::Face* aFace, const fontlist::Family* aFamily) {
1879 return mFontEntries
1880 .LookupOrInsertWith(aFace,
1881 [=] { return CreateFontEntry(aFace, aFamily); })
1882 .get();
1885 void gfxPlatformFontList::AddOtherFamilyNames(
1886 gfxFontFamily* aFamilyEntry, const nsTArray<nsCString>& aOtherFamilyNames) {
1887 AutoLock lock(mLock);
1889 for (const auto& name : aOtherFamilyNames) {
1890 nsAutoCString key;
1891 GenerateFontListKey(name, key);
1893 mOtherFamilyNames.LookupOrInsertWith(key, [&] {
1894 LOG_FONTLIST(
1895 ("(fontlist-otherfamily) canonical family: %s, other family: %s\n",
1896 aFamilyEntry->Name().get(), name.get()));
1897 if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
1898 aFamilyEntry->SetBadUnderlineFamily();
1900 return RefPtr{aFamilyEntry};
1905 void gfxPlatformFontList::AddFullnameLocked(gfxFontEntry* aFontEntry,
1906 const nsCString& aFullname) {
1907 mExtraNames->mFullnames.LookupOrInsertWith(aFullname, [&] {
1908 LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
1909 aFontEntry->Name().get(), aFullname.get()));
1910 return RefPtr{aFontEntry};
1914 void gfxPlatformFontList::AddPostscriptNameLocked(
1915 gfxFontEntry* aFontEntry, const nsCString& aPostscriptName) {
1916 mExtraNames->mPostscriptNames.LookupOrInsertWith(aPostscriptName, [&] {
1917 LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
1918 aFontEntry->Name().get(), aPostscriptName.get()));
1919 return RefPtr{aFontEntry};
1923 bool gfxPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
1924 nsACString& aFamilyName) {
1925 AutoLock lock(mLock);
1926 FontFamily family = FindFamily(nullptr, aFontName);
1927 if (family.IsNull()) {
1928 return false;
1930 return GetLocalizedFamilyName(family, aFamilyName);
1933 bool gfxPlatformFontList::GetLocalizedFamilyName(const FontFamily& aFamily,
1934 nsACString& aFamilyName) {
1935 if (aFamily.mShared) {
1936 aFamilyName = SharedFontList()->LocalizedFamilyName(aFamily.mShared);
1937 return true;
1939 if (aFamily.mUnshared) {
1940 aFamily.mUnshared->LocalizedName(aFamilyName);
1941 return true;
1943 return false; // leaving the aFamilyName outparam untouched
1946 FamilyAndGeneric gfxPlatformFontList::GetDefaultFontFamily(
1947 const nsACString& aLangGroup, const nsACString& aGenericFamily) {
1948 if (NS_WARN_IF(aLangGroup.IsEmpty()) ||
1949 NS_WARN_IF(aGenericFamily.IsEmpty())) {
1950 return FamilyAndGeneric();
1953 AutoLock lock(mLock);
1955 nsAutoCString value;
1956 AutoTArray<nsCString, 4> names;
1957 if (mFontPrefs->LookupNameList(PrefName(aGenericFamily, aLangGroup), value)) {
1958 gfxFontUtils::ParseFontList(value, names);
1961 for (const nsCString& name : names) {
1962 FontFamily family = FindFamily(nullptr, name);
1963 if (!family.IsNull()) {
1964 return FamilyAndGeneric(family);
1968 return FamilyAndGeneric();
1971 ShmemCharMapHashEntry::ShmemCharMapHashEntry(const gfxSparseBitSet* aCharMap)
1972 : mList(gfxPlatformFontList::PlatformFontList()->SharedFontList()),
1973 mHash(aCharMap->GetChecksum()) {
1974 size_t len = SharedBitSet::RequiredSize(*aCharMap);
1975 mCharMap = mList->Alloc(len);
1976 SharedBitSet::Create(mCharMap.ToPtr(mList, len), len, *aCharMap);
1979 fontlist::Pointer gfxPlatformFontList::GetShmemCharMapLocked(
1980 const gfxSparseBitSet* aCmap) {
1981 auto* entry = mShmemCharMaps.GetEntry(aCmap);
1982 if (!entry) {
1983 entry = mShmemCharMaps.PutEntry(aCmap);
1985 return entry->GetCharMap();
1988 // Lookup aCmap in the shared cmap set, adding if not already present.
1989 // This is the only way for a reference to a gfxCharacterMap to be acquired
1990 // by another thread than its original creator.
1991 already_AddRefed<gfxCharacterMap> gfxPlatformFontList::FindCharMap(
1992 gfxCharacterMap* aCmap) {
1993 // Lock to prevent potentially racing against MaybeRemoveCmap.
1994 AutoLock lock(mLock);
1996 // Find existing entry or insert a new one (which will add a reference).
1997 aCmap->CalcHash();
1998 aCmap->mShared = true; // Set the shared flag in preparation for adding
1999 // to the global table.
2000 RefPtr cmap = mSharedCmaps.PutEntry(aCmap)->GetKey();
2002 // If we ended up finding a different, pre-existing entry, clear the
2003 // shared flag on this one so that it'll get deleted on Release().
2004 if (cmap.get() != aCmap) {
2005 aCmap->mShared = false;
2008 return cmap.forget();
2011 // Potentially remove the charmap from the shared cmap set. This is called
2012 // when a user of the charmap drops a reference and the refcount goes to 1;
2013 // in that case, it is possible our shared set is the only remaining user
2014 // of the object, and we should remove it.
2015 // Note that aCharMap might have already been freed, so we must not try to
2016 // dereference it until we have checked that it's still present in our table.
2017 void gfxPlatformFontList::MaybeRemoveCmap(gfxCharacterMap* aCharMap) {
2018 // Lock so that nobody else can get a reference via FindCharMap while we're
2019 // checking here.
2020 AutoLock lock(mLock);
2022 // Skip lookups during teardown.
2023 if (!mSharedCmaps.Count()) {
2024 return;
2027 // aCharMap needs to match the entry and be the same ptr and still have a
2028 // refcount of exactly 1 (i.e. we hold the only reference) before removing.
2029 // If we're racing another thread, it might already have been removed, in
2030 // which case GetEntry will not find it and we won't try to dereference the
2031 // already-freed pointer.
2032 CharMapHashKey* found =
2033 mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
2034 if (found && found->GetKey() == aCharMap && aCharMap->RefCount() == 1) {
2035 // Forget our reference to the object that's being deleted, without
2036 // calling Release() on it.
2037 Unused << found->mCharMap.forget();
2039 // Do the deletion.
2040 delete aCharMap;
2042 // Log this as a "Release" to keep leak-checking correct.
2043 NS_LOG_RELEASE(aCharMap, 0, "gfxCharacterMap");
2045 mSharedCmaps.RemoveEntry(found);
2049 static void GetSystemUIFontFamilies([[maybe_unused]] nsAtom* aLangGroup,
2050 nsTArray<nsCString>& aFamilies) {
2051 // TODO: On macOS, use CTCreateUIFontForLanguage or such thing (though the
2052 // code below ends up using [NSFont systemFontOfSize: 0.0].
2053 nsFont systemFont;
2054 gfxFontStyle fontStyle;
2055 nsAutoString systemFontName;
2056 if (!LookAndFeel::GetFont(StyleSystemFont::Menu, systemFontName, fontStyle)) {
2057 return;
2059 systemFontName.Trim("\"'");
2060 CopyUTF16toUTF8(systemFontName, *aFamilies.AppendElement());
2063 void gfxPlatformFontList::ResolveGenericFontNames(
2064 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2065 eFontPrefLang aPrefLang, PrefFontList* aGenericFamilies) {
2066 const char* langGroupStr = GetPrefLangName(aPrefLang);
2067 const char* generic = GetGenericName(aGenericType);
2069 if (!generic) {
2070 return;
2073 AutoTArray<nsCString, 4> genericFamilies;
2075 // load family for "font.name.generic.lang"
2076 PrefName prefName(generic, langGroupStr);
2077 nsAutoCString value;
2078 if (mFontPrefs->LookupName(prefName, value)) {
2079 gfxFontUtils::ParseFontList(value, genericFamilies);
2082 // load fonts for "font.name-list.generic.lang"
2083 if (mFontPrefs->LookupNameList(prefName, value)) {
2084 gfxFontUtils::ParseFontList(value, genericFamilies);
2087 nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
2088 MOZ_ASSERT(langGroup, "null lang group for pref lang");
2090 if (aGenericType == StyleGenericFontFamily::SystemUi) {
2091 GetSystemUIFontFamilies(langGroup, genericFamilies);
2094 GetFontFamiliesFromGenericFamilies(
2095 aPresContext, aGenericType, genericFamilies, langGroup, aGenericFamilies);
2097 #if 0 // dump out generic mappings
2098 printf("%s ===> ", NamePref(generic, langGroupStr).get());
2099 for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
2100 if (k > 0) printf(", ");
2101 printf("%s", (*aGenericFamilies)[k].mIsShared
2102 ? (*aGenericFamilies)[k].mShared->DisplayName().AsString(SharedFontList()).get()
2103 : (*aGenericFamilies)[k].mUnshared->Name().get());
2105 printf("\n");
2106 #endif
2109 void gfxPlatformFontList::ResolveEmojiFontNames(
2110 nsPresContext* aPresContext, PrefFontList* aGenericFamilies) {
2111 // emoji preference has no lang name
2112 AutoTArray<nsCString, 4> genericFamilies;
2114 nsAutoCString value;
2115 if (mFontPrefs->LookupNameList(PrefName("emoji", ""), value)) {
2116 gfxFontUtils::ParseFontList(value, genericFamilies);
2119 GetFontFamiliesFromGenericFamilies(
2120 aPresContext, StyleGenericFontFamily::MozEmoji, genericFamilies, nullptr,
2121 aGenericFamilies);
2124 void gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(
2125 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2126 nsTArray<nsCString>& aGenericNameFamilies, nsAtom* aLangGroup,
2127 PrefFontList* aGenericFamilies) {
2128 // lookup and add platform fonts uniquely
2129 for (const nsCString& genericFamily : aGenericNameFamilies) {
2130 AutoTArray<FamilyAndGeneric, 10> families;
2131 FindAndAddFamiliesLocked(aPresContext, aGenericType, genericFamily,
2132 &families, FindFamiliesFlags(0), nullptr,
2133 aLangGroup);
2134 for (const FamilyAndGeneric& f : families) {
2135 if (!aGenericFamilies->Contains(f.mFamily)) {
2136 aGenericFamilies->AppendElement(f.mFamily);
2142 gfxPlatformFontList::PrefFontList*
2143 gfxPlatformFontList::GetPrefFontsLangGroupLocked(
2144 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2145 eFontPrefLang aPrefLang) {
2146 if (aGenericType == StyleGenericFontFamily::MozEmoji ||
2147 aPrefLang == eFontPrefLang_Emoji) {
2148 // Emoji font has no lang
2149 PrefFontList* prefFonts = mEmojiPrefFont.get();
2150 if (MOZ_UNLIKELY(!prefFonts)) {
2151 prefFonts = new PrefFontList;
2152 ResolveEmojiFontNames(aPresContext, prefFonts);
2153 mEmojiPrefFont.reset(prefFonts);
2155 return prefFonts;
2158 auto index = static_cast<size_t>(aGenericType);
2159 PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][index].get();
2160 if (MOZ_UNLIKELY(!prefFonts)) {
2161 prefFonts = new PrefFontList;
2162 ResolveGenericFontNames(aPresContext, aGenericType, aPrefLang, prefFonts);
2163 mLangGroupPrefFonts[aPrefLang][index].reset(prefFonts);
2165 return prefFonts;
2168 void gfxPlatformFontList::AddGenericFonts(
2169 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2170 nsAtom* aLanguage, nsTArray<FamilyAndGeneric>& aFamilyList) {
2171 AutoLock lock(mLock);
2173 // map lang ==> langGroup
2174 nsAtom* langGroup = GetLangGroup(aLanguage);
2176 // langGroup ==> prefLang
2177 eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
2179 // lookup pref fonts
2180 PrefFontList* prefFonts =
2181 GetPrefFontsLangGroupLocked(aPresContext, aGenericType, prefLang);
2183 if (!prefFonts->IsEmpty()) {
2184 aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
2185 for (auto& f : *prefFonts) {
2186 aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
2191 static nsAtom* PrefLangToLangGroups(uint32_t aIndex) {
2192 // static array here avoids static constructor
2193 static nsAtom* gPrefLangToLangGroups[] = {
2194 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
2195 #include "gfxFontPrefLangList.h"
2196 #undef FONT_PREF_LANG
2199 return aIndex < ArrayLength(gPrefLangToLangGroups)
2200 ? gPrefLangToLangGroups[aIndex]
2201 : nsGkAtoms::Unicode;
2204 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(const char* aLang) {
2205 if (!aLang || !aLang[0]) {
2206 return eFontPrefLang_Others;
2208 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
2209 if (!nsCRT::strcasecmp(gPrefLangNames[i], aLang)) {
2210 return eFontPrefLang(i);
2213 return eFontPrefLang_Others;
2216 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(nsAtom* aLang) {
2217 if (!aLang) return eFontPrefLang_Others;
2218 nsAutoCString lang;
2219 aLang->ToUTF8String(lang);
2220 return GetFontPrefLangFor(lang.get());
2223 nsAtom* gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang) {
2224 // the special CJK set pref lang should be resolved into separate
2225 // calls to individual CJK pref langs before getting here
2226 NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
2228 return PrefLangToLangGroups(uint32_t(aLang));
2231 const char* gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang) {
2232 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
2233 return gPrefLangNames[uint32_t(aLang)];
2235 return nullptr;
2238 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(uint32_t aCh) {
2239 switch (ublock_getCode(aCh)) {
2240 case UBLOCK_BASIC_LATIN:
2241 case UBLOCK_LATIN_1_SUPPLEMENT:
2242 case UBLOCK_LATIN_EXTENDED_A:
2243 case UBLOCK_LATIN_EXTENDED_B:
2244 case UBLOCK_IPA_EXTENSIONS:
2245 case UBLOCK_SPACING_MODIFIER_LETTERS:
2246 case UBLOCK_LATIN_EXTENDED_ADDITIONAL:
2247 case UBLOCK_LATIN_EXTENDED_C:
2248 case UBLOCK_LATIN_EXTENDED_D:
2249 case UBLOCK_LATIN_EXTENDED_E:
2250 case UBLOCK_PHONETIC_EXTENSIONS:
2251 return eFontPrefLang_Western;
2252 case UBLOCK_GREEK:
2253 case UBLOCK_GREEK_EXTENDED:
2254 return eFontPrefLang_Greek;
2255 case UBLOCK_CYRILLIC:
2256 case UBLOCK_CYRILLIC_SUPPLEMENT:
2257 case UBLOCK_CYRILLIC_EXTENDED_A:
2258 case UBLOCK_CYRILLIC_EXTENDED_B:
2259 case UBLOCK_CYRILLIC_EXTENDED_C:
2260 return eFontPrefLang_Cyrillic;
2261 case UBLOCK_ARMENIAN:
2262 return eFontPrefLang_Armenian;
2263 case UBLOCK_HEBREW:
2264 return eFontPrefLang_Hebrew;
2265 case UBLOCK_ARABIC:
2266 case UBLOCK_ARABIC_PRESENTATION_FORMS_A:
2267 case UBLOCK_ARABIC_PRESENTATION_FORMS_B:
2268 case UBLOCK_ARABIC_SUPPLEMENT:
2269 case UBLOCK_ARABIC_EXTENDED_A:
2270 case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS:
2271 return eFontPrefLang_Arabic;
2272 case UBLOCK_DEVANAGARI:
2273 case UBLOCK_DEVANAGARI_EXTENDED:
2274 return eFontPrefLang_Devanagari;
2275 case UBLOCK_BENGALI:
2276 return eFontPrefLang_Bengali;
2277 case UBLOCK_GURMUKHI:
2278 return eFontPrefLang_Gurmukhi;
2279 case UBLOCK_GUJARATI:
2280 return eFontPrefLang_Gujarati;
2281 case UBLOCK_ORIYA:
2282 return eFontPrefLang_Oriya;
2283 case UBLOCK_TAMIL:
2284 return eFontPrefLang_Tamil;
2285 case UBLOCK_TELUGU:
2286 return eFontPrefLang_Telugu;
2287 case UBLOCK_KANNADA:
2288 return eFontPrefLang_Kannada;
2289 case UBLOCK_MALAYALAM:
2290 return eFontPrefLang_Malayalam;
2291 case UBLOCK_SINHALA:
2292 case UBLOCK_SINHALA_ARCHAIC_NUMBERS:
2293 return eFontPrefLang_Sinhala;
2294 case UBLOCK_THAI:
2295 return eFontPrefLang_Thai;
2296 case UBLOCK_TIBETAN:
2297 return eFontPrefLang_Tibetan;
2298 case UBLOCK_GEORGIAN:
2299 case UBLOCK_GEORGIAN_SUPPLEMENT:
2300 case UBLOCK_GEORGIAN_EXTENDED:
2301 return eFontPrefLang_Georgian;
2302 case UBLOCK_HANGUL_JAMO:
2303 case UBLOCK_HANGUL_COMPATIBILITY_JAMO:
2304 case UBLOCK_HANGUL_SYLLABLES:
2305 case UBLOCK_HANGUL_JAMO_EXTENDED_A:
2306 case UBLOCK_HANGUL_JAMO_EXTENDED_B:
2307 return eFontPrefLang_Korean;
2308 case UBLOCK_ETHIOPIC:
2309 case UBLOCK_ETHIOPIC_EXTENDED:
2310 case UBLOCK_ETHIOPIC_SUPPLEMENT:
2311 case UBLOCK_ETHIOPIC_EXTENDED_A:
2312 return eFontPrefLang_Ethiopic;
2313 case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS:
2314 case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED:
2315 return eFontPrefLang_Canadian;
2316 case UBLOCK_KHMER:
2317 case UBLOCK_KHMER_SYMBOLS:
2318 return eFontPrefLang_Khmer;
2319 case UBLOCK_CJK_RADICALS_SUPPLEMENT:
2320 case UBLOCK_KANGXI_RADICALS:
2321 case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS:
2322 case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
2323 case UBLOCK_HIRAGANA:
2324 case UBLOCK_KATAKANA:
2325 case UBLOCK_BOPOMOFO:
2326 case UBLOCK_KANBUN:
2327 case UBLOCK_BOPOMOFO_EXTENDED:
2328 case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS:
2329 case UBLOCK_CJK_COMPATIBILITY:
2330 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:
2331 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS:
2332 case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS:
2333 case UBLOCK_CJK_COMPATIBILITY_FORMS:
2334 case UBLOCK_SMALL_FORM_VARIANTS:
2335 case UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS:
2336 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:
2337 case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:
2338 case UBLOCK_KATAKANA_PHONETIC_EXTENSIONS:
2339 case UBLOCK_CJK_STROKES:
2340 case UBLOCK_VERTICAL_FORMS:
2341 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C:
2342 case UBLOCK_KANA_SUPPLEMENT:
2343 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D:
2344 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E:
2345 case UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION:
2346 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F:
2347 case UBLOCK_KANA_EXTENDED_A:
2348 return eFontPrefLang_CJKSet;
2349 case UBLOCK_MATHEMATICAL_OPERATORS:
2350 case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS:
2351 case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A:
2352 case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B:
2353 case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS:
2354 return eFontPrefLang_Mathematics;
2355 default:
2356 return eFontPrefLang_Others;
2360 bool gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang) {
2361 switch (aLang) {
2362 case eFontPrefLang_Japanese:
2363 case eFontPrefLang_ChineseTW:
2364 case eFontPrefLang_ChineseCN:
2365 case eFontPrefLang_ChineseHK:
2366 case eFontPrefLang_Korean:
2367 case eFontPrefLang_CJKSet:
2368 return true;
2369 default:
2370 return false;
2374 void gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[],
2375 uint32_t& aLen, eFontPrefLang aCharLang,
2376 eFontPrefLang aPageLang) {
2377 AutoLock lock(mLock);
2378 if (IsLangCJK(aCharLang)) {
2379 AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
2380 } else {
2381 AppendPrefLang(aPrefLangs, aLen, aCharLang);
2384 AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
2387 void gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[],
2388 uint32_t& aLen,
2389 eFontPrefLang aCharLang,
2390 eFontPrefLang aPageLang) {
2391 // prefer the lang specified by the page *if* CJK
2392 if (IsLangCJK(aPageLang)) {
2393 AppendPrefLang(aPrefLangs, aLen, aPageLang);
2396 // if not set up, set up the default CJK order, based on accept lang
2397 // settings and locale
2398 if (mCJKPrefLangs.Length() == 0) {
2399 // temp array
2400 eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
2401 uint32_t tempLen = 0;
2403 // Add the CJK pref fonts from accept languages, the order should be same
2404 // order. We use gfxFontUtils::GetPrefsFontList to read the list even
2405 // though it's not actually a list of fonts but of lang codes; the format
2406 // is the same.
2407 AutoTArray<nsCString, 5> list;
2408 gfxFontUtils::GetPrefsFontList("intl.accept_languages", list, true);
2409 for (const auto& lang : list) {
2410 eFontPrefLang fpl = GetFontPrefLangFor(lang.get());
2411 switch (fpl) {
2412 case eFontPrefLang_Japanese:
2413 case eFontPrefLang_Korean:
2414 case eFontPrefLang_ChineseCN:
2415 case eFontPrefLang_ChineseHK:
2416 case eFontPrefLang_ChineseTW:
2417 AppendPrefLang(tempPrefLangs, tempLen, fpl);
2418 break;
2419 default:
2420 break;
2424 // Try using app's locale
2425 nsAutoCString localeStr;
2426 LocaleService::GetInstance()->GetAppLocaleAsBCP47(localeStr);
2429 Locale locale;
2430 if (LocaleParser::TryParse(localeStr, locale).isOk() &&
2431 locale.Canonicalize().isOk()) {
2432 if (locale.Language().EqualTo("ja")) {
2433 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2434 } else if (locale.Language().EqualTo("zh")) {
2435 if (locale.Region().EqualTo("CN")) {
2436 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2437 } else if (locale.Region().EqualTo("TW")) {
2438 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2439 } else if (locale.Region().EqualTo("HK")) {
2440 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2442 } else if (locale.Language().EqualTo("ko")) {
2443 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2448 // Then add the known CJK prefs in order of system preferred locales
2449 AutoTArray<nsCString, 5> prefLocales;
2450 prefLocales.AppendElement("ja"_ns);
2451 prefLocales.AppendElement("zh-CN"_ns);
2452 prefLocales.AppendElement("zh-TW"_ns);
2453 prefLocales.AppendElement("zh-HK"_ns);
2454 prefLocales.AppendElement("ko"_ns);
2456 AutoTArray<nsCString, 16> sysLocales;
2457 AutoTArray<nsCString, 16> negLocales;
2458 if (NS_SUCCEEDED(
2459 OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) {
2460 LocaleService::GetInstance()->NegotiateLanguages(
2461 sysLocales, prefLocales, ""_ns,
2462 LocaleService::kLangNegStrategyFiltering, negLocales);
2463 for (const auto& localeStr : negLocales) {
2464 Locale locale;
2465 if (LocaleParser::TryParse(localeStr, locale).isOk() &&
2466 locale.Canonicalize().isOk()) {
2467 if (locale.Language().EqualTo("ja")) {
2468 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2469 } else if (locale.Language().EqualTo("zh")) {
2470 if (locale.Region().EqualTo("CN")) {
2471 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2472 } else if (locale.Region().EqualTo("TW")) {
2473 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2474 } else if (locale.Region().EqualTo("HK")) {
2475 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2477 } else if (locale.Language().EqualTo("ko")) {
2478 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2484 // Last resort... set up CJK font prefs in the order listed by the user-
2485 // configurable ordering pref.
2486 gfxFontUtils::GetPrefsFontList(kCJKFallbackOrderPref, list);
2487 for (const auto& item : list) {
2488 eFontPrefLang fpl = GetFontPrefLangFor(item.get());
2489 switch (fpl) {
2490 case eFontPrefLang_Japanese:
2491 case eFontPrefLang_Korean:
2492 case eFontPrefLang_ChineseCN:
2493 case eFontPrefLang_ChineseHK:
2494 case eFontPrefLang_ChineseTW:
2495 AppendPrefLang(tempPrefLangs, tempLen, fpl);
2496 break;
2497 default:
2498 break;
2502 // Truly-last resort... try Chinese font prefs before Japanese because
2503 // they tend to have more complete character coverage, and therefore less
2504 // risk of "ransom-note" effects.
2505 // (If the kCJKFallbackOrderPref was fully populated, as it is by default,
2506 // this will do nothing as all these values are already present.)
2507 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2508 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2509 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2510 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2511 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2513 // copy into the cached array
2514 for (const auto lang : Span<eFontPrefLang>(tempPrefLangs, tempLen)) {
2515 mCJKPrefLangs.AppendElement(lang);
2519 // append in cached CJK langs
2520 for (const auto lang : mCJKPrefLangs) {
2521 AppendPrefLang(aPrefLangs, aLen, eFontPrefLang(lang));
2525 void gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[],
2526 uint32_t& aLen,
2527 eFontPrefLang aAddLang) {
2528 if (aLen >= kMaxLenPrefLangList) {
2529 return;
2532 // If the lang is already present, just ignore the addition.
2533 for (const auto lang : Span<eFontPrefLang>(aPrefLangs, aLen)) {
2534 if (lang == aAddLang) {
2535 return;
2539 aPrefLangs[aLen++] = aAddLang;
2542 StyleGenericFontFamily gfxPlatformFontList::GetDefaultGeneric(
2543 eFontPrefLang aLang) {
2544 if (aLang == eFontPrefLang_Emoji) {
2545 return StyleGenericFontFamily::MozEmoji;
2548 AutoLock lock(mLock);
2550 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
2551 return mDefaultGenericsLangGroup[uint32_t(aLang)];
2553 return StyleGenericFontFamily::Serif;
2556 FontFamily gfxPlatformFontList::GetDefaultFont(nsPresContext* aPresContext,
2557 const gfxFontStyle* aStyle) {
2558 AutoLock lock(mLock);
2559 return GetDefaultFontLocked(aPresContext, aStyle);
2562 FontFamily gfxPlatformFontList::GetDefaultFontLocked(
2563 nsPresContext* aPresContext, const gfxFontStyle* aStyle) {
2564 FontFamily family = GetDefaultFontForPlatform(aPresContext, aStyle);
2565 if (!family.IsNull()) {
2566 return family;
2568 // Something has gone wrong and we were unable to retrieve a default font
2569 // from the platform. (Likely the whitelist has blocked all potential
2570 // default fonts.) As a last resort, we return the first font in our list.
2571 if (SharedFontList()) {
2572 MOZ_RELEASE_ASSERT(SharedFontList()->NumFamilies() > 0);
2573 return FontFamily(SharedFontList()->Families());
2575 MOZ_RELEASE_ASSERT(mFontFamilies.Count() > 0);
2576 return FontFamily(mFontFamilies.ConstIter().Data());
2579 void gfxPlatformFontList::GetFontFamilyNames(
2580 nsTArray<nsCString>& aFontFamilyNames) {
2581 if (SharedFontList()) {
2582 fontlist::FontList* list = SharedFontList();
2583 const fontlist::Family* families = list->Families();
2584 if (families) {
2585 for (uint32_t i = 0, n = list->NumFamilies(); i < n; i++) {
2586 const fontlist::Family& family = families[i];
2587 if (!family.IsHidden()) {
2588 aFontFamilyNames.AppendElement(family.DisplayName().AsString(list));
2592 } else {
2593 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2594 if (!family->IsHidden()) {
2595 aFontFamilyNames.AppendElement(family->Name());
2601 nsAtom* gfxPlatformFontList::GetLangGroup(nsAtom* aLanguage) {
2602 // map lang ==> langGroup
2603 nsAtom* langGroup = nullptr;
2604 if (aLanguage) {
2605 langGroup = mLangService->GetLanguageGroup(aLanguage);
2607 if (!langGroup) {
2608 langGroup = nsGkAtoms::Unicode;
2610 return langGroup;
2613 /* static */ const char* gfxPlatformFontList::GetGenericName(
2614 StyleGenericFontFamily aGenericType) {
2615 // type should be standard generic type at this point
2616 // map generic type to string
2617 switch (aGenericType) {
2618 case StyleGenericFontFamily::Serif:
2619 return "serif";
2620 case StyleGenericFontFamily::SansSerif:
2621 return "sans-serif";
2622 case StyleGenericFontFamily::Monospace:
2623 return "monospace";
2624 case StyleGenericFontFamily::Cursive:
2625 return "cursive";
2626 case StyleGenericFontFamily::Fantasy:
2627 return "fantasy";
2628 case StyleGenericFontFamily::SystemUi:
2629 return "system-ui";
2630 case StyleGenericFontFamily::MozEmoji:
2631 return "-moz-emoji";
2632 case StyleGenericFontFamily::None:
2633 break;
2635 MOZ_ASSERT_UNREACHABLE("Unknown generic");
2636 return nullptr;
2639 void gfxPlatformFontList::InitLoader() {
2640 GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
2641 mStartIndex = 0;
2642 mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
2643 memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
2646 #define FONT_LOADER_MAX_TIMESLICE \
2647 20 // max time for one pass through RunLoader = 20ms
2649 bool gfxPlatformFontList::LoadFontInfo() {
2650 AutoLock lock(mLock);
2651 TimeStamp start = TimeStamp::Now();
2652 uint32_t i, endIndex = mNumFamilies;
2653 fontlist::FontList* list = SharedFontList();
2654 bool loadCmaps =
2655 !list && (!UsesSystemFallback() ||
2656 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback());
2658 // for each font family, load in various font info
2659 for (i = mStartIndex; i < endIndex; i++) {
2660 nsAutoCString key;
2661 GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
2663 if (list) {
2664 fontlist::Family* family = list->FindFamily(key);
2665 if (!family) {
2666 continue;
2668 ReadFaceNamesForFamily(family, NeedFullnamePostscriptNames());
2669 } else {
2670 // lookup in canonical (i.e. English) family name list
2671 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
2672 if (!familyEntry) {
2673 continue;
2676 // read in face names
2677 familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(),
2678 mFontInfo);
2680 // load the cmaps if needed
2681 if (loadCmaps) {
2682 familyEntry->ReadAllCMAPs(mFontInfo);
2686 // Limit the time spent reading fonts in one pass, unless the font-loader
2687 // delay was set to zero, in which case we run to completion even if it
2688 // causes some jank.
2689 if (StaticPrefs::gfx_font_loader_delay() > 0) {
2690 TimeDuration elapsed = TimeStamp::Now() - start;
2691 if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
2692 i + 1 != endIndex) {
2693 endIndex = i + 1;
2694 break;
2699 mStartIndex = endIndex;
2700 bool done = mStartIndex >= mNumFamilies;
2702 if (LOG_FONTINIT_ENABLED()) {
2703 TimeDuration elapsed = TimeStamp::Now() - start;
2704 LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
2705 elapsed.ToMilliseconds(), (done ? "true" : "false")));
2708 if (done) {
2709 mOtherFamilyNamesInitialized = true;
2710 CancelInitOtherFamilyNamesTask();
2711 mFaceNameListsInitialized = true;
2714 return done;
2717 void gfxPlatformFontList::CleanupLoader() {
2718 AutoLock lock(mLock);
2720 mFontFamiliesToLoad.Clear();
2721 mNumFamilies = 0;
2722 bool rebuilt = false, forceReflow = false;
2724 // if had missed face names that are now available, force reflow all
2725 if (mFaceNamesMissed) {
2726 rebuilt = std::any_of(mFaceNamesMissed->cbegin(), mFaceNamesMissed->cend(),
2727 [&](const auto& key) {
2728 mLock.AssertCurrentThreadIn();
2729 return FindFaceName(key);
2731 if (rebuilt) {
2732 RebuildLocalFonts();
2735 mFaceNamesMissed = nullptr;
2738 if (mOtherNamesMissed) {
2739 forceReflow = std::any_of(
2740 mOtherNamesMissed->cbegin(), mOtherNamesMissed->cend(),
2741 [&](const auto& key) {
2742 mLock.AssertCurrentThreadIn();
2743 return FindUnsharedFamily(
2744 nullptr, key,
2745 (FindFamiliesFlags::eForceOtherFamilyNamesLoading |
2746 FindFamiliesFlags::eNoAddToNamesMissedWhenSearching));
2748 if (forceReflow) {
2749 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::No);
2752 mOtherNamesMissed = nullptr;
2755 if (LOG_FONTINIT_ENABLED() && mFontInfo) {
2756 LOG_FONTINIT(
2757 ("(fontinit) fontloader load thread took %8.2f ms "
2758 "%d families %d fonts %d cmaps "
2759 "%d facenames %d othernames %s %s",
2760 mLoadTime.ToMilliseconds(), mFontInfo->mLoadStats.families,
2761 mFontInfo->mLoadStats.fonts, mFontInfo->mLoadStats.cmaps,
2762 mFontInfo->mLoadStats.facenames, mFontInfo->mLoadStats.othernames,
2763 (rebuilt ? "(userfont sets rebuilt)" : ""),
2764 (forceReflow ? "(global reflow)" : "")));
2767 gfxFontInfoLoader::CleanupLoader();
2770 void gfxPlatformFontList::ForceGlobalReflowLocked(
2771 gfxPlatform::NeedsReframe aNeedsReframe,
2772 gfxPlatform::BroadcastToChildren aBroadcastToChildren) {
2773 if (!NS_IsMainThread()) {
2774 NS_DispatchToMainThread(NS_NewRunnableFunction(
2775 "gfxPlatformFontList::ForceGlobalReflowLocked",
2776 [aNeedsReframe, aBroadcastToChildren] {
2777 gfxPlatform::ForceGlobalReflow(aNeedsReframe, aBroadcastToChildren);
2778 }));
2779 return;
2782 AutoUnlock unlock(mLock);
2783 gfxPlatform::ForceGlobalReflow(aNeedsReframe, aBroadcastToChildren);
2786 void gfxPlatformFontList::GetPrefsAndStartLoader() {
2787 // If we're already in shutdown, there's no point in starting this, and it
2788 // could trigger an assertion if we try to use the Thread Manager too late.
2789 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
2790 return;
2792 uint32_t delay = std::max(1u, StaticPrefs::gfx_font_loader_delay());
2793 if (NS_IsMainThread()) {
2794 StartLoader(delay);
2795 } else {
2796 NS_DispatchToMainThread(NS_NewRunnableFunction(
2797 "StartLoader callback", [delay, fontList = this] {
2798 fontList->Lock();
2799 fontList->StartLoader(delay);
2800 fontList->Unlock();
2801 }));
2805 void gfxPlatformFontList::RebuildLocalFonts(bool aForgetLocalFaces) {
2806 for (auto* fontset : mUserFontSetList) {
2807 if (aForgetLocalFaces) {
2808 fontset->ForgetLocalFaces();
2810 fontset->RebuildLocalRules();
2814 void gfxPlatformFontList::ClearLangGroupPrefFontsLocked() {
2815 for (uint32_t i = eFontPrefLang_First;
2816 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
2817 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
2818 for (auto& pref : prefFontsLangGroup) {
2819 pref = nullptr;
2822 mCJKPrefLangs.Clear();
2823 mEmojiPrefFont = nullptr;
2825 // Create a new FontPrefs and replace the existing one.
2826 mFontPrefs = MakeUnique<FontPrefs>();
2829 // Support for memory reporting
2831 // this is also used by subclasses that hold additional font tables
2832 /*static*/
2833 size_t gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis(
2834 const FontFamilyTable& aTable, MallocSizeOf aMallocSizeOf) {
2835 return std::accumulate(
2836 aTable.Keys().cbegin(), aTable.Keys().cend(),
2837 aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
2838 [&](size_t oldValue, const nsACString& key) {
2839 // We don't count the size of the family here, because this is an
2840 // *extra* reference to a family that will have already been counted in
2841 // the main list.
2842 return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2846 /*static*/
2847 size_t gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
2848 const FontEntryTable& aTable, MallocSizeOf aMallocSizeOf) {
2849 return std::accumulate(
2850 aTable.Keys().cbegin(), aTable.Keys().cend(),
2851 aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
2852 [&](size_t oldValue, const nsACString& key) {
2853 // The font itself is counted by its owning family; here we only care
2854 // about the names stored in the hashtable keys.
2856 return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2860 void gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2861 FontListSizes* aSizes) const {
2862 AutoLock lock(mLock);
2864 aSizes->mFontListSize +=
2865 mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
2866 for (const auto& entry : mFontFamilies) {
2867 aSizes->mFontListSize +=
2868 entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2869 entry.GetData()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2872 aSizes->mFontListSize +=
2873 SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
2875 if (mExtraNames) {
2876 aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
2877 mExtraNames->mFullnames, aMallocSizeOf);
2878 aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
2879 mExtraNames->mPostscriptNames, aMallocSizeOf);
2882 for (uint32_t i = eFontPrefLang_First;
2883 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
2884 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
2885 for (const UniquePtr<PrefFontList>& pf : prefFontsLangGroup) {
2886 if (pf) {
2887 aSizes->mFontListSize += pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
2892 for (const auto& bitset : mCodepointsWithNoFonts) {
2893 aSizes->mFontListSize += bitset.SizeOfExcludingThis(aMallocSizeOf);
2895 aSizes->mFontListSize +=
2896 mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
2898 aSizes->mFontListSize +=
2899 mBadUnderlineFamilyNames.ShallowSizeOfExcludingThis(aMallocSizeOf);
2900 for (const auto& i : mBadUnderlineFamilyNames) {
2901 aSizes->mFontListSize += i.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2904 aSizes->mFontListSize +=
2905 mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
2906 for (const auto& entry : mSharedCmaps) {
2907 aSizes->mCharMapsSize += entry.GetKey()->SizeOfIncludingThis(aMallocSizeOf);
2910 aSizes->mFontListSize +=
2911 mFontEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
2912 for (const auto& entry : mFontEntries.Values()) {
2913 if (entry) {
2914 entry->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2918 if (SharedFontList()) {
2919 aSizes->mFontListSize +=
2920 SharedFontList()->SizeOfIncludingThis(aMallocSizeOf);
2921 if (XRE_IsParentProcess()) {
2922 aSizes->mSharedSize += SharedFontList()->AllocatedShmemSize();
2927 void gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2928 FontListSizes* aSizes) const {
2929 aSizes->mFontListSize += aMallocSizeOf(this);
2930 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2933 void gfxPlatformFontList::InitOtherFamilyNamesInternal(
2934 bool aDeferOtherFamilyNamesLoading) {
2935 if (mOtherFamilyNamesInitialized) {
2936 return;
2939 AutoLock lock(mLock);
2941 if (aDeferOtherFamilyNamesLoading) {
2942 TimeStamp start = TimeStamp::Now();
2943 bool timedOut = false;
2945 auto list = SharedFontList();
2946 if (list) {
2947 // If the gfxFontInfoLoader task is not yet running, kick it off now so
2948 // that it will load remaining names etc as soon as idle time permits.
2949 if (mState == stateInitial || mState == stateTimerOnDelay) {
2950 StartLoader(0);
2951 timedOut = true;
2953 } else {
2954 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2955 family->ReadOtherFamilyNames(this);
2956 TimeDuration elapsed = TimeStamp::Now() - start;
2957 if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
2958 timedOut = true;
2959 break;
2964 if (!timedOut) {
2965 mOtherFamilyNamesInitialized = true;
2966 CancelInitOtherFamilyNamesTask();
2968 TimeStamp end = TimeStamp::Now();
2969 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
2970 start, end);
2972 if (LOG_FONTINIT_ENABLED()) {
2973 TimeDuration elapsed = end - start;
2974 LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
2975 elapsed.ToMilliseconds(), (timedOut ? "timeout" : "")));
2977 } else {
2978 TimeStamp start = TimeStamp::Now();
2980 auto list = SharedFontList();
2981 if (list) {
2982 for (auto& f : mozilla::Range<fontlist::Family>(list->Families(),
2983 list->NumFamilies())) {
2984 ReadFaceNamesForFamily(&f, false);
2986 } else {
2987 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2988 family->ReadOtherFamilyNames(this);
2992 mOtherFamilyNamesInitialized = true;
2993 CancelInitOtherFamilyNamesTask();
2995 TimeStamp end = TimeStamp::Now();
2996 Telemetry::AccumulateTimeDelta(
2997 Telemetry::FONTLIST_INITOTHERFAMILYNAMES_NO_DEFERRING, start, end);
2999 if (LOG_FONTINIT_ENABLED()) {
3000 TimeDuration elapsed = end - start;
3001 LOG_FONTINIT(
3002 ("(fontinit) InitOtherFamilyNames without deferring took %8.2f ms",
3003 elapsed.ToMilliseconds()));
3008 void gfxPlatformFontList::CancelInitOtherFamilyNamesTask() {
3009 if (mPendingOtherFamilyNameTask) {
3010 mPendingOtherFamilyNameTask->Cancel();
3011 mPendingOtherFamilyNameTask = nullptr;
3013 auto list = SharedFontList();
3014 if (list && XRE_IsParentProcess()) {
3015 bool forceReflow = false;
3016 if (!mAliasTable.IsEmpty()) {
3017 list->SetAliases(mAliasTable);
3018 mAliasTable.Clear();
3019 forceReflow = true;
3021 if (mLocalNameTable.Count()) {
3022 list->SetLocalNames(mLocalNameTable);
3023 mLocalNameTable.Clear();
3024 forceReflow = true;
3026 if (forceReflow) {
3027 dom::ContentParent::BroadcastFontListChanged();
3032 void gfxPlatformFontList::ShareFontListShmBlockToProcess(
3033 uint32_t aGeneration, uint32_t aIndex, base::ProcessId aPid,
3034 base::SharedMemoryHandle* aOut) {
3035 auto list = SharedFontList();
3036 if (!list) {
3037 return;
3039 if (!aGeneration || list->GetGeneration() == aGeneration) {
3040 list->ShareShmBlockToProcess(aIndex, aPid, aOut);
3041 } else {
3042 *aOut = base::SharedMemory::NULLHandle();
3046 void gfxPlatformFontList::ShareFontListToProcess(
3047 nsTArray<base::SharedMemoryHandle>* aBlocks, base::ProcessId aPid) {
3048 auto list = SharedFontList();
3049 if (list) {
3050 list->ShareBlocksToProcess(aBlocks, aPid);
3054 base::SharedMemoryHandle gfxPlatformFontList::ShareShmBlockToProcess(
3055 uint32_t aIndex, base::ProcessId aPid) {
3056 MOZ_RELEASE_ASSERT(SharedFontList());
3057 return SharedFontList()->ShareBlockToProcess(aIndex, aPid);
3060 void gfxPlatformFontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
3061 base::SharedMemoryHandle aHandle) {
3062 if (SharedFontList()) {
3063 AutoLock lock(mLock);
3064 SharedFontList()->ShmBlockAdded(aGeneration, aIndex, std::move(aHandle));
3068 void gfxPlatformFontList::InitializeFamily(uint32_t aGeneration,
3069 uint32_t aFamilyIndex,
3070 bool aLoadCmaps) {
3071 auto list = SharedFontList();
3072 MOZ_ASSERT(list);
3073 if (!list) {
3074 return;
3076 if (list->GetGeneration() != aGeneration) {
3077 return;
3079 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3080 return;
3082 if (aFamilyIndex >= list->NumFamilies()) {
3083 return;
3085 fontlist::Family* family = list->Families() + aFamilyIndex;
3086 if (!family->IsInitialized() || aLoadCmaps) {
3087 Unused << InitializeFamily(family, aLoadCmaps);
3091 void gfxPlatformFontList::SetCharacterMap(uint32_t aGeneration,
3092 uint32_t aFamilyIndex, bool aAlias,
3093 uint32_t aFaceIndex,
3094 const gfxSparseBitSet& aMap) {
3095 MOZ_ASSERT(XRE_IsParentProcess());
3096 auto list = SharedFontList();
3097 MOZ_ASSERT(list);
3098 if (!list) {
3099 return;
3101 if (list->GetGeneration() != aGeneration) {
3102 return;
3104 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3105 return;
3108 const fontlist::Family* family;
3109 if (aAlias) {
3110 if (aFamilyIndex >= list->NumAliases()) {
3111 MOZ_ASSERT(false, "AliasFamily index out of range");
3112 return;
3114 family = list->AliasFamilies() + aFamilyIndex;
3115 } else {
3116 if (aFamilyIndex >= list->NumFamilies()) {
3117 MOZ_ASSERT(false, "Family index out of range");
3118 return;
3120 family = list->Families() + aFamilyIndex;
3123 if (aFaceIndex >= family->NumFaces()) {
3124 MOZ_ASSERT(false, "Face index out of range");
3125 return;
3128 if (auto* face =
3129 family->Faces(list)[aFaceIndex].ToPtr<fontlist::Face>(list)) {
3130 face->mCharacterMap = GetShmemCharMap(&aMap);
3134 void gfxPlatformFontList::SetupFamilyCharMap(uint32_t aGeneration,
3135 uint32_t aIndex, bool aAlias) {
3136 MOZ_ASSERT(XRE_IsParentProcess());
3137 auto list = SharedFontList();
3138 MOZ_ASSERT(list);
3139 if (!list) {
3140 return;
3142 if (list->GetGeneration() != aGeneration) {
3143 return;
3145 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3146 return;
3149 if (aAlias) {
3150 if (aIndex >= list->NumAliases()) {
3151 MOZ_ASSERT(false, "AliasFamily index out of range");
3152 return;
3154 list->AliasFamilies()[aIndex].SetupFamilyCharMap(list);
3155 return;
3158 if (aIndex >= list->NumFamilies()) {
3159 MOZ_ASSERT(false, "Family index out of range");
3160 return;
3162 list->Families()[aIndex].SetupFamilyCharMap(list);
3165 bool gfxPlatformFontList::InitOtherFamilyNames(uint32_t aGeneration,
3166 bool aDefer) {
3167 auto list = SharedFontList();
3168 MOZ_ASSERT(list);
3169 if (!list) {
3170 return false;
3172 if (list->GetGeneration() != aGeneration) {
3173 return false;
3175 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3176 return false;
3178 return InitOtherFamilyNames(aDefer);
3181 uint32_t gfxPlatformFontList::GetGeneration() const {
3182 return SharedFontList() ? SharedFontList()->GetGeneration() : 0;
3185 gfxPlatformFontList::FontPrefs::FontPrefs() {
3186 // This must be created on the main thread, so that we can safely use the
3187 // Preferences service. Once created, it can be read from any thread.
3188 MOZ_ASSERT(NS_IsMainThread());
3189 Init();
3192 void gfxPlatformFontList::FontPrefs::Init() {
3193 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
3194 return;
3196 nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch();
3197 if (!prefRootBranch) {
3198 return;
3200 nsTArray<nsCString> prefNames;
3201 if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNamePrefix, prefNames))) {
3202 for (auto& prefName : prefNames) {
3203 nsAutoCString value;
3204 if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
3205 nsAutoCString pref(Substring(prefName, sizeof(kNamePrefix) - 1));
3206 mFontName.InsertOrUpdate(pref, value);
3210 if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNameListPrefix, prefNames))) {
3211 for (auto& prefName : prefNames) {
3212 nsAutoCString value;
3213 if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
3214 nsAutoCString pref(Substring(prefName, sizeof(kNameListPrefix) - 1));
3215 mFontNameList.InsertOrUpdate(pref, value);
3219 mEmojiHasUserValue = Preferences::HasUserValue("font.name-list.emoji");
3222 bool gfxPlatformFontList::FontPrefs::LookupName(const nsACString& aPref,
3223 nsACString& aValue) const {
3224 if (const auto& value = mFontName.Lookup(aPref)) {
3225 aValue = *value;
3226 return true;
3228 return false;
3231 bool gfxPlatformFontList::FontPrefs::LookupNameList(const nsACString& aPref,
3232 nsACString& aValue) const {
3233 if (const auto& value = mFontNameList.Lookup(aPref)) {
3234 aValue = *value;
3235 return true;
3237 return false;
3240 bool gfxPlatformFontList::IsKnownIconFontFamily(
3241 const nsAtom* aFamilyName) const {
3242 nsAtomCString fam(aFamilyName);
3243 ToLowerCase(fam);
3244 return mIconFontsSet.Contains(fam);
3247 #undef LOG
3248 #undef LOG_ENABLED