Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/StructuredCloneHolder.cpp
Line
Count
Source (jump to first uncovered line)
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 "StructuredCloneHolder.h"
8
9
#include "ImageContainer.h"
10
#include "mozilla/AutoRestore.h"
11
#include "mozilla/dom/BlobBinding.h"
12
#include "mozilla/dom/CryptoKey.h"
13
#include "mozilla/dom/StructuredCloneBlob.h"
14
#include "mozilla/dom/Directory.h"
15
#include "mozilla/dom/DirectoryBinding.h"
16
#include "mozilla/dom/File.h"
17
#include "mozilla/dom/FileList.h"
18
#include "mozilla/dom/FileListBinding.h"
19
#include "mozilla/dom/FormData.h"
20
#include "mozilla/dom/ImageBitmap.h"
21
#include "mozilla/dom/ImageBitmapBinding.h"
22
#include "mozilla/dom/ImageData.h"
23
#include "mozilla/dom/ImageDataBinding.h"
24
#include "mozilla/dom/StructuredClone.h"
25
#include "mozilla/dom/MessagePort.h"
26
#include "mozilla/dom/MessagePortBinding.h"
27
#include "mozilla/dom/OffscreenCanvas.h"
28
#include "mozilla/dom/OffscreenCanvasBinding.h"
29
#include "mozilla/dom/PMessagePort.h"
30
#include "mozilla/dom/StructuredCloneTags.h"
31
#include "mozilla/dom/SubtleCryptoBinding.h"
32
#include "mozilla/dom/ToJSValue.h"
33
#include "mozilla/dom/URLSearchParams.h"
34
#include "mozilla/dom/URLSearchParamsBinding.h"
35
#include "mozilla/dom/WebCryptoCommon.h"
36
#include "mozilla/gfx/2D.h"
37
#include "mozilla/ipc/BackgroundChild.h"
38
#include "mozilla/ipc/BackgroundUtils.h"
39
#include "mozilla/ipc/PBackgroundSharedTypes.h"
40
#include "MultipartBlobImpl.h"
41
#include "nsQueryObject.h"
42
43
#ifdef MOZ_WEBRTC
44
#include "mozilla/dom/RTCCertificate.h"
45
#include "mozilla/dom/RTCCertificateBinding.h"
46
#endif
47
48
using namespace mozilla::ipc;
49
50
namespace mozilla {
51
namespace dom {
52
53
namespace {
54
55
JSObject*
56
StructuredCloneCallbacksRead(JSContext* aCx,
57
                             JSStructuredCloneReader* aReader,
58
                             uint32_t aTag, uint32_t aIndex,
59
                             void* aClosure)
60
0
{
61
0
  StructuredCloneHolderBase* holder =
62
0
    static_cast<StructuredCloneHolderBase*>(aClosure);
63
0
  MOZ_ASSERT(holder);
64
0
  return holder->CustomReadHandler(aCx, aReader, aTag, aIndex);
65
0
}
66
67
bool
68
StructuredCloneCallbacksWrite(JSContext* aCx,
69
                              JSStructuredCloneWriter* aWriter,
70
                              JS::Handle<JSObject*> aObj,
71
                              void* aClosure)
72
0
{
73
0
  StructuredCloneHolderBase* holder =
74
0
    static_cast<StructuredCloneHolderBase*>(aClosure);
75
0
  MOZ_ASSERT(holder);
76
0
  return holder->CustomWriteHandler(aCx, aWriter, aObj);
77
0
}
78
79
bool
80
StructuredCloneCallbacksReadTransfer(JSContext* aCx,
81
                                     JSStructuredCloneReader* aReader,
82
                                     uint32_t aTag,
83
                                     void* aContent,
84
                                     uint64_t aExtraData,
85
                                     void* aClosure,
86
                                     JS::MutableHandleObject aReturnObject)
87
0
{
88
0
  StructuredCloneHolderBase* holder =
89
0
    static_cast<StructuredCloneHolderBase*>(aClosure);
90
0
  MOZ_ASSERT(holder);
91
0
  return holder->CustomReadTransferHandler(aCx, aReader, aTag, aContent,
92
0
                                           aExtraData, aReturnObject);
93
0
}
94
95
bool
96
StructuredCloneCallbacksWriteTransfer(JSContext* aCx,
97
                                      JS::Handle<JSObject*> aObj,
98
                                      void* aClosure,
99
                                      // Output:
100
                                      uint32_t* aTag,
101
                                      JS::TransferableOwnership* aOwnership,
102
                                      void** aContent,
103
                                      uint64_t* aExtraData)
104
0
{
105
0
  StructuredCloneHolderBase* holder =
106
0
    static_cast<StructuredCloneHolderBase*>(aClosure);
107
0
  MOZ_ASSERT(holder);
108
0
  return holder->CustomWriteTransferHandler(aCx, aObj, aTag, aOwnership,
109
0
                                            aContent, aExtraData);
110
0
}
111
112
void
113
StructuredCloneCallbacksFreeTransfer(uint32_t aTag,
114
                                     JS::TransferableOwnership aOwnership,
115
                                     void* aContent,
116
                                     uint64_t aExtraData,
117
                                     void* aClosure)
118
0
{
119
0
  StructuredCloneHolderBase* holder =
120
0
    static_cast<StructuredCloneHolderBase*>(aClosure);
121
0
  MOZ_ASSERT(holder);
122
0
  return holder->CustomFreeTransferHandler(aTag, aOwnership, aContent,
123
0
                                           aExtraData);
124
0
}
125
126
bool
127
StructuredCloneCallbacksCanTransfer(JSContext* aCx,
128
                                    JS::Handle<JSObject*> aObject,
129
                                    void* aClosure)
130
0
{
131
0
  StructuredCloneHolderBase* holder =
132
0
    static_cast<StructuredCloneHolderBase*>(aClosure);
133
0
  MOZ_ASSERT(holder);
134
0
  return holder->CustomCanTransferHandler(aCx, aObject);
135
0
}
136
137
void
138
StructuredCloneCallbacksError(JSContext* aCx,
139
                              uint32_t aErrorId)
140
0
{
141
0
  NS_WARNING("Failed to clone data.");
142
0
}
143
144
} // anonymous namespace
145
146
const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
147
  StructuredCloneCallbacksRead,
148
  StructuredCloneCallbacksWrite,
149
  StructuredCloneCallbacksError,
150
  StructuredCloneCallbacksReadTransfer,
151
  StructuredCloneCallbacksWriteTransfer,
152
  StructuredCloneCallbacksFreeTransfer,
153
  StructuredCloneCallbacksCanTransfer,
154
};
155
156
// StructuredCloneHolderBase class
157
158
StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope)
159
  : mStructuredCloneScope(aScope)
