/* -*- 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);
}
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 ist noch experimentell.