Bug 1547813 - Part 7: Introduce a storage partitioning API; r=baku
☠☠ backed out by 8fea66166287 ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 08 May 2019 20:15:13 +0000
changeset 473245 807ce59e7e6eecbbb83b02d1ff45ecbda9896835
parent 473244 06943593738cbfb00527317c54f8b58d324060ab
child 473246 8cad4fd197b1bdbee9e36bc54a12c48dd8c8e269
push id113072
push usernbeleuzu@mozilla.com
push dateFri, 10 May 2019 02:59:17 +0000
treeherdermozilla-inbound@f71645b9b3e0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1547813
milestone68.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 1547813 - Part 7: Introduce a storage partitioning API; r=baku This API abstracts away the details of the decision on what context should be partitioned away from the consumers and centralizes the decision making into the same location in the code base. Differential Revision: https://phabricator.services.mozilla.com/D29742
dom/base/Document.cpp
dom/base/Document.h
dom/base/nsGlobalWindowInner.cpp
dom/broadcastchannel/BroadcastChannel.cpp
dom/indexedDB/IDBFactory.cpp
dom/workers/WorkerScope.cpp
dom/workers/sharedworkers/SharedWorker.cpp
netwerk/cookie/CookieServiceChild.cpp
netwerk/cookie/CookieServiceChild.h
netwerk/cookie/CookieServiceParent.cpp
netwerk/cookie/CookieServiceParent.h
netwerk/cookie/PCookieService.ipdl
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
toolkit/components/antitracking/StorageAccess.cpp
toolkit/components/antitracking/StorageAccess.h
toolkit/components/antitracking/StoragePrincipalHelper.cpp
toolkit/components/antitracking/moz.build
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/IdentifierMapEntry.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Likely.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/StaticPrefs.h"
+#include "mozilla/StorageAccess.h"
 #include "mozilla/URLExtraData.h"
 #include <algorithm>
 
 #include "mozilla/Logging.h"
 #include "plstr.h"
 #include "mozilla/Sprintf.h"
 
 #include "mozilla/Telemetry.h"
@@ -3474,19 +3475,18 @@ void Document::GetCookie(nsAString& aCoo
   }
 
   nsContentUtils::StorageAccess storageAccess =
       nsContentUtils::StorageAllowedForDocument(this);
   if (storageAccess == nsContentUtils::StorageAccess::eDeny) {
     return;
   }
 
-  if (storageAccess ==
-          nsContentUtils::StorageAccess::ePartitionTrackersOrDeny &&
-      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+  if (ShouldPartitionStorage(storageAccess) &&
+      !StoragePartitioningEnabled(storageAccess, CookieSettings())) {
     return;
   }
 
   // If the document is a cookie-averse Document... return the empty string.
   if (IsCookieAverse()) {
     return;
   }
 
@@ -3535,19 +3535,18 @@ void Document::SetCookie(const nsAString
   }
 
   nsContentUtils::StorageAccess storageAccess =
       nsContentUtils::StorageAllowedForDocument(this);
   if (storageAccess == nsContentUtils::StorageAccess::eDeny) {
     return;
   }
 
-  if (storageAccess ==
-          nsContentUtils::StorageAccess::ePartitionTrackersOrDeny &&
-      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+  if (ShouldPartitionStorage(storageAccess) &&
+      !StoragePartitioningEnabled(storageAccess, CookieSettings())) {
     return;
   }
 
   // If the document is a cookie-averse Document... do nothing.
   if (IsCookieAverse()) {
     return;
   }
 
@@ -11578,16 +11577,50 @@ Document* Document::GetTopLevelContentDo
     }
 
     parent = parent->GetParentDocument();
   } while (parent);
 
   return parent;
 }
 
