From a4433a4de898334fb6e3a8a32ec944f729c46ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 23 Mar 2018 16:06:56 +0100 Subject: [PATCH] Bug 1420680: Rework how the loadability of font-faces is computed. r=jfkthame,bz This reworks bug 1440561 so that we only precompute loads that belong to our user font set, avoiding messing up with fonts in the cache that belong to other pages. The loadability of a font is precomputed in PreTraverse in the same way as we did, but only for the fonts that we may end up loading. This is stored in FontFaceSet now. Also, the principal shenanigans that this code did are reworked to be explicit about when the document principal changes in ResetToURI, instead of having a member around and a mutable variable. This makes the code easier to follow. MozReview-Commit-ID: 9ofTbaLDUF7 --- dom/base/nsDocument.cpp | 4 + gfx/thebes/gfxUserFontSet.cpp | 300 ++++++++++----------------------- gfx/thebes/gfxUserFontSet.h | 112 +++--------- layout/style/FontFaceSet.cpp | 141 ++++++---------- layout/style/FontFaceSet.h | 57 +++---- layout/style/ServoStyleSet.cpp | 19 +-- layout/style/ServoStyleSet.h | 1 - 7 files changed, 194 insertions(+), 440 deletions(-) diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index df9b08826707..a45c79c5cd41 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -2370,6 +2370,10 @@ nsIDocument::ResetToURI(nsIURI* aURI, } } + if (mFontFaceSet) { + mFontFaceSet->RefreshStandardFontLoadPrincipal(); + } + // Refresh the principal on the compartment. if (nsPIDOMWindowInner* win = GetInnerWindow()) { nsGlobalWindowInner::Cast(win)->RefreshCompartmentPrincipal(); diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index eacb5028d290..564e8e7ad25f 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -333,6 +333,16 @@ gfxUserFontFamily::~gfxUserFontFamily() MOZ_ASSERT(NS_IsMainThread()); } +gfxFontSrcPrincipal* +gfxFontFaceSrc::LoadPrincipal(const gfxUserFontSet& aFontSet) const +{ + MOZ_ASSERT(mSourceType == eSourceType_URL); + if (mUseOriginPrincipal && mOriginPrincipal) { + return mOriginPrincipal; + } + return aFontSet.GetStandardFontLoadPrincipal(); +} + void gfxUserFontEntry::GetFamilyNameAndURIForLogging(nsACString& aFamilyName, nsACString& aURI) @@ -561,95 +571,77 @@ gfxUserFontEntry::DoLoadNextSrc(bool aForceAsync) } } - gfxFontSrcPrincipal* principal = nullptr; - bool bypassCache; - nsresult rv = mFontSet->CheckFontLoad(&currSrc, &principal, - &bypassCache); - - if (NS_SUCCEEDED(rv) && principal != nullptr) { - if (!bypassCache) { - // see if we have an existing entry for this source - gfxFontEntry* fe = gfxUserFontSet:: - UserFontCache::GetFont(currSrc.mURI, - principal, - this, - mFontSet->GetPrivateBrowsing()); - if (fe) { - mPlatformFontEntry = fe; - SetLoadState(STATUS_LOADED); - if (LOG_ENABLED()) { - LOG(("userfonts (%p) [src %d] " - "loaded uri from cache: (%s) for (%s)\n", - mFontSet, mSrcIndex, - currSrc.mURI->GetSpecOrDefault().get(), - NS_ConvertUTF16toUTF8(mFamilyName).get())); - } - return; - } + // see if we have an existing entry for this source + gfxFontEntry* fe = + gfxUserFontSet::UserFontCache::GetFont(currSrc, *this); + if (fe) { + mPlatformFontEntry = fe; + SetLoadState(STATUS_LOADED); + if (LOG_ENABLED()) { + LOG(("userfonts (%p) [src %d] " + "loaded uri from cache: (%s) for (%s)\n", + mFontSet, mSrcIndex, + currSrc.mURI->GetSpecOrDefault().get(), + NS_ConvertUTF16toUTF8(mFamilyName).get())); } + return; + } - if (ServoStyleSet* set = ServoStyleSet::Current()) { - // If we need to start a font load and we're on a style - // worker thread, we have to defer it. - set->AppendTask(PostTraversalTask::LoadFontEntry(this)); - SetLoadState(STATUS_LOAD_PENDING); + if (ServoStyleSet* set = ServoStyleSet::Current()) { + // If we need to start a font load and we're on a style + // worker thread, we have to defer it. + set->AppendTask(PostTraversalTask::LoadFontEntry(this)); + SetLoadState(STATUS_LOAD_PENDING); + return; + } + + // record the principal we should use for the load for use when + // creating a channel and when caching the loaded entry. + mPrincipal = currSrc.LoadPrincipal(*mFontSet); + + bool loadDoesntSpin = + !aForceAsync && currSrc.mURI->SyncLoadIsOK(); + + if (loadDoesntSpin) { + uint8_t* buffer = nullptr; + uint32_t bufferLength = 0; + + // sync load font immediately + nsresult rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer, + bufferLength); + + if (NS_SUCCEEDED(rv) && + LoadPlatformFont(buffer, bufferLength)) { + SetLoadState(STATUS_LOADED); + Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE, + currSrc.mSourceType + 1); return; - } - - // record the principal returned by CheckFontLoad, - // for use when creating a channel - // and when caching the loaded entry - mPrincipal = principal; - - bool loadDoesntSpin = false; - if (!aForceAsync) { - loadDoesntSpin = currSrc.mURI->SyncLoadIsOK(); - } - - if (NS_SUCCEEDED(rv) && loadDoesntSpin) { - uint8_t* buffer = nullptr; - uint32_t bufferLength = 0; - - // sync load font immediately - rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer, - bufferLength); - - if (NS_SUCCEEDED(rv) && - LoadPlatformFont(buffer, bufferLength)) { - SetLoadState(STATUS_LOADED); - Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE, - currSrc.mSourceType + 1); - return; - } else { - mFontSet->LogMessage(this, - "font load failed", - nsIScriptError::errorFlag, - rv); - } - } else { - // otherwise load font async - rv = mFontSet->StartLoad(this, &currSrc); - bool loadOK = NS_SUCCEEDED(rv); - - if (loadOK) { - if (LOG_ENABLED()) { - LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n", - mFontSet, mSrcIndex, - currSrc.mURI->GetSpecOrDefault().get(), - NS_ConvertUTF16toUTF8(mFamilyName).get())); - } - return; - } else { - mFontSet->LogMessage(this, - "download failed", - nsIScriptError::errorFlag, - rv); - } + mFontSet->LogMessage(this, + "font load failed", + nsIScriptError::errorFlag, + rv); } + } else { - mFontSet->LogMessage(this, "download not allowed", - nsIScriptError::errorFlag, rv); + // otherwise load font async + nsresult rv = mFontSet->StartLoad(this, &currSrc); + bool loadOK = NS_SUCCEEDED(rv); + + if (loadOK) { + if (LOG_ENABLED()) { + LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n", + mFontSet, mSrcIndex, + currSrc.mURI->GetSpecOrDefault().get(), + NS_ConvertUTF16toUTF8(mFamilyName).get())); + } + return; + } else { + mFontSet->LogMessage(this, + "download failed", + nsIScriptError::errorFlag, + rv); + } } } else { // We don't log a warning to the web console yet, @@ -937,8 +929,6 @@ gfxUserFontSet::~gfxUserFontSet() if (fp) { fp->RemoveUserFontSet(this); } - - UserFontCache::ClearAllowedFontSets(this); } already_AddRefed @@ -1292,26 +1282,25 @@ gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry* aFontEntry) } gfxFontEntry* -gfxUserFontSet::UserFontCache::GetFont(gfxFontSrcURI* aSrcURI, - gfxFontSrcPrincipal* aPrincipal, - gfxUserFontEntry* aUserFontEntry, - bool aPrivate) +gfxUserFontSet::UserFontCache::GetFont(const gfxFontFaceSrc& aSrc, + const gfxUserFontEntry& aUserFontEntry) { if (!sUserFonts || + aUserFontEntry.mFontSet->BypassCache() || Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) { return nullptr; } // Ignore principal when looking up a data: URI. - gfxFontSrcPrincipal* principal; - if (IgnorePrincipal(aSrcURI)) { - principal = nullptr; - } else { - principal = aPrincipal; - } + gfxFontSrcPrincipal* principal = IgnorePrincipal(aSrc.mURI) + ? nullptr + : aSrc.LoadPrincipal(*aUserFontEntry.mFontSet); - Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, principal, aUserFontEntry, - aPrivate)); + Entry* entry = sUserFonts->GetEntry( + Key(aSrc.mURI, + principal, + const_cast(&aUserFontEntry), + aUserFontEntry.mFontSet->GetPrivateBrowsing())); if (!entry) { return nullptr; } @@ -1319,79 +1308,13 @@ gfxUserFontSet::UserFontCache::GetFont(gfxFontSrcURI* aSrcURI, // 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->CheckIsFontSetAllowedAndDispatchViolations( - aUserFontEntry->mFontSet); - } else { - // Call IsFontLoadAllowed directly, since we are on the main thread. - MOZ_ASSERT(NS_IsMainThread()); - nsIPrincipal* principal = aPrincipal ? aPrincipal->get() : nullptr; - allowed = aUserFontEntry->mFontSet->IsFontLoadAllowed( - aSrcURI->get(), - principal, - /* aViolations */ nullptr); - MOZ_ASSERT(!entry->IsFontSetAllowedKnown(aUserFontEntry->mFontSet) || - entry->CheckIsFontSetAllowed(aUserFontEntry->mFontSet) == allowed, - "why does IsFontLoadAllowed return a different value from " - "the cached value in mAllowedFontSets?"); - } - - if (!allowed) { + if (!aUserFontEntry.mFontSet->IsFontLoadAllowed(aSrc)) { 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)) { - gfxFontSrcPrincipal* 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(); - } - nsTArray> violations; - bool allowed = - aUserFontSet->IsFontLoadAllowed(entry->GetURI()->get(), - principal->get(), - &violations); - entry->SetIsFontSetAllowed(aUserFontSet, allowed, Move(violations)); - } - } -} - -/* 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 gfxUserFontSet::UserFontCache::Shutdown() { @@ -1403,55 +1326,6 @@ gfxUserFontSet::UserFontCache::Shutdown() MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf) -bool -gfxUserFontSet::UserFontCache::Entry::CheckIsFontSetAllowed( - gfxUserFontSet* aUserFontSet) const -{ - LoadResultEntry* entry = mAllowedFontSets.GetEntry(aUserFontSet); - MOZ_ASSERT(entry, "UpdateAllowedFontSets should have been called and " - "added an entry to mAllowedFontSets"); - return entry->mAllowed; -} - -bool -gfxUserFontSet::UserFontCache::Entry::CheckIsFontSetAllowedAndDispatchViolations( - gfxUserFontSet* aUserFontSet) const -{ - LoadResultEntry* entry = mAllowedFontSets.GetEntry(aUserFontSet); - MOZ_ASSERT(entry, "UpdateAllowedFontSets should have been called and " - "added an entry to mAllowedFontSets"); - if (!entry->mViolations.IsEmpty()) { - aUserFontSet->DispatchFontLoadViolations(entry->mViolations); - } - return entry->mAllowed; -} - -bool -gfxUserFontSet::UserFontCache::Entry::IsFontSetAllowedKnown( - gfxUserFontSet* aUserFontSet) const -{ - return mAllowedFontSets.Contains(aUserFontSet); -} - -void -gfxUserFontSet::UserFontCache::Entry::SetIsFontSetAllowed( - gfxUserFontSet* aUserFontSet, - bool aAllowed, - nsTArray>&& aViolations) -{ - MOZ_ASSERT(!IsFontSetAllowedKnown(aUserFontSet)); - LoadResultEntry* entry = mAllowedFontSets.PutEntry(aUserFontSet); - entry->mAllowed = aAllowed; - entry->mViolations.SwapElements(aViolations); -} - -void -gfxUserFontSet::UserFontCache::Entry::ClearIsFontSetAllowed( - gfxUserFontSet* aUserFontSet) -{ - mAllowedFontSets.RemoveEntry(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 26463d446200..59037723deae 100644 --- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -63,6 +63,10 @@ struct gfxFontFaceSrc { RefPtr mOriginPrincipal; // principal if url RefPtr mBuffer; + + // The principal that should be used for the load. Should only be used for + // URL sources. + gfxFontSrcPrincipal* LoadPrincipal(const gfxUserFontSet&) const; }; inline bool @@ -265,19 +269,10 @@ public: // Look up names in a fontlist and return true if any are in the set bool ContainsUserFontSetFonts(const mozilla::FontFamilyList& aFontList) 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, - gfxFontSrcPrincipal** aPrincipal, - bool* aBypassCache) = 0; - - virtual gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() = 0; + virtual gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() const = 0; // check whether content policies allow the given URI to load. - virtual bool IsFontLoadAllowed(nsIURI* aFontLocation, - nsIPrincipal* aPrincipal, - nsTArray>* aViolations) = 0; + virtual bool IsFontLoadAllowed(const gfxFontFaceSrc&) = 0; // Dispatches all of the specified runnables to the font face set's // document's event queue. @@ -320,34 +315,12 @@ public: // 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(gfxFontSrcURI* aSrcURI, - gfxFontSrcPrincipal* aPrincipal, - gfxUserFontEntry* aUserFontEntry, - bool aPrivate); + static gfxFontEntry* GetFont(const gfxFontFaceSrc&, const gfxUserFontEntry&); // Generation number that is incremented whenever an entry is added to // 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(); @@ -415,11 +388,10 @@ public: { } Entry(Entry&& aOther) - : mAllowedFontSets(mozilla::Move(aOther.mAllowedFontSets)), - mURI(mozilla::Move(aOther.mURI)), - mPrincipal(mozilla::Move(aOther.mPrincipal)), - mFontEntry(mozilla::Move(aOther.mFontEntry)), - mPrivate(mozilla::Move(aOther.mPrivate)) + : mURI(mozilla::Move(aOther.mURI)) + , mPrincipal(mozilla::Move(aOther.mPrincipal)) + , mFontEntry(mozilla::Move(aOther.mFontEntry)) + , mPrivate(mozilla::Move(aOther.mPrivate)) { } ~Entry() { } @@ -449,14 +421,6 @@ public: gfxFontEntry* GetFontEntry() const { return mFontEntry; } bool IsPrivate() const { return mPrivate; } - bool CheckIsFontSetAllowed(gfxUserFontSet* aUserFontSet) const; - bool CheckIsFontSetAllowedAndDispatchViolations(gfxUserFontSet* aUserFontSet) const; - bool IsFontSetAllowedKnown(gfxUserFontSet* aUserFontSet) const; - void SetIsFontSetAllowed(gfxUserFontSet* aUserFontSet, - bool aAllowed, - nsTArray>&& aViolations); - void ClearIsFontSetAllowed(gfxUserFontSet* aUserFontSet); - void ReportMemory(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize); @@ -477,51 +441,6 @@ public: aVariations.Length() * sizeof(gfxFontVariation)); } - // An entry in mAllowedFontSets. - class LoadResultEntry : public nsPtrHashKey - { - public: - explicit LoadResultEntry(KeyTypePointer aKey) - : nsPtrHashKey(aKey) - , mAllowed(false) - { - } - - LoadResultEntry(LoadResultEntry&& aOther) - : nsPtrHashKey(aOther.mKey) - , mAllowed(aOther.mAllowed) - , mViolations(mozilla::Move(aOther.mViolations)) - { - } - - ~LoadResultEntry() {} - - // Whether the user font set (the key) is allowed to load this - // entry's font. - bool mAllowed; - - // If the load is not allowed, the CSP violation reports that - // must be dispatched when we attempt to use the entry's font. - // (Should be empty if mAllowed is true.) - nsTArray> mViolations; - - enum { ALLOW_MEMMOVE = false }; - }; - - // 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. - nsTHashtable mAllowedFontSets; - RefPtr mURI; RefPtr mPrincipal; // or nullptr for data: URLs @@ -561,6 +480,10 @@ protected: // Return whether the font set is associated with a private-browsing tab. virtual bool GetPrivateBrowsing() = 0; + // Return whether the font set is associated with a document that was + // shift-reloaded, for example, and thus should bypass the font cache. + virtual bool BypassCache() = 0; + // parse data for a data URL virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, const gfxFontFaceSrc* aFontFaceSrc, @@ -704,6 +627,11 @@ public: gfxUserFontSet* GetUserFontSet() const { return mFontSet; } #endif + const nsTArray& SourceList() const + { + return mSrcList; + } + protected: const uint8_t* SanitizeOpenTypeData(const uint8_t* aData, uint32_t aLength, diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp index df4a80912d11..fe87894df57d 100644 --- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -105,6 +105,7 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument) : DOMEventTargetHelper(aWindow) , mDocument(aDocument) + , mStandardFontLoadPrincipal(new gfxFontSrcPrincipal(mDocument->NodePrincipal())) , mResolveLazilyCreatedReadyPromise(false) , mStatus(FontFaceSetLoadStatus::Loaded) , mNonRuleFacesDirty(false) @@ -113,10 +114,12 @@ FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument) , mDelayedLoadCheck(false) , mBypassCache(false) , mPrivateBrowsing(false) - , mHasStandardFontLoadPrincipalChanged(false) { MOZ_ASSERT(mDocument, "We should get a valid document from the caller!"); + mStandardFontLoadPrincipal = + new gfxFontSrcPrincipal(mDocument->NodePrincipal()); + // If the pref is not set, don't create the Promise (which the page wouldn't // be able to get to anyway) as it causes the window.FontFaceSet constructor // to be created. @@ -1324,77 +1327,71 @@ FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry, return NS_OK; } -gfxFontSrcPrincipal* -FontFaceSet::GetStandardFontLoadPrincipal() +void +FontFaceSet::CacheFontLoadability() { - if (!ServoStyleSet::IsInServoTraversal()) { - UpdateStandardFontLoadPrincipal(); + if (!mUserFontSet) { + return; } - return mStandardFontLoadPrincipal; -} + // TODO(emilio): We could do it a bit more incrementally maybe? + for (auto iter = mUserFontSet->mFontFamilies.Iter(); !iter.Done(); iter.Next()) { + for (const gfxFontEntry* entry : iter.Data()->GetFontList()) { + if (!entry->mIsUserFontContainer) { + continue; + } -nsresult -FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, - gfxFontSrcPrincipal** aPrincipal, - bool* aBypassCache) -{ - NS_ASSERTION(aFontFaceSrc && - aFontFaceSrc->mSourceType == gfxFontFaceSrc::eSourceType_URL, - "bad font face url passed to fontloader"); - - // check same-site origin - - NS_ASSERTION(aFontFaceSrc->mURI, "null font uri"); - if (!aFontFaceSrc->mURI) - return NS_ERROR_FAILURE; - - // use document principal, original principal if flag set - // this enables user stylesheets to load font files via - // @font-face rules - *aPrincipal = GetStandardFontLoadPrincipal(); - - NS_ASSERTION(aFontFaceSrc->mOriginPrincipal, - "null origin principal in @font-face rule"); - if (aFontFaceSrc->mUseOriginPrincipal) { - *aPrincipal = aFontFaceSrc->mOriginPrincipal; + const auto& sourceList = + static_cast(entry)->SourceList(); + for (const gfxFontFaceSrc& src : sourceList) { + if (src.mSourceType != gfxFontFaceSrc::eSourceType_URL) { + continue; + } + mAllowedFontLoads.LookupForAdd(&src).OrInsert([&] { + return IsFontLoadAllowed(src); + }); + } + } } - - *aBypassCache = mBypassCache; - - return NS_OK; } -// @arg aPrincipal: generally this is mDocument->NodePrincipal() but -// might also be the original principal which enables user stylesheets -// to load font files via @font-face rules. bool -FontFaceSet::IsFontLoadAllowed(nsIURI* aFontLocation, - nsIPrincipal* aPrincipal, - nsTArray>* aViolations) +FontFaceSet::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) { - if (aViolations) { - mDocument->StartBufferingCSPViolations(); + MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL); + + if (ServoStyleSet::IsInServoTraversal()) { + bool* entry = mAllowedFontLoads.GetValue(&aSrc); + MOZ_DIAGNOSTIC_ASSERT(entry, "Missed an update?"); + return entry ? *entry : false; } + MOZ_ASSERT(NS_IsMainThread()); + + if (!mUserFontSet) { + return false; + } + + gfxFontSrcPrincipal* gfxPrincipal = + aSrc.mURI->InheritsSecurityContext() + ? nullptr : aSrc.LoadPrincipal(*mUserFontSet); + + nsIPrincipal* principal = gfxPrincipal ? gfxPrincipal->get() : nullptr; + nsCOMPtr secCheckLoadInfo = new net::LoadInfo(mDocument->NodePrincipal(), // loading principal - aPrincipal, // triggering principal + principal, // triggering principal mDocument, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, nsIContentPolicy::TYPE_FONT); int16_t shouldLoad = nsIContentPolicy::ACCEPT; - nsresult rv = NS_CheckContentLoadPolicy(aFontLocation, + nsresult rv = NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo, EmptyCString(), // mime type &shouldLoad, nsContentUtils::GetContentPolicy()); - if (aViolations) { - mDocument->StopBufferingCSPViolations(*aViolations); - } - return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); } @@ -1829,53 +1826,23 @@ FontFaceSet::GetPresContext() } void -FontFaceSet::UpdateStandardFontLoadPrincipal() +FontFaceSet::RefreshStandardFontLoadPrincipal() { MOZ_ASSERT(NS_IsMainThread()); - - nsIPrincipal* documentPrincipal = mDocument->NodePrincipal(); - - if (!mStandardFontLoadPrincipal || - mStandardFontLoadPrincipal->get() != documentPrincipal) { - if (mStandardFontLoadPrincipal) { - mHasStandardFontLoadPrincipalChanged = true; - } - mStandardFontLoadPrincipal = new gfxFontSrcPrincipal(documentPrincipal); + mStandardFontLoadPrincipal = + new gfxFontSrcPrincipal(mDocument->NodePrincipal()); + mAllowedFontLoads.Clear(); + if (mUserFontSet) { + mUserFontSet->IncrementGeneration(false); } } // -- FontFaceSet::UserFontSet ------------------------------------------------ -/* virtual */ nsresult -FontFaceSet::UserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, - gfxFontSrcPrincipal** aPrincipal, - bool* aBypassCache) -{ - if (!mFontFaceSet) { - return NS_ERROR_FAILURE; - } - return mFontFaceSet->CheckFontLoad(aFontFaceSrc, aPrincipal, aBypassCache); -} - -/* virtual */ gfxFontSrcPrincipal* -FontFaceSet::UserFontSet::GetStandardFontLoadPrincipal() -{ - if (!mFontFaceSet) { - return nullptr; - } - return mFontFaceSet->GetStandardFontLoadPrincipal(); -} - /* virtual */ bool -FontFaceSet::UserFontSet::IsFontLoadAllowed( - nsIURI* aFontLocation, - nsIPrincipal* aPrincipal, - nsTArray>* aViolations) +FontFaceSet::UserFontSet::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) { - return mFontFaceSet && - mFontFaceSet->IsFontLoadAllowed(aFontLocation, - aPrincipal, - aViolations); + return mFontFaceSet && mFontFaceSet->IsFontLoadAllowed(aSrc); } /* virtual */ void diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h index 746580481e34..9fe8152cb0e0 100644 --- a/layout/style/FontFaceSet.h +++ b/layout/style/FontFaceSet.h @@ -63,16 +63,12 @@ public: FontFaceSet* GetFontFaceSet() { return mFontFaceSet; } - gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() override; + gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() const final + { + return mFontFaceSet ? mFontFaceSet->mStandardFontLoadPrincipal.get() : nullptr; + } - virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, - gfxFontSrcPrincipal** aPrincipal, - bool* aBypassCache) override; - - virtual bool IsFontLoadAllowed(nsIURI* aFontLocation, - nsIPrincipal* aPrincipal, - nsTArray>* aViolations) - override; + bool IsFontLoadAllowed(const gfxFontFaceSrc&) final; void DispatchFontLoadViolations( nsTArray>& aViolations) override; @@ -83,6 +79,11 @@ public: void RecordFontLoadDone(uint32_t aFontSize, mozilla::TimeStamp aDoneTime) override; + bool BypassCache() final + { + return mFontFaceSet && mFontFaceSet->mBypassCache; + } + protected: virtual bool GetPrivateBrowsing() override; virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, @@ -172,14 +173,7 @@ public: return set ? set->GetPresContext() : nullptr; } - void UpdateStandardFontLoadPrincipal(); - - bool HasStandardFontLoadPrincipalChanged() - { - bool changed = mHasStandardFontLoadPrincipalChanged; - mHasStandardFontLoadPrincipalChanged = false; - return changed; - } + void RefreshStandardFontLoadPrincipal(); nsIDocument* Document() const { return mDocument; } @@ -209,6 +203,9 @@ public: JS::Handle aThisArg, mozilla::ErrorResult& aRv); + // For ServoStyleSet to know ahead of time whether a font is loadable. + void CacheFontLoadability(); + private: ~FontFaceSet(); @@ -282,9 +279,8 @@ private: nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, gfxFontSrcPrincipal** aPrincipal, bool* aBypassCache); - bool IsFontLoadAllowed(nsIURI* aFontLocation, - nsIPrincipal* aPrincipal, - nsTArray>* aViolations); + bool IsFontLoadAllowed(const gfxFontFaceSrc& aSrc); + void DispatchFontLoadViolations(nsTArray>& aViolations); nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, const gfxFontFaceSrc* aFontFaceSrc, @@ -342,11 +338,10 @@ private: // // This field is used from GetStandardFontLoadPrincipal. When on a // style worker thread, we use mStandardFontLoadPrincipal assuming - // it is up to date. Because mDocument's principal can change over time, - // its value must be updated by a call to UpdateStandardFontLoadPrincipal - // before a restyle. (When called while on the main thread, - // GetStandardFontLoadPrincipal will call UpdateStandardFontLoadPrincipal - // to ensure its value is up to date.) + // it is up to date. + // + // Because mDocument's principal can change over time, + // its value must be updated by a call to ResetStandardFontLoadPrincipal. RefPtr mStandardFontLoadPrincipal; // A Promise that is fulfilled once all of the FontFace objects @@ -375,6 +370,14 @@ private: // The overall status of the loading or loaded fonts in the FontFaceSet. mozilla::dom::FontFaceSetLoadStatus mStatus; + // A map from gfxFontFaceSrc pointer identity to whether the load is allowed + // by CSP or other checks. We store this here because querying CSP off the + // main thread is not a great idea. + // + // We could use just the pointer and use this as a hash set, but then we'd + // have no way to verify that we've checked all the loads we should. + nsDataHashtable, bool> mAllowedFontLoads; + // Whether mNonRuleFaces has changed since last time UpdateRules ran. bool mNonRuleFacesDirty; @@ -397,10 +400,6 @@ private: // Whether the docshell for our document indicates that we are in private // browsing mode. bool mPrivateBrowsing; - - // Whether mStandardFontLoadPrincipal has changed since the last call to - // HasStandardFontLoadPrincipalChanged. - bool mHasStandardFontLoadPrincipalChanged; }; } // namespace dom diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index a4377aec943b..7f4dfbf8bc4f 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -104,7 +104,6 @@ ServoStyleSet::ServoStyleSet() , mAuthorStyleDisabled(false) , mStylistState(StylistState::NotDirty) , mUserFontSetUpdateGeneration(0) - , mUserFontCacheUpdateGeneration(0) , mNeedsRestyleAfterEnsureUniqueInner(false) { } @@ -467,26 +466,10 @@ ServoStyleSet::PreTraverseSync() // Ensure that the @font-face data is not stale uint64_t generation = userFontSet->GetGeneration(); if (generation != mUserFontSetUpdateGeneration) { + mDocument->GetFonts()->CacheFontLoadability(); presContext->DeviceContext()->UpdateFontCacheUserFonts(userFontSet); mUserFontSetUpdateGeneration = generation; } - - // Ensure that the FontFaceSet's cached document principal is up to date. - FontFaceSet* fontFaceSet = - static_cast(userFontSet)->GetFontFaceSet(); - fontFaceSet->UpdateStandardFontLoadPrincipal(); - bool principalChanged = fontFaceSet->HasStandardFontLoadPrincipalChanged(); - - // 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 (principalChanged) { - gfxUserFontSet::UserFontCache::ClearAllowedFontSets(userFontSet); - } - if (cacheGeneration != mUserFontCacheUpdateGeneration || principalChanged) { - gfxUserFontSet::UserFontCache::UpdateAllowedFontSets(userFontSet); - mUserFontCacheUpdateGeneration = cacheGeneration; - } } MOZ_ASSERT(!StylistNeedsUpdate()); diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h index d6c336bb1a64..72914c9a8c11 100644 --- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -577,7 +577,6 @@ private: bool mAuthorStyleDisabled; StylistState mStylistState; uint64_t mUserFontSetUpdateGeneration; - uint32_t mUserFontCacheUpdateGeneration; bool mNeedsRestyleAfterEnsureUniqueInner;