160
#ifdef DEBUG
161
  , mClearCalled(false)
162
#endif
163
0
{}
164
165
StructuredCloneHolderBase::~StructuredCloneHolderBase()
166
0
{
167
#ifdef DEBUG
168
  MOZ_ASSERT(mClearCalled);
169
#endif
170
}
171
172
void
173
StructuredCloneHolderBase::Clear()
174
0
{
175
#ifdef DEBUG
176
  mClearCalled = true;
177
#endif
178
179
0
  mBuffer = nullptr;
180
0
}
181
182
bool
183
StructuredCloneHolderBase::Write(JSContext* aCx,
184
                                 JS::Handle<JS::Value> aValue)
185
0
{
186
0
  return Write(aCx, aValue, JS::UndefinedHandleValue,
187
0
               JS::CloneDataPolicy().denySharedArrayBuffer());
188
0
}
189
190
bool
191
StructuredCloneHolderBase::Write(JSContext* aCx,
192
                                 JS::Handle<JS::Value> aValue,
193
                                 JS::Handle<JS::Value> aTransfer,
194
                                 JS::CloneDataPolicy cloneDataPolicy)
195
0
{
196
0
  MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
197
0
  MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
198
0
199
0
  mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this);
200
0
201
0
  if (!mBuffer->write(aCx, aValue, aTransfer, cloneDataPolicy,
202
0
                      &StructuredCloneHolder::sCallbacks, this))
203
0
  {
204
0
    mBuffer = nullptr;
205
0
    return false;
206
0
  }
207
0
208
0
  return true;
209
0
}
210
211
bool
212
StructuredCloneHolderBase::Read(JSContext* aCx,
213
                                JS::MutableHandle<JS::Value> aValue)
214
0
{
215
0
  MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
216
0
  MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
217
0
218
0
  bool ok = mBuffer->read(aCx, aValue, &StructuredCloneHolder::sCallbacks, this);
219
0
  return ok;
220
0
}
221
222
bool
223
StructuredCloneHolderBase::CustomReadTransferHandler(JSContext* aCx,
224
                                                     JSStructuredCloneReader* aReader,
225
                                                     uint32_t aTag,
226
                                                     void* aContent,
227
                                                     uint64_t aExtraData,
228
                                                     JS::MutableHandleObject aReturnObject)
229
0
{
230
0
  MOZ_CRASH("Nothing to read.");
231
0
  return false;
232
0
}
233
234
bool
235
StructuredCloneHolderBase::CustomWriteTransferHandler(JSContext* aCx,
236
                                                      JS::Handle<JSObject*> aObj,
237
                                                      uint32_t* aTag,
238
                                                      JS::TransferableOwnership* aOwnership,
239
                                                      void** aContent,
240
                                                      uint64_t* aExtraData)
241
0
{
242
0
  // No transfers are supported by default.
243
0
  return false;
244
0
}
245
246
void
247
StructuredCloneHolderBase::CustomFreeTransferHandler(uint32_t aTag,
248
                                                     JS::TransferableOwnership aOwnership,
249
                                                     void* aContent,
250
                                                     uint64_t aExtraData)
251
0
{
252
0
  MOZ_CRASH("Nothing to free.");
253
0
}
254
255
bool
256
StructuredCloneHolderBase::CustomCanTransferHandler(JSContext* aCx,
257
                                                    JS::Handle<JSObject*> aObj)
258
0
{
259
0
  return false;
260
0
}
261
262
// StructuredCloneHolder class
263
264
StructuredCloneHolder::StructuredCloneHolder(CloningSupport aSupportsCloning,
265
                                             TransferringSupport aSupportsTransferring,
266
                                             StructuredCloneScope aScope)
267
  : StructuredCloneHolderBase(aScope)
268
  , mSupportsCloning(aSupportsCloning == CloningSupported)
269
  , mSupportsTransferring(aSupportsTransferring == TransferringSupported)
270
  , mParent(nullptr)
271
#ifdef DEBUG
272
  , mCreationEventTarget(GetCurrentThreadEventTarget())
273
#endif
274
0
{}
275
276
StructuredCloneHolder::~StructuredCloneHolder()
277
0
{
278
0
  Clear();
279
0
  MOZ_ASSERT(mTransferredPorts.IsEmpty());
280
0
}
281
282
void
283
StructuredCloneHolder::Write(JSContext* aCx,
284
                             JS::Handle<JS::Value> aValue,
285
                             ErrorResult& aRv)
286
0
{
287
0
  Write(aCx, aValue, JS::UndefinedHandleValue,
288
0
        JS::CloneDataPolicy().denySharedArrayBuffer(), aRv);
289
0
}
290
291
void
292
StructuredCloneHolder::Write(JSContext* aCx,
293
                             JS::Handle<JS::Value> aValue,
294
                             JS::Handle<JS::Value> aTransfer,
295
                             JS::CloneDataPolicy cloneDataPolicy,
296
                             ErrorResult& aRv)
297
0
{
298
0
  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
299
0
                mCreationEventTarget->IsOnCurrentThread());
300
0
301
0
  if (!StructuredCloneHolderBase::Write(aCx, aValue, aTransfer, cloneDataPolicy)) {
302
0
    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
303
0
    return;
304
0
  }
305
0
}
306
307
void
308
StructuredCloneHolder::Read(nsISupports* aParent,
309
                            JSContext* aCx,
310
                            JS::MutableHandle<JS::Value> aValue,
311
                            ErrorResult& aRv)
312
0
{
313
0
  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
314
0
                mCreationEventTarget->IsOnCurrentThread());
315
0
  MOZ_ASSERT(aParent);
316
0
317
0
  mozilla::AutoRestore<nsISupports*> guard(mParent);
318
0
  mParent = aParent;
319
0
320
0
  if (!StructuredCloneHolderBase::Read(aCx, aValue)) {
321
0
    JS_ClearPendingException(aCx);
322
0
    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
323
0
    return;
324
0
  }
325
0
326
0
  // If we are tranferring something, we cannot call 'Read()' more than once.
327
0
  if (mSupportsTransferring) {
328
0
    mBlobImplArray.Clear();
329
0
    mWasmModuleArray.Clear();
330
0
    mClonedSurfaces.Clear();
331
0
    mInputStreamArray.Clear();
332
0
    Clear();
333
0
  }
334
0
}
335
336
void
337
StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
338
                                      JSContext* aCx,
339
                                      JSStructuredCloneData& aBuffer,
340
                                      JS::MutableHandle<JS::Value> aValue,
