Bug 1536411 - StoragePrincipal - part 1 - Implementation, r=Ehsan
☠☠ backed out by a047b64590a4 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 11 Apr 2019 16:27:12 +0000
changeset 469033 45708d22bb6d665c71111dfeeb706a52828b37b6
parent 469032 87601ef1d4d4ec187b36f1b1f643d16f984e2fc6
child 469034 2eb20578159b4f3e398859ce0decf8dc68f2802e
push id35856
push usercsabou@mozilla.com
push dateFri, 12 Apr 2019 03:19:48 +0000
treeherdermozilla-central@940684cd1065 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersEhsan
bugs1536411
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1536411 - StoragePrincipal - part 1 - Implementation, r=Ehsan Differential Revision: https://phabricator.services.mozilla.com/D24025
caps/BasePrincipal.cpp
caps/BasePrincipal.h
caps/OriginAttributes.cpp
caps/OriginAttributes.h
caps/nsIScriptSecurityManager.idl
caps/nsScriptSecurityManager.cpp
dom/base/Document.cpp
dom/base/Document.h
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsGlobalWindowOuter.h
dom/base/nsIScriptObjectPrincipal.h
dom/html/nsHTMLDocument.cpp
dom/html/nsHTMLDocument.h
dom/webidl/Document.webidl
dom/xml/XMLDocument.cpp
dom/xml/XMLDocument.h
dom/xslt/base/txURIUtils.cpp
dom/xul/XULDocument.cpp
dom/xul/XULDocument.h
js/xpconnect/src/BackstagePass.h
js/xpconnect/src/SandboxPrivate.h
layout/build/nsContentDLF.cpp
modules/libpref/init/StaticPrefList.h
netwerk/base/LoadInfo.cpp
parser/prototype/PrototypeDocumentParser.cpp
toolkit/components/antitracking/StoragePrincipalHelper.cpp
toolkit/components/antitracking/StoragePrincipalHelper.h
toolkit/components/antitracking/moz.build
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -450,16 +450,38 @@ BasePrincipal::CloneStrippingUserContext
 
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
 }
 
+already_AddRefed<BasePrincipal> BasePrincipal::CloneForcingFirstPartyDomain(
+    nsIURI* aURI) {
+  if (NS_WARN_IF(!IsCodebasePrincipal())) {
+    return nullptr;
+  }
+
+  OriginAttributes attrs = OriginAttributesRef();
+  // XXX this is slow. Maybe we should consider to make it faster.
+  attrs.SetFirstPartyDomain(false, aURI, true /* aForced */);
+
+  nsAutoCString originNoSuffix;
+  nsresult rv = GetOriginNoSuffix(originNoSuffix);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  nsIURI* uri = static_cast<ContentPrincipal*>(this)->mCodebase;
+  RefPtr<ContentPrincipal> copy = new ContentPrincipal();
+  rv = copy->Init(uri, attrs, originNoSuffix);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return copy.forget();
+}
+
 extensions::WebExtensionPolicy* BasePrincipal::ContentScriptAddonPolicy() {
   if (!Is<ExpandedPrincipal>()) {
     return nullptr;
   }
 
   auto expanded = As<ExpandedPrincipal>();
   for (auto& prin : expanded->AllowList()) {
     if (auto policy = BasePrincipal::Cast(prin)->AddonPolicy()) {
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -164,16 +164,18 @@ class BasePrincipal : public nsJSPrincip
     return mOriginAttributes.mInIsolatedMozBrowser;
   }
 
   PrincipalKind Kind() const { return mKind; }
 
   already_AddRefed<BasePrincipal>
   CloneStrippingUserContextIdAndFirstPartyDomain();
 
+  already_AddRefed<BasePrincipal> CloneForcingFirstPartyDomain(nsIURI* aURI);
+
   // If this is an add-on content script principal, returns its AddonPolicy.
   // Otherwise returns null.
   extensions::WebExtensionPolicy* ContentScriptAddonPolicy();
 
   // Helper to check whether this principal is associated with an addon that
   // allows unprivileged code to load aURI.  aExplicit == true will prevent
   // use of all_urls permission, requiring the domain in its permissions.
   bool AddonAllowsLoad(nsIURI* aURI, bool aExplicit = false);
--- a/caps/OriginAttributes.cpp
+++ b/caps/OriginAttributes.cpp
@@ -33,21 +33,21 @@ void OriginAttributes::InitPrefs() {
         "privacy.firstparty.isolate.restrict_opener_access");
     Preferences::AddBoolVarCache(
         &sBlockPostMessageForFPI,
         "privacy.firstparty.isolate.block_post_message");
   }
 }
 
 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument,
