Bug 1588614 - Cache the document's effective storage principal to avoid running excessive anti-tracking checks; r=baku
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 21 Oct 2019 12:53:38 +0000
changeset 498352 55743f55095ccd3851db6abb49ca36e8f1eebceb
parent 498351 71e1f7fa5cf2b9a522d6c7b356420961e9285368
child 498353 a25d757eaf2923f4774ad561600cd747519b3a9a
push id36717
push usernbeleuzu@mozilla.com
push dateMon, 21 Oct 2019 21:51:55 +0000
treeherdermozilla-central@563f437f24b9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1588614
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1588614 - Cache the document's effective storage principal to avoid running excessive anti-tracking checks; r=baku Differential Revision: https://phabricator.services.mozilla.com/D49791
dom/base/Document.cpp
dom/base/Document.h
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -16046,34 +16046,39 @@ nsICookieSettings* Document::CookieSetti
 }
 
 nsIPrincipal* Document::EffectiveStoragePrincipal() const {
   nsPIDOMWindowInner* inner = GetInnerWindow();
   if (!inner) {
     return NodePrincipal();
   }
 
+  // Return our cached storage principal if one exists.
+  if (mActiveStoragePrincipal) {
+    return mActiveStoragePrincipal;
+  }
+
   // We use the lower-level AntiTrackingCommon API here to ensure this
   // check doesn't send notifications.
   uint32_t rejectedReason = 0;
   if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
           inner, GetDocumentURI(), &rejectedReason)) {
-    return NodePrincipal();
+    return mActiveStoragePrincipal = NodePrincipal();
   }
 
   // Let's use the storage principal only if we need to partition the cookie
   // jar. When the permission is granted, access will be different and the
   // normal principal will be used.
   if (ShouldPartitionStorage(rejectedReason) &&
       !StoragePartitioningEnabled(
           rejectedReason, const_cast<Document*>(this)->CookieSettings())) {
-    return NodePrincipal();
-  }
-
-  return mIntrinsicStoragePrincipal;
+    return mActiveStoragePrincipal = NodePrincipal();
+  }
+
+  return mActiveStoragePrincipal = mIntrinsicStoragePrincipal;
 }
 
 void Document::SetIsInitialDocument(bool aIsInitialDocument) {
   mIsInitialDocumentInWindow = aIsInitialDocument;
 
   // Asynchronously tell the parent process that we are, or are no longer, the
   // initial document. This happens async.
   if (RefPtr<nsPIDOMWindowInner> inner = GetInnerWindow()) {
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -568,16 +568,18 @@ class Document : public nsINode,
   // You should probably not be using this function, since it performs no
   // checks to ensure that the intrinsic storage principal should really be
   // used here.  It is only designed to be used in very specific circumstances,
   // such as when inheriting the document/storage principal.
   nsIPrincipal* IntrinsicStoragePrincipal() const {
     return mIntrinsicStoragePrincipal;
   }
 
+  void ClearActiveStoragePrincipal() { mActiveStoragePrincipal = nullptr; }
+
   nsIPrincipal* GetContentBlockingAllowListPrincipal() const {
     return mContentBlockingAllowListPrincipal;
   }
 
   already_AddRefed<nsIPrincipal> RecomputeContentBlockingAllowListPrincipal(
       nsIURI* aURIBeingLoaded, const OriginAttributes& aAttrs);
 
   // EventTarget
@@ -5338,16 +5340,21 @@ class Document : public nsINode,
   int32_t mCachedTabSizeGeneration;
   nsTabSizes mCachedTabSizes;
 
   bool mInRDMPane;
 
   // The principal to use for the storage area of this document.
   nsCOMPtr<nsIPrincipal> mIntrinsicStoragePrincipal;
 
+  // The cached storage principal for this document.
+  // This is mutable so that we can keep EffectiveStoragePrincipal() const
+  // which is required due to its CloneDocHelper() call site.  :-(
+  mutable nsCOMPtr<nsIPrincipal> mActiveStoragePrincipal;
+
   // The principal to use for the content blocking allow list.
   nsCOMPtr<nsIPrincipal> mContentBlockingAllowListPrincipal;
 
   // See GetNextFormNumber and GetNextControlNumber.
   int32_t mNextFormNumber;
   int32_t mNextControlNumber;
 
  public:
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -7142,16 +7142,21 @@ void nsGlobalWindowInner::StorageAccessG
     }
   }
 
   // Reset the IndexedDB factory.
   mIndexedDB = nullptr;
 
   // Reset DOM Cache
   mCacheStorage = nullptr;
+
+  // Reset the active storage principal
+  if (mDoc) {
+    mDoc->ClearActiveStoragePrincipal();
+  }
 }
 
 mozilla::dom::TabGroup* nsPIDOMWindowInner::TabGroup() {
   return nsGlobalWindowInner::Cast(this)->TabGroupInner();
 }
 
 /* static */
 already_AddRefed<nsGlobalWindowInner> nsGlobalWindowInner::Create(
@@ -7208,16 +7213,27 @@ const nsIGlobalObject* nsPIDOMWindowInne
   return nsGlobalWindowInner::Cast(this);
 }
 
 void nsPIDOMWindowInner::SaveStorageAccessGranted(
     const nsACString& aPermissionKey) {
   if (!HasStorageAccessGranted(aPermissionKey)) {
     mStorageAccessGranted.AppendElement(aPermissionKey);
   }
+
+  nsGlobalWindowInner::Cast(this)->ClearActiveStoragePrincipal();
+}
+
+void nsGlobalWindowInner::ClearActiveStoragePrincipal() {
+  Document* doc = GetExtantDoc();
+  if (doc) {
+    doc->ClearActiveStoragePrincipal();
+  }
+
+  CallOnChildren(&nsGlobalWindowInner::ClearActiveStoragePrincipal);
 }
 
 bool nsPIDOMWindowInner::HasStorageAccessGranted(
     const nsACString& aPermissionKey) {
   return mStorageAccessGranted.Contains(aPermissionKey);
 }
 
 nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow,
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -304,16 +304,18 @@ class nsGlobalWindowInner final : public
   bool DispatchEvent(mozilla::dom::Event& aEvent,
                      mozilla::dom::CallerType aCallerType,
                      mozilla::ErrorResult& aRv) override;
 
   void GetEventTargetParent(mozilla::EventChainPreVisitor& aVisitor) override;
 
   nsresult PostHandleEvent(mozilla::EventChainPostVisitor& aVisitor) override;
 
+  void ClearActiveStoragePrincipal();
+
   void Suspend();
   void Resume();
   virtual bool IsSuspended() const override;
 
   // Calling Freeze() on a window will automatically Suspend() it.  In
   // addition, the window and its children are further treated as no longer
   // suitable for interaction with the user.  For example, it may be marked
   // non-visible, cannot be focused, etc.  All worker threads are also frozen