/* -*- 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 "StructuredCloneData.h" #include "nsIMutable.h" #include "nsIXPConnect.h" #include "ipc/IPCMessageUtils.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/DOMTypes.h" #include "mozilla/dom/File.h" #include "mozilla/dom/IPCBlobUtils.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/IPCStreamUtils.h" #include "nsContentUtils.h" #include "nsJSEnvironment.h" #include "MainThreadUtils.h" #include "StructuredCloneTags.h" #include "jsapi.h" using namespace mozilla::ipc; namespace mozilla { namespace dom { namespace ipc { using mozilla::ipc::AutoIPCStream; using mozilla::ipc::IPCStream; using mozilla::ipc::PBackgroundChild; using mozilla::ipc::PBackgroundParent; StructuredCloneData::StructuredCloneData() : StructuredCloneData(StructuredCloneHolder::TransferringSupported) {} StructuredCloneData::StructuredCloneData(StructuredCloneData&& aOther) : StructuredCloneData(StructuredCloneHolder::TransferringSupported) { *this = std::move(aOther); } StructuredCloneData::StructuredCloneData( TransferringSupport aSupportsTransferring) : StructuredCloneHolder( StructuredCloneHolder::CloningSupported, aSupportsTransferring, StructuredCloneHolder::StructuredCloneScope::DifferentProcess), mExternalData(JS::StructuredCloneScope::DifferentProcess), mInitialized(false) {} StructuredCloneData::~StructuredCloneData() {} StructuredCloneData& StructuredCloneData::operator=( StructuredCloneData&& aOther) { mBlobImplArray = std::move(aOther.mBlobImplArray); mExternalData = std::move(aOther.mExternalData); mSharedData = std::move(aOther.mSharedData); mIPCStreams = std::move(aOther.mIPCStreams); mInitialized = aOther.mInitialized; return *this; } bool StructuredCloneData::Copy(const StructuredCloneData& aData) { if (!aData.mInitialized) { return true; } if (aData.SharedData()) { mSharedData = aData.SharedData(); } else { mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData.Data()); NS_ENSURE_TRUE(mSharedData, false); } if (mSupportsTransferring) { PortIdentifiers().AppendElements(aData.PortIdentifiers()); } MOZ_ASSERT(BlobImpls().IsEmpty()); BlobImpls().AppendElements(aData.BlobImpls()); MOZ_ASSERT(GetSurfaces().IsEmpty()); MOZ_ASSERT(WasmModules().IsEmpty()); MOZ_ASSERT(InputStreams().IsEmpty()); InputStreams().AppendElements(aData.InputStreams()); mInitialized = true; return true; } void StructuredCloneData::Read(JSContext* aCx, JS::MutableHandle aValue, ErrorResult& aRv) { MOZ_ASSERT(mInitialized); nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); MOZ_ASSERT(global); ReadFromBuffer(global, aCx, Data(), aValue, aRv); } void StructuredCloneData::Write(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv) { Write(aCx, aValue, JS::UndefinedHandleValue, aRv); } void StructuredCloneData::Write(JSContext* aCx, JS::Handle aValue, JS::Handle aTransfer, ErrorResult& aRv) { MOZ_ASSERT(!mInitialized); StructuredCloneHolder::Write(aCx, aValue, aTransfer, JS::CloneDataPolicy().denySharedArrayBuffer(), aRv); if (NS_WARN_IF(aRv.Failed())) { return; } JSStructuredCloneData data(mBuffer->scope()); mBuffer->abandon(); mBuffer->steal(&data); mBuffer = nullptr; mSharedData = new SharedJSAllocatedData(std::move(data)); mInitialized = true; } enum ActorFlavorEnum { Parent = 0, Child, }; enum ManagerFlavorEnum { ContentProtocol = 0, BackgroundProtocol }; template bool BuildClonedMessageData(M* aManager, StructuredCloneData& aData, ClonedMessageData& aClonedData) { SerializedStructuredCloneBuffer& buffer = aClonedData.data(); auto iter = aData.Data().Start(); size_t size = aData.Data().Size(); bool success; buffer.data = aData.Data().Borrow(iter, size, &success); if (NS_WARN_IF(!success)) { return false; } if (aData.SupportsTransferring()) { aClonedData.identfiers().AppendElements(aData.PortIdentifiers()); } const nsTArray>& blobImpls = aData.BlobImpls(); if (!blobImpls.IsEmpty()) { if (NS_WARN_IF( !aClonedData.blobs().SetLength(blobImpls.Length(), fallible))) { return false; } for (uint32_t i = 0; i < blobImpls.Length(); ++i) { nsresult rv = IPCBlobUtils::Serialize(blobImpls[i], aManager, aClonedData.blobs()[i]); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } } } const nsTArray>& inputStreams = aData.InputStreams(); if (!inputStreams.IsEmpty()) { if (NS_WARN_IF( !aData.IPCStreams().SetCapacity(inputStreams.Length(), fallible))) { return false; } InfallibleTArray& streams = aClonedData.inputStreams(); uint32_t length = inputStreams.Length(); streams.SetCapacity(length); for (uint32_t i = 0; i < length; ++i) { AutoIPCStream* stream = aData.IPCStreams().AppendElement(fallible); if (NS_WARN_IF(!stream)) { return false; } if (!stream->Serialize(inputStreams[i], aManager)) { return false; } streams.AppendElement(stream->TakeValue()); } } return true; } bool StructuredCloneData::BuildClonedMessageDataForParent( nsIContentParent* aParent, ClonedMessageData& aClonedData) { return BuildClonedMessageData(aParent, *this, aClonedData); } bool StructuredCloneData::BuildClonedMessageDataForChild( nsIContentChild* aChild, ClonedMessageData& aClonedData) { return BuildClonedMessageData(aChild, *this, aClonedData); } bool StructuredCloneData::BuildClonedMessageDataForBackgroundParent( PBackgroundParent* aParent, ClonedMessageData& aClonedData) { return BuildClonedMessageData(aParent, *this, aClonedData); } bool StructuredCloneData::BuildClonedMessageDataForBackgroundChild( PBackgroundChild* aChild, ClonedMessageData& aClonedData) { return BuildClonedMessageData(aChild, *this, aClonedData); } // See the StructuredCloneData class block comment for the meanings of each val. enum MemoryFlavorEnum { BorrowMemory = 0, CopyMemory, StealMemory }; template struct MemoryTraits {}; template <> struct MemoryTraits { typedef const mozilla::dom::ClonedMessageData ClonedMessageType; static void ProvideBuffer(const ClonedMessageData& aClonedData, StructuredCloneData& aData) { const SerializedStructuredCloneBuffer& buffer = aClonedData.data(); aData.UseExternalData(buffer.data); } }; template <> struct MemoryTraits { typedef const mozilla::dom::ClonedMessageData ClonedMessageType; static void ProvideBuffer(const ClonedMessageData& aClonedData, StructuredCloneData& aData) { const SerializedStructuredCloneBuffer& buffer = aClonedData.data(); aData.CopyExternalData(buffer.data); } }; template <> struct MemoryTraits { // note: not const! typedef mozilla::dom::ClonedMessageData ClonedMessageType; static void ProvideBuffer(ClonedMessageData& aClonedData, StructuredCloneData& aData) { SerializedStructuredCloneBuffer& buffer = aClonedData.data(); aData.StealExternalData(buffer.data); } }; // Note that there isn't actually a difference between Parent/BackgroundParent // and Child/BackgroundChild in this implementation. The calling methods, // however, do maintain the distinction for code-reading purposes and are backed // by assertions to enforce there is no misuse. template static void UnpackClonedMessageData( typename MemoryTraits::ClonedMessageType& aClonedData, StructuredCloneData& aData) { const InfallibleTArray& identifiers = aClonedData.identfiers(); MemoryTraits::ProvideBuffer(aClonedData, aData); if (aData.SupportsTransferring()) { aData.PortIdentifiers().AppendElements(identifiers); } const nsTArray& blobs = aClonedData.blobs(); if (!blobs.IsEmpty()) { uint32_t length = blobs.Length(); aData.BlobImpls().SetCapacity(length); for (uint32_t i = 0; i < length; ++i) { RefPtr blobImpl = IPCBlobUtils::Deserialize(blobs[i]); MOZ_ASSERT(blobImpl); aData.BlobImpls().AppendElement(blobImpl); } } const InfallibleTArray& streams = aClonedData.inputStreams(); if (!streams.IsEmpty()) { uint32_t length = streams.Length(); aData.InputStreams().SetCapacity(length); for (uint32_t i = 0; i < length; ++i) { nsCOMPtr stream = DeserializeIPCStream(streams[i]); aData.InputStreams().AppendElement(stream); } } } void StructuredCloneData::BorrowFromClonedMessageDataForParent( const ClonedMessageData& aClonedData) { // PContent parent is always main thread and actor constraints demand we're // likewise on that thread. MOZ_ASSERT(NS_IsMainThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::BorrowFromClonedMessageDataForChild( const ClonedMessageData& aClonedData) { // PContent child is always main thread and actor constraints demand we're // likewise on that thread. MOZ_ASSERT(NS_IsMainThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::BorrowFromClonedMessageDataForBackgroundParent( const ClonedMessageData& aClonedData) { MOZ_ASSERT(IsOnBackgroundThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::BorrowFromClonedMessageDataForBackgroundChild( const ClonedMessageData& aClonedData) { // No thread assertion; BackgroundChild can happen on any thread. UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::CopyFromClonedMessageDataForParent( const ClonedMessageData& aClonedData) { MOZ_ASSERT(NS_IsMainThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::CopyFromClonedMessageDataForChild( const ClonedMessageData& aClonedData) { MOZ_ASSERT(NS_IsMainThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::CopyFromClonedMessageDataForBackgroundParent( const ClonedMessageData& aClonedData) { MOZ_ASSERT(IsOnBackgroundThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::CopyFromClonedMessageDataForBackgroundChild( const ClonedMessageData& aClonedData) { UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::StealFromClonedMessageDataForParent( ClonedMessageData& aClonedData) { MOZ_ASSERT(NS_IsMainThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::StealFromClonedMessageDataForChild( ClonedMessageData& aClonedData) { MOZ_ASSERT(NS_IsMainThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::StealFromClonedMessageDataForBackgroundParent( ClonedMessageData& aClonedData) { MOZ_ASSERT(IsOnBackgroundThread()); UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::StealFromClonedMessageDataForBackgroundChild( ClonedMessageData& aClonedData) { UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const { WriteParam(aMsg, Data()); } bool StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg, PickleIterator* aIter) { MOZ_ASSERT(!mInitialized); JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess); if (!ReadParam(aMsg, aIter, &data)) { return false; } mSharedData = new SharedJSAllocatedData(std::move(data)); mInitialized = true; return true; } bool StructuredCloneData::CopyExternalData(const char* aData, size_t aDataLength) { MOZ_ASSERT(!mInitialized); mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData, aDataLength); NS_ENSURE_TRUE(mSharedData, false); mInitialized = true; return true; } bool StructuredCloneData::CopyExternalData(const JSStructuredCloneData& aData) { MOZ_ASSERT(!mInitialized); mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData); NS_ENSURE_TRUE(mSharedData, false); mInitialized = true; return true; } bool StructuredCloneData::StealExternalData(JSStructuredCloneData& aData) { MOZ_ASSERT(!mInitialized); mSharedData = new SharedJSAllocatedData(std::move(aData)); mInitialized = true; return true; } already_AddRefed StructuredCloneData::TakeSharedData() { return mSharedData.forget(); } } // namespace ipc } // namespace dom } // namespace mozilla