Backed out changeset 09d64535bcda (bug 1216687), a7f1a289dd78, 4dbf06183e6c, 26318a5e3006, 9ae2af3cf86d (bug 1226909) for M(1,2,5) oranges. r=backout
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sat, 05 Dec 2015 16:34:47 +0100
changeset 314484 e648ed99a3a2c93261b8b18647ca445f2e7f869b
parent 314483 3ca0a1e37264624f4304aec23918fb9e0f4910e2
child 314485 35d0e5235621eaf2c176695ca349191eb762bc66
push id8217
push usermartin.thomson@gmail.com
push dateSun, 06 Dec 2015 20:42:51 +0000
reviewersbackout
bugs1216687, 1226909
milestone45.0a1
backs out09d64535bcda005593b0e29fcfe813f07e128b79
Backed out changeset 09d64535bcda (bug 1216687), a7f1a289dd78, 4dbf06183e6c, 26318a5e3006, 9ae2af3cf86d (bug 1226909) for M(1,2,5) oranges. r=backout
caps/nsScriptSecurityManager.cpp
caps/nsScriptSecurityManager.h
dom/base/EventSource.cpp
dom/base/Navigator.cpp
dom/base/nsScriptLoader.cpp
dom/base/nsXMLHttpRequest.cpp
dom/base/nsXMLHttpRequest.h
dom/base/test/bug435425_redirect.sjs
dom/base/test/test_bug338583.html
dom/fetch/FetchDriver.cpp
dom/fetch/FetchDriver.h
dom/html/HTMLMediaElement.cpp
dom/security/nsContentSecurityManager.cpp
dom/security/nsContentSecurityManager.h
dom/security/test/cors/file_CrossSiteXHR_server.sjs
dom/security/test/cors/test_CrossSiteXHR.html
dom/security/test/csp/file_redirects_main.html
dom/security/test/csp/file_redirects_page.sjs
dom/security/test/csp/file_redirects_resource.sjs
dom/security/test/csp/test_redirects.html
dom/tests/mochitest/fetch/test_fetch_cors.js
ipc/glue/BackgroundUtils.cpp
layout/build/nsLayoutModule.cpp
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/base/nsIOService.cpp
netwerk/base/nsNetUtil.cpp
netwerk/base/nsNetUtil.h
netwerk/build/nsNetCID.h
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/PHttpChannel.ipdl
netwerk/protocol/http/nsCORSListenerProxy.cpp
netwerk/protocol/http/nsCORSListenerProxy.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsIHttpChannelChild.idl
netwerk/protocol/http/nsIHttpChannelInternal.idl
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
testing/web-platform/meta/XMLHttpRequest/send-redirect-to-cors.htm.ini
uriloader/prefetch/nsPrefetchService.cpp
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -426,16 +426,17 @@ nsScriptSecurityManager::IsSystemPrincip
 // nsScriptSecurityManager //
 /////////////////////////////
 
 ////////////////////////////////////
 // Methods implementing ISupports //
 ////////////////////////////////////
 NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
                   nsIScriptSecurityManager,
+                  nsIChannelEventSink,
                   nsIObserver)
 
 ///////////////////////////////////////////////////
 // Methods implementing nsIScriptSecurityManager //
 ///////////////////////////////////////////////////
 
 ///////////////// Security Checks /////////////////
 
@@ -1230,16 +1231,51 @@ nsScriptSecurityManager::CanGetService(J
     nsAutoCString errorMsg("Permission denied to get service. CID=");
     char cidStr[NSID_LENGTH];
     aCID.ToProvidedString(cidStr);
     errorMsg.Append(cidStr);
     SetPendingException(cx, errorMsg.get());
     return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
 }
 
+/////////////////////////////////////////////
+// Method implementing nsIChannelEventSink //
+/////////////////////////////////////////////
+NS_IMETHODIMP
+nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel, 
+                                                nsIChannel* newChannel,
+                                                uint32_t redirFlags,
+                                                nsIAsyncVerifyRedirectCallback *cb)
+{
+    nsCOMPtr<nsIPrincipal> oldPrincipal;
+    GetChannelResultPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
+
+    nsCOMPtr<nsIURI> newURI;
+    newChannel->GetURI(getter_AddRefs(newURI));
+    nsCOMPtr<nsIURI> newOriginalURI;
+    newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
+
+    NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
+
+    const uint32_t flags =
+        nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
+        nsIScriptSecurityManager::DISALLOW_SCRIPT;
+    nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
+    if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
+        rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
+    }
+
+    if (NS_FAILED(rv))
+        return rv;
+
+    cb->OnRedirectVerifyCallback(NS_OK);
+    return NS_OK;
+}
+
+
 /////////////////////////////////////
 // Method implementing nsIObserver //
 /////////////////////////////////////
 const char sJSEnabledPrefName[] = "javascript.enabled";
 const char sFileOriginPolicyPrefName[] =
     "security.fileuri.strict_origin_policy";
 
 static const char* kObservedPrefs[] = {
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -9,16 +9,17 @@
 
 #include "nsIScriptSecurityManager.h"
 
 #include "nsIAddonPolicyService.h"
 #include "mozilla/Maybe.h"
 #include "nsIAddonPolicyService.h"
 #include "nsIPrincipal.h"
 #include "nsCOMPtr.h"
+#include "nsIChannelEventSink.h"
 #include "nsIObserver.h"
 #include "nsServiceManagerUtils.h"
 #include "plstr.h"
 #include "js/TypeDecls.h"
 
 #include <stdint.h>
 
 class nsCString;
@@ -33,25 +34,27 @@ class PrincipalOriginAttributes;
 /////////////////////////////
 // nsScriptSecurityManager //
 /////////////////////////////
 #define NS_SCRIPTSECURITYMANAGER_CID \
 { 0x7ee2a4c0, 0x4b93, 0x17d3, \
 { 0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2 }}
 
 class nsScriptSecurityManager final : public nsIScriptSecurityManager,
+                                      public nsIChannelEventSink,
                                       public nsIObserver
 {
 public:
     static void Shutdown();
 
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_SCRIPTSECURITYMANAGER_CID)
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSISCRIPTSECURITYMANAGER
+    NS_DECL_NSICHANNELEVENTSINK
     NS_DECL_NSIOBSERVER
 
     static nsScriptSecurityManager*
     GetScriptSecurityManager();
 
     // Invoked exactly once, by XPConnect.
     static void InitStatics();
 
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -673,17 +673,17 @@ EventSource::InitChannelAndRequestEventS
   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
   nsCOMPtr<nsIDocument> doc =
     nsContentUtils::GetDocumentFromScriptContext(sc);
 
   nsSecurityFlags securityFlags =
     nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
 
   if (mWithCredentials) {
-    securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+    securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
   }
 
   nsCOMPtr<nsIChannel> channel;
   // If we have the document, use it
   if (doc) {
     rv = NS_NewChannel(getter_AddRefs(channel),
                        mSrc,
                        doc,
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1199,18 +1199,17 @@ Navigator::SendBeacon(const nsAString& a
     aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
     return false;
   }
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      doc,
-                     nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
-                       nsILoadInfo::SEC_COOKIES_INCLUDE,
+                     nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
                      nsIContentPolicy::TYPE_BEACON);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
@@ -1311,16 +1310,51 @@ Navigator::SendBeacon(const nsAString& a
   // cancel the channel and any redirected channels it may create.
   nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   nsCOMPtr<nsIInterfaceRequestor> callbacks =
     do_QueryInterface(mWindow->GetDocShell());
   loadGroup->SetNotificationCallbacks(callbacks);
   channel->SetLoadGroup(loadGroup);
 
   RefPtr<BeaconStreamListener> beaconListener = new BeaconStreamListener();
+
+  // Start a preflight if cross-origin and content type is not whitelisted
+  nsCOMPtr<nsIScriptSecurityManager> secMan = nsContentUtils::GetSecurityManager();
+  rv = secMan->CheckSameOriginURI(documentURI, uri, false);
+  bool crossOrigin = NS_FAILED(rv);
+  nsAutoCString contentType, parsedCharset;
+  rv = NS_ParseRequestContentType(mimeType, contentType, parsedCharset);
+  if (crossOrigin &&
+      mimeType.Length() > 0 &&
+      !contentType.Equals(APPLICATION_WWW_FORM_URLENCODED) &&
+      !contentType.Equals(MULTIPART_FORM_DATA) &&
+      !contentType.Equals(TEXT_PLAIN)) {
+
+    // we need to set the sameOriginChecker as a notificationCallback
+    // so we can tell the channel not to follow redirects
+    nsCOMPtr<nsIInterfaceRequestor> soc = nsContentUtils::SameOriginChecker();
+    channel->SetNotificationCallbacks(soc);
+
+    nsCOMPtr<nsIHttpChannelInternal> internalChannel =
+      do_QueryInterface(channel);
+    if (!internalChannel) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return false;
+    }
+    nsTArray<nsCString> unsafeHeaders;
+    unsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
+    rv = internalChannel->SetCorsPreflightParameters(unsafeHeaders,
+                                                     true,
+                                                     doc->NodePrincipal());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRv.Throw(rv);
+      return false;
+    }
+  }
+
   rv = channel->AsyncOpen2(beaconListener);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
   // make the beaconListener hold a strong reference to the loadgroup
   // which is released in ::OnStartRequest
   beaconListener->SetLoadGroup(loadGroup);
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -293,17 +293,17 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
   nsIDocShell *docshell = window->GetDocShell();
   nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
 
   nsSecurityFlags securityFlags =
     aRequest->mCORSMode == CORS_NONE
     ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
     : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
   if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) {
-    securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+    securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
   }
   securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
 
   nsCOMPtr<nsIChannel> channel;
   nsresult rv = NS_NewChannel(getter_AddRefs(channel),
                               aRequest->mURI,
                               context,
                               securityFlags,
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -117,20 +117,21 @@ using namespace mozilla::dom;
                                                    // terms.
 // The above states are mutually exclusive, change with ChangeState() only.
 // The states below can be combined.
 #define XML_HTTP_REQUEST_ABORTED        (1 << 7)  // Internal
 #define XML_HTTP_REQUEST_ASYNC          (1 << 8)  // Internal
 #define XML_HTTP_REQUEST_PARSEBODY      (1 << 9)  // Internal
 #define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 10) // Internal
 #define XML_HTTP_REQUEST_BACKGROUND     (1 << 13) // Internal
-#define XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND (1 << 14) // Internal
-#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 15) // Internal
-#define XML_HTTP_REQUEST_TIMED_OUT (1 << 16) // Internal
-#define XML_HTTP_REQUEST_DELETED (1 << 17) // Internal
+#define XML_HTTP_REQUEST_USE_XSITE_AC   (1 << 14) // Internal
+#define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE (1 << 15) // Internal
+#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 16) // Internal
+#define XML_HTTP_REQUEST_TIMED_OUT (1 << 17) // Internal
+#define XML_HTTP_REQUEST_DELETED (1 << 18) // Internal
 
 #define XML_HTTP_REQUEST_LOADSTATES         \
   (XML_HTTP_REQUEST_UNSENT |                \
    XML_HTTP_REQUEST_OPENED |                \
    XML_HTTP_REQUEST_HEADERS_RECEIVED |      \
    XML_HTTP_REQUEST_LOADING |               \
    XML_HTTP_REQUEST_DONE |                  \
    XML_HTTP_REQUEST_SENT)
@@ -1064,32 +1065,19 @@ nsXMLHttpRequest::GetResponse(JSContext*
   default:
     NS_ERROR("Should not happen");
   }
 
   aResponse.setNull();
 }
 
 bool
-nsXMLHttpRequest::IsCrossSiteCORSRequest()
+nsXMLHttpRequest::IsDeniedCrossSiteRequest()
 {
-  if (!mChannel) {
-    return false;
-  }
-
-  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-  MOZ_ASSERT(loadInfo);
-
-  return loadInfo->GetTainting() == LoadTainting::CORS;
-}
-
-bool
-nsXMLHttpRequest::IsDeniedCrossSiteCORSRequest()
-{
-  if (IsCrossSiteCORSRequest()) {
+  if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) && mChannel) {
     nsresult rv;
     mChannel->GetStatus(&rv);
     if (NS_FAILED(rv)) {
       return true;
     }
   }
   return false;
 }
