Bug 1376964 - Part 4: Call FontLoadAllowed ahead of time and cache the results for style worker threads. r=jfkthame

Handling a document's node principal changing is done in part 9.

MozReview-Commit-ID: 1gPtRpddys5

--HG--
extra : rebase_source : 5b1d40af5ad0484440075e7229dc9ae3d5a13764
This commit is contained in:
Cameron McCormack 2017-07-12 13:03:35 +08:00
parent 5b5b4e145c
commit b2cd9f4a51
6 changed files with 177 additions and 13 deletions

View File

@ -909,6 +909,8 @@ gfxUserFontSet::~gfxUserFontSet()
if (fp) {
fp->RemoveUserFontSet(this);
}
UserFontCache::ClearAllowedFontSets(this);
}
already_AddRefed<gfxUserFontEntry>
@ -1279,13 +1281,6 @@ gfxUserFontSet::UserFontCache::GetFont(nsIURI* aSrcURI,
return nullptr;
}
// We have to perform another content policy check here to prevent
// cache poisoning. E.g. a.com loads a font into the cache but
// b.com has a CSP not allowing any fonts to be loaded.
if (!aUserFontEntry->mFontSet->IsFontLoadAllowed(aSrcURI, aPrincipal)) {
return nullptr;
}
// Ignore principal when looking up a data: URI.
nsIPrincipal* principal;
if (IgnorePrincipal(aSrcURI)) {
@ -1296,11 +1291,77 @@ gfxUserFontSet::UserFontCache::GetFont(nsIURI* aSrcURI,
Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, principal, aUserFontEntry,
aPrivate));
if (entry) {
return entry->GetFontEntry();
if (!entry) {
return nullptr;
}
return nullptr;
// We have to perform another content policy check here to prevent
// cache poisoning. E.g. a.com loads a font into the cache but
// b.com has a CSP not allowing any fonts to be loaded.
bool allowed = false;
if (ServoStyleSet::IsInServoTraversal()) {
// Use the cached IsFontLoadAllowed results in mAllowedFontSets.
allowed = entry->IsFontSetAllowed(aUserFontEntry->mFontSet);
} else {
// Call IsFontLoadAllowed directly, since we are on the main thread.
MOZ_ASSERT(NS_IsMainThread());
allowed = aUserFontEntry->mFontSet->IsFontLoadAllowed(aSrcURI,
aPrincipal);
MOZ_ASSERT(!entry->IsFontSetAllowedKnown(aUserFontEntry->mFontSet) ||
entry->IsFontSetAllowed(aUserFontEntry->mFontSet) == allowed,
"why does IsFontLoadAllowed return a different value from "
"the cached value in mAllowedFontSets?");
}
if (!allowed) {
return nullptr;
}
return entry->GetFontEntry();
}
/* static */ void
gfxUserFontSet::UserFontCache::UpdateAllowedFontSets(
gfxUserFontSet* aUserFontSet)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sUserFonts) {
return;
}
for (auto iter = sUserFonts->Iter(); !iter.Done(); iter.Next()) {
Entry* entry = iter.Get();
if (!entry->IsFontSetAllowedKnown(aUserFontSet)) {
nsIPrincipal* principal = entry->GetPrincipal();
if (!principal) {
// This is a data: URI. Just get the standard principal the
// font set uses. (For cases when mUseOriginPrincipal is true,
// we don't use the cached results of IsFontLoadAllowed, and
// instead just process the data: URI load async.)
principal = aUserFontSet->GetStandardFontLoadPrincipal();
}
bool allowed =
aUserFontSet->IsFontLoadAllowed(entry->GetURI(), principal);
entry->SetIsFontSetAllowed(aUserFontSet, allowed);
}
}
}
/* static */ void
gfxUserFontSet::UserFontCache::ClearAllowedFontSets(
gfxUserFontSet* aUserFontSet)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sUserFonts) {
return;
}
for (auto iter = sUserFonts->Iter(); !iter.Done(); iter.Next()) {
Entry* entry = iter.Get();
entry->ClearIsFontSetAllowed(aUserFontSet);
}
}
void
@ -1314,6 +1375,40 @@ gfxUserFontSet::UserFontCache::Shutdown()
MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)
bool
gfxUserFontSet::UserFontCache::Entry::IsFontSetAllowed(
gfxUserFontSet* aUserFontSet) const
{
bool allowed = false;
DebugOnly<bool> found = mAllowedFontSets.Get(aUserFontSet, &allowed);
MOZ_ASSERT(found, "UpdateAllowedFontSets should have been called and "
"added an entry to mAllowedFontSets");
return allowed;
}
bool
gfxUserFontSet::UserFontCache::Entry::IsFontSetAllowedKnown(
gfxUserFontSet* aUserFontSet) const
{
return mAllowedFontSets.Contains(aUserFontSet);
}
void
gfxUserFontSet::UserFontCache::Entry::SetIsFontSetAllowed(
gfxUserFontSet* aUserFontSet,
bool aAllowed)
{
MOZ_ASSERT(!IsFontSetAllowedKnown(aUserFontSet));
mAllowedFontSets.Put(aUserFontSet, aAllowed);
}
void
gfxUserFontSet::UserFontCache::Entry::ClearIsFontSetAllowed(
gfxUserFontSet* aUserFontSet)
{
mAllowedFontSets.Remove(aUserFontSet);
}
void
gfxUserFontSet::UserFontCache::Entry::ReportMemory(
nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)

