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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "XMLHttpRequestWorker.h"
9 #include "nsIDOMEventListener.h"
11 #include "GeckoProfiler.h"
12 #include "jsfriendapi.h"
13 #include "js/ArrayBuffer.h" // JS::Is{,Detached}ArrayBufferObject
14 #include "js/GCPolicyAPI.h"
16 #include "js/RootingAPI.h" // JS::{Handle,Heap,PersistentRooted}
17 #include "js/TracingAPI.h"
18 #include "js/Value.h" // JS::{Undefined,}Value
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/HoldDropJSObjects.h"
21 #include "mozilla/dom/Exceptions.h"
22 #include "mozilla/dom/Event.h"
23 #include "mozilla/dom/File.h"
24 #include "mozilla/dom/FormData.h"
25 #include "mozilla/dom/ProgressEvent.h"
26 #include "mozilla/dom/SerializedStackHolder.h"
27 #include "mozilla/dom/StreamBlobImpl.h"
28 #include "mozilla/dom/StructuredCloneHolder.h"
29 #include "mozilla/dom/URLSearchParams.h"
30 #include "mozilla/dom/WorkerScope.h"
31 #include "mozilla/dom/WorkerRef.h"
32 #include "mozilla/dom/WorkerRunnable.h"
33 #include "mozilla/dom/XMLHttpRequestBinding.h"
34 #include "mozilla/Telemetry.h"
35 #include "nsComponentManagerUtils.h"
36 #include "nsContentUtils.h"
37 #include "nsJSUtils.h"
38 #include "nsThreadUtils.h"
40 #include "XMLHttpRequestMainThread.h"
41 #include "XMLHttpRequestUpload.h"
43 #include "mozilla/UniquePtr.h"
45 extern mozilla::LazyLogModule gXMLHttpRequestLog
;
47 namespace mozilla::dom
{
49 using EventType
= XMLHttpRequest::EventType
;
50 using Events
= XMLHttpRequest::Events
;
53 * XMLHttpRequest in workers
55 * XHR in workers is implemented by proxying calls/events/etc between the
56 * worker thread and an XMLHttpRequest on the main thread. The glue
57 * object here is the Proxy, which lives on both threads. All other objects
58 * live on either the main thread (the XMLHttpRequest) or the worker thread
59 * (the worker and XHR private objects).
61 * The main thread XHR is always operated in async mode, even for sync XHR
62 * in workers. Calls made on the worker thread are proxied to the main thread
63 * synchronously (meaning the worker thread is blocked until the call
64 * returns). Each proxied call spins up a sync queue, which captures any
65 * synchronously dispatched events and ensures that they run synchronously
66 * on the worker as well. Asynchronously dispatched events are posted to the
67 * worker thread to run asynchronously. Some of the XHR state is mirrored on
68 * the worker thread to avoid needing a cross-thread call on every property
71 * The XHR private is stored in the private slot of the XHR JSObject on the
72 * worker thread. It is destroyed when that JSObject is GCd. The private
73 * roots its JSObject while network activity is in progress. It also
74 * adds itself as a feature to the worker to give itself a chance to clean up
75 * if the worker goes away during an XHR call. It is important that the
76 * rooting and feature registration (collectively called pinning) happens at
77 * the proper times. If we pin for too long we can cause memory leaks or even
78 * shutdown hangs. If we don't pin for long enough we introduce a GC hazard.
80 * The XHR is pinned from the time Send is called to roughly the time loadend
81 * is received. There are some complications involved with Abort and XHR
82 * reuse. We maintain a counter on the main thread of how many times Send was
83 * called on this XHR, and we decrement the counter every time we receive a
84 * loadend event. When the counter reaches zero we dispatch a runnable to the
85 * worker thread to unpin the XHR. We only decrement the counter if the
86 * dispatch was successful, because the worker may no longer be accepting
87 * regular runnables. In the event that we reach Proxy::Teardown and there
88 * the outstanding Send count is still non-zero, we dispatch a control
89 * runnable which is guaranteed to run.
91 * NB: Some of this could probably be simplified now that we have the
92 * inner/outer channel ids.
95 class Proxy final
: public nsIDOMEventListener
{
97 // Read on multiple threads.
98 WorkerPrivate
* mWorkerPrivate
;
99 const ClientInfo mClientInfo
;
100 const Maybe
<ServiceWorkerDescriptor
> mController
;
102 // Only ever dereferenced and/or checked on the worker thread. Cleared
103 // explicitly on the worker thread inside XMLHttpRequestWorker::ReleaseProxy.
104 WeakPtr
<XMLHttpRequestWorker
> mXMLHttpRequestPrivate
;
110 // Only touched on the main thread.
111 RefPtr
<XMLHttpRequestMainThread
> mXHR
;
112 RefPtr
<XMLHttpRequestUpload
> mXHRUpload
;
113 nsCOMPtr
<nsIEventTarget
> mSyncLoopTarget
;
114 nsCOMPtr
<nsIEventTarget
> mSyncEventResponseTarget
;
115 uint32_t mInnerEventStreamId
;
116 uint32_t mInnerChannelId
;
117 uint32_t mOutstandingSendCount
;
119 // Only touched on the worker thread.
120 uint32_t mOuterEventStreamId
;
121 uint32_t mOuterChannelId
;
123 uint64_t mLastLoaded
;
125 uint64_t mLastUploadLoaded
;
126 uint64_t mLastUploadTotal
;
127 nsresult mLastErrorDetailAtLoadend
;
129 bool mLastLengthComputable
;
130 bool mLastUploadLengthComputable
;
132 bool mSeenUploadLoadStart
;
133 bool mDispatchPrematureAbortEvent
;
134 bool mDispatchPrematureAbortEventToUpload
;
136 // Only touched on the main thread.
137 bool mUploadEventListenersAttached
;
138 bool mMainThreadSeenLoadStart
;
142 Proxy(XMLHttpRequestWorker
* aXHRPrivate
, const ClientInfo
& aClientInfo
,
143 const Maybe
<ServiceWorkerDescriptor
>& aController
, bool aMozAnon
,
145 : mWorkerPrivate(nullptr),
146 mClientInfo(aClientInfo
),
147 mController(aController
),
148 mXMLHttpRequestPrivate(aXHRPrivate
),
150 mMozSystem(aMozSystem
),
151 mInnerEventStreamId(0),
153 mOutstandingSendCount(0),
154 mOuterEventStreamId(0),
159 mLastUploadLoaded(0),
161 mLastErrorDetailAtLoadend(NS_OK
),
163 mLastLengthComputable(false),
164 mLastUploadLengthComputable(false),
165 mSeenLoadStart(false),
166 mSeenUploadLoadStart(false),
167 mDispatchPrematureAbortEvent(false),
168 mDispatchPrematureAbortEventToUpload(false),
169 mUploadEventListenersAttached(false),
170 mMainThreadSeenLoadStart(false),
173 NS_DECL_THREADSAFE_ISUPPORTS
174 NS_DECL_NSIDOMEVENTLISTENER
180 bool AddRemoveEventListeners(bool aUpload
, bool aAdd
);
183 AssertIsOnMainThread();
185 if (mUploadEventListenersAttached
) {
186 AddRemoveEventListeners(true, false);
190 already_AddRefed
<nsIEventTarget
> GetEventTarget() {
191 AssertIsOnMainThread();
193 nsCOMPtr
<nsIEventTarget
> target
=
194 mSyncEventResponseTarget
? mSyncEventResponseTarget
: mSyncLoopTarget
;
195 return target
.forget();
199 void DebugStoreWorkerRef(RefPtr
<StrongWorkerRef
>& aWorkerRef
) {
200 MOZ_ASSERT(!NS_IsMainThread());
201 mXHR
->mTSWorkerRef
= new ThreadSafeWorkerRef(aWorkerRef
);
204 void DebugForgetWorkerRef() {
205 MOZ_ASSERT(!NS_IsMainThread());
206 mXHR
->mTSWorkerRef
= nullptr;
213 MOZ_ASSERT(!mXHRUpload
);
214 MOZ_ASSERT(!mOutstandingSendCount
);
218 class WorkerThreadProxySyncRunnable
: public WorkerMainThreadRunnable
{
220 RefPtr
<Proxy
> mProxy
;
223 // mErrorCode is set on the main thread by MainThreadRun and it's used at the
224 // end of the Dispatch() to return the error code.
228 WorkerThreadProxySyncRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
)
229 : WorkerMainThreadRunnable(aWorkerPrivate
, "XHR"_ns
),
232 MOZ_ASSERT(aWorkerPrivate
);
234 aWorkerPrivate
->AssertIsOnWorkerThread();
237 void Dispatch(WorkerStatus aFailStatus
, ErrorResult
& aRv
) {
238 WorkerMainThreadRunnable::Dispatch(aFailStatus
, aRv
);
239 if (NS_WARN_IF(aRv
.Failed())) {
243 if (NS_FAILED(mErrorCode
)) {
244 aRv
.Throw(mErrorCode
);
249 virtual ~WorkerThreadProxySyncRunnable() = default;
251 virtual void RunOnMainThread(ErrorResult
& aRv
) = 0;
254 virtual bool MainThreadRun() override
;
257 class SendRunnable final
: public WorkerThreadProxySyncRunnable
{
258 RefPtr
<BlobImpl
> mBlobImpl
;
259 nsCOMPtr
<nsIEventTarget
> mSyncLoopTarget
;
260 bool mHasUploadListeners
;
263 SendRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
265 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
),
266 mBlobImpl(aBlobImpl
),
267 mHasUploadListeners(false) {}
269 void SetHaveUploadListeners(bool aHasUploadListeners
) {
270 mHasUploadListeners
= aHasUploadListeners
;
273 void SetSyncLoopTarget(nsIEventTarget
* aSyncLoopTarget
) {
274 mSyncLoopTarget
= aSyncLoopTarget
;
278 ~SendRunnable() = default;
280 virtual void RunOnMainThread(ErrorResult
& aRv
) override
;
285 class MainThreadProxyRunnable
: public MainThreadWorkerSyncRunnable
{
287 RefPtr
<Proxy
> mProxy
;
289 MainThreadProxyRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
290 const char* aName
= "MainThreadProxyRunnable")
291 : MainThreadWorkerSyncRunnable(aWorkerPrivate
, aProxy
->GetEventTarget(),
297 virtual ~MainThreadProxyRunnable() = default;
300 class AsyncTeardownRunnable final
: public Runnable
{
301 RefPtr
<Proxy
> mProxy
;
304 explicit AsyncTeardownRunnable(Proxy
* aProxy
)
305 : Runnable("dom::AsyncTeardownRunnable"), mProxy(aProxy
) {
310 ~AsyncTeardownRunnable() = default;
314 AssertIsOnMainThread();
323 class LoadStartDetectionRunnable final
: public Runnable
,
324 public nsIDOMEventListener
{
325 WorkerPrivate
* mWorkerPrivate
;
326 RefPtr
<Proxy
> mProxy
;
327 RefPtr
<XMLHttpRequest
> mXHR
;
329 bool mReceivedLoadStart
;
331 class ProxyCompleteRunnable final
: public MainThreadProxyRunnable
{
335 ProxyCompleteRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
337 : MainThreadProxyRunnable(aWorkerPrivate
, aProxy
,
338 "ProxyCompleteRunnable"),
339 mChannelId(aChannelId
) {}
342 ~ProxyCompleteRunnable() = default;
344 virtual bool WorkerRun(JSContext
* aCx
,
345 WorkerPrivate
* aWorkerPrivate
) override
{
346 if (mChannelId
!= mProxy
->mOuterChannelId
) {
347 // Threads raced, this event is now obsolete.
351 if (mSyncLoopTarget
) {
352 aWorkerPrivate
->StopSyncLoop(mSyncLoopTarget
, NS_OK
);
355 XMLHttpRequestWorker
* xhrw
= mProxy
->mXMLHttpRequestPrivate
.get();
356 if (xhrw
&& xhrw
->SendInProgress()) {
363 nsresult
Cancel() override
{ return Run(); }
367 explicit LoadStartDetectionRunnable(Proxy
* aProxy
)
368 : Runnable("dom::LoadStartDetectionRunnable"),
369 mWorkerPrivate(aProxy
->mWorkerPrivate
),
372 mChannelId(mProxy
->mInnerChannelId
),
373 mReceivedLoadStart(false) {
374 AssertIsOnMainThread();
377 NS_DECL_ISUPPORTS_INHERITED
379 NS_DECL_NSIDOMEVENTLISTENER
381 bool RegisterAndDispatch() {
382 AssertIsOnMainThread();
385 mXHR
->AddEventListener(Events::loadstart
, this, false, false))) {
386 NS_WARNING("Failed to add event listener!");
390 return NS_SUCCEEDED(mWorkerPrivate
->DispatchToMainThread(this));
394 ~LoadStartDetectionRunnable() { AssertIsOnMainThread(); }
397 class EventRunnable final
: public MainThreadProxyRunnable
{
398 const EventType
& mType
;
399 UniquePtr
<XMLHttpRequestWorker::ResponseData
> mResponseData
;
400 nsString mResponseURL
;
401 nsCString mStatusText
;
404 uint32_t mEventStreamId
;
406 uint16_t mReadyState
;
409 bool mLengthComputable
;
410 nsresult mStatusResult
;
411 nsresult mErrorDetail
;
412 // mScopeObj is used in PreDispatch only. We init it in our constructor, and
413 // reset() in PreDispatch, to ensure that it's not still linked into the
414 // runtime once we go off-thread.
415 JS::PersistentRooted
<JSObject
*> mScopeObj
;
418 EventRunnable(Proxy
* aProxy
, bool aUploadEvent
, const EventType
& aType
,
419 bool aLengthComputable
, uint64_t aLoaded
, uint64_t aTotal
,
420 JS::Handle
<JSObject
*> aScopeObj
)
421 : MainThreadProxyRunnable(aProxy
->mWorkerPrivate
, aProxy
,
424 mResponseData(new XMLHttpRequestWorker::ResponseData()),
427 mEventStreamId(aProxy
->mInnerEventStreamId
),
430 mUploadEvent(aUploadEvent
),
431 mProgressEvent(true),
432 mLengthComputable(aLengthComputable
),
433 mStatusResult(NS_OK
),
435 mScopeObj(RootingCx(), aScopeObj
) {}
437 EventRunnable(Proxy
* aProxy
, bool aUploadEvent
, const EventType
& aType
,
438 JS::Handle
<JSObject
*> aScopeObj
)
439 : MainThreadProxyRunnable(aProxy
->mWorkerPrivate
, aProxy
,
442 mResponseData(new XMLHttpRequestWorker::ResponseData()),
445 mEventStreamId(aProxy
->mInnerEventStreamId
),
448 mUploadEvent(aUploadEvent
),
449 mProgressEvent(false),
450 mLengthComputable(0),
451 mStatusResult(NS_OK
),
453 mScopeObj(RootingCx(), aScopeObj
) {}
456 ~EventRunnable() = default;
458 bool PreDispatch(WorkerPrivate
* /* unused */) final
;
459 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
;
462 class SyncTeardownRunnable final
: public WorkerThreadProxySyncRunnable
{
464 SyncTeardownRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
)
465 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
) {}
468 ~SyncTeardownRunnable() = default;
470 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
472 MOZ_ASSERT(!mProxy
->mSyncLoopTarget
);
476 class SetBackgroundRequestRunnable final
477 : public WorkerThreadProxySyncRunnable
{
481 SetBackgroundRequestRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
483 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
), mValue(aValue
) {}
486 ~SetBackgroundRequestRunnable() = default;
488 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
489 // XXXedgar, do we intend to ignore the errors?
490 mProxy
->mXHR
->SetMozBackgroundRequest(mValue
, aRv
);
494 class SetWithCredentialsRunnable final
: public WorkerThreadProxySyncRunnable
{
498 SetWithCredentialsRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
500 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
), mValue(aValue
) {}
503 ~SetWithCredentialsRunnable() = default;
505 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
506 mProxy
->mXHR
->SetWithCredentials(mValue
, aRv
);
510 class SetResponseTypeRunnable final
: public WorkerThreadProxySyncRunnable
{
511 XMLHttpRequestResponseType mResponseType
;
514 SetResponseTypeRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
515 XMLHttpRequestResponseType aResponseType
)
516 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
),
517 mResponseType(aResponseType
) {}
519 XMLHttpRequestResponseType
ResponseType() { return mResponseType
; }
522 ~SetResponseTypeRunnable() = default;
524 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
525 mProxy
->mXHR
->SetResponseTypeRaw(mResponseType
);
526 mResponseType
= mProxy
->mXHR
->ResponseType();
530 class SetTimeoutRunnable final
: public WorkerThreadProxySyncRunnable
{
534 SetTimeoutRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
536 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
),
537 mTimeout(aTimeout
) {}
540 ~SetTimeoutRunnable() = default;
542 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
543 mProxy
->mXHR
->SetTimeout(mTimeout
, aRv
);
547 class AbortRunnable final
: public WorkerThreadProxySyncRunnable
{
549 AbortRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
)
550 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
) {}
553 ~AbortRunnable() = default;
555 virtual void RunOnMainThread(ErrorResult
& aRv
) override
;
558 class GetAllResponseHeadersRunnable final
559 : public WorkerThreadProxySyncRunnable
{
560 nsCString
& mResponseHeaders
;
563 GetAllResponseHeadersRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
564 nsCString
& aResponseHeaders
)
565 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
),
566 mResponseHeaders(aResponseHeaders
) {}
569 ~GetAllResponseHeadersRunnable() = default;
571 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
572 mProxy
->mXHR
->GetAllResponseHeaders(mResponseHeaders
, aRv
);
576 class GetResponseHeaderRunnable final
: public WorkerThreadProxySyncRunnable
{
577 const nsCString mHeader
;
581 GetResponseHeaderRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
582 const nsACString
& aHeader
, nsCString
& aValue
)
583 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
),
588 ~GetResponseHeaderRunnable() = default;
590 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
591 mProxy
->mXHR
->GetResponseHeader(mHeader
, mValue
, aRv
);
595 class OpenRunnable final
: public WorkerThreadProxySyncRunnable
{
598 Optional
<nsAString
> mUser
;
600 Optional
<nsAString
> mPassword
;
601 nsString mPasswordStr
;
602 bool mBackgroundRequest
;
603 bool mWithCredentials
;
605 XMLHttpRequestResponseType mResponseType
;
606 const nsString mMimeTypeOverride
;
608 // Remember the worker thread's stack when the XHR was opened, so that it can
609 // be passed on to the net monitor.
610 UniquePtr
<SerializedStackHolder
> mOriginStack
;
612 // Remember the worker thread's stack when the XHR was opened for profiling
614 UniquePtr
<ProfileChunkedBuffer
> mSource
;
617 OpenRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
618 const nsACString
& aMethod
, const nsAString
& aURL
,
619 const Optional
<nsAString
>& aUser
,
620 const Optional
<nsAString
>& aPassword
, bool aBackgroundRequest
,
621 bool aWithCredentials
, uint32_t aTimeout
,
622 XMLHttpRequestResponseType aResponseType
,
623 const nsString
& aMimeTypeOverride
,
624 UniquePtr
<SerializedStackHolder
> aOriginStack
,
625 UniquePtr
<ProfileChunkedBuffer
> aSource
= nullptr)
626 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
),
629 mBackgroundRequest(aBackgroundRequest
),
630 mWithCredentials(aWithCredentials
),
632 mResponseType(aResponseType
),
633 mMimeTypeOverride(aMimeTypeOverride
),
634 mOriginStack(std::move(aOriginStack
)),
635 mSource(std::move(aSource
)) {
636 if (aUser
.WasPassed()) {
637 mUserStr
= aUser
.Value();
640 if (aPassword
.WasPassed()) {
641 mPasswordStr
= aPassword
.Value();
642 mPassword
= &mPasswordStr
;
647 ~OpenRunnable() = default;
649 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
650 WorkerPrivate
* oldWorker
= mProxy
->mWorkerPrivate
;
651 mProxy
->mWorkerPrivate
= mWorkerPrivate
;
653 MainThreadRunInternal(aRv
);
655 mProxy
->mWorkerPrivate
= oldWorker
;
658 void MainThreadRunInternal(ErrorResult
& aRv
);
661 class SetRequestHeaderRunnable final
: public WorkerThreadProxySyncRunnable
{
666 SetRequestHeaderRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
667 const nsACString
& aHeader
, const nsACString
& aValue
)
668 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
),
673 ~SetRequestHeaderRunnable() = default;
675 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
676 mProxy
->mXHR
->SetRequestHeader(mHeader
, mValue
, aRv
);
680 class OverrideMimeTypeRunnable final
: public WorkerThreadProxySyncRunnable
{
684 OverrideMimeTypeRunnable(WorkerPrivate
* aWorkerPrivate
, Proxy
* aProxy
,
685 const nsAString
& aMimeType
)
686 : WorkerThreadProxySyncRunnable(aWorkerPrivate
, aProxy
),
687 mMimeType(aMimeType
) {}
690 ~OverrideMimeTypeRunnable() = default;
692 virtual void RunOnMainThread(ErrorResult
& aRv
) override
{
693 mProxy
->mXHR
->OverrideMimeType(mMimeType
, aRv
);
698 XMLHttpRequestWorker
* mXMLHttpRequestPrivate
;
701 explicit AutoUnpinXHR(XMLHttpRequestWorker
* aXMLHttpRequestPrivate
)
702 : mXMLHttpRequestPrivate(aXMLHttpRequestPrivate
) {
703 MOZ_ASSERT(aXMLHttpRequestPrivate
);
707 if (mXMLHttpRequestPrivate
) {
708 mXMLHttpRequestPrivate
->Unpin();
712 void Clear() { mXMLHttpRequestPrivate
= nullptr; }
718 AssertIsOnMainThread();
719 MOZ_ASSERT(mWorkerPrivate
);
725 nsPIDOMWindowInner
* ownerWindow
= mWorkerPrivate
->GetWindow();
726 if (ownerWindow
&& !ownerWindow
->IsCurrentInnerWindow()) {
727 NS_WARNING("Window has navigated, cannot create XHR here.");
731 mXHR
= new XMLHttpRequestMainThread(ownerWindow
? ownerWindow
->AsGlobal()
733 mXHR
->Construct(mWorkerPrivate
->GetPrincipal(),
734 mWorkerPrivate
->CookieJarSettings(), true,
735 mWorkerPrivate
->GetBaseURI(), mWorkerPrivate
->GetLoadGroup(),
736 mWorkerPrivate
->GetPerformanceStorage(),
737 mWorkerPrivate
->CSPEventListener());
739 mXHR
->SetParameters(mMozAnon
, mMozSystem
);
740 mXHR
->SetClientInfoAndController(mClientInfo
, mController
);
743 mXHRUpload
= mXHR
->GetUpload(rv
);
744 if (NS_WARN_IF(rv
.Failed())) {
749 if (!AddRemoveEventListeners(false, true)) {
751 mXHRUpload
= nullptr;
758 void Proxy::Teardown() {
759 AssertIsOnMainThread();
764 // NB: We are intentionally dropping events coming from xhr.abort on the
766 AddRemoveEventListeners(false, false);
770 if (NS_WARN_IF(rv
.Failed())) {
771 rv
.SuppressException();
774 if (mOutstandingSendCount
) {
775 if (mSyncLoopTarget
) {
776 // We have an unclosed sync loop. Fix that now.
777 RefPtr
<MainThreadStopSyncLoopRunnable
> runnable
=
778 new MainThreadStopSyncLoopRunnable(
779 mWorkerPrivate
, std::move(mSyncLoopTarget
), NS_ERROR_FAILURE
);
780 MOZ_ALWAYS_TRUE(runnable
->Dispatch());
783 mOutstandingSendCount
= 0;
786 mWorkerPrivate
= nullptr;
787 mXHRUpload
= nullptr;
791 MOZ_ASSERT(!mWorkerPrivate
);
792 MOZ_ASSERT(!mSyncLoopTarget
);
793 // If there are rare edge cases left that violate our invariants
794 // just ensure that they won't harm us too much.
795 mWorkerPrivate
= nullptr;
796 mSyncLoopTarget
= nullptr;
799 bool Proxy::AddRemoveEventListeners(bool aUpload
, bool aAdd
) {
800 AssertIsOnMainThread();
802 NS_ASSERTION(!aUpload
|| (mUploadEventListenersAttached
&& !aAdd
) ||
803 (!mUploadEventListenersAttached
&& aAdd
),
804 "Messed up logic for upload listeners!");
806 RefPtr
<DOMEventTargetHelper
> targetHelper
=
807 aUpload
? static_cast<XMLHttpRequestUpload
*>(mXHRUpload
.get())
808 : static_cast<XMLHttpRequestEventTarget
*>(mXHR
.get());
809 MOZ_ASSERT(targetHelper
, "This should never fail!");
811 for (const EventType
* type
: Events::All
) {
812 if (aUpload
&& *type
== Events::readystatechange
) {
816 if (NS_FAILED(targetHelper
->AddEventListener(*type
, this, false))) {
820 targetHelper
->RemoveEventListener(*type
, this, false);
825 mUploadEventListenersAttached
= aAdd
;
831 NS_IMPL_ISUPPORTS(Proxy
, nsIDOMEventListener
)
834 Proxy::HandleEvent(Event
* aEvent
) {
835 AssertIsOnMainThread();
837 // EventRunnable::WorkerRun will bail out if mXMLHttpRequestWorker is null,
838 // so we do not need to prevent the dispatch from the main thread such that
839 // we do not need to touch it off-worker-thread.
840 if (!mWorkerPrivate
) {
841 NS_ERROR("Shouldn't get here!");
846 aEvent
->GetType(_type
);
847 const EventType
* typePtr
= Events::Find(_type
);
848 MOZ_DIAGNOSTIC_ASSERT(typePtr
, "Shouldn't get non-XMLHttpRequest events");
849 const EventType
& type
= *typePtr
;
851 bool isUploadTarget
= mXHR
!= aEvent
->GetTarget();
852 ProgressEvent
* progressEvent
= aEvent
->AsProgressEvent();
854 if (mInOpen
&& type
== Events::readystatechange
) {
855 if (mXHR
->ReadyState() == 1) {
856 mInnerEventStreamId
++;
862 JSObject
* junkScope
= xpc::UnprivilegedJunkScope(fallible
);
863 if (!junkScope
|| !jsapi
.Init(junkScope
)) {
864 return NS_ERROR_FAILURE
;
866 JSContext
* cx
= jsapi
.cx();
868 JS::Rooted
<JS::Value
> value(cx
);
869 if (!GetOrCreateDOMReflectorNoWrap(cx
, mXHR
, &value
)) {
870 return NS_ERROR_FAILURE
;
873 JS::Rooted
<JSObject
*> scope(cx
, &value
.toObject());
875 RefPtr
<EventRunnable
> runnable
;
877 if (!mIsSyncXHR
|| type
!= Events::progress
) {
878 runnable
= new EventRunnable(
879 this, isUploadTarget
, type
, progressEvent
->LengthComputable(),
880 progressEvent
->Loaded(), progressEvent
->Total(), scope
);
883 runnable
= new EventRunnable(this, isUploadTarget
, type
, scope
);
887 runnable
->Dispatch();
891 if (!isUploadTarget
) {
892 if (type
== Events::loadstart
) {
893 mMainThreadSeenLoadStart
= true;
894 } else if (mMainThreadSeenLoadStart
&& type
== Events::loadend
) {
895 mMainThreadSeenLoadStart
= false;
897 RefPtr
<LoadStartDetectionRunnable
> runnable
=
898 new LoadStartDetectionRunnable(this);
899 if (!runnable
->RegisterAndDispatch()) {
900 NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
908 NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable
, Runnable
,
912 LoadStartDetectionRunnable::Run() {
913 AssertIsOnMainThread();
915 mXHR
->RemoveEventListener(Events::loadstart
, this, false);
917 if (!mReceivedLoadStart
) {
918 if (mProxy
->mOutstandingSendCount
> 1) {
919 mProxy
->mOutstandingSendCount
--;
920 } else if (mProxy
->mOutstandingSendCount
== 1) {
923 RefPtr
<ProxyCompleteRunnable
> runnable
=
924 new ProxyCompleteRunnable(mWorkerPrivate
, mProxy
, mChannelId
);
925 if (runnable
->Dispatch()) {
926 mProxy
->mWorkerPrivate
= nullptr;
927 mProxy
->mSyncLoopTarget
= nullptr;
928 mProxy
->mOutstandingSendCount
--;
939 LoadStartDetectionRunnable::HandleEvent(Event
* aEvent
) {
940 AssertIsOnMainThread();
945 aEvent
->GetType(type
);
946 MOZ_ASSERT(type
== Events::loadstart
);
950 mReceivedLoadStart
= true;
954 bool EventRunnable::PreDispatch(WorkerPrivate
* /* unused */) {
955 AssertIsOnMainThread();
958 DebugOnly
<bool> ok
= jsapi
.Init(xpc::NativeGlobal(mScopeObj
));
960 JSContext
* cx
= jsapi
.cx();
961 // Now keep the mScopeObj alive for the duration
962 JS::Rooted
<JSObject
*> scopeObj(cx
, mScopeObj
);
963 // And reset mScopeObj now, before we have a chance to run its destructor on
964 // some background thread.
967 RefPtr
<XMLHttpRequestMainThread
>& xhr
= mProxy
->mXHR
;
972 XMLHttpRequestResponseType type
= xhr
->ResponseType();
974 // We want to take the result data only if this is available.
975 if (mType
== Events::readystatechange
) {
977 case XMLHttpRequestResponseType::_empty
:
978 case XMLHttpRequestResponseType::Text
: {
979 xhr
->GetResponseText(mResponseData
->mResponseText
, rv
);
980 mResponseData
->mResponseResult
= rv
.StealNSResult();
984 case XMLHttpRequestResponseType::Blob
: {
985 mResponseData
->mResponseBlobImpl
= xhr
->GetResponseBlobImpl();
989 case XMLHttpRequestResponseType::Arraybuffer
: {
990 mResponseData
->mResponseArrayBufferBuilder
=
991 xhr
->GetResponseArrayBufferBuilder();
995 case XMLHttpRequestResponseType::Json
: {
996 mResponseData
->mResponseResult
=
997 xhr
->GetResponseTextForJSON(mResponseData
->mResponseJSON
);
1002 MOZ_ASSERT_UNREACHABLE("Invalid response type");
1007 mStatus
= xhr
->GetStatus(rv
);
1008 mStatusResult
= rv
.StealNSResult();
1010 mErrorDetail
= xhr
->ErrorDetail();
1012 xhr
->GetStatusText(mStatusText
, rv
);
1013 MOZ_ASSERT(!rv
.Failed());
1015 mReadyState
= xhr
->ReadyState();
1017 xhr
->GetResponseURL(mResponseURL
);
1022 bool EventRunnable::WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
1023 if (mEventStreamId
!= mProxy
->mOuterEventStreamId
) {
1024 // Threads raced, this event is now obsolete.
1028 if (!mProxy
->mXMLHttpRequestPrivate
) {
1029 // Object was finalized, bail.
1033 if (mType
== Events::loadend
) {
1034 mProxy
->mLastErrorDetailAtLoadend
= mErrorDetail
;
1037 if (mType
== Events::loadstart
) {
1039 mProxy
->mSeenUploadLoadStart
= true;
1041 mProxy
->mSeenLoadStart
= true;
1043 } else if (mType
== Events::loadend
) {
1045 mProxy
->mSeenUploadLoadStart
= false;
1046 if (mProxy
->mDispatchPrematureAbortEventToUpload
) {
1047 // We've already dispatched premature abort events.
1051 mProxy
->mSeenLoadStart
= false;
1052 if (mProxy
->mDispatchPrematureAbortEvent
) {
1053 // We've already dispatched premature abort events.
1057 } else if (mType
== Events::abort
) {
1058 if ((mUploadEvent
&& mProxy
->mDispatchPrematureAbortEventToUpload
) ||
1059 (!mUploadEvent
&& mProxy
->mDispatchPrematureAbortEvent
)) {
1060 // We've already dispatched premature abort events.
1065 if (mProgressEvent
) {
1066 // Cache these for premature abort events.
1068 mProxy
->mLastUploadLengthComputable
= mLengthComputable
;
1069 mProxy
->mLastUploadLoaded
= mLoaded
;
1070 mProxy
->mLastUploadTotal
= mTotal
;
1072 mProxy
->mLastLengthComputable
= mLengthComputable
;
1073 mProxy
->mLastLoaded
= mLoaded
;
1074 mProxy
->mLastTotal
= mTotal
;
1078 UniquePtr
<XMLHttpRequestWorker::StateData
> state(
1079 new XMLHttpRequestWorker::StateData());
1081 state
->mStatusResult
= mStatusResult
;
1082 state
->mStatus
= mStatus
;
1084 state
->mStatusText
= mStatusText
;
1086 state
->mReadyState
= mReadyState
;
1088 state
->mResponseURL
= mResponseURL
;
1090 XMLHttpRequestWorker
* xhr
= mProxy
->mXMLHttpRequestPrivate
;
1091 xhr
->UpdateState(std::move(state
), mType
== Events::readystatechange
1092 ? std::move(mResponseData
)
1095 if (mUploadEvent
&& !xhr
->GetUploadObjectNoCreate()) {
1099 XMLHttpRequestEventTarget
* target
;
1101 target
= xhr
->GetUploadObjectNoCreate();
1108 RefPtr
<Event
> event
;
1109 if (mProgressEvent
) {
1110 ProgressEventInit init
;
1111 init
.mBubbles
= false;
1112 init
.mCancelable
= false;
1113 init
.mLengthComputable
= mLengthComputable
;
1114 init
.mLoaded
= mLoaded
;
1115 init
.mTotal
= mTotal
;
1117 event
= ProgressEvent::Constructor(target
, mType
, init
);
1119 event
= NS_NewDOMEvent(target
, nullptr, nullptr);
1122 event
->InitEvent(mType
, false, false);
1130 event
->SetTrusted(true);
1132 if (MOZ_LOG_TEST(gXMLHttpRequestLog
, LogLevel::Debug
)) {
1134 event
->GetType(type
);
1135 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1136 ("%p firing %s event (%u,%u,%" PRIu64
",%" PRIu64
")",
1137 mProxy
->mXHR
.get(), NS_ConvertUTF16toUTF8(type
).get(),
1138 mUploadEvent
, mLengthComputable
, mLoaded
, mTotal
));
1141 target
->DispatchEvent(*event
);
1146 bool WorkerThreadProxySyncRunnable::MainThreadRun() {
1147 AssertIsOnMainThread();
1149 nsCOMPtr
<nsIEventTarget
> tempTarget
= mSyncLoopTarget
;
1151 mProxy
->mSyncEventResponseTarget
.swap(tempTarget
);
1154 RunOnMainThread(rv
);
1155 mErrorCode
= rv
.StealNSResult();
1157 mProxy
->mSyncEventResponseTarget
.swap(tempTarget
);
1162 void AbortRunnable::RunOnMainThread(ErrorResult
& aRv
) {
1163 mProxy
->mInnerEventStreamId
++;
1165 WorkerPrivate
* oldWorker
= mProxy
->mWorkerPrivate
;
1166 mProxy
->mWorkerPrivate
= mWorkerPrivate
;
1168 mProxy
->mXHR
->Abort(aRv
);
1170 mProxy
->mWorkerPrivate
= oldWorker
;
1175 void OpenRunnable::MainThreadRunInternal(ErrorResult
& aRv
) {
1176 if (!mProxy
->Init()) {
1177 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1181 if (mBackgroundRequest
) {
1182 mProxy
->mXHR
->SetMozBackgroundRequestExternal(mBackgroundRequest
, aRv
);
1189 mProxy
->mXHR
->SetOriginStack(std::move(mOriginStack
));
1192 if (mWithCredentials
) {
1193 mProxy
->mXHR
->SetWithCredentials(mWithCredentials
, aRv
);
1194 if (NS_WARN_IF(aRv
.Failed())) {
1200 mProxy
->mXHR
->SetTimeout(mTimeout
, aRv
);
1201 if (NS_WARN_IF(aRv
.Failed())) {
1206 if (!mMimeTypeOverride
.IsVoid()) {
1207 mProxy
->mXHR
->OverrideMimeType(mMimeTypeOverride
, aRv
);
1208 if (NS_WARN_IF(aRv
.Failed())) {
1213 MOZ_ASSERT(!mProxy
->mInOpen
);
1214 mProxy
->mInOpen
= true;
1217 mMethod
, mURL
, true, mUser
.WasPassed() ? mUser
.Value() : VoidString(),
1218 mPassword
.WasPassed() ? mPassword
.Value() : VoidString(), aRv
);
1220 MOZ_ASSERT(mProxy
->mInOpen
);
1221 mProxy
->mInOpen
= false;
1223 if (NS_WARN_IF(aRv
.Failed())) {
1228 mProxy
->mXHR
->SetSource(std::move(mSource
));
1231 mProxy
->mXHR
->SetResponseType(mResponseType
, aRv
);
1234 void SendRunnable::RunOnMainThread(ErrorResult
& aRv
) {
1235 // Before we change any state let's check if we can send.
1236 if (!mProxy
->mXHR
->CanSend(aRv
)) {
1241 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
>
1247 JS::Rooted
<JSObject
*> globalObject(RootingCx(),
1248 xpc::UnprivilegedJunkScope(fallible
));
1249 if (NS_WARN_IF(!globalObject
)) {
1250 aRv
.Throw(NS_ERROR_FAILURE
);
1254 nsCOMPtr
<nsIGlobalObject
> parent
= xpc::NativeGlobal(globalObject
);
1255 if (NS_WARN_IF(!parent
)) {
1256 aRv
.Throw(NS_ERROR_FAILURE
);
1260 RefPtr
<Blob
> blob
= Blob::Create(parent
, mBlobImpl
);
1263 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
&
1264 ref
= payload
.SetValue();
1265 ref
.SetAsBlob() = blob
;
1268 // Send() has been already called, reset the proxy.
1269 if (mProxy
->mWorkerPrivate
) {
1273 mProxy
->mWorkerPrivate
= mWorkerPrivate
;
1275 MOZ_ASSERT(!mProxy
->mSyncLoopTarget
);
1276 mProxy
->mSyncLoopTarget
.swap(mSyncLoopTarget
);
1278 if (mHasUploadListeners
) {
1279 // Send() can be called more than once before failure,
1280 // so don't attach the upload listeners more than once.
1281 if (!mProxy
->mUploadEventListenersAttached
&&
1282 !mProxy
->AddRemoveEventListeners(true, true)) {
1283 MOZ_ASSERT(false, "This should never fail!");
1287 mProxy
->mInnerChannelId
++;
1289 mProxy
->mXHR
->Send(payload
, aRv
);
1291 if (!aRv
.Failed()) {
1292 mProxy
->mOutstandingSendCount
++;
1294 if (!mHasUploadListeners
) {
1295 // Send() can be called more than once before failure,
1296 // so don't attach the upload listeners more than once.
1297 if (!mProxy
->mUploadEventListenersAttached
&&
1298 !mProxy
->AddRemoveEventListeners(true, true)) {
1299 MOZ_ASSERT(false, "This should never fail!");
1303 // In case of failure we just break the sync loop
1304 mProxy
->mSyncLoopTarget
= nullptr;
1305 mSyncLoopTarget
= nullptr;
1309 XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate
* aWorkerPrivate
,
1310 nsIGlobalObject
* aGlobalObject
)
1311 : XMLHttpRequest(aGlobalObject
),
1312 mWorkerPrivate(aWorkerPrivate
),
1313 mResponseType(XMLHttpRequestResponseType::_empty
),
1314 mStateData(new StateData()),
1315 mResponseData(new ResponseData()),
1316 mResponseArrayBufferValue(nullptr),
1317 mResponseJSONValue(JS::UndefinedValue()),
1319 mBackgroundRequest(false),
1320 mWithCredentials(false),
1322 mFlagSendActive(false),
1325 mMimeTypeOverride(VoidString()) {
1326 mWorkerPrivate
->AssertIsOnWorkerThread();
1328 mozilla::HoldJSObjects(this);
1331 XMLHttpRequestWorker::~XMLHttpRequestWorker() {
1332 mWorkerPrivate
->AssertIsOnWorkerThread();
1334 ReleaseProxy(XHRIsGoingAway
);
1336 MOZ_ASSERT(!mWorkerRef
);
1338 mozilla::DropJSObjects(this);
1341 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestWorker
, XMLHttpRequestEventTarget
)
1342 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestWorker
, XMLHttpRequestEventTarget
)
1344 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestWorker
)
1345 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget
)
1347 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestWorker
)
1349 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestWorker
,
1350 XMLHttpRequestEventTarget
)
1351 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload
)
1352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob
)
1353 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1355 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestWorker
,
1356 XMLHttpRequestEventTarget
)
1357 tmp
->ReleaseProxy(XHRIsGoingAway
);
1358 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload
)
1359 tmp
->mResponseData
= nullptr;
1360 tmp
->mResponseBlob
= nullptr;
1361 tmp
->mResponseArrayBufferValue
= nullptr;
1362 tmp
->mResponseJSONValue
.setUndefined();
1363 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1365 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestWorker
,
1366 XMLHttpRequestEventTarget
)
1367 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseArrayBufferValue
)
1368 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseJSONValue
)
1369 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1372 already_AddRefed
<XMLHttpRequest
> XMLHttpRequestWorker::Construct(
1373 const GlobalObject
& aGlobal
, const MozXMLHttpRequestParameters
& aParams
,
1375 JSContext
* cx
= aGlobal
.Context();
1376 WorkerPrivate
* workerPrivate
= GetWorkerPrivateFromContext(cx
);
1377 MOZ_ASSERT(workerPrivate
);
1379 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
1380 if (NS_WARN_IF(!global
)) {
1381 aRv
.Throw(NS_ERROR_FAILURE
);
1385 RefPtr
<XMLHttpRequestWorker
> xhr
=
1386 new XMLHttpRequestWorker(workerPrivate
, global
);
1388 if (workerPrivate
->XHRParamsAllowed()) {
1389 if (aParams
.mMozSystem
)
1390 xhr
->mMozAnon
= true;
1392 xhr
->mMozAnon
= aParams
.mMozAnon
;
1393 xhr
->mMozSystem
= aParams
.mMozSystem
;
1396 return xhr
.forget();
1399 void XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType
) {
1400 // Can't assert that we're on the worker thread here because mWorkerPrivate
1404 if (aType
== XHRIsGoingAway
) {
1405 // Coming here means the XHR was GC'd, so we can't be pinned.
1406 MOZ_ASSERT(!mProxy
->mXMLHttpRequestPrivate
||
1407 !mProxy
->mXMLHttpRequestPrivate
->mPinnedSelfRef
);
1409 // We need to clear our weak pointer on the worker thread, let's do it now
1410 // before doing it implicitly in the Proxy dtor on the wrong thread.
1411 mProxy
->mXMLHttpRequestPrivate
= nullptr;
1413 // We're in a GC finalizer, so we can't do a sync call here (and we don't
1415 RefPtr
<AsyncTeardownRunnable
> runnable
=
1416 new AsyncTeardownRunnable(mProxy
);
1419 if (NS_FAILED(mWorkerPrivate
->DispatchToMainThread(runnable
.forget()))) {
1420 NS_ERROR("Failed to dispatch teardown runnable!");
1423 // This isn't necessary if the worker is going away or the XHR is going
1425 if (aType
== Default
) {
1426 // Don't let any more events run.
1427 mProxy
->mOuterEventStreamId
++;
1430 // Ensure we are unpinned before we clear the weak reference.
1431 RefPtr
<XMLHttpRequestWorker
> self
= this;
1432 if (mPinnedSelfRef
) {
1435 mProxy
->mXMLHttpRequestPrivate
= nullptr;
1437 // We need to make a sync call here.
1438 RefPtr
<SyncTeardownRunnable
> runnable
=
1439 new SyncTeardownRunnable(mWorkerPrivate
, mProxy
);
1442 IgnoredErrorResult forAssertionsOnly
;
1443 // This runnable _must_ be executed.
1444 runnable
->Dispatch(Dead
, forAssertionsOnly
);
1445 MOZ_DIAGNOSTIC_ASSERT(!forAssertionsOnly
.Failed());
1450 void XMLHttpRequestWorker::MaybePin(ErrorResult
& aRv
) {
1451 mWorkerPrivate
->AssertIsOnWorkerThread();
1457 RefPtr
<XMLHttpRequestWorker
> self
= this;
1459 StrongWorkerRef::Create(mWorkerPrivate
, "XMLHttpRequestWorker", [self
]() {
1460 if (!self
->mCanceled
) {
1461 self
->mCanceled
= true;
1462 self
->ReleaseProxy(WorkerIsGoingAway
);
1465 if (NS_WARN_IF(!mWorkerRef
)) {
1466 aRv
.Throw(NS_ERROR_FAILURE
);
1470 mPinnedSelfRef
= this;
1473 mProxy
->DebugStoreWorkerRef(mWorkerRef
);
1477 void XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult
& aRv
) {
1478 mWorkerPrivate
->AssertIsOnWorkerThread();
1481 // Only send readystatechange event when state changed.
1482 bool isStateChanged
= false;
1483 if ((mStateData
->mReadyState
== 1 && mStateData
->mFlagSend
) ||
1484 mStateData
->mReadyState
== 2 || mStateData
->mReadyState
== 3) {
1485 isStateChanged
= true;
1486 mStateData
->mReadyState
= 4;
1489 if (mProxy
->mSeenUploadLoadStart
) {
1490 MOZ_ASSERT(mUpload
);
1492 FireEvent(mUpload
, Events::abort
, true, aRv
);
1497 FireEvent(mUpload
, Events::loadend
, true, aRv
);
1502 // Similarly to null check in ::Open, mProxy may have been cleared here.
1504 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1508 mProxy
->mSeenUploadLoadStart
= false;
1509 mProxy
->mDispatchPrematureAbortEventToUpload
= true;
1512 if (mProxy
->mSeenLoadStart
) {
1513 if (isStateChanged
) {
1514 FireEvent(this, Events::readystatechange
, false, aRv
);
1520 FireEvent(this, Events::abort
, false, aRv
);
1525 FireEvent(this, Events::loadend
, false, aRv
);
1530 // Similarly to null check in ::Open, mProxy may have been cleared here.
1532 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1536 mProxy
->mSeenLoadStart
= false;
1537 mProxy
->mDispatchPrematureAbortEvent
= true;
1541 void XMLHttpRequestWorker::FireEvent(EventTarget
* aTarget
,
1542 const EventType
& aEventType
,
1543 bool aUploadTarget
, ErrorResult
& aRv
) {
1544 mWorkerPrivate
->AssertIsOnWorkerThread();
1545 MOZ_ASSERT(aTarget
);
1548 aRv
.Throw(NS_ERROR_FAILURE
);
1552 RefPtr
<Event
> event
;
1553 if (aEventType
== Events::readystatechange
) {
1554 event
= NS_NewDOMEvent(aTarget
, nullptr, nullptr);
1555 event
->InitEvent(aEventType
, false, false);
1557 if (mProxy
->mIsSyncXHR
&& aEventType
== Events::progress
) {
1561 ProgressEventInit init
;
1562 init
.mBubbles
= false;
1563 init
.mCancelable
= false;
1564 if (aUploadTarget
) {
1565 init
.mLengthComputable
= mProxy
->mLastUploadLengthComputable
;
1566 init
.mLoaded
= mProxy
->mLastUploadLoaded
;
1567 init
.mTotal
= mProxy
->mLastUploadTotal
;
1569 init
.mLengthComputable
= mProxy
->mLastLengthComputable
;
1570 init
.mLoaded
= mProxy
->mLastLoaded
;
1571 init
.mTotal
= mProxy
->mLastTotal
;
1573 event
= ProgressEvent::Constructor(aTarget
, aEventType
, init
);
1577 aRv
.Throw(NS_ERROR_FAILURE
);
1581 event
->SetTrusted(true);
1583 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1584 ("%p firing %s pre-abort event (%u,%u,%" PRIu64
",%" PRIu64
, this,
1585 aEventType
.cStr
, aUploadTarget
,
1586 aUploadTarget
? mProxy
->mLastUploadLengthComputable
1587 : mProxy
->mLastLengthComputable
,
1588 aUploadTarget
? mProxy
->mLastUploadLoaded
: mProxy
->mLastLoaded
,
1589 aUploadTarget
? mProxy
->mLastUploadTotal
: mProxy
->mLastTotal
));
1590 aTarget
->DispatchEvent(*event
);
1593 void XMLHttpRequestWorker::Unpin() {
1594 mWorkerPrivate
->AssertIsOnWorkerThread();
1596 MOZ_ASSERT(mWorkerRef
, "Mismatched calls to Unpin!");
1600 // The proxy will be gone if WorkerIsGoingAway
1601 mProxy
->DebugForgetWorkerRef();
1605 mWorkerRef
= nullptr;
1607 mPinnedSelfRef
= nullptr;
1610 void XMLHttpRequestWorker::SendInternal(const BodyExtractorBase
* aBody
,
1612 mWorkerPrivate
->AssertIsOnWorkerThread();
1614 // We don't really need to keep the same body-type when we proxy the send()
1615 // call to the main-thread XHR. Let's extract the nsIInputStream from the
1616 // aBody and let's wrap it into a StreamBlobImpl.
1618 RefPtr
<BlobImpl
> blobImpl
;
1621 nsAutoCString charset
;
1622 nsAutoCString defaultContentType
;
1623 nsCOMPtr
<nsIInputStream
> uploadStream
;
1626 aRv
= aBody
->GetAsStream(getter_AddRefs(uploadStream
), &size_u64
,
1627 defaultContentType
, charset
);
1628 if (NS_WARN_IF(aRv
.Failed())) {
1632 blobImpl
= StreamBlobImpl::Create(uploadStream
.forget(),
1633 NS_ConvertUTF8toUTF16(defaultContentType
),
1634 size_u64
, u
"StreamBlobImpl"_ns
);
1635 MOZ_ASSERT(blobImpl
);
1638 RefPtr
<SendRunnable
> sendRunnable
=
1639 new SendRunnable(mWorkerPrivate
, mProxy
, blobImpl
);
1641 // No send() calls when open is running.
1642 if (mProxy
->mOpenCount
) {
1643 aRv
.Throw(NS_ERROR_FAILURE
);
1647 bool hasUploadListeners
= mUpload
? mUpload
->HasListeners() : false;
1654 RefPtr
<XMLHttpRequestWorker
> selfRef
= this;
1655 AutoUnpinXHR
autoUnpin(this);
1656 Maybe
<AutoSyncLoopHolder
> autoSyncLoop
;
1658 nsCOMPtr
<nsISerialEventTarget
> syncLoopTarget
;
1659 bool isSyncXHR
= mProxy
->mIsSyncXHR
;
1661 autoSyncLoop
.emplace(mWorkerPrivate
, Canceling
);
1662 syncLoopTarget
= autoSyncLoop
->GetSerialEventTarget();
1663 if (!syncLoopTarget
) {
1664 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1669 mProxy
->mOuterChannelId
++;
1670 mProxy
->mDispatchPrematureAbortEvent
= false;
1671 mProxy
->mDispatchPrematureAbortEventToUpload
= false;
1673 sendRunnable
->SetSyncLoopTarget(syncLoopTarget
);
1674 sendRunnable
->SetHaveUploadListeners(hasUploadListeners
);
1676 mStateData
->mFlagSend
= true;
1678 sendRunnable
->Dispatch(Canceling
, aRv
);
1680 // Dispatch() may have spun the event loop and we may have already unrooted.
1681 // If so we don't want autoUnpin to try again.
1690 MOZ_ASSERT(!autoSyncLoop
);
1696 bool succeeded
= NS_SUCCEEDED(autoSyncLoop
->Run());
1697 mStateData
->mFlagSend
= false;
1699 // Throw appropriately If a sync XHR failed per spec's RequestErrorSteps
1700 if (isSyncXHR
&& mProxy
) {
1701 nsresult error
= mProxy
->mLastErrorDetailAtLoadend
;
1702 if (error
== NS_ERROR_DOM_ABORT_ERR
) {
1703 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Info
,
1704 ("%p throwing NS_ERROR_DOM_ABORT_ERR", this));
1708 if (error
== NS_ERROR_DOM_TIMEOUT_ERR
) {
1709 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Info
,
1710 ("%p throwing NS_ERROR_DOM_TIMEOUT_ERR", this));
1714 if (error
== NS_ERROR_DOM_NETWORK_ERR
||
1715 NS_ERROR_GET_MODULE(error
) == NS_ERROR_MODULE_NETWORK
) {
1716 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Info
,
1717 ("%p throwing NS_ERROR_DOM_NETWORK_ERR (0x%" PRIx32
")", this,
1718 static_cast<uint32_t>(error
)));
1719 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
1724 // Don't clobber an existing exception that we may have thrown on aRv
1725 // already... though can there really be one? In any case, it seems to me
1726 // that this autoSyncLoop->Run() can never fail, since the StopSyncLoop call
1727 // for it will come from ProxyCompleteRunnable and that always passes true for
1729 if (!succeeded
&& !aRv
.Failed()) {
1730 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1731 ("%p SendInternal failed; throwing NS_ERROR_FAILURE", this));
1732 aRv
.Throw(NS_ERROR_FAILURE
);
1736 void XMLHttpRequestWorker::Open(const nsACString
& aMethod
,
1737 const nsAString
& aUrl
, bool aAsync
,
1738 const Optional
<nsAString
>& aUser
,
1739 const Optional
<nsAString
>& aPassword
,
1741 mWorkerPrivate
->AssertIsOnWorkerThread();
1743 MOZ_LOG(gXMLHttpRequestLog
, LogLevel::Debug
,
1744 ("%p Open(%s,%s,%d)", this, nsAutoCString(aMethod
).get(),
1745 NS_ConvertUTF16toUTF8(aUrl
).get(), aAsync
));
1748 aRv
.ThrowUncatchableException();
1752 bool alsoOverrideMimeType
= false;
1754 MaybeDispatchPrematureAbortEvents(aRv
);
1759 Maybe
<ClientInfo
> clientInfo(
1760 mWorkerPrivate
->GlobalScope()->GetClientInfo());
1761 if (clientInfo
.isNothing()) {
1762 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1765 mProxy
= new Proxy(this, clientInfo
.ref(),
1766 mWorkerPrivate
->GlobalScope()->GetController(), mMozAnon
,
1768 alsoOverrideMimeType
= true;
1771 mProxy
->mOuterEventStreamId
++;
1773 UniquePtr
<SerializedStackHolder
> stack
;
1774 if (mWorkerPrivate
->IsWatchedByDevTools()) {
1775 if (JSContext
* cx
= nsContentUtils::GetCurrentJSContext()) {
1776 stack
= GetCurrentStackForNetMonitor(cx
);
1780 RefPtr
<OpenRunnable
> runnable
= new OpenRunnable(
1781 mWorkerPrivate
, mProxy
, aMethod
, aUrl
, aUser
, aPassword
,
1782 mBackgroundRequest
, mWithCredentials
, mTimeout
, mResponseType
,
1783 alsoOverrideMimeType
? mMimeTypeOverride
: VoidString(), std::move(stack
),
1784 profiler_capture_backtrace());
1786 ++mProxy
->mOpenCount
;
1787 runnable
->Dispatch(Canceling
, aRv
);
1789 if (mProxy
&& !--mProxy
->mOpenCount
) {
1796 // We have been released in one of the nested Open() calls.
1798 aRv
.Throw(NS_ERROR_FAILURE
);
1802 --mProxy
->mOpenCount
;
1803 mProxy
->mIsSyncXHR
= !aAsync
;
1806 void XMLHttpRequestWorker::SetRequestHeader(const nsACString
& aHeader
,
1807 const nsACString
& aValue
,
1809 mWorkerPrivate
->AssertIsOnWorkerThread();
1812 aRv
.ThrowUncatchableException();
1817 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1821 RefPtr
<SetRequestHeaderRunnable
> runnable
=
1822 new SetRequestHeaderRunnable(mWorkerPrivate
, mProxy
, aHeader
, aValue
);
1823 runnable
->Dispatch(Canceling
, aRv
);
1826 void XMLHttpRequestWorker::SetTimeout(uint32_t aTimeout
, ErrorResult
& aRv
) {
1827 mWorkerPrivate
->AssertIsOnWorkerThread();
1830 aRv
.ThrowUncatchableException();
1834 mTimeout
= aTimeout
;
1837 // Open might not have been called yet, in which case we'll handle the
1838 // timeout in OpenRunnable.
1842 RefPtr
<SetTimeoutRunnable
> runnable
=
1843 new SetTimeoutRunnable(mWorkerPrivate
, mProxy
, aTimeout
);
1844 runnable
->Dispatch(Canceling
, aRv
);
1847 void XMLHttpRequestWorker::SetWithCredentials(bool aWithCredentials
,
1849 mWorkerPrivate
->AssertIsOnWorkerThread();
1852 aRv
.ThrowUncatchableException();
1856 mWithCredentials
= aWithCredentials
;
1859 // Open might not have been called yet, in which case we'll handle the
1860 // credentials in OpenRunnable.
1864 RefPtr
<SetWithCredentialsRunnable
> runnable
=
1865 new SetWithCredentialsRunnable(mWorkerPrivate
, mProxy
, aWithCredentials
);
1866 runnable
->Dispatch(Canceling
, aRv
);
1869 void XMLHttpRequestWorker::SetMozBackgroundRequest(bool aBackgroundRequest
,
1871 mWorkerPrivate
->AssertIsOnWorkerThread();
1874 aRv
.ThrowUncatchableException();
1878 mBackgroundRequest
= aBackgroundRequest
;
1881 // Open might not have been called yet, in which case we'll handle the
1882 // background request in OpenRunnable.
1886 RefPtr
<SetBackgroundRequestRunnable
> runnable
=
1887 new SetBackgroundRequestRunnable(mWorkerPrivate
, mProxy
,
1888 aBackgroundRequest
);
1889 runnable
->Dispatch(Canceling
, aRv
);
1892 XMLHttpRequestUpload
* XMLHttpRequestWorker::GetUpload(ErrorResult
& aRv
) {
1893 mWorkerPrivate
->AssertIsOnWorkerThread();
1896 aRv
.ThrowUncatchableException();
1901 mUpload
= new XMLHttpRequestUpload(this);
1907 void XMLHttpRequestWorker::Send(
1909 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
>&
1912 mWorkerPrivate
->AssertIsOnWorkerThread();
1914 if (mFlagSendActive
) {
1915 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT
);
1918 mFlagSendActive
= true;
1919 auto clearRecursionFlag
= MakeScopeExit([&]() {
1920 // No one else should have touched this flag.
1921 MOZ_ASSERT(mFlagSendActive
);
1922 mFlagSendActive
= false;
1926 aRv
.ThrowUncatchableException();
1930 if (mStateData
->mReadyState
!= XMLHttpRequest_Binding::OPENED
) {
1931 aRv
.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
1935 if (!mProxy
|| !mProxy
->mXMLHttpRequestPrivate
|| mStateData
->mFlagSend
) {
1936 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1940 if (aData
.IsNull()) {
1941 SendInternal(nullptr, aRv
);
1945 if (aData
.Value().IsDocument()) {
1946 MOZ_ASSERT_UNREACHABLE("Documents are not exposed to workers.");
1947 aRv
.Throw(NS_ERROR_FAILURE
);
1951 if (aData
.Value().IsBlob()) {
1952 BodyExtractor
<const Blob
> body(&aData
.Value().GetAsBlob());
1953 SendInternal(&body
, aRv
);
1957 if (aData
.Value().IsArrayBuffer()) {
1958 BodyExtractor
<const ArrayBuffer
> body(&aData
.Value().GetAsArrayBuffer());
1959 SendInternal(&body
, aRv
);
1963 if (aData
.Value().IsArrayBufferView()) {
1964 BodyExtractor
<const ArrayBufferView
> body(
1965 &aData
.Value().GetAsArrayBufferView());
1966 SendInternal(&body
, aRv
);
1970 if (aData
.Value().IsFormData()) {
1971 BodyExtractor
<const FormData
> body(&aData
.Value().GetAsFormData());
1972 SendInternal(&body
, aRv
);
1976 if (aData
.Value().IsURLSearchParams()) {
1977 BodyExtractor
<const URLSearchParams
> body(
1978 &aData
.Value().GetAsURLSearchParams());
1979 SendInternal(&body
, aRv
);
1983 if (aData
.Value().IsUSVString()) {
1984 BodyExtractor
<const nsAString
> body(&aData
.Value().GetAsUSVString());
1985 SendInternal(&body
, aRv
);
1990 void XMLHttpRequestWorker::Abort(ErrorResult
& aRv
) {
1991 mWorkerPrivate
->AssertIsOnWorkerThread();
1994 aRv
.ThrowUncatchableException();
2002 // Set our status to 0 and statusText to "" if we
2003 // will be aborting an ongoing fetch, so the upcoming
2004 // abort events we dispatch have the correct info.
2005 if ((mStateData
->mReadyState
== XMLHttpRequest_Binding::OPENED
&&
2006 mStateData
->mFlagSend
) ||
2007 mStateData
->mReadyState
== XMLHttpRequest_Binding::HEADERS_RECEIVED
||
2008 mStateData
->mReadyState
== XMLHttpRequest_Binding::LOADING
||
2009 mStateData
->mReadyState
== XMLHttpRequest_Binding::DONE
) {
2010 mStateData
->mStatus
= 0;
2011 mStateData
->mStatusText
.Truncate();
2014 MaybeDispatchPrematureAbortEvents(aRv
);
2019 if (mStateData
->mReadyState
== 4) {
2020 // No one did anything to us while we fired abort events, so reset our state
2022 mStateData
->mReadyState
= 0;
2025 mProxy
->mOuterEventStreamId
++;
2027 RefPtr
<AbortRunnable
> runnable
= new AbortRunnable(mWorkerPrivate
, mProxy
);
2028 runnable
->Dispatch(Canceling
, aRv
);
2031 void XMLHttpRequestWorker::GetResponseHeader(const nsACString
& aHeader
,
2032 nsACString
& aResponseHeader
,
2034 mWorkerPrivate
->AssertIsOnWorkerThread();
2037 aRv
.ThrowUncatchableException();
2042 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2046 nsCString responseHeader
;
2047 RefPtr
<GetResponseHeaderRunnable
> runnable
= new GetResponseHeaderRunnable(
2048 mWorkerPrivate
, mProxy
, aHeader
, responseHeader
);
2049 runnable
->Dispatch(Canceling
, aRv
);
2053 aResponseHeader
= responseHeader
;
2056 void XMLHttpRequestWorker::GetAllResponseHeaders(nsACString
& aResponseHeaders
,
2058 mWorkerPrivate
->AssertIsOnWorkerThread();
2061 aRv
.ThrowUncatchableException();
2066 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2070 nsCString responseHeaders
;
2071 RefPtr
<GetAllResponseHeadersRunnable
> runnable
=
2072 new GetAllResponseHeadersRunnable(mWorkerPrivate
, mProxy
,
2074 runnable
->Dispatch(Canceling
, aRv
);
2079 aResponseHeaders
= responseHeaders
;
2082 void XMLHttpRequestWorker::OverrideMimeType(const nsAString
& aMimeType
,
2084 mWorkerPrivate
->AssertIsOnWorkerThread();
2087 aRv
.ThrowUncatchableException();
2091 // We're supposed to throw if the state is LOADING or DONE.
2092 if (mStateData
->mReadyState
== XMLHttpRequest_Binding::LOADING
||
2093 mStateData
->mReadyState
== XMLHttpRequest_Binding::DONE
) {
2094 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2098 mMimeTypeOverride
= aMimeType
;
2101 RefPtr
<OverrideMimeTypeRunnable
> runnable
=
2102 new OverrideMimeTypeRunnable(mWorkerPrivate
, mProxy
, aMimeType
);
2103 runnable
->Dispatch(Canceling
, aRv
);
2107 void XMLHttpRequestWorker::SetResponseType(
2108 XMLHttpRequestResponseType aResponseType
, ErrorResult
& aRv
) {
2109 mWorkerPrivate
->AssertIsOnWorkerThread();
2111 // "document" is fine for the main thread but not for a worker. Short-circuit
2113 if (aResponseType
== XMLHttpRequestResponseType::Document
) {
2118 // Open() has not been called yet. We store the responseType and we will use
2119 // it later in Open().
2120 mResponseType
= aResponseType
;
2124 if (mStateData
->mReadyState
== XMLHttpRequest_Binding::LOADING
||
2125 mStateData
->mReadyState
== XMLHttpRequest_Binding::DONE
) {
2126 aRv
.ThrowInvalidStateError(
2127 "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
2128 "(when its state is LOADING or DONE).");
2132 RefPtr
<SetResponseTypeRunnable
> runnable
=
2133 new SetResponseTypeRunnable(mWorkerPrivate
, mProxy
, aResponseType
);
2134 runnable
->Dispatch(Canceling
, aRv
);
2139 mResponseType
= runnable
->ResponseType();
2142 void XMLHttpRequestWorker::GetResponse(JSContext
* aCx
,
2143 JS::MutableHandle
<JS::Value
> aResponse
,
2145 if (NS_FAILED(mResponseData
->mResponseResult
)) {
2146 aRv
.Throw(mResponseData
->mResponseResult
);
2150 switch (mResponseType
) {
2151 case XMLHttpRequestResponseType::_empty
:
2152 case XMLHttpRequestResponseType::Text
: {
2155 if (mResponseData
->mResponseText
.IsEmpty()) {
2156 aResponse
.set(JS_GetEmptyStringValue(aCx
));
2160 str
= mResponseData
->mResponseText
.GetAsJSStringCopy(aCx
);
2162 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
2166 aResponse
.setString(str
);
2170 case XMLHttpRequestResponseType::Arraybuffer
: {
2171 if (!mResponseData
->mResponseArrayBufferBuilder
) {
2172 aResponse
.setNull();
2176 if (!mResponseArrayBufferValue
) {
2177 mResponseArrayBufferValue
=
2178 mResponseData
->mResponseArrayBufferBuilder
->TakeArrayBuffer(aCx
);
2179 if (!mResponseArrayBufferValue
) {
2180 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
2185 aResponse
.setObject(*mResponseArrayBufferValue
);
2189 case XMLHttpRequestResponseType::Blob
: {
2190 if (!mResponseData
->mResponseBlobImpl
) {
2191 aResponse
.setNull();
2195 if (!mResponseBlob
) {
2197 Blob::Create(GetOwnerGlobal(), mResponseData
->mResponseBlobImpl
);
2200 if (!mResponseBlob
||
2201 !GetOrCreateDOMReflector(aCx
, mResponseBlob
, aResponse
)) {
2202 aResponse
.setNull();
2208 case XMLHttpRequestResponseType::Json
: {
2209 if (mResponseData
->mResponseJSON
.IsVoid()) {
2210 aResponse
.setNull();
2214 if (mResponseJSONValue
.isUndefined()) {
2215 // The Unicode converter has already zapped the BOM if there was one
2216 JS::Rooted
<JS::Value
> value(aCx
);
2217 if (!JS_ParseJSON(aCx
, mResponseData
->mResponseJSON
.BeginReading(),
2218 mResponseData
->mResponseJSON
.Length(), &value
)) {
2219 JS_ClearPendingException(aCx
);
2220 mResponseJSONValue
.setNull();
2222 mResponseJSONValue
= value
;
2225 mResponseData
->mResponseJSON
.Truncate();
2228 aResponse
.set(mResponseJSONValue
);
2233 MOZ_ASSERT_UNREACHABLE("Invalid type");
2234 aResponse
.setNull();
2239 void XMLHttpRequestWorker::GetResponseText(DOMString
& aResponseText
,
2241 MOZ_DIAGNOSTIC_ASSERT(mResponseData
);
2243 if (mResponseType
!= XMLHttpRequestResponseType::_empty
&&
2244 mResponseType
!= XMLHttpRequestResponseType::Text
) {
2245 aRv
.ThrowInvalidStateError(
2246 "responseText is only available if responseType is '' or 'text'.");
2250 if (!mResponseData
->mResponseText
.GetAsString(aResponseText
)) {
2251 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
2256 void XMLHttpRequestWorker::UpdateState(
2257 UniquePtr
<StateData
>&& aStateData
,
2258 UniquePtr
<ResponseData
>&& aResponseData
) {
2259 mStateData
= std::move(aStateData
);
2261 UniquePtr
<ResponseData
> responseData
= std::move(aResponseData
);
2263 ResetResponseData();
2264 mResponseData
= std::move(responseData
);
2267 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
2270 void XMLHttpRequestWorker::ResetResponseData() {
2271 mResponseBlob
= nullptr;
2272 mResponseArrayBufferValue
= nullptr;
2273 mResponseJSONValue
.setUndefined();
2276 } // namespace mozilla::dom