StructuredCloneData.cpp (10067B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "StructuredCloneData.h" 8 9 #include "MainThreadUtils.h" 10 #include "StructuredCloneTags.h" 11 #include "jsapi.h" 12 #include "mozilla/dom/BindingUtils.h" 13 #include "mozilla/dom/BlobBinding.h" 14 #include "mozilla/dom/BlobImpl.h" 15 #include "mozilla/dom/DOMTypes.h" 16 #include "mozilla/dom/File.h" 17 #include "mozilla/dom/IPCBlobUtils.h" 18 #include "mozilla/ipc/BackgroundParent.h" 19 #include "mozilla/ipc/IPCStreamUtils.h" 20 #include "mozilla/ipc/SerializedStructuredCloneBuffer.h" 21 #include "nsContentUtils.h" 22 #include "nsJSEnvironment.h" 23 24 using namespace mozilla::ipc; 25 26 namespace mozilla::dom::ipc { 27 28 using mozilla::ipc::IPCStream; 29 using mozilla::ipc::PBackgroundChild; 30 using mozilla::ipc::PBackgroundParent; 31 32 StructuredCloneData::StructuredCloneData() 33 : StructuredCloneData( 34 StructuredCloneHolder::StructuredCloneScope::DifferentProcess, 35 StructuredCloneHolder::TransferringSupported) {} 36 37 StructuredCloneData::StructuredCloneData( 38 StructuredCloneHolder::StructuredCloneScope aScope, 39 TransferringSupport aSupportsTransferring) 40 : StructuredCloneHolder(StructuredCloneHolder::CloningSupported, 41 aSupportsTransferring, aScope), 42 mExternalData(JS::StructuredCloneScope::DifferentProcess), 43 mInitialized(false) { 44 MOZ_ASSERT( 45 aScope == StructuredCloneHolder::StructuredCloneScope::DifferentProcess || 46 aScope == 47 StructuredCloneHolder::StructuredCloneScope::UnknownDestination); 48 } 49 50 StructuredCloneData::~StructuredCloneData() = default; 51 52 bool StructuredCloneData::Copy(const StructuredCloneData& aData) { 53 if (!aData.mInitialized) { 54 return true; 55 } 56 57 if (aData.SharedData()) { 58 mSharedData = aData.SharedData(); 59 } else { 60 mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData.Data()); 61 NS_ENSURE_TRUE(mSharedData, false); 62 } 63 64 if (mSupportsTransferring) { 65 PortIdentifiers().AppendElements(aData.PortIdentifiers()); 66 } 67 68 MOZ_ASSERT(BlobImpls().IsEmpty()); 69 BlobImpls().AppendElements(aData.BlobImpls()); 70 71 MOZ_ASSERT(GetSurfaces().IsEmpty()); 72 MOZ_ASSERT(WasmModules().IsEmpty()); 73 74 MOZ_ASSERT(InputStreams().IsEmpty()); 75 InputStreams().AppendElements(aData.InputStreams()); 76 77 mInitialized = true; 78 79 return true; 80 } 81 82 void StructuredCloneData::Read(JSContext* aCx, 83 JS::MutableHandle<JS::Value> aValue, 84 ErrorResult& aRv) { 85 Read(aCx, aValue, JS::CloneDataPolicy(), aRv); 86 } 87 88 void StructuredCloneData::Read(JSContext* aCx, 89 JS::MutableHandle<JS::Value> aValue, 90 const JS::CloneDataPolicy& aCloneDataPolicy, 91 ErrorResult& aRv) { 92 MOZ_ASSERT(mInitialized); 93 94 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 95 MOZ_ASSERT(global); 96 97 ReadFromBuffer(global, aCx, Data(), aValue, aCloneDataPolicy, aRv); 98 } 99 100 void StructuredCloneData::Write(JSContext* aCx, JS::Handle<JS::Value> aValue, 101 ErrorResult& aRv) { 102 Write(aCx, aValue, JS::UndefinedHandleValue, JS::CloneDataPolicy(), aRv); 103 } 104 105 void StructuredCloneData::Write(JSContext* aCx, JS::Handle<JS::Value> aValue, 106 JS::Handle<JS::Value> aTransfer, 107 const JS::CloneDataPolicy& aCloneDataPolicy, 108 ErrorResult& aRv) { 109 MOZ_ASSERT(!mInitialized); 110 111 StructuredCloneHolder::Write(aCx, aValue, aTransfer, aCloneDataPolicy, aRv); 112 if (NS_WARN_IF(aRv.Failed())) { 113 return; 114 } 115 116 JSStructuredCloneData data(mBuffer->scope()); 117 mBuffer->giveTo(&data); 118 mBuffer = nullptr; 119 mSharedData = new SharedJSAllocatedData(std::move(data)); 120 mInitialized = true; 121 } 122 123 bool StructuredCloneData::BuildClonedMessageData( 124 ClonedMessageData& aClonedData) { 125 SerializedStructuredCloneBuffer& buffer = aClonedData.data(); 126 auto iter = Data().Start(); 127 size_t size = Data().Size(); 128 bool success; 129 buffer.data = Data().Borrow(iter, size, &success); 130 if (NS_WARN_IF(!success)) { 131 return false; 132 } 133 if (SupportsTransferring()) { 134 aClonedData.identifiers().AppendElements(PortIdentifiers()); 135 } 136 137 const nsTArray<RefPtr<BlobImpl>>& blobImpls = BlobImpls(); 138 139 if (!blobImpls.IsEmpty()) { 140 if (NS_WARN_IF( 141 !aClonedData.blobs().SetLength(blobImpls.Length(), fallible))) { 142 return false; 143 } 144 145 for (uint32_t i = 0; i < blobImpls.Length(); ++i) { 146 nsresult rv = 147 IPCBlobUtils::Serialize(blobImpls[i], aClonedData.blobs()[i]); 148 if (NS_WARN_IF(NS_FAILED(rv))) { 149 return false; 150 } 151 } 152 } 153 154 const nsTArray<nsCOMPtr<nsIInputStream>>& inputStreams = InputStreams(); 155 if (!inputStreams.IsEmpty()) { 156 nsTArray<IPCStream>& streams = aClonedData.inputStreams(); 157 uint32_t length = inputStreams.Length(); 158 streams.SetCapacity(length); 159 for (uint32_t i = 0; i < length; ++i) { 160 IPCStream value; 161 if (!mozilla::ipc::SerializeIPCStream(do_AddRef(inputStreams[i]), value, 162 /* aAllowLazy */ false)) { 163 return false; 164 } 165 streams.AppendElement(value); 166 } 167 } 168 169 return true; 170 } 171 172 // See the StructuredCloneData class block comment for the meanings of each val. 173 enum MemoryFlavorEnum { BorrowMemory = 0, CopyMemory, StealMemory }; 174 175 template <MemoryFlavorEnum> 176 struct MemoryTraits {}; 177 178 template <> 179 struct MemoryTraits<BorrowMemory> { 180 using ClonedMessageType = const mozilla::dom::ClonedMessageData; 181 182 static void ProvideBuffer(const ClonedMessageData& aClonedData, 183 StructuredCloneData& aData) { 184 const SerializedStructuredCloneBuffer& buffer = aClonedData.data(); 185 aData.UseExternalData(buffer.data); 186 } 187 }; 188 189 template <> 190 struct MemoryTraits<CopyMemory> { 191 using ClonedMessageType = const mozilla::dom::ClonedMessageData; 192 193 static void ProvideBuffer(const ClonedMessageData& aClonedData, 194 StructuredCloneData& aData) { 195 const SerializedStructuredCloneBuffer& buffer = aClonedData.data(); 196 aData.CopyExternalData(buffer.data); 197 } 198 }; 199 200 template <> 201 struct MemoryTraits<StealMemory> { 202 // note: not const! 203 using ClonedMessageType = mozilla::dom::ClonedMessageData; 204 205 static void ProvideBuffer(ClonedMessageData& aClonedData, 206 StructuredCloneData& aData) { 207 SerializedStructuredCloneBuffer& buffer = aClonedData.data(); 208 aData.StealExternalData(buffer.data); 209 } 210 }; 211 212 // Note that there isn't actually a difference between Parent/BackgroundParent 213 // and Child/BackgroundChild in this implementation. The calling methods, 214 // however, do maintain the distinction for code-reading purposes and are backed 215 // by assertions to enforce there is no misuse. 216 template <MemoryFlavorEnum MemoryFlavor> 217 static void UnpackClonedMessageData( 218 typename MemoryTraits<MemoryFlavor>::ClonedMessageType& aClonedData, 219 StructuredCloneData& aData) { 220 const nsTArray<MessagePortIdentifier>& identifiers = 221 aClonedData.identifiers(); 222 223 MemoryTraits<MemoryFlavor>::ProvideBuffer(aClonedData, aData); 224 225 if (aData.SupportsTransferring()) { 226 aData.PortIdentifiers().AppendElements(identifiers); 227 } 228 229 const nsTArray<IPCBlob>& blobs = aClonedData.blobs(); 230 if (!blobs.IsEmpty()) { 231 uint32_t length = blobs.Length(); 232 aData.BlobImpls().SetCapacity(length); 233 for (uint32_t i = 0; i < length; ++i) { 234 RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobs[i]); 235 MOZ_ASSERT(blobImpl); 236 237 aData.BlobImpls().AppendElement(blobImpl); 238 } 239 } 240 241 const nsTArray<IPCStream>& streams = aClonedData.inputStreams(); 242 if (!streams.IsEmpty()) { 243 uint32_t length = streams.Length(); 244 aData.InputStreams().SetCapacity(length); 245 for (uint32_t i = 0; i < length; ++i) { 246 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(streams[i]); 247 aData.InputStreams().AppendElement(stream); 248 } 249 } 250 } 251 252 void StructuredCloneData::BorrowFromClonedMessageData( 253 const ClonedMessageData& aClonedData) { 254 UnpackClonedMessageData<BorrowMemory>(aClonedData, *this); 255 } 256 257 void StructuredCloneData::CopyFromClonedMessageData( 258 const ClonedMessageData& aClonedData) { 259 UnpackClonedMessageData<CopyMemory>(aClonedData, *this); 260 } 261 262 void StructuredCloneData::StealFromClonedMessageData( 263 ClonedMessageData& aClonedData) { 264 UnpackClonedMessageData<StealMemory>(aClonedData, *this); 265 } 266 267 void StructuredCloneData::WriteIPCParams(IPC::MessageWriter* aWriter) const { 268 WriteParam(aWriter, Data()); 269 } 270 271 bool StructuredCloneData::ReadIPCParams(IPC::MessageReader* aReader) { 272 MOZ_ASSERT(!mInitialized); 273 JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess); 274 if (!ReadParam(aReader, &data)) { 275 return false; 276 } 277 mSharedData = new SharedJSAllocatedData(std::move(data)); 278 mInitialized = true; 279 return true; 280 } 281 282 bool StructuredCloneData::CopyExternalData(const char* aData, 283 size_t aDataLength) { 284 MOZ_ASSERT(!mInitialized); 285 mSharedData = 286 SharedJSAllocatedData::CreateFromExternalData(aData, aDataLength); 287 NS_ENSURE_TRUE(mSharedData, false); 288 mInitialized = true; 289 return true; 290 } 291 292 bool StructuredCloneData::CopyExternalData(const JSStructuredCloneData& aData) { 293 MOZ_ASSERT(!mInitialized); 294 mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData); 295 NS_ENSURE_TRUE(mSharedData, false); 296 mInitialized = true; 297 return true; 298 } 299 300 bool StructuredCloneData::StealExternalData(JSStructuredCloneData& aData) { 301 MOZ_ASSERT(!mInitialized); 302 mSharedData = new SharedJSAllocatedData(std::move(aData)); 303 mInitialized = true; 304 return true; 305 } 306 307 already_AddRefed<SharedJSAllocatedData> StructuredCloneData::TakeSharedData() { 308 return mSharedData.forget(); 309 } 310 311 } // namespace mozilla::dom::ipc