Bug 1480780 - Merge the privacy.3rdpartystorage.enabled pref with the network.cookie.cookieBehavior pref; r=ehsan
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 10 Aug 2018 15:55:22 -0400
changeset 489054 e70b1d7ba1ebeb23000e6c1a97dafafd6b79a324
parent 489028 6f69e808328401eba4d3984f5c755f82ebbcd631
child 489055 f93a51060a07cb216d9c44cb538a948155e1ad32
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1480780
milestone63.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 1480780 - Merge the privacy.3rdpartystorage.enabled pref with the network.cookie.cookieBehavior pref; r=ehsan This patch introduces a new cookie behavior policy called BEHAVIOR_REJECT_TRACKER. It also makes it possible to override that behavior with cookie permissions similar to other cookie behaviors.
dom/base/Navigator.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsDocument.cpp
dom/broadcastchannel/BroadcastChannel.cpp
dom/html/nsHTMLDocument.cpp
dom/workers/RuntimeService.cpp
image/ImageCacheKey.cpp
modules/libpref/init/StaticPrefList.h
netwerk/base/nsChannelClassifier.cpp
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsICookiePermission.idl
netwerk/cookie/nsICookieService.idl
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsIHttpChannel.idl
toolkit/components/antitracking/AntiTrackingCommon.cpp
toolkit/components/antitracking/AntiTrackingCommon.h
toolkit/components/antitracking/test/browser/browser_script.js
toolkit/components/antitracking/test/browser/browser_subResources.js
toolkit/components/antitracking/test/browser/head.js
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -6,16 +6,17 @@
 
 // Needs to be first.
 #include "base/basictypes.h"
 
 #include "Navigator.h"
 #include "nsIXULAppInfo.h"
 #include "nsPluginArray.h"
 #include "nsMimeTypeArray.h"
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/BodyExtractor.h"
 #include "mozilla/dom/FetchBinding.h"
 #include "mozilla/dom/File.h"
 #include "nsGeolocation.h"
 #include "nsIClassOfService.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsIContentPolicy.h"
@@ -58,16 +59,17 @@
 #include "mozilla/dom/Event.h" // for Event
 #include "nsGlobalWindow.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 #include "nsRFPService.h"
 #include "nsStringStream.h"
 #include "nsComponentManagerUtils.h"
+#include "nsICookieService.h"
 #include "nsIStringStream.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsStreamUtils.h"
 #include "WidgetUtils.h"
 #include "nsIPresentationService.h"
 #include "nsIScriptError.h"
 
@@ -508,25 +510,21 @@ Navigator::Storage()
 
   if(!mStorageManager) {
     mStorageManager = new StorageManager(mWindow->AsGlobal());
   }
 
   return mStorageManager;
 }
 
-// Values for the network.cookie.cookieBehavior pref are documented in
-// nsCookieService.cpp.
-#define COOKIE_BEHAVIOR_REJECT 2
-
 bool
 Navigator::CookieEnabled()
 {
   bool cookieEnabled = (StaticPrefs::network_cookie_cookieBehavior() !=
-                        COOKIE_BEHAVIOR_REJECT);
+                        nsICookieService::BEHAVIOR_REJECT);
 
   // Check whether an exception overrides the global cookie behavior
   // Note that the code for getting the URI here matches that in
   // nsHTMLDocument::SetCookie.
   if (!mWindow || !mWindow->GetDocShell()) {
     return cookieEnabled;
   }
 
@@ -539,30 +537,18 @@ Navigator::CookieEnabled()
   doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
 
   if (!codebaseURI) {
     // Not a codebase, so technically can't set cookies, but let's
     // just return the default value.
     return cookieEnabled;
   }
 
-  nsCOMPtr<nsICookiePermission> permMgr =
-    do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
-  NS_ENSURE_TRUE(permMgr, cookieEnabled);
-
-  // Pass null for the channel, just like the cookie service does.
-  nsCookieAccess access;
-  nsresult rv = permMgr->CanAccess(doc->NodePrincipal(), &access);
-  NS_ENSURE_SUCCESS(rv, cookieEnabled);
-
-  if (access != nsICookiePermission::ACCESS_DEFAULT) {
-    cookieEnabled = access != nsICookiePermission::ACCESS_DENY;
-  }
-
-  return cookieEnabled;
+  return AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(mWindow,
+                                                                 codebaseURI);
 }
 
 bool
 Navigator::OnLine()
 {
   return !NS_IsOffline();
 }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -212,18 +212,18 @@
 #include "nsWidgetsCID.h"
 #include "nsIWindowProvider.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsXULPopupManager.h"
 #include "xpcprivate.h" // nsXPConnect
 #include "HTMLSplitOnSpacesTokenizer.h"
 #include "nsContentTypeParser.h"
 #include "nsICookiePermission.h"
+#include "nsICookieService.h"
 #include "mozIThirdPartyUtil.h"
-#include "nsICookieService.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/BloomFilter.h"
 #include "TabChild.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #include "nsIWebNavigationInfo.h"
 #include "nsPluginHost.h"
 #include "mozilla/HangAnnotations.h"
@@ -8746,57 +8746,50 @@ nsContentUtils::StorageAccess
 nsContentUtils::StorageAllowedForPrincipal(nsIPrincipal* aPrincipal)
 {
   return InternalStorageAllowedForPrincipal(aPrincipal, nullptr, nullptr,
                                             nullptr);
 }
 
 // static, private
 void
