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 draft
authorCameron McCormack <cam@mcc.id.au>
Mon, 07 Aug 2017 10:12:12 +0800
changeset 641462 4d004bba223194290524c740621ffbb51952a386
parent 641461 6621f3417ef42da4020feb5c31310dbb7fe1716b
child 641463 6d3c87ecc5fcd781c0b21a0404ecf52ae55abfd8
push id72530
push userbmo:cam@mcc.id.au
push dateMon, 07 Aug 2017 02:52:03 +0000
reviewersjfkthame
bugs1384741
milestone57.0a1
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
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/gfxUserFontSet.h
layout/style/FontFaceSet.cpp
layout/style/FontFaceSet.h
--- 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);