Bug 1134324 - Set CORS mode and credentials on Fetch event Request. r=michal
authorNikhil Marathe <nsm.nikhil@gmail.com>
Tue, 17 Mar 2015 08:47:02 -0700
changeset 264010 6a639c5517e34f0df0b4a7c6b7137a7ddf636ad6
parent 264009 f55f24f650e1c898501e2f5a5abb62302be48b05
child 264011 2e4fa9ce9708f0538ee535532d28f6945f793d80
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal
bugs1134324
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1134324 - Set CORS mode and credentials on Fetch event Request. r=michal renames fetch to fetchXHR() since fetch() is now a superpower.
dom/security/nsCORSListenerProxy.cpp
dom/workers/ServiceWorkerEvents.cpp
dom/workers/ServiceWorkerManager.cpp
dom/workers/test/serviceworkers/fetch/fetch_tests.js
dom/workers/test/serviceworkers/fetch/index.html
dom/workers/test/serviceworkers/fetch_event_worker.js
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/nsIHttpChannelInternal.idl
--- a/dom/security/nsCORSListenerProxy.cpp
+++ b/dom/security/nsCORSListenerProxy.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/LinkedList.h"
 
 #include "nsCORSListenerProxy.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
 #include "nsError.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsNetUtil.h"
 #include "nsMimeTypes.h"
 #include "nsIStreamConverterService.h"
 #include "nsStringStream.h"
 #include "nsGkAtoms.h"
@@ -814,16 +815,32 @@ nsCORSListenerProxy::UpdateChannel(nsICh
     bool dataScheme = false;
     rv = uri->SchemeIs("data", &dataScheme);
     NS_ENSURE_SUCCESS(rv, rv);
     if (dataScheme) {
       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
+  // to early return. We can't be sure this is an http channel though, so we
+  // can't return early on failure.
+  nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aChannel);
+  if (internal) {
+    if (mIsPreflight) {
+      rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT);
+    } else {
+      rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = internal->SetCorsIncludeCredentials(mWithCredentials);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   // Check that the uri is ok to load
   rv = nsContentUtils::GetSecurityManager()->
     CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
                               nsIScriptSecurityManager::STANDARD);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (originalURI != uri) {
     rv = nsContentUtils::GetSecurityManager()->
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -115,17 +115,17 @@ public:
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     mChannel->SynthesizeStatus(mInternalResponse->GetStatus(), mInternalResponse->GetStatusText());
 
     nsAutoTArray<InternalHeaders::Entry, 5> entries;
-    mInternalResponse->Headers()->GetEntries(entries);
+    mInternalResponse->UnfilteredHeaders()->GetEntries(entries);
     for (uint32_t i = 0; i < entries.Length(); ++i) {
        mChannel->SynthesizeHeader(entries[i].mName, entries[i].mValue);
     }
 
     rv = mChannel->FinishSynthesizedResponse();
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to finish synthesized response");
     return rv;
   }
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -55,16 +55,25 @@
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 BEGIN_WORKERS_NAMESPACE
 
+static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin),
+              "RequestMode enumeration value should match Necko CORS mode value.");
+static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors),
+              "RequestMode enumeration value should match Necko CORS mode value.");
+static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
+              "RequestMode enumeration value should match Necko CORS mode value.");
+static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast<uint32_t>(RequestMode::Cors_with_forced_preflight),
+              "RequestMode enumeration value should match Necko CORS mode value.");
+
 struct ServiceWorkerManager::PendingOperation
 {
   nsCOMPtr<nsIRunnable> mRunnable;
 
   ServiceWorkerJobQueue* mQueue;
   nsRefPtr<ServiceWorkerJob> mJob;
 
   ServiceWorkerRegistrationData mRegistration;
@@ -2126,27 +2135,33 @@ class FetchEventRunnable : public Worker
   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
   nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
   nsTArray<nsCString> mHeaderNames;
   nsTArray<nsCString> mHeaderValues;
   nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
   nsCString mSpec;
   nsCString mMethod;
   bool mIsReload;
+  RequestMode mRequestMode;
+  RequestCredentials mRequestCredentials;
 public:
   FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
                      nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
                      nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
                      nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
                      bool aIsReload)
     : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
     , mInterceptedChannel(aChannel)
     , mServiceWorker(aServiceWorker)
     , mClientInfo(aClientInfo)
     , mIsReload(aIsReload)
