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(aWorkerPrivate
, "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(aWorkerPrivate
),
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()) {
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(aWorkerPrivate
,
163 "ContinueConsumeBlobBodyRunnable"),
164 mBodyConsumer(aBodyConsumer
),
165 mBlobImpl(aBlobImpl
) {
166 MOZ_ASSERT(NS_IsMainThread());
167 MOZ_ASSERT(mBlobImpl
);
170 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
171 mBodyConsumer
->ContinueConsumeBlobBody(mBlobImpl
);
176 // ControlRunnable used to complete the releasing of resources on the worker
177 // thread when already shutting down.
178 class AbortConsumeBlobBodyControlRunnable final
179 : public MainThreadWorkerControlRunnable
{
180 RefPtr
<BodyConsumer
> mBodyConsumer
;
183 AbortConsumeBlobBodyControlRunnable(BodyConsumer
* aBodyConsumer
,
184 WorkerPrivate
* aWorkerPrivate
)
185 : MainThreadWorkerControlRunnable(aWorkerPrivate
),
186 mBodyConsumer(aBodyConsumer
) {
187 MOZ_ASSERT(NS_IsMainThread());
190 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
191 mBodyConsumer
->ContinueConsumeBlobBody(nullptr, true /* shutting down */);
196 class ConsumeBodyDoneObserver final
: public nsIStreamLoaderObserver
,
197 public MutableBlobStorageCallback
{
199 NS_DECL_THREADSAFE_ISUPPORTS
201 ConsumeBodyDoneObserver(BodyConsumer
* aBodyConsumer
,
202 ThreadSafeWorkerRef
* aWorkerRef
)
203 : mBodyConsumer(aBodyConsumer
), mWorkerRef(aWorkerRef
) {}
206 OnStreamComplete(nsIStreamLoader
* aLoader
, nsISupports
* aCtxt
,
207 nsresult aStatus
, uint32_t aResultLength
,
208 const uint8_t* aResult
) override
{
209 MOZ_ASSERT(NS_IsMainThread());
211 // The loading is completed. Let's nullify the pump before continuing the
212 // consuming of the body.
213 mBodyConsumer
->NullifyConsumeBodyPump();
215 uint8_t* nonconstResult
= const_cast<uint8_t*>(aResult
);
219 mBodyConsumer
->ContinueConsumeBody(aStatus
, aResultLength
,
221 // The caller is responsible for data.
222 return NS_SUCCESS_ADOPTED_DATA
;
227 RefPtr
<ContinueConsumeBodyRunnable
> r
= new ContinueConsumeBodyRunnable(
228 mBodyConsumer
, mWorkerRef
->Private(), aStatus
, aResultLength
,
231 // The caller is responsible for data.
232 return NS_SUCCESS_ADOPTED_DATA
;
236 // The worker is shutting down. Let's use a control runnable to complete the
237 // shutting down procedure.
239 RefPtr
<AbortConsumeBodyControlRunnable
> r
=
240 new AbortConsumeBodyControlRunnable(mBodyConsumer
,
241 mWorkerRef
->Private());
242 if (NS_WARN_IF(!r
->Dispatch())) {
243 return NS_ERROR_FAILURE
;
246 // We haven't taken ownership of the data.
250 virtual void BlobStoreCompleted(MutableBlobStorage
* aBlobStorage
,
251 BlobImpl
* aBlobImpl
, nsresult aRv
) override
{
253 if (NS_FAILED(aRv
)) {
254 OnStreamComplete(nullptr, nullptr, aRv
, 0, nullptr);
258 // The loading is completed. Let's nullify the pump before continuing the
259 // consuming of the body.
260 mBodyConsumer
->NullifyConsumeBodyPump();
262 mBodyConsumer
->OnBlobResult(aBlobImpl
, mWorkerRef
);
266 ~ConsumeBodyDoneObserver() = default;
268 RefPtr
<BodyConsumer
> mBodyConsumer
;
269 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
272 NS_IMPL_ISUPPORTS(ConsumeBodyDoneObserver
, nsIStreamLoaderObserver
)
276 /* static */ already_AddRefed
<Promise
> BodyConsumer::Create(
277 nsIGlobalObject
* aGlobal
, nsISerialEventTarget
* aMainThreadEventTarget
,
278 nsIInputStream
* aBodyStream
, AbortSignalImpl
* aSignalImpl
,
279 ConsumeType aType
, const nsACString
& aBodyBlobURISpec
,
280 const nsAString
& aBodyLocalPath
, const nsACString
& aBodyMimeType
,
281 const nsACString
& aMixedCaseMimeType
,
282 MutableBlobStorage::MutableBlobStorageType aBlobStorageType
,
284 MOZ_ASSERT(aBodyStream
);
285 MOZ_ASSERT(aMainThreadEventTarget
);
287 RefPtr
<Promise
> promise
= Promise::Create(aGlobal
, aRv
);
292 RefPtr
<BodyConsumer
> consumer
=
293 new BodyConsumer(aMainThreadEventTarget
, aGlobal
, aBodyStream
, promise
,
294 aType
, aBodyBlobURISpec
, aBodyLocalPath
, aBodyMimeType
,
295 aMixedCaseMimeType
, aBlobStorageType
);
297 RefPtr
<ThreadSafeWorkerRef
> workerRef
;
299 if (!NS_IsMainThread()) {
300 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
301 MOZ_ASSERT(workerPrivate
);
303 RefPtr
<StrongWorkerRef
> strongWorkerRef
=
304 StrongWorkerRef::Create(workerPrivate
, "BodyConsumer", [consumer
]() {
305 consumer
->mConsumePromise
= nullptr;
306 consumer
->mBodyConsumed
= true;
307 consumer
->ReleaseObject();
308 consumer
->ShutDownMainThreadConsuming();
310 if (NS_WARN_IF(!strongWorkerRef
)) {
311 aRv
.Throw(NS_ERROR_FAILURE
);
315 workerRef
= new ThreadSafeWorkerRef(strongWorkerRef
);
317 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
318 if (NS_WARN_IF(!os
)) {
319 aRv
.Throw(NS_ERROR_FAILURE
);
323 aRv
= os
->AddObserver(consumer
, DOM_WINDOW_DESTROYED_TOPIC
, true);
324 if (NS_WARN_IF(aRv
.Failed())) {
328 aRv
= os
->AddObserver(consumer
, DOM_WINDOW_FROZEN_TOPIC
, true);
329 if (NS_WARN_IF(aRv
.Failed())) {
334 nsCOMPtr
<nsIRunnable
> r
= new BeginConsumeBodyRunnable(consumer
, workerRef
);
335 aRv
= aMainThreadEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
336 if (NS_WARN_IF(aRv
.Failed())) {
341 consumer
->Follow(aSignalImpl
);
344 return promise
.forget();
347 void BodyConsumer::ReleaseObject() {
348 AssertIsOnTargetThread();
350 if (NS_IsMainThread()) {
351 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
353 os
->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC
);
354 os
->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC
);
363 BodyConsumer::BodyConsumer(
364 nsISerialEventTarget
* aMainThreadEventTarget
,
365 nsIGlobalObject
* aGlobalObject
, nsIInputStream
* aBodyStream
,
366 Promise
* aPromise
, ConsumeType aType
, const nsACString
& aBodyBlobURISpec
,
367 const nsAString
& aBodyLocalPath
, const nsACString
& aBodyMimeType
,
368 const nsACString
& aMixedCaseMimeType
,
369 MutableBlobStorage::MutableBlobStorageType aBlobStorageType
)
370 : mTargetThread(NS_GetCurrentThread()),
371 mMainThreadEventTarget(aMainThreadEventTarget
),
372 mBodyStream(aBodyStream
),
373 mBlobStorageType(aBlobStorageType
),
374 mBodyMimeType(aBodyMimeType
),
375 mMixedCaseMimeType(aMixedCaseMimeType
),
376 mBodyBlobURISpec(aBodyBlobURISpec
),
377 mBodyLocalPath(aBodyLocalPath
),
378 mGlobal(aGlobalObject
),
380 mConsumePromise(aPromise
),
381 mBodyConsumed(false),
382 mShuttingDown(false) {
383 MOZ_ASSERT(aMainThreadEventTarget
);
384 MOZ_ASSERT(aBodyStream
);
385 MOZ_ASSERT(aPromise
);
388 BodyConsumer::~BodyConsumer() = default;
390 void BodyConsumer::AssertIsOnTargetThread() const {
391 MOZ_ASSERT(NS_GetCurrentThread() == mTargetThread
);
396 class FileCreationHandler final
: public PromiseNativeHandler
{
398 NS_DECL_THREADSAFE_ISUPPORTS
400 static void Create(Promise
* aPromise
, BodyConsumer
* aConsumer
,
401 ThreadSafeWorkerRef
* aWorkerRef
) {
402 AssertIsOnMainThread();
403 MOZ_ASSERT(aPromise
);
405 RefPtr
<FileCreationHandler
> handler
=
406 new FileCreationHandler(aConsumer
, aWorkerRef
);
407 aPromise
->AppendNativeHandler(handler
);
410 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
411 ErrorResult
& aRv
) override
{
412 AssertIsOnMainThread();
414 if (NS_WARN_IF(!aValue
.isObject())) {
415 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
420 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob
, &aValue
.toObject(), blob
)))) {
421 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
425 mConsumer
->OnBlobResult(blob
->Impl(), mWorkerRef
);
428 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
429 ErrorResult
& aRv
) override
{
430 AssertIsOnMainThread();
432 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
436 FileCreationHandler(BodyConsumer
* aConsumer
, ThreadSafeWorkerRef
* aWorkerRef
)
437 : mConsumer(aConsumer
), mWorkerRef(aWorkerRef
) {
438 AssertIsOnMainThread();
439 MOZ_ASSERT(aConsumer
);
442 ~FileCreationHandler() = default;
444 RefPtr
<BodyConsumer
> mConsumer
;
445 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
448 NS_IMPL_ISUPPORTS0(FileCreationHandler
)
452 nsresult
BodyConsumer::GetBodyLocalFile(nsIFile
** aFile
) const {
453 AssertIsOnMainThread();
455 if (!mBodyLocalPath
.Length()) {
460 nsCOMPtr
<nsIFile
> file
= do_CreateInstance("@mozilla.org/file/local;1", &rv
);
465 rv
= file
->InitWithPath(mBodyLocalPath
);
466 NS_ENSURE_SUCCESS(rv
, rv
);
469 rv
= file
->Exists(&exists
);
470 NS_ENSURE_SUCCESS(rv
, rv
);
472 return NS_ERROR_FILE_NOT_FOUND
;
476 rv
= file
->IsDirectory(&isDir
);
477 NS_ENSURE_SUCCESS(rv
, rv
);
479 return NS_ERROR_FILE_IS_DIRECTORY
;
487 * BeginConsumeBodyMainThread() will automatically reject the consume promise
488 * and clean up on any failures, so there is no need for callers to do so,
489 * reflected in a lack of error return code.
491 void BodyConsumer::BeginConsumeBodyMainThread(ThreadSafeWorkerRef
* aWorkerRef
) {
492 AssertIsOnMainThread();
494 AutoFailConsumeBody
autoReject(this, aWorkerRef
);
497 // We haven't started yet, but we have been terminated. AutoFailConsumeBody
498 // will dispatch a runnable to release resources.
502 if (mConsumeType
== CONSUME_BLOB
) {
505 // If we're trying to consume a blob, and the request was for a blob URI,
506 // then just consume that URI's blob instance.
507 if (!mBodyBlobURISpec
.IsEmpty()) {
508 RefPtr
<BlobImpl
> blobImpl
;
509 rv
= NS_GetBlobForBlobURISpec(mBodyBlobURISpec
, getter_AddRefs(blobImpl
));
510 if (NS_WARN_IF(NS_FAILED(rv
)) || !blobImpl
) {
513 autoReject
.DontFail();
514 DispatchContinueConsumeBlobBody(blobImpl
, aWorkerRef
);
518 // If we're trying to consume a blob, and the request was for a local
519 // file, then generate and return a File blob.
520 nsCOMPtr
<nsIFile
> file
;
521 rv
= GetBodyLocalFile(getter_AddRefs(file
));
522 if (!NS_WARN_IF(NS_FAILED(rv
)) && file
&& !aWorkerRef
) {
523 ChromeFilePropertyBag bag
;
524 CopyUTF8toUTF16(mBodyMimeType
, bag
.mType
);
527 RefPtr
<Promise
> promise
=
528 FileCreatorHelper::CreateFile(mGlobal
, file
, bag
, true, error
);
529 if (NS_WARN_IF(error
.Failed())) {
533 autoReject
.DontFail();
534 FileCreationHandler::Create(promise
, this, aWorkerRef
);
539 nsCOMPtr
<nsIInputStreamPump
> pump
;
541 NS_NewInputStreamPump(getter_AddRefs(pump
), mBodyStream
.forget(), 0, 0,
542 false, mMainThreadEventTarget
);
543 if (NS_WARN_IF(NS_FAILED(rv
))) {
547 RefPtr
<ConsumeBodyDoneObserver
> p
=
548 new ConsumeBodyDoneObserver(this, aWorkerRef
);
550 nsCOMPtr
<nsIStreamListener
> listener
;
551 if (mConsumeType
== CONSUME_BLOB
) {
552 listener
= new MutableBlobStreamListener(mBlobStorageType
, mBodyMimeType
, p
,
553 mMainThreadEventTarget
);
555 nsCOMPtr
<nsIStreamLoader
> loader
;
556 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), p
);
557 if (NS_WARN_IF(NS_FAILED(rv
))) {
564 rv
= pump
->AsyncRead(listener
);
565 if (NS_WARN_IF(NS_FAILED(rv
))) {
569 // Now that everything succeeded, we can assign the pump to a pointer that
570 // stays alive for the lifetime of the BodyConsumer.
571 mConsumeBodyPump
= pump
;
573 // It is ok for retargeting to fail and reads to happen on the main thread.
574 autoReject
.DontFail();
576 // Try to retarget, otherwise fall back to main thread.
577 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(pump
);
579 nsCOMPtr
<nsIEventTarget
> sts
=
580 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
581 RefPtr
<TaskQueue
> queue
=
582 TaskQueue::Create(sts
.forget(), "BodyConsumer STS Delivery Queue");
583 rv
= rr
->RetargetDeliveryTo(queue
);
585 NS_WARNING("Retargeting failed");
591 * OnBlobResult() is called when a blob body is ready to be consumed (when its
592 * network transfer completes in BeginConsumeBodyRunnable or its local File has
593 * been wrapped by FileCreationHandler). The blob is sent to the target thread
594 * and ContinueConsumeBody is called.
596 void BodyConsumer::OnBlobResult(BlobImpl
* aBlobImpl
,
597 ThreadSafeWorkerRef
* aWorkerRef
) {
598 AssertIsOnMainThread();
600 DispatchContinueConsumeBlobBody(aBlobImpl
, aWorkerRef
);
603 void BodyConsumer::DispatchContinueConsumeBlobBody(
604 BlobImpl
* aBlobImpl
, ThreadSafeWorkerRef
* aWorkerRef
) {
605 AssertIsOnMainThread();
610 ContinueConsumeBlobBody(aBlobImpl
);
612 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
619 RefPtr
<ContinueConsumeBlobBodyRunnable
> r
=
620 new ContinueConsumeBlobBodyRunnable(this, aWorkerRef
->Private(),
627 RefPtr
<ContinueConsumeBodyRunnable
> r
= new ContinueConsumeBodyRunnable(
628 this, aWorkerRef
->Private(), NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
635 // The worker is shutting down. Let's use a control runnable to complete the
636 // shutting down procedure.
638 RefPtr
<AbortConsumeBlobBodyControlRunnable
> r
=
639 new AbortConsumeBlobBodyControlRunnable(this, aWorkerRef
->Private());
641 Unused
<< NS_WARN_IF(!r
->Dispatch());
645 * ContinueConsumeBody() is to be called on the target thread whenever the
646 * final result of the fetch is known. The fetch promise is resolved or
647 * rejected based on whether the fetch succeeded, and the body can be
648 * converted into the expected type of JS object.
650 void BodyConsumer::ContinueConsumeBody(nsresult aStatus
, uint32_t aResultLength
,
651 uint8_t* aResult
, bool aShuttingDown
) {
652 AssertIsOnTargetThread();
654 // This makes sure that we free the data correctly.
655 UniquePtr
<uint8_t[], JS::FreePolicy
> resultPtr
{aResult
};
660 mBodyConsumed
= true;
662 MOZ_ASSERT(mConsumePromise
);
663 RefPtr
<Promise
> localPromise
= std::move(mConsumePromise
);
665 RefPtr
<BodyConsumer
> self
= this;
666 auto autoReleaseObject
=
667 mozilla::MakeScopeExit([self
] { self
->ReleaseObject(); });
670 // If shutting down, we don't want to resolve any promise.
674 if (NS_WARN_IF(NS_FAILED(aStatus
))) {
676 // https://fetch.spec.whatwg.org/#concept-read-all-bytes-from-readablestream
677 // Decoding errors should reject with a TypeError
678 if (aStatus
== NS_ERROR_INVALID_CONTENT_ENCODING
) {
679 localPromise
->MaybeRejectWithTypeError
<MSG_DOM_DECODING_FAILED
>();
680 } else if (aStatus
== NS_ERROR_DOM_WRONG_TYPE_ERR
) {
681 localPromise
->MaybeRejectWithTypeError
<MSG_FETCH_BODY_WRONG_TYPE
>();
683 localPromise
->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
687 // Don't warn here since we warned above.
688 if (NS_FAILED(aStatus
)) {
692 // Finish successfully consuming body according to type.
693 MOZ_ASSERT(resultPtr
);
696 if (!jsapi
.Init(mGlobal
)) {
697 localPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
701 JSContext
* cx
= jsapi
.cx();
704 switch (mConsumeType
) {
705 case CONSUME_ARRAYBUFFER
: {
706 JS::Rooted
<JSObject
*> arrayBuffer(cx
);
707 BodyUtil::ConsumeArrayBuffer(cx
, &arrayBuffer
, aResultLength
,
708 std::move(resultPtr
), error
);
710 if (!error
.Failed()) {
711 JS::Rooted
<JS::Value
> val(cx
);
712 val
.setObjectOrNull(arrayBuffer
);
714 localPromise
->MaybeResolve(val
);
719 MOZ_CRASH("This should not happen.");
722 case CONSUME_FORMDATA
: {
724 data
.Adopt(reinterpret_cast<char*>(resultPtr
.release()), aResultLength
);
726 RefPtr
<dom::FormData
> fd
= BodyUtil::ConsumeFormData(
727 mGlobal
, mBodyMimeType
, mMixedCaseMimeType
, data
, error
);
728 if (!error
.Failed()) {
729 localPromise
->MaybeResolve(fd
);
734 // fall through handles early exit.
738 BodyUtil::ConsumeText(aResultLength
, resultPtr
.get(), decoded
))) {
739 if (mConsumeType
== CONSUME_TEXT
) {
740 localPromise
->MaybeResolve(decoded
);
742 JS::Rooted
<JS::Value
> json(cx
);
743 BodyUtil::ConsumeJson(cx
, &json
, decoded
, error
);
744 if (!error
.Failed()) {
745 localPromise
->MaybeResolve(json
);
752 MOZ_ASSERT_UNREACHABLE("Unexpected consume body type");
755 error
.WouldReportJSException();
756 if (error
.Failed()) {
757 localPromise
->MaybeReject(std::move(error
));
761 void BodyConsumer::ContinueConsumeBlobBody(BlobImpl
* aBlobImpl
,
762 bool aShuttingDown
) {
763 AssertIsOnTargetThread();
764 MOZ_ASSERT(mConsumeType
== CONSUME_BLOB
);
769 mBodyConsumed
= true;
771 MOZ_ASSERT(mConsumePromise
);
772 RefPtr
<Promise
> localPromise
= std::move(mConsumePromise
);
774 if (!aShuttingDown
) {
775 RefPtr
<dom::Blob
> blob
= dom::Blob::Create(mGlobal
, aBlobImpl
);
776 if (NS_WARN_IF(!blob
)) {
777 localPromise
->MaybeReject(NS_ERROR_FAILURE
);
781 localPromise
->MaybeResolve(blob
);
787 void BodyConsumer::ShutDownMainThreadConsuming() {
788 if (!NS_IsMainThread()) {
789 RefPtr
<BodyConsumer
> self
= this;
791 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
792 "BodyConsumer::ShutDownMainThreadConsuming",
793 [self
]() { self
->ShutDownMainThreadConsuming(); });
795 mMainThreadEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
799 // We need this because maybe, mConsumeBodyPump has not been created yet. We
800 // must be sure that we don't try to do it.
801 mShuttingDown
= true;
803 if (mConsumeBodyPump
) {
804 mConsumeBodyPump
->CancelWithReason(
805 NS_BINDING_ABORTED
, "BodyConsumer::ShutDownMainThreadConsuming"_ns
);
806 mConsumeBodyPump
= nullptr;
810 NS_IMETHODIMP
BodyConsumer::Observe(nsISupports
* aSubject
, const char* aTopic
,
811 const char16_t
* aData
) {
812 AssertIsOnMainThread();
814 MOZ_ASSERT((strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) ||
815 (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0));
817 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(mGlobal
);
818 if (SameCOMIdentity(aSubject
, window
)) {
819 ContinueConsumeBody(NS_BINDING_ABORTED
, 0, nullptr);
825 void BodyConsumer::RunAbortAlgorithm() {
826 AssertIsOnTargetThread();
827 ShutDownMainThreadConsuming();
828 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
831 NS_IMPL_ISUPPORTS(BodyConsumer
, nsIObserver
, nsISupportsWeakReference
)
833 } // namespace mozilla::dom