Bug 965637: Move CSP from Principal into Client, part 1: backend changes. r=mccr8
authorChristoph Kerschbaumer <ckerschb@christophkerschbaumer.com>
Tue, 21 May 2019 23:14:27 +0000
changeset 474862 ddf4012a7652e36d144fc43bc99335276deeafbe
parent 474861 6c2047bc2f3e9e2e165d0c03177ae4212a912077
child 474863 3399e7c519424a82824f48abd3b2a4d75346d723
push id36047
push usernerli@mozilla.com
push dateWed, 22 May 2019 03:40:58 +0000
treeherdermozilla-central@267ddc3595fe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs965637
milestone69.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 965637: Move CSP from Principal into Client, part 1: backend changes. r=mccr8 Differential Revision: https://phabricator.services.mozilla.com/D27654
caps/BasePrincipal.cpp
caps/BasePrincipal.h
caps/ContentPrincipal.cpp
caps/ExpandedPrincipal.cpp
caps/ExpandedPrincipal.h
caps/NullPrincipal.cpp
caps/NullPrincipal.h
caps/SystemPrincipal.cpp
caps/SystemPrincipal.h
caps/nsIPrincipal.idl
caps/nsJSPrincipals.cpp
caps/nsScriptSecurityManager.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
dom/base/Document.cpp
dom/base/Document.h
dom/base/Location.cpp
dom/base/nsContentPolicy.cpp
dom/base/nsContentUtils.cpp
dom/base/nsFrameLoader.cpp
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsGlobalWindowOuter.h
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/base/nsJSTimeoutHandler.cpp
dom/base/nsPIDOMWindow.h
dom/base/nsStyleLinkElement.cpp
dom/base/nsStyledElement.cpp
dom/cache/DBSchema.cpp
dom/clients/manager/ClientIPCTypes.ipdlh
dom/clients/manager/ClientInfo.cpp
dom/clients/manager/ClientInfo.h
dom/clients/manager/ClientNavigateOpChild.cpp
dom/clients/manager/ClientSource.cpp
dom/clients/manager/ClientSource.h
dom/events/EventListenerManager.cpp
dom/html/HTMLFormSubmission.cpp
dom/html/HTMLMetaElement.cpp
dom/html/HTMLSharedElement.cpp
dom/html/nsHTMLDocument.cpp
dom/interfaces/security/nsIContentSecurityPolicy.idl
dom/ipc/ContentChild.cpp
dom/jsurl/nsJSProtocolHandler.cpp
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/script/ScriptLoader.cpp
dom/security/CSPEvalChecker.cpp
dom/security/FramingChecker.cpp
dom/security/FramingChecker.h
dom/security/nsCSPContext.cpp
dom/security/nsCSPContext.h
dom/security/nsCSPService.cpp
dom/security/nsCSPUtils.cpp
dom/security/nsCSPUtils.h
dom/security/nsContentSecurityManager.cpp
dom/smil/SMILCSSValueType.cpp
dom/webidl/Document.webidl
ipc/glue/BackgroundUtils.cpp
ipc/glue/BackgroundUtils.h
ipc/glue/PBackgroundSharedTypes.ipdlh
layout/style/nsStyleUtil.cpp
layout/style/nsStyleUtil.h
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/ipc/NeckoChannelParams.ipdlh
parser/html/nsHtml5TreeOpExecutor.cpp
toolkit/components/antitracking/StoragePrincipalHelper.cpp
toolkit/components/windowwatcher/nsWindowWatcher.cpp
toolkit/mozapps/extensions/AddonContentPolicy.cpp
xpfe/appshell/nsAppShellService.cpp
xpfe/appshell/nsWebShellWindow.cpp
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -2,33 +2,30 @@
 /* vim: set ts=2 sw=2 et 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/BasePrincipal.h"
 
 #include "nsDocShell.h"
-#include "nsIContentSecurityPolicy.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIStandardURL.h"
 
 #include "ExpandedPrincipal.h"
 #include "nsNetUtil.h"
 #include "nsIURIWithSpecialOrigin.h"
 #include "nsScriptSecurityManager.h"
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/ContentPrincipal.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
 #include "mozilla/dom/ChromeUtils.h"
-#include "mozilla/dom/CSPDictionariesBinding.h"
-#include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/ToJSValue.h"
 
 namespace mozilla {
 
 BasePrincipal::BasePrincipal(PrincipalKind aKind)
     : mKind(aKind), mHasExplicitDomain(false), mInitialized(false) {}
 
 BasePrincipal::~BasePrincipal() {}
@@ -161,98 +158,16 @@ BasePrincipal::CheckMayLoad(nsIURI* aURI
           mOriginAttributes.mPrivateBrowsingId > 0);
     }
   }
 
   return NS_ERROR_DOM_BAD_URI;
 }
 
 NS_IMETHODIMP
-BasePrincipal::GetCsp(nsIContentSecurityPolicy** aCsp) {
-  NS_IF_ADDREF(*aCsp = mCSP);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-BasePrincipal::SetCsp(nsIContentSecurityPolicy* aCsp) {
-  // Never destroy an existing CSP on the principal.
-  // This method should only be called in rare cases.
-
-  MOZ_ASSERT(!mCSP, "do not destroy an existing CSP");
-  if (mCSP) {
-    return NS_ERROR_ALREADY_INITIALIZED;
-  }
-
-  mCSP = aCsp;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-BasePrincipal::EnsureCSP(dom::Document* aDocument,
-                         nsIContentSecurityPolicy** aCSP) {
-  if (mCSP) {
-    // if there is a CSP already associated with this principal
-    // then just return that - do not overwrite it!!!
-    NS_IF_ADDREF(*aCSP = mCSP);
-    return NS_OK;
-  }
-
-  nsresult rv = NS_OK;
-  mCSP = do_CreateInstance("@mozilla.org/cspcontext;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Store the request context for violation reports
-  rv = aDocument ? mCSP->SetRequestContext(aDocument, nullptr)
-                 : mCSP->SetRequestContext(nullptr, this);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_IF_ADDREF(*aCSP = mCSP);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-BasePrincipal::GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) {
-  NS_IF_ADDREF(*aPreloadCSP = mPreloadCSP);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-BasePrincipal::EnsurePreloadCSP(dom::Document* aDocument,
-                                nsIContentSecurityPolicy** aPreloadCSP) {
-  if (mPreloadCSP) {
-    // if there is a speculative CSP already associated with this principal
-    // then just return that - do not overwrite it!!!
-    NS_IF_ADDREF(*aPreloadCSP = mPreloadCSP);
-    return NS_OK;
-  }
-
-  nsresult rv = NS_OK;
-  mPreloadCSP = do_CreateInstance("@mozilla.org/cspcontext;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Store the request context for violation reports
-  rv = aDocument ? mPreloadCSP->SetRequestContext(aDocument, nullptr)
-                 : mPreloadCSP->SetRequestContext(nullptr, this);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_IF_ADDREF(*aPreloadCSP = mPreloadCSP);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-BasePrincipal::GetCspJSON(nsAString& outCSPinJSON) {
-  outCSPinJSON.Truncate();
-  dom::CSPPolicies jsonPolicies;
-
-  if (!mCSP) {
-    jsonPolicies.ToJSON(outCSPinJSON);
-    return NS_OK;
-  }
-  return mCSP->ToJSON(outCSPinJSON);
-}
-
-NS_IMETHODIMP
 BasePrincipal::GetIsNullPrincipal(bool* aResult) {
   *aResult = Kind() == eNullPrincipal;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetIsCodebasePrincipal(bool* aResult) {
   *aResult = Kind() == eCodebasePrincipal;
@@ -504,30 +419,16 @@ void BasePrincipal::FinishInit(BasePrinc
 
   // First compute the origin suffix since it's infallible.
   nsAutoCString originSuffix;
   mOriginAttributes.CreateSuffix(originSuffix);
   mOriginSuffix = NS_Atomize(originSuffix);
 
   mOriginNoSuffix = aOther->mOriginNoSuffix;
   mHasExplicitDomain = aOther->mHasExplicitDomain;
-
-  if (aOther->mPreloadCSP) {
-    mPreloadCSP = do_CreateInstance("@mozilla.org/cspcontext;1");
-    nsCSPContext* preloadCSP = static_cast<nsCSPContext*>(mPreloadCSP.get());
-    preloadCSP->InitFromOther(
-        static_cast<nsCSPContext*>(aOther->mPreloadCSP.get()), nullptr, this);
-  }
-
-  if (aOther->mCSP) {
-    mCSP = do_CreateInstance("@mozilla.org/cspcontext;1");
-    nsCSPContext* csp = static_cast<nsCSPContext*>(mCSP.get());
-    csp->InitFromOther(static_cast<nsCSPContext*>(aOther->mCSP.get()), nullptr,
-                       this);
-  }
 }
 
 bool SiteIdentifier::Equals(const SiteIdentifier& aOther) const {
   MOZ_ASSERT(IsInitialized());
   MOZ_ASSERT(aOther.IsInitialized());
   return mPrincipal->FastEquals(aOther.mPrincipal);
 }
 
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -101,24 +101,16 @@ class BasePrincipal : public nsJSPrincip
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD SubsumesConsideringDomain(nsIPrincipal* other,
                                        bool* _retval) final;
   NS_IMETHOD SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* other,
                                                   bool* _retval) final;
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report,
                           bool allowIfInheritsPrincipal) final;
   NS_IMETHOD GetAddonPolicy(nsISupports** aResult) final;
-  NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
-  NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
-  NS_IMETHOD EnsureCSP(dom::Document* aDocument,
-                       nsIContentSecurityPolicy** aCSP) override;
-  NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
-  NS_IMETHOD EnsurePreloadCSP(dom::Document* aDocument,
-                              nsIContentSecurityPolicy** aCSP) override;
-  NS_IMETHOD GetCspJSON(nsAString& outCSPinJSON) override;
   NS_IMETHOD GetIsNullPrincipal(bool* aResult) override;
   NS_IMETHOD GetIsCodebasePrincipal(bool* aResult) override;
   NS_IMETHOD GetIsExpandedPrincipal(bool* aResult) override;
   NS_IMETHOD GetIsSystemPrincipal(bool* aResult) override;
   NS_IMETHOD GetIsAddonOrExpandedAddonPrincipal(bool* aResult) override;
   NS_IMETHOD GetOriginAttributes(JSContext* aCx,
                                  JS::MutableHandle<JS::Value> aVal) final;
   NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
@@ -244,19 +236,16 @@ class BasePrincipal : public nsJSPrincip
   // Either of these functions should be called as the last step of the
   // initialization of the principal objects.  It's typically called as the
   // last step from the Init() method of the child classes.
   void FinishInit(const nsACString& aOriginNoSuffix,
                   const OriginAttributes& aOriginAttributes);
   void FinishInit(BasePrincipal* aOther,
                   const OriginAttributes& aOriginAttributes);
 
-  nsCOMPtr<nsIContentSecurityPolicy> mCSP;
-  nsCOMPtr<nsIContentSecurityPolicy> mPreloadCSP;
-
  private:
   static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(
       nsIURI* aURI, const OriginAttributes& aAttrs,
       const nsACString& aOriginNoSuffix);
 
   inline bool FastSubsumesIgnoringFPD(
       nsIPrincipal* aOther, DocumentDomainConsideration aConsideration);
 
--- a/caps/ContentPrincipal.cpp
+++ b/caps/ContentPrincipal.cpp
@@ -25,17 +25,16 @@
 #include "nsIObjectOutputStream.h"
 #include "nsIProtocolHandler.h"
 #include "nsError.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsNetCID.h"
 #include "js/Wrapper.h"
 
 #include "mozilla/dom/BlobURLProtocolHandler.h"
-#include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/ExtensionPolicyService.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/HashFunctions.h"
 
 using namespace mozilla;
 
@@ -46,20 +45,16 @@ static inline ExtensionPolicyService& EP
 NS_IMPL_CLASSINFO(ContentPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
                   NS_PRINCIPAL_CID)
 NS_IMPL_QUERY_INTERFACE_CI(ContentPrincipal, nsIPrincipal, nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER(ContentPrincipal, nsIPrincipal, nsISerializable)
 
 ContentPrincipal::ContentPrincipal() : BasePrincipal(eCodebasePrincipal) {}
 
 ContentPrincipal::~ContentPrincipal() {
-  // let's clear the principal within the csp to avoid a tangling pointer
-  if (mCSP) {
-    static_cast<nsCSPContext*>(mCSP.get())->clearLoadingPrincipal();
-  }
 }
 
 nsresult ContentPrincipal::Init(nsIURI* aCodebase,
                                 const OriginAttributes& aOriginAttributes,
                                 const nsACString& aOriginNoSuffix) {
   NS_ENSURE_ARG(aCodebase);
 
   // Assert that the URI we get here isn't any of the schemes that we know we
@@ -576,33 +571,36 @@ ContentPrincipal::Read(nsIObjectInputStr
   nsAutoCString suffix;
   rv = aStream->ReadCString(suffix);
   NS_ENSURE_SUCCESS(rv, rv);
 
   OriginAttributes attrs;
   bool ok = attrs.PopulateFromSuffix(suffix);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
-  rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
-  NS_ENSURE_SUCCESS(rv, rv);
+  // Since Bug 965637 we do not serialize the CSP within the
+  // Principal anymore. Nevertheless there might still be
+  // serialized Principals that do have a serialized CSP.
+  // For now, we just read the CSP here but do not actually
+  // consume it. Please note that we deliberately ignore
+  // the return value to avoid CSP deserialization problems.
+  // After Bug 1508939 we will have a new serialization for
+  // Principals which allows us to update the code here.
+  // Additionally, the format for serialized CSPs changed
+  // within Bug 965637 which also can cause failures within
+  // the CSP deserialization code.
+  Unused << NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
 
   nsAutoCString originNoSuffix;
   rv = GenerateOriginNoSuffixFromURI(codebase, originNoSuffix);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = Init(codebase, attrs, originNoSuffix);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mCSP = do_QueryInterface(supports, &rv);
-  // make sure setRequestContext is called after Init(),
-  // to make sure  the principals URI been initalized.
-  if (mCSP) {
-    mCSP->SetRequestContext(nullptr, this);
-  }
-
   // Note: we don't call SetDomain here because we don't need the wrapper
   // recomputation code there (we just created this principal).
   mDomain = domain;
   if (mDomain) {
     SetHasExplicitDomain();
   }
 
   return NS_OK;
@@ -624,16 +622,22 @@ ContentPrincipal::Write(nsIObjectOutputS
   }
 
   nsAutoCString suffix;
   OriginAttributesRef().CreateSuffix(suffix);
 
   rv = aStream->WriteStringZ(suffix.get());
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Since Bug 965637 we do not serialize the CSP within the
+  // Principal anymore. Nevertheless there might still be
+  // serialized Principals that do have a serialized CSP.
+  // For now, we just write a null CSP here to avoid breakage.
+  // After Bug 1508939 we will have a new serialization for
+  // Principals which allows us to update the code here.
   rv = NS_WriteOptionalCompoundObject(
-      aStream, mCSP, NS_GET_IID(nsIContentSecurityPolicy), true);
+      aStream, nullptr, NS_GET_IID(nsIContentSecurityPolicy), true);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return NS_OK;
 }
--- a/caps/ExpandedPrincipal.cpp
+++ b/caps/ExpandedPrincipal.cpp
@@ -164,16 +164,24 @@ bool ExpandedPrincipal::AddonAllowsLoad(
   for (const auto& principal : mPrincipals) {
     if (Cast(principal)->AddonAllowsLoad(aURI, aExplicit)) {
       return true;
     }
   }
   return false;
 }
 
+void ExpandedPrincipal::SetCsp(nsIContentSecurityPolicy* aCSP) { mCSP = aCSP; }
+
+NS_IMETHODIMP
+ExpandedPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp) {
+  NS_IF_ADDREF(*aCsp = mCSP);
+  return NS_OK;
+}
+
 nsIPrincipal* ExpandedPrincipal::PrincipalToInherit(nsIURI* aRequestedURI) {
   if (aRequestedURI) {
     // If a given sub-principal subsumes the given URI, use that principal for
     // inheritance. In general, this only happens with certain CORS modes, loads
     // with forced principal inheritance, and creation of XML documents from
     // XMLHttpRequests or fetch requests. For URIs that normally inherit a
     // principal (such as data: URIs), we fall back to the last principal in the
     // allowlist.
--- a/caps/ExpandedPrincipal.h
+++ b/caps/ExpandedPrincipal.h
@@ -7,16 +7,18 @@
 #define ExpandedPrincipal_h
 
 #include "nsCOMPtr.h"
 #include "nsJSPrincipals.h"
 #include "nsTArray.h"
 #include "nsNetUtil.h"
 #include "mozilla/BasePrincipal.h"
 
+class nsIContentSecurityPolicy;
+
 class ExpandedPrincipal : public nsIExpandedPrincipal,
                           public mozilla::BasePrincipal {
  public:
   static already_AddRefed<ExpandedPrincipal> Create(
       nsTArray<nsCOMPtr<nsIPrincipal>>& aAllowList,
       const mozilla::OriginAttributes& aAttrs);
 
   static PrincipalKind Kind() { return eExpandedPrincipal; }
@@ -41,16 +43,18 @@ class ExpandedPrincipal : public nsIExpa
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
   NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
   virtual bool AddonHasPermission(const nsAtom* aPerm) override;
   virtual nsresult GetScriptLocation(nsACString& aStr) override;
 
   bool AddonAllowsLoad(nsIURI* aURI, bool aExplicit = false);
 
+  void SetCsp(nsIContentSecurityPolicy* aCSP);
+
   // Returns the principal to inherit when this principal requests the given
   // URL. See BasePrincipal::PrincipalToInherit.
   nsIPrincipal* PrincipalToInherit(nsIURI* aRequestedURI = nullptr);
 
   nsresult GetSiteIdentifier(mozilla::SiteIdentifier& aSite) override;
 
  protected:
   explicit ExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>>& aAllowList);
@@ -59,16 +63,17 @@ class ExpandedPrincipal : public nsIExpa
 
   bool SubsumesInternal(nsIPrincipal* aOther,
                         DocumentDomainConsideration aConsideration) override;
 
   bool MayLoadInternal(nsIURI* aURI) override;
 
  private:
   nsTArray<nsCOMPtr<nsIPrincipal>> mPrincipals;
+  nsCOMPtr<nsIContentSecurityPolicy> mCSP;
 };
 
 #define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1"
 #define NS_EXPANDEDPRINCIPAL_CID                     \
   {                                                  \
     0xe8ee88b0, 0x5571, 0x4086, {                    \
       0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb \
     }                                                \
--- a/caps/NullPrincipal.cpp
+++ b/caps/NullPrincipal.cpp
@@ -129,30 +129,16 @@ nsresult NullPrincipal::GetScriptLocatio
 
 /**
  * nsIPrincipal implementation
  */
 
 uint32_t NullPrincipal::GetHashValue() { return (NS_PTR_TO_INT32(this) >> 2); }
 
 NS_IMETHODIMP
-NullPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp) {
-  // Never destroy an existing CSP on the principal.
-  // This method should only be called in rare cases.
-
-  MOZ_ASSERT(!mCSP, "do not destroy an existing CSP");
-  if (mCSP) {
-    return NS_ERROR_ALREADY_INITIALIZED;
-  }
-
-  mCSP = aCsp;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 NullPrincipal::GetURI(nsIURI** aURI) {
   nsCOMPtr<nsIURI> uri = mURI;
   uri.forget(aURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 NullPrincipal::GetDomain(nsIURI** aDomain) {
--- a/caps/NullPrincipal.h
+++ b/caps/NullPrincipal.h
@@ -11,17 +11,16 @@
 
 #ifndef mozilla_NullPrincipal_h
 #define mozilla_NullPrincipal_h
 
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCOMPtr.h"
-#include "nsIContentSecurityPolicy.h"
 
 #include "mozilla/BasePrincipal.h"
 
 class nsIDocShell;
 class nsIURI;
 
 #define NS_NULLPRINCIPAL_CID                         \
   {                                                  \
@@ -43,17 +42,16 @@ class NullPrincipal final : public BaseP
   NullPrincipal() : BasePrincipal(eNullPrincipal) {}
 
   static PrincipalKind Kind() { return eNullPrincipal; }
 
   NS_DECL_NSISERIALIZABLE
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   uint32_t GetHashValue() override;
-  NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
   NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
 
   static already_AddRefed<NullPrincipal> CreateWithInheritedAttributes(
       nsIPrincipal* aInheritFrom);
--- a/caps/SystemPrincipal.cpp
+++ b/caps/SystemPrincipal.cpp
@@ -47,50 +47,16 @@ uint32_t SystemPrincipal::GetHashValue()
 
 NS_IMETHODIMP
 SystemPrincipal::GetURI(nsIURI** aURI) {
   *aURI = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SystemPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp) {
-  *aCsp = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-SystemPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp) {
-  // Never destroy an existing CSP on the principal.
-  // This method should only be called in rare cases.
-
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-SystemPrincipal::EnsureCSP(dom::Document* aDocument,
-                           nsIContentSecurityPolicy** aCSP) {
-  // CSP on a system principal makes no sense
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-SystemPrincipal::GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) {
-  *aPreloadCSP = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-SystemPrincipal::EnsurePreloadCSP(dom::Document* aDocument,
-                                  nsIContentSecurityPolicy** aPreloadCSP) {
-  // CSP on a system principal makes no sense
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
 SystemPrincipal::GetDomain(nsIURI** aDomain) {
   *aDomain = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SystemPrincipal::SetDomain(nsIURI* aDomain) { return NS_OK; }
 
--- a/caps/SystemPrincipal.h
+++ b/caps/SystemPrincipal.h
@@ -33,23 +33,16 @@ class SystemPrincipal final : public Bas
   static PrincipalKind Kind() { return eSystemPrincipal; }
 
   NS_DECL_NSISERIALIZABLE
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   uint32_t GetHashValue() override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
-  NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
-  NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
-  NS_IMETHOD EnsureCSP(dom::Document* aDocument,
-                       nsIContentSecurityPolicy** aCSP) override;
-  NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
-  NS_IMETHOD EnsurePreloadCSP(dom::Document* aDocument,
-                              nsIContentSecurityPolicy** aCSP) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
   NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
 
   virtual nsresult GetScriptLocation(nsACString& aStr) override;
 
   nsresult GetSiteIdentifier(SiteIdentifier& aSite) override {
     aSite.Init(this);
     return NS_OK;
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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/. */
 
 /* Defines the abstract interface for a principal. */
 
+#include "nsIContentSecurityPolicy.idl"
 #include "nsISerializable.idl"
 
 %{C++
 struct JSPrincipals;
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "mozilla/DebugOnly.h"
 namespace mozilla {
@@ -32,19 +33,16 @@ class OriginAttributes;
     return                                                     \
       this == aOther ||                                        \
       (NS_SUCCEEDED(method_(aOther, &retVal)) && retVal);      \
   }
 
 %}
 
 interface nsIURI;
-interface nsIContentSecurityPolicy;
-
-webidl Document;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 [ref] native PrincipalArray(const nsTArray<nsCOMPtr<nsIPrincipal>>);
 [ref] native const_OriginAttributes(const mozilla::OriginAttributes);
 
 [scriptable, builtinclass, uuid(f75f502d-79fd-48be-a079-e5a7b8f80c8b)]
 interface nsIPrincipal : nsISerializable
@@ -146,63 +144,16 @@ interface nsIPrincipal : nsISerializable
      *                                   loadee inherits the principal of the
      *                                   loader.
      * @throws NS_ERROR_DOM_BAD_URI if the load is not allowed.
      */
     void checkMayLoad(in nsIURI uri, in boolean report,
                       in boolean allowIfInheritsPrincipal);
 
     /**
-     * A Content Security Policy associated with this principal. Use this function to
-     * query the associated CSP with this principal, but please *only* use this
-     * function to *set* a CSP when you know exactly what you are doing.
-     * Most likely you want to call ensureCSP instead of setCSP.
-     */
-    readonly attribute nsIContentSecurityPolicy csp;
-    [noscript] void setCsp(in nsIContentSecurityPolicy aCsp);
-
-    /*
-     * Use this function to query a CSP associated with this principal.
-     * If no CSP is associated with this principal then one is created
-     * internally and setRequestContext is called on the CSP using aDocument.
-     *
-     * Please note if aDocument is null, then setRequestContext on the
-     * CSP object is called using the current principal.
-     */
-    [noscript] nsIContentSecurityPolicy ensureCSP(in Document aDocument);
-
-    /**
-     * A speculative Content Security Policy associated with this
-     * principal. Set during speculative loading (preloading) and
-     * used *only* for preloads.
-     *
-     * If you want to query the CSP associated with that principal,
-     * then this is *not* what you want. Instead query 'csp'.
-     */
-    [noscript] readonly attribute nsIContentSecurityPolicy preloadCsp;
-
-    /*
-     * Use this function to query a speculative CSP associated with this
-     * principal. If no speculative CSP is associated with this principal
-     * then one is created internally and setRequestContext is called on
-     * the CSP using aDocument.
-     *
-     * Please note if aDocument is null, then setRequestContext on the
-     * speculative CSP object is called using the current principal.
-     */
-    [noscript] nsIContentSecurityPolicy ensurePreloadCSP(in Document aDocument);
-
-    /**
-     * The CSP of the principal in JSON notation.
-     * Note, that the CSP itself is not exposed to JS, but script
-     * should be able to obtain a JSON representation of the CSP.
-     */
-    readonly attribute AString cspJSON;
-
-    /**
      * A dictionary of the non-default origin attributes associated with this
      * nsIPrincipal.
      *
      * Attributes are tokens that are taken into account when determining whether
      * two principals are same-origin - if any attributes differ, the principals
      * are cross-origin, even if the scheme, host, and port are the same.
      * Attributes should also be considered for all security and bucketing decisions,
      * even those which make non-standard comparisons (like cookies, which ignore
@@ -356,9 +307,29 @@ interface nsIExpandedPrincipal : nsISupp
    * principals is inherited rather than the expanded principal itself. The
    * last principal in the allowlist is the default principal to inherit.
    *
    * Note: this list is not reference counted, it is shared, so
    * should not be changed and should only be used ephemerally.
    */
   [noscript, notxpcom, nostdcall]
   PrincipalArray AllowList();
+
+
+  /**
+   * Bug 1548468: Move CSP off ExpandedPrincipal.
+   *
+   * A Content Security Policy associated with this principal. Use this function
+   * to query the associated CSP with this principal.
+   */
+  readonly attribute nsIContentSecurityPolicy csp;
+
+%{ C++
+  inline already_AddRefed<nsIContentSecurityPolicy> GetCsp()
+  {
+    nsCOMPtr<nsIContentSecurityPolicy> result;
+    mozilla::DebugOnly<nsresult> rv = GetCsp(getter_AddRefs(result));
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return result.forget();
+  }
+%}
+
 };
--- a/caps/nsJSPrincipals.cpp
+++ b/caps/nsJSPrincipals.cpp
@@ -118,20 +118,20 @@ bool nsJSPrincipals::ReadPrincipals(JSCo
         tag == SCTAG_DOM_WORKER_PRINCIPAL)) {
     xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
     return false;
   }
 
   return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
 }
 
-static bool ReadPrincipalInfo(
-    JSStructuredCloneReader* aReader, OriginAttributes& aAttrs,
-    nsACString& aSpec, nsACString& aOriginNoSuffix, nsACString& aBaseDomain,
-    nsTArray<ContentSecurityPolicy>* aPolicies = nullptr) {
+static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader,
+                              OriginAttributes& aAttrs, nsACString& aSpec,
+                              nsACString& aOriginNoSuffix,
+                              nsACString& aBaseDomain) {
   uint32_t suffixLength, specLength;
   if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
     return false;
   }
 
   nsAutoCString suffix;
   if (!suffix.SetLength(suffixLength, fallible)) {
     return false;
@@ -148,57 +148,35 @@ static bool ReadPrincipalInfo(
   if (!aSpec.SetLength(specLength, fallible)) {
     return false;
   }
 
   if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
     return false;
   }
 
-  uint32_t originNoSuffixLength, policyCount;
-  if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &policyCount)) {
+  uint32_t originNoSuffixLength, dummy;
+  if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &dummy)) {
     return false;
   }
 
-  if (!aPolicies) {
-    MOZ_ASSERT(policyCount == 0);
+  MOZ_ASSERT(dummy == 0);
+  if (dummy != 0) {
+    return false;
   }
 
   if (!aOriginNoSuffix.SetLength(originNoSuffixLength, fallible)) {
     return false;
   }
 
   if (!JS_ReadBytes(aReader, aOriginNoSuffix.BeginWriting(),
                     originNoSuffixLength)) {
     return false;
   }
 
-  for (uint32_t i = 0; i < policyCount; i++) {
-    uint32_t policyLength, reportAndMeta;
-    if (!JS_ReadUint32Pair(aReader, &policyLength, &reportAndMeta)) {
-      return false;
-    }
-    bool reportOnly = reportAndMeta & 1;
-    bool deliveredViaMetaTag = reportAndMeta & 2;
-
-    nsAutoCString policyStr;
-    if (!policyStr.SetLength(policyLength, fallible)) {
-      return false;
-    }
-
-    if (!JS_ReadBytes(aReader, policyStr.BeginWriting(), policyLength)) {
-      return false;
-    }
-
-    if (aPolicies) {
-      aPolicies->AppendElement(ContentSecurityPolicy(
-          NS_ConvertUTF8toUTF16(policyStr), reportOnly, deliveredViaMetaTag));
-    }
-  }
-
   uint32_t baseDomainIsVoid, baseDomainLength;
   if (!JS_ReadUint32Pair(aReader, &baseDomainIsVoid, &baseDomainLength)) {
     return false;
   }
 
   MOZ_ASSERT(baseDomainIsVoid == 0 || baseDomainIsVoid == 1);
 
   if (baseDomainIsVoid) {
@@ -254,33 +232,31 @@ static bool ReadPrincipalInfo(JSStructur
     }
 
     aInfo = expanded;
   } else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
     OriginAttributes attrs;
     nsAutoCString spec;
     nsAutoCString originNoSuffix;
     nsAutoCString baseDomain;
-    nsTArray<ContentSecurityPolicy> policies;
-    if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain,
-                           &policies)) {
+    if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain)) {
       return false;
     }
 
 #ifdef FUZZING
     if (originNoSuffix.IsEmpty()) {
       return false;
     }
 #endif
 
     MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty());
 
     // XXX: Do we care about mDomain for structured clone?
     aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec, Nothing(),
-                                 std::move(policies), baseDomain);
+                                 baseDomain);
   } else {
 #ifdef FUZZING
     return false;
 #else
     MOZ_CRASH("unexpected principal structured clone tag");
 #endif
   }
 
