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