Bug 1360185 - Use of IPCBlob in IndexedDB - part 4 - IPCBlob and sharing files when used by IDB, r=baku

This commit is contained in:
Jan Varga 2017-05-18 09:50:57 +02:00
Родитель 335ccef6d3
Коммит 148a63cc3f
5 изменённых файлов: 196 добавлений и 104 удалений

Просмотреть файл

@ -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());

Просмотреть файл

@ -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<nsIFile> mFile;
bool mWholeFile;
int64_t mFileId;
};
} // namespace dom

Просмотреть файл

@ -57,6 +57,23 @@ IPCBlobInputStreamParent::ActorDestroy(IProtocol::ActorDestroyReason aReason)
mPBackgroundManager = nullptr;
IPCBlobInputStreamStorage::Get()->ForgetStream(mID);
RefPtr<IPCBlobInputStreamParentCallback> 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

Просмотреть файл

@ -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<IPCBlobInputStreamParentCallback> mCallback;
};
} // namespace dom

Просмотреть файл

@ -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<Factory> mFactory;
@ -6405,6 +6404,7 @@ private:
RefPtr<DirectoryLock> mDirectoryLock;
nsTHashtable<nsPtrHashKey<TransactionBase>> mTransactions;
nsTHashtable<nsPtrHashKey<MutableFile>> mMutableFiles;
nsRefPtrHashtable<nsIDHashKey, FileInfo> mMappedBlobs;
RefPtr<DatabaseConnection> mConnection;
const PrincipalInfo mPrincipalInfo;
const Maybe<ContentParentId> 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<FileInfo>
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<Database> 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> 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<FileInfo> 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<BlobImpl> impl = new BlobImplStoredFile(nativeFile,
file.mFileInfo,
/* aSnapshot */ false);
RefPtr<FileBlobImpl> 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<BlobImpl> impl = new BlobImplStoredFile(nativeFile,
file.mFileInfo,
/* aSnapshot */ false);
RefPtr<FileBlobImpl> 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<IPCBlobInputStreamParent*>(stream.get_PIPCBlobInputStreamParent());
MOZ_ASSERT(!mMappedBlobs.GetWeak(actor->ID()));
mMappedBlobs.Put(actor->ID(), aFileInfo);
RefPtr<UnmapBlobCallback> callback = new UnmapBlobCallback(this);
actor->SetCallback(callback);
}
already_AddRefed<FileInfo>
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> 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> blobImpl = IPCBlobUtils::Deserialize(aIPCBlob);
MOZ_ASSERT(blobImpl);
RefPtr<FileInfo> fileInfo;
RefPtr<FileInfo> fileInfo = GetBlob(aIPCBlob);
RefPtr<DatabaseFile> actor;
RefPtr<BlobImplStoredFile> 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> blobImpl =
new BlobImplStoredFile(mFile, mFileInfo, /* aSnapshot */ true);
RefPtr<FileBlobImpl> blobImpl = new FileBlobImpl(mFile);
blobImpl->SetFileId(mFileInfo->Id());
return blobImpl.forget();
}