/* -*- 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 "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/BlobImpl.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 "mozilla/ipc/SerializedStructuredCloneBuffer.h" #include "nsContentUtils.h" #include "nsJSEnvironment.h" #include "MainThreadUtils.h" #include "StructuredCloneTags.h" #include "jsapi.h" using namespace mozilla::ipc; namespace mozilla::dom::ipc { using mozilla::ipc::IPCStream; using mozilla::ipc::PBackgroundChild; using mozilla::ipc::PBackgroundParent; StructuredCloneData::StructuredCloneData() : StructuredCloneData( StructuredCloneHolder::StructuredCloneScope::DifferentProcess, StructuredCloneHolder::TransferringSupported) {} StructuredCloneData::StructuredCloneData(StructuredCloneData&& aOther) : StructuredCloneData( StructuredCloneHolder::StructuredCloneScope::DifferentProcess, StructuredCloneHolder::TransferringSupported) { *this = std::move(aOther); } StructuredCloneData::StructuredCloneData( StructuredCloneHolder::StructuredCloneScope aScope, TransferringSupport aSupportsTransferring) : StructuredCloneHolder(StructuredCloneHolder::CloningSupported, aSupportsTransferring, aScope), mExternalData(JS::StructuredCloneScope::DifferentProcess), mInitialized(false) { MOZ_ASSERT( aScope == StructuredCloneHolder::StructuredCloneScope::DifferentProcess || aScope == StructuredCloneHolder::StructuredCloneScope::UnknownDestination); } StructuredCloneData::~StructuredCloneData() = default; StructuredCloneData& StructuredCloneData::operator=( StructuredCloneData&& aOther) { mBlobImplArray = std::move(aOther.mBlobImplArray); mExternalData = std::move(aOther.mExternalData); mSharedData = std::move(aOther.mSharedData); mPortIdentifiers = std::move(aOther.mPortIdentifiers); 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) { Read(aCx, aValue, JS::CloneDataPolicy(), aRv); } void StructuredCloneData::Read(JSContext* aCx, JS::MutableHandle aValue, const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv) { MOZ_ASSERT(mInitialized); nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); MOZ_ASSERT(global); ReadFromBuffer(global, aCx, Data(), aValue, aCloneDataPolicy, aRv); } void StructuredCloneData::Write(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv) { Write(aCx, aValue, JS::UndefinedHandleValue, JS::CloneDataPolicy(), aRv); } void StructuredCloneData::Write(JSContext* aCx, JS::Handle aValue, JS::Handle aTransfer, const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv) { MOZ_ASSERT(!mInitialized); StructuredCloneHolder::Write(aCx, aValue, aTransfer, aCloneDataPolicy, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } JSStructuredCloneData data(mBuffer->scope()); mBuffer->giveTo(&data); mBuffer = nullptr; mSharedData = new SharedJSAllocatedData(std::move(data)); mInitialized = true; } bool StructuredCloneData::BuildClonedMessageData( ClonedMessageData& aClonedData) { SerializedStructuredCloneBuffer& buffer = aClonedData.data(); auto iter = Data().Start(); size_t size = Data().Size(); bool success; buffer.data = Data().Borrow(iter, size, &success); if (NS_WARN_IF(!success)) { return false; } if (SupportsTransferring()) { aClonedData.identifiers().AppendElements(PortIdentifiers()); } const nsTArray>& blobImpls = 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], aClonedData.blobs()[i]); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } } } const nsTArray>& inputStreams = InputStreams(); if (!inputStreams.IsEmpty()) { nsTArray& streams = aClonedData.inputStreams(); uint32_t length = inputStreams.Length(); streams.SetCapacity(length); for (uint32_t i = 0; i < length; ++i) { IPCStream value; if (!mozilla::ipc::SerializeIPCStream(do_AddRef(inputStreams[i]), value, /* aAllowLazy */ false)) { return false; } streams.AppendElement(value); } } return true; } // See the StructuredCloneData class block comment for the meanings of each val. enum MemoryFlavorEnum { BorrowMemory = 0, CopyMemory, StealMemory }; template struct MemoryTraits {}; template <> struct MemoryTraits { using ClonedMessageType = const mozilla::dom::ClonedMessageData; static void ProvideBuffer(const ClonedMessageData& aClonedData, StructuredCloneData& aData) { const SerializedStructuredCloneBuffer& buffer = aClonedData.data(); aData.UseExternalData(buffer.data); } }; template <> struct MemoryTraits { using ClonedMessageType = const mozilla::dom::ClonedMessageData; static void ProvideBuffer(const ClonedMessageData& aClonedData, StructuredCloneData& aData) { const SerializedStructuredCloneBuffer& buffer = aClonedData.data(); aData.CopyExternalData(buffer.data); } }; template <> struct MemoryTraits { // note: not const! using ClonedMessageType = mozilla::dom::ClonedMessageData; 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 nsTArray& identifiers = aClonedData.identifiers(); 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 nsTArray& 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::BorrowFromClonedMessageData( const ClonedMessageData& aClonedData) { UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::CopyFromClonedMessageData( const ClonedMessageData& aClonedData) { UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::StealFromClonedMessageData( ClonedMessageData& aClonedData) { UnpackClonedMessageData(aClonedData, *this); } void StructuredCloneData::WriteIPCParams(IPC::MessageWriter* aWriter) const { WriteParam(aWriter, Data()); } bool StructuredCloneData::ReadIPCParams(IPC::MessageReader* aReader) { MOZ_ASSERT(!mInitialized); JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess); if (!ReadParam(aReader, &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 mozilla::dom::ipc