+const Document* Document::GetTopLevelContentDocument() const {
+  const Document* parent;
+
+  if (!mLoadedAsData) {
+    parent = this;
+  } else {
+    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
+    if (!window) {
+      return nullptr;
+    }
+
+    parent = window->GetExtantDoc();
+    if (!parent) {
+      return nullptr;
+    }
+  }
+
+  do {
+    if (parent->IsTopLevelContentDocument()) {
+      break;
+    }
+
+    // If we ever have a non-content parent before we hit a toplevel content
+    // parent, then we're never going to find one.  Just bail.
+    if (!parent->IsContentDocument()) {
+      return nullptr;
+    }
+
+    parent = parent->GetParentDocument();
+  } while (parent);
+
+  return parent;
+}
+
 static bool MightBeChromeScheme(nsIURI* aURI) {
   MOZ_ASSERT(aURI);
   bool isChrome = true;
   aURI->SchemeIs("chrome", &isChrome);
   return isChrome;
 }
 
 static bool MightBeAboutOrChromeScheme(nsIURI* aURI) {
@@ -13019,27 +13052,36 @@ nsICookieSettings* Document::CookieSetti
   if (!mCookieSettings) {
     mCookieSettings = net::CookieSettings::Create();
   }
 
   return mCookieSettings;
 }
 
 nsIPrincipal* Document::EffectiveStoragePrincipal() const {
-  if (!StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+  const Document* toplevel = GetTopLevelContentDocument();
+  nsPIDOMWindowInner* inner = GetInnerWindow();
+  if (!toplevel || !inner) {
     return NodePrincipal();
   }
 
-  nsContentUtils::StorageAccess access =
-      nsContentUtils::StorageAllowedForDocument(this);
+  // We use the lower-level AntiTrackingCommon API here to ensure this
+  // check doesn't send notifications.
+  uint32_t rejectedReason = 0;
+  if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
+          inner, toplevel->GetDocumentURI(), &rejectedReason)) {
+    return 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 (access != nsContentUtils::StorageAccess::ePartitionTrackersOrDeny) {
+  if (!ShouldPartitionStorage(rejectedReason) ||
+      !StoragePartitioningEnabled(
+          rejectedReason, const_cast<Document*>(this)->CookieSettings())) {
     return NodePrincipal();
   }
 
   return mIntrinsicStoragePrincipal;
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3172,16 +3172,17 @@ class Document : public nsINode,
     eConnected,
     eDisconnected,
     eAdopted,
     eAttributeChanged,
     eGetCustomInterface
   };
 
   Document* GetTopLevelContentDocument();
+  const Document* GetTopLevelContentDocument() const;
 
   // Returns the associated XUL window if this is a top-level chrome document,
   // null otherwise.
   already_AddRefed<nsIXULWindow> GetXULWindowIfToplevelChrome() const;
 
   already_AddRefed<Element> CreateElement(
       const nsAString& aTagName, const ElementCreationOptionsOrString& aOptions,
       ErrorResult& rv);
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -77,16 +77,17 @@
 #include "js/Wrapper.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsReadableUtils.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/StorageAccess.h"
 #include "mozilla/Unused.h"
 
 // Other Classes
 #include "mozilla/dom/BarProps.h"
 #include "nsContentCID.h"
 #include "nsLayoutStatics.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozilla/dom/WorkerCommon.h"
@@ -4398,17 +4399,17 @@ Storage* nsGlobalWindowInner::GetLocalSt
   // privacy.restrict3rdpartystorage.partitionedHosts pref. See
   // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
   // This is a temporary web-compatibility hack.
 
   nsContentUtils::StorageAccess access =
       nsContentUtils::StorageAllowedForWindow(this);
 
   // We allow partitioned localStorage only to some hosts.
-  if (access == nsContentUtils::StorageAccess::ePartitionTrackersOrDeny) {
+  if (ShouldPartitionStorage(access)) {
     if (!mDoc) {
       access = nsContentUtils::StorageAccess::eDeny;
     } else {
       nsCOMPtr<nsIURI> uri;
       Unused << mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri));
       static const char* kPrefName =
           "privacy.restrict3rdpartystorage.partitionedHosts";
       if (!uri || !nsContentUtils::IsURIInPrefList(uri, kPrefName)) {
@@ -4427,17 +4428,17 @@ Storage* nsGlobalWindowInner::GetLocalSt
     }
     return nullptr;
   }
 
   // Note that this behavior is observable: if we grant storage permission to a
   // tracker, we pass from the partitioned LocalStorage to the 'normal'
   // LocalStorage. The previous data is lost and the 2 window.localStorage
   // objects, before and after the permission granted, will be different.
-  if (access != nsContentUtils::StorageAccess::ePartitionTrackersOrDeny &&
+  if (!ShouldPartitionStorage(access) &&
       (!mLocalStorage ||
        mLocalStorage->Type() == Storage::ePartitionedLocalStorage)) {
     RefPtr<Storage> storage;
 
     if (NextGenLocalStorageEnabled()) {
       aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
     } else {
       nsresult rv;
@@ -4470,30 +4471,28 @@ Storage* nsGlobalWindowInner::GetLocalSt
     if (aError.Failed()) {
       return nullptr;
     }
 
     mLocalStorage = storage;
     MOZ_ASSERT(mLocalStorage);
   }
 
-  if (access == nsContentUtils::StorageAccess::ePartitionTrackersOrDeny &&
-      !mLocalStorage) {
+  if (ShouldPartitionStorage(access) && !mLocalStorage) {
     nsIPrincipal* principal = GetPrincipal();
     if (!principal) {
       aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
       return nullptr;
     }
 
     mLocalStorage = new PartitionedLocalStorage(this, principal);
   }
 
-  MOZ_ASSERT(
-      (access == nsContentUtils::StorageAccess::ePartitionTrackersOrDeny) ==
-      (mLocalStorage->Type() == Storage::ePartitionedLocalStorage));
+  MOZ_ASSERT(ShouldPartitionStorage(access) ==
+             (mLocalStorage->Type() == Storage::ePartitionedLocalStorage));
 
   return mLocalStorage;
 }
 
 IDBFactory* nsGlobalWindowInner::GetIndexedDB(ErrorResult& aError) {
   if (!mIndexedDB) {
     // This may keep mIndexedDB null without setting an error.
     aError = IDBFactory::CreateForWindow(this, getter_AddRefs(mIndexedDB));
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/StorageAccess.h"
 #include "nsContentUtils.h"
 
 #include "nsIBFCacheEntry.h"
 #include "nsICookieService.h"
 #include "mozilla/dom/Document.h"
 #include "nsISupportsPrimitives.h"
 
 #ifdef XP_WIN
@@ -228,16 +229,17 @@ already_AddRefed<BroadcastChannel> Broad
 
   RefPtr<BroadcastChannel> bc = new BroadcastChannel(global, aChannel);
 
   nsAutoCString origin;
   PrincipalInfo storagePrincipalInfo;
 
   nsContentUtils::StorageAccess storageAccess;
 
+  nsCOMPtr<nsICookieSettings> cs;
   if (NS_IsMainThread()) {
     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
     if (NS_WARN_IF(!window)) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
     nsCOMPtr<nsIGlobalObject> incumbent = mozilla::dom::GetIncumbentGlobal();
@@ -265,16 +267,21 @@ already_AddRefed<BroadcastChannel> Broad
     }
 
     aRv = PrincipalToPrincipalInfo(storagePrincipal, &storagePrincipalInfo);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
     storageAccess = nsContentUtils::StorageAllowedForWindow(window);
+
+    Document* doc = window->GetExtantDoc();
+    if (doc) {
+      cs = doc->CookieSettings();
+    }
   } else {
     JSContext* cx = aGlobal.Context();
 
     WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
     MOZ_ASSERT(workerPrivate);
 
     RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
         workerPrivate, "BroadcastChannel", [bc]() { bc->Shutdown(); });
@@ -291,24 +298,25 @@ already_AddRefed<BroadcastChannel> Broad
         new InitializeRunnable(tsr, origin, storagePrincipalInfo, aRv);
     runnable->Dispatch(Canceling, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
 
     storageAccess = workerPrivate->StorageAccess();
     bc->mWorkerRef = workerRef;
+
+    cs = workerPrivate->CookieSettings();
   }
 
   // We want to allow opaque origins.
   if (storagePrincipalInfo.type() != PrincipalInfo::TNullPrincipalInfo &&
       (storageAccess == nsContentUtils::StorageAccess::eDeny ||
-       (storageAccess ==
-            nsContentUtils::StorageAccess::ePartitionTrackersOrDeny &&
-        !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()))) {
+       (ShouldPartitionStorage(storageAccess) &&
+        !StoragePartitioningEnabled(storageAccess, cs)))) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // Register this component to PBackground.
   PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
   if (NS_WARN_IF(!actorChild)) {
     // Firefox is probably shutting down. Let's return a 'generic' error.
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackground.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/StaticPrefs.h"
+#include "mozilla/StorageAccess.h"
 #include "mozilla/Telemetry.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIAboutModule.h"
 #include "nsILoadContext.h"
 #include "nsIPrincipal.h"
@@ -300,18 +301,19 @@ nsresult IDBFactory::AllowedForWindowInt
 
   // the factory callsite records whether the browser is in private browsing.
   // and thus we don't have to respect that setting here. IndexedDB has no
   // concept of session-local storage, and thus ignores it.
   if (access == nsContentUtils::StorageAccess::eDeny) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  if (access == nsContentUtils::StorageAccess::ePartitionTrackersOrDeny &&
-      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+  if (ShouldPartitionStorage(access) &&
+      !StoragePartitioningEnabled(access,
+                                  aWindow->GetExtantDoc()->CookieSettings())) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
   MOZ_ASSERT(sop);
 
   nsCOMPtr<nsIPrincipal> principal = sop->GetEffectiveStoragePrincipal();
   if (NS_WARN_IF(!principal)) {
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -27,16 +27,17 @@
 #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/SimpleGlobalObject.h"
 #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
 #include "mozilla/dom/WorkerGlobalScopeBinding.h"
 #include "mozilla/dom/WorkerLocation.h"
 #include "mozilla/dom/WorkerNavigator.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/StaticPrefs.h"
+#include "mozilla/StorageAccess.h"
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/dom/Document.h"
 #include "nsIServiceWorkerManager.h"
 #include "nsIScriptError.h"
 #include "nsIScriptTimeoutHandler.h"
 
 #ifdef ANDROID
@@ -401,18 +402,18 @@ already_AddRefed<IDBFactory> WorkerGloba
     nsContentUtils::StorageAccess access = mWorkerPrivate->StorageAccess();
 
     if (access == nsContentUtils::StorageAccess::eDeny) {
       NS_WARNING("IndexedDB is not allowed in this worker!");
       aErrorResult = NS_ERROR_DOM_SECURITY_ERR;
       return nullptr;
     }
 
-    if (access == nsContentUtils::StorageAccess::ePartitionTrackersOrDeny &&
-        !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+    if (ShouldPartitionStorage(access) &&
+        !StoragePartitioningEnabled(access, mWorkerPrivate->CookieSettings())) {
       NS_WARNING("IndexedDB is not allowed in this worker!");
       aErrorResult = NS_ERROR_DOM_SECURITY_ERR;
       return nullptr;
     }
 
     const PrincipalInfo& principalInfo =
         mWorkerPrivate->GetEffectiveStoragePrincipalInfo();
 
--- a/dom/workers/sharedworkers/SharedWorker.cpp
+++ b/dom/workers/sharedworkers/SharedWorker.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/dom/SharedWorkerChild.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerLoadInfo.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/URIUtils.h"
+#include "mozilla/StorageAccess.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindowInner.h"
 #include "nsPIDOMWindow.h"
 
 #ifdef XP_WIN
 #  undef PostMessage
 #endif
 
@@ -103,19 +104,19 @@ already_AddRefed<SharedWorker> SharedWor
   MOZ_ASSERT(window);
 
   auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
   if (storageAllowed == nsContentUtils::StorageAccess::eDeny) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
-  if (storageAllowed ==
-          nsContentUtils::StorageAccess::ePartitionTrackersOrDeny &&
-      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+  if (ShouldPartitionStorage(storageAllowed) &&
+      !StoragePartitioningEnabled(storageAllowed,
+                                  window->GetExtantDoc()->CookieSettings())) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // Assert that the principal private browsing state matches the
   // StorageAccess value.
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   if (storageAllowed == nsContentUtils::StorageAccess::ePrivateBrowsing) {
@@ -175,18 +176,17 @@ already_AddRefed<SharedWorker> SharedWor
                                            loadingPrincipalPreloadCSP);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   // Here, the StoragePrincipal is always equal to the SharedWorker's principal
   // because the channel is not opened yet, and, because of this, it's not
   // classified. We need to force the correct originAttributes.
-  if (storageAllowed ==
-      nsContentUtils::StorageAccess::ePartitionTrackersOrDeny) {
+  if (ShouldPartitionStorage(storageAllowed)) {
     nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window);
     if (!sop) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
     nsIPrincipal* windowPrincipal = sop->GetPrincipal();
     if (!windowPrincipal) {
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -143,29 +143,29 @@ void CookieServiceChild::ActorDestroy(Ac
 void CookieServiceChild::TrackCookieLoad(nsIChannel* aChannel) {
   if (!mIPCOpen) {
     return;
   }
 
   bool isForeign = false;
   bool isTrackingResource = false;
   bool firstPartyStorageAccessGranted = false;
+  uint32_t rejectedReason = 0;
   nsCOMPtr<nsIURI> uri;
   aChannel->GetURI(getter_AddRefs(uri));
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   if (RequireThirdPartyCheck(loadInfo)) {
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
   }
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->IsTrackingResource();
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
-    uint32_t rejectedReason = 0;
     if (isForeign && AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
                          httpChannel, uri, &rejectedReason)) {
       firstPartyStorageAccessGranted = true;
     }
 
     // We need to notify about the outcome of the content blocking check here
     // since the parent process can't do it for us as it won't have a channel
     // object handy.
@@ -177,18 +177,18 @@ void CookieServiceChild::TrackCookieLoad
   }
   mozilla::OriginAttributes attrs = loadInfo->GetOriginAttributes();
   StoragePrincipalHelper::PrepareOriginAttributes(aChannel, attrs);
   URIParams uriParams;
   SerializeURI(uri, uriParams);
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
   bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, uri);
   SendPrepareCookieList(uriParams, isForeign, isTrackingResource,
-                        firstPartyStorageAccessGranted, isSafeTopLevelNav,
-                        isSameSiteForeign, attrs);
+                        firstPartyStorageAccessGranted, rejectedReason,
+                        isSafeTopLevelNav, isSameSiteForeign, attrs);
 }
 
 mozilla::ipc::IPCResult CookieServiceChild::RecvRemoveAll() {
   mCookiesMap.Clear();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult CookieServiceChild::RecvRemoveCookie(
@@ -278,18 +278,19 @@ void CookieServiceChild::PrefChanged(nsI
     if (mCookieTimer) {
       mCookieTimer->SetDelay(gMoveCookiesIntervalSeconds * 1000);
     }
   }
 }
 
 void CookieServiceChild::GetCookieStringFromCookieHashTable(
     nsIURI* aHostURI, bool aIsForeign, bool aIsTrackingResource,
-    bool aFirstPartyStorageAccessGranted, bool aIsSafeTopLevelNav,
-    bool aIsSameSiteForeign, nsIChannel* aChannel, nsCString& aCookieString) {
+    bool aFirstPartyStorageAccessGranted, uint32_t aRejectedReason,
+    bool aIsSafeTopLevelNav, bool aIsSameSiteForeign, nsIChannel* aChannel,
+    nsCString& aCookieString) {
   nsCOMPtr<nsIEffectiveTLDService> TLDService =
       do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   NS_ASSERTION(TLDService, "Can't get TLDService");
   bool requireHostMatch;
   nsAutoCString baseDomain;
 
   nsCOMPtr<nsILoadInfo> loadInfo;
   mozilla::OriginAttributes attrs;
@@ -318,17 +319,17 @@ void CookieServiceChild::GetCookieString
   int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
 
   nsCOMPtr<nsICookieSettings> cookieSettings =
       nsCookieService::GetCookieSettings(aChannel);
 
   CookieStatus cookieStatus = nsCookieService::CheckPrefs(
       cookieSettings, mThirdPartySession, mThirdPartyNonsecureSession, aHostURI,
       aIsForeign, aIsTrackingResource, aFirstPartyStorageAccessGranted, nullptr,
-      CountCookiesFromHashTable(baseDomain, attrs), attrs, nullptr);
+      CountCookiesFromHashTable(baseDomain, attrs), attrs, &aRejectedReason);
 
   if (cookieStatus != STATUS_ACCEPTED &&
       cookieStatus != STATUS_ACCEPT_SESSION) {
     return;
   }
 
   cookiesList->Sort(CompareCookiesForSending());
   for (uint32_t i = 0; i < cookiesList->Length(); i++) {
@@ -484,35 +485,36 @@ nsresult CookieServiceChild::GetCookieSt
   bool isForeign = true;
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel ? aChannel->LoadInfo() : nullptr;
   if (RequireThirdPartyCheck(loadInfo)) {
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
   }
 
   bool isTrackingResource = false;
   bool firstPartyStorageAccessGranted = false;
+  uint32_t rejectedReason = 0;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->IsTrackingResource();
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
     if (isForeign && AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-                         httpChannel, aHostURI, nullptr)) {
+                         httpChannel, aHostURI, &rejectedReason)) {
       firstPartyStorageAccessGranted = true;
     }
   }
 
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
   bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, aHostURI);
 
   nsAutoCString result;
   GetCookieStringFromCookieHashTable(
       aHostURI, isForeign, isTrackingResource, firstPartyStorageAccessGranted,
-      isSafeTopLevelNav, isSameSiteForeign, aChannel, result);
+      rejectedReason, isSafeTopLevelNav, isSameSiteForeign, aChannel, result);
 
   if (!result.IsEmpty()) *aCookieString = ToNewCString(result);
 
   return NS_OK;
 }
 
 nsresult CookieServiceChild::SetCookieStringInternal(nsIURI* aHostURI,
                                                      nsIChannel* aChannel,
@@ -532,24 +534,25 @@ nsresult CookieServiceChild::SetCookieSt
   bool isForeign = true;
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel ? aChannel->LoadInfo() : nullptr;
   if (RequireThirdPartyCheck(loadInfo)) {
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
   }
 
   bool isTrackingResource = false;
   bool firstPartyStorageAccessGranted = false;
+  uint32_t rejectedReason = 0;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->IsTrackingResource();
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
     if (isForeign && AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-                         httpChannel, aHostURI, nullptr)) {
+                         httpChannel, aHostURI, &rejectedReason)) {
       firstPartyStorageAccessGranted = true;
     }
   }
 
   nsDependentCString cookieString(aCookieString);
   nsDependentCString stringServerTime;
   if (aServerTime) stringServerTime.Rebind(aServerTime);
 
@@ -572,33 +575,33 @@ nsresult CookieServiceChild::SetCookieSt
 
   Maybe<LoadInfoArgs> optionalLoadInfoArgs;
   LoadInfoToLoadInfoArgs(loadInfo, &optionalLoadInfoArgs);
 
   // Asynchronously call the parent.
   if (mIPCOpen) {
     SendSetCookieString(hostURIParams, channelURIParams, optionalLoadInfoArgs,
                         isForeign, isTrackingResource,
-                        firstPartyStorageAccessGranted, attrs, cookieString,
-                        stringServerTime, aFromHttp);
+                        firstPartyStorageAccessGranted, rejectedReason, attrs,
+                        cookieString, stringServerTime, aFromHttp);
   }
 
   bool requireHostMatch;
   nsCString baseDomain;
   nsCookieService::GetBaseDomain(mTLDService, aHostURI, baseDomain,
                                  requireHostMatch);
 
   nsCOMPtr<nsICookieSettings> cookieSettings =
       nsCookieService::GetCookieSettings(aChannel);
 
   CookieStatus cookieStatus = nsCookieService::CheckPrefs(
       cookieSettings, mThirdPartySession, mThirdPartyNonsecureSession, aHostURI,
       isForeign, isTrackingResource, firstPartyStorageAccessGranted,
       aCookieString, CountCookiesFromHashTable(baseDomain, attrs), attrs,
-      nullptr);
+      &rejectedReason);
 
   if (cookieStatus != STATUS_ACCEPTED &&
       cookieStatus != STATUS_ACCEPT_SESSION) {
     return NS_OK;
   }
 
   nsCookieKey key(baseDomain, attrs);
   CookiesList* cookies = mCookiesMap.Get(key);
