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"
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"
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"
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"
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"
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
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"
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) \
138 nsXMLHttpRequest::_name(nsACString& aOut) \
146 NS_IMPL_ISUPPORTS(nsXHRParseEndListener
, nsIDOMEventListener
)
148 class nsResumeTimeoutsEvent
: public nsRunnable
151 explicit nsResumeTimeoutsEvent(nsPIDOMWindow
* aWindow
) : mWindow(aWindow
) {}
155 mWindow
->ResumeTimeouts(false);
160 nsCOMPtr
<nsPIDOMWindow
> mWindow
;
164 // This helper function adds the given load flags to the request's existing
166 static void AddLoadFlags(nsIRequest
*request
, nsLoadFlags newFlags
)
169 request
->GetLoadFlags(&flags
);
171 request
->SetLoadFlags(flags
);
174 //-----------------------------------------------------------------------------
175 // XMLHttpRequestAuthPrompt
176 //-----------------------------------------------------------------------------
178 class XMLHttpRequestAuthPrompt
: public nsIAuthPrompt
182 NS_DECL_NSIAUTHPROMPT
184 XMLHttpRequestAuthPrompt();
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
);
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
,
216 XMLHttpRequestAuthPrompt::PromptUsernameAndPassword(const char16_t
* aDialogTitle
,
217 const char16_t
* aDialogText
,
218 const char16_t
* aPasswordRealm
,
219 uint32_t aSavePassword
,
229 XMLHttpRequestAuthPrompt::PromptPassword(const char16_t
* aDialogTitle
,
230 const char16_t
* aText
,
231 const char16_t
* aPasswordRealm
,
232 uint32_t aSavePassword
,
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
)
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 /////////////////////////////////////////////
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),
298 mWarnAboutSyncHtml(false),
299 mLoadLengthComputable(false), mLoadTotal(0),
302 mFirstStartRequestSeen(false),
303 mInLoadProgressEvent(false),
304 mResultJSON(JSVAL_VOID
),
305 mResultArrayBuffer(nullptr),
306 mIsMappedArrayBuffer(false),
314 nsXMLHttpRequest::~nsXMLHttpRequest()
316 mState
|= XML_HTTP_REQUEST_DELETED
;
318 if (mState
& (XML_HTTP_REQUEST_SENT
|
319 XML_HTTP_REQUEST_LOADING
)) {
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);
332 nsXMLHttpRequest::RootJSResultObjects()
334 mozilla::HoldJSObjects(this);
338 * This Init method is called from the factory constructor.
341 nsXMLHttpRequest::Init()
343 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
344 nsCOMPtr
<nsIPrincipal
> subjectPrincipal
;
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()));
358 * This Init method should only be called by C++ consumers.
361 nsXMLHttpRequest::Init(nsIPrincipal
* aPrincipal
,
362 nsIScriptContext
* aScriptContext
,
363 nsIGlobalObject
* aGlobalObject
,
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
);
383 nsXMLHttpRequest::InitParameters(bool aAnon
, bool aSystem
)
385 if (!aAnon
&& !aSystem
) {
389 // Check for permissions.
390 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(GetOwner());
391 if (!window
|| !window
->GetDocShell()) {
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();
403 nsCOMPtr
<nsIPrincipal
> principal
= doc
->NodePrincipal();
404 nsCOMPtr
<nsIPermissionManager
> permMgr
=
405 services::GetPermissionManager();
411 permMgr
->TestPermissionFromPrincipal(principal
, "systemXHR", &permission
);
412 if (NS_FAILED(rv
) || permission
!= nsIPermissionManager::ALLOW_ACTION
) {
417 SetParameters(aAnon
, aSystem
);
421 nsXMLHttpRequest::ResetResponse()
423 mResponseXML
= nullptr;
424 mResponseBody
.Truncate();
425 mResponseText
.Truncate();
426 mResponseBlob
= nullptr;
429 mResultArrayBuffer
= nullptr;
430 mArrayBufferBuilder
.reset();
431 mResultJSON
= JSVAL_VOID
;
433 mLoadTransferred
= 0;
434 mResponseBodyDecodedPos
= 0;
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.
457 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
459 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXMLHttpRequest
)
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
,
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
,
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
,
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
)
535 nsXMLHttpRequest::DisconnectFromOwner()
537 nsXHREventTarget::DisconnectFromOwner();
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
556 // - Binary extensions, but they're *extremely* unlikely to do any memory
559 n
+= mResponseText
.SizeOfExcludingThisEvenIfShared(aMallocSizeOf
);
563 // Measurement of the following members may be added later if DMD finds it is
568 /* readonly attribute nsIChannel channel; */
570 nsXMLHttpRequest::GetChannel(nsIChannel
**aChannel
)
572 NS_ENSURE_ARG_POINTER(aChannel
);
573 NS_IF_ADDREF(*aChannel
= mChannel
);
578 static void LogMessage(const char* aWarning
, nsPIDOMWindow
* aWindow
)
580 nsCOMPtr
<nsIDocument
> doc
;
582 doc
= aWindow
->GetExtantDoc();
584 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
585 NS_LITERAL_CSTRING("DOM"), doc
,
586 nsContentUtils::eDOM_PROPERTIES
,
590 /* readonly attribute nsIDOMDocument responseXML; */
592 nsXMLHttpRequest::GetResponseXML(nsIDOMDocument
**aResponseXML
)
595 nsIDocument
* responseXML
= GetResponseXML(rv
);
597 return rv
.ErrorCode();
601 *aResponseXML
= nullptr;
605 return CallQueryInterface(responseXML
, aResponseXML
);
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
);
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
628 nsXMLHttpRequest::DetectCharset()
630 mResponseCharset
.Truncate();
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
) {
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
);
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
,
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
,
688 MOZ_ASSERT(NS_SUCCEEDED(rv
));
690 totalChars
+= destlen
;
692 mResponseText
.SetLength(totalChars
);
697 /* readonly attribute AString responseText; */
699 nsXMLHttpRequest::GetResponseText(nsAString
& aResponseText
)
702 nsString responseText
;
703 GetResponseText(responseText
, rv
);
704 aResponseText
= responseText
;
705 return rv
.ErrorCode();
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
);
720 if (mResponseType
== XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT
&&
721 !mInLoadProgressEvent
) {
722 aResponseText
.SetIsVoid(true);
726 if (!(mState
& (XML_HTTP_REQUEST_DONE
| XML_HTTP_REQUEST_LOADING
))) {
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
734 mResponseBodyDecodedPos
== mResponseBody
.Length()) {
735 aResponseText
= mResponseText
;
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
);
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
;
766 nsXMLHttpRequest::CreateResponseParsedJSON(JSContext
* 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(),
778 return NS_ERROR_FAILURE
;
786 nsXMLHttpRequest::CreatePartialBlob()
789 // Use progress info to determine whether load is complete, but use
790 // mDataAvailable to ensure a slice is created based on the uncompressed
792 if (mLoadTotal
== mLoadTransferred
) {
793 mResponseBlob
= mDOMFile
;
796 mResponseBlob
= mDOMFile
->CreateSlice(0, mDataAvailable
,
802 // mBlobSet can be null if the request has been canceled
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();
822 case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER
:
823 aResponseType
.AssignLiteral("arraybuffer");
825 case XML_HTTP_RESPONSE_TYPE_BLOB
:
826 aResponseType
.AssignLiteral("blob");
828 case XML_HTTP_RESPONSE_TYPE_DOCUMENT
:
829 aResponseType
.AssignLiteral("document");
831 case XML_HTTP_RESPONSE_TYPE_TEXT
:
832 aResponseType
.AssignLiteral("text");
834 case XML_HTTP_RESPONSE_TYPE_JSON
:
835 aResponseType
.AssignLiteral("json");
837 case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT
:
838 aResponseType
.AssignLiteral("moz-chunked-text");
840 case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER
:
841 aResponseType
.AssignLiteral("moz-chunked-arraybuffer");
843 case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB
:
844 aResponseType
.AssignLiteral("moz-blob");
847 NS_ERROR("Should not happen");
855 nsXMLHttpRequest::StaticAssertions()
857 #define ASSERT_ENUM_EQUAL(_lc, _uc) \
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
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
;
903 SetResponseType(responseType
, rv
);
904 return rv
.ErrorCode();
908 nsXMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aType
,
911 SetResponseType(ResponseTypeEnum(static_cast<int>(aType
)), aRv
);
915 nsXMLHttpRequest::SetResponseType(nsXMLHttpRequest::ResponseTypeEnum aResponseType
,
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
);
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
);
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
);
941 // Set the responseType attribute's value to the given value.
942 mResponseType
= aResponseType
;
946 /* readonly attribute jsval response; */
948 nsXMLHttpRequest::GetResponse(JSContext
*aCx
, JS::MutableHandle
<JS::Value
> aResult
)
951 GetResponse(aCx
, aResult
, rv
);
952 return rv
.ErrorCode();
956 nsXMLHttpRequest::GetResponse(JSContext
* aCx
,
957 JS::MutableHandle
<JS::Value
> aResponse
,
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
:
966 aRv
= GetResponseText(str
);
970 if (!xpc::StringToJsval(aCx
, str
, aResponse
)) {
971 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
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
)) {
987 if (!mResultArrayBuffer
) {
988 RootJSResultObjects();
990 mResultArrayBuffer
= mArrayBufferBuilder
.getArrayBuffer(aCx
);
991 if (!mResultArrayBuffer
) {
992 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
996 JS::ExposeObjectToActiveJS(mResultArrayBuffer
);
997 aResponse
.setObject(*mResultArrayBuffer
);
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();
1009 if (!mResponseBlob
) {
1010 CreatePartialBlob();
1014 if (!mResponseBlob
) {
1015 aResponse
.setNull();
1019 GetOrCreateDOMReflector(aCx
, mResponseBlob
, aResponse
);
1022 case XML_HTTP_RESPONSE_TYPE_DOCUMENT
:
1024 if (!(mState
& XML_HTTP_REQUEST_DONE
) || !mResponseXML
) {
1025 aResponse
.setNull();
1029 aRv
= nsContentUtils::WrapNative(aCx
, mResponseXML
, aResponse
);
1032 case XML_HTTP_RESPONSE_TYPE_JSON
:
1034 if (!(mState
& XML_HTTP_REQUEST_DONE
)) {
1035 aResponse
.setNull();
1039 if (mResultJSON
.isUndefined()) {
1040 aRv
= CreateResponseParsedJSON(aCx
);
1041 mResponseText
.Truncate();
1043 // Per spec, errors aren't propagated. null is returned instead.
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
);
1056 NS_ERROR("Should not happen");
1059 aResponse
.setNull();
1063 nsXMLHttpRequest::IsDeniedCrossSiteRequest()
1065 if ((mState
& XML_HTTP_REQUEST_USE_XSITE_AC
) && mChannel
) {
1067 mChannel
->GetStatus(&rv
);
1068 if (NS_FAILED(rv
)) {
1075 /* readonly attribute AString responseURL; */
1077 nsXMLHttpRequest::GetResponseURL(nsAString
& aUrl
)
1081 uint16_t readyState
;
1082 GetReadyState(&readyState
);
1083 if ((readyState
== UNSENT
|| readyState
== OPENED
) || !mChannel
) {
1087 // Make sure we don't leak responseURL information from denied cross-site
1089 if (IsDeniedCrossSiteRequest()) {
1093 nsCOMPtr
<nsIURI
> responseUrl
;
1094 mChannel
->GetURI(getter_AddRefs(responseUrl
));
1101 responseUrl
->GetSpecIgnoringRef(temp
);
1102 CopyUTF8toUTF16(temp
, aUrl
);
1105 /* readonly attribute unsigned long status; */
1107 nsXMLHttpRequest::GetStatus(uint32_t *aStatus
)
1109 *aStatus
= Status();
1114 nsXMLHttpRequest::Status()
1116 // Make sure we don't leak status information from denied cross-site
1118 if (IsDeniedCrossSiteRequest()) {
1122 uint16_t readyState
= ReadyState();
1123 if (readyState
== UNSENT
|| readyState
== OPENED
) {
1128 // Let's simulate the http protocol for jar/app requests:
1129 nsCOMPtr
<nsIJARChannel
> jarChannel
= GetCurrentJARChannel();
1132 mChannel
->GetStatus(&status
);
1134 if (status
== NS_ERROR_FILE_NOT_FOUND
) {
1135 return 404; // Not Found
1137 return 500; // Internal Error
1144 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
1146 // Pretend like we got a 200 response, since our load was successful
1151 nsresult rv
= httpChannel
->GetResponseStatus(&status
);
1152 if (NS_FAILED(rv
)) {
1159 IMPL_CSTRING_GETTER(GetStatusText
)
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
1168 if (IsDeniedCrossSiteRequest()) {
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
1176 uint16_t readyState
= ReadyState();
1177 if (readyState
== UNSENT
|| readyState
== OPENED
) {
1185 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
1187 httpChannel
->GetResponseStatusText(aStatusText
);
1189 aStatusText
.AssignLiteral("OK");
1194 nsXMLHttpRequest::CloseRequestWithError(const nsAString
& aType
,
1195 const uint32_t aFlag
)
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();
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
;
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
,
1224 if (mUpload
&& !mUploadComplete
) {
1225 mUploadComplete
= true;
1226 DispatchProgressEvent(mUpload
, aType
, true, mUploadTransferred
,
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 (); */
1244 nsXMLHttpRequest::Abort()
1246 CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR
), XML_HTTP_REQUEST_ABORTED
);
1250 nsXMLHttpRequest::SlowAbort()
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.*/
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");
1266 // if this is not a CORS call all headers are safe
1267 if (!(mState
& XML_HTTP_REQUEST_USE_XSITE_AC
)){
1270 // Check for dangerous headers
1271 // Make sure we don't leak header information from denied cross-site
1275 mChannel
->GetStatus(&status
);
1276 if (NS_FAILED(status
)) {
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
])) {
1289 nsAutoCString headerVal
;
1290 // The "Access-Control-Expose-Headers" header contains a comma separated
1291 // list of method names.
1293 GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"),
1295 nsCCharSeparatedTokenizer
exposeTokens(headerVal
, ',');
1296 bool isSafe
= false;
1297 while (exposeTokens
.hasMoreTokens()) {
1298 const nsDependentCSubstring
& token
= exposeTokens
.nextToken();
1299 if (token
.IsEmpty()) {
1302 if (!NS_IsValidHTTPToken(token
)) {
1305 if (header
.Equals(token
, nsCaseInsensitiveCStringComparator())) {
1312 /* ByteString getAllResponseHeaders(); */
1313 IMPL_CSTRING_GETTER(GetAllResponseHeaders
)
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
)) {
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();
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");
1351 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1352 aResponseHeaders
.AppendLiteral("Content-Length: ");
1353 aResponseHeaders
.AppendInt(length
);
1354 aResponseHeaders
.AppendLiteral("\r\n");
1359 nsXMLHttpRequest::GetResponseHeader(const nsACString
& aHeader
,
1360 nsACString
& aResult
)
1363 GetResponseHeader(aHeader
, aResult
, rv
);
1364 return rv
.ErrorCode();
1368 nsXMLHttpRequest::GetResponseHeader(const nsACString
& header
,
1369 nsACString
& _retval
, ErrorResult
& aRv
)
1371 _retval
.SetIsVoid(true);
1373 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
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
)) {
1383 // Even non-http channels supply content type and content length.
1384 // Remember we don't leak header information from denied cross-site
1388 NS_FAILED(mChannel
->GetStatus(&status
)) ||
1389 NS_FAILED(status
)) {
1394 if (header
.LowerCaseEqualsASCII("content-type")) {
1395 if (NS_FAILED(mChannel
->GetContentType(_retval
))) {
1396 // Means no content type
1397 _retval
.SetIsVoid(true);
1402 if (NS_SUCCEEDED(mChannel
->GetContentCharset(value
)) &&
1404 _retval
.AppendLiteral(";charset=");
1405 _retval
.Append(value
);
1410 else if (header
.LowerCaseEqualsASCII("content-length")) {
1412 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1413 _retval
.AppendInt(length
);
1420 // Check for dangerous headers
1421 if (!IsSafeHeader(header
, httpChannel
)) {
1425 aRv
= httpChannel
->GetResponseHeader(header
, _retval
);
1426 if (aRv
.ErrorCode() == NS_ERROR_NOT_AVAILABLE
) {
1428 _retval
.SetIsVoid(true);
1433 already_AddRefed
<nsILoadGroup
>
1434 nsXMLHttpRequest::GetLoadGroup() const
1436 if (mState
& XML_HTTP_REQUEST_BACKGROUND
) {
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
);
1451 return doc
->GetDocumentLoadGroup();
1458 nsXMLHttpRequest::CreateReadystatechangeEvent(nsIDOMEvent
** aDOMEvent
)
1460 nsresult rv
= EventDispatcher::CreateEvent(this, nullptr, nullptr,
1461 NS_LITERAL_STRING("Events"),
1463 if (NS_FAILED(rv
)) {
1467 (*aDOMEvent
)->InitEvent(NS_LITERAL_STRING(READYSTATE_STR
),
1470 // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1471 (*aDOMEvent
)->SetTrusted(true);
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
)) {
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();
1529 nsXMLHttpRequest::IsSystemXHR()
1531 return mIsSystem
|| nsContentUtils::IsSystemPrincipal(mPrincipal
);
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
);
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)) {
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
;
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.
1586 Optional
<nsAString
> realUser
;
1587 if (optional_argc
> 1) {
1590 Optional
<nsAString
> realPassword
;
1591 if (optional_argc
> 2) {
1592 realPassword
= &password
;
1594 return Open(method
, url
, async
, realUser
, realPassword
);
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
,
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)
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");
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
;
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
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
;
1680 mState
|= XML_HTTP_REQUEST_ASYNC
;
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
;
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
,
1708 EmptyCString(), //mime guess
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
;
1749 secFlags
|= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
1752 // If we have the document, use it
1754 rv
= NS_NewChannel(getter_AddRefs(mChannel
),
1758 nsIContentPolicy::TYPE_XMLHTTPREQUEST
,
1760 nullptr, // aCallbacks
1761 nsIRequest::LOAD_BACKGROUND
);
1763 //otherwise use the principal
1764 rv
= NS_NewChannel(getter_AddRefs(mChannel
),
1768 nsIContentPolicy::TYPE_XMLHTTPREQUEST
,
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
));
1781 rv
= httpChannel
->SetRequestMethod(method
);
1782 NS_ENSURE_SUCCESS(rv
, rv
);
1784 // Set the initiator type
1785 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChannel
));
1787 timedChannel
->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
1791 ChangeState(XML_HTTP_REQUEST_OPENED
);
1797 * "Copy" from a stream.
1800 nsXMLHttpRequest::StreamReaderFunc(nsIInputStream
* in
,
1802 const char* fromRawSegment
,
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
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
;
1887 bool nsXMLHttpRequest::CreateDOMFile(nsIRequest
*request
)
1889 nsCOMPtr
<nsIFile
> file
;
1890 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(request
);
1892 fc
->GetFile(getter_AddRefs(file
));
1898 nsAutoCString contentType
;
1899 mChannel
->GetContentType(contentType
);
1901 mDOMFile
= File::CreateFromFile(GetOwner(), file
, EmptyString(),
1902 NS_ConvertASCIItoUTF16(contentType
));
1905 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1910 nsXMLHttpRequest::OnDataAvailable(nsIRequest
*request
,
1912 nsIInputStream
*inStr
,
1913 uint64_t sourceOffset
,
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.
1931 nsresult rv
= inStr
->ReadSegments(nsXMLHttpRequest::StreamReaderFunc
,
1932 (void*)this, count
, &totalRead
);
1933 NS_ENSURE_SUCCESS(rv
, rv
);
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);
1951 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
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?
1969 // Don't do anything if we have been aborted
1970 if (mState
& XML_HTTP_REQUEST_UNSENT
)
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
) {
1986 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
1987 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
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
);
2011 mState
|= XML_HTTP_REQUEST_PARSEBODY
;
2012 ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED
);
2016 if (!mOverrideMimeType
.IsEmpty()) {
2017 channel
->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
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
);
2027 nsCOMPtr
<nsIURI
> uri
;
2028 rv
= channel
->GetURI(getter_AddRefs(uri
));
2029 if (NS_SUCCEEDED(rv
)) {
2031 nsAutoCString scheme
;
2032 uri
->GetScheme(scheme
);
2033 if (scheme
.LowerCaseEqualsLiteral("app")) {
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
);
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;
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");
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.
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
;
2102 } else if (type
.Find("xml") == kNotFound
) {
2103 mState
&= ~XML_HTTP_REQUEST_PARSEBODY
;
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
);
2116 nsIScriptContext
* sc
= GetContextForEventHandlers(&rv
);
2117 NS_ENSURE_SUCCESS(rv
, rv
);
2118 nsCOMPtr
<nsIDocument
> doc
=
2119 nsContentUtils::GetDocumentFromScriptContext(sc
);
2120 nsCOMPtr
<nsIURI
> chromeXHRDocURI
, chromeXHRDocBaseURI
;
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
);
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();
2182 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status, in wstring statusArg); */
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?
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
);
2212 // Is this good enough here?
2213 if (mState
& XML_HTTP_REQUEST_PARSEBODY
&& mXMLParserStreamListener
) {
2214 mXMLParserStreamListener
->OnStopRequest(request
, ctxt
, status
);
2217 mXMLParserStreamListener
= 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.
2224 MaybeDispatchProgressEvents(true);
2227 if (NS_SUCCEEDED(status
) &&
2228 (mResponseType
== XML_HTTP_RESPONSE_TYPE_BLOB
||
2229 mResponseType
== XML_HTTP_RESPONSE_TYPE_MOZ_BLOB
)) {
2231 CreateDOMFile(request
);
2234 mResponseBlob
= mDOMFile
;
2237 // mBlobSet can be null if the channel is non-file non-cacheable
2238 // and if the response length is zero.
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
);
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.
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
)) {
2289 if (!mResponseXML
) {
2290 ChangeStateToDone();
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());
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();
2316 nsXMLHttpRequest::ChangeStateToDone()
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
,
2335 mErrorLoad
? 0 : mLoadTransferred
);
2336 if (mErrorLoad
&& mUpload
&& !mUploadComplete
) {
2337 DispatchProgressEvent(mUpload
, errorStr
, true,
2338 mUploadTransferred
, mUploadTotal
);
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.
2347 mCORSPreflightChannel
= nullptr;
2352 nsXMLHttpRequest::SendAsBinary(const nsAString
&aBody
)
2355 SendAsBinary(aBody
, rv
);
2356 return rv
.ErrorCode();
2360 nsXMLHttpRequest::SendAsBinary(const nsAString
&aBody
,
2363 char *data
= static_cast<char*>(NS_Alloc(aBody
.Length() + 1));
2365 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
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
);
2377 while (iter
!= end
) {
2378 if (*iter
& 0xFF00) {
2380 aRv
.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR
);
2383 *p
++ = static_cast<char>(*iter
++);
2387 nsCOMPtr
<nsIInputStream
> stream
;
2388 aRv
= NS_NewByteInputStream(getter_AddRefs(stream
), data
, aBody
.Length(),
2389 NS_ASSIGNMENT_ADOPT
);
2395 nsCOMPtr
<nsIWritableVariant
> variant
= new nsVariant();
2397 aRv
= variant
->SetAsISupports(stream
);
2402 aRv
= Send(variant
);
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");
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
);
2435 rv
= storStream
->GetLength(&length
);
2436 NS_ENSURE_SUCCESS(rv
, rv
);
2437 *aContentLength
= length
;
2439 return storStream
->NewInputStream(0, aResult
);
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
);
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
);
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
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
);
2501 GetRequestBody(nsIVariant
* aBody
, nsIInputStream
** aResult
, uint64_t* aContentLength
,
2502 nsACString
& aContentType
, nsACString
& aCharset
)
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
;
2514 rv
= aBody
->GetAsInterface(&iid
, getter_AddRefs(supports
));
2515 NS_ENSURE_SUCCESS(rv
, rv
);
2517 nsMemory::Free(iid
);
2520 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryInterface(supports
);
2522 return GetRequestBody(doc
, aResult
, aContentLength
, aContentType
, aCharset
);
2525 // nsISupportsString?
2526 nsCOMPtr
<nsISupportsString
> wstr
= do_QueryInterface(supports
);
2528 nsAutoString string
;
2529 wstr
->GetData(string
);
2531 return GetRequestBody(string
, aResult
, aContentLength
, aContentType
, aCharset
);
2535 nsCOMPtr
<nsIInputStream
> stream
= do_QueryInterface(supports
);
2537 return GetRequestBody(stream
, aResult
, aContentLength
, aContentType
, aCharset
);
2541 nsCOMPtr
<nsIXHRSendable
> sendable
= do_QueryInterface(supports
);
2543 return GetRequestBody(sendable
, aResult
, aContentLength
, aContentType
, aCharset
);
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());
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;
2571 char16_t
* data
= nullptr;
2573 rv
= aBody
->GetAsWStringWithSize(&len
, &data
);
2574 NS_ENSURE_SUCCESS(rv
, rv
);
2577 string
.Adopt(data
, len
);
2579 return GetRequestBody(string
, aResult
, aContentLength
, aContentType
, aCharset
);
2584 nsXMLHttpRequest::GetRequestBody(nsIVariant
* aVariant
,
2585 const Nullable
<RequestBody
>& aBody
,
2586 nsIInputStream
** aResult
,
2587 uint64_t* aContentLength
,
2588 nsACString
& aContentType
, nsACString
& aCharset
)
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
:
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
);
2643 return NS_ERROR_FAILURE
;
2647 NS_NOTREACHED("Default cases exist for a reason");
2651 /* void send (in nsIVariant aBody); */
2653 nsXMLHttpRequest::Send(nsIVariant
*aBody
)
2655 return Send(aBody
, Nullable
<RequestBody
>());
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
2696 nsAutoCString method
;
2697 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
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
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
;
2728 docCurURI
= doc
->GetDocumentURI();
2729 docOrigURI
= doc
->GetOriginalURI();
2730 referrerPolicy
= doc
->GetReferrerPolicy();
2733 nsCOMPtr
<nsIURI
> referrerURI
;
2735 if (principalURI
&& docCurURI
&& docOrigURI
) {
2737 principalURI
->Equals(docOrigURI
, &equal
);
2739 referrerURI
= docCurURI
;
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.
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."
2769 mUploadTransferred
= 0;
2771 // By default we don't have any upload, so mark upload complete.
2772 mUploadComplete
= true;
2774 mLoadLengthComputable
= false;
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
2791 nsAutoCString contentType
;
2792 if (NS_FAILED(httpChannel
->
2793 GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
2795 contentType
.IsEmpty()) {
2796 contentType
= defaultContentType
;
2799 // We don't want to set a charset for streams.
2800 if (!charset
.IsEmpty()) {
2801 nsAutoCString specifiedCharset
;
2803 int32_t charsetStart
, charsetEnd
;
2804 rv
= NS_ExtractCharsetFromContentType(contentType
, specifiedCharset
,
2805 &haveCharset
, &charsetStart
,
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
,
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
),
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);
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
);
2874 nsAutoCString contentTypeHeader
;
2875 rv
= httpChannel
->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
2877 if (NS_SUCCEEDED(rv
)) {
2878 if (!nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader
)) {
2879 mCORSUnsafeHeaders
.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
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
));
2901 // we never let XHR be blocked by head CSS/JS loads to avoid
2902 // potential deadlock where server generation of CSS/JS requires
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
;
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
);
2933 AddLoadFlags(mChannel
, nsIRequest::LOAD_ANONYMOUS
);
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
,
2981 getter_AddRefs(mCORSPreflightChannel
));
2982 NS_ENSURE_SUCCESS(rv
, rv
);
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
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
3015 mCORSPreflightChannel
= nullptr;
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
;
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();
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
;
3060 suspendedDoc
->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents
,
3064 if (resumeTimeoutRunnable
) {
3065 NS_DispatchToCurrentThread(resumeTimeoutRunnable
);
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,
3078 if (mUpload
&& !mUploadComplete
) {
3079 DispatchProgressEvent(mUpload
, NS_LITERAL_STRING(LOADSTART_STR
), true,
3085 return NS_ERROR_FAILURE
;
3091 /* void setRequestHeader (in ByteString header, in ByteString value); */
3092 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
3094 nsXMLHttpRequest::SetRequestHeader(const nsACString
& header
,
3095 const nsACString
& value
)
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.");
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
) {
3114 nsresult rv
= mCORSPreflightChannel
->IsPending(&pending
);
3115 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
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,
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");
3146 // Check for dangerous cross-site headers
3147 bool safeHeader
= IsSystemXHR();
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",
3154 for (uint32_t i
= 0; i
< ArrayLength(kCrossOriginSafeHeaders
); ++i
) {
3155 if (header
.LowerCaseEqualsASCII(kCrossOriginSafeHeaders
[i
])) {
3163 if (!mCORSUnsafeHeaders
.Contains(header
)) {
3164 mCORSUnsafeHeaders
.AppendElement(header
);
3169 if (nsContentUtils::IsForbiddenSystemRequestHeader(header
)) {
3170 mergeHeaders
= false;
3174 if (!mAlreadySetHeaders
.Contains(header
)) {
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
);
3197 /* attribute unsigned long timeout; */
3199 nsXMLHttpRequest::GetTimeout(uint32_t *aTimeout
)
3201 *aTimeout
= Timeout();
3206 nsXMLHttpRequest::SetTimeout(uint32_t aTimeout
)
3209 SetTimeout(aTimeout
, rv
);
3210 return rv
.ErrorCode();
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,
3220 LogMessage("TimeoutSyncXHRWarning", GetOwner());
3221 aRv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
3225 mTimeoutMilliseconds
= aTimeout
;
3226 if (mRequestSentTime
) {
3227 StartTimeoutTimer();
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
) {
3241 if (mTimeoutTimer
) {
3242 mTimeoutTimer
->Cancel();
3245 if (!mTimeoutMilliseconds
) {
3249 if (!mTimeoutTimer
) {
3250 mTimeoutTimer
= do_CreateInstance(NS_TIMER_CONTRACTID
);
3253 (uint32_t)((PR_Now() - mRequestSentTime
) / PR_USEC_PER_MSEC
);
3254 mTimeoutTimer
->InitWithCallback(
3256 mTimeoutMilliseconds
> elapsed
? mTimeoutMilliseconds
- elapsed
: 0,
3257 nsITimer::TYPE_ONE_SHOT
3261 /* readonly attribute unsigned short readyState; */
3263 nsXMLHttpRequest::GetReadyState(uint16_t *aState
)
3265 *aState
= ReadyState();
3270 nsXMLHttpRequest::ReadyState()
3272 // Translate some of our internal states for external consumers
3273 if (mState
& XML_HTTP_REQUEST_UNSENT
) {
3276 if (mState
& (XML_HTTP_REQUEST_OPENED
| XML_HTTP_REQUEST_SENT
)) {
3279 if (mState
& XML_HTTP_REQUEST_HEADERS_RECEIVED
) {
3280 return HEADERS_RECEIVED
;
3282 if (mState
& XML_HTTP_REQUEST_LOADING
) {
3285 MOZ_ASSERT(mState
& XML_HTTP_REQUEST_DONE
);
3289 /* void overrideMimeType(in DOMString mimetype); */
3291 nsXMLHttpRequest::SlowOverrideMimeType(const nsAString
& aMimeType
)
3293 OverrideMimeType(aMimeType
);
3297 /* attribute boolean mozBackgroundRequest; */
3299 nsXMLHttpRequest::GetMozBackgroundRequest(bool *_retval
)
3301 *_retval
= MozBackgroundRequest();
3306 nsXMLHttpRequest::MozBackgroundRequest()
3308 return !!(mState
& XML_HTTP_REQUEST_BACKGROUND
);
3312 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest
)
3314 nsresult rv
= NS_OK
;
3315 SetMozBackgroundRequest(aMozBackgroundRequest
, rv
);
3320 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest
, nsresult
& aRv
)
3322 if (!IsSystemXHR()) {
3323 aRv
= NS_ERROR_DOM_SECURITY_ERR
;
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
;
3333 if (aMozBackgroundRequest
) {
3334 mState
|= XML_HTTP_REQUEST_BACKGROUND
;
3336 mState
&= ~XML_HTTP_REQUEST_BACKGROUND
;
3340 /* attribute boolean withCredentials; */
3342 nsXMLHttpRequest::GetWithCredentials(bool *_retval
)
3344 *_retval
= WithCredentials();
3349 nsXMLHttpRequest::WithCredentials()
3351 return !!(mState
& XML_HTTP_REQUEST_AC_WITH_CREDENTIALS
);
3355 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials
)
3358 SetWithCredentials(aWithCredentials
, rv
);
3359 return rv
.ErrorCode();
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
;
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
);
3379 if (aWithCredentials
) {
3380 mState
|= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS
;
3382 mState
&= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS
;
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
;
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
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);
3420 * Simple helper class that just forwards the redirect callback back
3421 * to the nsXMLHttpRequest.
3423 class AsyncVerifyRedirectCallbackForwarder MOZ_FINAL
: public nsIAsyncVerifyRedirectCallback
3426 explicit AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest
* 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
);
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:
3463 nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel
*aOldChannel
,
3464 nsIChannel
*aNewChannel
,
3466 nsIAsyncVerifyRedirectCallback
*callback
)
3468 NS_PRECONDITION(aNewChannel
, "Redirect without a channel?");
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");
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
,
3499 if (NS_FAILED(rv
)) {
3500 mRedirectCallback
= nullptr;
3501 mNewRedirectChannel
= nullptr;
3505 OnRedirectVerifyCallback(NS_OK
);
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
));
3520 // Ensure all original headers are duplicated for the new channel (bug #553888)
3521 for (uint32_t i
= mModifiedRequestHeaders
.Length(); i
> 0; ) {
3523 httpChannel
->SetRequestHeader(mModifiedRequestHeaders
[i
].header
,
3524 mModifiedRequestHeaders
[i
].value
,
3532 mNewRedirectChannel
= nullptr;
3534 mRedirectCallback
->OnRedirectVerifyCallback(result
);
3535 mRedirectCallback
= nullptr;
3538 /////////////////////////////////////////////////////
3539 // nsIProgressEventSink methods:
3543 nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress
)
3545 if (aFinalProgress
&& mProgressTimerIsActive
) {
3546 mProgressTimerIsActive
= false;
3547 mProgressNotifier
->Cancel();
3550 if (mProgressTimerIsActive
||
3551 !mProgressSinceLastProgressEvent
||
3553 !(mState
& XML_HTTP_REQUEST_ASYNC
)) {
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
,
3570 if (aFinalProgress
) {
3571 mLoadTotal
= mLoadTransferred
;
3573 mInLoadProgressEvent
= true;
3574 DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR
),
3575 mLoadLengthComputable
, mLoadTransferred
,
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;
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
);
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);
3611 mLoadLengthComputable
= lengthComputable
;
3612 mLoadTotal
= lengthComputable
? aProgressMax
: 0;
3613 mLoadTransferred
= aProgress
;
3614 // Don't dispatch progress events here. OnDataAvailable will take care
3618 if (mProgressEventSink
) {
3619 mProgressEventSink
->OnProgress(aRequest
, aContext
, aProgress
,
3627 nsXMLHttpRequest::OnStatus(nsIRequest
*aRequest
, nsISupports
*aContext
, nsresult aStatus
, const char16_t
*aStatusArg
)
3629 if (mProgressEventSink
) {
3630 mProgressEventSink
->OnStatus(aRequest
, aContext
, aStatus
, aStatusArg
);
3637 nsXMLHttpRequest::AllowUploadProgress()
3639 return !(mState
& XML_HTTP_REQUEST_USE_XSITE_AC
) ||
3640 (mState
& XML_HTTP_REQUEST_NEED_AC_PREFLIGHT
);
3643 /////////////////////////////////////////////////////
3644 // nsIInterfaceRequestor methods:
3647 nsXMLHttpRequest::GetInterface(const nsIID
& aIID
, void **aResult
)
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());
3659 } else if (aIID
.Equals(NS_GET_IID(nsIProgressEventSink
))) {
3660 mProgressEventSink
= do_GetInterface(mNotificationCallbacks
);
3661 *aResult
= static_cast<nsIProgressEventSink
*>(EnsureXPCOMifier().take());
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!");
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
3680 if (NS_SUCCEEDED(rv
)) {
3681 rv
= badCertHandler
->GetInterface(aIID
, aResult
);
3682 if (NS_SUCCEEDED(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) {
3705 // ... Authorization is not in the list of author request headers, ...
3707 for (uint32_t i
= 0, len
= mModifiedRequestHeaders
.Length(); i
< len
; ++i
) {
3708 if (mModifiedRequestHeaders
[i
].header
.
3709 LowerCaseEqualsLiteral("authorization")) {
3716 // ... request username is null, and request password is null,
3720 rv
= uri
->GetUsername(username
);
3721 NS_ENSURE_SUCCESS(rv
, rv
);
3724 rv
= uri
->GetPassword(password
);
3725 NS_ENSURE_SUCCESS(rv
, rv
);
3727 if (!username
.IsEmpty() || !password
.IsEmpty()) {
3732 // ... user agents should prompt the end user for their username and password.
3734 nsRefPtr
<XMLHttpRequestAuthPrompt
> prompt
= new XMLHttpRequestAuthPrompt();
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
;
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
3759 else if (aIID
.Equals(NS_GET_IID(nsIStreamListener
))) {
3760 *aResult
= static_cast<nsIStreamListener
*>(EnsureXPCOMifier().take());
3763 else if (aIID
.Equals(NS_GET_IID(nsIRequestObserver
))) {
3764 *aResult
= static_cast<nsIRequestObserver
*>(EnsureXPCOMifier().take());
3767 else if (aIID
.Equals(NS_GET_IID(nsITimerCallback
))) {
3768 *aResult
= static_cast<nsITimerCallback
*>(EnsureXPCOMifier().take());
3772 return QueryInterface(aIID
, aResult
);
3776 nsXMLHttpRequest::GetInterface(JSContext
* aCx
, nsIJSID
* aIID
,
3777 JS::MutableHandle
<JS::Value
> aRetval
,
3780 dom::GetInterface(aCx
, this, aIID
, aRetval
, aRv
);
3783 nsXMLHttpRequestUpload
*
3784 nsXMLHttpRequest::Upload()
3787 mUpload
= new nsXMLHttpRequestUpload(this);
3793 nsXMLHttpRequest::GetUpload(nsIXMLHttpRequestUpload
** aUpload
)
3795 nsRefPtr
<nsXMLHttpRequestUpload
> upload
= Upload();
3796 upload
.forget(aUpload
);
3801 nsXMLHttpRequest::MozAnon()
3807 nsXMLHttpRequest::GetMozAnon(bool* aAnon
)
3814 nsXMLHttpRequest::MozSystem()
3816 return IsSystemXHR();
3820 nsXMLHttpRequest::GetMozSystem(bool* aSystem
)
3822 *aSystem
= MozSystem();
3827 nsXMLHttpRequest::HandleTimeoutCallback()
3829 if (mState
& XML_HTTP_REQUEST_DONE
) {
3830 NS_NOTREACHED("nsXMLHttpRequest::HandleTimeoutCallback with completed request");
3835 CloseRequestWithError(NS_LITERAL_STRING(TIMEOUT_STR
),
3836 XML_HTTP_REQUEST_TIMED_OUT
);
3840 nsXMLHttpRequest::Notify(nsITimer
* aTimer
)
3842 if (mProgressNotifier
== aTimer
) {
3843 HandleProgressTimerCallback();
3847 if (mTimeoutTimer
== aTimer
) {
3848 HandleTimeoutCallback();
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
;
3858 nsXMLHttpRequest::HandleProgressTimerCallback()
3860 mProgressTimerIsActive
= false;
3861 MaybeDispatchProgressEvents(false);
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()
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");
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
)
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
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
)) {
3943 return mXHR
->GetInterface(aIID
, aResult
);
3948 ArrayBufferBuilder::ArrayBufferBuilder()
3949 : mDataPtr(nullptr),
3956 ArrayBufferBuilder::~ArrayBufferBuilder()
3962 ArrayBufferBuilder::reset()
3965 JS_free(nullptr, mDataPtr
);
3969 JS_ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
3974 mCapacity
= mLength
= 0;
3978 ArrayBufferBuilder::setCapacity(uint32_t aNewCap
)
3980 MOZ_ASSERT(!mMapPtr
);
3982 // To ensure that realloc won't free mDataPtr, use a size of 1
3984 uint8_t* newdata
= (uint8_t *) js_realloc(mDataPtr
, aNewCap
? aNewCap
: 1);
3990 if (aNewCap
> mCapacity
) {
3991 memset(newdata
+ mCapacity
, 0, aNewCap
- mCapacity
);
3995 mCapacity
= aNewCap
;
3996 if (mLength
> aNewCap
) {
4004 ArrayBufferBuilder::append(const uint8_t *aNewData
, uint32_t aDataLen
,
4005 uint32_t aMaxGrowth
)
4007 MOZ_ASSERT(!mMapPtr
);
4009 if (mLength
+ aDataLen
> mCapacity
) {
4011 // Double while under aMaxGrowth or if not specified.
4012 if (!aMaxGrowth
|| mCapacity
< aMaxGrowth
) {
4013 newcap
= mCapacity
* 2;
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
;
4024 if (newcap
< mCapacity
) {
4028 if (!setCapacity(newcap
)) {
4033 // Assert that the region isn't overlapping so we can memcpy.
4034 MOZ_ASSERT(!areOverlappingRegions(aNewData
, aDataLen
, mDataPtr
+ mLength
,
4037 memcpy(mDataPtr
+ mLength
, aNewData
, aDataLen
);
4038 mLength
+= aDataLen
;
4044 ArrayBufferBuilder::getArrayBuffer(JSContext
* aCx
)
4047 JSObject
* obj
= JS_NewMappedArrayBufferWithContents(aCx
, mLength
, mMapPtr
);
4049 JS_ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
4053 // The memory-mapped contents will be released when obj been finalized(GCed
4058 // we need to check for mLength == 0, because nothing may have been
4060 if (mCapacity
> mLength
|| mLength
== 0) {
4061 if (!setCapacity(mLength
)) {
4066 JSObject
* obj
= JS_NewArrayBufferWithContents(aCx
, mLength
, mDataPtr
);
4067 mLength
= mCapacity
= 0;
4076 ArrayBufferBuilder::mapToFileInPackage(const nsCString
& aFile
,
4080 // TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
4081 MOZ_CRASH("Not implemented");
4082 return NS_ERROR_NOT_IMPLEMENTED
;
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
)) {
4092 nsZipItem
* zipItem
= zip
->GetItem(aFile
.get());
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
)) {
4108 fd
.rwget() = PR_FileDesc2NativeHandle(pr_fd
);
4109 mMapPtr
= JS_CreateMappedArrayBufferContents(fd
, offset
, size
);
4115 return NS_ERROR_FAILURE
;
4120 ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1
,
4122 const uint8_t* aStart2
,
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