-nsContentUtils::GetCookieBehaviorForPrincipal(nsIPrincipal* aPrincipal,
-                                              uint32_t* aLifetimePolicy,
-                                              uint32_t* aBehavior)
+nsContentUtils::GetCookieLifetimePolicyForPrincipal(nsIPrincipal* aPrincipal,
+                                                    uint32_t* aLifetimePolicy)
 {
   *aLifetimePolicy = sCookiesLifetimePolicy;
-  *aBehavior = StaticPrefs::network_cookie_cookieBehavior();
 
   // Any permissions set for the given principal will override our default
   // settings from preferences.
   nsCOMPtr<nsIPermissionManager> permissionManager =
     services::GetPermissionManager();
   if (!permissionManager) {
     return;
   }
 
   uint32_t perm;
   permissionManager->TestPermissionFromPrincipal(aPrincipal, "cookie", &perm);
   switch (perm) {
     case nsICookiePermission::ACCESS_ALLOW:
-      *aBehavior = nsICookieService::BEHAVIOR_ACCEPT;
       *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
       break;
     case nsICookiePermission::ACCESS_DENY:
-      *aBehavior = nsICookieService::BEHAVIOR_REJECT;
       *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
       break;
     case nsICookiePermission::ACCESS_SESSION:
-      *aBehavior = nsICookieService::BEHAVIOR_ACCEPT;
       *aLifetimePolicy = nsICookieService::ACCEPT_SESSION;
       break;
     case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
-      *aBehavior = nsICookieService::BEHAVIOR_REJECT_FOREIGN;
       // NOTE: The decision was made here to override the lifetime policy to be
       // ACCEPT_NORMALLY for consistency with ACCESS_ALLOW, but this does
       // prevent us from expressing BEHAVIOR_REJECT_FOREIGN/ACCEPT_SESSION for a
       // specific domain. As BEHAVIOR_REJECT_FOREIGN isn't visible in our UI,
       // this is probably not an issue.
       *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
       break;
     case nsICookiePermission::ACCESS_LIMIT_THIRD_PARTY:
-      *aBehavior = nsICookieService::BEHAVIOR_LIMIT_FOREIGN;
       // NOTE: The decision was made here to override the lifetime policy to be
       // ACCEPT_NORMALLY for consistency with ACCESS_ALLOW, but this does
       // prevent us from expressing BEHAVIOR_REJECT_FOREIGN/ACCEPT_SESSION for a
       // specific domain. As BEHAVIOR_LIMIT_FOREIGN isn't visible in our UI,
       // this is probably not an issue.
       *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
       break;
   }
@@ -8856,88 +8849,66 @@ nsContentUtils::IsTrackingResourceWindow
   }
 
   return httpChannel->GetIsTrackingResource();
 }
 
 static bool
 StorageDisabledByAntiTrackingInternal(nsPIDOMWindowInner* aWindow,
                                       nsIChannel* aChannel,
+                                      nsIPrincipal* aPrincipal,
                                       nsIURI* aURI)
 {
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
-    return false;
-  }
-
-  // Let's check if this is a 3rd party context.
-  if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, aChannel, aURI)) {
-    return false;
-  }
+  MOZ_ASSERT(aWindow || aChannel || aPrincipal);
 
   if (aWindow) {
-    nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow);
-    nsGlobalWindowOuter* outerWindow =
-      nsGlobalWindowOuter::Cast(innerWindow->GetOuterWindow());
-    if (NS_WARN_IF(!outerWindow)) {
-      return false;
-    }
-
-    // We are a first party resource.
-    if (outerWindow->IsTopLevelWindow()) {
-      return false;
-    }
-
     nsIURI* documentURI = aURI ? aURI : aWindow->GetDocumentURI();
