This gives us various positive benefits, such as using a shared memory ring buffer for faster communication, not having data streaming being bound to the thread which transferred the nsIInputStream (which is often the main thread), and the ability for some backpressure to be applied to data streaming. After this change, the "delayed start" parameter for IPCStream serialization is less relevant, as backpressure will serve a similar purpose. It will still be used to determine whether or not to use RemoteLazyInputStream when serializing from the parent process. Differential Revision: https://phabricator.services.mozilla.com/D141038
270 lines
9.6 KiB
C++
270 lines
9.6 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 "InputStreamUtils.h"
|
|
|
|
#include "nsIIPCSerializableInputStream.h"
|
|
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/dom/File.h"
|
|
#include "mozilla/dom/quota/DecryptingInputStream_impl.h"
|
|
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
|
|
#include "mozilla/ipc/DataPipe.h"
|
|
#include "mozilla/InputStreamLengthHelper.h"
|
|
#include "mozilla/RemoteLazyInputStream.h"
|
|
#include "mozilla/RemoteLazyInputStreamChild.h"
|
|
#include "mozilla/RemoteLazyInputStreamStorage.h"
|
|
#include "mozilla/SlicedInputStream.h"
|
|
#include "mozilla/InputStreamLengthWrapper.h"
|
|
#include "nsBufferedStreams.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsDebug.h"
|
|
#include "nsFileStreams.h"
|
|
#include "nsIAsyncInputStream.h"
|
|
#include "nsIAsyncOutputStream.h"
|
|
#include "nsID.h"
|
|
#include "nsIMIMEInputStream.h"
|
|
#include "nsIMultiplexInputStream.h"
|
|
#include "nsIPipe.h"
|
|
#include "nsMIMEInputStream.h"
|
|
#include "nsMultiplexInputStream.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsStringStream.h"
|
|
#include "nsXULAppAPI.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
namespace mozilla {
|
|
namespace ipc {
|
|
|
|
namespace {
|
|
|
|
template <typename M>
|
|
void SerializeInputStreamInternal(nsIInputStream* aInputStream,
|
|
InputStreamParams& aParams,
|
|
nsTArray<FileDescriptor>& aFileDescriptors,
|
|
bool aDelayedStart, uint32_t aMaxSize,
|
|
uint32_t* aSizeUsed, M* aManager) {
|
|
MOZ_ASSERT(aInputStream);
|
|
MOZ_ASSERT(aManager);
|
|
|
|
nsCOMPtr<nsIIPCSerializableInputStream> serializable =
|
|
do_QueryInterface(aInputStream);
|
|
if (!serializable) {
|
|
MOZ_CRASH("Input stream is not serializable!");
|
|
}
|
|
|
|
serializable->Serialize(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
|
|
aSizeUsed, aManager);
|
|
|
|
if (aParams.type() == InputStreamParams::T__None) {
|
|
MOZ_CRASH("Serialize failed!");
|
|
}
|
|
}
|
|
|
|
template <typename M>
|
|
void SerializeInputStreamAsPipeInternal(nsIInputStream* aInputStream,
|
|
InputStreamParams& aParams,
|
|
bool aDelayedStart, M* aManager) {
|
|
MOZ_ASSERT(aInputStream);
|
|
MOZ_ASSERT(aManager);
|
|
|
|
// Let's try to take the length using InputStreamLengthHelper. If the length
|
|
// cannot be taken synchronously, and its length is needed, the stream needs
|
|
// to be fully copied in memory on the deserialization side.
|
|
int64_t length;
|
|
if (!InputStreamLengthHelper::GetSyncLength(aInputStream, &length)) {
|
|
length = -1;
|
|
}
|
|
|
|
RefPtr<DataPipeSender> sender;
|
|
RefPtr<DataPipeReceiver> receiver;
|
|
nsresult rv = NewDataPipe(kDefaultDataPipeCapacity, getter_AddRefs(sender),
|
|
getter_AddRefs(receiver));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIEventTarget> target =
|
|
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
|
|
|
rv =
|
|
NS_AsyncCopy(aInputStream, sender, target, NS_ASYNCCOPY_VIA_WRITESEGMENTS,
|
|
kDefaultDataPipeCapacity, nullptr, nullptr);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
|
|
aParams = DataPipeReceiverStreamParams(receiver);
|
|
if (length != -1) {
|
|
aParams = InputStreamLengthWrapperParams(aParams, length, false);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void InputStreamHelper::SerializedComplexity(nsIInputStream* aInputStream,
|
|
uint32_t aMaxSize,
|
|
uint32_t* aSizeUsed,
|
|
uint32_t* aPipes,
|
|
uint32_t* aTransferables) {
|
|
MOZ_ASSERT(aInputStream);
|
|
|
|
nsCOMPtr<nsIIPCSerializableInputStream> serializable =
|
|
do_QueryInterface(aInputStream);
|
|
if (!serializable) {
|
|
MOZ_CRASH("Input stream is not serializable!");
|
|
}
|
|
|
|
serializable->SerializedComplexity(aMaxSize, aSizeUsed, aPipes,
|
|
aTransferables);
|
|
}
|
|
|
|
void InputStreamHelper::SerializeInputStream(
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
|
|
uint32_t aMaxSize, uint32_t* aSizeUsed,
|
|
ParentToChildStreamActorManager* aManager) {
|
|
SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
|
|
aDelayedStart, aMaxSize, aSizeUsed, aManager);
|
|
}
|
|
|
|
void InputStreamHelper::SerializeInputStream(
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
|
|
uint32_t aMaxSize, uint32_t* aSizeUsed,
|
|
ChildToParentStreamActorManager* aManager) {
|
|
SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
|
|
aDelayedStart, aMaxSize, aSizeUsed, aManager);
|
|
}
|
|
|
|
void InputStreamHelper::SerializeInputStreamAsPipe(
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
bool aDelayedStart, ParentToChildStreamActorManager* aManager) {
|
|
SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
|
|
aManager);
|
|
}
|
|
|
|
void InputStreamHelper::SerializeInputStreamAsPipe(
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
bool aDelayedStart, ChildToParentStreamActorManager* aManager) {
|
|
SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
|
|
aManager);
|
|
}
|
|
|
|
already_AddRefed<nsIInputStream> InputStreamHelper::DeserializeInputStream(
|
|
const InputStreamParams& aParams,
|
|
const nsTArray<FileDescriptor>& aFileDescriptors) {
|
|
if (aParams.type() == InputStreamParams::TRemoteLazyInputStreamParams) {
|
|
const RemoteLazyInputStreamParams& params =
|
|
aParams.get_RemoteLazyInputStreamParams();
|
|
|
|
// RemoteLazyInputStreamRefs are not deserializable on the parent side,
|
|
// because the parent is the only one that has a copy of the original stream
|
|
// in the RemoteLazyInputStreamStorage.
|
|
if (params.type() ==
|
|
RemoteLazyInputStreamParams::TRemoteLazyInputStreamRef) {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
const RemoteLazyInputStreamRef& ref =
|
|
params.get_RemoteLazyInputStreamRef();
|
|
|
|
auto storage = RemoteLazyInputStreamStorage::Get().unwrapOr(nullptr);
|
|
MOZ_ASSERT(storage);
|
|
nsCOMPtr<nsIInputStream> stream;
|
|
storage->GetStream(ref.id(), ref.start(), ref.length(),
|
|
getter_AddRefs(stream));
|
|
return stream.forget();
|
|
}
|
|
|
|
// parent -> child serializations receive an RemoteLazyInputStream actor.
|
|
MOZ_ASSERT(params.type() ==
|
|
RemoteLazyInputStreamParams::TPRemoteLazyInputStreamChild);
|
|
RemoteLazyInputStreamChild* actor =
|
|
static_cast<RemoteLazyInputStreamChild*>(
|
|
params.get_PRemoteLazyInputStreamChild());
|
|
nsCOMPtr<nsIInputStream> stream = actor->CreateStream();
|
|
return stream.forget();
|
|
}
|
|
|
|
if (aParams.type() == InputStreamParams::TDataPipeReceiverStreamParams) {
|
|
const DataPipeReceiverStreamParams& pipeParams =
|
|
aParams.get_DataPipeReceiverStreamParams();
|
|
return do_AddRef(pipeParams.pipe());
|
|
}
|
|
|
|
nsCOMPtr<nsIIPCSerializableInputStream> serializable;
|
|
|
|
switch (aParams.type()) {
|
|
case InputStreamParams::TStringInputStreamParams: {
|
|
nsCOMPtr<nsIInputStream> stream;
|
|
NS_NewCStringInputStream(getter_AddRefs(stream), ""_ns);
|
|
serializable = do_QueryInterface(stream);
|
|
} break;
|
|
|
|
case InputStreamParams::TFileInputStreamParams: {
|
|
nsCOMPtr<nsIFileInputStream> stream;
|
|
nsFileInputStream::Create(nullptr, NS_GET_IID(nsIFileInputStream),
|
|
getter_AddRefs(stream));
|
|
serializable = do_QueryInterface(stream);
|
|
} break;
|
|
|
|
case InputStreamParams::TBufferedInputStreamParams: {
|
|
nsCOMPtr<nsIBufferedInputStream> stream;
|
|
nsBufferedInputStream::Create(nullptr, NS_GET_IID(nsIBufferedInputStream),
|
|
getter_AddRefs(stream));
|
|
serializable = do_QueryInterface(stream);
|
|
} break;
|
|
|
|
case InputStreamParams::TMIMEInputStreamParams: {
|
|
nsCOMPtr<nsIMIMEInputStream> stream;
|
|
nsMIMEInputStreamConstructor(nullptr, NS_GET_IID(nsIMIMEInputStream),
|
|
getter_AddRefs(stream));
|
|
serializable = do_QueryInterface(stream);
|
|
} break;
|
|
|
|
case InputStreamParams::TMultiplexInputStreamParams: {
|
|
nsCOMPtr<nsIMultiplexInputStream> stream;
|
|
nsMultiplexInputStreamConstructor(
|
|
nullptr, NS_GET_IID(nsIMultiplexInputStream), getter_AddRefs(stream));
|
|
serializable = do_QueryInterface(stream);
|
|
} break;
|
|
|
|
case InputStreamParams::TSlicedInputStreamParams:
|
|
serializable = new SlicedInputStream();
|
|
break;
|
|
|
|
case InputStreamParams::TInputStreamLengthWrapperParams:
|
|
serializable = new InputStreamLengthWrapper();
|
|
break;
|
|
|
|
case InputStreamParams::TEncryptedFileInputStreamParams:
|
|
serializable = new dom::quota::DecryptingInputStream<
|
|
dom::quota::IPCStreamCipherStrategy>();
|
|
break;
|
|
|
|
default:
|
|
MOZ_ASSERT(false, "Unknown params!");
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(serializable);
|
|
|
|
if (!serializable->Deserialize(aParams, aFileDescriptors)) {
|
|
MOZ_ASSERT(false, "Deserialize failed!");
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(serializable);
|
|
MOZ_ASSERT(stream);
|
|
|
|
return stream.forget();
|
|
}
|
|
|
|
} // namespace ipc
|
|
} // namespace mozilla
|