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