Bug 1492908 [wpt PR 13122] - Update wpt metadata, a=testonly
[gecko.git] / gfx / thebes / gfxPlatformFontList.cpp
blobb91fb83bd9d39303197880475c3fdf2a3ee2a2d4
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
7 #include "mozilla/intl/LocaleService.h"
8 #include "mozilla/intl/MozLocale.h"
9 #include "mozilla/intl/OSPreferences.h"
11 #include "gfxPlatformFontList.h"
12 #include "gfxTextRun.h"
13 #include "gfxUserFontSet.h"
15 #include "nsCRT.h"
16 #include "nsGkAtoms.h"
17 #include "nsServiceManagerUtils.h"
18 #include "nsUnicharUtils.h"
19 #include "nsUnicodeRange.h"
20 #include "nsUnicodeProperties.h"
21 #include "nsXULAppAPI.h"
23 #include "mozilla/Attributes.h"
24 #include "mozilla/Likely.h"
25 #include "mozilla/MemoryReporting.h"
26 #include "mozilla/Mutex.h"
27 #include "mozilla/Preferences.h"
28 #include "mozilla/Telemetry.h"
29 #include "mozilla/TimeStamp.h"
30 #include "mozilla/dom/ContentParent.h"
31 #include "mozilla/gfx/2D.h"
33 #include <locale.h>
35 using namespace mozilla;
36 using mozilla::intl::LocaleService;
37 using mozilla::intl::Locale;
38 using mozilla::intl::OSPreferences;
40 #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
41 LogLevel::Debug, args)
42 #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
43 gfxPlatform::GetLog(eGfxLog_fontlist), \
44 LogLevel::Debug)
45 #define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
46 LogLevel::Debug, args)
47 #define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
48 gfxPlatform::GetLog(eGfxLog_fontinit), \
49 LogLevel::Debug)
51 gfxPlatformFontList *gfxPlatformFontList::sPlatformFontList = nullptr;
53 // Character ranges that require complex-script shaping support in the font,
54 // and so should be masked out by ReadCMAP if the necessary layout tables
55 // are not present.
56 // Currently used by the Mac and FT2 implementations only, but probably should
57 // be supported on Windows as well.
58 const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = {
59 // Actually, now that harfbuzz supports presentation-forms shaping for
60 // Arabic, we can render it without layout tables. So maybe we don't
61 // want to mask the basic Arabic block here?
62 // This affects the arabic-fallback-*.html reftests, which rely on
63 // loading a font that *doesn't* have any GSUB table.
64 { 0x0600, 0x06FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
65 { 0x0700, 0x074F, { TRUETYPE_TAG('s','y','r','c'), 0, 0 } },
66 { 0x0750, 0x077F, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
67 { 0x08A0, 0x08FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
68 { 0x0900, 0x097F, { TRUETYPE_TAG('d','e','v','2'),
69 TRUETYPE_TAG('d','e','v','a'), 0 } },
70 { 0x0980, 0x09FF, { TRUETYPE_TAG('b','n','g','2'),
71 TRUETYPE_TAG('b','e','n','g'), 0 } },
72 { 0x0A00, 0x0A7F, { TRUETYPE_TAG('g','u','r','2'),
73 TRUETYPE_TAG('g','u','r','u'), 0 } },
74 { 0x0A80, 0x0AFF, { TRUETYPE_TAG('g','j','r','2'),
75 TRUETYPE_TAG('g','u','j','r'), 0 } },
76 { 0x0B00, 0x0B7F, { TRUETYPE_TAG('o','r','y','2'),
77 TRUETYPE_TAG('o','r','y','a'), 0 } },
78 { 0x0B80, 0x0BFF, { TRUETYPE_TAG('t','m','l','2'),
79 TRUETYPE_TAG('t','a','m','l'), 0 } },
80 { 0x0C00, 0x0C7F, { TRUETYPE_TAG('t','e','l','2'),
81 TRUETYPE_TAG('t','e','l','u'), 0 } },
82 { 0x0C80, 0x0CFF, { TRUETYPE_TAG('k','n','d','2'),
83 TRUETYPE_TAG('k','n','d','a'), 0 } },
84 { 0x0D00, 0x0D7F, { TRUETYPE_TAG('m','l','m','2'),
85 TRUETYPE_TAG('m','l','y','m'), 0 } },
86 { 0x0D80, 0x0DFF, { TRUETYPE_TAG('s','i','n','h'), 0, 0 } },
87 { 0x0E80, 0x0EFF, { TRUETYPE_TAG('l','a','o',' '), 0, 0 } },
88 { 0x0F00, 0x0FFF, { TRUETYPE_TAG('t','i','b','t'), 0, 0 } },
89 { 0x1000, 0x109f, { TRUETYPE_TAG('m','y','m','r'),
90 TRUETYPE_TAG('m','y','m','2'), 0 } },
91 { 0x1780, 0x17ff, { TRUETYPE_TAG('k','h','m','r'), 0, 0 } },
92 // Khmer Symbols (19e0..19ff) don't seem to need any special shaping
93 { 0xaa60, 0xaa7f, { TRUETYPE_TAG('m','y','m','r'),
94 TRUETYPE_TAG('m','y','m','2'), 0 } },
95 // Thai seems to be "renderable" without AAT morphing tables
96 { 0, 0, { 0, 0, 0 } } // terminator
99 // prefs for the font info loader
100 #define FONT_LOADER_DELAY_PREF "gfx.font_loader.delay"
101 #define FONT_LOADER_INTERVAL_PREF "gfx.font_loader.interval"
103 static const char* kObservedPrefs[] = {
104 "font.",
105 "font.name-list.",
106 "intl.accept_languages", // hmmmm...
107 nullptr
110 static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
112 // xxx - this can probably be eliminated by reworking pref font handling code
113 static const char *gPrefLangNames[] = {
114 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
115 #include "gfxFontPrefLangList.h"
116 #undef FONT_PREF_LANG
119 static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
120 "size of pref lang name array doesn't match pref lang enum size");
122 class gfxFontListPrefObserver final : public nsIObserver {
123 ~gfxFontListPrefObserver() {}
124 public:
125 NS_DECL_ISUPPORTS
126 NS_DECL_NSIOBSERVER
129 static void
130 FontListPrefChanged(const char* aPref, void* aData = nullptr)
132 // XXX this could be made to only clear out the cache for the prefs that were changed
133 // but it probably isn't that big a deal.
134 gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
135 gfxFontCache::GetCache()->AgeAllGenerations();
138 static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
140 NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
142 #define LOCALES_CHANGED_TOPIC "intl:system-locales-changed"
144 NS_IMETHODIMP
145 gfxFontListPrefObserver::Observe(nsISupports *aSubject,
146 const char *aTopic,
147 const char16_t *aData)
149 NS_ASSERTION(!strcmp(aTopic, LOCALES_CHANGED_TOPIC), "invalid topic");
150 FontListPrefChanged(nullptr);
152 if (XRE_IsParentProcess()) {
153 gfxPlatform::ForceGlobalReflow();
155 return NS_OK;
158 MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
160 NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
162 NS_IMETHODIMP
163 gfxPlatformFontList::MemoryReporter::CollectReports(
164 nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
166 FontListSizes sizes;
167 sizes.mFontListSize = 0;
168 sizes.mFontTableCacheSize = 0;
169 sizes.mCharMapsSize = 0;
170 sizes.mLoaderSize = 0;
172 gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(&FontListMallocSizeOf,
173 &sizes);
175 MOZ_COLLECT_REPORT(
176 "explicit/gfx/font-list", KIND_HEAP, UNITS_BYTES,
177 sizes.mFontListSize,
178 "Memory used to manage the list of font families and faces.");
180 MOZ_COLLECT_REPORT(
181 "explicit/gfx/font-charmaps", KIND_HEAP, UNITS_BYTES,
182 sizes.mCharMapsSize,
183 "Memory used to record the character coverage of individual fonts.");
185 if (sizes.mFontTableCacheSize) {
186 MOZ_COLLECT_REPORT(
187 "explicit/gfx/font-tables", KIND_HEAP, UNITS_BYTES,
188 sizes.mFontTableCacheSize,
189 "Memory used for cached font metrics and layout tables.");
192 if (sizes.mLoaderSize) {
193 MOZ_COLLECT_REPORT(
194 "explicit/gfx/font-loader", KIND_HEAP, UNITS_BYTES,
195 sizes.mLoaderSize,
196 "Memory used for (platform-specific) font loader.");
199 return NS_OK;
202 gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
203 : mFontFamiliesMutex("gfxPlatformFontList::mFontFamiliesMutex"), mFontFamilies(64),
204 mOtherFamilyNames(16), mBadUnderlineFamilyNames(8), mSharedCmaps(8),
205 mStartIndex(0), mNumFamilies(0), mFontlistInitCount(0),
206 mFontFamilyWhitelistActive(false)
208 mOtherFamilyNamesInitialized = false;
210 if (aNeedFullnamePostscriptNames) {
211 mExtraNames = MakeUnique<ExtraNames>();
213 mFaceNameListsInitialized = false;
215 mLangService = nsLanguageAtomService::GetService();
217 LoadBadUnderlineList();
219 // pref changes notification setup
220 NS_ASSERTION(!gFontListPrefObserver,
221 "There has been font list pref observer already");
222 gFontListPrefObserver = new gfxFontListPrefObserver();
223 NS_ADDREF(gFontListPrefObserver);
225 Preferences::RegisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
227 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
228 if (obs) {
229 obs->AddObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC, false);
232 // Only the parent process listens for whitelist changes; it will then
233 // notify its children to rebuild their font lists.
234 if (XRE_IsParentProcess()) {
235 Preferences::RegisterCallback(FontWhitelistPrefChanged,
236 kFontSystemWhitelistPref);
239 RegisterStrongMemoryReporter(new MemoryReporter());
242 gfxPlatformFontList::~gfxPlatformFontList()
244 mSharedCmaps.Clear();
245 ClearLangGroupPrefFonts();
246 NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
248 Preferences::UnregisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
250 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
251 if (obs) {
252 obs->RemoveObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC);
255 if (XRE_IsParentProcess()) {
256 Preferences::UnregisterCallback(FontWhitelistPrefChanged,
257 kFontSystemWhitelistPref);
259 NS_RELEASE(gFontListPrefObserver);
262 /* static */
263 void
264 gfxPlatformFontList::FontWhitelistPrefChanged(const char *aPref,
265 void *aClosure)
267 MOZ_ASSERT(XRE_IsParentProcess());
268 gfxPlatformFontList::PlatformFontList()->UpdateFontList();
269 mozilla::dom::ContentParent::NotifyUpdatedFonts();
272 // number of CSS generic font families
273 const uint32_t kNumGenerics = 5;
275 void
276 gfxPlatformFontList::ApplyWhitelist()
278 nsTArray<nsCString> list;
279 gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, list);
280 uint32_t numFonts = list.Length();
281 mFontFamilyWhitelistActive = (numFonts > 0);
282 if (!mFontFamilyWhitelistActive) {
283 return;
285 nsTHashtable<nsCStringHashKey> familyNamesWhitelist;
286 for (uint32_t i = 0; i < numFonts; i++) {
287 nsAutoCString key;
288 ToLowerCase(list[i], key);
289 familyNamesWhitelist.PutEntry(key);
291 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
292 // Don't continue if we only have one font left.
293 if (mFontFamilies.Count() == 1) {
294 break;
296 nsAutoCString fontFamilyName(iter.Key());
297 ToLowerCase(fontFamilyName);
298 if (!familyNamesWhitelist.Contains(fontFamilyName)) {
299 iter.Remove();
304 bool
305 gfxPlatformFontList::AddWithLegacyFamilyName(const nsACString& aLegacyName,
306 gfxFontEntry* aFontEntry)
308 bool added = false;
309 nsAutoCString key;
310 ToLowerCase(aLegacyName, key);
311 gfxFontFamily* family = mOtherFamilyNames.GetWeak(key);
312 if (!family) {
313 family = CreateFontFamily(aLegacyName);
314 family->SetHasStyles(true); // we don't want the family to search for
315 // faces, we're adding them directly here
316 mOtherFamilyNames.Put(key, family);
317 added = true;
319 family->AddFontEntry(aFontEntry->Clone());
320 return added;
323 nsresult
324 gfxPlatformFontList::InitFontList()
326 mFontlistInitCount++;
328 if (LOG_FONTINIT_ENABLED()) {
329 LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
332 // rebuilding fontlist so clear out font/word caches
333 gfxFontCache *fontCache = gfxFontCache::GetCache();
334 if (fontCache) {
335 fontCache->AgeAllGenerations();
336 fontCache->FlushShapedWordCaches();
339 gfxPlatform::PurgeSkiaFontCache();
341 CancelInitOtherFamilyNamesTask();
342 MutexAutoLock lock(mFontFamiliesMutex);
343 mFontFamilies.Clear();
344 mOtherFamilyNames.Clear();
345 mOtherFamilyNamesInitialized = false;
347 if (mExtraNames) {
348 mExtraNames->mFullnames.Clear();
349 mExtraNames->mPostscriptNames.Clear();
351 mFaceNameListsInitialized = false;
352 ClearLangGroupPrefFonts();
353 mReplacementCharFallbackFamily = nullptr;
354 CancelLoader();
356 // initialize ranges of characters for which system-wide font search should be skipped
357 mCodepointsWithNoFonts.reset();
358 mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
359 mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
361 sPlatformFontList = this;
363 nsresult rv = InitFontListForPlatform();
364 if (NS_FAILED(rv)) {
365 return rv;
368 ApplyWhitelist();
369 return NS_OK;
372 void
373 gfxPlatformFontList::GenerateFontListKey(const nsACString& aKeyName, nsACString& aResult)
375 aResult = aKeyName;
376 ToLowerCase(aResult);
379 #define OTHERNAMES_TIMEOUT 200
381 void
382 gfxPlatformFontList::InitOtherFamilyNames(bool aDeferOtherFamilyNamesLoading)
384 if (mOtherFamilyNamesInitialized) {
385 return;
388 // If the font loader delay has been set to zero, we don't defer loading
389 // additional family names (regardless of the aDefer... parameter), as we
390 // take this to mean availability of font info is to be prioritized over
391 // potential startup perf or main-thread jank.
392 // (This is used so we can reliably run reftests that depend on localized
393 // font-family names being available.)
394 if (aDeferOtherFamilyNamesLoading &&
395 Preferences::GetUint(FONT_LOADER_DELAY_PREF) > 0) {
396 if (!mPendingOtherFamilyNameTask) {
397 RefPtr<mozilla::CancelableRunnable> task = new InitOtherFamilyNamesRunnable();
398 mPendingOtherFamilyNameTask = task;
399 NS_IdleDispatchToCurrentThread(task.forget());
401 } else {
402 InitOtherFamilyNamesInternal(false);
406 // time limit for loading facename lists (ms)
407 #define NAMELIST_TIMEOUT 200
409 gfxFontEntry*
410 gfxPlatformFontList::SearchFamiliesForFaceName(const nsACString& aFaceName)
412 TimeStamp start = TimeStamp::Now();
413 bool timedOut = false;
414 // if mFirstChar is not 0, only load facenames for families
415 // that start with this character
416 char16_t firstChar = 0;
417 gfxFontEntry *lookup = nullptr;
419 // iterate over familes starting with the same letter
420 firstChar = ToLowerCase(aFaceName.CharAt(0));
422 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
423 nsCStringHashKey::KeyType key = iter.Key();
424 RefPtr<gfxFontFamily>& family = iter.Data();
426 // when filtering, skip names that don't start with the filter character
427 if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
428 continue;
431 family->ReadFaceNames(this, NeedFullnamePostscriptNames());
433 TimeDuration elapsed = TimeStamp::Now() - start;
434 if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
435 timedOut = true;
436 break;
440 lookup = FindFaceName(aFaceName);
442 TimeStamp end = TimeStamp::Now();
443 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS,
444 start, end);
445 if (LOG_FONTINIT_ENABLED()) {
446 TimeDuration elapsed = end - start;
447 LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
448 elapsed.ToMilliseconds(),
449 (lookup ? "found name" : ""),
450 (timedOut ? "timeout" : "")));
453 return lookup;
456 gfxFontEntry*
457 gfxPlatformFontList::FindFaceName(const nsACString& aFaceName)
459 gfxFontEntry *lookup;
461 // lookup in name lookup tables, return null if not found
462 if (mExtraNames &&
463 ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
464 (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
465 return lookup;
468 return nullptr;
471 gfxFontEntry*
472 gfxPlatformFontList::LookupInFaceNameLists(const nsACString& aFaceName)
474 gfxFontEntry *lookup = nullptr;
476 // initialize facename lookup tables if needed
477 // note: this can terminate early or time out, in which case
478 // mFaceNameListsInitialized remains false
479 if (!mFaceNameListsInitialized) {
480 lookup = SearchFamiliesForFaceName(aFaceName);
481 if (lookup) {
482 return lookup;
486 // lookup in name lookup tables, return null if not found
487 if (!(lookup = FindFaceName(aFaceName))) {
488 // names not completely initialized, so keep track of lookup misses
489 if (!mFaceNameListsInitialized) {
490 if (!mFaceNamesMissed) {
491 mFaceNamesMissed = MakeUnique<nsTHashtable<nsCStringHashKey>>(2);
493 mFaceNamesMissed->PutEntry(aFaceName);
497 return lookup;
500 void
501 gfxPlatformFontList::PreloadNamesList()
503 AutoTArray<nsCString, 10> preloadFonts;
504 gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
506 uint32_t numFonts = preloadFonts.Length();
507 for (uint32_t i = 0; i < numFonts; i++) {
508 nsAutoCString key;
509 GenerateFontListKey(preloadFonts[i], key);
511 // only search canonical names!
512 gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
513 if (familyEntry) {
514 familyEntry->ReadOtherFamilyNames(this);
520 void
521 gfxPlatformFontList::LoadBadUnderlineList()
523 AutoTArray<nsCString, 10> blacklist;
524 gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
525 uint32_t numFonts = blacklist.Length();
526 for (uint32_t i = 0; i < numFonts; i++) {
527 nsAutoCString key;
528 GenerateFontListKey(blacklist[i], key);
529 mBadUnderlineFamilyNames.PutEntry(key);
533 void
534 gfxPlatformFontList::UpdateFontList()
536 InitFontList();
537 RebuildLocalFonts();
540 void
541 gfxPlatformFontList::GetFontList(nsAtom *aLangGroup,
542 const nsACString& aGenericFamily,
543 nsTArray<nsString>& aListOfFonts)
545 MutexAutoLock lock(mFontFamiliesMutex);
546 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
547 RefPtr<gfxFontFamily>& family = iter.Data();
548 if (family->FilterForFontList(aLangGroup, aGenericFamily)) {
549 nsAutoCString localizedFamilyName;
550 family->LocalizedName(localizedFamilyName);
551 aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(localizedFamilyName));
555 aListOfFonts.Sort();
556 aListOfFonts.Compact();
559 void
560 gfxPlatformFontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
562 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
563 RefPtr<gfxFontFamily>& family = iter.Data();
564 aFamilyArray.AppendElement(family);
568 gfxFontEntry*
569 gfxPlatformFontList::SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
570 Script aRunScript,
571 const gfxFontStyle* aStyle)
573 gfxFontEntry* fontEntry = nullptr;
575 // is codepoint with no matching font? return null immediately
576 if (mCodepointsWithNoFonts.test(aCh)) {
577 return nullptr;
580 // Try to short-circuit font fallback for U+FFFD, used to represent
581 // encoding errors: just use cached family from last time U+FFFD was seen.
582 // This helps speed up pages with lots of encoding errors, binary-as-text,
583 // etc.
584 if (aCh == 0xFFFD && mReplacementCharFallbackFamily) {
585 fontEntry =
586 mReplacementCharFallbackFamily->FindFontForStyle(*aStyle);
588 // this should never fail, as we must have found U+FFFD in order to set
589 // mReplacementCharFallbackFamily at all, but better play it safe
590 if (fontEntry && fontEntry->HasCharacter(aCh)) {
591 return fontEntry;
595 TimeStamp start = TimeStamp::Now();
597 // search commonly available fonts
598 bool common = true;
599 gfxFontFamily *fallbackFamily = nullptr;
600 fontEntry = CommonFontFallback(aCh, aNextCh, aRunScript, aStyle,
601 &fallbackFamily);
603 // if didn't find a font, do system-wide fallback (except for specials)
604 uint32_t cmapCount = 0;
605 if (!fontEntry) {
606 common = false;
607 fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount,
608 &fallbackFamily);
610 TimeDuration elapsed = TimeStamp::Now() - start;
612 LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
614 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
615 uint32_t unicodeRange = FindCharUnicodeRange(aCh);
616 Script script = mozilla::unicode::GetScriptCode(aCh);
617 MOZ_LOG(log, LogLevel::Warning,\
618 ("(textrun-systemfallback-%s) char: u+%6.6x "
619 "unicode-range: %d script: %d match: [%s]"
620 " time: %dus cmaps: %d\n",
621 (common ? "common" : "global"), aCh,
622 unicodeRange, static_cast<int>(script),
623 (fontEntry ? fontEntry->Name().get() :
624 "<none>"),
625 int32_t(elapsed.ToMicroseconds()),
626 cmapCount));
629 // no match? add to set of non-matching codepoints
630 if (!fontEntry) {
631 mCodepointsWithNoFonts.set(aCh);
632 } else if (aCh == 0xFFFD && fontEntry && fallbackFamily) {
633 mReplacementCharFallbackFamily = fallbackFamily;
636 // track system fallback time
637 static bool first = true;
638 int32_t intElapsed = int32_t(first ? elapsed.ToMilliseconds() :
639 elapsed.ToMicroseconds());
640 Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST :
641 Telemetry::SYSTEM_FONT_FALLBACK),
642 intElapsed);
643 first = false;
645 // track the script for which fallback occurred (incremented one make it
646 // 1-based)
647 Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT,
648 int(aRunScript) + 1);
650 return fontEntry;
653 #define NUM_FALLBACK_FONTS 8
655 gfxFontEntry*
656 gfxPlatformFontList::CommonFontFallback(uint32_t aCh, uint32_t aNextCh,
657 Script aRunScript,
658 const gfxFontStyle* aMatchStyle,
659 gfxFontFamily** aMatchedFamily)
661 AutoTArray<const char*,NUM_FALLBACK_FONTS> defaultFallbacks;
662 uint32_t i, numFallbacks;
664 gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aNextCh,
665 aRunScript,
666 defaultFallbacks);
667 numFallbacks = defaultFallbacks.Length();
668 for (i = 0; i < numFallbacks; i++) {
669 nsAutoCString familyName(defaultFallbacks[i]);
670 gfxFontFamily *fallback = FindFamilyByCanonicalName(familyName);
671 if (!fallback) {
672 continue;
675 gfxFontEntry *fontEntry;
677 // use first font in list that supports a given character
678 fontEntry = fallback->FindFontForStyle(*aMatchStyle);
679 if (fontEntry) {
680 if (fontEntry->HasCharacter(aCh)) {
681 *aMatchedFamily = fallback;
682 return fontEntry;
684 // If we requested a styled font (bold and/or italic), and the char
685 // was not available, check other faces of the family.
686 if (!fontEntry->IsNormalStyle()) {
687 // If style/weight/stretch was not Normal, see if we can
688 // fall back to a next-best face (e.g. Arial Black -> Bold,
689 // or Arial Narrow -> Regular).
690 GlobalFontMatch data(aCh, *aMatchStyle);
691 fallback->SearchAllFontsForChar(&data);
692 if (data.mBestMatch) {
693 *aMatchedFamily = fallback;
694 return data.mBestMatch;
700 return nullptr;
703 gfxFontEntry*
704 gfxPlatformFontList::GlobalFontFallback(const uint32_t aCh,
705 Script aRunScript,
706 const gfxFontStyle* aMatchStyle,
707 uint32_t& aCmapCount,
708 gfxFontFamily** aMatchedFamily)
710 bool useCmaps = IsFontFamilyWhitelistActive() ||
711 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
712 if (!useCmaps) {
713 // Allow platform-specific fallback code to try and find a usable font
714 gfxFontEntry* fe =
715 PlatformGlobalFontFallback(aCh, aRunScript, aMatchStyle,
716 aMatchedFamily);
717 if (fe) {
718 return fe;
722 // otherwise, try to find it among local fonts
723 GlobalFontMatch data(aCh, *aMatchStyle);
725 // iterate over all font families to find a font that support the character
726 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
727 RefPtr<gfxFontFamily>& family = iter.Data();
728 // evaluate all fonts in this family for a match
729 family->FindFontForChar(&data);
732 aCmapCount = data.mCmapsTested;
733 *aMatchedFamily = data.mMatchedFamily;
735 return data.mBestMatch;
738 gfxFontFamily*
739 gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily)
741 if (aFamily && !aFamily->HasStyles()) {
742 aFamily->FindStyleVariations();
743 aFamily->CheckForSimpleFamily();
746 if (aFamily && aFamily->GetFontList().Length() == 0) {
747 // failed to load any faces for this family, so discard it
748 nsAutoCString key;
749 GenerateFontListKey(aFamily->Name(), key);
750 mFontFamilies.Remove(key);
751 return nullptr;
754 return aFamily;
757 bool
758 gfxPlatformFontList::FindAndAddFamilies(const nsACString& aFamily,
759 nsTArray<FamilyAndGeneric>* aOutput,
760 FindFamiliesFlags aFlags,
761 gfxFontStyle* aStyle,
762 gfxFloat aDevToCssSize)
764 nsAutoCString key;
765 GenerateFontListKey(aFamily, key);
767 NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
769 // lookup in canonical (i.e. English) family name list
770 gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
772 // if not found, lookup in other family names list (mostly localized names)
773 if (!familyEntry) {
774 familyEntry = mOtherFamilyNames.GetWeak(key);
777 // if still not found and other family names not yet fully initialized,
778 // initialize the rest of the list and try again. this is done lazily
779 // since reading name table entries is expensive.
780 // although ASCII localized family names are possible they don't occur
781 // in practice so avoid pulling in names at startup
782 if (!familyEntry && !mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
783 InitOtherFamilyNames(!(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading));
784 familyEntry = mOtherFamilyNames.GetWeak(key);
785 if (!familyEntry && !mOtherFamilyNamesInitialized &&
786 !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
787 // localized family names load timed out, add name to list of
788 // names to check after localized names are loaded
789 if (!mOtherNamesMissed) {
790 mOtherNamesMissed = MakeUnique<nsTHashtable<nsCStringHashKey>>(2);
792 mOtherNamesMissed->PutEntry(key);
796 familyEntry = CheckFamily(familyEntry);
798 // If we failed to find the requested family, check for a space in the
799 // name; if found, and if the "base" name (up to the last space) exists
800 // as a family, then this might be a legacy GDI-style family name for
801 // an additional weight/width. Try searching the faces of the base family
802 // and create any corresponding legacy families.
803 if (!familyEntry && !(aFlags & FindFamiliesFlags::eNoSearchForLegacyFamilyNames)) {
804 // We don't have nsAString::RFindChar, so look for a space manually
805 const char* data = aFamily.BeginReading();
806 int32_t index = aFamily.Length();
807 while (--index > 0) {
808 if (data[index] == ' ') {
809 break;
812 if (index > 0) {
813 gfxFontFamily* base =
814 FindFamily(Substring(aFamily, 0, index),
815 FindFamiliesFlags::eNoSearchForLegacyFamilyNames);
816 // If we found the "base" family name, and if it has members with
817 // legacy names, this will add corresponding font-family entries to
818 // the mOtherFamilyNames list; then retry the legacy-family search.
819 if (base && base->CheckForLegacyFamilyNames(this)) {
820 familyEntry = mOtherFamilyNames.GetWeak(key);
825 if (familyEntry) {
826 aOutput->AppendElement(FamilyAndGeneric(familyEntry));
827 return true;
830 return false;
833 gfxFontEntry*
834 gfxPlatformFontList::FindFontForFamily(const nsACString& aFamily,
835 const gfxFontStyle* aStyle)
837 gfxFontFamily *familyEntry = FindFamily(aFamily);
839 if (familyEntry)
840 return familyEntry->FindFontForStyle(*aStyle);
842 return nullptr;
845 void
846 gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsCString& aOtherFamilyName)
848 nsAutoCString key;
849 GenerateFontListKey(aOtherFamilyName, key);
851 if (!mOtherFamilyNames.GetWeak(key)) {
852 mOtherFamilyNames.Put(key, aFamilyEntry);
853 LOG_FONTLIST(("(fontlist-otherfamily) canonical family: %s, "
854 "other family: %s\n",
855 aFamilyEntry->Name().get(),
856 aOtherFamilyName.get()));
857 if (mBadUnderlineFamilyNames.Contains(key))
858 aFamilyEntry->SetBadUnderlineFamily();
862 void
863 gfxPlatformFontList::AddFullname(gfxFontEntry *aFontEntry, const nsCString& aFullname)
865 if (!mExtraNames->mFullnames.GetWeak(aFullname)) {
866 mExtraNames->mFullnames.Put(aFullname, aFontEntry);
867 LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
868 aFontEntry->Name().get(),
869 aFullname.get()));
873 void
874 gfxPlatformFontList::AddPostscriptName(gfxFontEntry *aFontEntry,
875 const nsCString& aPostscriptName)
877 if (!mExtraNames->mPostscriptNames.GetWeak(aPostscriptName)) {
878 mExtraNames->mPostscriptNames.Put(aPostscriptName, aFontEntry);
879 LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
880 aFontEntry->Name().get(),
881 aPostscriptName.get()));
885 bool
886 gfxPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
887 nsACString& aFamilyName)
889 aFamilyName.Truncate();
890 gfxFontFamily *ff = FindFamily(aFontName);
891 if (!ff) {
892 return false;
894 aFamilyName = ff->Name();
895 return true;
898 gfxFontFamily*
899 gfxPlatformFontList::GetDefaultFontFamily(const nsACString& aLangGroup,
900 const nsACString& aGenericFamily)
902 if (NS_WARN_IF(aLangGroup.IsEmpty()) ||
903 NS_WARN_IF(aGenericFamily.IsEmpty())) {
904 return nullptr;
907 AutoTArray<nsCString,4> names;
908 gfxFontUtils::AppendPrefsFontList(
909 NameListPref(aGenericFamily, aLangGroup).get(), names);
911 for (const nsCString& name : names) {
912 gfxFontFamily* fontFamily = FindFamily(name);
913 if (fontFamily) {
914 return fontFamily;
917 return nullptr;
920 gfxCharacterMap*
921 gfxPlatformFontList::FindCharMap(gfxCharacterMap *aCmap)
923 aCmap->CalcHash();
924 gfxCharacterMap *cmap = AddCmap(aCmap);
925 cmap->mShared = true;
926 return cmap;
929 // add a cmap to the shared cmap set
930 gfxCharacterMap*
931 gfxPlatformFontList::AddCmap(const gfxCharacterMap* aCharMap)
933 CharMapHashKey *found =
934 mSharedCmaps.PutEntry(const_cast<gfxCharacterMap*>(aCharMap));
935 return found->GetKey();
938 // remove the cmap from the shared cmap set
939 void
940 gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap)
942 // skip lookups during teardown
943 if (mSharedCmaps.Count() == 0) {
944 return;
947 // cmap needs to match the entry *and* be the same ptr before removing
948 CharMapHashKey *found =
949 mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
950 if (found && found->GetKey() == aCharMap) {
951 mSharedCmaps.RemoveEntry(found);
955 void
956 gfxPlatformFontList::ResolveGenericFontNames(
957 FontFamilyType aGenericType,
958 eFontPrefLang aPrefLang,
959 nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
961 const char* langGroupStr = GetPrefLangName(aPrefLang);
962 const char* generic = GetGenericName(aGenericType);
964 if (!generic) {
965 return;
968 AutoTArray<nsCString,4> genericFamilies;
970 // load family for "font.name.generic.lang"
971 gfxFontUtils::AppendPrefsFontList(
972 NamePref(generic, langGroupStr).get(), genericFamilies);
974 // load fonts for "font.name-list.generic.lang"
975 gfxFontUtils::AppendPrefsFontList(
976 NameListPref(generic, langGroupStr).get(), genericFamilies);
978 nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
979 NS_ASSERTION(langGroup, "null lang group for pref lang");
981 gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies,
982 langGroup,
983 aGenericFamilies);
985 #if 0 // dump out generic mappings
986 printf("%s ===> ", prefFontName.get());
987 for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
988 if (k > 0) printf(", ");
989 printf("%s", aGenericFamilies[k]->Name().get());
991 printf("\n");
992 #endif
995 void
996 gfxPlatformFontList::ResolveEmojiFontNames(
997 nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
999 // emoji preference has no lang name
1000 AutoTArray<nsCString,4> genericFamilies;
1002 nsAutoCString prefFontListName("font.name-list.emoji");
1003 gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies);
1005 gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies,
1006 nullptr,
1007 aGenericFamilies);
1010 void
1011 gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(
1012 nsTArray<nsCString>& aGenericNameFamilies,
1013 nsAtom* aLangGroup,
1014 nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
1016 // lookup and add platform fonts uniquely
1017 for (const nsCString& genericFamily : aGenericNameFamilies) {
1018 gfxFontStyle style;
1019 style.language = aLangGroup;
1020 style.systemFont = false;
1021 AutoTArray<FamilyAndGeneric,10> families;
1022 FindAndAddFamilies(genericFamily,
1023 &families, FindFamiliesFlags(0),
1024 &style);
1025 for (const FamilyAndGeneric& f : families) {
1026 if (!aGenericFamilies->Contains(f.mFamily)) {
1027 aGenericFamilies->AppendElement(f.mFamily);
1033 nsTArray<RefPtr<gfxFontFamily>>*
1034 gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
1035 eFontPrefLang aPrefLang)
1037 // treat -moz-fixed as monospace
1038 if (aGenericType == eFamily_moz_fixed) {
1039 aGenericType = eFamily_monospace;
1042 if (aGenericType == eFamily_moz_emoji) {
1043 // Emoji font has no lang
1044 PrefFontList* prefFonts = mEmojiPrefFont.get();
1045 if (MOZ_UNLIKELY(!prefFonts)) {
1046 prefFonts = new PrefFontList;
1047 ResolveEmojiFontNames(prefFonts);
1048 mEmojiPrefFont.reset(prefFonts);
1050 return prefFonts;
1053 PrefFontList* prefFonts =
1054 mLangGroupPrefFonts[aPrefLang][aGenericType].get();
1055 if (MOZ_UNLIKELY(!prefFonts)) {
1056 prefFonts = new PrefFontList;
1057 ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts);
1058 mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts);
1060 return prefFonts;
1063 void
1064 gfxPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
1065 nsAtom* aLanguage,
1066 nsTArray<FamilyAndGeneric>& aFamilyList)
1068 // map lang ==> langGroup
1069 nsAtom* langGroup = GetLangGroup(aLanguage);
1071 // langGroup ==> prefLang
1072 eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
1074 // lookup pref fonts
1075 nsTArray<RefPtr<gfxFontFamily>>* prefFonts =
1076 GetPrefFontsLangGroup(aGenericType, prefLang);
1078 if (!prefFonts->IsEmpty()) {
1079 aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
1080 for (auto& f : *prefFonts) {
1081 aFamilyList.AppendElement(FamilyAndGeneric(f.get(), aGenericType));
1086 static nsAtom* PrefLangToLangGroups(uint32_t aIndex)
1088 // static array here avoids static constructor
1089 static nsAtom* gPrefLangToLangGroups[] = {
1090 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
1091 #include "gfxFontPrefLangList.h"
1092 #undef FONT_PREF_LANG
1095 return aIndex < ArrayLength(gPrefLangToLangGroups)
1096 ? gPrefLangToLangGroups[aIndex]
1097 : nsGkAtoms::Unicode;
1100 eFontPrefLang
1101 gfxPlatformFontList::GetFontPrefLangFor(const char* aLang)
1103 if (!aLang || !aLang[0]) {
1104 return eFontPrefLang_Others;
1106 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
1107 if (!PL_strcasecmp(gPrefLangNames[i], aLang)) {
1108 return eFontPrefLang(i);
1111 return eFontPrefLang_Others;
1114 eFontPrefLang
1115 gfxPlatformFontList::GetFontPrefLangFor(nsAtom *aLang)
1117 if (!aLang)
1118 return eFontPrefLang_Others;
1119 nsAutoCString lang;
1120 aLang->ToUTF8String(lang);
1121 return GetFontPrefLangFor(lang.get());
1124 nsAtom*
1125 gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang)
1127 // the special CJK set pref lang should be resolved into separate
1128 // calls to individual CJK pref langs before getting here
1129 NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
1131 return PrefLangToLangGroups(uint32_t(aLang));
1134 const char*
1135 gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang)
1137 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
1138 return gPrefLangNames[uint32_t(aLang)];
1140 return nullptr;
1143 eFontPrefLang
1144 gfxPlatformFontList::GetFontPrefLangFor(uint8_t aUnicodeRange)
1146 switch (aUnicodeRange) {
1147 case kRangeSetLatin: return eFontPrefLang_Western;
1148 case kRangeCyrillic: return eFontPrefLang_Cyrillic;
1149 case kRangeGreek: return eFontPrefLang_Greek;
1150 case kRangeHebrew: return eFontPrefLang_Hebrew;
1151 case kRangeArabic: return eFontPrefLang_Arabic;
1152 case kRangeThai: return eFontPrefLang_Thai;
1153 case kRangeKorean: return eFontPrefLang_Korean;
1154 case kRangeJapanese: return eFontPrefLang_Japanese;
1155 case kRangeSChinese: return eFontPrefLang_ChineseCN;
1156 case kRangeTChinese: return eFontPrefLang_ChineseTW;
1157 case kRangeDevanagari: return eFontPrefLang_Devanagari;
1158 case kRangeTamil: return eFontPrefLang_Tamil;
1159 case kRangeArmenian: return eFontPrefLang_Armenian;
1160 case kRangeBengali: return eFontPrefLang_Bengali;
1161 case kRangeCanadian: return eFontPrefLang_Canadian;
1162 case kRangeEthiopic: return eFontPrefLang_Ethiopic;
1163 case kRangeGeorgian: return eFontPrefLang_Georgian;
1164 case kRangeGujarati: return eFontPrefLang_Gujarati;
1165 case kRangeGurmukhi: return eFontPrefLang_Gurmukhi;
1166 case kRangeKhmer: return eFontPrefLang_Khmer;
1167 case kRangeMalayalam: return eFontPrefLang_Malayalam;
1168 case kRangeOriya: return eFontPrefLang_Oriya;
1169 case kRangeTelugu: return eFontPrefLang_Telugu;
1170 case kRangeKannada: return eFontPrefLang_Kannada;
1171 case kRangeSinhala: return eFontPrefLang_Sinhala;
1172 case kRangeTibetan: return eFontPrefLang_Tibetan;
1173 case kRangeSetCJK: return eFontPrefLang_CJKSet;
1174 default: return eFontPrefLang_Others;
1178 bool
1179 gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang)
1181 switch (aLang) {
1182 case eFontPrefLang_Japanese:
1183 case eFontPrefLang_ChineseTW:
1184 case eFontPrefLang_ChineseCN:
1185 case eFontPrefLang_ChineseHK:
1186 case eFontPrefLang_Korean:
1187 case eFontPrefLang_CJKSet:
1188 return true;
1189 default:
1190 return false;
1194 void
1195 gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
1197 if (IsLangCJK(aCharLang)) {
1198 AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
1199 } else {
1200 AppendPrefLang(aPrefLangs, aLen, aCharLang);
1203 AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
1206 void
1207 gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
1209 // prefer the lang specified by the page *if* CJK
1210 if (IsLangCJK(aPageLang)) {
1211 AppendPrefLang(aPrefLangs, aLen, aPageLang);
1214 // if not set up, set up the default CJK order, based on accept lang settings and locale
1215 if (mCJKPrefLangs.Length() == 0) {
1217 // temp array
1218 eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
1219 uint32_t tempLen = 0;
1221 // Add the CJK pref fonts from accept languages, the order should be same order
1222 nsAutoCString list;
1223 Preferences::GetLocalizedCString("intl.accept_languages", list);
1224 if (!list.IsEmpty()) {
1225 const char kComma = ',';
1226 const char *p, *p_end;
1227 list.BeginReading(p);
1228 list.EndReading(p_end);
1229 while (p < p_end) {
1230 while (nsCRT::IsAsciiSpace(*p)) {
1231 if (++p == p_end)
1232 break;
1234 if (p == p_end)
1235 break;
1236 const char *start = p;
1237 while (++p != p_end && *p != kComma)
1238 /* nothing */ ;
1239 nsAutoCString lang(Substring(start, p));
1240 lang.CompressWhitespace(false, true);
1241 eFontPrefLang fpl = gfxPlatformFontList::GetFontPrefLangFor(lang.get());
1242 switch (fpl) {
1243 case eFontPrefLang_Japanese:
1244 case eFontPrefLang_Korean:
1245 case eFontPrefLang_ChineseCN:
1246 case eFontPrefLang_ChineseHK:
1247 case eFontPrefLang_ChineseTW:
1248 AppendPrefLang(tempPrefLangs, tempLen, fpl);
1249 break;
1250 default:
1251 break;
1253 p++;
1257 // Try using app's locale
1258 nsAutoCString localeStr;
1259 LocaleService::GetInstance()->GetAppLocaleAsLangTag(localeStr);
1262 Locale locale(localeStr);
1263 if (locale.GetLanguage().Equals("ja")) {
1264 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
1265 } else if (locale.GetLanguage().Equals("zh")) {
1266 if (locale.GetRegion().Equals("CN")) {
1267 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
1268 } else if (locale.GetRegion().Equals("TW")) {
1269 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
1270 } else if (locale.GetRegion().Equals("HK")) {
1271 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
1273 } else if (locale.GetLanguage().Equals("ko")) {
1274 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
1278 // Then add the known CJK prefs in order of system preferred locales
1279 AutoTArray<nsCString,5> prefLocales;
1280 prefLocales.AppendElement(NS_LITERAL_CSTRING("ja"));
1281 prefLocales.AppendElement(NS_LITERAL_CSTRING("zh-CN"));
1282 prefLocales.AppendElement(NS_LITERAL_CSTRING("zh-TW"));
1283 prefLocales.AppendElement(NS_LITERAL_CSTRING("zh-HK"));
1284 prefLocales.AppendElement(NS_LITERAL_CSTRING("ko"));
1286 AutoTArray<nsCString,16> sysLocales;
1287 AutoTArray<nsCString,16> negLocales;
1288 if (NS_SUCCEEDED(OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) {
1289 LocaleService::GetInstance()->NegotiateLanguages(
1290 sysLocales, prefLocales, NS_LITERAL_CSTRING(""),
1291 LocaleService::kLangNegStrategyFiltering, negLocales);
1292 for (const auto& localeStr : negLocales) {
1293 Locale locale(localeStr);
1295 if (locale.GetLanguage().Equals("ja")) {
1296 AppendPrefLang(tempPrefLangs, tempLen,
1297 eFontPrefLang_Japanese);
1298 } else if (locale.GetLanguage().Equals("zh")) {
1299 if (locale.GetRegion().Equals("CN")) {
1300 AppendPrefLang(tempPrefLangs, tempLen,
1301 eFontPrefLang_ChineseCN);
1302 } else if (locale.GetRegion().Equals("TW")) {
1303 AppendPrefLang(tempPrefLangs, tempLen,
1304 eFontPrefLang_ChineseTW);
1305 } else if (locale.GetRegion().Equals("HK")) {
1306 AppendPrefLang(tempPrefLangs, tempLen,
1307 eFontPrefLang_ChineseHK);
1309 } else if (locale.GetLanguage().Equals("ko")) {
1310 AppendPrefLang(tempPrefLangs, tempLen,
1311 eFontPrefLang_Korean);
1316 // last resort... (the order is same as old gfx.)
1317 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
1318 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
1319 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
1320 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
1321 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
1323 // copy into the cached array
1324 uint32_t j;
1325 for (j = 0; j < tempLen; j++) {
1326 mCJKPrefLangs.AppendElement(tempPrefLangs[j]);
1330 // append in cached CJK langs
1331 uint32_t i, numCJKlangs = mCJKPrefLangs.Length();
1333 for (i = 0; i < numCJKlangs; i++) {
1334 AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i]));
1339 void
1340 gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang)
1342 if (aLen >= kMaxLenPrefLangList) return;
1344 // make sure
1345 uint32_t i = 0;
1346 while (i < aLen && aPrefLangs[i] != aAddLang) {
1347 i++;
1350 if (i == aLen) {
1351 aPrefLangs[aLen] = aAddLang;
1352 aLen++;
1356 mozilla::FontFamilyType
1357 gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang)
1359 if (aLang == eFontPrefLang_Emoji) {
1360 return eFamily_moz_emoji;
1363 // initialize lang group pref font defaults (i.e. serif/sans-serif)
1364 if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) {
1365 mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
1366 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
1367 nsAutoCString prefDefaultFontType("font.default.");
1368 prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
1369 nsAutoCString serifOrSans;
1370 Preferences::GetCString(prefDefaultFontType.get(), serifOrSans);
1371 if (serifOrSans.EqualsLiteral("sans-serif")) {
1372 mDefaultGenericsLangGroup[i] = eFamily_sans_serif;
1373 } else {
1374 mDefaultGenericsLangGroup[i] = eFamily_serif;
1379 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
1380 return mDefaultGenericsLangGroup[uint32_t(aLang)];
1382 return eFamily_serif;
1386 gfxFontFamily*
1387 gfxPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
1389 gfxFontFamily* family = GetDefaultFontForPlatform(aStyle);
1390 if (family) {
1391 return family;
1393 // Something has gone wrong and we were unable to retrieve a default font
1394 // from the platform. (Likely the whitelist has blocked all potential
1395 // default fonts.) As a last resort, we return the first font listed in
1396 // mFontFamilies.
1397 return mFontFamilies.Iter().Data();
1400 void
1401 gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsCString>& aFontFamilyNames)
1403 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1404 RefPtr<gfxFontFamily>& family = iter.Data();
1405 aFontFamilyNames.AppendElement(family->Name());
1409 nsAtom*
1410 gfxPlatformFontList::GetLangGroup(nsAtom* aLanguage)
1412 // map lang ==> langGroup
1413 nsAtom *langGroup = nullptr;
1414 if (aLanguage) {
1415 langGroup = mLangService->GetLanguageGroup(aLanguage);
1417 if (!langGroup) {
1418 langGroup = nsGkAtoms::Unicode;
1420 return langGroup;
1423 /* static */ const char*
1424 gfxPlatformFontList::GetGenericName(FontFamilyType aGenericType)
1426 static const char kGeneric_serif[] = "serif";
1427 static const char kGeneric_sans_serif[] = "sans-serif";
1428 static const char kGeneric_monospace[] = "monospace";
1429 static const char kGeneric_cursive[] = "cursive";
1430 static const char kGeneric_fantasy[] = "fantasy";
1432 // type should be standard generic type at this point
1433 NS_ASSERTION(aGenericType >= eFamily_serif &&
1434 aGenericType <= eFamily_fantasy,
1435 "standard generic font family type required");
1437 // map generic type to string
1438 const char *generic = nullptr;
1439 switch (aGenericType) {
1440 case eFamily_serif:
1441 generic = kGeneric_serif;
1442 break;
1443 case eFamily_sans_serif:
1444 generic = kGeneric_sans_serif;
1445 break;
1446 case eFamily_monospace:
1447 generic = kGeneric_monospace;
1448 break;
1449 case eFamily_cursive:
1450 generic = kGeneric_cursive;
1451 break;
1452 case eFamily_fantasy:
1453 generic = kGeneric_fantasy;
1454 break;
1455 default:
1456 break;
1459 return generic;
1462 void
1463 gfxPlatformFontList::InitLoader()
1465 GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
1466 mStartIndex = 0;
1467 mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
1468 memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
1471 #define FONT_LOADER_MAX_TIMESLICE 100 // max time for one pass through RunLoader = 100ms
1473 bool
1474 gfxPlatformFontList::LoadFontInfo()
1476 TimeStamp start = TimeStamp::Now();
1477 uint32_t i, endIndex = mNumFamilies;
1478 bool loadCmaps = !UsesSystemFallback() ||
1479 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1481 // for each font family, load in various font info
1482 for (i = mStartIndex; i < endIndex; i++) {
1483 nsAutoCString key;
1484 gfxFontFamily *familyEntry;
1485 GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
1487 // lookup in canonical (i.e. English) family name list
1488 if (!(familyEntry = mFontFamilies.GetWeak(key))) {
1489 continue;
1492 // read in face names
1493 familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(), mFontInfo);
1495 // load the cmaps if needed
1496 if (loadCmaps) {
1497 familyEntry->ReadAllCMAPs(mFontInfo);
1500 // limit the time spent reading fonts in one pass
1501 TimeDuration elapsed = TimeStamp::Now() - start;
1502 if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
1503 i + 1 != endIndex) {
1504 endIndex = i + 1;
1505 break;
1509 mStartIndex = endIndex;
1510 bool done = mStartIndex >= mNumFamilies;
1512 if (LOG_FONTINIT_ENABLED()) {
1513 TimeDuration elapsed = TimeStamp::Now() - start;
1514 LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
1515 elapsed.ToMilliseconds(), (done ? "true" : "false")));
1518 if (done) {
1519 mOtherFamilyNamesInitialized = true;
1520 CancelInitOtherFamilyNamesTask();
1521 mFaceNameListsInitialized = true;
1524 return done;
1527 void
1528 gfxPlatformFontList::CleanupLoader()
1530 mFontFamiliesToLoad.Clear();
1531 mNumFamilies = 0;
1532 bool rebuilt = false, forceReflow = false;
1534 // if had missed face names that are now available, force reflow all
1535 if (mFaceNamesMissed) {
1536 for (auto it = mFaceNamesMissed->Iter(); !it.Done(); it.Next()) {
1537 if (FindFaceName(it.Get()->GetKey())) {
1538 rebuilt = true;
1539 RebuildLocalFonts();
1540 break;
1543 mFaceNamesMissed = nullptr;
1546 if (mOtherNamesMissed) {
1547 for (auto it = mOtherNamesMissed->Iter(); !it.Done(); it.Next()) {
1548 if (FindFamily(it.Get()->GetKey(),
1549 (FindFamiliesFlags::eForceOtherFamilyNamesLoading |
1550 FindFamiliesFlags::eNoAddToNamesMissedWhenSearching))) {
1551 forceReflow = true;
1552 ForceGlobalReflow();
1553 break;
1556 mOtherNamesMissed = nullptr;
1559 if (LOG_FONTINIT_ENABLED() && mFontInfo) {
1560 LOG_FONTINIT(("(fontinit) fontloader load thread took %8.2f ms "
1561 "%d families %d fonts %d cmaps "
1562 "%d facenames %d othernames %s %s",
1563 mLoadTime.ToMilliseconds(),
1564 mFontInfo->mLoadStats.families,
1565 mFontInfo->mLoadStats.fonts,
1566 mFontInfo->mLoadStats.cmaps,
1567 mFontInfo->mLoadStats.facenames,
1568 mFontInfo->mLoadStats.othernames,
1569 (rebuilt ? "(userfont sets rebuilt)" : ""),
1570 (forceReflow ? "(global reflow)" : "")));
1573 gfxFontInfoLoader::CleanupLoader();
1576 void
1577 gfxPlatformFontList::GetPrefsAndStartLoader()
1579 uint32_t delay =
1580 std::max(1u, Preferences::GetUint(FONT_LOADER_DELAY_PREF));
1581 uint32_t interval =
1582 std::max(1u, Preferences::GetUint(FONT_LOADER_INTERVAL_PREF));
1584 StartLoader(delay, interval);
1587 void
1588 gfxPlatformFontList::RebuildLocalFonts()
1590 for (auto it = mUserFontSetList.Iter(); !it.Done(); it.Next()) {
1591 it.Get()->GetKey()->RebuildLocalRules();
1595 void
1596 gfxPlatformFontList::ClearLangGroupPrefFonts()
1598 for (uint32_t i = eFontPrefLang_First;
1599 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
1600 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
1601 for (uint32_t j = eFamily_generic_first;
1602 j < eFamily_generic_first + eFamily_generic_count; j++) {
1603 prefFontsLangGroup[j] = nullptr;
1606 mCJKPrefLangs.Clear();
1607 mEmojiPrefFont = nullptr;
1610 // Support for memory reporting
1612 // this is also used by subclasses that hold additional font tables
1613 /*static*/ size_t
1614 gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis(
1615 const FontFamilyTable& aTable,
1616 MallocSizeOf aMallocSizeOf)
1618 size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
1619 for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
1620 // We don't count the size of the family here, because this is an
1621 // *extra* reference to a family that will have already been counted in
1622 // the main list.
1623 n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1625 return n;
1628 /*static*/ size_t
1629 gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
1630 const FontEntryTable& aTable,
1631 MallocSizeOf aMallocSizeOf)
1633 size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
1634 for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
1635 // The font itself is counted by its owning family; here we only care
1636 // about the names stored in the hashtable keys.
1637 n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1639 return n;
1642 void
1643 gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
1644 FontListSizes* aSizes) const
1646 aSizes->mFontListSize +=
1647 mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
1648 for (auto iter = mFontFamilies.ConstIter(); !iter.Done(); iter.Next()) {
1649 aSizes->mFontListSize +=
1650 iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1651 iter.Data()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
1654 aSizes->mFontListSize +=
1655 SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
1657 if (mExtraNames) {
1658 aSizes->mFontListSize +=
1659 SizeOfFontEntryTableExcludingThis(mExtraNames->mFullnames,
1660 aMallocSizeOf);
1661 aSizes->mFontListSize +=
1662 SizeOfFontEntryTableExcludingThis(mExtraNames->mPostscriptNames,
1663 aMallocSizeOf);
1666 for (uint32_t i = eFontPrefLang_First;
1667 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
1668 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
1669 for (uint32_t j = eFamily_generic_first;
1670 j < eFamily_generic_first + eFamily_generic_count; j++) {
1671 PrefFontList* pf = prefFontsLangGroup[j].get();
1672 if (pf) {
1673 aSizes->mFontListSize +=
1674 pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
1679 aSizes->mFontListSize +=
1680 mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf);
1681 aSizes->mFontListSize +=
1682 mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
1684 aSizes->mFontListSize +=
1685 mBadUnderlineFamilyNames.SizeOfExcludingThis(aMallocSizeOf);
1687 aSizes->mFontListSize +=
1688 mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
1689 for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
1690 aSizes->mCharMapsSize +=
1691 iter.Get()->GetKey()->SizeOfIncludingThis(aMallocSizeOf);
1695 void
1696 gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
1697 FontListSizes* aSizes) const
1699 aSizes->mFontListSize += aMallocSizeOf(this);
1700 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
1703 bool
1704 gfxPlatformFontList::IsFontFamilyWhitelistActive()
1706 return mFontFamilyWhitelistActive;
1709 void
1710 gfxPlatformFontList::InitOtherFamilyNamesInternal(bool aDeferOtherFamilyNamesLoading)
1712 if (mOtherFamilyNamesInitialized) {
1713 return;
1716 if (aDeferOtherFamilyNamesLoading) {
1717 TimeStamp start = TimeStamp::Now();
1718 bool timedOut = false;
1720 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1721 RefPtr<gfxFontFamily>& family = iter.Data();
1722 family->ReadOtherFamilyNames(this);
1723 TimeDuration elapsed = TimeStamp::Now() - start;
1724 if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
1725 timedOut = true;
1726 break;
1730 if (!timedOut) {
1731 mOtherFamilyNamesInitialized = true;
1732 CancelInitOtherFamilyNamesTask();
1734 TimeStamp end = TimeStamp::Now();
1735 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
1736 start, end);
1738 if (LOG_FONTINIT_ENABLED()) {
1739 TimeDuration elapsed = end - start;
1740 LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
1741 elapsed.ToMilliseconds(),
1742 (timedOut ? "timeout" : "")));
1744 } else {
1745 TimeStamp start = TimeStamp::Now();
1747 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1748 RefPtr<gfxFontFamily>& family = iter.Data();
1749 family->ReadOtherFamilyNames(this);
1752 mOtherFamilyNamesInitialized = true;
1753 CancelInitOtherFamilyNamesTask();
1755 TimeStamp end = TimeStamp::Now();
1756 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES_NO_DEFERRING,
1757 start, end);
1759 if (LOG_FONTINIT_ENABLED()) {
1760 TimeDuration elapsed = end - start;
1761 LOG_FONTINIT(("(fontinit) InitOtherFamilyNames without deferring took %8.2f ms",
1762 elapsed.ToMilliseconds()));
1767 void
1768 gfxPlatformFontList::CancelInitOtherFamilyNamesTask()
1770 if (mPendingOtherFamilyNameTask) {
1771 mPendingOtherFamilyNameTask->Cancel();
1772 mPendingOtherFamilyNameTask = nullptr;
1776 #undef LOG
1777 #undef LOG_ENABLED