View File

@ -256,6 +256,8 @@ public:
nsIPrincipal** aPrincipal,
bool* aBypassCache) = 0;
virtual nsIPrincipal* GetStandardFontLoadPrincipal() = 0;
// check whether content policies allow the given URI to load.
virtual bool IsFontLoadAllowed(nsIURI* aFontLocation,
nsIPrincipal* aPrincipal) = 0;
@ -305,6 +307,25 @@ public:
// the cache. (Removals don't increment it.)
static uint32_t Generation() { return sGeneration; }
// For each entry in the user font cache where we haven't recorded
// whether the given user font set is allowed to use the entry,
// call IsFontLoadAllowed and record it.
//
// This function should be called just before a Servo restyle, so
// that we can determine whether a given font load (using a cached
// font) would be allowed without having to call the non-OMT-safe
// IsFontLoadAllowed from the style worker threads.
static void UpdateAllowedFontSets(gfxUserFontSet* aUserFontSet);
// Clears all recorded IsFontLoadAllowed results for the given
// user font set.
//
// This function should be called just before the user font set is
// going away, or when we detect that a document's node principal
// has changed (and thus the already recorded IsFontLoadAllowed
// results are no longer valid).
static void ClearAllowedFontSets(gfxUserFontSet* aUserFontSet);
// Clear everything so that we don't leak URIs and Principals.
static void Shutdown();
@ -401,10 +422,16 @@ public:
enum { ALLOW_MEMMOVE = false };
nsIURI* GetURI() const { return mURI; }
nsIPrincipal* GetPrincipal() const { return mPrincipal; }
gfxFontEntry* GetFontEntry() const { return mFontEntry; }
bool IsPrivate() const { return mPrivate; }
bool IsFontSetAllowed(gfxUserFontSet* aUserFontSet) const;
bool IsFontSetAllowedKnown(gfxUserFontSet* aUserFontSet) const;
void SetIsFontSetAllowed(gfxUserFontSet* aUserFontSet, bool aAllowed);
void ClearIsFontSetAllowed(gfxUserFontSet* aUserFontSet);
void ReportMemory(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize);
@ -419,6 +446,20 @@ public:
aFeatures.Length() * sizeof(gfxFontFeature));
}
// Set of gfxUserFontSets that are allowed to use this cached font
// entry.
//
// This is basically a cache of results of calls to
// gfxUserFontSet::IsFontLoadAllowed for each font set to be used
// when using the cache from style worker threads (where calling
// IsFontLoadAllowed is not possible). Whenever a new entry is
// added to the cache, sGeneration is bumped, and a FontFaceSet
// for a document about to be styled can call UpdateAllowedFontSets
// to record IsFontLoadAllowed results for the new entries. When
// a FontFaceSet is going away, it calls ClearAllowedFontSets
// to remove entries from the mAllowedFontSets tables.
nsDataHashtable<nsPtrHashKey<gfxUserFontSet>, bool> mAllowedFontSets;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> mPrincipal; // or nullptr for data: URLs