341
                                      ErrorResult& aRv)
342
0
{
343
0
  ReadFromBuffer(aParent, aCx, aBuffer,
344
0
                 JS_STRUCTURED_CLONE_VERSION, aValue, aRv);
345
0
}
346
347
void
348
StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
349
                                      JSContext* aCx,
350
                                      JSStructuredCloneData& aBuffer,
351
                                      uint32_t aAlgorithmVersion,
352
                                      JS::MutableHandle<JS::Value> aValue,
353
                                      ErrorResult& aRv)
354
0
{
355
0
  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
356
0
                mCreationEventTarget->IsOnCurrentThread());
357
0
358
0
  MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
359
0
360
0
  mozilla::AutoRestore<nsISupports*> guard(mParent);
361
0
  mParent = aParent;
362
0
363
0
  if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion,
364
0
                              mStructuredCloneScope, aValue, &sCallbacks,
365
0
                              this)) {
366
0
    JS_ClearPendingException(aCx);
367
0
    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
368
0
  }
369
0
}
370
371
/* static */ JSObject*
372
StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx,
373
                                                    JSStructuredCloneReader* aReader,
374
                                                    uint32_t aTag)
375
0
{
376
0
  if (aTag == SCTAG_DOM_IMAGEDATA) {
377
0
    return ReadStructuredCloneImageData(aCx, aReader);
378
0
  }
379
0
380
0
  if (aTag == SCTAG_DOM_WEBCRYPTO_KEY || aTag == SCTAG_DOM_URLSEARCHPARAMS) {
381
0
    nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
382
0
    if (!global) {
383
0
      return nullptr;
384
0
    }
385
0
386
0
    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
387
0
    JS::Rooted<JSObject*> result(aCx);
388
0
    {
389
0
      if (aTag == SCTAG_DOM_WEBCRYPTO_KEY) {
390
0
        RefPtr<CryptoKey> key = new CryptoKey(global);
391
0
        if (!key->ReadStructuredClone(aReader)) {
392
0
         result = nullptr;
393
0
        } else {
394
0
          result = key->WrapObject(aCx, nullptr);
395
0
        }
396
0
      } else if (aTag == SCTAG_DOM_URLSEARCHPARAMS) {
397
0
        RefPtr<URLSearchParams> usp = new URLSearchParams(global);
398
0
       if (!usp->ReadStructuredClone(aReader)) {
399
0
          result = nullptr;
400
0
        } else {
401
0
          result = usp->WrapObject(aCx, nullptr);
402
0
        }
403
0
      }
404
0
    }
405
0
    return result;
406
0
  }
407
0
408
0
  if (aTag == SCTAG_DOM_NULL_PRINCIPAL ||
409
0
      aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
410
0
      aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
411
0
      aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
412
0
    JSPrincipals* prin;
413
0
    if (!nsJSPrincipals::ReadKnownPrincipalType(aCx, aReader, aTag, &prin)) {
414
0
      return nullptr;
415
0
    }
416
0
    // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of the
417
0
    // casting between JSPrincipals* and nsIPrincipal* we can't use
418
0
    // getter_AddRefs above and have to already_AddRefed here.
419
0
    nsCOMPtr<nsIPrincipal> principal = already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
420
0
421
0
    JS::RootedValue result(aCx);
422
0
    nsresult rv = nsContentUtils::WrapNative(aCx, principal,
423
0
                                             &NS_GET_IID(nsIPrincipal),
424
0
                                             &result);
425
0
    if (NS_FAILED(rv)) {
426
0
      xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
427
0
      return nullptr;
428
0
    }
429
0
430
0
    return result.toObjectOrNull();
431
0
  }
432
0
433
0
#ifdef MOZ_WEBRTC
434
0
  if (aTag == SCTAG_DOM_RTC_CERTIFICATE) {
435
0
    if (!NS_IsMainThread()) {
436
0
      return nullptr;
437
0
    }
438
0
439
0
    nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
440
0
    if (!global) {
441
0
      return nullptr;
442
0
    }
443
0
444
0
    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
445
0
    JS::Rooted<JSObject*> result(aCx);
446
0
    {
447
0
      RefPtr<RTCCertificate> cert = new RTCCertificate(global);
448
0
      if (!cert->ReadStructuredClone(aReader)) {
449
0
        result = nullptr;
450
0
      } else {
451
0
        result = cert->WrapObject(aCx, nullptr);
452
0
      }
453
0
    }
454
0
    return result;
455
0
  }
456
0
#endif
457
0
458
0
  // Don't know what this is. Bail.
459
0
  xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
460
0
  return nullptr;
461
0
}
462
463
/* static */ bool
464
StructuredCloneHolder::WriteFullySerializableObjects(JSContext* aCx,
465
                                                     JSStructuredCloneWriter* aWriter,
466
                                                     JS::Handle<JSObject*> aObj)
467
0
{
468
0
  JS::Rooted<JSObject*> obj(aCx, aObj);
469
0
470
0
  // See if this is a ImageData object.
471
0
  {
472
0
    ImageData* imageData = nullptr;
473
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, &obj, imageData))) {
474
0
      return WriteStructuredCloneImageData(aCx, aWriter, imageData);
475
0
    }
476
0
  }
477
0
478
0
  // Handle URLSearchParams cloning
479
0
  {
480
0
    URLSearchParams* usp = nullptr;
481
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(URLSearchParams, &obj, usp))) {
482
0
      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_URLSEARCHPARAMS, 0) &&
483
0
             usp->WriteStructuredClone(aWriter);
484
0
    }
485
0
  }
486
0
487
0
  // Handle Key cloning
488
0
  {
489
0
    CryptoKey* key = nullptr;
490
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, &obj, key))) {
491
0
      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
492
0
             key->WriteStructuredClone(aWriter);
493
0
    }
494
0
  }
495
0
496
0
#ifdef MOZ_WEBRTC
497
0
  {
498
0
    // Handle WebRTC Certificate cloning
499
0
    RTCCertificate* cert = nullptr;
500
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, &obj, cert))) {
501
0
      MOZ_ASSERT(NS_IsMainThread());
502
0
      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
503
0
             cert->WriteStructuredClone(aWriter);
504
0
    }
505
0
  }
506
0
#endif
507
0
508
0
  if (NS_IsMainThread() && xpc::IsReflector(obj)) {
509
0
    nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
510
0
    nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
511
0
    if (principal) {
512
0
      auto nsjsprincipals = nsJSPrincipals::get(principal);
513
0
      return nsjsprincipals->write(aCx, aWriter);
514
0
    }
515
0
  }
516
0
517
0
  // Don't know what this is
518
0
  xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
519
0
  return false;
520
0
}
521
522
namespace {
523
524
JSObject*
525
ReadBlob(JSContext* aCx,
526
         uint32_t aIndex,
527
         StructuredCloneHolder* aHolder)
528
0
{
529
0
  MOZ_ASSERT(aHolder);
530
0
#ifdef FUZZING
531
0
  if (aIndex >= aHolder->BlobImpls().Length()) {
532
0
    return nullptr;
533
0
  }
534
0
#endif
535
0
  MOZ_ASSERT(aIndex < aHolder->BlobImpls().Length());
536
0
  RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[aIndex];
537
0
538
0
  MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
539
0
540
0
  // RefPtr<File> needs to go out of scope before toObject() is
541
0
  // called because the static analysis thinks dereferencing XPCOM objects
542
0
  // can GC (because in some cases it can!), and a return statement with a
543
0
  // JSObject* type means that JSObject* is on the stack as a raw pointer
544
0
  // while destructors are running.
545
0
  JS::Rooted<JS::Value> val(aCx);
546
0
  {
547
0
    RefPtr<Blob> blob = Blob::Create(aHolder->ParentDuringRead(), blobImpl);
548
0
    if (!ToJSValue(aCx, blob, &val)) {
549
0
      return nullptr;
550
0
    }
551
0
  }
552
0
553
0
  return &val.toObject();
554
0
}
555
556
bool
557
WriteBlob(JSStructuredCloneWriter* aWriter,
558
          Blob* aBlob,
559
          StructuredCloneHolder* aHolder)
560
0
{
561
0
  MOZ_ASSERT(aWriter);
562
0
  MOZ_ASSERT(aBlob);
563
0
  MOZ_ASSERT(aHolder);
564
0
565
0
  if (JS_GetStructuredCloneScope(aWriter) != JS::StructuredCloneScope::SameProcessSameThread &&
566
0
      !aBlob->Impl()->MayBeClonedToOtherThreads()) {
567
0
    return false;
568
0
  }
569
0
570
0
  RefPtr<BlobImpl> blobImpl = aBlob->Impl();
571
0
  MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
572
0
573
0
  // We store the position of the blobImpl in the array as index.
574
0
  if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
575
0
                         aHolder->BlobImpls().Length())) {
576
0
    aHolder->BlobImpls().AppendElement(blobImpl);
577
0
    return true;
578
0
  }
579
0
580
0
  return false;
581
0
}
582
583
// A directory is serialized as:
584
// - pair of ints: SCTAG_DOM_DIRECTORY, path length
585
// - path as string
586
bool
587
WriteDirectory(JSStructuredCloneWriter* aWriter,
588
               Directory* aDirectory)
589
0
{
590
0
  MOZ_ASSERT(aWriter);
591
0
  MOZ_ASSERT(aDirectory);
592
0
593
0
  nsAutoString path;
594
0
  aDirectory->GetFullRealPath(path);
595
0
596
0
  size_t charSize = sizeof(nsString::char_type);
597
0
  return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DIRECTORY, path.Length()) &&
598
0
         JS_WriteBytes(aWriter, path.get(), path.Length() * charSize);
599
0
}
600
601
already_AddRefed<Directory>
602
ReadDirectoryInternal(JSStructuredCloneReader* aReader,
603
                      uint32_t aPathLength,
604
                      StructuredCloneHolder* aHolder)
605
0
{
606
0
  MOZ_ASSERT(aReader);
607
0
  MOZ_ASSERT(aHolder);
608
0
609
0
  nsAutoString path;
610
0
  if (NS_WARN_IF(!path.SetLength(aPathLength, fallible))) {
611
0
    return nullptr;
612
0
  }
613
0
  size_t charSize = sizeof(nsString::char_type);
614
0
  if (!JS_ReadBytes(aReader, (void*) path.BeginWriting(),
615
0
                    aPathLength * charSize)) {
616
0
    return nullptr;
617
0
  }
618
0
619
0
  nsCOMPtr<nsIFile> file;
620
0
  nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(file));
621
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
622
0
    return nullptr;
623
0
  }
624
0
625
0
  RefPtr<Directory> directory =
626
0
    Directory::Create(aHolder->ParentDuringRead(), file);
627
0
  return directory.forget();
628
0
}
629
630
JSObject*
631
ReadDirectory(JSContext* aCx,
632
              JSStructuredCloneReader* aReader,
633
              uint32_t aPathLength,
634
              StructuredCloneHolder* aHolder)
635
0
{
636
0
  MOZ_ASSERT(aCx);
637
0
  MOZ_ASSERT(aReader);
638
0
  MOZ_ASSERT(aHolder);
639
0
640
0
  // RefPtr<Directory> needs to go out of scope before toObject() is
641
0
  // called because the static analysis thinks dereferencing XPCOM objects
642
0
  // can GC (because in some cases it can!), and a return statement with a
643
0
  // JSObject* type means that JSObject* is on the stack as a raw pointer
644
0
  // while destructors are running.
645
0
  JS::Rooted<JS::Value> val(aCx);
646
0
  {
647
0
    RefPtr<Directory> directory =
648
0
      ReadDirectoryInternal(aReader, aPathLength, aHolder);
649
0
    if (!directory) {
650
0
      return nullptr;
651
0
    }
652
0
653
0
    if (!ToJSValue(aCx, directory, &val)) {
654
0
      return nullptr;
655
0
    }
656
0
  }
657
0
658
0
  return &val.toObject();
659
0
}
660
661
// Read the WriteFileList for the format.
662
JSObject*
663
ReadFileList(JSContext* aCx,
664
             JSStructuredCloneReader* aReader,
665
             uint32_t aCount,
666
             StructuredCloneHolder* aHolder)
