no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / gfx / thebes / gfxPlatformFontList.cpp
blob709a0f3c2773e653b154ac6fdafe5fdeaea0b802
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 /* static */
382 void gfxPlatformFontList::FontWhitelistPrefChanged(const char* aPref,
383 void* aClosure) {
384 MOZ_ASSERT(XRE_IsParentProcess());
385 auto* pfl = gfxPlatformFontList::PlatformFontList();
386 pfl->UpdateFontList(true);
387 dom::ContentParent::NotifyUpdatedFonts(true);
390 void gfxPlatformFontList::ApplyWhitelist() {
391 uint32_t numFonts = mEnabledFontsList.Length();
392 if (!mFontFamilyWhitelistActive) {
393 return;
395 nsTHashSet<nsCString> familyNamesWhitelist;
396 for (uint32_t i = 0; i < numFonts; i++) {
397 nsAutoCString key;
398 ToLowerCase(mEnabledFontsList[i], key);
399 familyNamesWhitelist.Insert(key);
401 AutoTArray<RefPtr<gfxFontFamily>, 128> accepted;
402 bool whitelistedFontFound = false;
403 for (const auto& entry : mFontFamilies) {
404 if (entry.GetData()->IsHidden()) {
405 // Hidden system fonts are exempt from whitelisting, but don't count
406 // towards determining whether we "kept" any (user-visible) fonts
407 accepted.AppendElement(entry.GetData());
408 continue;
410 nsAutoCString fontFamilyName(entry.GetKey());
411 ToLowerCase(fontFamilyName);
412 if (familyNamesWhitelist.Contains(fontFamilyName)) {
413 accepted.AppendElement(entry.GetData());
414 whitelistedFontFound = true;
417 if (!whitelistedFontFound) {
418 // No whitelisted fonts found! Ignore the whitelist.
419 return;
421 // Replace the original full list with the accepted subset.
422 mFontFamilies.Clear();
423 for (auto& f : accepted) {
424 nsAutoCString fontFamilyName(f->Name());
425 ToLowerCase(fontFamilyName);
426 mFontFamilies.InsertOrUpdate(fontFamilyName, std::move(f));
430 void gfxPlatformFontList::ApplyWhitelist(
431 nsTArray<fontlist::Family::InitData>& aFamilies) {
432 mLock.AssertCurrentThreadIn();
433 if (!mFontFamilyWhitelistActive) {
434 return;
436 nsTHashSet<nsCString> familyNamesWhitelist;
437 for (const auto& item : mEnabledFontsList) {
438 nsAutoCString key;
439 ToLowerCase(item, key);
440 familyNamesWhitelist.Insert(key);
442 AutoTArray<fontlist::Family::InitData, 128> accepted;
443 bool keptNonHidden = false;
444 for (auto& f : aFamilies) {
445 if (f.mVisibility == FontVisibility::Hidden ||
446 familyNamesWhitelist.Contains(f.mKey)) {
447 accepted.AppendElement(f);
448 if (f.mVisibility != FontVisibility::Hidden) {
449 keptNonHidden = true;
453 if (!keptNonHidden) {
454 // No (visible) families were whitelisted: ignore the whitelist
455 // and just leave the fontlist unchanged.
456 return;
458 aFamilies = std::move(accepted);
461 bool gfxPlatformFontList::FamilyInList(const nsACString& aName,
462 const char* aList[], size_t aCount) {
463 size_t result;
464 return BinarySearchIf(
465 aList, 0, aCount,
466 [&](const char* const aVal) -> int {
467 return nsCaseInsensitiveUTF8StringComparator(
468 aName.BeginReading(), aVal, aName.Length(), strlen(aVal));
470 &result);
473 void gfxPlatformFontList::CheckFamilyList(const char* aList[], size_t aCount) {
474 #ifdef DEBUG
475 MOZ_ASSERT(aCount > 0, "empty font family list?");
476 const char* a = aList[0];
477 uint32_t aLen = strlen(a);
478 for (size_t i = 1; i < aCount; ++i) {
479 const char* b = aList[i];
480 uint32_t bLen = strlen(b);
481 if (nsCaseInsensitiveUTF8StringComparator(a, b, aLen, bLen) >= 0) {
482 MOZ_CRASH_UNSAFE_PRINTF("incorrectly sorted font family list: %s >= %s",
483 a, b);
485 a = b;
486 aLen = bLen;
488 #endif
491 bool gfxPlatformFontList::AddWithLegacyFamilyName(const nsACString& aLegacyName,
492 gfxFontEntry* aFontEntry,
493 FontVisibility aVisibility) {
494 mLock.AssertCurrentThreadIn();
495 bool added = false;
496 nsAutoCString key;
497 ToLowerCase(aLegacyName, key);
498 mOtherFamilyNames
499 .LookupOrInsertWith(key,
500 [&] {
501 RefPtr<gfxFontFamily> family =
502 CreateFontFamily(aLegacyName, aVisibility);
503 // We don't want the family to search for faces,
504 // we're adding them directly here.
505 family->SetHasStyles(true);
506 // And we don't want it to attempt to search for
507 // legacy names, because we've already done that
508 // (and this is the result).
509 family->SetCheckedForLegacyFamilyNames(true);
510 added = true;
511 return family;
513 ->AddFontEntry(aFontEntry->Clone());
514 return added;
517 bool gfxPlatformFontList::InitFontList() {
518 // If the startup font-list-init thread is still running, we need to wait
519 // for it to finish before trying to reinitialize here.
520 if (sInitFontListThread && !IsInitFontListThread()) {
521 PR_JoinThread(sInitFontListThread);
522 sInitFontListThread = nullptr;
525 AutoLock lock(mLock);
527 if (LOG_FONTINIT_ENABLED()) {
528 LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
531 if (IsInitialized()) {
532 // Font-list reinitialization always occurs on the main thread, in response
533 // to a change notification; it's only the initial creation during startup
534 // that may be on another thread.
535 MOZ_ASSERT(NS_IsMainThread());
537 // Rebuilding fontlist so clear out font/word caches.
538 gfxFontCache* fontCache = gfxFontCache::GetCache();
539 if (fontCache) {
540 fontCache->FlushShapedWordCaches();
541 fontCache->Flush();
544 gfxPlatform::PurgeSkiaFontCache();
546 // There's no need to broadcast this reflow request to child processes, as
547 // ContentParent::NotifyUpdatedFonts deals with it by re-entering into this
548 // function on child processes.
549 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::Yes,
550 gfxPlatform::BroadcastToChildren::No);
552 mAliasTable.Clear();
553 mLocalNameTable.Clear();
554 mIconFontsSet.Clear();
556 CancelLoadCmapsTask();
557 mStartedLoadingCmapsFrom = 0xffffffffu;
559 CancelInitOtherFamilyNamesTask();
560 mFontFamilies.Clear();
561 mOtherFamilyNames.Clear();
562 mOtherFamilyNamesInitialized = false;
564 if (mExtraNames) {
565 mExtraNames->mFullnames.Clear();
566 mExtraNames->mPostscriptNames.Clear();
568 mFaceNameListsInitialized = false;
569 ClearLangGroupPrefFontsLocked();
570 CancelLoader();
572 // Clear cached family records that will no longer be valid.
573 for (auto& f : mReplacementCharFallbackFamily) {
574 f = FontFamily();
577 gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
578 mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
580 LoadIconFontOverrideList();
583 // From here, gfxPlatformFontList::IsInitialized will return true,
584 // unless InitFontListForPlatform() fails and we reset it below.
585 mFontlistInitCount++;
587 InitializeCodepointsWithNoFonts();
589 // Try to initialize the cross-process shared font list if enabled by prefs,
590 // but not if we're running in Safe Mode.
591 if (StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
592 !gfxPlatform::InSafeMode()) {
593 for (const auto& entry : mFontEntries.Values()) {
594 if (!entry) {
595 continue;
597 AutoWriteLock lock(entry->mLock);
598 entry->mShmemCharacterMap = nullptr;
599 entry->mShmemFace = nullptr;
600 entry->mFamilyName.Truncate();
602 mFontEntries.Clear();
603 mShmemCharMaps.Clear();
604 bool oldSharedList = mSharedFontList != nullptr;
605 mSharedFontList.reset(new fontlist::FontList(mFontlistInitCount));
606 InitSharedFontListForPlatform();
607 if (mSharedFontList && mSharedFontList->Initialized()) {
608 if (mLocalNameTable.Count()) {
609 SharedFontList()->SetLocalNames(mLocalNameTable);
610 mLocalNameTable.Clear();
612 } else {
613 // something went wrong, fall back to in-process list
614 gfxCriticalNote << "Failed to initialize shared font list, "
615 "falling back to in-process list.";
616 mSharedFontList.reset(nullptr);
618 if (oldSharedList && XRE_IsParentProcess()) {
619 // notify all children of the change
620 if (NS_IsMainThread()) {
621 dom::ContentParent::NotifyUpdatedFonts(true);
622 } else {
623 NS_DispatchToMainThread(NS_NewRunnableFunction(
624 "NotifyUpdatedFonts callback",
625 [] { dom::ContentParent::NotifyUpdatedFonts(true); }));
630 if (!SharedFontList()) {
631 if (NS_FAILED(InitFontListForPlatform())) {
632 mFontlistInitCount = 0;
633 return false;
635 ApplyWhitelist();
638 // Set up mDefaultFontEntry as a "last resort" default that we can use
639 // to avoid crashing if the font list is otherwise unusable.
640 gfxFontStyle defStyle;
641 FontFamily fam = GetDefaultFontLocked(nullptr, &defStyle);
642 gfxFontEntry* fe;
643 if (fam.mShared) {
644 auto face = fam.mShared->FindFaceForStyle(SharedFontList(), defStyle);
645 fe = face ? GetOrCreateFontEntryLocked(face, fam.mShared) : nullptr;
646 } else {
647 fe = fam.mUnshared->FindFontForStyle(defStyle);
649 mDefaultFontEntry = fe;
651 return true;
654 void gfxPlatformFontList::LoadIconFontOverrideList() {
655 mIconFontsSet.Clear();
656 AutoTArray<nsCString, 20> iconFontsList;
657 gfxFontUtils::GetPrefsFontList(kIconFontsPref, iconFontsList);
658 for (auto& name : iconFontsList) {
659 ToLowerCase(name);
660 mIconFontsSet.Insert(name);
664 void gfxPlatformFontList::InitializeCodepointsWithNoFonts() {
665 auto& first = mCodepointsWithNoFonts[FontVisibility(0)];
666 for (auto& bitset : mCodepointsWithNoFonts) {
667 if (&bitset == &first) {
668 bitset.reset();
669 bitset.SetRange(0, 0x1f); // C0 controls
670 bitset.SetRange(0x7f, 0x9f); // C1 controls
671 bitset.SetRange(0xE000, 0xF8FF); // PUA
672 bitset.SetRange(0xF0000, 0x10FFFD); // Supplementary PUA
673 bitset.SetRange(0xfdd0, 0xfdef); // noncharacters
674 for (unsigned i = 0; i <= 0x100000; i += 0x10000) {
675 bitset.SetRange(i + 0xfffe, i + 0xffff); // noncharacters
677 bitset.Compact();
678 } else {
679 bitset = first;
684 void gfxPlatformFontList::FontListChanged() {
685 MOZ_ASSERT(!XRE_IsParentProcess());
686 AutoLock lock(mLock);
687 InitializeCodepointsWithNoFonts();
688 if (SharedFontList()) {
689 // If we're using a shared local face-name list, this may have changed
690 // such that existing font entries held by user font sets are no longer
691 // safe to use: ensure they all get flushed.
692 RebuildLocalFonts(/*aForgetLocalFaces*/ true);
694 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::Yes);
697 void gfxPlatformFontList::GenerateFontListKey(const nsACString& aKeyName,
698 nsACString& aResult) {
699 aResult = aKeyName;
700 ToLowerCase(aResult);
703 // Used if a stylo thread wants to trigger InitOtherFamilyNames in the main
704 // process: we can't do IPC from the stylo thread so we post this to the main
705 // thread instead.
706 class InitOtherFamilyNamesForStylo : public mozilla::Runnable {
707 public:
708 explicit InitOtherFamilyNamesForStylo(bool aDeferOtherFamilyNamesLoading)
709 : Runnable("gfxPlatformFontList::InitOtherFamilyNamesForStylo"),
710 mDefer(aDeferOtherFamilyNamesLoading) {}
712 NS_IMETHOD Run() override {
713 auto pfl = gfxPlatformFontList::PlatformFontList();
714 auto list = pfl->SharedFontList();
715 if (!list) {
716 return NS_OK;
718 bool initialized = false;
719 dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
720 list->GetGeneration(), mDefer, &initialized);
721 pfl->mOtherFamilyNamesInitialized.compareExchange(false, initialized);
722 return NS_OK;
725 private:
726 bool mDefer;
729 #define OTHERNAMES_TIMEOUT 200
731 bool gfxPlatformFontList::InitOtherFamilyNames(
732 bool aDeferOtherFamilyNamesLoading) {
733 if (mOtherFamilyNamesInitialized) {
734 return true;
737 if (SharedFontList() && !XRE_IsParentProcess()) {
738 if (NS_IsMainThread()) {
739 bool initialized;
740 dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
741 SharedFontList()->GetGeneration(), aDeferOtherFamilyNamesLoading,
742 &initialized);
743 mOtherFamilyNamesInitialized.compareExchange(false, initialized);
744 } else {
745 NS_DispatchToMainThread(
746 new InitOtherFamilyNamesForStylo(aDeferOtherFamilyNamesLoading));
748 return mOtherFamilyNamesInitialized;
751 // If the font loader delay has been set to zero, we don't defer loading
752 // additional family names (regardless of the aDefer... parameter), as we
753 // take this to mean availability of font info is to be prioritized over
754 // potential startup perf or main-thread jank.
755 // (This is used so we can reliably run reftests that depend on localized
756 // font-family names being available.)
757 if (aDeferOtherFamilyNamesLoading &&
758 StaticPrefs::gfx_font_loader_delay_AtStartup() > 0) {
759 if (!mPendingOtherFamilyNameTask) {
760 RefPtr<mozilla::CancelableRunnable> task =
761 new InitOtherFamilyNamesRunnable();
762 mPendingOtherFamilyNameTask = task;
763 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
765 } else {
766 InitOtherFamilyNamesInternal(false);
768 return mOtherFamilyNamesInitialized;
771 // time limit for loading facename lists (ms)
772 #define NAMELIST_TIMEOUT 200
774 gfxFontEntry* gfxPlatformFontList::SearchFamiliesForFaceName(
775 const nsACString& aFaceName) {
776 TimeStamp start = TimeStamp::Now();
777 bool timedOut = false;
778 // if mFirstChar is not 0, only load facenames for families
779 // that start with this character
780 char16_t firstChar = 0;
781 gfxFontEntry* lookup = nullptr;
783 // iterate over familes starting with the same letter
784 firstChar = ToLowerCase(aFaceName.CharAt(0));
786 for (const auto& entry : mFontFamilies) {
787 nsCStringHashKey::KeyType key = entry.GetKey();
788 const RefPtr<gfxFontFamily>& family = entry.GetData();
790 // when filtering, skip names that don't start with the filter character
791 if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
792 continue;
795 family->ReadFaceNames(this, NeedFullnamePostscriptNames());
797 TimeDuration elapsed = TimeStamp::Now() - start;
798 if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
799 timedOut = true;
800 break;
804 lookup = FindFaceName(aFaceName);
806 TimeStamp end = TimeStamp::Now();
807 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS, start,
808 end);
809 if (LOG_FONTINIT_ENABLED()) {
810 TimeDuration elapsed = end - start;
811 LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
812 elapsed.ToMilliseconds(), (lookup ? "found name" : ""),
813 (timedOut ? "timeout" : "")));
816 return lookup;
819 gfxFontEntry* gfxPlatformFontList::FindFaceName(const nsACString& aFaceName) {
820 gfxFontEntry* lookup;
822 // lookup in name lookup tables, return null if not found
823 if (mExtraNames &&
824 ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
825 (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
826 return lookup;
829 return nullptr;
832 gfxFontEntry* gfxPlatformFontList::LookupInFaceNameLists(
833 const nsACString& aFaceName) {
834 gfxFontEntry* lookup = nullptr;
836 // initialize facename lookup tables if needed
837 // note: this can terminate early or time out, in which case
838 // mFaceNameListsInitialized remains false
839 if (!mFaceNameListsInitialized) {
840 lookup = SearchFamiliesForFaceName(aFaceName);
841 if (lookup) {
842 return lookup;
846 // lookup in name lookup tables, return null if not found
847 if (!(lookup = FindFaceName(aFaceName))) {
848 // names not completely initialized, so keep track of lookup misses
849 if (!mFaceNameListsInitialized) {
850 if (!mFaceNamesMissed) {
851 mFaceNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
853 mFaceNamesMissed->Insert(aFaceName);
857 return lookup;
860 gfxFontEntry* gfxPlatformFontList::LookupInSharedFaceNameList(
861 nsPresContext* aPresContext, const nsACString& aFaceName,
862 WeightRange aWeightForEntry, StretchRange aStretchForEntry,
863 SlantStyleRange aStyleForEntry) {
864 nsAutoCString keyName(aFaceName);
865 ToLowerCase(keyName);
866 fontlist::FontList* list = SharedFontList();
867 fontlist::Family* family = nullptr;
868 fontlist::Face* face = nullptr;
869 if (list->NumLocalFaces()) {
870 fontlist::LocalFaceRec* rec = list->FindLocalFace(keyName);
871 if (rec) {
872 auto* families = list->Families();
873 if (families) {
874 family = &families[rec->mFamilyIndex];
875 face = family->Faces(list)[rec->mFaceIndex].ToPtr<fontlist::Face>(list);
878 } else {
879 list->SearchForLocalFace(keyName, &family, &face);
881 if (!face || !family) {
882 return nullptr;
884 FontVisibility level =
885 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
886 if (!IsVisibleToCSS(*family, level)) {
887 if (aPresContext) {
888 aPresContext->ReportBlockedFontFamily(*family);
890 return nullptr;
892 gfxFontEntry* fe = CreateFontEntry(face, family);
893 if (fe) {
894 fe->mIsLocalUserFont = true;
895 fe->mWeightRange = aWeightForEntry;
896 fe->mStretchRange = aStretchForEntry;
897 fe->mStyleRange = aStyleForEntry;
899 return fe;
902 void gfxPlatformFontList::LoadBadUnderlineList() {
903 gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset",
904 mBadUnderlineFamilyNames);
905 for (auto& fam : mBadUnderlineFamilyNames) {
906 ToLowerCase(fam);
908 mBadUnderlineFamilyNames.Compact();
909 mBadUnderlineFamilyNames.Sort();
912 void gfxPlatformFontList::UpdateFontList(bool aFullRebuild) {
913 MOZ_ASSERT(NS_IsMainThread());
914 if (aFullRebuild) {
915 InitFontList();
916 AutoLock lock(mLock);
917 RebuildLocalFonts();
918 } else {
919 // The font list isn't being fully rebuilt, we're just being notified that
920 // character maps have been updated and so font fallback needs to be re-
921 // done. We only care about this if we have previously encountered a
922 // fallback that required cmaps that were not yet available, and so we
923 // asked for the async cmap loader to run.
924 AutoLock lock(mLock);
925 if (mStartedLoadingCmapsFrom != 0xffffffffu) {
926 InitializeCodepointsWithNoFonts();
927 mStartedLoadingCmapsFrom = 0xffffffffu;
928 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::No);
933 bool gfxPlatformFontList::IsVisibleToCSS(const gfxFontFamily& aFamily,
934 FontVisibility aVisibility) const {
935 return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
938 bool gfxPlatformFontList::IsVisibleToCSS(const fontlist::Family& aFamily,
939 FontVisibility aVisibility) const {
940 return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
943 void gfxPlatformFontList::GetFontList(nsAtom* aLangGroup,
944 const nsACString& aGenericFamily,
945 nsTArray<nsString>& aListOfFonts) {
946 AutoLock lock(mLock);
948 if (SharedFontList()) {
949 fontlist::FontList* list = SharedFontList();
950 const fontlist::Family* families = list->Families();
951 if (families) {
952 for (uint32_t i = 0; i < list->NumFamilies(); i++) {
953 auto& f = families[i];
954 if (!IsVisibleToCSS(f, FontVisibility::User) || f.IsAltLocaleFamily()) {
955 continue;
957 // XXX TODO: filter families for aGenericFamily, if supported by
958 // platform
959 aListOfFonts.AppendElement(
960 NS_ConvertUTF8toUTF16(list->LocalizedFamilyName(&f)));
963 return;
966 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
967 if (!IsVisibleToCSS(*family, FontVisibility::User)) {
968 continue;
970 if (family->FilterForFontList(aLangGroup, aGenericFamily)) {
971 nsAutoCString localizedFamilyName;
972 family->LocalizedName(localizedFamilyName);
973 aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(localizedFamilyName));
977 aListOfFonts.Sort();
978 aListOfFonts.Compact();
981 void gfxPlatformFontList::GetFontFamilyList(
982 nsTArray<RefPtr<gfxFontFamily>>& aFamilyArray) {
983 AutoLock lock(mLock);
984 MOZ_ASSERT(aFamilyArray.IsEmpty());
985 // This doesn't use ToArray, because the caller passes an AutoTArray.
986 aFamilyArray.SetCapacity(mFontFamilies.Count());
987 for (const auto& family : mFontFamilies.Values()) {
988 aFamilyArray.AppendElement(family);
992 already_AddRefed<gfxFont> gfxPlatformFontList::SystemFindFontForChar(
993 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
994 Script aRunScript, eFontPresentation aPresentation,
995 const gfxFontStyle* aStyle, FontVisibility* aVisibility) {
996 AutoLock lock(mLock);
997 FontVisibility level =
998 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
999 MOZ_ASSERT(!mCodepointsWithNoFonts[level].test(aCh),
1000 "don't call for codepoints already known to be unsupported");
1002 // Try to short-circuit font fallback for U+FFFD, used to represent
1003 // encoding errors: just use cached family from last time U+FFFD was seen.
1004 // This helps speed up pages with lots of encoding errors, binary-as-text,
1005 // etc.
1006 if (aCh == 0xFFFD) {
1007 gfxFontEntry* fontEntry = nullptr;
1008 auto& fallbackFamily = mReplacementCharFallbackFamily[level];
1009 if (fallbackFamily.mShared) {
1010 fontlist::Face* face =
1011 fallbackFamily.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
1012 if (face) {
1013 fontEntry = GetOrCreateFontEntryLocked(face, fallbackFamily.mShared);
1014 *aVisibility = fallbackFamily.mShared->Visibility();
1016 } else if (fallbackFamily.mUnshared) {
1017 fontEntry = fallbackFamily.mUnshared->FindFontForStyle(*aStyle);
1018 *aVisibility = fallbackFamily.mUnshared->Visibility();
1021 // this should never fail, as we must have found U+FFFD in order to set
1022 // mReplacementCharFallbackFamily[...] at all, but better play it safe
1023 if (fontEntry && fontEntry->HasCharacter(aCh)) {
1024 return fontEntry->FindOrMakeFont(aStyle);
1028 TimeStamp start = TimeStamp::Now();
1030 // search commonly available fonts
1031 bool common = true;
1032 FontFamily fallbackFamily;
1033 RefPtr<gfxFont> candidate =
1034 CommonFontFallback(aPresContext, aCh, aNextCh, aRunScript, aPresentation,
1035 aStyle, fallbackFamily);
1036 RefPtr<gfxFont> font;
1037 if (candidate) {
1038 if (aPresentation == eFontPresentation::Any) {
1039 font = std::move(candidate);
1040 } else {
1041 bool hasColorGlyph = candidate->HasColorGlyphFor(aCh, aNextCh);
1042 if (hasColorGlyph == PrefersColor(aPresentation)) {
1043 font = std::move(candidate);
1048 // If we didn't find a common font, or it was not the preferred type (color
1049 // or monochrome), do system-wide fallback (except for specials).
1050 uint32_t cmapCount = 0;
1051 if (!font) {
1052 common = false;
1053 font = GlobalFontFallback(aPresContext, aCh, aNextCh, aRunScript,
1054 aPresentation, aStyle, cmapCount, fallbackFamily);
1055 // If the font we found doesn't match the requested type, and we also found
1056 // a candidate above, prefer that one.
1057 if (font && aPresentation != eFontPresentation::Any && candidate) {
1058 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1059 if (hasColorGlyph != PrefersColor(aPresentation)) {
1060 font = std::move(candidate);
1064 TimeDuration elapsed = TimeStamp::Now() - start;
1066 LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
1068 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
1069 Script script = intl::UnicodeProperties::GetScriptCode(aCh);
1070 MOZ_LOG(log, LogLevel::Warning,
1071 ("(textrun-systemfallback-%s) char: u+%6.6x "
1072 "script: %d match: [%s]"
1073 " time: %dus cmaps: %d\n",
1074 (common ? "common" : "global"), aCh, static_cast<int>(script),
1075 (font ? font->GetFontEntry()->Name().get() : "<none>"),
1076 int32_t(elapsed.ToMicroseconds()), cmapCount));
1079 // no match? add to set of non-matching codepoints
1080 if (!font) {
1081 mCodepointsWithNoFonts[level].set(aCh);
1082 } else {
1083 *aVisibility = fallbackFamily.mShared
1084 ? fallbackFamily.mShared->Visibility()
1085 : fallbackFamily.mUnshared->Visibility();
1086 if (aCh == 0xFFFD) {
1087 mReplacementCharFallbackFamily[level] = fallbackFamily;
1091 // track system fallback time
1092 static bool first = true;
1093 int32_t intElapsed =
1094 int32_t(first ? elapsed.ToMilliseconds() : elapsed.ToMicroseconds());
1095 Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST
1096 : Telemetry::SYSTEM_FONT_FALLBACK),
1097 intElapsed);
1098 first = false;
1100 // track the script for which fallback occurred (incremented one make it
1101 // 1-based)
1102 Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT,
1103 int(aRunScript) + 1);
1105 return font.forget();
1108 #define NUM_FALLBACK_FONTS 8
1110 already_AddRefed<gfxFont> gfxPlatformFontList::CommonFontFallback(
1111 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
1112 Script aRunScript, eFontPresentation aPresentation,
1113 const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
1114 AutoTArray<const char*, NUM_FALLBACK_FONTS> defaultFallbacks;
1115 gfxPlatform::GetPlatform()->GetCommonFallbackFonts(
1116 aCh, aRunScript, aPresentation, defaultFallbacks);
1117 GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
1118 FontVisibility level =
1119 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1121 // If a color-emoji presentation is requested, we will check any font found
1122 // to see if it can provide this; if not, we'll remember it as a possible
1123 // candidate but search the remainder of the list for a better choice.
1124 RefPtr<gfxFont> candidateFont;
1125 FontFamily candidateFamily;
1126 auto check = [&](gfxFontEntry* aFontEntry,
1127 FontFamily aFamily) -> already_AddRefed<gfxFont> {
1128 RefPtr<gfxFont> font = aFontEntry->FindOrMakeFont(aMatchStyle);
1129 if (aPresentation < eFontPresentation::EmojiDefault ||
1130 font->HasColorGlyphFor(aCh, aNextCh)) {
1131 aMatchedFamily = aFamily;
1132 return font.forget();
1134 // We want a color glyph but this font only has monochrome; remember it
1135 // (unless we already have a candidate) but continue to search.
1136 if (!candidateFont) {
1137 candidateFont = std::move(font);
1138 candidateFamily = aFamily;
1140 return nullptr;
1143 if (SharedFontList()) {
1144 for (const auto name : defaultFallbacks) {
1145 fontlist::Family* family =
1146 FindSharedFamily(aPresContext, nsDependentCString(name));
1147 if (!family || !IsVisibleToCSS(*family, level)) {
1148 continue;
1150 // XXX(jfkthame) Should we fire the async cmap-loader here, or let it
1151 // always do a potential sync initialization of the family?
1152 family->SearchAllFontsForChar(SharedFontList(), &data);
1153 if (data.mBestMatch) {
1154 RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(family));
1155 if (font) {
1156 return font.forget();
1160 } else {
1161 for (const auto name : defaultFallbacks) {
1162 gfxFontFamily* fallback =
1163 FindFamilyByCanonicalName(nsDependentCString(name));
1164 if (!fallback || !IsVisibleToCSS(*fallback, level)) {
1165 continue;
1167 fallback->FindFontForChar(&data);
1168 if (data.mBestMatch) {
1169 RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(fallback));
1170 if (font) {
1171 return font.forget();
1177 // If we had a candidate that supports the character, but doesn't have the
1178 // desired emoji-style glyph, we'll return it anyhow as nothing better was
1179 // found.
1180 if (candidateFont) {
1181 aMatchedFamily = candidateFamily;
1182 return candidateFont.forget();
1185 return nullptr;
1188 already_AddRefed<gfxFont> gfxPlatformFontList::GlobalFontFallback(
1189 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
1190 Script aRunScript, eFontPresentation aPresentation,
1191 const gfxFontStyle* aMatchStyle, uint32_t& aCmapCount,
1192 FontFamily& aMatchedFamily) {
1193 bool useCmaps = IsFontFamilyWhitelistActive() ||
1194 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1195 FontVisibility level =
1196 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1197 if (!useCmaps) {
1198 // Allow platform-specific fallback code to try and find a usable font
1199 gfxFontEntry* fe = PlatformGlobalFontFallback(aPresContext, aCh, aRunScript,
1200 aMatchStyle, aMatchedFamily);
1201 if (fe) {
1202 if (aMatchedFamily.mShared) {
1203 if (IsVisibleToCSS(*aMatchedFamily.mShared, level)) {
1204 RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle);
1205 if (font) {
1206 if (aPresentation == eFontPresentation::Any) {
1207 return font.forget();
1209 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1210 if (hasColorGlyph == PrefersColor(aPresentation)) {
1211 return font.forget();
1215 } else {
1216 if (IsVisibleToCSS(*aMatchedFamily.mUnshared, level)) {
1217 RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle);
1218 if (font) {
1219 if (aPresentation == eFontPresentation::Any) {
1220 return font.forget();
1222 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1223 if (hasColorGlyph == PrefersColor(aPresentation)) {
1224 return font.forget();
1232 // otherwise, try to find it among local fonts
1233 GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
1234 if (SharedFontList()) {
1235 fontlist::Family* families = SharedFontList()->Families();
1236 if (families) {
1237 for (uint32_t i = 0; i < SharedFontList()->NumFamilies(); i++) {
1238 fontlist::Family& family = families[i];
1239 if (!IsVisibleToCSS(family, level)) {
1240 continue;
1242 if (!family.IsFullyInitialized() &&
1243 StaticPrefs::gfx_font_rendering_fallback_async() &&
1244 !XRE_IsParentProcess()) {
1245 // Start loading all the missing charmaps; but this is async,
1246 // so for now we just continue, ignoring this family.
1247 StartCmapLoadingFromFamily(i);
1248 } else {
1249 family.SearchAllFontsForChar(SharedFontList(), &data);
1250 if (data.mMatchDistance == 0.0) {
1251 // no better style match is possible, so stop searching
1252 break;
1256 if (data.mBestMatch) {
1257 aMatchedFamily = FontFamily(data.mMatchedSharedFamily);
1258 return data.mBestMatch->FindOrMakeFont(aMatchStyle);
1261 } else {
1262 // iterate over all font families to find a font that support the
1263 // character
1264 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
1265 if (!IsVisibleToCSS(*family, level)) {
1266 continue;
1268 // evaluate all fonts in this family for a match
1269 family->FindFontForChar(&data);
1270 if (data.mMatchDistance == 0.0) {
1271 // no better style match is possible, so stop searching
1272 break;
1276 aCmapCount = data.mCmapsTested;
1277 if (data.mBestMatch) {
1278 aMatchedFamily = FontFamily(data.mMatchedFamily);
1279 return data.mBestMatch->FindOrMakeFont(aMatchStyle);
1283 return nullptr;
1286 class StartCmapLoadingRunnable : public mozilla::Runnable {
1287 public:
1288 explicit StartCmapLoadingRunnable(uint32_t aStartIndex)
1289 : Runnable("gfxPlatformFontList::StartCmapLoadingRunnable"),
1290 mStartIndex(aStartIndex) {}
1292 NS_IMETHOD Run() override {
1293 auto* pfl = gfxPlatformFontList::PlatformFontList();
1294 auto* list = pfl->SharedFontList();
1295 if (!list) {
1296 return NS_OK;
1298 if (mStartIndex >= list->NumFamilies()) {
1299 return NS_OK;
1301 if (XRE_IsParentProcess()) {
1302 pfl->StartCmapLoading(list->GetGeneration(), mStartIndex);
1303 } else {
1304 dom::ContentChild::GetSingleton()->SendStartCmapLoading(
1305 list->GetGeneration(), mStartIndex);
1307 return NS_OK;
1310 private:
1311 uint32_t mStartIndex;
1314 void gfxPlatformFontList::StartCmapLoadingFromFamily(uint32_t aStartIndex) {
1315 AutoLock lock(mLock);
1316 if (aStartIndex >= mStartedLoadingCmapsFrom) {
1317 // We already initiated cmap-loading from here or earlier in the list;
1318 // no need to do it again here.
1319 return;
1321 mStartedLoadingCmapsFrom = aStartIndex;
1323 // If we're already on the main thread, don't bother dispatching a runnable
1324 // here to kick off the loading process, just do it directly.
1325 if (NS_IsMainThread()) {
1326 auto* list = SharedFontList();
1327 if (XRE_IsParentProcess()) {
1328 StartCmapLoading(list->GetGeneration(), aStartIndex);
1329 } else {
1330 dom::ContentChild::GetSingleton()->SendStartCmapLoading(
1331 list->GetGeneration(), aStartIndex);
1333 } else {
1334 NS_DispatchToMainThread(new StartCmapLoadingRunnable(aStartIndex));
1338 class LoadCmapsRunnable : public CancelableRunnable {
1339 class WillShutdownObserver : public nsIObserver {
1340 public:
1341 NS_DECL_ISUPPORTS
1342 NS_DECL_NSIOBSERVER
1344 explicit WillShutdownObserver(LoadCmapsRunnable* aRunnable)
1345 : mRunnable(aRunnable) {}
1347 void Remove() {
1348 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1349 if (obs) {
1350 obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
1352 mRunnable = nullptr;
1355 protected:
1356 virtual ~WillShutdownObserver() = default;
1358 LoadCmapsRunnable* mRunnable;
1361 public:
1362 explicit LoadCmapsRunnable(uint32_t aGeneration, uint32_t aFamilyIndex)
1363 : CancelableRunnable("gfxPlatformFontList::LoadCmapsRunnable"),
1364 mGeneration(aGeneration),
1365 mStartIndex(aFamilyIndex),
1366 mIndex(aFamilyIndex) {
1367 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1368 if (obs) {
1369 mObserver = new WillShutdownObserver(this);
1370 obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
1374 virtual ~LoadCmapsRunnable() {
1375 if (mObserver) {
1376 mObserver->Remove();
1380 // Reset the current family index, if the value passed is earlier than our
1381 // original starting position. We don't "reset" if it would move the current
1382 // position forward, or back into the already-scanned range.
1383 // We could optimize further by remembering the current position reached,
1384 // and then skipping ahead from the original start, but it doesn't seem worth
1385 // extra complexity for a task that usually only happens once, and already-
1386 // processed families will be skipped pretty quickly in Run() anyhow.
1387 void MaybeResetIndex(uint32_t aFamilyIndex) {
1388 if (aFamilyIndex < mStartIndex) {
1389 mStartIndex = aFamilyIndex;
1390 mIndex = aFamilyIndex;
1394 nsresult Cancel() override {
1395 mIsCanceled = true;
1396 return NS_OK;
1399 NS_IMETHOD Run() override {
1400 if (mIsCanceled) {
1401 return NS_OK;
1403 auto* pfl = gfxPlatformFontList::PlatformFontList();
1404 auto* list = pfl->SharedFontList();
1405 MOZ_ASSERT(list);
1406 if (!list) {
1407 return NS_OK;
1409 if (mGeneration != list->GetGeneration()) {
1410 return NS_OK;
1412 uint32_t numFamilies = list->NumFamilies();
1413 if (mIndex >= numFamilies) {
1414 return NS_OK;
1416 auto* families = list->Families();
1417 // Skip any families that are already initialized.
1418 while (mIndex < numFamilies && families[mIndex].IsFullyInitialized()) {
1419 ++mIndex;
1421 // Fully process one family, and advance index.
1422 if (mIndex < numFamilies) {
1423 Unused << pfl->InitializeFamily(&families[mIndex], true);
1424 ++mIndex;
1426 // If there are more families to initialize, post ourselves back to the
1427 // idle queue to handle the next one; otherwise we're finished and we need
1428 // to notify content processes to update their rendering.
1429 if (mIndex < numFamilies) {
1430 RefPtr<CancelableRunnable> task = this;
1431 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
1432 } else {
1433 pfl->Lock();
1434 pfl->CancelLoadCmapsTask();
1435 pfl->InitializeCodepointsWithNoFonts();
1436 dom::ContentParent::NotifyUpdatedFonts(false);
1437 pfl->Unlock();
1439 return NS_OK;
1442 private:
1443 uint32_t mGeneration;
1444 uint32_t mStartIndex;
1445 uint32_t mIndex;
1446 bool mIsCanceled = false;
1448 RefPtr<WillShutdownObserver> mObserver;
1451 NS_IMPL_ISUPPORTS(LoadCmapsRunnable::WillShutdownObserver, nsIObserver)
1453 NS_IMETHODIMP
1454 LoadCmapsRunnable::WillShutdownObserver::Observe(nsISupports* aSubject,
1455 const char* aTopic,
1456 const char16_t* aData) {
1457 if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
1458 if (mRunnable) {
1459 mRunnable->Cancel();
1461 } else {
1462 MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
1464 return NS_OK;
1467 void gfxPlatformFontList::StartCmapLoading(uint32_t aGeneration,
1468 uint32_t aStartIndex) {
1469 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
1470 if (aGeneration != SharedFontList()->GetGeneration()) {
1471 return;
1473 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1474 return;
1476 if (mLoadCmapsRunnable) {
1477 // We already have a runnable; just make sure it covers the full range of
1478 // families needed.
1479 static_cast<LoadCmapsRunnable*>(mLoadCmapsRunnable.get())
1480 ->MaybeResetIndex(aStartIndex);
1481 return;
1483 mLoadCmapsRunnable = new LoadCmapsRunnable(aGeneration, aStartIndex);
1484 RefPtr<CancelableRunnable> task = mLoadCmapsRunnable;
1485 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
1488 gfxFontFamily* gfxPlatformFontList::CheckFamily(gfxFontFamily* aFamily) {
1489 if (aFamily && !aFamily->HasStyles()) {
1490 aFamily->FindStyleVariations();
1493 if (aFamily && aFamily->FontListLength() == 0) {
1494 // Failed to load any faces for this family, so discard it.
1495 nsAutoCString key;
1496 GenerateFontListKey(aFamily->Name(), key);
1497 mFontFamilies.Remove(key);
1498 return nullptr;
1501 return aFamily;
1504 bool gfxPlatformFontList::FindAndAddFamilies(
1505 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
1506 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
1507 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1508 gfxFloat aDevToCssSize) {
1509 AutoLock lock(mLock);
1511 #ifdef DEBUG
1512 auto initialLength = aOutput->Length();
1513 #endif
1515 bool didFind =
1516 FindAndAddFamiliesLocked(aPresContext, aGeneric, aFamily, aOutput, aFlags,
1517 aStyle, aLanguage, aDevToCssSize);
1518 #ifdef DEBUG
1519 auto finalLength = aOutput->Length();
1520 // Validate the expectation that the output-array grows if we return true,
1521 // or remains the same (probably empty) if we return false.
1522 MOZ_ASSERT_IF(didFind, finalLength > initialLength);
1523 MOZ_ASSERT_IF(!didFind, finalLength == initialLength);
1524 #endif
1526 return didFind;
1529 bool gfxPlatformFontList::FindAndAddFamiliesLocked(
1530 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
1531 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
1532 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1533 gfxFloat aDevToCssSize) {
1534 nsAutoCString key;
1535 GenerateFontListKey(aFamily, key);
1537 bool allowHidden = bool(aFlags & FindFamiliesFlags::eSearchHiddenFamilies);
1538 FontVisibility visibilityLevel =
1539 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1541 // If this font lookup is the result of resolving a CSS generic (not a direct
1542 // font-family request by the page), and RFP settings allow generics to be
1543 // unrestricted, bump the effective visibility level applied here so as to
1544 // allow user-installed fonts to be used.
1545 if (visibilityLevel < FontVisibility::User &&
1546 aGeneric != StyleGenericFontFamily::None &&
1547 !aPresContext->Document()->ShouldResistFingerprinting(
1548 RFPTarget::FontVisibilityRestrictGenerics)) {
1549 visibilityLevel = FontVisibility::User;
1552 if (SharedFontList()) {
1553 fontlist::Family* family = SharedFontList()->FindFamily(key);
1554 // If not found, and other family names have not yet been initialized,
1555 // initialize the rest of the list and try again. This is done lazily
1556 // since reading name table entries is expensive.
1557 // Although ASCII localized family names are possible they don't occur
1558 // in practice, so avoid pulling in names at startup.
1559 if (!family && !mOtherFamilyNamesInitialized) {
1560 bool triggerLoading = true;
1561 bool mayDefer =
1562 !(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading);
1563 if (IsAscii(key)) {
1564 // If `key` is an ASCII name, only trigger loading if it includes a
1565 // space, and the "base" name (up to the last space) exists as a known
1566 // family, so that this might be a legacy styled-family name.
1567 const char* data = key.BeginReading();
1568 int32_t index = key.Length();
1569 while (--index > 0) {
1570 if (data[index] == ' ') {
1571 break;
1574 if (index <= 0 ||
1575 !SharedFontList()->FindFamily(nsAutoCString(key.get(), index))) {
1576 triggerLoading = false;
1579 if (triggerLoading) {
1580 if (InitOtherFamilyNames(mayDefer)) {
1581 family = SharedFontList()->FindFamily(key);
1584 if (!family && !mOtherFamilyNamesInitialized &&
1585 !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
1586 AddToMissedNames(key);
1589 // Check whether the family we found is actually allowed to be looked up,
1590 // according to current font-visibility prefs.
1591 if (family) {
1592 bool visible = IsVisibleToCSS(*family, visibilityLevel);
1593 if (visible || (allowHidden && family->IsHidden())) {
1594 aOutput->AppendElement(FamilyAndGeneric(family, aGeneric));
1595 return true;
1597 if (aPresContext) {
1598 aPresContext->ReportBlockedFontFamily(*family);
1601 return false;
1604 NS_ASSERTION(mFontFamilies.Count() != 0,
1605 "system font list was not initialized correctly");
1607 auto isBlockedByVisibilityLevel = [=](gfxFontFamily* aFamily) -> bool {
1608 bool visible = IsVisibleToCSS(*aFamily, visibilityLevel);
1609 if (visible || (allowHidden && aFamily->IsHidden())) {
1610 return false;
1612 if (aPresContext) {
1613 aPresContext->ReportBlockedFontFamily(*aFamily);
1615 return true;
1618 // lookup in canonical (i.e. English) family name list
1619 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
1620 if (familyEntry) {
1621 if (isBlockedByVisibilityLevel(familyEntry)) {
1622 return false;
1626 // if not found, lookup in other family names list (mostly localized names)
1627 if (!familyEntry) {
1628 familyEntry = mOtherFamilyNames.GetWeak(key);
1630 if (familyEntry) {
1631 if (isBlockedByVisibilityLevel(familyEntry)) {
1632 return false;
1636 // if still not found and other family names not yet fully initialized,
1637 // initialize the rest of the list and try again. this is done lazily
1638 // since reading name table entries is expensive.
1639 // although ASCII localized family names are possible they don't occur
1640 // in practice so avoid pulling in names at startup
1641 if (!familyEntry && !mOtherFamilyNamesInitialized && !IsAscii(aFamily)) {
1642 InitOtherFamilyNames(
1643 !(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading));
1644 familyEntry = mOtherFamilyNames.GetWeak(key);
1645 if (!familyEntry && !mOtherFamilyNamesInitialized &&
1646 !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
1647 // localized family names load timed out, add name to list of
1648 // names to check after localized names are loaded
1649 AddToMissedNames(key);
1651 if (familyEntry) {
1652 if (isBlockedByVisibilityLevel(familyEntry)) {
1653 return false;
1658 familyEntry = CheckFamily(familyEntry);
1660 // If we failed to find the requested family, check for a space in the
1661 // name; if found, and if the "base" name (up to the last space) exists
1662 // as a family, then this might be a legacy GDI-style family name for
1663 // an additional weight/width. Try searching the faces of the base family
1664 // and create any corresponding legacy families.
1665 if (!familyEntry &&
1666 !(aFlags & FindFamiliesFlags::eNoSearchForLegacyFamilyNames)) {
1667 // We don't have nsAString::RFindChar, so look for a space manually
1668 const char* data = aFamily.BeginReading();
1669 int32_t index = aFamily.Length();
1670 while (--index > 0) {
1671 if (data[index] == ' ') {
1672 break;
1675 if (index > 0) {
1676 gfxFontFamily* base =
1677 FindUnsharedFamily(aPresContext, Substring(aFamily, 0, index),
1678 FindFamiliesFlags::eNoSearchForLegacyFamilyNames);
1679 // If we found the "base" family name, and if it has members with
1680 // legacy names, this will add corresponding font-family entries to
1681 // the mOtherFamilyNames list; then retry the legacy-family search.
1682 if (base && base->CheckForLegacyFamilyNames(this)) {
1683 familyEntry = mOtherFamilyNames.GetWeak(key);
1685 if (familyEntry) {
1686 if (isBlockedByVisibilityLevel(familyEntry)) {
1687 return false;
1693 if (familyEntry) {
1694 aOutput->AppendElement(FamilyAndGeneric(familyEntry, aGeneric));
1695 return true;
1698 return false;
1701 void gfxPlatformFontList::AddToMissedNames(const nsCString& aKey) {
1702 if (!mOtherNamesMissed) {
1703 mOtherNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
1705 mOtherNamesMissed->Insert(aKey);
1708 fontlist::Family* gfxPlatformFontList::FindSharedFamily(
1709 nsPresContext* aPresContext, const nsACString& aFamily,
1710 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1711 gfxFloat aDevToCss) {
1712 if (!SharedFontList()) {
1713 return nullptr;
1715 AutoTArray<FamilyAndGeneric, 1> families;
1716 if (!FindAndAddFamiliesLocked(aPresContext, StyleGenericFontFamily::None,
1717 aFamily, &families, aFlags, aStyle, aLanguage,
1718 aDevToCss) ||
1719 !families[0].mFamily.mShared) {
1720 return nullptr;
1722 fontlist::Family* family = families[0].mFamily.mShared;
1723 if (!family->IsInitialized()) {
1724 if (!InitializeFamily(family)) {
1725 return nullptr;
1728 return family;
1731 class InitializeFamilyRunnable : public mozilla::Runnable {
1732 public:
1733 explicit InitializeFamilyRunnable(uint32_t aFamilyIndex, bool aLoadCmaps)
1734 : Runnable("gfxPlatformFontList::InitializeFamilyRunnable"),
1735 mIndex(aFamilyIndex),
1736 mLoadCmaps(aLoadCmaps) {}
1738 NS_IMETHOD Run() override {
1739 auto list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
1740 if (!list) {
1741 return NS_OK;
1743 if (mIndex >= list->NumFamilies()) {
1744 // Out of range? Maybe the list got reinitialized since this request
1745 // was posted - just ignore it.
1746 return NS_OK;
1748 dom::ContentChild::GetSingleton()->SendInitializeFamily(
1749 list->GetGeneration(), mIndex, mLoadCmaps);
1750 return NS_OK;
1753 private:
1754 uint32_t mIndex;
1755 bool mLoadCmaps;
1758 bool gfxPlatformFontList::InitializeFamily(fontlist::Family* aFamily,
1759 bool aLoadCmaps) {
1760 MOZ_ASSERT(SharedFontList());
1761 auto list = SharedFontList();
1762 if (!XRE_IsParentProcess()) {
1763 auto* families = list->Families();
1764 if (!families) {
1765 return false;
1767 uint32_t index = aFamily - families;
1768 if (index >= list->NumFamilies()) {
1769 return false;
1771 if (NS_IsMainThread()) {
1772 dom::ContentChild::GetSingleton()->SendInitializeFamily(
1773 list->GetGeneration(), index, aLoadCmaps);
1774 } else {
1775 NS_DispatchToMainThread(new InitializeFamilyRunnable(index, aLoadCmaps));
1777 return aFamily->IsInitialized();
1780 if (!aFamily->IsInitialized()) {
1781 // The usual case: we're being asked to populate the face list.
1782 AutoTArray<fontlist::Face::InitData, 16> faceList;
1783 GetFacesInitDataForFamily(aFamily, faceList, aLoadCmaps);
1784 aFamily->AddFaces(list, faceList);
1785 } else {
1786 // The family's face list was already initialized, but if aLoadCmaps is
1787 // true we also want to eagerly load character maps. This is used when a
1788 // child process is doing SearchAllFontsForChar, to have the parent load
1789 // all the cmaps at once and reduce IPC traffic (and content-process file
1790 // access overhead, which is crippling for DirectWrite on Windows).
1791 if (aLoadCmaps) {
1792 auto* faces = aFamily->Faces(list);
1793 if (faces) {
1794 for (size_t i = 0; i < aFamily->NumFaces(); i++) {
1795 auto* face = faces[i].ToPtr<fontlist::Face>(list);
1796 if (face && face->mCharacterMap.IsNull()) {
1797 // We don't want to cache this font entry, as the parent will most
1798 // likely never use it again; it's just to populate the charmap for
1799 // the benefit of the child process.
1800 RefPtr<gfxFontEntry> fe = CreateFontEntry(face, aFamily);
1801 if (fe) {
1802 fe->ReadCMAP();
1810 if (aLoadCmaps && aFamily->IsInitialized()) {
1811 aFamily->SetupFamilyCharMap(list);
1814 return aFamily->IsInitialized();
1817 gfxFontEntry* gfxPlatformFontList::FindFontForFamily(
1818 nsPresContext* aPresContext, const nsACString& aFamily,
1819 const gfxFontStyle* aStyle) {
1820 AutoLock lock(mLock);
1822 nsAutoCString key;
1823 GenerateFontListKey(aFamily, key);
1825 FontFamily family = FindFamily(aPresContext, key);
1826 if (family.IsNull()) {
1827 return nullptr;
1829 if (family.mShared) {
1830 auto face = family.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
1831 if (!face) {
1832 return nullptr;
1834 return GetOrCreateFontEntryLocked(face, family.mShared);
1836 return family.mUnshared->FindFontForStyle(*aStyle);
1839 gfxFontEntry* gfxPlatformFontList::GetOrCreateFontEntryLocked(
1840 fontlist::Face* aFace, const fontlist::Family* aFamily) {
1841 return mFontEntries
1842 .LookupOrInsertWith(aFace,
1843 [=] { return CreateFontEntry(aFace, aFamily); })
1844 .get();
1847 void gfxPlatformFontList::AddOtherFamilyNames(
1848 gfxFontFamily* aFamilyEntry, const nsTArray<nsCString>& aOtherFamilyNames) {
1849 AutoLock lock(mLock);
1851 for (const auto& name : aOtherFamilyNames) {
1852 nsAutoCString key;
1853 GenerateFontListKey(name, key);
1855 mOtherFamilyNames.LookupOrInsertWith(key, [&] {
1856 LOG_FONTLIST(
1857 ("(fontlist-otherfamily) canonical family: %s, other family: %s\n",
1858 aFamilyEntry->Name().get(), name.get()));
1859 if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
1860 aFamilyEntry->SetBadUnderlineFamily();
1862 return RefPtr{aFamilyEntry};
1867 void gfxPlatformFontList::AddFullnameLocked(gfxFontEntry* aFontEntry,
1868 const nsCString& aFullname) {
1869 mExtraNames->mFullnames.LookupOrInsertWith(aFullname, [&] {
1870 LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
1871 aFontEntry->Name().get(), aFullname.get()));
1872 return RefPtr{aFontEntry};
1876 void gfxPlatformFontList::AddPostscriptNameLocked(
1877 gfxFontEntry* aFontEntry, const nsCString& aPostscriptName) {
1878 mExtraNames->mPostscriptNames.LookupOrInsertWith(aPostscriptName, [&] {
1879 LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
1880 aFontEntry->Name().get(), aPostscriptName.get()));
1881 return RefPtr{aFontEntry};
1885 bool gfxPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
1886 nsACString& aFamilyName) {
1887 AutoLock lock(mLock);
1888 FontFamily family = FindFamily(nullptr, aFontName);
1889 if (family.IsNull()) {
1890 return false;
1892 return GetLocalizedFamilyName(family, aFamilyName);
1895 bool gfxPlatformFontList::GetLocalizedFamilyName(const FontFamily& aFamily,
1896 nsACString& aFamilyName) {
1897 if (aFamily.mShared) {
1898 aFamilyName = SharedFontList()->LocalizedFamilyName(aFamily.mShared);
1899 return true;
1901 if (aFamily.mUnshared) {
1902 aFamily.mUnshared->LocalizedName(aFamilyName);
1903 return true;
1905 return false; // leaving the aFamilyName outparam untouched
1908 FamilyAndGeneric gfxPlatformFontList::GetDefaultFontFamily(
1909 const nsACString& aLangGroup, const nsACString& aGenericFamily) {
1910 if (NS_WARN_IF(aLangGroup.IsEmpty()) ||
1911 NS_WARN_IF(aGenericFamily.IsEmpty())) {
1912 return FamilyAndGeneric();
1915 AutoLock lock(mLock);
1917 nsAutoCString value;
1918 AutoTArray<nsCString, 4> names;
1919 if (mFontPrefs->LookupNameList(PrefName(aGenericFamily, aLangGroup), value)) {
1920 gfxFontUtils::ParseFontList(value, names);
1923 for (const nsCString& name : names) {
1924 FontFamily family = FindFamily(nullptr, name);
1925 if (!family.IsNull()) {
1926 return FamilyAndGeneric(family);
1930 return FamilyAndGeneric();
1933 ShmemCharMapHashEntry::ShmemCharMapHashEntry(const gfxSparseBitSet* aCharMap)
1934 : mList(gfxPlatformFontList::PlatformFontList()->SharedFontList()),
1935 mHash(aCharMap->GetChecksum()) {
1936 size_t len = SharedBitSet::RequiredSize(*aCharMap);
1937 mCharMap = mList->Alloc(len);
1938 SharedBitSet::Create(mCharMap.ToPtr(mList, len), len, *aCharMap);
1941 fontlist::Pointer gfxPlatformFontList::GetShmemCharMapLocked(
1942 const gfxSparseBitSet* aCmap) {
1943 auto* entry = mShmemCharMaps.GetEntry(aCmap);
1944 if (!entry) {
1945 entry = mShmemCharMaps.PutEntry(aCmap);
1947 return entry->GetCharMap();
1950 // Lookup aCmap in the shared cmap set, adding if not already present.
1951 // This is the only way for a reference to a gfxCharacterMap to be acquired
1952 // by another thread than its original creator.
1953 already_AddRefed<gfxCharacterMap> gfxPlatformFontList::FindCharMap(
1954 gfxCharacterMap* aCmap) {
1955 // Lock to prevent potentially racing against MaybeRemoveCmap.
1956 AutoLock lock(mLock);
1958 // Find existing entry or insert a new one (which will add a reference).
1959 aCmap->CalcHash();
1960 aCmap->mShared = true; // Set the shared flag in preparation for adding
1961 // to the global table.
1962 RefPtr cmap = mSharedCmaps.PutEntry(aCmap)->GetKey();
1964 // If we ended up finding a different, pre-existing entry, clear the
1965 // shared flag on this one so that it'll get deleted on Release().
1966 if (cmap.get() != aCmap) {
1967 aCmap->mShared = false;
1970 return cmap.forget();
1973 // Potentially remove the charmap from the shared cmap set. This is called
1974 // when a user of the charmap drops a reference and the refcount goes to 1;
1975 // in that case, it is possible our shared set is the only remaining user
1976 // of the object, and we should remove it.
1977 // Note that aCharMap might have already been freed, so we must not try to
1978 // dereference it until we have checked that it's still present in our table.
1979 void gfxPlatformFontList::MaybeRemoveCmap(gfxCharacterMap* aCharMap) {
1980 // Lock so that nobody else can get a reference via FindCharMap while we're
1981 // checking here.
1982 AutoLock lock(mLock);
1984 // Skip lookups during teardown.
1985 if (!mSharedCmaps.Count()) {
1986 return;
1989 // aCharMap needs to match the entry and be the same ptr and still have a
1990 // refcount of exactly 1 (i.e. we hold the only reference) before removing.
1991 // If we're racing another thread, it might already have been removed, in
1992 // which case GetEntry will not find it and we won't try to dereference the
1993 // already-freed pointer.
1994 CharMapHashKey* found =
1995 mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
1996 if (found && found->GetKey() == aCharMap && aCharMap->RefCount() == 1) {
1997 // Forget our reference to the object that's being deleted, without
1998 // calling Release() on it.
1999 Unused << found->mCharMap.forget();
2001 // Do the deletion.
2002 delete aCharMap;
2004 // Log this as a "Release" to keep leak-checking correct.
2005 NS_LOG_RELEASE(aCharMap, 0, "gfxCharacterMap");
2007 mSharedCmaps.RemoveEntry(found);
2011 static void GetSystemUIFontFamilies([[maybe_unused]] nsAtom* aLangGroup,
2012 nsTArray<nsCString>& aFamilies) {
2013 // TODO: On macOS, use CTCreateUIFontForLanguage or such thing (though the
2014 // code below ends up using [NSFont systemFontOfSize: 0.0].
2015 nsFont systemFont;
2016 gfxFontStyle fontStyle;
2017 nsAutoString systemFontName;
2018 if (!LookAndFeel::GetFont(StyleSystemFont::Menu, systemFontName, fontStyle)) {
2019 return;
2021 systemFontName.Trim("\"'");
2022 CopyUTF16toUTF8(systemFontName, *aFamilies.AppendElement());
2025 void gfxPlatformFontList::ResolveGenericFontNames(
2026 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2027 eFontPrefLang aPrefLang, PrefFontList* aGenericFamilies) {
2028 const char* langGroupStr = GetPrefLangName(aPrefLang);
2029 const char* generic = GetGenericName(aGenericType);
2031 if (!generic) {
2032 return;
2035 AutoTArray<nsCString, 4> genericFamilies;
2037 // load family for "font.name.generic.lang"
2038 PrefName prefName(generic, langGroupStr);
2039 nsAutoCString value;
2040 if (mFontPrefs->LookupName(prefName, value)) {
2041 gfxFontUtils::ParseFontList(value, genericFamilies);
2044 // load fonts for "font.name-list.generic.lang"
2045 if (mFontPrefs->LookupNameList(prefName, value)) {
2046 gfxFontUtils::ParseFontList(value, genericFamilies);
2049 nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
2050 MOZ_ASSERT(langGroup, "null lang group for pref lang");
2052 if (aGenericType == StyleGenericFontFamily::SystemUi) {
2053 GetSystemUIFontFamilies(langGroup, genericFamilies);
2056 GetFontFamiliesFromGenericFamilies(
2057 aPresContext, aGenericType, genericFamilies, langGroup, aGenericFamilies);
2059 #if 0 // dump out generic mappings
2060 printf("%s ===> ", NamePref(generic, langGroupStr).get());
2061 for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
2062 if (k > 0) printf(", ");
2063 printf("%s", (*aGenericFamilies)[k].mIsShared
2064 ? (*aGenericFamilies)[k].mShared->DisplayName().AsString(SharedFontList()).get()
2065 : (*aGenericFamilies)[k].mUnshared->Name().get());
2067 printf("\n");
2068 #endif
2071 void gfxPlatformFontList::ResolveEmojiFontNames(
2072 nsPresContext* aPresContext, PrefFontList* aGenericFamilies) {
2073 // emoji preference has no lang name
2074 AutoTArray<nsCString, 4> genericFamilies;
2076 nsAutoCString value;
2077 if (mFontPrefs->LookupNameList(PrefName("emoji", ""), value)) {
2078 gfxFontUtils::ParseFontList(value, genericFamilies);
2081 GetFontFamiliesFromGenericFamilies(
2082 aPresContext, StyleGenericFontFamily::MozEmoji, genericFamilies, nullptr,
2083 aGenericFamilies);
2086 void gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(
2087 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2088 nsTArray<nsCString>& aGenericNameFamilies, nsAtom* aLangGroup,
2089 PrefFontList* aGenericFamilies) {
2090 // lookup and add platform fonts uniquely
2091 for (const nsCString& genericFamily : aGenericNameFamilies) {
2092 AutoTArray<FamilyAndGeneric, 10> families;
2093 FindAndAddFamiliesLocked(aPresContext, aGenericType, genericFamily,
2094 &families, FindFamiliesFlags(0), nullptr,
2095 aLangGroup);
2096 for (const FamilyAndGeneric& f : families) {
2097 if (!aGenericFamilies->Contains(f.mFamily)) {
2098 aGenericFamilies->AppendElement(f.mFamily);
2104 gfxPlatformFontList::PrefFontList*
2105 gfxPlatformFontList::GetPrefFontsLangGroupLocked(
2106 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2107 eFontPrefLang aPrefLang) {
2108 if (aGenericType == StyleGenericFontFamily::MozEmoji ||
2109 aPrefLang == eFontPrefLang_Emoji) {
2110 // Emoji font has no lang
2111 PrefFontList* prefFonts = mEmojiPrefFont.get();
2112 if (MOZ_UNLIKELY(!prefFonts)) {
2113 prefFonts = new PrefFontList;
2114 ResolveEmojiFontNames(aPresContext, prefFonts);
2115 mEmojiPrefFont.reset(prefFonts);
2117 return prefFonts;
2120 auto index = static_cast<size_t>(aGenericType);
2121 PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][index].get();
2122 if (MOZ_UNLIKELY(!prefFonts)) {
2123 prefFonts = new PrefFontList;
2124 ResolveGenericFontNames(aPresContext, aGenericType, aPrefLang, prefFonts);
2125 mLangGroupPrefFonts[aPrefLang][index].reset(prefFonts);
2127 return prefFonts;
2130 void gfxPlatformFontList::AddGenericFonts(
2131 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2132 nsAtom* aLanguage, nsTArray<FamilyAndGeneric>& aFamilyList) {
2133 AutoLock lock(mLock);
2135 // map lang ==> langGroup
2136 nsAtom* langGroup = GetLangGroup(aLanguage);
2138 // langGroup ==> prefLang
2139 eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
2141 // lookup pref fonts
2142 PrefFontList* prefFonts =
2143 GetPrefFontsLangGroupLocked(aPresContext, aGenericType, prefLang);
2145 if (!prefFonts->IsEmpty()) {
2146 aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
2147 for (auto& f : *prefFonts) {
2148 aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
2153 static nsAtom* PrefLangToLangGroups(uint32_t aIndex) {
2154 // static array here avoids static constructor
2155 static nsAtom* gPrefLangToLangGroups[] = {
2156 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
2157 #include "gfxFontPrefLangList.h"
2158 #undef FONT_PREF_LANG
2161 return aIndex < ArrayLength(gPrefLangToLangGroups)
2162 ? gPrefLangToLangGroups[aIndex]
2163 : nsGkAtoms::Unicode;
2166 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(const char* aLang) {
2167 if (!aLang || !aLang[0]) {
2168 return eFontPrefLang_Others;
2170 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
2171 if (!nsCRT::strcasecmp(gPrefLangNames[i], aLang)) {
2172 return eFontPrefLang(i);
2175 return eFontPrefLang_Others;
2178 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(nsAtom* aLang) {
2179 if (!aLang) return eFontPrefLang_Others;
2180 nsAutoCString lang;
2181 aLang->ToUTF8String(lang);
2182 return GetFontPrefLangFor(lang.get());
2185 nsAtom* gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang) {
2186 // the special CJK set pref lang should be resolved into separate
2187 // calls to individual CJK pref langs before getting here
2188 NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
2190 return PrefLangToLangGroups(uint32_t(aLang));
2193 const char* gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang) {
2194 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
2195 return gPrefLangNames[uint32_t(aLang)];
2197 return nullptr;
2200 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(uint32_t aCh) {
2201 switch (ublock_getCode(aCh)) {
2202 case UBLOCK_BASIC_LATIN:
2203 case UBLOCK_LATIN_1_SUPPLEMENT:
2204 case UBLOCK_LATIN_EXTENDED_A:
2205 case UBLOCK_LATIN_EXTENDED_B:
2206 case UBLOCK_IPA_EXTENSIONS:
2207 case UBLOCK_SPACING_MODIFIER_LETTERS:
2208 case UBLOCK_LATIN_EXTENDED_ADDITIONAL:
2209 case UBLOCK_LATIN_EXTENDED_C:
2210 case UBLOCK_LATIN_EXTENDED_D:
2211 case UBLOCK_LATIN_EXTENDED_E:
2212 case UBLOCK_PHONETIC_EXTENSIONS:
2213 return eFontPrefLang_Western;
2214 case UBLOCK_GREEK:
2215 case UBLOCK_GREEK_EXTENDED:
2216 return eFontPrefLang_Greek;
2217 case UBLOCK_CYRILLIC:
2218 case UBLOCK_CYRILLIC_SUPPLEMENT:
2219 case UBLOCK_CYRILLIC_EXTENDED_A:
2220 case UBLOCK_CYRILLIC_EXTENDED_B:
2221 case UBLOCK_CYRILLIC_EXTENDED_C:
2222 return eFontPrefLang_Cyrillic;
2223 case UBLOCK_ARMENIAN:
2224 return eFontPrefLang_Armenian;
2225 case UBLOCK_HEBREW:
2226 return eFontPrefLang_Hebrew;
2227 case UBLOCK_ARABIC:
2228 case UBLOCK_ARABIC_PRESENTATION_FORMS_A:
2229 case UBLOCK_ARABIC_PRESENTATION_FORMS_B:
2230 case UBLOCK_ARABIC_SUPPLEMENT:
2231 case UBLOCK_ARABIC_EXTENDED_A:
2232 case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS:
2233 return eFontPrefLang_Arabic;
2234 case UBLOCK_DEVANAGARI:
2235 case UBLOCK_DEVANAGARI_EXTENDED:
2236 return eFontPrefLang_Devanagari;
2237 case UBLOCK_BENGALI:
2238 return eFontPrefLang_Bengali;
2239 case UBLOCK_GURMUKHI:
2240 return eFontPrefLang_Gurmukhi;
2241 case UBLOCK_GUJARATI:
2242 return eFontPrefLang_Gujarati;
2243 case UBLOCK_ORIYA:
2244 return eFontPrefLang_Oriya;
2245 case UBLOCK_TAMIL:
2246 return eFontPrefLang_Tamil;
2247 case UBLOCK_TELUGU:
2248 return eFontPrefLang_Telugu;
2249 case UBLOCK_KANNADA:
2250 return eFontPrefLang_Kannada;
2251 case UBLOCK_MALAYALAM:
2252 return eFontPrefLang_Malayalam;
2253 case UBLOCK_SINHALA:
2254 case UBLOCK_SINHALA_ARCHAIC_NUMBERS:
2255 return eFontPrefLang_Sinhala;
2256 case UBLOCK_THAI:
2257 return eFontPrefLang_Thai;
2258 case UBLOCK_TIBETAN:
2259 return eFontPrefLang_Tibetan;
2260 case UBLOCK_GEORGIAN:
2261 case UBLOCK_GEORGIAN_SUPPLEMENT:
2262 case UBLOCK_GEORGIAN_EXTENDED:
2263 return eFontPrefLang_Georgian;
2264 case UBLOCK_HANGUL_JAMO:
2265 case UBLOCK_HANGUL_COMPATIBILITY_JAMO:
2266 case UBLOCK_HANGUL_SYLLABLES:
2267 case UBLOCK_HANGUL_JAMO_EXTENDED_A:
2268 case UBLOCK_HANGUL_JAMO_EXTENDED_B:
2269 return eFontPrefLang_Korean;
2270 case UBLOCK_ETHIOPIC:
2271 case UBLOCK_ETHIOPIC_EXTENDED:
2272 case UBLOCK_ETHIOPIC_SUPPLEMENT:
2273 case UBLOCK_ETHIOPIC_EXTENDED_A:
2274 return eFontPrefLang_Ethiopic;
2275 case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS:
2276 case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED:
2277 return eFontPrefLang_Canadian;
2278 case UBLOCK_KHMER:
2279 case UBLOCK_KHMER_SYMBOLS:
2280 return eFontPrefLang_Khmer;
2281 case UBLOCK_CJK_RADICALS_SUPPLEMENT:
2282 case UBLOCK_KANGXI_RADICALS:
2283 case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS:
2284 case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
2285 case UBLOCK_HIRAGANA:
2286 case UBLOCK_KATAKANA:
2287 case UBLOCK_BOPOMOFO:
2288 case UBLOCK_KANBUN:
2289 case UBLOCK_BOPOMOFO_EXTENDED:
2290 case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS:
2291 case UBLOCK_CJK_COMPATIBILITY:
2292 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:
2293 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS:
2294 case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS:
2295 case UBLOCK_CJK_COMPATIBILITY_FORMS:
2296 case UBLOCK_SMALL_FORM_VARIANTS:
2297 case UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS:
2298 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:
2299 case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:
2300 case UBLOCK_KATAKANA_PHONETIC_EXTENSIONS:
2301 case UBLOCK_CJK_STROKES:
2302 case UBLOCK_VERTICAL_FORMS:
2303 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C:
2304 case UBLOCK_KANA_SUPPLEMENT:
2305 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D:
2306 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E:
2307 case UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION:
2308 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F:
2309 case UBLOCK_KANA_EXTENDED_A:
2310 return eFontPrefLang_CJKSet;
2311 case UBLOCK_MATHEMATICAL_OPERATORS:
2312 case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS:
2313 case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A:
2314 case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B:
2315 case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS:
2316 return eFontPrefLang_Mathematics;
2317 default:
2318 return eFontPrefLang_Others;
2322 bool gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang) {
2323 switch (aLang) {
2324 case eFontPrefLang_Japanese:
2325 case eFontPrefLang_ChineseTW:
2326 case eFontPrefLang_ChineseCN:
2327 case eFontPrefLang_ChineseHK:
2328 case eFontPrefLang_Korean:
2329 case eFontPrefLang_CJKSet:
2330 return true;
2331 default:
2332 return false;
2336 void gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[],
2337 uint32_t& aLen, eFontPrefLang aCharLang,
2338 eFontPrefLang aPageLang) {
2339 AutoLock lock(mLock);
2340 if (IsLangCJK(aCharLang)) {
2341 AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
2342 } else {
2343 AppendPrefLang(aPrefLangs, aLen, aCharLang);
2346 AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
2349 void gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[],
2350 uint32_t& aLen,
2351 eFontPrefLang aCharLang,
2352 eFontPrefLang aPageLang) {
2353 // prefer the lang specified by the page *if* CJK
2354 if (IsLangCJK(aPageLang)) {
2355 AppendPrefLang(aPrefLangs, aLen, aPageLang);
2358 // if not set up, set up the default CJK order, based on accept lang
2359 // settings and locale
2360 if (mCJKPrefLangs.Length() == 0) {
2361 // temp array
2362 eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
2363 uint32_t tempLen = 0;
2365 // Add the CJK pref fonts from accept languages, the order should be same
2366 // order. We use gfxFontUtils::GetPrefsFontList to read the list even
2367 // though it's not actually a list of fonts but of lang codes; the format
2368 // is the same.
2369 AutoTArray<nsCString, 5> list;
2370 gfxFontUtils::GetPrefsFontList("intl.accept_languages", list, true);
2371 for (const auto& lang : list) {
2372 eFontPrefLang fpl = GetFontPrefLangFor(lang.get());
2373 switch (fpl) {
2374 case eFontPrefLang_Japanese:
2375 case eFontPrefLang_Korean:
2376 case eFontPrefLang_ChineseCN:
2377 case eFontPrefLang_ChineseHK:
2378 case eFontPrefLang_ChineseTW:
2379 AppendPrefLang(tempPrefLangs, tempLen, fpl);
2380 break;
2381 default:
2382 break;
2386 // Try using app's locale
2387 nsAutoCString localeStr;
2388 LocaleService::GetInstance()->GetAppLocaleAsBCP47(localeStr);
2391 Locale locale;
2392 if (LocaleParser::TryParse(localeStr, locale).isOk() &&
2393 locale.Canonicalize().isOk()) {
2394 if (locale.Language().EqualTo("ja")) {
2395 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2396 } else if (locale.Language().EqualTo("zh")) {
2397 if (locale.Region().EqualTo("CN")) {
2398 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2399 } else if (locale.Region().EqualTo("TW")) {
2400 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2401 } else if (locale.Region().EqualTo("HK")) {
2402 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2404 } else if (locale.Language().EqualTo("ko")) {
2405 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2410 // Then add the known CJK prefs in order of system preferred locales
2411 AutoTArray<nsCString, 5> prefLocales;
2412 prefLocales.AppendElement("ja"_ns);
2413 prefLocales.AppendElement("zh-CN"_ns);
2414 prefLocales.AppendElement("zh-TW"_ns);
2415 prefLocales.AppendElement("zh-HK"_ns);
2416 prefLocales.AppendElement("ko"_ns);
2418 AutoTArray<nsCString, 16> sysLocales;
2419 AutoTArray<nsCString, 16> negLocales;
2420 if (NS_SUCCEEDED(
2421 OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) {
2422 LocaleService::GetInstance()->NegotiateLanguages(
2423 sysLocales, prefLocales, ""_ns,
2424 LocaleService::kLangNegStrategyFiltering, negLocales);
2425 for (const auto& localeStr : negLocales) {
2426 Locale locale;
2427 if (LocaleParser::TryParse(localeStr, locale).isOk() &&
2428 locale.Canonicalize().isOk()) {
2429 if (locale.Language().EqualTo("ja")) {
2430 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2431 } else if (locale.Language().EqualTo("zh")) {
2432 if (locale.Region().EqualTo("CN")) {
2433 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2434 } else if (locale.Region().EqualTo("TW")) {
2435 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2436 } else if (locale.Region().EqualTo("HK")) {
2437 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2439 } else if (locale.Language().EqualTo("ko")) {
2440 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2446 // Last resort... set up CJK font prefs in the order listed by the user-
2447 // configurable ordering pref.
2448 gfxFontUtils::GetPrefsFontList(kCJKFallbackOrderPref, list);
2449 for (const auto& item : list) {
2450 eFontPrefLang fpl = GetFontPrefLangFor(item.get());
2451 switch (fpl) {
2452 case eFontPrefLang_Japanese:
2453 case eFontPrefLang_Korean:
2454 case eFontPrefLang_ChineseCN:
2455 case eFontPrefLang_ChineseHK:
2456 case eFontPrefLang_ChineseTW:
2457 AppendPrefLang(tempPrefLangs, tempLen, fpl);
2458 break;
2459 default:
2460 break;
2464 // Truly-last resort... try Chinese font prefs before Japanese because
2465 // they tend to have more complete character coverage, and therefore less
2466 // risk of "ransom-note" effects.
2467 // (If the kCJKFallbackOrderPref was fully populated, as it is by default,
2468 // this will do nothing as all these values are already present.)
2469 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2470 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2471 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2472 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2473 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2475 // copy into the cached array
2476 for (const auto lang : Span<eFontPrefLang>(tempPrefLangs, tempLen)) {
2477 mCJKPrefLangs.AppendElement(lang);
2481 // append in cached CJK langs
2482 for (const auto lang : mCJKPrefLangs) {
2483 AppendPrefLang(aPrefLangs, aLen, eFontPrefLang(lang));
2487 void gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[],
2488 uint32_t& aLen,
2489 eFontPrefLang aAddLang) {
2490 if (aLen >= kMaxLenPrefLangList) {
2491 return;
2494 // If the lang is already present, just ignore the addition.
2495 for (const auto lang : Span<eFontPrefLang>(aPrefLangs, aLen)) {
2496 if (lang == aAddLang) {
2497 return;
2501 aPrefLangs[aLen++] = aAddLang;
2504 StyleGenericFontFamily gfxPlatformFontList::GetDefaultGeneric(
2505 eFontPrefLang aLang) {
2506 if (aLang == eFontPrefLang_Emoji) {
2507 return StyleGenericFontFamily::MozEmoji;
2510 AutoLock lock(mLock);
2512 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
2513 return mDefaultGenericsLangGroup[uint32_t(aLang)];
2515 return StyleGenericFontFamily::Serif;
2518 FontFamily gfxPlatformFontList::GetDefaultFont(nsPresContext* aPresContext,
2519 const gfxFontStyle* aStyle) {
2520 AutoLock lock(mLock);
2521 return GetDefaultFontLocked(aPresContext, aStyle);
2524 FontFamily gfxPlatformFontList::GetDefaultFontLocked(
2525 nsPresContext* aPresContext, const gfxFontStyle* aStyle) {
2526 FontFamily family = GetDefaultFontForPlatform(aPresContext, aStyle);
2527 if (!family.IsNull()) {
2528 return family;
2530 // Something has gone wrong and we were unable to retrieve a default font
2531 // from the platform. (Likely the whitelist has blocked all potential
2532 // default fonts.) As a last resort, we return the first font in our list.
2533 if (SharedFontList()) {
2534 MOZ_RELEASE_ASSERT(SharedFontList()->NumFamilies() > 0);
2535 return FontFamily(SharedFontList()->Families());
2537 MOZ_RELEASE_ASSERT(mFontFamilies.Count() > 0);
2538 return FontFamily(mFontFamilies.ConstIter().Data());
2541 void gfxPlatformFontList::GetFontFamilyNames(
2542 nsTArray<nsCString>& aFontFamilyNames) {
2543 if (SharedFontList()) {
2544 fontlist::FontList* list = SharedFontList();
2545 const fontlist::Family* families = list->Families();
2546 if (families) {
2547 for (uint32_t i = 0, n = list->NumFamilies(); i < n; i++) {
2548 const fontlist::Family& family = families[i];
2549 if (!family.IsHidden()) {
2550 aFontFamilyNames.AppendElement(family.DisplayName().AsString(list));
2554 } else {
2555 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2556 if (!family->IsHidden()) {
2557 aFontFamilyNames.AppendElement(family->Name());
2563 nsAtom* gfxPlatformFontList::GetLangGroup(nsAtom* aLanguage) {
2564 // map lang ==> langGroup
2565 nsAtom* langGroup = nullptr;
2566 if (aLanguage) {
2567 langGroup = mLangService->GetLanguageGroup(aLanguage);
2569 if (!langGroup) {
2570 langGroup = nsGkAtoms::Unicode;
2572 return langGroup;
2575 /* static */ const char* gfxPlatformFontList::GetGenericName(
2576 StyleGenericFontFamily aGenericType) {
2577 // type should be standard generic type at this point
2578 // map generic type to string
2579 switch (aGenericType) {
2580 case StyleGenericFontFamily::Serif:
2581 return "serif";
2582 case StyleGenericFontFamily::SansSerif:
2583 return "sans-serif";
2584 case StyleGenericFontFamily::Monospace:
2585 return "monospace";
2586 case StyleGenericFontFamily::Cursive:
2587 return "cursive";
2588 case StyleGenericFontFamily::Fantasy:
2589 return "fantasy";
2590 case StyleGenericFontFamily::SystemUi:
2591 return "system-ui";
2592 case StyleGenericFontFamily::MozEmoji:
2593 return "-moz-emoji";
2594 case StyleGenericFontFamily::None:
2595 break;
2597 MOZ_ASSERT_UNREACHABLE("Unknown generic");
2598 return nullptr;
2601 void gfxPlatformFontList::InitLoader() {
2602 GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
2603 mStartIndex = 0;
2604 mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
2605 memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
2608 #define FONT_LOADER_MAX_TIMESLICE \
2609 20 // max time for one pass through RunLoader = 20ms
2611 bool gfxPlatformFontList::LoadFontInfo() {
2612 AutoLock lock(mLock);
2613 TimeStamp start = TimeStamp::Now();
2614 uint32_t i, endIndex = mNumFamilies;
2615 fontlist::FontList* list = SharedFontList();
2616 bool loadCmaps =
2617 !list && (!UsesSystemFallback() ||
2618 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback());
2620 // for each font family, load in various font info
2621 for (i = mStartIndex; i < endIndex; i++) {
2622 nsAutoCString key;
2623 GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
2625 if (list) {
2626 fontlist::Family* family = list->FindFamily(key);
2627 if (!family) {
2628 continue;
2630 ReadFaceNamesForFamily(family, NeedFullnamePostscriptNames());
2631 } else {
2632 // lookup in canonical (i.e. English) family name list
2633 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
2634 if (!familyEntry) {
2635 continue;
2638 // read in face names
2639 familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(),
2640 mFontInfo);
2642 // load the cmaps if needed
2643 if (loadCmaps) {
2644 familyEntry->ReadAllCMAPs(mFontInfo);
2648 // Limit the time spent reading fonts in one pass, unless the font-loader
2649 // delay was set to zero, in which case we run to completion even if it
2650 // causes some jank.
2651 if (StaticPrefs::gfx_font_loader_delay_AtStartup() > 0) {
2652 TimeDuration elapsed = TimeStamp::Now() - start;
2653 if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
2654 i + 1 != endIndex) {
2655 endIndex = i + 1;
2656 break;
2661 mStartIndex = endIndex;
2662 bool done = mStartIndex >= mNumFamilies;
2664 if (LOG_FONTINIT_ENABLED()) {
2665 TimeDuration elapsed = TimeStamp::Now() - start;
2666 LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
2667 elapsed.ToMilliseconds(), (done ? "true" : "false")));
2670 if (done) {
2671 mOtherFamilyNamesInitialized = true;
2672 CancelInitOtherFamilyNamesTask();
2673 mFaceNameListsInitialized = true;
2676 return done;
2679 void gfxPlatformFontList::CleanupLoader() {
2680 AutoLock lock(mLock);
2682 mFontFamiliesToLoad.Clear();
2683 mNumFamilies = 0;
2684 bool rebuilt = false, forceReflow = false;
2686 // if had missed face names that are now available, force reflow all
2687 if (mFaceNamesMissed) {
2688 rebuilt = std::any_of(mFaceNamesMissed->cbegin(), mFaceNamesMissed->cend(),
2689 [&](const auto& key) {
2690 mLock.AssertCurrentThreadIn();
2691 return FindFaceName(key);
2693 if (rebuilt) {
2694 RebuildLocalFonts();
2697 mFaceNamesMissed = nullptr;
2700 if (mOtherNamesMissed) {
2701 forceReflow = std::any_of(
2702 mOtherNamesMissed->cbegin(), mOtherNamesMissed->cend(),
2703 [&](const auto& key) {
2704 mLock.AssertCurrentThreadIn();
2705 return FindUnsharedFamily(
2706 nullptr, key,
2707 (FindFamiliesFlags::eForceOtherFamilyNamesLoading |
2708 FindFamiliesFlags::eNoAddToNamesMissedWhenSearching));
2710 if (forceReflow) {
2711 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::No);
2714 mOtherNamesMissed = nullptr;
2717 if (LOG_FONTINIT_ENABLED() && mFontInfo) {
2718 LOG_FONTINIT(
2719 ("(fontinit) fontloader load thread took %8.2f ms "
2720 "%d families %d fonts %d cmaps "
2721 "%d facenames %d othernames %s %s",
2722 mLoadTime.ToMilliseconds(), mFontInfo->mLoadStats.families,
2723 mFontInfo->mLoadStats.fonts, mFontInfo->mLoadStats.cmaps,
2724 mFontInfo->mLoadStats.facenames, mFontInfo->mLoadStats.othernames,
2725 (rebuilt ? "(userfont sets rebuilt)" : ""),
2726 (forceReflow ? "(global reflow)" : "")));
2729 gfxFontInfoLoader::CleanupLoader();
2732 void gfxPlatformFontList::ForceGlobalReflowLocked(
2733 gfxPlatform::NeedsReframe aNeedsReframe,
2734 gfxPlatform::BroadcastToChildren aBroadcastToChildren) {
2735 if (!NS_IsMainThread()) {
2736 NS_DispatchToMainThread(NS_NewRunnableFunction(
2737 "gfxPlatformFontList::ForceGlobalReflowLocked",
2738 [aNeedsReframe, aBroadcastToChildren] {
2739 gfxPlatform::ForceGlobalReflow(aNeedsReframe, aBroadcastToChildren);
2740 }));
2741 return;
2744 AutoUnlock unlock(mLock);
2745 gfxPlatform::ForceGlobalReflow(aNeedsReframe, aBroadcastToChildren);
2748 void gfxPlatformFontList::GetPrefsAndStartLoader() {
2749 // If we're already in shutdown, there's no point in starting this, and it
2750 // could trigger an assertion if we try to use the Thread Manager too late.
2751 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
2752 return;
2754 uint32_t delay = std::max(1u, StaticPrefs::gfx_font_loader_delay_AtStartup());
2755 if (NS_IsMainThread()) {
2756 StartLoader(delay);
2757 } else {
2758 NS_DispatchToMainThread(NS_NewRunnableFunction(
2759 "StartLoader callback", [delay, fontList = this] {
2760 fontList->Lock();
2761 fontList->StartLoader(delay);
2762 fontList->Unlock();
2763 }));
2767 void gfxPlatformFontList::RebuildLocalFonts(bool aForgetLocalFaces) {
2768 for (auto* fontset : mUserFontSetList) {
2769 if (aForgetLocalFaces) {
2770 fontset->ForgetLocalFaces();
2772 fontset->RebuildLocalRules();
2776 void gfxPlatformFontList::ClearLangGroupPrefFontsLocked() {
2777 for (uint32_t i = eFontPrefLang_First;
2778 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
2779 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
2780 for (auto& pref : prefFontsLangGroup) {
2781 pref = nullptr;
2784 mCJKPrefLangs.Clear();
2785 mEmojiPrefFont = nullptr;
2787 // Create a new FontPrefs and replace the existing one.
2788 mFontPrefs = MakeUnique<FontPrefs>();
2791 // Support for memory reporting
2793 // this is also used by subclasses that hold additional font tables
2794 /*static*/
2795 size_t gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis(
2796 const FontFamilyTable& aTable, MallocSizeOf aMallocSizeOf) {
2797 return std::accumulate(
2798 aTable.Keys().cbegin(), aTable.Keys().cend(),
2799 aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
2800 [&](size_t oldValue, const nsACString& key) {
2801 // We don't count the size of the family here, because this is an
2802 // *extra* reference to a family that will have already been counted in
2803 // the main list.
2804 return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2808 /*static*/
2809 size_t gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
2810 const FontEntryTable& aTable, MallocSizeOf aMallocSizeOf) {
2811 return std::accumulate(
2812 aTable.Keys().cbegin(), aTable.Keys().cend(),
2813 aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
2814 [&](size_t oldValue, const nsACString& key) {
2815 // The font itself is counted by its owning family; here we only care
2816 // about the names stored in the hashtable keys.
2818 return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2822 void gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2823 FontListSizes* aSizes) const {
2824 AutoLock lock(mLock);
2826 aSizes->mFontListSize +=
2827 mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
2828 for (const auto& entry : mFontFamilies) {
2829 aSizes->mFontListSize +=
2830 entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2831 entry.GetData()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2834 aSizes->mFontListSize +=
2835 SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
2837 if (mExtraNames) {
2838 aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
2839 mExtraNames->mFullnames, aMallocSizeOf);
2840 aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
2841 mExtraNames->mPostscriptNames, aMallocSizeOf);
2844 for (uint32_t i = eFontPrefLang_First;
2845 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
2846 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
2847 for (const UniquePtr<PrefFontList>& pf : prefFontsLangGroup) {
2848 if (pf) {
2849 aSizes->mFontListSize += pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
2854 for (const auto& bitset : mCodepointsWithNoFonts) {
2855 aSizes->mFontListSize += bitset.SizeOfExcludingThis(aMallocSizeOf);
2857 aSizes->mFontListSize +=
2858 mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
2860 aSizes->mFontListSize +=
2861 mBadUnderlineFamilyNames.ShallowSizeOfExcludingThis(aMallocSizeOf);
2862 for (const auto& i : mBadUnderlineFamilyNames) {
2863 aSizes->mFontListSize += i.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2866 aSizes->mFontListSize +=
2867 mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
2868 for (const auto& entry : mSharedCmaps) {
2869 aSizes->mCharMapsSize += entry.GetKey()->SizeOfIncludingThis(aMallocSizeOf);
2872 aSizes->mFontListSize +=
2873 mFontEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
2874 for (const auto& entry : mFontEntries.Values()) {
2875 if (entry) {
2876 entry->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2880 if (SharedFontList()) {
2881 aSizes->mFontListSize +=
2882 SharedFontList()->SizeOfIncludingThis(aMallocSizeOf);
2883 if (XRE_IsParentProcess()) {
2884 aSizes->mSharedSize += SharedFontList()->AllocatedShmemSize();
2889 void gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2890 FontListSizes* aSizes) const {
2891 aSizes->mFontListSize += aMallocSizeOf(this);
2892 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2895 void gfxPlatformFontList::InitOtherFamilyNamesInternal(
2896 bool aDeferOtherFamilyNamesLoading) {
2897 if (mOtherFamilyNamesInitialized) {
2898 return;
2901 AutoLock lock(mLock);
2903 if (aDeferOtherFamilyNamesLoading) {
2904 TimeStamp start = TimeStamp::Now();
2905 bool timedOut = false;
2907 auto list = SharedFontList();
2908 if (list) {
2909 // If the gfxFontInfoLoader task is not yet running, kick it off now so
2910 // that it will load remaining names etc as soon as idle time permits.
2911 if (mState == stateInitial || mState == stateTimerOnDelay) {
2912 StartLoader(0);
2913 timedOut = true;
2915 } else {
2916 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2917 family->ReadOtherFamilyNames(this);
2918 TimeDuration elapsed = TimeStamp::Now() - start;
2919 if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
2920 timedOut = true;
2921 break;
2926 if (!timedOut) {
2927 mOtherFamilyNamesInitialized = true;
2928 CancelInitOtherFamilyNamesTask();
2930 TimeStamp end = TimeStamp::Now();
2931 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
2932 start, end);
2934 if (LOG_FONTINIT_ENABLED()) {
2935 TimeDuration elapsed = end - start;
2936 LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
2937 elapsed.ToMilliseconds(), (timedOut ? "timeout" : "")));
2939 } else {
2940 TimeStamp start = TimeStamp::Now();
2942 auto list = SharedFontList();
2943 if (list) {
2944 for (auto& f : mozilla::Range<fontlist::Family>(list->Families(),
2945 list->NumFamilies())) {
2946 ReadFaceNamesForFamily(&f, false);
2948 } else {
2949 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2950 family->ReadOtherFamilyNames(this);
2954 mOtherFamilyNamesInitialized = true;
2955 CancelInitOtherFamilyNamesTask();
2957 TimeStamp end = TimeStamp::Now();
2958 Telemetry::AccumulateTimeDelta(
2959 Telemetry::FONTLIST_INITOTHERFAMILYNAMES_NO_DEFERRING, start, end);
2961 if (LOG_FONTINIT_ENABLED()) {
2962 TimeDuration elapsed = end - start;
2963 LOG_FONTINIT(
2964 ("(fontinit) InitOtherFamilyNames without deferring took %8.2f ms",
2965 elapsed.ToMilliseconds()));
2970 void gfxPlatformFontList::CancelInitOtherFamilyNamesTask() {
2971 if (mPendingOtherFamilyNameTask) {
2972 mPendingOtherFamilyNameTask->Cancel();
2973 mPendingOtherFamilyNameTask = nullptr;
2975 auto list = SharedFontList();
2976 if (list && XRE_IsParentProcess()) {
2977 bool forceReflow = false;
2978 if (!mAliasTable.IsEmpty()) {
2979 list->SetAliases(mAliasTable);
2980 mAliasTable.Clear();
2981 forceReflow = true;
2983 if (mLocalNameTable.Count()) {
2984 list->SetLocalNames(mLocalNameTable);
2985 mLocalNameTable.Clear();
2986 forceReflow = true;
2988 if (forceReflow) {
2989 dom::ContentParent::BroadcastFontListChanged();
2994 void gfxPlatformFontList::ShareFontListShmBlockToProcess(
2995 uint32_t aGeneration, uint32_t aIndex, base::ProcessId aPid,
2996 base::SharedMemoryHandle* aOut) {
2997 auto list = SharedFontList();
2998 if (!list) {
2999 return;
3001 if (!aGeneration || list->GetGeneration() == aGeneration) {
3002 list->ShareShmBlockToProcess(aIndex, aPid, aOut);
3003 } else {
3004 *aOut = base::SharedMemory::NULLHandle();
3008 void gfxPlatformFontList::ShareFontListToProcess(
3009 nsTArray<base::SharedMemoryHandle>* aBlocks, base::ProcessId aPid) {
3010 auto list = SharedFontList();
3011 if (list) {
3012 list->ShareBlocksToProcess(aBlocks, aPid);
3016 base::SharedMemoryHandle gfxPlatformFontList::ShareShmBlockToProcess(
3017 uint32_t aIndex, base::ProcessId aPid) {
3018 MOZ_RELEASE_ASSERT(SharedFontList());
3019 return SharedFontList()->ShareBlockToProcess(aIndex, aPid);
3022 void gfxPlatformFontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
3023 base::SharedMemoryHandle aHandle) {
3024 if (SharedFontList()) {
3025 AutoLock lock(mLock);
3026 SharedFontList()->ShmBlockAdded(aGeneration, aIndex, std::move(aHandle));
3030 void gfxPlatformFontList::InitializeFamily(uint32_t aGeneration,
3031 uint32_t aFamilyIndex,
3032 bool aLoadCmaps) {
3033 auto list = SharedFontList();
3034 MOZ_ASSERT(list);
3035 if (!list) {
3036 return;
3038 if (list->GetGeneration() != aGeneration) {
3039 return;
3041 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3042 return;
3044 if (aFamilyIndex >= list->NumFamilies()) {
3045 return;
3047 fontlist::Family* family = list->Families() + aFamilyIndex;
3048 if (!family->IsInitialized() || aLoadCmaps) {
3049 Unused << InitializeFamily(family, aLoadCmaps);
3053 void gfxPlatformFontList::SetCharacterMap(uint32_t aGeneration,
3054 uint32_t aFamilyIndex, bool aAlias,
3055 uint32_t aFaceIndex,
3056 const gfxSparseBitSet& aMap) {
3057 MOZ_ASSERT(XRE_IsParentProcess());
3058 auto list = SharedFontList();
3059 MOZ_ASSERT(list);
3060 if (!list) {
3061 return;
3063 if (list->GetGeneration() != aGeneration) {
3064 return;
3066 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3067 return;
3070 const fontlist::Family* family;
3071 if (aAlias) {
3072 if (aFamilyIndex >= list->NumAliases()) {
3073 MOZ_ASSERT(false, "AliasFamily index out of range");
3074 return;
3076 family = list->AliasFamilies() + aFamilyIndex;
3077 } else {
3078 if (aFamilyIndex >= list->NumFamilies()) {
3079 MOZ_ASSERT(false, "Family index out of range");
3080 return;
3082 family = list->Families() + aFamilyIndex;
3085 if (aFaceIndex >= family->NumFaces()) {
3086 MOZ_ASSERT(false, "Face index out of range");
3087 return;
3090 if (auto* face =
3091 family->Faces(list)[aFaceIndex].ToPtr<fontlist::Face>(list)) {
3092 face->mCharacterMap = GetShmemCharMap(&aMap);
3096 void gfxPlatformFontList::SetupFamilyCharMap(uint32_t aGeneration,
3097 uint32_t aIndex, bool aAlias) {
3098 MOZ_ASSERT(XRE_IsParentProcess());
3099 auto list = SharedFontList();
3100 MOZ_ASSERT(list);
3101 if (!list) {
3102 return;
3104 if (list->GetGeneration() != aGeneration) {
3105 return;
3107 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3108 return;
3111 if (aAlias) {
3112 if (aIndex >= list->NumAliases()) {
3113 MOZ_ASSERT(false, "AliasFamily index out of range");
3114 return;
3116 list->AliasFamilies()[aIndex].SetupFamilyCharMap(list);
3117 return;
3120 if (aIndex >= list->NumFamilies()) {
3121 MOZ_ASSERT(false, "Family index out of range");
3122 return;
3124 list->Families()[aIndex].SetupFamilyCharMap(list);
3127 bool gfxPlatformFontList::InitOtherFamilyNames(uint32_t aGeneration,
3128 bool aDefer) {
3129 auto list = SharedFontList();
3130 MOZ_ASSERT(list);
3131 if (!list) {
3132 return false;
3134 if (list->GetGeneration() != aGeneration) {
3135 return false;
3137 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3138 return false;
3140 return InitOtherFamilyNames(aDefer);
3143 uint32_t gfxPlatformFontList::GetGeneration() const {
3144 return SharedFontList() ? SharedFontList()->GetGeneration() : 0;
3147 gfxPlatformFontList::FontPrefs::FontPrefs() {
3148 // This must be created on the main thread, so that we can safely use the
3149 // Preferences service. Once created, it can be read from any thread.
3150 MOZ_ASSERT(NS_IsMainThread());
3151 Init();
3154 void gfxPlatformFontList::FontPrefs::Init() {
3155 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
3156 return;
3158 nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch();
3159 if (!prefRootBranch) {
3160 return;
3162 nsTArray<nsCString> prefNames;
3163 if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNamePrefix, prefNames))) {
3164 for (auto& prefName : prefNames) {
3165 nsAutoCString value;
3166 if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
3167 nsAutoCString pref(Substring(prefName, sizeof(kNamePrefix) - 1));
3168 mFontName.InsertOrUpdate(pref, value);
3172 if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNameListPrefix, prefNames))) {
3173 for (auto& prefName : prefNames) {
3174 nsAutoCString value;
3175 if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
3176 nsAutoCString pref(Substring(prefName, sizeof(kNameListPrefix) - 1));
3177 mFontNameList.InsertOrUpdate(pref, value);
3181 mEmojiHasUserValue = Preferences::HasUserValue("font.name-list.emoji");
3184 bool gfxPlatformFontList::FontPrefs::LookupName(const nsACString& aPref,
3185 nsACString& aValue) const {
3186 if (const auto& value = mFontName.Lookup(aPref)) {
3187 aValue = *value;
3188 return true;
3190 return false;
3193 bool gfxPlatformFontList::FontPrefs::LookupNameList(const nsACString& aPref,
3194 nsACString& aValue) const {
3195 if (const auto& value = mFontNameList.Lookup(aPref)) {
3196 aValue = *value;
3197 return true;
3199 return false;
3202 bool gfxPlatformFontList::IsKnownIconFontFamily(
3203 const nsAtom* aFamilyName) const {
3204 nsAtomCString fam(aFamilyName);
3205 ToLowerCase(fam);
3206 return mIconFontsSet.Contains(fam);
3209 #undef LOG
3210 #undef LOG_ENABLED