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 "XMLHttpRequestMainThread.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/BasePrincipal.h"
15 #include "mozilla/CheckedInt.h"
16 #include "mozilla/Components.h"
17 #include "mozilla/dom/BlobBinding.h"
18 #include "mozilla/dom/BlobURLProtocolHandler.h"
19 #include "mozilla/dom/DocGroup.h"
20 #include "mozilla/dom/DOMString.h"
21 #include "mozilla/dom/File.h"
22 #include "mozilla/dom/FileBinding.h"
23 #include "mozilla/dom/FileCreatorHelper.h"
24 #include "mozilla/dom/FetchUtil.h"
25 #include "mozilla/dom/FormData.h"
26 #include "mozilla/dom/MutableBlobStorage.h"
27 #include "mozilla/dom/XMLDocument.h"
28 #include "mozilla/dom/URLSearchParams.h"
29 #include "mozilla/dom/UserActivation.h"
30 #include "mozilla/dom/Promise.h"
31 #include "mozilla/dom/PromiseNativeHandler.h"
32 #include "mozilla/dom/WorkerError.h"
33 #include "mozilla/Encoding.h"
34 #include "mozilla/EventDispatcher.h"
35 #include "mozilla/EventListenerManager.h"
36 #include "mozilla/HoldDropJSObjects.h"
37 #include "mozilla/LoadInfo.h"
38 #include "mozilla/LoadContext.h"
39 #include "mozilla/MemoryReporting.h"
40 #include "mozilla/PreloaderBase.h"
41 #include "mozilla/ScopeExit.h"
42 #include "mozilla/SpinEventLoopUntil.h"
43 #include "mozilla/StaticPrefs_dom.h"
44 #include "mozilla/StaticPrefs_network.h"
45 #include "mozilla/StaticPrefs_privacy.h"
46 #include "mozilla/dom/ProgressEvent.h"
47 #include "nsIJARChannel.h"
48 #include "nsIJARURI.h"
49 #include "nsLayoutCID.h"
50 #include "nsReadableUtils.h"
51 #include "nsSandboxFlags.h"
54 #include "nsIURIMutator.h"
55 #include "nsILoadGroup.h"
56 #include "nsNetUtil.h"
57 #include "nsStringStream.h"
58 #include "nsIAuthPrompt.h"
59 #include "nsIAuthPrompt2.h"
60 #include "nsIClassOfService.h"
61 #include "nsIHttpChannel.h"
62 #include "nsISupportsPriority.h"
63 #include "nsIInterfaceRequestorUtils.h"
64 #include "nsStreamUtils.h"
65 #include "nsThreadUtils.h"
66 #include "nsIUploadChannel.h"
67 #include "nsIUploadChannel2.h"
69 #include "nsIDOMEventListener.h"
70 #include "nsVariant.h"
71 #include "nsIScriptError.h"
72 #include "nsICachingChannel.h"
73 #include "nsICookieJarSettings.h"
74 #include "nsContentUtils.h"
75 #include "nsCycleCollectionParticipant.h"
77 #include "nsIPromptFactory.h"
78 #include "nsIWindowWatcher.h"
79 #include "nsIConsoleService.h"
80 #include "nsAsyncRedirectVerifyHelper.h"
81 #include "nsStringBuffer.h"
82 #include "nsIFileChannel.h"
83 #include "mozilla/Telemetry.h"
84 #include "js/ArrayBuffer.h" // JS::{Create,Release}MappedArrayBufferContents,New{,Mapped}ArrayBufferWithContents
85 #include "js/JSON.h" // JS_ParseJSON
86 #include "js/MemoryFunctions.h"
87 #include "js/RootingAPI.h" // JS::{{,Mutable}Handle,Rooted}
88 #include "js/Value.h" // JS::{,Undefined}Value
89 #include "jsapi.h" // JS_ClearPendingException
90 #include "GeckoProfiler.h"
91 #include "mozilla/dom/XMLHttpRequestBinding.h"
92 #include "mozilla/Attributes.h"
93 #include "MultipartBlobImpl.h"
94 #include "nsIPermissionManager.h"
95 #include "nsMimeTypes.h"
96 #include "nsIHttpChannelInternal.h"
97 #include "nsIClassOfService.h"
98 #include "nsCharSeparatedTokenizer.h"
99 #include "nsStreamListenerWrapper.h"
100 #include "nsITimedChannel.h"
101 #include "nsWrapperCacheInlines.h"
102 #include "nsZipArchive.h"
103 #include "mozilla/Preferences.h"
104 #include "private/pprio.h"
105 #include "XMLHttpRequestUpload.h"
107 // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
108 // replaced by FileCreatorHelper#CreateFileW.
113 using namespace mozilla::net
;
118 // Maximum size that we'll grow an ArrayBuffer instead of doubling,
119 // once doubling reaches this threshold
120 const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH
= 32 * 1024 * 1024;
121 // start at 32k to avoid lots of doubling right at the start
122 const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE
= 32 * 1024;
123 // the maximum Content-Length that we'll preallocate. 1GB. Must fit
125 const int32_t XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE
=
126 1 * 1024 * 1024 * 1024LL;
129 const nsLiteralString ProgressEventTypeStrings
[] = {
130 u
"loadstart"_ns
, u
"progress"_ns
, u
"error"_ns
, u
"abort"_ns
,
131 u
"timeout"_ns
, u
"load"_ns
, u
"loadend"_ns
};
132 static_assert(MOZ_ARRAY_LENGTH(ProgressEventTypeStrings
) ==
133 size_t(XMLHttpRequestMainThread::ProgressEventType::ENUM_MAX
),
134 "Mismatched lengths for ProgressEventTypeStrings and "
135 "ProgressEventType enums");
137 const nsString kLiteralString_readystatechange
= u
"readystatechange"_ns
;
138 const nsString kLiteralString_xmlhttprequest
= u
"xmlhttprequest"_ns
;
139 const nsString kLiteralString_DOMContentLoaded
= u
"DOMContentLoaded"_ns
;
140 const nsCString kLiteralString_charset
= "charset"_ns
;
141 const nsCString kLiteralString_UTF_8
= "UTF-8"_ns
;
144 #define NS_PROGRESS_EVENT_INTERVAL 50
145 #define MAX_SYNC_TIMEOUT_WHEN_UNLOADING 10000 /* 10 secs */
147 NS_IMPL_ISUPPORTS(nsXHRParseEndListener
, nsIDOMEventListener
)
149 class nsResumeTimeoutsEvent
: public Runnable
{
151 explicit nsResumeTimeoutsEvent(nsPIDOMWindowInner
* aWindow
)
152 : Runnable("dom::nsResumeTimeoutsEvent"), mWindow(aWindow
) {}
154 NS_IMETHOD
Run() override
{
160 nsCOMPtr
<nsPIDOMWindowInner
> mWindow
;
163 // This helper function adds the given load flags to the request's existing
165 static void AddLoadFlags(nsIRequest
* request
, nsLoadFlags newFlags
) {
167 request
->GetLoadFlags(&flags
);
169 request
->SetLoadFlags(flags
);
172 // We are in a sync event loop.
173 #define NOT_CALLABLE_IN_SYNC_SEND_RV \
174 if (mFlagSyncLooping || mEventDispatchingSuspended) { \
175 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT); \
179 /////////////////////////////////////////////
182 /////////////////////////////////////////////
184 bool XMLHttpRequestMainThread::sDontWarnAboutSyncXHR
= false;
186 XMLHttpRequestMainThread::XMLHttpRequestMainThread(
187 nsIGlobalObject
* aGlobalObject
)
188 : XMLHttpRequest(aGlobalObject
),
189 mResponseBodyDecodedPos(0),
190 mResponseType(XMLHttpRequestResponseType::_empty
),
191 mRequestObserver(nullptr),
192 mState(XMLHttpRequest_Binding::UNSENT
),
193 mFlagSynchronous(false),
195 mFlagParseBody(false),
196 mFlagSyncLooping(false),
197 mFlagBackgroundRequest(false),
198 mFlagHadUploadListenersOnSend(false),
199 mFlagACwithCredentials(false),
200 mFlagTimedOut(false),
203 mUploadTransferred(0),
205 mUploadComplete(true),
206 mProgressSinceLastProgressEvent(false),
208 mTimeoutMilliseconds(0),
209 mErrorLoad(ErrorType::eOK
),
210 mErrorParsingXML(false),
211 mWaitingForOnStopRequest(false),
212 mProgressTimerIsActive(false),
214 mWarnAboutSyncHtml(false),
219 mFirstStartRequestSeen(false),
220 mInLoadProgressEvent(false),
221 mResultJSON(JS::UndefinedValue()),
222 mArrayBufferBuilder(new ArrayBufferBuilder()),
223 mResultArrayBuffer(nullptr),
224 mIsMappedArrayBuffer(false),
225 mXPCOMifier(nullptr),
226 mEventDispatchingSuspended(false),
228 mDelayedDoneNotifier(nullptr) {
229 mozilla::HoldJSObjects(this);
232 XMLHttpRequestMainThread::~XMLHttpRequestMainThread() {
234 !mDelayedDoneNotifier
,
235 "How can we have mDelayedDoneNotifier, which owns us, in destructor?");
239 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
240 mState
== XMLHttpRequest_Binding::LOADING
) {
244 if (mParseEndListener
) {
245 mParseEndListener
->SetIsStale();
246 mParseEndListener
= nullptr;
249 MOZ_ASSERT(!mFlagSyncLooping
, "we rather crash than hang");
250 mFlagSyncLooping
= false;
252 mozilla::DropJSObjects(this);
255 void XMLHttpRequestMainThread::Construct(
256 nsIPrincipal
* aPrincipal
, nsICookieJarSettings
* aCookieJarSettings
,
257 bool aForWorker
, nsIURI
* aBaseURI
/* = nullptr */,
258 nsILoadGroup
* aLoadGroup
/* = nullptr */,
259 PerformanceStorage
* aPerformanceStorage
/* = nullptr */,
260 nsICSPEventListener
* aCSPEventListener
/* = nullptr */) {
261 MOZ_ASSERT(aPrincipal
);
262 mPrincipal
= aPrincipal
;
264 mLoadGroup
= aLoadGroup
;
265 mCookieJarSettings
= aCookieJarSettings
;
266 mForWorker
= aForWorker
;
267 mPerformanceStorage
= aPerformanceStorage
;
268 mCSPEventListener
= aCSPEventListener
;
271 void XMLHttpRequestMainThread::InitParameters(bool aAnon
, bool aSystem
) {
272 if (!aAnon
&& !aSystem
) {
276 // Check for permissions.
277 // Chrome is always allowed access, so do the permission check only
278 // for non-chrome pages.
279 if (!IsSystemXHR() && aSystem
) {
280 nsIGlobalObject
* global
= GetOwnerGlobal();
281 if (NS_WARN_IF(!global
)) {
282 SetParameters(aAnon
, false);
286 nsIPrincipal
* principal
= global
->PrincipalOrNull();
287 if (NS_WARN_IF(!principal
)) {
288 SetParameters(aAnon
, false);
292 nsCOMPtr
<nsIPermissionManager
> permMgr
=
293 components::PermissionManager::Service();
294 if (NS_WARN_IF(!permMgr
)) {
295 SetParameters(aAnon
, false);
300 nsresult rv
= permMgr
->TestPermissionFromPrincipal(
301 principal
, "systemXHR"_ns
, &permission
);
302 if (NS_FAILED(rv
) || permission
!= nsIPermissionManager::ALLOW_ACTION
) {
303 SetParameters(aAnon
, false);
308 SetParameters(aAnon
, aSystem
);
311 void XMLHttpRequestMainThread::SetClientInfoAndController(
312 const ClientInfo
& aClientInfo
,
313 const Maybe
<ServiceWorkerDescriptor
>& aController
) {
314 mClientInfo
.emplace(aClientInfo
);
315 mController
= aController
;
318 void XMLHttpRequestMainThread::ResetResponse() {
319 mResponseXML
= nullptr;
320 mResponseBody
.Truncate();
321 TruncateResponseText();
322 mResponseBlobImpl
= nullptr;
323 mResponseBlob
= nullptr;
324 mBlobStorage
= nullptr;
325 mResultArrayBuffer
= nullptr;
326 mArrayBufferBuilder
= new ArrayBufferBuilder();
327 mResultJSON
.setUndefined();
328 mLoadTransferred
= 0;
329 mResponseBodyDecodedPos
= 0;
333 void XMLHttpRequestMainThread::SetRequestObserver(
334 nsIRequestObserver
* aObserver
) {
335 mRequestObserver
= aObserver
;
338 NS_IMPL_CYCLE_COLLECTION_MULTI_ZONE_JSHOLDER_CLASS(XMLHttpRequestMainThread
)
340 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestMainThread
,
341 XMLHttpRequestEventTarget
)
342 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext
)
343 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel
)
344 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML
)
346 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener
)
348 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob
)
349 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks
)
351 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink
)
352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink
)
354 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload
)
355 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
357 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread
,
358 XMLHttpRequestEventTarget
)
359 tmp
->mResultArrayBuffer
= nullptr;
360 tmp
->mArrayBufferBuilder
= nullptr;
361 tmp
->mResultJSON
.setUndefined();
362 tmp
->mResponseBlobImpl
= nullptr;
364 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext
)
365 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel
)
366 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML
)
368 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener
)
370 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob
)
371 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks
)
373 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink
)
374 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink
)
376 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload
)
377 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
379 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestMainThread
,
380 XMLHttpRequestEventTarget
)
381 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer
)
382 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON
)
383 NS_IMPL_CYCLE_COLLECTION_TRACE_END
385 bool XMLHttpRequestMainThread::IsCertainlyAliveForCC() const {
386 return mWaitingForOnStopRequest
;
389 // QueryInterface implementation for XMLHttpRequestMainThread
390 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestMainThread
)
391 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
392 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
393 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
394 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
395 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
396 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
397 NS_INTERFACE_MAP_ENTRY(nsINamed
)
398 NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget
)
399 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget
)
401 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestMainThread
, XMLHttpRequestEventTarget
)
402 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestMainThread
, XMLHttpRequestEventTarget
)
404 void XMLHttpRequestMainThread::DisconnectFromOwner() {
405 XMLHttpRequestEventTarget::DisconnectFromOwner();
409 size_t XMLHttpRequestMainThread::SizeOfEventTargetIncludingThis(
410 MallocSizeOf aMallocSizeOf
) const {
411 size_t n
= aMallocSizeOf(this);
412 n
+= mResponseBody
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
414 // Why is this safe? Because no-one else will report this string. The
415 // other possible sharers of this string are as follows.
417 // - The JS engine could hold copies if the JS code holds references, e.g.
418 // |var text = XHR.responseText|. However, those references will be via JS
419 // external strings, for which the JS memory reporter does *not* report the
422 // - Binary extensions, but they're *extremely* unlikely to do any memory
425 n
+= mResponseText
.SizeOfThis(aMallocSizeOf
);
429 // Measurement of the following members may be added later if DMD finds it is
434 static void LogMessage(
435 const char* aWarning
, nsPIDOMWindowInner
* aWindow
,
436 const nsTArray
<nsString
>& aParams
= nsTArray
<nsString
>()) {
437 nsCOMPtr
<Document
> doc
;
439 doc
= aWindow
->GetExtantDoc();
441 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "DOM"_ns
, doc
,
442 nsContentUtils::eDOM_PROPERTIES
, aWarning
,
446 Document
* XMLHttpRequestMainThread::GetResponseXML(ErrorResult
& aRv
) {
447 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
448 mResponseType
!= XMLHttpRequestResponseType::Document
) {
449 aRv
.ThrowInvalidStateError(
450 "responseXML is only available if responseType is '' or 'document'.");
453 if (mWarnAboutSyncHtml
) {
454 mWarnAboutSyncHtml
= false;
455 LogMessage("HTMLSyncXHRWarning", GetOwner());
457 if (mState
!= XMLHttpRequest_Binding::DONE
) {
464 * This piece copied from XMLDocument, we try to get the charset
467 nsresult
XMLHttpRequestMainThread::DetectCharset() {
470 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
471 mResponseType
!= XMLHttpRequestResponseType::Text
&&
472 mResponseType
!= XMLHttpRequestResponseType::Json
) {
476 nsAutoCString charsetVal
;
477 const Encoding
* encoding
;
478 bool ok
= mChannel
&& NS_SUCCEEDED(mChannel
->GetContentCharset(charsetVal
)) &&
479 (encoding
= Encoding::ForLabel(charsetVal
));
481 // MS documentation states UTF-8 is default for responseText
482 encoding
= UTF_8_ENCODING
;
485 if (mResponseType
== XMLHttpRequestResponseType::Json
&&
486 encoding
!= UTF_8_ENCODING
) {
487 // The XHR spec says only UTF-8 is supported for responseType == "json"
488 LogMessage("JSONCharsetWarning", GetOwner());
489 encoding
= UTF_8_ENCODING
;
492 // Only sniff the BOM for non-JSON responseTypes
493 if (mResponseType
== XMLHttpRequestResponseType::Json
) {
494 mDecoder
= encoding
->NewDecoderWithBOMRemoval();
496 mDecoder
= encoding
->NewDecoder();
502 nsresult
XMLHttpRequestMainThread::AppendToResponseText(
503 Span
<const uint8_t> aBuffer
, bool aLast
) {
504 // Call this with an empty buffer to send the decoder the signal
505 // that we have hit the end of the stream.
507 NS_ENSURE_STATE(mDecoder
);
509 CheckedInt
<size_t> destBufferLen
=
510 mDecoder
->MaxUTF16BufferLength(aBuffer
.Length());
512 { // scope for holding the mutex that protects mResponseText
513 XMLHttpRequestStringWriterHelper
helper(mResponseText
);
515 uint32_t len
= helper
.Length();
517 destBufferLen
+= len
;
518 if (!destBufferLen
.isValid() || destBufferLen
.value() > UINT32_MAX
) {
519 return NS_ERROR_OUT_OF_MEMORY
;
522 auto handleOrErr
= helper
.BulkWrite(destBufferLen
.value());
523 if (handleOrErr
.isErr()) {
524 return handleOrErr
.unwrapErr();
527 auto handle
= handleOrErr
.unwrap();
533 Tie(result
, read
, written
, hadErrors
) =
534 mDecoder
->DecodeToUTF16(aBuffer
, handle
.AsSpan().From(len
), aLast
);
535 MOZ_ASSERT(result
== kInputEmpty
);
536 MOZ_ASSERT(read
== aBuffer
.Length());
538 MOZ_ASSERT(len
<= destBufferLen
.value());
540 handle
.Finish(len
, false);
544 // Drop the finished decoder to avoid calling into a decoder
545 // that has finished.
552 void XMLHttpRequestMainThread::GetResponseText(DOMString
& aResponseText
,
554 MOZ_DIAGNOSTIC_ASSERT(!mForWorker
);
556 XMLHttpRequestStringSnapshot snapshot
;
557 GetResponseText(snapshot
, aRv
);
562 if (!snapshot
.GetAsString(aResponseText
)) {
563 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
568 void XMLHttpRequestMainThread::GetResponseText(
569 XMLHttpRequestStringSnapshot
& aSnapshot
, ErrorResult
& aRv
) {
572 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
573 mResponseType
!= XMLHttpRequestResponseType::Text
) {
574 aRv
.ThrowInvalidStateError(
575 "responseText is only available if responseType is '' or 'text'.");
579 if (mState
!= XMLHttpRequest_Binding::LOADING
&&
580 mState
!= XMLHttpRequest_Binding::DONE
) {
584 // Main Fetch step 18 requires to ignore body for head/connect methods.
585 if (mRequestMethod
.EqualsLiteral("HEAD") ||
586 mRequestMethod
.EqualsLiteral("CONNECT")) {
590 // We only decode text lazily if we're also parsing to a doc.
591 // Also, if we've decoded all current data already, then no need to decode
593 if ((!mResponseXML
&& !mErrorParsingXML
) ||
594 (mResponseBodyDecodedPos
== mResponseBody
.Length() &&
595 (mState
!= XMLHttpRequest_Binding::DONE
|| mEofDecoded
))) {
596 mResponseText
.CreateSnapshot(aSnapshot
);
600 MatchCharsetAndDecoderToResponseDocument();
602 MOZ_ASSERT(mResponseBodyDecodedPos
< mResponseBody
.Length() ||
603 mState
== XMLHttpRequest_Binding::DONE
,
604 "Unexpected mResponseBodyDecodedPos");
605 Span
<const uint8_t> span
= mResponseBody
;
606 aRv
= AppendToResponseText(span
.From(mResponseBodyDecodedPos
),
607 mState
== XMLHttpRequest_Binding::DONE
);
612 mResponseBodyDecodedPos
= mResponseBody
.Length();
615 // Free memory buffer which we no longer need
616 mResponseBody
.Truncate();
617 mResponseBodyDecodedPos
= 0;
620 mResponseText
.CreateSnapshot(aSnapshot
);
623 nsresult
XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext
* aCx
) {
625 return NS_ERROR_FAILURE
;
629 nsresult rv
= GetResponseTextForJSON(string
);
630 if (NS_WARN_IF(NS_FAILED(rv
))) {
634 // The Unicode converter has already zapped the BOM if there was one
635 JS::Rooted
<JS::Value
> value(aCx
);
636 if (!JS_ParseJSON(aCx
, string
.BeginReading(), string
.Length(), &value
)) {
637 return NS_ERROR_FAILURE
;
644 void XMLHttpRequestMainThread::SetResponseType(
645 XMLHttpRequestResponseType aResponseType
, ErrorResult
& aRv
) {
646 NOT_CALLABLE_IN_SYNC_SEND_RV
648 if (mState
== XMLHttpRequest_Binding::LOADING
||
649 mState
== XMLHttpRequest_Binding::DONE
) {
650 aRv
.ThrowInvalidStateError(
651 "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
652 "(when its state is LOADING or DONE).");
656 // sync request is not allowed setting responseType in window context
657 if (HasOrHasHadOwner() && mState
!= XMLHttpRequest_Binding::UNSENT
&&
659 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
660 aRv
.ThrowInvalidAccessError(
661 "synchronous XMLHttpRequests do not support timeout and responseType");
665 // Set the responseType attribute's value to the given value.
666 SetResponseTypeRaw(aResponseType
);
669 void XMLHttpRequestMainThread::GetResponse(
670 JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aResponse
, ErrorResult
& aRv
) {
671 MOZ_DIAGNOSTIC_ASSERT(!mForWorker
);
673 switch (mResponseType
) {
674 case XMLHttpRequestResponseType::_empty
:
675 case XMLHttpRequestResponseType::Text
: {
677 GetResponseText(str
, aRv
);
681 if (!xpc::StringToJsval(aCx
, str
, aResponse
)) {
682 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
687 case XMLHttpRequestResponseType::Arraybuffer
: {
688 if (mState
!= XMLHttpRequest_Binding::DONE
) {
693 if (!mResultArrayBuffer
) {
694 mResultArrayBuffer
= mArrayBufferBuilder
->TakeArrayBuffer(aCx
);
695 if (!mResultArrayBuffer
) {
696 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
700 aResponse
.setObject(*mResultArrayBuffer
);
703 case XMLHttpRequestResponseType::Blob
: {
704 if (mState
!= XMLHttpRequest_Binding::DONE
) {
709 if (!mResponseBlobImpl
) {
714 if (!mResponseBlob
) {
715 mResponseBlob
= Blob::Create(GetOwnerGlobal(), mResponseBlobImpl
);
718 if (!GetOrCreateDOMReflector(aCx
, mResponseBlob
, aResponse
)) {
724 case XMLHttpRequestResponseType::Document
: {
725 if (!mResponseXML
|| mState
!= XMLHttpRequest_Binding::DONE
) {
731 nsContentUtils::WrapNative(aCx
, ToSupports(mResponseXML
), aResponse
);
734 case XMLHttpRequestResponseType::Json
: {
735 if (mState
!= XMLHttpRequest_Binding::DONE
) {
740 if (mResultJSON
.isUndefined()) {
741 aRv
= CreateResponseParsedJSON(aCx
);
742 TruncateResponseText();
744 // Per spec, errors aren't propagated. null is returned instead.
746 // It would be nice to log the error to the console. That's hard to
747 // do without calling window.onerror as a side effect, though.
748 JS_ClearPendingException(aCx
);
749 mResultJSON
.setNull();
752 aResponse
.set(mResultJSON
);
756 NS_ERROR("Should not happen");
762 already_AddRefed
<BlobImpl
> XMLHttpRequestMainThread::GetResponseBlobImpl() {
763 MOZ_DIAGNOSTIC_ASSERT(mForWorker
);
764 MOZ_DIAGNOSTIC_ASSERT(mResponseType
== XMLHttpRequestResponseType::Blob
);
766 if (mState
!= XMLHttpRequest_Binding::DONE
) {
770 RefPtr
<BlobImpl
> blobImpl
= mResponseBlobImpl
;
771 return blobImpl
.forget();
774 already_AddRefed
<ArrayBufferBuilder
>
775 XMLHttpRequestMainThread::GetResponseArrayBufferBuilder() {
776 MOZ_DIAGNOSTIC_ASSERT(mForWorker
);
777 MOZ_DIAGNOSTIC_ASSERT(mResponseType
==
778 XMLHttpRequestResponseType::Arraybuffer
);
780 if (mState
!= XMLHttpRequest_Binding::DONE
) {
784 RefPtr
<ArrayBufferBuilder
> builder
= mArrayBufferBuilder
;
785 return builder
.forget();
788 nsresult
XMLHttpRequestMainThread::GetResponseTextForJSON(nsAString
& aString
) {
789 if (mState
!= XMLHttpRequest_Binding::DONE
) {
790 aString
.SetIsVoid(true);
794 if (!mResponseText
.GetAsString(aString
)) {
795 return NS_ERROR_OUT_OF_MEMORY
;
801 bool XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const {
806 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
807 return loadInfo
->GetTainting() == LoadTainting::CORS
;
810 bool XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest() {
811 if (IsCrossSiteCORSRequest()) {
813 mChannel
->GetStatus(&rv
);
821 void XMLHttpRequestMainThread::GetResponseURL(nsAString
& aUrl
) {
824 if ((mState
== XMLHttpRequest_Binding::UNSENT
||
825 mState
== XMLHttpRequest_Binding::OPENED
) ||
830 // Make sure we don't leak responseURL information from denied cross-site
832 if (IsDeniedCrossSiteCORSRequest()) {
836 nsCOMPtr
<nsIURI
> responseUrl
;
837 if (NS_FAILED(NS_GetFinalChannelURI(mChannel
, getter_AddRefs(responseUrl
)))) {
842 responseUrl
->GetSpecIgnoringRef(temp
);
843 CopyUTF8toUTF16(temp
, aUrl
);
846 uint32_t XMLHttpRequestMainThread::GetStatus(ErrorResult
& aRv
) {
847 // Make sure we don't leak status information from denied cross-site
849 if (IsDeniedCrossSiteCORSRequest()) {
853 if (mState
== XMLHttpRequest_Binding::UNSENT
||
854 mState
== XMLHttpRequest_Binding::OPENED
) {
858 if (mErrorLoad
!= ErrorType::eOK
) {
859 // Let's simulate the http protocol for jar/app requests:
860 nsCOMPtr
<nsIJARChannel
> jarChannel
= GetCurrentJARChannel();
863 mChannel
->GetStatus(&status
);
865 if (status
== NS_ERROR_FILE_NOT_FOUND
) {
866 return 404; // Not Found
868 return 500; // Internal Error
875 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
877 // Pretend like we got a 200 response, since our load was successful
882 nsresult rv
= httpChannel
->GetResponseStatus(&status
);
890 void XMLHttpRequestMainThread::GetStatusText(nsACString
& aStatusText
,
892 // Return an empty status text on all error loads.
893 aStatusText
.Truncate();
895 // Make sure we don't leak status information from denied cross-site
897 if (IsDeniedCrossSiteCORSRequest()) {
901 // Check the current XHR state to see if it is valid to obtain the statusText
902 // value. This check is to prevent the status text for redirects from being
903 // available before all the redirects have been followed and HTTP headers have
905 if (mState
== XMLHttpRequest_Binding::UNSENT
||
906 mState
== XMLHttpRequest_Binding::OPENED
) {
910 if (mErrorLoad
!= ErrorType::eOK
) {
914 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
916 Unused
<< httpChannel
->GetResponseStatusText(aStatusText
);
918 aStatusText
.AssignLiteral("OK");
922 void XMLHttpRequestMainThread::TerminateOngoingFetch() {
923 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
924 mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
925 mState
== XMLHttpRequest_Binding::LOADING
) {
930 void XMLHttpRequestMainThread::CloseRequest() {
931 mWaitingForOnStopRequest
= false;
932 mErrorLoad
= ErrorType::eTerminated
;
934 mChannel
->Cancel(NS_BINDING_ABORTED
);
937 mTimeoutTimer
->Cancel();
941 void XMLHttpRequestMainThread::CloseRequestWithError(
942 const ProgressEventType aType
) {
947 // If we're in the destructor, don't risk dispatching an event.
949 mFlagSyncLooping
= false;
953 if (mState
!= XMLHttpRequest_Binding::UNSENT
&&
954 !(mState
== XMLHttpRequest_Binding::OPENED
&& !mFlagSend
) &&
955 mState
!= XMLHttpRequest_Binding::DONE
) {
956 ChangeState(XMLHttpRequest_Binding::DONE
, true);
958 if (!mFlagSyncLooping
) {
959 if (mUpload
&& !mUploadComplete
) {
960 mUploadComplete
= true;
961 DispatchProgressEvent(mUpload
, aType
, 0, -1);
963 DispatchProgressEvent(this, aType
, 0, -1);
967 // The ChangeState call above calls onreadystatechange handlers which
968 // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
969 // the abort state bit. If this occurs we're not uninitialized (bug 361773).
971 ChangeState(XMLHttpRequest_Binding::UNSENT
, false); // IE seems to do it
974 mFlagSyncLooping
= false;
977 void XMLHttpRequestMainThread::RequestErrorSteps(
978 const ProgressEventType aEventType
, const nsresult aOptionalException
,
981 mState
= XMLHttpRequest_Binding::DONE
;
983 StopProgressEventTimer();
991 // If we're in the destructor, don't risk dispatching an event.
993 mFlagSyncLooping
= false;
998 if (mFlagSynchronous
&& NS_FAILED(aOptionalException
)) {
999 aRv
.Throw(aOptionalException
);
1004 FireReadystatechangeEvent();
1007 if (mUpload
&& !mUploadComplete
) {
1009 mUploadComplete
= true;
1012 if (mFlagHadUploadListenersOnSend
) {
1013 // Steps 6-3, 6-4 (loadend is fired for us)
1014 DispatchProgressEvent(mUpload
, aEventType
, 0, -1);
1018 // Steps 7 and 8 (loadend is fired for us)
1019 DispatchProgressEvent(this, aEventType
, 0, -1);
1022 void XMLHttpRequestMainThread::Abort(ErrorResult
& aRv
) {
1023 NOT_CALLABLE_IN_SYNC_SEND_RV
1027 void XMLHttpRequestMainThread::AbortInternal(ErrorResult
& aRv
) {
1028 mFlagAborted
= true;
1029 DisconnectDoneNotifier();
1032 TerminateOngoingFetch();
1035 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
1036 mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
1037 mState
== XMLHttpRequest_Binding::LOADING
) {
1038 RequestErrorSteps(ProgressEventType::abort
, NS_OK
, aRv
);
1042 if (mState
== XMLHttpRequest_Binding::DONE
) {
1043 ChangeState(XMLHttpRequest_Binding::UNSENT
,
1044 false); // no ReadystateChange event
1047 mFlagSyncLooping
= false;
1050 /*Method that checks if it is safe to expose a header value to the client.
1051 It is used to check what headers are exposed for CORS requests.*/
1052 bool XMLHttpRequestMainThread::IsSafeHeader(
1053 const nsACString
& aHeader
, NotNull
<nsIHttpChannel
*> aHttpChannel
) const {
1054 // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
1055 if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(aHeader
)) {
1056 NS_WARNING("blocked access to response header");
1059 // if this is not a CORS call all headers are safe
1060 if (!IsCrossSiteCORSRequest()) {
1063 // Check for dangerous headers
1064 // Make sure we don't leak header information from denied cross-site
1068 mChannel
->GetStatus(&status
);
1069 if (NS_FAILED(status
)) {
1073 const char* kCrossOriginSafeHeaders
[] = {
1074 "cache-control", "content-language", "content-type", "content-length",
1075 "expires", "last-modified", "pragma"};
1076 for (uint32_t i
= 0; i
< ArrayLength(kCrossOriginSafeHeaders
); ++i
) {
1077 if (aHeader
.LowerCaseEqualsASCII(kCrossOriginSafeHeaders
[i
])) {
1081 nsAutoCString headerVal
;
1082 // The "Access-Control-Expose-Headers" header contains a comma separated
1083 // list of method names.
1084 Unused
<< aHttpChannel
->GetResponseHeader("Access-Control-Expose-Headers"_ns
,
1086 bool isSafe
= false;
1087 for (const nsACString
& token
:
1088 nsCCharSeparatedTokenizer(headerVal
, ',').ToRange()) {
1089 if (token
.IsEmpty()) {
1092 if (!NS_IsValidHTTPToken(token
)) {
1096 if (token
.EqualsLiteral("*") && !mFlagACwithCredentials
) {
1098 } else if (aHeader
.Equals(token
, nsCaseInsensitiveCStringComparator
)) {
1106 void XMLHttpRequestMainThread::GetAllResponseHeaders(
1107 nsACString
& aResponseHeaders
, ErrorResult
& aRv
) {
1108 NOT_CALLABLE_IN_SYNC_SEND_RV
1110 aResponseHeaders
.Truncate();
1112 // If the state is UNSENT or OPENED,
1113 // return the empty string and terminate these steps.
1114 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1115 mState
== XMLHttpRequest_Binding::OPENED
) {
1119 if (mErrorLoad
!= ErrorType::eOK
) {
1123 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel()) {
1124 RefPtr
<nsHeaderVisitor
> visitor
=
1125 new nsHeaderVisitor(*this, WrapNotNull(httpChannel
));
1126 if (NS_SUCCEEDED(httpChannel
->VisitResponseHeaders(visitor
))) {
1127 aResponseHeaders
= visitor
->Headers();
1136 // Even non-http channels supply content type.
1137 nsAutoCString value
;
1138 if (NS_SUCCEEDED(mChannel
->GetContentType(value
))) {
1139 aResponseHeaders
.AppendLiteral("Content-Type: ");
1140 aResponseHeaders
.Append(value
);
1141 if (NS_SUCCEEDED(mChannel
->GetContentCharset(value
)) && !value
.IsEmpty()) {
1142 aResponseHeaders
.AppendLiteral(";charset=");
1143 aResponseHeaders
.Append(value
);
1145 aResponseHeaders
.AppendLiteral("\r\n");
1148 // Don't provide Content-Length for data URIs
1149 nsCOMPtr
<nsIURI
> uri
;
1150 if (NS_FAILED(mChannel
->GetURI(getter_AddRefs(uri
))) ||
1151 !uri
->SchemeIs("data")) {
1153 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1154 aResponseHeaders
.AppendLiteral("Content-Length: ");
1155 aResponseHeaders
.AppendInt(length
);
1156 aResponseHeaders
.AppendLiteral("\r\n");
1161 void XMLHttpRequestMainThread::GetResponseHeader(const nsACString
& header
,
1162 nsACString
& _retval
,
1164 NOT_CALLABLE_IN_SYNC_SEND_RV
1166 _retval
.SetIsVoid(true);
1168 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
1171 // If the state is UNSENT or OPENED,
1172 // return null and terminate these steps.
1173 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1174 mState
== XMLHttpRequest_Binding::OPENED
) {
1178 // Even non-http channels supply content type and content length.
1179 // Remember we don't leak header information from denied cross-site
1180 // requests. However, we handle file: and blob: URLs for blob response
1181 // types by canceling them with a specific error, so we have to allow
1182 // them to pass through this check.
1184 if (!mChannel
|| NS_FAILED(mChannel
->GetStatus(&status
)) ||
1185 (NS_FAILED(status
) && status
!= NS_ERROR_FILE_ALREADY_EXISTS
)) {
1190 if (header
.LowerCaseEqualsASCII("content-type")) {
1191 if (NS_FAILED(mChannel
->GetContentType(_retval
))) {
1192 // Means no content type
1193 _retval
.SetIsVoid(true);
1198 if (NS_SUCCEEDED(mChannel
->GetContentCharset(value
)) &&
1200 _retval
.AppendLiteral(";charset=");
1201 _retval
.Append(value
);
1206 else if (header
.LowerCaseEqualsASCII("content-length")) {
1208 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1209 _retval
.AppendInt(length
);
1216 // Check for dangerous headers
1217 if (!IsSafeHeader(header
, WrapNotNull(httpChannel
))) {
1221 aRv
= httpChannel
->GetResponseHeader(header
, _retval
);
1222 if (aRv
.ErrorCodeIs(NS_ERROR_NOT_AVAILABLE
)) {
1224 _retval
.SetIsVoid(true);
1225 aRv
.SuppressException();
1229 already_AddRefed
<nsILoadGroup
> XMLHttpRequestMainThread::GetLoadGroup() const {
1230 if (mFlagBackgroundRequest
) {
1235 nsCOMPtr
<nsILoadGroup
> ref
= mLoadGroup
;
1236 return ref
.forget();
1239 Document
* doc
= GetDocumentIfCurrent();
1241 return doc
->GetDocumentLoadGroup();
1247 nsresult
XMLHttpRequestMainThread::FireReadystatechangeEvent() {
1248 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::UNSENT
);
1249 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1250 event
->InitEvent(kLiteralString_readystatechange
, false, false);
1251 // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1252 event
->SetTrusted(true);
1253 DispatchOrStoreEvent(this, event
);
1257 void XMLHttpRequestMainThread::DispatchProgressEvent(
1258 DOMEventTargetHelper
* aTarget
, const ProgressEventType aType
,
1259 int64_t aLoaded
, int64_t aTotal
) {
1260 NS_ASSERTION(aTarget
, "null target");
1262 if (NS_FAILED(CheckCurrentGlobalCorrectness()) ||
1263 (!AllowUploadProgress() && aTarget
== mUpload
)) {
1267 // If blocked by CORS, zero-out the stats on progress events
1268 // and never fire "progress" or "load" events at all.
1269 if (IsDeniedCrossSiteCORSRequest()) {
1270 if (aType
== ProgressEventType::progress
||
1271 aType
== ProgressEventType::load
) {
1278 if (aType
== ProgressEventType::progress
) {
1279 mInLoadProgressEvent
= true;
1282 ProgressEventInit init
;
1283 init
.mBubbles
= false;
1284 init
.mCancelable
= false;
1285 init
.mLengthComputable
= aTotal
!= -1; // XHR spec step 6.1
1286 init
.mLoaded
= aLoaded
;
1287 init
.mTotal
= (aTotal
== -1) ? 0 : aTotal
;
1289 const nsAString
& typeString
= ProgressEventTypeStrings
[(uint8_t)aType
];
1290 RefPtr
<ProgressEvent
> event
=
1291 ProgressEvent::Constructor(aTarget
, typeString
, init
);
1292 event
->SetTrusted(true);
1294 DispatchOrStoreEvent(aTarget
, event
);
1296 if (aType
== ProgressEventType::progress
) {
1297 mInLoadProgressEvent
= false;
1300 // If we're sending a load, error, timeout or abort event, then
1301 // also dispatch the subsequent loadend event.
1302 if (aType
== ProgressEventType::load
|| aType
== ProgressEventType::error
||
1303 aType
== ProgressEventType::timeout
||
1304 aType
== ProgressEventType::abort
) {
1305 DispatchProgressEvent(aTarget
, ProgressEventType::loadend
, aLoaded
, aTotal
);
1309 void XMLHttpRequestMainThread::DispatchOrStoreEvent(
1310 DOMEventTargetHelper
* aTarget
, Event
* aEvent
) {
1311 MOZ_ASSERT(aTarget
);
1314 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
1318 if (mEventDispatchingSuspended
) {
1319 PendingEvent
* event
= mPendingEvents
.AppendElement();
1320 event
->mTarget
= aTarget
;
1321 event
->mEvent
= aEvent
;
1325 aTarget
->DispatchEvent(*aEvent
);
1328 void XMLHttpRequestMainThread::SuspendEventDispatching() {
1329 MOZ_ASSERT(!mEventDispatchingSuspended
);
1330 mEventDispatchingSuspended
= true;
1333 void XMLHttpRequestMainThread::ResumeEventDispatching() {
1334 MOZ_ASSERT(mEventDispatchingSuspended
);
1335 mEventDispatchingSuspended
= false;
1337 nsTArray
<PendingEvent
> pendingEvents
= std::move(mPendingEvents
);
1339 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
1343 for (uint32_t i
= 0; i
< pendingEvents
.Length(); ++i
) {
1344 pendingEvents
[i
].mTarget
->DispatchEvent(*pendingEvents
[i
].mEvent
);
1348 already_AddRefed
<nsIHttpChannel
>
1349 XMLHttpRequestMainThread::GetCurrentHttpChannel() {
1350 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
1351 return httpChannel
.forget();
1354 already_AddRefed
<nsIJARChannel
>
1355 XMLHttpRequestMainThread::GetCurrentJARChannel() {
1356 nsCOMPtr
<nsIJARChannel
> appChannel
= do_QueryInterface(mChannel
);
1357 return appChannel
.forget();
1360 bool XMLHttpRequestMainThread::IsSystemXHR() const {
1361 return mIsSystem
|| mPrincipal
->IsSystemPrincipal();
1364 bool XMLHttpRequestMainThread::InUploadPhase() const {
1365 // We're in the upload phase while our state is OPENED.
1366 return mState
== XMLHttpRequest_Binding::OPENED
;
1369 // This case is hit when the async parameter is outright omitted, which
1370 // should set it to true (and the username and password to null).
1371 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1372 const nsAString
& aUrl
, ErrorResult
& aRv
) {
1373 Open(aMethod
, aUrl
, true, VoidString(), VoidString(), aRv
);
1376 // This case is hit when the async parameter is specified, even if the
1377 // JS value was "undefined" (which due to legacy reasons should be
1378 // treated as true, which is how it will already be passed in here).
1379 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1380 const nsAString
& aUrl
, bool aAsync
,
1381 const nsAString
& aUsername
,
1382 const nsAString
& aPassword
,
1384 Open(aMethod
, NS_ConvertUTF16toUTF8(aUrl
), aAsync
, aUsername
, aPassword
, aRv
);
1387 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1388 const nsACString
& aUrl
, bool aAsync
,
1389 const nsAString
& aUsername
,
1390 const nsAString
& aPassword
,
1392 NOT_CALLABLE_IN_SYNC_SEND_RV
1395 if (!aAsync
&& !DontWarnAboutSyncXHR() && GetOwner() &&
1396 GetOwner()->GetExtantDoc()) {
1397 GetOwner()->GetExtantDoc()->WarnOnceAbout(
1398 DeprecatedOperations::eSyncXMLHttpRequest
);
1401 Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC
,
1405 nsCOMPtr
<Document
> responsibleDocument
= GetDocumentIfCurrent();
1406 if (!responsibleDocument
) {
1407 // This could be because we're no longer current or because we're in some
1408 // non-window context...
1409 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
1410 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1415 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
1420 if (!aAsync
&& responsibleDocument
&& GetOwner()) {
1421 // We have no extant document during unload, so the above general
1422 // syncXHR warning will not display. But we do want to display a
1423 // recommendation to use sendBeacon instead of syncXHR during unload.
1424 nsCOMPtr
<nsIDocShell
> shell
= responsibleDocument
->GetDocShell();
1426 bool inUnload
= false;
1427 shell
->GetIsInUnload(&inUnload
);
1429 LogMessage("UseSendBeaconDuringUnloadAndPagehideWarning", GetOwner());
1435 nsAutoCString method
;
1436 aRv
= FetchUtil::GetValidRequestMethod(aMethod
, method
);
1437 if (NS_WARN_IF(aRv
.Failed())) {
1442 nsIURI
* baseURI
= nullptr;
1445 } else if (responsibleDocument
) {
1446 baseURI
= responsibleDocument
->GetBaseURI();
1449 // Use the responsible document's encoding for the URL if we have one,
1450 // except for dedicated workers. Use UTF-8 otherwise.
1451 NotNull
<const Encoding
*> originCharset
= UTF_8_ENCODING
;
1452 if (responsibleDocument
&&
1453 responsibleDocument
->NodePrincipal() == mPrincipal
) {
1454 originCharset
= responsibleDocument
->GetDocumentCharacterSet();
1457 nsCOMPtr
<nsIURI
> parsedURL
;
1459 NS_NewURI(getter_AddRefs(parsedURL
), aUrl
, originCharset
, baseURI
);
1460 if (NS_FAILED(rv
)) {
1461 if (rv
== NS_ERROR_MALFORMED_URI
) {
1462 aRv
.Throw(NS_ERROR_DOM_MALFORMED_URI
);
1468 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
1469 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1474 // This is already handled by the other Open() method, which passes
1475 // username and password in as NullStrings.
1479 parsedURL
->GetHost(host
);
1480 if (!host
.IsEmpty() && (!aUsername
.IsVoid() || !aPassword
.IsVoid())) {
1481 auto mutator
= NS_MutateURI(parsedURL
);
1482 if (!aUsername
.IsVoid()) {
1483 mutator
.SetUsername(NS_ConvertUTF16toUTF8(aUsername
));
1485 if (!aPassword
.IsVoid()) {
1486 mutator
.SetPassword(NS_ConvertUTF16toUTF8(aPassword
));
1488 Unused
<< mutator
.Finalize(parsedURL
);
1492 if (!aAsync
&& HasOrHasHadOwner() &&
1493 (mTimeoutMilliseconds
||
1494 mResponseType
!= XMLHttpRequestResponseType::_empty
)) {
1495 if (mTimeoutMilliseconds
) {
1496 LogMessage("TimeoutSyncXHRWarning", GetOwner());
1498 if (mResponseType
!= XMLHttpRequestResponseType::_empty
) {
1499 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
1501 aRv
.ThrowInvalidAccessError(
1502 "synchronous XMLHttpRequests do not support timeout and responseType");
1507 TerminateOngoingFetch();
1510 // timeouts are handled without a flag
1511 DisconnectDoneNotifier();
1513 mRequestMethod
.Assign(method
);
1514 mRequestURL
= parsedURL
;
1515 mFlagSynchronous
= !aAsync
;
1516 mAuthorRequestHeaders
.Clear();
1520 mFlagHadUploadListenersOnSend
= false;
1521 mFlagAborted
= false;
1522 mFlagTimedOut
= false;
1525 // Per spec we should only create the channel on send(), but we have internal
1526 // code that relies on the channel being created now, and that code is not
1527 // always IsSystemXHR(). However, we're not supposed to throw channel-creation
1528 // errors during open(), so we silently ignore those here.
1532 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
1533 mState
= XMLHttpRequest_Binding::OPENED
;
1534 FireReadystatechangeEvent();
1538 void XMLHttpRequestMainThread::SetOriginAttributes(
1539 const OriginAttributesDictionary
& aAttrs
) {
1540 MOZ_ASSERT((mState
== XMLHttpRequest_Binding::OPENED
) && !mFlagSend
);
1542 OriginAttributes
attrs(aAttrs
);
1544 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
1545 loadInfo
->SetOriginAttributes(attrs
);
1549 * "Copy" from a stream.
1551 nsresult
XMLHttpRequestMainThread::StreamReaderFunc(
1552 nsIInputStream
* in
, void* closure
, const char* fromRawSegment
,
1553 uint32_t toOffset
, uint32_t count
, uint32_t* writeCount
) {
1554 XMLHttpRequestMainThread
* xmlHttpRequest
=
1555 static_cast<XMLHttpRequestMainThread
*>(closure
);
1556 if (!xmlHttpRequest
|| !writeCount
) {
1558 "XMLHttpRequest cannot read from stream: no closure or writeCount");
1559 return NS_ERROR_FAILURE
;
1562 nsresult rv
= NS_OK
;
1564 if (xmlHttpRequest
->mResponseType
== XMLHttpRequestResponseType::Blob
) {
1565 xmlHttpRequest
->MaybeCreateBlobStorage();
1566 rv
= xmlHttpRequest
->mBlobStorage
->Append(fromRawSegment
, count
);
1567 } else if (xmlHttpRequest
->mResponseType
==
1568 XMLHttpRequestResponseType::Arraybuffer
&&
1569 !xmlHttpRequest
->mIsMappedArrayBuffer
) {
1570 // get the initial capacity to something reasonable to avoid a bunch of
1571 // reallocs right at the start
1572 if (xmlHttpRequest
->mArrayBufferBuilder
->Capacity() == 0)
1573 xmlHttpRequest
->mArrayBufferBuilder
->SetCapacity(
1574 std::max(count
, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE
));
1576 if (NS_WARN_IF(!xmlHttpRequest
->mArrayBufferBuilder
->Append(
1577 reinterpret_cast<const uint8_t*>(fromRawSegment
), count
,
1578 XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH
))) {
1579 return NS_ERROR_OUT_OF_MEMORY
;
1582 } else if (xmlHttpRequest
->mResponseType
==
1583 XMLHttpRequestResponseType::_empty
&&
1584 xmlHttpRequest
->mResponseXML
) {
1585 // Copy for our own use
1586 if (!xmlHttpRequest
->mResponseBody
.Append(fromRawSegment
, count
,
1588 return NS_ERROR_OUT_OF_MEMORY
;
1590 } else if (xmlHttpRequest
->mResponseType
==
1591 XMLHttpRequestResponseType::_empty
||
1592 xmlHttpRequest
->mResponseType
==
1593 XMLHttpRequestResponseType::Text
||
1594 xmlHttpRequest
->mResponseType
==
1595 XMLHttpRequestResponseType::Json
) {
1596 MOZ_ASSERT(!xmlHttpRequest
->mResponseXML
,
1597 "We shouldn't be parsing a doc here");
1598 rv
= xmlHttpRequest
->AppendToResponseText(
1599 AsBytes(Span(fromRawSegment
, count
)));
1600 if (NS_WARN_IF(NS_FAILED(rv
))) {
1605 if (xmlHttpRequest
->mFlagParseBody
) {
1606 // Give the same data to the parser.
1608 // We need to wrap the data in a new lightweight stream and pass that
1609 // to the parser, because calling ReadSegments() recursively on the same
1610 // stream is not supported.
1611 nsCOMPtr
<nsIInputStream
> copyStream
;
1612 rv
= NS_NewByteInputStream(getter_AddRefs(copyStream
),
1613 Span(fromRawSegment
, count
),
1614 NS_ASSIGNMENT_DEPEND
);
1616 if (NS_SUCCEEDED(rv
) && xmlHttpRequest
->mXMLParserStreamListener
) {
1617 NS_ASSERTION(copyStream
, "NS_NewByteInputStream lied");
1618 nsresult parsingResult
=
1619 xmlHttpRequest
->mXMLParserStreamListener
->OnDataAvailable(
1620 xmlHttpRequest
->mChannel
, copyStream
, toOffset
, count
);
1622 // No use to continue parsing if we failed here, but we
1623 // should still finish reading the stream
1624 if (NS_FAILED(parsingResult
)) {
1625 xmlHttpRequest
->mFlagParseBody
= false;
1630 if (NS_SUCCEEDED(rv
)) {
1631 *writeCount
= count
;
1641 void GetBlobURIFromChannel(nsIRequest
* aRequest
, nsIURI
** aURI
) {
1642 MOZ_ASSERT(aRequest
);
1647 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1652 nsCOMPtr
<nsIURI
> uri
;
1653 nsresult rv
= channel
->GetURI(getter_AddRefs(uri
));
1654 if (NS_FAILED(rv
)) {
1658 if (!dom::IsBlobURI(uri
)) {
1665 nsresult
GetLocalFileFromChannel(nsIRequest
* aRequest
, nsIFile
** aFile
) {
1666 MOZ_ASSERT(aRequest
);
1671 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(aRequest
);
1676 nsCOMPtr
<nsIFile
> file
;
1677 nsresult rv
= fc
->GetFile(getter_AddRefs(file
));
1678 if (NS_WARN_IF(NS_FAILED(rv
))) {
1686 nsresult
DummyStreamReaderFunc(nsIInputStream
* aInputStream
, void* aClosure
,
1687 const char* aFromRawSegment
, uint32_t aToOffset
,
1688 uint32_t aCount
, uint32_t* aWriteCount
) {
1689 *aWriteCount
= aCount
;
1693 class FileCreationHandler final
: public PromiseNativeHandler
{
1697 static void Create(Promise
* aPromise
, XMLHttpRequestMainThread
* aXHR
) {
1698 MOZ_ASSERT(aPromise
);
1700 RefPtr
<FileCreationHandler
> handler
= new FileCreationHandler(aXHR
);
1701 aPromise
->AppendNativeHandler(handler
);
1704 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) override
{
1705 if (NS_WARN_IF(!aValue
.isObject())) {
1706 mXHR
->LocalFileToBlobCompleted(nullptr);
1711 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob
, &aValue
.toObject(), blob
)))) {
1712 mXHR
->LocalFileToBlobCompleted(nullptr);
1716 mXHR
->LocalFileToBlobCompleted(blob
->Impl());
1719 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) override
{
1720 mXHR
->LocalFileToBlobCompleted(nullptr);
1724 explicit FileCreationHandler(XMLHttpRequestMainThread
* aXHR
) : mXHR(aXHR
) {
1728 ~FileCreationHandler() = default;
1730 RefPtr
<XMLHttpRequestMainThread
> mXHR
;
1733 NS_IMPL_ISUPPORTS0(FileCreationHandler
)
1737 void XMLHttpRequestMainThread::LocalFileToBlobCompleted(BlobImpl
* aBlobImpl
) {
1738 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::DONE
);
1740 mResponseBlobImpl
= aBlobImpl
;
1741 mBlobStorage
= nullptr;
1742 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1744 ChangeStateToDone(mFlagSyncLooping
);
1748 XMLHttpRequestMainThread::OnDataAvailable(nsIRequest
* request
,
1749 nsIInputStream
* inStr
,
1750 uint64_t sourceOffset
,
1752 NS_ENSURE_ARG_POINTER(inStr
);
1754 mProgressSinceLastProgressEvent
= true;
1755 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
1759 if (mResponseType
== XMLHttpRequestResponseType::Blob
) {
1760 nsCOMPtr
<nsIFile
> localFile
;
1761 nsCOMPtr
<nsIURI
> blobURI
;
1762 GetBlobURIFromChannel(request
, getter_AddRefs(blobURI
));
1764 RefPtr
<BlobImpl
> blobImpl
;
1765 rv
= NS_GetBlobForBlobURI(blobURI
, getter_AddRefs(blobImpl
));
1766 if (NS_SUCCEEDED(rv
)) {
1767 mResponseBlobImpl
= blobImpl
;
1770 rv
= GetLocalFileFromChannel(request
, getter_AddRefs(localFile
));
1772 if (NS_WARN_IF(NS_FAILED(rv
))) {
1776 if (mResponseBlobImpl
|| localFile
) {
1777 mBlobStorage
= nullptr;
1778 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1780 // The nsIStreamListener contract mandates us to read from the stream
1781 // before returning.
1783 rv
= inStr
->ReadSegments(DummyStreamReaderFunc
, nullptr, count
,
1785 NS_ENSURE_SUCCESS(rv
, rv
);
1787 ChangeState(XMLHttpRequest_Binding::LOADING
);
1789 // Cancel() must be called with an error. We use
1790 // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
1791 // just because we can retrieve the File from the channel directly.
1792 return request
->Cancel(NS_ERROR_FILE_ALREADY_EXISTS
);
1797 rv
= inStr
->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc
,
1798 (void*)this, count
, &totalRead
);
1799 NS_ENSURE_SUCCESS(rv
, rv
);
1801 // Fire the first progress event/loading state change
1802 if (mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
) {
1803 ChangeState(XMLHttpRequest_Binding::LOADING
);
1804 if (!mFlagSynchronous
) {
1805 DispatchProgressEvent(this, ProgressEventType::progress
, mLoadTransferred
,
1808 mProgressSinceLastProgressEvent
= false;
1811 if (!mFlagSynchronous
&& !mProgressTimerIsActive
) {
1812 StartProgressEventTimer();
1819 XMLHttpRequestMainThread::OnStartRequest(nsIRequest
* request
) {
1820 AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStartRequest", NETWORK
);
1822 nsresult rv
= NS_OK
;
1823 if (!mFirstStartRequestSeen
&& mRequestObserver
) {
1824 mFirstStartRequestSeen
= true;
1825 mRequestObserver
->OnStartRequest(request
);
1828 if (request
!= mChannel
) {
1829 // Can this still happen?
1833 // Don't do anything if we have been aborted
1834 if (mState
== XMLHttpRequest_Binding::UNSENT
) {
1838 // Don't do anything if we're in mid-abort, but let the request
1839 // know (this can happen due to race conditions in valid XHRs,
1840 // see bz1070763 for info).
1842 return NS_BINDING_ABORTED
;
1845 // Don't do anything if we have timed out.
1846 if (mFlagTimedOut
) {
1850 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
1851 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
1854 request
->GetStatus(&status
);
1855 if (mErrorLoad
== ErrorType::eOK
&& NS_FAILED(status
)) {
1856 mErrorLoad
= ErrorType::eRequest
;
1859 // Upload phase is now over. If we were uploading anything,
1860 // stop the timer and fire any final progress events.
1861 if (mUpload
&& !mUploadComplete
&& mErrorLoad
== ErrorType::eOK
&&
1862 !mFlagSynchronous
) {
1863 StopProgressEventTimer();
1865 mUploadTransferred
= mUploadTotal
;
1867 if (mProgressSinceLastProgressEvent
) {
1868 DispatchProgressEvent(mUpload
, ProgressEventType::progress
,
1869 mUploadTransferred
, mUploadTotal
);
1870 mProgressSinceLastProgressEvent
= false;
1873 mUploadComplete
= true;
1874 DispatchProgressEvent(mUpload
, ProgressEventType::load
, mUploadTotal
,
1878 mFlagParseBody
= true;
1879 if (mErrorLoad
== ErrorType::eOK
) {
1880 ChangeState(XMLHttpRequest_Binding::HEADERS_RECEIVED
);
1885 if (!mOverrideMimeType
.IsEmpty()) {
1886 channel
->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
1889 // Fallback to 'application/octet-stream'
1891 channel
->GetContentType(type
);
1892 if (type
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
1893 channel
->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM
));
1898 // Set up arraybuffer
1899 if (mResponseType
== XMLHttpRequestResponseType::Arraybuffer
&&
1900 NS_SUCCEEDED(status
)) {
1901 if (mIsMappedArrayBuffer
) {
1902 nsCOMPtr
<nsIJARChannel
> jarChannel
= do_QueryInterface(channel
);
1904 nsCOMPtr
<nsIURI
> uri
;
1905 rv
= channel
->GetURI(getter_AddRefs(uri
));
1906 if (NS_SUCCEEDED(rv
)) {
1908 nsAutoCString scheme
;
1909 uri
->GetScheme(scheme
);
1910 if (scheme
.LowerCaseEqualsLiteral("jar")) {
1911 nsCOMPtr
<nsIJARURI
> jarURI
= do_QueryInterface(uri
);
1913 jarURI
->GetJAREntry(file
);
1916 nsCOMPtr
<nsIFile
> jarFile
;
1917 jarChannel
->GetJarFile(getter_AddRefs(jarFile
));
1919 mIsMappedArrayBuffer
= false;
1921 rv
= mArrayBufferBuilder
->MapToFileInPackage(file
, jarFile
);
1922 // This can happen legitimately if there are compressed files
1923 // in the jarFile. See bug #1357219. No need to warn on the error.
1924 if (NS_FAILED(rv
)) {
1925 mIsMappedArrayBuffer
= false;
1927 channel
->SetContentType("application/mem-mapped"_ns
);
1933 // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
1934 // and we want it fallback to the malloc way.
1935 if (!mIsMappedArrayBuffer
) {
1936 int64_t contentLength
;
1937 rv
= channel
->GetContentLength(&contentLength
);
1938 if (NS_SUCCEEDED(rv
) && contentLength
> 0 &&
1939 contentLength
< XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE
) {
1940 mArrayBufferBuilder
->SetCapacity(static_cast<int32_t>(contentLength
));
1945 // Set up responseXML
1946 // Note: Main Fetch step 18 requires to ignore body for head/connect methods.
1947 bool parseBody
= (mResponseType
== XMLHttpRequestResponseType::_empty
||
1948 mResponseType
== XMLHttpRequestResponseType::Document
) &&
1949 !(mRequestMethod
.EqualsLiteral("HEAD") ||
1950 mRequestMethod
.EqualsLiteral("CONNECT"));
1953 // Do not try to parse documents if content-length = 0
1954 int64_t contentLength
;
1955 if (NS_SUCCEEDED(mChannel
->GetContentLength(&contentLength
)) &&
1956 contentLength
== 0) {
1962 mWarnAboutSyncHtml
= false;
1963 if (parseBody
&& NS_SUCCEEDED(status
)) {
1964 // We can gain a huge performance win by not even trying to
1965 // parse non-XML data. This also protects us from the situation
1966 // where we have an XML document and sink, but HTML (or other)
1967 // parser, which can produce unreliable results.
1969 channel
->GetContentType(type
);
1971 if ((mResponseType
== XMLHttpRequestResponseType::Document
) &&
1972 type
.EqualsLiteral("text/html")) {
1973 // HTML parsing is only supported for responseType == "document" to
1974 // avoid running the parser and, worse, populating responseXML for
1975 // legacy users of XHR who use responseType == "" for retrieving the
1976 // responseText of text/html resources. This legacy case is so common
1977 // that it's not useful to emit a warning about it.
1978 if (mFlagSynchronous
) {
1979 // We don't make cool new features available in the bad synchronous
1980 // mode. The synchronous mode is for legacy only.
1981 mWarnAboutSyncHtml
= true;
1982 mFlagParseBody
= false;
1986 } else if (!(type
.EqualsLiteral("text/xml") ||
1987 type
.EqualsLiteral("application/xml") ||
1988 type
.RFind("+xml", true, -1, 4) != kNotFound
)) {
1989 // Follow https://xhr.spec.whatwg.org/
1990 // If final MIME type is not null, text/html, text/xml, application/xml,
1991 // or does not end in +xml, return null.
1992 mFlagParseBody
= false;
1995 // The request failed, so we shouldn't be parsing anyway
1996 mFlagParseBody
= false;
1999 if (mFlagParseBody
) {
2000 nsCOMPtr
<nsIURI
> baseURI
, docURI
;
2001 rv
= mChannel
->GetURI(getter_AddRefs(docURI
));
2002 NS_ENSURE_SUCCESS(rv
, rv
);
2005 nsCOMPtr
<Document
> doc
= GetDocumentIfCurrent();
2006 nsCOMPtr
<nsIURI
> chromeXHRDocURI
, chromeXHRDocBaseURI
;
2008 chromeXHRDocURI
= doc
->GetDocumentURI();
2009 chromeXHRDocBaseURI
= doc
->GetBaseURI();
2011 // If we're no longer current, just kill the load, though it really should
2012 // have been killed already.
2013 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
2014 return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
;
2018 // Create an empty document from it.
2019 const auto& emptyStr
= u
""_ns
;
2020 nsIGlobalObject
* global
= DOMEventTargetHelper::GetParentObject();
2022 nsCOMPtr
<nsIPrincipal
> requestingPrincipal
;
2023 rv
= nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
2024 channel
, getter_AddRefs(requestingPrincipal
));
2025 NS_ENSURE_SUCCESS(rv
, rv
);
2027 rv
= NS_NewDOMDocument(
2028 getter_AddRefs(mResponseXML
), emptyStr
, emptyStr
, nullptr, docURI
,
2029 baseURI
, requestingPrincipal
, true, global
,
2030 mIsHtml
? DocumentFlavorHTML
: DocumentFlavorLegacyGuess
);
2031 NS_ENSURE_SUCCESS(rv
, rv
);
2032 mResponseXML
->SetChromeXHRDocURI(chromeXHRDocURI
);
2033 mResponseXML
->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI
);
2035 // suppress parsing failure messages to console for statuses which
2036 // can have empty bodies (see bug 884693).
2037 IgnoredErrorResult rv2
;
2038 uint32_t responseStatus
= GetStatus(rv2
);
2039 if (!rv2
.Failed() && (responseStatus
== 201 || responseStatus
== 202 ||
2040 responseStatus
== 204 || responseStatus
== 205 ||
2041 responseStatus
== 304)) {
2042 mResponseXML
->SetSuppressParserErrorConsoleMessages(true);
2045 if (mPrincipal
->IsSystemPrincipal()) {
2046 mResponseXML
->ForceEnableXULXBL();
2049 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2050 bool isCrossSite
= false;
2051 isCrossSite
= loadInfo
->GetTainting() != LoadTainting::Basic
;
2054 mResponseXML
->DisableCookieAccess();
2057 nsCOMPtr
<nsIStreamListener
> listener
;
2058 nsCOMPtr
<nsILoadGroup
> loadGroup
;
2059 channel
->GetLoadGroup(getter_AddRefs(loadGroup
));
2061 // suppress <parsererror> nodes on XML document parse failure, but only
2062 // for non-privileged code (including Web Extensions). See bug 289714.
2063 if (!IsSystemXHR()) {
2064 mResponseXML
->SetSuppressParserErrorElement(true);
2067 rv
= mResponseXML
->StartDocumentLoad(kLoadAsData
, channel
, loadGroup
,
2068 nullptr, getter_AddRefs(listener
),
2070 NS_ENSURE_SUCCESS(rv
, rv
);
2072 // the spec requires the response document.referrer to be the empty string
2073 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2074 new ReferrerInfo(nullptr, mResponseXML
->ReferrerPolicy());
2075 mResponseXML
->SetReferrerInfo(referrerInfo
);
2077 mXMLParserStreamListener
= listener
;
2078 rv
= mXMLParserStreamListener
->OnStartRequest(request
);
2079 NS_ENSURE_SUCCESS(rv
, rv
);
2082 // Download phase beginning; start the progress event timer if necessary.
2083 if (NS_SUCCEEDED(rv
) && HasListenersFor(nsGkAtoms::onprogress
)) {
2084 StartProgressEventTimer();
2091 XMLHttpRequestMainThread::OnStopRequest(nsIRequest
* request
, nsresult status
) {
2092 AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStopRequest", NETWORK
);
2094 if (request
!= mChannel
) {
2095 // Can this still happen?
2099 // Send the decoder the signal that we've hit the end of the stream,
2100 // but only when decoding text eagerly.
2101 if (mDecoder
&& ((mResponseType
== XMLHttpRequestResponseType::Text
) ||
2102 (mResponseType
== XMLHttpRequestResponseType::Json
) ||
2103 (mResponseType
== XMLHttpRequestResponseType::_empty
&&
2105 AppendToResponseText(Span
<const uint8_t>(), true);
2108 mWaitingForOnStopRequest
= false;
2110 if (mRequestObserver
) {
2111 NS_ASSERTION(mFirstStartRequestSeen
, "Inconsistent state!");
2112 mFirstStartRequestSeen
= false;
2113 mRequestObserver
->OnStopRequest(request
, status
);
2116 // make sure to notify the listener if we were aborted
2117 // XXX in fact, why don't we do the cleanup below in this case??
2118 // UNSENT is for abort calls. See OnStartRequest above.
2119 if (mState
== XMLHttpRequest_Binding::UNSENT
|| mFlagTimedOut
) {
2120 if (mXMLParserStreamListener
)
2121 (void)mXMLParserStreamListener
->OnStopRequest(request
, status
);
2125 // Is this good enough here?
2126 if (mXMLParserStreamListener
&& mFlagParseBody
) {
2127 mXMLParserStreamListener
->OnStopRequest(request
, status
);
2130 mXMLParserStreamListener
= nullptr;
2133 // If window.stop() or other aborts were issued, handle as an abort
2134 if (status
== NS_BINDING_ABORTED
) {
2135 mFlagParseBody
= false;
2136 IgnoredErrorResult rv
;
2137 RequestErrorSteps(ProgressEventType::abort
, NS_OK
, rv
);
2138 ChangeState(XMLHttpRequest_Binding::UNSENT
, false);
2142 // If we were just reading a blob URL, we're already done
2143 if (status
== NS_ERROR_FILE_ALREADY_EXISTS
&& mResponseBlobImpl
) {
2144 ChangeStateToDone(mFlagSyncLooping
);
2148 bool waitingForBlobCreation
= false;
2150 // If we have this error, we have to deal with a file: URL + responseType =
2151 // blob. We have this error because we canceled the channel. The status will
2153 if (!mResponseBlobImpl
&& status
== NS_ERROR_FILE_ALREADY_EXISTS
&&
2154 mResponseType
== XMLHttpRequestResponseType::Blob
) {
2155 nsCOMPtr
<nsIFile
> file
;
2156 nsresult rv
= GetLocalFileFromChannel(request
, getter_AddRefs(file
));
2157 if (NS_WARN_IF(NS_FAILED(rv
))) {
2162 nsAutoCString contentType
;
2163 rv
= mChannel
->GetContentType(contentType
);
2164 if (NS_WARN_IF(NS_FAILED(rv
))) {
2168 ChromeFilePropertyBag bag
;
2169 CopyUTF8toUTF16(contentType
, bag
.mType
);
2171 nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal();
2174 RefPtr
<Promise
> promise
=
2175 FileCreatorHelper::CreateFile(global
, file
, bag
, true, error
);
2176 if (NS_WARN_IF(error
.Failed())) {
2177 return error
.StealNSResult();
2180 FileCreationHandler::Create(promise
, this);
2181 waitingForBlobCreation
= true;
2184 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
2185 NS_ASSERTION(mResponseText
.IsEmpty(), "mResponseText should be empty");
2189 if (NS_SUCCEEDED(status
) &&
2190 mResponseType
== XMLHttpRequestResponseType::Blob
&&
2191 !waitingForBlobCreation
) {
2192 // Smaller files may be written in cache map instead of separate files.
2193 // Also, no-store response cannot be written in persistent cache.
2194 nsAutoCString contentType
;
2195 if (!mOverrideMimeType
.IsEmpty()) {
2196 contentType
.Assign(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
2198 mChannel
->GetContentType(contentType
);
2201 // mBlobStorage can be null if the channel is non-file non-cacheable
2202 // and if the response length is zero.
2203 MaybeCreateBlobStorage();
2204 mBlobStorage
->GetBlobImplWhenReady(contentType
, this);
2205 waitingForBlobCreation
= true;
2207 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
2208 NS_ASSERTION(mResponseText
.IsEmpty(), "mResponseText should be empty");
2209 } else if (NS_SUCCEEDED(status
) && !mIsMappedArrayBuffer
&&
2210 mResponseType
== XMLHttpRequestResponseType::Arraybuffer
) {
2211 // set the capacity down to the actual length, to realloc back
2212 // down to the actual size
2213 if (!mArrayBufferBuilder
->SetCapacity(mArrayBufferBuilder
->Length())) {
2214 // this should never happen!
2215 status
= NS_ERROR_UNEXPECTED
;
2219 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
2220 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
2222 channel
->SetNotificationCallbacks(nullptr);
2223 mNotificationCallbacks
= nullptr;
2224 mChannelEventSink
= nullptr;
2225 mProgressEventSink
= nullptr;
2227 bool wasSync
= mFlagSyncLooping
;
2228 mFlagSyncLooping
= false;
2229 mRequestSentTime
= 0;
2231 // update our charset and decoder to match mResponseXML,
2232 // before it is possibly nulled out
2233 MatchCharsetAndDecoderToResponseDocument();
2235 if (NS_FAILED(status
)) {
2236 // This can happen if the server is unreachable. Other possible
2237 // reasons are that the user leaves the page or hits the ESC key.
2239 mErrorLoad
= ErrorType::eUnreachable
;
2240 mResponseXML
= nullptr;
2243 // If we're uninitialized at this point, we encountered an error
2244 // earlier and listeners have already been notified. Also we do
2245 // not want to do this if we already completed.
2246 if (mState
== XMLHttpRequest_Binding::UNSENT
||
2247 mState
== XMLHttpRequest_Binding::DONE
) {
2251 if (!mResponseXML
) {
2252 mFlagParseBody
= false;
2254 // We postpone the 'done' until the creation of the Blob is completed.
2255 if (!waitingForBlobCreation
) {
2256 ChangeStateToDone(wasSync
);
2263 NS_ASSERTION(!mFlagSyncLooping
,
2264 "We weren't supposed to support HTML parsing with XHR!");
2265 mParseEndListener
= new nsXHRParseEndListener(this);
2266 RefPtr
<EventTarget
> eventTarget
= mResponseXML
;
2267 EventListenerManager
* manager
= eventTarget
->GetOrCreateListenerManager();
2268 manager
->AddEventListenerByType(mParseEndListener
,
2269 kLiteralString_DOMContentLoaded
,
2270 TrustedEventsAtSystemGroupBubble());
2273 mFlagParseBody
= false;
2276 // We might have been sent non-XML data. If that was the case,
2277 // we should null out the document member. The idea in this
2278 // check here is that if there is no document element it is not
2279 // an XML document. We might need a fancier check...
2280 if (!mResponseXML
->GetRootElement()) {
2281 mErrorParsingXML
= true;
2282 mResponseXML
= nullptr;
2284 ChangeStateToDone(wasSync
);
2288 void XMLHttpRequestMainThread::OnBodyParseEnd() {
2289 mFlagParseBody
= false;
2290 mParseEndListener
= nullptr;
2291 ChangeStateToDone(mFlagSyncLooping
);
2294 void XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument() {
2297 mDecoder
->Encoding() != mResponseXML
->GetDocumentCharacterSet())) {
2298 TruncateResponseText();
2299 mResponseBodyDecodedPos
= 0;
2300 mEofDecoded
= false;
2301 mDecoder
= mResponseXML
->GetDocumentCharacterSet()->NewDecoder();
2304 void XMLHttpRequestMainThread::DisconnectDoneNotifier() {
2305 if (mDelayedDoneNotifier
) {
2306 // Disconnect may release the last reference to 'this'.
2307 RefPtr
<XMLHttpRequestMainThread
> kungfuDeathGrip
= this;
2308 mDelayedDoneNotifier
->Disconnect();
2309 mDelayedDoneNotifier
= nullptr;
2313 void XMLHttpRequestMainThread::ChangeStateToDone(bool aWasSync
) {
2314 DisconnectDoneNotifier();
2316 if (!mForWorker
&& !aWasSync
&& mChannel
) {
2317 // If the top level page is loading, try to postpone the handling of the
2319 nsLoadFlags loadFlags
= 0;
2320 mChannel
->GetLoadFlags(&loadFlags
);
2321 if (loadFlags
& nsIRequest::LOAD_BACKGROUND
) {
2322 nsPIDOMWindowInner
* owner
= GetOwner();
2323 BrowsingContext
* bc
= owner
? owner
->GetBrowsingContext() : nullptr;
2324 bc
= bc
? bc
->Top() : nullptr;
2325 if (bc
&& bc
->IsLoading()) {
2326 MOZ_ASSERT(!mDelayedDoneNotifier
);
2327 RefPtr
<XMLHttpRequestDoneNotifier
> notifier
=
2328 new XMLHttpRequestDoneNotifier(this);
2329 mDelayedDoneNotifier
= notifier
;
2330 bc
->AddDeprioritizedLoadRunner(notifier
);
2336 ChangeStateToDoneInternal();
2339 void XMLHttpRequestMainThread::ChangeStateToDoneInternal() {
2340 DisconnectDoneNotifier();
2341 StopProgressEventTimer();
2343 MOZ_ASSERT(!mFlagParseBody
,
2344 "ChangeStateToDone() called before async HTML parsing is done.");
2348 if (mTimeoutTimer
) {
2349 mTimeoutTimer
->Cancel();
2352 // Per spec, fire the last download progress event, if any,
2353 // before readystatechange=4/done. (Note that 0-sized responses
2354 // will have not sent a progress event yet, so one must be sent here).
2355 if (!mFlagSynchronous
&&
2356 (!mLoadTransferred
|| mProgressSinceLastProgressEvent
)) {
2357 DispatchProgressEvent(this, ProgressEventType::progress
, mLoadTransferred
,
2359 mProgressSinceLastProgressEvent
= false;
2362 // Notify the document when an XHR request completes successfully.
2363 // This is used by the password manager as a hint to observe DOM mutations.
2364 // Call this prior to changing state to DONE to ensure we set up the
2365 // observer before mutations occur.
2366 if (mErrorLoad
== ErrorType::eOK
) {
2367 Document
* doc
= GetDocumentIfCurrent();
2369 doc
->NotifyFetchOrXHRSuccess();
2373 // Per spec, fire readystatechange=4/done before final error events.
2374 ChangeState(XMLHttpRequest_Binding::DONE
, true);
2376 // Per spec, if we failed in the upload phase, fire a final error
2377 // and loadend events for the upload after readystatechange=4/done.
2378 if (!mFlagSynchronous
&& mUpload
&& !mUploadComplete
) {
2379 DispatchProgressEvent(mUpload
, ProgressEventType::error
, 0, -1);
2382 // Per spec, fire download's load/error and loadend events after
2383 // readystatechange=4/done (and of course all upload events).
2384 if (mErrorLoad
!= ErrorType::eOK
) {
2385 DispatchProgressEvent(this, ProgressEventType::error
, 0, -1);
2387 DispatchProgressEvent(this, ProgressEventType::load
, mLoadTransferred
,
2391 if (mErrorLoad
!= ErrorType::eOK
) {
2392 // By nulling out channel here we make it so that Send() can test
2393 // for that and throw. Also calling the various status
2394 // methods/members will not throw.
2395 // This matches what IE does.
2400 nsresult
XMLHttpRequestMainThread::CreateChannel() {
2401 // When we are called from JS we can find the load group for the page,
2402 // and add ourselves to it. This way any pending requests
2403 // will be automatically aborted if the user leaves the page.
2404 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetLoadGroup();
2406 nsSecurityFlags secFlags
;
2407 nsLoadFlags loadFlags
= nsIRequest::LOAD_BACKGROUND
;
2408 uint32_t sandboxFlags
= 0;
2409 if (mPrincipal
->IsSystemPrincipal()) {
2410 // When chrome is loading we want to make sure to sandbox any potential
2411 // result document. We also want to allow cross-origin loads.
2412 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
2413 sandboxFlags
= SANDBOXED_ORIGIN
;
2414 } else if (IsSystemXHR()) {
2415 // For pages that have appropriate permissions, we want to still allow
2416 // cross-origin loads, but make sure that the any potential result
2417 // documents get the same principal as the loader.
2418 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
|
2419 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
2420 loadFlags
|= nsIChannel::LOAD_BYPASS_SERVICE_WORKER
;
2422 // Otherwise use CORS. Again, make sure that potential result documents
2423 // use the same principal as the loader.
2424 secFlags
= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
|
2425 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
2429 secFlags
|= nsILoadInfo::SEC_COOKIES_OMIT
;
2432 // Use the responsibleDocument if we have it, except for dedicated workers
2433 // where it will be the parent document, which is not the one we want to use.
2435 nsCOMPtr
<Document
> responsibleDocument
= GetDocumentIfCurrent();
2436 if (responsibleDocument
&&
2437 responsibleDocument
->NodePrincipal() == mPrincipal
) {
2438 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
,
2439 responsibleDocument
, secFlags
,
2440 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2441 nullptr, // aPerformanceStorage
2443 nullptr, // aCallbacks
2444 loadFlags
, nullptr, sandboxFlags
);
2445 } else if (mClientInfo
.isSome()) {
2446 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
, mPrincipal
,
2447 mClientInfo
.ref(), mController
, secFlags
,
2448 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2450 mPerformanceStorage
, // aPerformanceStorage
2452 nullptr, // aCallbacks
2453 loadFlags
, nullptr, sandboxFlags
);
2455 // Otherwise use the principal.
2456 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
, mPrincipal
,
2457 secFlags
, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2459 mPerformanceStorage
, // aPerformanceStorage
2461 nullptr, // aCallbacks
2462 loadFlags
, nullptr, sandboxFlags
);
2464 NS_ENSURE_SUCCESS(rv
, rv
);
2466 if (mCSPEventListener
) {
2467 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2468 rv
= loadInfo
->SetCspEventListener(mCSPEventListener
);
2469 NS_ENSURE_SUCCESS(rv
, rv
);
2472 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2474 rv
= httpChannel
->SetRequestMethod(mRequestMethod
);
2475 NS_ENSURE_SUCCESS(rv
, rv
);
2477 httpChannel
->SetSource(profiler_capture_backtrace());
2479 // Set the initiator type
2480 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChannel
));
2482 timedChannel
->SetInitiatorType(u
"xmlhttprequest"_ns
);
2489 void XMLHttpRequestMainThread::MaybeLowerChannelPriority() {
2490 nsCOMPtr
<Document
> doc
= GetDocumentIfCurrent();
2496 if (!jsapi
.Init(GetOwnerGlobal())) {
2500 JSContext
* cx
= jsapi
.cx();
2502 if (!doc
->IsScriptTracking(cx
)) {
2506 if (StaticPrefs::network_http_tailing_enabled()) {
2507 nsCOMPtr
<nsIClassOfService
> cos
= do_QueryInterface(mChannel
);
2509 // Adding TailAllowed to overrule the Unblocked flag, but to preserve
2510 // the effect of Unblocked when tailing is off.
2511 cos
->AddClassFlags(nsIClassOfService::Throttleable
|
2512 nsIClassOfService::Tail
|
2513 nsIClassOfService::TailAllowed
);
2517 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(mChannel
);
2519 p
->SetPriority(nsISupportsPriority::PRIORITY_LOWEST
);
2523 nsresult
XMLHttpRequestMainThread::InitiateFetch(
2524 already_AddRefed
<nsIInputStream
> aUploadStream
, int64_t aUploadLength
,
2525 nsACString
& aUploadContentType
) {
2527 nsCOMPtr
<nsIInputStream
> uploadStream
= std::move(aUploadStream
);
2529 if (!uploadStream
) {
2530 RefPtr
<PreloaderBase
> preload
= FindPreload();
2532 // Because of bug 682305, we can't let listener be the XHR object itself
2533 // because JS wouldn't be able to use it. So create a listener around
2534 // 'this'. Make sure to hold a strong reference so that we don't leak the
2536 nsCOMPtr
<nsIStreamListener
> listener
=
2537 new net::nsStreamListenerWrapper(this);
2538 rv
= preload
->AsyncConsume(listener
);
2539 if (NS_SUCCEEDED(rv
)) {
2540 mFromPreload
= true;
2542 // May be null when the preload has already finished, but the XHR code
2543 // is safe to live with it.
2544 mChannel
= preload
->Channel();
2545 MOZ_ASSERT(mChannel
);
2546 EnsureChannelContentType();
2554 // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
2555 // in turn keeps STOP button from becoming active. If the consumer passed in
2556 // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
2557 // necko won't generate any progress notifications.
2558 if (HasListenersFor(nsGkAtoms::onprogress
) ||
2559 (mUpload
&& mUpload
->HasListenersFor(nsGkAtoms::onprogress
))) {
2560 nsLoadFlags loadFlags
;
2561 mChannel
->GetLoadFlags(&loadFlags
);
2562 loadFlags
&= ~nsIRequest::LOAD_BACKGROUND
;
2563 loadFlags
|= nsIRequest::LOAD_NORMAL
;
2564 mChannel
->SetLoadFlags(loadFlags
);
2567 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2569 // If the user hasn't overridden the Accept header, set it to */* per spec.
2570 if (!mAuthorRequestHeaders
.Has("accept")) {
2571 mAuthorRequestHeaders
.Set("accept", "*/*"_ns
);
2574 mAuthorRequestHeaders
.ApplyToChannel(httpChannel
, false);
2576 if (!IsSystemXHR()) {
2577 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
2578 nsCOMPtr
<Document
> doc
= owner
? owner
->GetExtantDoc() : nullptr;
2579 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2580 ReferrerInfo::CreateForFetch(mPrincipal
, doc
);
2581 Unused
<< httpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
2584 // Some extensions override the http protocol handler and provide their own
2585 // implementation. The channels returned from that implementation don't
2586 // always seem to implement the nsIUploadChannel2 interface, presumably
2587 // because it's a new interface. Eventually we should remove this and simply
2588 // require that http channels implement the new interface (see bug 529041).
2589 nsCOMPtr
<nsIUploadChannel2
> uploadChannel2
= do_QueryInterface(httpChannel
);
2590 if (!uploadChannel2
) {
2591 nsCOMPtr
<nsIConsoleService
> consoleService
=
2592 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
2593 if (consoleService
) {
2594 consoleService
->LogStringMessage(
2595 u
"Http channel implementation doesn't support nsIUploadChannel2. "
2596 "An extension has supplied a non-functional http protocol handler. "
2597 "This will break behavior and in future releases not work at all.");
2602 // If necessary, wrap the stream in a buffered stream so as to guarantee
2603 // support for our upload when calling ExplicitSetUploadStream.
2604 if (!NS_InputStreamIsBuffered(uploadStream
)) {
2605 nsCOMPtr
<nsIInputStream
> bufferedStream
;
2606 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
),
2607 uploadStream
.forget(), 4096);
2608 NS_ENSURE_SUCCESS(rv
, rv
);
2610 uploadStream
= bufferedStream
;
2613 // We want to use a newer version of the upload channel that won't
2614 // ignore the necessary headers for an empty Content-Type.
2615 nsCOMPtr
<nsIUploadChannel2
> uploadChannel2(
2616 do_QueryInterface(httpChannel
));
2617 // This assertion will fire if buggy extensions are installed
2618 NS_ASSERTION(uploadChannel2
, "http must support nsIUploadChannel2");
2619 if (uploadChannel2
) {
2620 uploadChannel2
->ExplicitSetUploadStream(
2621 uploadStream
, aUploadContentType
, mUploadTotal
, mRequestMethod
,
2624 // The http channel doesn't support the new nsIUploadChannel2.
2625 // Emulate it as best we can using nsIUploadChannel.
2626 if (aUploadContentType
.IsEmpty()) {
2627 aUploadContentType
.AssignLiteral("application/octet-stream");
2629 nsCOMPtr
<nsIUploadChannel
> uploadChannel
=
2630 do_QueryInterface(httpChannel
);
2631 uploadChannel
->SetUploadStream(uploadStream
, aUploadContentType
,
2633 // Reset the method to its original value
2634 rv
= httpChannel
->SetRequestMethod(mRequestMethod
);
2635 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2640 // Due to the chrome-only XHR.channel API, we need a hacky way to set the
2641 // SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
2642 // .withCredentials can be called after open() is called.
2643 // Not doing this for privileged system XHRs since those don't use CORS.
2644 if (!IsSystemXHR() && !mIsAnon
&& mFlagACwithCredentials
) {
2645 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2646 static_cast<net::LoadInfo
*>(loadInfo
.get())->SetIncludeCookiesSecFlag();
2649 // We never let XHR be blocked by head CSS/JS loads to avoid potential
2650 // deadlock where server generation of CSS/JS requires an XHR signal.
2651 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(mChannel
));
2653 cos
->AddClassFlags(nsIClassOfService::Unblocked
);
2655 // Mark channel as urgent-start if the XHR is triggered by user input
2657 if (UserActivation::IsHandlingUserInput()) {
2658 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
2662 // Disable Necko-internal response timeouts.
2663 nsCOMPtr
<nsIHttpChannelInternal
> internalHttpChannel(
2664 do_QueryInterface(mChannel
));
2665 if (internalHttpChannel
) {
2666 rv
= internalHttpChannel
->SetResponseTimeoutEnabled(false);
2667 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2671 AddLoadFlags(mChannel
, nsIChannel::LOAD_EXPLICIT_CREDENTIALS
);
2674 // Bypass the network cache in cases where it makes no sense:
2675 // POST responses are always unique, and we provide no API that would
2676 // allow our consumers to specify a "cache key" to access old POST
2677 // responses, so they are not worth caching.
2678 if (mRequestMethod
.EqualsLiteral("POST")) {
2679 AddLoadFlags(mChannel
, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE
|
2680 nsIRequest::INHIBIT_CACHING
);
2682 // When we are sync loading, we need to bypass the local cache when it would
2683 // otherwise block us waiting for exclusive access to the cache. If we
2684 // don't do this, then we could dead lock in some cases (see bug 309424).
2686 // Also don't block on the cache entry on async if it is busy - favoring
2687 // parallelism over cache hit rate for xhr. This does not disable the cache
2688 // everywhere - only in cases where more than one channel for the same URI
2689 // is accessed simultanously.
2690 AddLoadFlags(mChannel
, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
);
2693 EnsureChannelContentType();
2695 // Set up the preflight if needed
2696 if (!IsSystemXHR()) {
2697 nsTArray
<nsCString
> CORSUnsafeHeaders
;
2698 mAuthorRequestHeaders
.GetCORSUnsafeHeaders(CORSUnsafeHeaders
);
2699 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2700 loadInfo
->SetCorsPreflightInfo(CORSUnsafeHeaders
,
2701 mFlagHadUploadListenersOnSend
);
2704 // Hook us up to listen to redirects and the like. Only do this very late
2705 // since this creates a cycle between the channel and us. This cycle has
2706 // to be manually broken if anything below fails.
2707 mChannel
->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks
));
2708 mChannel
->SetNotificationCallbacks(this);
2710 if (internalHttpChannel
) {
2711 internalHttpChannel
->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
2714 // Because of bug 682305, we can't let listener be the XHR object itself
2715 // because JS wouldn't be able to use it. So create a listener around 'this'.
2716 // Make sure to hold a strong reference so that we don't leak the wrapper.
2717 nsCOMPtr
<nsIStreamListener
> listener
= new net::nsStreamListenerWrapper(this);
2719 // Check if this XHR is created from a tracking script.
2720 // If yes, lower the channel's priority.
2721 if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
2722 MaybeLowerChannelPriority();
2725 // Associate any originating stack with the channel.
2726 NotifyNetworkMonitorAlternateStack(mChannel
, std::move(mOriginStack
));
2728 // Start reading from the channel
2729 rv
= mChannel
->AsyncOpen(listener
);
2731 if (NS_WARN_IF(NS_FAILED(rv
))) {
2732 // Drop our ref to the channel to avoid cycles. Also drop channel's
2733 // ref to us to be extra safe.
2734 mChannel
->SetNotificationCallbacks(mNotificationCallbacks
);
2737 mErrorLoad
= ErrorType::eChannelOpen
;
2739 // Per spec, we throw on sync errors, but not async.
2740 if (mFlagSynchronous
) {
2741 mState
= XMLHttpRequest_Binding::DONE
;
2742 return NS_ERROR_DOM_NETWORK_ERR
;
2749 already_AddRefed
<PreloaderBase
> XMLHttpRequestMainThread::FindPreload() {
2750 Document
* doc
= GetDocumentIfCurrent();
2754 if (mPrincipal
->IsSystemPrincipal() || IsSystemXHR()) {
2757 if (!mRequestMethod
.EqualsLiteral("GET")) {
2758 // Preload can only do GET.
2761 if (!mAuthorRequestHeaders
.IsEmpty()) {
2762 // Preload can't set headers.
2766 // mIsAnon overrules mFlagACwithCredentials.
2767 CORSMode cors
= (mIsAnon
|| !mFlagACwithCredentials
)
2768 ? CORSMode::CORS_ANONYMOUS
2769 : CORSMode::CORS_USE_CREDENTIALS
;
2770 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2771 ReferrerInfo::CreateForFetch(mPrincipal
, doc
);
2772 auto key
= PreloadHashKey::CreateAsFetch(mRequestURL
, cors
);
2773 RefPtr
<PreloaderBase
> preload
= doc
->Preloads().LookupPreload(key
);
2778 preload
->RemoveSelf(doc
);
2779 preload
->NotifyUsage(PreloaderBase::LoadBackground::Keep
);
2781 return preload
.forget();
2784 void XMLHttpRequestMainThread::EnsureChannelContentType() {
2785 MOZ_ASSERT(mChannel
);
2787 // Since we expect XML data, set the type hint accordingly
2788 // if the channel doesn't know any content type.
2789 // This means that we always try to parse local files as XML
2790 // ignoring return value, as this is not critical. Use text/xml as fallback
2792 nsAutoCString contentType
;
2793 if (NS_FAILED(mChannel
->GetContentType(contentType
)) ||
2794 contentType
.IsEmpty() ||
2795 contentType
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
2796 mChannel
->SetContentType("text/xml"_ns
);
2800 void XMLHttpRequestMainThread::UnsuppressEventHandlingAndResume() {
2801 MOZ_ASSERT(NS_IsMainThread());
2802 MOZ_ASSERT(mFlagSynchronous
);
2804 if (mSuspendedDoc
) {
2805 mSuspendedDoc
->UnsuppressEventHandlingAndFireEvents(true);
2806 mSuspendedDoc
= nullptr;
2809 if (mResumeTimeoutRunnable
) {
2810 DispatchToMainThread(mResumeTimeoutRunnable
.forget());
2811 mResumeTimeoutRunnable
= nullptr;
2815 void XMLHttpRequestMainThread::Send(
2817 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
>&
2820 NOT_CALLABLE_IN_SYNC_SEND_RV
2822 if (aData
.IsNull()) {
2823 SendInternal(nullptr, false, aRv
);
2827 if (aData
.Value().IsDocument()) {
2828 BodyExtractor
<Document
> body(&aData
.Value().GetAsDocument());
2829 SendInternal(&body
, true, aRv
);
2833 if (aData
.Value().IsBlob()) {
2834 BodyExtractor
<const Blob
> body(&aData
.Value().GetAsBlob());
2835 SendInternal(&body
, false, aRv
);
2839 if (aData
.Value().IsArrayBuffer()) {
2840 BodyExtractor
<const ArrayBuffer
> body(&aData
.Value().GetAsArrayBuffer());
2841 SendInternal(&body
, false, aRv
);
2845 if (aData
.Value().IsArrayBufferView()) {
2846 BodyExtractor
<const ArrayBufferView
> body(
2847 &aData
.Value().GetAsArrayBufferView());
2848 SendInternal(&body
, false, aRv
);
2852 if (aData
.Value().IsFormData()) {
2853 BodyExtractor
<const FormData
> body(&aData
.Value().GetAsFormData());
2854 SendInternal(&body
, false, aRv
);
2858 if (aData
.Value().IsURLSearchParams()) {
2859 BodyExtractor
<const URLSearchParams
> body(
2860 &aData
.Value().GetAsURLSearchParams());
2861 SendInternal(&body
, false, aRv
);
2865 if (aData
.Value().IsUSVString()) {
2866 BodyExtractor
<const nsAString
> body(&aData
.Value().GetAsUSVString());
2867 SendInternal(&body
, true, aRv
);
2872 nsresult
XMLHttpRequestMainThread::MaybeSilentSendFailure(nsresult aRv
) {
2873 // Per spec, silently fail on async request failures; throw for sync.
2874 if (mFlagSynchronous
) {
2875 mState
= XMLHttpRequest_Binding::DONE
;
2876 return NS_ERROR_DOM_NETWORK_ERR
;
2879 // Defer the actual sending of async events just in case listeners
2880 // are attached after the send() method is called.
2881 Unused
<< NS_WARN_IF(
2882 NS_FAILED(DispatchToMainThread(NewRunnableMethod
<ProgressEventType
>(
2883 "dom::XMLHttpRequestMainThread::CloseRequestWithError", this,
2884 &XMLHttpRequestMainThread::CloseRequestWithError
,
2885 ProgressEventType::error
))));
2889 void XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase
* aBody
,
2890 bool aBodyIsDocumentOrString
,
2892 MOZ_ASSERT(NS_IsMainThread());
2895 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
2900 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
2901 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
2907 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
2911 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
2912 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
2916 // If open() failed to create the channel, then throw a network error
2917 // as per spec. We really should create the channel here in send(), but
2918 // we have internal code relying on the channel being created in open().
2920 mFlagSend
= true; // so CloseRequestWithError sets us to DONE.
2921 aRv
= MaybeSilentSendFailure(NS_ERROR_DOM_NETWORK_ERR
);
2925 // non-GET requests aren't allowed for blob.
2926 if (IsBlobURI(mRequestURL
) && !mRequestMethod
.EqualsLiteral("GET")) {
2927 mFlagSend
= true; // so CloseRequestWithError sets us to DONE.
2928 aRv
= MaybeSilentSendFailure(NS_ERROR_DOM_NETWORK_ERR
);
2932 // XXX We should probably send a warning to the JS console
2933 // if there are no event listeners set and we are doing
2934 // an asynchronous call.
2936 mUploadTransferred
= 0;
2938 // By default we don't have any upload, so mark upload complete.
2939 mUploadComplete
= true;
2940 mErrorLoad
= ErrorType::eOK
;
2942 nsCOMPtr
<nsIInputStream
> uploadStream
;
2943 nsAutoCString uploadContentType
;
2944 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2945 if (aBody
&& httpChannel
&& !mRequestMethod
.EqualsLiteral("GET") &&
2946 !mRequestMethod
.EqualsLiteral("HEAD")) {
2947 nsAutoCString charset
;
2948 nsAutoCString defaultContentType
;
2950 aRv
= aBody
->GetAsStream(getter_AddRefs(uploadStream
), &size_u64
,
2951 defaultContentType
, charset
);
2956 // make sure it fits within js MAX_SAFE_INTEGER
2958 net::InScriptableRange(size_u64
) ? static_cast<int64_t>(size_u64
) : -1;
2961 // If author set no Content-Type, use the default from GetAsStream().
2962 mAuthorRequestHeaders
.Get("content-type", uploadContentType
);
2963 if (uploadContentType
.IsVoid()) {
2964 uploadContentType
= defaultContentType
;
2965 } else if (aBodyIsDocumentOrString
&&
2966 StaticPrefs::dom_xhr_standard_content_type_normalization()) {
2967 UniquePtr
<CMimeType
> contentTypeRecord
=
2968 CMimeType::Parse(uploadContentType
);
2969 nsAutoCString charset
;
2970 if (contentTypeRecord
&&
2971 contentTypeRecord
->GetParameterValue(kLiteralString_charset
,
2973 !charset
.EqualsIgnoreCase("utf-8")) {
2974 contentTypeRecord
->SetParameterValue(kLiteralString_charset
,
2975 kLiteralString_UTF_8
);
2976 contentTypeRecord
->Serialize(uploadContentType
);
2978 } else if (!charset
.IsEmpty()) {
2979 // We don't want to set a charset for streams.
2980 // Replace all case-insensitive matches of the charset in the
2981 // content-type with the correct case.
2982 RequestHeaders::CharsetIterator
iter(uploadContentType
);
2983 while (iter
.Next()) {
2984 if (!iter
.Equals(charset
, nsCaseInsensitiveCStringComparator
)) {
2985 iter
.Replace(charset
);
2990 mUploadComplete
= false;
2996 // Check if we should enable cross-origin upload listeners.
2997 if (mUpload
&& mUpload
->HasListeners()) {
2998 mFlagHadUploadListenersOnSend
= true;
3001 mIsMappedArrayBuffer
= false;
3002 if (mResponseType
== XMLHttpRequestResponseType::Arraybuffer
&&
3003 StaticPrefs::dom_mapped_arraybuffer_enabled()) {
3004 nsCOMPtr
<nsIURI
> uri
;
3005 nsAutoCString scheme
;
3007 aRv
= mChannel
->GetURI(getter_AddRefs(uri
));
3008 if (!aRv
.Failed()) {
3009 uri
->GetScheme(scheme
);
3010 if (scheme
.LowerCaseEqualsLiteral("jar")) {
3011 mIsMappedArrayBuffer
= true;
3016 aRv
= InitiateFetch(uploadStream
.forget(), mUploadTotal
, uploadContentType
);
3021 // Start our timeout
3022 mRequestSentTime
= PR_Now();
3023 StartTimeoutTimer();
3025 mWaitingForOnStopRequest
= true;
3030 // If we're synchronous, spin an event loop here and wait
3031 if (mFlagSynchronous
) {
3032 mFlagSyncLooping
= true;
3035 if (nsCOMPtr
<nsPIDOMWindowOuter
> topWindow
=
3036 GetOwner()->GetOuterWindow()->GetInProcessTop()) {
3037 if (nsCOMPtr
<nsPIDOMWindowInner
> topInner
=
3038 topWindow
->GetCurrentInnerWindow()) {
3039 mSuspendedDoc
= topWindow
->GetExtantDoc();
3040 if (mSuspendedDoc
) {
3041 mSuspendedDoc
->SuppressEventHandling();
3043 topInner
->Suspend();
3044 mResumeTimeoutRunnable
= new nsResumeTimeoutsEvent(topInner
);
3049 SuspendEventDispatching();
3050 StopProgressEventTimer();
3051 auto scopeExit
= MakeScopeExit([&] {
3052 CancelSyncTimeoutTimer();
3053 UnsuppressEventHandlingAndResume();
3054 ResumeEventDispatching();
3057 SyncTimeoutType syncTimeoutType
= MaybeStartSyncTimeoutTimer();
3058 if (syncTimeoutType
== eErrorOrExpired
) {
3060 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
3064 nsAutoSyncOperation
sync(mSuspendedDoc
,
3065 SyncOperationBehavior::eSuspendInput
);
3066 if (!SpinEventLoopUntil([&]() { return !mFlagSyncLooping
; })) {
3067 aRv
.Throw(NS_ERROR_UNEXPECTED
);
3071 // Time expired... We should throw.
3072 if (syncTimeoutType
== eTimerStarted
&& !mSyncTimeoutTimer
) {
3073 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
3077 // Now that we've successfully opened the channel, we can change state. Note
3078 // that this needs to come after the AsyncOpen() and rv check, because this
3079 // can run script that would try to restart this request, and that could end
3080 // up doing our AsyncOpen on a null channel if the reentered AsyncOpen
3082 StopProgressEventTimer();
3084 // Upload phase beginning; start the progress event timer if necessary.
3085 if (mUpload
&& mUpload
->HasListenersFor(nsGkAtoms::onprogress
)) {
3086 StartProgressEventTimer();
3088 // Dispatch loadstart events
3089 DispatchProgressEvent(this, ProgressEventType::loadstart
, 0, -1);
3090 if (mUpload
&& !mUploadComplete
) {
3091 DispatchProgressEvent(mUpload
, ProgressEventType::loadstart
, 0,
3097 aRv
= MaybeSilentSendFailure(NS_ERROR_DOM_NETWORK_ERR
);
3101 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
3102 void XMLHttpRequestMainThread::SetRequestHeader(const nsACString
& aName
,
3103 const nsACString
& aValue
,
3105 NOT_CALLABLE_IN_SYNC_SEND_RV
3108 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
3109 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
3115 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3120 nsAutoCString value
;
3121 NS_TrimHTTPWhitespace(aValue
, value
);
3124 if (!NS_IsValidHTTPToken(aName
) || !NS_IsReasonableHTTPHeaderValue(value
)) {
3125 aRv
.Throw(NS_ERROR_DOM_INVALID_HEADER_NAME
);
3130 bool isPrivilegedCaller
= IsSystemXHR();
3131 bool isForbiddenHeader
= nsContentUtils::IsForbiddenRequestHeader(aName
);
3132 if (!isPrivilegedCaller
&& isForbiddenHeader
) {
3133 AutoTArray
<nsString
, 1> params
;
3134 CopyUTF8toUTF16(aName
, *params
.AppendElement());
3135 LogMessage("ForbiddenHeaderWarning", GetOwner(), params
);
3140 // Skipping for now, as normalizing the case of header names may not be
3141 // web-compatible. See bug 1285036.
3144 // Gecko-specific: invalid headers can be set by privileged
3145 // callers, but will not merge.
3146 if (isPrivilegedCaller
&& isForbiddenHeader
) {
3147 mAuthorRequestHeaders
.Set(aName
, value
);
3149 mAuthorRequestHeaders
.MergeOrSet(aName
, value
);
3153 void XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout
, ErrorResult
& aRv
) {
3154 NOT_CALLABLE_IN_SYNC_SEND_RV
3156 if (mFlagSynchronous
&& mState
!= XMLHttpRequest_Binding::UNSENT
&&
3157 HasOrHasHadOwner()) {
3158 /* Timeout is not supported for synchronous requests with an owning window,
3160 LogMessage("TimeoutSyncXHRWarning", GetOwner());
3161 aRv
.ThrowInvalidAccessError(
3162 "synchronous XMLHttpRequests do not support timeout and responseType");
3166 mTimeoutMilliseconds
= aTimeout
;
3167 if (mRequestSentTime
) {
3168 StartTimeoutTimer();
3172 nsIEventTarget
* XMLHttpRequestMainThread::GetTimerEventTarget() {
3173 if (nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal()) {
3174 return global
->EventTargetFor(TaskCategory::Other
);
3179 nsresult
XMLHttpRequestMainThread::DispatchToMainThread(
3180 already_AddRefed
<nsIRunnable
> aRunnable
) {
3181 if (nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal()) {
3182 nsCOMPtr
<nsIEventTarget
> target
=
3183 global
->EventTargetFor(TaskCategory::Other
);
3186 return target
->Dispatch(std::move(aRunnable
), NS_DISPATCH_NORMAL
);
3189 return NS_DispatchToMainThread(std::move(aRunnable
));
3192 void XMLHttpRequestMainThread::StartTimeoutTimer() {
3195 "StartTimeoutTimer mustn't be called before the request was sent!");
3196 if (mState
== XMLHttpRequest_Binding::DONE
) {
3201 if (mTimeoutTimer
) {
3202 mTimeoutTimer
->Cancel();
3205 if (!mTimeoutMilliseconds
) {
3209 if (!mTimeoutTimer
) {
3210 mTimeoutTimer
= NS_NewTimer(GetTimerEventTarget());
3213 (uint32_t)((PR_Now() - mRequestSentTime
) / PR_USEC_PER_MSEC
);
3214 mTimeoutTimer
->InitWithCallback(
3215 this, mTimeoutMilliseconds
> elapsed
? mTimeoutMilliseconds
- elapsed
: 0,
3216 nsITimer::TYPE_ONE_SHOT
);
3219 uint16_t XMLHttpRequestMainThread::ReadyState() const { return mState
; }
3221 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString
& aMimeType
,
3223 NOT_CALLABLE_IN_SYNC_SEND_RV
3225 if (mState
== XMLHttpRequest_Binding::LOADING
||
3226 mState
== XMLHttpRequest_Binding::DONE
) {
3227 aRv
.ThrowInvalidStateError(
3228 "Cannot call 'overrideMimeType()' on XMLHttpRequest after 'send()' "
3229 "(when its state is LOADING or DONE).");
3233 UniquePtr
<MimeType
> parsed
= MimeType::Parse(aMimeType
);
3235 parsed
->Serialize(mOverrideMimeType
);
3237 mOverrideMimeType
.AssignLiteral(APPLICATION_OCTET_STREAM
);
3241 bool XMLHttpRequestMainThread::MozBackgroundRequest() const {
3242 return mFlagBackgroundRequest
;
3245 void XMLHttpRequestMainThread::SetMozBackgroundRequestExternal(
3246 bool aMozBackgroundRequest
, ErrorResult
& aRv
) {
3247 if (!IsSystemXHR()) {
3248 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
3252 if (mState
!= XMLHttpRequest_Binding::UNSENT
) {
3253 // Can't change this while we're in the middle of something.
3254 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3258 mFlagBackgroundRequest
= aMozBackgroundRequest
;
3261 void XMLHttpRequestMainThread::SetMozBackgroundRequest(
3262 bool aMozBackgroundRequest
, ErrorResult
& aRv
) {
3263 // No errors for this webIDL method on main-thread.
3264 SetMozBackgroundRequestExternal(aMozBackgroundRequest
, IgnoreErrors());
3267 void XMLHttpRequestMainThread::SetOriginStack(
3268 UniquePtr
<SerializedStackHolder
> aOriginStack
) {
3269 mOriginStack
= std::move(aOriginStack
);
3272 void XMLHttpRequestMainThread::SetSource(
3273 UniquePtr
<ProfileChunkedBuffer
> aSource
) {
3277 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
3280 httpChannel
->SetSource(std::move(aSource
));
3284 bool XMLHttpRequestMainThread::WithCredentials() const {
3285 return mFlagACwithCredentials
;
3288 void XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials
,
3290 NOT_CALLABLE_IN_SYNC_SEND_RV
3292 // Return error if we're already processing a request. Note that we can't use
3293 // ReadyState() here, because it can't differentiate between "opened" and
3294 // "sent", so we use mState directly.
3296 if ((mState
!= XMLHttpRequest_Binding::UNSENT
&&
3297 mState
!= XMLHttpRequest_Binding::OPENED
) ||
3298 mFlagSend
|| mIsAnon
) {
3299 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3303 mFlagACwithCredentials
= aWithCredentials
;
3306 nsresult
XMLHttpRequestMainThread::ChangeState(uint16_t aState
,
3309 nsresult rv
= NS_OK
;
3311 if (aState
!= XMLHttpRequest_Binding::HEADERS_RECEIVED
&&
3312 aState
!= XMLHttpRequest_Binding::LOADING
) {
3313 StopProgressEventTimer();
3317 (!mFlagSynchronous
|| aState
== XMLHttpRequest_Binding::OPENED
||
3318 aState
== XMLHttpRequest_Binding::DONE
)) {
3319 rv
= FireReadystatechangeEvent();
3325 /////////////////////////////////////////////////////
3326 // nsIChannelEventSink methods:
3329 XMLHttpRequestMainThread::AsyncOnChannelRedirect(
3330 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
3331 nsIAsyncVerifyRedirectCallback
* callback
) {
3332 MOZ_ASSERT(aNewChannel
, "Redirect without a channel?");
3334 // Prepare to receive callback
3335 mRedirectCallback
= callback
;
3336 mNewRedirectChannel
= aNewChannel
;
3338 if (mChannelEventSink
) {
3339 nsCOMPtr
<nsIAsyncVerifyRedirectCallback
> fwd
= EnsureXPCOMifier();
3341 nsresult rv
= mChannelEventSink
->AsyncOnChannelRedirect(
3342 aOldChannel
, aNewChannel
, aFlags
, fwd
);
3343 if (NS_FAILED(rv
)) {
3344 mRedirectCallback
= nullptr;
3345 mNewRedirectChannel
= nullptr;
3349 OnRedirectVerifyCallback(NS_OK
);
3353 nsresult
XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result
) {
3354 NS_ASSERTION(mRedirectCallback
, "mRedirectCallback not set in callback");
3355 NS_ASSERTION(mNewRedirectChannel
, "mNewRedirectChannel not set in callback");
3357 if (NS_SUCCEEDED(result
)) {
3358 bool rewriteToGET
= false;
3359 nsCOMPtr
<nsIHttpChannel
> oldHttpChannel
= GetCurrentHttpChannel();
3361 Unused
<< oldHttpChannel
->ShouldStripRequestBodyHeader(mRequestMethod
,
3364 mChannel
= mNewRedirectChannel
;
3366 nsCOMPtr
<nsIHttpChannel
> newHttpChannel(do_QueryInterface(mChannel
));
3367 if (newHttpChannel
) {
3368 // Ensure all original headers are duplicated for the new channel (bug
3370 mAuthorRequestHeaders
.ApplyToChannel(newHttpChannel
, rewriteToGET
);
3373 mErrorLoad
= ErrorType::eRedirect
;
3376 mNewRedirectChannel
= nullptr;
3378 mRedirectCallback
->OnRedirectVerifyCallback(result
);
3379 mRedirectCallback
= nullptr;
3381 // It's important that we return success here. If we return the result code
3382 // that we were passed, JavaScript callers who cancel the redirect will wind
3383 // up throwing an exception in the process.
3387 /////////////////////////////////////////////////////
3388 // nsIProgressEventSink methods:
3392 XMLHttpRequestMainThread::OnProgress(nsIRequest
* aRequest
, int64_t aProgress
,
3393 int64_t aProgressMax
) {
3394 // When uploading, OnProgress reports also headers in aProgress and
3395 // aProgressMax. So, try to remove the headers, if possible.
3396 bool lengthComputable
= (aProgressMax
!= -1);
3397 if (InUploadPhase()) {
3398 int64_t loaded
= aProgress
;
3399 if (lengthComputable
) {
3400 int64_t headerSize
= aProgressMax
- mUploadTotal
;
3401 loaded
-= headerSize
;
3403 mUploadTransferred
= loaded
;
3404 mProgressSinceLastProgressEvent
= true;
3406 if (!mFlagSynchronous
&& !mProgressTimerIsActive
) {
3407 StartProgressEventTimer();
3410 mLoadTotal
= aProgressMax
;
3411 mLoadTransferred
= aProgress
;
3412 // OnDataAvailable() handles mProgressSinceLastProgressEvent
3413 // for the download phase.
3416 if (mProgressEventSink
) {
3417 mProgressEventSink
->OnProgress(aRequest
, aProgress
, aProgressMax
);
3424 XMLHttpRequestMainThread::OnStatus(nsIRequest
* aRequest
, nsresult aStatus
,
3425 const char16_t
* aStatusArg
) {
3426 if (mProgressEventSink
) {
3427 mProgressEventSink
->OnStatus(aRequest
, aStatus
, aStatusArg
);
3433 bool XMLHttpRequestMainThread::AllowUploadProgress() {
3434 return !IsCrossSiteCORSRequest() || mFlagHadUploadListenersOnSend
;
3437 /////////////////////////////////////////////////////
3438 // nsIInterfaceRequestor methods:
3441 XMLHttpRequestMainThread::GetInterface(const nsIID
& aIID
, void** aResult
) {
3444 // Make sure to return ourselves for the channel event sink interface and
3445 // progress event sink interface, no matter what. We can forward these to
3446 // mNotificationCallbacks if it wants to get notifications for them. But we
3447 // need to see these notifications for proper functioning.
3448 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
3449 mChannelEventSink
= do_GetInterface(mNotificationCallbacks
);
3450 *aResult
= static_cast<nsIChannelEventSink
*>(EnsureXPCOMifier().take());
3452 } else if (aIID
.Equals(NS_GET_IID(nsIProgressEventSink
))) {
3453 mProgressEventSink
= do_GetInterface(mNotificationCallbacks
);
3454 *aResult
= static_cast<nsIProgressEventSink
*>(EnsureXPCOMifier().take());
3458 // Now give mNotificationCallbacks (if non-null) a chance to return the
3459 // desired interface.
3460 if (mNotificationCallbacks
) {
3461 rv
= mNotificationCallbacks
->GetInterface(aIID
, aResult
);
3462 if (NS_SUCCEEDED(rv
)) {
3463 NS_ASSERTION(*aResult
, "Lying nsIInterfaceRequestor implementation!");
3468 if (!mFlagBackgroundRequest
&& (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
3469 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
)))) {
3470 nsCOMPtr
<nsIPromptFactory
> wwatch
=
3471 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
3472 NS_ENSURE_SUCCESS(rv
, rv
);
3474 // Get the an auth prompter for our window so that the parenting
3475 // of the dialogs works as it should when using tabs.
3476 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
3478 window
= GetOwner()->GetOuterWindow();
3480 return wwatch
->GetPrompt(window
, aIID
, reinterpret_cast<void**>(aResult
));
3483 // Now check for the various XHR non-DOM interfaces, except
3484 // nsIProgressEventSink and nsIChannelEventSink which we already
3486 if (aIID
.Equals(NS_GET_IID(nsIStreamListener
))) {
3487 *aResult
= static_cast<nsIStreamListener
*>(EnsureXPCOMifier().take());
3490 if (aIID
.Equals(NS_GET_IID(nsIRequestObserver
))) {
3491 *aResult
= static_cast<nsIRequestObserver
*>(EnsureXPCOMifier().take());
3494 if (aIID
.Equals(NS_GET_IID(nsITimerCallback
))) {
3495 *aResult
= static_cast<nsITimerCallback
*>(EnsureXPCOMifier().take());
3499 return QueryInterface(aIID
, aResult
);
3502 void XMLHttpRequestMainThread::GetInterface(
3503 JSContext
* aCx
, JS::Handle
<JS::Value
> aIID
,
3504 JS::MutableHandle
<JS::Value
> aRetval
, ErrorResult
& aRv
) {
3505 dom::GetInterface(aCx
, this, aIID
, aRetval
, aRv
);
3508 XMLHttpRequestUpload
* XMLHttpRequestMainThread::GetUpload(ErrorResult
& aRv
) {
3510 mUpload
= new XMLHttpRequestUpload(this);
3515 bool XMLHttpRequestMainThread::MozAnon() const { return mIsAnon
; }
3517 bool XMLHttpRequestMainThread::MozSystem() const { return IsSystemXHR(); }
3519 void XMLHttpRequestMainThread::HandleTimeoutCallback() {
3520 if (mState
== XMLHttpRequest_Binding::DONE
) {
3521 MOZ_ASSERT_UNREACHABLE(
3522 "XMLHttpRequestMainThread::HandleTimeoutCallback "
3523 "with completed request");
3528 mFlagTimedOut
= true;
3529 CloseRequestWithError(ProgressEventType::timeout
);
3533 XMLHttpRequestMainThread::Notify(nsITimer
* aTimer
) {
3534 if (mProgressNotifier
== aTimer
) {
3535 HandleProgressTimerCallback();
3539 if (mTimeoutTimer
== aTimer
) {
3540 HandleTimeoutCallback();
3544 if (mSyncTimeoutTimer
== aTimer
) {
3545 HandleSyncTimeoutTimer();
3549 // Just in case some JS user wants to QI to nsITimerCallback and play with
3551 NS_WARNING("Unexpected timer!");
3552 return NS_ERROR_INVALID_POINTER
;
3555 void XMLHttpRequestMainThread::HandleProgressTimerCallback() {
3556 // Don't fire the progress event if mLoadTotal is 0, see XHR spec step 6.1
3557 if (!mLoadTotal
&& mLoadTransferred
) {
3561 mProgressTimerIsActive
= false;
3563 if (!mProgressSinceLastProgressEvent
|| mErrorLoad
!= ErrorType::eOK
) {
3567 if (InUploadPhase()) {
3568 if (mUpload
&& !mUploadComplete
&& mFlagHadUploadListenersOnSend
) {
3569 DispatchProgressEvent(mUpload
, ProgressEventType::progress
,
3570 mUploadTransferred
, mUploadTotal
);
3573 FireReadystatechangeEvent();
3574 DispatchProgressEvent(this, ProgressEventType::progress
, mLoadTransferred
,
3578 mProgressSinceLastProgressEvent
= false;
3580 StartProgressEventTimer();
3583 void XMLHttpRequestMainThread::StopProgressEventTimer() {
3584 if (mProgressNotifier
) {
3585 mProgressTimerIsActive
= false;
3586 mProgressNotifier
->Cancel();
3590 void XMLHttpRequestMainThread::StartProgressEventTimer() {
3591 if (!mProgressNotifier
) {
3592 mProgressNotifier
= NS_NewTimer(GetTimerEventTarget());
3594 if (mProgressNotifier
) {
3595 mProgressTimerIsActive
= true;
3596 mProgressNotifier
->Cancel();
3597 mProgressNotifier
->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL
,
3598 nsITimer::TYPE_ONE_SHOT
);
3602 XMLHttpRequestMainThread::SyncTimeoutType
3603 XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer() {
3604 MOZ_ASSERT(mFlagSynchronous
);
3606 Document
* doc
= GetDocumentIfCurrent();
3607 if (!doc
|| !doc
->GetPageUnloadingEventTimeStamp()) {
3608 return eNoTimerNeeded
;
3611 // If we are in a beforeunload or a unload event, we must force a timeout.
3613 (TimeStamp::NowLoRes() - doc
->GetPageUnloadingEventTimeStamp());
3614 if (diff
.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING
) {
3615 return eErrorOrExpired
;
3618 mSyncTimeoutTimer
= NS_NewTimer(GetTimerEventTarget());
3619 if (!mSyncTimeoutTimer
) {
3620 return eErrorOrExpired
;
3623 uint32_t timeout
= MAX_SYNC_TIMEOUT_WHEN_UNLOADING
- diff
.ToMilliseconds();
3624 nsresult rv
= mSyncTimeoutTimer
->InitWithCallback(this, timeout
,
3625 nsITimer::TYPE_ONE_SHOT
);
3626 return NS_FAILED(rv
) ? eErrorOrExpired
: eTimerStarted
;
3629 void XMLHttpRequestMainThread::HandleSyncTimeoutTimer() {
3630 MOZ_ASSERT(mSyncTimeoutTimer
);
3631 MOZ_ASSERT(mFlagSyncLooping
);
3633 CancelSyncTimeoutTimer();
3637 void XMLHttpRequestMainThread::CancelSyncTimeoutTimer() {
3638 if (mSyncTimeoutTimer
) {
3639 mSyncTimeoutTimer
->Cancel();
3640 mSyncTimeoutTimer
= nullptr;
3644 already_AddRefed
<nsXMLHttpRequestXPCOMifier
>
3645 XMLHttpRequestMainThread::EnsureXPCOMifier() {
3647 mXPCOMifier
= new nsXMLHttpRequestXPCOMifier(this);
3649 RefPtr
<nsXMLHttpRequestXPCOMifier
> newRef(mXPCOMifier
);
3650 return newRef
.forget();
3653 bool XMLHttpRequestMainThread::ShouldBlockAuthPrompt() {
3654 // Verify that it's ok to prompt for credentials here, per spec
3655 // http://xhr.spec.whatwg.org/#the-send%28%29-method
3657 if (mAuthorRequestHeaders
.Has("authorization")) {
3661 nsCOMPtr
<nsIURI
> uri
;
3662 nsresult rv
= mChannel
->GetURI(getter_AddRefs(uri
));
3663 if (NS_WARN_IF(NS_FAILED(rv
))) {
3667 // Also skip if a username and/or password is provided in the URI.
3669 rv
= uri
->GetUsername(username
);
3670 if (NS_WARN_IF(NS_FAILED(rv
))) {
3675 rv
= uri
->GetPassword(password
);
3676 if (NS_WARN_IF(NS_FAILED(rv
))) {
3680 if (!username
.IsEmpty() || !password
.IsEmpty()) {
3687 void XMLHttpRequestMainThread::TruncateResponseText() {
3688 mResponseText
.Truncate();
3689 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
3692 NS_IMPL_ISUPPORTS(XMLHttpRequestMainThread::nsHeaderVisitor
,
3693 nsIHttpHeaderVisitor
)
3695 NS_IMETHODIMP
XMLHttpRequestMainThread::nsHeaderVisitor::VisitHeader(
3696 const nsACString
& header
, const nsACString
& value
) {
3697 if (mXHR
.IsSafeHeader(header
, mHttpChannel
)) {
3698 nsAutoCString
lowerHeader(header
);
3699 ToLowerCase(lowerHeader
);
3700 if (!mHeaderList
.InsertElementSorted(HeaderEntry(lowerHeader
, value
),
3702 return NS_ERROR_OUT_OF_MEMORY
;
3708 XMLHttpRequestMainThread::nsHeaderVisitor::nsHeaderVisitor(
3709 const XMLHttpRequestMainThread
& aXMLHttpRequest
,
3710 NotNull
<nsIHttpChannel
*> aHttpChannel
)
3711 : mXHR(aXMLHttpRequest
), mHttpChannel(aHttpChannel
) {}
3713 XMLHttpRequestMainThread::nsHeaderVisitor::~nsHeaderVisitor() = default;
3715 void XMLHttpRequestMainThread::MaybeCreateBlobStorage() {
3716 MOZ_ASSERT(mResponseType
== XMLHttpRequestResponseType::Blob
);
3722 MutableBlobStorage::MutableBlobStorageType storageType
=
3723 BasePrincipal::Cast(mPrincipal
)->PrivateBrowsingId() == 0
3724 ? MutableBlobStorage::eCouldBeInTemporaryFile
3725 : MutableBlobStorage::eOnlyInMemory
;
3727 nsCOMPtr
<nsIEventTarget
> eventTarget
;
3728 if (nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal()) {
3729 eventTarget
= global
->EventTargetFor(TaskCategory::Other
);
3732 mBlobStorage
= new MutableBlobStorage(storageType
, eventTarget
);
3735 void XMLHttpRequestMainThread::BlobStoreCompleted(
3736 MutableBlobStorage
* aBlobStorage
, BlobImpl
* aBlobImpl
, nsresult aRv
) {
3737 // Ok, the state is changed...
3738 if (mBlobStorage
!= aBlobStorage
|| NS_FAILED(aRv
)) {
3742 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::DONE
);
3744 mResponseBlobImpl
= aBlobImpl
;
3745 mBlobStorage
= nullptr;
3747 ChangeStateToDone(mFlagSyncLooping
);
3751 XMLHttpRequestMainThread::GetName(nsACString
& aName
) {
3752 aName
.AssignLiteral("XMLHttpRequest");
3756 // nsXMLHttpRequestXPCOMifier implementation
3757 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier
)
3758 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
3759 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
3760 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
3761 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback
)
3762 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
3763 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
3764 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
3765 NS_INTERFACE_MAP_ENTRY(nsINamed
)
3766 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIStreamListener
)
3767 NS_INTERFACE_MAP_END
3769 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier
)
3770 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier
)
3772 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
3773 // inheritance from nsISupports.
3774 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier
)
3776 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier
)
3778 tmp
->mXHR
->mXPCOMifier
= nullptr;
3780 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR
)
3781 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3783 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier
)
3784 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR
)
3785 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3788 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID
& aIID
, void** aResult
) {
3789 // Return ourselves for the things we implement (except
3790 // nsIInterfaceRequestor) and the XHR for the rest.
3791 if (!aIID
.Equals(NS_GET_IID(nsIInterfaceRequestor
))) {
3792 nsresult rv
= QueryInterface(aIID
, aResult
);
3793 if (NS_SUCCEEDED(rv
)) {
3798 return mXHR
->GetInterface(aIID
, aResult
);
3801 ArrayBufferBuilder::ArrayBufferBuilder()
3802 : mMutex("ArrayBufferBuilder"),
3809 ArrayBufferBuilder::~ArrayBufferBuilder() {
3811 JS_free(nullptr, mDataPtr
);
3815 JS::ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
3820 mCapacity
= mLength
= 0;
3823 bool ArrayBufferBuilder::SetCapacity(uint32_t aNewCap
) {
3824 MutexAutoLock
lock(mMutex
);
3825 return SetCapacityInternal(aNewCap
, lock
);
3828 bool ArrayBufferBuilder::SetCapacityInternal(
3829 uint32_t aNewCap
, const MutexAutoLock
& aProofOfLock
) {
3830 MOZ_ASSERT(!mMapPtr
);
3831 MOZ_ASSERT(!mNeutered
);
3833 // To ensure that realloc won't free mDataPtr, use a size of 1
3835 uint8_t* newdata
= (uint8_t*)js_realloc(mDataPtr
, aNewCap
? aNewCap
: 1);
3841 if (aNewCap
> mCapacity
) {
3842 memset(newdata
+ mCapacity
, 0, aNewCap
- mCapacity
);
3846 mCapacity
= aNewCap
;
3847 if (mLength
> aNewCap
) {
3854 bool ArrayBufferBuilder::Append(const uint8_t* aNewData
, uint32_t aDataLen
,
3855 uint32_t aMaxGrowth
) {
3856 MutexAutoLock
lock(mMutex
);
3857 MOZ_ASSERT(!mMapPtr
);
3858 MOZ_ASSERT(!mNeutered
);
3860 CheckedUint32 neededCapacity
= mLength
;
3861 neededCapacity
+= aDataLen
;
3862 if (!neededCapacity
.isValid()) {
3865 if (mLength
+ aDataLen
> mCapacity
) {
3866 CheckedUint32 newcap
= mCapacity
;
3867 // Double while under aMaxGrowth or if not specified.
3868 if (!aMaxGrowth
|| mCapacity
< aMaxGrowth
) {
3871 newcap
+= aMaxGrowth
;
3874 if (!newcap
.isValid()) {
3878 // But make sure there's always enough to satisfy our request.
3879 if (newcap
.value() < neededCapacity
.value()) {
3880 newcap
= neededCapacity
;
3883 if (!SetCapacityInternal(newcap
.value(), lock
)) {
3888 // Assert that the region isn't overlapping so we can memcpy.
3890 !AreOverlappingRegions(aNewData
, aDataLen
, mDataPtr
+ mLength
, aDataLen
));
3892 memcpy(mDataPtr
+ mLength
, aNewData
, aDataLen
);
3893 mLength
+= aDataLen
;
3898 uint32_t ArrayBufferBuilder::Length() {
3899 MutexAutoLock
lock(mMutex
);
3900 MOZ_ASSERT(!mNeutered
);
3904 uint32_t ArrayBufferBuilder::Capacity() {
3905 MutexAutoLock
lock(mMutex
);
3906 MOZ_ASSERT(!mNeutered
);
3910 JSObject
* ArrayBufferBuilder::TakeArrayBuffer(JSContext
* aCx
) {
3911 MutexAutoLock
lock(mMutex
);
3912 MOZ_DIAGNOSTIC_ASSERT(!mNeutered
);
3915 JSObject
* obj
= JS::NewMappedArrayBufferWithContents(aCx
, mLength
, mMapPtr
);
3917 JS::ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
3923 // The memory-mapped contents will be released when the ArrayBuffer becomes
3924 // detached or is GC'd.
3928 // we need to check for mLength == 0, because nothing may have been
3930 if (mCapacity
> mLength
|| mLength
== 0) {
3931 if (!SetCapacityInternal(mLength
, lock
)) {
3936 JSObject
* obj
= JS::NewArrayBufferWithContents(aCx
, mLength
, mDataPtr
);
3942 mCapacity
= mLength
= 0;
3948 nsresult
ArrayBufferBuilder::MapToFileInPackage(const nsCString
& aFile
,
3949 nsIFile
* aJarFile
) {
3950 MutexAutoLock
lock(mMutex
);
3951 MOZ_ASSERT(NS_IsMainThread());
3952 MOZ_ASSERT(!mNeutered
);
3956 // Open Jar file to get related attributes of target file.
3957 RefPtr
<nsZipArchive
> zip
= new nsZipArchive();
3958 rv
= zip
->OpenArchive(aJarFile
);
3959 if (NS_FAILED(rv
)) {
3962 nsZipItem
* zipItem
= zip
->GetItem(aFile
.get());
3964 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
;
3967 // If file was added to the package as stored(uncompressed), map to the
3968 // offset of file in zip package.
3969 if (!zipItem
->Compression()) {
3970 uint32_t offset
= zip
->GetDataOffset(zipItem
);
3971 uint32_t size
= zipItem
->RealSize();
3972 mozilla::AutoFDClose pr_fd
;
3973 rv
= aJarFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, &pr_fd
.rwget());
3974 if (NS_FAILED(rv
)) {
3977 mMapPtr
= JS::CreateMappedArrayBufferContents(
3978 PR_FileDesc2NativeHandle(pr_fd
), offset
, size
);
3984 return NS_ERROR_FAILURE
;
3988 bool ArrayBufferBuilder::AreOverlappingRegions(const uint8_t* aStart1
,
3990 const uint8_t* aStart2
,
3991 uint32_t aLength2
) {
3992 const uint8_t* end1
= aStart1
+ aLength1
;
3993 const uint8_t* end2
= aStart2
+ aLength2
;
3995 const uint8_t* max_start
= aStart1
> aStart2
? aStart1
: aStart2
;
3996 const uint8_t* min_end
= end1
< end2
? end1
: end2
;
3998 return max_start
< min_end
;
4001 RequestHeaders::RequestHeader
* RequestHeaders::Find(const nsACString
& aName
) {
4002 for (RequestHeaders::RequestHeader
& header
: mHeaders
) {
4003 if (header
.mName
.Equals(aName
, nsCaseInsensitiveCStringComparator
)) {
4010 bool RequestHeaders::IsEmpty() const { return mHeaders
.IsEmpty(); }
4012 bool RequestHeaders::Has(const char* aName
) {
4013 return Has(nsDependentCString(aName
));
4016 bool RequestHeaders::Has(const nsACString
& aName
) { return !!Find(aName
); }
4018 void RequestHeaders::Get(const char* aName
, nsACString
& aValue
) {
4019 Get(nsDependentCString(aName
), aValue
);
4022 void RequestHeaders::Get(const nsACString
& aName
, nsACString
& aValue
) {
4023 RequestHeader
* header
= Find(aName
);
4025 aValue
= header
->mValue
;
4027 aValue
.SetIsVoid(true);
4031 void RequestHeaders::Set(const char* aName
, const nsACString
& aValue
) {
4032 Set(nsDependentCString(aName
), aValue
);
4035 void RequestHeaders::Set(const nsACString
& aName
, const nsACString
& aValue
) {
4036 RequestHeader
* header
= Find(aName
);
4038 header
->mValue
.Assign(aValue
);
4040 RequestHeader newHeader
= {nsCString(aName
), nsCString(aValue
)};
4041 mHeaders
.AppendElement(newHeader
);
4045 void RequestHeaders::MergeOrSet(const char* aName
, const nsACString
& aValue
) {
4046 MergeOrSet(nsDependentCString(aName
), aValue
);
4049 void RequestHeaders::MergeOrSet(const nsACString
& aName
,
4050 const nsACString
& aValue
) {
4051 RequestHeader
* header
= Find(aName
);
4053 header
->mValue
.AppendLiteral(", ");
4054 header
->mValue
.Append(aValue
);
4056 RequestHeader newHeader
= {nsCString(aName
), nsCString(aValue
)};
4057 mHeaders
.AppendElement(newHeader
);
4061 void RequestHeaders::Clear() { mHeaders
.Clear(); }
4063 void RequestHeaders::ApplyToChannel(nsIHttpChannel
* aChannel
,
4064 bool aStripRequestBodyHeader
) const {
4065 for (const RequestHeader
& header
: mHeaders
) {
4066 if (aStripRequestBodyHeader
&&
4067 (header
.mName
.LowerCaseEqualsASCII("content-type") ||
4068 header
.mName
.LowerCaseEqualsASCII("content-encoding") ||
4069 header
.mName
.LowerCaseEqualsASCII("content-language") ||
4070 header
.mName
.LowerCaseEqualsASCII("content-location"))) {
4073 // Update referrerInfo to override referrer header in system privileged.
4074 if (header
.mName
.LowerCaseEqualsASCII("referer")) {
4075 DebugOnly
<nsresult
> rv
= aChannel
->SetNewReferrerInfo(
4076 header
.mValue
, nsIReferrerInfo::ReferrerPolicyIDL::UNSAFE_URL
, true);
4077 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4079 if (header
.mValue
.IsEmpty()) {
4080 DebugOnly
<nsresult
> rv
= aChannel
->SetEmptyRequestHeader(header
.mName
);
4081 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4083 DebugOnly
<nsresult
> rv
=
4084 aChannel
->SetRequestHeader(header
.mName
, header
.mValue
, false);
4085 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4090 void RequestHeaders::GetCORSUnsafeHeaders(nsTArray
<nsCString
>& aArray
) const {
4091 for (const RequestHeader
& header
: mHeaders
) {
4092 if (!nsContentUtils::IsCORSSafelistedRequestHeader(header
.mName
,
4094 aArray
.AppendElement(header
.mName
);
4099 RequestHeaders::CharsetIterator::CharsetIterator(nsACString
& aSource
)
4103 mCutoff(aSource
.Length()),
4106 bool RequestHeaders::CharsetIterator::Equals(
4107 const nsACString
& aOther
, const nsCStringComparator
& aCmp
) const {
4109 return Substring(mSource
, mCurPos
, mCurLen
).Equals(aOther
, aCmp
);
4115 void RequestHeaders::CharsetIterator::Replace(const nsACString
& aReplacement
) {
4117 mSource
.Replace(mCurPos
, mCurLen
, aReplacement
);
4118 mCurLen
= aReplacement
.Length();
4122 bool RequestHeaders::CharsetIterator::Next() {
4124 nsAutoCString charset
;
4126 // Look for another charset declaration in the string, limiting the
4127 // search to only the characters before the parts we've already searched
4128 // (before mCutoff), so that we don't find the same charset twice.
4129 NS_ExtractCharsetFromContentType(Substring(mSource
, 0, mCutoff
), charset
,
4130 &mValid
, &start
, &end
);
4136 // Everything after the = sign is the part of the charset we want.
4137 mCurPos
= mSource
.FindChar('=', start
) + 1;
4138 mCurLen
= end
- mCurPos
;
4140 // Special case: the extracted charset is quoted with single quotes.
4141 // For the purpose of preserving what was set we want to handle them
4142 // as delimiters (although they aren't really).
4143 if (charset
.Length() >= 2 && charset
.First() == '\'' &&
4144 charset
.Last() == '\'') {
4155 } // namespace mozilla