mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-10 05:08:36 +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
919 lines
24 KiB
C++
919 lines
24 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 "IPCBlobInputStream.h"
|
|
#include "IPCBlobInputStreamChild.h"
|
|
#include "IPCBlobInputStreamStorage.h"
|
|
#include "mozilla/ipc/InputStreamParams.h"
|
|
#include "mozilla/SlicedInputStream.h"
|
|
#include "mozilla/NonBlockingAsyncInputStream.h"
|
|
#include "IPCBlobInputStreamThread.h"
|
|
#include "nsIAsyncInputStream.h"
|
|
#include "nsIAsyncOutputStream.h"
|
|
#include "nsIPipe.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsStringStream.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
namespace {
|
|
|
|
class InputStreamCallbackRunnable final : public CancelableRunnable {
|
|
public:
|
|
// Note that the execution can be synchronous in case the event target is
|
|
// null.
|
|
static void Execute(nsIInputStreamCallback* aCallback,
|
|
nsIEventTarget* aEventTarget,
|
|
IPCBlobInputStream* aStream) {
|
|
MOZ_ASSERT(aCallback);
|
|
|
|
RefPtr<InputStreamCallbackRunnable> runnable =
|
|
new InputStreamCallbackRunnable(aCallback, aStream);
|
|
|
|
nsCOMPtr<nsIEventTarget> target = aEventTarget;
|
|
if (aEventTarget) {
|
|
target->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
|
} else {
|
|
runnable->Run();
|
|
}
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run() override {
|
|
mCallback->OnInputStreamReady(mStream);
|
|
mCallback = nullptr;
|
|
mStream = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
InputStreamCallbackRunnable(nsIInputStreamCallback* aCallback,
|
|
IPCBlobInputStream* aStream)
|
|
: CancelableRunnable("dom::InputStreamCallbackRunnable"),
|
|
mCallback(aCallback),
|
|
mStream(aStream) {
|
|
MOZ_ASSERT(mCallback);
|
|
MOZ_ASSERT(mStream);
|
|
}
|
|
|
|
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
|
RefPtr<IPCBlobInputStream> mStream;
|
|
};
|
|
|
|
class FileMetadataCallbackRunnable final : public CancelableRunnable {
|
|
public:
|
|
static void Execute(nsIFileMetadataCallback* aCallback,
|
|
nsIEventTarget* aEventTarget,
|
|
IPCBlobInputStream* aStream) {
|
|
MOZ_ASSERT(aCallback);
|
|
MOZ_ASSERT(aEventTarget);
|
|
|
|
RefPtr<FileMetadataCallbackRunnable> runnable =
|
|
new FileMetadataCallbackRunnable(aCallback, aStream);
|
|
|
|
nsCOMPtr<nsIEventTarget> target = aEventTarget;
|
|
target->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run() override {
|
|
mCallback->OnFileMetadataReady(mStream);
|
|
mCallback = nullptr;
|
|
mStream = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
FileMetadataCallbackRunnable(nsIFileMetadataCallback* aCallback,
|
|
IPCBlobInputStream* aStream)
|
|
: CancelableRunnable("dom::FileMetadataCallbackRunnable"),
|
|
mCallback(aCallback),
|
|
mStream(aStream) {
|
|
MOZ_ASSERT(mCallback);
|
|
MOZ_ASSERT(mStream);
|
|
}
|
|
|
|
nsCOMPtr<nsIFileMetadataCallback> mCallback;
|
|
RefPtr<IPCBlobInputStream> mStream;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
NS_IMPL_ADDREF(IPCBlobInputStream);
|
|
NS_IMPL_RELEASE(IPCBlobInputStream);
|
|
|
|
NS_INTERFACE_MAP_BEGIN(IPCBlobInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
|
|
NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsICloneableInputStreamWithRange)
|
|
NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsIFileMetadata)
|
|
NS_INTERFACE_MAP_ENTRY(nsIAsyncFileMetadata)
|
|
NS_INTERFACE_MAP_ENTRY(nsIInputStreamLength)
|
|
NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStreamLength)
|
|
NS_INTERFACE_MAP_ENTRY(nsIIPCBlobInputStream)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
IPCBlobInputStream::IPCBlobInputStream(IPCBlobInputStreamChild* aActor)
|
|
: mActor(aActor),
|
|
mState(eInit),
|
|
mStart(0),
|
|
mLength(0),
|
|
mConsumed(false),
|
|
mMutex("IPCBlobInputStream::mMutex") {
|
|
MOZ_ASSERT(aActor);
|
|
|
|
mLength = aActor->Size();
|
|
|
|
if (XRE_IsParentProcess()) {
|
|
nsCOMPtr<nsIInputStream> stream;
|
|
IPCBlobInputStreamStorage::Get()->GetStream(mActor->ID(), 0, mLength,
|
|
getter_AddRefs(stream));
|
|
if (stream) {
|
|
mState = eRunning;
|
|
mRemoteStream = stream;
|
|
}
|
|
}
|
|
}
|
|
|
|
IPCBlobInputStream::~IPCBlobInputStream() { Close(); }
|
|
|
|
// nsIInputStream interface
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Available(uint64_t* aLength) {
|
|
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// We don't have a remoteStream yet: let's return 0.
|
|
if (mState == eInit || mState == ePending) {
|
|
*aLength = 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mState == eClosed) {
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eRunning);
|
|
MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
|
|
|
|
nsresult rv = EnsureAsyncRemoteStream(lock);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
asyncRemoteStream = mAsyncRemoteStream;
|
|
}
|
|
|
|
MOZ_ASSERT(asyncRemoteStream);
|
|
return asyncRemoteStream->Available(aLength);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) {
|
|
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// Read is not available is we don't have a remoteStream.
|
|
if (mState == eInit || mState == ePending) {
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
}
|
|
|
|
if (mState == eClosed) {
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eRunning);
|
|
MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
|
|
|
|
nsresult rv = EnsureAsyncRemoteStream(lock);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
asyncRemoteStream = mAsyncRemoteStream;
|
|
}
|
|
|
|
MOZ_ASSERT(asyncRemoteStream);
|
|
nsresult rv = asyncRemoteStream->Read(aBuffer, aCount, aReadCount);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
mConsumed = true;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
|
uint32_t aCount, uint32_t* aResult) {
|
|
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// ReadSegments is not available is we don't have a remoteStream.
|
|
if (mState == eInit || mState == ePending) {
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
}
|
|
|
|
if (mState == eClosed) {
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eRunning);
|
|
MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
|
|
|
|
nsresult rv = EnsureAsyncRemoteStream(lock);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
asyncRemoteStream = mAsyncRemoteStream;
|
|
}
|
|
|
|
MOZ_ASSERT(asyncRemoteStream);
|
|
nsresult rv =
|
|
asyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
// If some data has been read, we mark the stream as consumed.
|
|
if (*aResult != 0) {
|
|
MutexAutoLock lock(mMutex);
|
|
mConsumed = true;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking) {
|
|
*aNonBlocking = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Close() {
|
|
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
|
nsCOMPtr<nsIInputStream> remoteStream;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (mActor) {
|
|
mActor->ForgetStream(this);
|
|
mActor = nullptr;
|
|
}
|
|
|
|
asyncRemoteStream.swap(mAsyncRemoteStream);
|
|
remoteStream.swap(mRemoteStream);
|
|
|
|
mInputStreamCallback = nullptr;
|
|
mInputStreamCallbackEventTarget = nullptr;
|
|
|
|
mFileMetadataCallback = nullptr;
|
|
mFileMetadataCallbackEventTarget = nullptr;
|
|
|
|
mState = eClosed;
|
|
}
|
|
|
|
if (asyncRemoteStream) {
|
|
asyncRemoteStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
|
|
}
|
|
|
|
if (remoteStream) {
|
|
remoteStream->Close();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsICloneableInputStream interface
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::GetCloneable(bool* aCloneable) {
|
|
MutexAutoLock lock(mMutex);
|
|
*aCloneable = mState != eClosed;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Clone(nsIInputStream** aResult) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (mState == eClosed) {
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
MOZ_ASSERT(mActor);
|
|
|
|
RefPtr<IPCBlobInputStream> stream = mActor->CreateStream();
|
|
if (!stream) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
stream->InitWithExistingRange(mStart, mLength, lock);
|
|
|
|
stream.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsICloneableInputStreamWithRange interface
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::CloneWithRange(uint64_t aStart, uint64_t aLength,
|
|
nsIInputStream** aResult) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (mState == eClosed) {
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
// Too short or out of range.
|
|
if (aLength == 0 || aStart >= mLength) {
|
|
return NS_NewCStringInputStream(aResult, EmptyCString());
|
|
}
|
|
|
|
MOZ_ASSERT(mActor);
|
|
|
|
RefPtr<IPCBlobInputStream> stream = mActor->CreateStream();
|
|
if (!stream) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
CheckedInt<uint64_t> streamSize = mLength;
|
|
streamSize -= aStart;
|
|
if (!streamSize.isValid()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (aLength > streamSize.value()) {
|
|
aLength = streamSize.value();
|
|
}
|
|
|
|
stream->InitWithExistingRange(aStart + mStart, aLength, lock);
|
|
|
|
stream.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIAsyncInputStream interface
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::CloseWithStatus(nsresult aStatus) { return Close(); }
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
|
|
uint32_t aFlags, uint32_t aRequestedCount,
|
|
nsIEventTarget* aEventTarget) {
|
|
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// See IPCBlobInputStream.h for more information about this state machine.
|
|
|
|
switch (mState) {
|
|
// First call, we need to retrieve the stream from the parent actor.
|
|
case eInit:
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mInputStreamCallback = aCallback;
|
|
mInputStreamCallbackEventTarget = aEventTarget;
|
|
mState = ePending;
|
|
|
|
mActor->StreamNeeded(this, aEventTarget);
|
|
return NS_OK;
|
|
|
|
// We are still waiting for the remote inputStream
|
|
case ePending: {
|
|
if (mInputStreamCallback && aCallback) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mInputStreamCallback = aCallback;
|
|
mInputStreamCallbackEventTarget = aEventTarget;
|
|
return NS_OK;
|
|
}
|
|
|
|
// We have the remote inputStream, let's check if we can execute the
|
|
// callback.
|
|
case eRunning: {
|
|
if (mInputStreamCallback && aCallback) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult rv = EnsureAsyncRemoteStream(lock);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
mInputStreamCallback = aCallback;
|
|
mInputStreamCallbackEventTarget = aEventTarget;
|
|
|
|
asyncRemoteStream = mAsyncRemoteStream;
|
|
break;
|
|
}
|
|
|
|
// Stream is closed.
|
|
default:
|
|
MOZ_ASSERT(mState == eClosed);
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
}
|
|
|
|
MOZ_ASSERT(asyncRemoteStream);
|
|
return asyncRemoteStream->AsyncWait(aCallback ? this : nullptr, 0, 0,
|
|
aEventTarget);
|
|
}
|
|
|
|
void IPCBlobInputStream::StreamReady(
|
|
already_AddRefed<nsIInputStream> aInputStream) {
|
|
nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
|
|
|
|
// If inputStream is null, it means that the serialization went wrong or the
|
|
// stream is not available anymore. We keep the state as pending just to block
|
|
// any additional operation.
|
|
|
|
if (!inputStream) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
|
|
nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
|
|
nsCOMPtr<nsIInputStreamCallback> inputStreamCallback;
|
|
nsCOMPtr<nsIEventTarget> inputStreamCallbackEventTarget;
|
|
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// We have been closed in the meantime.
|
|
if (mState == eClosed) {
|
|
if (inputStream) {
|
|
MutexAutoUnlock unlock(mMutex);
|
|
inputStream->Close();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Now it's the right time to apply a slice if needed.
|
|
if (mStart > 0 || mLength < mActor->Size()) {
|
|
inputStream =
|
|
new SlicedInputStream(inputStream.forget(), mStart, mLength);
|
|
}
|
|
|
|
mRemoteStream = inputStream;
|
|
|
|
MOZ_ASSERT(mState == ePending);
|
|
mState = eRunning;
|
|
|
|
fileMetadataCallback.swap(mFileMetadataCallback);
|
|
fileMetadataCallbackEventTarget.swap(mFileMetadataCallbackEventTarget);
|
|
|
|
inputStreamCallback = mInputStreamCallback ? this : nullptr;
|
|
inputStreamCallbackEventTarget = mInputStreamCallbackEventTarget;
|
|
|
|
if (inputStreamCallback) {
|
|
nsresult rv = EnsureAsyncRemoteStream(lock);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(mAsyncRemoteStream);
|
|
asyncRemoteStream = mAsyncRemoteStream;
|
|
}
|
|
}
|
|
|
|
if (fileMetadataCallback) {
|
|
FileMetadataCallbackRunnable::Execute(
|
|
fileMetadataCallback, fileMetadataCallbackEventTarget, this);
|
|
}
|
|
|
|
if (inputStreamCallback) {
|
|
MOZ_ASSERT(asyncRemoteStream);
|
|
|
|
nsresult rv = asyncRemoteStream->AsyncWait(inputStreamCallback, 0, 0,
|
|
inputStreamCallbackEventTarget);
|
|
Unused << NS_WARN_IF(NS_FAILED(rv));
|
|
}
|
|
}
|
|
|
|
void IPCBlobInputStream::InitWithExistingRange(
|
|
uint64_t aStart, uint64_t aLength, const MutexAutoLock& aProofOfLock) {
|
|
MOZ_ASSERT(mActor->Size() >= aStart + aLength);
|
|
mStart = aStart;
|
|
mLength = aLength;
|
|
|
|
// In the child, we slice in StreamReady() when we set mState to eRunning.
|
|
// But in the parent, we start out eRunning, so it's necessary to slice the
|
|
// stream as soon as we have the information during the initialization phase
|
|
// because the stream is immediately consumable.
|
|
if (mState == eRunning && mRemoteStream && XRE_IsParentProcess() &&
|
|
(mStart > 0 || mLength < mActor->Size())) {
|
|
mRemoteStream =
|
|
new SlicedInputStream(mRemoteStream.forget(), mStart, mLength);
|
|
}
|
|
}
|
|
|
|
// nsIInputStreamCallback
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream) {
|
|
nsCOMPtr<nsIInputStreamCallback> callback;
|
|
nsCOMPtr<nsIEventTarget> callbackEventTarget;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// We have been closed in the meantime.
|
|
if (mState == eClosed) {
|
|
return NS_OK;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eRunning);
|
|
MOZ_ASSERT(mAsyncRemoteStream == aStream);
|
|
|
|
// The callback has been canceled in the meantime.
|
|
if (!mInputStreamCallback) {
|
|
return NS_OK;
|
|
}
|
|
|
|
callback.swap(mInputStreamCallback);
|
|
callbackEventTarget.swap(mInputStreamCallbackEventTarget);
|
|
}
|
|
|
|
// This must be the last operation because the execution of the callback can
|
|
// be synchronous.
|
|
MOZ_ASSERT(callback);
|
|
InputStreamCallbackRunnable::Execute(callback, callbackEventTarget, this);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIIPCSerializableInputStream
|
|
|
|
void IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
|
|
FileDescriptorArray& aFileDescriptors,
|
|
bool aDelayedStart, uint32_t aMaxSize,
|
|
uint32_t* aSizeUsed,
|
|
nsIContentChild* aManager) {
|
|
MOZ_ASSERT(aSizeUsed);
|
|
*aSizeUsed = 0;
|
|
|
|
SerializeInternal(aParams);
|
|
}
|
|
|
|
void IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
|
|
FileDescriptorArray& aFileDescriptors,
|
|
bool aDelayedStart, uint32_t aMaxSize,
|
|
uint32_t* aSizeUsed,
|
|
mozilla::ipc::PBackgroundChild* aManager) {
|
|
MOZ_ASSERT(aSizeUsed);
|
|
*aSizeUsed = 0;
|
|
|
|
SerializeInternal(aParams);
|
|
}
|
|
|
|
void IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
|
|
FileDescriptorArray& aFileDescriptors,
|
|
bool aDelayedStart, uint32_t aMaxSize,
|
|
uint32_t* aSizeUsed,
|
|
nsIContentParent* aManager) {
|
|
MOZ_ASSERT(aSizeUsed);
|
|
*aSizeUsed = 0;
|
|
|
|
SerializeInternal(aParams);
|
|
}
|
|
|
|
void IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
|
|
FileDescriptorArray& aFileDescriptors,
|
|
bool aDelayedStart, uint32_t aMaxSize,
|
|
uint32_t* aSizeUsed,
|
|
mozilla::ipc::PBackgroundParent* aManager) {
|
|
MOZ_ASSERT(aSizeUsed);
|
|
*aSizeUsed = 0;
|
|
|
|
SerializeInternal(aParams);
|
|
}
|
|
|
|
void IPCBlobInputStream::SerializeInternal(
|
|
mozilla::ipc::InputStreamParams& aParams) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
mozilla::ipc::IPCBlobInputStreamParams params;
|
|
params.id() = mActor->ID();
|
|
params.start() = mStart;
|
|
params.length() = mLength;
|
|
|
|
aParams = params;
|
|
}
|
|
|
|
bool IPCBlobInputStream::Deserialize(
|
|
const mozilla::ipc::InputStreamParams& aParams,
|
|
const FileDescriptorArray& aFileDescriptors) {
|
|
MOZ_CRASH("This should never be called.");
|
|
return false;
|
|
}
|
|
|
|
// nsIAsyncFileMetadata
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::AsyncFileMetadataWait(nsIFileMetadataCallback* aCallback,
|
|
nsIEventTarget* aEventTarget) {
|
|
MOZ_ASSERT(!!aCallback == !!aEventTarget);
|
|
|
|
// If we have the callback, we must have the event target.
|
|
if (NS_WARN_IF(!!aCallback != !!aEventTarget)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// See IPCBlobInputStream.h for more information about this state machine.
|
|
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
switch (mState) {
|
|
// First call, we need to retrieve the stream from the parent actor.
|
|
case eInit:
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mFileMetadataCallback = aCallback;
|
|
mFileMetadataCallbackEventTarget = aEventTarget;
|
|
mState = ePending;
|
|
|
|
mActor->StreamNeeded(this, aEventTarget);
|
|
return NS_OK;
|
|
|
|
// We are still waiting for the remote inputStream
|
|
case ePending:
|
|
if (mFileMetadataCallback && aCallback) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mFileMetadataCallback = aCallback;
|
|
mFileMetadataCallbackEventTarget = aEventTarget;
|
|
return NS_OK;
|
|
|
|
// We have the remote inputStream, let's check if we can execute the
|
|
// callback.
|
|
case eRunning:
|
|
break;
|
|
|
|
// Stream is closed.
|
|
default:
|
|
MOZ_ASSERT(mState == eClosed);
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eRunning);
|
|
}
|
|
|
|
FileMetadataCallbackRunnable::Execute(aCallback, aEventTarget, this);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIFileMetadata
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::GetSize(int64_t* aRetval) {
|
|
nsCOMPtr<nsIFileMetadata> fileMetadata;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
fileMetadata = do_QueryInterface(mRemoteStream);
|
|
if (!fileMetadata) {
|
|
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
return fileMetadata->GetSize(aRetval);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::GetLastModified(int64_t* aRetval) {
|
|
nsCOMPtr<nsIFileMetadata> fileMetadata;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
fileMetadata = do_QueryInterface(mRemoteStream);
|
|
if (!fileMetadata) {
|
|
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
return fileMetadata->GetLastModified(aRetval);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::GetFileDescriptor(PRFileDesc** aRetval) {
|
|
nsCOMPtr<nsIFileMetadata> fileMetadata;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
fileMetadata = do_QueryInterface(mRemoteStream);
|
|
if (!fileMetadata) {
|
|
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
return fileMetadata->GetFileDescriptor(aRetval);
|
|
}
|
|
|
|
nsresult IPCBlobInputStream::EnsureAsyncRemoteStream(
|
|
const MutexAutoLock& aProofOfLock) {
|
|
// We already have an async remote stream.
|
|
if (mAsyncRemoteStream) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!mRemoteStream) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// If the stream is blocking, we want to make it unblocking using a pipe.
|
|
bool nonBlocking = false;
|
|
nsresult rv = mRemoteStream->IsNonBlocking(&nonBlocking);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mRemoteStream);
|
|
|
|
// If non-blocking and non-async, let's use NonBlockingAsyncInputStream.
|
|
if (nonBlocking && !asyncStream) {
|
|
rv = NonBlockingAsyncInputStream::Create(mRemoteStream.forget(),
|
|
getter_AddRefs(asyncStream));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
MOZ_ASSERT(asyncStream);
|
|
}
|
|
|
|
if (!asyncStream) {
|
|
// Let's make the stream async using the DOMFile thread.
|
|
nsCOMPtr<nsIAsyncInputStream> pipeIn;
|
|
nsCOMPtr<nsIAsyncOutputStream> pipeOut;
|
|
rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true,
|
|
true);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
RefPtr<IPCBlobInputStreamThread> thread =
|
|
IPCBlobInputStreamThread::GetOrCreate();
|
|
if (NS_WARN_IF(!thread)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
rv = NS_AsyncCopy(mRemoteStream, pipeOut, thread,
|
|
NS_ASYNCCOPY_VIA_WRITESEGMENTS);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
asyncStream = pipeIn;
|
|
}
|
|
|
|
MOZ_ASSERT(asyncStream);
|
|
mAsyncRemoteStream = asyncStream;
|
|
mRemoteStream = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIInputStreamLength
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Length(int64_t* aLength) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (mState == eClosed) {
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
if (mConsumed) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
}
|
|
|
|
// nsIAsyncInputStreamLength
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
|
|
nsIEventTarget* aEventTarget) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (mState == eClosed) {
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
if (mConsumed) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
// If we have the callback, we must have the event target.
|
|
if (NS_WARN_IF(!!aCallback != !!aEventTarget)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mLengthCallback = aCallback;
|
|
mLengthCallbackEventTarget = aEventTarget;
|
|
|
|
if (aCallback) {
|
|
mActor->LengthNeeded(this, aEventTarget);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class InputStreamLengthCallbackRunnable final : public CancelableRunnable {
|
|
public:
|
|
static void Execute(nsIInputStreamLengthCallback* aCallback,
|
|
nsIEventTarget* aEventTarget, IPCBlobInputStream* aStream,
|
|
int64_t aLength) {
|
|
MOZ_ASSERT(aCallback);
|
|
MOZ_ASSERT(aEventTarget);
|
|
|
|
RefPtr<InputStreamLengthCallbackRunnable> runnable =
|
|
new InputStreamLengthCallbackRunnable(aCallback, aStream, aLength);
|
|
|
|
nsCOMPtr<nsIEventTarget> target = aEventTarget;
|
|
target->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run() override {
|
|
mCallback->OnInputStreamLengthReady(mStream, mLength);
|
|
mCallback = nullptr;
|
|
mStream = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
InputStreamLengthCallbackRunnable(nsIInputStreamLengthCallback* aCallback,
|
|
IPCBlobInputStream* aStream,
|
|
int64_t aLength)
|
|
: CancelableRunnable("dom::InputStreamLengthCallbackRunnable"),
|
|
mCallback(aCallback),
|
|
mStream(aStream),
|
|
mLength(aLength) {
|
|
MOZ_ASSERT(mCallback);
|
|
MOZ_ASSERT(mStream);
|
|
}
|
|
|
|
nsCOMPtr<nsIInputStreamLengthCallback> mCallback;
|
|
RefPtr<IPCBlobInputStream> mStream;
|
|
int64_t mLength;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
void IPCBlobInputStream::LengthReady(int64_t aLength) {
|
|
nsCOMPtr<nsIInputStreamLengthCallback> lengthCallback;
|
|
nsCOMPtr<nsIEventTarget> lengthCallbackEventTarget;
|
|
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// We have been closed in the meantime.
|
|
if (mState == eClosed || mConsumed) {
|
|
return;
|
|
}
|
|
|
|
if (mStart > 0) {
|
|
aLength -= mStart;
|
|
}
|
|
|
|
if (mLength < mActor->Size()) {
|
|
// If the remote stream must be sliced, we must return here the correct
|
|
// value.
|
|
aLength = XPCOM_MIN(aLength, (int64_t)mLength);
|
|
}
|
|
|
|
lengthCallback.swap(mLengthCallback);
|
|
lengthCallbackEventTarget.swap(mLengthCallbackEventTarget);
|
|
}
|
|
|
|
if (lengthCallback) {
|
|
InputStreamLengthCallbackRunnable::Execute(
|
|
lengthCallback, lengthCallbackEventTarget, this, aLength);
|
|
}
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|