667
0
{
668
0
  MOZ_ASSERT(aCx);
669
0
  MOZ_ASSERT(aReader);
670
0
671
0
  JS::Rooted<JS::Value> val(aCx);
672
0
  {
673
0
    RefPtr<FileList> fileList = new FileList(aHolder->ParentDuringRead());
674
0
675
0
    uint32_t zero, index;
676
0
    // |index| is the index of the first blobImpl.
677
0
    if (!JS_ReadUint32Pair(aReader, &zero, &index)) {
678
0
      return nullptr;
679
0
    }
680
0
681
0
    MOZ_ASSERT(zero == 0);
682
0
683
0
    // |aCount| is the number of BlobImpls to use from the |index|.
684
0
    for (uint32_t i = 0; i < aCount; ++i) {
685
0
      uint32_t pos = index + i;
686
0
#ifdef FUZZING
687
0
      if (pos >= aHolder->BlobImpls().Length()) {
688
0
        return nullptr;
689
0
      }
690
0
#endif
691
0
      MOZ_ASSERT(pos < aHolder->BlobImpls().Length());
692
0
693
0
      RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[pos];
694
0
      MOZ_ASSERT(blobImpl->IsFile());
695
0
696
0
      MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
697
0
698
0
      RefPtr<File> file = File::Create(aHolder->ParentDuringRead(), blobImpl);
699
0
      if (!fileList->Append(file)) {
700
0
        return nullptr;
701
0
      }
702
0
    }
703
0
704
0
    if (!ToJSValue(aCx, fileList, &val)) {
705
0
      return nullptr;
706
0
    }
707
0
  }
708
0
709
0
  return &val.toObject();
710
0
}
711
712
// The format of the FileList serialization is:
713
// - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList
714
// - pair of ints: 0, The offset of the BlobImpl array
715
bool
716
WriteFileList(JSStructuredCloneWriter* aWriter,
717
              FileList* aFileList,
718
              StructuredCloneHolder* aHolder)
719
0
{
720
0
  MOZ_ASSERT(aWriter);
721
0
  MOZ_ASSERT(aFileList);
722
0
  MOZ_ASSERT(aHolder);
723
0
724
0
  // A FileList is serialized writing the X number of elements and the offset
725
0
  // from mBlobImplArray. The Read will take X elements from mBlobImplArray
726
0
  // starting from the offset.
727
0
  if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
728
0
                          aFileList->Length()) ||
729
0
      !JS_WriteUint32Pair(aWriter, 0,
730
0
                          aHolder->BlobImpls().Length())) {
731
0
    return false;
732
0
  }
733
0
734
0
  nsTArray<RefPtr<BlobImpl>> blobImpls;
735
0
736
0
  for (uint32_t i = 0; i < aFileList->Length(); ++i) {
737
0
    RefPtr<BlobImpl> blobImpl = aFileList->Item(i)->Impl();
738
0
    MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
739
0
    blobImpls.AppendElement(blobImpl);
740
0
  }
741
0
742
0
  aHolder->BlobImpls().AppendElements(blobImpls);
743
0
  return true;
744
0
}
745
746
// Read the WriteFormData for the format.
747
JSObject*
748
ReadFormData(JSContext* aCx,
749
             JSStructuredCloneReader* aReader,
750
             uint32_t aCount,
751
             StructuredCloneHolder* aHolder)
752
0
{
753
0
  MOZ_ASSERT(aCx);
754
0
  MOZ_ASSERT(aReader);
755
0
  MOZ_ASSERT(aHolder);
756
0
757
0
  // See the serialization of the FormData for the format.
758
0
  JS::Rooted<JS::Value> val(aCx);
759
0
  {
760
0
    RefPtr<FormData> formData =
761
0
      new FormData(aHolder->ParentDuringRead());
762
0
763
0
    Optional<nsAString> thirdArg;
764
0
    for (uint32_t i = 0; i < aCount; ++i) {
765
0
      nsAutoString name;
766
0
      if (!ReadString(aReader, name)) {
767
0
        return nullptr;
768
0
      }
769
0
770
0
      uint32_t tag, indexOrLengthOfString;
771
0
      if (!JS_ReadUint32Pair(aReader, &tag, &indexOrLengthOfString)) {
772
0
        return nullptr;
773
0
      }
774
0
775
0
      if (tag == SCTAG_DOM_BLOB) {
776
0
#ifdef FUZZING
777
0
        if (indexOrLengthOfString >= aHolder->BlobImpls().Length()) {
778
0
          return nullptr;
779
0
        }
780
0
#endif
781
0
        MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length());
782
0
783
0
        RefPtr<BlobImpl> blobImpl =
784
0
          aHolder->BlobImpls()[indexOrLengthOfString];
785
0
        MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
786
0
787
0
        RefPtr<Blob> blob =
788
0
          Blob::Create(aHolder->ParentDuringRead(), blobImpl);
789
0
        MOZ_ASSERT(blob);
790
0
791
0
        ErrorResult rv;
792
0
        formData->Append(name, *blob, thirdArg, rv);
793
0
        if (NS_WARN_IF(rv.Failed())) {
794
0
          rv.SuppressException();
795
0
          return nullptr;
796
0
        }
797
0
798
0
      } else if (tag == SCTAG_DOM_DIRECTORY) {
799
0
        RefPtr<Directory> directory =
800
0
          ReadDirectoryInternal(aReader, indexOrLengthOfString, aHolder);
801
0
        if (!directory) {
802
0
          return nullptr;
803
0
        }
804
0
805
0
        formData->Append(name, directory);
806
0
807
0
      } else {
808
0
        MOZ_ASSERT(tag == 0);
809
0
810
0
        nsAutoString value;
811
0
        if (NS_WARN_IF(!value.SetLength(indexOrLengthOfString, fallible))) {
812
0
          return nullptr;
813
0
        }
814
0
        size_t charSize = sizeof(nsString::char_type);
815
0
        if (!JS_ReadBytes(aReader, (void*) value.BeginWriting(),
816
0
                          indexOrLengthOfString * charSize)) {
817
0
          return nullptr;
818
0
        }
819
0
820
0
        ErrorResult rv;
821
0
        formData->Append(name, value, rv);
822
0
        if (NS_WARN_IF(rv.Failed())) {
823
0
          rv.SuppressException();
824
0
          return nullptr;
825
0
        }
826
0
      }
827
0
    }
828
0
829
0
    if (!ToJSValue(aCx, formData, &val)) {
830
0
      return nullptr;
831
0
    }
832
0
  }
833
0
834
0
  return &val.toObject();
835
0
}
836
837
// The format of the FormData serialization is:
838
// - pair of ints: SCTAG_DOM_FORMDATA, Length of the FormData elements
839
// - for each Element element:
840
//   - name string
841
//   - if it's a blob:
842
//     - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array
843
//       mBlobImplArray.
844
//   - if it's a directory (See WriteDirectory):
845
//     - pair of ints: SCTAG_DOM_DIRECTORY, path length
846
//     - path as string
847
//   - else:
848
//     - pair of ints: 0, string length
849
//     - value string
850
bool
851
WriteFormData(JSStructuredCloneWriter* aWriter,
852
              FormData* aFormData,
853
              StructuredCloneHolder* aHolder)
854
0
{
855
0
  MOZ_ASSERT(aWriter);
856
0
  MOZ_ASSERT(aFormData);
857
0
  MOZ_ASSERT(aHolder);
858
0
859
0
  if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FORMDATA,
860
0
                          aFormData->Length())) {
861
0
    return false;
862
0
  }