-                                           nsIURI* aURI) {
+                                           nsIURI* aURI, bool aForced) {
   bool isFirstPartyEnabled = IsFirstPartyEnabled();
 
-  // If the pref is off or this is not a top level load, bail out.
-  if (!isFirstPartyEnabled || !aIsTopLevelDocument) {
+  // If the prefs are off or this is not a top level load, bail out.
+  if ((!isFirstPartyEnabled || !aIsTopLevelDocument) && !aForced) {
     return;
   }
 
   nsCOMPtr<nsIEffectiveTLDService> tldService =
       do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   MOZ_ASSERT(tldService);
   if (!tldService) {
     return;
--- a/caps/OriginAttributes.h
+++ b/caps/OriginAttributes.h
@@ -20,17 +20,18 @@ class OriginAttributes : public dom::Ori
   OriginAttributes(uint32_t aAppId, bool aInIsolatedMozBrowser) {
     mAppId = aAppId;
     mInIsolatedMozBrowser = aInIsolatedMozBrowser;
   }
 
   explicit OriginAttributes(const OriginAttributesDictionary& aOther)
       : OriginAttributesDictionary(aOther) {}
 
-  void SetFirstPartyDomain(const bool aIsTopLevelDocument, nsIURI* aURI);
+  void SetFirstPartyDomain(const bool aIsTopLevelDocument, nsIURI* aURI,
+                           bool aForced = false);
   void SetFirstPartyDomain(const bool aIsTopLevelDocument,
                            const nsACString& aDomain);
 
   enum {
     STRIP_FIRST_PARTY_DOMAIN = 0x01,
     STRIP_USER_CONTEXT_ID = 0x02,
   };
 
--- a/caps/nsIScriptSecurityManager.idl
+++ b/caps/nsIScriptSecurityManager.idl
@@ -192,24 +192,42 @@ interface nsIScriptSecurityManager : nsI
      * don't need reporting.
      * FromPrivateWindow indicates whether the error occurs in a private
      * window or not.
      */
     void checkSameOriginURI(in nsIURI aSourceURI,
                             in nsIURI aTargetURI,
                             in boolean reportError,
                             in boolean fromPrivateWindow);
+
     /**
      * Get the principal for the given channel.  This will typically be the
      * channel owner if there is one, and the codebase principal for the
      * channel's URI otherwise.  aChannel must not be null.
      */
     nsIPrincipal getChannelResultPrincipal(in nsIChannel aChannel);
 
     /**
+     * Get the storage principal for the given channel.  This is basically the
+     * same of getChannelResultPrincipal() execept for trackers, where we
+     * return a principal with a different OriginAttributes.
+     */
+    nsIPrincipal getChannelResultStoragePrincipal(in nsIChannel aChannel);
+
+    /**
+     * This method does getChannelResultPrincipal() +
+     * getChannelResultStoragePrincipal().
+     * This method is mainly done for Document::Reset(). There are no other
+     * reasons to use this method.
+     */
+    void getChannelResultPrincipals(in nsIChannel aChannel,
+                                    out nsIPrincipal aPrincipal,
+                                    out nsIPrincipal aStoragePrincipal);
+
+    /**
      * Temporary API until bug 1220687 is fixed.
      *
      * Returns the same value as getChannelResultPrincipal, but ignoring
      * sandboxing.  Specifically, if sandboxing would have prevented the
      * channel's triggering principal from being returned by
      * getChannelResultPrincipal, the triggering principal will be returned
      * by this method.
      *
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -2,16 +2,17 @@
 /* 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 "nsScriptSecurityManager.h"
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/StoragePrincipalHelper.h"
 
 #include "xpcpublic.h"
 #include "XPCWrapper.h"
 #include "nsIInputStreamChannel.h"
 #include "nsILoadContext.h"
 #include "nsIServiceManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
@@ -240,16 +241,43 @@ nsScriptSecurityManager::GetChannelResul
 }
 
 nsresult nsScriptSecurityManager::GetChannelResultPrincipalIfNotSandboxed(
     nsIChannel* aChannel, nsIPrincipal** aPrincipal) {
   return GetChannelResultPrincipal(aChannel, aPrincipal,
                                    /*aIgnoreSandboxing*/ true);
 }
 
+NS_IMETHODIMP
+nsScriptSecurityManager::GetChannelResultStoragePrincipal(
+    nsIChannel* aChannel, nsIPrincipal** aPrincipal) {
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetChannelResultPrincipal(aChannel, getter_AddRefs(principal),
+                                          /*aIgnoreSandboxing*/ false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return StoragePrincipalHelper::Create(aChannel, principal, aPrincipal);
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::GetChannelResultPrincipals(
+    nsIChannel* aChannel, nsIPrincipal** aPrincipal,
+    nsIPrincipal** aStoragePrincipal) {
+  nsresult rv = GetChannelResultPrincipal(aChannel, aPrincipal,
+                                          /*aIgnoreSandboxing*/ false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return StoragePrincipalHelper::Create(aChannel, *aPrincipal,
+                                        aStoragePrincipal);
+}
+
 static void InheritAndSetCSPOnPrincipalIfNeeded(nsIChannel* aChannel,
                                                 nsIPrincipal* aPrincipal) {
   // loading a data: URI into an iframe, or loading frame[srcdoc] need
   // to inherit the CSP (see Bug 1073952, 1381761).
   MOZ_ASSERT(aChannel && aPrincipal, "need a valid channel and principal");
   if (!aChannel) {
     return;
   }
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1918,33 +1918,42 @@ bool Document::IsVisibleConsideringAnces
   } while ((parent = parent->GetParentDocument()));
 
   return true;
 }
 
 void Document::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) {
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIPrincipal> principal;
+  nsCOMPtr<nsIPrincipal> storagePrincipal;
   if (aChannel) {
     // Note: this code is duplicated in XULDocument::StartDocumentLoad and
-    // nsScriptSecurityManager::GetChannelResultPrincipal.
+    // nsScriptSecurityManager::GetChannelResultPrincipals.
     // Note: this should match nsDocShell::OnLoadingSite
     NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
 
     nsIScriptSecurityManager* securityManager =
         nsContentUtils::GetSecurityManager();
     if (securityManager) {
-      securityManager->GetChannelResultPrincipal(aChannel,
-                                                 getter_AddRefs(principal));
-    }
-  }
+      securityManager->GetChannelResultPrincipals(
+          aChannel, getter_AddRefs(principal),
+          getter_AddRefs(storagePrincipal));
+    }
+  }
+
+  bool equal = principal->Equals(storagePrincipal);
 
   principal = MaybeDowngradePrincipal(principal);
-
-  ResetToURI(uri, aLoadGroup, principal);
+  if (equal) {
+    storagePrincipal = principal;
+  } else {
+    storagePrincipal = MaybeDowngradePrincipal(storagePrincipal);
+  }
+
+  ResetToURI(uri, aLoadGroup, principal, storagePrincipal);
 
   // Note that, since mTiming does not change during a reset, the
   // navigationStart time remains unchanged and therefore any future new
   // timeline will have the same global clock time as the old one.
   mDocumentTimeline = nullptr;
 
   nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
   if (bag) {
@@ -2026,18 +2035,20 @@ void Document::DisconnectNodeTree() {
     }
     MOZ_ASSERT(!mCachedRootElement,
                "After removing all children, there should be no root elem");
   }
   mInUnlinkOrDeletion = oldVal;
 }
 
 void Document::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