@@ -339,47 +315,33 @@ bool nsJSPrincipals::ReadKnownPrincipalT
     xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
     return false;
   }
 
   *aOutPrincipals = get(prin.forget().take());
   return true;
 }
 
-static bool WritePrincipalInfo(
-    JSStructuredCloneWriter* aWriter, const OriginAttributes& aAttrs,
-    const nsCString& aSpec, const nsCString& aOriginNoSuffix,
-    const nsCString& aBaseDomain,
-    const nsTArray<ContentSecurityPolicy>* aPolicies = nullptr) {
+static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
+                               const OriginAttributes& aAttrs,
+                               const nsCString& aSpec,
+                               const nsCString& aOriginNoSuffix,
+                               const nsCString& aBaseDomain) {
   nsAutoCString suffix;
   aAttrs.CreateSuffix(suffix);
-  size_t policyCount = aPolicies ? aPolicies->Length() : 0;
 
   if (!(JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
         JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
         JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length()) &&
-        JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), policyCount) &&
+        JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), 0) &&
         JS_WriteBytes(aWriter, aOriginNoSuffix.get(),
                       aOriginNoSuffix.Length()))) {
     return false;
   }
 
-  for (uint32_t i = 0; i < policyCount; i++) {
-    nsCString policy;
-    CopyUTF16toUTF8((*aPolicies)[i].policy(), policy);
-    uint32_t reportAndMeta =
-        ((*aPolicies)[i].reportOnlyFlag() ? 1 : 0) |
-        ((*aPolicies)[i].deliveredViaMetaTagFlag() ? 2 : 0);
-    if (!(JS_WriteUint32Pair(aWriter, policy.Length(), reportAndMeta) &&
-          JS_WriteBytes(aWriter, PromiseFlatCString(policy).get(),
-                        policy.Length()))) {
-      return false;
-    }
-  }
-
   if (aBaseDomain.IsVoid()) {
     return JS_WriteUint32Pair(aWriter, 1, 0);
   }
 
   return JS_WriteUint32Pair(aWriter, 0, aBaseDomain.Length()) &&
          JS_WriteBytes(aWriter, aBaseDomain.get(), aBaseDomain.Length());
 }
 
@@ -408,18 +370,17 @@ static bool WritePrincipalInfo(JSStructu
     }
     return true;
   }
 
   MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo);
   const ContentPrincipalInfo& cInfo = aInfo;
   return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
          WritePrincipalInfo(aWriter, cInfo.attrs(), cInfo.spec(),
-                            cInfo.originNoSuffix(), cInfo.baseDomain(),
-                            &(cInfo.securityPolicies()));
+                            cInfo.originNoSuffix(), cInfo.baseDomain());
 }
 
 bool nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter) {
   PrincipalInfo info;
   if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
     xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
     return false;
   }
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -269,107 +269,16 @@ nsScriptSecurityManager::GetChannelResul
   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;
-  }
-
-  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
-  if (loadInfo->GetExternalContentPolicyType() !=
-      nsIContentPolicy::TYPE_SUBDOCUMENT) {
-    return;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS_VOID(rv);
-  nsAutoCString URISpec;
-  rv = uri->GetSpec(URISpec);
-  NS_ENSURE_SUCCESS_VOID(rv);
-
-  bool isSrcDoc = URISpec.EqualsLiteral("about:srcdoc");
-  bool isData = (NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData);
-
-  if (!isSrcDoc && !isData) {
-    return;
-  }
-
-  nsCOMPtr<nsIPrincipal> principalToInherit =
-      loadInfo->FindPrincipalToInherit(aChannel);
-
-  nsCOMPtr<nsIContentSecurityPolicy> originalCSP;
-  principalToInherit->GetCsp(getter_AddRefs(originalCSP));
-  if (!originalCSP) {
-    return;
-  }
-
-  // if the principalToInherit had a CSP, add it to the before
-  // created NullPrincipal (unless it already has one)
-  MOZ_ASSERT(aPrincipal->GetIsNullPrincipal(),
-             "inheriting the CSP only valid for NullPrincipal");
-  nsCOMPtr<nsIContentSecurityPolicy> nullPrincipalCSP;
-  aPrincipal->GetCsp(getter_AddRefs(nullPrincipalCSP));
-  if (nullPrincipalCSP) {
-    MOZ_ASSERT(nsCSPContext::Equals(originalCSP, nullPrincipalCSP));
-    // CSPs are equal, no need to set it again.
-    return;
-  }
-
-  // After 965637 all that magical CSP inheritance goes away. For now,
-  // we have to create a clone of the current CSP and have to manually
-  // set it on the Principal.
-  uint32_t count = 0;
-  rv = originalCSP->GetPolicyCount(&count);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  if (count == 0) {
-    // fast path: if there is nothing to inherit, we can return here.
-    return;
-  }
-
-  RefPtr<nsCSPContext> newCSP = new nsCSPContext();
-  nsWeakPtr loadingContext =
-      static_cast<nsCSPContext*>(originalCSP.get())->GetLoadingContext();
-  nsCOMPtr<Document> doc = do_QueryReferent(loadingContext);
-
-  rv = doc ? newCSP->SetRequestContext(doc, nullptr)
-           : newCSP->SetRequestContext(nullptr, aPrincipal);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  for (uint32_t i = 0; i < count; ++i) {
-    const nsCSPPolicy* policy = originalCSP->GetPolicy(i);
-    MOZ_ASSERT(policy);
-
-    nsAutoString policyString;
-    policy->toString(policyString);
-
-    rv = newCSP->AppendPolicy(policyString, policy->getReportOnlyFlag(),
-                              policy->getDeliveredViaMetaTagFlag());
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-  }
-  aPrincipal->SetCsp(newCSP);
-}
-
 nsresult nsScriptSecurityManager::GetChannelResultPrincipal(
     nsIChannel* aChannel, nsIPrincipal** aPrincipal, bool aIgnoreSandboxing) {
   MOZ_ASSERT(aChannel, "Must have channel!");
 
   // Check whether we have an nsILoadInfo that says what we should do.
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   if (loadInfo->GetForceInheritPrincipalOverruleOwner()) {
     nsCOMPtr<nsIPrincipal> principalToInherit =
@@ -386,17 +295,16 @@ nsresult nsScriptSecurityManager::GetCha
       return NS_OK;
     }
   }
 
   if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
     nsCOMPtr<nsIPrincipal> sandboxedLoadingPrincipal =
         loadInfo->GetSandboxedLoadingPrincipal();
     MOZ_ASSERT(sandboxedLoadingPrincipal);
-    InheritAndSetCSPOnPrincipalIfNeeded(aChannel, sandboxedLoadingPrincipal);
     sandboxedLoadingPrincipal.forget(aPrincipal);
     return NS_OK;
   }
 
   bool forceInherit = loadInfo->GetForceInheritPrincipal();
   if (aIgnoreSandboxing && !forceInherit) {
     // Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of
     // sandboxing:
@@ -428,20 +336,17 @@ nsresult nsScriptSecurityManager::GetCha
     bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
 
     if (nsContentUtils::ChannelShouldInheritPrincipal(
             principalToInherit, uri, inheritForAboutBlank, false)) {
       principalToInherit.forget(aPrincipal);
       return NS_OK;
     }
   }
-  nsresult rv = GetChannelURIPrincipal(aChannel, aPrincipal);
-  NS_ENSURE_SUCCESS(rv, rv);
-  InheritAndSetCSPOnPrincipalIfNeeded(aChannel, *aPrincipal);
-  return NS_OK;
+  return GetChannelURIPrincipal(aChannel, aPrincipal);
 }
 
 /* The principal of the URI that this channel is loading. This is never
  * affected by things like sandboxed loads, or loads where we forcefully
  * inherit the principal.  Think of this as the principal of the server
  * which this channel is loading from.  Most callers should use
  * GetChannelResultPrincipal instead of GetChannelURIPrincipal.  Only
  * call GetChannelURIPrincipal if you are sure that you want the
@@ -489,42 +394,44 @@ NS_IMPL_ISUPPORTS(nsScriptSecurityManage
 // Methods implementing nsIScriptSecurityManager //
 ///////////////////////////////////////////////////
 
 ///////////////// Security Checks /////////////////
 
 bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
     JSContext* cx, JS::HandleValue aValue) {
   MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
-  nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
 
 #if defined(DEBUG) && !defined(ANDROID)
+  nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
   nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal(subjectPrincipal,
                                                               cx);
 #endif
 
+  // Get the window, if any, corresponding to the current global
   nsCOMPtr<nsIContentSecurityPolicy> csp;
-  nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
-  NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
+  if (nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(cx)) {
+    csp = win->GetCsp();
+  }
 
   // don't do anything unless there's a CSP
   if (!csp) return true;
 
   nsCOMPtr<nsICSPEventListener> cspEventListener;
   if (!NS_IsMainThread()) {
     WorkerPrivate* workerPrivate =
         mozilla::dom::GetWorkerPrivateFromContext(cx);
     if (workerPrivate) {
       cspEventListener = workerPrivate->CSPEventListener();
     }
   }
 
   bool evalOK = true;
   bool reportViolation = false;
-  rv = csp->GetAllowsEval(&reportViolation, &evalOK);
+  nsresult rv = csp->GetAllowsEval(&reportViolation, &evalOK);
 
   if (NS_FAILED(rv)) {
     NS_WARNING("CSP: failed to get allowsEval");
     return true;  // fail open to not break sites.
   }
 
   if (reportViolation) {
     JS::Rooted<JSString*> jsString(cx, JS::ToString(cx, aValue));
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4623,22 +4623,17 @@ nsDocShell::Reload(uint32_t aReloadFlags
     uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
     nsAutoString srcdoc;
     nsCOMPtr<nsIURI> baseURI;
     nsCOMPtr<nsIURI> originalURI;
     nsCOMPtr<nsIURI> resultPrincipalURI;
     bool loadReplace = false;
 
     nsIPrincipal* triggeringPrincipal = doc->NodePrincipal();
-    // Currently the NodePrincipal holds the CSP for that document,
-    // after Bug 965637 we can query the CSP directly from the doc
-    // instead of doc->NodePrincipal().
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
 
     nsAutoString contentTypeHint;
     doc->GetContentType(contentTypeHint);
 
     if (doc->IsSrcdocDocument()) {
       doc->GetSrcdocData(srcdoc);
       flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
       baseURI = doc->GetBaseURI();
@@ -5880,39 +5875,35 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI
   loadState->SetOriginalURI(mCurrentURI);
   loadState->SetResultPrincipalURI(aURI);
   loadState->SetResultPrincipalURIIsSome(true);
   loadState->SetKeepResultPrincipalURIIfSet(true);
 
   // Set the triggering pricipal to aPrincipal if available, or current
   // document's principal otherwise.
   nsCOMPtr<nsIPrincipal> principal = aPrincipal;
+  RefPtr<Document> doc = GetDocument();
   if (!principal) {
-    RefPtr<Document> doc = GetDocument();
     if (!doc) {
       return NS_ERROR_FAILURE;
     }
     principal = doc->NodePrincipal();
   }
   loadState->SetTriggeringPrincipal(principal);
-  // Currently the principal (NodePrincipal) holds the CSP for that
-  // document, after Bug 965637 we can query the CSP directly from
-  // the doc instead of the principal.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  nsresult rv = principal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-  loadState->SetCsp(csp);
+  if (doc) {
+    loadState->SetCsp(doc->GetCsp());
+  }
 
   loadState->SetPrincipalIsExplicit(true);
 
   /* Check if this META refresh causes a redirection
    * to another site.
    */
   bool equalUri = false;
-  rv = aURI->Equals(mCurrentURI, &equalUri);
+  nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
   if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
       aDelay <= REFRESH_REDIRECT_TIMER) {
     /* It is a META refresh based redirection within the threshold time
      * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
      * Pass a REPLACE flag to LoadURI().
      */
     loadState->SetLoadType(LOAD_NORMAL_REPLACE);
 
@@ -6900,23 +6891,17 @@ nsresult nsDocShell::EndPageLoad(nsIWebP
 
           nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
           MOZ_ASSERT(loadInfo, "loadInfo is required on all channels");
           nsCOMPtr<nsIPrincipal> triggeringPrincipal =
               loadInfo->TriggeringPrincipal();
 
           LoadURIOptions loadURIOptions;
           loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
-          // Currently we query the CSP from the triggeringPrincipal within
-          // the loadInfo. After Bug 965637, we can query the CSP from the
-          // loadInfo, which internally queries the CSP from the Client.
-          nsCOMPtr<nsIContentSecurityPolicy> csp;
-          nsresult rv = triggeringPrincipal->GetCsp(getter_AddRefs(csp));
-          NS_ENSURE_SUCCESS(rv, rv);
-          loadURIOptions.mCsp = csp;
+          loadURIOptions.mCsp = loadInfo->GetCsp();
           loadURIOptions.mPostData = newPostData;
           return LoadURI(newSpecW, loadURIOptions);
         }
       }
     }
 
     // Well, fixup didn't work :-(
     // It is time to throw an error dialog box, and be done with it...
@@ -6971,30 +6956,33 @@ nsresult nsDocShell::EndPageLoad(nsIWebP
 nsresult nsDocShell::EnsureContentViewer() {
   if (mContentViewer) {
     return NS_OK;
   }
   if (mIsBeingDestroyed) {
     return NS_ERROR_FAILURE;
   }
 
+  nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
   nsCOMPtr<nsIURI> baseURI;
   nsIPrincipal* principal = GetInheritedPrincipal(false);
   nsCOMPtr<nsIDocShellTreeItem> parentItem;
   GetSameTypeParent(getter_AddRefs(parentItem));
   if (parentItem) {
     if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
       nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
       if (parentElement) {
         baseURI = parentElement->GetBaseURI();
-      }
-    }
-  }
-
-  nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
+        cspToInheritForAboutBlank = parentElement->GetCsp();
+      }
+    }
+  }
+
+  nsresult rv = CreateAboutBlankContentViewer(
+      principal, cspToInheritForAboutBlank, baseURI);
 
   NS_ENSURE_STATE(mContentViewer);
 
   if (NS_SUCCEEDED(rv)) {
     RefPtr<Document> doc(GetDocument());
     NS_ASSERTION(doc,
                  "Should have doc if CreateAboutBlankContentViewer "
                  "succeeded!");
@@ -7010,18 +6998,18 @@ nsresult nsDocShell::EnsureContentViewer
     // creating the extra document.
     doc->IgnoreDocGroupMismatches();
   }
 
   return rv;
 }
 
 nsresult nsDocShell::CreateAboutBlankContentViewer(
-    nsIPrincipal* aPrincipal, nsIURI* aBaseURI, bool aTryToSaveOldPresentation,
-    bool aCheckPermitUnload) {
+    nsIPrincipal* aPrincipal, nsIContentSecurityPolicy* aCSP, nsIURI* aBaseURI,
+    bool aTryToSaveOldPresentation, bool aCheckPermitUnload) {
   RefPtr<Document> blankDoc;
   nsCOMPtr<nsIContentViewer> viewer;
   nsresult rv = NS_ERROR_FAILURE;
 
   /* mCreatingDocument should never be true at this point. However, it's
      a theoretical possibility. We want to know about it and make it stop,
      and this sounds like a job for an assertion. */
   NS_ASSERTION(!mCreatingDocument,
@@ -7115,16 +7103,26 @@ nsresult nsDocShell::CreateAboutBlankCon
       principal = aPrincipal;
     }
 
     MaybeCreateInitialClientSource(principal);
 
     // generate (about:blank) document to load
     blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal, this);
     if (blankDoc) {
+      // Hack: manually set the CSP for the new document
+      // Please create an actual copy of the CSP (do not share the same
+      // reference) otherwise appending a new policy within the new
+      // document will be incorrectly propagated to the opening doc.
+      if (aCSP) {
+        RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
+        cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
+        blankDoc->SetCsp(cspToInherit);
+      }
+
       // Hack: set the base URI manually, since this document never
       // got Reset() with a channel.
       blankDoc->SetBaseURI(aBaseURI);
 
       // Copy our sandbox flags to the document. These are immutable
       // after being set here.
       blankDoc->SetSandboxFlags(mSandboxFlags);
 
@@ -7154,18 +7152,19 @@ nsresult nsDocShell::CreateAboutBlankCon
     mTiming = nullptr;
     mBlankTiming = true;
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
-nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal) {
-  return CreateAboutBlankContentViewer(aPrincipal, nullptr);
+nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
+                                          nsIContentSecurityPolicy* aCSP) {
+  return CreateAboutBlankContentViewer(aPrincipal, aCSP, nullptr);
 }
 
 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
                                      nsIRequest* aNewRequest,
                                      Document* aNewDocument) {
   if (!mOSHE) {
     return false;  // no entry to save into
   }
@@ -9045,23 +9044,17 @@ nsresult nsDocShell::MaybeHandleSameDocu
   nsCOMPtr<nsIContentSecurityPolicy> newCsp;
   if (mOSHE) {
     newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
     newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
     newCsp = mOSHE->GetCsp();
   } else {
     newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
     newURIPrincipalToInherit = doc->NodePrincipal();
-    // This is a same-document navigation hence we query the CSP
-    // from the current document. Please note that currently the
-    // NodePrincipal holds the CSP for that document, after
-    // Bug 965637 we can query the CSP directly from
-    // the doc instead of the NodePrincipal.
-    nsresult rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(newCsp));
-    NS_ENSURE_SUCCESS(rv, rv);
+    newCsp = doc->GetCsp();
   }
   // Pass true for aCloneSHChildren, since we're not
   // changing documents here, so all of our subframes are
   // still relevant to the new session history entry.
   //
   // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
   // flag on firing onLocationChange(...).
   // Anyway, aCloneSHChildren param is simply reflecting
@@ -9349,17 +9342,17 @@ nsresult nsDocShell::InternalLoad(nsDocS
     loadFromExternal = true;
     // Disallow external chrome: loads targetted at content windows
     if (SchemeIsChrome(aLoadState->URI())) {
       NS_WARNING("blocked external chrome: url -- use '--chrome' option");
       return NS_ERROR_FAILURE;
     }
 
     // clear the decks to prevent context bleed-through (bug 298255)
-    rv = CreateAboutBlankContentViewer(nullptr, nullptr);
+    rv = CreateAboutBlankContentViewer(nullptr, nullptr, nullptr);
     if (NS_FAILED(rv)) {
       return NS_ERROR_FAILURE;
     }
 
     // reset loadType so we don't have to add lots of tests for
     // LOAD_NORMAL_EXTERNAL after this point
     aLoadState->SetLoadType(LOAD_NORMAL);
   }
@@ -10005,46 +9998,44 @@ nsresult nsDocShell::DoURILoad(nsDocShel
         getter_AddRefs(channel), aLoadState->URI(), srcdoc,
         NS_LITERAL_CSTRING("text/html"), loadInfo, true);
     NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
     MOZ_ASSERT(isc);
     isc->SetBaseURI(baseURI);
   }
 
-  // Navigational requests that are same origin need to be upgraded in case
-  // upgrade-insecure-requests is present. Please note that in that case
-  // the triggeringPrincipal is holding the CSP that potentially
-  // holds upgrade-insecure-requests.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  aLoadState->TriggeringPrincipal()->GetCsp(getter_AddRefs(csp));
-#ifdef DEBUG
-  // We only serialize the CSP within CodebasePrincipals hence
-  // lets only assert if the load is triggered by a CodebesPrincipal.
-  // After Bug 965637 we can remove that assertion anyway.
-  if (aLoadState->TriggeringPrincipal()->GetIsCodebasePrincipal()) {
-    nsCOMPtr<nsIContentSecurityPolicy> argsCSP = aLoadState->Csp();
-    MOZ_ASSERT(nsCSPContext::Equals(csp, argsCSP));
-  }
-#endif
-
+  nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
   if (csp) {
+    // Navigational requests that are same origin need to be upgraded in case
+    // upgrade-insecure-requests is present.
     bool upgradeInsecureRequests = false;
     csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
     if (upgradeInsecureRequests) {
       // only upgrade if the navigation is same origin
       nsCOMPtr<nsIPrincipal> resultPrincipal;
       rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
           channel, getter_AddRefs(resultPrincipal));
       NS_ENSURE_SUCCESS(rv, rv);
       if (IsConsideredSameOriginForUIR(aLoadState->TriggeringPrincipal(),
                                        resultPrincipal)) {
         loadInfo->SetUpgradeInsecureRequests();
       }
     }