@@ -1101,17 +1089,17 @@ nsXMLHttpRequest::GetResponseURL(nsAStri
 
   uint16_t readyState = ReadyState();
   if ((readyState == UNSENT || readyState == OPENED) || !mChannel) {
     return;
   }
 
   // Make sure we don't leak responseURL information from denied cross-site
   // requests.
-  if (IsDeniedCrossSiteCORSRequest()) {
+  if (IsDeniedCrossSiteRequest()) {
     return;
   }
 
   nsCOMPtr<nsIURI> responseUrl;
   mChannel->GetURI(getter_AddRefs(responseUrl));
 
   if (!responseUrl) {
     return;
@@ -1129,17 +1117,17 @@ nsXMLHttpRequest::GetStatus(uint32_t *aS
   return NS_OK;
 }
 
 uint32_t
 nsXMLHttpRequest::Status()
 {
   // Make sure we don't leak status information from denied cross-site
   // requests.
-  if (IsDeniedCrossSiteCORSRequest()) {
+  if (IsDeniedCrossSiteRequest()) {
     return 0;
   }
 
   uint16_t readyState = ReadyState();
   if (readyState == UNSENT || readyState == OPENED) {
     return 0;
   }
 
@@ -1179,17 +1167,17 @@ IMPL_CSTRING_GETTER(GetStatusText)
 void
 nsXMLHttpRequest::GetStatusText(nsCString& aStatusText)
 {
   // Return an empty status text on all error loads.
   aStatusText.Truncate();
 
   // Make sure we don't leak status information from denied cross-site
   // requests.
-  if (IsDeniedCrossSiteCORSRequest()) {
+  if (IsDeniedCrossSiteRequest()) {
     return;
   }
 
   // Check the current XHR state to see if it is valid to obtain the statusText
   // value.  This check is to prevent the status text for redirects from being
   // available before all the redirects have been followed and HTTP headers have
   // been received.
   uint16_t readyState = ReadyState();
@@ -1274,17 +1262,17 @@ bool
 nsXMLHttpRequest::IsSafeHeader(const nsACString& header, nsIHttpChannel* httpChannel)
 {
   // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
   if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(header)) {
     NS_WARNING("blocked access to response header");
     return false;
   }
   // if this is not a CORS call all headers are safe
-  if (!IsCrossSiteCORSRequest()) {
+  if (!(mState & XML_HTTP_REQUEST_USE_XSITE_AC)){
     return true;
   }
   // Check for dangerous headers
   // Make sure we don't leak header information from denied cross-site
   // requests.
   if (mChannel) {
     nsresult status;
     mChannel->GetStatus(&status);
@@ -1536,16 +1524,27 @@ nsXMLHttpRequest::GetCurrentJARChannel()
 }
 
 bool
 nsXMLHttpRequest::IsSystemXHR()
 {
   return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
 }
 
+void
+nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
+{
+  // A system XHR (chrome code or a web app with the right permission) can
+  // load anything, and same-origin loads are always allowed.
+  if (!IsSystemXHR() &&
+      !nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
+    mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
+  }
+}
+
 NS_IMETHODIMP
 nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
                        bool async, const nsAString& user,
                        const nsAString& password, uint8_t optional_argc)
 {
   if (!optional_argc) {
     // No optional arguments were passed in. Default async to true.
     async = true;
@@ -1686,20 +1685,16 @@ nsXMLHttpRequest::Open(const nsACString&
   }
   else {
     // Otherwise use CORS. Again, make sure that potential result documents
     // use the same principal as the loader.
     secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
                nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
 
-  if (mIsAnon) {
-    secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
-  }
-
   // If we have the document, use it. Unfortunately, for dedicated workers
   // 'doc' ends up being the parent document, which is not the document
   // that we want to use. So make sure to avoid using 'doc' in that situation.
   if (doc && doc->NodePrincipal() == mPrincipal) {
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        uri,
                        doc,
                        secFlags,
@@ -1716,17 +1711,18 @@ nsXMLHttpRequest::Open(const nsACString&
                        nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                        loadGroup,
                        nullptr,   // aCallbacks
                        loadFlags);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mState &= ~XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
+  mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
+              XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE);
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (httpChannel) {
     rv = httpChannel->SetRequestMethod(method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the initiator type
     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
@@ -1923,16 +1919,22 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
     mRequestObserver->OnStartRequest(request, ctxt);
   }
 
   if (request != mChannel) {
     // Can this still happen?
     return NS_OK;
   }
 
+  // Always treat tainted channels as cross-origin.
+  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
+  if (loadInfo && loadInfo->GetTainting() != LoadTainting::Basic) {
+    mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
+  }
+
   // Don't do anything if we have been aborted
   if (mState & XML_HTTP_REQUEST_UNSENT)
     return NS_OK;
 
   /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT.  See bug 361773.
      XHR2 spec says this is correct. */
   if (mState & XML_HTTP_REQUEST_ABORTED) {
     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
@@ -2108,34 +2110,30 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
     mResponseXML = do_QueryInterface(responseDoc);
     mResponseXML->SetChromeXHRDocURI(chromeXHRDocURI);
     mResponseXML->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI);
 
     if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
       mResponseXML->ForceEnableXULXBL();
     }
 
-    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-    MOZ_ASSERT(loadInfo);
-    bool isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic;
-
-    if (isCrossSite) {
+    if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
       if (htmlDoc) {
         htmlDoc->DisableCookieAccess();
       }
     }
 
     nsCOMPtr<nsIStreamListener> listener;
     nsCOMPtr<nsILoadGroup> loadGroup;
     channel->GetLoadGroup(getter_AddRefs(loadGroup));
 
     rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
                                          nullptr, getter_AddRefs(listener),
-                                         !isCrossSite);
+                                         !(mState & XML_HTTP_REQUEST_USE_XSITE_AC));
     NS_ENSURE_SUCCESS(rv, rv);
 
     mXMLParserStreamListener = listener;
     rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // We won't get any progress events anyway if we didn't have progress
@@ -2791,29 +2789,43 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
           do_QueryInterface(httpChannel);
         uploadChannel->SetUploadStream(postDataStream, contentType, mUploadTotal);
         // Reset the method to its original value
         httpChannel->SetRequestMethod(method);
       }
     }
   }
 
+  if (httpChannel) {
+    nsAutoCString contentTypeHeader;
+    rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
+                                       contentTypeHeader);
+    if (NS_SUCCEEDED(rv)) {
+      if (!nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader)) {
+        mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
+      }
+    }
+  }
+
   ResetResponse();
 
-  if (!IsSystemXHR() && !mIsAnon &&
-      (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS)) {
+  CheckChannelForCrossSiteRequest(mChannel);
+
+  bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
+
+  if (!IsSystemXHR() && withCredentials) {
     // This is quite sad. We have to create the channel in .open(), since the
     // chrome-only xhr.channel API depends on that. However .withCredentials
     // can be modified after, so we don't know what to set the
-    // SEC_COOKIES_INCLUDE flag to when the channel is
+    // SEC_REQUIRE_CORS_WITH_CREDENTIALS flag to when the channel is
     // created. So set it here using a hacky internal API.
 
     // Not doing this for system XHR uses since those don't use CORS.
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-    static_cast<LoadInfo*>(loadInfo.get())->SetIncludeCookiesSecFlag();
+    static_cast<LoadInfo*>(loadInfo.get())->SetWithCredentialsSecFlag();
   }
 
   // Blocking gets are common enough out of XHR that we should mark
   // the channel slow by default for pipeline purposes
   AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
 
   nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
   if (cos) {
@@ -2825,17 +2837,20 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
 
   nsCOMPtr<nsIHttpChannelInternal>
     internalHttpChannel(do_QueryInterface(mChannel));
   if (internalHttpChannel) {
     // Disable Necko-internal response timeouts.
     internalHttpChannel->SetResponseTimeoutEnabled(false);
   }
 
-  if (!mIsAnon) {
+  if (mIsAnon) {
+    AddLoadFlags(mChannel, nsIRequest::LOAD_ANONYMOUS);
+  }
+  else {
     AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
   }
 
   // When we are sync loading, we need to bypass the local cache when it would
   // otherwise block us waiting for exclusive access to the cache.  If we don't
   // do this, then we could dead lock in some cases (see bug 309424).
   //
   // Also don't block on the cache entry on async if it is busy - favoring parallelism
@@ -2861,26 +2876,33 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
       contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
     mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
   }
 
   // We're about to send the request.  Start our timeout.
   mRequestSentTime = PR_Now();
   StartTimeoutTimer();
 
-  // Check if we should enabled cross-origin upload listeners.
-  if (mUpload && mUpload->HasListeners()) {
-    mState |= XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
+  // Check if we need to do a preflight request.
+  if (!mCORSUnsafeHeaders.IsEmpty() ||
+      (mUpload && mUpload->HasListeners()) ||
+      (!method.LowerCaseEqualsLiteral("get") &&
+       !method.LowerCaseEqualsLiteral("post") &&
+       !method.LowerCaseEqualsLiteral("head"))) {
+    mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE;
   }
 
   // Set up the preflight if needed
-  if (!IsSystemXHR()) {
-    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-    loadInfo->SetCorsPreflightInfo(mCORSUnsafeHeaders,
-                                   mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
+  if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) &&
+      (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE)) {
+    NS_ENSURE_TRUE(internalHttpChannel, NS_ERROR_DOM_BAD_URI);
+
+    rv = internalHttpChannel->SetCorsPreflightParameters(mCORSUnsafeHeaders,
+                                                         withCredentials, mPrincipal);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   mIsMappedArrayBuffer = false;
   if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
       Preferences::GetBool("dom.mapped_arraybuffer.enabled", false)) {
     nsCOMPtr<nsIURI> uri;
     nsAutoCString scheme;
 
@@ -3040,17 +3062,17 @@ nsXMLHttpRequest::SetRequestHeader(const
         if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
           safeHeader = true;
           break;
         }
       }
     }
 
     if (!safeHeader) {
-      if (!mCORSUnsafeHeaders.Contains(header, nsCaseInsensitiveCStringArrayComparator())) {
+      if (!mCORSUnsafeHeaders.Contains(header)) {
         mCORSUnsafeHeaders.AppendElement(header);
       }
     }
   } else {
     // Case 1 above
     if (nsContentUtils::IsForbiddenSystemRequestHeader(header)) {
       mergeHeaders = false;
     }
@@ -3245,19 +3267,18 @@ nsXMLHttpRequest::SetWithCredentials(boo
 }
 
 void
 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
 {
   // Return error if we're already processing a request.  Note that we can't use
   // ReadyState() here, because it can't differentiate between "opened" and
   // "sent", so we use mState directly.
-  if ((!(mState & XML_HTTP_REQUEST_UNSENT) &&
-       !(mState & XML_HTTP_REQUEST_OPENED)) ||
-      mIsAnon) {
+  if (!(mState & XML_HTTP_REQUEST_UNSENT) &&
+      !(mState & XML_HTTP_REQUEST_OPENED)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // sync request is not allowed setting withCredentials in window context
   if (HasOrHasHadOwner() &&
       !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
     LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
@@ -3300,80 +3321,132 @@ nsXMLHttpRequest::ChangeState(uint32_t a
     NS_ENSURE_SUCCESS(rv, rv);
 
     DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   }
 
   return rv;
 }
 
+/*
+ * Simple helper class that just forwards the redirect callback back
+ * to the nsXMLHttpRequest.
+ */
+class AsyncVerifyRedirectCallbackForwarder final : public nsIAsyncVerifyRedirectCallback
+{
+public:
+  explicit AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest* xhr)
+    : mXHR(xhr)
+  {
+  }
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
+
+  // nsIAsyncVerifyRedirectCallback implementation
+  NS_IMETHOD OnRedirectVerifyCallback(nsresult result) override
+  {
+    mXHR->OnRedirectVerifyCallback(result);
+
+    return NS_OK;
+  }
+
+private:
+  ~AsyncVerifyRedirectCallbackForwarder() {}
+
+  RefPtr<nsXMLHttpRequest> mXHR;
+};
+
+NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder, mXHR)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)
+
+
 /////////////////////////////////////////////////////
 // nsIChannelEventSink methods:
 //
 NS_IMETHODIMP
 nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
                                          nsIChannel *aNewChannel,
                                          uint32_t    aFlags,
                                          nsIAsyncVerifyRedirectCallback *callback)
 {
   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
 
+  nsresult rv;
+
+  if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
+    CheckChannelForCrossSiteRequest(aNewChannel);
+
+    // Disable redirects for preflighted cross-site requests entirely for now
+    if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) &&
+        (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE)) {
+       aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
+       return NS_ERROR_DOM_BAD_URI;
+    }
+  }
+
   // Prepare to receive callback
   mRedirectCallback = callback;
   mNewRedirectChannel = aNewChannel;
 
   if (mChannelEventSink) {
-    nsCOMPtr<nsIAsyncVerifyRedirectCallback> fwd =
-      EnsureXPCOMifier();
-
-    nsresult rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
-                                                            aNewChannel,
-                                                            aFlags, fwd);
+    RefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
+      new AsyncVerifyRedirectCallbackForwarder(this);
+
+    rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
+                                                   aNewChannel,
+                                                   aFlags, fwd);
     if (NS_FAILED(rv)) {
         mRedirectCallback = nullptr;
         mNewRedirectChannel = nullptr;
     }
     return rv;
   }
   OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
-nsresult
+void
 nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
 {
   NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
   NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
 
   if (NS_SUCCEEDED(result)) {
     mChannel = mNewRedirectChannel;
 
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
     if (httpChannel) {
       // Ensure all original headers are duplicated for the new channel (bug #553888)
-      for (RequestHeader& requestHeader : mModifiedRequestHeaders) {
-        if (requestHeader.value.IsEmpty()) {
-          httpChannel->SetEmptyRequestHeader(requestHeader.header);
+      for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) {
+        --i;
+        if (mModifiedRequestHeaders[i].value.IsEmpty()) {
+          httpChannel->SetEmptyRequestHeader(mModifiedRequestHeaders[i].header);
         } else {
-          httpChannel->SetRequestHeader(requestHeader.header,
-                                        requestHeader.value,
+          httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
+                                        mModifiedRequestHeaders[i].value,
                                         false);
         }
       }
     }
   } else {
     mErrorLoad = true;
   }
 
   mNewRedirectChannel = nullptr;
 
   mRedirectCallback->OnRedirectVerifyCallback(result);
   mRedirectCallback = nullptr;
-
-  return result;
 }
 
 /////////////////////////////////////////////////////
 // nsIProgressEventSink methods:
 //
 
 void
 nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
@@ -3467,18 +3540,18 @@ nsXMLHttpRequest::OnStatus(nsIRequest *a
   }
 
   return NS_OK;
 }
 
 bool
 nsXMLHttpRequest::AllowUploadProgress()
 {
-  return !IsCrossSiteCORSRequest() ||
-    (mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
+  return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
+    (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE);
 }
 
 /////////////////////////////////////////////////////
 // nsIInterfaceRequestor methods:
 //
 NS_IMETHODIMP
 nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
 {
@@ -3528,17 +3601,17 @@ nsXMLHttpRequest::GetInterface(const nsI
 
     // Verify that it's ok to prompt for credentials here, per spec
     // http://xhr.spec.whatwg.org/#the-send%28%29-method
     bool showPrompt = true;
 
     // If authentication fails, XMLHttpRequest origin and
     // the request URL are same origin, ...
     /* Disabled - bug: 799540
-    if (IsCrossSiteCORSRequest()) {
+    if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
       showPrompt = false;
     }
     */
 
     // ... Authorization is not in the list of author request headers, ...
     if (showPrompt) {
       for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
         if (mModifiedRequestHeaders[i].header.
@@ -3735,17 +3808,16 @@ nsHeaderVisitor::VisitHeader(const nsACS
   return NS_OK;
 }
 
 // nsXMLHttpRequestXPCOMifier implementation
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
-  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
--- a/dom/base/nsXMLHttpRequest.h
+++ b/dom/base/nsXMLHttpRequest.h
@@ -36,16 +36,17 @@
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 
 #ifdef Status
 /* Xlib headers insist on this for some reason... Nuke it because
    it'll override our member name */
 #undef Status
 #endif
 
+class AsyncVerifyRedirectCallbackForwarder;
 class nsFormData;
 class nsIJARChannel;
 class nsILoadGroup;
 class nsIUnicodeDecoder;
 class nsIJSID;
 
 namespace mozilla {
 
@@ -420,18 +421,17 @@ private:
   {
     return Send(nullptr, aBody);
   }
   nsresult Send(const RequestBody& aBody)
   {
     return Send(Nullable<RequestBody>(aBody));
   }
 
-  bool IsCrossSiteCORSRequest();
-  bool IsDeniedCrossSiteCORSRequest();
+  bool IsDeniedCrossSiteRequest();
 
   // Tell our channel what network interface ID we were told to use.
   // If it's an HTTP channel and we were told to use a non-default
   // interface ID.
   void PopulateNetworkInterfaceId();
 
 public:
   void Send(JSContext* /*aCx*/, ErrorResult& aRv)
@@ -615,19 +615,28 @@ protected:
 
   already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel();
   already_AddRefed<nsIJARChannel> GetCurrentJARChannel();
 
   bool IsSystemXHR();
 
   void ChangeStateToDone();
 
+  /**
+   * Check if aChannel is ok for a cross-site request by making sure no
+   * inappropriate headers are set, and no username/password is set.
+   *
+   * Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit.
+   */
+  void CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
+
   void StartProgressEventTimer();
 
-  nsresult OnRedirectVerifyCallback(nsresult result);
+  friend class AsyncVerifyRedirectCallbackForwarder;
+  void OnRedirectVerifyCallback(nsresult result);
 
   nsresult Open(const nsACString& method, const nsACString& url, bool async,
                 const mozilla::dom::Optional<nsAString>& user,
                 const mozilla::dom::Optional<nsAString>& password);
 
   already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
 
   nsCOMPtr<nsISupports> mContext;
@@ -826,17 +835,16 @@ public:
 private:
   bool mOldVal;
 };
 
 // A shim class designed to expose the non-DOM interfaces of
 // XMLHttpRequest via XPCOM stuff.
 class nsXMLHttpRequestXPCOMifier final : public nsIStreamListener,
                                          public nsIChannelEventSink,
-                                         public nsIAsyncVerifyRedirectCallback,
                                          public nsIProgressEventSink,
                                          public nsIInterfaceRequestor,
                                          public nsITimerCallback
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXMLHttpRequestXPCOMifier,
                                            nsIStreamListener)
 
@@ -851,17 +859,16 @@ private:
       mXHR->mXPCOMifier = nullptr;
     }
   }
 
 public:
   NS_FORWARD_NSISTREAMLISTENER(mXHR->)
   NS_FORWARD_NSIREQUESTOBSERVER(mXHR->)
   NS_FORWARD_NSICHANNELEVENTSINK(mXHR->)
-  NS_FORWARD_NSIASYNCVERIFYREDIRECTCALLBACK(mXHR->)
   NS_FORWARD_NSIPROGRESSEVENTSINK(mXHR->)
   NS_FORWARD_NSITIMERCALLBACK(mXHR->)
 
   NS_DECL_NSIINTERFACEREQUESTOR
 
 private:
   RefPtr<nsXMLHttpRequest> mXHR;
 };
--- a/dom/base/test/bug435425_redirect.sjs
+++ b/dom/base/test/bug435425_redirect.sjs
@@ -1,6 +1,6 @@
 function handleRequest(request, response)
 {
   response.setStatusLine(null, 302, "Moved");
-  response.setHeader("Location", "http://nosuchdomain.localhost", false);
+  response.setHeader("Location", "http://www.mozilla.org", false);
 }
 
--- a/dom/base/test/test_bug338583.html
+++ b/dom/base/test/test_bug338583.html
@@ -461,16 +461,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       setTestHasFinished(test_id);
     }, parseInt(3000*stress_factor));
   }
 
   function doTest5_c(test_id)
   {
     // credentials using the auth cache
     var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
+    xhr.withCredentials = true;
     // also, test mixed mode UI
     xhr.open("GET", "https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_xhr", true, "user 1", "password 1");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.c");
 
       gEventSourceObj5_c = new EventSource("https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_evtsrc",
                                            { withCredentials: true } );
@@ -489,16 +490,17 @@ https://bugzilla.mozilla.org/show_bug.cg
         doTest5_d(test_id);
       }, parseInt(3000*stress_factor));
     };
   }
 
   function doTest5_d(test_id)
   {
     var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
+    xhr.withCredentials = true;
     xhr.open("GET", "https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_xhr", true, "user 2", "password 2");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.d");
   
       gEventSourceObj5_d = new EventSource("https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_evtsrc");
       ok(!gEventSourceObj5_d.withCredentials, "Wrong withCredentials in test 5.d");
   
@@ -516,16 +518,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       }, parseInt(3000*stress_factor));
     };
   }
 
   function doTest5_e(test_id)
   {
     // credentials using the auth cache
     var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
+    xhr.withCredentials = true;
     xhr.open("GET", "http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_xhr", true, "user 1", "password 1");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.e");
 
       gEventSourceObj5_e = new EventSource("http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_evtsrc",
                                            { get withCredentials() { return true; } } );
       ok(gEventSourceObj5_e.withCredentials, "Wrong withCredentials in test 5.e");
@@ -543,16 +546,17 @@ https://bugzilla.mozilla.org/show_bug.cg
         doTest5_f(test_id);
       }, parseInt(5000*stress_factor));
     };
   }
 
   function doTest5_f(test_id)
   {
     var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
+    xhr.withCredentials = true;
     xhr.open("GET", "http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_xhr", true, "user 2", "password 2");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.f");
 
       gEventSourceObj5_f = new EventSource("http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_evtsrc",
                                            { });
       ok(!gEventSourceObj5_f.withCredentials, "Wrong withCredentials in test 5.f");
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -38,24 +38,25 @@
 #include "Fetch.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(FetchDriver,
-                  nsIStreamListener, nsIInterfaceRequestor,
+                  nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
                   nsIThreadRetargetableStreamListener)
 
 FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
                          nsILoadGroup* aLoadGroup)
   : mPrincipal(aPrincipal)
   , mLoadGroup(aLoadGroup)
   , mRequest(aRequest)