--- a/netwerk/cookie/CookieServiceChild.h
+++ b/netwerk/cookie/CookieServiceChild.h
@@ -58,18 +58,19 @@ class CookieServiceChild : public PCooki
                      nsCString& aOriginatingSpec,
                      nsCString& aOriginatingCharset);
 
   nsresult GetCookieStringInternal(nsIURI* aHostURI, nsIChannel* aChannel,
                                    char** aCookieString);
 
   void GetCookieStringFromCookieHashTable(
       nsIURI* aHostURI, bool aIsForeign, bool aIsTrackingResource,
-      bool aFirstPartyStorageAccessGranted, bool aIsSafeTopLevelNav,
-      bool aIsSameSiteForeign, nsIChannel* aChannel, nsCString& aCookieString);
+      bool aFirstPartyStorageAccessGranted, uint32_t aRejectedReason,
+      bool aIsSafeTopLevelNav, bool aIsSameSiteForeign, nsIChannel* aChannel,
+      nsCString& aCookieString);
 
   nsresult SetCookieStringInternal(nsIURI* aHostURI, nsIChannel* aChannel,
                                    const char* aCookieString,
                                    const char* aServerTime, bool aFromHttp);
 
   void RecordDocumentCookie(nsCookie* aCookie, const OriginAttributes& aAttrs);
 
   void SetCookieInternal(nsCookieAttributes& aCookieAttributes,
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -132,32 +132,34 @@ void CookieServiceParent::TrackCookieLoa
   // Send matching cookies to Child.
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil;
   thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
   bool isForeign = true;
   thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
 
   bool isTrackingResource = false;
   bool storageAccessGranted = false;
+  uint32_t rejectedReason = 0;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->IsTrackingResource();
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
-    if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
-                                                                uri, nullptr)) {
+    if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
+            httpChannel, uri, &rejectedReason)) {
       storageAccessGranted = true;
     }
   }
 
   nsTArray<nsCookie*> foundCookieList;
