content/base/src/nsXMLHttpRequest.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 #include "nsXMLHttpRequest.h"
     9 #include "mozilla/ArrayUtils.h"
    10 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
    11 #include "mozilla/EventDispatcher.h"
    12 #include "mozilla/EventListenerManager.h"
    13 #include "mozilla/MemoryReporting.h"
    14 #include "nsDOMBlobBuilder.h"
    15 #include "nsIDOMDocument.h"
    16 #include "nsIDOMProgressEvent.h"
    17 #include "nsIJARChannel.h"
    18 #include "nsLayoutCID.h"
    19 #include "nsReadableUtils.h"
    21 #include "nsIURI.h"
    22 #include "nsILoadGroup.h"
    23 #include "nsNetUtil.h"
    24 #include "nsStreamUtils.h"
    25 #include "nsThreadUtils.h"
    26 #include "nsIUploadChannel.h"
    27 #include "nsIUploadChannel2.h"
    28 #include "nsIDOMSerializer.h"
    29 #include "nsXPCOM.h"
    30 #include "nsIDOMEventListener.h"
    31 #include "nsIScriptSecurityManager.h"
    32 #include "nsIDOMWindow.h"
    33 #include "nsIVariant.h"
    34 #include "nsVariant.h"
    35 #include "nsIScriptError.h"
    36 #include "nsIStreamConverterService.h"
    37 #include "nsICachingChannel.h"
    38 #include "nsContentUtils.h"
    39 #include "nsCxPusher.h"
    40 #include "nsCycleCollectionParticipant.h"
    41 #include "nsIContentPolicy.h"
    42 #include "nsContentPolicyUtils.h"
    43 #include "nsError.h"
    44 #include "nsCrossSiteListenerProxy.h"
    45 #include "nsIHTMLDocument.h"
    46 #include "nsIStorageStream.h"
    47 #include "nsIPromptFactory.h"
    48 #include "nsIWindowWatcher.h"
    49 #include "nsIConsoleService.h"
    50 #include "nsIChannelPolicy.h"
    51 #include "nsChannelPolicy.h"
    52 #include "nsIContentSecurityPolicy.h"
    53 #include "nsAsyncRedirectVerifyHelper.h"
    54 #include "nsStringBuffer.h"
    55 #include "nsDOMFile.h"
    56 #include "nsIFileChannel.h"
    57 #include "mozilla/Telemetry.h"
    58 #include "jsfriendapi.h"
    59 #include "GeckoProfiler.h"
    60 #include "mozilla/dom/EncodingUtils.h"
    61 #include "nsIUnicodeDecoder.h"
    62 #include "mozilla/dom/XMLHttpRequestBinding.h"
    63 #include "mozilla/Attributes.h"
    64 #include "nsIPermissionManager.h"
    65 #include "nsMimeTypes.h"
    66 #include "nsIHttpChannelInternal.h"
    67 #include "nsCharSeparatedTokenizer.h"
    68 #include "nsFormData.h"
    69 #include "nsStreamListenerWrapper.h"
    70 #include "xpcjsid.h"
    71 #include "nsITimedChannel.h"
    73 #include "nsWrapperCacheInlines.h"
    75 using namespace mozilla;
    76 using namespace mozilla::dom;
    78 // Maximum size that we'll grow an ArrayBuffer instead of doubling,
    79 // once doubling reaches this threshold
    80 #define XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH (32*1024*1024)
    81 // start at 32k to avoid lots of doubling right at the start
    82 #define XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE (32*1024)
    83 // the maximum Content-Length that we'll preallocate.  1GB.  Must fit
    84 // in an int32_t!
    85 #define XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE (1*1024*1024*1024LL)
    87 #define LOAD_STR "load"
    88 #define ERROR_STR "error"
    89 #define ABORT_STR "abort"
    90 #define TIMEOUT_STR "timeout"
    91 #define LOADSTART_STR "loadstart"
    92 #define PROGRESS_STR "progress"
    93 #define READYSTATE_STR "readystatechange"
    94 #define LOADEND_STR "loadend"
    96 // CIDs
    98 // State
    99 #define XML_HTTP_REQUEST_UNSENT           (1 << 0) // 0 UNSENT
   100 #define XML_HTTP_REQUEST_OPENED           (1 << 1) // 1 OPENED
   101 #define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
   102 #define XML_HTTP_REQUEST_LOADING          (1 << 3) // 3 LOADING
   103 #define XML_HTTP_REQUEST_DONE             (1 << 4) // 4 DONE
   104 #define XML_HTTP_REQUEST_SENT             (1 << 5) // Internal, OPENED in IE and external view
   105 // The above states are mutually exclusive, change with ChangeState() only.
   106 // The states below can be combined.
   107 #define XML_HTTP_REQUEST_ABORTED        (1 << 7)  // Internal
   108 #define XML_HTTP_REQUEST_ASYNC          (1 << 8)  // Internal
   109 #define XML_HTTP_REQUEST_PARSEBODY      (1 << 9)  // Internal
   110 #define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 10) // Internal
   111 #define XML_HTTP_REQUEST_BACKGROUND     (1 << 13) // Internal
   112 #define XML_HTTP_REQUEST_USE_XSITE_AC   (1 << 14) // Internal
   113 #define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 15) // Internal
   114 #define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 16) // Internal
   115 #define XML_HTTP_REQUEST_TIMED_OUT (1 << 17) // Internal
   116 #define XML_HTTP_REQUEST_DELETED (1 << 18) // Internal
   118 #define XML_HTTP_REQUEST_LOADSTATES         \
   119   (XML_HTTP_REQUEST_UNSENT |                \
   120    XML_HTTP_REQUEST_OPENED |                \
   121    XML_HTTP_REQUEST_HEADERS_RECEIVED |      \
   122    XML_HTTP_REQUEST_LOADING |               \
   123    XML_HTTP_REQUEST_DONE |                  \
   124    XML_HTTP_REQUEST_SENT)
   126 #define NS_BADCERTHANDLER_CONTRACTID \
   127   "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
   129 #define NS_PROGRESS_EVENT_INTERVAL 50
   131 #define IMPL_CSTRING_GETTER(_name)                                              \
   132   NS_IMETHODIMP                                                                 \
   133   nsXMLHttpRequest::_name(nsACString& aOut)                                     \
   134   {                                                                             \
   135     nsCString tmp;                                                              \
   136     _name(tmp);                                                                 \
   137     aOut = tmp;                                                                 \
   138     return NS_OK;                                                               \
   139   }
   141 NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
   143 class nsResumeTimeoutsEvent : public nsRunnable
   144 {
   145 public:
   146   nsResumeTimeoutsEvent(nsPIDOMWindow* aWindow) : mWindow(aWindow) {}
   148   NS_IMETHOD Run()
   149   {
   150     mWindow->ResumeTimeouts(false);
   151     return NS_OK;
   152   }
   154 private:
   155   nsCOMPtr<nsPIDOMWindow> mWindow;
   156 };
   159 // This helper function adds the given load flags to the request's existing
   160 // load flags.
   161 static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
   162 {
   163   nsLoadFlags flags;
   164   request->GetLoadFlags(&flags);
   165   flags |= newFlags;
   166   request->SetLoadFlags(flags);
   167 }
   169 //-----------------------------------------------------------------------------
   170 // XMLHttpRequestAuthPrompt
   171 //-----------------------------------------------------------------------------
   173 class XMLHttpRequestAuthPrompt : public nsIAuthPrompt
   174 {
   175 public:
   176   NS_DECL_ISUPPORTS
   177   NS_DECL_NSIAUTHPROMPT
   179   XMLHttpRequestAuthPrompt();
   180   virtual ~XMLHttpRequestAuthPrompt();
   181 };
   183 NS_IMPL_ISUPPORTS(XMLHttpRequestAuthPrompt, nsIAuthPrompt)
   185 XMLHttpRequestAuthPrompt::XMLHttpRequestAuthPrompt()
   186 {
   187   MOZ_COUNT_CTOR(XMLHttpRequestAuthPrompt);
   188 }
   190 XMLHttpRequestAuthPrompt::~XMLHttpRequestAuthPrompt()
   191 {
   192   MOZ_COUNT_DTOR(XMLHttpRequestAuthPrompt);
   193 }
   195 NS_IMETHODIMP
   196 XMLHttpRequestAuthPrompt::Prompt(const char16_t* aDialogTitle,
   197                                  const char16_t* aText,
   198                                  const char16_t* aPasswordRealm,
   199                                  uint32_t aSavePassword,
   200                                  const char16_t* aDefaultText,
   201                                  char16_t** aResult,
   202                                  bool* aRetval)
   203 {
   204   *aRetval = false;
   205   return NS_OK;
   206 }
   208 NS_IMETHODIMP
   209 XMLHttpRequestAuthPrompt::PromptUsernameAndPassword(const char16_t* aDialogTitle,
   210                                                     const char16_t* aDialogText,
   211                                                     const char16_t* aPasswordRealm,
   212                                                     uint32_t aSavePassword,
   213                                                     char16_t** aUser,
   214                                                     char16_t** aPwd,
   215                                                     bool* aRetval)
   216 {
   217   *aRetval = false;
   218   return NS_OK;
   219 }
   221 NS_IMETHODIMP
   222 XMLHttpRequestAuthPrompt::PromptPassword(const char16_t* aDialogTitle,
   223                                          const char16_t* aText,
   224                                          const char16_t* aPasswordRealm,
   225                                          uint32_t aSavePassword,
   226                                          char16_t** aPwd,
   227                                          bool* aRetval)
   228 {
   229   *aRetval = false;
   230   return NS_OK;
   231 }
   233 /////////////////////////////////////////////
   235 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
   237 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXHREventTarget,
   238                                                   DOMEventTargetHelper)
   239 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   241 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXHREventTarget,
   242                                                 DOMEventTargetHelper)
   243 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   245 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXHREventTarget)
   246   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
   247 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
   249 NS_IMPL_ADDREF_INHERITED(nsXHREventTarget, DOMEventTargetHelper)
   250 NS_IMPL_RELEASE_INHERITED(nsXHREventTarget, DOMEventTargetHelper)
   252 void
   253 nsXHREventTarget::DisconnectFromOwner()
   254 {
   255   DOMEventTargetHelper::DisconnectFromOwner();
   256 }
   258 /////////////////////////////////////////////
   260 NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequestUpload)
   261   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestUpload)
   262 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
   264 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
   265 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
   267 /* virtual */ JSObject*
   268 nsXMLHttpRequestUpload::WrapObject(JSContext* aCx)
   269 {
   270   return XMLHttpRequestUploadBinding::Wrap(aCx, this);
   271 }
   273 /////////////////////////////////////////////
   274 //
   275 //
   276 /////////////////////////////////////////////
   278 bool
   279 nsXMLHttpRequest::sDontWarnAboutSyncXHR = false;
   281 nsXMLHttpRequest::nsXMLHttpRequest()
   282   : mResponseBodyDecodedPos(0),
   283     mResponseType(XML_HTTP_RESPONSE_TYPE_DEFAULT),
   284     mRequestObserver(nullptr), mState(XML_HTTP_REQUEST_UNSENT),
   285     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
   286     mProgressSinceLastProgressEvent(false),
   287     mRequestSentTime(0), mTimeoutMilliseconds(0),
   288     mErrorLoad(false), mWaitingForOnStopRequest(false),
   289     mProgressTimerIsActive(false),
   290     mIsHtml(false),
   291     mWarnAboutSyncHtml(false),
   292     mLoadLengthComputable(false), mLoadTotal(0),
   293     mIsSystem(false),
   294     mIsAnon(false),
   295     mFirstStartRequestSeen(false),
   296     mInLoadProgressEvent(false),
   297     mResultJSON(JSVAL_VOID),
   298     mResultArrayBuffer(nullptr),
   299     mXPCOMifier(nullptr)
   300 {
   301   SetIsDOMBinding();
   302 #ifdef DEBUG
   303   StaticAssertions();
   304 #endif
   305 }
   307 nsXMLHttpRequest::~nsXMLHttpRequest()
   308 {
   309   mState |= XML_HTTP_REQUEST_DELETED;
   311   if (mState & (XML_HTTP_REQUEST_SENT |
   312                 XML_HTTP_REQUEST_LOADING)) {
   313     Abort();
   314   }
   316   NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
   317   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
   319   mResultJSON = JSVAL_VOID;
   320   mResultArrayBuffer = nullptr;
   321   mozilla::DropJSObjects(this);
   322 }
   324 void
   325 nsXMLHttpRequest::RootJSResultObjects()
   326 {
   327   mozilla::HoldJSObjects(this);
   328 }
   330 /**
   331  * This Init method is called from the factory constructor.
   332  */
   333 nsresult
   334 nsXMLHttpRequest::Init()
   335 {
   336   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   337   nsCOMPtr<nsIPrincipal> subjectPrincipal;
   338   if (secMan) {
   339     secMan->GetSystemPrincipal(getter_AddRefs(subjectPrincipal));
   340   }
   341   NS_ENSURE_STATE(subjectPrincipal);
   343   // Instead of grabbing some random global from the context stack,
   344   // let's use the default one (junk scope) for now.
   345   // We should move away from this Init...
   346   nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
   347   NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
   348   Construct(subjectPrincipal, global);
   349   return NS_OK;
   350 }
   352 /**
   353  * This Init method should only be called by C++ consumers.
   354  */
   355 NS_IMETHODIMP
   356 nsXMLHttpRequest::Init(nsIPrincipal* aPrincipal,
   357                        nsIScriptContext* aScriptContext,
   358                        nsIGlobalObject* aGlobalObject,
   359                        nsIURI* aBaseURI)
   360 {
   361   NS_ENSURE_ARG_POINTER(aPrincipal);
   363   if (nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobalObject)) {
   364     if (win->IsOuterWindow()) {
   365       // Must be bound to inner window, innerize if necessary.
   366       nsCOMPtr<nsIGlobalObject> inner = do_QueryInterface(
   367         win->GetCurrentInnerWindow());
   368       aGlobalObject = inner.get();
   369     }
   370   }
   372   Construct(aPrincipal, aGlobalObject, aBaseURI);
   373   return NS_OK;
   374 }
   376 void
   377 nsXMLHttpRequest::InitParameters(bool aAnon, bool aSystem)
   378 {
   379   if (!aAnon && !aSystem) {
   380     return;
   381   }
   383   // Check for permissions.
   384   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetOwner());
   385   if (!window || !window->GetDocShell()) {
   386     return;
   387   }
   389   // Chrome is always allowed access, so do the permission check only
   390   // for non-chrome pages.
   391   if (!IsSystemXHR() && aSystem) {
   392     nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   393     if (!doc) {
   394       return;
   395     }
   397     nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
   398     nsCOMPtr<nsIPermissionManager> permMgr =
   399       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   400     if (!permMgr)
   401       return;
   403     uint32_t permission;
   404     nsresult rv =
   405       permMgr->TestPermissionFromPrincipal(principal, "systemXHR", &permission);
   406     if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
   407       return;
   408     }
   409   }
   411   SetParameters(aAnon, aSystem);
   412 }
   414 void
   415 nsXMLHttpRequest::ResetResponse()
   416 {
   417   mResponseXML = nullptr;
   418   mResponseBody.Truncate();
   419   mResponseText.Truncate();
   420   mResponseBlob = nullptr;
   421   mDOMFile = nullptr;
   422   mBlobSet = nullptr;
   423   mResultArrayBuffer = nullptr;
   424   mArrayBufferBuilder.reset();
   425   mResultJSON = JSVAL_VOID;
   426   mLoadTransferred = 0;
   427   mResponseBodyDecodedPos = 0;
   428 }
   430 void
   431 nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
   432 {
   433   mRequestObserver = aObserver;
   434 }
   436 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
   438 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
   439   bool isBlack = tmp->IsBlack();
   440   if (isBlack || tmp->mWaitingForOnStopRequest) {
   441     if (tmp->mListenerManager) {
   442       tmp->mListenerManager->MarkForCC();
   443     }
   444     if (!isBlack && tmp->PreservingWrapper()) {
   445       // This marks the wrapper black.
   446       tmp->GetWrapper();
   447     }
   448     return true;
   449   }
   450 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   452 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXMLHttpRequest)
   453   return tmp->
   454     IsBlackAndDoesNotNeedTracing(static_cast<DOMEventTargetHelper*>(tmp));
   455 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   457 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXMLHttpRequest)
   458   return tmp->IsBlack();
   459 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   461 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
   462                                                   nsXHREventTarget)
   463   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
   464   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
   465   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
   466   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCORSPreflightChannel)
   468   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
   470   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
   471   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
   473   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
   474 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   476 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
   477                                                 nsXHREventTarget)
   478   tmp->mResultArrayBuffer = nullptr;
   479   tmp->mArrayBufferBuilder.reset();
   480   tmp->mResultJSON = JSVAL_VOID;
   481   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   482   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
   483   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
   484   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCORSPreflightChannel)
   486   NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
   488   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
   489   NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
   491   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
   492 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   494 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
   495                                                nsXHREventTarget)
   496   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
   497   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultJSON)
   498 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   500 // QueryInterface implementation for nsXMLHttpRequest
   501 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLHttpRequest)
   502   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
   503   NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
   504   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   505   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   506   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
   507   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   508   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   509   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   510   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   511   NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget)
   512 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
   514 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
   515 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
   517 NS_IMPL_EVENT_HANDLER(nsXMLHttpRequest, readystatechange)
   519 void
   520 nsXMLHttpRequest::DisconnectFromOwner()
   521 {
   522   nsXHREventTarget::DisconnectFromOwner();
   523   Abort();
   524 }
   526 size_t
   527 nsXMLHttpRequest::SizeOfEventTargetIncludingThis(
   528   MallocSizeOf aMallocSizeOf) const
   529 {
   530   size_t n = aMallocSizeOf(this);
   531   n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   533   // Why is this safe?  Because no-one else will report this string.  The
   534   // other possible sharers of this string are as follows.
   535   //
   536   // - The JS engine could hold copies if the JS code holds references, e.g.
   537   //   |var text = XHR.responseText|.  However, those references will be via JS
   538   //   external strings, for which the JS memory reporter does *not* report the
   539   //   chars.
   540   //
   541   // - Binary extensions, but they're *extremely* unlikely to do any memory
   542   //   reporting.
   543   //
   544   n += mResponseText.SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
   546   return n;
   548   // Measurement of the following members may be added later if DMD finds it is
   549   // worthwhile:
   550   // - lots
   551 }
   553 /* readonly attribute nsIChannel channel; */
   554 NS_IMETHODIMP
   555 nsXMLHttpRequest::GetChannel(nsIChannel **aChannel)
   556 {
   557   NS_ENSURE_ARG_POINTER(aChannel);
   558   NS_IF_ADDREF(*aChannel = mChannel);
   560   return NS_OK;
   561 }
   563 static void LogMessage(const char* aWarning, nsPIDOMWindow* aWindow)
   564 {
   565   nsCOMPtr<nsIDocument> doc;
   566   if (aWindow) {
   567     doc = aWindow->GetExtantDoc();
   568   }
   569   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
   570                                   NS_LITERAL_CSTRING("DOM"), doc,
   571                                   nsContentUtils::eDOM_PROPERTIES,
   572                                   aWarning);
   573 }
   575 /* readonly attribute nsIDOMDocument responseXML; */
   576 NS_IMETHODIMP
   577 nsXMLHttpRequest::GetResponseXML(nsIDOMDocument **aResponseXML)
   578 {
   579   ErrorResult rv;
   580   nsIDocument* responseXML = GetResponseXML(rv);
   581   if (rv.Failed()) {
   582     return rv.ErrorCode();
   583   }
   585   if (!responseXML) {
   586     *aResponseXML = nullptr;
   587     return NS_OK;
   588   }
   590   return CallQueryInterface(responseXML, aResponseXML);
   591 }
   593 nsIDocument*
   594 nsXMLHttpRequest::GetResponseXML(ErrorResult& aRv)
   595 {
   596   if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
   597       mResponseType != XML_HTTP_RESPONSE_TYPE_DOCUMENT) {
   598     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   599     return nullptr;
   600   }
   601   if (mWarnAboutSyncHtml) {
   602     mWarnAboutSyncHtml = false;
   603     LogMessage("HTMLSyncXHRWarning", GetOwner());
   604   }
   605   return (XML_HTTP_REQUEST_DONE & mState) ? mResponseXML : nullptr;
   606 }
   608 /*
   609  * This piece copied from XMLDocument, we try to get the charset
   610  * from HTTP headers.
   611  */
   612 nsresult
   613 nsXMLHttpRequest::DetectCharset()
   614 {
   615   mResponseCharset.Truncate();
   616   mDecoder = nullptr;
   618   if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
   619       mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT &&
   620       mResponseType != XML_HTTP_RESPONSE_TYPE_JSON &&
   621       mResponseType != XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
   622     return NS_OK;
   623   }
   625   nsAutoCString charsetVal;
   626   bool ok = mChannel &&
   627             NS_SUCCEEDED(mChannel->GetContentCharset(charsetVal)) &&
   628             EncodingUtils::FindEncodingForLabel(charsetVal, mResponseCharset);
   629   if (!ok || mResponseCharset.IsEmpty()) {
   630     // MS documentation states UTF-8 is default for responseText
   631     mResponseCharset.AssignLiteral("UTF-8");
   632   }
   634   if (mResponseType == XML_HTTP_RESPONSE_TYPE_JSON &&
   635       !mResponseCharset.EqualsLiteral("UTF-8")) {
   636     // The XHR spec says only UTF-8 is supported for responseType == "json"
   637     LogMessage("JSONCharsetWarning", GetOwner());
   638     mResponseCharset.AssignLiteral("UTF-8");
   639   }
   641   mDecoder = EncodingUtils::DecoderForEncoding(mResponseCharset);
   643   return NS_OK;
   644 }
   646 nsresult
   647 nsXMLHttpRequest::AppendToResponseText(const char * aSrcBuffer,
   648                                        uint32_t aSrcBufferLen)
   649 {
   650   NS_ENSURE_STATE(mDecoder);
   652   int32_t destBufferLen;
   653   nsresult rv = mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen,
   654                                        &destBufferLen);
   655   NS_ENSURE_SUCCESS(rv, rv);
   657   if (!mResponseText.SetCapacity(mResponseText.Length() + destBufferLen, fallible_t())) {
   658     return NS_ERROR_OUT_OF_MEMORY;
   659   }
   661   char16_t* destBuffer = mResponseText.BeginWriting() + mResponseText.Length();
   663   int32_t totalChars = mResponseText.Length();
   665   // This code here is basically a copy of a similar thing in
   666   // nsScanner::Append(const char* aBuffer, uint32_t aLen).
   667   int32_t srclen = (int32_t)aSrcBufferLen;
   668   int32_t destlen = (int32_t)destBufferLen;
   669   rv = mDecoder->Convert(aSrcBuffer,
   670                          &srclen,
   671                          destBuffer,
   672                          &destlen);
   673   MOZ_ASSERT(NS_SUCCEEDED(rv));
   675   totalChars += destlen;
   677   mResponseText.SetLength(totalChars);
   679   return NS_OK;
   680 }
   682 /* readonly attribute AString responseText; */
   683 NS_IMETHODIMP
   684 nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
   685 {
   686   ErrorResult rv;
   687   nsString responseText;
   688   GetResponseText(responseText, rv);
   689   aResponseText = responseText;
   690   return rv.ErrorCode();
   691 }
   693 void
   694 nsXMLHttpRequest::GetResponseText(nsString& aResponseText, ErrorResult& aRv)
   695 {
   696   aResponseText.Truncate();
   698   if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
   699       mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT &&
   700       mResponseType != XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
   701     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   702     return;
   703   }
   705   if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT &&
   706       !mInLoadProgressEvent) {
   707     aResponseText.SetIsVoid(true);
   708     return;
   709   }
   711   if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
   712     return;
   713   }
   715   // We only decode text lazily if we're also parsing to a doc.
   716   // Also, if we've decoded all current data already, then no need to decode
   717   // more.
   718   if (!mResponseXML ||
   719       mResponseBodyDecodedPos == mResponseBody.Length()) {
   720     aResponseText = mResponseText;
   721     return;
   722   }
   724   if (mResponseCharset != mResponseXML->GetDocumentCharacterSet()) {
   725     mResponseCharset = mResponseXML->GetDocumentCharacterSet();
   726     mResponseText.Truncate();
   727     mResponseBodyDecodedPos = 0;
   728     mDecoder = EncodingUtils::DecoderForEncoding(mResponseCharset);
   729   }
   731   NS_ASSERTION(mResponseBodyDecodedPos < mResponseBody.Length(),
   732                "Unexpected mResponseBodyDecodedPos");
   733   aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
   734                              mResponseBody.Length() - mResponseBodyDecodedPos);
   735   if (aRv.Failed()) {
   736     return;
   737   }
   739   mResponseBodyDecodedPos = mResponseBody.Length();
   741   if (mState & XML_HTTP_REQUEST_DONE) {
   742     // Free memory buffer which we no longer need
   743     mResponseBody.Truncate();
   744     mResponseBodyDecodedPos = 0;
   745   }
   747   aResponseText = mResponseText;
   748 }
   750 nsresult
   751 nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx)
   752 {
   753   if (!aCx) {
   754     return NS_ERROR_FAILURE;
   755   }
   756   RootJSResultObjects();
   758   // The Unicode converter has already zapped the BOM if there was one
   759   JS::Rooted<JS::Value> value(aCx);
   760   if (!JS_ParseJSON(aCx,
   761                     static_cast<const jschar*>(mResponseText.get()), mResponseText.Length(),
   762                     &value)) {
   763     return NS_ERROR_FAILURE;
   764   }
   766   mResultJSON = value;
   767   return NS_OK;
   768 }
   770 void
   771 nsXMLHttpRequest::CreatePartialBlob()
   772 {
   773   if (mDOMFile) {
   774     if (mLoadTotal == mLoadTransferred) {
   775       mResponseBlob = mDOMFile;
   776     } else {
   777       mResponseBlob =
   778         mDOMFile->CreateSlice(0, mLoadTransferred, EmptyString());
   779     }
   780     return;
   781   }
   783   // mBlobSet can be null if the request has been canceled
   784   if (!mBlobSet) {
   785     return;
   786   }
   788   nsAutoCString contentType;
   789   if (mLoadTotal == mLoadTransferred) {
   790     mChannel->GetContentType(contentType);
   791   }
   793   mResponseBlob = mBlobSet->GetBlobInternal(contentType);
   794 }
   796 /* attribute AString responseType; */
   797 NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
   798 {
   799   switch (mResponseType) {
   800   case XML_HTTP_RESPONSE_TYPE_DEFAULT:
   801     aResponseType.Truncate();
   802     break;
   803   case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
   804     aResponseType.AssignLiteral("arraybuffer");
   805     break;
   806   case XML_HTTP_RESPONSE_TYPE_BLOB:
   807     aResponseType.AssignLiteral("blob");
   808     break;
   809   case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
   810     aResponseType.AssignLiteral("document");
   811     break;
   812   case XML_HTTP_RESPONSE_TYPE_TEXT:
   813     aResponseType.AssignLiteral("text");
   814     break;
   815   case XML_HTTP_RESPONSE_TYPE_JSON:
   816     aResponseType.AssignLiteral("json");
   817     break;
   818   case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
   819     aResponseType.AssignLiteral("moz-chunked-text");
   820     break;
   821   case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
   822     aResponseType.AssignLiteral("moz-chunked-arraybuffer");
   823     break;
   824   case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
   825     aResponseType.AssignLiteral("moz-blob");
   826     break;
   827   default:
   828     NS_ERROR("Should not happen");
   829   }
   831   return NS_OK;
   832 }
   834 #ifdef DEBUG
   835 void
   836 nsXMLHttpRequest::StaticAssertions()
   837 {
   838 #define ASSERT_ENUM_EQUAL(_lc, _uc) \
   839   static_assert(\
   840     static_cast<int>(XMLHttpRequestResponseType::_lc)  \
   841     == XML_HTTP_RESPONSE_TYPE_ ## _uc, \
   842     #_uc " should match")
   844   ASSERT_ENUM_EQUAL(_empty, DEFAULT);
   845   ASSERT_ENUM_EQUAL(Arraybuffer, ARRAYBUFFER);
   846   ASSERT_ENUM_EQUAL(Blob, BLOB);
   847   ASSERT_ENUM_EQUAL(Document, DOCUMENT);
   848   ASSERT_ENUM_EQUAL(Json, JSON);
   849   ASSERT_ENUM_EQUAL(Text, TEXT);
   850   ASSERT_ENUM_EQUAL(Moz_chunked_text, CHUNKED_TEXT);
   851   ASSERT_ENUM_EQUAL(Moz_chunked_arraybuffer, CHUNKED_ARRAYBUFFER);
   852   ASSERT_ENUM_EQUAL(Moz_blob, MOZ_BLOB);
   853 #undef ASSERT_ENUM_EQUAL
   854 }
   855 #endif
   857 /* attribute AString responseType; */
   858 NS_IMETHODIMP nsXMLHttpRequest::SetResponseType(const nsAString& aResponseType)
   859 {
   860   nsXMLHttpRequest::ResponseTypeEnum responseType;
   861   if (aResponseType.IsEmpty()) {
   862     responseType = XML_HTTP_RESPONSE_TYPE_DEFAULT;
   863   } else if (aResponseType.EqualsLiteral("arraybuffer")) {
   864     responseType = XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER;
   865   } else if (aResponseType.EqualsLiteral("blob")) {
   866     responseType = XML_HTTP_RESPONSE_TYPE_BLOB;
   867   } else if (aResponseType.EqualsLiteral("document")) {
   868     responseType = XML_HTTP_RESPONSE_TYPE_DOCUMENT;
   869   } else if (aResponseType.EqualsLiteral("text")) {
   870     responseType = XML_HTTP_RESPONSE_TYPE_TEXT;
   871   } else if (aResponseType.EqualsLiteral("json")) {
   872     responseType = XML_HTTP_RESPONSE_TYPE_JSON;
   873   } else if (aResponseType.EqualsLiteral("moz-chunked-text")) {
   874     responseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT;
   875   } else if (aResponseType.EqualsLiteral("moz-chunked-arraybuffer")) {
   876     responseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER;
   877   } else if (aResponseType.EqualsLiteral("moz-blob")) {
   878     responseType = XML_HTTP_RESPONSE_TYPE_MOZ_BLOB;
   879   } else {
   880     return NS_OK;
   881   }
   883   ErrorResult rv;
   884   SetResponseType(responseType, rv);
   885   return rv.ErrorCode();
   886 }
   888 void
   889 nsXMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aType,
   890                                   ErrorResult& aRv)
   891 {
   892   SetResponseType(ResponseTypeEnum(static_cast<int>(aType)), aRv);
   893 }
   895 void
   896 nsXMLHttpRequest::SetResponseType(nsXMLHttpRequest::ResponseTypeEnum aResponseType,
   897                                   ErrorResult& aRv)
   898 {
   899   // If the state is not OPENED or HEADERS_RECEIVED raise an
   900   // INVALID_STATE_ERR exception and terminate these steps.
   901   if (!(mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT |
   902                   XML_HTTP_REQUEST_HEADERS_RECEIVED))) {
   903     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   904     return;
   905   }
   907   // sync request is not allowed setting responseType in window context
   908   if (HasOrHasHadOwner() &&
   909       !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
   910     LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
   911     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   912     return;
   913   }
   915   if (!(mState & XML_HTTP_REQUEST_ASYNC) &&
   916       (aResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
   917        aResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
   918     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   919     return;
   920   }
   922   // Set the responseType attribute's value to the given value.
   923   mResponseType = aResponseType;
   925 }
   927 /* readonly attribute jsval response; */
   928 NS_IMETHODIMP
   929 nsXMLHttpRequest::GetResponse(JSContext *aCx, JS::MutableHandle<JS::Value> aResult)
   930 {
   931   ErrorResult rv;
   932   GetResponse(aCx, aResult, rv);
   933   return rv.ErrorCode();
   934 }
   936 void
   937 nsXMLHttpRequest::GetResponse(JSContext* aCx,
   938                               JS::MutableHandle<JS::Value> aResponse,
   939                               ErrorResult& aRv)
   940 {
   941   switch (mResponseType) {
   942   case XML_HTTP_RESPONSE_TYPE_DEFAULT:
   943   case XML_HTTP_RESPONSE_TYPE_TEXT:
   944   case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
   945   {
   946     nsString str;
   947     aRv = GetResponseText(str);
   948     if (aRv.Failed()) {
   949       return;
   950     }
   951     if (!xpc::StringToJsval(aCx, str, aResponse)) {
   952       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   953     }
   954     return;
   955   }
   957   case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
   958   case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
   959   {
   960     if (!(mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
   961           mState & XML_HTTP_REQUEST_DONE) &&
   962         !(mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER &&
   963           mInLoadProgressEvent)) {
   964       aResponse.setNull();
   965       return;
   966     }
   968     if (!mResultArrayBuffer) {
   969       RootJSResultObjects();
   971       mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
   972       if (!mResultArrayBuffer) {
   973         aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   974         return;
   975       }
   976     }
   977     JS::ExposeObjectToActiveJS(mResultArrayBuffer);
   978     aResponse.setObject(*mResultArrayBuffer);
   979     return;
   980   }
   981   case XML_HTTP_RESPONSE_TYPE_BLOB:
   982   case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
   983   {
   984     if (!(mState & XML_HTTP_REQUEST_DONE)) {
   985       if (mResponseType != XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
   986         aResponse.setNull();
   987         return;
   988       }
   990       if (!mResponseBlob) {
   991         CreatePartialBlob();
   992       }
   993     }
   995     if (!mResponseBlob) {
   996       aResponse.setNull();
   997       return;
   998     }
  1000     aRv = nsContentUtils::WrapNative(aCx, mResponseBlob, aResponse);
  1001     return;
  1003   case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
  1005     if (!(mState & XML_HTTP_REQUEST_DONE) || !mResponseXML) {
  1006       aResponse.setNull();
  1007       return;
  1010     aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
  1011     return;
  1013   case XML_HTTP_RESPONSE_TYPE_JSON:
  1015     if (!(mState & XML_HTTP_REQUEST_DONE)) {
  1016       aResponse.setNull();
  1017       return;
  1020     if (mResultJSON.isUndefined()) {
  1021       aRv = CreateResponseParsedJSON(aCx);
  1022       mResponseText.Truncate();
  1023       if (aRv.Failed()) {
  1024         // Per spec, errors aren't propagated. null is returned instead.
  1025         aRv = NS_OK;
  1026         // It would be nice to log the error to the console. That's hard to
  1027         // do without calling window.onerror as a side effect, though.
  1028         JS_ClearPendingException(aCx);
  1029         mResultJSON.setNull();
  1032     JS::ExposeValueToActiveJS(mResultJSON);
  1033     aResponse.set(mResultJSON);
  1034     return;
  1036   default:
  1037     NS_ERROR("Should not happen");
  1040   aResponse.setNull();
  1043 /* readonly attribute unsigned long status; */
  1044 NS_IMETHODIMP
  1045 nsXMLHttpRequest::GetStatus(uint32_t *aStatus)
  1047   *aStatus = Status();
  1048   return NS_OK;
  1051 uint32_t
  1052 nsXMLHttpRequest::Status()
  1054   if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
  1055     // Make sure we don't leak status information from denied cross-site
  1056     // requests.
  1057     if (mChannel) {
  1058       nsresult status;
  1059       mChannel->GetStatus(&status);
  1060       if (NS_FAILED(status)) {
  1061         return 0;
  1066   uint16_t readyState;
  1067   GetReadyState(&readyState);
  1068   if (readyState == UNSENT || readyState == OPENED) {
  1069     return 0;
  1072   if (mErrorLoad) {
  1073     // Let's simulate the http protocol for jar/app requests:
  1074     nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
  1075     if (jarChannel) {
  1076       nsresult status;
  1077       mChannel->GetStatus(&status);
  1079       if (status == NS_ERROR_FILE_NOT_FOUND) {
  1080         return 404; // Not Found
  1081       } else {
  1082         return 500; // Internal Error
  1086     return 0;
  1089   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
  1090   if (!httpChannel) {
  1092     // Let's simulate the http protocol for jar/app requests:
  1093     nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
  1094     if (jarChannel) {
  1095       return 200; // Ok
  1098     return 0;
  1101   uint32_t status;
  1102   nsresult rv = httpChannel->GetResponseStatus(&status);
  1103   if (NS_FAILED(rv)) {
  1104     status = 0;
  1107   return status;
  1110 IMPL_CSTRING_GETTER(GetStatusText)
  1111 void
  1112 nsXMLHttpRequest::GetStatusText(nsCString& aStatusText)
  1114   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
  1116   aStatusText.Truncate();
  1118   if (!httpChannel) {
  1119     return;
  1122   if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
  1123     // Make sure we don't leak status information from denied cross-site
  1124     // requests.
  1125     if (mChannel) {
  1126       nsresult status;
  1127       mChannel->GetStatus(&status);
  1128       if (NS_FAILED(status)) {
  1129         return;
  1134   httpChannel->GetResponseStatusText(aStatusText);
  1138 void
  1139 nsXMLHttpRequest::CloseRequestWithError(const nsAString& aType,
  1140                                         const uint32_t aFlag)
  1142   if (mChannel) {
  1143     mChannel->Cancel(NS_BINDING_ABORTED);
  1145   if (mCORSPreflightChannel) {
  1146     mCORSPreflightChannel->Cancel(NS_BINDING_ABORTED);
  1148   if (mTimeoutTimer) {
  1149     mTimeoutTimer->Cancel();
  1151   uint32_t responseLength = mResponseBody.Length();
  1152   ResetResponse();
  1153   mState |= aFlag;
  1155   // If we're in the destructor, don't risk dispatching an event.
  1156   if (mState & XML_HTTP_REQUEST_DELETED) {
  1157     mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
  1158     return;
  1161   if (!(mState & (XML_HTTP_REQUEST_UNSENT |
  1162                   XML_HTTP_REQUEST_OPENED |
  1163                   XML_HTTP_REQUEST_DONE))) {
  1164     ChangeState(XML_HTTP_REQUEST_DONE, true);
  1166     if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
  1167       DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
  1168                             mLoadTotal);
  1169       if (mUpload && !mUploadComplete) {
  1170         mUploadComplete = true;
  1171         DispatchProgressEvent(mUpload, aType, true, mUploadTransferred,
  1172                               mUploadTotal);
  1177   // The ChangeState call above calls onreadystatechange handlers which
  1178   // if they load a new url will cause nsXMLHttpRequest::Open to clear
  1179   // the abort state bit. If this occurs we're not uninitialized (bug 361773).
  1180   if (mState & XML_HTTP_REQUEST_ABORTED) {
  1181     ChangeState(XML_HTTP_REQUEST_UNSENT, false);  // IE seems to do it
  1184   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
  1187 /* void abort (); */
  1188 void
  1189 nsXMLHttpRequest::Abort()
  1191   CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR), XML_HTTP_REQUEST_ABORTED);
  1194 NS_IMETHODIMP
  1195 nsXMLHttpRequest::SlowAbort()
  1197   Abort();
  1198   return NS_OK;
  1201 /*Method that checks if it is safe to expose a header value to the client.
  1202 It is used to check what headers are exposed for CORS requests.*/
  1203 bool
  1204 nsXMLHttpRequest::IsSafeHeader(const nsACString& header, nsIHttpChannel* httpChannel)
  1206   // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
  1207   if (!IsSystemXHR() &&
  1208        (header.LowerCaseEqualsASCII("set-cookie") ||
  1209         header.LowerCaseEqualsASCII("set-cookie2"))) {
  1210     NS_WARNING("blocked access to response header");
  1211     return false;
  1213   // if this is not a CORS call all headers are safe
  1214   if (!(mState & XML_HTTP_REQUEST_USE_XSITE_AC)){
  1215     return true;
  1217   // Check for dangerous headers
  1218   // Make sure we don't leak header information from denied cross-site
  1219   // requests.
  1220   if (mChannel) {
  1221     nsresult status;
  1222     mChannel->GetStatus(&status);
  1223     if (NS_FAILED(status)) {
  1224       return false;
  1227   const char* kCrossOriginSafeHeaders[] = {
  1228     "cache-control", "content-language", "content-type", "expires",
  1229     "last-modified", "pragma"
  1230   };
  1231   for (uint32_t i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
  1232     if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
  1233       return true;
  1236   nsAutoCString headerVal;
  1237   // The "Access-Control-Expose-Headers" header contains a comma separated
  1238   // list of method names.
  1239   httpChannel->
  1240       GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"),
  1241                         headerVal);
  1242   nsCCharSeparatedTokenizer exposeTokens(headerVal, ',');
  1243   bool isSafe = false;
  1244   while (exposeTokens.hasMoreTokens()) {
  1245     const nsDependentCSubstring& token = exposeTokens.nextToken();
  1246     if (token.IsEmpty()) {
  1247       continue;
  1249     if (!IsValidHTTPToken(token)) {
  1250       return false;
  1252     if (header.Equals(token, nsCaseInsensitiveCStringComparator())) {
  1253       isSafe = true;
  1256   return isSafe;
  1259 /* ByteString getAllResponseHeaders(); */
  1260 IMPL_CSTRING_GETTER(GetAllResponseHeaders)
  1261 void
  1262 nsXMLHttpRequest::GetAllResponseHeaders(nsCString& aResponseHeaders)
  1264   aResponseHeaders.Truncate();
  1266   // If the state is UNSENT or OPENED,
  1267   // return the empty string and terminate these steps.
  1268   if (mState & (XML_HTTP_REQUEST_UNSENT |
  1269                 XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
  1270     return;
  1273   if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
  1274     nsRefPtr<nsHeaderVisitor> visitor = new nsHeaderVisitor(this, httpChannel);
  1275     if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
  1276       aResponseHeaders = visitor->Headers();
  1278     return;
  1281   if (!mChannel) {
  1282     return;
  1285   // Even non-http channels supply content type.
  1286   nsAutoCString value;
  1287   if (NS_SUCCEEDED(mChannel->GetContentType(value))) {
  1288     aResponseHeaders.AppendLiteral("Content-Type: ");
  1289     aResponseHeaders.Append(value);
  1290     if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
  1291       aResponseHeaders.AppendLiteral(";charset=");
  1292       aResponseHeaders.Append(value);
  1294     aResponseHeaders.AppendLiteral("\r\n");
  1297   int64_t length;
  1298   if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
  1299     aResponseHeaders.AppendLiteral("Content-Length: ");
  1300     aResponseHeaders.AppendInt(length);
  1301     aResponseHeaders.AppendLiteral("\r\n");
  1305 NS_IMETHODIMP
  1306 nsXMLHttpRequest::GetResponseHeader(const nsACString& aHeader,
  1307                                     nsACString& aResult)
  1309   ErrorResult rv;
  1310   GetResponseHeader(aHeader, aResult, rv);
  1311   return rv.ErrorCode();
  1314 void
  1315 nsXMLHttpRequest::GetResponseHeader(const nsACString& header,
  1316                                     nsACString& _retval, ErrorResult& aRv)
  1318   _retval.SetIsVoid(true);
  1320   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
  1322   if (!httpChannel) {
  1323     // If the state is UNSENT or OPENED,
  1324     // return null and terminate these steps.
  1325     if (mState & (XML_HTTP_REQUEST_UNSENT |
  1326                   XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
  1327       return;
  1330     // Even non-http channels supply content type and content length.
  1331     // Remember we don't leak header information from denied cross-site
  1332     // requests.
  1333     nsresult status;
  1334     if (!mChannel ||
  1335         NS_FAILED(mChannel->GetStatus(&status)) ||
  1336         NS_FAILED(status)) {
  1337       return;
  1340     // Content Type:
  1341     if (header.LowerCaseEqualsASCII("content-type")) {
  1342       if (NS_FAILED(mChannel->GetContentType(_retval))) {
  1343         // Means no content type
  1344         _retval.SetIsVoid(true);
  1345         return;
  1348       nsCString value;
  1349       if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
  1350           !value.IsEmpty()) {
  1351         _retval.Append(";charset=");
  1352         _retval.Append(value);
  1356     // Content Length:
  1357     else if (header.LowerCaseEqualsASCII("content-length")) {
  1358       int64_t length;
  1359       if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
  1360         _retval.AppendInt(length);
  1364     return;
  1367   // Check for dangerous headers
  1368   if (!IsSafeHeader(header, httpChannel)) {
  1369     return;
  1372   aRv = httpChannel->GetResponseHeader(header, _retval);
  1373   if (aRv.ErrorCode() == NS_ERROR_NOT_AVAILABLE) {
  1374     // Means no header
  1375     _retval.SetIsVoid(true);
  1376     aRv = NS_OK;
  1380 already_AddRefed<nsILoadGroup>
  1381 nsXMLHttpRequest::GetLoadGroup() const
  1383   if (mState & XML_HTTP_REQUEST_BACKGROUND) {                 
  1384     return nullptr;
  1387   nsresult rv = NS_ERROR_FAILURE;
  1388   nsIScriptContext* sc =
  1389     const_cast<nsXMLHttpRequest*>(this)->GetContextForEventHandlers(&rv);
  1390   nsCOMPtr<nsIDocument> doc =
  1391     nsContentUtils::GetDocumentFromScriptContext(sc);
  1392   if (doc) {
  1393     return doc->GetDocumentLoadGroup();
  1396   return nullptr;
  1399 nsresult
  1400 nsXMLHttpRequest::CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent)
  1402   nsresult rv = EventDispatcher::CreateEvent(this, nullptr, nullptr,
  1403                                              NS_LITERAL_STRING("Events"),
  1404                                              aDOMEvent);
  1405   if (NS_FAILED(rv)) {
  1406     return rv;
  1409   (*aDOMEvent)->InitEvent(NS_LITERAL_STRING(READYSTATE_STR),
  1410                           false, false);
  1412   // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
  1413   (*aDOMEvent)->SetTrusted(true);
  1415   return NS_OK;
  1418 void
  1419 nsXMLHttpRequest::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
  1420                                         const nsAString& aType,
  1421                                         bool aLengthComputable,
  1422                                         uint64_t aLoaded, uint64_t aTotal)
  1424   NS_ASSERTION(aTarget, "null target");
  1425   NS_ASSERTION(!aType.IsEmpty(), "missing event type");
  1427   if (NS_FAILED(CheckInnerWindowCorrectness()) ||
  1428       (!AllowUploadProgress() && aTarget == mUpload)) {
  1429     return;
  1432   bool dispatchLoadend = aType.EqualsLiteral(LOAD_STR) ||
  1433                          aType.EqualsLiteral(ERROR_STR) ||
  1434                          aType.EqualsLiteral(TIMEOUT_STR) ||
  1435                          aType.EqualsLiteral(ABORT_STR);
  1437   nsCOMPtr<nsIDOMEvent> event;
  1438   nsresult rv = NS_NewDOMProgressEvent(getter_AddRefs(event), this,
  1439                                        nullptr, nullptr);
  1440   if (NS_FAILED(rv)) {
  1441     return;
  1444   nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
  1445   if (!progress) {
  1446     return;
  1449   progress->InitProgressEvent(aType, false, false, aLengthComputable,
  1450                               aLoaded, (aTotal == UINT64_MAX) ? 0 : aTotal);
  1452   event->SetTrusted(true);
  1454   aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  1456   if (dispatchLoadend) {
  1457     DispatchProgressEvent(aTarget, NS_LITERAL_STRING(LOADEND_STR),
  1458                           aLengthComputable, aLoaded, aTotal);
  1462 already_AddRefed<nsIHttpChannel>
  1463 nsXMLHttpRequest::GetCurrentHttpChannel()
  1465   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
  1466   return httpChannel.forget();
  1469 already_AddRefed<nsIJARChannel>
  1470 nsXMLHttpRequest::GetCurrentJARChannel()
  1472   nsCOMPtr<nsIJARChannel> appChannel = do_QueryInterface(mChannel);
  1473   return appChannel.forget();
  1476 bool
  1477 nsXMLHttpRequest::IsSystemXHR()
  1479   return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
  1482 nsresult
  1483 nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
  1485   // A system XHR (chrome code or a web app with the right permission) can
  1486   // always perform cross-site requests. In the web app case, however, we
  1487   // must still check for protected URIs like file:///.
  1488   if (IsSystemXHR()) {
  1489     if (!nsContentUtils::IsSystemPrincipal(mPrincipal)) {
  1490       nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
  1491       nsCOMPtr<nsIURI> uri;
  1492       aChannel->GetOriginalURI(getter_AddRefs(uri));
  1493       return secMan->CheckLoadURIWithPrincipal(
  1494         mPrincipal, uri, nsIScriptSecurityManager::STANDARD);
  1496     return NS_OK;
  1499   // If this is a same-origin request or the channel's URI inherits
  1500   // its principal, it's allowed.
  1501   if (nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
  1502     return NS_OK;
  1505   // This is a cross-site request
  1506   mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
  1508   // Check if we need to do a preflight request.
  1509   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
  1510   NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
  1512   nsAutoCString method;
  1513   httpChannel->GetRequestMethod(method);
  1514   if (!mCORSUnsafeHeaders.IsEmpty() ||
  1515       (mUpload && mUpload->HasListeners()) ||
  1516       (!method.LowerCaseEqualsLiteral("get") &&
  1517        !method.LowerCaseEqualsLiteral("post") &&
  1518        !method.LowerCaseEqualsLiteral("head"))) {
  1519     mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
  1522   return NS_OK;
  1525 NS_IMETHODIMP
  1526 nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
  1527                        bool async, const nsAString& user,
  1528                        const nsAString& password, uint8_t optional_argc)
  1530   if (!optional_argc) {
  1531     // No optional arguments were passed in. Default async to true.
  1532     async = true;
  1534   Optional<nsAString> realUser;
  1535   if (optional_argc > 1) {
  1536     realUser = &user;
  1538   Optional<nsAString> realPassword;
  1539   if (optional_argc > 2) {
  1540     realPassword = &password;
  1542   return Open(method, url, async, realUser, realPassword);
  1545 nsresult
  1546 nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
  1547                        bool async, const Optional<nsAString>& user,
  1548                        const Optional<nsAString>& password)
  1550   NS_ENSURE_ARG(!inMethod.IsEmpty());
  1552   if (!async && !DontWarnAboutSyncXHR() && GetOwner() &&
  1553       GetOwner()->GetExtantDoc()) {
  1554     GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
  1557   Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC,
  1558                         async ? 0 : 1);
  1560   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
  1562   // Disallow HTTP/1.1 TRACE method (see bug 302489)
  1563   // and MS IIS equivalent TRACK (see bug 381264)
  1564   // and CONNECT
  1565   if (inMethod.LowerCaseEqualsLiteral("trace") ||
  1566       inMethod.LowerCaseEqualsLiteral("connect") ||
  1567       inMethod.LowerCaseEqualsLiteral("track")) {
  1568     return NS_ERROR_DOM_SECURITY_ERR;
  1571   nsAutoCString method;
  1572   // GET, POST, DELETE, HEAD, OPTIONS, PUT methods normalized to upper case
  1573   if (inMethod.LowerCaseEqualsLiteral("get")) {
  1574     method.Assign(NS_LITERAL_CSTRING("GET"));
  1575   } else if (inMethod.LowerCaseEqualsLiteral("post")) {
  1576     method.Assign(NS_LITERAL_CSTRING("POST"));
  1577   } else if (inMethod.LowerCaseEqualsLiteral("delete")) {
  1578     method.Assign(NS_LITERAL_CSTRING("DELETE"));
  1579   } else if (inMethod.LowerCaseEqualsLiteral("head")) {
  1580     method.Assign(NS_LITERAL_CSTRING("HEAD"));
  1581   } else if (inMethod.LowerCaseEqualsLiteral("options")) {
  1582     method.Assign(NS_LITERAL_CSTRING("OPTIONS"));
  1583   } else if (inMethod.LowerCaseEqualsLiteral("put")) {
  1584     method.Assign(NS_LITERAL_CSTRING("PUT"));
  1585   } else {
  1586     method = inMethod; // other methods are not normalized
  1589   // sync request is not allowed using withCredential or responseType
  1590   // in window context
  1591   if (!async && HasOrHasHadOwner() &&
  1592       (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
  1593        mTimeoutMilliseconds ||
  1594        mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
  1595     if (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS) {
  1596       LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
  1598     if (mTimeoutMilliseconds) {
  1599       LogMessage("TimeoutSyncXHRWarning", GetOwner());
  1601     if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT) {
  1602       LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
  1604     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
  1607   nsresult rv;
  1608   nsCOMPtr<nsIURI> uri;
  1610   if (mState & (XML_HTTP_REQUEST_OPENED |
  1611                 XML_HTTP_REQUEST_HEADERS_RECEIVED |
  1612                 XML_HTTP_REQUEST_LOADING |
  1613                 XML_HTTP_REQUEST_SENT)) {
  1614     // IE aborts as well
  1615     Abort();
  1617     // XXX We should probably send a warning to the JS console
  1618     //     that load was aborted and event listeners were cleared
  1619     //     since this looks like a situation that could happen
  1620     //     by accident and you could spend a lot of time wondering
  1621     //     why things didn't work.
  1624   // Unset any pre-existing aborted and timed-out states.
  1625   mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
  1627   if (async) {
  1628     mState |= XML_HTTP_REQUEST_ASYNC;
  1629   } else {
  1630     mState &= ~XML_HTTP_REQUEST_ASYNC;
  1633   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
  1634   NS_ENSURE_SUCCESS(rv, rv);
  1635   nsCOMPtr<nsIDocument> doc =
  1636     nsContentUtils::GetDocumentFromScriptContext(sc);
  1638   nsCOMPtr<nsIURI> baseURI;
  1639   if (mBaseURI) {
  1640     baseURI = mBaseURI;
  1642   else if (doc) {
  1643     baseURI = doc->GetBaseURI();
  1646   rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, baseURI);
  1647   if (NS_FAILED(rv)) return rv;
  1649   rv = CheckInnerWindowCorrectness();
  1650   NS_ENSURE_SUCCESS(rv, rv);
  1651   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
  1652   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XMLHTTPREQUEST,
  1653                                  uri,
  1654                                  mPrincipal,
  1655                                  doc,
  1656                                  EmptyCString(), //mime guess
  1657                                  nullptr,         //extra
  1658                                  &shouldLoad,
  1659                                  nsContentUtils::GetContentPolicy(),
  1660                                  nsContentUtils::GetSecurityManager());
  1661   if (NS_FAILED(rv)) return rv;
  1662   if (NS_CP_REJECTED(shouldLoad)) {
  1663     // Disallowed by content policy
  1664     return NS_ERROR_CONTENT_BLOCKED;
  1667   // XXXbz this is wrong: we should only be looking at whether
  1668   // user/password were passed, not at the values!  See bug 759624.
  1669   if (user.WasPassed() && !user.Value().IsEmpty()) {
  1670     nsAutoCString userpass;
  1671     CopyUTF16toUTF8(user.Value(), userpass);
  1672     if (password.WasPassed() && !password.Value().IsEmpty()) {
  1673       userpass.Append(':');
  1674       AppendUTF16toUTF8(password.Value(), userpass);
  1676     uri->SetUserPass(userpass);
  1679   // Clear our record of previously set headers so future header set
  1680   // operations will merge/override correctly.
  1681   mAlreadySetHeaders.Clear();
  1683   // When we are called from JS we can find the load group for the page,
  1684   // and add ourselves to it. This way any pending requests
  1685   // will be automatically aborted if the user leaves the page.
  1686   nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
  1688   // get Content Security Policy from principal to pass into channel
  1689   nsCOMPtr<nsIChannelPolicy> channelPolicy;
  1690   nsCOMPtr<nsIContentSecurityPolicy> csp;
  1691   rv = mPrincipal->GetCsp(getter_AddRefs(csp));
  1692   NS_ENSURE_SUCCESS(rv, rv);
  1693   if (csp) {
  1694     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
  1695     channelPolicy->SetContentSecurityPolicy(csp);
  1696     channelPolicy->SetLoadType(nsIContentPolicy::TYPE_XMLHTTPREQUEST);
  1698   rv = NS_NewChannel(getter_AddRefs(mChannel),
  1699                      uri,
  1700                      nullptr,                    // ioService
  1701                      loadGroup,
  1702                      nullptr,                    // callbacks
  1703                      nsIRequest::LOAD_BACKGROUND,
  1704                      channelPolicy);
  1705   if (NS_FAILED(rv)) return rv;
  1707   mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
  1708               XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
  1710   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
  1711   if (httpChannel) {
  1712     rv = httpChannel->SetRequestMethod(method);
  1713     NS_ENSURE_SUCCESS(rv, rv);
  1715     // Set the initiator type
  1716     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
  1717     if (timedChannel) {
  1718       timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
  1722   ChangeState(XML_HTTP_REQUEST_OPENED);
  1724   return rv;
  1727 /*
  1728  * "Copy" from a stream.
  1729  */
  1730 NS_METHOD
  1731 nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
  1732                                    void* closure,
  1733                                    const char* fromRawSegment,
  1734                                    uint32_t toOffset,
  1735                                    uint32_t count,
  1736                                    uint32_t *writeCount)
  1738   nsXMLHttpRequest* xmlHttpRequest = static_cast<nsXMLHttpRequest*>(closure);
  1739   if (!xmlHttpRequest || !writeCount) {
  1740     NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
  1741     return NS_ERROR_FAILURE;
  1744   nsresult rv = NS_OK;
  1746   if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
  1747       xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
  1748     if (!xmlHttpRequest->mDOMFile) {
  1749       if (!xmlHttpRequest->mBlobSet) {
  1750         xmlHttpRequest->mBlobSet = new BlobSet();
  1752       rv = xmlHttpRequest->mBlobSet->AppendVoidPtr(fromRawSegment, count);
  1754     // Clear the cache so that the blob size is updated.
  1755     if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
  1756       xmlHttpRequest->mResponseBlob = nullptr;
  1758   } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
  1759              xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
  1760     // get the initial capacity to something reasonable to avoid a bunch of reallocs right
  1761     // at the start
  1762     if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
  1763       xmlHttpRequest->mArrayBufferBuilder.setCapacity(PR_MAX(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
  1765     xmlHttpRequest->mArrayBufferBuilder.append(reinterpret_cast<const uint8_t*>(fromRawSegment), count,
  1766                                                XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH);
  1767   } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT &&
  1768              xmlHttpRequest->mResponseXML) {
  1769     // Copy for our own use
  1770     uint32_t previousLength = xmlHttpRequest->mResponseBody.Length();
  1771     xmlHttpRequest->mResponseBody.Append(fromRawSegment,count);
  1772     if (count > 0 && xmlHttpRequest->mResponseBody.Length() == previousLength) {
  1773       return NS_ERROR_OUT_OF_MEMORY;
  1775   } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
  1776              xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_TEXT ||
  1777              xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_JSON ||
  1778              xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
  1779     NS_ASSERTION(!xmlHttpRequest->mResponseXML,
  1780                  "We shouldn't be parsing a doc here");
  1781     xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
  1784   if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
  1785     // Give the same data to the parser.
  1787     // We need to wrap the data in a new lightweight stream and pass that
  1788     // to the parser, because calling ReadSegments() recursively on the same
  1789     // stream is not supported.
  1790     nsCOMPtr<nsIInputStream> copyStream;
  1791     rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
  1793     if (NS_SUCCEEDED(rv) && xmlHttpRequest->mXMLParserStreamListener) {
  1794       NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
  1795       nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
  1796                                   ->OnDataAvailable(xmlHttpRequest->mChannel,
  1797                                                     xmlHttpRequest->mContext,
  1798                                                     copyStream, toOffset, count);
  1800       // No use to continue parsing if we failed here, but we
  1801       // should still finish reading the stream
  1802       if (NS_FAILED(parsingResult)) {
  1803         xmlHttpRequest->mState &= ~XML_HTTP_REQUEST_PARSEBODY;
  1808   if (NS_SUCCEEDED(rv)) {
  1809     *writeCount = count;
  1810   } else {
  1811     *writeCount = 0;
  1814   return rv;
  1817 bool nsXMLHttpRequest::CreateDOMFile(nsIRequest *request)
  1819   nsCOMPtr<nsIFile> file;
  1820   nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(request);
  1821   if (fc) {
  1822     fc->GetFile(getter_AddRefs(file));
  1825   if (!file)
  1826     return false;
  1828   nsAutoCString contentType;
  1829   mChannel->GetContentType(contentType);
  1831   mDOMFile =
  1832     new nsDOMFileFile(file, EmptyString(), NS_ConvertASCIItoUTF16(contentType));
  1833   mBlobSet = nullptr;
  1834   NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
  1835   return true;
  1838 NS_IMETHODIMP
  1839 nsXMLHttpRequest::OnDataAvailable(nsIRequest *request,
  1840                                   nsISupports *ctxt,
  1841                                   nsIInputStream *inStr,
  1842                                   uint64_t sourceOffset,
  1843                                   uint32_t count)
  1845   NS_ENSURE_ARG_POINTER(inStr);
  1847   NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context");
  1849   mProgressSinceLastProgressEvent = true;
  1851   bool cancelable = false;
  1852   if ((mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
  1853        mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) && !mDOMFile) {
  1854     cancelable = CreateDOMFile(request);
  1855     // The nsIStreamListener contract mandates us
  1856     // to read from the stream before returning.
  1859   uint32_t totalRead;
  1860   nsresult rv = inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc,
  1861                                     (void*)this, count, &totalRead);
  1862   NS_ENSURE_SUCCESS(rv, rv);
  1864   if (cancelable) {
  1865     // We don't have to read from the local file for the blob response
  1866     mDOMFile->GetSize(&mLoadTransferred);
  1867     ChangeState(XML_HTTP_REQUEST_LOADING);
  1868     return request->Cancel(NS_OK);
  1871   mLoadTransferred += totalRead;
  1873   ChangeState(XML_HTTP_REQUEST_LOADING);
  1875   MaybeDispatchProgressEvents(false);
  1877   return NS_OK;
  1880 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
  1881 NS_IMETHODIMP
  1882 nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
  1884   PROFILER_LABEL("nsXMLHttpRequest", "OnStartRequest");
  1885   nsresult rv = NS_OK;
  1886   if (!mFirstStartRequestSeen && mRequestObserver) {
  1887     mFirstStartRequestSeen = true;
  1888     mRequestObserver->OnStartRequest(request, ctxt);
  1891   if (request != mChannel) {
  1892     // Can this still happen?
  1893     return NS_OK;
  1896   // Don't do anything if we have been aborted
  1897   if (mState & XML_HTTP_REQUEST_UNSENT)
  1898     return NS_OK;
  1900   /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT.  See bug 361773.
  1901      XHR2 spec says this is correct. */
  1902   if (mState & XML_HTTP_REQUEST_ABORTED) {
  1903     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
  1905     return NS_ERROR_UNEXPECTED;
  1908   // Don't do anything if we have timed out.
  1909   if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
  1910     return NS_OK;
  1913   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
  1914   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
  1916   nsCOMPtr<nsIPrincipal> documentPrincipal;
  1917   if (IsSystemXHR()) {
  1918     // Don't give this document the system principal.  We need to keep track of
  1919     // mPrincipal being system because we use it for various security checks
  1920     // that should be passing, but the document data shouldn't get a system
  1921     // principal.
  1922     nsresult rv;
  1923     documentPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
  1924     NS_ENSURE_SUCCESS(rv, rv);
  1925   } else {
  1926     documentPrincipal = mPrincipal;
  1929   channel->SetOwner(documentPrincipal);
  1931   nsresult status;
  1932   request->GetStatus(&status);
  1933   mErrorLoad = mErrorLoad || NS_FAILED(status);
  1935   if (mUpload && !mUploadComplete && !mErrorLoad &&
  1936       (mState & XML_HTTP_REQUEST_ASYNC)) {
  1937     if (mProgressTimerIsActive) {
  1938       mProgressTimerIsActive = false;
  1939       mProgressNotifier->Cancel();
  1941     if (mUploadTransferred < mUploadTotal) {
  1942       mUploadTransferred = mUploadTotal;
  1943       mProgressSinceLastProgressEvent = true;
  1944       mUploadLengthComputable = true;
  1945       MaybeDispatchProgressEvents(true);
  1947     mUploadComplete = true;
  1948     DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR),
  1949                           true, mUploadTotal, mUploadTotal);
  1952   mContext = ctxt;
  1953   mState |= XML_HTTP_REQUEST_PARSEBODY;
  1954   ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
  1956   ResetResponse();
  1958   if (!mOverrideMimeType.IsEmpty()) {
  1959     channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
  1962   DetectCharset();
  1964   // Set up arraybuffer
  1965   if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER && NS_SUCCEEDED(status)) {
  1966     int64_t contentLength;
  1967     rv = channel->GetContentLength(&contentLength);
  1968     if (NS_SUCCEEDED(rv) &&
  1969         contentLength > 0 &&
  1970         contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
  1971       mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
  1975   // Set up responseXML
  1976   bool parseBody = mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
  1977                      mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT;
  1978   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
  1979   if (parseBody && httpChannel) {
  1980     nsAutoCString method;
  1981     httpChannel->GetRequestMethod(method);
  1982     parseBody = !method.EqualsLiteral("HEAD");
  1985   mIsHtml = false;
  1986   mWarnAboutSyncHtml = false;
  1987   if (parseBody && NS_SUCCEEDED(status)) {
  1988     // We can gain a huge performance win by not even trying to
  1989     // parse non-XML data. This also protects us from the situation
  1990     // where we have an XML document and sink, but HTML (or other)
  1991     // parser, which can produce unreliable results.
  1992     nsAutoCString type;
  1993     channel->GetContentType(type);
  1995     if ((mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT) &&
  1996         type.EqualsLiteral("text/html")) {
  1997       // HTML parsing is only supported for responseType == "document" to
  1998       // avoid running the parser and, worse, populating responseXML for
  1999       // legacy users of XHR who use responseType == "" for retrieving the
  2000       // responseText of text/html resources. This legacy case is so common
  2001       // that it's not useful to emit a warning about it.
  2002       if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
  2003         // We don't make cool new features available in the bad synchronous
  2004         // mode. The synchronous mode is for legacy only.
  2005         mWarnAboutSyncHtml = true;
  2006         mState &= ~XML_HTTP_REQUEST_PARSEBODY;
  2007       } else {
  2008         mIsHtml = true;
  2010     } else if (type.Find("xml") == kNotFound) {
  2011       mState &= ~XML_HTTP_REQUEST_PARSEBODY;
  2013   } else {
  2014     // The request failed, so we shouldn't be parsing anyway
  2015     mState &= ~XML_HTTP_REQUEST_PARSEBODY;
  2018   if (mState & XML_HTTP_REQUEST_PARSEBODY) {
  2019     nsCOMPtr<nsIURI> baseURI, docURI;
  2020     rv = mChannel->GetURI(getter_AddRefs(docURI));
  2021     NS_ENSURE_SUCCESS(rv, rv);
  2022     baseURI = docURI;
  2024     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
  2025     NS_ENSURE_SUCCESS(rv, rv);
  2026     nsCOMPtr<nsIDocument> doc =
  2027       nsContentUtils::GetDocumentFromScriptContext(sc);
  2028     nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
  2029     if (doc) {
  2030       chromeXHRDocURI = doc->GetDocumentURI();
  2031       chromeXHRDocBaseURI = doc->GetBaseURI();
  2034     // Create an empty document from it.  Here we have to cheat a little bit...
  2035     // Setting the base URI to |baseURI| won't work if the document has a null
  2036     // principal, so use mPrincipal when creating the document, then reset the
  2037     // principal.
  2038     const nsAString& emptyStr = EmptyString();
  2039     nsCOMPtr<nsIDOMDocument> responseDoc;
  2040     nsIGlobalObject* global = DOMEventTargetHelper::GetParentObject();
  2041     rv = NS_NewDOMDocument(getter_AddRefs(responseDoc),
  2042                            emptyStr, emptyStr, nullptr, docURI,
  2043                            baseURI, mPrincipal, true, global,
  2044                            mIsHtml ? DocumentFlavorHTML :
  2045                                      DocumentFlavorLegacyGuess);
  2046     NS_ENSURE_SUCCESS(rv, rv);
  2047     mResponseXML = do_QueryInterface(responseDoc);
  2048     mResponseXML->SetPrincipal(documentPrincipal);
  2049     mResponseXML->SetChromeXHRDocURI(chromeXHRDocURI);
  2050     mResponseXML->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI);
  2052     if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
  2053       mResponseXML->ForceEnableXULXBL();
  2056     if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
  2057       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
  2058       if (htmlDoc) {
  2059         htmlDoc->DisableCookieAccess();
  2063     nsCOMPtr<nsIStreamListener> listener;
  2064     nsCOMPtr<nsILoadGroup> loadGroup;
  2065     channel->GetLoadGroup(getter_AddRefs(loadGroup));
  2067     rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
  2068                                          nullptr, getter_AddRefs(listener),
  2069                                          !(mState & XML_HTTP_REQUEST_USE_XSITE_AC));
  2070     NS_ENSURE_SUCCESS(rv, rv);
  2072     mXMLParserStreamListener = listener;
  2073     rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
  2074     NS_ENSURE_SUCCESS(rv, rv);
  2077   // We won't get any progress events anyway if we didn't have progress
  2078   // events when starting the request - so maybe no need to start timer here.
  2079   if (NS_SUCCEEDED(rv) &&
  2080       (mState & XML_HTTP_REQUEST_ASYNC) &&
  2081       HasListenersFor(nsGkAtoms::onprogress)) {
  2082     StartProgressEventTimer();
  2085   return NS_OK;
  2088 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status, in wstring statusArg); */
  2089 NS_IMETHODIMP
  2090 nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
  2092   PROFILER_LABEL("content", "nsXMLHttpRequest::OnStopRequest");
  2093   if (request != mChannel) {
  2094     // Can this still happen?
  2095     return NS_OK;
  2098   mWaitingForOnStopRequest = false;
  2100   if (mRequestObserver) {
  2101     NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
  2102     mFirstStartRequestSeen = false;
  2103     mRequestObserver->OnStopRequest(request, ctxt, status);
  2106   // make sure to notify the listener if we were aborted
  2107   // XXX in fact, why don't we do the cleanup below in this case??
  2108   // XML_HTTP_REQUEST_UNSENT is for abort calls.  See OnStartRequest above.
  2109   if ((mState & XML_HTTP_REQUEST_UNSENT) ||
  2110       (mState & XML_HTTP_REQUEST_TIMED_OUT)) {
  2111     if (mXMLParserStreamListener)
  2112       (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
  2113     return NS_OK;
  2116   // Is this good enough here?
  2117   if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
  2118     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
  2121   mXMLParserStreamListener = nullptr;
  2122   mContext = nullptr;
  2124   // If we're received data since the last progress event, make sure to fire
  2125   // an event for it, except in the HTML case, defer the last progress event
  2126   // until the parser is done.
  2127   if (!mIsHtml) {
  2128     MaybeDispatchProgressEvents(true);
  2131   if (NS_SUCCEEDED(status) &&
  2132       (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
  2133        mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB)) {
  2134     if (!mDOMFile) {
  2135       CreateDOMFile(request);
  2137     if (mDOMFile) {
  2138       mResponseBlob = mDOMFile;
  2139       mDOMFile = nullptr;
  2140     } else {
  2141       // mBlobSet can be null if the channel is non-file non-cacheable
  2142       // and if the response length is zero.
  2143       if (!mBlobSet) {
  2144         mBlobSet = new BlobSet();
  2146       // Smaller files may be written in cache map instead of separate files.
  2147       // Also, no-store response cannot be written in persistent cache.
  2148       nsAutoCString contentType;
  2149       mChannel->GetContentType(contentType);
  2150       mResponseBlob = mBlobSet->GetBlobInternal(contentType);
  2151       mBlobSet = nullptr;
  2153     NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
  2154     NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
  2155   } else if (NS_SUCCEEDED(status) &&
  2156              (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
  2157               mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
  2158     // set the capacity down to the actual length, to realloc back
  2159     // down to the actual size
  2160     if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
  2161       // this should never happen!
  2162       status = NS_ERROR_UNEXPECTED;
  2166   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
  2167   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
  2169   channel->SetNotificationCallbacks(nullptr);
  2170   mNotificationCallbacks = nullptr;
  2171   mChannelEventSink = nullptr;
  2172   mProgressEventSink = nullptr;
  2174   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
  2176   if (NS_FAILED(status)) {
  2177     // This can happen if the server is unreachable. Other possible
  2178     // reasons are that the user leaves the page or hits the ESC key.
  2180     mErrorLoad = true;
  2181     mResponseXML = nullptr;
  2184   // If we're uninitialized at this point, we encountered an error
  2185   // earlier and listeners have already been notified. Also we do
  2186   // not want to do this if we already completed.
  2187   if (mState & (XML_HTTP_REQUEST_UNSENT |
  2188                 XML_HTTP_REQUEST_DONE)) {
  2189     return NS_OK;
  2192   if (!mResponseXML) {
  2193     ChangeStateToDone();
  2194     return NS_OK;
  2196   if (mIsHtml) {
  2197     NS_ASSERTION(!(mState & XML_HTTP_REQUEST_SYNCLOOPING),
  2198       "We weren't supposed to support HTML parsing with XHR!");
  2199     nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
  2200     EventListenerManager* manager =
  2201       eventTarget->GetOrCreateListenerManager();
  2202     manager->AddEventListenerByType(new nsXHRParseEndListener(this),
  2203                                     NS_LITERAL_STRING("DOMContentLoaded"),
  2204                                     TrustedEventsAtSystemGroupBubble());
  2205     return NS_OK;
  2207   // We might have been sent non-XML data. If that was the case,
  2208   // we should null out the document member. The idea in this
  2209   // check here is that if there is no document element it is not
  2210   // an XML document. We might need a fancier check...
  2211   if (!mResponseXML->GetRootElement()) {
  2212     mResponseXML = nullptr;
  2214   ChangeStateToDone();
  2215   return NS_OK;
  2218 void
  2219 nsXMLHttpRequest::ChangeStateToDone()
  2221   if (mIsHtml) {
  2222     // In the HTML case, this has to be deferred, because the parser doesn't
  2223     // do it's job synchronously.
  2224     MaybeDispatchProgressEvents(true);
  2227   ChangeState(XML_HTTP_REQUEST_DONE, true);
  2228   if (mTimeoutTimer) {
  2229     mTimeoutTimer->Cancel();
  2232   NS_NAMED_LITERAL_STRING(errorStr, ERROR_STR);
  2233   NS_NAMED_LITERAL_STRING(loadStr, LOAD_STR);
  2234   DispatchProgressEvent(this,
  2235                         mErrorLoad ? errorStr : loadStr,
  2236                         !mErrorLoad,
  2237                         mLoadTransferred,
  2238                         mErrorLoad ? 0 : mLoadTransferred);
  2239   if (mErrorLoad && mUpload && !mUploadComplete) {
  2240     DispatchProgressEvent(mUpload, errorStr, true,
  2241                           mUploadTransferred, mUploadTotal);
  2244   if (mErrorLoad) {
  2245     // By nulling out channel here we make it so that Send() can test
  2246     // for that and throw. Also calling the various status
  2247     // methods/members will not throw.
  2248     // This matches what IE does.
  2249     mChannel = nullptr;
  2250     mCORSPreflightChannel = nullptr;
  2254 NS_IMETHODIMP
  2255 nsXMLHttpRequest::SendAsBinary(const nsAString &aBody)
  2257   ErrorResult rv;
  2258   SendAsBinary(aBody, rv);
  2259   return rv.ErrorCode();
  2262 void
  2263 nsXMLHttpRequest::SendAsBinary(const nsAString &aBody,
  2264                                ErrorResult& aRv)
  2266   char *data = static_cast<char*>(NS_Alloc(aBody.Length() + 1));
  2267   if (!data) {
  2268     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  2269     return;
  2272   if (GetOwner() && GetOwner()->GetExtantDoc()) {
  2273     GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSendAsBinary);
  2276   nsAString::const_iterator iter, end;
  2277   aBody.BeginReading(iter);
  2278   aBody.EndReading(end);
  2279   char *p = data;
  2280   while (iter != end) {
  2281     if (*iter & 0xFF00) {
  2282       NS_Free(data);
  2283       aRv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
  2284       return;
  2286     *p++ = static_cast<char>(*iter++);
  2288   *p = '\0';
  2290   nsCOMPtr<nsIInputStream> stream;
  2291   aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, aBody.Length(),
  2292                               NS_ASSIGNMENT_ADOPT);
  2293   if (aRv.Failed()) {
  2294     NS_Free(data);
  2295     return;
  2298   nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
  2300   aRv = variant->SetAsISupports(stream);
  2301   if (aRv.Failed()) {
  2302     return;
  2305   aRv = Send(variant);
  2308 static nsresult
  2309 GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
  2310                uint64_t* aContentLength, nsACString& aContentType,
  2311                nsACString& aCharset)
  2313   aContentType.AssignLiteral("application/xml");
  2314   nsAutoString inputEncoding;
  2315   aDoc->GetInputEncoding(inputEncoding);
  2316   if (!DOMStringIsNull(inputEncoding)) {
  2317     CopyUTF16toUTF8(inputEncoding, aCharset);
  2319   else {
  2320     aCharset.AssignLiteral("UTF-8");
  2323   // Serialize to a stream so that the encoding used will
  2324   // match the document's.
  2325   nsresult rv;
  2326   nsCOMPtr<nsIDOMSerializer> serializer =
  2327     do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
  2328   NS_ENSURE_SUCCESS(rv, rv);
  2330   nsCOMPtr<nsIStorageStream> storStream;
  2331   rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
  2332   NS_ENSURE_SUCCESS(rv, rv);
  2334   nsCOMPtr<nsIOutputStream> output;
  2335   rv = storStream->GetOutputStream(0, getter_AddRefs(output));
  2336   NS_ENSURE_SUCCESS(rv, rv);
  2338   // Make sure to use the encoding we'll send
  2339   rv = serializer->SerializeToStream(aDoc, output, aCharset);
  2340   NS_ENSURE_SUCCESS(rv, rv);
  2342   output->Close();
  2344   uint32_t length;
  2345   rv = storStream->GetLength(&length);
  2346   NS_ENSURE_SUCCESS(rv, rv);
  2347   *aContentLength = length;
  2349   return storStream->NewInputStream(0, aResult);
  2352 static nsresult
  2353 GetRequestBody(const nsAString& aString, nsIInputStream** aResult,
  2354                uint64_t* aContentLength, nsACString& aContentType,
  2355                nsACString& aCharset)
  2357   aContentType.AssignLiteral("text/plain");
  2358   aCharset.AssignLiteral("UTF-8");
  2360   nsCString converted = NS_ConvertUTF16toUTF8(aString);
  2361   *aContentLength = converted.Length();
  2362   return NS_NewCStringInputStream(aResult, converted);
  2365 static nsresult
  2366 GetRequestBody(nsIInputStream* aStream, nsIInputStream** aResult,
  2367                uint64_t* aContentLength, nsACString& aContentType,
  2368                nsACString& aCharset)
  2370   aContentType.AssignLiteral("text/plain");
  2371   aCharset.Truncate();
  2373   nsresult rv = aStream->Available(aContentLength);
  2374   NS_ENSURE_SUCCESS(rv, rv);
  2376   NS_ADDREF(*aResult = aStream);
  2378   return NS_OK;
  2381 static nsresult
  2382 GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult, uint64_t* aContentLength,
  2383                nsACString& aContentType, nsACString& aCharset)
  2385   return aSendable->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
  2388 // Used for array buffers and array buffer views
  2389 static nsresult
  2390 GetRequestBody(const uint8_t* aData, uint32_t aDataLength,
  2391                nsIInputStream** aResult, uint64_t* aContentLength,
  2392                nsACString& aContentType, nsACString& aCharset)
  2394   aContentType.SetIsVoid(true);
  2395   aCharset.Truncate();
  2397   *aContentLength = aDataLength;
  2398   const char* data = reinterpret_cast<const char*>(aData);
  2400   nsCOMPtr<nsIInputStream> stream;
  2401   nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
  2402                                       NS_ASSIGNMENT_COPY);
  2403   NS_ENSURE_SUCCESS(rv, rv);
  2405   stream.forget(aResult);
  2407   return NS_OK;
  2410 static nsresult
  2411 GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLength,
  2412                nsACString& aContentType, nsACString& aCharset)
  2414   *aResult = nullptr;
  2416   uint16_t dataType;
  2417   nsresult rv = aBody->GetDataType(&dataType);
  2418   NS_ENSURE_SUCCESS(rv, rv);
  2420   if (dataType == nsIDataType::VTYPE_INTERFACE ||
  2421       dataType == nsIDataType::VTYPE_INTERFACE_IS) {
  2422     nsCOMPtr<nsISupports> supports;
  2423     nsID *iid;
  2424     rv = aBody->GetAsInterface(&iid, getter_AddRefs(supports));
  2425     NS_ENSURE_SUCCESS(rv, rv);
  2427     nsMemory::Free(iid);
  2429     // document?
  2430     nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(supports);
  2431     if (doc) {
  2432       return GetRequestBody(doc, aResult, aContentLength, aContentType, aCharset);
  2435     // nsISupportsString?
  2436     nsCOMPtr<nsISupportsString> wstr = do_QueryInterface(supports);
  2437     if (wstr) {
  2438       nsAutoString string;
  2439       wstr->GetData(string);
  2441       return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
  2444     // nsIInputStream?
  2445     nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
  2446     if (stream) {
  2447       return GetRequestBody(stream, aResult, aContentLength, aContentType, aCharset);
  2450     // nsIXHRSendable?
  2451     nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
  2452     if (sendable) {
  2453       return GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
  2456     // ArrayBuffer?
  2457     AutoSafeJSContext cx;
  2458     JS::Rooted<JS::Value> realVal(cx);
  2460     nsresult rv = aBody->GetAsJSVal(&realVal);
  2461     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal)) {
  2462       JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(realVal));
  2463       if (JS_IsArrayBufferObject(obj)) {
  2464           ArrayBuffer buf(obj);
  2465           buf.ComputeLengthAndData();
  2466           return GetRequestBody(buf.Data(), buf.Length(), aResult,
  2467                                 aContentLength, aContentType, aCharset);
  2471   else if (dataType == nsIDataType::VTYPE_VOID ||
  2472            dataType == nsIDataType::VTYPE_EMPTY) {
  2473     // Makes us act as if !aBody, don't upload anything
  2474     aContentType.AssignLiteral("text/plain");
  2475     aCharset.AssignLiteral("UTF-8");
  2476     *aContentLength = 0;
  2478     return NS_OK;
  2481   char16_t* data = nullptr;
  2482   uint32_t len = 0;
  2483   rv = aBody->GetAsWStringWithSize(&len, &data);
  2484   NS_ENSURE_SUCCESS(rv, rv);
  2486   nsString string;
  2487   string.Adopt(data, len);
  2489   return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
  2492 /* static */
  2493 nsresult
  2494 nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
  2495                                  const Nullable<RequestBody>& aBody,
  2496                                  nsIInputStream** aResult,
  2497                                  uint64_t* aContentLength,
  2498                                  nsACString& aContentType, nsACString& aCharset)
  2500   if (aVariant) {
  2501     return ::GetRequestBody(aVariant, aResult, aContentLength, aContentType, aCharset);
  2504   const RequestBody& body = aBody.Value();
  2505   RequestBody::Value value = body.GetValue();
  2506   switch (body.GetType()) {
  2507     case nsXMLHttpRequest::RequestBody::ArrayBuffer:
  2509       const ArrayBuffer* buffer = value.mArrayBuffer;
  2510       buffer->ComputeLengthAndData();
  2511       return ::GetRequestBody(buffer->Data(), buffer->Length(), aResult,
  2512                               aContentLength, aContentType, aCharset);
  2514     case nsXMLHttpRequest::RequestBody::ArrayBufferView:
  2516       const ArrayBufferView* view = value.mArrayBufferView;
  2517       view->ComputeLengthAndData();
  2518       return ::GetRequestBody(view->Data(), view->Length(), aResult,
  2519                               aContentLength, aContentType, aCharset);
  2521     case nsXMLHttpRequest::RequestBody::Blob:
  2523       nsresult rv;
  2524       nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mBlob, &rv);
  2525       NS_ENSURE_SUCCESS(rv, rv);
  2527       return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
  2529     case nsXMLHttpRequest::RequestBody::Document:
  2531       nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(value.mDocument);
  2532       return ::GetRequestBody(document, aResult, aContentLength, aContentType, aCharset);
  2534     case nsXMLHttpRequest::RequestBody::DOMString:
  2536       return ::GetRequestBody(*value.mString, aResult, aContentLength,
  2537                               aContentType, aCharset);
  2539     case nsXMLHttpRequest::RequestBody::FormData:
  2541       MOZ_ASSERT(value.mFormData);
  2542       return ::GetRequestBody(value.mFormData, aResult, aContentLength,
  2543                               aContentType, aCharset);
  2545     case nsXMLHttpRequest::RequestBody::InputStream:
  2547       return ::GetRequestBody(value.mStream, aResult, aContentLength,
  2548                               aContentType, aCharset);
  2550     default:
  2552       return NS_ERROR_FAILURE;
  2556   NS_NOTREACHED("Default cases exist for a reason");
  2557   return NS_OK;
  2560 /* void send (in nsIVariant aBody); */
  2561 NS_IMETHODIMP
  2562 nsXMLHttpRequest::Send(nsIVariant *aBody)
  2564   return Send(aBody, Nullable<RequestBody>());
  2567 nsresult
  2568 nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
  2570   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
  2572   nsresult rv = CheckInnerWindowCorrectness();
  2573   NS_ENSURE_SUCCESS(rv, rv);
  2575   // Return error if we're already processing a request
  2576   if (XML_HTTP_REQUEST_SENT & mState) {
  2577     return NS_ERROR_FAILURE;
  2580   // Make sure we've been opened
  2581   if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
  2582     return NS_ERROR_NOT_INITIALIZED;
  2586   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
  2587   // in turn keeps STOP button from becoming active.  If the consumer passed in
  2588   // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
  2589   // necko won't generate any progress notifications.
  2590   if (HasListenersFor(nsGkAtoms::onprogress) ||
  2591       (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress))) {
  2592     nsLoadFlags loadFlags;
  2593     mChannel->GetLoadFlags(&loadFlags);
  2594     loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
  2595     loadFlags |= nsIRequest::LOAD_NORMAL;
  2596     mChannel->SetLoadFlags(loadFlags);
  2599   // XXX We should probably send a warning to the JS console
  2600   //     if there are no event listeners set and we are doing
  2601   //     an asynchronous call.
  2603   // Ignore argument if method is GET, there is no point in trying to
  2604   // upload anything
  2605   nsAutoCString method;
  2606   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
  2608   if (httpChannel) {
  2609     httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
  2611     if (!IsSystemXHR()) {
  2612       // Get the referrer for the request.
  2613       //
  2614       // If it weren't for history.push/replaceState, we could just use the
  2615       // principal's URI here.  But since we want changes to the URI effected
  2616       // by push/replaceState to be reflected in the XHR referrer, we have to
  2617       // be more clever.
  2618       //
  2619       // If the document's original URI (before any push/replaceStates) matches
  2620       // our principal, then we use the document's current URI (after
  2621       // push/replaceStates).  Otherwise (if the document is, say, a data:
  2622       // URI), we just use the principal's URI.
  2624       nsCOMPtr<nsIURI> principalURI;
  2625       mPrincipal->GetURI(getter_AddRefs(principalURI));
  2627       nsIScriptContext* sc = GetContextForEventHandlers(&rv);
  2628       NS_ENSURE_SUCCESS(rv, rv);
  2629       nsCOMPtr<nsIDocument> doc =
  2630         nsContentUtils::GetDocumentFromScriptContext(sc);
  2632       nsCOMPtr<nsIURI> docCurURI;
  2633       nsCOMPtr<nsIURI> docOrigURI;
  2634       if (doc) {
  2635         docCurURI = doc->GetDocumentURI();
  2636         docOrigURI = doc->GetOriginalURI();
  2639       nsCOMPtr<nsIURI> referrerURI;
  2641       if (principalURI && docCurURI && docOrigURI) {
  2642         bool equal = false;
  2643         principalURI->Equals(docOrigURI, &equal);
  2644         if (equal) {
  2645           referrerURI = docCurURI;
  2649       if (!referrerURI)
  2650         referrerURI = principalURI;
  2652       httpChannel->SetReferrer(referrerURI);
  2655     // Some extensions override the http protocol handler and provide their own
  2656     // implementation. The channels returned from that implementation doesn't
  2657     // seem to always implement the nsIUploadChannel2 interface, presumably
  2658     // because it's a new interface.
  2659     // Eventually we should remove this and simply require that http channels
  2660     // implement the new interface.
  2661     // See bug 529041
  2662     nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
  2663       do_QueryInterface(httpChannel);
  2664     if (!uploadChannel2) {
  2665       nsCOMPtr<nsIConsoleService> consoleService =
  2666         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
  2667       if (consoleService) {
  2668         consoleService->LogStringMessage(NS_LITERAL_STRING(
  2669           "Http channel implementation doesn't support nsIUploadChannel2. An extension has supplied a non-functional http protocol handler. This will break behavior and in future releases not work at all."
  2670                                                            ).get());
  2675   mUploadTransferred = 0;
  2676   mUploadTotal = 0;
  2677   // By default we don't have any upload, so mark upload complete.
  2678   mUploadComplete = true;
  2679   mErrorLoad = false;
  2680   mLoadLengthComputable = false;
  2681   mLoadTotal = 0;
  2682   if ((aVariant || !aBody.IsNull()) && httpChannel &&
  2683       !method.LowerCaseEqualsLiteral("get") &&
  2684       !method.LowerCaseEqualsLiteral("head")) {
  2686     nsAutoCString charset;
  2687     nsAutoCString defaultContentType;
  2688     nsCOMPtr<nsIInputStream> postDataStream;
  2690     rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream),
  2691                         &mUploadTotal, defaultContentType, charset);
  2692     NS_ENSURE_SUCCESS(rv, rv);
  2694     if (postDataStream) {
  2695       // If no content type header was set by the client, we set it to
  2696       // application/xml.
  2697       nsAutoCString contentType;
  2698       if (NS_FAILED(httpChannel->
  2699                       GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
  2700                                        contentType)) ||
  2701           contentType.IsEmpty()) {
  2702         contentType = defaultContentType;
  2705       // We don't want to set a charset for streams.
  2706       if (!charset.IsEmpty()) {
  2707         nsAutoCString specifiedCharset;
  2708         bool haveCharset;
  2709         int32_t charsetStart, charsetEnd;
  2710         rv = NS_ExtractCharsetFromContentType(contentType, specifiedCharset,
  2711                                               &haveCharset, &charsetStart,
  2712                                               &charsetEnd);
  2713         if (NS_SUCCEEDED(rv)) {
  2714           // special case: the extracted charset is quoted with single quotes
  2715           // -- for the purpose of preserving what was set we want to handle
  2716           // them as delimiters (although they aren't really)
  2717           if (specifiedCharset.Length() >= 2 &&
  2718               specifiedCharset.First() == '\'' &&
  2719               specifiedCharset.Last() == '\'') {
  2720             specifiedCharset = Substring(specifiedCharset, 1,
  2721                                          specifiedCharset.Length() - 2);
  2724           // If the content-type the page set already has a charset parameter,
  2725           // and it's the same charset, up to case, as |charset|, just send the
  2726           // page-set content-type header.  Apparently at least
  2727           // google-web-toolkit is broken and relies on the exact case of its
  2728           // charset parameter, which makes things break if we use |charset|
  2729           // (which is always a fully resolved charset per our charset alias
  2730           // table, hence might be differently cased).
  2731           if (!specifiedCharset.Equals(charset,
  2732                                        nsCaseInsensitiveCStringComparator())) {
  2733             nsAutoCString newCharset("; charset=");
  2734             newCharset.Append(charset);
  2735             contentType.Replace(charsetStart, charsetEnd - charsetStart,
  2736                                 newCharset);
  2741       // If necessary, wrap the stream in a buffered stream so as to guarantee
  2742       // support for our upload when calling ExplicitSetUploadStream.
  2743       if (!NS_InputStreamIsBuffered(postDataStream)) {
  2744         nsCOMPtr<nsIInputStream> bufferedStream;
  2745         rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
  2746                                        postDataStream, 
  2747                                        4096);
  2748         NS_ENSURE_SUCCESS(rv, rv);
  2750         postDataStream = bufferedStream;
  2753       mUploadComplete = false;
  2755       // We want to use a newer version of the upload channel that won't
  2756       // ignore the necessary headers for an empty Content-Type.
  2757       nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
  2758       // This assertion will fire if buggy extensions are installed
  2759       NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
  2760       if (uploadChannel2) {
  2761           uploadChannel2->ExplicitSetUploadStream(postDataStream, contentType,
  2762                                                  mUploadTotal, method, false);
  2764       else {
  2765         // http channel doesn't support the new nsIUploadChannel2. Emulate
  2766         // as best we can using nsIUploadChannel
  2767         if (contentType.IsEmpty()) {
  2768           contentType.AssignLiteral("application/octet-stream");
  2770         nsCOMPtr<nsIUploadChannel> uploadChannel =
  2771           do_QueryInterface(httpChannel);
  2772         uploadChannel->SetUploadStream(postDataStream, contentType, mUploadTotal);
  2773         // Reset the method to its original value
  2774         httpChannel->SetRequestMethod(method);
  2779   if (httpChannel) {
  2780     nsAutoCString contentTypeHeader;
  2781     rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
  2782                                        contentTypeHeader);
  2783     if (NS_SUCCEEDED(rv)) {
  2784       nsAutoCString contentType, charset;
  2785       rv = NS_ParseContentType(contentTypeHeader, contentType, charset);
  2786       NS_ENSURE_SUCCESS(rv, rv);
  2788       if (!contentType.LowerCaseEqualsLiteral("text/plain") &&
  2789           !contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") &&
  2790           !contentType.LowerCaseEqualsLiteral("multipart/form-data")) {
  2791         mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
  2796   ResetResponse();
  2798   rv = CheckChannelForCrossSiteRequest(mChannel);
  2799   NS_ENSURE_SUCCESS(rv, rv);
  2801   bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
  2803   // Hook us up to listen to redirects and the like
  2804   mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
  2805   mChannel->SetNotificationCallbacks(this);
  2807   // Blocking gets are common enough out of XHR that we should mark
  2808   // the channel slow by default for pipeline purposes
  2809   AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
  2811   nsCOMPtr<nsIHttpChannelInternal>
  2812     internalHttpChannel(do_QueryInterface(mChannel));
  2813   if (internalHttpChannel) {
  2814     // we never let XHR be blocked by head CSS/JS loads to avoid
  2815     // potential deadlock where server generation of CSS/JS requires
  2816     // an XHR signal.
  2817     internalHttpChannel->SetLoadUnblocked(true);
  2819     // Disable Necko-internal response timeouts.
  2820     internalHttpChannel->SetResponseTimeoutEnabled(false);
  2823   nsCOMPtr<nsIStreamListener> listener = this;
  2824   if (!IsSystemXHR()) {
  2825     // Always create a nsCORSListenerProxy here even if it's
  2826     // a same-origin request right now, since it could be redirected.
  2827     nsRefPtr<nsCORSListenerProxy> corsListener =
  2828       new nsCORSListenerProxy(listener, mPrincipal, withCredentials);
  2829     rv = corsListener->Init(mChannel, true);
  2830     NS_ENSURE_SUCCESS(rv, rv);
  2831     listener = corsListener;
  2833   else {
  2834     // Because of bug 682305, we can't let listener be the XHR object itself
  2835     // because JS wouldn't be able to use it. So if we haven't otherwise
  2836     // created a listener around 'this', do so now.
  2838     listener = new nsStreamListenerWrapper(listener);
  2841   if (mIsAnon) {
  2842     AddLoadFlags(mChannel, nsIRequest::LOAD_ANONYMOUS);
  2844   else {
  2845     AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
  2848   NS_ASSERTION(listener != this,
  2849                "Using an object as a listener that can't be exposed to JS");
  2851   // Bypass the network cache in cases where it makes no sense:
  2852   // POST responses are always unique, and we provide no API that would
  2853   // allow our consumers to specify a "cache key" to access old POST
  2854   // responses, so they are not worth caching.
  2855   if (method.EqualsLiteral("POST")) {
  2856     AddLoadFlags(mChannel,
  2857         nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
  2859   // When we are sync loading, we need to bypass the local cache when it would
  2860   // otherwise block us waiting for exclusive access to the cache.  If we don't
  2861   // do this, then we could dead lock in some cases (see bug 309424).
  2862   else if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
  2863     AddLoadFlags(mChannel,
  2864         nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
  2867   // Since we expect XML data, set the type hint accordingly
  2868   // if the channel doesn't know any content type.
  2869   // This means that we always try to parse local files as XML
  2870   // ignoring return value, as this is not critical
  2871   nsAutoCString contentType;
  2872   if (NS_FAILED(mChannel->GetContentType(contentType)) ||
  2873       contentType.IsEmpty() ||
  2874       contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
  2875     mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
  2878   // We're about to send the request.  Start our timeout.
  2879   mRequestSentTime = PR_Now();
  2880   StartTimeoutTimer();
  2882   // Set up the preflight if needed
  2883   if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
  2884     // Check to see if this initial OPTIONS request has already been cached
  2885     // in our special Access Control Cache.
  2887     rv = NS_StartCORSPreflight(mChannel, listener,
  2888                                mPrincipal, withCredentials,
  2889                                mCORSUnsafeHeaders,
  2890                                getter_AddRefs(mCORSPreflightChannel));
  2891     NS_ENSURE_SUCCESS(rv, rv);
  2893   else {
  2894     // Start reading from the channel
  2895     rv = mChannel->AsyncOpen(listener, nullptr);
  2898   if (NS_FAILED(rv)) {
  2899     // Drop our ref to the channel to avoid cycles
  2900     mChannel = nullptr;
  2901     mCORSPreflightChannel = nullptr;
  2902     return rv;
  2905   // Either AsyncOpen was called, or CORS will open the channel later.
  2906   mWaitingForOnStopRequest = true;
  2908   // If we're synchronous, spin an event loop here and wait
  2909   if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
  2910     mState |= XML_HTTP_REQUEST_SYNCLOOPING;
  2912     nsCOMPtr<nsIDocument> suspendedDoc;
  2913     nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
  2914     if (GetOwner()) {
  2915       nsCOMPtr<nsIDOMWindow> topWindow;
  2916       if (NS_SUCCEEDED(GetOwner()->GetTop(getter_AddRefs(topWindow)))) {
  2917         nsCOMPtr<nsPIDOMWindow> suspendedWindow(do_QueryInterface(topWindow));
  2918         if (suspendedWindow &&
  2919             (suspendedWindow = suspendedWindow->GetCurrentInnerWindow())) {
  2920           suspendedDoc = suspendedWindow->GetExtantDoc();
  2921           if (suspendedDoc) {
  2922             suspendedDoc->SuppressEventHandling(nsIDocument::eEvents);
  2924           suspendedWindow->SuspendTimeouts(1, false);
  2925           resumeTimeoutRunnable = new nsResumeTimeoutsEvent(suspendedWindow);
  2930     ChangeState(XML_HTTP_REQUEST_SENT);
  2933       nsAutoSyncOperation sync(suspendedDoc);
  2934       // Note, calling ChangeState may have cleared
  2935       // XML_HTTP_REQUEST_SYNCLOOPING flag.
  2936       nsIThread *thread = NS_GetCurrentThread();
  2937       while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
  2938         if (!NS_ProcessNextEvent(thread)) {
  2939           rv = NS_ERROR_UNEXPECTED;
  2940           break;
  2945     if (suspendedDoc) {
  2946       suspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents,
  2947                                                          true);
  2950     if (resumeTimeoutRunnable) {
  2951       NS_DispatchToCurrentThread(resumeTimeoutRunnable);
  2953   } else {
  2954     // Now that we've successfully opened the channel, we can change state.  Note
  2955     // that this needs to come after the AsyncOpen() and rv check, because this
  2956     // can run script that would try to restart this request, and that could end
  2957     // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
  2958     ChangeState(XML_HTTP_REQUEST_SENT);
  2959     if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
  2960       StartProgressEventTimer();
  2962     DispatchProgressEvent(this, NS_LITERAL_STRING(LOADSTART_STR), false,
  2963                           0, 0);
  2964     if (mUpload && !mUploadComplete) {
  2965       DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOADSTART_STR), true,
  2966                             0, mUploadTotal);
  2970   if (!mChannel) {
  2971     return NS_ERROR_FAILURE;
  2974   return rv;
  2977 /* void setRequestHeader (in ByteString header, in ByteString value); */
  2978 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
  2979 NS_IMETHODIMP
  2980 nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
  2981                                    const nsACString& value)
  2983   // Step 1 and 2
  2984   if (!(mState & XML_HTTP_REQUEST_OPENED)) {
  2985     return NS_ERROR_DOM_INVALID_STATE_ERR;
  2987   NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED.");
  2989   // Step 3
  2990   // Make sure we don't store an invalid header name in mCORSUnsafeHeaders
  2991   if (!IsValidHTTPToken(header)) { // XXX nsHttp::IsValidToken?
  2992     return NS_ERROR_DOM_SYNTAX_ERR;
  2995   // Check that we haven't already opened the channel. We can't rely on
  2996   // the channel throwing from mChannel->SetRequestHeader since we might
  2997   // still be waiting for mCORSPreflightChannel to actually open mChannel
  2998   if (mCORSPreflightChannel) {
  2999     bool pending;
  3000     nsresult rv = mCORSPreflightChannel->IsPending(&pending);
  3001     NS_ENSURE_SUCCESS(rv, rv);
  3003     if (pending) {
  3004       return NS_ERROR_IN_PROGRESS;
  3008   if (!mChannel)             // open() initializes mChannel, and open()
  3009     return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
  3011   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
  3012   if (!httpChannel) {
  3013     return NS_OK;
  3016   // We will merge XHR headers, per the spec (secion 4.6.2) unless:
  3017   // 1 - The caller is privileged and setting an invalid header,
  3018   // or
  3019   // 2 - we have not yet explicitly set that header; this allows web
  3020   //     content to override default headers the first time they set them.
  3021   bool mergeHeaders = true;
  3023   // Prevent modification to certain HTTP headers (see bug 302263), unless
  3024   // the executing script is privileged.
  3025   bool isInvalidHeader = false;
  3026   static const char *kInvalidHeaders[] = {
  3027     "accept-charset", "accept-encoding", "access-control-request-headers",
  3028     "access-control-request-method", "connection", "content-length",
  3029     "cookie", "cookie2", "content-transfer-encoding", "date", "dnt",
  3030     "expect", "host", "keep-alive", "origin", "referer", "te", "trailer",
  3031     "transfer-encoding", "upgrade", "user-agent", "via"
  3032   };
  3033   uint32_t i;
  3034   for (i = 0; i < ArrayLength(kInvalidHeaders); ++i) {
  3035     if (header.LowerCaseEqualsASCII(kInvalidHeaders[i])) {
  3036       isInvalidHeader = true;
  3037       break;
  3041   if (!IsSystemXHR()) {
  3042     // Step 5: Check for dangerous headers.
  3043     if (isInvalidHeader) {
  3044       NS_WARNING("refusing to set request header");
  3045       return NS_OK;
  3047     if (StringBeginsWith(header, NS_LITERAL_CSTRING("proxy-"),
  3048                          nsCaseInsensitiveCStringComparator()) ||
  3049         StringBeginsWith(header, NS_LITERAL_CSTRING("sec-"),
  3050                          nsCaseInsensitiveCStringComparator())) {
  3051       NS_WARNING("refusing to set request header");
  3052       return NS_OK;
  3055     // Check for dangerous cross-site headers
  3056     bool safeHeader = IsSystemXHR();
  3057     if (!safeHeader) {
  3058       // Content-Type isn't always safe, but we'll deal with it in Send()
  3059       const char *kCrossOriginSafeHeaders[] = {
  3060         "accept", "accept-language", "content-language", "content-type",
  3061         "last-event-id"
  3062       };
  3063       for (i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
  3064         if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
  3065           safeHeader = true;
  3066           break;
  3071     if (!safeHeader) {
  3072       if (!mCORSUnsafeHeaders.Contains(header)) {
  3073         mCORSUnsafeHeaders.AppendElement(header);
  3076   } else {
  3077     // Case 1 above
  3078     if (isInvalidHeader) {
  3079       mergeHeaders = false;
  3083   if (!mAlreadySetHeaders.Contains(header)) {
  3084     // Case 2 above
  3085     mergeHeaders = false;
  3088   // Merge headers depending on what we decided above.
  3089   nsresult rv = httpChannel->SetRequestHeader(header, value, mergeHeaders);
  3090   if (rv == NS_ERROR_INVALID_ARG) {
  3091     return NS_ERROR_DOM_SYNTAX_ERR;
  3093   if (NS_SUCCEEDED(rv)) {
  3094     // Remember that we've set this header, so subsequent set operations will merge values.
  3095     mAlreadySetHeaders.PutEntry(nsCString(header));
  3097     // We'll want to duplicate this header for any replacement channels (eg. on redirect)
  3098     RequestHeader reqHeader = {
  3099       nsCString(header), nsCString(value)
  3100     };
  3101     mModifiedRequestHeaders.AppendElement(reqHeader);
  3103   return rv;
  3106 /* attribute unsigned long timeout; */
  3107 NS_IMETHODIMP
  3108 nsXMLHttpRequest::GetTimeout(uint32_t *aTimeout)
  3110   *aTimeout = Timeout();
  3111   return NS_OK;
  3114 NS_IMETHODIMP
  3115 nsXMLHttpRequest::SetTimeout(uint32_t aTimeout)
  3117   ErrorResult rv;
  3118   SetTimeout(aTimeout, rv);
  3119   return rv.ErrorCode();
  3122 void
  3123 nsXMLHttpRequest::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
  3125   if (!(mState & (XML_HTTP_REQUEST_ASYNC | XML_HTTP_REQUEST_UNSENT)) &&
  3126       HasOrHasHadOwner()) {
  3127     /* Timeout is not supported for synchronous requests with an owning window,
  3128        per XHR2 spec. */
  3129     LogMessage("TimeoutSyncXHRWarning", GetOwner());
  3130     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
  3131     return;
  3134   mTimeoutMilliseconds = aTimeout;
  3135   if (mRequestSentTime) {
  3136     StartTimeoutTimer();
  3140 void
  3141 nsXMLHttpRequest::StartTimeoutTimer()
  3143   NS_ABORT_IF_FALSE(mRequestSentTime,
  3144                     "StartTimeoutTimer mustn't be called before the request was sent!");
  3145   if (mState & XML_HTTP_REQUEST_DONE) {
  3146     // do nothing!
  3147     return;
  3150   if (mTimeoutTimer) {
  3151     mTimeoutTimer->Cancel();
  3154   if (!mTimeoutMilliseconds) {
  3155     return;
  3158   if (!mTimeoutTimer) {
  3159     mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
  3161   uint32_t elapsed =
  3162     (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
  3163   mTimeoutTimer->InitWithCallback(
  3164     this,
  3165     mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
  3166     nsITimer::TYPE_ONE_SHOT
  3167   );
  3170 /* readonly attribute unsigned short readyState; */
  3171 NS_IMETHODIMP
  3172 nsXMLHttpRequest::GetReadyState(uint16_t *aState)
  3174   *aState = ReadyState();
  3175   return NS_OK;
  3178 uint16_t
  3179 nsXMLHttpRequest::ReadyState()
  3181   // Translate some of our internal states for external consumers
  3182   if (mState & XML_HTTP_REQUEST_UNSENT) {
  3183     return UNSENT;
  3185   if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
  3186     return OPENED;
  3188   if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
  3189     return HEADERS_RECEIVED;
  3191   if (mState & XML_HTTP_REQUEST_LOADING) {
  3192     return LOADING;
  3194   MOZ_ASSERT(mState & XML_HTTP_REQUEST_DONE);
  3195   return DONE;
  3198 /* void overrideMimeType(in DOMString mimetype); */
  3199 NS_IMETHODIMP
  3200 nsXMLHttpRequest::SlowOverrideMimeType(const nsAString& aMimeType)
  3202   OverrideMimeType(aMimeType);
  3203   return NS_OK;
  3206 /* attribute boolean mozBackgroundRequest; */
  3207 NS_IMETHODIMP
  3208 nsXMLHttpRequest::GetMozBackgroundRequest(bool *_retval)
  3210   *_retval = MozBackgroundRequest();
  3211   return NS_OK;
  3214 bool
  3215 nsXMLHttpRequest::MozBackgroundRequest()
  3217   return !!(mState & XML_HTTP_REQUEST_BACKGROUND);
  3220 NS_IMETHODIMP
  3221 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest)
  3223   nsresult rv = NS_OK;
  3224   SetMozBackgroundRequest(aMozBackgroundRequest, rv);
  3225   return rv;
  3228 void
  3229 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest, nsresult& aRv)
  3231   if (!IsSystemXHR()) {
  3232     aRv = NS_ERROR_DOM_SECURITY_ERR;
  3233     return;
  3236   if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
  3237     // Can't change this while we're in the middle of something.
  3238     aRv = NS_ERROR_IN_PROGRESS;
  3239     return;
  3242   if (aMozBackgroundRequest) {
  3243     mState |= XML_HTTP_REQUEST_BACKGROUND;
  3244   } else {
  3245     mState &= ~XML_HTTP_REQUEST_BACKGROUND;
  3249 /* attribute boolean withCredentials; */
  3250 NS_IMETHODIMP
  3251 nsXMLHttpRequest::GetWithCredentials(bool *_retval)
  3253   *_retval = WithCredentials();
  3254   return NS_OK;
  3257 bool
  3258 nsXMLHttpRequest::WithCredentials()
  3260   return !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
  3263 NS_IMETHODIMP
  3264 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials)
  3266   ErrorResult rv;
  3267   SetWithCredentials(aWithCredentials, rv);
  3268   return rv.ErrorCode();
  3271 void
  3272 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
  3274   // Return error if we're already processing a request
  3275   if (XML_HTTP_REQUEST_SENT & mState) {
  3276     aRv = NS_ERROR_FAILURE;
  3277     return;
  3280   // sync request is not allowed setting withCredentials in window context
  3281   if (HasOrHasHadOwner() &&
  3282       !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
  3283     LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
  3284     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
  3285     return;
  3288   if (aWithCredentials) {
  3289     mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
  3290   } else {
  3291     mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
  3295 nsresult
  3296 nsXMLHttpRequest::ChangeState(uint32_t aState, bool aBroadcast)
  3298   // If we are setting one of the mutually exclusive states,
  3299   // unset those state bits first.
  3300   if (aState & XML_HTTP_REQUEST_LOADSTATES) {
  3301     mState &= ~XML_HTTP_REQUEST_LOADSTATES;
  3303   mState |= aState;
  3304   nsresult rv = NS_OK;
  3306   if (mProgressNotifier &&
  3307       !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
  3308     mProgressTimerIsActive = false;
  3309     mProgressNotifier->Cancel();
  3312   if ((aState & XML_HTTP_REQUEST_LOADSTATES) &&  // Broadcast load states only
  3313       aState != XML_HTTP_REQUEST_SENT && // And not internal ones
  3314       aBroadcast &&
  3315       (mState & XML_HTTP_REQUEST_ASYNC ||
  3316        aState & XML_HTTP_REQUEST_OPENED ||
  3317        aState & XML_HTTP_REQUEST_DONE)) {
  3318     nsCOMPtr<nsIDOMEvent> event;
  3319     rv = CreateReadystatechangeEvent(getter_AddRefs(event));
  3320     NS_ENSURE_SUCCESS(rv, rv);
  3322     DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  3325   return rv;
  3328 /*
  3329  * Simple helper class that just forwards the redirect callback back
  3330  * to the nsXMLHttpRequest.
  3331  */
  3332 class AsyncVerifyRedirectCallbackForwarder MOZ_FINAL : public nsIAsyncVerifyRedirectCallback
  3334 public:
  3335   AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest *xhr)
  3336     : mXHR(xhr)
  3340   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  3341   NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
  3343   // nsIAsyncVerifyRedirectCallback implementation
  3344   NS_IMETHOD OnRedirectVerifyCallback(nsresult result)
  3346     mXHR->OnRedirectVerifyCallback(result);
  3348     return NS_OK;
  3351 private:
  3352   nsRefPtr<nsXMLHttpRequest> mXHR;
  3353 };
  3355 NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder, mXHR)
  3357 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
  3358   NS_INTERFACE_MAP_ENTRY(nsISupports)
  3359   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
  3360 NS_INTERFACE_MAP_END
  3362 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
  3363 NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)
  3366 /////////////////////////////////////////////////////
  3367 // nsIChannelEventSink methods:
  3368 //
  3369 NS_IMETHODIMP
  3370 nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
  3371                                          nsIChannel *aNewChannel,
  3372                                          uint32_t    aFlags,
  3373                                          nsIAsyncVerifyRedirectCallback *callback)
  3375   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
  3377   nsresult rv;
  3379   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
  3380     rv = CheckChannelForCrossSiteRequest(aNewChannel);
  3381     if (NS_FAILED(rv)) {
  3382       NS_WARNING("nsXMLHttpRequest::OnChannelRedirect: "
  3383                  "CheckChannelForCrossSiteRequest returned failure");
  3384       return rv;
  3387     // Disable redirects for preflighted cross-site requests entirely for now
  3388     // Note, do this after the call to CheckChannelForCrossSiteRequest
  3389     // to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
  3390     if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
  3391        return NS_ERROR_DOM_BAD_URI;
  3395   // Prepare to receive callback
  3396   mRedirectCallback = callback;
  3397   mNewRedirectChannel = aNewChannel;
  3399   if (mChannelEventSink) {
  3400     nsRefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
  3401       new AsyncVerifyRedirectCallbackForwarder(this);
  3403     rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
  3404                                                    aNewChannel,
  3405                                                    aFlags, fwd);
  3406     if (NS_FAILED(rv)) {
  3407         mRedirectCallback = nullptr;
  3408         mNewRedirectChannel = nullptr;
  3410     return rv;
  3412   OnRedirectVerifyCallback(NS_OK);
  3413   return NS_OK;
  3416 void
  3417 nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
  3419   NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
  3420   NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
  3422   if (NS_SUCCEEDED(result)) {
  3423     mChannel = mNewRedirectChannel;
  3425     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
  3426     if (httpChannel) {
  3427       // Ensure all original headers are duplicated for the new channel (bug #553888)
  3428       for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) {
  3429         --i;
  3430         httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
  3431                                       mModifiedRequestHeaders[i].value,
  3432                                       false);
  3435   } else {
  3436     mErrorLoad = true;
  3439   mNewRedirectChannel = nullptr;
  3441   mRedirectCallback->OnRedirectVerifyCallback(result);
  3442   mRedirectCallback = nullptr;
  3445 /////////////////////////////////////////////////////
  3446 // nsIProgressEventSink methods:
  3447 //
  3449 void
  3450 nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
  3452   if (aFinalProgress && mProgressTimerIsActive) {
  3453     mProgressTimerIsActive = false;
  3454     mProgressNotifier->Cancel();
  3457   if (mProgressTimerIsActive ||
  3458       !mProgressSinceLastProgressEvent ||
  3459       mErrorLoad ||
  3460       !(mState & XML_HTTP_REQUEST_ASYNC)) {
  3461     return;
  3464   if (!aFinalProgress) {
  3465     StartProgressEventTimer();
  3468   // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
  3469   // XML_HTTP_REQUEST_SENT
  3470   if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
  3471     if (mUpload && !mUploadComplete) {
  3472       DispatchProgressEvent(mUpload, NS_LITERAL_STRING(PROGRESS_STR),
  3473                             mUploadLengthComputable, mUploadTransferred,
  3474                             mUploadTotal);
  3476   } else {
  3477     if (aFinalProgress) {
  3478       mLoadTotal = mLoadTransferred;
  3480     mInLoadProgressEvent = true;
  3481     DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR),
  3482                           mLoadLengthComputable, mLoadTransferred,
  3483                           mLoadTotal);
  3484     mInLoadProgressEvent = false;
  3485     if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
  3486         mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
  3487       mResponseBody.Truncate();
  3488       mResponseText.Truncate();
  3489       mResultArrayBuffer = nullptr;
  3490       mArrayBufferBuilder.reset();
  3494   mProgressSinceLastProgressEvent = false;
  3497 NS_IMETHODIMP
  3498 nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, uint64_t aProgress, uint64_t aProgressMax)
  3500   // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
  3501   // XML_HTTP_REQUEST_SENT
  3502   bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
  3503   // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
  3504   // So, try to remove the headers, if possible.
  3505   bool lengthComputable = (aProgressMax != UINT64_MAX);
  3506   if (upload) {
  3507     uint64_t loaded = aProgress;
  3508     uint64_t total = aProgressMax;
  3509     if (lengthComputable) {
  3510       uint64_t headerSize = aProgressMax - mUploadTotal;
  3511       loaded -= headerSize;
  3512       total -= headerSize;
  3514     mUploadLengthComputable = lengthComputable;
  3515     mUploadTransferred = loaded;
  3516     mProgressSinceLastProgressEvent = true;
  3518     MaybeDispatchProgressEvents(false);
  3519   } else {
  3520     mLoadLengthComputable = lengthComputable;
  3521     mLoadTotal = lengthComputable ? aProgressMax : 0;
  3523     // Don't dispatch progress events here. OnDataAvailable will take care
  3524     // of that.
  3527   if (mProgressEventSink) {
  3528     mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
  3529                                    aProgressMax);
  3532   return NS_OK;
  3535 NS_IMETHODIMP
  3536 nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatus, const char16_t *aStatusArg)
  3538   if (mProgressEventSink) {
  3539     mProgressEventSink->OnStatus(aRequest, aContext, aStatus, aStatusArg);
  3542   return NS_OK;
  3545 bool
  3546 nsXMLHttpRequest::AllowUploadProgress()
  3548   return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
  3549     (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
  3552 /////////////////////////////////////////////////////
  3553 // nsIInterfaceRequestor methods:
  3554 //
  3555 NS_IMETHODIMP
  3556 nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
  3558   nsresult rv;
  3560   // Make sure to return ourselves for the channel event sink interface and
  3561   // progress event sink interface, no matter what.  We can forward these to
  3562   // mNotificationCallbacks if it wants to get notifications for them.  But we
  3563   // need to see these notifications for proper functioning.
  3564   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
  3565     mChannelEventSink = do_GetInterface(mNotificationCallbacks);
  3566     *aResult = static_cast<nsIChannelEventSink*>(EnsureXPCOMifier().take());
  3567     return NS_OK;
  3568   } else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
  3569     mProgressEventSink = do_GetInterface(mNotificationCallbacks);
  3570     *aResult = static_cast<nsIProgressEventSink*>(EnsureXPCOMifier().take());
  3571     return NS_OK;
  3574   // Now give mNotificationCallbacks (if non-null) a chance to return the
  3575   // desired interface.
  3576   if (mNotificationCallbacks) {
  3577     rv = mNotificationCallbacks->GetInterface(aIID, aResult);
  3578     if (NS_SUCCEEDED(rv)) {
  3579       NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
  3580       return rv;
  3584   if (mState & XML_HTTP_REQUEST_BACKGROUND) {
  3585     nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
  3587     // Ignore failure to get component, we may not have all its dependencies
  3588     // available
  3589     if (NS_SUCCEEDED(rv)) {
  3590       rv = badCertHandler->GetInterface(aIID, aResult);
  3591       if (NS_SUCCEEDED(rv))
  3592         return rv;
  3595   else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
  3596            aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
  3598     nsCOMPtr<nsIURI> uri;
  3599     rv = mChannel->GetURI(getter_AddRefs(uri));
  3600     NS_ENSURE_SUCCESS(rv, rv);
  3602     // Verify that it's ok to prompt for credentials here, per spec
  3603     // http://xhr.spec.whatwg.org/#the-send%28%29-method
  3604     bool showPrompt = true;
  3606     // If authentication fails, XMLHttpRequest origin and
  3607     // the request URL are same origin, ...
  3608     /* Disabled - bug: 799540
  3609     if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
  3610       showPrompt = false;
  3612     */
  3614     // ... Authorization is not in the list of author request headers, ...
  3615     if (showPrompt) {
  3616       for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
  3617         if (mModifiedRequestHeaders[i].header.
  3618               LowerCaseEqualsLiteral("authorization")) {
  3619           showPrompt = false;
  3620           break;
  3625     // ... request username is null, and request password is null,
  3626     if (showPrompt) {
  3628       nsCString username;
  3629       rv = uri->GetUsername(username);
  3630       NS_ENSURE_SUCCESS(rv, rv);
  3632       nsCString password;
  3633       rv = uri->GetPassword(password);
  3634       NS_ENSURE_SUCCESS(rv, rv);
  3636       if (!username.IsEmpty() || !password.IsEmpty()) {
  3637         showPrompt = false;
  3641     // ... user agents should prompt the end user for their username and password.
  3642     if (!showPrompt) {
  3643       nsRefPtr<XMLHttpRequestAuthPrompt> prompt = new XMLHttpRequestAuthPrompt();
  3644       if (!prompt)
  3645         return NS_ERROR_OUT_OF_MEMORY;
  3647       return prompt->QueryInterface(aIID, aResult);
  3650     nsCOMPtr<nsIPromptFactory> wwatch =
  3651       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
  3652     NS_ENSURE_SUCCESS(rv, rv);
  3654     // Get the an auth prompter for our window so that the parenting
  3655     // of the dialogs works as it should when using tabs.
  3657     nsCOMPtr<nsIDOMWindow> window;
  3658     if (GetOwner()) {
  3659       window = GetOwner()->GetOuterWindow();
  3662     return wwatch->GetPrompt(window, aIID,
  3663                              reinterpret_cast<void**>(aResult));
  3665   // Now check for the various XHR non-DOM interfaces, except
  3666   // nsIProgressEventSink and nsIChannelEventSink which we already
  3667   // handled above.
  3668   else if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
  3669     *aResult = static_cast<nsIStreamListener*>(EnsureXPCOMifier().take());
  3670     return NS_OK;
  3672   else if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
  3673     *aResult = static_cast<nsIRequestObserver*>(EnsureXPCOMifier().take());
  3674     return NS_OK;
  3676   else if (aIID.Equals(NS_GET_IID(nsITimerCallback))) {
  3677     *aResult = static_cast<nsITimerCallback*>(EnsureXPCOMifier().take());
  3678     return NS_OK;
  3681   return QueryInterface(aIID, aResult);
  3684 void
  3685 nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID,
  3686                                JS::MutableHandle<JS::Value> aRetval,
  3687                                ErrorResult& aRv)
  3689   dom::GetInterface(aCx, this, aIID, aRetval, aRv);
  3692 nsXMLHttpRequestUpload*
  3693 nsXMLHttpRequest::Upload()
  3695   if (!mUpload) {
  3696     mUpload = new nsXMLHttpRequestUpload(this);
  3698   return mUpload;
  3701 NS_IMETHODIMP
  3702 nsXMLHttpRequest::GetUpload(nsIXMLHttpRequestUpload** aUpload)
  3704   nsRefPtr<nsXMLHttpRequestUpload> upload = Upload();
  3705   upload.forget(aUpload);
  3706   return NS_OK;
  3709 bool
  3710 nsXMLHttpRequest::MozAnon()
  3712   return mIsAnon;
  3715 NS_IMETHODIMP
  3716 nsXMLHttpRequest::GetMozAnon(bool* aAnon)
  3718   *aAnon = MozAnon();
  3719   return NS_OK;
  3722 bool
  3723 nsXMLHttpRequest::MozSystem()
  3725   return IsSystemXHR();
  3728 NS_IMETHODIMP
  3729 nsXMLHttpRequest::GetMozSystem(bool* aSystem)
  3731   *aSystem = MozSystem();
  3732   return NS_OK;
  3735 void
  3736 nsXMLHttpRequest::HandleTimeoutCallback()
  3738   if (mState & XML_HTTP_REQUEST_DONE) {
  3739     NS_NOTREACHED("nsXMLHttpRequest::HandleTimeoutCallback with completed request");
  3740     // do nothing!
  3741     return;
  3744   CloseRequestWithError(NS_LITERAL_STRING(TIMEOUT_STR),
  3745                         XML_HTTP_REQUEST_TIMED_OUT);
  3748 NS_IMETHODIMP
  3749 nsXMLHttpRequest::Notify(nsITimer* aTimer)
  3751   if (mProgressNotifier == aTimer) {
  3752     HandleProgressTimerCallback();
  3753     return NS_OK;
  3756   if (mTimeoutTimer == aTimer) {
  3757     HandleTimeoutCallback();
  3758     return NS_OK;
  3761   // Just in case some JS user wants to QI to nsITimerCallback and play with us...
  3762   NS_WARNING("Unexpected timer!");
  3763   return NS_ERROR_INVALID_POINTER;
  3766 void
  3767 nsXMLHttpRequest::HandleProgressTimerCallback()
  3769   mProgressTimerIsActive = false;
  3770   MaybeDispatchProgressEvents(false);
  3773 void
  3774 nsXMLHttpRequest::StartProgressEventTimer()
  3776   if (!mProgressNotifier) {
  3777     mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
  3779   if (mProgressNotifier) {
  3780     mProgressTimerIsActive = true;
  3781     mProgressNotifier->Cancel();
  3782     mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
  3783                                         nsITimer::TYPE_ONE_SHOT);
  3787 already_AddRefed<nsXMLHttpRequestXPCOMifier>
  3788 nsXMLHttpRequest::EnsureXPCOMifier()
  3790   if (!mXPCOMifier) {
  3791     mXPCOMifier = new nsXMLHttpRequestXPCOMifier(this);
  3793   nsRefPtr<nsXMLHttpRequestXPCOMifier> newRef(mXPCOMifier);
  3794   return newRef.forget();
  3797 NS_IMPL_ISUPPORTS(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
  3799 NS_IMETHODIMP nsXMLHttpRequest::
  3800 nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value)
  3802   if (mXHR->IsSafeHeader(header, mHttpChannel)) {
  3803     mHeaders.Append(header);
  3804     mHeaders.Append(": ");
  3805     mHeaders.Append(value);
  3806     mHeaders.Append("\r\n");
  3808   return NS_OK;
  3811 // nsXMLHttpRequestXPCOMifier implementation
  3812 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
  3813   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
  3814   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
  3815   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
  3816   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
  3817   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
  3818   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
  3819   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
  3820 NS_INTERFACE_MAP_END
  3822 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
  3823 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
  3825 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
  3826 // inheritance from nsISupports.
  3827 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
  3829 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier)
  3830 if (tmp->mXHR) {
  3831   tmp->mXHR->mXPCOMifier = nullptr;
  3833 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR)
  3834 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  3836 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
  3837 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
  3838 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  3840 NS_IMETHODIMP
  3841 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID & aIID, void **aResult)
  3843   // Return ourselves for the things we implement (except
  3844   // nsIInterfaceRequestor) and the XHR for the rest.
  3845   if (!aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
  3846     nsresult rv = QueryInterface(aIID, aResult);
  3847     if (NS_SUCCEEDED(rv)) {
  3848       return rv;
  3852   return mXHR->GetInterface(aIID, aResult);
  3855 namespace mozilla {
  3857 ArrayBufferBuilder::ArrayBufferBuilder()
  3858   : mDataPtr(nullptr),
  3859     mCapacity(0),
  3860     mLength(0)
  3864 ArrayBufferBuilder::~ArrayBufferBuilder()
  3866   reset();
  3869 void
  3870 ArrayBufferBuilder::reset()
  3872   if (mDataPtr) {
  3873     JS_free(nullptr, mDataPtr);
  3875   mDataPtr = nullptr;
  3876   mCapacity = mLength = 0;
  3879 bool
  3880 ArrayBufferBuilder::setCapacity(uint32_t aNewCap)
  3882   uint8_t *newdata = (uint8_t *) JS_ReallocateArrayBufferContents(nullptr, aNewCap, mDataPtr, mCapacity);
  3883   if (!newdata) {
  3884     return false;
  3887   mDataPtr = newdata;
  3888   mCapacity = aNewCap;
  3889   if (mLength > aNewCap) {
  3890     mLength = aNewCap;
  3893   return true;
  3896 bool
  3897 ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
  3898                            uint32_t aMaxGrowth)
  3900   if (mLength + aDataLen > mCapacity) {
  3901     uint32_t newcap;
  3902     // Double while under aMaxGrowth or if not specified.
  3903     if (!aMaxGrowth || mCapacity < aMaxGrowth) {
  3904       newcap = mCapacity * 2;
  3905     } else {
  3906       newcap = mCapacity + aMaxGrowth;
  3909     // But make sure there's always enough to satisfy our request.
  3910     if (newcap < mLength + aDataLen) {
  3911       newcap = mLength + aDataLen;
  3914     // Did we overflow?
  3915     if (newcap < mCapacity) {
  3916       return false;
  3919     if (!setCapacity(newcap)) {
  3920       return false;
  3924   // Assert that the region isn't overlapping so we can memcpy.
  3925   MOZ_ASSERT(!areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength,
  3926                                     aDataLen));
  3928   memcpy(mDataPtr + mLength, aNewData, aDataLen);
  3929   mLength += aDataLen;
  3931   return true;
  3934 JSObject*
  3935 ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
  3937   // we need to check for mLength == 0, because nothing may have been
  3938   // added
  3939   if (mCapacity > mLength || mLength == 0) {
  3940     if (!setCapacity(mLength)) {
  3941       return nullptr;
  3945   JSObject* obj = JS_NewArrayBufferWithContents(aCx, mLength, mDataPtr);
  3946   mDataPtr = nullptr;
  3947   mLength = mCapacity = 0;
  3948   if (!obj) {
  3949     js_free(mDataPtr);
  3950     return nullptr;
  3952   return obj;
  3955 /* static */ bool
  3956 ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
  3957                                           uint32_t aLength1,
  3958                                           const uint8_t* aStart2,
  3959                                           uint32_t aLength2)
  3961   const uint8_t* end1 = aStart1 + aLength1;
  3962   const uint8_t* end2 = aStart2 + aLength2;
  3964   const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
  3965   const uint8_t* min_end   = end1 < end2 ? end1 : end2;
  3967   return max_start < min_end;
  3970 } // namespace mozilla

mercurial