bug 816483 - cache instantiated user fonts and share them across pages that use the same resources. r=roc

This commit is contained in:
Jonathan Kew 2012-12-10 09:31:07 +00:00
Родитель 943f5b03eb
Коммит a294160dcc
7 изменённых файлов: 294 добавлений и 76 удалений

Просмотреть файл

@ -196,6 +196,7 @@ FT2FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry,
fe->mItalic = aProxyEntry.mItalic;
fe->mWeight = aProxyEntry.mWeight;
fe->mStretch = aProxyEntry.mStretch;
fe->mIsUserFont = true;
}
return fe;
}

Просмотреть файл

@ -82,8 +82,14 @@ gfxCharacterMap::NotifyReleased()
delete this;
}
gfxFontEntry::~gfxFontEntry()
gfxFontEntry::~gfxFontEntry()
{
// For downloaded fonts, we need to tell the user font cache that this
// entry is being deleted.
if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) {
gfxUserFontSet::UserFontCache::ForgetFont(this);
}
if (mSVGGlyphs) {
delete mSVGGlyphs;
}
@ -1238,6 +1244,11 @@ gfxFontCache::gfxFontCache()
gfxFontCache::~gfxFontCache()
{
// Ensure the user font cache releases its references to font entries,
// so they aren't kept alive after the font instances and font-list
// have been shut down.
gfxUserFontSet::UserFontCache::Shutdown();
if (mWordCacheExpirationTimer) {
mWordCacheExpirationTimer->Cancel();
mWordCacheExpirationTimer = nullptr;

Просмотреть файл

@ -1504,6 +1504,15 @@ gfxFcFontSet::SortPreferredFonts(bool &aWaitForUserFont)
for (uint32_t f = 0; f < familyFonts->Length(); ++f) {
FcPattern *font = familyFonts->ElementAt(f);
// Fix up the family name of user-font patterns, as the same
// font entry may be used (via the UserFontCache) for multiple
// CSS family names
if (isUserFont) {
font = FcPatternDuplicate(font);
FcPatternDel(font, FC_FAMILY);
FcPatternAddString(font, FC_FAMILY, family);
}
// User fonts are already filtered by slant (but not size) in
// mUserFontSet->FindFontEntry().
if (!isUserFont && !SlantIsAcceptable(font, requestedSlant))
@ -1524,7 +1533,12 @@ gfxFcFontSet::SortPreferredFonts(bool &aWaitForUserFont)
// FcFontSetDestroy will remove a reference but FcFontSetAdd
// does _not_ take a reference!
if (FcFontSetAdd(fontSet, font)) {
FcPatternReference(font);
// We don't add a reference here for user fonts, because we're
// using a local clone of the pattern (see above) in order to
// override the family name
if (!isUserFont) {
FcPatternReference(font);
}
}
}
}

Просмотреть файл

@ -15,6 +15,8 @@
#include "prlong.h"
#include "nsNetUtil.h"
#include "nsIProtocolHandler.h"
#include "nsIPrincipal.h"
#include "mozilla/Telemetry.h"
#include "woff.h"
@ -406,6 +408,7 @@ StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy,
userFontData->mLocalName = src.mLocalName;
} else {
userFontData->mURI = src.mURI;
userFontData->mPrincipal = aProxy->mPrincipal;
}
userFontData->mFormat = src.mFormatFlags;
userFontData->mRealName = aOriginalName;
@ -559,48 +562,65 @@ gfxUserFontSet::LoadNext(gfxProxyFontEntry *aProxyEntry)
if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
currSrc.mFormatFlags)) {
nsresult rv;
bool loadDoesntSpin = false;
rv = NS_URIChainHasFlags(currSrc.mURI,
nsIProtocolHandler::URI_SYNC_LOAD_IS_OK,
&loadDoesntSpin);
nsIPrincipal *principal = nullptr;
nsresult rv = CheckFontLoad(&currSrc, &principal);
if (NS_SUCCEEDED(rv) && loadDoesntSpin) {
uint8_t *buffer = nullptr;
uint32_t bufferLength = 0;
// sync load font immediately
rv = SyncLoadFontData(aProxyEntry, &currSrc, buffer,
bufferLength);
if (NS_SUCCEEDED(rv) &&
LoadFont(aProxyEntry, buffer, bufferLength)) {
if (NS_SUCCEEDED(rv) && principal != nullptr) {
// see if we have an existing entry for this source
gfxFontEntry *fe =
UserFontCache::GetFont(currSrc.mURI, principal,
aProxyEntry);
if (fe) {
ReplaceFontEntry(aProxyEntry, fe);
return STATUS_LOADED;
} else {
LogMessage(aProxyEntry, "font load failed",
nsIScriptError::errorFlag, rv);
}
} else {
// otherwise load font async
rv = StartLoad(aProxyEntry, &currSrc);
bool loadOK = NS_SUCCEEDED(rv);
// record the principal returned by CheckFontLoad,
// for use when creating a channel
// and when caching the loaded entry
aProxyEntry->mPrincipal = principal;
if (loadOK) {
#ifdef PR_LOGGING
if (LOG_ENABLED()) {
nsAutoCString fontURI;
currSrc.mURI->GetSpec(fontURI);
LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
this, aProxyEntry->mSrcIndex, fontURI.get(),
NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
bool loadDoesntSpin = false;
rv = NS_URIChainHasFlags(currSrc.mURI,
nsIProtocolHandler::URI_SYNC_LOAD_IS_OK,
&loadDoesntSpin);
if (NS_SUCCEEDED(rv) && loadDoesntSpin) {
uint8_t *buffer = nullptr;
uint32_t bufferLength = 0;
// sync load font immediately
rv = SyncLoadFontData(aProxyEntry, &currSrc,
buffer, bufferLength);
if (NS_SUCCEEDED(rv) &&
(fe = LoadFont(aProxyEntry, buffer, bufferLength))) {
UserFontCache::CacheFont(fe);
return STATUS_LOADED;
} else {
LogMessage(aProxyEntry, "font load failed",
nsIScriptError::errorFlag, rv);
}
#endif
return STATUS_LOADING;
} else {
LogMessage(aProxyEntry, "download failed",
nsIScriptError::errorFlag, rv);
// otherwise load font async
rv = StartLoad(aProxyEntry, &currSrc);
if (NS_SUCCEEDED(rv)) {
#ifdef PR_LOGGING
if (LOG_ENABLED()) {
nsAutoCString fontURI;
currSrc.mURI->GetSpec(fontURI);
LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
this, aProxyEntry->mSrcIndex, fontURI.get(),
NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
}
#endif
return STATUS_LOADING;
} else {
LogMessage(aProxyEntry, "download failed",
nsIScriptError::errorFlag, rv);
}
}
} else {
LogMessage(aProxyEntry, "download not allowed",
nsIScriptError::errorFlag, rv);
}
} else {
// We don't log a warning to the web console yet,
@ -752,6 +772,7 @@ gfxUserFontSet::LoadFont(gfxProxyFontEntry *aProxy,
uint32_t(mGeneration)));
}
#endif
UserFontCache::CacheFont(fe);
ReplaceFontEntry(aProxy, fe);
} else {
#ifdef PR_LOGGING
@ -778,3 +799,94 @@ gfxUserFontSet::GetFamily(const nsAString& aFamilyName) const
return mFontFamilies.GetWeak(key);
}
///////////////////////////////////////////////////////////////////////////////
// gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
// across pages/fontsets rather than instantiating new platform fonts.
//
// Entries are added to this cache when a platform font is instantiated from
// downloaded data, and removed when the platform font entry is destroyed.
// We don't need to use a timed expiration scheme here because the gfxFontEntry
// for a downloaded font will be kept alive by its corresponding gfxFont
// instance(s) until they are deleted, and *that* happens using an expiration
// tracker (gfxFontCache). The result is that the downloaded font instances
// recorded here will persist between pages and can get reused (provided the
// source URI and principal match, of course).
///////////////////////////////////////////////////////////////////////////////
nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
gfxUserFontSet::UserFontCache::sUserFonts = nullptr;
bool
gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
{
bool equal;
if (NS_FAILED(mURI->Equals(aKey->mURI, &equal)) || !equal) {
return false;
}
if (NS_FAILED(mPrincipal->Equals(aKey->mPrincipal, &equal)) || !equal) {
return false;
}
const gfxFontEntry *fe = aKey->mFontEntry;
if (mFontEntry->mItalic != fe->mItalic ||
mFontEntry->mWeight != fe->mWeight ||
mFontEntry->mStretch != fe->mStretch ||
mFontEntry->mFeatureSettings != fe->mFeatureSettings ||
mFontEntry->mLanguageOverride != fe->mLanguageOverride) {
return false;
}
return true;
}
void
gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
{
if (!sUserFonts) {
sUserFonts = new nsTHashtable<Entry>;
sUserFonts->Init();
}
gfxUserFontData *data = aFontEntry->mUserFontData;
sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry));
}
void
gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry *aFontEntry)
{
if (!sUserFonts) {
// if we've already deleted the cache (i.e. during shutdown),
// just ignore this
return;
}
gfxUserFontData *data = aFontEntry->mUserFontData;
sUserFonts->RemoveEntry(Key(data->mURI, data->mPrincipal, aFontEntry));
}
gfxFontEntry*
gfxUserFontSet::UserFontCache::GetFont(nsIURI *aSrcURI,
nsIPrincipal *aPrincipal,
gfxProxyFontEntry *aProxy)
{
if (!sUserFonts) {
return nullptr;
}
Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy));
if (entry) {
return entry->GetFontEntry();
}
return nullptr;
}
void
gfxUserFontSet::UserFontCache::Shutdown()
{
if (sUserFonts) {
delete sUserFonts;
sUserFonts = nullptr;
}
}

