Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / thebes / gfxPlatformFontList.cpp
blob45a5bea6e13619d458feaa04feface8bbef48a4d
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
7 #include "mozilla/gfx/Logging.h"
8 #include "mozilla/intl/Locale.h"
9 #include "mozilla/intl/LocaleService.h"
10 #include "mozilla/intl/OSPreferences.h"
12 #include "gfxPlatformFontList.h"
13 #include "gfxTextRun.h"
14 #include "gfxUserFontSet.h"
15 #include "SharedFontList-impl.h"
17 #include "GeckoProfiler.h"
18 #include "nsCRT.h"
19 #include "nsGkAtoms.h"
20 #include "nsPresContext.h"
21 #include "nsServiceManagerUtils.h"
22 #include "nsUnicharUtils.h"
23 #include "nsUnicodeProperties.h"
24 #include "nsXULAppAPI.h"
26 #include "mozilla/AppShutdown.h"
27 #include "mozilla/Attributes.h"
28 #include "mozilla/BinarySearch.h"
29 #include "mozilla/Likely.h"
30 #include "mozilla/MemoryReporting.h"
31 #include "mozilla/Mutex.h"
32 #include "mozilla/Preferences.h"
33 #include "mozilla/StaticPrefs_gfx.h"
34 #include "mozilla/StaticPrefs_layout.h"
35 #include "mozilla/Telemetry.h"
36 #include "mozilla/TimeStamp.h"
37 #include "mozilla/dom/BlobImpl.h"
38 #include "mozilla/dom/ContentChild.h"
39 #include "mozilla/dom/ContentParent.h"
40 #include "mozilla/dom/ContentProcessMessageManager.h"
41 #include "mozilla/dom/Document.h"
42 #include "mozilla/gfx/2D.h"
43 #include "mozilla/ipc/FileDescriptorUtils.h"
44 #include "mozilla/ResultExtensions.h"
45 #include "mozilla/TextUtils.h"
46 #include "mozilla/Unused.h"
48 #include "base/eintr_wrapper.h"
50 #include <locale.h>
51 #include <numeric>
53 using namespace mozilla;
54 using mozilla::intl::Locale;
55 using mozilla::intl::LocaleParser;
56 using mozilla::intl::LocaleService;
57 using mozilla::intl::OSPreferences;
59 #define LOG_FONTLIST(args) \
60 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
61 #define LOG_FONTLIST_ENABLED() \
62 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
63 #define LOG_FONTINIT(args) \
64 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
65 #define LOG_FONTINIT_ENABLED() \
66 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
68 gfxPlatformFontList* gfxPlatformFontList::sPlatformFontList = nullptr;
70 // Character ranges that require complex-script shaping support in the font,
71 // and so should be masked out by ReadCMAP if the necessary layout tables
72 // are not present.
73 // Currently used by the Mac and FT2 implementations only, but probably should
74 // be supported on Windows as well.
75 const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = {
76 // Actually, now that harfbuzz supports presentation-forms shaping for
77 // Arabic, we can render it without layout tables. So maybe we don't
78 // want to mask the basic Arabic block here?
79 // This affects the arabic-fallback-*.html reftests, which rely on
80 // loading a font that *doesn't* have any GSUB table.
81 {0x0600, 0x060B, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
82 // skip 060C Arabic comma, also used by N'Ko etc
83 {0x060D, 0x061A, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
84 // skip 061B Arabic semicolon, also used by N'Ko etc
85 {0x061C, 0x061E, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
86 // skip 061F Arabic question mark, also used by N'Ko etc
87 {0x0620, 0x063F, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
88 // skip 0640 Arabic tatweel (for syriac, adlam, etc)
89 {0x0641, 0x06D3, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
90 // skip 06D4 Arabic full stop (for hanifi rohingya)
91 {0x06D5, 0x06FF, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
92 {0x0700, 0x074F, 1, {TRUETYPE_TAG('s', 'y', 'r', 'c'), 0, 0}},
93 {0x0750, 0x077F, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
94 {0x08A0, 0x08FF, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
95 {0x0900,
96 0x0963,
98 {TRUETYPE_TAG('d', 'e', 'v', '2'), TRUETYPE_TAG('d', 'e', 'v', 'a'), 0}},
99 // skip 0964 DEVANAGARI DANDA and 0965 DEVANAGARI DOUBLE DANDA, shared by
100 // various other Indic writing systems
101 {0x0966,
102 0x097F,
104 {TRUETYPE_TAG('d', 'e', 'v', '2'), TRUETYPE_TAG('d', 'e', 'v', 'a'), 0}},
105 {0x0980,
106 0x09FF,
108 {TRUETYPE_TAG('b', 'n', 'g', '2'), TRUETYPE_TAG('b', 'e', 'n', 'g'), 0}},
109 {0x0A00,
110 0x0A7F,
112 {TRUETYPE_TAG('g', 'u', 'r', '2'), TRUETYPE_TAG('g', 'u', 'r', 'u'), 0}},
113 {0x0A80,
114 0x0AFF,
116 {TRUETYPE_TAG('g', 'j', 'r', '2'), TRUETYPE_TAG('g', 'u', 'j', 'r'), 0}},
117 {0x0B00,
118 0x0B7F,
120 {TRUETYPE_TAG('o', 'r', 'y', '2'), TRUETYPE_TAG('o', 'r', 'y', 'a'), 0}},
121 {0x0B80,
122 0x0BFF,
124 {TRUETYPE_TAG('t', 'm', 'l', '2'), TRUETYPE_TAG('t', 'a', 'm', 'l'), 0}},
125 {0x0C00,
126 0x0C7F,
128 {TRUETYPE_TAG('t', 'e', 'l', '2'), TRUETYPE_TAG('t', 'e', 'l', 'u'), 0}},
129 {0x0C80,
130 0x0CFF,
132 {TRUETYPE_TAG('k', 'n', 'd', '2'), TRUETYPE_TAG('k', 'n', 'd', 'a'), 0}},
133 {0x0D00,
134 0x0D7F,
136 {TRUETYPE_TAG('m', 'l', 'm', '2'), TRUETYPE_TAG('m', 'l', 'y', 'm'), 0}},
137 {0x0D80, 0x0DFF, 1, {TRUETYPE_TAG('s', 'i', 'n', 'h'), 0, 0}},
138 {0x0E80, 0x0EFF, 1, {TRUETYPE_TAG('l', 'a', 'o', ' '), 0, 0}},
139 {0x0F00, 0x0FFF, 1, {TRUETYPE_TAG('t', 'i', 'b', 't'), 0, 0}},
140 {0x1000,
141 0x109f,
143 {TRUETYPE_TAG('m', 'y', 'm', 'r'), TRUETYPE_TAG('m', 'y', 'm', '2'), 0}},
144 {0x1780, 0x17ff, 1, {TRUETYPE_TAG('k', 'h', 'm', 'r'), 0, 0}},
145 // Khmer Symbols (19e0..19ff) don't seem to need any special shaping
146 {0xaa60,
147 0xaa7f,
149 {TRUETYPE_TAG('m', 'y', 'm', 'r'), TRUETYPE_TAG('m', 'y', 'm', '2'), 0}},
150 // Thai seems to be "renderable" without AAT morphing tables
151 {0, 0, 0, {0, 0, 0}} // terminator
154 static const char* kObservedPrefs[] = {
155 "font.", "font.name-list.", "intl.accept_languages", // hmmmm...
156 "browser.display.use_document_fonts.icon_font_allowlist", nullptr};
158 static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
160 static const char kCJKFallbackOrderPref[] = "font.cjk_pref_fallback_order";
162 // Pref for the list of icon font families that still get to override the
163 // default font from prefs, even when use_document_fonts is disabled.
164 // (This is to enable ligature-based icon fonts to keep working.)
165 static const char kIconFontsPref[] =
166 "browser.display.use_document_fonts.icon_font_allowlist";
168 // xxx - this can probably be eliminated by reworking pref font handling code
169 static const char* gPrefLangNames[] = {
170 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
171 #include "gfxFontPrefLangList.h"
172 #undef FONT_PREF_LANG
175 static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
176 "size of pref lang name array doesn't match pref lang enum size");
178 class gfxFontListPrefObserver final : public nsIObserver {
179 ~gfxFontListPrefObserver() = default;
181 public:
182 NS_DECL_ISUPPORTS
183 NS_DECL_NSIOBSERVER
186 static void FontListPrefChanged(const char* aPref, void* aData = nullptr) {
187 // XXX this could be made to only clear out the cache for the prefs that were
188 // changed but it probably isn't that big a deal.
189 gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
190 gfxPlatformFontList::PlatformFontList()->LoadIconFontOverrideList();
191 gfxFontCache::GetCache()->Flush();
194 static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
196 NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
198 #define LOCALES_CHANGED_TOPIC "intl:system-locales-changed"
200 NS_IMETHODIMP
201 gfxFontListPrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
202 const char16_t* aData) {
203 NS_ASSERTION(!strcmp(aTopic, LOCALES_CHANGED_TOPIC), "invalid topic");
204 FontListPrefChanged(nullptr);
206 if (XRE_IsParentProcess()) {
207 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::No);
209 return NS_OK;
212 MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
214 NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
216 NS_IMETHODIMP
217 gfxPlatformFontList::MemoryReporter::CollectReports(
218 nsIHandleReportCallback* aHandleReport, nsISupports* aData,
219 bool aAnonymize) {
220 FontListSizes sizes;
221 sizes.mFontListSize = 0;
222 sizes.mFontTableCacheSize = 0;
223 sizes.mCharMapsSize = 0;
224 sizes.mLoaderSize = 0;
225 sizes.mSharedSize = 0;
227 gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(
228 &FontListMallocSizeOf, &sizes);
230 MOZ_COLLECT_REPORT(
231 "explicit/gfx/font-list", KIND_HEAP, UNITS_BYTES, sizes.mFontListSize,
232 "Memory used to manage the list of font families and faces.");
234 MOZ_COLLECT_REPORT(
235 "explicit/gfx/font-charmaps", KIND_HEAP, UNITS_BYTES, sizes.mCharMapsSize,
236 "Memory used to record the character coverage of individual fonts.");
238 if (sizes.mFontTableCacheSize) {
239 MOZ_COLLECT_REPORT(
240 "explicit/gfx/font-tables", KIND_HEAP, UNITS_BYTES,
241 sizes.mFontTableCacheSize,
242 "Memory used for cached font metrics and layout tables.");
245 if (sizes.mLoaderSize) {
246 MOZ_COLLECT_REPORT("explicit/gfx/font-loader", KIND_HEAP, UNITS_BYTES,
247 sizes.mLoaderSize,
248 "Memory used for (platform-specific) font loader.");
251 if (sizes.mSharedSize) {
252 MOZ_COLLECT_REPORT(
253 "font-list-shmem", KIND_NONHEAP, UNITS_BYTES, sizes.mSharedSize,
254 "Shared memory for system font list and character coverage data.");
257 return NS_OK;
260 PRThread* gfxPlatformFontList::sInitFontListThread = nullptr;
262 static void InitFontListCallback(void* aFontList) {
263 AUTO_PROFILER_REGISTER_THREAD("InitFontList");
264 PR_SetCurrentThreadName("InitFontList");
266 if (!static_cast<gfxPlatformFontList*>(aFontList)->InitFontList()) {
267 gfxPlatformFontList::Shutdown();
271 /* static */
272 bool gfxPlatformFontList::Initialize(gfxPlatformFontList* aList) {
273 sPlatformFontList = aList;
274 if (XRE_IsParentProcess() &&
275 StaticPrefs::gfx_font_list_omt_enabled_AtStartup() &&
276 StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
277 !gfxPlatform::InSafeMode()) {
278 sInitFontListThread = PR_CreateThread(
279 PR_USER_THREAD, InitFontListCallback, aList, PR_PRIORITY_NORMAL,
280 PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
281 return true;
283 if (aList->InitFontList()) {
284 return true;
286 Shutdown();
287 return false;
290 gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
291 : mLock("gfxPlatformFontList lock"),
292 mFontFamilies(64),
293 mOtherFamilyNames(16),
294 mSharedCmaps(8) {
295 if (aNeedFullnamePostscriptNames) {
296 mExtraNames = MakeUnique<ExtraNames>();
299 mLangService = nsLanguageAtomService::GetService();
301 LoadBadUnderlineList();
302 LoadIconFontOverrideList();
304 mFontPrefs = MakeUnique<FontPrefs>();
306 gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
307 mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
309 // pref changes notification setup
310 NS_ASSERTION(!gFontListPrefObserver,
311 "There has been font list pref observer already");
312 gFontListPrefObserver = new gfxFontListPrefObserver();
313 NS_ADDREF(gFontListPrefObserver);
315 Preferences::RegisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
317 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
318 if (obs) {
319 obs->AddObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC, false);
322 // Only the parent process listens for whitelist changes; it will then
323 // notify its children to rebuild their font lists.
324 if (XRE_IsParentProcess()) {
325 Preferences::RegisterCallback(FontWhitelistPrefChanged,
326 kFontSystemWhitelistPref);
329 RegisterStrongMemoryReporter(new MemoryReporter());
331 // initialize lang group pref font defaults (i.e. serif/sans-serif)
332 mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
333 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
334 nsAutoCString prefDefaultFontType("font.default.");
335 prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
336 nsAutoCString serifOrSans;
337 Preferences::GetCString(prefDefaultFontType.get(), serifOrSans);
338 if (serifOrSans.EqualsLiteral("sans-serif")) {
339 mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::SansSerif;
340 } else {
341 mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::Serif;
346 gfxPlatformFontList::~gfxPlatformFontList() {
347 // Note that gfxPlatformFontList::Shutdown() ensures that the init-font-list
348 // thread is finished before we come here.
350 AutoLock lock(mLock);
352 // We can't just do mSharedCmaps.Clear() here because removing each item from
353 // the table would drop its last reference, and its Release() method would
354 // then call back to MaybeRemoveCmap to search for it, which we can't do
355 // while in the middle of clearing the table.
356 // So we first clear the "shared" flag in each entry, so Release() won't try
357 // to re-find them in the table.
358 for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
359 iter.Get()->mCharMap->ClearSharedFlag();
361 mSharedCmaps.Clear();
363 ClearLangGroupPrefFontsLocked();
365 NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
367 Preferences::UnregisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
369 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
370 if (obs) {
371 obs->RemoveObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC);
374 if (XRE_IsParentProcess()) {
375 Preferences::UnregisterCallback(FontWhitelistPrefChanged,
376 kFontSystemWhitelistPref);
378 NS_RELEASE(gFontListPrefObserver);
381 void gfxPlatformFontList::GetMissingFonts(nsCString& aMissingFonts) {
382 AutoLock lock(mLock);
384 auto fontLists = GetFilteredPlatformFontLists();
386 if (!fontLists.Length()) {
387 aMissingFonts.Append("No font list available for this device.");
390 for (unsigned int i = 0; i < fontLists.Length(); i++) {
391 for (unsigned int j = 0; j < fontLists[i].second; j++) {
392 nsCString key(fontLists[i].first[j]);
393 GenerateFontListKey(key);
395 if (SharedFontList()) {
396 fontlist::Family* family = SharedFontList()->FindFamily(key);
397 if (!family) {
398 aMissingFonts.Append(fontLists[i].first[j]);
399 aMissingFonts.Append("|");
401 } else {
402 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
403 if (!familyEntry) {
404 familyEntry = mOtherFamilyNames.GetWeak(key);
406 if (!familyEntry) {
407 aMissingFonts.Append(fontLists[i].first[j]);
408 aMissingFonts.Append("|");
415 /* static */
416 void gfxPlatformFontList::FontWhitelistPrefChanged(const char* aPref,
417 void* aClosure) {
418 MOZ_ASSERT(XRE_IsParentProcess());
419 auto* pfl = gfxPlatformFontList::PlatformFontList();
420 pfl->UpdateFontList(true);
421 dom::ContentParent::NotifyUpdatedFonts(true);
424 void gfxPlatformFontList::ApplyWhitelist() {
425 uint32_t numFonts = mEnabledFontsList.Length();
426 if (!mFontFamilyWhitelistActive) {
427 return;
429 nsTHashSet<nsCString> familyNamesWhitelist;
430 for (uint32_t i = 0; i < numFonts; i++) {
431 nsAutoCString key;
432 ToLowerCase(mEnabledFontsList[i], key);
433 familyNamesWhitelist.Insert(key);
435 AutoTArray<RefPtr<gfxFontFamily>, 128> accepted;
436 bool whitelistedFontFound = false;
437 for (const auto& entry : mFontFamilies) {
438 nsAutoCString fontFamilyName(entry.GetKey());
439 ToLowerCase(fontFamilyName);
440 if (familyNamesWhitelist.Contains(fontFamilyName)) {
441 accepted.AppendElement(entry.GetData());
442 whitelistedFontFound = true;
445 if (!whitelistedFontFound) {
446 // No whitelisted fonts found! Ignore the whitelist.
447 return;
449 // Replace the original full list with the accepted subset.
450 mFontFamilies.Clear();
451 for (auto& f : accepted) {
452 nsAutoCString fontFamilyName(f->Name());
453 ToLowerCase(fontFamilyName);
454 mFontFamilies.InsertOrUpdate(fontFamilyName, std::move(f));
458 void gfxPlatformFontList::ApplyWhitelist(
459 nsTArray<fontlist::Family::InitData>& aFamilies) {
460 mLock.AssertCurrentThreadIn();
461 if (!mFontFamilyWhitelistActive) {
462 return;
464 nsTHashSet<nsCString> familyNamesWhitelist;
465 for (const auto& item : mEnabledFontsList) {
466 nsAutoCString key;
467 ToLowerCase(item, key);
468 familyNamesWhitelist.Insert(key);
470 AutoTArray<fontlist::Family::InitData, 128> accepted;
471 bool keptNonHidden = false;
472 for (auto& f : aFamilies) {
473 if (familyNamesWhitelist.Contains(f.mKey)) {
474 accepted.AppendElement(f);
475 if (f.mVisibility != FontVisibility::Hidden) {
476 keptNonHidden = true;
480 if (!keptNonHidden) {
481 // No (visible) families were whitelisted: ignore the whitelist
482 // and just leave the fontlist unchanged.
483 return;
485 aFamilies = std::move(accepted);
488 bool gfxPlatformFontList::FamilyInList(const nsACString& aName,
489 const char* aList[], size_t aCount) {
490 size_t result;
491 return BinarySearchIf(
492 aList, 0, aCount,
493 [&](const char* const aVal) -> int {
494 return nsCaseInsensitiveUTF8StringComparator(
495 aName.BeginReading(), aVal, aName.Length(), strlen(aVal));
497 &result);
500 void gfxPlatformFontList::CheckFamilyList(const char* aList[], size_t aCount) {
501 #ifdef DEBUG
502 MOZ_ASSERT(aCount > 0, "empty font family list?");
503 const char* a = aList[0];
504 uint32_t aLen = strlen(a);
505 for (size_t i = 1; i < aCount; ++i) {
506 const char* b = aList[i];
507 uint32_t bLen = strlen(b);
508 if (nsCaseInsensitiveUTF8StringComparator(a, b, aLen, bLen) >= 0) {
509 MOZ_CRASH_UNSAFE_PRINTF("incorrectly sorted font family list: %s >= %s",
510 a, b);
512 a = b;
513 aLen = bLen;
515 #endif
518 bool gfxPlatformFontList::AddWithLegacyFamilyName(const nsACString& aLegacyName,
519 gfxFontEntry* aFontEntry,
520 FontVisibility aVisibility) {
521 mLock.AssertCurrentThreadIn();
522 bool added = false;
523 nsAutoCString key;
524 ToLowerCase(aLegacyName, key);
525 mOtherFamilyNames
526 .LookupOrInsertWith(key,
527 [&] {
528 RefPtr<gfxFontFamily> family =
529 CreateFontFamily(aLegacyName, aVisibility);
530 // We don't want the family to search for faces,
531 // we're adding them directly here.
532 family->SetHasStyles(true);
533 // And we don't want it to attempt to search for
534 // legacy names, because we've already done that
535 // (and this is the result).
536 family->SetCheckedForLegacyFamilyNames(true);
537 added = true;
538 return family;
540 ->AddFontEntry(aFontEntry->Clone());
541 return added;
544 bool gfxPlatformFontList::InitFontList() {
545 // If the startup font-list-init thread is still running, we need to wait
546 // for it to finish before trying to reinitialize here.
547 if (sInitFontListThread && !IsInitFontListThread()) {
548 PR_JoinThread(sInitFontListThread);
549 sInitFontListThread = nullptr;
552 AutoLock lock(mLock);
554 if (LOG_FONTINIT_ENABLED()) {
555 LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
558 if (IsInitialized()) {
559 // Font-list reinitialization always occurs on the main thread, in response
560 // to a change notification; it's only the initial creation during startup
561 // that may be on another thread.
562 MOZ_ASSERT(NS_IsMainThread());
564 // Rebuilding fontlist so clear out font/word caches.
565 gfxFontCache* fontCache = gfxFontCache::GetCache();
566 if (fontCache) {
567 fontCache->FlushShapedWordCaches();
568 fontCache->Flush();
571 gfxPlatform::PurgeSkiaFontCache();
573 // There's no need to broadcast this reflow request to child processes, as
574 // ContentParent::NotifyUpdatedFonts deals with it by re-entering into this
575 // function on child processes.
576 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::Yes,
577 gfxPlatform::BroadcastToChildren::No);
579 mAliasTable.Clear();
580 mLocalNameTable.Clear();
581 mIconFontsSet.Clear();
583 CancelLoadCmapsTask();
584 mStartedLoadingCmapsFrom = 0xffffffffu;
586 CancelInitOtherFamilyNamesTask();
587 mFontFamilies.Clear();
588 mOtherFamilyNames.Clear();
589 mOtherFamilyNamesInitialized = false;
591 if (mExtraNames) {
592 mExtraNames->mFullnames.Clear();
593 mExtraNames->mPostscriptNames.Clear();
595 mFaceNameListsInitialized = false;
596 ClearLangGroupPrefFontsLocked();
597 CancelLoader();
599 // Clear cached family records that will no longer be valid.
600 for (auto& f : mReplacementCharFallbackFamily) {
601 f = FontFamily();
604 gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
605 mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
607 LoadIconFontOverrideList();
610 // From here, gfxPlatformFontList::IsInitialized will return true,
611 // unless InitFontListForPlatform() fails and we reset it below.
612 mFontlistInitCount++;
614 InitializeCodepointsWithNoFonts();
616 // Try to initialize the cross-process shared font list if enabled by prefs,
617 // but not if we're running in Safe Mode.
618 if (StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
619 !gfxPlatform::InSafeMode()) {
620 for (const auto& entry : mFontEntries.Values()) {
621 if (!entry) {
622 continue;
624 AutoWriteLock lock(entry->mLock);
625 entry->mShmemCharacterMap = nullptr;
626 entry->mShmemFace = nullptr;
627 entry->mFamilyName.Truncate();
629 mFontEntries.Clear();
630 mShmemCharMaps.Clear();
631 bool oldSharedList = mSharedFontList != nullptr;
632 mSharedFontList.reset(new fontlist::FontList(mFontlistInitCount));
633 InitSharedFontListForPlatform();
634 if (mSharedFontList && mSharedFontList->Initialized()) {
635 if (mLocalNameTable.Count()) {
636 SharedFontList()->SetLocalNames(mLocalNameTable);
637 mLocalNameTable.Clear();
639 } else {
640 // something went wrong, fall back to in-process list
641 gfxCriticalNote << "Failed to initialize shared font list, "
642 "falling back to in-process list.";
643 mSharedFontList.reset(nullptr);
645 if (oldSharedList && XRE_IsParentProcess()) {
646 // notify all children of the change
647 if (NS_IsMainThread()) {
648 dom::ContentParent::NotifyUpdatedFonts(true);
649 } else {
650 NS_DispatchToMainThread(NS_NewRunnableFunction(
651 "NotifyUpdatedFonts callback",
652 [] { dom::ContentParent::NotifyUpdatedFonts(true); }));
657 if (!SharedFontList()) {
658 if (NS_FAILED(InitFontListForPlatform())) {
659 mFontlistInitCount = 0;
660 return false;
662 ApplyWhitelist();
665 // Set up mDefaultFontEntry as a "last resort" default that we can use
666 // to avoid crashing if the font list is otherwise unusable.
667 gfxFontStyle defStyle;
668 FontFamily fam = GetDefaultFontLocked(nullptr, &defStyle);
669 gfxFontEntry* fe;
670 if (fam.mShared) {
671 auto face = fam.mShared->FindFaceForStyle(SharedFontList(), defStyle);
672 fe = face ? GetOrCreateFontEntryLocked(face, fam.mShared) : nullptr;
673 } else {
674 fe = fam.mUnshared->FindFontForStyle(defStyle);
676 mDefaultFontEntry = fe;
678 return true;
681 void gfxPlatformFontList::LoadIconFontOverrideList() {
682 mIconFontsSet.Clear();
683 AutoTArray<nsCString, 20> iconFontsList;
684 gfxFontUtils::GetPrefsFontList(kIconFontsPref, iconFontsList);
685 for (auto& name : iconFontsList) {
686 ToLowerCase(name);
687 mIconFontsSet.Insert(name);
691 void gfxPlatformFontList::InitializeCodepointsWithNoFonts() {
692 auto& first = mCodepointsWithNoFonts[FontVisibility(0)];
693 for (auto& bitset : mCodepointsWithNoFonts) {
694 if (&bitset == &first) {
695 bitset.reset();
696 bitset.SetRange(0, 0x1f); // C0 controls
697 bitset.SetRange(0x7f, 0x9f); // C1 controls
698 bitset.SetRange(0xE000, 0xF8FF); // PUA
699 bitset.SetRange(0xF0000, 0x10FFFD); // Supplementary PUA
700 bitset.SetRange(0xfdd0, 0xfdef); // noncharacters
701 for (unsigned i = 0; i <= 0x100000; i += 0x10000) {
702 bitset.SetRange(i + 0xfffe, i + 0xffff); // noncharacters
704 bitset.Compact();
705 } else {
706 bitset = first;
711 void gfxPlatformFontList::FontListChanged() {
712 MOZ_ASSERT(!XRE_IsParentProcess());
713 AutoLock lock(mLock);
714 InitializeCodepointsWithNoFonts();
715 if (SharedFontList()) {
716 // If we're using a shared local face-name list, this may have changed
717 // such that existing font entries held by user font sets are no longer
718 // safe to use: ensure they all get flushed.
719 RebuildLocalFonts(/*aForgetLocalFaces*/ true);
721 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::Yes);
724 void gfxPlatformFontList::GenerateFontListKey(const nsACString& aKeyName,
725 nsACString& aResult) {
726 aResult = aKeyName;
727 ToLowerCase(aResult);
730 void gfxPlatformFontList::GenerateFontListKey(nsACString& aKeyName) {
731 ToLowerCase(aKeyName);
734 // Used if a stylo thread wants to trigger InitOtherFamilyNames in the main
735 // process: we can't do IPC from the stylo thread so we post this to the main
736 // thread instead.
737 class InitOtherFamilyNamesForStylo : public mozilla::Runnable {
738 public:
739 explicit InitOtherFamilyNamesForStylo(bool aDeferOtherFamilyNamesLoading)
740 : Runnable("gfxPlatformFontList::InitOtherFamilyNamesForStylo"),
741 mDefer(aDeferOtherFamilyNamesLoading) {}
743 NS_IMETHOD Run() override {
744 auto pfl = gfxPlatformFontList::PlatformFontList();
745 auto list = pfl->SharedFontList();
746 if (!list) {
747 return NS_OK;
749 bool initialized = false;
750 dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
751 list->GetGeneration(), mDefer, &initialized);
752 pfl->mOtherFamilyNamesInitialized.compareExchange(false, initialized);
753 return NS_OK;
756 private:
757 bool mDefer;
760 #define OTHERNAMES_TIMEOUT 200
762 bool gfxPlatformFontList::InitOtherFamilyNames(
763 bool aDeferOtherFamilyNamesLoading) {
764 if (mOtherFamilyNamesInitialized) {
765 return true;
768 if (SharedFontList() && !XRE_IsParentProcess()) {
769 if (NS_IsMainThread()) {
770 bool initialized;
771 dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
772 SharedFontList()->GetGeneration(), aDeferOtherFamilyNamesLoading,
773 &initialized);
774 mOtherFamilyNamesInitialized.compareExchange(false, initialized);
775 } else {
776 NS_DispatchToMainThread(
777 new InitOtherFamilyNamesForStylo(aDeferOtherFamilyNamesLoading));
779 return mOtherFamilyNamesInitialized;
782 // If the font loader delay has been set to zero, we don't defer loading
783 // additional family names (regardless of the aDefer... parameter), as we
784 // take this to mean availability of font info is to be prioritized over
785 // potential startup perf or main-thread jank.
786 // (This is used so we can reliably run reftests that depend on localized
787 // font-family names being available.)
788 if (aDeferOtherFamilyNamesLoading &&
789 StaticPrefs::gfx_font_loader_delay() > 0) {
790 if (!mPendingOtherFamilyNameTask) {
791 RefPtr<mozilla::CancelableRunnable> task =
792 new InitOtherFamilyNamesRunnable();
793 mPendingOtherFamilyNameTask = task;
794 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
796 } else {
797 InitOtherFamilyNamesInternal(false);
799 return mOtherFamilyNamesInitialized;
802 // time limit for loading facename lists (ms)
803 #define NAMELIST_TIMEOUT 200
805 gfxFontEntry* gfxPlatformFontList::SearchFamiliesForFaceName(
806 const nsACString& aFaceName) {
807 TimeStamp start = TimeStamp::Now();
808 bool timedOut = false;
809 // if mFirstChar is not 0, only load facenames for families
810 // that start with this character
811 char16_t firstChar = 0;
812 gfxFontEntry* lookup = nullptr;
814 // iterate over familes starting with the same letter
815 firstChar = ToLowerCase(aFaceName.CharAt(0));
817 for (const auto& entry : mFontFamilies) {
818 nsCStringHashKey::KeyType key = entry.GetKey();
819 const RefPtr<gfxFontFamily>& family = entry.GetData();
821 // when filtering, skip names that don't start with the filter character
822 if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
823 continue;
826 family->ReadFaceNames(this, NeedFullnamePostscriptNames());
828 TimeDuration elapsed = TimeStamp::Now() - start;
829 if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
830 timedOut = true;
831 break;
835 lookup = FindFaceName(aFaceName);
837 TimeStamp end = TimeStamp::Now();
838 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS, start,
839 end);
840 if (LOG_FONTINIT_ENABLED()) {
841 TimeDuration elapsed = end - start;
842 LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
843 elapsed.ToMilliseconds(), (lookup ? "found name" : ""),
844 (timedOut ? "timeout" : "")));
847 return lookup;
850 gfxFontEntry* gfxPlatformFontList::FindFaceName(const nsACString& aFaceName) {
851 gfxFontEntry* lookup;
853 // lookup in name lookup tables, return null if not found
854 if (mExtraNames &&
855 ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
856 (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
857 return lookup;
860 return nullptr;
863 gfxFontEntry* gfxPlatformFontList::LookupInFaceNameLists(
864 const nsACString& aFaceName) {
865 gfxFontEntry* lookup = nullptr;
867 // initialize facename lookup tables if needed
868 // note: this can terminate early or time out, in which case
869 // mFaceNameListsInitialized remains false
870 if (!mFaceNameListsInitialized) {
871 lookup = SearchFamiliesForFaceName(aFaceName);
872 if (lookup) {
873 return lookup;
877 // lookup in name lookup tables, return null if not found
878 if (!(lookup = FindFaceName(aFaceName))) {
879 // names not completely initialized, so keep track of lookup misses
880 if (!mFaceNameListsInitialized) {
881 if (!mFaceNamesMissed) {
882 mFaceNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
884 mFaceNamesMissed->Insert(aFaceName);
888 return lookup;
891 gfxFontEntry* gfxPlatformFontList::LookupInSharedFaceNameList(
892 nsPresContext* aPresContext, const nsACString& aFaceName,
893 WeightRange aWeightForEntry, StretchRange aStretchForEntry,
894 SlantStyleRange aStyleForEntry) {
895 nsAutoCString keyName(aFaceName);
896 ToLowerCase(keyName);
897 fontlist::FontList* list = SharedFontList();
898 fontlist::Family* family = nullptr;
899 fontlist::Face* face = nullptr;
900 if (list->NumLocalFaces()) {
901 fontlist::LocalFaceRec* rec = list->FindLocalFace(keyName);
902 if (rec) {
903 auto* families = list->Families();
904 if (families) {
905 family = &families[rec->mFamilyIndex];
906 face = family->Faces(list)[rec->mFaceIndex].ToPtr<fontlist::Face>(list);
909 } else {
910 list->SearchForLocalFace(keyName, &family, &face);
912 if (!face || !family) {
913 return nullptr;
915 FontVisibility level =
916 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
917 if (!IsVisibleToCSS(*family, level)) {
918 if (aPresContext) {
919 aPresContext->ReportBlockedFontFamily(*family);
921 return nullptr;
923 gfxFontEntry* fe = CreateFontEntry(face, family);
924 if (fe) {
925 fe->mIsLocalUserFont = true;
926 fe->mWeightRange = aWeightForEntry;
927 fe->mStretchRange = aStretchForEntry;
928 fe->mStyleRange = aStyleForEntry;
930 return fe;
933 void gfxPlatformFontList::LoadBadUnderlineList() {
934 gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset",
935 mBadUnderlineFamilyNames);
936 for (auto& fam : mBadUnderlineFamilyNames) {
937 ToLowerCase(fam);
939 mBadUnderlineFamilyNames.Compact();
940 mBadUnderlineFamilyNames.Sort();
943 void gfxPlatformFontList::UpdateFontList(bool aFullRebuild) {
944 MOZ_ASSERT(NS_IsMainThread());
945 if (aFullRebuild) {
946 InitFontList();
947 AutoLock lock(mLock);
948 RebuildLocalFonts();
949 } else {
950 // The font list isn't being fully rebuilt, we're just being notified that
951 // character maps have been updated and so font fallback needs to be re-
952 // done. We only care about this if we have previously encountered a
953 // fallback that required cmaps that were not yet available, and so we
954 // asked for the async cmap loader to run.
955 AutoLock lock(mLock);
956 if (mStartedLoadingCmapsFrom != 0xffffffffu) {
957 InitializeCodepointsWithNoFonts();
958 mStartedLoadingCmapsFrom = 0xffffffffu;
959 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::No);
964 bool gfxPlatformFontList::IsVisibleToCSS(const gfxFontFamily& aFamily,
965 FontVisibility aVisibility) const {
966 return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
969 bool gfxPlatformFontList::IsVisibleToCSS(const fontlist::Family& aFamily,
970 FontVisibility aVisibility) const {
971 return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
974 void gfxPlatformFontList::GetFontList(nsAtom* aLangGroup,
975 const nsACString& aGenericFamily,
976 nsTArray<nsString>& aListOfFonts) {
977 AutoLock lock(mLock);
979 if (SharedFontList()) {
980 fontlist::FontList* list = SharedFontList();
981 const fontlist::Family* families = list->Families();
982 if (families) {
983 for (uint32_t i = 0; i < list->NumFamilies(); i++) {
984 auto& f = families[i];
985 if (!IsVisibleToCSS(f, FontVisibility::User) || f.IsAltLocaleFamily()) {
986 continue;
988 // XXX TODO: filter families for aGenericFamily, if supported by
989 // platform
990 aListOfFonts.AppendElement(
991 NS_ConvertUTF8toUTF16(list->LocalizedFamilyName(&f)));
994 return;
997 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
998 if (!IsVisibleToCSS(*family, FontVisibility::User)) {
999 continue;
1001 if (family->FilterForFontList(aLangGroup, aGenericFamily)) {
1002 nsAutoCString localizedFamilyName;
1003 family->LocalizedName(localizedFamilyName);
1004 aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(localizedFamilyName));
1008 aListOfFonts.Sort();
1009 aListOfFonts.Compact();
1012 void gfxPlatformFontList::GetFontFamilyList(
1013 nsTArray<RefPtr<gfxFontFamily>>& aFamilyArray) {
1014 AutoLock lock(mLock);
1015 MOZ_ASSERT(aFamilyArray.IsEmpty());
1016 // This doesn't use ToArray, because the caller passes an AutoTArray.
1017 aFamilyArray.SetCapacity(mFontFamilies.Count());
1018 for (const auto& family : mFontFamilies.Values()) {
1019 aFamilyArray.AppendElement(family);
1023 already_AddRefed<gfxFont> gfxPlatformFontList::SystemFindFontForChar(
1024 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
1025 Script aRunScript, eFontPresentation aPresentation,
1026 const gfxFontStyle* aStyle, FontVisibility* aVisibility) {
1027 AutoLock lock(mLock);
1028 FontVisibility level =
1029 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1030 MOZ_ASSERT(!mCodepointsWithNoFonts[level].test(aCh),
1031 "don't call for codepoints already known to be unsupported");
1033 // Try to short-circuit font fallback for U+FFFD, used to represent
1034 // encoding errors: just use cached family from last time U+FFFD was seen.
1035 // This helps speed up pages with lots of encoding errors, binary-as-text,
1036 // etc.
1037 if (aCh == 0xFFFD) {
1038 gfxFontEntry* fontEntry = nullptr;
1039 auto& fallbackFamily = mReplacementCharFallbackFamily[level];
1040 if (fallbackFamily.mShared) {
1041 fontlist::Face* face =
1042 fallbackFamily.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
1043 if (face) {
1044 fontEntry = GetOrCreateFontEntryLocked(face, fallbackFamily.mShared);
1045 *aVisibility = fallbackFamily.mShared->Visibility();
1047 } else if (fallbackFamily.mUnshared) {
1048 fontEntry = fallbackFamily.mUnshared->FindFontForStyle(*aStyle);
1049 *aVisibility = fallbackFamily.mUnshared->Visibility();
1052 // this should never fail, as we must have found U+FFFD in order to set
1053 // mReplacementCharFallbackFamily[...] at all, but better play it safe
1054 if (fontEntry && fontEntry->HasCharacter(aCh)) {
1055 return fontEntry->FindOrMakeFont(aStyle);
1059 TimeStamp start = TimeStamp::Now();
1061 // search commonly available fonts
1062 bool common = true;
1063 FontFamily fallbackFamily;
1064 RefPtr<gfxFont> candidate =
1065 CommonFontFallback(aPresContext, aCh, aNextCh, aRunScript, aPresentation,
1066 aStyle, fallbackFamily);
1067 RefPtr<gfxFont> font;
1068 if (candidate) {
1069 if (aPresentation == eFontPresentation::Any) {
1070 font = std::move(candidate);
1071 } else {
1072 bool hasColorGlyph = candidate->HasColorGlyphFor(aCh, aNextCh);
1073 if (hasColorGlyph == PrefersColor(aPresentation)) {
1074 font = std::move(candidate);
1079 // If we didn't find a common font, or it was not the preferred type (color
1080 // or monochrome), do system-wide fallback (except for specials).
1081 uint32_t cmapCount = 0;
1082 if (!font) {
1083 common = false;
1084 font = GlobalFontFallback(aPresContext, aCh, aNextCh, aRunScript,
1085 aPresentation, aStyle, cmapCount, fallbackFamily);
1086 // If the font we found doesn't match the requested type, and we also found
1087 // a candidate above, prefer that one.
1088 if (font && aPresentation != eFontPresentation::Any && candidate) {
1089 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1090 if (hasColorGlyph != PrefersColor(aPresentation)) {
1091 font = std::move(candidate);
1095 TimeDuration elapsed = TimeStamp::Now() - start;
1097 LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
1099 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
1100 Script script = intl::UnicodeProperties::GetScriptCode(aCh);
1101 MOZ_LOG(log, LogLevel::Warning,
1102 ("(textrun-systemfallback-%s) char: u+%6.6x "
1103 "script: %d match: [%s]"
1104 " time: %dus cmaps: %d\n",
1105 (common ? "common" : "global"), aCh, static_cast<int>(script),
1106 (font ? font->GetFontEntry()->Name().get() : "<none>"),
1107 int32_t(elapsed.ToMicroseconds()), cmapCount));
1110 // no match? add to set of non-matching codepoints
1111 if (!font) {
1112 mCodepointsWithNoFonts[level].set(aCh);
1113 } else {
1114 *aVisibility = fallbackFamily.mShared
1115 ? fallbackFamily.mShared->Visibility()
1116 : fallbackFamily.mUnshared->Visibility();
1117 if (aCh == 0xFFFD) {
1118 mReplacementCharFallbackFamily[level] = fallbackFamily;
1122 // track system fallback time
1123 static bool first = true;
1124 int32_t intElapsed =
1125 int32_t(first ? elapsed.ToMilliseconds() : elapsed.ToMicroseconds());
1126 Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST
1127 : Telemetry::SYSTEM_FONT_FALLBACK),
1128 intElapsed);
1129 first = false;
1131 // track the script for which fallback occurred (incremented one make it
1132 // 1-based)
1133 Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT,
1134 int(aRunScript) + 1);
1136 return font.forget();
1139 #define NUM_FALLBACK_FONTS 8
1141 already_AddRefed<gfxFont> gfxPlatformFontList::CommonFontFallback(
1142 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
1143 Script aRunScript, eFontPresentation aPresentation,
1144 const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
1145 AutoTArray<const char*, NUM_FALLBACK_FONTS> defaultFallbacks;
1146 gfxPlatform::GetPlatform()->GetCommonFallbackFonts(
1147 aCh, aRunScript, aPresentation, defaultFallbacks);
1148 GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
1149 FontVisibility level =
1150 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1152 // If a color-emoji presentation is requested, we will check any font found
1153 // to see if it can provide this; if not, we'll remember it as a possible
1154 // candidate but search the remainder of the list for a better choice.
1155 RefPtr<gfxFont> candidateFont;
1156 FontFamily candidateFamily;
1157 auto check = [&](gfxFontEntry* aFontEntry,
1158 FontFamily aFamily) -> already_AddRefed<gfxFont> {
1159 RefPtr<gfxFont> font = aFontEntry->FindOrMakeFont(aMatchStyle);
1160 if (aPresentation < eFontPresentation::EmojiDefault ||
1161 font->HasColorGlyphFor(aCh, aNextCh)) {
1162 aMatchedFamily = aFamily;
1163 return font.forget();
1165 // We want a color glyph but this font only has monochrome; remember it
1166 // (unless we already have a candidate) but continue to search.
1167 if (!candidateFont) {
1168 candidateFont = std::move(font);
1169 candidateFamily = aFamily;
1171 return nullptr;
1174 if (SharedFontList()) {
1175 for (const auto name : defaultFallbacks) {
1176 fontlist::Family* family =
1177 FindSharedFamily(aPresContext, nsDependentCString(name));
1178 if (!family || !IsVisibleToCSS(*family, level)) {
1179 continue;
1181 // XXX(jfkthame) Should we fire the async cmap-loader here, or let it
1182 // always do a potential sync initialization of the family?
1183 family->SearchAllFontsForChar(SharedFontList(), &data);
1184 if (data.mBestMatch) {
1185 RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(family));
1186 if (font) {
1187 return font.forget();
1191 } else {
1192 for (const auto name : defaultFallbacks) {
1193 gfxFontFamily* fallback =
1194 FindFamilyByCanonicalName(nsDependentCString(name));
1195 if (!fallback || !IsVisibleToCSS(*fallback, level)) {
1196 continue;
1198 fallback->FindFontForChar(&data);
1199 if (data.mBestMatch) {
1200 RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(fallback));
1201 if (font) {
1202 return font.forget();
1208 // If we had a candidate that supports the character, but doesn't have the
1209 // desired emoji-style glyph, we'll return it anyhow as nothing better was
1210 // found.
1211 if (candidateFont) {
1212 aMatchedFamily = candidateFamily;
1213 return candidateFont.forget();
1216 return nullptr;
1219 already_AddRefed<gfxFont> gfxPlatformFontList::GlobalFontFallback(
1220 nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
1221 Script aRunScript, eFontPresentation aPresentation,
1222 const gfxFontStyle* aMatchStyle, uint32_t& aCmapCount,
1223 FontFamily& aMatchedFamily) {
1224 bool useCmaps = IsFontFamilyWhitelistActive() ||
1225 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1226 FontVisibility level =
1227 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1228 if (!useCmaps) {
1229 // Allow platform-specific fallback code to try and find a usable font
1230 gfxFontEntry* fe = PlatformGlobalFontFallback(aPresContext, aCh, aRunScript,
1231 aMatchStyle, aMatchedFamily);
1232 if (fe) {
1233 if (aMatchedFamily.mShared) {
1234 if (IsVisibleToCSS(*aMatchedFamily.mShared, level)) {
1235 RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle);
1236 if (font) {
1237 if (aPresentation == eFontPresentation::Any) {
1238 return font.forget();
1240 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1241 if (hasColorGlyph == PrefersColor(aPresentation)) {
1242 return font.forget();
1246 } else {
1247 if (IsVisibleToCSS(*aMatchedFamily.mUnshared, level)) {
1248 RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle);
1249 if (font) {
1250 if (aPresentation == eFontPresentation::Any) {
1251 return font.forget();
1253 bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
1254 if (hasColorGlyph == PrefersColor(aPresentation)) {
1255 return font.forget();
1263 // otherwise, try to find it among local fonts
1264 GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
1265 if (SharedFontList()) {
1266 fontlist::Family* families = SharedFontList()->Families();
1267 if (families) {
1268 for (uint32_t i = 0; i < SharedFontList()->NumFamilies(); i++) {
1269 fontlist::Family& family = families[i];
1270 if (!IsVisibleToCSS(family, level)) {
1271 continue;
1273 if (!family.IsFullyInitialized() &&
1274 StaticPrefs::gfx_font_rendering_fallback_async() &&
1275 !XRE_IsParentProcess()) {
1276 // Start loading all the missing charmaps; but this is async,
1277 // so for now we just continue, ignoring this family.
1278 StartCmapLoadingFromFamily(i);
1279 } else {
1280 family.SearchAllFontsForChar(SharedFontList(), &data);
1281 if (data.mMatchDistance == 0.0) {
1282 // no better style match is possible, so stop searching
1283 break;
1287 if (data.mBestMatch) {
1288 aMatchedFamily = FontFamily(data.mMatchedSharedFamily);
1289 return data.mBestMatch->FindOrMakeFont(aMatchStyle);
1292 } else {
1293 // iterate over all font families to find a font that support the
1294 // character
1295 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
1296 if (!IsVisibleToCSS(*family, level)) {
1297 continue;
1299 // evaluate all fonts in this family for a match
1300 family->FindFontForChar(&data);
1301 if (data.mMatchDistance == 0.0) {
1302 // no better style match is possible, so stop searching
1303 break;
1307 aCmapCount = data.mCmapsTested;
1308 if (data.mBestMatch) {
1309 aMatchedFamily = FontFamily(data.mMatchedFamily);
1310 return data.mBestMatch->FindOrMakeFont(aMatchStyle);
1314 return nullptr;
1317 class StartCmapLoadingRunnable : public mozilla::Runnable {
1318 public:
1319 explicit StartCmapLoadingRunnable(uint32_t aStartIndex)
1320 : Runnable("gfxPlatformFontList::StartCmapLoadingRunnable"),
1321 mStartIndex(aStartIndex) {}
1323 NS_IMETHOD Run() override {
1324 auto* pfl = gfxPlatformFontList::PlatformFontList();
1325 auto* list = pfl->SharedFontList();
1326 if (!list) {
1327 return NS_OK;
1329 if (mStartIndex >= list->NumFamilies()) {
1330 return NS_OK;
1332 if (XRE_IsParentProcess()) {
1333 pfl->StartCmapLoading(list->GetGeneration(), mStartIndex);
1334 } else {
1335 dom::ContentChild::GetSingleton()->SendStartCmapLoading(
1336 list->GetGeneration(), mStartIndex);
1338 return NS_OK;
1341 private:
1342 uint32_t mStartIndex;
1345 void gfxPlatformFontList::StartCmapLoadingFromFamily(uint32_t aStartIndex) {
1346 AutoLock lock(mLock);
1347 if (aStartIndex >= mStartedLoadingCmapsFrom) {
1348 // We already initiated cmap-loading from here or earlier in the list;
1349 // no need to do it again here.
1350 return;
1352 mStartedLoadingCmapsFrom = aStartIndex;
1354 // If we're already on the main thread, don't bother dispatching a runnable
1355 // here to kick off the loading process, just do it directly.
1356 if (NS_IsMainThread()) {
1357 auto* list = SharedFontList();
1358 if (XRE_IsParentProcess()) {
1359 StartCmapLoading(list->GetGeneration(), aStartIndex);
1360 } else {
1361 dom::ContentChild::GetSingleton()->SendStartCmapLoading(
1362 list->GetGeneration(), aStartIndex);
1364 } else {
1365 NS_DispatchToMainThread(new StartCmapLoadingRunnable(aStartIndex));
1369 class LoadCmapsRunnable : public CancelableRunnable {
1370 class WillShutdownObserver : public nsIObserver {
1371 public:
1372 NS_DECL_ISUPPORTS
1373 NS_DECL_NSIOBSERVER
1375 explicit WillShutdownObserver(LoadCmapsRunnable* aRunnable)
1376 : mRunnable(aRunnable) {}
1378 void Remove() {
1379 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1380 if (obs) {
1381 obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
1383 mRunnable = nullptr;
1386 protected:
1387 virtual ~WillShutdownObserver() = default;
1389 LoadCmapsRunnable* mRunnable;
1392 public:
1393 explicit LoadCmapsRunnable(uint32_t aGeneration, uint32_t aFamilyIndex)
1394 : CancelableRunnable("gfxPlatformFontList::LoadCmapsRunnable"),
1395 mGeneration(aGeneration),
1396 mStartIndex(aFamilyIndex),
1397 mIndex(aFamilyIndex) {
1398 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1399 if (obs) {
1400 mObserver = new WillShutdownObserver(this);
1401 obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
1405 virtual ~LoadCmapsRunnable() {
1406 if (mObserver) {
1407 mObserver->Remove();
1411 // Reset the current family index, if the value passed is earlier than our
1412 // original starting position. We don't "reset" if it would move the current
1413 // position forward, or back into the already-scanned range.
1414 // We could optimize further by remembering the current position reached,
1415 // and then skipping ahead from the original start, but it doesn't seem worth
1416 // extra complexity for a task that usually only happens once, and already-
1417 // processed families will be skipped pretty quickly in Run() anyhow.
1418 void MaybeResetIndex(uint32_t aFamilyIndex) {
1419 if (aFamilyIndex < mStartIndex) {
1420 mStartIndex = aFamilyIndex;
1421 mIndex = aFamilyIndex;
1425 nsresult Cancel() override {
1426 mIsCanceled = true;
1427 return NS_OK;
1430 NS_IMETHOD Run() override {
1431 if (mIsCanceled) {
1432 return NS_OK;
1434 auto* pfl = gfxPlatformFontList::PlatformFontList();
1435 auto* list = pfl->SharedFontList();
1436 MOZ_ASSERT(list);
1437 if (!list) {
1438 return NS_OK;
1440 if (mGeneration != list->GetGeneration()) {
1441 return NS_OK;
1443 uint32_t numFamilies = list->NumFamilies();
1444 if (mIndex >= numFamilies) {
1445 return NS_OK;
1447 auto* families = list->Families();
1448 // Skip any families that are already initialized.
1449 while (mIndex < numFamilies && families[mIndex].IsFullyInitialized()) {
1450 ++mIndex;
1452 // Fully process one family, and advance index.
1453 if (mIndex < numFamilies) {
1454 Unused << pfl->InitializeFamily(&families[mIndex], true);
1455 ++mIndex;
1457 // If there are more families to initialize, post ourselves back to the
1458 // idle queue to handle the next one; otherwise we're finished and we need
1459 // to notify content processes to update their rendering.
1460 if (mIndex < numFamilies) {
1461 RefPtr<CancelableRunnable> task = this;
1462 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
1463 } else {
1464 pfl->Lock();
1465 pfl->CancelLoadCmapsTask();
1466 pfl->InitializeCodepointsWithNoFonts();
1467 dom::ContentParent::NotifyUpdatedFonts(false);
1468 pfl->Unlock();
1470 return NS_OK;
1473 private:
1474 uint32_t mGeneration;
1475 uint32_t mStartIndex;
1476 uint32_t mIndex;
1477 bool mIsCanceled = false;
1479 RefPtr<WillShutdownObserver> mObserver;
1482 NS_IMPL_ISUPPORTS(LoadCmapsRunnable::WillShutdownObserver, nsIObserver)
1484 NS_IMETHODIMP
1485 LoadCmapsRunnable::WillShutdownObserver::Observe(nsISupports* aSubject,
1486 const char* aTopic,
1487 const char16_t* aData) {
1488 if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
1489 if (mRunnable) {
1490 mRunnable->Cancel();
1492 } else {
1493 MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
1495 return NS_OK;
1498 void gfxPlatformFontList::StartCmapLoading(uint32_t aGeneration,
1499 uint32_t aStartIndex) {
1500 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
1501 if (aGeneration != SharedFontList()->GetGeneration()) {
1502 return;
1504 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1505 return;
1507 if (mLoadCmapsRunnable) {
1508 // We already have a runnable; just make sure it covers the full range of
1509 // families needed.
1510 static_cast<LoadCmapsRunnable*>(mLoadCmapsRunnable.get())
1511 ->MaybeResetIndex(aStartIndex);
1512 return;
1514 mLoadCmapsRunnable = new LoadCmapsRunnable(aGeneration, aStartIndex);
1515 RefPtr<CancelableRunnable> task = mLoadCmapsRunnable;
1516 NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
1519 gfxFontFamily* gfxPlatformFontList::CheckFamily(gfxFontFamily* aFamily) {
1520 if (aFamily && !aFamily->HasStyles()) {
1521 aFamily->FindStyleVariations();
1524 if (aFamily && aFamily->FontListLength() == 0) {
1525 // Failed to load any faces for this family, so discard it.
1526 nsAutoCString key;
1527 GenerateFontListKey(aFamily->Name(), key);
1528 mFontFamilies.Remove(key);
1529 return nullptr;
1532 return aFamily;
1535 bool gfxPlatformFontList::FindAndAddFamilies(
1536 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
1537 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
1538 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1539 gfxFloat aDevToCssSize) {
1540 AutoLock lock(mLock);
1542 #ifdef DEBUG
1543 auto initialLength = aOutput->Length();
1544 #endif
1546 bool didFind =
1547 FindAndAddFamiliesLocked(aPresContext, aGeneric, aFamily, aOutput, aFlags,
1548 aStyle, aLanguage, aDevToCssSize);
1549 #ifdef DEBUG
1550 auto finalLength = aOutput->Length();
1551 // Validate the expectation that the output-array grows if we return true,
1552 // or remains the same (probably empty) if we return false.
1553 MOZ_ASSERT_IF(didFind, finalLength > initialLength);
1554 MOZ_ASSERT_IF(!didFind, finalLength == initialLength);
1555 #endif
1557 return didFind;
1560 bool gfxPlatformFontList::FindAndAddFamiliesLocked(
1561 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
1562 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
1563 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1564 gfxFloat aDevToCssSize) {
1565 nsAutoCString key;
1566 GenerateFontListKey(aFamily, key);
1568 bool allowHidden = bool(aFlags & FindFamiliesFlags::eSearchHiddenFamilies);
1569 FontVisibility visibilityLevel =
1570 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
1572 // If this font lookup is the result of resolving a CSS generic (not a direct
1573 // font-family request by the page), and RFP settings allow generics to be
1574 // unrestricted, bump the effective visibility level applied here so as to
1575 // allow user-installed fonts to be used.
1576 if (visibilityLevel < FontVisibility::User &&
1577 aGeneric != StyleGenericFontFamily::None &&
1578 !aPresContext->Document()->ShouldResistFingerprinting(
1579 RFPTarget::FontVisibilityRestrictGenerics)) {
1580 visibilityLevel = FontVisibility::User;
1583 if (SharedFontList()) {
1584 fontlist::Family* family = SharedFontList()->FindFamily(key);
1585 // If not found, and other family names have not yet been initialized,
1586 // initialize the rest of the list and try again. This is done lazily
1587 // since reading name table entries is expensive.
1588 // Although ASCII localized family names are possible they don't occur
1589 // in practice, so avoid pulling in names at startup.
1590 if (!family && !mOtherFamilyNamesInitialized) {
1591 bool triggerLoading = true;
1592 bool mayDefer =
1593 !(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading);
1594 if (IsAscii(key)) {
1595 // If `key` is an ASCII name, only trigger loading if it includes a
1596 // space, and the "base" name (up to the last space) exists as a known
1597 // family, so that this might be a legacy styled-family name.
1598 const char* data = key.BeginReading();
1599 int32_t index = key.Length();
1600 while (--index > 0) {
1601 if (data[index] == ' ') {
1602 break;
1605 if (index <= 0 ||
1606 !SharedFontList()->FindFamily(nsAutoCString(key.get(), index))) {
1607 triggerLoading = false;
1610 if (triggerLoading) {
1611 if (InitOtherFamilyNames(mayDefer)) {
1612 family = SharedFontList()->FindFamily(key);
1615 if (!family && !mOtherFamilyNamesInitialized &&
1616 !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
1617 AddToMissedNames(key);
1620 // Check whether the family we found is actually allowed to be looked up,
1621 // according to current font-visibility prefs.
1622 if (family) {
1623 bool visible = IsVisibleToCSS(*family, visibilityLevel);
1624 if (visible || (allowHidden && family->IsHidden())) {
1625 aOutput->AppendElement(FamilyAndGeneric(family, aGeneric));
1626 return true;
1628 if (aPresContext) {
1629 aPresContext->ReportBlockedFontFamily(*family);
1632 return false;
1635 NS_ASSERTION(mFontFamilies.Count() != 0,
1636 "system font list was not initialized correctly");
1638 auto isBlockedByVisibilityLevel = [=](gfxFontFamily* aFamily) -> bool {
1639 bool visible = IsVisibleToCSS(*aFamily, visibilityLevel);
1640 if (visible || (allowHidden && aFamily->IsHidden())) {
1641 return false;
1643 if (aPresContext) {
1644 aPresContext->ReportBlockedFontFamily(*aFamily);
1646 return true;
1649 // lookup in canonical (i.e. English) family name list
1650 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
1651 if (familyEntry) {
1652 if (isBlockedByVisibilityLevel(familyEntry)) {
1653 return false;
1657 // if not found, lookup in other family names list (mostly localized names)
1658 if (!familyEntry) {
1659 familyEntry = mOtherFamilyNames.GetWeak(key);
1661 if (familyEntry) {
1662 if (isBlockedByVisibilityLevel(familyEntry)) {
1663 return false;
1667 // if still not found and other family names not yet fully initialized,
1668 // initialize the rest of the list and try again. this is done lazily
1669 // since reading name table entries is expensive.
1670 // although ASCII localized family names are possible they don't occur
1671 // in practice so avoid pulling in names at startup
1672 if (!familyEntry && !mOtherFamilyNamesInitialized && !IsAscii(aFamily)) {
1673 InitOtherFamilyNames(
1674 !(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading));
1675 familyEntry = mOtherFamilyNames.GetWeak(key);
1676 if (!familyEntry && !mOtherFamilyNamesInitialized &&
1677 !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
1678 // localized family names load timed out, add name to list of
1679 // names to check after localized names are loaded
1680 AddToMissedNames(key);
1682 if (familyEntry) {
1683 if (isBlockedByVisibilityLevel(familyEntry)) {
1684 return false;
1689 familyEntry = CheckFamily(familyEntry);
1691 // If we failed to find the requested family, check for a space in the
1692 // name; if found, and if the "base" name (up to the last space) exists
1693 // as a family, then this might be a legacy GDI-style family name for
1694 // an additional weight/width. Try searching the faces of the base family
1695 // and create any corresponding legacy families.
1696 if (!familyEntry &&
1697 !(aFlags & FindFamiliesFlags::eNoSearchForLegacyFamilyNames)) {
1698 // We don't have nsAString::RFindChar, so look for a space manually
1699 const char* data = aFamily.BeginReading();
1700 int32_t index = aFamily.Length();
1701 while (--index > 0) {
1702 if (data[index] == ' ') {
1703 break;
1706 if (index > 0) {
1707 gfxFontFamily* base =
1708 FindUnsharedFamily(aPresContext, Substring(aFamily, 0, index),
1709 FindFamiliesFlags::eNoSearchForLegacyFamilyNames);
1710 // If we found the "base" family name, and if it has members with
1711 // legacy names, this will add corresponding font-family entries to
1712 // the mOtherFamilyNames list; then retry the legacy-family search.
1713 if (base && base->CheckForLegacyFamilyNames(this)) {
1714 familyEntry = mOtherFamilyNames.GetWeak(key);
1716 if (familyEntry) {
1717 if (isBlockedByVisibilityLevel(familyEntry)) {
1718 return false;
1724 if (familyEntry) {
1725 aOutput->AppendElement(FamilyAndGeneric(familyEntry, aGeneric));
1726 return true;
1729 return false;
1732 void gfxPlatformFontList::AddToMissedNames(const nsCString& aKey) {
1733 if (!mOtherNamesMissed) {
1734 mOtherNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
1736 mOtherNamesMissed->Insert(aKey);
1739 fontlist::Family* gfxPlatformFontList::FindSharedFamily(
1740 nsPresContext* aPresContext, const nsACString& aFamily,
1741 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1742 gfxFloat aDevToCss) {
1743 if (!SharedFontList()) {
1744 return nullptr;
1746 AutoTArray<FamilyAndGeneric, 1> families;
1747 if (!FindAndAddFamiliesLocked(aPresContext, StyleGenericFontFamily::None,
1748 aFamily, &families, aFlags, aStyle, aLanguage,
1749 aDevToCss) ||
1750 !families[0].mFamily.mShared) {
1751 return nullptr;
1753 fontlist::Family* family = families[0].mFamily.mShared;
1754 if (!family->IsInitialized()) {
1755 if (!InitializeFamily(family)) {
1756 return nullptr;
1759 return family;
1762 class InitializeFamilyRunnable : public mozilla::Runnable {
1763 public:
1764 explicit InitializeFamilyRunnable(uint32_t aFamilyIndex, bool aLoadCmaps)
1765 : Runnable("gfxPlatformFontList::InitializeFamilyRunnable"),
1766 mIndex(aFamilyIndex),
1767 mLoadCmaps(aLoadCmaps) {}
1769 NS_IMETHOD Run() override {
1770 auto list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
1771 if (!list) {
1772 return NS_OK;
1774 if (mIndex >= list->NumFamilies()) {
1775 // Out of range? Maybe the list got reinitialized since this request
1776 // was posted - just ignore it.
1777 return NS_OK;
1779 dom::ContentChild::GetSingleton()->SendInitializeFamily(
1780 list->GetGeneration(), mIndex, mLoadCmaps);
1781 return NS_OK;
1784 private:
1785 uint32_t mIndex;
1786 bool mLoadCmaps;
1789 bool gfxPlatformFontList::InitializeFamily(fontlist::Family* aFamily,
1790 bool aLoadCmaps) {
1791 MOZ_ASSERT(SharedFontList());
1792 auto list = SharedFontList();
1793 if (!XRE_IsParentProcess()) {
1794 auto* families = list->Families();
1795 if (!families) {
1796 return false;
1798 uint32_t index = aFamily - families;
1799 if (index >= list->NumFamilies()) {
1800 return false;
1802 if (NS_IsMainThread()) {
1803 dom::ContentChild::GetSingleton()->SendInitializeFamily(
1804 list->GetGeneration(), index, aLoadCmaps);
1805 } else {
1806 NS_DispatchToMainThread(new InitializeFamilyRunnable(index, aLoadCmaps));
1808 return aFamily->IsInitialized();
1811 if (!aFamily->IsInitialized()) {
1812 // The usual case: we're being asked to populate the face list.
1813 AutoTArray<fontlist::Face::InitData, 16> faceList;
1814 GetFacesInitDataForFamily(aFamily, faceList, aLoadCmaps);
1815 aFamily->AddFaces(list, faceList);
1816 } else {
1817 // The family's face list was already initialized, but if aLoadCmaps is
1818 // true we also want to eagerly load character maps. This is used when a
1819 // child process is doing SearchAllFontsForChar, to have the parent load
1820 // all the cmaps at once and reduce IPC traffic (and content-process file
1821 // access overhead, which is crippling for DirectWrite on Windows).
1822 if (aLoadCmaps) {
1823 auto* faces = aFamily->Faces(list);
1824 if (faces) {
1825 for (size_t i = 0; i < aFamily->NumFaces(); i++) {
1826 auto* face = faces[i].ToPtr<fontlist::Face>(list);
1827 if (face && face->mCharacterMap.IsNull()) {
1828 // We don't want to cache this font entry, as the parent will most
1829 // likely never use it again; it's just to populate the charmap for
1830 // the benefit of the child process.
1831 RefPtr<gfxFontEntry> fe = CreateFontEntry(face, aFamily);
1832 if (fe) {
1833 fe->ReadCMAP();
1841 if (aLoadCmaps && aFamily->IsInitialized()) {
1842 aFamily->SetupFamilyCharMap(list);
1845 return aFamily->IsInitialized();
1848 gfxFontEntry* gfxPlatformFontList::FindFontForFamily(
1849 nsPresContext* aPresContext, const nsACString& aFamily,
1850 const gfxFontStyle* aStyle) {
1851 AutoLock lock(mLock);
1853 nsAutoCString key;
1854 GenerateFontListKey(aFamily, key);
1856 FontFamily family = FindFamily(aPresContext, key);
1857 if (family.IsNull()) {
1858 return nullptr;
1860 if (family.mShared) {
1861 auto face = family.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
1862 if (!face) {
1863 return nullptr;
1865 return GetOrCreateFontEntryLocked(face, family.mShared);
1867 return family.mUnshared->FindFontForStyle(*aStyle);
1870 gfxFontEntry* gfxPlatformFontList::GetOrCreateFontEntryLocked(
1871 fontlist::Face* aFace, const fontlist::Family* aFamily) {
1872 return mFontEntries
1873 .LookupOrInsertWith(aFace,
1874 [=] { return CreateFontEntry(aFace, aFamily); })
1875 .get();
1878 void gfxPlatformFontList::AddOtherFamilyNames(
1879 gfxFontFamily* aFamilyEntry, const nsTArray<nsCString>& aOtherFamilyNames) {
1880 AutoLock lock(mLock);
1882 for (const auto& name : aOtherFamilyNames) {
1883 nsAutoCString key;
1884 GenerateFontListKey(name, key);
1886 mOtherFamilyNames.LookupOrInsertWith(key, [&] {
1887 LOG_FONTLIST(
1888 ("(fontlist-otherfamily) canonical family: %s, other family: %s\n",
1889 aFamilyEntry->Name().get(), name.get()));
1890 if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
1891 aFamilyEntry->SetBadUnderlineFamily();
1893 return RefPtr{aFamilyEntry};
1898 void gfxPlatformFontList::AddFullnameLocked(gfxFontEntry* aFontEntry,
1899 const nsCString& aFullname) {
1900 mExtraNames->mFullnames.LookupOrInsertWith(aFullname, [&] {
1901 LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
1902 aFontEntry->Name().get(), aFullname.get()));
1903 return RefPtr{aFontEntry};
1907 void gfxPlatformFontList::AddPostscriptNameLocked(
1908 gfxFontEntry* aFontEntry, const nsCString& aPostscriptName) {
1909 mExtraNames->mPostscriptNames.LookupOrInsertWith(aPostscriptName, [&] {
1910 LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
1911 aFontEntry->Name().get(), aPostscriptName.get()));
1912 return RefPtr{aFontEntry};
1916 bool gfxPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
1917 nsACString& aFamilyName) {
1918 AutoLock lock(mLock);
1919 FontFamily family = FindFamily(nullptr, aFontName);
1920 if (family.IsNull()) {
1921 return false;
1923 return GetLocalizedFamilyName(family, aFamilyName);
1926 bool gfxPlatformFontList::GetLocalizedFamilyName(const FontFamily& aFamily,
1927 nsACString& aFamilyName) {
1928 if (aFamily.mShared) {
1929 aFamilyName = SharedFontList()->LocalizedFamilyName(aFamily.mShared);
1930 return true;
1932 if (aFamily.mUnshared) {
1933 aFamily.mUnshared->LocalizedName(aFamilyName);
1934 return true;
1936 return false; // leaving the aFamilyName outparam untouched
1939 FamilyAndGeneric gfxPlatformFontList::GetDefaultFontFamily(
1940 const nsACString& aLangGroup, const nsACString& aGenericFamily) {
1941 if (NS_WARN_IF(aLangGroup.IsEmpty()) ||
1942 NS_WARN_IF(aGenericFamily.IsEmpty())) {
1943 return FamilyAndGeneric();
1946 AutoLock lock(mLock);
1948 nsAutoCString value;
1949 AutoTArray<nsCString, 4> names;
1950 if (mFontPrefs->LookupNameList(PrefName(aGenericFamily, aLangGroup), value)) {
1951 gfxFontUtils::ParseFontList(value, names);
1954 for (const nsCString& name : names) {
1955 FontFamily family = FindFamily(nullptr, name);
1956 if (!family.IsNull()) {
1957 return FamilyAndGeneric(family);
1961 return FamilyAndGeneric();
1964 ShmemCharMapHashEntry::ShmemCharMapHashEntry(const gfxSparseBitSet* aCharMap)
1965 : mList(gfxPlatformFontList::PlatformFontList()->SharedFontList()),
1966 mHash(aCharMap->GetChecksum()) {
1967 size_t len = SharedBitSet::RequiredSize(*aCharMap);
1968 mCharMap = mList->Alloc(len);
1969 SharedBitSet::Create(mCharMap.ToPtr(mList, len), len, *aCharMap);
1972 fontlist::Pointer gfxPlatformFontList::GetShmemCharMapLocked(
1973 const gfxSparseBitSet* aCmap) {
1974 auto* entry = mShmemCharMaps.GetEntry(aCmap);
1975 if (!entry) {
1976 entry = mShmemCharMaps.PutEntry(aCmap);
1978 return entry->GetCharMap();
1981 // Lookup aCmap in the shared cmap set, adding if not already present.
1982 // This is the only way for a reference to a gfxCharacterMap to be acquired
1983 // by another thread than its original creator.
1984 already_AddRefed<gfxCharacterMap> gfxPlatformFontList::FindCharMap(
1985 gfxCharacterMap* aCmap) {
1986 // Lock to prevent potentially racing against MaybeRemoveCmap.
1987 AutoLock lock(mLock);
1989 // Find existing entry or insert a new one (which will add a reference).
1990 aCmap->CalcHash();
1991 aCmap->mShared = true; // Set the shared flag in preparation for adding
1992 // to the global table.
1993 RefPtr cmap = mSharedCmaps.PutEntry(aCmap)->GetKey();
1995 // If we ended up finding a different, pre-existing entry, clear the
1996 // shared flag on this one so that it'll get deleted on Release().
1997 if (cmap.get() != aCmap) {
1998 aCmap->mShared = false;
2001 return cmap.forget();
2004 // Potentially remove the charmap from the shared cmap set. This is called
2005 // when a user of the charmap drops a reference and the refcount goes to 1;
2006 // in that case, it is possible our shared set is the only remaining user
2007 // of the object, and we should remove it.
2008 // Note that aCharMap might have already been freed, so we must not try to
2009 // dereference it until we have checked that it's still present in our table.
2010 void gfxPlatformFontList::MaybeRemoveCmap(gfxCharacterMap* aCharMap) {
2011 // Lock so that nobody else can get a reference via FindCharMap while we're
2012 // checking here.
2013 AutoLock lock(mLock);
2015 // Skip lookups during teardown.
2016 if (!mSharedCmaps.Count()) {
2017 return;
2020 // aCharMap needs to match the entry and be the same ptr and still have a
2021 // refcount of exactly 1 (i.e. we hold the only reference) before removing.
2022 // If we're racing another thread, it might already have been removed, in
2023 // which case GetEntry will not find it and we won't try to dereference the
2024 // already-freed pointer.
2025 CharMapHashKey* found =
2026 mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
2027 if (found && found->GetKey() == aCharMap && aCharMap->RefCount() == 1) {
2028 // Forget our reference to the object that's being deleted, without
2029 // calling Release() on it.
2030 Unused << found->mCharMap.forget();
2032 // Do the deletion.
2033 delete aCharMap;
2035 // Log this as a "Release" to keep leak-checking correct.
2036 NS_LOG_RELEASE(aCharMap, 0, "gfxCharacterMap");
2038 mSharedCmaps.RemoveEntry(found);
2042 static void GetSystemUIFontFamilies([[maybe_unused]] nsAtom* aLangGroup,
2043 nsTArray<nsCString>& aFamilies) {
2044 // TODO: On macOS, use CTCreateUIFontForLanguage or such thing (though the
2045 // code below ends up using [NSFont systemFontOfSize: 0.0].
2046 nsFont systemFont;
2047 gfxFontStyle fontStyle;
2048 nsAutoString systemFontName;
2049 if (!LookAndFeel::GetFont(StyleSystemFont::Menu, systemFontName, fontStyle)) {
2050 return;
2052 systemFontName.Trim("\"'");
2053 CopyUTF16toUTF8(systemFontName, *aFamilies.AppendElement());
2056 void gfxPlatformFontList::ResolveGenericFontNames(
2057 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2058 eFontPrefLang aPrefLang, PrefFontList* aGenericFamilies) {
2059 const char* langGroupStr = GetPrefLangName(aPrefLang);
2060 const char* generic = GetGenericName(aGenericType);
2062 if (!generic) {
2063 return;
2066 AutoTArray<nsCString, 4> genericFamilies;
2068 // load family for "font.name.generic.lang"
2069 PrefName prefName(generic, langGroupStr);
2070 nsAutoCString value;
2071 if (mFontPrefs->LookupName(prefName, value)) {
2072 gfxFontUtils::ParseFontList(value, genericFamilies);
2075 // load fonts for "font.name-list.generic.lang"
2076 if (mFontPrefs->LookupNameList(prefName, value)) {
2077 gfxFontUtils::ParseFontList(value, genericFamilies);
2080 nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
2081 MOZ_ASSERT(langGroup, "null lang group for pref lang");
2083 if (aGenericType == StyleGenericFontFamily::SystemUi) {
2084 GetSystemUIFontFamilies(langGroup, genericFamilies);
2087 GetFontFamiliesFromGenericFamilies(
2088 aPresContext, aGenericType, genericFamilies, langGroup, aGenericFamilies);
2090 #if 0 // dump out generic mappings
2091 printf("%s ===> ", NamePref(generic, langGroupStr).get());
2092 for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
2093 if (k > 0) printf(", ");
2094 printf("%s", (*aGenericFamilies)[k].mIsShared
2095 ? (*aGenericFamilies)[k].mShared->DisplayName().AsString(SharedFontList()).get()
2096 : (*aGenericFamilies)[k].mUnshared->Name().get());
2098 printf("\n");
2099 #endif
2102 void gfxPlatformFontList::ResolveEmojiFontNames(
2103 nsPresContext* aPresContext, PrefFontList* aGenericFamilies) {
2104 // emoji preference has no lang name
2105 AutoTArray<nsCString, 4> genericFamilies;
2107 nsAutoCString value;
2108 if (mFontPrefs->LookupNameList(PrefName("emoji", ""), value)) {
2109 gfxFontUtils::ParseFontList(value, genericFamilies);
2112 GetFontFamiliesFromGenericFamilies(
2113 aPresContext, StyleGenericFontFamily::MozEmoji, genericFamilies, nullptr,
2114 aGenericFamilies);
2117 void gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(
2118 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2119 nsTArray<nsCString>& aGenericNameFamilies, nsAtom* aLangGroup,
2120 PrefFontList* aGenericFamilies) {
2121 // lookup and add platform fonts uniquely
2122 for (const nsCString& genericFamily : aGenericNameFamilies) {
2123 AutoTArray<FamilyAndGeneric, 10> families;
2124 FindAndAddFamiliesLocked(aPresContext, aGenericType, genericFamily,
2125 &families, FindFamiliesFlags(0), nullptr,
2126 aLangGroup);
2127 for (const FamilyAndGeneric& f : families) {
2128 if (!aGenericFamilies->Contains(f.mFamily)) {
2129 aGenericFamilies->AppendElement(f.mFamily);
2135 gfxPlatformFontList::PrefFontList*
2136 gfxPlatformFontList::GetPrefFontsLangGroupLocked(
2137 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2138 eFontPrefLang aPrefLang) {
2139 if (aGenericType == StyleGenericFontFamily::MozEmoji ||
2140 aPrefLang == eFontPrefLang_Emoji) {
2141 // Emoji font has no lang
2142 PrefFontList* prefFonts = mEmojiPrefFont.get();
2143 if (MOZ_UNLIKELY(!prefFonts)) {
2144 prefFonts = new PrefFontList;
2145 ResolveEmojiFontNames(aPresContext, prefFonts);
2146 mEmojiPrefFont.reset(prefFonts);
2148 return prefFonts;
2151 auto index = static_cast<size_t>(aGenericType);
2152 PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][index].get();
2153 if (MOZ_UNLIKELY(!prefFonts)) {
2154 prefFonts = new PrefFontList;
2155 ResolveGenericFontNames(aPresContext, aGenericType, aPrefLang, prefFonts);
2156 mLangGroupPrefFonts[aPrefLang][index].reset(prefFonts);
2158 return prefFonts;
2161 void gfxPlatformFontList::AddGenericFonts(
2162 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2163 nsAtom* aLanguage, nsTArray<FamilyAndGeneric>& aFamilyList) {
2164 AutoLock lock(mLock);
2166 // map lang ==> langGroup
2167 nsAtom* langGroup = GetLangGroup(aLanguage);
2169 // langGroup ==> prefLang
2170 eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
2172 // lookup pref fonts
2173 PrefFontList* prefFonts =
2174 GetPrefFontsLangGroupLocked(aPresContext, aGenericType, prefLang);
2176 if (!prefFonts->IsEmpty()) {
2177 aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
2178 for (auto& f : *prefFonts) {
2179 aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
2184 static nsAtom* PrefLangToLangGroups(uint32_t aIndex) {
2185 // static array here avoids static constructor
2186 static nsAtom* gPrefLangToLangGroups[] = {
2187 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
2188 #include "gfxFontPrefLangList.h"
2189 #undef FONT_PREF_LANG
2192 return aIndex < ArrayLength(gPrefLangToLangGroups)
2193 ? gPrefLangToLangGroups[aIndex]
2194 : nsGkAtoms::Unicode;
2197 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(const char* aLang) {
2198 if (!aLang || !aLang[0]) {
2199 return eFontPrefLang_Others;
2201 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
2202 if (!nsCRT::strcasecmp(gPrefLangNames[i], aLang)) {
2203 return eFontPrefLang(i);
2206 return eFontPrefLang_Others;
2209 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(nsAtom* aLang) {
2210 if (!aLang) return eFontPrefLang_Others;
2211 nsAutoCString lang;
2212 aLang->ToUTF8String(lang);
2213 return GetFontPrefLangFor(lang.get());
2216 nsAtom* gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang) {
2217 // the special CJK set pref lang should be resolved into separate
2218 // calls to individual CJK pref langs before getting here
2219 NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
2221 return PrefLangToLangGroups(uint32_t(aLang));
2224 const char* gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang) {
2225 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
2226 return gPrefLangNames[uint32_t(aLang)];
2228 return nullptr;
2231 eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(uint32_t aCh) {
2232 switch (ublock_getCode(aCh)) {
2233 case UBLOCK_BASIC_LATIN:
2234 case UBLOCK_LATIN_1_SUPPLEMENT:
2235 case UBLOCK_LATIN_EXTENDED_A:
2236 case UBLOCK_LATIN_EXTENDED_B:
2237 case UBLOCK_IPA_EXTENSIONS:
2238 case UBLOCK_SPACING_MODIFIER_LETTERS:
2239 case UBLOCK_LATIN_EXTENDED_ADDITIONAL:
2240 case UBLOCK_LATIN_EXTENDED_C:
2241 case UBLOCK_LATIN_EXTENDED_D:
2242 case UBLOCK_LATIN_EXTENDED_E:
2243 case UBLOCK_PHONETIC_EXTENSIONS:
2244 return eFontPrefLang_Western;
2245 case UBLOCK_GREEK:
2246 case UBLOCK_GREEK_EXTENDED:
2247 return eFontPrefLang_Greek;
2248 case UBLOCK_CYRILLIC:
2249 case UBLOCK_CYRILLIC_SUPPLEMENT:
2250 case UBLOCK_CYRILLIC_EXTENDED_A:
2251 case UBLOCK_CYRILLIC_EXTENDED_B:
2252 case UBLOCK_CYRILLIC_EXTENDED_C:
2253 return eFontPrefLang_Cyrillic;
2254 case UBLOCK_ARMENIAN:
2255 return eFontPrefLang_Armenian;
2256 case UBLOCK_HEBREW:
2257 return eFontPrefLang_Hebrew;
2258 case UBLOCK_ARABIC:
2259 case UBLOCK_ARABIC_PRESENTATION_FORMS_A:
2260 case UBLOCK_ARABIC_PRESENTATION_FORMS_B:
2261 case UBLOCK_ARABIC_SUPPLEMENT:
2262 case UBLOCK_ARABIC_EXTENDED_A:
2263 case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS:
2264 return eFontPrefLang_Arabic;
2265 case UBLOCK_DEVANAGARI:
2266 case UBLOCK_DEVANAGARI_EXTENDED:
2267 return eFontPrefLang_Devanagari;
2268 case UBLOCK_BENGALI:
2269 return eFontPrefLang_Bengali;
2270 case UBLOCK_GURMUKHI:
2271 return eFontPrefLang_Gurmukhi;
2272 case UBLOCK_GUJARATI:
2273 return eFontPrefLang_Gujarati;
2274 case UBLOCK_ORIYA:
2275 return eFontPrefLang_Oriya;
2276 case UBLOCK_TAMIL:
2277 return eFontPrefLang_Tamil;
2278 case UBLOCK_TELUGU:
2279 return eFontPrefLang_Telugu;
2280 case UBLOCK_KANNADA:
2281 return eFontPrefLang_Kannada;
2282 case UBLOCK_MALAYALAM:
2283 return eFontPrefLang_Malayalam;
2284 case UBLOCK_SINHALA:
2285 case UBLOCK_SINHALA_ARCHAIC_NUMBERS:
2286 return eFontPrefLang_Sinhala;
2287 case UBLOCK_THAI:
2288 return eFontPrefLang_Thai;
2289 case UBLOCK_TIBETAN:
2290 return eFontPrefLang_Tibetan;
2291 case UBLOCK_GEORGIAN:
2292 case UBLOCK_GEORGIAN_SUPPLEMENT:
2293 case UBLOCK_GEORGIAN_EXTENDED:
2294 return eFontPrefLang_Georgian;
2295 case UBLOCK_HANGUL_JAMO:
2296 case UBLOCK_HANGUL_COMPATIBILITY_JAMO:
2297 case UBLOCK_HANGUL_SYLLABLES:
2298 case UBLOCK_HANGUL_JAMO_EXTENDED_A:
2299 case UBLOCK_HANGUL_JAMO_EXTENDED_B:
2300 return eFontPrefLang_Korean;
2301 case UBLOCK_ETHIOPIC:
2302 case UBLOCK_ETHIOPIC_EXTENDED:
2303 case UBLOCK_ETHIOPIC_SUPPLEMENT:
2304 case UBLOCK_ETHIOPIC_EXTENDED_A:
2305 return eFontPrefLang_Ethiopic;
2306 case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS:
2307 case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED:
2308 return eFontPrefLang_Canadian;
2309 case UBLOCK_KHMER:
2310 case UBLOCK_KHMER_SYMBOLS:
2311 return eFontPrefLang_Khmer;
2312 case UBLOCK_CJK_RADICALS_SUPPLEMENT:
2313 case UBLOCK_KANGXI_RADICALS:
2314 case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS:
2315 case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
2316 case UBLOCK_HIRAGANA:
2317 case UBLOCK_KATAKANA:
2318 case UBLOCK_BOPOMOFO:
2319 case UBLOCK_KANBUN:
2320 case UBLOCK_BOPOMOFO_EXTENDED:
2321 case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS:
2322 case UBLOCK_CJK_COMPATIBILITY:
2323 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:
2324 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS:
2325 case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS:
2326 case UBLOCK_CJK_COMPATIBILITY_FORMS:
2327 case UBLOCK_SMALL_FORM_VARIANTS:
2328 case UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS:
2329 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:
2330 case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:
2331 case UBLOCK_KATAKANA_PHONETIC_EXTENSIONS:
2332 case UBLOCK_CJK_STROKES:
2333 case UBLOCK_VERTICAL_FORMS:
2334 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C:
2335 case UBLOCK_KANA_SUPPLEMENT:
2336 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D:
2337 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E:
2338 case UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION:
2339 case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F:
2340 case UBLOCK_KANA_EXTENDED_A:
2341 return eFontPrefLang_CJKSet;
2342 case UBLOCK_MATHEMATICAL_OPERATORS:
2343 case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS:
2344 case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A:
2345 case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B:
2346 case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS:
2347 return eFontPrefLang_Mathematics;
2348 default:
2349 return eFontPrefLang_Others;
2353 bool gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang) {
2354 switch (aLang) {
2355 case eFontPrefLang_Japanese:
2356 case eFontPrefLang_ChineseTW:
2357 case eFontPrefLang_ChineseCN:
2358 case eFontPrefLang_ChineseHK:
2359 case eFontPrefLang_Korean:
2360 case eFontPrefLang_CJKSet:
2361 return true;
2362 default:
2363 return false;
2367 void gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[],
2368 uint32_t& aLen, eFontPrefLang aCharLang,
2369 eFontPrefLang aPageLang) {
2370 AutoLock lock(mLock);
2371 if (IsLangCJK(aCharLang)) {
2372 AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
2373 } else {
2374 AppendPrefLang(aPrefLangs, aLen, aCharLang);
2377 AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
2380 void gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[],
2381 uint32_t& aLen,
2382 eFontPrefLang aCharLang,
2383 eFontPrefLang aPageLang) {
2384 // prefer the lang specified by the page *if* CJK
2385 if (IsLangCJK(aPageLang)) {
2386 AppendPrefLang(aPrefLangs, aLen, aPageLang);
2389 // if not set up, set up the default CJK order, based on accept lang
2390 // settings and locale
2391 if (mCJKPrefLangs.Length() == 0) {
2392 // temp array
2393 eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
2394 uint32_t tempLen = 0;
2396 // Add the CJK pref fonts from accept languages, the order should be same
2397 // order. We use gfxFontUtils::GetPrefsFontList to read the list even
2398 // though it's not actually a list of fonts but of lang codes; the format
2399 // is the same.
2400 AutoTArray<nsCString, 5> list;
2401 gfxFontUtils::GetPrefsFontList("intl.accept_languages", list, true);
2402 for (const auto& lang : list) {
2403 eFontPrefLang fpl = GetFontPrefLangFor(lang.get());
2404 switch (fpl) {
2405 case eFontPrefLang_Japanese:
2406 case eFontPrefLang_Korean:
2407 case eFontPrefLang_ChineseCN:
2408 case eFontPrefLang_ChineseHK:
2409 case eFontPrefLang_ChineseTW:
2410 AppendPrefLang(tempPrefLangs, tempLen, fpl);
2411 break;
2412 default:
2413 break;
2417 // Try using app's locale
2418 nsAutoCString localeStr;
2419 LocaleService::GetInstance()->GetAppLocaleAsBCP47(localeStr);
2422 Locale locale;
2423 if (LocaleParser::TryParse(localeStr, locale).isOk() &&
2424 locale.Canonicalize().isOk()) {
2425 if (locale.Language().EqualTo("ja")) {
2426 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2427 } else if (locale.Language().EqualTo("zh")) {
2428 if (locale.Region().EqualTo("CN")) {
2429 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2430 } else if (locale.Region().EqualTo("TW")) {
2431 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2432 } else if (locale.Region().EqualTo("HK")) {
2433 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2435 } else if (locale.Language().EqualTo("ko")) {
2436 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2441 // Then add the known CJK prefs in order of system preferred locales
2442 AutoTArray<nsCString, 5> prefLocales;
2443 prefLocales.AppendElement("ja"_ns);
2444 prefLocales.AppendElement("zh-CN"_ns);
2445 prefLocales.AppendElement("zh-TW"_ns);
2446 prefLocales.AppendElement("zh-HK"_ns);
2447 prefLocales.AppendElement("ko"_ns);
2449 AutoTArray<nsCString, 16> sysLocales;
2450 AutoTArray<nsCString, 16> negLocales;
2451 if (NS_SUCCEEDED(
2452 OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) {
2453 LocaleService::GetInstance()->NegotiateLanguages(
2454 sysLocales, prefLocales, ""_ns,
2455 LocaleService::kLangNegStrategyFiltering, negLocales);
2456 for (const auto& localeStr : negLocales) {
2457 Locale locale;
2458 if (LocaleParser::TryParse(localeStr, locale).isOk() &&
2459 locale.Canonicalize().isOk()) {
2460 if (locale.Language().EqualTo("ja")) {
2461 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2462 } else if (locale.Language().EqualTo("zh")) {
2463 if (locale.Region().EqualTo("CN")) {
2464 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2465 } else if (locale.Region().EqualTo("TW")) {
2466 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2467 } else if (locale.Region().EqualTo("HK")) {
2468 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2470 } else if (locale.Language().EqualTo("ko")) {
2471 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2477 // Last resort... set up CJK font prefs in the order listed by the user-
2478 // configurable ordering pref.
2479 gfxFontUtils::GetPrefsFontList(kCJKFallbackOrderPref, list);
2480 for (const auto& item : list) {
2481 eFontPrefLang fpl = GetFontPrefLangFor(item.get());
2482 switch (fpl) {
2483 case eFontPrefLang_Japanese:
2484 case eFontPrefLang_Korean:
2485 case eFontPrefLang_ChineseCN:
2486 case eFontPrefLang_ChineseHK:
2487 case eFontPrefLang_ChineseTW:
2488 AppendPrefLang(tempPrefLangs, tempLen, fpl);
2489 break;
2490 default:
2491 break;
2495 // Truly-last resort... try Chinese font prefs before Japanese because
2496 // they tend to have more complete character coverage, and therefore less
2497 // risk of "ransom-note" effects.
2498 // (If the kCJKFallbackOrderPref was fully populated, as it is by default,
2499 // this will do nothing as all these values are already present.)
2500 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
2501 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
2502 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
2503 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
2504 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
2506 // copy into the cached array
2507 for (const auto lang : Span<eFontPrefLang>(tempPrefLangs, tempLen)) {
2508 mCJKPrefLangs.AppendElement(lang);
2512 // append in cached CJK langs
2513 for (const auto lang : mCJKPrefLangs) {
2514 AppendPrefLang(aPrefLangs, aLen, eFontPrefLang(lang));
2518 void gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[],
2519 uint32_t& aLen,
2520 eFontPrefLang aAddLang) {
2521 if (aLen >= kMaxLenPrefLangList) {
2522 return;
2525 // If the lang is already present, just ignore the addition.
2526 for (const auto lang : Span<eFontPrefLang>(aPrefLangs, aLen)) {
2527 if (lang == aAddLang) {
2528 return;
2532 aPrefLangs[aLen++] = aAddLang;
2535 StyleGenericFontFamily gfxPlatformFontList::GetDefaultGeneric(
2536 eFontPrefLang aLang) {
2537 if (aLang == eFontPrefLang_Emoji) {
2538 return StyleGenericFontFamily::MozEmoji;
2541 AutoLock lock(mLock);
2543 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
2544 return mDefaultGenericsLangGroup[uint32_t(aLang)];
2546 return StyleGenericFontFamily::Serif;
2549 FontFamily gfxPlatformFontList::GetDefaultFont(nsPresContext* aPresContext,
2550 const gfxFontStyle* aStyle) {
2551 AutoLock lock(mLock);
2552 return GetDefaultFontLocked(aPresContext, aStyle);
2555 FontFamily gfxPlatformFontList::GetDefaultFontLocked(
2556 nsPresContext* aPresContext, const gfxFontStyle* aStyle) {
2557 FontFamily family = GetDefaultFontForPlatform(aPresContext, aStyle);
2558 if (!family.IsNull()) {
2559 return family;
2561 // Something has gone wrong and we were unable to retrieve a default font
2562 // from the platform. (Likely the whitelist has blocked all potential
2563 // default fonts.) As a last resort, we return the first font in our list.
2564 if (SharedFontList()) {
2565 MOZ_RELEASE_ASSERT(SharedFontList()->NumFamilies() > 0);
2566 return FontFamily(SharedFontList()->Families());
2568 MOZ_RELEASE_ASSERT(mFontFamilies.Count() > 0);
2569 return FontFamily(mFontFamilies.ConstIter().Data());
2572 void gfxPlatformFontList::GetFontFamilyNames(
2573 nsTArray<nsCString>& aFontFamilyNames) {
2574 if (SharedFontList()) {
2575 fontlist::FontList* list = SharedFontList();
2576 const fontlist::Family* families = list->Families();
2577 if (families) {
2578 for (uint32_t i = 0, n = list->NumFamilies(); i < n; i++) {
2579 const fontlist::Family& family = families[i];
2580 if (!family.IsHidden()) {
2581 aFontFamilyNames.AppendElement(family.DisplayName().AsString(list));
2585 } else {
2586 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2587 if (!family->IsHidden()) {
2588 aFontFamilyNames.AppendElement(family->Name());
2594 nsAtom* gfxPlatformFontList::GetLangGroup(nsAtom* aLanguage) {
2595 // map lang ==> langGroup
2596 nsAtom* langGroup = nullptr;
2597 if (aLanguage) {
2598 langGroup = mLangService->GetLanguageGroup(aLanguage);
2600 if (!langGroup) {
2601 langGroup = nsGkAtoms::Unicode;
2603 return langGroup;
2606 /* static */ const char* gfxPlatformFontList::GetGenericName(
2607 StyleGenericFontFamily aGenericType) {
2608 // type should be standard generic type at this point
2609 // map generic type to string
2610 switch (aGenericType) {
2611 case StyleGenericFontFamily::Serif:
2612 return "serif";
2613 case StyleGenericFontFamily::SansSerif:
2614 return "sans-serif";
2615 case StyleGenericFontFamily::Monospace:
2616 return "monospace";
2617 case StyleGenericFontFamily::Cursive:
2618 return "cursive";
2619 case StyleGenericFontFamily::Fantasy:
2620 return "fantasy";
2621 case StyleGenericFontFamily::SystemUi:
2622 return "system-ui";
2623 case StyleGenericFontFamily::MozEmoji:
2624 return "-moz-emoji";
2625 case StyleGenericFontFamily::None:
2626 break;
2628 MOZ_ASSERT_UNREACHABLE("Unknown generic");
2629 return nullptr;
2632 void gfxPlatformFontList::InitLoader() {
2633 GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
2634 mStartIndex = 0;
2635 mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
2636 memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
2639 #define FONT_LOADER_MAX_TIMESLICE \
2640 20 // max time for one pass through RunLoader = 20ms
2642 bool gfxPlatformFontList::LoadFontInfo() {
2643 AutoLock lock(mLock);
2644 TimeStamp start = TimeStamp::Now();
2645 uint32_t i, endIndex = mNumFamilies;
2646 fontlist::FontList* list = SharedFontList();
2647 bool loadCmaps =
2648 !list && (!UsesSystemFallback() ||
2649 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback());
2651 // for each font family, load in various font info
2652 for (i = mStartIndex; i < endIndex; i++) {
2653 nsAutoCString key;
2654 GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
2656 if (list) {
2657 fontlist::Family* family = list->FindFamily(key);
2658 if (!family) {
2659 continue;
2661 ReadFaceNamesForFamily(family, NeedFullnamePostscriptNames());
2662 } else {
2663 // lookup in canonical (i.e. English) family name list
2664 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
2665 if (!familyEntry) {
2666 continue;
2669 // read in face names
2670 familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(),
2671 mFontInfo);
2673 // load the cmaps if needed
2674 if (loadCmaps) {
2675 familyEntry->ReadAllCMAPs(mFontInfo);
2679 // Limit the time spent reading fonts in one pass, unless the font-loader
2680 // delay was set to zero, in which case we run to completion even if it
2681 // causes some jank.
2682 if (StaticPrefs::gfx_font_loader_delay() > 0) {
2683 TimeDuration elapsed = TimeStamp::Now() - start;
2684 if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
2685 i + 1 != endIndex) {
2686 endIndex = i + 1;
2687 break;
2692 mStartIndex = endIndex;
2693 bool done = mStartIndex >= mNumFamilies;
2695 if (LOG_FONTINIT_ENABLED()) {
2696 TimeDuration elapsed = TimeStamp::Now() - start;
2697 LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
2698 elapsed.ToMilliseconds(), (done ? "true" : "false")));
2701 if (done) {
2702 mOtherFamilyNamesInitialized = true;
2703 CancelInitOtherFamilyNamesTask();
2704 mFaceNameListsInitialized = true;
2707 return done;
2710 void gfxPlatformFontList::CleanupLoader() {
2711 AutoLock lock(mLock);
2713 mFontFamiliesToLoad.Clear();
2714 mNumFamilies = 0;
2715 bool rebuilt = false, forceReflow = false;
2717 // if had missed face names that are now available, force reflow all
2718 if (mFaceNamesMissed) {
2719 rebuilt = std::any_of(mFaceNamesMissed->cbegin(), mFaceNamesMissed->cend(),
2720 [&](const auto& key) {
2721 mLock.AssertCurrentThreadIn();
2722 return FindFaceName(key);
2724 if (rebuilt) {
2725 RebuildLocalFonts();
2728 mFaceNamesMissed = nullptr;
2731 if (mOtherNamesMissed) {
2732 forceReflow = std::any_of(
2733 mOtherNamesMissed->cbegin(), mOtherNamesMissed->cend(),
2734 [&](const auto& key) {
2735 mLock.AssertCurrentThreadIn();
2736 return FindUnsharedFamily(
2737 nullptr, key,
2738 (FindFamiliesFlags::eForceOtherFamilyNamesLoading |
2739 FindFamiliesFlags::eNoAddToNamesMissedWhenSearching));
2741 if (forceReflow) {
2742 ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::No);
2745 mOtherNamesMissed = nullptr;
2748 if (LOG_FONTINIT_ENABLED() && mFontInfo) {
2749 LOG_FONTINIT(
2750 ("(fontinit) fontloader load thread took %8.2f ms "
2751 "%d families %d fonts %d cmaps "
2752 "%d facenames %d othernames %s %s",
2753 mLoadTime.ToMilliseconds(), mFontInfo->mLoadStats.families,
2754 mFontInfo->mLoadStats.fonts, mFontInfo->mLoadStats.cmaps,
2755 mFontInfo->mLoadStats.facenames, mFontInfo->mLoadStats.othernames,
2756 (rebuilt ? "(userfont sets rebuilt)" : ""),
2757 (forceReflow ? "(global reflow)" : "")));
2760 gfxFontInfoLoader::CleanupLoader();
2763 void gfxPlatformFontList::ForceGlobalReflowLocked(
2764 gfxPlatform::NeedsReframe aNeedsReframe,
2765 gfxPlatform::BroadcastToChildren aBroadcastToChildren) {
2766 if (!NS_IsMainThread()) {
2767 NS_DispatchToMainThread(NS_NewRunnableFunction(
2768 "gfxPlatformFontList::ForceGlobalReflowLocked",
2769 [aNeedsReframe, aBroadcastToChildren] {
2770 gfxPlatform::ForceGlobalReflow(aNeedsReframe, aBroadcastToChildren);
2771 }));
2772 return;
2775 AutoUnlock unlock(mLock);
2776 gfxPlatform::ForceGlobalReflow(aNeedsReframe, aBroadcastToChildren);
2779 void gfxPlatformFontList::GetPrefsAndStartLoader() {
2780 // If we're already in shutdown, there's no point in starting this, and it
2781 // could trigger an assertion if we try to use the Thread Manager too late.
2782 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
2783 return;
2785 uint32_t delay = std::max(1u, StaticPrefs::gfx_font_loader_delay());
2786 if (NS_IsMainThread()) {
2787 StartLoader(delay);
2788 } else {
2789 NS_DispatchToMainThread(NS_NewRunnableFunction(
2790 "StartLoader callback", [delay, fontList = this] {
2791 fontList->Lock();
2792 fontList->StartLoader(delay);
2793 fontList->Unlock();
2794 }));
2798 void gfxPlatformFontList::RebuildLocalFonts(bool aForgetLocalFaces) {
2799 for (auto* fontset : mUserFontSetList) {
2800 if (aForgetLocalFaces) {
2801 fontset->ForgetLocalFaces();
2803 fontset->RebuildLocalRules();
2807 void gfxPlatformFontList::ClearLangGroupPrefFontsLocked() {
2808 for (uint32_t i = eFontPrefLang_First;
2809 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
2810 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
2811 for (auto& pref : prefFontsLangGroup) {
2812 pref = nullptr;
2815 mCJKPrefLangs.Clear();
2816 mEmojiPrefFont = nullptr;
2818 // Create a new FontPrefs and replace the existing one.
2819 mFontPrefs = MakeUnique<FontPrefs>();
2822 // Support for memory reporting
2824 // this is also used by subclasses that hold additional font tables
2825 /*static*/
2826 size_t gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis(
2827 const FontFamilyTable& aTable, MallocSizeOf aMallocSizeOf) {
2828 return std::accumulate(
2829 aTable.Keys().cbegin(), aTable.Keys().cend(),
2830 aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
2831 [&](size_t oldValue, const nsACString& key) {
2832 // We don't count the size of the family here, because this is an
2833 // *extra* reference to a family that will have already been counted in
2834 // the main list.
2835 return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2839 /*static*/
2840 size_t gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
2841 const FontEntryTable& aTable, MallocSizeOf aMallocSizeOf) {
2842 return std::accumulate(
2843 aTable.Keys().cbegin(), aTable.Keys().cend(),
2844 aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
2845 [&](size_t oldValue, const nsACString& key) {
2846 // The font itself is counted by its owning family; here we only care
2847 // about the names stored in the hashtable keys.
2849 return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2853 void gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2854 FontListSizes* aSizes) const {
2855 AutoLock lock(mLock);
2857 aSizes->mFontListSize +=
2858 mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
2859 for (const auto& entry : mFontFamilies) {
2860 aSizes->mFontListSize +=
2861 entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2862 entry.GetData()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2865 aSizes->mFontListSize +=
2866 SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
2868 if (mExtraNames) {
2869 aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
2870 mExtraNames->mFullnames, aMallocSizeOf);
2871 aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
2872 mExtraNames->mPostscriptNames, aMallocSizeOf);
2875 for (uint32_t i = eFontPrefLang_First;
2876 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
2877 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
2878 for (const UniquePtr<PrefFontList>& pf : prefFontsLangGroup) {
2879 if (pf) {
2880 aSizes->mFontListSize += pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
2885 for (const auto& bitset : mCodepointsWithNoFonts) {
2886 aSizes->mFontListSize += bitset.SizeOfExcludingThis(aMallocSizeOf);
2888 aSizes->mFontListSize +=
2889 mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
2891 aSizes->mFontListSize +=
2892 mBadUnderlineFamilyNames.ShallowSizeOfExcludingThis(aMallocSizeOf);
2893 for (const auto& i : mBadUnderlineFamilyNames) {
2894 aSizes->mFontListSize += i.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2897 aSizes->mFontListSize +=
2898 mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
2899 for (const auto& entry : mSharedCmaps) {
2900 aSizes->mCharMapsSize += entry.GetKey()->SizeOfIncludingThis(aMallocSizeOf);
2903 aSizes->mFontListSize +=
2904 mFontEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
2905 for (const auto& entry : mFontEntries.Values()) {
2906 if (entry) {
2907 entry->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2911 if (SharedFontList()) {
2912 aSizes->mFontListSize +=
2913 SharedFontList()->SizeOfIncludingThis(aMallocSizeOf);
2914 if (XRE_IsParentProcess()) {
2915 aSizes->mSharedSize += SharedFontList()->AllocatedShmemSize();
2920 void gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2921 FontListSizes* aSizes) const {
2922 aSizes->mFontListSize += aMallocSizeOf(this);
2923 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2926 void gfxPlatformFontList::InitOtherFamilyNamesInternal(
2927 bool aDeferOtherFamilyNamesLoading) {
2928 if (mOtherFamilyNamesInitialized) {
2929 return;
2932 AutoLock lock(mLock);
2934 if (aDeferOtherFamilyNamesLoading) {
2935 TimeStamp start = TimeStamp::Now();
2936 bool timedOut = false;
2938 auto list = SharedFontList();
2939 if (list) {
2940 // If the gfxFontInfoLoader task is not yet running, kick it off now so
2941 // that it will load remaining names etc as soon as idle time permits.
2942 if (mState == stateInitial || mState == stateTimerOnDelay) {
2943 StartLoader(0);
2944 timedOut = true;
2946 } else {
2947 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2948 family->ReadOtherFamilyNames(this);
2949 TimeDuration elapsed = TimeStamp::Now() - start;
2950 if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
2951 timedOut = true;
2952 break;
2957 if (!timedOut) {
2958 mOtherFamilyNamesInitialized = true;
2959 CancelInitOtherFamilyNamesTask();
2961 TimeStamp end = TimeStamp::Now();
2962 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
2963 start, end);
2965 if (LOG_FONTINIT_ENABLED()) {
2966 TimeDuration elapsed = end - start;
2967 LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
2968 elapsed.ToMilliseconds(), (timedOut ? "timeout" : "")));
2970 } else {
2971 TimeStamp start = TimeStamp::Now();
2973 auto list = SharedFontList();
2974 if (list) {
2975 for (auto& f : mozilla::Range<fontlist::Family>(list->Families(),
2976 list->NumFamilies())) {
2977 ReadFaceNamesForFamily(&f, false);
2979 } else {
2980 for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
2981 family->ReadOtherFamilyNames(this);
2985 mOtherFamilyNamesInitialized = true;
2986 CancelInitOtherFamilyNamesTask();
2988 TimeStamp end = TimeStamp::Now();
2989 Telemetry::AccumulateTimeDelta(
2990 Telemetry::FONTLIST_INITOTHERFAMILYNAMES_NO_DEFERRING, start, end);
2992 if (LOG_FONTINIT_ENABLED()) {
2993 TimeDuration elapsed = end - start;
2994 LOG_FONTINIT(
2995 ("(fontinit) InitOtherFamilyNames without deferring took %8.2f ms",
2996 elapsed.ToMilliseconds()));
3001 void gfxPlatformFontList::CancelInitOtherFamilyNamesTask() {
3002 if (mPendingOtherFamilyNameTask) {
3003 mPendingOtherFamilyNameTask->Cancel();
3004 mPendingOtherFamilyNameTask = nullptr;
3006 auto list = SharedFontList();
3007 if (list && XRE_IsParentProcess()) {
3008 bool forceReflow = false;
3009 if (!mAliasTable.IsEmpty()) {
3010 list->SetAliases(mAliasTable);
3011 mAliasTable.Clear();
3012 forceReflow = true;
3014 if (mLocalNameTable.Count()) {
3015 list->SetLocalNames(mLocalNameTable);
3016 mLocalNameTable.Clear();
3017 forceReflow = true;
3019 if (forceReflow) {
3020 dom::ContentParent::BroadcastFontListChanged();
3025 void gfxPlatformFontList::ShareFontListShmBlockToProcess(
3026 uint32_t aGeneration, uint32_t aIndex, base::ProcessId aPid,
3027 base::SharedMemoryHandle* aOut) {
3028 auto list = SharedFontList();
3029 if (!list) {
3030 return;
3032 if (!aGeneration || list->GetGeneration() == aGeneration) {
3033 list->ShareShmBlockToProcess(aIndex, aPid, aOut);
3034 } else {
3035 *aOut = base::SharedMemory::NULLHandle();
3039 void gfxPlatformFontList::ShareFontListToProcess(
3040 nsTArray<base::SharedMemoryHandle>* aBlocks, base::ProcessId aPid) {
3041 auto list = SharedFontList();
3042 if (list) {
3043 list->ShareBlocksToProcess(aBlocks, aPid);
3047 base::SharedMemoryHandle gfxPlatformFontList::ShareShmBlockToProcess(
3048 uint32_t aIndex, base::ProcessId aPid) {
3049 MOZ_RELEASE_ASSERT(SharedFontList());
3050 return SharedFontList()->ShareBlockToProcess(aIndex, aPid);
3053 void gfxPlatformFontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
3054 base::SharedMemoryHandle aHandle) {
3055 if (SharedFontList()) {
3056 AutoLock lock(mLock);
3057 SharedFontList()->ShmBlockAdded(aGeneration, aIndex, std::move(aHandle));
3061 void gfxPlatformFontList::InitializeFamily(uint32_t aGeneration,
3062 uint32_t aFamilyIndex,
3063 bool aLoadCmaps) {
3064 auto list = SharedFontList();
3065 MOZ_ASSERT(list);
3066 if (!list) {
3067 return;
3069 if (list->GetGeneration() != aGeneration) {
3070 return;
3072 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3073 return;
3075 if (aFamilyIndex >= list->NumFamilies()) {
3076 return;
3078 fontlist::Family* family = list->Families() + aFamilyIndex;
3079 if (!family->IsInitialized() || aLoadCmaps) {
3080 Unused << InitializeFamily(family, aLoadCmaps);
3084 void gfxPlatformFontList::SetCharacterMap(uint32_t aGeneration,
3085 uint32_t aFamilyIndex, bool aAlias,
3086 uint32_t aFaceIndex,
3087 const gfxSparseBitSet& aMap) {
3088 MOZ_ASSERT(XRE_IsParentProcess());
3089 auto list = SharedFontList();
3090 MOZ_ASSERT(list);
3091 if (!list) {
3092 return;
3094 if (list->GetGeneration() != aGeneration) {
3095 return;
3097 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3098 return;
3101 const fontlist::Family* family;
3102 if (aAlias) {
3103 if (aFamilyIndex >= list->NumAliases()) {
3104 MOZ_ASSERT(false, "AliasFamily index out of range");
3105 return;
3107 family = list->AliasFamilies() + aFamilyIndex;
3108 } else {
3109 if (aFamilyIndex >= list->NumFamilies()) {
3110 MOZ_ASSERT(false, "Family index out of range");
3111 return;
3113 family = list->Families() + aFamilyIndex;
3116 if (aFaceIndex >= family->NumFaces()) {
3117 MOZ_ASSERT(false, "Face index out of range");
3118 return;
3121 if (auto* face =
3122 family->Faces(list)[aFaceIndex].ToPtr<fontlist::Face>(list)) {
3123 face->mCharacterMap = GetShmemCharMap(&aMap);
3127 void gfxPlatformFontList::SetupFamilyCharMap(uint32_t aGeneration,
3128 uint32_t aIndex, bool aAlias) {
3129 MOZ_ASSERT(XRE_IsParentProcess());
3130 auto list = SharedFontList();
3131 MOZ_ASSERT(list);
3132 if (!list) {
3133 return;
3135 if (list->GetGeneration() != aGeneration) {
3136 return;
3138 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3139 return;
3142 if (aAlias) {
3143 if (aIndex >= list->NumAliases()) {
3144 MOZ_ASSERT(false, "AliasFamily index out of range");
3145 return;
3147 list->AliasFamilies()[aIndex].SetupFamilyCharMap(list);
3148 return;
3151 if (aIndex >= list->NumFamilies()) {
3152 MOZ_ASSERT(false, "Family index out of range");
3153 return;
3155 list->Families()[aIndex].SetupFamilyCharMap(list);
3158 bool gfxPlatformFontList::InitOtherFamilyNames(uint32_t aGeneration,
3159 bool aDefer) {
3160 auto list = SharedFontList();
3161 MOZ_ASSERT(list);
3162 if (!list) {
3163 return false;
3165 if (list->GetGeneration() != aGeneration) {
3166 return false;
3168 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
3169 return false;
3171 return InitOtherFamilyNames(aDefer);
3174 uint32_t gfxPlatformFontList::GetGeneration() const {
3175 return SharedFontList() ? SharedFontList()->GetGeneration() : 0;
3178 gfxPlatformFontList::FontPrefs::FontPrefs() {
3179 // This must be created on the main thread, so that we can safely use the
3180 // Preferences service. Once created, it can be read from any thread.
3181 MOZ_ASSERT(NS_IsMainThread());
3182 Init();
3185 void gfxPlatformFontList::FontPrefs::Init() {
3186 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
3187 return;
3189 nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch();
3190 if (!prefRootBranch) {
3191 return;
3193 nsTArray<nsCString> prefNames;
3194 if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNamePrefix, prefNames))) {
3195 for (auto& prefName : prefNames) {
3196 nsAutoCString value;
3197 if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
3198 nsAutoCString pref(Substring(prefName, sizeof(kNamePrefix) - 1));
3199 mFontName.InsertOrUpdate(pref, value);
3203 if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNameListPrefix, prefNames))) {
3204 for (auto& prefName : prefNames) {
3205 nsAutoCString value;
3206 if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
3207 nsAutoCString pref(Substring(prefName, sizeof(kNameListPrefix) - 1));
3208 mFontNameList.InsertOrUpdate(pref, value);
3212 mEmojiHasUserValue = Preferences::HasUserValue("font.name-list.emoji");
3215 bool gfxPlatformFontList::FontPrefs::LookupName(const nsACString& aPref,
3216 nsACString& aValue) const {
3217 if (const auto& value = mFontName.Lookup(aPref)) {
3218 aValue = *value;
3219 return true;
3221 return false;
3224 bool gfxPlatformFontList::FontPrefs::LookupNameList(const nsACString& aPref,
3225 nsACString& aValue) const {
3226 if (const auto& value = mFontNameList.Lookup(aPref)) {
3227 aValue = *value;
3228 return true;
3230 return false;
3233 bool gfxPlatformFontList::IsKnownIconFontFamily(
3234 const nsAtom* aFamilyName) const {
3235 nsAtomCString fam(aFamilyName);
3236 ToLowerCase(fam);
3237 return mIconFontsSet.Contains(fam);
3240 #undef LOG
3241 #undef LOG_ENABLED