-  mCookieService->GetCookiesForURI(
-      uri, aChannel, isForeign, isTrackingResource, storageAccessGranted,
-      isSafeTopLevelNav, aIsSameSiteForeign, false, attrs, foundCookieList);
+  mCookieService->GetCookiesForURI(uri, aChannel, isForeign, isTrackingResource,
+                                   storageAccessGranted, rejectedReason,
+                                   isSafeTopLevelNav, aIsSameSiteForeign, false,
+                                   attrs, foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
   SerialializeCookieList(foundCookieList, matchingCookiesList, uri);
   Unused << SendTrackCookiesLoad(matchingCookiesList, attrs);
 }
 
 void CookieServiceParent::SerialializeCookieList(
     const nsTArray<nsCookie*>& aFoundCookieList,
     nsTArray<CookieStruct>& aCookiesList, nsIURI* aHostURI) {
@@ -177,45 +179,47 @@ void CookieServiceParent::SerialializeCo
     cookieStruct->isSecure() = cookie->IsSecure();
     cookieStruct->sameSite() = cookie->SameSite();
   }
 }
 
 mozilla::ipc::IPCResult CookieServiceParent::RecvPrepareCookieList(
     const URIParams& aHost, const bool& aIsForeign,
     const bool& aIsTrackingResource,
-    const bool& aFirstPartyStorageAccessGranted, const bool& aIsSafeTopLevelNav,
+    const bool& aFirstPartyStorageAccessGranted,
+    const uint32_t& aRejectedReason, const bool& aIsSafeTopLevelNav,
     const bool& aIsSameSiteForeign, const OriginAttributes& aAttrs) {
   nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
 
   // Send matching cookies to Child.
   nsTArray<nsCookie*> foundCookieList;
   // Note: passing nullptr as aChannel to GetCookiesForURI() here is fine since
   // this argument is only used for proper reporting of cookie loads, but the
   // child process already does the necessary reporting in this case for us.
   mCookieService->GetCookiesForURI(
       hostURI, nullptr, aIsForeign, aIsTrackingResource,
-      aFirstPartyStorageAccessGranted, aIsSafeTopLevelNav, aIsSameSiteForeign,
-      false, aAttrs, foundCookieList);
+      aFirstPartyStorageAccessGranted, aRejectedReason, aIsSafeTopLevelNav,
+      aIsSameSiteForeign, false, aAttrs, foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
   SerialializeCookieList(foundCookieList, matchingCookiesList, hostURI);
   Unused << SendTrackCookiesLoad(matchingCookiesList, aAttrs);
   return IPC_OK();
 }
 
 void CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy) {
   // Nothing needed here. Called right before destructor since this is a
   // non-refcounted class.
 }
 
 mozilla::ipc::IPCResult CookieServiceParent::RecvSetCookieString(
     const URIParams& aHost, const Maybe<URIParams>& aChannelURI,
     const Maybe<LoadInfoArgs>& aLoadInfoArgs, const bool& aIsForeign,
     const bool& aIsTrackingResource,
-    const bool& aFirstPartyStorageAccessGranted, const OriginAttributes& aAttrs,
+    const bool& aFirstPartyStorageAccessGranted,
+    const uint32_t& aRejectedReason, const OriginAttributes& aAttrs,
     const nsCString& aCookieString, const nsCString& aServerTime,
     const bool& aFromHttp) {
   if (!mCookieService) return IPC_OK();
 
   // Deserialize URI. Having a host URI is mandatory and should always be
   // provided by the child; thus we consider failure fatal.
   nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
   if (!hostURI) return IPC_FAIL_NO_REASON(this);
@@ -244,15 +248,16 @@ mozilla::ipc::IPCResult CookieServicePar
   // NB: dummyChannel could be null if something failed in CreateDummyChannel.
   nsDependentCString cookieString(aCookieString, 0);
 
   // We set this to true while processing this cookie update, to make sure
   // we don't send it back to the same content process.
   mProcessingCookie = true;
   mCookieService->SetCookieStringInternal(
       hostURI, aIsForeign, aIsTrackingResource, aFirstPartyStorageAccessGranted,
-      cookieString, aServerTime, aFromHttp, aAttrs, dummyChannel);
+      aRejectedReason, cookieString, aServerTime, aFromHttp, aAttrs,
+      dummyChannel);
   mProcessingCookie = false;
   return IPC_OK();
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/cookie/CookieServiceParent.h
+++ b/netwerk/cookie/CookieServiceParent.h
@@ -44,25 +44,26 @@ class CookieServiceParent : public PCook
  protected:
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   mozilla::ipc::IPCResult RecvSetCookieString(
       const URIParams& aHost, const Maybe<URIParams>& aChannelURI,
       const Maybe<LoadInfoArgs>& aLoadInfoArgs, const bool& aIsForeign,
       const bool& aIsTrackingResource,
       const bool& aFirstPartyStorageAccessGranted,
-      const OriginAttributes& aAttrs, const nsCString& aCookieString,
-      const nsCString& aServerTime, const bool& aFromHttp);
+      const uint32_t& aRejectedReason, const OriginAttributes& aAttrs,
+      const nsCString& aCookieString, const nsCString& aServerTime,
+      const bool& aFromHttp);
 
   mozilla::ipc::IPCResult RecvPrepareCookieList(
       const URIParams& aHost, const bool& aIsForeign,
       const bool& aIsTrackingResource,
       const bool& aFirstPartyStorageAccessGranted,
-      const bool& aIsSafeTopLevelNav, const bool& aIsSameSiteForeign,
-      const OriginAttributes& aAttrs);
+      const uint32_t& aRejectedReason, const bool& aIsSafeTopLevelNav,
+      const bool& aIsSameSiteForeign, const OriginAttributes& aAttrs);
 
   void SerialializeCookieList(const nsTArray<nsCookie*>& aFoundCookieList,
                               nsTArray<CookieStruct>& aCookiesList,
                               nsIURI* aHostURI);
 
   RefPtr<nsCookieService> mCookieService;
   bool mProcessingCookie;
 };
