diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index b38be303c61d..1b6df9e9f0c2 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -909,6 +909,8 @@ gfxUserFontSet::~gfxUserFontSet() if (fp) { fp->RemoveUserFontSet(this); } + + UserFontCache::ClearAllowedFontSets(this); } already_AddRefed @@ -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 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) diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h index 9d9e0753fb06..d90a8ca3f1d7 100644 --- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -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, bool> mAllowedFontSets; + nsCOMPtr mURI; nsCOMPtr mPrincipal; // or nullptr for data: URLs diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp index 63d7057e102e..479b3a64f553 100644 --- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -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) diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h index f72f6e94a7f9..24beb90f777f 100644 --- a/layout/style/FontFaceSet.h +++ b/layout/style/FontFaceSet.h @@ -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); diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index 5268b34de7aa..9e886928d6b9 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -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(); diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h index c120611c399b..cb6569289cdd 100644 --- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -583,6 +583,7 @@ private: bool mAuthorStyleDisabled; StylistState mStylistState; uint64_t mUserFontSetUpdateGeneration; + uint32_t mUserFontCacheUpdateGeneration; bool mNeedsRestyleAfterEnsureUniqueInner;