diff --git a/dom/file/FileBlobImpl.cpp b/dom/file/FileBlobImpl.cpp index 1121696edf97..b9b03aa119e0 100644 --- a/dom/file/FileBlobImpl.cpp +++ b/dom/file/FileBlobImpl.cpp @@ -26,6 +26,7 @@ FileBlobImpl::FileBlobImpl(nsIFile* aFile) : BaseBlobImpl(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX) , mFile(aFile) , mWholeFile(true) + , mFileId(-1) { MOZ_ASSERT(mFile, "must have file"); MOZ_ASSERT(XRE_IsParentProcess()); @@ -40,6 +41,7 @@ FileBlobImpl::FileBlobImpl(const nsAString& aName, : BaseBlobImpl(aName, aContentType, aLength, UINT64_MAX) , mFile(aFile) , mWholeFile(true) + , mFileId(-1) { MOZ_ASSERT(mFile, "must have file"); MOZ_ASSERT(XRE_IsParentProcess()); @@ -52,6 +54,7 @@ FileBlobImpl::FileBlobImpl(const nsAString& aName, : BaseBlobImpl(aName, aContentType, aLength, aLastModificationDate) , mFile(aFile) , mWholeFile(true) + , mFileId(-1) { MOZ_ASSERT(mFile, "must have file"); MOZ_ASSERT(XRE_IsParentProcess()); @@ -62,6 +65,7 @@ FileBlobImpl::FileBlobImpl(nsIFile* aFile, const nsAString& aName, : BaseBlobImpl(aName, aContentType, UINT64_MAX, INT64_MAX) , mFile(aFile) , mWholeFile(true) + , mFileId(-1) { MOZ_ASSERT(mFile, "must have file"); MOZ_ASSERT(XRE_IsParentProcess()); @@ -76,6 +80,7 @@ FileBlobImpl::FileBlobImpl(const FileBlobImpl* aOther, uint64_t aStart, : BaseBlobImpl(aContentType, aOther->mStart + aStart, aLength) , mFile(aOther->mFile) , mWholeFile(false) + , mFileId(-1) { MOZ_ASSERT(mFile, "must have file"); MOZ_ASSERT(XRE_IsParentProcess()); diff --git a/dom/file/FileBlobImpl.h b/dom/file/FileBlobImpl.h index c7315cc489f9..c59c3288abb5 100644 --- a/dom/file/FileBlobImpl.h +++ b/dom/file/FileBlobImpl.h @@ -60,6 +60,16 @@ public: mContentType = aType; } + int64_t GetFileId() override + { + return mFileId; + } + + void SetFileId(int64_t aFileId) + { + mFileId = aFileId; + } + protected: virtual ~FileBlobImpl() = default; @@ -74,6 +84,7 @@ private: nsCOMPtr mFile; bool mWholeFile; + int64_t mFileId; }; } // namespace dom diff --git a/dom/file/ipc/IPCBlobInputStreamParent.cpp b/dom/file/ipc/IPCBlobInputStreamParent.cpp index 35724be3456c..837d5976fd0c 100644 --- a/dom/file/ipc/IPCBlobInputStreamParent.cpp +++ b/dom/file/ipc/IPCBlobInputStreamParent.cpp @@ -57,6 +57,23 @@ IPCBlobInputStreamParent::ActorDestroy(IProtocol::ActorDestroyReason aReason) mPBackgroundManager = nullptr; IPCBlobInputStreamStorage::Get()->ForgetStream(mID); + + RefPtr callback; + mCallback.swap(callback); + + if (callback) { + callback->ActorDestroyed(mID); + } +} + +void +IPCBlobInputStreamParent::SetCallback( + IPCBlobInputStreamParentCallback* aCallback) +{ + MOZ_ASSERT(aCallback); + MOZ_ASSERT(!mCallback); + + mCallback = aCallback; } mozilla::ipc::IPCResult diff --git a/dom/file/ipc/IPCBlobInputStreamParent.h b/dom/file/ipc/IPCBlobInputStreamParent.h index 3ab06aed7013..b403693e65f6 100644 --- a/dom/file/ipc/IPCBlobInputStreamParent.h +++ b/dom/file/ipc/IPCBlobInputStreamParent.h @@ -14,6 +14,19 @@ class nsIInputStream; namespace mozilla { namespace dom { +class NS_NO_VTABLE IPCBlobInputStreamParentCallback +{ +public: + virtual void + ActorDestroyed(const nsID& aID) = 0; + + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + +protected: + virtual ~IPCBlobInputStreamParentCallback() + { } +}; + class IPCBlobInputStreamParent final : public mozilla::ipc::PIPCBlobInputStreamParent { @@ -41,6 +54,9 @@ public: return mSize; } + void + SetCallback(IPCBlobInputStreamParentCallback* aCallback); + mozilla::ipc::IPCResult RecvStreamNeeded() override; @@ -61,6 +77,8 @@ private: // the parent actor alive. The pointers will be nullified in ActorDestroyed. nsIContentParent* mContentManager; mozilla::ipc::PBackgroundParent* mPBackgroundManager; + + RefPtr mCallback; }; } // namespace dom diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index e9e573e46625..e4212d9a28a0 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -53,6 +53,7 @@ #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h" #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h" #include "mozilla/dom/IPCBlobUtils.h" +#include "mozilla/dom/ipc/IPCBlobInputStreamParent.h" #include "mozilla/dom/quota/Client.h" #include "mozilla/dom/quota/FileStreams.h" #include "mozilla/dom/quota/OriginScope.h" @@ -127,9 +128,6 @@ #define IDB_MOBILE #endif -#define BLOB_IMPL_STORED_FILE_IID \ - {0x6b505c84, 0x2c60, 0x4ffb, {0x8b, 0x91, 0xfe, 0x22, 0xb1, 0xec, 0x75, 0xe2}} - namespace mozilla { MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, @@ -6397,6 +6395,7 @@ class Database final friend class VersionChangeTransaction; class StartTransactionOp; + class UnmapBlobCallback; private: RefPtr mFactory; @@ -6405,6 +6404,7 @@ private: RefPtr mDirectoryLock; nsTHashtable> mTransactions; nsTHashtable> mMutableFiles; + nsRefPtrHashtable mMappedBlobs; RefPtr mConnection; const PrincipalInfo mPrincipalInfo; const Maybe mOptionalContentParentId; @@ -6422,6 +6422,9 @@ private: bool mActorWasAlive; bool mActorDestroyed; bool mMetadataCleanedUp; +#ifdef DEBUG + bool mAllBlobsUnmapped; +#endif public: // Created by OpenDatabaseOp. @@ -6571,6 +6574,9 @@ public: void SetActorAlive(); + void + MapBlob(const IPCBlob& aIPCBlob, FileInfo* aFileInfo); + bool IsActorAlive() const { @@ -6626,6 +6632,15 @@ private: MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed); } + already_AddRefed + GetBlob(const IPCBlob& aID); + + void + UnmapBlob(const nsID& aID); + + void + UnmapAllBlobs(); + bool CloseInternal(); @@ -6747,6 +6762,36 @@ private: Cleanup() override; }; +class Database::UnmapBlobCallback final + : public IPCBlobInputStreamParentCallback +{ + RefPtr mDatabase; + +public: + explicit UnmapBlobCallback(Database* aDatabase) + : mDatabase(aDatabase) + { + AssertIsOnBackgroundThread(); + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Database::UnmapBlobCallback, override) + + void + ActorDestroyed(const nsID& aID) override + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mDatabase); + + RefPtr database; + mDatabase.swap(database); + + database->UnmapBlob(aID); + } + +private: + ~UnmapBlobCallback() = default; +}; + /** * In coordination with IDBDatabase's mFileActors weak-map on the child side, a * long-lived mapping from a child process's live Blobs to their corresponding @@ -6755,15 +6800,15 @@ private: * - Blobs retrieved from this database and sent to the child that do not need * to be written to disk because they already exist on disk in this database's * files directory. - * - Blobs retrieved from other databases (that are therefore !IsShareable()) - * or from anywhere else that will need to be written to this database's files - * directory. In this case we will hold a reference to its BlobImpl in - * mBlobImpl until we have successfully written the Blob to disk. + * - Blobs retrieved from other databases or from anywhere else that will need + * to be written to this database's files directory. In this case we will + * hold a reference to its BlobImpl in mBlobImpl until we have successfully + * written the Blob to disk. * * Relevant Blob context: Blobs sent from the parent process to child processes * are automatically linked back to their source BlobImpl when the child process * references the Blob via IPC. This is done using the internal IPCBlob - * inputStream ID and IPCBlobInputStreamStorage. However, when getting an actor + * inputStream actor ID to FileInfo mapping. However, when getting an actor * in the child process for sending an in-child-created Blob to the parent * process, there is (currently) no Blob machinery to automatically establish * and reuse a long-lived Actor. As a result, without IDB's weak-map @@ -9109,75 +9154,6 @@ private: ~DatabaseLoggingInfo(); }; -class BlobImplStoredFile final - : public FileBlobImpl -{ - RefPtr mFileInfo; - const bool mSnapshot; - -public: - BlobImplStoredFile(nsIFile* aFile, FileInfo* aFileInfo, bool aSnapshot) - : FileBlobImpl(aFile) - , mFileInfo(aFileInfo) - , mSnapshot(aSnapshot) - { - AssertIsOnBackgroundThread(); - - // Getting the content type is not currently supported off the main thread. - // This isn't a problem here because: - // - // 1. The real content type is stored in the structured clone data and - // that's all that the DOM will see. - // 2. The nsExternalHelperAppService guesses the content type based only - // on the file extension. Our stored files have no extension so the - // current code path fails and sets the content type to the empty - // string. - // - // So, this is a hack to keep the nsExternalHelperAppService out of the - // picture entirely. Eventually we should probably fix this some other way. - mContentType.Truncate(); - - // In order to call GetMozFullPathInternal, this must be a file. - // In reality, we don't know the real 'nature' if this BlobImpl because we - // need to wait to retrieve such information from the structured clone data. - // But at that point we are already on the child side and we will update the - // blob generated by IPCBlob, and not this one. - mIsFile = true; - } - - bool - IsShareable(FileManager* aFileManager) const - { - AssertIsOnBackgroundThread(); - - return mFileInfo->Manager() == aFileManager && !mSnapshot; - } - - FileInfo* - GetFileInfo() const - { - AssertIsOnBackgroundThread(); - - return mFileInfo; - } - -private: - ~BlobImplStoredFile() override = default; - - NS_DECL_ISUPPORTS_INHERITED - NS_DECLARE_STATIC_IID_ACCESSOR(BLOB_IMPL_STORED_FILE_IID) - - int64_t - GetFileId() override - { - MOZ_ASSERT(mFileInfo); - - return mFileInfo->Id(); - } -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(BlobImplStoredFile, BLOB_IMPL_STORED_FILE_IID) - class QuotaClient final : public mozilla::dom::quota::Client { @@ -10162,9 +10138,8 @@ SerializeStructuredCloneFiles( switch (file.mType) { case StructuredCloneFile::eBlob: { - RefPtr impl = new BlobImplStoredFile(nativeFile, - file.mFileInfo, - /* aSnapshot */ false); + RefPtr impl = new FileBlobImpl(nativeFile); + impl->SetFileId(file.mFileInfo->Id()); IPCBlob ipcBlob; nsresult rv = IPCBlobUtils::Serialize(impl, aBackgroundActor, ipcBlob); @@ -10174,12 +10149,14 @@ SerializeStructuredCloneFiles( return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - SerializedStructuredCloneFile* file = aResult.AppendElement(fallible); - MOZ_ASSERT(file); + SerializedStructuredCloneFile* serializedFile = + aResult.AppendElement(fallible); + MOZ_ASSERT(serializedFile); - file->file() = ipcBlob; - file->type() = StructuredCloneFile::eBlob; + serializedFile->file() = ipcBlob; + serializedFile->type() = StructuredCloneFile::eBlob; + aDatabase->MapBlob(ipcBlob, file.mFileInfo); break; } @@ -10239,9 +10216,8 @@ SerializeStructuredCloneFiles( serializedFile->file() = null_t(); serializedFile->type() = file.mType; } else { - RefPtr impl = new BlobImplStoredFile(nativeFile, - file.mFileInfo, - /* aSnapshot */ false); + RefPtr impl = new FileBlobImpl(nativeFile); + impl->SetFileId(file.mFileInfo->Id()); IPCBlob ipcBlob; nsresult rv = @@ -10258,6 +10234,8 @@ SerializeStructuredCloneFiles( serializedFile->file() = ipcBlob; serializedFile->type() = file.mType; + + aDatabase->MapBlob(ipcBlob, file.mFileInfo); } break; @@ -14176,6 +14154,9 @@ Database::Database(Factory* aFactory, , mActorWasAlive(false) , mActorDestroyed(false) , mMetadataCleanedUp(false) +#ifdef DEBUG + , mAllBlobsUnmapped(false) +#endif { AssertIsOnBackgroundThread(); MOZ_ASSERT(aFactory); @@ -14401,6 +14382,76 @@ Database::SetActorAlive() AddRef(); } +void +Database::MapBlob(const IPCBlob& aIPCBlob, FileInfo* aFileInfo) +{ + AssertIsOnBackgroundThread(); + + const IPCBlobStream& stream = aIPCBlob.inputStream(); + MOZ_ASSERT(stream.type() == IPCBlobStream::TPIPCBlobInputStreamParent); + + IPCBlobInputStreamParent* actor = + static_cast(stream.get_PIPCBlobInputStreamParent()); + + MOZ_ASSERT(!mMappedBlobs.GetWeak(actor->ID())); + mMappedBlobs.Put(actor->ID(), aFileInfo); + + RefPtr callback = new UnmapBlobCallback(this); + actor->SetCallback(callback); +} + +already_AddRefed +Database::GetBlob(const IPCBlob& aIPCBlob) +{ + AssertIsOnBackgroundThread(); + + const IPCBlobStream& stream = aIPCBlob.inputStream(); + MOZ_ASSERT(stream.type() == IPCBlobStream::TIPCStream); + + const IPCStream& ipcStream = stream.get_IPCStream(); + + if (ipcStream.type() != IPCStream::TInputStreamParamsWithFds) { + return nullptr; + } + + const InputStreamParams& inputStreamParams = + ipcStream.get_InputStreamParamsWithFds().stream(); + if (inputStreamParams.type() != + InputStreamParams::TIPCBlobInputStreamParams) { + return nullptr; + } + + const nsID& id = inputStreamParams.get_IPCBlobInputStreamParams().id(); + + RefPtr fileInfo; + if (!mMappedBlobs.Get(id, getter_AddRefs(fileInfo))) { + return nullptr; + } + + return fileInfo.forget(); +} + +void +Database::UnmapBlob(const nsID& aID) +{ + AssertIsOnBackgroundThread(); + + MOZ_ASSERT_IF(!mAllBlobsUnmapped, mMappedBlobs.GetWeak(aID)); + mMappedBlobs.Remove(aID); +} + +void +Database::UnmapAllBlobs() +{ + AssertIsOnBackgroundThread(); + +#ifdef DEBUG + mAllBlobsUnmapped = true; +#endif + + mMappedBlobs.Clear(); +} + bool Database::CloseInternal() { @@ -14466,6 +14517,8 @@ Database::ConnectionClosedCallback() CleanupMetadata(); + UnmapAllBlobs(); + if (IsInvalidated() && IsActorAlive()) { // Step 3 and 4 of "5.2 Closing a Database": // 1. Wait for all transactions to complete. @@ -14549,15 +14602,10 @@ Database::AllocPBackgroundIDBDatabaseFileParent(const IPCBlob& aIPCBlob) RefPtr blobImpl = IPCBlobUtils::Deserialize(aIPCBlob); MOZ_ASSERT(blobImpl); - RefPtr fileInfo; + RefPtr fileInfo = GetBlob(aIPCBlob); RefPtr actor; - RefPtr storedFileImpl = do_QueryObject(blobImpl); - if (storedFileImpl && storedFileImpl->IsShareable(mFileManager)) { - // This blob was previously shared with the child. - fileInfo = storedFileImpl->GetFileInfo(); - MOZ_ASSERT(fileInfo); - + if (fileInfo) { actor = new DatabaseFile(fileInfo); } else { // This is a blob we haven't seen before. @@ -17678,14 +17726,6 @@ FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) return NS_OK; } -/******************************************************************************* - * FileImplStoredFile - ******************************************************************************/ - -NS_IMPL_ISUPPORTS_INHERITED(BlobImplStoredFile, - FileBlobImpl, - BlobImplStoredFile) - /******************************************************************************* * QuotaClient ******************************************************************************/ @@ -20850,8 +20890,9 @@ MutableFile::CreateBlobImpl() { AssertIsOnBackgroundThread(); - RefPtr blobImpl = - new BlobImplStoredFile(mFile, mFileInfo, /* aSnapshot */ true); + RefPtr blobImpl = new FileBlobImpl(mFile); + blobImpl->SetFileId(mFileInfo->Id()); + return blobImpl.forget(); }