2016-05-27 21:54:31 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-08-16 04:02:32 +00:00
|
|
|
/* 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"
|
2014-10-08 16:15:23 +00:00
|
|
|
#include "mozilla/dom/File.h"
|
2019-04-26 18:04:02 +00:00
|
|
|
#include "mozilla/dom/IPCBlobInputStream.h"
|
|
|
|
#include "mozilla/dom/IPCBlobInputStreamStorage.h"
|
2019-01-28 09:49:13 +00:00
|
|
|
#include "mozilla/ipc/IPCStreamDestination.h"
|
|
|
|
#include "mozilla/ipc/IPCStreamSource.h"
|
|
|
|
#include "mozilla/InputStreamLengthHelper.h"
|
2017-11-07 00:04:06 +00:00
|
|
|
#include "mozilla/SlicedInputStream.h"
|
2018-05-31 16:12:25 +00:00
|
|
|
#include "mozilla/InputStreamLengthWrapper.h"
|
2012-08-16 04:02:32 +00:00
|
|
|
#include "nsComponentManagerUtils.h"
|
|
|
|
#include "nsDebug.h"
|
2019-01-28 09:49:13 +00:00
|
|
|
#include "nsIAsyncInputStream.h"
|
|
|
|
#include "nsIAsyncOutputStream.h"
|
2012-08-16 04:02:32 +00:00
|
|
|
#include "nsID.h"
|
2019-01-28 09:49:13 +00:00
|
|
|
#include "nsIPipe.h"
|
2012-09-21 17:26:13 +00:00
|
|
|
#include "nsIXULRuntime.h"
|
2012-08-23 02:13:54 +00:00
|
|
|
#include "nsMIMEInputStream.h"
|
2012-08-16 04:02:32 +00:00
|
|
|
#include "nsMultiplexInputStream.h"
|
|
|
|
#include "nsNetCID.h"
|
2019-01-28 09:49:13 +00:00
|
|
|
#include "nsStreamUtils.h"
|
2012-08-16 04:02:32 +00:00
|
|
|
#include "nsStringStream.h"
|
2012-09-21 17:26:13 +00:00
|
|
|
#include "nsXULAppAPI.h"
|
2012-08-16 04:02:32 +00:00
|
|
|
|
2019-01-28 09:49:13 +00:00
|
|
|
using namespace mozilla;
|
2014-09-26 23:21:57 +00:00
|
|
|
using namespace mozilla::dom;
|
2012-08-16 04:02:32 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
NS_DEFINE_CID(kStringInputStreamCID, NS_STRINGINPUTSTREAM_CID);
|
|
|
|
NS_DEFINE_CID(kFileInputStreamCID, NS_LOCALFILEINPUTSTREAM_CID);
|
2012-08-23 02:13:54 +00:00
|
|
|
NS_DEFINE_CID(kBufferedInputStreamCID, NS_BUFFEREDINPUTSTREAM_CID);
|
|
|
|
NS_DEFINE_CID(kMIMEInputStreamCID, NS_MIMEINPUTSTREAM_CID);
|
2012-08-16 04:02:32 +00:00
|
|
|
NS_DEFINE_CID(kMultiplexInputStreamCID, NS_MULTIPLEXINPUTSTREAM_CID);
|
|
|
|
|
2015-07-13 15:25:42 +00:00
|
|
|
} // namespace
|
2012-08-16 04:02:32 +00:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace ipc {
|
|
|
|
|
2019-01-28 09:48:35 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
template <typename M>
|
|
|
|
void SerializeInputStreamInternal(nsIInputStream* aInputStream,
|
|
|
|
InputStreamParams& aParams,
|
|
|
|
nsTArray<FileDescriptor>& aFileDescriptors,
|
2019-02-04 22:50:51 +00:00
|
|
|
bool aDelayedStart, uint32_t aMaxSize,
|
|
|
|
uint32_t* aSizeUsed, M* aManager) {
|
2012-08-23 02:13:54 +00:00
|
|
|
MOZ_ASSERT(aInputStream);
|
2019-01-28 09:48:35 +00:00
|
|
|
MOZ_ASSERT(aManager);
|
2012-08-23 02:13:54 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIIPCSerializableInputStream> serializable =
|
|
|
|
do_QueryInterface(aInputStream);
|
|
|
|
if (!serializable) {
|
2013-06-29 01:38:30 +00:00
|
|
|
MOZ_CRASH("Input stream is not serializable!");
|
2012-08-23 02:13:54 +00:00
|
|
|
}
|
|
|
|
|
2019-02-04 22:50:51 +00:00
|
|
|
serializable->Serialize(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
|
|
|
|
aSizeUsed, aManager);
|
2012-08-23 02:13:54 +00:00
|
|
|
|
2012-08-23 19:33:46 +00:00
|
|
|
if (aParams.type() == InputStreamParams::T__None) {
|
2013-06-29 01:38:30 +00:00
|
|
|
MOZ_CRASH("Serialize failed!");
|
2012-08-23 19:33:46 +00:00
|
|
|
}
|
2012-08-23 02:13:54 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 09:49:28 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// As a fallback, attempt to stream the data across using a IPCStream
|
|
|
|
// actor. For blocking streams, create a nonblocking pipe instead,
|
|
|
|
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aInputStream);
|
|
|
|
if (!asyncStream) {
|
|
|
|
const uint32_t kBufferSize = 32768; // matches IPCStream buffer size.
|
|
|
|
nsCOMPtr<nsIAsyncOutputStream> sink;
|
|
|
|
nsresult rv = NS_NewPipe2(getter_AddRefs(asyncStream), getter_AddRefs(sink),
|
|
|
|
true, false, kBufferSize, UINT32_MAX);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIEventTarget> target =
|
|
|
|
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
|
|
|
|
|
|
|
rv = NS_AsyncCopy(aInputStream, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS,
|
|
|
|
kBufferSize);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(asyncStream);
|
|
|
|
|
|
|
|
aParams = IPCRemoteStreamParams(
|
|
|
|
aDelayedStart, IPCStreamSource::Create(asyncStream, aManager), length);
|
|
|
|
}
|
|
|
|
|
2019-01-28 09:48:35 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
void InputStreamHelper::SerializeInputStream(
|
|
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
|
|
nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
|
2019-02-25 20:04:49 +00:00
|
|
|
uint32_t aMaxSize, uint32_t* aSizeUsed, ContentChild* aManager) {
|
2019-01-28 09:48:35 +00:00
|
|
|
SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
|
2019-02-04 22:50:51 +00:00
|
|
|
aDelayedStart, aMaxSize, aSizeUsed, aManager);
|
2019-01-28 09:48:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputStreamHelper::SerializeInputStream(
|
|
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
|
|
nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
|
2019-02-04 22:50:51 +00:00
|
|
|
uint32_t aMaxSize, uint32_t* aSizeUsed, PBackgroundChild* aManager) {
|
2019-01-28 09:48:35 +00:00
|
|
|
SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
|
2019-02-04 22:50:51 +00:00
|
|
|
aDelayedStart, aMaxSize, aSizeUsed, aManager);
|
2019-01-28 09:48:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputStreamHelper::SerializeInputStream(
|
|
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
|
|
nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
|
2019-02-25 20:04:47 +00:00
|
|
|
uint32_t aMaxSize, uint32_t* aSizeUsed, ContentParent* aManager) {
|
2019-01-28 09:48:35 +00:00
|
|
|
SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
|
2019-02-04 22:50:51 +00:00
|
|
|
aDelayedStart, aMaxSize, aSizeUsed, aManager);
|
2019-01-28 09:48:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputStreamHelper::SerializeInputStream(
|
|
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
|
|
nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
|
2019-02-04 22:50:51 +00:00
|
|
|
uint32_t aMaxSize, uint32_t* aSizeUsed, PBackgroundParent* aManager) {
|
2019-01-28 09:48:35 +00:00
|
|
|
SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
|
2019-02-04 22:50:51 +00:00
|
|
|
aDelayedStart, aMaxSize, aSizeUsed, aManager);
|
2019-01-28 09:48:35 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 09:49:13 +00:00
|
|
|
void InputStreamHelper::SerializeInputStreamAsPipe(nsIInputStream* aInputStream,
|
|
|
|
InputStreamParams& aParams,
|
|
|
|
bool aDelayedStart,
|
2019-02-25 20:04:49 +00:00
|
|
|
ContentChild* aManager) {
|
2019-01-28 09:49:13 +00:00
|
|
|
SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
|
|
|
|
aManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputStreamHelper::SerializeInputStreamAsPipe(nsIInputStream* aInputStream,
|
|
|
|
InputStreamParams& aParams,
|
|
|
|
bool aDelayedStart,
|
|
|
|
PBackgroundChild* aManager) {
|
|
|
|
SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
|
|
|
|
aManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputStreamHelper::SerializeInputStreamAsPipe(nsIInputStream* aInputStream,
|
|
|
|
InputStreamParams& aParams,
|
|
|
|
bool aDelayedStart,
|
2019-02-25 20:04:47 +00:00
|
|
|
ContentParent* aManager) {
|
2019-01-28 09:49:13 +00:00
|
|
|
SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
|
|
|
|
aManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputStreamHelper::SerializeInputStreamAsPipe(
|
|
|
|
nsIInputStream* aInputStream, InputStreamParams& aParams,
|
|
|
|
bool aDelayedStart, PBackgroundParent* aManager) {
|
|
|
|
SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
|
|
|
|
aManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputStreamHelper::PostSerializationActivation(InputStreamParams& aParams,
|
|
|
|
bool aConsumedByIPC,
|
|
|
|
bool aDelayedStart) {
|
|
|
|
switch (aParams.type()) {
|
|
|
|
case InputStreamParams::TBufferedInputStreamParams: {
|
|
|
|
BufferedInputStreamParams& params =
|
|
|
|
aParams.get_BufferedInputStreamParams();
|
|
|
|
InputStreamHelper::PostSerializationActivation(
|
|
|
|
params.optionalStream(), aConsumedByIPC, aDelayedStart);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case InputStreamParams::TMIMEInputStreamParams: {
|
|
|
|
MIMEInputStreamParams& params = aParams.get_MIMEInputStreamParams();
|
|
|
|
InputStreamHelper::PostSerializationActivation(
|
|
|
|
params.optionalStream(), aConsumedByIPC, aDelayedStart);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case InputStreamParams::TMultiplexInputStreamParams: {
|
|
|
|
MultiplexInputStreamParams& params =
|
|
|
|
aParams.get_MultiplexInputStreamParams();
|
|
|
|
for (InputStreamParams& subParams : params.streams()) {
|
|
|
|
InputStreamHelper::PostSerializationActivation(
|
|
|
|
subParams, aConsumedByIPC, aDelayedStart);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case InputStreamParams::TSlicedInputStreamParams: {
|
|
|
|
SlicedInputStreamParams& params = aParams.get_SlicedInputStreamParams();
|
|
|
|
InputStreamHelper::PostSerializationActivation(
|
|
|
|
params.stream(), aConsumedByIPC, aDelayedStart);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case InputStreamParams::TInputStreamLengthWrapperParams: {
|
|
|
|
InputStreamLengthWrapperParams& params =
|
|
|
|
aParams.get_InputStreamLengthWrapperParams();
|
|
|
|
InputStreamHelper::PostSerializationActivation(
|
|
|
|
params.stream(), aConsumedByIPC, aDelayedStart);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case InputStreamParams::TIPCRemoteStreamParams: {
|
|
|
|
IPCRemoteStreamType& remoteInputStream =
|
|
|
|
aParams.get_IPCRemoteStreamParams().stream();
|
|
|
|
|
|
|
|
IPCStreamSource* source = nullptr;
|
|
|
|
if (remoteInputStream.type() ==
|
|
|
|
IPCRemoteStreamType::TPChildToParentStreamChild) {
|
|
|
|
source = IPCStreamSource::Cast(
|
|
|
|
remoteInputStream.get_PChildToParentStreamChild());
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(remoteInputStream.type() ==
|
|
|
|
IPCRemoteStreamType::TPParentToChildStreamParent);
|
|
|
|
source = IPCStreamSource::Cast(
|
|
|
|
remoteInputStream.get_PParentToChildStreamParent());
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(source);
|
|
|
|
|
|
|
|
// If the source stream has not been taken to be sent to the other side,
|
|
|
|
// we can destroy it.
|
|
|
|
if (!aConsumedByIPC) {
|
|
|
|
source->StartDestroy();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aDelayedStart) {
|
|
|
|
// If we don't need to do a delayedStart, we start it now. Otherwise,
|
|
|
|
// the Start() will be called at the first use by the
|
|
|
|
// IPCStreamDestination::DelayedStartInputStream.
|
|
|
|
source->Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case InputStreamParams::TStringInputStreamParams:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InputStreamParams::TFileInputStreamParams:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InputStreamParams::TIPCBlobInputStreamParams:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
MOZ_CRASH(
|
|
|
|
"A new stream? Should decide if it must be processed recursively or "
|
|
|
|
"not.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputStreamHelper::PostSerializationActivation(
|
2019-03-12 15:52:26 +00:00
|
|
|
Maybe<InputStreamParams>& aParams, bool aConsumedByIPC,
|
2019-01-28 09:49:13 +00:00
|
|
|
bool aDelayedStart) {
|
2019-03-12 15:52:26 +00:00
|
|
|
if (aParams.isSome()) {
|
2019-01-28 09:49:13 +00:00
|
|
|
InputStreamHelper::PostSerializationActivation(
|
2019-03-12 15:52:26 +00:00
|
|
|
aParams.ref(), aConsumedByIPC, aDelayedStart);
|
2019-01-28 09:49:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-21 06:47:25 +00:00
|
|
|
already_AddRefed<nsIInputStream> InputStreamHelper::DeserializeInputStream(
|
|
|
|
const InputStreamParams& aParams,
|
|
|
|
const nsTArray<FileDescriptor>& aFileDescriptors) {
|
2017-04-24 10:09:40 +00:00
|
|
|
// IPCBlobInputStreams are not deserializable on the parent side.
|
|
|
|
if (aParams.type() == InputStreamParams::TIPCBlobInputStreamParams) {
|
|
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
2019-01-28 09:49:13 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIInputStream> stream;
|
2017-04-24 10:09:40 +00:00
|
|
|
IPCBlobInputStreamStorage::Get()->GetStream(
|
|
|
|
aParams.get_IPCBlobInputStreamParams().id(),
|
2017-09-11 15:29:44 +00:00
|
|
|
aParams.get_IPCBlobInputStreamParams().start(),
|
|
|
|
aParams.get_IPCBlobInputStreamParams().length(),
|
2017-04-24 10:09:40 +00:00
|
|
|
getter_AddRefs(stream));
|
|
|
|
return stream.forget();
|
|
|
|
}
|
|
|
|
|
2019-01-28 09:49:13 +00:00
|
|
|
if (aParams.type() == InputStreamParams::TIPCRemoteStreamParams) {
|
|
|
|
const IPCRemoteStreamParams& remoteStream =
|
|
|
|
aParams.get_IPCRemoteStreamParams();
|
|
|
|
const IPCRemoteStreamType& remoteStreamType = remoteStream.stream();
|
|
|
|
IPCStreamDestination* destinationStream;
|
|
|
|
|
|
|
|
if (remoteStreamType.type() ==
|
|
|
|
IPCRemoteStreamType::TPChildToParentStreamParent) {
|
|
|
|
destinationStream = IPCStreamDestination::Cast(
|
|
|
|
remoteStreamType.get_PChildToParentStreamParent());
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(remoteStreamType.type() ==
|
|
|
|
IPCRemoteStreamType::TPParentToChildStreamChild);
|
|
|
|
destinationStream = IPCStreamDestination::Cast(
|
|
|
|
remoteStreamType.get_PParentToChildStreamChild());
|
|
|
|
}
|
|
|
|
|
|
|
|
destinationStream->SetDelayedStart(remoteStream.delayedStart());
|
|
|
|
destinationStream->SetLength(remoteStream.length());
|
|
|
|
return destinationStream->TakeReader();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIIPCSerializableInputStream> serializable;
|
|
|
|
|
2012-08-16 04:02:32 +00:00
|
|
|
switch (aParams.type()) {
|
|
|
|
case InputStreamParams::TStringInputStreamParams:
|
|
|
|
serializable = do_CreateInstance(kStringInputStreamCID);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InputStreamParams::TFileInputStreamParams:
|
|
|
|
serializable = do_CreateInstance(kFileInputStreamCID);
|
|
|
|
break;
|
|
|
|
|
2012-08-23 02:13:54 +00:00
|
|
|
case InputStreamParams::TBufferedInputStreamParams:
|
|
|
|
serializable = do_CreateInstance(kBufferedInputStreamCID);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InputStreamParams::TMIMEInputStreamParams:
|
|
|
|
serializable = do_CreateInstance(kMIMEInputStreamCID);
|
|
|
|
break;
|
|
|
|
|
2012-08-16 04:02:32 +00:00
|
|
|
case InputStreamParams::TMultiplexInputStreamParams:
|
|
|
|
serializable = do_CreateInstance(kMultiplexInputStreamCID);
|
|
|
|
break;
|
|
|
|
|
2017-02-22 08:41:05 +00:00
|
|
|
case InputStreamParams::TSlicedInputStreamParams:
|
2019-01-28 09:49:13 +00:00
|
|
|
serializable = new SlicedInputStream();
|
2017-02-22 08:41:05 +00:00
|
|
|
break;
|
|
|
|
|
2018-05-31 16:12:25 +00:00
|
|
|
case InputStreamParams::TInputStreamLengthWrapperParams:
|
2019-01-28 09:49:13 +00:00
|
|
|
serializable = new InputStreamLengthWrapper();
|
2018-05-31 16:12:25 +00:00
|
|
|
break;
|
|
|
|
|
2012-08-16 04:02:32 +00:00
|
|
|
default:
|
2012-08-23 02:13:54 +00:00
|
|
|
MOZ_ASSERT(false, "Unknown params!");
|
2012-08-16 04:02:32 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(serializable);
|
|
|
|
|
2014-03-25 18:37:13 +00:00
|
|
|
if (!serializable->Deserialize(aParams, aFileDescriptors)) {
|
2012-08-23 02:13:54 +00:00
|
|
|
MOZ_ASSERT(false, "Deserialize failed!");
|
2012-08-16 04:02:32 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-01-28 09:49:13 +00:00
|
|
|
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(serializable);
|
2012-08-16 04:02:32 +00:00
|
|
|
MOZ_ASSERT(stream);
|
|
|
|
|
|
|
|
return stream.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace ipc
|
|
|
|
} // namespace mozilla
|