Bug 1525245 - Stabilize cookiePolicy/cookiePermission for live documents - part 1 - information stored into loadInfo, r=Ehsan,ckerschb
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 08 Mar 2019 09:00:06 +0000
changeset 520980 ccb173c2766838a1ef52bbf590947565398928fb
parent 520979 615a12a9276839868bef0858f3fb535a04106295
child 520981 c08a35a9c0d6482a7dd85f00ee0d3b1b685c9a84
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersEhsan, ckerschb
bugs1525245
milestone67.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 1525245 - Stabilize cookiePolicy/cookiePermission for live documents - part 1 - information stored into loadInfo, r=Ehsan,ckerschb Differential Revision: https://phabricator.services.mozilla.com/D18949
dom/base/Document.cpp
dom/base/Document.h
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
ipc/glue/BackgroundUtils.cpp
modules/libpref/init/StaticPrefList.h
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/cookie/CookieSettings.cpp
netwerk/cookie/CookieSettings.h
netwerk/cookie/moz.build
netwerk/cookie/nsICookieSettings.idl
netwerk/ipc/NeckoChannelParams.ipdlh
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -73,16 +73,17 @@
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
 #include "mozilla/dom/StyleSheetList.h"
 #include "mozilla/dom/SVGUseElement.h"
+#include "mozilla/net/CookieSettings.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/CDATASection.h"
 #include "mozilla/dom/ProcessingInstruction.h"
 #include "nsDOMString.h"
 #include "nsNodeUtils.h"
 #include "nsLayoutUtils.h"  // for GetFrameForPoint
 #include "nsIFrame.h"
 #include "nsITabChild.h"
@@ -123,16 +124,17 @@
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "ExpandedPrincipal.h"
 #include "mozilla/NullPrincipal.h"
 
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsFocusManager.h"
+#include "nsICookiePermission.h"
 #include "nsICookieService.h"
 
 #include "nsBidiUtils.h"
 
 #include "nsContentCreatorFunctions.h"
 
 #include "nsIScriptContext.h"
 #include "nsBindingManager.h"
@@ -2554,16 +2556,28 @@ nsresult Document::StartDocumentLoad(con
   // the CSP defines frame-ancestors.
   if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
     MOZ_LOG(gCspPRLog, LogLevel::Debug,
             ("XFO doesn't like frame's ancestry, not loading."));
     // stop!  ERROR page!
     aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
   }
 
+  // Let's take the CookieSettings from the loadInfo or from the parent
+  // document.
+  if (loadInfo) {
+    rv = loadInfo->GetCookieSettings(getter_AddRefs(mCookieSettings));
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    nsCOMPtr<Document> parentDocument = GetParentDocument();
+    if (parentDocument) {
+      mCookieSettings = parentDocument->CookieSettings();
+    }
+  }
+
   return NS_OK;
 }
 
 void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
   for (uint32_t i = 0; i < aMessages.Length(); ++i) {
     nsAutoString messageTag;
     aMessages[i]->GetTag(messageTag);
 
@@ -12613,10 +12627,20 @@ void Document::RecomputeLanguageFromChar
   if (language == mLanguageFromCharset) {
     return;
   }
 
   ResetLangPrefs();
   mLanguageFromCharset = language.forget();
 }
 