-                          nsIPrincipal* aPrincipal) {
+                          nsIPrincipal* aPrincipal,
+                          nsIPrincipal* aStoragePrincipal) {
   MOZ_ASSERT(aURI, "Null URI passed to ResetToURI");
+  MOZ_ASSERT(!!aPrincipal == !!aStoragePrincipal);
 
   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
           ("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get()));
 
   mSecurityInfo = nullptr;
 
   nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
   if (!aLoadGroup || group != aLoadGroup) {
@@ -2057,17 +2068,17 @@ void Document::ResetToURI(nsIURI* aURI, 
 
   // Release the stylesheets list.
   mDOMStyleSheets = nullptr;
 
   // Release our principal after tearing down the document, rather than before.
   // This ensures that, during teardown, the document and the dying window
   // (which already nulled out its document pointer and cached the principal)
   // have matching principals.
-  SetPrincipal(nullptr);
+  SetPrincipals(nullptr, nullptr);
 
   // Clear the original URI so SetDocumentURI sets it.
   mOriginalURI = nullptr;
 
   SetDocumentURI(aURI);
   mChromeXHRDocURI = nullptr;
   // If mDocumentBaseURI is null, Document::GetBaseURI() returns
   // mDocumentURI.
@@ -2105,17 +2116,17 @@ void Document::ResetToURI(nsIURI* aURI, 
   mContentLanguage.Truncate();
   mBaseTarget.Truncate();
   mReferrer.Truncate();
 
   mXMLDeclarationBits = 0;
 
   // Now get our new principal
   if (aPrincipal) {
-    SetPrincipal(aPrincipal);
+    SetPrincipals(aPrincipal, aStoragePrincipal);
   } else {
     nsIScriptSecurityManager* securityManager =
         nsContentUtils::GetSecurityManager();
     if (securityManager) {
       nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
 
       if (!loadContext && aLoadGroup) {
         nsCOMPtr<nsIInterfaceRequestor> cbs;
@@ -2125,17 +2136,17 @@ void Document::ResetToURI(nsIURI* aURI, 
 
       MOZ_ASSERT(loadContext,
                  "must have a load context or pass in an explicit principal");
 
       nsCOMPtr<nsIPrincipal> principal;
       nsresult rv = securityManager->GetLoadContextCodebasePrincipal(
           mDocumentURI, loadContext, getter_AddRefs(principal));
       if (NS_SUCCEEDED(rv)) {
-        SetPrincipal(principal);
+        SetPrincipals(principal, principal);
       }
     }
   }
 
   if (mFontFaceSet) {
     mFontFaceSet->RefreshStandardFontLoadPrincipal();
   }
 
@@ -2788,17 +2799,17 @@ nsresult Document::InitCSP(nsIChannel* a
   bool needNewNullPrincipal = (cspSandboxFlags & SANDBOXED_ORIGIN) &&
                               !(mSandboxFlags & SANDBOXED_ORIGIN);
 
   mSandboxFlags |= cspSandboxFlags;
 
   if (needNewNullPrincipal) {
     principal = NullPrincipal::CreateWithInheritedAttributes(principal);
     principal->SetCsp(csp);
-    SetPrincipal(principal);
+    SetPrincipals(principal, principal);
   }
 
   // ----- Enforce frame-ancestor policy on any applied policies
   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   if (docShell) {
     bool safeAncestry = false;
 
     // PermitsAncestry sends violation reports when necessary
@@ -3012,26 +3023,29 @@ void Document::RemoveFromIdTable(Element
       !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
     IncrementExpandoGeneration(*this);
   }
   if (entry->IsEmpty()) {
     mIdentifierMap.RemoveEntry(entry);
   }
 }
 
-void Document::SetPrincipal(nsIPrincipal* aNewPrincipal) {
+void Document::SetPrincipals(nsIPrincipal* aNewPrincipal,
+                             nsIPrincipal* aNewStoragePrincipal) {
+  MOZ_ASSERT(!!aNewPrincipal == !!aNewStoragePrincipal);
   if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
     nsCOMPtr<nsIURI> uri;
     aNewPrincipal->GetURI(getter_AddRefs(uri));
     bool isHTTPS;
     if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) || isHTTPS) {
       mAllowDNSPrefetch = false;
     }
   }
   mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
+  mIntrinsicStoragePrincipal = aNewStoragePrincipal;
 
 #ifdef DEBUG
   // Validate that the docgroup is set correctly by calling its getter and
   // triggering its sanity check.
   //
   // If we're setting the principal to null, we don't want to perform the check,
   // as the document is entering an intermediate state where it does not have a
   // principal. It will be given another real principal shortly which we will
@@ -8188,31 +8202,32 @@ nsresult Document::CloneDocHelper(Docume
     nsCOMPtr<nsIURI> uri;
     if (channel) {
       NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
     } else {
       uri = Document::GetDocumentURI();
     }
     clone->mChannel = channel;
     if (uri) {
-      clone->ResetToURI(uri, loadGroup, NodePrincipal());
+      clone->ResetToURI(uri, loadGroup, NodePrincipal(),
+                        EffectiveStoragePrincipal());
     }
 
     clone->SetContainer(mDocumentContainer);
   }
 
   // Now ensure that our clone has the same URI, base URI, and principal as us.
   // We do this after the mCreatingStaticClone block above, because that block
   // can set the base URI to an incorrect value in cases when base URI
   // information came from the channel.  So we override explicitly, and do it
   // for all these properties, in case ResetToURI messes with any of the rest of
   // them.
   clone->SetDocumentURI(Document::GetDocumentURI());
   clone->SetChromeXHRDocURI(mChromeXHRDocURI);
-  clone->SetPrincipal(NodePrincipal());
+  clone->SetPrincipals(NodePrincipal(), EffectiveStoragePrincipal());
   clone->mDocumentBaseURI = mDocumentBaseURI;
   clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
 
   bool hasHadScriptObject = true;
   nsIScriptGlobalObject* scriptObject =
       GetScriptHandlingObject(hasHadScriptObject);
   NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
   if (mCreatingStaticClone) {
@@ -12824,10 +12839,28 @@ nsICookieSettings* Document::CookieSetti
   // we must have a nsCookieSettings. Let's create it.
   if (!mCookieSettings) {
     mCookieSettings = net::CookieSettings::Create();
   }
 
   return mCookieSettings;
 }
 
+nsIPrincipal* Document::EffectiveStoragePrincipal() const {
+  if (!StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+    return NodePrincipal();
+  }
+
+  nsContentUtils::StorageAccess access =
+      nsContentUtils::StorageAllowedForDocument(this);
+
+  // Let's use the storage principal only if we need to partition the cookie
+  // jar. When the permission is granted, access will be different and the
+  // normal principal will be used.
+  if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny) {
+    return NodePrincipal();
+  }
+
+  return mIntrinsicStoragePrincipal;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -518,19 +518,25 @@ class Document : public nsINode,
   }
   bool GetValueMissingState(const nsAString& aName) const final {
     return DocumentOrShadowRoot::GetValueMissingState(aName);
   }
   void SetValueMissingState(const nsAString& aName, bool aValue) final {
     return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
   }
 
+  nsIPrincipal* EffectiveStoragePrincipal() const;
+
   // nsIScriptObjectPrincipal
   nsIPrincipal* GetPrincipal() final { return NodePrincipal(); }
 
+  nsIPrincipal* GetEffectiveStoragePrincipal() final {
+    return EffectiveStoragePrincipal();
+  }
+
   // EventTarget
   void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
   EventListenerManager* GetOrCreateListenerManager() override;
   EventListenerManager* GetExistingListenerManager() const override;
 
   // This helper class must be set when we dispatch beforeunload and unload
   // events in order to avoid unterminate sync XHRs.
   class MOZ_RAII PageUnloadingEventTimeStamp {
@@ -720,20 +726,20 @@ class Document : public nsINode,
       return mUpgradeInsecurePreloads;
     }
     return mUpgradeInsecureRequests;
   }
 
   void SetReferrer(const nsACString& aReferrer) { mReferrer = aReferrer; }
 
   /**
-   * Set the principal responsible for this document.  Chances are,
-   * you do not want to be using this.
-   */
-  void SetPrincipal(nsIPrincipal* aPrincipal);
+   * Set the principals responsible for this document.  Chances are, you do not
+   * want to be using this.
+   */
+  void SetPrincipals(nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal);
 
   /**
    * Get the list of ancestor principals for a document.  This is the same as
    * the ancestor list for the document's docshell the last time SetContainer()
    * was called with a non-null argument. See the documentation for the
    * corresponding getter in docshell for how this list is determined.  We store
    * a copy of the list, because we may lose the ability to reach our docshell
    * before people stop asking us for this information.
@@ -2081,22 +2087,23 @@ class Document : public nsINode,
   /**
    * Reset the document using the given channel and loadgroup.  This works
    * like ResetToURI, but also sets the document's channel to aChannel.
    * The principal of the document will be set from the channel.
    */
   virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup);
 
   /**
-   * Reset this document to aURI, aLoadGroup, and aPrincipal.  aURI must not be
-   * null.  If aPrincipal is null, a codebase principal based on aURI will be
-   * used.
+   * Reset this document to aURI, aLoadGroup, aPrincipal and aStoragePrincipal.
+   * aURI must not be null.  If aPrincipal is null, a codebase principal based
+   * on aURI will be used.
    */
   virtual void ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
-                          nsIPrincipal* aPrincipal);
+                          nsIPrincipal* aPrincipal,
+                          nsIPrincipal* aStoragePrincipal);
 
   /**
    * Set the container (docshell) for this document. Virtual so that
    * docshell can call it.
    */
   virtual void SetContainer(nsDocShell* aContainer);
 
   /**
@@ -4729,16 +4736,19 @@ class Document : public nsINode,
   int32_t mGeneration;
 
   // Cached TabSizes values for the document.
   int32_t mCachedTabSizeGeneration;
   nsTabSizes mCachedTabSizes;
 
   bool mInRDMPane;
 
+  // The principal to use for the storage area of this document.
+  nsCOMPtr<nsIPrincipal> mIntrinsicStoragePrincipal;
+
  public:
   // Needs to be public because the bindings code pokes at it.
   js::ExpandoAndGeneration mExpandoAndGeneration;
 
   bool HasPendingInitialTranslation() { return mPendingInitialTranslation; }
 
   void TraceProtos(JSTracer* aTrc);
 };
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8221,17 +8221,17 @@ nsContentUtils::StorageAccess nsContentU
   }
 
   // No document? Let's return a generic rejected reason.
   return StorageAccess::eDeny;
 }
 
 // static, public
 nsContentUtils::StorageAccess nsContentUtils::StorageAllowedForDocument(
-    Document* aDoc) {
+    const Document* aDoc) {
   MOZ_ASSERT(aDoc);
 
   if (nsPIDOMWindowInner* inner = aDoc->GetInnerWindow()) {
     nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal();
     // Note that GetChannel() below may return null, but that's OK, since the
     // callee is able to deal with a null channel argument, and if passed null,
     // will only fail to notify the UI in case storage gets blocked.
     nsIChannel* channel = aDoc->GetChannel();
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2990,17 +2990,17 @@ class nsContentUtils {
    * Checks if storage for the given document is permitted by a combination of
    * the user's preferences, and whether the document's window is a third-party
    * iframe.
    *
    * Note, this may be used on documents during the loading process where
    * the window's extant document has not been set yet.  The code in
    * StorageAllowedForWindow(), however, will not work in these cases.
    */
-  static StorageAccess StorageAllowedForDocument(Document* aDoc);
+  static StorageAccess StorageAllowedForDocument(const Document* aDoc);
 
   /*
    * Checks if storage should be allowed for a new window with the given
    * principal, load URI, and parent.
    */
   static StorageAccess StorageAllowedForNewWindow(nsIPrincipal* aPrincipal,
                                                   nsIURI* aURI,
                                                   nsPIDOMWindowInner* aParent);
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -1123,16 +1123,17 @@ void nsGlobalWindowInner::FreeInnerObjec
 
 #if defined(MOZ_WIDGET_ANDROID)
   mOrientationChangeObserver = nullptr;
 #endif
 
   if (mDoc) {
     // Remember the document's principal and URI.
     mDocumentPrincipal = mDoc->NodePrincipal();
+    mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
     mDocumentURI = mDoc->GetDocumentURI();
     mDocBaseURI = mDoc->GetDocBaseURI();
 
     while (mDoc->EventHandlingSuppressed()) {
       mDoc->UnsuppressEventHandlingAndFireEvents(false);
     }
 
     if (mObservingDidRefresh) {
@@ -1352,16 +1353,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
   for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
     cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
   }
 
@@ -1455,16 +1457,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
         ->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
   }
   if (tmp->mIndexedDB) {
     tmp->mIndexedDB->DisconnectFromGlobal(tmp);
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
 
@@ -2043,16 +2046,39 @@ nsIPrincipal* nsGlobalWindowInner::GetPr
 
   if (objPrincipal) {
     return objPrincipal->GetPrincipal();
   }
 
   return nullptr;
 }
 
+nsIPrincipal* nsGlobalWindowInner::GetEffectiveStoragePrincipal() {
+  if (mDoc) {
+    // If we have a document, get the principal from the document
+    return mDoc->EffectiveStoragePrincipal();
+  }
+
+  if (mDocumentStoragePrincipal) {
+    return mDocumentStoragePrincipal;
+  }
+
+  // If we don't have a storage principal and we don't have a document we ask
+  // the parent window for the storage principal.
+
+  nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
+      do_QueryInterface(GetParentInternal());
+
+  if (objPrincipal) {
+    return objPrincipal->GetEffectiveStoragePrincipal();
+  }
+
+  return nullptr;
+}
+
 //*****************************************************************************
 // nsGlobalWindowInner::nsIDOMWindow
 //*****************************************************************************
 
 bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) {
   mAudioContexts.AppendElement(aAudioContext);
 
   // Return true if the context should be muted and false if not.
@@ -2790,20 +2816,19 @@ bool nsGlobalWindowInner::MayResolve(jsi
     // We only resolve .controllers/.Controllers in release builds and on
     // non-chrome windows, but let's not worry about any of that stuff.
     return true;
   }
 
   return WebIDLGlobalNameHash::MayResolve(aId);
 }
 
-void nsGlobalWindowInner::GetOwnPropertyNames(JSContext* aCx,
-                                              JS::MutableHandleVector<jsid> aNames,
-                                              bool aEnumerableOnly,
-                                              ErrorResult& aRv) {
+void nsGlobalWindowInner::GetOwnPropertyNames(
+    JSContext* aCx, JS::MutableHandleVector<jsid> aNames, bool aEnumerableOnly,
+    ErrorResult& aRv) {
   if (aEnumerableOnly) {
     // The names we would return from here get defined on the window via one of
     // two codepaths.  The ones coming from the WebIDLGlobalNameHash will end up
     // in the DefineConstructor function in BindingUtils, which always defines
     // things as non-enumerable.  The ones coming from the script namespace
     // manager get defined by our resolve hook using FillPropertyDescriptor with
     // 0 for the property attributes, so non-enumerable as well.
     //
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -255,16 +255,18 @@ class nsGlobalWindowInner final : public
 
   virtual nsIScriptContext* GetScriptContext() override;
 
   virtual bool IsBlackForCC(bool aTracingNeeded = true) override;
 
   // nsIScriptObjectPrincipal
   virtual nsIPrincipal* GetPrincipal() override;
 
+  virtual nsIPrincipal* GetEffectiveStoragePrincipal() override;
+
   // nsIDOMWindow
   NS_DECL_NSIDOMWINDOW
 
   // nsIDOMChromeWindow (only implemented on chrome windows)
   NS_DECL_NSIDOMCHROMEWINDOW
 
   void CaptureEvents();
   void ReleaseEvents();
@@ -1289,16 +1291,18 @@ class nsGlobalWindowInner final : public
   RefPtr<nsHistory> mHistory;
   RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
 
   nsTObserverArray<RefPtr<mozilla::dom::SharedWorker>> mSharedWorkers;
 
   RefPtr<mozilla::dom::VisualViewport> mVisualViewport;
 
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
+  nsCOMPtr<nsIPrincipal> mDocumentStoragePrincipal;
+
   // mTabChild is only ever populated in the content process.
   nsCOMPtr<nsITabChild> mTabChild;
 
   uint32_t mSuspendDepth;
   uint32_t mFreezeDepth;
 
 #ifdef DEBUG
   uint32_t mSerial;
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -471,18 +471,19 @@ class nsOuterWindowProxy : public MaybeC
    * enumerating.
    *
    * We have to override this because js::Wrapper overrides it, but we want
    * different behavior from js::Wrapper.
    *
    * "proxy" is the WindowProxy object involved.  It may not be same-compartment
    * with cx.
    */
-  bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
-                                    JS::MutableHandleVector<jsid> props) const override;
+  bool getOwnEnumerablePropertyKeys(
+      JSContext* cx, JS::Handle<JSObject*> proxy,
+      JS::MutableHandleVector<jsid> props) const override;
 
   /**
    * Hook used by SpiderMonkey to implement Object.prototype.toString.
    */
   const char* className(JSContext* cx,
                         JS::Handle<JSObject*> wrapper) const override;
 
   void finalize(JSFreeOp* fop, JSObject* proxy) const override;
@@ -728,19 +729,19 @@ bool nsOuterWindowProxy::definePropertyS
     return true;
   }
 #endif
 
   result.succeed();
   return true;
 }
 
-bool nsOuterWindowProxy::ownPropertyKeys(JSContext* cx,
-                                         JS::Handle<JSObject*> proxy,
-                                         JS::MutableHandleVector<jsid> props) const {
+bool nsOuterWindowProxy::ownPropertyKeys(
+    JSContext* cx, JS::Handle<JSObject*> proxy,
+    JS::MutableHandleVector<jsid> props) const {
   // Just our indexed stuff followed by our "normal" own property names.
   if (!AppendIndexedPropertyNames(proxy, props)) {
     return false;
   }
 
   if (IsPlatformObjectSameOrigin(cx, proxy)) {
     // When forwarding to js::Wrapper, we should just enter the Realm of proxy
     // for now.  That's what js::Wrapper expects, and since we're same-origin
@@ -933,17 +934,18 @@ bool nsOuterWindowProxy::set(JSContext* 
   }
 
   JS_MarkCrossZoneId(cx, id);
 
   return js::Wrapper::set(cx, proxy, id, wrappedArg, wrappedReceiver, result);
 }
 
 bool nsOuterWindowProxy::getOwnEnumerablePropertyKeys(
-    JSContext* cx, JS::Handle<JSObject*> proxy, JS::MutableHandleVector<jsid> props) const {
+    JSContext* cx, JS::Handle<JSObject*> proxy,
+    JS::MutableHandleVector<jsid> props) const {
   // We could just stop overring getOwnEnumerablePropertyKeys and let our
   // superclasses deal (by falling back on the BaseProxyHandler implementation
   // that uses a combination of ownPropertyKeys and getOwnPropertyDescriptor to
   // only return the enumerable ones.  But maybe there's value in having
   // somewhat faster for-in iteration on Window objects...
 
   // Like ownPropertyKeys, our indexed stuff followed by our "normal" enumerable
   // own property names.
@@ -1396,16 +1398,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
 
   // Traverse stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerForInitialContentBrowser)
@@ -1422,16 +1425,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
 
   // Unlink stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerForInitialContentBrowser)
@@ -1889,16 +1893,18 @@ static nsresult CreateNativeGlobalForInn
   return NS_OK;
 }
 
 nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
                                              nsISupports* aState,
                                              bool aForceReuseInnerWindow) {
   MOZ_ASSERT(mDocumentPrincipal == nullptr,
              "mDocumentPrincipal prematurely set!");
+  MOZ_ASSERT(mDocumentStoragePrincipal == nullptr,
+             "mDocumentStoragePrincipal prematurely set!");
   MOZ_ASSERT(aDocument);
 
   // Bail out early if we're in process of closing down the window.
   NS_ENSURE_STATE(!mCleanedUp);
 
   NS_ASSERTION(!GetCurrentInnerWindow() ||
                    GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
                "Uh, mDoc doesn't match the current inner window "
@@ -2441,16 +2447,17 @@ void nsGlobalWindowOuter::DetachFromDocS
 
   nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal();
 
   if (currentInner) {
     NS_ASSERTION(mDoc, "Must have doc!");
 
     // Remember the document's principal and URI.
     mDocumentPrincipal = mDoc->NodePrincipal();
+    mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
     mDocumentURI = mDoc->GetDocumentURI();
 
     // Release our document reference
     DropOuterWindowDocs();
   }
 
   ClearControllers();
 
@@ -2756,16 +2763,39 @@ nsIPrincipal* nsGlobalWindowOuter::GetPr
 
   if (objPrincipal) {
     return objPrincipal->GetPrincipal();
   }
 
   return nullptr;
 }
 
+nsIPrincipal* nsGlobalWindowOuter::GetEffectiveStoragePrincipal() {
+  if (mDoc) {
+    // If we have a document, get the principal from the document
+    return mDoc->EffectiveStoragePrincipal();
+  }
+
+  if (mDocumentStoragePrincipal) {
+    return mDocumentStoragePrincipal;
+  }
+
+  // If we don't have a storage principal and we don't have a document we ask
+  // the parent window for the storage principal.
+
+  nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
+      do_QueryInterface(GetParentInternal());
+
+  if (objPrincipal) {
+    return objPrincipal->GetEffectiveStoragePrincipal();
+  }
+
+  return nullptr;
+}
+
 //*****************************************************************************
 // nsGlobalWindowOuter::nsIDOMWindow
 //*****************************************************************************
 
 void nsPIDOMWindowOuter::SetInitialKeyboardIndicators(
     UIStateChangeType aShowAccelerators, UIStateChangeType aShowFocusRings) {
   MOZ_ASSERT(!GetCurrentInnerWindow());
 
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -242,16 +242,18 @@ class nsGlobalWindowOuter final : public
 
   void PoisonOuterWindowProxy(JSObject* aObject);
 
   virtual bool IsBlackForCC(bool aTracingNeeded = true) override;
 
   // nsIScriptObjectPrincipal
   virtual nsIPrincipal* GetPrincipal() override;
 
+  virtual nsIPrincipal* GetEffectiveStoragePrincipal() override;
+
   // nsIDOMWindow
   NS_DECL_NSIDOMWINDOW
 
   // nsIDOMChromeWindow (only implemented on chrome windows)
   NS_DECL_NSIDOMCHROMEWINDOW
 
   mozilla::dom::ChromeMessageBroadcaster* GetMessageManager();
   mozilla::dom::ChromeMessageBroadcaster* GetGroupMessageManager(
@@ -1093,16 +1095,17 @@ class nsGlobalWindowOuter final : public
 
   RefPtr<nsDOMWindowList> mFrames;
   RefPtr<nsDOMWindowUtils> mWindowUtils;
   nsString mStatus;
 
   RefPtr<mozilla::dom::Storage> mLocalStorage;
 
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
+  nsCOMPtr<nsIPrincipal> mDocumentStoragePrincipal;
 
 #ifdef DEBUG
   uint32_t mSerial;
 
   bool mSetOpenerWindowCalled;
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
--- a/dom/base/nsIScriptObjectPrincipal.h
+++ b/dom/base/nsIScriptObjectPrincipal.h
@@ -21,14 +21,16 @@ class nsIPrincipal;
 /**
  * JS Object Principal information.
  */
 class nsIScriptObjectPrincipal : public nsISupports {
  public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTOBJECTPRINCIPAL_IID)
 
   virtual nsIPrincipal* GetPrincipal() = 0;
+
+  virtual nsIPrincipal* GetEffectiveStoragePrincipal() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptObjectPrincipal,
                               NS_ISCRIPTOBJECTPRINCIPAL_IID)
 
 #endif  // nsIScriptObjectPrincipal_h__
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -217,20 +217,21 @@ void nsHTMLDocument::Reset(nsIChannel* a
   Document::Reset(aChannel, aLoadGroup);
 
   if (aChannel) {
     aChannel->GetLoadFlags(&mLoadFlags);
   }
 }
 
 void nsHTMLDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
-                                nsIPrincipal* aPrincipal) {
+                                nsIPrincipal* aPrincipal,
+                                nsIPrincipal* aStoragePrincipal) {
   mLoadFlags = nsIRequest::LOAD_NORMAL;
 
-  Document::ResetToURI(aURI, aLoadGroup, aPrincipal);
+  Document::ResetToURI(aURI, aLoadGroup, aPrincipal, aStoragePrincipal);
 
   mImages = nullptr;
   mApplets = nullptr;
   mEmbeds = nullptr;
   mLinks = nullptr;
   mAnchors = nullptr;
   mScripts = nullptr;
 
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -51,17 +51,18 @@ class nsHTMLDocument : public mozilla::d
   virtual nsresult Init() override;
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHTMLDocument, Document)
 
   // Document
   virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override;
   virtual void ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
-                          nsIPrincipal* aPrincipal) override;
+                          nsIPrincipal* aPrincipal,
+                          nsIPrincipal* aStoragePrincipal) override;
 
   virtual nsresult StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
                                      nsILoadGroup* aLoadGroup,
                                      nsISupports* aContainer,
                                      nsIStreamListener** aDocListener,
                                      bool aReset = true,
                                      nsIContentSink* aSink = nullptr) override;
 
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -10,16 +10,17 @@
  * https://w3c.github.io/pointerlock/#extensions-to-the-document-interface
  * https://w3c.github.io/pointerlock/#extensions-to-the-documentorshadowroot-mixin
  * https://w3c.github.io/page-visibility/#extensions-to-the-document-interface
  * https://drafts.csswg.org/cssom/#extensions-to-the-document-interface
  * https://drafts.csswg.org/cssom-view/#extensions-to-the-document-interface
  * https://wicg.github.io/feature-policy/#policy
  */
 