+
+    if (CSP_ShouldResponseInheritCSP(channel)) {
+      // If the new load needs to inherit the CSP, temporarily store the CSP
+      // on the loadinfo, and transfer it to the new Document within
+      // Document::InitCSP(). Please create an actual copy of the CSP (do not
+      // share the same reference) otherwise a Meta CSP of an opaque origin
+      // will incorrectly be propagated to the embedding document.
+      RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
+      cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
+      loadInfo->SetCSPToInherit(cspToInherit);
+    }
   }
 
   nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
       do_QueryInterface(channel);
   if (appCacheChannel) {
     // Any document load should not inherit application cache.
     appCacheChannel->SetInheritApplicationCache(false);
 
@@ -11135,22 +11126,17 @@ nsresult nsDocShell::UpdateURLAndHistory
   nsCOMPtr<nsISHEntry> newSHEntry;
   if (!aReplace) {
     // Step 2.
     // Save the current scroll position (bug 590573).  Step 2.3.
     nsPoint scrollPos = GetCurScrollPos();
     mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
 
     bool scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
-
-    // Currently the NodePrincipal holds the CSP for that document,
-    // after Bug 965637 we can query the CSP directly from
-    // the doc instead of the NodePrincipal.
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+    nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
 
     // Since we're not changing which page we have loaded, pass
     // true for aCloneChildren.
     nsresult rv =
         AddToSessionHistory(aNewURI, nullptr,
                             aDocument->NodePrincipal(),  // triggeringPrincipal
                             nullptr, csp, true, getter_AddRefs(newSHEntry));
     NS_ENSURE_SUCCESS(rv, rv);
@@ -11180,28 +11166,22 @@ nsresult nsDocShell::UpdateURLAndHistory
     profiler_register_page(mHistoryID, id, aNewURI->GetSpecOrDefault(),
                            IsFrame());
 #endif
 
   } else {
     // Step 3.
     newSHEntry = mOSHE;
 
-    // Currently the NodePrincipal holds the CSP for that document,
-    // after Bug 965637 we can query the CSP directly from
-    // the doc instead of the NodePrincipal.
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-
     // Since we're not changing which page we have loaded, pass
     if (!newSHEntry) {
       nsresult rv = AddToSessionHistory(
           aNewURI, nullptr,
           aDocument->NodePrincipal(),  // triggeringPrincipal
-          nullptr, csp, true, getter_AddRefs(newSHEntry));
+          nullptr, aDocument->GetCsp(), true, getter_AddRefs(newSHEntry));
       NS_ENSURE_SUCCESS(rv, rv);
       mOSHE = newSHEntry;
     }
     newSHEntry->SetURI(aNewURI);
     newSHEntry->SetOriginalURI(aNewURI);
     newSHEntry->SetLoadReplace(false);
   }
 
@@ -11443,23 +11423,18 @@ nsresult nsDocShell::AddToSessionHistory
 
       discardLayoutState = ShouldDiscardLayoutState(httpChannel);
     }
 
     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
     if (!triggeringPrincipal) {
       triggeringPrincipal = loadInfo->TriggeringPrincipal();
     }