+  , mHasBeenCrossSite(false)
   , mResponseAvailableCalled(false)
   , mFetchCalled(false)
 {
 }
 
 FetchDriver::~FetchDriver()
 {
   // We assert this since even on failures, we should call
@@ -81,16 +82,71 @@ FetchDriver::Fetch(FetchDriverObserver* 
                      "Synchronous fetch not supported");
 
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableMethod(this, &FetchDriver::ContinueFetch);
   return NS_DispatchToCurrentThread(r);
 }
 
 nsresult
+FetchDriver::SetTainting()
+{
+  workers::AssertIsOnMainThread();
+
+  // If we've already been cross-site then we should be fully updated
+  if (mHasBeenCrossSite) {
+    return NS_OK;
+  }
+
+  nsAutoCString url;
+  mRequest->GetURL(url);
+  nsCOMPtr<nsIURI> requestURI;
+  nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url,
+                          nullptr, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Begin Step 8 of the Main Fetch algorithm
+  // https://fetch.spec.whatwg.org/#fetching
+
+  // request's current url's origin is request's origin and the CORS flag is unset
+  // request's current url's scheme is "data" and request's same-origin data-URL flag is set
+  // request's current url's scheme is "about"
+
+  // We have to manually check about:blank here since it's not treated as
+  // an inheriting URL by CheckMayLoad.
+  if (NS_IsAboutBlank(requestURI) ||
+       NS_SUCCEEDED(mPrincipal->CheckMayLoad(requestURI, false /* report */,
+                                             true /*allowIfInheritsPrincipal*/))) {
+    // What the spec calls "basic fetch" is handled within our necko channel
+    // code.  Therefore everything goes through HTTP Fetch
+    return NS_OK;
+  }
+
+  mHasBeenCrossSite = true;
+
+  // request's mode is "same-origin"
+  if (mRequest->Mode() == RequestMode::Same_origin) {
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
+  // request's mode is "no-cors"
+  if (mRequest->Mode() == RequestMode::No_cors) {
+    mRequest->MaybeIncreaseResponseTainting(LoadTainting::Opaque);
+    // What the spec calls "basic fetch" is handled within our necko channel
+    // code.  Therefore everything goes through HTTP Fetch
+    return NS_OK;
+  }
+
+  // Otherwise
+  mRequest->MaybeIncreaseResponseTainting(LoadTainting::CORS);
+
+  return NS_OK;
+}
+
+nsresult
 FetchDriver::ContinueFetch()
 {
   workers::AssertIsOnMainThread();
 
   nsresult rv = HttpFetch();
   if (NS_FAILED(rv)) {
     FailWithNetworkError();
   }
@@ -116,24 +172,18 @@ FetchDriver::HttpFetch()
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri),
                           url,
                           nullptr,
                           nullptr,
                           ios);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Unsafe requests aren't allowed with when using no-core mode.
-  if (mRequest->Mode() == RequestMode::No_cors &&
-      mRequest->UnsafeRequest() &&
-      (!mRequest->HasSimpleMethod() ||
-       !mRequest->Headers()->HasOnlySimpleHeaders())) {
-    MOZ_ASSERT(false, "The API should have caught this");
-    return NS_ERROR_DOM_BAD_URI;
-  }
+  rv = SetTainting();
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Step 2 deals with letting ServiceWorkers intercept requests. This is
   // handled by Necko after the channel is opened.
   // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
   // set based on the Request's flag.
 
   // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
   // true..." is handled by the CORS proxy.
@@ -147,85 +197,100 @@ FetchDriver::HttpFetch()
   // request too.
 
   // Step 3.3 "Let credentials flag be set if one of
   //  - request's credentials mode is "include"
   //  - request's credentials mode is "same-origin" and either the CORS flag
   //    is unset or response tainting is "opaque"
   // is true, and unset otherwise."
 
+  // This is effectivetly the opposite of the use credentials flag in "HTTP
+  // network or cache fetch" in the spec and decides whether to transmit
+  // cookies and other identifying information. LOAD_ANONYMOUS also prevents
+  // new cookies sent by the server from being stored.  This value will
+  // propagate across redirects, which is what we want.
+  const nsLoadFlags credentialsFlag =
+    (mRequest->GetCredentialsMode() == RequestCredentials::Omit ||
+    (mHasBeenCrossSite &&
+     mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
+     mRequest->Mode() == RequestMode::No_cors)) ?
+    nsIRequest::LOAD_ANONYMOUS : 0;
+
   // Set skip serviceworker flag.
   // While the spec also gates on the client being a ServiceWorker, we can't
   // infer that here. Instead we rely on callers to set the flag correctly.
   const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
                                  nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;
 
-  nsSecurityFlags secFlags = nsILoadInfo::SEC_ABOUT_BLANK_INHERITS;
-  if (mRequest->Mode() == RequestMode::Cors) {
-    secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
+  nsSecurityFlags secFlags;
+  if (mRequest->Mode() == RequestMode::Cors &&
+      mRequest->GetCredentialsMode() == RequestCredentials::Include) {
+    secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
+               nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
+  } else if (mRequest->Mode() == RequestMode::Cors) {
+    secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
   } else if (mRequest->Mode() == RequestMode::Same_origin) {
-    secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
+    secFlags = nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
   } else if (mRequest->Mode() == RequestMode::No_cors) {
-    secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
+    secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
   } else {
     MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
     return NS_ERROR_UNEXPECTED;
   }
 
   if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
     secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
   }
 
-  // This is handles the use credentials flag in "HTTP
-  // network or cache fetch" in the spec and decides whether to transmit
-  // cookies and other identifying information.
-  if (mRequest->GetCredentialsMode() == RequestCredentials::Include) {
-    secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
-  } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
-    secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
-  } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) {
-    secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
-  } else {
-    MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
-    return NS_ERROR_UNEXPECTED;
-  }
-
   // From here on we create a channel and set its properties with the
   // information from the InternalRequest. This is an implementation detail.
   MOZ_ASSERT(mLoadGroup);
   nsCOMPtr<nsIChannel> chan;
 
-  nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
+  nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | credentialsFlag |
     bypassFlag | nsIChannel::LOAD_CLASSIFY_URI;
   if (mDocument) {
     MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal);
     rv = NS_NewChannel(getter_AddRefs(chan),
                        uri,
                        mDocument,
-                       secFlags,
+                       secFlags |
+                         nsILoadInfo::SEC_ABOUT_BLANK_INHERITS,
                        mRequest->ContentPolicyType(),
                        mLoadGroup,
                        nullptr, /* aCallbacks */
                        loadFlags,
                        ios);
   } else {
     rv = NS_NewChannel(getter_AddRefs(chan),
                        uri,
                        mPrincipal,
-                       secFlags,
+                       secFlags |
+                         nsILoadInfo::SEC_ABOUT_BLANK_INHERITS,
                        mRequest->ContentPolicyType(),
                        mLoadGroup,
                        nullptr, /* aCallbacks */
                        loadFlags,
                        ios);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   mLoadGroup = nullptr;
 
+  // Insert ourselves into the notification callbacks chain so we can handle
+  // cross-origin redirects.
+#ifdef DEBUG
+  {
+    nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
+    chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
+    MOZ_ASSERT(!notificationCallbacks);
+  }
+#endif
+  chan->SetNotificationCallbacks(this);
+
   // FIXME(nsm): Bug 1120715.
   // Step 3.4 "If request's cache mode is default and request's header list
   // contains a header named `If-Modified-Since`, `If-None-Match`,
   // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode
   // to no-store."
 
   // Step 3.5 begins "HTTP network or cache fetch".
   // HTTP network or cache fetch
@@ -340,30 +405,52 @@ FetchDriver::HttpFetch()
     }
   }
 
   // If preflight is required, start a "CORS preflight fetch"
   // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
   // implementation is handled by the http channel calling into
   // nsCORSListenerProxy. We just inform it which unsafe headers are included
   // in the request.
-  if (mRequest->Mode() == RequestMode::Cors) {
+  if (IsUnsafeRequest()) {
+    if (mRequest->Mode() == RequestMode::No_cors) {
+      return NS_ERROR_DOM_BAD_URI;
+    }
+
+    mRequest->SetRedirectMode(RequestRedirect::Error);
+
     nsAutoTArray<nsCString, 5> unsafeHeaders;
     mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
-    nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
-    loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
+
+    nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
+    NS_ENSURE_TRUE(internalChan, NS_ERROR_DOM_BAD_URI);
+
+    rv = internalChan->SetCorsPreflightParameters(
+      unsafeHeaders,
+      mRequest->GetCredentialsMode() == RequestCredentials::Include,
+      mPrincipal);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   rv = chan->AsyncOpen2(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
   return NS_OK;
 }
 
+bool
+FetchDriver::IsUnsafeRequest()
+{
+  return mHasBeenCrossSite &&
+         (mRequest->UnsafeRequest() &&
+          (!mRequest->HasSimpleMethod() ||
+           !mRequest->Headers()->HasOnlySimpleHeaders()));
+}
+
 already_AddRefed<InternalResponse>
 FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
                                          nsIURI* aFinalURI,
                                          bool aFoundOpaqueRedirect)
 {
   MOZ_ASSERT(aResponse);
   nsAutoCString reqURL;
   if (aFinalURI) {
@@ -467,33 +554,16 @@ FetchDriver::OnStartRequest(nsIRequest* 
   MOZ_ASSERT(!mPipeOutputStream);
   MOZ_ASSERT(mObserver);
 
   RefPtr<InternalResponse> response;
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aRequest);
 
-  // On a successful redirect we perform the following substeps of HTTP Fetch,
-  // step 5, "redirect status", step 11.
-
-  // Step 11.5 "Append locationURL to request's url list." so that when we set the
-  // Response's URL from the Request's URL in Main Fetch, step 15, we get the
-  // final value. Note, we still use a single URL value instead of a list.
-  // Because of that we only need to do this after the request finishes.
-  nsCOMPtr<nsIURI> newURI;
-  rv = NS_GetFinalChannelURI(channel, getter_AddRefs(newURI));
-  if (NS_FAILED(rv)) {
-    FailWithNetworkError();
-    return rv;
-  }
-  nsAutoCString newUrl;
-  newURI->GetSpec(newUrl);
-  mRequest->SetURL(newUrl);
-
   bool foundOpaqueRedirect = false;
 
   if (httpChannel) {
     uint32_t responseStatus;
     httpChannel->GetResponseStatus(&responseStatus);
 
     if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
       if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
@@ -590,23 +660,28 @@ FetchDriver::OnStartRequest(nsIRequest* 
 
   nsCOMPtr<nsILoadInfo> loadInfo;
   rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     return rv;
   }
 
+  LoadTainting channelTainting = LoadTainting::Basic;
+  if (loadInfo) {
+    channelTainting = loadInfo->GetTainting();
+  }
+
   // Propagate any tainting from the channel back to our response here.  This
   // step is not reflected in the spec because the spec is written such that
   // FetchEvent.respondWith() just passes the already-tainted Response back to
   // the outer fetch().  In gecko, however, we serialize the Response through
   // the channel and must regenerate the tainting from the channel in the
   // interception case.
-  mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
+  mRequest->MaybeIncreaseResponseTainting(channelTainting);
 
   // Resolves fetch() promise which may trigger code running in a worker.  Make
   // sure the Response is fully initialized before calling this.
   mResponse = BeginAndGetFilteredResponse(response, channelURI,
                                           foundOpaqueRedirect);
 
   nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -669,25 +744,168 @@ FetchDriver::OnStopRequest(nsIRequest* a
   if (mObserver) {
     mObserver->OnResponseEnd();
     mObserver = nullptr;
   }
 
   return NS_OK;
 }
 
+// This is called when the channel is redirected.
+NS_IMETHODIMP
+FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+                                    nsIChannel* aNewChannel,
+                                    uint32_t aFlags,
+                                    nsIAsyncVerifyRedirectCallback *aCallback)
+{
+  NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
+
+  if (NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) ||
+      NS_IsHSTSUpgradeRedirect(aOldChannel, aNewChannel, aFlags)) {
+    aCallback->OnRedirectVerifyCallback(NS_OK);
+    return NS_OK;
+  }
+
+  // We should only ever get here if we use a "follow" redirect policy,
+  // or if if we set an "error" policy as a result of a CORS policy.
+  MOZ_ASSERT(mRequest->GetRedirectMode() == RequestRedirect::Follow ||
+             (mRequest->GetRedirectMode() == RequestRedirect::Error &&
+              IsUnsafeRequest()));
+
+  // HTTP Fetch step 5, "redirect status", step 1
+  if (NS_WARN_IF(mRequest->GetRedirectMode() == RequestRedirect::Error)) {
+    aOldChannel->Cancel(NS_BINDING_FAILED);
+    return NS_BINDING_FAILED;
+  }
+
+  // HTTP Fetch step 5, "redirect status", steps 2 through 6 are automatically
+  // handled by necko before calling AsyncOnChannelRedirect() with the new
+  // nsIChannel.
+
+  // HTTP Fetch step 5, "redirect status", steps 7 and 8 enforcing a redirect
+  // count are done by Necko.  The pref used is "network.http.redirection-limit"
+  // which is set to 20 by default.
+
+  // HTTP Fetch Step 9, "redirect status". This is enforced by the
+  // nsCORSListenerProxy. It forbids redirecting to data:
+
+  // HTTP Fetch step 5, "redirect status", step 10 requires us to halt the
+  // redirect, but successfully return an opaqueredirect Response to the
+  // initiating Fetch.
+
+  // The following steps are from HTTP Fetch step 5, "redirect status", step 11
+  // which requires the RequestRedirect to be "follow". We asserted that we're
+  // in either "follow" or "error" mode here.
+
+  // HTTP Fetch step 5, "redirect status", steps 11.1 and 11.2 block redirecting
+  // to a URL with credentials in CORS mode.  This is implemented in
+  // nsCORSListenerProxy.
+
+  // On a successful redirect we perform the following substeps of HTTP Fetch,
+  // step 5, "redirect status", step 11.
+
+  // Step 11.5 "Append locationURL to request's url list." so that when we set the
+  // Response's URL from the Request's URL in Main Fetch, step 15, we get the
+  // final value. Note, we still use a single URL value instead of a list.
+  nsCOMPtr<nsIURI> newURI;
+  nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
+  if (NS_FAILED(rv)) {
+    aOldChannel->Cancel(rv);
+    return rv;
+  }
+
+  // We need to update our request's URL.
+  nsAutoCString newUrl;
+  newURI->GetSpec(newUrl);
+  mRequest->SetURL(newUrl);
+
+  // Implement Main Fetch step 8 again on redirect.
+  rv = SetTainting();
+  if (NS_FAILED(rv)) {
+    aOldChannel->Cancel(rv);
+    return rv;
+  }
+
+  // Requests that require preflight are not permitted to redirect.
+  // Fetch spec section 4.2 "HTTP Fetch", step 4.9 just uses the manual
+  // redirect flag to decide whether to execute step 4.10 or not. We do not
+  // represent it in our implementation.
+  // The only thing we do is to check if the request requires a preflight (part
+  // of step 4.9), in which case we abort. This part cannot be done by
+  // nsCORSListenerProxy since it does not have access to mRequest.
+  // which case. Step 4.10.3 is handled by OnRedirectVerifyCallback(), and all
+  // the other steps are handled by nsCORSListenerProxy.
+
+  if (IsUnsafeRequest()) {
+    // We can't handle redirects that require preflight yet.
+    // This is especially true for no-cors requests, which much always be
+    // blocked if they require preflight.
+
+    // Simply fire an error here.
+    aOldChannel->Cancel(NS_BINDING_FAILED);
+    return NS_BINDING_FAILED;
+  }
+
+  // Otherwise, we rely on necko and the CORS proxy to do the right thing
+  // as the redirect is followed.  In general this means http
+  // fetch.  If we've ever been CORS, we need to stay CORS.
+
+  // Possibly set the LOAD_ANONYMOUS flag on the channel.
+  if (mHasBeenCrossSite &&
+      mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
+      mRequest->Mode() == RequestMode::No_cors) {
+    // In the case of a "no-cors" mode request with "same-origin" credentials,
+    // we have to set LOAD_ANONYMOUS manually here in order to avoid sending
+    // credentials on a cross-origin redirect.
+    nsLoadFlags flags;
+    rv = aNewChannel->GetLoadFlags(&flags);
+    if (NS_SUCCEEDED(rv)) {
+      flags |= nsIRequest::LOAD_ANONYMOUS;
+      rv = aNewChannel->SetLoadFlags(flags);
+    }
+    if (NS_FAILED(rv)) {
+      aOldChannel->Cancel(rv);
+      return rv;
+    }
+  }
+#ifdef DEBUG
+  {
+    // Make sure nothing in the redirect chain screws up our credentials
+    // settings. LOAD_ANONYMOUS must be set if we RequestCredentials is "omit"
+    // or "same-origin".
+    nsLoadFlags flags;
+    aNewChannel->GetLoadFlags(&flags);
+    bool shouldBeAnon =
+      mRequest->GetCredentialsMode() == RequestCredentials::Omit ||
+      (mHasBeenCrossSite &&
+       mRequest->GetCredentialsMode() == RequestCredentials::Same_origin);
+    MOZ_ASSERT(!!(flags & nsIRequest::LOAD_ANONYMOUS) == shouldBeAnon);
+  }
+#endif
+
+  aCallback->OnRedirectVerifyCallback(NS_OK);
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 FetchDriver::CheckListenerChain()
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
 {
+  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
+    *aResult = static_cast<nsIChannelEventSink*>(this);
+    NS_ADDREF_THIS();
+    return NS_OK;
+  }
+
   if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
     *aResult = static_cast<nsIStreamListener*>(this);
     NS_ADDREF_THIS();
     return NS_OK;
   }
   if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
     *aResult = static_cast<nsIRequestObserver*>(this);
     NS_ADDREF_THIS();
--- a/dom/fetch/FetchDriver.h
+++ b/dom/fetch/FetchDriver.h
@@ -50,23 +50,25 @@ protected:
 
   virtual void OnResponseAvailableInternal(InternalResponse* aResponse) = 0;
 
 private:
   bool mGotResponseAvailable;
 };
 
 class FetchDriver final : public nsIStreamListener,
+                          public nsIChannelEventSink,
                           public nsIInterfaceRequestor,
                           public nsIThreadRetargetableStreamListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
 
   explicit FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
                        nsILoadGroup* aLoadGroup);
   NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
 
   void