+interface Principal;
 interface WindowProxy;
 interface nsISupports;
 interface URI;
 interface nsIDocShell;
 interface nsILoadGroup;
 
 enum VisibilityState { "hidden", "visible" };
 
@@ -353,16 +354,20 @@ partial interface Document {
   [Func="IsChromeOrXBL"]
   Element? getBindingParent(Node node);
   [Throws, Func="IsChromeOrXBL", NeedsSubjectPrincipal]
   void loadBindingDocument(DOMString documentURL);
   // Creates a new XUL element regardless of the document's default type.
   [CEReactions, NewObject, Throws, Func="IsChromeOrXBL"]
   Element createXULElement(DOMString localName, optional (ElementCreationOptions or DOMString) options);
 
+  // The principal to use for the storage area of this document
+  [ChromeOnly]
+  readonly attribute Principal effectiveStoragePrincipal;
+
   // Touch bits
   // XXXbz I can't find the sane spec for this stuff, so just cribbing
   // from our xpidl for now.
   [NewObject, Func="nsGenericHTMLElement::LegacyTouchAPIEnabled"]
   Touch createTouch(optional Window? view = null,
                     optional EventTarget? target = null,
                     optional long identifier = 0,
                     optional long pageX = 0,
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -124,17 +124,17 @@ nsresult NS_NewDOMDocument(Document** aI
     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(d);
     NS_ASSERTION(htmlDoc, "HTML Document doesn't implement nsIHTMLDocument?");
     htmlDoc->SetCompatibilityMode(eCompatibility_FullStandards);
     htmlDoc->SetIsXHTML(isXHTML);
   }
   d->SetLoadedAsData(aLoadedAsData);
   d->SetDocumentURI(aDocumentURI);
   // Must set the principal first, since SetBaseURI checks it.
-  d->SetPrincipal(aPrincipal);
+  d->SetPrincipals(aPrincipal, aPrincipal);
   d->SetBaseURI(aBaseURI);
 
   // We need to set the script handling object after we set the principal such
   // that the doc group is assigned correctly.
   if (nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aEventObject)) {
     d->SetScriptHandlingObject(sgo);
   } else if (aEventObject) {
     d->SetScopeObject(aEventObject);
@@ -247,38 +247,40 @@ nsresult XMLDocument::Init() {
   return rv;
 }
 
 void XMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) {
   Document::Reset(aChannel, aLoadGroup);
 }
 
 void XMLDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
-                             nsIPrincipal* aPrincipal) {
+                             nsIPrincipal* aPrincipal,
+                             nsIPrincipal* aStoragePrincipal) {
   if (mChannelIsPending) {
     StopDocumentLoad();
     mChannel->Cancel(NS_BINDING_ABORTED);
     mChannelIsPending = false;
   }
 
-  Document::ResetToURI(aURI, aLoadGroup, aPrincipal);
+  Document::ResetToURI(aURI, aLoadGroup, aPrincipal, aStoragePrincipal);
 }
 
 bool XMLDocument::Load(const nsAString& aUrl, CallerType aCallerType,
                        ErrorResult& aRv) {
   bool hasHadScriptObject = true;
   nsIScriptGlobalObject* scriptObject =
       GetScriptHandlingObject(hasHadScriptObject);
   if (!scriptObject && hasHadScriptObject) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return false;
   }
 
   nsCOMPtr<Document> callingDoc = GetEntryDocument();
   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
+  nsCOMPtr<nsIPrincipal> storagePrincipal = EffectiveStoragePrincipal();
 
   // The callingDoc's Principal and doc's Principal should be the same
   if (callingDoc && (callingDoc->NodePrincipal() != principal)) {
     nsContentUtils::ReportToConsole(
         nsIScriptError::errorFlag, NS_LITERAL_CSTRING("DOM"), callingDoc,
         nsContentUtils::eDOM_PROPERTIES, "XMLDocumentLoadPrincipalMismatch");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return false;
@@ -365,17 +367,17 @@ bool XMLDocument::Load(const nsAString& 
   // and add ourselves to it. This way any pending requests
   // will be automatically aborted if the user leaves the page.
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   if (callingDoc) {
     loadGroup = callingDoc->GetDocumentLoadGroup();
   }
 
-  ResetToURI(uri, loadGroup, principal);
+  ResetToURI(uri, loadGroup, principal, storagePrincipal);
 
   mListenerManager = elm;
 
   // Create a channel
   nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::SameOriginChecker();
 
   nsCOMPtr<nsIChannel> channel;
   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active,
--- a/dom/xml/XMLDocument.h
+++ b/dom/xml/XMLDocument.h
@@ -21,17 +21,18 @@ namespace dom {
 class XMLDocument : public Document {
  public:
   explicit XMLDocument(const char* aContentType = "application/xml");
 
   NS_INLINE_DECL_REFCOUNTING_INHERITED(XMLDocument, Document)
 
   virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override;
   virtual void ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
-                          nsIPrincipal* aPrincipal) override;
+                          nsIPrincipal* aPrincipal,
+                          nsIPrincipal* aStoragePrincipal) override;
 
   virtual void SetSuppressParserErrorElement(bool aSuppress) override;
   virtual bool SuppressParserErrorElement() override;
 
   virtual void SetSuppressParserErrorConsoleMessages(bool aSuppress) override;
   virtual bool SuppressParserErrorConsoleMessages() override;
 
   virtual nsresult StartDocumentLoad(const char* aCommand, nsIChannel* channel,
--- a/dom/xslt/base/txURIUtils.cpp
+++ b/dom/xslt/base/txURIUtils.cpp
@@ -41,16 +41,17 @@ void URIUtils::resolveHref(const nsAStri
     dest.Append(resultHref);
   }
 }  //-- resolveHref
 
 // static
 void URIUtils::ResetWithSource(Document* aNewDoc, nsINode* aSourceNode) {
   nsCOMPtr<Document> sourceDoc = aSourceNode->OwnerDoc();
   nsIPrincipal* sourcePrincipal = sourceDoc->NodePrincipal();
+  nsIPrincipal* sourceStoragePrincipal = sourceDoc->EffectiveStoragePrincipal();
 
   // Copy the channel and loadgroup from the source document.
   nsCOMPtr<nsILoadGroup> loadGroup = sourceDoc->GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> channel = sourceDoc->GetChannel();
   if (!channel) {
     // Need to synthesize one
     nsresult rv = NS_NewChannel(
         getter_AddRefs(channel), sourceDoc->GetDocumentURI(), sourceDoc,
@@ -61,16 +62,16 @@ void URIUtils::ResetWithSource(Document*
         nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
 
     if (NS_FAILED(rv)) {
       return;
     }
   }
 
   aNewDoc->Reset(channel, loadGroup);
-  aNewDoc->SetPrincipal(sourcePrincipal);
+  aNewDoc->SetPrincipals(sourcePrincipal, sourceStoragePrincipal);
   aNewDoc->SetBaseURI(sourceDoc->GetDocBaseURI());
 
   // Copy charset
   aNewDoc->SetDocumentCharacterSetSource(
       sourceDoc->GetDocumentCharacterSetSource());
   aNewDoc->SetDocumentCharacterSet(sourceDoc->GetDocumentCharacterSet());
 }
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -188,17 +188,18 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHER
 // Document interface
 //
 
 void XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) {
   MOZ_ASSERT_UNREACHABLE("Reset");
 }
 
 void XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
-                             nsIPrincipal* aPrincipal) {
+                             nsIPrincipal* aPrincipal,
+                             nsIPrincipal* aStoragePrincipal) {
   MOZ_ASSERT_UNREACHABLE("ResetToURI");
 }
 
 void XULDocument::SetContentType(const nsAString& aContentType) {
   NS_ASSERTION(
       aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
       "xul-documents always has content-type application/vnd.mozilla.xul+xml");
   // Don't do anything, xul always has the mimetype
@@ -237,20 +238,31 @@ nsresult XULDocument::StartDocumentLoad(
   // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mOriginalURI = mDocumentURI;
 
   // Get the document's principal
   nsCOMPtr<nsIPrincipal> principal;
-  nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
-      mChannel, getter_AddRefs(principal));
+  nsCOMPtr<nsIPrincipal> storagePrincipal;
+  rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipals(
+      mChannel, getter_AddRefs(principal), getter_AddRefs(storagePrincipal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool equal = principal->Equals(storagePrincipal);
+
   principal = MaybeDowngradePrincipal(principal);
-  SetPrincipal(principal);
+  if (equal) {
+    storagePrincipal = principal;
+  } else {
+    storagePrincipal = MaybeDowngradePrincipal(storagePrincipal);
+  }
+
+  SetPrincipals(principal, storagePrincipal);
 
   ResetStylesheetsToURI(mDocumentURI);
 
   RetrieveRelevantHeaders(aChannel);
 
   mParser = new mozilla::parser::PrototypeDocumentParser(mDocumentURI, this);
   nsCOMPtr<nsIStreamListener> listener = mParser->GetStreamListener();
   listener.forget(aDocListener);
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -54,17 +54,18 @@ class XULDocument final : public XMLDocu
   XULDocument();
 
   // nsISupports interface
   NS_DECL_ISUPPORTS_INHERITED
 
   // Document interface
   virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override;
   virtual void ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
-                          nsIPrincipal* aPrincipal) override;
+                          nsIPrincipal* aPrincipal,
+                          nsIPrincipal* aStoragePrincipal) override;
 
   virtual nsresult StartDocumentLoad(const char* aCommand, nsIChannel* channel,
                                      nsILoadGroup* aLoadGroup,
                                      nsISupports* aContainer,
                                      nsIStreamListener** aDocListener,
                                      bool aReset = true,
                                      nsIContentSink* aSink = nullptr) override;
   virtual void EndLoad() override;
--- a/js/xpconnect/src/BackstagePass.h
+++ b/js/xpconnect/src/BackstagePass.h
@@ -24,16 +24,20 @@ class BackstagePass : public nsIGlobalOb
                       public nsSupportsWeakReference {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIXPCSCRIPTABLE
   NS_DECL_NSICLASSINFO
 
   virtual nsIPrincipal* GetPrincipal() override { return mPrincipal; }
 
+  virtual nsIPrincipal* GetEffectiveStoragePrincipal() override {
+    return nullptr;
+  }
+
   virtual JSObject* GetGlobalJSObject() override;
 
   void ForgetGlobalObject() { mWrapper = nullptr; }
 
   void SetGlobalObject(JSObject* global);
 
   explicit BackstagePass(nsIPrincipal* prin)
       : mPrincipal(prin), mWrapper(nullptr) {}
--- a/js/xpconnect/src/SandboxPrivate.h
+++ b/js/xpconnect/src/SandboxPrivate.h
@@ -40,16 +40,18 @@ class SandboxPrivate : public nsIGlobalO
   static SandboxPrivate* GetPrivate(JSObject* obj) {
     // The type used to cast to void needs to match the one in Create.
     return static_cast<SandboxPrivate*>(
         static_cast<nsIScriptObjectPrincipal*>(JS_GetPrivate(obj)));
   }
 
   nsIPrincipal* GetPrincipal() override { return mPrincipal; }
 
+  nsIPrincipal* GetEffectiveStoragePrincipal() override { return nullptr; }
+
   JSObject* GetGlobalJSObject() override { return GetWrapper(); }
 
   void ForgetGlobalObject(JSObject* obj) { ClearWrapper(obj); }
 
   virtual JSObject* WrapObject(JSContext* cx,
                                JS::Handle<JSObject*> aGivenProto) override {
     MOZ_CRASH("SandboxPrivate doesn't use DOM bindings!");
   }
--- a/layout/build/nsContentDLF.cpp
+++ b/layout/build/nsContentDLF.cpp
@@ -272,17 +272,17 @@ already_AddRefed<Document> nsContentDLF:
   }
 
   // initialize
   nsCOMPtr<nsIURI> uri;
   NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:blank"));
   if (!uri) {
     return nullptr;
   }
-  blankDoc->ResetToURI(uri, aLoadGroup, aPrincipal);
+  blankDoc->ResetToURI(uri, aLoadGroup, aPrincipal, aPrincipal);
   blankDoc->SetContainer(aContainer);
 
   // add some simple content structure
   nsNodeInfoManager* nim = blankDoc->NodeInfoManager();
 
   RefPtr<mozilla::dom::NodeInfo> htmlNodeInfo;
 
   // generate an html html element
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -2228,16 +2228,22 @@ VARCACHE_PREF(
 )
 
 VARCACHE_PREF(
   "privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts",
    privacy_resistFingerprinting_autoDeclineNoUserInputCanvasPrompts,
   RelaxedAtomicBool, false
 )
 
+VARCACHE_PREF(
+  "privacy.storagePrincipal.enabledForTrackers",
+   privacy_storagePrincipal_enabledForTrackers,
+  RelaxedAtomicBool, false
+)
+
 // Password protection
 VARCACHE_PREF(
   "browser.safebrowsing.passwords.enabled",
    browser_safebrowsing_passwords_enabled,
   bool, false
 )
 
 // Malware protection
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -184,16 +184,22 @@ LoadInfo::LoadInfo(
           mTopLevelStorageAreaPrincipal =
               innerWindow->GetTopLevelStorageAreaPrincipal();
         } else if (contextOuter->IsTopLevelWindow()) {
           Document* doc = innerWindow->GetExtantDoc();
           if (!doc || (!doc->StorageAccessSandboxed() &&
                        !nsContentUtils::IsInPrivateBrowsing(doc))) {
             mTopLevelStorageAreaPrincipal = innerWindow->GetPrincipal();
           }
+
+          // If this is the first level iframe, innerWindow is our top-level
+          // principal.
+          if (!mTopLevelPrincipal) {
+            mTopLevelPrincipal = innerWindow->GetPrincipal();
+          }
         }
 
         mDocumentHasLoaded = innerWindow->IsDocumentLoaded();
 
         if (innerWindow->IsFrame()) {
           // For resources within iframes, we actually want the
           // top-level document's flag, not the iframe document's.
           mDocumentHasLoaded = false;
--- a/parser/prototype/PrototypeDocumentParser.cpp
+++ b/parser/prototype/PrototypeDocumentParser.cpp
@@ -79,17 +79,18 @@ PrototypeDocumentParser::Parse(nsIURI* a
   //        due to being accessed before a profile has been selected (e.g.
   //        loading chrome for the profile manager itself). This must be
   //        parsed from disk.
   nsresult rv;
   if (proto) {
     mCurrentPrototype = proto;
 
     // Set up the right principal on the document.
-    mDocument->SetPrincipal(proto->DocumentPrincipal());
+    mDocument->SetPrincipals(proto->DocumentPrincipal(),
+                             proto->DocumentPrincipal());
   } else {
     // It's just a vanilla document load. Create a parser to deal
     // with the stream n' stuff.
 
     nsCOMPtr<nsIParser> parser;
     // Get the document's principal
     nsCOMPtr<nsIPrincipal> principal = mDocument->NodePrincipal();
     rv =
@@ -180,17 +181,17 @@ nsresult PrototypeDocumentParser::Prepar
 
   // Store the new prototype right away so if there are multiple requests
   // for the same document they all get the same prototype.
   if (IsChromeURI(mDocumentURI) &&
       nsXULPrototypeCache::GetInstance()->IsEnabled()) {
     nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
   }
 
-  mDocument->SetPrincipal(aDocumentPrincipal);
+  mDocument->SetPrincipals(aDocumentPrincipal, aDocumentPrincipal);
 
   // Create a XUL content sink, a parser, and kick off a load for
   // the document.
   RefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
 
   rv = sink->Init(mDocument, mCurrentPrototype);
   NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
   if (NS_FAILED(rv)) return rv;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "StoragePrincipalHelper.h"
+
+#include "mozilla/ScopeExit.h"
+#include "mozilla/StaticPrefs.h"
+#include "nsContentUtils.h"
+#include "nsIHttpChannel.h"
+
+namespace mozilla {
+
+// static
+nsresult StoragePrincipalHelper::Create(nsIChannel* aChannel,
+                                        nsIPrincipal* aPrincipal,
+                                        nsIPrincipal** aStoragePrincipal) {
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aStoragePrincipal);
+
+  auto scopeExit = MakeScopeExit([&] {
+    nsCOMPtr<nsIPrincipal> storagePrincipal = aPrincipal;
+    storagePrincipal.forget(aStoragePrincipal);
+  });
+
+  if (!StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+    return NS_OK;
+  }
+
+  // Let's use the storage principal only if we need to partition the cookie
+  // jar.
+  nsContentUtils::StorageAccess access =
+      nsContentUtils::StorageAllowedForChannel(aChannel);
+  if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+  if (!httpChannel) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(httpChannel->IsThirdPartyTrackingResource());
+
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+  nsCOMPtr<nsIPrincipal> toplevelPrincipal = loadInfo->GetTopLevelPrincipal();
+  if (!toplevelPrincipal) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIURI> principalURI;
+  nsresult rv = toplevelPrincipal->GetURI(getter_AddRefs(principalURI));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  scopeExit.release();
+
+  nsCOMPtr<nsIPrincipal> storagePrincipal =
+      BasePrincipal::Cast(aPrincipal)
+          ->CloneForcingFirstPartyDomain(principalURI);
+
+  storagePrincipal.forget(aStoragePrincipal);
+  return NS_OK;
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.h
@@ -0,0 +1,23 @@
+/* -*- 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_StoragePrincipalHelper_h
+#define mozilla_StoragePrincipalHelper_h
+
+class nsIChannel;
+class nsIPrincipal;
+
+namespace mozilla {
+
+class StoragePrincipalHelper final {
+ public:
+  static nsresult Create(nsIChannel* aChannel, nsIPrincipal* aPrincipal,
+                         nsIPrincipal** aStoragePrincipal);
+};
+
+}  // namespace mozilla
+
+#endif  // mozilla_StoragePrincipalHelper_h
--- a/toolkit/components/antitracking/moz.build
+++ b/toolkit/components/antitracking/moz.build
@@ -4,20 +4,22 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'DOM: Security')
 
 EXPORTS.mozilla = [
     'AntiTrackingCommon.h',
+    'StoragePrincipalHelper.h',
 ]
 
 UNIFIED_SOURCES += [
     'AntiTrackingCommon.cpp',
+    'StoragePrincipalHelper.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/extensions/cookie',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')