/* -*- 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 "IPCBlobUtils.h" #include "RemoteLazyInputStream.h" #include "RemoteLazyInputStreamChild.h" #include "RemoteLazyInputStreamParent.h" #include "RemoteLazyInputStreamUtils.h" #include "mozilla/dom/IPCBlob.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/PBackgroundParent.h" #include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/ipc/IPCStreamUtils.h" #include "mozilla/ipc/ProtocolUtils.h" #include "RemoteLazyInputStreamStorage.h" #include "StreamBlobImpl.h" #include "prtime.h" namespace mozilla { using namespace ipc; namespace dom::IPCBlobUtils { already_AddRefed Deserialize(const IPCBlob& aIPCBlob) { nsCOMPtr inputStream; const RemoteLazyStream& stream = aIPCBlob.inputStream(); switch (stream.type()) { // Parent to child: when an nsIInputStream is sent from parent to child, the // child receives a RemoteLazyInputStream actor. case RemoteLazyStream::TPRemoteLazyInputStreamChild: { RemoteLazyInputStreamChild* actor = static_cast( stream.get_PRemoteLazyInputStreamChild()); inputStream = actor->CreateStream(); break; } // Child to Parent: when a blob is created on the content process send it's // sent to the parent, we have an IPCStream object. case RemoteLazyStream::TIPCStream: MOZ_ASSERT(XRE_IsParentProcess()); inputStream = DeserializeIPCStream(stream.get_IPCStream()); break; default: MOZ_CRASH("Unknown type."); break; } MOZ_ASSERT(inputStream); RefPtr blobImpl; if (aIPCBlob.file().isNothing()) { blobImpl = StreamBlobImpl::Create(inputStream.forget(), aIPCBlob.type(), aIPCBlob.size(), aIPCBlob.blobImplType()); } else { const IPCFile& file = aIPCBlob.file().ref(); blobImpl = StreamBlobImpl::Create(inputStream.forget(), file.name(), aIPCBlob.type(), file.lastModified(), aIPCBlob.size(), aIPCBlob.blobImplType()); blobImpl->SetDOMPath(file.DOMPath()); blobImpl->SetFullPath(file.fullPath()); blobImpl->SetIsDirectory(file.isDirectory()); } blobImpl->SetFileId(aIPCBlob.fileId()); return blobImpl.forget(); } template nsresult SerializeInternal(BlobImpl* aBlobImpl, M* aManager, IPCBlob& aIPCBlob) { MOZ_ASSERT(aBlobImpl); nsAutoString value; aBlobImpl->GetType(value); aIPCBlob.type() = value; aBlobImpl->GetBlobImplType(value); aIPCBlob.blobImplType() = value; ErrorResult rv; aIPCBlob.size() = aBlobImpl->GetSize(rv); if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } if (!aBlobImpl->IsFile()) { aIPCBlob.file() = Nothing(); } else { IPCFile file; aBlobImpl->GetName(value); file.name() = value; file.lastModified() = aBlobImpl->GetLastModified(rv) * PR_USEC_PER_MSEC; if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } aBlobImpl->GetDOMPath(value); file.DOMPath() = value; aBlobImpl->GetMozFullPathInternal(value, rv); if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } file.fullPath() = value; file.isDirectory() = aBlobImpl->IsDirectory(); aIPCBlob.file() = Some(file); } aIPCBlob.fileId() = aBlobImpl->GetFileId(); nsCOMPtr inputStream; aBlobImpl->CreateInputStream(getter_AddRefs(inputStream), rv); if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } RemoteLazyStream stream; rv = RemoteLazyInputStreamUtils::SerializeInputStream( inputStream, aIPCBlob.size(), stream, aManager); if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } aIPCBlob.inputStream() = stream; return NS_OK; } nsresult Serialize(BlobImpl* aBlobImpl, ContentChild* aManager, IPCBlob& aIPCBlob) { return SerializeInternal(aBlobImpl, aManager, aIPCBlob); } nsresult Serialize(BlobImpl* aBlobImpl, PBackgroundChild* aManager, IPCBlob& aIPCBlob) { return SerializeInternal(aBlobImpl, aManager, aIPCBlob); } nsresult Serialize(BlobImpl* aBlobImpl, ContentParent* aManager, IPCBlob& aIPCBlob) { return SerializeInternal(aBlobImpl, aManager, aIPCBlob); } nsresult Serialize(BlobImpl* aBlobImpl, PBackgroundParent* aManager, IPCBlob& aIPCBlob) { return SerializeInternal(aBlobImpl, aManager, aIPCBlob); } nsresult SerializeUntyped(BlobImpl* aBlobImpl, IProtocol* aActor, IPCBlob& aIPCBlob) { // We always want to act on the toplevel protocol. IProtocol* manager = aActor; while (manager->Manager()) { manager = manager->Manager(); } // We always need the toplevel protocol switch (manager->GetProtocolId()) { case PBackgroundMsgStart: if (manager->GetSide() == mozilla::ipc::ParentSide) { return SerializeInternal( aBlobImpl, static_cast(manager), aIPCBlob); } else { return SerializeInternal( aBlobImpl, static_cast(manager), aIPCBlob); } case PContentMsgStart: if (manager->GetSide() == mozilla::ipc::ParentSide) { return SerializeInternal( aBlobImpl, static_cast(manager), aIPCBlob); } else { return SerializeInternal(aBlobImpl, static_cast(manager), aIPCBlob); } default: MOZ_CRASH("Unsupported protocol passed to BlobImpl serialize"); } } } // namespace dom::IPCBlobUtils namespace ipc { void IPDLParamTraits::Write( IPC::Message* aMsg, IProtocol* aActor, mozilla::dom::BlobImpl* aParam) { nsresult rv; mozilla::dom::IPCBlob ipcblob; if (aParam) { rv = mozilla::dom::IPCBlobUtils::SerializeUntyped(aParam, aActor, ipcblob); } if (!aParam || NS_WARN_IF(NS_FAILED(rv))) { WriteIPDLParam(aMsg, aActor, false); } else { WriteIPDLParam(aMsg, aActor, true); WriteIPDLParam(aMsg, aActor, ipcblob); } } bool IPDLParamTraits::Read( const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor, RefPtr* aResult) { *aResult = nullptr; bool notnull = false; if (!ReadIPDLParam(aMsg, aIter, aActor, ¬null)) { return false; } if (notnull) { mozilla::dom::IPCBlob ipcblob; if (!ReadIPDLParam(aMsg, aIter, aActor, &ipcblob)) { return false; } *aResult = mozilla::dom::IPCBlobUtils::Deserialize(ipcblob); } return true; } } // namespace ipc } // namespace mozilla