Line data Source code
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 "nsIDOMDOMException.h"
10 : #include "nsIMutable.h"
11 : #include "nsIXPConnect.h"
12 :
13 : #include "ipc/IPCMessageUtils.h"
14 : #include "mozilla/dom/BindingUtils.h"
15 : #include "mozilla/dom/BlobBinding.h"
16 : #include "mozilla/dom/IPCBlobUtils.h"
17 : #include "mozilla/dom/File.h"
18 : #include "mozilla/ipc/IPCStreamUtils.h"
19 : #include "nsContentUtils.h"
20 : #include "nsJSEnvironment.h"
21 : #include "MainThreadUtils.h"
22 : #include "StructuredCloneTags.h"
23 : #include "jsapi.h"
24 :
25 : namespace mozilla {
26 : namespace dom {
27 : namespace ipc {
28 :
29 101 : StructuredCloneData::StructuredCloneData()
30 101 : : StructuredCloneData(StructuredCloneHolder::TransferringSupported)
31 101 : {}
32 :
33 0 : StructuredCloneData::StructuredCloneData(StructuredCloneData&& aOther)
34 0 : : StructuredCloneData(StructuredCloneHolder::TransferringSupported)
35 : {
36 0 : *this = Move(aOther);
37 0 : }
38 :
39 101 : StructuredCloneData::StructuredCloneData(TransferringSupport aSupportsTransferring)
40 : : StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
41 : aSupportsTransferring,
42 : StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
43 101 : , mInitialized(false)
44 101 : {}
45 :
46 100 : StructuredCloneData::~StructuredCloneData()
47 100 : {}
48 :
49 : StructuredCloneData&
50 0 : StructuredCloneData::operator=(StructuredCloneData&& aOther)
51 : {
52 0 : mExternalData = Move(aOther.mExternalData);
53 0 : mSharedData = Move(aOther.mSharedData);
54 0 : mIPCStreams = Move(aOther.mIPCStreams);
55 0 : mInitialized = aOther.mInitialized;
56 :
57 0 : return *this;
58 : }
59 :
60 : bool
61 7 : StructuredCloneData::Copy(const StructuredCloneData& aData)
62 : {
63 7 : if (!aData.mInitialized) {
64 1 : return true;
65 : }
66 :
67 6 : if (aData.SharedData()) {
68 6 : mSharedData = aData.SharedData();
69 : } else {
70 : mSharedData =
71 0 : SharedJSAllocatedData::CreateFromExternalData(aData.Data());
72 0 : NS_ENSURE_TRUE(mSharedData, false);
73 : }
74 :
75 6 : if (mSupportsTransferring) {
76 6 : PortIdentifiers().AppendElements(aData.PortIdentifiers());
77 : }
78 :
79 6 : MOZ_ASSERT(BlobImpls().IsEmpty());
80 6 : BlobImpls().AppendElements(aData.BlobImpls());
81 :
82 6 : MOZ_ASSERT(GetSurfaces().IsEmpty());
83 6 : MOZ_ASSERT(WasmModules().IsEmpty());
84 :
85 6 : MOZ_ASSERT(InputStreams().IsEmpty());
86 6 : InputStreams().AppendElements(aData.InputStreams());
87 :
88 6 : mInitialized = true;
89 :
90 6 : return true;
91 : }
92 :
93 : void
94 43 : StructuredCloneData::Read(JSContext* aCx,
95 : JS::MutableHandle<JS::Value> aValue,
96 : ErrorResult &aRv)
97 : {
98 43 : MOZ_ASSERT(mInitialized);
99 :
100 43 : nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
101 43 : MOZ_ASSERT(global);
102 :
103 43 : ReadFromBuffer(global, aCx, Data(), aValue, aRv);
104 43 : }
105 :
106 : void
107 2 : StructuredCloneData::Write(JSContext* aCx,
108 : JS::Handle<JS::Value> aValue,
109 : ErrorResult &aRv)
110 : {
111 2 : Write(aCx, aValue, JS::UndefinedHandleValue, aRv);
112 2 : }
113 :
114 : void
115 44 : StructuredCloneData::Write(JSContext* aCx,
116 : JS::Handle<JS::Value> aValue,
117 : JS::Handle<JS::Value> aTransfer,
118 : ErrorResult &aRv)
119 : {
120 44 : MOZ_ASSERT(!mInitialized);
121 :
122 44 : StructuredCloneHolder::Write(aCx, aValue, aTransfer,
123 88 : JS::CloneDataPolicy().denySharedArrayBuffer(), aRv);
124 44 : if (NS_WARN_IF(aRv.Failed())) {
125 0 : return;
126 : }
127 :
128 88 : JSStructuredCloneData data;
129 44 : mBuffer->abandon();
130 44 : mBuffer->steal(&data);
131 44 : mBuffer = nullptr;
132 88 : mSharedData = new SharedJSAllocatedData(Move(data));
133 44 : mInitialized = true;
134 : }
135 :
136 : enum ActorFlavorEnum {
137 : Parent = 0,
138 : Child,
139 : };
140 :
141 : enum ManagerFlavorEnum {
142 : ContentProtocol = 0,
143 : BackgroundProtocol
144 : };
145 :
146 : template<typename M>
147 : bool
148 44 : BuildClonedMessageData(M* aManager, StructuredCloneData& aData,
149 : ClonedMessageData& aClonedData)
150 : {
151 44 : SerializedStructuredCloneBuffer& buffer = aClonedData.data();
152 44 : auto iter = aData.Data().Iter();
153 44 : size_t size = aData.Data().Size();
154 : bool success;
155 44 : buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success);
156 44 : if (NS_WARN_IF(!success)) {
157 0 : return false;
158 : }
159 44 : if (aData.SupportsTransferring()) {
160 44 : aClonedData.identfiers().AppendElements(aData.PortIdentifiers());
161 : }
162 :
163 44 : const nsTArray<RefPtr<BlobImpl>>& blobImpls = aData.BlobImpls();
164 :
165 44 : if (!blobImpls.IsEmpty()) {
166 0 : if (NS_WARN_IF(!aClonedData.blobs().SetLength(blobImpls.Length(), fallible))) {
167 0 : return false;
168 : }
169 :
170 0 : for (uint32_t i = 0; i < blobImpls.Length(); ++i) {
171 0 : nsresult rv = IPCBlobUtils::Serialize(blobImpls[i], aManager,
172 0 : aClonedData.blobs()[i]);
173 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
174 0 : return false;
175 : }
176 : }
177 : }
178 :
179 44 : const nsTArray<nsCOMPtr<nsIInputStream>>& inputStreams = aData.InputStreams();
180 44 : if (!inputStreams.IsEmpty()) {
181 0 : if (NS_WARN_IF(!aData.IPCStreams().SetCapacity(inputStreams.Length(),
182 : fallible))) {
183 0 : return false;
184 : }
185 :
186 0 : InfallibleTArray<IPCStream>& streams = aClonedData.inputStreams();
187 0 : uint32_t length = inputStreams.Length();
188 0 : streams.SetCapacity(length);
189 0 : for (uint32_t i = 0; i < length; ++i) {
190 0 : AutoIPCStream* stream = aData.IPCStreams().AppendElement(fallible);
191 0 : if (NS_WARN_IF(!stream)) {
192 0 : return false;
193 : }
194 :
195 0 : if (!stream->Serialize(inputStreams[i], aManager)) {
196 0 : return false;
197 : }
198 0 : streams.AppendElement(stream->TakeValue());
199 : }
200 : }
201 :
202 44 : return true;
203 : }
204 :
205 : bool
206 10 : StructuredCloneData::BuildClonedMessageDataForParent(
207 : nsIContentParent* aParent,
208 : ClonedMessageData& aClonedData)
209 : {
210 10 : return BuildClonedMessageData(aParent, *this, aClonedData);
211 : }
212 :
213 : bool
214 34 : StructuredCloneData::BuildClonedMessageDataForChild(
215 : nsIContentChild* aChild,
216 : ClonedMessageData& aClonedData)
217 : {
218 34 : return BuildClonedMessageData(aChild, *this, aClonedData);
219 : }
220 :
221 : bool
222 0 : StructuredCloneData::BuildClonedMessageDataForBackgroundParent(
223 : PBackgroundParent* aParent,
224 : ClonedMessageData& aClonedData)
225 : {
226 0 : return BuildClonedMessageData(aParent, *this, aClonedData);
227 : }
228 :
229 : bool
230 0 : StructuredCloneData::BuildClonedMessageDataForBackgroundChild(
231 : PBackgroundChild* aChild,
232 : ClonedMessageData& aClonedData)
233 : {
234 0 : return BuildClonedMessageData(aChild, *this, aClonedData);
235 : }
236 :
237 : // See the StructuredCloneData class block comment for the meanings of each val.
238 : enum MemoryFlavorEnum {
239 : BorrowMemory = 0,
240 : CopyMemory,
241 : StealMemory
242 : };
243 :
244 : template<MemoryFlavorEnum>
245 : struct MemoryTraits
246 : { };
247 :
248 : template<>
249 : struct MemoryTraits<BorrowMemory>
250 : {
251 : typedef const mozilla::dom::ClonedMessageData ClonedMessageType;
252 :
253 42 : static void ProvideBuffer(const ClonedMessageData& aClonedData,
254 : StructuredCloneData& aData)
255 : {
256 42 : const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
257 42 : aData.UseExternalData(buffer.data);
258 42 : }
259 : };
260 :
261 : template<>
262 : struct MemoryTraits<CopyMemory>
263 : {
264 : typedef const mozilla::dom::ClonedMessageData ClonedMessageType;
265 :
266 0 : static void ProvideBuffer(const ClonedMessageData& aClonedData,
267 : StructuredCloneData& aData)
268 : {
269 0 : const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
270 0 : aData.CopyExternalData(buffer.data);
271 0 : }
272 : };
273 :
274 : template<>
275 : struct MemoryTraits<StealMemory>
276 : {
277 : // note: not const!
278 : typedef mozilla::dom::ClonedMessageData ClonedMessageType;
279 :
280 0 : static void ProvideBuffer(ClonedMessageData& aClonedData,
281 : StructuredCloneData& aData)
282 : {
283 0 : SerializedStructuredCloneBuffer& buffer = aClonedData.data();
284 0 : aData.StealExternalData(buffer.data);
285 0 : }
286 : };
287 :
288 : // Note that there isn't actually a difference between Parent/BackgroundParent
289 : // and Child/BackgroundChild in this implementation. The calling methods,
290 : // however, do maintain the distinction for code-reading purposes and are backed
291 : // by assertions to enforce there is no misuse.
292 : template<MemoryFlavorEnum MemoryFlavor, ActorFlavorEnum Flavor>
293 : static void
294 42 : UnpackClonedMessageData(typename MemoryTraits<MemoryFlavor>::ClonedMessageType& aClonedData,
295 : StructuredCloneData& aData)
296 : {
297 42 : const InfallibleTArray<MessagePortIdentifier>& identifiers = aClonedData.identfiers();
298 :
299 42 : MemoryTraits<MemoryFlavor>::ProvideBuffer(aClonedData, aData);
300 :
301 42 : if (aData.SupportsTransferring()) {
302 42 : aData.PortIdentifiers().AppendElements(identifiers);
303 : }
304 :
305 42 : const nsTArray<IPCBlob>& blobs = aClonedData.blobs();
306 42 : if (!blobs.IsEmpty()) {
307 0 : uint32_t length = blobs.Length();
308 0 : aData.BlobImpls().SetCapacity(length);
309 0 : for (uint32_t i = 0; i < length; ++i) {
310 0 : RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobs[i]);
311 0 : MOZ_ASSERT(blobImpl);
312 :
313 0 : aData.BlobImpls().AppendElement(blobImpl);
314 : }
315 : }
316 :
317 42 : const InfallibleTArray<IPCStream>& streams = aClonedData.inputStreams();
318 42 : if (!streams.IsEmpty()) {
319 0 : uint32_t length = streams.Length();
320 0 : aData.InputStreams().SetCapacity(length);
321 0 : for (uint32_t i = 0; i < length; ++i) {
322 0 : nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(streams[i]);
323 0 : aData.InputStreams().AppendElement(stream);
324 : }
325 : }
326 42 : }
327 :
328 : void
329 32 : StructuredCloneData::BorrowFromClonedMessageDataForParent(const ClonedMessageData& aClonedData)
330 : {
331 : // PContent parent is always main thread and actor constraints demand we're
332 : // likewise on that thread.
333 32 : MOZ_ASSERT(NS_IsMainThread());
334 32 : UnpackClonedMessageData<BorrowMemory, Parent>(aClonedData, *this);
335 32 : }
336 :
337 : void
338 10 : StructuredCloneData::BorrowFromClonedMessageDataForChild(const ClonedMessageData& aClonedData)
339 : {
340 : // PContent child is always main thread and actor constraints demand we're
341 : // likewise on that thread.
342 10 : MOZ_ASSERT(NS_IsMainThread());
343 10 : UnpackClonedMessageData<BorrowMemory, Child>(aClonedData, *this);
344 10 : }
345 :
346 : void
347 0 : StructuredCloneData::BorrowFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData)
348 : {
349 0 : MOZ_ASSERT(IsOnBackgroundThread());
350 0 : UnpackClonedMessageData<BorrowMemory, Parent>(aClonedData, *this);
351 0 : }
352 :
353 : void
354 0 : StructuredCloneData::BorrowFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData)
355 : {
356 : // No thread assertion; BackgroundChild can happen on any thread.
357 0 : UnpackClonedMessageData<BorrowMemory, Child>(aClonedData, *this);
358 0 : }
359 :
360 : void
361 0 : StructuredCloneData::CopyFromClonedMessageDataForParent(const ClonedMessageData& aClonedData)
362 : {
363 0 : MOZ_ASSERT(NS_IsMainThread());
364 0 : UnpackClonedMessageData<CopyMemory, Parent>(aClonedData, *this);
365 0 : }
366 :
367 : void
368 0 : StructuredCloneData::CopyFromClonedMessageDataForChild(const ClonedMessageData& aClonedData)
369 : {
370 0 : MOZ_ASSERT(NS_IsMainThread());
371 0 : UnpackClonedMessageData<CopyMemory, Child>(aClonedData, *this);
372 0 : }
373 :
374 : void
375 0 : StructuredCloneData::CopyFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData)
376 : {
377 0 : MOZ_ASSERT(IsOnBackgroundThread());
378 0 : UnpackClonedMessageData<BorrowMemory, Parent>(aClonedData, *this);
379 0 : }
380 :
381 : void
382 0 : StructuredCloneData::CopyFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData)
383 : {
384 0 : UnpackClonedMessageData<CopyMemory, Child>(aClonedData, *this);
385 0 : }
386 :
387 : void
388 0 : StructuredCloneData::StealFromClonedMessageDataForParent(ClonedMessageData& aClonedData)
389 : {
390 0 : MOZ_ASSERT(NS_IsMainThread());
391 0 : UnpackClonedMessageData<StealMemory, Parent>(aClonedData, *this);
392 0 : }
393 :
394 : void
395 0 : StructuredCloneData::StealFromClonedMessageDataForChild(ClonedMessageData& aClonedData)
396 : {
397 0 : MOZ_ASSERT(NS_IsMainThread());
398 0 : UnpackClonedMessageData<StealMemory, Child>(aClonedData, *this);
399 0 : }
400 :
401 : void
402 0 : StructuredCloneData::StealFromClonedMessageDataForBackgroundParent(ClonedMessageData& aClonedData)
403 : {
404 0 : MOZ_ASSERT(IsOnBackgroundThread());
405 0 : UnpackClonedMessageData<StealMemory, Parent>(aClonedData, *this);
406 0 : }
407 :
408 : void
409 0 : StructuredCloneData::StealFromClonedMessageDataForBackgroundChild(ClonedMessageData& aClonedData)
410 : {
411 0 : UnpackClonedMessageData<StealMemory, Child>(aClonedData, *this);
412 0 : }
413 :
414 :
415 : void
416 2 : StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const
417 : {
418 2 : WriteParam(aMsg, Data());
419 2 : }
420 :
421 : bool
422 2 : StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
423 : PickleIterator* aIter)
424 : {
425 2 : MOZ_ASSERT(!mInitialized);
426 4 : JSStructuredCloneData data;
427 2 : if (!ReadParam(aMsg, aIter, &data)) {
428 0 : return false;
429 : }
430 4 : mSharedData = new SharedJSAllocatedData(Move(data));
431 2 : mInitialized = true;
432 2 : return true;
433 : }
434 :
435 : bool
436 0 : StructuredCloneData::CopyExternalData(const char* aData,
437 : size_t aDataLength)
438 : {
439 0 : MOZ_ASSERT(!mInitialized);
440 0 : mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData,
441 0 : aDataLength);
442 0 : NS_ENSURE_TRUE(mSharedData, false);
443 0 : mInitialized = true;
444 0 : return true;
445 : }
446 :
447 : bool
448 0 : StructuredCloneData::CopyExternalData(const JSStructuredCloneData& aData)
449 : {
450 0 : MOZ_ASSERT(!mInitialized);
451 0 : mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData);
452 0 : NS_ENSURE_TRUE(mSharedData, false);
453 0 : mInitialized = true;
454 0 : return true;
455 : }
456 :
457 : bool
458 0 : StructuredCloneData::StealExternalData(JSStructuredCloneData& aData)
459 : {
460 0 : MOZ_ASSERT(!mInitialized);
461 0 : mSharedData = new SharedJSAllocatedData(Move(aData));
462 0 : mInitialized = true;
463 0 : return true;
464 : }
465 :
466 : } // namespace ipc
467 : } // namespace dom
468 : } // namespace mozilla
|