863
0
864
0
  class MOZ_STACK_CLASS Closure final
865
0
  {
866
0
    JSStructuredCloneWriter* mWriter;
867
0
    StructuredCloneHolder* mHolder;
868
0
869
0
  public:
870
0
    Closure(JSStructuredCloneWriter* aWriter,
871
0
            StructuredCloneHolder* aHolder)
872
0
      : mWriter(aWriter),
873
0
        mHolder(aHolder)
874
0
    { }
875
0
876
0
    static bool
877
0
    Write(const nsString& aName, const OwningBlobOrDirectoryOrUSVString& aValue,
878
0
          void* aClosure)
879
0
    {
880
0
      Closure* closure = static_cast<Closure*>(aClosure);
881
0
      if (!WriteString(closure->mWriter, aName)) {
882
0
        return false;
883
0
      }
884
0
885
0
      if (aValue.IsBlob()) {
886
0
        if (!JS_WriteUint32Pair(closure->mWriter, SCTAG_DOM_BLOB,
887
0
                                closure->mHolder->BlobImpls().Length())) {
888
0
          return false;
889
0
        }
890
0
891
0
        RefPtr<BlobImpl> blobImpl = aValue.GetAsBlob()->Impl();
892
0
        MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
893
0
894
0
        closure->mHolder->BlobImpls().AppendElement(blobImpl);
895
0
        return true;
896
0
      }
897
0
898
0
      if (aValue.IsDirectory()) {
899
0
        Directory* directory = aValue.GetAsDirectory();
900
0
        return WriteDirectory(closure->mWriter, directory);
901
0
      }
902
0
903
0
      size_t charSize = sizeof(nsString::char_type);
904
0
      if (!JS_WriteUint32Pair(closure->mWriter, 0,
905
0
                              aValue.GetAsUSVString().Length()) ||
906
0
          !JS_WriteBytes(closure->mWriter, aValue.GetAsUSVString().get(),
907
0
                         aValue.GetAsUSVString().Length() * charSize)) {
908
0
        return false;
909
0
      }
910
0
911
0
      return true;
912
0
    }
913
0
  };
914
0
  Closure closure(aWriter, aHolder);
915
0
  return aFormData->ForEach(Closure::Write, &closure);
916
0
}
917
918
JSObject*
919
ReadWasmModule(JSContext* aCx,
920
               uint32_t aIndex,
921
               StructuredCloneHolder* aHolder)
922
0
{
923
0
  MOZ_ASSERT(aHolder);
924
0
  MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
925
0
             aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
926
0
#ifdef FUZZING
927
0
  if (aIndex >= aHolder->WasmModules().Length()) {
928
0
    return nullptr;
929
0
  }
930
0
#endif
931
0
  MOZ_ASSERT(aIndex < aHolder->WasmModules().Length());
932
0
933
0
  return aHolder->WasmModules()[aIndex]->createObject(aCx);
934
0
}
935
936
bool
937
WriteWasmModule(JSStructuredCloneWriter* aWriter,
938
                JS::WasmModule* aWasmModule,
939
                StructuredCloneHolder* aHolder)
940
0
{
941
0
  MOZ_ASSERT(aWriter);
942
0
  MOZ_ASSERT(aWasmModule);
943
0
  MOZ_ASSERT(aHolder);
944
0
  MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
945
0
             aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
946
0
947
0
  // We store the position of the wasmModule in the array as index.
948
0
  if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_WASM,
949
0
                         aHolder->WasmModules().Length())) {
950
0
    aHolder->WasmModules().AppendElement(aWasmModule);
951
0
    return true;
952
0
  }
953
0
954
0
  return false;
955
0
}
956
957
JSObject*
958
ReadInputStream(JSContext* aCx,
959
                uint32_t aIndex,
960
                StructuredCloneHolder* aHolder)
961
0
{
962
0
  MOZ_ASSERT(aHolder);
963
0
#ifdef FUZZING
964
0
  if (aIndex >= aHolder->InputStreams().Length()) {
965
0
    return nullptr;
966
0
  }
967
0
#endif
968
0
  MOZ_ASSERT(aIndex < aHolder->InputStreams().Length());
969
0
  nsCOMPtr<nsIInputStream> inputStream = aHolder->InputStreams()[aIndex];
970
0
971
0
  JS::RootedValue result(aCx);
972
0
  nsresult rv = nsContentUtils::WrapNative(aCx, inputStream,
973
0
                                           &NS_GET_IID(nsIInputStream),
974
0
                                           &result);
975
0
  if (NS_FAILED(rv)) {
976
0
    return nullptr;
977
0
  }
978
0
979
0
  return &result.toObject();
980
0
}
981
982
bool
983
WriteInputStream(JSStructuredCloneWriter* aWriter,
984
                 nsIInputStream* aInputStream,
985
                 StructuredCloneHolder* aHolder)
986
0
{
987
0
  MOZ_ASSERT(aWriter);
988
0
  MOZ_ASSERT(aInputStream);
989
0
  MOZ_ASSERT(aHolder);
990
0
991
0
  // We store the position of the inputStream in the array as index.
992
0
  if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_INPUTSTREAM,
993
0
                         aHolder->InputStreams().Length())) {
994
0
    aHolder->InputStreams().AppendElement(aInputStream);
995
0
    return true;
996
0
  }
997
0
998
0
  return false;
999
0
}
1000
1001
} // anonymous namespace
1002
1003
JSObject*
1004
StructuredCloneHolder::CustomReadHandler(JSContext* aCx,
1005
                                         JSStructuredCloneReader* aReader,
1006
                                         uint32_t aTag,
1007
                                         uint32_t aIndex)