-    if (!csp && triggeringPrincipal) {
-      // Currently if no CSP is passed explicitly we query the CSP from
-      // the triggeringPrincipal from within the loadinfo. After Bug 965637,
-      // we can query the CSP from the loadInfo directly in case the CSP is
-      // not passed explicitly. Internally the loadinfo queries the CSP
-      // from the Client.
-      triggeringPrincipal->GetCsp(getter_AddRefs(csp));
+    if (!csp) {
+      csp = static_cast<net::LoadInfo*>(loadInfo.get())->GetCSPToInherit();
     }
 
     loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
 
     // For now keep storing just the principal in the SHEntry.
     if (!principalToInherit) {
       if (loadInfo->GetLoadingSandboxed()) {
         if (loadInfo->LoadingPrincipal()) {
@@ -11614,17 +11589,17 @@ nsresult nsDocShell::LoadHistoryEntry(ns
   if (SchemeIsJavascript(uri)) {
     // We're loading a URL that will execute script from inside asyncOpen.
     // Replace the current document with about:blank now to prevent
     // anything from the current document from leaking into any JavaScript
     // code in the URL.
     // Don't cache the presentation if we're going to just reload the
     // current entry. Caching would lead to trying to save the different
     // content viewers in the same nsISHEntry object.
-    rv = CreateAboutBlankContentViewer(principalToInherit, nullptr,
+    rv = CreateAboutBlankContentViewer(principalToInherit, nullptr, nullptr,
                                        aEntry != mOSHE);
 
     if (NS_FAILED(rv)) {
       // The creation of the intermittent about:blank content
       // viewer failed for some reason (potentially because the
       // user prevented it). Interrupt the history load.
       return NS_OK;
     }
@@ -12654,19 +12629,18 @@ nsDocShell::OnLinkClickSync(
   // if the triggeringPrincipal is not passed explicitly, then we
   // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
   nsCOMPtr<nsIPrincipal> triggeringPrincipal =
       aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
 
   nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
   if (!csp) {
     // Currently, if no csp is passed explicitly we fall back to querying the
-    // CSP from the NodePrincipal(). After Bug 965637 we can fall back to
-    // querying the CSP from the document (aContent->OwnerDoc()).
-    aContent->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+    // CSP from the document.
+    csp = aContent->GetCsp();
   }
 
   uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
   if (IsElementAnchorOrArea(aContent)) {
     MOZ_ASSERT(aContent->IsHTMLElement());
     nsAutoString relString;
     aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel,
                                    relString);
@@ -12925,17 +12899,18 @@ nsDocShell::InitOrReusePrintPreviewViewe
     // time we enter here seems overwork. We could skip ahead to where
     // we QI the mContentViewer if the current URI is either about:blank
     // or about:printpreview.
     Stop(nsIWebNavigation::STOP_ALL);
     nsCOMPtr<nsIPrincipal> principal =
         NullPrincipal::CreateWithInheritedAttributes(this);
     nsCOMPtr<nsIURI> uri;
     NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:printpreview"));
-    nsresult rv = CreateAboutBlankContentViewer(principal, uri);
+    nsresult rv =
+        CreateAboutBlankContentViewer(principal, /* aCsp = */ nullptr, uri);
     NS_ENSURE_SUCCESS(rv, rv);
     // Here we manually set current URI since we have just created a
     // brand new content viewer (about:blank) to host preview.
     SetCurrentURI(uri, nullptr, true, 0);
     print = do_QueryInterface(mContentViewer);
     NS_ENSURE_STATE(print);
     print->InitializeForPrintPreview();
   }
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -481,17 +481,19 @@ class nsDocShell final : public nsDocLoa
   //
   // Content Viewer Management
   //
 
   nsresult EnsureContentViewer();
 
   // aPrincipal can be passed in if the caller wants. If null is
   // passed in, the about:blank principal will end up being used.
+  // aCSP, if any, will be used for the new about:blank load.
   nsresult CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
+                                         nsIContentSecurityPolicy* aCSP,
                                          nsIURI* aBaseURI,
                                          bool aTryToSaveOldPresentation = true,
                                          bool aCheckPermitUnload = true);
 
   nsresult CreateContentViewer(const nsACString& aContentType,
                                nsIRequest* aRequest,
                                nsIStreamListener** aContentHandler);
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -38,16 +38,17 @@ class ClientSource;
 [ptr] native PresShell(mozilla::PresShell);
 [ref] native MaybeURI(mozilla::Maybe<nsCOMPtr<nsIURI>>);
 [ref] native Encoding(const mozilla::Encoding*);
       native UniqueClientSource(mozilla::UniquePtr<mozilla::dom::ClientSource>);
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
+interface nsIContentSecurityPolicy;
 interface nsIDocShellLoadInfo;
 interface nsIEditor;
 interface nsIEditingSession;
 interface nsISimpleEnumerator;
 interface nsIInputStream;
 interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
@@ -601,18 +602,20 @@ interface nsIDocShell : nsIDocShellTreeI
    * differently than a non-app tab docshell in some cases, such as when
    * handling link clicks. Docshells are not app tabs unless told otherwise.
    */
   attribute boolean isAppTab;
 
   /**
    * Create a new about:blank document and content viewer.
    * @param aPrincipal the principal to use for the new document.
+   * @param aCsp the CSP to use for the new document.
    */
-  void createAboutBlankContentViewer(in nsIPrincipal aPrincipal);
+  void createAboutBlankContentViewer(in nsIPrincipal aPrincipal,
+                                     [optional] in nsIContentSecurityPolicy aCSP);
 
   /**
    * Upon getting, returns the canonical encoding label of the document
    * currently loaded into this docshell.
    *
    * Upon setting, sets forcedCharset for compatibility with legacy callers.
    */
   attribute ACString charset;
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/CSSEnabledState.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/IdentifierMapEntry.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Likely.h"
+#include "mozilla/LoadInfo.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/URLExtraData.h"
 #include <algorithm>
 
 #include "mozilla/Logging.h"
@@ -64,16 +65,17 @@
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/FullscreenChange.h"
 #include "mozilla/PendingAnimationTracker.h"
 
 #include "mozilla/dom/Attr.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/CSPDictionariesBinding.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/FeaturePolicy.h"
 #include "mozilla/dom/FramingChecker.h"
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/TreeOrderedArrayInlines.h"
@@ -2692,17 +2694,18 @@ nsresult Document::StartDocumentLoad(con
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Initialize FeaturePolicy
   rv = InitFeaturePolicy(aChannel);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // XFO needs to be checked after CSP because it is ignored if
   // the CSP defines frame-ancestors.
-  if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
+  nsCOMPtr<nsIContentSecurityPolicy> cspForFA = mCSP;
+  if (!FramingChecker::CheckFrameOptions(aChannel, docShell, cspForFA)) {
     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.
@@ -2714,16 +2717,39 @@ nsresult Document::StartDocumentLoad(con
     if (parentDocument) {
       mCookieSettings = parentDocument->CookieSettings();
     }
   }
 
   return NS_OK;
 }
 
+nsIContentSecurityPolicy* Document::GetCsp() const { return mCSP; }
+
+void Document::SetCsp(nsIContentSecurityPolicy* aCSP) { mCSP = aCSP; }
+
+nsIContentSecurityPolicy* Document::GetPreloadCsp() const {
+  return mPreloadCSP;
+}
+
+void Document::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCSP) {
+  mPreloadCSP = aPreloadCSP;
+}
+
+void Document::GetCspJSON(nsString& aJSON) {
+  aJSON.Truncate();
+
+  if (!mCSP) {
+    dom::CSPPolicies jsonPolicies;
+    jsonPolicies.ToJSON(aJSON);
+    return;
+  }
+  mCSP->ToJSON(aJSON);
+}
+
 void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
   for (uint32_t i = 0; i < aMessages.Length(); ++i) {
     nsAutoString messageTag;
     aMessages[i]->GetTag(messageTag);
 
     nsAutoString category;
     aMessages[i]->GetCategory(category);
 
@@ -2733,54 +2759,48 @@ void Document::SendToConsole(nsCOMArray<
                                     NS_ConvertUTF16toUTF8(messageTag).get());
   }
 }
 
 void Document::ApplySettingsFromCSP(bool aSpeculative) {
   nsresult rv = NS_OK;
   if (!aSpeculative) {
     // 1) apply settings from regular CSP
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
-    NS_ENSURE_SUCCESS_VOID(rv);
-    if (csp) {
+    if (mCSP) {
       // Set up 'block-all-mixed-content' if not already inherited
       // from the parent context or set by any other CSP.
       if (!mBlockAllMixedContent) {
-        rv = csp->GetBlockAllMixedContent(&mBlockAllMixedContent);
+        rv = mCSP->GetBlockAllMixedContent(&mBlockAllMixedContent);
         NS_ENSURE_SUCCESS_VOID(rv);
       }
       if (!mBlockAllMixedContentPreloads) {
         mBlockAllMixedContentPreloads = mBlockAllMixedContent;
       }
 
       // Set up 'upgrade-insecure-requests' if not already inherited
       // from the parent context or set by any other CSP.
       if (!mUpgradeInsecureRequests) {
-        rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
+        rv = mCSP->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
         NS_ENSURE_SUCCESS_VOID(rv);
       }
       if (!mUpgradeInsecurePreloads) {
         mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
       }
     }
     return;
   }
 
   // 2) apply settings from speculative csp
-  nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
-  rv = NodePrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp));
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (preloadCsp) {
+  if (mPreloadCSP) {
     if (!mBlockAllMixedContentPreloads) {
-      rv = preloadCsp->GetBlockAllMixedContent(&mBlockAllMixedContentPreloads);
+      rv = mPreloadCSP->GetBlockAllMixedContent(&mBlockAllMixedContentPreloads);
       NS_ENSURE_SUCCESS_VOID(rv);
     }
     if (!mUpgradeInsecurePreloads) {
-      rv = preloadCsp->GetUpgradeInsecureRequests(&mUpgradeInsecurePreloads);
+      rv = mPreloadCSP->GetUpgradeInsecureRequests(&mUpgradeInsecurePreloads);
       NS_ENSURE_SUCCESS_VOID(rv);
     }
   }
 }
 
 nsresult Document::InitCSP(nsIChannel* aChannel) {
   MOZ_ASSERT(!mScriptGlobalObject,
              "CSP must be initialized before mScriptGlobalObject is set!");
@@ -2790,20 +2810,51 @@ nsresult Document::InitCSP(nsIChannel* a
     return NS_OK;
   }
 
   // If this is a data document - no need to set CSP.
   if (mLoadedAsData) {
     return NS_OK;
   }
 
+  // If this is a system privileged document - do not apply a CSP.
+  // After Bug 1496418 we can remove the early return and apply
+  // a CSP even when loading using a SystemPrincipal.
+  if (nsContentUtils::IsSystemPrincipal(NodePrincipal())) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(!mCSP, "where did mCSP get set if not here?");
+
+  // If there is a CSP that needs to be inherited either from the
+  // embedding doc or from the opening doc, then we query it here
+  // from the loadinfo because the docshell temporarily stored it
+  // on the loadinfo so we can set it here.
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+  mCSP = static_cast<net::LoadInfo*>(loadInfo.get())->GetCSPToInherit();
+
+  // If there is no CSP to inherit, then we create a new CSP here so
+  // that history entries always have the right reference in case a
+  // Meta CSP gets dynamically added after the history entry has
+  // already been created.
+  if (!mCSP) {
+    mCSP = new nsCSPContext();
+  }
+
+  // Always overwrite the requesting context of the CSP so that any new
+  // 'self' keyword added to an inherited CSP translates correctly.
+  nsresult rv = mCSP->SetRequestContextWithDocument(this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   nsAutoCString tCspHeaderValue, tCspROHeaderValue;
 
   nsCOMPtr<nsIHttpChannel> httpChannel;
-  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
+  rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (httpChannel) {
     Unused << httpChannel->GetResponseHeader(
         NS_LITERAL_CSTRING("content-security-policy"), tCspHeaderValue);
 
@@ -2813,30 +2864,16 @@ nsresult Document::InitCSP(nsIChannel* a
   }
   NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
   NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
 
   // Check if this is a document from a WebExtension.
   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
   auto addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy();
 
-  // Unless the NodePrincipal is a SystemPrincipal, which currently can not
-  // hold a CSP, we always call EnsureCSP() on the Principal. We do this
-  // so that a Meta CSP does not have to create a new CSP object. This is
-  // important because history entries hold a reference to the CSP object,
-  // which then gets dynamically updated in case a meta CSP is present.
-  // Note that after Bug 965637 we can remove that carve out, because the
-  // CSP will hang off the Client/Document and not the Principal anymore.
-  if (principal->IsSystemPrincipal()) {
-    return NS_OK;
-  }
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->EnsureCSP(this, getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // If there's no CSP to apply, go ahead and return early
   if (!addonPolicy && cspHeaderValue.IsEmpty() && cspROHeaderValue.IsEmpty()) {
     if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
       nsCOMPtr<nsIURI> chanURI;
       aChannel->GetURI(getter_AddRefs(chanURI));
       nsAutoCString aspec;
       chanURI->GetAsciiSpec(aspec);
       MOZ_LOG(gCspPRLog, LogLevel::Debug,
@@ -2848,63 +2885,70 @@ nsresult Document::InitCSP(nsIChannel* a
 
   MOZ_LOG(gCspPRLog, LogLevel::Debug,
           ("Document is an add-on or CSP header specified %p", this));
 
   // ----- if the doc is an addon, apply its CSP.
   if (addonPolicy) {
     nsAutoString addonCSP;
     Unused << ExtensionPolicyService::GetSingleton().GetBaseCSP(addonCSP);
-    csp->AppendPolicy(addonCSP, false, false);
-
-    csp->AppendPolicy(addonPolicy->ContentSecurityPolicy(), false, false);
+    mCSP->AppendPolicy(addonCSP, false, false);
+
+    mCSP->AppendPolicy(addonPolicy->ContentSecurityPolicy(), false, false);
+    // Bug 1548468: Move CSP off ExpandedPrincipal
+    // Currently the LoadInfo holds the source of truth for every resource load
+    // because LoadInfo::GetCSP() queries the CSP from an ExpandedPrincipal
+    // (and not from the Client) if the load was triggered by an extension.
+    auto* basePrin = BasePrincipal::Cast(principal);
+    if (basePrin->Is<ExpandedPrincipal>()) {
+      basePrin->As<ExpandedPrincipal>()->SetCsp(mCSP);
+    }
   }
 
   // ----- if there's a full-strength CSP header, apply it.
   if (!cspHeaderValue.IsEmpty()) {
-    rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
+    rv = CSP_AppendCSPFromHeader(mCSP, cspHeaderValue, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // ----- if there's a report-only CSP header, apply it.
   if (!cspROHeaderValue.IsEmpty()) {
-    rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
+    rv = CSP_AppendCSPFromHeader(mCSP, cspROHeaderValue, true);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // ----- Enforce sandbox policy if supplied in CSP header
   // The document may already have some sandbox flags set (e.g. if the document
   // is an iframe with the sandbox attribute set). If we have a CSP sandbox
   // directive, intersect the CSP sandbox flags with the existing flags. This
   // corresponds to the _least_ permissive policy.
   uint32_t cspSandboxFlags = SANDBOXED_NONE;
-  rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
+  rv = mCSP->GetCSPSandboxFlags(&cspSandboxFlags);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Probably the iframe sandbox attribute already caused the creation of a
   // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
   // and no one has been created yet.
   bool needNewNullPrincipal = (cspSandboxFlags & SANDBOXED_ORIGIN) &&
                               !(mSandboxFlags & SANDBOXED_ORIGIN);
 
   mSandboxFlags |= cspSandboxFlags;
 
   if (needNewNullPrincipal) {
     principal = NullPrincipal::CreateWithInheritedAttributes(principal);
-    principal->SetCsp(csp);
     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
-    rv = csp->PermitsAncestry(docShell, &safeAncestry);
+    rv = mCSP->PermitsAncestry(docShell, &safeAncestry);
 
     if (NS_FAILED(rv) || !safeAncestry) {
       MOZ_LOG(gCspPRLog, LogLevel::Debug,
               ("CSP doesn't like frame's ancestry, not loading."));
       // stop!  ERROR page!
       aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
     }
   }
@@ -4728,20 +4772,18 @@ void Document::SetScriptGlobalObject(
   // Remember the pointer to our window (or lack there of), to avoid
   // having to QI every time it's asked for.
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
   mWindow = window;
 
   // Now that we know what our window is, we can flush the CSP errors to the
   // Web Console. We are flushing all messages that occured and were stored
   // in the queue prior to this point.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  if (csp) {
-    static_cast<nsCSPContext*>(csp.get())->flushConsoleMessages();
+  if (mCSP) {
+    static_cast<nsCSPContext*>(mCSP.get())->flushConsoleMessages();
   }
 
   nsCOMPtr<nsIHttpChannelInternal> internalChannel =
       do_QueryInterface(GetChannel());
   if (internalChannel) {
     nsCOMArray<nsISecurityConsoleMessage> messages;
     DebugOnly<nsresult> rv = internalChannel->TakeAllSecurityMessages(messages);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
@@ -5107,22 +5149,21 @@ void Document::DispatchContentLoadedEven
 // Initially we will whitelist legacy about: pages which not yet have a CSP
 // attached, but ultimately that whitelist should disappear.
 // Please note that any about: page should not use inline JS or inline CSS,
 // and instead should load JS and CSS from an external file (*.js, *.css)
 // which allows us to apply a strong CSP omitting 'unsafe-inline'. Ideally,
 // the CSP allows precisely the resources that need to be loaded; but it
 // should at least be as strong as:
 // <meta http-equiv="Content-Security-Policy" content="default-src chrome:"/>
-static void AssertAboutPageHasCSP(nsIURI* aDocumentURI,
-                                  nsIPrincipal* aPrincipal) {
+static void AssertAboutPageHasCSP(Document* aDocument) {
   // Check if we are loading an about: URI at all
+  nsCOMPtr<nsIURI> documentURI = aDocument->GetDocumentURI();
   bool isAboutURI =
-      (NS_SUCCEEDED(aDocumentURI->SchemeIs("about", &isAboutURI)) &&
-       isAboutURI);
+      (NS_SUCCEEDED(documentURI->SchemeIs("about", &isAboutURI)) && isAboutURI);
 
   if (!isAboutURI ||
       Preferences::GetBool("csp.skip_about_page_has_csp_assert")) {
     return;
   }
 
   // Potentially init the legacy whitelist of about URIs without a CSP.
   static StaticAutoPtr<nsTArray<nsCString>> sLegacyAboutPagesWithNoCSP;
@@ -5140,29 +5181,28 @@ static void AssertAboutPageHasCSP(nsIURI
       aboutURI.Append(hostString);
       sLegacyAboutPagesWithNoCSP->AppendElement(aboutURI);
     }
     ClearOnShutdown(&sLegacyAboutPagesWithNoCSP);
   }
 
   // Check if the about URI is whitelisted
   nsAutoCString aboutSpec;
-  aDocumentURI->GetSpec(aboutSpec);
+  documentURI->GetSpec(aboutSpec);
   ToLowerCase(aboutSpec);
   for (auto& legacyPageEntry : *sLegacyAboutPagesWithNoCSP) {
     // please note that we perform a substring match here on purpose,
     // so we don't have to deal and parse out all the query arguments
     // the various about pages rely on.
     if (aboutSpec.Find(legacyPageEntry) == 0) {
       return;
     }
   }
 
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  aPrincipal->GetCsp(getter_AddRefs(csp));
+  nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
   bool foundDefaultSrc = false;
   if (csp) {
     uint32_t policyCount = 0;
     csp->GetPolicyCount(&policyCount);
     nsAutoString parsedPolicyStr;
     for (uint32_t i = 0; i < policyCount; ++i) {
       csp->GetPolicyString(i, parsedPolicyStr);
       if (parsedPolicyStr.Find("default-src") >= 0) {
@@ -5180,17 +5220,17 @@ static void AssertAboutPageHasCSP(nsIURI
 }
 #endif
 
 void Document::EndLoad() {
 #if defined(DEBUG) && !defined(ANDROID)
   // only assert if nothing stopped the load on purpose
   // TODO: we probably also want to check XUL documents here too
   if (!mParserAborted && !IsXULDocument()) {
-    AssertAboutPageHasCSP(mDocumentURI, NodePrincipal());
+    AssertAboutPageHasCSP(this);
   }
 #endif
 
   // EndLoad may have been called without a matching call to BeginLoad, in the
   // case of a failed parse (for example, due to timeout). In such a case, we
   // still want to execute part of this code to do appropriate cleanup, but we
   // gate part of it because it is intended to match 1-for-1 with calls to
   // BeginLoad. We have an explicit flag bit for this purpose, since it's
@@ -11682,22 +11722,19 @@ void Document::SetPageUseCounter(UseCoun
 
 bool Document::HasScriptsBlockedBySandbox() {
   return mSandboxFlags & SANDBOXED_SCRIPTS;
 }
 
 bool Document::InlineScriptAllowedByCSP() {
   // this function assumes the inline script is parser created
   //  (e.g., before setting attribute(!) event handlers)
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, true);
   bool allowsInlineScript = true;
-  if (csp) {
-    nsresult rv = csp->GetAllowsInline(
+  if (mCSP) {
+    nsresult rv = mCSP->GetAllowsInline(
         nsIContentPolicy::TYPE_SCRIPT,
         EmptyString(),  // aNonce
         true,           // aParserCreated
         nullptr,        // aTriggeringElement
         nullptr,        // aCSPEventListener
         EmptyString(),  // FIXME get script sample (bug 1314567)
         0,              // aLineNumber
         0,              // aColumnNumber
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -93,16 +93,17 @@ class nsHtml5TreeOpExecutor;
 class nsHTMLCSSStyleSheet;
 class nsHTMLDocument;
 class nsHTMLStyleSheet;
 class nsGenericHTMLElement;
 class nsAtom;
 class nsIBFCacheEntry;
 class nsIChannel;
 class nsIContent;
+class nsIContentSecurityPolicy;
 class nsIContentSink;
 class nsIDocShell;
 class nsIDocShellTreeItem;
 class nsIDocumentEncoder;
 class nsIDocumentObserver;
 class nsIHTMLCollection;
 class nsILayoutHistoryState;
 class nsILoadContext;
@@ -692,16 +693,34 @@ class Document : public nsINode,
 
   /**
    * Set the base URI for the document loaded via XHR, when accessed from
    * chrome privileged script.
    */
   void SetChromeXHRDocBaseURI(nsIURI* aURI) { mChromeXHRDocBaseURI = aURI; }
 
   /**
+   * The CSP in general is stored in the ClientInfo, but we also cache
+   * the CSP on the document so subresources loaded within a document
+   * can query that cached CSP instead of having to deserialize the CSP
+   * from the Client.
+   *
+   * Please note that at the time of CSP parsing the Client is not
+   * available yet, hence we sync CSP of document and Client when the
+   * Client becomes available within nsGlobalWindowInner::EnsureClientSource().
+   */
+  nsIContentSecurityPolicy* GetCsp() const;
+  void SetCsp(nsIContentSecurityPolicy* aCSP);
+
+  nsIContentSecurityPolicy* GetPreloadCsp() const;
+  void SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCSP);
+
+  void GetCspJSON(nsString& aJSON);
+
+  /**
    * Set referrer policy and upgrade-insecure-requests flags
    */
   void ApplySettingsFromCSP(bool aSpeculative);
 
   already_AddRefed<nsIParser> CreatorParserOrNull() {
     nsCOMPtr<nsIParser> parser = mParser;
     return parser.forget();
   }
@@ -4410,16 +4429,22 @@ class Document : public nsINode,
   // possible flags.
   uint32_t mSandboxFlags;
 
   nsCString mContentLanguage;
 
   // The channel that got passed to Document::StartDocumentLoad(), if any.
   nsCOMPtr<nsIChannel> mChannel;
 
+  // The CSP for every load lives in the Client within the LoadInfo. For all
+  // document-initiated subresource loads we can use that cached version of the
+  // CSP so we do not have to deserialize the CSP from the Client all the time.
+  nsCOMPtr<nsIContentSecurityPolicy> mCSP;
+  nsCOMPtr<nsIContentSecurityPolicy> mPreloadCSP;
+
  private:
   nsCString mContentType;
 
  protected:
   // The document's security info
   nsCOMPtr<nsISupports> mSecurityInfo;
 
   // The channel that failed to load and resulted in an error page.
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -145,25 +145,19 @@ already_AddRefed<nsDocShellLoadState> Lo
     // No document; just use our subject principal as the triggering principal.
     triggeringPrincipal = &aSubjectPrincipal;
   }
 
   // Create load info
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
 
   loadState->SetTriggeringPrincipal(triggeringPrincipal);
-
-  // Currently we query the CSP from the triggeringPrincipal, which is the
-  // doc->NodePrincipal() in case there is a doc. In that case we can query
-  // the CSP directly from the doc after Bug 965637. In case there is no doc,
-  // then we also do not need to query the CSP, because only documents can have
-  // a CSP attached.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  triggeringPrincipal->GetCsp(getter_AddRefs(csp));
-  loadState->SetCsp(csp);
+  if (doc) {
+    loadState->SetCsp(doc->GetCsp());
+  }
 
   if (sourceURI) {
     nsCOMPtr<nsIReferrerInfo> referrerInfo =
         new ReferrerInfo(sourceURI, referrerPolicy);
     loadState->SetReferrerInfo(referrerInfo);
   }
 
   return loadState.forget();
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -65,17 +65,16 @@ nsContentPolicy::~nsContentPolicy() {}
 
 inline nsresult nsContentPolicy::CheckPolicy(CPMethod policyMethod,
                                              nsIURI* contentLocation,
                                              nsILoadInfo* loadInfo,
                                              const nsACString& mimeType,
                                              int16_t* decision) {
   nsContentPolicyType contentType = loadInfo->InternalContentPolicyType();
   nsCOMPtr<nsISupports> requestingContext = loadInfo->GetLoadingContext();
-  nsCOMPtr<nsIPrincipal> requestPrincipal = loadInfo->TriggeringPrincipal();
   nsCOMPtr<nsIURI> requestingLocation;
   nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->LoadingPrincipal();
   if (loadingPrincipal) {
     loadingPrincipal->GetURI(getter_AddRefs(requestingLocation));
   }
 
   // sanity-check passed-through parameters
   MOZ_ASSERT(decision, "Null out pointer");
@@ -93,28 +92,27 @@ inline nsresult nsContentPolicy::CheckPo
   }
 #endif
 
   /*
    * There might not be a requestinglocation. This can happen for
    * iframes with an image as src. Get the uri from the dom node.
    * See bug 254510
    */
-  if (!requestingLocation) {
-    nsCOMPtr<mozilla::dom::Document> doc;
-    nsCOMPtr<nsIContent> node = do_QueryInterface(requestingContext);
-    if (node) {
-      doc = node->OwnerDoc();
-    }
-    if (!doc) {
-      doc = do_QueryInterface(requestingContext);
-    }
-    if (doc) {
-      requestingLocation = doc->GetDocumentURI();
-    }
+  nsCOMPtr<mozilla::dom::Document> doc;
+  nsCOMPtr<nsIContent> node = do_QueryInterface(requestingContext);
+  if (node) {
+    doc = node->OwnerDoc();
+  }
+  if (!doc) {
+    doc = do_QueryInterface(requestingContext);
+  }
+
+  if (!requestingLocation && doc) {
+    requestingLocation = doc->GetDocumentURI();
   }
 
   nsContentPolicyType externalType =
       nsContentUtils::InternalContentPolicyTypeToExternal(contentType);
 
   /*
    * Enumerate mPolicies and ask each of them, taking the logical AND of
    * their permissions.
@@ -124,19 +122,18 @@ inline nsresult nsContentPolicy::CheckPo
 
   nsCOMPtr<nsPIDOMWindowOuter> window;
   if (nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext)) {
     window = node->OwnerDoc()->GetWindow();
   } else {
     window = do_QueryInterface(requestingContext);
   }
 
-  if (requestPrincipal) {
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    requestPrincipal->GetCsp(getter_AddRefs(csp));
+  if (doc) {
+    nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
     if (csp && window) {
       csp->EnsureEventTarget(
           window->EventTargetFor(mozilla::TaskCategory::Other));
     }
   }
 
   int32_t count = entries.Count();
   for (int32_t i = 0; i < count; i++) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5053,22 +5053,18 @@ void nsContentUtils::TriggerLink(nsICont
          !aContent->IsSVGElement(nsGkAtoms::a)) ||
         !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download,
                                         fileName) ||
         NS_FAILED(
             aContent->NodePrincipal()->CheckMayLoad(aLinkURI, false, true))) {
       fileName.SetIsVoid(true);  // No actionable download attribute was found.
     }
 
-    // Currently we query the CSP from the triggeringPrincipal, which is
-    // aContent->NodePrincipal(). After Bug 965637 we can query the CSP
-    // directly from the doc instead (aContent->OwnerDoc()).
     nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    triggeringPrincipal->GetCsp(getter_AddRefs(csp));
+    nsCOMPtr<nsIContentSecurityPolicy> csp = aContent->GetCsp();
 
     handler->OnLinkClick(
         aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : EmptyString(),
         fileName, nullptr, nullptr, EventStateManager::IsHandlingUserInput(),
         aIsTrusted, triggeringPrincipal, csp);
   }
 }
 
@@ -9790,25 +9786,17 @@ bool nsContentUtils::AttemptLargeAllocat
   rv = aChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
   NS_ENSURE_SUCCESS(rv, false);
   if (referrerInfo) {
     referrer = referrerInfo->GetComputedReferrer();
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
-
-  // Currently we query the CSP from the triggeringPrincipal within the
-  // loadInfo. After Bug 965637, we can query the CSP from the loadInfo, which
-  // internally queries the CSP from the Client.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  if (triggeringPrincipal) {
-    rv = triggeringPrincipal->GetCsp(getter_AddRefs(csp));
-    NS_ENSURE_SUCCESS(rv, false);
-  }
+  nsCOMPtr<nsIContentSecurityPolicy> csp = loadInfo->GetCsp();
 
   // Get the channel's load flags, and use them to generate nsIWebNavigation
   // load flags. We want to make sure to propagate the refresh and cache busting
   // flags.
   nsLoadFlags channelLoadFlags;
   aChannel->GetLoadFlags(&channelLoadFlags);
 
   uint32_t webnavLoadFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -68,16 +68,17 @@
 
 #include "Layers.h"
 #include "ClientLayerManager.h"
 
 #include "ContentParent.h"
 #include "BrowserParent.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/ExpandedPrincipal.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ChromeMessageSender.h"
@@ -400,40 +401,34 @@ void nsFrameLoader::LoadFrame(bool aOrig
   nsCOMPtr<nsIPrincipal> principal;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
 
   bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
                   mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
   if (isSrcdoc) {
     src.AssignLiteral("about:srcdoc");
     principal = mOwnerContent->NodePrincipal();
-    // Currently the NodePrincipal holds the CSP for a document. After
-    // Bug 965637 we can query the CSP from mOwnerContent->OwnerDoc()
-    // instead of mOwnerContent->NodePrincipal().
-    mOwnerContent->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+    csp = mOwnerContent->GetCsp();
   } else {
     GetURL(src, getter_AddRefs(principal), getter_AddRefs(csp));
 
     src.Trim(" \t\n\r");
 
     if (src.IsEmpty()) {
       // If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
       // then we will not use 'about:blank' as fallback but return early without
       // starting a load if no 'src' attribute is given (or it's empty).
       if (mOwnerContent->IsXULElement() &&
           mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
                                      nsGkAtoms::_true, eCaseMatters)) {
         return;
       }
       src.AssignLiteral("about:blank");
       principal = mOwnerContent->NodePrincipal();
-      // Currently the NodePrincipal holds the CSP for a document. After
-      // Bug 965637 we can query the CSP from mOwnerContent->OwnerDoc()
-      // instead of mOwnerContent->NodePrincipal().
-      mOwnerContent->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+      csp = mOwnerContent->GetCsp();
     }
   }
 
   Document* doc = mOwnerContent->OwnerDoc();
   if (doc->IsStaticDocument()) {
     return;
   }
 
@@ -515,18 +510,17 @@ void nsFrameLoader::ResumeLoad(uint64_t 
     FireErrorEvent();
     return;
   }
 
   mLoadingOriginalSrc = false;
   mURIToLoad = nullptr;
   mPendingSwitchID = aPendingSwitchID;
   mTriggeringPrincipal = mOwnerContent->NodePrincipal();
-  // NOTE: This can be gotten off of the document after bug 965637.
-  mOwnerContent->NodePrincipal()->GetCsp(getter_AddRefs(mCsp));
+  mCsp = mOwnerContent->GetCsp();
 
   nsresult rv = doc->InitializeFrameLoader(this);
   if (NS_FAILED(rv)) {
     mPendingSwitchID = 0;
     mTriggeringPrincipal = nullptr;
     mCsp = nullptr;
 
     FireErrorEvent();
@@ -622,28 +616,24 @@ nsresult nsFrameLoader::ReallyStartLoadi
   // subframes!
   if (mTriggeringPrincipal) {
     loadState->SetTriggeringPrincipal(mTriggeringPrincipal);
   } else {
     loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
   }
 
   // If we have an explicit CSP, we set it. If not, we only query it from
-  // the NodePrincipal in case there was no explicit triggeringPrincipal.
+  // the document in case there was no explicit triggeringPrincipal.
   // Otherwise it's possible that the original triggeringPrincipal did not
   // have a CSP which causes the CSP on the Principal and explicit CSP
   // to be out of sync.
   if (mCsp) {
     loadState->SetCsp(mCsp);
   } else if (!mTriggeringPrincipal) {
-    // Currently the NodePrincipal holds the CSP for a document. After
-    // Bug 965637 we can query the CSP from mOwnerContent->OwnerDoc()
-    // instead of mOwnerContent->NodePrincipal().
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    mOwnerContent->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+    nsCOMPtr<nsIContentSecurityPolicy> csp = mOwnerContent->GetCsp();
     loadState->SetCsp(csp);
   }
 
   nsCOMPtr<nsIURI> referrer;
 
   nsAutoString srcdoc;
   bool isSrcdoc =
       mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
@@ -2338,37 +2328,37 @@ nsresult nsFrameLoader::MaybeCreateDocSh
 
   return NS_OK;
 }
 
 void nsFrameLoader::GetURL(nsString& aURI, nsIPrincipal** aTriggeringPrincipal,
                            nsIContentSecurityPolicy** aCsp) {
   aURI.Truncate();
   // Within this function we default to using the NodePrincipal as the
-  // triggeringPrincipal and the CSP of the document, currently stored
-  // on the NodePrincipal. After Bug 965637 we can query the CSP from
-  // mOwnerContent->OwnerDoc() instead of mOwnerContent->NodePrincipal().
+  // triggeringPrincipal and the CSP of the document.
   // Expanded Principals however override the CSP of the document, hence
   // if frame->GetSrcTriggeringPrincipal() returns a valid principal, we
-  // have to query the CSP from that Principal. Note that even after
-  // Bug 965637, Expanded Principals will hold their own CSP.
+  // have to query the CSP from that Principal.
   nsCOMPtr<nsIPrincipal> triggeringPrincipal = mOwnerContent->NodePrincipal();
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  mOwnerContent->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+  nsCOMPtr<nsIContentSecurityPolicy> csp = mOwnerContent->GetCsp();
 
   if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) {
     mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, aURI);
   } else {
     mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, aURI);
     if (RefPtr<nsGenericHTMLFrameElement> frame =
             do_QueryObject(mOwnerContent)) {
       nsCOMPtr<nsIPrincipal> srcPrincipal = frame->GetSrcTriggeringPrincipal();
       if (srcPrincipal) {
         triggeringPrincipal = srcPrincipal;
-        triggeringPrincipal->GetCsp(getter_AddRefs(csp));
+        nsCOMPtr<nsIExpandedPrincipal> ep =
+            do_QueryInterface(triggeringPrincipal);
+        if (ep) {
+          csp = ep->GetCsp();
+        }
       }
     }
   }
   triggeringPrincipal.forget(aTriggeringPrincipal);
   csp.forget(aCsp);
 }
 
 nsresult nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) {
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -45,16 +45,17 @@
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsISizeOfEventTarget.h"
 #include "nsDOMJSUtils.h"
 #include "nsArrayUtils.h"
 #include "nsDOMWindowList.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
+#include "nsIContentSecurityPolicy.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocumentLoader.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPermission.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptTimeoutHandler.h"
 #include "nsITimeoutHandler.h"
@@ -1117,21 +1118,22 @@ void nsGlobalWindowInner::FreeInnerObjec
 
   mScreen = nullptr;
 
 #if defined(MOZ_WIDGET_ANDROID)
   mOrientationChangeObserver = nullptr;
 #endif
 
   if (mDoc) {
-    // Remember the document's principal and URI.
+    // Remember the document's principal, URI, and CSP.
     mDocumentPrincipal = mDoc->NodePrincipal();
     mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
     mDocumentURI = mDoc->GetDocumentURI();
     mDocBaseURI = mDoc->GetDocBaseURI();
+    mDocumentCsp = mDoc->GetCsp();
 
     while (mDoc->EventHandlingSuppressed()) {
       mDoc->UnsuppressEventHandlingAndFireEvents(false);
     }
 
     if (mObservingDidRefresh) {
       PresShell* presShell = mDoc->GetPresShell();
       if (presShell) {
@@ -1345,16 +1347,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   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(mDocumentCsp)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
   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));
   }
 
@@ -1449,16 +1452,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     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(mDocumentCsp)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
 
@@ -1784,16 +1788,24 @@ nsresult nsGlobalWindowInner::EnsureClie
       mClientSource = ClientManager::CreateSource(
           ClientType::Window, EventTargetFor(TaskCategory::Other),
           mDoc->NodePrincipal());
       MOZ_DIAGNOSTIC_ASSERT(mClientSource);
       newClientSource = true;
     }
   }
 
+  // Generally the CSP is stored within the Client and cached on the document.
+  // At the time of CSP parsing however, the Client has not been created yet,
+  // hence we store the CSP on the document and propagate/sync the CSP with
+  // Client here when we create the Client.
+  if (mClientSource) {
+    mClientSource->SetCsp(mDoc->GetCsp());
+  }
+
   // Its possible that we got a client just after being frozen in
   // the bfcache.  In that case freeze the client immediately.
   if (newClientSource && IsFrozen()) {
     mClientSource->Freeze();
   }
 
   return NS_OK;
 }
@@ -2192,16 +2204,28 @@ Maybe<ClientInfo> nsPIDOMWindowInner::Ge
 Maybe<ClientState> nsPIDOMWindowInner::GetClientState() const {
   return nsGlobalWindowInner::Cast(this)->GetClientState();
 }
 
 Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
   return nsGlobalWindowInner::Cast(this)->GetController();
 }
 
+void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
+  return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp);
+}
+
+void nsPIDOMWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
+  return nsGlobalWindowInner::Cast(this)->SetPreloadCsp(aPreloadCsp);
+}
+
+nsIContentSecurityPolicy* nsPIDOMWindowInner::GetCsp() {
+  return nsGlobalWindowInner::Cast(this)->GetCsp();
+}
+
 void nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(
     const nsACString& aScope) {
   nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(
       aScope);
 }
 
 void nsPIDOMWindowInner::NoteDOMContentLoaded() {
   nsGlobalWindowInner::Cast(this)->NoteDOMContentLoaded();
@@ -5358,16 +5382,47 @@ Maybe<ServiceWorkerDescriptor> nsGlobalW
   MOZ_ASSERT(NS_IsMainThread());
   Maybe<ServiceWorkerDescriptor> controller;
   if (mClientSource) {
     controller = mClientSource->GetController();
   }
   return controller;
 }
 
+void nsGlobalWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
+  if (!mClientSource) {
+    return;
+  }
+  mClientSource->SetCsp(aCsp);
+  // Also cache the CSP within the document
+  mDoc->SetCsp(aCsp);
+}
+
+void nsGlobalWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
+  if (!mClientSource) {
+    return;
+  }
+  mClientSource->SetPreloadCsp(aPreloadCsp);
+  // Also cache the preload CSP within the document
+  mDoc->SetPreloadCsp(aPreloadCsp);
+}
+
+nsIContentSecurityPolicy* nsGlobalWindowInner::GetCsp() {
+  if (mDoc) {
+    return mDoc->GetCsp();
+  }
+
+  // If the window is partially torn down and has its document nulled out,
+  // we query the CSP we snapshot in FreeInnerObjects.
+  if (mDocumentCsp) {
+    return mDocumentCsp;
+  }
+  return nullptr;
+}
+
 RefPtr<ServiceWorker> nsGlobalWindowInner::GetOrCreateServiceWorker(
     const ServiceWorkerDescriptor& aDescriptor) {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<ServiceWorker> ref;
   ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
     RefPtr<ServiceWorker> sw = do_QueryObject(aTarget);
     if (!sw || !sw->Descriptor().Matches(aDescriptor)) {
       return;
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -317,16 +317,20 @@ class nsGlobalWindowInner final : public
   virtual bool IsFrozen() const override;
   void SyncStateFromParentWindow();
 
   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const override;
   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController()
       const override;
 
+  void SetCsp(nsIContentSecurityPolicy* aCsp);
+  void SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp);
+  nsIContentSecurityPolicy* GetCsp();
+
   virtual RefPtr<mozilla::dom::ServiceWorker> GetOrCreateServiceWorker(
       const mozilla::dom::ServiceWorkerDescriptor& aDescriptor) override;
 
   RefPtr<mozilla::dom::ServiceWorkerRegistration> GetServiceWorkerRegistration(
       const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor)
       const override;
 
   RefPtr<mozilla::dom::ServiceWorkerRegistration>
@@ -1296,18 +1300,21 @@ class nsGlobalWindowInner final : public
   RefPtr<mozilla::dom::Location> mLocation;
   RefPtr<nsHistory> mHistory;
   RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
 
   nsTObserverArray<RefPtr<mozilla::dom::SharedWorker>> mSharedWorkers;
 
   RefPtr<mozilla::dom::VisualViewport> mVisualViewport;
 
+  // The document's principals and CSP are only stored if
+  // FreeInnerObjects has been called.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIPrincipal> mDocumentStoragePrincipal;
+  nsCOMPtr<nsIContentSecurityPolicy> mDocumentCsp;
 
   // mBrowserChild is only ever populated in the content process.
   nsCOMPtr<nsIBrowserChild> mBrowserChild;
 
   uint32_t mSuspendDepth;
   uint32_t mFreezeDepth;
 
 #ifdef DEBUG
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1543,17 +1543,18 @@ bool nsGlobalWindowOuter::WouldReuseInne
       equal) {
     // The origin is the same.
     return true;
   }
 
   return false;
 }
 
-void nsGlobalWindowOuter::SetInitialPrincipalToSubject() {
+void nsGlobalWindowOuter::SetInitialPrincipalToSubject(
+    nsIContentSecurityPolicy* aCSP) {
   // First, grab the subject principal.
   nsCOMPtr<nsIPrincipal> newWindowPrincipal =
       nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
 
   // We should never create windows with an expanded principal.
   // If we have a system principal, make sure we're not using it for a content
   // docshell.
   // NOTE: Please keep this logic in sync with nsWebShellWindow::Initialize().
@@ -1576,17 +1577,17 @@ void nsGlobalWindowOuter::SetInitialPrin
     // NullPrincipal.
     bool isNullPrincipal;
     MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal(
                    &isNullPrincipal)) &&
                isNullPrincipal);
 #endif
   }
 
-  GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal);
+  GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal, aCSP);
 
   if (mDoc) {
     mDoc->SetIsInitialDocument(true);
   }
 
   RefPtr<PresShell> presShell = GetDocShell()->GetPresShell();
   if (presShell && !presShell->DidInitialize()) {
     // Ensure that if someone plays with this document they will get
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -293,17 +293,18 @@ class nsGlobalWindowOuter final : public
   virtual void ActivateOrDeactivate(bool aActivate) override;
   virtual void SetActive(bool aActive) override;
   virtual bool IsTopLevelWindowActive() override;
   virtual void SetIsBackground(bool aIsBackground) override;
   virtual void SetChromeEventHandler(
       mozilla::dom::EventTarget* aChromeEventHandler) override;
 
   // Outer windows only.
-  virtual void SetInitialPrincipalToSubject() override;
+  virtual void SetInitialPrincipalToSubject(
+      nsIContentSecurityPolicy* aCSP) override;
 
   virtual already_AddRefed<nsISupports> SaveWindowState() override;
   virtual nsresult RestoreWindowState(nsISupports* aState) override;
 
   virtual bool IsSuspended() const override;
   virtual bool IsFrozen() const override;
 
   virtual nsresult FireDelayedDOMEvents() override;
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -190,16 +190,20 @@ void nsINode::DeleteProperty(const nsAto
   OwnerDoc()->PropertyTable().DeleteProperty(this, aPropertyName);
 }
 
 void* nsINode::UnsetProperty(const nsAtom* aPropertyName, nsresult* aStatus) {
   return OwnerDoc()->PropertyTable().UnsetProperty(this, aPropertyName,
                                                    aStatus);
 }
 
+nsIContentSecurityPolicy* nsINode::GetCsp() const {
+  return OwnerDoc()->GetCsp();
+}
+
 nsINode::nsSlots* nsINode::CreateSlots() { return new nsSlots(); }
 
 nsIContent* nsINode::GetTextEditorRootContent(TextEditor** aTextEditor) {
   if (aTextEditor) {
     *aTextEditor = nullptr;
   }
   for (nsINode* node = this; node; node = node->GetParentNode()) {
     if (!node->IsElement() || !node->IsHTMLElement()) continue;
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -35,16 +35,17 @@
 #  endif
 #endif
 
 class AttrArray;
 class nsAttrChildContentList;
 class nsDOMAttributeMap;
 class nsIAnimationObserver;
 class nsIContent;
+class nsIContentSecurityPolicy;
 class nsIFrame;
 class nsIHTMLCollection;
 class nsIMutationObserver;
 class nsINode;
 class nsINodeList;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeSupportsWeakRefTearoff;
@@ -850,16 +851,21 @@ class nsINode : public mozilla::dom::Eve
    * Return the principal of this node.  This is guaranteed to never be a null
    * pointer.
    */
   nsIPrincipal* NodePrincipal() const {
     return mNodeInfo->NodeInfoManager()->DocumentPrincipal();
   }
 
   /**
+   * Return the CSP of this node's document, if any.
+   */
+  nsIContentSecurityPolicy* GetCsp() const;
+
+  /**
    * Get the parent nsIContent for this node.
    * @return the parent, or null if no parent or the parent is not an nsIContent
    */
   nsIContent* GetParent() const {
     return MOZ_LIKELY(GetBoolFlag(ParentIsContent))
                ? reinterpret_cast<nsIContent*>(mParent)
                : nullptr;
   }
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -13,17 +13,16 @@
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/LoadedScript.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsGlobalWindow.h"
-#include "nsIContentSecurityPolicy.h"
 #include "mozilla/dom/Document.h"
 #include "nsIScriptTimeoutHandler.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -26,16 +26,17 @@
 
 class nsDOMOfflineResourceList;
 class nsDOMWindowList;
 class nsGlobalWindowInner;
 class nsGlobalWindowOuter;
 class nsIArray;
 class nsIChannel;
 class nsIContent;
+class nsIContentSecurityPolicy;
 class nsICSSDeclaration;
 class nsIDocShell;
 class nsDocShellLoadState;
 class nsIPrincipal;
 class nsIRunnable;
 class nsIScriptTimeoutHandler;
 class nsISerialEventTarget;
 class nsIURI;
@@ -321,16 +322,20 @@ class nsPIDOMWindowInner : public mozIDO
   // Return true if there are any open WebSockets that could block
   // timeout-throttling.
   bool HasOpenWebSockets() const;
 
   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
 
+  void SetCsp(nsIContentSecurityPolicy* aCsp);
+  void SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp);
+  nsIContentSecurityPolicy* GetCsp();
+
   void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
 
   void NoteDOMContentLoaded();
 
   mozilla::dom::TabGroup* TabGroup();
 
   virtual mozilla::dom::CustomElementRegistry* CustomElements() = 0;
 
@@ -816,18 +821,18 @@ class nsPIDOMWindowOuter : public mozIDO
   Document* GetDoc() {
     if (!mDoc) {
       MaybeCreateDoc();
     }
     return mDoc;
   }
 
   // Set the window up with an about:blank document with the current subject
-  // principal.
-  virtual void SetInitialPrincipalToSubject() = 0;
+  // principal and potentially a CSP.
+  virtual void SetInitialPrincipalToSubject(nsIContentSecurityPolicy* aCSP) = 0;
 
   // Returns an object containing the window's state.  This also suspends
   // all running timeouts in the window.
   virtual already_AddRefed<nsISupports> SaveWindowState() = 0;
 
   // Restore the window state from aState.
   virtual nsresult RestoreWindowState(nsISupports* aState) = 0;
 
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -325,19 +325,18 @@ nsStyleLinkElement::DoUpdateStyleSheet(D
       return Err(NS_ERROR_OUT_OF_MEMORY);
     }
 
     MOZ_ASSERT(thisContent->NodeInfo()->NameAtom() != nsGkAtoms::link,
                "<link> is not 'inline', and needs different CSP checks");
     MOZ_ASSERT(thisContent->IsElement());
     nsresult rv = NS_OK;
     if (!nsStyleUtil::CSPAllowsInlineStyle(
-            thisContent->AsElement(), thisContent->NodePrincipal(),
-            info->mTriggeringPrincipal, doc->GetDocumentURI(), mLineNumber,
-            mColumnNumber, text, &rv)) {
+            thisContent->AsElement(), doc, info->mTriggeringPrincipal,
+            mLineNumber, mColumnNumber, text, &rv)) {
       if (NS_FAILED(rv)) {
         return Err(rv);
       }
       return Update{};
     }
 
     // Parse the style sheet.
     return doc->CSSLoader()->LoadInlineStyle(*info, text, mLineNumber,
--- a/dom/base/nsStyledElement.cpp
+++ b/dom/base/nsStyledElement.cpp
@@ -180,19 +180,19 @@ nsICSSDeclaration* nsStyledElement::GetE
 
 void nsStyledElement::ParseStyleAttribute(const nsAString& aValue,
                                           nsIPrincipal* aMaybeScriptedPrincipal,
                                           nsAttrValue& aResult,
                                           bool aForceInDataDoc) {
   Document* doc = OwnerDoc();
   bool isNativeAnon = IsInNativeAnonymousSubtree();
 
-  if (!isNativeAnon && !nsStyleUtil::CSPAllowsInlineStyle(
-                           this, NodePrincipal(), aMaybeScriptedPrincipal,
-                           doc->GetDocumentURI(), 0, 0, aValue, nullptr))
+  if (!isNativeAnon &&
+      !nsStyleUtil::CSPAllowsInlineStyle(this, doc, aMaybeScriptedPrincipal, 0,
+                                         0, aValue, nullptr))
     return;
 
   if (aForceInDataDoc || !doc->IsLoadedAsData() || GetExistingStyle() ||
       doc->IsStaticDocument()) {
     bool isCSS = true;  // assume CSS until proven otherwise
 
     if (!isNativeAnon) {  // native anonymous content always assumes CSS
       nsAutoString styleType;
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -2487,29 +2487,25 @@ nsresult ReadResponse(mozIStorageConnect
 #ifdef DEBUG
     nsDependentCSubstring scheme = url->Scheme();
     MOZ_ASSERT(scheme == "http" || scheme == "https" || scheme == "file");
 #endif
 
     nsCString origin;
     url->Origin(origin);
 
-    // CSP is recovered from the headers, no need to initialise it here.
-    nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
-
     nsCString baseDomain;
     rv = url->BaseDomain(baseDomain);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     aSavedResponseOut->mValue.principalInfo() =
         Some(mozilla::ipc::ContentPrincipalInfo(attrs, origin, specNoSuffix,
-                                                Nothing(), std::move(policies),
-                                                baseDomain));
+                                                Nothing(), baseDomain));
   }
 
   bool nullPadding = false;
   rv = state->GetIsNull(6, &nullPadding);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/dom/clients/manager/ClientIPCTypes.ipdlh
+++ b/dom/clients/manager/ClientIPCTypes.ipdlh
@@ -27,16 +27,18 @@ struct ClientSourceConstructorArgs
 struct IPCClientInfo
 {
   nsID id;
   ClientType type;
   PrincipalInfo principalInfo;
   TimeStamp creationTime;
   nsCString url;
   FrameType frameType;
+  CSPInfo? cspInfo;
+  CSPInfo? preloadCspInfo;
 };
 
 struct IPCClientWindowState
 {
   VisibilityState visibilityState;
   TimeStamp lastFocusTime;
   StorageAccess storageAccess;
   bool focused;
--- a/dom/clients/manager/ClientInfo.cpp
+++ b/dom/clients/manager/ClientInfo.cpp
@@ -15,17 +15,19 @@ namespace dom {
 using mozilla::ipc::PrincipalInfo;
 using mozilla::ipc::PrincipalInfoToPrincipal;
 
 ClientInfo::ClientInfo(const nsID& aId, ClientType aType,
                        const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                        const TimeStamp& aCreationTime)
     : mData(MakeUnique<IPCClientInfo>(aId, aType, aPrincipalInfo, aCreationTime,
                                       EmptyCString(),
-                                      mozilla::dom::FrameType::None)) {}
+                                      mozilla::dom::FrameType::None,
+                                      mozilla::Nothing(), mozilla::Nothing())) {
+}
 
 ClientInfo::ClientInfo(const IPCClientInfo& aData)
     : mData(MakeUnique<IPCClientInfo>(aData)) {}
 
 ClientInfo::ClientInfo(const ClientInfo& aRight) { operator=(aRight); }
 
 ClientInfo& ClientInfo::operator=(const ClientInfo& aRight) {
   mData.reset();
@@ -92,10 +94,27 @@ bool ClientInfo::IsPrivateBrowsing() con
 }
 
 nsCOMPtr<nsIPrincipal> ClientInfo::GetPrincipal() const {
   MOZ_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIPrincipal> ref = PrincipalInfoToPrincipal(PrincipalInfo());
   return ref;
 }
 
+const Maybe<mozilla::ipc::CSPInfo>& ClientInfo::GetCspInfo() const {
+  return mData->cspInfo();
+}
+
+void ClientInfo::SetCspInfo(const mozilla::ipc::CSPInfo& aCSPInfo) {
+  mData->cspInfo() = Some(aCSPInfo);
+}
+
+const Maybe<mozilla::ipc::CSPInfo>& ClientInfo::GetPreloadCspInfo() const {
+  return mData->preloadCspInfo();
+}
+
+void ClientInfo::SetPreloadCspInfo(
+    const mozilla::ipc::CSPInfo& aPreloadCSPInfo) {
+  mData->preloadCspInfo() = Some(aPreloadCSPInfo);
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/clients/manager/ClientInfo.h
+++ b/dom/clients/manager/ClientInfo.h
@@ -3,21 +3,23 @@
 /* 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_dom_ClientInfo_h
 #define _mozilla_dom_ClientInfo_h
 
 #include "mozilla/dom/ClientBinding.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 
 namespace ipc {
+class CSPInfo;
 class PrincipalInfo;
 }  // namespace ipc
 
 namespace dom {
 
 class IPCClientInfo;
 
 // This class provides a simple structure that represents a global living
@@ -82,14 +84,20 @@ class ClientInfo final {
   const IPCClientInfo& ToIPC() const;
 
   // Determine if the client is in private browsing mode.
   bool IsPrivateBrowsing() const;
 
   // Get a main-thread nsIPrincipal for the client.  This may return nullptr
   // if the PrincipalInfo() fails to deserialize for some reason.
   nsCOMPtr<nsIPrincipal> GetPrincipal() const;
+
+  const Maybe<mozilla::ipc::CSPInfo>& GetCspInfo() const;
+  void SetCspInfo(const mozilla::ipc::CSPInfo& aCSPInfo);
+
+  const Maybe<mozilla::ipc::CSPInfo>& GetPreloadCspInfo() const;
+  void SetPreloadCspInfo(const mozilla::ipc::CSPInfo& aPreloadCSPInfo);
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // _mozilla_dom_ClientInfo_h
--- a/dom/clients/manager/ClientNavigateOpChild.cpp
+++ b/dom/clients/manager/ClientNavigateOpChild.cpp
@@ -226,24 +226,17 @@ RefPtr<ClientOpPromise> ClientNavigateOp
                                             __func__);
   }
 
   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(url);
   nsCOMPtr<nsIReferrerInfo> referrerInfo =
       new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
   loadState->SetTriggeringPrincipal(principal);
 
-  // Currently we query the CSP from the principal, which is the
-  // doc->NodePrincipal(). After Bug 965637 we can query the CSP
-  // from the doc directly.
-  if (principal) {
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    principal->GetCsp(getter_AddRefs(csp));
-    loadState->SetCsp(csp);
-  }
+  loadState->SetCsp(doc->GetCsp());
 
   loadState->SetReferrerInfo(referrerInfo);
   loadState->SetLoadType(LOAD_STOP_CONTENT);
   loadState->SetSourceDocShell(docShell);
   loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
   loadState->SetFirstParty(true);
   rv = docShell->LoadURI(loadState);
   if (NS_FAILED(rv)) {
--- a/dom/clients/manager/ClientSource.cpp
+++ b/dom/clients/manager/ClientSource.cpp
@@ -18,24 +18,29 @@
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
+#include "nsIContentSecurityPolicy.h"
 #include "nsContentUtils.h"
 #include "nsIDocShell.h"
 #include "nsPIDOMWindow.h"
 
+#include "mozilla/ipc/BackgroundUtils.h"
+
 namespace mozilla {
 namespace dom {
 
 using mozilla::dom::ipc::StructuredCloneData;
+using mozilla::ipc::CSPInfo;
+using mozilla::ipc::CSPToCSPInfo;
 using mozilla::ipc::PrincipalInfo;
 using mozilla::ipc::PrincipalInfoToPrincipal;
 
 void ClientSource::Shutdown() {
   NS_ASSERT_OWNINGTHREAD(ClientSource);
   if (IsShutdown()) {
     return;
   }
@@ -652,16 +657,54 @@ nsresult ClientSource::SnapshotState(Cli
   }
 
   *aStateOut = ClientState(ClientWorkerState(workerPrivate->StorageAccess()));
   return NS_OK;
 }
 
 nsISerialEventTarget* ClientSource::EventTarget() const { return mEventTarget; }
 
+void ClientSource::SetCsp(nsIContentSecurityPolicy* aCsp) {
+  NS_ASSERT_OWNINGTHREAD(ClientSource);
+  if (!aCsp) {
+    return;
+  }
+
+  CSPInfo cspInfo;
+  nsresult rv = CSPToCSPInfo(aCsp, &cspInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+  mClientInfo.SetCspInfo(cspInfo);
+}
+
+void ClientSource::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
+  NS_ASSERT_OWNINGTHREAD(ClientSource);
+  if (!aPreloadCsp) {
+    return;
+  }
+
+  CSPInfo cspPreloadInfo;
+  nsresult rv = CSPToCSPInfo(aPreloadCsp, &cspPreloadInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+  mClientInfo.SetPreloadCspInfo(cspPreloadInfo);
+}
+
+void ClientSource::SetCspInfo(const CSPInfo& aCSPInfo) {
+  NS_ASSERT_OWNINGTHREAD(ClientSource);
+  mClientInfo.SetCspInfo(aCSPInfo);
+}
+
+const Maybe<mozilla::ipc::CSPInfo>& ClientSource::GetCspInfo() {
+  NS_ASSERT_OWNINGTHREAD(ClientSource);
+  return mClientInfo.GetCspInfo();
+}
+
 void ClientSource::Traverse(nsCycleCollectionTraversalCallback& aCallback,
                             const char* aName, uint32_t aFlags) {
   if (mOwner.is<RefPtr<nsPIDOMWindowInner>>()) {
     ImplCycleCollectionTraverse(
         aCallback, mOwner.as<RefPtr<nsPIDOMWindowInner>>(), aName, aFlags);
   } else if (mOwner.is<nsCOMPtr<nsIDocShell>>()) {
     ImplCycleCollectionTraverse(aCallback, mOwner.as<nsCOMPtr<nsIDocShell>>(),
                                 aName, aFlags);
--- a/dom/clients/manager/ClientSource.h
+++ b/dom/clients/manager/ClientSource.h
@@ -11,16 +11,17 @@
 #include "mozilla/dom/ClientThing.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/Variant.h"
 
 #ifdef XP_WIN
 #  undef PostMessage
 #endif
 
+class nsIContentSecurityPolicy;
 class nsIDocShell;
 class nsIGlobalObject;
 class nsISerialEventTarget;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
@@ -143,16 +144,21 @@ class ClientSource final : public Client
 
   RefPtr<ClientOpPromise> GetInfoAndState(
       const ClientGetInfoAndStateArgs& aArgs);
 
   nsresult SnapshotState(ClientState* aStateOut);
 
   nsISerialEventTarget* EventTarget() const;
 
+  void SetCsp(nsIContentSecurityPolicy* aCsp);
+  void SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCSP);
+  void SetCspInfo(const mozilla::ipc::CSPInfo& aCSPInfo);
+  const Maybe<mozilla::ipc::CSPInfo>& GetCspInfo();
+
   void Traverse(nsCycleCollectionTraversalCallback& aCallback,
                 const char* aName, uint32_t aFlags);
 
   void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
 
   bool CalledRegisterForServiceWorkerScope(const nsACString& aScope);
 };
 
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -780,20 +780,17 @@ nsresult EventListenerManager::SetEventH
   if (doc) {
     // Don't allow adding an event listener if the document is sandboxed
     // without 'allow-scripts'.
     if (doc->HasScriptsBlockedBySandbox()) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     // Perform CSP check
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-    NS_ENSURE_SUCCESS(rv, rv);
-
+    nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
     unsigned lineNum = 0;
     unsigned columnNum = 0;
 
     JSContext* cx = nsContentUtils::GetCurrentJSContext();
     if (cx && !JS::DescribeScriptedCaller(cx, nullptr, &lineNum, &columnNum)) {
       JS_ClearPendingException(cx);
     }
 
--- a/dom/html/HTMLFormSubmission.cpp
+++ b/dom/html/HTMLFormSubmission.cpp
@@ -820,19 +820,17 @@ nsresult HTMLFormSubmission::GetFromForm
   nsresult rv;
 
   // Get action
   nsCOMPtr<nsIURI> actionURL;
   rv = aForm->GetActionURL(getter_AddRefs(actionURL), aOriginatingElement);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Check if CSP allows this form-action
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = aForm->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIContentSecurityPolicy> csp = aForm->GetCsp();
   if (csp) {
     bool permitsFormAction = true;
 
     // form-action is only enforced if explicitly defined in the
     // policy - do *not* consult default-src, see:
     // http://www.w3.org/TR/CSP2/#directive-default-src
     rv = csp->Permits(aForm, nullptr /* nsICSPEventListener */, actionURL,
                       nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE, true,
--- a/dom/html/HTMLMetaElement.cpp
+++ b/dom/html/HTMLMetaElement.cpp
@@ -91,19 +91,17 @@ nsresult HTMLMetaElement::BindToTree(Doc
     Element* headElt = aDocument->GetHeadElement();
     if (headElt && nsContentUtils::ContentIsDescendantOf(this, headElt)) {
       nsAutoString content;
       GetContent(content);
       content =
           nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
               content);
 
-      nsIPrincipal* principal = aDocument->NodePrincipal();
-      nsCOMPtr<nsIContentSecurityPolicy> csp;
-      principal->EnsureCSP(aDocument, getter_AddRefs(csp));
+      nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
       if (csp) {
         if (LOG_ENABLED()) {
           nsAutoCString documentURIspec;
           nsIURI* documentURI = aDocument->GetDocumentURI();
           if (documentURI) {
             documentURI->GetAsciiSpec(documentURIspec);
           }
 
@@ -117,16 +115,20 @@ nsresult HTMLMetaElement::BindToTree(Doc
         // Multiple CSPs (delivered through either header of meta tag) need to
         // be joined together, see:
         // https://w3c.github.io/webappsec/specs/content-security-policy/#delivery-html-meta-element
         rv =
             csp->AppendPolicy(content,
                               false,  // csp via meta tag can not be report only
                               true);  // delivered through the meta tag
         NS_ENSURE_SUCCESS(rv, rv);
+        nsPIDOMWindowInner* inner = aDocument->GetInnerWindow();
+        if (inner) {
+          inner->SetCsp(csp);
+        }
         aDocument->ApplySettingsFromCSP(false);
       }
     }
   }
 
   // Referrer Policy spec requires a <meta name="referrer" tag to be in the
   // <head> element.
   SetMetaReferrer(aDocument);
--- a/dom/html/HTMLSharedElement.cpp
+++ b/dom/html/HTMLSharedElement.cpp
@@ -143,24 +143,18 @@ static void SetBaseURIUsingFirstBaseWith
       child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
 
       nsCOMPtr<nsIURI> newBaseURI;
       nsContentUtils::NewURIWithDocumentCharset(
           getter_AddRefs(newBaseURI), href, aDocument,
           aDocument->GetFallbackBaseURI());
 
       // Check if CSP allows this base-uri
-      nsCOMPtr<nsIContentSecurityPolicy> csp;
-      nsresult rv = aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-      NS_ASSERTION(NS_SUCCEEDED(rv), "Getting CSP Failed");
-      // For all the different error cases we assign a nullptr to
-      // newBaseURI, so we basically call aDocument->SetBaseURI(nullptr);
-      if (NS_FAILED(rv)) {
-        newBaseURI = nullptr;
-      }
+      nsresult rv = NS_OK;
+      nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
       if (csp && newBaseURI) {
         // base-uri is only enforced if explicitly defined in the
         // policy - do *not* consult default-src, see:
         // http://www.w3.org/TR/CSP2/#directive-default-src
         bool cspPermitsBaseURI = true;
         rv = csp->Permits(child->AsElement(), nullptr /* nsICSPEventListener */,
                           newBaseURI,
                           nsIContentSecurityPolicy::BASE_URI_DIRECTIVE, true,
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/dom/FeaturePolicyUtils.h"
 #include "nsCommandManager.h"
 #include "nsCOMPtr.h"
 #include "nsGlobalWindow.h"
 #include "nsString.h"
 #include "nsPrintfCString.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
+#include "nsIContentSecurityPolicy.h"
 #include "nsGlobalWindowInner.h"
 #include "nsIDocumentLoader.h"
 #include "nsIHTMLContentSink.h"
 #include "nsIXMLContentSink.h"
 #include "nsHTMLParts.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsGkAtoms.h"
 #include "nsPresContext.h"
@@ -97,16 +98,17 @@
 #include "nsMimeTypes.h"
 #include "nsIRequest.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5Parser.h"
 #include "nsSandboxFlags.h"
 #include "nsIImageDocument.h"
 #include "mozilla/dom/HTMLBodyElement.h"
 #include "mozilla/dom/HTMLDocumentBinding.h"
+#include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
 #include "nsCharsetSource.h"
 #include "nsIStringBundle.h"
 #include "nsFocusManager.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsIStructuredCloneContainer.h"
@@ -1038,16 +1040,27 @@ Document* nsHTMLDocument::Open(const Opt
   if (shell) {
     bool inUnload;
     shell->GetIsInUnload(&inUnload);
     if (inUnload) {
       return this;
     }
   }
 
+  // document.open() inherits the CSP from the opening document.
+  // Please create an actual copy of the CSP (do not share the same
+  // reference) otherwise appending a new policy within the opened
+  // document will be incorrectly propagated to the opening doc.
+  nsCOMPtr<nsIContentSecurityPolicy> csp = callerDoc->GetCsp();
+  if (csp) {
+    RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
+    cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
+    mCSP = cspToInherit;
+  }
+
   // At this point we know this is a valid-enough document.open() call
   // and not a no-op.  Increment our use counter.
   SetDocumentAndPageUseCounter(eUseCounter_custom_DocumentOpen);
 
   // Step 7 -- stop existing navigation of our browsing context (and all other
   // loads it's doing) if we're the active document of our browsing context.
   // Note that we do not want to stop anything if there is no existing
   // navigation.
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -216,20 +216,31 @@ interface nsIContentSecurityPolicy : nsI
   const unsigned short VIOLATION_TYPE_HASH_STYLE             = 7;
   const unsigned short VIOLATION_TYPE_REQUIRE_SRI_FOR_STYLE  = 8;
   const unsigned short VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT = 9;
 
   /**
    * Called after the CSP object is created to fill in appropriate request
    * context. Either use
    *  * aDocument (preferred), or if no document is available, then provide
-   *  * aPrincipal
+   *  * aPrincipal, aSelfURI, aReferrer, aInnerWindowId explicitly.
    */
-  void setRequestContext(in Document aDocument,
-                         in nsIPrincipal aPrincipal);
+  [must_use] void setRequestContextWithDocument(in Document aDocument);
+  [must_use] void setRequestContextWithPrincipal(in nsIPrincipal aRequestPrincipal,
+                                                 in nsIURI aSelfURI,
+                                                 in AString aReferrer,
+                                                 in unsigned long long aInnerWindowId);
+
+  /**
+   * Get the various arguments needed to create a new request context for a CSP.
+   */
+  [noscript, notxpcom, nostdcall] readonly attribute nsIPrincipal requestPrincipal;
+  [noscript, notxpcom, nostdcall] readonly attribute nsIURI selfURI;
+  [noscript] readonly attribute AString referrer;
+  [noscript, notxpcom, nostdcall] readonly attribute unsigned long long innerWindowID;
 
   /**
    *  Ensure we have a nsIEventTarget to use to label CSPReportSenderRunnable
    */
   [noscript] void ensureEventTarget(in nsIEventTarget aEventTarget);
 
  
   /**
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -805,20 +805,17 @@ static nsresult GetCreateWindowParams(mo
     referrerInfo.swap(*aReferrerInfo);
     NS_ADDREF(*aTriggeringPrincipal = nullPrincipal);
     return NS_OK;
   }
 
   nsCOMPtr<Document> doc = opener->GetDoc();
   NS_ADDREF(*aTriggeringPrincipal = doc->NodePrincipal());
 
-  // Currently we query the CSP from the doc->NodePrincipal(). After
-  // Bug 965637 we can query the CSP from the doc directly.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+  nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
   if (csp) {
     csp.forget(aCsp);
   }
 
   nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
   if (!baseURI) {
     NS_ERROR("Document didn't return a base URI");
     return NS_ERROR_FAILURE;
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -140,35 +140,33 @@ nsresult nsJSThunk::EvaluateScript(
   }
 
   NS_ENSURE_ARG_POINTER(aChannel);
 
   // Get principal of code for execution
   nsCOMPtr<nsISupports> owner;
   aChannel->GetOwner(getter_AddRefs(owner));
   nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner);
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   if (!principal) {
-    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
     if (loadInfo->GetForceInheritPrincipal()) {
       principal = loadInfo->FindPrincipalToInherit(aChannel);
     } else {
       // No execution without a principal!
       NS_ASSERTION(!owner, "Non-principal owner?");
       NS_WARNING("No principal to execute JS with");
       return NS_ERROR_DOM_RETVAL_UNDEFINED;
     }
   }
 
   nsresult rv;
 
   // CSP check: javascript: URIs disabled unless "inline" scripts are
   // allowed.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIContentSecurityPolicy> csp = loadInfo->GetCsp();
   if (csp) {
     bool allowsInlineScript = true;
     rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
                               EmptyString(),  // aNonce
                               true,           // aParserCreated
                               nullptr,        // aElement,
                               nullptr,        // nsICSPEventListener
                               EmptyString(),  // aContent
@@ -177,16 +175,39 @@ nsresult nsJSThunk::EvaluateScript(
                               &allowsInlineScript);
 
     // return early if inline scripts are not allowed
     if (!allowsInlineScript) {
       return NS_ERROR_DOM_RETVAL_UNDEFINED;
     }
   }
 
+  // for document navigations we need to check the CSP of the previous document.
+  csp = nullptr;
+  mozilla::dom::Document* prevDoc = aOriginalInnerWindow->GetExtantDoc();
+  if (prevDoc) {
+    csp = prevDoc->GetCsp();
+  }
+  if (csp) {
+    bool allowsInlineScript = true;
+    rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
+                              EmptyString(),  // aNonce
+                              true,           // aParserCreated
+                              nullptr,        // aElement,
+                              nullptr,        // nsICSPEventListener
+                              EmptyString(),  // aContent
+                              0,              // aLineNumber
+                              0,              // aColumnNumber
+                              &allowsInlineScript);
+    // return early if inline scripts are not allowed
+    if (!allowsInlineScript) {
+      return NS_ERROR_DOM_RETVAL_UNDEFINED;
+    }
+  }
+
   // Get the global object we should be running on.
   nsIScriptGlobalObject* global = GetGlobalObject(aChannel);
   if (!global) {
     return NS_ERROR_FAILURE;
   }
 
   // Sandboxed document check: javascript: URI's are disabled
   // in a sandboxed document unless 'allow-scripts' was specified.
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -440,20 +440,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
     mozilla::OriginAttributes attrs =
         BasePrincipal::Cast(content->NodePrincipal())->OriginAttributesRef();
     triggeringPrincipal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
   } else {
     triggeringPrincipal =
         NullPrincipal::CreateWithInheritedAttributes(content->NodePrincipal());
   }
 
-  // Currently we query the CSP from the NodePrincipal. After Bug 965637
-  // we can query the CSP from the doc directly (content->OwerDoc()).
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  content->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+  nsCOMPtr<nsIContentSecurityPolicy> csp = content->GetCsp();
 
   rv = lh->OnLinkClick(content, uri, unitarget, VoidString(), aPostStream,
                        headersDataStream,
                        /* isUserTriggered */ false,
                        /* isTrusted */ true, triggeringPrincipal, csp);
 
   return rv;
 }
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -1464,21 +1464,18 @@ nsresult ScriptLoader::StartLoad(ScriptL
 bool ScriptLoader::PreloadURIComparator::Equals(const PreloadInfo& aPi,
                                                 nsIURI* const& aURI) const {
   bool same;
   return NS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI, &same)) && same;
 }
 
 static bool CSPAllowsInlineScript(nsIScriptElement* aElement,
                                   Document* aDocument) {
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  // Note: For imports NodePrincipal and the principal of the master are
-  // the same.
-  nsresult rv = aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
+  nsresult rv = NS_OK;
 
   if (!csp) {
     // no CSP --> allow
     return true;
   }
 
   // query the nonce
   nsCOMPtr<Element> scriptContent = do_QueryInterface(aElement);
@@ -1488,17 +1485,17 @@ static bool CSPAllowsInlineScript(nsIScr
       aElement->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER;
 
   bool allowInlineScript = false;
   rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT, nonce, parserCreated,
                             scriptContent, nullptr /* nsICSPEventListener */,
                             EmptyString(), aElement->GetScriptLineNumber(),
                             aElement->GetScriptColumnNumber(),
                             &allowInlineScript);
-  return allowInlineScript;
+  return NS_SUCCEEDED(rv) && allowInlineScript;
 }
 
 ScriptLoadRequest* ScriptLoader::CreateLoadRequest(
     ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement,
     nsIPrincipal* aTriggeringPrincipal, CORSMode aCORSMode,
     const SRIMetadata& aIntegrity,
     mozilla::net::ReferrerPolicy aReferrerPolicy) {
   nsIURI* referrer = mDocument->GetDocumentURI();
--- a/dom/security/CSPEvalChecker.cpp
+++ b/dom/security/CSPEvalChecker.cpp
@@ -118,32 +118,28 @@ nsresult CSPEvalChecker::CheckForWindow(
   // disable the registration and log an error
   nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
   if (!doc) {
     // if there's no document, we don't have to do anything.
     *aAllowEval = true;
     return NS_OK;
   }
 
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  nsresult rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    *aAllowEval = false;
-    return rv;
-  }
+  nsresult rv = NS_OK;
 
   // Get the calling location.
   uint32_t lineNum = 0;
   uint32_t columnNum = 0;
   nsAutoString fileNameString;
   if (!nsJSUtils::GetCallingLocation(aCx, fileNameString, &lineNum,
                                      &columnNum)) {
     fileNameString.AssignLiteral("unknown");
   }
 
+  nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
   rv = CheckInternal(csp, nullptr /* no CSPEventListener for window */,
                      doc->NodePrincipal(), aExpression, fileNameString, lineNum,
                      columnNum, aAllowEval);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     *aAllowEval = false;
     return rv;
   }
 
--- a/dom/security/FramingChecker.cpp
+++ b/dom/security/FramingChecker.cpp
@@ -157,29 +157,22 @@ bool FramingChecker::CheckOneFrameOption
     }
   }
 
   return true;
 }
 
 // Ignore x-frame-options if CSP with frame-ancestors exists
 static bool ShouldIgnoreFrameOptions(nsIChannel* aChannel,
-                                     nsIPrincipal* aPrincipal) {
+                                     nsIContentSecurityPolicy* aCSP) {
   NS_ENSURE_TRUE(aChannel, false);
-  NS_ENSURE_TRUE(aPrincipal, false);
-
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  aPrincipal->GetCsp(getter_AddRefs(csp));
-  if (!csp) {
-    // if there is no CSP, then there is nothing to do here
-    return false;
-  }
+  NS_ENSURE_TRUE(aCSP, false);
 
   bool enforcesFrameAncestors = false;
-  csp->GetEnforcesFrameAncestors(&enforcesFrameAncestors);
+  aCSP->GetEnforcesFrameAncestors(&enforcesFrameAncestors);
   if (!enforcesFrameAncestors) {
     // if CSP does not contain frame-ancestors, then there
     // is nothing to do here.
     return false;
   }
 
   // log warning to console that xfo is ignored because of CSP
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
@@ -200,22 +193,22 @@ static bool ShouldIgnoreFrameOptions(nsI
 }
 
 // Check if X-Frame-Options permits this document to be loaded as a subdocument.
 // This will iterate through and check any number of X-Frame-Options policies
 // in the request (comma-separated in a header, multiple headers, etc).
 /* static */
 bool FramingChecker::CheckFrameOptions(nsIChannel* aChannel,
                                        nsIDocShell* aDocShell,
-                                       nsIPrincipal* aPrincipal) {
+                                       nsIContentSecurityPolicy* aCsp) {
   if (!aChannel || !aDocShell) {
     return true;
   }
 
-  if (ShouldIgnoreFrameOptions(aChannel, aPrincipal)) {
+  if (ShouldIgnoreFrameOptions(aChannel, aCsp)) {
     return true;
   }
 
   nsresult rv;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (!httpChannel) {
     // check if it is hiding in a multipart channel
     rv = nsDocShell::Cast(aDocShell)->GetHttpChannel(
--- a/dom/security/FramingChecker.h
+++ b/dom/security/FramingChecker.h
@@ -7,24 +7,24 @@
 #ifndef mozilla_dom_FramingChecker_h
 #define mozilla_dom_FramingChecker_h
 
 class nsIDocShell;
 class nsIChannel;
 class nsIHttpChannel;
 class nsIDocShellTreeItem;
 class nsIURI;
-class nsIPrincipal;
+class nsIContentSecurityPolicy;
 
 class FramingChecker {
  public:
   // Determine if X-Frame-Options allows content to be framed
   // as a subdocument
   static bool CheckFrameOptions(nsIChannel* aChannel, nsIDocShell* aDocShell,
-                                nsIPrincipal* aPrincipal);
+                                nsIContentSecurityPolicy* aCSP);
 
  protected:
   enum XFOHeader { eDENY, eSAMEORIGIN, eALLOWFROM };
 
   static bool CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
                                          const nsAString& aPolicy,
                                          nsIDocShell* aDocShell);
 
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -282,38 +282,40 @@ bool nsCSPContext::Equals(nsIContentSecu
     if (!policyStr.Equals(otherPolicyStr)) {
       return false;
     }
   }
 
   return true;
 }
 
-nsresult nsCSPContext::InitFromOther(nsCSPContext* aOtherContext,
-                                     Document* aDoc, nsIPrincipal* aPrincipal) {
+nsresult nsCSPContext::InitFromOther(nsCSPContext* aOtherContext) {
   NS_ENSURE_ARG(aOtherContext);
 
-  nsresult rv = SetRequestContext(aDoc, aPrincipal);
+  nsresult rv = NS_OK;
+  nsCOMPtr<Document> doc = do_QueryReferent(aOtherContext->mLoadingContext);
+  if (doc) {
+    rv = SetRequestContextWithDocument(doc);
+  } else {
+    rv = SetRequestContextWithPrincipal(
+        aOtherContext->mLoadingPrincipal, aOtherContext->mSelfURI,
+        aOtherContext->mReferrer, aOtherContext->mInnerWindowID);
+  }
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (auto policy : aOtherContext->mPolicies) {
     nsAutoString policyStr;
     policy->toString(policyStr);
     AppendPolicy(policyStr, policy->getReportOnlyFlag(),
                  policy->getDeliveredViaMetaTagFlag());
   }
   mIPCPolicies = aOtherContext->mIPCPolicies;
   return NS_OK;
 }
 
-void nsCSPContext::SetIPCPolicies(
-    const nsTArray<mozilla::ipc::ContentSecurityPolicy>& aPolicies) {
-  mIPCPolicies = aPolicies;
-}
-
 void nsCSPContext::EnsureIPCPoliciesRead() {
   if (mIPCPolicies.Length() > 0) {
     nsresult rv;
     for (auto& policy : mIPCPolicies) {
       rv = AppendPolicy(policy.policy(), policy.reportOnlyFlag(),
                         policy.deliveredViaMetaTagFlag());
       Unused << NS_WARN_IF(NS_FAILED(rv));
     }
@@ -392,18 +394,31 @@ nsCSPContext::GetEnforcesFrameAncestors(
 }
 
 NS_IMETHODIMP
 nsCSPContext::AppendPolicy(const nsAString& aPolicyString, bool aReportOnly,
                            bool aDeliveredViaMetaTag) {
   CSPCONTEXTLOG(("nsCSPContext::AppendPolicy: %s",
                  NS_ConvertUTF16toUTF8(aPolicyString).get()));
 
-  // Use the mSelfURI from setRequestContext, see bug 991474
-  NS_ASSERTION(mSelfURI, "mSelfURI required for AppendPolicy, but not set");
+  // Use mSelfURI from setRequestContextWith{Document,Principal} (bug 991474)
+  MOZ_ASSERT(
+      mLoadingPrincipal,
+      "did you forget to call setRequestContextWith{Document,Principal}?");
+  MOZ_ASSERT(
+      mSelfURI,
+      "did you forget to call setRequestContextWith{Document,Principal}?");
+  // After Bug 1496418 we can remove that assertion because we will allow
+  // CSP on system privileged documents.
+  MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(mLoadingPrincipal),
+             "Do not call setRequestContextWith{Document,Principal} using "
+             "SystemPrincipal");
+  NS_ENSURE_TRUE(mLoadingPrincipal, NS_ERROR_UNEXPECTED);
+  NS_ENSURE_TRUE(mSelfURI, NS_ERROR_UNEXPECTED);
+
   nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(
       aPolicyString, mSelfURI, aReportOnly, this, aDeliveredViaMetaTag);
   if (policy) {
     if (policy->hasDirective(
             nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
       nsAutoCString selfURIspec, referrer;
       if (mSelfURI) {
         mSelfURI->GetAsciiSpec(selfURIspec);
@@ -686,56 +701,87 @@ nsCSPContext::LogViolationDetails(
     }
   }
   return NS_OK;
 }
 
 #undef CASE_CHECK_AND_REPORT
 
 NS_IMETHODIMP
-nsCSPContext::SetRequestContext(Document* aDocument, nsIPrincipal* aPrincipal) {
-  MOZ_ASSERT(aDocument || aPrincipal,
-             "Can't set context without doc or principal");
-  NS_ENSURE_ARG(aDocument || aPrincipal);
+nsCSPContext::SetRequestContextWithDocument(Document* aDocument) {
+  MOZ_ASSERT(aDocument, "Can't set context without doc");
+  NS_ENSURE_ARG(aDocument);
 
-  if (aDocument) {
-    mLoadingContext = do_GetWeakReference(aDocument);
-    mSelfURI = aDocument->GetDocumentURI();
-    mLoadingPrincipal = aDocument->NodePrincipal();
-    aDocument->GetReferrer(mReferrer);
-    mInnerWindowID = aDocument->InnerWindowID();
-    // the innerWindowID is not available for CSPs delivered through the
-    // header at the time setReqeustContext is called - let's queue up
-    // console messages until it becomes available, see flushConsoleMessages
-    mQueueUpMessages = !mInnerWindowID;
-    mCallingChannelLoadGroup = aDocument->GetDocumentLoadGroup();
-    mEventTarget = aDocument->EventTargetFor(TaskCategory::Other);
-  } else {
-    CSPCONTEXTLOG(
-        ("No Document in SetRequestContext; can not query loadgroup; sending "
-         "reports may fail."));
-    mLoadingPrincipal = aPrincipal;
-    mLoadingPrincipal->GetURI(getter_AddRefs(mSelfURI));
-    // if no document is available, then it also does not make sense to queue
-    // console messages sending messages to the browser conolse instead of the
-    // web console in that case.
-    mQueueUpMessages = false;
-  }
+  mLoadingContext = do_GetWeakReference(aDocument);
+  mSelfURI = aDocument->GetDocumentURI();
+  mLoadingPrincipal = aDocument->NodePrincipal();
+  aDocument->GetReferrer(mReferrer);
+  mInnerWindowID = aDocument->InnerWindowID();
+  // the innerWindowID is not available for CSPs delivered through the
+  // header at the time setReqeustContext is called - let's queue up
+  // console messages until it becomes available, see flushConsoleMessages
+  mQueueUpMessages = !mInnerWindowID;
+  mCallingChannelLoadGroup = aDocument->GetDocumentLoadGroup();
+  // set the flag on the document for CSP telemetry
+  mEventTarget = aDocument->EventTargetFor(TaskCategory::Other);
 
-  NS_ASSERTION(
-      mSelfURI,
-      "mSelfURI not available, can not translate 'self' into actual URI");
+  MOZ_ASSERT(mLoadingPrincipal, "need a valid requestPrincipal");
+  MOZ_ASSERT(mSelfURI, "need mSelfURI to translate 'self' into actual URI");
+  // After Bug 1496418 we can remove that assertion because we will allow
+  // CSP on system privileged documents.
+  MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(mLoadingPrincipal),
+             "do not apply CSP to system privileged documents");
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsCSPContext::SetRequestContextWithPrincipal(nsIPrincipal* aRequestPrincipal,
+                                             nsIURI* aSelfURI,
+                                             const nsAString& aReferrer,
+                                             uint64_t aInnerWindowId) {
+  NS_ENSURE_ARG(aRequestPrincipal);
+
+  mLoadingPrincipal = aRequestPrincipal;
+  mSelfURI = aSelfURI;
+  mReferrer = aReferrer;
+  mInnerWindowID = aInnerWindowId;
+  // if no document is available, then it also does not make sense to queue
+  // console messages sending messages to the browser console instead of the web
+  // console in that case.
+  mQueueUpMessages = false;
+  mCallingChannelLoadGroup = nullptr;
+  mEventTarget = nullptr;
+
+  MOZ_ASSERT(mLoadingPrincipal, "need a valid requestPrincipal");
+  MOZ_ASSERT(mSelfURI, "need mSelfURI to translate 'self' into actual URI");
+  // After Bug 1496418 we can remove that assertion because we will allow
+  // CSP on system privileged documents.
+  MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(mLoadingPrincipal),
+             "do not apply CSP to system privileged documents");
+  return NS_OK;
+}
+
+nsIPrincipal* nsCSPContext::GetRequestPrincipal() { return mLoadingPrincipal; }
+
+nsIURI* nsCSPContext::GetSelfURI() { return mSelfURI; }
+
+NS_IMETHODIMP
+nsCSPContext::GetReferrer(nsAString& outReferrer) {
+  outReferrer.Truncate();
+  outReferrer.Append(mReferrer);
+  return NS_OK;
+}
+
+uint64_t nsCSPContext::GetInnerWindowID() { return mInnerWindowID; }
+
+NS_IMETHODIMP
 nsCSPContext::EnsureEventTarget(nsIEventTarget* aEventTarget) {
   NS_ENSURE_ARG(aEventTarget);
   // Don't bother if we did have a valid event target (if the csp object is
-  // tied to a document in SetRequestContext)
+  // tied to a document in SetRequestContextWithDocument)
   if (mEventTarget) {
     return NS_OK;
   }
 
   mEventTarget = aEventTarget;
   return NS_OK;
 }
 
@@ -1081,19 +1127,19 @@ nsresult nsCSPContext::SendReports(
     RefPtr<CSPReportRedirectSink> reportSink = new CSPReportRedirectSink();
     if (doc && doc->GetDocShell()) {
       nsCOMPtr<nsINetworkInterceptController> interceptController =
           do_QueryInterface(doc->GetDocShell());
       reportSink->SetInterceptController(interceptController);
     }
     reportChannel->SetNotificationCallbacks(reportSink);
 
-    // apply the loadgroup from the channel taken by setRequestContext.  If
-    // there's no loadgroup, AsyncOpen will fail on process-split necko (since
-    // the channel cannot query the iBrowserChild).
+    // apply the loadgroup taken by setRequestContextWithDocument. If there's
+    // no loadgroup, AsyncOpen will fail on process-split necko (since the
+    // channel cannot query the iBrowserChild).
     rv = reportChannel->SetLoadGroup(mCallingChannelLoadGroup);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // wire in the string input stream to send the report
     nsCOMPtr<nsIStringInputStream> sis(
         do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
     NS_ASSERTION(sis,
                  "nsIStringInputStream is needed but not available to send CSP "
@@ -1121,19 +1167,19 @@ nsresult nsCSPContext::SendReports(
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
 
     RefPtr<CSPViolationReportListener> listener =
         new CSPViolationReportListener();
     rv = reportChannel->AsyncOpen(listener);
 
     // AsyncOpen should not fail, but could if there's no load group (like if
-    // SetRequestContext is not given a channel).  This should fail quietly and
-    // not return an error since it's really ok if reports don't go out, but
-    // it's good to log the error locally.
+    // SetRequestContextWith{Document,Principal} is not given a channel). This
+    // should fail quietly and not return an error since it's really ok if
+    // reports don't go out, but it's good to log the error locally.
 
     if (NS_FAILED(rv)) {
       const char16_t* params[] = {reportURIs[r].get()};
       CSPCONTEXTLOG(("AsyncOpen failed for report URI %s",
                      NS_ConvertUTF16toUTF8(params[0]).get()));
       logToConsole("triedToSendReport", params, ArrayLength(params),
                    aViolationEventInit.mSourceFile, aViolationEventInit.mSample,
                    aViolationEventInit.mLineNumber,
@@ -1684,17 +1730,23 @@ NS_IMETHODIMP
 nsCSPContext::Read(nsIObjectInputStream* aStream) {
   nsresult rv;
   nsCOMPtr<nsISupports> supports;
 
   rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mSelfURI = do_QueryInterface(supports);
-  NS_ASSERTION(mSelfURI, "need a self URI to de-serialize");
+  MOZ_ASSERT(mSelfURI, "need a self URI to de-serialize");
+
+  rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mLoadingPrincipal = do_QueryInterface(supports);
+  MOZ_ASSERT(mLoadingPrincipal, "need a loadingPrincipal to de-serialize");
 
   uint32_t numPolicies;
   rv = aStream->Read32(&numPolicies);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString policyString;
 
   while (numPolicies > 0) {
@@ -1718,16 +1770,20 @@ nsCSPContext::Read(nsIObjectInputStream*
 }
 
 NS_IMETHODIMP
 nsCSPContext::Write(nsIObjectOutputStream* aStream) {
   nsresult rv = NS_WriteOptionalCompoundObject(aStream, mSelfURI,
                                                NS_GET_IID(nsIURI), true);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = NS_WriteOptionalCompoundObject(aStream, mLoadingPrincipal,
+                                      NS_GET_IID(nsIPrincipal), true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Serialize all the policies.
   aStream->Write32(mPolicies.Length() + mIPCPolicies.Length());
 
   nsAutoString polStr;
   for (uint32_t p = 0; p < mPolicies.Length(); p++) {
     polStr.Truncate();
     mPolicies[p]->toString(polStr);
     aStream->WriteWStringZ(polStr.get());
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -52,27 +52,26 @@ class nsCSPContext : public nsIContentSe
   virtual ~nsCSPContext();
 
  public:
   nsCSPContext();
 
   static bool Equals(nsIContentSecurityPolicy* aCSP,
                      nsIContentSecurityPolicy* aOtherCSP);
 
-  nsresult InitFromOther(nsCSPContext* otherContext,
-                         mozilla::dom::Document* aDoc,
-                         nsIPrincipal* aPrincipal);
-
-  void SetIPCPolicies(
-      const nsTArray<mozilla::ipc::ContentSecurityPolicy>& policies);
+  // Init a CSP from a different CSP
+  nsresult InitFromOther(nsCSPContext* otherContext);
 
   /**
-   * SetRequestContext() needs to be called before the innerWindowID
-   * is initialized on the document. Use this function to call back to
-   * flush queued up console messages and initalize the innerWindowID.
+   * SetRequestContextWithDocument() needs to be called before the
+   * innerWindowID is initialized on the document. Use this function
+   * to call back to flush queued up console messages and initialize
+   * the innerWindowID. Node, If SetRequestContextWithPrincipal() was
+   * called then we do not have a innerWindowID anyway and hence
+   * we can not flush messages to the correct console.
    */
   void flushConsoleMessages();
 
   void logToConsole(const char* aName, const char16_t** aParams,
                     uint32_t aParamsLength, const nsAString& aSourceName,
                     const nsAString& aSourceLine, uint32_t aLineNumber,
                     uint32_t aColumnNumber, uint32_t aSeverityFlag);
 
@@ -169,20 +168,17 @@ class nsCSPContext : public nsIContentSe
   // when there's an attempt to use the CSP. Given a better way to serialize/
   // deserialize individual nsCSPPolicy objects, this performance
   // optimization could go away.
   nsTArray<mozilla::ipc::ContentSecurityPolicy> mIPCPolicies;
   nsTArray<nsCSPPolicy*> mPolicies;
   nsCOMPtr<nsIURI> mSelfURI;
   nsCOMPtr<nsILoadGroup> mCallingChannelLoadGroup;
   nsWeakPtr mLoadingContext;
-  // The CSP hangs off the principal, so let's store a raw pointer of the
-  // principal to avoid memory leaks. Within the destructor of the principal we
-  // explicitly set mLoadingPrincipal to null.
-  nsIPrincipal* mLoadingPrincipal;
+  nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
 
   // helper members used to queue up web console messages till
   // the windowID becomes available. see flushConsoleMessages()
   nsTArray<ConsoleMsgQueueElem> mConsoleMsgQueue;
   bool mQueueUpMessages;
   nsCOMPtr<nsIEventTarget> mEventTarget;
 };
 
--- a/dom/security/nsCSPService.cpp
+++ b/dom/security/nsCSPService.cpp
@@ -118,17 +118,16 @@ NS_IMETHODIMP
 CSPService::ShouldLoad(nsIURI* aContentLocation, nsILoadInfo* aLoadInfo,
                        const nsACString& aMimeTypeGuess, int16_t* aDecision) {
   if (!aContentLocation) {
     return NS_ERROR_FAILURE;
   }
 
   uint32_t contentType = aLoadInfo->InternalContentPolicyType();
   nsCOMPtr<nsISupports> requestContext = aLoadInfo->GetLoadingContext();
-  nsCOMPtr<nsIPrincipal> requestPrincipal = aLoadInfo->TriggeringPrincipal();
   nsCOMPtr<nsIURI> requestOrigin;
   nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadInfo->LoadingPrincipal();
   if (loadingPrincipal) {
     loadingPrincipal->GetURI(getter_AddRefs(requestOrigin));
   }
 
   nsCOMPtr<nsICSPEventListener> cspEventListener;
   nsresult rv =
@@ -149,46 +148,25 @@ CSPService::ShouldLoad(nsIURI* aContentL
   // Please note, the correct way to opt-out of CSP using a custom
   // protocolHandler is to set one of the nsIProtocolHandler flags
   // that are whitelistet in subjectToCSP()
   if (!StaticPrefs::security_csp_enable() ||
       !subjectToCSP(aContentLocation, contentType)) {
     return NS_OK;
   }
 
-  // Find a principal to retrieve the CSP from. If we don't have a context node
-  // (because, for instance, the load originates in a service worker), or the
-  // requesting principal's CSP overrides our document CSP, use the request
-  // principal. Otherwise, use the document principal.
-  nsCOMPtr<nsINode> node(do_QueryInterface(requestContext));
-  nsCOMPtr<nsIPrincipal> principal;
-  if (!node ||
-      (requestPrincipal && BasePrincipal::Cast(requestPrincipal)
-                               ->OverridesCSP(node->NodePrincipal()))) {
-    principal = requestPrincipal;
-  } else {
-    principal = node->NodePrincipal();
-  }
-  if (!principal) {
-    // if we can't query a principal, then there is nothing to do.
-    return NS_OK;
-  }
-
   nsAutoString cspNonce;
   rv = aLoadInfo->GetCspNonce(cspNonce);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // 1) Apply speculate CSP for preloads
   bool isPreload = nsContentUtils::IsPreloadType(contentType);
 
   if (isPreload) {
-    nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
-    rv = principal->GetPreloadCsp(getter_AddRefs(preloadCsp));
-    NS_ENSURE_SUCCESS(rv, rv);
-
+    nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = aLoadInfo->GetPreloadCsp();
     if (preloadCsp) {
       // obtain the enforcement decision
       rv = preloadCsp->ShouldLoad(
           contentType, cspEventListener, aContentLocation, requestOrigin,
           requestContext, aMimeTypeGuess,
           nullptr,  // no redirect, aOriginal URL is null.
           aLoadInfo->GetSendCSPViolationEvents(), cspNonce, aDecision);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -199,20 +177,21 @@ CSPService::ShouldLoad(nsIURI* aContentL
         NS_SetRequestBlockingReason(
             aLoadInfo, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_PRELOAD);
 
         return NS_OK;
       }
     }
   }
 
-  // 2) Apply actual CSP to all loads
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
+  // 2) Apply actual CSP to all loads. Please note that in case
+  // the csp should be overruled (e.g. by an ExpandedPrincipal)
+  // then loadinfo->GetCSP() returns that CSP instead of the
+  // document's CSP.
+  nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadInfo->GetCsp();
 
   if (csp) {
     // obtain the enforcement decision
     rv = csp->ShouldLoad(contentType, cspEventListener, aContentLocation,
                          requestOrigin, requestContext, aMimeTypeGuess,
                          nullptr,  // no redirect, aOriginal URL is null.
                          aLoadInfo->GetSendCSPViolationEvents(), cspNonce,
                          aDecision);
@@ -260,16 +239,27 @@ CSPService::ShouldProcess(nsIURI* aConte
 
 /* nsIChannelEventSink implementation */
 NS_IMETHODIMP
 CSPService::AsyncOnChannelRedirect(nsIChannel* oldChannel,
                                    nsIChannel* newChannel, uint32_t flags,
                                    nsIAsyncVerifyRedirectCallback* callback) {
   net::nsAsyncRedirectAutoCallback autoCallback(callback);
 
+  if (XRE_IsE10sParentProcess()) {
+    nsCOMPtr<nsIParentChannel> parentChannel;
+    NS_QueryNotificationCallbacks(oldChannel, parentChannel);
+    // Since this is an IPC'd channel we do not have access to the request
+    // context. In turn, we do not have an event target for policy violations.
+    // Enforce the CSP check in the content process where we have that info.
+    if (parentChannel) {
+      return NS_OK;
+    }
+  }
+
   nsCOMPtr<nsIURI> newUri;
   nsresult rv = newChannel->GetURI(getter_AddRefs(newUri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsILoadInfo> loadInfo = oldChannel->LoadInfo();
   nsCOMPtr<nsICSPEventListener> cspEventListener;
   rv = loadInfo->GetCspEventListener(getter_AddRefs(cspEventListener));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -312,19 +302,17 @@ CSPService::AsyncOnChannelRedirect(nsICh
    */
   policyType =
       nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(policyType);
 
   int16_t aDecision = nsIContentPolicy::ACCEPT;
   nsCOMPtr<nsISupports> requestContext = loadInfo->GetLoadingContext();
   // 1) Apply speculative CSP for preloads
   if (isPreload) {
-    nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
-    loadInfo->LoadingPrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp));
-
+    nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = loadInfo->GetPreloadCsp();
     if (preloadCsp) {
       // Pass  originalURI to indicate the redirect
       preloadCsp->ShouldLoad(
           policyType,  // load type per nsIContentPolicy (uint32_t)
           cspEventListener,
           newUri,          // nsIURI
           nullptr,         // nsIURI
           requestContext,  // nsISupports
@@ -340,19 +328,17 @@ CSPService::AsyncOnChannelRedirect(nsICh
         autoCallback.DontCallback();
         oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
         return NS_BINDING_FAILED;
       }
     }
   }
 
   // 2) Apply actual CSP to all loads
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
-
+  nsCOMPtr<nsIContentSecurityPolicy> csp = loadInfo->GetCsp();
   if (csp) {
     // Pass  originalURI to indicate the redirect
     csp->ShouldLoad(policyType,  // load type per nsIContentPolicy (uint32_t)
                     cspEventListener,
                     newUri,          // nsIURI
                     nullptr,         // nsIURI
                     requestContext,  // nsISupports
                     EmptyCString(),  // ACString - MIME guess
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAttrValue.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsCSPUtils.h"
 #include "nsDebug.h"
 #include "nsIConsoleService.h"
+#include "nsIChannel.h"
 #include "nsICryptoHash.h"
 #include "nsIScriptError.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
 #include "nsIURL.h"
 #include "nsReadableUtils.h"
 #include "nsSandboxFlags.h"
 
@@ -79,16 +80,48 @@ void CSP_PercentDecodeStr(const nsAStrin
         (local::convertHexDig(*hexDig1) << 4) + local::convertHexDig(*hexDig2);
     outDecStr.Append(decChar);
 
     // increment 'cur' to after the second hexDig
     cur = ++hexDig2;
   }
 }
 
+// The Content Security Policy should be inherited for
+// local schemes like: "about", "blob", "data", or "filesystem".
+// see: https://w3c.github.io/webappsec-csp/#initialize-document-csp
+bool CSP_ShouldResponseInheritCSP(nsIChannel* aChannel) {
+  if (!aChannel) {
+    return false;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  bool isAbout = (NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout);
+  if (isAbout) {
+    nsAutoCString aboutSpec;
+    rv = uri->GetSpec(aboutSpec);
+    NS_ENSURE_SUCCESS(rv, false);
+    // also allow about:blank#foo
+    if (StringBeginsWith(aboutSpec, NS_LITERAL_CSTRING("about:blank")) ||
+        StringBeginsWith(aboutSpec, NS_LITERAL_CSTRING("about:srcdoc"))) {
+      return true;
+    }
+  }
+
+  bool isBlob = (NS_SUCCEEDED(uri->SchemeIs("blob", &isBlob)) && isBlob);
+  bool isData = (NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData);
+  bool isFS = (NS_SUCCEEDED(uri->SchemeIs("filesystem", &isFS)) && isFS);
+  bool isJS = (NS_SUCCEEDED(uri->SchemeIs("javascript", &isJS)) && isJS);
+
+  return isBlob || isData || isFS || isJS;
+}
+
 void CSP_GetLocalizedStr(const char* aName, const char16_t** aParams,
                          uint32_t aLength, nsAString& outResult) {
   nsCOMPtr<nsIStringBundle> keyStringBundle;
   nsCOMPtr<nsIStringBundleService> stringBundleService =
       mozilla::services::GetStringBundleService();
 
   NS_ASSERTION(stringBundleService, "String bundle service must be present!");
   stringBundleService->CreateBundle(
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -12,16 +12,18 @@
 #include "nsIContentSecurityPolicy.h"
 #include "nsIURI.h"
 #include "nsLiteralString.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Logging.h"
 
+class nsIChannel;
+
 namespace mozilla {
 namespace dom {
 struct CSP;
 }  // namespace dom
 }  // namespace mozilla
 
 /* =============== Logging =================== */
 
@@ -198,16 +200,17 @@ bool CSP_IsValidDirective(const nsAStrin
 bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir);
 bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
 bool CSP_IsQuotelessKeyword(const nsAString& aKey);
 CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType);
 
 class nsCSPSrcVisitor;
 
 void CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr);
+bool CSP_ShouldResponseInheritCSP(nsIChannel* aChannel);
 
 /* =============== nsCSPSrc ================== */
 
 class nsCSPBaseSrc {
  public:
   nsCSPBaseSrc();
   virtual ~nsCSPBaseSrc();
 
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -775,18 +775,17 @@ static void DebugDoContentSecurityCheck(
     MOZ_LOG(sCSMLog, LogLevel::Debug,
             ("  upgradeInsecureRequests: %s\n",
              aLoadInfo->GetUpgradeInsecureRequests() ? "true" : "false"));
     MOZ_LOG(sCSMLog, LogLevel::Debug,
             ("  initalSecurityChecksDone: %s\n",
              aLoadInfo->GetInitialSecurityCheckDone() ? "true" : "false"));
 
     // Log CSPrequestPrincipal
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    requestPrincipal->GetCsp(getter_AddRefs(csp));
+    nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadInfo->GetCsp();
     if (csp) {
       nsAutoString parsedPolicyStr;
       uint32_t count = 0;
       csp->GetPolicyCount(&count);
       MOZ_LOG(sCSMLog, LogLevel::Debug, ("  CSP (%d): ", count));
       for (uint32_t i = 0; i < count; ++i) {
         csp->GetPolicyString(i, parsedPolicyStr);
         MOZ_LOG(sCSMLog, LogLevel::Debug,
--- a/dom/smil/SMILCSSValueType.cpp
+++ b/dom/smil/SMILCSSValueType.cpp
@@ -448,19 +448,18 @@ void SMILCSSValueType::ValueFromString(n
   nsPresContext* presContext =
       nsContentUtils::GetContextForContent(aTargetElement);
   if (!presContext) {
     NS_WARNING("Not parsing animation value; unable to get PresContext");
     return;
   }
 
   Document* doc = aTargetElement->GetComposedDoc();
-  if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, doc->NodePrincipal(),
-                                                nullptr, doc->GetDocumentURI(),
-                                                0, 0, aString, nullptr)) {
+  if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, doc, nullptr, 0, 0,
+                                                aString, nullptr)) {
     return;
   }
 
   RefPtr<ComputedStyle> computedStyle =
       nsComputedDOMStyle::GetComputedStyle(aTargetElement, nullptr);
   if (!computedStyle) {
     return;
   }
@@ -487,19 +486,18 @@ SMILValue SMILCSSValueType::ValueFromAni
 
   Document* doc = aTargetElement->GetComposedDoc();
   // We'd like to avoid serializing |aValue| if possible, and since the
   // string passed to CSPAllowsInlineStyle is only used for reporting violations
   // and an intermediate CSS value is not likely to be particularly useful
   // in that case, we just use a generic placeholder string instead.
   static const nsLiteralString kPlaceholderText =
       NS_LITERAL_STRING("[SVG animation of CSS]");
-  if (doc && !nsStyleUtil::CSPAllowsInlineStyle(
-                 nullptr, doc->NodePrincipal(), nullptr, doc->GetDocumentURI(),
-                 0, 0, kPlaceholderText, nullptr)) {
+  if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, doc, nullptr, 0, 0,
+                                                kPlaceholderText, nullptr)) {
     return result;
   }
 
   sSingleton.Init(result);
   result.mU.mPtr = new ValueWrapper(aPropID, aValue);
 
   return result;
 }
--- 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 ContentSecurityPolicy;
 interface Principal;
 interface WindowProxy;
 interface nsISupports;
 interface URI;
 interface nsIDocShell;
 interface nsILoadGroup;
 
 enum VisibilityState { "hidden", "visible" };
@@ -539,16 +540,24 @@ partial interface Document {
 // Extension to give chrome and XBL JS the ability to determine whether
 // the document is sandboxed without permission to run scripts
 // and whether inline scripts are blocked by the document's CSP.
 partial interface Document {
   [Func="IsChromeOrXBL"] readonly attribute boolean hasScriptsBlockedBySandbox;
   [Func="IsChromeOrXBL"] readonly attribute boolean inlineScriptAllowedByCSP;
 };
 
+// Allows frontend code to query a CSP which needs to be passed for a
+// new load into docshell. Further, allows to query the CSP in JSON
+// format for testing purposes.
+partial interface Document {
+  [ChromeOnly] readonly attribute ContentSecurityPolicy? csp;
+  [ChromeOnly] readonly attribute DOMString cspJSON;
+};
+
 // For more information on Flash classification, see
 // toolkit/components/url-classifier/flash-block-lists.rst
 enum FlashClassification {
   "unknown",        // Site is not on the whitelist or blacklist
   "allowed",        // Site is on the Flash whitelist
   "denied"          // Site is on the Flash blacklist
 };
 partial interface Document {
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -106,32 +106,16 @@ already_AddRefed<nsIPrincipal> Principal
         }
 
         rv = principal->SetDomain(domain);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return nullptr;
         }
       }
 
-      if (info.securityPolicies().Length() > 0) {
-        nsCOMPtr<nsIContentSecurityPolicy> csp =
-            do_CreateInstance(NS_CSPCONTEXT_CONTRACTID, &rv);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return nullptr;
-        }
-
-        rv = csp->SetRequestContext(nullptr, principal);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return nullptr;
-        }
-        static_cast<nsCSPContext*>(csp.get())->SetIPCPolicies(
-            info.securityPolicies());
-        principal->SetCsp(csp);
-      }
-
       if (!info.baseDomain().IsVoid()) {
         nsAutoCString baseDomain;
         rv = principal->GetBaseDomain(baseDomain);
         if (NS_WARN_IF(NS_FAILED(rv)) ||
             !info.baseDomain().Equals(baseDomain)) {
           MOZ_CRASH("Base domain must be available when deserialized");
         }
       }
@@ -168,41 +152,111 @@ already_AddRefed<nsIPrincipal> Principal
 
     default:
       MOZ_CRASH("Unknown PrincipalInfo type!");
   }
 
   MOZ_CRASH("Should never get here!");
 }
 
-nsresult PopulateContentSecurityPolicies(
-    nsIContentSecurityPolicy* aCSP,
-    nsTArray<ContentSecurityPolicy>& aPolicies) {
-  MOZ_ASSERT(aCSP);
-  MOZ_ASSERT(aPolicies.IsEmpty());
+already_AddRefed<nsIContentSecurityPolicy> CSPInfoToCSP(
+    const CSPInfo& aCSPInfo, Document* aRequestingDoc,
+    nsresult* aOptionalResult) {
   MOZ_ASSERT(NS_IsMainThread());
 
+  nsresult stackResult;
+  nsresult& rv = aOptionalResult ? *aOptionalResult : stackResult;
+
+  nsCOMPtr<nsIContentSecurityPolicy> csp = new nsCSPContext();
+
+  if (aRequestingDoc) {
+    rv = csp->SetRequestContextWithDocument(aRequestingDoc);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+  } else {
+    nsCOMPtr<nsIPrincipal> requestingPrincipal =
+        PrincipalInfoToPrincipal(aCSPInfo.requestPrincipalInfo(), &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    nsCOMPtr<nsIURI> selfURI;
+    if (!aCSPInfo.selfURISpec().IsEmpty()) {
+      rv = NS_NewURI(getter_AddRefs(selfURI), aCSPInfo.selfURISpec());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return nullptr;
+      }
+    }
+    rv = csp->SetRequestContextWithPrincipal(requestingPrincipal, selfURI,
+                                             aCSPInfo.referrer(),
+                                             aCSPInfo.innerWindowID());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+  }
+
+  for (uint32_t i = 0; i < aCSPInfo.policyInfos().Length(); i++) {
+    const PolicyInfo& policyInfo = aCSPInfo.policyInfos()[i];
+    rv = csp->AppendPolicy(NS_ConvertUTF8toUTF16(policyInfo.policy()),
+                           policyInfo.reportOnly(),
+                           policyInfo.deliveredViaMetaTag());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+  }
+  return csp.forget();
+}
+
+nsresult CSPToCSPInfo(nsIContentSecurityPolicy* aCSP, CSPInfo* aCSPInfo) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aCSPInfo);
+
+  if (!aCSP || !aCSPInfo) {
+    return NS_OK;
+  }
+
   uint32_t count = 0;
   nsresult rv = aCSP->GetPolicyCount(&count);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  nsCOMPtr<nsIPrincipal> requestPrincipal = aCSP->GetRequestPrincipal();
+
+  PrincipalInfo requestingPrincipalInfo;
+  rv = PrincipalToPrincipalInfo(requestPrincipal, &requestingPrincipalInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIURI> selfURI = aCSP->GetSelfURI();
+  nsAutoCString selfURISpec;
+  if (selfURI) {
+    selfURI->GetSpec(selfURISpec);
+  }
+
+  nsAutoString referrer;
+  aCSP->GetReferrer(referrer);
+
+  uint64_t windowID = aCSP->GetInnerWindowID();
+
+  nsTArray<PolicyInfo> policyInfos;
   for (uint32_t i = 0; i < count; ++i) {
     const nsCSPPolicy* policy = aCSP->GetPolicy(i);
     MOZ_ASSERT(policy);
 
     nsAutoString policyString;
     policy->toString(policyString);
-
-    aPolicies.AppendElement(
-        ContentSecurityPolicy(policyString, policy->getReportOnlyFlag(),
-                              policy->getDeliveredViaMetaTagFlag()));
+    policyInfos.AppendElement(PolicyInfo(NS_ConvertUTF16toUTF8(policyString),
+                                         policy->getReportOnlyFlag(),
+                                         policy->getDeliveredViaMetaTagFlag()));
   }
-
+  *aCSPInfo = CSPInfo(std::move(policyInfos), requestingPrincipalInfo,
+                      selfURISpec, referrer, windowID);
   return NS_OK;
 }
 
 nsresult PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
                                   PrincipalInfo* aPrincipalInfo,
                                   bool aSkipBaseDomain) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
@@ -292,41 +346,30 @@ nsresult PrincipalToPrincipalInfo(nsIPri
   if (domainUri) {
     domain.emplace();
     rv = domainUri->GetSpec(domain.ref());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = aPrincipal->GetCsp(getter_AddRefs(csp));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsTArray<ContentSecurityPolicy> policies;
-  if (csp) {
-    PopulateContentSecurityPolicies(csp, policies);
-  }
-
   // This attribute is not crucial.
   nsCString baseDomain;
   if (aSkipBaseDomain) {
     baseDomain.SetIsVoid(true);
   } else {
     if (NS_FAILED(aPrincipal->GetBaseDomain(baseDomain))) {
       NS_WARNING("Failed to get base domain!");
       baseDomain.SetIsVoid(true);
     }
   }
 
   *aPrincipalInfo =
       ContentPrincipalInfo(aPrincipal->OriginAttributesRef(), originNoSuffix,
-                           spec, domain, std::move(policies), baseDomain);
+                           spec, domain, baseDomain);
   return NS_OK;
 }
 
 bool IsPincipalInfoPrivate(const PrincipalInfo& aPrincipalInfo) {
   if (aPrincipalInfo.type() != ipc::PrincipalInfo::TContentPrincipalInfo) {
     return false;
   }
 
@@ -496,16 +539,26 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
   nsCOMPtr<nsICookieSettings> cookieSettings;
   rv = aLoadInfo->GetCookieSettings(getter_AddRefs(cookieSettings));
   NS_ENSURE_SUCCESS(rv, rv);
 
   CookieSettingsArgs cookieSettingsArgs;
   static_cast<CookieSettings*>(cookieSettings.get())
       ->Serialize(cookieSettingsArgs);
 
+  Maybe<CSPInfo> maybeCspToInheritInfo;
+  nsCOMPtr<nsIContentSecurityPolicy> cspToInherit =
+      static_cast<net::LoadInfo*>(aLoadInfo)->GetCSPToInherit();
+  if (cspToInherit) {
+    CSPInfo cspToInheritInfo;
+    Unused << NS_WARN_IF(
+        NS_FAILED(CSPToCSPInfo(cspToInherit, &cspToInheritInfo)));
+    maybeCspToInheritInfo.emplace(cspToInheritInfo);
+  }
+
   *aOptionalLoadInfoArgs = Some(LoadInfoArgs(
       loadingPrincipalInfo, triggeringPrincipalInfo, principalToInheritInfo,
       sandboxedLoadingPrincipalInfo, topLevelPrincipalInfo,
       topLevelStorageAreaPrincipalInfo, optionalResultPrincipalURI,
       aLoadInfo->GetSecurityFlags(), aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetBrowserUpgradeInsecureRequests(),
@@ -525,17 +578,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(), cookieSettingsArgs,
-      aLoadInfo->GetRequestBlockingReason()));
+      aLoadInfo->GetRequestBlockingReason(), maybeCspToInheritInfo));
 
   return NS_OK;
 }
 
 nsresult LoadInfoArgsToLoadInfo(
     const Maybe<LoadInfoArgs>& aOptionalLoadInfoArgs,
     nsILoadInfo** outLoadInfo) {
   if (aOptionalLoadInfoArgs.isNothing()) {
@@ -649,22 +702,30 @@ nsresult LoadInfoArgsToLoadInfo(
     controller.emplace(
         ServiceWorkerDescriptor(loadInfoArgs.controller().ref()));
   }
 
   nsCOMPtr<nsICookieSettings> cookieSettings;
   CookieSettings::Deserialize(loadInfoArgs.cookieSettings(),
                               getter_AddRefs(cookieSettings));
 
+  nsCOMPtr<nsIContentSecurityPolicy> cspToInherit;
+  Maybe<mozilla::ipc::CSPInfo> cspToInheritInfo =
+      loadInfoArgs.cspToInheritInfo();
+  if (cspToInheritInfo.isSome()) {
+    cspToInherit = CSPInfoToCSP(cspToInheritInfo.ref(), nullptr);
+  }
+
   RefPtr<mozilla::LoadInfo> loadInfo = new mozilla::LoadInfo(
       loadingPrincipal, triggeringPrincipal, principalToInherit,
       sandboxedLoadingPrincipal, topLevelPrincipal,
       topLevelStorageAreaPrincipal, resultPrincipalURI, cookieSettings,
-      clientInfo, reservedClientInfo, initialClientInfo, controller,
-      loadInfoArgs.securityFlags(), loadInfoArgs.contentPolicyType(),
+      cspToInherit, clientInfo, reservedClientInfo, initialClientInfo,
+      controller, loadInfoArgs.securityFlags(),
+      loadInfoArgs.contentPolicyType(),
       static_cast<LoadTainting>(loadInfoArgs.tainting()),
       loadInfoArgs.upgradeInsecureRequests(),
       loadInfoArgs.browserUpgradeInsecureRequests(),
       loadInfoArgs.browserWouldUpgradeInsecureRequests(),
       loadInfoArgs.forceAllowDataURI(),
       loadInfoArgs.allowInsecureRedirectToDataURI(),
       loadInfoArgs.skipContentPolicyCheckForWebRequest(),
       loadInfoArgs.forceInheritPrincipalDropped(), loadInfoArgs.innerWindowID(),
--- a/ipc/glue/BackgroundUtils.h
+++ b/ipc/glue/BackgroundUtils.h
@@ -8,16 +8,17 @@
 #define mozilla_ipc_backgroundutils_h__
 
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/BasePrincipal.h"
 #include "nsCOMPtr.h"
 #include "nscore.h"
 
+class nsIContentSecurityPolicy;
 class nsILoadInfo;
 class nsIPrincipal;
 class nsIRedirectHistoryEntry;
 
 namespace IPC {
 
 namespace detail {
 template <class ParamType>
@@ -51,44 +52,58 @@ class ChildLoadInfoForwarderArgs;
 class LoadInfoArgs;
 class ParentLoadInfoForwarderArgs;
 class RedirectHistoryEntryInfo;
 }  // namespace net
 
 namespace ipc {
 
 class ContentSecurityPolicy;
+class CSPInfo;
 class PrincipalInfo;
 
 /**
  * Convert a PrincipalInfo to an nsIPrincipal.
  *
  * MUST be called on the main thread only.
  */
 already_AddRefed<nsIPrincipal> PrincipalInfoToPrincipal(
     const PrincipalInfo& aPrincipalInfo, nsresult* aOptionalResult = nullptr);
 
 /**
- * Populate an array of ContentSecurityPolicy objects from a CSP object.
- *
- * MUST be called on the main thread only.
- */
-nsresult PopulateContentSecurityPolicies(
-    nsIContentSecurityPolicy* aCSP, nsTArray<ContentSecurityPolicy>& aPolicies);
-
-/**
  * Convert an nsIPrincipal to a PrincipalInfo.
  *
  * MUST be called on the main thread only.
  */
 nsresult PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
                                   PrincipalInfo* aPrincipalInfo,
                                   bool aSkipBaseDomain = false);
 
 /**
+ * Convert a CSPInfo to an nsIContentSecurityPolicy.
+ *
+ * MUST be called on the main thread only.
+ *
+ * If possible, provide a requesting doc, so policy violation events can
+ * be dispatched correctly. If aRequestingDoc is null, then the CSPInfo holds
+ * the necessary fallback information, like a serialized requestPrincipal,
+ * to generate a valid nsIContentSecurityPolicy.
+ */
+already_AddRefed<nsIContentSecurityPolicy> CSPInfoToCSP(
+    const CSPInfo& aCSPInfo, mozilla::dom::Document* aRequestingDoc,
+    nsresult* aOptionalResult = nullptr);
+
+/**
+ * Convert an nsIContentSecurityPolicy to a CSPInfo.
+ *
+ * MUST be called on the main thread only.
+ */
+nsresult CSPToCSPInfo(nsIContentSecurityPolicy* aCSP, CSPInfo* aCSPInfo);
+
+/**
  * Return true if this PrincipalInfo is a content principal and it has
  * a privateBrowsing id in its OriginAttributes
  */
 bool IsPincipalInfoPrivate(const PrincipalInfo& aPrincipalInfo);
 
 /**
  * Convert an RedirectHistoryEntryInfo to a nsIRedirectHistoryEntry.
  */
--- a/ipc/glue/PBackgroundSharedTypes.ipdlh
+++ b/ipc/glue/PBackgroundSharedTypes.ipdlh
@@ -27,18 +27,16 @@ struct ContentPrincipalInfo
   // ContentPrincipalInfo is used out of the main-thread. Having this value
   // here allows us to retrive the origin without creating a full nsIPrincipal.
   nsCString originNoSuffix;
 
   nsCString spec;
 
   nsCString? domain;
 
-  ContentSecurityPolicy[] securityPolicies;
-
   // Like originNoSuffix, baseDomain is used out of the main-thread.
   nsCString baseDomain;
 };
 
 struct SystemPrincipalInfo
 { };
 
 struct NullPrincipalInfo
@@ -56,10 +54,26 @@ struct ExpandedPrincipalInfo
 union PrincipalInfo
 {
   ContentPrincipalInfo;
   SystemPrincipalInfo;
   NullPrincipalInfo;
   ExpandedPrincipalInfo;
 };
 
+struct PolicyInfo
+{
+  nsCString policy;
+  bool reportOnly;
+  bool deliveredViaMetaTag;
+};
+
+struct CSPInfo
+{
+  PolicyInfo[] policyInfos;
+  PrincipalInfo requestPrincipalInfo;
+  nsCString selfURISpec;
+  nsString referrer;
+  uint64_t innerWindowID;
+};
+
 } // namespace ipc
 } // namespace mozilla
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.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 "nsStyleUtil.h"
 #include "nsStyleConsts.h"
 
+#include "mozilla/ExpandedPrincipal.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "nsIContent.h"
 #include "nsCSSProps.h"
 #include "nsContentUtils.h"
 #include "nsROCSSPrimitiveValue.h"
 #include "nsStyleStruct.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
@@ -368,41 +369,35 @@ bool nsStyleUtil::ObjectPropsMightCauseO
       ObjectPositionCoordMightCauseOverflow(objectPosistion.vertical)) {
     return true;
   }
 
   return false;
 }
 
 /* static */
-bool nsStyleUtil::CSPAllowsInlineStyle(Element* aElement,
-                                       nsIPrincipal* aPrincipal,
-                                       nsIPrincipal* aTriggeringPrincipal,
-                                       nsIURI* aSourceURI, uint32_t aLineNumber,
-                                       uint32_t aColumnNumber,
-                                       const nsAString& aStyleText,
-                                       nsresult* aRv) {
+bool nsStyleUtil::CSPAllowsInlineStyle(
+    Element* aElement, dom::Document* aDocument,
+    nsIPrincipal* aTriggeringPrincipal, uint32_t aLineNumber,
+    uint32_t aColumnNumber, const nsAString& aStyleText, nsresult* aRv) {
   nsresult rv;
 
   if (aRv) {
     *aRv = NS_OK;
   }
 
-  nsIPrincipal* principal = aPrincipal;
-  if (aTriggeringPrincipal &&
-      BasePrincipal::Cast(aTriggeringPrincipal)->OverridesCSP(aPrincipal)) {
-    principal = aTriggeringPrincipal;
-  }
-
   nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(csp));
-
-  if (NS_FAILED(rv)) {
-    if (aRv) *aRv = rv;
-    return false;
+  if (aTriggeringPrincipal && BasePrincipal::Cast(aTriggeringPrincipal)
+                                  ->OverridesCSP(aDocument->NodePrincipal())) {
+    nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aTriggeringPrincipal);
+    if (ep) {
+      csp = ep->GetCsp();
+    }
+  } else {
+    csp = aDocument->GetCsp();
   }
 
   if (!csp) {
     // No CSP --> the style is allowed
     return true;
   }
 
   // query the nonce
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -22,16 +22,17 @@ class nsIURI;
 struct gfxFontFeature;
 struct gfxAlternateValue;
 struct nsCSSKTableEntry;
 struct nsCSSValueList;
 
 namespace mozilla {
 class FontSlantStyle;
 namespace dom {
+class Document;
 class Element;
 }
 }  // namespace mozilla
 
 // Style utility functions
 class nsStyleUtil {
  public:
   static bool DashMatchCompare(const nsAString& aAttributeValue,
@@ -122,52 +123,47 @@ class nsStyleUtil {
    * @return false if we can be sure that the object-fit & object-position
    *         properties on 'aStylePos' cannot cause a replaced element's
    *         contents to overflow its content-box. Otherwise (if overflow is
    *         is possible), returns true.
    */
   static bool ObjectPropsMightCauseOverflow(const nsStylePosition* aStylePos);
 
   /*
-   *  Does this principal have a CSP that blocks the application of
+   *  Does the document have a CSP that blocks the application of
    *  inline styles? Returns false if application of the style should
    *  be blocked.
    *
    *  @param aContent
    *      The <style> element that the caller wants to know whether to honor.
    *      Included to check the nonce attribute if one is provided. Allowed to
    *      be null, if this is for something other than a <style> element (in
    *      which case nonces won't be checked).
-   *  @param aPrincipal
-   *      The principal of the of the document (*not* of the style sheet).
-   *      The document's principal is where any Content Security Policy that
-   *      should be used to block or allow inline styles will be located.
+   *  @param aDocument
+   *      The document containing the inline style (for querying the CSP);
    *  @param aTriggeringPrincipal
    *      The principal of the scripted caller which added the inline
    *      stylesheet, or null if no scripted caller can be identified.
-   *  @param aSourceURI
-   *      URI of document containing inline style (for reporting violations)
    *  @param aLineNumber
    *      Line number of inline style element in the containing document (for
    *      reporting violations)
    *  @param aColumnNumber
    *      Column number of inline style element in the containing document (for
    *      reporting violations)
    *  @param aStyleText
    *      Contents of the inline style element (for reporting violations)
    *  @param aRv
    *      Return error code in case of failure
    *  @return
    *      Does CSP allow application of the specified inline style?
    */
   static bool CSPAllowsInlineStyle(mozilla::dom::Element* aContent,
-                                   nsIPrincipal* aPrincipal,
+                                   mozilla::dom::Document* aDocument,
                                    nsIPrincipal* aTriggeringPrincipal,
-                                   nsIURI* aSourceURI, uint32_t aLineNumber,
-                                   uint32_t aColumnNumber,
+                                   uint32_t aLineNumber, uint32_t aColumnNumber,
                                    const nsAString& aStyleText, nsresult* aRv);
 
   template <size_t N>
   static bool MatchesLanguagePrefix(const char16_t* aLang, size_t aLen,
                                     const char16_t (&aPrefix)[N]) {
     return !NS_strncmp(aLang, aPrefix, N - 1) &&
            (aLen == N - 1 || aLang[N - 1] == '-');
   }
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.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 "mozilla/LoadInfo.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/ExpandedPrincipal.h"
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/ClientSource.h"
 #include "mozilla/dom/PerformanceStorage.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/net/CookieSettings.h"
 #include "mozilla/NullPrincipal.h"
@@ -23,17 +24,16 @@
 #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"
 #include "nsQueryObject.h"
 #include "nsRedirectHistoryEntry.h"
 #include "nsSandboxFlags.h"
 #include "LoadInfo.h"
 
@@ -412,16 +412,17 @@ 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),
+      mCspToInherit(rhs.mCspToInherit),
       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),
@@ -468,17 +469,18 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
       mCspNonce(rhs.mCspNonce),
       mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes) {}
 
 LoadInfo::LoadInfo(
     nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
     nsIPrincipal* aPrincipalToInherit, nsIPrincipal* aSandboxedLoadingPrincipal,
     nsIPrincipal* aTopLevelPrincipal,
     nsIPrincipal* aTopLevelStorageAreaPrincipal, nsIURI* aResultPrincipalURI,
-    nsICookieSettings* aCookieSettings, const Maybe<ClientInfo>& aClientInfo,
+    nsICookieSettings* aCookieSettings, nsIContentSecurityPolicy* aCspToInherit,
+    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 aForceAllowDataURI,
     bool aAllowInsecureRedirectToDataURI,
@@ -501,16 +503,17 @@ LoadInfo::LoadInfo(
     uint32_t aRequestBlockingReason)
     : mLoadingPrincipal(aLoadingPrincipal),
       mTriggeringPrincipal(aTriggeringPrincipal),
       mPrincipalToInherit(aPrincipalToInherit),
       mTopLevelPrincipal(aTopLevelPrincipal),
       mTopLevelStorageAreaPrincipal(aTopLevelStorageAreaPrincipal),
       mResultPrincipalURI(aResultPrincipalURI),
       mCookieSettings(aCookieSettings),
+      mCspToInherit(aCspToInherit),
       mClientInfo(aClientInfo),
       mReservedClientInfo(aReservedClientInfo),
       mInitialClientInfo(aInitialClientInfo),
       mController(aController),
       mSecurityFlags(aSecurityFlags),
       mInternalContentPolicyType(aContentPolicyType),
       mTainting(aTainting),
       mUpgradeInsecureRequests(aUpgradeInsecureRequests),
@@ -1413,10 +1416,73 @@ LoadInfo::GetCspEventListener(nsICSPEven
 }
 
 NS_IMETHODIMP
 LoadInfo::SetCspEventListener(nsICSPEventListener* aCSPEventListener) {
   mCSPEventListener = aCSPEventListener;
   return NS_OK;
 }
 
+already_AddRefed<nsIContentSecurityPolicy> LoadInfo::GetCsp() {
+  // Before querying the CSP from the client we have to check if the
+  // triggeringPrincipal originates from an addon and potentially
+  // overrides the CSP stored within the client.
+  if (mLoadingPrincipal && BasePrincipal::Cast(mTriggeringPrincipal)
+                               ->OverridesCSP(mLoadingPrincipal)) {
+    nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(mTriggeringPrincipal);
+    nsCOMPtr<nsIContentSecurityPolicy> addonCSP;
+    if (ep) {
+      addonCSP = ep->GetCsp();
+    }
+    return addonCSP.forget();
+  }
+
+  if (mClientInfo.isNothing()) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsINode> node = do_QueryReferent(mLoadingContext);
+  RefPtr<Document> doc = node ? node->OwnerDoc() : nullptr;
+
+  // If the client is of type window, then we return the cached CSP
+  // stored on the document instead of having to deserialize the CSP
+  // from the ClientInfo.
+  if (doc && mClientInfo->Type() == ClientType::Window) {
+    nsCOMPtr<nsIContentSecurityPolicy> docCSP = doc->GetCsp();
+    return docCSP.forget();
+  }
+
+  Maybe<mozilla::ipc::CSPInfo> cspInfo = mClientInfo->GetCspInfo();
+  if (cspInfo.isNothing()) {
+    return nullptr;
+  }
+  nsCOMPtr<nsIContentSecurityPolicy> clientCSP =
+      CSPInfoToCSP(cspInfo.ref(), doc);
+  return clientCSP.forget();
+}
+
+already_AddRefed<nsIContentSecurityPolicy> LoadInfo::GetPreloadCsp() {
+  if (mClientInfo.isNothing()) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsINode> node = do_QueryReferent(mLoadingContext);
+  RefPtr<Document> doc = node ? node->OwnerDoc() : nullptr;
+
+  // If the client is of type window, then we return the cached CSP
+  // stored on the document instead of having to deserialize the CSP
+  // from the ClientInfo.
+  if (doc && mClientInfo->Type() == ClientType::Window) {
+    nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = doc->GetPreloadCsp();
+    return preloadCsp.forget();
+  }
+
+  Maybe<mozilla::ipc::CSPInfo> cspInfo = mClientInfo->GetPreloadCspInfo();
+  if (cspInfo.isNothing()) {
+    return nullptr;
+  }
+  nsCOMPtr<nsIContentSecurityPolicy> preloadCSP =
+      CSPInfoToCSP(cspInfo.ref(), doc);
+  return preloadCSP.forget();
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -2,21 +2,23 @@
 /* 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_LoadInfo_h
 #define mozilla_LoadInfo_h
 
+#include "nsIContentSecurityPolicy.h"
 #include "nsIContentPolicy.h"
 #include "nsILoadInfo.h"
 #include "nsIPrincipal.h"
 #include "nsIWeakReferenceUtils.h"  // for nsWeakPtr
 #include "nsIURI.h"
+#include "nsContentUtils.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 
 class nsICookieSettings;
@@ -83,27 +85,45 @@ class LoadInfo final : public nsILoadInf
   already_AddRefed<nsILoadInfo> CloneForNewRequest() const;
 
   void SetIsPreflight();
   void SetUpgradeInsecureRequests();
   void SetBrowserUpgradeInsecureRequests();
   void SetBrowserWouldUpgradeInsecureRequests();
   void SetIsFromProcessingFrameAttributes();
 
+  // Hands off from the cspToInherit functionality!
+  // The only place that knows that a new document load needs to inherit the
+  // CSP is the docshell. At that point neither the document nor the client
+  // are available yet. Since we need a way to transfer the CSP from the
+  // docshell to the document, we temporarily store the CSP that needs to
+  // be inherited within the Loadinfo. In other words, those two functions
+  // build the bridge to transfer the CSP from the docshell into the doc.
+  void SetCSPToInherit(nsIContentSecurityPolicy* aCspToInherit) {
+    mCspToInherit = aCspToInherit;
+  }
+  // Certain schemes need to inherit the CSP. If needed, we temporarily
+  // store the CSP from the embedding/opening document here which then
+  // gets propagated to the new doc within Document::InitCSP(). This
+  // member is only ever non-null if the new doc actually needs to
+  // inherit the CSP from the embedding/opening document.
+  nsIContentSecurityPolicy* GetCSPToInherit() { return mCspToInherit; }
+
  private:
   // private constructor that is only allowed to be called from within
   // 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, nsICookieSettings* aCookieSettings,
+           nsIContentSecurityPolicy* aCspToInherit,
            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 aForceAllowDataURI,
@@ -152,16 +172,17 @@ class LoadInfo final : public nsILoadInf
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
   nsCOMPtr<nsIPrincipal> mSandboxedLoadingPrincipal;
   nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
   nsCOMPtr<nsIPrincipal> mTopLevelStorageAreaPrincipal;
   nsCOMPtr<nsIURI> mResultPrincipalURI;
   nsCOMPtr<nsICSPEventListener> mCSPEventListener;
   nsCOMPtr<nsICookieSettings> mCookieSettings;
+  nsCOMPtr<nsIContentSecurityPolicy> mCspToInherit;
 
   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 nsIContentSecurityPolicy;
 interface nsICookieSettings;
 interface nsICSPEventListener;
 interface nsINode;
 interface nsIPrincipal;
 interface nsIRedirectHistoryEntry;
 interface nsIURI;
 webidl Document;
 webidl BrowsingContext;
@@ -44,16 +45,17 @@ native OriginAttributes(mozilla::OriginA
 [ref] native const_ClientInfoRef(const mozilla::dom::ClientInfo);
       native UniqueClientSource(mozilla::UniquePtr<mozilla::dom::ClientSource>);
       native UniqueClientSourceMove(mozilla::UniquePtr<mozilla::dom::ClientSource>&&);
 [ref] native const_MaybeClientInfoRef(const mozilla::Maybe<mozilla::dom::ClientInfo>);
 [ref] native const_ServiceWorkerDescriptorRef(const mozilla::dom::ServiceWorkerDescriptor);
 [ref] native const_MaybeServiceWorkerDescriptorRef(const mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor>);
 [ptr] native PerformanceStoragePtr(mozilla::dom::PerformanceStorage);
       native LoadTainting(mozilla::LoadTainting);
+      native CSPRef(already_AddRefed<nsIContentSecurityPolicy>);
 
 typedef unsigned long nsSecurityFlags;
 
 /**
  * The LoadInfo object contains information about a network load, why it
  * was started, and how we plan on using the resulting response.
  * If a network request is redirected, the new channel will receive a new
  * LoadInfo object. The new object will contain mostly the same
@@ -1020,16 +1022,23 @@ interface nsILoadInfo : nsISupports
 
   /**
    * Get the PerformanceStorage.
    */
   [noscript, nostdcall, notxpcom]
   PerformanceStoragePtr GetPerformanceStorage();
 
   /**
+   * Query the CSP (or Preload CSP for preloads) which should be
+   * enforced for the channel this loadinfo belongs to.
+   */
+  [notxpcom,nostdcall] CSPRef GetCsp();
+  [notxpcom,nostdcall] CSPRef GetPreloadCsp();
+
+  /**
     * The service worker and fetch specifications require returning the
     * exact tainting level of the Response passed to FetchEvent.respondWith().
     * This method allows us to override the tainting level in that case.
     *
     * NOTE: This should not be used outside of service worker code! Use
     *       nsILoadInfo::MaybeIncreaseTainting() instead.
    */
   [noscript, nostdcall, notxpcom]
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -135,16 +135,17 @@ struct LoadInfoArgs
   bool                        loadTriggeredFromExternal;
   bool                        serviceWorkerTaintingSynthesized;
   bool                        documentHasUserInteracted;
   bool                        documentHasLoaded;
   nsString                    cspNonce;
   bool                        isFromProcessingFrameAttributes;
   CookieSettingsArgs cookieSettings;
   uint32_t                    requestBlockingReason;
+  CSPInfo?                    cspToInheritInfo;
 };
 
 /**
  * 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.
  */
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -2,16 +2,17 @@
 /* vim: set sw=2 ts=2 et 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/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/dom/ScriptLoader.h"
+#include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/nsCSPService.h"
 
 #include "GeckoProfiler.h"
 #include "mozAutoDocUpdate.h"
 #include "mozilla/IdleTaskRunner.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/css/Loader.h"
@@ -1063,30 +1064,36 @@ void nsHtml5TreeOpExecutor::SetSpeculati
 
 void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) {
   if (!StaticPrefs::security_csp_enable()) {
     return;
   }
 
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsIPrincipal* principal = mDocument->NodePrincipal();
-  nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
-  nsresult rv =
-      principal->EnsurePreloadCSP(mDocument, getter_AddRefs(preloadCsp));
-  NS_ENSURE_SUCCESS_VOID(rv);
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = mDocument->GetPreloadCsp();
+  if (!preloadCsp) {
+    preloadCsp = new nsCSPContext();
+    rv = preloadCsp->SetRequestContextWithDocument(mDocument);
+    NS_ENSURE_SUCCESS_VOID(rv);
+  }
 
   // please note that meta CSPs and CSPs delivered through a header need
   // to be joined together.
   rv = preloadCsp->AppendPolicy(
       aCSP,
       false,  // csp via meta tag can not be report only
       true);  // delivered through the meta tag
   NS_ENSURE_SUCCESS_VOID(rv);
 
+  nsPIDOMWindowInner* inner = mDocument->GetInnerWindow();
+  if (inner) {
+    inner->SetPreloadCsp(preloadCsp);
+  }
   mDocument->ApplySettingsFromCSP(true);
 }
 
 void nsHtml5TreeOpExecutor::SetSpeculationReferrerPolicy(
     ReferrerPolicy aReferrerPolicy) {
   // Record "speculated" referrer policy locally and thread through the
   // speculation phase.  The actual referrer policy will be set by
   // HTMLMetaElement::BindToTree().
--- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp
@@ -111,33 +111,20 @@ bool StoragePrincipalHelper::VerifyValid
     const mozilla::ipc::ContentPrincipalInfo& spInfo =
         aStoragePrincipalInfo.get_ContentPrincipalInfo();
     const mozilla::ipc::ContentPrincipalInfo& pInfo =
         aPrincipalInfo.get_ContentPrincipalInfo();
 
     if (!spInfo.attrs().EqualsIgnoringFPD(pInfo.attrs()) ||
         spInfo.originNoSuffix() != pInfo.originNoSuffix() ||
         spInfo.spec() != pInfo.spec() || spInfo.domain() != pInfo.domain() ||
-        spInfo.baseDomain() != pInfo.baseDomain() ||
-        spInfo.securityPolicies().Length() !=
-            pInfo.securityPolicies().Length()) {
+        spInfo.baseDomain() != pInfo.baseDomain()) {
       return false;
     }
 
-    for (uint32_t i = 0; i < spInfo.securityPolicies().Length(); ++i) {
-      if (spInfo.securityPolicies()[i].policy() !=
-              pInfo.securityPolicies()[i].policy() ||
-          spInfo.securityPolicies()[i].reportOnlyFlag() !=
-              pInfo.securityPolicies()[i].reportOnlyFlag() ||
-          spInfo.securityPolicies()[i].deliveredViaMetaTagFlag() !=
-              pInfo.securityPolicies()[i].deliveredViaMetaTagFlag()) {
-        return false;
-      }
-    }
-
     return true;
   }
 
   if (aStoragePrincipalInfo.type() ==
       mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
     // Nothing to check here.
     return true;
   }
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -1023,17 +1023,26 @@ nsresult nsWindowWatcher::OpenWindowInte
     nsCOMPtr<nsPIDOMWindowOuter> newWindow = do_QueryInterface(*aResult);
     NS_ASSERTION(newWindow == newDocShell->GetWindow(), "Different windows??");
 
     // The principal of the initial about:blank document gets set up in
     // nsWindowWatcher::AddWindow. Make sure to call it. In the common case
     // this call already happened when the window was created, but
     // SetInitialPrincipalToSubject is safe to call multiple times.
     if (newWindow) {
-      newWindow->SetInitialPrincipalToSubject();
+      nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
+      nsCOMPtr<mozIDOMWindowProxy> targetOpener = newWindow->GetOpener();
+      nsCOMPtr<nsIDocShell> openerDocShell(do_GetInterface(targetOpener));
+      if (openerDocShell) {
+        RefPtr<Document> openerDoc =
+            static_cast<nsDocShell*>(openerDocShell.get())->GetDocument();
+        cspToInheritForAboutBlank = openerDoc ? openerDoc->GetCsp() : nullptr;
+      }
+      newWindow->SetInitialPrincipalToSubject(cspToInheritForAboutBlank);
+
       if (aIsPopupSpam) {
         nsGlobalWindowOuter* globalWin = nsGlobalWindowOuter::Cast(newWindow);
         MOZ_ASSERT(!globalWin->IsPopupSpamWindow(),
                    "Who marked it as popup spam already???");
         if (!globalWin->IsPopupSpamWindow()) {  // Make sure we don't mess up
                                                 // our counter even if the above
                                                 // assert fails.
           globalWin->SetIsPopupSpamWindow(true);
@@ -1097,31 +1106,21 @@ nsresult nsWindowWatcher::OpenWindowInte
       if (doc) {
         nsCOMPtr<nsIReferrerInfo> referrerInfo =
             new ReferrerInfo(doc->GetDocumentURI(), doc->GetReferrerPolicy());
         loadState->SetReferrerInfo(referrerInfo);
       }
     }
   }
 
-  // Currently we query the CSP from the principal of the inner window.
-  // After Bug 965637 we can query the CSP directly from the inner window.
-  // Further, if the JS context is null, then the subjectPrincipal falls
-  // back to being the SystemPrincipal (see above) and the SystemPrincipal
-  // can currently not hold a CSP. We use the same semantics here.
   if (loadState && cx) {
     nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(cx);
     if (win) {
-      nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
-      if (principal) {
-        nsCOMPtr<nsIContentSecurityPolicy> csp;
-        rv = principal->GetCsp(getter_AddRefs(csp));
-        NS_ENSURE_SUCCESS(rv, rv);
-        loadState->SetCsp(csp);
-      }
+      nsCOMPtr<nsIContentSecurityPolicy> csp = win->GetCsp();
+      loadState->SetCsp(csp);
     }
   }
 
   if (isNewToplevelWindow) {
     // Notify observers that the window is open and ready.
     // The window has not yet started to load a document.
     nsCOMPtr<nsIObserverService> obsSvc =
         mozilla::services::GetObserverService();
--- a/toolkit/mozapps/extensions/AddonContentPolicy.cpp
+++ b/toolkit/mozapps/extensions/AddonContentPolicy.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "AddonContentPolicy.h"
 
-#include "mozilla/dom/nsCSPUtils.h"
+#include "mozilla/dom/nsCSPContext.h"
 #include "nsCOMPtr.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentTypeParser.h"
 #include "nsContentUtils.h"
 #include "nsIConsoleService.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIContent.h"
 #include "mozilla/dom/Document.h"
@@ -392,20 +392,22 @@ AddonContentPolicy::ValidateAddonCSP(con
                        "UUID generator did not return a valid UUID");
 
     url.AppendASCII(idString + 1, NSID_LENGTH - 3);
   }
 
   RefPtr<BasePrincipal> principal =
       BasePrincipal::CreateCodebasePrincipal(NS_ConvertUTF16toUTF8(url));
 
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->EnsureCSP(nullptr, getter_AddRefs(csp));
+  nsCOMPtr<nsIURI> selfURI;
+  principal->GetURI(getter_AddRefs(selfURI));
+  RefPtr<nsCSPContext> csp = new nsCSPContext();
+  rv =
+      csp->SetRequestContextWithPrincipal(principal, selfURI, EmptyString(), 0);
   NS_ENSURE_SUCCESS(rv, rv);
-
   csp->AppendPolicy(aPolicyString, false, false);
 
   const nsCSPPolicy* policy = csp->GetPolicy(0);
   if (!policy) {
     CSPValidator validator(url, nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE);
     aResult.Assign(validator.GetError());
     return NS_OK;
   }
--- a/xpfe/appshell/nsAppShellService.cpp
+++ b/xpfe/appshell/nsAppShellService.cpp
@@ -841,17 +841,17 @@ nsAppShellService::RegisterTopLevelWindo
   NS_ENSURE_ARG_POINTER(aWindow);
 
   nsCOMPtr<nsIDocShell> docShell;
   aWindow->GetDocShell(getter_AddRefs(docShell));
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow());
   NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
-  domWindow->SetInitialPrincipalToSubject();
+  domWindow->SetInitialPrincipalToSubject(nullptr);
 
   // tell the window mediator about the new window
   nsCOMPtr<nsIWindowMediator> mediator(
       do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
   NS_ASSERTION(mediator, "Couldn't get window mediator.");
 
   if (mediator) mediator->RegisterWindow(aWindow);
 
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -225,17 +225,18 @@ nsresult nsWebShellWindow::Initialize(
   if (nsContentUtils::IsInitialized()) {  // Sometimes this happens really early
                                           // See bug 793370.
     MOZ_ASSERT(mDocShell->ItemType() == nsIDocShellTreeItem::typeChrome);
     nsCOMPtr<nsIPrincipal> principal =
         nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
     if (nsContentUtils::IsExpandedPrincipal(principal)) {
       principal = nullptr;
     }
-    rv = mDocShell->CreateAboutBlankContentViewer(principal);
+    rv = mDocShell->CreateAboutBlankContentViewer(principal,
+                                                  /* aCsp = */ nullptr);
     NS_ENSURE_SUCCESS(rv, rv);
     RefPtr<Document> doc = mDocShell->GetDocument();
     NS_ENSURE_TRUE(!!doc, NS_ERROR_FAILURE);
     doc->SetIsInitialDocument(true);
   }
 
   if (nullptr != aUrl) {
     nsCString tmpStr;