Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / BodyConsumer.cpp
blob754586a08b426867e82cdbb3f32448c062f1dcf4
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(aWorkerPrivate, "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(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 */);
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()) {
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(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);
172 return true;
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;
182 public:
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 */);
192 return true;
196 class ConsumeBodyDoneObserver final : public nsIStreamLoaderObserver,
197 public MutableBlobStorageCallback {
198 public:
199 NS_DECL_THREADSAFE_ISUPPORTS
201 ConsumeBodyDoneObserver(BodyConsumer* aBodyConsumer,
202 ThreadSafeWorkerRef* aWorkerRef)
203 : mBodyConsumer(aBodyConsumer), mWorkerRef(aWorkerRef) {}
205 NS_IMETHOD
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);
217 // Main-thread.
218 if (!mWorkerRef) {
219 mBodyConsumer->ContinueConsumeBody(aStatus, aResultLength,
220 nonconstResult);
221 // The caller is responsible for data.
222 return NS_SUCCESS_ADOPTED_DATA;
225 // Web Worker.
227 RefPtr<ContinueConsumeBodyRunnable> r = new ContinueConsumeBodyRunnable(
228 mBodyConsumer, mWorkerRef->Private(), aStatus, aResultLength,
229 nonconstResult);
230 if (r->Dispatch()) {
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.
247 return NS_OK;
250 virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
251 BlobImpl* aBlobImpl, nsresult aRv) override {
252 // On error.
253 if (NS_FAILED(aRv)) {
254 OnStreamComplete(nullptr, nullptr, aRv, 0, nullptr);
255 return;
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);
265 private:
266 ~ConsumeBodyDoneObserver() = default;
268 RefPtr<BodyConsumer> mBodyConsumer;
269 RefPtr<ThreadSafeWorkerRef> mWorkerRef;
272 NS_IMPL_ISUPPORTS(ConsumeBodyDoneObserver, nsIStreamLoaderObserver)
274 } // namespace
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,
283 ErrorResult& aRv) {
284 MOZ_ASSERT(aBodyStream);
285 MOZ_ASSERT(aMainThreadEventTarget);
287 RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
288 if (aRv.Failed()) {
289 return nullptr;
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);
312 return nullptr;
315 workerRef = new ThreadSafeWorkerRef(strongWorkerRef);
316 } else {
317 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
318 if (NS_WARN_IF(!os)) {
319 aRv.Throw(NS_ERROR_FAILURE);
320 return nullptr;
323 aRv = os->AddObserver(consumer, DOM_WINDOW_DESTROYED_TOPIC, true);
324 if (NS_WARN_IF(aRv.Failed())) {
325 return nullptr;
328 aRv = os->AddObserver(consumer, DOM_WINDOW_FROZEN_TOPIC, true);
329 if (NS_WARN_IF(aRv.Failed())) {
330 return nullptr;
334 nsCOMPtr<nsIRunnable> r = new BeginConsumeBodyRunnable(consumer, workerRef);
335 aRv = aMainThreadEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
336 if (NS_WARN_IF(aRv.Failed())) {
337 return nullptr;
340 if (aSignalImpl) {
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();
352 if (os) {
353 os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
354 os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
358 mGlobal = nullptr;
360 Unfollow();
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),
379 mConsumeType(aType),
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);
394 namespace {
396 class FileCreationHandler final : public PromiseNativeHandler {
397 public:
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);
416 return;
419 RefPtr<Blob> blob;
420 if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) {
421 mConsumer->OnBlobResult(nullptr, mWorkerRef);
422 return;
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);
435 private:
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)
450 } // namespace
452 nsresult BodyConsumer::GetBodyLocalFile(nsIFile** aFile) const {
453 AssertIsOnMainThread();
455 if (!mBodyLocalPath.Length()) {
456 return NS_OK;
459 nsresult rv;
460 nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1", &rv);
461 if (NS_FAILED(rv)) {
462 return rv;
465 rv = file->InitWithPath(mBodyLocalPath);
466 NS_ENSURE_SUCCESS(rv, rv);
468 bool exists;
469 rv = file->Exists(&exists);
470 NS_ENSURE_SUCCESS(rv, rv);
471 if (!exists) {
472 return NS_ERROR_FILE_NOT_FOUND;
475 bool isDir;
476 rv = file->IsDirectory(&isDir);
477 NS_ENSURE_SUCCESS(rv, rv);
478 if (isDir) {
479 return NS_ERROR_FILE_IS_DIRECTORY;
482 file.forget(aFile);
483 return NS_OK;
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);
496 if (mShuttingDown) {
497 // We haven't started yet, but we have been terminated. AutoFailConsumeBody
498 // will dispatch a runnable to release resources.
499 return;
502 if (mConsumeType == CONSUME_BLOB) {
503 nsresult rv;
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) {
511 return;
513 autoReject.DontFail();
514 DispatchContinueConsumeBlobBody(blobImpl, aWorkerRef);
515 return;
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);
526 ErrorResult error;
527 RefPtr<Promise> promise =
528 FileCreatorHelper::CreateFile(mGlobal, file, bag, true, error);
529 if (NS_WARN_IF(error.Failed())) {
530 return;
533 autoReject.DontFail();
534 FileCreationHandler::Create(promise, this, aWorkerRef);
535 return;
539 nsCOMPtr<nsIInputStreamPump> pump;
540 nsresult rv =
541 NS_NewInputStreamPump(getter_AddRefs(pump), mBodyStream.forget(), 0, 0,
542 false, mMainThreadEventTarget);
543 if (NS_WARN_IF(NS_FAILED(rv))) {
544 return;
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);
554 } else {
555 nsCOMPtr<nsIStreamLoader> loader;
556 rv = NS_NewStreamLoader(getter_AddRefs(loader), p);
557 if (NS_WARN_IF(NS_FAILED(rv))) {
558 return;
561 listener = loader;
564 rv = pump->AsyncRead(listener);
565 if (NS_WARN_IF(NS_FAILED(rv))) {
566 return;
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);
578 if (rr) {
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);
584 if (NS_FAILED(rv)) {
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();
607 // Main-thread.
608 if (!aWorkerRef) {
609 if (aBlobImpl) {
610 ContinueConsumeBlobBody(aBlobImpl);
611 } else {
612 ContinueConsumeBody(NS_ERROR_DOM_ABORT_ERR, 0, nullptr);
614 return;
617 // Web Worker.
618 if (aBlobImpl) {
619 RefPtr<ContinueConsumeBlobBodyRunnable> r =
620 new ContinueConsumeBlobBodyRunnable(this, aWorkerRef->Private(),
621 aBlobImpl);
623 if (r->Dispatch()) {
624 return;
626 } else {
627 RefPtr<ContinueConsumeBodyRunnable> r = new ContinueConsumeBodyRunnable(
628 this, aWorkerRef->Private(), NS_ERROR_DOM_ABORT_ERR, 0, nullptr);
630 if (r->Dispatch()) {
631 return;
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};
657 if (mBodyConsumed) {
658 return;
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(); });
669 if (aShuttingDown) {
670 // If shutting down, we don't want to resolve any promise.
671 return;
674 if (NS_WARN_IF(NS_FAILED(aStatus))) {
675 // Per
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>();
682 } else {
683 localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
687 // Don't warn here since we warned above.
688 if (NS_FAILED(aStatus)) {
689 return;
692 // Finish successfully consuming body according to type.
693 MOZ_ASSERT(resultPtr);
695 AutoJSAPI jsapi;
696 if (!jsapi.Init(mGlobal)) {
697 localPromise->MaybeReject(NS_ERROR_UNEXPECTED);
698 return;
701 JSContext* cx = jsapi.cx();
702 ErrorResult error;
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);
716 break;
718 case CONSUME_BLOB: {
719 MOZ_CRASH("This should not happen.");
720 break;
722 case CONSUME_FORMDATA: {
723 nsCString data;
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);
731 break;
733 case CONSUME_TEXT:
734 // fall through handles early exit.
735 case CONSUME_JSON: {
736 nsString decoded;
737 if (NS_SUCCEEDED(
738 BodyUtil::ConsumeText(aResultLength, resultPtr.get(), decoded))) {
739 if (mConsumeType == CONSUME_TEXT) {
740 localPromise->MaybeResolve(decoded);
741 } else {
742 JS::Rooted<JS::Value> json(cx);
743 BodyUtil::ConsumeJson(cx, &json, decoded, error);
744 if (!error.Failed()) {
745 localPromise->MaybeResolve(json);
749 break;
751 default:
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);
766 if (mBodyConsumed) {
767 return;
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);
778 return;
781 localPromise->MaybeResolve(blob);
784 ReleaseObject();
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);
796 return;
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);
822 return NS_OK;
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