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/PreloaderBase.h"
44 #include "mozilla/ScopeExit.h"
45 #include "mozilla/SpinEventLoopUntil.h"
46 #include "mozilla/StaticPrefs_dom.h"
47 #include "mozilla/StaticPrefs_network.h"
48 #include "mozilla/StaticPrefs_privacy.h"
49 #include "mozilla/dom/ProgressEvent.h"
50 #include "nsDataChannel.h"
51 #include "nsIJARChannel.h"
52 #include "nsIJARURI.h"
53 #include "nsReadableUtils.h"
54 #include "nsSandboxFlags.h"
57 #include "nsIURIMutator.h"
58 #include "nsILoadGroup.h"
59 #include "nsNetUtil.h"
60 #include "nsStringStream.h"
61 #include "nsIAuthPrompt.h"
62 #include "nsIAuthPrompt2.h"
63 #include "nsIClassOfService.h"
64 #include "nsIHttpChannel.h"
65 #include "nsISupportsPriority.h"
66 #include "nsIInterfaceRequestorUtils.h"
67 #include "nsStreamUtils.h"
68 #include "nsThreadUtils.h"
69 #include "nsIUploadChannel.h"
70 #include "nsIUploadChannel2.h"
72 #include "nsIDOMEventListener.h"
73 #include "nsVariant.h"
74 #include "nsIScriptError.h"
75 #include "nsICachingChannel.h"
76 #include "nsICookieJarSettings.h"
77 #include "nsContentUtils.h"
78 #include "nsCycleCollectionParticipant.h"
80 #include "nsIPromptFactory.h"
81 #include "nsIWindowWatcher.h"
82 #include "nsIConsoleService.h"
83 #include "nsAsyncRedirectVerifyHelper.h"
84 #include "nsStringBuffer.h"
85 #include "nsIFileChannel.h"
86 #include "mozilla/Telemetry.h"
87 #include "js/ArrayBuffer.h" // JS::{Create,Release}MappedArrayBufferContents,New{,Mapped}ArrayBufferWithContents
88 #include "js/JSON.h" // JS_ParseJSON
89 #include "js/MemoryFunctions.h"
90 #include "js/RootingAPI.h" // JS::{{,Mutable}Handle,Rooted}
91 #include "js/Value.h" // JS::{,Undefined}Value
92 #include "jsapi.h" // JS_ClearPendingException
93 #include "GeckoProfiler.h"
94 #include "mozilla/dom/XMLHttpRequestBinding.h"
95 #include "mozilla/Attributes.h"
96 #include "MultipartBlobImpl.h"
97 #include "nsIPermissionManager.h"
98 #include "nsMimeTypes.h"
99 #include "nsIHttpChannelInternal.h"
100 #include "nsCharSeparatedTokenizer.h"
101 #include "nsStreamListenerWrapper.h"
102 #include "nsITimedChannel.h"
103 #include "nsWrapperCacheInlines.h"
104 #include "nsZipArchive.h"
105 #include "mozilla/Preferences.h"
106 #include "private/pprio.h"
107 #include "XMLHttpRequestUpload.h"
109 // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
110 // replaced by FileCreatorHelper#CreateFileW.
115 extern mozilla::LazyLogModule gXMLHttpRequestLog
;
117 using namespace mozilla::net
;
119 namespace mozilla::dom
{
121 using EventType
= XMLHttpRequest::EventType
;
122 using Events
= XMLHttpRequest::Events
;
124 // Maximum size that we'll grow an ArrayBuffer instead of doubling,
125 // once doubling reaches this threshold
126 const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH
= 32 * 1024 * 1024;
127 // start at 32k to avoid lots of doubling right at the start
128 const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE
= 32 * 1024;
129 // the maximum Content-Length that we'll preallocate. 1GB. Must fit
131 const int32_t XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE
=
132 1 * 1024 * 1024 * 1024LL;
135 const nsString kLiteralString_readystatechange
= u
"readystatechange"_ns
;
136 const nsString kLiteralString_xmlhttprequest
= u
"xmlhttprequest"_ns
;
137 const nsString kLiteralString_DOMContentLoaded
= u
"DOMContentLoaded"_ns
;
138 const nsCString kLiteralString_charset
= "charset"_ns
;
139 const nsCString kLiteralString_UTF_8
= "UTF-8"_ns
;
142 #define NS_PROGRESS_EVENT_INTERVAL 50
143 #define MAX_SYNC_TIMEOUT_WHEN_UNLOADING 10000 /* 10 secs */
145 NS_IMPL_ISUPPORTS(nsXHRParseEndListener
, nsIDOMEventListener
)
147 class nsResumeTimeoutsEvent
: public Runnable
{
149 explicit nsResumeTimeoutsEvent(nsPIDOMWindowInner
* aWindow
)
150 : Runnable("dom::nsResumeTimeoutsEvent"), mWindow(aWindow
) {}
152 NS_IMETHOD
Run() override
{
158 nsCOMPtr
<nsPIDOMWindowInner
> mWindow
;
161 // This helper function adds the given load flags to the request's existing
163 static void AddLoadFlags(nsIRequest
* request
, nsLoadFlags newFlags
) {
165 request
->GetLoadFlags(&flags
);
167 request
->SetLoadFlags(flags
);
170 // We are in a sync event loop.
171 #define NOT_CALLABLE_IN_SYNC_SEND_RV \
172 if (mFlagSyncLooping || mEventDispatchingSuspended) { \
173 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT); \
177 /////////////////////////////////////////////
180 /////////////////////////////////////////////
184 // In debug mode, annotate WorkerRefs with the name of the function being
185 // invoked for increased scrutability. Save the previous value on the stack.
187 struct DebugWorkerRefs
{
188 RefPtr
<ThreadSafeWorkerRef
> mTSWorkerRef
;
191 DebugWorkerRefs(RefPtr
<ThreadSafeWorkerRef
>& aTSWorkerRef
,
192 const std::string
& aStatus
)
193 : mTSWorkerRef(aTSWorkerRef
) {
195 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Info
,
196 ("No WorkerRef during: %s", aStatus
.c_str()));
200 MOZ_ASSERT(mTSWorkerRef
->Private());
202 nsCString
status(aStatus
.c_str());
203 mPrev
= GET_WORKERREF_DEBUG_STATUS(mTSWorkerRef
->Ref());
204 SET_WORKERREF_DEBUG_STATUS(mTSWorkerRef
->Ref(), status
);
212 MOZ_ASSERT(mTSWorkerRef
->Private());
214 SET_WORKERREF_DEBUG_STATUS(mTSWorkerRef
->Ref(), mPrev
);
219 # define STREAM_STRING(stuff) \
220 (((const std::ostringstream&)(std::ostringstream() << stuff)) \
222 # define DEBUG_WORKERREFS \
223 DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)(mTSWorkerRef, __func__)
224 # define DEBUG_WORKERREFS1(x) \
225 DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)( \
226 mTSWorkerRef, STREAM_STRING(__func__ << ": " << x)) // NOLINT
229 # define DEBUG_WORKERREFS void()
230 # define DEBUG_WORKERREFS1(x) void()
233 bool XMLHttpRequestMainThread::sDontWarnAboutSyncXHR
= false;
235 XMLHttpRequestMainThread::XMLHttpRequestMainThread(
236 nsIGlobalObject
* aGlobalObject
)
237 : XMLHttpRequest(aGlobalObject
),
238 mResponseBodyDecodedPos(0),
239 mResponseType(XMLHttpRequestResponseType::_empty
),
240 mState(XMLHttpRequest_Binding::UNSENT
),
241 mFlagSynchronous(false),
243 mFlagParseBody(false),
244 mFlagSyncLooping(false),
245 mFlagBackgroundRequest(false),
246 mFlagHadUploadListenersOnSend(false),
247 mFlagACwithCredentials(false),
248 mFlagTimedOut(false),
251 mUploadTransferred(0),
253 mUploadComplete(true),
254 mProgressSinceLastProgressEvent(false),
256 mTimeoutMilliseconds(0),
257 mErrorLoad(ErrorType::eOK
),
258 mErrorLoadDetail(NS_OK
),
259 mErrorParsingXML(false),
260 mWaitingForOnStopRequest(false),
261 mProgressTimerIsActive(false),
263 mWarnAboutSyncHtml(false),
268 mResultJSON(JS::UndefinedValue()),
269 mArrayBufferBuilder(new ArrayBufferBuilder()),
270 mResultArrayBuffer(nullptr),
271 mIsMappedArrayBuffer(false),
272 mXPCOMifier(nullptr),
273 mEventDispatchingSuspended(false),
275 mDelayedDoneNotifier(nullptr) {
277 mozilla::HoldJSObjects(this);
280 XMLHttpRequestMainThread::~XMLHttpRequestMainThread() {
283 !mDelayedDoneNotifier
,
284 "How can we have mDelayedDoneNotifier, which owns us, in destructor?");
288 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
289 mState
== XMLHttpRequest_Binding::LOADING
) {
293 if (mParseEndListener
) {
294 mParseEndListener
->SetIsStale();
295 mParseEndListener
= nullptr;
298 MOZ_ASSERT(!mFlagSyncLooping
, "we rather crash than hang");
299 mFlagSyncLooping
= false;
301 mozilla::DropJSObjects(this);
304 void XMLHttpRequestMainThread::Construct(
305 nsIPrincipal
* aPrincipal
, nsICookieJarSettings
* aCookieJarSettings
,
306 bool aForWorker
, nsIURI
* aBaseURI
/* = nullptr */,
307 nsILoadGroup
* aLoadGroup
/* = nullptr */,
308 PerformanceStorage
* aPerformanceStorage
/* = nullptr */,
309 nsICSPEventListener
* aCSPEventListener
/* = nullptr */) {
311 MOZ_ASSERT(aPrincipal
);
312 mPrincipal
= aPrincipal
;
314 mLoadGroup
= aLoadGroup
;
315 mCookieJarSettings
= aCookieJarSettings
;
316 mForWorker
= aForWorker
;
317 mPerformanceStorage
= aPerformanceStorage
;
318 mCSPEventListener
= aCSPEventListener
;
321 void XMLHttpRequestMainThread::InitParameters(bool aAnon
, bool aSystem
) {
323 if (!aAnon
&& !aSystem
) {
327 // Check for permissions.
328 // Chrome is always allowed access, so do the permission check only
329 // for non-chrome pages.
330 if (!IsSystemXHR() && aSystem
) {
331 nsIGlobalObject
* global
= GetOwnerGlobal();
332 if (NS_WARN_IF(!global
)) {
333 SetParameters(aAnon
, false);
337 nsIPrincipal
* principal
= global
->PrincipalOrNull();
338 if (NS_WARN_IF(!principal
)) {
339 SetParameters(aAnon
, false);
343 nsCOMPtr
<nsIPermissionManager
> permMgr
=
344 components::PermissionManager::Service();
345 if (NS_WARN_IF(!permMgr
)) {
346 SetParameters(aAnon
, false);
351 nsresult rv
= permMgr
->TestPermissionFromPrincipal(
352 principal
, "systemXHR"_ns
, &permission
);
353 if (NS_FAILED(rv
) || permission
!= nsIPermissionManager::ALLOW_ACTION
) {
354 SetParameters(aAnon
, false);
359 SetParameters(aAnon
, aSystem
);
362 void XMLHttpRequestMainThread::SetClientInfoAndController(
363 const ClientInfo
& aClientInfo
,
364 const Maybe
<ServiceWorkerDescriptor
>& aController
) {
365 mClientInfo
.emplace(aClientInfo
);
366 mController
= aController
;
369 void XMLHttpRequestMainThread::ResetResponse() {
370 mResponseXML
= nullptr;
371 mResponseBody
.Truncate();
372 TruncateResponseText();
373 mResponseBlobImpl
= nullptr;
374 mResponseBlob
= nullptr;
375 mBlobStorage
= nullptr;
376 mResultArrayBuffer
= nullptr;
377 mArrayBufferBuilder
= new ArrayBufferBuilder();
378 mResultJSON
.setUndefined();
379 mLoadTransferred
= 0;
380 mResponseBodyDecodedPos
= 0;
384 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestMainThread
)
386 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestMainThread
,
387 XMLHttpRequestEventTarget
)
388 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext
)
389 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel
)
390 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML
)
392 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener
)
394 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob
)
395 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks
)
397 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink
)
398 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink
)
400 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload
)
401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
403 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread
,
404 XMLHttpRequestEventTarget
)
405 tmp
->mResultArrayBuffer
= nullptr;
406 tmp
->mArrayBufferBuilder
= nullptr;
407 tmp
->mResultJSON
.setUndefined();
408 tmp
->mResponseBlobImpl
= nullptr;
410 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext
)
411 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel
)
412 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML
)
414 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener
)
416 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob
)
417 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks
)
419 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink
)
420 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink
)
422 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload
)
423 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
425 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestMainThread
,
426 XMLHttpRequestEventTarget
)
427 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer
)
428 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON
)
429 NS_IMPL_CYCLE_COLLECTION_TRACE_END
431 bool XMLHttpRequestMainThread::IsCertainlyAliveForCC() const {
432 return mWaitingForOnStopRequest
;
435 // QueryInterface implementation for XMLHttpRequestMainThread
436 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestMainThread
)
437 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
438 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
439 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
440 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
441 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
442 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
443 NS_INTERFACE_MAP_ENTRY(nsINamed
)
444 NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget
)
445 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget
)
447 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestMainThread
, XMLHttpRequestEventTarget
)
448 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestMainThread
, XMLHttpRequestEventTarget
)
450 void XMLHttpRequestMainThread::DisconnectFromOwner() {
451 XMLHttpRequestEventTarget::DisconnectFromOwner();
455 size_t XMLHttpRequestMainThread::SizeOfEventTargetIncludingThis(
456 MallocSizeOf aMallocSizeOf
) const {
457 size_t n
= aMallocSizeOf(this);
458 n
+= mResponseBody
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
460 // Why is this safe? Because no-one else will report this string. The
461 // other possible sharers of this string are as follows.
463 // - The JS engine could hold copies if the JS code holds references, e.g.
464 // |var text = XHR.responseText|. However, those references will be via JS
465 // external strings, for which the JS memory reporter does *not* report the
468 // - Binary extensions, but they're *extremely* unlikely to do any memory
471 n
+= mResponseText
.SizeOfThis(aMallocSizeOf
);
475 // Measurement of the following members may be added later if DMD finds it is
480 static void LogMessage(
481 const char* aWarning
, nsPIDOMWindowInner
* aWindow
,
482 const nsTArray
<nsString
>& aParams
= nsTArray
<nsString
>()) {
483 nsCOMPtr
<Document
> doc
;
485 doc
= aWindow
->GetExtantDoc();
487 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "DOM"_ns
, doc
,
488 nsContentUtils::eDOM_PROPERTIES
, aWarning
,
492 Document
* XMLHttpRequestMainThread::GetResponseXML(ErrorResult
& aRv
) {
493 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
494 mResponseType
!= XMLHttpRequestResponseType::Document
) {
495 aRv
.ThrowInvalidStateError(
496 "responseXML is only available if responseType is '' or 'document'.");
499 if (mWarnAboutSyncHtml
) {
500 mWarnAboutSyncHtml
= false;
501 LogMessage("HTMLSyncXHRWarning", GetOwner());
503 if (mState
!= XMLHttpRequest_Binding::DONE
) {
510 * This piece copied from XMLDocument, we try to get the charset
513 nsresult
XMLHttpRequestMainThread::DetectCharset() {
517 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
518 mResponseType
!= XMLHttpRequestResponseType::Text
&&
519 mResponseType
!= XMLHttpRequestResponseType::Json
) {
523 nsAutoCString charsetVal
;
524 const Encoding
* encoding
;
525 bool ok
= mChannel
&& NS_SUCCEEDED(mChannel
->GetContentCharset(charsetVal
)) &&
526 (encoding
= Encoding::ForLabel(charsetVal
));
528 // MS documentation states UTF-8 is default for responseText
529 encoding
= UTF_8_ENCODING
;
532 if (mResponseType
== XMLHttpRequestResponseType::Json
&&
533 encoding
!= UTF_8_ENCODING
) {
534 // The XHR spec says only UTF-8 is supported for responseType == "json"
535 LogMessage("JSONCharsetWarning", GetOwner());
536 encoding
= UTF_8_ENCODING
;
539 // Only sniff the BOM for non-JSON responseTypes
540 if (mResponseType
== XMLHttpRequestResponseType::Json
) {
541 mDecoder
= encoding
->NewDecoderWithBOMRemoval();
543 mDecoder
= encoding
->NewDecoder();
549 nsresult
XMLHttpRequestMainThread::AppendToResponseText(
550 Span
<const uint8_t> aBuffer
, bool aLast
) {
551 // Call this with an empty buffer to send the decoder the signal
552 // that we have hit the end of the stream.
554 NS_ENSURE_STATE(mDecoder
);
556 CheckedInt
<size_t> destBufferLen
=
557 mDecoder
->MaxUTF16BufferLength(aBuffer
.Length());
559 { // scope for holding the mutex that protects mResponseText
560 XMLHttpRequestStringWriterHelper
helper(mResponseText
);
562 uint32_t len
= helper
.Length();
564 destBufferLen
+= len
;
565 if (!destBufferLen
.isValid() || destBufferLen
.value() > UINT32_MAX
) {
566 return NS_ERROR_OUT_OF_MEMORY
;
569 auto handleOrErr
= helper
.BulkWrite(destBufferLen
.value());
570 if (handleOrErr
.isErr()) {
571 return handleOrErr
.unwrapErr();
574 auto handle
= handleOrErr
.unwrap();
579 std::tie(result
, read
, written
, std::ignore
) =
580 mDecoder
->DecodeToUTF16(aBuffer
, handle
.AsSpan().From(len
), aLast
);
581 MOZ_ASSERT(result
== kInputEmpty
);
582 MOZ_ASSERT(read
== aBuffer
.Length());
584 MOZ_ASSERT(len
<= destBufferLen
.value());
585 handle
.Finish(len
, false);
589 // Drop the finished decoder to avoid calling into a decoder
590 // that has finished.
597 void XMLHttpRequestMainThread::GetResponseText(DOMString
& aResponseText
,
599 MOZ_DIAGNOSTIC_ASSERT(!mForWorker
);
601 XMLHttpRequestStringSnapshot snapshot
;
602 GetResponseText(snapshot
, aRv
);
607 if (!snapshot
.GetAsString(aResponseText
)) {
608 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
613 void XMLHttpRequestMainThread::GetResponseText(
614 XMLHttpRequestStringSnapshot
& aSnapshot
, ErrorResult
& aRv
) {
617 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
618 mResponseType
!= XMLHttpRequestResponseType::Text
) {
619 aRv
.ThrowInvalidStateError(
620 "responseText is only available if responseType is '' or 'text'.");
624 if (mState
!= XMLHttpRequest_Binding::LOADING
&&
625 mState
!= XMLHttpRequest_Binding::DONE
) {
629 // Main Fetch step 18 requires to ignore body for head/connect methods.
630 if (mRequestMethod
.EqualsLiteral("HEAD") ||
631 mRequestMethod
.EqualsLiteral("CONNECT")) {
635 // We only decode text lazily if we're also parsing to a doc.
636 // Also, if we've decoded all current data already, then no need to decode
638 if ((!mResponseXML
&& !mErrorParsingXML
) ||
639 (mResponseBodyDecodedPos
== mResponseBody
.Length() &&
640 (mState
!= XMLHttpRequest_Binding::DONE
|| mEofDecoded
))) {
641 mResponseText
.CreateSnapshot(aSnapshot
);
645 MatchCharsetAndDecoderToResponseDocument();
647 MOZ_ASSERT(mResponseBodyDecodedPos
< mResponseBody
.Length() ||
648 mState
== XMLHttpRequest_Binding::DONE
,
649 "Unexpected mResponseBodyDecodedPos");
650 Span
<const uint8_t> span
= mResponseBody
;
651 aRv
= AppendToResponseText(span
.From(mResponseBodyDecodedPos
),
652 mState
== XMLHttpRequest_Binding::DONE
);
657 mResponseBodyDecodedPos
= mResponseBody
.Length();
660 // Free memory buffer which we no longer need
661 mResponseBody
.Truncate();
662 mResponseBodyDecodedPos
= 0;
665 mResponseText
.CreateSnapshot(aSnapshot
);
668 nsresult
XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext
* aCx
) {
670 return NS_ERROR_FAILURE
;
674 nsresult rv
= GetResponseTextForJSON(string
);
675 if (NS_WARN_IF(NS_FAILED(rv
))) {
679 // The Unicode converter has already zapped the BOM if there was one
680 JS::Rooted
<JS::Value
> value(aCx
);
681 if (!JS_ParseJSON(aCx
, string
.BeginReading(), string
.Length(), &value
)) {
682 return NS_ERROR_FAILURE
;
689 void XMLHttpRequestMainThread::SetResponseType(
690 XMLHttpRequestResponseType aResponseType
, ErrorResult
& aRv
) {
691 NOT_CALLABLE_IN_SYNC_SEND_RV
693 if (mState
== XMLHttpRequest_Binding::LOADING
||
694 mState
== XMLHttpRequest_Binding::DONE
) {
695 aRv
.ThrowInvalidStateError(
696 "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
697 "(when its state is LOADING or DONE).");
701 // sync request is not allowed setting responseType in window context
702 if (HasOrHasHadOwner() && mState
!= XMLHttpRequest_Binding::UNSENT
&&
704 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
705 aRv
.ThrowInvalidAccessError(
706 "synchronous XMLHttpRequests do not support timeout and responseType");
710 // Set the responseType attribute's value to the given value.
711 SetResponseTypeRaw(aResponseType
);
714 void XMLHttpRequestMainThread::GetResponse(
715 JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aResponse
, ErrorResult
& aRv
) {
716 MOZ_DIAGNOSTIC_ASSERT(!mForWorker
);
718 switch (mResponseType
) {
719 case XMLHttpRequestResponseType::_empty
:
720 case XMLHttpRequestResponseType::Text
: {
722 GetResponseText(str
, aRv
);
726 if (!xpc::StringToJsval(aCx
, str
, aResponse
)) {
727 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
732 case XMLHttpRequestResponseType::Arraybuffer
: {
733 if (mState
!= XMLHttpRequest_Binding::DONE
) {
738 if (!mResultArrayBuffer
) {
739 mResultArrayBuffer
= mArrayBufferBuilder
->TakeArrayBuffer(aCx
);
740 if (!mResultArrayBuffer
) {
741 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
745 aResponse
.setObject(*mResultArrayBuffer
);
748 case XMLHttpRequestResponseType::Blob
: {
749 if (mState
!= XMLHttpRequest_Binding::DONE
) {
754 if (!mResponseBlobImpl
) {
759 if (!mResponseBlob
) {
760 mResponseBlob
= Blob::Create(GetOwnerGlobal(), mResponseBlobImpl
);
763 if (!GetOrCreateDOMReflector(aCx
, mResponseBlob
, aResponse
)) {
769 case XMLHttpRequestResponseType::Document
: {
770 if (!mResponseXML
|| mState
!= XMLHttpRequest_Binding::DONE
) {
776 nsContentUtils::WrapNative(aCx
, ToSupports(mResponseXML
), aResponse
);
779 case XMLHttpRequestResponseType::Json
: {
780 if (mState
!= XMLHttpRequest_Binding::DONE
) {
785 if (mResultJSON
.isUndefined()) {
786 aRv
= CreateResponseParsedJSON(aCx
);
787 TruncateResponseText();
789 // Per spec, errors aren't propagated. null is returned instead.
791 // It would be nice to log the error to the console. That's hard to
792 // do without calling window.onerror as a side effect, though.
793 JS_ClearPendingException(aCx
);
794 mResultJSON
.setNull();
797 aResponse
.set(mResultJSON
);
801 NS_ERROR("Should not happen");
807 already_AddRefed
<BlobImpl
> XMLHttpRequestMainThread::GetResponseBlobImpl() {
808 MOZ_DIAGNOSTIC_ASSERT(mForWorker
);
809 MOZ_DIAGNOSTIC_ASSERT(mResponseType
== XMLHttpRequestResponseType::Blob
);
811 if (mState
!= XMLHttpRequest_Binding::DONE
) {
815 RefPtr
<BlobImpl
> blobImpl
= mResponseBlobImpl
;
816 return blobImpl
.forget();
819 already_AddRefed
<ArrayBufferBuilder
>
820 XMLHttpRequestMainThread::GetResponseArrayBufferBuilder() {
821 MOZ_DIAGNOSTIC_ASSERT(mForWorker
);
822 MOZ_DIAGNOSTIC_ASSERT(mResponseType
==
823 XMLHttpRequestResponseType::Arraybuffer
);
825 if (mState
!= XMLHttpRequest_Binding::DONE
) {
829 RefPtr
<ArrayBufferBuilder
> builder
= mArrayBufferBuilder
;
830 return builder
.forget();
833 nsresult
XMLHttpRequestMainThread::GetResponseTextForJSON(nsAString
& aString
) {
834 if (mState
!= XMLHttpRequest_Binding::DONE
) {
835 aString
.SetIsVoid(true);
839 if (!mResponseText
.GetAsString(aString
)) {
840 return NS_ERROR_OUT_OF_MEMORY
;
846 bool XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const {
851 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
852 return loadInfo
->GetTainting() == LoadTainting::CORS
;
855 bool XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest() {
856 if (IsCrossSiteCORSRequest()) {
858 mChannel
->GetStatus(&rv
);
866 Maybe
<nsBaseChannel::ContentRange
>
867 XMLHttpRequestMainThread::GetRequestedContentRange() const {
868 MOZ_ASSERT(mChannel
);
869 nsBaseChannel
* baseChan
= static_cast<nsBaseChannel
*>(mChannel
.get());
871 return mozilla::Nothing();
873 return baseChan
->GetContentRange();
876 void XMLHttpRequestMainThread::GetContentRangeHeader(nsACString
& out
) const {
877 if (!IsBlobURI(mRequestURL
)) {
881 Maybe
<nsBaseChannel::ContentRange
> range
= GetRequestedContentRange();
882 if (range
.isSome()) {
883 range
->AsHeader(out
);
889 void XMLHttpRequestMainThread::GetResponseURL(nsAString
& aUrl
) {
892 if ((mState
== XMLHttpRequest_Binding::UNSENT
||
893 mState
== XMLHttpRequest_Binding::OPENED
) ||
898 // Make sure we don't leak responseURL information from denied cross-site
900 if (IsDeniedCrossSiteCORSRequest()) {
904 nsCOMPtr
<nsIURI
> responseUrl
;
905 if (NS_FAILED(NS_GetFinalChannelURI(mChannel
, getter_AddRefs(responseUrl
)))) {
910 responseUrl
->GetSpecIgnoringRef(temp
);
911 CopyUTF8toUTF16(temp
, aUrl
);
914 uint32_t XMLHttpRequestMainThread::GetStatus(ErrorResult
& aRv
) {
915 // Make sure we don't leak status information from denied cross-site
917 if (IsDeniedCrossSiteCORSRequest()) {
921 if (mState
== XMLHttpRequest_Binding::UNSENT
||
922 mState
== XMLHttpRequest_Binding::OPENED
) {
926 if (mErrorLoad
!= ErrorType::eOK
) {
927 // Let's simulate the http protocol for jar/app requests:
928 nsCOMPtr
<nsIJARChannel
> jarChannel
= GetCurrentJARChannel();
931 mChannel
->GetStatus(&status
);
933 if (status
== NS_ERROR_FILE_NOT_FOUND
) {
934 return 404; // Not Found
936 return 500; // Internal Error
943 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
945 // Pretend like we got a 200/206 response, since our load was successful
946 return IsBlobURI(mRequestURL
) && GetRequestedContentRange().isSome() ? 206
951 nsresult rv
= httpChannel
->GetResponseStatus(&status
);
959 void XMLHttpRequestMainThread::GetStatusText(nsACString
& aStatusText
,
961 // Return an empty status text on all error loads.
962 aStatusText
.Truncate();
964 // Make sure we don't leak status information from denied cross-site
966 if (IsDeniedCrossSiteCORSRequest()) {
970 // Check the current XHR state to see if it is valid to obtain the statusText
971 // value. This check is to prevent the status text for redirects from being
972 // available before all the redirects have been followed and HTTP headers have
974 if (mState
== XMLHttpRequest_Binding::UNSENT
||
975 mState
== XMLHttpRequest_Binding::OPENED
) {
979 if (mErrorLoad
!= ErrorType::eOK
) {
983 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
985 Unused
<< httpChannel
->GetResponseStatusText(aStatusText
);
987 aStatusText
.AssignLiteral("OK");
991 void XMLHttpRequestMainThread::TerminateOngoingFetch(nsresult detail
) {
993 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
994 mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
995 mState
== XMLHttpRequest_Binding::LOADING
) {
996 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Info
,
997 ("%p TerminateOngoingFetch(0x%" PRIx32
")", this,
998 static_cast<uint32_t>(detail
)));
999 CloseRequest(detail
);
1003 void XMLHttpRequestMainThread::CloseRequest(nsresult detail
) {
1005 mWaitingForOnStopRequest
= false;
1006 mErrorLoad
= ErrorType::eTerminated
;
1007 mErrorLoadDetail
= detail
;
1009 mChannel
->CancelWithReason(NS_BINDING_ABORTED
,
1010 "XMLHttpRequestMainThread::CloseRequest"_ns
);
1012 CancelTimeoutTimer();
1015 void XMLHttpRequestMainThread::CloseRequestWithError(
1016 const ErrorProgressEventType
& aType
) {
1018 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1019 ("%p CloseRequestWithError(%s)", this, aType
.cStr
));
1021 CloseRequest(aType
.errorCode
);
1025 // If we're in the destructor, don't risk dispatching an event.
1027 mFlagSyncLooping
= false;
1031 if (mState
!= XMLHttpRequest_Binding::UNSENT
&&
1032 !(mState
== XMLHttpRequest_Binding::OPENED
&& !mFlagSend
) &&
1033 mState
!= XMLHttpRequest_Binding::DONE
) {
1034 ChangeState(XMLHttpRequest_Binding::DONE
, true);
1036 if (!mFlagSyncLooping
) {
1037 if (mUpload
&& !mUploadComplete
) {
1038 mUploadComplete
= true;
1039 DispatchProgressEvent(mUpload
, aType
, 0, -1);
1041 DispatchProgressEvent(this, aType
, 0, -1);
1045 // The ChangeState call above calls onreadystatechange handlers which
1046 // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
1047 // the abort state bit. If this occurs we're not uninitialized (bug 361773).
1049 ChangeState(XMLHttpRequest_Binding::UNSENT
, false); // IE seems to do it
1052 mFlagSyncLooping
= false;
1055 void XMLHttpRequestMainThread::RequestErrorSteps(
1056 const ProgressEventType aEventType
, const nsresult aOptionalException
,
1058 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1059 ("%p RequestErrorSteps(%s,0x%" PRIx32
")", this, aEventType
.cStr
,
1060 static_cast<uint32_t>(aOptionalException
)));
1062 // Cancel our timers first before setting our state to done, so we don't
1063 // trip any assertions if one fires and asserts that state != done.
1064 CancelTimeoutTimer();
1065 CancelSyncTimeoutTimer();
1066 StopProgressEventTimer();
1069 mState
= XMLHttpRequest_Binding::DONE
;
1077 // If we're in the destructor, don't risk dispatching an event.
1079 mFlagSyncLooping
= false;
1084 if (mFlagSynchronous
&& NS_FAILED(aOptionalException
)) {
1085 aRv
.Throw(aOptionalException
);
1090 FireReadystatechangeEvent();
1093 if (mUpload
&& !mUploadComplete
) {
1095 mUploadComplete
= true;
1098 if (mFlagHadUploadListenersOnSend
) {
1099 // Steps 6-3, 6-4 (loadend is fired for us)
1100 DispatchProgressEvent(mUpload
, aEventType
, 0, -1);
1104 // Steps 7 and 8 (loadend is fired for us)
1105 DispatchProgressEvent(this, aEventType
, 0, -1);
1108 void XMLHttpRequestMainThread::Abort(ErrorResult
& aRv
) {
1109 NOT_CALLABLE_IN_SYNC_SEND_RV
1110 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
, ("%p Abort()", this));
1114 void XMLHttpRequestMainThread::AbortInternal(ErrorResult
& aRv
) {
1115 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
, ("%p AbortInternal()", this));
1116 mFlagAborted
= true;
1117 DisconnectDoneNotifier();
1120 TerminateOngoingFetch(NS_ERROR_DOM_ABORT_ERR
);
1123 if ((mState
== XMLHttpRequest_Binding::OPENED
&& mFlagSend
) ||
1124 mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
1125 mState
== XMLHttpRequest_Binding::LOADING
) {
1126 RequestErrorSteps(Events::abort
, NS_ERROR_DOM_ABORT_ERR
, aRv
);
1130 if (mState
== XMLHttpRequest_Binding::DONE
) {
1131 ChangeState(XMLHttpRequest_Binding::UNSENT
,
1132 false); // no ReadystateChange event
1135 mFlagSyncLooping
= false;
1138 /*Method that checks if it is safe to expose a header value to the client.
1139 It is used to check what headers are exposed for CORS requests.*/
1140 bool XMLHttpRequestMainThread::IsSafeHeader(
1141 const nsACString
& aHeader
, NotNull
<nsIHttpChannel
*> aHttpChannel
) const {
1142 // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
1143 if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(aHeader
)) {
1144 NS_WARNING("blocked access to response header");
1147 // if this is not a CORS call all headers are safe
1148 if (!IsCrossSiteCORSRequest()) {
1151 // Check for dangerous headers
1152 // Make sure we don't leak header information from denied cross-site
1156 mChannel
->GetStatus(&status
);
1157 if (NS_FAILED(status
)) {
1161 const char* kCrossOriginSafeHeaders
[] = {
1162 "cache-control", "content-language", "content-type", "content-length",
1163 "expires", "last-modified", "pragma"};
1164 for (uint32_t i
= 0; i
< ArrayLength(kCrossOriginSafeHeaders
); ++i
) {
1165 if (aHeader
.LowerCaseEqualsASCII(kCrossOriginSafeHeaders
[i
])) {
1169 nsAutoCString headerVal
;
1170 // The "Access-Control-Expose-Headers" header contains a comma separated
1171 // list of method names.
1172 Unused
<< aHttpChannel
->GetResponseHeader("Access-Control-Expose-Headers"_ns
,
1174 bool isSafe
= false;
1175 for (const nsACString
& token
:
1176 nsCCharSeparatedTokenizer(headerVal
, ',').ToRange()) {
1177 if (token
.IsEmpty()) {
1180 if (!NS_IsValidHTTPToken(token
)) {
1184 if (token
.EqualsLiteral("*") && !mFlagACwithCredentials
) {
1186 } else if (aHeader
.Equals(token
, nsCaseInsensitiveCStringComparator
)) {
1194 bool XMLHttpRequestMainThread::GetContentType(nsACString
& aValue
) const {
1195 MOZ_ASSERT(mChannel
);
1196 nsCOMPtr
<nsIURI
> uri
;
1197 if (NS_SUCCEEDED(mChannel
->GetURI(getter_AddRefs(uri
))) &&
1198 uri
->SchemeIs("data")) {
1199 nsDataChannel
* dchan
= static_cast<nsDataChannel
*>(mChannel
.get());
1201 aValue
.Assign(dchan
->MimeType());
1204 if (NS_SUCCEEDED(mChannel
->GetContentType(aValue
))) {
1206 if (NS_SUCCEEDED(mChannel
->GetContentCharset(value
)) && !value
.IsEmpty()) {
1207 aValue
.AppendLiteral(";charset=");
1208 aValue
.Append(value
);
1214 void XMLHttpRequestMainThread::GetAllResponseHeaders(
1215 nsACString
& aResponseHeaders
, ErrorResult
& aRv
) {
1216 NOT_CALLABLE_IN_SYNC_SEND_RV
1218 aResponseHeaders
.Truncate();
1220 // If the state is UNSENT or OPENED,
1221 // return the empty string and terminate these steps.
1222 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1223 mState
== XMLHttpRequest_Binding::OPENED
) {
1227 if (mErrorLoad
!= ErrorType::eOK
) {
1231 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel()) {
1232 RefPtr
<nsHeaderVisitor
> visitor
=
1233 new nsHeaderVisitor(*this, WrapNotNull(httpChannel
));
1234 if (NS_SUCCEEDED(httpChannel
->VisitResponseHeaders(visitor
))) {
1235 aResponseHeaders
= visitor
->Headers();
1244 // Even non-http channels supply content type.
1245 nsAutoCString value
;
1246 if (GetContentType(value
)) {
1247 aResponseHeaders
.AppendLiteral("Content-Type: ");
1248 aResponseHeaders
.Append(value
);
1249 aResponseHeaders
.AppendLiteral("\r\n");
1252 // Don't provide Content-Length for data URIs
1253 nsCOMPtr
<nsIURI
> uri
;
1254 if (NS_FAILED(mChannel
->GetURI(getter_AddRefs(uri
))) ||
1255 !uri
->SchemeIs("data")) {
1257 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1258 aResponseHeaders
.AppendLiteral("Content-Length: ");
1259 aResponseHeaders
.AppendInt(length
);
1260 aResponseHeaders
.AppendLiteral("\r\n");
1264 // Should set a Content-Range header for blob scheme.
1265 // From https://fetch.spec.whatwg.org/#scheme-fetch 3.blob.9.20:
1266 // "Set response’s header list to «(`Content-Length`, serializedSlicedLength),
1267 // (`Content-Type`, type), (`Content-Range`, contentRange)»."
1268 GetContentRangeHeader(value
);
1269 if (!value
.IsVoid()) {
1270 aResponseHeaders
.AppendLiteral("Content-Range: ");
1271 aResponseHeaders
.Append(value
);
1272 aResponseHeaders
.AppendLiteral("\r\n");
1276 void XMLHttpRequestMainThread::GetResponseHeader(const nsACString
& header
,
1277 nsACString
& _retval
,
1279 NOT_CALLABLE_IN_SYNC_SEND_RV
1281 _retval
.SetIsVoid(true);
1283 nsCOMPtr
<nsIHttpChannel
> httpChannel
= GetCurrentHttpChannel();
1286 // If the state is UNSENT or OPENED,
1287 // return null and terminate these steps.
1288 if (mState
== XMLHttpRequest_Binding::UNSENT
||
1289 mState
== XMLHttpRequest_Binding::OPENED
) {
1293 // Even non-http channels supply content type and content length.
1294 // Remember we don't leak header information from denied cross-site
1295 // requests. However, we handle file: and blob: URLs for blob response
1296 // types by canceling them with a specific error, so we have to allow
1297 // them to pass through this check.
1299 if (!mChannel
|| NS_FAILED(mChannel
->GetStatus(&status
)) ||
1300 (NS_FAILED(status
) && status
!= NS_ERROR_FILE_ALREADY_EXISTS
)) {
1305 if (header
.LowerCaseEqualsASCII("content-type")) {
1306 if (!GetContentType(_retval
)) {
1307 // Means no content type
1308 _retval
.SetIsVoid(true);
1314 else if (header
.LowerCaseEqualsASCII("content-length")) {
1316 if (NS_SUCCEEDED(mChannel
->GetContentLength(&length
))) {
1317 _retval
.AppendInt(length
);
1322 else if (header
.LowerCaseEqualsASCII("content-range")) {
1323 GetContentRangeHeader(_retval
);
1329 // Check for dangerous headers
1330 if (!IsSafeHeader(header
, WrapNotNull(httpChannel
))) {
1334 aRv
= httpChannel
->GetResponseHeader(header
, _retval
);
1335 if (aRv
.ErrorCodeIs(NS_ERROR_NOT_AVAILABLE
)) {
1337 _retval
.SetIsVoid(true);
1338 aRv
.SuppressException();
1342 already_AddRefed
<nsILoadGroup
> XMLHttpRequestMainThread::GetLoadGroup() const {
1343 if (mFlagBackgroundRequest
) {
1348 nsCOMPtr
<nsILoadGroup
> ref
= mLoadGroup
;
1349 return ref
.forget();
1352 Document
* doc
= GetDocumentIfCurrent();
1354 return doc
->GetDocumentLoadGroup();
1360 nsresult
XMLHttpRequestMainThread::FireReadystatechangeEvent() {
1361 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::UNSENT
);
1362 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1363 event
->InitEvent(kLiteralString_readystatechange
, false, false);
1364 // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1365 event
->SetTrusted(true);
1366 DispatchOrStoreEvent(this, event
);
1370 void XMLHttpRequestMainThread::DispatchProgressEvent(
1371 DOMEventTargetHelper
* aTarget
, const ProgressEventType
& aType
,
1372 int64_t aLoaded
, int64_t aTotal
) {
1374 NS_ASSERTION(aTarget
, "null target");
1376 if (NS_FAILED(CheckCurrentGlobalCorrectness()) ||
1377 (!AllowUploadProgress() && aTarget
== mUpload
)) {
1381 // If blocked by CORS, zero-out the stats on progress events
1382 // and never fire "progress" or "load" events at all.
1383 if (IsDeniedCrossSiteCORSRequest()) {
1384 if (aType
== Events::progress
|| aType
== Events::load
) {
1391 ProgressEventInit init
;
1392 init
.mBubbles
= false;
1393 init
.mCancelable
= false;
1394 init
.mLengthComputable
= aTotal
!= -1; // XHR spec step 6.1
1395 init
.mLoaded
= aLoaded
;
1396 init
.mTotal
= (aTotal
== -1) ? 0 : aTotal
;
1398 RefPtr
<ProgressEvent
> event
=
1399 ProgressEvent::Constructor(aTarget
, aType
, init
);
1400 event
->SetTrusted(true);
1403 gXMLHttpRequestLog
, LogLevel::Debug
,
1404 ("firing %s event (%u,%u,%" PRIu64
",%" PRIu64
")", aType
.cStr
,
1405 aTarget
== mUpload
, aTotal
!= -1, aLoaded
, (aTotal
== -1) ? 0 : aTotal
));
1407 DispatchOrStoreEvent(aTarget
, event
);
1409 // If we're sending a load, error, timeout or abort event, then
1410 // also dispatch the subsequent loadend event.
1411 if (aType
== Events::load
|| aType
== Events::error
||
1412 aType
== Events::timeout
|| aType
== Events::abort
) {
1413 DispatchProgressEvent(aTarget
, Events::loadend
, aLoaded
, aTotal
);
1417 void XMLHttpRequestMainThread::DispatchOrStoreEvent(
1418 DOMEventTargetHelper
* aTarget
, Event
* aEvent
) {
1420 MOZ_ASSERT(aTarget
);
1423 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
1427 if (mEventDispatchingSuspended
) {
1428 PendingEvent
* event
= mPendingEvents
.AppendElement();
1429 event
->mTarget
= aTarget
;
1430 event
->mEvent
= aEvent
;
1434 aTarget
->DispatchEvent(*aEvent
);
1437 void XMLHttpRequestMainThread::SuspendEventDispatching() {
1438 MOZ_ASSERT(!mEventDispatchingSuspended
);
1439 mEventDispatchingSuspended
= true;
1442 void XMLHttpRequestMainThread::ResumeEventDispatching() {
1443 MOZ_ASSERT(mEventDispatchingSuspended
);
1444 mEventDispatchingSuspended
= false;
1446 nsTArray
<PendingEvent
> pendingEvents
= std::move(mPendingEvents
);
1448 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
1452 for (uint32_t i
= 0; i
< pendingEvents
.Length(); ++i
) {
1453 pendingEvents
[i
].mTarget
->DispatchEvent(*pendingEvents
[i
].mEvent
);
1457 already_AddRefed
<nsIHttpChannel
>
1458 XMLHttpRequestMainThread::GetCurrentHttpChannel() {
1459 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
1460 return httpChannel
.forget();
1463 already_AddRefed
<nsIJARChannel
>
1464 XMLHttpRequestMainThread::GetCurrentJARChannel() {
1465 nsCOMPtr
<nsIJARChannel
> appChannel
= do_QueryInterface(mChannel
);
1466 return appChannel
.forget();
1469 bool XMLHttpRequestMainThread::IsSystemXHR() const {
1470 return mIsSystem
|| mPrincipal
->IsSystemPrincipal();
1473 bool XMLHttpRequestMainThread::InUploadPhase() const {
1474 // We're in the upload phase while our state is OPENED.
1475 return mState
== XMLHttpRequest_Binding::OPENED
;
1478 // This case is hit when the async parameter is outright omitted, which
1479 // should set it to true (and the username and password to null).
1480 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1481 const nsAString
& aUrl
, ErrorResult
& aRv
) {
1482 Open(aMethod
, aUrl
, true, VoidString(), VoidString(), aRv
);
1485 // This case is hit when the async parameter is specified, even if the
1486 // JS value was "undefined" (which due to legacy reasons should be
1487 // treated as true, which is how it will already be passed in here).
1488 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1489 const nsAString
& aUrl
, bool aAsync
,
1490 const nsAString
& aUsername
,
1491 const nsAString
& aPassword
,
1493 Open(aMethod
, NS_ConvertUTF16toUTF8(aUrl
), aAsync
, aUsername
, aPassword
, aRv
);
1496 void XMLHttpRequestMainThread::Open(const nsACString
& aMethod
,
1497 const nsACString
& aUrl
, bool aAsync
,
1498 const nsAString
& aUsername
,
1499 const nsAString
& aPassword
,
1501 DEBUG_WORKERREFS1(aMethod
<< " " << aUrl
);
1502 NOT_CALLABLE_IN_SYNC_SEND_RV
1505 if (!aAsync
&& !DontWarnAboutSyncXHR() && GetOwner() &&
1506 GetOwner()->GetExtantDoc()) {
1507 GetOwner()->GetExtantDoc()->WarnOnceAbout(
1508 DeprecatedOperations::eSyncXMLHttpRequestDeprecated
);
1511 Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC
,
1515 nsCOMPtr
<Document
> responsibleDocument
= GetDocumentIfCurrent();
1516 if (!responsibleDocument
) {
1517 // This could be because we're no longer current or because we're in some
1518 // non-window context...
1519 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
1520 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1525 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
1530 if (!aAsync
&& responsibleDocument
&& GetOwner()) {
1531 // We have no extant document during unload, so the above general
1532 // syncXHR warning will not display. But we do want to display a
1533 // recommendation to use sendBeacon instead of syncXHR during unload.
1534 nsCOMPtr
<nsIDocShell
> shell
= responsibleDocument
->GetDocShell();
1536 bool inUnload
= false;
1537 shell
->GetIsInUnload(&inUnload
);
1539 LogMessage("UseSendBeaconDuringUnloadAndPagehideWarning", GetOwner());
1545 nsAutoCString method
;
1546 aRv
= FetchUtil::GetValidRequestMethod(aMethod
, method
);
1547 if (NS_WARN_IF(aRv
.Failed())) {
1552 nsIURI
* baseURI
= nullptr;
1555 } else if (responsibleDocument
) {
1556 baseURI
= responsibleDocument
->GetBaseURI();
1559 // Use the responsible document's encoding for the URL if we have one,
1560 // except for dedicated workers. Use UTF-8 otherwise.
1561 NotNull
<const Encoding
*> originCharset
= UTF_8_ENCODING
;
1562 if (responsibleDocument
&&
1563 responsibleDocument
->NodePrincipal() == mPrincipal
) {
1564 originCharset
= responsibleDocument
->GetDocumentCharacterSet();
1567 nsCOMPtr
<nsIURI
> parsedURL
;
1569 NS_NewURI(getter_AddRefs(parsedURL
), aUrl
, originCharset
, baseURI
);
1570 if (NS_FAILED(rv
)) {
1571 aRv
.ThrowSyntaxError("'"_ns
+ aUrl
+ "' is not a valid URL."_ns
);
1574 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
1575 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1580 // This is already handled by the other Open() method, which passes
1581 // username and password in as NullStrings.
1585 parsedURL
->GetHost(host
);
1586 if (!host
.IsEmpty() && (!aUsername
.IsVoid() || !aPassword
.IsVoid())) {
1587 auto mutator
= NS_MutateURI(parsedURL
);
1588 if (!aUsername
.IsVoid()) {
1589 mutator
.SetUsername(NS_ConvertUTF16toUTF8(aUsername
));
1591 if (!aPassword
.IsVoid()) {
1592 mutator
.SetPassword(NS_ConvertUTF16toUTF8(aPassword
));
1594 Unused
<< mutator
.Finalize(parsedURL
);
1598 if (!aAsync
&& HasOrHasHadOwner() &&
1599 (mTimeoutMilliseconds
||
1600 mResponseType
!= XMLHttpRequestResponseType::_empty
)) {
1601 if (mTimeoutMilliseconds
) {
1602 LogMessage("TimeoutSyncXHRWarning", GetOwner());
1604 if (mResponseType
!= XMLHttpRequestResponseType::_empty
) {
1605 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
1607 aRv
.ThrowInvalidAccessError(
1608 "synchronous XMLHttpRequests do not support timeout and responseType");
1613 TerminateOngoingFetch(NS_OK
);
1616 // timeouts are handled without a flag
1617 DisconnectDoneNotifier();
1619 mRequestMethod
.Assign(method
);
1620 mRequestURL
= parsedURL
;
1621 mFlagSynchronous
= !aAsync
;
1622 mAuthorRequestHeaders
.Clear();
1626 mFlagHadUploadListenersOnSend
= false;
1627 mFlagAborted
= false;
1628 mFlagTimedOut
= false;
1631 // Per spec we should only create the channel on send(), but we have internal
1632 // code that relies on the channel being created now, and that code is not
1633 // always IsSystemXHR(). However, we're not supposed to throw channel-creation
1634 // errors during open(), so we silently ignore those here.
1638 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
1639 mState
= XMLHttpRequest_Binding::OPENED
;
1640 FireReadystatechangeEvent();
1644 void XMLHttpRequestMainThread::SetOriginAttributes(
1645 const OriginAttributesDictionary
& aAttrs
) {
1646 MOZ_ASSERT((mState
== XMLHttpRequest_Binding::OPENED
) && !mFlagSend
);
1648 OriginAttributes
attrs(aAttrs
);
1650 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
1651 loadInfo
->SetOriginAttributes(attrs
);
1655 * "Copy" from a stream.
1657 nsresult
XMLHttpRequestMainThread::StreamReaderFunc(
1658 nsIInputStream
* in
, void* closure
, const char* fromRawSegment
,
1659 uint32_t toOffset
, uint32_t count
, uint32_t* writeCount
) {
1660 XMLHttpRequestMainThread
* xmlHttpRequest
=
1661 static_cast<XMLHttpRequestMainThread
*>(closure
);
1662 if (!xmlHttpRequest
|| !writeCount
) {
1664 "XMLHttpRequest cannot read from stream: no closure or writeCount");
1665 return NS_ERROR_FAILURE
;
1668 nsresult rv
= NS_OK
;
1670 if (xmlHttpRequest
->mResponseType
== XMLHttpRequestResponseType::Blob
) {
1671 xmlHttpRequest
->MaybeCreateBlobStorage();
1672 rv
= xmlHttpRequest
->mBlobStorage
->Append(fromRawSegment
, count
);
1673 } else if (xmlHttpRequest
->mResponseType
==
1674 XMLHttpRequestResponseType::Arraybuffer
&&
1675 !xmlHttpRequest
->mIsMappedArrayBuffer
) {
1676 // get the initial capacity to something reasonable to avoid a bunch of
1677 // reallocs right at the start
1678 if (xmlHttpRequest
->mArrayBufferBuilder
->Capacity() == 0)
1679 xmlHttpRequest
->mArrayBufferBuilder
->SetCapacity(
1680 std::max(count
, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE
));
1682 if (NS_WARN_IF(!xmlHttpRequest
->mArrayBufferBuilder
->Append(
1683 reinterpret_cast<const uint8_t*>(fromRawSegment
), count
,
1684 XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH
))) {
1685 return NS_ERROR_OUT_OF_MEMORY
;
1688 } else if (xmlHttpRequest
->mResponseType
==
1689 XMLHttpRequestResponseType::_empty
&&
1690 xmlHttpRequest
->mResponseXML
) {
1691 // Copy for our own use
1692 if (!xmlHttpRequest
->mResponseBody
.Append(fromRawSegment
, count
,
1694 return NS_ERROR_OUT_OF_MEMORY
;
1696 } else if (xmlHttpRequest
->mResponseType
==
1697 XMLHttpRequestResponseType::_empty
||
1698 xmlHttpRequest
->mResponseType
==
1699 XMLHttpRequestResponseType::Text
||
1700 xmlHttpRequest
->mResponseType
==
1701 XMLHttpRequestResponseType::Json
) {
1702 MOZ_ASSERT(!xmlHttpRequest
->mResponseXML
,
1703 "We shouldn't be parsing a doc here");
1704 rv
= xmlHttpRequest
->AppendToResponseText(
1705 AsBytes(Span(fromRawSegment
, count
)));
1706 if (NS_WARN_IF(NS_FAILED(rv
))) {
1711 if (xmlHttpRequest
->mFlagParseBody
) {
1712 // Give the same data to the parser.
1714 // We need to wrap the data in a new lightweight stream and pass that
1715 // to the parser, because calling ReadSegments() recursively on the same
1716 // stream is not supported.
1717 nsCOMPtr
<nsIInputStream
> copyStream
;
1718 rv
= NS_NewByteInputStream(getter_AddRefs(copyStream
),
1719 Span(fromRawSegment
, count
),
1720 NS_ASSIGNMENT_DEPEND
);
1722 if (NS_SUCCEEDED(rv
) && xmlHttpRequest
->mXMLParserStreamListener
) {
1723 NS_ASSERTION(copyStream
, "NS_NewByteInputStream lied");
1724 nsresult parsingResult
=
1725 xmlHttpRequest
->mXMLParserStreamListener
->OnDataAvailable(
1726 xmlHttpRequest
->mChannel
, copyStream
, toOffset
, count
);
1728 // No use to continue parsing if we failed here, but we
1729 // should still finish reading the stream
1730 if (NS_FAILED(parsingResult
)) {
1731 xmlHttpRequest
->mFlagParseBody
= false;
1736 if (NS_SUCCEEDED(rv
)) {
1737 *writeCount
= count
;
1747 void GetBlobURIFromChannel(nsIRequest
* aRequest
, nsIURI
** aURI
) {
1748 MOZ_ASSERT(aRequest
);
1753 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1758 nsCOMPtr
<nsIURI
> uri
;
1759 nsresult rv
= channel
->GetURI(getter_AddRefs(uri
));
1760 if (NS_FAILED(rv
)) {
1764 if (!dom::IsBlobURI(uri
)) {
1771 nsresult
GetLocalFileFromChannel(nsIRequest
* aRequest
, nsIFile
** aFile
) {
1772 MOZ_ASSERT(aRequest
);
1777 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(aRequest
);
1782 nsCOMPtr
<nsIFile
> file
;
1783 nsresult rv
= fc
->GetFile(getter_AddRefs(file
));
1784 if (NS_WARN_IF(NS_FAILED(rv
))) {
1792 nsresult
DummyStreamReaderFunc(nsIInputStream
* aInputStream
, void* aClosure
,
1793 const char* aFromRawSegment
, uint32_t aToOffset
,
1794 uint32_t aCount
, uint32_t* aWriteCount
) {
1795 *aWriteCount
= aCount
;
1799 class FileCreationHandler final
: public PromiseNativeHandler
{
1803 static void Create(Promise
* aPromise
, XMLHttpRequestMainThread
* aXHR
) {
1804 MOZ_ASSERT(aPromise
);
1806 RefPtr
<FileCreationHandler
> handler
= new FileCreationHandler(aXHR
);
1807 aPromise
->AppendNativeHandler(handler
);
1810 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
1811 ErrorResult
& aRv
) override
{
1812 if (NS_WARN_IF(!aValue
.isObject())) {
1813 mXHR
->LocalFileToBlobCompleted(nullptr);
1818 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob
, &aValue
.toObject(), blob
)))) {
1819 mXHR
->LocalFileToBlobCompleted(nullptr);
1823 mXHR
->LocalFileToBlobCompleted(blob
->Impl());
1826 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
1827 ErrorResult
& aRv
) override
{
1828 mXHR
->LocalFileToBlobCompleted(nullptr);
1832 explicit FileCreationHandler(XMLHttpRequestMainThread
* aXHR
) : mXHR(aXHR
) {
1836 ~FileCreationHandler() = default;
1838 RefPtr
<XMLHttpRequestMainThread
> mXHR
;
1841 NS_IMPL_ISUPPORTS0(FileCreationHandler
)
1845 void XMLHttpRequestMainThread::LocalFileToBlobCompleted(BlobImpl
* aBlobImpl
) {
1846 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::DONE
);
1848 mResponseBlobImpl
= aBlobImpl
;
1849 mBlobStorage
= nullptr;
1850 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1852 ChangeStateToDone(mFlagSyncLooping
);
1856 XMLHttpRequestMainThread::OnDataAvailable(nsIRequest
* request
,
1857 nsIInputStream
* inStr
,
1858 uint64_t sourceOffset
,
1861 NS_ENSURE_ARG_POINTER(inStr
);
1863 mProgressSinceLastProgressEvent
= true;
1864 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
1868 if (mResponseType
== XMLHttpRequestResponseType::Blob
) {
1869 nsCOMPtr
<nsIFile
> localFile
;
1870 nsCOMPtr
<nsIURI
> blobURI
;
1871 GetBlobURIFromChannel(request
, getter_AddRefs(blobURI
));
1873 RefPtr
<BlobImpl
> blobImpl
;
1874 rv
= NS_GetBlobForBlobURI(blobURI
, getter_AddRefs(blobImpl
));
1875 if (NS_SUCCEEDED(rv
)) {
1876 mResponseBlobImpl
= blobImpl
;
1879 rv
= GetLocalFileFromChannel(request
, getter_AddRefs(localFile
));
1881 if (NS_WARN_IF(NS_FAILED(rv
))) {
1885 if (mResponseBlobImpl
|| localFile
) {
1886 mBlobStorage
= nullptr;
1887 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
1889 // The nsIStreamListener contract mandates us to read from the stream
1890 // before returning.
1892 rv
= inStr
->ReadSegments(DummyStreamReaderFunc
, nullptr, count
,
1894 NS_ENSURE_SUCCESS(rv
, rv
);
1896 ChangeState(XMLHttpRequest_Binding::LOADING
);
1898 // Cancel() must be called with an error. We use
1899 // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
1900 // just because we can retrieve the File from the channel directly.
1901 return request
->Cancel(NS_ERROR_FILE_ALREADY_EXISTS
);
1906 rv
= inStr
->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc
,
1907 (void*)this, count
, &totalRead
);
1908 NS_ENSURE_SUCCESS(rv
, rv
);
1910 // Fire the first progress event/loading state change
1911 if (mState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
) {
1912 ChangeState(XMLHttpRequest_Binding::LOADING
);
1913 if (!mFlagSynchronous
) {
1914 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
,
1917 mProgressSinceLastProgressEvent
= false;
1920 if (!mFlagSynchronous
&& !mProgressTimerIsActive
) {
1921 StartProgressEventTimer();
1928 XMLHttpRequestMainThread::OnStartRequest(nsIRequest
* request
) {
1930 AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStartRequest", NETWORK
);
1932 nsresult rv
= NS_OK
;
1934 if (request
!= mChannel
) {
1935 // Can this still happen?
1939 // Don't do anything if we have been aborted
1940 if (mState
== XMLHttpRequest_Binding::UNSENT
) {
1944 // Don't do anything if we're in mid-abort, but let the request
1945 // know (this can happen due to race conditions in valid XHRs,
1946 // see bz1070763 for info).
1948 return NS_BINDING_ABORTED
;
1951 // Don't do anything if we have timed out.
1952 if (mFlagTimedOut
) {
1956 // If we were asked for a bad range on a blob URL, but we're async,
1957 // we should throw now in order to fire an error progress event.
1958 if (IsBlobURI(mRequestURL
) && GetRequestedContentRange().isNothing() &&
1959 mAuthorRequestHeaders
.Has("range")) {
1960 return NS_ERROR_NET_PARTIAL_TRANSFER
;
1963 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
1964 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
1967 request
->GetStatus(&status
);
1968 if (mErrorLoad
== ErrorType::eOK
&& NS_FAILED(status
)) {
1969 mErrorLoad
= ErrorType::eRequest
;
1970 mErrorLoadDetail
= status
;
1973 // Upload phase is now over. If we were uploading anything,
1974 // stop the timer and fire any final progress events.
1975 if (mUpload
&& !mUploadComplete
&& mErrorLoad
== ErrorType::eOK
&&
1976 !mFlagSynchronous
) {
1977 StopProgressEventTimer();
1979 mUploadTransferred
= mUploadTotal
;
1981 if (mProgressSinceLastProgressEvent
) {
1982 DispatchProgressEvent(mUpload
, Events::progress
, mUploadTransferred
,
1984 mProgressSinceLastProgressEvent
= false;
1987 mUploadComplete
= true;
1988 DispatchProgressEvent(mUpload
, Events::load
, mUploadTotal
, mUploadTotal
);
1991 mFlagParseBody
= true;
1992 if (mErrorLoad
== ErrorType::eOK
) {
1993 ChangeState(XMLHttpRequest_Binding::HEADERS_RECEIVED
);
1998 if (!mOverrideMimeType
.IsEmpty()) {
1999 channel
->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
2002 // Fallback to 'application/octet-stream' (leaving data URLs alone)
2003 if (!IsBlobURI(mRequestURL
)) {
2005 channel
->GetContentType(type
);
2006 if (type
.IsEmpty() || type
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
2007 channel
->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM
));
2013 // Set up arraybuffer
2014 if (mResponseType
== XMLHttpRequestResponseType::Arraybuffer
&&
2015 NS_SUCCEEDED(status
)) {
2016 if (mIsMappedArrayBuffer
) {
2017 nsCOMPtr
<nsIJARChannel
> jarChannel
= do_QueryInterface(channel
);
2019 nsCOMPtr
<nsIURI
> uri
;
2020 rv
= channel
->GetURI(getter_AddRefs(uri
));
2021 if (NS_SUCCEEDED(rv
)) {
2023 nsAutoCString scheme
;
2024 uri
->GetScheme(scheme
);
2025 if (scheme
.LowerCaseEqualsLiteral("jar")) {
2026 nsCOMPtr
<nsIJARURI
> jarURI
= do_QueryInterface(uri
);
2028 jarURI
->GetJAREntry(file
);
2031 nsCOMPtr
<nsIFile
> jarFile
;
2032 jarChannel
->GetJarFile(getter_AddRefs(jarFile
));
2034 mIsMappedArrayBuffer
= false;
2036 rv
= mArrayBufferBuilder
->MapToFileInPackage(file
, jarFile
);
2037 // This can happen legitimately if there are compressed files
2038 // in the jarFile. See bug #1357219. No need to warn on the error.
2039 if (NS_FAILED(rv
)) {
2040 mIsMappedArrayBuffer
= false;
2042 channel
->SetContentType("application/mem-mapped"_ns
);
2048 // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
2049 // and we want it fallback to the malloc way.
2050 if (!mIsMappedArrayBuffer
) {
2051 int64_t contentLength
;
2052 rv
= channel
->GetContentLength(&contentLength
);
2053 if (NS_SUCCEEDED(rv
) && contentLength
> 0 &&
2054 contentLength
< XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE
) {
2055 mArrayBufferBuilder
->SetCapacity(static_cast<int32_t>(contentLength
));
2060 // Set up responseXML
2061 // Fetch spec Main Fetch step 21: ignore body for head/connect methods.
2062 bool parseBody
= (mResponseType
== XMLHttpRequestResponseType::_empty
||
2063 mResponseType
== XMLHttpRequestResponseType::Document
) &&
2064 !(mRequestMethod
.EqualsLiteral("HEAD") ||
2065 mRequestMethod
.EqualsLiteral("CONNECT"));
2068 // Do not try to parse documents if content-length = 0
2069 int64_t contentLength
;
2070 if (NS_SUCCEEDED(mChannel
->GetContentLength(&contentLength
)) &&
2071 contentLength
== 0) {
2077 mWarnAboutSyncHtml
= false;
2078 if (parseBody
&& NS_SUCCEEDED(status
)) {
2079 // We can gain a huge performance win by not even trying to
2080 // parse non-XML data. This also protects us from the situation
2081 // where we have an XML document and sink, but HTML (or other)
2082 // parser, which can produce unreliable results.
2084 channel
->GetContentType(type
);
2086 if ((mResponseType
== XMLHttpRequestResponseType::Document
) &&
2087 type
.EqualsLiteral("text/html")) {
2088 // HTML parsing is only supported for responseType == "document" to
2089 // avoid running the parser and, worse, populating responseXML for
2090 // legacy users of XHR who use responseType == "" for retrieving the
2091 // responseText of text/html resources. This legacy case is so common
2092 // that it's not useful to emit a warning about it.
2093 if (mFlagSynchronous
) {
2094 // We don't make cool new features available in the bad synchronous
2095 // mode. The synchronous mode is for legacy only.
2096 mWarnAboutSyncHtml
= true;
2097 mFlagParseBody
= false;
2101 } else if (!type
.IsEmpty() && (!(type
.EqualsLiteral("text/xml") ||
2102 type
.EqualsLiteral("application/xml") ||
2103 StringEndsWith(type
, "+xml"_ns
)))) {
2104 // Follow https://xhr.spec.whatwg.org/
2105 // If final MIME type is not null, text/html, text/xml, application/xml,
2106 // or does not end in +xml, return null.
2107 mFlagParseBody
= false;
2110 // The request failed, so we shouldn't be parsing anyway
2111 mFlagParseBody
= false;
2114 if (mFlagParseBody
) {
2115 nsCOMPtr
<nsIURI
> baseURI
, docURI
;
2116 rv
= mChannel
->GetURI(getter_AddRefs(docURI
));
2117 NS_ENSURE_SUCCESS(rv
, rv
);
2120 nsCOMPtr
<Document
> doc
= GetDocumentIfCurrent();
2121 nsCOMPtr
<nsIURI
> chromeXHRDocURI
, chromeXHRDocBaseURI
;
2123 chromeXHRDocURI
= doc
->GetDocumentURI();
2124 chromeXHRDocBaseURI
= doc
->GetBaseURI();
2126 // If we're no longer current, just kill the load, though it really should
2127 // have been killed already.
2128 if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
2129 return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
;
2133 // Create an empty document from it.
2134 const auto& emptyStr
= u
""_ns
;
2135 nsIGlobalObject
* global
= DOMEventTargetHelper::GetParentObject();
2137 nsCOMPtr
<nsIPrincipal
> requestingPrincipal
;
2138 rv
= nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
2139 channel
, getter_AddRefs(requestingPrincipal
));
2140 NS_ENSURE_SUCCESS(rv
, rv
);
2142 rv
= NS_NewDOMDocument(
2143 getter_AddRefs(mResponseXML
), emptyStr
, emptyStr
, nullptr, docURI
,
2144 baseURI
, requestingPrincipal
, true, global
,
2145 mIsHtml
? DocumentFlavorHTML
: DocumentFlavorLegacyGuess
);
2146 NS_ENSURE_SUCCESS(rv
, rv
);
2147 mResponseXML
->SetChromeXHRDocURI(chromeXHRDocURI
);
2148 mResponseXML
->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI
);
2150 // suppress parsing failure messages to console for statuses which
2151 // can have empty bodies (see bug 884693).
2152 IgnoredErrorResult rv2
;
2153 uint32_t responseStatus
= GetStatus(rv2
);
2154 if (!rv2
.Failed() && (responseStatus
== 201 || responseStatus
== 202 ||
2155 responseStatus
== 204 || responseStatus
== 205 ||
2156 responseStatus
== 304)) {
2157 mResponseXML
->SetSuppressParserErrorConsoleMessages(true);
2160 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2161 bool isCrossSite
= false;
2162 isCrossSite
= loadInfo
->GetTainting() != LoadTainting::Basic
;
2165 mResponseXML
->DisableCookieAccess();
2168 nsCOMPtr
<nsIStreamListener
> listener
;
2169 nsCOMPtr
<nsILoadGroup
> loadGroup
;
2170 channel
->GetLoadGroup(getter_AddRefs(loadGroup
));
2172 // suppress <parsererror> nodes on XML document parse failure, but only
2173 // for non-privileged code (including Web Extensions). See bug 289714.
2174 if (!IsSystemXHR()) {
2175 mResponseXML
->SetSuppressParserErrorElement(true);
2178 rv
= mResponseXML
->StartDocumentLoad(kLoadAsData
, channel
, loadGroup
,
2179 nullptr, getter_AddRefs(listener
),
2181 NS_ENSURE_SUCCESS(rv
, rv
);
2183 // the spec requires the response document.referrer to be the empty string
2184 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2185 new ReferrerInfo(nullptr, mResponseXML
->ReferrerPolicy());
2186 mResponseXML
->SetReferrerInfo(referrerInfo
);
2188 mXMLParserStreamListener
= listener
;
2189 rv
= mXMLParserStreamListener
->OnStartRequest(request
);
2190 NS_ENSURE_SUCCESS(rv
, rv
);
2193 // Download phase beginning; start the progress event timer if necessary.
2194 if (NS_SUCCEEDED(rv
) && HasListenersFor(nsGkAtoms::onprogress
)) {
2195 StartProgressEventTimer();
2202 XMLHttpRequestMainThread::OnStopRequest(nsIRequest
* request
, nsresult status
) {
2204 AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStopRequest", NETWORK
);
2206 if (request
!= mChannel
) {
2207 // Can this still happen?
2211 // Send the decoder the signal that we've hit the end of the stream,
2212 // but only when decoding text eagerly.
2213 if (mDecoder
&& ((mResponseType
== XMLHttpRequestResponseType::Text
) ||
2214 (mResponseType
== XMLHttpRequestResponseType::Json
) ||
2215 (mResponseType
== XMLHttpRequestResponseType::_empty
&&
2217 AppendToResponseText(Span
<const uint8_t>(), true);
2220 mWaitingForOnStopRequest
= false;
2222 // make sure to notify the listener if we were aborted
2223 // XXX in fact, why don't we do the cleanup below in this case??
2224 // UNSENT is for abort calls. See OnStartRequest above.
2225 if (mState
== XMLHttpRequest_Binding::UNSENT
|| mFlagTimedOut
) {
2226 if (mXMLParserStreamListener
)
2227 (void)mXMLParserStreamListener
->OnStopRequest(request
, status
);
2231 // Is this good enough here?
2232 if (mXMLParserStreamListener
&& mFlagParseBody
) {
2233 mXMLParserStreamListener
->OnStopRequest(request
, status
);
2236 mXMLParserStreamListener
= nullptr;
2239 // If window.stop() or other aborts were issued, handle as an abort
2240 if (status
== NS_BINDING_ABORTED
) {
2241 mFlagParseBody
= false;
2242 IgnoredErrorResult rv
;
2243 RequestErrorSteps(Events::abort
, NS_ERROR_DOM_ABORT_ERR
, rv
);
2244 ChangeState(XMLHttpRequest_Binding::UNSENT
, false);
2248 // If we were just reading a blob URL, we're already done
2249 if (status
== NS_ERROR_FILE_ALREADY_EXISTS
&& mResponseBlobImpl
) {
2250 ChangeStateToDone(mFlagSyncLooping
);
2254 bool waitingForBlobCreation
= false;
2256 // If we have this error, we have to deal with a file: URL + responseType =
2257 // blob. We have this error because we canceled the channel. The status will
2259 if (!mResponseBlobImpl
&& status
== NS_ERROR_FILE_ALREADY_EXISTS
&&
2260 mResponseType
== XMLHttpRequestResponseType::Blob
) {
2261 nsCOMPtr
<nsIFile
> file
;
2262 nsresult rv
= GetLocalFileFromChannel(request
, getter_AddRefs(file
));
2263 if (NS_WARN_IF(NS_FAILED(rv
))) {
2268 nsAutoCString contentType
;
2269 rv
= mChannel
->GetContentType(contentType
);
2270 if (NS_WARN_IF(NS_FAILED(rv
))) {
2274 ChromeFilePropertyBag bag
;
2275 CopyUTF8toUTF16(contentType
, bag
.mType
);
2277 nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal();
2280 RefPtr
<Promise
> promise
=
2281 FileCreatorHelper::CreateFile(global
, file
, bag
, true, error
);
2282 if (NS_WARN_IF(error
.Failed())) {
2283 return error
.StealNSResult();
2286 FileCreationHandler::Create(promise
, this);
2287 waitingForBlobCreation
= true;
2290 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
2291 NS_ASSERTION(mResponseText
.IsEmpty(), "mResponseText should be empty");
2295 if (NS_SUCCEEDED(status
) &&
2296 mResponseType
== XMLHttpRequestResponseType::Blob
&&
2297 !waitingForBlobCreation
) {
2298 // Smaller files may be written in cache map instead of separate files.
2299 // Also, no-store response cannot be written in persistent cache.
2300 nsAutoCString contentType
;
2301 if (!mOverrideMimeType
.IsEmpty()) {
2302 contentType
.Assign(NS_ConvertUTF16toUTF8(mOverrideMimeType
));
2304 mChannel
->GetContentType(contentType
);
2307 // mBlobStorage can be null if the channel is non-file non-cacheable
2308 // and if the response length is zero.
2309 MaybeCreateBlobStorage();
2310 mBlobStorage
->GetBlobImplWhenReady(contentType
, this);
2311 waitingForBlobCreation
= true;
2313 NS_ASSERTION(mResponseBody
.IsEmpty(), "mResponseBody should be empty");
2314 NS_ASSERTION(mResponseText
.IsEmpty(), "mResponseText should be empty");
2315 } else if (NS_SUCCEEDED(status
) && !mIsMappedArrayBuffer
&&
2316 mResponseType
== XMLHttpRequestResponseType::Arraybuffer
) {
2317 // set the capacity down to the actual length, to realloc back
2318 // down to the actual size
2319 if (!mArrayBufferBuilder
->SetCapacity(mArrayBufferBuilder
->Length())) {
2320 // this should never happen!
2321 status
= NS_ERROR_UNEXPECTED
;
2325 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(request
));
2326 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
2328 channel
->SetNotificationCallbacks(nullptr);
2329 mNotificationCallbacks
= nullptr;
2330 mChannelEventSink
= nullptr;
2331 mProgressEventSink
= nullptr;
2333 bool wasSync
= mFlagSyncLooping
;
2334 mFlagSyncLooping
= false;
2335 mRequestSentTime
= 0;
2337 // update our charset and decoder to match mResponseXML,
2338 // before it is possibly nulled out
2339 MatchCharsetAndDecoderToResponseDocument();
2341 if (NS_FAILED(status
)) {
2342 // This can happen if the server is unreachable. Other possible
2343 // reasons are that the user leaves the page or hits the ESC key.
2345 mErrorLoad
= ErrorType::eUnreachable
;
2346 mErrorLoadDetail
= status
;
2347 mResponseXML
= nullptr;
2349 // Handle network errors specifically per spec.
2350 if (NS_ERROR_GET_MODULE(status
) == NS_ERROR_MODULE_NETWORK
) {
2351 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
2352 ("%p detected networking error 0x%" PRIx32
"\n", this,
2353 static_cast<uint32_t>(status
)));
2354 IgnoredErrorResult rv
;
2355 mFlagParseBody
= false;
2356 RequestErrorSteps(Events::error
, NS_ERROR_DOM_NETWORK_ERR
, rv
);
2357 // RequestErrorSteps will not call ChangeStateToDone for sync XHRs, so we
2358 // do so here to ensure progress events are sent and our state is sane.
2359 if (mFlagSynchronous
) {
2360 ChangeStateToDone(wasSync
);
2365 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
2366 ("%p detected unreachable error 0x%" PRIx32
"\n", this,
2367 static_cast<uint32_t>(status
)));
2370 // If we're uninitialized at this point, we encountered an error
2371 // earlier and listeners have already been notified. Also we do
2372 // not want to do this if we already completed.
2373 if (mState
== XMLHttpRequest_Binding::UNSENT
||
2374 mState
== XMLHttpRequest_Binding::DONE
) {
2378 if (!mResponseXML
) {
2379 mFlagParseBody
= false;
2381 // We postpone the 'done' until the creation of the Blob is completed.
2382 if (!waitingForBlobCreation
) {
2383 ChangeStateToDone(wasSync
);
2390 NS_ASSERTION(!mFlagSyncLooping
,
2391 "We weren't supposed to support HTML parsing with XHR!");
2392 mParseEndListener
= new nsXHRParseEndListener(this);
2393 RefPtr
<EventTarget
> eventTarget
= mResponseXML
;
2394 EventListenerManager
* manager
= eventTarget
->GetOrCreateListenerManager();
2395 manager
->AddEventListenerByType(mParseEndListener
,
2396 kLiteralString_DOMContentLoaded
,
2397 TrustedEventsAtSystemGroupBubble());
2400 mFlagParseBody
= false;
2403 // We might have been sent non-XML data. If that was the case,
2404 // we should null out the document member. The idea in this
2405 // check here is that if there is no document element it is not
2406 // an XML document. We might need a fancier check...
2407 if (!mResponseXML
->GetRootElement()) {
2408 mErrorParsingXML
= true;
2409 mResponseXML
= nullptr;
2411 ChangeStateToDone(wasSync
);
2415 void XMLHttpRequestMainThread::OnBodyParseEnd() {
2416 mFlagParseBody
= false;
2417 mParseEndListener
= nullptr;
2418 ChangeStateToDone(mFlagSyncLooping
);
2421 void XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument() {
2424 mDecoder
->Encoding() != mResponseXML
->GetDocumentCharacterSet())) {
2425 TruncateResponseText();
2426 mResponseBodyDecodedPos
= 0;
2427 mEofDecoded
= false;
2428 mDecoder
= mResponseXML
->GetDocumentCharacterSet()->NewDecoder();
2432 void XMLHttpRequestMainThread::DisconnectDoneNotifier() {
2433 if (mDelayedDoneNotifier
) {
2434 // Disconnect may release the last reference to 'this'.
2435 RefPtr
<XMLHttpRequestMainThread
> kungfuDeathGrip
= this;
2436 mDelayedDoneNotifier
->Disconnect();
2437 mDelayedDoneNotifier
= nullptr;
2441 void XMLHttpRequestMainThread::ChangeStateToDone(bool aWasSync
) {
2443 DisconnectDoneNotifier();
2445 if (!mForWorker
&& !aWasSync
&& mChannel
) {
2446 // If the top level page is loading, try to postpone the handling of the
2448 nsLoadFlags loadFlags
= 0;
2449 mChannel
->GetLoadFlags(&loadFlags
);
2450 if (loadFlags
& nsIRequest::LOAD_BACKGROUND
) {
2451 nsPIDOMWindowInner
* owner
= GetOwner();
2452 BrowsingContext
* bc
= owner
? owner
->GetBrowsingContext() : nullptr;
2453 bc
= bc
? bc
->Top() : nullptr;
2454 if (bc
&& bc
->IsLoading()) {
2455 MOZ_ASSERT(!mDelayedDoneNotifier
);
2456 RefPtr
<XMLHttpRequestDoneNotifier
> notifier
=
2457 new XMLHttpRequestDoneNotifier(this);
2458 mDelayedDoneNotifier
= notifier
;
2459 bc
->AddDeprioritizedLoadRunner(notifier
);
2465 ChangeStateToDoneInternal();
2468 void XMLHttpRequestMainThread::ChangeStateToDoneInternal() {
2470 DisconnectDoneNotifier();
2471 StopProgressEventTimer();
2473 MOZ_ASSERT(!mFlagParseBody
,
2474 "ChangeStateToDone() called before async HTML parsing is done.");
2478 CancelTimeoutTimer();
2480 // Per spec, fire the last download progress event, if any,
2481 // before readystatechange=4/done. (Note that 0-sized responses
2482 // will have not sent a progress event yet, so one must be sent here).
2483 if (!mFlagSynchronous
&&
2484 (!mLoadTransferred
|| mProgressSinceLastProgressEvent
)) {
2485 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
, mLoadTotal
);
2486 mProgressSinceLastProgressEvent
= false;
2489 // Notify the document when an XHR request completes successfully.
2490 // This is used by the password manager as a hint to observe DOM mutations.
2491 // Call this prior to changing state to DONE to ensure we set up the
2492 // observer before mutations occur.
2493 if (mErrorLoad
== ErrorType::eOK
) {
2494 Document
* doc
= GetDocumentIfCurrent();
2496 doc
->NotifyFetchOrXHRSuccess();
2500 // Per spec, fire readystatechange=4/done before final error events.
2501 ChangeState(XMLHttpRequest_Binding::DONE
, true);
2503 // Per spec, if we failed in the upload phase, fire a final error
2504 // and loadend events for the upload after readystatechange=4/done.
2505 if (!mFlagSynchronous
&& mUpload
&& !mUploadComplete
) {
2506 DispatchProgressEvent(mUpload
, Events::error
, 0, -1);
2509 // Per spec, fire download's load/error and loadend events after
2510 // readystatechange=4/done (and of course all upload events).
2511 if (mErrorLoad
!= ErrorType::eOK
) {
2512 DispatchProgressEvent(this, Events::error
, 0, -1);
2514 DispatchProgressEvent(this, Events::load
, mLoadTransferred
, mLoadTotal
);
2517 if (mErrorLoad
!= ErrorType::eOK
) {
2518 // By nulling out channel here we make it so that Send() can test
2519 // for that and throw. Also calling the various status
2520 // methods/members will not throw.
2521 // This matches what IE does.
2526 nsresult
XMLHttpRequestMainThread::CreateChannel() {
2528 // When we are called from JS we can find the load group for the page,
2529 // and add ourselves to it. This way any pending requests
2530 // will be automatically aborted if the user leaves the page.
2531 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetLoadGroup();
2533 nsSecurityFlags secFlags
;
2534 nsLoadFlags loadFlags
= nsIRequest::LOAD_BACKGROUND
;
2535 uint32_t sandboxFlags
= 0;
2536 if (mPrincipal
->IsSystemPrincipal()) {
2537 // When chrome is loading we want to make sure to sandbox any potential
2538 // result document. We also want to allow cross-origin loads.
2539 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
2540 sandboxFlags
= SANDBOXED_ORIGIN
;
2541 } else if (IsSystemXHR()) {
2542 // For pages that have appropriate permissions, we want to still allow
2543 // cross-origin loads, but make sure that the any potential result
2544 // documents get the same principal as the loader.
2545 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
|
2546 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
2547 loadFlags
|= nsIChannel::LOAD_BYPASS_SERVICE_WORKER
;
2549 // Otherwise use CORS. Again, make sure that potential result documents
2550 // use the same principal as the loader.
2551 secFlags
= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
|
2552 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
2556 secFlags
|= nsILoadInfo::SEC_COOKIES_OMIT
;
2559 // Use the responsibleDocument if we have it, except for dedicated workers
2560 // where it will be the parent document, which is not the one we want to use.
2562 nsCOMPtr
<Document
> responsibleDocument
= GetDocumentIfCurrent();
2563 if (responsibleDocument
&&
2564 responsibleDocument
->NodePrincipal() == mPrincipal
) {
2565 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
,
2566 responsibleDocument
, secFlags
,
2567 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2568 nullptr, // aPerformanceStorage
2570 nullptr, // aCallbacks
2571 loadFlags
, nullptr, sandboxFlags
);
2572 } else if (mClientInfo
.isSome()) {
2573 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
, mPrincipal
,
2574 mClientInfo
.ref(), mController
, secFlags
,
2575 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2577 mPerformanceStorage
, // aPerformanceStorage
2579 nullptr, // aCallbacks
2580 loadFlags
, nullptr, sandboxFlags
);
2582 // Otherwise use the principal.
2583 rv
= NS_NewChannel(getter_AddRefs(mChannel
), mRequestURL
, mPrincipal
,
2584 secFlags
, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST
,
2586 mPerformanceStorage
, // aPerformanceStorage
2588 nullptr, // aCallbacks
2589 loadFlags
, nullptr, sandboxFlags
);
2591 NS_ENSURE_SUCCESS(rv
, rv
);
2593 if (mCSPEventListener
) {
2594 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2595 rv
= loadInfo
->SetCspEventListener(mCSPEventListener
);
2596 NS_ENSURE_SUCCESS(rv
, rv
);
2599 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2601 rv
= httpChannel
->SetRequestMethod(mRequestMethod
);
2602 NS_ENSURE_SUCCESS(rv
, rv
);
2604 httpChannel
->SetSource(profiler_capture_backtrace());
2606 // Set the initiator type
2607 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChannel
));
2609 timedChannel
->SetInitiatorType(u
"xmlhttprequest"_ns
);
2616 void XMLHttpRequestMainThread::MaybeLowerChannelPriority() {
2617 nsCOMPtr
<Document
> doc
= GetDocumentIfCurrent();
2623 if (!jsapi
.Init(GetOwnerGlobal())) {
2627 JSContext
* cx
= jsapi
.cx();
2629 if (!doc
->IsScriptTracking(cx
)) {
2633 if (StaticPrefs::network_http_tailing_enabled()) {
2634 nsCOMPtr
<nsIClassOfService
> cos
= do_QueryInterface(mChannel
);
2636 // Adding TailAllowed to overrule the Unblocked flag, but to preserve
2637 // the effect of Unblocked when tailing is off.
2638 cos
->AddClassFlags(nsIClassOfService::Throttleable
|
2639 nsIClassOfService::Tail
|
2640 nsIClassOfService::TailAllowed
);
2644 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(mChannel
);
2646 p
->SetPriority(nsISupportsPriority::PRIORITY_LOWEST
);
2650 nsresult
XMLHttpRequestMainThread::InitiateFetch(
2651 already_AddRefed
<nsIInputStream
> aUploadStream
, int64_t aUploadLength
,
2652 nsACString
& aUploadContentType
) {
2655 nsCOMPtr
<nsIInputStream
> uploadStream
= std::move(aUploadStream
);
2657 if (!uploadStream
) {
2658 RefPtr
<PreloaderBase
> preload
= FindPreload();
2660 // Because of bug 682305, we can't let listener be the XHR object itself
2661 // because JS wouldn't be able to use it. So create a listener around
2662 // 'this'. Make sure to hold a strong reference so that we don't leak the
2664 nsCOMPtr
<nsIStreamListener
> listener
=
2665 new net::nsStreamListenerWrapper(this);
2666 rv
= preload
->AsyncConsume(listener
);
2667 if (NS_SUCCEEDED(rv
)) {
2668 mFromPreload
= true;
2670 // May be null when the preload has already finished, but the XHR code
2671 // is safe to live with it.
2672 mChannel
= preload
->Channel();
2673 MOZ_ASSERT(mChannel
);
2674 EnsureChannelContentType();
2682 // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
2683 // in turn keeps STOP button from becoming active. If the consumer passed in
2684 // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
2685 // necko won't generate any progress notifications.
2686 if (HasListenersFor(nsGkAtoms::onprogress
) ||
2687 (mUpload
&& mUpload
->HasListenersFor(nsGkAtoms::onprogress
))) {
2688 nsLoadFlags loadFlags
;
2689 mChannel
->GetLoadFlags(&loadFlags
);
2690 loadFlags
&= ~nsIRequest::LOAD_BACKGROUND
;
2691 loadFlags
|= nsIRequest::LOAD_NORMAL
;
2692 mChannel
->SetLoadFlags(loadFlags
);
2695 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
2697 // If the user hasn't overridden the Accept header, set it to */* per spec.
2698 if (!mAuthorRequestHeaders
.Has("accept")) {
2699 mAuthorRequestHeaders
.Set("accept", "*/*"_ns
);
2702 mAuthorRequestHeaders
.ApplyToChannel(httpChannel
, false, false);
2704 if (!IsSystemXHR()) {
2705 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
2706 nsCOMPtr
<Document
> doc
= owner
? owner
->GetExtantDoc() : nullptr;
2707 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2708 ReferrerInfo::CreateForFetch(mPrincipal
, doc
);
2709 Unused
<< httpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
2712 // Some extensions override the http protocol handler and provide their own
2713 // implementation. The channels returned from that implementation don't
2714 // always seem to implement the nsIUploadChannel2 interface, presumably
2715 // because it's a new interface. Eventually we should remove this and simply
2716 // require that http channels implement the new interface (see bug 529041).
2717 nsCOMPtr
<nsIUploadChannel2
> uploadChannel2
= do_QueryInterface(httpChannel
);
2718 if (!uploadChannel2
) {
2719 nsCOMPtr
<nsIConsoleService
> consoleService
=
2720 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
2721 if (consoleService
) {
2722 consoleService
->LogStringMessage(
2723 u
"Http channel implementation doesn't support nsIUploadChannel2. "
2724 "An extension has supplied a non-functional http protocol handler. "
2725 "This will break behavior and in future releases not work at all.");
2730 // If necessary, wrap the stream in a buffered stream so as to guarantee
2731 // support for our upload when calling ExplicitSetUploadStream.
2732 if (!NS_InputStreamIsBuffered(uploadStream
)) {
2733 nsCOMPtr
<nsIInputStream
> bufferedStream
;
2734 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
),
2735 uploadStream
.forget(), 4096);
2736 NS_ENSURE_SUCCESS(rv
, rv
);
2738 uploadStream
= bufferedStream
;
2741 // We want to use a newer version of the upload channel that won't
2742 // ignore the necessary headers for an empty Content-Type.
2743 nsCOMPtr
<nsIUploadChannel2
> uploadChannel2(
2744 do_QueryInterface(httpChannel
));
2745 // This assertion will fire if buggy extensions are installed
2746 NS_ASSERTION(uploadChannel2
, "http must support nsIUploadChannel2");
2747 if (uploadChannel2
) {
2748 uploadChannel2
->ExplicitSetUploadStream(
2749 uploadStream
, aUploadContentType
, mUploadTotal
, mRequestMethod
,
2752 // The http channel doesn't support the new nsIUploadChannel2.
2753 // Emulate it as best we can using nsIUploadChannel.
2754 if (aUploadContentType
.IsEmpty()) {
2755 aUploadContentType
.AssignLiteral("application/octet-stream");
2757 nsCOMPtr
<nsIUploadChannel
> uploadChannel
=
2758 do_QueryInterface(httpChannel
);
2759 uploadChannel
->SetUploadStream(uploadStream
, aUploadContentType
,
2761 // Reset the method to its original value
2762 rv
= httpChannel
->SetRequestMethod(mRequestMethod
);
2763 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2768 // Should set a Content-Range header for blob scheme, and also slice the
2769 // blob appropriately, so we process the Range header here for later use.
2770 if (IsBlobURI(mRequestURL
)) {
2771 nsAutoCString range
;
2772 mAuthorRequestHeaders
.Get("range", range
);
2773 if (!range
.IsVoid()) {
2774 rv
= NS_SetChannelContentRangeForBlobURI(mChannel
, mRequestURL
, range
);
2775 if (mFlagSynchronous
&& NS_FAILED(rv
)) {
2776 // We later fire an error progress event for non-sync
2777 mState
= XMLHttpRequest_Binding::DONE
;
2778 return NS_ERROR_DOM_NETWORK_ERR
;
2783 // Due to the chrome-only XHR.channel API, we need a hacky way to set the
2784 // SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
2785 // .withCredentials can be called after open() is called.
2786 // Not doing this for privileged system XHRs since those don't use CORS.
2787 if (!IsSystemXHR() && !mIsAnon
&& mFlagACwithCredentials
) {
2788 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2789 static_cast<net::LoadInfo
*>(loadInfo
.get())->SetIncludeCookiesSecFlag();
2792 // We never let XHR be blocked by head CSS/JS loads to avoid potential
2793 // deadlock where server generation of CSS/JS requires an XHR signal.
2794 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(mChannel
));
2796 cos
->AddClassFlags(nsIClassOfService::Unblocked
);
2798 // Mark channel as urgent-start if the XHR is triggered by user input
2800 if (UserActivation::IsHandlingUserInput()) {
2801 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
2805 // Disable Necko-internal response timeouts.
2806 nsCOMPtr
<nsIHttpChannelInternal
> internalHttpChannel(
2807 do_QueryInterface(mChannel
));
2808 if (internalHttpChannel
) {
2809 rv
= internalHttpChannel
->SetResponseTimeoutEnabled(false);
2810 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2814 AddLoadFlags(mChannel
, nsIChannel::LOAD_EXPLICIT_CREDENTIALS
);
2817 // Bypass the network cache in cases where it makes no sense:
2818 // POST responses are always unique, and we provide no API that would
2819 // allow our consumers to specify a "cache key" to access old POST
2820 // responses, so they are not worth caching.
2821 if (mRequestMethod
.EqualsLiteral("POST")) {
2822 AddLoadFlags(mChannel
, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE
|
2823 nsIRequest::INHIBIT_CACHING
);
2825 // When we are sync loading, we need to bypass the local cache when it would
2826 // otherwise block us waiting for exclusive access to the cache. If we
2827 // don't do this, then we could dead lock in some cases (see bug 309424).
2829 // Also don't block on the cache entry on async if it is busy - favoring
2830 // parallelism over cache hit rate for xhr. This does not disable the cache
2831 // everywhere - only in cases where more than one channel for the same URI
2832 // is accessed simultanously.
2833 AddLoadFlags(mChannel
, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
);
2836 EnsureChannelContentType();
2838 // Set up the preflight if needed
2839 if (!IsSystemXHR()) {
2840 nsTArray
<nsCString
> CORSUnsafeHeaders
;
2841 mAuthorRequestHeaders
.GetCORSUnsafeHeaders(CORSUnsafeHeaders
);
2842 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
2843 loadInfo
->SetCorsPreflightInfo(CORSUnsafeHeaders
,
2844 mFlagHadUploadListenersOnSend
);
2847 // Hook us up to listen to redirects and the like. Only do this very late
2848 // since this creates a cycle between the channel and us. This cycle has
2849 // to be manually broken if anything below fails.
2850 mChannel
->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks
));
2851 mChannel
->SetNotificationCallbacks(this);
2853 if (internalHttpChannel
) {
2854 internalHttpChannel
->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
2857 // Because of bug 682305, we can't let listener be the XHR object itself
2858 // because JS wouldn't be able to use it. So create a listener around 'this'.
2859 // Make sure to hold a strong reference so that we don't leak the wrapper.
2860 nsCOMPtr
<nsIStreamListener
> listener
= new net::nsStreamListenerWrapper(this);
2862 // Check if this XHR is created from a tracking script.
2863 // If yes, lower the channel's priority.
2864 if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
2865 MaybeLowerChannelPriority();
2868 // Associate any originating stack with the channel.
2869 NotifyNetworkMonitorAlternateStack(mChannel
, std::move(mOriginStack
));
2871 // Start reading from the channel
2872 rv
= mChannel
->AsyncOpen(listener
);
2874 if (NS_WARN_IF(NS_FAILED(rv
))) {
2875 // Drop our ref to the channel to avoid cycles. Also drop channel's
2876 // ref to us to be extra safe.
2877 mChannel
->SetNotificationCallbacks(mNotificationCallbacks
);
2880 mErrorLoad
= ErrorType::eChannelOpen
;
2881 mErrorLoadDetail
= rv
;
2883 // Per spec, we throw on sync errors, but not async.
2884 if (mFlagSynchronous
) {
2885 mState
= XMLHttpRequest_Binding::DONE
;
2886 return NS_ERROR_DOM_NETWORK_ERR
;
2893 already_AddRefed
<PreloaderBase
> XMLHttpRequestMainThread::FindPreload() {
2894 Document
* doc
= GetDocumentIfCurrent();
2898 if (mPrincipal
->IsSystemPrincipal() || IsSystemXHR()) {
2901 if (!mRequestMethod
.EqualsLiteral("GET")) {
2902 // Preload can only do GET.
2905 if (!mAuthorRequestHeaders
.IsEmpty()) {
2906 // Preload can't set headers.
2910 // mIsAnon overrules mFlagACwithCredentials.
2911 CORSMode cors
= (mIsAnon
|| !mFlagACwithCredentials
)
2912 ? CORSMode::CORS_ANONYMOUS
2913 : CORSMode::CORS_USE_CREDENTIALS
;
2914 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2915 ReferrerInfo::CreateForFetch(mPrincipal
, doc
);
2916 auto key
= PreloadHashKey::CreateAsFetch(mRequestURL
, cors
);
2917 RefPtr
<PreloaderBase
> preload
= doc
->Preloads().LookupPreload(key
);
2922 preload
->RemoveSelf(doc
);
2923 preload
->NotifyUsage(doc
, PreloaderBase::LoadBackground::Keep
);
2925 return preload
.forget();
2928 void XMLHttpRequestMainThread::EnsureChannelContentType() {
2929 MOZ_ASSERT(mChannel
);
2931 // We don't mess with the content type of a blob URL.
2932 if (IsBlobURI(mRequestURL
)) {
2936 // Since we expect XML data, set the type hint accordingly
2937 // if the channel doesn't know any content type.
2938 // This means that we always try to parse local files as XML
2939 // ignoring return value, as this is not critical. Use text/xml as fallback
2941 nsAutoCString contentType
;
2942 if (NS_FAILED(mChannel
->GetContentType(contentType
)) ||
2943 contentType
.IsEmpty() ||
2944 contentType
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
2945 mChannel
->SetContentType("text/xml"_ns
);
2949 void XMLHttpRequestMainThread::ResumeTimeout() {
2951 MOZ_ASSERT(NS_IsMainThread());
2952 MOZ_ASSERT(mFlagSynchronous
);
2954 if (mResumeTimeoutRunnable
) {
2955 DispatchToMainThread(mResumeTimeoutRunnable
.forget());
2956 mResumeTimeoutRunnable
= nullptr;
2960 void XMLHttpRequestMainThread::Send(
2962 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
>&
2965 DEBUG_WORKERREFS1(mRequestURL
);
2966 NOT_CALLABLE_IN_SYNC_SEND_RV
2968 if (!CanSend(aRv
)) {
2972 if (aData
.IsNull()) {
2973 SendInternal(nullptr, false, aRv
);
2977 if (aData
.Value().IsDocument()) {
2978 BodyExtractor
<Document
> body(&aData
.Value().GetAsDocument());
2979 SendInternal(&body
, true, aRv
);
2983 if (aData
.Value().IsBlob()) {
2984 BodyExtractor
<const Blob
> body(&aData
.Value().GetAsBlob());
2985 SendInternal(&body
, false, aRv
);
2989 if (aData
.Value().IsArrayBuffer()) {
2990 BodyExtractor
<const ArrayBuffer
> body(&aData
.Value().GetAsArrayBuffer());
2991 SendInternal(&body
, false, aRv
);
2995 if (aData
.Value().IsArrayBufferView()) {
2996 BodyExtractor
<const ArrayBufferView
> body(
2997 &aData
.Value().GetAsArrayBufferView());
2998 SendInternal(&body
, false, aRv
);
3002 if (aData
.Value().IsFormData()) {
3003 BodyExtractor
<const FormData
> body(&aData
.Value().GetAsFormData());
3004 SendInternal(&body
, false, aRv
);
3008 if (aData
.Value().IsURLSearchParams()) {
3009 BodyExtractor
<const URLSearchParams
> body(
3010 &aData
.Value().GetAsURLSearchParams());
3011 SendInternal(&body
, false, aRv
);
3015 if (aData
.Value().IsUSVString()) {
3016 BodyExtractor
<const nsAString
> body(&aData
.Value().GetAsUSVString());
3017 SendInternal(&body
, true, aRv
);
3022 nsresult
XMLHttpRequestMainThread::MaybeSilentSendFailure(nsresult aRv
) {
3023 // Per spec, silently fail on async request failures; throw for sync.
3024 if (mFlagSynchronous
) {
3025 mState
= XMLHttpRequest_Binding::DONE
;
3026 return NS_ERROR_DOM_NETWORK_ERR
;
3029 // Defer the actual sending of async events just in case listeners
3030 // are attached after the send() method is called.
3031 Unused
<< NS_WARN_IF(
3032 NS_FAILED(DispatchToMainThread(NewRunnableMethod
<ErrorProgressEventType
>(
3033 "dom::XMLHttpRequestMainThread::CloseRequestWithError", this,
3034 &XMLHttpRequestMainThread::CloseRequestWithError
, Events::error
))));
3038 bool XMLHttpRequestMainThread::CanSend(ErrorResult
& aRv
) {
3040 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
3045 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
3046 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
3052 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3056 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
3057 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
3064 void XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase
* aBody
,
3065 bool aBodyIsDocumentOrString
,
3068 MOZ_ASSERT(NS_IsMainThread());
3070 // We expect that CanSend has been called before we get here!
3071 // We cannot move the remaining two checks below there because
3072 // MaybeSilentSendFailure can cause unexpected side effects if called
3075 // If open() failed to create the channel, then throw a network error
3076 // as per spec. We really should create the channel here in send(), but
3077 // we have internal code relying on the channel being created in open().
3079 mErrorLoad
= ErrorType::eChannelOpen
;
3080 mErrorLoadDetail
= NS_ERROR_DOM_NETWORK_ERR
;
3081 mFlagSend
= true; // so CloseRequestWithError sets us to DONE.
3082 aRv
= MaybeSilentSendFailure(mErrorLoadDetail
);
3086 // non-GET requests aren't allowed for blob.
3087 if (IsBlobURI(mRequestURL
) && !mRequestMethod
.EqualsLiteral("GET")) {
3088 mErrorLoad
= ErrorType::eChannelOpen
;
3089 mErrorLoadDetail
= NS_ERROR_DOM_NETWORK_ERR
;
3090 mFlagSend
= true; // so CloseRequestWithError sets us to DONE.
3091 aRv
= MaybeSilentSendFailure(mErrorLoadDetail
);
3095 // XXX We should probably send a warning to the JS console
3096 // if there are no event listeners set and we are doing
3097 // an asynchronous call.
3099 mUploadTransferred
= 0;
3101 // By default we don't have any upload, so mark upload complete.
3102 mUploadComplete
= true;
3103 mErrorLoad
= ErrorType::eOK
;
3104 mErrorLoadDetail
= NS_OK
;
3106 nsCOMPtr
<nsIInputStream
> uploadStream
;
3107 nsAutoCString uploadContentType
;
3108 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mChannel
));
3109 if (aBody
&& httpChannel
&& !mRequestMethod
.EqualsLiteral("GET") &&
3110 !mRequestMethod
.EqualsLiteral("HEAD")) {
3111 nsAutoCString charset
;
3112 nsAutoCString defaultContentType
;
3114 aRv
= aBody
->GetAsStream(getter_AddRefs(uploadStream
), &size_u64
,
3115 defaultContentType
, charset
);
3120 // make sure it fits within js MAX_SAFE_INTEGER
3122 net::InScriptableRange(size_u64
) ? static_cast<int64_t>(size_u64
) : -1;
3125 // If author set no Content-Type, use the default from GetAsStream().
3126 mAuthorRequestHeaders
.Get("content-type", uploadContentType
);
3127 if (uploadContentType
.IsVoid()) {
3128 uploadContentType
= defaultContentType
;
3129 } else if (aBodyIsDocumentOrString
) {
3130 UniquePtr
<CMimeType
> contentTypeRecord
=
3131 CMimeType::Parse(uploadContentType
);
3132 nsAutoCString charset
;
3133 if (contentTypeRecord
&&
3134 contentTypeRecord
->GetParameterValue(kLiteralString_charset
,
3136 !charset
.EqualsIgnoreCase("utf-8")) {
3137 contentTypeRecord
->SetParameterValue(kLiteralString_charset
,
3138 kLiteralString_UTF_8
);
3139 contentTypeRecord
->Serialize(uploadContentType
);
3141 } else if (!charset
.IsEmpty()) {
3142 // We don't want to set a charset for streams.
3143 // Replace all case-insensitive matches of the charset in the
3144 // content-type with the correct case.
3145 RequestHeaders::CharsetIterator
iter(uploadContentType
);
3146 while (iter
.Next()) {
3147 if (!iter
.Equals(charset
, nsCaseInsensitiveCStringComparator
)) {
3148 iter
.Replace(charset
);
3153 mUploadComplete
= false;
3159 // Check if we should enable cross-origin upload listeners.
3160 if (mUpload
&& mUpload
->HasListeners()) {
3161 mFlagHadUploadListenersOnSend
= true;
3164 mIsMappedArrayBuffer
= false;
3165 if (mResponseType
== XMLHttpRequestResponseType::Arraybuffer
&&
3166 StaticPrefs::dom_mapped_arraybuffer_enabled()) {
3167 nsCOMPtr
<nsIURI
> uri
;
3168 nsAutoCString scheme
;
3170 aRv
= mChannel
->GetURI(getter_AddRefs(uri
));
3171 if (!aRv
.Failed()) {
3172 uri
->GetScheme(scheme
);
3173 if (scheme
.LowerCaseEqualsLiteral("jar")) {
3174 mIsMappedArrayBuffer
= true;
3179 aRv
= InitiateFetch(uploadStream
.forget(), mUploadTotal
, uploadContentType
);
3184 // Start our timeout
3185 mRequestSentTime
= PR_Now();
3186 StartTimeoutTimer();
3188 mWaitingForOnStopRequest
= true;
3193 // If we're synchronous, spin an event loop here and wait
3194 RefPtr
<Document
> suspendedDoc
;
3195 if (mFlagSynchronous
) {
3196 auto scopeExit
= MakeScopeExit([&] {
3197 CancelSyncTimeoutTimer();
3199 ResumeEventDispatching();
3201 Maybe
<AutoSuppressEventHandling
> autoSuppress
;
3203 mFlagSyncLooping
= true;
3206 if (nsCOMPtr
<nsPIDOMWindowOuter
> topWindow
=
3207 GetOwner()->GetOuterWindow()->GetInProcessTop()) {
3208 if (nsCOMPtr
<nsPIDOMWindowInner
> topInner
=
3209 topWindow
->GetCurrentInnerWindow()) {
3210 suspendedDoc
= topWindow
->GetExtantDoc();
3211 autoSuppress
.emplace(topWindow
->GetBrowsingContext());
3212 topInner
->Suspend();
3213 mResumeTimeoutRunnable
= new nsResumeTimeoutsEvent(topInner
);
3218 SuspendEventDispatching();
3219 StopProgressEventTimer();
3221 SyncTimeoutType syncTimeoutType
= MaybeStartSyncTimeoutTimer();
3222 if (syncTimeoutType
== eErrorOrExpired
) {
3224 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
3228 nsAutoSyncOperation
sync(suspendedDoc
,
3229 SyncOperationBehavior::eSuspendInput
);
3230 if (!SpinEventLoopUntil("XMLHttpRequestMainThread::SendInternal"_ns
,
3231 [&]() { return !mFlagSyncLooping
; })) {
3232 aRv
.Throw(NS_ERROR_UNEXPECTED
);
3236 // Time expired... We should throw.
3237 if (syncTimeoutType
== eTimerStarted
&& !mSyncTimeoutTimer
) {
3238 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
3242 // Now that we've successfully opened the channel, we can change state. Note
3243 // that this needs to come after the AsyncOpen() and rv check, because this
3244 // can run script that would try to restart this request, and that could end
3245 // up doing our AsyncOpen on a null channel if the reentered AsyncOpen
3247 StopProgressEventTimer();
3249 // Upload phase beginning; start the progress event timer if necessary.
3250 if (mUpload
&& mUpload
->HasListenersFor(nsGkAtoms::onprogress
)) {
3251 StartProgressEventTimer();
3253 // Dispatch loadstart events
3254 DispatchProgressEvent(this, Events::loadstart
, 0, -1);
3255 if (mUpload
&& !mUploadComplete
) {
3256 DispatchProgressEvent(mUpload
, Events::loadstart
, 0, mUploadTotal
);
3261 aRv
= MaybeSilentSendFailure(NS_ERROR_DOM_NETWORK_ERR
);
3265 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
3266 void XMLHttpRequestMainThread::SetRequestHeader(const nsACString
& aName
,
3267 const nsACString
& aValue
,
3269 NOT_CALLABLE_IN_SYNC_SEND_RV
3272 if (mState
!= XMLHttpRequest_Binding::OPENED
) {
3273 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
3279 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3284 nsAutoCString value
;
3285 NS_TrimHTTPWhitespace(aValue
, value
);
3288 if (!NS_IsValidHTTPToken(aName
) || !NS_IsReasonableHTTPHeaderValue(value
)) {
3289 aRv
.Throw(NS_ERROR_DOM_INVALID_HEADER_NAME
);
3294 bool isPrivilegedCaller
= IsSystemXHR();
3295 bool isForbiddenHeader
=
3296 nsContentUtils::IsForbiddenRequestHeader(aName
, aValue
);
3297 if (!isPrivilegedCaller
&& isForbiddenHeader
) {
3298 AutoTArray
<nsString
, 1> params
;
3299 CopyUTF8toUTF16(aName
, *params
.AppendElement());
3300 LogMessage("ForbiddenHeaderWarning", GetOwner(), params
);
3305 // Skipping for now, as normalizing the case of header names may not be
3306 // web-compatible. See bug 1285036.
3309 // Gecko-specific: invalid headers can be set by privileged
3310 // callers, but will not merge.
3311 if (isPrivilegedCaller
&& isForbiddenHeader
) {
3312 mAuthorRequestHeaders
.Set(aName
, value
);
3314 mAuthorRequestHeaders
.MergeOrSet(aName
, value
);
3318 void XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout
, ErrorResult
& aRv
) {
3319 NOT_CALLABLE_IN_SYNC_SEND_RV
3321 if (mFlagSynchronous
&& mState
!= XMLHttpRequest_Binding::UNSENT
&&
3322 HasOrHasHadOwner()) {
3323 /* Timeout is not supported for synchronous requests with an owning window,
3325 LogMessage("TimeoutSyncXHRWarning", GetOwner());
3326 aRv
.ThrowInvalidAccessError(
3327 "synchronous XMLHttpRequests do not support timeout and responseType");
3331 mTimeoutMilliseconds
= aTimeout
;
3332 if (mRequestSentTime
) {
3333 StartTimeoutTimer();
3337 nsIEventTarget
* XMLHttpRequestMainThread::GetTimerEventTarget() {
3338 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3339 return global
->SerialEventTarget();
3344 nsresult
XMLHttpRequestMainThread::DispatchToMainThread(
3345 already_AddRefed
<nsIRunnable
> aRunnable
) {
3347 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3348 return global
->Dispatch(std::move(aRunnable
));
3350 return NS_DispatchToMainThread(std::move(aRunnable
));
3353 void XMLHttpRequestMainThread::StartTimeoutTimer() {
3357 "StartTimeoutTimer mustn't be called before the request was sent!");
3358 if (mState
== XMLHttpRequest_Binding::DONE
) {
3363 CancelTimeoutTimer();
3365 if (!mTimeoutMilliseconds
) {
3369 if (!mTimeoutTimer
) {
3370 mTimeoutTimer
= NS_NewTimer(GetTimerEventTarget());
3373 (uint32_t)((PR_Now() - mRequestSentTime
) / PR_USEC_PER_MSEC
);
3374 mTimeoutTimer
->InitWithCallback(
3375 this, mTimeoutMilliseconds
> elapsed
? mTimeoutMilliseconds
- elapsed
: 0,
3376 nsITimer::TYPE_ONE_SHOT
);
3379 uint16_t XMLHttpRequestMainThread::ReadyState() const { return mState
; }
3381 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString
& aMimeType
,
3383 NOT_CALLABLE_IN_SYNC_SEND_RV
3385 if (mState
== XMLHttpRequest_Binding::LOADING
||
3386 mState
== XMLHttpRequest_Binding::DONE
) {
3387 aRv
.ThrowInvalidStateError(
3388 "Cannot call 'overrideMimeType()' on XMLHttpRequest after 'send()' "
3389 "(when its state is LOADING or DONE).");
3393 UniquePtr
<MimeType
> parsed
= MimeType::Parse(aMimeType
);
3395 parsed
->Serialize(mOverrideMimeType
);
3397 mOverrideMimeType
.AssignLiteral(APPLICATION_OCTET_STREAM
);
3401 bool XMLHttpRequestMainThread::MozBackgroundRequest() const {
3402 return mFlagBackgroundRequest
;
3405 void XMLHttpRequestMainThread::SetMozBackgroundRequestExternal(
3406 bool aMozBackgroundRequest
, ErrorResult
& aRv
) {
3407 if (!IsSystemXHR()) {
3408 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
3412 if (mState
!= XMLHttpRequest_Binding::UNSENT
) {
3413 // Can't change this while we're in the middle of something.
3414 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3418 mFlagBackgroundRequest
= aMozBackgroundRequest
;
3421 void XMLHttpRequestMainThread::SetMozBackgroundRequest(
3422 bool aMozBackgroundRequest
, ErrorResult
& aRv
) {
3423 // No errors for this webIDL method on main-thread.
3424 SetMozBackgroundRequestExternal(aMozBackgroundRequest
, IgnoreErrors());
3427 void XMLHttpRequestMainThread::SetOriginStack(
3428 UniquePtr
<SerializedStackHolder
> aOriginStack
) {
3429 mOriginStack
= std::move(aOriginStack
);
3432 void XMLHttpRequestMainThread::SetSource(
3433 UniquePtr
<ProfileChunkedBuffer
> aSource
) {
3437 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
3440 httpChannel
->SetSource(std::move(aSource
));
3444 bool XMLHttpRequestMainThread::WithCredentials() const {
3445 return mFlagACwithCredentials
;
3448 void XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials
,
3450 NOT_CALLABLE_IN_SYNC_SEND_RV
3452 // Return error if we're already processing a request. Note that we can't use
3453 // ReadyState() here, because it can't differentiate between "opened" and
3454 // "sent", so we use mState directly.
3456 if ((mState
!= XMLHttpRequest_Binding::UNSENT
&&
3457 mState
!= XMLHttpRequest_Binding::OPENED
) ||
3458 mFlagSend
|| mIsAnon
) {
3459 aRv
.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
3463 mFlagACwithCredentials
= aWithCredentials
;
3466 nsresult
XMLHttpRequestMainThread::ChangeState(uint16_t aState
,
3469 nsresult rv
= NS_OK
;
3471 if (aState
!= XMLHttpRequest_Binding::HEADERS_RECEIVED
&&
3472 aState
!= XMLHttpRequest_Binding::LOADING
) {
3473 StopProgressEventTimer();
3477 (!mFlagSynchronous
|| aState
== XMLHttpRequest_Binding::OPENED
||
3478 aState
== XMLHttpRequest_Binding::DONE
)) {
3479 rv
= FireReadystatechangeEvent();
3485 /////////////////////////////////////////////////////
3486 // nsIChannelEventSink methods:
3489 XMLHttpRequestMainThread::AsyncOnChannelRedirect(
3490 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
3491 nsIAsyncVerifyRedirectCallback
* callback
) {
3493 MOZ_ASSERT(aNewChannel
, "Redirect without a channel?");
3495 // Prepare to receive callback
3496 mRedirectCallback
= callback
;
3497 mNewRedirectChannel
= aNewChannel
;
3499 if (mChannelEventSink
) {
3500 nsCOMPtr
<nsIAsyncVerifyRedirectCallback
> fwd
= EnsureXPCOMifier();
3502 nsresult rv
= mChannelEventSink
->AsyncOnChannelRedirect(
3503 aOldChannel
, aNewChannel
, aFlags
, fwd
);
3504 if (NS_FAILED(rv
)) {
3505 mRedirectCallback
= nullptr;
3506 mNewRedirectChannel
= nullptr;
3511 // we need to strip Authentication headers for cross-origin requests
3512 // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
3514 StaticPrefs::network_fetch_redirect_stripAuthHeader() &&
3515 NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel
, aNewChannel
, aFlags
);
3517 OnRedirectVerifyCallback(NS_OK
, stripAuth
);
3522 nsresult
XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result
,
3525 NS_ASSERTION(mRedirectCallback
, "mRedirectCallback not set in callback");
3526 NS_ASSERTION(mNewRedirectChannel
, "mNewRedirectChannel not set in callback");
3528 if (NS_SUCCEEDED(result
)) {
3529 bool rewriteToGET
= false;
3530 nsCOMPtr
<nsIHttpChannel
> oldHttpChannel
= GetCurrentHttpChannel();
3532 Unused
<< oldHttpChannel
->ShouldStripRequestBodyHeader(mRequestMethod
,
3535 mChannel
= mNewRedirectChannel
;
3537 nsCOMPtr
<nsIHttpChannel
> newHttpChannel(do_QueryInterface(mChannel
));
3538 if (newHttpChannel
) {
3539 // Ensure all original headers are duplicated for the new channel (bug
3541 mAuthorRequestHeaders
.ApplyToChannel(newHttpChannel
, rewriteToGET
,
3545 mErrorLoad
= ErrorType::eRedirect
;
3546 mErrorLoadDetail
= result
;
3549 mNewRedirectChannel
= nullptr;
3551 mRedirectCallback
->OnRedirectVerifyCallback(result
);
3552 mRedirectCallback
= nullptr;
3554 // It's important that we return success here. If we return the result code
3555 // that we were passed, JavaScript callers who cancel the redirect will wind
3556 // up throwing an exception in the process.
3560 /////////////////////////////////////////////////////
3561 // nsIProgressEventSink methods:
3565 XMLHttpRequestMainThread::OnProgress(nsIRequest
* aRequest
, int64_t aProgress
,
3566 int64_t aProgressMax
) {
3568 // When uploading, OnProgress reports also headers in aProgress and
3569 // aProgressMax. So, try to remove the headers, if possible.
3570 bool lengthComputable
= (aProgressMax
!= -1);
3571 if (InUploadPhase()) {
3572 int64_t loaded
= aProgress
;
3573 if (lengthComputable
) {
3574 int64_t headerSize
= aProgressMax
- mUploadTotal
;
3575 loaded
-= headerSize
;
3577 mUploadTransferred
= loaded
;
3578 mProgressSinceLastProgressEvent
= true;
3580 if (!mFlagSynchronous
&& !mProgressTimerIsActive
) {
3581 StartProgressEventTimer();
3584 mLoadTotal
= aProgressMax
;
3585 mLoadTransferred
= aProgress
;
3586 // OnDataAvailable() handles mProgressSinceLastProgressEvent
3587 // for the download phase.
3590 if (mProgressEventSink
) {
3591 mProgressEventSink
->OnProgress(aRequest
, aProgress
, aProgressMax
);
3598 XMLHttpRequestMainThread::OnStatus(nsIRequest
* aRequest
, nsresult aStatus
,
3599 const char16_t
* aStatusArg
) {
3601 if (mProgressEventSink
) {
3602 mProgressEventSink
->OnStatus(aRequest
, aStatus
, aStatusArg
);
3608 bool XMLHttpRequestMainThread::AllowUploadProgress() {
3609 return !IsCrossSiteCORSRequest() || mFlagHadUploadListenersOnSend
;
3612 /////////////////////////////////////////////////////
3613 // nsIInterfaceRequestor methods:
3616 XMLHttpRequestMainThread::GetInterface(const nsIID
& aIID
, void** aResult
) {
3619 // Make sure to return ourselves for the channel event sink interface and
3620 // progress event sink interface, no matter what. We can forward these to
3621 // mNotificationCallbacks if it wants to get notifications for them. But we
3622 // need to see these notifications for proper functioning.
3623 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
3624 mChannelEventSink
= do_GetInterface(mNotificationCallbacks
);
3625 *aResult
= static_cast<nsIChannelEventSink
*>(EnsureXPCOMifier().take());
3627 } else if (aIID
.Equals(NS_GET_IID(nsIProgressEventSink
))) {
3628 mProgressEventSink
= do_GetInterface(mNotificationCallbacks
);
3629 *aResult
= static_cast<nsIProgressEventSink
*>(EnsureXPCOMifier().take());
3633 // Now give mNotificationCallbacks (if non-null) a chance to return the
3634 // desired interface.
3635 if (mNotificationCallbacks
) {
3636 rv
= mNotificationCallbacks
->GetInterface(aIID
, aResult
);
3637 if (NS_SUCCEEDED(rv
)) {
3638 NS_ASSERTION(*aResult
, "Lying nsIInterfaceRequestor implementation!");
3643 if (!mFlagBackgroundRequest
&& (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
3644 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
)))) {
3645 nsCOMPtr
<nsIPromptFactory
> wwatch
=
3646 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
3647 NS_ENSURE_SUCCESS(rv
, rv
);
3649 // Get the an auth prompter for our window so that the parenting
3650 // of the dialogs works as it should when using tabs.
3651 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
3653 window
= GetOwner()->GetOuterWindow();
3655 return wwatch
->GetPrompt(window
, aIID
, reinterpret_cast<void**>(aResult
));
3658 // Now check for the various XHR non-DOM interfaces, except
3659 // nsIProgressEventSink and nsIChannelEventSink which we already
3661 if (aIID
.Equals(NS_GET_IID(nsIStreamListener
))) {
3662 *aResult
= static_cast<nsIStreamListener
*>(EnsureXPCOMifier().take());
3665 if (aIID
.Equals(NS_GET_IID(nsIRequestObserver
))) {
3666 *aResult
= static_cast<nsIRequestObserver
*>(EnsureXPCOMifier().take());
3669 if (aIID
.Equals(NS_GET_IID(nsITimerCallback
))) {
3670 *aResult
= static_cast<nsITimerCallback
*>(EnsureXPCOMifier().take());
3674 return QueryInterface(aIID
, aResult
);
3677 void XMLHttpRequestMainThread::GetInterface(
3678 JSContext
* aCx
, JS::Handle
<JS::Value
> aIID
,
3679 JS::MutableHandle
<JS::Value
> aRetval
, ErrorResult
& aRv
) {
3680 dom::GetInterface(aCx
, this, aIID
, aRetval
, aRv
);
3683 XMLHttpRequestUpload
* XMLHttpRequestMainThread::GetUpload(ErrorResult
& aRv
) {
3685 mUpload
= new XMLHttpRequestUpload(this);
3690 bool XMLHttpRequestMainThread::MozAnon() const { return mIsAnon
; }
3692 bool XMLHttpRequestMainThread::MozSystem() const { return IsSystemXHR(); }
3694 void XMLHttpRequestMainThread::HandleTimeoutCallback() {
3696 if (mState
== XMLHttpRequest_Binding::DONE
) {
3697 MOZ_ASSERT_UNREACHABLE(
3698 "XMLHttpRequestMainThread::HandleTimeoutCallback "
3699 "with completed request");
3704 mFlagTimedOut
= true;
3705 CloseRequestWithError(Events::timeout
);
3708 void XMLHttpRequestMainThread::CancelTimeoutTimer() {
3710 if (mTimeoutTimer
) {
3711 mTimeoutTimer
->Cancel();
3712 mTimeoutTimer
= nullptr;
3717 XMLHttpRequestMainThread::Notify(nsITimer
* aTimer
) {
3719 if (mProgressNotifier
== aTimer
) {
3720 HandleProgressTimerCallback();
3724 if (mTimeoutTimer
== aTimer
) {
3725 HandleTimeoutCallback();
3729 if (mSyncTimeoutTimer
== aTimer
) {
3730 HandleSyncTimeoutTimer();
3734 // Just in case some JS user wants to QI to nsITimerCallback and play with
3736 NS_WARNING("Unexpected timer!");
3737 return NS_ERROR_INVALID_POINTER
;
3740 void XMLHttpRequestMainThread::HandleProgressTimerCallback() {
3742 // Don't fire the progress event if mLoadTotal is 0, see XHR spec step 6.1
3743 if (!mLoadTotal
&& mLoadTransferred
) {
3747 mProgressTimerIsActive
= false;
3749 if (!mProgressSinceLastProgressEvent
|| mErrorLoad
!= ErrorType::eOK
) {
3753 if (InUploadPhase()) {
3754 if (mUpload
&& !mUploadComplete
&& mFlagHadUploadListenersOnSend
) {
3755 DispatchProgressEvent(mUpload
, Events::progress
, mUploadTransferred
,
3759 FireReadystatechangeEvent();
3760 DispatchProgressEvent(this, Events::progress
, mLoadTransferred
, mLoadTotal
);
3763 mProgressSinceLastProgressEvent
= false;
3765 StartProgressEventTimer();
3768 void XMLHttpRequestMainThread::StopProgressEventTimer() {
3769 if (mProgressNotifier
) {
3770 mProgressTimerIsActive
= false;
3771 mProgressNotifier
->Cancel();
3775 void XMLHttpRequestMainThread::StartProgressEventTimer() {
3776 if (!mProgressNotifier
) {
3777 mProgressNotifier
= NS_NewTimer(GetTimerEventTarget());
3779 if (mProgressNotifier
) {
3780 mProgressTimerIsActive
= true;
3781 mProgressNotifier
->Cancel();
3782 mProgressNotifier
->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL
,
3783 nsITimer::TYPE_ONE_SHOT
);
3787 XMLHttpRequestMainThread::SyncTimeoutType
3788 XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer() {
3789 MOZ_ASSERT(mFlagSynchronous
);
3791 Document
* doc
= GetDocumentIfCurrent();
3792 if (!doc
|| !doc
->GetPageUnloadingEventTimeStamp()) {
3793 return eNoTimerNeeded
;
3796 // If we are in a beforeunload or a unload event, we must force a timeout.
3798 (TimeStamp::NowLoRes() - doc
->GetPageUnloadingEventTimeStamp());
3799 if (diff
.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING
) {
3800 return eErrorOrExpired
;
3803 mSyncTimeoutTimer
= NS_NewTimer(GetTimerEventTarget());
3804 if (!mSyncTimeoutTimer
) {
3805 return eErrorOrExpired
;
3808 uint32_t timeout
= MAX_SYNC_TIMEOUT_WHEN_UNLOADING
- diff
.ToMilliseconds();
3809 nsresult rv
= mSyncTimeoutTimer
->InitWithCallback(this, timeout
,
3810 nsITimer::TYPE_ONE_SHOT
);
3811 return NS_FAILED(rv
) ? eErrorOrExpired
: eTimerStarted
;
3814 void XMLHttpRequestMainThread::HandleSyncTimeoutTimer() {
3815 MOZ_ASSERT(mSyncTimeoutTimer
);
3816 MOZ_ASSERT(mFlagSyncLooping
);
3818 CancelSyncTimeoutTimer();
3820 mErrorLoadDetail
= NS_ERROR_DOM_TIMEOUT_ERR
;
3823 void XMLHttpRequestMainThread::CancelSyncTimeoutTimer() {
3824 if (mSyncTimeoutTimer
) {
3825 mSyncTimeoutTimer
->Cancel();
3826 mSyncTimeoutTimer
= nullptr;
3830 already_AddRefed
<nsXMLHttpRequestXPCOMifier
>
3831 XMLHttpRequestMainThread::EnsureXPCOMifier() {
3833 mXPCOMifier
= new nsXMLHttpRequestXPCOMifier(this);
3835 RefPtr
<nsXMLHttpRequestXPCOMifier
> newRef(mXPCOMifier
);
3836 return newRef
.forget();
3839 bool XMLHttpRequestMainThread::ShouldBlockAuthPrompt() {
3840 // Verify that it's ok to prompt for credentials here, per spec
3841 // http://xhr.spec.whatwg.org/#the-send%28%29-method
3843 if (mAuthorRequestHeaders
.Has("authorization")) {
3847 nsCOMPtr
<nsIURI
> uri
;
3848 nsresult rv
= mChannel
->GetURI(getter_AddRefs(uri
));
3849 if (NS_WARN_IF(NS_FAILED(rv
))) {
3853 // Also skip if a username and/or password is provided in the URI.
3855 return NS_SUCCEEDED(uri
->GetHasUserPass(&hasUserPass
)) && hasUserPass
;
3858 void XMLHttpRequestMainThread::TruncateResponseText() {
3859 mResponseText
.Truncate();
3860 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
3863 NS_IMPL_ISUPPORTS(XMLHttpRequestMainThread::nsHeaderVisitor
,
3864 nsIHttpHeaderVisitor
)
3866 NS_IMETHODIMP
XMLHttpRequestMainThread::nsHeaderVisitor::VisitHeader(
3867 const nsACString
& header
, const nsACString
& value
) {
3868 if (mXHR
.IsSafeHeader(header
, mHttpChannel
)) {
3869 nsAutoCString
lowerHeader(header
);
3870 ToLowerCase(lowerHeader
);
3871 if (!mHeaderList
.InsertElementSorted(HeaderEntry(lowerHeader
, value
),
3873 return NS_ERROR_OUT_OF_MEMORY
;
3879 XMLHttpRequestMainThread::nsHeaderVisitor::nsHeaderVisitor(
3880 const XMLHttpRequestMainThread
& aXMLHttpRequest
,
3881 NotNull
<nsIHttpChannel
*> aHttpChannel
)
3882 : mXHR(aXMLHttpRequest
), mHttpChannel(aHttpChannel
) {}
3884 XMLHttpRequestMainThread::nsHeaderVisitor::~nsHeaderVisitor() = default;
3886 void XMLHttpRequestMainThread::MaybeCreateBlobStorage() {
3888 MOZ_ASSERT(mResponseType
== XMLHttpRequestResponseType::Blob
);
3894 MutableBlobStorage::MutableBlobStorageType storageType
=
3895 BasePrincipal::Cast(mPrincipal
)->PrivateBrowsingId() == 0
3896 ? MutableBlobStorage::eCouldBeInTemporaryFile
3897 : MutableBlobStorage::eOnlyInMemory
;
3899 nsCOMPtr
<nsIEventTarget
> eventTarget
;
3900 if (nsIGlobalObject
* global
= GetOwnerGlobal()) {
3901 eventTarget
= global
->SerialEventTarget();
3904 mBlobStorage
= new MutableBlobStorage(storageType
, eventTarget
);
3907 void XMLHttpRequestMainThread::BlobStoreCompleted(
3908 MutableBlobStorage
* aBlobStorage
, BlobImpl
* aBlobImpl
, nsresult aRv
) {
3910 // Ok, the state is changed...
3911 if (mBlobStorage
!= aBlobStorage
|| NS_FAILED(aRv
)) {
3915 MOZ_ASSERT(mState
!= XMLHttpRequest_Binding::DONE
);
3917 mResponseBlobImpl
= aBlobImpl
;
3918 mBlobStorage
= nullptr;
3920 ChangeStateToDone(mFlagSyncLooping
);
3924 XMLHttpRequestMainThread::GetName(nsACString
& aName
) {
3925 aName
.AssignLiteral("XMLHttpRequest");
3929 // nsXMLHttpRequestXPCOMifier implementation
3930 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier
)
3931 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
3932 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
3933 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
3934 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback
)
3935 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
3936 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
3937 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
3938 NS_INTERFACE_MAP_ENTRY(nsINamed
)
3939 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIStreamListener
)
3940 NS_INTERFACE_MAP_END
3942 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier
)
3943 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier
)
3945 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
3946 // inheritance from nsISupports.
3947 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier
)
3949 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier
)
3951 tmp
->mXHR
->mXPCOMifier
= nullptr;
3953 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR
)
3954 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3956 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier
)
3957 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR
)
3958 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3961 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID
& aIID
, void** aResult
) {
3962 // Return ourselves for the things we implement (except
3963 // nsIInterfaceRequestor) and the XHR for the rest.
3964 if (!aIID
.Equals(NS_GET_IID(nsIInterfaceRequestor
))) {
3965 nsresult rv
= QueryInterface(aIID
, aResult
);
3966 if (NS_SUCCEEDED(rv
)) {
3971 return mXHR
->GetInterface(aIID
, aResult
);
3974 ArrayBufferBuilder::ArrayBufferBuilder()
3975 : mMutex("ArrayBufferBuilder"),
3982 ArrayBufferBuilder::~ArrayBufferBuilder() {
3984 JS_free(nullptr, mDataPtr
);
3988 JS::ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
3993 mCapacity
= mLength
= 0;
3996 bool ArrayBufferBuilder::SetCapacity(uint32_t aNewCap
) {
3997 MutexAutoLock
lock(mMutex
);
3998 return SetCapacityInternal(aNewCap
, lock
);
4001 bool ArrayBufferBuilder::SetCapacityInternal(
4002 uint32_t aNewCap
, const MutexAutoLock
& aProofOfLock
) {
4003 MOZ_ASSERT(!mMapPtr
);
4004 MOZ_ASSERT(!mNeutered
);
4006 // To ensure that realloc won't free mDataPtr, use a size of 1
4008 uint8_t* newdata
= (uint8_t*)js_realloc(mDataPtr
, aNewCap
? aNewCap
: 1);
4014 if (aNewCap
> mCapacity
) {
4015 memset(newdata
+ mCapacity
, 0, aNewCap
- mCapacity
);
4019 mCapacity
= aNewCap
;
4020 if (mLength
> aNewCap
) {
4027 bool ArrayBufferBuilder::Append(const uint8_t* aNewData
, uint32_t aDataLen
,
4028 uint32_t aMaxGrowth
) {
4029 MutexAutoLock
lock(mMutex
);
4030 MOZ_ASSERT(!mMapPtr
);
4031 MOZ_ASSERT(!mNeutered
);
4033 CheckedUint32 neededCapacity
= mLength
;
4034 neededCapacity
+= aDataLen
;
4035 if (!neededCapacity
.isValid()) {
4038 if (mLength
+ aDataLen
> mCapacity
) {
4039 CheckedUint32 newcap
= mCapacity
;
4040 // Double while under aMaxGrowth or if not specified.
4041 if (!aMaxGrowth
|| mCapacity
< aMaxGrowth
) {
4044 newcap
+= aMaxGrowth
;
4047 if (!newcap
.isValid()) {
4051 // But make sure there's always enough to satisfy our request.
4052 if (newcap
.value() < neededCapacity
.value()) {
4053 newcap
= neededCapacity
;
4056 if (!SetCapacityInternal(newcap
.value(), lock
)) {
4061 // Assert that the region isn't overlapping so we can memcpy.
4063 !AreOverlappingRegions(aNewData
, aDataLen
, mDataPtr
+ mLength
, aDataLen
));
4065 memcpy(mDataPtr
+ mLength
, aNewData
, aDataLen
);
4066 mLength
+= aDataLen
;
4071 uint32_t ArrayBufferBuilder::Length() {
4072 MutexAutoLock
lock(mMutex
);
4073 MOZ_ASSERT(!mNeutered
);
4077 uint32_t ArrayBufferBuilder::Capacity() {
4078 MutexAutoLock
lock(mMutex
);
4079 MOZ_ASSERT(!mNeutered
);
4083 JSObject
* ArrayBufferBuilder::TakeArrayBuffer(JSContext
* aCx
) {
4084 MutexAutoLock
lock(mMutex
);
4085 MOZ_DIAGNOSTIC_ASSERT(!mNeutered
);
4088 JSObject
* obj
= JS::NewMappedArrayBufferWithContents(aCx
, mLength
, mMapPtr
);
4090 JS::ReleaseMappedArrayBufferContents(mMapPtr
, mLength
);
4096 // The memory-mapped contents will be released when the ArrayBuffer becomes
4097 // detached or is GC'd.
4101 // we need to check for mLength == 0, because nothing may have been
4103 if (mCapacity
> mLength
|| mLength
== 0) {
4104 if (!SetCapacityInternal(mLength
, lock
)) {
4109 // |mDataPtr| will be deallocated in ArrayBufferBuilder's destructor when this
4110 // ArrayBuffer allocation failed.
4111 JSObject
* obj
= JS::NewArrayBufferWithContents(
4112 aCx
, mLength
, mDataPtr
,
4113 JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory
);
4119 mCapacity
= mLength
= 0;
4125 nsresult
ArrayBufferBuilder::MapToFileInPackage(const nsCString
& aFile
,
4126 nsIFile
* aJarFile
) {
4127 MutexAutoLock
lock(mMutex
);
4128 MOZ_ASSERT(NS_IsMainThread());
4129 MOZ_ASSERT(!mNeutered
);
4133 // Open Jar file to get related attributes of target file.
4134 RefPtr
<nsZipArchive
> zip
= nsZipArchive::OpenArchive(aJarFile
);
4136 return NS_ERROR_FAILURE
;
4138 nsZipItem
* zipItem
= zip
->GetItem(aFile
.get());
4140 return NS_ERROR_FILE_NOT_FOUND
;
4143 // If file was added to the package as stored(uncompressed), map to the
4144 // offset of file in zip package.
4145 if (!zipItem
->Compression()) {
4146 uint32_t offset
= zip
->GetDataOffset(zipItem
);
4147 uint32_t size
= zipItem
->RealSize();
4148 mozilla::AutoFDClose pr_fd
;
4149 rv
= aJarFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, getter_Transfers(pr_fd
));
4150 if (NS_FAILED(rv
)) {
4153 mMapPtr
= JS::CreateMappedArrayBufferContents(
4154 PR_FileDesc2NativeHandle(pr_fd
.get()), offset
, size
);
4160 return NS_ERROR_FAILURE
;
4164 bool ArrayBufferBuilder::AreOverlappingRegions(const uint8_t* aStart1
,
4166 const uint8_t* aStart2
,
4167 uint32_t aLength2
) {
4168 const uint8_t* end1
= aStart1
+ aLength1
;
4169 const uint8_t* end2
= aStart2
+ aLength2
;
4171 const uint8_t* max_start
= aStart1
> aStart2
? aStart1
: aStart2
;
4172 const uint8_t* min_end
= end1
< end2
? end1
: end2
;
4174 return max_start
< min_end
;
4177 RequestHeaders::RequestHeader
* RequestHeaders::Find(const nsACString
& aName
) {
4178 for (RequestHeaders::RequestHeader
& header
: mHeaders
) {
4179 if (header
.mName
.Equals(aName
, nsCaseInsensitiveCStringComparator
)) {
4186 bool RequestHeaders::IsEmpty() const { return mHeaders
.IsEmpty(); }
4188 bool RequestHeaders::Has(const char* aName
) {
4189 return Has(nsDependentCString(aName
));
4192 bool RequestHeaders::Has(const nsACString
& aName
) { return !!Find(aName
); }
4194 void RequestHeaders::Get(const char* aName
, nsACString
& aValue
) {
4195 Get(nsDependentCString(aName
), aValue
);
4198 void RequestHeaders::Get(const nsACString
& aName
, nsACString
& aValue
) {
4199 RequestHeader
* header
= Find(aName
);
4201 aValue
= header
->mValue
;
4203 aValue
.SetIsVoid(true);
4207 void RequestHeaders::Set(const char* aName
, const nsACString
& aValue
) {
4208 Set(nsDependentCString(aName
), aValue
);
4211 void RequestHeaders::Set(const nsACString
& aName
, const nsACString
& aValue
) {
4212 RequestHeader
* header
= Find(aName
);
4214 header
->mValue
.Assign(aValue
);
4216 RequestHeader newHeader
= {nsCString(aName
), nsCString(aValue
)};
4217 mHeaders
.AppendElement(newHeader
);
4221 void RequestHeaders::MergeOrSet(const char* aName
, const nsACString
& aValue
) {
4222 MergeOrSet(nsDependentCString(aName
), aValue
);
4225 void RequestHeaders::MergeOrSet(const nsACString
& aName
,
4226 const nsACString
& aValue
) {
4227 RequestHeader
* header
= Find(aName
);
4229 header
->mValue
.AppendLiteral(", ");
4230 header
->mValue
.Append(aValue
);
4232 RequestHeader newHeader
= {nsCString(aName
), nsCString(aValue
)};
4233 mHeaders
.AppendElement(newHeader
);
4237 void RequestHeaders::Clear() { mHeaders
.Clear(); }
4239 void RequestHeaders::ApplyToChannel(nsIHttpChannel
* aChannel
,
4240 bool aStripRequestBodyHeader
,
4241 bool aStripAuthHeader
) const {
4242 for (const RequestHeader
& header
: mHeaders
) {
4243 if (aStripRequestBodyHeader
&&
4244 (header
.mName
.LowerCaseEqualsASCII("content-type") ||
4245 header
.mName
.LowerCaseEqualsASCII("content-encoding") ||
4246 header
.mName
.LowerCaseEqualsASCII("content-language") ||
4247 header
.mName
.LowerCaseEqualsASCII("content-location"))) {
4251 if (aStripAuthHeader
&&
4252 header
.mName
.LowerCaseEqualsASCII("authorization")) {
4256 // Update referrerInfo to override referrer header in system privileged.
4257 if (header
.mName
.LowerCaseEqualsASCII("referer")) {
4258 DebugOnly
<nsresult
> rv
= aChannel
->SetNewReferrerInfo(
4259 header
.mValue
, nsIReferrerInfo::ReferrerPolicyIDL::UNSAFE_URL
, true);
4260 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4262 if (header
.mValue
.IsEmpty()) {
4263 DebugOnly
<nsresult
> rv
= aChannel
->SetEmptyRequestHeader(header
.mName
);
4264 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4266 DebugOnly
<nsresult
> rv
=
4267 aChannel
->SetRequestHeader(header
.mName
, header
.mValue
, false);
4268 MOZ_ASSERT(NS_SUCCEEDED(rv
));
4273 void RequestHeaders::GetCORSUnsafeHeaders(nsTArray
<nsCString
>& aArray
) const {
4274 for (const RequestHeader
& header
: mHeaders
) {
4275 if (!nsContentUtils::IsCORSSafelistedRequestHeader(header
.mName
,
4277 aArray
.AppendElement(header
.mName
);
4282 RequestHeaders::CharsetIterator::CharsetIterator(nsACString
& aSource
)
4286 mCutoff(aSource
.Length()),
4289 bool RequestHeaders::CharsetIterator::Equals(
4290 const nsACString
& aOther
, const nsCStringComparator
& aCmp
) const {
4292 return Substring(mSource
, mCurPos
, mCurLen
).Equals(aOther
, aCmp
);
4298 void RequestHeaders::CharsetIterator::Replace(const nsACString
& aReplacement
) {
4300 mSource
.Replace(mCurPos
, mCurLen
, aReplacement
);
4301 mCurLen
= aReplacement
.Length();
4305 bool RequestHeaders::CharsetIterator::Next() {
4307 nsAutoCString charset
;
4309 // Look for another charset declaration in the string, limiting the
4310 // search to only the characters before the parts we've already searched
4311 // (before mCutoff), so that we don't find the same charset twice.
4312 NS_ExtractCharsetFromContentType(Substring(mSource
, 0, mCutoff
), charset
,
4313 &mValid
, &start
, &end
);
4319 // Everything after the = sign is the part of the charset we want.
4320 mCurPos
= mSource
.FindChar('=', start
) + 1;
4321 mCurLen
= end
- mCurPos
;
4323 // Special case: the extracted charset is quoted with single quotes.
4324 // For the purpose of preserving what was set we want to handle them
4325 // as delimiters (although they aren't really).
4326 if (charset
.Length() >= 2 && charset
.First() == '\'' &&
4327 charset
.Last() == '\'') {
4337 } // namespace mozilla::dom