/* -*- 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 void SerializeInputStreamInternal(nsIInputStream* aInputStream, InputStreamParams& aParams, nsTArray& aFileDescriptors, bool aDelayedStart, uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) { MOZ_ASSERT(aInputStream); MOZ_ASSERT(aManager); nsCOMPtr 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 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 sender; RefPtr receiver; nsresult rv = NewDataPipe(kDefaultDataPipeCapacity, getter_AddRefs(sender), getter_AddRefs(receiver)); if (NS_WARN_IF(NS_FAILED(rv))) { return; } nsCOMPtr 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 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& 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& 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 InputStreamHelper::DeserializeInputStream( const InputStreamParams& aParams, const nsTArray& 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 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( params.get_PRemoteLazyInputStreamChild()); nsCOMPtr stream = actor->CreateStream(); return stream.forget(); } if (aParams.type() == InputStreamParams::TDataPipeReceiverStreamParams) { const DataPipeReceiverStreamParams& pipeParams = aParams.get_DataPipeReceiverStreamParams(); return do_AddRef(pipeParams.pipe()); } nsCOMPtr serializable; switch (aParams.type()) { case InputStreamParams::TStringInputStreamParams: { nsCOMPtr stream; NS_NewCStringInputStream(getter_AddRefs(stream), ""_ns); serializable = do_QueryInterface(stream); } break; case InputStreamParams::TFileInputStreamParams: { nsCOMPtr stream; nsFileInputStream::Create(nullptr, NS_GET_IID(nsIFileInputStream), getter_AddRefs(stream)); serializable = do_QueryInterface(stream); } break; case InputStreamParams::TBufferedInputStreamParams: { nsCOMPtr stream; nsBufferedInputStream::Create(nullptr, NS_GET_IID(nsIBufferedInputStream), getter_AddRefs(stream)); serializable = do_QueryInterface(stream); } break; case InputStreamParams::TMIMEInputStreamParams: { nsCOMPtr stream; nsMIMEInputStreamConstructor(nullptr, NS_GET_IID(nsIMIMEInputStream), getter_AddRefs(stream)); serializable = do_QueryInterface(stream); } break; case InputStreamParams::TMultiplexInputStreamParams: { nsCOMPtr 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 stream = do_QueryInterface(serializable); MOZ_ASSERT(stream); return stream.forget(); } } // namespace ipc } // namespace mozilla