gecko-dev/dom/indexedDB/FileSnapshot.cpp
Andrea Marchesini be2551ab2c Bug 1523702 - Max IPC message size for InputStream serialization, r=smaug
The total size of an IPC inputStream message must be less than 1mb. When we
compose the message for the multiplex stream, each sub stream collaborates with
its own size, deciding if it's better to be a pipe stream (IPCRemoteStream) or
a full serialized one.

Differential Revision: https://phabricator.services.mozilla.com/D18543

--HG--
extra : moz-landing-system : lando
2019-02-04 22:50:51 +00:00

424 lines
12 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileSnapshot.h"
#include "IDBDatabase.h"
#include "IDBFileHandle.h"
#include "IDBMutableFile.h"
#include "mozilla/Assertions.h"
#include "mozilla/Mutex.h"
#include "nsIAsyncInputStream.h"
#include "nsICloneableInputStream.h"
#include "nsIIPCSerializableInputStream.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
using namespace mozilla::ipc;
namespace {
class StreamWrapper final : public nsIAsyncInputStream,
public nsIInputStreamCallback,
public nsICloneableInputStream,
public nsIIPCSerializableInputStream {
class CloseRunnable;
nsCOMPtr<nsIEventTarget> mOwningThread;
nsCOMPtr<nsIInputStream> mInputStream;
RefPtr<IDBFileHandle> mFileHandle;
bool mFinished;
// This is needed to call OnInputStreamReady() with the correct inputStream.
// It is protected by mutex.
nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
Mutex mMutex;
public:
StreamWrapper(nsIInputStream* aInputStream, IDBFileHandle* aFileHandle)
: mOwningThread(aFileHandle->GetMutableFile()->Database()->EventTarget()),
mInputStream(aInputStream),
mFileHandle(aFileHandle),
mFinished(false),
mMutex("StreamWrapper::mMutex") {
AssertIsOnOwningThread();
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(aFileHandle);
aFileHandle->AssertIsOnOwningThread();
mFileHandle->OnNewRequest();
}
private:
virtual ~StreamWrapper();
template <typename M>
void SerializeInternal(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors,
bool aDelayedStart, uint32_t aMaxSize,
uint32_t* aSizeUsed, M* aManager);
bool IsOnOwningThread() const {
MOZ_ASSERT(mOwningThread);
bool current;
return NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)) && current;
}
void AssertIsOnOwningThread() const { MOZ_ASSERT(IsOnOwningThread()); }
void Finish() {
AssertIsOnOwningThread();
if (mFinished) {
return;
}
mFinished = true;
mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true);
}
void Destroy() {
if (IsOnOwningThread()) {
delete this;
return;
}
RefPtr<Runnable> destroyRunnable = NewNonOwningRunnableMethod(
"StreamWrapper::Destroy", this, &StreamWrapper::Destroy);
MOZ_ALWAYS_SUCCEEDS(
mOwningThread->Dispatch(destroyRunnable, NS_DISPATCH_NORMAL));
}
bool IsCloneableInputStream() const {
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mInputStream);
return !!stream;
}
bool IsIPCSerializableInputStream() const {
nsCOMPtr<nsIIPCSerializableInputStream> stream =
do_QueryInterface(mInputStream);
return !!stream;
}
bool IsAsyncInputStream() const {
nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mInputStream);
return !!stream;
}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIASYNCINPUTSTREAM
NS_DECL_NSIINPUTSTREAMCALLBACK
NS_DECL_NSICLONEABLEINPUTSTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
};
class StreamWrapper::CloseRunnable final : public Runnable {
friend class StreamWrapper;
RefPtr<StreamWrapper> mStreamWrapper;
public:
NS_INLINE_DECL_REFCOUNTING_INHERITED(CloseRunnable, Runnable)
private:
explicit CloseRunnable(StreamWrapper* aStreamWrapper)
: Runnable("StreamWrapper::CloseRunnable"),
mStreamWrapper(aStreamWrapper) {}
~CloseRunnable() {}
NS_IMETHOD
Run() override;
};
} // anonymous namespace
BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl,
IDBFileHandle* aFileHandle)
: mBlobImpl(aFileImpl) {
MOZ_ASSERT(aFileImpl);
MOZ_ASSERT(aFileHandle);
mFileHandle =
do_GetWeakReference(NS_ISUPPORTS_CAST(EventTarget*, aFileHandle));
}
BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl,
nsIWeakReference* aFileHandle)
: mBlobImpl(aFileImpl), mFileHandle(aFileHandle) {
MOZ_ASSERT(aFileImpl);
MOZ_ASSERT(aFileHandle);
}
BlobImplSnapshot::~BlobImplSnapshot() {}
NS_IMPL_ISUPPORTS_INHERITED(BlobImplSnapshot, BlobImpl, PIBlobImplSnapshot)
already_AddRefed<BlobImpl> BlobImplSnapshot::CreateSlice(
uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
ErrorResult& aRv) {
RefPtr<BlobImpl> blobImpl =
mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
blobImpl = new BlobImplSnapshot(blobImpl, mFileHandle);
return blobImpl.forget();
}
void BlobImplSnapshot::CreateInputStream(nsIInputStream** aStream,
ErrorResult& aRv) {
nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
RefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
if (!fileHandle || !fileHandle->IsOpen()) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
return;
}
nsCOMPtr<nsIInputStream> stream;
mBlobImpl->CreateInputStream(getter_AddRefs(stream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
RefPtr<StreamWrapper> wrapper = new StreamWrapper(stream, fileHandle);
wrapper.forget(aStream);
}
BlobImpl* BlobImplSnapshot::GetBlobImpl() const {
nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
RefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
if (!fileHandle || !fileHandle->IsOpen()) {
return nullptr;
}
return mBlobImpl;
}
void BlobImplSnapshot::GetBlobImplType(nsAString& aBlobImplType) const {
aBlobImplType.AssignLiteral("BlobImplSnapshot[");
nsAutoString blobImplType;
mBlobImpl->GetBlobImplType(blobImplType);
aBlobImplType.Append(blobImplType);
aBlobImplType.AppendLiteral("]");
}
StreamWrapper::~StreamWrapper() {
AssertIsOnOwningThread();
Finish();
}
NS_IMPL_ADDREF(StreamWrapper)
NS_IMPL_RELEASE_WITH_DESTROY(StreamWrapper, Destroy())
NS_INTERFACE_MAP_BEGIN(StreamWrapper)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, IsAsyncInputStream())
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
IsAsyncInputStream())
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
IsCloneableInputStream())
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
IsIPCSerializableInputStream())
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
StreamWrapper::Close() {
RefPtr<CloseRunnable> closeRunnable = new CloseRunnable(this);
MOZ_ALWAYS_SUCCEEDS(
mOwningThread->Dispatch(closeRunnable, NS_DISPATCH_NORMAL));
return NS_OK;
}
NS_IMETHODIMP
StreamWrapper::Available(uint64_t* _retval) {
return mInputStream->Available(_retval);
}
NS_IMETHODIMP
StreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
return mInputStream->Read(aBuf, aCount, _retval);
}
NS_IMETHODIMP
StreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* _retval) {
return mInputStream->ReadSegments(aWriter, aClosure, aCount, _retval);
}
NS_IMETHODIMP
StreamWrapper::IsNonBlocking(bool* _retval) {
return mInputStream->IsNonBlocking(_retval);
}
void StreamWrapper::Serialize(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors,
bool aDelayedStart, uint32_t aMaxSize,
uint32_t* aSizeUsed, nsIContentChild* aManager) {
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
aSizeUsed, aManager);
}
void StreamWrapper::Serialize(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors,
bool aDelayedStart, uint32_t aMaxSize,
uint32_t* aSizeUsed, PBackgroundChild* aManager) {
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
aSizeUsed, aManager);
}
void StreamWrapper::Serialize(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors,
bool aDelayedStart, uint32_t aMaxSize,
uint32_t* aSizeUsed, nsIContentParent* aManager) {
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
aSizeUsed, aManager);
}
void StreamWrapper::Serialize(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors,
bool aDelayedStart, uint32_t aMaxSize,
uint32_t* aSizeUsed,
PBackgroundParent* aManager) {
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
aSizeUsed, aManager);
}
template <typename M>
void StreamWrapper::SerializeInternal(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors,
bool aDelayedStart, uint32_t aMaxSize,
uint32_t* aSizeUsed, M* aManager) {
MOZ_ASSERT(aSizeUsed);
*aSizeUsed = 0;
nsCOMPtr<nsIIPCSerializableInputStream> stream =
do_QueryInterface(mInputStream);
if (stream) {
stream->Serialize(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
aSizeUsed, aManager);
}
}
bool StreamWrapper::Deserialize(const InputStreamParams& aParams,
const FileDescriptorArray& aFileDescriptors) {
MOZ_CRASH("This method should never be called");
return false;
}
NS_IMETHODIMP
StreamWrapper::CloseWithStatus(nsresult aStatus) {
nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mInputStream);
if (!stream) {
return NS_ERROR_NO_INTERFACE;
}
nsresult rv = stream->CloseWithStatus(aStatus);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return Close();
}
NS_IMETHODIMP
StreamWrapper::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
uint32_t aRequestedCount,
nsIEventTarget* aEventTarget) {
nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mInputStream);
if (!stream) {
return NS_ERROR_NO_INTERFACE;
}
nsCOMPtr<nsIInputStreamCallback> callback = aCallback ? this : nullptr;
{
MutexAutoLock lock(mMutex);
if (mAsyncWaitCallback && aCallback) {
return NS_ERROR_FAILURE;
}
mAsyncWaitCallback = aCallback;
}
return stream->AsyncWait(callback, aFlags, aRequestedCount, aEventTarget);
}
// nsIInputStreamCallback
NS_IMETHODIMP
StreamWrapper::OnInputStreamReady(nsIAsyncInputStream* aStream) {
nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mInputStream);
if (!stream) {
return NS_ERROR_NO_INTERFACE;
}
nsCOMPtr<nsIInputStreamCallback> callback;
{
MutexAutoLock lock(mMutex);
// We have been canceled in the meanwhile.
if (!mAsyncWaitCallback) {
return NS_OK;
}
callback.swap(mAsyncWaitCallback);
}
MOZ_ASSERT(callback);
return callback->OnInputStreamReady(this);
}
// nsICloneableInputStream
NS_IMETHODIMP
StreamWrapper::GetCloneable(bool* aCloneable) {
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mInputStream);
if (!stream) {
*aCloneable = false;
return NS_ERROR_NO_INTERFACE;
}
return stream->GetCloneable(aCloneable);
}
NS_IMETHODIMP
StreamWrapper::Clone(nsIInputStream** aResult) {
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mInputStream);
if (!stream) {
return NS_ERROR_NO_INTERFACE;
}
return stream->Clone(aResult);
}
NS_IMETHODIMP
StreamWrapper::CloseRunnable::Run() {
mStreamWrapper->Finish();
return NS_OK;
}
} // namespace indexedDB
} // namespace dom
} // namespace mozilla