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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ActorsParent.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Atomics.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/SpinEventLoopUntil.h"
13 #include "mozilla/Unused.h"
14 #include "mozilla/dom/BlobImpl.h"
15 #include "mozilla/dom/File.h"
16 #include "mozilla/dom/PBackgroundFileHandleParent.h"
17 #include "mozilla/dom/PBackgroundFileRequestParent.h"
18 #include "mozilla/dom/indexedDB/ActorsParent.h"
19 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
20 #include "mozilla/dom/IPCBlobUtils.h"
21 #include "mozilla/dom/quota/MemoryOutputStream.h"
22 #include "nsComponentManagerUtils.h"
25 #include "nsIEventTarget.h"
27 #include "nsIFileStreams.h"
28 #include "nsIInputStream.h"
29 #include "nsIOutputStream.h"
30 #include "nsIRunnable.h"
31 #include "nsISeekableStream.h"
32 #include "nsIThread.h"
33 #include "nsIThreadPool.h"
34 #include "nsNetUtil.h"
35 #include "nsStreamUtils.h"
36 #include "nsStringStream.h"
38 #include "nsThreadPool.h"
39 #include "nsThreadUtils.h"
40 #include "nsXPCOMCIDInternal.h"
42 namespace mozilla::dom
{
44 using namespace mozilla::dom::quota
;
45 using namespace mozilla::ipc
;
49 /******************************************************************************
51 ******************************************************************************/
53 const uint32_t kThreadLimit
= 5;
54 const uint32_t kIdleThreadLimit
= 1;
55 const uint32_t kIdleThreadTimeoutMs
= 30000;
57 const uint32_t kStreamCopyBlockSize
= 32768;
61 class FileHandleThreadPool::FileHandleQueue final
: public Runnable
{
62 friend class FileHandleThreadPool
;
64 RefPtr
<FileHandleThreadPool
> mOwningFileHandleThreadPool
;
65 RefPtr
<FileHandle
> mFileHandle
;
66 nsTArray
<RefPtr
<FileHandleOp
>> mQueue
;
67 RefPtr
<FileHandleOp
> mCurrentOp
;
71 explicit FileHandleQueue(FileHandleThreadPool
* aFileHandleThreadPool
,
72 FileHandle
* aFileHandle
);
74 void Enqueue(FileHandleOp
* aFileHandleOp
);
81 ~FileHandleQueue() = default;
86 struct FileHandleThreadPool::DelayedEnqueueInfo
{
87 RefPtr
<FileHandle
> mFileHandle
;
88 RefPtr
<FileHandleOp
> mFileHandleOp
;
92 class FileHandleThreadPool::DirectoryInfo
{
93 friend class FileHandleThreadPool
;
95 RefPtr
<FileHandleThreadPool
> mOwningFileHandleThreadPool
;
96 nsTArray
<RefPtr
<FileHandleQueue
>> mFileHandleQueues
;
97 nsTArray
<DelayedEnqueueInfo
> mDelayedEnqueueInfos
;
98 nsTHashSet
<nsString
> mFilesReading
;
99 nsTHashSet
<nsString
> mFilesWriting
;
102 FileHandleQueue
* CreateFileHandleQueue(FileHandle
* aFileHandle
);
104 FileHandleQueue
* GetFileHandleQueue(FileHandle
* aFileHandle
);
106 void RemoveFileHandleQueue(FileHandle
* aFileHandle
);
108 bool HasRunningFileHandles() { return !mFileHandleQueues
.IsEmpty(); }
110 DelayedEnqueueInfo
* CreateDelayedEnqueueInfo(FileHandle
* aFileHandle
,
111 FileHandleOp
* aFileHandleOp
,
114 void LockFileForReading(const nsAString
& aFileName
) {
115 mFilesReading
.Insert(aFileName
);
118 void LockFileForWriting(const nsAString
& aFileName
) {
119 mFilesWriting
.Insert(aFileName
);
122 bool IsFileLockedForReading(const nsAString
& aFileName
) {
123 return mFilesReading
.Contains(aFileName
);
126 bool IsFileLockedForWriting(const nsAString
& aFileName
) {
127 return mFilesWriting
.Contains(aFileName
);
131 explicit DirectoryInfo(FileHandleThreadPool
* aFileHandleThreadPool
)
132 : mOwningFileHandleThreadPool(aFileHandleThreadPool
) {}
135 struct FileHandleThreadPool::StoragesCompleteCallback final
{
136 friend class DefaultDelete
<StoragesCompleteCallback
>;
138 nsTArray
<nsCString
> mDirectoryIds
;
139 nsCOMPtr
<nsIRunnable
> mCallback
;
141 StoragesCompleteCallback(nsTArray
<nsCString
>&& aDatabaseIds
,
142 nsIRunnable
* aCallback
);
145 ~StoragesCompleteCallback();
148 /******************************************************************************
149 * Actor class declarations
150 ******************************************************************************/
152 class FileHandle
: public PBackgroundFileHandleParent
{
153 friend class BackgroundMutableFileParentBase
;
157 RefPtr
<BackgroundMutableFileParentBase
> mMutableFile
;
158 nsCOMPtr
<nsISupports
> mStream
;
159 uint64_t mActiveRequestCount
;
160 FileHandleStorage mStorage
;
161 Atomic
<bool> mInvalidatedOnAnyThread
;
164 bool mActorDestroyed
;
167 bool mFinishOrAbortReceived
;
168 bool mFinishedOrAborted
;
172 nsCOMPtr
<nsIEventTarget
> mThreadPoolEventTarget
;
176 void AssertIsOnThreadPool() const;
178 bool IsActorDestroyed() const {
179 AssertIsOnBackgroundThread();
181 return mActorDestroyed
;
184 // Must be called on the background thread.
185 bool IsInvalidated() const {
186 MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
187 MOZ_ASSERT_IF(mInvalidated
, mAborted
);
192 // May be called on any thread, but is more expensive than IsInvalidated().
193 bool IsInvalidatedOnAnyThread() const { return mInvalidatedOnAnyThread
; }
196 AssertIsOnBackgroundThread();
198 mHasBeenActive
= true;
201 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::FileHandle
)
203 nsresult
GetOrCreateStream(nsISupports
** aStream
);
205 void Abort(bool aForce
);
207 FileHandleStorage
Storage() const { return mStorage
; }
209 FileMode
Mode() const { return mMode
; }
211 BackgroundMutableFileParentBase
* GetMutableFile() const {
212 AssertIsOnBackgroundThread();
213 MOZ_ASSERT(mMutableFile
);
218 bool IsAborted() const {
219 AssertIsOnBackgroundThread();
224 PBackgroundParent
* GetBackgroundParent() const {
225 AssertIsOnBackgroundThread();
226 MOZ_ASSERT(!IsActorDestroyed());
228 return GetMutableFile()->GetBackgroundParent();
231 void NoteActiveRequest();
233 void NoteFinishedRequest();
238 // This constructor is only called by BackgroundMutableFileParentBase.
239 FileHandle(BackgroundMutableFileParentBase
* aMutableFile
, FileMode aMode
);
241 // Reference counted.
244 void MaybeFinishOrAbort() {
245 AssertIsOnBackgroundThread();
247 // If we've already finished or aborted then there's nothing else to do.
248 if (mFinishedOrAborted
) {
252 // If there are active requests then we have to wait for those requests to
253 // complete (see NoteFinishedRequest).
254 if (mActiveRequestCount
) {
258 // If we haven't yet received a finish or abort message then there could be
259 // additional requests coming so we should wait unless we're being forced to
261 if (!mFinishOrAbortReceived
&& !mForceAborted
) {
268 void SendCompleteNotification(bool aAborted
);
270 bool VerifyRequestParams(const FileRequestParams
& aParams
) const;
272 bool VerifyRequestData(const FileRequestData
& aData
) const;
274 void FinishOrAbort();
276 // IPDL methods are only called by IPDL.
277 virtual void ActorDestroy(ActorDestroyReason aWhy
) override
;
279 virtual mozilla::ipc::IPCResult
RecvDeleteMe() override
;
281 virtual mozilla::ipc::IPCResult
RecvFinish() override
;
283 virtual mozilla::ipc::IPCResult
RecvAbort() override
;
285 virtual PBackgroundFileRequestParent
* AllocPBackgroundFileRequestParent(
286 const FileRequestParams
& aParams
) override
;
288 virtual mozilla::ipc::IPCResult
RecvPBackgroundFileRequestConstructor(
289 PBackgroundFileRequestParent
* aActor
,
290 const FileRequestParams
& aParams
) override
;
292 virtual bool DeallocPBackgroundFileRequestParent(
293 PBackgroundFileRequestParent
* aActor
) override
;
298 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
299 RefPtr
<FileHandle
> mFileHandle
;
305 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileHandleOp
)
307 void AssertIsOnOwningThread() const {
308 AssertIsOnBackgroundThread();
309 MOZ_ASSERT(mOwningEventTarget
);
310 DebugOnly
<bool> current
;
311 MOZ_ASSERT(NS_SUCCEEDED(mOwningEventTarget
->IsOnCurrentThread(¤t
)));
315 nsIEventTarget
* OwningThread() const { return mOwningEventTarget
; }
317 void AssertIsOnThreadPool() const {
318 MOZ_ASSERT(mFileHandle
);
319 mFileHandle
->AssertIsOnThreadPool();
324 virtual void RunOnThreadPool() = 0;
326 virtual void RunOnOwningThread() = 0;
329 FileHandleOp(FileHandle
* aFileHandle
)
330 : mOwningEventTarget(GetCurrentSerialEventTarget()),
331 mFileHandle(aFileHandle
)
337 AssertIsOnOwningThread();
338 MOZ_ASSERT(aFileHandle
);
341 virtual ~FileHandleOp() = default;
344 class FileHandle::FinishOp
: public FileHandleOp
{
345 friend class FileHandle
;
350 FinishOp(FileHandle
* aFileHandle
, bool aAborted
)
351 : FileHandleOp(aFileHandle
), mAborted(aAborted
) {
352 MOZ_ASSERT(aFileHandle
);
355 ~FinishOp() = default;
357 virtual void RunOnThreadPool() override
;
359 virtual void RunOnOwningThread() override
;
362 class NormalFileHandleOp
: public FileHandleOp
,
363 public PBackgroundFileRequestParent
{
364 nsresult mResultCode
;
365 Atomic
<bool> mOperationMayProceed
;
366 bool mActorDestroyed
;
367 const bool mFileHandleIsAborted
;
374 nsCOMPtr
<nsISupports
> mFileStream
;
377 void NoteActorDestroyed() {
378 AssertIsOnOwningThread();
380 mActorDestroyed
= true;
381 mOperationMayProceed
= false;
384 bool IsActorDestroyed() const {
385 AssertIsOnOwningThread();
387 return mActorDestroyed
;
390 // May be called on any thread, but you should call IsActorDestroyed() if
391 // you know you're on the background thread because it is slightly faster.
392 bool OperationMayProceed() const { return mOperationMayProceed
; }
394 // May be overridden by subclasses if they need to perform work on the
395 // background thread before being enqueued. Returning false will kill the
396 // child actors and prevent enqueue.
397 virtual bool Init(FileHandle
* aFileHandle
);
399 // This callback will be called on the background thread before releasing the
400 // final reference to this request object. Subclasses may perform any
401 // additional cleanup here but must always call the base class implementation.
402 virtual void Cleanup();
405 NormalFileHandleOp(FileHandle
* aFileHandle
)
406 : FileHandleOp(aFileHandle
),
408 mOperationMayProceed(true),
409 mActorDestroyed(false),
410 mFileHandleIsAborted(aFileHandle
->IsAborted())
416 MOZ_ASSERT(aFileHandle
);
419 virtual ~NormalFileHandleOp();
421 // Must be overridden in subclasses. Called on the target thread to allow the
422 // subclass to perform necessary file operations. A successful return value
423 // will trigger a SendSuccessResult callback on the background thread while
424 // a failure value will trigger a SendFailureResult callback.
425 virtual nsresult
DoFileWork(FileHandle
* aFileHandle
) = 0;
427 // Subclasses use this override to set the IPDL response value.
428 virtual void GetResponse(FileRequestResponse
& aResponse
) = 0;
431 nsresult
SendSuccessResult();
433 bool SendFailureResult(nsresult aResultCode
);
435 virtual void RunOnThreadPool() override
;
437 virtual void RunOnOwningThread() override
;
440 virtual void ActorDestroy(ActorDestroyReason aWhy
) override
;
443 class CopyFileHandleOp
: public NormalFileHandleOp
{
444 class ProgressRunnable
;
447 nsCOMPtr
<nsISupports
> mBufferStream
;
455 CopyFileHandleOp(FileHandle
* aFileHandle
)
456 : NormalFileHandleOp(aFileHandle
), mOffset(0), mSize(0), mRead(true) {}
458 virtual nsresult
DoFileWork(FileHandle
* aFileHandle
) override
;
460 virtual void Cleanup() override
;
463 class CopyFileHandleOp::ProgressRunnable final
: public Runnable
{
464 RefPtr
<CopyFileHandleOp
> mCopyFileHandleOp
;
466 uint64_t mProgressMax
;
469 ProgressRunnable(CopyFileHandleOp
* aCopyFileHandleOp
, uint64_t aProgress
,
470 uint64_t aProgressMax
)
471 : Runnable("dom::CopyFileHandleOp::ProgressRunnable"),
472 mCopyFileHandleOp(aCopyFileHandleOp
),
473 mProgress(aProgress
),
474 mProgressMax(aProgressMax
) {}
477 ~ProgressRunnable() = default;
482 class GetMetadataOp
: public NormalFileHandleOp
{
483 friend class FileHandle
;
485 const FileRequestGetMetadataParams mParams
;
488 FileRequestMetadata mMetadata
;
491 // Only created by FileHandle.
492 GetMetadataOp(FileHandle
* aFileHandle
, const FileRequestParams
& aParams
);
494 ~GetMetadataOp() = default;
496 virtual nsresult
DoFileWork(FileHandle
* aFileHandle
) override
;
498 virtual void GetResponse(FileRequestResponse
& aResponse
) override
;
501 class ReadOp final
: public CopyFileHandleOp
{
502 friend class FileHandle
;
504 const FileRequestReadParams mParams
;
507 // Only created by FileHandle.
508 ReadOp(FileHandle
* aFileHandle
, const FileRequestParams
& aParams
);
512 virtual bool Init(FileHandle
* aFileHandle
) override
;
514 virtual void GetResponse(FileRequestResponse
& aResponse
) override
;
517 class WriteOp final
: public CopyFileHandleOp
{
518 friend class FileHandle
;
520 const FileRequestWriteParams mParams
;
523 // Only created by FileHandle.
524 WriteOp(FileHandle
* aFileHandle
, const FileRequestParams
& aParams
);
526 ~WriteOp() = default;
528 virtual bool Init(FileHandle
* aFileHandle
) override
;
530 virtual void GetResponse(FileRequestResponse
& aResponse
) override
;
533 class TruncateOp final
: public NormalFileHandleOp
{
534 friend class FileHandle
;
536 const FileRequestTruncateParams mParams
;
539 // Only created by FileHandle.
540 TruncateOp(FileHandle
* aFileHandle
, const FileRequestParams
& aParams
);
542 ~TruncateOp() = default;
544 virtual nsresult
DoFileWork(FileHandle
* aFileHandle
) override
;
546 virtual void GetResponse(FileRequestResponse
& aResponse
) override
;
549 class FlushOp final
: public NormalFileHandleOp
{
550 friend class FileHandle
;
552 const FileRequestFlushParams mParams
;
555 // Only created by FileHandle.
556 FlushOp(FileHandle
* aFileHandle
, const FileRequestParams
& aParams
);
558 ~FlushOp() = default;
560 virtual nsresult
DoFileWork(FileHandle
* aFileHandle
) override
;
562 virtual void GetResponse(FileRequestResponse
& aResponse
) override
;
567 /*******************************************************************************
569 ******************************************************************************/
571 FileHandleThreadPool
* GetFileHandleThreadPoolFor(FileHandleStorage aStorage
) {
573 case FILE_HANDLE_STORAGE_IDB
:
574 return mozilla::dom::indexedDB::GetFileHandleThreadPool();
577 MOZ_CRASH("Bad file handle storage value!");
581 nsresult
ClampResultCode(nsresult aResultCode
) {
582 if (NS_SUCCEEDED(aResultCode
) ||
583 NS_ERROR_GET_MODULE(aResultCode
) == NS_ERROR_MODULE_DOM_FILEHANDLE
) {
587 NS_WARNING(nsPrintfCString("Converting non-filehandle error code (0x%" PRIX32
589 "NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR",
590 static_cast<uint32_t>(aResultCode
))
593 return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR
;
598 /*******************************************************************************
599 * FileHandleThreadPool implementation
600 ******************************************************************************/
602 FileHandleThreadPool::FileHandleThreadPool()
603 : mOwningEventTarget(GetCurrentSerialEventTarget()),
604 mShutdownRequested(false),
605 mShutdownComplete(false) {
606 AssertIsOnBackgroundThread();
607 MOZ_ASSERT(mOwningEventTarget
);
608 AssertIsOnOwningThread();
611 FileHandleThreadPool::~FileHandleThreadPool() {
612 AssertIsOnOwningThread();
613 MOZ_ASSERT(!mDirectoryInfos
.Count());
614 MOZ_ASSERT(mCompleteCallbacks
.IsEmpty());
615 MOZ_ASSERT(mShutdownRequested
);
616 MOZ_ASSERT(mShutdownComplete
);
620 already_AddRefed
<FileHandleThreadPool
> FileHandleThreadPool::Create() {
621 AssertIsOnBackgroundThread();
623 RefPtr
<FileHandleThreadPool
> fileHandleThreadPool
=
624 new FileHandleThreadPool();
625 fileHandleThreadPool
->AssertIsOnOwningThread();
627 if (NS_WARN_IF(NS_FAILED(fileHandleThreadPool
->Init()))) {
631 return fileHandleThreadPool
.forget();
636 void FileHandleThreadPool::AssertIsOnOwningThread() const {
637 MOZ_ASSERT(mOwningEventTarget
);
640 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget
->IsOnCurrentThread(¤t
));
644 nsIEventTarget
* FileHandleThreadPool::GetThreadPoolEventTarget() const {
645 AssertIsOnOwningThread();
646 MOZ_ASSERT(mThreadPool
);
653 void FileHandleThreadPool::Enqueue(FileHandle
* aFileHandle
,
654 FileHandleOp
* aFileHandleOp
, bool aFinish
) {
655 AssertIsOnOwningThread();
656 MOZ_ASSERT(aFileHandle
);
657 MOZ_ASSERT(!mShutdownRequested
);
659 BackgroundMutableFileParentBase
* mutableFile
= aFileHandle
->GetMutableFile();
661 const nsACString
& directoryId
= mutableFile
->DirectoryId();
662 const nsAString
& fileName
= mutableFile
->FileName();
663 bool modeIsWrite
= aFileHandle
->Mode() == FileMode::Readwrite
;
665 DirectoryInfo
* directoryInfo
=
669 [&] { return UniquePtr
<DirectoryInfo
>(new DirectoryInfo(this)); })
672 FileHandleQueue
* existingFileHandleQueue
=
673 directoryInfo
->GetFileHandleQueue(aFileHandle
);
675 if (existingFileHandleQueue
) {
676 existingFileHandleQueue
->Enqueue(aFileHandleOp
);
678 existingFileHandleQueue
->Finish();
683 bool lockedForReading
= directoryInfo
->IsFileLockedForReading(fileName
);
684 bool lockedForWriting
= directoryInfo
->IsFileLockedForWriting(fileName
);
687 if (!lockedForWriting
) {
688 directoryInfo
->LockFileForWriting(fileName
);
691 if (!lockedForReading
) {
692 directoryInfo
->LockFileForReading(fileName
);
696 if (lockedForWriting
|| (lockedForReading
&& modeIsWrite
)) {
697 directoryInfo
->CreateDelayedEnqueueInfo(aFileHandle
, aFileHandleOp
,
700 FileHandleQueue
* fileHandleQueue
=
701 directoryInfo
->CreateFileHandleQueue(aFileHandle
);
704 fileHandleQueue
->Enqueue(aFileHandleOp
);
706 fileHandleQueue
->Finish();
712 void FileHandleThreadPool::WaitForDirectoriesToComplete(
713 nsTArray
<nsCString
>&& aDirectoryIds
, nsIRunnable
* aCallback
) {
714 AssertIsOnOwningThread();
715 MOZ_ASSERT(!aDirectoryIds
.IsEmpty());
716 MOZ_ASSERT(aCallback
);
719 MakeUnique
<StoragesCompleteCallback
>(std::move(aDirectoryIds
), aCallback
);
721 if (!MaybeFireCallback(callback
.get())) {
722 mCompleteCallbacks
.AppendElement(std::move(callback
));
726 void FileHandleThreadPool::Shutdown() {
727 AssertIsOnOwningThread();
728 MOZ_ASSERT(!mShutdownRequested
);
729 MOZ_ASSERT(!mShutdownComplete
);
731 mShutdownRequested
= true;
734 MOZ_ASSERT(!mDirectoryInfos
.Count());
735 MOZ_ASSERT(mCompleteCallbacks
.IsEmpty());
737 mShutdownComplete
= true;
741 if (!mDirectoryInfos
.Count()) {
744 MOZ_ASSERT(mShutdownComplete
);
748 MOZ_ALWAYS_TRUE(SpinEventLoopUntil("FileHandleThreadPool::Shutdown"_ns
,
749 [&]() { return mShutdownComplete
; }));
752 nsresult
FileHandleThreadPool::Init() {
753 AssertIsOnOwningThread();
755 mThreadPool
= new nsThreadPool();
757 nsresult rv
= mThreadPool
->SetName("FileHandles"_ns
);
758 if (NS_WARN_IF(NS_FAILED(rv
))) {
762 rv
= mThreadPool
->SetThreadLimit(kThreadLimit
);
763 if (NS_WARN_IF(NS_FAILED(rv
))) {
767 rv
= mThreadPool
->SetIdleThreadLimit(kIdleThreadLimit
);
768 if (NS_WARN_IF(NS_FAILED(rv
))) {
772 rv
= mThreadPool
->SetIdleThreadTimeout(kIdleThreadTimeoutMs
);
773 if (NS_WARN_IF(NS_FAILED(rv
))) {
780 void FileHandleThreadPool::Cleanup() {
781 AssertIsOnOwningThread();
782 MOZ_ASSERT(mThreadPool
);
783 MOZ_ASSERT(mShutdownRequested
);
784 MOZ_ASSERT(!mShutdownComplete
);
785 MOZ_ASSERT(!mDirectoryInfos
.Count());
787 MOZ_ALWAYS_SUCCEEDS(mThreadPool
->Shutdown());
789 if (!mCompleteCallbacks
.IsEmpty()) {
790 // Run all callbacks manually now.
791 for (uint32_t count
= mCompleteCallbacks
.Length(), index
= 0; index
< count
;
793 UniquePtr
<StoragesCompleteCallback
> completeCallback
=
794 std::move(mCompleteCallbacks
[index
]);
795 MOZ_ASSERT(completeCallback
);
796 MOZ_ASSERT(completeCallback
->mCallback
);
798 Unused
<< completeCallback
->mCallback
->Run();
801 mCompleteCallbacks
.Clear();
803 // And make sure they get processed.
804 nsIThread
* currentThread
= NS_GetCurrentThread();
805 MOZ_ASSERT(currentThread
);
807 MOZ_ALWAYS_SUCCEEDS(NS_ProcessPendingEvents(currentThread
));
810 mShutdownComplete
= true;
813 void FileHandleThreadPool::FinishFileHandle(FileHandle
* aFileHandle
) {
814 AssertIsOnOwningThread();
815 MOZ_ASSERT(aFileHandle
);
817 BackgroundMutableFileParentBase
* mutableFile
= aFileHandle
->GetMutableFile();
818 const nsACString
& directoryId
= mutableFile
->DirectoryId();
820 DirectoryInfo
* directoryInfo
;
821 if (!mDirectoryInfos
.Get(directoryId
, &directoryInfo
)) {
822 NS_ERROR("We don't know anyting about this directory?!");
826 directoryInfo
->RemoveFileHandleQueue(aFileHandle
);
828 if (!directoryInfo
->HasRunningFileHandles()) {
829 mDirectoryInfos
.Remove(directoryId
);
831 // See if we need to fire any complete callbacks.
832 mCompleteCallbacks
.RemoveElementsBy(
833 [this](const UniquePtr
<StoragesCompleteCallback
>& callback
) {
834 return MaybeFireCallback(callback
.get());
837 if (mShutdownRequested
&& !mDirectoryInfos
.Count()) {
843 bool FileHandleThreadPool::MaybeFireCallback(
844 StoragesCompleteCallback
* aCallback
) {
845 AssertIsOnOwningThread();
846 MOZ_ASSERT(aCallback
);
847 MOZ_ASSERT(!aCallback
->mDirectoryIds
.IsEmpty());
848 MOZ_ASSERT(aCallback
->mCallback
);
850 for (uint32_t count
= aCallback
->mDirectoryIds
.Length(), index
= 0;
851 index
< count
; index
++) {
852 const nsCString
& directoryId
= aCallback
->mDirectoryIds
[index
];
853 MOZ_ASSERT(!directoryId
.IsEmpty());
855 if (mDirectoryInfos
.Get(directoryId
, nullptr)) {
860 aCallback
->mCallback
->Run();
864 FileHandleThreadPool::FileHandleQueue::FileHandleQueue(
865 FileHandleThreadPool
* aFileHandleThreadPool
, FileHandle
* aFileHandle
)
866 : Runnable("dom::FileHandleThreadPool::FileHandleQueue"),
867 mOwningFileHandleThreadPool(aFileHandleThreadPool
),
868 mFileHandle(aFileHandle
),
869 mShouldFinish(false) {
870 MOZ_ASSERT(aFileHandleThreadPool
);
871 aFileHandleThreadPool
->AssertIsOnOwningThread();
872 MOZ_ASSERT(aFileHandle
);
875 void FileHandleThreadPool::FileHandleQueue::Enqueue(
876 FileHandleOp
* aFileHandleOp
) {
877 MOZ_ASSERT(!mShouldFinish
, "Enqueue called after Finish!");
879 mQueue
.AppendElement(aFileHandleOp
);
884 void FileHandleThreadPool::FileHandleQueue::Finish() {
885 MOZ_ASSERT(!mShouldFinish
, "Finish called more than once!");
887 mShouldFinish
= true;
890 void FileHandleThreadPool::FileHandleQueue::ProcessQueue() {
895 if (mQueue
.IsEmpty()) {
897 mOwningFileHandleThreadPool
->FinishFileHandle(mFileHandle
);
899 // Make sure this is released on this thread.
900 mOwningFileHandleThreadPool
= nullptr;
906 mCurrentOp
= mQueue
[0];
907 mQueue
.RemoveElementAt(0);
909 nsCOMPtr
<nsIThreadPool
> threadPool
= mOwningFileHandleThreadPool
->mThreadPool
;
910 MOZ_ASSERT(threadPool
);
912 MOZ_ALWAYS_SUCCEEDS(threadPool
->Dispatch(this, NS_DISPATCH_NORMAL
));
916 FileHandleThreadPool::FileHandleQueue::Run() {
917 MOZ_ASSERT(mCurrentOp
);
919 if (IsOnBackgroundThread()) {
920 RefPtr
<FileHandleOp
> currentOp
;
922 mCurrentOp
.swap(currentOp
);
925 currentOp
->RunOnOwningThread();
927 mCurrentOp
->RunOnThreadPool();
929 nsCOMPtr
<nsIEventTarget
> backgroundThread
= mCurrentOp
->OwningThread();
931 MOZ_ALWAYS_SUCCEEDS(backgroundThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
937 auto FileHandleThreadPool::DirectoryInfo::CreateFileHandleQueue(
938 FileHandle
* aFileHandle
) -> FileHandleQueue
* {
939 RefPtr
<FileHandleQueue
>* fileHandleQueue
= mFileHandleQueues
.AppendElement();
941 new FileHandleQueue(mOwningFileHandleThreadPool
, aFileHandle
);
942 return fileHandleQueue
->get();
945 auto FileHandleThreadPool::DirectoryInfo::GetFileHandleQueue(
946 FileHandle
* aFileHandle
) -> FileHandleQueue
* {
947 uint32_t count
= mFileHandleQueues
.Length();
948 for (uint32_t index
= 0; index
< count
; index
++) {
949 RefPtr
<FileHandleQueue
>& fileHandleQueue
= mFileHandleQueues
[index
];
950 if (fileHandleQueue
->mFileHandle
== aFileHandle
) {
951 return fileHandleQueue
;
957 void FileHandleThreadPool::DirectoryInfo::RemoveFileHandleQueue(
958 FileHandle
* aFileHandle
) {
959 for (uint32_t index
= 0; index
< mDelayedEnqueueInfos
.Length(); index
++) {
960 if (mDelayedEnqueueInfos
[index
].mFileHandle
== aFileHandle
) {
961 MOZ_ASSERT(!mDelayedEnqueueInfos
[index
].mFileHandleOp
, "Should be null!");
962 mDelayedEnqueueInfos
.RemoveElementAt(index
);
967 uint32_t fileHandleCount
= mFileHandleQueues
.Length();
969 // We can't just remove entries from lock hash tables, we have to rebuild
970 // them instead. Multiple FileHandle objects may lock the same file
971 // (one entry can represent multiple locks).
973 mFilesReading
.Clear();
974 mFilesWriting
.Clear();
976 for (uint32_t index
= 0, count
= fileHandleCount
; index
< count
; index
++) {
977 FileHandle
* fileHandle
= mFileHandleQueues
[index
]->mFileHandle
;
978 if (fileHandle
== aFileHandle
) {
979 MOZ_ASSERT(count
== fileHandleCount
, "More than one match?!");
981 mFileHandleQueues
.RemoveElementAt(index
);
988 const nsAString
& fileName
= fileHandle
->GetMutableFile()->FileName();
990 if (fileHandle
->Mode() == FileMode::Readwrite
) {
991 if (!IsFileLockedForWriting(fileName
)) {
992 LockFileForWriting(fileName
);
995 if (!IsFileLockedForReading(fileName
)) {
996 LockFileForReading(fileName
);
1001 MOZ_ASSERT(mFileHandleQueues
.Length() == fileHandleCount
- 1,
1002 "Didn't find the file handle we were looking for!");
1004 nsTArray
<DelayedEnqueueInfo
> delayedEnqueueInfos
=
1005 std::move(mDelayedEnqueueInfos
);
1007 for (uint32_t index
= 0; index
< delayedEnqueueInfos
.Length(); index
++) {
1008 DelayedEnqueueInfo
& delayedEnqueueInfo
= delayedEnqueueInfos
[index
];
1009 mOwningFileHandleThreadPool
->Enqueue(delayedEnqueueInfo
.mFileHandle
,
1010 delayedEnqueueInfo
.mFileHandleOp
,
1011 delayedEnqueueInfo
.mFinish
);
1015 auto FileHandleThreadPool::DirectoryInfo::CreateDelayedEnqueueInfo(
1016 FileHandle
* aFileHandle
, FileHandleOp
* aFileHandleOp
, bool aFinish
)
1017 -> DelayedEnqueueInfo
* {
1018 DelayedEnqueueInfo
* info
= mDelayedEnqueueInfos
.AppendElement();
1019 info
->mFileHandle
= aFileHandle
;
1020 info
->mFileHandleOp
= aFileHandleOp
;
1021 info
->mFinish
= aFinish
;
1025 FileHandleThreadPool::StoragesCompleteCallback::StoragesCompleteCallback(
1026 nsTArray
<nsCString
>&& aDirectoryIds
, nsIRunnable
* aCallback
)
1027 : mDirectoryIds(std::move(aDirectoryIds
)), mCallback(aCallback
) {
1028 AssertIsOnBackgroundThread();
1029 MOZ_ASSERT(!mDirectoryIds
.IsEmpty());
1030 MOZ_ASSERT(aCallback
);
1032 MOZ_COUNT_CTOR(FileHandleThreadPool::StoragesCompleteCallback
);
1035 FileHandleThreadPool::StoragesCompleteCallback::~StoragesCompleteCallback() {
1036 AssertIsOnBackgroundThread();
1038 MOZ_COUNT_DTOR(FileHandleThreadPool::StoragesCompleteCallback
);
1041 /*******************************************************************************
1042 * BackgroundMutableFileParentBase
1043 ******************************************************************************/
1045 BackgroundMutableFileParentBase::BackgroundMutableFileParentBase(
1046 FileHandleStorage aStorage
, const nsACString
& aDirectoryId
,
1047 const nsAString
& aFileName
, nsIFile
* aFile
)
1048 : mDirectoryId(aDirectoryId
),
1049 mFileName(aFileName
),
1051 mInvalidated(false),
1052 mActorWasAlive(false),
1053 mActorDestroyed(false),
1055 AssertIsOnBackgroundThread();
1056 MOZ_ASSERT(aStorage
!= FILE_HANDLE_STORAGE_MAX
);
1057 MOZ_ASSERT(!aDirectoryId
.IsEmpty());
1058 MOZ_ASSERT(!aFileName
.IsEmpty());
1062 BackgroundMutableFileParentBase::~BackgroundMutableFileParentBase() {
1063 MOZ_ASSERT_IF(mActorWasAlive
, mActorDestroyed
);
1066 void BackgroundMutableFileParentBase::Invalidate() {
1067 AssertIsOnBackgroundThread();
1069 class MOZ_STACK_CLASS Helper final
{
1071 static bool InvalidateFileHandles(nsTHashSet
<FileHandle
*>& aTable
) {
1072 AssertIsOnBackgroundThread();
1074 const uint32_t count
= aTable
.Count();
1079 nsTArray
<RefPtr
<FileHandle
>> fileHandles
;
1080 if (NS_WARN_IF(!fileHandles
.SetCapacity(count
, fallible
))) {
1084 // This can't fail, since we already reserved the required capacity.
1085 std::copy(aTable
.cbegin(), aTable
.cend(), MakeBackInserter(fileHandles
));
1087 for (uint32_t index
= 0; index
< count
; index
++) {
1088 RefPtr
<FileHandle
> fileHandle
= std::move(fileHandles
[index
]);
1089 MOZ_ASSERT(fileHandle
);
1091 fileHandle
->Invalidate();
1102 mInvalidated
= true;
1104 if (!Helper::InvalidateFileHandles(mFileHandles
)) {
1105 NS_WARNING("Failed to abort all file handles!");
1109 bool BackgroundMutableFileParentBase::RegisterFileHandle(
1110 FileHandle
* aFileHandle
) {
1111 AssertIsOnBackgroundThread();
1112 MOZ_ASSERT(aFileHandle
);
1113 MOZ_ASSERT(!mFileHandles
.Contains(aFileHandle
));
1114 MOZ_ASSERT(!mInvalidated
);
1116 if (NS_WARN_IF(!mFileHandles
.Insert(aFileHandle
, fallible
))) {
1120 if (mFileHandles
.Count() == 1) {
1127 void BackgroundMutableFileParentBase::UnregisterFileHandle(
1128 FileHandle
* aFileHandle
) {
1129 AssertIsOnBackgroundThread();
1130 MOZ_ASSERT(aFileHandle
);
1131 MOZ_ASSERT(mFileHandles
.Contains(aFileHandle
));
1133 mFileHandles
.Remove(aFileHandle
);
1135 if (!mFileHandles
.Count()) {
1136 NoteInactiveState();
1140 void BackgroundMutableFileParentBase::SetActorAlive() {
1141 AssertIsOnBackgroundThread();
1142 MOZ_ASSERT(!mActorWasAlive
);
1143 MOZ_ASSERT(!mActorDestroyed
);
1145 mActorWasAlive
= true;
1147 // This reference will be absorbed by IPDL and released when the actor is
1152 already_AddRefed
<nsISupports
> BackgroundMutableFileParentBase::CreateStream(
1154 AssertIsOnBackgroundThread();
1159 nsCOMPtr
<nsIInputStream
> stream
;
1160 rv
= NS_NewLocalFileInputStream(getter_AddRefs(stream
), mFile
, -1, -1,
1161 nsIFileInputStream::DEFER_OPEN
);
1162 if (NS_WARN_IF(NS_FAILED(rv
))) {
1165 return stream
.forget();
1168 nsCOMPtr
<nsIFileStream
> stream
;
1169 rv
= NS_NewLocalFileStream(getter_AddRefs(stream
), mFile
, -1, -1,
1170 nsIFileStream::DEFER_OPEN
);
1171 if (NS_WARN_IF(NS_FAILED(rv
))) {
1174 return stream
.forget();
1177 void BackgroundMutableFileParentBase::ActorDestroy(ActorDestroyReason aWhy
) {
1178 AssertIsOnBackgroundThread();
1179 MOZ_ASSERT(!mActorDestroyed
);
1181 mActorDestroyed
= true;
1183 if (!IsInvalidated()) {
1188 PBackgroundFileHandleParent
*
1189 BackgroundMutableFileParentBase::AllocPBackgroundFileHandleParent(
1190 const FileMode
& aMode
) {
1191 AssertIsOnBackgroundThread();
1193 if (NS_WARN_IF(aMode
!= FileMode::Readonly
&& aMode
!= FileMode::Readwrite
)) {
1194 MOZ_CRASH_UNLESS_FUZZING();
1198 RefPtr
<FileHandle
> fileHandle
= new FileHandle(this, aMode
);
1200 return fileHandle
.forget().take();
1203 mozilla::ipc::IPCResult
1204 BackgroundMutableFileParentBase::RecvPBackgroundFileHandleConstructor(
1205 PBackgroundFileHandleParent
* aActor
, const FileMode
& aMode
) {
1206 AssertIsOnBackgroundThread();
1208 MOZ_ASSERT(aMode
== FileMode::Readonly
|| aMode
== FileMode::Readwrite
);
1210 FileHandleThreadPool
* fileHandleThreadPool
=
1211 GetFileHandleThreadPoolFor(mStorage
);
1212 MOZ_ASSERT(fileHandleThreadPool
);
1214 auto* fileHandle
= static_cast<FileHandle
*>(aActor
);
1216 // Add a placeholder for this file handle immediately.
1217 fileHandleThreadPool
->Enqueue(fileHandle
, nullptr, false);
1219 fileHandle
->SetActive();
1221 if (NS_WARN_IF(!RegisterFileHandle(fileHandle
))) {
1222 fileHandle
->Abort(/* aForce */ false);
1229 bool BackgroundMutableFileParentBase::DeallocPBackgroundFileHandleParent(
1230 PBackgroundFileHandleParent
* aActor
) {
1231 AssertIsOnBackgroundThread();
1234 RefPtr
<FileHandle
> fileHandle
= dont_AddRef(static_cast<FileHandle
*>(aActor
));
1238 mozilla::ipc::IPCResult
BackgroundMutableFileParentBase::RecvDeleteMe() {
1239 AssertIsOnBackgroundThread();
1240 MOZ_ASSERT(!mActorDestroyed
);
1242 IProtocol
* mgr
= Manager();
1243 if (!PBackgroundMutableFileParent::Send__delete__(this)) {
1244 return IPC_FAIL_NO_REASON(mgr
);
1249 mozilla::ipc::IPCResult
BackgroundMutableFileParentBase::RecvGetFileId(
1251 AssertIsOnBackgroundThread();
1257 /*******************************************************************************
1259 ******************************************************************************/
1261 FileHandle::FileHandle(BackgroundMutableFileParentBase
* aMutableFile
,
1263 : mMutableFile(aMutableFile
),
1264 mActiveRequestCount(0),
1265 mStorage(aMutableFile
->Storage()),
1266 mInvalidatedOnAnyThread(false),
1268 mHasBeenActive(false),
1269 mActorDestroyed(false),
1270 mInvalidated(false),
1272 mFinishOrAbortReceived(false),
1273 mFinishedOrAborted(false),
1274 mForceAborted(false) {
1275 AssertIsOnBackgroundThread();
1276 MOZ_ASSERT(aMutableFile
);
1279 FileHandleThreadPool
* fileHandleThreadPool
=
1280 GetFileHandleThreadPoolFor(mStorage
);
1281 MOZ_ASSERT(fileHandleThreadPool
);
1283 mThreadPoolEventTarget
= fileHandleThreadPool
->GetThreadPoolEventTarget();
1287 FileHandle::~FileHandle() {
1288 MOZ_ASSERT(!mActiveRequestCount
);
1289 MOZ_ASSERT(mActorDestroyed
);
1290 MOZ_ASSERT_IF(mHasBeenActive
, mFinishedOrAborted
);
1293 void FileHandle::AssertIsOnThreadPool() const {
1294 MOZ_ASSERT(mThreadPoolEventTarget
);
1295 DebugOnly
<bool> current
;
1296 MOZ_ASSERT(NS_SUCCEEDED(mThreadPoolEventTarget
->IsOnCurrentThread(¤t
)));
1297 MOZ_ASSERT(current
);
1300 nsresult
FileHandle::GetOrCreateStream(nsISupports
** aStream
) {
1301 AssertIsOnBackgroundThread();
1304 nsCOMPtr
<nsISupports
> stream
=
1305 mMutableFile
->CreateStream(mMode
== FileMode::Readonly
);
1306 if (NS_WARN_IF(!stream
)) {
1307 return NS_ERROR_FAILURE
;
1310 stream
.swap(mStream
);
1313 nsCOMPtr
<nsISupports
> stream(mStream
);
1314 stream
.forget(aStream
);
1319 void FileHandle::Abort(bool aForce
) {
1320 AssertIsOnBackgroundThread();
1325 mForceAborted
= true;
1328 MaybeFinishOrAbort();
1331 void FileHandle::NoteActiveRequest() {
1332 AssertIsOnBackgroundThread();
1333 MOZ_ASSERT(mActiveRequestCount
< UINT64_MAX
);
1335 mActiveRequestCount
++;
1338 void FileHandle::NoteFinishedRequest() {
1339 AssertIsOnBackgroundThread();
1340 MOZ_ASSERT(mActiveRequestCount
);
1342 mActiveRequestCount
--;
1344 MaybeFinishOrAbort();
1347 void FileHandle::Invalidate() {
1348 AssertIsOnBackgroundThread();
1349 MOZ_ASSERT(mInvalidated
== mInvalidatedOnAnyThread
);
1351 if (!mInvalidated
) {
1352 mInvalidated
= true;
1353 mInvalidatedOnAnyThread
= true;
1355 Abort(/* aForce */ true);
1359 void FileHandle::SendCompleteNotification(bool aAborted
) {
1360 AssertIsOnBackgroundThread();
1362 if (!IsActorDestroyed()) {
1363 Unused
<< SendComplete(aAborted
);
1367 bool FileHandle::VerifyRequestParams(const FileRequestParams
& aParams
) const {
1368 AssertIsOnBackgroundThread();
1369 MOZ_ASSERT(aParams
.type() != FileRequestParams::T__None
);
1371 switch (aParams
.type()) {
1372 case FileRequestParams::TFileRequestGetMetadataParams
: {
1373 const FileRequestGetMetadataParams
& params
=
1374 aParams
.get_FileRequestGetMetadataParams();
1376 if (NS_WARN_IF(!params
.size() && !params
.lastModified())) {
1377 MOZ_CRASH_UNLESS_FUZZING();
1384 case FileRequestParams::TFileRequestReadParams
: {
1385 const FileRequestReadParams
& params
= aParams
.get_FileRequestReadParams();
1387 if (NS_WARN_IF(params
.offset() == UINT64_MAX
)) {
1388 MOZ_CRASH_UNLESS_FUZZING();
1392 if (NS_WARN_IF(!params
.size())) {
1393 MOZ_CRASH_UNLESS_FUZZING();
1397 if (NS_WARN_IF(params
.size() > UINT32_MAX
)) {
1398 MOZ_CRASH_UNLESS_FUZZING();
1405 case FileRequestParams::TFileRequestWriteParams
: {
1406 if (NS_WARN_IF(mMode
!= FileMode::Readwrite
)) {
1407 MOZ_CRASH_UNLESS_FUZZING();
1411 const FileRequestWriteParams
& params
=
1412 aParams
.get_FileRequestWriteParams();
1414 if (NS_WARN_IF(!params
.dataLength())) {
1415 MOZ_CRASH_UNLESS_FUZZING();
1419 if (NS_WARN_IF(!VerifyRequestData(params
.data()))) {
1420 MOZ_CRASH_UNLESS_FUZZING();
1427 case FileRequestParams::TFileRequestTruncateParams
: {
1428 if (NS_WARN_IF(mMode
!= FileMode::Readwrite
)) {
1429 MOZ_CRASH_UNLESS_FUZZING();
1433 const FileRequestTruncateParams
& params
=
1434 aParams
.get_FileRequestTruncateParams();
1436 if (NS_WARN_IF(params
.offset() == UINT64_MAX
)) {
1437 MOZ_CRASH_UNLESS_FUZZING();
1444 case FileRequestParams::TFileRequestFlushParams
: {
1445 if (NS_WARN_IF(mMode
!= FileMode::Readwrite
)) {
1446 MOZ_CRASH_UNLESS_FUZZING();
1454 MOZ_CRASH("Should never get here!");
1460 bool FileHandle::VerifyRequestData(const FileRequestData
& aData
) const {
1461 AssertIsOnBackgroundThread();
1462 MOZ_ASSERT(aData
.type() != FileRequestData::T__None
);
1464 switch (aData
.type()) {
1465 case FileRequestData::TFileRequestStringData
: {
1466 const FileRequestStringData
& data
= aData
.get_FileRequestStringData();
1468 if (NS_WARN_IF(data
.string().IsEmpty())) {
1469 MOZ_CRASH_UNLESS_FUZZING();
1476 case FileRequestData::TFileRequestBlobData
: {
1481 MOZ_CRASH("Should never get here!");
1487 void FileHandle::FinishOrAbort() {
1488 AssertIsOnBackgroundThread();
1489 MOZ_ASSERT(!mFinishedOrAborted
);
1491 mFinishedOrAborted
= true;
1493 if (!mHasBeenActive
) {
1497 RefPtr
<FinishOp
> finishOp
= new FinishOp(this, mAborted
);
1499 FileHandleThreadPool
* fileHandleThreadPool
=
1500 GetFileHandleThreadPoolFor(mStorage
);
1501 MOZ_ASSERT(fileHandleThreadPool
);
1503 fileHandleThreadPool
->Enqueue(this, finishOp
, true);
1506 void FileHandle::ActorDestroy(ActorDestroyReason aWhy
) {
1507 AssertIsOnBackgroundThread();
1509 MOZ_ASSERT(!mActorDestroyed
);
1511 mActorDestroyed
= true;
1513 if (!mFinishedOrAborted
) {
1516 mForceAborted
= true;
1518 MaybeFinishOrAbort();
1522 mozilla::ipc::IPCResult
FileHandle::RecvDeleteMe() {
1523 AssertIsOnBackgroundThread();
1524 MOZ_ASSERT(!IsActorDestroyed());
1526 IProtocol
* mgr
= Manager();
1527 if (!PBackgroundFileHandleParent::Send__delete__(this)) {
1528 return IPC_FAIL_NO_REASON(mgr
);
1533 mozilla::ipc::IPCResult
FileHandle::RecvFinish() {
1534 AssertIsOnBackgroundThread();
1536 if (NS_WARN_IF(mFinishOrAbortReceived
)) {
1537 MOZ_CRASH_UNLESS_FUZZING();
1538 return IPC_FAIL_NO_REASON(this);
1541 mFinishOrAbortReceived
= true;
1543 MaybeFinishOrAbort();
1547 mozilla::ipc::IPCResult
FileHandle::RecvAbort() {
1548 AssertIsOnBackgroundThread();
1550 if (NS_WARN_IF(mFinishOrAbortReceived
)) {
1551 MOZ_CRASH_UNLESS_FUZZING();
1552 return IPC_FAIL_NO_REASON(this);
1555 mFinishOrAbortReceived
= true;
1557 Abort(/* aForce */ false);
1561 PBackgroundFileRequestParent
* FileHandle::AllocPBackgroundFileRequestParent(
1562 const FileRequestParams
& aParams
) {
1563 AssertIsOnBackgroundThread();
1564 MOZ_ASSERT(aParams
.type() != FileRequestParams::T__None
);
1567 // Always verify parameters in DEBUG builds!
1568 bool trustParams
= false;
1570 PBackgroundParent
* backgroundActor
= GetBackgroundParent();
1571 MOZ_ASSERT(backgroundActor
);
1573 bool trustParams
= !BackgroundParent::IsOtherProcessActor(backgroundActor
);
1576 if (NS_WARN_IF(!trustParams
&& !VerifyRequestParams(aParams
))) {
1577 MOZ_CRASH_UNLESS_FUZZING();
1581 if (NS_WARN_IF(mFinishOrAbortReceived
)) {
1582 MOZ_CRASH_UNLESS_FUZZING();
1586 RefPtr
<NormalFileHandleOp
> actor
;
1588 switch (aParams
.type()) {
1589 case FileRequestParams::TFileRequestGetMetadataParams
:
1590 actor
= new GetMetadataOp(this, aParams
);
1593 case FileRequestParams::TFileRequestReadParams
:
1594 actor
= new ReadOp(this, aParams
);
1597 case FileRequestParams::TFileRequestWriteParams
:
1598 actor
= new WriteOp(this, aParams
);
1601 case FileRequestParams::TFileRequestTruncateParams
:
1602 actor
= new TruncateOp(this, aParams
);
1605 case FileRequestParams::TFileRequestFlushParams
:
1606 actor
= new FlushOp(this, aParams
);
1610 MOZ_CRASH("Should never get here!");
1615 // Transfer ownership to IPDL.
1616 return actor
.forget().take();
1619 mozilla::ipc::IPCResult
FileHandle::RecvPBackgroundFileRequestConstructor(
1620 PBackgroundFileRequestParent
* aActor
, const FileRequestParams
& aParams
) {
1621 AssertIsOnBackgroundThread();
1623 MOZ_ASSERT(aParams
.type() != FileRequestParams::T__None
);
1625 auto* op
= static_cast<NormalFileHandleOp
*>(aActor
);
1627 if (NS_WARN_IF(!op
->Init(this))) {
1629 return IPC_FAIL_NO_REASON(this);
1636 bool FileHandle::DeallocPBackgroundFileRequestParent(
1637 PBackgroundFileRequestParent
* aActor
) {
1638 AssertIsOnBackgroundThread();
1641 // Transfer ownership back from IPDL.
1642 RefPtr
<NormalFileHandleOp
> actor
=
1643 dont_AddRef(static_cast<NormalFileHandleOp
*>(aActor
));
1647 /*******************************************************************************
1648 * Local class implementations
1649 ******************************************************************************/
1651 void FileHandleOp::Enqueue() {
1652 AssertIsOnOwningThread();
1654 FileHandleThreadPool
* fileHandleThreadPool
=
1655 GetFileHandleThreadPoolFor(mFileHandle
->Storage());
1656 MOZ_ASSERT(fileHandleThreadPool
);
1658 fileHandleThreadPool
->Enqueue(mFileHandle
, this, false);
1664 mFileHandle
->NoteActiveRequest();
1667 void FileHandle::FinishOp::RunOnThreadPool() {
1668 AssertIsOnThreadPool();
1669 MOZ_ASSERT(mFileHandle
);
1671 nsCOMPtr
<nsISupports
>& stream
= mFileHandle
->mStream
;
1677 nsCOMPtr
<nsIInputStream
> inputStream
= do_QueryInterface(stream
);
1678 MOZ_ASSERT(inputStream
);
1680 MOZ_ALWAYS_SUCCEEDS(inputStream
->Close());
1685 void FileHandle::FinishOp::RunOnOwningThread() {
1686 AssertIsOnOwningThread();
1687 MOZ_ASSERT(mFileHandle
);
1689 mFileHandle
->SendCompleteNotification(mAborted
);
1691 mFileHandle
->GetMutableFile()->UnregisterFileHandle(mFileHandle
);
1693 mFileHandle
= nullptr;
1696 NormalFileHandleOp::~NormalFileHandleOp() {
1697 MOZ_ASSERT(!mFileHandle
,
1698 "NormalFileHandleOp::Cleanup() was not called by a subclass!");
1701 bool NormalFileHandleOp::Init(FileHandle
* aFileHandle
) {
1702 AssertIsOnOwningThread();
1703 MOZ_ASSERT(aFileHandle
);
1705 nsresult rv
= aFileHandle
->GetOrCreateStream(getter_AddRefs(mFileStream
));
1706 if (NS_WARN_IF(NS_FAILED(rv
))) {
1713 void NormalFileHandleOp::Cleanup() {
1714 AssertIsOnOwningThread();
1715 MOZ_ASSERT(mFileHandle
);
1716 MOZ_ASSERT_IF(mEnqueued
&& !IsActorDestroyed(), mResponseSent
);
1718 mFileHandle
= nullptr;
1721 nsresult
NormalFileHandleOp::SendSuccessResult() {
1722 AssertIsOnOwningThread();
1724 if (!IsActorDestroyed()) {
1725 FileRequestResponse response
;
1726 GetResponse(response
);
1728 MOZ_ASSERT(response
.type() != FileRequestResponse::T__None
);
1730 if (response
.type() == FileRequestResponse::Tnsresult
) {
1731 MOZ_ASSERT(NS_FAILED(response
.get_nsresult()));
1733 return response
.get_nsresult();
1737 !PBackgroundFileRequestParent::Send__delete__(this, response
))) {
1738 return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR
;
1743 mResponseSent
= true;
1749 bool NormalFileHandleOp::SendFailureResult(nsresult aResultCode
) {
1750 AssertIsOnBackgroundThread();
1751 MOZ_ASSERT(NS_FAILED(aResultCode
));
1753 bool result
= false;
1755 if (!IsActorDestroyed()) {
1756 result
= PBackgroundFileRequestParent::Send__delete__(
1757 this, ClampResultCode(aResultCode
));
1761 mResponseSent
= true;
1767 void NormalFileHandleOp::RunOnThreadPool() {
1768 AssertIsOnThreadPool();
1769 MOZ_ASSERT(mFileHandle
);
1770 MOZ_ASSERT(NS_SUCCEEDED(mResultCode
));
1772 // There are several cases where we don't actually have to to any work here.
1774 if (mFileHandleIsAborted
) {
1775 // This transaction is already set to be aborted.
1776 mResultCode
= NS_ERROR_DOM_FILEHANDLE_ABORT_ERR
;
1777 } else if (mFileHandle
->IsInvalidatedOnAnyThread()) {
1778 // This file handle is being invalidated.
1779 mResultCode
= NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR
;
1780 } else if (!OperationMayProceed()) {
1781 // The operation was canceled in some way, likely because the child process
1783 mResultCode
= NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR
;
1785 nsresult rv
= DoFileWork(mFileHandle
);
1786 if (NS_FAILED(rv
)) {
1792 void NormalFileHandleOp::RunOnOwningThread() {
1793 AssertIsOnOwningThread();
1794 MOZ_ASSERT(mFileHandle
);
1796 if (NS_WARN_IF(IsActorDestroyed())) {
1797 // Don't send any notifications if the actor was destroyed already.
1798 if (NS_SUCCEEDED(mResultCode
)) {
1799 mResultCode
= NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR
;
1802 if (mFileHandle
->IsInvalidated()) {
1803 mResultCode
= NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR
;
1804 } else if (mFileHandle
->IsAborted()) {
1805 // Aborted file handles always see their requests fail with ABORT_ERR,
1806 // even if the request succeeded or failed with another error.
1807 mResultCode
= NS_ERROR_DOM_FILEHANDLE_ABORT_ERR
;
1808 } else if (NS_SUCCEEDED(mResultCode
)) {
1809 // This may release the IPDL reference.
1810 mResultCode
= SendSuccessResult();
1813 if (NS_FAILED(mResultCode
)) {
1814 // This should definitely release the IPDL reference.
1815 if (!SendFailureResult(mResultCode
)) {
1816 // Abort the file handle.
1817 mFileHandle
->Abort(/* aForce */ false);
1822 mFileHandle
->NoteFinishedRequest();
1827 void NormalFileHandleOp::ActorDestroy(ActorDestroyReason aWhy
) {
1828 AssertIsOnOwningThread();
1830 NoteActorDestroyed();
1833 nsresult
CopyFileHandleOp::DoFileWork(FileHandle
* aFileHandle
) {
1834 AssertIsOnThreadPool();
1836 nsCOMPtr
<nsIInputStream
> inputStream
;
1837 nsCOMPtr
<nsIOutputStream
> outputStream
;
1840 inputStream
= do_QueryInterface(mFileStream
);
1841 outputStream
= do_QueryInterface(mBufferStream
);
1843 inputStream
= do_QueryInterface(mBufferStream
);
1844 outputStream
= do_QueryInterface(mFileStream
);
1847 MOZ_ASSERT(inputStream
);
1848 MOZ_ASSERT(outputStream
);
1850 nsCOMPtr
<nsISeekableStream
> seekableStream
= do_QueryInterface(mFileStream
);
1854 if (seekableStream
) {
1855 if (mOffset
== UINT64_MAX
) {
1856 rv
= seekableStream
->Seek(nsISeekableStream::NS_SEEK_END
, 0);
1858 rv
= seekableStream
->Seek(nsISeekableStream::NS_SEEK_SET
, mOffset
);
1860 if (NS_WARN_IF(NS_FAILED(rv
))) {
1868 char copyBuffer
[kStreamCopyBlockSize
];
1870 uint64_t max
= mSize
- mOffset
;
1875 uint32_t count
= sizeof(copyBuffer
);
1881 rv
= inputStream
->Read(copyBuffer
, count
, &numRead
);
1882 if (NS_WARN_IF(NS_FAILED(rv
))) {
1891 rv
= outputStream
->Write(copyBuffer
, numRead
, &numWrite
);
1892 if (rv
== NS_ERROR_FILE_NO_DEVICE_SPACE
) {
1893 rv
= NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR
;
1895 if (NS_WARN_IF(NS_FAILED(rv
))) {
1899 if (NS_WARN_IF(numWrite
!= numRead
)) {
1900 return NS_ERROR_FAILURE
;
1903 mOffset
+= numWrite
;
1905 nsCOMPtr
<nsIRunnable
> runnable
= new ProgressRunnable(this, mOffset
, mSize
);
1907 mOwningEventTarget
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
1910 if (mOffset
< mSize
) {
1911 // end-of-file reached
1912 return NS_ERROR_FAILURE
;
1914 MOZ_ASSERT(mOffset
== mSize
);
1917 MOZ_ALWAYS_SUCCEEDS(outputStream
->Close());
1919 MOZ_ALWAYS_SUCCEEDS(inputStream
->Close());
1925 void CopyFileHandleOp::Cleanup() {
1926 AssertIsOnOwningThread();
1928 mBufferStream
= nullptr;
1930 NormalFileHandleOp::Cleanup();
1934 CopyFileHandleOp::ProgressRunnable::Run() {
1935 AssertIsOnBackgroundThread();
1937 Unused
<< mCopyFileHandleOp
->SendProgress(mProgress
, mProgressMax
);
1939 mCopyFileHandleOp
= nullptr;
1944 GetMetadataOp::GetMetadataOp(FileHandle
* aFileHandle
,
1945 const FileRequestParams
& aParams
)
1946 : NormalFileHandleOp(aFileHandle
),
1947 mParams(aParams
.get_FileRequestGetMetadataParams()) {
1948 MOZ_ASSERT(aParams
.type() ==
1949 FileRequestParams::TFileRequestGetMetadataParams
);
1952 nsresult
GetMetadataOp::DoFileWork(FileHandle
* aFileHandle
) {
1953 AssertIsOnThreadPool();
1957 if (mFileHandle
->Mode() == FileMode::Readwrite
) {
1958 // Force a flush (so all pending writes are flushed to the disk and file
1959 // metadata is updated too).
1961 nsCOMPtr
<nsIOutputStream
> ostream
= do_QueryInterface(mFileStream
);
1962 MOZ_ASSERT(ostream
);
1964 rv
= ostream
->Flush();
1965 if (NS_WARN_IF(NS_FAILED(rv
))) {
1970 nsCOMPtr
<nsIFileMetadata
> metadata
= do_QueryInterface(mFileStream
);
1971 MOZ_ASSERT(metadata
);
1973 if (mParams
.size()) {
1975 rv
= metadata
->GetSize(&size
);
1976 if (NS_WARN_IF(NS_FAILED(rv
))) {
1980 if (NS_WARN_IF(size
< 0)) {
1981 return NS_ERROR_FAILURE
;
1984 mMetadata
.size() = Some(uint64_t(size
));
1986 mMetadata
.size() = Nothing();
1989 if (mParams
.lastModified()) {
1990 int64_t lastModified
;
1991 rv
= metadata
->GetLastModified(&lastModified
);
1992 if (NS_WARN_IF(NS_FAILED(rv
))) {
1996 mMetadata
.lastModified() = Some(lastModified
);
1998 mMetadata
.lastModified() = Nothing();
2004 void GetMetadataOp::GetResponse(FileRequestResponse
& aResponse
) {
2005 AssertIsOnOwningThread();
2007 aResponse
= FileRequestGetMetadataResponse(mMetadata
);
2010 ReadOp::ReadOp(FileHandle
* aFileHandle
, const FileRequestParams
& aParams
)
2011 : CopyFileHandleOp(aFileHandle
),
2012 mParams(aParams
.get_FileRequestReadParams()) {
2013 MOZ_ASSERT(aParams
.type() == FileRequestParams::TFileRequestReadParams
);
2016 bool ReadOp::Init(FileHandle
* aFileHandle
) {
2017 AssertIsOnOwningThread();
2018 MOZ_ASSERT(aFileHandle
);
2020 if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle
))) {
2024 mBufferStream
= MemoryOutputStream::Create(mParams
.size());
2025 if (NS_WARN_IF(!mBufferStream
)) {
2029 mOffset
= mParams
.offset();
2030 mSize
= mParams
.size();
2036 void ReadOp::GetResponse(FileRequestResponse
& aResponse
) {
2037 AssertIsOnOwningThread();
2039 auto* stream
= static_cast<MemoryOutputStream
*>(mBufferStream
.get());
2041 aResponse
= FileRequestReadResponse(stream
->Data());
2044 WriteOp::WriteOp(FileHandle
* aFileHandle
, const FileRequestParams
& aParams
)
2045 : CopyFileHandleOp(aFileHandle
),
2046 mParams(aParams
.get_FileRequestWriteParams()) {
2047 MOZ_ASSERT(aParams
.type() == FileRequestParams::TFileRequestWriteParams
);
2050 bool WriteOp::Init(FileHandle
* aFileHandle
) {
2051 AssertIsOnOwningThread();
2052 MOZ_ASSERT(aFileHandle
);
2054 if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle
))) {
2058 nsCOMPtr
<nsIInputStream
> inputStream
;
2060 const FileRequestData
& data
= mParams
.data();
2061 switch (data
.type()) {
2062 case FileRequestData::TFileRequestStringData
: {
2063 const FileRequestStringData
& stringData
=
2064 data
.get_FileRequestStringData();
2066 const nsCString
& string
= stringData
.string();
2069 NS_NewCStringInputStream(getter_AddRefs(inputStream
), string
);
2070 if (NS_WARN_IF(NS_FAILED(rv
))) {
2076 case FileRequestData::TFileRequestBlobData
: {
2077 const FileRequestBlobData
& blobData
= data
.get_FileRequestBlobData();
2079 RefPtr
<BlobImpl
> blobImpl
= IPCBlobUtils::Deserialize(blobData
.blob());
2080 if (NS_WARN_IF(!blobImpl
)) {
2084 IgnoredErrorResult rv
;
2085 blobImpl
->CreateInputStream(getter_AddRefs(inputStream
), rv
);
2086 if (NS_WARN_IF(rv
.Failed())) {
2094 MOZ_CRASH("Should never get here!");
2097 mBufferStream
= inputStream
;
2098 mOffset
= mParams
.offset();
2099 mSize
= mParams
.dataLength();
2105 void WriteOp::GetResponse(FileRequestResponse
& aResponse
) {
2106 AssertIsOnOwningThread();
2107 aResponse
= FileRequestWriteResponse();
2110 TruncateOp::TruncateOp(FileHandle
* aFileHandle
,
2111 const FileRequestParams
& aParams
)
2112 : NormalFileHandleOp(aFileHandle
),
2113 mParams(aParams
.get_FileRequestTruncateParams()) {
2114 MOZ_ASSERT(aParams
.type() == FileRequestParams::TFileRequestTruncateParams
);
2117 nsresult
TruncateOp::DoFileWork(FileHandle
* aFileHandle
) {
2118 AssertIsOnThreadPool();
2120 nsCOMPtr
<nsISeekableStream
> sstream
= do_QueryInterface(mFileStream
);
2121 MOZ_ASSERT(sstream
);
2123 if (mParams
.offset()) {
2124 nsCOMPtr
<nsIFileMetadata
> fileMetadata
= do_QueryInterface(mFileStream
);
2125 MOZ_ASSERT(fileMetadata
);
2128 nsresult rv
= fileMetadata
->GetSize(&size
);
2129 if (NS_WARN_IF(NS_FAILED(rv
))) {
2132 MOZ_ASSERT(size
>= 0);
2134 if (mParams
.offset() > static_cast<uint64_t>(size
)) {
2135 // Cannot extend the size of a file through truncate.
2136 return NS_ERROR_DOM_INVALID_MODIFICATION_ERR
;
2140 // XXX If we allowed truncate to extend the file size, we would to ensure that
2141 // the quota limit is checked, e.g. by making FileQuotaStream override Seek.
2142 nsresult rv
= sstream
->Seek(nsISeekableStream::NS_SEEK_SET
, mParams
.offset());
2143 if (NS_WARN_IF(NS_FAILED(rv
))) {
2147 rv
= sstream
->SetEOF();
2148 if (NS_WARN_IF(NS_FAILED(rv
))) {
2155 void TruncateOp::GetResponse(FileRequestResponse
& aResponse
) {
2156 AssertIsOnOwningThread();
2157 aResponse
= FileRequestTruncateResponse();
2160 FlushOp::FlushOp(FileHandle
* aFileHandle
, const FileRequestParams
& aParams
)
2161 : NormalFileHandleOp(aFileHandle
),
2162 mParams(aParams
.get_FileRequestFlushParams()) {
2163 MOZ_ASSERT(aParams
.type() == FileRequestParams::TFileRequestFlushParams
);
2166 nsresult
FlushOp::DoFileWork(FileHandle
* aFileHandle
) {
2167 AssertIsOnThreadPool();
2169 nsCOMPtr
<nsIOutputStream
> ostream
= do_QueryInterface(mFileStream
);
2170 MOZ_ASSERT(ostream
);
2172 nsresult rv
= ostream
->Flush();
2173 if (NS_WARN_IF(NS_FAILED(rv
))) {
2180 void FlushOp::GetResponse(FileRequestResponse
& aResponse
) {
2181 AssertIsOnOwningThread();
2182 aResponse
= FileRequestFlushResponse();
2185 } // namespace mozilla::dom