mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-12 22:28:59 +02:00
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
424 lines
12 KiB
C++
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(¤t)) && 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
|