+    , mRequestMode(RequestMode::No_cors)
+    // By default we set it to same-origin since normal HTTP fetches always
+    // send credentials to same-origin websites unless explicitly forbidden.
+    , mRequestCredentials(RequestCredentials::Same_origin)
   {
     MOZ_ASSERT(aWorkerPrivate);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD
   VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
@@ -2154,16 +2169,17 @@ public:
     mHeaderNames.AppendElement(aHeader);
     mHeaderValues.AppendElement(aValue);
     return NS_OK;
   }
 
   nsresult
   Init()
   {
+    AssertIsOnMainThread();
     nsCOMPtr<nsIChannel> channel;
     nsresult rv = mInterceptedChannel->GetChannel(getter_AddRefs(channel));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIURI> uri;
     rv = channel->GetURI(getter_AddRefs(uri));
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2175,16 +2191,46 @@ public:
 
     rv = httpChannel->GetRequestMethod(mMethod);
     NS_ENSURE_SUCCESS(rv, rv);
 
     uint32_t loadFlags;
     rv = channel->GetLoadFlags(&loadFlags);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
+    NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
+
+    uint32_t mode;
+    internalChannel->GetCorsMode(&mode);
+    switch (mode) {
+      case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN:
+        mRequestMode = RequestMode::Same_origin;
+        break;
+      case nsIHttpChannelInternal::CORS_MODE_NO_CORS:
+        mRequestMode = RequestMode::No_cors;
+        break;
+      case nsIHttpChannelInternal::CORS_MODE_CORS:
+      case nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT:
+        mRequestMode = RequestMode::Cors;
+        break;
+      default:
+        MOZ_CRASH("Unexpected CORS mode");
+    }
+
+    if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
+      mRequestCredentials = RequestCredentials::Omit;
+    } else {
+      bool includeCrossOrigin;
+      internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin);
+      if (includeCrossOrigin) {
+        mRequestCredentials = RequestCredentials::Include;
+      }
+    }
+
     rv = httpChannel->VisitRequestHeaders(this);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
@@ -2238,17 +2284,19 @@ private:
       }
     }
 
     nsRefPtr<Headers> headers = new Headers(globalObj.GetAsSupports(), internalHeaders);
     reqInit.mHeaders.Construct();
     reqInit.mHeaders.Value().SetAsHeaders() = headers;
 
     //TODO(jdm): set request body
-    //TODO(jdm): set request same-origin mode and credentials
+
+    reqInit.mMode.Construct(mRequestMode);
+    reqInit.mCredentials.Construct(mRequestCredentials);
 
     ErrorResult rv;
     nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return false;
     }
 
     RootedDictionary<FetchEventInit> init(aCx);
