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 # define DEBUG_WORKERREFS \
232 DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)(*this, __func__)
234 # define DEBUG_WORKERREFS1(x) \
235 DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)( \
236 *this, STREAM_STRING(__func__ << ": " << x)) // NOLINT
239 # define DEBUG_WORKERREFS void()
240 # define DEBUG_WORKERREFS1(x) void()
243 bool XMLHttpRequestMainThread::sDontWarnAboutSyncXHR
= false;
245 XMLHttpRequestMainThread::XMLHttpRequestMainThread(
246 nsIGlobalObject
* aGlobalObject
)
247 : XMLHttpRequest(aGlobalObject
),
249 mTSWorkerRefMutex("Debug WorkerRefs"),
251 mResponseBodyDecodedPos(0),
252 mResponseType(XMLHttpRequestResponseType::_empty
),
253 mState(XMLHttpRequest_Binding::UNSENT
),
254 mFlagSynchronous(false),
256 mFlagParseBody(false),
257 mFlagSyncLooping(false),
258 mFlagBackgroundRequest(false),
259 mFlagHadUploadListenersOnSend(false),
260 mFlagACwithCredentials(false),
261 mFlagTimedOut(false),
264 mUploadTransferred(0),
266 mUploadComplete(true),
267 mProgressSinceLastProgressEvent(false),
269 mTimeoutMilliseconds(0),
270 mErrorLoad(ErrorType::eOK
),
271 mErrorLoadDetail(NS_OK
),
272 mErrorParsingXML(false),
273 mWaitingForOnStopRequest(false),
274 mProgressTimerIsActive(false),
276 mWarnAboutSyncHtml(false),
281 mResultJSON(JS::UndefinedValue()),
282 mArrayBufferBuilder(new ArrayBufferBuilder()),
283 mResultArrayBuffer(nullptr),
284 mIsMappedArrayBuffer(false),
285 mXPCOMifier(nullptr),
286 mEventDispatchingSuspended(false),
288 mDelayedDoneNotifier(nullptr) {
290 mozilla::HoldJSObjects(this);
293 XMLHttpRequestMainThread::~XMLHttpRequestMainThread() {
296 !mDelayedDoneNotifier
,
297 "How can we have mDelayedDoneNotifier, which owns us, in destructor?");
301 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
302 mState
== XMLHttpRequest_Binding::LOADING
) {
306 if (mParseEndListener
) {
307 mParseEndListener
->SetIsStale();
308 mParseEndListener
= nullptr;
311 MOZ_ASSERT(!mFlagSyncLooping
, "we rather crash than hang");
312 mFlagSyncLooping
= false;
314 mozilla::DropJSObjects(this);
317 void XMLHttpRequestMainThread::Construct(
318 nsIPrincipal
* aPrincipal
, nsICookieJarSettings
* aCookieJarSettings
,
319 bool aForWorker
, nsIURI
* aBaseURI
/* = nullptr */,
320 nsILoadGroup
* aLoadGroup
/* = nullptr */,
321 PerformanceStorage
* aPerformanceStorage
/* = nullptr */,
322 nsICSPEventListener
* aCSPEventListener
/* = nullptr */) {
324 MOZ_ASSERT(aPrincipal
);
325 mPrincipal
= aPrincipal
;
327 mLoadGroup
= aLoadGroup
;
328 mCookieJarSettings
= aCookieJarSettings
;
329 mForWorker
= aForWorker
;
330 mPerformanceStorage
= aPerformanceStorage
;
331 mCSPEventListener
= aCSPEventListener
;
334 void XMLHttpRequestMainThread::InitParameters(bool aAnon
, bool aSystem
) {
336 if (!aAnon
&& !aSystem
) {
340 // Check for permissions.
341 // Chrome is always allowed access, so do the permission check only
342 // for non-chrome pages.
343 if (!IsSystemXHR() && aSystem
) {
344 nsIGlobalObject
* global
= GetOwnerGlobal();
345 if (NS_WARN_IF(!global
)) {
346 SetParameters(aAnon
, false);
350 nsIPrincipal
* principal
= global
->PrincipalOrNull();
351 if (NS_WARN_IF(!principal
)) {
352 SetParameters(aAnon
, false);
356 nsCOMPtr
<nsIPermissionManager
> permMgr
=
357 components::PermissionManager::Service();
358 if (NS_WARN_IF(!permMgr
)) {
359 SetParameters(aAnon
, false);
364 nsresult rv
= permMgr
->TestPermissionFromPrincipal(
365 principal
, "systemXHR"_ns
, &permission
);
366 if (NS_FAILED(rv
) || permission
!= nsIPermissionManager::ALLOW_ACTION
) {
367 SetParameters(aAnon
, false);
372 SetParameters(aAnon
, aSystem
);
375 void XMLHttpRequestMainThread::SetClientInfoAndController(
376 const ClientInfo
& aClientInfo
,
377 const Maybe
<ServiceWorkerDescriptor
>& aController
) {
378 mClientInfo
.emplace(aClientInfo
);
379 mController
= aController
;
382 void XMLHttpRequestMainThread::ResetResponse() {
383 mResponseXML
= nullptr;
384 mResponseBody
.Truncate();
385 TruncateResponseText();
386 mResponseBlobImpl
= nullptr;
387 mResponseBlob
= nullptr;
388 mBlobStorage
= nullptr;
389 mResultArrayBuffer
= nullptr;
390 mArrayBufferBuilder
= new ArrayBufferBuilder();
391 mResultJSON
.setUndefined();
392 mLoadTransferred
= 0;
393 mResponseBodyDecodedPos
= 0;
397 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestMainThread
)
399 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestMainThread
,
400 XMLHttpRequestEventTarget
)
401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext
)
402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel
)
403 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML
)
405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener
)
407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob
)
408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks
)
410 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink
)
411 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink
)
413 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload
)
414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
416 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread
,
417 XMLHttpRequestEventTarget
)
418 tmp
->mResultArrayBuffer
= nullptr;
419 tmp
->mArrayBufferBuilder
= nullptr;
420 tmp
->mResultJSON
.setUndefined();
421 tmp
->mResponseBlobImpl
= nullptr;
423 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext
)
424 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel
)
425 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML
)
427 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener
)
429 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob
)
430 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks
)
432 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink
)
433 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink
)
435 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload
)
436 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
438 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestMainThread
,
439 XMLHttpRequestEventTarget
)
440 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer
)
441 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON
)
442 NS_IMPL_CYCLE_COLLECTION_TRACE_END
444 bool XMLHttpRequestMainThread::IsCertainlyAliveForCC() const {
445 return mWaitingForOnStopRequest
;
448 // QueryInterface implementation for XMLHttpRequestMainThread
449 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestMainThread
)
450 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
451 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
452 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
453 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
454 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
455 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
456 NS_INTERFACE_MAP_ENTRY(nsINamed
)
457 NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget
)
458 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget
)
460 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestMainThread
, XMLHttpRequestEventTarget
)
461 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestMainThread
, XMLHttpRequestEventTarget
)
463 void XMLHttpRequestMainThread::DisconnectFromOwner() {
464 XMLHttpRequestEventTarget::DisconnectFromOwner();
468 size_t XMLHttpRequestMainThread::SizeOfEventTargetIncludingThis(
469 MallocSizeOf aMallocSizeOf
) const {
470 size_t n
= aMallocSizeOf(this);
471 n
+= mResponseBody
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
473 // Why is this safe? Because no-one else will report this string. The
474 // other possible sharers of this string are as follows.
476 // - The JS engine could hold copies if the JS code holds references, e.g.
477 // |var text = XHR.responseText|. However, those references will be via JS
478 // external strings, for which the JS memory reporter does *not* report the
481 // - Binary extensions, but they're *extremely* unlikely to do any memory
484 n
+= mResponseText
.SizeOfThis(aMallocSizeOf
);
488 // Measurement of the following members may be added later if DMD finds it is
493 static void LogMessage(
494 const char* aWarning
, nsPIDOMWindowInner
* aWindow
,
495 const nsTArray
<nsString
>& aParams
= nsTArray
<nsString
>()) {
496 nsCOMPtr
<Document
> doc
;
498 doc
= aWindow
->GetExtantDoc();
500 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "DOM"_ns
, doc
,
501 nsContentUtils::eDOM_PROPERTIES
, aWarning
,
505 Document
* XMLHttpRequestMainThread::GetResponseXML(ErrorResult
& aRv
) {
506 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
507 mResponseType
!= XMLHttpRequestResponseType::Document
) {
508 aRv
.ThrowInvalidStateError(
509 "responseXML is only available if responseType is '' or 'document'.");
512 if (mWarnAboutSyncHtml
) {
513 mWarnAboutSyncHtml
= false;
514 LogMessage("HTMLSyncXHRWarning", GetOwner());
516 if (mState
!= XMLHttpRequest_Binding::DONE
) {
523 * This piece copied from XMLDocument, we try to get the charset
526 nsresult
XMLHttpRequestMainThread::DetectCharset() {
530 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
531 mResponseType
!= XMLHttpRequestResponseType::Text
&&
532 mResponseType
!= XMLHttpRequestResponseType::Json
) {
536 nsAutoCString charsetVal
;
537 const Encoding
* encoding
;
538 bool ok
= mChannel
&& NS_SUCCEEDED(mChannel
->GetContentCharset(charsetVal
)) &&
539 (encoding
= Encoding::ForLabel(charsetVal
));
541 // MS documentation states UTF-8 is default for responseText
542 encoding
= UTF_8_ENCODING
;
545 if (mResponseType
== XMLHttpRequestResponseType::Json
&&
546 encoding
!= UTF_8_ENCODING
) {
547 // The XHR spec says only UTF-8 is supported for responseType == "json"
548 LogMessage("JSONCharsetWarning", GetOwner());
549 encoding
= UTF_8_ENCODING
;
552 // Only sniff the BOM for non-JSON responseTypes
553 if (mResponseType
== XMLHttpRequestResponseType::Json
) {
554 mDecoder
= encoding
->NewDecoderWithBOMRemoval();
556 mDecoder
= encoding
->NewDecoder();
562 nsresult
XMLHttpRequestMainThread::AppendToResponseText(
563 Span
<const uint8_t> aBuffer
, bool aLast
) {
564 // Call this with an empty buffer to send the decoder the signal
565 // that we have hit the end of the stream.
567 NS_ENSURE_STATE(mDecoder
);
569 CheckedInt
<size_t> destBufferLen
=
570 mDecoder
->MaxUTF16BufferLength(aBuffer
.Length());
572 { // scope for holding the mutex that protects mResponseText
573 XMLHttpRequestStringWriterHelper
helper(mResponseText
);
575 uint32_t len
= helper
.Length();
577 destBufferLen
+= len
;
578 if (!destBufferLen
.isValid() || destBufferLen
.value() > UINT32_MAX
) {
579 return NS_ERROR_OUT_OF_MEMORY
;
582 auto handleOrErr
= helper
.BulkWrite(destBufferLen
.value());
583 if (handleOrErr
.isErr()) {
584 return handleOrErr
.unwrapErr();
587 auto handle
= handleOrErr
.unwrap();
592 std::tie(result
, read
, written
, std::ignore
) =
593 mDecoder
->DecodeToUTF16(aBuffer
, handle
.AsSpan().From(len
), aLast
);
594 MOZ_ASSERT(result
== kInputEmpty
);
595 MOZ_ASSERT(read
== aBuffer
.Length());
597 MOZ_ASSERT(len
<= destBufferLen
.value());
598 handle
.Finish(len
, false);
602 // Drop the finished decoder to avoid calling into a decoder
603 // that has finished.
610 void XMLHttpRequestMainThread::GetResponseText(DOMString
& aResponseText
,
612 MOZ_DIAGNOSTIC_ASSERT(!mForWorker
);
614 XMLHttpRequestStringSnapshot snapshot
;
615 GetResponseText(snapshot
, aRv
);
620 if (!snapshot
.GetAsString(aResponseText
)) {
621 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
626 void XMLHttpRequestMainThread::GetResponseText(
627 XMLHttpRequestStringSnapshot
& aSnapshot
, ErrorResult
& aRv
) {
630 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
631 mResponseType
!= XMLHttpRequestResponseType::Text
) {
632 aRv
.ThrowInvalidStateError(
633 "responseText is only available if responseType is '' or 'text'.");
637 if (mState
!= XMLHttpRequest_Binding::LOADING
&&
638 mState
!= XMLHttpRequest_Binding::DONE
) {
642 // Main Fetch step 18 requires to ignore body for head/connect methods.
643 if (mRequestMethod
.EqualsLiteral("HEAD") ||
644 mRequestMethod
.EqualsLiteral("CONNECT")) {
648 // We only decode text lazily if we're also parsing to a doc.
649 // Also, if we've decoded all current data already, then no need to decode
651 if ((!mResponseXML
&& !mErrorParsingXML
) ||
652 (mResponseBodyDecodedPos
== mResponseBody
.Length() &&
653 (mState
!= XMLHttpRequest_Binding::DONE
|| mEofDecoded
))) {
654 mResponseText
.CreateSnapshot(aSnapshot
);
658 MatchCharsetAndDecoderToResponseDocument();
660 MOZ_ASSERT(mResponseBodyDecodedPos
< mResponseBody
.Length() ||
661 mState
== XMLHttpRequest_Binding::DONE
,
662 "Unexpected mResponseBodyDecodedPos");
663 Span
<const uint8_t> span
= mResponseBody
;
664 aRv
= AppendToResponseText(span
.From(mResponseBodyDecodedPos
),
665 mState
== XMLHttpRequest_Binding::DONE
);
670 mResponseBodyDecodedPos
= mResponseBody
.Length();
673 // Free memory buffer which we no longer need
674 mResponseBody
.Truncate();
675 mResponseBodyDecodedPos
= 0;
678 mResponseText
.CreateSnapshot(aSnapshot
);
681 nsresult
XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext
* aCx
) {
683 return NS_ERROR_FAILURE
;
687 nsresult rv
= GetResponseTextForJSON(string
);
688 if (NS_WARN_IF(NS_FAILED(rv
))) {
692 // The Unicode converter has already zapped the BOM if there was one
693 JS::Rooted
<JS::Value
> value(aCx
);
694 if (!JS_ParseJSON(aCx
, string
.BeginReading(), string
.Length(), &value
)) {
695 return NS_ERROR_FAILURE
;
702 void XMLHttpRequestMainThread::SetResponseType(
703 XMLHttpRequestResponseType aResponseType
, ErrorResult
& aRv
) {
704 NOT_CALLABLE_IN_SYNC_SEND_RV
706 if (mState
== XMLHttpRequest_Binding::LOADING
||
707 mState
== XMLHttpRequest_Binding::DONE
) {
708 aRv
.ThrowInvalidStateError(
709 "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
710 "(when its state is LOADING or DONE).");
714 // sync request is not allowed setting responseType in window context
715 if (HasOrHasHadOwner() && mState
!= XMLHttpRequest_Binding::UNSENT
&&
717 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
718 aRv
.ThrowInvalidAccessError(
719 "synchronous XMLHttpRequests do not support timeout and responseType");
723 // Set the responseType attribute's value to the given value.
724 SetResponseTypeRaw(aResponseType
);
727 void XMLHttpRequestMainThread::GetResponse(
728 JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aResponse
, ErrorResult
& aRv
) {
729 MOZ_DIAGNOSTIC_ASSERT(!mForWorker
);
731 switch (mResponseType
) {
732 case XMLHttpRequestResponseType::_empty
:
733 case XMLHttpRequestResponseType::Text
: {
735 GetResponseText(str
, aRv
);
739 if (!xpc::StringToJsval(aCx
, str
, aResponse
)) {
740 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
745 case XMLHttpRequestResponseType::Arraybuffer
: {
746 if (mState
!= XMLHttpRequest_Binding::DONE
) {
751 if (!mResultArrayBuffer
) {
752 mResultArrayBuffer
= mArrayBufferBuilder
->TakeArrayBuffer(aCx
);
753 if (!mResultArrayBuffer
) {
754 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
758 aResponse
.setObject(*mResultArrayBuffer
);
761 case XMLHttpRequestResponseType::Blob
: {
762 if (mState
!= XMLHttpRequest_Binding::DONE
) {
767 if (!mResponseBlobImpl
) {
772 if (!mResponseBlob
) {
773 mResponseBlob
= Blob::Create(GetOwnerGlobal(), mResponseBlobImpl
);
776 if (!GetOrCreateDOMReflector(aCx
, mResponseBlob
, aResponse
)) {
782 case XMLHttpRequestResponseType::Document
: {
783 if (!mResponseXML
|| mState
!= XMLHttpRequest_Binding::DONE
) {
789 nsContentUtils::WrapNative(aCx
, ToSupports(mResponseXML
), aResponse
);
792 case XMLHttpRequestResponseType::Json
: {
793 if (mState
!= XMLHttpRequest_Binding::DONE
) {
798 if (mResultJSON
.isUndefined()) {
799 aRv
= CreateResponseParsedJSON(aCx
);
800 TruncateResponseText();
802 // Per spec, errors aren't propagated. null is returned instead.
804 // It would be nice to log the error to the console. That's hard to
805 // do without calling window.onerror as a side effect, though.
806 JS_ClearPendingException(aCx
);
807 mResultJSON
.setNull();
810 aResponse
.set(mResultJSON
);
814 NS_ERROR("Should not happen");
820 already_AddRefed
<BlobImpl
> XMLHttpRequestMainThread::GetResponseBlobImpl() {
821 MOZ_DIAGNOSTIC_ASSERT(mForWorker
);
822 MOZ_DIAGNOSTIC_ASSERT(mResponseType
== XMLHttpRequestResponseType::Blob
);
824 if (mState
!= XMLHttpRequest_Binding::DONE
) {
828 RefPtr
<BlobImpl
> blobImpl
= mResponseBlobImpl
;
829 return blobImpl
.forget();
832 already_AddRefed
<ArrayBufferBuilder
>
833 XMLHttpRequestMainThread::GetResponseArrayBufferBuilder() {
834 MOZ_DIAGNOSTIC_ASSERT(mForWorker
);
835 MOZ_DIAGNOSTIC_ASSERT(mResponseType
==
836 XMLHttpRequestResponseType::Arraybuffer
);
838 if (mState
!= XMLHttpRequest_Binding::DONE
) {
842 RefPtr
<ArrayBufferBuilder
> builder
= mArrayBufferBuilder
;
843 return builder
.forget();
846 nsresult
XMLHttpRequestMainThread::GetResponseTextForJSON(nsAString
& aString
) {
847 if (mState
!= XMLHttpRequest_Binding::DONE
) {
848 aString
.SetIsVoid(true);
852 if (!mResponseText
.GetAsString(aString
)) {
853 return NS_ERROR_OUT_OF_MEMORY
;
859 bool XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const {
864 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
865 return loadInfo
->GetTainting() == LoadTainting::CORS
;
868 bool XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest() {
869 if (IsCrossSiteCORSRequest()) {
871 mChannel
->GetStatus(&rv
);
879 bool XMLHttpRequestMainThread::BadContentRangeRequested() {
883 // Only nsIBaseChannel supports this
884 nsCOMPtr
<nsIBaseChannel
> baseChan
= do_QueryInterface(mChannel
);
888 // A bad range was requested if the channel has no content range
889 // despite the request specifying a range header.
890 return !baseChan
->ContentRange() && mAuthorRequestHeaders
.Has("range");
893 RefPtr
<mozilla::net::ContentRange
>
894 XMLHttpRequestMainThread::GetRequestedContentRange() const {
895 MOZ_ASSERT(mChannel
);
896 nsCOMPtr
<nsIBaseChannel
> baseChan
= do_QueryInterface(mChannel
);
900 return baseChan
->ContentRange();
903 void XMLHttpRequestMainThread::GetContentRangeHeader(nsACString
& out
) const {
904 if (!IsBlobURI(mRequestURL
)) {
908 RefPtr
<mozilla::net::ContentRange
> range
= GetRequestedContentRange();
910 range
->AsHeader(out
);
916 void XMLHttpRequestMainThread::GetResponseURL(nsAString
& aUrl
) {
919 if ((mState
== XMLHttpRequest_Binding::UNSENT
||
920 mState
== XMLHttpRequest_Binding::OPENED
) ||
925 // Make sure we don't leak responseURL information from denied cross-site
927 if (IsDeniedCrossSiteCORSRequest()) {
931 nsCOMPtr
<nsIURI
> responseUrl
;
932 if (NS_FAILED(NS_GetFinalChannelURI(mChannel
, getter_AddRefs(responseUrl
)))) {
937 responseUrl
->GetSpecIgnoringRef(temp
);
938 CopyUTF8toUTF16(temp
, aUrl
);
941 uint32_t XMLHttpRequestMainThread::GetStatus(ErrorResult
& aRv
) {
942 // Make sure we don't leak status information from denied cross-site
944 if (IsDeniedCrossSiteCORSRequest()) {
948 if (mState
== XMLHttpRequest_Binding::UNSENT
||
949 mState
== XMLHttpRequest_Binding::OPENED
) {
953 if (mErrorLoad
!= ErrorType::eOK
) {
954 // Let's simulate the http protocol for jar/app requests:
955 nsCOMPtr
<nsIJARChannel
> jarChannel
= GetCurrentJARChannel();
958 mChannel
->GetStatus(&status
);
960 if (status
== NS_ERROR_FILE_NOT_FOUND
) {
961 return 404; // Not Found
963 return 500; // Internal Error
970 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
972 // Pretend like we got a 200/206 response, since our load was successful
973 return GetRequestedContentRange() ? 206 : 200;
977 nsresult rv
= httpChannel
->GetResponseStatus(&status
);
985 void XMLHttpRequestMainThread::GetStatusText(nsACString
& aStatusText
,
987 // Return an empty status text on all error loads.
988 aStatusText
.Truncate();
990 // Make sure we don't leak status information from denied cross-site
992 if (IsDeniedCrossSiteCORSRequest()) {
996 // Check the current XHR state to see if it is valid to obtain the statusText
997 // value. This check is to prevent the status text for redirects from being
998 // available before all the redirects have been followed and HTTP headers have
1000 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1001 mState
== XMLHttpRequest_Binding::OPENED
) {
1005 if (mErrorLoad
!= ErrorType::eOK
) {
1009 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
1011 Unused
<< httpChannel
->GetResponseStatusText(aStatusText
);
1013 aStatusText
.AssignLiteral("OK");
1017 void XMLHttpRequestMainThread::TerminateOngoingFetch(nsresult detail
) {
1019 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
1020 mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
1021 mState
== XMLHttpRequest_Binding::LOADING
) {
1022 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Info
,
1023 ("%p TerminateOngoingFetch(0x%" PRIx32
")", this,
1024 static_cast<uint32_t>(detail
)));
1025 CloseRequest(detail
);
1029 void XMLHttpRequestMainThread::CloseRequest(nsresult detail
) {
1031 mWaitingForOnStopRequest
= false;
1032 mErrorLoad
= ErrorType::eTerminated
;
1033 mErrorLoadDetail
= detail
;
1035 mChannel
->CancelWithReason(NS_BINDING_ABORTED
,
1036 "XMLHttpRequestMainThread::CloseRequest"_ns
);
1038 CancelTimeoutTimer();
1041 void XMLHttpRequestMainThread::CloseRequestWithError(
1042 const ErrorProgressEventType
& aType
) {
1044 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1045 ("%p CloseRequestWithError(%s)", this, aType
.cStr
));
1047 CloseRequest(aType
.errorCode
);
1051 // If we're in the destructor, don't risk dispatching an event.
1053 mFlagSyncLooping
= false;
1057 if (mState
!= XMLHttpRequest_Binding::UNSENT
&&
1058 !(mState
== XMLHttpRequest_Binding::OPENED
&& !mFlagSend
) &&
1059 mState
!= XMLHttpRequest_Binding::DONE
) {
1060 ChangeState(XMLHttpRequest_Binding::DONE
, true);
1062 if (!mFlagSyncLooping
) {
1063 if (mUpload
&& !mUploadComplete
) {
1064 mUploadComplete
= true;
1065 DispatchProgressEvent(mUpload
, aType
, 0, -1);
1067 DispatchProgressEvent(this, aType
, 0, -1);
1071 // The ChangeState call above calls onreadystatechange handlers which
1072 // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
1073 // the abort state bit. If this occurs we're not uninitialized (bug 361773).
1075 ChangeState(XMLHttpRequest_Binding::UNSENT
, false); // IE seems to do it
1078 mFlagSyncLooping
= false;
1081 void XMLHttpRequestMainThread::RequestErrorSteps(
1082 const ProgressEventType aEventType
, const nsresult aOptionalException
,
1084 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1085 ("%p RequestErrorSteps(%s,0x%" PRIx32
")", this, aEventType
.cStr
,
1086 static_cast<uint32_t>(aOptionalException
)));
1088 // Cancel our timers first before setting our state to done, so we don't
1089 // trip any assertions if one fires and asserts that state != done.
1090 CancelTimeoutTimer();
1091 CancelSyncTimeoutTimer();
1092 StopProgressEventTimer();
1095 mState
= XMLHttpRequest_Binding::DONE
;
1103 // If we're in the destructor, don't risk dispatching an event.
1105 mFlagSyncLooping
= false;
1110 if (mFlagSynchronous
&& NS_FAILED(aOptionalException
)) {
1111 aRv
.Throw(aOptionalException
);
1116 FireReadystatechangeEvent();
1119 if (mUpload
&& !mUploadComplete
) {
1121 mUploadComplete
= true;
1124 if (mFlagHadUploadListenersOnSend
) {
1125 // Steps 6-3, 6-4 (loadend is fired for us)
1126 DispatchProgressEvent(mUpload
, aEventType
, 0, -1);
1130 // Steps 7 and 8 (loadend is fired for us)
1131 DispatchProgressEvent(this, aEventType
, 0, -1);
1134 void XMLHttpRequestMainThread::Abort(ErrorResult
& aRv
) {
1135 NOT_CALLABLE_IN_SYNC_SEND_RV
1136 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
, ("%p Abort()", this));
1140 void XMLHttpRequestMainThread::AbortInternal(ErrorResult
& aRv
) {
1141 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
, ("%p AbortInternal()", this));
1142 mFlagAborted
= true;
1143 DisconnectDoneNotifier();
1146 TerminateOngoingFetch(NS_ERROR_DOM_ABORT_ERR
);
1149 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
1150 mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
1151 mState
== XMLHttpRequest_Binding::LOADING
) {
1152 RequestErrorSteps(Events::abort
, NS_ERROR_DOM_ABORT_ERR
, aRv
);
1156 if (mState
== XMLHttpRequest_Binding::DONE
) {
1157 ChangeState(XMLHttpRequest_Binding::UNSENT
,
1158 false); // no ReadystateChange event
1161 mFlagSyncLooping
= false;
1164 /*Method that checks if it is safe to expose a header value to the client.
1165 It is used to check what headers are exposed for CORS requests.*/
1166 bool XMLHttpRequestMainThread::IsSafeHeader(
1167 const nsACString
& aHeader
, NotNull
<nsIHttpChannel
*> aHttpChannel
) const {
1168 // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
1169 if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(aHeader
)) {
1170 NS_WARNING("blocked access to response header");
1173 // if this is not a CORS call all headers are safe
1174 if (!IsCrossSiteCORSRequest()) {
1177 // Check for dangerous headers
1178 // Make sure we don't leak header information from denied cross-site
1182 mChannel
->GetStatus(&status
);
1183 if (NS_FAILED(status
)) {
1187 const char* kCrossOriginSafeHeaders
[] = {
1188 "cache-control", "content-language", "content-type", "content-length",
1189 "expires", "last-modified", "pragma"};
1190 for (uint32_t i
= 0; i
< ArrayLength(kCrossOriginSafeHeaders
); ++i
) {
1191 if (aHeader
.LowerCaseEqualsASCII(kCrossOriginSafeHeaders
[i
])) {
1195 nsAutoCString headerVal
;
1196 // The "Access-Control-Expose-Headers" header contains a comma separated
1197 // list of method names.
1198 Unused
<< aHttpChannel
->GetResponseHeader("Access-Control-Expose-Headers"_ns
,
1200 bool isSafe
= false;
1201 for (const nsACString
& token
:
1202 nsCCharSeparatedTokenizer(headerVal
, ',').ToRange()) {
1203 if (token
.IsEmpty()) {
1206 if (!NS_IsValidHTTPToken(token
)) {
1210 if (token
.EqualsLiteral("*") && !mFlagACwithCredentials
) {
1212 } else if (aHeader
.Equals(token
, nsCaseInsensitiveCStringComparator
)) {
1220 bool XMLHttpRequestMainThread::GetContentType(nsACString
& aValue
) const {
1221 MOZ_ASSERT(mChannel
);
1222 nsCOMPtr
<nsIBaseChannel
> baseChan
= do_QueryInterface(mChannel
);
1224 RefPtr
<CMimeType
> fullMimeType(baseChan
->FullMimeType());
1226 fullMimeType
->Serialize(aValue
);
1230 if (NS_SUCCEEDED(mChannel
->GetContentType(aValue
))) {
1232 if (NS_SUCCEEDED(mChannel
->GetContentCharset(value
)) && !value
.IsEmpty()) {
1233 aValue
.AppendLiteral(";charset=");
1234 aValue
.Append(value
);
1240 void XMLHttpRequestMainThread::GetAllResponseHeaders(
1241 nsACString
& aResponseHeaders
, ErrorResult
& aRv
) {
1242 NOT_CALLABLE_IN_SYNC_SEND_RV
1244 aResponseHeaders
.Truncate();
1246 // If the state is UNSENT or OPENED,
1247 // return the empty string and terminate these steps.
1248 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1249 mState
== XMLHttpRequest_Binding::OPENED
) {
1253 if (mErrorLoad
!= ErrorType::eOK
) {
1257 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel()) {
1258 RefPtr
<nsHeaderVisitor
> visitor
=
1259 new nsHeaderVisitor(*this, WrapNotNull(httpChannel
));
1260 if (NS_SUCCEEDED(httpChannel
->VisitResponseHeaders(visitor
))) {
1261 aResponseHeaders
= visitor
->Headers();
1270 // Even non-http channels supply content type.
1271 nsAutoCString value
;
1272 if (GetContentType(value
)) {
1273 aResponseHeaders
.AppendLiteral("Content-Type: ");
1274 aResponseHeaders
.Append(value
);
1275 aResponseHeaders
.AppendLiteral("\r\n");
1278 // Don't provide Content-Length for data URIs
1279 nsCOMPtr
<nsIURI
> uri
;
1280 if (NS_FAILED(mChannel
->GetURI(getter_AddRefs(uri
))) ||
1281 !uri
->SchemeIs("data")) {
1283 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1284 aResponseHeaders
.AppendLiteral("Content-Length: ");
1285 aResponseHeaders
.AppendInt(length
);
1286 aResponseHeaders
.AppendLiteral("\r\n");
1290 // Should set a Content-Range header for blob scheme.
1291 // From https://fetch.spec.whatwg.org/#scheme-fetch 3.blob.9.20:
1292 // "Set response’s header list to «(`Content-Length`, serializedSlicedLength),
1293 // (`Content-Type`, type), (`Content-Range`, contentRange)»."
1294 GetContentRangeHeader(value
);
1295 if (!value
.IsVoid()) {
1296 aResponseHeaders
.AppendLiteral("Content-Range: ");
1297 aResponseHeaders
.Append(value
);
1298 aResponseHeaders
.AppendLiteral("\r\n");
1302 void XMLHttpRequestMainThread::GetResponseHeader(const nsACString
& header
,
1303 nsACString
& _retval
,
1305 NOT_CALLABLE_IN_SYNC_SEND_RV
1307 _retval
.SetIsVoid(true);
1309 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
1312 // If the state is UNSENT or OPENED,
1313 // return null and terminate these steps.
1314 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1315 mState
== XMLHttpRequest_Binding::OPENED
) {
1319 // Even non-http channels supply content type and content length.
1320 // Remember we don't leak header information from denied cross-site
1321 // requests. However, we handle file: and blob: URLs for blob response
1322 // types by canceling them with a specific error, so we have to allow
1323 // them to pass through this check.
1325 if (!mChannel
|| NS_FAILED(mChannel
->GetStatus(&status
)) ||
1326 (NS_FAILED(status
) && status
!= NS_ERROR_FILE_ALREADY_EXISTS
)) {
1331 if (header
.LowerCaseEqualsASCII("content-type")) {
1332 if (!GetContentType(_retval
)) {
1333 // Means no content type
1334 _retval
.SetIsVoid(true);
1340 else if (header
.LowerCaseEqualsASCII("content-length")) {
1342 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1343 _retval
.AppendInt(length
);
1348 else if (header
.LowerCaseEqualsASCII("content-range")) {
1349 GetContentRangeHeader(_retval
);
1355 // Check for dangerous headers
1356 if (!IsSafeHeader(header
, WrapNotNull(httpChannel
))) {
1360 aRv
= httpChannel
->GetResponseHeader(header
, _retval
);
1361 if (aRv
.ErrorCodeIs(NS_ERROR_NOT_AVAILABLE
)) {
1363 _retval
.SetIsVoid(true);
1364 aRv
.SuppressException();
1368 already_AddRefed
<nsILoadGroup
> XMLHttpRequestMainThread::GetLoadGroup() const {
1369 if (mFlagBackgroundRequest
) {
1374 nsCOMPtr
<nsILoadGroup
> ref
= mLoadGroup
;
1375 return ref
.forget();
1378 Document
* doc
= GetDocumentIfCurrent();
1380 return doc
->GetDocumentLoadGroup();
1386 nsresult
XMLHttpRequestMainThread::FireReadystatechangeEvent() {
1387 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::UNSENT
);
1388 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1389 event
->InitEvent(kLiteralString_readystatechange
, false, false);
1390 // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1391 event
->SetTrusted(true);
1392 DispatchOrStoreEvent(this, event
);
1396 void XMLHttpRequestMainThread::DispatchProgressEvent(
1397 DOMEventTargetHelper
* aTarget
, const ProgressEventType
& aType
,
1398 int64_t aLoaded
, int64_t aTotal
) {
1400 NS_ASSERTION(aTarget
, "null target");
1402 if (NS_FAILED(CheckCurrentGlobalCorrectness()) ||
1403 (!AllowUploadProgress() && aTarget
== mUpload
)) {
1407 // If blocked by CORS, zero-out the stats on progress events
1408 // and never fire "progress" or "load" events at all.
1409 if (IsDeniedCrossSiteCORSRequest()) {
1410 if (aType
== Events::progress
|| aType
== Events::load
) {
1417 ProgressEventInit init
;
1418 init
.mBubbles
= false;
1419 init
.mCancelable
= false;
1420 init
.mLengthComputable
= aTotal
!= -1; // XHR spec step 6.1
1421 init
.mLoaded
= aLoaded
;
1422 init
.mTotal
= (aTotal
== -1) ? 0 : aTotal
;
1424 RefPtr
<ProgressEvent
> event
=
1425 ProgressEvent::Constructor(aTarget
, aType
, init
);
1426 event
->SetTrusted(true);
1429 gXMLHttpRequestLog
, LogLevel::Debug
,
1430 ("firing %s event (%u,%u,%" PRIu64
",%" PRIu64
")", aType
.cStr
,
1431 aTarget
== mUpload
, aTotal
!= -1, aLoaded
, (aTotal
== -1) ? 0 : aTotal
));
1433 DispatchOrStoreEvent(aTarget
, event
);
1435 // If we're sending a load, error, timeout or abort event, then
1436 // also dispatch the subsequent loadend event.
1437 if (aType
== Events::load
|| aType
== Events::error
||
1438 aType
== Events::timeout
|| aType
== Events::abort
) {
1439 DispatchProgressEvent(aTarget
, Events::loadend
, aLoaded
, aTotal
);
1443 void XMLHttpRequestMainThread::DispatchOrStoreEvent(
1444 DOMEventTargetHelper
* aTarget
, Event
* aEvent
) {
1446 MOZ_ASSERT(aTarget
);
1449 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
1453 if (mEventDispatchingSuspended
) {
1454 PendingEvent
* event
= mPendingEvents
.AppendElement();
1455 event
->mTarget
= aTarget
;
1456 event
->mEvent
= aEvent
;
1460 aTarget
->DispatchEvent(*aEvent
);
1463 void XMLHttpRequestMainThread::SuspendEventDispatching() {
1464 MOZ_ASSERT(!mEventDispatchingSuspended
);
1465 mEventDispatchingSuspended
= true;
1468 void XMLHttpRequestMainThread::ResumeEventDispatching() {
1469 MOZ_ASSERT(mEventDispatchingSuspended
);
1470 mEventDispatchingSuspended
= false;
1472 nsTArray
<PendingEvent
> pendingEvents
= std::move(mPendingEvents
);
1474 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
1478 for (uint32_t i
= 0; i
< pendingEvents
.Length(); ++i
) {
1479 pendingEvents
[i
].mTarget
->DispatchEvent(*pendingEvents
[i
].mEvent
);
1483 already_AddRefed
<nsIHttpChannel
>
1484 XMLHttpRequestMainThread::GetCurrentHttpChannel() {
1485 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
1486 return httpChannel
.forget();
1489 already_AddRefed
<nsIJARChannel
>
1490 XMLHttpRequestMainThread::GetCurrentJARChannel() {
1491 nsCOMPtr
<nsIJARChannel
> appChannel
= do_QueryInterface(mChannel
);
1492 return appChannel
.forget();
1495 bool XMLHttpRequestMainThread::IsSystemXHR() const {
1496 return mIsSystem
|| mPrincipal
->IsSystemPrincipal();
1499 bool XMLHttpRequestMainThread::InUploadPhase() const {
1500 // We're in the upload phase while our state is OPENED.
1501 return mState
== XMLHttpRequest_Binding::OPENED
;
1504 // This case is hit when the async parameter is outright omitted, which
1505 // should set it to true (and the username and password to null).
1506 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1507 const nsAString
& aUrl
, ErrorResult
& aRv
) {
1508 Open(aMethod
, aUrl
, true, VoidString(), VoidString(), aRv
);
1511 // This case is hit when the async parameter is specified, even if the
1512 // JS value was "undefined" (which due to legacy reasons should be
1513 // treated as true, which is how it will already be passed in here).
1514 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1515 const nsAString
& aUrl
, bool aAsync
,
1516 const nsAString
& aUsername
,
1517 const nsAString
& aPassword
,
1519 Open(aMethod
, NS_ConvertUTF16toUTF8(aUrl
), aAsync
, aUsername
, aPassword
, aRv
);
1522 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1523 const nsACString
& aUrl
, bool aAsync
,
1524 const nsAString
& aUsername
,
1525 const nsAString
& aPassword
,
1527 DEBUG_WORKERREFS1(aMethod
<< " " << aUrl
);
1528 NOT_CALLABLE_IN_SYNC_SEND_RV
1531 if (!aAsync
&& !DontWarnAboutSyncXHR() && GetOwner() &&
1532 GetOwner()->GetExtantDoc()) {
1533 GetOwner()->GetExtantDoc()->WarnOnceAbout(
1534 DeprecatedOperations::eSyncXMLHttpRequestDeprecated
);
1537 Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC
,
1541 nsCOMPtr
<Document
> responsibleDocument
= GetDocumentIfCurrent();
1542 if (!responsibleDocument
) {
1543 // This could be because we're no longer current or because we're in some
1544 // non-window context...
1545 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
1546 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1551 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
1556 if (!aAsync
&& responsibleDocument
&& GetOwner()) {
1557 // We have no extant document during unload, so the above general
1558 // syncXHR warning will not display. But we do want to display a
1559 // recommendation to use sendBeacon instead of syncXHR during unload.
1560 nsCOMPtr
<nsIDocShell
> shell
= responsibleDocument
->GetDocShell();
1562 bool inUnload
= false;
1563 shell
->GetIsInUnload(&inUnload
);
1565 LogMessage("UseSendBeaconDuringUnloadAndPagehideWarning", GetOwner());
1571 nsAutoCString method
;
1572 aRv
= FetchUtil::GetValidRequestMethod(aMethod
, method
);
1573 if (NS_WARN_IF(aRv
.Failed())) {
1578 nsIURI
* baseURI
= nullptr;
1581 } else if (responsibleDocument
) {
1582 baseURI
= responsibleDocument
->GetBaseURI();
1585 // Use the responsible document's encoding for the URL if we have one,
1586 // except for dedicated workers. Use UTF-8 otherwise.
1587 NotNull
<const Encoding
*> originCharset
= UTF_8_ENCODING
;
1588 if (responsibleDocument
&&
1589 responsibleDocument
->NodePrincipal() == mPrincipal
) {
1590 originCharset
= responsibleDocument
->GetDocumentCharacterSet();
1593 nsCOMPtr
<nsIURI
> parsedURL
;
1595 NS_NewURI(getter_AddRefs(parsedURL
), aUrl
, originCharset
, baseURI
);
1596 if (NS_FAILED(rv
)) {
1597 aRv
.ThrowSyntaxError("'"_ns
+ aUrl
+ "' is not a valid URL."_ns
);
1600 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
1601 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1606 // This is already handled by the other Open() method, which passes
1607 // username and password in as NullStrings.
1611 parsedURL
->GetHost(host
);
1612 if (!host
.IsEmpty() && (!aUsername
.IsVoid() || !aPassword
.IsVoid())) {
1613 auto mutator
= NS_MutateURI(parsedURL
);
1614 if (!aUsername
.IsVoid()) {
1615 mutator
.SetUsername(NS_ConvertUTF16toUTF8(aUsername
));
1617 if (!aPassword
.IsVoid()) {
1618 mutator
.SetPassword(NS_ConvertUTF16toUTF8(aPassword
));
1620 Unused
<< mutator
.Finalize(parsedURL
);
1624 if (!aAsync
&& HasOrHasHadOwner() &&
1625 (mTimeoutMilliseconds
||
1626 mResponseType
!= XMLHttpRequestResponseType::_empty
)) {
1627 if (mTimeoutMilliseconds
) {
1628 LogMessage("TimeoutSyncXHRWarning", GetOwner());
1630 if (mResponseType
!= XMLHttpRequestResponseType::_empty
) {
1631 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
1633 aRv
.ThrowInvalidAccessError(
1634 "synchronous XMLHttpRequests do not support timeout and responseType");
1639 TerminateOngoingFetch(NS_OK
);
1642 // timeouts are handled without a flag
1643 DisconnectDoneNotifier();
1645 mRequestMethod
.Assign(method
);
1646 mRequestURL
= parsedURL
;
1647 mFlagSynchronous
= !aAsync
;
1648 mAuthorRequestHeaders
.Clear();
1652 mFlagHadUploadListenersOnSend
= false;
1653 mFlagAborted
= false;
1654 mFlagTimedOut
= false;
1657 // Per spec we should only create the channel on send(), but we have internal
1658 // code that relies on the channel being created now, and that code is not
1659 // always IsSystemXHR(). However, we're not supposed to throw channel-creation
1660 // errors during open(), so we silently ignore those here.
1664 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
1665 mState
= XMLHttpRequest_Binding::OPENED
;
1666 FireReadystatechangeEvent();
1670 void XMLHttpRequestMainThread::SetOriginAttributes(
1671 const OriginAttributesDictionary
& aAttrs
) {
1672 MOZ_ASSERT((mState
== XMLHttpRequest_Binding::OPENED
) && !mFlagSend
);
1674 OriginAttributes
attrs(aAttrs
);
1676 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
1677 loadInfo
->SetOriginAttributes(attrs
);
1681 * "Copy" from a stream.
1683 nsresult
XMLHttpRequestMainThread::StreamReaderFunc(
1684 nsIInputStream
* in
, void* closure
, const char* fromRawSegment
,
1685 uint32_t toOffset
, uint32_t count
, uint32_t* writeCount
) {
1686 XMLHttpRequestMainThread
* xmlHttpRequest
=
1687 static_cast<XMLHttpRequestMainThread
*>(closure
);
1688 if (!xmlHttpRequest
|| !writeCount
) {
1690 "XMLHttpRequest cannot read from stream: no closure or writeCount");
1691 return NS_ERROR_FAILURE
;
1694 nsresult rv
= NS_OK
;
1696 if (xmlHttpRequest
->mResponseType
== XMLHttpRequestResponseType::Blob
) {
1697 xmlHttpRequest
->MaybeCreateBlobStorage();
1698 rv
= xmlHttpRequest
->mBlobStorage
->Append(fromRawSegment
, count
);
1699 } else if (xmlHttpRequest
->mResponseType
==
1700 XMLHttpRequestResponseType::Arraybuffer
&&
1701 !xmlHttpRequest
->mIsMappedArrayBuffer
) {
1702 // get the initial capacity to something reasonable to avoid a bunch of
1703 // reallocs right at the start
1704 if (xmlHttpRequest
->mArrayBufferBuilder
->Capacity() == 0)
1705 xmlHttpRequest
->mArrayBufferBuilder
->SetCapacity(
1706 std::max(count
, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE
));
1708 if (NS_WARN_IF(!xmlHttpRequest
->mArrayBufferBuilder
->Append(
1709 reinterpret_cast<const uint8_t*>(fromRawSegment
), count
,
1710 XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH
))) {
1711 return NS_ERROR_OUT_OF_MEMORY
;
1714 } else if (xmlHttpRequest
->mResponseType
==
1715 XMLHttpRequestResponseType::_empty
&&
1716 xmlHttpRequest
->mResponseXML
) {
1717 // Copy for our own use
1718 if (!xmlHttpRequest
->mResponseBody
.Append(fromRawSegment
, count
,
1720 return NS_ERROR_OUT_OF_MEMORY
;
1722 } else if (xmlHttpRequest
->mResponseType
==
1723 XMLHttpRequestResponseType::_empty
||
1724 xmlHttpRequest
->mResponseType
==
1725 XMLHttpRequestResponseType::Text
||
1726 xmlHttpRequest
->mResponseType
==
1727 XMLHttpRequestResponseType::Json
) {
1728 MOZ_ASSERT(!xmlHttpRequest
->mResponseXML
,
1729 "We shouldn't be parsing a doc here");
1730 rv
= xmlHttpRequest
->AppendToResponseText(
1731 AsBytes(Span(fromRawSegment
, count
)));
1732 if (NS_WARN_IF(NS_FAILED(rv
))) {
1737 if (xmlHttpRequest
->mFlagParseBody
) {
1738 // Give the same data to the parser.
1740 // We need to wrap the data in a new lightweight stream and pass that
1741 // to the parser, because calling ReadSegments() recursively on the same
1742 // stream is not supported.
1743 nsCOMPtr
<nsIInputStream
> copyStream
;
1744 rv
= NS_NewByteInputStream(getter_AddRefs(copyStream
),
1745 Span(fromRawSegment
, count
),
1746 NS_ASSIGNMENT_DEPEND
);
1748 if (NS_SUCCEEDED(rv
) && xmlHttpRequest
->mXMLParserStreamListener
) {
1749 NS_ASSERTION(copyStream
, "NS_NewByteInputStream lied");
1750 nsresult parsingResult
=
1751 xmlHttpRequest
->mXMLParserStreamListener
->OnDataAvailable(
1752 xmlHttpRequest
->mChannel
, copyStream
, toOffset
, count
);
1754 // No use to continue parsing if we failed here, but we
1755 // should still finish reading the stream
1756 if (NS_FAILED(parsingResult
)) {
1757 xmlHttpRequest
->mFlagParseBody
= false;
1762 if (NS_SUCCEEDED(rv
)) {
1763 *writeCount
= count
;
1773 void GetBlobURIFromChannel(nsIRequest
* aRequest
, nsIURI
** aURI
) {
1774 MOZ_ASSERT(aRequest
);
1779 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1784 nsCOMPtr
<nsIURI
> uri
;
1785 nsresult rv
= channel
->GetURI(getter_AddRefs(uri
));
1786 if (NS_FAILED(rv
)) {
1790 if (!dom::IsBlobURI(uri
)) {
1797 nsresult
GetLocalFileFromChannel(nsIRequest
* aRequest
, nsIFile
** aFile
) {
1798 MOZ_ASSERT(aRequest
);
1803 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(aRequest
);
1808 nsCOMPtr
<nsIFile
> file
;
1809 nsresult rv
= fc
->GetFile(getter_AddRefs(file
));
1810 if (NS_WARN_IF(NS_FAILED(rv
))) {
1818 nsresult
DummyStreamReaderFunc(nsIInputStream
* aInputStream
, void* aClosure
,
1819 const char* aFromRawSegment
, uint32_t aToOffset
,
1820 uint32_t aCount
, uint32_t* aWriteCount
) {
1821 *aWriteCount
= aCount
;
1825 class FileCreationHandler final
: public PromiseNativeHandler
{
1829 static void Create(Promise
* aPromise
, XMLHttpRequestMainThread
* aXHR
) {
1830 MOZ_ASSERT(aPromise
);
1832 RefPtr
<FileCreationHandler
> handler
= new FileCreationHandler(aXHR
);
1833 aPromise
->AppendNativeHandler(handler
);
1836 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
1837 ErrorResult
& aRv
) override
{
1838 if (NS_WARN_IF(!aValue
.isObject())) {
1839 mXHR
->LocalFileToBlobCompleted(nullptr);
1844 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob
, &aValue
.toObject(), blob
)))) {
1845 mXHR
->LocalFileToBlobCompleted(nullptr);
1849 mXHR
->LocalFileToBlobCompleted(blob
->Impl());
1852 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
1853 ErrorResult
& aRv
) override
{
1854 mXHR
->LocalFileToBlobCompleted(nullptr);
1858 explicit FileCreationHandler(XMLHttpRequestMainThread
* aXHR
) : mXHR(aXHR
) {
1862 ~FileCreationHandler() = default;
1864 RefPtr
<XMLHttpRequestMainThread
> mXHR
;
1867 NS_IMPL_ISUPPORTS0(FileCreationHandler
)
1871 void XMLHttpRequestMainThread::LocalFileToBlobCompleted(BlobImpl
* aBlobImpl
) {
1872 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::DONE
);
1874 mResponseBlobImpl
= aBlobImpl
;
1875 mBlobStorage
= nullptr;
1876 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1878 ChangeStateToDone(mFlagSyncLooping
);
1882 XMLHttpRequestMainThread::OnDataAvailable(nsIRequest
* request
,
1883 nsIInputStream
* inStr
,
1884 uint64_t sourceOffset
,
1887 NS_ENSURE_ARG_POINTER(inStr
);
1889 mProgressSinceLastProgressEvent
= true;
1890 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
1894 if (mResponseType
== XMLHttpRequestResponseType::Blob
) {
1895 nsCOMPtr
<nsIFile
> localFile
;
1896 nsCOMPtr
<nsIURI
> blobURI
;
1897 GetBlobURIFromChannel(request
, getter_AddRefs(blobURI
));
1899 RefPtr
<BlobImpl
> blobImpl
;
1900 rv
= NS_GetBlobForBlobURI(blobURI
, getter_AddRefs(blobImpl
));
1901 if (NS_SUCCEEDED(rv
)) {
1902 mResponseBlobImpl
= blobImpl
;
1905 rv
= GetLocalFileFromChannel(request
, getter_AddRefs(localFile
));
1907 if (NS_WARN_IF(NS_FAILED(rv
))) {
1911 if (mResponseBlobImpl
|| localFile
) {
1912 mBlobStorage
= nullptr;
1913 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1915 // The nsIStreamListener contract mandates us to read from the stream
1916 // before returning.
1918 rv
= inStr
->ReadSegments(DummyStreamReaderFunc
, nullptr, count
,
1920 NS_ENSURE_SUCCESS(rv
, rv
);
1922 ChangeState(XMLHttpRequest_Binding::LOADING
);
1924 // Cancel() must be called with an error. We use
1925 // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
1926 // just because we can retrieve the File from the channel directly.
1927 return request
->Cancel(NS_ERROR_FILE_ALREADY_EXISTS
);
1932 rv
= inStr
->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc
,
1933 (void*)this, count
, &totalRead
);
1934 NS_ENSURE_SUCCESS(rv
, rv
);
1936 // Fire the first progress event/loading state change
1937 if (mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
) {
1938 ChangeState(XMLHttpRequest_Binding::LOADING
);
1939 if (!mFlagSynchronous
) {
1940 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
,
1943 mProgressSinceLastProgressEvent
= false;
1946 if (!mFlagSynchronous
&& !mProgressTimerIsActive
) {
1947 StartProgressEventTimer();
1954 XMLHttpRequestMainThread::OnStartRequest(nsIRequest
* request
) {
1956 AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStartRequest", NETWORK
);
1958 nsresult rv
= NS_OK
;
1960 if (request
!= mChannel
) {
1961 // Can this still happen?
1965 // Don't do anything if we have been aborted
1966 if (mState
== XMLHttpRequest_Binding::UNSENT
) {
1970 // Don't do anything if we're in mid-abort, but let the request
1971 // know (this can happen due to race conditions in valid XHRs,
1972 // see bz1070763 for info).
1974 return NS_BINDING_ABORTED
;
1977 // Don't do anything if we have timed out.
1978 if (mFlagTimedOut
) {
1982 // If we were asked for a bad range on a blob URL, but we're async,
1983 // we should throw now in order to fire an error progress event.
1984 if (BadContentRangeRequested()) {
1985 return NS_ERROR_NET_PARTIAL_TRANSFER
;
1988 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
1989 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
1992 request
->GetStatus(&status
);
1993 if (mErrorLoad
== ErrorType::eOK
&& NS_FAILED(status
)) {
1994 mErrorLoad
= ErrorType::eRequest
;
1995 mErrorLoadDetail
= status
;
1998 // Upload phase is now over. If we were uploading anything,
1999 // stop the timer and fire any final progress events.
2000 if (mUpload
&& !mUploadComplete
&& mErrorLoad
== ErrorType::eOK
&&
2001 !mFlagSynchronous
) {
2002 StopProgressEventTimer();
2004 mUploadTransferred
= mUploadTotal
;
2006 if (mProgressSinceLastProgressEvent
) {
2007 DispatchProgressEvent(mUpload
, Events::progress
, mUploadTransferred
,
2009 mProgressSinceLastProgressEvent
= false;
2012 mUploadComplete
= true;
2013 DispatchProgressEvent(mUpload
, Events::load
, mUploadTotal
, mUploadTotal
);
2016 mFlagParseBody
= true;
2017 if (mErrorLoad
== ErrorType::eOK
) {
2018 ChangeState(XMLHttpRequest_Binding::HEADERS_RECEIVED
);
2023 if (!mOverrideMimeType
.IsEmpty()) {
2024 channel
->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
2027 // Fallback to 'application/octet-stream' (leaving data URLs alone)
2028 if (!IsBlobURI(mRequestURL
)) {
2030 channel
->GetContentType(type
);
2031 if (type
.IsEmpty() || type
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
2032 channel
->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM
));
2038 // Set up arraybuffer
2039 if (mResponseType
== XMLHttpRequestResponseType::Arraybuffer
&&
2040 NS_SUCCEEDED(status
)) {
2041 if (mIsMappedArrayBuffer
) {
2042 nsCOMPtr
<nsIJARChannel
> jarChannel
= do_QueryInterface(channel
);
2044 nsCOMPtr
<nsIURI
> uri
;
2045 rv
= channel
->GetURI(getter_AddRefs(uri
));
2046 if (NS_SUCCEEDED(rv
)) {
2048 nsAutoCString scheme
;
2049 uri
->GetScheme(scheme
);
2050 if (scheme
.LowerCaseEqualsLiteral("jar")) {
2051 nsCOMPtr
<nsIJARURI
> jarURI
= do_QueryInterface(uri
);
2053 jarURI
->GetJAREntry(file
);
2056 nsCOMPtr
<nsIFile
> jarFile
;
2057 jarChannel
->GetJarFile(getter_AddRefs(jarFile
));
2059 mIsMappedArrayBuffer
= false;
2061 rv
= mArrayBufferBuilder
->MapToFileInPackage(file
, jarFile
);
2062 // This can happen legitimately if there are compressed files
2063 // in the jarFile. See bug #1357219. No need to warn on the error.
2064 if (NS_FAILED(rv
)) {
2065 mIsMappedArrayBuffer
= false;
2067 channel
->SetContentType("application/mem-mapped"_ns
);
2073 // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
2074 // and we want it fallback to the malloc way.
2075 if (!mIsMappedArrayBuffer
) {
2076 int64_t contentLength
;
2077 rv
= channel
->GetContentLength(&contentLength
);
2078 if (NS_SUCCEEDED(rv
) && contentLength
> 0 &&
2079 contentLength
< XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE
) {
2080 mArrayBufferBuilder
->SetCapacity(static_cast<int32_t>(contentLength
));
2085 // Set up responseXML
2086 // Fetch spec Main Fetch step 21: ignore body for head/connect methods.
2087 bool parseBody
= (mResponseType
== XMLHttpRequestResponseType::_empty
||
2088 mResponseType
== XMLHttpRequestResponseType::Document
) &&
2089 !(mRequestMethod
.EqualsLiteral("HEAD") ||
2090 mRequestMethod
.EqualsLiteral("CONNECT"));
2093 // Do not try to parse documents if content-length = 0
2094 int64_t contentLength
;
2095 if (NS_SUCCEEDED(mChannel
->GetContentLength(&contentLength
)) &&
2096 contentLength
== 0) {
2102 mWarnAboutSyncHtml
= false;
2103 if (parseBody
&& NS_SUCCEEDED(status
)) {
2104 // We can gain a huge performance win by not even trying to
2105 // parse non-XML data. This also protects us from the situation
2106 // where we have an XML document and sink, but HTML (or other)
2107 // parser, which can produce unreliable results.
2109 channel
->GetContentType(type
);
2111 if ((mResponseType
== XMLHttpRequestResponseType::Document
) &&
2112 type
.EqualsLiteral("text/html")) {
2113 // HTML parsing is only supported for responseType == "document" to
2114 // avoid running the parser and, worse, populating responseXML for
2115 // legacy users of XHR who use responseType == "" for retrieving the
2116 // responseText of text/html resources. This legacy case is so common
2117 // that it's not useful to emit a warning about it.
2118 if (mFlagSynchronous
) {
2119 // We don't make cool new features available in the bad synchronous
2120 // mode. The synchronous mode is for legacy only.
2121 mWarnAboutSyncHtml
= true;
2122 mFlagParseBody
= false;
2126 } else if (!type
.IsEmpty() && (!(type
.EqualsLiteral("text/xml") ||
2127 type
.EqualsLiteral("application/xml") ||
2128 StringEndsWith(type
, "+xml"_ns
)))) {
2129 // Follow https://xhr.spec.whatwg.org/
2130 // If final MIME type is not null, text/html, text/xml, application/xml,
2131 // or does not end in +xml, return null.
2132 mFlagParseBody
= false;
2135 // The request failed, so we shouldn't be parsing anyway
2136 mFlagParseBody
= false;
2139 if (mFlagParseBody
) {
2140 nsCOMPtr
<nsIURI
> baseURI
, docURI
;
2141 rv
= mChannel
->GetURI(getter_AddRefs(docURI
));
2142 NS_ENSURE_SUCCESS(rv
, rv
);
2145 nsCOMPtr
<Document
> doc
= GetDocumentIfCurrent();
2146 nsCOMPtr
<nsIURI
> chromeXHRDocURI
, chromeXHRDocBaseURI
;
2148 chromeXHRDocURI
= doc
->GetDocumentURI();
2149 chromeXHRDocBaseURI
= doc
->GetBaseURI();
2151 // If we're no longer current, just kill the load, though it really should
2152 // have been killed already.
2153 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
2154 return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
;
2158 // Create an empty document from it.
2159 const auto& emptyStr
= u
""_ns
;
2160 nsIGlobalObject
* global
= DOMEventTargetHelper::GetParentObject();
2162 nsCOMPtr
<nsIPrincipal
> requestingPrincipal
;
2163 rv
= nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
2164 channel
, getter_AddRefs(requestingPrincipal
));
2165 NS_ENSURE_SUCCESS(rv
, rv
);
2167 rv
= NS_NewDOMDocument(
2168 getter_AddRefs(mResponseXML
), emptyStr
, emptyStr
, nullptr, docURI
,
2169 baseURI
, requestingPrincipal
, true, global
,
2170 mIsHtml
? DocumentFlavorHTML
: DocumentFlavorLegacyGuess
);
2171 NS_ENSURE_SUCCESS(rv
, rv
);
2172 mResponseXML
->SetChromeXHRDocURI(chromeXHRDocURI
);
2173 mResponseXML
->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI
);
2175 // suppress parsing failure messages to console for statuses which
2176 // can have empty bodies (see bug 884693).
2177 IgnoredErrorResult rv2
;
2178 uint32_t responseStatus
= GetStatus(rv2
);
2179 if (!rv2
.Failed() && (responseStatus
== 201 || responseStatus
== 202 ||
2180 responseStatus
== 204 || responseStatus
== 205 ||
2181 responseStatus
== 304)) {
2182 mResponseXML
->SetSuppressParserErrorConsoleMessages(true);
2185 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2186 bool isCrossSite
= false;
2187 isCrossSite
= loadInfo
->GetTainting() != LoadTainting::Basic
;
2190 mResponseXML
->DisableCookieAccess();
2193 nsCOMPtr
<nsIStreamListener
> listener
;
2194 nsCOMPtr
<nsILoadGroup
> loadGroup
;
2195 channel
->GetLoadGroup(getter_AddRefs(loadGroup
));
2197 // suppress <parsererror> nodes on XML document parse failure, but only
2198 // for non-privileged code (including Web Extensions). See bug 289714.
2199 if (!IsSystemXHR()) {
2200 mResponseXML
->SetSuppressParserErrorElement(true);
2203 rv
= mResponseXML
->StartDocumentLoad(kLoadAsData
, channel
, loadGroup
,
2204 nullptr, getter_AddRefs(listener
),
2206 NS_ENSURE_SUCCESS(rv
, rv
);
2208 // the spec requires the response document.referrer to be the empty string
2209 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2210 new ReferrerInfo(nullptr, mResponseXML
->ReferrerPolicy());
2211 mResponseXML
->SetReferrerInfo(referrerInfo
);
2213 mXMLParserStreamListener
= listener
;
2214 rv
= mXMLParserStreamListener
->OnStartRequest(request
);
2215 NS_ENSURE_SUCCESS(rv
, rv
);
2218 // Download phase beginning; start the progress event timer if necessary.
2219 if (NS_SUCCEEDED(rv
) && HasListenersFor(nsGkAtoms::onprogress
)) {
2220 StartProgressEventTimer();
2227 XMLHttpRequestMainThread::OnStopRequest(nsIRequest
* request
, nsresult status
) {
2229 AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStopRequest", NETWORK
);
2231 if (request
!= mChannel
) {
2232 // Can this still happen?
2236 // Send the decoder the signal that we've hit the end of the stream,
2237 // but only when decoding text eagerly.
2238 if (mDecoder
&& ((mResponseType
== XMLHttpRequestResponseType::Text
) ||
2239 (mResponseType
== XMLHttpRequestResponseType::Json
) ||
2240 (mResponseType
== XMLHttpRequestResponseType::_empty
&&
2242 AppendToResponseText(Span
<const uint8_t>(), true);
2245 mWaitingForOnStopRequest
= false;
2247 // make sure to notify the listener if we were aborted
2248 // XXX in fact, why don't we do the cleanup below in this case??
2249 // UNSENT is for abort calls. See OnStartRequest above.
2250 if (mState
== XMLHttpRequest_Binding::UNSENT
|| mFlagTimedOut
) {
2251 if (mXMLParserStreamListener
)
2252 (void)mXMLParserStreamListener
->OnStopRequest(request
, status
);
2256 // Is this good enough here?
2257 if (mXMLParserStreamListener
&& mFlagParseBody
) {
2258 mXMLParserStreamListener
->OnStopRequest(request
, status
);
2261 mXMLParserStreamListener
= nullptr;
2264 // If window.stop() or other aborts were issued, handle as an abort
2265 if (status
== NS_BINDING_ABORTED
) {
2266 mFlagParseBody
= false;
2267 IgnoredErrorResult rv
;
2268 RequestErrorSteps(Events::abort
, NS_ERROR_DOM_ABORT_ERR
, rv
);
2269 ChangeState(XMLHttpRequest_Binding::UNSENT
, false);
2273 // If we were just reading a blob URL, we're already done
2274 if (status
== NS_ERROR_FILE_ALREADY_EXISTS
&& mResponseBlobImpl
) {
2275 ChangeStateToDone(mFlagSyncLooping
);
2279 bool waitingForBlobCreation
= false;
2281 // If we have this error, we have to deal with a file: URL + responseType =
2282 // blob. We have this error because we canceled the channel. The status will
2284 if (!mResponseBlobImpl
&& status
== NS_ERROR_FILE_ALREADY_EXISTS
&&
2285 mResponseType
== XMLHttpRequestResponseType::Blob
) {
2286 nsCOMPtr
<nsIFile
> file
;
2287 nsresult rv
= GetLocalFileFromChannel(request
, getter_AddRefs(file
));
2288 if (NS_WARN_IF(NS_FAILED(rv
))) {
2293 nsAutoCString contentType
;
2294 rv
= mChannel
->GetContentType(contentType
);
2295 if (NS_WARN_IF(NS_FAILED(rv
))) {
2299 ChromeFilePropertyBag bag
;
2300 CopyUTF8toUTF16(contentType
, bag
.mType
);
2302 nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal();
2305 RefPtr
<Promise
> promise
=
2306 FileCreatorHelper::CreateFile(global
, file
, bag
, true, error
);
2307 if (NS_WARN_IF(error
.Failed())) {
2308 return error
.StealNSResult();
2311 FileCreationHandler::Create(promise
, this);
2312 waitingForBlobCreation
= true;
2315 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
2316 NS_ASSERTION(mResponseText
.IsEmpty(), "mResponseText should be empty");
2320 if (NS_SUCCEEDED(status
) &&
2321 mResponseType
== XMLHttpRequestResponseType::Blob
&&
2322 !waitingForBlobCreation
) {
2323 // Smaller files may be written in cache map instead of separate files.
2324 // Also, no-store response cannot be written in persistent cache.
2325 nsAutoCString contentType
;
2326 if (!mOverrideMimeType
.IsEmpty()) {
2327 contentType
.Assign(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
2329 mChannel
->GetContentType(contentType
);
2332 // mBlobStorage can be null if the channel is non-file non-cacheable
2333 // and if the response length is zero.
2334 MaybeCreateBlobStorage();
2335 mBlobStorage
->GetBlobImplWhenReady(contentType
, this);
2336 waitingForBlobCreation
= true;
2338 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
2339 NS_ASSERTION(mResponseText
.IsEmpty(), "mResponseText should be empty");
2340 } else if (NS_SUCCEEDED(status
) && !mIsMappedArrayBuffer
&&
2341 mResponseType
== XMLHttpRequestResponseType::Arraybuffer
) {
2342 // set the capacity down to the actual length, to realloc back
2343 // down to the actual size
2344 if (!mArrayBufferBuilder
->SetCapacity(mArrayBufferBuilder
->Length())) {
2345 // this should never happen!
2346 status
= NS_ERROR_UNEXPECTED
;
2350 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
2351 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
2353 channel
->SetNotificationCallbacks(nullptr);
2354 mNotificationCallbacks
= nullptr;
2355 mChannelEventSink
= nullptr;
2356 mProgressEventSink
= nullptr;
2358 bool wasSync
= mFlagSyncLooping
;
2359 mFlagSyncLooping
= false;
2360 mRequestSentTime
= 0;
2362 // update our charset and decoder to match mResponseXML,
2363 // before it is possibly nulled out
2364 MatchCharsetAndDecoderToResponseDocument();
2366 if (NS_FAILED(status
)) {
2367 // This can happen if the server is unreachable. Other possible
2368 // reasons are that the user leaves the page or hits the ESC key.
2370 mErrorLoad
= ErrorType::eUnreachable
;
2371 mErrorLoadDetail
= status
;
2372 mResponseXML
= nullptr;
2374 // Handle network errors specifically per spec.
2375 if (NS_ERROR_GET_MODULE(status
) == NS_ERROR_MODULE_NETWORK
) {
2376 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
2377 ("%p detected networking error 0x%" PRIx32
"\n", this,
2378 static_cast<uint32_t>(status
)));
2379 IgnoredErrorResult rv
;
2380 mFlagParseBody
= false;
2381 RequestErrorSteps(Events::error
, NS_ERROR_DOM_NETWORK_ERR
, rv
);
2382 // RequestErrorSteps will not call ChangeStateToDone for sync XHRs, so we
2383 // do so here to ensure progress events are sent and our state is sane.
2384 if (mFlagSynchronous
) {
2385 ChangeStateToDone(wasSync
);
2390 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
2391 ("%p detected unreachable error 0x%" PRIx32
"\n", this,
2392 static_cast<uint32_t>(status
)));
2395 // If we're uninitialized at this point, we encountered an error
2396 // earlier and listeners have already been notified. Also we do
2397 // not want to do this if we already completed.
2398 if (mState
== XMLHttpRequest_Binding::UNSENT
||
2399 mState
== XMLHttpRequest_Binding::DONE
) {
2403 if (!mResponseXML
) {
2404 mFlagParseBody
= false;
2406 // We postpone the 'done' until the creation of the Blob is completed.
2407 if (!waitingForBlobCreation
) {
2408 ChangeStateToDone(wasSync
);
2415 NS_ASSERTION(!mFlagSyncLooping
,
2416 "We weren't supposed to support HTML parsing with XHR!");
2417 mParseEndListener
= new nsXHRParseEndListener(this);
2418 RefPtr
<EventTarget
> eventTarget
= mResponseXML
;
2419 EventListenerManager
* manager
= eventTarget
->GetOrCreateListenerManager();
2420 manager
->AddEventListenerByType(mParseEndListener
,
2421 kLiteralString_DOMContentLoaded
,
2422 TrustedEventsAtSystemGroupBubble());
2425 mFlagParseBody
= false;
2428 // We might have been sent non-XML data. If that was the case,
2429 // we should null out the document member. The idea in this
2430 // check here is that if there is no document element it is not
2431 // an XML document. We might need a fancier check...
2432 if (!mResponseXML
->GetRootElement()) {
2433 mErrorParsingXML
= true;
2434 mResponseXML
= nullptr;
2436 ChangeStateToDone(wasSync
);
2440 void XMLHttpRequestMainThread::OnBodyParseEnd() {
2441 mFlagParseBody
= false;
2442 mParseEndListener
= nullptr;
2443 ChangeStateToDone(mFlagSyncLooping
);
2446 void XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument() {
2449 mDecoder
->Encoding() != mResponseXML
->GetDocumentCharacterSet())) {
2450 TruncateResponseText();
2451 mResponseBodyDecodedPos
= 0;
2452 mEofDecoded
= false;
2453 mDecoder
= mResponseXML
->GetDocumentCharacterSet()->NewDecoder();
2457 void XMLHttpRequestMainThread::DisconnectDoneNotifier() {
2458 if (mDelayedDoneNotifier
) {
2459 // Disconnect may release the last reference to 'this'.
2460 RefPtr
<XMLHttpRequestMainThread
> kungfuDeathGrip
= this;
2461 mDelayedDoneNotifier
->Disconnect();
2462 mDelayedDoneNotifier
= nullptr;
2466 void XMLHttpRequestMainThread::ChangeStateToDone(bool aWasSync
) {
2468 DisconnectDoneNotifier();
2470 if (!mForWorker
&& !aWasSync
&& mChannel
) {
2471 // If the top level page is loading, try to postpone the handling of the
2473 nsLoadFlags loadFlags
= 0;
2474 mChannel
->GetLoadFlags(&loadFlags
);
2475 if (loadFlags
& nsIRequest::LOAD_BACKGROUND
) {
2476 nsPIDOMWindowInner
* owner
= GetOwner();
2477 BrowsingContext
* bc
= owner
? owner
->GetBrowsingContext() : nullptr;
2478 bc
= bc
? bc
->Top() : nullptr;
2479 if (bc
&& bc
->IsLoading()) {
2480 MOZ_ASSERT(!mDelayedDoneNotifier
);
2481 RefPtr
<XMLHttpRequestDoneNotifier
> notifier
=
2482 new XMLHttpRequestDoneNotifier(this);
2483 mDelayedDoneNotifier
= notifier
;
2484 bc
->AddDeprioritizedLoadRunner(notifier
);
2490 ChangeStateToDoneInternal();
2493 void XMLHttpRequestMainThread::ChangeStateToDoneInternal() {
2495 DisconnectDoneNotifier();
2496 StopProgressEventTimer();
2498 MOZ_ASSERT(!mFlagParseBody
,
2499 "ChangeStateToDone() called before async HTML parsing is done.");
2503 CancelTimeoutTimer();
2505 // Per spec, fire the last download progress event, if any,
2506 // before readystatechange=4/done. (Note that 0-sized responses
2507 // will have not sent a progress event yet, so one must be sent here).
2508 if (!mFlagSynchronous
&&
2509 (!mLoadTransferred
|| mProgressSinceLastProgressEvent
)) {
2510 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
, mLoadTotal
);
2511 mProgressSinceLastProgressEvent
= false;
2514 // Notify the document when an XHR request completes successfully.
2515 // This is used by the password manager as a hint to observe DOM mutations.
2516 // Call this prior to changing state to DONE to ensure we set up the
2517 // observer before mutations occur.
2518 if (mErrorLoad
== ErrorType::eOK
) {
2519 Document
* doc
= GetDocumentIfCurrent();
2521 doc
->NotifyFetchOrXHRSuccess();
2525 // Per spec, fire readystatechange=4/done before final error events.
2526 ChangeState(XMLHttpRequest_Binding::DONE
, true);
2528 // Per spec, if we failed in the upload phase, fire a final error
2529 // and loadend events for the upload after readystatechange=4/done.
2530 if (!mFlagSynchronous
&& mUpload
&& !mUploadComplete
) {
2531 DispatchProgressEvent(mUpload
, Events::error
, 0, -1);
2534 // Per spec, fire download's load/error and loadend events after
2535 // readystatechange=4/done (and of course all upload events).
2536 if (mErrorLoad
!= ErrorType::eOK
) {
2537 DispatchProgressEvent(this, Events::error
, 0, -1);
2539 DispatchProgressEvent(this, Events::load
, mLoadTransferred
, mLoadTotal
);
2542 if (mErrorLoad
!= ErrorType::eOK
) {
2543 // By nulling out channel here we make it so that Send() can test
2544 // for that and throw. Also calling the various status
2545 // methods/members will not throw.
2546 // This matches what IE does.
2551 nsresult
XMLHttpRequestMainThread::CreateChannel() {
2553 // When we are called from JS we can find the load group for the page,
2554 // and add ourselves to it. This way any pending requests
2555 // will be automatically aborted if the user leaves the page.
2556 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetLoadGroup();
2558 nsSecurityFlags secFlags
;
2559 nsLoadFlags loadFlags
= nsIRequest::LOAD_BACKGROUND
;
2560 uint32_t sandboxFlags
= 0;
2561 if (mPrincipal
->IsSystemPrincipal()) {
2562 // When chrome is loading we want to make sure to sandbox any potential
2563 // result document. We also want to allow cross-origin loads.
2564 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
2565 sandboxFlags
= SANDBOXED_ORIGIN
;
2566 } else if (IsSystemXHR()) {
2567 // For pages that have appropriate permissions, we want to still allow
2568 // cross-origin loads, but make sure that the any potential result
2569 // documents get the same principal as the loader.
2570 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
|
2571 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
2572 loadFlags
|= nsIChannel::LOAD_BYPASS_SERVICE_WORKER
;
2574 // Otherwise use CORS. Again, make sure that potential result documents
2575 // use the same principal as the loader.
2576 secFlags
= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
|
2577 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
2581 secFlags
|= nsILoadInfo::SEC_COOKIES_OMIT
;
2584 // Use the responsibleDocument if we have it, except for dedicated workers
2585 // where it will be the parent document, which is not the one we want to use.
2587 nsCOMPtr
<Document
> responsibleDocument
= GetDocumentIfCurrent();
2588 if (responsibleDocument
&&
2589 responsibleDocument
->NodePrincipal() == mPrincipal
) {
2590 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
,
2591 responsibleDocument
, secFlags
,
2592 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2593 nullptr, // aPerformanceStorage
2595 nullptr, // aCallbacks
2596 loadFlags
, nullptr, sandboxFlags
);
2597 } else if (mClientInfo
.isSome()) {
2598 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
, mPrincipal
,
2599 mClientInfo
.ref(), mController
, secFlags
,
2600 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2602 mPerformanceStorage
, // aPerformanceStorage
2604 nullptr, // aCallbacks
2605 loadFlags
, nullptr, sandboxFlags
);
2607 // Otherwise use the principal.
2608 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
, mPrincipal
,
2609 secFlags
, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2611 mPerformanceStorage
, // aPerformanceStorage
2613 nullptr, // aCallbacks
2614 loadFlags
, nullptr, sandboxFlags
);
2616 NS_ENSURE_SUCCESS(rv
, rv
);
2618 if (mCSPEventListener
) {
2619 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2620 rv
= loadInfo
->SetCspEventListener(mCSPEventListener
);
2621 NS_ENSURE_SUCCESS(rv
, rv
);
2624 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2626 rv
= httpChannel
->SetRequestMethod(mRequestMethod
);
2627 NS_ENSURE_SUCCESS(rv
, rv
);
2629 httpChannel
->SetSource(profiler_capture_backtrace());
2631 // Set the initiator type
2632 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChannel
));
2634 timedChannel
->SetInitiatorType(u
"xmlhttprequest"_ns
);
2641 void XMLHttpRequestMainThread::MaybeLowerChannelPriority() {
2642 nsCOMPtr
<Document
> doc
= GetDocumentIfCurrent();
2648 if (!jsapi
.Init(GetOwnerGlobal())) {
2652 JSContext
* cx
= jsapi
.cx();
2654 if (!doc
->IsScriptTracking(cx
)) {
2658 if (StaticPrefs::network_http_tailing_enabled()) {
2659 nsCOMPtr
<nsIClassOfService
> cos
= do_QueryInterface(mChannel
);
2661 // Adding TailAllowed to overrule the Unblocked flag, but to preserve
2662 // the effect of Unblocked when tailing is off.
2663 cos
->AddClassFlags(nsIClassOfService::Throttleable
|
2664 nsIClassOfService::Tail
|
2665 nsIClassOfService::TailAllowed
);
2669 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(mChannel
);
2671 p
->SetPriority(nsISupportsPriority::PRIORITY_LOWEST
);
2675 nsresult
XMLHttpRequestMainThread::InitiateFetch(
2676 already_AddRefed
<nsIInputStream
> aUploadStream
, int64_t aUploadLength
,
2677 nsACString
& aUploadContentType
) {
2680 nsCOMPtr
<nsIInputStream
> uploadStream
= std::move(aUploadStream
);
2682 if (!uploadStream
) {
2683 RefPtr
<PreloaderBase
> preload
= FindPreload();
2685 // Because of bug 682305, we can't let listener be the XHR object itself
2686 // because JS wouldn't be able to use it. So create a listener around
2687 // 'this'. Make sure to hold a strong reference so that we don't leak the
2689 nsCOMPtr
<nsIStreamListener
> listener
=
2690 new net::nsStreamListenerWrapper(this);
2691 rv
= preload
->AsyncConsume(listener
);
2692 if (NS_SUCCEEDED(rv
)) {
2693 mFromPreload
= true;
2695 // May be null when the preload has already finished, but the XHR code
2696 // is safe to live with it.
2697 mChannel
= preload
->Channel();
2698 MOZ_ASSERT(mChannel
);
2699 EnsureChannelContentType();
2707 // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
2708 // in turn keeps STOP button from becoming active. If the consumer passed in
2709 // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
2710 // necko won't generate any progress notifications.
2711 if (HasListenersFor(nsGkAtoms::onprogress
) ||
2712 (mUpload
&& mUpload
->HasListenersFor(nsGkAtoms::onprogress
))) {
2713 nsLoadFlags loadFlags
;
2714 mChannel
->GetLoadFlags(&loadFlags
);
2715 loadFlags
&= ~nsIRequest::LOAD_BACKGROUND
;
2716 loadFlags
|= nsIRequest::LOAD_NORMAL
;
2717 mChannel
->SetLoadFlags(loadFlags
);
2720 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2722 // If the user hasn't overridden the Accept header, set it to */* per spec.
2723 if (!mAuthorRequestHeaders
.Has("accept")) {
2724 mAuthorRequestHeaders
.Set("accept", "*/*"_ns
);
2727 mAuthorRequestHeaders
.ApplyToChannel(httpChannel
, false, false);
2729 if (!IsSystemXHR()) {
2730 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
2731 nsCOMPtr
<Document
> doc
= owner
? owner
->GetExtantDoc() : nullptr;
2732 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2733 ReferrerInfo::CreateForFetch(mPrincipal
, doc
);
2734 Unused
<< httpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
2737 // Some extensions override the http protocol handler and provide their own
2738 // implementation. The channels returned from that implementation don't
2739 // always seem to implement the nsIUploadChannel2 interface, presumably
2740 // because it's a new interface. Eventually we should remove this and simply
2741 // require that http channels implement the new interface (see bug 529041).
2742 nsCOMPtr
<nsIUploadChannel2
> uploadChannel2
= do_QueryInterface(httpChannel
);
2743 if (!uploadChannel2
) {
2744 nsCOMPtr
<nsIConsoleService
> consoleService
=
2745 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
2746 if (consoleService
) {
2747 consoleService
->LogStringMessage(
2748 u
"Http channel implementation doesn't support nsIUploadChannel2. "
2749 "An extension has supplied a non-functional http protocol handler. "
2750 "This will break behavior and in future releases not work at all.");
2755 // If necessary, wrap the stream in a buffered stream so as to guarantee
2756 // support for our upload when calling ExplicitSetUploadStream.
2757 if (!NS_InputStreamIsBuffered(uploadStream
)) {
2758 nsCOMPtr
<nsIInputStream
> bufferedStream
;
2759 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
),
2760 uploadStream
.forget(), 4096);
2761 NS_ENSURE_SUCCESS(rv
, rv
);
2763 uploadStream
= bufferedStream
;
2766 // We want to use a newer version of the upload channel that won't
2767 // ignore the necessary headers for an empty Content-Type.
2768 nsCOMPtr
<nsIUploadChannel2
> uploadChannel2(
2769 do_QueryInterface(httpChannel
));
2770 // This assertion will fire if buggy extensions are installed
2771 NS_ASSERTION(uploadChannel2
, "http must support nsIUploadChannel2");
2772 if (uploadChannel2
) {
2773 uploadChannel2
->ExplicitSetUploadStream(
2774 uploadStream
, aUploadContentType
, mUploadTotal
, mRequestMethod
,
2777 // The http channel doesn't support the new nsIUploadChannel2.
2778 // Emulate it as best we can using nsIUploadChannel.
2779 if (aUploadContentType
.IsEmpty()) {
2780 aUploadContentType
.AssignLiteral("application/octet-stream");
2782 nsCOMPtr
<nsIUploadChannel
> uploadChannel
=
2783 do_QueryInterface(httpChannel
);
2784 uploadChannel
->SetUploadStream(uploadStream
, aUploadContentType
,
2786 // Reset the method to its original value
2787 rv
= httpChannel
->SetRequestMethod(mRequestMethod
);
2788 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2793 // Should set a Content-Range header for blob scheme, and also slice the
2794 // blob appropriately, so we process the Range header here for later use.
2795 if (IsBlobURI(mRequestURL
)) {
2796 nsAutoCString range
;
2797 mAuthorRequestHeaders
.Get("range", range
);
2798 if (!range
.IsVoid()) {
2799 rv
= NS_SetChannelContentRangeForBlobURI(mChannel
, mRequestURL
, range
);
2800 if (mFlagSynchronous
&& NS_FAILED(rv
)) {
2801 // We later fire an error progress event for non-sync
2802 mState
= XMLHttpRequest_Binding::DONE
;
2803 return NS_ERROR_DOM_NETWORK_ERR
;
2808 // Due to the chrome-only XHR.channel API, we need a hacky way to set the
2809 // SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
2810 // .withCredentials can be called after open() is called.
2811 // Not doing this for privileged system XHRs since those don't use CORS.
2812 if (!IsSystemXHR() && !mIsAnon
&& mFlagACwithCredentials
) {
2813 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2814 static_cast<net::LoadInfo
*>(loadInfo
.get())->SetIncludeCookiesSecFlag();
2817 // We never let XHR be blocked by head CSS/JS loads to avoid potential
2818 // deadlock where server generation of CSS/JS requires an XHR signal.
2819 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(mChannel
));
2821 cos
->AddClassFlags(nsIClassOfService::Unblocked
);
2823 // Mark channel as urgent-start if the XHR is triggered by user input
2825 if (UserActivation::IsHandlingUserInput()) {
2826 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
2830 // Disable Necko-internal response timeouts.
2831 nsCOMPtr
<nsIHttpChannelInternal
> internalHttpChannel(
2832 do_QueryInterface(mChannel
));
2833 if (internalHttpChannel
) {
2834 rv
= internalHttpChannel
->SetResponseTimeoutEnabled(false);
2835 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2839 AddLoadFlags(mChannel
, nsIChannel::LOAD_EXPLICIT_CREDENTIALS
);
2842 // Bypass the network cache in cases where it makes no sense:
2843 // POST responses are always unique, and we provide no API that would
2844 // allow our consumers to specify a "cache key" to access old POST
2845 // responses, so they are not worth caching.
2846 if (mRequestMethod
.EqualsLiteral("POST")) {
2847 AddLoadFlags(mChannel
, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE
|
2848 nsIRequest::INHIBIT_CACHING
);
2850 // When we are sync loading, we need to bypass the local cache when it would
2851 // otherwise block us waiting for exclusive access to the cache. If we
2852 // don't do this, then we could dead lock in some cases (see bug 309424).
2854 // Also don't block on the cache entry on async if it is busy - favoring
2855 // parallelism over cache hit rate for xhr. This does not disable the cache
2856 // everywhere - only in cases where more than one channel for the same URI
2857 // is accessed simultanously.
2858 AddLoadFlags(mChannel
, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
);
2861 EnsureChannelContentType();
2863 // Set up the preflight if needed
2864 if (!IsSystemXHR()) {
2865 nsTArray
<nsCString
> CORSUnsafeHeaders
;
2866 mAuthorRequestHeaders
.GetCORSUnsafeHeaders(CORSUnsafeHeaders
);
2867 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2868 loadInfo
->SetCorsPreflightInfo(CORSUnsafeHeaders
,
2869 mFlagHadUploadListenersOnSend
);
2872 // Hook us up to listen to redirects and the like. Only do this very late
2873 // since this creates a cycle between the channel and us. This cycle has
2874 // to be manually broken if anything below fails.
2875 mChannel
->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks
));
2876 mChannel
->SetNotificationCallbacks(this);
2878 if (internalHttpChannel
) {
2879 internalHttpChannel
->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
2882 // Because of bug 682305, we can't let listener be the XHR object itself
2883 // because JS wouldn't be able to use it. So create a listener around 'this'.
2884 // Make sure to hold a strong reference so that we don't leak the wrapper.
2885 nsCOMPtr
<nsIStreamListener
> listener
= new net::nsStreamListenerWrapper(this);
2887 // Check if this XHR is created from a tracking script.
2888 // If yes, lower the channel's priority.
2889 if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
2890 MaybeLowerChannelPriority();
2893 // Associate any originating stack with the channel.
2894 NotifyNetworkMonitorAlternateStack(mChannel
, std::move(mOriginStack
));
2896 // Start reading from the channel
2897 rv
= mChannel
->AsyncOpen(listener
);
2899 if (NS_WARN_IF(NS_FAILED(rv
))) {
2900 // Drop our ref to the channel to avoid cycles. Also drop channel's
2901 // ref to us to be extra safe.
2902 mChannel
->SetNotificationCallbacks(mNotificationCallbacks
);
2905 mErrorLoad
= ErrorType::eChannelOpen
;
2906 mErrorLoadDetail
= rv
;
2908 // Per spec, we throw on sync errors, but not async.
2909 if (mFlagSynchronous
) {
2910 mState
= XMLHttpRequest_Binding::DONE
;
2911 return NS_ERROR_DOM_NETWORK_ERR
;
2918 already_AddRefed
<PreloaderBase
> XMLHttpRequestMainThread::FindPreload() {
2919 Document
* doc
= GetDocumentIfCurrent();
2923 if (mPrincipal
->IsSystemPrincipal() || IsSystemXHR()) {
2926 if (!mRequestMethod
.EqualsLiteral("GET")) {
2927 // Preload can only do GET.
2930 if (!mAuthorRequestHeaders
.IsEmpty()) {
2931 // Preload can't set headers.
2935 // mIsAnon overrules mFlagACwithCredentials.
2936 CORSMode cors
= (mIsAnon
|| !mFlagACwithCredentials
)
2937 ? CORSMode::CORS_ANONYMOUS
2938 : CORSMode::CORS_USE_CREDENTIALS
;
2939 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2940 ReferrerInfo::CreateForFetch(mPrincipal
, doc
);
2941 auto key
= PreloadHashKey::CreateAsFetch(mRequestURL
, cors
);
2942 RefPtr
<PreloaderBase
> preload
= doc
->Preloads().LookupPreload(key
);
2947 preload
->RemoveSelf(doc
);
2948 preload
->NotifyUsage(doc
, PreloaderBase::LoadBackground::Keep
);
2950 return preload
.forget();
2953 void XMLHttpRequestMainThread::EnsureChannelContentType() {
2954 MOZ_ASSERT(mChannel
);
2956 // We don't mess with the content type of a blob URL.
2957 if (IsBlobURI(mRequestURL
)) {
2961 // Since we expect XML data, set the type hint accordingly
2962 // if the channel doesn't know any content type.
2963 // This means that we always try to parse local files as XML
2964 // ignoring return value, as this is not critical. Use text/xml as fallback
2966 nsAutoCString contentType
;
2967 if (NS_FAILED(mChannel
->GetContentType(contentType
)) ||
2968 contentType
.IsEmpty() ||
2969 contentType
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
2970 mChannel
->SetContentType("text/xml"_ns
);
2974 void XMLHttpRequestMainThread::ResumeTimeout() {
2976 MOZ_ASSERT(NS_IsMainThread());
2977 MOZ_ASSERT(mFlagSynchronous
);
2979 if (mResumeTimeoutRunnable
) {
2980 DispatchToMainThread(mResumeTimeoutRunnable
.forget());
2981 mResumeTimeoutRunnable
= nullptr;
2985 void XMLHttpRequestMainThread::Send(
2987 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
>&
2990 DEBUG_WORKERREFS1(mRequestURL
);
2991 NOT_CALLABLE_IN_SYNC_SEND_RV
2993 if (!CanSend(aRv
)) {
2997 if (aData
.IsNull()) {
2998 SendInternal(nullptr, false, aRv
);
3002 if (aData
.Value().IsDocument()) {
3003 BodyExtractor
<Document
> body(&aData
.Value().GetAsDocument());
3004 SendInternal(&body
, true, aRv
);
3008 if (aData
.Value().IsBlob()) {
3009 BodyExtractor
<const Blob
> body(&aData
.Value().GetAsBlob());
3010 SendInternal(&body
, false, aRv
);
3014 if (aData
.Value().IsArrayBuffer()) {
3015 BodyExtractor
<const ArrayBuffer
> body(&aData
.Value().GetAsArrayBuffer());
3016 SendInternal(&body
, false, aRv
);
3020 if (aData
.Value().IsArrayBufferView()) {
3021 BodyExtractor
<const ArrayBufferView
> body(
3022 &aData
.Value().GetAsArrayBufferView());
3023 SendInternal(&body
, false, aRv
);
3027 if (aData
.Value().IsFormData()) {
3028 BodyExtractor
<const FormData
> body(&aData
.Value().GetAsFormData());
3029 SendInternal(&body
, false, aRv
);
3033 if (aData
.Value().IsURLSearchParams()) {
3034 BodyExtractor
<const URLSearchParams
> body(
3035 &aData
.Value().GetAsURLSearchParams());
3036 SendInternal(&body
, false, aRv
);
3040 if (aData
.Value().IsUSVString()) {
3041 BodyExtractor
<const nsAString
> body(&aData
.Value().GetAsUSVString());
3042 SendInternal(&body
, true, aRv
);
3047 nsresult
XMLHttpRequestMainThread::MaybeSilentSendFailure(nsresult aRv
) {
3048 // Per spec, silently fail on async request failures; throw for sync.
3049 if (mFlagSynchronous
) {
3050 mState
= XMLHttpRequest_Binding::DONE
;
3051 return NS_ERROR_DOM_NETWORK_ERR
;
3054 // Defer the actual sending of async events just in case listeners
3055 // are attached after the send() method is called.
3056 Unused
<< NS_WARN_IF(
3057 NS_FAILED(DispatchToMainThread(NewRunnableMethod
<ErrorProgressEventType
>(
3058 "dom::XMLHttpRequestMainThread::CloseRequestWithError", this,
3059 &XMLHttpRequestMainThread::CloseRequestWithError
, Events::error
))));
3063 bool XMLHttpRequestMainThread::CanSend(ErrorResult
& aRv
) {
3065 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
3070 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
3071 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
3077 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3081 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
3082 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
3089 void XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase
* aBody
,
3090 bool aBodyIsDocumentOrString
,
3093 MOZ_ASSERT(NS_IsMainThread());
3095 // We expect that CanSend has been called before we get here!
3096 // We cannot move the remaining two checks below there because
3097 // MaybeSilentSendFailure can cause unexpected side effects if called
3100 // If open() failed to create the channel, then throw a network error
3101 // as per spec. We really should create the channel here in send(), but
3102 // we have internal code relying on the channel being created in open().
3104 mErrorLoad
= ErrorType::eChannelOpen
;
3105 mErrorLoadDetail
= NS_ERROR_DOM_NETWORK_ERR
;
3106 mFlagSend
= true; // so CloseRequestWithError sets us to DONE.
3107 aRv
= MaybeSilentSendFailure(mErrorLoadDetail
);
3111 // non-GET requests aren't allowed for blob.
3112 if (IsBlobURI(mRequestURL
) && !mRequestMethod
.EqualsLiteral("GET")) {
3113 mErrorLoad
= ErrorType::eChannelOpen
;
3114 mErrorLoadDetail
= NS_ERROR_DOM_NETWORK_ERR
;
3115 mFlagSend
= true; // so CloseRequestWithError sets us to DONE.
3116 aRv
= MaybeSilentSendFailure(mErrorLoadDetail
);
3120 // XXX We should probably send a warning to the JS console
3121 // if there are no event listeners set and we are doing
3122 // an asynchronous call.
3124 mUploadTransferred
= 0;
3126 // By default we don't have any upload, so mark upload complete.
3127 mUploadComplete
= true;
3128 mErrorLoad
= ErrorType::eOK
;
3129 mErrorLoadDetail
= NS_OK
;
3131 nsCOMPtr
<nsIInputStream
> uploadStream
;
3132 nsAutoCString uploadContentType
;
3133 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
3134 if (aBody
&& httpChannel
&& !mRequestMethod
.EqualsLiteral("GET") &&
3135 !mRequestMethod
.EqualsLiteral("HEAD")) {
3136 nsAutoCString charset
;
3137 nsAutoCString defaultContentType
;
3139 aRv
= aBody
->GetAsStream(getter_AddRefs(uploadStream
), &size_u64
,
3140 defaultContentType
, charset
);
3145 // make sure it fits within js MAX_SAFE_INTEGER
3147 net::InScriptableRange(size_u64
) ? static_cast<int64_t>(size_u64
) : -1;
3150 // If author set no Content-Type, use the default from GetAsStream().
3151 mAuthorRequestHeaders
.Get("content-type", uploadContentType
);
3152 if (uploadContentType
.IsVoid()) {
3153 uploadContentType
= defaultContentType
;
3154 } else if (aBodyIsDocumentOrString
) {
3155 RefPtr
<CMimeType
> contentTypeRecord
=
3156 CMimeType::Parse(uploadContentType
);
3157 nsAutoCString charset
;
3158 if (contentTypeRecord
&&
3159 contentTypeRecord
->GetParameterValue(kLiteralString_charset
,
3161 !charset
.EqualsIgnoreCase("utf-8")) {
3162 contentTypeRecord
->SetParameterValue(kLiteralString_charset
,
3163 kLiteralString_UTF_8
);
3164 contentTypeRecord
->Serialize(uploadContentType
);
3166 } else if (!charset
.IsEmpty()) {
3167 // We don't want to set a charset for streams.
3168 // Replace all case-insensitive matches of the charset in the
3169 // content-type with the correct case.
3170 RequestHeaders::CharsetIterator
iter(uploadContentType
);
3171 while (iter
.Next()) {
3172 if (!iter
.Equals(charset
, nsCaseInsensitiveCStringComparator
)) {
3173 iter
.Replace(charset
);
3178 mUploadComplete
= false;
3184 // Check if we should enable cross-origin upload listeners.
3185 if (mUpload
&& mUpload
->HasListeners()) {
3186 mFlagHadUploadListenersOnSend
= true;
3189 mIsMappedArrayBuffer
= false;
3190 if (mResponseType
== XMLHttpRequestResponseType::Arraybuffer
&&
3191 StaticPrefs::dom_mapped_arraybuffer_enabled()) {
3192 nsCOMPtr
<nsIURI
> uri
;
3193 nsAutoCString scheme
;
3195 aRv
= mChannel
->GetURI(getter_AddRefs(uri
));
3196 if (!aRv
.Failed()) {
3197 uri
->GetScheme(scheme
);
3198 if (scheme
.LowerCaseEqualsLiteral("jar")) {
3199 mIsMappedArrayBuffer
= true;
3204 aRv
= InitiateFetch(uploadStream
.forget(), mUploadTotal
, uploadContentType
);
3209 // Start our timeout
3210 mRequestSentTime
= PR_Now();
3211 StartTimeoutTimer();
3213 mWaitingForOnStopRequest
= true;
3218 // If we're synchronous, spin an event loop here and wait
3219 RefPtr
<Document
> suspendedDoc
;
3220 if (mFlagSynchronous
) {
3221 auto scopeExit
= MakeScopeExit([&] {
3222 CancelSyncTimeoutTimer();
3224 ResumeEventDispatching();
3226 Maybe
<AutoSuppressEventHandling
> autoSuppress
;
3228 mFlagSyncLooping
= true;
3231 if (nsCOMPtr
<nsPIDOMWindowOuter
> topWindow
=
3232 GetOwner()->GetOuterWindow()->GetInProcessTop()) {
3233 if (nsCOMPtr
<nsPIDOMWindowInner
> topInner
=
3234 topWindow
->GetCurrentInnerWindow()) {
3235 suspendedDoc
= topWindow
->GetExtantDoc();
3236 autoSuppress
.emplace(topWindow
->GetBrowsingContext());
3237 topInner
->Suspend();
3238 mResumeTimeoutRunnable
= new nsResumeTimeoutsEvent(topInner
);
3243 SuspendEventDispatching();
3244 StopProgressEventTimer();
3246 SyncTimeoutType syncTimeoutType
= MaybeStartSyncTimeoutTimer();
3247 if (syncTimeoutType
== eErrorOrExpired
) {
3249 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
3253 nsAutoSyncOperation
sync(suspendedDoc
,
3254 SyncOperationBehavior::eSuspendInput
);
3255 if (!SpinEventLoopUntil("XMLHttpRequestMainThread::SendInternal"_ns
,
3256 [&]() { return !mFlagSyncLooping
; })) {
3257 aRv
.Throw(NS_ERROR_UNEXPECTED
);
3261 // Time expired... We should throw.
3262 if (syncTimeoutType
== eTimerStarted
&& !mSyncTimeoutTimer
) {
3263 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
3267 // Now that we've successfully opened the channel, we can change state. Note
3268 // that this needs to come after the AsyncOpen() and rv check, because this
3269 // can run script that would try to restart this request, and that could end
3270 // up doing our AsyncOpen on a null channel if the reentered AsyncOpen
3272 StopProgressEventTimer();
3274 // Upload phase beginning; start the progress event timer if necessary.
3275 if (mUpload
&& mUpload
->HasListenersFor(nsGkAtoms::onprogress
)) {
3276 StartProgressEventTimer();
3278 // Dispatch loadstart events
3279 DispatchProgressEvent(this, Events::loadstart
, 0, -1);
3280 if (mUpload
&& !mUploadComplete
) {
3281 DispatchProgressEvent(mUpload
, Events::loadstart
, 0, mUploadTotal
);
3286 aRv
= MaybeSilentSendFailure(NS_ERROR_DOM_NETWORK_ERR
);
3290 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
3291 void XMLHttpRequestMainThread::SetRequestHeader(const nsACString
& aName
,
3292 const nsACString
& aValue
,
3294 NOT_CALLABLE_IN_SYNC_SEND_RV
3297 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
3298 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
3304 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3309 nsAutoCString value
;
3310 NS_TrimHTTPWhitespace(aValue
, value
);
3313 if (!NS_IsValidHTTPToken(aName
) || !NS_IsReasonableHTTPHeaderValue(value
)) {
3314 aRv
.Throw(NS_ERROR_DOM_INVALID_HEADER_NAME
);
3319 bool isPrivilegedCaller
= IsSystemXHR();
3320 bool isForbiddenHeader
=
3321 nsContentUtils::IsForbiddenRequestHeader(aName
, aValue
);
3322 if (!isPrivilegedCaller
&& isForbiddenHeader
) {
3323 AutoTArray
<nsString
, 1> params
;
3324 CopyUTF8toUTF16(aName
, *params
.AppendElement());
3325 LogMessage("ForbiddenHeaderWarning", GetOwner(), params
);
3330 // Skipping for now, as normalizing the case of header names may not be
3331 // web-compatible. See bug 1285036.
3334 // Gecko-specific: invalid headers can be set by privileged
3335 // callers, but will not merge.
3336 if (isPrivilegedCaller
&& isForbiddenHeader
) {
3337 mAuthorRequestHeaders
.Set(aName
, value
);
3339 mAuthorRequestHeaders
.MergeOrSet(aName
, value
);
3343 void XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout
, ErrorResult
& aRv
) {
3344 NOT_CALLABLE_IN_SYNC_SEND_RV
3346 if (mFlagSynchronous
&& mState
!= XMLHttpRequest_Binding::UNSENT
&&
3347 HasOrHasHadOwner()) {
3348 /* Timeout is not supported for synchronous requests with an owning window,
3350 LogMessage("TimeoutSyncXHRWarning", GetOwner());
3351 aRv
.ThrowInvalidAccessError(
3352 "synchronous XMLHttpRequests do not support timeout and responseType");
3356 mTimeoutMilliseconds
= aTimeout
;
3357 if (mRequestSentTime
) {
3358 StartTimeoutTimer();
3362 nsIEventTarget
* XMLHttpRequestMainThread::GetTimerEventTarget() {
3363 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3364 return global
->SerialEventTarget();
3369 nsresult
XMLHttpRequestMainThread::DispatchToMainThread(
3370 already_AddRefed
<nsIRunnable
> aRunnable
) {
3372 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3373 return global
->Dispatch(std::move(aRunnable
));
3375 return NS_DispatchToMainThread(std::move(aRunnable
));
3378 void XMLHttpRequestMainThread::StartTimeoutTimer() {
3382 "StartTimeoutTimer mustn't be called before the request was sent!");
3383 if (mState
== XMLHttpRequest_Binding::DONE
) {
3388 CancelTimeoutTimer();
3390 if (!mTimeoutMilliseconds
) {
3394 if (!mTimeoutTimer
) {
3395 mTimeoutTimer
= NS_NewTimer(GetTimerEventTarget());
3398 (uint32_t)((PR_Now() - mRequestSentTime
) / PR_USEC_PER_MSEC
);
3399 mTimeoutTimer
->InitWithCallback(
3400 this, mTimeoutMilliseconds
> elapsed
? mTimeoutMilliseconds
- elapsed
: 0,
3401 nsITimer::TYPE_ONE_SHOT
);
3404 uint16_t XMLHttpRequestMainThread::ReadyState() const { return mState
; }
3406 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString
& aMimeType
,
3408 NOT_CALLABLE_IN_SYNC_SEND_RV
3410 if (mState
== XMLHttpRequest_Binding::LOADING
||
3411 mState
== XMLHttpRequest_Binding::DONE
) {
3412 aRv
.ThrowInvalidStateError(
3413 "Cannot call 'overrideMimeType()' on XMLHttpRequest after 'send()' "
3414 "(when its state is LOADING or DONE).");
3418 RefPtr
<MimeType
> parsed
= MimeType::Parse(aMimeType
);
3420 parsed
->Serialize(mOverrideMimeType
);
3422 mOverrideMimeType
.AssignLiteral(APPLICATION_OCTET_STREAM
);
3426 bool XMLHttpRequestMainThread::MozBackgroundRequest() const {
3427 return mFlagBackgroundRequest
;
3430 void XMLHttpRequestMainThread::SetMozBackgroundRequestExternal(
3431 bool aMozBackgroundRequest
, ErrorResult
& aRv
) {
3432 if (!IsSystemXHR()) {
3433 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
3437 if (mState
!= XMLHttpRequest_Binding::UNSENT
) {
3438 // Can't change this while we're in the middle of something.
3439 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3443 mFlagBackgroundRequest
= aMozBackgroundRequest
;
3446 void XMLHttpRequestMainThread::SetMozBackgroundRequest(
3447 bool aMozBackgroundRequest
, ErrorResult
& aRv
) {
3448 // No errors for this webIDL method on main-thread.
3449 SetMozBackgroundRequestExternal(aMozBackgroundRequest
, IgnoreErrors());
3452 void XMLHttpRequestMainThread::SetOriginStack(
3453 UniquePtr
<SerializedStackHolder
> aOriginStack
) {
3454 mOriginStack
= std::move(aOriginStack
);
3457 void XMLHttpRequestMainThread::SetSource(
3458 UniquePtr
<ProfileChunkedBuffer
> aSource
) {
3462 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
3465 httpChannel
->SetSource(std::move(aSource
));
3469 bool XMLHttpRequestMainThread::WithCredentials() const {
3470 return mFlagACwithCredentials
;
3473 void XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials
,
3475 NOT_CALLABLE_IN_SYNC_SEND_RV
3477 // Return error if we're already processing a request. Note that we can't use
3478 // ReadyState() here, because it can't differentiate between "opened" and
3479 // "sent", so we use mState directly.
3481 if ((mState
!= XMLHttpRequest_Binding::UNSENT
&&
3482 mState
!= XMLHttpRequest_Binding::OPENED
) ||
3483 mFlagSend
|| mIsAnon
) {
3484 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3488 mFlagACwithCredentials
= aWithCredentials
;
3491 nsresult
XMLHttpRequestMainThread::ChangeState(uint16_t aState
,
3494 nsresult rv
= NS_OK
;
3496 if (aState
!= XMLHttpRequest_Binding::HEADERS_RECEIVED
&&
3497 aState
!= XMLHttpRequest_Binding::LOADING
) {
3498 StopProgressEventTimer();
3502 (!mFlagSynchronous
|| aState
== XMLHttpRequest_Binding::OPENED
||
3503 aState
== XMLHttpRequest_Binding::DONE
)) {
3504 rv
= FireReadystatechangeEvent();
3510 /////////////////////////////////////////////////////
3511 // nsIChannelEventSink methods:
3514 XMLHttpRequestMainThread::AsyncOnChannelRedirect(
3515 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
3516 nsIAsyncVerifyRedirectCallback
* callback
) {
3518 MOZ_ASSERT(aNewChannel
, "Redirect without a channel?");
3520 // Prepare to receive callback
3521 mRedirectCallback
= callback
;
3522 mNewRedirectChannel
= aNewChannel
;
3524 if (mChannelEventSink
) {
3525 nsCOMPtr
<nsIAsyncVerifyRedirectCallback
> fwd
= EnsureXPCOMifier();
3527 nsresult rv
= mChannelEventSink
->AsyncOnChannelRedirect(
3528 aOldChannel
, aNewChannel
, aFlags
, fwd
);
3529 if (NS_FAILED(rv
)) {
3530 mRedirectCallback
= nullptr;
3531 mNewRedirectChannel
= nullptr;
3536 // we need to strip Authentication headers for cross-origin requests
3537 // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
3539 StaticPrefs::network_fetch_redirect_stripAuthHeader() &&
3540 NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel
, aNewChannel
, aFlags
);
3542 OnRedirectVerifyCallback(NS_OK
, stripAuth
);
3547 nsresult
XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result
,
3550 NS_ASSERTION(mRedirectCallback
, "mRedirectCallback not set in callback");
3551 NS_ASSERTION(mNewRedirectChannel
, "mNewRedirectChannel not set in callback");
3553 if (NS_SUCCEEDED(result
)) {
3554 bool rewriteToGET
= false;
3555 nsCOMPtr
<nsIHttpChannel
> oldHttpChannel
= GetCurrentHttpChannel();
3557 Unused
<< oldHttpChannel
->ShouldStripRequestBodyHeader(mRequestMethod
,
3560 mChannel
= mNewRedirectChannel
;
3562 nsCOMPtr
<nsIHttpChannel
> newHttpChannel(do_QueryInterface(mChannel
));
3563 if (newHttpChannel
) {
3564 // Ensure all original headers are duplicated for the new channel (bug
3566 mAuthorRequestHeaders
.ApplyToChannel(newHttpChannel
, rewriteToGET
,
3570 mErrorLoad
= ErrorType::eRedirect
;
3571 mErrorLoadDetail
= result
;
3574 mNewRedirectChannel
= nullptr;
3576 mRedirectCallback
->OnRedirectVerifyCallback(result
);
3577 mRedirectCallback
= nullptr;
3579 // It's important that we return success here. If we return the result code
3580 // that we were passed, JavaScript callers who cancel the redirect will wind
3581 // up throwing an exception in the process.
3585 /////////////////////////////////////////////////////
3586 // nsIProgressEventSink methods:
3590 XMLHttpRequestMainThread::OnProgress(nsIRequest
* aRequest
, int64_t aProgress
,
3591 int64_t aProgressMax
) {
3593 // When uploading, OnProgress reports also headers in aProgress and
3594 // aProgressMax. So, try to remove the headers, if possible.
3595 bool lengthComputable
= (aProgressMax
!= -1);
3596 if (InUploadPhase()) {
3597 int64_t loaded
= aProgress
;
3598 if (lengthComputable
) {
3599 int64_t headerSize
= aProgressMax
- mUploadTotal
;
3600 loaded
-= headerSize
;
3602 mUploadTransferred
= loaded
;
3603 mProgressSinceLastProgressEvent
= true;
3605 if (!mFlagSynchronous
&& !mProgressTimerIsActive
) {
3606 StartProgressEventTimer();
3609 mLoadTotal
= aProgressMax
;
3610 mLoadTransferred
= aProgress
;
3611 // OnDataAvailable() handles mProgressSinceLastProgressEvent
3612 // for the download phase.
3615 if (mProgressEventSink
) {
3616 mProgressEventSink
->OnProgress(aRequest
, aProgress
, aProgressMax
);
3623 XMLHttpRequestMainThread::OnStatus(nsIRequest
* aRequest
, nsresult aStatus
,
3624 const char16_t
* aStatusArg
) {
3626 if (mProgressEventSink
) {
3627 mProgressEventSink
->OnStatus(aRequest
, aStatus
, aStatusArg
);
3633 bool XMLHttpRequestMainThread::AllowUploadProgress() {
3634 return !IsCrossSiteCORSRequest() || mFlagHadUploadListenersOnSend
;
3637 /////////////////////////////////////////////////////
3638 // nsIInterfaceRequestor methods:
3641 XMLHttpRequestMainThread::GetInterface(const nsIID
& aIID
, void** aResult
) {
3644 // Make sure to return ourselves for the channel event sink interface and
3645 // progress event sink interface, no matter what. We can forward these to
3646 // mNotificationCallbacks if it wants to get notifications for them. But we
3647 // need to see these notifications for proper functioning.
3648 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
3649 mChannelEventSink
= do_GetInterface(mNotificationCallbacks
);
3650 *aResult
= static_cast<nsIChannelEventSink
*>(EnsureXPCOMifier().take());
3652 } else if (aIID
.Equals(NS_GET_IID(nsIProgressEventSink
))) {
3653 mProgressEventSink
= do_GetInterface(mNotificationCallbacks
);
3654 *aResult
= static_cast<nsIProgressEventSink
*>(EnsureXPCOMifier().take());
3658 // Now give mNotificationCallbacks (if non-null) a chance to return the
3659 // desired interface.
3660 if (mNotificationCallbacks
) {
3661 rv
= mNotificationCallbacks
->GetInterface(aIID
, aResult
);
3662 if (NS_SUCCEEDED(rv
)) {
3663 NS_ASSERTION(*aResult
, "Lying nsIInterfaceRequestor implementation!");
3668 if (!mFlagBackgroundRequest
&& (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
3669 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
)))) {
3670 nsCOMPtr
<nsIPromptFactory
> wwatch
=
3671 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
3672 NS_ENSURE_SUCCESS(rv
, rv
);
3674 // Get the an auth prompter for our window so that the parenting
3675 // of the dialogs works as it should when using tabs.
3676 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
3678 window
= GetOwner()->GetOuterWindow();
3680 return wwatch
->GetPrompt(window
, aIID
, reinterpret_cast<void**>(aResult
));
3683 // Now check for the various XHR non-DOM interfaces, except
3684 // nsIProgressEventSink and nsIChannelEventSink which we already
3686 if (aIID
.Equals(NS_GET_IID(nsIStreamListener
))) {
3687 *aResult
= static_cast<nsIStreamListener
*>(EnsureXPCOMifier().take());
3690 if (aIID
.Equals(NS_GET_IID(nsIRequestObserver
))) {
3691 *aResult
= static_cast<nsIRequestObserver
*>(EnsureXPCOMifier().take());
3694 if (aIID
.Equals(NS_GET_IID(nsITimerCallback
))) {
3695 *aResult
= static_cast<nsITimerCallback
*>(EnsureXPCOMifier().take());
3699 return QueryInterface(aIID
, aResult
);
3702 void XMLHttpRequestMainThread::GetInterface(
3703 JSContext
* aCx
, JS::Handle
<JS::Value
> aIID
,
3704 JS::MutableHandle
<JS::Value
> aRetval
, ErrorResult
& aRv
) {
3705 dom::GetInterface(aCx
, this, aIID
, aRetval
, aRv
);
3708 XMLHttpRequestUpload
* XMLHttpRequestMainThread::GetUpload(ErrorResult
& aRv
) {
3710 mUpload
= new XMLHttpRequestUpload(this);
3715 bool XMLHttpRequestMainThread::MozAnon() const { return mIsAnon
; }
3717 bool XMLHttpRequestMainThread::MozSystem() const { return IsSystemXHR(); }
3719 void XMLHttpRequestMainThread::HandleTimeoutCallback() {
3721 if (mState
== XMLHttpRequest_Binding::DONE
) {
3722 MOZ_ASSERT_UNREACHABLE(
3723 "XMLHttpRequestMainThread::HandleTimeoutCallback "
3724 "with completed request");
3729 mFlagTimedOut
= true;
3730 CloseRequestWithError(Events::timeout
);
3733 void XMLHttpRequestMainThread::CancelTimeoutTimer() {
3735 if (mTimeoutTimer
) {
3736 mTimeoutTimer
->Cancel();
3737 mTimeoutTimer
= nullptr;
3742 XMLHttpRequestMainThread::Notify(nsITimer
* aTimer
) {
3744 if (mProgressNotifier
== aTimer
) {
3745 HandleProgressTimerCallback();
3749 if (mTimeoutTimer
== aTimer
) {
3750 HandleTimeoutCallback();
3754 if (mSyncTimeoutTimer
== aTimer
) {
3755 HandleSyncTimeoutTimer();
3759 // Just in case some JS user wants to QI to nsITimerCallback and play with
3761 NS_WARNING("Unexpected timer!");
3762 return NS_ERROR_INVALID_POINTER
;
3765 void XMLHttpRequestMainThread::HandleProgressTimerCallback() {
3767 // Don't fire the progress event if mLoadTotal is 0, see XHR spec step 6.1
3768 if (!mLoadTotal
&& mLoadTransferred
) {
3772 mProgressTimerIsActive
= false;
3774 if (!mProgressSinceLastProgressEvent
|| mErrorLoad
!= ErrorType::eOK
) {
3778 if (InUploadPhase()) {
3779 if (mUpload
&& !mUploadComplete
&& mFlagHadUploadListenersOnSend
) {
3780 DispatchProgressEvent(mUpload
, Events::progress
, mUploadTransferred
,
3784 FireReadystatechangeEvent();
3785 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
, mLoadTotal
);
3788 mProgressSinceLastProgressEvent
= false;
3790 StartProgressEventTimer();
3793 void XMLHttpRequestMainThread::StopProgressEventTimer() {
3794 if (mProgressNotifier
) {
3795 mProgressTimerIsActive
= false;
3796 mProgressNotifier
->Cancel();
3800 void XMLHttpRequestMainThread::StartProgressEventTimer() {
3801 if (!mProgressNotifier
) {
3802 mProgressNotifier
= NS_NewTimer(GetTimerEventTarget());
3804 if (mProgressNotifier
) {
3805 mProgressTimerIsActive
= true;
3806 mProgressNotifier
->Cancel();
3807 mProgressNotifier
->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL
,
3808 nsITimer::TYPE_ONE_SHOT
);
3812 XMLHttpRequestMainThread::SyncTimeoutType
3813 XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer() {
3814 MOZ_ASSERT(mFlagSynchronous
);
3816 Document
* doc
= GetDocumentIfCurrent();
3817 if (!doc
|| !doc
->GetPageUnloadingEventTimeStamp()) {
3818 return eNoTimerNeeded
;
3821 // If we are in a beforeunload or a unload event, we must force a timeout.
3823 (TimeStamp::NowLoRes() - doc
->GetPageUnloadingEventTimeStamp());
3824 if (diff
.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING
) {
3825 return eErrorOrExpired
;
3828 mSyncTimeoutTimer
= NS_NewTimer(GetTimerEventTarget());
3829 if (!mSyncTimeoutTimer
) {
3830 return eErrorOrExpired
;
3833 uint32_t timeout
= MAX_SYNC_TIMEOUT_WHEN_UNLOADING
- diff
.ToMilliseconds();
3834 nsresult rv
= mSyncTimeoutTimer
->InitWithCallback(this, timeout
,
3835 nsITimer::TYPE_ONE_SHOT
);
3836 return NS_FAILED(rv
) ? eErrorOrExpired
: eTimerStarted
;
3839 void XMLHttpRequestMainThread::HandleSyncTimeoutTimer() {
3840 MOZ_ASSERT(mSyncTimeoutTimer
);
3841 MOZ_ASSERT(mFlagSyncLooping
);
3843 CancelSyncTimeoutTimer();
3845 mErrorLoadDetail
= NS_ERROR_DOM_TIMEOUT_ERR
;
3848 void XMLHttpRequestMainThread::CancelSyncTimeoutTimer() {
3849 if (mSyncTimeoutTimer
) {
3850 mSyncTimeoutTimer
->Cancel();
3851 mSyncTimeoutTimer
= nullptr;
3855 already_AddRefed
<nsXMLHttpRequestXPCOMifier
>
3856 XMLHttpRequestMainThread::EnsureXPCOMifier() {
3858 mXPCOMifier
= new nsXMLHttpRequestXPCOMifier(this);
3860 RefPtr
<nsXMLHttpRequestXPCOMifier
> newRef(mXPCOMifier
);
3861 return newRef
.forget();
3864 bool XMLHttpRequestMainThread::ShouldBlockAuthPrompt() {
3865 // Verify that it's ok to prompt for credentials here, per spec
3866 // http://xhr.spec.whatwg.org/#the-send%28%29-method
3868 if (mAuthorRequestHeaders
.Has("authorization")) {
3872 nsCOMPtr
<nsIURI
> uri
;
3873 nsresult rv
= mChannel
->GetURI(getter_AddRefs(uri
));
3874 if (NS_WARN_IF(NS_FAILED(rv
))) {
3878 // Also skip if a username and/or password is provided in the URI.
3880 return NS_SUCCEEDED(uri
->GetHasUserPass(&hasUserPass
)) && hasUserPass
;
3883 void XMLHttpRequestMainThread::TruncateResponseText() {
3884 mResponseText
.Truncate();
3885 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
3888 NS_IMPL_ISUPPORTS(XMLHttpRequestMainThread::nsHeaderVisitor
,
3889 nsIHttpHeaderVisitor
)
3891 NS_IMETHODIMP
XMLHttpRequestMainThread::nsHeaderVisitor::VisitHeader(
3892 const nsACString
& header
, const nsACString
& value
) {
3893 if (mXHR
.IsSafeHeader(header
, mHttpChannel
)) {
3894 nsAutoCString
lowerHeader(header
);
3895 ToLowerCase(lowerHeader
);
3896 if (!mHeaderList
.InsertElementSorted(HeaderEntry(lowerHeader
, value
),
3898 return NS_ERROR_OUT_OF_MEMORY
;
3904 XMLHttpRequestMainThread::nsHeaderVisitor::nsHeaderVisitor(
3905 const XMLHttpRequestMainThread
& aXMLHttpRequest
,
3906 NotNull
<nsIHttpChannel
*> aHttpChannel
)
3907 : mXHR(aXMLHttpRequest
), mHttpChannel(aHttpChannel
) {}
3909 XMLHttpRequestMainThread::nsHeaderVisitor::~nsHeaderVisitor() = default;
3911 void XMLHttpRequestMainThread::MaybeCreateBlobStorage() {
3913 MOZ_ASSERT(mResponseType
== XMLHttpRequestResponseType::Blob
);
3919 MutableBlobStorage::MutableBlobStorageType storageType
=
3920 BasePrincipal::Cast(mPrincipal
)->PrivateBrowsingId() == 0
3921 ? MutableBlobStorage::eCouldBeInTemporaryFile
3922 : MutableBlobStorage::eOnlyInMemory
;
3924 nsCOMPtr
<nsIEventTarget
> eventTarget
;
3925 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3926 eventTarget
= global
->SerialEventTarget();
3929 mBlobStorage
= new MutableBlobStorage(storageType
, eventTarget
);
3932 void XMLHttpRequestMainThread::BlobStoreCompleted(
3933 MutableBlobStorage
* aBlobStorage
, BlobImpl
* aBlobImpl
, nsresult aRv
) {
3935 // Ok, the state is changed...
3936 if (mBlobStorage
!= aBlobStorage
|| NS_FAILED(aRv
)) {
3940 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::DONE
);
3942 mResponseBlobImpl
= aBlobImpl
;
3943 mBlobStorage
= nullptr;
3945 ChangeStateToDone(mFlagSyncLooping
);
3949 XMLHttpRequestMainThread::GetName(nsACString
& aName
) {
3950 aName
.AssignLiteral("XMLHttpRequest");
3954 // nsXMLHttpRequestXPCOMifier implementation
3955 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier
)
3956 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
3957 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
3958 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
3959 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback
)
3960 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
3961 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
3962 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
3963 NS_INTERFACE_MAP_ENTRY(nsINamed
)
3964 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIStreamListener
)
3965 NS_INTERFACE_MAP_END
3967 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier
)
3968 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier
)
3970 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
3971 // inheritance from nsISupports.
3972 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier
)
3974 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier
)
3976 tmp
->mXHR
->mXPCOMifier
= nullptr;
3978 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR
)
3979 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3981 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier
)
3982 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR
)
3983 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3986 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID
& aIID
, void** aResult
) {
3987 // Return ourselves for the things we implement (except
3988 // nsIInterfaceRequestor) and the XHR for the rest.
3989 if (!aIID
.Equals(NS_GET_IID(nsIInterfaceRequestor
))) {
3990 nsresult rv
= QueryInterface(aIID
, aResult
);
3991 if (NS_SUCCEEDED(rv
)) {
3996 return mXHR
->GetInterface(aIID
, aResult
);
3999 ArrayBufferBuilder::ArrayBufferBuilder()
4000 : mMutex("ArrayBufferBuilder"),
4007 ArrayBufferBuilder::~ArrayBufferBuilder() {
4009 JS_free(nullptr, mDataPtr
);
4013 JS::ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
4018 mCapacity
= mLength
= 0;
4021 bool ArrayBufferBuilder::SetCapacity(uint32_t aNewCap
) {
4022 MutexAutoLock
lock(mMutex
);
4023 return SetCapacityInternal(aNewCap
, lock
);
4026 bool ArrayBufferBuilder::SetCapacityInternal(
4027 uint32_t aNewCap
, const MutexAutoLock
& aProofOfLock
) {
4028 MOZ_ASSERT(!mMapPtr
);
4029 MOZ_ASSERT(!mNeutered
);
4031 // To ensure that realloc won't free mDataPtr, use a size of 1
4033 uint8_t* newdata
= (uint8_t*)js_realloc(mDataPtr
, aNewCap
? aNewCap
: 1);
4039 if (aNewCap
> mCapacity
) {
4040 memset(newdata
+ mCapacity
, 0, aNewCap
- mCapacity
);
4044 mCapacity
= aNewCap
;
4045 if (mLength
> aNewCap
) {
4052 bool ArrayBufferBuilder::Append(const uint8_t* aNewData
, uint32_t aDataLen
,
4053 uint32_t aMaxGrowth
) {
4054 MutexAutoLock
lock(mMutex
);
4055 MOZ_ASSERT(!mMapPtr
);
4056 MOZ_ASSERT(!mNeutered
);
4058 CheckedUint32 neededCapacity
= mLength
;
4059 neededCapacity
+= aDataLen
;
4060 if (!neededCapacity
.isValid()) {
4063 if (mLength
+ aDataLen
> mCapacity
) {
4064 CheckedUint32 newcap
= mCapacity
;
4065 // Double while under aMaxGrowth or if not specified.
4066 if (!aMaxGrowth
|| mCapacity
< aMaxGrowth
) {
4069 newcap
+= aMaxGrowth
;
4072 if (!newcap
.isValid()) {
4076 // But make sure there's always enough to satisfy our request.
4077 if (newcap
.value() < neededCapacity
.value()) {
4078 newcap
= neededCapacity
;
4081 if (!SetCapacityInternal(newcap
.value(), lock
)) {
4086 // Assert that the region isn't overlapping so we can memcpy.
4088 !AreOverlappingRegions(aNewData
, aDataLen
, mDataPtr
+ mLength
, aDataLen
));
4090 memcpy(mDataPtr
+ mLength
, aNewData
, aDataLen
);
4091 mLength
+= aDataLen
;
4096 uint32_t ArrayBufferBuilder::Length() {
4097 MutexAutoLock
lock(mMutex
);
4098 MOZ_ASSERT(!mNeutered
);
4102 uint32_t ArrayBufferBuilder::Capacity() {
4103 MutexAutoLock
lock(mMutex
);
4104 MOZ_ASSERT(!mNeutered
);
4108 JSObject
* ArrayBufferBuilder::TakeArrayBuffer(JSContext
* aCx
) {
4109 MutexAutoLock
lock(mMutex
);
4110 MOZ_DIAGNOSTIC_ASSERT(!mNeutered
);
4113 JSObject
* obj
= JS::NewMappedArrayBufferWithContents(aCx
, mLength
, mMapPtr
);
4115 JS::ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
4121 // The memory-mapped contents will be released when the ArrayBuffer becomes
4122 // detached or is GC'd.
4126 // we need to check for mLength == 0, because nothing may have been
4128 if (mCapacity
> mLength
|| mLength
== 0) {
4129 if (!SetCapacityInternal(mLength
, lock
)) {
4134 // |mDataPtr| will be deallocated in ArrayBufferBuilder's destructor when this
4135 // ArrayBuffer allocation failed.
4136 JSObject
* obj
= JS::NewArrayBufferWithContents(
4137 aCx
, mLength
, mDataPtr
,
4138 JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory
);
4144 mCapacity
= mLength
= 0;
4150 nsresult
ArrayBufferBuilder::MapToFileInPackage(const nsCString
& aFile
,
4151 nsIFile
* aJarFile
) {
4152 MutexAutoLock
lock(mMutex
);
4153 MOZ_ASSERT(NS_IsMainThread());
4154 MOZ_ASSERT(!mNeutered
);
4158 // Open Jar file to get related attributes of target file.
4159 RefPtr
<nsZipArchive
> zip
= nsZipArchive::OpenArchive(aJarFile
);
4161 return NS_ERROR_FAILURE
;
4163 nsZipItem
* zipItem
= zip
->GetItem(aFile
.get());
4165 return NS_ERROR_FILE_NOT_FOUND
;
4168 // If file was added to the package as stored(uncompressed), map to the
4169 // offset of file in zip package.
4170 if (!zipItem
->Compression()) {
4171 uint32_t offset
= zip
->GetDataOffset(zipItem
);
4172 uint32_t size
= zipItem
->RealSize();
4173 mozilla::AutoFDClose pr_fd
;
4174 rv
= aJarFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, getter_Transfers(pr_fd
));
4175 if (NS_FAILED(rv
)) {
4178 mMapPtr
= JS::CreateMappedArrayBufferContents(
4179 PR_FileDesc2NativeHandle(pr_fd
.get()), offset
, size
);
4185 return NS_ERROR_FAILURE
;
4189 bool ArrayBufferBuilder::AreOverlappingRegions(const uint8_t* aStart1
,
4191 const uint8_t* aStart2
,
4192 uint32_t aLength2
) {
4193 const uint8_t* end1
= aStart1
+ aLength1
;
4194 const uint8_t* end2
= aStart2
+ aLength2
;
4196 const uint8_t* max_start
= aStart1
> aStart2
? aStart1
: aStart2
;
4197 const uint8_t* min_end
= end1
< end2
? end1
: end2
;
4199 return max_start
< min_end
;
4202 RequestHeaders::RequestHeader
* RequestHeaders::Find(const nsACString
& aName
) {
4203 for (RequestHeaders::RequestHeader
& header
: mHeaders
) {
4204 if (header
.mName
.Equals(aName
, nsCaseInsensitiveCStringComparator
)) {
4211 bool RequestHeaders::IsEmpty() const { return mHeaders
.IsEmpty(); }
4213 bool RequestHeaders::Has(const char* aName
) {
4214 return Has(nsDependentCString(aName
));
4217 bool RequestHeaders::Has(const nsACString
& aName
) { return !!Find(aName
); }
4219 void RequestHeaders::Get(const char* aName
, nsACString
& aValue
) {
4220 Get(nsDependentCString(aName
), aValue
);
4223 void RequestHeaders::Get(const nsACString
& aName
, nsACString
& aValue
) {
4224 RequestHeader
* header
= Find(aName
);
4226 aValue
= header
->mValue
;
4228 aValue
.SetIsVoid(true);
4232 void RequestHeaders::Set(const char* aName
, const nsACString
& aValue
) {
4233 Set(nsDependentCString(aName
), aValue
);
4236 void RequestHeaders::Set(const nsACString
& aName
, const nsACString
& aValue
) {
4237 RequestHeader
* header
= Find(aName
);
4239 header
->mValue
.Assign(aValue
);
4241 RequestHeader newHeader
= {nsCString(aName
), nsCString(aValue
)};
4242 mHeaders
.AppendElement(newHeader
);
4246 void RequestHeaders::MergeOrSet(const char* aName
, const nsACString
& aValue
) {
4247 MergeOrSet(nsDependentCString(aName
), aValue
);
4250 void RequestHeaders::MergeOrSet(const nsACString
& aName
,
4251 const nsACString
& aValue
) {
4252 RequestHeader
* header
= Find(aName
);
4254 header
->mValue
.AppendLiteral(", ");
4255 header
->mValue
.Append(aValue
);
4257 RequestHeader newHeader
= {nsCString(aName
), nsCString(aValue
)};
4258 mHeaders
.AppendElement(newHeader
);
4262 void RequestHeaders::Clear() { mHeaders
.Clear(); }
4264 void RequestHeaders::ApplyToChannel(nsIHttpChannel
* aChannel
,
4265 bool aStripRequestBodyHeader
,
4266 bool aStripAuthHeader
) const {
4267 for (const RequestHeader
& header
: mHeaders
) {
4268 if (aStripRequestBodyHeader
&&
4269 (header
.mName
.LowerCaseEqualsASCII("content-type") ||
4270 header
.mName
.LowerCaseEqualsASCII("content-encoding") ||
4271 header
.mName
.LowerCaseEqualsASCII("content-language") ||
4272 header
.mName
.LowerCaseEqualsASCII("content-location"))) {
4276 if (aStripAuthHeader
&&
4277 header
.mName
.LowerCaseEqualsASCII("authorization")) {
4281 // Update referrerInfo to override referrer header in system privileged.
4282 if (header
.mName
.LowerCaseEqualsASCII("referer")) {
4283 DebugOnly
<nsresult
> rv
= aChannel
->SetNewReferrerInfo(
4284 header
.mValue
, nsIReferrerInfo::ReferrerPolicyIDL::UNSAFE_URL
, true);
4285 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4287 if (header
.mValue
.IsEmpty()) {
4288 DebugOnly
<nsresult
> rv
= aChannel
->SetEmptyRequestHeader(header
.mName
);
4289 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4291 DebugOnly
<nsresult
> rv
=
4292 aChannel
->SetRequestHeader(header
.mName
, header
.mValue
, false);
4293 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4298 void RequestHeaders::GetCORSUnsafeHeaders(nsTArray
<nsCString
>& aArray
) const {
4299 for (const RequestHeader
& header
: mHeaders
) {
4300 if (!nsContentUtils::IsCORSSafelistedRequestHeader(header
.mName
,
4302 aArray
.AppendElement(header
.mName
);
4307 RequestHeaders::CharsetIterator::CharsetIterator(nsACString
& aSource
)
4311 mCutoff(aSource
.Length()),
4314 bool RequestHeaders::CharsetIterator::Equals(
4315 const nsACString
& aOther
, const nsCStringComparator
& aCmp
) const {
4317 return Substring(mSource
, mCurPos
, mCurLen
).Equals(aOther
, aCmp
);
4323 void RequestHeaders::CharsetIterator::Replace(const nsACString
& aReplacement
) {
4325 mSource
.Replace(mCurPos
, mCurLen
, aReplacement
);
4326 mCurLen
= aReplacement
.Length();
4330 bool RequestHeaders::CharsetIterator::Next() {
4332 nsAutoCString charset
;
4334 // Look for another charset declaration in the string, limiting the
4335 // search to only the characters before the parts we've already searched
4336 // (before mCutoff), so that we don't find the same charset twice.
4337 NS_ExtractCharsetFromContentType(Substring(mSource
, 0, mCutoff
), charset
,
4338 &mValid
, &start
, &end
);
4344 // Everything after the = sign is the part of the charset we want.
4345 mCurPos
= mSource
.FindChar('=', start
) + 1;
4346 mCurLen
= end
- mCurPos
;
4348 // Special case: the extracted charset is quoted with single quotes.
4349 // For the purpose of preserving what was set we want to handle them
4350 // as delimiters (although they aren't really).
4351 if (charset
.Length() >= 2 && charset
.First() == '\'' &&
4352 charset
.Last() == '\'') {
4362 } // namespace mozilla::dom