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/AutoSuppressEventHandlingAndSuspend.h"
18 #include "mozilla/dom/BlobBinding.h"
19 #include "mozilla/dom/BlobURLProtocolHandler.h"
20 #include "mozilla/dom/DocGroup.h"
21 #include "mozilla/dom/DOMString.h"
22 #include "mozilla/dom/File.h"
23 #include "mozilla/dom/FileBinding.h"
24 #include "mozilla/dom/FileCreatorHelper.h"
25 #include "mozilla/dom/FetchUtil.h"
26 #include "mozilla/dom/FormData.h"
27 #include "mozilla/dom/quota/QuotaCommon.h"
28 #include "mozilla/dom/MutableBlobStorage.h"
29 #include "mozilla/dom/XMLDocument.h"
30 #include "mozilla/dom/URLSearchParams.h"
31 #include "mozilla/dom/UserActivation.h"
32 #include "mozilla/dom/Promise.h"
33 #include "mozilla/dom/PromiseNativeHandler.h"
34 #include "mozilla/dom/ReferrerInfo.h"
35 #include "mozilla/dom/WorkerError.h"
36 #include "mozilla/Encoding.h"
37 #include "mozilla/EventDispatcher.h"
38 #include "mozilla/EventListenerManager.h"
39 #include "mozilla/HoldDropJSObjects.h"
40 #include "mozilla/LoadInfo.h"
41 #include "mozilla/LoadContext.h"
42 #include "mozilla/MemoryReporting.h"
43 #include "mozilla/net/ContentRange.h"
44 #include "mozilla/PreloaderBase.h"
45 #include "mozilla/ScopeExit.h"
46 #include "mozilla/SpinEventLoopUntil.h"
47 #include "mozilla/StaticPrefs_dom.h"
48 #include "mozilla/StaticPrefs_network.h"
49 #include "mozilla/StaticPrefs_privacy.h"
50 #include "mozilla/dom/ProgressEvent.h"
51 #include "nsDataChannel.h"
52 #include "nsIBaseChannel.h"
53 #include "nsIJARChannel.h"
54 #include "nsIJARURI.h"
55 #include "nsReadableUtils.h"
56 #include "nsSandboxFlags.h"
59 #include "nsIURIMutator.h"
60 #include "nsILoadGroup.h"
61 #include "nsNetUtil.h"
62 #include "nsStringStream.h"
63 #include "nsIAuthPrompt.h"
64 #include "nsIAuthPrompt2.h"
65 #include "nsIClassOfService.h"
66 #include "nsIHttpChannel.h"
67 #include "nsISupportsPriority.h"
68 #include "nsIInterfaceRequestorUtils.h"
69 #include "nsStreamUtils.h"
70 #include "nsThreadUtils.h"
71 #include "nsIUploadChannel.h"
72 #include "nsIUploadChannel2.h"
74 #include "nsIDOMEventListener.h"
75 #include "nsVariant.h"
76 #include "nsIScriptError.h"
77 #include "nsICachingChannel.h"
78 #include "nsICookieJarSettings.h"
79 #include "nsContentUtils.h"
80 #include "nsCycleCollectionParticipant.h"
82 #include "nsIPromptFactory.h"
83 #include "nsIWindowWatcher.h"
84 #include "nsIConsoleService.h"
85 #include "nsAsyncRedirectVerifyHelper.h"
86 #include "nsStringBuffer.h"
87 #include "nsIFileChannel.h"
88 #include "mozilla/Telemetry.h"
89 #include "js/ArrayBuffer.h" // JS::{Create,Release}MappedArrayBufferContents,New{,Mapped}ArrayBufferWithContents
90 #include "js/JSON.h" // JS_ParseJSON
91 #include "js/MemoryFunctions.h"
92 #include "js/RootingAPI.h" // JS::{{,Mutable}Handle,Rooted}
93 #include "js/Value.h" // JS::{,Undefined}Value
94 #include "jsapi.h" // JS_ClearPendingException
95 #include "GeckoProfiler.h"
96 #include "mozilla/dom/XMLHttpRequestBinding.h"
97 #include "mozilla/Attributes.h"
98 #include "MultipartBlobImpl.h"
99 #include "nsIPermissionManager.h"
100 #include "nsMimeTypes.h"
101 #include "nsIHttpChannelInternal.h"
102 #include "nsCharSeparatedTokenizer.h"
103 #include "nsStreamListenerWrapper.h"
104 #include "nsITimedChannel.h"
105 #include "nsWrapperCacheInlines.h"
106 #include "nsZipArchive.h"
107 #include "mozilla/Preferences.h"
108 #include "private/pprio.h"
109 #include "XMLHttpRequestUpload.h"
111 // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
112 // replaced by FileCreatorHelper#CreateFileW.
117 extern mozilla::LazyLogModule gXMLHttpRequestLog
;
119 using namespace mozilla::net
;
121 namespace mozilla::dom
{
123 using EventType
= XMLHttpRequest::EventType
;
124 using Events
= XMLHttpRequest::Events
;
126 // Maximum size that we'll grow an ArrayBuffer instead of doubling,
127 // once doubling reaches this threshold
128 const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH
= 32 * 1024 * 1024;
129 // start at 32k to avoid lots of doubling right at the start
130 const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE
= 32 * 1024;
131 // the maximum Content-Length that we'll preallocate. 1GB. Must fit
133 const int32_t XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE
=
134 1 * 1024 * 1024 * 1024LL;
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 /////////////////////////////////////////////
186 // In debug mode, annotate WorkerRefs with the name of the function being
187 // invoked for increased scrutability. Save the previous value on the stack.
189 struct DebugWorkerRefs
{
191 RefPtr
<ThreadSafeWorkerRef
> mTSWorkerRef
;
194 DebugWorkerRefs(XMLHttpRequestMainThread
& aXHR
, const std::string
& aStatus
)
195 : mMutex(aXHR
.mTSWorkerRefMutex
) {
196 MutexAutoLock
lock(mMutex
);
198 mTSWorkerRef
= aXHR
.mTSWorkerRef
;
204 MOZ_ASSERT(mTSWorkerRef
->Private());
206 nsCString
status(aStatus
.c_str());
207 mPrev
= GET_WORKERREF_DEBUG_STATUS(mTSWorkerRef
->Ref());
208 SET_WORKERREF_DEBUG_STATUS(mTSWorkerRef
->Ref(), status
);
212 MutexAutoLock
lock(mMutex
);
218 MOZ_ASSERT(mTSWorkerRef
->Private());
220 SET_WORKERREF_DEBUG_STATUS(mTSWorkerRef
->Ref(), mPrev
);
222 mTSWorkerRef
= nullptr;
227 # define STREAM_STRING(stuff) \
228 (((const std::ostringstream&)(std::ostringstream() << stuff)) \
231 # if 1 // Disabling because bug 1855699
232 # define DEBUG_WORKERREFS void()
233 # define DEBUG_WORKERREFS1(x) void()
236 # define DEBUG_WORKERREFS \
237 DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)(*this, __func__)
239 # define DEBUG_WORKERREFS1(x) \
240 DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)( \
241 *this, STREAM_STRING(__func__ << ": " << x)) // NOLINT
246 # define DEBUG_WORKERREFS void()
247 # define DEBUG_WORKERREFS1(x) void()
250 bool XMLHttpRequestMainThread::sDontWarnAboutSyncXHR
= false;
252 XMLHttpRequestMainThread::XMLHttpRequestMainThread(
253 nsIGlobalObject
* aGlobalObject
)
254 : XMLHttpRequest(aGlobalObject
),
256 mTSWorkerRefMutex("Debug WorkerRefs"),
258 mResponseBodyDecodedPos(0),
259 mResponseType(XMLHttpRequestResponseType::_empty
),
260 mState(XMLHttpRequest_Binding::UNSENT
),
261 mFlagSynchronous(false),
263 mFlagParseBody(false),
264 mFlagSyncLooping(false),
265 mFlagBackgroundRequest(false),
266 mFlagHadUploadListenersOnSend(false),
267 mFlagACwithCredentials(false),
268 mFlagTimedOut(false),
271 mUploadTransferred(0),
273 mUploadComplete(true),
274 mProgressSinceLastProgressEvent(false),
276 mTimeoutMilliseconds(0),
277 mErrorLoad(ErrorType::eOK
),
278 mErrorLoadDetail(NS_OK
),
279 mErrorParsingXML(false),
280 mWaitingForOnStopRequest(false),
281 mProgressTimerIsActive(false),
283 mWarnAboutSyncHtml(false),
288 mResultJSON(JS::UndefinedValue()),
289 mArrayBufferBuilder(new ArrayBufferBuilder()),
290 mResultArrayBuffer(nullptr),
291 mIsMappedArrayBuffer(false),
292 mXPCOMifier(nullptr),
293 mEventDispatchingSuspended(false),
295 mDelayedDoneNotifier(nullptr) {
297 mozilla::HoldJSObjects(this);
300 XMLHttpRequestMainThread::~XMLHttpRequestMainThread() {
303 !mDelayedDoneNotifier
,
304 "How can we have mDelayedDoneNotifier, which owns us, in destructor?");
308 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
309 mState
== XMLHttpRequest_Binding::LOADING
) {
313 if (mParseEndListener
) {
314 mParseEndListener
->SetIsStale();
315 mParseEndListener
= nullptr;
318 MOZ_ASSERT(!mFlagSyncLooping
, "we rather crash than hang");
319 mFlagSyncLooping
= false;
321 mozilla::DropJSObjects(this);
324 void XMLHttpRequestMainThread::Construct(
325 nsIPrincipal
* aPrincipal
, nsICookieJarSettings
* aCookieJarSettings
,
326 bool aForWorker
, nsIURI
* aBaseURI
/* = nullptr */,
327 nsILoadGroup
* aLoadGroup
/* = nullptr */,
328 PerformanceStorage
* aPerformanceStorage
/* = nullptr */,
329 nsICSPEventListener
* aCSPEventListener
/* = nullptr */) {
331 MOZ_ASSERT(aPrincipal
);
332 mPrincipal
= aPrincipal
;
334 mLoadGroup
= aLoadGroup
;
335 mCookieJarSettings
= aCookieJarSettings
;
336 mForWorker
= aForWorker
;
337 mPerformanceStorage
= aPerformanceStorage
;
338 mCSPEventListener
= aCSPEventListener
;
341 void XMLHttpRequestMainThread::InitParameters(bool aAnon
, bool aSystem
) {
343 if (!aAnon
&& !aSystem
) {
347 // Check for permissions.
348 // Chrome is always allowed access, so do the permission check only
349 // for non-chrome pages.
350 if (!IsSystemXHR() && aSystem
) {
351 nsIGlobalObject
* global
= GetOwnerGlobal();
352 if (NS_WARN_IF(!global
)) {
353 SetParameters(aAnon
, false);
357 nsIPrincipal
* principal
= global
->PrincipalOrNull();
358 if (NS_WARN_IF(!principal
)) {
359 SetParameters(aAnon
, false);
363 nsCOMPtr
<nsIPermissionManager
> permMgr
=
364 components::PermissionManager::Service();
365 if (NS_WARN_IF(!permMgr
)) {
366 SetParameters(aAnon
, false);
371 nsresult rv
= permMgr
->TestPermissionFromPrincipal(
372 principal
, "systemXHR"_ns
, &permission
);
373 if (NS_FAILED(rv
) || permission
!= nsIPermissionManager::ALLOW_ACTION
) {
374 SetParameters(aAnon
, false);
379 SetParameters(aAnon
, aSystem
);
382 void XMLHttpRequestMainThread::SetClientInfoAndController(
383 const ClientInfo
& aClientInfo
,
384 const Maybe
<ServiceWorkerDescriptor
>& aController
) {
385 mClientInfo
.emplace(aClientInfo
);
386 mController
= aController
;
389 void XMLHttpRequestMainThread::ResetResponse() {
390 mResponseXML
= nullptr;
391 mResponseBody
.Truncate();
392 TruncateResponseText();
393 mResponseBlobImpl
= nullptr;
394 mResponseBlob
= nullptr;
395 mBlobStorage
= nullptr;
396 mResultArrayBuffer
= nullptr;
397 mArrayBufferBuilder
= new ArrayBufferBuilder();
398 mResultJSON
.setUndefined();
399 mLoadTransferred
= 0;
400 mResponseBodyDecodedPos
= 0;
404 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestMainThread
)
406 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestMainThread
,
407 XMLHttpRequestEventTarget
)
408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext
)
409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel
)
410 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML
)
412 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener
)
414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob
)
415 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks
)
417 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink
)
418 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink
)
420 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload
)
421 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
423 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread
,
424 XMLHttpRequestEventTarget
)
425 tmp
->mResultArrayBuffer
= nullptr;
426 tmp
->mArrayBufferBuilder
= nullptr;
427 tmp
->mResultJSON
.setUndefined();
428 tmp
->mResponseBlobImpl
= nullptr;
430 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext
)
431 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel
)
432 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML
)
434 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener
)
436 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob
)
437 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks
)
439 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink
)
440 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink
)
442 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload
)
443 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
445 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestMainThread
,
446 XMLHttpRequestEventTarget
)
447 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer
)
448 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON
)
449 NS_IMPL_CYCLE_COLLECTION_TRACE_END
451 bool XMLHttpRequestMainThread::IsCertainlyAliveForCC() const {
452 return mWaitingForOnStopRequest
;
455 // QueryInterface implementation for XMLHttpRequestMainThread
456 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestMainThread
)
457 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
458 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
459 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
460 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
461 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
462 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
463 NS_INTERFACE_MAP_ENTRY(nsINamed
)
464 NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget
)
465 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget
)
467 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestMainThread
, XMLHttpRequestEventTarget
)
468 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestMainThread
, XMLHttpRequestEventTarget
)
470 void XMLHttpRequestMainThread::DisconnectFromOwner() {
471 XMLHttpRequestEventTarget::DisconnectFromOwner();
475 size_t XMLHttpRequestMainThread::SizeOfEventTargetIncludingThis(
476 MallocSizeOf aMallocSizeOf
) const {
477 size_t n
= aMallocSizeOf(this);
478 n
+= mResponseBody
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
480 // Why is this safe? Because no-one else will report this string. The
481 // other possible sharers of this string are as follows.
483 // - The JS engine could hold copies if the JS code holds references, e.g.
484 // |var text = XHR.responseText|. However, those references will be via JS
485 // external strings, for which the JS memory reporter does *not* report the
488 // - Binary extensions, but they're *extremely* unlikely to do any memory
491 n
+= mResponseText
.SizeOfThis(aMallocSizeOf
);
495 // Measurement of the following members may be added later if DMD finds it is
500 static void LogMessage(
501 const char* aWarning
, nsPIDOMWindowInner
* aWindow
,
502 const nsTArray
<nsString
>& aParams
= nsTArray
<nsString
>()) {
503 nsCOMPtr
<Document
> doc
;
505 doc
= aWindow
->GetExtantDoc();
507 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "DOM"_ns
, doc
,
508 nsContentUtils::eDOM_PROPERTIES
, aWarning
,
512 Document
* XMLHttpRequestMainThread::GetResponseXML(ErrorResult
& aRv
) {
513 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
514 mResponseType
!= XMLHttpRequestResponseType::Document
) {
515 aRv
.ThrowInvalidStateError(
516 "responseXML is only available if responseType is '' or 'document'.");
519 if (mWarnAboutSyncHtml
) {
520 mWarnAboutSyncHtml
= false;
521 LogMessage("HTMLSyncXHRWarning", GetOwner());
523 if (mState
!= XMLHttpRequest_Binding::DONE
) {
530 * This piece copied from XMLDocument, we try to get the charset
533 nsresult
XMLHttpRequestMainThread::DetectCharset() {
537 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
538 mResponseType
!= XMLHttpRequestResponseType::Text
&&
539 mResponseType
!= XMLHttpRequestResponseType::Json
) {
543 nsAutoCString charsetVal
;
544 const Encoding
* encoding
;
545 bool ok
= mChannel
&& NS_SUCCEEDED(mChannel
->GetContentCharset(charsetVal
)) &&
546 (encoding
= Encoding::ForLabel(charsetVal
));
548 // MS documentation states UTF-8 is default for responseText
549 encoding
= UTF_8_ENCODING
;
552 if (mResponseType
== XMLHttpRequestResponseType::Json
&&
553 encoding
!= UTF_8_ENCODING
) {
554 // The XHR spec says only UTF-8 is supported for responseType == "json"
555 LogMessage("JSONCharsetWarning", GetOwner());
556 encoding
= UTF_8_ENCODING
;
559 // Only sniff the BOM for non-JSON responseTypes
560 if (mResponseType
== XMLHttpRequestResponseType::Json
) {
561 mDecoder
= encoding
->NewDecoderWithBOMRemoval();
563 mDecoder
= encoding
->NewDecoder();
569 nsresult
XMLHttpRequestMainThread::AppendToResponseText(
570 Span
<const uint8_t> aBuffer
, bool aLast
) {
571 // Call this with an empty buffer to send the decoder the signal
572 // that we have hit the end of the stream.
574 NS_ENSURE_STATE(mDecoder
);
576 CheckedInt
<size_t> destBufferLen
=
577 mDecoder
->MaxUTF16BufferLength(aBuffer
.Length());
579 { // scope for holding the mutex that protects mResponseText
580 XMLHttpRequestStringWriterHelper
helper(mResponseText
);
582 uint32_t len
= helper
.Length();
584 destBufferLen
+= len
;
585 if (!destBufferLen
.isValid() || destBufferLen
.value() > UINT32_MAX
) {
586 return NS_ERROR_OUT_OF_MEMORY
;
589 auto handleOrErr
= helper
.BulkWrite(destBufferLen
.value());
590 if (handleOrErr
.isErr()) {
591 return handleOrErr
.unwrapErr();
594 auto handle
= handleOrErr
.unwrap();
599 std::tie(result
, read
, written
, std::ignore
) =
600 mDecoder
->DecodeToUTF16(aBuffer
, handle
.AsSpan().From(len
), aLast
);
601 MOZ_ASSERT(result
== kInputEmpty
);
602 MOZ_ASSERT(read
== aBuffer
.Length());
604 MOZ_ASSERT(len
<= destBufferLen
.value());
605 handle
.Finish(len
, false);
609 // Drop the finished decoder to avoid calling into a decoder
610 // that has finished.
617 void XMLHttpRequestMainThread::GetResponseText(DOMString
& aResponseText
,
619 MOZ_DIAGNOSTIC_ASSERT(!mForWorker
);
621 XMLHttpRequestStringSnapshot snapshot
;
622 GetResponseText(snapshot
, aRv
);
627 if (!snapshot
.GetAsString(aResponseText
)) {
628 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
633 void XMLHttpRequestMainThread::GetResponseText(
634 XMLHttpRequestStringSnapshot
& aSnapshot
, ErrorResult
& aRv
) {
637 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
638 mResponseType
!= XMLHttpRequestResponseType::Text
) {
639 aRv
.ThrowInvalidStateError(
640 "responseText is only available if responseType is '' or 'text'.");
644 if (mState
!= XMLHttpRequest_Binding::LOADING
&&
645 mState
!= XMLHttpRequest_Binding::DONE
) {
649 // Main Fetch step 18 requires to ignore body for head/connect methods.
650 if (mRequestMethod
.EqualsLiteral("HEAD") ||
651 mRequestMethod
.EqualsLiteral("CONNECT")) {
655 // We only decode text lazily if we're also parsing to a doc.
656 // Also, if we've decoded all current data already, then no need to decode
658 if ((!mResponseXML
&& !mErrorParsingXML
) ||
659 (mResponseBodyDecodedPos
== mResponseBody
.Length() &&
660 (mState
!= XMLHttpRequest_Binding::DONE
|| mEofDecoded
))) {
661 mResponseText
.CreateSnapshot(aSnapshot
);
665 MatchCharsetAndDecoderToResponseDocument();
667 MOZ_ASSERT(mResponseBodyDecodedPos
< mResponseBody
.Length() ||
668 mState
== XMLHttpRequest_Binding::DONE
,
669 "Unexpected mResponseBodyDecodedPos");
670 Span
<const uint8_t> span
= mResponseBody
;
671 aRv
= AppendToResponseText(span
.From(mResponseBodyDecodedPos
),
672 mState
== XMLHttpRequest_Binding::DONE
);
677 mResponseBodyDecodedPos
= mResponseBody
.Length();
680 // Free memory buffer which we no longer need
681 mResponseBody
.Truncate();
682 mResponseBodyDecodedPos
= 0;
685 mResponseText
.CreateSnapshot(aSnapshot
);
688 nsresult
XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext
* aCx
) {
690 return NS_ERROR_FAILURE
;
694 nsresult rv
= GetResponseTextForJSON(string
);
695 if (NS_WARN_IF(NS_FAILED(rv
))) {
699 // The Unicode converter has already zapped the BOM if there was one
700 JS::Rooted
<JS::Value
> value(aCx
);
701 if (!JS_ParseJSON(aCx
, string
.BeginReading(), string
.Length(), &value
)) {
702 return NS_ERROR_FAILURE
;
709 void XMLHttpRequestMainThread::SetResponseType(
710 XMLHttpRequestResponseType aResponseType
, ErrorResult
& aRv
) {
711 NOT_CALLABLE_IN_SYNC_SEND_RV
713 if (mState
== XMLHttpRequest_Binding::LOADING
||
714 mState
== XMLHttpRequest_Binding::DONE
) {
715 aRv
.ThrowInvalidStateError(
716 "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
717 "(when its state is LOADING or DONE).");
721 // sync request is not allowed setting responseType in window context
722 if (HasOrHasHadOwner() && mState
!= XMLHttpRequest_Binding::UNSENT
&&
724 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
725 aRv
.ThrowInvalidAccessError(
726 "synchronous XMLHttpRequests do not support timeout and responseType");
730 // Set the responseType attribute's value to the given value.
731 SetResponseTypeRaw(aResponseType
);
734 void XMLHttpRequestMainThread::GetResponse(
735 JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aResponse
, ErrorResult
& aRv
) {
736 MOZ_DIAGNOSTIC_ASSERT(!mForWorker
);
738 switch (mResponseType
) {
739 case XMLHttpRequestResponseType::_empty
:
740 case XMLHttpRequestResponseType::Text
: {
742 GetResponseText(str
, aRv
);
746 if (!xpc::StringToJsval(aCx
, str
, aResponse
)) {
747 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
752 case XMLHttpRequestResponseType::Arraybuffer
: {
753 if (mState
!= XMLHttpRequest_Binding::DONE
) {
758 if (!mResultArrayBuffer
) {
759 mResultArrayBuffer
= mArrayBufferBuilder
->TakeArrayBuffer(aCx
);
760 if (!mResultArrayBuffer
) {
761 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
765 aResponse
.setObject(*mResultArrayBuffer
);
768 case XMLHttpRequestResponseType::Blob
: {
769 if (mState
!= XMLHttpRequest_Binding::DONE
) {
774 if (!mResponseBlobImpl
) {
779 if (!mResponseBlob
) {
780 mResponseBlob
= Blob::Create(GetOwnerGlobal(), mResponseBlobImpl
);
783 if (!GetOrCreateDOMReflector(aCx
, mResponseBlob
, aResponse
)) {
789 case XMLHttpRequestResponseType::Document
: {
790 if (!mResponseXML
|| mState
!= XMLHttpRequest_Binding::DONE
) {
796 nsContentUtils::WrapNative(aCx
, ToSupports(mResponseXML
), aResponse
);
799 case XMLHttpRequestResponseType::Json
: {
800 if (mState
!= XMLHttpRequest_Binding::DONE
) {
805 if (mResultJSON
.isUndefined()) {
806 aRv
= CreateResponseParsedJSON(aCx
);
807 TruncateResponseText();
809 // Per spec, errors aren't propagated. null is returned instead.
811 // It would be nice to log the error to the console. That's hard to
812 // do without calling window.onerror as a side effect, though.
813 JS_ClearPendingException(aCx
);
814 mResultJSON
.setNull();
817 aResponse
.set(mResultJSON
);
821 NS_ERROR("Should not happen");
827 already_AddRefed
<BlobImpl
> XMLHttpRequestMainThread::GetResponseBlobImpl() {
828 MOZ_DIAGNOSTIC_ASSERT(mForWorker
);
829 MOZ_DIAGNOSTIC_ASSERT(mResponseType
== XMLHttpRequestResponseType::Blob
);
831 if (mState
!= XMLHttpRequest_Binding::DONE
) {
835 RefPtr
<BlobImpl
> blobImpl
= mResponseBlobImpl
;
836 return blobImpl
.forget();
839 already_AddRefed
<ArrayBufferBuilder
>
840 XMLHttpRequestMainThread::GetResponseArrayBufferBuilder() {
841 MOZ_DIAGNOSTIC_ASSERT(mForWorker
);
842 MOZ_DIAGNOSTIC_ASSERT(mResponseType
==
843 XMLHttpRequestResponseType::Arraybuffer
);
845 if (mState
!= XMLHttpRequest_Binding::DONE
) {
849 RefPtr
<ArrayBufferBuilder
> builder
= mArrayBufferBuilder
;
850 return builder
.forget();
853 nsresult
XMLHttpRequestMainThread::GetResponseTextForJSON(nsAString
& aString
) {
854 if (mState
!= XMLHttpRequest_Binding::DONE
) {
855 aString
.SetIsVoid(true);
859 if (!mResponseText
.GetAsString(aString
)) {
860 return NS_ERROR_OUT_OF_MEMORY
;
866 bool XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const {
871 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
872 return loadInfo
->GetTainting() == LoadTainting::CORS
;
875 bool XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest() {
876 if (IsCrossSiteCORSRequest()) {
878 mChannel
->GetStatus(&rv
);
886 bool XMLHttpRequestMainThread::BadContentRangeRequested() {
890 // Only nsIBaseChannel supports this
891 nsCOMPtr
<nsIBaseChannel
> baseChan
= do_QueryInterface(mChannel
);
895 // A bad range was requested if the channel has no content range
896 // despite the request specifying a range header.
897 return !baseChan
->ContentRange() && mAuthorRequestHeaders
.Has("range");
900 RefPtr
<mozilla::net::ContentRange
>
901 XMLHttpRequestMainThread::GetRequestedContentRange() const {
902 MOZ_ASSERT(mChannel
);
903 nsCOMPtr
<nsIBaseChannel
> baseChan
= do_QueryInterface(mChannel
);
907 return baseChan
->ContentRange();
910 void XMLHttpRequestMainThread::GetContentRangeHeader(nsACString
& out
) const {
911 if (!IsBlobURI(mRequestURL
)) {
915 RefPtr
<mozilla::net::ContentRange
> range
= GetRequestedContentRange();
917 range
->AsHeader(out
);
923 void XMLHttpRequestMainThread::GetResponseURL(nsAString
& aUrl
) {
926 if ((mState
== XMLHttpRequest_Binding::UNSENT
||
927 mState
== XMLHttpRequest_Binding::OPENED
) ||
932 // Make sure we don't leak responseURL information from denied cross-site
934 if (IsDeniedCrossSiteCORSRequest()) {
938 nsCOMPtr
<nsIURI
> responseUrl
;
939 if (NS_FAILED(NS_GetFinalChannelURI(mChannel
, getter_AddRefs(responseUrl
)))) {
944 responseUrl
->GetSpecIgnoringRef(temp
);
945 CopyUTF8toUTF16(temp
, aUrl
);
948 uint32_t XMLHttpRequestMainThread::GetStatus(ErrorResult
& aRv
) {
949 // Make sure we don't leak status information from denied cross-site
951 if (IsDeniedCrossSiteCORSRequest()) {
955 if (mState
== XMLHttpRequest_Binding::UNSENT
||
956 mState
== XMLHttpRequest_Binding::OPENED
) {
960 if (mErrorLoad
!= ErrorType::eOK
) {
961 // Let's simulate the http protocol for jar/app requests:
962 nsCOMPtr
<nsIJARChannel
> jarChannel
= GetCurrentJARChannel();
965 mChannel
->GetStatus(&status
);
967 if (status
== NS_ERROR_FILE_NOT_FOUND
) {
968 return 404; // Not Found
970 return 500; // Internal Error
977 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
979 // Pretend like we got a 200/206 response, since our load was successful
980 return GetRequestedContentRange() ? 206 : 200;
984 nsresult rv
= httpChannel
->GetResponseStatus(&status
);
992 void XMLHttpRequestMainThread::GetStatusText(nsACString
& aStatusText
,
994 // Return an empty status text on all error loads.
995 aStatusText
.Truncate();
997 // Make sure we don't leak status information from denied cross-site
999 if (IsDeniedCrossSiteCORSRequest()) {
1003 // Check the current XHR state to see if it is valid to obtain the statusText
1004 // value. This check is to prevent the status text for redirects from being
1005 // available before all the redirects have been followed and HTTP headers have
1007 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1008 mState
== XMLHttpRequest_Binding::OPENED
) {
1012 if (mErrorLoad
!= ErrorType::eOK
) {
1016 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
1018 Unused
<< httpChannel
->GetResponseStatusText(aStatusText
);
1020 aStatusText
.AssignLiteral("OK");
1024 void XMLHttpRequestMainThread::TerminateOngoingFetch(nsresult detail
) {
1026 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
1027 mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
1028 mState
== XMLHttpRequest_Binding::LOADING
) {
1029 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Info
,
1030 ("%p TerminateOngoingFetch(0x%" PRIx32
")", this,
1031 static_cast<uint32_t>(detail
)));
1032 CloseRequest(detail
);
1036 void XMLHttpRequestMainThread::CloseRequest(nsresult detail
) {
1038 mWaitingForOnStopRequest
= false;
1039 mErrorLoad
= ErrorType::eTerminated
;
1040 mErrorLoadDetail
= detail
;
1042 mChannel
->CancelWithReason(NS_BINDING_ABORTED
,
1043 "XMLHttpRequestMainThread::CloseRequest"_ns
);
1045 CancelTimeoutTimer();
1048 void XMLHttpRequestMainThread::CloseRequestWithError(
1049 const ErrorProgressEventType
& aType
) {
1051 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1052 ("%p CloseRequestWithError(%s)", this, aType
.cStr
));
1054 CloseRequest(aType
.errorCode
);
1058 // If we're in the destructor, don't risk dispatching an event.
1060 mFlagSyncLooping
= false;
1064 if (mState
!= XMLHttpRequest_Binding::UNSENT
&&
1065 !(mState
== XMLHttpRequest_Binding::OPENED
&& !mFlagSend
) &&
1066 mState
!= XMLHttpRequest_Binding::DONE
) {
1067 ChangeState(XMLHttpRequest_Binding::DONE
, true);
1069 if (!mFlagSyncLooping
) {
1070 if (mUpload
&& !mUploadComplete
) {
1071 mUploadComplete
= true;
1072 DispatchProgressEvent(mUpload
, aType
, 0, -1);
1074 DispatchProgressEvent(this, aType
, 0, -1);
1078 // The ChangeState call above calls onreadystatechange handlers which
1079 // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
1080 // the abort state bit. If this occurs we're not uninitialized (bug 361773).
1082 ChangeState(XMLHttpRequest_Binding::UNSENT
, false); // IE seems to do it
1085 mFlagSyncLooping
= false;
1088 void XMLHttpRequestMainThread::RequestErrorSteps(
1089 const ProgressEventType aEventType
, const nsresult aOptionalException
,
1091 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1092 ("%p RequestErrorSteps(%s,0x%" PRIx32
")", this, aEventType
.cStr
,
1093 static_cast<uint32_t>(aOptionalException
)));
1095 // Cancel our timers first before setting our state to done, so we don't
1096 // trip any assertions if one fires and asserts that state != done.
1097 CancelTimeoutTimer();
1098 CancelSyncTimeoutTimer();
1099 StopProgressEventTimer();
1102 mState
= XMLHttpRequest_Binding::DONE
;
1110 // If we're in the destructor, don't risk dispatching an event.
1112 mFlagSyncLooping
= false;
1117 if (mFlagSynchronous
&& NS_FAILED(aOptionalException
)) {
1118 aRv
.Throw(aOptionalException
);
1123 FireReadystatechangeEvent();
1126 if (mUpload
&& !mUploadComplete
) {
1128 mUploadComplete
= true;
1131 if (mFlagHadUploadListenersOnSend
) {
1132 // Steps 6-3, 6-4 (loadend is fired for us)
1133 DispatchProgressEvent(mUpload
, aEventType
, 0, -1);
1137 // Steps 7 and 8 (loadend is fired for us)
1138 DispatchProgressEvent(this, aEventType
, 0, -1);
1141 void XMLHttpRequestMainThread::Abort(ErrorResult
& aRv
) {
1142 NOT_CALLABLE_IN_SYNC_SEND_RV
1143 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
, ("%p Abort()", this));
1147 void XMLHttpRequestMainThread::AbortInternal(ErrorResult
& aRv
) {
1148 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
, ("%p AbortInternal()", this));
1149 mFlagAborted
= true;
1150 DisconnectDoneNotifier();
1153 TerminateOngoingFetch(NS_ERROR_DOM_ABORT_ERR
);
1156 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
1157 mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
1158 mState
== XMLHttpRequest_Binding::LOADING
) {
1159 RequestErrorSteps(Events::abort
, NS_ERROR_DOM_ABORT_ERR
, aRv
);
1163 if (mState
== XMLHttpRequest_Binding::DONE
) {
1164 ChangeState(XMLHttpRequest_Binding::UNSENT
,
1165 false); // no ReadystateChange event
1168 mFlagSyncLooping
= false;
1171 /*Method that checks if it is safe to expose a header value to the client.
1172 It is used to check what headers are exposed for CORS requests.*/
1173 bool XMLHttpRequestMainThread::IsSafeHeader(
1174 const nsACString
& aHeader
, NotNull
<nsIHttpChannel
*> aHttpChannel
) const {
1175 // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
1176 if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(aHeader
)) {
1177 NS_WARNING("blocked access to response header");
1180 // if this is not a CORS call all headers are safe
1181 if (!IsCrossSiteCORSRequest()) {
1184 // Check for dangerous headers
1185 // Make sure we don't leak header information from denied cross-site
1189 mChannel
->GetStatus(&status
);
1190 if (NS_FAILED(status
)) {
1194 const char* kCrossOriginSafeHeaders
[] = {
1195 "cache-control", "content-language", "content-type", "content-length",
1196 "expires", "last-modified", "pragma"};
1197 for (uint32_t i
= 0; i
< ArrayLength(kCrossOriginSafeHeaders
); ++i
) {
1198 if (aHeader
.LowerCaseEqualsASCII(kCrossOriginSafeHeaders
[i
])) {
1202 nsAutoCString headerVal
;
1203 // The "Access-Control-Expose-Headers" header contains a comma separated
1204 // list of method names.
1205 Unused
<< aHttpChannel
->GetResponseHeader("Access-Control-Expose-Headers"_ns
,
1207 bool isSafe
= false;
1208 for (const nsACString
& token
:
1209 nsCCharSeparatedTokenizer(headerVal
, ',').ToRange()) {
1210 if (token
.IsEmpty()) {
1213 if (!NS_IsValidHTTPToken(token
)) {
1217 if (token
.EqualsLiteral("*") && !mFlagACwithCredentials
) {
1219 } else if (aHeader
.Equals(token
, nsCaseInsensitiveCStringComparator
)) {
1227 bool XMLHttpRequestMainThread::GetContentType(nsACString
& aValue
) const {
1228 MOZ_ASSERT(mChannel
);
1229 nsCOMPtr
<nsIBaseChannel
> baseChan
= do_QueryInterface(mChannel
);
1231 RefPtr
<CMimeType
> fullMimeType(baseChan
->FullMimeType());
1233 fullMimeType
->Serialize(aValue
);
1237 if (NS_SUCCEEDED(mChannel
->GetContentType(aValue
))) {
1239 if (NS_SUCCEEDED(mChannel
->GetContentCharset(value
)) && !value
.IsEmpty()) {
1240 aValue
.AppendLiteral(";charset=");
1241 aValue
.Append(value
);
1247 void XMLHttpRequestMainThread::GetAllResponseHeaders(
1248 nsACString
& aResponseHeaders
, ErrorResult
& aRv
) {
1249 NOT_CALLABLE_IN_SYNC_SEND_RV
1251 aResponseHeaders
.Truncate();
1253 // If the state is UNSENT or OPENED,
1254 // return the empty string and terminate these steps.
1255 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1256 mState
== XMLHttpRequest_Binding::OPENED
) {
1260 if (mErrorLoad
!= ErrorType::eOK
) {
1264 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel()) {
1265 RefPtr
<nsHeaderVisitor
> visitor
=
1266 new nsHeaderVisitor(*this, WrapNotNull(httpChannel
));
1267 if (NS_SUCCEEDED(httpChannel
->VisitResponseHeaders(visitor
))) {
1268 aResponseHeaders
= visitor
->Headers();
1277 // Even non-http channels supply content type.
1278 nsAutoCString value
;
1279 if (GetContentType(value
)) {
1280 aResponseHeaders
.AppendLiteral("Content-Type: ");
1281 aResponseHeaders
.Append(value
);
1282 aResponseHeaders
.AppendLiteral("\r\n");
1285 // Don't provide Content-Length for data URIs
1286 nsCOMPtr
<nsIURI
> uri
;
1287 if (NS_FAILED(mChannel
->GetURI(getter_AddRefs(uri
))) ||
1288 !uri
->SchemeIs("data")) {
1290 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1291 aResponseHeaders
.AppendLiteral("Content-Length: ");
1292 aResponseHeaders
.AppendInt(length
);
1293 aResponseHeaders
.AppendLiteral("\r\n");
1297 // Should set a Content-Range header for blob scheme.
1298 // From https://fetch.spec.whatwg.org/#scheme-fetch 3.blob.9.20:
1299 // "Set response’s header list to «(`Content-Length`, serializedSlicedLength),
1300 // (`Content-Type`, type), (`Content-Range`, contentRange)»."
1301 GetContentRangeHeader(value
);
1302 if (!value
.IsVoid()) {
1303 aResponseHeaders
.AppendLiteral("Content-Range: ");
1304 aResponseHeaders
.Append(value
);
1305 aResponseHeaders
.AppendLiteral("\r\n");
1309 void XMLHttpRequestMainThread::GetResponseHeader(const nsACString
& header
,
1310 nsACString
& _retval
,
1312 NOT_CALLABLE_IN_SYNC_SEND_RV
1314 _retval
.SetIsVoid(true);
1316 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
1319 // If the state is UNSENT or OPENED,
1320 // return null and terminate these steps.
1321 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1322 mState
== XMLHttpRequest_Binding::OPENED
) {
1326 // Even non-http channels supply content type and content length.
1327 // Remember we don't leak header information from denied cross-site
1328 // requests. However, we handle file: and blob: URLs for blob response
1329 // types by canceling them with a specific error, so we have to allow
1330 // them to pass through this check.
1332 if (!mChannel
|| NS_FAILED(mChannel
->GetStatus(&status
)) ||
1333 (NS_FAILED(status
) && status
!= NS_ERROR_FILE_ALREADY_EXISTS
)) {
1338 if (header
.LowerCaseEqualsASCII("content-type")) {
1339 if (!GetContentType(_retval
)) {
1340 // Means no content type
1341 _retval
.SetIsVoid(true);
1347 else if (header
.LowerCaseEqualsASCII("content-length")) {
1349 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1350 _retval
.AppendInt(length
);
1355 else if (header
.LowerCaseEqualsASCII("content-range")) {
1356 GetContentRangeHeader(_retval
);
1362 // Check for dangerous headers
1363 if (!IsSafeHeader(header
, WrapNotNull(httpChannel
))) {
1367 aRv
= httpChannel
->GetResponseHeader(header
, _retval
);
1368 if (aRv
.ErrorCodeIs(NS_ERROR_NOT_AVAILABLE
)) {
1370 _retval
.SetIsVoid(true);
1371 aRv
.SuppressException();
1375 already_AddRefed
<nsILoadGroup
> XMLHttpRequestMainThread::GetLoadGroup() const {
1376 if (mFlagBackgroundRequest
) {
1381 nsCOMPtr
<nsILoadGroup
> ref
= mLoadGroup
;
1382 return ref
.forget();
1385 Document
* doc
= GetDocumentIfCurrent();
1387 return doc
->GetDocumentLoadGroup();
1393 nsresult
XMLHttpRequestMainThread::FireReadystatechangeEvent() {
1394 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::UNSENT
);
1395 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1396 event
->InitEvent(kLiteralString_readystatechange
, false, false);
1397 // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1398 event
->SetTrusted(true);
1399 DispatchOrStoreEvent(this, event
);
1403 void XMLHttpRequestMainThread::DispatchProgressEvent(
1404 DOMEventTargetHelper
* aTarget
, const ProgressEventType
& aType
,
1405 int64_t aLoaded
, int64_t aTotal
) {
1407 NS_ASSERTION(aTarget
, "null target");
1409 if (NS_FAILED(CheckCurrentGlobalCorrectness()) ||
1410 (!AllowUploadProgress() && aTarget
== mUpload
)) {
1414 // If blocked by CORS, zero-out the stats on progress events
1415 // and never fire "progress" or "load" events at all.
1416 if (IsDeniedCrossSiteCORSRequest()) {
1417 if (aType
== Events::progress
|| aType
== Events::load
) {
1424 ProgressEventInit init
;
1425 init
.mBubbles
= false;
1426 init
.mCancelable
= false;
1427 init
.mLengthComputable
= aTotal
!= -1; // XHR spec step 6.1
1428 init
.mLoaded
= aLoaded
;
1429 init
.mTotal
= (aTotal
== -1) ? 0 : aTotal
;
1431 RefPtr
<ProgressEvent
> event
=
1432 ProgressEvent::Constructor(aTarget
, aType
, init
);
1433 event
->SetTrusted(true);
1436 gXMLHttpRequestLog
, LogLevel::Debug
,
1437 ("firing %s event (%u,%u,%" PRIu64
",%" PRIu64
")", aType
.cStr
,
1438 aTarget
== mUpload
, aTotal
!= -1, aLoaded
, (aTotal
== -1) ? 0 : aTotal
));
1440 DispatchOrStoreEvent(aTarget
, event
);
1442 // If we're sending a load, error, timeout or abort event, then
1443 // also dispatch the subsequent loadend event.
1444 if (aType
== Events::load
|| aType
== Events::error
||
1445 aType
== Events::timeout
|| aType
== Events::abort
) {
1446 DispatchProgressEvent(aTarget
, Events::loadend
, aLoaded
, aTotal
);
1450 void XMLHttpRequestMainThread::DispatchOrStoreEvent(
1451 DOMEventTargetHelper
* aTarget
, Event
* aEvent
) {
1453 MOZ_ASSERT(aTarget
);
1456 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
1460 if (mEventDispatchingSuspended
) {
1461 PendingEvent
* event
= mPendingEvents
.AppendElement();
1462 event
->mTarget
= aTarget
;
1463 event
->mEvent
= aEvent
;
1467 aTarget
->DispatchEvent(*aEvent
);
1470 void XMLHttpRequestMainThread::SuspendEventDispatching() {
1471 MOZ_ASSERT(!mEventDispatchingSuspended
);
1472 mEventDispatchingSuspended
= true;
1475 void XMLHttpRequestMainThread::ResumeEventDispatching() {
1476 MOZ_ASSERT(mEventDispatchingSuspended
);
1477 mEventDispatchingSuspended
= false;
1479 nsTArray
<PendingEvent
> pendingEvents
= std::move(mPendingEvents
);
1481 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
1485 for (uint32_t i
= 0; i
< pendingEvents
.Length(); ++i
) {
1486 pendingEvents
[i
].mTarget
->DispatchEvent(*pendingEvents
[i
].mEvent
);
1490 already_AddRefed
<nsIHttpChannel
>
1491 XMLHttpRequestMainThread::GetCurrentHttpChannel() {
1492 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
1493 return httpChannel
.forget();
1496 already_AddRefed
<nsIJARChannel
>
1497 XMLHttpRequestMainThread::GetCurrentJARChannel() {
1498 nsCOMPtr
<nsIJARChannel
> appChannel
= do_QueryInterface(mChannel
);
1499 return appChannel
.forget();
1502 bool XMLHttpRequestMainThread::IsSystemXHR() const {
1503 return mIsSystem
|| mPrincipal
->IsSystemPrincipal();
1506 bool XMLHttpRequestMainThread::InUploadPhase() const {
1507 // We're in the upload phase while our state is OPENED.
1508 return mState
== XMLHttpRequest_Binding::OPENED
;
1511 // This case is hit when the async parameter is outright omitted, which
1512 // should set it to true (and the username and password to null).
1513 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1514 const nsAString
& aUrl
, ErrorResult
& aRv
) {
1515 Open(aMethod
, aUrl
, true, VoidString(), VoidString(), aRv
);
1518 // This case is hit when the async parameter is specified, even if the
1519 // JS value was "undefined" (which due to legacy reasons should be
1520 // treated as true, which is how it will already be passed in here).
1521 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1522 const nsAString
& aUrl
, bool aAsync
,
1523 const nsAString
& aUsername
,
1524 const nsAString
& aPassword
,
1526 Open(aMethod
, NS_ConvertUTF16toUTF8(aUrl
), aAsync
, aUsername
, aPassword
, aRv
);
1529 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1530 const nsACString
& aUrl
, bool aAsync
,
1531 const nsAString
& aUsername
,
1532 const nsAString
& aPassword
,
1534 DEBUG_WORKERREFS1(aMethod
<< " " << aUrl
);
1535 NOT_CALLABLE_IN_SYNC_SEND_RV
1538 if (!aAsync
&& !DontWarnAboutSyncXHR() && GetOwner() &&
1539 GetOwner()->GetExtantDoc()) {
1540 GetOwner()->GetExtantDoc()->WarnOnceAbout(
1541 DeprecatedOperations::eSyncXMLHttpRequestDeprecated
);
1544 Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC
,
1548 nsCOMPtr
<Document
> responsibleDocument
= GetDocumentIfCurrent();
1549 if (!responsibleDocument
) {
1550 // This could be because we're no longer current or because we're in some
1551 // non-window context...
1552 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
1553 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1558 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
1563 if (!aAsync
&& responsibleDocument
&& GetOwner()) {
1564 // We have no extant document during unload, so the above general
1565 // syncXHR warning will not display. But we do want to display a
1566 // recommendation to use sendBeacon instead of syncXHR during unload.
1567 nsCOMPtr
<nsIDocShell
> shell
= responsibleDocument
->GetDocShell();
1569 bool inUnload
= false;
1570 shell
->GetIsInUnload(&inUnload
);
1572 LogMessage("UseSendBeaconDuringUnloadAndPagehideWarning", GetOwner());
1578 nsAutoCString method
;
1579 aRv
= FetchUtil::GetValidRequestMethod(aMethod
, method
);
1580 if (NS_WARN_IF(aRv
.Failed())) {
1585 nsIURI
* baseURI
= nullptr;
1588 } else if (responsibleDocument
) {
1589 baseURI
= responsibleDocument
->GetBaseURI();
1592 // Use the responsible document's encoding for the URL if we have one,
1593 // except for dedicated workers. Use UTF-8 otherwise.
1594 NotNull
<const Encoding
*> originCharset
= UTF_8_ENCODING
;
1595 if (responsibleDocument
&&
1596 responsibleDocument
->NodePrincipal() == mPrincipal
) {
1597 originCharset
= responsibleDocument
->GetDocumentCharacterSet();
1600 nsCOMPtr
<nsIURI
> parsedURL
;
1602 NS_NewURI(getter_AddRefs(parsedURL
), aUrl
, originCharset
, baseURI
);
1603 if (NS_FAILED(rv
)) {
1604 aRv
.ThrowSyntaxError("'"_ns
+ aUrl
+ "' is not a valid URL."_ns
);
1607 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
1608 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1613 // This is already handled by the other Open() method, which passes
1614 // username and password in as NullStrings.
1618 parsedURL
->GetHost(host
);
1619 if (!host
.IsEmpty() && (!aUsername
.IsVoid() || !aPassword
.IsVoid())) {
1620 auto mutator
= NS_MutateURI(parsedURL
);
1621 if (!aUsername
.IsVoid()) {
1622 mutator
.SetUsername(NS_ConvertUTF16toUTF8(aUsername
));
1624 if (!aPassword
.IsVoid()) {
1625 mutator
.SetPassword(NS_ConvertUTF16toUTF8(aPassword
));
1627 Unused
<< mutator
.Finalize(parsedURL
);
1631 if (!aAsync
&& HasOrHasHadOwner() &&
1632 (mTimeoutMilliseconds
||
1633 mResponseType
!= XMLHttpRequestResponseType::_empty
)) {
1634 if (mTimeoutMilliseconds
) {
1635 LogMessage("TimeoutSyncXHRWarning", GetOwner());
1637 if (mResponseType
!= XMLHttpRequestResponseType::_empty
) {
1638 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
1640 aRv
.ThrowInvalidAccessError(
1641 "synchronous XMLHttpRequests do not support timeout and responseType");
1646 TerminateOngoingFetch(NS_OK
);
1649 // timeouts are handled without a flag
1650 DisconnectDoneNotifier();
1652 mRequestMethod
.Assign(method
);
1653 mRequestURL
= parsedURL
;
1654 mFlagSynchronous
= !aAsync
;
1655 mAuthorRequestHeaders
.Clear();
1659 mFlagHadUploadListenersOnSend
= false;
1660 mFlagAborted
= false;
1661 mFlagTimedOut
= false;
1664 // Per spec we should only create the channel on send(), but we have internal
1665 // code that relies on the channel being created now, and that code is not
1666 // always IsSystemXHR(). However, we're not supposed to throw channel-creation
1667 // errors during open(), so we silently ignore those here.
1671 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
1672 mState
= XMLHttpRequest_Binding::OPENED
;
1673 FireReadystatechangeEvent();
1677 void XMLHttpRequestMainThread::SetOriginAttributes(
1678 const OriginAttributesDictionary
& aAttrs
) {
1679 MOZ_ASSERT((mState
== XMLHttpRequest_Binding::OPENED
) && !mFlagSend
);
1681 OriginAttributes
attrs(aAttrs
);
1683 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
1684 loadInfo
->SetOriginAttributes(attrs
);
1688 * "Copy" from a stream.
1690 nsresult
XMLHttpRequestMainThread::StreamReaderFunc(
1691 nsIInputStream
* in
, void* closure
, const char* fromRawSegment
,
1692 uint32_t toOffset
, uint32_t count
, uint32_t* writeCount
) {
1693 XMLHttpRequestMainThread
* xmlHttpRequest
=
1694 static_cast<XMLHttpRequestMainThread
*>(closure
);
1695 if (!xmlHttpRequest
|| !writeCount
) {
1697 "XMLHttpRequest cannot read from stream: no closure or writeCount");
1698 return NS_ERROR_FAILURE
;
1701 nsresult rv
= NS_OK
;
1703 if (xmlHttpRequest
->mResponseType
== XMLHttpRequestResponseType::Blob
) {
1704 xmlHttpRequest
->MaybeCreateBlobStorage();
1705 rv
= xmlHttpRequest
->mBlobStorage
->Append(fromRawSegment
, count
);
1706 } else if (xmlHttpRequest
->mResponseType
==
1707 XMLHttpRequestResponseType::Arraybuffer
&&
1708 !xmlHttpRequest
->mIsMappedArrayBuffer
) {
1709 // get the initial capacity to something reasonable to avoid a bunch of
1710 // reallocs right at the start
1711 if (xmlHttpRequest
->mArrayBufferBuilder
->Capacity() == 0)
1712 xmlHttpRequest
->mArrayBufferBuilder
->SetCapacity(
1713 std::max(count
, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE
));
1715 if (NS_WARN_IF(!xmlHttpRequest
->mArrayBufferBuilder
->Append(
1716 reinterpret_cast<const uint8_t*>(fromRawSegment
), count
,
1717 XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH
))) {
1718 return NS_ERROR_OUT_OF_MEMORY
;
1721 } else if (xmlHttpRequest
->mResponseType
==
1722 XMLHttpRequestResponseType::_empty
&&
1723 xmlHttpRequest
->mResponseXML
) {
1724 // Copy for our own use
1725 if (!xmlHttpRequest
->mResponseBody
.Append(fromRawSegment
, count
,
1727 return NS_ERROR_OUT_OF_MEMORY
;
1729 } else if (xmlHttpRequest
->mResponseType
==
1730 XMLHttpRequestResponseType::_empty
||
1731 xmlHttpRequest
->mResponseType
==
1732 XMLHttpRequestResponseType::Text
||
1733 xmlHttpRequest
->mResponseType
==
1734 XMLHttpRequestResponseType::Json
) {
1735 MOZ_ASSERT(!xmlHttpRequest
->mResponseXML
,
1736 "We shouldn't be parsing a doc here");
1737 rv
= xmlHttpRequest
->AppendToResponseText(
1738 AsBytes(Span(fromRawSegment
, count
)));
1739 if (NS_WARN_IF(NS_FAILED(rv
))) {
1744 if (xmlHttpRequest
->mFlagParseBody
) {
1745 // Give the same data to the parser.
1747 // We need to wrap the data in a new lightweight stream and pass that
1748 // to the parser, because calling ReadSegments() recursively on the same
1749 // stream is not supported.
1750 nsCOMPtr
<nsIInputStream
> copyStream
;
1751 rv
= NS_NewByteInputStream(getter_AddRefs(copyStream
),
1752 Span(fromRawSegment
, count
),
1753 NS_ASSIGNMENT_DEPEND
);
1755 if (NS_SUCCEEDED(rv
) && xmlHttpRequest
->mXMLParserStreamListener
) {
1756 NS_ASSERTION(copyStream
, "NS_NewByteInputStream lied");
1757 nsresult parsingResult
=
1758 xmlHttpRequest
->mXMLParserStreamListener
->OnDataAvailable(
1759 xmlHttpRequest
->mChannel
, copyStream
, toOffset
, count
);
1761 // No use to continue parsing if we failed here, but we
1762 // should still finish reading the stream
1763 if (NS_FAILED(parsingResult
)) {
1764 xmlHttpRequest
->mFlagParseBody
= false;
1769 if (NS_SUCCEEDED(rv
)) {
1770 *writeCount
= count
;
1780 void GetBlobURIFromChannel(nsIRequest
* aRequest
, nsIURI
** aURI
) {
1781 MOZ_ASSERT(aRequest
);
1786 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1791 nsCOMPtr
<nsIURI
> uri
;
1792 nsresult rv
= channel
->GetURI(getter_AddRefs(uri
));
1793 if (NS_FAILED(rv
)) {
1797 if (!dom::IsBlobURI(uri
)) {
1804 nsresult
GetLocalFileFromChannel(nsIRequest
* aRequest
, nsIFile
** aFile
) {
1805 MOZ_ASSERT(aRequest
);
1810 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(aRequest
);
1815 nsCOMPtr
<nsIFile
> file
;
1816 nsresult rv
= fc
->GetFile(getter_AddRefs(file
));
1817 if (NS_WARN_IF(NS_FAILED(rv
))) {
1825 nsresult
DummyStreamReaderFunc(nsIInputStream
* aInputStream
, void* aClosure
,
1826 const char* aFromRawSegment
, uint32_t aToOffset
,
1827 uint32_t aCount
, uint32_t* aWriteCount
) {
1828 *aWriteCount
= aCount
;
1832 class FileCreationHandler final
: public PromiseNativeHandler
{
1836 static void Create(Promise
* aPromise
, XMLHttpRequestMainThread
* aXHR
) {
1837 MOZ_ASSERT(aPromise
);
1839 RefPtr
<FileCreationHandler
> handler
= new FileCreationHandler(aXHR
);
1840 aPromise
->AppendNativeHandler(handler
);
1843 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
1844 ErrorResult
& aRv
) override
{
1845 if (NS_WARN_IF(!aValue
.isObject())) {
1846 mXHR
->LocalFileToBlobCompleted(nullptr);
1851 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob
, &aValue
.toObject(), blob
)))) {
1852 mXHR
->LocalFileToBlobCompleted(nullptr);
1856 mXHR
->LocalFileToBlobCompleted(blob
->Impl());
1859 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
1860 ErrorResult
& aRv
) override
{
1861 mXHR
->LocalFileToBlobCompleted(nullptr);
1865 explicit FileCreationHandler(XMLHttpRequestMainThread
* aXHR
) : mXHR(aXHR
) {
1869 ~FileCreationHandler() = default;
1871 RefPtr
<XMLHttpRequestMainThread
> mXHR
;
1874 NS_IMPL_ISUPPORTS0(FileCreationHandler
)
1878 void XMLHttpRequestMainThread::LocalFileToBlobCompleted(BlobImpl
* aBlobImpl
) {
1879 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::DONE
);
1881 mResponseBlobImpl
= aBlobImpl
;
1882 mBlobStorage
= nullptr;
1883 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1885 ChangeStateToDone(mFlagSyncLooping
);
1889 XMLHttpRequestMainThread::OnDataAvailable(nsIRequest
* request
,
1890 nsIInputStream
* inStr
,
1891 uint64_t sourceOffset
,
1894 NS_ENSURE_ARG_POINTER(inStr
);
1896 mProgressSinceLastProgressEvent
= true;
1897 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
1901 if (mResponseType
== XMLHttpRequestResponseType::Blob
) {
1902 nsCOMPtr
<nsIFile
> localFile
;
1903 nsCOMPtr
<nsIURI
> blobURI
;
1904 GetBlobURIFromChannel(request
, getter_AddRefs(blobURI
));
1906 RefPtr
<BlobImpl
> blobImpl
;
1907 rv
= NS_GetBlobForBlobURI(blobURI
, getter_AddRefs(blobImpl
));
1908 if (NS_SUCCEEDED(rv
)) {
1909 mResponseBlobImpl
= blobImpl
;
1912 rv
= GetLocalFileFromChannel(request
, getter_AddRefs(localFile
));
1914 if (NS_WARN_IF(NS_FAILED(rv
))) {
1918 if (mResponseBlobImpl
|| localFile
) {
1919 mBlobStorage
= nullptr;
1920 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1922 // The nsIStreamListener contract mandates us to read from the stream
1923 // before returning.
1925 rv
= inStr
->ReadSegments(DummyStreamReaderFunc
, nullptr, count
,
1927 NS_ENSURE_SUCCESS(rv
, rv
);
1929 ChangeState(XMLHttpRequest_Binding::LOADING
);
1931 // Cancel() must be called with an error. We use
1932 // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
1933 // just because we can retrieve the File from the channel directly.
1934 return request
->Cancel(NS_ERROR_FILE_ALREADY_EXISTS
);
1939 rv
= inStr
->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc
,
1940 (void*)this, count
, &totalRead
);
1941 NS_ENSURE_SUCCESS(rv
, rv
);
1943 // Fire the first progress event/loading state change
1944 if (mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
) {
1945 ChangeState(XMLHttpRequest_Binding::LOADING
);
1946 if (!mFlagSynchronous
) {
1947 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
,
1950 mProgressSinceLastProgressEvent
= false;
1953 if (!mFlagSynchronous
&& !mProgressTimerIsActive
) {
1954 StartProgressEventTimer();
1961 XMLHttpRequestMainThread::OnStartRequest(nsIRequest
* request
) {
1963 AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStartRequest", NETWORK
);
1965 nsresult rv
= NS_OK
;
1967 if (request
!= mChannel
) {
1968 // Can this still happen?
1972 // Don't do anything if we have been aborted
1973 if (mState
== XMLHttpRequest_Binding::UNSENT
) {
1977 // Don't do anything if we're in mid-abort, but let the request
1978 // know (this can happen due to race conditions in valid XHRs,
1979 // see bz1070763 for info).
1981 return NS_BINDING_ABORTED
;
1984 // Don't do anything if we have timed out.
1985 if (mFlagTimedOut
) {
1989 // If we were asked for a bad range on a blob URL, but we're async,
1990 // we should throw now in order to fire an error progress event.
1991 if (BadContentRangeRequested()) {
1992 return NS_ERROR_NET_PARTIAL_TRANSFER
;
1995 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
1996 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
1999 request
->GetStatus(&status
);
2000 if (mErrorLoad
== ErrorType::eOK
&& NS_FAILED(status
)) {
2001 mErrorLoad
= ErrorType::eRequest
;
2002 mErrorLoadDetail
= status
;
2005 // Upload phase is now over. If we were uploading anything,
2006 // stop the timer and fire any final progress events.
2007 if (mUpload
&& !mUploadComplete
&& mErrorLoad
== ErrorType::eOK
&&
2008 !mFlagSynchronous
) {
2009 StopProgressEventTimer();
2011 mUploadTransferred
= mUploadTotal
;
2013 if (mProgressSinceLastProgressEvent
) {
2014 DispatchProgressEvent(mUpload
, Events::progress
, mUploadTransferred
,
2016 mProgressSinceLastProgressEvent
= false;
2019 mUploadComplete
= true;
2020 DispatchProgressEvent(mUpload
, Events::load
, mUploadTotal
, mUploadTotal
);
2023 mFlagParseBody
= true;
2024 if (mErrorLoad
== ErrorType::eOK
) {
2025 ChangeState(XMLHttpRequest_Binding::HEADERS_RECEIVED
);
2030 if (!mOverrideMimeType
.IsEmpty()) {
2031 channel
->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
2034 // Fallback to 'application/octet-stream' (leaving data URLs alone)
2035 if (!IsBlobURI(mRequestURL
)) {
2037 channel
->GetContentType(type
);
2038 if (type
.IsEmpty() || type
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
2039 channel
->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM
));
2045 // Set up arraybuffer
2046 if (mResponseType
== XMLHttpRequestResponseType::Arraybuffer
&&
2047 NS_SUCCEEDED(status
)) {
2048 if (mIsMappedArrayBuffer
) {
2049 nsCOMPtr
<nsIJARChannel
> jarChannel
= do_QueryInterface(channel
);
2051 nsCOMPtr
<nsIURI
> uri
;
2052 rv
= channel
->GetURI(getter_AddRefs(uri
));
2053 if (NS_SUCCEEDED(rv
)) {
2055 nsAutoCString scheme
;
2056 uri
->GetScheme(scheme
);
2057 if (scheme
.LowerCaseEqualsLiteral("jar")) {
2058 nsCOMPtr
<nsIJARURI
> jarURI
= do_QueryInterface(uri
);
2060 jarURI
->GetJAREntry(file
);
2063 nsCOMPtr
<nsIFile
> jarFile
;
2064 jarChannel
->GetJarFile(getter_AddRefs(jarFile
));
2066 mIsMappedArrayBuffer
= false;
2068 rv
= mArrayBufferBuilder
->MapToFileInPackage(file
, jarFile
);
2069 // This can happen legitimately if there are compressed files
2070 // in the jarFile. See bug #1357219. No need to warn on the error.
2071 if (NS_FAILED(rv
)) {
2072 mIsMappedArrayBuffer
= false;
2074 channel
->SetContentType("application/mem-mapped"_ns
);
2080 // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
2081 // and we want it fallback to the malloc way.
2082 if (!mIsMappedArrayBuffer
) {
2083 int64_t contentLength
;
2084 rv
= channel
->GetContentLength(&contentLength
);
2085 if (NS_SUCCEEDED(rv
) && contentLength
> 0 &&
2086 contentLength
< XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE
) {
2087 mArrayBufferBuilder
->SetCapacity(static_cast<int32_t>(contentLength
));
2092 // Set up responseXML
2093 // Fetch spec Main Fetch step 21: ignore body for head/connect methods.
2094 bool parseBody
= (mResponseType
== XMLHttpRequestResponseType::_empty
||
2095 mResponseType
== XMLHttpRequestResponseType::Document
) &&
2096 !(mRequestMethod
.EqualsLiteral("HEAD") ||
2097 mRequestMethod
.EqualsLiteral("CONNECT"));
2100 // Do not try to parse documents if content-length = 0
2101 int64_t contentLength
;
2102 if (NS_SUCCEEDED(mChannel
->GetContentLength(&contentLength
)) &&
2103 contentLength
== 0) {
2109 mWarnAboutSyncHtml
= false;
2110 if (parseBody
&& NS_SUCCEEDED(status
)) {
2111 // We can gain a huge performance win by not even trying to
2112 // parse non-XML data. This also protects us from the situation
2113 // where we have an XML document and sink, but HTML (or other)
2114 // parser, which can produce unreliable results.
2116 channel
->GetContentType(type
);
2118 if ((mResponseType
== XMLHttpRequestResponseType::Document
) &&
2119 type
.EqualsLiteral("text/html")) {
2120 // HTML parsing is only supported for responseType == "document" to
2121 // avoid running the parser and, worse, populating responseXML for
2122 // legacy users of XHR who use responseType == "" for retrieving the
2123 // responseText of text/html resources. This legacy case is so common
2124 // that it's not useful to emit a warning about it.
2125 if (mFlagSynchronous
) {
2126 // We don't make cool new features available in the bad synchronous
2127 // mode. The synchronous mode is for legacy only.
2128 mWarnAboutSyncHtml
= true;
2129 mFlagParseBody
= false;
2133 } else if (!type
.IsEmpty() && (!(type
.EqualsLiteral("text/xml") ||
2134 type
.EqualsLiteral("application/xml") ||
2135 StringEndsWith(type
, "+xml"_ns
)))) {
2136 // Follow https://xhr.spec.whatwg.org/
2137 // If final MIME type is not null, text/html, text/xml, application/xml,
2138 // or does not end in +xml, return null.
2139 mFlagParseBody
= false;
2142 // The request failed, so we shouldn't be parsing anyway
2143 mFlagParseBody
= false;
2146 if (mFlagParseBody
) {
2147 nsCOMPtr
<nsIURI
> baseURI
, docURI
;
2148 rv
= mChannel
->GetURI(getter_AddRefs(docURI
));
2149 NS_ENSURE_SUCCESS(rv
, rv
);
2152 nsCOMPtr
<Document
> doc
= GetDocumentIfCurrent();
2153 nsCOMPtr
<nsIURI
> chromeXHRDocURI
, chromeXHRDocBaseURI
;
2155 chromeXHRDocURI
= doc
->GetDocumentURI();
2156 chromeXHRDocBaseURI
= doc
->GetBaseURI();
2158 // If we're no longer current, just kill the load, though it really should
2159 // have been killed already.
2160 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
2161 return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
;
2165 // Create an empty document from it.
2166 const auto& emptyStr
= u
""_ns
;
2167 nsIGlobalObject
* global
= DOMEventTargetHelper::GetParentObject();
2169 nsCOMPtr
<nsIPrincipal
> requestingPrincipal
;
2170 rv
= nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
2171 channel
, getter_AddRefs(requestingPrincipal
));
2172 NS_ENSURE_SUCCESS(rv
, rv
);
2174 rv
= NS_NewDOMDocument(
2175 getter_AddRefs(mResponseXML
), emptyStr
, emptyStr
, nullptr, docURI
,
2176 baseURI
, requestingPrincipal
, true, global
,
2177 mIsHtml
? DocumentFlavorHTML
: DocumentFlavorLegacyGuess
);
2178 NS_ENSURE_SUCCESS(rv
, rv
);
2179 mResponseXML
->SetChromeXHRDocURI(chromeXHRDocURI
);
2180 mResponseXML
->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI
);
2182 // suppress parsing failure messages to console for statuses which
2183 // can have empty bodies (see bug 884693).
2184 IgnoredErrorResult rv2
;
2185 uint32_t responseStatus
= GetStatus(rv2
);
2186 if (!rv2
.Failed() && (responseStatus
== 201 || responseStatus
== 202 ||
2187 responseStatus
== 204 || responseStatus
== 205 ||
2188 responseStatus
== 304)) {
2189 mResponseXML
->SetSuppressParserErrorConsoleMessages(true);
2192 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2193 bool isCrossSite
= false;
2194 isCrossSite
= loadInfo
->GetTainting() != LoadTainting::Basic
;
2197 mResponseXML
->DisableCookieAccess();
2200 nsCOMPtr
<nsIStreamListener
> listener
;
2201 nsCOMPtr
<nsILoadGroup
> loadGroup
;
2202 channel
->GetLoadGroup(getter_AddRefs(loadGroup
));
2204 // suppress <parsererror> nodes on XML document parse failure, but only
2205 // for non-privileged code (including Web Extensions). See bug 289714.
2206 if (!IsSystemXHR()) {
2207 mResponseXML
->SetSuppressParserErrorElement(true);
2210 rv
= mResponseXML
->StartDocumentLoad(kLoadAsData
, channel
, loadGroup
,
2211 nullptr, getter_AddRefs(listener
),
2213 NS_ENSURE_SUCCESS(rv
, rv
);
2215 // the spec requires the response document.referrer to be the empty string
2216 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2217 new ReferrerInfo(nullptr, mResponseXML
->ReferrerPolicy());
2218 mResponseXML
->SetReferrerInfo(referrerInfo
);
2220 mXMLParserStreamListener
= listener
;
2221 rv
= mXMLParserStreamListener
->OnStartRequest(request
);
2222 NS_ENSURE_SUCCESS(rv
, rv
);
2225 // Download phase beginning; start the progress event timer if necessary.
2226 if (NS_SUCCEEDED(rv
) && HasListenersFor(nsGkAtoms::onprogress
)) {
2227 StartProgressEventTimer();
2234 XMLHttpRequestMainThread::OnStopRequest(nsIRequest
* request
, nsresult status
) {
2236 AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStopRequest", NETWORK
);
2238 if (request
!= mChannel
) {
2239 // Can this still happen?
2243 // Send the decoder the signal that we've hit the end of the stream,
2244 // but only when decoding text eagerly.
2245 if (mDecoder
&& ((mResponseType
== XMLHttpRequestResponseType::Text
) ||
2246 (mResponseType
== XMLHttpRequestResponseType::Json
) ||
2247 (mResponseType
== XMLHttpRequestResponseType::_empty
&&
2249 AppendToResponseText(Span
<const uint8_t>(), true);
2252 mWaitingForOnStopRequest
= false;
2254 // make sure to notify the listener if we were aborted
2255 // XXX in fact, why don't we do the cleanup below in this case??
2256 // UNSENT is for abort calls. See OnStartRequest above.
2257 if (mState
== XMLHttpRequest_Binding::UNSENT
|| mFlagTimedOut
) {
2258 if (mXMLParserStreamListener
)
2259 (void)mXMLParserStreamListener
->OnStopRequest(request
, status
);
2263 // Is this good enough here?
2264 if (mXMLParserStreamListener
&& mFlagParseBody
) {
2265 mXMLParserStreamListener
->OnStopRequest(request
, status
);
2268 mXMLParserStreamListener
= nullptr;
2271 // If window.stop() or other aborts were issued, handle as an abort
2272 if (status
== NS_BINDING_ABORTED
) {
2273 mFlagParseBody
= false;
2274 IgnoredErrorResult rv
;
2275 RequestErrorSteps(Events::abort
, NS_ERROR_DOM_ABORT_ERR
, rv
);
2276 ChangeState(XMLHttpRequest_Binding::UNSENT
, false);
2280 // If we were just reading a blob URL, we're already done
2281 if (status
== NS_ERROR_FILE_ALREADY_EXISTS
&& mResponseBlobImpl
) {
2282 ChangeStateToDone(mFlagSyncLooping
);
2286 bool waitingForBlobCreation
= false;
2288 // If we have this error, we have to deal with a file: URL + responseType =
2289 // blob. We have this error because we canceled the channel. The status will
2291 if (!mResponseBlobImpl
&& status
== NS_ERROR_FILE_ALREADY_EXISTS
&&
2292 mResponseType
== XMLHttpRequestResponseType::Blob
) {
2293 nsCOMPtr
<nsIFile
> file
;
2294 nsresult rv
= GetLocalFileFromChannel(request
, getter_AddRefs(file
));
2295 if (NS_WARN_IF(NS_FAILED(rv
))) {
2300 nsAutoCString contentType
;
2301 rv
= mChannel
->GetContentType(contentType
);
2302 if (NS_WARN_IF(NS_FAILED(rv
))) {
2306 ChromeFilePropertyBag bag
;
2307 CopyUTF8toUTF16(contentType
, bag
.mType
);
2309 nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal();
2312 RefPtr
<Promise
> promise
=
2313 FileCreatorHelper::CreateFile(global
, file
, bag
, true, error
);
2314 if (NS_WARN_IF(error
.Failed())) {
2315 return error
.StealNSResult();
2318 FileCreationHandler::Create(promise
, this);
2319 waitingForBlobCreation
= true;
2322 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
2323 NS_ASSERTION(mResponseText
.IsEmpty(), "mResponseText should be empty");
2327 if (NS_SUCCEEDED(status
) &&
2328 mResponseType
== XMLHttpRequestResponseType::Blob
&&
2329 !waitingForBlobCreation
) {
2330 // Smaller files may be written in cache map instead of separate files.
2331 // Also, no-store response cannot be written in persistent cache.
2332 nsAutoCString contentType
;
2333 if (!mOverrideMimeType
.IsEmpty()) {
2334 contentType
.Assign(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
2336 mChannel
->GetContentType(contentType
);
2339 // mBlobStorage can be null if the channel is non-file non-cacheable
2340 // and if the response length is zero.
2341 MaybeCreateBlobStorage();
2342 mBlobStorage
->GetBlobImplWhenReady(contentType
, this);
2343 waitingForBlobCreation
= true;
2345 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
2346 NS_ASSERTION(mResponseText
.IsEmpty(), "mResponseText should be empty");
2347 } else if (NS_SUCCEEDED(status
) && !mIsMappedArrayBuffer
&&
2348 mResponseType
== XMLHttpRequestResponseType::Arraybuffer
) {
2349 // set the capacity down to the actual length, to realloc back
2350 // down to the actual size
2351 if (!mArrayBufferBuilder
->SetCapacity(mArrayBufferBuilder
->Length())) {
2352 // this should never happen!
2353 status
= NS_ERROR_UNEXPECTED
;
2357 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
2358 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
2360 channel
->SetNotificationCallbacks(nullptr);
2361 mNotificationCallbacks
= nullptr;
2362 mChannelEventSink
= nullptr;
2363 mProgressEventSink
= nullptr;
2365 bool wasSync
= mFlagSyncLooping
;
2366 mFlagSyncLooping
= false;
2367 mRequestSentTime
= 0;
2369 // update our charset and decoder to match mResponseXML,
2370 // before it is possibly nulled out
2371 MatchCharsetAndDecoderToResponseDocument();
2373 if (NS_FAILED(status
)) {
2374 // This can happen if the server is unreachable. Other possible
2375 // reasons are that the user leaves the page or hits the ESC key.
2377 mErrorLoad
= ErrorType::eUnreachable
;
2378 mErrorLoadDetail
= status
;
2379 mResponseXML
= nullptr;
2381 // Handle network errors specifically per spec.
2382 if (NS_ERROR_GET_MODULE(status
) == NS_ERROR_MODULE_NETWORK
) {
2383 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
2384 ("%p detected networking error 0x%" PRIx32
"\n", this,
2385 static_cast<uint32_t>(status
)));
2386 IgnoredErrorResult rv
;
2387 mFlagParseBody
= false;
2388 RequestErrorSteps(Events::error
, NS_ERROR_DOM_NETWORK_ERR
, rv
);
2389 // RequestErrorSteps will not call ChangeStateToDone for sync XHRs, so we
2390 // do so here to ensure progress events are sent and our state is sane.
2391 if (mFlagSynchronous
) {
2392 ChangeStateToDone(wasSync
);
2397 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
2398 ("%p detected unreachable error 0x%" PRIx32
"\n", this,
2399 static_cast<uint32_t>(status
)));
2402 // If we're uninitialized at this point, we encountered an error
2403 // earlier and listeners have already been notified. Also we do
2404 // not want to do this if we already completed.
2405 if (mState
== XMLHttpRequest_Binding::UNSENT
||
2406 mState
== XMLHttpRequest_Binding::DONE
) {
2410 if (!mResponseXML
) {
2411 mFlagParseBody
= false;
2413 // We postpone the 'done' until the creation of the Blob is completed.
2414 if (!waitingForBlobCreation
) {
2415 ChangeStateToDone(wasSync
);
2422 NS_ASSERTION(!mFlagSyncLooping
,
2423 "We weren't supposed to support HTML parsing with XHR!");
2424 mParseEndListener
= new nsXHRParseEndListener(this);
2425 RefPtr
<EventTarget
> eventTarget
= mResponseXML
;
2426 EventListenerManager
* manager
= eventTarget
->GetOrCreateListenerManager();
2427 manager
->AddEventListenerByType(mParseEndListener
,
2428 kLiteralString_DOMContentLoaded
,
2429 TrustedEventsAtSystemGroupBubble());
2432 mFlagParseBody
= false;
2435 // We might have been sent non-XML data. If that was the case,
2436 // we should null out the document member. The idea in this
2437 // check here is that if there is no document element it is not
2438 // an XML document. We might need a fancier check...
2439 if (!mResponseXML
->GetRootElement()) {
2440 mErrorParsingXML
= true;
2441 mResponseXML
= nullptr;
2443 ChangeStateToDone(wasSync
);
2447 void XMLHttpRequestMainThread::OnBodyParseEnd() {
2448 mFlagParseBody
= false;
2449 mParseEndListener
= nullptr;
2450 ChangeStateToDone(mFlagSyncLooping
);
2453 void XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument() {
2456 mDecoder
->Encoding() != mResponseXML
->GetDocumentCharacterSet())) {
2457 TruncateResponseText();
2458 mResponseBodyDecodedPos
= 0;
2459 mEofDecoded
= false;
2460 mDecoder
= mResponseXML
->GetDocumentCharacterSet()->NewDecoder();
2464 void XMLHttpRequestMainThread::DisconnectDoneNotifier() {
2465 if (mDelayedDoneNotifier
) {
2466 // Disconnect may release the last reference to 'this'.
2467 RefPtr
<XMLHttpRequestMainThread
> kungfuDeathGrip
= this;
2468 mDelayedDoneNotifier
->Disconnect();
2469 mDelayedDoneNotifier
= nullptr;
2473 void XMLHttpRequestMainThread::ChangeStateToDone(bool aWasSync
) {
2475 DisconnectDoneNotifier();
2477 if (!mForWorker
&& !aWasSync
&& mChannel
) {
2478 // If the top level page is loading, try to postpone the handling of the
2480 nsLoadFlags loadFlags
= 0;
2481 mChannel
->GetLoadFlags(&loadFlags
);
2482 if (loadFlags
& nsIRequest::LOAD_BACKGROUND
) {
2483 nsPIDOMWindowInner
* owner
= GetOwner();
2484 BrowsingContext
* bc
= owner
? owner
->GetBrowsingContext() : nullptr;
2485 bc
= bc
? bc
->Top() : nullptr;
2486 if (bc
&& bc
->IsLoading()) {
2487 MOZ_ASSERT(!mDelayedDoneNotifier
);
2488 RefPtr
<XMLHttpRequestDoneNotifier
> notifier
=
2489 new XMLHttpRequestDoneNotifier(this);
2490 mDelayedDoneNotifier
= notifier
;
2491 bc
->AddDeprioritizedLoadRunner(notifier
);
2497 ChangeStateToDoneInternal();
2500 void XMLHttpRequestMainThread::ChangeStateToDoneInternal() {
2502 DisconnectDoneNotifier();
2503 StopProgressEventTimer();
2505 MOZ_ASSERT(!mFlagParseBody
,
2506 "ChangeStateToDone() called before async HTML parsing is done.");
2510 CancelTimeoutTimer();
2512 // Per spec, fire the last download progress event, if any,
2513 // before readystatechange=4/done. (Note that 0-sized responses
2514 // will have not sent a progress event yet, so one must be sent here).
2515 if (!mFlagSynchronous
&&
2516 (!mLoadTransferred
|| mProgressSinceLastProgressEvent
)) {
2517 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
, mLoadTotal
);
2518 mProgressSinceLastProgressEvent
= false;
2521 // Notify the document when an XHR request completes successfully.
2522 // This is used by the password manager as a hint to observe DOM mutations.
2523 // Call this prior to changing state to DONE to ensure we set up the
2524 // observer before mutations occur.
2525 if (mErrorLoad
== ErrorType::eOK
) {
2526 Document
* doc
= GetDocumentIfCurrent();
2528 doc
->NotifyFetchOrXHRSuccess();
2532 // Per spec, fire readystatechange=4/done before final error events.
2533 ChangeState(XMLHttpRequest_Binding::DONE
, true);
2535 // Per spec, if we failed in the upload phase, fire a final error
2536 // and loadend events for the upload after readystatechange=4/done.
2537 if (!mFlagSynchronous
&& mUpload
&& !mUploadComplete
) {
2538 DispatchProgressEvent(mUpload
, Events::error
, 0, -1);
2541 // Per spec, fire download's load/error and loadend events after
2542 // readystatechange=4/done (and of course all upload events).
2543 if (mErrorLoad
!= ErrorType::eOK
) {
2544 DispatchProgressEvent(this, Events::error
, 0, -1);
2546 DispatchProgressEvent(this, Events::load
, mLoadTransferred
, mLoadTotal
);
2549 if (mErrorLoad
!= ErrorType::eOK
) {
2550 // By nulling out channel here we make it so that Send() can test
2551 // for that and throw. Also calling the various status
2552 // methods/members will not throw.
2553 // This matches what IE does.
2558 nsresult
XMLHttpRequestMainThread::CreateChannel() {
2560 // When we are called from JS we can find the load group for the page,
2561 // and add ourselves to it. This way any pending requests
2562 // will be automatically aborted if the user leaves the page.
2563 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetLoadGroup();
2565 nsSecurityFlags secFlags
;
2566 nsLoadFlags loadFlags
= nsIRequest::LOAD_BACKGROUND
;
2567 uint32_t sandboxFlags
= 0;
2568 if (mPrincipal
->IsSystemPrincipal()) {
2569 // When chrome is loading we want to make sure to sandbox any potential
2570 // result document. We also want to allow cross-origin loads.
2571 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
2572 sandboxFlags
= SANDBOXED_ORIGIN
;
2573 } else if (IsSystemXHR()) {
2574 // For pages that have appropriate permissions, we want to still allow
2575 // cross-origin loads, but make sure that the any potential result
2576 // documents get the same principal as the loader.
2577 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
|
2578 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
2579 loadFlags
|= nsIChannel::LOAD_BYPASS_SERVICE_WORKER
;
2581 // Otherwise use CORS. Again, make sure that potential result documents
2582 // use the same principal as the loader.
2583 secFlags
= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
|
2584 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
2588 secFlags
|= nsILoadInfo::SEC_COOKIES_OMIT
;
2591 // Use the responsibleDocument if we have it, except for dedicated workers
2592 // where it will be the parent document, which is not the one we want to use.
2594 nsCOMPtr
<Document
> responsibleDocument
= GetDocumentIfCurrent();
2595 if (responsibleDocument
&&
2596 responsibleDocument
->NodePrincipal() == mPrincipal
) {
2597 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
,
2598 responsibleDocument
, secFlags
,
2599 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2600 nullptr, // aPerformanceStorage
2602 nullptr, // aCallbacks
2603 loadFlags
, nullptr, sandboxFlags
);
2604 } else if (mClientInfo
.isSome()) {
2605 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
, mPrincipal
,
2606 mClientInfo
.ref(), mController
, secFlags
,
2607 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2609 mPerformanceStorage
, // aPerformanceStorage
2611 nullptr, // aCallbacks
2612 loadFlags
, nullptr, sandboxFlags
);
2614 // Otherwise use the principal.
2615 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
, mPrincipal
,
2616 secFlags
, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2618 mPerformanceStorage
, // aPerformanceStorage
2620 nullptr, // aCallbacks
2621 loadFlags
, nullptr, sandboxFlags
);
2623 NS_ENSURE_SUCCESS(rv
, rv
);
2625 if (mCSPEventListener
) {
2626 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2627 rv
= loadInfo
->SetCspEventListener(mCSPEventListener
);
2628 NS_ENSURE_SUCCESS(rv
, rv
);
2631 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2633 rv
= httpChannel
->SetRequestMethod(mRequestMethod
);
2634 NS_ENSURE_SUCCESS(rv
, rv
);
2636 httpChannel
->SetSource(profiler_capture_backtrace());
2638 // Set the initiator type
2639 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChannel
));
2641 timedChannel
->SetInitiatorType(u
"xmlhttprequest"_ns
);
2648 void XMLHttpRequestMainThread::MaybeLowerChannelPriority() {
2649 nsCOMPtr
<Document
> doc
= GetDocumentIfCurrent();
2655 if (!jsapi
.Init(GetOwnerGlobal())) {
2659 JSContext
* cx
= jsapi
.cx();
2661 if (!doc
->IsScriptTracking(cx
)) {
2665 if (StaticPrefs::network_http_tailing_enabled()) {
2666 nsCOMPtr
<nsIClassOfService
> cos
= do_QueryInterface(mChannel
);
2668 // Adding TailAllowed to overrule the Unblocked flag, but to preserve
2669 // the effect of Unblocked when tailing is off.
2670 cos
->AddClassFlags(nsIClassOfService::Throttleable
|
2671 nsIClassOfService::Tail
|
2672 nsIClassOfService::TailAllowed
);
2676 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(mChannel
);
2678 p
->SetPriority(nsISupportsPriority::PRIORITY_LOWEST
);
2682 nsresult
XMLHttpRequestMainThread::InitiateFetch(
2683 already_AddRefed
<nsIInputStream
> aUploadStream
, int64_t aUploadLength
,
2684 nsACString
& aUploadContentType
) {
2687 nsCOMPtr
<nsIInputStream
> uploadStream
= std::move(aUploadStream
);
2689 if (!uploadStream
) {
2690 RefPtr
<PreloaderBase
> preload
= FindPreload();
2692 // Because of bug 682305, we can't let listener be the XHR object itself
2693 // because JS wouldn't be able to use it. So create a listener around
2694 // 'this'. Make sure to hold a strong reference so that we don't leak the
2696 nsCOMPtr
<nsIStreamListener
> listener
=
2697 new net::nsStreamListenerWrapper(this);
2698 rv
= preload
->AsyncConsume(listener
);
2699 if (NS_SUCCEEDED(rv
)) {
2700 mFromPreload
= true;
2702 // May be null when the preload has already finished, but the XHR code
2703 // is safe to live with it.
2704 mChannel
= preload
->Channel();
2705 MOZ_ASSERT(mChannel
);
2706 EnsureChannelContentType();
2714 // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
2715 // in turn keeps STOP button from becoming active. If the consumer passed in
2716 // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
2717 // necko won't generate any progress notifications.
2718 if (HasListenersFor(nsGkAtoms::onprogress
) ||
2719 (mUpload
&& mUpload
->HasListenersFor(nsGkAtoms::onprogress
))) {
2720 nsLoadFlags loadFlags
;
2721 mChannel
->GetLoadFlags(&loadFlags
);
2722 loadFlags
&= ~nsIRequest::LOAD_BACKGROUND
;
2723 loadFlags
|= nsIRequest::LOAD_NORMAL
;
2724 mChannel
->SetLoadFlags(loadFlags
);
2727 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2729 // If the user hasn't overridden the Accept header, set it to */* per spec.
2730 if (!mAuthorRequestHeaders
.Has("accept")) {
2731 mAuthorRequestHeaders
.Set("accept", "*/*"_ns
);
2734 mAuthorRequestHeaders
.ApplyToChannel(httpChannel
, false, false);
2736 if (!IsSystemXHR()) {
2737 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
2738 nsCOMPtr
<Document
> doc
= owner
? owner
->GetExtantDoc() : nullptr;
2739 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2740 ReferrerInfo::CreateForFetch(mPrincipal
, doc
);
2741 Unused
<< httpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
2744 // Some extensions override the http protocol handler and provide their own
2745 // implementation. The channels returned from that implementation don't
2746 // always seem to implement the nsIUploadChannel2 interface, presumably
2747 // because it's a new interface. Eventually we should remove this and simply
2748 // require that http channels implement the new interface (see bug 529041).
2749 nsCOMPtr
<nsIUploadChannel2
> uploadChannel2
= do_QueryInterface(httpChannel
);
2750 if (!uploadChannel2
) {
2751 nsCOMPtr
<nsIConsoleService
> consoleService
=
2752 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
2753 if (consoleService
) {
2754 consoleService
->LogStringMessage(
2755 u
"Http channel implementation doesn't support nsIUploadChannel2. "
2756 "An extension has supplied a non-functional http protocol handler. "
2757 "This will break behavior and in future releases not work at all.");
2762 // If necessary, wrap the stream in a buffered stream so as to guarantee
2763 // support for our upload when calling ExplicitSetUploadStream.
2764 if (!NS_InputStreamIsBuffered(uploadStream
)) {
2765 nsCOMPtr
<nsIInputStream
> bufferedStream
;
2766 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
),
2767 uploadStream
.forget(), 4096);
2768 NS_ENSURE_SUCCESS(rv
, rv
);
2770 uploadStream
= bufferedStream
;
2773 // We want to use a newer version of the upload channel that won't
2774 // ignore the necessary headers for an empty Content-Type.
2775 nsCOMPtr
<nsIUploadChannel2
> uploadChannel2(
2776 do_QueryInterface(httpChannel
));
2777 // This assertion will fire if buggy extensions are installed
2778 NS_ASSERTION(uploadChannel2
, "http must support nsIUploadChannel2");
2779 if (uploadChannel2
) {
2780 uploadChannel2
->ExplicitSetUploadStream(
2781 uploadStream
, aUploadContentType
, mUploadTotal
, mRequestMethod
,
2784 // The http channel doesn't support the new nsIUploadChannel2.
2785 // Emulate it as best we can using nsIUploadChannel.
2786 if (aUploadContentType
.IsEmpty()) {
2787 aUploadContentType
.AssignLiteral("application/octet-stream");
2789 nsCOMPtr
<nsIUploadChannel
> uploadChannel
=
2790 do_QueryInterface(httpChannel
);
2791 uploadChannel
->SetUploadStream(uploadStream
, aUploadContentType
,
2793 // Reset the method to its original value
2794 rv
= httpChannel
->SetRequestMethod(mRequestMethod
);
2795 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2800 // Should set a Content-Range header for blob scheme, and also slice the
2801 // blob appropriately, so we process the Range header here for later use.
2802 if (IsBlobURI(mRequestURL
)) {
2803 nsAutoCString range
;
2804 mAuthorRequestHeaders
.Get("range", range
);
2805 if (!range
.IsVoid()) {
2806 rv
= NS_SetChannelContentRangeForBlobURI(mChannel
, mRequestURL
, range
);
2807 if (mFlagSynchronous
&& NS_FAILED(rv
)) {
2808 // We later fire an error progress event for non-sync
2809 mState
= XMLHttpRequest_Binding::DONE
;
2810 return NS_ERROR_DOM_NETWORK_ERR
;
2815 // Due to the chrome-only XHR.channel API, we need a hacky way to set the
2816 // SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
2817 // .withCredentials can be called after open() is called.
2818 // Not doing this for privileged system XHRs since those don't use CORS.
2819 if (!IsSystemXHR() && !mIsAnon
&& mFlagACwithCredentials
) {
2820 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2821 static_cast<net::LoadInfo
*>(loadInfo
.get())->SetIncludeCookiesSecFlag();
2824 // We never let XHR be blocked by head CSS/JS loads to avoid potential
2825 // deadlock where server generation of CSS/JS requires an XHR signal.
2826 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(mChannel
));
2828 cos
->AddClassFlags(nsIClassOfService::Unblocked
);
2830 // Mark channel as urgent-start if the XHR is triggered by user input
2832 if (UserActivation::IsHandlingUserInput()) {
2833 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
2837 // Disable Necko-internal response timeouts.
2838 nsCOMPtr
<nsIHttpChannelInternal
> internalHttpChannel(
2839 do_QueryInterface(mChannel
));
2840 if (internalHttpChannel
) {
2841 rv
= internalHttpChannel
->SetResponseTimeoutEnabled(false);
2842 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2846 AddLoadFlags(mChannel
, nsIChannel::LOAD_EXPLICIT_CREDENTIALS
);
2849 // Bypass the network cache in cases where it makes no sense:
2850 // POST responses are always unique, and we provide no API that would
2851 // allow our consumers to specify a "cache key" to access old POST
2852 // responses, so they are not worth caching.
2853 if (mRequestMethod
.EqualsLiteral("POST")) {
2854 AddLoadFlags(mChannel
, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE
|
2855 nsIRequest::INHIBIT_CACHING
);
2857 // When we are sync loading, we need to bypass the local cache when it would
2858 // otherwise block us waiting for exclusive access to the cache. If we
2859 // don't do this, then we could dead lock in some cases (see bug 309424).
2861 // Also don't block on the cache entry on async if it is busy - favoring
2862 // parallelism over cache hit rate for xhr. This does not disable the cache
2863 // everywhere - only in cases where more than one channel for the same URI
2864 // is accessed simultanously.
2865 AddLoadFlags(mChannel
, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
);
2868 EnsureChannelContentType();
2870 // Set up the preflight if needed
2871 if (!IsSystemXHR()) {
2872 nsTArray
<nsCString
> CORSUnsafeHeaders
;
2873 mAuthorRequestHeaders
.GetCORSUnsafeHeaders(CORSUnsafeHeaders
);
2874 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2875 loadInfo
->SetCorsPreflightInfo(CORSUnsafeHeaders
,
2876 mFlagHadUploadListenersOnSend
);
2879 // Hook us up to listen to redirects and the like. Only do this very late
2880 // since this creates a cycle between the channel and us. This cycle has
2881 // to be manually broken if anything below fails.
2882 mChannel
->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks
));
2883 mChannel
->SetNotificationCallbacks(this);
2885 if (internalHttpChannel
) {
2886 internalHttpChannel
->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
2889 // Because of bug 682305, we can't let listener be the XHR object itself
2890 // because JS wouldn't be able to use it. So create a listener around 'this'.
2891 // Make sure to hold a strong reference so that we don't leak the wrapper.
2892 nsCOMPtr
<nsIStreamListener
> listener
= new net::nsStreamListenerWrapper(this);
2894 // Check if this XHR is created from a tracking script.
2895 // If yes, lower the channel's priority.
2896 if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
2897 MaybeLowerChannelPriority();
2900 // Associate any originating stack with the channel.
2901 NotifyNetworkMonitorAlternateStack(mChannel
, std::move(mOriginStack
));
2903 // Start reading from the channel
2904 rv
= mChannel
->AsyncOpen(listener
);
2906 if (NS_WARN_IF(NS_FAILED(rv
))) {
2907 // Drop our ref to the channel to avoid cycles. Also drop channel's
2908 // ref to us to be extra safe.
2909 mChannel
->SetNotificationCallbacks(mNotificationCallbacks
);
2912 mErrorLoad
= ErrorType::eChannelOpen
;
2913 mErrorLoadDetail
= rv
;
2915 // Per spec, we throw on sync errors, but not async.
2916 if (mFlagSynchronous
) {
2917 mState
= XMLHttpRequest_Binding::DONE
;
2918 return NS_ERROR_DOM_NETWORK_ERR
;
2925 already_AddRefed
<PreloaderBase
> XMLHttpRequestMainThread::FindPreload() {
2926 Document
* doc
= GetDocumentIfCurrent();
2930 if (mPrincipal
->IsSystemPrincipal() || IsSystemXHR()) {
2933 if (!mRequestMethod
.EqualsLiteral("GET")) {
2934 // Preload can only do GET.
2937 if (!mAuthorRequestHeaders
.IsEmpty()) {
2938 // Preload can't set headers.
2942 // mIsAnon overrules mFlagACwithCredentials.
2943 CORSMode cors
= (mIsAnon
|| !mFlagACwithCredentials
)
2944 ? CORSMode::CORS_ANONYMOUS
2945 : CORSMode::CORS_USE_CREDENTIALS
;
2946 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2947 ReferrerInfo::CreateForFetch(mPrincipal
, doc
);
2948 auto key
= PreloadHashKey::CreateAsFetch(mRequestURL
, cors
);
2949 RefPtr
<PreloaderBase
> preload
= doc
->Preloads().LookupPreload(key
);
2954 preload
->RemoveSelf(doc
);
2955 preload
->NotifyUsage(doc
, PreloaderBase::LoadBackground::Keep
);
2957 return preload
.forget();
2960 void XMLHttpRequestMainThread::EnsureChannelContentType() {
2961 MOZ_ASSERT(mChannel
);
2963 // We don't mess with the content type of a blob URL.
2964 if (IsBlobURI(mRequestURL
)) {
2968 // Since we expect XML data, set the type hint accordingly
2969 // if the channel doesn't know any content type.
2970 // This means that we always try to parse local files as XML
2971 // ignoring return value, as this is not critical. Use text/xml as fallback
2973 nsAutoCString contentType
;
2974 if (NS_FAILED(mChannel
->GetContentType(contentType
)) ||
2975 contentType
.IsEmpty() ||
2976 contentType
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
2977 mChannel
->SetContentType("text/xml"_ns
);
2981 void XMLHttpRequestMainThread::ResumeTimeout() {
2983 MOZ_ASSERT(NS_IsMainThread());
2984 MOZ_ASSERT(mFlagSynchronous
);
2986 if (mResumeTimeoutRunnable
) {
2987 DispatchToMainThread(mResumeTimeoutRunnable
.forget());
2988 mResumeTimeoutRunnable
= nullptr;
2992 void XMLHttpRequestMainThread::Send(
2994 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
>&
2997 DEBUG_WORKERREFS1(mRequestURL
);
2998 NOT_CALLABLE_IN_SYNC_SEND_RV
3000 if (!CanSend(aRv
)) {
3004 if (aData
.IsNull()) {
3005 SendInternal(nullptr, false, aRv
);
3009 if (aData
.Value().IsDocument()) {
3010 BodyExtractor
<Document
> body(&aData
.Value().GetAsDocument());
3011 SendInternal(&body
, true, aRv
);
3015 if (aData
.Value().IsBlob()) {
3016 BodyExtractor
<const Blob
> body(&aData
.Value().GetAsBlob());
3017 SendInternal(&body
, false, aRv
);
3021 if (aData
.Value().IsArrayBuffer()) {
3022 BodyExtractor
<const ArrayBuffer
> body(&aData
.Value().GetAsArrayBuffer());
3023 SendInternal(&body
, false, aRv
);
3027 if (aData
.Value().IsArrayBufferView()) {
3028 BodyExtractor
<const ArrayBufferView
> body(
3029 &aData
.Value().GetAsArrayBufferView());
3030 SendInternal(&body
, false, aRv
);
3034 if (aData
.Value().IsFormData()) {
3035 BodyExtractor
<const FormData
> body(&aData
.Value().GetAsFormData());
3036 SendInternal(&body
, false, aRv
);
3040 if (aData
.Value().IsURLSearchParams()) {
3041 BodyExtractor
<const URLSearchParams
> body(
3042 &aData
.Value().GetAsURLSearchParams());
3043 SendInternal(&body
, false, aRv
);
3047 if (aData
.Value().IsUSVString()) {
3048 BodyExtractor
<const nsAString
> body(&aData
.Value().GetAsUSVString());
3049 SendInternal(&body
, true, aRv
);
3054 nsresult
XMLHttpRequestMainThread::MaybeSilentSendFailure(nsresult aRv
) {
3055 // Per spec, silently fail on async request failures; throw for sync.
3056 if (mFlagSynchronous
) {
3057 mState
= XMLHttpRequest_Binding::DONE
;
3058 return NS_ERROR_DOM_NETWORK_ERR
;
3061 // Defer the actual sending of async events just in case listeners
3062 // are attached after the send() method is called.
3063 Unused
<< NS_WARN_IF(
3064 NS_FAILED(DispatchToMainThread(NewRunnableMethod
<ErrorProgressEventType
>(
3065 "dom::XMLHttpRequestMainThread::CloseRequestWithError", this,
3066 &XMLHttpRequestMainThread::CloseRequestWithError
, Events::error
))));
3070 bool XMLHttpRequestMainThread::CanSend(ErrorResult
& aRv
) {
3072 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
3077 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
3078 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
3084 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3088 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
3089 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
3096 void XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase
* aBody
,
3097 bool aBodyIsDocumentOrString
,
3100 MOZ_ASSERT(NS_IsMainThread());
3102 // We expect that CanSend has been called before we get here!
3103 // We cannot move the remaining two checks below there because
3104 // MaybeSilentSendFailure can cause unexpected side effects if called
3107 // If open() failed to create the channel, then throw a network error
3108 // as per spec. We really should create the channel here in send(), but
3109 // we have internal code relying on the channel being created in open().
3111 mErrorLoad
= ErrorType::eChannelOpen
;
3112 mErrorLoadDetail
= NS_ERROR_DOM_NETWORK_ERR
;
3113 mFlagSend
= true; // so CloseRequestWithError sets us to DONE.
3114 aRv
= MaybeSilentSendFailure(mErrorLoadDetail
);
3118 // non-GET requests aren't allowed for blob.
3119 if (IsBlobURI(mRequestURL
) && !mRequestMethod
.EqualsLiteral("GET")) {
3120 mErrorLoad
= ErrorType::eChannelOpen
;
3121 mErrorLoadDetail
= NS_ERROR_DOM_NETWORK_ERR
;
3122 mFlagSend
= true; // so CloseRequestWithError sets us to DONE.
3123 aRv
= MaybeSilentSendFailure(mErrorLoadDetail
);
3127 // XXX We should probably send a warning to the JS console
3128 // if there are no event listeners set and we are doing
3129 // an asynchronous call.
3131 mUploadTransferred
= 0;
3133 // By default we don't have any upload, so mark upload complete.
3134 mUploadComplete
= true;
3135 mErrorLoad
= ErrorType::eOK
;
3136 mErrorLoadDetail
= NS_OK
;
3138 nsCOMPtr
<nsIInputStream
> uploadStream
;
3139 nsAutoCString uploadContentType
;
3140 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
3141 if (aBody
&& httpChannel
&& !mRequestMethod
.EqualsLiteral("GET") &&
3142 !mRequestMethod
.EqualsLiteral("HEAD")) {
3143 nsAutoCString charset
;
3144 nsAutoCString defaultContentType
;
3146 aRv
= aBody
->GetAsStream(getter_AddRefs(uploadStream
), &size_u64
,
3147 defaultContentType
, charset
);
3152 // make sure it fits within js MAX_SAFE_INTEGER
3154 net::InScriptableRange(size_u64
) ? static_cast<int64_t>(size_u64
) : -1;
3157 // If author set no Content-Type, use the default from GetAsStream().
3158 mAuthorRequestHeaders
.Get("content-type", uploadContentType
);
3159 if (uploadContentType
.IsVoid()) {
3160 uploadContentType
= defaultContentType
;
3161 } else if (aBodyIsDocumentOrString
) {
3162 RefPtr
<CMimeType
> contentTypeRecord
=
3163 CMimeType::Parse(uploadContentType
);
3164 nsAutoCString charset
;
3165 if (contentTypeRecord
&&
3166 contentTypeRecord
->GetParameterValue(kLiteralString_charset
,
3168 !charset
.EqualsIgnoreCase("utf-8")) {
3169 contentTypeRecord
->SetParameterValue(kLiteralString_charset
,
3170 kLiteralString_UTF_8
);
3171 contentTypeRecord
->Serialize(uploadContentType
);
3173 } else if (!charset
.IsEmpty()) {
3174 // We don't want to set a charset for streams.
3175 // Replace all case-insensitive matches of the charset in the
3176 // content-type with the correct case.
3177 RequestHeaders::CharsetIterator
iter(uploadContentType
);
3178 while (iter
.Next()) {
3179 if (!iter
.Equals(charset
, nsCaseInsensitiveCStringComparator
)) {
3180 iter
.Replace(charset
);
3185 mUploadComplete
= false;
3191 // Check if we should enable cross-origin upload listeners.
3192 if (mUpload
&& mUpload
->HasListeners()) {
3193 mFlagHadUploadListenersOnSend
= true;
3196 mIsMappedArrayBuffer
= false;
3197 if (mResponseType
== XMLHttpRequestResponseType::Arraybuffer
&&
3198 StaticPrefs::dom_mapped_arraybuffer_enabled()) {
3199 nsCOMPtr
<nsIURI
> uri
;
3200 nsAutoCString scheme
;
3202 aRv
= mChannel
->GetURI(getter_AddRefs(uri
));
3203 if (!aRv
.Failed()) {
3204 uri
->GetScheme(scheme
);
3205 if (scheme
.LowerCaseEqualsLiteral("jar")) {
3206 mIsMappedArrayBuffer
= true;
3211 aRv
= InitiateFetch(uploadStream
.forget(), mUploadTotal
, uploadContentType
);
3216 // Start our timeout
3217 mRequestSentTime
= PR_Now();
3218 StartTimeoutTimer();
3220 mWaitingForOnStopRequest
= true;
3225 // If we're synchronous, spin an event loop here and wait
3226 RefPtr
<Document
> suspendedDoc
;
3227 if (mFlagSynchronous
) {
3228 auto scopeExit
= MakeScopeExit([&] {
3229 CancelSyncTimeoutTimer();
3231 ResumeEventDispatching();
3233 Maybe
<AutoSuppressEventHandling
> autoSuppress
;
3235 mFlagSyncLooping
= true;
3238 if (nsCOMPtr
<nsPIDOMWindowOuter
> topWindow
=
3239 GetOwner()->GetOuterWindow()->GetInProcessTop()) {
3240 if (nsCOMPtr
<nsPIDOMWindowInner
> topInner
=
3241 topWindow
->GetCurrentInnerWindow()) {
3242 suspendedDoc
= topWindow
->GetExtantDoc();
3243 autoSuppress
.emplace(topWindow
->GetBrowsingContext());
3244 topInner
->Suspend();
3245 mResumeTimeoutRunnable
= new nsResumeTimeoutsEvent(topInner
);
3250 SuspendEventDispatching();
3251 StopProgressEventTimer();
3253 SyncTimeoutType syncTimeoutType
= MaybeStartSyncTimeoutTimer();
3254 if (syncTimeoutType
== eErrorOrExpired
) {
3256 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
3260 nsAutoSyncOperation
sync(suspendedDoc
,
3261 SyncOperationBehavior::eSuspendInput
);
3262 if (!SpinEventLoopUntil("XMLHttpRequestMainThread::SendInternal"_ns
,
3263 [&]() { return !mFlagSyncLooping
; })) {
3264 aRv
.Throw(NS_ERROR_UNEXPECTED
);
3268 // Time expired... We should throw.
3269 if (syncTimeoutType
== eTimerStarted
&& !mSyncTimeoutTimer
) {
3270 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
3274 // Now that we've successfully opened the channel, we can change state. Note
3275 // that this needs to come after the AsyncOpen() and rv check, because this
3276 // can run script that would try to restart this request, and that could end
3277 // up doing our AsyncOpen on a null channel if the reentered AsyncOpen
3279 StopProgressEventTimer();
3281 // Upload phase beginning; start the progress event timer if necessary.
3282 if (mUpload
&& mUpload
->HasListenersFor(nsGkAtoms::onprogress
)) {
3283 StartProgressEventTimer();
3285 // Dispatch loadstart events
3286 DispatchProgressEvent(this, Events::loadstart
, 0, -1);
3287 if (mUpload
&& !mUploadComplete
) {
3288 DispatchProgressEvent(mUpload
, Events::loadstart
, 0, mUploadTotal
);
3293 aRv
= MaybeSilentSendFailure(NS_ERROR_DOM_NETWORK_ERR
);
3297 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
3298 void XMLHttpRequestMainThread::SetRequestHeader(const nsACString
& aName
,
3299 const nsACString
& aValue
,
3301 NOT_CALLABLE_IN_SYNC_SEND_RV
3304 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
3305 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
3311 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3316 nsAutoCString value
;
3317 NS_TrimHTTPWhitespace(aValue
, value
);
3320 if (!NS_IsValidHTTPToken(aName
) || !NS_IsReasonableHTTPHeaderValue(value
)) {
3321 aRv
.Throw(NS_ERROR_DOM_INVALID_HEADER_NAME
);
3326 bool isPrivilegedCaller
= IsSystemXHR();
3327 bool isForbiddenHeader
=
3328 nsContentUtils::IsForbiddenRequestHeader(aName
, aValue
);
3329 if (!isPrivilegedCaller
&& isForbiddenHeader
) {
3330 AutoTArray
<nsString
, 1> params
;
3331 CopyUTF8toUTF16(aName
, *params
.AppendElement());
3332 LogMessage("ForbiddenHeaderWarning", GetOwner(), params
);
3337 // Skipping for now, as normalizing the case of header names may not be
3338 // web-compatible. See bug 1285036.
3341 // Gecko-specific: invalid headers can be set by privileged
3342 // callers, but will not merge.
3343 if (isPrivilegedCaller
&& isForbiddenHeader
) {
3344 mAuthorRequestHeaders
.Set(aName
, value
);
3346 mAuthorRequestHeaders
.MergeOrSet(aName
, value
);
3350 void XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout
, ErrorResult
& aRv
) {
3351 NOT_CALLABLE_IN_SYNC_SEND_RV
3353 if (mFlagSynchronous
&& mState
!= XMLHttpRequest_Binding::UNSENT
&&
3354 HasOrHasHadOwner()) {
3355 /* Timeout is not supported for synchronous requests with an owning window,
3357 LogMessage("TimeoutSyncXHRWarning", GetOwner());
3358 aRv
.ThrowInvalidAccessError(
3359 "synchronous XMLHttpRequests do not support timeout and responseType");
3363 mTimeoutMilliseconds
= aTimeout
;
3364 if (mRequestSentTime
) {
3365 StartTimeoutTimer();
3369 nsIEventTarget
* XMLHttpRequestMainThread::GetTimerEventTarget() {
3370 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3371 return global
->SerialEventTarget();
3376 nsresult
XMLHttpRequestMainThread::DispatchToMainThread(
3377 already_AddRefed
<nsIRunnable
> aRunnable
) {
3379 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3380 return global
->Dispatch(std::move(aRunnable
));
3382 return NS_DispatchToMainThread(std::move(aRunnable
));
3385 void XMLHttpRequestMainThread::StartTimeoutTimer() {
3389 "StartTimeoutTimer mustn't be called before the request was sent!");
3390 if (mState
== XMLHttpRequest_Binding::DONE
) {
3395 CancelTimeoutTimer();
3397 if (!mTimeoutMilliseconds
) {
3401 if (!mTimeoutTimer
) {
3402 mTimeoutTimer
= NS_NewTimer(GetTimerEventTarget());
3405 (uint32_t)((PR_Now() - mRequestSentTime
) / PR_USEC_PER_MSEC
);
3406 mTimeoutTimer
->InitWithCallback(
3407 this, mTimeoutMilliseconds
> elapsed
? mTimeoutMilliseconds
- elapsed
: 0,
3408 nsITimer::TYPE_ONE_SHOT
);
3411 uint16_t XMLHttpRequestMainThread::ReadyState() const { return mState
; }
3413 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString
& aMimeType
,
3415 NOT_CALLABLE_IN_SYNC_SEND_RV
3417 if (mState
== XMLHttpRequest_Binding::LOADING
||
3418 mState
== XMLHttpRequest_Binding::DONE
) {
3419 aRv
.ThrowInvalidStateError(
3420 "Cannot call 'overrideMimeType()' on XMLHttpRequest after 'send()' "
3421 "(when its state is LOADING or DONE).");
3425 RefPtr
<MimeType
> parsed
= MimeType::Parse(aMimeType
);
3427 parsed
->Serialize(mOverrideMimeType
);
3429 mOverrideMimeType
.AssignLiteral(APPLICATION_OCTET_STREAM
);
3433 bool XMLHttpRequestMainThread::MozBackgroundRequest() const {
3434 return mFlagBackgroundRequest
;
3437 void XMLHttpRequestMainThread::SetMozBackgroundRequestExternal(
3438 bool aMozBackgroundRequest
, ErrorResult
& aRv
) {
3439 if (!IsSystemXHR()) {
3440 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
3444 if (mState
!= XMLHttpRequest_Binding::UNSENT
) {
3445 // Can't change this while we're in the middle of something.
3446 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3450 mFlagBackgroundRequest
= aMozBackgroundRequest
;
3453 void XMLHttpRequestMainThread::SetMozBackgroundRequest(
3454 bool aMozBackgroundRequest
, ErrorResult
& aRv
) {
3455 // No errors for this webIDL method on main-thread.
3456 SetMozBackgroundRequestExternal(aMozBackgroundRequest
, IgnoreErrors());
3459 void XMLHttpRequestMainThread::SetOriginStack(
3460 UniquePtr
<SerializedStackHolder
> aOriginStack
) {
3461 mOriginStack
= std::move(aOriginStack
);
3464 void XMLHttpRequestMainThread::SetSource(
3465 UniquePtr
<ProfileChunkedBuffer
> aSource
) {
3469 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
3472 httpChannel
->SetSource(std::move(aSource
));
3476 bool XMLHttpRequestMainThread::WithCredentials() const {
3477 return mFlagACwithCredentials
;
3480 void XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials
,
3482 NOT_CALLABLE_IN_SYNC_SEND_RV
3484 // Return error if we're already processing a request. Note that we can't use
3485 // ReadyState() here, because it can't differentiate between "opened" and
3486 // "sent", so we use mState directly.
3488 if ((mState
!= XMLHttpRequest_Binding::UNSENT
&&
3489 mState
!= XMLHttpRequest_Binding::OPENED
) ||
3490 mFlagSend
|| mIsAnon
) {
3491 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3495 mFlagACwithCredentials
= aWithCredentials
;
3498 nsresult
XMLHttpRequestMainThread::ChangeState(uint16_t aState
,
3501 nsresult rv
= NS_OK
;
3503 if (aState
!= XMLHttpRequest_Binding::HEADERS_RECEIVED
&&
3504 aState
!= XMLHttpRequest_Binding::LOADING
) {
3505 StopProgressEventTimer();
3509 (!mFlagSynchronous
|| aState
== XMLHttpRequest_Binding::OPENED
||
3510 aState
== XMLHttpRequest_Binding::DONE
)) {
3511 rv
= FireReadystatechangeEvent();
3517 /////////////////////////////////////////////////////
3518 // nsIChannelEventSink methods:
3521 XMLHttpRequestMainThread::AsyncOnChannelRedirect(
3522 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
3523 nsIAsyncVerifyRedirectCallback
* callback
) {
3525 MOZ_ASSERT(aNewChannel
, "Redirect without a channel?");
3527 // Prepare to receive callback
3528 mRedirectCallback
= callback
;
3529 mNewRedirectChannel
= aNewChannel
;
3531 if (mChannelEventSink
) {
3532 nsCOMPtr
<nsIAsyncVerifyRedirectCallback
> fwd
= EnsureXPCOMifier();
3534 nsresult rv
= mChannelEventSink
->AsyncOnChannelRedirect(
3535 aOldChannel
, aNewChannel
, aFlags
, fwd
);
3536 if (NS_FAILED(rv
)) {
3537 mRedirectCallback
= nullptr;
3538 mNewRedirectChannel
= nullptr;
3543 // we need to strip Authentication headers for cross-origin requests
3544 // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
3546 StaticPrefs::network_fetch_redirect_stripAuthHeader() &&
3547 NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel
, aNewChannel
, aFlags
);
3549 OnRedirectVerifyCallback(NS_OK
, stripAuth
);
3554 nsresult
XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result
,
3557 NS_ASSERTION(mRedirectCallback
, "mRedirectCallback not set in callback");
3558 NS_ASSERTION(mNewRedirectChannel
, "mNewRedirectChannel not set in callback");
3560 if (NS_SUCCEEDED(result
)) {
3561 bool rewriteToGET
= false;
3562 nsCOMPtr
<nsIHttpChannel
> oldHttpChannel
= GetCurrentHttpChannel();
3564 Unused
<< oldHttpChannel
->ShouldStripRequestBodyHeader(mRequestMethod
,
3567 mChannel
= mNewRedirectChannel
;
3569 nsCOMPtr
<nsIHttpChannel
> newHttpChannel(do_QueryInterface(mChannel
));
3570 if (newHttpChannel
) {
3571 // Ensure all original headers are duplicated for the new channel (bug
3573 mAuthorRequestHeaders
.ApplyToChannel(newHttpChannel
, rewriteToGET
,
3577 mErrorLoad
= ErrorType::eRedirect
;
3578 mErrorLoadDetail
= result
;
3581 mNewRedirectChannel
= nullptr;
3583 mRedirectCallback
->OnRedirectVerifyCallback(result
);
3584 mRedirectCallback
= nullptr;
3586 // It's important that we return success here. If we return the result code
3587 // that we were passed, JavaScript callers who cancel the redirect will wind
3588 // up throwing an exception in the process.
3592 /////////////////////////////////////////////////////
3593 // nsIProgressEventSink methods:
3597 XMLHttpRequestMainThread::OnProgress(nsIRequest
* aRequest
, int64_t aProgress
,
3598 int64_t aProgressMax
) {
3600 // When uploading, OnProgress reports also headers in aProgress and
3601 // aProgressMax. So, try to remove the headers, if possible.
3602 bool lengthComputable
= (aProgressMax
!= -1);
3603 if (InUploadPhase()) {
3604 int64_t loaded
= aProgress
;
3605 if (lengthComputable
) {
3606 int64_t headerSize
= aProgressMax
- mUploadTotal
;
3607 loaded
-= headerSize
;
3609 mUploadTransferred
= loaded
;
3610 mProgressSinceLastProgressEvent
= true;
3612 if (!mFlagSynchronous
&& !mProgressTimerIsActive
) {
3613 StartProgressEventTimer();
3616 mLoadTotal
= aProgressMax
;
3617 mLoadTransferred
= aProgress
;
3618 // OnDataAvailable() handles mProgressSinceLastProgressEvent
3619 // for the download phase.
3622 if (mProgressEventSink
) {
3623 mProgressEventSink
->OnProgress(aRequest
, aProgress
, aProgressMax
);
3630 XMLHttpRequestMainThread::OnStatus(nsIRequest
* aRequest
, nsresult aStatus
,
3631 const char16_t
* aStatusArg
) {
3633 if (mProgressEventSink
) {
3634 mProgressEventSink
->OnStatus(aRequest
, aStatus
, aStatusArg
);
3640 bool XMLHttpRequestMainThread::AllowUploadProgress() {
3641 return !IsCrossSiteCORSRequest() || mFlagHadUploadListenersOnSend
;
3644 /////////////////////////////////////////////////////
3645 // nsIInterfaceRequestor methods:
3648 XMLHttpRequestMainThread::GetInterface(const nsIID
& aIID
, void** aResult
) {
3651 // Make sure to return ourselves for the channel event sink interface and
3652 // progress event sink interface, no matter what. We can forward these to
3653 // mNotificationCallbacks if it wants to get notifications for them. But we
3654 // need to see these notifications for proper functioning.
3655 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
3656 mChannelEventSink
= do_GetInterface(mNotificationCallbacks
);
3657 *aResult
= static_cast<nsIChannelEventSink
*>(EnsureXPCOMifier().take());
3659 } else if (aIID
.Equals(NS_GET_IID(nsIProgressEventSink
))) {
3660 mProgressEventSink
= do_GetInterface(mNotificationCallbacks
);
3661 *aResult
= static_cast<nsIProgressEventSink
*>(EnsureXPCOMifier().take());
3665 // Now give mNotificationCallbacks (if non-null) a chance to return the
3666 // desired interface.
3667 if (mNotificationCallbacks
) {
3668 rv
= mNotificationCallbacks
->GetInterface(aIID
, aResult
);
3669 if (NS_SUCCEEDED(rv
)) {
3670 NS_ASSERTION(*aResult
, "Lying nsIInterfaceRequestor implementation!");
3675 if (!mFlagBackgroundRequest
&& (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
3676 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
)))) {
3677 nsCOMPtr
<nsIPromptFactory
> wwatch
=
3678 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
3679 NS_ENSURE_SUCCESS(rv
, rv
);
3681 // Get the an auth prompter for our window so that the parenting
3682 // of the dialogs works as it should when using tabs.
3683 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
3685 window
= GetOwner()->GetOuterWindow();
3687 return wwatch
->GetPrompt(window
, aIID
, reinterpret_cast<void**>(aResult
));
3690 // Now check for the various XHR non-DOM interfaces, except
3691 // nsIProgressEventSink and nsIChannelEventSink which we already
3693 if (aIID
.Equals(NS_GET_IID(nsIStreamListener
))) {
3694 *aResult
= static_cast<nsIStreamListener
*>(EnsureXPCOMifier().take());
3697 if (aIID
.Equals(NS_GET_IID(nsIRequestObserver
))) {
3698 *aResult
= static_cast<nsIRequestObserver
*>(EnsureXPCOMifier().take());
3701 if (aIID
.Equals(NS_GET_IID(nsITimerCallback
))) {
3702 *aResult
= static_cast<nsITimerCallback
*>(EnsureXPCOMifier().take());
3706 return QueryInterface(aIID
, aResult
);
3709 void XMLHttpRequestMainThread::GetInterface(
3710 JSContext
* aCx
, JS::Handle
<JS::Value
> aIID
,
3711 JS::MutableHandle
<JS::Value
> aRetval
, ErrorResult
& aRv
) {
3712 dom::GetInterface(aCx
, this, aIID
, aRetval
, aRv
);
3715 XMLHttpRequestUpload
* XMLHttpRequestMainThread::GetUpload(ErrorResult
& aRv
) {
3717 mUpload
= new XMLHttpRequestUpload(this);
3722 bool XMLHttpRequestMainThread::MozAnon() const { return mIsAnon
; }
3724 bool XMLHttpRequestMainThread::MozSystem() const { return IsSystemXHR(); }
3726 void XMLHttpRequestMainThread::HandleTimeoutCallback() {
3728 if (mState
== XMLHttpRequest_Binding::DONE
) {
3729 MOZ_ASSERT_UNREACHABLE(
3730 "XMLHttpRequestMainThread::HandleTimeoutCallback "
3731 "with completed request");
3736 mFlagTimedOut
= true;
3737 CloseRequestWithError(Events::timeout
);
3740 void XMLHttpRequestMainThread::CancelTimeoutTimer() {
3742 if (mTimeoutTimer
) {
3743 mTimeoutTimer
->Cancel();
3744 mTimeoutTimer
= nullptr;
3749 XMLHttpRequestMainThread::Notify(nsITimer
* aTimer
) {
3751 if (mProgressNotifier
== aTimer
) {
3752 HandleProgressTimerCallback();
3756 if (mTimeoutTimer
== aTimer
) {
3757 HandleTimeoutCallback();
3761 if (mSyncTimeoutTimer
== aTimer
) {
3762 HandleSyncTimeoutTimer();
3766 // Just in case some JS user wants to QI to nsITimerCallback and play with
3768 NS_WARNING("Unexpected timer!");
3769 return NS_ERROR_INVALID_POINTER
;
3772 void XMLHttpRequestMainThread::HandleProgressTimerCallback() {
3774 // Don't fire the progress event if mLoadTotal is 0, see XHR spec step 6.1
3775 if (!mLoadTotal
&& mLoadTransferred
) {
3779 mProgressTimerIsActive
= false;
3781 if (!mProgressSinceLastProgressEvent
|| mErrorLoad
!= ErrorType::eOK
) {
3785 if (InUploadPhase()) {
3786 if (mUpload
&& !mUploadComplete
&& mFlagHadUploadListenersOnSend
) {
3787 DispatchProgressEvent(mUpload
, Events::progress
, mUploadTransferred
,
3791 FireReadystatechangeEvent();
3792 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
, mLoadTotal
);
3795 mProgressSinceLastProgressEvent
= false;
3797 StartProgressEventTimer();
3800 void XMLHttpRequestMainThread::StopProgressEventTimer() {
3801 if (mProgressNotifier
) {
3802 mProgressTimerIsActive
= false;
3803 mProgressNotifier
->Cancel();
3807 void XMLHttpRequestMainThread::StartProgressEventTimer() {
3808 if (!mProgressNotifier
) {
3809 mProgressNotifier
= NS_NewTimer(GetTimerEventTarget());
3811 if (mProgressNotifier
) {
3812 mProgressTimerIsActive
= true;
3813 mProgressNotifier
->Cancel();
3814 mProgressNotifier
->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL
,
3815 nsITimer::TYPE_ONE_SHOT
);
3819 XMLHttpRequestMainThread::SyncTimeoutType
3820 XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer() {
3821 MOZ_ASSERT(mFlagSynchronous
);
3823 Document
* doc
= GetDocumentIfCurrent();
3824 if (!doc
|| !doc
->GetPageUnloadingEventTimeStamp()) {
3825 return eNoTimerNeeded
;
3828 // If we are in a beforeunload or a unload event, we must force a timeout.
3830 (TimeStamp::NowLoRes() - doc
->GetPageUnloadingEventTimeStamp());
3831 if (diff
.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING
) {
3832 return eErrorOrExpired
;
3835 mSyncTimeoutTimer
= NS_NewTimer(GetTimerEventTarget());
3836 if (!mSyncTimeoutTimer
) {
3837 return eErrorOrExpired
;
3840 uint32_t timeout
= MAX_SYNC_TIMEOUT_WHEN_UNLOADING
- diff
.ToMilliseconds();
3841 nsresult rv
= mSyncTimeoutTimer
->InitWithCallback(this, timeout
,
3842 nsITimer::TYPE_ONE_SHOT
);
3843 return NS_FAILED(rv
) ? eErrorOrExpired
: eTimerStarted
;
3846 void XMLHttpRequestMainThread::HandleSyncTimeoutTimer() {
3847 MOZ_ASSERT(mSyncTimeoutTimer
);
3848 MOZ_ASSERT(mFlagSyncLooping
);
3850 CancelSyncTimeoutTimer();
3852 mErrorLoadDetail
= NS_ERROR_DOM_TIMEOUT_ERR
;
3855 void XMLHttpRequestMainThread::CancelSyncTimeoutTimer() {
3856 if (mSyncTimeoutTimer
) {
3857 mSyncTimeoutTimer
->Cancel();
3858 mSyncTimeoutTimer
= nullptr;
3862 already_AddRefed
<nsXMLHttpRequestXPCOMifier
>
3863 XMLHttpRequestMainThread::EnsureXPCOMifier() {
3865 mXPCOMifier
= new nsXMLHttpRequestXPCOMifier(this);
3867 RefPtr
<nsXMLHttpRequestXPCOMifier
> newRef(mXPCOMifier
);
3868 return newRef
.forget();
3871 bool XMLHttpRequestMainThread::ShouldBlockAuthPrompt() {
3872 // Verify that it's ok to prompt for credentials here, per spec
3873 // http://xhr.spec.whatwg.org/#the-send%28%29-method
3875 if (mAuthorRequestHeaders
.Has("authorization")) {
3879 nsCOMPtr
<nsIURI
> uri
;
3880 nsresult rv
= mChannel
->GetURI(getter_AddRefs(uri
));
3881 if (NS_WARN_IF(NS_FAILED(rv
))) {
3885 // Also skip if a username and/or password is provided in the URI.
3887 return NS_SUCCEEDED(uri
->GetHasUserPass(&hasUserPass
)) && hasUserPass
;
3890 void XMLHttpRequestMainThread::TruncateResponseText() {
3891 mResponseText
.Truncate();
3892 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
3895 NS_IMPL_ISUPPORTS(XMLHttpRequestMainThread::nsHeaderVisitor
,
3896 nsIHttpHeaderVisitor
)
3898 NS_IMETHODIMP
XMLHttpRequestMainThread::nsHeaderVisitor::VisitHeader(
3899 const nsACString
& header
, const nsACString
& value
) {
3900 if (mXHR
.IsSafeHeader(header
, mHttpChannel
)) {
3901 nsAutoCString
lowerHeader(header
);
3902 ToLowerCase(lowerHeader
);
3903 if (!mHeaderList
.InsertElementSorted(HeaderEntry(lowerHeader
, value
),
3905 return NS_ERROR_OUT_OF_MEMORY
;
3911 XMLHttpRequestMainThread::nsHeaderVisitor::nsHeaderVisitor(
3912 const XMLHttpRequestMainThread
& aXMLHttpRequest
,
3913 NotNull
<nsIHttpChannel
*> aHttpChannel
)
3914 : mXHR(aXMLHttpRequest
), mHttpChannel(aHttpChannel
) {}
3916 XMLHttpRequestMainThread::nsHeaderVisitor::~nsHeaderVisitor() = default;
3918 void XMLHttpRequestMainThread::MaybeCreateBlobStorage() {
3920 MOZ_ASSERT(mResponseType
== XMLHttpRequestResponseType::Blob
);
3926 MutableBlobStorage::MutableBlobStorageType storageType
=
3927 BasePrincipal::Cast(mPrincipal
)->PrivateBrowsingId() == 0
3928 ? MutableBlobStorage::eCouldBeInTemporaryFile
3929 : MutableBlobStorage::eOnlyInMemory
;
3931 nsCOMPtr
<nsIEventTarget
> eventTarget
;
3932 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3933 eventTarget
= global
->SerialEventTarget();
3936 mBlobStorage
= new MutableBlobStorage(storageType
, eventTarget
);
3939 void XMLHttpRequestMainThread::BlobStoreCompleted(
3940 MutableBlobStorage
* aBlobStorage
, BlobImpl
* aBlobImpl
, nsresult aRv
) {
3942 // Ok, the state is changed...
3943 if (mBlobStorage
!= aBlobStorage
|| NS_FAILED(aRv
)) {
3947 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::DONE
);
3949 mResponseBlobImpl
= aBlobImpl
;
3950 mBlobStorage
= nullptr;
3952 ChangeStateToDone(mFlagSyncLooping
);
3956 XMLHttpRequestMainThread::GetName(nsACString
& aName
) {
3957 aName
.AssignLiteral("XMLHttpRequest");
3961 // nsXMLHttpRequestXPCOMifier implementation
3962 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier
)
3963 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
3964 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
3965 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
3966 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback
)
3967 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
3968 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
3969 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
3970 NS_INTERFACE_MAP_ENTRY(nsINamed
)
3971 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIStreamListener
)
3972 NS_INTERFACE_MAP_END
3974 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier
)
3975 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier
)
3977 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
3978 // inheritance from nsISupports.
3979 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier
)
3981 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier
)
3983 tmp
->mXHR
->mXPCOMifier
= nullptr;
3985 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR
)
3986 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3988 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier
)
3989 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR
)
3990 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3993 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID
& aIID
, void** aResult
) {
3994 // Return ourselves for the things we implement (except
3995 // nsIInterfaceRequestor) and the XHR for the rest.
3996 if (!aIID
.Equals(NS_GET_IID(nsIInterfaceRequestor
))) {
3997 nsresult rv
= QueryInterface(aIID
, aResult
);
3998 if (NS_SUCCEEDED(rv
)) {
4003 return mXHR
->GetInterface(aIID
, aResult
);
4006 ArrayBufferBuilder::ArrayBufferBuilder()
4007 : mMutex("ArrayBufferBuilder"),
4014 ArrayBufferBuilder::~ArrayBufferBuilder() {
4016 JS_free(nullptr, mDataPtr
);
4020 JS::ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
4025 mCapacity
= mLength
= 0;
4028 bool ArrayBufferBuilder::SetCapacity(uint32_t aNewCap
) {
4029 MutexAutoLock
lock(mMutex
);
4030 return SetCapacityInternal(aNewCap
, lock
);
4033 bool ArrayBufferBuilder::SetCapacityInternal(
4034 uint32_t aNewCap
, const MutexAutoLock
& aProofOfLock
) {
4035 MOZ_ASSERT(!mMapPtr
);
4036 MOZ_ASSERT(!mNeutered
);
4038 // To ensure that realloc won't free mDataPtr, use a size of 1
4040 uint8_t* newdata
= (uint8_t*)js_realloc(mDataPtr
, aNewCap
? aNewCap
: 1);
4046 if (aNewCap
> mCapacity
) {
4047 memset(newdata
+ mCapacity
, 0, aNewCap
- mCapacity
);
4051 mCapacity
= aNewCap
;
4052 if (mLength
> aNewCap
) {
4059 bool ArrayBufferBuilder::Append(const uint8_t* aNewData
, uint32_t aDataLen
,
4060 uint32_t aMaxGrowth
) {
4061 MutexAutoLock
lock(mMutex
);
4062 MOZ_ASSERT(!mMapPtr
);
4063 MOZ_ASSERT(!mNeutered
);
4065 CheckedUint32 neededCapacity
= mLength
;
4066 neededCapacity
+= aDataLen
;
4067 if (!neededCapacity
.isValid()) {
4070 if (mLength
+ aDataLen
> mCapacity
) {
4071 CheckedUint32 newcap
= mCapacity
;
4072 // Double while under aMaxGrowth or if not specified.
4073 if (!aMaxGrowth
|| mCapacity
< aMaxGrowth
) {
4076 newcap
+= aMaxGrowth
;
4079 if (!newcap
.isValid()) {
4083 // But make sure there's always enough to satisfy our request.
4084 if (newcap
.value() < neededCapacity
.value()) {
4085 newcap
= neededCapacity
;
4088 if (!SetCapacityInternal(newcap
.value(), lock
)) {
4093 // Assert that the region isn't overlapping so we can memcpy.
4095 !AreOverlappingRegions(aNewData
, aDataLen
, mDataPtr
+ mLength
, aDataLen
));
4097 memcpy(mDataPtr
+ mLength
, aNewData
, aDataLen
);
4098 mLength
+= aDataLen
;
4103 uint32_t ArrayBufferBuilder::Length() {
4104 MutexAutoLock
lock(mMutex
);
4105 MOZ_ASSERT(!mNeutered
);
4109 uint32_t ArrayBufferBuilder::Capacity() {
4110 MutexAutoLock
lock(mMutex
);
4111 MOZ_ASSERT(!mNeutered
);
4115 JSObject
* ArrayBufferBuilder::TakeArrayBuffer(JSContext
* aCx
) {
4116 MutexAutoLock
lock(mMutex
);
4117 MOZ_DIAGNOSTIC_ASSERT(!mNeutered
);
4120 JSObject
* obj
= JS::NewMappedArrayBufferWithContents(aCx
, mLength
, mMapPtr
);
4122 JS::ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
4128 // The memory-mapped contents will be released when the ArrayBuffer becomes
4129 // detached or is GC'd.
4133 // we need to check for mLength == 0, because nothing may have been
4135 if (mCapacity
> mLength
|| mLength
== 0) {
4136 if (!SetCapacityInternal(mLength
, lock
)) {
4141 // |mDataPtr| will be deallocated in ArrayBufferBuilder's destructor when this
4142 // ArrayBuffer allocation failed.
4143 JSObject
* obj
= JS::NewArrayBufferWithContents(
4144 aCx
, mLength
, mDataPtr
,
4145 JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory
);
4151 mCapacity
= mLength
= 0;
4157 nsresult
ArrayBufferBuilder::MapToFileInPackage(const nsCString
& aFile
,
4158 nsIFile
* aJarFile
) {
4159 MutexAutoLock
lock(mMutex
);
4160 MOZ_ASSERT(NS_IsMainThread());
4161 MOZ_ASSERT(!mNeutered
);
4165 // Open Jar file to get related attributes of target file.
4166 RefPtr
<nsZipArchive
> zip
= nsZipArchive::OpenArchive(aJarFile
);
4168 return NS_ERROR_FAILURE
;
4170 nsZipItem
* zipItem
= zip
->GetItem(aFile
.get());
4172 return NS_ERROR_FILE_NOT_FOUND
;
4175 // If file was added to the package as stored(uncompressed), map to the
4176 // offset of file in zip package.
4177 if (!zipItem
->Compression()) {
4178 uint32_t offset
= zip
->GetDataOffset(zipItem
);
4179 uint32_t size
= zipItem
->RealSize();
4180 mozilla::AutoFDClose pr_fd
;
4181 rv
= aJarFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, getter_Transfers(pr_fd
));
4182 if (NS_FAILED(rv
)) {
4185 mMapPtr
= JS::CreateMappedArrayBufferContents(
4186 PR_FileDesc2NativeHandle(pr_fd
.get()), offset
, size
);
4192 return NS_ERROR_FAILURE
;
4196 bool ArrayBufferBuilder::AreOverlappingRegions(const uint8_t* aStart1
,
4198 const uint8_t* aStart2
,
4199 uint32_t aLength2
) {
4200 const uint8_t* end1
= aStart1
+ aLength1
;
4201 const uint8_t* end2
= aStart2
+ aLength2
;
4203 const uint8_t* max_start
= aStart1
> aStart2
? aStart1
: aStart2
;
4204 const uint8_t* min_end
= end1
< end2
? end1
: end2
;
4206 return max_start
< min_end
;
4209 RequestHeaders::RequestHeader
* RequestHeaders::Find(const nsACString
& aName
) {
4210 for (RequestHeaders::RequestHeader
& header
: mHeaders
) {
4211 if (header
.mName
.Equals(aName
, nsCaseInsensitiveCStringComparator
)) {
4218 bool RequestHeaders::IsEmpty() const { return mHeaders
.IsEmpty(); }
4220 bool RequestHeaders::Has(const char* aName
) {
4221 return Has(nsDependentCString(aName
));
4224 bool RequestHeaders::Has(const nsACString
& aName
) { return !!Find(aName
); }
4226 void RequestHeaders::Get(const char* aName
, nsACString
& aValue
) {
4227 Get(nsDependentCString(aName
), aValue
);
4230 void RequestHeaders::Get(const nsACString
& aName
, nsACString
& aValue
) {
4231 RequestHeader
* header
= Find(aName
);
4233 aValue
= header
->mValue
;
4235 aValue
.SetIsVoid(true);
4239 void RequestHeaders::Set(const char* aName
, const nsACString
& aValue
) {
4240 Set(nsDependentCString(aName
), aValue
);
4243 void RequestHeaders::Set(const nsACString
& aName
, const nsACString
& aValue
) {
4244 RequestHeader
* header
= Find(aName
);
4246 header
->mValue
.Assign(aValue
);
4248 RequestHeader newHeader
= {nsCString(aName
), nsCString(aValue
)};
4249 mHeaders
.AppendElement(newHeader
);
4253 void RequestHeaders::MergeOrSet(const char* aName
, const nsACString
& aValue
) {
4254 MergeOrSet(nsDependentCString(aName
), aValue
);
4257 void RequestHeaders::MergeOrSet(const nsACString
& aName
,
4258 const nsACString
& aValue
) {
4259 RequestHeader
* header
= Find(aName
);
4261 header
->mValue
.AppendLiteral(", ");
4262 header
->mValue
.Append(aValue
);
4264 RequestHeader newHeader
= {nsCString(aName
), nsCString(aValue
)};
4265 mHeaders
.AppendElement(newHeader
);
4269 void RequestHeaders::Clear() { mHeaders
.Clear(); }
4271 void RequestHeaders::ApplyToChannel(nsIHttpChannel
* aChannel
,
4272 bool aStripRequestBodyHeader
,
4273 bool aStripAuthHeader
) const {
4274 for (const RequestHeader
& header
: mHeaders
) {
4275 if (aStripRequestBodyHeader
&&
4276 (header
.mName
.LowerCaseEqualsASCII("content-type") ||
4277 header
.mName
.LowerCaseEqualsASCII("content-encoding") ||
4278 header
.mName
.LowerCaseEqualsASCII("content-language") ||
4279 header
.mName
.LowerCaseEqualsASCII("content-location"))) {
4283 if (aStripAuthHeader
&&
4284 header
.mName
.LowerCaseEqualsASCII("authorization")) {
4288 // Update referrerInfo to override referrer header in system privileged.
4289 if (header
.mName
.LowerCaseEqualsASCII("referer")) {
4290 DebugOnly
<nsresult
> rv
= aChannel
->SetNewReferrerInfo(
4291 header
.mValue
, nsIReferrerInfo::ReferrerPolicyIDL::UNSAFE_URL
, true);
4292 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4294 if (header
.mValue
.IsEmpty()) {
4295 DebugOnly
<nsresult
> rv
= aChannel
->SetEmptyRequestHeader(header
.mName
);
4296 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4298 DebugOnly
<nsresult
> rv
=
4299 aChannel
->SetRequestHeader(header
.mName
, header
.mValue
, false);
4300 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4305 void RequestHeaders::GetCORSUnsafeHeaders(nsTArray
<nsCString
>& aArray
) const {
4306 for (const RequestHeader
& header
: mHeaders
) {
4307 if (!nsContentUtils::IsCORSSafelistedRequestHeader(header
.mName
,
4309 aArray
.AppendElement(header
.mName
);
4314 RequestHeaders::CharsetIterator::CharsetIterator(nsACString
& aSource
)
4318 mCutoff(aSource
.Length()),
4321 bool RequestHeaders::CharsetIterator::Equals(
4322 const nsACString
& aOther
, const nsCStringComparator
& aCmp
) const {
4324 return Substring(mSource
, mCurPos
, mCurLen
).Equals(aOther
, aCmp
);
4330 void RequestHeaders::CharsetIterator::Replace(const nsACString
& aReplacement
) {
4332 mSource
.Replace(mCurPos
, mCurLen
, aReplacement
);
4333 mCurLen
= aReplacement
.Length();
4337 bool RequestHeaders::CharsetIterator::Next() {
4339 nsAutoCString charset
;
4341 // Look for another charset declaration in the string, limiting the
4342 // search to only the characters before the parts we've already searched
4343 // (before mCutoff), so that we don't find the same charset twice.
4344 NS_ExtractCharsetFromContentType(Substring(mSource
, 0, mCutoff
), charset
,
4345 &mValid
, &start
, &end
);
4351 // Everything after the = sign is the part of the charset we want.
4352 mCurPos
= mSource
.FindChar('=', start
) + 1;
4353 mCurLen
= end
- mCurPos
;
4355 // Special case: the extracted charset is quoted with single quotes.
4356 // For the purpose of preserving what was set we want to handle them
4357 // as delimiters (although they aren't really).
4358 if (charset
.Length() >= 2 && charset
.First() == '\'' &&
4359 charset
.Last() == '\'') {
4369 } // namespace mozilla::dom