summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxPlatformFontList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/gfxPlatformFontList.cpp')
-rw-r--r--gfx/thebes/gfxPlatformFontList.cpp3174
1 files changed, 3174 insertions, 0 deletions
diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp
new file mode 100644
index 0000000000..1131ecb513
--- /dev/null
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -0,0 +1,3174 @@
+/* -*- 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/. */
+
+#include "mozilla/Logging.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/intl/Locale.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/intl/OSPreferences.h"
+
+#include "gfxPlatformFontList.h"
+#include "gfxTextRun.h"
+#include "gfxUserFontSet.h"
+#include "SharedFontList-impl.h"
+
+#include "GeckoProfiler.h"
+#include "nsCRT.h"
+#include "nsGkAtoms.h"
+#include "nsPresContext.h"
+#include "nsServiceManagerUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
+#include "nsXULAppAPI.h"
+
+#include "mozilla/AppShutdown.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/BlobImpl.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentProcessMessageManager.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/Unused.h"
+
+#include "base/eintr_wrapper.h"
+
+#include <locale.h>
+#include <numeric>
+
+using namespace mozilla;
+using mozilla::intl::Locale;
+using mozilla::intl::LocaleParser;
+using mozilla::intl::LocaleService;
+using mozilla::intl::OSPreferences;
+
+#define LOG_FONTLIST(args) \
+ MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
+#define LOG_FONTLIST_ENABLED() \
+ MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
+#define LOG_FONTINIT(args) \
+ MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
+#define LOG_FONTINIT_ENABLED() \
+ MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
+
+gfxPlatformFontList* gfxPlatformFontList::sPlatformFontList = nullptr;
+
+// Character ranges that require complex-script shaping support in the font,
+// and so should be masked out by ReadCMAP if the necessary layout tables
+// are not present.
+// Currently used by the Mac and FT2 implementations only, but probably should
+// be supported on Windows as well.
+const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = {
+ // Actually, now that harfbuzz supports presentation-forms shaping for
+ // Arabic, we can render it without layout tables. So maybe we don't
+ // want to mask the basic Arabic block here?
+ // This affects the arabic-fallback-*.html reftests, which rely on
+ // loading a font that *doesn't* have any GSUB table.
+ {0x0600, 0x060B, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
+ // skip 060C Arabic comma, also used by N'Ko etc
+ {0x060D, 0x061A, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
+ // skip 061B Arabic semicolon, also used by N'Ko etc
+ {0x061C, 0x061E, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
+ // skip 061F Arabic question mark, also used by N'Ko etc
+ {0x0620, 0x063F, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
+ // skip 0640 Arabic tatweel (for syriac, adlam, etc)
+ {0x0641, 0x06D3, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
+ // skip 06D4 Arabic full stop (for hanifi rohingya)
+ {0x06D5, 0x06FF, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
+ {0x0700, 0x074F, 1, {TRUETYPE_TAG('s', 'y', 'r', 'c'), 0, 0}},
+ {0x0750, 0x077F, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
+ {0x08A0, 0x08FF, 1, {TRUETYPE_TAG('a', 'r', 'a', 'b'), 0, 0}},
+ {0x0900,
+ 0x0963,
+ 2,
+ {TRUETYPE_TAG('d', 'e', 'v', '2'), TRUETYPE_TAG('d', 'e', 'v', 'a'), 0}},
+ // skip 0964 DEVANAGARI DANDA and 0965 DEVANAGARI DOUBLE DANDA, shared by
+ // various other Indic writing systems
+ {0x0966,
+ 0x097F,
+ 2,
+ {TRUETYPE_TAG('d', 'e', 'v', '2'), TRUETYPE_TAG('d', 'e', 'v', 'a'), 0}},
+ {0x0980,
+ 0x09FF,
+ 2,
+ {TRUETYPE_TAG('b', 'n', 'g', '2'), TRUETYPE_TAG('b', 'e', 'n', 'g'), 0}},
+ {0x0A00,
+ 0x0A7F,
+ 2,
+ {TRUETYPE_TAG('g', 'u', 'r', '2'), TRUETYPE_TAG('g', 'u', 'r', 'u'), 0}},
+ {0x0A80,
+ 0x0AFF,
+ 2,
+ {TRUETYPE_TAG('g', 'j', 'r', '2'), TRUETYPE_TAG('g', 'u', 'j', 'r'), 0}},
+ {0x0B00,
+ 0x0B7F,
+ 2,
+ {TRUETYPE_TAG('o', 'r', 'y', '2'), TRUETYPE_TAG('o', 'r', 'y', 'a'), 0}},
+ {0x0B80,
+ 0x0BFF,
+ 2,
+ {TRUETYPE_TAG('t', 'm', 'l', '2'), TRUETYPE_TAG('t', 'a', 'm', 'l'), 0}},
+ {0x0C00,
+ 0x0C7F,
+ 2,
+ {TRUETYPE_TAG('t', 'e', 'l', '2'), TRUETYPE_TAG('t', 'e', 'l', 'u'), 0}},
+ {0x0C80,
+ 0x0CFF,
+ 2,
+ {TRUETYPE_TAG('k', 'n', 'd', '2'), TRUETYPE_TAG('k', 'n', 'd', 'a'), 0}},
+ {0x0D00,
+ 0x0D7F,
+ 2,
+ {TRUETYPE_TAG('m', 'l', 'm', '2'), TRUETYPE_TAG('m', 'l', 'y', 'm'), 0}},
+ {0x0D80, 0x0DFF, 1, {TRUETYPE_TAG('s', 'i', 'n', 'h'), 0, 0}},
+ {0x0E80, 0x0EFF, 1, {TRUETYPE_TAG('l', 'a', 'o', ' '), 0, 0}},
+ {0x0F00, 0x0FFF, 1, {TRUETYPE_TAG('t', 'i', 'b', 't'), 0, 0}},
+ {0x1000,
+ 0x109f,
+ 2,
+ {TRUETYPE_TAG('m', 'y', 'm', 'r'), TRUETYPE_TAG('m', 'y', 'm', '2'), 0}},
+ {0x1780, 0x17ff, 1, {TRUETYPE_TAG('k', 'h', 'm', 'r'), 0, 0}},
+ // Khmer Symbols (19e0..19ff) don't seem to need any special shaping
+ {0xaa60,
+ 0xaa7f,
+ 2,
+ {TRUETYPE_TAG('m', 'y', 'm', 'r'), TRUETYPE_TAG('m', 'y', 'm', '2'), 0}},
+ // Thai seems to be "renderable" without AAT morphing tables
+ {0, 0, 0, {0, 0, 0}} // terminator
+};
+
+static const char* kObservedPrefs[] = {
+ "font.", "font.name-list.", "intl.accept_languages", // hmmmm...
+ "browser.display.use_document_fonts.icon_font_allowlist", nullptr};
+
+static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
+
+static const char kCJKFallbackOrderPref[] = "font.cjk_pref_fallback_order";
+
+// 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.)
+static const char kIconFontsPref[] =
+ "browser.display.use_document_fonts.icon_font_allowlist";
+
+// xxx - this can probably be eliminated by reworking pref font handling code
+static const char* gPrefLangNames[] = {
+#define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
+#include "gfxFontPrefLangList.h"
+#undef FONT_PREF_LANG
+};
+
+static_assert(MOZ_ARRAY_LENGTH(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
+};
+
+static void FontListPrefChanged(const char* 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();
+}
+
+static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
+
+NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
+
+#define LOCALES_CHANGED_TOPIC "intl:system-locales-changed"
+
+NS_IMETHODIMP
+gfxFontListPrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ NS_ASSERTION(!strcmp(aTopic, LOCALES_CHANGED_TOPIC), "invalid topic");
+ FontListPrefChanged(nullptr);
+
+ if (XRE_IsParentProcess()) {
+ gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::No);
+ }
+ return NS_OK;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
+
+NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
+
+NS_IMETHODIMP
+gfxPlatformFontList::MemoryReporter::CollectReports(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) {
+ FontListSizes sizes;
+ sizes.mFontListSize = 0;
+ sizes.mFontTableCacheSize = 0;
+ sizes.mCharMapsSize = 0;
+ sizes.mLoaderSize = 0;
+ sizes.mSharedSize = 0;
+
+ gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(
+ &FontListMallocSizeOf, &sizes);
+
+ 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.");
+ }
+
+ return NS_OK;
+}
+
+PRThread* gfxPlatformFontList::sInitFontListThread = nullptr;
+
+static void InitFontListCallback(void* aFontList) {
+ AUTO_PROFILER_REGISTER_THREAD("InitFontList");
+ PR_SetCurrentThreadName("InitFontList");
+
+ if (!static_cast<gfxPlatformFontList*>(aFontList)->InitFontList()) {
+ gfxPlatformFontList::Shutdown();
+ }
+}
+
+/* static */
+bool gfxPlatformFontList::Initialize(gfxPlatformFontList* aList) {
+ sPlatformFontList = aList;
+ if (XRE_IsParentProcess() &&
+ StaticPrefs::gfx_font_list_omt_enabled_AtStartup() &&
+ StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
+ !gfxPlatform::InSafeMode()) {
+ sInitFontListThread = PR_CreateThread(
+ PR_USER_THREAD, InitFontListCallback, aList, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ return true;
+ }
+ if (aList->InitFontList()) {
+ return true;
+ }
+ Shutdown();
+ return false;
+}
+
+gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
+ : mLock("gfxPlatformFontList lock"),
+ mFontFamilies(64),
+ mOtherFamilyNames(16),
+ mSharedCmaps(8) {
+ if (aNeedFullnamePostscriptNames) {
+ mExtraNames = MakeUnique<ExtraNames>();
+ }
+
+ mLangService = nsLanguageAtomService::GetService();
+
+ LoadBadUnderlineList();
+ LoadIconFontOverrideList();
+
+ mFontPrefs = MakeUnique<FontPrefs>();
+
+ gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
+ mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
+
+ // pref changes notification setup
+ NS_ASSERTION(!gFontListPrefObserver,
+ "There has been font list pref observer already");
+ gFontListPrefObserver = new gfxFontListPrefObserver();
+ NS_ADDREF(gFontListPrefObserver);
+
+ Preferences::RegisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC, false);
+ }
+
+ // 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);
+ }
+
+ RegisterStrongMemoryReporter(new MemoryReporter());
+
+ // initialize lang group pref font defaults (i.e. serif/sans-serif)
+ mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
+ for (uint32_t i = 0; i < ArrayLength(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() {
+ // We take the lock here because it's possible the InitFontList thread is
+ // still running, in which case we need to wait for it to finish; this will
+ // block until the lock becomes available, ensuring we don't destroy things
+ // the initialization thread is using.
+ 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");
+
+ Preferences::UnregisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC);
+ }
+
+ if (XRE_IsParentProcess()) {
+ Preferences::UnregisterCallback(FontWhitelistPrefChanged,
+ kFontSystemWhitelistPref);
+ }
+ NS_RELEASE(gFontListPrefObserver);
+}
+
+/* static */
+void gfxPlatformFontList::FontWhitelistPrefChanged(const char* aPref,
+ void* aClosure) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ auto* pfl = gfxPlatformFontList::PlatformFontList();
+ pfl->UpdateFontList(true);
+ dom::ContentParent::NotifyUpdatedFonts(true);
+}
+
+void gfxPlatformFontList::ApplyWhitelist() {
+ uint32_t numFonts = mEnabledFontsList.Length();
+ if (!mFontFamilyWhitelistActive) {
+ return;
+ }
+ nsTHashSet<nsCString> familyNamesWhitelist;
+ for (uint32_t i = 0; i < numFonts; i++) {
+ nsAutoCString key;
+ ToLowerCase(mEnabledFontsList[i], key);
+ familyNamesWhitelist.Insert(key);
+ }
+ AutoTArray<RefPtr<gfxFontFamily>, 128> accepted;
+ bool whitelistedFontFound = false;
+ for (const auto& entry : mFontFamilies) {
+ if (entry.GetData()->IsHidden()) {
+ // Hidden system fonts are exempt from whitelisting, but don't count
+ // towards determining whether we "kept" any (user-visible) fonts
+ accepted.AppendElement(entry.GetData());
+ continue;
+ }
+ nsAutoCString fontFamilyName(entry.GetKey());
+ ToLowerCase(fontFamilyName);
+ if (familyNamesWhitelist.Contains(fontFamilyName)) {
+ accepted.AppendElement(entry.GetData());
+ whitelistedFontFound = true;
+ }
+ }
+ if (!whitelistedFontFound) {
+ // No whitelisted fonts found! Ignore the whitelist.
+ return;
+ }
+ // Replace the original full list with the accepted subset.
+ mFontFamilies.Clear();
+ for (auto& f : accepted) {
+ nsAutoCString fontFamilyName(f->Name());
+ ToLowerCase(fontFamilyName);
+ mFontFamilies.InsertOrUpdate(fontFamilyName, std::move(f));
+ }
+}
+
+void gfxPlatformFontList::ApplyWhitelist(
+ nsTArray<fontlist::Family::InitData>& aFamilies) {
+ mLock.AssertCurrentThreadIn();
+ if (!mFontFamilyWhitelistActive) {
+ return;
+ }
+ nsTHashSet<nsCString> familyNamesWhitelist;
+ for (const auto& item : mEnabledFontsList) {
+ nsAutoCString key;
+ ToLowerCase(item, key);
+ familyNamesWhitelist.Insert(key);
+ }
+ AutoTArray<fontlist::Family::InitData, 128> accepted;
+ bool keptNonHidden = false;
+ for (auto& f : aFamilies) {
+ if (f.mVisibility == FontVisibility::Hidden ||
+ familyNamesWhitelist.Contains(f.mKey)) {
+ accepted.AppendElement(f);
+ if (f.mVisibility != FontVisibility::Hidden) {
+ keptNonHidden = true;
+ }
+ }
+ }
+ if (!keptNonHidden) {
+ // No (visible) families were whitelisted: ignore the whitelist
+ // and just leave the fontlist unchanged.
+ return;
+ }
+ aFamilies = std::move(accepted);
+}
+
+bool gfxPlatformFontList::FamilyInList(const nsACString& aName,
+ const char* aList[], size_t aCount) {
+ size_t result;
+ return BinarySearchIf(
+ aList, 0, aCount,
+ [&](const char* const aVal) -> int {
+ return nsCaseInsensitiveUTF8StringComparator(
+ aName.BeginReading(), aVal, aName.Length(), strlen(aVal));
+ },
+ &result);
+}
+
+void gfxPlatformFontList::CheckFamilyList(const char* aList[], size_t aCount) {
+#ifdef DEBUG
+ MOZ_ASSERT(aCount > 0, "empty font family list?");
+ const char* a = aList[0];
+ uint32_t aLen = strlen(a);
+ for (size_t i = 1; i < aCount; ++i) {
+ const char* b = aList[i];
+ uint32_t bLen = strlen(b);
+ MOZ_ASSERT(nsCaseInsensitiveUTF8StringComparator(a, b, aLen, bLen) < 0,
+ "incorrectly sorted font family list!");
+ 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.
+ ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::Yes,
+ gfxPlatform::BroadcastToChildren::No);
+
+ mAliasTable.Clear();
+ mLocalNameTable.Clear();
+ mIconFontsSet.Clear();
+
+ CancelLoadCmapsTask();
+ mStartedLoadingCmapsFrom = 0xffffffffu;
+
+ CancelInitOtherFamilyNamesTask();
+ mFontFamilies.Clear();
+ mOtherFamilyNames.Clear();
+ mOtherFamilyNamesInitialized = false;
+
+ if (mExtraNames) {
+ mExtraNames->mFullnames.Clear();
+ mExtraNames->mPostscriptNames.Clear();
+ }
+ mFaceNameListsInitialized = false;
+ ClearLangGroupPrefFontsLocked();
+ CancelLoader();
+
+ // Clear cached family records that will no longer be valid.
+ for (auto& f : mReplacementCharFallbackFamily) {
+ f = FontFamily();
+ }
+
+ gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList);
+ mFontFamilyWhitelistActive = !mEnabledFontsList.IsEmpty();
+
+ LoadIconFontOverrideList();
+ }
+
+ // 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 (const auto& 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;
+ return false;
+ }
+ 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;
+
+ return true;
+}
+
+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;
+ }
+ }
+}
+
+void gfxPlatformFontList::FontListChanged() {
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ 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);
+ }
+ ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::Yes);
+}
+
+void gfxPlatformFontList::GenerateFontListKey(const nsACString& aKeyName,
+ nsACString& aResult) {
+ aResult = aKeyName;
+ ToLowerCase(aResult);
+}
+
+// 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) {
+ return true;
+ }
+
+ 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_AtStartup() > 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 (const auto& 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;
+ }
+
+ family->ReadFaceNames(this, NeedFullnamePostscriptNames());
+
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
+ timedOut = true;
+ break;
+ }
+ }
+
+ lookup = FindFaceName(aFaceName);
+
+ TimeStamp end = TimeStamp::Now();
+ Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS, start,
+ end);
+ if (LOG_FONTINIT_ENABLED()) {
+ TimeDuration elapsed = end - start;
+ LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
+ elapsed.ToMilliseconds(), (lookup ? "found name" : ""),
+ (timedOut ? "timeout" : "")));
+ }
+
+ return lookup;
+}
+
+gfxFontEntry* gfxPlatformFontList::FindFaceName(const nsACString& aFaceName) {
+ gfxFontEntry* lookup;
+
+ // lookup in name lookup tables, return null if not found
+ if (mExtraNames &&
+ ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
+ (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
+ return lookup;
+ }
+
+ return nullptr;
+}
+
+gfxFontEntry* gfxPlatformFontList::LookupInFaceNameLists(
+ const nsACString& aFaceName) {
+ gfxFontEntry* lookup = nullptr;
+
+ // 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::LoadBadUnderlineList() {
+ gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset",
+ mBadUnderlineFamilyNames);
+ for (auto& fam : mBadUnderlineFamilyNames) {
+ ToLowerCase(fam);
+ }
+ mBadUnderlineFamilyNames.Compact();
+ mBadUnderlineFamilyNames.Sort();
+}
+
+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;
+ ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::No);
+ }
+ }
+}
+
+bool gfxPlatformFontList::IsVisibleToCSS(const gfxFontFamily& aFamily,
+ FontVisibility aVisibility) const {
+ return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
+}
+
+bool gfxPlatformFontList::IsVisibleToCSS(const fontlist::Family& aFamily,
+ FontVisibility aVisibility) const {
+ return aFamily.Visibility() <= aVisibility || IsFontFamilyWhitelistActive();
+}
+
+void gfxPlatformFontList::GetFontList(nsAtom* aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts) {
+ AutoLock lock(mLock);
+
+ 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 (const auto& 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();
+ }
+ } else if (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;
+
+ LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
+
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
+ Script script = intl::UnicodeProperties::GetScriptCode(aCh);
+ MOZ_LOG(log, LogLevel::Warning,
+ ("(textrun-systemfallback-%s) char: u+%6.6x "
+ "script: %d match: [%s]"
+ " time: %dus cmaps: %d\n",
+ (common ? "common" : "global"), aCh, static_cast<int>(script),
+ (font ? font->GetFontEntry()->Name().get() : "<none>"),
+ int32_t(elapsed.ToMicroseconds()), cmapCount));
+ }
+
+ // 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
+ static bool first = true;
+ int32_t intElapsed =
+ int32_t(first ? elapsed.ToMilliseconds() : elapsed.ToMicroseconds());
+ Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST
+ : Telemetry::SYSTEM_FONT_FALLBACK),
+ intElapsed);
+ first = false;
+
+ // track the script for which fallback occurred (incremented one make it
+ // 1-based)
+ Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT,
+ int(aRunScript) + 1);
+
+ return font.forget();
+}
+
+#define NUM_FALLBACK_FONTS 8
+
+already_AddRefed<gfxFont> gfxPlatformFontList::CommonFontFallback(
+ nsPresContext* aPresContext, uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript, eFontPresentation aPresentation,
+ const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
+ AutoTArray<const char*, NUM_FALLBACK_FONTS> defaultFallbacks;
+ gfxPlatform::GetPlatform()->GetCommonFallbackFonts(
+ aCh, aRunScript, aPresentation, defaultFallbacks);
+ GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
+ FontVisibility level =
+ aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
+
+ // 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 (const auto 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 (const auto 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;
+ }
+ }
+
+ aCmapCount = data.mCmapsTested;
+ if (data.mBestMatch) {
+ aMatchedFamily = FontFamily(data.mMatchedFamily);
+ return data.mBestMatch->FindOrMakeFont(aMatchStyle);
+ }
+ }
+
+ return nullptr;
+}
+
+class StartCmapLoadingRunnable : public mozilla::Runnable {
+ public:
+ explicit StartCmapLoadingRunnable(uint32_t aStartIndex)
+ : Runnable("gfxPlatformFontList::StartCmapLoadingRunnable"),
+ mStartIndex(aStartIndex) {}
+
+ NS_IMETHOD Run() override {
+ auto* pfl = gfxPlatformFontList::PlatformFontList();
+ auto* list = pfl->SharedFontList();
+ if (!list) {
+ return NS_OK;
+ }
+ if (mStartIndex >= list->NumFamilies()) {
+ return NS_OK;
+ }
+ if (XRE_IsParentProcess()) {
+ pfl->StartCmapLoading(list->GetGeneration(), mStartIndex);
+ } else {
+ dom::ContentChild::GetSingleton()->SendStartCmapLoading(
+ list->GetGeneration(), mStartIndex);
+ }
+ return NS_OK;
+ }
+
+ private:
+ uint32_t mStartIndex;
+};
+
+void gfxPlatformFontList::StartCmapLoadingFromFamily(uint32_t aStartIndex) {
+ AutoLock lock(mLock);
+ if (aStartIndex > mStartedLoadingCmapsFrom) {
+ // We already initiated cmap-loading from somewhere 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 : public CancelableRunnable {
+ class WillShutdownObserver : public nsIObserver {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit WillShutdownObserver(LoadCmapsRunnable* aRunnable)
+ : mRunnable(aRunnable) {}
+
+ void Remove() {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+ }
+ mRunnable = nullptr;
+ }
+
+ protected:
+ virtual ~WillShutdownObserver() = default;
+
+ LoadCmapsRunnable* mRunnable;
+ };
+
+ public:
+ explicit LoadCmapsRunnable(uint32_t aGeneration, uint32_t aFamilyIndex)
+ : CancelableRunnable("gfxPlatformFontList::LoadCmapsRunnable"),
+ mGeneration(aGeneration),
+ mStartIndex(aFamilyIndex),
+ mIndex(aFamilyIndex) {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ mObserver = new WillShutdownObserver(this);
+ obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
+ }
+ }
+
+ virtual ~LoadCmapsRunnable() {
+ if (mObserver) {
+ mObserver->Remove();
+ }
+ }
+
+ // 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;
+ }
+ }
+
+ nsresult Cancel() override {
+ mIsCanceled = true;
+ return NS_OK;
+ }
+
+ 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();
+ // Skip any families that are already initialized.
+ while (mIndex < numFamilies && families[mIndex].IsFullyInitialized()) {
+ ++mIndex;
+ }
+ // Fully process one family, and advance index.
+ if (mIndex < numFamilies) {
+ Unused << pfl->InitializeFamily(&families[mIndex], true);
+ ++mIndex;
+ }
+ // If there are more families to initialize, post ourselves back to the
+ // idle queue to handle the next one; otherwise we're finished and we need
+ // to notify content processes to update their rendering.
+ if (mIndex < numFamilies) {
+ RefPtr<CancelableRunnable> task = this;
+ NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
+ } else {
+ pfl->Lock();
+ pfl->CancelLoadCmapsTask();
+ pfl->InitializeCodepointsWithNoFonts();
+ dom::ContentParent::NotifyUpdatedFonts(false);
+ pfl->Unlock();
+ }
+ return NS_OK;
+ }
+
+ private:
+ uint32_t mGeneration;
+ uint32_t mStartIndex;
+ uint32_t mIndex;
+ bool mIsCanceled = false;
+
+ RefPtr<WillShutdownObserver> mObserver;
+};
+
+NS_IMPL_ISUPPORTS(LoadCmapsRunnable::WillShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+LoadCmapsRunnable::WillShutdownObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
+ if (mRunnable) {
+ mRunnable->Cancel();
+ }
+ } else {
+ MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
+ }
+ 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.
+ static_cast<LoadCmapsRunnable*>(mLoadCmapsRunnable.get())
+ ->MaybeResetIndex(aStartIndex);
+ return;
+ }
+ mLoadCmapsRunnable = new LoadCmapsRunnable(aGeneration, aStartIndex);
+ RefPtr<CancelableRunnable> task = mLoadCmapsRunnable;
+ NS_DispatchToMainThreadQueue(task.forget(), EventQueuePriority::Idle);
+}
+
+gfxFontFamily* gfxPlatformFontList::CheckFamily(gfxFontFamily* aFamily) {
+ if (aFamily && !aFamily->HasStyles()) {
+ aFamily->FindStyleVariations();
+ }
+
+ 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;
+ }
+
+ return aFamily;
+}
+
+bool gfxPlatformFontList::FindAndAddFamiliesLocked(
+ nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
+ const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
+ FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
+ gfxFloat aDevToCssSize) {
+ nsAutoCString key;
+ GenerateFontListKey(aFamily, key);
+
+ bool allowHidden = bool(aFlags & FindFamiliesFlags::eSearchHiddenFamilies);
+ FontVisibility visibilityLevel =
+ aPresContext ? aPresContext->GetFontVisibility() : 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.
+ const char* 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));
+ return true;
+ }
+ if (aPresContext) {
+ aPresContext->ReportBlockedFontFamily(*family);
+ }
+ }
+ return false;
+ }
+
+ 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())) {
+ return false;
+ }
+ if (aPresContext) {
+ aPresContext->ReportBlockedFontFamily(*aFamily);
+ }
+ return true;
+ };
+
+ // lookup in canonical (i.e. English) family name list
+ gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
+ if (familyEntry) {
+ if (isBlockedByVisibilityLevel(familyEntry)) {
+ return false;
+ }
+ }
+
+ // if not found, lookup in other family names list (mostly localized names)
+ if (!familyEntry) {
+ familyEntry = mOtherFamilyNames.GetWeak(key);
+ }
+ if (familyEntry) {
+ if (isBlockedByVisibilityLevel(familyEntry)) {
+ return false;
+ }
+ }
+
+ // 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)) {
+ return false;
+ }
+ }
+ }
+
+ 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
+ const char* 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)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ if (familyEntry) {
+ aOutput->AppendElement(FamilyAndGeneric(familyEntry, aGeneric));
+ return true;
+ }
+
+ return false;
+}
+
+void gfxPlatformFontList::AddToMissedNames(const nsCString& aKey) {
+ if (!mOtherNamesMissed) {
+ mOtherNamesMissed = MakeUnique<nsTHashSet<nsCString>>(2);
+ }
+ mOtherNamesMissed->Insert(aKey);
+}
+
+fontlist::Family* gfxPlatformFontList::FindSharedFamily(
+ nsPresContext* aPresContext, const nsACString& aFamily,
+ FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
+ gfxFloat aDevToCss) {
+ if (!SharedFontList()) {
+ return nullptr;
+ }
+ AutoTArray<FamilyAndGeneric, 1> families;
+ if (!FindAndAddFamiliesLocked(aPresContext, StyleGenericFontFamily::None,
+ aFamily, &families, aFlags, aStyle, aLanguage,
+ aDevToCss) ||
+ !families[0].mFamily.mShared) {
+ return nullptr;
+ }
+ fontlist::Family* family = families[0].mFamily.mShared;
+ if (!family->IsInitialized()) {
+ if (!InitializeFamily(family)) {
+ return nullptr;
+ }
+ }
+ return family;
+}
+
+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) {
+ return false;
+ }
+ uint32_t index = aFamily - families;
+ if (index >= list->NumFamilies()) {
+ return false;
+ }
+ 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);
+ }
+
+ return aFamily->IsInitialized();
+}
+
+gfxFontEntry* gfxPlatformFontList::FindFontForFamily(
+ nsPresContext* aPresContext, const nsACString& aFamily,
+ const gfxFontStyle* aStyle) {
+ AutoLock lock(mLock);
+
+ nsAutoCString key;
+ GenerateFontListKey(aFamily, key);
+
+ FontFamily family = FindFamily(aPresContext, key);
+ if (family.IsNull()) {
+ return nullptr;
+ }
+ if (family.mShared) {
+ auto face = family.mShared->FindFaceForStyle(SharedFontList(), *aStyle);
+ if (!face) {
+ return nullptr;
+ }
+ return GetOrCreateFontEntryLocked(face, family.mShared);
+ }
+ return family.mUnshared->FindFontForStyle(*aStyle);
+}
+
+gfxFontEntry* gfxPlatformFontList::GetOrCreateFontEntryLocked(
+ fontlist::Face* aFace, const fontlist::Family* aFamily) {
+ return mFontEntries
+ .LookupOrInsertWith(aFace,
+ [=] { return CreateFontEntry(aFace, aFamily); })
+ .get();
+}
+
+void gfxPlatformFontList::AddOtherFamilyNames(
+ gfxFontFamily* aFamilyEntry, const nsTArray<nsCString>& aOtherFamilyNames) {
+ AutoLock lock(mLock);
+
+ for (const auto& name : aOtherFamilyNames) {
+ nsAutoCString key;
+ GenerateFontListKey(name, key);
+
+ mOtherFamilyNames.LookupOrInsertWith(key, [&] {
+ LOG_FONTLIST(
+ ("(fontlist-otherfamily) canonical family: %s, other family: %s\n",
+ aFamilyEntry->Name().get(), name.get()));
+ if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
+ aFamilyEntry->SetBadUnderlineFamily();
+ }
+ return RefPtr{aFamilyEntry};
+ });
+ }
+}
+
+void gfxPlatformFontList::AddFullnameLocked(gfxFontEntry* aFontEntry,
+ const nsCString& aFullname) {
+ mExtraNames->mFullnames.LookupOrInsertWith(aFullname, [&] {
+ LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
+ aFontEntry->Name().get(), aFullname.get()));
+ return RefPtr{aFontEntry};
+ });
+}
+
+void gfxPlatformFontList::AddPostscriptNameLocked(
+ gfxFontEntry* aFontEntry, const nsCString& aPostscriptName) {
+ mExtraNames->mPostscriptNames.LookupOrInsertWith(aPostscriptName, [&] {
+ LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
+ aFontEntry->Name().get(), aPostscriptName.get()));
+ return RefPtr{aFontEntry};
+ });
+}
+
+bool gfxPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
+ nsACString& aFamilyName) {
+ AutoLock lock(mLock);
+ FontFamily family = FindFamily(nullptr, aFontName);
+ if (family.IsNull()) {
+ return false;
+ }
+ return GetLocalizedFamilyName(family, aFamilyName);
+}
+
+bool gfxPlatformFontList::GetLocalizedFamilyName(const FontFamily& aFamily,
+ nsACString& aFamilyName) {
+ if (aFamily.mShared) {
+ aFamilyName = SharedFontList()->LocalizedFamilyName(aFamily.mShared);
+ return true;
+ }
+ if (aFamily.mUnshared) {
+ aFamily.mUnshared->LocalizedName(aFamilyName);
+ return true;
+ }
+ return false; // leaving the aFamilyName outparam untouched
+}
+
+FamilyAndGeneric gfxPlatformFontList::GetDefaultFontFamily(
+ const nsACString& aLangGroup, const nsACString& aGenericFamily) {
+ if (NS_WARN_IF(aLangGroup.IsEmpty()) ||
+ NS_WARN_IF(aGenericFamily.IsEmpty())) {
+ return FamilyAndGeneric();
+ }
+
+ AutoLock lock(mLock);
+
+ nsAutoCString value;
+ AutoTArray<nsCString, 4> names;
+ if (mFontPrefs->LookupNameList(PrefName(aGenericFamily, aLangGroup), value)) {
+ gfxFontUtils::ParseFontList(value, names);
+ }
+
+ for (const nsCString& name : names) {
+ FontFamily family = FindFamily(nullptr, name);
+ if (!family.IsNull()) {
+ return FamilyAndGeneric(family);
+ }
+ }
+
+ return FamilyAndGeneric();
+}
+
+ShmemCharMapHashEntry::ShmemCharMapHashEntry(const gfxSparseBitSet* aCharMap)
+ : mList(gfxPlatformFontList::PlatformFontList()->SharedFontList()),
+ mCharMap(),
+ mHash(aCharMap->GetChecksum()) {
+ size_t len = SharedBitSet::RequiredSize(*aCharMap);
+ mCharMap = mList->Alloc(len);
+ SharedBitSet::Create(mCharMap.ToPtr(mList, len), len, *aCharMap);
+}
+
+fontlist::Pointer gfxPlatformFontList::GetShmemCharMapLocked(
+ const gfxSparseBitSet* aCmap) {
+ auto* entry = mShmemCharMaps.GetEntry(aCmap);
+ if (!entry) {
+ entry = mShmemCharMaps.PutEntry(aCmap);
+ }
+ return entry->GetCharMap();
+}
+
+// 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);
+ }
+}
+
+static void GetSystemUIFontFamilies([[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 (!LookAndFeel::GetFont(StyleSystemFont::Menu, systemFontName, fontStyle)) {
+ return;
+ }
+ systemFontName.Trim("\"'");
+ CopyUTF16toUTF8(systemFontName, *aFamilies.AppendElement());
+}
+
+void gfxPlatformFontList::ResolveGenericFontNames(
+ nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
+ eFontPrefLang aPrefLang, PrefFontList* aGenericFamilies) {
+ const char* langGroupStr = GetPrefLangName(aPrefLang);
+ const char* generic = GetGenericName(aGenericType);
+
+ if (!generic) {
+ return;
+ }
+
+ AutoTArray<nsCString, 4> genericFamilies;
+
+ // load family for "font.name.generic.lang"
+ PrefName prefName(generic, langGroupStr);
+ nsAutoCString value;
+ if (mFontPrefs->LookupName(prefName, value)) {
+ gfxFontUtils::ParseFontList(value, genericFamilies);
+ }
+
+ // load fonts for "font.name-list.generic.lang"
+ if (mFontPrefs->LookupNameList(prefName, value)) {
+ gfxFontUtils::ParseFontList(value, genericFamilies);
+ }
+
+ nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
+ MOZ_ASSERT(langGroup, "null lang group for pref lang");
+
+ if (aGenericType == StyleGenericFontFamily::SystemUi) {
+ GetSystemUIFontFamilies(langGroup, genericFamilies);
+ }
+
+ GetFontFamiliesFromGenericFamilies(
+ aPresContext, aGenericType, genericFamilies, langGroup, aGenericFamilies);
+
+#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);
+ }
+
+ GetFontFamiliesFromGenericFamilies(
+ aPresContext, StyleGenericFontFamily::MozEmoji, genericFamilies, nullptr,
+ aGenericFamilies);
+}
+
+void gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(
+ nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
+ nsTArray<nsCString>& aGenericNameFamilies, nsAtom* aLangGroup,
+ PrefFontList* aGenericFamilies) {
+ // lookup and add platform fonts uniquely
+ for (const nsCString& genericFamily : aGenericNameFamilies) {
+ AutoTArray<FamilyAndGeneric, 10> families;
+ FindAndAddFamiliesLocked(aPresContext, aGenericType, genericFamily,
+ &families, FindFamiliesFlags(0), nullptr,
+ aLangGroup);
+ for (const FamilyAndGeneric& f : families) {
+ if (!aGenericFamilies->Contains(f.mFamily)) {
+ aGenericFamilies->AppendElement(f.mFamily);
+ }
+ }
+ }
+}
+
+gfxPlatformFontList::PrefFontList*
+gfxPlatformFontList::GetPrefFontsLangGroupLocked(
+ nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
+ eFontPrefLang aPrefLang) {
+ if (aGenericType == StyleGenericFontFamily::MozEmoji ||
+ aPrefLang == eFontPrefLang_Emoji) {
+ // Emoji font has no lang
+ PrefFontList* prefFonts = mEmojiPrefFont.get();
+ if (MOZ_UNLIKELY(!prefFonts)) {
+ prefFonts = new PrefFontList;
+ ResolveEmojiFontNames(aPresContext, prefFonts);
+ mEmojiPrefFont.reset(prefFonts);
+ }
+ return prefFonts;
+ }
+
+ auto index = static_cast<size_t>(aGenericType);
+ PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][index].get();
+ if (MOZ_UNLIKELY(!prefFonts)) {
+ prefFonts = new PrefFontList;
+ ResolveGenericFontNames(aPresContext, aGenericType, aPrefLang, prefFonts);
+ mLangGroupPrefFonts[aPrefLang][index].reset(prefFonts);
+ }
+ return prefFonts;
+}
+
+void gfxPlatformFontList::AddGenericFonts(
+ nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
+ nsAtom* aLanguage, nsTArray<FamilyAndGeneric>& aFamilyList) {
+ AutoLock lock(mLock);
+
+ // map lang ==> langGroup
+ nsAtom* langGroup = GetLangGroup(aLanguage);
+
+ // langGroup ==> prefLang
+ eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
+
+ // lookup pref fonts
+ PrefFontList* prefFonts =
+ GetPrefFontsLangGroupLocked(aPresContext, aGenericType, prefLang);
+
+ if (!prefFonts->IsEmpty()) {
+ aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
+ for (auto& f : *prefFonts) {
+ aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
+ }
+ }
+}
+
+static nsAtom* PrefLangToLangGroups(uint32_t aIndex) {
+ // static array here avoids static constructor
+ static nsAtom* gPrefLangToLangGroups[] = {
+#define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
+#include "gfxFontPrefLangList.h"
+#undef FONT_PREF_LANG
+ };
+
+ return aIndex < ArrayLength(gPrefLangToLangGroups)
+ ? gPrefLangToLangGroups[aIndex]
+ : nsGkAtoms::Unicode;
+}
+
+eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(const char* aLang) {
+ if (!aLang || !aLang[0]) {
+ return eFontPrefLang_Others;
+ }
+ for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
+ if (!nsCRT::strcasecmp(gPrefLangNames[i], aLang)) {
+ return eFontPrefLang(i);
+ }
+ }
+ return eFontPrefLang_Others;
+}
+
+eFontPrefLang gfxPlatformFontList::GetFontPrefLangFor(nsAtom* aLang) {
+ if (!aLang) return eFontPrefLang_Others;
+ nsAutoCString lang;
+ aLang->ToUTF8String(lang);
+ return GetFontPrefLangFor(lang.get());
+}
+
+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");
+
+ return PrefLangToLangGroups(uint32_t(aLang));
+}
+
+const char* gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang) {
+ if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
+ return gPrefLangNames[uint32_t(aLang)];
+ }
+ return nullptr;
+}
+
+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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[],
+ uint32_t& aLen, eFontPrefLang aCharLang,
+ eFontPrefLang aPageLang) {
+ AutoLock lock(mLock);
+ if (IsLangCJK(aCharLang)) {
+ AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
+ } else {
+ AppendPrefLang(aPrefLangs, aLen, aCharLang);
+ }
+
+ AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
+}
+
+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 (const auto& 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);
+
+ {
+ Locale locale;
+ if (LocaleParser::TryParse(localeStr, locale).isOk() &&
+ locale.Canonicalize().isOk()) {
+ if (locale.Language().EqualTo("ja")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ } else if (locale.Language().EqualTo("zh")) {
+ if (locale.Region().EqualTo("CN")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ } else if (locale.Region().EqualTo("TW")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+ } else if (locale.Region().EqualTo("HK")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ }
+ } else if (locale.Language().EqualTo("ko")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+ }
+ }
+ }
+
+ // 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);
+
+ AutoTArray<nsCString, 16> sysLocales;
+ AutoTArray<nsCString, 16> negLocales;
+ if (NS_SUCCEEDED(
+ OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) {
+ LocaleService::GetInstance()->NegotiateLanguages(
+ sysLocales, prefLocales, ""_ns,
+ LocaleService::kLangNegStrategyFiltering, negLocales);
+ for (const auto& localeStr : negLocales) {
+ Locale locale;
+ if (LocaleParser::TryParse(localeStr, locale).isOk() &&
+ locale.Canonicalize().isOk()) {
+ if (locale.Language().EqualTo("ja")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ } else if (locale.Language().EqualTo("zh")) {
+ if (locale.Region().EqualTo("CN")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ } else if (locale.Region().EqualTo("TW")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+ } else if (locale.Region().EqualTo("HK")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ }
+ } else if (locale.Language().EqualTo("ko")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+ }
+ }
+ }
+ }
+
+ // Last resort... set up CJK font prefs in the order listed by the user-
+ // configurable ordering pref.
+ gfxFontUtils::GetPrefsFontList(kCJKFallbackOrderPref, list);
+ for (const auto& 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 (const auto lang : Span<eFontPrefLang>(tempPrefLangs, tempLen)) {
+ mCJKPrefLangs.AppendElement(lang);
+ }
+ }
+
+ // append in cached CJK langs
+ for (const auto lang : mCJKPrefLangs) {
+ AppendPrefLang(aPrefLangs, aLen, eFontPrefLang(lang));
+ }
+}
+
+void gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[],
+ uint32_t& aLen,
+ eFontPrefLang aAddLang) {
+ if (aLen >= kMaxLenPrefLangList) {
+ return;
+ }
+
+ // If the lang is already present, just ignore the addition.
+ for (const auto lang : Span<eFontPrefLang>(aPrefLangs, aLen)) {
+ if (lang == aAddLang) {
+ return;
+ }
+ }
+
+ aPrefLangs[aLen++] = aAddLang;
+}
+
+StyleGenericFontFamily gfxPlatformFontList::GetDefaultGeneric(
+ eFontPrefLang aLang) {
+ if (aLang == eFontPrefLang_Emoji) {
+ return StyleGenericFontFamily::MozEmoji;
+ }
+
+ AutoLock lock(mLock);
+
+ if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
+ return mDefaultGenericsLangGroup[uint32_t(aLang)];
+ }
+ return StyleGenericFontFamily::Serif;
+}
+
+FontFamily gfxPlatformFontList::GetDefaultFont(nsPresContext* aPresContext,
+ const gfxFontStyle* aStyle) {
+ AutoLock lock(mLock);
+ return GetDefaultFontLocked(aPresContext, aStyle);
+}
+
+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;
+}
+
+void gfxPlatformFontList::InitLoader() {
+ GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
+ mStartIndex = 0;
+ mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
+ memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
+}
+
+#define FONT_LOADER_MAX_TIMESLICE \
+ 20 // max time for one pass through RunLoader = 20ms
+
+bool gfxPlatformFontList::LoadFontInfo() {
+ AutoLock lock(mLock);
+ TimeStamp start = TimeStamp::Now();
+ uint32_t i, endIndex = mNumFamilies;
+ fontlist::FontList* list = SharedFontList();
+ bool loadCmaps =
+ !list && (!UsesSystemFallback() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback());
+
+ // 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_AtStartup() > 0) {
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
+ i + 1 != endIndex) {
+ endIndex = i + 1;
+ break;
+ }
+ }
+ }
+
+ mStartIndex = endIndex;
+ bool done = mStartIndex >= mNumFamilies;
+
+ if (LOG_FONTINIT_ENABLED()) {
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
+ elapsed.ToMilliseconds(), (done ? "true" : "false")));
+ }
+
+ if (done) {
+ mOtherFamilyNamesInitialized = true;
+ CancelInitOtherFamilyNamesTask();
+ mFaceNameListsInitialized = true;
+ }
+
+ return done;
+}
+
+void gfxPlatformFontList::CleanupLoader() {
+ AutoLock lock(mLock);
+
+ mFontFamiliesToLoad.Clear();
+ mNumFamilies = 0;
+ bool rebuilt = false, forceReflow = false;
+
+ // if had missed face names that are now available, force reflow all
+ if (mFaceNamesMissed) {
+ rebuilt = std::any_of(mFaceNamesMissed->cbegin(), mFaceNamesMissed->cend(),
+ [&](const auto& key) {
+ mLock.AssertCurrentThreadIn();
+ return FindFaceName(key);
+ });
+ if (rebuilt) {
+ RebuildLocalFonts();
+ }
+
+ mFaceNamesMissed = nullptr;
+ }
+
+ if (mOtherNamesMissed) {
+ forceReflow = std::any_of(
+ mOtherNamesMissed->cbegin(), mOtherNamesMissed->cend(),
+ [&](const auto& key) {
+ mLock.AssertCurrentThreadIn();
+ return FindUnsharedFamily(
+ nullptr, key,
+ (FindFamiliesFlags::eForceOtherFamilyNamesLoading |
+ FindFamiliesFlags::eNoAddToNamesMissedWhenSearching));
+ });
+ if (forceReflow) {
+ ForceGlobalReflowLocked(gfxPlatform::NeedsReframe::No);
+ }
+
+ mOtherNamesMissed = nullptr;
+ }
+
+ if (LOG_FONTINIT_ENABLED() && mFontInfo) {
+ LOG_FONTINIT(
+ ("(fontinit) fontloader load thread took %8.2f ms "
+ "%d families %d fonts %d cmaps "
+ "%d facenames %d othernames %s %s",
+ mLoadTime.ToMilliseconds(), mFontInfo->mLoadStats.families,
+ mFontInfo->mLoadStats.fonts, mFontInfo->mLoadStats.cmaps,
+ mFontInfo->mLoadStats.facenames, mFontInfo->mLoadStats.othernames,
+ (rebuilt ? "(userfont sets rebuilt)" : ""),
+ (forceReflow ? "(global reflow)" : "")));
+ }
+
+ gfxFontInfoLoader::CleanupLoader();
+}
+
+void gfxPlatformFontList::ForceGlobalReflowLocked(
+ gfxPlatform::NeedsReframe aNeedsReframe,
+ gfxPlatform::BroadcastToChildren aBroadcastToChildren) {
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "gfxPlatformFontList::ForceGlobalReflowLocked",
+ [aNeedsReframe, aBroadcastToChildren] {
+ gfxPlatform::ForceGlobalReflow(aNeedsReframe, aBroadcastToChildren);
+ }));
+ return;
+ }
+
+ AutoUnlock unlock(mLock);
+ gfxPlatform::ForceGlobalReflow(aNeedsReframe, aBroadcastToChildren);
+}
+
+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_AtStartup());
+ 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) {
+ for (auto* fontset : mUserFontSetList) {
+ 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.
+
+ return oldValue + key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ });
+}
+
+void gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const {
+ AutoLock lock(mLock);
+
+ aSizes->mFontListSize +=
+ mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& entry : mFontFamilies) {
+ aSizes->mFontListSize +=
+ entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ entry.GetData()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
+ }
+
+ aSizes->mFontListSize +=
+ SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
+
+ if (mExtraNames) {
+ aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
+ mExtraNames->mFullnames, aMallocSizeOf);
+ aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
+ mExtraNames->mPostscriptNames, aMallocSizeOf);
+ }
+
+ for (uint32_t i = eFontPrefLang_First;
+ i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
+ auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
+ for (const UniquePtr<PrefFontList>& pf : prefFontsLangGroup) {
+ if (pf) {
+ aSizes->mFontListSize += pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+ }
+ }
+
+ for (const auto& bitset : mCodepointsWithNoFonts) {
+ aSizes->mFontListSize += bitset.SizeOfExcludingThis(aMallocSizeOf);
+ }
+ aSizes->mFontListSize +=
+ mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ aSizes->mFontListSize +=
+ mBadUnderlineFamilyNames.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& i : mBadUnderlineFamilyNames) {
+ aSizes->mFontListSize += i.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ aSizes->mFontListSize +=
+ mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& entry : mSharedCmaps) {
+ aSizes->mCharMapsSize += entry.GetKey()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ aSizes->mFontListSize +=
+ mFontEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& entry : mFontEntries.Values()) {
+ if (entry) {
+ entry->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
+ }
+ }
+
+ if (SharedFontList()) {
+ aSizes->mFontListSize +=
+ SharedFontList()->SizeOfIncludingThis(aMallocSizeOf);
+ if (XRE_IsParentProcess()) {
+ aSizes->mSharedSize += SharedFontList()->AllocatedShmemSize();
+ }
+ }
+}
+
+void gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const {
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+void gfxPlatformFontList::InitOtherFamilyNamesInternal(
+ bool aDeferOtherFamilyNamesLoading) {
+ if (mOtherFamilyNamesInitialized) {
+ return;
+ }
+
+ AutoLock lock(mLock);
+
+ if (aDeferOtherFamilyNamesLoading) {
+ TimeStamp start = TimeStamp::Now();
+ bool timedOut = false;
+
+ 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 (!timedOut) {
+ mOtherFamilyNamesInitialized = true;
+ CancelInitOtherFamilyNamesTask();
+ }
+ TimeStamp end = TimeStamp::Now();
+ Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
+ start, end);
+
+ if (LOG_FONTINIT_ENABLED()) {
+ TimeDuration elapsed = end - start;
+ 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);
+ }
+ }
+
+ mOtherFamilyNamesInitialized = true;
+ CancelInitOtherFamilyNamesTask();
+
+ TimeStamp end = TimeStamp::Now();
+ Telemetry::AccumulateTimeDelta(
+ Telemetry::FONTLIST_INITOTHERFAMILYNAMES_NO_DEFERRING, start, end);
+
+ if (LOG_FONTINIT_ENABLED()) {
+ TimeDuration elapsed = end - start;
+ 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 (forceReflow) {
+ dom::ContentParent::BroadcastFontListChanged();
+ }
+ }
+}
+
+void gfxPlatformFontList::ShareFontListShmBlockToProcess(
+ uint32_t aGeneration, uint32_t aIndex, base::ProcessId aPid,
+ base::SharedMemoryHandle* aOut) {
+ auto list = SharedFontList();
+ if (!list) {
+ return;
+ }
+ if (!aGeneration || list->GetGeneration() == aGeneration) {
+ list->ShareShmBlockToProcess(aIndex, aPid, aOut);
+ } else {
+ *aOut = base::SharedMemory::NULLHandle();
+ }
+}
+
+void gfxPlatformFontList::ShareFontListToProcess(
+ nsTArray<base::SharedMemoryHandle>* aBlocks, base::ProcessId aPid) {
+ auto list = SharedFontList();
+ if (list) {
+ list->ShareBlocksToProcess(aBlocks, aPid);
+ }
+}
+
+base::SharedMemoryHandle gfxPlatformFontList::ShareShmBlockToProcess(
+ uint32_t aIndex, base::ProcessId aPid) {
+ MOZ_RELEASE_ASSERT(SharedFontList());
+ return SharedFontList()->ShareBlockToProcess(aIndex, aPid);
+}
+
+void gfxPlatformFontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
+ base::SharedMemoryHandle aHandle) {
+ if (SharedFontList()) {
+ AutoLock lock(mLock);
+ SharedFontList()->ShmBlockAdded(aGeneration, aIndex, std::move(aHandle));
+ }
+}
+
+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,
+ const fontlist::Pointer& aFacePtr,
+ 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;
+ }
+ auto* face = aFacePtr.ToPtr<fontlist::Face>(list);
+ if (face) {
+ face->mCharacterMap = GetShmemCharMap(&aMap);
+ }
+}
+
+void gfxPlatformFontList::SetupFamilyCharMap(
+ uint32_t aGeneration, const fontlist::Pointer& aFamilyPtr) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ auto list = SharedFontList();
+ MOZ_ASSERT(list);
+ if (!list) {
+ return;
+ }
+ if (list->GetGeneration() != aGeneration) {
+ return;
+ }
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ return;
+ }
+
+ // aFamilyPtr was passed from a content process which may not be trusted,
+ // so we cannot assume it is valid or safe to use. If the Pointer value is
+ // bad, we must not crash or do anything bad, just bail out.
+ // (In general, if the child process was trying to use an invalid pointer it
+ // should have hit the MOZ_DIAGNOSTIC_ASSERT in FontList::ToSharedPointer
+ // rather than passing a null or bad pointer to the parent.)
+
+ auto* family = aFamilyPtr.ToPtr<fontlist::Family>(list);
+ if (!family) {
+ // Unable to resolve to a native pointer (or it was null).
+ NS_WARNING("unexpected null Family pointer");
+ return;
+ }
+
+ // Validate the pointer before trying to use it: check that it points to a
+ // correctly-aligned offset within the Families() or AliasFamilies() array.
+ // We just assert (in debug builds only) on failure, and return safely.
+ // A misaligned pointer here would indicate a buggy (or compromised) child
+ // process, but crashing the parent would be unnecessary and does not yield
+ // any useful insight.
+ if (family >= list->Families() &&
+ family < list->Families() + list->NumFamilies()) {
+ size_t offset = (char*)family - (char*)list->Families();
+ if (offset % sizeof(fontlist::Family) != 0) {
+ MOZ_ASSERT(false, "misaligned Family pointer");
+ return;
+ }
+ } else if (family >= list->AliasFamilies() &&
+ family < list->AliasFamilies() + list->NumAliases()) {
+ size_t offset = (char*)family - (char*)list->AliasFamilies();
+ if (offset % sizeof(fontlist::Family) != 0) {
+ MOZ_ASSERT(false, "misaligned Family pointer");
+ return;
+ }
+ } else {
+ MOZ_ASSERT(false, "not a valid Family or AliasFamily pointer");
+ return;
+ }
+
+ family->SetupFamilyCharMap(list);
+}
+
+bool gfxPlatformFontList::InitOtherFamilyNames(uint32_t aGeneration,
+ bool aDefer) {
+ auto list = SharedFontList();
+ MOZ_ASSERT(list);
+ if (!list) {
+ return false;
+ }
+ if (list->GetGeneration() != aGeneration) {
+ return false;
+ }
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ return false;
+ }
+ return InitOtherFamilyNames(aDefer);
+}
+
+uint32_t gfxPlatformFontList::GetGeneration() const {
+ return SharedFontList() ? SharedFontList()->GetGeneration() : 0;
+}
+
+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() {
+ 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");
+}
+
+bool gfxPlatformFontList::FontPrefs::LookupName(const nsACString& aPref,
+ nsACString& aValue) const {
+ if (const auto& value = mFontName.Lookup(aPref)) {
+ aValue = *value;
+ return true;
+ }
+ return false;
+}
+
+bool gfxPlatformFontList::FontPrefs::LookupNameList(const nsACString& aPref,
+ nsACString& aValue) const {
+ if (const auto& value = mFontNameList.Lookup(aPref)) {
+ aValue = *value;
+ return true;
+ }
+ return false;
+}
+
+bool gfxPlatformFontList::IsKnownIconFontFamily(
+ const nsAtom* aFamilyName) const {
+ nsAtomCString fam(aFamilyName);
+ ToLowerCase(fam);
+ return mIconFontsSet.Contains(fam);
+}
+
+#undef LOG
+#undef LOG_ENABLED