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