Просмотреть файл

@ -14,10 +14,11 @@
#include "nsCOMPtr.h"
#include "nsIURI.h"
#include "nsIFile.h"
#include "nsIPrincipal.h"
#include "nsISupportsImpl.h"
#include "nsIScriptError.h"
#include "nsURIHashKey.h"
class nsIURI;
class gfxMixedFontFamily;
class nsFontFaceLoader;
@ -35,10 +36,9 @@ struct gfxFontFaceSrc {
uint32_t mFormatFlags;
nsString mLocalName; // full font name if local
nsCOMPtr<nsIURI> mURI; // uri if url
nsCOMPtr<nsIURI> mURI; // uri if url
nsCOMPtr<nsIURI> mReferrer; // referrer url if url
nsCOMPtr<nsISupports> mOriginPrincipal; // principal if url
nsCOMPtr<nsIPrincipal> mOriginPrincipal; // principal if url
};
// Subclassed to store platform-specific code cleaned out when font entry is
@ -56,6 +56,7 @@ public:
nsTArray<uint8_t> mMetadata; // woff metadata block (compressed), if any
nsCOMPtr<nsIURI> mURI; // URI of the source, if it was url()
nsCOMPtr<nsIPrincipal> 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
@ -202,10 +203,14 @@ public:
bool& aFoundFamily,
bool& aNeedsBold,
bool& aWaitForUserFont);
// check whether the given source is allowed to be loaded
virtual nsresult CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc,
nsIPrincipal **aPrincipal) = 0;
// initialize the process that loads external font data, which upon
// completion will call OnLoadComplete method
virtual nsresult StartLoad(gfxProxyFontEntry *aProxy,
virtual nsresult StartLoad(gfxProxyFontEntry *aProxy,
const gfxFontFaceSrc *aFontFaceSrc) = 0;
// when download has been completed, pass back data here
@ -231,6 +236,104 @@ public:
// increment the generation on font load
void IncrementGeneration();
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
static gfxFontEntry* GetFont(nsIURI *aSrcURI,
nsIPrincipal *aPrincipal,
gfxProxyFontEntry *aProxy);
// Clear everything so that we don't leak URIs and Principals.
static void Shutdown();
private:
// 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<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> mPrincipal;
gfxFontEntry *mFontEntry;
Key(nsIURI* aURI, nsIPrincipal* aPrincipal,
gfxFontEntry* aFontEntry)
: mURI(aURI),
mPrincipal(aPrincipal),
mFontEntry(aFontEntry)
{ }
};
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)
{ }
Entry(const Entry& aOther)
: mURI(aOther.mURI),
mPrincipal(aOther.mPrincipal),
mFontEntry(aOther.mFontEntry)
{ }
~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,
nsURIHashKey::HashKey(aKey->mURI),
HashFeatures(aKey->mFontEntry->mFeatureSettings),
( aKey->mFontEntry->mItalic |
(aKey->mFontEntry->mWeight << 1) |
(aKey->mFontEntry->mStretch << 10) ) ^
aKey->mFontEntry->mLanguageOverride);
}
enum { ALLOW_MEMMOVE = false };
gfxFontEntry* GetFontEntry() const { return mFontEntry; }
private:
static uint32_t
HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) {
return mozilla::HashBytes(aFeatures.Elements(),
aFeatures.Length() * sizeof(gfxFontFeature));
}
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> 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;
};
static nsTHashtable<Entry> *sUserFonts;
};
protected:
// for a given proxy font entry, attempt to load the next resource
// in the src list
@ -247,11 +350,7 @@ protected:
virtual nsresult SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
const gfxFontFaceSrc *aFontFaceSrc,
uint8_t* &aBuffer,
uint32_t &aBufferLength)
{
// implemented in nsUserFontSet
return NS_ERROR_NOT_IMPLEMENTED;
}
uint32_t &aBufferLength) = 0;
gfxMixedFontFamily *GetFamily(const nsAString& aName) const;
@ -320,6 +419,7 @@ public:
nsTArray<gfxFontFaceSrc> mSrcList;
uint32_t mSrcIndex; // index of loading src item
nsFontFaceLoader *mLoader; // current loader for this entry, if any
nsCOMPtr<nsIPrincipal> mPrincipal;
};

