diff options
Diffstat (limited to 'dom/ipc/StructuredCloneData.cpp')
-rw-r--r-- | dom/ipc/StructuredCloneData.cpp | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp new file mode 100644 index 0000000000..956653ce59 --- /dev/null +++ b/dom/ipc/StructuredCloneData.cpp @@ -0,0 +1,328 @@ +/* -*- 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); + 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<JS::Value> aValue, + ErrorResult& aRv) { + Read(aCx, aValue, JS::CloneDataPolicy(), aRv); +} + +void StructuredCloneData::Read(JSContext* aCx, + JS::MutableHandle<JS::Value> 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<JS::Value> aValue, + ErrorResult& aRv) { + Write(aCx, aValue, JS::UndefinedHandleValue, JS::CloneDataPolicy(), aRv); +} + +void StructuredCloneData::Write(JSContext* aCx, JS::Handle<JS::Value> aValue, + JS::Handle<JS::Value> 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<RefPtr<BlobImpl>>& 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<nsCOMPtr<nsIInputStream>>& inputStreams = InputStreams(); + if (!inputStreams.IsEmpty()) { + nsTArray<IPCStream>& 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 <MemoryFlavorEnum> +struct MemoryTraits {}; + +template <> +struct MemoryTraits<BorrowMemory> { + 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<CopyMemory> { + 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<StealMemory> { + // 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 <MemoryFlavorEnum MemoryFlavor> +static void UnpackClonedMessageData( + typename MemoryTraits<MemoryFlavor>::ClonedMessageType& aClonedData, + StructuredCloneData& aData) { + const nsTArray<MessagePortIdentifier>& identifiers = + aClonedData.identifiers(); + + MemoryTraits<MemoryFlavor>::ProvideBuffer(aClonedData, aData); + + if (aData.SupportsTransferring()) { + aData.PortIdentifiers().AppendElements(identifiers); + } + + const nsTArray<IPCBlob>& 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> blobImpl = IPCBlobUtils::Deserialize(blobs[i]); + MOZ_ASSERT(blobImpl); + + aData.BlobImpls().AppendElement(blobImpl); + } + } + + const nsTArray<IPCStream>& streams = aClonedData.inputStreams(); + if (!streams.IsEmpty()) { + uint32_t length = streams.Length(); + aData.InputStreams().SetCapacity(length); + for (uint32_t i = 0; i < length; ++i) { + nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(streams[i]); + aData.InputStreams().AppendElement(stream); + } + } +} + +void StructuredCloneData::BorrowFromClonedMessageData( + const ClonedMessageData& aClonedData) { + UnpackClonedMessageData<BorrowMemory>(aClonedData, *this); +} + +void StructuredCloneData::CopyFromClonedMessageData( + const ClonedMessageData& aClonedData) { + UnpackClonedMessageData<CopyMemory>(aClonedData, *this); +} + +void StructuredCloneData::StealFromClonedMessageData( + ClonedMessageData& aClonedData) { + UnpackClonedMessageData<StealMemory>(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<SharedJSAllocatedData> StructuredCloneData::TakeSharedData() { + return mSharedData.forget(); +} + +} // namespace mozilla::dom::ipc |