Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / nsXMLHttpRequest.cpp
blob766ff232bd6faa2624ad7a31b01044273db74110
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 #ifndef XP_WIN
10 #include <unistd.h>
11 #endif
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/dom/BlobSet.h"
14 #include "mozilla/dom/File.h"
15 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
16 #include "mozilla/EventDispatcher.h"
17 #include "mozilla/EventListenerManager.h"
18 #include "mozilla/LoadInfo.h"
19 #include "mozilla/MemoryReporting.h"
20 #include "nsIDOMDocument.h"
21 #include "mozilla/dom/ProgressEvent.h"
22 #include "nsIJARChannel.h"
23 #include "nsIJARURI.h"
24 #include "nsLayoutCID.h"
25 #include "nsReadableUtils.h"
27 #include "nsIURI.h"
28 #include "nsILoadGroup.h"
29 #include "nsNetUtil.h"
30 #include "nsStreamUtils.h"
31 #include "nsThreadUtils.h"
32 #include "nsIUploadChannel.h"
33 #include "nsIUploadChannel2.h"
34 #include "nsIDOMSerializer.h"
35 #include "nsXPCOM.h"
36 #include "nsIDOMEventListener.h"
37 #include "nsIScriptSecurityManager.h"
38 #include "nsIDOMWindow.h"
39 #include "nsIVariant.h"
40 #include "nsVariant.h"
41 #include "nsIScriptError.h"
42 #include "nsIStreamConverterService.h"
43 #include "nsICachingChannel.h"
44 #include "nsContentUtils.h"
45 #include "nsCycleCollectionParticipant.h"
46 #include "nsIContentPolicy.h"
47 #include "nsContentPolicyUtils.h"
48 #include "nsError.h"
49 #include "nsCORSListenerProxy.h"
50 #include "nsIHTMLDocument.h"
51 #include "nsIStorageStream.h"
52 #include "nsIPromptFactory.h"
53 #include "nsIWindowWatcher.h"
54 #include "nsIConsoleService.h"
55 #include "nsIContentSecurityPolicy.h"
56 #include "nsAsyncRedirectVerifyHelper.h"
57 #include "nsStringBuffer.h"
58 #include "nsIFileChannel.h"
59 #include "mozilla/Telemetry.h"
60 #include "jsfriendapi.h"
61 #include "GeckoProfiler.h"
62 #include "mozilla/dom/EncodingUtils.h"
63 #include "nsIUnicodeDecoder.h"
64 #include "mozilla/dom/XMLHttpRequestBinding.h"
65 #include "mozilla/Attributes.h"
66 #include "nsIPermissionManager.h"
67 #include "nsMimeTypes.h"
68 #include "nsIHttpChannelInternal.h"
69 #include "nsIClassOfService.h"
70 #include "nsCharSeparatedTokenizer.h"
71 #include "nsFormData.h"
72 #include "nsStreamListenerWrapper.h"
73 #include "xpcjsid.h"
74 #include "nsITimedChannel.h"
75 #include "nsWrapperCacheInlines.h"
76 #include "nsZipArchive.h"
77 #include "mozilla/Preferences.h"
78 #include "private/pprio.h"
80 using namespace mozilla;
81 using namespace mozilla::dom;
83 // Maximum size that we'll grow an ArrayBuffer instead of doubling,
84 // once doubling reaches this threshold
85 #define XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH (32*1024*1024)
86 // start at 32k to avoid lots of doubling right at the start
87 #define XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE (32*1024)
88 // the maximum Content-Length that we'll preallocate. 1GB. Must fit
89 // in an int32_t!
90 #define XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE (1*1024*1024*1024LL)
92 #define LOAD_STR "load"
93 #define ERROR_STR "error"
94 #define ABORT_STR "abort"
95 #define TIMEOUT_STR "timeout"
96 #define LOADSTART_STR "loadstart"
97 #define PROGRESS_STR "progress"
98 #define READYSTATE_STR "readystatechange"
99 #define LOADEND_STR "loadend"
101 // CIDs
103 // State
104 #define XML_HTTP_REQUEST_UNSENT (1 << 0) // 0 UNSENT
105 #define XML_HTTP_REQUEST_OPENED (1 << 1) // 1 OPENED
106 #define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
107 #define XML_HTTP_REQUEST_LOADING (1 << 3) // 3 LOADING
108 #define XML_HTTP_REQUEST_DONE (1 << 4) // 4 DONE
109 #define XML_HTTP_REQUEST_SENT (1 << 5) // Internal, OPENED in IE and external view
110 // The above states are mutually exclusive, change with ChangeState() only.
111 // The states below can be combined.
112 #define XML_HTTP_REQUEST_ABORTED (1 << 7) // Internal
113 #define XML_HTTP_REQUEST_ASYNC (1 << 8) // Internal
114 #define XML_HTTP_REQUEST_PARSEBODY (1 << 9) // Internal
115 #define XML_HTTP_REQUEST_SYNCLOOPING (1 << 10) // Internal
116 #define XML_HTTP_REQUEST_BACKGROUND (1 << 13) // Internal
117 #define XML_HTTP_REQUEST_USE_XSITE_AC (1 << 14) // Internal
118 #define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 15) // Internal
119 #define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 16) // Internal
120 #define XML_HTTP_REQUEST_TIMED_OUT (1 << 17) // Internal
121 #define XML_HTTP_REQUEST_DELETED (1 << 18) // Internal
123 #define XML_HTTP_REQUEST_LOADSTATES \
124 (XML_HTTP_REQUEST_UNSENT | \
125 XML_HTTP_REQUEST_OPENED | \
126 XML_HTTP_REQUEST_HEADERS_RECEIVED | \
127 XML_HTTP_REQUEST_LOADING | \
128 XML_HTTP_REQUEST_DONE | \
129 XML_HTTP_REQUEST_SENT)
131 #define NS_BADCERTHANDLER_CONTRACTID \
132 "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
134 #define NS_PROGRESS_EVENT_INTERVAL 50
136 #define IMPL_CSTRING_GETTER(_name) \
137 NS_IMETHODIMP \
138 nsXMLHttpRequest::_name(nsACString& aOut) \
140 nsCString tmp; \
141 _name(tmp); \
142 aOut = tmp; \
143 return NS_OK; \
146 NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
148 class nsResumeTimeoutsEvent : public nsRunnable
150 public:
151 explicit nsResumeTimeoutsEvent(nsPIDOMWindow* aWindow) : mWindow(aWindow) {}
153 NS_IMETHOD Run()
155 mWindow->ResumeTimeouts(false);
156 return NS_OK;
159 private:
160 nsCOMPtr<nsPIDOMWindow> mWindow;
164 // This helper function adds the given load flags to the request's existing
165 // load flags.
166 static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
168 nsLoadFlags flags;
169 request->GetLoadFlags(&flags);
170 flags |= newFlags;
171 request->SetLoadFlags(flags);
174 //-----------------------------------------------------------------------------
175 // XMLHttpRequestAuthPrompt
176 //-----------------------------------------------------------------------------
178 class XMLHttpRequestAuthPrompt : public nsIAuthPrompt
180 public:
181 NS_DECL_ISUPPORTS
182 NS_DECL_NSIAUTHPROMPT
184 XMLHttpRequestAuthPrompt();
186 protected:
187 virtual ~XMLHttpRequestAuthPrompt();
190 NS_IMPL_ISUPPORTS(XMLHttpRequestAuthPrompt, nsIAuthPrompt)
192 XMLHttpRequestAuthPrompt::XMLHttpRequestAuthPrompt()
194 MOZ_COUNT_CTOR(XMLHttpRequestAuthPrompt);
197 XMLHttpRequestAuthPrompt::~XMLHttpRequestAuthPrompt()
199 MOZ_COUNT_DTOR(XMLHttpRequestAuthPrompt);
202 NS_IMETHODIMP
203 XMLHttpRequestAuthPrompt::Prompt(const char16_t* aDialogTitle,
204 const char16_t* aText,
205 const char16_t* aPasswordRealm,
206 uint32_t aSavePassword,
207 const char16_t* aDefaultText,
208 char16_t** aResult,
209 bool* aRetval)
211 *aRetval = false;
212 return NS_OK;
215 NS_IMETHODIMP
216 XMLHttpRequestAuthPrompt::PromptUsernameAndPassword(const char16_t* aDialogTitle,
217 const char16_t* aDialogText,
218 const char16_t* aPasswordRealm,
219 uint32_t aSavePassword,
220 char16_t** aUser,
221 char16_t** aPwd,
222 bool* aRetval)
224 *aRetval = false;
225 return NS_OK;
228 NS_IMETHODIMP
229 XMLHttpRequestAuthPrompt::PromptPassword(const char16_t* aDialogTitle,
230 const char16_t* aText,
231 const char16_t* aPasswordRealm,
232 uint32_t aSavePassword,
233 char16_t** aPwd,
234 bool* aRetval)
236 *aRetval = false;
237 return NS_OK;
240 /////////////////////////////////////////////
242 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
244 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXHREventTarget,
245 DOMEventTargetHelper)
246 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
248 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXHREventTarget,
249 DOMEventTargetHelper)
250 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
252 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXHREventTarget)
253 NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
254 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
256 NS_IMPL_ADDREF_INHERITED(nsXHREventTarget, DOMEventTargetHelper)
257 NS_IMPL_RELEASE_INHERITED(nsXHREventTarget, DOMEventTargetHelper)
259 void
260 nsXHREventTarget::DisconnectFromOwner()
262 DOMEventTargetHelper::DisconnectFromOwner();
265 /////////////////////////////////////////////
267 NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequestUpload)
268 NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestUpload)
269 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
271 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
272 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
274 /* virtual */ JSObject*
275 nsXMLHttpRequestUpload::WrapObject(JSContext* aCx)
277 return XMLHttpRequestUploadBinding::Wrap(aCx, this);
280 /////////////////////////////////////////////
283 /////////////////////////////////////////////
285 bool
286 nsXMLHttpRequest::sDontWarnAboutSyncXHR = false;
288 nsXMLHttpRequest::nsXMLHttpRequest()
289 : mResponseBodyDecodedPos(0),
290 mResponseType(XML_HTTP_RESPONSE_TYPE_DEFAULT),
291 mRequestObserver(nullptr), mState(XML_HTTP_REQUEST_UNSENT),
292 mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
293 mProgressSinceLastProgressEvent(false),
294 mRequestSentTime(0), mTimeoutMilliseconds(0),
295 mErrorLoad(false), mWaitingForOnStopRequest(false),
296 mProgressTimerIsActive(false),
297 mIsHtml(false),
298 mWarnAboutSyncHtml(false),
299 mLoadLengthComputable(false), mLoadTotal(0),
300 mIsSystem(false),
301 mIsAnon(false),
302 mFirstStartRequestSeen(false),
303 mInLoadProgressEvent(false),
304 mResultJSON(JSVAL_VOID),
305 mResultArrayBuffer(nullptr),
306 mIsMappedArrayBuffer(false),
307 mXPCOMifier(nullptr)
309 #ifdef DEBUG
310 StaticAssertions();
311 #endif
314 nsXMLHttpRequest::~nsXMLHttpRequest()
316 mState |= XML_HTTP_REQUEST_DELETED;
318 if (mState & (XML_HTTP_REQUEST_SENT |
319 XML_HTTP_REQUEST_LOADING)) {
320 Abort();
323 NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
324 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
326 mResultJSON = JSVAL_VOID;
327 mResultArrayBuffer = nullptr;
328 mozilla::DropJSObjects(this);
331 void
332 nsXMLHttpRequest::RootJSResultObjects()
334 mozilla::HoldJSObjects(this);
338 * This Init method is called from the factory constructor.
340 nsresult
341 nsXMLHttpRequest::Init()
343 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
344 nsCOMPtr<nsIPrincipal> subjectPrincipal;
345 if (secMan) {
346 secMan->GetSystemPrincipal(getter_AddRefs(subjectPrincipal));
348 NS_ENSURE_STATE(subjectPrincipal);
350 // Instead of grabbing some random global from the context stack,
351 // let's use the default one (junk scope) for now.
352 // We should move away from this Init...
353 Construct(subjectPrincipal, xpc::NativeGlobal(xpc::PrivilegedJunkScope()));
354 return NS_OK;
358 * This Init method should only be called by C++ consumers.
360 NS_IMETHODIMP
361 nsXMLHttpRequest::Init(nsIPrincipal* aPrincipal,
362 nsIScriptContext* aScriptContext,
363 nsIGlobalObject* aGlobalObject,
364 nsIURI* aBaseURI,
365 nsILoadGroup* aLoadGroup)
367 NS_ENSURE_ARG_POINTER(aPrincipal);
369 if (nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobalObject)) {
370 if (win->IsOuterWindow()) {
371 // Must be bound to inner window, innerize if necessary.
372 nsCOMPtr<nsIGlobalObject> inner = do_QueryInterface(
373 win->GetCurrentInnerWindow());
374 aGlobalObject = inner.get();
378 Construct(aPrincipal, aGlobalObject, aBaseURI, aLoadGroup);
379 return NS_OK;
382 void
383 nsXMLHttpRequest::InitParameters(bool aAnon, bool aSystem)
385 if (!aAnon && !aSystem) {
386 return;
389 // Check for permissions.
390 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetOwner());
391 if (!window || !window->GetDocShell()) {
392 return;
395 // Chrome is always allowed access, so do the permission check only
396 // for non-chrome pages.
397 if (!IsSystemXHR() && aSystem) {
398 nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
399 if (!doc) {
400 return;
403 nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
404 nsCOMPtr<nsIPermissionManager> permMgr =
405 services::GetPermissionManager();
406 if (!permMgr)
407 return;
409 uint32_t permission;
410 nsresult rv =
411 permMgr->TestPermissionFromPrincipal(principal, "systemXHR", &permission);
412 if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
413 return;
417 SetParameters(aAnon, aSystem);
420 void
421 nsXMLHttpRequest::ResetResponse()
423 mResponseXML = nullptr;
424 mResponseBody.Truncate();
425 mResponseText.Truncate();
426 mResponseBlob = nullptr;
427 mDOMFile = nullptr;
428 mBlobSet = nullptr;
429 mResultArrayBuffer = nullptr;
430 mArrayBufferBuilder.reset();
431 mResultJSON = JSVAL_VOID;
432 mDataAvailable = 0;
433 mLoadTransferred = 0;
434 mResponseBodyDecodedPos = 0;
437 void
438 nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
440 mRequestObserver = aObserver;
443 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
445 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
446 bool isBlack = tmp->IsBlack();
447 if (isBlack || tmp->mWaitingForOnStopRequest) {
448 if (tmp->mListenerManager) {
449 tmp->mListenerManager->MarkForCC();
451 if (!isBlack && tmp->PreservingWrapper()) {
452 // This marks the wrapper black.
453 tmp->GetWrapper();
455 return true;
457 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
459 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXMLHttpRequest)
460 return tmp->
461 IsBlackAndDoesNotNeedTracing(static_cast<DOMEventTargetHelper*>(tmp));
462 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
464 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXMLHttpRequest)
465 return tmp->IsBlack();
466 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
468 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
469 nsXHREventTarget)
470 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
471 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
472 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
473 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCORSPreflightChannel)
475 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
477 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
478 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMFile)
479 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
481 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
482 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
484 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
485 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
487 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
488 nsXHREventTarget)
489 tmp->mResultArrayBuffer = nullptr;
490 tmp->mArrayBufferBuilder.reset();
491 tmp->mResultJSON = JSVAL_VOID;
492 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
493 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
494 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
495 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCORSPreflightChannel)
497 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
499 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
500 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMFile)
501 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)
503 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
504 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
506 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
507 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
509 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
510 nsXHREventTarget)
511 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
512 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultJSON)
513 NS_IMPL_CYCLE_COLLECTION_TRACE_END
515 // QueryInterface implementation for nsXMLHttpRequest
516 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLHttpRequest)
517 NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
518 NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
519 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
520 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
521 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
522 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
523 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
524 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
525 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
526 NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget)
527 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
529 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
530 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
532 NS_IMPL_EVENT_HANDLER(nsXMLHttpRequest, readystatechange)
534 void
535 nsXMLHttpRequest::DisconnectFromOwner()
537 nsXHREventTarget::DisconnectFromOwner();
538 Abort();
541 size_t
542 nsXMLHttpRequest::SizeOfEventTargetIncludingThis(
543 MallocSizeOf aMallocSizeOf) const
545 size_t n = aMallocSizeOf(this);
546 n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
548 // Why is this safe? Because no-one else will report this string. The
549 // other possible sharers of this string are as follows.
551 // - The JS engine could hold copies if the JS code holds references, e.g.
552 // |var text = XHR.responseText|. However, those references will be via JS
553 // external strings, for which the JS memory reporter does *not* report the
554 // chars.
556 // - Binary extensions, but they're *extremely* unlikely to do any memory
557 // reporting.
559 n += mResponseText.SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
561 return n;
563 // Measurement of the following members may be added later if DMD finds it is
564 // worthwhile:
565 // - lots
568 /* readonly attribute nsIChannel channel; */
569 NS_IMETHODIMP
570 nsXMLHttpRequest::GetChannel(nsIChannel **aChannel)
572 NS_ENSURE_ARG_POINTER(aChannel);
573 NS_IF_ADDREF(*aChannel = mChannel);
575 return NS_OK;
578 static void LogMessage(const char* aWarning, nsPIDOMWindow* aWindow)
580 nsCOMPtr<nsIDocument> doc;
581 if (aWindow) {
582 doc = aWindow->GetExtantDoc();
584 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
585 NS_LITERAL_CSTRING("DOM"), doc,
586 nsContentUtils::eDOM_PROPERTIES,
587 aWarning);
590 /* readonly attribute nsIDOMDocument responseXML; */
591 NS_IMETHODIMP
592 nsXMLHttpRequest::GetResponseXML(nsIDOMDocument **aResponseXML)
594 ErrorResult rv;
595 nsIDocument* responseXML = GetResponseXML(rv);
596 if (rv.Failed()) {
597 return rv.ErrorCode();
600 if (!responseXML) {
601 *aResponseXML = nullptr;
602 return NS_OK;
605 return CallQueryInterface(responseXML, aResponseXML);
608 nsIDocument*
609 nsXMLHttpRequest::GetResponseXML(ErrorResult& aRv)
611 if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
612 mResponseType != XML_HTTP_RESPONSE_TYPE_DOCUMENT) {
613 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
614 return nullptr;
616 if (mWarnAboutSyncHtml) {
617 mWarnAboutSyncHtml = false;
618 LogMessage("HTMLSyncXHRWarning", GetOwner());
620 return (XML_HTTP_REQUEST_DONE & mState) ? mResponseXML : nullptr;
624 * This piece copied from XMLDocument, we try to get the charset
625 * from HTTP headers.
627 nsresult
628 nsXMLHttpRequest::DetectCharset()
630 mResponseCharset.Truncate();
631 mDecoder = nullptr;
633 if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
634 mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT &&
635 mResponseType != XML_HTTP_RESPONSE_TYPE_JSON &&
636 mResponseType != XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
637 return NS_OK;
640 nsAutoCString charsetVal;
641 bool ok = mChannel &&
642 NS_SUCCEEDED(mChannel->GetContentCharset(charsetVal)) &&
643 EncodingUtils::FindEncodingForLabel(charsetVal, mResponseCharset);
644 if (!ok || mResponseCharset.IsEmpty()) {
645 // MS documentation states UTF-8 is default for responseText
646 mResponseCharset.AssignLiteral("UTF-8");
649 if (mResponseType == XML_HTTP_RESPONSE_TYPE_JSON &&
650 !mResponseCharset.EqualsLiteral("UTF-8")) {
651 // The XHR spec says only UTF-8 is supported for responseType == "json"
652 LogMessage("JSONCharsetWarning", GetOwner());
653 mResponseCharset.AssignLiteral("UTF-8");
656 mDecoder = EncodingUtils::DecoderForEncoding(mResponseCharset);
658 return NS_OK;
661 nsresult
662 nsXMLHttpRequest::AppendToResponseText(const char * aSrcBuffer,
663 uint32_t aSrcBufferLen)
665 NS_ENSURE_STATE(mDecoder);
667 int32_t destBufferLen;
668 nsresult rv = mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen,
669 &destBufferLen);
670 NS_ENSURE_SUCCESS(rv, rv);
672 if (!mResponseText.SetCapacity(mResponseText.Length() + destBufferLen, fallible_t())) {
673 return NS_ERROR_OUT_OF_MEMORY;
676 char16_t* destBuffer = mResponseText.BeginWriting() + mResponseText.Length();
678 int32_t totalChars = mResponseText.Length();
680 // This code here is basically a copy of a similar thing in
681 // nsScanner::Append(const char* aBuffer, uint32_t aLen).
682 int32_t srclen = (int32_t)aSrcBufferLen;
683 int32_t destlen = (int32_t)destBufferLen;
684 rv = mDecoder->Convert(aSrcBuffer,
685 &srclen,
686 destBuffer,
687 &destlen);
688 MOZ_ASSERT(NS_SUCCEEDED(rv));
690 totalChars += destlen;
692 mResponseText.SetLength(totalChars);
694 return NS_OK;
697 /* readonly attribute AString responseText; */
698 NS_IMETHODIMP
699 nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
701 ErrorResult rv;
702 nsString responseText;
703 GetResponseText(responseText, rv);
704 aResponseText = responseText;
705 return rv.ErrorCode();
708 void
709 nsXMLHttpRequest::GetResponseText(nsString& aResponseText, ErrorResult& aRv)
711 aResponseText.Truncate();
713 if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
714 mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT &&
715 mResponseType != XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
716 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
717 return;
720 if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT &&
721 !mInLoadProgressEvent) {
722 aResponseText.SetIsVoid(true);
723 return;
726 if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
727 return;
730 // We only decode text lazily if we're also parsing to a doc.
731 // Also, if we've decoded all current data already, then no need to decode
732 // more.
733 if (!mResponseXML ||
734 mResponseBodyDecodedPos == mResponseBody.Length()) {
735 aResponseText = mResponseText;
736 return;
739 if (mResponseCharset != mResponseXML->GetDocumentCharacterSet()) {
740 mResponseCharset = mResponseXML->GetDocumentCharacterSet();
741 mResponseText.Truncate();
742 mResponseBodyDecodedPos = 0;
743 mDecoder = EncodingUtils::DecoderForEncoding(mResponseCharset);
746 NS_ASSERTION(mResponseBodyDecodedPos < mResponseBody.Length(),
747 "Unexpected mResponseBodyDecodedPos");
748 aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
749 mResponseBody.Length() - mResponseBodyDecodedPos);
750 if (aRv.Failed()) {
751 return;
754 mResponseBodyDecodedPos = mResponseBody.Length();
756 if (mState & XML_HTTP_REQUEST_DONE) {
757 // Free memory buffer which we no longer need
758 mResponseBody.Truncate();
759 mResponseBodyDecodedPos = 0;
762 aResponseText = mResponseText;
765 nsresult
766 nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx)
768 if (!aCx) {
769 return NS_ERROR_FAILURE;
771 RootJSResultObjects();
773 // The Unicode converter has already zapped the BOM if there was one
774 JS::Rooted<JS::Value> value(aCx);
775 if (!JS_ParseJSON(aCx,
776 static_cast<const char16_t*>(mResponseText.get()), mResponseText.Length(),
777 &value)) {
778 return NS_ERROR_FAILURE;
781 mResultJSON = value;
782 return NS_OK;
785 void
786 nsXMLHttpRequest::CreatePartialBlob()
788 if (mDOMFile) {
789 // Use progress info to determine whether load is complete, but use
790 // mDataAvailable to ensure a slice is created based on the uncompressed
791 // data count.
792 if (mLoadTotal == mLoadTransferred) {
793 mResponseBlob = mDOMFile;
794 } else {
795 ErrorResult rv;
796 mResponseBlob = mDOMFile->CreateSlice(0, mDataAvailable,
797 EmptyString(), rv);
799 return;
802 // mBlobSet can be null if the request has been canceled
803 if (!mBlobSet) {
804 return;
807 nsAutoCString contentType;
808 if (mLoadTotal == mLoadTransferred) {
809 mChannel->GetContentType(contentType);
812 mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType);
815 /* attribute AString responseType; */
816 NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
818 switch (mResponseType) {
819 case XML_HTTP_RESPONSE_TYPE_DEFAULT:
820 aResponseType.Truncate();
821 break;
822 case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
823 aResponseType.AssignLiteral("arraybuffer");
824 break;
825 case XML_HTTP_RESPONSE_TYPE_BLOB:
826 aResponseType.AssignLiteral("blob");
827 break;
828 case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
829 aResponseType.AssignLiteral("document");
830 break;
831 case XML_HTTP_RESPONSE_TYPE_TEXT:
832 aResponseType.AssignLiteral("text");
833 break;
834 case XML_HTTP_RESPONSE_TYPE_JSON:
835 aResponseType.AssignLiteral("json");
836 break;
837 case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
838 aResponseType.AssignLiteral("moz-chunked-text");
839 break;
840 case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
841 aResponseType.AssignLiteral("moz-chunked-arraybuffer");
842 break;
843 case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
844 aResponseType.AssignLiteral("moz-blob");
845 break;
846 default:
847 NS_ERROR("Should not happen");
850 return NS_OK;
853 #ifdef DEBUG
854 void
855 nsXMLHttpRequest::StaticAssertions()
857 #define ASSERT_ENUM_EQUAL(_lc, _uc) \
858 static_assert(\
859 static_cast<int>(XMLHttpRequestResponseType::_lc) \
860 == XML_HTTP_RESPONSE_TYPE_ ## _uc, \
861 #_uc " should match")
863 ASSERT_ENUM_EQUAL(_empty, DEFAULT);
864 ASSERT_ENUM_EQUAL(Arraybuffer, ARRAYBUFFER);
865 ASSERT_ENUM_EQUAL(Blob, BLOB);
866 ASSERT_ENUM_EQUAL(Document, DOCUMENT);
867 ASSERT_ENUM_EQUAL(Json, JSON);
868 ASSERT_ENUM_EQUAL(Text, TEXT);
869 ASSERT_ENUM_EQUAL(Moz_chunked_text, CHUNKED_TEXT);
870 ASSERT_ENUM_EQUAL(Moz_chunked_arraybuffer, CHUNKED_ARRAYBUFFER);
871 ASSERT_ENUM_EQUAL(Moz_blob, MOZ_BLOB);
872 #undef ASSERT_ENUM_EQUAL
874 #endif
876 /* attribute AString responseType; */
877 NS_IMETHODIMP nsXMLHttpRequest::SetResponseType(const nsAString& aResponseType)
879 nsXMLHttpRequest::ResponseTypeEnum responseType;
880 if (aResponseType.IsEmpty()) {
881 responseType = XML_HTTP_RESPONSE_TYPE_DEFAULT;
882 } else if (aResponseType.EqualsLiteral("arraybuffer")) {
883 responseType = XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER;
884 } else if (aResponseType.EqualsLiteral("blob")) {
885 responseType = XML_HTTP_RESPONSE_TYPE_BLOB;
886 } else if (aResponseType.EqualsLiteral("document")) {
887 responseType = XML_HTTP_RESPONSE_TYPE_DOCUMENT;
888 } else if (aResponseType.EqualsLiteral("text")) {
889 responseType = XML_HTTP_RESPONSE_TYPE_TEXT;
890 } else if (aResponseType.EqualsLiteral("json")) {
891 responseType = XML_HTTP_RESPONSE_TYPE_JSON;
892 } else if (aResponseType.EqualsLiteral("moz-chunked-text")) {
893 responseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT;
894 } else if (aResponseType.EqualsLiteral("moz-chunked-arraybuffer")) {
895 responseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER;
896 } else if (aResponseType.EqualsLiteral("moz-blob")) {
897 responseType = XML_HTTP_RESPONSE_TYPE_MOZ_BLOB;
898 } else {
899 return NS_OK;
902 ErrorResult rv;
903 SetResponseType(responseType, rv);
904 return rv.ErrorCode();
907 void
908 nsXMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aType,
909 ErrorResult& aRv)
911 SetResponseType(ResponseTypeEnum(static_cast<int>(aType)), aRv);
914 void
915 nsXMLHttpRequest::SetResponseType(nsXMLHttpRequest::ResponseTypeEnum aResponseType,
916 ErrorResult& aRv)
918 // If the state is not OPENED or HEADERS_RECEIVED raise an
919 // INVALID_STATE_ERR exception and terminate these steps.
920 if (!(mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT |
921 XML_HTTP_REQUEST_HEADERS_RECEIVED))) {
922 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
923 return;
926 // sync request is not allowed setting responseType in window context
927 if (HasOrHasHadOwner() &&
928 !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
929 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
930 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
931 return;
934 if (!(mState & XML_HTTP_REQUEST_ASYNC) &&
935 (aResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
936 aResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
937 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
938 return;
941 // Set the responseType attribute's value to the given value.
942 mResponseType = aResponseType;
946 /* readonly attribute jsval response; */
947 NS_IMETHODIMP
948 nsXMLHttpRequest::GetResponse(JSContext *aCx, JS::MutableHandle<JS::Value> aResult)
950 ErrorResult rv;
951 GetResponse(aCx, aResult, rv);
952 return rv.ErrorCode();
955 void
956 nsXMLHttpRequest::GetResponse(JSContext* aCx,
957 JS::MutableHandle<JS::Value> aResponse,
958 ErrorResult& aRv)
960 switch (mResponseType) {
961 case XML_HTTP_RESPONSE_TYPE_DEFAULT:
962 case XML_HTTP_RESPONSE_TYPE_TEXT:
963 case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
965 nsString str;
966 aRv = GetResponseText(str);
967 if (aRv.Failed()) {
968 return;
970 if (!xpc::StringToJsval(aCx, str, aResponse)) {
971 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
973 return;
976 case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
977 case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
979 if (!(mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
980 mState & XML_HTTP_REQUEST_DONE) &&
981 !(mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER &&
982 mInLoadProgressEvent)) {
983 aResponse.setNull();
984 return;
987 if (!mResultArrayBuffer) {
988 RootJSResultObjects();
990 mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
991 if (!mResultArrayBuffer) {
992 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
993 return;
996 JS::ExposeObjectToActiveJS(mResultArrayBuffer);
997 aResponse.setObject(*mResultArrayBuffer);
998 return;
1000 case XML_HTTP_RESPONSE_TYPE_BLOB:
1001 case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
1003 if (!(mState & XML_HTTP_REQUEST_DONE)) {
1004 if (mResponseType != XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
1005 aResponse.setNull();
1006 return;
1009 if (!mResponseBlob) {
1010 CreatePartialBlob();
1014 if (!mResponseBlob) {
1015 aResponse.setNull();
1016 return;
1019 GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
1020 return;
1022 case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
1024 if (!(mState & XML_HTTP_REQUEST_DONE) || !mResponseXML) {
1025 aResponse.setNull();
1026 return;
1029 aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
1030 return;
1032 case XML_HTTP_RESPONSE_TYPE_JSON:
1034 if (!(mState & XML_HTTP_REQUEST_DONE)) {
1035 aResponse.setNull();
1036 return;
1039 if (mResultJSON.isUndefined()) {
1040 aRv = CreateResponseParsedJSON(aCx);
1041 mResponseText.Truncate();
1042 if (aRv.Failed()) {
1043 // Per spec, errors aren't propagated. null is returned instead.
1044 aRv = NS_OK;
1045 // It would be nice to log the error to the console. That's hard to
1046 // do without calling window.onerror as a side effect, though.
1047 JS_ClearPendingException(aCx);
1048 mResultJSON.setNull();
1051 JS::ExposeValueToActiveJS(mResultJSON);
1052 aResponse.set(mResultJSON);
1053 return;
1055 default:
1056 NS_ERROR("Should not happen");
1059 aResponse.setNull();
1062 bool
1063 nsXMLHttpRequest::IsDeniedCrossSiteRequest()
1065 if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) && mChannel) {
1066 nsresult rv;
1067 mChannel->GetStatus(&rv);
1068 if (NS_FAILED(rv)) {
1069 return true;
1072 return false;
1075 /* readonly attribute AString responseURL; */
1076 void
1077 nsXMLHttpRequest::GetResponseURL(nsAString& aUrl)
1079 aUrl.Truncate();
1081 uint16_t readyState;
1082 GetReadyState(&readyState);
1083 if ((readyState == UNSENT || readyState == OPENED) || !mChannel) {
1084 return;
1087 // Make sure we don't leak responseURL information from denied cross-site
1088 // requests.
1089 if (IsDeniedCrossSiteRequest()) {
1090 return;
1093 nsCOMPtr<nsIURI> responseUrl;
1094 mChannel->GetURI(getter_AddRefs(responseUrl));
1096 if (!responseUrl) {
1097 return;
1100 nsAutoCString temp;
1101 responseUrl->GetSpecIgnoringRef(temp);
1102 CopyUTF8toUTF16(temp, aUrl);
1105 /* readonly attribute unsigned long status; */
1106 NS_IMETHODIMP
1107 nsXMLHttpRequest::GetStatus(uint32_t *aStatus)
1109 *aStatus = Status();
1110 return NS_OK;
1113 uint32_t
1114 nsXMLHttpRequest::Status()
1116 // Make sure we don't leak status information from denied cross-site
1117 // requests.
1118 if (IsDeniedCrossSiteRequest()) {
1119 return 0;
1122 uint16_t readyState = ReadyState();
1123 if (readyState == UNSENT || readyState == OPENED) {
1124 return 0;
1127 if (mErrorLoad) {
1128 // Let's simulate the http protocol for jar/app requests:
1129 nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
1130 if (jarChannel) {
1131 nsresult status;
1132 mChannel->GetStatus(&status);
1134 if (status == NS_ERROR_FILE_NOT_FOUND) {
1135 return 404; // Not Found
1136 } else {
1137 return 500; // Internal Error
1141 return 0;
1144 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1145 if (!httpChannel) {
1146 // Pretend like we got a 200 response, since our load was successful
1147 return 200;
1150 uint32_t status;
1151 nsresult rv = httpChannel->GetResponseStatus(&status);
1152 if (NS_FAILED(rv)) {
1153 status = 0;
1156 return status;
1159 IMPL_CSTRING_GETTER(GetStatusText)
1160 void
1161 nsXMLHttpRequest::GetStatusText(nsCString& aStatusText)
1163 // Return an empty status text on all error loads.
1164 aStatusText.Truncate();
1166 // Make sure we don't leak status information from denied cross-site
1167 // requests.
1168 if (IsDeniedCrossSiteRequest()) {
1169 return;
1172 // Check the current XHR state to see if it is valid to obtain the statusText
1173 // value. This check is to prevent the status text for redirects from being
1174 // available before all the redirects have been followed and HTTP headers have
1175 // been received.
1176 uint16_t readyState = ReadyState();
1177 if (readyState == UNSENT || readyState == OPENED) {
1178 return;
1181 if (mErrorLoad) {
1182 return;
1185 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1186 if (httpChannel) {
1187 httpChannel->GetResponseStatusText(aStatusText);
1188 } else {
1189 aStatusText.AssignLiteral("OK");
1193 void
1194 nsXMLHttpRequest::CloseRequestWithError(const nsAString& aType,
1195 const uint32_t aFlag)
1197 if (mChannel) {
1198 mChannel->Cancel(NS_BINDING_ABORTED);
1200 if (mCORSPreflightChannel) {
1201 mCORSPreflightChannel->Cancel(NS_BINDING_ABORTED);
1203 if (mTimeoutTimer) {
1204 mTimeoutTimer->Cancel();
1206 uint32_t responseLength = mResponseBody.Length();
1207 ResetResponse();
1208 mState |= aFlag;
1210 // If we're in the destructor, don't risk dispatching an event.
1211 if (mState & XML_HTTP_REQUEST_DELETED) {
1212 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
1213 return;
1216 if (!(mState & (XML_HTTP_REQUEST_UNSENT |
1217 XML_HTTP_REQUEST_OPENED |
1218 XML_HTTP_REQUEST_DONE))) {
1219 ChangeState(XML_HTTP_REQUEST_DONE, true);
1221 if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
1222 DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
1223 mLoadTotal);
1224 if (mUpload && !mUploadComplete) {
1225 mUploadComplete = true;
1226 DispatchProgressEvent(mUpload, aType, true, mUploadTransferred,
1227 mUploadTotal);
1232 // The ChangeState call above calls onreadystatechange handlers which
1233 // if they load a new url will cause nsXMLHttpRequest::Open to clear
1234 // the abort state bit. If this occurs we're not uninitialized (bug 361773).
1235 if (mState & XML_HTTP_REQUEST_ABORTED) {
1236 ChangeState(XML_HTTP_REQUEST_UNSENT, false); // IE seems to do it
1239 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
1242 /* void abort (); */
1243 void
1244 nsXMLHttpRequest::Abort()
1246 CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR), XML_HTTP_REQUEST_ABORTED);
1249 NS_IMETHODIMP
1250 nsXMLHttpRequest::SlowAbort()
1252 Abort();
1253 return NS_OK;
1256 /*Method that checks if it is safe to expose a header value to the client.
1257 It is used to check what headers are exposed for CORS requests.*/
1258 bool
1259 nsXMLHttpRequest::IsSafeHeader(const nsACString& header, nsIHttpChannel* httpChannel)
1261 // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
1262 if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(header)) {
1263 NS_WARNING("blocked access to response header");
1264 return false;
1266 // if this is not a CORS call all headers are safe
1267 if (!(mState & XML_HTTP_REQUEST_USE_XSITE_AC)){
1268 return true;
1270 // Check for dangerous headers
1271 // Make sure we don't leak header information from denied cross-site
1272 // requests.
1273 if (mChannel) {
1274 nsresult status;
1275 mChannel->GetStatus(&status);
1276 if (NS_FAILED(status)) {
1277 return false;
1280 const char* kCrossOriginSafeHeaders[] = {
1281 "cache-control", "content-language", "content-type", "expires",
1282 "last-modified", "pragma"
1284 for (uint32_t i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
1285 if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
1286 return true;
1289 nsAutoCString headerVal;
1290 // The "Access-Control-Expose-Headers" header contains a comma separated
1291 // list of method names.
1292 httpChannel->
1293 GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"),
1294 headerVal);
1295 nsCCharSeparatedTokenizer exposeTokens(headerVal, ',');
1296 bool isSafe = false;
1297 while (exposeTokens.hasMoreTokens()) {
1298 const nsDependentCSubstring& token = exposeTokens.nextToken();
1299 if (token.IsEmpty()) {
1300 continue;
1302 if (!NS_IsValidHTTPToken(token)) {
1303 return false;
1305 if (header.Equals(token, nsCaseInsensitiveCStringComparator())) {
1306 isSafe = true;
1309 return isSafe;
1312 /* ByteString getAllResponseHeaders(); */
1313 IMPL_CSTRING_GETTER(GetAllResponseHeaders)
1314 void
1315 nsXMLHttpRequest::GetAllResponseHeaders(nsCString& aResponseHeaders)
1317 aResponseHeaders.Truncate();
1319 // If the state is UNSENT or OPENED,
1320 // return the empty string and terminate these steps.
1321 if (mState & (XML_HTTP_REQUEST_UNSENT |
1322 XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
1323 return;
1326 if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
1327 nsRefPtr<nsHeaderVisitor> visitor = new nsHeaderVisitor(this, httpChannel);
1328 if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
1329 aResponseHeaders = visitor->Headers();
1331 return;
1334 if (!mChannel) {
1335 return;
1338 // Even non-http channels supply content type.
1339 nsAutoCString value;
1340 if (NS_SUCCEEDED(mChannel->GetContentType(value))) {
1341 aResponseHeaders.AppendLiteral("Content-Type: ");
1342 aResponseHeaders.Append(value);
1343 if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
1344 aResponseHeaders.AppendLiteral(";charset=");
1345 aResponseHeaders.Append(value);
1347 aResponseHeaders.AppendLiteral("\r\n");
1350 int64_t length;
1351 if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
1352 aResponseHeaders.AppendLiteral("Content-Length: ");
1353 aResponseHeaders.AppendInt(length);
1354 aResponseHeaders.AppendLiteral("\r\n");
1358 NS_IMETHODIMP
1359 nsXMLHttpRequest::GetResponseHeader(const nsACString& aHeader,
1360 nsACString& aResult)
1362 ErrorResult rv;
1363 GetResponseHeader(aHeader, aResult, rv);
1364 return rv.ErrorCode();
1367 void
1368 nsXMLHttpRequest::GetResponseHeader(const nsACString& header,
1369 nsACString& _retval, ErrorResult& aRv)
1371 _retval.SetIsVoid(true);
1373 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1375 if (!httpChannel) {
1376 // If the state is UNSENT or OPENED,
1377 // return null and terminate these steps.
1378 if (mState & (XML_HTTP_REQUEST_UNSENT |
1379 XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
1380 return;
1383 // Even non-http channels supply content type and content length.
1384 // Remember we don't leak header information from denied cross-site
1385 // requests.
1386 nsresult status;
1387 if (!mChannel ||
1388 NS_FAILED(mChannel->GetStatus(&status)) ||
1389 NS_FAILED(status)) {
1390 return;
1393 // Content Type:
1394 if (header.LowerCaseEqualsASCII("content-type")) {
1395 if (NS_FAILED(mChannel->GetContentType(_retval))) {
1396 // Means no content type
1397 _retval.SetIsVoid(true);
1398 return;
1401 nsCString value;
1402 if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
1403 !value.IsEmpty()) {
1404 _retval.AppendLiteral(";charset=");
1405 _retval.Append(value);
1409 // Content Length:
1410 else if (header.LowerCaseEqualsASCII("content-length")) {
1411 int64_t length;
1412 if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
1413 _retval.AppendInt(length);
1417 return;
1420 // Check for dangerous headers
1421 if (!IsSafeHeader(header, httpChannel)) {
1422 return;
1425 aRv = httpChannel->GetResponseHeader(header, _retval);
1426 if (aRv.ErrorCode() == NS_ERROR_NOT_AVAILABLE) {
1427 // Means no header
1428 _retval.SetIsVoid(true);
1429 aRv = NS_OK;
1433 already_AddRefed<nsILoadGroup>
1434 nsXMLHttpRequest::GetLoadGroup() const
1436 if (mState & XML_HTTP_REQUEST_BACKGROUND) {
1437 return nullptr;
1440 if (mLoadGroup) {
1441 nsCOMPtr<nsILoadGroup> ref = mLoadGroup;
1442 return ref.forget();
1445 nsresult rv = NS_ERROR_FAILURE;
1446 nsIScriptContext* sc =
1447 const_cast<nsXMLHttpRequest*>(this)->GetContextForEventHandlers(&rv);
1448 nsCOMPtr<nsIDocument> doc =
1449 nsContentUtils::GetDocumentFromScriptContext(sc);
1450 if (doc) {
1451 return doc->GetDocumentLoadGroup();
1454 return nullptr;
1457 nsresult
1458 nsXMLHttpRequest::CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent)
1460 nsresult rv = EventDispatcher::CreateEvent(this, nullptr, nullptr,
1461 NS_LITERAL_STRING("Events"),
1462 aDOMEvent);
1463 if (NS_FAILED(rv)) {
1464 return rv;
1467 (*aDOMEvent)->InitEvent(NS_LITERAL_STRING(READYSTATE_STR),
1468 false, false);
1470 // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1471 (*aDOMEvent)->SetTrusted(true);
1473 return NS_OK;
1476 void
1477 nsXMLHttpRequest::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
1478 const nsAString& aType,
1479 bool aLengthComputable,
1480 uint64_t aLoaded, uint64_t aTotal)
1482 NS_ASSERTION(aTarget, "null target");
1483 NS_ASSERTION(!aType.IsEmpty(), "missing event type");
1485 if (NS_FAILED(CheckInnerWindowCorrectness()) ||
1486 (!AllowUploadProgress() && aTarget == mUpload)) {
1487 return;
1490 bool dispatchLoadend = aType.EqualsLiteral(LOAD_STR) ||
1491 aType.EqualsLiteral(ERROR_STR) ||
1492 aType.EqualsLiteral(TIMEOUT_STR) ||
1493 aType.EqualsLiteral(ABORT_STR);
1495 ProgressEventInit init;
1496 init.mBubbles = false;
1497 init.mCancelable = false;
1498 init.mLengthComputable = aLengthComputable;
1499 init.mLoaded = aLoaded;
1500 init.mTotal = (aTotal == UINT64_MAX) ? 0 : aTotal;
1502 nsRefPtr<ProgressEvent> event =
1503 ProgressEvent::Constructor(aTarget, aType, init);
1504 event->SetTrusted(true);
1506 aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
1508 if (dispatchLoadend) {
1509 DispatchProgressEvent(aTarget, NS_LITERAL_STRING(LOADEND_STR),
1510 aLengthComputable, aLoaded, aTotal);
1514 already_AddRefed<nsIHttpChannel>
1515 nsXMLHttpRequest::GetCurrentHttpChannel()
1517 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
1518 return httpChannel.forget();
1521 already_AddRefed<nsIJARChannel>
1522 nsXMLHttpRequest::GetCurrentJARChannel()
1524 nsCOMPtr<nsIJARChannel> appChannel = do_QueryInterface(mChannel);
1525 return appChannel.forget();
1528 bool
1529 nsXMLHttpRequest::IsSystemXHR()
1531 return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
1534 nsresult
1535 nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
1537 // A system XHR (chrome code or a web app with the right permission) can
1538 // always perform cross-site requests. In the web app case, however, we
1539 // must still check for protected URIs like file:///.
1540 if (IsSystemXHR()) {
1541 if (!nsContentUtils::IsSystemPrincipal(mPrincipal)) {
1542 nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
1543 nsCOMPtr<nsIURI> uri;
1544 aChannel->GetOriginalURI(getter_AddRefs(uri));
1545 return secMan->CheckLoadURIWithPrincipal(
1546 mPrincipal, uri, nsIScriptSecurityManager::STANDARD);
1548 return NS_OK;
1551 // If this is a same-origin request or the channel's URI inherits
1552 // its principal, it's allowed.
1553 if (nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
1554 return NS_OK;
1557 // This is a cross-site request
1558 mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
1560 // Check if we need to do a preflight request.
1561 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
1562 NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
1564 nsAutoCString method;
1565 httpChannel->GetRequestMethod(method);
1566 if (!mCORSUnsafeHeaders.IsEmpty() ||
1567 (mUpload && mUpload->HasListeners()) ||
1568 (!method.LowerCaseEqualsLiteral("get") &&
1569 !method.LowerCaseEqualsLiteral("post") &&
1570 !method.LowerCaseEqualsLiteral("head"))) {
1571 mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
1574 return NS_OK;
1577 NS_IMETHODIMP
1578 nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
1579 bool async, const nsAString& user,
1580 const nsAString& password, uint8_t optional_argc)
1582 if (!optional_argc) {
1583 // No optional arguments were passed in. Default async to true.
1584 async = true;
1586 Optional<nsAString> realUser;
1587 if (optional_argc > 1) {
1588 realUser = &user;
1590 Optional<nsAString> realPassword;
1591 if (optional_argc > 2) {
1592 realPassword = &password;
1594 return Open(method, url, async, realUser, realPassword);
1597 nsresult
1598 nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
1599 bool async, const Optional<nsAString>& user,
1600 const Optional<nsAString>& password)
1602 NS_ENSURE_ARG(!inMethod.IsEmpty());
1604 if (!async && !DontWarnAboutSyncXHR() && GetOwner() &&
1605 GetOwner()->GetExtantDoc()) {
1606 GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
1609 Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC,
1610 async ? 0 : 1);
1612 NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
1614 // Disallow HTTP/1.1 TRACE method (see bug 302489)
1615 // and MS IIS equivalent TRACK (see bug 381264)
1616 // and CONNECT
1617 if (inMethod.LowerCaseEqualsLiteral("trace") ||
1618 inMethod.LowerCaseEqualsLiteral("connect") ||
1619 inMethod.LowerCaseEqualsLiteral("track")) {
1620 return NS_ERROR_DOM_SECURITY_ERR;
1623 nsAutoCString method;
1624 // GET, POST, DELETE, HEAD, OPTIONS, PUT methods normalized to upper case
1625 if (inMethod.LowerCaseEqualsLiteral("get")) {
1626 method.AssignLiteral("GET");
1627 } else if (inMethod.LowerCaseEqualsLiteral("post")) {
1628 method.AssignLiteral("POST");
1629 } else if (inMethod.LowerCaseEqualsLiteral("delete")) {
1630 method.AssignLiteral("DELETE");
1631 } else if (inMethod.LowerCaseEqualsLiteral("head")) {
1632 method.AssignLiteral("HEAD");
1633 } else if (inMethod.LowerCaseEqualsLiteral("options")) {
1634 method.AssignLiteral("OPTIONS");
1635 } else if (inMethod.LowerCaseEqualsLiteral("put")) {
1636 method.AssignLiteral("PUT");
1637 } else {
1638 method = inMethod; // other methods are not normalized
1641 // sync request is not allowed using withCredential or responseType
1642 // in window context
1643 if (!async && HasOrHasHadOwner() &&
1644 (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
1645 mTimeoutMilliseconds ||
1646 mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
1647 if (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS) {
1648 LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
1650 if (mTimeoutMilliseconds) {
1651 LogMessage("TimeoutSyncXHRWarning", GetOwner());
1653 if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT) {
1654 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
1656 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1659 nsresult rv;
1660 nsCOMPtr<nsIURI> uri;
1662 if (mState & (XML_HTTP_REQUEST_OPENED |
1663 XML_HTTP_REQUEST_HEADERS_RECEIVED |
1664 XML_HTTP_REQUEST_LOADING |
1665 XML_HTTP_REQUEST_SENT)) {
1666 // IE aborts as well
1667 Abort();
1669 // XXX We should probably send a warning to the JS console
1670 // that load was aborted and event listeners were cleared
1671 // since this looks like a situation that could happen
1672 // by accident and you could spend a lot of time wondering
1673 // why things didn't work.
1676 // Unset any pre-existing aborted and timed-out states.
1677 mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
1679 if (async) {
1680 mState |= XML_HTTP_REQUEST_ASYNC;
1681 } else {
1682 mState &= ~XML_HTTP_REQUEST_ASYNC;
1685 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
1686 NS_ENSURE_SUCCESS(rv, rv);
1687 nsCOMPtr<nsIDocument> doc =
1688 nsContentUtils::GetDocumentFromScriptContext(sc);
1690 nsCOMPtr<nsIURI> baseURI;
1691 if (mBaseURI) {
1692 baseURI = mBaseURI;
1694 else if (doc) {
1695 baseURI = doc->GetBaseURI();
1698 rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, baseURI);
1699 if (NS_FAILED(rv)) return rv;
1701 rv = CheckInnerWindowCorrectness();
1702 NS_ENSURE_SUCCESS(rv, rv);
1703 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1704 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XMLHTTPREQUEST,
1705 uri,
1706 mPrincipal,
1707 doc,
1708 EmptyCString(), //mime guess
1709 nullptr, //extra
1710 &shouldLoad,
1711 nsContentUtils::GetContentPolicy(),
1712 nsContentUtils::GetSecurityManager());
1713 if (NS_FAILED(rv)) return rv;
1714 if (NS_CP_REJECTED(shouldLoad)) {
1715 // Disallowed by content policy
1716 return NS_ERROR_CONTENT_BLOCKED;
1719 // XXXbz this is wrong: we should only be looking at whether
1720 // user/password were passed, not at the values! See bug 759624.
1721 if (user.WasPassed() && !user.Value().IsEmpty()) {
1722 nsAutoCString userpass;
1723 CopyUTF16toUTF8(user.Value(), userpass);
1724 if (password.WasPassed() && !password.Value().IsEmpty()) {
1725 userpass.Append(':');
1726 AppendUTF16toUTF8(password.Value(), userpass);
1728 uri->SetUserPass(userpass);
1731 // Clear our record of previously set headers so future header set
1732 // operations will merge/override correctly.
1733 mAlreadySetHeaders.Clear();
1735 // When we are called from JS we can find the load group for the page,
1736 // and add ourselves to it. This way any pending requests
1737 // will be automatically aborted if the user leaves the page.
1738 nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
1740 nsSecurityFlags secFlags = nsILoadInfo::SEC_NORMAL;
1741 if (IsSystemXHR()) {
1742 // Don't give this document the system principal. We need to keep track of
1743 // mPrincipal being system because we use it for various security checks
1744 // that should be passing, but the document data shouldn't get a system
1745 // principal. Hence we set the sandbox flag in loadinfo, so that
1746 // GetChannelResultPrincipal will give us the nullprincipal.
1747 secFlags |= nsILoadInfo::SEC_SANDBOXED;
1748 } else {
1749 secFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
1752 // If we have the document, use it
1753 if (doc) {
1754 rv = NS_NewChannel(getter_AddRefs(mChannel),
1755 uri,
1756 doc,
1757 secFlags,
1758 nsIContentPolicy::TYPE_XMLHTTPREQUEST,
1759 loadGroup,
1760 nullptr, // aCallbacks
1761 nsIRequest::LOAD_BACKGROUND);
1762 } else {
1763 //otherwise use the principal
1764 rv = NS_NewChannel(getter_AddRefs(mChannel),
1765 uri,
1766 mPrincipal,
1767 secFlags,
1768 nsIContentPolicy::TYPE_XMLHTTPREQUEST,
1769 loadGroup,
1770 nullptr, // aCallbacks
1771 nsIRequest::LOAD_BACKGROUND);
1774 if (NS_FAILED(rv)) return rv;
1776 mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
1777 XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
1779 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
1780 if (httpChannel) {
1781 rv = httpChannel->SetRequestMethod(method);
1782 NS_ENSURE_SUCCESS(rv, rv);
1784 // Set the initiator type
1785 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
1786 if (timedChannel) {
1787 timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
1791 ChangeState(XML_HTTP_REQUEST_OPENED);
1793 return rv;
1797 * "Copy" from a stream.
1799 NS_METHOD
1800 nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
1801 void* closure,
1802 const char* fromRawSegment,
1803 uint32_t toOffset,
1804 uint32_t count,
1805 uint32_t *writeCount)
1807 nsXMLHttpRequest* xmlHttpRequest = static_cast<nsXMLHttpRequest*>(closure);
1808 if (!xmlHttpRequest || !writeCount) {
1809 NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
1810 return NS_ERROR_FAILURE;
1813 nsresult rv = NS_OK;
1815 if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
1816 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
1817 if (!xmlHttpRequest->mDOMFile) {
1818 if (!xmlHttpRequest->mBlobSet) {
1819 xmlHttpRequest->mBlobSet = new BlobSet();
1821 rv = xmlHttpRequest->mBlobSet->AppendVoidPtr(fromRawSegment, count);
1823 // Clear the cache so that the blob size is updated.
1824 if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
1825 xmlHttpRequest->mResponseBlob = nullptr;
1827 } else if ((xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
1828 !xmlHttpRequest->mIsMappedArrayBuffer) ||
1829 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
1830 // get the initial capacity to something reasonable to avoid a bunch of reallocs right
1831 // at the start
1832 if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
1833 xmlHttpRequest->mArrayBufferBuilder.setCapacity(PR_MAX(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
1835 xmlHttpRequest->mArrayBufferBuilder.append(reinterpret_cast<const uint8_t*>(fromRawSegment), count,
1836 XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH);
1837 } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT &&
1838 xmlHttpRequest->mResponseXML) {
1839 // Copy for our own use
1840 uint32_t previousLength = xmlHttpRequest->mResponseBody.Length();
1841 xmlHttpRequest->mResponseBody.Append(fromRawSegment,count);
1842 if (count > 0 && xmlHttpRequest->mResponseBody.Length() == previousLength) {
1843 return NS_ERROR_OUT_OF_MEMORY;
1845 } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
1846 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_TEXT ||
1847 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_JSON ||
1848 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
1849 NS_ASSERTION(!xmlHttpRequest->mResponseXML,
1850 "We shouldn't be parsing a doc here");
1851 xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
1854 if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
1855 // Give the same data to the parser.
1857 // We need to wrap the data in a new lightweight stream and pass that
1858 // to the parser, because calling ReadSegments() recursively on the same
1859 // stream is not supported.
1860 nsCOMPtr<nsIInputStream> copyStream;
1861 rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
1863 if (NS_SUCCEEDED(rv) && xmlHttpRequest->mXMLParserStreamListener) {
1864 NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
1865 nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
1866 ->OnDataAvailable(xmlHttpRequest->mChannel,
1867 xmlHttpRequest->mContext,
1868 copyStream, toOffset, count);
1870 // No use to continue parsing if we failed here, but we
1871 // should still finish reading the stream
1872 if (NS_FAILED(parsingResult)) {
1873 xmlHttpRequest->mState &= ~XML_HTTP_REQUEST_PARSEBODY;
1878 if (NS_SUCCEEDED(rv)) {
1879 *writeCount = count;
1880 } else {
1881 *writeCount = 0;
1884 return rv;
1887 bool nsXMLHttpRequest::CreateDOMFile(nsIRequest *request)
1889 nsCOMPtr<nsIFile> file;
1890 nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(request);
1891 if (fc) {
1892 fc->GetFile(getter_AddRefs(file));
1895 if (!file)
1896 return false;
1898 nsAutoCString contentType;
1899 mChannel->GetContentType(contentType);
1901 mDOMFile = File::CreateFromFile(GetOwner(), file, EmptyString(),
1902 NS_ConvertASCIItoUTF16(contentType));
1904 mBlobSet = nullptr;
1905 NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
1906 return true;
1909 NS_IMETHODIMP
1910 nsXMLHttpRequest::OnDataAvailable(nsIRequest *request,
1911 nsISupports *ctxt,
1912 nsIInputStream *inStr,
1913 uint64_t sourceOffset,
1914 uint32_t count)
1916 NS_ENSURE_ARG_POINTER(inStr);
1918 NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context");
1920 mProgressSinceLastProgressEvent = true;
1922 bool cancelable = false;
1923 if ((mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
1924 mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) && !mDOMFile) {
1925 cancelable = CreateDOMFile(request);
1926 // The nsIStreamListener contract mandates us
1927 // to read from the stream before returning.
1930 uint32_t totalRead;
1931 nsresult rv = inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc,
1932 (void*)this, count, &totalRead);
1933 NS_ENSURE_SUCCESS(rv, rv);
1935 if (cancelable) {
1936 // We don't have to read from the local file for the blob response
1937 mDOMFile->GetSize(&mDataAvailable);
1938 ChangeState(XML_HTTP_REQUEST_LOADING);
1939 return request->Cancel(NS_OK);
1942 mDataAvailable += totalRead;
1944 ChangeState(XML_HTTP_REQUEST_LOADING);
1946 MaybeDispatchProgressEvents(false);
1948 return NS_OK;
1951 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
1952 NS_IMETHODIMP
1953 nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
1955 PROFILER_LABEL("nsXMLHttpRequest", "OnStartRequest",
1956 js::ProfileEntry::Category::NETWORK);
1958 nsresult rv = NS_OK;
1959 if (!mFirstStartRequestSeen && mRequestObserver) {
1960 mFirstStartRequestSeen = true;
1961 mRequestObserver->OnStartRequest(request, ctxt);
1964 if (request != mChannel) {
1965 // Can this still happen?
1966 return NS_OK;
1969 // Don't do anything if we have been aborted
1970 if (mState & XML_HTTP_REQUEST_UNSENT)
1971 return NS_OK;
1973 /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT. See bug 361773.
1974 XHR2 spec says this is correct. */
1975 if (mState & XML_HTTP_REQUEST_ABORTED) {
1976 NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
1978 return NS_ERROR_UNEXPECTED;
1981 // Don't do anything if we have timed out.
1982 if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
1983 return NS_OK;
1986 nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
1987 NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
1989 nsresult status;
1990 request->GetStatus(&status);
1991 mErrorLoad = mErrorLoad || NS_FAILED(status);
1993 if (mUpload && !mUploadComplete && !mErrorLoad &&
1994 (mState & XML_HTTP_REQUEST_ASYNC)) {
1995 if (mProgressTimerIsActive) {
1996 mProgressTimerIsActive = false;
1997 mProgressNotifier->Cancel();
1999 if (mUploadTransferred < mUploadTotal) {
2000 mUploadTransferred = mUploadTotal;
2001 mProgressSinceLastProgressEvent = true;
2002 mUploadLengthComputable = true;
2003 MaybeDispatchProgressEvents(true);
2005 mUploadComplete = true;
2006 DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR),
2007 true, mUploadTotal, mUploadTotal);
2010 mContext = ctxt;
2011 mState |= XML_HTTP_REQUEST_PARSEBODY;
2012 ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
2014 ResetResponse();
2016 if (!mOverrideMimeType.IsEmpty()) {
2017 channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
2020 DetectCharset();
2022 // Set up arraybuffer
2023 if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER && NS_SUCCEEDED(status)) {
2024 if (mIsMappedArrayBuffer) {
2025 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
2026 if (jarChannel) {
2027 nsCOMPtr<nsIURI> uri;
2028 rv = channel->GetURI(getter_AddRefs(uri));
2029 if (NS_SUCCEEDED(rv)) {
2030 nsAutoCString file;
2031 nsAutoCString scheme;
2032 uri->GetScheme(scheme);
2033 if (scheme.LowerCaseEqualsLiteral("app")) {
2034 uri->GetPath(file);
2035 // The actual file inside zip package has no leading slash.
2036 file.Trim("/", true, false, false);
2037 } else if (scheme.LowerCaseEqualsLiteral("jar")) {
2038 nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
2039 if (jarURI) {
2040 jarURI->GetJAREntry(file);
2043 nsCOMPtr<nsIFile> jarFile;
2044 jarChannel->GetJarFile(getter_AddRefs(jarFile));
2045 rv = mArrayBufferBuilder.mapToFileInPackage(file, jarFile);
2046 if (NS_WARN_IF(NS_FAILED(rv))) {
2047 mIsMappedArrayBuffer = false;
2048 } else {
2049 channel->SetContentType(NS_LITERAL_CSTRING("application/mem-mapped"));
2054 // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
2055 // and we want it fallback to the malloc way.
2056 if (!mIsMappedArrayBuffer) {
2057 int64_t contentLength;
2058 rv = channel->GetContentLength(&contentLength);
2059 if (NS_SUCCEEDED(rv) &&
2060 contentLength > 0 &&
2061 contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
2062 mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
2067 // Set up responseXML
2068 bool parseBody = mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
2069 mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT;
2070 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2071 if (parseBody && httpChannel) {
2072 nsAutoCString method;
2073 httpChannel->GetRequestMethod(method);
2074 parseBody = !method.EqualsLiteral("HEAD");
2077 mIsHtml = false;
2078 mWarnAboutSyncHtml = false;
2079 if (parseBody && NS_SUCCEEDED(status)) {
2080 // We can gain a huge performance win by not even trying to
2081 // parse non-XML data. This also protects us from the situation
2082 // where we have an XML document and sink, but HTML (or other)
2083 // parser, which can produce unreliable results.
2084 nsAutoCString type;
2085 channel->GetContentType(type);
2087 if ((mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT) &&
2088 type.EqualsLiteral("text/html")) {
2089 // HTML parsing is only supported for responseType == "document" to
2090 // avoid running the parser and, worse, populating responseXML for
2091 // legacy users of XHR who use responseType == "" for retrieving the
2092 // responseText of text/html resources. This legacy case is so common
2093 // that it's not useful to emit a warning about it.
2094 if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
2095 // We don't make cool new features available in the bad synchronous
2096 // mode. The synchronous mode is for legacy only.
2097 mWarnAboutSyncHtml = true;
2098 mState &= ~XML_HTTP_REQUEST_PARSEBODY;
2099 } else {
2100 mIsHtml = true;
2102 } else if (type.Find("xml") == kNotFound) {
2103 mState &= ~XML_HTTP_REQUEST_PARSEBODY;
2105 } else {
2106 // The request failed, so we shouldn't be parsing anyway
2107 mState &= ~XML_HTTP_REQUEST_PARSEBODY;
2110 if (mState & XML_HTTP_REQUEST_PARSEBODY) {
2111 nsCOMPtr<nsIURI> baseURI, docURI;
2112 rv = mChannel->GetURI(getter_AddRefs(docURI));
2113 NS_ENSURE_SUCCESS(rv, rv);
2114 baseURI = docURI;
2116 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
2117 NS_ENSURE_SUCCESS(rv, rv);
2118 nsCOMPtr<nsIDocument> doc =
2119 nsContentUtils::GetDocumentFromScriptContext(sc);
2120 nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
2121 if (doc) {
2122 chromeXHRDocURI = doc->GetDocumentURI();
2123 chromeXHRDocBaseURI = doc->GetBaseURI();
2126 // Create an empty document from it.
2127 const nsAString& emptyStr = EmptyString();
2128 nsCOMPtr<nsIDOMDocument> responseDoc;
2129 nsIGlobalObject* global = DOMEventTargetHelper::GetParentObject();
2131 nsCOMPtr<nsIPrincipal> requestingPrincipal;
2132 rv = nsContentUtils::GetSecurityManager()->
2133 GetChannelResultPrincipal(channel, getter_AddRefs(requestingPrincipal));
2134 NS_ENSURE_SUCCESS(rv, rv);
2136 rv = NS_NewDOMDocument(getter_AddRefs(responseDoc),
2137 emptyStr, emptyStr, nullptr, docURI,
2138 baseURI, requestingPrincipal, true, global,
2139 mIsHtml ? DocumentFlavorHTML :
2140 DocumentFlavorLegacyGuess);
2141 NS_ENSURE_SUCCESS(rv, rv);
2142 mResponseXML = do_QueryInterface(responseDoc);
2143 mResponseXML->SetChromeXHRDocURI(chromeXHRDocURI);
2144 mResponseXML->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI);
2146 if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
2147 mResponseXML->ForceEnableXULXBL();
2150 if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
2151 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
2152 if (htmlDoc) {
2153 htmlDoc->DisableCookieAccess();
2157 nsCOMPtr<nsIStreamListener> listener;
2158 nsCOMPtr<nsILoadGroup> loadGroup;
2159 channel->GetLoadGroup(getter_AddRefs(loadGroup));
2161 rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
2162 nullptr, getter_AddRefs(listener),
2163 !(mState & XML_HTTP_REQUEST_USE_XSITE_AC));
2164 NS_ENSURE_SUCCESS(rv, rv);
2166 mXMLParserStreamListener = listener;
2167 rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
2168 NS_ENSURE_SUCCESS(rv, rv);
2171 // We won't get any progress events anyway if we didn't have progress
2172 // events when starting the request - so maybe no need to start timer here.
2173 if (NS_SUCCEEDED(rv) &&
2174 (mState & XML_HTTP_REQUEST_ASYNC) &&
2175 HasListenersFor(nsGkAtoms::onprogress)) {
2176 StartProgressEventTimer();
2179 return NS_OK;
2182 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status, in wstring statusArg); */
2183 NS_IMETHODIMP
2184 nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
2186 PROFILER_LABEL("nsXMLHttpRequest", "OnStopRequest",
2187 js::ProfileEntry::Category::NETWORK);
2189 if (request != mChannel) {
2190 // Can this still happen?
2191 return NS_OK;
2194 mWaitingForOnStopRequest = false;
2196 if (mRequestObserver) {
2197 NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
2198 mFirstStartRequestSeen = false;
2199 mRequestObserver->OnStopRequest(request, ctxt, status);
2202 // make sure to notify the listener if we were aborted
2203 // XXX in fact, why don't we do the cleanup below in this case??
2204 // XML_HTTP_REQUEST_UNSENT is for abort calls. See OnStartRequest above.
2205 if ((mState & XML_HTTP_REQUEST_UNSENT) ||
2206 (mState & XML_HTTP_REQUEST_TIMED_OUT)) {
2207 if (mXMLParserStreamListener)
2208 (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
2209 return NS_OK;
2212 // Is this good enough here?
2213 if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
2214 mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
2217 mXMLParserStreamListener = nullptr;
2218 mContext = nullptr;
2220 // If we're received data since the last progress event, make sure to fire
2221 // an event for it, except in the HTML case, defer the last progress event
2222 // until the parser is done.
2223 if (!mIsHtml) {
2224 MaybeDispatchProgressEvents(true);
2227 if (NS_SUCCEEDED(status) &&
2228 (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
2229 mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB)) {
2230 if (!mDOMFile) {
2231 CreateDOMFile(request);
2233 if (mDOMFile) {
2234 mResponseBlob = mDOMFile;
2235 mDOMFile = nullptr;
2236 } else {
2237 // mBlobSet can be null if the channel is non-file non-cacheable
2238 // and if the response length is zero.
2239 if (!mBlobSet) {
2240 mBlobSet = new BlobSet();
2242 // Smaller files may be written in cache map instead of separate files.
2243 // Also, no-store response cannot be written in persistent cache.
2244 nsAutoCString contentType;
2245 mChannel->GetContentType(contentType);
2246 mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType);
2247 mBlobSet = nullptr;
2249 NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
2250 NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
2251 } else if (NS_SUCCEEDED(status) &&
2252 ((mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
2253 !mIsMappedArrayBuffer) ||
2254 mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
2255 // set the capacity down to the actual length, to realloc back
2256 // down to the actual size
2257 if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
2258 // this should never happen!
2259 status = NS_ERROR_UNEXPECTED;
2263 nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
2264 NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
2266 channel->SetNotificationCallbacks(nullptr);
2267 mNotificationCallbacks = nullptr;
2268 mChannelEventSink = nullptr;
2269 mProgressEventSink = nullptr;
2271 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
2273 if (NS_FAILED(status)) {
2274 // This can happen if the server is unreachable. Other possible
2275 // reasons are that the user leaves the page or hits the ESC key.
2277 mErrorLoad = true;
2278 mResponseXML = nullptr;
2281 // If we're uninitialized at this point, we encountered an error
2282 // earlier and listeners have already been notified. Also we do
2283 // not want to do this if we already completed.
2284 if (mState & (XML_HTTP_REQUEST_UNSENT |
2285 XML_HTTP_REQUEST_DONE)) {
2286 return NS_OK;
2289 if (!mResponseXML) {
2290 ChangeStateToDone();
2291 return NS_OK;
2293 if (mIsHtml) {
2294 NS_ASSERTION(!(mState & XML_HTTP_REQUEST_SYNCLOOPING),
2295 "We weren't supposed to support HTML parsing with XHR!");
2296 nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
2297 EventListenerManager* manager =
2298 eventTarget->GetOrCreateListenerManager();
2299 manager->AddEventListenerByType(new nsXHRParseEndListener(this),
2300 NS_LITERAL_STRING("DOMContentLoaded"),
2301 TrustedEventsAtSystemGroupBubble());
2302 return NS_OK;
2304 // We might have been sent non-XML data. If that was the case,
2305 // we should null out the document member. The idea in this
2306 // check here is that if there is no document element it is not
2307 // an XML document. We might need a fancier check...
2308 if (!mResponseXML->GetRootElement()) {
2309 mResponseXML = nullptr;
2311 ChangeStateToDone();
2312 return NS_OK;
2315 void
2316 nsXMLHttpRequest::ChangeStateToDone()
2318 if (mIsHtml) {
2319 // In the HTML case, this has to be deferred, because the parser doesn't
2320 // do it's job synchronously.
2321 MaybeDispatchProgressEvents(true);
2324 ChangeState(XML_HTTP_REQUEST_DONE, true);
2325 if (mTimeoutTimer) {
2326 mTimeoutTimer->Cancel();
2329 NS_NAMED_LITERAL_STRING(errorStr, ERROR_STR);
2330 NS_NAMED_LITERAL_STRING(loadStr, LOAD_STR);
2331 DispatchProgressEvent(this,
2332 mErrorLoad ? errorStr : loadStr,
2333 !mErrorLoad,
2334 mLoadTransferred,
2335 mErrorLoad ? 0 : mLoadTransferred);
2336 if (mErrorLoad && mUpload && !mUploadComplete) {
2337 DispatchProgressEvent(mUpload, errorStr, true,
2338 mUploadTransferred, mUploadTotal);
2341 if (mErrorLoad) {
2342 // By nulling out channel here we make it so that Send() can test
2343 // for that and throw. Also calling the various status
2344 // methods/members will not throw.
2345 // This matches what IE does.
2346 mChannel = nullptr;
2347 mCORSPreflightChannel = nullptr;
2351 NS_IMETHODIMP
2352 nsXMLHttpRequest::SendAsBinary(const nsAString &aBody)
2354 ErrorResult rv;
2355 SendAsBinary(aBody, rv);
2356 return rv.ErrorCode();
2359 void
2360 nsXMLHttpRequest::SendAsBinary(const nsAString &aBody,
2361 ErrorResult& aRv)
2363 char *data = static_cast<char*>(NS_Alloc(aBody.Length() + 1));
2364 if (!data) {
2365 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2366 return;
2369 if (GetOwner() && GetOwner()->GetExtantDoc()) {
2370 GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSendAsBinary);
2373 nsAString::const_iterator iter, end;
2374 aBody.BeginReading(iter);
2375 aBody.EndReading(end);
2376 char *p = data;
2377 while (iter != end) {
2378 if (*iter & 0xFF00) {
2379 NS_Free(data);
2380 aRv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
2381 return;
2383 *p++ = static_cast<char>(*iter++);
2385 *p = '\0';
2387 nsCOMPtr<nsIInputStream> stream;
2388 aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, aBody.Length(),
2389 NS_ASSIGNMENT_ADOPT);
2390 if (aRv.Failed()) {
2391 NS_Free(data);
2392 return;
2395 nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
2397 aRv = variant->SetAsISupports(stream);
2398 if (aRv.Failed()) {
2399 return;
2402 aRv = Send(variant);
2405 static nsresult
2406 GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
2407 uint64_t* aContentLength, nsACString& aContentType,
2408 nsACString& aCharset)
2410 aContentType.AssignLiteral("application/xml");
2411 nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDoc));
2412 NS_ENSURE_STATE(doc);
2413 aCharset.AssignLiteral("UTF-8");
2415 nsresult rv;
2416 nsCOMPtr<nsIDOMSerializer> serializer =
2417 do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
2418 NS_ENSURE_SUCCESS(rv, rv);
2420 nsCOMPtr<nsIStorageStream> storStream;
2421 rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
2422 NS_ENSURE_SUCCESS(rv, rv);
2424 nsCOMPtr<nsIOutputStream> output;
2425 rv = storStream->GetOutputStream(0, getter_AddRefs(output));
2426 NS_ENSURE_SUCCESS(rv, rv);
2428 // Make sure to use the encoding we'll send
2429 rv = serializer->SerializeToStream(aDoc, output, aCharset);
2430 NS_ENSURE_SUCCESS(rv, rv);
2432 output->Close();
2434 uint32_t length;
2435 rv = storStream->GetLength(&length);
2436 NS_ENSURE_SUCCESS(rv, rv);
2437 *aContentLength = length;
2439 return storStream->NewInputStream(0, aResult);
2442 static nsresult
2443 GetRequestBody(const nsAString& aString, nsIInputStream** aResult,
2444 uint64_t* aContentLength, nsACString& aContentType,
2445 nsACString& aCharset)
2447 aContentType.AssignLiteral("text/plain");
2448 aCharset.AssignLiteral("UTF-8");
2450 nsCString converted = NS_ConvertUTF16toUTF8(aString);
2451 *aContentLength = converted.Length();
2452 return NS_NewCStringInputStream(aResult, converted);
2455 static nsresult
2456 GetRequestBody(nsIInputStream* aStream, nsIInputStream** aResult,
2457 uint64_t* aContentLength, nsACString& aContentType,
2458 nsACString& aCharset)
2460 aContentType.AssignLiteral("text/plain");
2461 aCharset.Truncate();
2463 nsresult rv = aStream->Available(aContentLength);
2464 NS_ENSURE_SUCCESS(rv, rv);
2466 NS_ADDREF(*aResult = aStream);
2468 return NS_OK;
2471 static nsresult
2472 GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult, uint64_t* aContentLength,
2473 nsACString& aContentType, nsACString& aCharset)
2475 return aSendable->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
2478 // Used for array buffers and array buffer views
2479 static nsresult
2480 GetRequestBody(const uint8_t* aData, uint32_t aDataLength,
2481 nsIInputStream** aResult, uint64_t* aContentLength,
2482 nsACString& aContentType, nsACString& aCharset)
2484 aContentType.SetIsVoid(true);
2485 aCharset.Truncate();
2487 *aContentLength = aDataLength;
2488 const char* data = reinterpret_cast<const char*>(aData);
2490 nsCOMPtr<nsIInputStream> stream;
2491 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
2492 NS_ASSIGNMENT_COPY);
2493 NS_ENSURE_SUCCESS(rv, rv);
2495 stream.forget(aResult);
2497 return NS_OK;
2500 static nsresult
2501 GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLength,
2502 nsACString& aContentType, nsACString& aCharset)
2504 *aResult = nullptr;
2506 uint16_t dataType;
2507 nsresult rv = aBody->GetDataType(&dataType);
2508 NS_ENSURE_SUCCESS(rv, rv);
2510 if (dataType == nsIDataType::VTYPE_INTERFACE ||
2511 dataType == nsIDataType::VTYPE_INTERFACE_IS) {
2512 nsCOMPtr<nsISupports> supports;
2513 nsID *iid;
2514 rv = aBody->GetAsInterface(&iid, getter_AddRefs(supports));
2515 NS_ENSURE_SUCCESS(rv, rv);
2517 nsMemory::Free(iid);
2519 // document?
2520 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(supports);
2521 if (doc) {
2522 return GetRequestBody(doc, aResult, aContentLength, aContentType, aCharset);
2525 // nsISupportsString?
2526 nsCOMPtr<nsISupportsString> wstr = do_QueryInterface(supports);
2527 if (wstr) {
2528 nsAutoString string;
2529 wstr->GetData(string);
2531 return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
2534 // nsIInputStream?
2535 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
2536 if (stream) {
2537 return GetRequestBody(stream, aResult, aContentLength, aContentType, aCharset);
2540 // nsIXHRSendable?
2541 nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
2542 if (sendable) {
2543 return GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
2546 // ArrayBuffer?
2547 AutoSafeJSContext cx;
2548 JS::Rooted<JS::Value> realVal(cx);
2550 nsresult rv = aBody->GetAsJSVal(&realVal);
2551 if (NS_SUCCEEDED(rv) && !realVal.isPrimitive()) {
2552 JS::Rooted<JSObject*> obj(cx, realVal.toObjectOrNull());
2553 ArrayBuffer buf;
2554 if (buf.Init(obj)) {
2555 buf.ComputeLengthAndData();
2556 return GetRequestBody(buf.Data(), buf.Length(), aResult,
2557 aContentLength, aContentType, aCharset);
2561 else if (dataType == nsIDataType::VTYPE_VOID ||
2562 dataType == nsIDataType::VTYPE_EMPTY) {
2563 // Makes us act as if !aBody, don't upload anything
2564 aContentType.AssignLiteral("text/plain");
2565 aCharset.AssignLiteral("UTF-8");
2566 *aContentLength = 0;
2568 return NS_OK;
2571 char16_t* data = nullptr;
2572 uint32_t len = 0;
2573 rv = aBody->GetAsWStringWithSize(&len, &data);
2574 NS_ENSURE_SUCCESS(rv, rv);
2576 nsString string;
2577 string.Adopt(data, len);
2579 return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
2582 /* static */
2583 nsresult
2584 nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
2585 const Nullable<RequestBody>& aBody,
2586 nsIInputStream** aResult,
2587 uint64_t* aContentLength,
2588 nsACString& aContentType, nsACString& aCharset)
2590 if (aVariant) {
2591 return ::GetRequestBody(aVariant, aResult, aContentLength, aContentType, aCharset);
2594 const RequestBody& body = aBody.Value();
2595 RequestBody::Value value = body.GetValue();
2596 switch (body.GetType()) {
2597 case nsXMLHttpRequest::RequestBody::ArrayBuffer:
2599 const ArrayBuffer* buffer = value.mArrayBuffer;
2600 buffer->ComputeLengthAndData();
2601 return ::GetRequestBody(buffer->Data(), buffer->Length(), aResult,
2602 aContentLength, aContentType, aCharset);
2604 case nsXMLHttpRequest::RequestBody::ArrayBufferView:
2606 const ArrayBufferView* view = value.mArrayBufferView;
2607 view->ComputeLengthAndData();
2608 return ::GetRequestBody(view->Data(), view->Length(), aResult,
2609 aContentLength, aContentType, aCharset);
2611 case nsXMLHttpRequest::RequestBody::Blob:
2613 nsresult rv;
2614 nsCOMPtr<nsIDOMBlob> blob = value.mBlob;
2615 nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(blob, &rv);
2616 NS_ENSURE_SUCCESS(rv, rv);
2618 return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
2620 case nsXMLHttpRequest::RequestBody::Document:
2622 nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(value.mDocument);
2623 return ::GetRequestBody(document, aResult, aContentLength, aContentType, aCharset);
2625 case nsXMLHttpRequest::RequestBody::DOMString:
2627 return ::GetRequestBody(*value.mString, aResult, aContentLength,
2628 aContentType, aCharset);
2630 case nsXMLHttpRequest::RequestBody::FormData:
2632 MOZ_ASSERT(value.mFormData);
2633 return ::GetRequestBody(value.mFormData, aResult, aContentLength,
2634 aContentType, aCharset);
2636 case nsXMLHttpRequest::RequestBody::InputStream:
2638 return ::GetRequestBody(value.mStream, aResult, aContentLength,
2639 aContentType, aCharset);
2641 default:
2643 return NS_ERROR_FAILURE;
2647 NS_NOTREACHED("Default cases exist for a reason");
2648 return NS_OK;
2651 /* void send (in nsIVariant aBody); */
2652 NS_IMETHODIMP
2653 nsXMLHttpRequest::Send(nsIVariant *aBody)
2655 return Send(aBody, Nullable<RequestBody>());
2658 nsresult
2659 nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
2661 NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
2663 nsresult rv = CheckInnerWindowCorrectness();
2664 NS_ENSURE_SUCCESS(rv, rv);
2666 // Return error if we're already processing a request
2667 if (XML_HTTP_REQUEST_SENT & mState) {
2668 return NS_ERROR_FAILURE;
2671 // Make sure we've been opened
2672 if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
2673 return NS_ERROR_NOT_INITIALIZED;
2677 // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
2678 // in turn keeps STOP button from becoming active. If the consumer passed in
2679 // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
2680 // necko won't generate any progress notifications.
2681 if (HasListenersFor(nsGkAtoms::onprogress) ||
2682 (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress))) {
2683 nsLoadFlags loadFlags;
2684 mChannel->GetLoadFlags(&loadFlags);
2685 loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
2686 loadFlags |= nsIRequest::LOAD_NORMAL;
2687 mChannel->SetLoadFlags(loadFlags);
2690 // XXX We should probably send a warning to the JS console
2691 // if there are no event listeners set and we are doing
2692 // an asynchronous call.
2694 // Ignore argument if method is GET, there is no point in trying to
2695 // upload anything
2696 nsAutoCString method;
2697 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2699 if (httpChannel) {
2700 httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
2702 if (!IsSystemXHR()) {
2703 // Get the referrer for the request.
2705 // If it weren't for history.push/replaceState, we could just use the
2706 // principal's URI here. But since we want changes to the URI effected
2707 // by push/replaceState to be reflected in the XHR referrer, we have to
2708 // be more clever.
2710 // If the document's original URI (before any push/replaceStates) matches
2711 // our principal, then we use the document's current URI (after
2712 // push/replaceStates). Otherwise (if the document is, say, a data:
2713 // URI), we just use the principal's URI.
2715 nsCOMPtr<nsIURI> principalURI;
2716 mPrincipal->GetURI(getter_AddRefs(principalURI));
2718 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
2719 NS_ENSURE_SUCCESS(rv, rv);
2720 nsCOMPtr<nsIDocument> doc =
2721 nsContentUtils::GetDocumentFromScriptContext(sc);
2723 nsCOMPtr<nsIURI> docCurURI;
2724 nsCOMPtr<nsIURI> docOrigURI;
2725 net::ReferrerPolicy referrerPolicy = net::RP_Default;
2727 if (doc) {
2728 docCurURI = doc->GetDocumentURI();
2729 docOrigURI = doc->GetOriginalURI();
2730 referrerPolicy = doc->GetReferrerPolicy();
2733 nsCOMPtr<nsIURI> referrerURI;
2735 if (principalURI && docCurURI && docOrigURI) {
2736 bool equal = false;
2737 principalURI->Equals(docOrigURI, &equal);
2738 if (equal) {
2739 referrerURI = docCurURI;
2743 if (!referrerURI)
2744 referrerURI = principalURI;
2746 httpChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
2749 // Some extensions override the http protocol handler and provide their own
2750 // implementation. The channels returned from that implementation doesn't
2751 // seem to always implement the nsIUploadChannel2 interface, presumably
2752 // because it's a new interface.
2753 // Eventually we should remove this and simply require that http channels
2754 // implement the new interface.
2755 // See bug 529041
2756 nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
2757 do_QueryInterface(httpChannel);
2758 if (!uploadChannel2) {
2759 nsCOMPtr<nsIConsoleService> consoleService =
2760 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2761 if (consoleService) {
2762 consoleService->LogStringMessage(NS_LITERAL_STRING(
2763 "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."
2764 ).get());
2769 mUploadTransferred = 0;
2770 mUploadTotal = 0;
2771 // By default we don't have any upload, so mark upload complete.
2772 mUploadComplete = true;
2773 mErrorLoad = false;
2774 mLoadLengthComputable = false;
2775 mLoadTotal = 0;
2776 if ((aVariant || !aBody.IsNull()) && httpChannel &&
2777 !method.LowerCaseEqualsLiteral("get") &&
2778 !method.LowerCaseEqualsLiteral("head")) {
2780 nsAutoCString charset;
2781 nsAutoCString defaultContentType;
2782 nsCOMPtr<nsIInputStream> postDataStream;
2784 rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream),
2785 &mUploadTotal, defaultContentType, charset);
2786 NS_ENSURE_SUCCESS(rv, rv);
2788 if (postDataStream) {
2789 // If no content type header was set by the client, we set it to
2790 // application/xml.
2791 nsAutoCString contentType;
2792 if (NS_FAILED(httpChannel->
2793 GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
2794 contentType)) ||
2795 contentType.IsEmpty()) {
2796 contentType = defaultContentType;
2799 // We don't want to set a charset for streams.
2800 if (!charset.IsEmpty()) {
2801 nsAutoCString specifiedCharset;
2802 bool haveCharset;
2803 int32_t charsetStart, charsetEnd;
2804 rv = NS_ExtractCharsetFromContentType(contentType, specifiedCharset,
2805 &haveCharset, &charsetStart,
2806 &charsetEnd);
2807 if (NS_SUCCEEDED(rv)) {
2808 // special case: the extracted charset is quoted with single quotes
2809 // -- for the purpose of preserving what was set we want to handle
2810 // them as delimiters (although they aren't really)
2811 if (specifiedCharset.Length() >= 2 &&
2812 specifiedCharset.First() == '\'' &&
2813 specifiedCharset.Last() == '\'') {
2814 specifiedCharset = Substring(specifiedCharset, 1,
2815 specifiedCharset.Length() - 2);
2818 // If the content-type the page set already has a charset parameter,
2819 // and it's the same charset, up to case, as |charset|, just send the
2820 // page-set content-type header. Apparently at least
2821 // google-web-toolkit is broken and relies on the exact case of its
2822 // charset parameter, which makes things break if we use |charset|
2823 // (which is always a fully resolved charset per our charset alias
2824 // table, hence might be differently cased).
2825 if (!specifiedCharset.Equals(charset,
2826 nsCaseInsensitiveCStringComparator())) {
2827 nsAutoCString newCharset("; charset=");
2828 newCharset.Append(charset);
2829 contentType.Replace(charsetStart, charsetEnd - charsetStart,
2830 newCharset);
2835 // If necessary, wrap the stream in a buffered stream so as to guarantee
2836 // support for our upload when calling ExplicitSetUploadStream.
2837 if (!NS_InputStreamIsBuffered(postDataStream)) {
2838 nsCOMPtr<nsIInputStream> bufferedStream;
2839 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
2840 postDataStream,
2841 4096);
2842 NS_ENSURE_SUCCESS(rv, rv);
2844 postDataStream = bufferedStream;
2847 mUploadComplete = false;
2849 // We want to use a newer version of the upload channel that won't
2850 // ignore the necessary headers for an empty Content-Type.
2851 nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
2852 // This assertion will fire if buggy extensions are installed
2853 NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
2854 if (uploadChannel2) {
2855 uploadChannel2->ExplicitSetUploadStream(postDataStream, contentType,
2856 mUploadTotal, method, false);
2858 else {
2859 // http channel doesn't support the new nsIUploadChannel2. Emulate
2860 // as best we can using nsIUploadChannel
2861 if (contentType.IsEmpty()) {
2862 contentType.AssignLiteral("application/octet-stream");
2864 nsCOMPtr<nsIUploadChannel> uploadChannel =
2865 do_QueryInterface(httpChannel);
2866 uploadChannel->SetUploadStream(postDataStream, contentType, mUploadTotal);
2867 // Reset the method to its original value
2868 httpChannel->SetRequestMethod(method);
2873 if (httpChannel) {
2874 nsAutoCString contentTypeHeader;
2875 rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
2876 contentTypeHeader);
2877 if (NS_SUCCEEDED(rv)) {
2878 if (!nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader)) {
2879 mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
2884 ResetResponse();
2886 rv = CheckChannelForCrossSiteRequest(mChannel);
2887 NS_ENSURE_SUCCESS(rv, rv);
2889 bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
2891 // Hook us up to listen to redirects and the like
2892 mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
2893 mChannel->SetNotificationCallbacks(this);
2895 // Blocking gets are common enough out of XHR that we should mark
2896 // the channel slow by default for pipeline purposes
2897 AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
2899 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
2900 if (cos) {
2901 // we never let XHR be blocked by head CSS/JS loads to avoid
2902 // potential deadlock where server generation of CSS/JS requires
2903 // an XHR signal.
2904 cos->AddClassFlags(nsIClassOfService::Unblocked);
2907 nsCOMPtr<nsIHttpChannelInternal>
2908 internalHttpChannel(do_QueryInterface(mChannel));
2909 if (internalHttpChannel) {
2910 // Disable Necko-internal response timeouts.
2911 internalHttpChannel->SetResponseTimeoutEnabled(false);
2914 nsCOMPtr<nsIStreamListener> listener = this;
2915 if (!IsSystemXHR()) {
2916 // Always create a nsCORSListenerProxy here even if it's
2917 // a same-origin request right now, since it could be redirected.
2918 nsRefPtr<nsCORSListenerProxy> corsListener =
2919 new nsCORSListenerProxy(listener, mPrincipal, withCredentials);
2920 rv = corsListener->Init(mChannel, true);
2921 NS_ENSURE_SUCCESS(rv, rv);
2922 listener = corsListener;
2924 else {
2925 // Because of bug 682305, we can't let listener be the XHR object itself
2926 // because JS wouldn't be able to use it. So if we haven't otherwise
2927 // created a listener around 'this', do so now.
2929 listener = new nsStreamListenerWrapper(listener);
2932 if (mIsAnon) {
2933 AddLoadFlags(mChannel, nsIRequest::LOAD_ANONYMOUS);
2935 else {
2936 AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
2939 NS_ASSERTION(listener != this,
2940 "Using an object as a listener that can't be exposed to JS");
2942 // Bypass the network cache in cases where it makes no sense:
2943 // POST responses are always unique, and we provide no API that would
2944 // allow our consumers to specify a "cache key" to access old POST
2945 // responses, so they are not worth caching.
2946 if (method.EqualsLiteral("POST")) {
2947 AddLoadFlags(mChannel,
2948 nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
2950 // When we are sync loading, we need to bypass the local cache when it would
2951 // otherwise block us waiting for exclusive access to the cache. If we don't
2952 // do this, then we could dead lock in some cases (see bug 309424).
2953 else if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
2954 AddLoadFlags(mChannel,
2955 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
2958 // Since we expect XML data, set the type hint accordingly
2959 // if the channel doesn't know any content type.
2960 // This means that we always try to parse local files as XML
2961 // ignoring return value, as this is not critical
2962 nsAutoCString contentType;
2963 if (NS_FAILED(mChannel->GetContentType(contentType)) ||
2964 contentType.IsEmpty() ||
2965 contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
2966 mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
2969 // We're about to send the request. Start our timeout.
2970 mRequestSentTime = PR_Now();
2971 StartTimeoutTimer();
2973 // Set up the preflight if needed
2974 if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
2975 // Check to see if this initial OPTIONS request has already been cached
2976 // in our special Access Control Cache.
2978 rv = NS_StartCORSPreflight(mChannel, listener,
2979 mPrincipal, withCredentials,
2980 mCORSUnsafeHeaders,
2981 getter_AddRefs(mCORSPreflightChannel));
2982 NS_ENSURE_SUCCESS(rv, rv);
2984 else {
2985 mIsMappedArrayBuffer = false;
2986 if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
2987 Preferences::GetBool("dom.mapped_arraybuffer.enabled", false)) {
2988 nsCOMPtr<nsIURI> uri;
2989 nsAutoCString scheme;
2991 rv = mChannel->GetURI(getter_AddRefs(uri));
2992 if (NS_SUCCEEDED(rv)) {
2993 uri->GetScheme(scheme);
2994 if (scheme.LowerCaseEqualsLiteral("app") ||
2995 scheme.LowerCaseEqualsLiteral("jar")) {
2996 mIsMappedArrayBuffer = true;
2997 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2998 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(mChannel);
2999 // For memory mapping from child process, we need to get file
3000 // descriptor of the JAR file opened remotely on the parent proess.
3001 // Set this to make sure that file descriptor can be obtained by
3002 // child process.
3003 jarChannel->EnsureChildFd();
3008 // Start reading from the channel
3009 rv = mChannel->AsyncOpen(listener, nullptr);
3012 if (NS_FAILED(rv)) {
3013 // Drop our ref to the channel to avoid cycles
3014 mChannel = nullptr;
3015 mCORSPreflightChannel = nullptr;
3016 return rv;
3019 // Either AsyncOpen was called, or CORS will open the channel later.
3020 mWaitingForOnStopRequest = true;
3022 // If we're synchronous, spin an event loop here and wait
3023 if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
3024 mState |= XML_HTTP_REQUEST_SYNCLOOPING;
3026 nsCOMPtr<nsIDocument> suspendedDoc;
3027 nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
3028 if (GetOwner()) {
3029 nsCOMPtr<nsIDOMWindow> topWindow;
3030 if (NS_SUCCEEDED(GetOwner()->GetTop(getter_AddRefs(topWindow)))) {
3031 nsCOMPtr<nsPIDOMWindow> suspendedWindow(do_QueryInterface(topWindow));
3032 if (suspendedWindow &&
3033 (suspendedWindow = suspendedWindow->GetCurrentInnerWindow())) {
3034 suspendedDoc = suspendedWindow->GetExtantDoc();
3035 if (suspendedDoc) {
3036 suspendedDoc->SuppressEventHandling(nsIDocument::eEvents);
3038 suspendedWindow->SuspendTimeouts(1, false);
3039 resumeTimeoutRunnable = new nsResumeTimeoutsEvent(suspendedWindow);
3044 ChangeState(XML_HTTP_REQUEST_SENT);
3047 nsAutoSyncOperation sync(suspendedDoc);
3048 // Note, calling ChangeState may have cleared
3049 // XML_HTTP_REQUEST_SYNCLOOPING flag.
3050 nsIThread *thread = NS_GetCurrentThread();
3051 while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
3052 if (!NS_ProcessNextEvent(thread)) {
3053 rv = NS_ERROR_UNEXPECTED;
3054 break;
3059 if (suspendedDoc) {
3060 suspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents,
3061 true);
3064 if (resumeTimeoutRunnable) {
3065 NS_DispatchToCurrentThread(resumeTimeoutRunnable);
3067 } else {
3068 // Now that we've successfully opened the channel, we can change state. Note
3069 // that this needs to come after the AsyncOpen() and rv check, because this
3070 // can run script that would try to restart this request, and that could end
3071 // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
3072 ChangeState(XML_HTTP_REQUEST_SENT);
3073 if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
3074 StartProgressEventTimer();
3076 DispatchProgressEvent(this, NS_LITERAL_STRING(LOADSTART_STR), false,
3077 0, 0);
3078 if (mUpload && !mUploadComplete) {
3079 DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOADSTART_STR), true,
3080 0, mUploadTotal);
3084 if (!mChannel) {
3085 return NS_ERROR_FAILURE;
3088 return rv;
3091 /* void setRequestHeader (in ByteString header, in ByteString value); */
3092 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
3093 NS_IMETHODIMP
3094 nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
3095 const nsACString& value)
3097 // Step 1 and 2
3098 if (!(mState & XML_HTTP_REQUEST_OPENED)) {
3099 return NS_ERROR_DOM_INVALID_STATE_ERR;
3101 NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED.");
3103 // Step 3
3104 // Make sure we don't store an invalid header name in mCORSUnsafeHeaders
3105 if (!NS_IsValidHTTPToken(header)) {
3106 return NS_ERROR_DOM_SYNTAX_ERR;
3109 // Check that we haven't already opened the channel. We can't rely on
3110 // the channel throwing from mChannel->SetRequestHeader since we might
3111 // still be waiting for mCORSPreflightChannel to actually open mChannel
3112 if (mCORSPreflightChannel) {
3113 bool pending;
3114 nsresult rv = mCORSPreflightChannel->IsPending(&pending);
3115 NS_ENSURE_SUCCESS(rv, rv);
3117 if (pending) {
3118 return NS_ERROR_IN_PROGRESS;
3122 if (!mChannel) // open() initializes mChannel, and open()
3123 return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
3125 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
3126 if (!httpChannel) {
3127 return NS_OK;
3130 // We will merge XHR headers, per the spec (secion 4.6.2) unless:
3131 // 1 - The caller is privileged and setting an invalid header,
3132 // or
3133 // 2 - we have not yet explicitly set that header; this allows web
3134 // content to override default headers the first time they set them.
3135 bool mergeHeaders = true;
3137 if (!IsSystemXHR()) {
3138 // Step 5: Check for dangerous headers.
3139 // Prevent modification to certain HTTP headers (see bug 302263), unless
3140 // the executing script is privileged.
3141 if (nsContentUtils::IsForbiddenRequestHeader(header)) {
3142 NS_WARNING("refusing to set request header");
3143 return NS_OK;
3146 // Check for dangerous cross-site headers
3147 bool safeHeader = IsSystemXHR();
3148 if (!safeHeader) {
3149 // Content-Type isn't always safe, but we'll deal with it in Send()
3150 const char *kCrossOriginSafeHeaders[] = {
3151 "accept", "accept-language", "content-language", "content-type",
3152 "last-event-id"
3154 for (uint32_t i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
3155 if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
3156 safeHeader = true;
3157 break;
3162 if (!safeHeader) {
3163 if (!mCORSUnsafeHeaders.Contains(header)) {
3164 mCORSUnsafeHeaders.AppendElement(header);
3167 } else {
3168 // Case 1 above
3169 if (nsContentUtils::IsForbiddenSystemRequestHeader(header)) {
3170 mergeHeaders = false;
3174 if (!mAlreadySetHeaders.Contains(header)) {
3175 // Case 2 above
3176 mergeHeaders = false;
3179 // Merge headers depending on what we decided above.
3180 nsresult rv = httpChannel->SetRequestHeader(header, value, mergeHeaders);
3181 if (rv == NS_ERROR_INVALID_ARG) {
3182 return NS_ERROR_DOM_SYNTAX_ERR;
3184 if (NS_SUCCEEDED(rv)) {
3185 // Remember that we've set this header, so subsequent set operations will merge values.
3186 mAlreadySetHeaders.PutEntry(nsCString(header));
3188 // We'll want to duplicate this header for any replacement channels (eg. on redirect)
3189 RequestHeader reqHeader = {
3190 nsCString(header), nsCString(value)
3192 mModifiedRequestHeaders.AppendElement(reqHeader);
3194 return rv;
3197 /* attribute unsigned long timeout; */
3198 NS_IMETHODIMP
3199 nsXMLHttpRequest::GetTimeout(uint32_t *aTimeout)
3201 *aTimeout = Timeout();
3202 return NS_OK;
3205 NS_IMETHODIMP
3206 nsXMLHttpRequest::SetTimeout(uint32_t aTimeout)
3208 ErrorResult rv;
3209 SetTimeout(aTimeout, rv);
3210 return rv.ErrorCode();
3213 void
3214 nsXMLHttpRequest::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
3216 if (!(mState & (XML_HTTP_REQUEST_ASYNC | XML_HTTP_REQUEST_UNSENT)) &&
3217 HasOrHasHadOwner()) {
3218 /* Timeout is not supported for synchronous requests with an owning window,
3219 per XHR2 spec. */
3220 LogMessage("TimeoutSyncXHRWarning", GetOwner());
3221 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3222 return;
3225 mTimeoutMilliseconds = aTimeout;
3226 if (mRequestSentTime) {
3227 StartTimeoutTimer();
3231 void
3232 nsXMLHttpRequest::StartTimeoutTimer()
3234 NS_ABORT_IF_FALSE(mRequestSentTime,
3235 "StartTimeoutTimer mustn't be called before the request was sent!");
3236 if (mState & XML_HTTP_REQUEST_DONE) {
3237 // do nothing!
3238 return;
3241 if (mTimeoutTimer) {
3242 mTimeoutTimer->Cancel();
3245 if (!mTimeoutMilliseconds) {
3246 return;
3249 if (!mTimeoutTimer) {
3250 mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
3252 uint32_t elapsed =
3253 (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
3254 mTimeoutTimer->InitWithCallback(
3255 this,
3256 mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
3257 nsITimer::TYPE_ONE_SHOT
3261 /* readonly attribute unsigned short readyState; */
3262 NS_IMETHODIMP
3263 nsXMLHttpRequest::GetReadyState(uint16_t *aState)
3265 *aState = ReadyState();
3266 return NS_OK;
3269 uint16_t
3270 nsXMLHttpRequest::ReadyState()
3272 // Translate some of our internal states for external consumers
3273 if (mState & XML_HTTP_REQUEST_UNSENT) {
3274 return UNSENT;
3276 if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
3277 return OPENED;
3279 if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
3280 return HEADERS_RECEIVED;
3282 if (mState & XML_HTTP_REQUEST_LOADING) {
3283 return LOADING;
3285 MOZ_ASSERT(mState & XML_HTTP_REQUEST_DONE);
3286 return DONE;
3289 /* void overrideMimeType(in DOMString mimetype); */
3290 NS_IMETHODIMP
3291 nsXMLHttpRequest::SlowOverrideMimeType(const nsAString& aMimeType)
3293 OverrideMimeType(aMimeType);
3294 return NS_OK;
3297 /* attribute boolean mozBackgroundRequest; */
3298 NS_IMETHODIMP
3299 nsXMLHttpRequest::GetMozBackgroundRequest(bool *_retval)
3301 *_retval = MozBackgroundRequest();
3302 return NS_OK;
3305 bool
3306 nsXMLHttpRequest::MozBackgroundRequest()
3308 return !!(mState & XML_HTTP_REQUEST_BACKGROUND);
3311 NS_IMETHODIMP
3312 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest)
3314 nsresult rv = NS_OK;
3315 SetMozBackgroundRequest(aMozBackgroundRequest, rv);
3316 return rv;
3319 void
3320 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest, nsresult& aRv)
3322 if (!IsSystemXHR()) {
3323 aRv = NS_ERROR_DOM_SECURITY_ERR;
3324 return;
3327 if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
3328 // Can't change this while we're in the middle of something.
3329 aRv = NS_ERROR_IN_PROGRESS;
3330 return;
3333 if (aMozBackgroundRequest) {
3334 mState |= XML_HTTP_REQUEST_BACKGROUND;
3335 } else {
3336 mState &= ~XML_HTTP_REQUEST_BACKGROUND;
3340 /* attribute boolean withCredentials; */
3341 NS_IMETHODIMP
3342 nsXMLHttpRequest::GetWithCredentials(bool *_retval)
3344 *_retval = WithCredentials();
3345 return NS_OK;
3348 bool
3349 nsXMLHttpRequest::WithCredentials()
3351 return !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
3354 NS_IMETHODIMP
3355 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials)
3357 ErrorResult rv;
3358 SetWithCredentials(aWithCredentials, rv);
3359 return rv.ErrorCode();
3362 void
3363 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
3365 // Return error if we're already processing a request
3366 if (XML_HTTP_REQUEST_SENT & mState) {
3367 aRv = NS_ERROR_FAILURE;
3368 return;
3371 // sync request is not allowed setting withCredentials in window context
3372 if (HasOrHasHadOwner() &&
3373 !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
3374 LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
3375 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3376 return;
3379 if (aWithCredentials) {
3380 mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
3381 } else {
3382 mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
3386 nsresult
3387 nsXMLHttpRequest::ChangeState(uint32_t aState, bool aBroadcast)
3389 // If we are setting one of the mutually exclusive states,
3390 // unset those state bits first.
3391 if (aState & XML_HTTP_REQUEST_LOADSTATES) {
3392 mState &= ~XML_HTTP_REQUEST_LOADSTATES;
3394 mState |= aState;
3395 nsresult rv = NS_OK;
3397 if (mProgressNotifier &&
3398 !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
3399 mProgressTimerIsActive = false;
3400 mProgressNotifier->Cancel();
3403 if ((aState & XML_HTTP_REQUEST_LOADSTATES) && // Broadcast load states only
3404 aState != XML_HTTP_REQUEST_SENT && // And not internal ones
3405 aBroadcast &&
3406 (mState & XML_HTTP_REQUEST_ASYNC ||
3407 aState & XML_HTTP_REQUEST_OPENED ||
3408 aState & XML_HTTP_REQUEST_DONE)) {
3409 nsCOMPtr<nsIDOMEvent> event;
3410 rv = CreateReadystatechangeEvent(getter_AddRefs(event));
3411 NS_ENSURE_SUCCESS(rv, rv);
3413 DispatchDOMEvent(nullptr, event, nullptr, nullptr);
3416 return rv;
3420 * Simple helper class that just forwards the redirect callback back
3421 * to the nsXMLHttpRequest.
3423 class AsyncVerifyRedirectCallbackForwarder MOZ_FINAL : public nsIAsyncVerifyRedirectCallback
3425 public:
3426 explicit AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest* xhr)
3427 : mXHR(xhr)
3431 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
3432 NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
3434 // nsIAsyncVerifyRedirectCallback implementation
3435 NS_IMETHOD OnRedirectVerifyCallback(nsresult result) MOZ_OVERRIDE
3437 mXHR->OnRedirectVerifyCallback(result);
3439 return NS_OK;
3442 private:
3443 ~AsyncVerifyRedirectCallbackForwarder() {}
3445 nsRefPtr<nsXMLHttpRequest> mXHR;
3448 NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder, mXHR)
3450 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
3451 NS_INTERFACE_MAP_ENTRY(nsISupports)
3452 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
3453 NS_INTERFACE_MAP_END
3455 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
3456 NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)
3459 /////////////////////////////////////////////////////
3460 // nsIChannelEventSink methods:
3462 NS_IMETHODIMP
3463 nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
3464 nsIChannel *aNewChannel,
3465 uint32_t aFlags,
3466 nsIAsyncVerifyRedirectCallback *callback)
3468 NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
3470 nsresult rv;
3472 if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
3473 rv = CheckChannelForCrossSiteRequest(aNewChannel);
3474 if (NS_FAILED(rv)) {
3475 NS_WARNING("nsXMLHttpRequest::OnChannelRedirect: "
3476 "CheckChannelForCrossSiteRequest returned failure");
3477 return rv;
3480 // Disable redirects for preflighted cross-site requests entirely for now
3481 // Note, do this after the call to CheckChannelForCrossSiteRequest
3482 // to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
3483 if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
3484 return NS_ERROR_DOM_BAD_URI;
3488 // Prepare to receive callback
3489 mRedirectCallback = callback;
3490 mNewRedirectChannel = aNewChannel;
3492 if (mChannelEventSink) {
3493 nsRefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
3494 new AsyncVerifyRedirectCallbackForwarder(this);
3496 rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
3497 aNewChannel,
3498 aFlags, fwd);
3499 if (NS_FAILED(rv)) {
3500 mRedirectCallback = nullptr;
3501 mNewRedirectChannel = nullptr;
3503 return rv;
3505 OnRedirectVerifyCallback(NS_OK);
3506 return NS_OK;
3509 void
3510 nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
3512 NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
3513 NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
3515 if (NS_SUCCEEDED(result)) {
3516 mChannel = mNewRedirectChannel;
3518 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
3519 if (httpChannel) {
3520 // Ensure all original headers are duplicated for the new channel (bug #553888)
3521 for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) {
3522 --i;
3523 httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
3524 mModifiedRequestHeaders[i].value,
3525 false);
3528 } else {
3529 mErrorLoad = true;
3532 mNewRedirectChannel = nullptr;
3534 mRedirectCallback->OnRedirectVerifyCallback(result);
3535 mRedirectCallback = nullptr;
3538 /////////////////////////////////////////////////////
3539 // nsIProgressEventSink methods:
3542 void
3543 nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
3545 if (aFinalProgress && mProgressTimerIsActive) {
3546 mProgressTimerIsActive = false;
3547 mProgressNotifier->Cancel();
3550 if (mProgressTimerIsActive ||
3551 !mProgressSinceLastProgressEvent ||
3552 mErrorLoad ||
3553 !(mState & XML_HTTP_REQUEST_ASYNC)) {
3554 return;
3557 if (!aFinalProgress) {
3558 StartProgressEventTimer();
3561 // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
3562 // XML_HTTP_REQUEST_SENT
3563 if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
3564 if (mUpload && !mUploadComplete) {
3565 DispatchProgressEvent(mUpload, NS_LITERAL_STRING(PROGRESS_STR),
3566 mUploadLengthComputable, mUploadTransferred,
3567 mUploadTotal);
3569 } else {
3570 if (aFinalProgress) {
3571 mLoadTotal = mLoadTransferred;
3573 mInLoadProgressEvent = true;
3574 DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR),
3575 mLoadLengthComputable, mLoadTransferred,
3576 mLoadTotal);
3577 mInLoadProgressEvent = false;
3578 if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
3579 mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
3580 mResponseBody.Truncate();
3581 mResponseText.Truncate();
3582 mResultArrayBuffer = nullptr;
3583 mArrayBufferBuilder.reset();
3587 mProgressSinceLastProgressEvent = false;
3590 NS_IMETHODIMP
3591 nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, uint64_t aProgress, uint64_t aProgressMax)
3593 // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
3594 // XML_HTTP_REQUEST_SENT
3595 bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
3596 // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
3597 // So, try to remove the headers, if possible.
3598 bool lengthComputable = (aProgressMax != UINT64_MAX);
3599 if (upload) {
3600 uint64_t loaded = aProgress;
3601 if (lengthComputable) {
3602 uint64_t headerSize = aProgressMax - mUploadTotal;
3603 loaded -= headerSize;
3605 mUploadLengthComputable = lengthComputable;
3606 mUploadTransferred = loaded;
3607 mProgressSinceLastProgressEvent = true;
3609 MaybeDispatchProgressEvents(false);
3610 } else {
3611 mLoadLengthComputable = lengthComputable;
3612 mLoadTotal = lengthComputable ? aProgressMax : 0;
3613 mLoadTransferred = aProgress;
3614 // Don't dispatch progress events here. OnDataAvailable will take care
3615 // of that.
3618 if (mProgressEventSink) {
3619 mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
3620 aProgressMax);
3623 return NS_OK;
3626 NS_IMETHODIMP
3627 nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatus, const char16_t *aStatusArg)
3629 if (mProgressEventSink) {
3630 mProgressEventSink->OnStatus(aRequest, aContext, aStatus, aStatusArg);
3633 return NS_OK;
3636 bool
3637 nsXMLHttpRequest::AllowUploadProgress()
3639 return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
3640 (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
3643 /////////////////////////////////////////////////////
3644 // nsIInterfaceRequestor methods:
3646 NS_IMETHODIMP
3647 nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
3649 nsresult rv;
3651 // Make sure to return ourselves for the channel event sink interface and
3652 // progress event sink interface, no matter what. We can forward these to
3653 // mNotificationCallbacks if it wants to get notifications for them. But we
3654 // need to see these notifications for proper functioning.
3655 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
3656 mChannelEventSink = do_GetInterface(mNotificationCallbacks);
3657 *aResult = static_cast<nsIChannelEventSink*>(EnsureXPCOMifier().take());
3658 return NS_OK;
3659 } else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
3660 mProgressEventSink = do_GetInterface(mNotificationCallbacks);
3661 *aResult = static_cast<nsIProgressEventSink*>(EnsureXPCOMifier().take());
3662 return NS_OK;
3665 // Now give mNotificationCallbacks (if non-null) a chance to return the
3666 // desired interface.
3667 if (mNotificationCallbacks) {
3668 rv = mNotificationCallbacks->GetInterface(aIID, aResult);
3669 if (NS_SUCCEEDED(rv)) {
3670 NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
3671 return rv;
3675 if (mState & XML_HTTP_REQUEST_BACKGROUND) {
3676 nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
3678 // Ignore failure to get component, we may not have all its dependencies
3679 // available
3680 if (NS_SUCCEEDED(rv)) {
3681 rv = badCertHandler->GetInterface(aIID, aResult);
3682 if (NS_SUCCEEDED(rv))
3683 return rv;
3686 else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
3687 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
3689 nsCOMPtr<nsIURI> uri;
3690 rv = mChannel->GetURI(getter_AddRefs(uri));
3691 NS_ENSURE_SUCCESS(rv, rv);
3693 // Verify that it's ok to prompt for credentials here, per spec
3694 // http://xhr.spec.whatwg.org/#the-send%28%29-method
3695 bool showPrompt = true;
3697 // If authentication fails, XMLHttpRequest origin and
3698 // the request URL are same origin, ...
3699 /* Disabled - bug: 799540
3700 if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
3701 showPrompt = false;
3705 // ... Authorization is not in the list of author request headers, ...
3706 if (showPrompt) {
3707 for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
3708 if (mModifiedRequestHeaders[i].header.
3709 LowerCaseEqualsLiteral("authorization")) {
3710 showPrompt = false;
3711 break;
3716 // ... request username is null, and request password is null,
3717 if (showPrompt) {
3719 nsCString username;
3720 rv = uri->GetUsername(username);
3721 NS_ENSURE_SUCCESS(rv, rv);
3723 nsCString password;
3724 rv = uri->GetPassword(password);
3725 NS_ENSURE_SUCCESS(rv, rv);
3727 if (!username.IsEmpty() || !password.IsEmpty()) {
3728 showPrompt = false;
3732 // ... user agents should prompt the end user for their username and password.
3733 if (!showPrompt) {
3734 nsRefPtr<XMLHttpRequestAuthPrompt> prompt = new XMLHttpRequestAuthPrompt();
3735 if (!prompt)
3736 return NS_ERROR_OUT_OF_MEMORY;
3738 return prompt->QueryInterface(aIID, aResult);
3741 nsCOMPtr<nsIPromptFactory> wwatch =
3742 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
3743 NS_ENSURE_SUCCESS(rv, rv);
3745 // Get the an auth prompter for our window so that the parenting
3746 // of the dialogs works as it should when using tabs.
3748 nsCOMPtr<nsIDOMWindow> window;
3749 if (GetOwner()) {
3750 window = GetOwner()->GetOuterWindow();
3753 return wwatch->GetPrompt(window, aIID,
3754 reinterpret_cast<void**>(aResult));
3756 // Now check for the various XHR non-DOM interfaces, except
3757 // nsIProgressEventSink and nsIChannelEventSink which we already
3758 // handled above.
3759 else if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
3760 *aResult = static_cast<nsIStreamListener*>(EnsureXPCOMifier().take());
3761 return NS_OK;
3763 else if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
3764 *aResult = static_cast<nsIRequestObserver*>(EnsureXPCOMifier().take());
3765 return NS_OK;
3767 else if (aIID.Equals(NS_GET_IID(nsITimerCallback))) {
3768 *aResult = static_cast<nsITimerCallback*>(EnsureXPCOMifier().take());
3769 return NS_OK;
3772 return QueryInterface(aIID, aResult);
3775 void
3776 nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID,
3777 JS::MutableHandle<JS::Value> aRetval,
3778 ErrorResult& aRv)
3780 dom::GetInterface(aCx, this, aIID, aRetval, aRv);
3783 nsXMLHttpRequestUpload*
3784 nsXMLHttpRequest::Upload()
3786 if (!mUpload) {
3787 mUpload = new nsXMLHttpRequestUpload(this);
3789 return mUpload;
3792 NS_IMETHODIMP
3793 nsXMLHttpRequest::GetUpload(nsIXMLHttpRequestUpload** aUpload)
3795 nsRefPtr<nsXMLHttpRequestUpload> upload = Upload();
3796 upload.forget(aUpload);
3797 return NS_OK;
3800 bool
3801 nsXMLHttpRequest::MozAnon()
3803 return mIsAnon;
3806 NS_IMETHODIMP
3807 nsXMLHttpRequest::GetMozAnon(bool* aAnon)
3809 *aAnon = MozAnon();
3810 return NS_OK;
3813 bool
3814 nsXMLHttpRequest::MozSystem()
3816 return IsSystemXHR();
3819 NS_IMETHODIMP
3820 nsXMLHttpRequest::GetMozSystem(bool* aSystem)
3822 *aSystem = MozSystem();
3823 return NS_OK;
3826 void
3827 nsXMLHttpRequest::HandleTimeoutCallback()
3829 if (mState & XML_HTTP_REQUEST_DONE) {
3830 NS_NOTREACHED("nsXMLHttpRequest::HandleTimeoutCallback with completed request");
3831 // do nothing!
3832 return;
3835 CloseRequestWithError(NS_LITERAL_STRING(TIMEOUT_STR),
3836 XML_HTTP_REQUEST_TIMED_OUT);
3839 NS_IMETHODIMP
3840 nsXMLHttpRequest::Notify(nsITimer* aTimer)
3842 if (mProgressNotifier == aTimer) {
3843 HandleProgressTimerCallback();
3844 return NS_OK;
3847 if (mTimeoutTimer == aTimer) {
3848 HandleTimeoutCallback();
3849 return NS_OK;
3852 // Just in case some JS user wants to QI to nsITimerCallback and play with us...
3853 NS_WARNING("Unexpected timer!");
3854 return NS_ERROR_INVALID_POINTER;
3857 void
3858 nsXMLHttpRequest::HandleProgressTimerCallback()
3860 mProgressTimerIsActive = false;
3861 MaybeDispatchProgressEvents(false);
3864 void
3865 nsXMLHttpRequest::StartProgressEventTimer()
3867 if (!mProgressNotifier) {
3868 mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
3870 if (mProgressNotifier) {
3871 mProgressTimerIsActive = true;
3872 mProgressNotifier->Cancel();
3873 mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
3874 nsITimer::TYPE_ONE_SHOT);
3878 already_AddRefed<nsXMLHttpRequestXPCOMifier>
3879 nsXMLHttpRequest::EnsureXPCOMifier()
3881 if (!mXPCOMifier) {
3882 mXPCOMifier = new nsXMLHttpRequestXPCOMifier(this);
3884 nsRefPtr<nsXMLHttpRequestXPCOMifier> newRef(mXPCOMifier);
3885 return newRef.forget();
3888 NS_IMPL_ISUPPORTS(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
3890 NS_IMETHODIMP nsXMLHttpRequest::
3891 nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value)
3893 if (mXHR->IsSafeHeader(header, mHttpChannel)) {
3894 mHeaders.Append(header);
3895 mHeaders.AppendLiteral(": ");
3896 mHeaders.Append(value);
3897 mHeaders.AppendLiteral("\r\n");
3899 return NS_OK;
3902 // nsXMLHttpRequestXPCOMifier implementation
3903 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
3904 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
3905 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
3906 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
3907 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
3908 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
3909 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
3910 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
3911 NS_INTERFACE_MAP_END
3913 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
3914 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
3916 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
3917 // inheritance from nsISupports.
3918 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
3920 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier)
3921 if (tmp->mXHR) {
3922 tmp->mXHR->mXPCOMifier = nullptr;
3924 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR)
3925 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3927 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
3928 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
3929 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3931 NS_IMETHODIMP
3932 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID & aIID, void **aResult)
3934 // Return ourselves for the things we implement (except
3935 // nsIInterfaceRequestor) and the XHR for the rest.
3936 if (!aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
3937 nsresult rv = QueryInterface(aIID, aResult);
3938 if (NS_SUCCEEDED(rv)) {
3939 return rv;
3943 return mXHR->GetInterface(aIID, aResult);
3946 namespace mozilla {
3948 ArrayBufferBuilder::ArrayBufferBuilder()
3949 : mDataPtr(nullptr),
3950 mCapacity(0),
3951 mLength(0),
3952 mMapPtr(nullptr)
3956 ArrayBufferBuilder::~ArrayBufferBuilder()
3958 reset();
3961 void
3962 ArrayBufferBuilder::reset()
3964 if (mDataPtr) {
3965 JS_free(nullptr, mDataPtr);
3968 if (mMapPtr) {
3969 JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
3970 mMapPtr = nullptr;
3973 mDataPtr = nullptr;
3974 mCapacity = mLength = 0;
3977 bool
3978 ArrayBufferBuilder::setCapacity(uint32_t aNewCap)
3980 MOZ_ASSERT(!mMapPtr);
3982 // To ensure that realloc won't free mDataPtr, use a size of 1
3983 // instead of 0.
3984 uint8_t* newdata = (uint8_t *) js_realloc(mDataPtr, aNewCap ? aNewCap : 1);
3986 if (!newdata) {
3987 return false;
3990 if (aNewCap > mCapacity) {
3991 memset(newdata + mCapacity, 0, aNewCap - mCapacity);
3994 mDataPtr = newdata;
3995 mCapacity = aNewCap;
3996 if (mLength > aNewCap) {
3997 mLength = aNewCap;
4000 return true;
4003 bool
4004 ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
4005 uint32_t aMaxGrowth)
4007 MOZ_ASSERT(!mMapPtr);
4009 if (mLength + aDataLen > mCapacity) {
4010 uint32_t newcap;
4011 // Double while under aMaxGrowth or if not specified.
4012 if (!aMaxGrowth || mCapacity < aMaxGrowth) {
4013 newcap = mCapacity * 2;
4014 } else {
4015 newcap = mCapacity + aMaxGrowth;
4018 // But make sure there's always enough to satisfy our request.
4019 if (newcap < mLength + aDataLen) {
4020 newcap = mLength + aDataLen;
4023 // Did we overflow?
4024 if (newcap < mCapacity) {
4025 return false;
4028 if (!setCapacity(newcap)) {
4029 return false;
4033 // Assert that the region isn't overlapping so we can memcpy.
4034 MOZ_ASSERT(!areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength,
4035 aDataLen));
4037 memcpy(mDataPtr + mLength, aNewData, aDataLen);
4038 mLength += aDataLen;
4040 return true;
4043 JSObject*
4044 ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
4046 if (mMapPtr) {
4047 JSObject* obj = JS_NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
4048 if (!obj) {
4049 JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
4051 mMapPtr = nullptr;
4053 // The memory-mapped contents will be released when obj been finalized(GCed
4054 // or neutered).
4055 return obj;
4058 // we need to check for mLength == 0, because nothing may have been
4059 // added
4060 if (mCapacity > mLength || mLength == 0) {
4061 if (!setCapacity(mLength)) {
4062 return nullptr;
4066 JSObject* obj = JS_NewArrayBufferWithContents(aCx, mLength, mDataPtr);
4067 mLength = mCapacity = 0;
4068 if (!obj) {
4069 js_free(mDataPtr);
4071 mDataPtr = nullptr;
4072 return obj;
4075 nsresult
4076 ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
4077 nsIFile* aJarFile)
4079 #ifdef XP_WIN
4080 // TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
4081 MOZ_CRASH("Not implemented");
4082 return NS_ERROR_NOT_IMPLEMENTED;
4083 #else
4084 nsresult rv;
4086 // Open Jar file to get related attributes of target file.
4087 nsRefPtr<nsZipArchive> zip = new nsZipArchive();
4088 rv = zip->OpenArchive(aJarFile);
4089 if (NS_FAILED(rv)) {
4090 return rv;
4092 nsZipItem* zipItem = zip->GetItem(aFile.get());
4093 if (!zipItem) {
4094 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
4097 // If file was added to the package as stored(uncompressed), map to the
4098 // offset of file in zip package.
4099 if (!zipItem->Compression()) {
4100 uint32_t offset = zip->GetDataOffset(zipItem);
4101 uint32_t size = zipItem->RealSize();
4102 mozilla::AutoFDClose pr_fd;
4103 mozilla::ScopedClose fd;
4104 rv = aJarFile->OpenNSPRFileDesc(PR_RDONLY, 0, &pr_fd.rwget());
4105 if (NS_FAILED(rv)) {
4106 return rv;
4108 fd.rwget() = PR_FileDesc2NativeHandle(pr_fd);
4109 mMapPtr = JS_CreateMappedArrayBufferContents(fd, offset, size);
4110 if (mMapPtr) {
4111 mLength = size;
4112 return NS_OK;
4115 return NS_ERROR_FAILURE;
4116 #endif
4119 /* static */ bool
4120 ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
4121 uint32_t aLength1,
4122 const uint8_t* aStart2,
4123 uint32_t aLength2)
4125 const uint8_t* end1 = aStart1 + aLength1;
4126 const uint8_t* end2 = aStart2 + aLength2;
4128 const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
4129 const uint8_t* min_end = end1 < end2 ? end1 : end2;
4131 return max_start < min_end;
4134 } // namespace mozilla