/* -*- 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 "nsIDOMDOMException.h" #include "nsIMutable.h" #include "nsIXPConnect.h" #include "ipc/IPCMessageUtils.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/IPCBlobUtils.h" #include "mozilla/dom/File.h" #include "mozilla/ipc/IPCStreamUtils.h" #include "nsContentUtils.h" #include "nsJSEnvironment.h" #include "MainThreadUtils.h" #include "StructuredCloneTags.h" #include "jsapi.h" namespace mozilla { namespace dom { namespace ipc { StructuredCloneData::StructuredCloneData() : StructuredCloneData(StructuredCloneHolder::TransferringSupported) {} StructuredCloneData::StructuredCloneData(StructuredCloneData&& aOther) : StructuredCloneData(StructuredCloneHolder::TransferringSupported) { *this = Move(aOther); } StructuredCloneData::StructuredCloneData(TransferringSupport aSupportsTransferring) : StructuredCloneHolder(StructuredCloneHolder::CloningSupported, aSupportsTransferring, StructuredCloneHolder::StructuredCloneScope::DifferentProcess) , mInitialized(false) {} StructuredCloneData::~StructuredCloneData() {} StructuredCloneData& StructuredCloneData::operator=(StructuredCloneData&& aOther) { mExternalData = Move(aOther.mExternalData); mSharedData = Move(aOther.mSharedData); mIPCStreams = 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::NativeGlobal(JS::CurrentGlobalOrNull(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->abandon(); mBuffer->steal(&data); mBuffer = nullptr; mSharedData = new SharedJSAllocatedData(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().Iter(); 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; if (!ReadParam(aMsg, aIter, &data)) { return false; } mSharedData = new SharedJSAllocatedData(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(Move(aData)); mInitialized = true; return true; } } // namespace ipc } // namespace dom } // namespace mozilla