--- a/netwerk/cookie/PCookieService.ipdl
+++ b/netwerk/cookie/PCookieService.ipdl
@@ -70,25 +70,27 @@ parent:
    * @see mozIThirdPartyUtil.isThirdPartyChannel
    */
   nested(inside_cpow) async SetCookieString(URIParams host,
                                             URIParams? channelURI,
                                             LoadInfoArgs? loadInfoArgs,
                                             bool isForeign,
                                             bool isTrackingResource,
                                             bool firstPartyStorageAccessGranted,
+                                            uint32_t rejectedReason,
                                             OriginAttributes aStoragePrincipalAttrs,
                                             nsCString cookieString,
                                             nsCString serverTime,
                                             bool aFromHttp);
 
   async PrepareCookieList(URIParams host,
                           bool isForeign,
                           bool isTrackingResource,
                           bool firstPartyStorageAccessGranted,
+                          uint32_t rejectedReason,
                           bool isSafeTopLevelNav,
                           bool isSameSiteForeign,
                           OriginAttributes attrs);
 
   async __delete__();
 
 child:
   async TrackCookiesLoad(CookieStruct[] cookiesList,
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Printf.h"
+#include "mozilla/StorageAccess.h"
 #include "mozilla/Unused.h"
 
 #include "mozilla/net/CookieSettings.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/NeckoCommon.h"
 
 #include "nsCookieService.h"
 #include "nsContentUtils.h"
@@ -1969,41 +1970,43 @@ nsresult nsCookieService::GetCookieStrin
   NS_ENSURE_ARG(aCookie);
 
   // Determine whether the request is foreign. Failure is acceptable.
   bool isForeign = true;
   mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   bool isTrackingResource = false;
   bool firstPartyStorageAccessGranted = false;
+  uint32_t rejectedReason = 0;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->IsTrackingResource();
 
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
     if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-            httpChannel, aHostURI, nullptr)) {
+            httpChannel, aHostURI, &rejectedReason)) {
       firstPartyStorageAccessGranted = true;
     }
   }
 
   OriginAttributes attrs;
   if (aChannel) {
     NS_GetOriginAttributes(aChannel, attrs,
                            true /* considering storage principal */);
   }
 
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
   bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, aHostURI);
   nsAutoCString result;
   GetCookieStringInternal(aHostURI, aChannel, isForeign, isTrackingResource,
-                          firstPartyStorageAccessGranted, isSafeTopLevelNav,
-                          isSameSiteForeign, aHttpBound, attrs, result);
+                          firstPartyStorageAccessGranted, rejectedReason,
+                          isSafeTopLevelNav, isSameSiteForeign, aHttpBound,
+                          attrs, result);
   *aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
   return NS_OK;
 }
 
 // static
 already_AddRefed<nsICookieSettings> nsCookieService::GetCookieSettings(
     nsIChannel* aChannel) {
   nsCOMPtr<nsICookieSettings> cookieSettings;
@@ -2089,48 +2092,50 @@ nsresult nsCookieService::SetCookieStrin
   NS_ENSURE_ARG(aCookieHeader);
 
   // Determine whether the request is foreign. Failure is acceptable.
   bool isForeign = true;
   mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   bool isTrackingResource = false;
   bool firstPartyStorageAccessGranted = false;
+  uint32_t rejectedReason = 0;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->IsTrackingResource();
 
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
     if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-            httpChannel, aHostURI, nullptr)) {
+            httpChannel, aHostURI, &rejectedReason)) {
       firstPartyStorageAccessGranted = true;
     }
   }
 
   OriginAttributes attrs;
   if (aChannel) {
     NS_GetOriginAttributes(aChannel, attrs,
                            true /* considering storage principal */);
   }
 
   nsDependentCString cookieString(aCookieHeader);
   nsDependentCString serverTime(aServerTime ? aServerTime : "");
   SetCookieStringInternal(aHostURI, isForeign, isTrackingResource,
-                          firstPartyStorageAccessGranted, cookieString,
-                          serverTime, aFromHttp, attrs, aChannel);
+                          firstPartyStorageAccessGranted, rejectedReason,
+                          cookieString, serverTime, aFromHttp, attrs, aChannel);
   return NS_OK;
 }
 
 void nsCookieService::SetCookieStringInternal(
     nsIURI* aHostURI, bool aIsForeign, bool aIsTrackingResource,
-    bool aFirstPartyStorageAccessGranted, nsDependentCString& aCookieHeader,
-    const nsCString& aServerTime, bool aFromHttp,
-    const OriginAttributes& aOriginAttrs, nsIChannel* aChannel) {
+    bool aFirstPartyStorageAccessGranted, uint32_t aRejectedReason,
+    nsDependentCString& aCookieHeader, const nsCString& aServerTime,
+    bool aFromHttp, const OriginAttributes& aOriginAttrs,
+    nsIChannel* aChannel) {
   NS_ASSERTION(aHostURI, "null host!");
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return;
   }
 
   EnsureReadComplete(true);
