Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / base / BodyConsumer.cpp
blobda69ea2a7da5f5fac606ef70bb495b7e3cd6a1ec
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"
28 #include "nsIFile.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.
37 #ifdef CreateFile
38 # undef CreateFile
39 #endif
41 namespace mozilla::dom {
43 namespace {
45 class BeginConsumeBodyRunnable final : public Runnable {
46 public:
47 BeginConsumeBodyRunnable(BodyConsumer* aConsumer,
48 ThreadSafeWorkerRef* aWorkerRef)
49 : Runnable("BeginConsumeBodyRunnable"),
50 mBodyConsumer(aConsumer),
51 mWorkerRef(aWorkerRef) {}
53 NS_IMETHOD
54 Run() override {
55 mBodyConsumer->BeginConsumeBodyMainThread(mWorkerRef);
56 return NS_OK;
59 private:
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;
69 nsresult mStatus;
70 uint32_t mLength;
71 uint8_t* mResult;
73 public:
74 ContinueConsumeBodyRunnable(BodyConsumer* aBodyConsumer,
75 WorkerPrivate* aWorkerPrivate, nsresult aStatus,
76 uint32_t aLength, uint8_t* aResult)
77 : MainThreadWorkerRunnable("ContinueConsumeBodyRunnable"),
78 mBodyConsumer(aBodyConsumer),
79 mStatus(aStatus),
80 mLength(aLength),
81 mResult(aResult) {
82 MOZ_ASSERT(NS_IsMainThread());
85 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
86 mBodyConsumer->ContinueConsumeBody(mStatus, mLength, mResult);
87 return true;
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;
97 public:
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 */);
108 return true;
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 {
117 public:
118 AutoFailConsumeBody(BodyConsumer* aBodyConsumer,
119 ThreadSafeWorkerRef* aWorkerRef)
120 : mBodyConsumer(aBodyConsumer), mWorkerRef(aWorkerRef) {}
122 ~AutoFailConsumeBody() {
123 AssertIsOnMainThread();
125 if (!mBodyConsumer) {
126 return;
129 // Web Worker
130 if (mWorkerRef) {
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");
137 return;
140 // Main-thread
141 mBodyConsumer->ContinueConsumeBody(NS_ERROR_FAILURE, 0, nullptr);
144 void DontFail() { mBodyConsumer = nullptr; }
146 private:
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;
158 public:
159 ContinueConsumeBlobBodyRunnable(BodyConsumer* aBodyConsumer,
160 WorkerPrivate* aWorkerPrivate,
161 BlobImpl* aBlobImpl)
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);
171 return true;
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;
181 public:
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 */);
191 return true;
195 class ConsumeBodyDoneObserver final : public nsIStreamLoaderObserver,
196 public MutableBlobStorageCallback {
197 public:
198 NS_DECL_THREADSAFE_ISUPPORTS
200 ConsumeBodyDoneObserver(BodyConsumer* aBodyConsumer,
201 ThreadSafeWorkerRef* aWorkerRef)
202 : mBodyConsumer(aBodyConsumer), mWorkerRef(aWorkerRef) {}
204 NS_IMETHOD
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);
216 // Main-thread.
217 if (!mWorkerRef) {
218 mBodyConsumer->ContinueConsumeBody(aStatus, aResultLength,
219 nonconstResult);
220 // The caller is responsible for data.
221 return NS_SUCCESS_ADOPTED_DATA;
224 // Web Worker.
226 RefPtr<ContinueConsumeBodyRunnable> r = new ContinueConsumeBodyRunnable(
227 mBodyConsumer, mWorkerRef->Private(), aStatus, aResultLength,
228 nonconstResult);
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.
246 return NS_OK;
249 virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
250 BlobImpl* aBlobImpl, nsresult aRv) override {
251 // On error.
252 if (NS_FAILED(aRv)) {
253 OnStreamComplete(nullptr, nullptr, aRv, 0, nullptr);
254 return;
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);
264 private:
265 ~ConsumeBodyDoneObserver() = default;
267 RefPtr<BodyConsumer> mBodyConsumer;
268 RefPtr<ThreadSafeWorkerRef> mWorkerRef;
271 NS_IMPL_ISUPPORTS(ConsumeBodyDoneObserver, nsIStreamLoaderObserver)
273 } // namespace
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,
282 ErrorResult& aRv) {
283 MOZ_ASSERT(aBodyStream);
284 MOZ_ASSERT(aMainThreadEventTarget);
286 RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
287 if (aRv.Failed()) {
288 return nullptr;
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);
311 return nullptr;
314 workerRef = new ThreadSafeWorkerRef(strongWorkerRef);
315 } else {
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())) {
323 return nullptr;
326 if (aSignalImpl) {
327 consumer->Follow(aSignalImpl);
330 return promise.forget();
333 void BodyConsumer::ReleaseObject() {
334 AssertIsOnTargetThread();
336 if (NS_IsMainThread()) {
337 GlobalTeardownObserver::DisconnectFromOwner();
338 DisconnectFreezeObserver();
341 mGlobal = nullptr;
343 Unfollow();
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),
362 mConsumeType(aType),
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);
377 namespace {
379 class FileCreationHandler final : public PromiseNativeHandler {
380 public:
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);
399 return;
402 RefPtr<Blob> blob;
403 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) {
404 mConsumer->OnBlobResult(nullptr, mWorkerRef);
405 return;
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);
418 private:
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)
433 } // namespace
435 nsresult BodyConsumer::GetBodyLocalFile(nsIFile** aFile) const {
436 AssertIsOnMainThread();
438 if (!mBodyLocalPath.Length()) {
439 return NS_OK;
442 nsCOMPtr<nsIFile> file;
443 MOZ_TRY(NS_NewLocalFile(mBodyLocalPath, getter_AddRefs(file)));
445 bool exists;
446 MOZ_TRY(file->Exists(&exists));
447 if (!exists) {
448 return NS_ERROR_FILE_NOT_FOUND;
451 bool isDir;
452 MOZ_TRY(file->IsDirectory(&isDir));
453 if (isDir) {
454 return NS_ERROR_FILE_IS_DIRECTORY;
457 file.forget(aFile);
458 return NS_OK;
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);
471 if (mShuttingDown) {
472 // We haven't started yet, but we have been terminated. AutoFailConsumeBody
473 // will dispatch a runnable to release resources.
474 return;
477 if (mConsumeType == ConsumeType::Blob) {
478 nsresult rv;
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) {
486 return;
488 autoReject.DontFail();
489 DispatchContinueConsumeBlobBody(blobImpl, aWorkerRef);
490 return;
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);
501 ErrorResult error;
502 RefPtr<Promise> promise =
503 FileCreatorHelper::CreateFile(mGlobal, file, bag, true, error);
504 if (NS_WARN_IF(error.Failed())) {
505 return;
508 autoReject.DontFail();
509 FileCreationHandler::Create(promise, this, aWorkerRef);
510 return;
514 nsCOMPtr<nsIInputStreamPump> pump;
515 nsresult rv =
516 NS_NewInputStreamPump(getter_AddRefs(pump), mBodyStream.forget(), 0, 0,
517 false, mMainThreadEventTarget);
518 if (NS_WARN_IF(NS_FAILED(rv))) {
519 return;
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);
529 } else {
530 nsCOMPtr<nsIStreamLoader> loader;
531 rv = NS_NewStreamLoader(getter_AddRefs(loader), p);
532 if (NS_WARN_IF(NS_FAILED(rv))) {
533 return;
536 listener = loader;
539 rv = pump->AsyncRead(listener);
540 if (NS_WARN_IF(NS_FAILED(rv))) {
541 return;
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);
553 if (rr) {
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);
559 if (NS_FAILED(rv)) {
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();
582 // Main-thread.
583 if (!aWorkerRef) {
584 if (aBlobImpl) {
585 ContinueConsumeBlobBody(aBlobImpl);
586 } else {
587 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR, 0, nullptr);
589 return;
592 // Web Worker.
593 if (aBlobImpl) {
594 RefPtr<ContinueConsumeBlobBodyRunnable> r =
595 new ContinueConsumeBlobBodyRunnable(this, aWorkerRef->Private(),
596 aBlobImpl);
598 if (r->Dispatch(aWorkerRef->Private())) {
599 return;
601 } else {
602 RefPtr<ContinueConsumeBodyRunnable> r = new ContinueConsumeBodyRunnable(
603 this, aWorkerRef->Private(), NS_ERROR_DOM_ABORT_ERR, 0, nullptr);
605 if (r->Dispatch(aWorkerRef->Private())) {
606 return;
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};
632 if (mBodyConsumed) {
633 return;
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(); });
644 if (aShuttingDown) {
645 // If shutting down, we don't want to resolve any promise.
646 return;
649 if (NS_WARN_IF(NS_FAILED(aStatus))) {
650 // Per
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>();
659 } else {
660 localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
663 return;
666 // Finish successfully consuming body according to type.
667 MOZ_ASSERT(resultPtr);
669 AutoJSAPI jsapi;
670 if (!jsapi.Init(mGlobal)) {
671 localPromise->MaybeReject(NS_ERROR_UNEXPECTED);
672 return;
675 JSContext* cx = jsapi.cx();
676 ErrorResult error;
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);
687 break;
689 case ConsumeType::Blob: {
690 MOZ_CRASH("This should not happen.");
691 break;
693 case ConsumeType::Bytes: {
694 JS::Rooted<JSObject*> bytes(cx);
695 BodyUtil::ConsumeBytes(cx, &bytes, aResultLength, std::move(resultPtr),
696 error);
697 if (!error.Failed()) {
698 JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*bytes));
699 localPromise->MaybeResolve(val);
701 break;
703 case ConsumeType::FormData: {
704 nsCString data;
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);
712 break;
714 case ConsumeType::Text:
715 // fall through handles early exit.
716 case ConsumeType::JSON: {
717 nsString decoded;
718 if (NS_SUCCEEDED(
719 BodyUtil::ConsumeText(aResultLength, resultPtr.get(), decoded))) {
720 if (mConsumeType == ConsumeType::Text) {
721 localPromise->MaybeResolve(decoded);
722 } else {
723 JS::Rooted<JS::Value> json(cx);
724 BodyUtil::ConsumeJson(cx, &json, decoded, error);
725 if (!error.Failed()) {
726 localPromise->MaybeResolve(json);
730 break;
732 default:
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);
747 if (mBodyConsumed) {
748 return;
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);
759 return;
762 localPromise->MaybeResolve(blob);
765 ReleaseObject();
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);
777 return;
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