summaryrefslogtreecommitdiffstats
path: root/dom/ipc/StructuredCloneData.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/ipc/StructuredCloneData.cpp')
-rw-r--r--dom/ipc/StructuredCloneData.cpp328
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