@@ -2154,17 +2159,17 @@ void nsCookieService::SetCookieStringInt
     return;
   }
 
   nsCookieKey key(baseDomain, aOriginAttrs);
   nsCOMPtr<nsICookieSettings> cookieSettings = GetCookieSettings(aChannel);
 
   // check default prefs
   uint32_t priorCookieCount = 0;
-  uint32_t rejectedReason = 0;
+  uint32_t rejectedReason = aRejectedReason;
   nsAutoCString hostFromURI;
   aHostURI->GetHost(hostFromURI);
   CountCookiesFromHost(hostFromURI, &priorCookieCount);
   CookieStatus cookieStatus = CheckPrefs(
       cookieSettings, mThirdPartySession, mThirdPartyNonsecureSession, aHostURI,
       aIsForeign, aIsTrackingResource, aFirstPartyStorageAccessGranted,
       aCookieHeader.get(), priorCookieCount, aOriginAttrs, &rejectedReason);
 
@@ -3014,18 +3019,19 @@ bool nsCookieService::PathMatches(nsCook
   // either the paths match exactly, or the cookie path is a prefix of
   // the given path.
   return true;
 }
 
 void nsCookieService::GetCookiesForURI(
     nsIURI* aHostURI, nsIChannel* aChannel, bool aIsForeign,
     bool aIsTrackingResource, bool aFirstPartyStorageAccessGranted,
-    bool aIsSafeTopLevelNav, bool aIsSameSiteForeign, bool aHttpBound,
-    const OriginAttributes& aOriginAttrs, nsTArray<nsCookie*>& aCookieList) {
+    uint32_t aRejectedReason, bool aIsSafeTopLevelNav, bool aIsSameSiteForeign,
+    bool aHttpBound, const OriginAttributes& aOriginAttrs,
+    nsTArray<nsCookie*>& aCookieList) {
   NS_ASSERTION(aHostURI, "null host!");
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return;
   }
 
   EnsureReadComplete(true);