-    if (documentURI &&
-        AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(aWindow,
-                                                                documentURI)) {
+    return !documentURI ||
+           !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(aWindow,
+                                                                    documentURI);
+  }
+
+  if (aChannel) {
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+    if (!httpChannel) {
       return false;
     }
 
-    return true;
-  }
-
-  // aChannel and aWindow are mutually exclusive.
-  MOZ_ASSERT(aChannel);
-
-  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
-  if (!httpChannel) {
-    return false;
-  }
-
-  // If this is not a tracking resource, nothing is disabled.
-  if (!httpChannel->GetIsTrackingResource()) {
-    return false;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = httpChannel->GetURI(getter_AddRefs(uri));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
-  return AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
-                                                                 uri);
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = httpChannel->GetURI(getter_AddRefs(uri));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    return !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
+                                                                    uri);
+  }
+
+  MOZ_ASSERT(aPrincipal);
+  return !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(aPrincipal);
 }
 
 // static public
 bool
 nsContentUtils::StorageDisabledByAntiTracking(nsPIDOMWindowInner* aWindow,
                                               nsIChannel* aChannel,
+                                              nsIPrincipal* aPrincipal,
                                               nsIURI* aURI)
 {
   bool disabled =
-    StorageDisabledByAntiTrackingInternal(aWindow, aChannel, aURI);
+    StorageDisabledByAntiTrackingInternal(aWindow, aChannel, aPrincipal, aURI);
   if (disabled &&
       StaticPrefs::privacy_restrict3rdpartystorage_ui_enabled()) {
     nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
     if (!thirdPartyUtil) {
       return false;
     }
 
+    // FIXME: this is wrong. This method is called also with aWindow and a null
+    // aChannel.
     nsCOMPtr<mozIDOMWindowProxy> win;
     nsresult rv = thirdPartyUtil->GetTopWindowForChannel(aChannel,
                                                          getter_AddRefs(win));
     NS_ENSURE_SUCCESS(rv, false);
     auto* pwin = nsPIDOMWindowOuter::From(win);
 
     pwin->NotifyContentBlockingState(
       nsIWebProgressListener::STATE_BLOCKED_TRACKING_COOKIES, aChannel);
@@ -8957,45 +8928,39 @@ nsContentUtils::InternalStorageAllowedFo
   StorageAccess access = StorageAccess::eAllow;
 
   // We don't allow storage on the null principal, in general. Even if the
   // calling context is chrome.
   if (aPrincipal->GetIsNullPrincipal()) {
     return StorageAccess::eDeny;
   }
 
-  if (StorageDisabledByAntiTracking(aWindow, aChannel, aURI)) {
-    return StorageAccess::eDeny;
-  }
-
   if (aWindow) {
     // If the document is sandboxed, then it is not permitted to use storage
     nsIDocument* document = aWindow->GetExtantDoc();
     if (document && document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
       return StorageAccess::eDeny;
     }
 
     // Check if we are in private browsing, and record that fact
     if (IsInPrivateBrowsing(document)) {
       access = StorageAccess::ePrivateBrowsing;
     }
   }
 
   uint32_t lifetimePolicy;
-  uint32_t behavior;
 
   // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior
   // and ACCEPT_NORMALLY as lifetimePolicy (See Bug 1406675 for rationale).
   auto policy = BasePrincipal::Cast(aPrincipal)->AddonPolicy();
 
   if (policy) {
-    behavior = nsICookieService::BEHAVIOR_ACCEPT;
     lifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
   } else {
-    GetCookieBehaviorForPrincipal(aPrincipal, &lifetimePolicy, &behavior);
+    GetCookieLifetimePolicyForPrincipal(aPrincipal, &lifetimePolicy);
   }
 
   // Check if we should only allow storage for the session, and record that fact
   if (lifetimePolicy == nsICookieService::ACCEPT_SESSION) {
     // Storage could be StorageAccess::ePrivateBrowsing or StorageAccess::eAllow
     // so perform a std::min comparison to make sure we preserve ePrivateBrowsing
     // if it has been set.
     access = std::min(StorageAccess::eSessionScoped, access);
@@ -9031,29 +8996,17 @@ nsContentUtils::InternalStorageAllowedFo
   if (uri) {
     bool isAbout = false;
     MOZ_ALWAYS_SUCCEEDS(uri->SchemeIs("about", &isAbout));
     if (isAbout) {
       return access;
     }
   }
 
-  // We don't want to prompt for every attempt to access permissions.
-  if (behavior == nsICookieService::BEHAVIOR_REJECT) {
-    return StorageAccess::eDeny;
-  }
-
-  if ((behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
-       behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) &&
-      IsThirdPartyWindowOrChannel(aWindow, aChannel, aURI)) {
-    // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
-    // simply rejecting the request to use the storage. In the future, if we
-    // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
-    // for non-cookie storage types, this may change.
-
+  if (StorageDisabledByAntiTracking(aWindow, aChannel, aPrincipal, aURI)) {
     return StorageAccess::eDeny;
   }
 
   return access;
 }
 
 namespace {
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2946,21 +2946,22 @@ public:
   /*
    * Checks if storage for the given principal is permitted by the user's
    * preferences. The caller is assumed to not be a third-party iframe.
    * (if that is possible, the caller should use StorageAllowedForWindow)
    */
   static StorageAccess StorageAllowedForPrincipal(nsIPrincipal* aPrincipal);
 
   /*
-   * Returns true if this window/channel should disable storages because of the
-   * anti-tracking feature.
+   * Returns true if this window/channel/aPrincipal should disable storages
+   * because of the anti-tracking feature.
    */
   static bool StorageDisabledByAntiTracking(nsPIDOMWindowInner* aWindow,
                                             nsIChannel* aChannel,
+                                            nsIPrincipal* aPrincipal,
                                             nsIURI* aURI);
 
   /*
    * Returns true if this window/channel is a 3rd party context.
    */
   static bool IsThirdPartyWindowOrChannel(nsPIDOMWindowInner* aWindow,
                                           nsIChannel* aChannel,
                                           nsIURI* aURI);
@@ -3337,24 +3338,23 @@ private:
                                                                       mozilla::dom::AutocompleteInfo& aInfo,
                                                                       bool aGrantAllValidValue = false);
 
   static bool CallOnAllRemoteChildren(mozilla::dom::MessageBroadcaster* aManager,
                                       CallOnRemoteChildFunction aCallback,
                                       void* aArg);
 
   /**
-   * Gets the current cookie lifetime policy and cookie behavior for a given
-   * principal by checking with preferences and the permission manager.
+   * Gets the current cookie lifetime policy for a given principal by checking
+   * with preferences and the permission manager.
    *
    * Used in the implementation of InternalStorageAllowedForPrincipal.
    */
-  static void GetCookieBehaviorForPrincipal(nsIPrincipal* aPrincipal,
-                                            uint32_t* aLifetimePolicy,
-                                            uint32_t* aBehavior);
+  static void GetCookieLifetimePolicyForPrincipal(nsIPrincipal* aPrincipal,
+                                                  uint32_t* aLifetimePolicy);
 
   /*
    * Checks if storage for a given principal is permitted by the user's
    * preferences. If aWindow is non-null, its principal must be passed as
    * aPrincipal, and the third-party iframe and sandboxing status of the window
    * are also checked.  If aURI is non-null, then it is used as the comparison
    * against aWindow to determine if this is a third-party load.  We also
    * allow a channel instead of the window reference when determining 3rd party
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12638,17 +12638,18 @@ nsIDocument::SetDocTreeHadPlayRevoked()
   if (topLevelDoc) {
     topLevelDoc->mDocTreeHadPlayRevoked = true;
   }
 }
 
 void
 nsIDocument::MaybeAllowStorageForOpener()
 {
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
+  if (StaticPrefs::network_cookie_cookieBehavior() !=
+        nsICookieService::BEHAVIOR_REJECT_TRACKER) {
     return;
   }
 
   // This will probably change for project fission, but currently this document
   // and the opener are on the same process. In the future, we should make this
   // part async.
 
   nsPIDOMWindowInner* inner = GetInnerWindow();
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -12,20 +12,20 @@
 #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/StaticPrefs.h"
 #include "nsContentUtils.h"
 
 #include "nsIBFCacheEntry.h"
+#include "nsICookieService.h"
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
 
 #ifdef XP_WIN
 #undef PostMessage
 #endif
 
 namespace mozilla {
@@ -68,22 +68,24 @@ GetPrincipalFromThreadSafeWorkerRef(Thre
 
   return wp->GetPrincipal();
 }
 
 class InitializeRunnable final : public WorkerMainThreadRunnable
 {
 public:
   InitializeRunnable(ThreadSafeWorkerRef* aWorkerRef, nsACString& aOrigin,
-                     PrincipalInfo& aPrincipalInfo, ErrorResult& aRv)
+                     PrincipalInfo& aPrincipalInfo, bool* aThirdPartyWindow,
+                     ErrorResult& aRv)
     : WorkerMainThreadRunnable(aWorkerRef->Private(),
                                NS_LITERAL_CSTRING("BroadcastChannel :: Initialize"))
     , mWorkerRef(aWorkerRef)
     , mOrigin(aOrigin)
     , mPrincipalInfo(aPrincipalInfo)
+    , mThirdPartyWindow(aThirdPartyWindow)
     , mRv(aRv)
   {
     MOZ_ASSERT(mWorkerRef);
   }
 
   bool MainThreadRun() override
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -111,23 +113,27 @@ public:
     }
 
     // Window doesn't exist for some kind of workers (eg: SharedWorkers)
     nsPIDOMWindowInner* window = wp->GetWindow();
     if (!window) {
       return true;
     }
 
+    *mThirdPartyWindow =
+      nsContentUtils::IsThirdPartyWindowOrChannel(window, nullptr, nullptr);
+
     return true;
   }
 
 private:
   RefPtr<ThreadSafeWorkerRef> mWorkerRef;
   nsACString& mOrigin;
   PrincipalInfo& mPrincipalInfo;
+  bool* mThirdPartyWindow;
   ErrorResult& mRv;
 };
 
 class CloseRunnable final : public nsIRunnable,
                             public nsICancelableRunnable
 {
 public:
   NS_DECL_ISUPPORTS
@@ -296,53 +302,56 @@ BroadcastChannel::Constructor(const Glob
       return nullptr;
     }
 
     aRv = PrincipalToPrincipalInfo(principal, &principalInfo);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
-    if (StaticPrefs::privacy_restrict3rdpartystorage_enabled() &&
+    if (nsContentUtils::IsThirdPartyWindowOrChannel(window, nullptr,
+                                                    nullptr) &&
         nsContentUtils::StorageAllowedForWindow(window) !=
           nsContentUtils::StorageAccess::eAllow) {
       aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
       return nullptr;
     }
   } else {
     JSContext* cx = aGlobal.Context();
 
     WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
     MOZ_ASSERT(workerPrivate);
 
-    if (StaticPrefs::privacy_restrict3rdpartystorage_enabled() &&
-        !workerPrivate->IsStorageAllowed()) {
-      aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-      return nullptr;
-    }
-
     RefPtr<StrongWorkerRef> workerRef =
       StrongWorkerRef::Create(workerPrivate, "BroadcastChannel",
                               [bc] () { bc->Shutdown(); });
     // We are already shutting down the worker. Let's return a non-active
     // object.
     if (NS_WARN_IF(!workerRef)) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
     RefPtr<ThreadSafeWorkerRef> tsr = new ThreadSafeWorkerRef(workerRef);
 
+    bool thirdPartyWindow = false;
+
     RefPtr<InitializeRunnable> runnable =
-      new InitializeRunnable(tsr, origin, principalInfo, aRv);
+      new InitializeRunnable(tsr, origin, principalInfo, &thirdPartyWindow,
+                             aRv);
     runnable->Dispatch(Canceling, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
 
+    if (thirdPartyWindow && !workerPrivate->IsStorageAllowed()) {
+      aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+      return nullptr;
+    }
+
     bc->mWorkerRef = std::move(workerRef);
   }
 
   // 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.
     aRv.Throw(NS_ERROR_FAILURE);
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1102,17 +1102,17 @@ nsHTMLDocument::GetCookie(nsAString& aCo
   // If the document's sandboxed origin flag is set, access to read cookies
   // is prohibited.
   if (mSandboxFlags & SANDBOXED_ORIGIN) {
     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   if (nsContentUtils::StorageDisabledByAntiTracking(GetInnerWindow(), nullptr,
-                                                    nullptr)) {
+                                                    NodePrincipal(), nullptr)) {
     return;
   }
 
   // If the document is a cookie-averse Document... return the empty string.
   if (IsCookieAverse()) {
     return;
   }
 
@@ -1157,17 +1157,17 @@ nsHTMLDocument::SetCookie(const nsAStrin
   // If the document's sandboxed origin flag is set, access to write cookies
   // is prohibited.
   if (mSandboxFlags & SANDBOXED_ORIGIN) {
     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   if (nsContentUtils::StorageDisabledByAntiTracking(GetInnerWindow(), nullptr,
-                                                    nullptr)) {
+                                                    NodePrincipal(), nullptr)) {
     return;
   }
 
   // If the document is a cookie-averse Document... do nothing.
   if (IsCookieAverse()) {
     return;
   }
 
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -4,16 +4,17 @@
  * 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 "RuntimeService.h"
 
 #include "nsAutoPtr.h"
 #include "nsIChannel.h"
 #include "nsIContentSecurityPolicy.h"
+#include "nsICookieService.h"
 #include "nsIDocument.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIObserverService.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
@@ -2263,17 +2264,18 @@ RuntimeService::ResumeWorkersForWindow(n
   }
 }
 
 void
 RuntimeService::PropagateFirstPartyStorageAccessGranted(nsPIDOMWindowInner* aWindow)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWindow);
-  MOZ_ASSERT(StaticPrefs::privacy_restrict3rdpartystorage_enabled());
+  MOZ_ASSERT(StaticPrefs::network_cookie_cookieBehavior() ==
+               nsICookieService::BEHAVIOR_REJECT_TRACKER);
 
   nsTArray<WorkerPrivate*> workers;
   GetWorkersForWindow(aWindow, workers);
 
   for (uint32_t index = 0; index < workers.Length(); index++) {
     workers[index]->PropagateFirstPartyStorageAccessGranted();
   }
 }
@@ -2876,17 +2878,18 @@ ResumeWorkersForWindow(nsPIDOMWindowInne
     runtime->ResumeWorkersForWindow(aWindow);
   }
 }
 
 void
 PropagateFirstPartyStorageAccessGrantedToWorkers(nsPIDOMWindowInner* aWindow)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(StaticPrefs::privacy_restrict3rdpartystorage_enabled());
+  MOZ_ASSERT(StaticPrefs::network_cookie_cookieBehavior() ==
+               nsICookieService::BEHAVIOR_REJECT_TRACKER);
 
   RuntimeService* runtime = RuntimeService::GetService();
   if (runtime) {
     runtime->PropagateFirstPartyStorageAccessGranted(aWindow);
   }
 }
 
 WorkerPrivate*
--- a/image/ImageCacheKey.cpp
+++ b/image/ImageCacheKey.cpp
@@ -3,23 +3,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/. */
 
 #include "ImageCacheKey.h"
 
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Move.h"
 #include "nsContentUtils.h"
+#include "nsICookieService.h"
 #include "nsLayoutUtils.h"
 #include "nsString.h"
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
-#include "mozilla/StaticPrefs.h"
 #include "nsIDocument.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 namespace image {
@@ -136,39 +136,33 @@ ImageCacheKey::GetSpecialCaseDocumentTok
 
   // For controlled documents, we cast the pointer into a void* to avoid
   // dereferencing it (since we only use it for comparisons).
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (swm && aDocument->GetController().isSome()) {
     return aDocument;
   }
 
-  // We want to have a unique image cache if the anti-tracking feature is
-  // enabled for 3rd party resources.
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled() ||
-      !nsContentUtils::IsThirdPartyWindowOrChannel(aDocument->GetInnerWindow(),
-                                                   nullptr, aURI)) {
-    return nullptr;
-  }
-
-  // If the window is 3rd party resource, let's see if the first party storage
-  // access is granted for this image.
-  if (nsContentUtils::IsTrackingResourceWindow(aDocument->GetInnerWindow())) {
-    return nsContentUtils::StorageDisabledByAntiTracking(aDocument->GetInnerWindow(),
-                                                         nullptr, aURI)
-             ? aDocument : nullptr;
+  // If we must disable the storage, we want to create a unique cache key for
+  // this image.
+  if (nsContentUtils::StorageDisabledByAntiTracking(aDocument->GetInnerWindow(),
+                                                    nullptr,
+                                                    aDocument->NodePrincipal(),
+                                                    aURI)) {
+    return aDocument;
   }
 
   // Another scenario is if this image is a 3rd party resource loaded by a
   // first party context. In this case, we should check if the nsIChannel has
   // been marked as tracking resource, but we don't have the channel yet at
   // this point.  The best approach here is to be conservative: if we are sure
   // that the permission is granted, let's return a nullptr. Otherwise, let's
   // make a unique image cache.
-  if (!AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(aDocument->GetInnerWindow(),
+  if (!aDocument->IsCookieAverse() &&
+      !AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(aDocument->GetInnerWindow(),
                                                                     aURI)) {
     return aDocument;
   }
 
   return nullptr;
 }
 
 } // namespace image
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1086,22 +1086,23 @@ VARCACHE_PREF(
 // - true: They are allow to present http auth. dialog
 // - false: They are not allow to present http auth. dialog.
 VARCACHE_PREF(
   "network.auth.non-web-content-triggered-resources-http-auth-allow",
    network_auth_non_web_content_triggered_resources_http_auth_allow,
   bool, false
 )
 
-// 0-Accept, 1-dontAcceptForeign, 2-dontAcceptAny, 3-limitForeign
+// 0-Accept, 1-dontAcceptForeign, 2-dontAcceptAny, 3-limitForeign,
+// 4-rejectTracker
 // Keep the old default of accepting all cookies
 VARCACHE_PREF(
   "network.cookie.cookieBehavior",
   network_cookie_cookieBehavior,
-  int32_t, 0
+  RelaxedAtomicInt32, 0
 )
 
 // Enables the predictive service.
 VARCACHE_PREF(
   "network.predictor.enabled",
    network_predictor_enabled,
   bool, true
 )
@@ -1227,22 +1228,16 @@ VARCACHE_PREF(
 
 PREF("preferences.allow.omt-write", bool, true)
 
 //---------------------------------------------------------------------------
 // Privacy prefs
 //---------------------------------------------------------------------------
 
 VARCACHE_PREF(
-  "privacy.restrict3rdpartystorage.enabled",
-   privacy_restrict3rdpartystorage_enabled,
-  RelaxedAtomicBool, false
-)
-
-VARCACHE_PREF(
   "privacy.restrict3rdpartystorage.ui.enabled",
    privacy_restrict3rdpartystorage_ui_enabled,
   RelaxedAtomicBool, false
 )
 
 // Anti-tracking permission expiration
 VARCACHE_PREF(
   "privacy.restrict3rdpartystorage.expiration",
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -7,16 +7,17 @@
 #include "nsChannelClassifier.h"
 
 #include "mozIThirdPartyUtil.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsIAddonPolicyService.h"
 #include "nsICacheEntry.h"
 #include "nsICachingChannel.h"
+#include "nsICookieService.h"
 #include "nsIChannel.h"
 #include "nsIClassOfService.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIIOService.h"
 #include "nsIParentChannel.h"
@@ -946,17 +947,18 @@ TrackingURICallback::OnClassifyComplete(
                                         const nsACString& aFullHash)
 {
   MOZ_ASSERT(aErrorCode == NS_OK);
 
   const bool shouldEnableTrackingProtection =
     mChannelClassifier->ShouldEnableTrackingProtection();
   const bool shouldEnableTrackingAnnotation =
     mChannelClassifier->ShouldEnableTrackingAnnotation() ||
-    StaticPrefs::privacy_restrict3rdpartystorage_enabled();
+    StaticPrefs::network_cookie_cookieBehavior() ==
+      nsICookieService::BEHAVIOR_REJECT_TRACKER;
   MOZ_ASSERT(shouldEnableTrackingProtection || shouldEnableTrackingAnnotation);
 
   LOG(("TrackingURICallback[%p]:OnClassifyComplete "
        "shouldEnableTrackingProtection=%d, shouldEnableTrackingAnnnotation=%d",
        mChannelClassifier.get(), shouldEnableTrackingProtection,
        shouldEnableTrackingAnnotation));
 
   // Figure out whether we are receiving the results of a blacklist or
@@ -1194,17 +1196,18 @@ TrackingURICallback::OnTrackerFound(nsre
     mChannelClassifier->SetBlockedContent(channel, aErrorCode,
                                           mList, mProvider, mFullHash);
     LOG(("TrackingURICallback[%p]::OnTrackerFound, cancelling channel[%p]",
          mChannelClassifier.get(), channel.get()));
     channel->Cancel(aErrorCode);
   } else {
     MOZ_ASSERT(aErrorCode == NS_ERROR_TRACKING_ANNOTATION_URI);
     MOZ_ASSERT(mChannelClassifier->ShouldEnableTrackingAnnotation() ||
-               StaticPrefs::privacy_restrict3rdpartystorage_enabled());
+               StaticPrefs::network_cookie_cookieBehavior() ==
+                 nsICookieService::BEHAVIOR_REJECT_TRACKER);
 
     LOG(("TrackingURICallback[%p]::OnTrackerFound, annotating channel[%p]",
          mChannelClassifier.get(), channel.get()));
 
     // Even with TP disabled, we still want to show the user that there
     // are unblocked trackers on the site, so notify the UI that we loaded
     // tracking content. UI code can treat this notification differently
     // depending on whether TP is enabled or disabled.
@@ -1416,17 +1419,18 @@ nsChannelClassifier::CheckIsTrackerWithL
 
   nsCOMPtr<nsIURIClassifier> uriClassifier =
     do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   const bool shouldEnableTrackingProtection = ShouldEnableTrackingProtection();
-  const bool shouldEnableTrackingAnnotation = ShouldEnableTrackingAnnotation() || StaticPrefs::privacy_restrict3rdpartystorage_enabled();
+  const bool shouldEnableTrackingAnnotation = ShouldEnableTrackingAnnotation() ||
+    StaticPrefs::network_cookie_cookieBehavior() == nsICookieService::BEHAVIOR_REJECT_TRACKER;
   if (!shouldEnableTrackingProtection && !shouldEnableTrackingAnnotation) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIURI> uri;
   rv = mChannel->GetURI(getter_AddRefs(uri));
   if (NS_FAILED(rv) || !uri) {
     return rv;
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2405,17 +2405,17 @@ nsCookieService::RunInTransaction(nsICoo
  * pref observer impl
  ******************************************************************************/
 
 void
 nsCookieService::PrefChanged(nsIPrefBranch *aPrefBranch)
 {
   int32_t val;
   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookieBehavior, &val)))
-    mCookieBehavior = (uint8_t) LIMIT(val, 0, 3, 0);
+    mCookieBehavior = (uint8_t) LIMIT(val, 0, nsICookieService::BEHAVIOR_LAST, 0);
 
   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxNumberOfCookies, &val)))
     mMaxNumberOfCookies = (uint16_t) LIMIT(val, 1, 0xFFFF, kMaxNumberOfCookies);
 
   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val)))
     mMaxCookiesPerHost = (uint16_t) LIMIT(val, 1, 0xFFFF, kMaxCookiesPerHost);
 
   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiePurgeAge, &val))) {
@@ -4194,24 +4194,16 @@ nsCookieService::CheckPrefs(nsICookiePer
   nsCOMPtr<nsIPrincipal> principal =
       BasePrincipal::CreateCodebasePrincipal(aHostURI, aOriginAttrs);
 
   if (!principal) {
     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "non-codebase principals cannot get/set cookies");
     return STATUS_REJECTED_WITH_ERROR;
   }
 
-  // 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 &&
-      StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
-      return STATUS_REJECTED;
-  }
-
   // check the permission list first; if we find an entry, it overrides
   // default prefs. see bug 184059.
   if (aPermissionService) {
     nsCookieAccess access;
     // Not passing an nsIChannel here is probably OK; our implementation
     // doesn't do anything with it anyway.
     rv = aPermissionService->CanAccess(principal, &access);
 
@@ -4245,16 +4237,25 @@ nsCookieService::CheckPrefs(nsICookiePer
                             "for this site");
           return STATUS_REJECTED;
         }
         return STATUS_ACCEPTED;
       }
     }
   }
 