--- a/dom/workers/test/serviceworkers/fetch/fetch_tests.js
+++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
@@ -1,105 +1,139 @@
-function fetch(name, onload, onerror, headers) {
+function fetchXHR(name, onload, onerror, headers) {
   expectAsyncResult();
 
   onload = onload || function() {
     my_ok(false, "XHR load should not complete successfully");
     finish();
   };
   onerror = onerror || function() {
-    my_ok(false, "XHR load should be intercepted successfully");
+    my_ok(false, "XHR load for " + name + " should be intercepted successfully");
     finish();
   };
 
   var x = new XMLHttpRequest();
   x.open('GET', name, true);
   x.onload = function() { onload(x) };
   x.onerror = function() { onerror(x) };
   headers = headers || [];
   headers.forEach(function(header) {
     x.setRequestHeader(header[0], header[1]);
   });
   x.send();
 }
 
-fetch('synthesized.txt', function(xhr) {
+fetchXHR('synthesized.txt', function(xhr) {
   my_ok(xhr.status == 200, "load should be successful");
   my_ok(xhr.responseText == "synthesized response body", "load should have synthesized response");
   finish();
 });
 
-fetch('test-respondwith-response.txt', function(xhr) {
+fetchXHR('test-respondwith-response.txt', function(xhr) {
   my_ok(xhr.status == 200, "test-respondwith-response load should be successful");
   my_ok(xhr.responseText == "test-respondwith-response response body", "load should have response");
   finish();
 });
 
-fetch('synthesized-404.txt', function(xhr) {
+fetchXHR('synthesized-404.txt', function(xhr) {
   my_ok(xhr.status == 404, "load should 404");
   my_ok(xhr.responseText == "synthesized response body", "404 load should have synthesized response");
   finish();
 });
 
-fetch('synthesized-headers.txt', function(xhr) {
+fetchXHR('synthesized-headers.txt', function(xhr) {
   my_ok(xhr.status == 200, "load should be successful");
   my_ok(xhr.getResponseHeader("X-Custom-Greeting") === "Hello", "custom header should be set");
   my_ok(xhr.responseText == "synthesized response body", "custom header load should have synthesized response");
   finish();
 });
 
-fetch('ignored.txt', function(xhr) {
+fetchXHR('ignored.txt', function(xhr) {
   my_ok(xhr.status == 404, "load should be uninterrupted");
   finish();
 });
 
-fetch('rejected.txt', null, function(xhr) {
+fetchXHR('rejected.txt', null, function(xhr) {
   my_ok(xhr.status == 0, "load should not complete");
   finish();
 });
 
-fetch('nonresponse.txt', null, function(xhr) {
+fetchXHR('nonresponse.txt', null, function(xhr) {
   my_ok(xhr.status == 0, "load should not complete");
   finish();
 });
 
-fetch('nonresponse2.txt', null, function(xhr) {
+fetchXHR('nonresponse2.txt', null, function(xhr) {
   my_ok(xhr.status == 0, "load should not complete");
   finish();
 });
 
-fetch('headers.txt', function(xhr) {
+fetchXHR('headers.txt', function(xhr) {
   my_ok(xhr.status == 200, "load should be successful");
   my_ok(xhr.responseText == "1", "request header checks should have passed");
   finish();
 }, null, [["X-Test1", "header1"], ["X-Test2", "header2"]]);
 
 var expectedUncompressedResponse = "";
 for (var i = 0; i < 10; ++i) {
   expectedUncompressedResponse += "hello";
 }
 expectedUncompressedResponse += "\n";
 
 // ServiceWorker does not intercept, at which point the network request should
 // be correctly decoded.
-fetch('deliver-gzip.sjs', function(xhr) {
+fetchXHR('deliver-gzip.sjs', function(xhr) {
   my_ok(xhr.status == 200, "network gzip load should be successful");
   my_ok(xhr.responseText == expectedUncompressedResponse, "network gzip load should have synthesized response.");
   my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "network Content-Encoding should be gzip.");
   my_ok(xhr.getResponseHeader("Content-Length") == "35", "network Content-Length should be of original gzipped file.");
   finish();
 });
 
-fetch('hello.gz', function(xhr) {
+fetchXHR('hello.gz', function(xhr) {
+  my_ok(xhr.status == 200, "gzip load should be successful");
+  my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
+  my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
+  my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file.");
+  finish();
+});
+
+fetchXHR('hello-after-extracting.gz', function(xhr) {
   my_ok(xhr.status == 200, "gzip load should be successful");
   my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
   my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
   my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file.");
   finish();
 });
 
-fetch('hello-after-extracting.gz', function(xhr) {
-  my_ok(xhr.status == 200, "gzip load should be successful");
-  my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
-  my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
-  my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file.");
+fetchXHR('http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*', function(xhr) {
+  my_ok(xhr.status == 200, "cross origin load with correct headers should be successful");
+  my_ok(xhr.getResponseHeader("access-control-allow-origin") == null, "cors headers should be filtered out");
   finish();
 });
+
+expectAsyncResult();
+fetch('http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*')
+.then(function(res) {
+  my_ok(res.ok, "Valid CORS request should receive valid response");
+  my_ok(res.type == "cors", "Response type should be CORS");
+  res.text().then(function(body) {
+    my_ok(body === "<res>hello pass</res>\n", "cors response body should match");
+    finish();
+  });
+}, function(e) {
+  my_ok(false, "CORS Fetch failed");
+  finish();
+});
+
+expectAsyncResult();
+fetch('http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200', { mode: 'no-cors' })
+.then(function(res) {
+  my_ok(res.type == "opaque", "Response type should be opaque");
+  my_ok(res.status == 0, "Status should be 0");
+  res.text().then(function(body) {
+    my_ok(body === "", "opaque response body should be empty");
+    finish();
+  });
+}, function(e) {
+  my_ok(false, "no-cors Fetch failed");
+  finish();
+});
--- a/dom/workers/test/serviceworkers/fetch/index.html
+++ b/dom/workers/test/serviceworkers/fetch/index.html
@@ -19,17 +19,17 @@
     window.opener.postMessage({status: "ok", result: result, message: msg}, "*");
   }
 
   function check_intercepted_script() {
     document.getElementById('intercepted-script').test_result =
       document.currentScript == document.getElementById('intercepted-script');
   }
 
-  function fetch(name, onload, onerror, headers) {
+  function fetchXHR(name, onload, onerror, headers) {
     gExpected++;
 
     onload = onload || function() {
       my_ok(false, "load should not complete successfully");
       finish();
     };
     onerror = onerror || function() {
       my_ok(false, "load should be intercepted successfully");
--- a/dom/workers/test/serviceworkers/fetch_event_worker.js
+++ b/dom/workers/test/serviceworkers/fetch_event_worker.js
@@ -113,16 +113,20 @@ onfetch = function(ev) {
   else if (ev.request.url.contains("hello-after-extracting.gz")) {
     ev.respondWith(fetch("fetch/deliver-gzip.sjs").then(function(res) {
       return res.text().then(function(body) {
         return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
       });
     }));
   }
 
+  else if (ev.request.url.contains('example.com')) {
+    ev.respondWith(fetch(ev.request));
+  }
+
   else if (ev.request.url.contains("index.html")) {
     if (seenIndex) {
         var body = "<script>" +
                      "opener.postMessage({status: 'ok', result: " + ev.isReload + "," +
                                          "message: 'reload status should be indicated'}, '*');" +
                      "opener.postMessage({status: 'done'}, '*');" +
                    "</script>";
         ev.respondWith(new Response(body, {headers: {'Content-Type': 'text/html'}}));
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -75,16 +75,18 @@ HttpBaseChannel::HttpBaseChannel()
   , mSuspendCount(0)
   , mProxyResolveFlags(0)
   , mProxyURI(nullptr)
   , mContentDispositionHint(UINT32_MAX)
   , mHttpHandler(gHttpHandler)
   , mReferrerPolicy(REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE)
   , mRedirectCount(0)
   , mForcePending(false)
+  , mCorsIncludeCredentials(false)
+  , mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS)
 {
   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
   // to another process.
   memset(&mSelfAddr, 0, sizeof(NetAddr));
@@ -1849,16 +1851,44 @@ HttpBaseChannel::GetLastModifiedTime(PRT
 
 NS_IMETHODIMP
 HttpBaseChannel::ForceNoIntercept()
 {
   mForceNoIntercept = true;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::GetCorsIncludeCredentials(bool* aInclude)
+{
+  *aInclude = mCorsIncludeCredentials;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetCorsIncludeCredentials(bool aInclude)
+{
+  mCorsIncludeCredentials = aInclude;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetCorsMode(uint32_t* aMode)
+{
+  *aMode = mCorsMode;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetCorsMode(uint32_t aMode)
+{
+  mCorsMode = aMode;
+  return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsISupportsPriority
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetPriority(int32_t *value)
 {
   *value = mPriority;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -183,16 +183,20 @@ public:
   nsresult AddSecurityMessage(const nsAString &aMessageTag, const nsAString &aMessageCategory);
   NS_IMETHOD TakeAllSecurityMessages(nsCOMArray<nsISecurityConsoleMessage> &aMessages) override;
   NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable) override;
   NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable) override;
   NS_IMETHOD AddRedirect(nsIPrincipal *aRedirect) override;
   NS_IMETHOD ForcePending(bool aForcePending) override;
   NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime) override;
   NS_IMETHOD ForceNoIntercept() override;
+  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 GetTopWindowURI(nsIURI **aTopWindowURI) override;
   NS_IMETHOD ContinueBeginConnect() override;
   NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override;
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
@@ -412,16 +416,19 @@ protected:
   // copied from the transaction before we null out mTransaction
   // so that the timing can still be queried from OnStopRequest
   TimingStruct                      mTransactionTimings;
 
   nsCOMPtr<nsIPrincipal>            mPrincipal;
 
   bool                              mForcePending;
   nsCOMPtr<nsIURI>                  mTopWindowURI;
+
+  bool mCorsIncludeCredentials;
+  uint32_t mCorsMode;
 };
 
 // Share some code while working around C++'s absurd inability to handle casting
 // of member functions between base/derived types.
 // - We want to store member function pointer to call at resume time, but one
 //   such function--HandleAsyncAbort--we want to share between the
 //   nsHttpChannel/HttpChannelChild.  Can't define it in base class, because
 //   then we'd have to cast member function ptr between base/derived class
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -33,17 +33,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(a3cd6509-da6a-4cd4-8f97-2645396909da)]
+[scriptable, uuid(80ee20a9-757b-4c4e-8a4a-84cbb5981879)]
 interface nsIHttpChannelInternal : nsISupports
 {
     /**
      * An http channel can own a reference to the document URI
      */
     attribute nsIURI documentURI;
 
     /**
@@ -216,16 +216,32 @@ interface nsIHttpChannelInternal : nsISu
 
     /**
      * Force a channel that has not been AsyncOpen'ed to skip any check for possible
      * interception and proceed immediately to the network/cache.
      */
     void forceNoIntercept();
 
     /**
+     * Set by nsCORSListenerProxy if credentials should be included in
+     * cross-origin requests. false indicates "same-origin", users should still
+     * check flag LOAD_ANONYMOUS!
+     */
+    attribute boolean corsIncludeCredentials;
+
+    const unsigned long CORS_MODE_SAME_ORIGIN = 0;
+    const unsigned long CORS_MODE_NO_CORS = 1;
+    const unsigned long CORS_MODE_CORS = 2;
+    const unsigned long CORS_MODE_CORS_WITH_FORCED_PREFLIGHT = 3;
+    /**
+     * Set by nsCORSListenerProxy to indicate CORS load type. Defaults to CORS_MODE_NO_CORS.
+     */
+    attribute unsigned long corsMode;
+
+    /**
      * The URI of the top-level window that's associated with this channel.
      */
     readonly attribute nsIURI topWindowURI;
 
     /**
      * Used only by nsChannelClassifier to resume connecting or abort the
      * channel after a remote classification verdict.
      */