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 "BodyConsumer.h"
9 #include "mozilla/dom/BlobBinding.h"
10 #include "mozilla/dom/BlobImpl.h"
11 #include "mozilla/dom/BlobURLProtocolHandler.h"
12 #include "mozilla/dom/BodyUtil.h"
13 #include "mozilla/dom/File.h"
14 #include "mozilla/dom/FileBinding.h"
15 #include "mozilla/dom/FileCreatorHelper.h"
16 #include "mozilla/dom/MutableBlobStreamListener.h"
17 #include "mozilla/dom/Promise.h"
18 #include "mozilla/dom/PromiseNativeHandler.h"
19 #include "mozilla/dom/WorkerCommon.h"
20 #include "mozilla/dom/WorkerPrivate.h"
21 #include "mozilla/dom/WorkerRef.h"
22 #include "mozilla/dom/WorkerRunnable.h"
23 #include "mozilla/dom/WorkerScope.h"
24 #include "mozilla/ipc/PBackgroundSharedTypes.h"
25 #include "mozilla/ScopeExit.h"
26 #include "nsComponentManagerUtils.h"
28 #include "nsIThreadRetargetableRequest.h"
29 #include "nsIStreamLoader.h"
30 #include "nsNetUtil.h"
31 #include "nsProxyRelease.h"
33 // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
34 // replaced by FileCreatorHelper#CreateFileW.
39 namespace mozilla::dom
{
43 class BeginConsumeBodyRunnable final
: public Runnable
{
45 BeginConsumeBodyRunnable(BodyConsumer
* aConsumer
,
46 ThreadSafeWorkerRef
* aWorkerRef
)
47 : Runnable("BeginConsumeBodyRunnable"),
48 mBodyConsumer(aConsumer
),
49 mWorkerRef(aWorkerRef
) {}
53 mBodyConsumer
->BeginConsumeBodyMainThread(mWorkerRef
);
58 RefPtr
<BodyConsumer
> mBodyConsumer
;
59 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
63 * Called on successfully reading the complete stream.
65 class ContinueConsumeBodyRunnable final
: public MainThreadWorkerRunnable
{
66 RefPtr
<BodyConsumer
> mBodyConsumer
;
72 ContinueConsumeBodyRunnable(BodyConsumer
* aBodyConsumer
,
73 WorkerPrivate
* aWorkerPrivate
, nsresult aStatus
,
74 uint32_t aLength
, uint8_t* aResult
)
75 : MainThreadWorkerRunnable(aWorkerPrivate
),
76 mBodyConsumer(aBodyConsumer
),
80 MOZ_ASSERT(NS_IsMainThread());
83 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
84 mBodyConsumer
->ContinueConsumeBody(mStatus
, mLength
, mResult
);
89 // ControlRunnable used to complete the releasing of resources on the worker
90 // thread when already shutting down.
91 class AbortConsumeBodyControlRunnable final
92 : public MainThreadWorkerControlRunnable
{
93 RefPtr
<BodyConsumer
> mBodyConsumer
;
96 AbortConsumeBodyControlRunnable(BodyConsumer
* aBodyConsumer
,
97 WorkerPrivate
* aWorkerPrivate
)
98 : MainThreadWorkerControlRunnable(aWorkerPrivate
),
99 mBodyConsumer(aBodyConsumer
) {
100 MOZ_ASSERT(NS_IsMainThread());
103 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
104 mBodyConsumer
->ContinueConsumeBody(NS_BINDING_ABORTED
, 0, nullptr,
105 true /* shutting down */);
111 * In case of failure to create a stream pump or dispatch stream completion to
112 * worker, ensure we cleanup properly. Thread agnostic.
114 class MOZ_STACK_CLASS AutoFailConsumeBody final
{
116 AutoFailConsumeBody(BodyConsumer
* aBodyConsumer
,
117 ThreadSafeWorkerRef
* aWorkerRef
)
118 : mBodyConsumer(aBodyConsumer
), mWorkerRef(aWorkerRef
) {}
120 ~AutoFailConsumeBody() {
121 AssertIsOnMainThread();
123 if (!mBodyConsumer
) {
129 RefPtr
<AbortConsumeBodyControlRunnable
> r
=
130 new AbortConsumeBodyControlRunnable(mBodyConsumer
,
131 mWorkerRef
->Private());
132 if (!r
->Dispatch()) {
133 MOZ_CRASH("We are going to leak");
139 mBodyConsumer
->ContinueConsumeBody(NS_ERROR_FAILURE
, 0, nullptr);
142 void DontFail() { mBodyConsumer
= nullptr; }
145 RefPtr
<BodyConsumer
> mBodyConsumer
;
146 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
150 * Called on successfully reading the complete stream for Blob.
152 class ContinueConsumeBlobBodyRunnable final
: public MainThreadWorkerRunnable
{
153 RefPtr
<BodyConsumer
> mBodyConsumer
;
154 RefPtr
<BlobImpl
> mBlobImpl
;
157 ContinueConsumeBlobBodyRunnable(BodyConsumer
* aBodyConsumer
,
158 WorkerPrivate
* aWorkerPrivate
,
160 : MainThreadWorkerRunnable(aWorkerPrivate
),
161 mBodyConsumer(aBodyConsumer
),
162 mBlobImpl(aBlobImpl
) {
163 MOZ_ASSERT(NS_IsMainThread());
164 MOZ_ASSERT(mBlobImpl
);
167 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
168 mBodyConsumer
->ContinueConsumeBlobBody(mBlobImpl
);
173 // ControlRunnable used to complete the releasing of resources on the worker
174 // thread when already shutting down.
175 class AbortConsumeBlobBodyControlRunnable final
176 : public MainThreadWorkerControlRunnable
{
177 RefPtr
<BodyConsumer
> mBodyConsumer
;
180 AbortConsumeBlobBodyControlRunnable(BodyConsumer
* aBodyConsumer
,
181 WorkerPrivate
* aWorkerPrivate
)
182 : MainThreadWorkerControlRunnable(aWorkerPrivate
),
183 mBodyConsumer(aBodyConsumer
) {
184 MOZ_ASSERT(NS_IsMainThread());
187 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
188 mBodyConsumer
->ContinueConsumeBlobBody(nullptr, true /* shutting down */);
193 class ConsumeBodyDoneObserver final
: public nsIStreamLoaderObserver
,
194 public MutableBlobStorageCallback
{
196 NS_DECL_THREADSAFE_ISUPPORTS
198 ConsumeBodyDoneObserver(BodyConsumer
* aBodyConsumer
,
199 ThreadSafeWorkerRef
* aWorkerRef
)
200 : mBodyConsumer(aBodyConsumer
), mWorkerRef(aWorkerRef
) {}
203 OnStreamComplete(nsIStreamLoader
* aLoader
, nsISupports
* aCtxt
,
204 nsresult aStatus
, uint32_t aResultLength
,
205 const uint8_t* aResult
) override
{
206 MOZ_ASSERT(NS_IsMainThread());
208 // The loading is completed. Let's nullify the pump before continuing the
209 // consuming of the body.
210 mBodyConsumer
->NullifyConsumeBodyPump();
212 uint8_t* nonconstResult
= const_cast<uint8_t*>(aResult
);
216 mBodyConsumer
->ContinueConsumeBody(aStatus
, aResultLength
,
218 // The caller is responsible for data.
219 return NS_SUCCESS_ADOPTED_DATA
;
224 RefPtr
<ContinueConsumeBodyRunnable
> r
= new ContinueConsumeBodyRunnable(
225 mBodyConsumer
, mWorkerRef
->Private(), aStatus
, aResultLength
,
228 // The caller is responsible for data.
229 return NS_SUCCESS_ADOPTED_DATA
;
233 // The worker is shutting down. Let's use a control runnable to complete the
234 // shutting down procedure.
236 RefPtr
<AbortConsumeBodyControlRunnable
> r
=
237 new AbortConsumeBodyControlRunnable(mBodyConsumer
,
238 mWorkerRef
->Private());
239 if (NS_WARN_IF(!r
->Dispatch())) {
240 return NS_ERROR_FAILURE
;
243 // We haven't taken ownership of the data.
247 virtual void BlobStoreCompleted(MutableBlobStorage
* aBlobStorage
,
248 BlobImpl
* aBlobImpl
, nsresult aRv
) override
{
250 if (NS_FAILED(aRv
)) {
251 OnStreamComplete(nullptr, nullptr, aRv
, 0, nullptr);
255 // The loading is completed. Let's nullify the pump before continuing the
256 // consuming of the body.
257 mBodyConsumer
->NullifyConsumeBodyPump();
259 mBodyConsumer
->OnBlobResult(aBlobImpl
, mWorkerRef
);
263 ~ConsumeBodyDoneObserver() = default;
265 RefPtr
<BodyConsumer
> mBodyConsumer
;
266 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
269 NS_IMPL_ISUPPORTS(ConsumeBodyDoneObserver
, nsIStreamLoaderObserver
)
273 /* static */ already_AddRefed
<Promise
> BodyConsumer::Create(
274 nsIGlobalObject
* aGlobal
, nsIEventTarget
* aMainThreadEventTarget
,
275 nsIInputStream
* aBodyStream
, AbortSignalImpl
* aSignalImpl
,
276 ConsumeType aType
, const nsACString
& aBodyBlobURISpec
,
277 const nsAString
& aBodyLocalPath
, const nsACString
& aBodyMimeType
,
278 MutableBlobStorage::MutableBlobStorageType aBlobStorageType
,
280 MOZ_ASSERT(aBodyStream
);
281 MOZ_ASSERT(aMainThreadEventTarget
);
283 RefPtr
<Promise
> promise
= Promise::Create(aGlobal
, aRv
);
288 RefPtr
<BodyConsumer
> consumer
= new BodyConsumer(
289 aMainThreadEventTarget
, aGlobal
, aBodyStream
, promise
, aType
,
290 aBodyBlobURISpec
, aBodyLocalPath
, aBodyMimeType
, aBlobStorageType
);
292 RefPtr
<ThreadSafeWorkerRef
> workerRef
;
294 if (!NS_IsMainThread()) {
295 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
296 MOZ_ASSERT(workerPrivate
);
298 RefPtr
<StrongWorkerRef
> strongWorkerRef
=
299 StrongWorkerRef::Create(workerPrivate
, "BodyConsumer", [consumer
]() {
300 consumer
->mConsumePromise
= nullptr;
301 consumer
->mBodyConsumed
= true;
302 consumer
->ReleaseObject();
303 consumer
->ShutDownMainThreadConsuming();
305 if (NS_WARN_IF(!strongWorkerRef
)) {
306 aRv
.Throw(NS_ERROR_FAILURE
);
310 workerRef
= new ThreadSafeWorkerRef(strongWorkerRef
);
312 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
313 if (NS_WARN_IF(!os
)) {
314 aRv
.Throw(NS_ERROR_FAILURE
);
318 aRv
= os
->AddObserver(consumer
, DOM_WINDOW_DESTROYED_TOPIC
, true);
319 if (NS_WARN_IF(aRv
.Failed())) {
323 aRv
= os
->AddObserver(consumer
, DOM_WINDOW_FROZEN_TOPIC
, true);
324 if (NS_WARN_IF(aRv
.Failed())) {
329 nsCOMPtr
<nsIRunnable
> r
= new BeginConsumeBodyRunnable(consumer
, workerRef
);
330 aRv
= aMainThreadEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
331 if (NS_WARN_IF(aRv
.Failed())) {
336 consumer
->Follow(aSignalImpl
);
339 return promise
.forget();
342 void BodyConsumer::ReleaseObject() {
343 AssertIsOnTargetThread();
345 if (NS_IsMainThread()) {
346 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
348 os
->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC
);
349 os
->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC
);
358 BodyConsumer::BodyConsumer(
359 nsIEventTarget
* aMainThreadEventTarget
, nsIGlobalObject
* aGlobalObject
,
360 nsIInputStream
* aBodyStream
, Promise
* aPromise
, ConsumeType aType
,
361 const nsACString
& aBodyBlobURISpec
, const nsAString
& aBodyLocalPath
,
362 const nsACString
& aBodyMimeType
,
363 MutableBlobStorage::MutableBlobStorageType aBlobStorageType
)
364 : mTargetThread(NS_GetCurrentThread()),
365 mMainThreadEventTarget(aMainThreadEventTarget
),
366 mBodyStream(aBodyStream
),
367 mBlobStorageType(aBlobStorageType
),
368 mBodyMimeType(aBodyMimeType
),
369 mBodyBlobURISpec(aBodyBlobURISpec
),
370 mBodyLocalPath(aBodyLocalPath
),
371 mGlobal(aGlobalObject
),
373 mConsumePromise(aPromise
),
374 mBodyConsumed(false),
375 mShuttingDown(false) {
376 MOZ_ASSERT(aMainThreadEventTarget
);
377 MOZ_ASSERT(aBodyStream
);
378 MOZ_ASSERT(aPromise
);
381 BodyConsumer::~BodyConsumer() = default;
383 void BodyConsumer::AssertIsOnTargetThread() const {
384 MOZ_ASSERT(NS_GetCurrentThread() == mTargetThread
);
389 class FileCreationHandler final
: public PromiseNativeHandler
{
391 NS_DECL_THREADSAFE_ISUPPORTS
393 static void Create(Promise
* aPromise
, BodyConsumer
* aConsumer
,
394 ThreadSafeWorkerRef
* aWorkerRef
) {
395 AssertIsOnMainThread();
396 MOZ_ASSERT(aPromise
);
398 RefPtr
<FileCreationHandler
> handler
=
399 new FileCreationHandler(aConsumer
, aWorkerRef
);
400 aPromise
->AppendNativeHandler(handler
);
403 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) override
{
404 AssertIsOnMainThread();
406 if (NS_WARN_IF(!aValue
.isObject())) {
407 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
412 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob
, &aValue
.toObject(), blob
)))) {
413 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
417 mConsumer
->OnBlobResult(blob
->Impl(), mWorkerRef
);
420 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) override
{
421 AssertIsOnMainThread();
423 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
427 FileCreationHandler(BodyConsumer
* aConsumer
, ThreadSafeWorkerRef
* aWorkerRef
)
428 : mConsumer(aConsumer
), mWorkerRef(aWorkerRef
) {
429 AssertIsOnMainThread();
430 MOZ_ASSERT(aConsumer
);
433 ~FileCreationHandler() = default;
435 RefPtr
<BodyConsumer
> mConsumer
;
436 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
439 NS_IMPL_ISUPPORTS0(FileCreationHandler
)
443 nsresult
BodyConsumer::GetBodyLocalFile(nsIFile
** aFile
) const {
444 AssertIsOnMainThread();
446 if (!mBodyLocalPath
.Length()) {
451 nsCOMPtr
<nsIFile
> file
= do_CreateInstance("@mozilla.org/file/local;1", &rv
);
456 rv
= file
->InitWithPath(mBodyLocalPath
);
457 NS_ENSURE_SUCCESS(rv
, rv
);
460 rv
= file
->Exists(&exists
);
461 NS_ENSURE_SUCCESS(rv
, rv
);
463 return NS_ERROR_FILE_NOT_FOUND
;
467 rv
= file
->IsDirectory(&isDir
);
468 NS_ENSURE_SUCCESS(rv
, rv
);
470 return NS_ERROR_FILE_IS_DIRECTORY
;
478 * BeginConsumeBodyMainThread() will automatically reject the consume promise
479 * and clean up on any failures, so there is no need for callers to do so,
480 * reflected in a lack of error return code.
482 void BodyConsumer::BeginConsumeBodyMainThread(ThreadSafeWorkerRef
* aWorkerRef
) {
483 AssertIsOnMainThread();
485 AutoFailConsumeBody
autoReject(this, aWorkerRef
);
488 // We haven't started yet, but we have been terminated. AutoFailConsumeBody
489 // will dispatch a runnable to release resources.
493 if (mConsumeType
== CONSUME_BLOB
) {
496 // If we're trying to consume a blob, and the request was for a blob URI,
497 // then just consume that URI's blob instance.
498 if (!mBodyBlobURISpec
.IsEmpty()) {
499 RefPtr
<BlobImpl
> blobImpl
;
500 rv
= NS_GetBlobForBlobURISpec(mBodyBlobURISpec
, getter_AddRefs(blobImpl
));
501 if (NS_WARN_IF(NS_FAILED(rv
)) || !blobImpl
) {
504 autoReject
.DontFail();
505 DispatchContinueConsumeBlobBody(blobImpl
, aWorkerRef
);
509 // If we're trying to consume a blob, and the request was for a local
510 // file, then generate and return a File blob.
511 nsCOMPtr
<nsIFile
> file
;
512 rv
= GetBodyLocalFile(getter_AddRefs(file
));
513 if (!NS_WARN_IF(NS_FAILED(rv
)) && file
&& !aWorkerRef
) {
514 ChromeFilePropertyBag bag
;
515 CopyUTF8toUTF16(mBodyMimeType
, bag
.mType
);
518 RefPtr
<Promise
> promise
=
519 FileCreatorHelper::CreateFile(mGlobal
, file
, bag
, true, error
);
520 if (NS_WARN_IF(error
.Failed())) {
524 autoReject
.DontFail();
525 FileCreationHandler::Create(promise
, this, aWorkerRef
);
530 nsCOMPtr
<nsIInputStreamPump
> pump
;
532 NS_NewInputStreamPump(getter_AddRefs(pump
), mBodyStream
.forget(), 0, 0,
533 false, mMainThreadEventTarget
);
534 if (NS_WARN_IF(NS_FAILED(rv
))) {
538 RefPtr
<ConsumeBodyDoneObserver
> p
=
539 new ConsumeBodyDoneObserver(this, aWorkerRef
);
541 nsCOMPtr
<nsIStreamListener
> listener
;
542 if (mConsumeType
== CONSUME_BLOB
) {
543 listener
= new MutableBlobStreamListener(mBlobStorageType
, mBodyMimeType
, p
,
544 mMainThreadEventTarget
);
546 nsCOMPtr
<nsIStreamLoader
> loader
;
547 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), p
);
548 if (NS_WARN_IF(NS_FAILED(rv
))) {
555 rv
= pump
->AsyncRead(listener
);
556 if (NS_WARN_IF(NS_FAILED(rv
))) {
560 // Now that everything succeeded, we can assign the pump to a pointer that
561 // stays alive for the lifetime of the BodyConsumer.
562 mConsumeBodyPump
= pump
;
564 // It is ok for retargeting to fail and reads to happen on the main thread.
565 autoReject
.DontFail();
567 // Try to retarget, otherwise fall back to main thread.
568 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(pump
);
570 nsCOMPtr
<nsIEventTarget
> sts
=
571 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
572 rv
= rr
->RetargetDeliveryTo(sts
);
574 NS_WARNING("Retargeting failed");
580 * OnBlobResult() is called when a blob body is ready to be consumed (when its
581 * network transfer completes in BeginConsumeBodyRunnable or its local File has
582 * been wrapped by FileCreationHandler). The blob is sent to the target thread
583 * and ContinueConsumeBody is called.
585 void BodyConsumer::OnBlobResult(BlobImpl
* aBlobImpl
,
586 ThreadSafeWorkerRef
* aWorkerRef
) {
587 AssertIsOnMainThread();
589 DispatchContinueConsumeBlobBody(aBlobImpl
, aWorkerRef
);
592 void BodyConsumer::DispatchContinueConsumeBlobBody(
593 BlobImpl
* aBlobImpl
, ThreadSafeWorkerRef
* aWorkerRef
) {
594 AssertIsOnMainThread();
599 ContinueConsumeBlobBody(aBlobImpl
);
601 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
608 RefPtr
<ContinueConsumeBlobBodyRunnable
> r
=
609 new ContinueConsumeBlobBodyRunnable(this, aWorkerRef
->Private(),
616 RefPtr
<ContinueConsumeBodyRunnable
> r
= new ContinueConsumeBodyRunnable(
617 this, aWorkerRef
->Private(), NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
624 // The worker is shutting down. Let's use a control runnable to complete the
625 // shutting down procedure.
627 RefPtr
<AbortConsumeBlobBodyControlRunnable
> r
=
628 new AbortConsumeBlobBodyControlRunnable(this, aWorkerRef
->Private());
630 Unused
<< NS_WARN_IF(!r
->Dispatch());
634 * ContinueConsumeBody() is to be called on the target thread whenever the
635 * final result of the fetch is known. The fetch promise is resolved or
636 * rejected based on whether the fetch succeeded, and the body can be
637 * converted into the expected type of JS object.
639 void BodyConsumer::ContinueConsumeBody(nsresult aStatus
, uint32_t aResultLength
,
640 uint8_t* aResult
, bool aShuttingDown
) {
641 AssertIsOnTargetThread();
643 // This makes sure that we free the data correctly.
644 auto autoFree
= mozilla::MakeScopeExit([&] { free(aResult
); });
649 mBodyConsumed
= true;
651 MOZ_ASSERT(mConsumePromise
);
652 RefPtr
<Promise
> localPromise
= std::move(mConsumePromise
);
654 RefPtr
<BodyConsumer
> self
= this;
655 auto autoReleaseObject
=
656 mozilla::MakeScopeExit([self
] { self
->ReleaseObject(); });
659 // If shutting down, we don't want to resolve any promise.
663 if (NS_WARN_IF(NS_FAILED(aStatus
))) {
665 // https://fetch.spec.whatwg.org/#concept-read-all-bytes-from-readablestream
666 // Decoding errors should reject with a TypeError
667 if (aStatus
== NS_ERROR_INVALID_CONTENT_ENCODING
) {
668 localPromise
->MaybeRejectWithTypeError
<MSG_DOM_DECODING_FAILED
>();
670 localPromise
->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
674 // Don't warn here since we warned above.
675 if (NS_FAILED(aStatus
)) {
679 // Finish successfully consuming body according to type.
683 if (!jsapi
.Init(mGlobal
)) {
684 localPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
688 JSContext
* cx
= jsapi
.cx();
691 switch (mConsumeType
) {
692 case CONSUME_ARRAYBUFFER
: {
693 JS::Rooted
<JSObject
*> arrayBuffer(cx
);
694 BodyUtil::ConsumeArrayBuffer(cx
, &arrayBuffer
, aResultLength
, aResult
,
697 if (!error
.Failed()) {
698 JS::Rooted
<JS::Value
> val(cx
);
699 val
.setObjectOrNull(arrayBuffer
);
701 localPromise
->MaybeResolve(val
);
702 // ArrayBuffer takes over ownership.
708 MOZ_CRASH("This should not happen.");
711 case CONSUME_FORMDATA
: {
713 data
.Adopt(reinterpret_cast<char*>(aResult
), aResultLength
);
716 RefPtr
<dom::FormData
> fd
=
717 BodyUtil::ConsumeFormData(mGlobal
, mBodyMimeType
, data
, error
);
718 if (!error
.Failed()) {
719 localPromise
->MaybeResolve(fd
);
724 // fall through handles early exit.
728 BodyUtil::ConsumeText(aResultLength
, aResult
, decoded
))) {
729 if (mConsumeType
== CONSUME_TEXT
) {
730 localPromise
->MaybeResolve(decoded
);
732 JS::Rooted
<JS::Value
> json(cx
);
733 BodyUtil::ConsumeJson(cx
, &json
, decoded
, error
);
734 if (!error
.Failed()) {
735 localPromise
->MaybeResolve(json
);
742 MOZ_ASSERT_UNREACHABLE("Unexpected consume body type");
745 error
.WouldReportJSException();
746 if (error
.Failed()) {
747 localPromise
->MaybeReject(std::move(error
));
751 void BodyConsumer::ContinueConsumeBlobBody(BlobImpl
* aBlobImpl
,
752 bool aShuttingDown
) {
753 AssertIsOnTargetThread();
754 MOZ_ASSERT(mConsumeType
== CONSUME_BLOB
);
759 mBodyConsumed
= true;
761 MOZ_ASSERT(mConsumePromise
);
762 RefPtr
<Promise
> localPromise
= std::move(mConsumePromise
);
764 if (!aShuttingDown
) {
765 RefPtr
<dom::Blob
> blob
= dom::Blob::Create(mGlobal
, aBlobImpl
);
766 if (NS_WARN_IF(!blob
)) {
767 localPromise
->MaybeReject(NS_ERROR_FAILURE
);
771 localPromise
->MaybeResolve(blob
);
777 void BodyConsumer::ShutDownMainThreadConsuming() {
778 if (!NS_IsMainThread()) {
779 RefPtr
<BodyConsumer
> self
= this;
781 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
782 "BodyConsumer::ShutDownMainThreadConsuming",
783 [self
]() { self
->ShutDownMainThreadConsuming(); });
785 mMainThreadEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
789 // We need this because maybe, mConsumeBodyPump has not been created yet. We
790 // must be sure that we don't try to do it.
791 mShuttingDown
= true;
793 if (mConsumeBodyPump
) {
794 mConsumeBodyPump
->Cancel(NS_BINDING_ABORTED
);
795 mConsumeBodyPump
= nullptr;
799 NS_IMETHODIMP
BodyConsumer::Observe(nsISupports
* aSubject
, const char* aTopic
,
800 const char16_t
* aData
) {
801 AssertIsOnMainThread();
803 MOZ_ASSERT((strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) ||
804 (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0));
806 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(mGlobal
);
807 if (SameCOMIdentity(aSubject
, window
)) {
808 ContinueConsumeBody(NS_BINDING_ABORTED
, 0, nullptr);
814 void BodyConsumer::RunAbortAlgorithm() {
815 AssertIsOnTargetThread();
816 ShutDownMainThreadConsuming();
817 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
820 NS_IMPL_ISUPPORTS(BodyConsumer
, nsIObserver
, nsISupportsWeakReference
)
822 } // namespace mozilla::dom