+  // 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 &&
+      aCookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER) {
+      COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are disabled in trackers");
+      return STATUS_REJECTED;
+  }
+
   // check default prefs
   if (aCookieBehavior == nsICookieService::BEHAVIOR_REJECT) {
     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are disabled");
     return STATUS_REJECTED;
   }
 
   // check if cookie is foreign
   if (aIsForeign) {
@@ -4266,17 +4267,19 @@ nsCookieService::CheckPrefs(nsICookiePer
     if (aCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
       if (aNumOfCookies == 0) {
         COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "context is third party");
         return STATUS_REJECTED;
       }
     }
 
     MOZ_ASSERT(aCookieBehavior == nsICookieService::BEHAVIOR_ACCEPT ||
-               aCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN);
+               aCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
+               // But with permission granted.
+               aCookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER);
 
     if (aThirdPartySession)
       return STATUS_ACCEPT_SESSION;
 
     if (aThirdPartyNonsecureSession) {
       bool isHTTPS = false;
       aHostURI->SchemeIs("https", &isHTTPS);
       if (!isHTTPS)
--- a/netwerk/cookie/nsICookiePermission.idl
+++ b/netwerk/cookie/nsICookiePermission.idl
@@ -47,17 +47,17 @@ interface nsICookiePermission : nsISuppo
    *        the new cookie access for the URI.
    */
   void setAccess(in nsIURI         aURI,
                  in nsCookieAccess aAccess);
 
   /**
    * canAccess
    *
-   * this method is called to test whether or not the given URI/channel may
+   * this method is called to test whether or not the given principal may
    * access the cookie database, either to set or get cookies.
    *
    * @param aPrincipal
    *        the principal trying to access cookies.
    *
    * @return one of the following nsCookieAccess values:
    *         ACCESS_DEFAULT, ACCESS_ALLOW, ACCESS_DENY, or
    *         ACCESS_ALLOW_FIRST_PARTY_ONLY
--- a/netwerk/cookie/nsICookieService.idl
+++ b/netwerk/cookie/nsICookieService.idl
@@ -84,16 +84,19 @@ interface nsICookieService : nsISupports
   /*
    * Possible values for the "network.cookie.cookieBehavior" preference.
    */
   const uint32_t BEHAVIOR_ACCEPT         = 0; // allow all cookies
   const uint32_t BEHAVIOR_REJECT_FOREIGN = 1; // reject all third-party cookies
   const uint32_t BEHAVIOR_REJECT         = 2; // reject all cookies
   const uint32_t BEHAVIOR_LIMIT_FOREIGN  = 3; // reject third-party cookies unless the
                                               // eTLD already has at least one cookie
+  const uint32_t BEHAVIOR_REJECT_TRACKER = 4; // reject trackers
+  // When adding a new cookie behavior, please increase this value!
+  const uint32_t BEHAVIOR_LAST           = 4;
 
   /*
    * Possible values for the "network.cookie.lifetimePolicy" preference.
    */
   const uint32_t ACCEPT_NORMALLY   = 0; // accept normally
   // Value = 1 is considered the same as 0 (See Bug 606655).
   const uint32_t ACCEPT_SESSION    = 2; // downgrade to session
   // Value = 3 is considered the same as 0
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -17,16 +17,17 @@
 #include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsIApplicationCacheService.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsICacheStorageService.h"
 #include "nsICacheStorage.h"
 #include "nsICacheEntry.h"
 #include "nsICaptivePortalService.h"
+#include "nsICookieService.h"
 #include "nsICryptoHash.h"
 #include "nsINetworkInterceptController.h"
 #include "nsINSSErrorsService.h"
 #include "nsISecurityReporter.h"
 #include "nsIStringBundle.h"
 #include "nsIStreamListenerTee.h"
 #include "nsISeekableStream.h"
 #include "nsILoadGroupChild.h"
@@ -50,16 +51,17 @@
 #include "mozilla/TimeStamp.h"
 #include "nsError.h"
 #include "nsPrintfCString.h"
 #include "nsAlgorithm.h"
 #include "nsQueryObject.h"
 #include "nsThreadUtils.h"
 #include "GeckoProfiler.h"
 #include "nsIConsoleService.h"
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsISSLSocketControl.h"
 #include "sslt.h"
 #include "nsContentUtils.h"
 #include "nsContentSecurityManager.h"
@@ -98,17 +100,16 @@
 #include "nsIDocument.h"
 #include "nsICompressConvStats.h"
 #include "nsCORSListenerProxy.h"
 #include "nsISocketProvider.h"
 #include "mozilla/extensions/StreamFilterParent.h"
 #include "mozilla/net/Predictor.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/NullPrincipal.h"
-#include "mozilla/StaticPrefs.h"
 #include "CacheControlParser.h"
 #include "nsMixedContentBlocker.h"
 #include "CacheStorageService.h"
 #include "HttpChannelParent.h"
 #include "InterceptedHttpChannel.h"
 #include "nsIBufferedStreams.h"
 #include "nsIFileStreams.h"
 #include "nsIMIMEInputStream.h"
@@ -3822,38 +3823,28 @@ nsHttpChannel::OpenCacheEntryInternal(bo
 
     if (mPostID) {
         extension.Append(nsPrintfCString("%d", mPostID));
     }
     if (mTRR) {
         extension.Append("TRR");
     }
 
-    if (StaticPrefs::privacy_restrict3rdpartystorage_enabled() &&
-        mIsTrackingResource) {
-        nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-            services::GetThirdPartyUtil();
-        if (thirdPartyUtil) {
-            bool thirdParty = false;
-            Unused << thirdPartyUtil->IsThirdPartyChannel(this,
-                                                          nullptr,
-                                                          &thirdParty);
-            if (thirdParty) {
-                nsCOMPtr<nsIURI> topWindowURI;
-                rv = GetTopWindowURI(getter_AddRefs(topWindowURI));
-                NS_ENSURE_SUCCESS(rv, rv);
-
-                nsAutoString topWindowOrigin;
-                rv = nsContentUtils::GetUTFOrigin(topWindowURI, topWindowOrigin);
-                NS_ENSURE_SUCCESS(rv, rv);
-
-                extension.Append("-trackerFor:");
-                extension.Append(NS_ConvertUTF16toUTF8(topWindowOrigin));
-            }
-        }
+    if (!AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(this, mURI)) {
+        nsCOMPtr<nsIURI> topWindowURI;
+        rv = GetTopWindowURI(getter_AddRefs(topWindowURI));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsAutoString topWindowOrigin;
+        rv = nsContentUtils::GetUTFOrigin(topWindowURI ? topWindowURI : mURI,
+                                          topWindowOrigin);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        extension.Append("-unique:");
+        extension.Append(NS_ConvertUTF16toUTF8(topWindowOrigin));
     }
 
     mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY;
     mCacheQueueSizeWhenOpen = CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);
 
     if (sRCWNEnabled && maybeRCWN && !mApplicationCacheForWrite) {
         bool hasAltData = false;
         uint32_t sizeInKb = 0;
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -474,17 +474,17 @@ interface nsIHttpChannel : nsIChannel
      */
     [must_use] attribute uint64_t topLevelContentWindowId;
 
     /**
      * Returns true if the channel has loaded a resource that is on the tracking
      * protection list.  This is only available if the
      * privacy.trackingprotection.annotate_channels pref is set and its value
      * should only be relied on after the channel has established a connection.
-     * Note that, if privacy.restrict3rdpartystorage.enabled pref is set, also
+     * Note that, if the privacy.trackingprotection.annotate_channels pref is set, also
      * top-level channels could be marked as tracking resource.
      */
     [infallible] readonly attribute boolean isTrackingResource;
 
     /**
      * This method is used in order to override the tracking status of an HTTP
      * channel.  This should only be called by Gecko under certain circumstances
      * when Gecko can guarantee that the channel classifier service will not be
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -8,20 +8,23 @@
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindowInner.h"
+#include "nsICookiePermission.h"
+#include "nsICookieService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
+#include "nsScriptSecurityManager.h"
 #include "prtime.h"
 
 #define ANTITRACKING_PERM_KEY "3rdPartyStorage"
 
 using namespace mozilla;
 using mozilla::dom::ContentChild;
 
 namespace {
@@ -66,25 +69,63 @@ CreatePermissionKey(const nsCString& aTr
     return;
   }
 
   aPermissionKey = nsPrintfCString(ANTITRACKING_PERM_KEY "^%s^%s",
                                    aTrackingOrigin.get(),
                                    aGrantedOrigin.get());
 }
 
+// This internal method returns ACCESS_DENY if the access is denied,
+// ACCESS_DEFAULT if unknown, some other access code if granted.
+nsCookieAccess
+CheckCookiePermissionForPrincipal(nsIPrincipal* aPrincipal)
+{
+  nsCookieAccess access = nsICookiePermission::ACCESS_DEFAULT;
+  if (!aPrincipal->GetIsCodebasePrincipal()) {
+    return access;
+  }
+
+  nsCOMPtr<nsICookiePermission> cps =
+    do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
+  if (NS_WARN_IF(!cps)) {
+    return access;
+  }
+
+  nsresult rv = cps->CanAccess(aPrincipal, &access);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return access;
+  }
+
+  // If we have a custom cookie permission, let's use it.
+  return access;
+}
+
+int32_t
+CookiesBehavior(nsIPrincipal* aPrincipal)
+{
+  // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior
+  // (See Bug 1406675 for rationale).
+  if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
+    return nsICookieService::BEHAVIOR_ACCEPT;
+  }
+
+  return StaticPrefs::network_cookie_cookieBehavior();
+}
+
 } // anonymous
 
 /* static */ RefPtr<AntiTrackingCommon::StorageAccessGrantPromise>
 AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin,
                                                          nsPIDOMWindowInner* aParentWindow)
 {
   MOZ_ASSERT(aParentWindow);
 
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
+  if (StaticPrefs::network_cookie_cookieBehavior() !=
+        nsICookieService::BEHAVIOR_REJECT_TRACKER) {
     return StorageAccessGrantPromise::CreateAndResolve(true, __func__);
   }
 
   nsCOMPtr<nsIPrincipal> topLevelStoragePrincipal;
   nsAutoCString trackingOrigin;
 
   nsGlobalWindowInner* parentWindow = nsGlobalWindowInner::Cast(aParentWindow);
   nsGlobalWindowOuter* outerParentWindow =
@@ -170,35 +211,70 @@ AntiTrackingCommon::SaveFirstPartyStorag
   nsresult rv = pm->AddFromPrincipal(aParentPrincipal, type.get(),
                                      nsIPermissionManager::ALLOW_ACTION,
                                      nsIPermissionManager::EXPIRE_TIME, when);
   Unused << NS_WARN_IF(NS_FAILED(rv));
   aResolver(NS_SUCCEEDED(rv));
 }
 
 bool
-AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* a3rdPartyTrackingWindow,
+AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* aWindow,
                                                         nsIURI* aURI)
 {
-  MOZ_ASSERT(a3rdPartyTrackingWindow);
+  MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aURI);
 
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
+  nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow);
+  nsIPrincipal* toplevelPrincipal = innerWindow->GetTopLevelPrincipal();
+  if (!toplevelPrincipal) {
+    // We are already the top-level principal. Let's use the window's principal.
+    toplevelPrincipal = innerWindow->GetPrincipal();
+  }
+
+  if (!toplevelPrincipal) {
+    // This should not be possible, right?
+    return false;
+  }
+
+  nsCookieAccess access = CheckCookiePermissionForPrincipal(toplevelPrincipal);
+  if (access != nsICookiePermission::ACCESS_DEFAULT) {
+    return access != nsICookiePermission::ACCESS_DENY;
+  }
+
+  int32_t behavior = CookiesBehavior(toplevelPrincipal);
+  if (behavior == nsICookieService::BEHAVIOR_ACCEPT) {
     return true;
   }
 
