tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StructuredCloneHolder.cpp (65607B)


      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 "mozilla/dom/StructuredCloneHolder.h"
      8 
      9 #include <new>
     10 
     11 #include "ErrorList.h"
     12 #include "MainThreadUtils.h"
     13 #include "js/CallArgs.h"
     14 #include "js/Value.h"
     15 #include "js/WasmModule.h"
     16 #include "js/Wrapper.h"
     17 #include "jsapi.h"
     18 #include "mozilla/AlreadyAddRefed.h"
     19 #include "mozilla/AutoRestore.h"
     20 #include "mozilla/ErrorResult.h"
     21 #include "mozilla/OwningNonNull.h"
     22 #include "mozilla/RefPtr.h"
     23 #include "mozilla/ScopeExit.h"
     24 #include "mozilla/StaticPrefs_dom.h"
     25 #ifdef MOZ_WEBRTC
     26 #  include "mozilla/StaticPrefs_media.h"
     27 #endif
     28 #include "mozilla/dom/AudioData.h"
     29 #include "mozilla/dom/BindingDeclarations.h"
     30 #include "mozilla/dom/BindingUtils.h"
     31 #include "mozilla/dom/Blob.h"
     32 #include "mozilla/dom/BlobBinding.h"
     33 #include "mozilla/dom/BlobImpl.h"
     34 #include "mozilla/dom/BrowsingContext.h"
     35 #include "mozilla/dom/ClonedErrorHolder.h"
     36 #include "mozilla/dom/ClonedErrorHolderBinding.h"
     37 #include "mozilla/dom/DOMJSClass.h"
     38 #include "mozilla/dom/DOMTypes.h"
     39 #include "mozilla/dom/Directory.h"
     40 #include "mozilla/dom/DirectoryBinding.h"
     41 #include "mozilla/dom/DocGroup.h"
     42 #include "mozilla/dom/EncodedAudioChunk.h"
     43 #include "mozilla/dom/EncodedAudioChunkBinding.h"
     44 #include "mozilla/dom/EncodedVideoChunk.h"
     45 #include "mozilla/dom/EncodedVideoChunkBinding.h"
     46 #include "mozilla/dom/File.h"
     47 #include "mozilla/dom/FileList.h"
     48 #include "mozilla/dom/FileListBinding.h"
     49 #include "mozilla/dom/FormData.h"
     50 #include "mozilla/dom/FormDataBinding.h"
     51 #include "mozilla/dom/ImageBitmap.h"
     52 #include "mozilla/dom/ImageBitmapBinding.h"
     53 #include "mozilla/dom/JSExecutionManager.h"
     54 #include "mozilla/dom/MessagePort.h"
     55 #include "mozilla/dom/MessagePortBinding.h"
     56 #include "mozilla/dom/OffscreenCanvas.h"
     57 #include "mozilla/dom/OffscreenCanvasBinding.h"
     58 #ifdef MOZ_WEBRTC
     59 #  include "mozilla/dom/RTCEncodedAudioFrame.h"
     60 #  include "mozilla/dom/RTCEncodedAudioFrameBinding.h"
     61 #  include "mozilla/dom/RTCEncodedVideoFrame.h"
     62 #  include "mozilla/dom/RTCEncodedVideoFrameBinding.h"
     63 #endif
     64 #include "mozilla/dom/ReadableStream.h"
     65 #include "mozilla/dom/ReadableStreamBinding.h"
     66 #include "mozilla/dom/ScriptSettings.h"
     67 #include "mozilla/dom/StructuredCloneBlob.h"
     68 #include "mozilla/dom/StructuredCloneHolderBinding.h"
     69 #include "mozilla/dom/StructuredCloneTags.h"
     70 #include "mozilla/dom/ToJSValue.h"
     71 #include "mozilla/dom/TransformStream.h"
     72 #include "mozilla/dom/TransformStreamBinding.h"
     73 #include "mozilla/dom/VideoFrame.h"
     74 #include "mozilla/dom/VideoFrameBinding.h"
     75 #include "mozilla/dom/WebIDLSerializable.h"
     76 #include "mozilla/dom/WorkerCommon.h"
     77 #include "mozilla/dom/WorkerPrivate.h"
     78 #include "mozilla/dom/WritableStream.h"
     79 #include "mozilla/dom/WritableStreamBinding.h"
     80 #include "mozilla/fallible.h"
     81 #include "mozilla/gfx/2D.h"
     82 #include "nsContentUtils.h"
     83 #include "nsDebug.h"
     84 #include "nsError.h"
     85 #include "nsID.h"
     86 #include "nsIEventTarget.h"
     87 #include "nsIFile.h"
     88 #include "nsIGlobalObject.h"
     89 #include "nsIInputStream.h"
     90 #include "nsIPrincipal.h"
     91 #include "nsISupports.h"
     92 #include "nsJSPrincipals.h"
     93 #include "nsPIDOMWindow.h"
     94 #include "nsString.h"
     95 #include "nsThreadUtils.h"
     96 #include "nsXPCOM.h"
     97 #include "xpcpublic.h"
     98 
     99 #ifdef MOZ_WEBRTC
    100 #  include "mozilla/dom/RTCDataChannel.h"
    101 #endif
    102 
    103 using namespace mozilla::ipc;
    104 
    105 namespace mozilla::dom {
    106 
    107 namespace {
    108 
    109 JSObject* StructuredCloneCallbacksRead(
    110    JSContext* aCx, JSStructuredCloneReader* aReader,
    111    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aIndex,
    112    void* aClosure) {
    113  StructuredCloneHolderBase* holder =
    114      static_cast<StructuredCloneHolderBase*>(aClosure);
    115  MOZ_ASSERT(holder);
    116  return holder->CustomReadHandler(aCx, aReader, aCloneDataPolicy, aTag,
    117                                   aIndex);
    118 }
    119 
    120 bool StructuredCloneCallbacksWrite(JSContext* aCx,
    121                                   JSStructuredCloneWriter* aWriter,
    122                                   JS::Handle<JSObject*> aObj,
    123                                   bool* aSameProcessScopeRequired,
    124                                   void* aClosure) {
    125  StructuredCloneHolderBase* holder =
    126      static_cast<StructuredCloneHolderBase*>(aClosure);
    127  MOZ_ASSERT(holder);
    128  return holder->CustomWriteHandler(aCx, aWriter, aObj,
    129                                    aSameProcessScopeRequired);
    130 }
    131 
    132 bool StructuredCloneCallbacksReadTransfer(
    133    JSContext* aCx, JSStructuredCloneReader* aReader,
    134    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, void* aContent,
    135    uint64_t aExtraData, void* aClosure,
    136    JS::MutableHandle<JSObject*> aReturnObject) {
    137  StructuredCloneHolderBase* holder =
    138      static_cast<StructuredCloneHolderBase*>(aClosure);
    139  MOZ_ASSERT(holder);
    140  return holder->CustomReadTransferHandler(aCx, aReader, aCloneDataPolicy, aTag,
    141                                           aContent, aExtraData, aReturnObject);
    142 }
    143 
    144 bool StructuredCloneCallbacksWriteTransfer(
    145    JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
    146    // Output:
    147    uint32_t* aTag, JS::TransferableOwnership* aOwnership, void** aContent,
    148    uint64_t* aExtraData) {
    149  StructuredCloneHolderBase* holder =
    150      static_cast<StructuredCloneHolderBase*>(aClosure);
    151  MOZ_ASSERT(holder);
    152  return holder->CustomWriteTransferHandler(aCx, aObj, aTag, aOwnership,
    153                                            aContent, aExtraData);
    154 }
    155 
    156 void StructuredCloneCallbacksFreeTransfer(uint32_t aTag,
    157                                          JS::TransferableOwnership aOwnership,
    158                                          void* aContent, uint64_t aExtraData,
    159                                          void* aClosure) {
    160  StructuredCloneHolderBase* holder =
    161      static_cast<StructuredCloneHolderBase*>(aClosure);
    162  MOZ_ASSERT(holder);
    163  return holder->CustomFreeTransferHandler(aTag, aOwnership, aContent,
    164                                           aExtraData);
    165 }
    166 
    167 bool StructuredCloneCallbacksCanTransfer(JSContext* aCx,
    168                                         JS::Handle<JSObject*> aObject,
    169                                         bool* aSameProcessScopeRequired,
    170                                         void* aClosure) {
    171  StructuredCloneHolderBase* holder =
    172      static_cast<StructuredCloneHolderBase*>(aClosure);
    173  MOZ_ASSERT(holder);
    174  return holder->CustomCanTransferHandler(aCx, aObject,
    175                                          aSameProcessScopeRequired);
    176 }
    177 
    178 bool StructuredCloneCallbacksSharedArrayBuffer(JSContext* cx, bool aReceiving,
    179                                               void* aClosure) {
    180  if (!StaticPrefs::dom_workers_serialized_sab_access()) {
    181    return true;
    182  }
    183 
    184  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    185 
    186  if (workerPrivate) {
    187    workerPrivate->SetExecutionManager(
    188        JSExecutionManager::GetSABSerializationManager());
    189  } else if (NS_IsMainThread()) {
    190    nsIGlobalObject* global = GetCurrentGlobal();
    191 
    192    nsPIDOMWindowInner* innerWindow = nullptr;
    193    if (global) {
    194      innerWindow = global->GetAsInnerWindow();
    195    }
    196 
    197    DocGroup* docGroup = nullptr;
    198    if (innerWindow) {
    199      docGroup = innerWindow->GetDocGroup();
    200    }
    201 
    202    if (docGroup) {
    203      docGroup->SetExecutionManager(
    204          JSExecutionManager::GetSABSerializationManager());
    205    }
    206  }
    207  return true;
    208 }
    209 
    210 void StructuredCloneCallbacksError(JSContext* aCx, uint32_t aErrorId,
    211                                   void* aClosure, const char* aErrorMessage) {
    212  NS_WARNING("Failed to clone data.");
    213  StructuredCloneHolderBase* holder =
    214      static_cast<StructuredCloneHolderBase*>(aClosure);
    215  MOZ_ASSERT(holder);
    216  return holder->SetErrorMessage(aErrorMessage);
    217 }
    218 
    219 void AssertTagValues() {
    220  static_assert(SCTAG_DOM_IMAGEDATA == 0xffff8007 &&
    221                    SCTAG_DOM_DOMPOINT == 0xffff8008 &&
    222                    SCTAG_DOM_DOMPOINTREADONLY == 0xffff8009 &&
    223                    SCTAG_DOM_CRYPTOKEY == 0xffff800a &&
    224                    SCTAG_DOM_NULL_PRINCIPAL == 0xffff800b &&
    225                    SCTAG_DOM_SYSTEM_PRINCIPAL == 0xffff800c &&
    226                    SCTAG_DOM_CONTENT_PRINCIPAL == 0xffff800d &&
    227                    SCTAG_DOM_DOMQUAD == 0xffff800e &&
    228                    SCTAG_DOM_RTCCERTIFICATE == 0xffff800f &&
    229                    SCTAG_DOM_DOMRECT == 0xffff8010 &&
    230                    SCTAG_DOM_DOMRECTREADONLY == 0xffff8011 &&
    231                    SCTAG_DOM_EXPANDED_PRINCIPAL == 0xffff8012 &&
    232                    SCTAG_DOM_DOMMATRIX == 0xffff8013 &&
    233                    SCTAG_DOM_URLSEARCHPARAMS == 0xffff8014 &&
    234                    SCTAG_DOM_DOMMATRIXREADONLY == 0xffff8015 &&
    235                    SCTAG_DOM_STRUCTUREDCLONETESTER == 0xffff8018 &&
    236                    SCTAG_DOM_FILESYSTEMHANDLE == 0xffff8019 &&
    237                    SCTAG_DOM_FILESYSTEMFILEHANDLE == 0xffff801a &&
    238                    SCTAG_DOM_FILESYSTEMDIRECTORYHANDLE == 0xffff801b,
    239                "Something has changed the sctag values. This is wrong!");
    240 }
    241 
    242 }  // anonymous namespace
    243 
    244 const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
    245    StructuredCloneCallbacksRead,
    246    StructuredCloneCallbacksWrite,
    247    StructuredCloneCallbacksError,
    248    StructuredCloneCallbacksReadTransfer,
    249    StructuredCloneCallbacksWriteTransfer,
    250    StructuredCloneCallbacksFreeTransfer,
    251    StructuredCloneCallbacksCanTransfer,
    252    StructuredCloneCallbacksSharedArrayBuffer,
    253 };
    254 
    255 // StructuredCloneHolderBase class
    256 
    257 StructuredCloneHolderBase::StructuredCloneHolderBase(
    258    StructuredCloneScope aScope)
    259    : mStructuredCloneScope(aScope)
    260 #ifdef DEBUG
    261      ,
    262      mClearCalled(false)
    263 #endif
    264 {
    265 }
    266 
    267 StructuredCloneHolderBase::~StructuredCloneHolderBase() {
    268 #ifdef DEBUG
    269  MOZ_ASSERT(mClearCalled);
    270 #endif
    271 }
    272 
    273 void StructuredCloneHolderBase::Clear() {
    274 #ifdef DEBUG
    275  mClearCalled = true;
    276 #endif
    277 
    278  mBuffer = nullptr;
    279 }
    280 
    281 bool StructuredCloneHolderBase::Write(JSContext* aCx,
    282                                      JS::Handle<JS::Value> aValue) {
    283  return Write(aCx, aValue, JS::UndefinedHandleValue, JS::CloneDataPolicy());
    284 }
    285 
    286 bool StructuredCloneHolderBase::Write(
    287    JSContext* aCx, JS::Handle<JS::Value> aValue,
    288    JS::Handle<JS::Value> aTransfer,
    289    const JS::CloneDataPolicy& aCloneDataPolicy) {
    290  MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
    291  MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
    292 
    293  mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(
    294      mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this);
    295 
    296  if (!mBuffer->write(aCx, aValue, aTransfer, aCloneDataPolicy,
    297                      &StructuredCloneHolder::sCallbacks, this)) {
    298    mBuffer = nullptr;
    299    return false;
    300  }
    301 
    302  // Let's update our scope to the final one. The new one could be more
    303  // restrictive of the current one.
    304  MOZ_ASSERT(mStructuredCloneScope >= mBuffer->scope());
    305  mStructuredCloneScope = mBuffer->scope();
    306  return true;
    307 }
    308 
    309 bool StructuredCloneHolderBase::Read(JSContext* aCx,
    310                                     JS::MutableHandle<JS::Value> aValue) {
    311  return Read(aCx, aValue, JS::CloneDataPolicy());
    312 }
    313 
    314 bool StructuredCloneHolderBase::Read(
    315    JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
    316    const JS::CloneDataPolicy& aCloneDataPolicy) {
    317  MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
    318  MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
    319 
    320  bool ok = mBuffer->read(aCx, aValue, aCloneDataPolicy,
    321                          &StructuredCloneHolder::sCallbacks, this);
    322  return ok;
    323 }
    324 
    325 bool StructuredCloneHolderBase::CustomReadTransferHandler(
    326    JSContext* aCx, JSStructuredCloneReader* aReader,
    327    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, void* aContent,
    328    uint64_t aExtraData, JS::MutableHandle<JSObject*> aReturnObject) {
    329  MOZ_CRASH("Nothing to read.");
    330  return false;
    331 }
    332 
    333 bool StructuredCloneHolderBase::CustomWriteTransferHandler(
    334    JSContext* aCx, JS::Handle<JSObject*> aObj, uint32_t* aTag,
    335    JS::TransferableOwnership* aOwnership, void** aContent,
    336    uint64_t* aExtraData) {
    337  // No transfers are supported by default.
    338  return false;
    339 }
    340 
    341 void StructuredCloneHolderBase::CustomFreeTransferHandler(
    342    uint32_t aTag, JS::TransferableOwnership aOwnership, void* aContent,
    343    uint64_t aExtraData) {
    344  MOZ_CRASH("Nothing to free.");
    345 }
    346 
    347 bool StructuredCloneHolderBase::CustomCanTransferHandler(
    348    JSContext* aCx, JS::Handle<JSObject*> aObj,
    349    bool* aSameProcessScopeRequired) {
    350  return false;
    351 }
    352 
    353 // StructuredCloneHolder class
    354 
    355 StructuredCloneHolder::StructuredCloneHolder(
    356    CloningSupport aSupportsCloning, TransferringSupport aSupportsTransferring,
    357    StructuredCloneScope aScope)
    358    : StructuredCloneHolderBase(aScope),
    359      mSupportsCloning(aSupportsCloning == CloningSupported),
    360      mSupportsTransferring(aSupportsTransferring == TransferringSupported),
    361      mGlobal(nullptr)
    362 #ifdef DEBUG
    363      ,
    364      mCreationEventTarget(GetCurrentSerialEventTarget())
    365 #endif
    366 {
    367 }
    368 
    369 StructuredCloneHolder::~StructuredCloneHolder() {
    370  Clear();
    371  MOZ_ASSERT(mTransferredPorts.IsEmpty());
    372 }
    373 
    374 void StructuredCloneHolder::Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
    375                                  ErrorResult& aRv) {
    376  Write(aCx, aValue, JS::UndefinedHandleValue, JS::CloneDataPolicy(), aRv);
    377 }
    378 
    379 void StructuredCloneHolder::Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
    380                                  JS::Handle<JS::Value> aTransfer,
    381                                  const JS::CloneDataPolicy& aCloneDataPolicy,
    382                                  ErrorResult& aRv) {
    383  if (!StructuredCloneHolderBase::Write(aCx, aValue, aTransfer,
    384                                        aCloneDataPolicy)) {
    385    aRv.ThrowDataCloneError(mErrorMessage);
    386    return;
    387  }
    388 }
    389 
    390 void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx,
    391                                 JS::MutableHandle<JS::Value> aValue,
    392                                 ErrorResult& aRv) {
    393  return Read(aGlobal, aCx, aValue, JS::CloneDataPolicy(), aRv);
    394 }
    395 
    396 void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx,
    397                                 JS::MutableHandle<JS::Value> aValue,
    398                                 const JS::CloneDataPolicy& aCloneDataPolicy,
    399                                 ErrorResult& aRv) {
    400  MOZ_ASSERT(aGlobal);
    401 
    402  mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal);
    403  auto errorMessageGuard = MakeScopeExit([&] { mErrorMessage.Truncate(); });
    404  mGlobal = aGlobal;
    405 
    406  if (!StructuredCloneHolderBase::Read(aCx, aValue, aCloneDataPolicy)) {
    407    mTransferredPorts.Clear();
    408    JS_ClearPendingException(aCx);
    409    aRv.ThrowDataCloneError(mErrorMessage);
    410    return;
    411  }
    412 
    413  // If we are transferring something, we cannot call 'Read()' more than once.
    414  if (mSupportsTransferring) {
    415 #define STMT(_member) (_member).Clear()
    416    CLONED_DATA_MEMBERS
    417 #undef STMT
    418 
    419    Clear();
    420  }
    421 }
    422 
    423 void StructuredCloneHolder::ReadFromBuffer(
    424    nsIGlobalObject* aGlobal, JSContext* aCx, JSStructuredCloneData& aBuffer,
    425    JS::MutableHandle<JS::Value> aValue,
    426    const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv) {
    427  ReadFromBuffer(aGlobal, aCx, aBuffer, JS_STRUCTURED_CLONE_VERSION, aValue,
    428                 aCloneDataPolicy, aRv);
    429 }
    430 
    431 void StructuredCloneHolder::ReadFromBuffer(
    432    nsIGlobalObject* aGlobal, JSContext* aCx, JSStructuredCloneData& aBuffer,
    433    uint32_t aAlgorithmVersion, JS::MutableHandle<JS::Value> aValue,
    434    const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv) {
    435  MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
    436 
    437  mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal);
    438  auto errorMessageGuard = MakeScopeExit([&] { mErrorMessage.Truncate(); });
    439  mGlobal = aGlobal;
    440 
    441  if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion, CloneScope(),
    442                              aValue, aCloneDataPolicy, &sCallbacks, this)) {
    443    JS_ClearPendingException(aCx);
    444    aRv.ThrowDataCloneError(mErrorMessage);
    445    return;
    446  }
    447 }
    448 
    449 static bool CheckExposedGlobals(JSContext* aCx, nsIGlobalObject* aGlobal,
    450                                uint16_t aExposedGlobals) {
    451  JS::Rooted<JSObject*> global(aCx, aGlobal->GetGlobalJSObject());
    452 
    453  // Sandboxes aren't really DOM globals (though they do set the
    454  // JSCLASS_DOM_GLOBAL flag), and so we can't simply do the exposure check.
    455  // Some sandboxes do have a DOM global as their prototype, so using the
    456  // prototype to check for exposure will at least make it work for those
    457  // specific cases.
    458  {
    459    JSObject* proto = xpc::SandboxPrototypeOrNull(aCx, global);
    460    if (proto) {
    461      global = proto;
    462    }
    463  }
    464 
    465  if (!IsGlobalInExposureSet(aCx, global, aExposedGlobals)) {
    466    ErrorResult error;
    467    error.ThrowDataCloneError("Interface is not exposed.");
    468    MOZ_ALWAYS_TRUE(error.MaybeSetPendingException(aCx));
    469    return false;
    470  }
    471  return true;
    472 }
    473 
    474 /* static */
    475 JSObject* StructuredCloneHolder::ReadFullySerializableObjects(
    476    JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
    477    bool aIsForIndexedDB) {
    478  AssertTagValues();
    479 
    480  nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
    481  if (!global) {
    482    return nullptr;
    483  }
    484 
    485  Maybe<std::pair<uint16_t, WebIDLDeserializer>> deserializer =
    486      LookupDeserializer(StructuredCloneTags(aTag));
    487  if (deserializer.isSome()) {
    488    uint16_t exposedGlobals;
    489    WebIDLDeserializer deserialize;
    490    std::tie(exposedGlobals, deserialize) = deserializer.ref();
    491 
    492    // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
    493    //
    494    // 22. Otherwise:
    495    //
    496    //   1. Let interfaceName be serialized.[[Type]].
    497    //   2. If the interface identified by interfaceName is not exposed in
    498    //      targetRealm, then throw a "DataCloneError" DOMException.
    499    //
    500    // The special-casing for IndexedDB is because it uses a sandbox to
    501    // deserialize, which means we don't actually have access to exposure
    502    // information.
    503    if (!aIsForIndexedDB && !CheckExposedGlobals(aCx, global, exposedGlobals)) {
    504      return nullptr;
    505    }
    506 
    507    return deserialize(aCx, global, aReader);
    508  }
    509 
    510  if (aTag == SCTAG_DOM_NULL_PRINCIPAL || aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
    511      aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
    512      aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
    513    JSPrincipals* prin;
    514    if (!nsJSPrincipals::ReadKnownPrincipalType(aCx, aReader, aTag, &prin)) {
    515      return nullptr;
    516    }
    517 
    518    JS::Rooted<JS::Value> result(aCx);
    519    {
    520      // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of
    521      // the casting between JSPrincipals* and nsIPrincipal* we can't use
    522      // getter_AddRefs above and have to already_AddRefed here.
    523      nsCOMPtr<nsIPrincipal> principal =
    524          already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
    525 
    526      nsresult rv = nsContentUtils::WrapNative(
    527          aCx, principal, &NS_GET_IID(nsIPrincipal), &result);
    528      if (NS_FAILED(rv)) {
    529        xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
    530        return nullptr;
    531      }
    532    }
    533    return result.toObjectOrNull();
    534  }
    535 
    536  // Don't know what this is. Bail.
    537  xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
    538  return nullptr;
    539 }
    540 
    541 /* static */
    542 bool StructuredCloneHolder::WriteFullySerializableObjects(
    543    JSContext* aCx, JSStructuredCloneWriter* aWriter,
    544    JS::Handle<JSObject*> aObj) {
    545  AssertTagValues();
    546 
    547  // Window and Location are not serializable, so it's OK to just do a static
    548  // unwrap here.
    549  JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrapStatic(aObj));
    550  if (!obj) {
    551    return xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
    552  }
    553 
    554  const DOMJSClass* domClass = GetDOMClass(obj);
    555  if (domClass && domClass->mSerializer) {
    556    return domClass->mSerializer(aCx, aWriter, obj);
    557  }
    558 
    559  if (NS_IsMainThread() && xpc::IsReflector(obj, aCx)) {
    560    // We only care about principals, so ReflectorToISupportsStatic is fine.
    561    nsCOMPtr<nsISupports> base = xpc::ReflectorToISupportsStatic(obj);
    562    nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
    563    if (principal) {
    564      auto nsjsprincipals = nsJSPrincipals::get(principal);
    565      return nsjsprincipals->write(aCx, aWriter);
    566    }
    567  }
    568 
    569  // Don't know what this is
    570  ErrorResult rv;
    571  const char* className = JS::GetClass(obj)->name;
    572  rv.ThrowDataCloneError(nsDependentCString(className) +
    573                         " object could not be cloned."_ns);
    574  MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(aCx));
    575  return false;
    576 }
    577 
    578 template <typename char_type>
    579 static bool ReadTString(JSStructuredCloneReader* aReader,
    580                        nsTString<char_type>& aString) {
    581  uint32_t length, zero;
    582  if (!JS_ReadUint32Pair(aReader, &length, &zero)) {
    583    return false;
    584  }
    585 
    586  if (NS_WARN_IF(!aString.SetLength(length, fallible))) {
    587    return false;
    588  }
    589  size_t charSize = sizeof(char_type);
    590  return JS_ReadBytes(aReader, (void*)aString.BeginWriting(),
    591                      length * charSize);
    592 }
    593 
    594 template <typename char_type>
    595 static bool WriteTString(JSStructuredCloneWriter* aWriter,
    596                         const nsTSubstring<char_type>& aString) {
    597  size_t charSize = sizeof(char_type);
    598  return JS_WriteUint32Pair(aWriter, aString.Length(), 0) &&
    599         JS_WriteBytes(aWriter, aString.BeginReading(),
    600                       aString.Length() * charSize);
    601 }
    602 
    603 /* static */
    604 bool StructuredCloneHolder::ReadString(JSStructuredCloneReader* aReader,
    605                                       nsString& aString) {
    606  return ReadTString(aReader, aString);
    607 }
    608 
    609 /* static */
    610 bool StructuredCloneHolder::WriteString(JSStructuredCloneWriter* aWriter,
    611                                        const nsAString& aString) {
    612  return WriteTString(aWriter, aString);
    613 }
    614 
    615 /* static */
    616 bool StructuredCloneHolder::ReadCString(JSStructuredCloneReader* aReader,
    617                                        nsCString& aString) {
    618  return ReadTString(aReader, aString);
    619 }
    620 
    621 /* static */
    622 bool StructuredCloneHolder::WriteCString(JSStructuredCloneWriter* aWriter,
    623                                         const nsACString& aString) {
    624  return WriteTString(aWriter, aString);
    625 }
    626 
    627 namespace {
    628 
    629 JSObject* ReadBlob(JSContext* aCx, uint32_t aIndex,
    630                   StructuredCloneHolder* aHolder) {
    631  MOZ_ASSERT(aHolder);
    632 #ifdef FUZZING
    633  if (aIndex >= aHolder->BlobImpls().Length()) {
    634    return nullptr;
    635  }
    636 #endif
    637  MOZ_ASSERT(aIndex < aHolder->BlobImpls().Length());
    638  JS::Rooted<JS::Value> val(aCx);
    639  {
    640    // RefPtr<File> and RefPtr<BlobImpl> need to go out of scope before
    641    // toObject() is called because the static analysis thinks releasing XPCOM
    642    // objects can GC (because in some cases it can!), and a return statement
    643    // with a JSObject* type means that JSObject* is on the stack as a raw
    644    // pointer while destructors are running.
    645    RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[aIndex];
    646 
    647    RefPtr<Blob> blob = Blob::Create(aHolder->GlobalDuringRead(), blobImpl);
    648    if (NS_WARN_IF(!blob)) {
    649      return nullptr;
    650    }
    651 
    652    if (!ToJSValue(aCx, blob, &val)) {
    653      return nullptr;
    654    }
    655  }
    656 
    657  return &val.toObject();
    658 }
    659 
    660 bool WriteBlob(JSStructuredCloneWriter* aWriter, Blob* aBlob,
    661               StructuredCloneHolder* aHolder) {
    662  MOZ_ASSERT(aWriter);
    663  MOZ_ASSERT(aBlob);
    664  MOZ_ASSERT(aHolder);
    665 
    666  RefPtr<BlobImpl> blobImpl = aBlob->Impl();
    667 
    668  // We store the position of the blobImpl in the array as index.
    669  if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
    670                         aHolder->BlobImpls().Length())) {
    671    aHolder->BlobImpls().AppendElement(blobImpl);
    672    return true;
    673  }
    674 
    675  return false;
    676 }
    677 
    678 // A directory is serialized as:
    679 // - pair of ints: SCTAG_DOM_DIRECTORY, path length
    680 // - path as string
    681 bool WriteDirectory(JSStructuredCloneWriter* aWriter, Directory* aDirectory) {
    682  MOZ_ASSERT(aWriter);
    683  MOZ_ASSERT(aDirectory);
    684 
    685  nsAutoString path;
    686  aDirectory->GetFullRealPath(path);
    687 
    688  size_t charSize = sizeof(nsString::char_type);
    689  return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DIRECTORY, path.Length()) &&
    690         JS_WriteBytes(aWriter, path.get(), path.Length() * charSize);
    691 }
    692 
    693 already_AddRefed<Directory> ReadDirectoryInternal(
    694    JSStructuredCloneReader* aReader, uint32_t aPathLength,
    695    StructuredCloneHolder* aHolder) {
    696  MOZ_ASSERT(aReader);
    697  MOZ_ASSERT(aHolder);
    698 
    699  nsAutoString path;
    700  if (NS_WARN_IF(!path.SetLength(aPathLength, fallible))) {
    701    return nullptr;
    702  }
    703  size_t charSize = sizeof(nsString::char_type);
    704  if (!JS_ReadBytes(aReader, (void*)path.BeginWriting(),
    705                    aPathLength * charSize)) {
    706    return nullptr;
    707  }
    708 
    709  nsCOMPtr<nsIFile> file;
    710  nsresult rv = NS_NewLocalFile(path, getter_AddRefs(file));
    711  if (NS_WARN_IF(NS_FAILED(rv))) {
    712    return nullptr;
    713  }
    714 
    715  RefPtr<Directory> directory =
    716      Directory::Create(aHolder->GlobalDuringRead(), file);
    717  return directory.forget();
    718 }
    719 
    720 JSObject* ReadDirectory(JSContext* aCx, JSStructuredCloneReader* aReader,
    721                        uint32_t aPathLength, StructuredCloneHolder* aHolder) {
    722  MOZ_ASSERT(aCx);
    723  MOZ_ASSERT(aReader);
    724  MOZ_ASSERT(aHolder);
    725 
    726  // RefPtr<Directory> needs to go out of scope before toObject() is
    727  // called because the static analysis thinks dereferencing XPCOM objects
    728  // can GC (because in some cases it can!), and a return statement with a
    729  // JSObject* type means that JSObject* is on the stack as a raw pointer
    730  // while destructors are running.
    731  JS::Rooted<JS::Value> val(aCx);
    732  {
    733    RefPtr<Directory> directory =
    734        ReadDirectoryInternal(aReader, aPathLength, aHolder);
    735    if (!directory) {
    736      return nullptr;
    737    }
    738 
    739    if (!ToJSValue(aCx, directory, &val)) {
    740      return nullptr;
    741    }
    742  }
    743 
    744  return &val.toObject();
    745 }
    746 
    747 // Read the WriteFileList for the format.
    748 JSObject* ReadFileList(JSContext* aCx, JSStructuredCloneReader* aReader,
    749                       uint32_t aCount, StructuredCloneHolder* aHolder) {
    750  MOZ_ASSERT(aCx);
    751  MOZ_ASSERT(aReader);
    752 
    753  JS::Rooted<JS::Value> val(aCx);
    754  {
    755    RefPtr<FileList> fileList = new FileList(aHolder->GlobalDuringRead());
    756 
    757    uint32_t zero, index;
    758    // |index| is the index of the first blobImpl.
    759    if (!JS_ReadUint32Pair(aReader, &zero, &index) || zero != 0) {
    760      return nullptr;
    761    }
    762 
    763    // |aCount| is the number of BlobImpls to use from the |index|.
    764    for (uint32_t i = 0; i < aCount; ++i) {
    765      uint32_t pos = index + i;
    766 #ifdef FUZZING
    767      if (pos >= aHolder->BlobImpls().Length()) {
    768        return nullptr;
    769      }
    770 #endif
    771      MOZ_ASSERT(pos < aHolder->BlobImpls().Length());
    772 
    773      RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[pos];
    774      MOZ_ASSERT(blobImpl->IsFile());
    775 
    776      RefPtr<File> file = File::Create(aHolder->GlobalDuringRead(), blobImpl);
    777      if (NS_WARN_IF(!file)) {
    778        return nullptr;
    779      }
    780 
    781      if (!fileList->Append(file)) {
    782        return nullptr;
    783      }
    784    }
    785 
    786    if (!ToJSValue(aCx, fileList, &val)) {
    787      return nullptr;
    788    }
    789  }
    790 
    791  return &val.toObject();
    792 }
    793 
    794 // The format of the FileList serialization is:
    795 // - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList
    796 // - pair of ints: 0, The offset of the BlobImpl array
    797 bool WriteFileList(JSStructuredCloneWriter* aWriter, FileList* aFileList,
    798                   StructuredCloneHolder* aHolder) {
    799  MOZ_ASSERT(aWriter);
    800  MOZ_ASSERT(aFileList);
    801  MOZ_ASSERT(aHolder);
    802 
    803  // A FileList is serialized writing the X number of elements and the offset
    804  // from mBlobImplArray. The Read will take X elements from mBlobImplArray
    805  // starting from the offset.
    806  if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, aFileList->Length()) ||
    807      !JS_WriteUint32Pair(aWriter, 0, aHolder->BlobImpls().Length())) {
    808    return false;
    809  }
    810 
    811  nsTArray<RefPtr<BlobImpl>> blobImpls;
    812 
    813  for (uint32_t i = 0; i < aFileList->Length(); ++i) {
    814    RefPtr<BlobImpl> blobImpl = aFileList->Item(i)->Impl();
    815    blobImpls.AppendElement(blobImpl);
    816  }
    817 
    818  aHolder->BlobImpls().AppendElements(blobImpls);
    819  return true;
    820 }
    821 
    822 // Read the WriteFormData for the format.
    823 JSObject* ReadFormData(JSContext* aCx, JSStructuredCloneReader* aReader,
    824                       uint32_t aCount, StructuredCloneHolder* aHolder) {
    825  MOZ_ASSERT(aCx);
    826  MOZ_ASSERT(aReader);
    827  MOZ_ASSERT(aHolder);
    828 
    829  // See the serialization of the FormData for the format.
    830  JS::Rooted<JS::Value> val(aCx);
    831  {
    832    RefPtr<FormData> formData = new FormData(aHolder->GlobalDuringRead());
    833 
    834    Optional<nsAString> thirdArg;
    835    for (uint32_t i = 0; i < aCount; ++i) {
    836      nsAutoString name;
    837      if (!StructuredCloneHolder::ReadString(aReader, name)) {
    838        return nullptr;
    839      }
    840 
    841      uint32_t tag, indexOrLengthOfString;
    842      if (!JS_ReadUint32Pair(aReader, &tag, &indexOrLengthOfString)) {
    843        return nullptr;
    844      }
    845 
    846      if (tag == SCTAG_DOM_BLOB) {
    847 #ifdef FUZZING
    848        if (indexOrLengthOfString >= aHolder->BlobImpls().Length()) {
    849          return nullptr;
    850        }
    851 #endif
    852        MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length());
    853 
    854        RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[indexOrLengthOfString];
    855 
    856        RefPtr<Blob> blob = Blob::Create(aHolder->GlobalDuringRead(), blobImpl);
    857        if (NS_WARN_IF(!blob)) {
    858          return nullptr;
    859        }
    860 
    861        ErrorResult rv;
    862        formData->Append(name, *blob, thirdArg, rv);
    863        if (NS_WARN_IF(rv.Failed())) {
    864          rv.SuppressException();
    865          return nullptr;
    866        }
    867 
    868      } else if (tag == SCTAG_DOM_DIRECTORY) {
    869        RefPtr<Directory> directory =
    870            ReadDirectoryInternal(aReader, indexOrLengthOfString, aHolder);
    871        if (!directory) {
    872          return nullptr;
    873        }
    874 
    875        formData->Append(name, directory);
    876 
    877      } else {
    878        if (NS_WARN_IF(tag != 0)) {
    879          return nullptr;
    880        }
    881 
    882        nsAutoString value;
    883        if (NS_WARN_IF(!value.SetLength(indexOrLengthOfString, fallible))) {
    884          return nullptr;
    885        }
    886        size_t charSize = sizeof(nsString::char_type);
    887        if (!JS_ReadBytes(aReader, (void*)value.BeginWriting(),
    888                          indexOrLengthOfString * charSize)) {
    889          return nullptr;
    890        }
    891 
    892        ErrorResult rv;
    893        formData->Append(name, value, rv);
    894        if (NS_WARN_IF(rv.Failed())) {
    895          rv.SuppressException();
    896          return nullptr;
    897        }
    898      }
    899    }
    900 
    901    if (!ToJSValue(aCx, formData, &val)) {
    902      return nullptr;
    903    }
    904  }
    905 
    906  return &val.toObject();
    907 }
    908 
    909 // The format of the FormData serialization is:
    910 // - pair of ints: SCTAG_DOM_FORMDATA, Length of the FormData elements
    911 // - for each Element element:
    912 //   - name string
    913 //   - if it's a blob:
    914 //     - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array
    915 //       mBlobImplArray.
    916 //   - if it's a directory (See WriteDirectory):
    917 //     - pair of ints: SCTAG_DOM_DIRECTORY, path length
    918 //     - path as string
    919 //   - else:
    920 //     - pair of ints: 0, string length
    921 //     - value string
    922 bool WriteFormData(JSStructuredCloneWriter* aWriter, FormData* aFormData,
    923                   StructuredCloneHolder* aHolder) {
    924  MOZ_ASSERT(aWriter);
    925  MOZ_ASSERT(aFormData);
    926  MOZ_ASSERT(aHolder);
    927 
    928  if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FORMDATA, aFormData->Length())) {
    929    return false;
    930  }
    931 
    932  auto write = [aWriter, aHolder](
    933                   const nsString& aName,
    934                   const OwningBlobOrDirectoryOrUSVString& aValue) {
    935    if (!StructuredCloneHolder::WriteString(aWriter, aName)) {
    936      return false;
    937    }
    938 
    939    if (aValue.IsBlob()) {
    940      if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
    941                              aHolder->BlobImpls().Length())) {
    942        return false;
    943      }
    944 
    945      RefPtr<BlobImpl> blobImpl = aValue.GetAsBlob()->Impl();
    946 
    947      aHolder->BlobImpls().AppendElement(blobImpl);
    948      return true;
    949    }
    950 
    951    if (aValue.IsDirectory()) {
    952      Directory* directory = aValue.GetAsDirectory();
    953      return WriteDirectory(aWriter, directory);
    954    }
    955 
    956    const size_t charSize = sizeof(nsString::char_type);
    957    return JS_WriteUint32Pair(aWriter, 0, aValue.GetAsUSVString().Length()) &&
    958           JS_WriteBytes(aWriter, aValue.GetAsUSVString().get(),
    959                         aValue.GetAsUSVString().Length() * charSize);
    960  };
    961  return aFormData->ForEach(write);
    962 }
    963 
    964 JSObject* ReadWasmModule(JSContext* aCx, uint32_t aIndex,
    965                         StructuredCloneHolder* aHolder) {
    966  MOZ_ASSERT(aHolder);
    967  MOZ_ASSERT(aHolder->CloneScope() ==
    968             StructuredCloneHolder::StructuredCloneScope::SameProcess);
    969 #ifdef FUZZING
    970  if (aIndex >= aHolder->WasmModules().Length()) {
    971    return nullptr;
    972  }
    973 #endif
    974  MOZ_ASSERT(aIndex < aHolder->WasmModules().Length());
    975 
    976  return aHolder->WasmModules()[aIndex]->createObject(aCx);
    977 }
    978 
    979 bool WriteWasmModule(JSStructuredCloneWriter* aWriter,
    980                     JS::WasmModule* aWasmModule,
    981                     StructuredCloneHolder* aHolder) {
    982  MOZ_ASSERT(aWriter);
    983  MOZ_ASSERT(aWasmModule);
    984  MOZ_ASSERT(aHolder);
    985  MOZ_ASSERT(aHolder->CloneScope() ==
    986             StructuredCloneHolder::StructuredCloneScope::SameProcess);
    987 
    988  // We store the position of the wasmModule in the array as index.
    989  if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_WASM_MODULE,
    990                         aHolder->WasmModules().Length())) {
    991    aHolder->WasmModules().AppendElement(aWasmModule);
    992    return true;
    993  }
    994 
    995  return false;
    996 }
    997 
    998 JSObject* ReadInputStream(JSContext* aCx, uint32_t aIndex,
    999                          StructuredCloneHolder* aHolder) {
   1000  MOZ_ASSERT(aHolder);
   1001 #ifdef FUZZING
   1002  if (aIndex >= aHolder->InputStreams().Length()) {
   1003    return nullptr;
   1004  }
   1005 #endif
   1006  MOZ_ASSERT(aIndex < aHolder->InputStreams().Length());
   1007  JS::Rooted<JS::Value> result(aCx);
   1008  {
   1009    nsCOMPtr<nsIInputStream> inputStream = aHolder->InputStreams()[aIndex];
   1010 
   1011    nsresult rv = nsContentUtils::WrapNative(
   1012        aCx, inputStream, &NS_GET_IID(nsIInputStream), &result);
   1013    if (NS_FAILED(rv)) {
   1014      return nullptr;
   1015    }
   1016  }
   1017 
   1018  return &result.toObject();
   1019 }
   1020 
   1021 bool WriteInputStream(JSStructuredCloneWriter* aWriter,
   1022                      nsIInputStream* aInputStream,
   1023                      StructuredCloneHolder* aHolder) {
   1024  MOZ_ASSERT(aWriter);
   1025  MOZ_ASSERT(aInputStream);
   1026  MOZ_ASSERT(aHolder);
   1027 
   1028  // We store the position of the inputStream in the array as index.
   1029  if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_INPUTSTREAM,
   1030                         aHolder->InputStreams().Length())) {
   1031    aHolder->InputStreams().AppendElement(aInputStream);
   1032    return true;
   1033  }
   1034 
   1035  return false;
   1036 }
   1037 
   1038 }  // anonymous namespace
   1039 
   1040 static const uint16_t sWindowOrWorker =
   1041    GlobalNames::DedicatedWorkerGlobalScope |
   1042    GlobalNames::ServiceWorkerGlobalScope |
   1043    GlobalNames::SharedWorkerGlobalScope | GlobalNames::Window |
   1044    GlobalNames::WorkerDebuggerGlobalScope;
   1045 
   1046 JSObject* StructuredCloneHolder::CustomReadHandler(
   1047    JSContext* aCx, JSStructuredCloneReader* aReader,
   1048    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag,
   1049    uint32_t aIndex) {
   1050  MOZ_ASSERT(mSupportsCloning);
   1051 
   1052  if (aTag == SCTAG_DOM_BLOB) {
   1053    if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) {
   1054      return nullptr;
   1055    }
   1056    return ReadBlob(aCx, aIndex, this);
   1057  }
   1058 
   1059  if (aTag == SCTAG_DOM_DIRECTORY) {
   1060    if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) {
   1061      return nullptr;
   1062    }
   1063    return ReadDirectory(aCx, aReader, aIndex, this);
   1064  }
   1065 
   1066  if (aTag == SCTAG_DOM_FILELIST) {
   1067    if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) {
   1068      return nullptr;
   1069    }
   1070    return ReadFileList(aCx, aReader, aIndex, this);
   1071  }
   1072 
   1073  if (aTag == SCTAG_DOM_FORMDATA) {
   1074    if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) {
   1075      return nullptr;
   1076    }
   1077    return ReadFormData(aCx, aReader, aIndex, this);
   1078  }
   1079 
   1080  if (aTag == SCTAG_DOM_IMAGEBITMAP &&
   1081      CloneScope() == StructuredCloneScope::SameProcess) {
   1082    if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) {
   1083      return nullptr;
   1084    }
   1085    // Get the current global object.
   1086    // This can be null.
   1087    JS::Rooted<JSObject*> result(aCx);
   1088    {
   1089      // aIndex is the index of the cloned image.
   1090      result = ImageBitmap::ReadStructuredClone(aCx, aReader, mGlobal,
   1091                                                GetSurfaces(), aIndex);
   1092    }
   1093    return result;
   1094  }
   1095 
   1096  if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) {
   1097    return StructuredCloneBlob::ReadStructuredClone(aCx, aReader, this);
   1098  }
   1099 
   1100  if (aTag == SCTAG_DOM_WASM_MODULE &&
   1101      CloneScope() == StructuredCloneScope::SameProcess &&
   1102      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1103    return ReadWasmModule(aCx, aIndex, this);
   1104  }
   1105 
   1106  if (aTag == SCTAG_DOM_INPUTSTREAM) {
   1107    return ReadInputStream(aCx, aIndex, this);
   1108  }
   1109 
   1110  if (aTag == SCTAG_DOM_BROWSING_CONTEXT) {
   1111    if (!CheckExposedGlobals(aCx, mGlobal, GlobalNames::Window)) {
   1112      return nullptr;
   1113    }
   1114    return BrowsingContext::ReadStructuredClone(aCx, aReader, this);
   1115  }
   1116 
   1117  if (aTag == SCTAG_DOM_CLONED_ERROR_OBJECT) {
   1118    if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) {
   1119      return nullptr;
   1120    }
   1121    return ClonedErrorHolder::ReadStructuredClone(aCx, aReader, this);
   1122  }
   1123 
   1124  if (VideoFrame::PrefEnabled(aCx) && aTag == SCTAG_DOM_VIDEOFRAME &&
   1125      CloneScope() == StructuredCloneScope::SameProcess &&
   1126      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1127    JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject());
   1128    if (VideoFrame_Binding::ConstructorEnabled(aCx, global)) {
   1129      return VideoFrame::ReadStructuredClone(aCx, mGlobal, aReader,
   1130                                             VideoFrames()[aIndex]);
   1131    }
   1132  }
   1133 
   1134  if (StaticPrefs::dom_media_webcodecs_enabled() &&
   1135      aTag == SCTAG_DOM_ENCODEDVIDEOCHUNK &&
   1136      CloneScope() == StructuredCloneScope::SameProcess &&
   1137      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1138    JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject());
   1139    if (EncodedVideoChunk_Binding::ConstructorEnabled(aCx, global)) {
   1140      return EncodedVideoChunk::ReadStructuredClone(
   1141          aCx, mGlobal, aReader, EncodedVideoChunks()[aIndex]);
   1142    }
   1143  }
   1144 
   1145  if (StaticPrefs::dom_media_webcodecs_enabled() &&
   1146      aTag == SCTAG_DOM_AUDIODATA &&
   1147      CloneScope() == StructuredCloneScope::SameProcess &&
   1148      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1149    JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject());
   1150    if (AudioData_Binding::ConstructorEnabled(aCx, global)) {
   1151      return AudioData::ReadStructuredClone(aCx, mGlobal, aReader,
   1152                                            AudioData()[aIndex]);
   1153    }
   1154  }
   1155 
   1156  if (StaticPrefs::dom_media_webcodecs_enabled() &&
   1157      aTag == SCTAG_DOM_ENCODEDAUDIOCHUNK &&
   1158      CloneScope() == StructuredCloneScope::SameProcess &&
   1159      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1160    JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject());
   1161    if (EncodedAudioChunk_Binding::ConstructorEnabled(aCx, global)) {
   1162      return EncodedAudioChunk::ReadStructuredClone(
   1163          aCx, mGlobal, aReader, EncodedAudioChunks()[aIndex]);
   1164    }
   1165  }
   1166 
   1167 #ifdef MOZ_WEBRTC
   1168  if (StaticPrefs::media_peerconnection_enabled() &&
   1169      aTag == SCTAG_DOM_RTCENCODEDVIDEOFRAME &&
   1170      CloneScope() == StructuredCloneScope::SameProcess &&
   1171      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1172    JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject());
   1173    if (RTCEncodedVideoFrame_Binding::ConstructorEnabled(aCx, global)) {
   1174      return RTCEncodedVideoFrame::ReadStructuredClone(
   1175          aCx, mGlobal, aReader, RtcEncodedVideoFrames()[aIndex]);
   1176    }
   1177  }
   1178 
   1179  if (StaticPrefs::media_peerconnection_enabled() &&
   1180      aTag == SCTAG_DOM_RTCENCODEDAUDIOFRAME &&
   1181      CloneScope() == StructuredCloneScope::SameProcess &&
   1182      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1183    JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject());
   1184    if (RTCEncodedAudioFrame_Binding::ConstructorEnabled(aCx, global)) {
   1185      return RTCEncodedAudioFrame::ReadStructuredClone(
   1186          aCx, mGlobal, aReader, RtcEncodedAudioFrames()[aIndex]);
   1187    }
   1188  }
   1189 #endif
   1190 
   1191  return ReadFullySerializableObjects(aCx, aReader, aTag, false);
   1192 }
   1193 
   1194 bool StructuredCloneHolder::CustomWriteHandler(
   1195    JSContext* aCx, JSStructuredCloneWriter* aWriter,
   1196    JS::Handle<JSObject*> aObj, bool* aSameProcessScopeRequired) {
   1197  if (!mSupportsCloning) {
   1198    return false;
   1199  }
   1200 
   1201  JS::Rooted<JSObject*> obj(aCx, aObj);
   1202 
   1203  // See if this is a File/Blob object.
   1204  {
   1205    Blob* blob = nullptr;
   1206    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
   1207      return WriteBlob(aWriter, blob, this);
   1208    }
   1209  }
   1210 
   1211  // See if this is a Directory object.
   1212  {
   1213    Directory* directory = nullptr;
   1214    if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, &obj, directory))) {
   1215      return WriteDirectory(aWriter, directory);
   1216    }
   1217  }
   1218 
   1219  // See if this is a FileList object.
   1220  {
   1221    FileList* fileList = nullptr;
   1222    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, &obj, fileList))) {
   1223      return WriteFileList(aWriter, fileList, this);
   1224    }
   1225  }
   1226 
   1227  // See if this is a FormData object.
   1228  {
   1229    FormData* formData = nullptr;
   1230    if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, &obj, formData))) {
   1231      return WriteFormData(aWriter, formData, this);
   1232    }
   1233  }
   1234 
   1235  // See if this is an ImageBitmap object.
   1236  {
   1237    ImageBitmap* imageBitmap = nullptr;
   1238    if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, &obj, imageBitmap))) {
   1239      SameProcessScopeRequired(aSameProcessScopeRequired);
   1240 
   1241      if (CloneScope() == StructuredCloneScope::SameProcess) {
   1242        ErrorResult rv;
   1243        ImageBitmap::WriteStructuredClone(aWriter, GetSurfaces(), imageBitmap,
   1244                                          rv);
   1245        return !rv.MaybeSetPendingException(aCx);
   1246      }
   1247      return false;
   1248    }
   1249  }
   1250 
   1251  // See if this is a StructuredCloneBlob object.
   1252  {
   1253    StructuredCloneBlob* holder = nullptr;
   1254    if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder, &obj, holder))) {
   1255      return holder->WriteStructuredClone(aCx, aWriter, this);
   1256    }
   1257  }
   1258 
   1259  // See if this is a BrowsingContext object.
   1260  {
   1261    BrowsingContext* holder = nullptr;
   1262    if (NS_SUCCEEDED(UNWRAP_OBJECT(BrowsingContext, &obj, holder))) {
   1263      return holder->WriteStructuredClone(aCx, aWriter, this);
   1264    }
   1265  }
   1266 
   1267  // See if this is a ClonedErrorHolder object.
   1268  {
   1269    ClonedErrorHolder* holder = nullptr;
   1270    if (NS_SUCCEEDED(UNWRAP_OBJECT(ClonedErrorHolder, &obj, holder))) {
   1271      return holder->WriteStructuredClone(aCx, aWriter, this);
   1272    }
   1273  }
   1274 
   1275  // See if this is a WasmModule.
   1276  if (JS::IsWasmModuleObject(obj)) {
   1277    SameProcessScopeRequired(aSameProcessScopeRequired);
   1278    if (CloneScope() == StructuredCloneScope::SameProcess) {
   1279      RefPtr<JS::WasmModule> module = JS::GetWasmModule(obj);
   1280      MOZ_ASSERT(module);
   1281 
   1282      return WriteWasmModule(aWriter, module, this);
   1283    }
   1284    return false;
   1285  }
   1286 
   1287  // See if this is a VideoFrame object.
   1288  if (VideoFrame::PrefEnabled(aCx)) {
   1289    VideoFrame* videoFrame = nullptr;
   1290    if (NS_SUCCEEDED(UNWRAP_OBJECT(VideoFrame, &obj, videoFrame))) {
   1291      SameProcessScopeRequired(aSameProcessScopeRequired);
   1292      return CloneScope() == StructuredCloneScope::SameProcess
   1293                 ? videoFrame->WriteStructuredClone(aWriter, this)
   1294                 : false;
   1295    }
   1296  }
   1297 
   1298  // See if this is a EncodedVideoChunk object.
   1299  if (StaticPrefs::dom_media_webcodecs_enabled()) {
   1300    EncodedVideoChunk* encodedVideoChunk = nullptr;
   1301    if (NS_SUCCEEDED(
   1302            UNWRAP_OBJECT(EncodedVideoChunk, &obj, encodedVideoChunk))) {
   1303      SameProcessScopeRequired(aSameProcessScopeRequired);
   1304      return CloneScope() == StructuredCloneScope::SameProcess
   1305                 ? encodedVideoChunk->WriteStructuredClone(aWriter, this)
   1306                 : false;
   1307    }
   1308  }
   1309 
   1310  // See if this is an AudioData object.
   1311  if (StaticPrefs::dom_media_webcodecs_enabled()) {
   1312    mozilla::dom::AudioData* audioData = nullptr;
   1313    if (NS_SUCCEEDED(UNWRAP_OBJECT(AudioData, &obj, audioData))) {
   1314      SameProcessScopeRequired(aSameProcessScopeRequired);
   1315      return CloneScope() == StructuredCloneScope::SameProcess
   1316                 ? audioData->WriteStructuredClone(aWriter, this)
   1317                 : false;
   1318    }
   1319  }
   1320 
   1321  // See if this is a EncodedAudioChunk object.
   1322  if (StaticPrefs::dom_media_webcodecs_enabled()) {
   1323    EncodedAudioChunk* encodedAudioChunk = nullptr;
   1324    if (NS_SUCCEEDED(
   1325            UNWRAP_OBJECT(EncodedAudioChunk, &obj, encodedAudioChunk))) {
   1326      SameProcessScopeRequired(aSameProcessScopeRequired);
   1327      return CloneScope() == StructuredCloneScope::SameProcess
   1328                 ? encodedAudioChunk->WriteStructuredClone(aWriter, this)
   1329                 : false;
   1330    }
   1331  }
   1332 
   1333 #ifdef MOZ_WEBRTC
   1334  // See if this is an RTCEncodedVideoFrame object.
   1335  if (StaticPrefs::media_peerconnection_enabled()) {
   1336    RTCEncodedVideoFrame* rtcFrame = nullptr;
   1337    if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCEncodedVideoFrame, &obj, rtcFrame))) {
   1338      SameProcessScopeRequired(aSameProcessScopeRequired);
   1339      return CloneScope() == StructuredCloneScope::SameProcess
   1340                 ? rtcFrame->WriteStructuredClone(aWriter, this)
   1341                 : false;
   1342    }
   1343  }
   1344 
   1345  // See if this is an RTCEncodedAudioFrame object.
   1346  if (StaticPrefs::media_peerconnection_enabled()) {
   1347    RTCEncodedAudioFrame* rtcFrame = nullptr;
   1348    if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCEncodedAudioFrame, &obj, rtcFrame))) {
   1349      SameProcessScopeRequired(aSameProcessScopeRequired);
   1350      return CloneScope() == StructuredCloneScope::SameProcess
   1351                 ? rtcFrame->WriteStructuredClone(aWriter, this)
   1352                 : false;
   1353    }
   1354  }
   1355 #endif
   1356  {
   1357    // We only care about streams, so ReflectorToISupportsStatic is fine.
   1358    nsCOMPtr<nsISupports> base = xpc::ReflectorToISupportsStatic(aObj);
   1359    nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(base);
   1360    if (inputStream) {
   1361      return WriteInputStream(aWriter, inputStream, this);
   1362    }
   1363  }
   1364 
   1365  return WriteFullySerializableObjects(aCx, aWriter, aObj);
   1366 }
   1367 
   1368 already_AddRefed<MessagePort> StructuredCloneHolder::ReceiveMessagePort(
   1369    uint64_t aIndex) {
   1370  if (NS_WARN_IF(aIndex >= mPortIdentifiers.Length())) {
   1371    return nullptr;
   1372  }
   1373  UniqueMessagePortId portId(mPortIdentifiers[aIndex]);
   1374 
   1375  ErrorResult rv;
   1376  RefPtr<MessagePort> port = MessagePort::Create(mGlobal, portId, rv);
   1377  if (NS_WARN_IF(rv.Failed())) {
   1378    rv.SuppressException();
   1379    return nullptr;
   1380  }
   1381 
   1382  return port.forget();
   1383 }
   1384 
   1385 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
   1386 MOZ_CAN_RUN_SCRIPT_BOUNDARY bool
   1387 StructuredCloneHolder::CustomReadTransferHandler(
   1388    JSContext* aCx, JSStructuredCloneReader* aReader,
   1389    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, void* aContent,
   1390    uint64_t aExtraData, JS::MutableHandle<JSObject*> aReturnObject) {
   1391  MOZ_ASSERT(mSupportsTransferring);
   1392 
   1393  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
   1394    if (!CheckExposedGlobals(
   1395            aCx, mGlobal,
   1396            sWindowOrWorker | GlobalNames::AudioWorkletGlobalScope)) {
   1397      return false;
   1398    }
   1399 #ifdef FUZZING
   1400    if (aExtraData >= mPortIdentifiers.Length()) {
   1401      return false;
   1402    }
   1403 #endif
   1404    RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
   1405    if (!port) {
   1406      return false;
   1407    }
   1408    mTransferredPorts.AppendElement(port);
   1409 
   1410    JS::Rooted<JS::Value> value(aCx);
   1411    if (!GetOrCreateDOMReflector(aCx, port, &value)) {
   1412      JS_ClearPendingException(aCx);
   1413      return false;
   1414    }
   1415 
   1416    aReturnObject.set(&value.toObject());
   1417    return true;
   1418  }
   1419 
   1420  if (aTag == SCTAG_DOM_CANVAS &&
   1421      CloneScope() == StructuredCloneScope::SameProcess) {
   1422    if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) {
   1423      return false;
   1424    }
   1425    MOZ_ASSERT(aContent);
   1426    OffscreenCanvasCloneData* data =
   1427        static_cast<OffscreenCanvasCloneData*>(aContent);
   1428    RefPtr<OffscreenCanvas> canvas =
   1429        OffscreenCanvas::CreateFromCloneData(mGlobal, data);
   1430 
   1431    JS::Rooted<JS::Value> value(aCx);
   1432    if (!GetOrCreateDOMReflector(aCx, canvas, &value)) {
   1433      JS_ClearPendingException(aCx);
   1434      return false;
   1435    }
   1436 
   1437    delete data;
   1438    aReturnObject.set(&value.toObject());
   1439    return true;
   1440  }
   1441 
   1442  if (aTag == SCTAG_DOM_IMAGEBITMAP &&
   1443      CloneScope() == StructuredCloneScope::SameProcess) {
   1444    if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) {
   1445      return false;
   1446    }
   1447    MOZ_ASSERT(aContent);
   1448    ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent);
   1449    RefPtr<ImageBitmap> bitmap =
   1450        ImageBitmap::CreateFromCloneData(mGlobal, data);
   1451 
   1452    JS::Rooted<JS::Value> value(aCx);
   1453    if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) {
   1454      JS_ClearPendingException(aCx);
   1455      return false;
   1456    }
   1457 
   1458    delete data;
   1459    aReturnObject.set(&value.toObject());
   1460    return true;
   1461  }
   1462 
   1463  if (aTag == SCTAG_DOM_READABLESTREAM) {
   1464 #ifdef FUZZING
   1465    if (aExtraData >= mPortIdentifiers.Length()) {
   1466      return false;
   1467    }
   1468 #endif
   1469    RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
   1470    if (!port) {
   1471      return false;
   1472    }
   1473    nsCOMPtr<nsIGlobalObject> global = mGlobal;
   1474    return ReadableStream::ReceiveTransfer(aCx, global, *port, aReturnObject);
   1475  }
   1476 
   1477  if (aTag == SCTAG_DOM_WRITABLESTREAM) {
   1478 #ifdef FUZZING
   1479    if (aExtraData >= mPortIdentifiers.Length()) {
   1480      return false;
   1481    }
   1482 #endif
   1483    RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
   1484    if (!port) {
   1485      return false;
   1486    }
   1487    nsCOMPtr<nsIGlobalObject> global = mGlobal;
   1488    return WritableStream::ReceiveTransfer(aCx, global, *port, aReturnObject);
   1489  }
   1490 
   1491  if (aTag == SCTAG_DOM_TRANSFORMSTREAM) {
   1492 #ifdef FUZZING
   1493    if (aExtraData + 1 >= mPortIdentifiers.Length()) {
   1494      return false;
   1495    }
   1496 #endif
   1497    RefPtr<MessagePort> port1 = ReceiveMessagePort(aExtraData);
   1498    RefPtr<MessagePort> port2 = ReceiveMessagePort(aExtraData + 1);
   1499    if (!port1 || !port2) {
   1500      return false;
   1501    }
   1502    nsCOMPtr<nsIGlobalObject> global = mGlobal;
   1503    return TransformStream::ReceiveTransfer(aCx, global, *port1, *port2,
   1504                                            aReturnObject);
   1505  }
   1506 
   1507  if (VideoFrame::PrefEnabled(aCx) && aTag == SCTAG_DOM_VIDEOFRAME &&
   1508      CloneScope() == StructuredCloneScope::SameProcess &&
   1509      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1510    MOZ_ASSERT(aContent);
   1511 
   1512    JS::Rooted<JSObject*> globalObj(aCx, mGlobal->GetGlobalJSObject());
   1513    // aContent will be released in CustomFreeTransferHandler.
   1514    if (!VideoFrame_Binding::ConstructorEnabled(aCx, globalObj)) {
   1515      return false;
   1516    }
   1517 
   1518    VideoFrame::TransferredData* data =
   1519        static_cast<VideoFrame::TransferredData*>(aContent);
   1520    nsCOMPtr<nsIGlobalObject> global = mGlobal;
   1521    RefPtr<VideoFrame> frame = VideoFrame::FromTransferred(global.get(), data);
   1522    // aContent will be released in CustomFreeTransferHandler if frame is null.
   1523    if (!frame) {
   1524      return false;
   1525    }
   1526 
   1527    JS::Rooted<JS::Value> value(aCx);
   1528    if (!GetOrCreateDOMReflector(aCx, frame, &value)) {
   1529      JS_ClearPendingException(aCx);
   1530      return false;
   1531    }
   1532    delete data;
   1533    aContent = nullptr;
   1534    aReturnObject.set(&value.toObject());
   1535    return true;
   1536  }
   1537 
   1538  if (StaticPrefs::dom_media_webcodecs_enabled() &&
   1539      aTag == SCTAG_DOM_AUDIODATA &&
   1540      CloneScope() == StructuredCloneScope::SameProcess &&
   1541      aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) {
   1542    MOZ_ASSERT(aContent);
   1543 
   1544    JS::Rooted<JSObject*> globalObj(aCx, mGlobal->GetGlobalJSObject());
   1545    // aContent will be released in CustomFreeTransferHandler.
   1546    if (!AudioData_Binding::ConstructorEnabled(aCx, globalObj)) {
   1547      return false;
   1548    }
   1549 
   1550    AudioData::TransferredData* data =
   1551        static_cast<AudioData::TransferredData*>(aContent);
   1552    nsCOMPtr<nsIGlobalObject> global = mGlobal;
   1553    RefPtr<mozilla::dom::AudioData> audioData =
   1554        AudioData::FromTransferred(global.get(), data);
   1555    // aContent will be released in CustomFreeTransferHandler if frame is null.
   1556    if (!audioData) {
   1557      return false;
   1558    }
   1559 
   1560    JS::Rooted<JS::Value> value(aCx);
   1561    if (!GetOrCreateDOMReflector(aCx, audioData, &value)) {
   1562      JS_ClearPendingException(aCx);
   1563      return false;
   1564    }
   1565    delete data;
   1566    aContent = nullptr;
   1567    aReturnObject.set(&value.toObject());
   1568    return true;
   1569  }
   1570 
   1571 #ifdef MOZ_WEBRTC
   1572  if (aTag == SCTAG_DOM_RTCDATACHANNEL &&
   1573      CloneScope() == StructuredCloneScope::SameProcess) {
   1574    if (!CheckExposedGlobals(aCx, mGlobal,
   1575                             GlobalNames::DedicatedWorkerGlobalScope)) {
   1576      return false;
   1577    }
   1578    MOZ_ASSERT(aContent);
   1579 
   1580    // This DataHolder was created over in CustomWriteTransferHandler
   1581    RTCDataChannel::DataHolder* dataHolder =
   1582        static_cast<RTCDataChannel::DataHolder*>(aContent);
   1583    aContent = nullptr;
   1584 
   1585    RefPtr<RTCDataChannel> channel = new RTCDataChannel(mGlobal, *dataHolder);
   1586 
   1587    // dataHolder will be released in CustomFreeTransferHandler if we return
   1588    // false. Ordinarily, I would prefer taking ownership in here, but
   1589    // CustomFreeTransferHandler is called in situations other than failure
   1590    // here, and in those situations it *does* need to handle the cleanup.
   1591    if (!channel) {
   1592      // This should only happen on OOM
   1593      return false;
   1594    }
   1595    channel->Init();
   1596 
   1597    JS::Rooted<JS::Value> value(aCx);
   1598    if (!GetOrCreateDOMReflector(aCx, channel, &value)) {
   1599      JS_ClearPendingException(aCx);
   1600      return false;
   1601    }
   1602 
   1603    delete dataHolder;
   1604    aReturnObject.set(&value.toObject());
   1605    return true;
   1606  }
   1607 #endif
   1608 
   1609  return false;
   1610 }
   1611 
   1612 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
   1613 MOZ_CAN_RUN_SCRIPT_BOUNDARY bool
   1614 StructuredCloneHolder::CustomWriteTransferHandler(
   1615    JSContext* aCx, JS::Handle<JSObject*> aObj, uint32_t* aTag,
   1616    JS::TransferableOwnership* aOwnership, void** aContent,
   1617    uint64_t* aExtraData) {
   1618  if (!mSupportsTransferring) {
   1619    return false;
   1620  }
   1621 
   1622  JS::Rooted<JSObject*> obj(aCx, aObj);
   1623 
   1624  {
   1625    MessagePort* port = nullptr;
   1626    nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port);
   1627    if (NS_SUCCEEDED(rv)) {
   1628      if (!port->CanBeCloned()) {
   1629        return false;
   1630      }
   1631 
   1632      UniqueMessagePortId identifier;
   1633      port->CloneAndDisentangle(identifier);
   1634 
   1635      // We use aExtraData to store the index of this new port identifier.
   1636      *aExtraData = mPortIdentifiers.Length();
   1637      mPortIdentifiers.AppendElement(identifier.release());
   1638 
   1639      *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
   1640      *aContent = nullptr;
   1641      *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1642 
   1643      return true;
   1644    }
   1645 
   1646    if (CloneScope() == StructuredCloneScope::SameProcess) {
   1647      OffscreenCanvas* canvas = nullptr;
   1648      rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas);
   1649      if (NS_SUCCEEDED(rv)) {
   1650        MOZ_ASSERT(canvas);
   1651 
   1652        UniquePtr<OffscreenCanvasCloneData> clonedCanvas =
   1653            canvas->ToCloneData(aCx);
   1654        if (!clonedCanvas) {
   1655          return false;
   1656        }
   1657 
   1658        *aExtraData = 0;
   1659        *aTag = SCTAG_DOM_CANVAS;
   1660        *aContent = clonedCanvas.release();
   1661        MOZ_ASSERT(*aContent);
   1662        *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1663 
   1664        return true;
   1665      }
   1666 
   1667      ImageBitmap* bitmap = nullptr;
   1668      rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap);
   1669      if (NS_SUCCEEDED(rv)) {
   1670        MOZ_ASSERT(bitmap);
   1671        MOZ_ASSERT(!bitmap->IsWriteOnly());
   1672 
   1673        *aExtraData = 0;
   1674        *aTag = SCTAG_DOM_IMAGEBITMAP;
   1675 
   1676        UniquePtr<ImageBitmapCloneData> clonedBitmap = bitmap->ToCloneData();
   1677        if (!clonedBitmap) {
   1678          return false;
   1679        }
   1680 
   1681        *aContent = clonedBitmap.release();
   1682        MOZ_ASSERT(*aContent);
   1683        *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1684 
   1685        bitmap->Close();
   1686 
   1687        return true;
   1688      }
   1689 
   1690      if (VideoFrame::PrefEnabled(aCx)) {
   1691        VideoFrame* videoFrame = nullptr;
   1692        rv = UNWRAP_OBJECT(VideoFrame, &obj, videoFrame);
   1693        if (NS_SUCCEEDED(rv)) {
   1694          MOZ_ASSERT(videoFrame);
   1695 
   1696          *aExtraData = 0;
   1697          *aTag = SCTAG_DOM_VIDEOFRAME;
   1698          *aContent = nullptr;
   1699 
   1700          UniquePtr<VideoFrame::TransferredData> data = videoFrame->Transfer();
   1701          if (!data) {
   1702            return false;
   1703          }
   1704          *aContent = data.release();
   1705          MOZ_ASSERT(*aContent);
   1706          *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1707          return true;
   1708        }
   1709      }
   1710      if (StaticPrefs::dom_media_webcodecs_enabled()) {
   1711        mozilla::dom::AudioData* audioData = nullptr;
   1712        rv = UNWRAP_OBJECT(AudioData, &obj, audioData);
   1713        if (NS_SUCCEEDED(rv)) {
   1714          MOZ_ASSERT(audioData);
   1715 
   1716          *aExtraData = 0;
   1717          *aTag = SCTAG_DOM_AUDIODATA;
   1718          *aContent = nullptr;
   1719 
   1720          UniquePtr<AudioData::TransferredData> data = audioData->Transfer();
   1721          if (!data) {
   1722            return false;
   1723          }
   1724          *aContent = data.release();
   1725          MOZ_ASSERT(*aContent);
   1726          *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1727          return true;
   1728        }
   1729      }
   1730 
   1731 #ifdef MOZ_WEBRTC
   1732      {
   1733        mozilla::dom::RTCDataChannel* channel = nullptr;
   1734        rv = UNWRAP_OBJECT(RTCDataChannel, &obj, channel);
   1735        if (NS_SUCCEEDED(rv)) {
   1736          MOZ_ASSERT(channel);
   1737          // We check above that CloneScope() == SameProcess
   1738 
   1739          UniquePtr<RTCDataChannel::DataHolder> dataHolder =
   1740              channel->Transfer();
   1741          if (!dataHolder) {
   1742            // RTCDataChannel.[[IsTransferable]] is false, apparently
   1743            return false;
   1744          }
   1745 
   1746          *aExtraData = 0;
   1747          *aTag = SCTAG_DOM_RTCDATACHANNEL;
   1748 
   1749          // Transfer ownership out (JS::SCTAG_TMO_CUSTOM signals this)
   1750          // This will be processed by CustomReadTransferHandler, or freed by
   1751          // CustomFreeTransferHandler if there's some error.
   1752          *aContent = dataHolder.release();
   1753          *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1754 
   1755          return true;
   1756        }
   1757      }
   1758 #endif
   1759    }
   1760 
   1761    {
   1762      RefPtr<ReadableStream> stream;
   1763      rv = UNWRAP_OBJECT(ReadableStream, &obj, stream);
   1764      if (NS_SUCCEEDED(rv)) {
   1765        MOZ_ASSERT(stream);
   1766 
   1767        *aTag = SCTAG_DOM_READABLESTREAM;
   1768        *aContent = nullptr;
   1769 
   1770        UniqueMessagePortId id;
   1771        if (!stream->Transfer(aCx, id)) {
   1772          return false;
   1773        }
   1774        *aExtraData = mPortIdentifiers.Length();
   1775        mPortIdentifiers.AppendElement(id.release());
   1776        *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1777        return true;
   1778      }
   1779    }
   1780 
   1781    {
   1782      RefPtr<WritableStream> stream;
   1783      rv = UNWRAP_OBJECT(WritableStream, &obj, stream);
   1784      if (NS_SUCCEEDED(rv)) {
   1785        MOZ_ASSERT(stream);
   1786 
   1787        *aTag = SCTAG_DOM_WRITABLESTREAM;
   1788        *aContent = nullptr;
   1789 
   1790        UniqueMessagePortId id;
   1791        if (!stream->Transfer(aCx, id)) {
   1792          return false;
   1793        }
   1794        *aExtraData = mPortIdentifiers.Length();
   1795        mPortIdentifiers.AppendElement(id.release());
   1796        *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1797        return true;
   1798      }
   1799    }
   1800 
   1801    {
   1802      RefPtr<TransformStream> stream;
   1803      rv = UNWRAP_OBJECT(TransformStream, &obj, stream);
   1804      if (NS_SUCCEEDED(rv)) {
   1805        MOZ_ASSERT(stream);
   1806 
   1807        *aTag = SCTAG_DOM_TRANSFORMSTREAM;
   1808        *aContent = nullptr;
   1809 
   1810        UniqueMessagePortId id1;
   1811        UniqueMessagePortId id2;
   1812        if (!stream->Transfer(aCx, id1, id2)) {
   1813          return false;
   1814        }
   1815        *aExtraData = mPortIdentifiers.Length();
   1816        mPortIdentifiers.AppendElement(id1.release());
   1817        mPortIdentifiers.AppendElement(id2.release());
   1818        *aOwnership = JS::SCTAG_TMO_CUSTOM;
   1819        return true;
   1820      }
   1821    }
   1822  }
   1823 
   1824  return false;
   1825 }
   1826 
   1827 void StructuredCloneHolder::CustomFreeTransferHandler(
   1828    uint32_t aTag, JS::TransferableOwnership aOwnership, void* aContent,
   1829    uint64_t aExtraData) {
   1830  MOZ_ASSERT(mSupportsTransferring);
   1831 
   1832  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
   1833    MOZ_ASSERT(!aContent);
   1834 #ifdef FUZZING
   1835    if (aExtraData >= mPortIdentifiers.Length()) {
   1836      return;
   1837    }
   1838 #endif
   1839    MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
   1840    MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
   1841    return;
   1842  }
   1843 
   1844  if (aTag == SCTAG_DOM_CANVAS &&
   1845      CloneScope() == StructuredCloneScope::SameProcess) {
   1846    MOZ_ASSERT(aContent);
   1847    OffscreenCanvasCloneData* data =
   1848        static_cast<OffscreenCanvasCloneData*>(aContent);
   1849    delete data;
   1850    return;
   1851  }
   1852 
   1853  if (aTag == SCTAG_DOM_IMAGEBITMAP &&
   1854      CloneScope() == StructuredCloneScope::SameProcess) {
   1855    MOZ_ASSERT(aContent);
   1856    ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent);
   1857    delete data;
   1858    return;
   1859  }
   1860 
   1861  if (aTag == SCTAG_DOM_READABLESTREAM || aTag == SCTAG_DOM_WRITABLESTREAM) {
   1862    MOZ_ASSERT(!aContent);
   1863 #ifdef FUZZING
   1864    if (aExtraData >= mPortIdentifiers.Length()) {
   1865      return;
   1866    }
   1867 #endif
   1868    MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
   1869    MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
   1870    return;
   1871  }
   1872 
   1873  if (aTag == SCTAG_DOM_TRANSFORMSTREAM) {
   1874    MOZ_ASSERT(!aContent);
   1875 #ifdef FUZZING
   1876    if (aExtraData + 1 >= mPortIdentifiers.Length()) {
   1877      return;
   1878    }
   1879 #endif
   1880    MOZ_ASSERT(aExtraData + 1 < mPortIdentifiers.Length());
   1881    MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
   1882    MessagePort::ForceClose(mPortIdentifiers[aExtraData + 1]);
   1883    return;
   1884  }
   1885 
   1886  if (aTag == SCTAG_DOM_VIDEOFRAME &&
   1887      CloneScope() == StructuredCloneScope::SameProcess) {
   1888    if (aContent) {
   1889      VideoFrame::TransferredData* data =
   1890          static_cast<VideoFrame::TransferredData*>(aContent);
   1891      delete data;
   1892    }
   1893    return;
   1894  }
   1895  if (aTag == SCTAG_DOM_AUDIODATA &&
   1896      CloneScope() == StructuredCloneScope::SameProcess) {
   1897    if (aContent) {
   1898      AudioData::TransferredData* data =
   1899          static_cast<AudioData::TransferredData*>(aContent);
   1900      delete data;
   1901    }
   1902    return;
   1903  }
   1904 #ifdef MOZ_WEBRTC
   1905  if (aTag == SCTAG_DOM_RTCDATACHANNEL &&
   1906      CloneScope() == StructuredCloneScope::SameProcess) {
   1907    if (aContent) {
   1908      RTCDataChannel::DataHolder* dataHolder =
   1909          static_cast<RTCDataChannel::DataHolder*>(aContent);
   1910      delete dataHolder;
   1911    }
   1912    return;
   1913  }
   1914 #endif
   1915 }
   1916 
   1917 bool StructuredCloneHolder::CustomCanTransferHandler(
   1918    JSContext* aCx, JS::Handle<JSObject*> aObj,
   1919    bool* aSameProcessScopeRequired) {
   1920  if (!mSupportsTransferring) {
   1921    return false;
   1922  }
   1923 
   1924  JS::Rooted<JSObject*> obj(aCx, aObj);
   1925 
   1926  {
   1927    MessagePort* port = nullptr;
   1928    nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port);
   1929    if (NS_SUCCEEDED(rv)) {
   1930      return true;
   1931    }
   1932  }
   1933 
   1934  {
   1935    OffscreenCanvas* canvas = nullptr;
   1936    nsresult rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas);
   1937    if (NS_SUCCEEDED(rv)) {
   1938      SameProcessScopeRequired(aSameProcessScopeRequired);
   1939      return CloneScope() == StructuredCloneScope::SameProcess;
   1940    }
   1941  }
   1942 
   1943  {
   1944    ImageBitmap* bitmap = nullptr;
   1945    nsresult rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap);
   1946    if (NS_SUCCEEDED(rv)) {
   1947      if (bitmap->IsWriteOnly()) {
   1948        return false;
   1949      }
   1950 
   1951      SameProcessScopeRequired(aSameProcessScopeRequired);
   1952      return CloneScope() == StructuredCloneScope::SameProcess;
   1953    }
   1954  }
   1955 
   1956  {
   1957    ReadableStream* stream = nullptr;
   1958    nsresult rv = UNWRAP_OBJECT(ReadableStream, &obj, stream);
   1959    if (NS_SUCCEEDED(rv)) {
   1960      // https://streams.spec.whatwg.org/#ref-for-transfer-steps
   1961      // Step 1: If ! IsReadableStreamLocked(value) is true, throw a
   1962      // "DataCloneError" DOMException.
   1963      return !stream->Locked();
   1964    }
   1965  }
   1966 
   1967  {
   1968    WritableStream* stream = nullptr;
   1969    nsresult rv = UNWRAP_OBJECT(WritableStream, &obj, stream);
   1970    if (NS_SUCCEEDED(rv)) {
   1971      // https://streams.spec.whatwg.org/#ref-for-transfer-stepsâ‘ 
   1972      // Step 1: If ! IsWritableStreamLocked(value) is true, throw a
   1973      // "DataCloneError" DOMException.
   1974      return !stream->Locked();
   1975    }
   1976  }
   1977 
   1978  {
   1979    TransformStream* stream = nullptr;
   1980    nsresult rv = UNWRAP_OBJECT(TransformStream, &obj, stream);
   1981    if (NS_SUCCEEDED(rv)) {
   1982      // https://streams.spec.whatwg.org/#ref-for-transfer-steps②
   1983      // Step 3 + 4: If ! Is{Readable,Writable}StreamLocked(value) is true,
   1984      // throw a "DataCloneError" DOMException.
   1985      return !stream->Readable()->Locked() && !stream->Writable()->Locked();
   1986    }
   1987  }
   1988 
   1989  if (VideoFrame::PrefEnabled(aCx)) {
   1990    VideoFrame* videoframe = nullptr;
   1991    nsresult rv = UNWRAP_OBJECT(VideoFrame, &obj, videoframe);
   1992    if (NS_SUCCEEDED(rv)) {
   1993      SameProcessScopeRequired(aSameProcessScopeRequired);
   1994      return CloneScope() == StructuredCloneScope::SameProcess;
   1995    }
   1996  }
   1997 
   1998  if (StaticPrefs::dom_media_webcodecs_enabled()) {
   1999    mozilla::dom::AudioData* audioData = nullptr;
   2000    nsresult rv = UNWRAP_OBJECT(AudioData, &obj, audioData);
   2001    if (NS_SUCCEEDED(rv)) {
   2002      SameProcessScopeRequired(aSameProcessScopeRequired);
   2003      return CloneScope() == StructuredCloneScope::SameProcess;
   2004    }
   2005  }
   2006 
   2007 #ifdef MOZ_WEBRTC
   2008  {
   2009    mozilla::dom::RTCDataChannel* channel = nullptr;
   2010    nsresult rv = UNWRAP_OBJECT(RTCDataChannel, &obj, channel);
   2011    if (NS_SUCCEEDED(rv)) {
   2012      SameProcessScopeRequired(aSameProcessScopeRequired);
   2013      return CloneScope() == StructuredCloneScope::SameProcess;
   2014    }
   2015  }
   2016 #endif
   2017 
   2018  return false;
   2019 }
   2020 
   2021 bool StructuredCloneHolder::TakeTransferredPortsAsSequence(
   2022    Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts) {
   2023  nsTArray<RefPtr<MessagePort>> ports = TakeTransferredPorts();
   2024 
   2025  aPorts.Clear();
   2026  for (uint32_t i = 0, len = ports.Length(); i < len; ++i) {
   2027    if (!aPorts.AppendElement(ports[i].forget(), fallible)) {
   2028      return false;
   2029    }
   2030  }
   2031 
   2032  return true;
   2033 }
   2034 
   2035 void StructuredCloneHolder::SameProcessScopeRequired(
   2036    bool* aSameProcessScopeRequired) {
   2037  MOZ_ASSERT(aSameProcessScopeRequired);
   2038  if (mStructuredCloneScope == StructuredCloneScope::UnknownDestination) {
   2039    mStructuredCloneScope = StructuredCloneScope::SameProcess;
   2040    *aSameProcessScopeRequired = true;
   2041  }
   2042 }
   2043 
   2044 }  // namespace mozilla::dom