@@ -75,27 +77,30 @@ public:
 private:
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   RefPtr<InternalRequest> mRequest;
   RefPtr<InternalResponse> mResponse;
   nsCOMPtr<nsIOutputStream> mPipeOutputStream;
   RefPtr<FetchDriverObserver> mObserver;
   nsCOMPtr<nsIDocument> mDocument;
+  bool mHasBeenCrossSite;
 
   DebugOnly<bool> mResponseAvailableCalled;
   DebugOnly<bool> mFetchCalled;
 
   FetchDriver() = delete;
   FetchDriver(const FetchDriver&) = delete;
   FetchDriver& operator=(const FetchDriver&) = delete;
   ~FetchDriver();
 
+  nsresult SetTainting();
   nsresult ContinueFetch();
   nsresult HttpFetch();
+  bool IsUnsafeRequest();
   // Returns the filtered response sent to the observer.
   // Callers who don't have access to a channel can pass null for aFinalURI.
   already_AddRefed<InternalResponse>
   BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI,
                               bool aFoundOpaqueRedirect);
   // Utility since not all cases need to do any post processing of the filtered
   // response.
   nsresult FailWithNetworkError();
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1284,17 +1284,17 @@ nsresult HTMLMediaElement::LoadResource(
   }
 
   // determine what security checks need to be performed in AsyncOpen2().
   nsSecurityFlags securityFlags =
     ShouldCheckAllowOrigin() ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS :
                                nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
 
   if (GetCORSMode() == CORS_USE_CREDENTIALS) {
-    securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+    securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
   }
 
   MOZ_ASSERT(IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
   nsContentPolicyType contentPolicyType = IsHTMLElement(nsGkAtoms::audio) ?
     nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
 
   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> channel;
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -3,34 +3,38 @@
 #include "nsIStreamListener.h"
 #include "nsILoadInfo.h"
 #include "nsContentUtils.h"
 #include "nsCORSListenerProxy.h"
 #include "nsIStreamListener.h"
 
 #include "mozilla/dom/Element.h"
 
-NS_IMPL_ISUPPORTS(nsContentSecurityManager,
-                  nsIContentSecurityManager,
-                  nsIChannelEventSink)
+NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager)
 
 static nsresult
 ValidateSecurityFlags(nsILoadInfo* aLoadInfo)
 {
   nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode();
 
   if (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS &&
       securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
       securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS &&
       securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
       securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
     MOZ_ASSERT(false, "need one securityflag from nsILoadInfo to perform security checks");
     return NS_ERROR_FAILURE;
   }
 
+  // make sure that cors-with-credentials is only used in combination with CORS.
+  if (aLoadInfo->GetRequireCorsWithCredentials() &&
+      securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+    MOZ_ASSERT(false, "can not use cors-with-credentials without cors");
+    return NS_ERROR_FAILURE;
+  }
   // all good, found the right security flags
   return NS_OK;
 }
 
 static bool SchemeIs(nsIURI* aURI, const char* aScheme)
 {
   nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
   NS_ENSURE_TRUE(baseURI, false);
@@ -80,42 +84,50 @@ URIHasFlags(nsIURI* aURI, uint32_t aURIF
   bool hasFlags;
   nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &hasFlags);
   NS_ENSURE_SUCCESS(rv, false);
 
   return hasFlags;
 }
 
 static nsresult
-DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel* aChannel)
+DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
 {
   if (aLoadInfo->GetAllowChrome() &&
       (URIHasFlags(aURI, nsIProtocolHandler::URI_IS_UI_RESOURCE) ||
        SchemeIs(aURI, "moz-safe-about"))) {
     // UI resources are allowed.
     return DoCheckLoadURIChecks(aURI, aLoadInfo);
   }
 
-  NS_ENSURE_FALSE(NS_HasBeenCrossOrigin(aChannel, true),
-                  NS_ERROR_DOM_BAD_URI);
+  nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal();
+  bool sameOriginDataInherits =
+    aLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
 
-  return NS_OK;
+  if (sameOriginDataInherits &&
+      aLoadInfo->GetAboutBlankInherits() &&
+      NS_IsAboutBlank(aURI)) {
+    return NS_OK;
+  }
+
+  return loadingPrincipal->CheckMayLoad(aURI,
+                                        true, // report to console
+                                        sameOriginDataInherits);
 }
 
 static nsresult
 DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
              nsCOMPtr<nsIStreamListener>& aInAndOutListener)
 {
   MOZ_RELEASE_ASSERT(aInAndOutListener, "can not perform CORS checks without a listener");
   nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal();
   RefPtr<nsCORSListenerProxy> corsListener =
     new nsCORSListenerProxy(aInAndOutListener,
                             loadingPrincipal,
-                            aLoadInfo->GetCookiePolicy() ==
-                              nsILoadInfo::SEC_COOKIES_INCLUDE);
+                            aLoadInfo->GetRequireCorsWithCredentials());
   // XXX: @arg: DataURIHandling::Allow
   // lets use  DataURIHandling::Allow for now and then decide on callsite basis. see also:
   // http://mxr.mozilla.org/mozilla-central/source/dom/security/nsCORSListenerProxy.h#33
   nsresult rv = corsListener->Init(aChannel, DataURIHandling::Allow);
   NS_ENSURE_SUCCESS(rv, rv);
   aInAndOutListener = corsListener;
   return NS_OK;
 }
@@ -325,174 +337,82 @@ nsContentSecurityManager::doContentSecur
   NS_ENSURE_ARG(aChannel);
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
 
   if (!loadInfo) {
     MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks");
     return NS_ERROR_UNEXPECTED;
   }
 
-  // if dealing with a redirected channel then we have already installed
-  // streamlistener and redirect proxies and so we are done.
-  if (loadInfo->GetInitialSecurityCheckDone()) {
-    return NS_OK;
-  }
-
   // make sure that only one of the five security flags is set in the loadinfo
   // e.g. do not require same origin and allow cross origin at the same time
   nsresult rv = ValidateSecurityFlags(loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // lets store the initialSecurityCheckDone flag which indicates whether the channel
+  // was initialy evaluated by the contentSecurityManager. Once the inital
+  // asyncOpen() of the channel went through the contentSecurityManager then
+  // redirects do not have perform all the security checks, e.g. no reason
+  // to setup CORS again.
+  bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone();
+
+  // now lets set the initalSecurityFlag for subsequent calls
+  rv = loadInfo->SetInitialSecurityCheckDone(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // since aChannel was openend using asyncOpen2() we have to make sure
   // that redirects of that channel also get openend using asyncOpen2()
   // please note that some implementations of ::AsyncOpen2 might already
   // have set that flag to true (e.g. nsViewSourceChannel) in which case
   // we just set the flag again.
   rv = loadInfo->SetEnforceSecurity(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+  nsCOMPtr<nsIURI> finalChannelURI;
+  rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
+
+  // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
+  if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) ||
+      (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
+    rv = DoSOPChecks(finalChannelURI, loadInfo);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // if dealing with a redirected channel then we only enforce SOP
+  // and can return at this point.
+  if (initialSecurityCheckDone) {
+    return NS_OK;
+  }
+
+  if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
+      (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
+    // Please note that DoCheckLoadURIChecks should only be enforced for
+    // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
+    // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
+    // within nsCorsListenerProxy
+    rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
     rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  rv = CheckChannel(aChannel);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIURI> finalChannelURI;
-  rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Perform all ContentPolicy checks (MixedContent, CSP, ...)
   rv = DoContentSecurityChecks(finalChannelURI, loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // now lets set the initalSecurityFlag for subsequent calls
-  rv = loadInfo->SetInitialSecurityCheckDone(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // all security checks passed - lets allow the load
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsContentSecurityManager::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
-                                                 nsIChannel* aNewChannel,
-                                                 uint32_t aRedirFlags,
-                                                 nsIAsyncVerifyRedirectCallback *aCb)
-{
-  nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->GetLoadInfo();
-  // Are we enforcing security using LoadInfo?
-  if (loadInfo && loadInfo->GetEnforceSecurity()) {
-    nsresult rv = CheckChannel(aNewChannel);
-    if (NS_FAILED(rv)) {
-      aOldChannel->Cancel(rv);
-      return rv;
-    }
-  }
-
-  // Also verify that the redirecting server is allowed to redirect to the
-  // given URI
-  nsCOMPtr<nsIPrincipal> oldPrincipal;
-  nsContentUtils::GetSecurityManager()->
-    GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
-
-  nsCOMPtr<nsIURI> newURI;
-  aNewChannel->GetURI(getter_AddRefs(newURI));
-  nsCOMPtr<nsIURI> newOriginalURI;
-  aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
-
-  NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
-
-  const uint32_t flags =
-      nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
-      nsIScriptSecurityManager::DISALLOW_SCRIPT;
-  nsresult rv = nsContentUtils::GetSecurityManager()->
-    CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
-  if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
-      rv = nsContentUtils::GetSecurityManager()->
-        CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
-  }
-  NS_ENSURE_SUCCESS(rv, rv);  
-
-  aCb->OnRedirectVerifyCallback(NS_OK);
-  return NS_OK;
-}
-
-static void
-AddLoadFlags(nsIRequest *aRequest, nsLoadFlags aNewFlags)
-{
-  nsLoadFlags flags;
-  aRequest->GetLoadFlags(&flags);
-  flags |= aNewFlags;
-  aRequest->SetLoadFlags(flags);
-}
-
-/*
- * Check that this channel passes all security checks. Returns an error code
- * if this requesst should not be permitted.
- */
-nsresult
-nsContentSecurityManager::CheckChannel(nsIChannel* aChannel)
-{
-  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
-  MOZ_ASSERT(loadInfo);
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Handle cookie policies
-  uint32_t cookiePolicy = loadInfo->GetCookiePolicy();
-  if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) {
-    nsIPrincipal* loadingPrincipal = loadInfo->LoadingPrincipal();
-
-    // It doesn't matter what we pass for the third, data-inherits, argument.
-    // Any protocol which inherits won't pay attention to cookies anyway.
-    rv = loadingPrincipal->CheckMayLoad(uri, false, false);
-    if (NS_FAILED(rv)) {
-      AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
-    }
-  }
-  else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) {
-    AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
-  }
-
-  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
-
-  // CORS mode is handled by nsCORSListenerProxy
-  if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
-    if (NS_HasBeenCrossOrigin(aChannel)) {
-      loadInfo->MaybeIncreaseTainting(LoadTainting::CORS);
-    }
-    return NS_OK;
-  }
-
-  // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
-  if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) ||
-      (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
-    rv = DoSOPChecks(uri, loadInfo, aChannel);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
-      (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
-    if (NS_HasBeenCrossOrigin(aChannel)) {
-      loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque);
-    }
-    // Please note that DoCheckLoadURIChecks should only be enforced for
-    // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
-    // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
-    // within nsCorsListenerProxy
-    rv = DoCheckLoadURIChecks(uri, loadInfo);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return NS_OK;
-}
 
 // ==== nsIContentSecurityManager implementation =====
 
 NS_IMETHODIMP
 nsContentSecurityManager::PerformSecurityCheck(nsIChannel* aChannel,
                                                nsIStreamListener* aStreamListener,
                                                nsIStreamListener** outStreamListener)
 {
--- a/dom/security/nsContentSecurityManager.h
+++ b/dom/security/nsContentSecurityManager.h
@@ -4,39 +4,34 @@
  * 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 nsContentSecurityManager_h___
 #define nsContentSecurityManager_h___
 
 #include "nsIContentSecurityManager.h"
 #include "nsIChannel.h"
-#include "nsIChannelEventSink.h"
 
 class nsIStreamListener;
 
 #define NS_CONTENTSECURITYMANAGER_CONTRACTID "@mozilla.org/contentsecuritymanager;1"
 // cdcc1ab8-3cea-4e6c-a294-a651fa35227f
 #define NS_CONTENTSECURITYMANAGER_CID \
 { 0xcdcc1ab8, 0x3cea, 0x4e6c, \
   { 0xa2, 0x94, 0xa6, 0x51, 0xfa, 0x35, 0x22, 0x7f } }
 
 class nsContentSecurityManager : public nsIContentSecurityManager
-                               , public nsIChannelEventSink
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTSECURITYMANAGER
-  NS_DECL_NSICHANNELEVENTSINK
 
   nsContentSecurityManager() {}
 
   static nsresult doContentSecurityCheck(nsIChannel* aChannel,
                                          nsCOMPtr<nsIStreamListener>& aInAndOutListener);
 
 private:
-  static nsresult CheckChannel(nsIChannel* aChannel);
-
   virtual ~nsContentSecurityManager() {}
 
 };
 
 #endif /* nsContentSecurityManager_h___ */
--- a/dom/security/test/cors/file_CrossSiteXHR_server.sjs
+++ b/dom/security/test/cors/file_CrossSiteXHR_server.sjs
@@ -22,19 +22,17 @@ function handleRequest(request, response
     escape(String.fromCharCode.apply(null, bodyBytes)));
 
   if (query.hop) {
     query.hop = parseInt(query.hop, 10);
     hops = eval(query.hops);
     var curHop = hops[query.hop - 1];
     query.allowOrigin = curHop.allowOrigin;
     query.allowHeaders = curHop.allowHeaders;
-    query.allowMethods = curHop.allowMethods;
     query.allowCred = curHop.allowCred;
-    query.noAllowPreflight = curHop.noAllowPreflight;
     if (curHop.setCookie) {
       query.setCookie = unescape(curHop.setCookie);
     }
     if (curHop.cookie) {
       query.cookie = unescape(curHop.cookie);
     }
     query.noCookie = curHop.noCookie;
   }
--- a/dom/security/test/cors/test_CrossSiteXHR.html
+++ b/dom/security/test/cors/test_CrossSiteXHR.html
@@ -1079,158 +1079,36 @@ function runTest() {
              headers: { "Content-Type": "text/plain" },
              hops: [{ server: "http://example.org",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
                     },
                     ],
            },
-           { pass: 1,
+           { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
                       },
              hops: [{ server: "http://example.org",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
                       allowHeaders: "my-header",
                     },
                     ],
            },
            { pass: 0,
-             method: "POST",
-             body: "hi there",
-             headers: { "Content-Type": "text/plain",
-                        "my-header": "myValue",
-                      },
-             hops: [{ server: "http://example.org",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowHeaders: "my-header",
-                    },
-                    { server: "http://sub1.test1.example.org",
-                      allowOrigin: origin,
-                      allowHeaders: "my-header",
-                    },
-                    ],
-           },
-           { pass: 0,
-             method: "POST",
-             body: "hi there",
-             headers: { "Content-Type": "text/plain",
-                        "my-header": "myValue",
-                      },
-             hops: [{ server: "http://example.org",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowHeaders: "my-header",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowHeaders: "my-header",
-                    },
-                    ],
-           },
-           { pass: 0,
-             method: "POST",
-             body: "hi there",
-             headers: { "Content-Type": "text/plain",
-                        "my-header": "myValue",
-                      },
-             hops: [{ server: "http://example.org",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowHeaders: "my-header",
-                    },
-                    { server: "http://example.org",
-                      allowOrigin: origin,
-                      allowHeaders: "my-header",
-                    },
-                    ],
-           },
-           { pass: 0,
-             method: "POST",
-             body: "hi there",
-             headers: { "Content-Type": "text/plain",
-                        "my-header": "myValue",
-                      },
-             hops: [{ server: "http://example.org",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      noAllowPreflight: 1,
-                    },
-                    ],
-           },
-           { pass: 1,
              method: "DELETE",
              hops: [{ server: "http://example.org",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
-                      allowMethods: "DELETE",
-                    },
-                    ],
-           },
-           { pass: 0,
-             method: "DELETE",
-             hops: [{ server: "http://example.org",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowMethods: "DELETE",
-                    },
-                    { server: "http://sub1.test1.example.org",
-                      allowOrigin: origin,
-                      allowMethods: "DELETE",
-                    },
-                    ],
-           },
-           { pass: 0,
-             method: "DELETE",
-             hops: [{ server: "http://example.org",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowMethods: "DELETE",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowMethods: "DELETE",
-                    },
-                    ],
-           },
-           { pass: 0,
-             method: "DELETE",
-             hops: [{ server: "http://example.org",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowMethods: "DELETE",
-                    },
-                    { server: "http://example.org",
-                      allowOrigin: origin,
-                      allowMethods: "DELETE",
-                    },
-                    ],
-           },
-           { pass: 0,
-             method: "DELETE",
-             hops: [{ server: "http://example.org",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowMethods: "DELETE",
-                      noAllowPreflight: 1,
                     },
                     ],
            },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
--- a/dom/security/test/csp/file_redirects_main.html
+++ b/dom/security/test/csp/file_redirects_main.html
@@ -13,16 +13,17 @@ var page = "/tests/dom/security/test/csp
 
 var tests = { "font-src": thisSite+page+"?testid=font-src",
               "frame-src": thisSite+page+"?testid=frame-src",
               "img-src":  thisSite+page+"?testid=img-src",
               "media-src":  thisSite+page+"?testid=media-src",
               "object-src":  thisSite+page+"?testid=object-src",
               "script-src":  thisSite+page+"?testid=script-src",
               "style-src":  thisSite+page+"?testid=style-src",
+              "worker":  thisSite+page+"?testid=worker",
               "xhr-src":  thisSite+page+"?testid=xhr-src",
               "from-worker": thisSite+page+"?testid=from-worker",
               "from-blob-worker": thisSite+page+"?testid=from-blob-worker",
               "img-src-from-css":  thisSite+page+"?testid=img-src-from-css",
             };
 
 var container = document.getElementById("container");
 
--- a/dom/security/test/csp/file_redirects_page.sjs
+++ b/dom/security/test/csp/file_redirects_page.sjs
@@ -9,18 +9,23 @@ function handleRequest(request, response
   });
 
   response.setHeader("Cache-Control", "no-cache", false);
   response.setHeader("Content-Type", "text/html", false);
 
   var resource = "/tests/dom/security/test/csp/file_redirects_resource.sjs";
 
   // CSP header value
+  var additional = ""
+  if (query['testid'] == "worker") {
+    additional = "; script-src 'self' 'unsafe-inline'";
+  }
   response.setHeader("Content-Security-Policy",
-      "default-src 'self' blob: ; style-src 'self' 'unsafe-inline'", false);
+      "default-src 'self' blob: ; style-src 'self' 'unsafe-inline'" + additional,
+      false);
 
   // downloadable font that redirects to another site
   if (query["testid"] == "font-src") {
     var resp = '<style type="text/css"> @font-face { font-family:' +
                '"Redirecting Font"; src: url("' + resource +
                '?res=font&redir=other&id=font-src-redir")} #test{font-family:' +
                '"Redirecting Font"}</style></head><body>' +
                '<div id="test">test</div></body>';
@@ -59,16 +64,22 @@ function handleRequest(request, response
   }
 
   // external stylesheet that redirects to another site
   if (query["testid"] == "style-src") {
     response.write('<link rel="stylesheet" type="text/css" href="'+resource+'?res=style&redir=other&id=style-src-redir"></link>');
     return;
   }
 
+  // worker script resource that redirects to another site
+  if (query["testid"] == "worker") {
+    response.write('<script>var worker = new Worker("'+resource+'?res=worker&redir=other&id=worker-redir");</script>');
+    return;
+  }
+
   // script that XHR's to a resource that redirects to another site
   if (query["testid"] == "xhr-src") {
     response.write('<script src="'+resource+'?res=xhr"></script>');
     return;
   }
 
   // for bug949706
   if (query["testid"] == "img-src-from-css") {
--- a/dom/security/test/csp/file_redirects_resource.sjs
+++ b/dom/security/test/csp/file_redirects_resource.sjs
@@ -80,16 +80,23 @@ function handleRequest(request, response
 
   // external stylesheet
   if (query["res"] == "style") {
     response.setHeader("Content-Type", "text/css", false);
     response.write("css data...");
     return;
   }
 
+  // web worker resource
+  if (query["res"] == "worker") {
+    response.setHeader("Content-Type", "application/javascript", false);
+    response.write("worker script data...");
+    return;
+  }
+
   // internal stylesheet that loads an image from an external site
   if (query["res"] == "cssLoader") {
     let bgURL = thisSite + resource + '?redir=other&res=image&id=' + query["id"];
     response.setHeader("Content-Type", "text/css", false);
     response.write("body { background:url('" + bgURL + "'); }");
     return;
   }
 
--- a/dom/security/test/csp/test_redirects.html
+++ b/dom/security/test/csp/test_redirects.html
@@ -77,22 +77,24 @@ var testExpectedResults = { "font-src": 
                             "media-src": true,
                             "media-src-redir": false,
                             "object-src": true,
                             "object-src-redir": false,
                             "script-src": true,
                             "script-src-redir": false,
                             "style-src": true,
                             "style-src-redir": false,
+                            "worker": true,
+                            "worker-redir": false,
                             "xhr-src": true,
                             "xhr-src-redir": false,
                             "from-worker": true,
-                            "script-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
-                            "xhr-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
-                            "fetch-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
+                            "script-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
+                            "xhr-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
+                            "fetch-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
                             "from-blob-worker": true,
                             "script-src-redir-from-blob-worker": false,
                             "xhr-src-redir-from-blob-worker": false,
                             "fetch-src-redir-from-blob-worker": false,
                             "img-src-from-css": true,
                             "img-src-redir-from-css": false,
                           };
 
--- a/dom/tests/mochitest/fetch/test_fetch_cors.js
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.js
@@ -1278,42 +1278,27 @@ function testCORSRedirects() {
              headers: { "Content-Type": "text/plain" },
              hops: [{ server: "http://mochi.test:8888",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
                     },
                     ],
            },
-           { pass: 1,
-             method: "POST",
-             body: "hi there",
-             headers: { "Content-Type": "text/plain",
-                        "my-header": "myValue",
-                      },
-             hops: [{ server: "http://mochi.test:8888",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowHeaders: "my-header",
-                    },
-                    ],
-           },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
                       },
              hops: [{ server: "http://mochi.test:8888",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
                       allowHeaders: "my-header",
-                      noAllowPreflight: 1,
                     },
                     ],
            },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
@@ -1325,48 +1310,34 @@ function testCORSRedirects() {
                       allowHeaders: "my-header",
                     },
                     { server: "http://test2.example.com",
                       allowOrigin: origin,
                       allowHeaders: "my-header",
                     }
                     ],
            },
-           { pass: 1,
-             method: "DELETE",
-             hops: [{ server: "http://mochi.test:8888",
-                    },
-                    { server: "http://example.com",
-                      allowOrigin: origin,
-                      allowMethods: "DELETE",
-                    },
-                    ],
-           },
            { pass: 0,
              method: "DELETE",
              hops: [{ server: "http://mochi.test:8888",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
-                      allowMethods: "DELETE",
-                      noAllowPreflight: 1,
                     },
                     ],
            },
            { pass: 0,
              method: "DELETE",
              hops: [{ server: "http://mochi.test:8888",
                     },
                     { server: "http://test1.example.com",
                       allowOrigin: origin,
-                      allowMethods: "DELETE",
                     },
                     { server: "http://test2.example.com",
                       allowOrigin: origin,
-                      allowMethods: "DELETE",
                     },
                     ],
            },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
@@ -1378,21 +1349,19 @@ function testCORSRedirects() {
                       allowOrigin: origin,
                     },
                     ],
            },
            { pass: 0,
              method: "DELETE",
              hops: [{ server: "http://example.com",
                       allowOrigin: origin,
-                      allowMethods: "DELETE",
                     },
                     { server: "http://sub1.test1.mochi.test:8888",
                       allowOrigin: origin,
-                      allowMethods: "DELETE",
                     },
                     ],
            },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -237,31 +237,27 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
   }
 
   *aOptionalLoadInfoArgs =
     LoadInfoArgs(
       requestingPrincipalInfo,
       triggeringPrincipalInfo,
       aLoadInfo->GetSecurityFlags(),
       aLoadInfo->InternalContentPolicyType(),
-      static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetUpgradeInsecurePreloads(),
       aLoadInfo->GetInnerWindowID(),
       aLoadInfo->GetOuterWindowID(),
       aLoadInfo->GetParentOuterWindowID(),
       aLoadInfo->GetEnforceSecurity(),
       aLoadInfo->GetInitialSecurityCheckDone(),
       aLoadInfo->GetIsInThirdPartyContext(),
       aLoadInfo->GetOriginAttributes(),
       redirectChainIncludingInternalRedirects,
-      redirectChain,
-      aLoadInfo->CorsUnsafeHeaders(),
-      aLoadInfo->GetForcePreflight(),
-      aLoadInfo->GetIsPreflight());
+      redirectChain);
 
   return NS_OK;
 }
 
 nsresult
 LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
                        nsILoadInfo** outLoadInfo)
 {
@@ -297,30 +293,26 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
     redirectChain.AppendElement(redirectedPrincipal.forget());
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(requestingPrincipal,
                           triggeringPrincipal,
                           loadInfoArgs.securityFlags(),
                           loadInfoArgs.contentPolicyType(),
-                          static_cast<LoadTainting>(loadInfoArgs.tainting()),
                           loadInfoArgs.upgradeInsecureRequests(),
                           loadInfoArgs.upgradeInsecurePreloads(),
                           loadInfoArgs.innerWindowID(),
                           loadInfoArgs.outerWindowID(),
                           loadInfoArgs.parentOuterWindowID(),
                           loadInfoArgs.enforceSecurity(),
                           loadInfoArgs.initialSecurityCheckDone(),
                           loadInfoArgs.isInThirdPartyContext(),
                           loadInfoArgs.originAttributes(),
                           redirectChainIncludingInternalRedirects,
-                          redirectChain,
-                          loadInfoArgs.corsUnsafeHeaders(),
-                          loadInfoArgs.forcePreflight(),
-                          loadInfoArgs.isPreflight());
+                          redirectChain);
 
    loadInfo.forget(outLoadInfo);
    return NS_OK;
 }
 
 } // namespace ipc
 } // namespace mozilla
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -1280,16 +1280,17 @@ static const mozilla::Module::ContractID
   { CSPSERVICE_CONTRACTID, &kCSPSERVICE_CID },
   { NS_CSPCONTEXT_CONTRACTID, &kNS_CSPCONTEXT_CID },
   { NS_MIXEDCONTENTBLOCKER_CONTRACTID, &kNS_MIXEDCONTENTBLOCKER_CID },
   { NS_EVENTLISTENERSERVICE_CONTRACTID, &kNS_EVENTLISTENERSERVICE_CID },
   { NS_GLOBALMESSAGEMANAGER_CONTRACTID, &kNS_GLOBALMESSAGEMANAGER_CID },
   { NS_PARENTPROCESSMESSAGEMANAGER_CONTRACTID, &kNS_PARENTPROCESSMESSAGEMANAGER_CID },
   { NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID, &kNS_CHILDPROCESSMESSAGEMANAGER_CID },
   { NS_SCRIPTSECURITYMANAGER_CONTRACTID, &kNS_SCRIPTSECURITYMANAGER_CID },
+  { NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID, &kNS_SCRIPTSECURITYMANAGER_CID },
   { NS_PRINCIPAL_CONTRACTID, &kNS_PRINCIPAL_CID },
   { NS_SYSTEMPRINCIPAL_CONTRACTID, &kNS_SYSTEMPRINCIPAL_CID },
   { NS_NULLPRINCIPAL_CONTRACTID, &kNS_NULLPRINCIPAL_CID },
   { NS_DEVICE_SENSORS_CONTRACTID, &kNS_DEVICE_SENSORS_CID },
 #ifndef MOZ_WIDGET_GONK
 #if defined(ANDROID)
   { "@mozilla.org/widget/hapticfeedback;1", &kNS_HAPTICFEEDBACK_CID },
 #endif
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -37,18 +37,16 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mUpgradeInsecureRequests(false)
   , mUpgradeInsecurePreloads(false)
   , mInnerWindowID(0)
   , mOuterWindowID(0)
   , mParentOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(true)
-  , mForcePreflight(false)
-  , mIsPreflight(false)
 {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   // if consumers pass both, aLoadingContext and aLoadingPrincipal
   // then the loadingPrincipal must be the same as the node's principal
   MOZ_ASSERT(!aLoadingContext || !aLoadingPrincipal ||
              aLoadingContext->NodePrincipal() == aLoadingPrincipal);
@@ -108,58 +106,48 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mParentOuterWindowID(rhs.mParentOuterWindowID)
   , mEnforceSecurity(rhs.mEnforceSecurity)
   , mInitialSecurityCheckDone(rhs.mInitialSecurityCheckDone)
   , mIsThirdPartyContext(rhs.mIsThirdPartyContext)
   , mOriginAttributes(rhs.mOriginAttributes)
   , mRedirectChainIncludingInternalRedirects(
       rhs.mRedirectChainIncludingInternalRedirects)
   , mRedirectChain(rhs.mRedirectChain)
-  , mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders)
-  , mForcePreflight(rhs.mForcePreflight)
-  , mIsPreflight(rhs.mIsPreflight)
 {
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsSecurityFlags aSecurityFlags,
                    nsContentPolicyType aContentPolicyType,
-                   LoadTainting aTainting,
                    bool aUpgradeInsecureRequests,
                    bool aUpgradeInsecurePreloads,
                    uint64_t aInnerWindowID,
                    uint64_t aOuterWindowID,
                    uint64_t aParentOuterWindowID,
                    bool aEnforceSecurity,
                    bool aInitialSecurityCheckDone,
                    bool aIsThirdPartyContext,
                    const NeckoOriginAttributes& aOriginAttributes,
                    nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
-                   nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain,
-                   const nsTArray<nsCString>& aCorsUnsafeHeaders,
-                   bool aForcePreflight,
-                   bool aIsPreflight)
+                   nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
-  , mTainting(aTainting)
+  , mTainting(LoadTainting::Basic)
   , mUpgradeInsecureRequests(aUpgradeInsecureRequests)
   , mUpgradeInsecurePreloads(aUpgradeInsecurePreloads)
   , mInnerWindowID(aInnerWindowID)
   , mOuterWindowID(aOuterWindowID)
   , mParentOuterWindowID(aParentOuterWindowID)
   , mEnforceSecurity(aEnforceSecurity)
   , mInitialSecurityCheckDone(aInitialSecurityCheckDone)
   , mIsThirdPartyContext(aIsThirdPartyContext)
   , mOriginAttributes(aOriginAttributes)
-  , mCorsUnsafeHeaders(aCorsUnsafeHeaders)
-  , mForcePreflight(aForcePreflight)
-  , mIsPreflight(aIsPreflight)
 {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   mRedirectChainIncludingInternalRedirects.SwapElements(
     aRedirectChainIncludingInternalRedirects);
 
   mRedirectChain.SwapElements(aRedirectChain);
@@ -264,16 +252,24 @@ LoadInfo::LoadingNode()
 
 NS_IMETHODIMP
 LoadInfo::GetSecurityFlags(nsSecurityFlags* aResult)
 {
   *aResult = mSecurityFlags;
   return NS_OK;
 }
 
+void
+LoadInfo::SetWithCredentialsSecFlag()
+{
+  MOZ_ASSERT(!mEnforceSecurity,
+             "Request should not have been opened yet");
+  mSecurityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
+}
+
 NS_IMETHODIMP
 LoadInfo::GetSecurityMode(uint32_t* aFlags)
 {
   *aFlags = (mSecurityFlags &
               (nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS |
                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED |
                nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
                nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL |
@@ -283,46 +279,24 @@ LoadInfo::GetSecurityMode(uint32_t* aFla
 
 NS_IMETHODIMP
 LoadInfo::GetIsInThirdPartyContext(bool* aIsInThirdPartyContext)
 {
   *aIsInThirdPartyContext = mIsThirdPartyContext;
   return NS_OK;
 }
 
-static const uint32_t sCookiePolicyMask =
-  nsILoadInfo::SEC_COOKIES_DEFAULT |
-  nsILoadInfo::SEC_COOKIES_INCLUDE |
-  nsILoadInfo::SEC_COOKIES_SAME_ORIGIN |
-  nsILoadInfo::SEC_COOKIES_OMIT;
-
 NS_IMETHODIMP
-LoadInfo::GetCookiePolicy(uint32_t *aResult)
+LoadInfo::GetRequireCorsWithCredentials(bool* aResult)
 {
-  uint32_t policy = mSecurityFlags & sCookiePolicyMask;
-  if (policy == nsILoadInfo::SEC_COOKIES_DEFAULT) {
-    policy = (mSecurityFlags & SEC_REQUIRE_CORS_DATA_INHERITS) ?
-      nsILoadInfo::SEC_COOKIES_SAME_ORIGIN : nsILoadInfo::SEC_COOKIES_INCLUDE;
-  }
-
-  *aResult = policy;
+  *aResult =
+    (mSecurityFlags & nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS);
   return NS_OK;
 }
 
-void
-LoadInfo::SetIncludeCookiesSecFlag()
-{
-  MOZ_ASSERT(!mEnforceSecurity,
-             "Request should not have been opened yet");
-  MOZ_ASSERT((mSecurityFlags & sCookiePolicyMask) ==
-             nsILoadInfo::SEC_COOKIES_DEFAULT);
-  mSecurityFlags = (mSecurityFlags & ~sCookiePolicyMask) |
-                   nsILoadInfo::SEC_COOKIES_INCLUDE;
-}
-
 NS_IMETHODIMP
 LoadInfo::GetForceInheritPrincipal(bool* aInheritPrincipal)
 {
   *aInheritPrincipal =
     (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL);
   return NS_OK;
 }
 
@@ -518,54 +492,16 @@ LoadInfo::GetRedirectChain(JSContext* aC
 }
 
 const nsTArray<nsCOMPtr<nsIPrincipal>>&
 LoadInfo::RedirectChain()
 {
   return mRedirectChain;
 }
 
-void
-LoadInfo::SetCorsPreflightInfo(const nsTArray<nsCString>& aHeaders,
-                               bool aForcePreflight)
-{
-  MOZ_ASSERT(GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS);
-  MOZ_ASSERT(!mInitialSecurityCheckDone);
-  mCorsUnsafeHeaders = aHeaders;
-  mForcePreflight = aForcePreflight;
-}
-
-const nsTArray<nsCString>&
-LoadInfo::CorsUnsafeHeaders()
-{
-  return mCorsUnsafeHeaders;
-}
-
-NS_IMETHODIMP
-LoadInfo::GetForcePreflight(bool* aForcePreflight)
-{
-  *aForcePreflight = mForcePreflight;
-  return NS_OK;
-}
-
-void
-LoadInfo::SetIsPreflight()
-{
-  MOZ_ASSERT(GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS);
-  MOZ_ASSERT(!mInitialSecurityCheckDone);
-  mIsPreflight = true;
-}
-
-NS_IMETHODIMP
-LoadInfo::GetIsPreflight(bool* aIsPreflight)
-{
-  *aIsPreflight = mIsPreflight;
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 LoadInfo::GetTainting(uint32_t* aTaintingOut)
 {
   MOZ_ASSERT(aTaintingOut);
   *aTaintingOut = static_cast<uint32_t>(mTainting);
   return NS_OK;
 }
 
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -56,57 +56,51 @@ public:
 
   // create an exact copy of the loadinfo
   already_AddRefed<nsILoadInfo> Clone() const;
   // creates a copy of the loadinfo which is appropriate to use for a
   // separate request. I.e. not for a redirect or an inner channel, but
   // when a separate request is made with the same security properties.
   already_AddRefed<nsILoadInfo> CloneForNewRequest() const;
 
-  void SetIsPreflight();
-
 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,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
-           LoadTainting aTainting,
            bool aUpgradeInsecureRequests,
            bool aUpgradeInsecurePreloads,
            uint64_t aInnerWindowID,
            uint64_t aOuterWindowID,
            uint64_t aParentOuterWindowID,
            bool aEnforceSecurity,
            bool aInitialSecurityCheckDone,
            bool aIsThirdPartyRequest,
            const NeckoOriginAttributes& aOriginAttributes,
            nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
-           nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain,
-           const nsTArray<nsCString>& aUnsafeHeaders,
-           bool aForcePreflight,
-           bool aIsPreflight);
+           nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain);
   LoadInfo(const LoadInfo& rhs);
 
   friend nsresult
   mozilla::ipc::LoadInfoArgsToLoadInfo(
     const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
     nsILoadInfo** outLoadInfo);
 
   ~LoadInfo();
 
   void ComputeIsThirdPartyContext(nsPIDOMWindow* aOuterWindow);
 
   // This function is the *only* function which can change the securityflags
   // of a loadinfo. It only exists because of the XHR code. Don't call it
   // from anywhere else!
-  void SetIncludeCookiesSecFlag();
+  void SetWithCredentialsSecFlag();
   friend class ::nsXMLHttpRequest;
 
   // if you add a member, please also update the copy constructor
   nsCOMPtr<nsIPrincipal>           mLoadingPrincipal;
   nsCOMPtr<nsIPrincipal>           mTriggeringPrincipal;
   nsWeakPtr                        mLoadingContext;
   nsSecurityFlags                  mSecurityFlags;
   nsContentPolicyType              mInternalContentPolicyType;
@@ -117,17 +111,14 @@ private:
   uint64_t                         mOuterWindowID;
   uint64_t                         mParentOuterWindowID;
   bool                             mEnforceSecurity;
   bool                             mInitialSecurityCheckDone;
   bool                             mIsThirdPartyContext;
   NeckoOriginAttributes            mOriginAttributes;
   nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChainIncludingInternalRedirects;
   nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChain;
-  nsTArray<nsCString>              mCorsUnsafeHeaders;
-  bool                             mForcePreflight;
-  bool                             mIsPreflight;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_LoadInfo_h
 
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -10,31 +10,28 @@
 interface nsIDOMDocument;
 interface nsINode;
 interface nsIPrincipal;
 
 %{C++
 #include "nsTArray.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/LoadTainting.h"
-
-class nsCString;
 %}
 
 [ref] native const_nsIPrincipalArray(const nsTArray<nsCOMPtr<nsIPrincipal>>);
 native NeckoOriginAttributes(mozilla::NeckoOriginAttributes);
 [ref] native const_OriginAttributesRef(const mozilla::NeckoOriginAttributes);
-[ref] native StringArrayRef(const nsTArray<nsCString>);
 
 typedef unsigned long nsSecurityFlags;
 
 /**
  * An nsILoadOwner represents per-load information about who started the load.
  */
-[scriptable, builtinclass, uuid(41e311d0-5894-4aaa-80b5-5b7099dfc404)]
+[scriptable, builtinclass, uuid(5a2dce9f-accd-45ae-98cc-319dec0ae4f0)]
 interface nsILoadInfo : nsISupports
 {
   /**
    * No special security flags:
    */
   const unsigned long SEC_NORMAL = 0;
 
   /**
@@ -84,83 +81,73 @@ interface nsILoadInfo : nsISupports
    * loads. Loads from data: are allowed and the result will inherit
    * the principal of the origin that triggered the load.
    * Commonly used by <img crossorigin>, <video crossorigin>,
    * XHR, fetch(), etc.
    */
   const unsigned long SEC_REQUIRE_CORS_DATA_INHERITS = (1<<4);
 
   /**
-   * Choose cookie policy. The default policy is equivalent to "INCLUDE" for
-   * SEC_REQUIRE_SAME_ORIGIN_* and SEC_ALLOW_CROSS_ORIGIN_* modes, and
-   * equivalent to "SAME_ORIGIN" for SEC_REQUIRE_CORS_DATA_INHERITS mode.
-   *
-   * This means that if you want to perform a CORS load with credentials, pass
-   * SEC_COOKIES_INCLUDE.
-   *
-   * Note that these flags are still subject to the user's cookie policies.
-   * For example, if the user is blocking 3rd party cookies, those cookies
-   * will be blocked no matter which of these flags are set.
+   * Use this flag in addition to SEC_REQUIRE_CORS_DATA_INHERITS
+   * to make cross-origin CORS loads happen with credentials
+   * (such as cookies and client side certs).
    */
-  const unsigned long SEC_COOKIES_DEFAULT = (0 << 5);
-  const unsigned long SEC_COOKIES_INCLUDE = (1 << 5);
-  const unsigned long SEC_COOKIES_SAME_ORIGIN = (2 << 5);
-  const unsigned long SEC_COOKIES_OMIT = (3 << 5);
+  const unsigned long SEC_REQUIRE_CORS_WITH_CREDENTIALS = (1<<5);
 
   /**
    * Force inheriting of the Principal. The resulting resource will use the
    * principal of the document which is doing the load. Setting this flag
    * will cause GetChannelResultPrincipal to return the same principal as
    * the loading principal that's passed in when creating the channel.
    *
    * This will happen independently of the scheme of the URI that the
    * channel is loading.
    *
    * So if the loading document comes from "http://a.com/", and the channel
    * is loading the URI "http://b.com/whatever", GetChannelResultPrincipal
    * will return a principal from "http://a.com/".
    *
    * This flag can not be used together with SEC_SANDBOXED.
    */
-  const unsigned long SEC_FORCE_INHERIT_PRINCIPAL = (1<<7);
+  const unsigned long SEC_FORCE_INHERIT_PRINCIPAL = (1<<6);
 
   /**
    * Sandbox the load. The resulting resource will use a freshly created
    * null principal. So GetChannelResultPrincipal will always return a
    * null principal whenever this flag is set.
    *
    * This will happen independently of the scheme of the URI that the
    * channel is loading.
    *
    * This flag can not be used together with SEC_FORCE_INHERIT_PRINCIPAL.
    */
-  const unsigned long SEC_SANDBOXED = (1<<8);
+  const unsigned long SEC_SANDBOXED = (1<<7);
 
   /**
    * Inherit the Principal for about:blank.
    */
-  const unsigned long SEC_ABOUT_BLANK_INHERITS = (1<<9);
+  const unsigned long SEC_ABOUT_BLANK_INHERITS = (1<<8);
 
   /**
    * Allow access to chrome: packages that are content accessible.
    */
-  const unsigned long SEC_ALLOW_CHROME = (1<<10);
+  const unsigned long SEC_ALLOW_CHROME = (1<<9);
 
   /**
    * Don't follow redirects. Instead the redirect response is returned
    * as a successful response for the channel.
    *
    * Redirects not initiated by a server response, i.e. REDIRECT_INTERNAL and
    * REDIRECT_STS_UPGRADE, are still followed.
    *
    * Note: If this flag is set and the channel response is a redirect, then
    * the response body might not be available.
    * This can happen if the redirect was cached.
    */
-  const unsigned long SEC_DONT_FOLLOW_REDIRECTS = (1<<11);
+  const unsigned long SEC_DONT_FOLLOW_REDIRECTS = (1<<10);
 
   /**
    * The loadingPrincipal is the principal that is responsible for the load.
    * It is *NOT* the principal tied to the resource/URI that this
    * channel is loading, it's the principal of the resource's
    * caller or requester. For example, if this channel is loading
    * an image from http://b.com that is embedded in a document
    * who's origin is http://a.com, the loadingPrincipal is http://a.com.
@@ -249,22 +236,20 @@ interface nsILoadInfo : nsISupports
    * True if this request is embedded in a context that can't be third-party
    * (i.e. an iframe embedded in a cross-origin parent window). If this is
    * false, then this request may be third-party if it's a third-party to
    * loadingPrincipal.
    */
   [infallible] readonly attribute boolean isInThirdPartyContext;
 
   /**
-   * See the SEC_COOKIES_* flags above. This attribute will never return
-   * SEC_COOKIES_DEFAULT, but will instead return what the policy resolves to.
-   * I.e. SEC_COOKIES_SAME_ORIGIN for CORS mode, and SEC_COOKIES_INCLUDE
-   * otherwise.
+   * Determines whether credentials are sent with CORS requests.
+   * Using this flag requires SEC_REQUIRE_CORS_DATA_INHERITS also to be set.
    */
-  [infallible] readonly attribute unsigned long cookiePolicy;
+  [infallible] readonly attribute boolean requireCorsWithCredentials;
 
   /**
    * If forceInheritPrincipal is true, the data coming from the channel should
    * use loadingPrincipal for its principal, even when the data is loaded over
    * http:// or another protocol that would normally use a URI-based principal.
    * This attribute will never be true when loadingSandboxed is true.
    */
   [infallible] readonly attribute boolean forceInheritPrincipal;
@@ -450,46 +435,16 @@ interface nsILoadInfo : nsISupports
    * A C++-friendly version of redirectChain.
    * Please note that this array has the same lifetime as the
    * loadInfo object - use with caution!
    */
   [noscript, notxpcom, nostdcall, binaryname(RedirectChain)]
   const_nsIPrincipalArray binaryRedirectChain();
 
   /**
-   * Sets the list of unsafe headers according to CORS spec, as well as
-   * potentially forces a preflight.
-   * Note that you do not need to set the Content-Type header. That will be
-   * automatically detected as needed.
-   *
-   * Only call this function when using the SEC_REQUIRE_CORS_DATA_INHERITS mode.
-   */
-  [noscript, notxpcom, nostdcall]
-  void setCorsPreflightInfo(in StringArrayRef unsafeHeaders,
-                            in boolean forcePreflight);
-
-  /**
-   * A C++-friendly getter for the list of cors-unsafe headers.
-   * Please note that this array has the same lifetime as the
-   * loadInfo object - use with caution!
-   */
-  [noscript, notxpcom, nostdcall, binaryname(CorsUnsafeHeaders)]
-  StringArrayRef corsUnsafeHeaders();
-
-  /**
-   * Returns value set through setCorsPreflightInfo.
-   */
-  [infallible] readonly attribute boolean forcePreflight;
-
-  /**
-   * A C++ friendly getter for the forcePreflight flag.
-   */
-  [infallible] readonly attribute boolean isPreflight;
-
-  /**
   * Constants reflecting the channel tainting.  These are mainly defined here
   * for script.  Internal C++ code should use the enum defined in LoadTainting.h.
   * See LoadTainting.h for documentation.
   */
   const unsigned long TAINTING_BASIC = 0;
   const unsigned long TAINTING_CORS = 1;
   const unsigned long TAINTING_OPAQUE = 2;
 
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -45,17 +45,16 @@
 #include "mozilla/LoadInfo.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/net/DNS.h"
 #include "CaptivePortalService.h"
 #include "ClosingService.h"
 #include "ReferrerPolicy.h"
-#include "nsContentSecurityManager.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsINetworkManager.h"
 #include "nsINetworkInterface.h"
 #endif
 
 #if defined(XP_WIN)
 #include "nsNativeConnectionHelper.h"
@@ -411,21 +410,18 @@ nsresult
 nsIOService::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
                                     uint32_t flags,
                                     nsAsyncRedirectVerifyHelper *helper)
 {
     // If a redirect to a local network address occurs, then chances are we
     // are in a captive portal, so we trigger a recheck.
     RecheckCaptivePortalIfLocalRedirect(newChan);
 
-    // This is silly. I wish there was a simpler way to get at the global
-    // reference of the contentSecurityManager. But it lives in the XPCOM
-    // service registry.
     nsCOMPtr<nsIChannelEventSink> sink =
-        do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
+        do_GetService(NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID);
     if (sink) {
         nsresult rv = helper->DelegateOnChannelRedirect(sink, oldChan,
                                                         newChan, flags);
         if (NS_FAILED(rv))
             return rv;
     }
 
     // Finally, our category
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -1247,65 +1247,16 @@ NS_GetAppInfo(nsIChannel *aChannel,
     NS_ENSURE_SUCCESS(rv, false);
 
     rv = loadContext->GetIsInBrowserElement(aIsInBrowserElement);
     NS_ENSURE_SUCCESS(rv, false);
 
     return true;
 }
 
-bool
-NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport)
-{
-  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
-  MOZ_RELEASE_ASSERT(loadInfo, "Origin tracking only works for channels created with a loadinfo");
-
-  // Always treat tainted channels as cross-origin.
-  if (loadInfo->GetTainting() != LoadTainting::Basic) {
-    return true;
-  }
-
-  nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->LoadingPrincipal();
-  uint32_t mode = loadInfo->GetSecurityMode();
-  bool dataInherits =
-    mode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
-    mode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
-    mode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
-
-  bool aboutBlankInherits = dataInherits && loadInfo->GetAboutBlankInherits();
-
-  for (nsIPrincipal* principal : loadInfo->RedirectChain()) {
-    nsCOMPtr<nsIURI> uri;
-    principal->GetURI(getter_AddRefs(uri));
-    if (!uri) {
-      return true;
-    }
-
-    if (aboutBlankInherits && NS_IsAboutBlank(uri)) {
-      continue;
-    }
-
-    if (NS_FAILED(loadingPrincipal->CheckMayLoad(uri, aReport, dataInherits))) {
-      return true;
-    }
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
-  if (!uri) {
-    return true;
-  }
-
-  if (aboutBlankInherits && NS_IsAboutBlank(uri)) {
-    return false;
-  }
-
-  return NS_FAILED(loadingPrincipal->CheckMayLoad(uri, aReport, dataInherits));
-}
-
 nsresult
 NS_GetAppInfoFromClearDataNotification(nsISupports *aSubject,
                                        uint32_t *aAppID,
                                        bool *aBrowserOnly)
 {
     nsresult rv;
 
     nsCOMPtr<mozIApplicationClearPrivateDataParams>
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -686,22 +686,16 @@ NS_QueryNotificationCallbacks(nsIInterfa
 bool NS_UsePrivateBrowsing(nsIChannel *channel);
 
 /**
  * Extract the NeckoOriginAttributes from the channel's triggering principal.
  */
 bool NS_GetOriginAttributes(nsIChannel *aChannel,
                             mozilla::NeckoOriginAttributes &aAttributes);
 
-/**
- * Returns true if the channel has visited any cross-origin URLs on any
- * URLs that it was redirected through.
- */
-bool NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport = false);
-
 // Constants duplicated from nsIScriptSecurityManager so we avoid having necko
 // know about script security manager.
 #define NECKO_NO_APP_ID 0
 #define NECKO_UNKNOWN_APP_ID UINT32_MAX
 // special app id reserved for separating the safebrowsing cookie
 #define NECKO_SAFEBROWSING_APP_ID UINT32_MAX - 1
 
 /**
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -1000,16 +1000,25 @@
     { 0x86, 0xf8, 0x63, 0xf2, 0x25, 0xb9, 0x40, 0xae } \
   }
 
 /******************************************************************************
  * Contracts that can be implemented by necko users.
  */
 
 /**
+ * This contract ID will be gotten as a service and gets the opportunity to look
+ * at and veto all redirects that are processed by necko.
+ *
+ * Must implement nsIChannelEventSink
+ */
+#define NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID \
+  "@mozilla.org/netwerk/global-channel-event-sink;1"
+
+/**
  * This contract ID will be gotten as a service implementing nsINetworkLinkService
  * and monitored by IOService for automatic online/offline management.
  *
  * Must implement nsINetworkLinkService
  */
 #define NS_NETWORK_LINK_SERVICE_CONTRACTID \
   "@mozilla.org/network/network-link-service;1"
 
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -26,31 +26,27 @@ namespace net {
 //-----------------------------------------------------------------------------
 
 struct LoadInfoArgs
 {
   PrincipalInfo    requestingPrincipalInfo;
   PrincipalInfo    triggeringPrincipalInfo;
   uint32_t         securityFlags;
   uint32_t         contentPolicyType;
-  uint32_t         tainting;
   bool             upgradeInsecureRequests;
   bool             upgradeInsecurePreloads;
   uint64_t         innerWindowID;
   uint64_t         outerWindowID;
   uint64_t         parentOuterWindowID;
   bool             enforceSecurity;
   bool             initialSecurityCheckDone;
   bool             isInThirdPartyContext;
   NeckoOriginAttributes originAttributes;
   PrincipalInfo[]  redirectChainIncludingInternalRedirects;
   PrincipalInfo[]  redirectChain;
-  nsCString[]      corsUnsafeHeaders;
-  bool             forcePreflight;
-  bool             isPreflight;
 };
 
 /**
  * Not every channel necessarily has a loadInfo attached.
  */
 union OptionalLoadInfoArgs
 {
   void_t;
@@ -64,16 +60,18 @@ union OptionalLoadInfoArgs
 union OptionalHttpResponseHead
 {
   void_t;
   nsHttpResponseHead;
 };
 
 struct CorsPreflightArgs
 {
+  bool          withCredentials;
+  PrincipalInfo preflightPrincipal;
   nsCString[]   unsafeHeaders;
 };
 
 union OptionalCorsPreflightArgs
 {
   void_t;
   CorsPreflightArgs;
 };
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -94,16 +94,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mCorsIncludeCredentials(false)
   , mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS)
   , mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW)
   , mOnStartRequestCalled(false)
   , mTransferSize(0)
   , mDecodedBodySize(0)
   , mEncodedBodySize(0)
   , mRequireCORSPreflight(false)
+  , mWithCredentials(false)
   , mReportCollector(new ConsoleReportCollector())
   , mForceMainDocumentChannel(false)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // Subfields of unions cannot be targeted in an initializer list.
 #ifdef MOZ_VALGRIND
   // Zero the entire unions so that Valgrind doesn't complain when we send them
@@ -2604,17 +2605,21 @@ HttpBaseChannel::SetupReplacementChannel
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   if (!httpChannel)
     return NS_OK; // no other options to set
 
   // Preserve the CORS preflight information.
   nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
   if (mRequireCORSPreflight && httpInternal) {
-    httpInternal->SetCorsPreflightParameters(mUnsafeHeaders);
+    rv = httpInternal->SetCorsPreflightParameters(mUnsafeHeaders, mWithCredentials,
+                                                  mPreflightPrincipal);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   if (preserveMethod) {
     nsCOMPtr<nsIUploadChannel> uploadChannel =
       do_QueryInterface(httpChannel);
     nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
       do_QueryInterface(httpChannel);
     if (mUploadStream && (uploadChannel2 || uploadChannel)) {
@@ -3097,20 +3102,25 @@ HttpBaseChannel::EnsureSchedulingContext
         return false;
     }
 
     // Set the load group connection scope on the transaction
     rootLoadGroup->GetSchedulingContextID(&mSchedulingContextID);
     return true;
 }
 
-void
-HttpBaseChannel::SetCorsPreflightParameters(const nsTArray<nsCString>& aUnsafeHeaders)
+NS_IMETHODIMP
+HttpBaseChannel::SetCorsPreflightParameters(const nsTArray<nsCString>& aUnsafeHeaders,
+                                            bool aWithCredentials,
+                                            nsIPrincipal* aPrincipal)
 {
-  MOZ_RELEASE_ASSERT(!mRequestObserversCalled);
+  ENSURE_CALLED_BEFORE_CONNECT();
 
   mRequireCORSPreflight = true;
   mUnsafeHeaders = aUnsafeHeaders;
+  mWithCredentials = aWithCredentials;
+  mPreflightPrincipal = aPrincipal;
+  return NS_OK;
 }
 
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -208,17 +208,19 @@ public:
   NS_IMETHOD GetCorsIncludeCredentials(bool* aInclude) override;
   NS_IMETHOD SetCorsIncludeCredentials(bool aInclude) override;
   NS_IMETHOD GetCorsMode(uint32_t* aCorsMode) override;
   NS_IMETHOD SetCorsMode(uint32_t aCorsMode) override;
   NS_IMETHOD GetRedirectMode(uint32_t* aRedirectMode) override;
   NS_IMETHOD SetRedirectMode(uint32_t aRedirectMode) override;
   NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) override;
   NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override;
-  virtual void SetCorsPreflightParameters(const nsTArray<nsCString>& unsafeHeaders) override;
+  NS_IMETHOD SetCorsPreflightParameters(const nsTArray<nsCString>& unsafeHeaders,
+                                        bool aWithCredentials,
+                                        nsIPrincipal* aPrincipal) override;
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener) override;
 
@@ -485,17 +487,19 @@ protected:
 
   // The network interface id that's associated with this channel.
   nsCString mNetworkInterfaceId;
 
   nsID mSchedulingContextID;
   bool EnsureSchedulingContextID();
 
   bool                              mRequireCORSPreflight;
+  bool                              mWithCredentials;
   nsTArray<nsCString>               mUnsafeHeaders;
+  nsCOMPtr<nsIPrincipal>            mPreflightPrincipal;
 
   nsCOMPtr<nsIConsoleReportCollector> mReportCollector;
 
   bool mForceMainDocumentChannel;
 };
 
 // Share some code while working around C++'s absurd inability to handle casting
 // of member functions between base/derived types.
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1532,24 +1532,22 @@ HttpChannelChild::OnRedirectVerifyCallba
                                                  streamListener, mSynthesizedInput,
                                                  mResponseHead));
     return NS_OK;
   }
 
   RequestHeaderTuples emptyHeaders;
   RequestHeaderTuples* headerTuples = &emptyHeaders;
   nsLoadFlags loadFlags = 0;
-  OptionalCorsPreflightArgs corsPreflightArgs = mozilla::void_t();
 
   nsCOMPtr<nsIHttpChannelChild> newHttpChannelChild =
       do_QueryInterface(mRedirectChannelChild);
   if (newHttpChannelChild && NS_SUCCEEDED(result)) {
     newHttpChannelChild->AddCookiesToRequest();
     newHttpChannelChild->GetClientSetRequestHeaders(&headerTuples);
-    newHttpChannelChild->GetClientSetCorsPreflightParameters(corsPreflightArgs);
   }
 
   /* If the redirect was canceled, bypass OMR and send an empty API
    * redirect URI */
   SerializeURI(nullptr, redirectURI);
 
   if (NS_SUCCEEDED(result)) {
     // Note: this is where we would notify "http-on-modify-response" observers.
@@ -1574,18 +1572,17 @@ HttpChannelChild::OnRedirectVerifyCallba
 
     nsCOMPtr<nsIRequest> request = do_QueryInterface(mRedirectChannelChild);
     if (request) {
       request->GetLoadFlags(&loadFlags);
     }
   }
 
   if (mIPCOpen)
-    SendRedirect2Verify(result, *headerTuples, loadFlags, redirectURI,
-                        corsPreflightArgs);
+    SendRedirect2Verify(result, *headerTuples, loadFlags, redirectURI);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIRequest
 //-----------------------------------------------------------------------------
 
@@ -1844,17 +1841,28 @@ HttpChannelChild::ContinueAsyncOpen()
     for (uint32_t i = 1; i < fds.Length(); ++i) {
       Unused << fdSet->SendAddFileDescriptor(fds[i]);
     }
 
     optionalFDs = fdSet;
   }
 
   OptionalCorsPreflightArgs optionalCorsPreflightArgs;
-  GetClientSetCorsPreflightParameters(optionalCorsPreflightArgs);
+  if (mRequireCORSPreflight) {
+    CorsPreflightArgs args;
+    args.withCredentials() = mWithCredentials;
+    args.unsafeHeaders() = mUnsafeHeaders;
+    nsresult rv = PrincipalToPrincipalInfo(mPreflightPrincipal, &args.preflightPrincipal());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    optionalCorsPreflightArgs = args;
+  } else {
+    optionalCorsPreflightArgs = mozilla::void_t();
+  }
 
   // NB: This call forces us to cache mTopWindowURI if we haven't already.
   nsCOMPtr<nsIURI> uri;
   GetTopWindowURI(getter_AddRefs(uri));
 
   SerializeURI(mTopWindowURI, openArgs.topWindowURI());
 
   openArgs.fds() = optionalFDs;
@@ -2332,28 +2340,16 @@ NS_IMETHODIMP HttpChannelChild::AddCooki
 }
 
 NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(RequestHeaderTuples **aRequestHeaders)
 {
   *aRequestHeaders = &mClientSetRequestHeaders;
   return NS_OK;
 }
 
-void
-HttpChannelChild::GetClientSetCorsPreflightParameters(OptionalCorsPreflightArgs& aArgs)
-{
-  if (mRequireCORSPreflight) {
-    CorsPreflightArgs args;
-    args.unsafeHeaders() = mUnsafeHeaders;
-    aArgs = args;
-  } else {
-    aArgs = mozilla::void_t();
-  }
-}
-
 NS_IMETHODIMP
 HttpChannelChild::RemoveCorsPreflightCacheEntry(nsIURI* aURI,
                                                 nsIPrincipal* aPrincipal)
 {
   URIParams uri;
   SerializeURI(aURI, uri);
   PrincipalInfo principalInfo;
   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -471,17 +471,24 @@ HttpChannelParent::DoAsyncOpen(  const U
     Unused << fdSetActor->Send__delete__(fdSetActor);
   } else if (aFds.type() == OptionalFileDescriptorSet::TArrayOfFileDescriptor) {
     const_cast<OptionalFileDescriptorSet&>(aFds).
       get_ArrayOfFileDescriptor().SwapElements(fds);
   }
 
   if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
     const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
-    mChannel->SetCorsPreflightParameters(args.unsafeHeaders());
+    nsCOMPtr<nsIPrincipal> preflightPrincipal =
+      PrincipalInfoToPrincipal(args.preflightPrincipal());
+    rv = mChannel->SetCorsPreflightParameters(args.unsafeHeaders(),
+                                              args.withCredentials(),
+                                              preflightPrincipal);
+    if (NS_FAILED(rv)) {
+      return SendFailedAsyncOpen(rv);
+    }
   }
 
   nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream, fds);
   if (stream) {
     mChannel->InternalSetUploadStream(stream);
     mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
   }
 
@@ -718,18 +725,17 @@ HttpChannelParent::RecvUpdateAssociatedC
   }
   return true;
 }
 
 bool
 HttpChannelParent::RecvRedirect2Verify(const nsresult& result,
                                        const RequestHeaderTuples& changedHeaders,
                                        const uint32_t& loadFlags,
-                                       const OptionalURIParams& aAPIRedirectURI,
-                                       const OptionalCorsPreflightArgs& aCorsPreflightArgs)
+                                       const OptionalURIParams&   aAPIRedirectURI)
 {
   LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%x]\n",
        this, result));
   if (NS_SUCCEEDED(result)) {
     nsCOMPtr<nsIHttpChannel> newHttpChannel =
         do_QueryInterface(mRedirectChannel);
 
     if (newHttpChannel) {
@@ -748,24 +754,16 @@ HttpChannelParent::RecvRedirect2Verify(c
         }
       }
 
       // A successfully redirected channel must have the LOAD_REPLACE flag.
       MOZ_ASSERT(loadFlags & nsIChannel::LOAD_REPLACE);
       if (loadFlags & nsIChannel::LOAD_REPLACE) {
         newHttpChannel->SetLoadFlags(loadFlags);
       }
-
-      if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
-        nsCOMPtr<nsIHttpChannelInternal> newInternalChannel =
-          do_QueryInterface(newHttpChannel);
-        MOZ_RELEASE_ASSERT(newInternalChannel);
-        const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
-        newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders());
-      }
     }
   }
 
   if (!mRedirectCallback) {
     // This should according the logic never happen, log the situation.
     if (mReceivedRedirect2Verify)
       LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this));
     if (mSentRedirect1BeginFailed)
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -134,18 +134,17 @@ protected:
   virtual bool RecvSetClassOfService(const uint32_t& cos) override;
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
   virtual bool RecvSuspend() override;
   virtual bool RecvResume() override;
   virtual bool RecvCancel(const nsresult& status) override;
   virtual bool RecvRedirect2Verify(const nsresult& result,
                                    const RequestHeaderTuples& changedHeaders,
                                    const uint32_t& loadFlags,
-                                   const OptionalURIParams& apiRedirectUri,
-                                   const OptionalCorsPreflightArgs& aCorsPreflightArgs) override;
+                                   const OptionalURIParams& apiRedirectUri) override;
   virtual bool RecvUpdateAssociatedContentSecurity(const int32_t& broken,
                                                    const int32_t& no) override;
   virtual bool RecvDocumentChannelCleanup() override;
   virtual bool RecvMarkOfflineCacheEntryAsForeign() override;
   virtual bool RecvDivertOnDataAvailable(const nsCString& data,
                                          const uint64_t& offset,
                                          const uint32_t& count) override;
   virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) override;
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -4,23 +4,25 @@
 /* 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 protocol PNecko;
 include InputStreamParams;
 include URIParams;
 include PBackgroundSharedTypes;
-include NeckoChannelParams;
 
 include protocol PBlob; //FIXME: bug #792908
 
 include "mozilla/net/NeckoMessageUtils.h";
 
+using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
+using class nsHttpResponseHead from "nsHttpResponseHead.h";
+using struct nsHttpAtom from "nsHttp.h";
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
 using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
 
 namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 protocol PHttpChannel
@@ -40,18 +42,17 @@ parent:
                                   int32_t no);
   Suspend();
   Resume();
 
   Cancel(nsresult status);
 
   // Reports approval/veto of redirect by child process redirect observers
   Redirect2Verify(nsresult result, RequestHeaderTuples changedHeaders,
-                  uint32_t loadFlags, OptionalURIParams apiRedirectTo,
-                  OptionalCorsPreflightArgs corsPreflightArgs);
+                  uint32_t loadFlags, OptionalURIParams apiRedirectTo);
 
   // For document loads we keep this protocol open after child's
   // OnStopRequest, and send this msg (instead of __delete__) to allow
   // partial cleanup on parent.
   DocumentChannelCleanup();
 
   // This might have to be sync. If this fails we must fail the document load
   // to avoid endless loop.
--- a/netwerk/protocol/http/nsCORSListenerProxy.cpp
+++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp
@@ -816,26 +816,26 @@ nsCORSListenerProxy::UpdateChannel(nsICh
                                    DataURIHandling aAllowDataURI)
 {
   nsCOMPtr<nsIURI> uri, originalURI;
   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
-
   // exempt data URIs from the same origin check.
   if (aAllowDataURI == DataURIHandling::Allow && originalURI == uri) {
     bool dataScheme = false;
     rv = uri->SchemeIs("data", &dataScheme);
     NS_ENSURE_SUCCESS(rv, rv);
     if (dataScheme) {
       return NS_OK;
     }
+    nsCOMPtr<nsILoadInfo> loadInfo;
+    aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
     if (loadInfo && loadInfo->GetAboutBlankInherits() &&
         NS_IsAboutBlank(uri)) {
       return NS_OK;
     }
   }
 
   // Set CORS attributes on channel so that intercepted requests get correct
   // values. We have to do this here because the CheckMayLoad checks may lead
@@ -876,21 +876,16 @@ nsCORSListenerProxy::UpdateChannel(nsICh
   // e.g. toplevel page: https://www.example.com loads
   //                xhr: http://www.example.com/somefoo,
   // then the xhr request will be upgraded to https before it fetches any data
   // from the netwerk, hence we shouldn't require CORS in that specific case.
   if (CheckUpgradeInsecureRequestsPreventsCORS(mRequestingPrincipal, aChannel)) {
     return NS_OK;
   }
 
-  // Check if we need to do a preflight, and if so set one up. This must be
-  // called once we know that the request is going, or has gone, cross-origin.
-  rv = CheckPreflightNeeded(aChannel);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // It's a cross site load
   mHasBeenCrossSite = true;
 
   nsCString userpass;
   uri->GetUserPass(userpass);
   NS_ENSURE_TRUE(userpass.IsEmpty(), NS_ERROR_DOM_BAD_URI);
 
   // Add the Origin header
@@ -899,98 +894,30 @@ nsCORSListenerProxy::UpdateChannel(nsICh
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
   NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);
 
   rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Make cookie-less if needed. We don't need to do anything here if the
-  // channel was opened with AsyncOpen2, since then AsyncOpen2 will take
-  // care of the cookie policy for us.
-  if (!mWithCredentials &&
-      (!loadInfo || !loadInfo->GetEnforceSecurity())) {
+  // Make cookie-less if needed
+  if (!mWithCredentials) {
     nsLoadFlags flags;
     rv = http->GetLoadFlags(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     flags |= nsIRequest::LOAD_ANONYMOUS;
     rv = http->SetLoadFlags(flags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
-nsresult
-nsCORSListenerProxy::CheckPreflightNeeded(nsIChannel* aChannel)
-{
-  // If this caller isn't using AsyncOpen2, or if this *is* a preflight channel,
-  // then we shouldn't initiate preflight for this channel.
-  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
-  if (!loadInfo ||
-      loadInfo->GetSecurityMode() !=
-        nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS ||
-      loadInfo->GetIsPreflight()) {
-    return NS_OK;
-  }
-
-  bool doPreflight = loadInfo->GetForcePreflight();
-
-  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
-  NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI);
-  nsAutoCString method;
-  http->GetRequestMethod(method);
-  if (!method.LowerCaseEqualsLiteral("get") &&
-      !method.LowerCaseEqualsLiteral("post") &&
-      !method.LowerCaseEqualsLiteral("head")) {
-    doPreflight = true;
-  }
-
-  // Avoid copying the array here
-  const nsTArray<nsCString>& loadInfoHeaders = loadInfo->CorsUnsafeHeaders();
-  if (!loadInfoHeaders.IsEmpty()) {
-    doPreflight = true;
-  }
-
-  // Add Content-Type header if needed
-  nsTArray<nsCString> headers;
-  nsAutoCString contentTypeHeader;
-  nsresult rv = http->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
-                                       contentTypeHeader);
-  // GetRequestHeader return an error if the header is not set. Don't add
-  // "content-type" to the list if that's the case.
-  if (NS_SUCCEEDED(rv) &&
-      !nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader) &&
-      !loadInfoHeaders.Contains(NS_LITERAL_CSTRING("content-type"),
-                                nsCaseInsensitiveCStringArrayComparator())) {
-    headers.AppendElements(loadInfoHeaders);
-    headers.AppendElement(NS_LITERAL_CSTRING("content-type"));
-    doPreflight = true;
-  }
-
-  if (!doPreflight) {
-    return NS_OK;
-  }
-
-  // A preflight is needed. But if we've already been cross-site, then
-  // we already did a preflight when that happened, and so we're not allowed
-  // to do another preflight again.
-  NS_ENSURE_FALSE(mHasBeenCrossSite, NS_ERROR_DOM_BAD_URI);
-
-  nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(http);
-  NS_ENSURE_TRUE(internal, NS_ERROR_DOM_BAD_URI);
-
-  internal->SetCorsPreflightParameters(
-    headers.IsEmpty() ? loadInfoHeaders : headers);
-
-  return NS_OK;
-}
-
 //////////////////////////////////////////////////////////////////////////
 // Preflight proxy
 
 // Class used as streamlistener and notification callback when
 // doing the initial OPTIONS request for a CORS check
 class nsCORSPreflightListener final : public nsIStreamListener,
                                       public nsIInterfaceRequestor,
                                       public nsIChannelEventSink
@@ -1304,17 +1231,19 @@ nsCORSListenerProxy::RemoveFromCorsPrefl
   MOZ_ASSERT(XRE_IsParentProcess());
   if (sPreflightCache) {
     sPreflightCache->RemoveEntries(aURI, aRequestingPrincipal);
   }
 }
 
 nsresult
 nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
+                                        nsIPrincipal* aPrincipal,
                                         nsICorsPreflightCallback* aCallback,
+                                        bool aWithCredentials,
                                         nsTArray<nsCString>& aUnsafeHeaders,
                                         nsIChannel** aPreflightChannel)
 {
   *aPreflightChannel = nullptr;
 
   if (gDisableCORS) {
     LogBlockedRequest(aRequestChannel, "CORSDisabled", nullptr);
     return NS_ERROR_DOM_BAD_URI;
@@ -1330,41 +1259,38 @@ nsCORSListenerProxy::StartCORSPreflight(
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsILoadInfo> originalLoadInfo = aRequestChannel->GetLoadInfo();
   MOZ_ASSERT(originalLoadInfo, "can not perform CORS preflight without a loadInfo");
   if (!originalLoadInfo) {
     return NS_ERROR_FAILURE;
   }
 
-  MOZ_ASSERT(originalLoadInfo->GetSecurityMode() ==
-             nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
-             "how did we end up here?");
+  nsCOMPtr<nsILoadInfo> loadInfo = static_cast<mozilla::LoadInfo*>
+    (originalLoadInfo.get())->CloneForNewRequest();
 
-  nsCOMPtr<nsIPrincipal> principal = originalLoadInfo->LoadingPrincipal();
-  bool withCredentials = originalLoadInfo->GetCookiePolicy() ==
-    nsILoadInfo::SEC_COOKIES_INCLUDE;
+  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
+
+  MOZ_ASSERT(securityMode == 0 ||
+             securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+             "how did we end up here?");
 
   nsPreflightCache::CacheEntry* entry =
     sPreflightCache ?
-    sPreflightCache->GetEntry(uri, principal, withCredentials, false) :
+    sPreflightCache->GetEntry(uri, aPrincipal, aWithCredentials, false) :
     nullptr;
 
   if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
     aCallback->OnPreflightSucceeded();
     return NS_OK;
   }
 
   // Either it wasn't cached or the cached result has expired. Build a
   // channel for the OPTIONS request.
 
-  nsCOMPtr<nsILoadInfo> loadInfo = static_cast<mozilla::LoadInfo*>
-    (originalLoadInfo.get())->CloneForNewRequest();
-  static_cast<mozilla::LoadInfo*>(loadInfo.get())->SetIsPreflight();
-
   nsCOMPtr<nsILoadGroup> loadGroup;
   rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsLoadFlags loadFlags;
   rv = aRequestChannel->GetLoadFlags(&loadFlags);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1416,24 +1342,34 @@ nsCORSListenerProxy::StartCORSPreflight(
     rv = preHttp->
       SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
                        headers, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Set up listener which will start the original channel
   RefPtr<nsCORSPreflightListener> preflightListener =
-    new nsCORSPreflightListener(principal, aCallback, withCredentials,
+    new nsCORSPreflightListener(aPrincipal, aCallback, aWithCredentials,
                                 method, preflightHeaders);
 
   rv = preflightChannel->SetNotificationCallbacks(preflightListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Start preflight
-  rv = preflightChannel->AsyncOpen2(preflightListener);
+  if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+    rv = preflightChannel->AsyncOpen2(preflightListener);
+  }
+  else {
+    RefPtr<nsCORSListenerProxy> corsListener =
+      new nsCORSListenerProxy(preflightListener, aPrincipal,
+                              aWithCredentials);
+    rv = corsListener->Init(preflightChannel, DataURIHandling::Disallow);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = preflightChannel->AsyncOpen(corsListener, nullptr);
+  }
   NS_ENSURE_SUCCESS(rv, rv);
   
   // Return newly created preflight channel
   preflightChannel.forget(aPreflightChannel);
 
   return NS_OK;
 }
 
--- a/netwerk/protocol/http/nsCORSListenerProxy.h
+++ b/netwerk/protocol/http/nsCORSListenerProxy.h
@@ -67,25 +67,26 @@ private:
   // Only HttpChannelParent can call RemoveFromCorsPreflightCache
   friend class mozilla::net::HttpChannelParent;
   // Only nsHttpChannel can invoke CORS preflights
   friend class mozilla::net::nsHttpChannel;
 
   static void RemoveFromCorsPreflightCache(nsIURI* aURI,
                                            nsIPrincipal* aRequestingPrincipal);
   static nsresult StartCORSPreflight(nsIChannel* aRequestChannel,
+                                     nsIPrincipal* aPrincipal,
                                      nsICorsPreflightCallback* aCallback,
+                                     bool aWithCredentials,
                                      nsTArray<nsCString>& aACUnsafeHeaders,
                                      nsIChannel** aPreflightChannel);
 
   ~nsCORSListenerProxy();
 
   nsresult UpdateChannel(nsIChannel* aChannel, DataURIHandling aAllowDataURI);
   nsresult CheckRequestApproved(nsIRequest* aRequest);
-  nsresult CheckPreflightNeeded(nsIChannel* aChannel);
 
   nsCOMPtr<nsIStreamListener> mOuterListener;
   // The principal that originally kicked off the request
   nsCOMPtr<nsIPrincipal> mRequestingPrincipal;
   // The principal to use for our Origin header ("source origin" in spec terms).
   // This can get changed during redirects, unlike mRequestingPrincipal.
   nsCOMPtr<nsIPrincipal> mOriginHeaderPrincipal;
   nsCOMPtr<nsIInterfaceRequestor> mOuterNotificationCallbacks;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -446,17 +446,19 @@ nsresult
 nsHttpChannel::ContinueConnect()
 {
     // If we need to start a CORS preflight, do it now!
     // Note that it is important to do this before the early returns below.
     if (!mIsCorsPreflightDone && mRequireCORSPreflight &&
         mInterceptCache != INTERCEPTED) {
         MOZ_ASSERT(!mPreflightChannel);
         nsresult rv =
-            nsCORSListenerProxy::StartCORSPreflight(this, this,
+            nsCORSListenerProxy::StartCORSPreflight(this,
+                                                    mPreflightPrincipal, this,
+                                                    mWithCredentials,
                                                     mUnsafeHeaders,
                                                     getter_AddRefs(mPreflightChannel));
         return rv;
     }
 
     MOZ_RELEASE_ASSERT(!(mRequireCORSPreflight &&
                          mInterceptCache != INTERCEPTED) ||
                        mIsCorsPreflightDone,
--- a/netwerk/protocol/http/nsIHttpChannelChild.idl
+++ b/netwerk/protocol/http/nsIHttpChannelChild.idl
@@ -1,33 +1,28 @@
 /* -*- 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/. */
 
 #include "nsISupports.idl"
 
 [ptr] native RequestHeaderTuples(mozilla::net::RequestHeaderTuples);
-[ref] native OptionalCorsPreflightArgsRef(mozilla::OptionalCorsPreflightArgs);
 
 interface nsIPrincipal;
 interface nsIURI;
 
-[uuid(893e29fb-2e84-454e-afc7-41fadbe93fd9)]
+[uuid(81acb360-edd2-428e-935f-300a32efb649)]
 interface nsIHttpChannelChild : nsISupports
 {
   void addCookiesToRequest();
 
   // Mark this channel as requiring an interception; this will propagate
   // to the corresponding parent channel when a redirect occurs.
   void forceIntercepted();
 
   // Headers that the channel client has set via SetRequestHeader.
   readonly attribute RequestHeaderTuples clientSetRequestHeaders;
 
-  // Headers that the channel client has set via SetRequestHeader.
-  [notxpcom, nostdcall]
-  void GetClientSetCorsPreflightParameters(in OptionalCorsPreflightArgsRef args);
-
   // This method is called by nsCORSListenerProxy if we need to remove
   // an entry from the CORS preflight cache in the parent process.
   void removeCorsPreflightCacheEntry(in nsIURI aURI, in nsIPrincipal aRequestingPrincipal);
 };
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -34,17 +34,17 @@ interface nsIHttpUpgradeListener : nsISu
                               in nsIAsyncOutputStream aSocketOut);
 };
 
 /**
  * Dumping ground for http.  This interface will never be frozen.  If you are
  * using any feature exposed by this interface, be aware that this interface
  * will change and you will be broken.  You have been warned.
  */
-[scriptable, uuid(332d5f9c-991c-45e3-922f-99e6fe0deb60)]
+[scriptable, uuid(23b3f883-ce09-4350-8f67-1d9858d619e1)]
 interface nsIHttpChannelInternal : nsISupports
 {
     /**
      * An http channel can own a reference to the document URI
      */
     attribute nsIURI documentURI;
 
     /**
@@ -256,11 +256,13 @@ interface nsIHttpChannelInternal : nsISu
      * proxies for this channel.
      */
     readonly attribute nsIURI proxyURI;
 
     /**
      * Make cross-origin CORS loads happen with a CORS preflight, and specify
      * the CORS preflight parameters.
      */
-    [noscript, notxpcom, nostdcall]
-    void setCorsPreflightParameters(in StringArrayRef unsafeHeaders);
+    [noscript]
+    void setCorsPreflightParameters(in StringArrayRef unsafeHeaders,
+                                    in boolean withCredentials,
+                                    in nsIPrincipal preflightPrincipal);
 };
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -961,15 +961,8 @@ nsViewSourceChannel::GetIsMainDocumentCh
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::SetIsMainDocumentChannel(bool aValue)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->SetIsMainDocumentChannel(aValue);
 }
-
-// Have to manually forward SetCorsPreflightParameters since it's [notxpcom]
-void
-nsViewSourceChannel::SetCorsPreflightParameters(const nsTArray<nsCString>& aUnsafeHeaders)
-{
-  mHttpChannelInternal->SetCorsPreflightParameters(aUnsafeHeaders);
-}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/XMLHttpRequest/send-redirect-to-cors.htm.ini
@@ -0,0 +1,14 @@
+[send-redirect-to-cors.htm]
+  type: testharness
+  [XMLHttpRequest: send() - Redirect to CORS-enabled resource (301)]
+    expected: FAIL
+
+  [XMLHttpRequest: send() - Redirect to CORS-enabled resource (302)]
+    expected: FAIL
+
+  [XMLHttpRequest: send() - Redirect to CORS-enabled resource (303)]
+    expected: FAIL
+
+  [XMLHttpRequest: send() - Redirect to CORS-enabled resource (307)]
+    expected: FAIL
+
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -101,17 +101,17 @@ nsPrefetchNode::OpenChannel()
       corsMode = static_cast<dom::HTMLLinkElement*>(source.get())->GetCORSMode();
     }
     uint32_t securityFlags;
     if (corsMode == CORS_NONE) {
       securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
     } else {
       securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
       if (corsMode == CORS_USE_CREDENTIALS) {
-        securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+        securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
       }
     }
     nsresult rv = NS_NewChannelInternal(getter_AddRefs(mChannel),
                                         mURI,
                                         source,
                                         source->NodePrincipal(),
                                         nullptr,   //aTriggeringPrincipal
                                         securityFlags,