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 "mozilla/TaskQueue.h"
27 #include "nsComponentManagerUtils.h"
29 #include "nsIThreadRetargetableRequest.h"
30 #include "nsIStreamLoader.h"
31 #include "nsNetUtil.h"
32 #include "nsProxyRelease.h"
33 #include "nsIInputStream.h"
35 // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
36 // replaced by FileCreatorHelper#CreateFileW.
41 namespace mozilla::dom
{
45 class BeginConsumeBodyRunnable final
: public Runnable
{
47 BeginConsumeBodyRunnable(BodyConsumer
* aConsumer
,
48 ThreadSafeWorkerRef
* aWorkerRef
)
49 : Runnable("BeginConsumeBodyRunnable"),
50 mBodyConsumer(aConsumer
),
51 mWorkerRef(aWorkerRef
) {}
55 mBodyConsumer
->BeginConsumeBodyMainThread(mWorkerRef
);
60 RefPtr
<BodyConsumer
> mBodyConsumer
;
61 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
65 * Called on successfully reading the complete stream.
67 class ContinueConsumeBodyRunnable final
: public MainThreadWorkerRunnable
{
68 RefPtr
<BodyConsumer
> mBodyConsumer
;
74 ContinueConsumeBodyRunnable(BodyConsumer
* aBodyConsumer
,
75 WorkerPrivate
* aWorkerPrivate
, nsresult aStatus
,
76 uint32_t aLength
, uint8_t* aResult
)
77 : MainThreadWorkerRunnable("ContinueConsumeBodyRunnable"),
78 mBodyConsumer(aBodyConsumer
),
82 MOZ_ASSERT(NS_IsMainThread());
85 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
86 mBodyConsumer
->ContinueConsumeBody(mStatus
, mLength
, mResult
);
91 // ControlRunnable used to complete the releasing of resources on the worker
92 // thread when already shutting down.
93 class AbortConsumeBodyControlRunnable final
94 : public MainThreadWorkerControlRunnable
{
95 RefPtr
<BodyConsumer
> mBodyConsumer
;
98 AbortConsumeBodyControlRunnable(BodyConsumer
* aBodyConsumer
,
99 WorkerPrivate
* aWorkerPrivate
)
100 : MainThreadWorkerControlRunnable("AbortConsumeBodyControlRunnable"),
101 mBodyConsumer(aBodyConsumer
) {
102 MOZ_ASSERT(NS_IsMainThread());
105 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
106 mBodyConsumer
->ContinueConsumeBody(NS_BINDING_ABORTED
, 0, nullptr,
107 true /* shutting down */);
113 * In case of failure to create a stream pump or dispatch stream completion to
114 * worker, ensure we cleanup properly. Thread agnostic.
116 class MOZ_STACK_CLASS AutoFailConsumeBody final
{
118 AutoFailConsumeBody(BodyConsumer
* aBodyConsumer
,
119 ThreadSafeWorkerRef
* aWorkerRef
)
120 : mBodyConsumer(aBodyConsumer
), mWorkerRef(aWorkerRef
) {}
122 ~AutoFailConsumeBody() {
123 AssertIsOnMainThread();
125 if (!mBodyConsumer
) {
131 RefPtr
<AbortConsumeBodyControlRunnable
> r
=
132 new AbortConsumeBodyControlRunnable(mBodyConsumer
,
133 mWorkerRef
->Private());
134 if (!r
->Dispatch(mWorkerRef
->Private())) {
135 MOZ_CRASH("We are going to leak");
141 mBodyConsumer
->ContinueConsumeBody(NS_ERROR_FAILURE
, 0, nullptr);
144 void DontFail() { mBodyConsumer
= nullptr; }
147 RefPtr
<BodyConsumer
> mBodyConsumer
;
148 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
152 * Called on successfully reading the complete stream for Blob.
154 class ContinueConsumeBlobBodyRunnable final
: public MainThreadWorkerRunnable
{
155 RefPtr
<BodyConsumer
> mBodyConsumer
;
156 RefPtr
<BlobImpl
> mBlobImpl
;
159 ContinueConsumeBlobBodyRunnable(BodyConsumer
* aBodyConsumer
,
160 WorkerPrivate
* aWorkerPrivate
,
162 : MainThreadWorkerRunnable("ContinueConsumeBlobBodyRunnable"),
163 mBodyConsumer(aBodyConsumer
),
164 mBlobImpl(aBlobImpl
) {
165 MOZ_ASSERT(NS_IsMainThread());
166 MOZ_ASSERT(mBlobImpl
);
169 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
170 mBodyConsumer
->ContinueConsumeBlobBody(mBlobImpl
);
175 // ControlRunnable used to complete the releasing of resources on the worker
176 // thread when already shutting down.
177 class AbortConsumeBlobBodyControlRunnable final
178 : public MainThreadWorkerControlRunnable
{
179 RefPtr
<BodyConsumer
> mBodyConsumer
;
182 AbortConsumeBlobBodyControlRunnable(BodyConsumer
* aBodyConsumer
,
183 WorkerPrivate
* aWorkerPrivate
)
184 : MainThreadWorkerControlRunnable("AbortConsumeBlobBodyControlRunnable"),
185 mBodyConsumer(aBodyConsumer
) {
186 MOZ_ASSERT(NS_IsMainThread());
189 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
190 mBodyConsumer
->ContinueConsumeBlobBody(nullptr, true /* shutting down */);
195 class ConsumeBodyDoneObserver final
: public nsIStreamLoaderObserver
,
196 public MutableBlobStorageCallback
{
198 NS_DECL_THREADSAFE_ISUPPORTS
200 ConsumeBodyDoneObserver(BodyConsumer
* aBodyConsumer
,
201 ThreadSafeWorkerRef
* aWorkerRef
)
202 : mBodyConsumer(aBodyConsumer
), mWorkerRef(aWorkerRef
) {}
205 OnStreamComplete(nsIStreamLoader
* aLoader
, nsISupports
* aCtxt
,
206 nsresult aStatus
, uint32_t aResultLength
,
207 const uint8_t* aResult
) override
{
208 MOZ_ASSERT(NS_IsMainThread());
210 // The loading is completed. Let's nullify the pump before continuing the
211 // consuming of the body.
212 mBodyConsumer
->NullifyConsumeBodyPump();
214 uint8_t* nonconstResult
= const_cast<uint8_t*>(aResult
);
218 mBodyConsumer
->ContinueConsumeBody(aStatus
, aResultLength
,
220 // The caller is responsible for data.
221 return NS_SUCCESS_ADOPTED_DATA
;
226 RefPtr
<ContinueConsumeBodyRunnable
> r
= new ContinueConsumeBodyRunnable(
227 mBodyConsumer
, mWorkerRef
->Private(), aStatus
, aResultLength
,
229 if (r
->Dispatch(mWorkerRef
->Private())) {
230 // The caller is responsible for data.
231 return NS_SUCCESS_ADOPTED_DATA
;
235 // The worker is shutting down. Let's use a control runnable to complete the
236 // shutting down procedure.
238 RefPtr
<AbortConsumeBodyControlRunnable
> r
=
239 new AbortConsumeBodyControlRunnable(mBodyConsumer
,
240 mWorkerRef
->Private());
241 if (NS_WARN_IF(!r
->Dispatch(mWorkerRef
->Private()))) {
242 return NS_ERROR_FAILURE
;
245 // We haven't taken ownership of the data.
249 virtual void BlobStoreCompleted(MutableBlobStorage
* aBlobStorage
,
250 BlobImpl
* aBlobImpl
, nsresult aRv
) override
{
252 if (NS_FAILED(aRv
)) {
253 OnStreamComplete(nullptr, nullptr, aRv
, 0, nullptr);
257 // The loading is completed. Let's nullify the pump before continuing the
258 // consuming of the body.
259 mBodyConsumer
->NullifyConsumeBodyPump();
261 mBodyConsumer
->OnBlobResult(aBlobImpl
, mWorkerRef
);
265 ~ConsumeBodyDoneObserver() = default;
267 RefPtr
<BodyConsumer
> mBodyConsumer
;
268 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
271 NS_IMPL_ISUPPORTS(ConsumeBodyDoneObserver
, nsIStreamLoaderObserver
)
275 /* static */ already_AddRefed
<Promise
> BodyConsumer::Create(
276 nsIGlobalObject
* aGlobal
, nsISerialEventTarget
* aMainThreadEventTarget
,
277 nsIInputStream
* aBodyStream
, AbortSignalImpl
* aSignalImpl
,
278 ConsumeType aType
, const nsACString
& aBodyBlobURISpec
,
279 const nsAString
& aBodyLocalPath
, const nsACString
& aBodyMimeType
,
280 const nsACString
& aMixedCaseMimeType
,
281 MutableBlobStorage::MutableBlobStorageType aBlobStorageType
,
283 MOZ_ASSERT(aBodyStream
);
284 MOZ_ASSERT(aMainThreadEventTarget
);
286 RefPtr
<Promise
> promise
= Promise::Create(aGlobal
, aRv
);
291 RefPtr
<BodyConsumer
> consumer
=
292 new BodyConsumer(aMainThreadEventTarget
, aGlobal
, aBodyStream
, promise
,
293 aType
, aBodyBlobURISpec
, aBodyLocalPath
, aBodyMimeType
,
294 aMixedCaseMimeType
, aBlobStorageType
);
296 RefPtr
<ThreadSafeWorkerRef
> workerRef
;
298 if (!NS_IsMainThread()) {
299 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
300 MOZ_ASSERT(workerPrivate
);
302 RefPtr
<StrongWorkerRef
> strongWorkerRef
=
303 StrongWorkerRef::Create(workerPrivate
, "BodyConsumer", [consumer
]() {
304 consumer
->mConsumePromise
= nullptr;
305 consumer
->mBodyConsumed
= true;
306 consumer
->ReleaseObject();
307 consumer
->ShutDownMainThreadConsuming();
309 if (NS_WARN_IF(!strongWorkerRef
)) {
310 aRv
.Throw(NS_ERROR_FAILURE
);
314 workerRef
= new ThreadSafeWorkerRef(strongWorkerRef
);
316 consumer
->GlobalTeardownObserver::BindToOwner(aGlobal
);
317 consumer
->GlobalFreezeObserver::BindToOwner(aGlobal
);
320 nsCOMPtr
<nsIRunnable
> r
= new BeginConsumeBodyRunnable(consumer
, workerRef
);
321 aRv
= aMainThreadEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
322 if (NS_WARN_IF(aRv
.Failed())) {
327 consumer
->Follow(aSignalImpl
);
330 return promise
.forget();
333 void BodyConsumer::ReleaseObject() {
334 AssertIsOnTargetThread();
336 if (NS_IsMainThread()) {
337 GlobalTeardownObserver::DisconnectFromOwner();
338 DisconnectFreezeObserver();
346 BodyConsumer::BodyConsumer(
347 nsISerialEventTarget
* aMainThreadEventTarget
,
348 nsIGlobalObject
* aGlobalObject
, nsIInputStream
* aBodyStream
,
349 Promise
* aPromise
, ConsumeType aType
, const nsACString
& aBodyBlobURISpec
,
350 const nsAString
& aBodyLocalPath
, const nsACString
& aBodyMimeType
,
351 const nsACString
& aMixedCaseMimeType
,
352 MutableBlobStorage::MutableBlobStorageType aBlobStorageType
)
353 : mTargetThread(NS_GetCurrentThread()),
354 mMainThreadEventTarget(aMainThreadEventTarget
),
355 mBodyStream(aBodyStream
),
356 mBlobStorageType(aBlobStorageType
),
357 mBodyMimeType(aBodyMimeType
),
358 mMixedCaseMimeType(aMixedCaseMimeType
),
359 mBodyBlobURISpec(aBodyBlobURISpec
),
360 mBodyLocalPath(aBodyLocalPath
),
361 mGlobal(aGlobalObject
),
363 mConsumePromise(aPromise
),
364 mBodyConsumed(false),
365 mShuttingDown(false) {
366 MOZ_ASSERT(aMainThreadEventTarget
);
367 MOZ_ASSERT(aBodyStream
);
368 MOZ_ASSERT(aPromise
);
371 BodyConsumer::~BodyConsumer() = default;
373 void BodyConsumer::AssertIsOnTargetThread() const {
374 MOZ_ASSERT(NS_GetCurrentThread() == mTargetThread
);
379 class FileCreationHandler final
: public PromiseNativeHandler
{
381 NS_DECL_THREADSAFE_ISUPPORTS
383 static void Create(Promise
* aPromise
, BodyConsumer
* aConsumer
,
384 ThreadSafeWorkerRef
* aWorkerRef
) {
385 AssertIsOnMainThread();
386 MOZ_ASSERT(aPromise
);
388 RefPtr
<FileCreationHandler
> handler
=
389 new FileCreationHandler(aConsumer
, aWorkerRef
);
390 aPromise
->AppendNativeHandler(handler
);
393 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
394 ErrorResult
& aRv
) override
{
395 AssertIsOnMainThread();
397 if (NS_WARN_IF(!aValue
.isObject())) {
398 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
403 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob
, &aValue
.toObject(), blob
)))) {
404 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
408 mConsumer
->OnBlobResult(blob
->Impl(), mWorkerRef
);
411 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
412 ErrorResult
& aRv
) override
{
413 AssertIsOnMainThread();
415 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
419 FileCreationHandler(BodyConsumer
* aConsumer
, ThreadSafeWorkerRef
* aWorkerRef
)
420 : mConsumer(aConsumer
), mWorkerRef(aWorkerRef
) {
421 AssertIsOnMainThread();
422 MOZ_ASSERT(aConsumer
);
425 ~FileCreationHandler() = default;
427 RefPtr
<BodyConsumer
> mConsumer
;
428 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
431 NS_IMPL_ISUPPORTS0(FileCreationHandler
)
435 nsresult
BodyConsumer::GetBodyLocalFile(nsIFile
** aFile
) const {
436 AssertIsOnMainThread();
438 if (!mBodyLocalPath
.Length()) {
442 nsCOMPtr
<nsIFile
> file
;
443 MOZ_TRY(NS_NewLocalFile(mBodyLocalPath
, getter_AddRefs(file
)));
446 MOZ_TRY(file
->Exists(&exists
));
448 return NS_ERROR_FILE_NOT_FOUND
;
452 MOZ_TRY(file
->IsDirectory(&isDir
));
454 return NS_ERROR_FILE_IS_DIRECTORY
;
462 * BeginConsumeBodyMainThread() will automatically reject the consume promise
463 * and clean up on any failures, so there is no need for callers to do so,
464 * reflected in a lack of error return code.
466 void BodyConsumer::BeginConsumeBodyMainThread(ThreadSafeWorkerRef
* aWorkerRef
) {
467 AssertIsOnMainThread();
469 AutoFailConsumeBody
autoReject(this, aWorkerRef
);
472 // We haven't started yet, but we have been terminated. AutoFailConsumeBody
473 // will dispatch a runnable to release resources.
477 if (mConsumeType
== ConsumeType::Blob
) {
480 // If we're trying to consume a blob, and the request was for a blob URI,
481 // then just consume that URI's blob instance.
482 if (!mBodyBlobURISpec
.IsEmpty()) {
483 RefPtr
<BlobImpl
> blobImpl
;
484 rv
= NS_GetBlobForBlobURISpec(mBodyBlobURISpec
, getter_AddRefs(blobImpl
));
485 if (NS_WARN_IF(NS_FAILED(rv
)) || !blobImpl
) {
488 autoReject
.DontFail();
489 DispatchContinueConsumeBlobBody(blobImpl
, aWorkerRef
);
493 // If we're trying to consume a blob, and the request was for a local
494 // file, then generate and return a File blob.
495 nsCOMPtr
<nsIFile
> file
;
496 rv
= GetBodyLocalFile(getter_AddRefs(file
));
497 if (!NS_WARN_IF(NS_FAILED(rv
)) && file
&& !aWorkerRef
) {
498 ChromeFilePropertyBag bag
;
499 CopyUTF8toUTF16(mBodyMimeType
, bag
.mType
);
502 RefPtr
<Promise
> promise
=
503 FileCreatorHelper::CreateFile(mGlobal
, file
, bag
, true, error
);
504 if (NS_WARN_IF(error
.Failed())) {
508 autoReject
.DontFail();
509 FileCreationHandler::Create(promise
, this, aWorkerRef
);
514 nsCOMPtr
<nsIInputStreamPump
> pump
;
516 NS_NewInputStreamPump(getter_AddRefs(pump
), mBodyStream
.forget(), 0, 0,
517 false, mMainThreadEventTarget
);
518 if (NS_WARN_IF(NS_FAILED(rv
))) {
522 RefPtr
<ConsumeBodyDoneObserver
> p
=
523 new ConsumeBodyDoneObserver(this, aWorkerRef
);
525 nsCOMPtr
<nsIStreamListener
> listener
;
526 if (mConsumeType
== ConsumeType::Blob
) {
527 listener
= new MutableBlobStreamListener(mBlobStorageType
, mBodyMimeType
, p
,
528 mMainThreadEventTarget
);
530 nsCOMPtr
<nsIStreamLoader
> loader
;
531 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), p
);
532 if (NS_WARN_IF(NS_FAILED(rv
))) {
539 rv
= pump
->AsyncRead(listener
);
540 if (NS_WARN_IF(NS_FAILED(rv
))) {
544 // Now that everything succeeded, we can assign the pump to a pointer that
545 // stays alive for the lifetime of the BodyConsumer.
546 mConsumeBodyPump
= pump
;
548 // It is ok for retargeting to fail and reads to happen on the main thread.
549 autoReject
.DontFail();
551 // Try to retarget, otherwise fall back to main thread.
552 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(pump
);
554 nsCOMPtr
<nsIEventTarget
> sts
=
555 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
556 RefPtr
<TaskQueue
> queue
=
557 TaskQueue::Create(sts
.forget(), "BodyConsumer STS Delivery Queue");
558 rv
= rr
->RetargetDeliveryTo(queue
);
560 NS_WARNING("Retargeting failed");
566 * OnBlobResult() is called when a blob body is ready to be consumed (when its
567 * network transfer completes in BeginConsumeBodyRunnable or its local File has
568 * been wrapped by FileCreationHandler). The blob is sent to the target thread
569 * and ContinueConsumeBody is called.
571 void BodyConsumer::OnBlobResult(BlobImpl
* aBlobImpl
,
572 ThreadSafeWorkerRef
* aWorkerRef
) {
573 AssertIsOnMainThread();
575 DispatchContinueConsumeBlobBody(aBlobImpl
, aWorkerRef
);
578 void BodyConsumer::DispatchContinueConsumeBlobBody(
579 BlobImpl
* aBlobImpl
, ThreadSafeWorkerRef
* aWorkerRef
) {
580 AssertIsOnMainThread();
585 ContinueConsumeBlobBody(aBlobImpl
);
587 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
594 RefPtr
<ContinueConsumeBlobBodyRunnable
> r
=
595 new ContinueConsumeBlobBodyRunnable(this, aWorkerRef
->Private(),
598 if (r
->Dispatch(aWorkerRef
->Private())) {
602 RefPtr
<ContinueConsumeBodyRunnable
> r
= new ContinueConsumeBodyRunnable(
603 this, aWorkerRef
->Private(), NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
605 if (r
->Dispatch(aWorkerRef
->Private())) {
610 // The worker is shutting down. Let's use a control runnable to complete the
611 // shutting down procedure.
613 RefPtr
<AbortConsumeBlobBodyControlRunnable
> r
=
614 new AbortConsumeBlobBodyControlRunnable(this, aWorkerRef
->Private());
616 Unused
<< NS_WARN_IF(!r
->Dispatch(aWorkerRef
->Private()));
620 * ContinueConsumeBody() is to be called on the target thread whenever the
621 * final result of the fetch is known. The fetch promise is resolved or
622 * rejected based on whether the fetch succeeded, and the body can be
623 * converted into the expected type of JS object.
625 void BodyConsumer::ContinueConsumeBody(nsresult aStatus
, uint32_t aResultLength
,
626 uint8_t* aResult
, bool aShuttingDown
) {
627 AssertIsOnTargetThread();
629 // This makes sure that we free the data correctly.
630 UniquePtr
<uint8_t[], JS::FreePolicy
> resultPtr
{aResult
};
635 mBodyConsumed
= true;
637 MOZ_ASSERT(mConsumePromise
);
638 RefPtr
<Promise
> localPromise
= std::move(mConsumePromise
);
640 RefPtr
<BodyConsumer
> self
= this;
641 auto autoReleaseObject
=
642 mozilla::MakeScopeExit([self
] { self
->ReleaseObject(); });
645 // If shutting down, we don't want to resolve any promise.
649 if (NS_WARN_IF(NS_FAILED(aStatus
))) {
651 // https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes
652 // Decoding errors should reject with a TypeError
653 if (aStatus
== NS_ERROR_INVALID_CONTENT_ENCODING
) {
654 localPromise
->MaybeRejectWithTypeError
<MSG_DOM_DECODING_FAILED
>();
655 } else if (aStatus
== NS_ERROR_DOM_WRONG_TYPE_ERR
) {
656 localPromise
->MaybeRejectWithTypeError
<MSG_FETCH_BODY_WRONG_TYPE
>();
657 } else if (aStatus
== NS_ERROR_NET_PARTIAL_TRANSFER
) {
658 localPromise
->MaybeRejectWithTypeError
<MSG_FETCH_PARTIAL
>();
660 localPromise
->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
666 // Finish successfully consuming body according to type.
667 MOZ_ASSERT(resultPtr
);
670 if (!jsapi
.Init(mGlobal
)) {
671 localPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
675 JSContext
* cx
= jsapi
.cx();
678 switch (mConsumeType
) {
679 case ConsumeType::ArrayBuffer
: {
680 JS::Rooted
<JSObject
*> arrayBuffer(cx
);
681 BodyUtil::ConsumeArrayBuffer(cx
, &arrayBuffer
, aResultLength
,
682 std::move(resultPtr
), error
);
683 if (!error
.Failed()) {
684 JS::Rooted
<JS::Value
> val(cx
, JS::ObjectValue(*arrayBuffer
));
685 localPromise
->MaybeResolve(val
);
689 case ConsumeType::Blob
: {
690 MOZ_CRASH("This should not happen.");
693 case ConsumeType::Bytes
: {
694 JS::Rooted
<JSObject
*> bytes(cx
);
695 BodyUtil::ConsumeBytes(cx
, &bytes
, aResultLength
, std::move(resultPtr
),
697 if (!error
.Failed()) {
698 JS::Rooted
<JS::Value
> val(cx
, JS::ObjectValue(*bytes
));
699 localPromise
->MaybeResolve(val
);
703 case ConsumeType::FormData
: {
705 data
.Adopt(reinterpret_cast<char*>(resultPtr
.release()), aResultLength
);
707 RefPtr
<dom::FormData
> fd
= BodyUtil::ConsumeFormData(
708 mGlobal
, mBodyMimeType
, mMixedCaseMimeType
, data
, error
);
709 if (!error
.Failed()) {
710 localPromise
->MaybeResolve(fd
);
714 case ConsumeType::Text
:
715 // fall through handles early exit.
716 case ConsumeType::JSON
: {
719 BodyUtil::ConsumeText(aResultLength
, resultPtr
.get(), decoded
))) {
720 if (mConsumeType
== ConsumeType::Text
) {
721 localPromise
->MaybeResolve(decoded
);
723 JS::Rooted
<JS::Value
> json(cx
);
724 BodyUtil::ConsumeJson(cx
, &json
, decoded
, error
);
725 if (!error
.Failed()) {
726 localPromise
->MaybeResolve(json
);
733 MOZ_ASSERT_UNREACHABLE("Unexpected consume body type");
736 error
.WouldReportJSException();
737 if (error
.Failed()) {
738 localPromise
->MaybeReject(std::move(error
));
742 void BodyConsumer::ContinueConsumeBlobBody(BlobImpl
* aBlobImpl
,
743 bool aShuttingDown
) {
744 AssertIsOnTargetThread();
745 MOZ_ASSERT(mConsumeType
== ConsumeType::Blob
);
750 mBodyConsumed
= true;
752 MOZ_ASSERT(mConsumePromise
);
753 RefPtr
<Promise
> localPromise
= std::move(mConsumePromise
);
755 if (!aShuttingDown
) {
756 RefPtr
<dom::Blob
> blob
= dom::Blob::Create(mGlobal
, aBlobImpl
);
757 if (NS_WARN_IF(!blob
)) {
758 localPromise
->MaybeReject(NS_ERROR_FAILURE
);
762 localPromise
->MaybeResolve(blob
);
768 void BodyConsumer::ShutDownMainThreadConsuming() {
769 if (!NS_IsMainThread()) {
770 RefPtr
<BodyConsumer
> self
= this;
772 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
773 "BodyConsumer::ShutDownMainThreadConsuming",
774 [self
]() { self
->ShutDownMainThreadConsuming(); });
776 mMainThreadEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
780 // We need this because maybe, mConsumeBodyPump has not been created yet. We
781 // must be sure that we don't try to do it.
782 mShuttingDown
= true;
784 if (mConsumeBodyPump
) {
785 mConsumeBodyPump
->CancelWithReason(
786 NS_BINDING_ABORTED
, "BodyConsumer::ShutDownMainThreadConsuming"_ns
);
787 mConsumeBodyPump
= nullptr;
791 void BodyConsumer::MaybeAbortConsumption() {
792 AssertIsOnMainThread();
794 ContinueConsumeBody(NS_BINDING_ABORTED
, 0, nullptr);
797 void BodyConsumer::RunAbortAlgorithm() {
798 AssertIsOnTargetThread();
799 ShutDownMainThreadConsuming();
800 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
803 NS_IMPL_ADDREF(BodyConsumer
)
804 NS_IMPL_RELEASE(BodyConsumer
)
805 NS_INTERFACE_TABLE_HEAD(BodyConsumer
)
806 NS_INTERFACE_TABLE_BEGIN
807 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(BodyConsumer
, nsISupports
,
808 GlobalTeardownObserver
)
809 NS_INTERFACE_TABLE_END
810 NS_INTERFACE_TABLE_TAIL
812 } // namespace mozilla::dom