Bug 1521808 - Implement Cross-Origin-Opener-Policy header r=nika,mayhemer
☠☠ backed out by a791ece512e1 ☠ ☠
authorValentin Gosu <valentin.gosu@gmail.com>
Tue, 12 Feb 2019 12:15:54 +0000
changeset 458670 8d5174a560fac56a1a6046cba5ebe95df62836a3
parent 458669 9ad69518248dbb19568b8485df6fb6e36cc56f32
child 458671 cc0a5c7dabb45dcdfd2a2b723c950cb7f1baf322
push id35543
push userccoroiu@mozilla.com
push dateTue, 12 Feb 2019 16:27:27 +0000
treeherdermozilla-central@4ad4e42bcb99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika, mayhemer
bugs1521808
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1521808 - Implement Cross-Origin-Opener-Policy header r=nika,mayhemer Differential Revision: https://phabricator.services.mozilla.com/D18119
docshell/base/nsDocShell.cpp
ipc/glue/BackgroundUtils.cpp
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/ipc/NeckoMessageUtils.h
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/NullHttpChannel.cpp
netwerk/protocol/http/nsHttpAtomList.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsIHttpChannel.idl
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9826,16 +9826,18 @@ nsresult nsDocShell::DoURILoad(nsDocShel
 
   if (inheritPrincipal) {
     securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
   if (isSandBoxed) {
     securityFlags |= nsILoadInfo::SEC_SANDBOXED;
   }
 
+  // TODO: pass openerPolicy through loadInfo?
+
   RefPtr<LoadInfo> loadInfo =
       (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
           ? new LoadInfo(loadingWindow, aLoadState->TriggeringPrincipal(),
                          topLevelLoadingContext, securityFlags)
           : new LoadInfo(loadingPrincipal, aLoadState->TriggeringPrincipal(),
                          loadingNode, securityFlags, contentPolicyType);
 
   if (aLoadState->PrincipalToInherit()) {
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -480,17 +480,18 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
       redirectChainIncludingInternalRedirects, redirectChain,
       ancestorPrincipals, aLoadInfo->AncestorOuterWindowIDs(), ipcClientInfo,
       ipcReservedClientInfo, ipcInitialClientInfo, ipcController,
       aLoadInfo->CorsUnsafeHeaders(), aLoadInfo->GetForcePreflight(),
       aLoadInfo->GetIsPreflight(), aLoadInfo->GetLoadTriggeredFromExternal(),
       aLoadInfo->GetServiceWorkerTaintingSynthesized(),
       aLoadInfo->GetDocumentHasUserInteracted(),
       aLoadInfo->GetDocumentHasLoaded(),
-      aLoadInfo->GetIsFromProcessingFrameAttributes());
+      aLoadInfo->GetIsFromProcessingFrameAttributes(),
+      aLoadInfo->GetOpenerPolicy());
 
   return NS_OK;
 }
 
 nsresult LoadInfoArgsToLoadInfo(
     const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
     nsILoadInfo** outLoadInfo) {
   if (aOptionalLoadInfoArgs.type() == OptionalLoadInfoArgs::Tvoid_t) {
@@ -642,46 +643,51 @@ nsresult LoadInfoArgsToLoadInfo(
       loadInfoArgs.serviceWorkerTaintingSynthesized(),
       loadInfoArgs.documentHasUserInteracted(),
       loadInfoArgs.documentHasLoaded());
 
   if (loadInfoArgs.isFromProcessingFrameAttributes()) {
     loadInfo->SetIsFromProcessingFrameAttributes();
   }
 
+  loadInfo->SetOpenerPolicy(loadInfoArgs.openerPolicy());
+
   loadInfo.forget(outLoadInfo);
   return NS_OK;
 }
 
 void LoadInfoToParentLoadInfoForwarder(
     nsILoadInfo* aLoadInfo, ParentLoadInfoForwarderArgs* aForwarderArgsOut) {
   if (!aLoadInfo) {
     *aForwarderArgsOut = ParentLoadInfoForwarderArgs(
         false, void_t(), nsILoadInfo::TAINTING_BASIC,
         false,  // serviceWorkerTaintingSynthesized
         false,  // documentHasUserInteracted
-        false   // documentHasLoaded
-    );
+        false,  // documentHasLoaded
+        nsILoadInfo::OPENER_POLICY_NULL);
     return;
   }
 
   OptionalIPCServiceWorkerDescriptor ipcController = void_t();
   Maybe<ServiceWorkerDescriptor> controller(aLoadInfo->GetController());
   if (controller.isSome()) {
     ipcController = controller.ref().ToIPC();
   }
 
   uint32_t tainting = nsILoadInfo::TAINTING_BASIC;
   Unused << aLoadInfo->GetTainting(&tainting);
 
+  nsILoadInfo::CrossOriginOpenerPolicy openerPolicy =
+      aLoadInfo->GetOpenerPolicy();
+
   *aForwarderArgsOut = ParentLoadInfoForwarderArgs(
       aLoadInfo->GetAllowInsecureRedirectToDataURI(), ipcController, tainting,
       aLoadInfo->GetServiceWorkerTaintingSynthesized(),
       aLoadInfo->GetDocumentHasUserInteracted(),
-      aLoadInfo->GetDocumentHasLoaded());
+      aLoadInfo->GetDocumentHasLoaded(), openerPolicy);
 }
 
 nsresult MergeParentLoadInfoForwarder(
     ParentLoadInfoForwarderArgs const& aForwarderArgs, nsILoadInfo* aLoadInfo) {
   if (!aLoadInfo) {
     return NS_OK;
   }
 
@@ -700,16 +706,18 @@ nsresult MergeParentLoadInfoForwarder(
 
   if (aForwarderArgs.serviceWorkerTaintingSynthesized()) {
     aLoadInfo->SynthesizeServiceWorkerTainting(
         static_cast<LoadTainting>(aForwarderArgs.tainting()));
   } else {
     aLoadInfo->MaybeIncreaseTainting(aForwarderArgs.tainting());
   }
 
+  // TODO: merge openerPolicy
+
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasUserInteracted(
       aForwarderArgs.documentHasUserInteracted()));
   MOZ_ALWAYS_SUCCEEDS(
       aLoadInfo->SetDocumentHasLoaded(aForwarderArgs.documentHasLoaded()));
 
   return NS_OK;
 }
 
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -86,17 +86,18 @@ LoadInfo::LoadInfo(
       mIsDocshellReload(false),
       mSendCSPViolationEvents(true),
       mForcePreflight(false),
       mIsPreflight(false),
       mLoadTriggeredFromExternal(false),
       mServiceWorkerTaintingSynthesized(false),
       mDocumentHasUserInteracted(false),
       mDocumentHasLoaded(false),
-      mIsFromProcessingFrameAttributes(false) {
+      mIsFromProcessingFrameAttributes(false),
+      mOpenerPolicy(nsILoadInfo::OPENER_POLICY_NULL) {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
 
 #ifdef DEBUG
   // TYPE_DOCUMENT loads initiated by javascript tests will go through
   // nsIOService and use the wrong constructor.  Don't enforce the
   // !TYPE_DOCUMENT check in those cases
   bool skipContentTypeCheck = false;
@@ -369,17 +370,18 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
       mIsDocshellReload(false),
       mSendCSPViolationEvents(true),
       mForcePreflight(false),
       mIsPreflight(false),
       mLoadTriggeredFromExternal(false),
       mServiceWorkerTaintingSynthesized(false),
       mDocumentHasUserInteracted(false),
       mDocumentHasLoaded(false),
-      mIsFromProcessingFrameAttributes(false) {
+      mIsFromProcessingFrameAttributes(false),
+      mOpenerPolicy(nsILoadInfo::OPENER_POLICY_NULL) {
   // Top-level loads are never third-party
   // Grab the information we can out of the window.
   MOZ_ASSERT(aOuterWindow);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   // if the load is sandboxed, we can not also inherit the principal
   if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
     mForceInheritPrincipalDropped =
@@ -476,17 +478,18 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
       mForcePreflight(rhs.mForcePreflight),
       mIsPreflight(rhs.mIsPreflight),
       mLoadTriggeredFromExternal(rhs.mLoadTriggeredFromExternal),
       // mServiceWorkerTaintingSynthesized must be handled specially during
       // redirect
       mServiceWorkerTaintingSynthesized(false),
       mDocumentHasUserInteracted(rhs.mDocumentHasUserInteracted),
       mDocumentHasLoaded(rhs.mDocumentHasLoaded),
-      mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes) {}
+      mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes),
+      mOpenerPolicy(rhs.mOpenerPolicy) {}
 
 LoadInfo::LoadInfo(
     nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
     nsIPrincipal* aPrincipalToInherit, nsIPrincipal* aSandboxedLoadingPrincipal,
     nsIPrincipal* aTopLevelPrincipal,
     nsIPrincipal* aTopLevelStorageAreaPrincipal, nsIURI* aResultPrincipalURI,
     const Maybe<ClientInfo>& aClientInfo,
     const Maybe<ClientInfo>& aReservedClientInfo,
@@ -555,17 +558,18 @@ LoadInfo::LoadInfo(
       mAncestorOuterWindowIDs(aAncestorOuterWindowIDs),
       mCorsUnsafeHeaders(aCorsUnsafeHeaders),
       mForcePreflight(aForcePreflight),
       mIsPreflight(aIsPreflight),
       mLoadTriggeredFromExternal(aLoadTriggeredFromExternal),
       mServiceWorkerTaintingSynthesized(aServiceWorkerTaintingSynthesized),
       mDocumentHasUserInteracted(aDocumentHasUserInteracted),
       mDocumentHasLoaded(aDocumentHasLoaded),
-      mIsFromProcessingFrameAttributes(false) {
+      mIsFromProcessingFrameAttributes(false),
+      mOpenerPolicy(nsILoadInfo::OPENER_POLICY_NULL) {
   // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
   MOZ_ASSERT(mLoadingPrincipal ||
              aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   mRedirectChainIncludingInternalRedirects.SwapElements(
       aRedirectChainIncludingInternalRedirects);
 
@@ -1394,10 +1398,22 @@ LoadInfo::GetCspEventListener(nsICSPEven
 }
 
 NS_IMETHODIMP
 LoadInfo::SetCspEventListener(nsICSPEventListener* aCSPEventListener) {
   mCSPEventListener = aCSPEventListener;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+LoadInfo::GetOpenerPolicy(nsILoadInfo::CrossOriginOpenerPolicy* aOpenerPolicy) {
+  *aOpenerPolicy = mOpenerPolicy;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetOpenerPolicy(nsILoadInfo::CrossOriginOpenerPolicy aOpenerPolicy) {
+  mOpenerPolicy = aOpenerPolicy;
+  return NS_OK;
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -199,14 +199,16 @@ class LoadInfo final : public nsILoadInf
   bool mServiceWorkerTaintingSynthesized;
   bool mDocumentHasUserInteracted;
   bool mDocumentHasLoaded;
 
   // Is true if this load was triggered by processing the attributes of the
   // browsing context container.
   // See nsILoadInfo.isFromProcessingFrameAttributes
   bool mIsFromProcessingFrameAttributes;
+
+  nsILoadInfo::CrossOriginOpenerPolicy mOpenerPolicy;
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_LoadInfo_h
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -1067,9 +1067,20 @@ interface nsILoadInfo : nsISupports
   attribute nsICSPEventListener cspEventListener;
 
   /**
    * This attribute will be true if this is a load triggered by
    * https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes
    * or https://html.spec.whatwg.org/multipage/obsolete.html#process-the-frame-attributes
    */
   [infallible] readonly attribute boolean isFromProcessingFrameAttributes;
+
+  cenum CrossOriginOpenerPolicy : 8 {
+    OPENER_POLICY_NULL           = 0,
+    OPENER_POLICY_SAME_ORIGIN    = 1,
+    OPENER_POLICY_SAME_SITE      = 2,
+    OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG = 0x80,
+    OPENER_POLICY_SAME_ORIGIN_ALLOW_OUTGOING = OPENER_POLICY_SAME_ORIGIN | OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG,
+    OPENER_POLICY_SAME_SITE_ALLOW_OUTGOING = OPENER_POLICY_SAME_SITE | OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG
+  };
+
+  [infallible] attribute nsILoadInfo_CrossOriginOpenerPolicy openerPolicy;
 };
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -17,16 +17,17 @@ include PBackgroundSharedTypes;
 
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
 using ArrayOfStringPairs from "mozilla/net/PHttpChannelParams.h";
 using struct nsHttpAtom from "nsHttp.h";
 using class mozilla::net::nsHttpResponseHead from "nsHttpResponseHead.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using nsILoadInfo::CrossOriginOpenerPolicy from "mozilla/net/NeckoMessageUtils.h";
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 // LoadInfo IPDL structs
 //-----------------------------------------------------------------------------
 
@@ -106,16 +107,17 @@ struct LoadInfoArgs
   nsCString[]                 corsUnsafeHeaders;
   bool                        forcePreflight;
   bool                        isPreflight;
   bool                        loadTriggeredFromExternal;
   bool                        serviceWorkerTaintingSynthesized;
   bool                        documentHasUserInteracted;
   bool                        documentHasLoaded;
   bool                        isFromProcessingFrameAttributes;
+  CrossOriginOpenerPolicy     openerPolicy;
 };
 
 /**
  * Not every channel necessarily has a loadInfo attached.
  */
 union OptionalLoadInfoArgs
 {
   void_t;
@@ -146,16 +148,18 @@ struct ParentLoadInfoForwarderArgs
 
   // We must also note that the tainting value was explicitly set
   // by the service worker.
   bool serviceWorkerTaintingSynthesized;
 
   bool documentHasUserInteracted;
   bool documentHasLoaded;
 
+  CrossOriginOpenerPolicy openerPolicy;
+
   // IMPORTANT: when you add new properites here you must also update
   // LoadInfoToParentLoadInfoForwarder and MergeParentLoadInfoForwarder
   // in BackgroundUtils.cpp/.h!
 };
 
 /**
  * This structure is used to carry selected properties of a LoadInfo
  * object to the parent process that might have changed in the child
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -11,16 +11,17 @@
 #include "ipc/IPCMessageUtils.h"
 #include "nsExceptionHandler.h"
 #include "nsIHttpChannel.h"
 #include "nsPrintfCString.h"
 #include "nsString.h"
 #include "prio.h"
 #include "mozilla/net/DNS.h"
 #include "TimingStruct.h"
+#include "nsILoadInfo.h"
 
 namespace IPC {
 
 // nsIPermissionManager utilities
 
 struct Permission {
   nsCString origin, type;
   uint32_t capability, expireType;
@@ -181,11 +182,26 @@ struct ParamTraits<mozilla::net::Resourc
 };
 
 template <>
 struct ParamTraits<nsIHttpChannel::FlashPluginState>
     : public ContiguousEnumSerializerInclusive<
           nsIHttpChannel::FlashPluginState, nsIHttpChannel::FlashPluginUnknown,
           nsIHttpChannel::FlashPluginLastValue> {};
 
+struct CrossOriginOpenerPolicyValidator {
+  static bool IsLegalValue(nsILoadInfo::CrossOriginOpenerPolicy e) {
+    return e == nsILoadInfo::OPENER_POLICY_NULL ||
+           e == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
+           e == nsILoadInfo::OPENER_POLICY_SAME_SITE ||
+           e == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_OUTGOING ||
+           e == nsILoadInfo::OPENER_POLICY_SAME_SITE_ALLOW_OUTGOING;
+  }
+};
+
+template <>
+struct ParamTraits<nsILoadInfo::CrossOriginOpenerPolicy>
+    : EnumSerializer<nsILoadInfo::CrossOriginOpenerPolicy,
+                     CrossOriginOpenerPolicyValidator> {};
+
 }  // namespace IPC
 
 #endif  // mozilla_net_NeckoMessageUtils_h
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -2228,16 +2228,21 @@ HttpBaseChannel::RedirectTo(nsIURI* targ
 
 NS_IMETHODIMP
 HttpBaseChannel::SwitchProcessTo(mozilla::dom::Promise* aTabParent,
                                  uint64_t aIdentifier) {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::UpgradeToSecure() {
   // Upgrades are handled internally between http-on-modify-request and
   // http-on-before-connect, which means upgrades are only possible during
   // on-modify, or WebRequest.onBeforeRequest in Web Extensions.  Once we are
   // past the code path where upgrades are handled, attempting an upgrade
   // will throw an error.
   NS_ENSURE_TRUE(mUpgradableToSecure, NS_ERROR_NOT_AVAILABLE);
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -216,16 +216,17 @@ class HttpBaseChannel : public nsHashPro
   NS_IMETHOD IsNoCacheResponse(bool *value) override;
   NS_IMETHOD IsPrivateResponse(bool *value) override;
   NS_IMETHOD GetResponseStatus(uint32_t *aValue) override;
   NS_IMETHOD GetResponseStatusText(nsACString &aValue) override;
   NS_IMETHOD GetRequestSucceeded(bool *aValue) override;
   NS_IMETHOD RedirectTo(nsIURI *newURI) override;
   NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise *aTabParent,
                              uint64_t aIdentifier) override;
+  NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) override;
   NS_IMETHOD UpgradeToSecure() override;
   NS_IMETHOD GetRequestContextID(uint64_t *aRCID) override;
   NS_IMETHOD GetTransferSize(uint64_t *aTransferSize) override;
   NS_IMETHOD GetDecodedBodySize(uint64_t *aDecodedBodySize) override;
   NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
   NS_IMETHOD SetRequestContextID(uint64_t aRCID) override;
   NS_IMETHOD GetIsMainDocumentChannel(bool *aValue) override;
   NS_IMETHOD SetIsMainDocumentChannel(bool aValue) override;
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -270,16 +270,21 @@ NullHttpChannel::RedirectTo(nsIURI *aNew
 
 NS_IMETHODIMP
 NullHttpChannel::SwitchProcessTo(mozilla::dom::Promise *aTabParent,
                                  uint64_t aIdentifier) {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) {
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::UpgradeToSecure() { return NS_ERROR_NOT_IMPLEMENTED; }
 
 NS_IMETHODIMP
 NullHttpChannel::GetRequestContextID(uint64_t *_retval) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/http/nsHttpAtomList.h
+++ b/netwerk/protocol/http/nsHttpAtomList.h
@@ -35,16 +35,17 @@ HTTP_ATOM(Content_Disposition, "Content-
 HTTP_ATOM(Content_Encoding, "Content-Encoding")
 HTTP_ATOM(Content_Language, "Content-Language")
 HTTP_ATOM(Content_Length, "Content-Length")
 HTTP_ATOM(Content_Location, "Content-Location")
 HTTP_ATOM(Content_MD5, "Content-MD5")
 HTTP_ATOM(Content_Range, "Content-Range")
 HTTP_ATOM(Content_Type, "Content-Type")
 HTTP_ATOM(Cookie, "Cookie")
+HTTP_ATOM(Cross_Origin_Opener_Policy, "Cross-Origin-Opener-Policy")
 HTTP_ATOM(Date, "Date")
 HTTP_ATOM(DAV, "DAV")
 HTTP_ATOM(Depth, "Depth")
 HTTP_ATOM(Destination, "Destination")
 HTTP_ATOM(DoNotTrack, "DNT")
 HTTP_ATOM(ETag, "Etag")
 HTTP_ATOM(Expect, "Expect")
 HTTP_ATOM(Expires, "Expires")
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -118,16 +118,17 @@
 #include "nsIMultiplexInputStream.h"
 #include "../../cache2/CacheFileUtils.h"
 #include "nsINetworkLinkService.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
 #include "mozilla/net/AsyncUrlChannelClassifier.h"
 #include "mozilla/net/UrlClassifierFeatureFactory.h"
+#include "nsIWebNavigation.h"
 
 #ifdef MOZ_TASK_TRACER
 #  include "GeckoTaskTracer.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #  include "ProfilerMarkerPayload.h"
 #endif
@@ -7147,16 +7148,157 @@ nsresult nsHttpChannel::StartCrossProces
                                         mCrossProcessRedirectIdentifier);
 
   // This will suspend the channel
   rv = WaitForRedirectCallback();
 
   return rv;
 }
 
+static nsILoadInfo::CrossOriginOpenerPolicy GetCrossOriginOpenerPolicy(
+    nsHttpResponseHead *responseHead) {
+  MOZ_ASSERT(responseHead);
+
+  nsAutoCString openerPolicy;
+  Unused << responseHead->GetHeader(nsHttp::Cross_Origin_Opener_Policy,
+                                    openerPolicy);
+
+  // Cross-Origin-Opener-Policy = sameness [ RWS outgoing ]
+  // sameness = %s"same-origin" / %s"same-site" ; case-sensitive
+  // outgoing = %s"unsafe-allow-outgoing" ; case-sensitive
+
+  Tokenizer t(openerPolicy);
+  nsAutoCString sameness;
+  nsAutoCString outgoing;
+
+  // The return value will be true if we find any whitespace. If there is
+  // whitespace, then it must be followed by "unsafe-allow-outgoing" otherwise
+  // this is a malformed header value.
+  bool allowOutgoing = t.ReadUntil(Tokenizer::Token::Whitespace(), sameness);
+  if (allowOutgoing) {
+    t.SkipWhites();
+    bool foundEOF = t.ReadUntil(Tokenizer::Token::EndOfFile(), outgoing);
+    if (!foundEOF) {
+      // Malformed response. There should be no text after the second token.
+      return nsILoadInfo::OPENER_POLICY_NULL;
+    }
+    if (!outgoing.EqualsLiteral("unsafe-allow-outgoing")) {
+      // Malformed response. Only one allowed value for the second token.
+      return nsILoadInfo::OPENER_POLICY_NULL;
+    }
+  }
+
+  nsILoadInfo::CrossOriginOpenerPolicy policy = nsILoadInfo::OPENER_POLICY_NULL;
+  if (sameness.EqualsLiteral("same-origin")) {
+    policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN;
+    if (allowOutgoing) {
+      policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_OUTGOING;
+    }
+  } else if (sameness.EqualsLiteral("same-site")) {
+    policy = nsILoadInfo::OPENER_POLICY_SAME_SITE;
+    if (allowOutgoing) {
+      policy = nsILoadInfo::OPENER_POLICY_SAME_SITE_ALLOW_OUTGOING;
+    }
+  }
+
+  return policy;
+}
+
+static bool CompareCrossOriginOpenerPolicies(
+    nsILoadInfo::CrossOriginOpenerPolicy documentPolicy,
+    nsIPrincipal *documentOrigin,
+    nsILoadInfo::CrossOriginOpenerPolicy resultPolicy,
+    nsIPrincipal *resultOrigin) {
+  if (documentPolicy == nsILoadInfo::OPENER_POLICY_NULL &&
+      resultPolicy == nsILoadInfo::OPENER_POLICY_NULL) {
+    return true;
+  }
+
+  if (documentPolicy != resultPolicy) {
+    return false;
+  }
+  // For the next checks the document and result will have matching policies.
+
+  // We either check if they are same origin or same site.
+  if ((documentPolicy & nsILoadInfo::OPENER_POLICY_SAME_ORIGIN) &&
+      documentOrigin->Equals(resultOrigin)) {
+    return true;
+  }
+
+  if (documentPolicy & nsILoadInfo::OPENER_POLICY_SAME_SITE) {
+    nsAutoCString siteOriginA;
+    nsAutoCString siteOriginB;
+
+    documentOrigin->GetSiteOrigin(siteOriginA);
+    resultOrigin->GetSiteOrigin(siteOriginB);
+    if (siteOriginA == siteOriginB) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) {
+  MOZ_ASSERT(aMismatch);
+  if (!aMismatch) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  *aMismatch = false;
+  // Only consider Cross-Origin-Opener-Policy for toplevel document loads.
+  if (mLoadInfo->GetExternalContentPolicyType() !=
+      nsIContentPolicy::TYPE_DOCUMENT) {
+    return NS_OK;
+  }
+
+  // Maybe the channel failed and we have no response head?
+  nsHttpResponseHead *head =
+      mResponseHead ? mResponseHead : mCachedResponseHead;
+  if (!head) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // Get the policy of the active document, and the policy for the result.
+  nsILoadInfo::CrossOriginOpenerPolicy documentPolicy =
+      mLoadInfo->GetOpenerPolicy();
+  nsILoadInfo::CrossOriginOpenerPolicy resultPolicy =
+      GetCrossOriginOpenerPolicy(head);
+
+  // We use the top window principal as the documentOrigin
+  if (!mTopWindowPrincipal) {
+    GetTopWindowPrincipal(getter_AddRefs(mTopWindowPrincipal));
+  }
+
+  nsCOMPtr<nsIPrincipal> documentOrigin = mTopWindowPrincipal;
+  nsCOMPtr<nsIPrincipal> resultOrigin;
+
+  nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
+      this, getter_AddRefs(resultOrigin));
+
+  if (!CompareCrossOriginOpenerPolicies(documentPolicy, documentOrigin,
+                                        resultPolicy, resultOrigin)) {
+    // If one of the following is false:
+    //   - doc is the initial about:blank document
+    //   - document's unsafe-allow-outgoing is true
+    //   - resultPolicy is null
+    // then we have a mismatch.
+    if (!documentOrigin->GetIsNullPrincipal() ||
+        !(documentPolicy &
+          nsILoadInfo::OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG) ||
+        !(resultPolicy == nsILoadInfo::OPENER_POLICY_NULL)) {
+      *aMismatch = true;
+      return NS_OK;
+    }
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
   nsresult rv;
 
   MOZ_ASSERT(mRequestObserversCalled);
 
   AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK);
 
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -159,16 +159,17 @@ class nsHttpChannel final : public HttpB
   NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
   NS_IMETHOD AsyncOpen(nsIStreamListener *listener,
                        nsISupports *aContext) override;
   NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
   // nsIHttpChannel
   NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
   NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise *aTabParent,
                              uint64_t aIdentifier) override;
+  NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) override;
   // nsIHttpChannelInternal
   NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
   NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload) override;
   NS_IMETHOD GetNavigationStartTimeStamp(TimeStamp *aTimeStamp) override;
   NS_IMETHOD SetNavigationStartTimeStamp(TimeStamp aTimeStamp) override;
   NS_IMETHOD CancelByChannelClassifier(nsresult aErrorCode) override;
   // nsISupportsPriority
   NS_IMETHOD SetPriority(int32_t value) override;
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -453,16 +453,23 @@ interface nsIHttpChannel : nsIChannel
      *                      which the load will proceed in.
      * @param aIdentifier   a 64-bit ID which will be provided to the
      *                      ChildProcessChannelListener.
      */
     [must_use] void switchProcessTo(in Promise aTabPromise,
                                     in unsigned long long aIdentifier);
 
     /**
+     * Used to determine if there is a Cross-Origin-Opener-Policy mismatch
+     * that would require switching the channel to another process.
+     * @throws NS_ERROR_NOT_AVAILABLE if we don't have a responseHead
+     */
+    [must_use] boolean hasCrossOriginOpenerPolicyMismatch();
+
+    /**
      * Flags a channel to be upgraded to HTTPS.
      *
      * Upgrading to a secure channel must happen before or during
      * "http-on-modify-request". If redirectTo is called early as well, it
      * will win and upgradeToSecure will be a no-op.
      *
      * @throws NS_ERROR_NOT_AVAILABLE if called after the channel has already
      *         started to deliver the content to its listener.
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -1004,16 +1004,26 @@ nsViewSourceChannel::RedirectTo(nsIURI *
 NS_IMETHODIMP
 nsViewSourceChannel::SwitchProcessTo(mozilla::dom::Promise *aTabParent,
                                      uint64_t aIdentifier) {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER
                        : mHttpChannel->SwitchProcessTo(aTabParent, aIdentifier);
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) {
+  MOZ_ASSERT(aMismatch);
+  if (!aMismatch) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  *aMismatch = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::UpgradeToSecure() {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER
                        : mHttpChannel->UpgradeToSecure();
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::GetRequestContextID(uint64_t *_retval) {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER