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 311776 e648ed99a3a2c93261b8b18647ca445f2e7f869b
parent 311775 3ca0a1e37264624f4304aec23918fb9e0f4910e2
child 311777 35d0e5235621eaf2c176695ca349191eb762bc66
push id1040
push userraliiev@mozilla.com
push dateMon, 29 Feb 2016 17:11:22 +0000
treeherdermozilla-release@8c3167321162 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1216687, 1226909
milestone45.0a1
backs out09d64535bcda005593b0e29fcfe813f07e128b79
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,