@@ -3049,17 +3055,17 @@ void nsCookieService::GetCookiesForURI(
     COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nullptr,
                       "invalid host/path from URI");
     return;
   }
 
   nsCOMPtr<nsICookieSettings> cookieSettings = GetCookieSettings(aChannel);
 
   // check default prefs
-  uint32_t rejectedReason = 0;
+  uint32_t rejectedReason = aRejectedReason;
   uint32_t priorCookieCount = 0;
   CountCookiesFromHost(hostFromURI, &priorCookieCount);
   CookieStatus cookieStatus = CheckPrefs(
       cookieSettings, mThirdPartySession, mThirdPartyNonsecureSession, aHostURI,
       aIsForeign, aIsTrackingResource, aFirstPartyStorageAccessGranted, nullptr,
       priorCookieCount, aOriginAttrs, &rejectedReason);
 
   MOZ_ASSERT_IF(rejectedReason, cookieStatus == STATUS_REJECTED);
@@ -3188,23 +3194,24 @@ void nsCookieService::GetCookiesForURI(
   // this is required per RFC2109.  if cookies match in length,
   // then sort by creation time (see bug 236772).
   aCookieList.Sort(CompareCookiesForSending());
 }
 
 void nsCookieService::GetCookieStringInternal(
     nsIURI* aHostURI, nsIChannel* aChannel, bool aIsForeign,
     bool aIsTrackingResource, bool aFirstPartyStorageAccessGranted,
-    bool aIsSafeTopLevelNav, bool aIsSameSiteForeign, bool aHttpBound,
-    const OriginAttributes& aOriginAttrs, nsCString& aCookieString) {
+    uint32_t aRejectedReason, bool aIsSafeTopLevelNav, bool aIsSameSiteForeign,
+    bool aHttpBound, const OriginAttributes& aOriginAttrs,
+    nsCString& aCookieString) {
   AutoTArray<nsCookie*, 8> foundCookieList;
   GetCookiesForURI(aHostURI, aChannel, aIsForeign, aIsTrackingResource,
-                   aFirstPartyStorageAccessGranted, aIsSafeTopLevelNav,
-                   aIsSameSiteForeign, aHttpBound, aOriginAttrs,
-                   foundCookieList);
+                   aFirstPartyStorageAccessGranted, aRejectedReason,
+                   aIsSafeTopLevelNav, aIsSameSiteForeign, aHttpBound,
+                   aOriginAttrs, foundCookieList);
 
   nsCookie* cookie;
   for (uint32_t i = 0; i < foundCookieList.Length(); ++i) {
     cookie = foundCookieList.ElementAt(i);
 
     // check if we have anything to write
     if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
       // if we've already added a cookie to the return list, append a "; " so
@@ -3996,22 +4003,19 @@ static inline bool IsSubdomainOf(const n
 CookieStatus nsCookieService::CheckPrefs(
     nsICookieSettings* aCookieSettings, bool aThirdPartySession,
     bool aThirdPartyNonsecureSession, nsIURI* aHostURI, bool aIsForeign,
     bool aIsTrackingResource, bool aFirstPartyStorageAccessGranted,
     const char* aCookieHeader, const int aNumOfCookies,
     const OriginAttributes& aOriginAttrs, uint32_t* aRejectedReason) {
   nsresult rv;
 
-  // Let's use a internal value in order to avoid a null check on
-  // aRejectedReason everywhere.
-  uint32_t rejectedReason = 0;
-  if (!aRejectedReason) {
-    aRejectedReason = &rejectedReason;
-  }
+  MOZ_ASSERT(aRejectedReason);
+
+  uint32_t aInputRejectedReason = *aRejectedReason;
 
   *aRejectedReason = 0;
 
   // don't let ftp sites get/set cookies (could be a security issue)
   bool ftp;
   if (NS_SUCCEEDED(aHostURI->SchemeIs("ftp", &ftp)) && ftp) {
     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI,
                       aCookieHeader, "ftp sites cannot read cookies");
@@ -4047,17 +4051,17 @@ CookieStatus nsCookieService::CheckPrefs
   }
 
   // No cookies allowed if this request comes from a tracker, in a 3rd party
   // context, when anti-tracking protection is enabled and when we don't have
   // access to the first-party cookie jar.
   if (aIsForeign && aIsTrackingResource && !aFirstPartyStorageAccessGranted &&
       aCookieSettings->GetCookieBehavior() ==
           nsICookieService::BEHAVIOR_REJECT_TRACKER) {
-    if (StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+    if (StoragePartitioningEnabled(aInputRejectedReason, aCookieSettings)) {
       MOZ_ASSERT(!aOriginAttrs.mFirstPartyDomain.IsEmpty(),
                  "We must have a StoragePrincipal here!");
       return STATUS_ACCEPTED;
     }
 
     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI,
                       aCookieHeader, "cookies are disabled in trackers");
     *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -268,18 +268,19 @@ class nsCookieService final : public nsI
   static int64_t ParseServerTime(const nsCString& aServerTime);
 
   static already_AddRefed<nsICookieSettings> GetCookieSettings(
       nsIChannel* aChannel);
 
   void GetCookiesForURI(nsIURI* aHostURI, nsIChannel* aChannel, bool aIsForeign,
                         bool aIsTrackingResource,
                         bool aFirstPartyStorageAccessGranted,
-                        bool aIsSafeTopLevelNav, bool aIsSameSiteForeign,
-                        bool aHttpBound, const OriginAttributes& aOriginAttrs,
+                        uint32_t aRejectedReason, bool aIsSafeTopLevelNav,
+                        bool aIsSameSiteForeign, bool aHttpBound,
+                        const OriginAttributes& aOriginAttrs,
                         nsTArray<nsCookie*>& aCookieList);
 
  protected:
   virtual ~nsCookieService();
 
   void PrefChanged(nsIPrefBranch* aPrefBranch);
   void InitDBStates();
   OpenDBResult TryInitDB(bool aDeleteExistingDB);
@@ -301,26 +302,28 @@ class nsCookieService final : public nsI
       mozIStorageStatement* aRow, const OriginAttributes& aOriginAttributes);
   void EnsureReadComplete(bool aInitDBConn);
   nsresult NormalizeHost(nsCString& aHost);
   nsresult GetCookieStringCommon(nsIURI* aHostURI, nsIChannel* aChannel,
                                  bool aHttpBound, char** aCookie);
   void GetCookieStringInternal(nsIURI* aHostURI, nsIChannel* aChannel,
                                bool aIsForeign, bool aIsTrackingResource,
                                bool aFirstPartyStorageAccessGranted,
+                               uint32_t aRejectedReason,
                                bool aIsSafeTopLevelNav, bool aIsSameSiteForeign,
                                bool aHttpBound,
                                const OriginAttributes& aOriginAttrs,
                                nsCString& aCookie);
   nsresult SetCookieStringCommon(nsIURI* aHostURI, const char* aCookieHeader,
                                  const char* aServerTime, nsIChannel* aChannel,
                                  bool aFromHttp);
   void SetCookieStringInternal(nsIURI* aHostURI, bool aIsForeign,
                                bool aIsTrackingResource,
                                bool aFirstPartyStorageAccessGranted,
+                               uint32_t aRejectedReason,
                                nsDependentCString& aCookieHeader,
                                const nsCString& aServerTime, bool aFromHttp,
                                const OriginAttributes& aOriginAttrs,
                                nsIChannel* aChannel);
   bool SetCookieInternal(nsIURI* aHostURI, const nsCookieKey& aKey,
                          bool aRequireHostMatch, CookieStatus aStatus,
                          nsDependentCString& aCookieHeader, int64_t aServerTime,
                          bool aFromHttp, nsIChannel* aChannel);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/StorageAccess.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/StorageAccess.h"
+#include "nsICookieService.h"
+#include "nsICookieSettings.h"
+#include "nsIWebProgressListener.h"
+
+namespace mozilla {
+
+bool ShouldPartitionStorage(nsContentUtils::StorageAccess aAccess) {
+  return aAccess == nsContentUtils::StorageAccess::ePartitionTrackersOrDeny ||
+         aAccess == nsContentUtils::StorageAccess::ePartitionForeignOrDeny;
+}
+
+bool ShouldPartitionStorage(uint32_t aRejectedReason) {
+  return aRejectedReason ==
+             nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
+         aRejectedReason ==
+             nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN;
+}
+
+bool StoragePartitioningEnabled(nsContentUtils::StorageAccess aAccess,
+                                nsICookieSettings* aCookieSettings) {
+  if (aAccess == nsContentUtils::StorageAccess::ePartitionTrackersOrDeny) {
+    return aCookieSettings->GetCookieBehavior() ==
+               nsICookieService::BEHAVIOR_REJECT_TRACKER &&
+           StaticPrefs::privacy_storagePrincipal_enabledForTrackers();
+  }
+  if (aAccess == nsContentUtils::StorageAccess::ePartitionForeignOrDeny) {
+    return aCookieSettings->GetCookieBehavior() ==
+           nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
+  }
+  return false;
+}
+
+bool StoragePartitioningEnabled(uint32_t aRejectedReason,
+                                nsICookieSettings* aCookieSettings) {
+  if (aRejectedReason ==
+      nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
+    return aCookieSettings->GetCookieBehavior() ==
+               nsICookieService::BEHAVIOR_REJECT_TRACKER &&
+           StaticPrefs::privacy_storagePrincipal_enabledForTrackers();
+  }
+  if (aRejectedReason ==
+      nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN) {
+    return aCookieSettings->GetCookieBehavior() ==
+           nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
+  }
+  return false;
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/StorageAccess.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_StorageAccess_h
+#define mozilla_StorageAccess_h
+
+#include "nsContentUtils.h"
+
+class nsICookieSettings;
+
+namespace mozilla {
+
+bool ShouldPartitionStorage(nsContentUtils::StorageAccess aAccess);
+
+bool ShouldPartitionStorage(uint32_t aRejectedReason);
+
+bool StoragePartitioningEnabled(nsContentUtils::StorageAccess aAccess,
+                                nsICookieSettings* aCookieSettings);
+
+bool StoragePartitioningEnabled(uint32_t aRejectedReason,
+                                nsICookieSettings* aCookieSettings);
+
+}  // namespace mozilla
+
+#endif  // mozilla_StorageAccess_h
--- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp
@@ -1,55 +1,68 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "StoragePrincipalHelper.h"
 
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/ScopeExit.h"
+#include "mozilla/StorageAccess.h"
 #include "mozilla/StaticPrefs.h"
 #include "nsContentUtils.h"
 #include "nsIHttpChannel.h"
 
 namespace mozilla {
 
 namespace {
 
 already_AddRefed<nsIURI> MaybeGetFirstPartyURI(nsIChannel* aChannel) {
   MOZ_ASSERT(aChannel);
 
-  if (!StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+  nsCOMPtr<nsICookieSettings> cs;
+  if (NS_FAILED(loadInfo->GetCookieSettings(getter_AddRefs(cs)))) {
     return nullptr;
   }
 
-  // Let's use the storage principal only if we need to partition the cookie
-  // jar.
-  nsContentUtils::StorageAccess access =
-      nsContentUtils::StorageAllowedForChannel(aChannel);
-  if (access != nsContentUtils::StorageAccess::ePartitionTrackersOrDeny) {
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
+  if (NS_FAILED(rv)) {
     return nullptr;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (!httpChannel) {
     return nullptr;
   }
 
-  MOZ_ASSERT(httpChannel->IsThirdPartyTrackingResource());
+  uint32_t rejectedReason = 0;
+  if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
+          httpChannel, uri, &rejectedReason)) {
+    return nullptr;
+  }
 
-  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+  // Let's use the storage principal only if we need to partition the cookie
+  // jar.  We use the lower-level AntiTrackingCommon API here to ensure this
+  // check doesn't send notifications.
+  if (!ShouldPartitionStorage(rejectedReason) ||
+      !StoragePartitioningEnabled(rejectedReason, cs)) {
+    return nullptr;
+  }
+
   nsCOMPtr<nsIPrincipal> toplevelPrincipal = loadInfo->GetTopLevelPrincipal();
   if (!toplevelPrincipal) {
     return nullptr;
   }
 
   nsCOMPtr<nsIURI> principalURI;
-  nsresult rv = toplevelPrincipal->GetURI(getter_AddRefs(principalURI));
+  rv = toplevelPrincipal->GetURI(getter_AddRefs(principalURI));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   return principalURI.forget();
 }
 
 }  // namespace
--- a/toolkit/components/antitracking/moz.build
+++ b/toolkit/components/antitracking/moz.build
@@ -4,21 +4,23 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'DOM: Security')
 
 EXPORTS.mozilla = [
     'AntiTrackingCommon.h',
+    'StorageAccess.h',
     'StoragePrincipalHelper.h',
 ]
 
 UNIFIED_SOURCES += [
     'AntiTrackingCommon.cpp',
+    'StorageAccess.cpp',
     'StoragePrincipalHelper.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/extensions/permissions',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')