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/. */
6 
7 #include "EmptyBlobImpl.h"
8 #include "MutableBlobStorage.h"
9 #include "MemoryBlobImpl.h"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/dom/TemporaryIPCBlobChild.h"
12 #include "mozilla/dom/WorkerPrivate.h"
13 #include "mozilla/ipc/BackgroundChild.h"
14 #include "mozilla/ipc/PBackgroundChild.h"
15 #include "mozilla/Preferences.h"
16 #include "mozilla/TaskQueue.h"
17 #include "File.h"
18 #include "nsAnonymousTemporaryFile.h"
19 #include "nsNetCID.h"
20 #include "nsProxyRelease.h"
21 
22 #define BLOB_MEMORY_TEMPORARY_FILE 1048576
23 
24 namespace mozilla::dom {
25 
26 namespace {
27 
28 // This class uses the callback to inform when the Blob is created or when the
29 // error must be propagated.
30 class BlobCreationDoneRunnable final : public Runnable {
31  public:
BlobCreationDoneRunnable(MutableBlobStorage * aBlobStorage,MutableBlobStorageCallback * aCallback,BlobImpl * aBlobImpl,nsresult aRv)32   BlobCreationDoneRunnable(MutableBlobStorage* aBlobStorage,
33                            MutableBlobStorageCallback* aCallback,
34                            BlobImpl* aBlobImpl, nsresult aRv)
35       : Runnable("dom::BlobCreationDoneRunnable"),
36         mBlobStorage(aBlobStorage),
37         mCallback(aCallback),
38         mBlobImpl(aBlobImpl),
39         mRv(aRv) {
40     MOZ_ASSERT(aBlobStorage);
41     MOZ_ASSERT(aCallback);
42     MOZ_ASSERT((NS_FAILED(aRv) && !aBlobImpl) ||
43                (NS_SUCCEEDED(aRv) && aBlobImpl));
44   }
45 
46   NS_IMETHOD
Run()47   Run() override {
48     MOZ_ASSERT(NS_IsMainThread());
49     MOZ_ASSERT(mBlobStorage);
50     mCallback->BlobStoreCompleted(mBlobStorage, mBlobImpl, mRv);
51     mCallback = nullptr;
52     mBlobImpl = nullptr;
53     return NS_OK;
54   }
55 
56  private:
~BlobCreationDoneRunnable()57   ~BlobCreationDoneRunnable() {
58     MOZ_ASSERT(mBlobStorage);
59     // If something when wrong, we still have to release these objects in the
60     // correct thread.
61     NS_ProxyRelease("BlobCreationDoneRunnable::mCallback",
62                     mBlobStorage->EventTarget(), mCallback.forget());
63   }
64 
65   RefPtr<MutableBlobStorage> mBlobStorage;
66   RefPtr<MutableBlobStorageCallback> mCallback;
67   RefPtr<BlobImpl> mBlobImpl;
68   nsresult mRv;
69 };
70 
71 // Simple runnable to propagate the error to the BlobStorage.
72 class ErrorPropagationRunnable final : public Runnable {
73  public:
ErrorPropagationRunnable(MutableBlobStorage * aBlobStorage,nsresult aRv)74   ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv)
75       : Runnable("dom::ErrorPropagationRunnable"),
76         mBlobStorage(aBlobStorage),
77         mRv(aRv) {}
78 
79   NS_IMETHOD
Run()80   Run() override {
81     mBlobStorage->ErrorPropagated(mRv);
82     return NS_OK;
83   }
84 
85  private:
86   RefPtr<MutableBlobStorage> mBlobStorage;
87   nsresult mRv;
88 };
89 
90 // This runnable moves a buffer to the IO thread and there, it writes it into
91 // the temporary file, if its File Descriptor has not been already closed.
92 class WriteRunnable final : public Runnable {
93  public:
CopyBuffer(MutableBlobStorage * aBlobStorage,const void * aData,uint32_t aLength)94   static WriteRunnable* CopyBuffer(MutableBlobStorage* aBlobStorage,
95                                    const void* aData, uint32_t aLength) {
96     MOZ_ASSERT(aBlobStorage);
97     MOZ_ASSERT(aData);
98 
99     // We have to take a copy of this buffer.
100     void* data = malloc(aLength);
101     if (!data) {
102       return nullptr;
103     }
104 
105     memcpy((char*)data, aData, aLength);
106     return new WriteRunnable(aBlobStorage, data, aLength);
107   }
108 
AdoptBuffer(MutableBlobStorage * aBlobStorage,void * aData,uint32_t aLength)109   static WriteRunnable* AdoptBuffer(MutableBlobStorage* aBlobStorage,
110                                     void* aData, uint32_t aLength) {
111     MOZ_ASSERT(NS_IsMainThread());
112     MOZ_ASSERT(aBlobStorage);
113     MOZ_ASSERT(aData);
114 
115     return new WriteRunnable(aBlobStorage, aData, aLength);
116   }
117 
118   NS_IMETHOD
Run()119   Run() override {
120     MOZ_ASSERT(!NS_IsMainThread());
121     MOZ_ASSERT(mBlobStorage);
122 
123     PRFileDesc* fd = mBlobStorage->GetFD();
124     if (!fd) {
125       // The file descriptor has been closed in the meantime.
126       return NS_OK;
127     }
128 
129     int32_t written = PR_Write(fd, mData, mLength);
130     if (NS_WARN_IF(written < 0 || uint32_t(written) != mLength)) {
131       mBlobStorage->CloseFD();
132       return mBlobStorage->EventTarget()->Dispatch(
133           new ErrorPropagationRunnable(mBlobStorage, NS_ERROR_FAILURE),
134           NS_DISPATCH_NORMAL);
135     }
136 
137     return NS_OK;
138   }
139 
140  private:
WriteRunnable(MutableBlobStorage * aBlobStorage,void * aData,uint32_t aLength)141   WriteRunnable(MutableBlobStorage* aBlobStorage, void* aData, uint32_t aLength)
142       : Runnable("dom::WriteRunnable"),
143         mBlobStorage(aBlobStorage),
144         mData(aData),
145         mLength(aLength) {
146     MOZ_ASSERT(mBlobStorage);
147     MOZ_ASSERT(aData);
148   }
149 
~WriteRunnable()150   ~WriteRunnable() { free(mData); }
151 
152   RefPtr<MutableBlobStorage> mBlobStorage;
153   void* mData;
154   uint32_t mLength;
155 };
156 
157 // This runnable closes the FD in case something goes wrong or the temporary
158 // file is not needed anymore.
159 class CloseFileRunnable final : public Runnable {
160  public:
CloseFileRunnable(PRFileDesc * aFD)161   explicit CloseFileRunnable(PRFileDesc* aFD)
162       : Runnable("dom::CloseFileRunnable"), mFD(aFD) {}
163 
164   NS_IMETHOD
Run()165   Run() override {
166     MOZ_ASSERT(!NS_IsMainThread());
167     PR_Close(mFD);
168     mFD = nullptr;
169     return NS_OK;
170   }
171 
172  private:
~CloseFileRunnable()173   ~CloseFileRunnable() {
174     if (mFD) {
175       PR_Close(mFD);
176     }
177   }
178 
179   PRFileDesc* mFD;
180 };
181 
182 // This runnable is dispatched to the main-thread from the IO thread and its
183 // task is to create the blob and inform the callback.
184 class CreateBlobRunnable final : public Runnable,
185                                  public TemporaryIPCBlobChildCallback {
186  public:
187   // We need to always declare refcounting because
188   // TemporaryIPCBlobChildCallback has pure-virtual refcounting.
189   NS_DECL_ISUPPORTS_INHERITED
190 
CreateBlobRunnable(MutableBlobStorage * aBlobStorage,const nsACString & aContentType,already_AddRefed<MutableBlobStorageCallback> aCallback)191   CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
192                      const nsACString& aContentType,
193                      already_AddRefed<MutableBlobStorageCallback> aCallback)
194       : Runnable("dom::CreateBlobRunnable"),
195         mBlobStorage(aBlobStorage),
196         mContentType(aContentType),
197         mCallback(aCallback) {
198     MOZ_ASSERT(!NS_IsMainThread());
199     MOZ_ASSERT(aBlobStorage);
200   }
201 
202   NS_IMETHOD
Run()203   Run() override {
204     MOZ_ASSERT(NS_IsMainThread());
205     MOZ_ASSERT(mBlobStorage);
206     mBlobStorage->AskForBlob(this, mContentType);
207     return NS_OK;
208   }
209 
OperationSucceeded(BlobImpl * aBlobImpl)210   void OperationSucceeded(BlobImpl* aBlobImpl) override {
211     RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback));
212     callback->BlobStoreCompleted(mBlobStorage, aBlobImpl, NS_OK);
213   }
214 
OperationFailed(nsresult aRv)215   void OperationFailed(nsresult aRv) override {
216     RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback));
217     callback->BlobStoreCompleted(mBlobStorage, nullptr, aRv);
218   }
219 
220  private:
~CreateBlobRunnable()221   ~CreateBlobRunnable() {
222     MOZ_ASSERT(mBlobStorage);
223     // If something when wrong, we still have to release data in the correct
224     // thread.
225     NS_ProxyRelease("CreateBlobRunnable::mCallback",
226                     mBlobStorage->EventTarget(), mCallback.forget());
227   }
228 
229   RefPtr<MutableBlobStorage> mBlobStorage;
230   nsCString mContentType;
231   RefPtr<MutableBlobStorageCallback> mCallback;
232 };
233 
234 NS_IMPL_ISUPPORTS_INHERITED0(CreateBlobRunnable, Runnable)
235 
236 // This task is used to know when the writing is completed. From the IO thread
237 // it dispatches a CreateBlobRunnable to the main-thread.
238 class LastRunnable final : public Runnable {
239  public:
LastRunnable(MutableBlobStorage * aBlobStorage,const nsACString & aContentType,MutableBlobStorageCallback * aCallback)240   LastRunnable(MutableBlobStorage* aBlobStorage, const nsACString& aContentType,
241                MutableBlobStorageCallback* aCallback)
242       : Runnable("dom::LastRunnable"),
243         mBlobStorage(aBlobStorage),
244         mContentType(aContentType),
245         mCallback(aCallback) {
246     MOZ_ASSERT(NS_IsMainThread());
247     MOZ_ASSERT(mBlobStorage);
248     MOZ_ASSERT(aCallback);
249   }
250 
251   NS_IMETHOD
Run()252   Run() override {
253     MOZ_ASSERT(!NS_IsMainThread());
254 
255     RefPtr<Runnable> runnable =
256         new CreateBlobRunnable(mBlobStorage, mContentType, mCallback.forget());
257     return mBlobStorage->EventTarget()->Dispatch(runnable, NS_DISPATCH_NORMAL);
258   }
259 
260  private:
~LastRunnable()261   ~LastRunnable() {
262     MOZ_ASSERT(mBlobStorage);
263     // If something when wrong, we still have to release data in the correct
264     // thread.
265     NS_ProxyRelease("LastRunnable::mCallback", mBlobStorage->EventTarget(),
266                     mCallback.forget());
267   }
268 
269   RefPtr<MutableBlobStorage> mBlobStorage;
270   nsCString mContentType;
271   RefPtr<MutableBlobStorageCallback> mCallback;
272 };
273 
274 }  // anonymous namespace
275 
MutableBlobStorage(MutableBlobStorageType aType,nsIEventTarget * aEventTarget,uint32_t aMaxMemory)276 MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
277                                        nsIEventTarget* aEventTarget,
278                                        uint32_t aMaxMemory)
279     : mMutex("MutableBlobStorage::mMutex"),
280       mData(nullptr),
281       mDataLen(0),
282       mDataBufferLen(0),
283       mStorageState(aType == eOnlyInMemory ? eKeepInMemory : eInMemory),
284       mFD(nullptr),
285       mErrorResult(NS_OK),
286       mEventTarget(aEventTarget),
287       mMaxMemory(aMaxMemory) {
288   MOZ_ASSERT(NS_IsMainThread());
289 
290   if (!mEventTarget) {
291     mEventTarget = GetMainThreadEventTarget();
292   }
293 
294   if (aMaxMemory == 0 && aType == eCouldBeInTemporaryFile) {
295     mMaxMemory = Preferences::GetUint("dom.blob.memoryToTemporaryFile",
296                                       BLOB_MEMORY_TEMPORARY_FILE);
297   }
298 
299   MOZ_ASSERT(mEventTarget);
300 }
301 
~MutableBlobStorage()302 MutableBlobStorage::~MutableBlobStorage() {
303   free(mData);
304 
305   if (mFD) {
306     RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
307     Unused << DispatchToIOThread(runnable.forget());
308   }
309 
310   if (mTaskQueue) {
311     mTaskQueue->BeginShutdown();
312   }
313 
314   if (mActor) {
315     NS_ProxyRelease("MutableBlobStorage::mActor", EventTarget(),
316                     mActor.forget());
317   }
318 }
319 
GetBlobImplWhenReady(const nsACString & aContentType,MutableBlobStorageCallback * aCallback)320 void MutableBlobStorage::GetBlobImplWhenReady(
321     const nsACString& aContentType, MutableBlobStorageCallback* aCallback) {
322   MOZ_ASSERT(NS_IsMainThread());
323   MOZ_ASSERT(aCallback);
324 
325   MutexAutoLock lock(mMutex);
326 
327   // GetBlob can be called just once.
328   MOZ_ASSERT(mStorageState != eClosed);
329   StorageState previousState = mStorageState;
330   mStorageState = eClosed;
331 
332   if (previousState == eInTemporaryFile) {
333     if (NS_FAILED(mErrorResult)) {
334       MOZ_ASSERT(!mActor);
335 
336       RefPtr<Runnable> runnable =
337           new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult);
338       EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
339       return;
340     }
341 
342     MOZ_ASSERT(mActor);
343 
344     // We want to wait until all the WriteRunnable are completed. The way we do
345     // this is to go to the I/O thread and then we come back: the runnables are
346     // executed in order and this LastRunnable will be... the last one.
347     // This Runnable will also close the FD on the I/O thread.
348     RefPtr<Runnable> runnable = new LastRunnable(this, aContentType, aCallback);
349 
350     // If the dispatching fails, we are shutting down and it's fine to do not
351     // run the callback.
352     Unused << DispatchToIOThread(runnable.forget());
353     return;
354   }
355 
356   // If we are waiting for the temporary file, it's better to wait...
357   if (previousState == eWaitingForTemporaryFile) {
358     mPendingContentType = aContentType;
359     mPendingCallback = aCallback;
360     return;
361   }
362 
363   RefPtr<BlobImpl> blobImpl;
364 
365   if (mData) {
366     blobImpl = new MemoryBlobImpl(mData, mDataLen,
367                                   NS_ConvertUTF8toUTF16(aContentType));
368 
369     mData = nullptr;  // The MemoryBlobImpl takes ownership of the buffer
370     mDataLen = 0;
371     mDataBufferLen = 0;
372   } else {
373     blobImpl = new EmptyBlobImpl(NS_ConvertUTF8toUTF16(aContentType));
374   }
375 
376   RefPtr<BlobCreationDoneRunnable> runnable =
377       new BlobCreationDoneRunnable(this, aCallback, blobImpl, NS_OK);
378 
379   nsresult error =
380       EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
381   if (NS_WARN_IF(NS_FAILED(error))) {
382     return;
383   }
384 }
385 
Append(const void * aData,uint32_t aLength)386 nsresult MutableBlobStorage::Append(const void* aData, uint32_t aLength) {
387   // This method can be called on any thread.
388 
389   MutexAutoLock lock(mMutex);
390   MOZ_ASSERT(mStorageState != eClosed);
391   NS_ENSURE_ARG_POINTER(aData);
392 
393   if (!aLength) {
394     return NS_OK;
395   }
396 
397   // If eInMemory is the current Storage state, we could maybe migrate to
398   // a temporary file.
399   if (mStorageState == eInMemory && ShouldBeTemporaryStorage(lock, aLength) &&
400       !MaybeCreateTemporaryFile(lock)) {
401     return NS_ERROR_FAILURE;
402   }
403 
404   // If we are already in the temporaryFile mode, we have to dispatch a
405   // runnable.
406   if (mStorageState == eInTemporaryFile) {
407     // If a previous operation failed, let's return that error now.
408     if (NS_FAILED(mErrorResult)) {
409       return mErrorResult;
410     }
411 
412     RefPtr<WriteRunnable> runnable =
413         WriteRunnable::CopyBuffer(this, aData, aLength);
414     if (NS_WARN_IF(!runnable)) {
415       return NS_ERROR_OUT_OF_MEMORY;
416     }
417 
418     nsresult rv = DispatchToIOThread(runnable.forget());
419     if (NS_WARN_IF(NS_FAILED(rv))) {
420       return rv;
421     }
422 
423     mDataLen += aLength;
424     return NS_OK;
425   }
426 
427   // By default, we store in memory.
428 
429   uint64_t offset = mDataLen;
430 
431   if (!ExpandBufferSize(lock, aLength)) {
432     return NS_ERROR_OUT_OF_MEMORY;
433   }
434 
435   memcpy((char*)mData + offset, aData, aLength);
436   return NS_OK;
437 }
438 
ExpandBufferSize(const MutexAutoLock & aProofOfLock,uint64_t aSize)439 bool MutableBlobStorage::ExpandBufferSize(const MutexAutoLock& aProofOfLock,
440                                           uint64_t aSize) {
441   MOZ_ASSERT(mStorageState < eInTemporaryFile);
442 
443   if (mDataBufferLen >= mDataLen + aSize) {
444     mDataLen += aSize;
445     return true;
446   }
447 
448   // Start at 1 or we'll loop forever.
449   CheckedUint32 bufferLen =
450       std::max<uint32_t>(static_cast<uint32_t>(mDataBufferLen), 1);
451   while (bufferLen.isValid() && bufferLen.value() < mDataLen + aSize) {
452     bufferLen *= 2;
453   }
454 
455   if (!bufferLen.isValid()) {
456     return false;
457   }
458 
459   void* data = realloc(mData, bufferLen.value());
460   if (!data) {
461     return false;
462   }
463 
464   mData = data;
465   mDataBufferLen = bufferLen.value();
466   mDataLen += aSize;
467   return true;
468 }
469 
ShouldBeTemporaryStorage(const MutexAutoLock & aProofOfLock,uint64_t aSize) const470 bool MutableBlobStorage::ShouldBeTemporaryStorage(
471     const MutexAutoLock& aProofOfLock, uint64_t aSize) const {
472   MOZ_ASSERT(mStorageState == eInMemory);
473 
474   CheckedUint32 bufferSize = mDataLen;
475   bufferSize += aSize;
476 
477   if (!bufferSize.isValid()) {
478     return false;
479   }
480 
481   return bufferSize.value() >= mMaxMemory;
482 }
483 
MaybeCreateTemporaryFile(const MutexAutoLock & aProofOfLock)484 bool MutableBlobStorage::MaybeCreateTemporaryFile(
485     const MutexAutoLock& aProofOfLock) {
486   mStorageState = eWaitingForTemporaryFile;
487 
488   if (!NS_IsMainThread()) {
489     RefPtr<MutableBlobStorage> self = this;
490     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
491         "MutableBlobStorage::MaybeCreateTemporaryFile", [self]() {
492           MutexAutoLock lock(self->mMutex);
493           self->MaybeCreateTemporaryFileOnMainThread(lock);
494           if (!self->mActor) {
495             self->ErrorPropagated(NS_ERROR_FAILURE);
496           }
497         });
498     EventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
499     return true;
500   }
501 
502   MaybeCreateTemporaryFileOnMainThread(aProofOfLock);
503   return !!mActor;
504 }
505 
MaybeCreateTemporaryFileOnMainThread(const MutexAutoLock & aProofOfLock)506 void MutableBlobStorage::MaybeCreateTemporaryFileOnMainThread(
507     const MutexAutoLock& aProofOfLock) {
508   MOZ_ASSERT(NS_IsMainThread());
509   MOZ_ASSERT(!mActor);
510 
511   mozilla::ipc::PBackgroundChild* actorChild =
512       mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
513   if (NS_WARN_IF(!actorChild)) {
514     return;
515   }
516 
517   mActor = new TemporaryIPCBlobChild(this);
518   actorChild->SendPTemporaryIPCBlobConstructor(mActor);
519 
520   // We need manually to increase the reference for this actor because the
521   // IPC allocator method is not triggered. The Release() is called by IPDL
522   // when the actor is deleted.
523   mActor.get()->AddRef();
524 
525   // The actor will call us when the FileDescriptor is received.
526 }
527 
TemporaryFileCreated(PRFileDesc * aFD)528 void MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD) {
529   MOZ_ASSERT(NS_IsMainThread());
530 
531   MutexAutoLock lock(mMutex);
532   MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
533              mStorageState == eClosed);
534   MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
535   MOZ_ASSERT(mActor);
536   MOZ_ASSERT(aFD);
537 
538   // If the object has been already closed and we don't need to execute a
539   // callback, we need just to close the file descriptor in the correct thread.
540   if (mStorageState == eClosed && !mPendingCallback) {
541     RefPtr<Runnable> runnable = new CloseFileRunnable(aFD);
542 
543     // If this dispatching fails, CloseFileRunnable will close the FD in the
544     // DTOR on the current thread.
545     Unused << DispatchToIOThread(runnable.forget());
546 
547     // Let's inform the parent that we have nothing else to do.
548     mActor->SendOperationFailed();
549     mActor = nullptr;
550     return;
551   }
552 
553   // If we still receiving data, we can proceed in temporary-file mode.
554   if (mStorageState == eWaitingForTemporaryFile) {
555     mStorageState = eInTemporaryFile;
556   }
557 
558   mFD = aFD;
559   MOZ_ASSERT(NS_SUCCEEDED(mErrorResult));
560 
561   // This runnable takes the ownership of mData and it will write this buffer
562   // into the temporary file.
563   RefPtr<WriteRunnable> runnable =
564       WriteRunnable::AdoptBuffer(this, mData, mDataLen);
565   MOZ_ASSERT(runnable);
566 
567   mData = nullptr;
568 
569   nsresult rv = DispatchToIOThread(runnable.forget());
570   if (NS_WARN_IF(NS_FAILED(rv))) {
571     // Shutting down, we cannot continue.
572     return;
573   }
574 
575   // If we are closed, it means that GetBlobImplWhenReady() has been called when
576   // we were already waiting for a temporary file-descriptor. Finally we are
577   // here, AdoptBuffer runnable is going to write the current buffer into this
578   // file. After that, there is nothing else to write, and we dispatch
579   // LastRunnable which ends up calling mPendingCallback via CreateBlobRunnable.
580   if (mStorageState == eClosed) {
581     MOZ_ASSERT(mPendingCallback);
582 
583     RefPtr<Runnable> runnable =
584         new LastRunnable(this, mPendingContentType, mPendingCallback);
585     Unused << DispatchToIOThread(runnable.forget());
586 
587     mPendingCallback = nullptr;
588   }
589 }
590 
AskForBlob(TemporaryIPCBlobChildCallback * aCallback,const nsACString & aContentType)591 void MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
592                                     const nsACString& aContentType) {
593   MOZ_ASSERT(NS_IsMainThread());
594 
595   MutexAutoLock lock(mMutex);
596   MOZ_ASSERT(mStorageState == eClosed);
597   MOZ_ASSERT(mFD);
598   MOZ_ASSERT(mActor);
599   MOZ_ASSERT(aCallback);
600 
601   // Let's pass the FileDescriptor to the parent actor in order to keep the file
602   // locked on windows.
603   mActor->AskForBlob(aCallback, aContentType, mFD);
604 
605   // The previous operation has duplicated the file descriptor. Now we can close
606   // mFD. The parent will take care of closing the duplicated file descriptor on
607   // its side.
608   RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
609   Unused << DispatchToIOThread(runnable.forget());
610 
611   mFD = nullptr;
612   mActor = nullptr;
613 }
614 
ErrorPropagated(nsresult aRv)615 void MutableBlobStorage::ErrorPropagated(nsresult aRv) {
616   MOZ_ASSERT(NS_IsMainThread());
617 
618   MutexAutoLock lock(mMutex);
619   mErrorResult = aRv;
620 
621   if (mActor) {
622     mActor->SendOperationFailed();
623     mActor = nullptr;
624   }
625 }
626 
DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable)627 nsresult MutableBlobStorage::DispatchToIOThread(
628     already_AddRefed<nsIRunnable> aRunnable) {
629   if (!mTaskQueue) {
630     nsCOMPtr<nsIEventTarget> target =
631         do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
632     MOZ_ASSERT(target);
633 
634     mTaskQueue = new TaskQueue(target.forget());
635   }
636 
637   nsCOMPtr<nsIRunnable> runnable(aRunnable);
638   nsresult rv = mTaskQueue->Dispatch(runnable.forget());
639   if (NS_WARN_IF(NS_FAILED(rv))) {
640     return rv;
641   }
642 
643   return NS_OK;
644 }
645 
SizeOfCurrentMemoryBuffer()646 size_t MutableBlobStorage::SizeOfCurrentMemoryBuffer() {
647   MOZ_ASSERT(NS_IsMainThread());
648   MutexAutoLock lock(mMutex);
649   return mStorageState < eInTemporaryFile ? mDataLen : 0;
650 }
651 
GetFD()652 PRFileDesc* MutableBlobStorage::GetFD() {
653   MOZ_ASSERT(!NS_IsMainThread());
654   MutexAutoLock lock(mMutex);
655   return mFD;
656 }
657 
CloseFD()658 void MutableBlobStorage::CloseFD() {
659   MOZ_ASSERT(!NS_IsMainThread());
660   MutexAutoLock lock(mMutex);
661   MOZ_ASSERT(mFD);
662 
663   PR_Close(mFD);
664   mFD = nullptr;
665 }
666 
667 }  // namespace mozilla::dom
668