Bug 1384741 - Part 3: Buffer up CSP violation reports when pre-emptively checking cached font loads, and dispatch them when trying to use cached fonts. r?jfkthame
MozReview-Commit-ID: 7hUI160sNqv
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -1302,25 +1302,28 @@ gfxUserFontSet::UserFontCache::GetFont(g
}
// 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);
+ 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);
+ nsIPrincipal* principal = aPrincipal ? aPrincipal->get() : nullptr;
+ allowed = aUserFontEntry->mFontSet->IsFontLoadAllowed(
+ aSrcURI->get(),
+ principal,
+ /* aViolations */ nullptr);
MOZ_ASSERT(!entry->IsFontSetAllowedKnown(aUserFontEntry->mFontSet) ||
- entry->IsFontSetAllowed(aUserFontEntry->mFontSet) == allowed,
+ entry->CheckIsFontSetAllowed(aUserFontEntry->mFontSet) == allowed,
"why does IsFontLoadAllowed return a different value from "
"the cached value in mAllowedFontSets?");
}
if (!allowed) {
return nullptr;
}
@@ -1343,20 +1346,22 @@ gfxUserFontSet::UserFontCache::UpdateAll
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<nsCOMPtr<nsIRunnable>> violations;
bool allowed =
aUserFontSet->IsFontLoadAllowed(entry->GetURI()->get(),
- principal->get());
- entry->SetIsFontSetAllowed(aUserFontSet, allowed);
+ principal->get(),
+ &violations);
+ entry->SetIsFontSetAllowed(aUserFontSet, allowed, Move(violations));
}
}
}
/* static */ void
gfxUserFontSet::UserFontCache::ClearAllowedFontSets(
gfxUserFontSet* aUserFontSet)
{
@@ -1379,47 +1384,62 @@ gfxUserFontSet::UserFontCache::Shutdown(
delete sUserFonts;
sUserFonts = nullptr;
}
}
MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)
bool
-gfxUserFontSet::UserFontCache::Entry::IsFontSetAllowed(
+gfxUserFontSet::UserFontCache::Entry::CheckIsFontSetAllowed(
gfxUserFontSet* aUserFontSet) const
{
- bool allowed = false;
- DebugOnly<bool> found = mAllowedFontSets.Get(aUserFontSet, &allowed);
- MOZ_ASSERT(found, "UpdateAllowedFontSets should have been called and "
+ LoadResultEntry* entry = mAllowedFontSets.GetEntry(aUserFontSet);
+ MOZ_ASSERT(entry, "UpdateAllowedFontSets should have been called and "
"added an entry to mAllowedFontSets");
- return allowed;
+ 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)
+ bool aAllowed,
+ nsTArray<nsCOMPtr<nsIRunnable>>&& aViolations)
{
MOZ_ASSERT(!IsFontSetAllowedKnown(aUserFontSet));
- mAllowedFontSets.Put(aUserFontSet, aAllowed);
+ LoadResultEntry* entry = mAllowedFontSets.PutEntry(aUserFontSet);
+ entry->mAllowed = aAllowed;
+ entry->mViolations.SwapElements(aViolations);
}
void
gfxUserFontSet::UserFontCache::Entry::ClearIsFontSetAllowed(
gfxUserFontSet* aUserFontSet)
{
- mAllowedFontSets.Remove(aUserFontSet);
+ mAllowedFontSets.RemoveEntry(aUserFontSet);
}
void
gfxUserFontSet::UserFontCache::Entry::ReportMemory(
nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
{
MOZ_ASSERT(mFontEntry);
nsAutoCString path("explicit/gfx/user-fonts/font(");
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -260,17 +260,23 @@ public:
virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
gfxFontSrcPrincipal** aPrincipal,
bool* aBypassCache) = 0;
virtual gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() = 0;
// check whether content policies allow the given URI to load.
virtual bool IsFontLoadAllowed(nsIURI* aFontLocation,
- nsIPrincipal* aPrincipal) = 0;
+ nsIPrincipal* aPrincipal,
+ nsTArray<nsCOMPtr<nsIRunnable>>* aViolations) = 0;
+
+ // Dispatches all of the specified runnables to the font face set's
+ // document's event queue.
+ virtual void DispatchFontLoadViolations(
+ nsTArray<nsCOMPtr<nsIRunnable>>& aViolations) = 0;
// initialize the process that loads external font data, which upon
// completion will call FontDataDownloadComplete method
virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
const gfxFontFaceSrc* aFontFaceSrc) = 0;
// generation - each time a face is loaded, generation is
// incremented so that the change can be recognized
@@ -425,48 +431,82 @@ public:
enum { ALLOW_MEMMOVE = false };
gfxFontSrcURI* GetURI() const { return mURI; }
gfxFontSrcPrincipal* GetPrincipal() const { return mPrincipal; }
gfxFontEntry* GetFontEntry() const { return mFontEntry; }
bool IsPrivate() const { return mPrivate; }
- bool IsFontSetAllowed(gfxUserFontSet* aUserFontSet) const;
+ bool CheckIsFontSetAllowed(gfxUserFontSet* aUserFontSet) const;
+ bool CheckIsFontSetAllowedAndDispatchViolations(gfxUserFontSet* aUserFontSet) const;
bool IsFontSetAllowedKnown(gfxUserFontSet* aUserFontSet) const;
- void SetIsFontSetAllowed(gfxUserFontSet* aUserFontSet, bool aAllowed);
+ void SetIsFontSetAllowed(gfxUserFontSet* aUserFontSet,
+ bool aAllowed,
+ nsTArray<nsCOMPtr<nsIRunnable>>&& aViolations);
void ClearIsFontSetAllowed(gfxUserFontSet* aUserFontSet);
void ReportMemory(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize);
#ifdef DEBUG_USERFONT_CACHE
void Dump();
#endif
private:
static uint32_t
HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) {
return mozilla::HashBytes(aFeatures.Elements(),
aFeatures.Length() * sizeof(gfxFontFeature));
}
+ // An entry in mAllowedFontSets.
+ class LoadResultEntry : public nsPtrHashKey<gfxUserFontSet>
+ {
+ public:
+ explicit LoadResultEntry(KeyTypePointer aKey)
+ : nsPtrHashKey(aKey)
+ , mAllowed(false)
+ {
+ }
+
+ explicit 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<nsCOMPtr<nsIRunnable>> 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.
- nsDataHashtable<nsPtrHashKey<gfxUserFontSet>, bool> mAllowedFontSets;
+ nsTHashtable<LoadResultEntry> mAllowedFontSets;
RefPtr<gfxFontSrcURI> mURI;
RefPtr<gfxFontSrcPrincipal> mPrincipal; // or nullptr for data: URLs
// The "real" font entry corresponding to this downloaded font.
// The font entry MUST notify the cache when it is destroyed
// (by calling ForgetFont()).
gfxFontEntry* MOZ_NON_OWNING_REF mFontEntry;
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -1373,31 +1373,58 @@ FontFaceSet::CheckFontLoad(const gfxFont
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)
+FontFaceSet::IsFontLoadAllowed(nsIURI* aFontLocation,
+ nsIPrincipal* aPrincipal,
+ nsTArray<nsCOMPtr<nsIRunnable>>* aViolations)
{
+ if (aViolations) {
+ mDocument->StartBufferingCSPViolations();
+ }
+
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_FONT,
aFontLocation,
aPrincipal,
mDocument,
EmptyCString(), // mime type
nullptr, // aExtra
&shouldLoad,
nsContentUtils::GetContentPolicy());
+ if (aViolations) {
+ mDocument->StopBufferingCSPViolations(*aViolations);
+ }
+
return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
}
+void
+FontFaceSet::DispatchFontLoadViolations(
+ nsTArray<nsCOMPtr<nsIRunnable>>& aViolations)
+{
+ if (XRE_IsContentProcess()) {
+ nsCOMPtr<nsIEventTarget> eventTarget =
+ mDocument->EventTargetFor(TaskCategory::Other);
+ for (nsIRunnable* runnable : aViolations) {
+ eventTarget->Dispatch(do_AddRef(runnable), NS_DISPATCH_NORMAL);
+ }
+ } else {
+ for (nsIRunnable* runnable : aViolations) {
+ NS_DispatchToMainThread(do_AddRef(runnable));
+ }
+ }
+}
+
nsresult
FontFaceSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
const gfxFontFaceSrc* aFontFaceSrc,
uint8_t*& aBuffer,
uint32_t& aBufferLength)
{
nsresult rv;
@@ -1835,21 +1862,34 @@ FontFaceSet::UserFontSet::GetStandardFon
{
if (!mFontFaceSet) {
return nullptr;
}
return mFontFaceSet->GetStandardFontLoadPrincipal();
}
/* virtual */ bool
-FontFaceSet::UserFontSet::IsFontLoadAllowed(nsIURI* aFontLocation,
- nsIPrincipal* aPrincipal)
+FontFaceSet::UserFontSet::IsFontLoadAllowed(
+ nsIURI* aFontLocation,
+ nsIPrincipal* aPrincipal,
+ nsTArray<nsCOMPtr<nsIRunnable>>* aViolations)
{
return mFontFaceSet &&
- mFontFaceSet->IsFontLoadAllowed(aFontLocation, aPrincipal);
+ mFontFaceSet->IsFontLoadAllowed(aFontLocation,
+ aPrincipal,
+ aViolations);
+}
+
+/* virtual */ void
+FontFaceSet::UserFontSet::DispatchFontLoadViolations(
+ nsTArray<nsCOMPtr<nsIRunnable>>& aViolations)
+{
+ if (mFontFaceSet) {
+ mFontFaceSet->DispatchFontLoadViolations(aViolations);
+ }
}
/* virtual */ nsresult
FontFaceSet::UserFontSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
const gfxFontFaceSrc* aFontFaceSrc)
{
if (!mFontFaceSet) {
return NS_ERROR_FAILURE;
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -66,17 +66,22 @@ public:
gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() override;
virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
gfxFontSrcPrincipal** aPrincipal,
bool* aBypassCache) override;
virtual bool IsFontLoadAllowed(nsIURI* aFontLocation,
- nsIPrincipal* aPrincipal) override;
+ nsIPrincipal* aPrincipal,
+ nsTArray<nsCOMPtr<nsIRunnable>>* aViolations)
+ override;
+
+ void DispatchFontLoadViolations(
+ nsTArray<nsCOMPtr<nsIRunnable>>& aViolations) override;
virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
const gfxFontFaceSrc* aFontFaceSrc) override;
void RecordFontLoadDone(uint32_t aFontSize,
mozilla::TimeStamp aDoneTime) override;
protected:
@@ -272,17 +277,20 @@ private:
nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
const gfxFontFaceSrc* aFontFaceSrc);
gfxFontSrcPrincipal* GetStandardFontLoadPrincipal();
nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
gfxFontSrcPrincipal** aPrincipal,
bool* aBypassCache);
- bool IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal);
+ bool IsFontLoadAllowed(nsIURI* aFontLocation,
+ nsIPrincipal* aPrincipal,
+ nsTArray<nsCOMPtr<nsIRunnable>>* aViolations);
+ void DispatchFontLoadViolations(nsTArray<nsCOMPtr<nsIRunnable>>& aViolations);
nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
const gfxFontFaceSrc* aFontFaceSrc,
uint8_t*& aBuffer,
uint32_t& aBufferLength);
nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
const char* aMessage,
uint32_t aFlags,
nsresult aStatus);