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/. */
5
6 #include "mozilla/Logging.h"
7
8 #include "gfxPlatformFontList.h"
9 #include "gfxTextRun.h"
10 #include "gfxUserFontSet.h"
11
12 #include "nsCRT.h"
13 #include "nsGkAtoms.h"
14 #include "nsILocaleService.h"
15 #include "nsServiceManagerUtils.h"
16 #include "nsUnicharUtils.h"
17 #include "nsUnicodeRange.h"
18 #include "nsUnicodeProperties.h"
19
20 #include "mozilla/Attributes.h"
21 #include "mozilla/Likely.h"
22 #include "mozilla/MemoryReporting.h"
23 #include "mozilla/Preferences.h"
24 #include "mozilla/Telemetry.h"
25 #include "mozilla/TimeStamp.h"
26 #include "mozilla/gfx/2D.h"
27
28 #include <locale.h>
29
30 using namespace mozilla;
31
32 #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
33 LogLevel::Debug, args)
34 #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
35 gfxPlatform::GetLog(eGfxLog_fontlist), \
36 LogLevel::Debug)
37 #define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
38 LogLevel::Debug, args)
39 #define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
40 gfxPlatform::GetLog(eGfxLog_fontinit), \
41 LogLevel::Debug)
42
43 gfxPlatformFontList *gfxPlatformFontList::sPlatformFontList = nullptr;
44
45 // Character ranges that require complex-script shaping support in the font,
46 // and so should be masked out by ReadCMAP if the necessary layout tables
47 // are not present.
48 // Currently used by the Mac and FT2 implementations only, but probably should
49 // be supported on Windows as well.
50 const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = {
51 // Actually, now that harfbuzz supports presentation-forms shaping for
52 // Arabic, we can render it without layout tables. So maybe we don't
53 // want to mask the basic Arabic block here?
54 // This affects the arabic-fallback-*.html reftests, which rely on
55 // loading a font that *doesn't* have any GSUB table.
56 { 0x0600, 0x06FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
57 { 0x0700, 0x074F, { TRUETYPE_TAG('s','y','r','c'), 0, 0 } },
58 { 0x0750, 0x077F, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
59 { 0x08A0, 0x08FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
60 { 0x0900, 0x097F, { TRUETYPE_TAG('d','e','v','2'),
61 TRUETYPE_TAG('d','e','v','a'), 0 } },
62 { 0x0980, 0x09FF, { TRUETYPE_TAG('b','n','g','2'),
63 TRUETYPE_TAG('b','e','n','g'), 0 } },
64 { 0x0A00, 0x0A7F, { TRUETYPE_TAG('g','u','r','2'),
65 TRUETYPE_TAG('g','u','r','u'), 0 } },
66 { 0x0A80, 0x0AFF, { TRUETYPE_TAG('g','j','r','2'),
67 TRUETYPE_TAG('g','u','j','r'), 0 } },
68 { 0x0B00, 0x0B7F, { TRUETYPE_TAG('o','r','y','2'),
69 TRUETYPE_TAG('o','r','y','a'), 0 } },
70 { 0x0B80, 0x0BFF, { TRUETYPE_TAG('t','m','l','2'),
71 TRUETYPE_TAG('t','a','m','l'), 0 } },
72 { 0x0C00, 0x0C7F, { TRUETYPE_TAG('t','e','l','2'),
73 TRUETYPE_TAG('t','e','l','u'), 0 } },
74 { 0x0C80, 0x0CFF, { TRUETYPE_TAG('k','n','d','2'),
75 TRUETYPE_TAG('k','n','d','a'), 0 } },
76 { 0x0D00, 0x0D7F, { TRUETYPE_TAG('m','l','m','2'),
77 TRUETYPE_TAG('m','l','y','m'), 0 } },
78 { 0x0D80, 0x0DFF, { TRUETYPE_TAG('s','i','n','h'), 0, 0 } },
79 { 0x0E80, 0x0EFF, { TRUETYPE_TAG('l','a','o',' '), 0, 0 } },
80 { 0x0F00, 0x0FFF, { TRUETYPE_TAG('t','i','b','t'), 0, 0 } },
81 { 0x1000, 0x109f, { TRUETYPE_TAG('m','y','m','r'),
82 TRUETYPE_TAG('m','y','m','2'), 0 } },
83 { 0x1780, 0x17ff, { TRUETYPE_TAG('k','h','m','r'), 0, 0 } },
84 // Khmer Symbols (19e0..19ff) don't seem to need any special shaping
85 { 0xaa60, 0xaa7f, { TRUETYPE_TAG('m','y','m','r'),
86 TRUETYPE_TAG('m','y','m','2'), 0 } },
87 // Thai seems to be "renderable" without AAT morphing tables
88 { 0, 0, { 0, 0, 0 } } // terminator
89 };
90
91 // prefs for the font info loader
92 #define FONT_LOADER_FAMILIES_PER_SLICE_PREF "gfx.font_loader.families_per_slice"
93 #define FONT_LOADER_DELAY_PREF "gfx.font_loader.delay"
94 #define FONT_LOADER_INTERVAL_PREF "gfx.font_loader.interval"
95
96 static const char* kObservedPrefs[] = {
97 "font.",
98 "font.name-list.",
99 "intl.accept_languages", // hmmmm...
100 nullptr
101 };
102
103 static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
104
105 // xxx - this can probably be eliminated by reworking pref font handling code
106 static const char *gPrefLangNames[] = {
107 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
108 #include "gfxFontPrefLangList.h"
109 #undef FONT_PREF_LANG
110 };
111
112 static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
113 "size of pref lang name array doesn't match pref lang enum size");
114
115 class gfxFontListPrefObserver final : public nsIObserver {
~gfxFontListPrefObserver()116 ~gfxFontListPrefObserver() {}
117 public:
118 NS_DECL_ISUPPORTS
119 NS_DECL_NSIOBSERVER
120 };
121
122 static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
123
NS_IMPL_ISUPPORTS(gfxFontListPrefObserver,nsIObserver)124 NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
125
126 NS_IMETHODIMP
127 gfxFontListPrefObserver::Observe(nsISupports *aSubject,
128 const char *aTopic,
129 const char16_t *aData)
130 {
131 NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), "invalid topic");
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();
136 return NS_OK;
137 }
138
139 MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
140
NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter,nsIMemoryReporter)141 NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
142
143 NS_IMETHODIMP
144 gfxPlatformFontList::MemoryReporter::CollectReports(
145 nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
146 {
147 FontListSizes sizes;
148 sizes.mFontListSize = 0;
149 sizes.mFontTableCacheSize = 0;
150 sizes.mCharMapsSize = 0;
151
152 gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(&FontListMallocSizeOf,
153 &sizes);
154
155 MOZ_COLLECT_REPORT(
156 "explicit/gfx/font-list", KIND_HEAP, UNITS_BYTES,
157 sizes.mFontListSize,
158 "Memory used to manage the list of font families and faces.");
159
160 MOZ_COLLECT_REPORT(
161 "explicit/gfx/font-charmaps", KIND_HEAP, UNITS_BYTES,
162 sizes.mCharMapsSize,
163 "Memory used to record the character coverage of individual fonts.");
164
165 if (sizes.mFontTableCacheSize) {
166 MOZ_COLLECT_REPORT(
167 "explicit/gfx/font-tables", KIND_HEAP, UNITS_BYTES,
168 sizes.mFontTableCacheSize,
169 "Memory used for cached font metrics and layout tables.");
170 }
171
172 return NS_OK;
173 }
174
gfxPlatformFontList(bool aNeedFullnamePostscriptNames)175 gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
176 : mFontFamilies(64), mOtherFamilyNames(16),
177 mBadUnderlineFamilyNames(8), mSharedCmaps(8),
178 mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0),
179 mFontFamilyWhitelistActive(false)
180 {
181 mOtherFamilyNamesInitialized = false;
182
183 if (aNeedFullnamePostscriptNames) {
184 mExtraNames = MakeUnique<ExtraNames>();
185 }
186 mFaceNameListsInitialized = false;
187
188 LoadBadUnderlineList();
189
190 // pref changes notification setup
191 NS_ASSERTION(!gFontListPrefObserver,
192 "There has been font list pref observer already");
193 gFontListPrefObserver = new gfxFontListPrefObserver();
194 NS_ADDREF(gFontListPrefObserver);
195 Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
196
197 Preferences::RegisterCallback(FontWhitelistPrefChanged,
198 kFontSystemWhitelistPref);
199
200 RegisterStrongMemoryReporter(new MemoryReporter());
201 }
202
~gfxPlatformFontList()203 gfxPlatformFontList::~gfxPlatformFontList()
204 {
205 mSharedCmaps.Clear();
206 ClearLangGroupPrefFonts();
207 NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
208 Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
209 Preferences::UnregisterCallback(FontWhitelistPrefChanged,
210 kFontSystemWhitelistPref);
211 NS_RELEASE(gFontListPrefObserver);
212 }
213
214 // number of CSS generic font families
215 const uint32_t kNumGenerics = 5;
216
217 void
ApplyWhitelist()218 gfxPlatformFontList::ApplyWhitelist()
219 {
220 nsTArray<nsString> list;
221 gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, list);
222 uint32_t numFonts = list.Length();
223 mFontFamilyWhitelistActive = (numFonts > 0);
224 if (!mFontFamilyWhitelistActive) {
225 return;
226 }
227 nsTHashtable<nsStringHashKey> familyNamesWhitelist;
228 for (uint32_t i = 0; i < numFonts; i++) {
229 nsString key;
230 ToLowerCase(list[i], key);
231 familyNamesWhitelist.PutEntry(key);
232 }
233 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
234 // Don't continue if we only have one font left.
235 if (mFontFamilies.Count() == 1) {
236 break;
237 }
238 nsString fontFamilyName(iter.Key());
239 ToLowerCase(fontFamilyName);
240 if (!familyNamesWhitelist.Contains(fontFamilyName)) {
241 iter.Remove();
242 }
243 }
244 }
245
246 nsresult
InitFontList()247 gfxPlatformFontList::InitFontList()
248 {
249 mFontlistInitCount++;
250
251 if (LOG_FONTINIT_ENABLED()) {
252 LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
253 }
254
255 // rebuilding fontlist so clear out font/word caches
256 gfxFontCache *fontCache = gfxFontCache::GetCache();
257 if (fontCache) {
258 fontCache->AgeAllGenerations();
259 fontCache->FlushShapedWordCaches();
260 }
261
262 gfxPlatform::PurgeSkiaFontCache();
263
264 mFontFamilies.Clear();
265 mOtherFamilyNames.Clear();
266 mOtherFamilyNamesInitialized = false;
267 if (mExtraNames) {
268 mExtraNames->mFullnames.Clear();
269 mExtraNames->mPostscriptNames.Clear();
270 }
271 mFaceNameListsInitialized = false;
272 ClearLangGroupPrefFonts();
273 mReplacementCharFallbackFamily = nullptr;
274 CancelLoader();
275
276 // initialize ranges of characters for which system-wide font search should be skipped
277 mCodepointsWithNoFonts.reset();
278 mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
279 mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
280
281 sPlatformFontList = this;
282
283 nsresult rv = InitFontListForPlatform();
284 if (NS_FAILED(rv)) {
285 return rv;
286 }
287
288 ApplyWhitelist();
289 return NS_OK;
290 }
291
292 void
GenerateFontListKey(const nsAString & aKeyName,nsAString & aResult)293 gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
294 {
295 aResult = aKeyName;
296 ToLowerCase(aResult);
297 }
298
299 #define OTHERNAMES_TIMEOUT 200
300
301 void
InitOtherFamilyNames()302 gfxPlatformFontList::InitOtherFamilyNames()
303 {
304 if (mOtherFamilyNamesInitialized) {
305 return;
306 }
307
308 TimeStamp start = TimeStamp::Now();
309 bool timedOut = false;
310
311 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
312 RefPtr<gfxFontFamily>& family = iter.Data();
313 family->ReadOtherFamilyNames(this);
314 TimeDuration elapsed = TimeStamp::Now() - start;
315 if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
316 timedOut = true;
317 break;
318 }
319 }
320
321 if (!timedOut) {
322 mOtherFamilyNamesInitialized = true;
323 }
324 TimeStamp end = TimeStamp::Now();
325 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
326 start, end);
327
328 if (LOG_FONTINIT_ENABLED()) {
329 TimeDuration elapsed = end - start;
330 LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
331 elapsed.ToMilliseconds(),
332 (timedOut ? "timeout" : "")));
333 }
334 }
335
336 // time limit for loading facename lists (ms)
337 #define NAMELIST_TIMEOUT 200
338
339 gfxFontEntry*
SearchFamiliesForFaceName(const nsAString & aFaceName)340 gfxPlatformFontList::SearchFamiliesForFaceName(const nsAString& aFaceName)
341 {
342 TimeStamp start = TimeStamp::Now();
343 bool timedOut = false;
344 // if mFirstChar is not 0, only load facenames for families
345 // that start with this character
346 char16_t firstChar = 0;
347 gfxFontEntry *lookup = nullptr;
348
349 // iterate over familes starting with the same letter
350 firstChar = ToLowerCase(aFaceName.CharAt(0));
351
352 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
353 nsStringHashKey::KeyType key = iter.Key();
354 RefPtr<gfxFontFamily>& family = iter.Data();
355
356 // when filtering, skip names that don't start with the filter character
357 if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
358 continue;
359 }
360
361 family->ReadFaceNames(this, NeedFullnamePostscriptNames());
362
363 TimeDuration elapsed = TimeStamp::Now() - start;
364 if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
365 timedOut = true;
366 break;
367 }
368 }
369
370 lookup = FindFaceName(aFaceName);
371
372 TimeStamp end = TimeStamp::Now();
373 Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS,
374 start, end);
375 if (LOG_FONTINIT_ENABLED()) {
376 TimeDuration elapsed = end - start;
377 LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
378 elapsed.ToMilliseconds(),
379 (lookup ? "found name" : ""),
380 (timedOut ? "timeout" : "")));
381 }
382
383 return lookup;
384 }
385
386 gfxFontEntry*
FindFaceName(const nsAString & aFaceName)387 gfxPlatformFontList::FindFaceName(const nsAString& aFaceName)
388 {
389 gfxFontEntry *lookup;
390
391 // lookup in name lookup tables, return null if not found
392 if (mExtraNames &&
393 ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
394 (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
395 return lookup;
396 }
397
398 return nullptr;
399 }
400
401 gfxFontEntry*
LookupInFaceNameLists(const nsAString & aFaceName)402 gfxPlatformFontList::LookupInFaceNameLists(const nsAString& aFaceName)
403 {
404 gfxFontEntry *lookup = nullptr;
405
406 // initialize facename lookup tables if needed
407 // note: this can terminate early or time out, in which case
408 // mFaceNameListsInitialized remains false
409 if (!mFaceNameListsInitialized) {
410 lookup = SearchFamiliesForFaceName(aFaceName);
411 if (lookup) {
412 return lookup;
413 }
414 }
415
416 // lookup in name lookup tables, return null if not found
417 if (!(lookup = FindFaceName(aFaceName))) {
418 // names not completely initialized, so keep track of lookup misses
419 if (!mFaceNameListsInitialized) {
420 if (!mFaceNamesMissed) {
421 mFaceNamesMissed = MakeUnique<nsTHashtable<nsStringHashKey>>(2);
422 }
423 mFaceNamesMissed->PutEntry(aFaceName);
424 }
425 }
426
427 return lookup;
428 }
429
430 void
PreloadNamesList()431 gfxPlatformFontList::PreloadNamesList()
432 {
433 AutoTArray<nsString, 10> preloadFonts;
434 gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
435
436 uint32_t numFonts = preloadFonts.Length();
437 for (uint32_t i = 0; i < numFonts; i++) {
438 nsAutoString key;
439 GenerateFontListKey(preloadFonts[i], key);
440
441 // only search canonical names!
442 gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
443 if (familyEntry) {
444 familyEntry->ReadOtherFamilyNames(this);
445 }
446 }
447
448 }
449
450 void
LoadBadUnderlineList()451 gfxPlatformFontList::LoadBadUnderlineList()
452 {
453 AutoTArray<nsString, 10> blacklist;
454 gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
455 uint32_t numFonts = blacklist.Length();
456 for (uint32_t i = 0; i < numFonts; i++) {
457 nsAutoString key;
458 GenerateFontListKey(blacklist[i], key);
459 mBadUnderlineFamilyNames.PutEntry(key);
460 }
461 }
462
463 void
UpdateFontList()464 gfxPlatformFontList::UpdateFontList()
465 {
466 InitFontList();
467 RebuildLocalFonts();
468 }
469
470 void
GetFontList(nsIAtom * aLangGroup,const nsACString & aGenericFamily,nsTArray<nsString> & aListOfFonts)471 gfxPlatformFontList::GetFontList(nsIAtom *aLangGroup,
472 const nsACString& aGenericFamily,
473 nsTArray<nsString>& aListOfFonts)
474 {
475 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
476 RefPtr<gfxFontFamily>& family = iter.Data();
477 // use the first variation for now. This data should be the same
478 // for all the variations and should probably be moved up to
479 // the Family
480 gfxFontStyle style;
481 style.language = aLangGroup;
482 bool needsBold;
483 RefPtr<gfxFontEntry> fontEntry = family->FindFontForStyle(style, needsBold);
484 NS_ASSERTION(fontEntry, "couldn't find any font entry in family");
485 if (!fontEntry) {
486 continue;
487 }
488
489 /* skip symbol fonts */
490 if (fontEntry->IsSymbolFont()) {
491 continue;
492 }
493
494 if (fontEntry->SupportsLangGroup(aLangGroup) &&
495 fontEntry->MatchesGenericFamily(aGenericFamily)) {
496 nsAutoString localizedFamilyName;
497 family->LocalizedName(localizedFamilyName);
498 aListOfFonts.AppendElement(localizedFamilyName);
499 }
500 }
501
502 aListOfFonts.Sort();
503 aListOfFonts.Compact();
504 }
505
506 void
GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily>> & aFamilyArray)507 gfxPlatformFontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
508 {
509 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
510 RefPtr<gfxFontFamily>& family = iter.Data();
511 aFamilyArray.AppendElement(family);
512 }
513 }
514
515 gfxFontEntry*
SystemFindFontForChar(uint32_t aCh,uint32_t aNextCh,Script aRunScript,const gfxFontStyle * aStyle)516 gfxPlatformFontList::SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
517 Script aRunScript,
518 const gfxFontStyle* aStyle)
519 {
520 gfxFontEntry* fontEntry = nullptr;
521
522 // is codepoint with no matching font? return null immediately
523 if (mCodepointsWithNoFonts.test(aCh)) {
524 return nullptr;
525 }
526
527 // Try to short-circuit font fallback for U+FFFD, used to represent
528 // encoding errors: just use cached family from last time U+FFFD was seen.
529 // This helps speed up pages with lots of encoding errors, binary-as-text,
530 // etc.
531 if (aCh == 0xFFFD && mReplacementCharFallbackFamily) {
532 bool needsBold; // ignored in the system fallback case
533
534 fontEntry =
535 mReplacementCharFallbackFamily->FindFontForStyle(*aStyle,
536 needsBold);
537
538 // this should never fail, as we must have found U+FFFD in order to set
539 // mReplacementCharFallbackFamily at all, but better play it safe
540 if (fontEntry && fontEntry->HasCharacter(aCh)) {
541 return fontEntry;
542 }
543 }
544
545 TimeStamp start = TimeStamp::Now();
546
547 // search commonly available fonts
548 bool common = true;
549 gfxFontFamily *fallbackFamily = nullptr;
550 fontEntry = CommonFontFallback(aCh, aNextCh, aRunScript, aStyle,
551 &fallbackFamily);
552
553 // if didn't find a font, do system-wide fallback (except for specials)
554 uint32_t cmapCount = 0;
555 if (!fontEntry) {
556 common = false;
557 fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount,
558 &fallbackFamily);
559 }
560 TimeDuration elapsed = TimeStamp::Now() - start;
561
562 LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
563
564 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
565 uint32_t unicodeRange = FindCharUnicodeRange(aCh);
566 Script script = mozilla::unicode::GetScriptCode(aCh);
567 MOZ_LOG(log, LogLevel::Warning,\
568 ("(textrun-systemfallback-%s) char: u+%6.6x "
569 "unicode-range: %d script: %d match: [%s]"
570 " time: %dus cmaps: %d\n",
571 (common ? "common" : "global"), aCh,
572 unicodeRange, script,
573 (fontEntry ? NS_ConvertUTF16toUTF8(fontEntry->Name()).get() :
574 "<none>"),
575 int32_t(elapsed.ToMicroseconds()),
576 cmapCount));
577 }
578
579 // no match? add to set of non-matching codepoints
580 if (!fontEntry) {
581 mCodepointsWithNoFonts.set(aCh);
582 } else if (aCh == 0xFFFD && fontEntry && fallbackFamily) {
583 mReplacementCharFallbackFamily = fallbackFamily;
584 }
585
586 // track system fallback time
587 static bool first = true;
588 int32_t intElapsed = int32_t(first ? elapsed.ToMilliseconds() :
589 elapsed.ToMicroseconds());
590 Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST :
591 Telemetry::SYSTEM_FONT_FALLBACK),
592 intElapsed);
593 first = false;
594
595 // track the script for which fallback occurred (incremented one make it
596 // 1-based)
597 Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT,
598 int(aRunScript) + 1);
599
600 return fontEntry;
601 }
602
603 #define NUM_FALLBACK_FONTS 8
604
605 gfxFontEntry*
CommonFontFallback(uint32_t aCh,uint32_t aNextCh,Script aRunScript,const gfxFontStyle * aMatchStyle,gfxFontFamily ** aMatchedFamily)606 gfxPlatformFontList::CommonFontFallback(uint32_t aCh, uint32_t aNextCh,
607 Script aRunScript,
608 const gfxFontStyle* aMatchStyle,
609 gfxFontFamily** aMatchedFamily)
610 {
611 AutoTArray<const char*,NUM_FALLBACK_FONTS> defaultFallbacks;
612 uint32_t i, numFallbacks;
613
614 gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aNextCh,
615 aRunScript,
616 defaultFallbacks);
617 numFallbacks = defaultFallbacks.Length();
618 for (i = 0; i < numFallbacks; i++) {
619 nsAutoString familyName;
620 const char *fallbackFamily = defaultFallbacks[i];
621
622 familyName.AppendASCII(fallbackFamily);
623 gfxFontFamily *fallback = FindFamilyByCanonicalName(familyName);
624 if (!fallback)
625 continue;
626
627 gfxFontEntry *fontEntry;
628 bool needsBold; // ignored in the system fallback case
629
630 // use first font in list that supports a given character
631 fontEntry = fallback->FindFontForStyle(*aMatchStyle, needsBold);
632 if (fontEntry && fontEntry->HasCharacter(aCh)) {
633 *aMatchedFamily = fallback;
634 return fontEntry;
635 }
636 }
637
638 return nullptr;
639 }
640
641 gfxFontEntry*
GlobalFontFallback(const uint32_t aCh,Script aRunScript,const gfxFontStyle * aMatchStyle,uint32_t & aCmapCount,gfxFontFamily ** aMatchedFamily)642 gfxPlatformFontList::GlobalFontFallback(const uint32_t aCh,
643 Script aRunScript,
644 const gfxFontStyle* aMatchStyle,
645 uint32_t& aCmapCount,
646 gfxFontFamily** aMatchedFamily)
647 {
648 bool useCmaps = IsFontFamilyWhitelistActive() ||
649 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
650 if (!useCmaps) {
651 // Allow platform-specific fallback code to try and find a usable font
652 gfxFontEntry* fe =
653 PlatformGlobalFontFallback(aCh, aRunScript, aMatchStyle,
654 aMatchedFamily);
655 if (fe) {
656 return fe;
657 }
658 }
659
660 // otherwise, try to find it among local fonts
661 GlobalFontMatch data(aCh, aRunScript, aMatchStyle);
662
663 // iterate over all font families to find a font that support the character
664 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
665 RefPtr<gfxFontFamily>& family = iter.Data();
666 // evaluate all fonts in this family for a match
667 family->FindFontForChar(&data);
668 }
669
670 aCmapCount = data.mCmapsTested;
671 *aMatchedFamily = data.mMatchedFamily;
672
673 return data.mBestMatch;
674 }
675
676 gfxFontFamily*
CheckFamily(gfxFontFamily * aFamily)677 gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily)
678 {
679 if (aFamily && !aFamily->HasStyles()) {
680 aFamily->FindStyleVariations();
681 aFamily->CheckForSimpleFamily();
682 }
683
684 if (aFamily && aFamily->GetFontList().Length() == 0) {
685 // failed to load any faces for this family, so discard it
686 nsAutoString key;
687 GenerateFontListKey(aFamily->Name(), key);
688 mFontFamilies.Remove(key);
689 return nullptr;
690 }
691
692 return aFamily;
693 }
694
695 bool
FindAndAddFamilies(const nsAString & aFamily,nsTArray<gfxFontFamily * > * aOutput,gfxFontStyle * aStyle,gfxFloat aDevToCssSize)696 gfxPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
697 nsTArray<gfxFontFamily*>* aOutput,
698 gfxFontStyle* aStyle,
699 gfxFloat aDevToCssSize)
700 {
701 nsAutoString key;
702 GenerateFontListKey(aFamily, key);
703
704 NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
705
706 // lookup in canonical (i.e. English) family name list
707 gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
708
709 // if not found, lookup in other family names list (mostly localized names)
710 if (!familyEntry) {
711 familyEntry = mOtherFamilyNames.GetWeak(key);
712 }
713
714 // if still not found and other family names not yet fully initialized,
715 // initialize the rest of the list and try again. this is done lazily
716 // since reading name table entries is expensive.
717 // although ASCII localized family names are possible they don't occur
718 // in practice so avoid pulling in names at startup
719 if (!familyEntry && !mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
720 InitOtherFamilyNames();
721 familyEntry = mOtherFamilyNames.GetWeak(key);
722 if (!familyEntry && !mOtherFamilyNamesInitialized) {
723 // localized family names load timed out, add name to list of
724 // names to check after localized names are loaded
725 if (!mOtherNamesMissed) {
726 mOtherNamesMissed = MakeUnique<nsTHashtable<nsStringHashKey>>(2);
727 }
728 mOtherNamesMissed->PutEntry(key);
729 }
730 }
731
732 familyEntry = CheckFamily(familyEntry);
733 if (familyEntry) {
734 aOutput->AppendElement(familyEntry);
735 return true;
736 }
737
738 return false;
739 }
740
741 gfxFontEntry*
FindFontForFamily(const nsAString & aFamily,const gfxFontStyle * aStyle,bool & aNeedsBold)742 gfxPlatformFontList::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold)
743 {
744 gfxFontFamily *familyEntry = FindFamily(aFamily);
745
746 aNeedsBold = false;
747
748 if (familyEntry)
749 return familyEntry->FindFontForStyle(*aStyle, aNeedsBold);
750
751 return nullptr;
752 }
753
754 void
AddOtherFamilyName(gfxFontFamily * aFamilyEntry,nsAString & aOtherFamilyName)755 gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName)
756 {
757 nsAutoString key;
758 GenerateFontListKey(aOtherFamilyName, key);
759
760 if (!mOtherFamilyNames.GetWeak(key)) {
761 mOtherFamilyNames.Put(key, aFamilyEntry);
762 LOG_FONTLIST(("(fontlist-otherfamily) canonical family: %s, "
763 "other family: %s\n",
764 NS_ConvertUTF16toUTF8(aFamilyEntry->Name()).get(),
765 NS_ConvertUTF16toUTF8(aOtherFamilyName).get()));
766 if (mBadUnderlineFamilyNames.Contains(key))
767 aFamilyEntry->SetBadUnderlineFamily();
768 }
769 }
770
771 void
AddFullname(gfxFontEntry * aFontEntry,nsAString & aFullname)772 gfxPlatformFontList::AddFullname(gfxFontEntry *aFontEntry, nsAString& aFullname)
773 {
774 if (!mExtraNames->mFullnames.GetWeak(aFullname)) {
775 mExtraNames->mFullnames.Put(aFullname, aFontEntry);
776 LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
777 NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(),
778 NS_ConvertUTF16toUTF8(aFullname).get()));
779 }
780 }
781
782 void
AddPostscriptName(gfxFontEntry * aFontEntry,nsAString & aPostscriptName)783 gfxPlatformFontList::AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName)
784 {
785 if (!mExtraNames->mPostscriptNames.GetWeak(aPostscriptName)) {
786 mExtraNames->mPostscriptNames.Put(aPostscriptName, aFontEntry);
787 LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
788 NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(),
789 NS_ConvertUTF16toUTF8(aPostscriptName).get()));
790 }
791 }
792
793 bool
GetStandardFamilyName(const nsAString & aFontName,nsAString & aFamilyName)794 gfxPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
795 {
796 aFamilyName.Truncate();
797 gfxFontFamily *ff = FindFamily(aFontName);
798 if (!ff) {
799 return false;
800 }
801 aFamilyName.Assign(ff->Name());
802 return true;
803 }
804
805 gfxCharacterMap*
FindCharMap(gfxCharacterMap * aCmap)806 gfxPlatformFontList::FindCharMap(gfxCharacterMap *aCmap)
807 {
808 aCmap->CalcHash();
809 gfxCharacterMap *cmap = AddCmap(aCmap);
810 cmap->mShared = true;
811 return cmap;
812 }
813
814 // add a cmap to the shared cmap set
815 gfxCharacterMap*
AddCmap(const gfxCharacterMap * aCharMap)816 gfxPlatformFontList::AddCmap(const gfxCharacterMap* aCharMap)
817 {
818 CharMapHashKey *found =
819 mSharedCmaps.PutEntry(const_cast<gfxCharacterMap*>(aCharMap));
820 return found->GetKey();
821 }
822
823 // remove the cmap from the shared cmap set
824 void
RemoveCmap(const gfxCharacterMap * aCharMap)825 gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap)
826 {
827 // skip lookups during teardown
828 if (mSharedCmaps.Count() == 0) {
829 return;
830 }
831
832 // cmap needs to match the entry *and* be the same ptr before removing
833 CharMapHashKey *found =
834 mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
835 if (found && found->GetKey() == aCharMap) {
836 mSharedCmaps.RemoveEntry(const_cast<gfxCharacterMap*>(aCharMap));
837 }
838 }
839
840 void
ResolveGenericFontNames(FontFamilyType aGenericType,eFontPrefLang aPrefLang,nsTArray<RefPtr<gfxFontFamily>> * aGenericFamilies)841 gfxPlatformFontList::ResolveGenericFontNames(
842 FontFamilyType aGenericType,
843 eFontPrefLang aPrefLang,
844 nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
845 {
846 const char* langGroupStr = GetPrefLangName(aPrefLang);
847 const char* generic = GetGenericName(aGenericType);
848
849 if (!generic) {
850 return;
851 }
852
853 AutoTArray<nsString,4> genericFamilies;
854
855 // load family for "font.name.generic.lang"
856 nsAutoCString prefFontName("font.name.");
857 prefFontName.Append(generic);
858 prefFontName.Append('.');
859 prefFontName.Append(langGroupStr);
860 gfxFontUtils::AppendPrefsFontList(prefFontName.get(), genericFamilies);
861
862 // load fonts for "font.name-list.generic.lang"
863 nsAutoCString prefFontListName("font.name-list.");
864 prefFontListName.Append(generic);
865 prefFontListName.Append('.');
866 prefFontListName.Append(langGroupStr);
867 gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies);
868
869 nsIAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
870 NS_ASSERTION(langGroup, "null lang group for pref lang");
871
872 // lookup and add platform fonts uniquely
873 for (const nsString& genericFamily : genericFamilies) {
874 gfxFontStyle style;
875 style.language = langGroup;
876 style.systemFont = false;
877 AutoTArray<gfxFontFamily*,10> families;
878 FindAndAddFamilies(genericFamily, &families, &style);
879 for (gfxFontFamily* f : families) {
880 if (!aGenericFamilies->Contains(f)) {
881 aGenericFamilies->AppendElement(f);
882 }
883 }
884 }
885
886 #if 0 // dump out generic mappings
887 printf("%s ===> ", prefFontName.get());
888 for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
889 if (k > 0) printf(", ");
890 printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]->Name()).get());
891 }
892 printf("\n");
893 #endif
894 }
895
896 nsTArray<RefPtr<gfxFontFamily>>*
GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,eFontPrefLang aPrefLang)897 gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
898 eFontPrefLang aPrefLang)
899 {
900 // treat -moz-fixed as monospace
901 if (aGenericType == eFamily_moz_fixed) {
902 aGenericType = eFamily_monospace;
903 }
904
905 PrefFontList* prefFonts =
906 mLangGroupPrefFonts[aPrefLang][aGenericType].get();
907 if (MOZ_UNLIKELY(!prefFonts)) {
908 prefFonts = new PrefFontList;
909 ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts);
910 mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts);
911 }
912 return prefFonts;
913 }
914
915 void
AddGenericFonts(mozilla::FontFamilyType aGenericType,nsIAtom * aLanguage,nsTArray<gfxFontFamily * > & aFamilyList)916 gfxPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
917 nsIAtom* aLanguage,
918 nsTArray<gfxFontFamily*>& aFamilyList)
919 {
920 // map lang ==> langGroup
921 nsIAtom* langGroup = GetLangGroup(aLanguage);
922
923 // langGroup ==> prefLang
924 eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
925
926 // lookup pref fonts
927 nsTArray<RefPtr<gfxFontFamily>>* prefFonts =
928 GetPrefFontsLangGroup(aGenericType, prefLang);
929
930 if (!prefFonts->IsEmpty()) {
931 aFamilyList.AppendElements(*prefFonts);
932 }
933 }
934
PrefLangToLangGroups(uint32_t aIndex)935 static nsIAtom* PrefLangToLangGroups(uint32_t aIndex)
936 {
937 // static array here avoids static constructor
938 static nsIAtom* gPrefLangToLangGroups[] = {
939 #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
940 #include "gfxFontPrefLangList.h"
941 #undef FONT_PREF_LANG
942 };
943
944 return aIndex < ArrayLength(gPrefLangToLangGroups)
945 ? gPrefLangToLangGroups[aIndex]
946 : nsGkAtoms::Unicode;
947 }
948
949 eFontPrefLang
GetFontPrefLangFor(const char * aLang)950 gfxPlatformFontList::GetFontPrefLangFor(const char* aLang)
951 {
952 if (!aLang || !aLang[0]) {
953 return eFontPrefLang_Others;
954 }
955 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
956 if (!PL_strcasecmp(gPrefLangNames[i], aLang)) {
957 return eFontPrefLang(i);
958 }
959 }
960 return eFontPrefLang_Others;
961 }
962
963 eFontPrefLang
GetFontPrefLangFor(nsIAtom * aLang)964 gfxPlatformFontList::GetFontPrefLangFor(nsIAtom *aLang)
965 {
966 if (!aLang)
967 return eFontPrefLang_Others;
968 nsAutoCString lang;
969 aLang->ToUTF8String(lang);
970 return GetFontPrefLangFor(lang.get());
971 }
972
973 nsIAtom*
GetLangGroupForPrefLang(eFontPrefLang aLang)974 gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang)
975 {
976 // the special CJK set pref lang should be resolved into separate
977 // calls to individual CJK pref langs before getting here
978 NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
979
980 return PrefLangToLangGroups(uint32_t(aLang));
981 }
982
983 const char*
GetPrefLangName(eFontPrefLang aLang)984 gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang)
985 {
986 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
987 return gPrefLangNames[uint32_t(aLang)];
988 }
989 return nullptr;
990 }
991
992 eFontPrefLang
GetFontPrefLangFor(uint8_t aUnicodeRange)993 gfxPlatformFontList::GetFontPrefLangFor(uint8_t aUnicodeRange)
994 {
995 switch (aUnicodeRange) {
996 case kRangeSetLatin: return eFontPrefLang_Western;
997 case kRangeCyrillic: return eFontPrefLang_Cyrillic;
998 case kRangeGreek: return eFontPrefLang_Greek;
999 case kRangeHebrew: return eFontPrefLang_Hebrew;
1000 case kRangeArabic: return eFontPrefLang_Arabic;
1001 case kRangeThai: return eFontPrefLang_Thai;
1002 case kRangeKorean: return eFontPrefLang_Korean;
1003 case kRangeJapanese: return eFontPrefLang_Japanese;
1004 case kRangeSChinese: return eFontPrefLang_ChineseCN;
1005 case kRangeTChinese: return eFontPrefLang_ChineseTW;
1006 case kRangeDevanagari: return eFontPrefLang_Devanagari;
1007 case kRangeTamil: return eFontPrefLang_Tamil;
1008 case kRangeArmenian: return eFontPrefLang_Armenian;
1009 case kRangeBengali: return eFontPrefLang_Bengali;
1010 case kRangeCanadian: return eFontPrefLang_Canadian;
1011 case kRangeEthiopic: return eFontPrefLang_Ethiopic;
1012 case kRangeGeorgian: return eFontPrefLang_Georgian;
1013 case kRangeGujarati: return eFontPrefLang_Gujarati;
1014 case kRangeGurmukhi: return eFontPrefLang_Gurmukhi;
1015 case kRangeKhmer: return eFontPrefLang_Khmer;
1016 case kRangeMalayalam: return eFontPrefLang_Malayalam;
1017 case kRangeOriya: return eFontPrefLang_Oriya;
1018 case kRangeTelugu: return eFontPrefLang_Telugu;
1019 case kRangeKannada: return eFontPrefLang_Kannada;
1020 case kRangeSinhala: return eFontPrefLang_Sinhala;
1021 case kRangeTibetan: return eFontPrefLang_Tibetan;
1022 case kRangeSetCJK: return eFontPrefLang_CJKSet;
1023 default: return eFontPrefLang_Others;
1024 }
1025 }
1026
1027 bool
IsLangCJK(eFontPrefLang aLang)1028 gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang)
1029 {
1030 switch (aLang) {
1031 case eFontPrefLang_Japanese:
1032 case eFontPrefLang_ChineseTW:
1033 case eFontPrefLang_ChineseCN:
1034 case eFontPrefLang_ChineseHK:
1035 case eFontPrefLang_Korean:
1036 case eFontPrefLang_CJKSet:
1037 return true;
1038 default:
1039 return false;
1040 }
1041 }
1042
1043 void
GetLangPrefs(eFontPrefLang aPrefLangs[],uint32_t & aLen,eFontPrefLang aCharLang,eFontPrefLang aPageLang)1044 gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
1045 {
1046 if (IsLangCJK(aCharLang)) {
1047 AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
1048 } else {
1049 AppendPrefLang(aPrefLangs, aLen, aCharLang);
1050 }
1051
1052 AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
1053 }
1054
1055 void
AppendCJKPrefLangs(eFontPrefLang aPrefLangs[],uint32_t & aLen,eFontPrefLang aCharLang,eFontPrefLang aPageLang)1056 gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
1057 {
1058 // prefer the lang specified by the page *if* CJK
1059 if (IsLangCJK(aPageLang)) {
1060 AppendPrefLang(aPrefLangs, aLen, aPageLang);
1061 }
1062
1063 // if not set up, set up the default CJK order, based on accept lang settings and locale
1064 if (mCJKPrefLangs.Length() == 0) {
1065
1066 // temp array
1067 eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
1068 uint32_t tempLen = 0;
1069
1070 // Add the CJK pref fonts from accept languages, the order should be same order
1071 nsAdoptingCString list = Preferences::GetLocalizedCString("intl.accept_languages");
1072 if (!list.IsEmpty()) {
1073 const char kComma = ',';
1074 const char *p, *p_end;
1075 list.BeginReading(p);
1076 list.EndReading(p_end);
1077 while (p < p_end) {
1078 while (nsCRT::IsAsciiSpace(*p)) {
1079 if (++p == p_end)
1080 break;
1081 }
1082 if (p == p_end)
1083 break;
1084 const char *start = p;
1085 while (++p != p_end && *p != kComma)
1086 /* nothing */ ;
1087 nsAutoCString lang(Substring(start, p));
1088 lang.CompressWhitespace(false, true);
1089 eFontPrefLang fpl = gfxPlatformFontList::GetFontPrefLangFor(lang.get());
1090 switch (fpl) {
1091 case eFontPrefLang_Japanese:
1092 case eFontPrefLang_Korean:
1093 case eFontPrefLang_ChineseCN:
1094 case eFontPrefLang_ChineseHK:
1095 case eFontPrefLang_ChineseTW:
1096 AppendPrefLang(tempPrefLangs, tempLen, fpl);
1097 break;
1098 default:
1099 break;
1100 }
1101 p++;
1102 }
1103 }
1104
1105 do { // to allow 'break' to abort this block if a call fails
1106 nsresult rv;
1107 nsCOMPtr<nsILocaleService> ls =
1108 do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
1109 if (NS_FAILED(rv))
1110 break;
1111
1112 nsCOMPtr<nsILocale> appLocale;
1113 rv = ls->GetApplicationLocale(getter_AddRefs(appLocale));
1114 if (NS_FAILED(rv))
1115 break;
1116
1117 nsString localeStr;
1118 rv = appLocale->
1119 GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr);
1120 if (NS_FAILED(rv))
1121 break;
1122
1123 const nsAString& lang = Substring(localeStr, 0, 2);
1124 if (lang.EqualsLiteral("ja")) {
1125 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
1126 } else if (lang.EqualsLiteral("zh")) {
1127 const nsAString& region = Substring(localeStr, 3, 2);
1128 if (region.EqualsLiteral("CN")) {
1129 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
1130 } else if (region.EqualsLiteral("TW")) {
1131 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
1132 } else if (region.EqualsLiteral("HK")) {
1133 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
1134 }
1135 } else if (lang.EqualsLiteral("ko")) {
1136 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
1137 }
1138 } while (0);
1139
1140 // last resort... (the order is same as old gfx.)
1141 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
1142 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
1143 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
1144 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
1145 AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
1146
1147 // copy into the cached array
1148 uint32_t j;
1149 for (j = 0; j < tempLen; j++) {
1150 mCJKPrefLangs.AppendElement(tempPrefLangs[j]);
1151 }
1152 }
1153
1154 // append in cached CJK langs
1155 uint32_t i, numCJKlangs = mCJKPrefLangs.Length();
1156
1157 for (i = 0; i < numCJKlangs; i++) {
1158 AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i]));
1159 }
1160
1161 }
1162
1163 void
AppendPrefLang(eFontPrefLang aPrefLangs[],uint32_t & aLen,eFontPrefLang aAddLang)1164 gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang)
1165 {
1166 if (aLen >= kMaxLenPrefLangList) return;
1167
1168 // make sure
1169 uint32_t i = 0;
1170 while (i < aLen && aPrefLangs[i] != aAddLang) {
1171 i++;
1172 }
1173
1174 if (i == aLen) {
1175 aPrefLangs[aLen] = aAddLang;
1176 aLen++;
1177 }
1178 }
1179
1180 mozilla::FontFamilyType
GetDefaultGeneric(eFontPrefLang aLang)1181 gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang)
1182 {
1183 // initialize lang group pref font defaults (i.e. serif/sans-serif)
1184 if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) {
1185 mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
1186 for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
1187 nsAutoCString prefDefaultFontType("font.default.");
1188 prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
1189 nsAdoptingCString serifOrSans =
1190 Preferences::GetCString(prefDefaultFontType.get());
1191 if (serifOrSans.EqualsLiteral("sans-serif")) {
1192 mDefaultGenericsLangGroup[i] = eFamily_sans_serif;
1193 } else {
1194 mDefaultGenericsLangGroup[i] = eFamily_serif;
1195 }
1196 }
1197 }
1198
1199 if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
1200 return mDefaultGenericsLangGroup[uint32_t(aLang)];
1201 }
1202 return eFamily_serif;
1203 }
1204
1205
1206 gfxFontFamily*
GetDefaultFont(const gfxFontStyle * aStyle)1207 gfxPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
1208 {
1209 gfxFontFamily* family = GetDefaultFontForPlatform(aStyle);
1210 if (family) {
1211 return family;
1212 }
1213 // Something has gone wrong and we were unable to retrieve a default font
1214 // from the platform. (Likely the whitelist has blocked all potential
1215 // default fonts.) As a last resort, we return the first font listed in
1216 // mFontFamilies.
1217 return mFontFamilies.Iter().Data();
1218 }
1219
1220 void
GetFontFamilyNames(nsTArray<nsString> & aFontFamilyNames)1221 gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
1222 {
1223 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1224 RefPtr<gfxFontFamily>& family = iter.Data();
1225 aFontFamilyNames.AppendElement(family->Name());
1226 }
1227 }
1228
1229 nsILanguageAtomService*
GetLangService()1230 gfxPlatformFontList::GetLangService()
1231 {
1232 if (!mLangService) {
1233 mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
1234 }
1235 NS_ASSERTION(mLangService, "no language service!");
1236 return mLangService;
1237 }
1238
1239 nsIAtom*
GetLangGroup(nsIAtom * aLanguage)1240 gfxPlatformFontList::GetLangGroup(nsIAtom* aLanguage)
1241 {
1242 // map lang ==> langGroup
1243 nsIAtom *langGroup = nullptr;
1244 if (aLanguage) {
1245 nsresult rv;
1246 nsILanguageAtomService* langService = GetLangService();
1247 langGroup = langService->GetLanguageGroup(aLanguage, &rv);
1248 }
1249 if (!langGroup) {
1250 langGroup = nsGkAtoms::Unicode;
1251 }
1252 return langGroup;
1253 }
1254
1255 /* static */ const char*
GetGenericName(FontFamilyType aGenericType)1256 gfxPlatformFontList::GetGenericName(FontFamilyType aGenericType)
1257 {
1258 static const char kGeneric_serif[] = "serif";
1259 static const char kGeneric_sans_serif[] = "sans-serif";
1260 static const char kGeneric_monospace[] = "monospace";
1261 static const char kGeneric_cursive[] = "cursive";
1262 static const char kGeneric_fantasy[] = "fantasy";
1263
1264 // type should be standard generic type at this point
1265 NS_ASSERTION(aGenericType >= eFamily_serif &&
1266 aGenericType <= eFamily_fantasy,
1267 "standard generic font family type required");
1268
1269 // map generic type to string
1270 const char *generic = nullptr;
1271 switch (aGenericType) {
1272 case eFamily_serif:
1273 generic = kGeneric_serif;
1274 break;
1275 case eFamily_sans_serif:
1276 generic = kGeneric_sans_serif;
1277 break;
1278 case eFamily_monospace:
1279 generic = kGeneric_monospace;
1280 break;
1281 case eFamily_cursive:
1282 generic = kGeneric_cursive;
1283 break;
1284 case eFamily_fantasy:
1285 generic = kGeneric_fantasy;
1286 break;
1287 default:
1288 break;
1289 }
1290
1291 return generic;
1292 }
1293
1294 // mapping of moz lang groups ==> default lang
1295 struct MozLangGroupData {
1296 nsIAtom* const& mozLangGroup;
1297 const char *defaultLang;
1298 };
1299
1300 const MozLangGroupData MozLangGroups[] = {
1301 { nsGkAtoms::x_western, "en" },
1302 { nsGkAtoms::x_cyrillic, "ru" },
1303 { nsGkAtoms::x_devanagari, "hi" },
1304 { nsGkAtoms::x_tamil, "ta" },
1305 { nsGkAtoms::x_armn, "hy" },
1306 { nsGkAtoms::x_beng, "bn" },
1307 { nsGkAtoms::x_cans, "iu" },
1308 { nsGkAtoms::x_ethi, "am" },
1309 { nsGkAtoms::x_geor, "ka" },
1310 { nsGkAtoms::x_gujr, "gu" },
1311 { nsGkAtoms::x_guru, "pa" },
1312 { nsGkAtoms::x_khmr, "km" },
1313 { nsGkAtoms::x_knda, "kn" },
1314 { nsGkAtoms::x_mlym, "ml" },
1315 { nsGkAtoms::x_orya, "or" },
1316 { nsGkAtoms::x_sinh, "si" },
1317 { nsGkAtoms::x_tamil, "ta" },
1318 { nsGkAtoms::x_telu, "te" },
1319 { nsGkAtoms::x_tibt, "bo" },
1320 { nsGkAtoms::Unicode, 0 }
1321 };
1322
1323 bool
TryLangForGroup(const nsACString & aOSLang,nsIAtom * aLangGroup,nsACString & aFcLang)1324 gfxPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
1325 nsIAtom* aLangGroup,
1326 nsACString& aFcLang)
1327 {
1328 // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
1329 // aOSLang is in the form "language[_territory][.codeset][@modifier]".
1330 // fontconfig takes languages in the form "language-territory".
1331 // nsILanguageAtomService takes languages in the form language-subtag,
1332 // where subtag may be a territory. fontconfig and nsILanguageAtomService
1333 // handle case-conversion for us.
1334 const char *pos, *end;
1335 aOSLang.BeginReading(pos);
1336 aOSLang.EndReading(end);
1337 aFcLang.Truncate();
1338 while (pos < end) {
1339 switch (*pos) {
1340 case '.':
1341 case '@':
1342 end = pos;
1343 break;
1344 case '_':
1345 aFcLang.Append('-');
1346 break;
1347 default:
1348 aFcLang.Append(*pos);
1349 }
1350 ++pos;
1351 }
1352
1353 nsILanguageAtomService* langService = GetLangService();
1354 nsIAtom *atom = langService->LookupLanguage(aFcLang);
1355 return atom == aLangGroup;
1356 }
1357
1358 void
GetSampleLangForGroup(nsIAtom * aLanguage,nsACString & aLangStr,bool aCheckEnvironment)1359 gfxPlatformFontList::GetSampleLangForGroup(nsIAtom* aLanguage,
1360 nsACString& aLangStr,
1361 bool aCheckEnvironment)
1362 {
1363 aLangStr.Truncate();
1364 if (!aLanguage) {
1365 return;
1366 }
1367
1368 // set up lang string
1369 const MozLangGroupData *mozLangGroup = nullptr;
1370
1371 // -- look it up in the list of moz lang groups
1372 for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
1373 if (aLanguage == MozLangGroups[i].mozLangGroup) {
1374 mozLangGroup = &MozLangGroups[i];
1375 break;
1376 }
1377 }
1378
1379 // -- not a mozilla lang group? Just return the BCP47 string
1380 // representation of the lang group
1381 if (!mozLangGroup) {
1382 // Not a special mozilla language group.
1383 // Use aLanguage as a language code.
1384 aLanguage->ToUTF8String(aLangStr);
1385 return;
1386 }
1387
1388 // -- check the environment for the user's preferred language that
1389 // corresponds to this mozilla lang group.
1390 if (aCheckEnvironment) {
1391 const char *languages = getenv("LANGUAGE");
1392 if (languages) {
1393 const char separator = ':';
1394
1395 for (const char *pos = languages; true; ++pos) {
1396 if (*pos == '\0' || *pos == separator) {
1397 if (languages < pos &&
1398 TryLangForGroup(Substring(languages, pos),
1399 aLanguage, aLangStr))
1400 return;
1401
1402 if (*pos == '\0')
1403 break;
1404
1405 languages = pos + 1;
1406 }
1407 }
1408 }
1409 const char *ctype = setlocale(LC_CTYPE, nullptr);
1410 if (ctype &&
1411 TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr)) {
1412 return;
1413 }
1414 }
1415
1416 if (mozLangGroup->defaultLang) {
1417 aLangStr.Assign(mozLangGroup->defaultLang);
1418 } else {
1419 aLangStr.Truncate();
1420 }
1421 }
1422
1423 void
InitLoader()1424 gfxPlatformFontList::InitLoader()
1425 {
1426 GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
1427 mStartIndex = 0;
1428 mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
1429 memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
1430 }
1431
1432 #define FONT_LOADER_MAX_TIMESLICE 100 // max time for one pass through RunLoader = 100ms
1433
1434 bool
LoadFontInfo()1435 gfxPlatformFontList::LoadFontInfo()
1436 {
1437 TimeStamp start = TimeStamp::Now();
1438 uint32_t i, endIndex = mNumFamilies;
1439 bool loadCmaps = !UsesSystemFallback() ||
1440 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1441
1442 // for each font family, load in various font info
1443 for (i = mStartIndex; i < endIndex; i++) {
1444 nsAutoString key;
1445 gfxFontFamily *familyEntry;
1446 GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
1447
1448 // lookup in canonical (i.e. English) family name list
1449 if (!(familyEntry = mFontFamilies.GetWeak(key))) {
1450 continue;
1451 }
1452
1453 // read in face names
1454 familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(), mFontInfo);
1455
1456 // load the cmaps if needed
1457 if (loadCmaps) {
1458 familyEntry->ReadAllCMAPs(mFontInfo);
1459 }
1460
1461 // limit the time spent reading fonts in one pass
1462 TimeDuration elapsed = TimeStamp::Now() - start;
1463 if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
1464 i + 1 != endIndex) {
1465 endIndex = i + 1;
1466 break;
1467 }
1468 }
1469
1470 mStartIndex = endIndex;
1471 bool done = mStartIndex >= mNumFamilies;
1472
1473 if (LOG_FONTINIT_ENABLED()) {
1474 TimeDuration elapsed = TimeStamp::Now() - start;
1475 LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
1476 elapsed.ToMilliseconds(), (done ? "true" : "false")));
1477 }
1478
1479 if (done) {
1480 mOtherFamilyNamesInitialized = true;
1481 mFaceNameListsInitialized = true;
1482 }
1483
1484 return done;
1485 }
1486
1487 void
CleanupLoader()1488 gfxPlatformFontList::CleanupLoader()
1489 {
1490 mFontFamiliesToLoad.Clear();
1491 mNumFamilies = 0;
1492 bool rebuilt = false, forceReflow = false;
1493
1494 // if had missed face names that are now available, force reflow all
1495 if (mFaceNamesMissed) {
1496 for (auto it = mFaceNamesMissed->Iter(); !it.Done(); it.Next()) {
1497 if (FindFaceName(it.Get()->GetKey())) {
1498 rebuilt = true;
1499 RebuildLocalFonts();
1500 break;
1501 }
1502 }
1503 mFaceNamesMissed = nullptr;
1504 }
1505
1506 if (mOtherNamesMissed) {
1507 for (auto it = mOtherNamesMissed->Iter(); !it.Done(); it.Next()) {
1508 if (FindFamily(it.Get()->GetKey())) {
1509 forceReflow = true;
1510 ForceGlobalReflow();
1511 break;
1512 }
1513 }
1514 mOtherNamesMissed = nullptr;
1515 }
1516
1517 if (LOG_FONTINIT_ENABLED() && mFontInfo) {
1518 LOG_FONTINIT(("(fontinit) fontloader load thread took %8.2f ms "
1519 "%d families %d fonts %d cmaps "
1520 "%d facenames %d othernames %s %s",
1521 mLoadTime.ToMilliseconds(),
1522 mFontInfo->mLoadStats.families,
1523 mFontInfo->mLoadStats.fonts,
1524 mFontInfo->mLoadStats.cmaps,
1525 mFontInfo->mLoadStats.facenames,
1526 mFontInfo->mLoadStats.othernames,
1527 (rebuilt ? "(userfont sets rebuilt)" : ""),
1528 (forceReflow ? "(global reflow)" : "")));
1529 }
1530
1531 gfxFontInfoLoader::CleanupLoader();
1532 }
1533
1534 void
GetPrefsAndStartLoader()1535 gfxPlatformFontList::GetPrefsAndStartLoader()
1536 {
1537 mIncrement =
1538 std::max(1u, Preferences::GetUint(FONT_LOADER_FAMILIES_PER_SLICE_PREF));
1539
1540 uint32_t delay =
1541 std::max(1u, Preferences::GetUint(FONT_LOADER_DELAY_PREF));
1542 uint32_t interval =
1543 std::max(1u, Preferences::GetUint(FONT_LOADER_INTERVAL_PREF));
1544
1545 StartLoader(delay, interval);
1546 }
1547
1548 void
ForceGlobalReflow()1549 gfxPlatformFontList::ForceGlobalReflow()
1550 {
1551 // modify a preference that will trigger reflow everywhere
1552 static const char kPrefName[] = "font.internaluseonly.changed";
1553 bool fontInternalChange = Preferences::GetBool(kPrefName, false);
1554 Preferences::SetBool(kPrefName, !fontInternalChange);
1555 }
1556
1557 void
RebuildLocalFonts()1558 gfxPlatformFontList::RebuildLocalFonts()
1559 {
1560 for (auto it = mUserFontSetList.Iter(); !it.Done(); it.Next()) {
1561 it.Get()->GetKey()->RebuildLocalRules();
1562 }
1563 }
1564
1565 void
ClearLangGroupPrefFonts()1566 gfxPlatformFontList::ClearLangGroupPrefFonts()
1567 {
1568 for (uint32_t i = eFontPrefLang_First;
1569 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
1570 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
1571 for (uint32_t j = eFamily_generic_first;
1572 j < eFamily_generic_first + eFamily_generic_count; j++) {
1573 prefFontsLangGroup[j] = nullptr;
1574 }
1575 }
1576 }
1577
1578 // Support for memory reporting
1579
1580 // this is also used by subclasses that hold additional font tables
1581 /*static*/ size_t
SizeOfFontFamilyTableExcludingThis(const FontFamilyTable & aTable,MallocSizeOf aMallocSizeOf)1582 gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis(
1583 const FontFamilyTable& aTable,
1584 MallocSizeOf aMallocSizeOf)
1585 {
1586 size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
1587 for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
1588 // We don't count the size of the family here, because this is an
1589 // *extra* reference to a family that will have already been counted in
1590 // the main list.
1591 n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1592 }
1593 return n;
1594 }
1595
1596 /*static*/ size_t
SizeOfFontEntryTableExcludingThis(const FontEntryTable & aTable,MallocSizeOf aMallocSizeOf)1597 gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
1598 const FontEntryTable& aTable,
1599 MallocSizeOf aMallocSizeOf)
1600 {
1601 size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
1602 for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
1603 // The font itself is counted by its owning family; here we only care
1604 // about the names stored in the hashtable keys.
1605 n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1606 }
1607 return n;
1608 }
1609
1610 void
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const1611 gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
1612 FontListSizes* aSizes) const
1613 {
1614 aSizes->mFontListSize +=
1615 mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
1616 for (auto iter = mFontFamilies.ConstIter(); !iter.Done(); iter.Next()) {
1617 aSizes->mFontListSize +=
1618 iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1619 iter.Data()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
1620 }
1621
1622 aSizes->mFontListSize +=
1623 SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
1624
1625 if (mExtraNames) {
1626 aSizes->mFontListSize +=
1627 SizeOfFontEntryTableExcludingThis(mExtraNames->mFullnames,
1628 aMallocSizeOf);
1629 aSizes->mFontListSize +=
1630 SizeOfFontEntryTableExcludingThis(mExtraNames->mPostscriptNames,
1631 aMallocSizeOf);
1632 }
1633
1634 for (uint32_t i = eFontPrefLang_First;
1635 i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
1636 auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
1637 for (uint32_t j = eFamily_generic_first;
1638 j < eFamily_generic_first + eFamily_generic_count; j++) {
1639 PrefFontList* pf = prefFontsLangGroup[j].get();
1640 if (pf) {
1641 aSizes->mFontListSize +=
1642 pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
1643 }
1644 }
1645 }
1646
1647 aSizes->mFontListSize +=
1648 mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf);
1649 aSizes->mFontListSize +=
1650 mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
1651
1652 aSizes->mFontListSize +=
1653 mBadUnderlineFamilyNames.SizeOfExcludingThis(aMallocSizeOf);
1654
1655 aSizes->mFontListSize +=
1656 mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
1657 for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
1658 aSizes->mCharMapsSize +=
1659 iter.Get()->GetKey()->SizeOfIncludingThis(aMallocSizeOf);
1660 }
1661 }
1662
1663 void
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const1664 gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
1665 FontListSizes* aSizes) const
1666 {
1667 aSizes->mFontListSize += aMallocSizeOf(this);
1668 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
1669 }
1670
1671 bool
IsFontFamilyWhitelistActive()1672 gfxPlatformFontList::IsFontFamilyWhitelistActive()
1673 {
1674 return mFontFamilyWhitelistActive;
1675 }
1676
1677 #undef LOG
1678 #undef LOG_ENABLED
1679