-  if (!nsContentUtils::IsThirdPartyWindowOrChannel(a3rdPartyTrackingWindow,
-                                                   nullptr, aURI) ||
-      !nsContentUtils::IsTrackingResourceWindow(a3rdPartyTrackingWindow)) {
+  if (behavior == nsICookieService::BEHAVIOR_REJECT) {
+    return false;
+  }
+
+  // Let's check if this is a 3rd party context.
+  if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, nullptr, aURI)) {
+    return true;
+  }
+
+  if (behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
+      behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
+    // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
+    // simply rejecting the request to use the storage. In the future, if we
+    // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
+    // for non-cookie storage types, this may change.
+    return false;
+  }
+
+  MOZ_ASSERT(behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER);
+  if (!nsContentUtils::IsTrackingResourceWindow(aWindow)) {
     return true;
   }
 
   nsCOMPtr<nsIPrincipal> parentPrincipal;
   nsAutoCString trackingOrigin;
-  if (!GetParentPrincipalAndTrackingOrigin(nsGlobalWindowInner::Cast(a3rdPartyTrackingWindow),
+  if (!GetParentPrincipalAndTrackingOrigin(nsGlobalWindowInner::Cast(aWindow),
                                            getter_AddRefs(parentPrincipal),
                                            trackingOrigin)) {
     return false;
   }
 
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -223,40 +299,124 @@ AntiTrackingCommon::IsFirstPartyStorageA
 }
 
 bool
 AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* aChannel,
                                                         nsIURI* aURI)
 {
   MOZ_ASSERT(aURI);
   MOZ_ASSERT(aChannel);
-  MOZ_ASSERT(aChannel->GetIsTrackingResource());
 
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   if (!loadInfo) {
     return true;
   }
 
+  // We need to find the correct principal to check the cookie permission. For
+  // third-party contexts, we want to check if the top-level window has a custom
+  // cookie permission.
+  nsIPrincipal* toplevelPrincipal = loadInfo->TopLevelPrincipal();
+
+  // If this is already the top-level window, we should use the loading
+  // principal.
+  if (!toplevelPrincipal) {
+    toplevelPrincipal = loadInfo->LoadingPrincipal();
+  }
+
+  nsCOMPtr<nsIPrincipal> channelPrincipal;
+  nsIScriptSecurityManager* ssm = nsScriptSecurityManager::GetScriptSecurityManager();
+  nsresult rv = ssm->GetChannelResultPrincipal(aChannel,
+                                               getter_AddRefs(channelPrincipal));
+
+  // If we don't have a loading principal and this is a document channel, we are
+  // a top-level window!
+  if (!toplevelPrincipal) {
+    bool isDocument = false;
+    nsresult rv2 = aChannel->GetIsMainDocumentChannel(&isDocument);
+    if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2) && isDocument) {
+      toplevelPrincipal = channelPrincipal;
+    }
+  }
+
+  // Let's use the triggering principal then.
+  if (!toplevelPrincipal) {
+    toplevelPrincipal = loadInfo->TriggeringPrincipal();
+  }
+
+  if (NS_WARN_IF(!toplevelPrincipal)) {
+    return false;
+  }
+
+  nsCookieAccess access = CheckCookiePermissionForPrincipal(toplevelPrincipal);
+  if (access != nsICookiePermission::ACCESS_DEFAULT) {
+    return access != nsICookiePermission::ACCESS_DENY;
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv) || !channelPrincipal)) {
+    return false;
+  }
+
+  int32_t behavior = CookiesBehavior(channelPrincipal);
+  if (behavior == nsICookieService::BEHAVIOR_ACCEPT) {
+    return true;
+  }
+
+  if (behavior == nsICookieService::BEHAVIOR_REJECT) {
+    return false;
+  }
+
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
+  if (!thirdPartyUtil) {
+    return true;
+  }
+
+  bool thirdParty = false;
+  Unused << thirdPartyUtil->IsThirdPartyChannel(aChannel,
+                                                nullptr,
+                                                &thirdParty);
+  // Grant if it's not a 3rd party.
+  if (!thirdParty) {
+    return true;
+  }
+
+  if (behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
+      behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
+    // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
+    // simply rejecting the request to use the storage. In the future, if we
+    // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
+    // for non-cookie storage types, this may change.
+    return false;
+  }
+
+  MOZ_ASSERT(behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER);
+
   nsIPrincipal* parentPrincipal = loadInfo->TopLevelStorageAreaPrincipal();
   if (!parentPrincipal) {
     // parentPrincipal can be null if the parent window is not the top-level
     // window.
     if (loadInfo->TopLevelPrincipal()) {
       return false;
     }
 
     parentPrincipal = loadInfo->TriggeringPrincipal();
     if (NS_WARN_IF(!parentPrincipal)) {
       // Why we are here?!?
       return true;
     }
   }
 