View File

@ -1326,6 +1326,12 @@ FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry,
return NS_OK;
}
nsIPrincipal*
FontFaceSet::GetStandardFontLoadPrincipal()
{
return mDocument->NodePrincipal();
}
nsresult
FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
nsIPrincipal** aPrincipal,
@ -1344,7 +1350,7 @@ FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
// use document principal, original principal if flag set
// this enables user stylesheets to load font files via
// @font-face rules
*aPrincipal = mDocument->NodePrincipal();
*aPrincipal = GetStandardFontLoadPrincipal();
NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
"null origin principal in @font-face rule");
@ -1797,6 +1803,15 @@ FontFaceSet::UserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
return mFontFaceSet->CheckFontLoad(aFontFaceSrc, aPrincipal, aBypassCache);
}
/* virtual */ nsIPrincipal*
FontFaceSet::UserFontSet::GetStandardFontLoadPrincipal()
{
if (!mFontFaceSet) {
return nullptr;
}
return mFontFaceSet->GetStandardFontLoadPrincipal();
}
/* virtual */ bool
FontFaceSet::UserFontSet::IsFontLoadAllowed(nsIURI* aFontLocation,
nsIPrincipal* aPrincipal)

View File

@ -63,6 +63,8 @@ public:
FontFaceSet* GetFontFaceSet() { return mFontFaceSet; }
nsIPrincipal* GetStandardFontLoadPrincipal() override;
virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
nsIPrincipal** aPrincipal,
bool* aBypassCache) override;
@ -261,6 +263,7 @@ private:
nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
const gfxFontFaceSrc* aFontFaceSrc);
nsIPrincipal* GetStandardFontLoadPrincipal();
nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
nsIPrincipal** aPrincipal,
bool* aBypassCache);

View File

@ -40,6 +40,7 @@ ServoStyleSet::ServoStyleSet()
, mAuthorStyleDisabled(false)
, mStylistState(StylistState::NotDirty)
, mUserFontSetUpdateGeneration(0)
, mUserFontCacheUpdateGeneration(0)
, mNeedsRestyleAfterEnsureUniqueInner(false)
{
}
@ -298,13 +299,21 @@ ServoStyleSet::PreTraverseSync()
// it so force computation early.
mPresContext->Document()->GetDocumentState();
// Ensure that the @font-face data is not stale
if (gfxUserFontSet* userFontSet = mPresContext->Document()->GetUserFontSet()) {
// Ensure that the @font-face data is not stale
uint64_t generation = userFontSet->GetGeneration();
if (generation != mUserFontSetUpdateGeneration) {
mPresContext->DeviceContext()->UpdateFontCacheUserFonts(userFontSet);
mUserFontSetUpdateGeneration = generation;
}
// Ensure that the user font cache holds up-to-date data on whether
// our font set is allowed to re-use fonts from the cache.
uint32_t cacheGeneration = gfxUserFontSet::UserFontCache::Generation();
if (cacheGeneration != mUserFontCacheUpdateGeneration) {
gfxUserFontSet::UserFontCache::UpdateAllowedFontSets(userFontSet);
mUserFontCacheUpdateGeneration = cacheGeneration;
}
}
UpdateStylistIfNeeded();

View File

@ -583,6 +583,7 @@ private:
bool mAuthorStyleDisabled;
StylistState mStylistState;
uint64_t mUserFontSetUpdateGeneration;
uint32_t mUserFontCacheUpdateGeneration;
bool mNeedsRestyleAfterEnsureUniqueInner;