diff -r 000000000000 -r 6474c204b198 gfx/thebes/gfxUserFontSet.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/thebes/gfxUserFontSet.h Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,494 @@ +/* -*- 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/. */ + +#ifndef GFX_USER_FONT_SET_H +#define GFX_USER_FONT_SET_H + +#include "gfxFont.h" +#include "nsPresContext.h" +#include "nsRefPtrHashtable.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsIURI.h" +#include "nsIPrincipal.h" +#include "nsIScriptError.h" +#include "nsURIHashKey.h" + +class nsFontFaceLoader; + +//#define DEBUG_USERFONT_CACHE + +// parsed CSS @font-face rule information +// lifetime: from when @font-face rule processed until font is loaded +struct gfxFontFaceSrc { + bool mIsLocal; // url or local + + // if url, whether to use the origin principal or not + bool mUseOriginPrincipal; + + // format hint flags, union of all possible formats + // (e.g. TrueType, EOT, SVG, etc.) + // see FLAG_FORMAT_* enum values below + uint32_t mFormatFlags; + + nsString mLocalName; // full font name if local + nsCOMPtr mURI; // uri if url + nsCOMPtr mReferrer; // referrer url if url + nsCOMPtr mOriginPrincipal; // principal if url +}; + +inline bool +operator==(const gfxFontFaceSrc& a, const gfxFontFaceSrc& b) +{ + bool equals; + return (a.mIsLocal && b.mIsLocal && + a.mLocalName == b.mLocalName) || + (!a.mIsLocal && !b.mIsLocal && + a.mUseOriginPrincipal == b.mUseOriginPrincipal && + a.mFormatFlags == b.mFormatFlags && + NS_SUCCEEDED(a.mURI->Equals(b.mURI, &equals)) && equals && + NS_SUCCEEDED(a.mReferrer->Equals(b.mReferrer, &equals)) && equals && + a.mOriginPrincipal->Equals(b.mOriginPrincipal)); +} + +// Subclassed to store platform-specific code cleaned out when font entry is +// deleted. +// Lifetime: from when platform font is created until it is deactivated. +// If the platform does not need to add any platform-specific code/data here, +// then the gfxUserFontSet will allocate a base gfxUserFontData and attach +// to the entry to track the basic user font info fields here. +class gfxUserFontData { +public: + gfxUserFontData() + : mSrcIndex(0), mFormat(0), mMetaOrigLen(0) + { } + virtual ~gfxUserFontData() { } + + nsTArray mMetadata; // woff metadata block (compressed), if any + nsCOMPtr mURI; // URI of the source, if it was url() + nsCOMPtr mPrincipal; // principal for the download, if url() + nsString mLocalName; // font name used for the source, if local() + nsString mRealName; // original fullname from the font resource + uint32_t mSrcIndex; // index in the rule's source list + uint32_t mFormat; // format hint for the source used, if any + uint32_t mMetaOrigLen; // length needed to decompress metadata + bool mPrivate; // whether font belongs to a private window +}; + +// initially contains a set of proxy font entry objects, replaced with +// platform/user fonts as downloaded + +class gfxMixedFontFamily : public gfxFontFamily { +public: + friend class gfxUserFontSet; + + gfxMixedFontFamily(const nsAString& aName) + : gfxFontFamily(aName) { } + + virtual ~gfxMixedFontFamily() { } + + // Add the given font entry to the end of the family's list. + // Any earlier occurrence is removed, so this has the effect of "advancing" + // the entry to the end of the list. + void AddFontEntry(gfxFontEntry *aFontEntry) { + // We append to mAvailableFonts -before- searching for and removing + // any existing reference to avoid the risk that we'll remove the last + // reference to the font entry, and thus delete it. + mAvailableFonts.AppendElement(aFontEntry); + uint32_t i = mAvailableFonts.Length() - 1; + while (i > 0) { + if (mAvailableFonts[--i] == aFontEntry) { + mAvailableFonts.RemoveElementAt(i); + break; + } + } + aFontEntry->mFamilyName = Name(); + ResetCharacterMap(); + } + + // Replace aProxyFontEntry in the family's list with aRealFontEntry. + void ReplaceFontEntry(gfxFontEntry *aProxyFontEntry, + gfxFontEntry *aRealFontEntry) { + uint32_t numFonts = mAvailableFonts.Length(); + uint32_t i; + for (i = 0; i < numFonts; i++) { + gfxFontEntry *fe = mAvailableFonts[i]; + if (fe == aProxyFontEntry) { + // Note that this may delete aProxyFontEntry, if there's no + // other reference to it except from its family. + mAvailableFonts[i] = aRealFontEntry; + aRealFontEntry->mFamilyName = Name(); + break; + } + } + NS_ASSERTION(i < numFonts, "font entry not found in family!"); + ResetCharacterMap(); + } + + // Remove all font entries from the family + void DetachFontEntries() { + mAvailableFonts.Clear(); + } +}; + +class gfxProxyFontEntry; + +class gfxUserFontSet { + +public: + + NS_INLINE_DECL_REFCOUNTING(gfxUserFontSet) + + gfxUserFontSet(); + + enum { + // no flags ==> no hint set + // unknown ==> unknown format hint set + FLAG_FORMAT_UNKNOWN = 1, + FLAG_FORMAT_OPENTYPE = 1 << 1, + FLAG_FORMAT_TRUETYPE = 1 << 2, + FLAG_FORMAT_TRUETYPE_AAT = 1 << 3, + FLAG_FORMAT_EOT = 1 << 4, + FLAG_FORMAT_SVG = 1 << 5, + FLAG_FORMAT_WOFF = 1 << 6, + + // mask of all unused bits, update when adding new formats + FLAG_FORMAT_NOT_USED = ~((1 << 7)-1) + }; + + enum LoadStatus { + STATUS_LOADING = 0, + STATUS_LOADED, + STATUS_FORMAT_NOT_SUPPORTED, + STATUS_ERROR, + STATUS_END_OF_LIST + }; + + + // add in a font face + // weight - 0 == unknown, [100, 900] otherwise (multiples of 100) + // stretch = [NS_FONT_STRETCH_ULTRA_CONDENSED, NS_FONT_STRETCH_ULTRA_EXPANDED] + // italic style = constants in gfxFontConstants.h, e.g. NS_FONT_STYLE_NORMAL + // TODO: support for unicode ranges not yet implemented + gfxFontEntry *AddFontFace(const nsAString& aFamilyName, + const nsTArray& aFontFaceSrcList, + uint32_t aWeight, + int32_t aStretch, + uint32_t aItalicStyle, + const nsTArray& aFeatureSettings, + const nsString& aLanguageOverride, + gfxSparseBitSet *aUnicodeRanges = nullptr); + + // add in a font face for which we have the gfxFontEntry already + void AddFontFace(const nsAString& aFamilyName, gfxFontEntry* aFontEntry); + + // Whether there is a face with this family name + bool HasFamily(const nsAString& aFamilyName) const + { + return GetFamily(aFamilyName) != nullptr; + } + + gfxFontFamily *GetFamily(const nsAString& aName) const; + + // Lookup a font entry for a given style, returns null if not loaded. + // aFamily must be a family returned by our GetFamily method. + gfxFontEntry *FindFontEntry(gfxFontFamily *aFamily, + const gfxFontStyle& aFontStyle, + bool& aNeedsBold, + bool& aWaitForUserFont); + + // Find a family (possibly one of several!) that owns the given entry. + // This may be somewhat expensive, as it enumerates all the fonts in + // the set. Currently used only by the Linux (gfxPangoFontGroup) backend, + // which does not directly track families in the font group's list. + gfxFontFamily *FindFamilyFor(gfxFontEntry *aFontEntry) const; + + // check whether the given source is allowed to be loaded; + // returns the Principal (for use in the key when caching the loaded font), + // and whether the load should bypass the cache (force-reload). + virtual nsresult CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc, + nsIPrincipal **aPrincipal, + bool *aBypassCache) = 0; + + // initialize the process that loads external font data, which upon + // completion will call OnLoadComplete method + virtual nsresult StartLoad(gfxMixedFontFamily *aFamily, + gfxProxyFontEntry *aProxy, + const gfxFontFaceSrc *aFontFaceSrc) = 0; + + // when download has been completed, pass back data here + // aDownloadStatus == NS_OK ==> download succeeded, error otherwise + // returns true if platform font creation sucessful (or local() + // reference was next in line) + // Ownership of aFontData is passed in here; the font set must + // ensure that it is eventually deleted with NS_Free(). + bool OnLoadComplete(gfxMixedFontFamily *aFamily, + gfxProxyFontEntry *aProxy, + const uint8_t *aFontData, uint32_t aLength, + nsresult aDownloadStatus); + + // Replace a proxy with a real fontEntry; this is implemented in + // nsUserFontSet in order to keep track of the entry corresponding + // to each @font-face rule. + virtual void ReplaceFontEntry(gfxMixedFontFamily *aFamily, + gfxProxyFontEntry *aProxy, + gfxFontEntry *aFontEntry) = 0; + + // generation - each time a face is loaded, generation is + // incremented so that the change can be recognized + uint64_t GetGeneration() { return mGeneration; } + + // increment the generation on font load + void IncrementGeneration(); + + // rebuild if local rules have been used + void RebuildLocalRules(); + + virtual nsPresContext *GetPresContext() { return NULL; } + + class UserFontCache { + public: + // Record a loaded user-font in the cache. This requires that the + // font-entry's userFontData has been set up already, as it relies + // on the URI and Principal recorded there. + static void CacheFont(gfxFontEntry *aFontEntry); + + // The given gfxFontEntry is being destroyed, so remove any record that + // refers to it. + static void ForgetFont(gfxFontEntry *aFontEntry); + + // Return the gfxFontEntry corresponding to a given URI and principal, + // and the features of the given proxy, or nullptr if none is available. + // The aPrivate flag is set for requests coming from private windows, + // so we can avoid leaking fonts cached in private windows mode out to + // normal windows. + static gfxFontEntry* GetFont(nsIURI *aSrcURI, + nsIPrincipal *aPrincipal, + gfxProxyFontEntry *aProxy, + bool aPrivate); + + // Clear everything so that we don't leak URIs and Principals. + static void Shutdown(); + +#ifdef DEBUG_USERFONT_CACHE + // dump contents + static void Dump(); +#endif + + private: + // Helper that we use to observe the empty-cache notification + // from nsICacheService. + class Flusher : public nsIObserver + { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + Flusher() {} + virtual ~Flusher() {} + }; + + // Key used to look up entries in the user-font cache. + // Note that key comparison does *not* use the mFontEntry field + // as a whole; it only compares specific fields within the entry + // (weight/width/style/features) that could affect font selection + // or rendering, and that must match between a font-set's proxy + // entry and the corresponding "real" font entry. + struct Key { + nsCOMPtr mURI; + nsCOMPtr mPrincipal; + gfxFontEntry *mFontEntry; + bool mPrivate; + + Key(nsIURI* aURI, nsIPrincipal* aPrincipal, + gfxFontEntry* aFontEntry, bool aPrivate) + : mURI(aURI), + mPrincipal(aPrincipal), + mFontEntry(aFontEntry), + mPrivate(aPrivate) + { } + }; + + class Entry : public PLDHashEntryHdr { + public: + typedef const Key& KeyType; + typedef const Key* KeyTypePointer; + + Entry(KeyTypePointer aKey) + : mURI(aKey->mURI), + mPrincipal(aKey->mPrincipal), + mFontEntry(aKey->mFontEntry), + mPrivate(aKey->mPrivate) + { } + + Entry(const Entry& aOther) + : mURI(aOther.mURI), + mPrincipal(aOther.mPrincipal), + mFontEntry(aOther.mFontEntry), + mPrivate(aOther.mPrivate) + { } + + ~Entry() { } + + bool KeyEquals(const KeyTypePointer aKey) const; + + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } + + static PLDHashNumber HashKey(const KeyTypePointer aKey) { + uint32_t principalHash; + aKey->mPrincipal->GetHashValue(&principalHash); + return mozilla::HashGeneric(principalHash + int(aKey->mPrivate), + nsURIHashKey::HashKey(aKey->mURI), + HashFeatures(aKey->mFontEntry->mFeatureSettings), + mozilla::HashString(aKey->mFontEntry->mFamilyName), + ((uint32_t)aKey->mFontEntry->mItalic | + (aKey->mFontEntry->mWeight << 1) | + (aKey->mFontEntry->mStretch << 10) ) ^ + aKey->mFontEntry->mLanguageOverride); + } + + enum { ALLOW_MEMMOVE = false }; + + gfxFontEntry* GetFontEntry() const { return mFontEntry; } + + static PLDHashOperator RemoveIfPrivate(Entry* aEntry, void* aUserData); + static PLDHashOperator RemoveIfMatches(Entry* aEntry, void* aUserData); + static PLDHashOperator DisconnectSVG(Entry* aEntry, void* aUserData); + +#ifdef DEBUG_USERFONT_CACHE + static PLDHashOperator DumpEntry(Entry* aEntry, void* aUserData); +#endif + + private: + static uint32_t + HashFeatures(const nsTArray& aFeatures) { + return mozilla::HashBytes(aFeatures.Elements(), + aFeatures.Length() * sizeof(gfxFontFeature)); + } + + nsCOMPtr mURI; + nsCOMPtr mPrincipal; + + // The "real" font entry corresponding to this downloaded font. + // The font entry MUST notify the cache when it is destroyed + // (by calling Forget()). + gfxFontEntry *mFontEntry; + + // Whether this font was loaded from a private window. + bool mPrivate; + }; + + static nsTHashtable *sUserFonts; + }; + +protected: + // Protected destructor, to discourage deletion outside of Release(): + virtual ~gfxUserFontSet(); + + // Return whether the font set is associated with a private-browsing tab. + virtual bool GetPrivateBrowsing() = 0; + + // for a given proxy font entry, attempt to load the next resource + // in the src list + LoadStatus LoadNext(gfxMixedFontFamily *aFamily, + gfxProxyFontEntry *aProxyEntry); + + // helper method for creating a platform font + // returns font entry if platform font creation successful + // Ownership of aFontData is passed in here; the font set must + // ensure that it is eventually deleted with NS_Free(). + gfxFontEntry* LoadFont(gfxMixedFontFamily *aFamily, + gfxProxyFontEntry *aProxy, + const uint8_t *aFontData, uint32_t &aLength); + + // parse data for a data URL + virtual nsresult SyncLoadFontData(gfxProxyFontEntry *aFontToLoad, + const gfxFontFaceSrc *aFontFaceSrc, + uint8_t* &aBuffer, + uint32_t &aBufferLength) = 0; + + // report a problem of some kind (implemented in nsUserFontSet) + virtual nsresult LogMessage(gfxMixedFontFamily *aFamily, + gfxProxyFontEntry *aProxy, + const char *aMessage, + uint32_t aFlags = nsIScriptError::errorFlag, + nsresult aStatus = NS_OK) = 0; + + const uint8_t* SanitizeOpenTypeData(gfxMixedFontFamily *aFamily, + gfxProxyFontEntry *aProxy, + const uint8_t* aData, + uint32_t aLength, + uint32_t& aSaneLength, + bool aIsCompressed); + + static bool OTSMessage(void *aUserData, const char *format, ...); + + // helper method for performing the actual userfont set rebuild + virtual void DoRebuildUserFontSet() = 0; + + // font families defined by @font-face rules + nsRefPtrHashtable mFontFamilies; + + uint64_t mGeneration; + + // true when local names have been looked up, false otherwise + bool mLocalRulesUsed; + + static PRLogModuleInfo* GetUserFontsLog(); + +private: + static void CopyWOFFMetadata(const uint8_t* aFontData, + uint32_t aLength, + FallibleTArray* aMetadata, + uint32_t* aMetaOrigLen); +}; + +// acts a placeholder until the real font is downloaded + +class gfxProxyFontEntry : public gfxFontEntry { + friend class gfxUserFontSet; + +public: + gfxProxyFontEntry(const nsTArray& aFontFaceSrcList, + uint32_t aWeight, + int32_t aStretch, + uint32_t aItalicStyle, + const nsTArray& aFeatureSettings, + uint32_t aLanguageOverride, + gfxSparseBitSet *aUnicodeRanges); + + virtual ~gfxProxyFontEntry(); + + // Return whether the entry matches the given list of attributes + bool Matches(const nsTArray& aFontFaceSrcList, + uint32_t aWeight, + int32_t aStretch, + uint32_t aItalicStyle, + const nsTArray& aFeatureSettings, + uint32_t aLanguageOverride, + gfxSparseBitSet *aUnicodeRanges); + + virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold); + + // note that code depends on the ordering of these values! + enum LoadingState { + NOT_LOADING = 0, // not started to load any font resources yet + LOADING_STARTED, // loading has started; hide fallback font + LOADING_ALMOST_DONE, // timeout happened but we're nearly done, + // so keep hiding fallback font + LOADING_SLOWLY, // timeout happened and we're not nearly done, + // so use the fallback font + LOADING_FAILED // failed to load any source: use fallback + }; + LoadingState mLoadingState; + bool mUnsupportedFormat; + + nsTArray mSrcList; + uint32_t mSrcIndex; // index of loading src item + nsFontFaceLoader *mLoader; // current loader for this entry, if any + nsCOMPtr mPrincipal; +}; + + +#endif /* GFX_USER_FONT_SET_H */