1008
0
{
1009
0
  MOZ_ASSERT(mSupportsCloning);
1010
0
1011
0
  if (aTag == SCTAG_DOM_BLOB) {
1012
0
    return ReadBlob(aCx, aIndex, this);
1013
0
  }
1014
0
1015
0
  if (aTag == SCTAG_DOM_DIRECTORY) {
1016
0
    return ReadDirectory(aCx, aReader, aIndex, this);
1017
0
  }
1018
0
1019
0
  if (aTag == SCTAG_DOM_FILELIST) {
1020
0
    return ReadFileList(aCx, aReader, aIndex, this);
1021
0
  }
1022
0
1023
0
  if (aTag == SCTAG_DOM_FORMDATA) {
1024
0
    return ReadFormData(aCx, aReader, aIndex, this);
1025
0
  }
1026
0
1027
0
  if (aTag == SCTAG_DOM_IMAGEBITMAP &&
1028
0
      (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1029
0
       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
1030
0
1031
0
    // Get the current global object.
1032
0
    // This can be null.
1033
0
    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
1034
0
    // aIndex is the index of the cloned image.
1035
0
    return ImageBitmap::ReadStructuredClone(aCx, aReader,
1036
0
                                            parent, GetSurfaces(), aIndex);
1037
0
  }
1038
0
1039
0
  if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) {
1040
0
    return StructuredCloneBlob::ReadStructuredClone(aCx, aReader, this);
1041
0
  }
1042
0
1043
0
  if (aTag == SCTAG_DOM_WASM &&
1044
0
      (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1045
0
       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
1046
0
    return ReadWasmModule(aCx, aIndex, this);
1047
0
  }
1048
0
1049
0
  if (aTag == SCTAG_DOM_INPUTSTREAM) {
1050
0
    return ReadInputStream(aCx, aIndex, this);
1051
0
  }
1052
0
1053
0
  return ReadFullySerializableObjects(aCx, aReader, aTag);
1054
0
}
1055
1056
bool
1057
StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
1058
                                          JSStructuredCloneWriter* aWriter,
1059
                                          JS::Handle<JSObject*> aObj)
1060
0
{
1061
0
  if (!mSupportsCloning) {
1062
0
    return false;
1063
0
  }
1064
0
1065
0
  JS::Rooted<JSObject*> obj(aCx, aObj);
1066
0
1067
0
  // See if this is a File/Blob object.
1068
0
  {
1069
0
    Blob* blob = nullptr;
1070
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
1071
0
      return WriteBlob(aWriter, blob, this);
1072
0
    }
1073
0
  }
1074
0
1075
0
  // See if this is a Directory object.
1076
0
  {
1077
0
    Directory* directory = nullptr;
1078
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, &obj, directory))) {
1079
0
      return WriteDirectory(aWriter, directory);
1080
0
    }
1081
0
  }
1082
0
1083
0
  // See if this is a FileList object.
1084
0
  {
1085
0
    FileList* fileList = nullptr;
1086
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, &obj, fileList))) {
1087
0
      return WriteFileList(aWriter, fileList, this);
1088
0
    }
1089
0
  }
1090
0
1091
0
  // See if this is a FormData object.
1092
0
  {
1093
0
    FormData* formData = nullptr;
1094
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, &obj, formData))) {
1095
0
      return WriteFormData(aWriter, formData, this);
1096
0
    }
1097
0
  }
1098
0
1099
0
  // See if this is an ImageBitmap object.
1100
0
  if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1101
0
      mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
1102
0
    ImageBitmap* imageBitmap = nullptr;
1103
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, &obj, imageBitmap))) {
1104
0
      return ImageBitmap::WriteStructuredClone(aWriter,
1105
0
                                               GetSurfaces(),
1106
0
                                               imageBitmap);
1107
0
    }
1108
0
  }
1109
0
1110
0
  // See if this is a StructuredCloneBlob object.
1111
0
  {
1112
0
    StructuredCloneBlob* holder = nullptr;
1113
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder, &obj, holder))) {
1114
0
      return holder->WriteStructuredClone(aCx, aWriter, this);
1115
0
    }
1116
0
  }
1117
0
1118
0
  // See if this is a WasmModule.
1119
0
  if ((mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1120
0
       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) &&
1121
0
      JS::IsWasmModuleObject(obj)) {
1122
0
    RefPtr<JS::WasmModule> module = JS::GetWasmModule(obj);
1123
0
    MOZ_ASSERT(module);
1124
0
1125
0
    return WriteWasmModule(aWriter, module, this);
1126
0
  }
1127
0
1128
0
  {
1129
0
    nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(aObj);
1130
0
    nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(base);
1131
0
    if (inputStream) {
1132
0
      return WriteInputStream(aWriter, inputStream, this);
1133
0
    }
1134
0
  }
1135
0
1136
0
  return WriteFullySerializableObjects(aCx, aWriter, aObj);
1137
0
}
1138
1139
bool
1140
StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
1141
                                                 JSStructuredCloneReader* aReader,
1142
                                                 uint32_t aTag,
1143
                                                 void* aContent,
1144
                                                 uint64_t aExtraData,
1145
                                                 JS::MutableHandleObject aReturnObject)
1146
0
{
1147
0
  MOZ_ASSERT(mSupportsTransferring);
1148
0
1149
0
  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
1150
0
#ifdef FUZZING
1151
0
    if (aExtraData >= mPortIdentifiers.Length()) {
1152
0
      return false;
1153
0
    }
1154
0
#endif
1155
0
    MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
1156
0
    const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
1157
0
1158
0
    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
1159
0
1160
0
    ErrorResult rv;
1161
0
    RefPtr<MessagePort> port =
1162
0
      MessagePort::Create(global, portIdentifier, rv);
1163
0
    if (NS_WARN_IF(rv.Failed())) {
1164
0
      rv.SuppressException();
1165
0
      return false;
1166
0
    }
1167
0
1168
0
    mTransferredPorts.AppendElement(port);
1169
0
1170
0
    JS::Rooted<JS::Value> value(aCx);
1171
0
    if (!GetOrCreateDOMReflector(aCx, port, &value)) {
1172
0
      JS_ClearPendingException(aCx);
1173
0
      return false;
1174
0
    }
1175
0
1176
0
    aReturnObject.set(&value.toObject());
1177
0
    return true;
1178
0
  }
1179
0
1180
0
  if (aTag == SCTAG_DOM_CANVAS &&
1181
0
      (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1182
0
       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
1183
0
    MOZ_ASSERT(aContent);
1184
0
    OffscreenCanvasCloneData* data =
1185
0
      static_cast<OffscreenCanvasCloneData*>(aContent);
1186
0
    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
1187
0
    RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(parent, data);
1188
0
    delete data;
1189
0
1190
0
    JS::Rooted<JS::Value> value(aCx);
1191
0
    if (!GetOrCreateDOMReflector(aCx, canvas, &value)) {
1192
0
      JS_ClearPendingException(aCx);
1193
0
      return false;
1194
0
    }
1195
0
1196
0
    aReturnObject.set(&value.toObject());
1197
0
    return true;
1198
0
  }
1199
0
1200
0
  if (aTag == SCTAG_DOM_IMAGEBITMAP &&
1201
0
      (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1202
0
       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
1203
0
    MOZ_ASSERT(aContent);
1204
0
    ImageBitmapCloneData* data =
1205
0
      static_cast<ImageBitmapCloneData*>(aContent);
1206
0
    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
1207
0
    RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data);
1208
0
    delete data;
1209
0
1210
0
    JS::Rooted<JS::Value> value(aCx);
1211
0
    if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) {
1212
0
      JS_ClearPendingException(aCx);
1213
0
      return false;
1214
0
    }
1215
0
1216
0
    aReturnObject.set(&value.toObject());
1217
0
    return true;
1218
0
  }
