/src/mozilla-central/gfx/thebes/gfxPlatformFontList.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "mozilla/intl/LocaleService.h" |
8 | | #include "mozilla/intl/MozLocale.h" |
9 | | #include "mozilla/intl/OSPreferences.h" |
10 | | |
11 | | #include "gfxPlatformFontList.h" |
12 | | #include "gfxTextRun.h" |
13 | | #include "gfxUserFontSet.h" |
14 | | |
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" |
22 | | |
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" |
32 | | |
33 | | #include <locale.h> |
34 | | |
35 | | using namespace mozilla; |
36 | | using mozilla::intl::LocaleService; |
37 | | using mozilla::intl::Locale; |
38 | | using mozilla::intl::OSPreferences; |
39 | | |
40 | 0 | #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ |
41 | 0 | LogLevel::Debug, args) |
42 | | #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \ |
43 | | gfxPlatform::GetLog(eGfxLog_fontlist), \ |
44 | | LogLevel::Debug) |
45 | 0 | #define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \ |
46 | 0 | LogLevel::Debug, args) |
47 | 0 | #define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \ |
48 | 0 | gfxPlatform::GetLog(eGfxLog_fontinit), \ |
49 | 0 | LogLevel::Debug) |
50 | | |
51 | | gfxPlatformFontList *gfxPlatformFontList::sPlatformFontList = nullptr; |
52 | | |
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 |
97 | | }; |
98 | | |
99 | | // prefs for the font info loader |
100 | 0 | #define FONT_LOADER_DELAY_PREF "gfx.font_loader.delay" |
101 | 0 | #define FONT_LOADER_INTERVAL_PREF "gfx.font_loader.interval" |
102 | | |
103 | | static const char* kObservedPrefs[] = { |
104 | | "font.", |
105 | | "font.name-list.", |
106 | | "intl.accept_languages", // hmmmm... |
107 | | nullptr |
108 | | }; |
109 | | |
110 | | static const char kFontSystemWhitelistPref[] = "font.system.whitelist"; |
111 | | |
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 |
117 | | }; |
118 | | |
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"); |
121 | | |
122 | | class gfxFontListPrefObserver final : public nsIObserver { |
123 | 0 | ~gfxFontListPrefObserver() {} |
124 | | public: |
125 | | NS_DECL_ISUPPORTS |
126 | | NS_DECL_NSIOBSERVER |
127 | | }; |
128 | | |
129 | | static void |
130 | | FontListPrefChanged(const char* aPref, void* aData = nullptr) |
131 | 0 | { |
132 | 0 | // XXX this could be made to only clear out the cache for the prefs that were changed |
133 | 0 | // but it probably isn't that big a deal. |
134 | 0 | gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts(); |
135 | 0 | gfxFontCache::GetCache()->AgeAllGenerations(); |
136 | 0 | } |
137 | | |
138 | | static gfxFontListPrefObserver* gFontListPrefObserver = nullptr; |
139 | | |
140 | | NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver) |
141 | | |
142 | 0 | #define LOCALES_CHANGED_TOPIC "intl:system-locales-changed" |
143 | | |
144 | | NS_IMETHODIMP |
145 | | gfxFontListPrefObserver::Observe(nsISupports *aSubject, |
146 | | const char *aTopic, |
147 | | const char16_t *aData) |
148 | 0 | { |
149 | 0 | NS_ASSERTION(!strcmp(aTopic, LOCALES_CHANGED_TOPIC), "invalid topic"); |
150 | 0 | FontListPrefChanged(nullptr); |
151 | 0 |
|
152 | 0 | if (XRE_IsParentProcess()) { |
153 | 0 | gfxPlatform::ForceGlobalReflow(); |
154 | 0 | } |
155 | 0 | return NS_OK; |
156 | 0 | } |
157 | | |
158 | | MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf) |
159 | | |
160 | | NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter) |
161 | | |
162 | | NS_IMETHODIMP |
163 | | gfxPlatformFontList::MemoryReporter::CollectReports( |
164 | | nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) |
165 | 0 | { |
166 | 0 | FontListSizes sizes; |
167 | 0 | sizes.mFontListSize = 0; |
168 | 0 | sizes.mFontTableCacheSize = 0; |
169 | 0 | sizes.mCharMapsSize = 0; |
170 | 0 | sizes.mLoaderSize = 0; |
171 | 0 |
|
172 | 0 | gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(&FontListMallocSizeOf, |
173 | 0 | &sizes); |
174 | 0 |
|
175 | 0 | MOZ_COLLECT_REPORT( |
176 | 0 | "explicit/gfx/font-list", KIND_HEAP, UNITS_BYTES, |
177 | 0 | sizes.mFontListSize, |
178 | 0 | "Memory used to manage the list of font families and faces."); |
179 | 0 |
|
180 | 0 | MOZ_COLLECT_REPORT( |
181 | 0 | "explicit/gfx/font-charmaps", KIND_HEAP, UNITS_BYTES, |
182 | 0 | sizes.mCharMapsSize, |
183 | 0 | "Memory used to record the character coverage of individual fonts."); |
184 | 0 |
|
185 | 0 | if (sizes.mFontTableCacheSize) { |
186 | 0 | MOZ_COLLECT_REPORT( |
187 | 0 | "explicit/gfx/font-tables", KIND_HEAP, UNITS_BYTES, |
188 | 0 | sizes.mFontTableCacheSize, |
189 | 0 | "Memory used for cached font metrics and layout tables."); |
190 | 0 | } |
191 | 0 |
|
192 | 0 | if (sizes.mLoaderSize) { |
193 | 0 | MOZ_COLLECT_REPORT( |
194 | 0 | "explicit/gfx/font-loader", KIND_HEAP, UNITS_BYTES, |
195 | 0 | sizes.mLoaderSize, |
196 | 0 | "Memory used for (platform-specific) font loader."); |
197 | 0 | } |
198 | 0 |
|
199 | 0 | return NS_OK; |
200 | 0 | } |
201 | | |
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) |
207 | 0 | { |
208 | 0 | mOtherFamilyNamesInitialized = false; |
209 | 0 |
|
210 | 0 | if (aNeedFullnamePostscriptNames) { |
211 | 0 | mExtraNames = MakeUnique<ExtraNames>(); |
212 | 0 | } |
213 | 0 | mFaceNameListsInitialized = false; |
214 | 0 |
|
215 | 0 | mLangService = nsLanguageAtomService::GetService(); |
216 | 0 |
|
217 | 0 | LoadBadUnderlineList(); |
218 | 0 |
|
219 | 0 | // pref changes notification setup |
220 | 0 | NS_ASSERTION(!gFontListPrefObserver, |
221 | 0 | "There has been font list pref observer already"); |
222 | 0 | gFontListPrefObserver = new gfxFontListPrefObserver(); |
223 | 0 | NS_ADDREF(gFontListPrefObserver); |
224 | 0 |
|
225 | 0 | Preferences::RegisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs); |
226 | 0 |
|
227 | 0 | nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
228 | 0 | if (obs) { |
229 | 0 | obs->AddObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC, false); |
230 | 0 | } |
231 | 0 |
|
232 | 0 | // Only the parent process listens for whitelist changes; it will then |
233 | 0 | // notify its children to rebuild their font lists. |
234 | 0 | if (XRE_IsParentProcess()) { |
235 | 0 | Preferences::RegisterCallback(FontWhitelistPrefChanged, |
236 | 0 | kFontSystemWhitelistPref); |
237 | 0 | } |
238 | 0 |
|
239 | 0 | RegisterStrongMemoryReporter(new MemoryReporter()); |
240 | 0 | } |
241 | | |
242 | | gfxPlatformFontList::~gfxPlatformFontList() |
243 | 0 | { |
244 | 0 | mSharedCmaps.Clear(); |
245 | 0 | ClearLangGroupPrefFonts(); |
246 | 0 | NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer"); |
247 | 0 |
|
248 | 0 | Preferences::UnregisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs); |
249 | 0 |
|
250 | 0 | nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
251 | 0 | if (obs) { |
252 | 0 | obs->RemoveObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC); |
253 | 0 | } |
254 | 0 |
|
255 | 0 | if (XRE_IsParentProcess()) { |
256 | 0 | Preferences::UnregisterCallback(FontWhitelistPrefChanged, |
257 | 0 | kFontSystemWhitelistPref); |
258 | 0 | } |
259 | 0 | NS_RELEASE(gFontListPrefObserver); |
260 | 0 | } |
261 | | |
262 | | /* static */ |
263 | | void |
264 | | gfxPlatformFontList::FontWhitelistPrefChanged(const char *aPref, |
265 | | void *aClosure) |
266 | 0 | { |
267 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
268 | 0 | gfxPlatformFontList::PlatformFontList()->UpdateFontList(); |
269 | 0 | mozilla::dom::ContentParent::NotifyUpdatedFonts(); |
270 | 0 | } |
271 | | |
272 | | // number of CSS generic font families |
273 | | const uint32_t kNumGenerics = 5; |
274 | | |
275 | | void |
276 | | gfxPlatformFontList::ApplyWhitelist() |
277 | 0 | { |
278 | 0 | nsTArray<nsCString> list; |
279 | 0 | gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, list); |
280 | 0 | uint32_t numFonts = list.Length(); |
281 | 0 | mFontFamilyWhitelistActive = (numFonts > 0); |
282 | 0 | if (!mFontFamilyWhitelistActive) { |
283 | 0 | return; |
284 | 0 | } |
285 | 0 | nsTHashtable<nsCStringHashKey> familyNamesWhitelist; |
286 | 0 | for (uint32_t i = 0; i < numFonts; i++) { |
287 | 0 | nsAutoCString key; |
288 | 0 | ToLowerCase(list[i], key); |
289 | 0 | familyNamesWhitelist.PutEntry(key); |
290 | 0 | } |
291 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
292 | 0 | // Don't continue if we only have one font left. |
293 | 0 | if (mFontFamilies.Count() == 1) { |
294 | 0 | break; |
295 | 0 | } |
296 | 0 | nsAutoCString fontFamilyName(iter.Key()); |
297 | 0 | ToLowerCase(fontFamilyName); |
298 | 0 | if (!familyNamesWhitelist.Contains(fontFamilyName)) { |
299 | 0 | iter.Remove(); |
300 | 0 | } |
301 | 0 | } |
302 | 0 | } |
303 | | |
304 | | bool |
305 | | gfxPlatformFontList::AddWithLegacyFamilyName(const nsACString& aLegacyName, |
306 | | gfxFontEntry* aFontEntry) |
307 | 0 | { |
308 | 0 | bool added = false; |
309 | 0 | nsAutoCString key; |
310 | 0 | ToLowerCase(aLegacyName, key); |
311 | 0 | gfxFontFamily* family = mOtherFamilyNames.GetWeak(key); |
312 | 0 | if (!family) { |
313 | 0 | family = CreateFontFamily(aLegacyName); |
314 | 0 | family->SetHasStyles(true); // we don't want the family to search for |
315 | 0 | // faces, we're adding them directly here |
316 | 0 | mOtherFamilyNames.Put(key, family); |
317 | 0 | added = true; |
318 | 0 | } |
319 | 0 | family->AddFontEntry(aFontEntry->Clone()); |
320 | 0 | return added; |
321 | 0 | } |
322 | | |
323 | | nsresult |
324 | | gfxPlatformFontList::InitFontList() |
325 | 0 | { |
326 | 0 | mFontlistInitCount++; |
327 | 0 |
|
328 | 0 | if (LOG_FONTINIT_ENABLED()) { |
329 | 0 | LOG_FONTINIT(("(fontinit) system fontlist initialization\n")); |
330 | 0 | } |
331 | 0 |
|
332 | 0 | // rebuilding fontlist so clear out font/word caches |
333 | 0 | gfxFontCache *fontCache = gfxFontCache::GetCache(); |
334 | 0 | if (fontCache) { |
335 | 0 | fontCache->AgeAllGenerations(); |
336 | 0 | fontCache->FlushShapedWordCaches(); |
337 | 0 | } |
338 | 0 |
|
339 | 0 | gfxPlatform::PurgeSkiaFontCache(); |
340 | 0 |
|
341 | 0 | CancelInitOtherFamilyNamesTask(); |
342 | 0 | MutexAutoLock lock(mFontFamiliesMutex); |
343 | 0 | mFontFamilies.Clear(); |
344 | 0 | mOtherFamilyNames.Clear(); |
345 | 0 | mOtherFamilyNamesInitialized = false; |
346 | 0 |
|
347 | 0 | if (mExtraNames) { |
348 | 0 | mExtraNames->mFullnames.Clear(); |
349 | 0 | mExtraNames->mPostscriptNames.Clear(); |
350 | 0 | } |
351 | 0 | mFaceNameListsInitialized = false; |
352 | 0 | ClearLangGroupPrefFonts(); |
353 | 0 | mReplacementCharFallbackFamily = nullptr; |
354 | 0 | CancelLoader(); |
355 | 0 |
|
356 | 0 | // initialize ranges of characters for which system-wide font search should be skipped |
357 | 0 | mCodepointsWithNoFonts.reset(); |
358 | 0 | mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls |
359 | 0 | mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls |
360 | 0 |
|
361 | 0 | sPlatformFontList = this; |
362 | 0 |
|
363 | 0 | nsresult rv = InitFontListForPlatform(); |
364 | 0 | if (NS_FAILED(rv)) { |
365 | 0 | return rv; |
366 | 0 | } |
367 | 0 | |
368 | 0 | ApplyWhitelist(); |
369 | 0 | return NS_OK; |
370 | 0 | } |
371 | | |
372 | | void |
373 | | gfxPlatformFontList::GenerateFontListKey(const nsACString& aKeyName, nsACString& aResult) |
374 | 0 | { |
375 | 0 | aResult = aKeyName; |
376 | 0 | ToLowerCase(aResult); |
377 | 0 | } |
378 | | |
379 | 0 | #define OTHERNAMES_TIMEOUT 200 |
380 | | |
381 | | void |
382 | | gfxPlatformFontList::InitOtherFamilyNames(bool aDeferOtherFamilyNamesLoading) |
383 | 0 | { |
384 | 0 | if (mOtherFamilyNamesInitialized) { |
385 | 0 | return; |
386 | 0 | } |
387 | 0 | |
388 | 0 | // If the font loader delay has been set to zero, we don't defer loading |
389 | 0 | // additional family names (regardless of the aDefer... parameter), as we |
390 | 0 | // take this to mean availability of font info is to be prioritized over |
391 | 0 | // potential startup perf or main-thread jank. |
392 | 0 | // (This is used so we can reliably run reftests that depend on localized |
393 | 0 | // font-family names being available.) |
394 | 0 | if (aDeferOtherFamilyNamesLoading && |
395 | 0 | Preferences::GetUint(FONT_LOADER_DELAY_PREF) > 0) { |
396 | 0 | if (!mPendingOtherFamilyNameTask) { |
397 | 0 | RefPtr<mozilla::CancelableRunnable> task = new InitOtherFamilyNamesRunnable(); |
398 | 0 | mPendingOtherFamilyNameTask = task; |
399 | 0 | NS_IdleDispatchToCurrentThread(task.forget()); |
400 | 0 | } |
401 | 0 | } else { |
402 | 0 | InitOtherFamilyNamesInternal(false); |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | | // time limit for loading facename lists (ms) |
407 | 0 | #define NAMELIST_TIMEOUT 200 |
408 | | |
409 | | gfxFontEntry* |
410 | | gfxPlatformFontList::SearchFamiliesForFaceName(const nsACString& aFaceName) |
411 | 0 | { |
412 | 0 | TimeStamp start = TimeStamp::Now(); |
413 | 0 | bool timedOut = false; |
414 | 0 | // if mFirstChar is not 0, only load facenames for families |
415 | 0 | // that start with this character |
416 | 0 | char16_t firstChar = 0; |
417 | 0 | gfxFontEntry *lookup = nullptr; |
418 | 0 |
|
419 | 0 | // iterate over familes starting with the same letter |
420 | 0 | firstChar = ToLowerCase(aFaceName.CharAt(0)); |
421 | 0 |
|
422 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
423 | 0 | nsCStringHashKey::KeyType key = iter.Key(); |
424 | 0 | RefPtr<gfxFontFamily>& family = iter.Data(); |
425 | 0 |
|
426 | 0 | // when filtering, skip names that don't start with the filter character |
427 | 0 | if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) { |
428 | 0 | continue; |
429 | 0 | } |
430 | 0 | |
431 | 0 | family->ReadFaceNames(this, NeedFullnamePostscriptNames()); |
432 | 0 |
|
433 | 0 | TimeDuration elapsed = TimeStamp::Now() - start; |
434 | 0 | if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) { |
435 | 0 | timedOut = true; |
436 | 0 | break; |
437 | 0 | } |
438 | 0 | } |
439 | 0 |
|
440 | 0 | lookup = FindFaceName(aFaceName); |
441 | 0 |
|
442 | 0 | TimeStamp end = TimeStamp::Now(); |
443 | 0 | Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS, |
444 | 0 | start, end); |
445 | 0 | if (LOG_FONTINIT_ENABLED()) { |
446 | 0 | TimeDuration elapsed = end - start; |
447 | 0 | LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s", |
448 | 0 | elapsed.ToMilliseconds(), |
449 | 0 | (lookup ? "found name" : ""), |
450 | 0 | (timedOut ? "timeout" : ""))); |
451 | 0 | } |
452 | 0 |
|
453 | 0 | return lookup; |
454 | 0 | } |
455 | | |
456 | | gfxFontEntry* |
457 | | gfxPlatformFontList::FindFaceName(const nsACString& aFaceName) |
458 | 0 | { |
459 | 0 | gfxFontEntry *lookup; |
460 | 0 |
|
461 | 0 | // lookup in name lookup tables, return null if not found |
462 | 0 | if (mExtraNames && |
463 | 0 | ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) || |
464 | 0 | (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) { |
465 | 0 | return lookup; |
466 | 0 | } |
467 | 0 | |
468 | 0 | return nullptr; |
469 | 0 | } |
470 | | |
471 | | gfxFontEntry* |
472 | | gfxPlatformFontList::LookupInFaceNameLists(const nsACString& aFaceName) |
473 | 0 | { |
474 | 0 | gfxFontEntry *lookup = nullptr; |
475 | 0 |
|
476 | 0 | // initialize facename lookup tables if needed |
477 | 0 | // note: this can terminate early or time out, in which case |
478 | 0 | // mFaceNameListsInitialized remains false |
479 | 0 | if (!mFaceNameListsInitialized) { |
480 | 0 | lookup = SearchFamiliesForFaceName(aFaceName); |
481 | 0 | if (lookup) { |
482 | 0 | return lookup; |
483 | 0 | } |
484 | 0 | } |
485 | 0 | |
486 | 0 | // lookup in name lookup tables, return null if not found |
487 | 0 | if (!(lookup = FindFaceName(aFaceName))) { |
488 | 0 | // names not completely initialized, so keep track of lookup misses |
489 | 0 | if (!mFaceNameListsInitialized) { |
490 | 0 | if (!mFaceNamesMissed) { |
491 | 0 | mFaceNamesMissed = MakeUnique<nsTHashtable<nsCStringHashKey>>(2); |
492 | 0 | } |
493 | 0 | mFaceNamesMissed->PutEntry(aFaceName); |
494 | 0 | } |
495 | 0 | } |
496 | 0 |
|
497 | 0 | return lookup; |
498 | 0 | } |
499 | | |
500 | | void |
501 | | gfxPlatformFontList::PreloadNamesList() |
502 | 0 | { |
503 | 0 | AutoTArray<nsCString, 10> preloadFonts; |
504 | 0 | gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts); |
505 | 0 |
|
506 | 0 | uint32_t numFonts = preloadFonts.Length(); |
507 | 0 | for (uint32_t i = 0; i < numFonts; i++) { |
508 | 0 | nsAutoCString key; |
509 | 0 | GenerateFontListKey(preloadFonts[i], key); |
510 | 0 |
|
511 | 0 | // only search canonical names! |
512 | 0 | gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key); |
513 | 0 | if (familyEntry) { |
514 | 0 | familyEntry->ReadOtherFamilyNames(this); |
515 | 0 | } |
516 | 0 | } |
517 | 0 |
|
518 | 0 | } |
519 | | |
520 | | void |
521 | | gfxPlatformFontList::LoadBadUnderlineList() |
522 | 0 | { |
523 | 0 | AutoTArray<nsCString, 10> blacklist; |
524 | 0 | gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist); |
525 | 0 | uint32_t numFonts = blacklist.Length(); |
526 | 0 | for (uint32_t i = 0; i < numFonts; i++) { |
527 | 0 | nsAutoCString key; |
528 | 0 | GenerateFontListKey(blacklist[i], key); |
529 | 0 | mBadUnderlineFamilyNames.PutEntry(key); |
530 | 0 | } |
531 | 0 | } |
532 | | |
533 | | void |
534 | | gfxPlatformFontList::UpdateFontList() |
535 | 0 | { |
536 | 0 | InitFontList(); |
537 | 0 | RebuildLocalFonts(); |
538 | 0 | } |
539 | | |
540 | | void |
541 | | gfxPlatformFontList::GetFontList(nsAtom *aLangGroup, |
542 | | const nsACString& aGenericFamily, |
543 | | nsTArray<nsString>& aListOfFonts) |
544 | 0 | { |
545 | 0 | MutexAutoLock lock(mFontFamiliesMutex); |
546 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
547 | 0 | RefPtr<gfxFontFamily>& family = iter.Data(); |
548 | 0 | if (family->FilterForFontList(aLangGroup, aGenericFamily)) { |
549 | 0 | nsAutoCString localizedFamilyName; |
550 | 0 | family->LocalizedName(localizedFamilyName); |
551 | 0 | aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(localizedFamilyName)); |
552 | 0 | } |
553 | 0 | } |
554 | 0 |
|
555 | 0 | aListOfFonts.Sort(); |
556 | 0 | aListOfFonts.Compact(); |
557 | 0 | } |
558 | | |
559 | | void |
560 | | gfxPlatformFontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray) |
561 | 0 | { |
562 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
563 | 0 | RefPtr<gfxFontFamily>& family = iter.Data(); |
564 | 0 | aFamilyArray.AppendElement(family); |
565 | 0 | } |
566 | 0 | } |
567 | | |
568 | | gfxFontEntry* |
569 | | gfxPlatformFontList::SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh, |
570 | | Script aRunScript, |
571 | | const gfxFontStyle* aStyle) |
572 | 0 | { |
573 | 0 | gfxFontEntry* fontEntry = nullptr; |
574 | 0 |
|
575 | 0 | // is codepoint with no matching font? return null immediately |
576 | 0 | if (mCodepointsWithNoFonts.test(aCh)) { |
577 | 0 | return nullptr; |
578 | 0 | } |
579 | 0 | |
580 | 0 | // Try to short-circuit font fallback for U+FFFD, used to represent |
581 | 0 | // encoding errors: just use cached family from last time U+FFFD was seen. |
582 | 0 | // This helps speed up pages with lots of encoding errors, binary-as-text, |
583 | 0 | // etc. |
584 | 0 | if (aCh == 0xFFFD && mReplacementCharFallbackFamily) { |
585 | 0 | fontEntry = |
586 | 0 | mReplacementCharFallbackFamily->FindFontForStyle(*aStyle); |
587 | 0 |
|
588 | 0 | // this should never fail, as we must have found U+FFFD in order to set |
589 | 0 | // mReplacementCharFallbackFamily at all, but better play it safe |
590 | 0 | if (fontEntry && fontEntry->HasCharacter(aCh)) { |
591 | 0 | return fontEntry; |
592 | 0 | } |
593 | 0 | } |
594 | 0 | |
595 | 0 | TimeStamp start = TimeStamp::Now(); |
596 | 0 |
|
597 | 0 | // search commonly available fonts |
598 | 0 | bool common = true; |
599 | 0 | gfxFontFamily *fallbackFamily = nullptr; |
600 | 0 | fontEntry = CommonFontFallback(aCh, aNextCh, aRunScript, aStyle, |
601 | 0 | &fallbackFamily); |
602 | 0 | |
603 | 0 | // if didn't find a font, do system-wide fallback (except for specials) |
604 | 0 | uint32_t cmapCount = 0; |
605 | 0 | if (!fontEntry) { |
606 | 0 | common = false; |
607 | 0 | fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount, |
608 | 0 | &fallbackFamily); |
609 | 0 | } |
610 | 0 | TimeDuration elapsed = TimeStamp::Now() - start; |
611 | 0 |
|
612 | 0 | LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun); |
613 | 0 |
|
614 | 0 | if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) { |
615 | 0 | uint32_t unicodeRange = FindCharUnicodeRange(aCh); |
616 | 0 | Script script = mozilla::unicode::GetScriptCode(aCh); |
617 | 0 | MOZ_LOG(log, LogLevel::Warning,\ |
618 | 0 | ("(textrun-systemfallback-%s) char: u+%6.6x " |
619 | 0 | "unicode-range: %d script: %d match: [%s]" |
620 | 0 | " time: %dus cmaps: %d\n", |
621 | 0 | (common ? "common" : "global"), aCh, |
622 | 0 | unicodeRange, static_cast<int>(script), |
623 | 0 | (fontEntry ? fontEntry->Name().get() : |
624 | 0 | "<none>"), |
625 | 0 | int32_t(elapsed.ToMicroseconds()), |
626 | 0 | cmapCount)); |
627 | 0 | } |
628 | 0 |
|
629 | 0 | // no match? add to set of non-matching codepoints |
630 | 0 | if (!fontEntry) { |
631 | 0 | mCodepointsWithNoFonts.set(aCh); |
632 | 0 | } else if (aCh == 0xFFFD && fontEntry && fallbackFamily) { |
633 | 0 | mReplacementCharFallbackFamily = fallbackFamily; |
634 | 0 | } |
635 | 0 | |
636 | 0 | // track system fallback time |
637 | 0 | static bool first = true; |
638 | 0 | int32_t intElapsed = int32_t(first ? elapsed.ToMilliseconds() : |
639 | 0 | elapsed.ToMicroseconds()); |
640 | 0 | Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST : |
641 | 0 | Telemetry::SYSTEM_FONT_FALLBACK), |
642 | 0 | intElapsed); |
643 | 0 | first = false; |
644 | 0 |
|
645 | 0 | // track the script for which fallback occurred (incremented one make it |
646 | 0 | // 1-based) |
647 | 0 | Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT, |
648 | 0 | int(aRunScript) + 1); |
649 | 0 |
|
650 | 0 | return fontEntry; |
651 | 0 | } |
652 | | |
653 | | #define NUM_FALLBACK_FONTS 8 |
654 | | |
655 | | gfxFontEntry* |
656 | | gfxPlatformFontList::CommonFontFallback(uint32_t aCh, uint32_t aNextCh, |
657 | | Script aRunScript, |
658 | | const gfxFontStyle* aMatchStyle, |
659 | | gfxFontFamily** aMatchedFamily) |
660 | 0 | { |
661 | 0 | AutoTArray<const char*,NUM_FALLBACK_FONTS> defaultFallbacks; |
662 | 0 | uint32_t i, numFallbacks; |
663 | 0 |
|
664 | 0 | gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aNextCh, |
665 | 0 | aRunScript, |
666 | 0 | defaultFallbacks); |
667 | 0 | numFallbacks = defaultFallbacks.Length(); |
668 | 0 | for (i = 0; i < numFallbacks; i++) { |
669 | 0 | nsAutoCString familyName(defaultFallbacks[i]); |
670 | 0 | gfxFontFamily *fallback = FindFamilyByCanonicalName(familyName); |
671 | 0 | if (!fallback) { |
672 | 0 | continue; |
673 | 0 | } |
674 | 0 | |
675 | 0 | gfxFontEntry *fontEntry; |
676 | 0 |
|
677 | 0 | // use first font in list that supports a given character |
678 | 0 | fontEntry = fallback->FindFontForStyle(*aMatchStyle); |
679 | 0 | if (fontEntry) { |
680 | 0 | if (fontEntry->HasCharacter(aCh)) { |
681 | 0 | *aMatchedFamily = fallback; |
682 | 0 | return fontEntry; |
683 | 0 | } |
684 | 0 | // If we requested a styled font (bold and/or italic), and the char |
685 | 0 | // was not available, check other faces of the family. |
686 | 0 | if (!fontEntry->IsNormalStyle()) { |
687 | 0 | // If style/weight/stretch was not Normal, see if we can |
688 | 0 | // fall back to a next-best face (e.g. Arial Black -> Bold, |
689 | 0 | // or Arial Narrow -> Regular). |
690 | 0 | GlobalFontMatch data(aCh, *aMatchStyle); |
691 | 0 | fallback->SearchAllFontsForChar(&data); |
692 | 0 | if (data.mBestMatch) { |
693 | 0 | *aMatchedFamily = fallback; |
694 | 0 | return data.mBestMatch; |
695 | 0 | } |
696 | 0 | } |
697 | 0 | } |
698 | 0 | } |
699 | 0 |
|
700 | 0 | return nullptr; |
701 | 0 | } |
702 | | |
703 | | gfxFontEntry* |
704 | | gfxPlatformFontList::GlobalFontFallback(const uint32_t aCh, |
705 | | Script aRunScript, |
706 | | const gfxFontStyle* aMatchStyle, |
707 | | uint32_t& aCmapCount, |
708 | | gfxFontFamily** aMatchedFamily) |
709 | 0 | { |
710 | 0 | bool useCmaps = IsFontFamilyWhitelistActive() || |
711 | 0 | gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); |
712 | 0 | if (!useCmaps) { |
713 | 0 | // Allow platform-specific fallback code to try and find a usable font |
714 | 0 | gfxFontEntry* fe = |
715 | 0 | PlatformGlobalFontFallback(aCh, aRunScript, aMatchStyle, |
716 | 0 | aMatchedFamily); |
717 | 0 | if (fe) { |
718 | 0 | return fe; |
719 | 0 | } |
720 | 0 | } |
721 | 0 | |
722 | 0 | // otherwise, try to find it among local fonts |
723 | 0 | GlobalFontMatch data(aCh, *aMatchStyle); |
724 | 0 |
|
725 | 0 | // iterate over all font families to find a font that support the character |
726 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
727 | 0 | RefPtr<gfxFontFamily>& family = iter.Data(); |
728 | 0 | // evaluate all fonts in this family for a match |
729 | 0 | family->FindFontForChar(&data); |
730 | 0 | } |
731 | 0 |
|
732 | 0 | aCmapCount = data.mCmapsTested; |
733 | 0 | *aMatchedFamily = data.mMatchedFamily; |
734 | 0 |
|
735 | 0 | return data.mBestMatch; |
736 | 0 | } |
737 | | |
738 | | gfxFontFamily* |
739 | | gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily) |
740 | 0 | { |
741 | 0 | if (aFamily && !aFamily->HasStyles()) { |
742 | 0 | aFamily->FindStyleVariations(); |
743 | 0 | aFamily->CheckForSimpleFamily(); |
744 | 0 | } |
745 | 0 |
|
746 | 0 | if (aFamily && aFamily->GetFontList().Length() == 0) { |
747 | 0 | // failed to load any faces for this family, so discard it |
748 | 0 | nsAutoCString key; |
749 | 0 | GenerateFontListKey(aFamily->Name(), key); |
750 | 0 | mFontFamilies.Remove(key); |
751 | 0 | return nullptr; |
752 | 0 | } |
753 | 0 | |
754 | 0 | return aFamily; |
755 | 0 | } |
756 | | |
757 | | bool |
758 | | gfxPlatformFontList::FindAndAddFamilies(const nsACString& aFamily, |
759 | | nsTArray<FamilyAndGeneric>* aOutput, |
760 | | FindFamiliesFlags aFlags, |
761 | | gfxFontStyle* aStyle, |
762 | | gfxFloat aDevToCssSize) |
763 | 0 | { |
764 | 0 | nsAutoCString key; |
765 | 0 | GenerateFontListKey(aFamily, key); |
766 | 0 |
|
767 | 0 | NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly"); |
768 | 0 |
|
769 | 0 | // lookup in canonical (i.e. English) family name list |
770 | 0 | gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key); |
771 | 0 |
|
772 | 0 | // if not found, lookup in other family names list (mostly localized names) |
773 | 0 | if (!familyEntry) { |
774 | 0 | familyEntry = mOtherFamilyNames.GetWeak(key); |
775 | 0 | } |
776 | 0 |
|
777 | 0 | // if still not found and other family names not yet fully initialized, |
778 | 0 | // initialize the rest of the list and try again. this is done lazily |
779 | 0 | // since reading name table entries is expensive. |
780 | 0 | // although ASCII localized family names are possible they don't occur |
781 | 0 | // in practice so avoid pulling in names at startup |
782 | 0 | if (!familyEntry && !mOtherFamilyNamesInitialized && !IsASCII(aFamily)) { |
783 | 0 | InitOtherFamilyNames(!(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading)); |
784 | 0 | familyEntry = mOtherFamilyNames.GetWeak(key); |
785 | 0 | if (!familyEntry && !mOtherFamilyNamesInitialized && |
786 | 0 | !(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) { |
787 | 0 | // localized family names load timed out, add name to list of |
788 | 0 | // names to check after localized names are loaded |
789 | 0 | if (!mOtherNamesMissed) { |
790 | 0 | mOtherNamesMissed = MakeUnique<nsTHashtable<nsCStringHashKey>>(2); |
791 | 0 | } |
792 | 0 | mOtherNamesMissed->PutEntry(key); |
793 | 0 | } |
794 | 0 | } |
795 | 0 |
|
796 | 0 | familyEntry = CheckFamily(familyEntry); |
797 | 0 |
|
798 | 0 | // If we failed to find the requested family, check for a space in the |
799 | 0 | // name; if found, and if the "base" name (up to the last space) exists |
800 | 0 | // as a family, then this might be a legacy GDI-style family name for |
801 | 0 | // an additional weight/width. Try searching the faces of the base family |
802 | 0 | // and create any corresponding legacy families. |
803 | 0 | if (!familyEntry && !(aFlags & FindFamiliesFlags::eNoSearchForLegacyFamilyNames)) { |
804 | 0 | // We don't have nsAString::RFindChar, so look for a space manually |
805 | 0 | const char* data = aFamily.BeginReading(); |
806 | 0 | int32_t index = aFamily.Length(); |
807 | 0 | while (--index > 0) { |
808 | 0 | if (data[index] == ' ') { |
809 | 0 | break; |
810 | 0 | } |
811 | 0 | } |
812 | 0 | if (index > 0) { |
813 | 0 | gfxFontFamily* base = |
814 | 0 | FindFamily(Substring(aFamily, 0, index), |
815 | 0 | FindFamiliesFlags::eNoSearchForLegacyFamilyNames); |
816 | 0 | // If we found the "base" family name, and if it has members with |
817 | 0 | // legacy names, this will add corresponding font-family entries to |
818 | 0 | // the mOtherFamilyNames list; then retry the legacy-family search. |
819 | 0 | if (base && base->CheckForLegacyFamilyNames(this)) { |
820 | 0 | familyEntry = mOtherFamilyNames.GetWeak(key); |
821 | 0 | } |
822 | 0 | } |
823 | 0 | } |
824 | 0 |
|
825 | 0 | if (familyEntry) { |
826 | 0 | aOutput->AppendElement(FamilyAndGeneric(familyEntry)); |
827 | 0 | return true; |
828 | 0 | } |
829 | 0 | |
830 | 0 | return false; |
831 | 0 | } |
832 | | |
833 | | gfxFontEntry* |
834 | | gfxPlatformFontList::FindFontForFamily(const nsACString& aFamily, |
835 | | const gfxFontStyle* aStyle) |
836 | 0 | { |
837 | 0 | gfxFontFamily *familyEntry = FindFamily(aFamily); |
838 | 0 |
|
839 | 0 | if (familyEntry) |
840 | 0 | return familyEntry->FindFontForStyle(*aStyle); |
841 | 0 | |
842 | 0 | return nullptr; |
843 | 0 | } |
844 | | |
845 | | void |
846 | | gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsCString& aOtherFamilyName) |
847 | 0 | { |
848 | 0 | nsAutoCString key; |
849 | 0 | GenerateFontListKey(aOtherFamilyName, key); |
850 | 0 |
|
851 | 0 | if (!mOtherFamilyNames.GetWeak(key)) { |
852 | 0 | mOtherFamilyNames.Put(key, aFamilyEntry); |
853 | 0 | LOG_FONTLIST(("(fontlist-otherfamily) canonical family: %s, " |
854 | 0 | "other family: %s\n", |
855 | 0 | aFamilyEntry->Name().get(), |
856 | 0 | aOtherFamilyName.get())); |
857 | 0 | if (mBadUnderlineFamilyNames.Contains(key)) |
858 | 0 | aFamilyEntry->SetBadUnderlineFamily(); |
859 | 0 | } |
860 | 0 | } |
861 | | |
862 | | void |
863 | | gfxPlatformFontList::AddFullname(gfxFontEntry *aFontEntry, const nsCString& aFullname) |
864 | 0 | { |
865 | 0 | if (!mExtraNames->mFullnames.GetWeak(aFullname)) { |
866 | 0 | mExtraNames->mFullnames.Put(aFullname, aFontEntry); |
867 | 0 | LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n", |
868 | 0 | aFontEntry->Name().get(), |
869 | 0 | aFullname.get())); |
870 | 0 | } |
871 | 0 | } |
872 | | |
873 | | void |
874 | | gfxPlatformFontList::AddPostscriptName(gfxFontEntry *aFontEntry, |
875 | | const nsCString& aPostscriptName) |
876 | 0 | { |
877 | 0 | if (!mExtraNames->mPostscriptNames.GetWeak(aPostscriptName)) { |
878 | 0 | mExtraNames->mPostscriptNames.Put(aPostscriptName, aFontEntry); |
879 | 0 | LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n", |
880 | 0 | aFontEntry->Name().get(), |
881 | 0 | aPostscriptName.get())); |
882 | 0 | } |
883 | 0 | } |
884 | | |
885 | | bool |
886 | | gfxPlatformFontList::GetStandardFamilyName(const nsCString& aFontName, |
887 | | nsACString& aFamilyName) |
888 | 0 | { |
889 | 0 | aFamilyName.Truncate(); |
890 | 0 | gfxFontFamily *ff = FindFamily(aFontName); |
891 | 0 | if (!ff) { |
892 | 0 | return false; |
893 | 0 | } |
894 | 0 | aFamilyName = ff->Name(); |
895 | 0 | return true; |
896 | 0 | } |
897 | | |
898 | | gfxFontFamily* |
899 | | gfxPlatformFontList::GetDefaultFontFamily(const nsACString& aLangGroup, |
900 | | const nsACString& aGenericFamily) |
901 | 0 | { |
902 | 0 | if (NS_WARN_IF(aLangGroup.IsEmpty()) || |
903 | 0 | NS_WARN_IF(aGenericFamily.IsEmpty())) { |
904 | 0 | return nullptr; |
905 | 0 | } |
906 | 0 | |
907 | 0 | AutoTArray<nsCString,4> names; |
908 | 0 | gfxFontUtils::AppendPrefsFontList( |
909 | 0 | NameListPref(aGenericFamily, aLangGroup).get(), names); |
910 | 0 |
|
911 | 0 | for (const nsCString& name : names) { |
912 | 0 | gfxFontFamily* fontFamily = FindFamily(name); |
913 | 0 | if (fontFamily) { |
914 | 0 | return fontFamily; |
915 | 0 | } |
916 | 0 | } |
917 | 0 | return nullptr; |
918 | 0 | } |
919 | | |
920 | | gfxCharacterMap* |
921 | | gfxPlatformFontList::FindCharMap(gfxCharacterMap *aCmap) |
922 | 0 | { |
923 | 0 | aCmap->CalcHash(); |
924 | 0 | gfxCharacterMap *cmap = AddCmap(aCmap); |
925 | 0 | cmap->mShared = true; |
926 | 0 | return cmap; |
927 | 0 | } |
928 | | |
929 | | // add a cmap to the shared cmap set |
930 | | gfxCharacterMap* |
931 | | gfxPlatformFontList::AddCmap(const gfxCharacterMap* aCharMap) |
932 | 0 | { |
933 | 0 | CharMapHashKey *found = |
934 | 0 | mSharedCmaps.PutEntry(const_cast<gfxCharacterMap*>(aCharMap)); |
935 | 0 | return found->GetKey(); |
936 | 0 | } |
937 | | |
938 | | // remove the cmap from the shared cmap set |
939 | | void |
940 | | gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap) |
941 | 0 | { |
942 | 0 | // skip lookups during teardown |
943 | 0 | if (mSharedCmaps.Count() == 0) { |
944 | 0 | return; |
945 | 0 | } |
946 | 0 | |
947 | 0 | // cmap needs to match the entry *and* be the same ptr before removing |
948 | 0 | CharMapHashKey *found = |
949 | 0 | mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap)); |
950 | 0 | if (found && found->GetKey() == aCharMap) { |
951 | 0 | mSharedCmaps.RemoveEntry(found); |
952 | 0 | } |
953 | 0 | } |
954 | | |
955 | | void |
956 | | gfxPlatformFontList::ResolveGenericFontNames( |
957 | | FontFamilyType aGenericType, |
958 | | eFontPrefLang aPrefLang, |
959 | | nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies) |
960 | 0 | { |
961 | 0 | const char* langGroupStr = GetPrefLangName(aPrefLang); |
962 | 0 | const char* generic = GetGenericName(aGenericType); |
963 | 0 |
|
964 | 0 | if (!generic) { |
965 | 0 | return; |
966 | 0 | } |
967 | 0 | |
968 | 0 | AutoTArray<nsCString,4> genericFamilies; |
969 | 0 |
|
970 | 0 | // load family for "font.name.generic.lang" |
971 | 0 | gfxFontUtils::AppendPrefsFontList( |
972 | 0 | NamePref(generic, langGroupStr).get(), genericFamilies); |
973 | 0 |
|
974 | 0 | // load fonts for "font.name-list.generic.lang" |
975 | 0 | gfxFontUtils::AppendPrefsFontList( |
976 | 0 | NameListPref(generic, langGroupStr).get(), genericFamilies); |
977 | 0 |
|
978 | 0 | nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang); |
979 | 0 | NS_ASSERTION(langGroup, "null lang group for pref lang"); |
980 | 0 |
|
981 | 0 | gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies, |
982 | 0 | langGroup, |
983 | 0 | aGenericFamilies); |
984 | 0 |
|
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()); |
990 | | } |
991 | | printf("\n"); |
992 | | #endif |
993 | | } |
994 | | |
995 | | void |
996 | | gfxPlatformFontList::ResolveEmojiFontNames( |
997 | | nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies) |
998 | 0 | { |
999 | 0 | // emoji preference has no lang name |
1000 | 0 | AutoTArray<nsCString,4> genericFamilies; |
1001 | 0 |
|
1002 | 0 | nsAutoCString prefFontListName("font.name-list.emoji"); |
1003 | 0 | gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies); |
1004 | 0 |
|
1005 | 0 | gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies, |
1006 | 0 | nullptr, |
1007 | 0 | aGenericFamilies); |
1008 | 0 | } |
1009 | | |
1010 | | void |
1011 | | gfxPlatformFontList::GetFontFamiliesFromGenericFamilies( |
1012 | | nsTArray<nsCString>& aGenericNameFamilies, |
1013 | | nsAtom* aLangGroup, |
1014 | | nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies) |
1015 | 0 | { |
1016 | 0 | // lookup and add platform fonts uniquely |
1017 | 0 | for (const nsCString& genericFamily : aGenericNameFamilies) { |
1018 | 0 | gfxFontStyle style; |
1019 | 0 | style.language = aLangGroup; |
1020 | 0 | style.systemFont = false; |
1021 | 0 | AutoTArray<FamilyAndGeneric,10> families; |
1022 | 0 | FindAndAddFamilies(genericFamily, |
1023 | 0 | &families, FindFamiliesFlags(0), |
1024 | 0 | &style); |
1025 | 0 | for (const FamilyAndGeneric& f : families) { |
1026 | 0 | if (!aGenericFamilies->Contains(f.mFamily)) { |
1027 | 0 | aGenericFamilies->AppendElement(f.mFamily); |
1028 | 0 | } |
1029 | 0 | } |
1030 | 0 | } |
1031 | 0 | } |
1032 | | |
1033 | | nsTArray<RefPtr<gfxFontFamily>>* |
1034 | | gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType, |
1035 | | eFontPrefLang aPrefLang) |
1036 | 0 | { |
1037 | 0 | // treat -moz-fixed as monospace |
1038 | 0 | if (aGenericType == eFamily_moz_fixed) { |
1039 | 0 | aGenericType = eFamily_monospace; |
1040 | 0 | } |
1041 | 0 |
|
1042 | 0 | if (aGenericType == eFamily_moz_emoji) { |
1043 | 0 | // Emoji font has no lang |
1044 | 0 | PrefFontList* prefFonts = mEmojiPrefFont.get(); |
1045 | 0 | if (MOZ_UNLIKELY(!prefFonts)) { |
1046 | 0 | prefFonts = new PrefFontList; |
1047 | 0 | ResolveEmojiFontNames(prefFonts); |
1048 | 0 | mEmojiPrefFont.reset(prefFonts); |
1049 | 0 | } |
1050 | 0 | return prefFonts; |
1051 | 0 | } |
1052 | 0 |
|
1053 | 0 | PrefFontList* prefFonts = |
1054 | 0 | mLangGroupPrefFonts[aPrefLang][aGenericType].get(); |
1055 | 0 | if (MOZ_UNLIKELY(!prefFonts)) { |
1056 | 0 | prefFonts = new PrefFontList; |
1057 | 0 | ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts); |
1058 | 0 | mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts); |
1059 | 0 | } |
1060 | 0 | return prefFonts; |
1061 | 0 | } |
1062 | | |
1063 | | void |
1064 | | gfxPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType, |
1065 | | nsAtom* aLanguage, |
1066 | | nsTArray<FamilyAndGeneric>& aFamilyList) |
1067 | 0 | { |
1068 | 0 | // map lang ==> langGroup |
1069 | 0 | nsAtom* langGroup = GetLangGroup(aLanguage); |
1070 | 0 |
|
1071 | 0 | // langGroup ==> prefLang |
1072 | 0 | eFontPrefLang prefLang = GetFontPrefLangFor(langGroup); |
1073 | 0 |
|
1074 | 0 | // lookup pref fonts |
1075 | 0 | nsTArray<RefPtr<gfxFontFamily>>* prefFonts = |
1076 | 0 | GetPrefFontsLangGroup(aGenericType, prefLang); |
1077 | 0 |
|
1078 | 0 | if (!prefFonts->IsEmpty()) { |
1079 | 0 | aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length()); |
1080 | 0 | for (auto& f : *prefFonts) { |
1081 | 0 | aFamilyList.AppendElement(FamilyAndGeneric(f.get(), aGenericType)); |
1082 | 0 | } |
1083 | 0 | } |
1084 | 0 | } |
1085 | | |
1086 | | static nsAtom* PrefLangToLangGroups(uint32_t aIndex) |
1087 | 0 | { |
1088 | 0 | // static array here avoids static constructor |
1089 | 0 | static nsAtom* gPrefLangToLangGroups[] = { |
1090 | 0 | #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_ |
1091 | 0 | #include "gfxFontPrefLangList.h" |
1092 | 0 | #undef FONT_PREF_LANG |
1093 | 0 | }; |
1094 | 0 |
|
1095 | 0 | return aIndex < ArrayLength(gPrefLangToLangGroups) |
1096 | 0 | ? gPrefLangToLangGroups[aIndex] |
1097 | 0 | : nsGkAtoms::Unicode; |
1098 | 0 | } |
1099 | | |
1100 | | eFontPrefLang |
1101 | | gfxPlatformFontList::GetFontPrefLangFor(const char* aLang) |
1102 | 0 | { |
1103 | 0 | if (!aLang || !aLang[0]) { |
1104 | 0 | return eFontPrefLang_Others; |
1105 | 0 | } |
1106 | 0 | for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) { |
1107 | 0 | if (!PL_strcasecmp(gPrefLangNames[i], aLang)) { |
1108 | 0 | return eFontPrefLang(i); |
1109 | 0 | } |
1110 | 0 | } |
1111 | 0 | return eFontPrefLang_Others; |
1112 | 0 | } |
1113 | | |
1114 | | eFontPrefLang |
1115 | | gfxPlatformFontList::GetFontPrefLangFor(nsAtom *aLang) |
1116 | 0 | { |
1117 | 0 | if (!aLang) |
1118 | 0 | return eFontPrefLang_Others; |
1119 | 0 | nsAutoCString lang; |
1120 | 0 | aLang->ToUTF8String(lang); |
1121 | 0 | return GetFontPrefLangFor(lang.get()); |
1122 | 0 | } |
1123 | | |
1124 | | nsAtom* |
1125 | | gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang) |
1126 | 0 | { |
1127 | 0 | // the special CJK set pref lang should be resolved into separate |
1128 | 0 | // calls to individual CJK pref langs before getting here |
1129 | 0 | NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang"); |
1130 | 0 |
|
1131 | 0 | return PrefLangToLangGroups(uint32_t(aLang)); |
1132 | 0 | } |
1133 | | |
1134 | | const char* |
1135 | | gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang) |
1136 | 0 | { |
1137 | 0 | if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) { |
1138 | 0 | return gPrefLangNames[uint32_t(aLang)]; |
1139 | 0 | } |
1140 | 0 | return nullptr; |
1141 | 0 | } |
1142 | | |
1143 | | eFontPrefLang |
1144 | | gfxPlatformFontList::GetFontPrefLangFor(uint8_t aUnicodeRange) |
1145 | | { |
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; |
1175 | | } |
1176 | | } |
1177 | | |
1178 | | bool |
1179 | | gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang) |
1180 | | { |
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; |
1191 | | } |
1192 | | } |
1193 | | |
1194 | | void |
1195 | | gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang) |
1196 | 0 | { |
1197 | 0 | if (IsLangCJK(aCharLang)) { |
1198 | 0 | AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang); |
1199 | 0 | } else { |
1200 | 0 | AppendPrefLang(aPrefLangs, aLen, aCharLang); |
1201 | 0 | } |
1202 | 0 |
|
1203 | 0 | AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others); |
1204 | 0 | } |
1205 | | |
1206 | | void |
1207 | | gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang) |
1208 | 0 | { |
1209 | 0 | // prefer the lang specified by the page *if* CJK |
1210 | 0 | if (IsLangCJK(aPageLang)) { |
1211 | 0 | AppendPrefLang(aPrefLangs, aLen, aPageLang); |
1212 | 0 | } |
1213 | 0 |
|
1214 | 0 | // if not set up, set up the default CJK order, based on accept lang settings and locale |
1215 | 0 | if (mCJKPrefLangs.Length() == 0) { |
1216 | 0 |
|
1217 | 0 | // temp array |
1218 | 0 | eFontPrefLang tempPrefLangs[kMaxLenPrefLangList]; |
1219 | 0 | uint32_t tempLen = 0; |
1220 | 0 |
|
1221 | 0 | // Add the CJK pref fonts from accept languages, the order should be same order |
1222 | 0 | nsAutoCString list; |
1223 | 0 | Preferences::GetLocalizedCString("intl.accept_languages", list); |
1224 | 0 | if (!list.IsEmpty()) { |
1225 | 0 | const char kComma = ','; |
1226 | 0 | const char *p, *p_end; |
1227 | 0 | list.BeginReading(p); |
1228 | 0 | list.EndReading(p_end); |
1229 | 0 | while (p < p_end) { |
1230 | 0 | while (nsCRT::IsAsciiSpace(*p)) { |
1231 | 0 | if (++p == p_end) |
1232 | 0 | break; |
1233 | 0 | } |
1234 | 0 | if (p == p_end) |
1235 | 0 | break; |
1236 | 0 | const char *start = p; |
1237 | 0 | while (++p != p_end && *p != kComma) |
1238 | 0 | /* nothing */ ; |
1239 | 0 | nsAutoCString lang(Substring(start, p)); |
1240 | 0 | lang.CompressWhitespace(false, true); |
1241 | 0 | eFontPrefLang fpl = gfxPlatformFontList::GetFontPrefLangFor(lang.get()); |
1242 | 0 | switch (fpl) { |
1243 | 0 | case eFontPrefLang_Japanese: |
1244 | 0 | case eFontPrefLang_Korean: |
1245 | 0 | case eFontPrefLang_ChineseCN: |
1246 | 0 | case eFontPrefLang_ChineseHK: |
1247 | 0 | case eFontPrefLang_ChineseTW: |
1248 | 0 | AppendPrefLang(tempPrefLangs, tempLen, fpl); |
1249 | 0 | break; |
1250 | 0 | default: |
1251 | 0 | break; |
1252 | 0 | } |
1253 | 0 | p++; |
1254 | 0 | } |
1255 | 0 | } |
1256 | 0 |
|
1257 | 0 | // Try using app's locale |
1258 | 0 | nsAutoCString localeStr; |
1259 | 0 | LocaleService::GetInstance()->GetAppLocaleAsLangTag(localeStr); |
1260 | 0 |
|
1261 | 0 | { |
1262 | 0 | Locale locale(localeStr); |
1263 | 0 | if (locale.GetLanguage().Equals("ja")) { |
1264 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese); |
1265 | 0 | } else if (locale.GetLanguage().Equals("zh")) { |
1266 | 0 | if (locale.GetRegion().Equals("CN")) { |
1267 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN); |
1268 | 0 | } else if (locale.GetRegion().Equals("TW")) { |
1269 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW); |
1270 | 0 | } else if (locale.GetRegion().Equals("HK")) { |
1271 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK); |
1272 | 0 | } |
1273 | 0 | } else if (locale.GetLanguage().Equals("ko")) { |
1274 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean); |
1275 | 0 | } |
1276 | 0 | } |
1277 | 0 |
|
1278 | 0 | // Then add the known CJK prefs in order of system preferred locales |
1279 | 0 | AutoTArray<nsCString,5> prefLocales; |
1280 | 0 | prefLocales.AppendElement(NS_LITERAL_CSTRING("ja")); |
1281 | 0 | prefLocales.AppendElement(NS_LITERAL_CSTRING("zh-CN")); |
1282 | 0 | prefLocales.AppendElement(NS_LITERAL_CSTRING("zh-TW")); |
1283 | 0 | prefLocales.AppendElement(NS_LITERAL_CSTRING("zh-HK")); |
1284 | 0 | prefLocales.AppendElement(NS_LITERAL_CSTRING("ko")); |
1285 | 0 |
|
1286 | 0 | AutoTArray<nsCString,16> sysLocales; |
1287 | 0 | AutoTArray<nsCString,16> negLocales; |
1288 | 0 | if (NS_SUCCEEDED(OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) { |
1289 | 0 | LocaleService::GetInstance()->NegotiateLanguages( |
1290 | 0 | sysLocales, prefLocales, NS_LITERAL_CSTRING(""), |
1291 | 0 | LocaleService::kLangNegStrategyFiltering, negLocales); |
1292 | 0 | for (const auto& localeStr : negLocales) { |
1293 | 0 | Locale locale(localeStr); |
1294 | 0 |
|
1295 | 0 | if (locale.GetLanguage().Equals("ja")) { |
1296 | 0 | AppendPrefLang(tempPrefLangs, tempLen, |
1297 | 0 | eFontPrefLang_Japanese); |
1298 | 0 | } else if (locale.GetLanguage().Equals("zh")) { |
1299 | 0 | if (locale.GetRegion().Equals("CN")) { |
1300 | 0 | AppendPrefLang(tempPrefLangs, tempLen, |
1301 | 0 | eFontPrefLang_ChineseCN); |
1302 | 0 | } else if (locale.GetRegion().Equals("TW")) { |
1303 | 0 | AppendPrefLang(tempPrefLangs, tempLen, |
1304 | 0 | eFontPrefLang_ChineseTW); |
1305 | 0 | } else if (locale.GetRegion().Equals("HK")) { |
1306 | 0 | AppendPrefLang(tempPrefLangs, tempLen, |
1307 | 0 | eFontPrefLang_ChineseHK); |
1308 | 0 | } |
1309 | 0 | } else if (locale.GetLanguage().Equals("ko")) { |
1310 | 0 | AppendPrefLang(tempPrefLangs, tempLen, |
1311 | 0 | eFontPrefLang_Korean); |
1312 | 0 | } |
1313 | 0 | } |
1314 | 0 | } |
1315 | 0 |
|
1316 | 0 | // last resort... (the order is same as old gfx.) |
1317 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese); |
1318 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean); |
1319 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN); |
1320 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK); |
1321 | 0 | AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW); |
1322 | 0 |
|
1323 | 0 | // copy into the cached array |
1324 | 0 | uint32_t j; |
1325 | 0 | for (j = 0; j < tempLen; j++) { |
1326 | 0 | mCJKPrefLangs.AppendElement(tempPrefLangs[j]); |
1327 | 0 | } |
1328 | 0 | } |
1329 | 0 |
|
1330 | 0 | // append in cached CJK langs |
1331 | 0 | uint32_t i, numCJKlangs = mCJKPrefLangs.Length(); |
1332 | 0 |
|
1333 | 0 | for (i = 0; i < numCJKlangs; i++) { |
1334 | 0 | AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i])); |
1335 | 0 | } |
1336 | 0 |
|
1337 | 0 | } |
1338 | | |
1339 | | void |
1340 | | gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang) |
1341 | 0 | { |
1342 | 0 | if (aLen >= kMaxLenPrefLangList) return; |
1343 | 0 | |
1344 | 0 | // make sure |
1345 | 0 | uint32_t i = 0; |
1346 | 0 | while (i < aLen && aPrefLangs[i] != aAddLang) { |
1347 | 0 | i++; |
1348 | 0 | } |
1349 | 0 |
|
1350 | 0 | if (i == aLen) { |
1351 | 0 | aPrefLangs[aLen] = aAddLang; |
1352 | 0 | aLen++; |
1353 | 0 | } |
1354 | 0 | } |
1355 | | |
1356 | | mozilla::FontFamilyType |
1357 | | gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang) |
1358 | 0 | { |
1359 | 0 | if (aLang == eFontPrefLang_Emoji) { |
1360 | 0 | return eFamily_moz_emoji; |
1361 | 0 | } |
1362 | 0 | |
1363 | 0 | // initialize lang group pref font defaults (i.e. serif/sans-serif) |
1364 | 0 | if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) { |
1365 | 0 | mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames)); |
1366 | 0 | for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) { |
1367 | 0 | nsAutoCString prefDefaultFontType("font.default."); |
1368 | 0 | prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i))); |
1369 | 0 | nsAutoCString serifOrSans; |
1370 | 0 | Preferences::GetCString(prefDefaultFontType.get(), serifOrSans); |
1371 | 0 | if (serifOrSans.EqualsLiteral("sans-serif")) { |
1372 | 0 | mDefaultGenericsLangGroup[i] = eFamily_sans_serif; |
1373 | 0 | } else { |
1374 | 0 | mDefaultGenericsLangGroup[i] = eFamily_serif; |
1375 | 0 | } |
1376 | 0 | } |
1377 | 0 | } |
1378 | 0 |
|
1379 | 0 | if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) { |
1380 | 0 | return mDefaultGenericsLangGroup[uint32_t(aLang)]; |
1381 | 0 | } |
1382 | 0 | return eFamily_serif; |
1383 | 0 | } |
1384 | | |
1385 | | |
1386 | | gfxFontFamily* |
1387 | | gfxPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle) |
1388 | 0 | { |
1389 | 0 | gfxFontFamily* family = GetDefaultFontForPlatform(aStyle); |
1390 | 0 | if (family) { |
1391 | 0 | return family; |
1392 | 0 | } |
1393 | 0 | // Something has gone wrong and we were unable to retrieve a default font |
1394 | 0 | // from the platform. (Likely the whitelist has blocked all potential |
1395 | 0 | // default fonts.) As a last resort, we return the first font listed in |
1396 | 0 | // mFontFamilies. |
1397 | 0 | return mFontFamilies.Iter().Data(); |
1398 | 0 | } |
1399 | | |
1400 | | void |
1401 | | gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsCString>& aFontFamilyNames) |
1402 | 0 | { |
1403 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
1404 | 0 | RefPtr<gfxFontFamily>& family = iter.Data(); |
1405 | 0 | aFontFamilyNames.AppendElement(family->Name()); |
1406 | 0 | } |
1407 | 0 | } |
1408 | | |
1409 | | nsAtom* |
1410 | | gfxPlatformFontList::GetLangGroup(nsAtom* aLanguage) |
1411 | 0 | { |
1412 | 0 | // map lang ==> langGroup |
1413 | 0 | nsAtom *langGroup = nullptr; |
1414 | 0 | if (aLanguage) { |
1415 | 0 | langGroup = mLangService->GetLanguageGroup(aLanguage); |
1416 | 0 | } |
1417 | 0 | if (!langGroup) { |
1418 | 0 | langGroup = nsGkAtoms::Unicode; |
1419 | 0 | } |
1420 | 0 | return langGroup; |
1421 | 0 | } |
1422 | | |
1423 | | /* static */ const char* |
1424 | | gfxPlatformFontList::GetGenericName(FontFamilyType aGenericType) |
1425 | 0 | { |
1426 | 0 | static const char kGeneric_serif[] = "serif"; |
1427 | 0 | static const char kGeneric_sans_serif[] = "sans-serif"; |
1428 | 0 | static const char kGeneric_monospace[] = "monospace"; |
1429 | 0 | static const char kGeneric_cursive[] = "cursive"; |
1430 | 0 | static const char kGeneric_fantasy[] = "fantasy"; |
1431 | 0 |
|
1432 | 0 | // type should be standard generic type at this point |
1433 | 0 | NS_ASSERTION(aGenericType >= eFamily_serif && |
1434 | 0 | aGenericType <= eFamily_fantasy, |
1435 | 0 | "standard generic font family type required"); |
1436 | 0 |
|
1437 | 0 | // map generic type to string |
1438 | 0 | const char *generic = nullptr; |
1439 | 0 | switch (aGenericType) { |
1440 | 0 | case eFamily_serif: |
1441 | 0 | generic = kGeneric_serif; |
1442 | 0 | break; |
1443 | 0 | case eFamily_sans_serif: |
1444 | 0 | generic = kGeneric_sans_serif; |
1445 | 0 | break; |
1446 | 0 | case eFamily_monospace: |
1447 | 0 | generic = kGeneric_monospace; |
1448 | 0 | break; |
1449 | 0 | case eFamily_cursive: |
1450 | 0 | generic = kGeneric_cursive; |
1451 | 0 | break; |
1452 | 0 | case eFamily_fantasy: |
1453 | 0 | generic = kGeneric_fantasy; |
1454 | 0 | break; |
1455 | 0 | default: |
1456 | 0 | break; |
1457 | 0 | } |
1458 | 0 | |
1459 | 0 | return generic; |
1460 | 0 | } |
1461 | | |
1462 | | void |
1463 | | gfxPlatformFontList::InitLoader() |
1464 | 0 | { |
1465 | 0 | GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad); |
1466 | 0 | mStartIndex = 0; |
1467 | 0 | mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length(); |
1468 | 0 | memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats)); |
1469 | 0 | } |
1470 | | |
1471 | 0 | #define FONT_LOADER_MAX_TIMESLICE 100 // max time for one pass through RunLoader = 100ms |
1472 | | |
1473 | | bool |
1474 | | gfxPlatformFontList::LoadFontInfo() |
1475 | 0 | { |
1476 | 0 | TimeStamp start = TimeStamp::Now(); |
1477 | 0 | uint32_t i, endIndex = mNumFamilies; |
1478 | 0 | bool loadCmaps = !UsesSystemFallback() || |
1479 | 0 | gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); |
1480 | 0 |
|
1481 | 0 | // for each font family, load in various font info |
1482 | 0 | for (i = mStartIndex; i < endIndex; i++) { |
1483 | 0 | nsAutoCString key; |
1484 | 0 | gfxFontFamily *familyEntry; |
1485 | 0 | GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key); |
1486 | 0 |
|
1487 | 0 | // lookup in canonical (i.e. English) family name list |
1488 | 0 | if (!(familyEntry = mFontFamilies.GetWeak(key))) { |
1489 | 0 | continue; |
1490 | 0 | } |
1491 | 0 | |
1492 | 0 | // read in face names |
1493 | 0 | familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(), mFontInfo); |
1494 | 0 |
|
1495 | 0 | // load the cmaps if needed |
1496 | 0 | if (loadCmaps) { |
1497 | 0 | familyEntry->ReadAllCMAPs(mFontInfo); |
1498 | 0 | } |
1499 | 0 |
|
1500 | 0 | // limit the time spent reading fonts in one pass |
1501 | 0 | TimeDuration elapsed = TimeStamp::Now() - start; |
1502 | 0 | if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE && |
1503 | 0 | i + 1 != endIndex) { |
1504 | 0 | endIndex = i + 1; |
1505 | 0 | break; |
1506 | 0 | } |
1507 | 0 | } |
1508 | 0 |
|
1509 | 0 | mStartIndex = endIndex; |
1510 | 0 | bool done = mStartIndex >= mNumFamilies; |
1511 | 0 |
|
1512 | 0 | if (LOG_FONTINIT_ENABLED()) { |
1513 | 0 | TimeDuration elapsed = TimeStamp::Now() - start; |
1514 | 0 | LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n", |
1515 | 0 | elapsed.ToMilliseconds(), (done ? "true" : "false"))); |
1516 | 0 | } |
1517 | 0 |
|
1518 | 0 | if (done) { |
1519 | 0 | mOtherFamilyNamesInitialized = true; |
1520 | 0 | CancelInitOtherFamilyNamesTask(); |
1521 | 0 | mFaceNameListsInitialized = true; |
1522 | 0 | } |
1523 | 0 |
|
1524 | 0 | return done; |
1525 | 0 | } |
1526 | | |
1527 | | void |
1528 | | gfxPlatformFontList::CleanupLoader() |
1529 | 0 | { |
1530 | 0 | mFontFamiliesToLoad.Clear(); |
1531 | 0 | mNumFamilies = 0; |
1532 | 0 | bool rebuilt = false, forceReflow = false; |
1533 | 0 |
|
1534 | 0 | // if had missed face names that are now available, force reflow all |
1535 | 0 | if (mFaceNamesMissed) { |
1536 | 0 | for (auto it = mFaceNamesMissed->Iter(); !it.Done(); it.Next()) { |
1537 | 0 | if (FindFaceName(it.Get()->GetKey())) { |
1538 | 0 | rebuilt = true; |
1539 | 0 | RebuildLocalFonts(); |
1540 | 0 | break; |
1541 | 0 | } |
1542 | 0 | } |
1543 | 0 | mFaceNamesMissed = nullptr; |
1544 | 0 | } |
1545 | 0 |
|
1546 | 0 | if (mOtherNamesMissed) { |
1547 | 0 | for (auto it = mOtherNamesMissed->Iter(); !it.Done(); it.Next()) { |
1548 | 0 | if (FindFamily(it.Get()->GetKey(), |
1549 | 0 | (FindFamiliesFlags::eForceOtherFamilyNamesLoading | |
1550 | 0 | FindFamiliesFlags::eNoAddToNamesMissedWhenSearching))) { |
1551 | 0 | forceReflow = true; |
1552 | 0 | ForceGlobalReflow(); |
1553 | 0 | break; |
1554 | 0 | } |
1555 | 0 | } |
1556 | 0 | mOtherNamesMissed = nullptr; |
1557 | 0 | } |
1558 | 0 |
|
1559 | 0 | if (LOG_FONTINIT_ENABLED() && mFontInfo) { |
1560 | 0 | LOG_FONTINIT(("(fontinit) fontloader load thread took %8.2f ms " |
1561 | 0 | "%d families %d fonts %d cmaps " |
1562 | 0 | "%d facenames %d othernames %s %s", |
1563 | 0 | mLoadTime.ToMilliseconds(), |
1564 | 0 | mFontInfo->mLoadStats.families, |
1565 | 0 | mFontInfo->mLoadStats.fonts, |
1566 | 0 | mFontInfo->mLoadStats.cmaps, |
1567 | 0 | mFontInfo->mLoadStats.facenames, |
1568 | 0 | mFontInfo->mLoadStats.othernames, |
1569 | 0 | (rebuilt ? "(userfont sets rebuilt)" : ""), |
1570 | 0 | (forceReflow ? "(global reflow)" : ""))); |
1571 | 0 | } |
1572 | 0 |
|
1573 | 0 | gfxFontInfoLoader::CleanupLoader(); |
1574 | 0 | } |
1575 | | |
1576 | | void |
1577 | | gfxPlatformFontList::GetPrefsAndStartLoader() |
1578 | 0 | { |
1579 | 0 | uint32_t delay = |
1580 | 0 | std::max(1u, Preferences::GetUint(FONT_LOADER_DELAY_PREF)); |
1581 | 0 | uint32_t interval = |
1582 | 0 | std::max(1u, Preferences::GetUint(FONT_LOADER_INTERVAL_PREF)); |
1583 | 0 |
|
1584 | 0 | StartLoader(delay, interval); |
1585 | 0 | } |
1586 | | |
1587 | | void |
1588 | | gfxPlatformFontList::RebuildLocalFonts() |
1589 | 0 | { |
1590 | 0 | for (auto it = mUserFontSetList.Iter(); !it.Done(); it.Next()) { |
1591 | 0 | it.Get()->GetKey()->RebuildLocalRules(); |
1592 | 0 | } |
1593 | 0 | } |
1594 | | |
1595 | | void |
1596 | | gfxPlatformFontList::ClearLangGroupPrefFonts() |
1597 | 0 | { |
1598 | 0 | for (uint32_t i = eFontPrefLang_First; |
1599 | 0 | i < eFontPrefLang_First + eFontPrefLang_Count; i++) { |
1600 | 0 | auto& prefFontsLangGroup = mLangGroupPrefFonts[i]; |
1601 | 0 | for (uint32_t j = eFamily_generic_first; |
1602 | 0 | j < eFamily_generic_first + eFamily_generic_count; j++) { |
1603 | 0 | prefFontsLangGroup[j] = nullptr; |
1604 | 0 | } |
1605 | 0 | } |
1606 | 0 | mCJKPrefLangs.Clear(); |
1607 | 0 | mEmojiPrefFont = nullptr; |
1608 | 0 | } |
1609 | | |
1610 | | // Support for memory reporting |
1611 | | |
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) |
1617 | 0 | { |
1618 | 0 | size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf); |
1619 | 0 | for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) { |
1620 | 0 | // We don't count the size of the family here, because this is an |
1621 | 0 | // *extra* reference to a family that will have already been counted in |
1622 | 0 | // the main list. |
1623 | 0 | n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
1624 | 0 | } |
1625 | 0 | return n; |
1626 | 0 | } |
1627 | | |
1628 | | /*static*/ size_t |
1629 | | gfxPlatformFontList::SizeOfFontEntryTableExcludingThis( |
1630 | | const FontEntryTable& aTable, |
1631 | | MallocSizeOf aMallocSizeOf) |
1632 | 0 | { |
1633 | 0 | size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf); |
1634 | 0 | for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) { |
1635 | 0 | // The font itself is counted by its owning family; here we only care |
1636 | 0 | // about the names stored in the hashtable keys. |
1637 | 0 | n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
1638 | 0 | } |
1639 | 0 | return n; |
1640 | 0 | } |
1641 | | |
1642 | | void |
1643 | | gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, |
1644 | | FontListSizes* aSizes) const |
1645 | 0 | { |
1646 | 0 | aSizes->mFontListSize += |
1647 | 0 | mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf); |
1648 | 0 | for (auto iter = mFontFamilies.ConstIter(); !iter.Done(); iter.Next()) { |
1649 | 0 | aSizes->mFontListSize += |
1650 | 0 | iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
1651 | 0 | iter.Data()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes); |
1652 | 0 | } |
1653 | 0 |
|
1654 | 0 | aSizes->mFontListSize += |
1655 | 0 | SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf); |
1656 | 0 |
|
1657 | 0 | if (mExtraNames) { |
1658 | 0 | aSizes->mFontListSize += |
1659 | 0 | SizeOfFontEntryTableExcludingThis(mExtraNames->mFullnames, |
1660 | 0 | aMallocSizeOf); |
1661 | 0 | aSizes->mFontListSize += |
1662 | 0 | SizeOfFontEntryTableExcludingThis(mExtraNames->mPostscriptNames, |
1663 | 0 | aMallocSizeOf); |
1664 | 0 | } |
1665 | 0 |
|
1666 | 0 | for (uint32_t i = eFontPrefLang_First; |
1667 | 0 | i < eFontPrefLang_First + eFontPrefLang_Count; i++) { |
1668 | 0 | auto& prefFontsLangGroup = mLangGroupPrefFonts[i]; |
1669 | 0 | for (uint32_t j = eFamily_generic_first; |
1670 | 0 | j < eFamily_generic_first + eFamily_generic_count; j++) { |
1671 | 0 | PrefFontList* pf = prefFontsLangGroup[j].get(); |
1672 | 0 | if (pf) { |
1673 | 0 | aSizes->mFontListSize += |
1674 | 0 | pf->ShallowSizeOfExcludingThis(aMallocSizeOf); |
1675 | 0 | } |
1676 | 0 | } |
1677 | 0 | } |
1678 | 0 |
|
1679 | 0 | aSizes->mFontListSize += |
1680 | 0 | mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf); |
1681 | 0 | aSizes->mFontListSize += |
1682 | 0 | mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf); |
1683 | 0 |
|
1684 | 0 | aSizes->mFontListSize += |
1685 | 0 | mBadUnderlineFamilyNames.SizeOfExcludingThis(aMallocSizeOf); |
1686 | 0 |
|
1687 | 0 | aSizes->mFontListSize += |
1688 | 0 | mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf); |
1689 | 0 | for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) { |
1690 | 0 | aSizes->mCharMapsSize += |
1691 | 0 | iter.Get()->GetKey()->SizeOfIncludingThis(aMallocSizeOf); |
1692 | 0 | } |
1693 | 0 | } |
1694 | | |
1695 | | void |
1696 | | gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, |
1697 | | FontListSizes* aSizes) const |
1698 | 0 | { |
1699 | 0 | aSizes->mFontListSize += aMallocSizeOf(this); |
1700 | 0 | AddSizeOfExcludingThis(aMallocSizeOf, aSizes); |
1701 | 0 | } |
1702 | | |
1703 | | bool |
1704 | | gfxPlatformFontList::IsFontFamilyWhitelistActive() |
1705 | 0 | { |
1706 | 0 | return mFontFamilyWhitelistActive; |
1707 | 0 | } |
1708 | | |
1709 | | void |
1710 | | gfxPlatformFontList::InitOtherFamilyNamesInternal(bool aDeferOtherFamilyNamesLoading) |
1711 | 0 | { |
1712 | 0 | if (mOtherFamilyNamesInitialized) { |
1713 | 0 | return; |
1714 | 0 | } |
1715 | 0 | |
1716 | 0 | if (aDeferOtherFamilyNamesLoading) { |
1717 | 0 | TimeStamp start = TimeStamp::Now(); |
1718 | 0 | bool timedOut = false; |
1719 | 0 |
|
1720 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
1721 | 0 | RefPtr<gfxFontFamily>& family = iter.Data(); |
1722 | 0 | family->ReadOtherFamilyNames(this); |
1723 | 0 | TimeDuration elapsed = TimeStamp::Now() - start; |
1724 | 0 | if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) { |
1725 | 0 | timedOut = true; |
1726 | 0 | break; |
1727 | 0 | } |
1728 | 0 | } |
1729 | 0 |
|
1730 | 0 | if (!timedOut) { |
1731 | 0 | mOtherFamilyNamesInitialized = true; |
1732 | 0 | CancelInitOtherFamilyNamesTask(); |
1733 | 0 | } |
1734 | 0 | TimeStamp end = TimeStamp::Now(); |
1735 | 0 | Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES, |
1736 | 0 | start, end); |
1737 | 0 |
|
1738 | 0 | if (LOG_FONTINIT_ENABLED()) { |
1739 | 0 | TimeDuration elapsed = end - start; |
1740 | 0 | LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s", |
1741 | 0 | elapsed.ToMilliseconds(), |
1742 | 0 | (timedOut ? "timeout" : ""))); |
1743 | 0 | } |
1744 | 0 | } else { |
1745 | 0 | TimeStamp start = TimeStamp::Now(); |
1746 | 0 |
|
1747 | 0 | for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) { |
1748 | 0 | RefPtr<gfxFontFamily>& family = iter.Data(); |
1749 | 0 | family->ReadOtherFamilyNames(this); |
1750 | 0 | } |
1751 | 0 |
|
1752 | 0 | mOtherFamilyNamesInitialized = true; |
1753 | 0 | CancelInitOtherFamilyNamesTask(); |
1754 | 0 |
|
1755 | 0 | TimeStamp end = TimeStamp::Now(); |
1756 | 0 | Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES_NO_DEFERRING, |
1757 | 0 | start, end); |
1758 | 0 |
|
1759 | 0 | if (LOG_FONTINIT_ENABLED()) { |
1760 | 0 | TimeDuration elapsed = end - start; |
1761 | 0 | LOG_FONTINIT(("(fontinit) InitOtherFamilyNames without deferring took %8.2f ms", |
1762 | 0 | elapsed.ToMilliseconds())); |
1763 | 0 | } |
1764 | 0 | } |
1765 | 0 | } |
1766 | | |
1767 | | void |
1768 | | gfxPlatformFontList::CancelInitOtherFamilyNamesTask() |
1769 | 0 | { |
1770 | 0 | if (mPendingOtherFamilyNameTask) { |
1771 | 0 | mPendingOtherFamilyNameTask->Cancel(); |
1772 | 0 | mPendingOtherFamilyNameTask = nullptr; |
1773 | 0 | } |
1774 | 0 | } |
1775 | | |
1776 | | #undef LOG |
1777 | | #undef LOG_ENABLED |