+  // Not a tracker.
+  if (!aChannel->GetIsTrackingResource()) {
+    return true;
+  }
+
+  // Let's see if we have to grant the access for this particular channel.
+
   nsCOMPtr<nsIURI> trackingURI;
-  nsresult rv = aChannel->GetURI(getter_AddRefs(trackingURI));
+  rv = aChannel->GetURI(getter_AddRefs(trackingURI));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return true;
   }
 
   nsAutoString trackingOrigin;
   rv = nsContentUtils::GetUTFOrigin(trackingURI, trackingOrigin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
@@ -281,39 +441,59 @@ AntiTrackingCommon::IsFirstPartyStorageA
   rv = pm->TestPermissionFromPrincipal(parentPrincipal, type.get(), &result);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   return result == nsIPermissionManager::ALLOW_ACTION;
 }
 
+bool
+AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal)
+{
+  MOZ_ASSERT(aPrincipal);
+
+  nsCookieAccess access = CheckCookiePermissionForPrincipal(aPrincipal);
+  if (access != nsICookiePermission::ACCESS_DEFAULT) {
+    return access != nsICookiePermission::ACCESS_DENY;
+  }
+
+  int32_t behavior = CookiesBehavior(aPrincipal);
+  return behavior != nsICookieService::BEHAVIOR_REJECT;
+}
+
 /* static */ bool
 AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* aFirstPartyWindow,
                                                              nsIURI* aURI)
 {
   MOZ_ASSERT(aFirstPartyWindow);
   MOZ_ASSERT(!nsContentUtils::IsTrackingResourceWindow(aFirstPartyWindow));
   MOZ_ASSERT(aURI);
 
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
+  if (StaticPrefs::network_cookie_cookieBehavior() !=
+        nsICookieService::BEHAVIOR_REJECT_TRACKER) {
     return true;
   }
 
   if (!nsContentUtils::IsThirdPartyWindowOrChannel(aFirstPartyWindow,
                                                    nullptr, aURI)) {
     return true;
   }
 
   nsCOMPtr<nsIPrincipal> parentPrincipal =
     nsGlobalWindowInner::Cast(aFirstPartyWindow)->GetPrincipal();
   if (NS_WARN_IF(!parentPrincipal)) {
     return false;
   }
 
+  nsCookieAccess access = CheckCookiePermissionForPrincipal(parentPrincipal);
+  if (access != nsICookiePermission::ACCESS_DEFAULT) {
+    return access != nsICookiePermission::ACCESS_DENY;
+  }
+
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   NS_ConvertUTF16toUTF8 utf8Origin(origin);
 
--- a/toolkit/components/antitracking/AntiTrackingCommon.h
+++ b/toolkit/components/antitracking/AntiTrackingCommon.h
@@ -41,22 +41,25 @@ public:
   // For first party window, it's impossible to know if the aURI is a tracking
   // resource synchronously, so here we return the best guest: if we are sure
   // that the permission is granted for the origin of aURI, this method returns
   // true, otherwise false.
   static bool
   MaybeIsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* aFirstPartyWindow,
                                            nsIURI* aURI);
 
