diff --git a/gfx/thebes/gfxDWriteFontList.cpp b/gfx/thebes/gfxDWriteFontList.cpp index a432026dfe4a..d1ec2e6983d8 100644 --- a/gfx/thebes/gfxDWriteFontList.cpp +++ b/gfx/thebes/gfxDWriteFontList.cpp @@ -147,7 +147,7 @@ GetDirectWriteFaceName(IDWriteFont *aFont, } void -gfxDWriteFontFamily::FindStyleVariations() +gfxDWriteFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) { HRESULT hr; if (mHasStyles) { @@ -496,30 +496,38 @@ gfxDWriteFontEntry::GetFontTable(uint32_t aTag) } nsresult -gfxDWriteFontEntry::ReadCMAP() +gfxDWriteFontEntry::ReadCMAP(FontInfoData *aFontInfoData) { - nsresult rv; - // attempt this once, if errors occur leave a blank cmap if (mCharacterMap) { return NS_OK; } - nsRefPtr charmap = new gfxCharacterMap(); + nsRefPtr charmap; + nsresult rv; + bool symbolFont; - uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); - AutoTable cmapTable(this, kCMAP); - if (cmapTable) { - bool unicodeFont = false, symbolFont = false; // currently ignored - uint32_t cmapLen; - const uint8_t* cmapData = - reinterpret_cast(hb_blob_get_data(cmapTable, - &cmapLen)); - rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, - *charmap, mUVSOffset, - unicodeFont, symbolFont); + if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, + mUVSOffset, + symbolFont))) { + rv = NS_OK; } else { - rv = NS_ERROR_NOT_AVAILABLE; + uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); + charmap = new gfxCharacterMap(); + AutoTable cmapTable(this, kCMAP); + + if (cmapTable) { + bool unicodeFont = false, symbolFont = false; // currently ignored + uint32_t cmapLen; + const uint8_t* cmapData = + reinterpret_cast(hb_blob_get_data(cmapTable, + &cmapLen)); + rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, + *charmap, mUVSOffset, + unicodeFont, symbolFont); + } else { + rv = NS_ERROR_NOT_AVAILABLE; + } } mHasCmapTable = NS_SUCCEEDED(rv); diff --git a/gfx/thebes/gfxDWriteFontList.h b/gfx/thebes/gfxDWriteFontList.h index f1fc10d4b312..09244516ada8 100644 --- a/gfx/thebes/gfxDWriteFontList.h +++ b/gfx/thebes/gfxDWriteFontList.h @@ -43,7 +43,7 @@ public: : gfxFontFamily(aName), mDWFamily(aFamily), mForceGDIClassic(false) {} virtual ~gfxDWriteFontFamily(); - virtual void FindStyleVariations(); + virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr); virtual void LocalizedName(nsAString& aLocalizedName); @@ -149,7 +149,7 @@ public: virtual hb_blob_t* GetFontTable(uint32_t aTableTag) MOZ_OVERRIDE; - nsresult ReadCMAP(); + nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr); bool IsCJKFont(); diff --git a/gfx/thebes/gfxFT2FontList.cpp b/gfx/thebes/gfxFT2FontList.cpp index 2d204d6c7abe..cbfe9aee5c5c 100644 --- a/gfx/thebes/gfxFT2FontList.cpp +++ b/gfx/thebes/gfxFT2FontList.cpp @@ -463,7 +463,7 @@ FT2FontEntry::CairoFontFace() } nsresult -FT2FontEntry::ReadCMAP() +FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData) { if (mCharacterMap) { return NS_OK; diff --git a/gfx/thebes/gfxFT2FontList.h b/gfx/thebes/gfxFT2FontList.h index 531922eab2f4..d1c4208a23d5 100644 --- a/gfx/thebes/gfxFT2FontList.h +++ b/gfx/thebes/gfxFT2FontList.h @@ -74,7 +74,7 @@ public: // This may fail and return null, so caller must be prepared to handle this. cairo_scaled_font_t *CreateScaledFont(const gfxFontStyle *aStyle); - nsresult ReadCMAP(); + nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr); virtual hb_blob_t* GetFontTable(uint32_t aTableTag) MOZ_OVERRIDE; diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index ceda47310239..f3e868239cd8 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -225,7 +225,7 @@ uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS) return 0; } -nsresult gfxFontEntry::ReadCMAP() +nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData) { NS_ASSERTION(false, "using default no-op implementation of ReadCMAP"); mCharacterMap = new gfxCharacterMap(); @@ -564,6 +564,18 @@ gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag) return nullptr; } +already_AddRefed +gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData, + uint32_t& aUVSOffset, + bool& aSymbolFont) +{ + if (!aFontInfoData || !aFontInfoData->mLoadCmaps) { + return nullptr; + } + + return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont); +} + hb_blob_t * gfxFontEntry::GetFontTable(uint32_t aTag) { @@ -905,6 +917,11 @@ gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle, void gfxFontFamily::CheckForSimpleFamily() { + // already checked this family + if (mIsSimpleFamily) { + return; + }; + uint32_t count = mAvailableFonts.Length(); if (count > 4 || count == 0) { return; // can't be "simple" if there are >4 faces; @@ -1266,18 +1283,46 @@ gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList) void gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, - bool aNeedFullnamePostscriptNames) + bool aNeedFullnamePostscriptNames, + FontInfoData *aFontInfoData) { // if all needed names have already been read, skip if (mOtherFamilyNamesInitialized && (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) return; - FindStyleVariations(); + if (!mOtherFamilyNamesInitialized && + aFontInfoData && + aFontInfoData->mLoadOtherNames) + { + nsAutoTArray otherFamilyNames; + bool foundOtherNames = + aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames); + if (foundOtherNames) { + uint32_t i, n = otherFamilyNames.Length(); + for (i = 0; i < n; i++) { + aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]); + } + } + mOtherFamilyNamesInitialized = true; + } + + // if all needed data has been initialized, return + if (mOtherFamilyNamesInitialized && + (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { + return; + } + + FindStyleVariations(aFontInfoData); + + // check again, as style enumeration code may have loaded names + if (mOtherFamilyNamesInitialized && + (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { + return; + } uint32_t i, numFonts = mAvailableFonts.Length(); const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e'); - nsAutoString fullname, psname; bool firstTime = true, readAllFaces = false; for (i = 0; i < numFonts; ++i) { @@ -1285,11 +1330,35 @@ gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, if (!fe) { continue; } + + nsAutoString fullname, psname; + bool foundFaceNames = false; + if (!mFaceNamesInitialized && + aNeedFullnamePostscriptNames && + aFontInfoData && + aFontInfoData->mLoadFaceNames) { + aFontInfoData->GetFaceNames(fe->Name(), fullname, psname); + if (!fullname.IsEmpty()) { + aPlatformFontList->AddFullname(fe, fullname); + } + if (!psname.IsEmpty()) { + aPlatformFontList->AddPostscriptName(fe, psname); + } + foundFaceNames = true; + + // found everything needed? skip to next font + if (mOtherFamilyNamesInitialized) { + continue; + } + } + + // load directly from the name table gfxFontEntry::AutoTable nameTable(fe, kNAME); if (!nameTable) { continue; } - if (aNeedFullnamePostscriptNames) { + + if (aNeedFullnamePostscriptNames && !foundFaceNames) { if (gfxFontUtils::ReadCanonicalName( nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK) { @@ -1341,8 +1410,10 @@ gfxFontFamily::FindFont(const nsAString& aPostscriptName) } void -gfxFontFamily::ReadAllCMAPs() +gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData) { + FindStyleVariations(aFontInfoData); + uint32_t i, numFonts = mAvailableFonts.Length(); for (i = 0; i < numFonts; i++) { gfxFontEntry *fe = mAvailableFonts[i]; @@ -1350,7 +1421,7 @@ gfxFontFamily::ReadAllCMAPs() if (!fe || fe->mIsProxy) { continue; } - fe->ReadCMAP(); + fe->ReadCMAP(aFontInfoData); mFamilyCharacterMap.Union(*(fe->mCharacterMap)); } mFamilyCharacterMap.Compact(); diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index ff51cd55bb6b..2b688ab14d84 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -49,6 +49,7 @@ class gfxShapedText; class gfxShapedWord; class gfxSVGGlyphs; class gfxTextContextPaint; +class FontInfoData; class nsILanguageAtomService; @@ -299,7 +300,7 @@ public: // ReadCMAP() must *always* set the mCharacterMap pointer to a valid // gfxCharacterMap, even if empty, as other code assumes this pointer // can be safely dereferenced. - virtual nsresult ReadCMAP(); + virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr); bool TryGetSVGData(gfxFont* aFont); bool HasSVGGlyph(uint32_t aGlyphId); @@ -491,6 +492,12 @@ protected: // caller is responsible to do any sanitization/validation necessary. hb_blob_t* GetTableFromFontData(const void* aFontData, uint32_t aTableTag); + // lookup the cmap in cached font data + virtual already_AddRefed + GetCMAPFromFontInfo(FontInfoData *aFontInfoData, + uint32_t& aUVSOffset, + bool& aSymbolFont); + // Font's unitsPerEm from the 'head' table, if available (will be set to // kInvalidUPEM for non-sfnt font formats) uint16_t mUnitsPerEm; @@ -693,6 +700,7 @@ public: } // note that the styles for this family have been added + bool HasStyles() { return mHasStyles; } void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; } // choose a specific face to match a style using CSS font matching @@ -729,17 +737,18 @@ public: // read in other localized family names, fullnames and Postscript names // for all faces and append to lookup tables virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList, - bool aNeedFullnamePostscriptNames); + bool aNeedFullnamePostscriptNames, + FontInfoData *aFontInfoData = nullptr); // find faces belonging to this family (platform implementations override this; // should be made pure virtual once all subclasses have been updated) - virtual void FindStyleVariations() { } + virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) { } // search for a specific face using the Postscript name gfxFontEntry* FindFont(const nsAString& aPostscriptName); // read in cmaps for all the faces - void ReadAllCMAPs(); + void ReadAllCMAPs(FontInfoData *aFontInfoData = nullptr); bool TestCharacterMap(uint32_t aCh) { if (!mFamilyCharacterMapInitialized) { diff --git a/gfx/thebes/gfxFontInfoLoader.cpp b/gfx/thebes/gfxFontInfoLoader.cpp index 732deda36724..d27283f2630a 100644 --- a/gfx/thebes/gfxFontInfoLoader.cpp +++ b/gfx/thebes/gfxFontInfoLoader.cpp @@ -6,10 +6,86 @@ #include "gfxFontInfoLoader.h" #include "nsCRT.h" #include "nsIObserverService.h" +#include "nsThreadUtils.h" // for nsRunnable +#include "gfxPlatformFontList.h" using namespace mozilla; using mozilla::services::GetObserverService; +void +FontInfoData::Load() +{ + TimeStamp start = TimeStamp::Now(); + + uint32_t i, n = mFontFamiliesToLoad.Length(); + mLoadStats.families = n; + for (i = 0; i < n; i++) { + LoadFontFamilyData(mFontFamiliesToLoad[i]); + } + + mLoadTime = TimeStamp::Now() - start; +} + +class FontInfoLoadCompleteEvent : public nsRunnable { + NS_DECL_THREADSAFE_ISUPPORTS + + FontInfoLoadCompleteEvent(FontInfoData *aFontInfo) : + mFontInfo(aFontInfo) + {} + virtual ~FontInfoLoadCompleteEvent() {} + + NS_IMETHOD Run(); + + nsRefPtr mFontInfo; +}; + +class AsyncFontInfoLoader : public nsRunnable { + NS_DECL_THREADSAFE_ISUPPORTS + + AsyncFontInfoLoader(FontInfoData *aFontInfo) : + mFontInfo(aFontInfo) + { + mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo); + } + virtual ~AsyncFontInfoLoader() {} + + NS_IMETHOD Run(); + + nsRefPtr mFontInfo; + nsRefPtr mCompleteEvent; +}; + +// runs on main thread after async font info loading is done +nsresult +FontInfoLoadCompleteEvent::Run() +{ + gfxFontInfoLoader *loader = + static_cast(gfxPlatformFontList::PlatformFontList()); + + loader->FinalizeLoader(mFontInfo); + + mFontInfo = nullptr; + return NS_OK; +} + +NS_IMPL_ISUPPORTS1(FontInfoLoadCompleteEvent, nsIRunnable); + +// runs on separate thread +nsresult +AsyncFontInfoLoader::Run() +{ + // load platform-specific font info + mFontInfo->Load(); + + // post a completion event that transfer the data to the fontlist + NS_DispatchToMainThread(mCompleteEvent, NS_DISPATCH_NORMAL); + mFontInfo = nullptr; + + return NS_OK; +} + +NS_IMPL_ISUPPORTS1(AsyncFontInfoLoader, nsIRunnable); + NS_IMPL_ISUPPORTS1(gfxFontInfoLoader::ShutdownObserver, nsIObserver) NS_IMETHODIMP @@ -31,7 +107,9 @@ gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval) mInterval = aInterval; // sanity check - if (mState != stateInitial && mState != stateTimerOff) { + if (mState != stateInitial && + mState != stateTimerOff && + mState != stateTimerOnDelay) { CancelLoader(); } @@ -44,28 +122,55 @@ gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval) } } - // need an initial delay? - uint32_t timerInterval; + AddShutdownObserver(); + // delay? ==> start async thread after a delay if (aDelay) { mState = stateTimerOnDelay; - timerInterval = aDelay; - } else { - mState = stateTimerOnInterval; - timerInterval = mInterval; + mTimer->InitWithFuncCallback(DelayedStartCallback, this, aDelay, + nsITimer::TYPE_ONE_SHOT); + return; } + mFontInfo = CreateFontInfoData(); + + // initialize InitLoader(); - // start timer - mTimer->InitWithFuncCallback(LoaderTimerCallback, this, timerInterval, - nsITimer::TYPE_REPEATING_SLACK); - - nsCOMPtr obs = GetObserverService(); - if (obs) { - mObserver = new ShutdownObserver(this); - obs->AddObserver(mObserver, "quit-application", false); + // start async load + mState = stateAsyncLoad; + nsresult rv = NS_NewNamedThread("Font Loader", + getter_AddRefs(mFontLoaderThread), + nullptr); + if (NS_FAILED(rv)) { + return; } + + nsCOMPtr loadEvent = new AsyncFontInfoLoader(mFontInfo); + + mFontLoaderThread->Dispatch(loadEvent, NS_DISPATCH_NORMAL); +} + +void +gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo) +{ + // avoid loading data if loader has already been canceled + if (mState != stateAsyncLoad) { + return; + } + + mLoadTime = mFontInfo->mLoadTime; + + // try to load all font data immediately + if (LoadFontInfo()) { + CancelLoader(); + return; + } + + // not all work completed ==> run load on interval + mState = stateTimerOnInterval; + mTimer->InitWithFuncCallback(LoadFontInfoCallback, this, mInterval, + nsITimer::TYPE_REPEATING_SLACK); } void @@ -79,19 +184,23 @@ gfxFontInfoLoader::CancelLoader() mTimer->Cancel(); mTimer = nullptr; } + if (mFontLoaderThread) { + mFontLoaderThread->Shutdown(); + mFontLoaderThread = nullptr; + } RemoveShutdownObserver(); - FinishLoader(); + CleanupLoader(); } void -gfxFontInfoLoader::LoaderTimerFire() +gfxFontInfoLoader::LoadFontInfoTimerFire() { if (mState == stateTimerOnDelay) { mState = stateTimerOnInterval; mTimer->SetDelay(mInterval); } - bool done = RunLoader(); + bool done = LoadFontInfo(); if (done) { CancelLoader(); } @@ -102,6 +211,20 @@ gfxFontInfoLoader::~gfxFontInfoLoader() RemoveShutdownObserver(); } +void +gfxFontInfoLoader::AddShutdownObserver() +{ + if (mObserver) { + return; + } + + nsCOMPtr obs = GetObserverService(); + if (obs) { + mObserver = new ShutdownObserver(this); + obs->AddObserver(mObserver, "quit-application", false); + } +} + void gfxFontInfoLoader::RemoveShutdownObserver() { diff --git a/gfx/thebes/gfxFontInfoLoader.h b/gfx/thebes/gfxFontInfoLoader.h index 00499983db61..dc8936db9413 100644 --- a/gfx/thebes/gfxFontInfoLoader.h +++ b/gfx/thebes/gfxFontInfoLoader.h @@ -6,11 +6,143 @@ #ifndef GFX_FONT_INFO_LOADER_H #define GFX_FONT_INFO_LOADER_H +#include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsIObserver.h" #include "nsITimer.h" +#include "nsIThread.h" +#include "nsRefPtrHashtable.h" +#include "nsString.h" +#include "gfxFont.h" +#include "nsIRunnable.h" +#include "mozilla/TimeStamp.h" +#include "nsTraceRefcnt.h" -// helper class for loading in font info spaced out at regular intervals +// data retrieved for a given face + +struct FontFaceData { + FontFaceData() : mUVSOffset(0), mSymbolFont(false) {} + + FontFaceData(const FontFaceData& aFontFaceData) { + mFullName = aFontFaceData.mFullName; + mPostscriptName = aFontFaceData.mPostscriptName; + mCharacterMap = aFontFaceData.mCharacterMap; + mUVSOffset = aFontFaceData.mUVSOffset; + mSymbolFont = aFontFaceData.mSymbolFont; + } + + nsString mFullName; + nsString mPostscriptName; + nsRefPtr mCharacterMap; + uint32_t mUVSOffset; + bool mSymbolFont; +}; + +// base class used to contain cached system-wide font info. +// methods in this class are called on off-main threads so +// all methods use only static methods or other thread-safe +// font data access API's. specifically, no use is made of +// gfxPlatformFontList, gfxFontFamily, gfxFamily or any +// harfbuzz API methods within FontInfoData subclasses. + +class FontInfoData { +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FontInfoData) + + FontInfoData(bool aLoadOtherNames, + bool aLoadFaceNames, + bool aLoadCmaps) : + mLoadOtherNames(aLoadOtherNames), + mLoadFaceNames(aLoadFaceNames), + mLoadCmaps(aLoadCmaps) + { + MOZ_COUNT_CTOR(FontInfoData); + } + + virtual ~FontInfoData() { + MOZ_COUNT_DTOR(FontInfoData); + } + + virtual void Load(); + + // loads font data for all fonts of a given family + // (called on async thread) + virtual void LoadFontFamilyData(const nsAString& aFamilyName) = 0; + + // -- methods overriden by platform-specific versions -- + + // fetches cmap data for a particular font from cached font data + virtual already_AddRefed + GetCMAP(const nsAString& aFontName, + uint32_t& aUVSOffset, + bool& aSymbolFont) + { + FontFaceData faceData; + if (!mFontFaceData.Get(aFontName, &faceData) || + !faceData.mCharacterMap) { + return nullptr; + } + + aUVSOffset = faceData.mUVSOffset; + aSymbolFont = faceData.mSymbolFont; + nsRefPtr cmap = faceData.mCharacterMap; + return cmap.forget(); + } + + // fetches fullname/postscript names from cached font data + virtual void GetFaceNames(const nsAString& aFontName, + nsAString& aFullName, + nsAString& aPostscriptName) + { + FontFaceData faceData; + if (!mFontFaceData.Get(aFontName, &faceData)) { + return; + } + + aFullName = faceData.mFullName; + aPostscriptName = faceData.mPostscriptName; + } + + // fetches localized family name data from cached font data + virtual bool GetOtherFamilyNames(const nsAString& aFamilyName, + nsTArray& aOtherFamilyNames) + { + return mOtherFamilyNames.Get(aFamilyName, &aOtherFamilyNames); + } + + nsTArray mFontFamiliesToLoad; + + // time spent on the loader thread + mozilla::TimeDuration mLoadTime; + + struct FontCounts { + uint32_t families; + uint32_t fonts; + uint32_t cmaps; + uint32_t facenames; + uint32_t othernames; + }; + + FontCounts mLoadStats; + + bool mLoadOtherNames; + bool mLoadFaceNames; + bool mLoadCmaps; + + // face name ==> per-face data + nsDataHashtable mFontFaceData; + + // canonical family name ==> array of localized family names + nsDataHashtable > mOtherFamilyNames; +}; + +// gfxFontInfoLoader - helper class for loading font info on async thread +// For large, "all fonts on system" data, data needed on a given platform +// (e.g. localized names, face names, cmaps) are loaded async. + +// helper class for loading in font info on a separate async thread +// once async thread completes, completion process is run on regular +// intervals to prevent tying up the main thread class gfxFontInfoLoader { public: @@ -26,6 +158,7 @@ public: typedef enum { stateInitial, stateTimerOnDelay, + stateAsyncLoad, stateTimerOnInterval, stateTimerOff } TimerState; @@ -40,9 +173,14 @@ public: // start timer with an initial delay, then call Run method at regular intervals void StartLoader(uint32_t aDelay, uint32_t aInterval); + // Finalize - async load complete, transfer data (on intervals if necessary) + virtual void FinalizeLoader(FontInfoData *aFontInfo); + // cancel the timer and cleanup void CancelLoader(); + uint32_t GetInterval() { return mInterval; } + protected: class ShutdownObserver : public nsIObserver { @@ -61,29 +199,51 @@ protected: gfxFontInfoLoader *mLoader; }; - // Init - initialization at start time after initial delay - virtual void InitLoader() = 0; - - // Run - called at intervals, return true to indicate done - virtual bool RunLoader() = 0; - - // Finish - cleanup after done - virtual void FinishLoader() = 0; - - // Timer interval callbacks - static void LoaderTimerCallback(nsITimer *aTimer, void *aThis) { - gfxFontInfoLoader *loader = static_cast(aThis); - loader->LoaderTimerFire(); + // CreateFontInfo - create platform-specific object used + // to load system-wide font info + virtual already_AddRefed CreateFontInfoData() { + return nullptr; } - void LoaderTimerFire(); + // Init - initialization before async loader thread runs + virtual void InitLoader() = 0; + // LoadFontInfo - transfer font info data within a time limit, return + // true when done + virtual bool LoadFontInfo() = 0; + + // Cleanup - finish and cleanup after done, including possible reflows + virtual void CleanupLoader() { + mFontInfo = nullptr; + } + + // Timer interval callbacks + static void LoadFontInfoCallback(nsITimer *aTimer, void *aThis) { + gfxFontInfoLoader *loader = static_cast(aThis); + loader->LoadFontInfoTimerFire(); + } + + static void DelayedStartCallback(nsITimer *aTimer, void *aThis) { + gfxFontInfoLoader *loader = static_cast(aThis); + loader->StartLoader(0, loader->GetInterval()); + } + + void LoadFontInfoTimerFire(); + + void AddShutdownObserver(); void RemoveShutdownObserver(); nsCOMPtr mTimer; nsCOMPtr mObserver; + nsCOMPtr mFontLoaderThread; uint32_t mInterval; TimerState mState; + + // after async font loader completes, data is stored here + nsRefPtr mFontInfo; + + // time spent on the loader thread + mozilla::TimeDuration mLoadTime; }; #endif /* GFX_FONT_INFO_LOADER_H */ diff --git a/gfx/thebes/gfxGDIFontList.cpp b/gfx/thebes/gfxGDIFontList.cpp index 09613402039d..da485c7f5d90 100644 --- a/gfx/thebes/gfxGDIFontList.cpp +++ b/gfx/thebes/gfxGDIFontList.cpp @@ -146,7 +146,7 @@ GDIFontEntry::GDIFontEntry(const nsAString& aFaceName, } nsresult -GDIFontEntry::ReadCMAP() +GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData) { // attempt this once, if errors occur leave a blank cmap if (mCharacterMap) { @@ -163,22 +163,28 @@ GDIFontEntry::ReadCMAP() return NS_ERROR_FAILURE; } - nsRefPtr charmap = new gfxCharacterMap(); - - uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); + nsRefPtr charmap; nsresult rv; + bool unicodeFont = false, symbolFont = false; - AutoFallibleTArray cmap; - rv = CopyFontTable(kCMAP, cmap); + if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, + mUVSOffset, + symbolFont))) { + mSymbolFont = symbolFont; + rv = NS_OK; + } else { + uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); + charmap = new gfxCharacterMap(); + AutoFallibleTArray cmap; + rv = CopyFontTable(kCMAP, cmap); - bool unicodeFont = false, symbolFont = false; // currently ignored - - if (NS_SUCCEEDED(rv)) { - rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(), - *charmap, mUVSOffset, - unicodeFont, symbolFont); + if (NS_SUCCEEDED(rv)) { + rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(), + *charmap, mUVSOffset, + unicodeFont, symbolFont); + } + mSymbolFont = symbolFont; } - mSymbolFont = symbolFont; mHasCmapTable = NS_SUCCEEDED(rv); if (mHasCmapTable) { @@ -512,7 +518,7 @@ GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe, } void -GDIFontFamily::FindStyleVariations() +GDIFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) { if (mHasStyles) return; diff --git a/gfx/thebes/gfxGDIFontList.h b/gfx/thebes/gfxGDIFontList.h index d312137ec86b..320a65961f2f 100644 --- a/gfx/thebes/gfxGDIFontList.h +++ b/gfx/thebes/gfxGDIFontList.h @@ -108,7 +108,7 @@ class GDIFontEntry : public gfxFontEntry public: LPLOGFONTW GetLogFont() { return &mLogFont; } - nsresult ReadCMAP(); + nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr); virtual bool IsSymbolFont(); @@ -294,7 +294,7 @@ public: GDIFontFamily(nsAString &aName) : gfxFontFamily(aName) {} - virtual void FindStyleVariations(); + virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr); private: static int CALLBACK FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe, diff --git a/gfx/thebes/gfxMacPlatformFontList.h b/gfx/thebes/gfxMacPlatformFontList.h index 9c26b5ccbf6c..5dfee13c0dd8 100644 --- a/gfx/thebes/gfxMacPlatformFontList.h +++ b/gfx/thebes/gfxMacPlatformFontList.h @@ -48,7 +48,7 @@ public: virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, FontListSizes* aSizes) const; - nsresult ReadCMAP(); + nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr); bool RequiresAATLayout() const { return mRequiresAAT; } @@ -116,6 +116,8 @@ private: virtual bool UsesSystemFallback() { return true; } + virtual already_AddRefed CreateFontInfoData(); + enum { kATSGenerationInitial = -1 }; diff --git a/gfx/thebes/gfxMacPlatformFontList.mm b/gfx/thebes/gfxMacPlatformFontList.mm index c2fbb98a1343..b0636605c303 100644 --- a/gfx/thebes/gfxMacPlatformFontList.mm +++ b/gfx/thebes/gfxMacPlatformFontList.mm @@ -222,30 +222,38 @@ SupportsScriptInGSUB(gfxFontEntry* aFontEntry, const hb_tag_t* aScriptTags) } nsresult -MacOSFontEntry::ReadCMAP() +MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData) { // attempt this once, if errors occur leave a blank cmap if (mCharacterMap) { return NS_OK; } - nsRefPtr charmap = new gfxCharacterMap(); - - uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); - - AutoTable cmapTable(this, kCMAP); + nsRefPtr charmap; nsresult rv; + bool symbolFont; - if (cmapTable) { - bool unicodeFont = false, symbolFont = false; // currently ignored - - uint32_t cmapLen; - const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen); - rv = gfxFontUtils::ReadCMAP((const uint8_t*)cmapData, cmapLen, - *charmap, mUVSOffset, - unicodeFont, symbolFont); + if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, + mUVSOffset, + symbolFont))) { + rv = NS_OK; } else { - rv = NS_ERROR_NOT_AVAILABLE; + uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); + charmap = new gfxCharacterMap(); + AutoTable cmapTable(this, kCMAP); + + if (cmapTable) { + bool unicodeFont = false, symbolFont = false; // currently ignored + uint32_t cmapLen; + const uint8_t* cmapData = + reinterpret_cast(hb_blob_get_data(cmapTable, + &cmapLen)); + rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, + *charmap, mUVSOffset, + unicodeFont, symbolFont); + } else { + rv = NS_ERROR_NOT_AVAILABLE; + } } if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) { @@ -446,7 +454,7 @@ public: virtual void LocalizedName(nsAString& aLocalizedName); - virtual void FindStyleVariations(); + virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr); }; void @@ -474,7 +482,7 @@ gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName) } void -gfxMacFontFamily::FindStyleVariations() +gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) { if (mHasStyles) return; @@ -1014,3 +1022,139 @@ gfxMacPlatformFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, return nullptr; } + +// used to load system-wide font info on off-main thread +class MacFontInfo : public FontInfoData { +public: + MacFontInfo(bool aLoadOtherNames, + bool aLoadFaceNames, + bool aLoadCmaps) : + FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) + {} + + virtual ~MacFontInfo() {} + + virtual void Load() { + nsAutoreleasePool localPool; + FontInfoData::Load(); + } + + // loads font data for all members of a given family + virtual void LoadFontFamilyData(const nsAString& aFamilyName); +}; + +void +MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName) +{ + // family name ==> CTFontDescriptor + NSString *famName = GetNSStringForString(aFamilyName); + CFStringRef family = CFStringRef(famName); + + CFMutableDictionaryRef attr = + CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family); + CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr); + CFRelease(attr); + CFArrayRef matchingFonts = + CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL); + CFRelease(fd); + if (!matchingFonts) { + return; + } + + nsTArray otherFamilyNames; + bool hasOtherFamilyNames = true; + + // iterate over faces in the family + int f, numFaces = (int) CFArrayGetCount(matchingFonts); + for (f = 0; f < numFaces; f++) { + mLoadStats.fonts++; + + CTFontDescriptorRef faceDesc = + (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f); + if (!faceDesc) { + continue; + } + CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc, + 0.0, nullptr); + + if (mLoadCmaps) { + // face name + CFStringRef faceName = (CFStringRef) + CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute); + + nsAutoTArray buffer; + CFIndex len = CFStringGetLength(faceName); + buffer.SetLength(len+1); + CFStringGetCharacters(faceName, ::CFRangeMake(0, len), + buffer.Elements()); + buffer[len] = 0; + nsAutoString fontName(reinterpret_cast(buffer.Elements()), + len); + + // load the cmap data + FontFaceData fontData; + CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap, + kCTFontTableOptionNoOptions); + if (cmapTable) { + bool unicodeFont = false, symbolFont = false; // ignored + const uint8_t *cmapData = + (const uint8_t*)CFDataGetBytePtr(cmapTable); + uint32_t cmapLen = CFDataGetLength(cmapTable); + nsRefPtr charmap = new gfxCharacterMap(); + uint32_t offset; + nsresult rv; + + rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset, + unicodeFont, symbolFont); + if (NS_SUCCEEDED(rv)) { + fontData.mCharacterMap = charmap; + fontData.mUVSOffset = offset; + fontData.mSymbolFont = symbolFont; + mLoadStats.cmaps++; + } + CFRelease(cmapTable); + } + + mFontFaceData.Put(fontName, fontData); + CFRelease(faceName); + } + + if (mLoadOtherNames && hasOtherFamilyNames) { + CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName, + kCTFontTableOptionNoOptions); + if (nameTable) { + const char *nameData = (const char*)CFDataGetBytePtr(nameTable); + uint32_t nameLen = CFDataGetLength(nameTable); + gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName, + nameData, nameLen, + otherFamilyNames, + false); + hasOtherFamilyNames = otherFamilyNames.Length() != 0; + CFRelease(nameTable); + } + } + + CFRelease(fontRef); + } + CFRelease(matchingFonts); + + // if found other names, insert them in the hash table + if (otherFamilyNames.Length() != 0) { + mOtherFamilyNames.Put(aFamilyName, otherFamilyNames); + mLoadStats.othernames += otherFamilyNames.Length(); + } +} + +already_AddRefed +gfxMacPlatformFontList::CreateFontInfoData() +{ + bool loadCmaps = !UsesSystemFallback() || + gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); + + nsRefPtr fi = + new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps); + return fi.forget(); +} + diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp index bf19e21ad848..dee2446080d2 100644 --- a/gfx/thebes/gfxPlatformFontList.cpp +++ b/gfx/thebes/gfxPlatformFontList.cpp @@ -558,6 +558,25 @@ static void LogRegistryEvent(const wchar_t *msg) } #endif +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; +} + gfxFontFamily* gfxPlatformFontList::FindFamily(const nsAString& aFamily) { @@ -569,12 +588,12 @@ gfxPlatformFontList::FindFamily(const nsAString& aFamily) // lookup in canonical (i.e. English) family name list if ((familyEntry = mFontFamilies.GetWeak(key))) { - return familyEntry; + return CheckFamily(familyEntry); } // lookup in other family names list (mostly localized names) if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) { - return familyEntry; + return CheckFamily(familyEntry); } // name not found and other family names not yet fully initialized so @@ -585,7 +604,7 @@ gfxPlatformFontList::FindFamily(const nsAString& aFamily) if (!mOtherFamilyNamesInitialized && !IsASCII(aFamily)) { InitOtherFamilyNames(); if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) { - return familyEntry; + return CheckFamily(familyEntry); } } @@ -705,49 +724,59 @@ gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap) } } +static PLDHashOperator AppendFamilyToList(nsStringHashKey::KeyType aKey, + nsRefPtr& aFamilyEntry, + void *aUserArg) +{ + nsTArray *familyNames = static_cast *>(aUserArg); + familyNames->AppendElement(aFamilyEntry->Name()); + return PL_DHASH_NEXT; +} + +void +gfxPlatformFontList::GetFontFamilyNames(nsTArray& aFontFamilyNames) +{ + mFontFamilies.Enumerate(AppendFamilyToList, &aFontFamilyNames); +} + void gfxPlatformFontList::InitLoader() { - GetFontFamilyList(mFontFamiliesToLoad); + GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad); mStartIndex = 0; - mNumFamilies = mFontFamiliesToLoad.Length(); + 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::RunLoader() +gfxPlatformFontList::LoadFontInfo() { TimeStamp start = TimeStamp::Now(); - uint32_t i, endIndex = (mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies); + 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++) { - gfxFontFamily* familyEntry = mFontFamiliesToLoad[i]; + nsAutoString key; + gfxFontFamily *familyEntry; + GenerateFontListKey(mFontInfo->mFontFamiliesToLoad[i], key); - // find all faces that are members of this family - familyEntry->FindStyleVariations(); - if (familyEntry->GetFontList().Length() == 0) { - // failed to load any faces for this family, so discard it - nsAutoString key; - GenerateFontListKey(familyEntry->Name(), key); - mFontFamilies.Remove(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(); + familyEntry->ReadAllCMAPs(mFontInfo); } - // read in face names - familyEntry->ReadFaceNames(this, NeedFullnamePostscriptNames()); - - // check whether the family can be considered "simple" for style matching - familyEntry->CheckForSimpleFamily(); - // limit the time spent reading fonts in one pass TimeDuration elapsed = TimeStamp::Now() - start; if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE && @@ -758,15 +787,42 @@ gfxPlatformFontList::RunLoader() } mStartIndex = endIndex; + bool done = mStartIndex >= mNumFamilies; - return (mStartIndex >= mNumFamilies); +#ifdef PR_LOGGING + 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"))); + } +#endif + + return done; } void -gfxPlatformFontList::FinishLoader() +gfxPlatformFontList::CleanupLoader() { mFontFamiliesToLoad.Clear(); mNumFamilies = 0; + +#ifdef PR_LOGGING + 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", + mLoadTime.ToMilliseconds(), + mFontInfo->mLoadStats.families, + mFontInfo->mLoadStats.fonts, + mFontInfo->mLoadStats.cmaps, + mFontInfo->mLoadStats.facenames, + mFontInfo->mLoadStats.othernames)); + } +#endif + + mOtherFamilyNamesInitialized = true; + mFaceNamesInitialized = true; + gfxFontInfoLoader::CleanupLoader(); } void diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h index 7f09d8aa74fc..3cb1afcaa1e8 100644 --- a/gfx/thebes/gfxPlatformFontList.h +++ b/gfx/thebes/gfxPlatformFontList.h @@ -82,7 +82,7 @@ struct FontListSizes { uint32_t mCharMapsSize; // memory used for cmap coverage info }; -class gfxPlatformFontList : protected gfxFontInfoLoader +class gfxPlatformFontList : public gfxFontInfoLoader { public: static gfxPlatformFontList* PlatformFontList() { @@ -211,6 +211,9 @@ protected: // if system fallback is used, no need to load all cmaps virtual bool UsesSystemFallback() { return false; } + // verifies that a family contains a non-zero font count + gfxFontFamily* CheckFamily(gfxFontFamily *aFamily); + // separate initialization for reading in name tables, since this is expensive void InitOtherFamilyNames(); @@ -241,10 +244,12 @@ protected: nsRefPtr& aFamilyEntry, void* aUserArg); + virtual void GetFontFamilyNames(nsTArray& aFontFamilyNames); + // gfxFontInfoLoader overrides, used to load in font cmaps virtual void InitLoader(); - virtual bool RunLoader(); - virtual void FinishLoader(); + virtual bool LoadFontInfo(); + virtual void CleanupLoader(); // read the loader initialization prefs, and start it void GetPrefsAndStartLoader();