michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG /* Allow logging in the release build */ michael@0: #endif michael@0: #include "prlog.h" michael@0: michael@0: #include "gfxPlatformFontList.h" michael@0: #include "gfxUserFontSet.h" michael@0: michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsUnicodeRange.h" michael@0: #include "nsUnicodeProperties.h" michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #ifdef PR_LOGGING michael@0: michael@0: #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ michael@0: PR_LOG_DEBUG, args) michael@0: #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \ michael@0: gfxPlatform::GetLog(eGfxLog_fontlist), \ michael@0: PR_LOG_DEBUG) michael@0: #define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \ michael@0: PR_LOG_DEBUG, args) michael@0: #define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \ michael@0: gfxPlatform::GetLog(eGfxLog_fontinit), \ michael@0: PR_LOG_DEBUG) michael@0: michael@0: #endif // PR_LOGGING michael@0: michael@0: gfxPlatformFontList *gfxPlatformFontList::sPlatformFontList = nullptr; michael@0: michael@0: // Character ranges that require complex-script shaping support in the font, michael@0: // and so should be masked out by ReadCMAP if the necessary layout tables michael@0: // are not present. michael@0: // Currently used by the Mac and FT2 implementations only, but probably should michael@0: // be supported on Windows as well. michael@0: const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = { michael@0: // Actually, now that harfbuzz supports presentation-forms shaping for michael@0: // Arabic, we can render it without layout tables. So maybe we don't michael@0: // want to mask the basic Arabic block here? michael@0: // This affects the arabic-fallback-*.html reftests, which rely on michael@0: // loading a font that *doesn't* have any GSUB table. michael@0: { 0x0600, 0x06FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } }, michael@0: { 0x0700, 0x074F, { TRUETYPE_TAG('s','y','r','c'), 0, 0 } }, michael@0: { 0x0750, 0x077F, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } }, michael@0: { 0x08A0, 0x08FF, { TRUETYPE_TAG('a','r','a','b'), 0, 0 } }, michael@0: { 0x0900, 0x097F, { TRUETYPE_TAG('d','e','v','2'), michael@0: TRUETYPE_TAG('d','e','v','a'), 0 } }, michael@0: { 0x0980, 0x09FF, { TRUETYPE_TAG('b','n','g','2'), michael@0: TRUETYPE_TAG('b','e','n','g'), 0 } }, michael@0: { 0x0A00, 0x0A7F, { TRUETYPE_TAG('g','u','r','2'), michael@0: TRUETYPE_TAG('g','u','r','u'), 0 } }, michael@0: { 0x0A80, 0x0AFF, { TRUETYPE_TAG('g','j','r','2'), michael@0: TRUETYPE_TAG('g','u','j','r'), 0 } }, michael@0: { 0x0B00, 0x0B7F, { TRUETYPE_TAG('o','r','y','2'), michael@0: TRUETYPE_TAG('o','r','y','a'), 0 } }, michael@0: { 0x0B80, 0x0BFF, { TRUETYPE_TAG('t','m','l','2'), michael@0: TRUETYPE_TAG('t','a','m','l'), 0 } }, michael@0: { 0x0C00, 0x0C7F, { TRUETYPE_TAG('t','e','l','2'), michael@0: TRUETYPE_TAG('t','e','l','u'), 0 } }, michael@0: { 0x0C80, 0x0CFF, { TRUETYPE_TAG('k','n','d','2'), michael@0: TRUETYPE_TAG('k','n','d','a'), 0 } }, michael@0: { 0x0D00, 0x0D7F, { TRUETYPE_TAG('m','l','m','2'), michael@0: TRUETYPE_TAG('m','l','y','m'), 0 } }, michael@0: { 0x0D80, 0x0DFF, { TRUETYPE_TAG('s','i','n','h'), 0, 0 } }, michael@0: { 0x0E80, 0x0EFF, { TRUETYPE_TAG('l','a','o',' '), 0, 0 } }, michael@0: { 0x0F00, 0x0FFF, { TRUETYPE_TAG('t','i','b','t'), 0, 0 } }, michael@0: { 0x1000, 0x109f, { TRUETYPE_TAG('m','y','m','r'), michael@0: TRUETYPE_TAG('m','y','m','2'), 0 } }, michael@0: { 0x1780, 0x17ff, { TRUETYPE_TAG('k','h','m','r'), 0, 0 } }, michael@0: // Khmer Symbols (19e0..19ff) don't seem to need any special shaping michael@0: { 0xaa60, 0xaa7f, { TRUETYPE_TAG('m','y','m','r'), michael@0: TRUETYPE_TAG('m','y','m','2'), 0 } }, michael@0: // Thai seems to be "renderable" without AAT morphing tables michael@0: { 0, 0, { 0, 0, 0 } } // terminator michael@0: }; michael@0: michael@0: // prefs for the font info loader michael@0: #define FONT_LOADER_FAMILIES_PER_SLICE_PREF "gfx.font_loader.families_per_slice" michael@0: #define FONT_LOADER_DELAY_PREF "gfx.font_loader.delay" michael@0: #define FONT_LOADER_INTERVAL_PREF "gfx.font_loader.interval" michael@0: michael@0: static const char* kObservedPrefs[] = { michael@0: "font.", michael@0: "font.name-list.", michael@0: "intl.accept_languages", // hmmmm... michael@0: nullptr michael@0: }; michael@0: michael@0: class gfxFontListPrefObserver MOZ_FINAL : public nsIObserver { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: }; michael@0: michael@0: static gfxFontListPrefObserver* gFontListPrefObserver = nullptr; michael@0: michael@0: NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: gfxFontListPrefObserver::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), "invalid topic"); michael@0: // XXX this could be made to only clear out the cache for the prefs that were changed michael@0: // but it probably isn't that big a deal. michael@0: gfxPlatformFontList::PlatformFontList()->ClearPrefFonts(); michael@0: gfxFontCache::GetCache()->AgeAllGenerations(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf) michael@0: michael@0: NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter) michael@0: michael@0: NS_IMETHODIMP michael@0: gfxPlatformFontList::MemoryReporter::CollectReports michael@0: (nsIMemoryReporterCallback* aCb, michael@0: nsISupports* aClosure) michael@0: { michael@0: FontListSizes sizes; michael@0: sizes.mFontListSize = 0; michael@0: sizes.mFontTableCacheSize = 0; michael@0: sizes.mCharMapsSize = 0; michael@0: michael@0: gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(&FontListMallocSizeOf, michael@0: &sizes); michael@0: michael@0: aCb->Callback(EmptyCString(), michael@0: NS_LITERAL_CSTRING("explicit/gfx/font-list"), michael@0: KIND_HEAP, UNITS_BYTES, sizes.mFontListSize, michael@0: NS_LITERAL_CSTRING("Memory used to manage the list of font families and faces."), michael@0: aClosure); michael@0: michael@0: aCb->Callback(EmptyCString(), michael@0: NS_LITERAL_CSTRING("explicit/gfx/font-charmaps"), michael@0: KIND_HEAP, UNITS_BYTES, sizes.mCharMapsSize, michael@0: NS_LITERAL_CSTRING("Memory used to record the character coverage of individual fonts."), michael@0: aClosure); michael@0: michael@0: if (sizes.mFontTableCacheSize) { michael@0: aCb->Callback(EmptyCString(), michael@0: NS_LITERAL_CSTRING("explicit/gfx/font-tables"), michael@0: KIND_HEAP, UNITS_BYTES, sizes.mFontTableCacheSize, michael@0: NS_LITERAL_CSTRING("Memory used for cached font metrics and layout tables."), michael@0: aClosure); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames) michael@0: : mFontFamilies(100), mOtherFamilyNames(30), michael@0: mPrefFonts(10), mBadUnderlineFamilyNames(10), mSharedCmaps(16), michael@0: mStartIndex(0), mIncrement(1), mNumFamilies(0) michael@0: { michael@0: mOtherFamilyNamesInitialized = false; michael@0: michael@0: if (aNeedFullnamePostscriptNames) { michael@0: mExtraNames = new ExtraNames(); michael@0: } michael@0: mFaceNameListsInitialized = false; michael@0: michael@0: LoadBadUnderlineList(); michael@0: michael@0: // pref changes notification setup michael@0: NS_ASSERTION(!gFontListPrefObserver, michael@0: "There has been font list pref observer already"); michael@0: gFontListPrefObserver = new gfxFontListPrefObserver(); michael@0: NS_ADDREF(gFontListPrefObserver); michael@0: Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs); michael@0: michael@0: RegisterStrongMemoryReporter(new MemoryReporter()); michael@0: } michael@0: michael@0: gfxPlatformFontList::~gfxPlatformFontList() michael@0: { michael@0: mSharedCmaps.Clear(); michael@0: NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer"); michael@0: Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs); michael@0: NS_RELEASE(gFontListPrefObserver); michael@0: } michael@0: michael@0: nsresult michael@0: gfxPlatformFontList::InitFontList() michael@0: { michael@0: mFontFamilies.Clear(); michael@0: mOtherFamilyNames.Clear(); michael@0: mOtherFamilyNamesInitialized = false; michael@0: if (mExtraNames) { michael@0: mExtraNames->mFullnames.Clear(); michael@0: mExtraNames->mPostscriptNames.Clear(); michael@0: } michael@0: mFaceNameListsInitialized = false; michael@0: mPrefFonts.Clear(); michael@0: mReplacementCharFallbackFamily = nullptr; michael@0: CancelLoader(); michael@0: michael@0: // initialize ranges of characters for which system-wide font search should be skipped michael@0: mCodepointsWithNoFonts.reset(); michael@0: mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls michael@0: mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls michael@0: michael@0: sPlatformFontList = this; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult) michael@0: { michael@0: aResult = aKeyName; michael@0: ToLowerCase(aResult); michael@0: } michael@0: michael@0: struct InitOtherNamesData { michael@0: InitOtherNamesData(gfxPlatformFontList *aFontList, michael@0: TimeStamp aStartTime) michael@0: : mFontList(aFontList), mStartTime(aStartTime), mTimedOut(false) michael@0: {} michael@0: michael@0: gfxPlatformFontList *mFontList; michael@0: TimeStamp mStartTime; michael@0: bool mTimedOut; michael@0: }; michael@0: michael@0: void michael@0: gfxPlatformFontList::InitOtherFamilyNames() michael@0: { michael@0: if (mOtherFamilyNamesInitialized) { michael@0: return; michael@0: } michael@0: michael@0: TimeStamp start = TimeStamp::Now(); michael@0: michael@0: // iterate over all font families and read in other family names michael@0: InitOtherNamesData otherNamesData(this, start); michael@0: michael@0: mFontFamilies.Enumerate(gfxPlatformFontList::InitOtherFamilyNamesProc, michael@0: &otherNamesData); michael@0: michael@0: if (!otherNamesData.mTimedOut) { michael@0: mOtherFamilyNamesInitialized = true; michael@0: } michael@0: TimeStamp end = TimeStamp::Now(); michael@0: Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES, michael@0: start, end); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (LOG_FONTINIT_ENABLED()) { michael@0: TimeDuration elapsed = end - start; michael@0: LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s", michael@0: elapsed.ToMilliseconds(), michael@0: (otherNamesData.mTimedOut ? "timeout" : ""))); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #define OTHERNAMES_TIMEOUT 200 michael@0: michael@0: PLDHashOperator michael@0: gfxPlatformFontList::InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey, michael@0: nsRefPtr& aFamilyEntry, michael@0: void* userArg) michael@0: { michael@0: InitOtherNamesData *data = static_cast(userArg); michael@0: michael@0: aFamilyEntry->ReadOtherFamilyNames(data->mFontList); michael@0: TimeDuration elapsed = TimeStamp::Now() - data->mStartTime; michael@0: if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) { michael@0: data->mTimedOut = true; michael@0: return PL_DHASH_STOP; michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: struct ReadFaceNamesData { michael@0: ReadFaceNamesData(gfxPlatformFontList *aFontList, TimeStamp aStartTime) michael@0: : mFontList(aFontList), mStartTime(aStartTime), mTimedOut(false) michael@0: {} michael@0: michael@0: gfxPlatformFontList *mFontList; michael@0: TimeStamp mStartTime; michael@0: bool mTimedOut; michael@0: michael@0: // if mFirstChar is not empty, only load facenames for families michael@0: // that start with this character michael@0: nsString mFirstChar; michael@0: }; michael@0: michael@0: gfxFontEntry* michael@0: gfxPlatformFontList::SearchFamiliesForFaceName(const nsAString& aFaceName) michael@0: { michael@0: TimeStamp start = TimeStamp::Now(); michael@0: gfxFontEntry *lookup = nullptr; michael@0: michael@0: ReadFaceNamesData faceNameListsData(this, start); michael@0: michael@0: // iterate over familes starting with the same letter michael@0: faceNameListsData.mFirstChar.Assign(aFaceName.CharAt(0)); michael@0: ToLowerCase(faceNameListsData.mFirstChar); michael@0: mFontFamilies.Enumerate(gfxPlatformFontList::ReadFaceNamesProc, michael@0: &faceNameListsData); michael@0: lookup = FindFaceName(aFaceName); michael@0: michael@0: TimeStamp end = TimeStamp::Now(); michael@0: Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS, michael@0: start, end); michael@0: #ifdef PR_LOGGING michael@0: if (LOG_FONTINIT_ENABLED()) { michael@0: TimeDuration elapsed = end - start; michael@0: LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s", michael@0: elapsed.ToMilliseconds(), michael@0: (lookup ? "found name" : ""), michael@0: (faceNameListsData.mTimedOut ? "timeout" : ""))); michael@0: } michael@0: #endif michael@0: michael@0: return lookup; michael@0: } michael@0: michael@0: // time limit for loading facename lists (ms) michael@0: #define NAMELIST_TIMEOUT 200 michael@0: michael@0: PLDHashOperator michael@0: gfxPlatformFontList::ReadFaceNamesProc(nsStringHashKey::KeyType aKey, michael@0: nsRefPtr& aFamilyEntry, michael@0: void* userArg) michael@0: { michael@0: ReadFaceNamesData *data = static_cast(userArg); michael@0: gfxPlatformFontList *fc = data->mFontList; michael@0: michael@0: // when filtering, skip names that don't start with the filter character michael@0: if (!(data->mFirstChar.IsEmpty())) { michael@0: char16_t firstChar = aKey.CharAt(0); michael@0: nsAutoString firstCharStr(&firstChar, 1); michael@0: ToLowerCase(firstCharStr); michael@0: if (!firstCharStr.Equals(data->mFirstChar)) { michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: } michael@0: aFamilyEntry->ReadFaceNames(fc, fc->NeedFullnamePostscriptNames()); michael@0: michael@0: TimeDuration elapsed = TimeStamp::Now() - data->mStartTime; michael@0: if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) { michael@0: data->mTimedOut = true; michael@0: return PL_DHASH_STOP; michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: gfxFontEntry* michael@0: gfxPlatformFontList::FindFaceName(const nsAString& aFaceName) michael@0: { michael@0: gfxFontEntry *lookup; michael@0: michael@0: // lookup in name lookup tables, return null if not found michael@0: if (mExtraNames && michael@0: ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) || michael@0: (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) { michael@0: return lookup; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: gfxFontEntry* michael@0: gfxPlatformFontList::LookupInFaceNameLists(const nsAString& aFaceName) michael@0: { michael@0: gfxFontEntry *lookup = nullptr; michael@0: michael@0: // initialize facename lookup tables if needed michael@0: // note: this can terminate early or time out, in which case michael@0: // mFaceNameListsInitialized remains false michael@0: if (!mFaceNameListsInitialized) { michael@0: lookup = SearchFamiliesForFaceName(aFaceName); michael@0: if (lookup) { michael@0: return lookup; michael@0: } michael@0: } michael@0: michael@0: // lookup in name lookup tables, return null if not found michael@0: if (!(lookup = FindFaceName(aFaceName))) { michael@0: // names not completely initialized, so keep track of lookup misses michael@0: if (!mFaceNameListsInitialized) { michael@0: if (!mFaceNamesMissed) { michael@0: mFaceNamesMissed = new nsTHashtable(4); michael@0: } michael@0: mFaceNamesMissed->PutEntry(aFaceName); michael@0: } michael@0: } michael@0: michael@0: return lookup; michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::PreloadNamesList() michael@0: { michael@0: nsAutoTArray preloadFonts; michael@0: gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts); michael@0: michael@0: uint32_t numFonts = preloadFonts.Length(); michael@0: for (uint32_t i = 0; i < numFonts; i++) { michael@0: nsAutoString key; michael@0: GenerateFontListKey(preloadFonts[i], key); michael@0: michael@0: // only search canonical names! michael@0: gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key); michael@0: if (familyEntry) { michael@0: familyEntry->ReadOtherFamilyNames(this); michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::SetFixedPitch(const nsAString& aFamilyName) michael@0: { michael@0: gfxFontFamily *family = FindFamily(aFamilyName); michael@0: if (!family) return; michael@0: michael@0: family->FindStyleVariations(); michael@0: nsTArray >& fontlist = family->GetFontList(); michael@0: michael@0: uint32_t i, numFonts = fontlist.Length(); michael@0: michael@0: for (i = 0; i < numFonts; i++) { michael@0: fontlist[i]->mFixedPitch = 1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::LoadBadUnderlineList() michael@0: { michael@0: nsAutoTArray blacklist; michael@0: gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist); michael@0: uint32_t numFonts = blacklist.Length(); michael@0: for (uint32_t i = 0; i < numFonts; i++) { michael@0: nsAutoString key; michael@0: GenerateFontListKey(blacklist[i], key); michael@0: mBadUnderlineFamilyNames.PutEntry(key); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: gfxPlatformFontList::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName) michael@0: { michael@0: gfxFontFamily *family = FindFamily(aFontName); michael@0: if (family) { michael@0: aResolvedFontName = family->Name(); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: RebuildLocalFonts(nsPtrHashKey* aKey, michael@0: void* aClosure) michael@0: { michael@0: aKey->GetKey()->RebuildLocalRules(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::UpdateFontList() michael@0: { michael@0: InitFontList(); michael@0: mUserFontSetList.EnumerateEntries(RebuildLocalFonts, nullptr); michael@0: } michael@0: michael@0: struct FontListData { michael@0: FontListData(nsIAtom *aLangGroup, michael@0: const nsACString& aGenericFamily, michael@0: nsTArray& aListOfFonts) : michael@0: mLangGroup(aLangGroup), mGenericFamily(aGenericFamily), michael@0: mListOfFonts(aListOfFonts) {} michael@0: nsIAtom *mLangGroup; michael@0: const nsACString& mGenericFamily; michael@0: nsTArray& mListOfFonts; michael@0: }; michael@0: michael@0: PLDHashOperator michael@0: gfxPlatformFontList::HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey, michael@0: nsRefPtr& aFamilyEntry, michael@0: void *aUserArg) michael@0: { michael@0: FontListData *data = static_cast(aUserArg); michael@0: michael@0: // use the first variation for now. This data should be the same michael@0: // for all the variations and should probably be moved up to michael@0: // the Family michael@0: gfxFontStyle style; michael@0: style.language = data->mLangGroup; michael@0: bool needsBold; michael@0: nsRefPtr aFontEntry = aFamilyEntry->FindFontForStyle(style, needsBold); michael@0: NS_ASSERTION(aFontEntry, "couldn't find any font entry in family"); michael@0: if (!aFontEntry) michael@0: return PL_DHASH_NEXT; michael@0: michael@0: /* skip symbol fonts */ michael@0: if (aFontEntry->IsSymbolFont()) michael@0: return PL_DHASH_NEXT; michael@0: michael@0: if (aFontEntry->SupportsLangGroup(data->mLangGroup) && michael@0: aFontEntry->MatchesGenericFamily(data->mGenericFamily)) { michael@0: nsAutoString localizedFamilyName; michael@0: aFamilyEntry->LocalizedName(localizedFamilyName); michael@0: data->mListOfFonts.AppendElement(localizedFamilyName); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::GetFontList(nsIAtom *aLangGroup, michael@0: const nsACString& aGenericFamily, michael@0: nsTArray& aListOfFonts) michael@0: { michael@0: FontListData data(aLangGroup, aGenericFamily, aListOfFonts); michael@0: michael@0: mFontFamilies.Enumerate(gfxPlatformFontList::HashEnumFuncForFamilies, &data); michael@0: michael@0: aListOfFonts.Sort(); michael@0: aListOfFonts.Compact(); michael@0: } michael@0: michael@0: struct FontFamilyListData { michael@0: FontFamilyListData(nsTArray >& aFamilyArray) michael@0: : mFamilyArray(aFamilyArray) michael@0: {} michael@0: michael@0: static PLDHashOperator AppendFamily(nsStringHashKey::KeyType aKey, michael@0: nsRefPtr& aFamilyEntry, michael@0: void *aUserArg) michael@0: { michael@0: FontFamilyListData *data = static_cast(aUserArg); michael@0: data->mFamilyArray.AppendElement(aFamilyEntry); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsTArray >& mFamilyArray; michael@0: }; michael@0: michael@0: void michael@0: gfxPlatformFontList::GetFontFamilyList(nsTArray >& aFamilyArray) michael@0: { michael@0: FontFamilyListData data(aFamilyArray); michael@0: mFontFamilies.Enumerate(FontFamilyListData::AppendFamily, &data); michael@0: } michael@0: michael@0: gfxFontEntry* michael@0: gfxPlatformFontList::SystemFindFontForChar(const uint32_t aCh, michael@0: int32_t aRunScript, michael@0: const gfxFontStyle* aStyle) michael@0: { michael@0: gfxFontEntry* fontEntry = nullptr; michael@0: michael@0: // is codepoint with no matching font? return null immediately michael@0: if (mCodepointsWithNoFonts.test(aCh)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Try to short-circuit font fallback for U+FFFD, used to represent michael@0: // encoding errors: just use cached family from last time U+FFFD was seen. michael@0: // This helps speed up pages with lots of encoding errors, binary-as-text, michael@0: // etc. michael@0: if (aCh == 0xFFFD && mReplacementCharFallbackFamily) { michael@0: bool needsBold; // ignored in the system fallback case michael@0: michael@0: fontEntry = michael@0: mReplacementCharFallbackFamily->FindFontForStyle(*aStyle, michael@0: needsBold); michael@0: michael@0: // this should never fail, as we must have found U+FFFD in order to set michael@0: // mReplacementCharFallbackFamily at all, but better play it safe michael@0: if (fontEntry && fontEntry->TestCharacterMap(aCh)) { michael@0: return fontEntry; michael@0: } michael@0: } michael@0: michael@0: TimeStamp start = TimeStamp::Now(); michael@0: michael@0: // search commonly available fonts michael@0: bool common = true; michael@0: gfxFontFamily *fallbackFamily = nullptr; michael@0: fontEntry = CommonFontFallback(aCh, aRunScript, aStyle, &fallbackFamily); michael@0: michael@0: // if didn't find a font, do system-wide fallback (except for specials) michael@0: uint32_t cmapCount = 0; michael@0: if (!fontEntry) { michael@0: common = false; michael@0: fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount, michael@0: &fallbackFamily); michael@0: } michael@0: TimeDuration elapsed = TimeStamp::Now() - start; michael@0: michael@0: #ifdef PR_LOGGING michael@0: PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun); michael@0: michael@0: if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) { michael@0: uint32_t unicodeRange = FindCharUnicodeRange(aCh); michael@0: int32_t script = mozilla::unicode::GetScriptCode(aCh); michael@0: PR_LOG(log, PR_LOG_WARNING,\ michael@0: ("(textrun-systemfallback-%s) char: u+%6.6x " michael@0: "unicode-range: %d script: %d match: [%s]" michael@0: " time: %dus cmaps: %d\n", michael@0: (common ? "common" : "global"), aCh, michael@0: unicodeRange, script, michael@0: (fontEntry ? NS_ConvertUTF16toUTF8(fontEntry->Name()).get() : michael@0: ""), michael@0: int32_t(elapsed.ToMicroseconds()), michael@0: cmapCount)); michael@0: } michael@0: #endif michael@0: michael@0: // no match? add to set of non-matching codepoints michael@0: if (!fontEntry) { michael@0: mCodepointsWithNoFonts.set(aCh); michael@0: } else if (aCh == 0xFFFD && fontEntry && fallbackFamily) { michael@0: mReplacementCharFallbackFamily = fallbackFamily; michael@0: } michael@0: michael@0: // track system fallback time michael@0: static bool first = true; michael@0: int32_t intElapsed = int32_t(first ? elapsed.ToMilliseconds() : michael@0: elapsed.ToMicroseconds()); michael@0: Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST : michael@0: Telemetry::SYSTEM_FONT_FALLBACK), michael@0: intElapsed); michael@0: first = false; michael@0: michael@0: // track the script for which fallback occurred (incremented one make it michael@0: // 1-based) michael@0: Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT, aRunScript + 1); michael@0: michael@0: return fontEntry; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: gfxPlatformFontList::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr& aFamilyEntry, michael@0: void *userArg) michael@0: { michael@0: GlobalFontMatch *data = static_cast(userArg); michael@0: michael@0: // evaluate all fonts in this family for a match michael@0: aFamilyEntry->FindFontForChar(data); michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: #define NUM_FALLBACK_FONTS 8 michael@0: michael@0: gfxFontEntry* michael@0: gfxPlatformFontList::CommonFontFallback(const uint32_t aCh, michael@0: int32_t aRunScript, michael@0: const gfxFontStyle* aMatchStyle, michael@0: gfxFontFamily** aMatchedFamily) michael@0: { michael@0: nsAutoTArray defaultFallbacks; michael@0: uint32_t i, numFallbacks; michael@0: michael@0: gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aRunScript, michael@0: defaultFallbacks); michael@0: numFallbacks = defaultFallbacks.Length(); michael@0: for (i = 0; i < numFallbacks; i++) { michael@0: nsAutoString familyName; michael@0: const char *fallbackFamily = defaultFallbacks[i]; michael@0: michael@0: familyName.AppendASCII(fallbackFamily); michael@0: gfxFontFamily *fallback = michael@0: gfxPlatformFontList::PlatformFontList()->FindFamily(familyName); michael@0: if (!fallback) michael@0: continue; michael@0: michael@0: gfxFontEntry *fontEntry; michael@0: bool needsBold; // ignored in the system fallback case michael@0: michael@0: // use first font in list that supports a given character michael@0: fontEntry = fallback->FindFontForStyle(*aMatchStyle, needsBold); michael@0: if (fontEntry && fontEntry->TestCharacterMap(aCh)) { michael@0: *aMatchedFamily = fallback; michael@0: return fontEntry; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: gfxFontEntry* michael@0: gfxPlatformFontList::GlobalFontFallback(const uint32_t aCh, michael@0: int32_t aRunScript, michael@0: const gfxFontStyle* aMatchStyle, michael@0: uint32_t& aCmapCount, michael@0: gfxFontFamily** aMatchedFamily) michael@0: { michael@0: // otherwise, try to find it among local fonts michael@0: GlobalFontMatch data(aCh, aRunScript, aMatchStyle); michael@0: michael@0: // iterate over all font families to find a font that support the character michael@0: mFontFamilies.Enumerate(gfxPlatformFontList::FindFontForCharProc, &data); michael@0: michael@0: aCmapCount = data.mCmapsTested; michael@0: *aMatchedFamily = data.mMatchedFamily; michael@0: michael@0: return data.mBestMatch; michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: michael@0: // crude hack for using when monitoring process michael@0: static void LogRegistryEvent(const wchar_t *msg) michael@0: { michael@0: HKEY dummyKey; michael@0: HRESULT hr; michael@0: wchar_t buf[512]; michael@0: michael@0: wsprintfW(buf, L" log %s", msg); michael@0: hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey); michael@0: if (SUCCEEDED(hr)) { michael@0: RegCloseKey(dummyKey); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: gfxFontFamily* michael@0: gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily) michael@0: { michael@0: if (aFamily && !aFamily->HasStyles()) { michael@0: aFamily->FindStyleVariations(); michael@0: aFamily->CheckForSimpleFamily(); michael@0: } michael@0: michael@0: if (aFamily && aFamily->GetFontList().Length() == 0) { michael@0: // failed to load any faces for this family, so discard it michael@0: nsAutoString key; michael@0: GenerateFontListKey(aFamily->Name(), key); michael@0: mFontFamilies.Remove(key); michael@0: return nullptr; michael@0: } michael@0: michael@0: return aFamily; michael@0: } michael@0: michael@0: gfxFontFamily* michael@0: gfxPlatformFontList::FindFamily(const nsAString& aFamily) michael@0: { michael@0: nsAutoString key; michael@0: gfxFontFamily *familyEntry; michael@0: GenerateFontListKey(aFamily, key); michael@0: michael@0: NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly"); michael@0: michael@0: // lookup in canonical (i.e. English) family name list michael@0: if ((familyEntry = mFontFamilies.GetWeak(key))) { michael@0: return CheckFamily(familyEntry); michael@0: } michael@0: michael@0: // lookup in other family names list (mostly localized names) michael@0: if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) { michael@0: return CheckFamily(familyEntry); michael@0: } michael@0: michael@0: // name not found and other family names not yet fully initialized so michael@0: // initialize the rest of the list and try again. this is done lazily michael@0: // since reading name table entries is expensive. michael@0: // although ASCII localized family names are possible they don't occur michael@0: // in practice so avoid pulling in names at startup michael@0: if (!mOtherFamilyNamesInitialized && !IsASCII(aFamily)) { michael@0: InitOtherFamilyNames(); michael@0: if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) { michael@0: return CheckFamily(familyEntry); michael@0: } else if (!mOtherFamilyNamesInitialized) { michael@0: // localized family names load timed out, add name to list of michael@0: // names to check after localized names are loaded michael@0: if (!mOtherNamesMissed) { michael@0: mOtherNamesMissed = new nsTHashtable(4); michael@0: } michael@0: mOtherNamesMissed->PutEntry(key); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: gfxFontEntry* michael@0: gfxPlatformFontList::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold) michael@0: { michael@0: gfxFontFamily *familyEntry = FindFamily(aFamily); michael@0: michael@0: aNeedsBold = false; michael@0: michael@0: if (familyEntry) michael@0: return familyEntry->FindFontForStyle(*aStyle, aNeedsBold); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: gfxPlatformFontList::GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray > *array) michael@0: { michael@0: return mPrefFonts.Get(uint32_t(aLangGroup), array); michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray >& array) michael@0: { michael@0: mPrefFonts.Put(uint32_t(aLangGroup), array); michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName) michael@0: { michael@0: nsAutoString key; michael@0: GenerateFontListKey(aOtherFamilyName, key); michael@0: michael@0: if (!mOtherFamilyNames.GetWeak(key)) { michael@0: mOtherFamilyNames.Put(key, aFamilyEntry); michael@0: #ifdef PR_LOGGING michael@0: LOG_FONTLIST(("(fontlist-otherfamily) canonical family: %s, " michael@0: "other family: %s\n", michael@0: NS_ConvertUTF16toUTF8(aFamilyEntry->Name()).get(), michael@0: NS_ConvertUTF16toUTF8(aOtherFamilyName).get())); michael@0: #endif michael@0: if (mBadUnderlineFamilyNames.Contains(key)) michael@0: aFamilyEntry->SetBadUnderlineFamily(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::AddFullname(gfxFontEntry *aFontEntry, nsAString& aFullname) michael@0: { michael@0: if (!mExtraNames->mFullnames.GetWeak(aFullname)) { michael@0: mExtraNames->mFullnames.Put(aFullname, aFontEntry); michael@0: #ifdef PR_LOGGING michael@0: LOG_FONTLIST(("(fontlist-fullname) name: %s, fullname: %s\n", michael@0: NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(), michael@0: NS_ConvertUTF16toUTF8(aFullname).get())); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName) michael@0: { michael@0: if (!mExtraNames->mPostscriptNames.GetWeak(aPostscriptName)) { michael@0: mExtraNames->mPostscriptNames.Put(aPostscriptName, aFontEntry); michael@0: #ifdef PR_LOGGING michael@0: LOG_FONTLIST(("(fontlist-postscript) name: %s, psname: %s\n", michael@0: NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(), michael@0: NS_ConvertUTF16toUTF8(aPostscriptName).get())); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: bool michael@0: gfxPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) michael@0: { michael@0: aFamilyName.Truncate(); michael@0: ResolveFontName(aFontName, aFamilyName); michael@0: return !aFamilyName.IsEmpty(); michael@0: } michael@0: michael@0: gfxCharacterMap* michael@0: gfxPlatformFontList::FindCharMap(gfxCharacterMap *aCmap) michael@0: { michael@0: aCmap->CalcHash(); michael@0: gfxCharacterMap *cmap = AddCmap(aCmap); michael@0: cmap->mShared = true; michael@0: return cmap; michael@0: } michael@0: michael@0: // add a cmap to the shared cmap set michael@0: gfxCharacterMap* michael@0: gfxPlatformFontList::AddCmap(const gfxCharacterMap* aCharMap) michael@0: { michael@0: CharMapHashKey *found = michael@0: mSharedCmaps.PutEntry(const_cast(aCharMap)); michael@0: return found->GetKey(); michael@0: } michael@0: michael@0: // remove the cmap from the shared cmap set michael@0: void michael@0: gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap) michael@0: { michael@0: // skip lookups during teardown michael@0: if (mSharedCmaps.Count() == 0) { michael@0: return; michael@0: } michael@0: michael@0: // cmap needs to match the entry *and* be the same ptr before removing michael@0: CharMapHashKey *found = michael@0: mSharedCmaps.GetEntry(const_cast(aCharMap)); michael@0: if (found && found->GetKey() == aCharMap) { michael@0: mSharedCmaps.RemoveEntry(const_cast(aCharMap)); michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator AppendFamilyToList(nsStringHashKey::KeyType aKey, michael@0: nsRefPtr& aFamilyEntry, michael@0: void *aUserArg) michael@0: { michael@0: nsTArray *familyNames = static_cast *>(aUserArg); michael@0: familyNames->AppendElement(aFamilyEntry->Name()); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::GetFontFamilyNames(nsTArray& aFontFamilyNames) michael@0: { michael@0: mFontFamilies.Enumerate(AppendFamilyToList, &aFontFamilyNames); michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::InitLoader() michael@0: { michael@0: GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad); michael@0: mStartIndex = 0; michael@0: mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length(); michael@0: memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats)); michael@0: } michael@0: michael@0: #define FONT_LOADER_MAX_TIMESLICE 100 // max time for one pass through RunLoader = 100ms michael@0: michael@0: bool michael@0: gfxPlatformFontList::LoadFontInfo() michael@0: { michael@0: TimeStamp start = TimeStamp::Now(); michael@0: uint32_t i, endIndex = mNumFamilies; michael@0: bool loadCmaps = !UsesSystemFallback() || michael@0: gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); michael@0: michael@0: // for each font family, load in various font info michael@0: for (i = mStartIndex; i < endIndex; i++) { michael@0: nsAutoString key; michael@0: gfxFontFamily *familyEntry; michael@0: GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key); michael@0: michael@0: // lookup in canonical (i.e. English) family name list michael@0: if (!(familyEntry = mFontFamilies.GetWeak(key))) { michael@0: continue; michael@0: } michael@0: michael@0: // read in face names michael@0: familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames(), mFontInfo); michael@0: michael@0: // load the cmaps if needed michael@0: if (loadCmaps) { michael@0: familyEntry->ReadAllCMAPs(mFontInfo); michael@0: } michael@0: michael@0: // limit the time spent reading fonts in one pass michael@0: TimeDuration elapsed = TimeStamp::Now() - start; michael@0: if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE && michael@0: i + 1 != endIndex) { michael@0: endIndex = i + 1; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: mStartIndex = endIndex; michael@0: bool done = mStartIndex >= mNumFamilies; michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (LOG_FONTINIT_ENABLED()) { michael@0: TimeDuration elapsed = TimeStamp::Now() - start; michael@0: LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n", michael@0: elapsed.ToMilliseconds(), (done ? "true" : "false"))); michael@0: } michael@0: #endif michael@0: michael@0: if (done) { michael@0: mOtherFamilyNamesInitialized = true; michael@0: mFaceNameListsInitialized = true; michael@0: } michael@0: michael@0: return done; michael@0: } michael@0: michael@0: struct LookupMissedFaceNamesData { michael@0: LookupMissedFaceNamesData(gfxPlatformFontList *aFontList) michael@0: : mFontList(aFontList), mFoundName(false) {} michael@0: michael@0: gfxPlatformFontList *mFontList; michael@0: bool mFoundName; michael@0: }; michael@0: michael@0: /*static*/ PLDHashOperator michael@0: gfxPlatformFontList::LookupMissedFaceNamesProc(nsStringHashKey *aKey, michael@0: void *aUserArg) michael@0: { michael@0: LookupMissedFaceNamesData *data = michael@0: reinterpret_cast(aUserArg); michael@0: michael@0: if (data->mFontList->FindFaceName(aKey->GetKey())) { michael@0: data->mFoundName = true; michael@0: return PL_DHASH_STOP; michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: struct LookupMissedOtherNamesData { michael@0: LookupMissedOtherNamesData(gfxPlatformFontList *aFontList) michael@0: : mFontList(aFontList), mFoundName(false) {} michael@0: michael@0: gfxPlatformFontList *mFontList; michael@0: bool mFoundName; michael@0: }; michael@0: michael@0: /*static*/ PLDHashOperator michael@0: gfxPlatformFontList::LookupMissedOtherNamesProc(nsStringHashKey *aKey, michael@0: void *aUserArg) michael@0: { michael@0: LookupMissedOtherNamesData *data = michael@0: reinterpret_cast(aUserArg); michael@0: michael@0: if (data->mFontList->FindFamily(aKey->GetKey())) { michael@0: data->mFoundName = true; michael@0: return PL_DHASH_STOP; michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::CleanupLoader() michael@0: { michael@0: mFontFamiliesToLoad.Clear(); michael@0: mNumFamilies = 0; michael@0: bool rebuilt = false, forceReflow = false; michael@0: michael@0: // if had missed face names that are now available, force reflow all michael@0: if (mFaceNamesMissed && michael@0: mFaceNamesMissed->Count() != 0) { michael@0: LookupMissedFaceNamesData namedata(this); michael@0: mFaceNamesMissed->EnumerateEntries(LookupMissedFaceNamesProc, &namedata); michael@0: if (namedata.mFoundName) { michael@0: rebuilt = true; michael@0: mUserFontSetList.EnumerateEntries(RebuildLocalFonts, nullptr); michael@0: } michael@0: mFaceNamesMissed = nullptr; michael@0: } michael@0: michael@0: if (mOtherNamesMissed) { michael@0: LookupMissedOtherNamesData othernamesdata(this); michael@0: mOtherNamesMissed->EnumerateEntries(LookupMissedOtherNamesProc, michael@0: &othernamesdata); michael@0: mOtherNamesMissed = nullptr; michael@0: if (othernamesdata.mFoundName) { michael@0: forceReflow = true; michael@0: ForceGlobalReflow(); michael@0: } michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (LOG_FONTINIT_ENABLED() && mFontInfo) { michael@0: LOG_FONTINIT(("(fontinit) fontloader load thread took %8.2f ms " michael@0: "%d families %d fonts %d cmaps " michael@0: "%d facenames %d othernames %s %s", michael@0: mLoadTime.ToMilliseconds(), michael@0: mFontInfo->mLoadStats.families, michael@0: mFontInfo->mLoadStats.fonts, michael@0: mFontInfo->mLoadStats.cmaps, michael@0: mFontInfo->mLoadStats.facenames, michael@0: mFontInfo->mLoadStats.othernames, michael@0: (rebuilt ? "(userfont sets rebuilt)" : ""), michael@0: (forceReflow ? "(global reflow)" : ""))); michael@0: } michael@0: #endif michael@0: michael@0: gfxFontInfoLoader::CleanupLoader(); michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::GetPrefsAndStartLoader() michael@0: { michael@0: mIncrement = michael@0: std::max(1u, Preferences::GetUint(FONT_LOADER_FAMILIES_PER_SLICE_PREF)); michael@0: michael@0: uint32_t delay = michael@0: std::max(1u, Preferences::GetUint(FONT_LOADER_DELAY_PREF)); michael@0: uint32_t interval = michael@0: std::max(1u, Preferences::GetUint(FONT_LOADER_INTERVAL_PREF)); michael@0: michael@0: StartLoader(delay, interval); michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::ForceGlobalReflow() michael@0: { michael@0: // modify a preference that will trigger reflow everywhere michael@0: static const char kPrefName[] = "font.internaluseonly.changed"; michael@0: bool fontInternalChange = Preferences::GetBool(kPrefName, false); michael@0: Preferences::SetBool(kPrefName, !fontInternalChange); michael@0: } michael@0: michael@0: // Support for memory reporting michael@0: michael@0: static size_t michael@0: SizeOfFamilyEntryExcludingThis(const nsAString& aKey, michael@0: const nsRefPtr& aFamily, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg) michael@0: { michael@0: FontListSizes *sizes = static_cast(aUserArg); michael@0: aFamily->AddSizeOfExcludingThis(aMallocSizeOf, sizes); michael@0: michael@0: sizes->mFontListSize += aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf); michael@0: michael@0: // we return zero here because the measurements have been added directly michael@0: // to the relevant fields of the FontListSizes record michael@0: return 0; michael@0: } michael@0: michael@0: // this is also used by subclasses that hold additional hashes of family names michael@0: /*static*/ size_t michael@0: gfxPlatformFontList::SizeOfFamilyNameEntryExcludingThis michael@0: (const nsAString& aKey, michael@0: const nsRefPtr& aFamily, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg) michael@0: { michael@0: // we don't count the size of the family here, because this is an *extra* michael@0: // reference to a family that will have already been counted in the main list michael@0: return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf); michael@0: } michael@0: michael@0: static size_t michael@0: SizeOfFontNameEntryExcludingThis(const nsAString& aKey, michael@0: const nsRefPtr& aFont, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg) michael@0: { michael@0: // the font itself is counted by its owning family; here we only care about michael@0: // the name stored in the hashtable key michael@0: return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf); michael@0: } michael@0: michael@0: static size_t michael@0: SizeOfPrefFontEntryExcludingThis michael@0: (const uint32_t& aKey, michael@0: const nsTArray >& aList, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg) michael@0: { michael@0: // again, we only care about the size of the array itself; we don't follow michael@0: // the refPtrs stored in it, because they point to entries already owned michael@0: // and accounted-for by the main font list michael@0: return aList.SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: static size_t michael@0: SizeOfStringEntryExcludingThis(nsStringHashKey* aHashEntry, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg) michael@0: { michael@0: return aHashEntry->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf); michael@0: } michael@0: michael@0: static size_t michael@0: SizeOfSharedCmapExcludingThis(CharMapHashKey* aHashEntry, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg) michael@0: { michael@0: FontListSizes *sizes = static_cast(aUserArg); michael@0: michael@0: uint32_t size = aHashEntry->GetKey()->SizeOfIncludingThis(aMallocSizeOf); michael@0: sizes->mCharMapsSize += size; michael@0: michael@0: // we return zero here because the measurements have been added directly michael@0: // to the relevant fields of the FontListSizes record michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: aSizes->mFontListSize += michael@0: mFontFamilies.SizeOfExcludingThis(SizeOfFamilyEntryExcludingThis, michael@0: aMallocSizeOf, aSizes); michael@0: michael@0: aSizes->mFontListSize += michael@0: mOtherFamilyNames.SizeOfExcludingThis(SizeOfFamilyNameEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: michael@0: if (mExtraNames) { michael@0: aSizes->mFontListSize += michael@0: mExtraNames->mFullnames.SizeOfExcludingThis(SizeOfFontNameEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: aSizes->mFontListSize += michael@0: mExtraNames->mPostscriptNames.SizeOfExcludingThis(SizeOfFontNameEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: } michael@0: michael@0: aSizes->mFontListSize += michael@0: mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf); michael@0: aSizes->mFontListSize += michael@0: mFontFamiliesToLoad.SizeOfExcludingThis(aMallocSizeOf); michael@0: michael@0: aSizes->mFontListSize += michael@0: mPrefFonts.SizeOfExcludingThis(SizeOfPrefFontEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: michael@0: aSizes->mFontListSize += michael@0: mBadUnderlineFamilyNames.SizeOfExcludingThis(SizeOfStringEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: michael@0: aSizes->mFontListSize += michael@0: mSharedCmaps.SizeOfExcludingThis(SizeOfSharedCmapExcludingThis, michael@0: aMallocSizeOf, aSizes); michael@0: } michael@0: michael@0: void michael@0: gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: aSizes->mFontListSize += aMallocSizeOf(this); michael@0: AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: }