+nsICookieSettings* Document::CookieSettings() {
+  // If we are here, this is probably a javascript: URL document. In any case,
+  // we must have a nsCookieSettings. Let's create it.
+  if (!mCookieSettings) {
+    mCookieSettings = net::CookieSettings::Create();
+  }
+
+  return mCookieSettings;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -7,17 +7,18 @@
 #define mozilla_dom_Document_h___
 
 #include "mozilla/FlushType.h"  // for enum
 #include "mozilla/Pair.h"       // for Pair
 #include "nsAutoPtr.h"          // for member
 #include "nsCOMArray.h"         // for member
 #include "nsCompatibility.h"    // for member
 #include "nsCOMPtr.h"           // for member
-#include "nsGkAtoms.h"          // for static class members
+#include "nsICookieSettings.h"
+#include "nsGkAtoms.h"  // for static class members
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIContentViewer.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILoadContext.h"
 #include "nsILoadGroup.h"  // for member (in nsCOMPtr)
 #include "nsINode.h"       // for base class
@@ -1483,16 +1484,19 @@ class Document : public nsINode,
   // canceled by the URL classifier (Safebrowsing).
   //
   already_AddRefed<nsSimpleContentList> BlockedNodesByClassifier() const;
 
   // Helper method that returns true if the document has storage-access sandbox
   // flag.
   bool StorageAccessSandboxed() const;
 
+  // Returns the cookie settings for this and sub contexts.
+  nsICookieSettings* CookieSettings();
+
   // Increments the document generation.
   inline void Changed() { ++mGeneration; }
 
   // Returns the current generation.
   inline int32_t GetGeneration() const { return mGeneration; }
 
   // Adds cached sizes values to aSizes if there's any
   // cached value and if the document generation hasn't
@@ -4685,16 +4689,18 @@ class Document : public nsINode,
   // :-moz-lwtheme-brighttext and :-moz-lwtheme-darktext
   DocumentTheme mDocLWTheme;
 
   // Pres shell resolution saved before entering fullscreen mode.
   float mSavedResolution;
 
   bool mPendingInitialTranslation;
 
+  nsCOMPtr<nsICookieSettings> mCookieSettings;
+
   // Document generation. Gets incremented everytime it changes.
   int32_t mGeneration;
 
   // Cached TabSizes values for the document.
   int32_t mCachedTabSizeGeneration;
   nsTabSizes mCachedTabSizes;
 
  public:
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8221,41 +8221,40 @@ nsContentUtils::StorageAccess nsContentU
 nsContentUtils::StorageAccess nsContentUtils::StorageAllowedForServiceWorker(
     nsIPrincipal* aPrincipal) {
   uint32_t rejectedReason = 0;
   return InternalStorageAllowedCheck(aPrincipal, nullptr, nullptr, nullptr,
                                      rejectedReason);
 }
 
 // static, private
-void nsContentUtils::GetCookieLifetimePolicyForPrincipal(
-    nsIPrincipal* aPrincipal, uint32_t* aLifetimePolicy) {
+void nsContentUtils::GetCookieLifetimePolicyFromCookieSettings(
+    nsICookieSettings* aCookieSettings, nsIPrincipal* aPrincipal,
+    uint32_t* aLifetimePolicy) {
   *aLifetimePolicy = sCookiesLifetimePolicy;
 
-  // 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, NS_LITERAL_CSTRING("cookie"), &perm);
-  switch (perm) {
-    case nsICookiePermission::ACCESS_ALLOW:
-      *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
-      break;
-    case nsICookiePermission::ACCESS_DENY:
-      *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
-      break;
-    case nsICookiePermission::ACCESS_SESSION:
-      *aLifetimePolicy = nsICookieService::ACCEPT_SESSION;
-      break;
+  if (aCookieSettings) {
+    uint32_t cookiePermission = 0;
+    nsresult rv =
+        aCookieSettings->CookiePermission(aPrincipal, &cookiePermission);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    switch (cookiePermission) {
+      case nsICookiePermission::ACCESS_ALLOW:
+        *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
+        break;
+      case nsICookiePermission::ACCESS_DENY:
+        *aLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
+        break;
+      case nsICookiePermission::ACCESS_SESSION:
+        *aLifetimePolicy = nsICookieService::ACCEPT_SESSION;
+        break;
+    }
   }
 }
 
 // static public
 bool nsContentUtils::IsThirdPartyWindowOrChannel(nsPIDOMWindowInner* aWindow,
                                                  nsIChannel* aChannel,
                                                  nsIURI* aURI) {
   MOZ_ASSERT(!aWindow || !aChannel,
@@ -8416,16 +8415,17 @@ bool nsContentUtils::StorageDisabledByAn
 nsContentUtils::StorageAccess nsContentUtils::InternalStorageAllowedCheck(
     nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow, nsIURI* aURI,
     nsIChannel* aChannel, uint32_t& aRejectedReason) {
   MOZ_ASSERT(aPrincipal);
 
   aRejectedReason = 0;
 
   StorageAccess access = StorageAccess::eAllow;
+  nsCOMPtr<nsICookieSettings> cookieSettings;
 
   // 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 (aWindow) {
@@ -8434,28 +8434,38 @@ nsContentUtils::StorageAccess nsContentU
     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;
     }
+
+    if (document) {
+      cookieSettings = document->CookieSettings();
+    }
+  }
+
+  if (aChannel) {
+    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+    loadInfo->GetCookieSettings(getter_AddRefs(cookieSettings));
   }
 
   uint32_t lifetimePolicy;
 
   // 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) {
     lifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
   } else {
-    GetCookieLifetimePolicyForPrincipal(aPrincipal, &lifetimePolicy);
+    GetCookieLifetimePolicyFromCookieSettings(cookieSettings, 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);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -3378,23 +3378,24 @@ class nsContentUtils {
       const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
       bool aGrantAllValidValue = false);
 
   static bool CallOnAllRemoteChildren(
       mozilla::dom::MessageBroadcaster* aManager,
       CallOnRemoteChildFunction aCallback, void* aArg);
 
   /**
-   * Gets the current cookie lifetime policy for a given principal by checking
-   * with preferences and the permission manager.
+   * Gets the cookie lifetime policy for a given cookieSettings and a given
+   * principal by checking the permission value.
    *
    * Used in the implementation of InternalStorageAllowedCheck.
    */
-  static void GetCookieLifetimePolicyForPrincipal(nsIPrincipal* aPrincipal,
-                                                  uint32_t* aLifetimePolicy);
+  static void GetCookieLifetimePolicyFromCookieSettings(
+      nsICookieSettings* aCookieSettings, 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/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -7,16 +7,17 @@
 #include "BackgroundUtils.h"
 
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ContentPrincipal.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/URIUtils.h"
+#include "mozilla/net/CookieSettings.h"
 #include "mozilla/net/NeckoChannelParams.h"
 #include "ExpandedPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "mozilla/LoadInfo.h"
 #include "nsContentUtils.h"
 #include "nsString.h"
@@ -497,16 +498,24 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
   const Maybe<ServiceWorkerDescriptor>& controller = aLoadInfo->GetController();
   if (controller.isSome()) {
     ipcController = controller.ref().ToIPC();
   }
 
   nsAutoString cspNonce;
   Unused << NS_WARN_IF(NS_FAILED(aLoadInfo->GetCspNonce(cspNonce)));
 
+  nsCOMPtr<nsICookieSettings> cookieSettings;
+  rv = aLoadInfo->GetCookieSettings(getter_AddRefs(cookieSettings));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  CookieSettingsArgs cookieSettingsArgs;
+  static_cast<CookieSettings*>(cookieSettings.get())
+      ->Serialize(cookieSettingsArgs);
+
   *aOptionalLoadInfoArgs = Some(LoadInfoArgs(
       loadingPrincipalInfo, triggeringPrincipalInfo, principalToInheritInfo,
       sandboxedLoadingPrincipalInfo, topLevelPrincipalInfo,
       topLevelStorageAreaPrincipalInfo, optionalResultPrincipalURI,
       aLoadInfo->GetSecurityFlags(), aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetBrowserUpgradeInsecureRequests(),
@@ -527,17 +536,17 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
       ancestorPrincipals, aLoadInfo->AncestorOuterWindowIDs(), ipcClientInfo,
       ipcReservedClientInfo, ipcInitialClientInfo, ipcController,
       aLoadInfo->CorsUnsafeHeaders(), aLoadInfo->GetForcePreflight(),
       aLoadInfo->GetIsPreflight(), aLoadInfo->GetLoadTriggeredFromExternal(),
       aLoadInfo->GetServiceWorkerTaintingSynthesized(),
       aLoadInfo->GetDocumentHasUserInteracted(),
       aLoadInfo->GetDocumentHasLoaded(), cspNonce,
       aLoadInfo->GetIsFromProcessingFrameAttributes(),
-      aLoadInfo->GetOpenerPolicy()));
+      aLoadInfo->GetOpenerPolicy(), cookieSettingsArgs));
 
   return NS_OK;
 }
 
 nsresult LoadInfoArgsToLoadInfo(
     const Maybe<LoadInfoArgs>& aOptionalLoadInfoArgs,
     nsILoadInfo** outLoadInfo) {
   if (aOptionalLoadInfoArgs.isNothing()) {
@@ -648,21 +657,25 @@ nsresult LoadInfoArgsToLoadInfo(
 
   Maybe<ServiceWorkerDescriptor> controller;
   if (loadInfoArgs.controller().type() !=
       OptionalIPCServiceWorkerDescriptor::Tvoid_t) {
     controller.emplace(ServiceWorkerDescriptor(
         loadInfoArgs.controller().get_IPCServiceWorkerDescriptor()));
   }
 
+  nsCOMPtr<nsICookieSettings> cookieSettings;
+  CookieSettings::Deserialize(loadInfoArgs.cookieSettings(),
+                              getter_AddRefs(cookieSettings));
+
   RefPtr<mozilla::LoadInfo> loadInfo = new mozilla::LoadInfo(
       loadingPrincipal, triggeringPrincipal, principalToInherit,
       sandboxedLoadingPrincipal, topLevelPrincipal,
-      topLevelStorageAreaPrincipal, resultPrincipalURI, clientInfo,
-      reservedClientInfo, initialClientInfo, controller,
+      topLevelStorageAreaPrincipal, resultPrincipalURI, cookieSettings,
+      clientInfo, reservedClientInfo, initialClientInfo, controller,
       loadInfoArgs.securityFlags(), loadInfoArgs.contentPolicyType(),
       static_cast<LoadTainting>(loadInfoArgs.tainting()),
       loadInfoArgs.upgradeInsecureRequests(),
       loadInfoArgs.browserUpgradeInsecureRequests(),
       loadInfoArgs.browserWouldUpgradeInsecureRequests(),
       loadInfoArgs.verifySignedContent(), loadInfoArgs.enforceSRI(),
       loadInfoArgs.forceAllowDataURI(),
       loadInfoArgs.allowInsecureRedirectToDataURI(),
@@ -695,37 +708,47 @@ nsresult LoadInfoArgsToLoadInfo(
 void LoadInfoToParentLoadInfoForwarder(
     nsILoadInfo* aLoadInfo, ParentLoadInfoForwarderArgs* aForwarderArgsOut) {
   if (!aLoadInfo) {
     *aForwarderArgsOut = ParentLoadInfoForwarderArgs(
         false, void_t(), nsILoadInfo::TAINTING_BASIC,
         false,  // serviceWorkerTaintingSynthesized
         false,  // documentHasUserInteracted
         false,  // documentHasLoaded
-        nsILoadInfo::OPENER_POLICY_NULL);
+        nsILoadInfo::OPENER_POLICY_NULL, Maybe<CookieSettingsArgs>());
     return;
   }
 
   OptionalIPCServiceWorkerDescriptor ipcController = void_t();
   Maybe<ServiceWorkerDescriptor> controller(aLoadInfo->GetController());
   if (controller.isSome()) {
     ipcController = controller.ref().ToIPC();
   }
 
   uint32_t tainting = nsILoadInfo::TAINTING_BASIC;
   Unused << aLoadInfo->GetTainting(&tainting);
 
   nsILoadInfo::CrossOriginOpenerPolicy openerPolicy =
       aLoadInfo->GetOpenerPolicy();
 
+  Maybe<CookieSettingsArgs> cookieSettingsArgs;
+
+  nsCOMPtr<nsICookieSettings> cookieSettings;
+  nsresult rv = aLoadInfo->GetCookieSettings(getter_AddRefs(cookieSettings));
+  if (NS_SUCCEEDED(rv) && cookieSettings) {
+    CookieSettingsArgs args;
+    static_cast<CookieSettings*>(cookieSettings.get())->Serialize(args);
+    cookieSettingsArgs = Some(args);
+  }
+
   *aForwarderArgsOut = ParentLoadInfoForwarderArgs(
       aLoadInfo->GetAllowInsecureRedirectToDataURI(), ipcController, tainting,
       aLoadInfo->GetServiceWorkerTaintingSynthesized(),
       aLoadInfo->GetDocumentHasUserInteracted(),
-      aLoadInfo->GetDocumentHasLoaded(), openerPolicy);
+      aLoadInfo->GetDocumentHasLoaded(), openerPolicy, cookieSettingsArgs);
 }
 
 nsresult MergeParentLoadInfoForwarder(
     ParentLoadInfoForwarderArgs const& aForwarderArgs, nsILoadInfo* aLoadInfo) {
   if (!aLoadInfo) {
     return NS_OK;
   }
 
@@ -752,16 +775,27 @@ nsresult MergeParentLoadInfoForwarder(
   MOZ_ALWAYS_SUCCEEDS(
       aLoadInfo->SetOpenerPolicy(aForwarderArgs.openerPolicy()));
 
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasUserInteracted(
       aForwarderArgs.documentHasUserInteracted()));
   MOZ_ALWAYS_SUCCEEDS(
       aLoadInfo->SetDocumentHasLoaded(aForwarderArgs.documentHasLoaded()));
 
+  const Maybe<CookieSettingsArgs>& cookieSettingsArgs =
+      aForwarderArgs.cookieSettings();
+  if (cookieSettingsArgs.isSome()) {
+    nsCOMPtr<nsICookieSettings> cookieSettings;
+    nsresult rv = aLoadInfo->GetCookieSettings(getter_AddRefs(cookieSettings));
+    if (NS_SUCCEEDED(rv) && cookieSettings) {
+      static_cast<CookieSettings*>(cookieSettings.get())
+          ->Merge(cookieSettingsArgs.ref());
+    }
+  }
+
   return NS_OK;
 }
 
 void LoadInfoToChildLoadInfoForwarder(
     nsILoadInfo* aLoadInfo, ChildLoadInfoForwarderArgs* aForwarderArgsOut) {
   if (!aLoadInfo) {
     *aForwarderArgsOut =
         ChildLoadInfoForwarderArgs(Nothing(), Nothing(), void_t());
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1810,16 +1810,24 @@ VARCACHE_PREF(
 
 // Enables the predictive service.
 VARCACHE_PREF(
   "network.predictor.enabled",
    network_predictor_enabled,
   bool, true
 )
 
+// Allow CookieSettings to be unblocked for channels without a document.
+// This is for testing only.
+VARCACHE_PREF(
+  "network.cookieSettings.unblocked_for_testing",
+   network_cookieSettings_unblocked_for_testing,
+  bool, false
+)
+
 VARCACHE_PREF(
   "network.predictor.enable-hover-on-ssl",
    network_predictor_enable_hover_on_ssl,
   bool, false
 )
 
 VARCACHE_PREF(
   "network.predictor.enable-prefetch",
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -8,23 +8,27 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/ClientSource.h"
 #include "mozilla/dom/PerformanceStorage.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/net/CookieSettings.h"
 #include "mozilla/NullPrincipal.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsFrameLoader.h"
 #include "nsFrameLoaderOwner.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocShell.h"
 #include "mozilla/dom/Document.h"
+#include "nsCookiePermission.h"
+#include "nsICookieService.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsUtils.h"
 #include "nsIXPConnect.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsGlobalWindow.h"
 #include "nsMixedContentBlocker.h"
@@ -200,16 +204,20 @@ LoadInfo::LoadInfo(
             nsGlobalWindowInner* topInner =
                 nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow());
             if (topInner) {
               mDocumentHasLoaded = topInner->IsDocumentLoaded();
             }
           }
         }
       }
+
+      // Let's inherit the cookie behavior and permission from the parent
+      // document.
+      mCookieSettings = aLoadingContext->OwnerDoc()->CookieSettings();
     }
 
     mInnerWindowID = aLoadingContext->OwnerDoc()->InnerWindowID();
     mAncestorPrincipals = aLoadingContext->OwnerDoc()->AncestorPrincipals();
     mAncestorOuterWindowIDs =
         aLoadingContext->OwnerDoc()->AncestorOuterWindowIDs();
     MOZ_DIAGNOSTIC_ASSERT(mAncestorPrincipals.Length() ==
                           mAncestorOuterWindowIDs.Length());
@@ -421,26 +429,32 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
                         mAncestorOuterWindowIDs.Length());
 
 #ifdef DEBUG
   if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
     MOZ_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0,
                "chrome docshell shouldn't have mPrivateBrowsingId set.");
   }
 #endif
+
+  // Let's take the current cookie behavior and current cookie permission
+  // for the documents' loadInfo. Note that for any other loadInfos,
+  // cookieBehavior will be BEHAVIOR_REJECT for security reasons.
+  mCookieSettings = CookieSettings::Create();
 }
 
 LoadInfo::LoadInfo(const LoadInfo& rhs)
     : mLoadingPrincipal(rhs.mLoadingPrincipal),
       mTriggeringPrincipal(rhs.mTriggeringPrincipal),
       mPrincipalToInherit(rhs.mPrincipalToInherit),
       mSandboxedLoadingPrincipal(rhs.mSandboxedLoadingPrincipal),
       mTopLevelPrincipal(rhs.mTopLevelPrincipal),
       mTopLevelStorageAreaPrincipal(rhs.mTopLevelStorageAreaPrincipal),
       mResultPrincipalURI(rhs.mResultPrincipalURI),
+      mCookieSettings(rhs.mCookieSettings),
       mClientInfo(rhs.mClientInfo),
       // mReservedClientSource must be handled specially during redirect
       // mReservedClientInfo must be handled specially during redirect
       // mInitialClientInfo must be handled specially during redirect
       mController(rhs.mController),
       mPerformanceStorage(rhs.mPerformanceStorage),
       mLoadingContext(rhs.mLoadingContext),
       mContextForTopLevelLoad(rhs.mContextForTopLevelLoad),
@@ -489,17 +503,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
       mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes),
       mOpenerPolicy(rhs.mOpenerPolicy) {}
 
 LoadInfo::LoadInfo(
     nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
     nsIPrincipal* aPrincipalToInherit, nsIPrincipal* aSandboxedLoadingPrincipal,
     nsIPrincipal* aTopLevelPrincipal,
     nsIPrincipal* aTopLevelStorageAreaPrincipal, nsIURI* aResultPrincipalURI,
-    const Maybe<ClientInfo>& aClientInfo,
+    nsICookieSettings* aCookieSettings, const Maybe<ClientInfo>& aClientInfo,
     const Maybe<ClientInfo>& aReservedClientInfo,
     const Maybe<ClientInfo>& aInitialClientInfo,
     const Maybe<ServiceWorkerDescriptor>& aController,
     nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType,
     LoadTainting aTainting, bool aUpgradeInsecureRequests,
     bool aBrowserUpgradeInsecureRequests,
     bool aBrowserWouldUpgradeInsecureRequests, bool aVerifySignedContent,
     bool aEnforceSRI, bool aForceAllowDataURI,
@@ -521,16 +535,17 @@ LoadInfo::LoadInfo(
     bool aServiceWorkerTaintingSynthesized, bool aDocumentHasUserInteracted,
     bool aDocumentHasLoaded, const nsAString& aCspNonce)
     : mLoadingPrincipal(aLoadingPrincipal),
       mTriggeringPrincipal(aTriggeringPrincipal),
       mPrincipalToInherit(aPrincipalToInherit),
       mTopLevelPrincipal(aTopLevelPrincipal),
       mTopLevelStorageAreaPrincipal(aTopLevelStorageAreaPrincipal),
       mResultPrincipalURI(aResultPrincipalURI),
+      mCookieSettings(aCookieSettings),
       mClientInfo(aClientInfo),
       mReservedClientInfo(aReservedClientInfo),
       mInitialClientInfo(aInitialClientInfo),
       mController(aController),
       mSecurityFlags(aSecurityFlags),
       mInternalContentPolicyType(aContentPolicyType),
       mTainting(aTainting),
       mUpgradeInsecureRequests(aUpgradeInsecureRequests),
@@ -766,16 +781,31 @@ LoadInfo::GetCookiePolicy(uint32_t* aRes
                  ? nsILoadInfo::SEC_COOKIES_SAME_ORIGIN
                  : nsILoadInfo::SEC_COOKIES_INCLUDE;
   }
 
   *aResult = policy;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+LoadInfo::GetCookieSettings(nsICookieSettings** aCookieSettings) {
+  if (!mCookieSettings) {
+    if (StaticPrefs::network_cookieSettings_unblocked_for_testing()) {
+      mCookieSettings = CookieSettings::Create();
+    } else {
+      mCookieSettings = CookieSettings::CreateBlockingAll();
+    }
+  }
+
+  nsCOMPtr<nsICookieSettings> cookieSettings = mCookieSettings;
+  cookieSettings.forget(aCookieSettings);
+  return NS_OK;
+}
+
 void LoadInfo::SetIncludeCookiesSecFlag() {
   MOZ_ASSERT((mSecurityFlags & sCookiePolicyMask) ==
              nsILoadInfo::SEC_COOKIES_DEFAULT);
   mSecurityFlags =
       (mSecurityFlags & ~sCookiePolicyMask) | nsILoadInfo::SEC_COOKIES_INCLUDE;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -14,16 +14,17 @@
 #include "nsIURI.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 
+class nsICookieSettings;
 class nsINode;
 class nsPIDOMWindowOuter;
 
 namespace mozilla {
 
 namespace dom {
 class PerformanceStorage;
 class XMLHttpRequestMainThread;
@@ -65,16 +66,17 @@ class LoadInfo final : public nsILoadInf
   // loadingContext than other loads. This ContextForTopLevelLoad is
   // only used for content policy checks.
   LoadInfo(nsPIDOMWindowOuter* aOuterWindow, nsIPrincipal* aTriggeringPrincipal,
            nsISupports* aContextForTopLevelLoad,
            nsSecurityFlags aSecurityFlags);
 
   // create an exact copy of the loadinfo
   already_AddRefed<nsILoadInfo> Clone() const;
+
   // hands off!!! don't use CloneWithNewSecFlags unless you know
   // exactly what you are doing - it should only be used within
   // nsBaseChannel::Redirect()
   already_AddRefed<nsILoadInfo> CloneWithNewSecFlags(
       nsSecurityFlags aSecurityFlags) const;
   // creates a copy of the loadinfo which is appropriate to use for a
   // separate request. I.e. not for a redirect or an inner channel, but
   // when a separate request is made with the same security properties.
@@ -91,17 +93,17 @@ class LoadInfo final : public nsILoadInf
   // HttpChannelParent and FTPChannelParent declared as friends undeneath.
   // In e10s we can not serialize nsINode, hence we store the innerWindowID.
   // Please note that aRedirectChain uses swapElements.
   LoadInfo(nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
            nsIPrincipal* aPrincipalToInherit,
            nsIPrincipal* aSandboxedLoadingPrincipal,
            nsIPrincipal* aTopLevelPrincipal,
            nsIPrincipal* aTopLevelStorageAreaPrincipal,
-           nsIURI* aResultPrincipalURI,
+           nsIURI* aResultPrincipalURI, nsICookieSettings* aCookieSettings,
            const Maybe<mozilla::dom::ClientInfo>& aClientInfo,
            const Maybe<mozilla::dom::ClientInfo>& aReservedClientInfo,
            const Maybe<mozilla::dom::ClientInfo>& aInitialClientInfo,
            const Maybe<mozilla::dom::ServiceWorkerDescriptor>& aController,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType, LoadTainting aTainting,
            bool aUpgradeInsecureRequests, bool aBrowserUpgradeInsecureRequests,
            bool aBrowserWouldUpgradeInsecureRequests, bool aVerifySignedContent,
@@ -150,16 +152,17 @@ class LoadInfo final : public nsILoadInf
   nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
   nsCOMPtr<nsIPrincipal> mSandboxedLoadingPrincipal;
   nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
   nsCOMPtr<nsIPrincipal> mTopLevelStorageAreaPrincipal;
   nsCOMPtr<nsIURI> mResultPrincipalURI;
   nsCOMPtr<nsICSPEventListener> mCSPEventListener;
+  nsCOMPtr<nsICookieSettings> mCookieSettings;
 
   Maybe<mozilla::dom::ClientInfo> mClientInfo;
   UniquePtr<mozilla::dom::ClientSource> mReservedClientSource;
   Maybe<mozilla::dom::ClientInfo> mReservedClientInfo;
   Maybe<mozilla::dom::ClientInfo> mInitialClientInfo;
   Maybe<mozilla::dom::ServiceWorkerDescriptor> mController;
   RefPtr<mozilla::dom::PerformanceStorage> mPerformanceStorage;
 
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -3,16 +3,17 @@
  * 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 "nsISupports.idl"
 #include "nsIContentPolicy.idl"
 
 interface nsIChannel;
+interface nsICookieSettings;
 interface nsICSPEventListener;
 interface nsINode;
 interface nsIPrincipal;
 interface nsIRedirectHistoryEntry;
 interface nsIURI;
 webidl Document;
 webidl BrowsingContext;
 native LoadContextRef(already_AddRefed<nsISupports>);
@@ -422,16 +423,22 @@ interface nsILoadInfo : nsISupports
    * See the SEC_COOKIES_* flags above. This attribute will never return
    * SEC_COOKIES_DEFAULT, but will instead return what the policy resolves to.
    * I.e. SEC_COOKIES_SAME_ORIGIN for CORS mode, and SEC_COOKIES_INCLUDE
    * otherwise.
    */
   [infallible] readonly attribute unsigned long cookiePolicy;
 
   /**
+   * The cookie settings inherited from the top-level document's loadInfo.
+   * It cannot be null.
+   */
+  readonly attribute nsICookieSettings cookieSettings;
+
+  /**
    * If forceInheritPrincipal is true, the data coming from the channel should
    * inherit its principal, even when the data is loaded over http:// or another
    * protocol that would normally use a URI-based principal.
    *
    * See the documentation for principalToInherit, which describes exactly what
    * principal is inherited.
    *
    * This attribute will never be true when loadingSandboxed is true.
new file mode 100644
--- /dev/null
+++ b/netwerk/cookie/CookieSettings.cpp
@@ -0,0 +1,206 @@
+/* -*- 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/net/CookieSettings.h"
+#include "mozilla/Unused.h"
+#include "nsGlobalWindowInner.h"
+#include "nsPermission.h"
+#include "nsPermissionManager.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+class PermissionComparator {
+ public:
+  bool Equals(nsIPermission* aA, nsIPermission* aB) const {
+    nsCOMPtr<nsIPrincipal> principalA;
+    nsresult rv = aA->GetPrincipal(getter_AddRefs(principalA));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    nsCOMPtr<nsIPrincipal> principalB;
+    rv = aB->GetPrincipal(getter_AddRefs(principalB));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    bool equals = false;
+    rv = principalA->Equals(principalB, &equals);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    return equals;
+  }
+};
+
+}  // namespace
+
+// static
+already_AddRefed<nsICookieSettings> CookieSettings::CreateBlockingAll() {
+  RefPtr<CookieSettings> cookieSettings =
+      new CookieSettings(nsICookieService::BEHAVIOR_REJECT, eFixed);
+  return cookieSettings.forget();
+}
+
+// static
+already_AddRefed<nsICookieSettings> CookieSettings::Create() {
+  RefPtr<CookieSettings> cookieSettings = new CookieSettings(
+      StaticPrefs::network_cookie_cookieBehavior(), eProgressive);
+  return cookieSettings.forget();
+}
+
+CookieSettings::CookieSettings(uint32_t aCookieBehavior, State aState)
+    : mCookieBehavior(aCookieBehavior), mState(aState) {}
+
+CookieSettings::~CookieSettings() = default;
+
+NS_IMETHODIMP
+CookieSettings::GetCookieBehavior(uint32_t* aCookieBehavior) {
+  *aCookieBehavior = mCookieBehavior;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CookieSettings::CookiePermission(nsIPrincipal* aPrincipal,
+                                 uint32_t* aCookiePermission) {
+  NS_ENSURE_ARG_POINTER(aPrincipal);
+  NS_ENSURE_ARG_POINTER(aCookiePermission);
+
+  *aCookiePermission = nsIPermissionManager::UNKNOWN_ACTION;
+
+  nsresult rv;
+
+  // Let's see if we know this permission.
+  for (const RefPtr<nsIPermission>& permission : mCookiePermissions) {
+    bool match = false;
+    rv = permission->Matches(aPrincipal, false, &match);
+    if (NS_WARN_IF(NS_FAILED(rv)) || !match) {
+      continue;
+    }
+
+    rv = permission->GetCapability(aCookiePermission);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+  // Let's ask the permission manager.
+  nsPermissionManager* pm = nsPermissionManager::GetInstance();
+  if (NS_WARN_IF(!pm)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = pm->TestPermissionFromPrincipal(aPrincipal, NS_LITERAL_CSTRING("cookie"),
+                                       aCookiePermission);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Let's store the permission, also if the result is UNKNOWN in order to avoid
+  // race conditions.
+
+  nsCOMPtr<nsIPermission> permission = nsPermission::Create(
+      aPrincipal, NS_LITERAL_CSTRING("cookie"), *aCookiePermission, 0, 0);
+  if (permission) {
+    mCookiePermissions.AppendElement(permission);
+  }
+
+  return NS_OK;
+}
+
+void CookieSettings::Serialize(CookieSettingsArgs& aData) {
+  aData.isFixed() = mState == eFixed;
+  aData.cookieBehavior() = mCookieBehavior;
+
+  for (const RefPtr<nsIPermission>& permission : mCookiePermissions) {
+    nsCOMPtr<nsIPrincipal> principal;
+    nsresult rv = permission->GetPrincipal(getter_AddRefs(principal));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    PrincipalInfo principalInfo;
+    rv = PrincipalToPrincipalInfo(principal, &principalInfo);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    uint32_t cookiePermission = 0;
+    rv = permission->GetCapability(&cookiePermission);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    aData.cookiePermissions().AppendElement(
+        CookiePermissionData(principalInfo, cookiePermission));
+  }
+}
+
+/* static */ void CookieSettings::Deserialize(
+    const CookieSettingsArgs& aData, nsICookieSettings** aCookieSettings) {
+  CookiePermissionList list;
+  for (const CookiePermissionData& data : aData.cookiePermissions()) {
+    nsCOMPtr<nsIPrincipal> principal =
+        PrincipalInfoToPrincipal(data.principalInfo());
+    if (NS_WARN_IF(!principal)) {
+      continue;
+    }
+
+    nsCOMPtr<nsIPermission> permission = nsPermission::Create(
+        principal, NS_LITERAL_CSTRING("cookie"), data.cookiePermission(), 0, 0);
+    if (NS_WARN_IF(!permission)) {
+      continue;
+    }
+
+    list.AppendElement(permission);
+  }
+
+  RefPtr<CookieSettings> cookieSettings = new CookieSettings(
+      aData.cookieBehavior(), aData.isFixed() ? eFixed : eProgressive);
+
+  cookieSettings->mCookiePermissions.SwapElements(list);
+
+  cookieSettings.forget(aCookieSettings);
+}
+
+void CookieSettings::Merge(const CookieSettingsArgs& aData) {
+  MOZ_ASSERT(mCookieBehavior == aData.cookieBehavior());
+
+  if (mState == eFixed) {
+    return;
+  }
+
+  PermissionComparator comparator;
+
+  for (const CookiePermissionData& data : aData.cookiePermissions()) {
+    nsCOMPtr<nsIPrincipal> principal =
+        PrincipalInfoToPrincipal(data.principalInfo());
+    if (NS_WARN_IF(!principal)) {
+      continue;
+    }
+
+    nsCOMPtr<nsIPermission> permission = nsPermission::Create(
+        principal, NS_LITERAL_CSTRING("cookie"), data.cookiePermission(), 0, 0);
+    if (NS_WARN_IF(!permission)) {
+      continue;
+    }
+
+    if (!mCookiePermissions.Contains(permission, comparator)) {
+      mCookiePermissions.AppendElement(permission);
+    }
+  }
+}
+
+NS_IMPL_ISUPPORTS(CookieSettings, nsICookieSettings)
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/cookie/CookieSettings.h
@@ -0,0 +1,63 @@
+/* -*- 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_net_CookieSettings_h
+#define mozilla_net_CookieSettings_h
+
+#include "nsICookieSettings.h"
+#include "nsDataHashtable.h"
+
+class nsIPermission;
+
+namespace mozilla {
+namespace net {
+
+class CookieSettingsArgs;
+
+/**
+ * Class that provides an nsICookieSettings implementation.
+ */
+class CookieSettings final : public nsICookieSettings {
+ public:
+  typedef nsTArray<RefPtr<nsIPermission>> CookiePermissionList;
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICOOKIESETTINGS
+
+  static already_AddRefed<nsICookieSettings> CreateBlockingAll();
+
+  static already_AddRefed<nsICookieSettings> Create();
+
+  void Serialize(CookieSettingsArgs& aData);
+
+  static void Deserialize(const CookieSettingsArgs& aData,
+                          nsICookieSettings** aCookieSettings);
+
+  void Merge(const CookieSettingsArgs& aData);
+
+ private:
+  enum State {
+    // No cookie permissions are allowed to be stored in this object.
+    eFixed,
+
+    // Cookie permissions can be stored in case they are unknown when they are
+    // asked or when they are sent from the parent process.
+    eProgressive,
+  };
+
+  CookieSettings(uint32_t aCookieBehavior, State aState);
+  ~CookieSettings();
+
+  uint32_t mCookieBehavior;
+  CookiePermissionList mCookiePermissions;
+
+  State mState;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_CookieSettings_h
--- a/netwerk/cookie/moz.build
+++ b/netwerk/cookie/moz.build
@@ -9,36 +9,40 @@ with Files('**'):
 
 # export required interfaces, even if --disable-cookies has been given
 XPIDL_SOURCES += [
     'nsICookie.idl',
     'nsICookie2.idl',
     'nsICookieManager.idl',
     'nsICookiePermission.idl',
     'nsICookieService.idl',
+    'nsICookieSettings.idl',
 ]
 
 XPIDL_MODULE = 'necko_cookie'
 
 if CONFIG['NECKO_COOKIES']:
     EXPORTS.mozilla.net = [
         'CookieServiceChild.h',
         'CookieServiceParent.h',
+        'CookieSettings.h',
         'nsCookieKey.h',
     ]
     UNIFIED_SOURCES += [
         'CookieServiceChild.cpp',
         'CookieServiceParent.cpp',
+        'CookieSettings.cpp',
         'nsCookie.cpp',
     ]
     # nsCookieService.cpp can't be unified because of symbol conflicts
     SOURCES += [
         'nsCookieService.cpp',
     ]
     LOCAL_INCLUDES += [
+        '/extensions/cookie',
         '/intl/uconv',
     ]
 
     XPCSHELL_TESTS_MANIFESTS += [
         'test/unit/xpcshell.ini',
     ]
 
     BROWSER_CHROME_MANIFESTS += [
new file mode 100644
--- /dev/null
+++ b/netwerk/cookie/nsICookieSettings.idl
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: ft=cpp tw=78 sw=2 et ts=2 sts=2 cin
+ * 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 "nsISupports.idl"
+
+interface nsIPrincipal;
+
+/**
+ * Cookie settings for top-level documents.
+ */
+[builtinclass, uuid(3ec40331-7cf0-4b71-ba2a-2265aab8f6bc)]
+interface nsICookieSettings : nsISupports
+{
+  /**
+   * CookieBehavior at the loading of the document. Any other loadInfo
+   * inherits it from its document's loadInfo. If there is not a document
+   * involved, cookieBehavior is reject.
+   */
+  [infallible] readonly attribute unsigned long cookieBehavior;
+
+  /**
+   * CookiePermission at the loading of the document for a particular
+   * principal. It returns the same cookiePermission also in case it changes
+   * during the life-time of the top document.
+   */
+  unsigned long cookiePermission(in nsIPrincipal aPrincipal);
+};
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -23,16 +23,34 @@ using struct nsHttpAtom from "nsHttp.h";
 using class mozilla::net::nsHttpResponseHead from "nsHttpResponseHead.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using nsILoadInfo::CrossOriginOpenerPolicy from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
+// CookieSettings IPDL structs
+//-----------------------------------------------------------------------------
+
+struct CookiePermissionData
+{
+  PrincipalInfo principalInfo;
+  uint32_t cookiePermission;
+};
+
+struct CookieSettingsArgs
+{
+  // Copy of the cookie behavior and permissions for the top-level document.
+  uint32_t cookieBehavior;
+  CookiePermissionData[] cookiePermissions;
+  bool isFixed;
+};
+
+//-----------------------------------------------------------------------------
 // Preferrer alternative data type
 //-----------------------------------------------------------------------------
 
 struct PreferredAlternativeDataTypeParams
 {
   nsCString type;
   nsCString contentType;
   bool deliverAltData;
@@ -120,16 +138,18 @@ struct LoadInfoArgs
   bool                        isPreflight;
   bool                        loadTriggeredFromExternal;
   bool                        serviceWorkerTaintingSynthesized;
   bool                        documentHasUserInteracted;
   bool                        documentHasLoaded;
   nsString                    cspNonce;
   bool                        isFromProcessingFrameAttributes;
   CrossOriginOpenerPolicy     openerPolicy;
+
+  CookieSettingsArgs cookieSettings;
 };
 
 /**
  * This structure is used to carry selected properties of a LoadInfo
  * object to child processes to merge LoadInfo changes from the parent
  * process.  We don't want to use LoadInfoArgs for that since it's
  * too huge and we only care about small subpart of properties anyway.
  */
@@ -153,16 +173,18 @@ struct ParentLoadInfoForwarderArgs
   // by the service worker.
   bool serviceWorkerTaintingSynthesized;
 
   bool documentHasUserInteracted;
   bool documentHasLoaded;
 
   CrossOriginOpenerPolicy openerPolicy;
 
+  CookieSettingsArgs? cookieSettings;
+
   // IMPORTANT: when you add new properites here you must also update
   // LoadInfoToParentLoadInfoForwarder and MergeParentLoadInfoForwarder
   // in BackgroundUtils.cpp/.h!
 };
 
 /**
  * This structure is used to carry selected properties of a LoadInfo
  * object to the parent process that might have changed in the child