LCOV - code coverage report
Current view: top level - dom/xhr - XMLHttpRequestMainThread.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 589 2067 28.5 %
Date: 2017-07-14 16:53:18 Functions: 61 187 32.6 %
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 "XMLHttpRequestMainThread.h"
       8             : 
       9             : #include <algorithm>
      10             : #ifndef XP_WIN
      11             : #include <unistd.h>
      12             : #endif
      13             : #include "mozilla/ArrayUtils.h"
      14             : #include "mozilla/CheckedInt.h"
      15             : #include "mozilla/dom/BlobBinding.h"
      16             : #include "mozilla/dom/BlobSet.h"
      17             : #include "mozilla/dom/DocGroup.h"
      18             : #include "mozilla/dom/DOMString.h"
      19             : #include "mozilla/dom/File.h"
      20             : #include "mozilla/dom/FileBinding.h"
      21             : #include "mozilla/dom/FileCreatorHelper.h"
      22             : #include "mozilla/dom/FetchUtil.h"
      23             : #include "mozilla/dom/FormData.h"
      24             : #include "mozilla/dom/MutableBlobStorage.h"
      25             : #include "mozilla/dom/XMLDocument.h"
      26             : #include "mozilla/dom/URLSearchParams.h"
      27             : #include "mozilla/dom/PromiseNativeHandler.h"
      28             : #include "mozilla/Encoding.h"
      29             : #include "mozilla/EventDispatcher.h"
      30             : #include "mozilla/EventListenerManager.h"
      31             : #include "mozilla/EventStateManager.h"
      32             : #include "mozilla/LoadInfo.h"
      33             : #include "mozilla/LoadContext.h"
      34             : #include "mozilla/MemoryReporting.h"
      35             : #include "nsIDOMDocument.h"
      36             : #include "mozilla/dom/ProgressEvent.h"
      37             : #include "nsIJARChannel.h"
      38             : #include "nsIJARURI.h"
      39             : #include "nsLayoutCID.h"
      40             : #include "nsReadableUtils.h"
      41             : 
      42             : #include "nsIURI.h"
      43             : #include "nsILoadGroup.h"
      44             : #include "nsNetUtil.h"
      45             : #include "nsStringStream.h"
      46             : #include "nsIAuthPrompt.h"
      47             : #include "nsIAuthPrompt2.h"
      48             : #include "nsIOutputStream.h"
      49             : #include "nsISupportsPrimitives.h"
      50             : #include "nsISupportsPriority.h"
      51             : #include "nsIInterfaceRequestorUtils.h"
      52             : #include "nsStreamUtils.h"
      53             : #include "nsThreadUtils.h"
      54             : #include "nsIUploadChannel.h"
      55             : #include "nsIUploadChannel2.h"
      56             : #include "nsIDOMSerializer.h"
      57             : #include "nsXPCOM.h"
      58             : #include "nsIDOMEventListener.h"
      59             : #include "nsIScriptSecurityManager.h"
      60             : #include "nsIVariant.h"
      61             : #include "nsVariant.h"
      62             : #include "nsIScriptError.h"
      63             : #include "nsIStreamConverterService.h"
      64             : #include "nsICachingChannel.h"
      65             : #include "nsContentUtils.h"
      66             : #include "nsCycleCollectionParticipant.h"
      67             : #include "nsError.h"
      68             : #include "nsIHTMLDocument.h"
      69             : #include "nsIStorageStream.h"
      70             : #include "nsIPromptFactory.h"
      71             : #include "nsIWindowWatcher.h"
      72             : #include "nsIConsoleService.h"
      73             : #include "nsIContentSecurityPolicy.h"
      74             : #include "nsAsyncRedirectVerifyHelper.h"
      75             : #include "nsStringBuffer.h"
      76             : #include "nsIFileChannel.h"
      77             : #include "mozilla/Telemetry.h"
      78             : #include "jsfriendapi.h"
      79             : #include "GeckoProfiler.h"
      80             : #include "mozilla/dom/XMLHttpRequestBinding.h"
      81             : #include "mozilla/Attributes.h"
      82             : #include "MultipartBlobImpl.h"
      83             : #include "nsIPermissionManager.h"
      84             : #include "nsMimeTypes.h"
      85             : #include "nsIHttpChannelInternal.h"
      86             : #include "nsIClassOfService.h"
      87             : #include "nsCharSeparatedTokenizer.h"
      88             : #include "nsStreamListenerWrapper.h"
      89             : #include "xpcjsid.h"
      90             : #include "nsITimedChannel.h"
      91             : #include "nsWrapperCacheInlines.h"
      92             : #include "nsZipArchive.h"
      93             : #include "mozilla/Preferences.h"
      94             : #include "private/pprio.h"
      95             : #include "XMLHttpRequestUpload.h"
      96             : 
      97             : // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
      98             : // replaced by FileCreatorHelper#CreateFileW.
      99             : #ifdef CreateFile
     100             : #undef CreateFile
     101             : #endif
     102             : 
     103             : using namespace mozilla::net;
     104             : 
     105             : namespace mozilla {
     106             : namespace dom {
     107             : 
     108             : // Maximum size that we'll grow an ArrayBuffer instead of doubling,
     109             : // once doubling reaches this threshold
     110             : const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH = 32*1024*1024;
     111             : // start at 32k to avoid lots of doubling right at the start
     112             : const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE = 32*1024;
     113             : // the maximum Content-Length that we'll preallocate.  1GB.  Must fit
     114             : // in an int32_t!
     115             : const int32_t XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE = 1*1024*1024*1024LL;
     116             : 
     117             : namespace {
     118           3 :   const nsLiteralString ProgressEventTypeStrings[] = {
     119             :     NS_LITERAL_STRING("loadstart"),
     120             :     NS_LITERAL_STRING("progress"),
     121             :     NS_LITERAL_STRING("error"),
     122             :     NS_LITERAL_STRING("abort"),
     123             :     NS_LITERAL_STRING("timeout"),
     124             :     NS_LITERAL_STRING("load"),
     125             :     NS_LITERAL_STRING("loadend")
     126           3 :   };
     127             :   static_assert(MOZ_ARRAY_LENGTH(ProgressEventTypeStrings) ==
     128             :                   size_t(XMLHttpRequestMainThread::ProgressEventType::ENUM_MAX),
     129             :                 "Mismatched lengths for ProgressEventTypeStrings and ProgressEventType enums");
     130             : 
     131           3 :   const nsString kLiteralString_readystatechange = NS_LITERAL_STRING("readystatechange");
     132           3 :   const nsString kLiteralString_xmlhttprequest = NS_LITERAL_STRING("xmlhttprequest");
     133           3 :   const nsString kLiteralString_DOMContentLoaded = NS_LITERAL_STRING("DOMContentLoaded");
     134             : }
     135             : 
     136             : // CIDs
     137             : #define NS_BADCERTHANDLER_CONTRACTID \
     138             :   "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
     139             : 
     140             : #define NS_PROGRESS_EVENT_INTERVAL 50
     141             : #define MAX_SYNC_TIMEOUT_WHEN_UNLOADING 10000 /* 10 secs */
     142             : 
     143           0 : NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
     144             : 
     145           0 : class nsResumeTimeoutsEvent : public Runnable
     146             : {
     147             : public:
     148           0 :   explicit nsResumeTimeoutsEvent(nsPIDOMWindowInner* aWindow)
     149           0 :     : Runnable("dom::nsResumeTimeoutsEvent")
     150           0 :     , mWindow(aWindow)
     151             :   {
     152           0 :   }
     153             : 
     154           0 :   NS_IMETHOD Run() override
     155             :   {
     156           0 :     mWindow->Resume();
     157           0 :     return NS_OK;
     158             :   }
     159             : 
     160             : private:
     161             :   nsCOMPtr<nsPIDOMWindowInner> mWindow;
     162             : };
     163             : 
     164             : 
     165             : // This helper function adds the given load flags to the request's existing
     166             : // load flags.
     167           4 : static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
     168             : {
     169             :   nsLoadFlags flags;
     170           4 :   request->GetLoadFlags(&flags);
     171           4 :   flags |= newFlags;
     172           4 :   request->SetLoadFlags(flags);
     173           4 : }
     174             : 
     175             : /////////////////////////////////////////////
     176             : //
     177             : //
     178             : /////////////////////////////////////////////
     179             : 
     180             : bool
     181             : XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false;
     182             : 
     183           3 : XMLHttpRequestMainThread::XMLHttpRequestMainThread()
     184             :   : mResponseBodyDecodedPos(0),
     185             :     mResponseCharset(nullptr),
     186             :     mResponseType(XMLHttpRequestResponseType::_empty),
     187             :     mRequestObserver(nullptr),
     188             :     mState(State::unsent),
     189             :     mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false),
     190             :     mFlagSyncLooping(false), mFlagBackgroundRequest(false),
     191             :     mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
     192             :     mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
     193             :     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     194             :     mProgressSinceLastProgressEvent(false),
     195             :     mRequestSentTime(0), mTimeoutMilliseconds(0),
     196             :     mErrorLoad(ErrorType::eOK), mErrorParsingXML(false),
     197             :     mWaitingForOnStopRequest(false),
     198             :     mProgressTimerIsActive(false),
     199             :     mIsHtml(false),
     200             :     mWarnAboutSyncHtml(false),
     201             :     mLoadTotal(-1),
     202             :     mIsSystem(false),
     203             :     mIsAnon(false),
     204             :     mFirstStartRequestSeen(false),
     205             :     mInLoadProgressEvent(false),
     206           6 :     mResultJSON(JS::UndefinedValue()),
     207             :     mResultArrayBuffer(nullptr),
     208             :     mIsMappedArrayBuffer(false),
     209             :     mXPCOMifier(nullptr),
     210           9 :     mEventDispatchingSuspended(false)
     211             : {
     212           3 :   mozilla::HoldJSObjects(this);
     213           3 : }
     214             : 
     215           0 : XMLHttpRequestMainThread::~XMLHttpRequestMainThread()
     216             : {
     217           0 :   mFlagDeleted = true;
     218             : 
     219           0 :   if ((mState == State::opened && mFlagSend) ||
     220           0 :       mState == State::loading) {
     221           0 :     Abort();
     222             :   }
     223             : 
     224           0 :   MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
     225           0 :   mFlagSyncLooping = false;
     226             : 
     227           0 :   mResultJSON.setUndefined();
     228           0 :   mResultArrayBuffer = nullptr;
     229           0 :   mozilla::DropJSObjects(this);
     230           0 : }
     231             : 
     232             : /**
     233             :  * This Init method is called from the factory constructor.
     234             :  */
     235             : nsresult
     236           0 : XMLHttpRequestMainThread::Init()
     237             : {
     238           0 :   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
     239           0 :   nsCOMPtr<nsIPrincipal> subjectPrincipal;
     240           0 :   if (secMan) {
     241           0 :     secMan->GetSystemPrincipal(getter_AddRefs(subjectPrincipal));
     242             :   }
     243           0 :   NS_ENSURE_STATE(subjectPrincipal);
     244             : 
     245             :   // Instead of grabbing some random global from the context stack,
     246             :   // let's use the default one (junk scope) for now.
     247             :   // We should move away from this Init...
     248           0 :   Construct(subjectPrincipal, xpc::NativeGlobal(xpc::PrivilegedJunkScope()));
     249           0 :   return NS_OK;
     250             : }
     251             : 
     252             : /**
     253             :  * This Init method should only be called by C++ consumers.
     254             :  */
     255             : NS_IMETHODIMP
     256           0 : XMLHttpRequestMainThread::Init(nsIPrincipal* aPrincipal,
     257             :                                nsIGlobalObject* aGlobalObject,
     258             :                                nsIURI* aBaseURI,
     259             :                                nsILoadGroup* aLoadGroup)
     260             : {
     261           0 :   NS_ENSURE_ARG_POINTER(aPrincipal);
     262           0 :   Construct(aPrincipal, aGlobalObject, aBaseURI, aLoadGroup);
     263           0 :   return NS_OK;
     264             : }
     265             : 
     266             : void
     267           0 : XMLHttpRequestMainThread::InitParameters(bool aAnon, bool aSystem)
     268             : {
     269           0 :   if (!aAnon && !aSystem) {
     270           0 :     return;
     271             :   }
     272             : 
     273             :   // Check for permissions.
     274             :   // Chrome is always allowed access, so do the permission check only
     275             :   // for non-chrome pages.
     276           0 :   if (!IsSystemXHR() && aSystem) {
     277           0 :     nsIGlobalObject* global = GetOwnerGlobal();
     278           0 :     if (NS_WARN_IF(!global)) {
     279           0 :       SetParameters(aAnon, false);
     280           0 :       return;
     281             :     }
     282             : 
     283           0 :     nsIPrincipal* principal = global->PrincipalOrNull();
     284           0 :     if (NS_WARN_IF(!principal)) {
     285           0 :       SetParameters(aAnon, false);
     286           0 :       return;
     287             :     }
     288             : 
     289             :     nsCOMPtr<nsIPermissionManager> permMgr =
     290           0 :       services::GetPermissionManager();
     291           0 :     if (NS_WARN_IF(!permMgr)) {
     292           0 :       SetParameters(aAnon, false);
     293           0 :       return;
     294             :     }
     295             : 
     296             :     uint32_t permission;
     297             :     nsresult rv =
     298           0 :       permMgr->TestPermissionFromPrincipal(principal, "systemXHR", &permission);
     299           0 :     if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
     300           0 :       SetParameters(aAnon, false);
     301           0 :       return;
     302             :     }
     303             :   }
     304             : 
     305           0 :   SetParameters(aAnon, aSystem);
     306             : }
     307             : 
     308             : void
     309           7 : XMLHttpRequestMainThread::ResetResponse()
     310             : {
     311           7 :   mResponseXML = nullptr;
     312           7 :   mResponseBody.Truncate();
     313           7 :   TruncateResponseText();
     314           7 :   mResponseBlob = nullptr;
     315           7 :   mBlobStorage = nullptr;
     316           7 :   mBlobSet = nullptr;
     317           7 :   mResultArrayBuffer = nullptr;
     318           7 :   mArrayBufferBuilder.reset();
     319           7 :   mResultJSON.setUndefined();
     320           7 :   mLoadTransferred = 0;
     321           7 :   mResponseBodyDecodedPos = 0;
     322           7 : }
     323             : 
     324             : void
     325           0 : XMLHttpRequestMainThread::SetRequestObserver(nsIRequestObserver* aObserver)
     326             : {
     327           0 :   mRequestObserver = aObserver;
     328           0 : }
     329             : 
     330             : NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestMainThread)
     331             : 
     332           3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestMainThread,
     333             :                                                   XMLHttpRequestEventTarget)
     334           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
     335           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
     336           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
     337             : 
     338           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
     339             : 
     340           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
     341           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
     342             : 
     343           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
     344           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
     345             : 
     346           3 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
     347           3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     348             : 
     349           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread,
     350             :                                                 XMLHttpRequestEventTarget)
     351           0 :   tmp->mResultArrayBuffer = nullptr;
     352           0 :   tmp->mArrayBufferBuilder.reset();
     353           0 :   tmp->mResultJSON.setUndefined();
     354           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
     355           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
     356           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
     357             : 
     358           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
     359             : 
     360           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
     361           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)
     362             : 
     363           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
     364           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
     365             : 
     366           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
     367           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     368             : 
     369           7 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestMainThread,
     370             :                                                XMLHttpRequestEventTarget)
     371           7 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
     372           7 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON)
     373           7 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     374             : 
     375             : bool
     376           0 : XMLHttpRequestMainThread::IsCertainlyAliveForCC() const
     377             : {
     378           0 :   return mWaitingForOnStopRequest;
     379             : }
     380             : 
     381             : // QueryInterface implementation for XMLHttpRequestMainThread
     382         291 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XMLHttpRequestMainThread)
     383         259 :   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
     384         259 :   NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
     385         259 :   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
     386         259 :   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
     387         257 :   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
     388         257 :   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
     389         257 :   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
     390         251 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     391         251 :   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
     392         251 :   NS_INTERFACE_MAP_ENTRY(nsINamed)
     393         251 :   NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget)
     394         251 : NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget)
     395             : 
     396         335 : NS_IMPL_ADDREF_INHERITED(XMLHttpRequestMainThread, XMLHttpRequestEventTarget)
     397         329 : NS_IMPL_RELEASE_INHERITED(XMLHttpRequestMainThread, XMLHttpRequestEventTarget)
     398             : 
     399           0 : NS_IMPL_EVENT_HANDLER(XMLHttpRequestMainThread, readystatechange)
     400             : 
     401             : void
     402           0 : XMLHttpRequestMainThread::DisconnectFromOwner()
     403             : {
     404           0 :   XMLHttpRequestEventTarget::DisconnectFromOwner();
     405           0 :   Abort();
     406           0 : }
     407             : 
     408             : size_t
     409           0 : XMLHttpRequestMainThread::SizeOfEventTargetIncludingThis(
     410             :   MallocSizeOf aMallocSizeOf) const
     411             : {
     412           0 :   size_t n = aMallocSizeOf(this);
     413           0 :   n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
     414             : 
     415             :   // Why is this safe?  Because no-one else will report this string.  The
     416             :   // other possible sharers of this string are as follows.
     417             :   //
     418             :   // - The JS engine could hold copies if the JS code holds references, e.g.
     419             :   //   |var text = XHR.responseText|.  However, those references will be via JS
     420             :   //   external strings, for which the JS memory reporter does *not* report the
     421             :   //   chars.
     422             :   //
     423             :   // - Binary extensions, but they're *extremely* unlikely to do any memory
     424             :   //   reporting.
     425             :   //
     426           0 :   n += mResponseText.SizeOfThis(aMallocSizeOf);
     427             : 
     428           0 :   return n;
     429             : 
     430             :   // Measurement of the following members may be added later if DMD finds it is
     431             :   // worthwhile:
     432             :   // - lots
     433             : }
     434             : 
     435             : NS_IMETHODIMP
     436           0 : XMLHttpRequestMainThread::GetChannel(nsIChannel **aChannel)
     437             : {
     438           0 :   NS_ENSURE_ARG_POINTER(aChannel);
     439           0 :   NS_IF_ADDREF(*aChannel = mChannel);
     440             : 
     441           0 :   return NS_OK;
     442             : }
     443             : 
     444           0 : static void LogMessage(const char* aWarning, nsPIDOMWindowInner* aWindow,
     445             :                        const char16_t** aParams=nullptr, uint32_t aParamCount=0)
     446             : {
     447           0 :   nsCOMPtr<nsIDocument> doc;
     448           0 :   if (aWindow) {
     449           0 :     doc = aWindow->GetExtantDoc();
     450             :   }
     451           0 :   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
     452           0 :                                   NS_LITERAL_CSTRING("DOM"), doc,
     453             :                                   nsContentUtils::eDOM_PROPERTIES,
     454           0 :                                   aWarning, aParams, aParamCount);
     455           0 : }
     456             : 
     457             : NS_IMETHODIMP
     458           0 : XMLHttpRequestMainThread::GetResponseXML(nsIDOMDocument **aResponseXML)
     459             : {
     460           0 :   ErrorResult rv;
     461           0 :   nsIDocument* responseXML = GetResponseXML(rv);
     462           0 :   if (rv.Failed()) {
     463           0 :     return rv.StealNSResult();
     464             :   }
     465             : 
     466           0 :   if (!responseXML) {
     467           0 :     *aResponseXML = nullptr;
     468           0 :     return NS_OK;
     469             :   }
     470             : 
     471           0 :   return CallQueryInterface(responseXML, aResponseXML);
     472             : }
     473             : 
     474             : nsIDocument*
     475           0 : XMLHttpRequestMainThread::GetResponseXML(ErrorResult& aRv)
     476             : {
     477           0 :   if (mResponseType != XMLHttpRequestResponseType::_empty &&
     478           0 :       mResponseType != XMLHttpRequestResponseType::Document) {
     479           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML);
     480           0 :     return nullptr;
     481             :   }
     482           0 :   if (mWarnAboutSyncHtml) {
     483           0 :     mWarnAboutSyncHtml = false;
     484           0 :     LogMessage("HTMLSyncXHRWarning", GetOwner());
     485             :   }
     486           0 :   if (mState != State::done) {
     487           0 :     return nullptr;
     488             :   }
     489           0 :   return mResponseXML;
     490             : }
     491             : 
     492             : /*
     493             :  * This piece copied from XMLDocument, we try to get the charset
     494             :  * from HTTP headers.
     495             :  */
     496             : nsresult
     497           2 : XMLHttpRequestMainThread::DetectCharset()
     498             : {
     499           2 :   mResponseCharset = nullptr;
     500           2 :   mDecoder = nullptr;
     501             : 
     502           4 :   if (mResponseType != XMLHttpRequestResponseType::_empty &&
     503           2 :       mResponseType != XMLHttpRequestResponseType::Text &&
     504           0 :       mResponseType != XMLHttpRequestResponseType::Json &&
     505           0 :       mResponseType != XMLHttpRequestResponseType::Moz_chunked_text) {
     506           0 :     return NS_OK;
     507             :   }
     508             : 
     509           4 :   nsAutoCString charsetVal;
     510             :   const Encoding* encoding;
     511           2 :   bool ok = mChannel &&
     512           4 :             NS_SUCCEEDED(mChannel->GetContentCharset(charsetVal)) &&
     513           4 :             (encoding = Encoding::ForLabel(charsetVal));
     514           2 :   if (!ok) {
     515             :     // MS documentation states UTF-8 is default for responseText
     516           2 :     encoding = UTF_8_ENCODING;
     517             :   }
     518             : 
     519           2 :   if (mResponseType == XMLHttpRequestResponseType::Json &&
     520           0 :       encoding != UTF_8_ENCODING) {
     521             :     // The XHR spec says only UTF-8 is supported for responseType == "json"
     522           0 :     LogMessage("JSONCharsetWarning", GetOwner());
     523           0 :     encoding = UTF_8_ENCODING;
     524             :   }
     525             : 
     526           2 :   mResponseCharset = encoding;
     527           2 :   mDecoder = encoding->NewDecoderWithBOMRemoval();
     528             : 
     529           2 :   return NS_OK;
     530             : }
     531             : 
     532             : nsresult
     533           3 : XMLHttpRequestMainThread::AppendToResponseText(const char * aSrcBuffer,
     534             :                                                uint32_t aSrcBufferLen)
     535             : {
     536           3 :   NS_ENSURE_STATE(mDecoder);
     537             : 
     538             :   CheckedInt<size_t> destBufferLen =
     539           3 :     mDecoder->MaxUTF16BufferLength(aSrcBufferLen);
     540           3 :   if (!destBufferLen.isValid()) {
     541           0 :     return NS_ERROR_OUT_OF_MEMORY;
     542             :   }
     543             : 
     544           3 :   CheckedInt32 size = mResponseText.Length();
     545           3 :   size += destBufferLen.value();
     546           3 :   if (!size.isValid()) {
     547           0 :     return NS_ERROR_OUT_OF_MEMORY;
     548             :   }
     549             : 
     550           6 :   XMLHttpRequestStringWriterHelper helper(mResponseText);
     551             : 
     552           3 :   if (!helper.AddCapacity(destBufferLen.value())) {
     553           0 :     return NS_ERROR_OUT_OF_MEMORY;
     554             :   }
     555             : 
     556             :   // XXX there's no handling for incomplete byte sequences on EOF!
     557             :   uint32_t result;
     558             :   size_t read;
     559             :   size_t written;
     560             :   bool hadErrors;
     561           6 :   Tie(result, read, written, hadErrors) = mDecoder->DecodeToUTF16(
     562             :     AsBytes(MakeSpan(aSrcBuffer, aSrcBufferLen)),
     563             :     MakeSpan(helper.EndOfExistingData(), destBufferLen.value()),
     564           3 :     false);
     565           3 :   MOZ_ASSERT(result == kInputEmpty);
     566           3 :   MOZ_ASSERT(read == aSrcBufferLen);
     567           3 :   MOZ_ASSERT(written <= destBufferLen.value());
     568             :   Unused << hadErrors;
     569           3 :   helper.AddLength(written);
     570           3 :   return NS_OK;
     571             : }
     572             : 
     573             : NS_IMETHODIMP
     574           0 : XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText)
     575             : {
     576           0 :   ErrorResult rv;
     577           0 :   DOMString str;
     578           0 :   GetResponseText(str, rv);
     579           0 :   if (NS_WARN_IF(rv.Failed())) {
     580           0 :     return rv.StealNSResult();
     581             :   }
     582             : 
     583           0 :   str.ToString(aResponseText);
     584           0 :   return NS_OK;
     585             : }
     586             : 
     587             : void
     588           0 : XMLHttpRequestMainThread::GetResponseText(DOMString& aResponseText,
     589             :                                           ErrorResult& aRv)
     590             : {
     591           0 :   XMLHttpRequestStringSnapshot snapshot;
     592           0 :   GetResponseText(snapshot, aRv);
     593           0 :   if (aRv.Failed()) {
     594           0 :     return;
     595             :   }
     596             : 
     597           0 :   if (!snapshot.GetAsString(aResponseText)) {
     598           0 :     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     599           0 :     return;
     600             :   }
     601             : }
     602             : 
     603             : void
     604          17 : XMLHttpRequestMainThread::GetResponseText(XMLHttpRequestStringSnapshot& aSnapshot,
     605             :                                           ErrorResult& aRv)
     606             : {
     607          17 :   aSnapshot.Reset();
     608             : 
     609          31 :   if (mResponseType != XMLHttpRequestResponseType::_empty &&
     610          14 :       mResponseType != XMLHttpRequestResponseType::Text &&
     611           0 :       mResponseType != XMLHttpRequestResponseType::Moz_chunked_text) {
     612           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT);
     613           0 :     return;
     614             :   }
     615             : 
     616          17 :   if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text &&
     617           0 :       !mInLoadProgressEvent) {
     618           0 :     aSnapshot.SetVoid();
     619           0 :     return;
     620             :   }
     621             : 
     622          17 :   if (mState != State::loading && mState != State::done) {
     623           7 :     return;
     624             :   }
     625             : 
     626             :   // We only decode text lazily if we're also parsing to a doc.
     627             :   // Also, if we've decoded all current data already, then no need to decode
     628             :   // more.
     629          10 :   if ((!mResponseXML && !mErrorParsingXML) ||
     630           0 :       mResponseBodyDecodedPos == mResponseBody.Length()) {
     631          10 :     mResponseText.CreateSnapshot(aSnapshot);
     632          10 :     return;
     633             :   }
     634             : 
     635           0 :   MatchCharsetAndDecoderToResponseDocument();
     636             : 
     637           0 :   NS_ASSERTION(mResponseBodyDecodedPos < mResponseBody.Length(),
     638             :                "Unexpected mResponseBodyDecodedPos");
     639           0 :   aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
     640           0 :                              mResponseBody.Length() - mResponseBodyDecodedPos);
     641           0 :   if (aRv.Failed()) {
     642           0 :     return;
     643             :   }
     644             : 
     645           0 :   mResponseBodyDecodedPos = mResponseBody.Length();
     646             : 
     647           0 :   if (mState == State::done) {
     648             :     // Free memory buffer which we no longer need
     649           0 :     mResponseBody.Truncate();
     650           0 :     mResponseBodyDecodedPos = 0;
     651             :   }
     652             : 
     653           0 :   mResponseText.CreateSnapshot(aSnapshot);
     654             : }
     655             : 
     656             : nsresult
     657           0 : XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext* aCx)
     658             : {
     659           0 :   if (!aCx) {
     660           0 :     return NS_ERROR_FAILURE;
     661             :   }
     662             : 
     663           0 :   nsAutoString string;
     664           0 :   if (!mResponseText.GetAsString(string)) {
     665           0 :     return NS_ERROR_OUT_OF_MEMORY;
     666             :   }
     667             : 
     668             :   // The Unicode converter has already zapped the BOM if there was one
     669           0 :   JS::Rooted<JS::Value> value(aCx);
     670           0 :   if (!JS_ParseJSON(aCx, string.BeginReading(), string.Length(), &value)) {
     671           0 :     return NS_ERROR_FAILURE;
     672             :   }
     673             : 
     674           0 :   mResultJSON = value;
     675           0 :   return NS_OK;
     676             : }
     677             : 
     678             : void
     679           0 : XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv)
     680             : {
     681             :   // mBlobSet can be null if the request has been canceled
     682           0 :   if (!mBlobSet) {
     683           0 :     return;
     684             :   }
     685             : 
     686           0 :   nsAutoCString contentType;
     687           0 :   if (mState == State::done) {
     688           0 :     mChannel->GetContentType(contentType);
     689             :   }
     690             : 
     691           0 :   nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
     692             :   RefPtr<BlobImpl> blobImpl =
     693           0 :     MultipartBlobImpl::Create(Move(subImpls),
     694           0 :                               NS_ConvertASCIItoUTF16(contentType),
     695           0 :                               aRv);
     696           0 :   if (NS_WARN_IF(aRv.Failed())) {
     697           0 :     return;
     698             :   }
     699             : 
     700           0 :   mResponseBlob = Blob::Create(GetOwner(), blobImpl);
     701             : }
     702             : 
     703          17 : NS_IMETHODIMP XMLHttpRequestMainThread::GetResponseType(nsAString& aResponseType)
     704             : {
     705          17 :   MOZ_ASSERT(mResponseType < XMLHttpRequestResponseType::EndGuard_);
     706             :   const EnumEntry& entry =
     707          17 :     XMLHttpRequestResponseTypeValues::strings[static_cast<uint32_t>(mResponseType)];
     708          17 :   aResponseType.AssignASCII(entry.value, entry.length);
     709          17 :   return NS_OK;
     710             : }
     711             : 
     712           0 : NS_IMETHODIMP XMLHttpRequestMainThread::SetResponseType(const nsAString& aResponseType)
     713             : {
     714           0 :   uint32_t i = 0;
     715           0 :   for (const EnumEntry* entry = XMLHttpRequestResponseTypeValues::strings;
     716           0 :        entry->value; ++entry, ++i) {
     717           0 :     if (aResponseType.EqualsASCII(entry->value, entry->length)) {
     718           0 :       ErrorResult rv;
     719           0 :       SetResponseType(static_cast<XMLHttpRequestResponseType>(i), rv);
     720           0 :       return rv.StealNSResult();
     721             :     }
     722             :   }
     723             : 
     724           0 :   return NS_OK;
     725             : }
     726             : 
     727             : void
     728           5 : XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseType,
     729             :                                           ErrorResult& aRv)
     730             : {
     731           5 :   if (mState == State::loading || mState == State::done) {
     732           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
     733           0 :     return;
     734             :   }
     735             : 
     736             :   // sync request is not allowed setting responseType in window context
     737           5 :   if (HasOrHasHadOwner() && mState != State::unsent && mFlagSynchronous) {
     738           0 :     LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     739           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
     740           0 :     return;
     741             :   }
     742             : 
     743           5 :   if (mFlagSynchronous &&
     744           0 :       (aResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
     745             :        aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
     746           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC);
     747           0 :     return;
     748             :   }
     749             : 
     750             :   // We want to get rid of this moz-only types. Bug 1335365.
     751           5 :   if (aResponseType == XMLHttpRequestResponseType::Moz_blob) {
     752           0 :     Telemetry::Accumulate(Telemetry::MOZ_BLOB_IN_XHR, 1);
     753           5 :   } else if (aResponseType == XMLHttpRequestResponseType::Moz_chunked_text) {
     754           0 :     Telemetry::Accumulate(Telemetry::MOZ_CHUNKED_TEXT_IN_XHR, 1);
     755           5 :   } else if (aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
     756           0 :     Telemetry::Accumulate(Telemetry::MOZ_CHUNKED_ARRAYBUFFER_IN_XHR, 1);
     757             :   }
     758             : 
     759             :   // Set the responseType attribute's value to the given value.
     760           5 :   mResponseType = aResponseType;
     761             : }
     762             : 
     763             : NS_IMETHODIMP
     764           0 : XMLHttpRequestMainThread::GetResponse(JSContext *aCx, JS::MutableHandle<JS::Value> aResult)
     765             : {
     766           0 :   ErrorResult rv;
     767           0 :   GetResponse(aCx, aResult, rv);
     768           0 :   return rv.StealNSResult();
     769             : }
     770             : 
     771             : void
     772           0 : XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
     773             :                                       JS::MutableHandle<JS::Value> aResponse,
     774             :                                       ErrorResult& aRv)
     775             : {
     776           0 :   switch (mResponseType) {
     777             :   case XMLHttpRequestResponseType::_empty:
     778             :   case XMLHttpRequestResponseType::Text:
     779             :   case XMLHttpRequestResponseType::Moz_chunked_text:
     780             :   {
     781           0 :     DOMString str;
     782           0 :     GetResponseText(str, aRv);
     783           0 :     if (aRv.Failed()) {
     784           0 :       return;
     785             :     }
     786           0 :     if (!xpc::StringToJsval(aCx, str, aResponse)) {
     787           0 :       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     788             :     }
     789           0 :     return;
     790             :   }
     791             : 
     792             :   case XMLHttpRequestResponseType::Arraybuffer:
     793             :   case XMLHttpRequestResponseType::Moz_chunked_arraybuffer:
     794             :   {
     795           0 :     if (!(mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
     796           0 :           mState == State::done) &&
     797           0 :         !(mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer &&
     798           0 :           mInLoadProgressEvent)) {
     799           0 :       aResponse.setNull();
     800           0 :       return;
     801             :     }
     802             : 
     803           0 :     if (!mResultArrayBuffer) {
     804           0 :       mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
     805           0 :       if (!mResultArrayBuffer) {
     806           0 :         aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     807           0 :         return;
     808             :       }
     809             :     }
     810           0 :     aResponse.setObject(*mResultArrayBuffer);
     811           0 :     return;
     812             :   }
     813             :   case XMLHttpRequestResponseType::Blob:
     814             :   case XMLHttpRequestResponseType::Moz_blob:
     815             :   {
     816           0 :     if (mState != State::done) {
     817           0 :       if (mResponseType != XMLHttpRequestResponseType::Moz_blob) {
     818           0 :         aResponse.setNull();
     819           0 :         return;
     820             :       }
     821             : 
     822           0 :       if (!mResponseBlob) {
     823           0 :         CreatePartialBlob(aRv);
     824             :       }
     825             :     }
     826             : 
     827           0 :     if (!mResponseBlob) {
     828           0 :       aResponse.setNull();
     829           0 :       return;
     830             :     }
     831             : 
     832           0 :     GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
     833           0 :     return;
     834             :   }
     835             :   case XMLHttpRequestResponseType::Document:
     836             :   {
     837           0 :     if (!mResponseXML || mState != State::done) {
     838           0 :       aResponse.setNull();
     839           0 :       return;
     840             :     }
     841             : 
     842           0 :     aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
     843           0 :     return;
     844             :   }
     845             :   case XMLHttpRequestResponseType::Json:
     846             :   {
     847           0 :     if (mState != State::done) {
     848           0 :       aResponse.setNull();
     849           0 :       return;
     850             :     }
     851             : 
     852           0 :     if (mResultJSON.isUndefined()) {
     853           0 :       aRv = CreateResponseParsedJSON(aCx);
     854           0 :       TruncateResponseText();
     855           0 :       if (aRv.Failed()) {
     856             :         // Per spec, errors aren't propagated. null is returned instead.
     857           0 :         aRv = NS_OK;
     858             :         // It would be nice to log the error to the console. That's hard to
     859             :         // do without calling window.onerror as a side effect, though.
     860           0 :         JS_ClearPendingException(aCx);
     861           0 :         mResultJSON.setNull();
     862             :       }
     863             :     }
     864           0 :     aResponse.set(mResultJSON);
     865           0 :     return;
     866             :   }
     867             :   default:
     868           0 :     NS_ERROR("Should not happen");
     869             :   }
     870             : 
     871           0 :   aResponse.setNull();
     872             : }
     873             : 
     874             : bool
     875          62 : XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const
     876             : {
     877          62 :   if (!mChannel) {
     878           0 :     return false;
     879             :   }
     880             : 
     881         124 :   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
     882          62 :   MOZ_ASSERT(loadInfo);
     883             : 
     884          62 :   if (!loadInfo) {
     885           0 :     return false;
     886             :   }
     887             : 
     888          62 :   return loadInfo->GetTainting() == LoadTainting::CORS;
     889             : }
     890             : 
     891             : bool
     892          54 : XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest()
     893             : {
     894          54 :   if (IsCrossSiteCORSRequest()) {
     895             :     nsresult rv;
     896           0 :     mChannel->GetStatus(&rv);
     897           0 :     if (NS_FAILED(rv)) {
     898           0 :       return true;
     899             :     }
     900             :   }
     901          54 :   return false;
     902             : }
     903             : 
     904             : void
     905          17 : XMLHttpRequestMainThread::GetResponseURL(nsAString& aUrl)
     906             : {
     907          17 :   aUrl.Truncate();
     908             : 
     909          17 :   uint16_t readyState = ReadyState();
     910          17 :   if ((readyState == UNSENT || readyState == OPENED) || !mChannel) {
     911          10 :     return;
     912             :   }
     913             : 
     914             :   // Make sure we don't leak responseURL information from denied cross-site
     915             :   // requests.
     916          12 :   if (IsDeniedCrossSiteCORSRequest()) {
     917           0 :     return;
     918             :   }
     919             : 
     920          24 :   nsCOMPtr<nsIURI> responseUrl;
     921          12 :   mChannel->GetURI(getter_AddRefs(responseUrl));
     922             : 
     923          12 :   if (!responseUrl) {
     924           0 :     return;
     925             :   }
     926             : 
     927          24 :   nsAutoCString temp;
     928          12 :   responseUrl->GetSpecIgnoringRef(temp);
     929          12 :   CopyUTF8toUTF16(temp, aUrl);
     930             : }
     931             : 
     932             : NS_IMETHODIMP
     933          17 : XMLHttpRequestMainThread::GetStatus(uint32_t *aStatus)
     934             : {
     935          34 :   ErrorResult rv;
     936          17 :   *aStatus = GetStatus(rv);
     937          34 :   return rv.StealNSResult();
     938             : }
     939             : 
     940             : uint32_t
     941          17 : XMLHttpRequestMainThread::GetStatus(ErrorResult& aRv)
     942             : {
     943             :   // Make sure we don't leak status information from denied cross-site
     944             :   // requests.
     945          17 :   if (IsDeniedCrossSiteCORSRequest()) {
     946           0 :     return 0;
     947             :   }
     948             : 
     949          17 :   uint16_t readyState = ReadyState();
     950          17 :   if (readyState == UNSENT || readyState == OPENED) {
     951           5 :     return 0;
     952             :   }
     953             : 
     954          12 :   if (mErrorLoad != ErrorType::eOK) {
     955             :     // Let's simulate the http protocol for jar/app requests:
     956           0 :     nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
     957           0 :     if (jarChannel) {
     958             :       nsresult status;
     959           0 :       mChannel->GetStatus(&status);
     960             : 
     961           0 :       if (status == NS_ERROR_FILE_NOT_FOUND) {
     962           0 :         return 404; // Not Found
     963             :       } else {
     964           0 :         return 500; // Internal Error
     965             :       }
     966             :     }
     967             : 
     968           0 :     return 0;
     969             :   }
     970             : 
     971          24 :   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
     972          12 :   if (!httpChannel) {
     973             :     // Pretend like we got a 200 response, since our load was successful
     974          12 :     return 200;
     975             :   }
     976             : 
     977             :   uint32_t status;
     978           0 :   nsresult rv = httpChannel->GetResponseStatus(&status);
     979           0 :   if (NS_FAILED(rv)) {
     980           0 :     status = 0;
     981             :   }
     982             : 
     983           0 :   return status;
     984             : }
     985             : 
     986             : NS_IMETHODIMP
     987           0 : XMLHttpRequestMainThread::GetStatusText(nsACString& aOut)
     988             : {
     989           0 :   ErrorResult rv;
     990           0 :   GetStatusText(aOut, rv);
     991           0 :   return rv.StealNSResult();
     992             : }
     993             : 
     994             : void
     995          17 : XMLHttpRequestMainThread::GetStatusText(nsACString& aStatusText,
     996             :                                         ErrorResult& aRv)
     997             : {
     998             :   // Return an empty status text on all error loads.
     999          17 :   aStatusText.Truncate();
    1000             : 
    1001             :   // Make sure we don't leak status information from denied cross-site
    1002             :   // requests.
    1003          17 :   if (IsDeniedCrossSiteCORSRequest()) {
    1004           5 :     return;
    1005             :   }
    1006             : 
    1007             :   // Check the current XHR state to see if it is valid to obtain the statusText
    1008             :   // value.  This check is to prevent the status text for redirects from being
    1009             :   // available before all the redirects have been followed and HTTP headers have
    1010             :   // been received.
    1011          17 :   uint16_t readyState = ReadyState();
    1012          17 :   if (readyState == UNSENT || readyState == OPENED) {
    1013           5 :     return;
    1014             :   }
    1015             : 
    1016          12 :   if (mErrorLoad != ErrorType::eOK) {
    1017           0 :     return;
    1018             :   }
    1019             : 
    1020          24 :   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
    1021          12 :   if (httpChannel) {
    1022           0 :     Unused << httpChannel->GetResponseStatusText(aStatusText);
    1023             :   } else {
    1024          12 :     aStatusText.AssignLiteral("OK");
    1025             :   }
    1026             : }
    1027             : 
    1028             : void
    1029           3 : XMLHttpRequestMainThread::CloseRequest()
    1030             : {
    1031           3 :   mWaitingForOnStopRequest = false;
    1032           3 :   if (mChannel) {
    1033           0 :     mChannel->Cancel(NS_BINDING_ABORTED);
    1034             :   }
    1035           3 :   if (mTimeoutTimer) {
    1036           0 :     mTimeoutTimer->Cancel();
    1037             :   }
    1038           3 : }
    1039             : 
    1040             : void
    1041           0 : XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType)
    1042             : {
    1043           0 :   CloseRequest();
    1044             : 
    1045           0 :   ResetResponse();
    1046             : 
    1047             :   // If we're in the destructor, don't risk dispatching an event.
    1048           0 :   if (mFlagDeleted) {
    1049           0 :     mFlagSyncLooping = false;
    1050           0 :     return;
    1051             :   }
    1052             : 
    1053           0 :   if (mState != State::unsent &&
    1054           0 :       !(mState == State::opened && !mFlagSend) &&
    1055           0 :       mState != State::done) {
    1056           0 :     ChangeState(State::done, true);
    1057             : 
    1058           0 :     if (!mFlagSyncLooping) {
    1059           0 :       if (mUpload && !mUploadComplete) {
    1060           0 :         mUploadComplete = true;
    1061           0 :         DispatchProgressEvent(mUpload, aType, 0, -1);
    1062             :       }
    1063           0 :       DispatchProgressEvent(this, aType, 0, -1);
    1064             :     }
    1065             :   }
    1066             : 
    1067             :   // The ChangeState call above calls onreadystatechange handlers which
    1068             :   // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
    1069             :   // the abort state bit. If this occurs we're not uninitialized (bug 361773).
    1070           0 :   if (mFlagAborted) {
    1071           0 :     ChangeState(State::unsent, false);  // IE seems to do it
    1072             :   }
    1073             : 
    1074           0 :   mFlagSyncLooping = false;
    1075             : }
    1076             : 
    1077             : void
    1078           0 : XMLHttpRequestMainThread::RequestErrorSteps(const ProgressEventType aEventType,
    1079             :                                             const nsresult aOptionalException,
    1080             :                                             ErrorResult& aRv)
    1081             : {
    1082             :   // Step 1
    1083           0 :   mState = State::done;
    1084             : 
    1085           0 :   StopProgressEventTimer();
    1086             : 
    1087             :   // Step 2
    1088           0 :   mFlagSend = false;
    1089             : 
    1090             :   // Step 3
    1091           0 :   ResetResponse();
    1092             : 
    1093             :   // If we're in the destructor, don't risk dispatching an event.
    1094           0 :   if (mFlagDeleted) {
    1095           0 :     mFlagSyncLooping = false;
    1096           0 :     return;
    1097             :   }
    1098             : 
    1099             :   // Step 4
    1100           0 :   if (mFlagSynchronous && NS_FAILED(aOptionalException)) {
    1101           0 :     aRv.Throw(aOptionalException);
    1102           0 :     return;
    1103             :   }
    1104             : 
    1105             :   // Step 5
    1106           0 :   FireReadystatechangeEvent();
    1107             : 
    1108             :   // Step 6
    1109           0 :   if (mUpload) {
    1110             : 
    1111             :     // Step 6-1
    1112           0 :     if (!mUploadComplete) {
    1113           0 :       mUploadComplete = true;
    1114             :     }
    1115             : 
    1116             :     // Step 6-2
    1117           0 :     if (mFlagHadUploadListenersOnSend) {
    1118             : 
    1119             :       // Steps 6-3, 6-4 (loadend is fired for us)
    1120           0 :       DispatchProgressEvent(mUpload, aEventType, 0, -1);
    1121             :     }
    1122             :   }
    1123             : 
    1124             :   // Steps 7 and 8 (loadend is fired for us)
    1125           0 :   DispatchProgressEvent(this, aEventType, 0, -1);
    1126             : }
    1127             : 
    1128             : void
    1129           0 : XMLHttpRequestMainThread::Abort(ErrorResult& aRv)
    1130             : {
    1131           0 :   mFlagAborted = true;
    1132             : 
    1133             :   // Step 1
    1134           0 :   CloseRequest();
    1135             : 
    1136             :   // Step 2
    1137           0 :   if ((mState == State::opened && mFlagSend) ||
    1138           0 :        mState == State::headers_received ||
    1139           0 :        mState == State::loading) {
    1140           0 :     RequestErrorSteps(ProgressEventType::abort, NS_OK, aRv);
    1141             :   }
    1142             : 
    1143             :   // Step 3
    1144           0 :   if (mState == State::done) {
    1145           0 :     ChangeState(State::unsent, false); // no ReadystateChange event
    1146             :   }
    1147             : 
    1148           0 :   mFlagSyncLooping = false;
    1149           0 : }
    1150             : 
    1151             : NS_IMETHODIMP
    1152           0 : XMLHttpRequestMainThread::SlowAbort()
    1153             : {
    1154           0 :   Abort();
    1155           0 :   return NS_OK;
    1156             : }
    1157             : 
    1158             : /*Method that checks if it is safe to expose a header value to the client.
    1159             : It is used to check what headers are exposed for CORS requests.*/
    1160             : bool
    1161           0 : XMLHttpRequestMainThread::IsSafeHeader(const nsACString& aHeader,
    1162             :                                        NotNull<nsIHttpChannel*> aHttpChannel) const
    1163             : {
    1164             :   // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
    1165           0 :   if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(aHeader)) {
    1166           0 :     NS_WARNING("blocked access to response header");
    1167           0 :     return false;
    1168             :   }
    1169             :   // if this is not a CORS call all headers are safe
    1170           0 :   if (!IsCrossSiteCORSRequest()) {
    1171           0 :     return true;
    1172             :   }
    1173             :   // Check for dangerous headers
    1174             :   // Make sure we don't leak header information from denied cross-site
    1175             :   // requests.
    1176           0 :   if (mChannel) {
    1177             :     nsresult status;
    1178           0 :     mChannel->GetStatus(&status);
    1179           0 :     if (NS_FAILED(status)) {
    1180           0 :       return false;
    1181             :     }
    1182             :   }
    1183             :   const char* kCrossOriginSafeHeaders[] = {
    1184             :     "cache-control", "content-language", "content-type", "expires",
    1185             :     "last-modified", "pragma"
    1186           0 :   };
    1187           0 :   for (uint32_t i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
    1188           0 :     if (aHeader.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
    1189           0 :       return true;
    1190             :     }
    1191             :   }
    1192           0 :   nsAutoCString headerVal;
    1193             :   // The "Access-Control-Expose-Headers" header contains a comma separated
    1194             :   // list of method names.
    1195           0 :   Unused << aHttpChannel->
    1196           0 :       GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"),
    1197           0 :                         headerVal);
    1198           0 :   nsCCharSeparatedTokenizer exposeTokens(headerVal, ',');
    1199           0 :   bool isSafe = false;
    1200           0 :   while (exposeTokens.hasMoreTokens()) {
    1201           0 :     const nsDependentCSubstring& token = exposeTokens.nextToken();
    1202           0 :     if (token.IsEmpty()) {
    1203           0 :       continue;
    1204             :     }
    1205           0 :     if (!NS_IsValidHTTPToken(token)) {
    1206           0 :       return false;
    1207             :     }
    1208           0 :     if (aHeader.Equals(token, nsCaseInsensitiveCStringComparator())) {
    1209           0 :       isSafe = true;
    1210             :     }
    1211             :   }
    1212           0 :   return isSafe;
    1213             : }
    1214             : 
    1215             : NS_IMETHODIMP
    1216           0 : XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aOut)
    1217             : {
    1218           0 :   ErrorResult rv;
    1219           0 :   GetAllResponseHeaders(aOut, rv);
    1220           0 :   return rv.StealNSResult();
    1221             : }
    1222             : 
    1223             : void
    1224           0 : XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aResponseHeaders,
    1225             :                                                 ErrorResult& aRv)
    1226             : {
    1227           0 :   aResponseHeaders.Truncate();
    1228             : 
    1229             :   // If the state is UNSENT or OPENED,
    1230             :   // return the empty string and terminate these steps.
    1231           0 :   if (mState == State::unsent || mState == State::opened) {
    1232           0 :     return;
    1233             :   }
    1234             : 
    1235           0 :   if (mErrorLoad != ErrorType::eOK) {
    1236           0 :     return;
    1237             :   }
    1238             : 
    1239           0 :   if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
    1240             :     RefPtr<nsHeaderVisitor> visitor =
    1241           0 :       new nsHeaderVisitor(*this, WrapNotNull(httpChannel));
    1242           0 :     if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
    1243           0 :       aResponseHeaders = visitor->Headers();
    1244             :     }
    1245           0 :     return;
    1246             :   }
    1247             : 
    1248           0 :   if (!mChannel) {
    1249           0 :     return;
    1250             :   }
    1251             : 
    1252             :   // Even non-http channels supply content type.
    1253           0 :   nsAutoCString value;
    1254           0 :   if (NS_SUCCEEDED(mChannel->GetContentType(value))) {
    1255           0 :     aResponseHeaders.AppendLiteral("Content-Type: ");
    1256           0 :     aResponseHeaders.Append(value);
    1257           0 :     if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
    1258           0 :       aResponseHeaders.AppendLiteral(";charset=");
    1259           0 :       aResponseHeaders.Append(value);
    1260             :     }
    1261           0 :     aResponseHeaders.AppendLiteral("\r\n");
    1262             :   }
    1263             : 
    1264             :   // Don't provide Content-Length for data URIs
    1265           0 :   nsCOMPtr<nsIURI> uri;
    1266             :   bool isDataURI;
    1267           0 :   if (NS_FAILED(mChannel->GetURI(getter_AddRefs(uri))) ||
    1268           0 :       NS_FAILED(uri->SchemeIs("data", &isDataURI)) ||
    1269           0 :       !isDataURI) {
    1270             :     int64_t length;
    1271           0 :     if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
    1272           0 :       aResponseHeaders.AppendLiteral("Content-Length: ");
    1273           0 :       aResponseHeaders.AppendInt(length);
    1274           0 :       aResponseHeaders.AppendLiteral("\r\n");
    1275             :     }
    1276             :   }
    1277             : }
    1278             : 
    1279             : NS_IMETHODIMP
    1280           0 : XMLHttpRequestMainThread::GetResponseHeader(const nsACString& aHeader,
    1281             :                                             nsACString& aResult)
    1282             : {
    1283           0 :   ErrorResult rv;
    1284           0 :   GetResponseHeader(aHeader, aResult, rv);
    1285           0 :   return rv.StealNSResult();
    1286             : }
    1287             : 
    1288             : void
    1289           0 : XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
    1290             :                                             nsACString& _retval, ErrorResult& aRv)
    1291             : {
    1292           0 :   _retval.SetIsVoid(true);
    1293             : 
    1294           0 :   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
    1295             : 
    1296           0 :   if (!httpChannel) {
    1297             :     // If the state is UNSENT or OPENED,
    1298             :     // return null and terminate these steps.
    1299           0 :     if (mState == State::unsent || mState == State::opened) {
    1300           0 :       return;
    1301             :     }
    1302             : 
    1303             :     // Even non-http channels supply content type and content length.
    1304             :     // Remember we don't leak header information from denied cross-site
    1305             :     // requests.
    1306             :     nsresult status;
    1307           0 :     if (!mChannel ||
    1308           0 :         NS_FAILED(mChannel->GetStatus(&status)) ||
    1309           0 :         NS_FAILED(status)) {
    1310           0 :       return;
    1311             :     }
    1312             : 
    1313             :     // Content Type:
    1314           0 :     if (header.LowerCaseEqualsASCII("content-type")) {
    1315           0 :       if (NS_FAILED(mChannel->GetContentType(_retval))) {
    1316             :         // Means no content type
    1317           0 :         _retval.SetIsVoid(true);
    1318           0 :         return;
    1319             :       }
    1320             : 
    1321           0 :       nsCString value;
    1322           0 :       if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
    1323           0 :           !value.IsEmpty()) {
    1324           0 :         _retval.AppendLiteral(";charset=");
    1325           0 :         _retval.Append(value);
    1326             :       }
    1327             :     }
    1328             : 
    1329             :     // Content Length:
    1330           0 :     else if (header.LowerCaseEqualsASCII("content-length")) {
    1331             :       int64_t length;
    1332           0 :       if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
    1333           0 :         _retval.AppendInt(length);
    1334             :       }
    1335             :     }
    1336             : 
    1337           0 :     return;
    1338             :   }
    1339             : 
    1340             :   // Check for dangerous headers
    1341           0 :   if (!IsSafeHeader(header, WrapNotNull(httpChannel))) {
    1342           0 :     return;
    1343             :   }
    1344             : 
    1345           0 :   aRv = httpChannel->GetResponseHeader(header, _retval);
    1346           0 :   if (aRv.ErrorCodeIs(NS_ERROR_NOT_AVAILABLE)) {
    1347             :     // Means no header
    1348           0 :     _retval.SetIsVoid(true);
    1349           0 :     aRv.SuppressException();
    1350             :   }
    1351             : }
    1352             : 
    1353             : already_AddRefed<nsILoadGroup>
    1354           3 : XMLHttpRequestMainThread::GetLoadGroup() const
    1355             : {
    1356           3 :   if (mFlagBackgroundRequest) {
    1357           0 :     return nullptr;
    1358             :   }
    1359             : 
    1360           3 :   if (mLoadGroup) {
    1361           6 :     nsCOMPtr<nsILoadGroup> ref = mLoadGroup;
    1362           3 :     return ref.forget();
    1363             :   }
    1364             : 
    1365           0 :   nsIDocument* doc = GetDocumentIfCurrent();
    1366           0 :   if (doc) {
    1367           0 :     return doc->GetDocumentLoadGroup();
    1368             :   }
    1369             : 
    1370           0 :   return nullptr;
    1371             : }
    1372             : 
    1373             : nsresult
    1374           9 : XMLHttpRequestMainThread::FireReadystatechangeEvent()
    1375             : {
    1376          18 :   RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
    1377           9 :   event->InitEvent(kLiteralString_readystatechange, false, false);
    1378             :   // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
    1379           9 :   event->SetTrusted(true);
    1380           9 :   DispatchOrStoreEvent(this, event);
    1381          18 :   return NS_OK;
    1382             : }
    1383             : 
    1384             : void
    1385           8 : XMLHttpRequestMainThread::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
    1386             :                                                 const ProgressEventType aType,
    1387             :                                                 int64_t aLoaded, int64_t aTotal)
    1388             : {
    1389           8 :   NS_ASSERTION(aTarget, "null target");
    1390             : 
    1391          16 :   if (NS_FAILED(CheckInnerWindowCorrectness()) ||
    1392           8 :       (!AllowUploadProgress() && aTarget == mUpload)) {
    1393           0 :     return;
    1394             :   }
    1395             : 
    1396             :   // If blocked by CORS, zero-out the stats on progress events
    1397             :   // and never fire "progress" or "load" events at all.
    1398           8 :   if (IsDeniedCrossSiteCORSRequest()) {
    1399           0 :     if (aType == ProgressEventType::progress ||
    1400             :         aType == ProgressEventType::load) {
    1401           0 :       return;
    1402             :     }
    1403           0 :     aLoaded = 0;
    1404           0 :     aTotal = -1;
    1405             :   }
    1406             : 
    1407           8 :   if (aType == ProgressEventType::progress) {
    1408           2 :     mInLoadProgressEvent = true;
    1409             :   }
    1410             : 
    1411           8 :   ProgressEventInit init;
    1412           8 :   init.mBubbles = false;
    1413           8 :   init.mCancelable = false;
    1414           8 :   init.mLengthComputable = aTotal != -1; // XHR spec step 6.1
    1415           8 :   init.mLoaded = aLoaded;
    1416           8 :   init.mTotal = (aTotal == -1) ? 0 : aTotal;
    1417             : 
    1418           8 :   const nsAString& typeString = ProgressEventTypeStrings[(uint8_t)aType];
    1419             :   RefPtr<ProgressEvent> event =
    1420          16 :     ProgressEvent::Constructor(aTarget, typeString, init);
    1421           8 :   event->SetTrusted(true);
    1422             : 
    1423           8 :   DispatchOrStoreEvent(aTarget, event);
    1424             : 
    1425           8 :   if (aType == ProgressEventType::progress) {
    1426           2 :     mInLoadProgressEvent = false;
    1427             : 
    1428             :     // clear chunked responses after every progress event
    1429           4 :     if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
    1430           2 :         mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
    1431           0 :       mResponseBody.Truncate();
    1432           0 :       TruncateResponseText();
    1433           0 :       mResultArrayBuffer = nullptr;
    1434           0 :       mArrayBufferBuilder.reset();
    1435             :     }
    1436             :   }
    1437             : 
    1438             :   // If we're sending a load, error, timeout or abort event, then
    1439             :   // also dispatch the subsequent loadend event.
    1440           8 :   if (aType == ProgressEventType::load || aType == ProgressEventType::error ||
    1441           6 :       aType == ProgressEventType::timeout || aType == ProgressEventType::abort) {
    1442           2 :     DispatchProgressEvent(aTarget, ProgressEventType::loadend, aLoaded, aTotal);
    1443             :   }
    1444             : }
    1445             : 
    1446             : void
    1447          17 : XMLHttpRequestMainThread::DispatchOrStoreEvent(DOMEventTargetHelper* aTarget,
    1448             :                                                Event* aEvent)
    1449             : {
    1450          17 :   MOZ_ASSERT(aTarget);
    1451          17 :   MOZ_ASSERT(aEvent);
    1452             : 
    1453          17 :   if (mEventDispatchingSuspended) {
    1454           0 :     PendingEvent* event = mPendingEvents.AppendElement();
    1455           0 :     event->mTarget = aTarget;
    1456           0 :     event->mEvent = aEvent;
    1457           0 :     return;
    1458             :   }
    1459             : 
    1460          17 :   aTarget->DispatchDOMEvent(nullptr, aEvent, nullptr, nullptr);
    1461             : }
    1462             : 
    1463             : void
    1464           0 : XMLHttpRequestMainThread::SuspendEventDispatching()
    1465             : {
    1466           0 :   MOZ_ASSERT(!mEventDispatchingSuspended);
    1467           0 :   mEventDispatchingSuspended = true;
    1468           0 : }
    1469             : 
    1470             : void
    1471           0 : XMLHttpRequestMainThread::ResumeEventDispatching()
    1472             : {
    1473           0 :   MOZ_ASSERT(mEventDispatchingSuspended);
    1474           0 :   mEventDispatchingSuspended = false;
    1475             : 
    1476           0 :   nsTArray<PendingEvent> pendingEvents;
    1477           0 :   pendingEvents.SwapElements(mPendingEvents);
    1478             : 
    1479           0 :   for (uint32_t i = 0; i < pendingEvents.Length(); ++i) {
    1480           0 :     pendingEvents[i].mTarget->
    1481           0 :       DispatchDOMEvent(nullptr, pendingEvents[i].mEvent, nullptr, nullptr);
    1482             :   }
    1483           0 : }
    1484             : 
    1485             : already_AddRefed<nsIHttpChannel>
    1486          24 : XMLHttpRequestMainThread::GetCurrentHttpChannel()
    1487             : {
    1488          48 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
    1489          48 :   return httpChannel.forget();
    1490             : }
    1491             : 
    1492             : already_AddRefed<nsIJARChannel>
    1493           0 : XMLHttpRequestMainThread::GetCurrentJARChannel()
    1494             : {
    1495           0 :   nsCOMPtr<nsIJARChannel> appChannel = do_QueryInterface(mChannel);
    1496           0 :   return appChannel.forget();
    1497             : }
    1498             : 
    1499             : bool
    1500           4 : XMLHttpRequestMainThread::IsSystemXHR() const
    1501             : {
    1502           4 :   return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
    1503             : }
    1504             : 
    1505             : bool
    1506           2 : XMLHttpRequestMainThread::InUploadPhase() const
    1507             : {
    1508             :   // We're in the upload phase while our state is State::opened.
    1509           2 :   return mState == State::opened;
    1510             : }
    1511             : 
    1512             : NS_IMETHODIMP
    1513           0 : XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsACString& aUrl,
    1514             :                                bool aAsync, const nsAString& aUsername,
    1515             :                                const nsAString& aPassword, uint8_t optional_argc)
    1516             : {
    1517           0 :   return Open(aMethod, aUrl, optional_argc > 0 ? aAsync : true,
    1518           0 :               aUsername, aPassword);
    1519             : }
    1520             : 
    1521             : // This case is hit when the async parameter is outright omitted, which
    1522             : // should set it to true (and the username and password to null).
    1523             : void
    1524           0 : XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsAString& aUrl,
    1525             :                                ErrorResult& aRv)
    1526             : {
    1527           0 :   Open(aMethod, aUrl, true, NullString(), NullString(), aRv);
    1528           0 : }
    1529             : 
    1530             : // This case is hit when the async parameter is specified, even if the
    1531             : // JS value was "undefined" (which due to legacy reasons should be
    1532             : // treated as true, which is how it will already be passed in here).
    1533             : void
    1534           3 : XMLHttpRequestMainThread::Open(const nsACString& aMethod,
    1535             :                                const nsAString& aUrl,
    1536             :                                bool aAsync,
    1537             :                                const nsAString& aUsername,
    1538             :                                const nsAString& aPassword,
    1539             :                                ErrorResult& aRv)
    1540             : {
    1541           3 :   nsresult rv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl), aAsync, aUsername, aPassword);
    1542           3 :   if (NS_FAILED(rv)) {
    1543           0 :     aRv.Throw(rv);
    1544             :   }
    1545           3 : }
    1546             : 
    1547             : nsresult
    1548           3 : XMLHttpRequestMainThread::Open(const nsACString& aMethod,
    1549             :                                const nsACString& aUrl,
    1550             :                                bool aAsync,
    1551             :                                const nsAString& aUsername,
    1552             :                                const nsAString& aPassword) {
    1553             :   // Gecko-specific
    1554           3 :   if (!aAsync && !DontWarnAboutSyncXHR() && GetOwner() &&
    1555           0 :       GetOwner()->GetExtantDoc()) {
    1556           0 :     GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
    1557             :   }
    1558             : 
    1559           3 :   Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC, aAsync ? 0 : 1);
    1560             : 
    1561             :   // Step 1
    1562           6 :   nsCOMPtr<nsIDocument> responsibleDocument = GetDocumentIfCurrent();
    1563           3 :   if (!responsibleDocument) {
    1564             :     // This could be because we're no longer current or because we're in some
    1565             :     // non-window context...
    1566           3 :     nsresult rv = CheckInnerWindowCorrectness();
    1567           3 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1568           0 :       return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
    1569             :     }
    1570             :   }
    1571           3 :   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
    1572             : 
    1573             :   // Steps 2-4
    1574           6 :   nsAutoCString method;
    1575           3 :   nsresult rv = FetchUtil::GetValidRequestMethod(aMethod, method);
    1576           3 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1577           0 :     return rv;
    1578             :   }
    1579             : 
    1580             :   // Steps 5-6
    1581           6 :   nsCOMPtr<nsIURI> baseURI;
    1582           3 :   if (mBaseURI) {
    1583           3 :     baseURI = mBaseURI;
    1584           0 :   } else if (responsibleDocument) {
    1585           0 :     baseURI = responsibleDocument->GetBaseURI();
    1586             :   }
    1587           6 :   nsCOMPtr<nsIURI> parsedURL;
    1588           3 :   rv = NS_NewURI(getter_AddRefs(parsedURL), aUrl, nullptr, baseURI);
    1589           3 :   if (NS_FAILED(rv)) {
    1590           0 :     if (rv ==  NS_ERROR_MALFORMED_URI) {
    1591           0 :       return NS_ERROR_DOM_MALFORMED_URI;
    1592             :     }
    1593           0 :     return rv;
    1594             :   }
    1595           3 :   if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) {
    1596           0 :     return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
    1597             :   }
    1598             : 
    1599             :   // Step 7
    1600             :   // This is already handled by the other Open() method, which passes
    1601             :   // username and password in as NullStrings.
    1602             : 
    1603             :   // Step 8
    1604           6 :   nsAutoCString host;
    1605           3 :   parsedURL->GetHost(host);
    1606           3 :   if (!host.IsEmpty()) {
    1607           6 :     nsAutoCString userpass;
    1608           3 :     if (!aUsername.IsVoid()) {
    1609           0 :       CopyUTF16toUTF8(aUsername, userpass);
    1610             :     }
    1611           3 :     userpass.AppendLiteral(":");
    1612           3 :     if (!aPassword.IsVoid()) {
    1613           0 :       AppendUTF16toUTF8(aPassword, userpass);
    1614             :     }
    1615           3 :     parsedURL->SetUserPass(userpass);
    1616             :   }
    1617             : 
    1618             :   // Step 9
    1619           3 :   if (!aAsync && HasOrHasHadOwner() && (mTimeoutMilliseconds ||
    1620           0 :        mResponseType != XMLHttpRequestResponseType::_empty)) {
    1621           0 :     if (mTimeoutMilliseconds) {
    1622           0 :       LogMessage("TimeoutSyncXHRWarning", GetOwner());
    1623             :     }
    1624           0 :     if (mResponseType != XMLHttpRequestResponseType::_empty) {
    1625           0 :       LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
    1626             :     }
    1627           0 :     return NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC;
    1628             :   }
    1629             : 
    1630             :   // Step 10
    1631           3 :   CloseRequest();
    1632             : 
    1633             :   // Step 11
    1634             :   // timeouts are handled without a flag
    1635           3 :   mFlagSend = false;
    1636           3 :   mRequestMethod.Assign(method);
    1637           3 :   mRequestURL = parsedURL;
    1638           3 :   mFlagSynchronous = !aAsync;
    1639           3 :   mAuthorRequestHeaders.Clear();
    1640           3 :   ResetResponse();
    1641             : 
    1642             :   // Gecko-specific
    1643           3 :   mFlagHadUploadListenersOnSend = false;
    1644           3 :   mFlagAborted = false;
    1645           3 :   mFlagTimedOut = false;
    1646             : 
    1647             :   // Per spec we should only create the channel on send(), but we have internal
    1648             :   // code that relies on the channel being created now, and that code is not
    1649             :   // always IsSystemXHR(). However, we're not supposed to throw channel-creation
    1650             :   // errors during open(), so we silently ignore those here.
    1651           3 :   CreateChannel();
    1652             : 
    1653             :   // Step 12
    1654           3 :   if (mState != State::opened) {
    1655           3 :     mState = State::opened;
    1656           3 :     FireReadystatechangeEvent();
    1657             :   }
    1658             : 
    1659           3 :   return NS_OK;
    1660             : }
    1661             : 
    1662             : void
    1663           0 : XMLHttpRequestMainThread::SetOriginAttributes(const OriginAttributesDictionary& aAttrs)
    1664             : {
    1665           0 :   MOZ_ASSERT((mState == State::opened) && !mFlagSend);
    1666             : 
    1667           0 :   OriginAttributes attrs(aAttrs);
    1668             : 
    1669           0 :   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
    1670           0 :   MOZ_ASSERT(loadInfo);
    1671           0 :   if (loadInfo) {
    1672           0 :     loadInfo->SetOriginAttributes(attrs);
    1673             :   }
    1674           0 : }
    1675             : 
    1676             : void
    1677           2 : XMLHttpRequestMainThread::PopulateNetworkInterfaceId()
    1678             : {
    1679           2 :   if (mNetworkInterfaceId.IsEmpty()) {
    1680           4 :     return;
    1681             :   }
    1682           0 :   nsCOMPtr<nsIHttpChannelInternal> channel(do_QueryInterface(mChannel));
    1683           0 :   if (!channel) {
    1684           0 :     return;
    1685             :   }
    1686           0 :   DebugOnly<nsresult> rv = channel->SetNetworkInterfaceId(mNetworkInterfaceId);
    1687           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    1688             : }
    1689             : 
    1690             : /*
    1691             :  * "Copy" from a stream.
    1692             :  */
    1693             : nsresult
    1694           3 : XMLHttpRequestMainThread::StreamReaderFunc(nsIInputStream* in,
    1695             :                                            void* closure,
    1696             :                                            const char* fromRawSegment,
    1697             :                                            uint32_t toOffset,
    1698             :                                            uint32_t count,
    1699             :                                            uint32_t *writeCount)
    1700             : {
    1701           3 :   XMLHttpRequestMainThread* xmlHttpRequest = static_cast<XMLHttpRequestMainThread*>(closure);
    1702           3 :   if (!xmlHttpRequest || !writeCount) {
    1703           0 :     NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
    1704           0 :     return NS_ERROR_FAILURE;
    1705             :   }
    1706             : 
    1707           3 :   nsresult rv = NS_OK;
    1708             : 
    1709           3 :   if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob) {
    1710           0 :     xmlHttpRequest->MaybeCreateBlobStorage();
    1711           0 :     rv = xmlHttpRequest->mBlobStorage->Append(fromRawSegment, count);
    1712           3 :   } else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_blob) {
    1713           0 :     if (!xmlHttpRequest->mBlobSet) {
    1714           0 :       xmlHttpRequest->mBlobSet = new BlobSet();
    1715             :     }
    1716           0 :     rv = xmlHttpRequest->mBlobSet->AppendVoidPtr(fromRawSegment, count);
    1717             :     // Clear the cache so that the blob size is updated.
    1718           0 :     xmlHttpRequest->mResponseBlob = nullptr;
    1719           3 :   } else if ((xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
    1720           3 :               !xmlHttpRequest->mIsMappedArrayBuffer) ||
    1721           3 :              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
    1722             :     // get the initial capacity to something reasonable to avoid a bunch of reallocs right
    1723             :     // at the start
    1724           0 :     if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
    1725           0 :       xmlHttpRequest->mArrayBufferBuilder.setCapacity(std::max(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
    1726             : 
    1727           0 :     if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder.append(
    1728             :                       reinterpret_cast<const uint8_t*>(fromRawSegment),
    1729             :                       count,
    1730             :                       XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH))) {
    1731           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1732             :     }
    1733             : 
    1734           3 :   } else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::_empty &&
    1735           0 :              xmlHttpRequest->mResponseXML) {
    1736             :     // Copy for our own use
    1737           0 :     if (!xmlHttpRequest->mResponseBody.Append(fromRawSegment, count, fallible)) {
    1738           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1739             :     }
    1740           6 :   } else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::_empty ||
    1741           3 :              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Text ||
    1742           0 :              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Json ||
    1743           0 :              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_text) {
    1744           3 :     NS_ASSERTION(!xmlHttpRequest->mResponseXML,
    1745             :                  "We shouldn't be parsing a doc here");
    1746           3 :     rv = xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
    1747           3 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1748           0 :       return rv;
    1749             :     }
    1750             :   }
    1751             : 
    1752           3 :   if (xmlHttpRequest->mFlagParseBody) {
    1753             :     // Give the same data to the parser.
    1754             : 
    1755             :     // We need to wrap the data in a new lightweight stream and pass that
    1756             :     // to the parser, because calling ReadSegments() recursively on the same
    1757             :     // stream is not supported.
    1758           0 :     nsCOMPtr<nsIInputStream> copyStream;
    1759           0 :     rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
    1760             : 
    1761           0 :     if (NS_SUCCEEDED(rv) && xmlHttpRequest->mXMLParserStreamListener) {
    1762           0 :       NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
    1763             :       nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
    1764           0 :                                   ->OnDataAvailable(xmlHttpRequest->mChannel,
    1765             :                                                     xmlHttpRequest->mContext,
    1766           0 :                                                     copyStream, toOffset, count);
    1767             : 
    1768             :       // No use to continue parsing if we failed here, but we
    1769             :       // should still finish reading the stream
    1770           0 :       if (NS_FAILED(parsingResult)) {
    1771           0 :         xmlHttpRequest->mFlagParseBody = false;
    1772             :       }
    1773             :     }
    1774             :   }
    1775             : 
    1776           3 :   if (NS_SUCCEEDED(rv)) {
    1777           3 :     *writeCount = count;
    1778             :   } else {
    1779           0 :     *writeCount = 0;
    1780             :   }
    1781             : 
    1782           3 :   return rv;
    1783             : }
    1784             : 
    1785             : namespace {
    1786             : 
    1787             : nsresult
    1788           0 : GetLocalFileFromChannel(nsIRequest* aRequest, nsIFile** aFile)
    1789             : {
    1790           0 :   MOZ_ASSERT(aRequest);
    1791           0 :   MOZ_ASSERT(aFile);
    1792             : 
    1793           0 :   *aFile = nullptr;
    1794             : 
    1795           0 :   nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aRequest);
    1796           0 :   if (!fc) {
    1797           0 :     return NS_OK;
    1798             :   }
    1799             : 
    1800           0 :   nsCOMPtr<nsIFile> file;
    1801           0 :   nsresult rv = fc->GetFile(getter_AddRefs(file));
    1802           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1803           0 :     return rv;
    1804             :   }
    1805             : 
    1806           0 :   file.forget(aFile);
    1807           0 :   return NS_OK;
    1808             : }
    1809             : 
    1810             : nsresult
    1811           0 : DummyStreamReaderFunc(nsIInputStream* aInputStream,
    1812             :                       void* aClosure,
    1813             :                       const char* aFromRawSegment,
    1814             :                       uint32_t aToOffset,
    1815             :                       uint32_t aCount,
    1816             :                       uint32_t* aWriteCount)
    1817             : {
    1818           0 :   *aWriteCount = aCount;
    1819           0 :   return NS_OK;
    1820             : }
    1821             : 
    1822             : class FileCreationHandler final : public PromiseNativeHandler
    1823             : {
    1824             : public:
    1825             :   NS_DECL_ISUPPORTS
    1826             : 
    1827             :   static void
    1828           0 :   Create(Promise* aPromise, XMLHttpRequestMainThread* aXHR)
    1829             :   {
    1830           0 :     MOZ_ASSERT(aPromise);
    1831             : 
    1832           0 :     RefPtr<FileCreationHandler> handler = new FileCreationHandler(aXHR);
    1833           0 :     aPromise->AppendNativeHandler(handler);
    1834           0 :   }
    1835             : 
    1836             :   void
    1837           0 :   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
    1838             :   {
    1839           0 :     if (NS_WARN_IF(!aValue.isObject())) {
    1840           0 :       mXHR->LocalFileToBlobCompleted(nullptr);
    1841           0 :       return;
    1842             :     }
    1843             : 
    1844           0 :     RefPtr<Blob> blob;
    1845           0 :     if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) {
    1846           0 :       mXHR->LocalFileToBlobCompleted(nullptr);
    1847           0 :       return;
    1848             :     }
    1849             : 
    1850           0 :     mXHR->LocalFileToBlobCompleted(blob);
    1851             :   }
    1852             : 
    1853             :   void
    1854           0 :   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
    1855             :   {
    1856           0 :     mXHR->LocalFileToBlobCompleted(nullptr);
    1857           0 :   }
    1858             : 
    1859             : private:
    1860           0 :   explicit FileCreationHandler(XMLHttpRequestMainThread* aXHR)
    1861           0 :     : mXHR(aXHR)
    1862             :   {
    1863           0 :     MOZ_ASSERT(aXHR);
    1864           0 :   }
    1865             : 
    1866           0 :   ~FileCreationHandler() = default;
    1867             : 
    1868             :   RefPtr<XMLHttpRequestMainThread> mXHR;
    1869             : };
    1870             : 
    1871           0 : NS_IMPL_ISUPPORTS0(FileCreationHandler)
    1872             : 
    1873             : } // namespace
    1874             : 
    1875             : void
    1876           0 : XMLHttpRequestMainThread::LocalFileToBlobCompleted(Blob* aBlob)
    1877             : {
    1878           0 :   MOZ_ASSERT(mState != State::done);
    1879             : 
    1880           0 :   mResponseBlob = aBlob;
    1881           0 :   mBlobStorage = nullptr;
    1882           0 :   mBlobSet = nullptr;
    1883           0 :   NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
    1884             : 
    1885           0 :   ChangeStateToDone();
    1886           0 : }
    1887             : 
    1888             : NS_IMETHODIMP
    1889           2 : XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
    1890             :                                           nsISupports *ctxt,
    1891             :                                           nsIInputStream *inStr,
    1892             :                                           uint64_t sourceOffset,
    1893             :                                           uint32_t count)
    1894             : {
    1895           2 :   NS_ENSURE_ARG_POINTER(inStr);
    1896             : 
    1897           2 :   MOZ_ASSERT(mContext.get() == ctxt,"start context different from OnDataAvailable context");
    1898             : 
    1899           2 :   mProgressSinceLastProgressEvent = true;
    1900           2 :   XMLHttpRequestBinding::ClearCachedResponseTextValue(this);
    1901             : 
    1902             :   nsresult rv;
    1903             : 
    1904           4 :   nsCOMPtr<nsIFile> localFile;
    1905           4 :   if ((mResponseType == XMLHttpRequestResponseType::Blob ||
    1906           2 :        mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
    1907           0 :     rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile));
    1908           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1909           0 :       return rv;
    1910             :     }
    1911             : 
    1912           0 :     if (localFile) {
    1913           0 :       mBlobStorage = nullptr;
    1914           0 :       mBlobSet = nullptr;
    1915           0 :       NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
    1916             : 
    1917             :       // The nsIStreamListener contract mandates us to read from the stream
    1918             :       // before returning.
    1919             :       uint32_t totalRead;
    1920             :       rv =
    1921           0 :         inStr->ReadSegments(DummyStreamReaderFunc, nullptr, count, &totalRead);
    1922           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1923             : 
    1924           0 :       ChangeState(State::loading);
    1925             : 
    1926             :       // Cancel() must be called with an error. We use
    1927             :       // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
    1928             :       // just because we can retrieve the File from the channel directly.
    1929           0 :       return request->Cancel(NS_ERROR_FILE_ALREADY_EXISTS);
    1930             :     }
    1931             :   }
    1932             : 
    1933             :   uint32_t totalRead;
    1934             :   rv = inStr->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc,
    1935           2 :                            (void*)this, count, &totalRead);
    1936           2 :   NS_ENSURE_SUCCESS(rv, rv);
    1937             : 
    1938             :   // Fire the first progress event/loading state change
    1939           2 :   if (mState != State::loading) {
    1940           2 :     ChangeState(State::loading);
    1941           2 :     if (!mFlagSynchronous) {
    1942           2 :       DispatchProgressEvent(this, ProgressEventType::progress,
    1943           2 :                             mLoadTransferred, mLoadTotal);
    1944             :     }
    1945           2 :     mProgressSinceLastProgressEvent = false;
    1946             :   }
    1947             : 
    1948           2 :   if (!mFlagSynchronous && !mProgressTimerIsActive) {
    1949           0 :     StartProgressEventTimer();
    1950             :   }
    1951             : 
    1952           2 :   return NS_OK;
    1953             : }
    1954             : 
    1955             : NS_IMETHODIMP
    1956           2 : XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
    1957             : {
    1958           4 :   AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStartRequest", NETWORK);
    1959             : 
    1960           2 :   nsresult rv = NS_OK;
    1961           2 :   if (!mFirstStartRequestSeen && mRequestObserver) {
    1962           0 :     mFirstStartRequestSeen = true;
    1963           0 :     mRequestObserver->OnStartRequest(request, ctxt);
    1964             :   }
    1965             : 
    1966           2 :   if (request != mChannel) {
    1967             :     // Can this still happen?
    1968           0 :     return NS_OK;
    1969             :   }
    1970             : 
    1971             :   // Don't do anything if we have been aborted
    1972           2 :   if (mState == State::unsent) {
    1973           0 :     return NS_OK;
    1974             :   }
    1975             : 
    1976             :   /* Apparently, Abort() should set State::unsent.  See bug 361773.
    1977             :      XHR2 spec says this is correct. */
    1978           2 :   if (mFlagAborted) {
    1979           0 :     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
    1980             : 
    1981           0 :     return NS_ERROR_UNEXPECTED;
    1982             :   }
    1983             : 
    1984             :   // Don't do anything if we have timed out.
    1985           2 :   if (mFlagTimedOut) {
    1986           0 :     return NS_OK;
    1987             :   }
    1988             : 
    1989           4 :   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
    1990           2 :   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
    1991             : 
    1992             :   nsresult status;
    1993           2 :   request->GetStatus(&status);
    1994           2 :   if (mErrorLoad == ErrorType::eOK && NS_FAILED(status)) {
    1995           0 :     mErrorLoad = ErrorType::eRequest;
    1996             :   }
    1997             : 
    1998             :   // Upload phase is now over. If we were uploading anything,
    1999             :   // stop the timer and fire any final progress events.
    2000           2 :   if (mUpload && !mUploadComplete && mErrorLoad == ErrorType::eOK && !mFlagSynchronous) {
    2001           0 :     StopProgressEventTimer();
    2002             : 
    2003           0 :     mUploadTransferred = mUploadTotal;
    2004             : 
    2005           0 :     if (mProgressSinceLastProgressEvent) {
    2006           0 :       DispatchProgressEvent(mUpload, ProgressEventType::progress,
    2007           0 :                             mUploadTransferred, mUploadTotal);
    2008           0 :       mProgressSinceLastProgressEvent = false;
    2009             :     }
    2010             : 
    2011           0 :     mUploadComplete = true;
    2012           0 :     DispatchProgressEvent(mUpload, ProgressEventType::load,
    2013           0 :                           mUploadTotal, mUploadTotal);
    2014             :   }
    2015             : 
    2016           2 :   mContext = ctxt;
    2017           2 :   mFlagParseBody = true;
    2018           2 :   ChangeState(State::headers_received);
    2019             : 
    2020           2 :   ResetResponse();
    2021             : 
    2022           2 :   if (!mOverrideMimeType.IsEmpty()) {
    2023           0 :     channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
    2024             :   }
    2025             : 
    2026             :   // Fallback to 'application/octet-stream'
    2027           4 :   nsAutoCString type;
    2028           2 :   channel->GetContentType(type);
    2029           2 :   if (type.Equals(UNKNOWN_CONTENT_TYPE)) {
    2030           0 :     channel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
    2031             :   }
    2032             : 
    2033           2 :   DetectCharset();
    2034             : 
    2035             :   // Set up arraybuffer
    2036           2 :   if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
    2037           0 :       NS_SUCCEEDED(status)) {
    2038           0 :     if (mIsMappedArrayBuffer) {
    2039           0 :       nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
    2040           0 :       if (jarChannel) {
    2041           0 :         nsCOMPtr<nsIURI> uri;
    2042           0 :         rv = channel->GetURI(getter_AddRefs(uri));
    2043           0 :         if (NS_SUCCEEDED(rv)) {
    2044           0 :           nsAutoCString file;
    2045           0 :           nsAutoCString scheme;
    2046           0 :           uri->GetScheme(scheme);
    2047           0 :           if (scheme.LowerCaseEqualsLiteral("jar")) {
    2048           0 :             nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
    2049           0 :             if (jarURI) {
    2050           0 :               jarURI->GetJAREntry(file);
    2051             :             }
    2052             :           }
    2053           0 :           nsCOMPtr<nsIFile> jarFile;
    2054           0 :           jarChannel->GetJarFile(getter_AddRefs(jarFile));
    2055           0 :           if (!jarFile) {
    2056           0 :             mIsMappedArrayBuffer = false;
    2057             :           } else {
    2058           0 :             rv = mArrayBufferBuilder.mapToFileInPackage(file, jarFile);
    2059             :             // This can happen legitimately if there are compressed files
    2060             :             // in the jarFile. See bug #1357219. No need to warn on the error.
    2061           0 :             if (NS_FAILED(rv)) {
    2062           0 :               mIsMappedArrayBuffer = false;
    2063             :             } else {
    2064           0 :               channel->SetContentType(NS_LITERAL_CSTRING("application/mem-mapped"));
    2065             :             }
    2066             :           }
    2067             :         }
    2068             :       }
    2069             :     }
    2070             :     // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
    2071             :     // and we want it fallback to the malloc way.
    2072           0 :     if (!mIsMappedArrayBuffer) {
    2073             :       int64_t contentLength;
    2074           0 :       rv = channel->GetContentLength(&contentLength);
    2075           0 :       if (NS_SUCCEEDED(rv) &&
    2076           0 :           contentLength > 0 &&
    2077           0 :           contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
    2078           0 :         mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
    2079             :       }
    2080             :     }
    2081             :   }
    2082             : 
    2083             :   // Set up responseXML
    2084           4 :   bool parseBody = mResponseType == XMLHttpRequestResponseType::_empty ||
    2085           4 :                    mResponseType == XMLHttpRequestResponseType::Document;
    2086           4 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
    2087           2 :   if (parseBody && httpChannel) {
    2088           0 :     nsAutoCString method;
    2089           0 :     rv = httpChannel->GetRequestMethod(method);
    2090           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2091           0 :     parseBody = !method.EqualsLiteral("HEAD");
    2092             :   }
    2093             : 
    2094           2 :   mIsHtml = false;
    2095           2 :   mWarnAboutSyncHtml = false;
    2096           2 :   if (parseBody && NS_SUCCEEDED(status)) {
    2097             :     // We can gain a huge performance win by not even trying to
    2098             :     // parse non-XML data. This also protects us from the situation
    2099             :     // where we have an XML document and sink, but HTML (or other)
    2100             :     // parser, which can produce unreliable results.
    2101           0 :     nsAutoCString type;
    2102           0 :     channel->GetContentType(type);
    2103             : 
    2104           0 :     if ((mResponseType == XMLHttpRequestResponseType::Document) &&
    2105           0 :         type.EqualsLiteral("text/html")) {
    2106             :       // HTML parsing is only supported for responseType == "document" to
    2107             :       // avoid running the parser and, worse, populating responseXML for
    2108             :       // legacy users of XHR who use responseType == "" for retrieving the
    2109             :       // responseText of text/html resources. This legacy case is so common
    2110             :       // that it's not useful to emit a warning about it.
    2111           0 :       if (mFlagSynchronous) {
    2112             :         // We don't make cool new features available in the bad synchronous
    2113             :         // mode. The synchronous mode is for legacy only.
    2114           0 :         mWarnAboutSyncHtml = true;
    2115           0 :         mFlagParseBody = false;
    2116             :       } else {
    2117           0 :         mIsHtml = true;
    2118             :       }
    2119           0 :     } else if (!(type.EqualsLiteral("text/xml") ||
    2120           0 :                  type.EqualsLiteral("application/xml") ||
    2121           0 :                  type.RFind("+xml", true, -1, 4) != kNotFound)) {
    2122             :       // Follow https://xhr.spec.whatwg.org/
    2123             :       // If final MIME type is not null, text/html, text/xml, application/xml,
    2124             :       // or does not end in +xml, return null.
    2125           0 :       mFlagParseBody = false;
    2126             :     }
    2127             :   } else {
    2128             :     // The request failed, so we shouldn't be parsing anyway
    2129           2 :     mFlagParseBody = false;
    2130             :   }
    2131             : 
    2132           2 :   if (mFlagParseBody) {
    2133           0 :     nsCOMPtr<nsIURI> baseURI, docURI;
    2134           0 :     rv = mChannel->GetURI(getter_AddRefs(docURI));
    2135           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2136           0 :     baseURI = docURI;
    2137             : 
    2138           0 :     nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
    2139           0 :     nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
    2140           0 :     if (doc) {
    2141           0 :       chromeXHRDocURI = doc->GetDocumentURI();
    2142           0 :       chromeXHRDocBaseURI = doc->GetBaseURI();
    2143             :     } else {
    2144             :       // If we're no longer current, just kill the load, though it really should
    2145             :       // have been killed already.
    2146           0 :       if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) {
    2147           0 :         return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
    2148             :       }
    2149             :     }
    2150             : 
    2151             :     // Create an empty document from it.
    2152           0 :     const nsAString& emptyStr = EmptyString();
    2153           0 :     nsCOMPtr<nsIDOMDocument> responseDoc;
    2154           0 :     nsIGlobalObject* global = DOMEventTargetHelper::GetParentObject();
    2155             : 
    2156           0 :     nsCOMPtr<nsIPrincipal> requestingPrincipal;
    2157           0 :     rv = nsContentUtils::GetSecurityManager()->
    2158           0 :        GetChannelResultPrincipal(channel, getter_AddRefs(requestingPrincipal));
    2159           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2160             : 
    2161           0 :     rv = NS_NewDOMDocument(getter_AddRefs(responseDoc),
    2162             :                            emptyStr, emptyStr, nullptr, docURI,
    2163             :                            baseURI, requestingPrincipal, true, global,
    2164           0 :                            mIsHtml ? DocumentFlavorHTML :
    2165           0 :                                      DocumentFlavorLegacyGuess);
    2166           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2167           0 :     mResponseXML = do_QueryInterface(responseDoc);
    2168           0 :     mResponseXML->SetChromeXHRDocURI(chromeXHRDocURI);
    2169           0 :     mResponseXML->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI);
    2170             : 
    2171             :     // suppress parsing failure messages to console for statuses which
    2172             :     // can have empty bodies (see bug 884693).
    2173             :     uint32_t responseStatus;
    2174           0 :     if (NS_SUCCEEDED(GetStatus(&responseStatus)) &&
    2175           0 :         (responseStatus == 201 || responseStatus == 202 ||
    2176           0 :          responseStatus == 204 || responseStatus == 205 ||
    2177           0 :          responseStatus == 304)) {
    2178           0 :       mResponseXML->SetSuppressParserErrorConsoleMessages(true);
    2179             :     }
    2180             : 
    2181           0 :     if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
    2182           0 :       mResponseXML->ForceEnableXULXBL();
    2183             :     }
    2184             : 
    2185           0 :     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
    2186           0 :     MOZ_ASSERT(loadInfo);
    2187           0 :     bool isCrossSite = false;
    2188           0 :     if (loadInfo) {
    2189           0 :       isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic;
    2190             :     }
    2191             : 
    2192           0 :     if (isCrossSite) {
    2193           0 :       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
    2194           0 :       if (htmlDoc) {
    2195           0 :         htmlDoc->DisableCookieAccess();
    2196             :       }
    2197             :     }
    2198             : 
    2199           0 :     nsCOMPtr<nsIStreamListener> listener;
    2200           0 :     nsCOMPtr<nsILoadGroup> loadGroup;
    2201           0 :     channel->GetLoadGroup(getter_AddRefs(loadGroup));
    2202             : 
    2203             :     // suppress <parsererror> nodes on XML document parse failure, but only
    2204             :     // for non-privileged code (including Web Extensions). See bug 289714.
    2205           0 :     if (!IsSystemXHR()) {
    2206           0 :       mResponseXML->SetSuppressParserErrorElement(true);
    2207             :     }
    2208             : 
    2209           0 :     rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
    2210           0 :                                          nullptr, getter_AddRefs(listener),
    2211           0 :                                          !isCrossSite);
    2212           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2213             : 
    2214             :     // the spec requires the response document.referrer to be the empty string
    2215           0 :     mResponseXML->SetReferrer(NS_LITERAL_CSTRING(""));
    2216             : 
    2217           0 :     mXMLParserStreamListener = listener;
    2218           0 :     rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
    2219           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2220             :   }
    2221             : 
    2222             :   // Download phase beginning; start the progress event timer if necessary.
    2223           2 :   if (NS_SUCCEEDED(rv) && HasListenersFor(nsGkAtoms::onprogress)) {
    2224           2 :     StartProgressEventTimer();
    2225             :   }
    2226             : 
    2227           2 :   return NS_OK;
    2228             : }
    2229             : 
    2230             : NS_IMETHODIMP
    2231           2 : XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
    2232             : {
    2233           4 :   AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStopRequest", NETWORK);
    2234             : 
    2235           2 :   if (request != mChannel) {
    2236             :     // Can this still happen?
    2237           0 :     return NS_OK;
    2238             :   }
    2239             : 
    2240           2 :   mWaitingForOnStopRequest = false;
    2241             : 
    2242           2 :   if (mRequestObserver) {
    2243           0 :     NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
    2244           0 :     mFirstStartRequestSeen = false;
    2245           0 :     mRequestObserver->OnStopRequest(request, ctxt, status);
    2246             :   }
    2247             : 
    2248             :   // make sure to notify the listener if we were aborted
    2249             :   // XXX in fact, why don't we do the cleanup below in this case??
    2250             :   // State::unsent is for abort calls.  See OnStartRequest above.
    2251           2 :   if (mState == State::unsent || mFlagTimedOut) {
    2252           0 :     if (mXMLParserStreamListener)
    2253           0 :       (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
    2254           0 :     return NS_OK;
    2255             :   }
    2256             : 
    2257             :   // Is this good enough here?
    2258           2 :   if (mXMLParserStreamListener && mFlagParseBody) {
    2259           0 :     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
    2260             :   }
    2261             : 
    2262           2 :   mXMLParserStreamListener = nullptr;
    2263           2 :   mContext = nullptr;
    2264             : 
    2265           2 :   bool waitingForBlobCreation = false;
    2266             : 
    2267             :   // If we have this error, we have to deal with a file: URL + responseType =
    2268             :   // blob. We have this error because we canceled the channel. The status will
    2269             :   // be set to NS_OK.
    2270           2 :   if (status == NS_ERROR_FILE_ALREADY_EXISTS &&
    2271           0 :       (mResponseType == XMLHttpRequestResponseType::Blob ||
    2272           0 :        mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
    2273           0 :     nsCOMPtr<nsIFile> file;
    2274           0 :     nsresult rv = GetLocalFileFromChannel(request, getter_AddRefs(file));
    2275           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2276           0 :       return rv;
    2277             :     }
    2278             : 
    2279           0 :     if (file) {
    2280           0 :       nsAutoCString contentType;
    2281           0 :       rv = mChannel->GetContentType(contentType);
    2282           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2283           0 :         return rv;
    2284             :       }
    2285             : 
    2286           0 :       ChromeFilePropertyBag bag;
    2287           0 :       bag.mType = NS_ConvertUTF8toUTF16(contentType);
    2288             : 
    2289           0 :       nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
    2290             : 
    2291           0 :       ErrorResult error;
    2292             :       RefPtr<Promise> promise =
    2293           0 :         FileCreatorHelper::CreateFile(global, file, bag, true, error);
    2294           0 :       if (NS_WARN_IF(error.Failed())) {
    2295           0 :         return error.StealNSResult();
    2296             :       }
    2297             : 
    2298           0 :       FileCreationHandler::Create(promise, this);
    2299           0 :       waitingForBlobCreation = true;
    2300           0 :       status = NS_OK;
    2301             : 
    2302           0 :       NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
    2303           0 :       NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
    2304             :     }
    2305             :   }
    2306             : 
    2307           6 :   if (NS_SUCCEEDED(status) &&
    2308           4 :       (mResponseType == XMLHttpRequestResponseType::Blob ||
    2309           4 :        mResponseType == XMLHttpRequestResponseType::Moz_blob) &&
    2310           0 :       !waitingForBlobCreation) {
    2311             :     // Smaller files may be written in cache map instead of separate files.
    2312             :     // Also, no-store response cannot be written in persistent cache.
    2313           0 :     nsAutoCString contentType;
    2314           0 :     mChannel->GetContentType(contentType);
    2315             : 
    2316           0 :     if (mResponseType == XMLHttpRequestResponseType::Blob) {
    2317             :       // mBlobStorage can be null if the channel is non-file non-cacheable
    2318             :       // and if the response length is zero.
    2319           0 :       MaybeCreateBlobStorage();
    2320           0 :       mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
    2321           0 :       waitingForBlobCreation = true;
    2322             :     } else {
    2323             :       // mBlobSet can be null if the channel is non-file non-cacheable
    2324             :       // and if the response length is zero.
    2325           0 :       if (!mBlobSet) {
    2326           0 :         mBlobSet = new BlobSet();
    2327             :       }
    2328             : 
    2329           0 :       ErrorResult error;
    2330           0 :       nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
    2331             :       RefPtr<BlobImpl> blobImpl =
    2332           0 :         MultipartBlobImpl::Create(Move(subImpls),
    2333           0 :                                   NS_ConvertASCIItoUTF16(contentType),
    2334           0 :                                   error);
    2335           0 :       mBlobSet = nullptr;
    2336             : 
    2337           0 :       if (NS_WARN_IF(error.Failed())) {
    2338           0 :         return error.StealNSResult();
    2339             :       }
    2340             : 
    2341           0 :       mResponseBlob = Blob::Create(GetOwner(), blobImpl);
    2342             :     }
    2343             : 
    2344           0 :     NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
    2345           0 :     NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
    2346           4 :   } else if (NS_SUCCEEDED(status) &&
    2347           2 :              ((mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
    2348           2 :                !mIsMappedArrayBuffer) ||
    2349           2 :               mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
    2350             :     // set the capacity down to the actual length, to realloc back
    2351             :     // down to the actual size
    2352           0 :     if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
    2353             :       // this should never happen!
    2354           0 :       status = NS_ERROR_UNEXPECTED;
    2355             :     }
    2356             :   }
    2357             : 
    2358           4 :   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
    2359           2 :   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
    2360             : 
    2361           2 :   channel->SetNotificationCallbacks(nullptr);
    2362           2 :   mNotificationCallbacks = nullptr;
    2363           2 :   mChannelEventSink = nullptr;
    2364           2 :   mProgressEventSink = nullptr;
    2365             : 
    2366           2 :   mFlagSyncLooping = false;
    2367           2 :   mRequestSentTime = 0;
    2368             : 
    2369             :   // update our charset and decoder to match mResponseXML,
    2370             :   // before it is possibly nulled out
    2371           2 :   MatchCharsetAndDecoderToResponseDocument();
    2372             : 
    2373           2 :   if (NS_FAILED(status)) {
    2374             :     // This can happen if the server is unreachable. Other possible
    2375             :     // reasons are that the user leaves the page or hits the ESC key.
    2376             : 
    2377           0 :     mErrorLoad = ErrorType::eUnreachable;
    2378           0 :     mResponseXML = nullptr;
    2379             :   }
    2380             : 
    2381             :   // If we're uninitialized at this point, we encountered an error
    2382             :   // earlier and listeners have already been notified. Also we do
    2383             :   // not want to do this if we already completed.
    2384           2 :   if (mState == State::unsent || mState == State::done) {
    2385           0 :     return NS_OK;
    2386             :   }
    2387             : 
    2388           2 :   if (!mResponseXML) {
    2389           2 :     mFlagParseBody = false;
    2390             : 
    2391             :     //We postpone the 'done' until the creation of the Blob is completed.
    2392           2 :     if (!waitingForBlobCreation) {
    2393           2 :       ChangeStateToDone();
    2394             :     }
    2395             : 
    2396           2 :     return NS_OK;
    2397             :   }
    2398             : 
    2399           0 :   if (mIsHtml) {
    2400           0 :     NS_ASSERTION(!mFlagSyncLooping,
    2401             :       "We weren't supposed to support HTML parsing with XHR!");
    2402           0 :     nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
    2403             :     EventListenerManager* manager =
    2404           0 :       eventTarget->GetOrCreateListenerManager();
    2405           0 :     manager->AddEventListenerByType(new nsXHRParseEndListener(this),
    2406             :                                     kLiteralString_DOMContentLoaded,
    2407           0 :                                     TrustedEventsAtSystemGroupBubble());
    2408           0 :     return NS_OK;
    2409             :   } else {
    2410           0 :     mFlagParseBody = false;
    2411             :   }
    2412             : 
    2413             :   // We might have been sent non-XML data. If that was the case,
    2414             :   // we should null out the document member. The idea in this
    2415             :   // check here is that if there is no document element it is not
    2416             :   // an XML document. We might need a fancier check...
    2417           0 :   if (!mResponseXML->GetRootElement()) {
    2418           0 :     mErrorParsingXML = true;
    2419           0 :     mResponseXML = nullptr;
    2420             :   }
    2421           0 :   ChangeStateToDone();
    2422           0 :   return NS_OK;
    2423             : }
    2424             : 
    2425             : void
    2426           0 : XMLHttpRequestMainThread::OnBodyParseEnd()
    2427             : {
    2428           0 :   mFlagParseBody = false;
    2429           0 :   ChangeStateToDone();
    2430           0 : }
    2431             : 
    2432             : void
    2433           2 : XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument()
    2434             : {
    2435           2 :   if (mResponseXML && mResponseCharset != mResponseXML->GetDocumentCharacterSet()) {
    2436           0 :     mResponseCharset = mResponseXML->GetDocumentCharacterSet();
    2437           0 :     TruncateResponseText();
    2438           0 :     mResponseBodyDecodedPos = 0;
    2439           0 :     mDecoder = mResponseCharset->NewDecoderWithBOMRemoval();
    2440             :   }
    2441           2 : }
    2442             : 
    2443             : void
    2444           2 : XMLHttpRequestMainThread::ChangeStateToDone()
    2445             : {
    2446           2 :   StopProgressEventTimer();
    2447             : 
    2448           2 :   MOZ_ASSERT(!mFlagParseBody,
    2449             :              "ChangeStateToDone() called before async HTML parsing is done.");
    2450             : 
    2451           2 :   mFlagSend = false;
    2452             : 
    2453           2 :   if (mTimeoutTimer) {
    2454           0 :     mTimeoutTimer->Cancel();
    2455             :   }
    2456             : 
    2457             :   // Per spec, fire the last download progress event, if any,
    2458             :   // before readystatechange=4/done. (Note that 0-sized responses
    2459             :   // will have not sent a progress event yet, so one must be sent here).
    2460           4 :   if (!mFlagSynchronous &&
    2461           4 :       (!mLoadTransferred || mProgressSinceLastProgressEvent)) {
    2462           0 :     DispatchProgressEvent(this, ProgressEventType::progress,
    2463           0 :                           mLoadTransferred, mLoadTotal);
    2464           0 :     mProgressSinceLastProgressEvent = false;
    2465             :   }
    2466             : 
    2467             :   // Per spec, fire readystatechange=4/done before final error events.
    2468           2 :   ChangeState(State::done, true);
    2469             : 
    2470             :   // Per spec, if we failed in the upload phase, fire a final error
    2471             :   // and loadend events for the upload after readystatechange=4/done.
    2472           2 :   if (!mFlagSynchronous && mUpload && !mUploadComplete) {
    2473           0 :     DispatchProgressEvent(mUpload, ProgressEventType::error, 0, -1);
    2474             :   }
    2475             : 
    2476             :   // Per spec, fire download's load/error and loadend events after
    2477             :   // readystatechange=4/done (and of course all upload events).
    2478           2 :   if (mErrorLoad != ErrorType::eOK) {
    2479           0 :     DispatchProgressEvent(this, ProgressEventType::error, 0, -1);
    2480             :   } else {
    2481           2 :     DispatchProgressEvent(this, ProgressEventType::load,
    2482           2 :                           mLoadTransferred, mLoadTotal);
    2483             :   }
    2484             : 
    2485           2 :   if (mErrorLoad != ErrorType::eOK) {
    2486             :     // By nulling out channel here we make it so that Send() can test
    2487             :     // for that and throw. Also calling the various status
    2488             :     // methods/members will not throw.
    2489             :     // This matches what IE does.
    2490           0 :     mChannel = nullptr;
    2491             :   }
    2492           2 : }
    2493             : 
    2494             : nsresult
    2495           3 : XMLHttpRequestMainThread::CreateChannel()
    2496             : {
    2497             :   // When we are called from JS we can find the load group for the page,
    2498             :   // and add ourselves to it. This way any pending requests
    2499             :   // will be automatically aborted if the user leaves the page.
    2500           6 :   nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
    2501             : 
    2502             :   nsSecurityFlags secFlags;
    2503             :   nsLoadFlags loadFlags = nsIRequest::LOAD_BACKGROUND |
    2504           3 :                           nsIChannel::LOAD_CLASSIFY_URI;
    2505           3 :   if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
    2506             :     // When chrome is loading we want to make sure to sandbox any potential
    2507             :     // result document. We also want to allow cross-origin loads.
    2508           3 :     secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL |
    2509             :                nsILoadInfo::SEC_SANDBOXED;
    2510           0 :   } else if (IsSystemXHR()) {
    2511             :     // For pages that have appropriate permissions, we want to still allow
    2512             :     // cross-origin loads, but make sure that the any potential result
    2513             :     // documents get the same principal as the loader.
    2514           0 :     secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
    2515             :                nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
    2516           0 :     loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
    2517             :   } else {
    2518             :     // Otherwise use CORS. Again, make sure that potential result documents
    2519             :     // use the same principal as the loader.
    2520           0 :     secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
    2521             :                nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
    2522             :   }
    2523             : 
    2524           3 :   if (mIsAnon) {
    2525           0 :     secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
    2526             :   }
    2527             : 
    2528             :   // Use the responsibleDocument if we have it, except for dedicated workers
    2529             :   // where it will be the parent document, which is not the one we want to use.
    2530             :   nsresult rv;
    2531           6 :   nsCOMPtr<nsIDocument> responsibleDocument = GetDocumentIfCurrent();
    2532           3 :   if (responsibleDocument && responsibleDocument->NodePrincipal() == mPrincipal) {
    2533           0 :     rv = NS_NewChannel(getter_AddRefs(mChannel),
    2534             :                        mRequestURL,
    2535             :                        responsibleDocument,
    2536             :                        secFlags,
    2537             :                        nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
    2538             :                        loadGroup,
    2539             :                        nullptr,   // aCallbacks
    2540           0 :                        loadFlags);
    2541             :   } else {
    2542             :     // Otherwise use the principal.
    2543           6 :     rv = NS_NewChannel(getter_AddRefs(mChannel),
    2544             :                        mRequestURL,
    2545             :                        mPrincipal,
    2546             :                        secFlags,
    2547             :                        nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
    2548             :                        loadGroup,
    2549             :                        nullptr,   // aCallbacks
    2550           3 :                        loadFlags);
    2551             :   }
    2552           3 :   NS_ENSURE_SUCCESS(rv, rv);
    2553             : 
    2554           6 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
    2555           3 :   if (httpChannel) {
    2556           0 :     rv = httpChannel->SetRequestMethod(mRequestMethod);
    2557           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2558             : 
    2559             :     // Set the initiator type
    2560           0 :     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
    2561           0 :     if (timedChannel) {
    2562           0 :       timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
    2563             :     }
    2564             :   }
    2565             : 
    2566             :   // Using the provided principal as the triggeringPrincipal is fine, since we
    2567             :   // want to be able to access any of the origins that the principal has access
    2568             :   // to during the security checks, but we don't want a document to inherit an
    2569             :   // expanded principal, so in that case we need to select the principal in the
    2570             :   // expanded principal's whitelist that can load our URL as principalToInherit.
    2571           6 :   nsCOMPtr<nsIPrincipal> resultingDocumentPrincipal(mPrincipal);
    2572           6 :   nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(mPrincipal);
    2573           3 :   if (ep) {
    2574           0 :     nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist = nullptr;
    2575           0 :     ep->GetWhiteList(&whitelist);
    2576           0 :     if (!whitelist) {
    2577           0 :       return NS_ERROR_FAILURE;
    2578             :     }
    2579           0 :     MOZ_ASSERT(!(secFlags & nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS));
    2580           0 :     bool dataInherits = (secFlags &
    2581             :       (nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
    2582           0 :        nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) != 0;
    2583           0 :     for (const auto& principal : *whitelist) {
    2584           0 :       if (NS_SUCCEEDED(principal->CheckMayLoad(mRequestURL, false, dataInherits))) {
    2585           0 :         resultingDocumentPrincipal = principal;
    2586           0 :         break;
    2587             :       }
    2588             :     }
    2589             :   }
    2590             : 
    2591           6 :   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
    2592           3 :   if (loadInfo) {
    2593           3 :     rv = loadInfo->SetPrincipalToInherit(resultingDocumentPrincipal);
    2594           3 :     NS_ENSURE_SUCCESS(rv, rv);
    2595             :   }
    2596             : 
    2597           3 :   return NS_OK;
    2598             : }
    2599             : 
    2600             : void
    2601           2 : XMLHttpRequestMainThread::MaybeLowerChannelPriority()
    2602             : {
    2603           2 :   nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
    2604           2 :   if (!doc) {
    2605           2 :     return;
    2606             :   }
    2607             : 
    2608           0 :   AutoJSAPI jsapi;
    2609           0 :   if (!jsapi.Init(GetOwnerGlobal())) {
    2610           0 :     return;
    2611             :   }
    2612             : 
    2613           0 :   JSContext* cx = jsapi.cx();
    2614           0 :   nsAutoCString fileNameString;
    2615           0 :   if (!nsJSUtils::GetCallingLocation(cx, fileNameString)) {
    2616           0 :     return;
    2617             :   }
    2618             : 
    2619           0 :   if (!doc->IsScriptTracking(fileNameString)) {
    2620           0 :     return;
    2621             :   }
    2622             : 
    2623           0 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
    2624           0 :   if (!p) {
    2625           0 :     return;
    2626             :   }
    2627             : 
    2628           0 :   p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
    2629             : }
    2630             : 
    2631             : nsresult
    2632           2 : XMLHttpRequestMainThread::InitiateFetch(nsIInputStream* aUploadStream,
    2633             :                                         int64_t aUploadLength,
    2634             :                                         nsACString& aUploadContentType)
    2635             : {
    2636             :   nsresult rv;
    2637             : 
    2638             :   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
    2639             :   // in turn keeps STOP button from becoming active.  If the consumer passed in
    2640             :   // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
    2641             :   // necko won't generate any progress notifications.
    2642           4 :   if (HasListenersFor(nsGkAtoms::onprogress) ||
    2643           0 :       (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress))) {
    2644             :     nsLoadFlags loadFlags;
    2645           2 :     mChannel->GetLoadFlags(&loadFlags);
    2646           2 :     loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
    2647           2 :     loadFlags |= nsIRequest::LOAD_NORMAL;
    2648           2 :     mChannel->SetLoadFlags(loadFlags);
    2649             :   }
    2650             : 
    2651           4 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
    2652           2 :   if (httpChannel) {
    2653             :     // If the user hasn't overridden the Accept header, set it to */* per spec.
    2654           0 :     if (!mAuthorRequestHeaders.Has("accept")) {
    2655           0 :       mAuthorRequestHeaders.Set("accept", NS_LITERAL_CSTRING("*/*"));
    2656             :     }
    2657             : 
    2658           0 :     mAuthorRequestHeaders.ApplyToChannel(httpChannel);
    2659             : 
    2660           0 :     if (!IsSystemXHR()) {
    2661           0 :       nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
    2662           0 :       nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
    2663           0 :       mozilla::net::ReferrerPolicy referrerPolicy = doc ?
    2664           0 :         doc->GetReferrerPolicy() : mozilla::net::RP_Unset;
    2665           0 :       nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
    2666           0 :                                                     httpChannel, referrerPolicy);
    2667             :     }
    2668             : 
    2669             :     // Some extensions override the http protocol handler and provide their own
    2670             :     // implementation. The channels returned from that implementation don't
    2671             :     // always seem to implement the nsIUploadChannel2 interface, presumably
    2672             :     // because it's a new interface. Eventually we should remove this and simply
    2673             :     // require that http channels implement the new interface (see bug 529041).
    2674           0 :     nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(httpChannel);
    2675           0 :     if (!uploadChannel2) {
    2676             :       nsCOMPtr<nsIConsoleService> consoleService =
    2677           0 :         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
    2678           0 :       if (consoleService) {
    2679           0 :         consoleService->LogStringMessage(
    2680             :           u"Http channel implementation doesn't support nsIUploadChannel2. "
    2681             :           "An extension has supplied a non-functional http protocol handler. "
    2682           0 :           "This will break behavior and in future releases not work at all.");
    2683             :       }
    2684             :     }
    2685             : 
    2686           0 :     if (aUploadStream) {
    2687             :       // If necessary, wrap the stream in a buffered stream so as to guarantee
    2688             :       // support for our upload when calling ExplicitSetUploadStream.
    2689           0 :       nsCOMPtr<nsIInputStream> bufferedStream;
    2690           0 :       if (!NS_InputStreamIsBuffered(aUploadStream)) {
    2691           0 :         rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
    2692           0 :                                        aUploadStream, 4096);
    2693           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2694             : 
    2695           0 :         aUploadStream = bufferedStream;
    2696             :       }
    2697             : 
    2698             :       // We want to use a newer version of the upload channel that won't
    2699             :       // ignore the necessary headers for an empty Content-Type.
    2700           0 :       nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
    2701             :       // This assertion will fire if buggy extensions are installed
    2702           0 :       NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
    2703           0 :       if (uploadChannel2) {
    2704           0 :           uploadChannel2->ExplicitSetUploadStream(aUploadStream,
    2705             :                                                   aUploadContentType,
    2706             :                                                   mUploadTotal, mRequestMethod,
    2707           0 :                                                   false);
    2708             :       } else {
    2709             :         // The http channel doesn't support the new nsIUploadChannel2.
    2710             :         // Emulate it as best we can using nsIUploadChannel.
    2711           0 :         if (aUploadContentType.IsEmpty()) {
    2712           0 :           aUploadContentType.AssignLiteral("application/octet-stream");
    2713             :         }
    2714             :         nsCOMPtr<nsIUploadChannel> uploadChannel =
    2715           0 :           do_QueryInterface(httpChannel);
    2716           0 :         uploadChannel->SetUploadStream(aUploadStream, aUploadContentType,
    2717           0 :                                        mUploadTotal);
    2718             :         // Reset the method to its original value
    2719           0 :         rv = httpChannel->SetRequestMethod(mRequestMethod);
    2720           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    2721             :       }
    2722             :     }
    2723             :   }
    2724             : 
    2725             :   // Due to the chrome-only XHR.channel API, we need a hacky way to set the
    2726             :   // SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
    2727             :   // .withCredentials can be called after open() is called.
    2728             :   // Not doing this for privileged system XHRs since those don't use CORS.
    2729           2 :   if (!IsSystemXHR() && !mIsAnon && mFlagACwithCredentials) {
    2730           0 :     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
    2731           0 :     if (loadInfo) {
    2732           0 :       static_cast<net::LoadInfo*>(loadInfo.get())->SetIncludeCookiesSecFlag();
    2733             :     }
    2734             :   }
    2735             : 
    2736             :   // We never let XHR be blocked by head CSS/JS loads to avoid potential
    2737             :   // deadlock where server generation of CSS/JS requires an XHR signal.
    2738           4 :   nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
    2739           2 :   if (cos) {
    2740           0 :     cos->AddClassFlags(nsIClassOfService::Unblocked);
    2741             : 
    2742             :     // Mark channel as urgent-start if the XHR is triggered by user input
    2743             :     // events.
    2744           0 :     if (EventStateManager::IsHandlingUserInput()) {
    2745           0 :       cos->AddClassFlags(nsIClassOfService::UrgentStart);
    2746             :     }
    2747             :   }
    2748             : 
    2749             :   // Disable Necko-internal response timeouts.
    2750             :   nsCOMPtr<nsIHttpChannelInternal>
    2751           4 :     internalHttpChannel(do_QueryInterface(mChannel));
    2752           2 :   if (internalHttpChannel) {
    2753           0 :     rv = internalHttpChannel->SetResponseTimeoutEnabled(false);
    2754           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2755             :   }
    2756             : 
    2757           2 :   if (!mIsAnon) {
    2758           2 :     AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
    2759             :   }
    2760             : 
    2761             :   // Bypass the network cache in cases where it makes no sense:
    2762             :   // POST responses are always unique, and we provide no API that would
    2763             :   // allow our consumers to specify a "cache key" to access old POST
    2764             :   // responses, so they are not worth caching.
    2765           2 :   if (mRequestMethod.EqualsLiteral("POST")) {
    2766             :     AddLoadFlags(mChannel,
    2767             :                  nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE |
    2768           0 :                  nsIRequest::INHIBIT_CACHING);
    2769             :   } else {
    2770             :     // When we are sync loading, we need to bypass the local cache when it would
    2771             :     // otherwise block us waiting for exclusive access to the cache.  If we don't
    2772             :     // do this, then we could dead lock in some cases (see bug 309424).
    2773             :     //
    2774             :     // Also don't block on the cache entry on async if it is busy - favoring parallelism
    2775             :     // over cache hit rate for xhr. This does not disable the cache everywhere -
    2776             :     // only in cases where more than one channel for the same URI is accessed
    2777             :     // simultanously.
    2778           2 :     AddLoadFlags(mChannel, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
    2779             :   }
    2780             : 
    2781             :   // Since we expect XML data, set the type hint accordingly
    2782             :   // if the channel doesn't know any content type.
    2783             :   // This means that we always try to parse local files as XML
    2784             :   // ignoring return value, as this is not critical. Use text/xml as fallback
    2785             :   // MIME type.
    2786           4 :   nsAutoCString contentType;
    2787           6 :   if (NS_FAILED(mChannel->GetContentType(contentType)) ||
    2788           4 :       contentType.IsEmpty() ||
    2789           2 :       contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
    2790           2 :     mChannel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
    2791             :   }
    2792             : 
    2793             :   // Set up the preflight if needed
    2794           2 :   if (!IsSystemXHR()) {
    2795           0 :     nsTArray<nsCString> CORSUnsafeHeaders;
    2796           0 :     mAuthorRequestHeaders.GetCORSUnsafeHeaders(CORSUnsafeHeaders);
    2797           0 :     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
    2798           0 :     if (loadInfo) {
    2799           0 :       loadInfo->SetCorsPreflightInfo(CORSUnsafeHeaders,
    2800           0 :                                      mFlagHadUploadListenersOnSend);
    2801             :     }
    2802             :   }
    2803             : 
    2804             :   // Hook us up to listen to redirects and the like. Only do this very late
    2805             :   // since this creates a cycle between the channel and us. This cycle has
    2806             :   // to be manually broken if anything below fails.
    2807           2 :   mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
    2808           2 :   mChannel->SetNotificationCallbacks(this);
    2809             : 
    2810           2 :   if (internalHttpChannel) {
    2811           0 :     internalHttpChannel->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
    2812             :   }
    2813             : 
    2814             :   // Because of bug 682305, we can't let listener be the XHR object itself
    2815             :   // because JS wouldn't be able to use it. So create a listener around 'this'.
    2816             :   // Make sure to hold a strong reference so that we don't leak the wrapper.
    2817           4 :   nsCOMPtr<nsIStreamListener> listener = new net::nsStreamListenerWrapper(this);
    2818             : 
    2819             :   // Check if this XHR is created from a tracking script.
    2820             :   // If yes, lower the channel's priority.
    2821           2 :   if (nsContentUtils::IsLowerNetworkPriority()) {
    2822           2 :     MaybeLowerChannelPriority();
    2823             :   }
    2824             : 
    2825             :   // Start reading from the channel
    2826           2 :   rv = mChannel->AsyncOpen2(listener);
    2827           2 :   listener = nullptr;
    2828           2 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2829             :     // Drop our ref to the channel to avoid cycles. Also drop channel's
    2830             :     // ref to us to be extra safe.
    2831           0 :     mChannel->SetNotificationCallbacks(mNotificationCallbacks);
    2832           0 :     mChannel = nullptr;
    2833             : 
    2834           0 :     mErrorLoad = ErrorType::eChannelOpen;
    2835             : 
    2836             :     // Per spec, we throw on sync errors, but not async.
    2837           0 :     if (mFlagSynchronous) {
    2838           0 :       mState = State::done;
    2839           0 :       return NS_ERROR_DOM_NETWORK_ERR;
    2840             :     }
    2841             :   }
    2842             : 
    2843           2 :   return NS_OK;
    2844             : }
    2845             : 
    2846             : NS_IMETHODIMP
    2847           2 : XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
    2848             : {
    2849           2 :   if (!aVariant) {
    2850           0 :     return SendInternal(nullptr);
    2851             :   }
    2852             : 
    2853             :   uint16_t dataType;
    2854           2 :   nsresult rv = aVariant->GetDataType(&dataType);
    2855           2 :   NS_ENSURE_SUCCESS(rv, rv);
    2856             : 
    2857           4 :   if (dataType == nsIDataType::VTYPE_INTERFACE ||
    2858           2 :       dataType == nsIDataType::VTYPE_INTERFACE_IS) {
    2859           0 :     nsCOMPtr<nsISupports> supports;
    2860             :     nsID *iid;
    2861           0 :     rv = aVariant->GetAsInterface(&iid, getter_AddRefs(supports));
    2862           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2863             : 
    2864           0 :     free(iid);
    2865             : 
    2866             :     // document?
    2867           0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(supports);
    2868           0 :     if (doc) {
    2869           0 :       BodyExtractor<nsIDocument> body(doc);
    2870           0 :       return SendInternal(&body);
    2871             :     }
    2872             : 
    2873             :     // nsISupportsString?
    2874           0 :     nsCOMPtr<nsISupportsString> wstr = do_QueryInterface(supports);
    2875           0 :     if (wstr) {
    2876           0 :       nsAutoString string;
    2877           0 :       wstr->GetData(string);
    2878           0 :       BodyExtractor<const nsAString> body(&string);
    2879           0 :       return SendInternal(&body);
    2880             :     }
    2881             : 
    2882             :     // nsIInputStream?
    2883           0 :     nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
    2884           0 :     if (stream) {
    2885           0 :       BodyExtractor<nsIInputStream> body(stream);
    2886           0 :       return SendInternal(&body);
    2887             :     }
    2888             : 
    2889             :     // nsIXHRSendable?
    2890           0 :     nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
    2891           0 :     if (sendable) {
    2892           0 :       BodyExtractor<nsIXHRSendable> body(sendable);
    2893           0 :       return SendInternal(&body);
    2894             :     }
    2895             : 
    2896             :     // ArrayBuffer?
    2897           0 :     JS::RootingContext* rootingCx = RootingCx();
    2898           0 :     JS::Rooted<JS::Value> realVal(rootingCx);
    2899             : 
    2900           0 :     nsresult rv = aVariant->GetAsJSVal(&realVal);
    2901           0 :     if (NS_SUCCEEDED(rv) && !realVal.isPrimitive()) {
    2902           0 :       JS::Rooted<JSObject*> obj(rootingCx, realVal.toObjectOrNull());
    2903           0 :       RootedTypedArray<ArrayBuffer> buf(rootingCx);
    2904           0 :       if (buf.Init(obj)) {
    2905           0 :         BodyExtractor<const ArrayBuffer> body(&buf);
    2906           0 :         return SendInternal(&body);
    2907             :       }
    2908           0 :     }
    2909           4 :   } else if (dataType == nsIDataType::VTYPE_VOID ||
    2910           2 :            dataType == nsIDataType::VTYPE_EMPTY) {
    2911           0 :     return SendInternal(nullptr);
    2912             :   }
    2913             : 
    2914           2 :   char16_t* data = nullptr;
    2915           2 :   uint32_t len = 0;
    2916           2 :   rv = aVariant->GetAsWStringWithSize(&len, &data);
    2917           2 :   NS_ENSURE_SUCCESS(rv, rv);
    2918             : 
    2919           4 :   nsString string;
    2920           2 :   string.Adopt(data, len);
    2921             : 
    2922           2 :   BodyExtractor<const nsAString> body(&string);
    2923           2 :   return SendInternal(&body);
    2924             : }
    2925             : 
    2926             : void
    2927           0 : XMLHttpRequestMainThread::UnsuppressEventHandlingAndResume()
    2928             : {
    2929           0 :   MOZ_ASSERT(NS_IsMainThread());
    2930           0 :   MOZ_ASSERT(mFlagSynchronous);
    2931             : 
    2932           0 :   if (mSuspendedDoc) {
    2933           0 :     mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(true);
    2934           0 :     mSuspendedDoc = nullptr;
    2935             :   }
    2936             : 
    2937           0 :   if (mResumeTimeoutRunnable) {
    2938           0 :     DispatchToMainThread(mResumeTimeoutRunnable.forget());
    2939           0 :     mResumeTimeoutRunnable = nullptr;
    2940             :   }
    2941           0 : }
    2942             : 
    2943             : nsresult
    2944           2 : XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody)
    2945             : {
    2946           2 :   MOZ_ASSERT(NS_IsMainThread());
    2947             : 
    2948           2 :   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
    2949             : 
    2950             :   // Step 1
    2951           2 :   if (mState != State::opened) {
    2952           0 :     return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
    2953             :   }
    2954             : 
    2955             :   // Step 2
    2956           2 :   if (mFlagSend) {
    2957           0 :     return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
    2958             :   }
    2959             : 
    2960           2 :   nsresult rv = CheckInnerWindowCorrectness();
    2961           2 :   if (NS_FAILED(rv)) {
    2962           0 :     return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
    2963             :   }
    2964             : 
    2965             :   // If open() failed to create the channel, then throw a network error
    2966             :   // as per spec. We really should create the channel here in send(), but
    2967             :   // we have internal code relying on the channel being created in open().
    2968           2 :   if (!mChannel) {
    2969           0 :     return NS_ERROR_DOM_NETWORK_ERR;
    2970             :   }
    2971             : 
    2972           2 :   PopulateNetworkInterfaceId();
    2973             : 
    2974             :   // XXX We should probably send a warning to the JS console
    2975             :   //     if there are no event listeners set and we are doing
    2976             :   //     an asynchronous call.
    2977             : 
    2978           2 :   mUploadTransferred = 0;
    2979           2 :   mUploadTotal = 0;
    2980             :   // By default we don't have any upload, so mark upload complete.
    2981           2 :   mUploadComplete = true;
    2982           2 :   mErrorLoad = ErrorType::eOK;
    2983           2 :   mLoadTotal = -1;
    2984           4 :   nsCOMPtr<nsIInputStream> uploadStream;
    2985           4 :   nsAutoCString uploadContentType;
    2986           4 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
    2987           4 :   if (aBody && httpChannel &&
    2988           2 :       !mRequestMethod.EqualsLiteral("GET") &&
    2989           0 :       !mRequestMethod.EqualsLiteral("HEAD")) {
    2990             : 
    2991           0 :     nsAutoCString charset;
    2992           0 :     nsAutoCString defaultContentType;
    2993             :     uint64_t size_u64;
    2994           0 :     rv = aBody->GetAsStream(getter_AddRefs(uploadStream),
    2995           0 :                             &size_u64, defaultContentType, charset);
    2996           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2997             : 
    2998             :     // make sure it fits within js MAX_SAFE_INTEGER
    2999           0 :     mUploadTotal =
    3000           0 :       net::InScriptableRange(size_u64) ? static_cast<int64_t>(size_u64) : -1;
    3001             : 
    3002           0 :     if (uploadStream) {
    3003             :       // If author set no Content-Type, use the default from GetAsStream().
    3004           0 :       mAuthorRequestHeaders.Get("content-type", uploadContentType);
    3005           0 :       if (uploadContentType.IsVoid()) {
    3006           0 :         uploadContentType = defaultContentType;
    3007             :       }
    3008             : 
    3009             :       // We don't want to set a charset for streams.
    3010           0 :       if (!charset.IsEmpty()) {
    3011             :         // Replace all case-insensitive matches of the charset in the
    3012             :         // content-type with the correct case.
    3013           0 :         RequestHeaders::CharsetIterator iter(uploadContentType);
    3014           0 :         const nsCaseInsensitiveCStringComparator cmp;
    3015           0 :         while (iter.Next()) {
    3016           0 :           if (!iter.Equals(charset, cmp)) {
    3017           0 :             iter.Replace(charset);
    3018             :           }
    3019             :         }
    3020             :       }
    3021             : 
    3022           0 :       mUploadComplete = false;
    3023             :     }
    3024             :   }
    3025             : 
    3026           2 :   ResetResponse();
    3027             : 
    3028             :   // Check if we should enable cross-origin upload listeners.
    3029           2 :   if (mUpload && mUpload->HasListeners()) {
    3030           0 :     mFlagHadUploadListenersOnSend = true;
    3031             :   }
    3032             : 
    3033           2 :   mIsMappedArrayBuffer = false;
    3034           2 :   if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
    3035           0 :       IsMappedArrayBufferEnabled()) {
    3036           0 :     nsCOMPtr<nsIURI> uri;
    3037           0 :     nsAutoCString scheme;
    3038             : 
    3039           0 :     rv = mChannel->GetURI(getter_AddRefs(uri));
    3040           0 :     if (NS_SUCCEEDED(rv)) {
    3041           0 :       uri->GetScheme(scheme);
    3042           0 :       if (scheme.LowerCaseEqualsLiteral("jar")) {
    3043           0 :         mIsMappedArrayBuffer = true;
    3044             :       }
    3045             :     }
    3046             :   }
    3047             : 
    3048           2 :   rv = InitiateFetch(uploadStream, mUploadTotal, uploadContentType);
    3049           2 :   NS_ENSURE_SUCCESS(rv, rv);
    3050             : 
    3051             :   // Start our timeout
    3052           2 :   mRequestSentTime = PR_Now();
    3053           2 :   StartTimeoutTimer();
    3054             : 
    3055           2 :   mWaitingForOnStopRequest = true;
    3056             : 
    3057             :   // Step 8
    3058           2 :   mFlagSend = true;
    3059             : 
    3060             :   // If we're synchronous, spin an event loop here and wait
    3061           2 :   if (mFlagSynchronous) {
    3062           0 :     mFlagSyncLooping = true;
    3063             : 
    3064           0 :     if (GetOwner()) {
    3065           0 :       if (nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetOwner()->GetOuterWindow()->GetTop()) {
    3066           0 :         if (nsCOMPtr<nsPIDOMWindowInner> topInner = topWindow->GetCurrentInnerWindow()) {
    3067           0 :           mSuspendedDoc = topWindow->GetExtantDoc();
    3068           0 :           if (mSuspendedDoc) {
    3069           0 :             mSuspendedDoc->SuppressEventHandling();
    3070             :           }
    3071           0 :           topInner->Suspend();
    3072           0 :           mResumeTimeoutRunnable = new nsResumeTimeoutsEvent(topInner);
    3073             :         }
    3074             :       }
    3075             :     }
    3076             : 
    3077           0 :     SuspendEventDispatching();
    3078           0 :     StopProgressEventTimer();
    3079             : 
    3080           0 :     SyncTimeoutType syncTimeoutType = MaybeStartSyncTimeoutTimer();
    3081           0 :     if (syncTimeoutType == eErrorOrExpired) {
    3082           0 :       Abort();
    3083           0 :       rv = NS_ERROR_DOM_NETWORK_ERR;
    3084             :     }
    3085             : 
    3086           0 :     if (NS_SUCCEEDED(rv)) {
    3087           0 :       nsAutoSyncOperation sync(mSuspendedDoc);
    3088           0 :       if (!SpinEventLoopUntil([&]() { return !mFlagSyncLooping; })) {
    3089           0 :         rv = NS_ERROR_UNEXPECTED;
    3090             :       }
    3091             : 
    3092             :       // Time expired... We should throw.
    3093           0 :       if (syncTimeoutType == eTimerStarted && !mSyncTimeoutTimer) {
    3094           0 :         rv = NS_ERROR_DOM_NETWORK_ERR;
    3095             :       }
    3096             : 
    3097           0 :       CancelSyncTimeoutTimer();
    3098             :     }
    3099             : 
    3100           0 :     UnsuppressEventHandlingAndResume();
    3101           0 :     ResumeEventDispatching();
    3102             :   } else {
    3103             :     // Now that we've successfully opened the channel, we can change state.  Note
    3104             :     // that this needs to come after the AsyncOpen() and rv check, because this
    3105             :     // can run script that would try to restart this request, and that could end
    3106             :     // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
    3107           2 :     StopProgressEventTimer();
    3108             : 
    3109             :     // Upload phase beginning; start the progress event timer if necessary.
    3110           2 :     if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
    3111           0 :       StartProgressEventTimer();
    3112             :     }
    3113             :     // Dispatch loadstart events
    3114           2 :     DispatchProgressEvent(this, ProgressEventType::loadstart, 0, -1);
    3115           2 :     if (mUpload && !mUploadComplete) {
    3116           0 :       DispatchProgressEvent(mUpload, ProgressEventType::loadstart,
    3117           0 :                             0, mUploadTotal);
    3118             :     }
    3119             :   }
    3120             : 
    3121           2 :   if (!mChannel) {
    3122             :     // Per spec, silently fail on async request failures; throw for sync.
    3123           0 :     if (mFlagSynchronous) {
    3124           0 :       mState = State::done;
    3125           0 :       return NS_ERROR_DOM_NETWORK_ERR;
    3126             :     } else {
    3127             :       // Defer the actual sending of async events just in case listeners
    3128             :       // are attached after the send() method is called.
    3129           0 :       return DispatchToMainThread(NewRunnableMethod<ProgressEventType>(
    3130             :         "dom::XMLHttpRequestMainThread::CloseRequestWithError",
    3131             :         this,
    3132             :         &XMLHttpRequestMainThread::CloseRequestWithError,
    3133           0 :         ProgressEventType::error));
    3134             :     }
    3135             :   }
    3136             : 
    3137           2 :   return rv;
    3138             : }
    3139             : 
    3140             : /* static */
    3141             : bool
    3142           0 : XMLHttpRequestMainThread::IsMappedArrayBufferEnabled()
    3143             : {
    3144             :   static bool sMappedArrayBufferAdded = false;
    3145             :   static bool sIsMappedArrayBufferEnabled;
    3146             : 
    3147           0 :   if (!sMappedArrayBufferAdded) {
    3148             :     Preferences::AddBoolVarCache(&sIsMappedArrayBufferEnabled,
    3149             :                                  "dom.mapped_arraybuffer.enabled",
    3150           0 :                                  true);
    3151           0 :     sMappedArrayBufferAdded = true;
    3152             :   }
    3153             : 
    3154           0 :   return sIsMappedArrayBufferEnabled;
    3155             : }
    3156             : 
    3157             : /* static */
    3158             : bool
    3159           0 : XMLHttpRequestMainThread::IsLowercaseResponseHeader()
    3160             : {
    3161             :   static bool sLowercaseResponseHeaderAdded = false;
    3162             :   static bool sIsLowercaseResponseHeaderEnabled;
    3163             : 
    3164           0 :   if (!sLowercaseResponseHeaderAdded) {
    3165             :     Preferences::AddBoolVarCache(&sIsLowercaseResponseHeaderEnabled,
    3166             :                                  "dom.xhr.lowercase_header.enabled",
    3167           0 :                                  false);
    3168           0 :     sLowercaseResponseHeaderAdded = true;
    3169             :   }
    3170             : 
    3171           0 :   return sIsLowercaseResponseHeaderEnabled;
    3172             : }
    3173             : 
    3174             : // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
    3175             : NS_IMETHODIMP
    3176           0 : XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
    3177             :                                            const nsACString& aValue)
    3178             : {
    3179             :   // Step 1
    3180           0 :   if (mState != State::opened) {
    3181           0 :     return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
    3182             :   }
    3183             : 
    3184             :   // Step 2
    3185           0 :   if (mFlagSend) {
    3186           0 :     return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
    3187             :   }
    3188             : 
    3189             :   // Step 3
    3190           0 :   nsAutoCString value;
    3191           0 :   NS_TrimHTTPWhitespace(aValue, value);
    3192             : 
    3193             :   // Step 4
    3194           0 :   if (!NS_IsValidHTTPToken(aName) || !NS_IsReasonableHTTPHeaderValue(value)) {
    3195           0 :     return NS_ERROR_DOM_INVALID_HEADER_NAME;
    3196             :   }
    3197             : 
    3198             :   // Step 5
    3199           0 :   bool isPrivilegedCaller = IsSystemXHR();
    3200           0 :   bool isForbiddenHeader = nsContentUtils::IsForbiddenRequestHeader(aName);
    3201           0 :   if (!isPrivilegedCaller && isForbiddenHeader) {
    3202           0 :     NS_ConvertUTF8toUTF16 name(aName);
    3203           0 :     const char16_t* params[] = { name.get() };
    3204           0 :     LogMessage("ForbiddenHeaderWarning", GetOwner(), params, ArrayLength(params));
    3205           0 :     return NS_OK;
    3206             :   }
    3207             : 
    3208             :   // Step 6.1
    3209             :   // Skipping for now, as normalizing the case of header names may not be
    3210             :   // web-compatible. See bug 1285036.
    3211             : 
    3212             :   // Step 6.2-6.3
    3213             :   // Gecko-specific: invalid headers can be set by privileged
    3214             :   //                 callers, but will not merge.
    3215           0 :   if (isPrivilegedCaller && isForbiddenHeader) {
    3216           0 :     mAuthorRequestHeaders.Set(aName, value);
    3217             :   } else {
    3218           0 :     mAuthorRequestHeaders.MergeOrSet(aName, value);
    3219             :   }
    3220             : 
    3221           0 :   return NS_OK;
    3222             : }
    3223             : 
    3224             : NS_IMETHODIMP
    3225           0 : XMLHttpRequestMainThread::GetTimeout(uint32_t *aTimeout)
    3226             : {
    3227           0 :   *aTimeout = Timeout();
    3228           0 :   return NS_OK;
    3229             : }
    3230             : 
    3231             : NS_IMETHODIMP
    3232           0 : XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout)
    3233             : {
    3234           0 :   ErrorResult rv;
    3235           0 :   SetTimeout(aTimeout, rv);
    3236           0 :   return rv.StealNSResult();
    3237             : }
    3238             : 
    3239             : void
    3240           0 : XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
    3241             : {
    3242           0 :   if (mFlagSynchronous && mState != State::unsent && HasOrHasHadOwner()) {
    3243             :     /* Timeout is not supported for synchronous requests with an owning window,
    3244             :        per XHR2 spec. */
    3245           0 :     LogMessage("TimeoutSyncXHRWarning", GetOwner());
    3246           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
    3247           0 :     return;
    3248             :   }
    3249             : 
    3250           0 :   mTimeoutMilliseconds = aTimeout;
    3251           0 :   if (mRequestSentTime) {
    3252           0 :     StartTimeoutTimer();
    3253             :   }
    3254             : }
    3255             : 
    3256             : void
    3257           2 : XMLHttpRequestMainThread::SetTimerEventTarget(nsITimer* aTimer)
    3258             : {
    3259           4 :   if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
    3260           0 :     nsCOMPtr<nsIEventTarget> target = global->EventTargetFor(TaskCategory::Other);
    3261           0 :     aTimer->SetTarget(target);
    3262             :   }
    3263           2 : }
    3264             : 
    3265             : nsresult
    3266           0 : XMLHttpRequestMainThread::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable)
    3267             : {
    3268           0 :   if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
    3269           0 :     nsCOMPtr<nsIEventTarget> target = global->EventTargetFor(TaskCategory::Other);
    3270           0 :     MOZ_ASSERT(target);
    3271             : 
    3272           0 :     return target->Dispatch(Move(aRunnable), NS_DISPATCH_NORMAL);
    3273             :   }
    3274             : 
    3275           0 :   return NS_DispatchToMainThread(Move(aRunnable));
    3276             : }
    3277             : 
    3278             : void
    3279           2 : XMLHttpRequestMainThread::StartTimeoutTimer()
    3280             : {
    3281           2 :   MOZ_ASSERT(mRequestSentTime,
    3282             :              "StartTimeoutTimer mustn't be called before the request was sent!");
    3283           2 :   if (mState == State::done) {
    3284             :     // do nothing!
    3285           0 :     return;
    3286             :   }
    3287             : 
    3288           2 :   if (mTimeoutTimer) {
    3289           0 :     mTimeoutTimer->Cancel();
    3290             :   }
    3291             : 
    3292           2 :   if (!mTimeoutMilliseconds) {
    3293           2 :     return;
    3294             :   }
    3295             : 
    3296           0 :   if (!mTimeoutTimer) {
    3297           0 :     mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    3298           0 :     SetTimerEventTarget(mTimeoutTimer);
    3299             :   }
    3300             :   uint32_t elapsed =
    3301           0 :     (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
    3302           0 :   mTimeoutTimer->InitWithCallback(
    3303             :     this,
    3304           0 :     mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
    3305             :     nsITimer::TYPE_ONE_SHOT
    3306           0 :   );
    3307             : }
    3308             : 
    3309             : NS_IMETHODIMP
    3310           3 : XMLHttpRequestMainThread::GetReadyState(uint16_t *aState)
    3311             : {
    3312           3 :   *aState = ReadyState();
    3313           3 :   return NS_OK;
    3314             : }
    3315             : 
    3316             : uint16_t
    3317          71 : XMLHttpRequestMainThread::ReadyState() const
    3318             : {
    3319             :   // Translate some of our internal states for external consumers
    3320          71 :   switch(mState) {
    3321             :     case State::unsent:
    3322           0 :       return UNSENT;
    3323             :     case State::opened:
    3324          23 :       return OPENED;
    3325             :     case State::headers_received:
    3326           8 :       return HEADERS_RECEIVED;
    3327             :     case State::loading:
    3328          16 :       return LOADING;
    3329             :     case State::done:
    3330          24 :       return DONE;
    3331             :     default:
    3332           0 :       MOZ_CRASH("Unknown state");
    3333             :   }
    3334             :   return 0;
    3335             : }
    3336             : 
    3337           0 : void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
    3338             : {
    3339           0 :   if (mState == State::loading || mState == State::done) {
    3340           0 :     ResetResponse();
    3341           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
    3342           0 :     return;
    3343             :   }
    3344             : 
    3345           0 :   mOverrideMimeType = aMimeType;
    3346             : }
    3347             : 
    3348             : NS_IMETHODIMP
    3349           0 : XMLHttpRequestMainThread::SlowOverrideMimeType(const nsAString& aMimeType)
    3350             : {
    3351           0 :   ErrorResult aRv;
    3352           0 :   OverrideMimeType(aMimeType, aRv);
    3353           0 :   return aRv.StealNSResult();
    3354             : }
    3355             : 
    3356             : NS_IMETHODIMP
    3357           0 : XMLHttpRequestMainThread::GetMozBackgroundRequest(bool *_retval)
    3358             : {
    3359           0 :   *_retval = MozBackgroundRequest();
    3360           0 :   return NS_OK;
    3361             : }
    3362             : 
    3363             : bool
    3364           0 : XMLHttpRequestMainThread::MozBackgroundRequest() const
    3365             : {
    3366           0 :   return mFlagBackgroundRequest;
    3367             : }
    3368             : 
    3369             : NS_IMETHODIMP
    3370           0 : XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest)
    3371             : {
    3372           0 :   if (!IsSystemXHR()) {
    3373           0 :     return NS_ERROR_DOM_SECURITY_ERR;
    3374             :   }
    3375             : 
    3376           0 :   if (mState != State::unsent) {
    3377             :     // Can't change this while we're in the middle of something.
    3378           0 :     return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
    3379             :   }
    3380             : 
    3381           0 :   mFlagBackgroundRequest = aMozBackgroundRequest;
    3382             : 
    3383           0 :   return NS_OK;
    3384             : }
    3385             : 
    3386             : void
    3387           0 : XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest,
    3388             :                                                   ErrorResult& aRv)
    3389             : {
    3390             :   // No errors for this webIDL method on main-thread.
    3391           0 :   SetMozBackgroundRequest(aMozBackgroundRequest);
    3392           0 : }
    3393             : 
    3394             : NS_IMETHODIMP
    3395           0 : XMLHttpRequestMainThread::GetWithCredentials(bool *_retval)
    3396             : {
    3397           0 :   *_retval = WithCredentials();
    3398           0 :   return NS_OK;
    3399             : }
    3400             : 
    3401             : bool
    3402           0 : XMLHttpRequestMainThread::WithCredentials() const
    3403             : {
    3404           0 :   return mFlagACwithCredentials;
    3405             : }
    3406             : 
    3407             : NS_IMETHODIMP
    3408           0 : XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials)
    3409             : {
    3410           0 :   ErrorResult rv;
    3411           0 :   SetWithCredentials(aWithCredentials, rv);
    3412           0 :   return rv.StealNSResult();
    3413             : }
    3414             : 
    3415             : void
    3416           0 : XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
    3417             : {
    3418             :   // Return error if we're already processing a request.  Note that we can't use
    3419             :   // ReadyState() here, because it can't differentiate between "opened" and
    3420             :   // "sent", so we use mState directly.
    3421             : 
    3422           0 :   if ((mState != State::unsent && mState != State::opened) ||
    3423           0 :       mFlagSend || mIsAnon) {
    3424           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING);
    3425           0 :     return;
    3426             :   }
    3427             : 
    3428           0 :   mFlagACwithCredentials = aWithCredentials;
    3429             : }
    3430             : 
    3431             : nsresult
    3432           6 : XMLHttpRequestMainThread::ChangeState(State aState, bool aBroadcast)
    3433             : {
    3434           6 :   mState = aState;
    3435           6 :   nsresult rv = NS_OK;
    3436             : 
    3437           6 :   if (aState != State::headers_received && aState != State::loading) {
    3438           2 :     StopProgressEventTimer();
    3439             :   }
    3440             : 
    3441             : 
    3442           6 :   if (aBroadcast && (!mFlagSynchronous ||
    3443           0 :                      aState == State::opened ||
    3444             :                      aState == State::done)) {
    3445           6 :     rv = FireReadystatechangeEvent();
    3446             :   }
    3447             : 
    3448           6 :   return rv;
    3449             : }
    3450             : 
    3451             : /////////////////////////////////////////////////////
    3452             : // nsIChannelEventSink methods:
    3453             : //
    3454             : NS_IMETHODIMP
    3455           0 : XMLHttpRequestMainThread::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
    3456             :                                                  nsIChannel *aNewChannel,
    3457             :                                                  uint32_t    aFlags,
    3458             :                                                  nsIAsyncVerifyRedirectCallback *callback)
    3459             : {
    3460           0 :   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
    3461             : 
    3462             :   // Prepare to receive callback
    3463           0 :   mRedirectCallback = callback;
    3464           0 :   mNewRedirectChannel = aNewChannel;
    3465             : 
    3466           0 :   if (mChannelEventSink) {
    3467             :     nsCOMPtr<nsIAsyncVerifyRedirectCallback> fwd =
    3468           0 :       EnsureXPCOMifier();
    3469             : 
    3470           0 :     nsresult rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
    3471             :                                                             aNewChannel,
    3472           0 :                                                             aFlags, fwd);
    3473           0 :     if (NS_FAILED(rv)) {
    3474           0 :         mRedirectCallback = nullptr;
    3475           0 :         mNewRedirectChannel = nullptr;
    3476             :     }
    3477           0 :     return rv;
    3478             :   }
    3479           0 :   OnRedirectVerifyCallback(NS_OK);
    3480           0 :   return NS_OK;
    3481             : }
    3482             : 
    3483             : nsresult
    3484           0 : XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result)
    3485             : {
    3486           0 :   NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
    3487           0 :   NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
    3488             : 
    3489           0 :   if (NS_SUCCEEDED(result)) {
    3490           0 :     mChannel = mNewRedirectChannel;
    3491             : 
    3492           0 :     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
    3493           0 :     if (httpChannel) {
    3494             :       // Ensure all original headers are duplicated for the new channel (bug #553888)
    3495           0 :       mAuthorRequestHeaders.ApplyToChannel(httpChannel);
    3496             :     }
    3497             :   } else {
    3498           0 :     mErrorLoad = ErrorType::eRedirect;
    3499             :   }
    3500             : 
    3501           0 :   mNewRedirectChannel = nullptr;
    3502             : 
    3503           0 :   mRedirectCallback->OnRedirectVerifyCallback(result);
    3504           0 :   mRedirectCallback = nullptr;
    3505             : 
    3506             :   // It's important that we return success here. If we return the result code
    3507             :   // that we were passed, JavaScript callers who cancel the redirect will wind
    3508             :   // up throwing an exception in the process.
    3509           0 :   return NS_OK;
    3510             : }
    3511             : 
    3512             : /////////////////////////////////////////////////////
    3513             : // nsIProgressEventSink methods:
    3514             : //
    3515             : 
    3516             : NS_IMETHODIMP
    3517           2 : XMLHttpRequestMainThread::OnProgress(nsIRequest *aRequest, nsISupports *aContext, int64_t aProgress, int64_t aProgressMax)
    3518             : {
    3519             :   // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
    3520             :   // So, try to remove the headers, if possible.
    3521           2 :   bool lengthComputable = (aProgressMax != -1);
    3522           2 :   if (InUploadPhase()) {
    3523           0 :     int64_t loaded = aProgress;
    3524           0 :     if (lengthComputable) {
    3525           0 :       int64_t headerSize = aProgressMax - mUploadTotal;
    3526           0 :       loaded -= headerSize;
    3527             :     }
    3528           0 :     mUploadTransferred = loaded;
    3529           0 :     mProgressSinceLastProgressEvent = true;
    3530             : 
    3531           0 :     if (!mFlagSynchronous && !mProgressTimerIsActive) {
    3532           0 :       StartProgressEventTimer();
    3533             :     }
    3534             :   } else {
    3535           2 :     mLoadTotal = aProgressMax;
    3536           2 :     mLoadTransferred = aProgress;
    3537             :     // OnDataAvailable() handles mProgressSinceLastProgressEvent
    3538             :     // for the download phase.
    3539             :   }
    3540             : 
    3541           2 :   if (mProgressEventSink) {
    3542           0 :     mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
    3543           0 :                                    aProgressMax);
    3544             :   }
    3545             : 
    3546           2 :   return NS_OK;
    3547             : }
    3548             : 
    3549             : NS_IMETHODIMP
    3550           0 : XMLHttpRequestMainThread::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatus, const char16_t *aStatusArg)
    3551             : {
    3552           0 :   if (mProgressEventSink) {
    3553           0 :     mProgressEventSink->OnStatus(aRequest, aContext, aStatus, aStatusArg);
    3554             :   }
    3555             : 
    3556           0 :   return NS_OK;
    3557             : }
    3558             : 
    3559             : bool
    3560           8 : XMLHttpRequestMainThread::AllowUploadProgress()
    3561             : {
    3562           8 :   return !IsCrossSiteCORSRequest() ||
    3563           8 :          mFlagHadUploadListenersOnSend;
    3564             : }
    3565             : 
    3566             : /////////////////////////////////////////////////////
    3567             : // nsIInterfaceRequestor methods:
    3568             : //
    3569             : NS_IMETHODIMP
    3570           6 : XMLHttpRequestMainThread::GetInterface(const nsIID & aIID, void **aResult)
    3571             : {
    3572             :   nsresult rv;
    3573             : 
    3574             :   // Make sure to return ourselves for the channel event sink interface and
    3575             :   // progress event sink interface, no matter what.  We can forward these to
    3576             :   // mNotificationCallbacks if it wants to get notifications for them.  But we
    3577             :   // need to see these notifications for proper functioning.
    3578           6 :   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
    3579           0 :     mChannelEventSink = do_GetInterface(mNotificationCallbacks);
    3580           0 :     *aResult = static_cast<nsIChannelEventSink*>(EnsureXPCOMifier().take());
    3581           0 :     return NS_OK;
    3582           6 :   } else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
    3583           2 :     mProgressEventSink = do_GetInterface(mNotificationCallbacks);
    3584           2 :     *aResult = static_cast<nsIProgressEventSink*>(EnsureXPCOMifier().take());
    3585           2 :     return NS_OK;
    3586             :   }
    3587             : 
    3588             :   // Now give mNotificationCallbacks (if non-null) a chance to return the
    3589             :   // desired interface.
    3590           4 :   if (mNotificationCallbacks) {
    3591           0 :     rv = mNotificationCallbacks->GetInterface(aIID, aResult);
    3592           0 :     if (NS_SUCCEEDED(rv)) {
    3593           0 :       NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
    3594           0 :       return rv;
    3595             :     }
    3596             :   }
    3597             : 
    3598           4 :   if (mFlagBackgroundRequest) {
    3599           0 :     nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
    3600             : 
    3601             :     // Ignore failure to get component, we may not have all its dependencies
    3602             :     // available
    3603           0 :     if (NS_SUCCEEDED(rv)) {
    3604           0 :       rv = badCertHandler->GetInterface(aIID, aResult);
    3605           0 :       if (NS_SUCCEEDED(rv))
    3606           0 :         return rv;
    3607             :     }
    3608             :   }
    3609           8 :   else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
    3610           4 :            aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
    3611             :     nsCOMPtr<nsIPromptFactory> wwatch =
    3612           0 :       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
    3613           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3614             : 
    3615             :     // Get the an auth prompter for our window so that the parenting
    3616             :     // of the dialogs works as it should when using tabs.
    3617             : 
    3618           0 :     nsCOMPtr<nsPIDOMWindowOuter> window;
    3619           0 :     if (GetOwner()) {
    3620           0 :       window = GetOwner()->GetOuterWindow();
    3621             :     }
    3622             : 
    3623           0 :     return wwatch->GetPrompt(window, aIID,
    3624           0 :                              reinterpret_cast<void**>(aResult));
    3625             :   }
    3626             :   // Now check for the various XHR non-DOM interfaces, except
    3627             :   // nsIProgressEventSink and nsIChannelEventSink which we already
    3628             :   // handled above.
    3629           4 :   else if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
    3630           0 :     *aResult = static_cast<nsIStreamListener*>(EnsureXPCOMifier().take());
    3631           0 :     return NS_OK;
    3632             :   }
    3633           4 :   else if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
    3634           0 :     *aResult = static_cast<nsIRequestObserver*>(EnsureXPCOMifier().take());
    3635           0 :     return NS_OK;
    3636             :   }
    3637           4 :   else if (aIID.Equals(NS_GET_IID(nsITimerCallback))) {
    3638           0 :     *aResult = static_cast<nsITimerCallback*>(EnsureXPCOMifier().take());
    3639           0 :     return NS_OK;
    3640             :   }
    3641             : 
    3642           4 :   return QueryInterface(aIID, aResult);
    3643             : }
    3644             : 
    3645             : void
    3646           0 : XMLHttpRequestMainThread::GetInterface(JSContext* aCx, nsIJSID* aIID,
    3647             :                                        JS::MutableHandle<JS::Value> aRetval,
    3648             :                                        ErrorResult& aRv)
    3649             : {
    3650           0 :   dom::GetInterface(aCx, this, aIID, aRetval, aRv);
    3651           0 : }
    3652             : 
    3653             : XMLHttpRequestUpload*
    3654           3 : XMLHttpRequestMainThread::GetUpload(ErrorResult& aRv)
    3655             : {
    3656           3 :   if (!mUpload) {
    3657           3 :     mUpload = new XMLHttpRequestUpload(this);
    3658             :   }
    3659           3 :   return mUpload;
    3660             : }
    3661             : 
    3662             : NS_IMETHODIMP
    3663           0 : XMLHttpRequestMainThread::GetUpload(nsIXMLHttpRequestUpload** aUpload)
    3664             : {
    3665           0 :   ErrorResult rv;
    3666           0 :   RefPtr<XMLHttpRequestUpload> upload = GetUpload(rv);
    3667           0 :   upload.forget(aUpload);
    3668           0 :   return rv.StealNSResult();
    3669             : }
    3670             : 
    3671             : bool
    3672           0 : XMLHttpRequestMainThread::MozAnon() const
    3673             : {
    3674           0 :   return mIsAnon;
    3675             : }
    3676             : 
    3677             : NS_IMETHODIMP
    3678           0 : XMLHttpRequestMainThread::GetMozAnon(bool* aAnon)
    3679             : {
    3680           0 :   *aAnon = MozAnon();
    3681           0 :   return NS_OK;
    3682             : }
    3683             : 
    3684             : bool
    3685           0 : XMLHttpRequestMainThread::MozSystem() const
    3686             : {
    3687           0 :   return IsSystemXHR();
    3688             : }
    3689             : 
    3690             : NS_IMETHODIMP
    3691           0 : XMLHttpRequestMainThread::GetMozSystem(bool* aSystem)
    3692             : {
    3693           0 :   *aSystem = MozSystem();
    3694           0 :   return NS_OK;
    3695             : }
    3696             : 
    3697             : void
    3698           0 : XMLHttpRequestMainThread::HandleTimeoutCallback()
    3699             : {
    3700           0 :   if (mState == State::done) {
    3701           0 :     NS_NOTREACHED("XMLHttpRequestMainThread::HandleTimeoutCallback with completed request");
    3702             :     // do nothing!
    3703           0 :     return;
    3704             :   }
    3705             : 
    3706           0 :   mFlagTimedOut = true;
    3707           0 :   CloseRequestWithError(ProgressEventType::timeout);
    3708             : }
    3709             : 
    3710             : NS_IMETHODIMP
    3711           0 : XMLHttpRequestMainThread::Notify(nsITimer* aTimer)
    3712             : {
    3713           0 :   if (mProgressNotifier == aTimer) {
    3714           0 :     HandleProgressTimerCallback();
    3715           0 :     return NS_OK;
    3716             :   }
    3717             : 
    3718           0 :   if (mTimeoutTimer == aTimer) {
    3719           0 :     HandleTimeoutCallback();
    3720           0 :     return NS_OK;
    3721             :   }
    3722             : 
    3723           0 :   if (mSyncTimeoutTimer == aTimer) {
    3724           0 :     HandleSyncTimeoutTimer();
    3725           0 :     return NS_OK;
    3726             :   }
    3727             : 
    3728             :   // Just in case some JS user wants to QI to nsITimerCallback and play with us...
    3729           0 :   NS_WARNING("Unexpected timer!");
    3730           0 :   return NS_ERROR_INVALID_POINTER;
    3731             : }
    3732             : 
    3733             : void
    3734           0 : XMLHttpRequestMainThread::HandleProgressTimerCallback()
    3735             : {
    3736             :   // Don't fire the progress event if mLoadTotal is 0, see XHR spec step 6.1
    3737           0 :   if (!mLoadTotal && mLoadTransferred) {
    3738           0 :     return;
    3739             :   }
    3740             : 
    3741           0 :   mProgressTimerIsActive = false;
    3742             : 
    3743           0 :   if (!mProgressSinceLastProgressEvent || mErrorLoad != ErrorType::eOK) {
    3744           0 :     return;
    3745             :   }
    3746             : 
    3747           0 :   if (InUploadPhase()) {
    3748           0 :     if (mUpload && !mUploadComplete) {
    3749           0 :       DispatchProgressEvent(mUpload, ProgressEventType::progress,
    3750           0 :                             mUploadTransferred, mUploadTotal);
    3751             :     }
    3752             :   } else {
    3753           0 :     FireReadystatechangeEvent();
    3754           0 :     DispatchProgressEvent(this, ProgressEventType::progress,
    3755           0 :                           mLoadTransferred, mLoadTotal);
    3756             :   }
    3757             : 
    3758           0 :   mProgressSinceLastProgressEvent = false;
    3759             : 
    3760           0 :   StartProgressEventTimer();
    3761             : }
    3762             : 
    3763             : void
    3764           6 : XMLHttpRequestMainThread::StopProgressEventTimer()
    3765             : {
    3766           6 :   if (mProgressNotifier) {
    3767           4 :     mProgressTimerIsActive = false;
    3768           4 :     mProgressNotifier->Cancel();
    3769             :   }
    3770           6 : }
    3771             : 
    3772             : void
    3773           2 : XMLHttpRequestMainThread::StartProgressEventTimer()
    3774             : {
    3775           2 :   if (!mProgressNotifier) {
    3776           2 :     mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
    3777           2 :     SetTimerEventTarget(mProgressNotifier);
    3778             :   }
    3779           2 :   if (mProgressNotifier) {
    3780           2 :     mProgressTimerIsActive = true;
    3781           2 :     mProgressNotifier->Cancel();
    3782           4 :     mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
    3783           4 :                                         nsITimer::TYPE_ONE_SHOT);
    3784             :   }
    3785           2 : }
    3786             : 
    3787             : XMLHttpRequestMainThread::SyncTimeoutType
    3788           0 : XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer()
    3789             : {
    3790           0 :   MOZ_ASSERT(mFlagSynchronous);
    3791             : 
    3792           0 :   nsIDocument* doc = GetDocumentIfCurrent();
    3793           0 :   if (!doc || !doc->GetPageUnloadingEventTimeStamp()) {
    3794           0 :     return eNoTimerNeeded;
    3795             :   }
    3796             : 
    3797             :   // If we are in a beforeunload or a unload event, we must force a timeout.
    3798           0 :   TimeDuration diff = (TimeStamp::NowLoRes() - doc->GetPageUnloadingEventTimeStamp());
    3799           0 :   if (diff.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING) {
    3800           0 :     return eErrorOrExpired;
    3801             :   }
    3802             : 
    3803           0 :   mSyncTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    3804           0 :   SetTimerEventTarget(mSyncTimeoutTimer);
    3805           0 :   if (!mSyncTimeoutTimer) {
    3806           0 :     return eErrorOrExpired;
    3807             :   }
    3808             : 
    3809           0 :   uint32_t timeout = MAX_SYNC_TIMEOUT_WHEN_UNLOADING - diff.ToMilliseconds();
    3810           0 :   nsresult rv = mSyncTimeoutTimer->InitWithCallback(this, timeout,
    3811           0 :                                                     nsITimer::TYPE_ONE_SHOT);
    3812           0 :   return NS_FAILED(rv) ? eErrorOrExpired : eTimerStarted;
    3813             : }
    3814             : 
    3815             : void
    3816           0 : XMLHttpRequestMainThread::HandleSyncTimeoutTimer()
    3817             : {
    3818           0 :   MOZ_ASSERT(mSyncTimeoutTimer);
    3819           0 :   MOZ_ASSERT(mFlagSyncLooping);
    3820             : 
    3821           0 :   CancelSyncTimeoutTimer();
    3822           0 :   Abort();
    3823           0 : }
    3824             : 
    3825             : void
    3826           0 : XMLHttpRequestMainThread::CancelSyncTimeoutTimer()
    3827             : {
    3828           0 :   if (mSyncTimeoutTimer) {
    3829           0 :     mSyncTimeoutTimer->Cancel();
    3830           0 :     mSyncTimeoutTimer = nullptr;
    3831             :   }
    3832           0 : }
    3833             : 
    3834             : already_AddRefed<nsXMLHttpRequestXPCOMifier>
    3835           2 : XMLHttpRequestMainThread::EnsureXPCOMifier()
    3836             : {
    3837           2 :   if (!mXPCOMifier) {
    3838           2 :     mXPCOMifier = new nsXMLHttpRequestXPCOMifier(this);
    3839             :   }
    3840           4 :   RefPtr<nsXMLHttpRequestXPCOMifier> newRef(mXPCOMifier);
    3841           4 :   return newRef.forget();
    3842             : }
    3843             : 
    3844             : bool
    3845           0 : XMLHttpRequestMainThread::ShouldBlockAuthPrompt()
    3846             : {
    3847             :   // Verify that it's ok to prompt for credentials here, per spec
    3848             :   // http://xhr.spec.whatwg.org/#the-send%28%29-method
    3849             : 
    3850           0 :   if (mAuthorRequestHeaders.Has("authorization")) {
    3851           0 :     return true;
    3852             :   }
    3853             : 
    3854           0 :   nsCOMPtr<nsIURI> uri;
    3855           0 :   nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
    3856           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3857           0 :     return false;
    3858             :   }
    3859             : 
    3860             :   // Also skip if a username and/or password is provided in the URI.
    3861           0 :   nsCString username;
    3862           0 :   rv = uri->GetUsername(username);
    3863           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3864           0 :     return false;
    3865             :   }
    3866             : 
    3867           0 :   nsCString password;
    3868           0 :   rv = uri->GetPassword(password);
    3869           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3870           0 :     return false;
    3871             :   }
    3872             : 
    3873           0 :   if (!username.IsEmpty() || !password.IsEmpty()) {
    3874           0 :     return true;
    3875             :   }
    3876             : 
    3877           0 :   return false;
    3878             : }
    3879             : 
    3880             : void
    3881           7 : XMLHttpRequestMainThread::TruncateResponseText()
    3882             : {
    3883           7 :   mResponseText.Truncate();
    3884           7 :   XMLHttpRequestBinding::ClearCachedResponseTextValue(this);
    3885           7 : }
    3886             : 
    3887           0 : NS_IMPL_ISUPPORTS(XMLHttpRequestMainThread::nsHeaderVisitor, nsIHttpHeaderVisitor)
    3888             : 
    3889           0 : NS_IMETHODIMP XMLHttpRequestMainThread::
    3890             : nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value)
    3891             : {
    3892           0 :   if (mXHR.IsSafeHeader(header, mHttpChannel)) {
    3893           0 :     if (!IsLowercaseResponseHeader()) {
    3894           0 :       if(!mHeaderList.InsertElementSorted(HeaderEntry(header, value),
    3895             :                                           fallible)) {
    3896           0 :         return NS_ERROR_OUT_OF_MEMORY;
    3897             :       }
    3898           0 :       return NS_OK;
    3899             :     }
    3900             : 
    3901           0 :     nsAutoCString lowerHeader(header);
    3902           0 :     ToLowerCase(lowerHeader);
    3903           0 :     if (!mHeaderList.InsertElementSorted(HeaderEntry(lowerHeader, value),
    3904             :                                          fallible)) {
    3905           0 :       return NS_ERROR_OUT_OF_MEMORY;
    3906             :     }
    3907             :   }
    3908           0 :   return NS_OK;
    3909             : }
    3910             : 
    3911             : void
    3912           0 : XMLHttpRequestMainThread::MaybeCreateBlobStorage()
    3913             : {
    3914           0 :   MOZ_ASSERT(mResponseType == XMLHttpRequestResponseType::Blob);
    3915             : 
    3916           0 :   if (mBlobStorage) {
    3917           0 :     return;
    3918             :   }
    3919             : 
    3920             :   MutableBlobStorage::MutableBlobStorageType storageType =
    3921           0 :     BasePrincipal::Cast(mPrincipal)->PrivateBrowsingId() == 0
    3922           0 :       ? MutableBlobStorage::eCouldBeInTemporaryFile
    3923           0 :       : MutableBlobStorage::eOnlyInMemory;
    3924             : 
    3925           0 :   nsCOMPtr<nsIEventTarget> eventTarget;
    3926           0 :   if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
    3927           0 :     eventTarget = global->EventTargetFor(TaskCategory::Other);
    3928             :   }
    3929             : 
    3930           0 :   mBlobStorage = new MutableBlobStorage(storageType, eventTarget);
    3931             : }
    3932             : 
    3933             : void
    3934           0 : XMLHttpRequestMainThread::BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
    3935             :                                              Blob* aBlob, nsresult aRv)
    3936             : {
    3937             :   // Ok, the state is changed...
    3938           0 :   if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) {
    3939           0 :     return;
    3940             :   }
    3941             : 
    3942           0 :   MOZ_ASSERT(mState != State::done);
    3943             : 
    3944           0 :   mResponseBlob = aBlob;
    3945           0 :   mBlobStorage = nullptr;
    3946             : 
    3947           0 :   ChangeStateToDone();
    3948             : }
    3949             : 
    3950             : NS_IMETHODIMP
    3951           0 : XMLHttpRequestMainThread::GetName(nsACString& aName)
    3952             : {
    3953           0 :   aName.AssignLiteral("XMLHttpRequest");
    3954           0 :   return NS_OK;
    3955             : }
    3956             : 
    3957             : NS_IMETHODIMP
    3958           0 : XMLHttpRequestMainThread::SetName(const char* aName)
    3959             : {
    3960           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    3961             : }
    3962             : 
    3963             : // nsXMLHttpRequestXPCOMifier implementation
    3964          12 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
    3965           2 :   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
    3966           2 :   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
    3967           2 :   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
    3968           2 :   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
    3969           2 :   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
    3970           0 :   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
    3971           0 :   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
    3972           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
    3973           0 : NS_INTERFACE_MAP_END
    3974             : 
    3975           4 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
    3976           8 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
    3977             : 
    3978             : // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
    3979             : // inheritance from nsISupports.
    3980             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
    3981             : 
    3982           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier)
    3983           0 : if (tmp->mXHR) {
    3984           0 :   tmp->mXHR->mXPCOMifier = nullptr;
    3985             : }
    3986           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR)
    3987           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    3988             : 
    3989           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
    3990           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
    3991           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    3992             : 
    3993             : NS_IMETHODIMP
    3994           0 : nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID & aIID, void **aResult)
    3995             : {
    3996             :   // Return ourselves for the things we implement (except
    3997             :   // nsIInterfaceRequestor) and the XHR for the rest.
    3998           0 :   if (!aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
    3999           0 :     nsresult rv = QueryInterface(aIID, aResult);
    4000           0 :     if (NS_SUCCEEDED(rv)) {
    4001           0 :       return rv;
    4002             :     }
    4003             :   }
    4004             : 
    4005           0 :   return mXHR->GetInterface(aIID, aResult);
    4006             : }
    4007             : 
    4008           3 : ArrayBufferBuilder::ArrayBufferBuilder()
    4009             :   : mDataPtr(nullptr),
    4010             :     mCapacity(0),
    4011             :     mLength(0),
    4012           3 :     mMapPtr(nullptr)
    4013             : {
    4014           3 : }
    4015             : 
    4016           0 : ArrayBufferBuilder::~ArrayBufferBuilder()
    4017             : {
    4018           0 :   reset();
    4019           0 : }
    4020             : 
    4021             : void
    4022           7 : ArrayBufferBuilder::reset()
    4023             : {
    4024           7 :   if (mDataPtr) {
    4025           0 :     JS_free(nullptr, mDataPtr);
    4026             :   }
    4027             : 
    4028           7 :   if (mMapPtr) {
    4029           0 :     JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
    4030           0 :     mMapPtr = nullptr;
    4031             :   }
    4032             : 
    4033           7 :   mDataPtr = nullptr;
    4034           7 :   mCapacity = mLength = 0;
    4035           7 : }
    4036             : 
    4037             : bool
    4038           0 : ArrayBufferBuilder::setCapacity(uint32_t aNewCap)
    4039             : {
    4040           0 :   MOZ_ASSERT(!mMapPtr);
    4041             : 
    4042             :   // To ensure that realloc won't free mDataPtr, use a size of 1
    4043             :   // instead of 0.
    4044           0 :   uint8_t* newdata = (uint8_t *) js_realloc(mDataPtr, aNewCap ? aNewCap : 1);
    4045             : 
    4046           0 :   if (!newdata) {
    4047           0 :     return false;
    4048             :   }
    4049             : 
    4050           0 :   if (aNewCap > mCapacity) {
    4051           0 :     memset(newdata + mCapacity, 0, aNewCap - mCapacity);
    4052             :   }
    4053             : 
    4054           0 :   mDataPtr = newdata;
    4055           0 :   mCapacity = aNewCap;
    4056           0 :   if (mLength > aNewCap) {
    4057           0 :     mLength = aNewCap;
    4058             :   }
    4059             : 
    4060           0 :   return true;
    4061             : }
    4062             : 
    4063             : bool
    4064           0 : ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
    4065             :                            uint32_t aMaxGrowth)
    4066             : {
    4067           0 :   MOZ_ASSERT(!mMapPtr);
    4068             : 
    4069           0 :   CheckedUint32 neededCapacity = mLength;
    4070           0 :   neededCapacity += aDataLen;
    4071           0 :   if (!neededCapacity.isValid()) {
    4072           0 :     return false;
    4073             :   }
    4074           0 :   if (mLength + aDataLen > mCapacity) {
    4075           0 :     CheckedUint32 newcap = mCapacity;
    4076             :     // Double while under aMaxGrowth or if not specified.
    4077           0 :     if (!aMaxGrowth || mCapacity < aMaxGrowth) {
    4078           0 :       newcap *= 2;
    4079             :     } else {
    4080           0 :       newcap += aMaxGrowth;
    4081             :     }
    4082             : 
    4083           0 :     if (!newcap.isValid()) {
    4084           0 :       return false;
    4085             :     }
    4086             : 
    4087             :     // But make sure there's always enough to satisfy our request.
    4088           0 :     if (newcap.value() < neededCapacity.value()) {
    4089           0 :       newcap = neededCapacity;
    4090             :     }
    4091             : 
    4092           0 :     if (!setCapacity(newcap.value())) {
    4093           0 :       return false;
    4094             :     }
    4095             :   }
    4096             : 
    4097             :   // Assert that the region isn't overlapping so we can memcpy.
    4098           0 :   MOZ_ASSERT(!areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength,
    4099             :                                     aDataLen));
    4100             : 
    4101           0 :   memcpy(mDataPtr + mLength, aNewData, aDataLen);
    4102           0 :   mLength += aDataLen;
    4103             : 
    4104           0 :   return true;
    4105             : }
    4106             : 
    4107             : JSObject*
    4108           0 : ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
    4109             : {
    4110           0 :   if (mMapPtr) {
    4111           0 :     JSObject* obj = JS_NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
    4112           0 :     if (!obj) {
    4113           0 :       JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
    4114             :     }
    4115           0 :     mMapPtr = nullptr;
    4116             : 
    4117             :     // The memory-mapped contents will be released when the ArrayBuffer becomes
    4118             :     // detached or is GC'd.
    4119           0 :     return obj;
    4120             :   }
    4121             : 
    4122             :   // we need to check for mLength == 0, because nothing may have been
    4123             :   // added
    4124           0 :   if (mCapacity > mLength || mLength == 0) {
    4125           0 :     if (!setCapacity(mLength)) {
    4126           0 :       return nullptr;
    4127             :     }
    4128             :   }
    4129             : 
    4130           0 :   JSObject* obj = JS_NewArrayBufferWithContents(aCx, mLength, mDataPtr);
    4131           0 :   mLength = mCapacity = 0;
    4132           0 :   if (!obj) {
    4133           0 :     js_free(mDataPtr);
    4134             :   }
    4135           0 :   mDataPtr = nullptr;
    4136           0 :   return obj;
    4137             : }
    4138             : 
    4139             : nsresult
    4140           0 : ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
    4141             :                                        nsIFile* aJarFile)
    4142             : {
    4143             :   nsresult rv;
    4144             : 
    4145             :   // Open Jar file to get related attributes of target file.
    4146           0 :   RefPtr<nsZipArchive> zip = new nsZipArchive();
    4147           0 :   rv = zip->OpenArchive(aJarFile);
    4148           0 :   if (NS_FAILED(rv)) {
    4149           0 :     return rv;
    4150             :   }
    4151           0 :   nsZipItem* zipItem = zip->GetItem(aFile.get());
    4152           0 :   if (!zipItem) {
    4153           0 :     return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
    4154             :   }
    4155             : 
    4156             :   // If file was added to the package as stored(uncompressed), map to the
    4157             :   // offset of file in zip package.
    4158           0 :   if (!zipItem->Compression()) {
    4159           0 :     uint32_t offset = zip->GetDataOffset(zipItem);
    4160           0 :     uint32_t size = zipItem->RealSize();
    4161           0 :     mozilla::AutoFDClose pr_fd;
    4162           0 :     rv = aJarFile->OpenNSPRFileDesc(PR_RDONLY, 0, &pr_fd.rwget());
    4163           0 :     if (NS_FAILED(rv)) {
    4164           0 :       return rv;
    4165             :     }
    4166           0 :     mMapPtr = JS_CreateMappedArrayBufferContents(PR_FileDesc2NativeHandle(pr_fd),
    4167             :                                                  offset, size);
    4168           0 :     if (mMapPtr) {
    4169           0 :       mLength = size;
    4170           0 :       return NS_OK;
    4171             :     }
    4172             :   }
    4173           0 :   return NS_ERROR_FAILURE;
    4174             : }
    4175             : 
    4176             : /* static */ bool
    4177           0 : ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
    4178             :                                           uint32_t aLength1,
    4179             :                                           const uint8_t* aStart2,
    4180             :                                           uint32_t aLength2)
    4181             : {
    4182           0 :   const uint8_t* end1 = aStart1 + aLength1;
    4183           0 :   const uint8_t* end2 = aStart2 + aLength2;
    4184             : 
    4185           0 :   const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
    4186           0 :   const uint8_t* min_end   = end1 < end2 ? end1 : end2;
    4187             : 
    4188           0 :   return max_start < min_end;
    4189             : }
    4190             : 
    4191             : RequestHeaders::RequestHeader*
    4192           0 : RequestHeaders::Find(const nsACString& aName)
    4193             : {
    4194           0 :   const nsCaseInsensitiveCStringComparator ignoreCase;
    4195           0 :   for (RequestHeaders::RequestHeader& header : mHeaders) {
    4196           0 :     if (header.mName.Equals(aName, ignoreCase)) {
    4197           0 :       return &header;
    4198             :     }
    4199             :   }
    4200           0 :   return nullptr;
    4201             : }
    4202             : 
    4203             : bool
    4204           0 : RequestHeaders::Has(const char* aName)
    4205             : {
    4206           0 :   return Has(nsDependentCString(aName));
    4207             : }
    4208             : 
    4209             : bool
    4210           0 : RequestHeaders::Has(const nsACString& aName)
    4211             : {
    4212           0 :   return !!Find(aName);
    4213             : }
    4214             : 
    4215             : void
    4216           0 : RequestHeaders::Get(const char* aName, nsACString& aValue)
    4217             : {
    4218           0 :   Get(nsDependentCString(aName), aValue);
    4219           0 : }
    4220             : 
    4221             : void
    4222           0 : RequestHeaders::Get(const nsACString& aName, nsACString& aValue)
    4223             : {
    4224           0 :   RequestHeader* header = Find(aName);
    4225           0 :   if (header) {
    4226           0 :     aValue = header->mValue;
    4227             :   } else {
    4228           0 :     aValue.SetIsVoid(true);
    4229             :   }
    4230           0 : }
    4231             : 
    4232             : void
    4233           0 : RequestHeaders::Set(const char* aName, const nsACString& aValue)
    4234             : {
    4235           0 :   Set(nsDependentCString(aName), aValue);
    4236           0 : }
    4237             : 
    4238             : void
    4239           0 : RequestHeaders::Set(const nsACString& aName, const nsACString& aValue)
    4240             : {
    4241           0 :   RequestHeader* header = Find(aName);
    4242           0 :   if (header) {
    4243           0 :     header->mValue.Assign(aValue);
    4244             :   } else {
    4245             :     RequestHeader newHeader = {
    4246             :       nsCString(aName), nsCString(aValue)
    4247           0 :     };
    4248           0 :     mHeaders.AppendElement(newHeader);
    4249             :   }
    4250           0 : }
    4251             : 
    4252             : void
    4253           0 : RequestHeaders::MergeOrSet(const char* aName, const nsACString& aValue)
    4254             : {
    4255           0 :   MergeOrSet(nsDependentCString(aName), aValue);
    4256           0 : }
    4257             : 
    4258             : void
    4259           0 : RequestHeaders::MergeOrSet(const nsACString& aName, const nsACString& aValue)
    4260             : {
    4261           0 :   RequestHeader* header = Find(aName);
    4262           0 :   if (header) {
    4263           0 :     header->mValue.AppendLiteral(", ");
    4264           0 :     header->mValue.Append(aValue);
    4265             :   } else {
    4266             :     RequestHeader newHeader = {
    4267             :       nsCString(aName), nsCString(aValue)
    4268           0 :     };
    4269           0 :     mHeaders.AppendElement(newHeader);
    4270             :   }
    4271           0 : }
    4272             : 
    4273             : void
    4274           3 : RequestHeaders::Clear()
    4275             : {
    4276           3 :   mHeaders.Clear();
    4277           3 : }
    4278             : 
    4279             : void
    4280           0 : RequestHeaders::ApplyToChannel(nsIHttpChannel* aHttpChannel) const
    4281             : {
    4282           0 :   for (const RequestHeader& header : mHeaders) {
    4283           0 :     if (header.mValue.IsEmpty()) {
    4284             :       DebugOnly<nsresult> rv =
    4285           0 :         aHttpChannel->SetEmptyRequestHeader(header.mName);
    4286           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    4287             :     } else {
    4288             :       DebugOnly<nsresult> rv =
    4289           0 :         aHttpChannel->SetRequestHeader(header.mName, header.mValue, false);
    4290           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    4291             :     }
    4292             :   }
    4293           0 : }
    4294             : 
    4295             : void
    4296           0 : RequestHeaders::GetCORSUnsafeHeaders(nsTArray<nsCString>& aArray) const
    4297             : {
    4298             :   static const char *kCrossOriginSafeHeaders[] = {
    4299             :     "accept", "accept-language", "content-language", "content-type",
    4300             :     "last-event-id"
    4301             :   };
    4302             :   const uint32_t kCrossOriginSafeHeadersLength =
    4303           0 :     ArrayLength(kCrossOriginSafeHeaders);
    4304           0 :   for (const RequestHeader& header : mHeaders) {
    4305           0 :     bool safe = false;
    4306           0 :     for (uint32_t i = 0; i < kCrossOriginSafeHeadersLength; ++i) {
    4307           0 :       if (header.mName.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
    4308           0 :         safe = true;
    4309           0 :         break;
    4310             :       }
    4311             :     }
    4312           0 :     if (!safe) {
    4313           0 :       aArray.AppendElement(header.mName);
    4314             :     }
    4315             :   }
    4316           0 : }
    4317             : 
    4318           0 : RequestHeaders::CharsetIterator::CharsetIterator(nsACString& aSource) :
    4319             :   mValid(false),
    4320             :   mCurPos(-1),
    4321             :   mCurLen(-1),
    4322           0 :   mCutoff(aSource.Length()),
    4323           0 :   mSource(aSource)
    4324             : {
    4325           0 : }
    4326             : 
    4327             : bool
    4328           0 : RequestHeaders::CharsetIterator::Equals(const nsACString& aOther,
    4329             :                                         const nsCStringComparator& aCmp) const
    4330             : {
    4331           0 :   if (mValid) {
    4332           0 :     return Substring(mSource, mCurPos, mCurLen).Equals(aOther, aCmp);
    4333             :   } else {
    4334           0 :     return false;
    4335             :   }
    4336             : }
    4337             : 
    4338             : void
    4339           0 : RequestHeaders::CharsetIterator::Replace(const nsACString& aReplacement)
    4340             : {
    4341           0 :   if (mValid) {
    4342           0 :     mSource.Replace(mCurPos, mCurLen, aReplacement);
    4343           0 :     mCurLen = aReplacement.Length();
    4344             :   }
    4345           0 : }
    4346             : 
    4347             : bool
    4348           0 : RequestHeaders::CharsetIterator::Next()
    4349             : {
    4350             :   int32_t start, end;
    4351           0 :   nsAutoCString charset;
    4352             : 
    4353             :   // Look for another charset declaration in the string, limiting the
    4354             :   // search to only the characters before the parts we've already searched
    4355             :   // (before mCutoff), so that we don't find the same charset twice.
    4356           0 :   NS_ExtractCharsetFromContentType(Substring(mSource, 0, mCutoff),
    4357           0 :                                    charset, &mValid, &start, &end);
    4358             : 
    4359           0 :   if (!mValid) {
    4360           0 :     return false;
    4361             :   }
    4362             : 
    4363             :   // Everything after the = sign is the part of the charset we want.
    4364           0 :   mCurPos = mSource.FindChar('=', start) + 1;
    4365           0 :   mCurLen = end - mCurPos;
    4366             : 
    4367             :   // Special case: the extracted charset is quoted with single quotes.
    4368             :   // For the purpose of preserving what was set we want to handle them
    4369             :   // as delimiters (although they aren't really).
    4370           0 :   if (charset.Length() >= 2 &&
    4371           0 :       charset.First() == '\'' &&
    4372           0 :       charset.Last() == '\'') {
    4373           0 :     ++mCurPos;
    4374           0 :     mCurLen -= 2;
    4375             :   }
    4376             : 
    4377           0 :   mCutoff = start;
    4378             : 
    4379           0 :   return true;
    4380             : }
    4381             : 
    4382             : } // dom namespace
    4383             : } // mozilla namespace

Generated by: LCOV version 1.13