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
),
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 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(aWorkerPrivate
),
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
,
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())) {
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 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
317 if (NS_WARN_IF(!os
)) {
318 aRv
.Throw(NS_ERROR_FAILURE
);
322 aRv
= os
->AddObserver(consumer
, DOM_WINDOW_DESTROYED_TOPIC
, true);
323 if (NS_WARN_IF(aRv
.Failed())) {
327 aRv
= os
->AddObserver(consumer
, DOM_WINDOW_FROZEN_TOPIC
, true);
328 if (NS_WARN_IF(aRv
.Failed())) {
333 nsCOMPtr
<nsIRunnable
> r
= new BeginConsumeBodyRunnable(consumer
, workerRef
);
334 aRv
= aMainThreadEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
335 if (NS_WARN_IF(aRv
.Failed())) {
340 consumer
->Follow(aSignalImpl
);
343 return promise
.forget();
346 void BodyConsumer::ReleaseObject() {
347 AssertIsOnTargetThread();
349 if (NS_IsMainThread()) {
350 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
352 os
->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC
);
353 os
->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC
);
362 BodyConsumer::BodyConsumer(
363 nsISerialEventTarget
* aMainThreadEventTarget
,
364 nsIGlobalObject
* aGlobalObject
, nsIInputStream
* aBodyStream
,
365 Promise
* aPromise
, ConsumeType aType
, const nsACString
& aBodyBlobURISpec
,
366 const nsAString
& aBodyLocalPath
, const nsACString
& aBodyMimeType
,
367 const nsACString
& aMixedCaseMimeType
,
368 MutableBlobStorage::MutableBlobStorageType aBlobStorageType
)
369 : mTargetThread(NS_GetCurrentThread()),
370 mMainThreadEventTarget(aMainThreadEventTarget
),
371 mBodyStream(aBodyStream
),
372 mBlobStorageType(aBlobStorageType
),
373 mBodyMimeType(aBodyMimeType
),
374 mMixedCaseMimeType(aMixedCaseMimeType
),
375 mBodyBlobURISpec(aBodyBlobURISpec
),
376 mBodyLocalPath(aBodyLocalPath
),
377 mGlobal(aGlobalObject
),
379 mConsumePromise(aPromise
),
380 mBodyConsumed(false),
381 mShuttingDown(false) {
382 MOZ_ASSERT(aMainThreadEventTarget
);
383 MOZ_ASSERT(aBodyStream
);
384 MOZ_ASSERT(aPromise
);
387 BodyConsumer::~BodyConsumer() = default;
389 void BodyConsumer::AssertIsOnTargetThread() const {
390 MOZ_ASSERT(NS_GetCurrentThread() == mTargetThread
);
395 class FileCreationHandler final
: public PromiseNativeHandler
{
397 NS_DECL_THREADSAFE_ISUPPORTS
399 static void Create(Promise
* aPromise
, BodyConsumer
* aConsumer
,
400 ThreadSafeWorkerRef
* aWorkerRef
) {
401 AssertIsOnMainThread();
402 MOZ_ASSERT(aPromise
);
404 RefPtr
<FileCreationHandler
> handler
=
405 new FileCreationHandler(aConsumer
, aWorkerRef
);
406 aPromise
->AppendNativeHandler(handler
);
409 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
410 ErrorResult
& aRv
) override
{
411 AssertIsOnMainThread();
413 if (NS_WARN_IF(!aValue
.isObject())) {
414 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
419 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob
, &aValue
.toObject(), blob
)))) {
420 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
424 mConsumer
->OnBlobResult(blob
->Impl(), mWorkerRef
);
427 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
428 ErrorResult
& aRv
) override
{
429 AssertIsOnMainThread();
431 mConsumer
->OnBlobResult(nullptr, mWorkerRef
);
435 FileCreationHandler(BodyConsumer
* aConsumer
, ThreadSafeWorkerRef
* aWorkerRef
)
436 : mConsumer(aConsumer
), mWorkerRef(aWorkerRef
) {
437 AssertIsOnMainThread();
438 MOZ_ASSERT(aConsumer
);
441 ~FileCreationHandler() = default;
443 RefPtr
<BodyConsumer
> mConsumer
;
444 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
447 NS_IMPL_ISUPPORTS0(FileCreationHandler
)
451 nsresult
BodyConsumer::GetBodyLocalFile(nsIFile
** aFile
) const {
452 AssertIsOnMainThread();
454 if (!mBodyLocalPath
.Length()) {
459 nsCOMPtr
<nsIFile
> file
= do_CreateInstance("@mozilla.org/file/local;1", &rv
);
464 rv
= file
->InitWithPath(mBodyLocalPath
);
465 NS_ENSURE_SUCCESS(rv
, rv
);
468 rv
= file
->Exists(&exists
);
469 NS_ENSURE_SUCCESS(rv
, rv
);
471 return NS_ERROR_FILE_NOT_FOUND
;
475 rv
= file
->IsDirectory(&isDir
);
476 NS_ENSURE_SUCCESS(rv
, rv
);
478 return NS_ERROR_FILE_IS_DIRECTORY
;
486 * BeginConsumeBodyMainThread() will automatically reject the consume promise
487 * and clean up on any failures, so there is no need for callers to do so,
488 * reflected in a lack of error return code.
490 void BodyConsumer::BeginConsumeBodyMainThread(ThreadSafeWorkerRef
* aWorkerRef
) {
491 AssertIsOnMainThread();
493 AutoFailConsumeBody
autoReject(this, aWorkerRef
);
496 // We haven't started yet, but we have been terminated. AutoFailConsumeBody
497 // will dispatch a runnable to release resources.
501 if (mConsumeType
== CONSUME_BLOB
) {
504 // If we're trying to consume a blob, and the request was for a blob URI,
505 // then just consume that URI's blob instance.
506 if (!mBodyBlobURISpec
.IsEmpty()) {
507 RefPtr
<BlobImpl
> blobImpl
;
508 rv
= NS_GetBlobForBlobURISpec(mBodyBlobURISpec
, getter_AddRefs(blobImpl
));
509 if (NS_WARN_IF(NS_FAILED(rv
)) || !blobImpl
) {
512 autoReject
.DontFail();
513 DispatchContinueConsumeBlobBody(blobImpl
, aWorkerRef
);
517 // If we're trying to consume a blob, and the request was for a local
518 // file, then generate and return a File blob.
519 nsCOMPtr
<nsIFile
> file
;
520 rv
= GetBodyLocalFile(getter_AddRefs(file
));
521 if (!NS_WARN_IF(NS_FAILED(rv
)) && file
&& !aWorkerRef
) {
522 ChromeFilePropertyBag bag
;
523 CopyUTF8toUTF16(mBodyMimeType
, bag
.mType
);
526 RefPtr
<Promise
> promise
=
527 FileCreatorHelper::CreateFile(mGlobal
, file
, bag
, true, error
);
528 if (NS_WARN_IF(error
.Failed())) {
532 autoReject
.DontFail();
533 FileCreationHandler::Create(promise
, this, aWorkerRef
);
538 nsCOMPtr
<nsIInputStreamPump
> pump
;
540 NS_NewInputStreamPump(getter_AddRefs(pump
), mBodyStream
.forget(), 0, 0,
541 false, mMainThreadEventTarget
);
542 if (NS_WARN_IF(NS_FAILED(rv
))) {
546 RefPtr
<ConsumeBodyDoneObserver
> p
=
547 new ConsumeBodyDoneObserver(this, aWorkerRef
);
549 nsCOMPtr
<nsIStreamListener
> listener
;
550 if (mConsumeType
== CONSUME_BLOB
) {
551 listener
= new MutableBlobStreamListener(mBlobStorageType
, mBodyMimeType
, p
,
552 mMainThreadEventTarget
);
554 nsCOMPtr
<nsIStreamLoader
> loader
;
555 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), p
);
556 if (NS_WARN_IF(NS_FAILED(rv
))) {
563 rv
= pump
->AsyncRead(listener
);
564 if (NS_WARN_IF(NS_FAILED(rv
))) {
568 // Now that everything succeeded, we can assign the pump to a pointer that
569 // stays alive for the lifetime of the BodyConsumer.
570 mConsumeBodyPump
= pump
;
572 // It is ok for retargeting to fail and reads to happen on the main thread.
573 autoReject
.DontFail();
575 // Try to retarget, otherwise fall back to main thread.
576 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(pump
);
578 nsCOMPtr
<nsIEventTarget
> sts
=
579 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
580 RefPtr
<TaskQueue
> queue
=
581 TaskQueue::Create(sts
.forget(), "BodyConsumer STS Delivery Queue");
582 rv
= rr
->RetargetDeliveryTo(queue
);
584 NS_WARNING("Retargeting failed");
590 * OnBlobResult() is called when a blob body is ready to be consumed (when its
591 * network transfer completes in BeginConsumeBodyRunnable or its local File has
592 * been wrapped by FileCreationHandler). The blob is sent to the target thread
593 * and ContinueConsumeBody is called.
595 void BodyConsumer::OnBlobResult(BlobImpl
* aBlobImpl
,
596 ThreadSafeWorkerRef
* aWorkerRef
) {
597 AssertIsOnMainThread();
599 DispatchContinueConsumeBlobBody(aBlobImpl
, aWorkerRef
);
602 void BodyConsumer::DispatchContinueConsumeBlobBody(
603 BlobImpl
* aBlobImpl
, ThreadSafeWorkerRef
* aWorkerRef
) {
604 AssertIsOnMainThread();
609 ContinueConsumeBlobBody(aBlobImpl
);
611 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
618 RefPtr
<ContinueConsumeBlobBodyRunnable
> r
=
619 new ContinueConsumeBlobBodyRunnable(this, aWorkerRef
->Private(),
626 RefPtr
<ContinueConsumeBodyRunnable
> r
= new ContinueConsumeBodyRunnable(
627 this, aWorkerRef
->Private(), NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
634 // The worker is shutting down. Let's use a control runnable to complete the
635 // shutting down procedure.
637 RefPtr
<AbortConsumeBlobBodyControlRunnable
> r
=
638 new AbortConsumeBlobBodyControlRunnable(this, aWorkerRef
->Private());
640 Unused
<< NS_WARN_IF(!r
->Dispatch());
644 * ContinueConsumeBody() is to be called on the target thread whenever the
645 * final result of the fetch is known. The fetch promise is resolved or
646 * rejected based on whether the fetch succeeded, and the body can be
647 * converted into the expected type of JS object.
649 void BodyConsumer::ContinueConsumeBody(nsresult aStatus
, uint32_t aResultLength
,
650 uint8_t* aResult
, bool aShuttingDown
) {
651 AssertIsOnTargetThread();
653 // This makes sure that we free the data correctly.
654 UniquePtr
<uint8_t[], JS::FreePolicy
> resultPtr
{aResult
};
659 mBodyConsumed
= true;
661 MOZ_ASSERT(mConsumePromise
);
662 RefPtr
<Promise
> localPromise
= std::move(mConsumePromise
);
664 RefPtr
<BodyConsumer
> self
= this;
665 auto autoReleaseObject
=
666 mozilla::MakeScopeExit([self
] { self
->ReleaseObject(); });
669 // If shutting down, we don't want to resolve any promise.
673 if (NS_WARN_IF(NS_FAILED(aStatus
))) {
675 // https://fetch.spec.whatwg.org/#concept-read-all-bytes-from-readablestream
676 // Decoding errors should reject with a TypeError
677 if (aStatus
== NS_ERROR_INVALID_CONTENT_ENCODING
) {
678 localPromise
->MaybeRejectWithTypeError
<MSG_DOM_DECODING_FAILED
>();
679 } else if (aStatus
== NS_ERROR_DOM_WRONG_TYPE_ERR
) {
680 localPromise
->MaybeRejectWithTypeError
<MSG_FETCH_BODY_WRONG_TYPE
>();
682 localPromise
->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
686 // Don't warn here since we warned above.
687 if (NS_FAILED(aStatus
)) {
691 // Finish successfully consuming body according to type.
692 MOZ_ASSERT(resultPtr
);
695 if (!jsapi
.Init(mGlobal
)) {
696 localPromise
->MaybeReject(NS_ERROR_UNEXPECTED
);
700 JSContext
* cx
= jsapi
.cx();
703 switch (mConsumeType
) {
704 case CONSUME_ARRAYBUFFER
: {
705 JS::Rooted
<JSObject
*> arrayBuffer(cx
);
706 BodyUtil::ConsumeArrayBuffer(cx
, &arrayBuffer
, aResultLength
,
707 std::move(resultPtr
), error
);
709 if (!error
.Failed()) {
710 JS::Rooted
<JS::Value
> val(cx
);
711 val
.setObjectOrNull(arrayBuffer
);
713 localPromise
->MaybeResolve(val
);
718 MOZ_CRASH("This should not happen.");
721 case CONSUME_FORMDATA
: {
723 data
.Adopt(reinterpret_cast<char*>(resultPtr
.release()), aResultLength
);
725 RefPtr
<dom::FormData
> fd
= BodyUtil::ConsumeFormData(
726 mGlobal
, mBodyMimeType
, mMixedCaseMimeType
, data
, error
);
727 if (!error
.Failed()) {
728 localPromise
->MaybeResolve(fd
);
733 // fall through handles early exit.
737 BodyUtil::ConsumeText(aResultLength
, resultPtr
.get(), decoded
))) {
738 if (mConsumeType
== CONSUME_TEXT
) {
739 localPromise
->MaybeResolve(decoded
);
741 JS::Rooted
<JS::Value
> json(cx
);
742 BodyUtil::ConsumeJson(cx
, &json
, decoded
, error
);
743 if (!error
.Failed()) {
744 localPromise
->MaybeResolve(json
);
751 MOZ_ASSERT_UNREACHABLE("Unexpected consume body type");
754 error
.WouldReportJSException();
755 if (error
.Failed()) {
756 localPromise
->MaybeReject(std::move(error
));
760 void BodyConsumer::ContinueConsumeBlobBody(BlobImpl
* aBlobImpl
,
761 bool aShuttingDown
) {
762 AssertIsOnTargetThread();
763 MOZ_ASSERT(mConsumeType
== CONSUME_BLOB
);
768 mBodyConsumed
= true;
770 MOZ_ASSERT(mConsumePromise
);
771 RefPtr
<Promise
> localPromise
= std::move(mConsumePromise
);
773 if (!aShuttingDown
) {
774 RefPtr
<dom::Blob
> blob
= dom::Blob::Create(mGlobal
, aBlobImpl
);
775 if (NS_WARN_IF(!blob
)) {
776 localPromise
->MaybeReject(NS_ERROR_FAILURE
);
780 localPromise
->MaybeResolve(blob
);
786 void BodyConsumer::ShutDownMainThreadConsuming() {
787 if (!NS_IsMainThread()) {
788 RefPtr
<BodyConsumer
> self
= this;
790 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
791 "BodyConsumer::ShutDownMainThreadConsuming",
792 [self
]() { self
->ShutDownMainThreadConsuming(); });
794 mMainThreadEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
798 // We need this because maybe, mConsumeBodyPump has not been created yet. We
799 // must be sure that we don't try to do it.
800 mShuttingDown
= true;
802 if (mConsumeBodyPump
) {
803 mConsumeBodyPump
->CancelWithReason(
804 NS_BINDING_ABORTED
, "BodyConsumer::ShutDownMainThreadConsuming"_ns
);
805 mConsumeBodyPump
= nullptr;
809 NS_IMETHODIMP
BodyConsumer::Observe(nsISupports
* aSubject
, const char* aTopic
,
810 const char16_t
* aData
) {
811 AssertIsOnMainThread();
813 MOZ_ASSERT((strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) ||
814 (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0));
816 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(mGlobal
);
817 if (SameCOMIdentity(aSubject
, window
)) {
818 ContinueConsumeBody(NS_BINDING_ABORTED
, 0, nullptr);
824 void BodyConsumer::RunAbortAlgorithm() {
825 AssertIsOnTargetThread();
826 ShutDownMainThreadConsuming();
827 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR
, 0, nullptr);
830 NS_IMPL_ISUPPORTS(BodyConsumer
, nsIObserver
, nsISupportsWeakReference
)
832 } // namespace mozilla::dom