/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
usingnamespace mozilla; using mozilla::intl::Locale; using mozilla::intl::LocaleParser; using mozilla::intl::LocaleService; using mozilla::intl::OSPreferences;
// Pref for the list of icon font families that still get to override the // default font from prefs, even when use_document_fonts is disabled. // (This is to enable ligature-based icon fonts to keep working.) staticconstchar kIconFontsPref[] = "browser.display.use_document_fonts.icon_font_allowlist";
// xxx - this can probably be eliminated by reworking pref font handling code staticconstchar* gPrefLangNames[] = { #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_ #include"gfxFontPrefLangList.h" #undef FONT_PREF_LANG
};
static_assert(std::size(gPrefLangNames) == uint32_t(eFontPrefLang_Count), "size of pref lang name array doesn't match pref lang enum size");
class gfxFontListPrefObserver final : public nsIObserver {
~gfxFontListPrefObserver() = default;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
staticvoid FontListPrefChanged(constchar* aPref, void* aData = nullptr) { // XXX this could be made to only clear out the cache for the prefs that were // changed but it probably isn't that big a deal.
gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
gfxPlatformFontList::PlatformFontList()->LoadIconFontOverrideList();
gfxFontCache::GetCache()->Flush();
}
MOZ_COLLECT_REPORT( "explicit/gfx/font-list", KIND_HEAP, UNITS_BYTES, sizes.mFontListSize, "Memory used to manage the list of font families and faces.");
MOZ_COLLECT_REPORT( "explicit/gfx/font-charmaps", KIND_HEAP, UNITS_BYTES, sizes.mCharMapsSize, "Memory used to record the character coverage of individual fonts.");
if (sizes.mFontTableCacheSize) {
MOZ_COLLECT_REPORT( "explicit/gfx/font-tables", KIND_HEAP, UNITS_BYTES,
sizes.mFontTableCacheSize, "Memory used for cached font metrics and layout tables.");
}
if (sizes.mLoaderSize) {
MOZ_COLLECT_REPORT("explicit/gfx/font-loader", KIND_HEAP, UNITS_BYTES,
sizes.mLoaderSize, "Memory used for (platform-specific) font loader.");
}
if (sizes.mSharedSize) {
MOZ_COLLECT_REPORT( "font-list-shmem", KIND_NONHEAP, UNITS_BYTES, sizes.mSharedSize, "Shared memory for system font list and character coverage data.");
}
// pref changes notification setup
NS_ASSERTION(!gFontListPrefObserver, "There has been font list pref observer already");
gFontListPrefObserver = new gfxFontListPrefObserver();
NS_ADDREF(gFontListPrefObserver);
// Only the parent process listens for whitelist changes; it will then // notify its children to rebuild their font lists. if (XRE_IsParentProcess()) {
Preferences::RegisterCallback(FontWhitelistPrefChanged,
kFontSystemWhitelistPref);
}
// initialize lang group pref font defaults (i.e. serif/sans-serif)
mDefaultGenericsLangGroup.AppendElements(std::size(gPrefLangNames)); for (uint32_t i = 0; i < std::size(gPrefLangNames); i++) {
nsAutoCString prefDefaultFontType("font.default.");
prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
nsAutoCString serifOrSans;
Preferences::GetCString(prefDefaultFontType.get(), serifOrSans); if (serifOrSans.EqualsLiteral("sans-serif")) {
mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::SansSerif;
} else {
mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::Serif;
}
}
}
gfxPlatformFontList::~gfxPlatformFontList() { // Note that gfxPlatformFontList::Shutdown() ensures that the init-font-list // thread is finished before we come here.
AutoLock lock(mLock);
// We can't just do mSharedCmaps.Clear() here because removing each item from // the table would drop its last reference, and its Release() method would // then call back to MaybeRemoveCmap to search for it, which we can't do // while in the middle of clearing the table. // So we first clear the "shared" flag in each entry, so Release() won't try // to re-find them in the table. for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
iter.Get()->mCharMap->ClearSharedFlag();
}
mSharedCmaps.Clear();
ClearLangGroupPrefFontsLocked();
NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
void gfxPlatformFontList::CheckFamilyList(constchar* aList[], size_t aCount) { #ifdef DEBUG
MOZ_ASSERT(aCount > 0, "empty font family list?"); constchar* a = aList[0];
uint32_t aLen = strlen(a); for (size_t i = 1; i < aCount; ++i) { constchar* b = aList[i];
uint32_t bLen = strlen(b); if (nsCaseInsensitiveUTF8StringComparator(a, b, aLen, bLen) >= 0) {
MOZ_CRASH_UNSAFE_PRINTF("incorrectly sorted font family list: %s >= %s",
a, b);
}
a = b;
aLen = bLen;
} #endif
}
bool gfxPlatformFontList::AddWithLegacyFamilyName(const nsACString& aLegacyName,
gfxFontEntry* aFontEntry,
FontVisibility aVisibility) {
mLock.AssertCurrentThreadIn(); bool added = false;
nsAutoCString key;
ToLowerCase(aLegacyName, key);
mOtherFamilyNames
.LookupOrInsertWith(key,
[&] {
RefPtr<gfxFontFamily> family =
CreateFontFamily(aLegacyName, aVisibility); // We don't want the family to search for faces, // we're adding them directly here.
family->SetHasStyles(true); // And we don't want it to attempt to search for // legacy names, because we've already done that // (and this is the result).
family->SetCheckedForLegacyFamilyNames(true);
added = true; return family;
})
->AddFontEntry(aFontEntry->Clone()); return added;
}
bool gfxPlatformFontList::InitFontList() { // If the startup font-list-init thread is still running, we need to wait // for it to finish before trying to reinitialize here. if (sInitFontListThread && !IsInitFontListThread()) {
PR_JoinThread(sInitFontListThread);
sInitFontListThread = nullptr;
}
AutoLock lock(mLock);
if (LOG_FONTINIT_ENABLED()) {
LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
}
if (IsInitialized()) { // Font-list reinitialization always occurs on the main thread, in response // to a change notification; it's only the initial creation during startup // that may be on another thread.
MOZ_ASSERT(NS_IsMainThread());
// Rebuilding fontlist so clear out font/word caches.
gfxFontCache* fontCache = gfxFontCache::GetCache(); if (fontCache) {
fontCache->FlushShapedWordCaches();
fontCache->Flush();
}
gfxPlatform::PurgeSkiaFontCache();
// There's no need to broadcast this reflow request to child processes, as // ContentParent::NotifyUpdatedFonts deals with it by re-entering into this // function on child processes.
gfxPlatform::GlobalReflowFlags flags =
gfxPlatform::GlobalReflowFlags::NeedsReframe |
gfxPlatform::GlobalReflowFlags::FontsChanged;
ForceGlobalReflowLocked(flags);
// From here, gfxPlatformFontList::IsInitialized will return true, // unless InitFontListForPlatform() fails and we reset it below.
mFontlistInitCount++;
InitializeCodepointsWithNoFonts();
// Try to initialize the cross-process shared font list if enabled by prefs, // but not if we're running in Safe Mode. if (StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
!gfxPlatform::InSafeMode()) { for (constauto& entry : mFontEntries.Values()) { if (!entry) { continue;
}
AutoWriteLock lock(entry->mLock);
entry->mShmemCharacterMap = nullptr;
entry->mShmemFace = nullptr;
entry->mFamilyName.Truncate();
}
mFontEntries.Clear();
mShmemCharMaps.Clear(); bool oldSharedList = mSharedFontList != nullptr;
mSharedFontList.reset(new fontlist::FontList(mFontlistInitCount));
InitSharedFontListForPlatform(); if (mSharedFontList && mSharedFontList->Initialized()) { if (mLocalNameTable.Count()) {
SharedFontList()->SetLocalNames(mLocalNameTable);
mLocalNameTable.Clear();
}
} else { // something went wrong, fall back to in-process list
gfxCriticalNote << "Failed to initialize shared font list, " "falling back to in-process list.";
mSharedFontList.reset(nullptr);
} if (oldSharedList && XRE_IsParentProcess()) { // notify all children of the change if (NS_IsMainThread()) {
dom::ContentParent::NotifyUpdatedFonts(true);
} else {
NS_DispatchToMainThread(NS_NewRunnableFunction( "NotifyUpdatedFonts callback",
[] { dom::ContentParent::NotifyUpdatedFonts(true); }));
}
}
}
if (!SharedFontList()) { if (NS_FAILED(InitFontListForPlatform())) {
mFontlistInitCount = 0; returnfalse;
}
ApplyWhitelist();
}
// Set up mDefaultFontEntry as a "last resort" default that we can use // to avoid crashing if the font list is otherwise unusable.
gfxFontStyle defStyle;
FontFamily fam = GetDefaultFontLocked(nullptr, &defStyle);
gfxFontEntry* fe; if (fam.mShared) { auto face = fam.mShared->FindFaceForStyle(SharedFontList(), defStyle);
fe = face ? GetOrCreateFontEntryLocked(face, fam.mShared) : nullptr;
} else {
fe = fam.mUnshared->FindFontForStyle(defStyle);
}
mDefaultFontEntry = fe;
returntrue;
}
void gfxPlatformFontList::LoadIconFontOverrideList() {
mIconFontsSet.Clear();
AutoTArray<nsCString, 20> iconFontsList;
gfxFontUtils::GetPrefsFontList(kIconFontsPref, iconFontsList); for (auto& name : iconFontsList) {
ToLowerCase(name);
mIconFontsSet.Insert(name);
}
}
void gfxPlatformFontList::InitializeCodepointsWithNoFonts() { auto& first = mCodepointsWithNoFonts[FontVisibility(0)]; for (auto& bitset : mCodepointsWithNoFonts) { if (&bitset == &first) {
bitset.reset();
bitset.SetRange(0, 0x1f); // C0 controls
bitset.SetRange(0x7f, 0x9f); // C1 controls
bitset.SetRange(0xE000, 0xF8FF); // PUA
bitset.SetRange(0xF0000, 0x10FFFD); // Supplementary PUA
bitset.SetRange(0xfdd0, 0xfdef); // noncharacters for (unsigned i = 0; i <= 0x100000; i += 0x10000) {
bitset.SetRange(i + 0xfffe, i + 0xffff); // noncharacters
}
bitset.Compact();
} else {
bitset = first;
}
}
}
// Used if a stylo thread wants to trigger InitOtherFamilyNames in the main // process: we can't do IPC from the stylo thread so we post this to the main // thread instead. class InitOtherFamilyNamesForStylo : public mozilla::Runnable { public: explicit InitOtherFamilyNamesForStylo(bool aDeferOtherFamilyNamesLoading)
: Runnable("gfxPlatformFontList::InitOtherFamilyNamesForStylo"),
mDefer(aDeferOtherFamilyNamesLoading) {}
NS_IMETHOD Run() override { auto pfl = gfxPlatformFontList::PlatformFontList(); auto list = pfl->SharedFontList(); if (!list) { return NS_OK;
} bool initialized = false;
dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
list->GetGeneration(), mDefer, &initialized);
pfl->mOtherFamilyNamesInitialized.compareExchange(false, initialized); return NS_OK;
}
private: bool mDefer;
};
#define OTHERNAMES_TIMEOUT 200
bool gfxPlatformFontList::InitOtherFamilyNames( bool aDeferOtherFamilyNamesLoading) { if (mOtherFamilyNamesInitialized) { returntrue;
}
if (SharedFontList() && !XRE_IsParentProcess()) { if (NS_IsMainThread()) { bool initialized;
dom::ContentChild::GetSingleton()->SendInitOtherFamilyNames(
SharedFontList()->GetGeneration(), aDeferOtherFamilyNamesLoading,
&initialized);
mOtherFamilyNamesInitialized.compareExchange(false, initialized);
} else {
NS_DispatchToMainThread( new InitOtherFamilyNamesForStylo(aDeferOtherFamilyNamesLoading));
} return mOtherFamilyNamesInitialized;
}
// If the font loader delay has been set to zero, we don't defer loading // additional family names (regardless of the aDefer... parameter), as we // take this to mean availability of font info is to be prioritized over // potential startup perf or main-thread jank. // (This is used so we can reliably run reftests that depend on localized // font-family names being available.) if (aDeferOtherFamilyNamesLoading &&
StaticPrefs::gfx_font_loader_delay() > 0) { if (!mPendingOtherFamilyNameTask) {
RefPtr<mozilla::CancelableRunnable> task = new InitOtherFamilyNamesRunnable();
mPendingOtherFamilyNameTask = task;
NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
}
} else {
InitOtherFamilyNamesInternal(false);
} return mOtherFamilyNamesInitialized;
}
// time limit for loading facename lists (ms) #define NAMELIST_TIMEOUT 200
gfxFontEntry* gfxPlatformFontList::SearchFamiliesForFaceName( const nsACString& aFaceName) {
TimeStamp start = TimeStamp::Now(); bool timedOut = false; // if mFirstChar is not 0, only load facenames for families // that start with this character
char16_t firstChar = 0;
gfxFontEntry* lookup = nullptr;
// iterate over familes starting with the same letter
firstChar = ToLowerCase(aFaceName.CharAt(0));
for (constauto& entry : mFontFamilies) {
nsCStringHashKey::KeyType key = entry.GetKey(); const RefPtr<gfxFontFamily>& family = entry.GetData();
// when filtering, skip names that don't start with the filter character if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) { continue;
}
// lookup in name lookup tables, return null if not found if (mExtraNames &&
((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
(lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) { return lookup;
}
// initialize facename lookup tables if needed // note: this can terminate early or time out, in which case // mFaceNameListsInitialized remains false if (!mFaceNameListsInitialized) {
lookup = SearchFamiliesForFaceName(aFaceName); if (lookup) { return lookup;
}
}
// lookup in name lookup tables, return null if not found if (!(lookup = FindFaceName(aFaceName))) { // names not completely initialized, so keep track of lookup misses if (!mFaceNameListsInitialized) { if (!mFaceNamesMissed) {
mFaceNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
}
mFaceNamesMissed->Insert(aFaceName);
}
}
return lookup;
}
gfxFontEntry* gfxPlatformFontList::LookupInSharedFaceNameList(
nsPresContext* aPresContext, const nsACString& aFaceName,
WeightRange aWeightForEntry, StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry) {
nsAutoCString keyName(aFaceName);
ToLowerCase(keyName);
fontlist::FontList* list = SharedFontList();
fontlist::Family* family = nullptr;
fontlist::Face* face = nullptr; if (list->NumLocalFaces()) {
fontlist::LocalFaceRec* rec = list->FindLocalFace(keyName); if (rec) { auto* families = list->Families(); if (families) {
family = &families[rec->mFamilyIndex];
face = family->Faces(list)[rec->mFaceIndex].ToPtr<fontlist::Face>(list);
}
}
} else {
list->SearchForLocalFace(keyName, &family, &face);
} if (!face || !family) { return nullptr;
}
FontVisibility level =
aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User; if (!IsVisibleToCSS(*family, level)) { if (aPresContext) {
aPresContext->ReportBlockedFontFamily(*family);
} return nullptr;
}
gfxFontEntry* fe = CreateFontEntry(face, family); if (fe) {
fe->mIsLocalUserFont = true;
fe->mWeightRange = aWeightForEntry;
fe->mStretchRange = aStretchForEntry;
fe->mStyleRange = aStyleForEntry;
} return fe;
}
void gfxPlatformFontList::MaybeAddToLocalNameTable( const nsACString& aName, const fontlist::LocalFaceRec::InitData& aData) { // Compute a measure of the similarity between aName (which will be a PSName // or FullName) and aReference (a font family name). auto nameSimilarity = [](const nsACString& aName, const nsACString& aReference) -> uint32_t {
uint32_t nameIdx = 0, refIdx = 0, matchCount = 0; while (nameIdx < aName.Length() && refIdx < aReference.Length()) { // Ignore non-alphanumerics in the ASCII range, so that a PSname like // "TimesNewRomanPSMT" is a good match for family "Times New Roman". while (nameIdx < aName.Length() && IsAscii(aName[nameIdx]) &&
!IsAsciiAlphanumeric(aName[nameIdx])) {
++nameIdx;
} while (refIdx < aReference.Length() && IsAscii(aReference[refIdx]) &&
!IsAsciiAlphanumeric(aReference[refIdx])) {
++refIdx;
} if (nameIdx == aName.Length() || refIdx == aReference.Length() ||
aName[nameIdx] != aReference[refIdx]) { break;
}
++nameIdx;
++refIdx;
++matchCount;
} return matchCount;
};
void gfxPlatformFontList::UpdateFontList(bool aFullRebuild) {
MOZ_ASSERT(NS_IsMainThread()); if (aFullRebuild) {
InitFontList();
AutoLock lock(mLock);
RebuildLocalFonts();
} else { // The font list isn't being fully rebuilt, we're just being notified that // character maps have been updated and so font fallback needs to be re- // done. We only care about this if we have previously encountered a // fallback that required cmaps that were not yet available, and so we // asked for the async cmap loader to run.
AutoLock lock(mLock); if (mStartedLoadingCmapsFrom != 0xffffffffu) {
InitializeCodepointsWithNoFonts();
mStartedLoadingCmapsFrom = 0xffffffffu;
gfxPlatform::GlobalReflowFlags flags =
gfxPlatform::GlobalReflowFlags::FontsChanged |
gfxPlatform::GlobalReflowFlags::BroadcastToChildren;
ForceGlobalReflowLocked(flags);
}
}
}
if (SharedFontList()) {
fontlist::FontList* list = SharedFontList(); const fontlist::Family* families = list->Families(); if (families) { for (uint32_t i = 0; i < list->NumFamilies(); i++) { auto& f = families[i]; if (!IsVisibleToCSS(f, FontVisibility::User) || f.IsAltLocaleFamily()) { continue;
} // XXX TODO: filter families for aGenericFamily, if supported by // platform
aListOfFonts.AppendElement(
NS_ConvertUTF8toUTF16(list->LocalizedFamilyName(&f)));
}
} return;
}
for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) { if (!IsVisibleToCSS(*family, FontVisibility::User)) { continue;
} if (family->FilterForFontList(aLangGroup, aGenericFamily)) {
nsAutoCString localizedFamilyName;
family->LocalizedName(localizedFamilyName);
aListOfFonts.AppendElement(NS_ConvertUTF8toUTF16(localizedFamilyName));
}
}
aListOfFonts.Sort();
aListOfFonts.Compact();
}
void gfxPlatformFontList::GetFontFamilyList(
nsTArray<RefPtr<gfxFontFamily>>& aFamilyArray) {
AutoLock lock(mLock);
MOZ_ASSERT(aFamilyArray.IsEmpty()); // This doesn't use ToArray, because the caller passes an AutoTArray.
aFamilyArray.SetCapacity(mFontFamilies.Count()); for (constauto& family : mFontFamilies.Values()) {
aFamilyArray.AppendElement(family);
}
}
already_AddRefed<gfxFont> gfxPlatformFontList::SystemFindFontForChar(
nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
Script aRunScript, eFontPresentation aPresentation, const gfxFontStyle* aStyle, FontVisibility* aVisibility) {
AutoLock lock(mLock);
FontVisibility level =
aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
MOZ_ASSERT(!mCodepointsWithNoFonts[level].test(aCh), "don't call for codepoints already known to be unsupported");
// Try to short-circuit font fallback for U+FFFD, used to represent // encoding errors: just use cached family from last time U+FFFD was seen. // This helps speed up pages with lots of encoding errors, binary-as-text, // etc. if (aCh == 0xFFFD) {
gfxFontEntry* fontEntry = nullptr; auto& fallbackFamily = mReplacementCharFallbackFamily[level]; if (fallbackFamily.mShared) {
fontlist::Face* face =
fallbackFamily.mShared->FindFaceForStyle(SharedFontList(), *aStyle); if (face) {
fontEntry = GetOrCreateFontEntryLocked(face, fallbackFamily.mShared);
*aVisibility = fallbackFamily.mShared->Visibility();
}
} elseif (fallbackFamily.mUnshared) {
fontEntry = fallbackFamily.mUnshared->FindFontForStyle(*aStyle);
*aVisibility = fallbackFamily.mUnshared->Visibility();
}
// this should never fail, as we must have found U+FFFD in order to set // mReplacementCharFallbackFamily[...] at all, but better play it safe if (fontEntry && fontEntry->HasCharacter(aCh)) { return fontEntry->FindOrMakeFont(aStyle);
}
}
TimeStamp start = TimeStamp::Now();
// search commonly available fonts bool common = true;
FontFamily fallbackFamily;
RefPtr<gfxFont> candidate =
CommonFontFallback(aPresContext, aCh, aNextCh, aRunScript, aPresentation,
aStyle, fallbackFamily);
RefPtr<gfxFont> font; if (candidate) { if (aPresentation == eFontPresentation::Any) {
font = std::move(candidate);
} else { bool hasColorGlyph = candidate->HasColorGlyphFor(aCh, aNextCh); if (hasColorGlyph == PrefersColor(aPresentation)) {
font = std::move(candidate);
}
}
}
// If we didn't find a common font, or it was not the preferred type (color // or monochrome), do system-wide fallback (except for specials).
uint32_t cmapCount = 0; if (!font) {
common = false;
font = GlobalFontFallback(aPresContext, aCh, aNextCh, aRunScript,
aPresentation, aStyle, cmapCount, fallbackFamily); // If the font we found doesn't match the requested type, and we also found // a candidate above, prefer that one. if (font && aPresentation != eFontPresentation::Any && candidate) { bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh); if (hasColorGlyph != PrefersColor(aPresentation)) {
font = std::move(candidate);
}
}
}
TimeDuration elapsed = TimeStamp::Now() - start;
// no match? add to set of non-matching codepoints if (!font) {
mCodepointsWithNoFonts[level].set(aCh);
} else {
*aVisibility = fallbackFamily.mShared
? fallbackFamily.mShared->Visibility()
: fallbackFamily.mUnshared->Visibility(); if (aCh == 0xFFFD) {
mReplacementCharFallbackFamily[level] = fallbackFamily;
}
}
// track system fallback time staticbool first = true; if (first)
glean::fontlist::system_font_fallback_first.AccumulateRawDuration(elapsed); else
glean::fontlist::system_font_fallback.AccumulateRawDuration(elapsed);
first = false;
// If a color-emoji presentation is requested, we will check any font found // to see if it can provide this; if not, we'll remember it as a possible // candidate but search the remainder of the list for a better choice.
RefPtr<gfxFont> candidateFont;
FontFamily candidateFamily; auto check = [&](gfxFontEntry* aFontEntry,
FontFamily aFamily) -> already_AddRefed<gfxFont> {
RefPtr<gfxFont> font = aFontEntry->FindOrMakeFont(aMatchStyle); if (aPresentation < eFontPresentation::EmojiDefault ||
font->HasColorGlyphFor(aCh, aNextCh)) {
aMatchedFamily = aFamily; return font.forget();
} // We want a color glyph but this font only has monochrome; remember it // (unless we already have a candidate) but continue to search. if (!candidateFont) {
candidateFont = std::move(font);
candidateFamily = aFamily;
} return nullptr;
};
if (SharedFontList()) { for (constauto name : defaultFallbacks) {
fontlist::Family* family =
FindSharedFamily(aPresContext, nsDependentCString(name)); if (!family || !IsVisibleToCSS(*family, level)) { continue;
} // XXX(jfkthame) Should we fire the async cmap-loader here, or let it // always do a potential sync initialization of the family?
family->SearchAllFontsForChar(SharedFontList(), &data); if (data.mBestMatch) {
RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(family)); if (font) { return font.forget();
}
}
}
} else { for (constauto name : defaultFallbacks) {
gfxFontFamily* fallback =
FindFamilyByCanonicalName(nsDependentCString(name)); if (!fallback || !IsVisibleToCSS(*fallback, level)) { continue;
}
fallback->FindFontForChar(&data); if (data.mBestMatch) {
RefPtr<gfxFont> font = check(data.mBestMatch, FontFamily(fallback)); if (font) { return font.forget();
}
}
}
}
// If we had a candidate that supports the character, but doesn't have the // desired emoji-style glyph, we'll return it anyhow as nothing better was // found. if (candidateFont) {
aMatchedFamily = candidateFamily; return candidateFont.forget();
}
return nullptr;
}
already_AddRefed<gfxFont> gfxPlatformFontList::GlobalFontFallback(
nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
Script aRunScript, eFontPresentation aPresentation, const gfxFontStyle* aMatchStyle, uint32_t& aCmapCount,
FontFamily& aMatchedFamily) { bool useCmaps = IsFontFamilyWhitelistActive() ||
gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
FontVisibility level =
aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User; if (!useCmaps) { // Allow platform-specific fallback code to try and find a usable font
gfxFontEntry* fe = PlatformGlobalFontFallback(aPresContext, aCh, aRunScript,
aMatchStyle, aMatchedFamily); if (fe) { if (aMatchedFamily.mShared) { if (IsVisibleToCSS(*aMatchedFamily.mShared, level)) {
RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle); if (font) { if (aPresentation == eFontPresentation::Any) { return font.forget();
} bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh); if (hasColorGlyph == PrefersColor(aPresentation)) { return font.forget();
}
}
}
} else { if (IsVisibleToCSS(*aMatchedFamily.mUnshared, level)) {
RefPtr<gfxFont> font = fe->FindOrMakeFont(aMatchStyle); if (font) { if (aPresentation == eFontPresentation::Any) { return font.forget();
} bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh); if (hasColorGlyph == PrefersColor(aPresentation)) { return font.forget();
}
}
}
}
}
}
// otherwise, try to find it among local fonts
GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation); if (SharedFontList()) {
fontlist::Family* families = SharedFontList()->Families(); if (families) { for (uint32_t i = 0; i < SharedFontList()->NumFamilies(); i++) {
fontlist::Family& family = families[i]; if (!IsVisibleToCSS(family, level)) { continue;
} if (!family.IsFullyInitialized() &&
StaticPrefs::gfx_font_rendering_fallback_async() &&
!XRE_IsParentProcess()) { // Start loading all the missing charmaps; but this is async, // so for now we just continue, ignoring this family.
StartCmapLoadingFromFamily(i);
} else {
family.SearchAllFontsForChar(SharedFontList(), &data); if (data.mMatchDistance == 0.0) { // no better style match is possible, so stop searching break;
}
}
} if (data.mBestMatch) {
aMatchedFamily = FontFamily(data.mMatchedSharedFamily); return data.mBestMatch->FindOrMakeFont(aMatchStyle);
}
}
} else { // iterate over all font families to find a font that support the // character for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) { if (!IsVisibleToCSS(*family, level)) { continue;
} // evaluate all fonts in this family for a match
family->FindFontForChar(&data); if (data.mMatchDistance == 0.0) { // no better style match is possible, so stop searching break;
}
}
void gfxPlatformFontList::StartCmapLoadingFromFamily(uint32_t aStartIndex) {
AutoLock lock(mLock); if (aStartIndex >= mStartedLoadingCmapsFrom) { // We already initiated cmap-loading from here or earlier in the list; // no need to do it again here. return;
}
mStartedLoadingCmapsFrom = aStartIndex;
// If we're already on the main thread, don't bother dispatching a runnable // here to kick off the loading process, just do it directly. if (NS_IsMainThread()) { auto* list = SharedFontList(); if (XRE_IsParentProcess()) {
StartCmapLoading(list->GetGeneration(), aStartIndex);
} else {
dom::ContentChild::GetSingleton()->SendStartCmapLoading(
list->GetGeneration(), aStartIndex);
}
} else {
NS_DispatchToMainThread(new StartCmapLoadingRunnable(aStartIndex));
}
}
class LoadCmapsRunnable final : public IdleRunnable, public nsIObserver, public nsSupportsWeakReference {
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIOBSERVER
// Reset the current family index, if the value passed is earlier than our // original starting position. We don't "reset" if it would move the current // position forward, or back into the already-scanned range. // We could optimize further by remembering the current position reached, // and then skipping ahead from the original start, but it doesn't seem worth // extra complexity for a task that usually only happens once, and already- // processed families will be skipped pretty quickly in Run() anyhow. void MaybeResetIndex(uint32_t aFamilyIndex) { if (aFamilyIndex < mStartIndex) {
mStartIndex = aFamilyIndex;
mIndex = aFamilyIndex;
}
}
void Cancel() { mIsCanceled = true; }
NS_IMETHOD Run() override { if (mIsCanceled) { return NS_OK;
} auto* pfl = gfxPlatformFontList::PlatformFontList(); auto* list = pfl->SharedFontList();
MOZ_ASSERT(list); if (!list) { return NS_OK;
} if (mGeneration != list->GetGeneration()) { return NS_OK;
}
uint32_t numFamilies = list->NumFamilies(); if (mIndex >= numFamilies) { return NS_OK;
} auto* families = list->Families(); while (mIndex < numFamilies) { auto& family = families[mIndex++]; if (family.IsFullyInitialized()) { // Skip any families that are already initialized. continue;
} // Fully initialize this family.
Unused << pfl->InitializeFamily(&family, true); // TODO(emilio): It'd make sense to use mDeadline here to determine // whether we can do more work, but that is surprisingly a performance // regression in practice, see bug 1936489. Investigate if we can be // smarter about this. break;
} // If there are more families to initialize, post ourselves back to the // idle queue handle the next ones; otherwise we're finished and we need // to notify content processes to update their rendering. if (mIndex < numFamilies) {
mDeadline = TimeStamp();
NS_DispatchToMainThreadQueue(do_AddRef(this), EventQueuePriority::Idle);
} else {
pfl->Lock();
pfl->CancelLoadCmapsTask();
pfl->InitializeCodepointsWithNoFonts();
dom::ContentParent::NotifyUpdatedFonts(false);
pfl->Unlock();
} return NS_OK;
}
void gfxPlatformFontList::StartCmapLoading(uint32_t aGeneration,
uint32_t aStartIndex) {
MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); if (aGeneration != SharedFontList()->GetGeneration()) { return;
} if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { return;
} if (mLoadCmapsRunnable) { // We already have a runnable; just make sure it covers the full range of // families needed.
mLoadCmapsRunnable->MaybeResetIndex(aStartIndex); return;
}
mLoadCmapsRunnable = new LoadCmapsRunnable(aGeneration, aStartIndex); if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
obs->AddObserver(mLoadCmapsRunnable, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, /* ownsWeak = */ true);
}
NS_DispatchToMainThreadQueue(do_AddRef(mLoadCmapsRunnable),
EventQueuePriority::Idle);
}
if (aFamily && aFamily->FontListLength() == 0) { // Failed to load any faces for this family, so discard it.
nsAutoCString key;
GenerateFontListKey(aFamily->Name(), key);
mFontFamilies.Remove(key); return nullptr;
}
#ifdef DEBUG auto initialLength = aOutput->Length(); #endif
bool didFind =
FindAndAddFamiliesLocked(aPresContext, aGeneric, aFamily, aOutput, aFlags,
aStyle, aLanguage, aDevToCssSize); #ifdef DEBUG auto finalLength = aOutput->Length(); // Validate the expectation that the output-array grows if we return true, // or remains the same (probably empty) if we return false.
MOZ_ASSERT_IF(didFind, finalLength > initialLength);
MOZ_ASSERT_IF(!didFind, finalLength == initialLength); #endif
// If this font lookup is the result of resolving a CSS generic (not a direct // font-family request by the page), and RFP settings allow generics to be // unrestricted, bump the effective visibility level applied here so as to // allow user-installed fonts to be used. if (visibilityLevel < FontVisibility::User &&
aGeneric != StyleGenericFontFamily::None &&
!aPresContext->Document()->ShouldResistFingerprinting(
RFPTarget::FontVisibilityRestrictGenerics)) {
visibilityLevel = FontVisibility::User;
}
if (SharedFontList()) {
fontlist::Family* family = SharedFontList()->FindFamily(key); // If not found, and other family names have not yet been initialized, // initialize the rest of the list and try again. This is done lazily // since reading name table entries is expensive. // Although ASCII localized family names are possible they don't occur // in practice, so avoid pulling in names at startup. if (!family && !mOtherFamilyNamesInitialized) { bool triggerLoading = true; bool mayDefer =
!(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading); if (IsAscii(key)) { // If `key` is an ASCII name, only trigger loading if it includes a // space, and the "base" name (up to the last space) exists as a known // family, so that this might be a legacy styled-family name. constchar* data = key.BeginReading();
int32_t index = key.Length(); while (--index > 0) { if (data[index] == ' ') { break;
}
} if (index <= 0 ||
!SharedFontList()->FindFamily(nsAutoCString(key.get(), index))) {
triggerLoading = false;
}
} if (triggerLoading) { if (InitOtherFamilyNames(mayDefer)) {
family = SharedFontList()->FindFamily(key);
}
} if (!family && !mOtherFamilyNamesInitialized &&
!(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) {
AddToMissedNames(key);
}
} // Check whether the family we found is actually allowed to be looked up, // according to current font-visibility prefs. if (family) { bool visible = IsVisibleToCSS(*family, visibilityLevel); if (visible || (allowHidden && family->IsHidden())) {
aOutput->AppendElement(FamilyAndGeneric(family, aGeneric)); returntrue;
} if (aPresContext) {
aPresContext->ReportBlockedFontFamily(*family);
}
} returnfalse;
}
NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
auto isBlockedByVisibilityLevel = [=](gfxFontFamily* aFamily) -> bool { bool visible = IsVisibleToCSS(*aFamily, visibilityLevel); if (visible || (allowHidden && aFamily->IsHidden())) { returnfalse;
} if (aPresContext) {
aPresContext->ReportBlockedFontFamily(*aFamily);
} returntrue;
};
// lookup in canonical (i.e. English) family name list
gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key); if (familyEntry) { if (isBlockedByVisibilityLevel(familyEntry)) { returnfalse;
}
}
// if not found, lookup in other family names list (mostly localized names) if (!familyEntry) {
familyEntry = mOtherFamilyNames.GetWeak(key);
} if (familyEntry) { if (isBlockedByVisibilityLevel(familyEntry)) { returnfalse;
}
}
// if still not found and other family names not yet fully initialized, // initialize the rest of the list and try again. this is done lazily // since reading name table entries is expensive. // although ASCII localized family names are possible they don't occur // in practice so avoid pulling in names at startup if (!familyEntry && !mOtherFamilyNamesInitialized && !IsAscii(aFamily)) {
InitOtherFamilyNames(
!(aFlags & FindFamiliesFlags::eForceOtherFamilyNamesLoading));
familyEntry = mOtherFamilyNames.GetWeak(key); if (!familyEntry && !mOtherFamilyNamesInitialized &&
!(aFlags & FindFamiliesFlags::eNoAddToNamesMissedWhenSearching)) { // localized family names load timed out, add name to list of // names to check after localized names are loaded
AddToMissedNames(key);
} if (familyEntry) { if (isBlockedByVisibilityLevel(familyEntry)) { returnfalse;
}
}
}
familyEntry = CheckFamily(familyEntry);
// If we failed to find the requested family, check for a space in the // name; if found, and if the "base" name (up to the last space) exists // as a family, then this might be a legacy GDI-style family name for // an additional weight/width. Try searching the faces of the base family // and create any corresponding legacy families. if (!familyEntry &&
!(aFlags & FindFamiliesFlags::eNoSearchForLegacyFamilyNames)) { // We don't have nsAString::RFindChar, so look for a space manually constchar* data = aFamily.BeginReading();
int32_t index = aFamily.Length(); while (--index > 0) { if (data[index] == ' ') { break;
}
} if (index > 0) {
gfxFontFamily* base =
FindUnsharedFamily(aPresContext, Substring(aFamily, 0, index),
FindFamiliesFlags::eNoSearchForLegacyFamilyNames); // If we found the "base" family name, and if it has members with // legacy names, this will add corresponding font-family entries to // the mOtherFamilyNames list; then retry the legacy-family search. if (base && base->CheckForLegacyFamilyNames(this)) {
familyEntry = mOtherFamilyNames.GetWeak(key);
} if (familyEntry) { if (isBlockedByVisibilityLevel(familyEntry)) { returnfalse;
}
}
}
}
if (familyEntry) {
aOutput->AppendElement(FamilyAndGeneric(familyEntry, aGeneric)); returntrue;
}
class InitializeFamilyRunnable : public mozilla::Runnable { public: explicit InitializeFamilyRunnable(uint32_t aFamilyIndex, bool aLoadCmaps)
: Runnable("gfxPlatformFontList::InitializeFamilyRunnable"),
mIndex(aFamilyIndex),
mLoadCmaps(aLoadCmaps) {}
NS_IMETHOD Run() override { auto list = gfxPlatformFontList::PlatformFontList()->SharedFontList(); if (!list) { return NS_OK;
} if (mIndex >= list->NumFamilies()) { // Out of range? Maybe the list got reinitialized since this request // was posted - just ignore it. return NS_OK;
}
dom::ContentChild::GetSingleton()->SendInitializeFamily(
list->GetGeneration(), mIndex, mLoadCmaps); return NS_OK;
}
private:
uint32_t mIndex; bool mLoadCmaps;
};
bool gfxPlatformFontList::InitializeFamily(fontlist::Family* aFamily, bool aLoadCmaps) {
MOZ_ASSERT(SharedFontList()); auto list = SharedFontList(); if (!XRE_IsParentProcess()) { auto* families = list->Families(); if (!families) { returnfalse;
}
uint32_t index = aFamily - families; if (index >= list->NumFamilies()) { returnfalse;
} if (NS_IsMainThread()) {
dom::ContentChild::GetSingleton()->SendInitializeFamily(
list->GetGeneration(), index, aLoadCmaps);
} else {
NS_DispatchToMainThread(new InitializeFamilyRunnable(index, aLoadCmaps));
} return aFamily->IsInitialized();
}
if (!aFamily->IsInitialized()) { // The usual case: we're being asked to populate the face list.
AutoTArray<fontlist::Face::InitData, 16> faceList;
GetFacesInitDataForFamily(aFamily, faceList, aLoadCmaps);
aFamily->AddFaces(list, faceList);
} else { // The family's face list was already initialized, but if aLoadCmaps is // true we also want to eagerly load character maps. This is used when a // child process is doing SearchAllFontsForChar, to have the parent load // all the cmaps at once and reduce IPC traffic (and content-process file // access overhead, which is crippling for DirectWrite on Windows). if (aLoadCmaps) { auto* faces = aFamily->Faces(list); if (faces) { for (size_t i = 0; i < aFamily->NumFaces(); i++) { auto* face = faces[i].ToPtr<fontlist::Face>(list); if (face && face->mCharacterMap.IsNull()) { // We don't want to cache this font entry, as the parent will most // likely never use it again; it's just to populate the charmap for // the benefit of the child process.
RefPtr<gfxFontEntry> fe = CreateFontEntry(face, aFamily); if (fe) {
fe->ReadCMAP();
}
}
}
}
}
}
if (aLoadCmaps && aFamily->IsInitialized()) {
aFamily->SetupFamilyCharMap(list);
}
// Lookup aCmap in the shared cmap set, adding if not already present. // This is the only way for a reference to a gfxCharacterMap to be acquired // by another thread than its original creator.
already_AddRefed<gfxCharacterMap> gfxPlatformFontList::FindCharMap(
gfxCharacterMap* aCmap) { // Lock to prevent potentially racing against MaybeRemoveCmap.
AutoLock lock(mLock);
// Find existing entry or insert a new one (which will add a reference).
aCmap->CalcHash();
aCmap->mShared = true; // Set the shared flag in preparation for adding // to the global table.
RefPtr cmap = mSharedCmaps.PutEntry(aCmap)->GetKey();
// If we ended up finding a different, pre-existing entry, clear the // shared flag on this one so that it'll get deleted on Release(). if (cmap.get() != aCmap) {
aCmap->mShared = false;
}
return cmap.forget();
}
// Potentially remove the charmap from the shared cmap set. This is called // when a user of the charmap drops a reference and the refcount goes to 1; // in that case, it is possible our shared set is the only remaining user // of the object, and we should remove it. // Note that aCharMap might have already been freed, so we must not try to // dereference it until we have checked that it's still present in our table. void gfxPlatformFontList::MaybeRemoveCmap(gfxCharacterMap* aCharMap) { // Lock so that nobody else can get a reference via FindCharMap while we're // checking here.
AutoLock lock(mLock);
// Skip lookups during teardown. if (!mSharedCmaps.Count()) { return;
}
// aCharMap needs to match the entry and be the same ptr and still have a // refcount of exactly 1 (i.e. we hold the only reference) before removing. // If we're racing another thread, it might already have been removed, in // which case GetEntry will not find it and we won't try to dereference the // already-freed pointer.
CharMapHashKey* found =
mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap)); if (found && found->GetKey() == aCharMap && aCharMap->RefCount() == 1) { // Forget our reference to the object that's being deleted, without // calling Release() on it.
Unused << found->mCharMap.forget();
// Do the deletion. delete aCharMap;
// Log this as a "Release" to keep leak-checking correct.
NS_LOG_RELEASE(aCharMap, 0, "gfxCharacterMap");
mSharedCmaps.RemoveEntry(found);
}
}
staticvoid GetSystemUIFontFamilies(const nsPresContext* aPresContext,
[[maybe_unused]] nsAtom* aLangGroup,
nsTArray<nsCString>& aFamilies) { // TODO: On macOS, use CTCreateUIFontForLanguage or such thing (though the // code below ends up using [NSFont systemFontOfSize: 0.0].
nsFont systemFont;
gfxFontStyle fontStyle;
nsAutoString systemFontName; if (aPresContext ? aPresContext->Document()->ShouldResistFingerprinting(
RFPTarget::FontVisibilityRestrictGenerics)
: nsContentUtils::ShouldResistFingerprinting( "aPresContext not available",
RFPTarget::FontVisibilityRestrictGenerics)) { #ifdefined(XP_MACOSX) || defined(MOZ_WIDGET_UIKIT)
*aFamilies.AppendElement() = "-apple-system"_ns; return; #elif !defined(MOZ_WIDGET_ANDROID)
*aFamilies.AppendElement() = "sans-serif"_ns; return; #endif
} if (!LookAndFeel::GetFont(StyleSystemFont::Menu, systemFontName, fontStyle)) { return;
}
systemFontName.Trim("\"'");
CopyUTF16toUTF8(systemFontName, *aFamilies.AppendElement());
}
#if 0 // dump out generic mappings
printf("%s ===> ", NamePref(generic, langGroupStr).get()); for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) { if (k > 0) printf(", ");
printf("%s", (*aGenericFamilies)[k].mIsShared
? (*aGenericFamilies)[k].mShared->DisplayName().AsString(SharedFontList()).get()
: (*aGenericFamilies)[k].mUnshared->Name().get());
}
printf("\n"); #endif
}
void gfxPlatformFontList::ResolveEmojiFontNames(
nsPresContext* aPresContext, PrefFontList* aGenericFamilies) { // emoji preference has no lang name
AutoTArray<nsCString, 4> genericFamilies;
nsAutoCString value; if (mFontPrefs->LookupNameList(PrefName("emoji", ""), value)) {
gfxFontUtils::ParseFontList(value, genericFamilies);
}
eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(constchar* aLang) { if (!aLang || !aLang[0]) { return eFontPrefLang_Others;
} for (uint32_t i = 0; i < std::size(gPrefLangNames); ++i) { if (!nsCRT::strcasecmp(gPrefLangNames[i], aLang)) { return eFontPrefLang(i);
} // If the pref-lang is a two-character lang tag, try ignoring any trailing // subtags in aLang and see if the base lang matches. if (strlen(gPrefLangNames[i]) == 2 && strlen(aLang) > 3 &&
aLang[2] == '-' && !nsCRT::strncasecmp(gPrefLangNames[i], aLang, 2)) { return eFontPrefLang(i);
}
} return eFontPrefLang_Others;
}
nsAtom* gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang) { // the special CJK set pref lang should be resolved into separate // calls to individual CJK pref langs before getting here
NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(uint32_t aCh) { switch (ublock_getCode(aCh)) { case UBLOCK_BASIC_LATIN: case UBLOCK_LATIN_1_SUPPLEMENT: case UBLOCK_LATIN_EXTENDED_A: case UBLOCK_LATIN_EXTENDED_B: case UBLOCK_IPA_EXTENSIONS: case UBLOCK_SPACING_MODIFIER_LETTERS: case UBLOCK_LATIN_EXTENDED_ADDITIONAL: case UBLOCK_LATIN_EXTENDED_C: case UBLOCK_LATIN_EXTENDED_D: case UBLOCK_LATIN_EXTENDED_E: case UBLOCK_PHONETIC_EXTENSIONS: return eFontPrefLang_Western; case UBLOCK_GREEK: case UBLOCK_GREEK_EXTENDED: return eFontPrefLang_Greek; case UBLOCK_CYRILLIC: case UBLOCK_CYRILLIC_SUPPLEMENT: case UBLOCK_CYRILLIC_EXTENDED_A: case UBLOCK_CYRILLIC_EXTENDED_B: case UBLOCK_CYRILLIC_EXTENDED_C: return eFontPrefLang_Cyrillic; case UBLOCK_ARMENIAN: return eFontPrefLang_Armenian; case UBLOCK_HEBREW: return eFontPrefLang_Hebrew; case UBLOCK_ARABIC: case UBLOCK_ARABIC_PRESENTATION_FORMS_A: case UBLOCK_ARABIC_PRESENTATION_FORMS_B: case UBLOCK_ARABIC_SUPPLEMENT: case UBLOCK_ARABIC_EXTENDED_A: case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS: return eFontPrefLang_Arabic; case UBLOCK_DEVANAGARI: case UBLOCK_DEVANAGARI_EXTENDED: return eFontPrefLang_Devanagari; case UBLOCK_BENGALI: return eFontPrefLang_Bengali; case UBLOCK_GURMUKHI: return eFontPrefLang_Gurmukhi; case UBLOCK_GUJARATI: return eFontPrefLang_Gujarati; case UBLOCK_ORIYA: return eFontPrefLang_Oriya; case UBLOCK_TAMIL: return eFontPrefLang_Tamil; case UBLOCK_TELUGU: return eFontPrefLang_Telugu; case UBLOCK_KANNADA: return eFontPrefLang_Kannada; case UBLOCK_MALAYALAM: return eFontPrefLang_Malayalam; case UBLOCK_SINHALA: case UBLOCK_SINHALA_ARCHAIC_NUMBERS: return eFontPrefLang_Sinhala; case UBLOCK_THAI: return eFontPrefLang_Thai; case UBLOCK_TIBETAN: return eFontPrefLang_Tibetan; case UBLOCK_GEORGIAN: case UBLOCK_GEORGIAN_SUPPLEMENT: case UBLOCK_GEORGIAN_EXTENDED: return eFontPrefLang_Georgian; case UBLOCK_HANGUL_JAMO: case UBLOCK_HANGUL_COMPATIBILITY_JAMO: case UBLOCK_HANGUL_SYLLABLES: case UBLOCK_HANGUL_JAMO_EXTENDED_A: case UBLOCK_HANGUL_JAMO_EXTENDED_B: return eFontPrefLang_Korean; case UBLOCK_ETHIOPIC: case UBLOCK_ETHIOPIC_EXTENDED: case UBLOCK_ETHIOPIC_SUPPLEMENT: case UBLOCK_ETHIOPIC_EXTENDED_A: return eFontPrefLang_Ethiopic; case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS: case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED: return eFontPrefLang_Canadian; case UBLOCK_KHMER: case UBLOCK_KHMER_SYMBOLS: return eFontPrefLang_Khmer; case UBLOCK_CJK_RADICALS_SUPPLEMENT: case UBLOCK_KANGXI_RADICALS: case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS: case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION: case UBLOCK_HIRAGANA: case UBLOCK_KATAKANA: case UBLOCK_BOPOMOFO: case UBLOCK_KANBUN: case UBLOCK_BOPOMOFO_EXTENDED: case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS: case UBLOCK_CJK_COMPATIBILITY: case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A: case UBLOCK_CJK_UNIFIED_IDEOGRAPHS: case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS: case UBLOCK_CJK_COMPATIBILITY_FORMS: case UBLOCK_SMALL_FORM_VARIANTS: case UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS: case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B: case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT: case UBLOCK_KATAKANA_PHONETIC_EXTENSIONS: case UBLOCK_CJK_STROKES: case UBLOCK_VERTICAL_FORMS: case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C: case UBLOCK_KANA_SUPPLEMENT: case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D: case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E: case UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION: case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F: case UBLOCK_KANA_EXTENDED_A: return eFontPrefLang_CJKSet; case UBLOCK_MATHEMATICAL_OPERATORS: case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS: case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A: case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B: case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS: return eFontPrefLang_Mathematics; default: return eFontPrefLang_Others;
}
}
bool gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang) { switch (aLang) { case eFontPrefLang_Japanese: case eFontPrefLang_ChineseTW: case eFontPrefLang_ChineseCN: case eFontPrefLang_ChineseHK: case eFontPrefLang_Korean: case eFontPrefLang_CJKSet: returntrue; default: returnfalse;
}
}
void gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[],
uint32_t& aLen,
eFontPrefLang aCharLang,
eFontPrefLang aPageLang) { // prefer the lang specified by the page *if* CJK if (IsLangCJK(aPageLang)) {
AppendPrefLang(aPrefLangs, aLen, aPageLang);
}
// if not set up, set up the default CJK order, based on accept lang // settings and locale if (mCJKPrefLangs.Length() == 0) { // temp array
eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
uint32_t tempLen = 0;
// Add the CJK pref fonts from accept languages, the order should be same // order. We use gfxFontUtils::GetPrefsFontList to read the list even // though it's not actually a list of fonts but of lang codes; the format // is the same.
AutoTArray<nsCString, 5> list;
gfxFontUtils::GetPrefsFontList("intl.accept_languages", list, true); for (constauto& lang : list) {
eFontPrefLang fpl = GetFontPrefLangFor(lang.get()); switch (fpl) { case eFontPrefLang_Japanese: case eFontPrefLang_Korean: case eFontPrefLang_ChineseCN: case eFontPrefLang_ChineseHK: case eFontPrefLang_ChineseTW:
AppendPrefLang(tempPrefLangs, tempLen, fpl); break; default: break;
}
}
// Try using app's locale
nsAutoCString localeStr;
LocaleService::GetInstance()->GetAppLocaleAsBCP47(localeStr);
// Then add the known CJK prefs in order of system preferred locales
AutoTArray<nsCString, 5> prefLocales;
prefLocales.AppendElement("ja"_ns);
prefLocales.AppendElement("zh-CN"_ns);
prefLocales.AppendElement("zh-TW"_ns);
prefLocales.AppendElement("zh-HK"_ns);
prefLocales.AppendElement("ko"_ns);
// Last resort... set up CJK font prefs in the order listed by the user- // configurable ordering pref.
gfxFontUtils::GetPrefsFontList(kCJKFallbackOrderPref, list); for (constauto& item : list) {
eFontPrefLang fpl = GetFontPrefLangFor(item.get()); switch (fpl) { case eFontPrefLang_Japanese: case eFontPrefLang_Korean: case eFontPrefLang_ChineseCN: case eFontPrefLang_ChineseHK: case eFontPrefLang_ChineseTW:
AppendPrefLang(tempPrefLangs, tempLen, fpl); break; default: break;
}
}
// Truly-last resort... try Chinese font prefs before Japanese because // they tend to have more complete character coverage, and therefore less // risk of "ransom-note" effects. // (If the kCJKFallbackOrderPref was fully populated, as it is by default, // this will do nothing as all these values are already present.)
AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
// copy into the cached array for (constauto lang : Span<eFontPrefLang>(tempPrefLangs, tempLen)) {
mCJKPrefLangs.AppendElement(lang);
}
}
// append in cached CJK langs for (constauto lang : mCJKPrefLangs) {
AppendPrefLang(aPrefLangs, aLen, eFontPrefLang(lang));
}
}
// If the lang is already present, just ignore the addition. for (constauto lang : Span<eFontPrefLang>(aPrefLangs, aLen)) { if (lang == aAddLang) { return;
}
}
FontFamily gfxPlatformFontList::GetDefaultFontLocked(
nsPresContext* aPresContext, const gfxFontStyle* aStyle) {
FontFamily family = GetDefaultFontForPlatform(aPresContext, aStyle); if (!family.IsNull()) { return family;
} // Something has gone wrong and we were unable to retrieve a default font // from the platform. (Likely the whitelist has blocked all potential // default fonts.) As a last resort, we return the first font in our list. if (SharedFontList()) {
MOZ_RELEASE_ASSERT(SharedFontList()->NumFamilies() > 0); return FontFamily(SharedFontList()->Families());
}
MOZ_RELEASE_ASSERT(mFontFamilies.Count() > 0); return FontFamily(mFontFamilies.ConstIter().Data());
}
void gfxPlatformFontList::GetFontFamilyNames(
nsTArray<nsCString>& aFontFamilyNames) { if (SharedFontList()) {
fontlist::FontList* list = SharedFontList(); const fontlist::Family* families = list->Families(); if (families) { for (uint32_t i = 0, n = list->NumFamilies(); i < n; i++) { const fontlist::Family& family = families[i]; if (!family.IsHidden()) {
aFontFamilyNames.AppendElement(family.DisplayName().AsString(list));
}
}
}
} else { for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) { if (!family->IsHidden()) {
aFontFamilyNames.AppendElement(family->Name());
}
}
}
}
nsAtom* gfxPlatformFontList::GetLangGroup(nsAtom* aLanguage) { // map lang ==> langGroup
nsAtom* langGroup = nullptr; if (aLanguage) {
langGroup = mLangService->GetLanguageGroup(aLanguage);
} if (!langGroup) {
langGroup = nsGkAtoms::Unicode;
} return langGroup;
}
/* static */ const char* gfxPlatformFontList::GetGenericName(
StyleGenericFontFamily aGenericType) { // type should be standard generic type at this point // map generic type to string switch (aGenericType) { case StyleGenericFontFamily::Serif: return"serif"; case StyleGenericFontFamily::SansSerif: return"sans-serif"; case StyleGenericFontFamily::Monospace: return"monospace"; case StyleGenericFontFamily::Cursive: return"cursive"; case StyleGenericFontFamily::Fantasy: return"fantasy"; case StyleGenericFontFamily::SystemUi: return"system-ui"; case StyleGenericFontFamily::MozEmoji: return"-moz-emoji"; case StyleGenericFontFamily::None: break;
}
MOZ_ASSERT_UNREACHABLE("Unknown generic"); return nullptr;
}
// for each font family, load in various font info for (i = mStartIndex; i < endIndex; i++) {
nsAutoCString key;
GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
if (list) {
fontlist::Family* family = list->FindFamily(key); if (!family) { continue;
}
ReadFaceNamesForFamily(family, NeedFullnamePostscriptNames());
} else { // lookup in canonical (i.e. English) family name list
gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key); if (!familyEntry) { continue;
}
// read in face names
familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(),
mFontInfo);
// load the cmaps if needed if (loadCmaps) {
familyEntry->ReadAllCMAPs(mFontInfo);
}
}
// Limit the time spent reading fonts in one pass, unless the font-loader // delay was set to zero, in which case we run to completion even if it // causes some jank. if (StaticPrefs::gfx_font_loader_delay() > 0) {
TimeDuration elapsed = TimeStamp::Now() - start; if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
i + 1 != endIndex) {
endIndex = i + 1; break;
}
}
}
// if had missed face names that are now available, force reflow all if (mFaceNamesMissed) {
rebuilt = std::any_of(mFaceNamesMissed->cbegin(), mFaceNamesMissed->cend(),
[&](constauto& key) {
mLock.AssertCurrentThreadIn(); return FindFaceName(key);
}); if (rebuilt) {
RebuildLocalFonts();
}
if (aFlags & gfxPlatform::GlobalReflowFlags::FontsChanged) {
AutoLock lock(mLock);
InitializeCodepointsWithNoFonts(); if (SharedFontList()) { // If we're using a shared local face-name list, this may have changed // such that existing font entries held by user font sets are no longer // safe to use: ensure they all get flushed.
RebuildLocalFonts(/*aForgetLocalFaces*/ true);
}
}
void gfxPlatformFontList::GetPrefsAndStartLoader() { // If we're already in shutdown, there's no point in starting this, and it // could trigger an assertion if we try to use the Thread Manager too late. if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { return;
}
uint32_t delay = std::max(1u, StaticPrefs::gfx_font_loader_delay()); if (NS_IsMainThread()) {
StartLoader(delay);
} else {
NS_DispatchToMainThread(NS_NewRunnableFunction( "StartLoader callback", [delay, fontList = this] {
fontList->Lock();
fontList->StartLoader(delay);
fontList->Unlock();
}));
}
}
void gfxPlatformFontList::RebuildLocalFonts(bool aForgetLocalFaces) { // Make a local copy of the list of font sets we need to process.
AutoTArray<RefPtr<gfxUserFontSet>, 16> fontSets;
fontSets.SetCapacity(mUserFontSetList.Count()); for (auto* fontset : mUserFontSetList) {
fontSets.AppendElement(fontset);
} // Drop our lock before calling ForgetLocalFaces and RebuildLocalRules // for each set, to avoid possible deadlocks.
AutoUnlock unlock(mLock); for (auto fontset : fontSets) { if (aForgetLocalFaces) {
fontset->ForgetLocalFaces();
}
fontset->RebuildLocalRules();
}
}
void gfxPlatformFontList::ClearLangGroupPrefFontsLocked() { for (uint32_t i = eFontPrefLang_First;
i < eFontPrefLang_First + eFontPrefLang_Count; i++) { auto& prefFontsLangGroup = mLangGroupPrefFonts[i]; for (auto& pref : prefFontsLangGroup) {
pref = nullptr;
}
}
mCJKPrefLangs.Clear();
mEmojiPrefFont = nullptr;
// Create a new FontPrefs and replace the existing one.
mFontPrefs = MakeUnique<FontPrefs>();
}
// Support for memory reporting
// this is also used by subclasses that hold additional font tables /*static*/
size_t gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis( const FontFamilyTable& aTable, MallocSizeOf aMallocSizeOf) { return std::accumulate(
aTable.Keys().cbegin(), aTable.Keys().cend(),
aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
[&](size_t oldValue, const nsACString& key) { // We don't count the size of the family here, because this is an // *extra* reference to a family that will have already been counted in // the main list. return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
});
}
/*static*/
size_t gfxPlatformFontList::SizeOfFontEntryTableExcludingThis( const FontEntryTable& aTable, MallocSizeOf aMallocSizeOf) { return std::accumulate(
aTable.Keys().cbegin(), aTable.Keys().cend(),
aTable.ShallowSizeOfExcludingThis(aMallocSizeOf),
[&](size_t oldValue, const nsACString& key) { // The font itself is counted by its owning family; here we only care // about the names stored in the hashtable keys.
auto list = SharedFontList(); if (list) { // If the gfxFontInfoLoader task is not yet running, kick it off now so // that it will load remaining names etc as soon as idle time permits. if (mState == stateInitial || mState == stateTimerOnDelay) {
StartLoader(0);
timedOut = true;
}
} else { for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
family->ReadOtherFamilyNames(this);
TimeDuration elapsed = TimeStamp::Now() - start; if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
timedOut = true; break;
}
}
}
if (LOG_FONTINIT_ENABLED()) {
LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
elapsed.ToMilliseconds(), (timedOut ? "timeout" : "")));
}
} else {
TimeStamp start = TimeStamp::Now();
auto list = SharedFontList(); if (list) { for (auto& f : mozilla::Range<fontlist::Family>(list->Families(),
list->NumFamilies())) {
ReadFaceNamesForFamily(&f, false);
}
} else { for (const RefPtr<gfxFontFamily>& family : mFontFamilies.Values()) {
family->ReadOtherFamilyNames(this);
}
}
if (LOG_FONTINIT_ENABLED()) {
LOG_FONTINIT(
("(fontinit) InitOtherFamilyNames without deferring took %8.2f ms",
elapsed.ToMilliseconds()));
}
}
}
void gfxPlatformFontList::CancelInitOtherFamilyNamesTask() { if (mPendingOtherFamilyNameTask) {
mPendingOtherFamilyNameTask->Cancel();
mPendingOtherFamilyNameTask = nullptr;
} auto list = SharedFontList(); if (list && XRE_IsParentProcess()) { bool forceReflow = false; if (!mAliasTable.IsEmpty()) {
list->SetAliases(mAliasTable);
mAliasTable.Clear();
forceReflow = true;
} if (mLocalNameTable.Count()) {
list->SetLocalNames(mLocalNameTable);
mLocalNameTable.Clear();
forceReflow = true;
} // If there's a LoadCmapsRunnable alive, we ignore forceReflow because the // runnable will trigger a reflow when it completes, and we don't want to // reflow more times than necessary. if (forceReflow && !mLoadCmapsRunnable) {
gfxPlatform::GlobalReflowFlags flags =
gfxPlatform::GlobalReflowFlags::BroadcastToChildren |
gfxPlatform::GlobalReflowFlags::FontsChanged;
gfxPlatform::ForceGlobalReflow(flags);
}
}
}
void gfxPlatformFontList::ShareFontListShmBlockToProcess(
uint32_t aGeneration, uint32_t aIndex, base::ProcessId aPid,
mozilla::ipc::SharedMemory::Handle* aOut) { auto list = SharedFontList(); if (!list) { return;
} if (!aGeneration || list->GetGeneration() == aGeneration) {
list->ShareShmBlockToProcess(aIndex, aPid, aOut);
} else {
*aOut = mozilla::ipc::SharedMemory::NULLHandle();
}
}
void gfxPlatformFontList::ShareFontListToProcess(
nsTArray<mozilla::ipc::SharedMemory::Handle>* aBlocks,
base::ProcessId aPid) { auto list = SharedFontList(); if (list) {
list->ShareBlocksToProcess(aBlocks, aPid);
}
}
void gfxPlatformFontList::InitializeFamily(uint32_t aGeneration,
uint32_t aFamilyIndex, bool aLoadCmaps) { auto list = SharedFontList();
MOZ_ASSERT(list); if (!list) { return;
} if (list->GetGeneration() != aGeneration) { return;
} if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { return;
} if (aFamilyIndex >= list->NumFamilies()) { return;
}
fontlist::Family* family = list->Families() + aFamilyIndex; if (!family->IsInitialized() || aLoadCmaps) {
Unused << InitializeFamily(family, aLoadCmaps);
}
}
void gfxPlatformFontList::SetCharacterMap(uint32_t aGeneration,
uint32_t aFamilyIndex, bool aAlias,
uint32_t aFaceIndex, const gfxSparseBitSet& aMap) {
MOZ_ASSERT(XRE_IsParentProcess()); auto list = SharedFontList();
MOZ_ASSERT(list); if (!list) { return;
} if (list->GetGeneration() != aGeneration) { return;
} if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { return;
}
const fontlist::Family* family; if (aAlias) { if (aFamilyIndex >= list->NumAliases()) {
MOZ_ASSERT(false, "AliasFamily index out of range"); return;
}
family = list->AliasFamilies() + aFamilyIndex;
} else { if (aFamilyIndex >= list->NumFamilies()) {
MOZ_ASSERT(false, "Family index out of range"); return;
}
family = list->Families() + aFamilyIndex;
}
if (aFaceIndex >= family->NumFaces()) {
MOZ_ASSERT(false, "Face index out of range"); return;
}
if (auto* face =
family->Faces(list)[aFaceIndex].ToPtr<fontlist::Face>(list)) {
face->mCharacterMap = GetShmemCharMap(&aMap);
}
}
void gfxPlatformFontList::SetupFamilyCharMap(uint32_t aGeneration,
uint32_t aIndex, bool aAlias) {
MOZ_ASSERT(XRE_IsParentProcess()); auto list = SharedFontList();
MOZ_ASSERT(list); if (!list) { return;
} if (list->GetGeneration() != aGeneration) { return;
} if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { return;
}
if (aAlias) { if (aIndex >= list->NumAliases()) {
MOZ_ASSERT(false, "AliasFamily index out of range"); return;
}
list->AliasFamilies()[aIndex].SetupFamilyCharMap(list); return;
}
if (aIndex >= list->NumFamilies()) {
MOZ_ASSERT(false, "Family index out of range"); return;
}
list->Families()[aIndex].SetupFamilyCharMap(list);
}
bool gfxPlatformFontList::InitOtherFamilyNames(uint32_t aGeneration, bool aDefer) { auto list = SharedFontList();
MOZ_ASSERT(list); if (!list) { returnfalse;
} if (list->GetGeneration() != aGeneration) { returnfalse;
} if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { returnfalse;
} return InitOtherFamilyNames(aDefer);
}
gfxPlatformFontList::FontPrefs::FontPrefs() { // This must be created on the main thread, so that we can safely use the // Preferences service. Once created, it can be read from any thread.
MOZ_ASSERT(NS_IsMainThread());
Init();
}
void gfxPlatformFontList::FontPrefs::Init() { if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) { return;
}
nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch(); if (!prefRootBranch) { return;
}
nsTArray<nsCString> prefNames; if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNamePrefix, prefNames))) { for (auto& prefName : prefNames) {
nsAutoCString value; if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
nsAutoCString pref(Substring(prefName, sizeof(kNamePrefix) - 1));
mFontName.InsertOrUpdate(pref, value);
}
}
} if (NS_SUCCEEDED(prefRootBranch->GetChildList(kNameListPrefix, prefNames))) { for (auto& prefName : prefNames) {
nsAutoCString value; if (NS_SUCCEEDED(Preferences::GetCString(prefName.get(), value))) {
nsAutoCString pref(Substring(prefName, sizeof(kNameListPrefix) - 1));
mFontNameList.InsertOrUpdate(pref, value);
}
}
}
mEmojiHasUserValue = Preferences::HasUserValue("font.name-list.emoji");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.