LCOV - code coverage report
Current view: directory - content/base/src - nsCrossSiteListenerProxy.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 499 16 3.2 %
Date: 2012-06-02 Functions: 44 3 6.8 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Mozilla Foundation.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Jonas Sicking <jonas@sicking.cc> (Original Author)
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsCrossSiteListenerProxy.h"
      39                 : #include "nsIChannel.h"
      40                 : #include "nsIHttpChannel.h"
      41                 : #include "nsDOMError.h"
      42                 : #include "nsContentUtils.h"
      43                 : #include "nsIScriptSecurityManager.h"
      44                 : #include "nsNetUtil.h"
      45                 : #include "nsIParser.h"
      46                 : #include "nsParserCIID.h"
      47                 : #include "nsMimeTypes.h"
      48                 : #include "nsIStreamConverterService.h"
      49                 : #include "nsStringStream.h"
      50                 : #include "nsGkAtoms.h"
      51                 : #include "nsWhitespaceTokenizer.h"
      52                 : #include "nsIChannelEventSink.h"
      53                 : #include "nsIAsyncVerifyRedirectCallback.h"
      54                 : #include "nsCharSeparatedTokenizer.h"
      55                 : #include "nsAsyncRedirectVerifyHelper.h"
      56                 : #include "prclist.h"
      57                 : #include "prtime.h"
      58                 : #include "nsClassHashtable.h"
      59                 : #include "nsHashKeys.h"
      60                 : #include "nsStreamUtils.h"
      61                 : #include "mozilla/Preferences.h"
      62                 : 
      63                 : using namespace mozilla;
      64                 : 
      65                 : #define PREFLIGHT_CACHE_SIZE 100
      66                 : 
      67                 : static bool gDisableCORS = false;
      68                 : static bool gDisableCORSPrivateData = false;
      69                 : 
      70                 : //////////////////////////////////////////////////////////////////////////
      71                 : // Preflight cache
      72                 : 
      73                 : class nsPreflightCache
      74                 : {
      75                 : public:
      76                 :   struct TokenTime
      77               0 :   {
      78                 :     nsCString token;
      79                 :     PRTime expirationTime;
      80                 :   };
      81                 : 
      82                 :   struct CacheEntry : public PRCList
      83                 :   {
      84               0 :     CacheEntry(nsCString& aKey)
      85               0 :       : mKey(aKey)
      86                 :     {
      87               0 :       MOZ_COUNT_CTOR(nsPreflightCache::CacheEntry);
      88               0 :     }
      89                 :     
      90               0 :     ~CacheEntry()
      91               0 :     {
      92               0 :       MOZ_COUNT_DTOR(nsPreflightCache::CacheEntry);
      93               0 :     }
      94                 : 
      95                 :     void PurgeExpired(PRTime now);
      96                 :     bool CheckRequest(const nsCString& aMethod,
      97                 :                         const nsTArray<nsCString>& aCustomHeaders);
      98                 : 
      99                 :     nsCString mKey;
     100                 :     nsTArray<TokenTime> mMethods;
     101                 :     nsTArray<TokenTime> mHeaders;
     102                 :   };
     103                 : 
     104               0 :   nsPreflightCache()
     105               0 :   {
     106               0 :     MOZ_COUNT_CTOR(nsPreflightCache);
     107               0 :     PR_INIT_CLIST(&mList);
     108               0 :   }
     109                 : 
     110               0 :   ~nsPreflightCache()
     111               0 :   {
     112               0 :     Clear();
     113               0 :     MOZ_COUNT_DTOR(nsPreflightCache);
     114               0 :   }
     115                 : 
     116               0 :   bool Initialize()
     117                 :   {
     118               0 :     return mTable.Init();
     119                 :   }
     120                 : 
     121                 :   CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
     122                 :                        bool aWithCredentials, bool aCreate);
     123                 :   void RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal);
     124                 : 
     125                 :   void Clear();
     126                 : 
     127                 : private:
     128                 :   static PLDHashOperator
     129                 :     RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
     130                 :                          void* aUserData);
     131                 : 
     132                 :   static bool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
     133                 :                             bool aWithCredentials, nsACString& _retval);
     134                 : 
     135                 :   nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
     136                 :   PRCList mList;
     137                 : };
     138                 : 
     139                 : // Will be initialized in EnsurePreflightCache.
     140                 : static nsPreflightCache* sPreflightCache = nsnull;
     141                 : 
     142               0 : static bool EnsurePreflightCache()
     143                 : {
     144               0 :   if (sPreflightCache)
     145               0 :     return true;
     146                 : 
     147               0 :   nsAutoPtr<nsPreflightCache> newCache(new nsPreflightCache());
     148                 : 
     149               0 :   if (newCache->Initialize()) {
     150               0 :     sPreflightCache = newCache.forget();
     151               0 :     return true;
     152                 :   }
     153                 : 
     154               0 :   return false;
     155                 : }
     156                 : 
     157                 : void
     158               0 : nsPreflightCache::CacheEntry::PurgeExpired(PRTime now)
     159                 : {
     160                 :   PRUint32 i;
     161               0 :   for (i = 0; i < mMethods.Length(); ++i) {
     162               0 :     if (now >= mMethods[i].expirationTime) {
     163               0 :       mMethods.RemoveElementAt(i--);
     164                 :     }
     165                 :   }
     166               0 :   for (i = 0; i < mHeaders.Length(); ++i) {
     167               0 :     if (now >= mHeaders[i].expirationTime) {
     168               0 :       mHeaders.RemoveElementAt(i--);
     169                 :     }
     170                 :   }
     171               0 : }
     172                 : 
     173                 : bool
     174               0 : nsPreflightCache::CacheEntry::CheckRequest(const nsCString& aMethod,
     175                 :                                            const nsTArray<nsCString>& aHeaders)
     176                 : {
     177               0 :   PurgeExpired(PR_Now());
     178                 : 
     179               0 :   if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
     180                 :     PRUint32 i;
     181               0 :     for (i = 0; i < mMethods.Length(); ++i) {
     182               0 :       if (aMethod.Equals(mMethods[i].token))
     183               0 :         break;
     184                 :     }
     185               0 :     if (i == mMethods.Length()) {
     186               0 :       return false;
     187                 :     }
     188                 :   }
     189                 : 
     190               0 :   for (PRUint32 i = 0; i < aHeaders.Length(); ++i) {
     191                 :     PRUint32 j;
     192               0 :     for (j = 0; j < mHeaders.Length(); ++j) {
     193               0 :       if (aHeaders[i].Equals(mHeaders[j].token,
     194               0 :                              nsCaseInsensitiveCStringComparator())) {
     195               0 :         break;
     196                 :       }
     197                 :     }
     198               0 :     if (j == mHeaders.Length()) {
     199               0 :       return false;
     200                 :     }
     201                 :   }
     202                 : 
     203               0 :   return true;
     204                 : }
     205                 : 
     206                 : nsPreflightCache::CacheEntry*
     207               0 : nsPreflightCache::GetEntry(nsIURI* aURI,
     208                 :                            nsIPrincipal* aPrincipal,
     209                 :                            bool aWithCredentials,
     210                 :                            bool aCreate)
     211                 : {
     212               0 :   nsCString key;
     213               0 :   if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
     214               0 :     NS_WARNING("Invalid cache key!");
     215               0 :     return nsnull;
     216                 :   }
     217                 : 
     218                 :   CacheEntry* entry;
     219                 : 
     220               0 :   if (mTable.Get(key, &entry)) {
     221                 :     // Entry already existed so just return it. Also update the LRU list.
     222                 : 
     223                 :     // Move to the head of the list.
     224               0 :     PR_REMOVE_LINK(entry);
     225               0 :     PR_INSERT_LINK(entry, &mList);
     226                 : 
     227               0 :     return entry;
     228                 :   }
     229                 : 
     230               0 :   if (!aCreate) {
     231               0 :     return nsnull;
     232                 :   }
     233                 : 
     234                 :   // This is a new entry, allocate and insert into the table now so that any
     235                 :   // failures don't cause items to be removed from a full cache.
     236               0 :   entry = new CacheEntry(key);
     237               0 :   if (!entry) {
     238               0 :     NS_WARNING("Failed to allocate new cache entry!");
     239               0 :     return nsnull;
     240                 :   }
     241                 : 
     242               0 :   NS_ASSERTION(mTable.Count() <= PREFLIGHT_CACHE_SIZE,
     243                 :                "Something is borked, too many entries in the cache!");
     244                 : 
     245                 :   // Now enforce the max count.
     246               0 :   if (mTable.Count() == PREFLIGHT_CACHE_SIZE) {
     247                 :     // Try to kick out all the expired entries.
     248               0 :     PRTime now = PR_Now();
     249               0 :     mTable.Enumerate(RemoveExpiredEntries, &now);
     250                 : 
     251                 :     // If that didn't remove anything then kick out the least recently used
     252                 :     // entry.
     253               0 :     if (mTable.Count() == PREFLIGHT_CACHE_SIZE) {
     254               0 :       CacheEntry* lruEntry = static_cast<CacheEntry*>(PR_LIST_TAIL(&mList));
     255               0 :       PR_REMOVE_LINK(lruEntry);
     256                 : 
     257                 :       // This will delete 'lruEntry'.
     258               0 :       mTable.Remove(lruEntry->mKey);
     259                 : 
     260               0 :       NS_ASSERTION(mTable.Count() == PREFLIGHT_CACHE_SIZE - 1,
     261                 :                    "Somehow tried to remove an entry that was never added!");
     262                 :     }
     263                 :   }
     264                 :   
     265               0 :   if (!mTable.Put(key, entry)) {
     266                 :     // Failed, clean up the new entry.
     267               0 :     delete entry;
     268                 : 
     269               0 :     NS_WARNING("Failed to add entry to the CORS preflight cache!");
     270               0 :     return nsnull;
     271                 :   }
     272                 : 
     273               0 :   PR_INSERT_LINK(entry, &mList);
     274                 : 
     275               0 :   return entry;
     276                 : }
     277                 : 
     278                 : void
     279               0 : nsPreflightCache::RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal)
     280                 : {
     281                 :   CacheEntry* entry;
     282               0 :   nsCString key;
     283               0 :   if (GetCacheKey(aURI, aPrincipal, true, key) &&
     284               0 :       mTable.Get(key, &entry)) {
     285               0 :     PR_REMOVE_LINK(entry);
     286               0 :     mTable.Remove(key);
     287                 :   }
     288                 : 
     289               0 :   if (GetCacheKey(aURI, aPrincipal, false, key) &&
     290               0 :       mTable.Get(key, &entry)) {
     291               0 :     PR_REMOVE_LINK(entry);
     292               0 :     mTable.Remove(key);
     293                 :   }
     294               0 : }
     295                 : 
     296                 : void
     297               0 : nsPreflightCache::Clear()
     298                 : {
     299               0 :   PR_INIT_CLIST(&mList);
     300               0 :   mTable.Clear();
     301               0 : }
     302                 : 
     303                 : /* static */ PLDHashOperator
     304               0 : nsPreflightCache::RemoveExpiredEntries(const nsACString& aKey,
     305                 :                                            nsAutoPtr<CacheEntry>& aValue,
     306                 :                                            void* aUserData)
     307                 : {
     308               0 :   PRTime* now = static_cast<PRTime*>(aUserData);
     309                 :   
     310               0 :   aValue->PurgeExpired(*now);
     311                 :   
     312               0 :   if (aValue->mHeaders.IsEmpty() &&
     313               0 :       aValue->mMethods.IsEmpty()) {
     314                 :     // Expired, remove from the list as well as the hash table.
     315               0 :     PR_REMOVE_LINK(aValue);
     316               0 :     return PL_DHASH_REMOVE;
     317                 :   }
     318                 :   
     319               0 :   return PL_DHASH_NEXT;
     320                 : }
     321                 : 
     322                 : /* static */ bool
     323               0 : nsPreflightCache::GetCacheKey(nsIURI* aURI,
     324                 :                               nsIPrincipal* aPrincipal,
     325                 :                               bool aWithCredentials,
     326                 :                               nsACString& _retval)
     327                 : {
     328               0 :   NS_ASSERTION(aURI, "Null uri!");
     329               0 :   NS_ASSERTION(aPrincipal, "Null principal!");
     330                 :   
     331               0 :   NS_NAMED_LITERAL_CSTRING(space, " ");
     332                 : 
     333               0 :   nsCOMPtr<nsIURI> uri;
     334               0 :   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
     335               0 :   NS_ENSURE_SUCCESS(rv, false);
     336                 :   
     337               0 :   nsCAutoString scheme, host, port;
     338               0 :   if (uri) {
     339               0 :     uri->GetScheme(scheme);
     340               0 :     uri->GetHost(host);
     341               0 :     port.AppendInt(NS_GetRealPort(uri));
     342                 :   }
     343                 : 
     344               0 :   nsCAutoString cred;
     345               0 :   if (aWithCredentials) {
     346               0 :     _retval.AssignLiteral("cred");
     347                 :   }
     348                 :   else {
     349               0 :     _retval.AssignLiteral("nocred");
     350                 :   }
     351                 : 
     352               0 :   nsCAutoString spec;
     353               0 :   rv = aURI->GetSpec(spec);
     354               0 :   NS_ENSURE_SUCCESS(rv, false);
     355                 : 
     356               0 :   _retval.Assign(cred + space + scheme + space + host + space + port + space +
     357               0 :                  spec);
     358                 : 
     359               0 :   return true;
     360                 : }
     361                 : 
     362                 : //////////////////////////////////////////////////////////////////////////
     363                 : // nsCORSListenerProxy
     364                 : 
     365               0 : NS_IMPL_ISUPPORTS5(nsCORSListenerProxy, nsIStreamListener,
     366                 :                    nsIRequestObserver, nsIChannelEventSink,
     367                 :                    nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback)
     368                 : 
     369                 : /* static */
     370                 : void
     371            1404 : nsCORSListenerProxy::Startup()
     372                 : {
     373                 :   Preferences::AddBoolVarCache(&gDisableCORS,
     374            1404 :                                "content.cors.disable");
     375                 :   Preferences::AddBoolVarCache(&gDisableCORSPrivateData,
     376            1404 :                                "content.cors.no_private_data");
     377            1404 : }
     378                 : 
     379                 : /* static */
     380                 : void
     381            1403 : nsCORSListenerProxy::Shutdown()
     382                 : {
     383            1403 :   delete sPreflightCache;
     384            1403 :   sPreflightCache = nsnull;
     385            1403 : }
     386                 : 
     387               0 : nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
     388                 :                                          nsIPrincipal* aRequestingPrincipal,
     389                 :                                          nsIChannel* aChannel,
     390                 :                                          bool aWithCredentials,
     391                 :                                          nsresult* aResult)
     392                 :   : mOuterListener(aOuter),
     393                 :     mRequestingPrincipal(aRequestingPrincipal),
     394               0 :     mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
     395                 :     mRequestApproved(false),
     396                 :     mHasBeenCrossSite(false),
     397               0 :     mIsPreflight(false)
     398                 : {
     399               0 :   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
     400               0 :   aChannel->SetNotificationCallbacks(this);
     401                 : 
     402               0 :   *aResult = UpdateChannel(aChannel);
     403               0 :   if (NS_FAILED(*aResult)) {
     404               0 :     mOuterListener = nsnull;
     405               0 :     mRequestingPrincipal = nsnull;
     406               0 :     mOuterNotificationCallbacks = nsnull;
     407                 :   }
     408               0 : }
     409                 : 
     410               0 : nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
     411                 :                                          nsIPrincipal* aRequestingPrincipal,
     412                 :                                          nsIChannel* aChannel,
     413                 :                                          bool aWithCredentials,
     414                 :                                          bool aAllowDataURI,
     415                 :                                          nsresult* aResult)
     416                 :   : mOuterListener(aOuter),
     417                 :     mRequestingPrincipal(aRequestingPrincipal),
     418               0 :     mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
     419                 :     mRequestApproved(false),
     420                 :     mHasBeenCrossSite(false),
     421               0 :     mIsPreflight(false)
     422                 : {
     423               0 :   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
     424               0 :   aChannel->SetNotificationCallbacks(this);
     425                 : 
     426               0 :   *aResult = UpdateChannel(aChannel, aAllowDataURI);
     427               0 :   if (NS_FAILED(*aResult)) {
     428               0 :     mOuterListener = nsnull;
     429               0 :     mRequestingPrincipal = nsnull;
     430               0 :     mOuterNotificationCallbacks = nsnull;
     431                 :   }
     432               0 : }
     433                 : 
     434               0 : nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
     435                 :                                          nsIPrincipal* aRequestingPrincipal,
     436                 :                                          nsIChannel* aChannel,
     437                 :                                          bool aWithCredentials,
     438                 :                                          const nsCString& aPreflightMethod,
     439                 :                                          const nsTArray<nsCString>& aPreflightHeaders,
     440                 :                                          nsresult* aResult)
     441                 :   : mOuterListener(aOuter),
     442                 :     mRequestingPrincipal(aRequestingPrincipal),
     443               0 :     mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
     444                 :     mRequestApproved(false),
     445                 :     mHasBeenCrossSite(false),
     446                 :     mIsPreflight(true),
     447                 :     mPreflightMethod(aPreflightMethod),
     448               0 :     mPreflightHeaders(aPreflightHeaders)
     449                 : {
     450               0 :   for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
     451               0 :     ToLowerCase(mPreflightHeaders[i]);
     452                 :   }
     453               0 :   mPreflightHeaders.Sort();
     454                 : 
     455               0 :   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
     456               0 :   aChannel->SetNotificationCallbacks(this);
     457                 : 
     458               0 :   *aResult = UpdateChannel(aChannel);
     459               0 :   if (NS_FAILED(*aResult)) {
     460               0 :     mOuterListener = nsnull;
     461               0 :     mRequestingPrincipal = nsnull;
     462               0 :     mOuterNotificationCallbacks = nsnull;
     463                 :   }
     464               0 : }
     465                 : 
     466                 : NS_IMETHODIMP
     467               0 : nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
     468                 :                                     nsISupports* aContext)
     469                 : {
     470               0 :   mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest));
     471               0 :   if (!mRequestApproved) {
     472               0 :     if (sPreflightCache) {
     473               0 :       nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     474               0 :       if (channel) {
     475               0 :         nsCOMPtr<nsIURI> uri;
     476               0 :         NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
     477               0 :         if (uri) {
     478               0 :           sPreflightCache->RemoveEntries(uri, mRequestingPrincipal);
     479                 :         }
     480                 :       }
     481                 :     }
     482                 : 
     483               0 :     aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
     484               0 :     mOuterListener->OnStartRequest(aRequest, aContext);
     485                 : 
     486               0 :     return NS_ERROR_DOM_BAD_URI;
     487                 :   }
     488                 : 
     489               0 :   return mOuterListener->OnStartRequest(aRequest, aContext);
     490                 : }
     491                 : 
     492                 : bool
     493              80 : IsValidHTTPToken(const nsCSubstring& aToken)
     494                 : {
     495              80 :   if (aToken.IsEmpty()) {
     496               0 :     return false;
     497                 :   }
     498                 : 
     499                 :   nsCSubstring::const_char_iterator iter, end;
     500                 : 
     501              80 :   aToken.BeginReading(iter);
     502              80 :   aToken.EndReading(end);
     503                 : 
     504            1214 :   while (iter != end) {
     505            1054 :     if (*iter <= 32 ||
     506                 :         *iter >= 127 ||
     507                 :         *iter == '(' ||
     508                 :         *iter == ')' ||
     509                 :         *iter == '<' ||
     510                 :         *iter == '>' ||
     511                 :         *iter == '@' ||
     512                 :         *iter == ',' ||
     513                 :         *iter == ';' ||
     514                 :         *iter == ':' ||
     515                 :         *iter == '\\' ||
     516                 :         *iter == '\"' ||
     517                 :         *iter == '/' ||
     518                 :         *iter == '[' ||
     519                 :         *iter == ']' ||
     520                 :         *iter == '?' ||
     521                 :         *iter == '=' ||
     522                 :         *iter == '{' ||
     523                 :         *iter == '}') {
     524               0 :       return false;
     525                 :     }
     526            1054 :     ++iter;
     527                 :   }
     528                 : 
     529              80 :   return true;
     530                 : }
     531                 : 
     532                 : nsresult
     533               0 : nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
     534                 : {
     535                 :   // Check if this was actually a cross domain request
     536               0 :   if (!mHasBeenCrossSite) {
     537               0 :     return NS_OK;
     538                 :   }
     539                 : 
     540               0 :   if (gDisableCORS) {
     541               0 :     return NS_ERROR_DOM_BAD_URI;
     542                 :   }
     543                 : 
     544                 :   // Check if the request failed
     545                 :   nsresult status;
     546               0 :   nsresult rv = aRequest->GetStatus(&status);
     547               0 :   NS_ENSURE_SUCCESS(rv, rv);
     548               0 :   NS_ENSURE_SUCCESS(status, status);
     549                 : 
     550                 :   // Test that things worked on a HTTP level
     551               0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
     552               0 :   NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI);
     553                 : 
     554                 :   // Check the Access-Control-Allow-Origin header
     555               0 :   nsCAutoString allowedOriginHeader;
     556               0 :   rv = http->GetResponseHeader(
     557               0 :     NS_LITERAL_CSTRING("Access-Control-Allow-Origin"), allowedOriginHeader);
     558               0 :   NS_ENSURE_SUCCESS(rv, rv);
     559                 : 
     560               0 :   if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*")) {
     561               0 :     nsCAutoString origin;
     562               0 :     rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin);
     563               0 :     NS_ENSURE_SUCCESS(rv, rv);
     564                 : 
     565               0 :     if (!allowedOriginHeader.Equals(origin)) {
     566               0 :       return NS_ERROR_DOM_BAD_URI;
     567                 :     }
     568                 :   }
     569                 : 
     570                 :   // Check Access-Control-Allow-Credentials header
     571               0 :   if (mWithCredentials) {
     572               0 :     nsCAutoString allowCredentialsHeader;
     573               0 :     rv = http->GetResponseHeader(
     574               0 :       NS_LITERAL_CSTRING("Access-Control-Allow-Credentials"), allowCredentialsHeader);
     575               0 :     NS_ENSURE_SUCCESS(rv, rv);
     576                 : 
     577               0 :     if (!allowCredentialsHeader.EqualsLiteral("true")) {
     578               0 :       return NS_ERROR_DOM_BAD_URI;
     579                 :     }
     580                 :   }
     581                 : 
     582               0 :   if (mIsPreflight) {
     583                 :     bool succeeded;
     584               0 :     rv = http->GetRequestSucceeded(&succeeded);
     585               0 :     NS_ENSURE_SUCCESS(rv, rv);
     586               0 :     if (!succeeded) {
     587               0 :       return NS_ERROR_DOM_BAD_URI;
     588                 :     }
     589                 : 
     590               0 :     nsCAutoString headerVal;
     591                 :     // The "Access-Control-Allow-Methods" header contains a comma separated
     592                 :     // list of method names.
     593               0 :     http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
     594               0 :                             headerVal);
     595               0 :     bool foundMethod = mPreflightMethod.EqualsLiteral("GET") ||
     596               0 :                          mPreflightMethod.EqualsLiteral("HEAD") ||
     597               0 :                          mPreflightMethod.EqualsLiteral("POST");
     598               0 :     nsCCharSeparatedTokenizer methodTokens(headerVal, ',');
     599               0 :     while(methodTokens.hasMoreTokens()) {
     600               0 :       const nsDependentCSubstring& method = methodTokens.nextToken();
     601               0 :       if (method.IsEmpty()) {
     602               0 :         continue;
     603                 :       }
     604               0 :       if (!IsValidHTTPToken(method)) {
     605               0 :         return NS_ERROR_DOM_BAD_URI;
     606                 :       }
     607               0 :       foundMethod |= mPreflightMethod.Equals(method);
     608                 :     }
     609               0 :     NS_ENSURE_TRUE(foundMethod, NS_ERROR_DOM_BAD_URI);
     610                 : 
     611                 :     // The "Access-Control-Allow-Headers" header contains a comma separated
     612                 :     // list of header names.
     613               0 :     headerVal.Truncate();
     614               0 :     http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
     615               0 :                             headerVal);
     616               0 :     nsTArray<nsCString> headers;
     617               0 :     nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
     618               0 :     while(headerTokens.hasMoreTokens()) {
     619               0 :       const nsDependentCSubstring& header = headerTokens.nextToken();
     620               0 :       if (header.IsEmpty()) {
     621               0 :         continue;
     622                 :       }
     623               0 :       if (!IsValidHTTPToken(header)) {
     624               0 :         return NS_ERROR_DOM_BAD_URI;
     625                 :       }
     626               0 :       headers.AppendElement(header);
     627                 :     }
     628               0 :     for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
     629               0 :       if (!headers.Contains(mPreflightHeaders[i],
     630               0 :                             nsCaseInsensitiveCStringArrayComparator())) {
     631               0 :         return NS_ERROR_DOM_BAD_URI;
     632                 :       }
     633                 :     }
     634                 :   }
     635                 : 
     636               0 :   return NS_OK;
     637                 : }
     638                 : 
     639                 : NS_IMETHODIMP
     640               0 : nsCORSListenerProxy::OnStopRequest(nsIRequest* aRequest,
     641                 :                                    nsISupports* aContext,
     642                 :                                    nsresult aStatusCode)
     643                 : {
     644               0 :   nsresult rv = mOuterListener->OnStopRequest(aRequest, aContext, aStatusCode);
     645               0 :   mOuterListener = nsnull;
     646               0 :   mOuterNotificationCallbacks = nsnull;
     647               0 :   mRedirectCallback = nsnull;
     648               0 :   mOldRedirectChannel = nsnull;
     649               0 :   mNewRedirectChannel = nsnull;
     650               0 :   return rv;
     651                 : }
     652                 : 
     653                 : NS_IMETHODIMP
     654               0 : nsCORSListenerProxy::OnDataAvailable(nsIRequest* aRequest,
     655                 :                                      nsISupports* aContext, 
     656                 :                                      nsIInputStream* aInputStream,
     657                 :                                      PRUint32 aOffset,
     658                 :                                      PRUint32 aCount)
     659                 : {
     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                 : NS_IMETHODIMP
     668               0 : nsCORSListenerProxy::GetInterface(const nsIID & aIID, void **aResult)
     669                 : {
     670               0 :   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
     671               0 :     *aResult = static_cast<nsIChannelEventSink*>(this);
     672               0 :     NS_ADDREF_THIS();
     673                 : 
     674               0 :     return NS_OK;
     675                 :   }
     676                 : 
     677                 :   return mOuterNotificationCallbacks ?
     678               0 :     mOuterNotificationCallbacks->GetInterface(aIID, aResult) :
     679               0 :     NS_ERROR_NO_INTERFACE;
     680                 : }
     681                 : 
     682                 : NS_IMETHODIMP
     683               0 : nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
     684                 :                                             nsIChannel *aNewChannel,
     685                 :                                             PRUint32 aFlags,
     686                 :                                             nsIAsyncVerifyRedirectCallback *cb)
     687                 : {
     688                 :   nsresult rv;
     689               0 :   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
     690               0 :     rv = CheckRequestApproved(aOldChannel);
     691               0 :     if (NS_FAILED(rv)) {
     692               0 :       if (sPreflightCache) {
     693               0 :         nsCOMPtr<nsIURI> oldURI;
     694               0 :         NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
     695               0 :         if (oldURI) {
     696               0 :           sPreflightCache->RemoveEntries(oldURI, mRequestingPrincipal);
     697                 :         }
     698                 :       }
     699               0 :       aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     700               0 :       return NS_ERROR_DOM_BAD_URI;
     701                 :     }
     702                 :   }
     703                 : 
     704                 :   // Prepare to receive callback
     705               0 :   mRedirectCallback = cb;
     706               0 :   mOldRedirectChannel = aOldChannel;
     707               0 :   mNewRedirectChannel = aNewChannel;
     708                 : 
     709                 :   nsCOMPtr<nsIChannelEventSink> outer =
     710               0 :     do_GetInterface(mOuterNotificationCallbacks);
     711               0 :   if (outer) {
     712               0 :     rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this);
     713               0 :     if (NS_FAILED(rv)) {
     714               0 :         aOldChannel->Cancel(rv); // is this necessary...?
     715               0 :         mRedirectCallback = nsnull;
     716               0 :         mOldRedirectChannel = nsnull;
     717               0 :         mNewRedirectChannel = nsnull;
     718                 :     }
     719               0 :     return rv;  
     720                 :   }
     721                 : 
     722               0 :   (void) OnRedirectVerifyCallback(NS_OK);
     723               0 :   return NS_OK;
     724                 : }
     725                 : 
     726                 : NS_IMETHODIMP
     727               0 : nsCORSListenerProxy::OnRedirectVerifyCallback(nsresult result)
     728                 : {
     729               0 :   NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
     730               0 :   NS_ASSERTION(mOldRedirectChannel, "mOldRedirectChannel not set in callback");
     731               0 :   NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
     732                 : 
     733               0 :   if (NS_SUCCEEDED(result)) {
     734               0 :       nsresult rv = UpdateChannel(mNewRedirectChannel);
     735               0 :       if (NS_FAILED(rv)) {
     736                 :           NS_WARNING("nsCORSListenerProxy::OnRedirectVerifyCallback: "
     737               0 :                      "UpdateChannel() returned failure");
     738                 :       }
     739               0 :       result = rv;
     740                 :   }
     741                 : 
     742               0 :   if (NS_FAILED(result)) {
     743               0 :     mOldRedirectChannel->Cancel(result);
     744                 :   }
     745                 : 
     746               0 :   mOldRedirectChannel = nsnull;
     747               0 :   mNewRedirectChannel = nsnull;
     748               0 :   mRedirectCallback->OnRedirectVerifyCallback(result);
     749               0 :   mRedirectCallback   = nsnull;
     750               0 :   return NS_OK;
     751                 : }
     752                 : 
     753                 : nsresult
     754               0 : nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI)
     755                 : {
     756               0 :   nsCOMPtr<nsIURI> uri, originalURI;
     757               0 :   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
     758               0 :   NS_ENSURE_SUCCESS(rv, rv);
     759               0 :   rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
     760               0 :   NS_ENSURE_SUCCESS(rv, rv);
     761                 : 
     762                 :   // exempt data URIs from the same origin check.
     763               0 :   if (aAllowDataURI && originalURI == uri) {
     764               0 :     bool dataScheme = false;
     765               0 :     rv = uri->SchemeIs("data", &dataScheme);
     766               0 :     NS_ENSURE_SUCCESS(rv, rv);
     767               0 :     if (dataScheme) {
     768               0 :       return NS_OK;
     769                 :     }
     770                 :   }
     771                 : 
     772                 :   // Check that the uri is ok to load
     773               0 :   rv = nsContentUtils::GetSecurityManager()->
     774                 :     CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
     775               0 :                               nsIScriptSecurityManager::STANDARD);
     776               0 :   NS_ENSURE_SUCCESS(rv, rv);
     777                 : 
     778               0 :   if (originalURI != uri) {
     779               0 :     rv = nsContentUtils::GetSecurityManager()->
     780                 :       CheckLoadURIWithPrincipal(mRequestingPrincipal, originalURI,
     781               0 :                                 nsIScriptSecurityManager::STANDARD);
     782               0 :     NS_ENSURE_SUCCESS(rv, rv);
     783                 :   }
     784                 : 
     785               0 :   if (!mHasBeenCrossSite &&
     786               0 :       NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri, false)) &&
     787               0 :       (originalURI == uri ||
     788               0 :        NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(originalURI,
     789                 :                                                        false)))) {
     790               0 :     return NS_OK;
     791                 :   }
     792                 : 
     793                 :   // It's a cross site load
     794               0 :   mHasBeenCrossSite = true;
     795                 : 
     796               0 :   nsCString userpass;
     797               0 :   uri->GetUserPass(userpass);
     798               0 :   NS_ENSURE_TRUE(userpass.IsEmpty(), NS_ERROR_DOM_BAD_URI);
     799                 : 
     800                 :   // Add the Origin header
     801               0 :   nsCAutoString origin;
     802               0 :   rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin);
     803               0 :   NS_ENSURE_SUCCESS(rv, rv);
     804                 : 
     805               0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
     806               0 :   NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);
     807                 : 
     808               0 :   rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, false);
     809               0 :   NS_ENSURE_SUCCESS(rv, rv);
     810                 : 
     811                 :   // Add preflight headers if this is a preflight request
     812               0 :   if (mIsPreflight) {
     813               0 :     rv = http->
     814               0 :       SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
     815               0 :                        mPreflightMethod, false);
     816               0 :     NS_ENSURE_SUCCESS(rv, rv);
     817                 : 
     818               0 :     if (!mPreflightHeaders.IsEmpty()) {
     819               0 :       nsCAutoString headers;
     820               0 :       for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
     821               0 :         if (i != 0) {
     822               0 :           headers += ',';
     823                 :         }
     824               0 :         headers += mPreflightHeaders[i];
     825                 :       }
     826               0 :       rv = http->
     827               0 :         SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
     828               0 :                          headers, false);
     829               0 :       NS_ENSURE_SUCCESS(rv, rv);
     830                 :     }
     831                 :   }
     832                 : 
     833                 :   // Make cookie-less if needed
     834               0 :   if (mIsPreflight || !mWithCredentials) {
     835                 :     nsLoadFlags flags;
     836               0 :     rv = http->GetLoadFlags(&flags);
     837               0 :     NS_ENSURE_SUCCESS(rv, rv);
     838                 : 
     839               0 :     flags |= nsIRequest::LOAD_ANONYMOUS;
     840               0 :     rv = http->SetLoadFlags(flags);
     841               0 :     NS_ENSURE_SUCCESS(rv, rv);
     842                 :   }
     843                 : 
     844               0 :   return NS_OK;
     845                 : }
     846                 : 
     847                 : //////////////////////////////////////////////////////////////////////////
     848                 : // Preflight proxy
     849                 : 
     850                 : // Class used as streamlistener and notification callback when
     851                 : // doing the initial OPTIONS request for a CORS check
     852                 : class nsCORSPreflightListener : public nsIStreamListener,
     853                 :                                 public nsIInterfaceRequestor,
     854                 :                                 public nsIChannelEventSink
     855               0 : {
     856                 : public:
     857               0 :   nsCORSPreflightListener(nsIChannel* aOuterChannel,
     858                 :                           nsIStreamListener* aOuterListener,
     859                 :                           nsISupports* aOuterContext,
     860                 :                           nsIPrincipal* aReferrerPrincipal,
     861                 :                           const nsACString& aRequestMethod,
     862                 :                           bool aWithCredentials)
     863                 :    : mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
     864                 :      mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
     865               0 :      mRequestMethod(aRequestMethod), mWithCredentials(aWithCredentials)
     866               0 :   { }
     867                 : 
     868                 :   NS_DECL_ISUPPORTS
     869                 :   NS_DECL_NSISTREAMLISTENER
     870                 :   NS_DECL_NSIREQUESTOBSERVER
     871                 :   NS_DECL_NSIINTERFACEREQUESTOR
     872                 :   NS_DECL_NSICHANNELEVENTSINK
     873                 : 
     874                 : private:
     875                 :   void AddResultToCache(nsIRequest* aRequest);
     876                 : 
     877                 :   nsCOMPtr<nsIChannel> mOuterChannel;
     878                 :   nsCOMPtr<nsIStreamListener> mOuterListener;
     879                 :   nsCOMPtr<nsISupports> mOuterContext;
     880                 :   nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
     881                 :   nsCString mRequestMethod;
     882                 :   bool mWithCredentials;
     883                 : };
     884                 : 
     885               0 : NS_IMPL_ISUPPORTS4(nsCORSPreflightListener, nsIStreamListener,
     886                 :                    nsIRequestObserver, nsIInterfaceRequestor,
     887                 :                    nsIChannelEventSink)
     888                 : 
     889                 : void
     890               0 : nsCORSPreflightListener::AddResultToCache(nsIRequest *aRequest)
     891                 : {
     892               0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
     893               0 :   NS_ASSERTION(http, "Request was not http");
     894                 : 
     895                 :   // The "Access-Control-Max-Age" header should return an age in seconds.
     896               0 :   nsCAutoString headerVal;
     897               0 :   http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
     898               0 :                           headerVal);
     899               0 :   if (headerVal.IsEmpty()) {
     900                 :     return;
     901                 :   }
     902                 : 
     903                 :   // Sanitize the string. We only allow 'delta-seconds' as specified by
     904                 :   // http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
     905                 :   // trailing non-whitespace characters).
     906               0 :   PRUint32 age = 0;
     907                 :   nsCSubstring::const_char_iterator iter, end;
     908               0 :   headerVal.BeginReading(iter);
     909               0 :   headerVal.EndReading(end);
     910               0 :   while (iter != end) {
     911               0 :     if (*iter < '0' || *iter > '9') {
     912                 :       return;
     913                 :     }
     914               0 :     age = age * 10 + (*iter - '0');
     915                 :     // Cap at 24 hours. This also avoids overflow
     916               0 :     age = NS_MIN(age, 86400U);
     917               0 :     ++iter;
     918                 :   }
     919                 : 
     920               0 :   if (!age || !EnsurePreflightCache()) {
     921                 :     return;
     922                 :   }
     923                 : 
     924                 : 
     925                 :   // String seems fine, go ahead and cache.
     926                 :   // Note that we have already checked that these headers follow the correct
     927                 :   // syntax.
     928                 : 
     929               0 :   nsCOMPtr<nsIURI> uri;
     930               0 :   NS_GetFinalChannelURI(http, getter_AddRefs(uri));
     931                 : 
     932                 :   // PR_Now gives microseconds
     933               0 :   PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;
     934                 : 
     935                 :   nsPreflightCache::CacheEntry* entry =
     936                 :     sPreflightCache->GetEntry(uri, mReferrerPrincipal, mWithCredentials,
     937               0 :                               true);
     938               0 :   if (!entry) {
     939                 :     return;
     940                 :   }
     941                 : 
     942                 :   // The "Access-Control-Allow-Methods" header contains a comma separated
     943                 :   // list of method names.
     944               0 :   http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
     945               0 :                           headerVal);
     946                 : 
     947               0 :   nsCCharSeparatedTokenizer methods(headerVal, ',');
     948               0 :   while(methods.hasMoreTokens()) {
     949               0 :     const nsDependentCSubstring& method = methods.nextToken();
     950               0 :     if (method.IsEmpty()) {
     951               0 :       continue;
     952                 :     }
     953                 :     PRUint32 i;
     954               0 :     for (i = 0; i < entry->mMethods.Length(); ++i) {
     955               0 :       if (entry->mMethods[i].token.Equals(method)) {
     956               0 :         entry->mMethods[i].expirationTime = expirationTime;
     957               0 :         break;
     958                 :       }
     959                 :     }
     960               0 :     if (i == entry->mMethods.Length()) {
     961                 :       nsPreflightCache::TokenTime* newMethod =
     962               0 :         entry->mMethods.AppendElement();
     963               0 :       if (!newMethod) {
     964                 :         return;
     965                 :       }
     966                 : 
     967               0 :       newMethod->token = method;
     968               0 :       newMethod->expirationTime = expirationTime;
     969                 :     }
     970                 :   }
     971                 : 
     972                 :   // The "Access-Control-Allow-Headers" header contains a comma separated
     973                 :   // list of method names.
     974               0 :   http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
     975               0 :                           headerVal);
     976                 : 
     977               0 :   nsCCharSeparatedTokenizer headers(headerVal, ',');
     978               0 :   while(headers.hasMoreTokens()) {
     979               0 :     const nsDependentCSubstring& header = headers.nextToken();
     980               0 :     if (header.IsEmpty()) {
     981               0 :       continue;
     982                 :     }
     983                 :     PRUint32 i;
     984               0 :     for (i = 0; i < entry->mHeaders.Length(); ++i) {
     985               0 :       if (entry->mHeaders[i].token.Equals(header)) {
     986               0 :         entry->mHeaders[i].expirationTime = expirationTime;
     987               0 :         break;
     988                 :       }
     989                 :     }
     990               0 :     if (i == entry->mHeaders.Length()) {
     991                 :       nsPreflightCache::TokenTime* newHeader =
     992               0 :         entry->mHeaders.AppendElement();
     993               0 :       if (!newHeader) {
     994                 :         return;
     995                 :       }
     996                 : 
     997               0 :       newHeader->token = header;
     998               0 :       newHeader->expirationTime = expirationTime;
     999                 :     }
    1000                 :   }
    1001                 : }
    1002                 : 
    1003                 : NS_IMETHODIMP
    1004               0 : nsCORSPreflightListener::OnStartRequest(nsIRequest *aRequest,
    1005                 :                                         nsISupports *aContext)
    1006                 : {
    1007                 :   nsresult status;
    1008               0 :   nsresult rv = aRequest->GetStatus(&status);
    1009                 : 
    1010               0 :   if (NS_SUCCEEDED(rv)) {
    1011               0 :     rv = status;
    1012                 :   }
    1013                 : 
    1014               0 :   if (NS_SUCCEEDED(rv)) {
    1015                 :     // Everything worked, try to cache and then fire off the actual request.
    1016               0 :     AddResultToCache(aRequest);
    1017                 : 
    1018               0 :     rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
    1019                 :   }
    1020                 : 
    1021               0 :   if (NS_FAILED(rv)) {
    1022               0 :     mOuterChannel->Cancel(rv);
    1023               0 :     mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
    1024               0 :     mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
    1025                 :     
    1026               0 :     return rv;
    1027                 :   }
    1028                 : 
    1029               0 :   return NS_OK;
    1030                 : }
    1031                 : 
    1032                 : NS_IMETHODIMP
    1033               0 : nsCORSPreflightListener::OnStopRequest(nsIRequest *aRequest,
    1034                 :                                        nsISupports *aContext,
    1035                 :                                        nsresult aStatus)
    1036                 : {
    1037               0 :   mOuterChannel = nsnull;
    1038               0 :   mOuterListener = nsnull;
    1039               0 :   mOuterContext = nsnull;
    1040               0 :   return NS_OK;
    1041                 : }
    1042                 : 
    1043                 : /** nsIStreamListener methods **/
    1044                 : 
    1045                 : NS_IMETHODIMP
    1046               0 : nsCORSPreflightListener::OnDataAvailable(nsIRequest *aRequest,
    1047                 :                                          nsISupports *ctxt,
    1048                 :                                          nsIInputStream *inStr,
    1049                 :                                          PRUint32 sourceOffset,
    1050                 :                                          PRUint32 count)
    1051                 : {
    1052                 :   PRUint32 totalRead;
    1053               0 :   return inStr->ReadSegments(NS_DiscardSegment, nsnull, count, &totalRead);
    1054                 : }
    1055                 : 
    1056                 : NS_IMETHODIMP
    1057               0 : nsCORSPreflightListener::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
    1058                 :                                                 nsIChannel *aNewChannel,
    1059                 :                                                 PRUint32 aFlags,
    1060                 :                                                 nsIAsyncVerifyRedirectCallback *callback)
    1061                 : {
    1062                 :   // Only internal redirects allowed for now.
    1063               0 :   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags))
    1064               0 :     return NS_ERROR_DOM_BAD_URI;
    1065                 : 
    1066               0 :   callback->OnRedirectVerifyCallback(NS_OK);
    1067               0 :   return NS_OK;
    1068                 : }
    1069                 : 
    1070                 : NS_IMETHODIMP
    1071               0 : nsCORSPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
    1072                 : {
    1073               0 :   return QueryInterface(aIID, aResult);
    1074                 : }
    1075                 : 
    1076                 : 
    1077                 : nsresult
    1078               0 : NS_StartCORSPreflight(nsIChannel* aRequestChannel,
    1079                 :                       nsIStreamListener* aListener,
    1080                 :                       nsIPrincipal* aPrincipal,
    1081                 :                       bool aWithCredentials,
    1082                 :                       nsTArray<nsCString>& aUnsafeHeaders,
    1083                 :                       nsIChannel** aPreflightChannel)
    1084                 : {
    1085               0 :   *aPreflightChannel = nsnull;
    1086                 : 
    1087               0 :   nsCAutoString method;
    1088               0 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel));
    1089               0 :   NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED);
    1090               0 :   httpChannel->GetRequestMethod(method);
    1091                 : 
    1092               0 :   nsCOMPtr<nsIURI> uri;
    1093               0 :   nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(uri));
    1094               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1095                 : 
    1096                 :   nsPreflightCache::CacheEntry* entry =
    1097                 :     sPreflightCache ?
    1098               0 :     sPreflightCache->GetEntry(uri, aPrincipal, aWithCredentials, false) :
    1099               0 :     nsnull;
    1100                 : 
    1101               0 :   if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
    1102                 :     // We have a cached preflight result, just start the original channel
    1103               0 :     return aRequestChannel->AsyncOpen(aListener, nsnull);
    1104                 :   }
    1105                 : 
    1106                 :   // Either it wasn't cached or the cached result has expired. Build a
    1107                 :   // channel for the OPTIONS request.
    1108                 : 
    1109               0 :   nsCOMPtr<nsILoadGroup> loadGroup;
    1110               0 :   rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    1111               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1112                 : 
    1113                 :   nsLoadFlags loadFlags;
    1114               0 :   rv = aRequestChannel->GetLoadFlags(&loadFlags);
    1115               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1116                 : 
    1117               0 :   nsCOMPtr<nsIChannel> preflightChannel;
    1118               0 :   rv = NS_NewChannel(getter_AddRefs(preflightChannel), uri, nsnull,
    1119               0 :                      loadGroup, nsnull, loadFlags);
    1120               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1121                 : 
    1122               0 :   nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
    1123               0 :   NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");
    1124                 : 
    1125               0 :   rv = preHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
    1126               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1127                 :   
    1128                 :   // Set up listener which will start the original channel
    1129                 :   nsCOMPtr<nsIStreamListener> preflightListener =
    1130                 :     new nsCORSPreflightListener(aRequestChannel, aListener, nsnull, aPrincipal,
    1131               0 :                                 method, aWithCredentials);
    1132               0 :   NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
    1133                 : 
    1134                 :   preflightListener =
    1135                 :     new nsCORSListenerProxy(preflightListener, aPrincipal,
    1136                 :                             preflightChannel, aWithCredentials,
    1137               0 :                             method, aUnsafeHeaders, &rv);
    1138               0 :   NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
    1139               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1140                 : 
    1141                 :   // Start preflight
    1142               0 :   rv = preflightChannel->AsyncOpen(preflightListener, nsnull);
    1143               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1144                 :   
    1145                 :   // Return newly created preflight channel
    1146               0 :   preflightChannel.forget(aPreflightChannel);
    1147                 : 
    1148               0 :   return NS_OK;
    1149                 : }
    1150                 : 

Generated by: LCOV version 1.7