1219
0
1220
0
  return false;
1221
0
}
1222
1223
bool
1224
StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
1225
                                                  JS::Handle<JSObject*> aObj,
1226
                                                  uint32_t* aTag,
1227
                                                  JS::TransferableOwnership* aOwnership,
1228
                                                  void** aContent,
1229
                                                  uint64_t* aExtraData)
1230
0
{
1231
0
  if (!mSupportsTransferring) {
1232
0
    return false;
1233
0
  }
1234
0
1235
0
  JS::Rooted<JSObject*> obj(aCx, aObj);
1236
0
1237
0
  {
1238
0
    MessagePort* port = nullptr;
1239
0
    nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port);
1240
0
    if (NS_SUCCEEDED(rv)) {
1241
0
      // We use aExtraData to store the index of this new port identifier.
1242
0
      *aExtraData = mPortIdentifiers.Length();
1243
0
      MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement();
1244
0
1245
0
      if (!port->CanBeCloned()) {
1246
0
        return false;
1247
0
      }
1248
0
1249
0
      port->CloneAndDisentangle(*identifier);
1250
0
1251
0
      *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
1252
0
      *aOwnership = JS::SCTAG_TMO_CUSTOM;
1253
0
      *aContent = nullptr;
1254
0
1255
0
      return true;
1256
0
    }
1257
0
1258
0
    if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1259
0
        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
1260
0
      OffscreenCanvas* canvas = nullptr;
1261
0
      rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas);
1262
0
      if (NS_SUCCEEDED(rv)) {
1263
0
        MOZ_ASSERT(canvas);
1264
0
1265
0
        if (canvas->IsNeutered()) {
1266
0
          return false;
1267
0
        }
1268
0
1269
0
        *aExtraData = 0;
1270
0
        *aTag = SCTAG_DOM_CANVAS;
1271
0
        *aOwnership = JS::SCTAG_TMO_CUSTOM;
1272
0
        *aContent = canvas->ToCloneData();
1273
0
        MOZ_ASSERT(*aContent);
1274
0
        canvas->SetNeutered();
1275
0
1276
0
        return true;
1277
0
      }
1278
0
1279
0
      ImageBitmap* bitmap = nullptr;
1280
0
      rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap);
1281
0
      if (NS_SUCCEEDED(rv)) {
1282
0
        MOZ_ASSERT(bitmap);
1283
0
1284
0
        *aExtraData = 0;
1285
0
        *aTag = SCTAG_DOM_IMAGEBITMAP;
1286
0
        *aOwnership = JS::SCTAG_TMO_CUSTOM;
1287
0
1288
0
        UniquePtr<ImageBitmapCloneData> clonedBitmap = bitmap->ToCloneData();
1289
0
        if (!clonedBitmap) {
1290
0
          return false;
1291
0
        }
1292
0
1293
0
        *aContent = clonedBitmap.release();
1294
0
        MOZ_ASSERT(*aContent);
1295
0
        bitmap->Close();
1296
0
1297
0
        return true;
1298
0
      }
1299
0
    }
1300
0
  }
1301
0
1302
0
  return false;
1303
0
}
1304
1305
void
1306
StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
1307
                                                 JS::TransferableOwnership aOwnership,
1308
                                                 void* aContent,
1309
                                                 uint64_t aExtraData)
1310
0
{
1311
0
  MOZ_ASSERT(mSupportsTransferring);
1312
0
1313
0
  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
1314
0
    MOZ_ASSERT(!aContent);
1315
0
#ifdef FUZZING
1316
0
    if (aExtraData >= mPortIdentifiers.Length()) {
1317
0
      return;
1318
0
    }
1319
0
#endif
1320
0
    MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
1321
0
    MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
1322
0
    return;
1323
0
  }
1324
0
1325
0
  if (aTag == SCTAG_DOM_CANVAS &&
1326
0
      (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1327
0
       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
1328
0
    MOZ_ASSERT(aContent);
1329
0
    OffscreenCanvasCloneData* data =
1330
0
      static_cast<OffscreenCanvasCloneData*>(aContent);
1331
0
    delete data;
1332
0
    return;
1333
0
  }
1334
0
1335
0
  if (aTag == SCTAG_DOM_IMAGEBITMAP &&
1336
0
      (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1337
0
       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) {
1338
0
    MOZ_ASSERT(aContent);
1339
0
    ImageBitmapCloneData* data =
1340
0
      static_cast<ImageBitmapCloneData*>(aContent);
1341
0
    delete data;
1342
0
    return;
1343
0
  }
1344
0
}
1345
1346
bool
1347
StructuredCloneHolder::CustomCanTransferHandler(JSContext* aCx,
1348
                                                JS::Handle<JSObject*> aObj)
1349
0
{
1350
0
  if (!mSupportsTransferring) {
1351
0
    return false;
1352
0
  }
1353
0
1354
0
  JS::Rooted<JSObject*> obj(aCx, aObj);
1355
0
1356
0
  {
1357
0
    MessagePort* port = nullptr;
1358
0
    nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port);
1359
0
    if (NS_SUCCEEDED(rv)) {
1360
0
      return true;
1361
0
    }
1362
0
1363
0
    if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
1364
0
        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
1365
0
      OffscreenCanvas* canvas = nullptr;
1366
0
      rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas);
1367
0
      if (NS_SUCCEEDED(rv)) {
1368
0
        return true;
1369
0
      }
1370
0
1371
0
      ImageBitmap* bitmap = nullptr;
1372
0
      rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap);
1373
0
      if (NS_SUCCEEDED(rv)) {
1374
0
        return true;
1375
0
      }
1376
0
    }
1377
0
  }
1378
0
1379
0
  return false;
1380
0
}
1381
1382
bool
1383
StructuredCloneHolder::TakeTransferredPortsAsSequence(Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts)
1384
0
{
1385
0
  nsTArray<RefPtr<MessagePort>> ports = TakeTransferredPorts();
1386
0
1387
0
  aPorts.Clear();
1388
0
  for (uint32_t i = 0, len = ports.Length(); i < len; ++i) {
1389
0
    if (!aPorts.AppendElement(ports[i].forget(), fallible)) {
1390
0
      return false;
1391
0
    }
1392
0
  }
1393
0
1394
0
  return true;
1395
0
}
1396
1397
} // dom namespace
1398
} // mozilla namespace