LCOV - code coverage report
Current view: top level - netwerk/protocol/http - nsCORSListenerProxy.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 4 707 0.6 %
Date: 2017-07-14 16:53:18 Functions: 1 54 1.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/Assertions.h"
       8             : #include "mozilla/LinkedList.h"
       9             : 
      10             : #include "nsCORSListenerProxy.h"
      11             : #include "nsIChannel.h"
      12             : #include "nsIHttpChannel.h"
      13             : #include "HttpChannelChild.h"
      14             : #include "nsIHttpChannelInternal.h"
      15             : #include "nsError.h"
      16             : #include "nsContentUtils.h"
      17             : #include "nsIScriptSecurityManager.h"
      18             : #include "nsNetUtil.h"
      19             : #include "nsIInterfaceRequestorUtils.h"
      20             : #include "nsServiceManagerUtils.h"
      21             : #include "nsMimeTypes.h"
      22             : #include "nsIStreamConverterService.h"
      23             : #include "nsStringStream.h"
      24             : #include "nsGkAtoms.h"
      25             : #include "nsWhitespaceTokenizer.h"
      26             : #include "nsIChannelEventSink.h"
      27             : #include "nsIAsyncVerifyRedirectCallback.h"
      28             : #include "nsCharSeparatedTokenizer.h"
      29             : #include "nsAsyncRedirectVerifyHelper.h"
      30             : #include "nsClassHashtable.h"
      31             : #include "nsHashKeys.h"
      32             : #include "nsStreamUtils.h"
      33             : #include "mozilla/Preferences.h"
      34             : #include "nsIScriptError.h"
      35             : #include "nsILoadGroup.h"
      36             : #include "nsILoadContext.h"
      37             : #include "nsIConsoleService.h"
      38             : #include "nsIDOMNode.h"
      39             : #include "nsIDOMWindowUtils.h"
      40             : #include "nsIDOMWindow.h"
      41             : #include "nsINetworkInterceptController.h"
      42             : #include "NullPrincipal.h"
      43             : #include "nsICorsPreflightCallback.h"
      44             : #include "nsISupportsImpl.h"
      45             : #include "mozilla/LoadInfo.h"
      46             : #include "nsIHttpHeaderVisitor.h"
      47             : #include <algorithm>
      48             : 
      49             : using namespace mozilla;
      50             : 
      51             : #define PREFLIGHT_CACHE_SIZE 100
      52             : 
      53             : static bool gDisableCORS = false;
      54             : static bool gDisableCORSPrivateData = false;
      55             : 
      56             : static void
      57           0 : LogBlockedRequest(nsIRequest* aRequest,
      58             :                   const char* aProperty,
      59             :                   const char16_t* aParam)
      60             : {
      61           0 :   nsresult rv = NS_OK;
      62             : 
      63             :   // Build the error object and log it to the console
      64           0 :   nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
      65           0 :   if (NS_FAILED(rv)) {
      66           0 :     NS_WARNING("Failed to log blocked cross-site request (no console)");
      67           0 :     return;
      68             :   }
      69             : 
      70             :   nsCOMPtr<nsIScriptError> scriptError =
      71           0 :     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
      72           0 :   if (NS_FAILED(rv)) {
      73           0 :     NS_WARNING("Failed to log blocked cross-site request (no scriptError)");
      74           0 :     return;
      75             :   }
      76             : 
      77           0 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
      78           0 :   nsCOMPtr<nsIURI> aUri;
      79           0 :   channel->GetURI(getter_AddRefs(aUri));
      80           0 :   nsAutoCString spec;
      81           0 :   if (aUri) {
      82           0 :     spec = aUri->GetSpecOrDefault();
      83             :   }
      84             : 
      85             :   // Generate the error message
      86           0 :   nsXPIDLString blockedMessage;
      87           0 :   NS_ConvertUTF8toUTF16 specUTF16(spec);
      88           0 :   const char16_t* params[] = { specUTF16.get(), aParam };
      89           0 :   rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
      90             :                                              aProperty,
      91             :                                              params,
      92             :                                              blockedMessage);
      93             : 
      94           0 :   if (NS_FAILED(rv)) {
      95           0 :     NS_WARNING("Failed to log blocked cross-site request (no formalizedStr");
      96           0 :     return;
      97             :   }
      98             : 
      99           0 :   nsAutoString msg(blockedMessage.get());
     100             : 
     101             :   // query innerWindowID and log to web console, otherwise log to
     102             :   // the error to the browser console.
     103           0 :   uint64_t innerWindowID = nsContentUtils::GetInnerWindowID(aRequest);
     104             : 
     105           0 :   if (innerWindowID > 0) {
     106           0 :     rv = scriptError->InitWithWindowID(msg,
     107           0 :                                        EmptyString(), // sourceName
     108           0 :                                        EmptyString(), // sourceLine
     109             :                                        0,             // lineNumber
     110             :                                        0,             // columnNumber
     111             :                                        nsIScriptError::warningFlag,
     112             :                                        "CORS",
     113             :                                        innerWindowID);
     114             :   }
     115             :   else {
     116           0 :     rv = scriptError->Init(msg,
     117           0 :                            EmptyString(), // sourceName
     118           0 :                            EmptyString(), // sourceLine
     119             :                            0,             // lineNumber
     120             :                            0,             // columnNumber
     121             :                            nsIScriptError::warningFlag,
     122           0 :                            "CORS");
     123             :   }
     124           0 :   if (NS_FAILED(rv)) {
     125           0 :     NS_WARNING("Failed to log blocked cross-site request (scriptError init failed)");
     126           0 :     return;
     127             :   }
     128           0 :   console->LogMessage(scriptError);
     129             : }
     130             : 
     131             : //////////////////////////////////////////////////////////////////////////
     132             : // Preflight cache
     133             : 
     134             : class nsPreflightCache
     135             : {
     136             : public:
     137           0 :   struct TokenTime
     138             :   {
     139             :     nsCString token;
     140             :     TimeStamp expirationTime;
     141             :   };
     142             : 
     143             :   struct CacheEntry : public LinkedListElement<CacheEntry>
     144             :   {
     145           0 :     explicit CacheEntry(nsCString& aKey)
     146           0 :       : mKey(aKey)
     147             :     {
     148           0 :       MOZ_COUNT_CTOR(nsPreflightCache::CacheEntry);
     149           0 :     }
     150             : 
     151           0 :     ~CacheEntry()
     152           0 :     {
     153           0 :       MOZ_COUNT_DTOR(nsPreflightCache::CacheEntry);
     154           0 :     }
     155             : 
     156             :     void PurgeExpired(TimeStamp now);
     157             :     bool CheckRequest(const nsCString& aMethod,
     158             :                         const nsTArray<nsCString>& aCustomHeaders);
     159             : 
     160             :     nsCString mKey;
     161             :     nsTArray<TokenTime> mMethods;
     162             :     nsTArray<TokenTime> mHeaders;
     163             :   };
     164             : 
     165           0 :   nsPreflightCache()
     166           0 :   {
     167           0 :     MOZ_COUNT_CTOR(nsPreflightCache);
     168           0 :   }
     169             : 
     170           0 :   ~nsPreflightCache()
     171           0 :   {
     172           0 :     Clear();
     173           0 :     MOZ_COUNT_DTOR(nsPreflightCache);
     174           0 :   }
     175             : 
     176           0 :   bool Initialize()
     177             :   {
     178           0 :     return true;
     179             :   }
     180             : 
     181             :   CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
     182             :                        bool aWithCredentials, bool aCreate);
     183             :   void RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal);
     184             : 
     185             :   void Clear();
     186             : 
     187             : private:
     188             :   static bool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
     189             :                             bool aWithCredentials, nsACString& _retval);
     190             : 
     191             :   nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
     192             :   LinkedList<CacheEntry> mList;
     193             : };
     194             : 
     195             : // Will be initialized in EnsurePreflightCache.
     196             : static nsPreflightCache* sPreflightCache = nullptr;
     197             : 
     198           0 : static bool EnsurePreflightCache()
     199             : {
     200           0 :   if (sPreflightCache)
     201           0 :     return true;
     202             : 
     203           0 :   nsAutoPtr<nsPreflightCache> newCache(new nsPreflightCache());
     204             : 
     205           0 :   if (newCache->Initialize()) {
     206           0 :     sPreflightCache = newCache.forget();
     207           0 :     return true;
     208             :   }
     209             : 
     210           0 :   return false;
     211             : }
     212             : 
     213             : void
     214           0 : nsPreflightCache::CacheEntry::PurgeExpired(TimeStamp now)
     215             : {
     216             :   uint32_t i;
     217           0 :   for (i = 0; i < mMethods.Length(); ++i) {
     218           0 :     if (now >= mMethods[i].expirationTime) {
     219           0 :       mMethods.RemoveElementAt(i--);
     220             :     }
     221             :   }
     222           0 :   for (i = 0; i < mHeaders.Length(); ++i) {
     223           0 :     if (now >= mHeaders[i].expirationTime) {
     224           0 :       mHeaders.RemoveElementAt(i--);
     225             :     }
     226             :   }
     227           0 : }
     228             : 
     229             : bool
     230           0 : nsPreflightCache::CacheEntry::CheckRequest(const nsCString& aMethod,
     231             :                                            const nsTArray<nsCString>& aHeaders)
     232             : {
     233           0 :   PurgeExpired(TimeStamp::NowLoRes());
     234             : 
     235           0 :   if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
     236             :     uint32_t i;
     237           0 :     for (i = 0; i < mMethods.Length(); ++i) {
     238           0 :       if (aMethod.Equals(mMethods[i].token))
     239           0 :         break;
     240             :     }
     241           0 :     if (i == mMethods.Length()) {
     242           0 :       return false;
     243             :     }
     244             :   }
     245             : 
     246           0 :   for (uint32_t i = 0; i < aHeaders.Length(); ++i) {
     247             :     uint32_t j;
     248           0 :     for (j = 0; j < mHeaders.Length(); ++j) {
     249           0 :       if (aHeaders[i].Equals(mHeaders[j].token,
     250           0 :                              nsCaseInsensitiveCStringComparator())) {
     251           0 :         break;
     252             :       }
     253             :     }
     254           0 :     if (j == mHeaders.Length()) {
     255           0 :       return false;
     256             :     }
     257             :   }
     258             : 
     259           0 :   return true;
     260             : }
     261             : 
     262             : nsPreflightCache::CacheEntry*
     263           0 : nsPreflightCache::GetEntry(nsIURI* aURI,
     264             :                            nsIPrincipal* aPrincipal,
     265             :                            bool aWithCredentials,
     266             :                            bool aCreate)
     267             : {
     268           0 :   nsCString key;
     269           0 :   if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
     270           0 :     NS_WARNING("Invalid cache key!");
     271           0 :     return nullptr;
     272             :   }
     273             : 
     274           0 :   CacheEntry* existingEntry = nullptr;
     275             : 
     276           0 :   if (mTable.Get(key, &existingEntry)) {
     277             :     // Entry already existed so just return it. Also update the LRU list.
     278             : 
     279             :     // Move to the head of the list.
     280           0 :     existingEntry->removeFrom(mList);
     281           0 :     mList.insertFront(existingEntry);
     282             : 
     283           0 :     return existingEntry;
     284             :   }
     285             : 
     286           0 :   if (!aCreate) {
     287           0 :     return nullptr;
     288             :   }
     289             : 
     290             :   // This is a new entry, allocate and insert into the table now so that any
     291             :   // failures don't cause items to be removed from a full cache.
     292           0 :   CacheEntry* newEntry = new CacheEntry(key);
     293           0 :   if (!newEntry) {
     294           0 :     NS_WARNING("Failed to allocate new cache entry!");
     295           0 :     return nullptr;
     296             :   }
     297             : 
     298           0 :   NS_ASSERTION(mTable.Count() <= PREFLIGHT_CACHE_SIZE,
     299             :                "Something is borked, too many entries in the cache!");
     300             : 
     301             :   // Now enforce the max count.
     302           0 :   if (mTable.Count() == PREFLIGHT_CACHE_SIZE) {
     303             :     // Try to kick out all the expired entries.
     304           0 :     TimeStamp now = TimeStamp::NowLoRes();
     305           0 :     for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
     306           0 :       nsAutoPtr<CacheEntry>& entry = iter.Data();
     307           0 :       entry->PurgeExpired(now);
     308             : 
     309           0 :       if (entry->mHeaders.IsEmpty() &&
     310           0 :           entry->mMethods.IsEmpty()) {
     311             :         // Expired, remove from the list as well as the hash table.
     312           0 :         entry->removeFrom(sPreflightCache->mList);
     313           0 :         iter.Remove();
     314             :       }
     315             :     }
     316             : 
     317             :     // If that didn't remove anything then kick out the least recently used
     318             :     // entry.
     319           0 :     if (mTable.Count() == PREFLIGHT_CACHE_SIZE) {
     320           0 :       CacheEntry* lruEntry = static_cast<CacheEntry*>(mList.popLast());
     321           0 :       MOZ_ASSERT(lruEntry);
     322             : 
     323             :       // This will delete 'lruEntry'.
     324           0 :       mTable.Remove(lruEntry->mKey);
     325             : 
     326           0 :       NS_ASSERTION(mTable.Count() == PREFLIGHT_CACHE_SIZE - 1,
     327             :                    "Somehow tried to remove an entry that was never added!");
     328             :     }
     329             :   }
     330             : 
     331           0 :   mTable.Put(key, newEntry);
     332           0 :   mList.insertFront(newEntry);
     333             : 
     334           0 :   return newEntry;
     335             : }
     336             : 
     337             : void
     338           0 : nsPreflightCache::RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal)
     339             : {
     340             :   CacheEntry* entry;
     341           0 :   nsCString key;
     342           0 :   if (GetCacheKey(aURI, aPrincipal, true, key) &&
     343           0 :       mTable.Get(key, &entry)) {
     344           0 :     entry->removeFrom(mList);
     345           0 :     mTable.Remove(key);
     346             :   }
     347             : 
     348           0 :   if (GetCacheKey(aURI, aPrincipal, false, key) &&
     349           0 :       mTable.Get(key, &entry)) {
     350           0 :     entry->removeFrom(mList);
     351           0 :     mTable.Remove(key);
     352             :   }
     353           0 : }
     354             : 
     355             : void
     356           0 : nsPreflightCache::Clear()
     357             : {
     358           0 :   mList.clear();
     359           0 :   mTable.Clear();
     360           0 : }
     361             : 
     362             : /* static */ bool
     363           0 : nsPreflightCache::GetCacheKey(nsIURI* aURI,
     364             :                               nsIPrincipal* aPrincipal,
     365             :                               bool aWithCredentials,
     366             :                               nsACString& _retval)
     367             : {
     368           0 :   NS_ASSERTION(aURI, "Null uri!");
     369           0 :   NS_ASSERTION(aPrincipal, "Null principal!");
     370             : 
     371           0 :   NS_NAMED_LITERAL_CSTRING(space, " ");
     372             : 
     373           0 :   nsCOMPtr<nsIURI> uri;
     374           0 :   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
     375           0 :   NS_ENSURE_SUCCESS(rv, false);
     376             : 
     377           0 :   nsAutoCString scheme, host, port;
     378           0 :   if (uri) {
     379           0 :     uri->GetScheme(scheme);
     380           0 :     uri->GetHost(host);
     381           0 :     port.AppendInt(NS_GetRealPort(uri));
     382             :   }
     383             : 
     384           0 :   if (aWithCredentials) {
     385           0 :     _retval.AssignLiteral("cred");
     386             :   }
     387             :   else {
     388           0 :     _retval.AssignLiteral("nocred");
     389             :   }
     390             : 
     391           0 :   nsAutoCString spec;
     392           0 :   rv = aURI->GetSpec(spec);
     393           0 :   NS_ENSURE_SUCCESS(rv, false);
     394             : 
     395           0 :   _retval.Append(space + scheme + space + host + space + port + space +
     396           0 :                  spec);
     397             : 
     398           0 :   return true;
     399             : }
     400             : 
     401             : //////////////////////////////////////////////////////////////////////////
     402             : // nsCORSListenerProxy
     403             : 
     404           0 : NS_IMPL_ISUPPORTS(nsCORSListenerProxy, nsIStreamListener,
     405             :                   nsIRequestObserver, nsIChannelEventSink,
     406             :                   nsIInterfaceRequestor, nsIThreadRetargetableStreamListener)
     407             : 
     408             : /* static */
     409             : void
     410           3 : nsCORSListenerProxy::Startup()
     411             : {
     412             :   Preferences::AddBoolVarCache(&gDisableCORS,
     413           3 :                                "content.cors.disable");
     414             :   Preferences::AddBoolVarCache(&gDisableCORSPrivateData,
     415           3 :                                "content.cors.no_private_data");
     416           3 : }
     417             : 
     418             : /* static */
     419             : void
     420           0 : nsCORSListenerProxy::Shutdown()
     421             : {
     422           0 :   delete sPreflightCache;
     423           0 :   sPreflightCache = nullptr;
     424           0 : }
     425             : 
     426           0 : nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
     427             :                                          nsIPrincipal* aRequestingPrincipal,
     428           0 :                                          bool aWithCredentials)
     429             :   : mOuterListener(aOuter),
     430             :     mRequestingPrincipal(aRequestingPrincipal),
     431             :     mOriginHeaderPrincipal(aRequestingPrincipal),
     432           0 :     mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
     433             :     mRequestApproved(false),
     434           0 :     mHasBeenCrossSite(false)
     435             : {
     436           0 : }
     437             : 
     438           0 : nsCORSListenerProxy::~nsCORSListenerProxy()
     439             : {
     440           0 : }
     441             : 
     442             : nsresult
     443           0 : nsCORSListenerProxy::Init(nsIChannel* aChannel, DataURIHandling aAllowDataURI)
     444             : {
     445           0 :   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
     446           0 :   aChannel->SetNotificationCallbacks(this);
     447             : 
     448           0 :   nsresult rv = UpdateChannel(aChannel, aAllowDataURI, UpdateType::Default);
     449           0 :   if (NS_FAILED(rv)) {
     450           0 :     mOuterListener = nullptr;
     451           0 :     mRequestingPrincipal = nullptr;
     452           0 :     mOriginHeaderPrincipal = nullptr;
     453           0 :     mOuterNotificationCallbacks = nullptr;
     454             :   }
     455             : #ifdef DEBUG
     456           0 :   mInited = true;
     457             : #endif
     458           0 :   return rv;
     459             : }
     460             : 
     461             : NS_IMETHODIMP
     462           0 : nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
     463             :                                     nsISupports* aContext)
     464             : {
     465           0 :   MOZ_ASSERT(mInited, "nsCORSListenerProxy has not been initialized properly");
     466           0 :   nsresult rv = CheckRequestApproved(aRequest);
     467           0 :   mRequestApproved = NS_SUCCEEDED(rv);
     468           0 :   if (!mRequestApproved) {
     469           0 :     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     470           0 :     if (channel) {
     471           0 :       nsCOMPtr<nsIURI> uri;
     472           0 :       NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
     473           0 :       if (uri) {
     474           0 :         if (sPreflightCache) {
     475             :           // OK to use mRequestingPrincipal since preflights never get
     476             :           // redirected.
     477           0 :           sPreflightCache->RemoveEntries(uri, mRequestingPrincipal);
     478             :         } else {
     479             :           nsCOMPtr<nsIHttpChannelChild> httpChannelChild =
     480           0 :             do_QueryInterface(channel);
     481           0 :           if (httpChannelChild) {
     482           0 :             rv = httpChannelChild->RemoveCorsPreflightCacheEntry(uri, mRequestingPrincipal);
     483           0 :             if (NS_FAILED(rv)) {
     484             :               // Only warn here to ensure we fall through the request Cancel()
     485             :               // and outer listener OnStartRequest() calls.
     486           0 :               NS_WARNING("Failed to remove CORS preflight cache entry!");
     487             :             }
     488             :           }
     489             :         }
     490             :       }
     491             :     }
     492             : 
     493           0 :     aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
     494           0 :     mOuterListener->OnStartRequest(aRequest, aContext);
     495             : 
     496           0 :     return NS_ERROR_DOM_BAD_URI;
     497             :   }
     498             : 
     499           0 :   return mOuterListener->OnStartRequest(aRequest, aContext);
     500             : }
     501             : 
     502             : namespace {
     503             : class CheckOriginHeader final : public nsIHttpHeaderVisitor {
     504             : 
     505             : public:
     506             :   NS_DECL_ISUPPORTS
     507             : 
     508           0 :   CheckOriginHeader()
     509           0 :    : mHeaderCount(0)
     510           0 :   {}
     511             : 
     512             :   NS_IMETHOD
     513           0 :   VisitHeader(const nsACString & aHeader, const nsACString & aValue) override
     514             :   {
     515           0 :     if (aHeader.EqualsLiteral("Access-Control-Allow-Origin")) {
     516           0 :       mHeaderCount++;
     517             :     }
     518             : 
     519           0 :     if (mHeaderCount > 1) {
     520           0 :       return NS_ERROR_DOM_BAD_URI;
     521             :     }
     522           0 :     return NS_OK;
     523             :   }
     524             : 
     525             : private:
     526             :   uint32_t mHeaderCount;
     527             : 
     528           0 :   ~CheckOriginHeader()
     529           0 :   {}
     530             : 
     531             : };
     532             : 
     533           0 : NS_IMPL_ISUPPORTS(CheckOriginHeader, nsIHttpHeaderVisitor)
     534             : }
     535             : 
     536             : nsresult
     537           0 : nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
     538             : {
     539             :   // Check if this was actually a cross domain request
     540           0 :   if (!mHasBeenCrossSite) {
     541           0 :     return NS_OK;
     542             :   }
     543             : 
     544           0 :   if (gDisableCORS) {
     545           0 :     LogBlockedRequest(aRequest, "CORSDisabled", nullptr);
     546           0 :     return NS_ERROR_DOM_BAD_URI;
     547             :   }
     548             : 
     549             :   // Check if the request failed
     550             :   nsresult status;
     551           0 :   nsresult rv = aRequest->GetStatus(&status);
     552           0 :   if (NS_FAILED(rv)) {
     553           0 :    return rv;
     554             :   }
     555             : 
     556           0 :   if (NS_FAILED(status)) {
     557           0 :     return status;
     558             :   }
     559             : 
     560             :   // Test that things worked on a HTTP level
     561           0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
     562           0 :   if (!http) {
     563           0 :     LogBlockedRequest(aRequest, "CORSRequestNotHttp", nullptr);
     564           0 :     return NS_ERROR_DOM_BAD_URI;
     565             :   }
     566             : 
     567           0 :   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
     568           0 :   NS_ENSURE_STATE(internal);
     569           0 :   bool responseSynthesized = false;
     570           0 :   if (NS_SUCCEEDED(internal->GetResponseSynthesized(&responseSynthesized)) &&
     571             :       responseSynthesized) {
     572             :     // For synthesized responses, we don't need to perform any checks.
     573             :     // Note: This would be unsafe if we ever changed our behavior to allow
     574             :     // service workers to intercept CORS preflights.
     575           0 :     return NS_OK;
     576             :   }
     577             : 
     578             :   // Check the Access-Control-Allow-Origin header
     579           0 :   RefPtr<CheckOriginHeader> visitor = new CheckOriginHeader();
     580           0 :   nsAutoCString allowedOriginHeader;
     581             : 
     582             :   // check for duplicate headers
     583           0 :   rv = http->VisitOriginalResponseHeaders(visitor);
     584           0 :   if (NS_FAILED(rv)) {
     585           0 :     LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin", nullptr);
     586           0 :     return rv;
     587             :   }
     588             : 
     589           0 :   rv = http->GetResponseHeader(
     590           0 :     NS_LITERAL_CSTRING("Access-Control-Allow-Origin"), allowedOriginHeader);
     591           0 :   if (NS_FAILED(rv)) {
     592           0 :     LogBlockedRequest(aRequest, "CORSMissingAllowOrigin", nullptr);
     593           0 :     return rv;
     594             :   }
     595             : 
     596             :   // Bug 1210985 - Explicitly point out the error that the credential is
     597             :   // not supported if the allowing origin is '*'. Note that this check
     598             :   // has to be done before the condition
     599             :   //
     600             :   // >> if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*"))
     601             :   //
     602             :   // below since "if (A && B)" is included in "if (A || !B)".
     603             :   //
     604           0 :   if (mWithCredentials && allowedOriginHeader.EqualsLiteral("*")) {
     605           0 :     LogBlockedRequest(aRequest, "CORSNotSupportingCredentials", nullptr);
     606           0 :     return NS_ERROR_DOM_BAD_URI;
     607             :   }
     608             : 
     609           0 :   if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*")) {
     610           0 :     MOZ_ASSERT(!nsContentUtils::IsExpandedPrincipal(mOriginHeaderPrincipal));
     611           0 :     nsAutoCString origin;
     612           0 :     nsContentUtils::GetASCIIOrigin(mOriginHeaderPrincipal, origin);
     613             : 
     614           0 :     if (!allowedOriginHeader.Equals(origin)) {
     615           0 :       LogBlockedRequest(aRequest, "CORSAllowOriginNotMatchingOrigin",
     616           0 :                         NS_ConvertUTF8toUTF16(allowedOriginHeader).get());
     617           0 :       return NS_ERROR_DOM_BAD_URI;
     618             :     }
     619             :   }
     620             : 
     621             :   // Check Access-Control-Allow-Credentials header
     622           0 :   if (mWithCredentials) {
     623           0 :     nsAutoCString allowCredentialsHeader;
     624           0 :     rv = http->GetResponseHeader(
     625           0 :       NS_LITERAL_CSTRING("Access-Control-Allow-Credentials"), allowCredentialsHeader);
     626             : 
     627           0 :     if (!allowCredentialsHeader.EqualsLiteral("true")) {
     628           0 :       LogBlockedRequest(aRequest, "CORSMissingAllowCredentials", nullptr);
     629           0 :       return NS_ERROR_DOM_BAD_URI;
     630             :     }
     631             :   }
     632             : 
     633           0 :   return NS_OK;
     634             : }
     635             : 
     636             : NS_IMETHODIMP
     637           0 : nsCORSListenerProxy::OnStopRequest(nsIRequest* aRequest,
     638             :                                    nsISupports* aContext,
     639             :                                    nsresult aStatusCode)
     640             : {
     641           0 :   MOZ_ASSERT(mInited, "nsCORSListenerProxy has not been initialized properly");
     642           0 :   nsresult rv = mOuterListener->OnStopRequest(aRequest, aContext, aStatusCode);
     643           0 :   mOuterListener = nullptr;
     644           0 :   mOuterNotificationCallbacks = nullptr;
     645           0 :   return rv;
     646             : }
     647             : 
     648             : NS_IMETHODIMP
     649           0 : nsCORSListenerProxy::OnDataAvailable(nsIRequest* aRequest,
     650             :                                      nsISupports* aContext,
     651             :                                      nsIInputStream* aInputStream,
     652             :                                      uint64_t aOffset,
     653             :                                      uint32_t aCount)
     654             : {
     655             :   // NB: This can be called on any thread!  But we're guaranteed that it is
     656             :   // called between OnStartRequest and OnStopRequest, so we don't need to worry
     657             :   // about races.
     658             : 
     659           0 :   MOZ_ASSERT(mInited, "nsCORSListenerProxy has not been initialized properly");
     660           0 :   if (!mRequestApproved) {
     661           0 :     return NS_ERROR_DOM_BAD_URI;
     662             :   }
     663           0 :   return mOuterListener->OnDataAvailable(aRequest, aContext, aInputStream,
     664           0 :                                          aOffset, aCount);
     665             : }
     666             : 
     667             : void
     668           0 : nsCORSListenerProxy::SetInterceptController(nsINetworkInterceptController* aInterceptController)
     669             : {
     670           0 :   mInterceptController = aInterceptController;
     671           0 : }
     672             : 
     673             : NS_IMETHODIMP
     674           0 : nsCORSListenerProxy::GetInterface(const nsIID & aIID, void **aResult)
     675             : {
     676           0 :   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
     677           0 :     *aResult = static_cast<nsIChannelEventSink*>(this);
     678           0 :     NS_ADDREF_THIS();
     679             : 
     680           0 :     return NS_OK;
     681             :   }
     682             : 
     683           0 :   if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
     684           0 :       mInterceptController) {
     685           0 :     nsCOMPtr<nsINetworkInterceptController> copy(mInterceptController);
     686           0 :     *aResult = copy.forget().take();
     687             : 
     688           0 :     return NS_OK;
     689             :   }
     690             : 
     691           0 :   return mOuterNotificationCallbacks ?
     692           0 :     mOuterNotificationCallbacks->GetInterface(aIID, aResult) :
     693           0 :     NS_ERROR_NO_INTERFACE;
     694             : }
     695             : 
     696             : NS_IMETHODIMP
     697           0 : nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
     698             :                                             nsIChannel *aNewChannel,
     699             :                                             uint32_t aFlags,
     700             :                                             nsIAsyncVerifyRedirectCallback *aCb)
     701             : {
     702             :   nsresult rv;
     703           0 :   if (NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) ||
     704           0 :       NS_IsHSTSUpgradeRedirect(aOldChannel, aNewChannel, aFlags)) {
     705             :     // Internal redirects still need to be updated in order to maintain
     706             :     // the correct headers.  We use DataURIHandling::Allow, since unallowed
     707             :     // data URIs should have been blocked before we got to the internal
     708             :     // redirect.
     709             :     rv = UpdateChannel(aNewChannel, DataURIHandling::Allow,
     710           0 :                        UpdateType::InternalOrHSTSRedirect);
     711           0 :     if (NS_FAILED(rv)) {
     712             :         NS_WARNING("nsCORSListenerProxy::AsyncOnChannelRedirect: "
     713           0 :                    "internal redirect UpdateChannel() returned failure");
     714           0 :       aOldChannel->Cancel(rv);
     715           0 :       return rv;
     716             :     }
     717             :   } else {
     718             :     // A real, external redirect.  Perform CORS checking on new URL.
     719           0 :     rv = CheckRequestApproved(aOldChannel);
     720           0 :     if (NS_FAILED(rv)) {
     721           0 :       nsCOMPtr<nsIURI> oldURI;
     722           0 :       NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
     723           0 :       if (oldURI) {
     724           0 :         if (sPreflightCache) {
     725             :           // OK to use mRequestingPrincipal since preflights never get
     726             :           // redirected.
     727           0 :           sPreflightCache->RemoveEntries(oldURI, mRequestingPrincipal);
     728             :         } else {
     729             :           nsCOMPtr<nsIHttpChannelChild> httpChannelChild =
     730           0 :             do_QueryInterface(aOldChannel);
     731           0 :           if (httpChannelChild) {
     732           0 :             rv = httpChannelChild->RemoveCorsPreflightCacheEntry(oldURI, mRequestingPrincipal);
     733           0 :             if (NS_FAILED(rv)) {
     734             :               // Only warn here to ensure we call the channel Cancel() below
     735           0 :               NS_WARNING("Failed to remove CORS preflight cache entry!");
     736             :             }
     737             :           }
     738             :         }
     739             :       }
     740           0 :       aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     741           0 :       return NS_ERROR_DOM_BAD_URI;
     742             :     }
     743             : 
     744           0 :     if (mHasBeenCrossSite) {
     745             :       // Once we've been cross-site, cross-origin redirects reset our source
     746             :       // origin. Note that we need to call GetChannelURIPrincipal() because
     747             :       // we are looking for the principal that is actually being loaded and not
     748             :       // the principal that initiated the load.
     749           0 :       nsCOMPtr<nsIPrincipal> oldChannelPrincipal;
     750           0 :       nsContentUtils::GetSecurityManager()->
     751           0 :         GetChannelURIPrincipal(aOldChannel, getter_AddRefs(oldChannelPrincipal));
     752           0 :       nsCOMPtr<nsIPrincipal> newChannelPrincipal;
     753           0 :       nsContentUtils::GetSecurityManager()->
     754           0 :         GetChannelURIPrincipal(aNewChannel, getter_AddRefs(newChannelPrincipal));
     755           0 :       if (!oldChannelPrincipal || !newChannelPrincipal) {
     756           0 :         rv = NS_ERROR_OUT_OF_MEMORY;
     757             :       }
     758             : 
     759           0 :       if (NS_SUCCEEDED(rv)) {
     760             :         bool equal;
     761           0 :         rv = oldChannelPrincipal->Equals(newChannelPrincipal, &equal);
     762           0 :         if (NS_SUCCEEDED(rv) && !equal) {
     763             :           // Spec says to set our source origin to a unique origin.
     764             :           mOriginHeaderPrincipal =
     765           0 :             NullPrincipal::CreateWithInheritedAttributes(oldChannelPrincipal);
     766             :         }
     767             :       }
     768             : 
     769           0 :       if (NS_FAILED(rv)) {
     770           0 :         aOldChannel->Cancel(rv);
     771           0 :         return rv;
     772             :       }
     773             :     }
     774             : 
     775             :     rv = UpdateChannel(aNewChannel, DataURIHandling::Disallow,
     776           0 :                        UpdateType::Default);
     777           0 :     if (NS_FAILED(rv)) {
     778             :         NS_WARNING("nsCORSListenerProxy::AsyncOnChannelRedirect: "
     779           0 :                    "UpdateChannel() returned failure");
     780           0 :       aOldChannel->Cancel(rv);
     781           0 :       return rv;
     782             :     }
     783             :   }
     784             : 
     785             :   nsCOMPtr<nsIChannelEventSink> outer =
     786           0 :     do_GetInterface(mOuterNotificationCallbacks);
     787           0 :   if (outer) {
     788           0 :     return outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, aCb);
     789             :   }
     790             : 
     791           0 :   aCb->OnRedirectVerifyCallback(NS_OK);
     792             : 
     793           0 :   return NS_OK;
     794             : }
     795             : 
     796             : NS_IMETHODIMP
     797           0 : nsCORSListenerProxy::CheckListenerChain()
     798             : {
     799           0 :   MOZ_ASSERT(NS_IsMainThread());
     800             : 
     801           0 :   if (nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
     802           0 :         do_QueryInterface(mOuterListener)) {
     803           0 :     return retargetableListener->CheckListenerChain();
     804             :   }
     805             : 
     806           0 :   return NS_ERROR_NO_INTERFACE;
     807             : }
     808             : 
     809             : // Please note that the CSP directive 'upgrade-insecure-requests' relies
     810             : // on the promise that channels get updated from http: to https: before
     811             : // the channel fetches any data from the netwerk. Such channels should
     812             : // not be blocked by CORS and marked as cross origin requests. E.g.:
     813             : // toplevel page: https://www.example.com loads
     814             : //           xhr: http://www.example.com/foo which gets updated to
     815             : //                https://www.example.com/foo
     816             : // In such a case we should bail out of CORS and rely on the promise that
     817             : // nsHttpChannel::Connect() upgrades the request from http to https.
     818             : bool
     819           0 : CheckUpgradeInsecureRequestsPreventsCORS(nsIPrincipal* aRequestingPrincipal,
     820             :                                          nsIChannel* aChannel)
     821             : {
     822           0 :   nsCOMPtr<nsIURI> channelURI;
     823           0 :   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
     824           0 :   NS_ENSURE_SUCCESS(rv, false);
     825           0 :   bool isHttpScheme = false;
     826           0 :   rv = channelURI->SchemeIs("http", &isHttpScheme);
     827           0 :   NS_ENSURE_SUCCESS(rv, false);
     828             : 
     829             :   // upgrade insecure requests is only applicable to http requests
     830           0 :   if (!isHttpScheme) {
     831           0 :     return false;
     832             :   }
     833             : 
     834           0 :   nsCOMPtr<nsIURI> principalURI;
     835           0 :   rv = aRequestingPrincipal->GetURI(getter_AddRefs(principalURI));
     836           0 :   NS_ENSURE_SUCCESS(rv, false);
     837             : 
     838             :   // if the requestingPrincipal does not have a uri, there is nothing to do
     839           0 :   if (!principalURI) {
     840           0 :     return false;
     841             :   }
     842             : 
     843           0 :   nsCOMPtr<nsIURI>originalURI;
     844           0 :   rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
     845           0 :   NS_ENSURE_SUCCESS(rv, false);
     846             : 
     847           0 :   nsAutoCString principalHost, channelHost, origChannelHost;
     848             : 
     849             :   // if we can not query a host from the uri, there is nothing to do
     850           0 :   if (NS_FAILED(principalURI->GetAsciiHost(principalHost)) ||
     851           0 :       NS_FAILED(channelURI->GetAsciiHost(channelHost)) ||
     852           0 :       NS_FAILED(originalURI->GetAsciiHost(origChannelHost))) {
     853           0 :     return false;
     854             :   }
     855             : 
     856             :   // if the hosts do not match, there is nothing to do
     857           0 :   if (!principalHost.EqualsIgnoreCase(channelHost.get())) {
     858           0 :     return false;
     859             :   }
     860             : 
     861             :   // also check that uri matches the one of the originalURI
     862           0 :   if (!channelHost.EqualsIgnoreCase(origChannelHost.get())) {
     863           0 :     return false;
     864             :   }
     865             : 
     866           0 :   nsCOMPtr<nsILoadInfo> loadInfo;
     867           0 :   rv = aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
     868           0 :   NS_ENSURE_SUCCESS(rv, false);
     869             : 
     870           0 :   if (!loadInfo) {
     871           0 :     return false;
     872             :   }
     873             : 
     874             :   // lets see if the loadInfo indicates that the request will
     875             :   // be upgraded before fetching any data from the netwerk.
     876           0 :   return loadInfo->GetUpgradeInsecureRequests();
     877             : }
     878             : 
     879             : 
     880             : nsresult
     881           0 : nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
     882             :                                    DataURIHandling aAllowDataURI,
     883             :                                    UpdateType aUpdateType)
     884             : {
     885           0 :   nsCOMPtr<nsIURI> uri, originalURI;
     886           0 :   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
     887           0 :   NS_ENSURE_SUCCESS(rv, rv);
     888           0 :   rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
     889           0 :   NS_ENSURE_SUCCESS(rv, rv);
     890             : 
     891           0 :   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
     892             : 
     893             :   // exempt data URIs from the same origin check.
     894           0 :   if (aAllowDataURI == DataURIHandling::Allow && originalURI == uri) {
     895           0 :     bool dataScheme = false;
     896           0 :     rv = uri->SchemeIs("data", &dataScheme);
     897           0 :     NS_ENSURE_SUCCESS(rv, rv);
     898           0 :     if (dataScheme) {
     899           0 :       return NS_OK;
     900             :     }
     901           0 :     if (loadInfo && loadInfo->GetAboutBlankInherits() &&
     902           0 :         NS_IsAboutBlank(uri)) {
     903           0 :       return NS_OK;
     904             :     }
     905             :   }
     906             : 
     907             :   // Set CORS attributes on channel so that intercepted requests get correct
     908             :   // values. We have to do this here because the CheckMayLoad checks may lead
     909             :   // to early return. We can't be sure this is an http channel though, so we
     910             :   // can't return early on failure.
     911           0 :   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aChannel);
     912           0 :   if (internal) {
     913           0 :     rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS);
     914           0 :     NS_ENSURE_SUCCESS(rv, rv);
     915           0 :     rv = internal->SetCorsIncludeCredentials(mWithCredentials);
     916           0 :     NS_ENSURE_SUCCESS(rv, rv);
     917             :   }
     918             : 
     919             :   // TODO: Bug 1353683
     920             :   // consider calling SetBlockedRequest in nsCORSListenerProxy::UpdateChannel
     921             :   //
     922             :   // Check that the uri is ok to load
     923           0 :   rv = nsContentUtils::GetSecurityManager()->
     924           0 :     CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
     925           0 :                               nsIScriptSecurityManager::STANDARD);
     926           0 :   NS_ENSURE_SUCCESS(rv, rv);
     927             : 
     928           0 :   if (originalURI != uri) {
     929           0 :     rv = nsContentUtils::GetSecurityManager()->
     930           0 :       CheckLoadURIWithPrincipal(mRequestingPrincipal, originalURI,
     931           0 :                                 nsIScriptSecurityManager::STANDARD);
     932           0 :     NS_ENSURE_SUCCESS(rv, rv);
     933             :   }
     934             : 
     935           0 :   if (!mHasBeenCrossSite &&
     936           0 :       NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri, false, false)) &&
     937           0 :       (originalURI == uri ||
     938           0 :        NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(originalURI,
     939             :                                                        false, false)))) {
     940           0 :     return NS_OK;
     941             :   }
     942             : 
     943             :   // if the CSP directive 'upgrade-insecure-requests' is used then we should
     944             :   // not incorrectly require CORS if the only difference of a subresource
     945             :   // request and the main page is the scheme.
     946             :   // e.g. toplevel page: https://www.example.com loads
     947             :   //                xhr: http://www.example.com/somefoo,
     948             :   // then the xhr request will be upgraded to https before it fetches any data
     949             :   // from the netwerk, hence we shouldn't require CORS in that specific case.
     950           0 :   if (CheckUpgradeInsecureRequestsPreventsCORS(mRequestingPrincipal, aChannel)) {
     951           0 :     return NS_OK;
     952             :   }
     953             : 
     954             :   // Check if we need to do a preflight, and if so set one up. This must be
     955             :   // called once we know that the request is going, or has gone, cross-origin.
     956           0 :   rv = CheckPreflightNeeded(aChannel, aUpdateType);
     957           0 :   NS_ENSURE_SUCCESS(rv, rv);
     958             : 
     959             :   // It's a cross site load
     960           0 :   mHasBeenCrossSite = true;
     961             : 
     962           0 :   nsCString userpass;
     963           0 :   uri->GetUserPass(userpass);
     964           0 :   NS_ENSURE_TRUE(userpass.IsEmpty(), NS_ERROR_DOM_BAD_URI);
     965             : 
     966             :   // If we have an expanded principal here, we'll reject the CORS request,
     967             :   // because we can't send a useful Origin header which is required for CORS.
     968           0 :   if (nsContentUtils::IsExpandedPrincipal(mOriginHeaderPrincipal)) {
     969           0 :     return NS_ERROR_DOM_BAD_URI;
     970             :   }
     971             : 
     972             :   // Add the Origin header
     973           0 :   nsAutoCString origin;
     974           0 :   rv = nsContentUtils::GetASCIIOrigin(mOriginHeaderPrincipal, origin);
     975           0 :   NS_ENSURE_SUCCESS(rv, rv);
     976             : 
     977           0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
     978           0 :   NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);
     979             : 
     980           0 :   rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, false);
     981           0 :   NS_ENSURE_SUCCESS(rv, rv);
     982             : 
     983             :   // Make cookie-less if needed. We don't need to do anything here if the
     984             :   // channel was opened with AsyncOpen2, since then AsyncOpen2 will take
     985             :   // care of the cookie policy for us.
     986           0 :   if (!mWithCredentials &&
     987           0 :       (!loadInfo || !loadInfo->GetEnforceSecurity())) {
     988             :     nsLoadFlags flags;
     989           0 :     rv = http->GetLoadFlags(&flags);
     990           0 :     NS_ENSURE_SUCCESS(rv, rv);
     991             : 
     992           0 :     flags |= nsIRequest::LOAD_ANONYMOUS;
     993           0 :     rv = http->SetLoadFlags(flags);
     994           0 :     NS_ENSURE_SUCCESS(rv, rv);
     995             :   }
     996             : 
     997           0 :   return NS_OK;
     998             : }
     999             : 
    1000             : nsresult
    1001           0 : nsCORSListenerProxy::CheckPreflightNeeded(nsIChannel* aChannel, UpdateType aUpdateType)
    1002             : {
    1003             :   // If this caller isn't using AsyncOpen2, or if this *is* a preflight channel,
    1004             :   // then we shouldn't initiate preflight for this channel.
    1005           0 :   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
    1006           0 :   if (!loadInfo ||
    1007           0 :       loadInfo->GetSecurityMode() !=
    1008           0 :         nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS ||
    1009           0 :       loadInfo->GetIsPreflight()) {
    1010           0 :     return NS_OK;
    1011             :   }
    1012             : 
    1013           0 :   bool doPreflight = loadInfo->GetForcePreflight();
    1014             : 
    1015           0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
    1016           0 :   NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI);
    1017           0 :   nsAutoCString method;
    1018           0 :   Unused << http->GetRequestMethod(method);
    1019           0 :   if (!method.LowerCaseEqualsLiteral("get") &&
    1020           0 :       !method.LowerCaseEqualsLiteral("post") &&
    1021           0 :       !method.LowerCaseEqualsLiteral("head")) {
    1022           0 :     doPreflight = true;
    1023             :   }
    1024             : 
    1025             :   // Avoid copying the array here
    1026           0 :   const nsTArray<nsCString>& loadInfoHeaders = loadInfo->CorsUnsafeHeaders();
    1027           0 :   if (!loadInfoHeaders.IsEmpty()) {
    1028           0 :     doPreflight = true;
    1029             :   }
    1030             : 
    1031             :   // Add Content-Type header if needed
    1032           0 :   nsTArray<nsCString> headers;
    1033           0 :   nsAutoCString contentTypeHeader;
    1034           0 :   nsresult rv = http->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
    1035           0 :                                        contentTypeHeader);
    1036             :   // GetRequestHeader return an error if the header is not set. Don't add
    1037             :   // "content-type" to the list if that's the case.
    1038           0 :   if (NS_SUCCEEDED(rv) &&
    1039           0 :       !nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader) &&
    1040           0 :       !loadInfoHeaders.Contains(NS_LITERAL_CSTRING("content-type"),
    1041           0 :                                 nsCaseInsensitiveCStringArrayComparator())) {
    1042           0 :     headers.AppendElements(loadInfoHeaders);
    1043           0 :     headers.AppendElement(NS_LITERAL_CSTRING("content-type"));
    1044           0 :     doPreflight = true;
    1045             :   }
    1046             : 
    1047           0 :   if (!doPreflight) {
    1048           0 :     return NS_OK;
    1049             :   }
    1050             : 
    1051             :   // A preflight is needed. But if we've already been cross-site, then
    1052             :   // we already did a preflight when that happened, and so we're not allowed
    1053             :   // to do another preflight again.
    1054           0 :   if (aUpdateType != UpdateType::InternalOrHSTSRedirect) {
    1055           0 :     NS_ENSURE_FALSE(mHasBeenCrossSite, NS_ERROR_DOM_BAD_URI);
    1056             :   }
    1057             : 
    1058           0 :   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(http);
    1059           0 :   NS_ENSURE_TRUE(internal, NS_ERROR_DOM_BAD_URI);
    1060             : 
    1061           0 :   internal->SetCorsPreflightParameters(
    1062           0 :     headers.IsEmpty() ? loadInfoHeaders : headers);
    1063             : 
    1064           0 :   return NS_OK;
    1065             : }
    1066             : 
    1067             : //////////////////////////////////////////////////////////////////////////
    1068             : // Preflight proxy
    1069             : 
    1070             : // Class used as streamlistener and notification callback when
    1071             : // doing the initial OPTIONS request for a CORS check
    1072             : class nsCORSPreflightListener final : public nsIStreamListener,
    1073             :                                       public nsIInterfaceRequestor,
    1074             :                                       public nsIChannelEventSink
    1075             : {
    1076             : public:
    1077           0 :   nsCORSPreflightListener(nsIPrincipal* aReferrerPrincipal,
    1078             :                           nsICorsPreflightCallback* aCallback,
    1079             :                           nsILoadContext* aLoadContext,
    1080             :                           bool aWithCredentials,
    1081             :                           const nsCString& aPreflightMethod,
    1082             :                           const nsTArray<nsCString>& aPreflightHeaders)
    1083           0 :    : mPreflightMethod(aPreflightMethod),
    1084             :      mPreflightHeaders(aPreflightHeaders),
    1085             :      mReferrerPrincipal(aReferrerPrincipal),
    1086             :      mCallback(aCallback),
    1087             :      mLoadContext(aLoadContext),
    1088           0 :      mWithCredentials(aWithCredentials)
    1089             :   {
    1090           0 :   }
    1091             : 
    1092             :   NS_DECL_ISUPPORTS
    1093             :   NS_DECL_NSISTREAMLISTENER
    1094             :   NS_DECL_NSIREQUESTOBSERVER
    1095             :   NS_DECL_NSIINTERFACEREQUESTOR
    1096             :   NS_DECL_NSICHANNELEVENTSINK
    1097             : 
    1098             :   nsresult CheckPreflightRequestApproved(nsIRequest* aRequest);
    1099             : 
    1100             : private:
    1101           0 :   ~nsCORSPreflightListener() {}
    1102             : 
    1103             :   void AddResultToCache(nsIRequest* aRequest);
    1104             : 
    1105             :   nsCString mPreflightMethod;
    1106             :   nsTArray<nsCString> mPreflightHeaders;
    1107             :   nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
    1108             :   nsCOMPtr<nsICorsPreflightCallback> mCallback;
    1109             :   nsCOMPtr<nsILoadContext> mLoadContext;
    1110             :   bool mWithCredentials;
    1111             : };
    1112             : 
    1113           0 : NS_IMPL_ISUPPORTS(nsCORSPreflightListener, nsIStreamListener,
    1114             :                   nsIRequestObserver, nsIInterfaceRequestor,
    1115             :                   nsIChannelEventSink)
    1116             : 
    1117             : void
    1118           0 : nsCORSPreflightListener::AddResultToCache(nsIRequest *aRequest)
    1119             : {
    1120           0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
    1121           0 :   NS_ASSERTION(http, "Request was not http");
    1122             : 
    1123             :   // The "Access-Control-Max-Age" header should return an age in seconds.
    1124           0 :   nsAutoCString headerVal;
    1125           0 :   Unused << http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
    1126           0 :                                     headerVal);
    1127           0 :   if (headerVal.IsEmpty()) {
    1128           0 :     return;
    1129             :   }
    1130             : 
    1131             :   // Sanitize the string. We only allow 'delta-seconds' as specified by
    1132             :   // http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
    1133             :   // trailing non-whitespace characters).
    1134           0 :   uint32_t age = 0;
    1135             :   nsACString::const_char_iterator iter, end;
    1136           0 :   headerVal.BeginReading(iter);
    1137           0 :   headerVal.EndReading(end);
    1138           0 :   while (iter != end) {
    1139           0 :     if (*iter < '0' || *iter > '9') {
    1140           0 :       return;
    1141             :     }
    1142           0 :     age = age * 10 + (*iter - '0');
    1143             :     // Cap at 24 hours. This also avoids overflow
    1144           0 :     age = std::min(age, 86400U);
    1145           0 :     ++iter;
    1146             :   }
    1147             : 
    1148           0 :   if (!age || !EnsurePreflightCache()) {
    1149           0 :     return;
    1150             :   }
    1151             : 
    1152             : 
    1153             :   // String seems fine, go ahead and cache.
    1154             :   // Note that we have already checked that these headers follow the correct
    1155             :   // syntax.
    1156             : 
    1157           0 :   nsCOMPtr<nsIURI> uri;
    1158           0 :   NS_GetFinalChannelURI(http, getter_AddRefs(uri));
    1159             : 
    1160           0 :   TimeStamp expirationTime = TimeStamp::NowLoRes() + TimeDuration::FromSeconds(age);
    1161             : 
    1162             :   nsPreflightCache::CacheEntry* entry =
    1163           0 :     sPreflightCache->GetEntry(uri, mReferrerPrincipal, mWithCredentials,
    1164           0 :                               true);
    1165           0 :   if (!entry) {
    1166           0 :     return;
    1167             :   }
    1168             : 
    1169             :   // The "Access-Control-Allow-Methods" header contains a comma separated
    1170             :   // list of method names.
    1171             :   Unused <<
    1172           0 :     http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
    1173           0 :                             headerVal);
    1174             : 
    1175           0 :   nsCCharSeparatedTokenizer methods(headerVal, ',');
    1176           0 :   while(methods.hasMoreTokens()) {
    1177           0 :     const nsDependentCSubstring& method = methods.nextToken();
    1178           0 :     if (method.IsEmpty()) {
    1179           0 :       continue;
    1180             :     }
    1181             :     uint32_t i;
    1182           0 :     for (i = 0; i < entry->mMethods.Length(); ++i) {
    1183           0 :       if (entry->mMethods[i].token.Equals(method)) {
    1184           0 :         entry->mMethods[i].expirationTime = expirationTime;
    1185           0 :         break;
    1186             :       }
    1187             :     }
    1188           0 :     if (i == entry->mMethods.Length()) {
    1189             :       nsPreflightCache::TokenTime* newMethod =
    1190           0 :         entry->mMethods.AppendElement();
    1191           0 :       if (!newMethod) {
    1192           0 :         return;
    1193             :       }
    1194             : 
    1195           0 :       newMethod->token = method;
    1196           0 :       newMethod->expirationTime = expirationTime;
    1197             :     }
    1198             :   }
    1199             : 
    1200             :   // The "Access-Control-Allow-Headers" header contains a comma separated
    1201             :   // list of method names.
    1202             :   Unused <<
    1203           0 :     http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
    1204           0 :                             headerVal);
    1205             : 
    1206           0 :   nsCCharSeparatedTokenizer headers(headerVal, ',');
    1207           0 :   while(headers.hasMoreTokens()) {
    1208           0 :     const nsDependentCSubstring& header = headers.nextToken();
    1209           0 :     if (header.IsEmpty()) {
    1210           0 :       continue;
    1211             :     }
    1212             :     uint32_t i;
    1213           0 :     for (i = 0; i < entry->mHeaders.Length(); ++i) {
    1214           0 :       if (entry->mHeaders[i].token.Equals(header)) {
    1215           0 :         entry->mHeaders[i].expirationTime = expirationTime;
    1216           0 :         break;
    1217             :       }
    1218             :     }
    1219           0 :     if (i == entry->mHeaders.Length()) {
    1220             :       nsPreflightCache::TokenTime* newHeader =
    1221           0 :         entry->mHeaders.AppendElement();
    1222           0 :       if (!newHeader) {
    1223           0 :         return;
    1224             :       }
    1225             : 
    1226           0 :       newHeader->token = header;
    1227           0 :       newHeader->expirationTime = expirationTime;
    1228             :     }
    1229             :   }
    1230             : }
    1231             : 
    1232             : NS_IMETHODIMP
    1233           0 : nsCORSPreflightListener::OnStartRequest(nsIRequest *aRequest,
    1234             :                                         nsISupports *aContext)
    1235             : {
    1236             : #ifdef DEBUG
    1237             :   {
    1238           0 :     nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
    1239           0 :     bool responseSynthesized = false;
    1240           0 :     if (internal &&
    1241           0 :         NS_SUCCEEDED(internal->GetResponseSynthesized(&responseSynthesized))) {
    1242             :       // For synthesized responses, we don't need to perform any checks.
    1243             :       // This would be unsafe if we ever changed our behavior to allow
    1244             :       // service workers to intercept CORS preflights.
    1245           0 :       MOZ_ASSERT(!responseSynthesized);
    1246             :     }
    1247             :   }
    1248             : #endif
    1249             : 
    1250           0 :   nsresult rv = CheckPreflightRequestApproved(aRequest);
    1251             : 
    1252           0 :   if (NS_SUCCEEDED(rv)) {
    1253             :     // Everything worked, try to cache and then fire off the actual request.
    1254           0 :     AddResultToCache(aRequest);
    1255             : 
    1256           0 :     mCallback->OnPreflightSucceeded();
    1257             :   } else {
    1258           0 :     mCallback->OnPreflightFailed(rv);
    1259             :   }
    1260             : 
    1261           0 :   return rv;
    1262             : }
    1263             : 
    1264             : NS_IMETHODIMP
    1265           0 : nsCORSPreflightListener::OnStopRequest(nsIRequest *aRequest,
    1266             :                                        nsISupports *aContext,
    1267             :                                        nsresult aStatus)
    1268             : {
    1269           0 :   mCallback = nullptr;
    1270           0 :   return NS_OK;
    1271             : }
    1272             : 
    1273             : /** nsIStreamListener methods **/
    1274             : 
    1275             : NS_IMETHODIMP
    1276           0 : nsCORSPreflightListener::OnDataAvailable(nsIRequest *aRequest,
    1277             :                                          nsISupports *ctxt,
    1278             :                                          nsIInputStream *inStr,
    1279             :                                          uint64_t sourceOffset,
    1280             :                                          uint32_t count)
    1281             : {
    1282             :   uint32_t totalRead;
    1283           0 :   return inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &totalRead);
    1284             : }
    1285             : 
    1286             : NS_IMETHODIMP
    1287           0 : nsCORSPreflightListener::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
    1288             :                                                 nsIChannel *aNewChannel,
    1289             :                                                 uint32_t aFlags,
    1290             :                                                 nsIAsyncVerifyRedirectCallback *callback)
    1291             : {
    1292             :   // Only internal redirects allowed for now.
    1293           0 :   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) &&
    1294           0 :       !NS_IsHSTSUpgradeRedirect(aOldChannel, aNewChannel, aFlags))
    1295           0 :     return NS_ERROR_DOM_BAD_URI;
    1296             : 
    1297           0 :   callback->OnRedirectVerifyCallback(NS_OK);
    1298           0 :   return NS_OK;
    1299             : }
    1300             : 
    1301             : nsresult
    1302           0 : nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
    1303             : {
    1304             :   nsresult status;
    1305           0 :   nsresult rv = aRequest->GetStatus(&status);
    1306           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1307           0 :   NS_ENSURE_SUCCESS(status, status);
    1308             : 
    1309             :   // Test that things worked on a HTTP level
    1310           0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
    1311           0 :   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
    1312           0 :   NS_ENSURE_STATE(internal);
    1313             : 
    1314             :   bool succeedded;
    1315           0 :   rv = http->GetRequestSucceeded(&succeedded);
    1316           0 :   if (NS_FAILED(rv) || !succeedded) {
    1317           0 :     LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr);
    1318           0 :     return NS_ERROR_DOM_BAD_URI;
    1319             :   }
    1320             : 
    1321           0 :   nsAutoCString headerVal;
    1322             :   // The "Access-Control-Allow-Methods" header contains a comma separated
    1323             :   // list of method names.
    1324           0 :   Unused << http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
    1325           0 :                                     headerVal);
    1326           0 :   bool foundMethod = mPreflightMethod.EqualsLiteral("GET") ||
    1327           0 :                        mPreflightMethod.EqualsLiteral("HEAD") ||
    1328           0 :                        mPreflightMethod.EqualsLiteral("POST");
    1329           0 :   nsCCharSeparatedTokenizer methodTokens(headerVal, ',');
    1330           0 :   while(methodTokens.hasMoreTokens()) {
    1331           0 :     const nsDependentCSubstring& method = methodTokens.nextToken();
    1332           0 :     if (method.IsEmpty()) {
    1333           0 :       continue;
    1334             :     }
    1335           0 :     if (!NS_IsValidHTTPToken(method)) {
    1336           0 :       LogBlockedRequest(aRequest, "CORSInvalidAllowMethod",
    1337           0 :                         NS_ConvertUTF8toUTF16(method).get());
    1338           0 :       return NS_ERROR_DOM_BAD_URI;
    1339             :     }
    1340           0 :     foundMethod |= mPreflightMethod.Equals(method);
    1341             :   }
    1342           0 :   if (!foundMethod) {
    1343           0 :     LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
    1344           0 :     return NS_ERROR_DOM_BAD_URI;
    1345             :   }
    1346             : 
    1347             :   // The "Access-Control-Allow-Headers" header contains a comma separated
    1348             :   // list of header names.
    1349           0 :   Unused << http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
    1350           0 :                                     headerVal);
    1351           0 :   nsTArray<nsCString> headers;
    1352           0 :   nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
    1353           0 :   while(headerTokens.hasMoreTokens()) {
    1354           0 :     const nsDependentCSubstring& header = headerTokens.nextToken();
    1355           0 :     if (header.IsEmpty()) {
    1356           0 :       continue;
    1357             :     }
    1358           0 :     if (!NS_IsValidHTTPToken(header)) {
    1359           0 :       LogBlockedRequest(aRequest, "CORSInvalidAllowHeader",
    1360           0 :                         NS_ConvertUTF8toUTF16(header).get());
    1361           0 :       return NS_ERROR_DOM_BAD_URI;
    1362             :     }
    1363           0 :     headers.AppendElement(header);
    1364             :   }
    1365           0 :   for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
    1366           0 :     if (!headers.Contains(mPreflightHeaders[i],
    1367           0 :                           nsCaseInsensitiveCStringArrayComparator())) {
    1368           0 :       LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
    1369           0 :                         NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
    1370           0 :       return NS_ERROR_DOM_BAD_URI;
    1371             :     }
    1372             :   }
    1373             : 
    1374           0 :   return NS_OK;
    1375             : }
    1376             : 
    1377             : NS_IMETHODIMP
    1378           0 : nsCORSPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
    1379             : {
    1380           0 :   if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
    1381           0 :     nsCOMPtr<nsILoadContext> copy = mLoadContext;
    1382           0 :     copy.forget(aResult);
    1383           0 :     return NS_OK;
    1384             :   }
    1385             : 
    1386           0 :   return QueryInterface(aIID, aResult);
    1387             : }
    1388             : 
    1389             : void
    1390           0 : nsCORSListenerProxy::RemoveFromCorsPreflightCache(nsIURI* aURI,
    1391             :                                                   nsIPrincipal* aRequestingPrincipal)
    1392             : {
    1393           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    1394           0 :   if (sPreflightCache) {
    1395           0 :     sPreflightCache->RemoveEntries(aURI, aRequestingPrincipal);
    1396             :   }
    1397           0 : }
    1398             : 
    1399             : nsresult
    1400           0 : nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
    1401             :                                         nsICorsPreflightCallback* aCallback,
    1402             :                                         nsTArray<nsCString>& aUnsafeHeaders,
    1403             :                                         nsIChannel** aPreflightChannel)
    1404             : {
    1405           0 :   *aPreflightChannel = nullptr;
    1406             : 
    1407           0 :   if (gDisableCORS) {
    1408           0 :     LogBlockedRequest(aRequestChannel, "CORSDisabled", nullptr);
    1409           0 :     return NS_ERROR_DOM_BAD_URI;
    1410             :   }
    1411             : 
    1412           0 :   nsAutoCString method;
    1413           0 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel));
    1414           0 :   NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED);
    1415           0 :   Unused << httpChannel->GetRequestMethod(method);
    1416             : 
    1417           0 :   nsCOMPtr<nsIURI> uri;
    1418           0 :   nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(uri));
    1419           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1420             : 
    1421           0 :   nsCOMPtr<nsILoadInfo> originalLoadInfo = aRequestChannel->GetLoadInfo();
    1422           0 :   MOZ_ASSERT(originalLoadInfo, "can not perform CORS preflight without a loadInfo");
    1423           0 :   if (!originalLoadInfo) {
    1424           0 :     return NS_ERROR_FAILURE;
    1425             :   }
    1426             : 
    1427           0 :   MOZ_ASSERT(originalLoadInfo->GetSecurityMode() ==
    1428             :              nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
    1429             :              "how did we end up here?");
    1430             : 
    1431           0 :   nsCOMPtr<nsIPrincipal> principal = originalLoadInfo->LoadingPrincipal();
    1432           0 :   MOZ_ASSERT(principal &&
    1433             :              originalLoadInfo->GetExternalContentPolicyType() !=
    1434             :                nsIContentPolicy::TYPE_DOCUMENT,
    1435             :              "Should not do CORS loads for top-level loads, so a loadingPrincipal should always exist.");
    1436           0 :   bool withCredentials = originalLoadInfo->GetCookiePolicy() ==
    1437           0 :     nsILoadInfo::SEC_COOKIES_INCLUDE;
    1438             : 
    1439             :   nsPreflightCache::CacheEntry* entry =
    1440           0 :     sPreflightCache ?
    1441           0 :     sPreflightCache->GetEntry(uri, principal, withCredentials, false) :
    1442           0 :     nullptr;
    1443             : 
    1444           0 :   if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
    1445           0 :     aCallback->OnPreflightSucceeded();
    1446           0 :     return NS_OK;
    1447             :   }
    1448             : 
    1449             :   // Either it wasn't cached or the cached result has expired. Build a
    1450             :   // channel for the OPTIONS request.
    1451             : 
    1452             :   nsCOMPtr<nsILoadInfo> loadInfo = static_cast<mozilla::LoadInfo*>
    1453           0 :     (originalLoadInfo.get())->CloneForNewRequest();
    1454           0 :   static_cast<mozilla::LoadInfo*>(loadInfo.get())->SetIsPreflight();
    1455             : 
    1456           0 :   nsCOMPtr<nsILoadGroup> loadGroup;
    1457           0 :   rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    1458           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1459             : 
    1460             :   // We want to give the preflight channel's notification callbacks the same
    1461             :   // load context as the original channel's notification callbacks had.  We
    1462             :   // don't worry about a load context provided via the loadgroup here, since
    1463             :   // they have the same loadgroup.
    1464           0 :   nsCOMPtr<nsIInterfaceRequestor> callbacks;
    1465           0 :   rv = aRequestChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
    1466           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1467           0 :   nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
    1468             : 
    1469             :   nsLoadFlags loadFlags;
    1470           0 :   rv = aRequestChannel->GetLoadFlags(&loadFlags);
    1471           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1472             : 
    1473             :   // Preflight requests should never be intercepted by service workers and
    1474             :   // are always anonymous.
    1475             :   // NOTE: We ignore CORS checks on synthesized responses (see the CORS
    1476             :   // preflights, then we need to extend the GetResponseSynthesized() check in
    1477             :   // nsCORSListenerProxy::CheckRequestApproved()). If we change our behavior
    1478             :   // here and allow service workers to intercept CORS preflights, then that
    1479             :   // check won't be safe any more.
    1480           0 :   loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
    1481           0 :                nsIRequest::LOAD_ANONYMOUS;
    1482             : 
    1483           0 :   nsCOMPtr<nsIChannel> preflightChannel;
    1484           0 :   rv = NS_NewChannelInternal(getter_AddRefs(preflightChannel),
    1485             :                              uri,
    1486             :                              loadInfo,
    1487             :                              loadGroup,
    1488             :                              nullptr,   // aCallbacks
    1489           0 :                              loadFlags);
    1490           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1491             : 
    1492             :   // Set method and headers
    1493           0 :   nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
    1494           0 :   NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");
    1495             : 
    1496           0 :   rv = preHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
    1497           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1498             : 
    1499           0 :   rv = preHttp->
    1500           0 :     SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
    1501           0 :                      method, false);
    1502           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1503             : 
    1504           0 :   nsTArray<nsCString> preflightHeaders;
    1505           0 :   if (!aUnsafeHeaders.IsEmpty()) {
    1506           0 :     for (uint32_t i = 0; i < aUnsafeHeaders.Length(); ++i) {
    1507           0 :       preflightHeaders.AppendElement();
    1508           0 :       ToLowerCase(aUnsafeHeaders[i], preflightHeaders[i]);
    1509             :     }
    1510           0 :     preflightHeaders.Sort();
    1511           0 :     nsAutoCString headers;
    1512           0 :     for (uint32_t i = 0; i < preflightHeaders.Length(); ++i) {
    1513           0 :       if (i != 0) {
    1514           0 :         headers += ',';
    1515             :       }
    1516           0 :       headers += preflightHeaders[i];
    1517             :     }
    1518           0 :     rv = preHttp->
    1519           0 :       SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
    1520           0 :                        headers, false);
    1521           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1522             :   }
    1523             : 
    1524             :   // Set up listener which will start the original channel
    1525             :   RefPtr<nsCORSPreflightListener> preflightListener =
    1526             :     new nsCORSPreflightListener(principal, aCallback, loadContext,
    1527           0 :                                 withCredentials, method, preflightHeaders);
    1528             : 
    1529           0 :   rv = preflightChannel->SetNotificationCallbacks(preflightListener);
    1530           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1531             : 
    1532             :   // Start preflight
    1533           0 :   rv = preflightChannel->AsyncOpen2(preflightListener);
    1534           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1535             : 
    1536             :   // Return newly created preflight channel
    1537           0 :   preflightChannel.forget(aPreflightChannel);
    1538             : 
    1539           0 :   return NS_OK;
    1540             : }

Generated by: LCOV version 1.13