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