Просмотреть файл

@ -316,10 +316,6 @@ nsUserFontSet::StartLoad(gfxProxyFontEntry *aProxy,
const gfxFontFaceSrc *aFontFaceSrc)
{
nsresult rv;
nsIPrincipal *principal = nullptr;
rv = CheckFontLoad(aProxy, aFontFaceSrc, &principal);
NS_ENSURE_SUCCESS(rv, rv);
nsIPresShell *ps = mPresContext->PresShell();
if (!ps)
@ -332,7 +328,7 @@ nsUserFontSet::StartLoad(gfxProxyFontEntry *aProxy,
// get Content Security Policy from principal to pass into channel
nsCOMPtr<nsIChannelPolicy> channelPolicy;
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = principal->GetCsp(getter_AddRefs(csp));
rv = aProxy->mPrincipal->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
if (csp) {
channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
@ -382,8 +378,7 @@ nsUserFontSet::StartLoad(gfxProxyFontEntry *aProxy,
rv = channel->AsyncOpen(streamLoader, nullptr);
} else {
nsRefPtr<nsCORSListenerProxy> listener =
new nsCORSListenerProxy(streamLoader, principal,
false);
new nsCORSListenerProxy(streamLoader, aProxy->mPrincipal, false);
rv = listener->Init(channel);
if (NS_SUCCEEDED(rv)) {
rv = channel->AsyncOpen(listener, nullptr);
@ -802,12 +797,9 @@ nsUserFontSet::LogMessage(gfxProxyFontEntry *aProxy,
}
nsresult
nsUserFontSet::CheckFontLoad(gfxProxyFontEntry *aFontToLoad,
const gfxFontFaceSrc *aFontFaceSrc,
nsUserFontSet::CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc,
nsIPrincipal **aPrincipal)
{
nsresult rv;
// check same-site origin
nsIPresShell *ps = mPresContext->PresShell();
if (!ps)
@ -827,18 +819,11 @@ nsUserFontSet::CheckFontLoad(gfxProxyFontEntry *aFontToLoad,
NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
"null origin principal in @font-face rule");
if (aFontFaceSrc->mUseOriginPrincipal) {
nsCOMPtr<nsIPrincipal> p = do_QueryInterface(aFontFaceSrc->mOriginPrincipal);
*aPrincipal = p;
*aPrincipal = aFontFaceSrc->mOriginPrincipal;
}
rv = nsFontFaceLoader::CheckLoadAllowed(*aPrincipal, aFontFaceSrc->mURI,
ps->GetDocument());
if (NS_FAILED(rv)) {
LogMessage(aFontToLoad, "download not allowed",
nsIScriptError::errorFlag, rv);
}
return rv;
return nsFontFaceLoader::CheckLoadAllowed(*aPrincipal, aFontFaceSrc->mURI,
ps->GetDocument());
}
nsresult
@ -848,16 +833,12 @@ nsUserFontSet::SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
uint32_t &aBufferLength)
{
nsresult rv;
nsIPrincipal *principal = nullptr;
rv = CheckFontLoad(aFontToLoad, aFontFaceSrc, &principal);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> channel;
// get Content Security Policy from principal to pass into channel
nsCOMPtr<nsIChannelPolicy> channelPolicy;
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = principal->GetCsp(getter_AddRefs(csp));
rv = aFontToLoad->mPrincipal->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
if (csp) {
channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");

Просмотреть файл

@ -74,9 +74,8 @@ protected:
uint32_t aFlags = nsIScriptError::errorFlag,
nsresult aStatus = NS_OK);
nsresult CheckFontLoad(gfxProxyFontEntry *aFontToLoad,
const gfxFontFaceSrc *aFontFaceSrc,
nsIPrincipal **aPrincipal);
virtual nsresult CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc,
nsIPrincipal **aPrincipal);
virtual nsresult SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
const gfxFontFaceSrc *aFontFaceSrc,