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.cpp1678
1 files changed, 1678 insertions, 0 deletions
diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp
new file mode 100644
index 000000000..01394db14
--- /dev/null
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -0,0 +1,1678 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "gfxPlatformFontList.h"
+#include "gfxTextRun.h"
+#include "gfxUserFontSet.h"
+
+#include "nsCRT.h"
+#include "nsGkAtoms.h"
+#include "nsILocaleService.h"
+#include "nsServiceManagerUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsUnicodeRange.h"
+#include "nsUnicodeProperties.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/gfx/2D.h"
+
+#include <locale.h>
+
+using namespace mozilla;
+
+#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, 0x06FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
+ { 0x0700, 0x074F, { TRUETYPE_TAG('s','y','r','c'), 0, 0 } },
+ { 0x0750, 0x077F, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
+ { 0x08A0, 0x08FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } },
+ { 0x0900, 0x097F, { TRUETYPE_TAG('d','e','v','2'),
+ TRUETYPE_TAG('d','e','v','a'), 0 } },
+ { 0x0980, 0x09FF, { TRUETYPE_TAG('b','n','g','2'),
+ TRUETYPE_TAG('b','e','n','g'), 0 } },
+ { 0x0A00, 0x0A7F, { TRUETYPE_TAG('g','u','r','2'),
+ TRUETYPE_TAG('g','u','r','u'), 0 } },
+ { 0x0A80, 0x0AFF, { TRUETYPE_TAG('g','j','r','2'),
+ TRUETYPE_TAG('g','u','j','r'), 0 } },
+ { 0x0B00, 0x0B7F, { TRUETYPE_TAG('o','r','y','2'),
+ TRUETYPE_TAG('o','r','y','a'), 0 } },
+ { 0x0B80, 0x0BFF, { TRUETYPE_TAG('t','m','l','2'),
+ TRUETYPE_TAG('t','a','m','l'), 0 } },
+ { 0x0C00, 0x0C7F, { TRUETYPE_TAG('t','e','l','2'),
+ TRUETYPE_TAG('t','e','l','u'), 0 } },
+ { 0x0C80, 0x0CFF, { TRUETYPE_TAG('k','n','d','2'),
+ TRUETYPE_TAG('k','n','d','a'), 0 } },
+ { 0x0D00, 0x0D7F, { TRUETYPE_TAG('m','l','m','2'),
+ TRUETYPE_TAG('m','l','y','m'), 0 } },
+ { 0x0D80, 0x0DFF, { TRUETYPE_TAG('s','i','n','h'), 0, 0 } },
+ { 0x0E80, 0x0EFF, { TRUETYPE_TAG('l','a','o',' '), 0, 0 } },
+ { 0x0F00, 0x0FFF, { TRUETYPE_TAG('t','i','b','t'), 0, 0 } },
+ { 0x1000, 0x109f, { TRUETYPE_TAG('m','y','m','r'),
+ TRUETYPE_TAG('m','y','m','2'), 0 } },
+ { 0x1780, 0x17ff, { TRUETYPE_TAG('k','h','m','r'), 0, 0 } },
+ // Khmer Symbols (19e0..19ff) don't seem to need any special shaping
+ { 0xaa60, 0xaa7f, { 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 } } // terminator
+};
+
+// prefs for the font info loader
+#define FONT_LOADER_FAMILIES_PER_SLICE_PREF "gfx.font_loader.families_per_slice"
+#define FONT_LOADER_DELAY_PREF "gfx.font_loader.delay"
+#define FONT_LOADER_INTERVAL_PREF "gfx.font_loader.interval"
+
+static const char* kObservedPrefs[] = {
+ "font.",
+ "font.name-list.",
+ "intl.accept_languages", // hmmmm...
+ nullptr
+};
+
+static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
+
+// 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() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
+
+NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
+
+NS_IMETHODIMP
+gfxFontListPrefObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), "invalid topic");
+ // 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();
+ gfxFontCache::GetCache()->AgeAllGenerations();
+ 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;
+
+ 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.");
+ }
+
+ return NS_OK;
+}
+
+gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
+ : mFontFamilies(64), mOtherFamilyNames(16),
+ mBadUnderlineFamilyNames(8), mSharedCmaps(8),
+ mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0),
+ mFontFamilyWhitelistActive(false)
+{
+ mOtherFamilyNamesInitialized = false;
+
+ if (aNeedFullnamePostscriptNames) {
+ mExtraNames = MakeUnique<ExtraNames>();
+ }
+ mFaceNameListsInitialized = false;
+
+ LoadBadUnderlineList();
+
+ // pref changes notification setup
+ NS_ASSERTION(!gFontListPrefObserver,
+ "There has been font list pref observer already");
+ gFontListPrefObserver = new gfxFontListPrefObserver();
+ NS_ADDREF(gFontListPrefObserver);
+ Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
+
+ Preferences::RegisterCallback(FontWhitelistPrefChanged,
+ kFontSystemWhitelistPref);
+
+ RegisterStrongMemoryReporter(new MemoryReporter());
+}
+
+gfxPlatformFontList::~gfxPlatformFontList()
+{
+ mSharedCmaps.Clear();
+ ClearLangGroupPrefFonts();
+ NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
+ Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
+ Preferences::UnregisterCallback(FontWhitelistPrefChanged,
+ kFontSystemWhitelistPref);
+ NS_RELEASE(gFontListPrefObserver);
+}
+
+// number of CSS generic font families
+const uint32_t kNumGenerics = 5;
+
+void
+gfxPlatformFontList::ApplyWhitelist()
+{
+ nsTArray<nsString> list;
+ gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, list);
+ uint32_t numFonts = list.Length();
+ mFontFamilyWhitelistActive = (numFonts > 0);
+ if (!mFontFamilyWhitelistActive) {
+ return;
+ }
+ nsTHashtable<nsStringHashKey> familyNamesWhitelist;
+ for (uint32_t i = 0; i < numFonts; i++) {
+ nsString key;
+ ToLowerCase(list[i], key);
+ familyNamesWhitelist.PutEntry(key);
+ }
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ // Don't continue if we only have one font left.
+ if (mFontFamilies.Count() == 1) {
+ break;
+ }
+ nsString fontFamilyName(iter.Key());
+ ToLowerCase(fontFamilyName);
+ if (!familyNamesWhitelist.Contains(fontFamilyName)) {
+ iter.Remove();
+ }
+ }
+}
+
+nsresult
+gfxPlatformFontList::InitFontList()
+{
+ mFontlistInitCount++;
+
+ if (LOG_FONTINIT_ENABLED()) {
+ LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
+ }
+
+ // rebuilding fontlist so clear out font/word caches
+ gfxFontCache *fontCache = gfxFontCache::GetCache();
+ if (fontCache) {
+ fontCache->AgeAllGenerations();
+ fontCache->FlushShapedWordCaches();
+ }
+
+ gfxPlatform::PurgeSkiaFontCache();
+
+ mFontFamilies.Clear();
+ mOtherFamilyNames.Clear();
+ mOtherFamilyNamesInitialized = false;
+ if (mExtraNames) {
+ mExtraNames->mFullnames.Clear();
+ mExtraNames->mPostscriptNames.Clear();
+ }
+ mFaceNameListsInitialized = false;
+ ClearLangGroupPrefFonts();
+ mReplacementCharFallbackFamily = nullptr;
+ CancelLoader();
+
+ // initialize ranges of characters for which system-wide font search should be skipped
+ mCodepointsWithNoFonts.reset();
+ mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls
+ mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls
+
+ sPlatformFontList = this;
+
+ nsresult rv = InitFontListForPlatform();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ ApplyWhitelist();
+ return NS_OK;
+}
+
+void
+gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
+{
+ aResult = aKeyName;
+ ToLowerCase(aResult);
+}
+
+#define OTHERNAMES_TIMEOUT 200
+
+void
+gfxPlatformFontList::InitOtherFamilyNames()
+{
+ if (mOtherFamilyNamesInitialized) {
+ return;
+ }
+
+ TimeStamp start = TimeStamp::Now();
+ bool timedOut = false;
+
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ family->ReadOtherFamilyNames(this);
+ TimeDuration elapsed = TimeStamp::Now() - start;
+ if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
+ timedOut = true;
+ break;
+ }
+ }
+
+ if (!timedOut) {
+ mOtherFamilyNamesInitialized = true;
+ }
+ 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" : "")));
+ }
+}
+
+// time limit for loading facename lists (ms)
+#define NAMELIST_TIMEOUT 200
+
+gfxFontEntry*
+gfxPlatformFontList::SearchFamiliesForFaceName(const nsAString& 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 (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ nsStringHashKey::KeyType key = iter.Key();
+ RefPtr<gfxFontFamily>& family = iter.Data();
+
+ // 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 nsAString& 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 nsAString& 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<nsTHashtable<nsStringHashKey>>(2);
+ }
+ mFaceNamesMissed->PutEntry(aFaceName);
+ }
+ }
+
+ return lookup;
+}
+
+void
+gfxPlatformFontList::PreloadNamesList()
+{
+ AutoTArray<nsString, 10> preloadFonts;
+ gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
+
+ uint32_t numFonts = preloadFonts.Length();
+ for (uint32_t i = 0; i < numFonts; i++) {
+ nsAutoString key;
+ GenerateFontListKey(preloadFonts[i], key);
+
+ // only search canonical names!
+ gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
+ if (familyEntry) {
+ familyEntry->ReadOtherFamilyNames(this);
+ }
+ }
+
+}
+
+void
+gfxPlatformFontList::LoadBadUnderlineList()
+{
+ AutoTArray<nsString, 10> blacklist;
+ gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
+ uint32_t numFonts = blacklist.Length();
+ for (uint32_t i = 0; i < numFonts; i++) {
+ nsAutoString key;
+ GenerateFontListKey(blacklist[i], key);
+ mBadUnderlineFamilyNames.PutEntry(key);
+ }
+}
+
+void
+gfxPlatformFontList::UpdateFontList()
+{
+ InitFontList();
+ RebuildLocalFonts();
+}
+
+void
+gfxPlatformFontList::GetFontList(nsIAtom *aLangGroup,
+ const nsACString& aGenericFamily,
+ nsTArray<nsString>& aListOfFonts)
+{
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ // use the first variation for now. This data should be the same
+ // for all the variations and should probably be moved up to
+ // the Family
+ gfxFontStyle style;
+ style.language = aLangGroup;
+ bool needsBold;
+ RefPtr<gfxFontEntry> fontEntry = family->FindFontForStyle(style, needsBold);
+ NS_ASSERTION(fontEntry, "couldn't find any font entry in family");
+ if (!fontEntry) {
+ continue;
+ }
+
+ /* skip symbol fonts */
+ if (fontEntry->IsSymbolFont()) {
+ continue;
+ }
+
+ if (fontEntry->SupportsLangGroup(aLangGroup) &&
+ fontEntry->MatchesGenericFamily(aGenericFamily)) {
+ nsAutoString localizedFamilyName;
+ family->LocalizedName(localizedFamilyName);
+ aListOfFonts.AppendElement(localizedFamilyName);
+ }
+ }
+
+ aListOfFonts.Sort();
+ aListOfFonts.Compact();
+}
+
+void
+gfxPlatformFontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
+{
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ aFamilyArray.AppendElement(family);
+ }
+}
+
+gfxFontEntry*
+gfxPlatformFontList::SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ const gfxFontStyle* aStyle)
+ {
+ gfxFontEntry* fontEntry = nullptr;
+
+ // is codepoint with no matching font? return null immediately
+ if (mCodepointsWithNoFonts.test(aCh)) {
+ return nullptr;
+ }
+
+ // 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 && mReplacementCharFallbackFamily) {
+ bool needsBold; // ignored in the system fallback case
+
+ fontEntry =
+ mReplacementCharFallbackFamily->FindFontForStyle(*aStyle,
+ needsBold);
+
+ // 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;
+ }
+ }
+
+ TimeStamp start = TimeStamp::Now();
+
+ // search commonly available fonts
+ bool common = true;
+ gfxFontFamily *fallbackFamily = nullptr;
+ fontEntry = CommonFontFallback(aCh, aNextCh, aRunScript, aStyle,
+ &fallbackFamily);
+
+ // if didn't find a font, do system-wide fallback (except for specials)
+ uint32_t cmapCount = 0;
+ if (!fontEntry) {
+ common = false;
+ fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount,
+ &fallbackFamily);
+ }
+ TimeDuration elapsed = TimeStamp::Now() - start;
+
+ LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
+
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
+ uint32_t unicodeRange = FindCharUnicodeRange(aCh);
+ Script script = mozilla::unicode::GetScriptCode(aCh);
+ MOZ_LOG(log, LogLevel::Warning,\
+ ("(textrun-systemfallback-%s) char: u+%6.6x "
+ "unicode-range: %d script: %d match: [%s]"
+ " time: %dus cmaps: %d\n",
+ (common ? "common" : "global"), aCh,
+ unicodeRange, script,
+ (fontEntry ? NS_ConvertUTF16toUTF8(fontEntry->Name()).get() :
+ "<none>"),
+ int32_t(elapsed.ToMicroseconds()),
+ cmapCount));
+ }
+
+ // no match? add to set of non-matching codepoints
+ if (!fontEntry) {
+ mCodepointsWithNoFonts.set(aCh);
+ } else if (aCh == 0xFFFD && fontEntry && fallbackFamily) {
+ mReplacementCharFallbackFamily = 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 fontEntry;
+}
+
+#define NUM_FALLBACK_FONTS 8
+
+gfxFontEntry*
+gfxPlatformFontList::CommonFontFallback(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ gfxFontFamily** aMatchedFamily)
+{
+ AutoTArray<const char*,NUM_FALLBACK_FONTS> defaultFallbacks;
+ uint32_t i, numFallbacks;
+
+ gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aNextCh,
+ aRunScript,
+ defaultFallbacks);
+ numFallbacks = defaultFallbacks.Length();
+ for (i = 0; i < numFallbacks; i++) {
+ nsAutoString familyName;
+ const char *fallbackFamily = defaultFallbacks[i];
+
+ familyName.AppendASCII(fallbackFamily);
+ gfxFontFamily *fallback = FindFamilyByCanonicalName(familyName);
+ if (!fallback)
+ continue;
+
+ gfxFontEntry *fontEntry;
+ bool needsBold; // ignored in the system fallback case
+
+ // use first font in list that supports a given character
+ fontEntry = fallback->FindFontForStyle(*aMatchStyle, needsBold);
+ if (fontEntry && fontEntry->HasCharacter(aCh)) {
+ *aMatchedFamily = fallback;
+ return fontEntry;
+ }
+ }
+
+ return nullptr;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::GlobalFontFallback(const uint32_t aCh,
+ Script aRunScript,
+ const gfxFontStyle* aMatchStyle,
+ uint32_t& aCmapCount,
+ gfxFontFamily** aMatchedFamily)
+{
+ bool useCmaps = IsFontFamilyWhitelistActive() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+ if (!useCmaps) {
+ // Allow platform-specific fallback code to try and find a usable font
+ gfxFontEntry* fe =
+ PlatformGlobalFontFallback(aCh, aRunScript, aMatchStyle,
+ aMatchedFamily);
+ if (fe) {
+ return fe;
+ }
+ }
+
+ // otherwise, try to find it among local fonts
+ GlobalFontMatch data(aCh, aRunScript, aMatchStyle);
+
+ // iterate over all font families to find a font that support the character
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ // evaluate all fonts in this family for a match
+ family->FindFontForChar(&data);
+ }
+
+ aCmapCount = data.mCmapsTested;
+ *aMatchedFamily = data.mMatchedFamily;
+
+ return data.mBestMatch;
+}
+
+gfxFontFamily*
+gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily)
+{
+ if (aFamily && !aFamily->HasStyles()) {
+ aFamily->FindStyleVariations();
+ aFamily->CheckForSimpleFamily();
+ }
+
+ if (aFamily && aFamily->GetFontList().Length() == 0) {
+ // failed to load any faces for this family, so discard it
+ nsAutoString key;
+ GenerateFontListKey(aFamily->Name(), key);
+ mFontFamilies.Remove(key);
+ return nullptr;
+ }
+
+ return aFamily;
+}
+
+bool
+gfxPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
+ nsTArray<gfxFontFamily*>* aOutput,
+ gfxFontStyle* aStyle,
+ gfxFloat aDevToCssSize)
+{
+ nsAutoString key;
+ GenerateFontListKey(aFamily, key);
+
+ NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
+
+ // lookup in canonical (i.e. English) family name list
+ gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
+
+ // if not found, lookup in other family names list (mostly localized names)
+ if (!familyEntry) {
+ familyEntry = mOtherFamilyNames.GetWeak(key);
+ }
+
+ // 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();
+ familyEntry = mOtherFamilyNames.GetWeak(key);
+ if (!familyEntry && !mOtherFamilyNamesInitialized) {
+ // localized family names load timed out, add name to list of
+ // names to check after localized names are loaded
+ if (!mOtherNamesMissed) {
+ mOtherNamesMissed = MakeUnique<nsTHashtable<nsStringHashKey>>(2);
+ }
+ mOtherNamesMissed->PutEntry(key);
+ }
+ }
+
+ familyEntry = CheckFamily(familyEntry);
+ if (familyEntry) {
+ aOutput->AppendElement(familyEntry);
+ return true;
+ }
+
+ return false;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold)
+{
+ gfxFontFamily *familyEntry = FindFamily(aFamily);
+
+ aNeedsBold = false;
+
+ if (familyEntry)
+ return familyEntry->FindFontForStyle(*aStyle, aNeedsBold);
+
+ return nullptr;
+}
+
+void
+gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName)
+{
+ nsAutoString key;
+ GenerateFontListKey(aOtherFamilyName, key);
+
+ if (!mOtherFamilyNames.GetWeak(key)) {
+ mOtherFamilyNames.Put(key, aFamilyEntry);
+ LOG_FONTLIST(("(fontlist-otherfamily) canonical family: %s, "
+ "other family: %s\n",
+ NS_ConvertUTF16toUTF8(aFamilyEntry->Name()).get(),
+ NS_ConvertUTF16toUTF8(aOtherFamilyName).get()));
+ if (mBadUnderlineFamilyNames.Contains(key))
+ aFamilyEntry->SetBadUnderlineFamily();
+ }
+}
+
+void
+gfxPlatformFontList::AddFullname(gfxFontEntry *aFontEntry, nsAString& aFullname)
+{
+ if (!mExtraNames->mFullnames.GetWeak(aFullname)) {
+ mExtraNames->mFullnames.Put(aFullname, aFontEntry);
+ LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n",
+ NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(),
+ NS_ConvertUTF16toUTF8(aFullname).get()));
+ }
+}
+
+void
+gfxPlatformFontList::AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName)
+{
+ if (!mExtraNames->mPostscriptNames.GetWeak(aPostscriptName)) {
+ mExtraNames->mPostscriptNames.Put(aPostscriptName, aFontEntry);
+ LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n",
+ NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(),
+ NS_ConvertUTF16toUTF8(aPostscriptName).get()));
+ }
+}
+
+bool
+gfxPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+ aFamilyName.Truncate();
+ gfxFontFamily *ff = FindFamily(aFontName);
+ if (!ff) {
+ return false;
+ }
+ aFamilyName.Assign(ff->Name());
+ return true;
+}
+
+gfxCharacterMap*
+gfxPlatformFontList::FindCharMap(gfxCharacterMap *aCmap)
+{
+ aCmap->CalcHash();
+ gfxCharacterMap *cmap = AddCmap(aCmap);
+ cmap->mShared = true;
+ return cmap;
+}
+
+// add a cmap to the shared cmap set
+gfxCharacterMap*
+gfxPlatformFontList::AddCmap(const gfxCharacterMap* aCharMap)
+{
+ CharMapHashKey *found =
+ mSharedCmaps.PutEntry(const_cast<gfxCharacterMap*>(aCharMap));
+ return found->GetKey();
+}
+
+// remove the cmap from the shared cmap set
+void
+gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap)
+{
+ // skip lookups during teardown
+ if (mSharedCmaps.Count() == 0) {
+ return;
+ }
+
+ // cmap needs to match the entry *and* be the same ptr before removing
+ CharMapHashKey *found =
+ mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
+ if (found && found->GetKey() == aCharMap) {
+ mSharedCmaps.RemoveEntry(const_cast<gfxCharacterMap*>(aCharMap));
+ }
+}
+
+void
+gfxPlatformFontList::ResolveGenericFontNames(
+ FontFamilyType aGenericType,
+ eFontPrefLang aPrefLang,
+ nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
+{
+ const char* langGroupStr = GetPrefLangName(aPrefLang);
+ const char* generic = GetGenericName(aGenericType);
+
+ if (!generic) {
+ return;
+ }
+
+ AutoTArray<nsString,4> genericFamilies;
+
+ // load family for "font.name.generic.lang"
+ nsAutoCString prefFontName("font.name.");
+ prefFontName.Append(generic);
+ prefFontName.Append('.');
+ prefFontName.Append(langGroupStr);
+ gfxFontUtils::AppendPrefsFontList(prefFontName.get(), genericFamilies);
+
+ // load fonts for "font.name-list.generic.lang"
+ nsAutoCString prefFontListName("font.name-list.");
+ prefFontListName.Append(generic);
+ prefFontListName.Append('.');
+ prefFontListName.Append(langGroupStr);
+ gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies);
+
+ nsIAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
+ NS_ASSERTION(langGroup, "null lang group for pref lang");
+
+ // lookup and add platform fonts uniquely
+ for (const nsString& genericFamily : genericFamilies) {
+ gfxFontStyle style;
+ style.language = langGroup;
+ style.systemFont = false;
+ AutoTArray<gfxFontFamily*,10> families;
+ FindAndAddFamilies(genericFamily, &families, &style);
+ for (gfxFontFamily* f : families) {
+ if (!aGenericFamilies->Contains(f)) {
+ aGenericFamilies->AppendElement(f);
+ }
+ }
+ }
+
+#if 0 // dump out generic mappings
+ printf("%s ===> ", prefFontName.get());
+ for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
+ if (k > 0) printf(", ");
+ printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]->Name()).get());
+ }
+ printf("\n");
+#endif
+}
+
+nsTArray<RefPtr<gfxFontFamily>>*
+gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
+ eFontPrefLang aPrefLang)
+{
+ // treat -moz-fixed as monospace
+ if (aGenericType == eFamily_moz_fixed) {
+ aGenericType = eFamily_monospace;
+ }
+
+ PrefFontList* prefFonts =
+ mLangGroupPrefFonts[aPrefLang][aGenericType].get();
+ if (MOZ_UNLIKELY(!prefFonts)) {
+ prefFonts = new PrefFontList;
+ ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts);
+ mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts);
+ }
+ return prefFonts;
+}
+
+void
+gfxPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
+ nsIAtom* aLanguage,
+ nsTArray<gfxFontFamily*>& aFamilyList)
+{
+ // map lang ==> langGroup
+ nsIAtom* langGroup = GetLangGroup(aLanguage);
+
+ // langGroup ==> prefLang
+ eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
+
+ // lookup pref fonts
+ nsTArray<RefPtr<gfxFontFamily>>* prefFonts =
+ GetPrefFontsLangGroup(aGenericType, prefLang);
+
+ if (!prefFonts->IsEmpty()) {
+ aFamilyList.AppendElements(*prefFonts);
+ }
+}
+
+static nsIAtom* PrefLangToLangGroups(uint32_t aIndex)
+{
+ // static array here avoids static constructor
+ static nsIAtom* 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 (!PL_strcasecmp(gPrefLangNames[i], aLang)) {
+ return eFontPrefLang(i);
+ }
+ }
+ return eFontPrefLang_Others;
+}
+
+eFontPrefLang
+gfxPlatformFontList::GetFontPrefLangFor(nsIAtom *aLang)
+{
+ if (!aLang)
+ return eFontPrefLang_Others;
+ nsAutoCString lang;
+ aLang->ToUTF8String(lang);
+ return GetFontPrefLangFor(lang.get());
+}
+
+nsIAtom*
+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(uint8_t aUnicodeRange)
+{
+ switch (aUnicodeRange) {
+ case kRangeSetLatin: return eFontPrefLang_Western;
+ case kRangeCyrillic: return eFontPrefLang_Cyrillic;
+ case kRangeGreek: return eFontPrefLang_Greek;
+ case kRangeHebrew: return eFontPrefLang_Hebrew;
+ case kRangeArabic: return eFontPrefLang_Arabic;
+ case kRangeThai: return eFontPrefLang_Thai;
+ case kRangeKorean: return eFontPrefLang_Korean;
+ case kRangeJapanese: return eFontPrefLang_Japanese;
+ case kRangeSChinese: return eFontPrefLang_ChineseCN;
+ case kRangeTChinese: return eFontPrefLang_ChineseTW;
+ case kRangeDevanagari: return eFontPrefLang_Devanagari;
+ case kRangeTamil: return eFontPrefLang_Tamil;
+ case kRangeArmenian: return eFontPrefLang_Armenian;
+ case kRangeBengali: return eFontPrefLang_Bengali;
+ case kRangeCanadian: return eFontPrefLang_Canadian;
+ case kRangeEthiopic: return eFontPrefLang_Ethiopic;
+ case kRangeGeorgian: return eFontPrefLang_Georgian;
+ case kRangeGujarati: return eFontPrefLang_Gujarati;
+ case kRangeGurmukhi: return eFontPrefLang_Gurmukhi;
+ case kRangeKhmer: return eFontPrefLang_Khmer;
+ case kRangeMalayalam: return eFontPrefLang_Malayalam;
+ case kRangeOriya: return eFontPrefLang_Oriya;
+ case kRangeTelugu: return eFontPrefLang_Telugu;
+ case kRangeKannada: return eFontPrefLang_Kannada;
+ case kRangeSinhala: return eFontPrefLang_Sinhala;
+ case kRangeTibetan: return eFontPrefLang_Tibetan;
+ case kRangeSetCJK: return eFontPrefLang_CJKSet;
+ 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)
+{
+ 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
+ nsAdoptingCString list = Preferences::GetLocalizedCString("intl.accept_languages");
+ if (!list.IsEmpty()) {
+ const char kComma = ',';
+ const char *p, *p_end;
+ list.BeginReading(p);
+ list.EndReading(p_end);
+ while (p < p_end) {
+ while (nsCRT::IsAsciiSpace(*p)) {
+ if (++p == p_end)
+ break;
+ }
+ if (p == p_end)
+ break;
+ const char *start = p;
+ while (++p != p_end && *p != kComma)
+ /* nothing */ ;
+ nsAutoCString lang(Substring(start, p));
+ lang.CompressWhitespace(false, true);
+ eFontPrefLang fpl = gfxPlatformFontList::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;
+ }
+ p++;
+ }
+ }
+
+ do { // to allow 'break' to abort this block if a call fails
+ nsresult rv;
+ nsCOMPtr<nsILocaleService> ls =
+ do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ break;
+
+ nsCOMPtr<nsILocale> appLocale;
+ rv = ls->GetApplicationLocale(getter_AddRefs(appLocale));
+ if (NS_FAILED(rv))
+ break;
+
+ nsString localeStr;
+ rv = appLocale->
+ GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr);
+ if (NS_FAILED(rv))
+ break;
+
+ const nsAString& lang = Substring(localeStr, 0, 2);
+ if (lang.EqualsLiteral("ja")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ } else if (lang.EqualsLiteral("zh")) {
+ const nsAString& region = Substring(localeStr, 3, 2);
+ if (region.EqualsLiteral("CN")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ } else if (region.EqualsLiteral("TW")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+ } else if (region.EqualsLiteral("HK")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ }
+ } else if (lang.EqualsLiteral("ko")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+ }
+ } while (0);
+
+ // last resort... (the order is same as old gfx.)
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+
+ // copy into the cached array
+ uint32_t j;
+ for (j = 0; j < tempLen; j++) {
+ mCJKPrefLangs.AppendElement(tempPrefLangs[j]);
+ }
+ }
+
+ // append in cached CJK langs
+ uint32_t i, numCJKlangs = mCJKPrefLangs.Length();
+
+ for (i = 0; i < numCJKlangs; i++) {
+ AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i]));
+ }
+
+}
+
+void
+gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang)
+{
+ if (aLen >= kMaxLenPrefLangList) return;
+
+ // make sure
+ uint32_t i = 0;
+ while (i < aLen && aPrefLangs[i] != aAddLang) {
+ i++;
+ }
+
+ if (i == aLen) {
+ aPrefLangs[aLen] = aAddLang;
+ aLen++;
+ }
+}
+
+mozilla::FontFamilyType
+gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang)
+{
+ // initialize lang group pref font defaults (i.e. serif/sans-serif)
+ if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) {
+ mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
+ for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
+ nsAutoCString prefDefaultFontType("font.default.");
+ prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
+ nsAdoptingCString serifOrSans =
+ Preferences::GetCString(prefDefaultFontType.get());
+ if (serifOrSans.EqualsLiteral("sans-serif")) {
+ mDefaultGenericsLangGroup[i] = eFamily_sans_serif;
+ } else {
+ mDefaultGenericsLangGroup[i] = eFamily_serif;
+ }
+ }
+ }
+
+ if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
+ return mDefaultGenericsLangGroup[uint32_t(aLang)];
+ }
+ return eFamily_serif;
+}
+
+
+gfxFontFamily*
+gfxPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+{
+ gfxFontFamily* family = GetDefaultFontForPlatform(aStyle);
+ if (family) {
+ 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 listed in
+ // mFontFamilies.
+ return mFontFamilies.Iter().Data();
+}
+
+void
+gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
+{
+ for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<gfxFontFamily>& family = iter.Data();
+ aFontFamilyNames.AppendElement(family->Name());
+ }
+}
+
+nsILanguageAtomService*
+gfxPlatformFontList::GetLangService()
+{
+ if (!mLangService) {
+ mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
+ }
+ NS_ASSERTION(mLangService, "no language service!");
+ return mLangService;
+}
+
+nsIAtom*
+gfxPlatformFontList::GetLangGroup(nsIAtom* aLanguage)
+{
+ // map lang ==> langGroup
+ nsIAtom *langGroup = nullptr;
+ if (aLanguage) {
+ nsresult rv;
+ nsILanguageAtomService* langService = GetLangService();
+ langGroup = langService->GetLanguageGroup(aLanguage, &rv);
+ }
+ if (!langGroup) {
+ langGroup = nsGkAtoms::Unicode;
+ }
+ return langGroup;
+}
+
+/* static */ const char*
+gfxPlatformFontList::GetGenericName(FontFamilyType aGenericType)
+{
+ static const char kGeneric_serif[] = "serif";
+ static const char kGeneric_sans_serif[] = "sans-serif";
+ static const char kGeneric_monospace[] = "monospace";
+ static const char kGeneric_cursive[] = "cursive";
+ static const char kGeneric_fantasy[] = "fantasy";
+
+ // type should be standard generic type at this point
+ NS_ASSERTION(aGenericType >= eFamily_serif &&
+ aGenericType <= eFamily_fantasy,
+ "standard generic font family type required");
+
+ // map generic type to string
+ const char *generic = nullptr;
+ switch (aGenericType) {
+ case eFamily_serif:
+ generic = kGeneric_serif;
+ break;
+ case eFamily_sans_serif:
+ generic = kGeneric_sans_serif;
+ break;
+ case eFamily_monospace:
+ generic = kGeneric_monospace;
+ break;
+ case eFamily_cursive:
+ generic = kGeneric_cursive;
+ break;
+ case eFamily_fantasy:
+ generic = kGeneric_fantasy;
+ break;
+ default:
+ break;
+ }
+
+ return generic;
+}
+
+// mapping of moz lang groups ==> default lang
+struct MozLangGroupData {
+ nsIAtom* const& mozLangGroup;
+ const char *defaultLang;
+};
+
+const MozLangGroupData MozLangGroups[] = {
+ { nsGkAtoms::x_western, "en" },
+ { nsGkAtoms::x_cyrillic, "ru" },
+ { nsGkAtoms::x_devanagari, "hi" },
+ { nsGkAtoms::x_tamil, "ta" },
+ { nsGkAtoms::x_armn, "hy" },
+ { nsGkAtoms::x_beng, "bn" },
+ { nsGkAtoms::x_cans, "iu" },
+ { nsGkAtoms::x_ethi, "am" },
+ { nsGkAtoms::x_geor, "ka" },
+ { nsGkAtoms::x_gujr, "gu" },
+ { nsGkAtoms::x_guru, "pa" },
+ { nsGkAtoms::x_khmr, "km" },
+ { nsGkAtoms::x_knda, "kn" },
+ { nsGkAtoms::x_mlym, "ml" },
+ { nsGkAtoms::x_orya, "or" },
+ { nsGkAtoms::x_sinh, "si" },
+ { nsGkAtoms::x_tamil, "ta" },
+ { nsGkAtoms::x_telu, "te" },
+ { nsGkAtoms::x_tibt, "bo" },
+ { nsGkAtoms::Unicode, 0 }
+};
+
+bool
+gfxPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
+ nsIAtom* aLangGroup,
+ nsACString& aFcLang)
+{
+ // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
+ // aOSLang is in the form "language[_territory][.codeset][@modifier]".
+ // fontconfig takes languages in the form "language-territory".
+ // nsILanguageAtomService takes languages in the form language-subtag,
+ // where subtag may be a territory. fontconfig and nsILanguageAtomService
+ // handle case-conversion for us.
+ const char *pos, *end;
+ aOSLang.BeginReading(pos);
+ aOSLang.EndReading(end);
+ aFcLang.Truncate();
+ while (pos < end) {
+ switch (*pos) {
+ case '.':
+ case '@':
+ end = pos;
+ break;
+ case '_':
+ aFcLang.Append('-');
+ break;
+ default:
+ aFcLang.Append(*pos);
+ }
+ ++pos;
+ }
+
+ nsILanguageAtomService* langService = GetLangService();
+ nsIAtom *atom = langService->LookupLanguage(aFcLang);
+ return atom == aLangGroup;
+}
+
+void
+gfxPlatformFontList::GetSampleLangForGroup(nsIAtom* aLanguage,
+ nsACString& aLangStr,
+ bool aCheckEnvironment)
+{
+ aLangStr.Truncate();
+ if (!aLanguage) {
+ return;
+ }
+
+ // set up lang string
+ const MozLangGroupData *mozLangGroup = nullptr;
+
+ // -- look it up in the list of moz lang groups
+ for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
+ if (aLanguage == MozLangGroups[i].mozLangGroup) {
+ mozLangGroup = &MozLangGroups[i];
+ break;
+ }
+ }
+
+ // -- not a mozilla lang group? Just return the BCP47 string
+ // representation of the lang group
+ if (!mozLangGroup) {
+ // Not a special mozilla language group.
+ // Use aLanguage as a language code.
+ aLanguage->ToUTF8String(aLangStr);
+ return;
+ }
+
+ // -- check the environment for the user's preferred language that
+ // corresponds to this mozilla lang group.
+ if (aCheckEnvironment) {
+ const char *languages = getenv("LANGUAGE");
+ if (languages) {
+ const char separator = ':';
+
+ for (const char *pos = languages; true; ++pos) {
+ if (*pos == '\0' || *pos == separator) {
+ if (languages < pos &&
+ TryLangForGroup(Substring(languages, pos),
+ aLanguage, aLangStr))
+ return;
+
+ if (*pos == '\0')
+ break;
+
+ languages = pos + 1;
+ }
+ }
+ }
+ const char *ctype = setlocale(LC_CTYPE, nullptr);
+ if (ctype &&
+ TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr)) {
+ return;
+ }
+ }
+
+ if (mozLangGroup->defaultLang) {
+ aLangStr.Assign(mozLangGroup->defaultLang);
+ } else {
+ aLangStr.Truncate();
+ }
+}
+
+void
+gfxPlatformFontList::InitLoader()
+{
+ GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
+ mStartIndex = 0;
+ mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
+ memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
+}
+
+#define FONT_LOADER_MAX_TIMESLICE 100 // max time for one pass through RunLoader = 100ms
+
+bool
+gfxPlatformFontList::LoadFontInfo()
+{
+ TimeStamp start = TimeStamp::Now();
+ uint32_t i, endIndex = mNumFamilies;
+ bool loadCmaps = !UsesSystemFallback() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+ // for each font family, load in various font info
+ for (i = mStartIndex; i < endIndex; i++) {
+ nsAutoString key;
+ gfxFontFamily *familyEntry;
+ GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key);
+
+ // lookup in canonical (i.e. English) family name list
+ if (!(familyEntry = mFontFamilies.GetWeak(key))) {
+ 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
+ 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;
+ mFaceNameListsInitialized = true;
+ }
+
+ return done;
+}
+
+void
+gfxPlatformFontList::CleanupLoader()
+{
+ mFontFamiliesToLoad.Clear();
+ mNumFamilies = 0;
+ bool rebuilt = false, forceReflow = false;
+
+ // if had missed face names that are now available, force reflow all
+ if (mFaceNamesMissed) {
+ for (auto it = mFaceNamesMissed->Iter(); !it.Done(); it.Next()) {
+ if (FindFaceName(it.Get()->GetKey())) {
+ rebuilt = true;
+ RebuildLocalFonts();
+ break;
+ }
+ }
+ mFaceNamesMissed = nullptr;
+ }
+
+ if (mOtherNamesMissed) {
+ for (auto it = mOtherNamesMissed->Iter(); !it.Done(); it.Next()) {
+ if (FindFamily(it.Get()->GetKey())) {
+ forceReflow = true;
+ ForceGlobalReflow();
+ break;
+ }
+ }
+ 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::GetPrefsAndStartLoader()
+{
+ mIncrement =
+ std::max(1u, Preferences::GetUint(FONT_LOADER_FAMILIES_PER_SLICE_PREF));
+
+ uint32_t delay =
+ std::max(1u, Preferences::GetUint(FONT_LOADER_DELAY_PREF));
+ uint32_t interval =
+ std::max(1u, Preferences::GetUint(FONT_LOADER_INTERVAL_PREF));
+
+ StartLoader(delay, interval);
+}
+
+void
+gfxPlatformFontList::ForceGlobalReflow()
+{
+ // modify a preference that will trigger reflow everywhere
+ static const char kPrefName[] = "font.internaluseonly.changed";
+ bool fontInternalChange = Preferences::GetBool(kPrefName, false);
+ Preferences::SetBool(kPrefName, !fontInternalChange);
+}
+
+void
+gfxPlatformFontList::RebuildLocalFonts()
+{
+ for (auto it = mUserFontSetList.Iter(); !it.Done(); it.Next()) {
+ it.Get()->GetKey()->RebuildLocalRules();
+ }
+}
+
+void
+gfxPlatformFontList::ClearLangGroupPrefFonts()
+{
+ for (uint32_t i = eFontPrefLang_First;
+ i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
+ auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
+ for (uint32_t j = eFamily_generic_first;
+ j < eFamily_generic_first + eFamily_generic_count; j++) {
+ prefFontsLangGroup[j] = nullptr;
+ }
+ }
+}
+
+// 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)
+{
+ size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+ // 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.
+ n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+ return n;
+}
+
+/*static*/ size_t
+gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
+ const FontEntryTable& aTable,
+ MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+ // The font itself is counted by its owning family; here we only care
+ // about the names stored in the hashtable keys.
+ n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+ return n;
+}
+
+void
+gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize +=
+ mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mFontFamilies.ConstIter(); !iter.Done(); iter.Next()) {
+ aSizes->mFontListSize +=
+ iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ iter.Data()->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 (uint32_t j = eFamily_generic_first;
+ j < eFamily_generic_first + eFamily_generic_count; j++) {
+ PrefFontList* pf = prefFontsLangGroup[j].get();
+ if (pf) {
+ aSizes->mFontListSize +=
+ pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+ }
+ }
+
+ aSizes->mFontListSize +=
+ mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf);
+ aSizes->mFontListSize +=
+ mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ aSizes->mFontListSize +=
+ mBadUnderlineFamilyNames.SizeOfExcludingThis(aMallocSizeOf);
+
+ aSizes->mFontListSize +=
+ mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
+ aSizes->mCharMapsSize +=
+ iter.Get()->GetKey()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+}
+
+void
+gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const
+{
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+bool
+gfxPlatformFontList::IsFontFamilyWhitelistActive()
+{
+ return mFontFamilyWhitelistActive;
+}
+
+#undef LOG
+#undef LOG_ENABLED