-  // This can be called only if the a3rdPartyTrackingChannel is _really_ a 3rd
-  // party context and marked as tracking resource.
   // It returns true if the URI has access to the first party storage.
+  // aChannel can be a 3rd party channel, or not.
   static bool
-  IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* a3rdPartyTrackingChannel,
-                                      nsIURI* aURI);
+  IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* aChannel, nsIURI* aURI);
+
+  // This method checks if the principal has the permission to access to the
+  // first party storage.
+  static bool
+  IsFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal);
 
   // Grant the permission for aOrigin to have access to the first party storage.
   // This method can handle 2 different scenarios:
   // - aParentWindow is a 3rd party context, it opens an aOrigin window and the
   //   user interacts with it. We want to grant the permission at the
   //   combination: top-level + aParentWindow + aOrigin.
   //   Ex: example.net loads an iframe tracker.com, which opens a popup
   //   tracker.prg and the user interacts with it. tracker.org is allowed if
--- a/toolkit/components/antitracking/test/browser/browser_script.js
+++ b/toolkit/components/antitracking/test/browser/browser_script.js
@@ -1,16 +1,16 @@
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 add_task(async function() {
   info("Starting subResources test");
 
   await SpecialPowers.flushPrefEnv();
   await SpecialPowers.pushPrefEnv({"set": [
-    ["privacy.restrict3rdpartystorage.enabled", true],
+    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
     ["privacy.trackingprotection.enabled", false],
     ["privacy.trackingprotection.pbmode.enabled", false],
     ["privacy.trackingprotection.annotate_channels", true],
   ]});
 
   await UrlClassifierTestUtils.addTestTrackers();
 
   info("Creating a new tab");
--- a/toolkit/components/antitracking/test/browser/browser_subResources.js
+++ b/toolkit/components/antitracking/test/browser/browser_subResources.js
@@ -1,16 +1,16 @@
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 add_task(async function() {
   info("Starting subResources test");
 
   await SpecialPowers.flushPrefEnv();
   await SpecialPowers.pushPrefEnv({"set": [
-    ["privacy.restrict3rdpartystorage.enabled", true],
+    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
     ["privacy.trackingprotection.enabled", false],
     ["privacy.trackingprotection.pbmode.enabled", false],
     ["privacy.trackingprotection.annotate_channels", true],
   ]});
 
   await UrlClassifierTestUtils.addTestTrackers();
 
   info("Creating a new tab");
--- a/toolkit/components/antitracking/test/browser/head.js
+++ b/toolkit/components/antitracking/test/browser/head.js
@@ -34,17 +34,17 @@ this.AntiTracking = {
         this._createCleanupTask(cleanupFunction);
       }
     }
   },
 
   async _setupTest(blocking, extraPrefs) {
     await SpecialPowers.flushPrefEnv();
     await SpecialPowers.pushPrefEnv({"set": [
-      ["privacy.restrict3rdpartystorage.enabled", blocking],
+      ["network.cookie.cookieBehavior", blocking ? Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER : Ci.nsICookieService.BEHAVIOR_ACCEPT],
       ["privacy.trackingprotection.enabled", false],
       ["privacy.trackingprotection.pbmode.enabled", false],
       ["privacy.trackingprotection.annotate_channels", blocking],
     ]});
 
     if (extraPrefs && Array.isArray(extraPrefs) && extraPrefs.length) {
       await SpecialPowers.pushPrefEnv({"set": extraPrefs });
     }