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/. */
7 #include "mozilla/dom/StructuredCloneHolder.h"
10 #include "ErrorList.h"
11 #include "MainThreadUtils.h"
12 #include "js/CallArgs.h"
14 #include "js/WasmModule.h"
15 #include "js/Wrapper.h"
17 #include "mozilla/AlreadyAddRefed.h"
18 #include "mozilla/AutoRestore.h"
19 #include "mozilla/ErrorResult.h"
20 #include "mozilla/OwningNonNull.h"
21 #include "mozilla/RefPtr.h"
22 #include "mozilla/ScopeExit.h"
23 #include "mozilla/StaticPrefs_dom.h"
24 #include "mozilla/dom/BindingDeclarations.h"
25 #include "mozilla/dom/BindingUtils.h"
26 #include "mozilla/dom/Blob.h"
27 #include "mozilla/dom/BlobBinding.h"
28 #include "mozilla/dom/BlobImpl.h"
29 #include "mozilla/dom/BrowsingContext.h"
30 #include "mozilla/dom/ClonedErrorHolder.h"
31 #include "mozilla/dom/ClonedErrorHolderBinding.h"
32 #include "mozilla/dom/DirectoryBinding.h"
33 #include "mozilla/dom/DOMJSClass.h"
34 #include "mozilla/dom/DOMTypes.h"
35 #include "mozilla/dom/Directory.h"
36 #include "mozilla/dom/DocGroup.h"
37 #include "mozilla/dom/EncodedAudioChunk.h"
38 #include "mozilla/dom/EncodedAudioChunkBinding.h"
39 #include "mozilla/dom/EncodedVideoChunk.h"
40 #include "mozilla/dom/EncodedVideoChunkBinding.h"
41 #include "mozilla/dom/File.h"
42 #include "mozilla/dom/FileList.h"
43 #include "mozilla/dom/FileListBinding.h"
44 #include "mozilla/dom/FormData.h"
45 #include "mozilla/dom/FormDataBinding.h"
46 #include "mozilla/dom/ImageBitmap.h"
47 #include "mozilla/dom/ImageBitmapBinding.h"
48 #include "mozilla/dom/JSExecutionManager.h"
49 #include "mozilla/dom/MessagePort.h"
50 #include "mozilla/dom/MessagePortBinding.h"
51 #include "mozilla/dom/OffscreenCanvas.h"
52 #include "mozilla/dom/OffscreenCanvasBinding.h"
53 #include "mozilla/dom/ReadableStream.h"
54 #include "mozilla/dom/ReadableStreamBinding.h"
55 #include "mozilla/dom/ScriptSettings.h"
56 #include "mozilla/dom/StructuredCloneBlob.h"
57 #include "mozilla/dom/StructuredCloneHolderBinding.h"
58 #include "mozilla/dom/StructuredCloneTags.h"
59 #include "mozilla/dom/ToJSValue.h"
60 #include "mozilla/dom/TransformStream.h"
61 #include "mozilla/dom/TransformStreamBinding.h"
62 #include "mozilla/dom/VideoFrame.h"
63 #include "mozilla/dom/AudioData.h"
64 #include "mozilla/dom/VideoFrameBinding.h"
65 #include "mozilla/dom/WebIDLSerializable.h"
66 #include "mozilla/dom/WritableStream.h"
67 #include "mozilla/dom/WritableStreamBinding.h"
68 #include "mozilla/dom/WorkerCommon.h"
69 #include "mozilla/dom/WorkerPrivate.h"
70 #include "mozilla/fallible.h"
71 #include "mozilla/gfx/2D.h"
72 #include "nsContentUtils.h"
76 #include "nsIEventTarget.h"
78 #include "nsIGlobalObject.h"
79 #include "nsIInputStream.h"
80 #include "nsIPrincipal.h"
81 #include "nsISupports.h"
82 #include "nsJSPrincipals.h"
83 #include "nsPIDOMWindow.h"
85 #include "nsThreadUtils.h"
87 #include "xpcpublic.h"
89 using namespace mozilla::ipc
;
91 namespace mozilla::dom
{
95 JSObject
* StructuredCloneCallbacksRead(
96 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
97 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
, uint32_t aIndex
,
99 StructuredCloneHolderBase
* holder
=
100 static_cast<StructuredCloneHolderBase
*>(aClosure
);
102 return holder
->CustomReadHandler(aCx
, aReader
, aCloneDataPolicy
, aTag
,
106 bool StructuredCloneCallbacksWrite(JSContext
* aCx
,
107 JSStructuredCloneWriter
* aWriter
,
108 JS::Handle
<JSObject
*> aObj
,
109 bool* aSameProcessScopeRequired
,
111 StructuredCloneHolderBase
* holder
=
112 static_cast<StructuredCloneHolderBase
*>(aClosure
);
114 return holder
->CustomWriteHandler(aCx
, aWriter
, aObj
,
115 aSameProcessScopeRequired
);
118 bool StructuredCloneCallbacksReadTransfer(
119 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
120 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
, void* aContent
,
121 uint64_t aExtraData
, void* aClosure
,
122 JS::MutableHandle
<JSObject
*> aReturnObject
) {
123 StructuredCloneHolderBase
* holder
=
124 static_cast<StructuredCloneHolderBase
*>(aClosure
);
126 return holder
->CustomReadTransferHandler(aCx
, aReader
, aCloneDataPolicy
, aTag
,
127 aContent
, aExtraData
, aReturnObject
);
130 bool StructuredCloneCallbacksWriteTransfer(
131 JSContext
* aCx
, JS::Handle
<JSObject
*> aObj
, void* aClosure
,
133 uint32_t* aTag
, JS::TransferableOwnership
* aOwnership
, void** aContent
,
134 uint64_t* aExtraData
) {
135 StructuredCloneHolderBase
* holder
=
136 static_cast<StructuredCloneHolderBase
*>(aClosure
);
138 return holder
->CustomWriteTransferHandler(aCx
, aObj
, aTag
, aOwnership
,
139 aContent
, aExtraData
);
142 void StructuredCloneCallbacksFreeTransfer(uint32_t aTag
,
143 JS::TransferableOwnership aOwnership
,
144 void* aContent
, uint64_t aExtraData
,
146 StructuredCloneHolderBase
* holder
=
147 static_cast<StructuredCloneHolderBase
*>(aClosure
);
149 return holder
->CustomFreeTransferHandler(aTag
, aOwnership
, aContent
,
153 bool StructuredCloneCallbacksCanTransfer(JSContext
* aCx
,
154 JS::Handle
<JSObject
*> aObject
,
155 bool* aSameProcessScopeRequired
,
157 StructuredCloneHolderBase
* holder
=
158 static_cast<StructuredCloneHolderBase
*>(aClosure
);
160 return holder
->CustomCanTransferHandler(aCx
, aObject
,
161 aSameProcessScopeRequired
);
164 bool StructuredCloneCallbacksSharedArrayBuffer(JSContext
* cx
, bool aReceiving
,
166 if (!StaticPrefs::dom_workers_serialized_sab_access()) {
170 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
173 workerPrivate
->SetExecutionManager(
174 JSExecutionManager::GetSABSerializationManager());
175 } else if (NS_IsMainThread()) {
176 nsIGlobalObject
* global
= GetCurrentGlobal();
178 nsPIDOMWindowInner
* innerWindow
= nullptr;
180 innerWindow
= global
->GetAsInnerWindow();
183 DocGroup
* docGroup
= nullptr;
185 docGroup
= innerWindow
->GetDocGroup();
189 docGroup
->SetExecutionManager(
190 JSExecutionManager::GetSABSerializationManager());
196 void StructuredCloneCallbacksError(JSContext
* aCx
, uint32_t aErrorId
,
197 void* aClosure
, const char* aErrorMessage
) {
198 NS_WARNING("Failed to clone data.");
199 StructuredCloneHolderBase
* holder
=
200 static_cast<StructuredCloneHolderBase
*>(aClosure
);
202 return holder
->SetErrorMessage(aErrorMessage
);
205 void AssertTagValues() {
206 static_assert(SCTAG_DOM_IMAGEDATA
== 0xffff8007 &&
207 SCTAG_DOM_DOMPOINT
== 0xffff8008 &&
208 SCTAG_DOM_DOMPOINTREADONLY
== 0xffff8009 &&
209 SCTAG_DOM_CRYPTOKEY
== 0xffff800a &&
210 SCTAG_DOM_NULL_PRINCIPAL
== 0xffff800b &&
211 SCTAG_DOM_SYSTEM_PRINCIPAL
== 0xffff800c &&
212 SCTAG_DOM_CONTENT_PRINCIPAL
== 0xffff800d &&
213 SCTAG_DOM_DOMQUAD
== 0xffff800e &&
214 SCTAG_DOM_RTCCERTIFICATE
== 0xffff800f &&
215 SCTAG_DOM_DOMRECT
== 0xffff8010 &&
216 SCTAG_DOM_DOMRECTREADONLY
== 0xffff8011 &&
217 SCTAG_DOM_EXPANDED_PRINCIPAL
== 0xffff8012 &&
218 SCTAG_DOM_DOMMATRIX
== 0xffff8013 &&
219 SCTAG_DOM_URLSEARCHPARAMS
== 0xffff8014 &&
220 SCTAG_DOM_DOMMATRIXREADONLY
== 0xffff8015 &&
221 SCTAG_DOM_STRUCTUREDCLONETESTER
== 0xffff8018 &&
222 SCTAG_DOM_FILESYSTEMHANDLE
== 0xffff8019 &&
223 SCTAG_DOM_FILESYSTEMFILEHANDLE
== 0xffff801a &&
224 SCTAG_DOM_FILESYSTEMDIRECTORYHANDLE
== 0xffff801b,
225 "Something has changed the sctag values. This is wrong!");
228 } // anonymous namespace
230 const JSStructuredCloneCallbacks
StructuredCloneHolder::sCallbacks
= {
231 StructuredCloneCallbacksRead
,
232 StructuredCloneCallbacksWrite
,
233 StructuredCloneCallbacksError
,
234 StructuredCloneCallbacksReadTransfer
,
235 StructuredCloneCallbacksWriteTransfer
,
236 StructuredCloneCallbacksFreeTransfer
,
237 StructuredCloneCallbacksCanTransfer
,
238 StructuredCloneCallbacksSharedArrayBuffer
,
241 // StructuredCloneHolderBase class
243 StructuredCloneHolderBase::StructuredCloneHolderBase(
244 StructuredCloneScope aScope
)
245 : mStructuredCloneScope(aScope
)
253 StructuredCloneHolderBase::~StructuredCloneHolderBase() {
255 MOZ_ASSERT(mClearCalled
);
259 void StructuredCloneHolderBase::Clear() {
267 bool StructuredCloneHolderBase::Write(JSContext
* aCx
,
268 JS::Handle
<JS::Value
> aValue
) {
269 return Write(aCx
, aValue
, JS::UndefinedHandleValue
, JS::CloneDataPolicy());
272 bool StructuredCloneHolderBase::Write(
273 JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
274 JS::Handle
<JS::Value
> aTransfer
,
275 const JS::CloneDataPolicy
& aCloneDataPolicy
) {
276 MOZ_ASSERT(!mBuffer
, "Double Write is not allowed");
277 MOZ_ASSERT(!mClearCalled
, "This method cannot be called after Clear.");
279 mBuffer
= MakeUnique
<JSAutoStructuredCloneBuffer
>(
280 mStructuredCloneScope
, &StructuredCloneHolder::sCallbacks
, this);
282 if (!mBuffer
->write(aCx
, aValue
, aTransfer
, aCloneDataPolicy
,
283 &StructuredCloneHolder::sCallbacks
, this)) {
288 // Let's update our scope to the final one. The new one could be more
289 // restrictive of the current one.
290 MOZ_ASSERT(mStructuredCloneScope
>= mBuffer
->scope());
291 mStructuredCloneScope
= mBuffer
->scope();
295 bool StructuredCloneHolderBase::Read(JSContext
* aCx
,
296 JS::MutableHandle
<JS::Value
> aValue
) {
297 return Read(aCx
, aValue
, JS::CloneDataPolicy());
300 bool StructuredCloneHolderBase::Read(
301 JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aValue
,
302 const JS::CloneDataPolicy
& aCloneDataPolicy
) {
303 MOZ_ASSERT(mBuffer
, "Read() without Write() is not allowed.");
304 MOZ_ASSERT(!mClearCalled
, "This method cannot be called after Clear.");
306 bool ok
= mBuffer
->read(aCx
, aValue
, aCloneDataPolicy
,
307 &StructuredCloneHolder::sCallbacks
, this);
311 bool StructuredCloneHolderBase::CustomReadTransferHandler(
312 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
313 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
, void* aContent
,
314 uint64_t aExtraData
, JS::MutableHandle
<JSObject
*> aReturnObject
) {
315 MOZ_CRASH("Nothing to read.");
319 bool StructuredCloneHolderBase::CustomWriteTransferHandler(
320 JSContext
* aCx
, JS::Handle
<JSObject
*> aObj
, uint32_t* aTag
,
321 JS::TransferableOwnership
* aOwnership
, void** aContent
,
322 uint64_t* aExtraData
) {
323 // No transfers are supported by default.
327 void StructuredCloneHolderBase::CustomFreeTransferHandler(
328 uint32_t aTag
, JS::TransferableOwnership aOwnership
, void* aContent
,
329 uint64_t aExtraData
) {
330 MOZ_CRASH("Nothing to free.");
333 bool StructuredCloneHolderBase::CustomCanTransferHandler(
334 JSContext
* aCx
, JS::Handle
<JSObject
*> aObj
,
335 bool* aSameProcessScopeRequired
) {
339 // StructuredCloneHolder class
341 StructuredCloneHolder::StructuredCloneHolder(
342 CloningSupport aSupportsCloning
, TransferringSupport aSupportsTransferring
,
343 StructuredCloneScope aScope
)
344 : StructuredCloneHolderBase(aScope
),
345 mSupportsCloning(aSupportsCloning
== CloningSupported
),
346 mSupportsTransferring(aSupportsTransferring
== TransferringSupported
),
350 mCreationEventTarget(GetCurrentSerialEventTarget())
355 StructuredCloneHolder::~StructuredCloneHolder() {
357 MOZ_ASSERT(mTransferredPorts
.IsEmpty());
360 void StructuredCloneHolder::Write(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
362 Write(aCx
, aValue
, JS::UndefinedHandleValue
, JS::CloneDataPolicy(), aRv
);
365 void StructuredCloneHolder::Write(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
366 JS::Handle
<JS::Value
> aTransfer
,
367 const JS::CloneDataPolicy
& aCloneDataPolicy
,
369 if (!StructuredCloneHolderBase::Write(aCx
, aValue
, aTransfer
,
371 aRv
.ThrowDataCloneError(mErrorMessage
);
376 void StructuredCloneHolder::Read(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
377 JS::MutableHandle
<JS::Value
> aValue
,
379 return Read(aGlobal
, aCx
, aValue
, JS::CloneDataPolicy(), aRv
);
382 void StructuredCloneHolder::Read(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
383 JS::MutableHandle
<JS::Value
> aValue
,
384 const JS::CloneDataPolicy
& aCloneDataPolicy
,
388 mozilla::AutoRestore
<nsIGlobalObject
*> guard(mGlobal
);
389 auto errorMessageGuard
= MakeScopeExit([&] { mErrorMessage
.Truncate(); });
392 if (!StructuredCloneHolderBase::Read(aCx
, aValue
, aCloneDataPolicy
)) {
393 JS_ClearPendingException(aCx
);
394 aRv
.ThrowDataCloneError(mErrorMessage
);
398 // If we are tranferring something, we cannot call 'Read()' more than once.
399 if (mSupportsTransferring
) {
400 mBlobImplArray
.Clear();
401 mWasmModuleArray
.Clear();
402 mClonedSurfaces
.Clear();
403 mInputStreamArray
.Clear();
404 mVideoFrames
.Clear();
405 mEncodedAudioChunks
.Clear();
406 mEncodedVideoChunks
.Clear();
411 void StructuredCloneHolder::ReadFromBuffer(
412 nsIGlobalObject
* aGlobal
, JSContext
* aCx
, JSStructuredCloneData
& aBuffer
,
413 JS::MutableHandle
<JS::Value
> aValue
,
414 const JS::CloneDataPolicy
& aCloneDataPolicy
, ErrorResult
& aRv
) {
415 ReadFromBuffer(aGlobal
, aCx
, aBuffer
, JS_STRUCTURED_CLONE_VERSION
, aValue
,
416 aCloneDataPolicy
, aRv
);
419 void StructuredCloneHolder::ReadFromBuffer(
420 nsIGlobalObject
* aGlobal
, JSContext
* aCx
, JSStructuredCloneData
& aBuffer
,
421 uint32_t aAlgorithmVersion
, JS::MutableHandle
<JS::Value
> aValue
,
422 const JS::CloneDataPolicy
& aCloneDataPolicy
, ErrorResult
& aRv
) {
423 MOZ_ASSERT(!mBuffer
, "ReadFromBuffer() must be called without a Write().");
425 mozilla::AutoRestore
<nsIGlobalObject
*> guard(mGlobal
);
426 auto errorMessageGuard
= MakeScopeExit([&] { mErrorMessage
.Truncate(); });
429 if (!JS_ReadStructuredClone(aCx
, aBuffer
, aAlgorithmVersion
, CloneScope(),
430 aValue
, aCloneDataPolicy
, &sCallbacks
, this)) {
431 JS_ClearPendingException(aCx
);
432 aRv
.ThrowDataCloneError(mErrorMessage
);
437 static bool CheckExposedGlobals(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
438 uint16_t aExposedGlobals
) {
439 JS::Rooted
<JSObject
*> global(aCx
, aGlobal
->GetGlobalJSObject());
441 // Sandboxes aren't really DOM globals (though they do set the
442 // JSCLASS_DOM_GLOBAL flag), and so we can't simply do the exposure check.
443 // Some sandboxes do have a DOM global as their prototype, so using the
444 // prototype to check for exposure will at least make it work for those
447 JSObject
* proto
= xpc::SandboxPrototypeOrNull(aCx
, global
);
453 if (!IsGlobalInExposureSet(aCx
, global
, aExposedGlobals
)) {
455 error
.ThrowDataCloneError("Interface is not exposed.");
456 MOZ_ALWAYS_TRUE(error
.MaybeSetPendingException(aCx
));
463 JSObject
* StructuredCloneHolder::ReadFullySerializableObjects(
464 JSContext
* aCx
, JSStructuredCloneReader
* aReader
, uint32_t aTag
,
465 bool aIsForIndexedDB
) {
468 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
473 Maybe
<std::pair
<uint16_t, WebIDLDeserializer
>> deserializer
=
474 LookupDeserializer(StructuredCloneTags(aTag
));
475 if (deserializer
.isSome()) {
476 uint16_t exposedGlobals
;
477 WebIDLDeserializer deserialize
;
478 std::tie(exposedGlobals
, deserialize
) = deserializer
.ref();
480 // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
484 // 1. Let interfaceName be serialized.[[Type]].
485 // 2. If the interface identified by interfaceName is not exposed in
486 // targetRealm, then throw a "DataCloneError" DOMException.
488 // The special-casing for IndexedDB is because it uses a sandbox to
489 // deserialize, which means we don't actually have access to exposure
491 if (!aIsForIndexedDB
&& !CheckExposedGlobals(aCx
, global
, exposedGlobals
)) {
495 return deserialize(aCx
, global
, aReader
);
498 if (aTag
== SCTAG_DOM_NULL_PRINCIPAL
|| aTag
== SCTAG_DOM_SYSTEM_PRINCIPAL
||
499 aTag
== SCTAG_DOM_CONTENT_PRINCIPAL
||
500 aTag
== SCTAG_DOM_EXPANDED_PRINCIPAL
) {
502 if (!nsJSPrincipals::ReadKnownPrincipalType(aCx
, aReader
, aTag
, &prin
)) {
506 JS::Rooted
<JS::Value
> result(aCx
);
508 // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of
509 // the casting between JSPrincipals* and nsIPrincipal* we can't use
510 // getter_AddRefs above and have to already_AddRefed here.
511 nsCOMPtr
<nsIPrincipal
> principal
=
512 already_AddRefed
<nsIPrincipal
>(nsJSPrincipals::get(prin
));
514 nsresult rv
= nsContentUtils::WrapNative(
515 aCx
, principal
, &NS_GET_IID(nsIPrincipal
), &result
);
517 xpc::Throw(aCx
, NS_ERROR_DOM_DATA_CLONE_ERR
);
521 return result
.toObjectOrNull();
524 // Don't know what this is. Bail.
525 xpc::Throw(aCx
, NS_ERROR_DOM_DATA_CLONE_ERR
);
530 bool StructuredCloneHolder::WriteFullySerializableObjects(
531 JSContext
* aCx
, JSStructuredCloneWriter
* aWriter
,
532 JS::Handle
<JSObject
*> aObj
) {
535 // Window and Location are not serializable, so it's OK to just do a static
537 JS::Rooted
<JSObject
*> obj(aCx
, js::CheckedUnwrapStatic(aObj
));
539 return xpc::Throw(aCx
, NS_ERROR_DOM_DATA_CLONE_ERR
);
542 const DOMJSClass
* domClass
= GetDOMClass(obj
);
543 if (domClass
&& domClass
->mSerializer
) {
544 return domClass
->mSerializer(aCx
, aWriter
, obj
);
547 if (NS_IsMainThread() && xpc::IsReflector(obj
, aCx
)) {
548 // We only care about principals, so ReflectorToISupportsStatic is fine.
549 nsCOMPtr
<nsISupports
> base
= xpc::ReflectorToISupportsStatic(obj
);
550 nsCOMPtr
<nsIPrincipal
> principal
= do_QueryInterface(base
);
552 auto nsjsprincipals
= nsJSPrincipals::get(principal
);
553 return nsjsprincipals
->write(aCx
, aWriter
);
557 // Don't know what this is
559 const char* className
= JS::GetClass(obj
)->name
;
560 rv
.ThrowDataCloneError(nsDependentCString(className
) +
561 " object could not be cloned."_ns
);
562 MOZ_ALWAYS_TRUE(rv
.MaybeSetPendingException(aCx
));
566 template <typename char_type
>
567 static bool ReadTString(JSStructuredCloneReader
* aReader
,
568 nsTString
<char_type
>& aString
) {
569 uint32_t length
, zero
;
570 if (!JS_ReadUint32Pair(aReader
, &length
, &zero
)) {
574 if (NS_WARN_IF(!aString
.SetLength(length
, fallible
))) {
577 size_t charSize
= sizeof(char_type
);
578 return JS_ReadBytes(aReader
, (void*)aString
.BeginWriting(),
582 template <typename char_type
>
583 static bool WriteTString(JSStructuredCloneWriter
* aWriter
,
584 const nsTSubstring
<char_type
>& aString
) {
585 size_t charSize
= sizeof(char_type
);
586 return JS_WriteUint32Pair(aWriter
, aString
.Length(), 0) &&
587 JS_WriteBytes(aWriter
, aString
.BeginReading(),
588 aString
.Length() * charSize
);
592 bool StructuredCloneHolder::ReadString(JSStructuredCloneReader
* aReader
,
594 return ReadTString(aReader
, aString
);
598 bool StructuredCloneHolder::WriteString(JSStructuredCloneWriter
* aWriter
,
599 const nsAString
& aString
) {
600 return WriteTString(aWriter
, aString
);
604 bool StructuredCloneHolder::ReadCString(JSStructuredCloneReader
* aReader
,
605 nsCString
& aString
) {
606 return ReadTString(aReader
, aString
);
610 bool StructuredCloneHolder::WriteCString(JSStructuredCloneWriter
* aWriter
,
611 const nsACString
& aString
) {
612 return WriteTString(aWriter
, aString
);
617 JSObject
* ReadBlob(JSContext
* aCx
, uint32_t aIndex
,
618 StructuredCloneHolder
* aHolder
) {
621 if (aIndex
>= aHolder
->BlobImpls().Length()) {
625 MOZ_ASSERT(aIndex
< aHolder
->BlobImpls().Length());
626 JS::Rooted
<JS::Value
> val(aCx
);
628 // RefPtr<File> and RefPtr<BlobImpl> need to go out of scope before
629 // toObject() is called because the static analysis thinks releasing XPCOM
630 // objects can GC (because in some cases it can!), and a return statement
631 // with a JSObject* type means that JSObject* is on the stack as a raw
632 // pointer while destructors are running.
633 RefPtr
<BlobImpl
> blobImpl
= aHolder
->BlobImpls()[aIndex
];
635 RefPtr
<Blob
> blob
= Blob::Create(aHolder
->GlobalDuringRead(), blobImpl
);
636 if (NS_WARN_IF(!blob
)) {
640 if (!ToJSValue(aCx
, blob
, &val
)) {
645 return &val
.toObject();
648 bool WriteBlob(JSStructuredCloneWriter
* aWriter
, Blob
* aBlob
,
649 StructuredCloneHolder
* aHolder
) {
654 RefPtr
<BlobImpl
> blobImpl
= aBlob
->Impl();
656 // We store the position of the blobImpl in the array as index.
657 if (JS_WriteUint32Pair(aWriter
, SCTAG_DOM_BLOB
,
658 aHolder
->BlobImpls().Length())) {
659 aHolder
->BlobImpls().AppendElement(blobImpl
);
666 // A directory is serialized as:
667 // - pair of ints: SCTAG_DOM_DIRECTORY, path length
669 bool WriteDirectory(JSStructuredCloneWriter
* aWriter
, Directory
* aDirectory
) {
671 MOZ_ASSERT(aDirectory
);
674 aDirectory
->GetFullRealPath(path
);
676 size_t charSize
= sizeof(nsString::char_type
);
677 return JS_WriteUint32Pair(aWriter
, SCTAG_DOM_DIRECTORY
, path
.Length()) &&
678 JS_WriteBytes(aWriter
, path
.get(), path
.Length() * charSize
);
681 already_AddRefed
<Directory
> ReadDirectoryInternal(
682 JSStructuredCloneReader
* aReader
, uint32_t aPathLength
,
683 StructuredCloneHolder
* aHolder
) {
688 if (NS_WARN_IF(!path
.SetLength(aPathLength
, fallible
))) {
691 size_t charSize
= sizeof(nsString::char_type
);
692 if (!JS_ReadBytes(aReader
, (void*)path
.BeginWriting(),
693 aPathLength
* charSize
)) {
697 nsCOMPtr
<nsIFile
> file
;
698 nsresult rv
= NS_NewLocalFile(path
, true, getter_AddRefs(file
));
699 if (NS_WARN_IF(NS_FAILED(rv
))) {
703 RefPtr
<Directory
> directory
=
704 Directory::Create(aHolder
->GlobalDuringRead(), file
);
705 return directory
.forget();
708 JSObject
* ReadDirectory(JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
709 uint32_t aPathLength
, StructuredCloneHolder
* aHolder
) {
714 // RefPtr<Directory> needs to go out of scope before toObject() is
715 // called because the static analysis thinks dereferencing XPCOM objects
716 // can GC (because in some cases it can!), and a return statement with a
717 // JSObject* type means that JSObject* is on the stack as a raw pointer
718 // while destructors are running.
719 JS::Rooted
<JS::Value
> val(aCx
);
721 RefPtr
<Directory
> directory
=
722 ReadDirectoryInternal(aReader
, aPathLength
, aHolder
);
727 if (!ToJSValue(aCx
, directory
, &val
)) {
732 return &val
.toObject();
735 // Read the WriteFileList for the format.
736 JSObject
* ReadFileList(JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
737 uint32_t aCount
, StructuredCloneHolder
* aHolder
) {
741 JS::Rooted
<JS::Value
> val(aCx
);
743 RefPtr
<FileList
> fileList
= new FileList(aHolder
->GlobalDuringRead());
745 uint32_t zero
, index
;
746 // |index| is the index of the first blobImpl.
747 if (!JS_ReadUint32Pair(aReader
, &zero
, &index
) || zero
!= 0) {
751 // |aCount| is the number of BlobImpls to use from the |index|.
752 for (uint32_t i
= 0; i
< aCount
; ++i
) {
753 uint32_t pos
= index
+ i
;
755 if (pos
>= aHolder
->BlobImpls().Length()) {
759 MOZ_ASSERT(pos
< aHolder
->BlobImpls().Length());
761 RefPtr
<BlobImpl
> blobImpl
= aHolder
->BlobImpls()[pos
];
762 MOZ_ASSERT(blobImpl
->IsFile());
764 RefPtr
<File
> file
= File::Create(aHolder
->GlobalDuringRead(), blobImpl
);
765 if (NS_WARN_IF(!file
)) {
769 if (!fileList
->Append(file
)) {
774 if (!ToJSValue(aCx
, fileList
, &val
)) {
779 return &val
.toObject();
782 // The format of the FileList serialization is:
783 // - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList
784 // - pair of ints: 0, The offset of the BlobImpl array
785 bool WriteFileList(JSStructuredCloneWriter
* aWriter
, FileList
* aFileList
,
786 StructuredCloneHolder
* aHolder
) {
788 MOZ_ASSERT(aFileList
);
791 // A FileList is serialized writing the X number of elements and the offset
792 // from mBlobImplArray. The Read will take X elements from mBlobImplArray
793 // starting from the offset.
794 if (!JS_WriteUint32Pair(aWriter
, SCTAG_DOM_FILELIST
, aFileList
->Length()) ||
795 !JS_WriteUint32Pair(aWriter
, 0, aHolder
->BlobImpls().Length())) {
799 nsTArray
<RefPtr
<BlobImpl
>> blobImpls
;
801 for (uint32_t i
= 0; i
< aFileList
->Length(); ++i
) {
802 RefPtr
<BlobImpl
> blobImpl
= aFileList
->Item(i
)->Impl();
803 blobImpls
.AppendElement(blobImpl
);
806 aHolder
->BlobImpls().AppendElements(blobImpls
);
810 // Read the WriteFormData for the format.
811 JSObject
* ReadFormData(JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
812 uint32_t aCount
, StructuredCloneHolder
* aHolder
) {
817 // See the serialization of the FormData for the format.
818 JS::Rooted
<JS::Value
> val(aCx
);
820 RefPtr
<FormData
> formData
= new FormData(aHolder
->GlobalDuringRead());
822 Optional
<nsAString
> thirdArg
;
823 for (uint32_t i
= 0; i
< aCount
; ++i
) {
825 if (!StructuredCloneHolder::ReadString(aReader
, name
)) {
829 uint32_t tag
, indexOrLengthOfString
;
830 if (!JS_ReadUint32Pair(aReader
, &tag
, &indexOrLengthOfString
)) {
834 if (tag
== SCTAG_DOM_BLOB
) {
836 if (indexOrLengthOfString
>= aHolder
->BlobImpls().Length()) {
840 MOZ_ASSERT(indexOrLengthOfString
< aHolder
->BlobImpls().Length());
842 RefPtr
<BlobImpl
> blobImpl
= aHolder
->BlobImpls()[indexOrLengthOfString
];
844 RefPtr
<Blob
> blob
= Blob::Create(aHolder
->GlobalDuringRead(), blobImpl
);
845 if (NS_WARN_IF(!blob
)) {
850 formData
->Append(name
, *blob
, thirdArg
, rv
);
851 if (NS_WARN_IF(rv
.Failed())) {
852 rv
.SuppressException();
856 } else if (tag
== SCTAG_DOM_DIRECTORY
) {
857 RefPtr
<Directory
> directory
=
858 ReadDirectoryInternal(aReader
, indexOrLengthOfString
, aHolder
);
863 formData
->Append(name
, directory
);
866 if (NS_WARN_IF(tag
!= 0)) {
871 if (NS_WARN_IF(!value
.SetLength(indexOrLengthOfString
, fallible
))) {
874 size_t charSize
= sizeof(nsString::char_type
);
875 if (!JS_ReadBytes(aReader
, (void*)value
.BeginWriting(),
876 indexOrLengthOfString
* charSize
)) {
881 formData
->Append(name
, value
, rv
);
882 if (NS_WARN_IF(rv
.Failed())) {
883 rv
.SuppressException();
889 if (!ToJSValue(aCx
, formData
, &val
)) {
894 return &val
.toObject();
897 // The format of the FormData serialization is:
898 // - pair of ints: SCTAG_DOM_FORMDATA, Length of the FormData elements
899 // - for each Element element:
902 // - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array
904 // - if it's a directory (See WriteDirectory):
905 // - pair of ints: SCTAG_DOM_DIRECTORY, path length
908 // - pair of ints: 0, string length
910 bool WriteFormData(JSStructuredCloneWriter
* aWriter
, FormData
* aFormData
,
911 StructuredCloneHolder
* aHolder
) {
913 MOZ_ASSERT(aFormData
);
916 if (!JS_WriteUint32Pair(aWriter
, SCTAG_DOM_FORMDATA
, aFormData
->Length())) {
920 auto write
= [aWriter
, aHolder
](
921 const nsString
& aName
,
922 const OwningBlobOrDirectoryOrUSVString
& aValue
) {
923 if (!StructuredCloneHolder::WriteString(aWriter
, aName
)) {
927 if (aValue
.IsBlob()) {
928 if (!JS_WriteUint32Pair(aWriter
, SCTAG_DOM_BLOB
,
929 aHolder
->BlobImpls().Length())) {
933 RefPtr
<BlobImpl
> blobImpl
= aValue
.GetAsBlob()->Impl();
935 aHolder
->BlobImpls().AppendElement(blobImpl
);
939 if (aValue
.IsDirectory()) {
940 Directory
* directory
= aValue
.GetAsDirectory();
941 return WriteDirectory(aWriter
, directory
);
944 const size_t charSize
= sizeof(nsString::char_type
);
945 return JS_WriteUint32Pair(aWriter
, 0, aValue
.GetAsUSVString().Length()) &&
946 JS_WriteBytes(aWriter
, aValue
.GetAsUSVString().get(),
947 aValue
.GetAsUSVString().Length() * charSize
);
949 return aFormData
->ForEach(write
);
952 JSObject
* ReadWasmModule(JSContext
* aCx
, uint32_t aIndex
,
953 StructuredCloneHolder
* aHolder
) {
955 MOZ_ASSERT(aHolder
->CloneScope() ==
956 StructuredCloneHolder::StructuredCloneScope::SameProcess
);
958 if (aIndex
>= aHolder
->WasmModules().Length()) {
962 MOZ_ASSERT(aIndex
< aHolder
->WasmModules().Length());
964 return aHolder
->WasmModules()[aIndex
]->createObject(aCx
);
967 bool WriteWasmModule(JSStructuredCloneWriter
* aWriter
,
968 JS::WasmModule
* aWasmModule
,
969 StructuredCloneHolder
* aHolder
) {
971 MOZ_ASSERT(aWasmModule
);
973 MOZ_ASSERT(aHolder
->CloneScope() ==
974 StructuredCloneHolder::StructuredCloneScope::SameProcess
);
976 // We store the position of the wasmModule in the array as index.
977 if (JS_WriteUint32Pair(aWriter
, SCTAG_DOM_WASM_MODULE
,
978 aHolder
->WasmModules().Length())) {
979 aHolder
->WasmModules().AppendElement(aWasmModule
);
986 JSObject
* ReadInputStream(JSContext
* aCx
, uint32_t aIndex
,
987 StructuredCloneHolder
* aHolder
) {
990 if (aIndex
>= aHolder
->InputStreams().Length()) {
994 MOZ_ASSERT(aIndex
< aHolder
->InputStreams().Length());
995 JS::Rooted
<JS::Value
> result(aCx
);
997 nsCOMPtr
<nsIInputStream
> inputStream
= aHolder
->InputStreams()[aIndex
];
999 nsresult rv
= nsContentUtils::WrapNative(
1000 aCx
, inputStream
, &NS_GET_IID(nsIInputStream
), &result
);
1001 if (NS_FAILED(rv
)) {
1006 return &result
.toObject();
1009 bool WriteInputStream(JSStructuredCloneWriter
* aWriter
,
1010 nsIInputStream
* aInputStream
,
1011 StructuredCloneHolder
* aHolder
) {
1012 MOZ_ASSERT(aWriter
);
1013 MOZ_ASSERT(aInputStream
);
1014 MOZ_ASSERT(aHolder
);
1016 // We store the position of the inputStream in the array as index.
1017 if (JS_WriteUint32Pair(aWriter
, SCTAG_DOM_INPUTSTREAM
,
1018 aHolder
->InputStreams().Length())) {
1019 aHolder
->InputStreams().AppendElement(aInputStream
);
1026 } // anonymous namespace
1028 static const uint16_t sWindowOrWorker
=
1029 GlobalNames::DedicatedWorkerGlobalScope
|
1030 GlobalNames::ServiceWorkerGlobalScope
|
1031 GlobalNames::SharedWorkerGlobalScope
| GlobalNames::Window
|
1032 GlobalNames::WorkerDebuggerGlobalScope
;
1034 JSObject
* StructuredCloneHolder::CustomReadHandler(
1035 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
1036 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
,
1038 MOZ_ASSERT(mSupportsCloning
);
1040 if (aTag
== SCTAG_DOM_BLOB
) {
1041 if (!CheckExposedGlobals(aCx
, mGlobal
, sWindowOrWorker
)) {
1044 return ReadBlob(aCx
, aIndex
, this);
1047 if (aTag
== SCTAG_DOM_DIRECTORY
) {
1048 if (!CheckExposedGlobals(aCx
, mGlobal
, sWindowOrWorker
)) {
1051 return ReadDirectory(aCx
, aReader
, aIndex
, this);
1054 if (aTag
== SCTAG_DOM_FILELIST
) {
1055 if (!CheckExposedGlobals(aCx
, mGlobal
, sWindowOrWorker
)) {
1058 return ReadFileList(aCx
, aReader
, aIndex
, this);
1061 if (aTag
== SCTAG_DOM_FORMDATA
) {
1062 if (!CheckExposedGlobals(aCx
, mGlobal
, sWindowOrWorker
)) {
1065 return ReadFormData(aCx
, aReader
, aIndex
, this);
1068 if (aTag
== SCTAG_DOM_IMAGEBITMAP
&&
1069 CloneScope() == StructuredCloneScope::SameProcess
) {
1070 if (!CheckExposedGlobals(aCx
, mGlobal
, sWindowOrWorker
)) {
1073 // Get the current global object.
1074 // This can be null.
1075 JS::Rooted
<JSObject
*> result(aCx
);
1077 // aIndex is the index of the cloned image.
1078 result
= ImageBitmap::ReadStructuredClone(aCx
, aReader
, mGlobal
,
1079 GetSurfaces(), aIndex
);
1084 if (aTag
== SCTAG_DOM_STRUCTURED_CLONE_HOLDER
) {
1085 return StructuredCloneBlob::ReadStructuredClone(aCx
, aReader
, this);
1088 if (aTag
== SCTAG_DOM_WASM_MODULE
&&
1089 CloneScope() == StructuredCloneScope::SameProcess
&&
1090 aCloneDataPolicy
.areIntraClusterClonableSharedObjectsAllowed()) {
1091 return ReadWasmModule(aCx
, aIndex
, this);
1094 if (aTag
== SCTAG_DOM_INPUTSTREAM
) {
1095 return ReadInputStream(aCx
, aIndex
, this);
1098 if (aTag
== SCTAG_DOM_BROWSING_CONTEXT
) {
1099 if (!CheckExposedGlobals(aCx
, mGlobal
, GlobalNames::Window
)) {
1102 return BrowsingContext::ReadStructuredClone(aCx
, aReader
, this);
1105 if (aTag
== SCTAG_DOM_CLONED_ERROR_OBJECT
) {
1106 if (!CheckExposedGlobals(aCx
, mGlobal
, sWindowOrWorker
)) {
1109 return ClonedErrorHolder::ReadStructuredClone(aCx
, aReader
, this);
1112 if (StaticPrefs::dom_media_webcodecs_enabled() &&
1113 aTag
== SCTAG_DOM_VIDEOFRAME
&&
1114 CloneScope() == StructuredCloneScope::SameProcess
&&
1115 aCloneDataPolicy
.areIntraClusterClonableSharedObjectsAllowed()) {
1116 JS::Rooted
<JSObject
*> global(aCx
, mGlobal
->GetGlobalJSObject());
1117 if (VideoFrame_Binding::ConstructorEnabled(aCx
, global
)) {
1118 return VideoFrame::ReadStructuredClone(aCx
, mGlobal
, aReader
,
1119 VideoFrames()[aIndex
]);
1123 if (StaticPrefs::dom_media_webcodecs_enabled() &&
1124 aTag
== SCTAG_DOM_ENCODEDVIDEOCHUNK
&&
1125 CloneScope() == StructuredCloneScope::SameProcess
&&
1126 aCloneDataPolicy
.areIntraClusterClonableSharedObjectsAllowed()) {
1127 JS::Rooted
<JSObject
*> global(aCx
, mGlobal
->GetGlobalJSObject());
1128 if (EncodedVideoChunk_Binding::ConstructorEnabled(aCx
, global
)) {
1129 return EncodedVideoChunk::ReadStructuredClone(
1130 aCx
, mGlobal
, aReader
, EncodedVideoChunks()[aIndex
]);
1134 if (StaticPrefs::dom_media_webcodecs_enabled() &&
1135 aTag
== SCTAG_DOM_AUDIODATA
&&
1136 CloneScope() == StructuredCloneScope::SameProcess
&&
1137 aCloneDataPolicy
.areIntraClusterClonableSharedObjectsAllowed()) {
1138 JS::Rooted
<JSObject
*> global(aCx
, mGlobal
->GetGlobalJSObject());
1139 if (AudioData_Binding::ConstructorEnabled(aCx
, global
)) {
1140 return AudioData::ReadStructuredClone(aCx
, mGlobal
, aReader
,
1141 AudioData()[aIndex
]);
1145 if (StaticPrefs::dom_media_webcodecs_enabled() &&
1146 aTag
== SCTAG_DOM_ENCODEDAUDIOCHUNK
&&
1147 CloneScope() == StructuredCloneScope::SameProcess
&&
1148 aCloneDataPolicy
.areIntraClusterClonableSharedObjectsAllowed()) {
1149 JS::Rooted
<JSObject
*> global(aCx
, mGlobal
->GetGlobalJSObject());
1150 if (EncodedAudioChunk_Binding::ConstructorEnabled(aCx
, global
)) {
1151 return EncodedAudioChunk::ReadStructuredClone(
1152 aCx
, mGlobal
, aReader
, EncodedAudioChunks()[aIndex
]);
1156 return ReadFullySerializableObjects(aCx
, aReader
, aTag
, false);
1159 bool StructuredCloneHolder::CustomWriteHandler(
1160 JSContext
* aCx
, JSStructuredCloneWriter
* aWriter
,
1161 JS::Handle
<JSObject
*> aObj
, bool* aSameProcessScopeRequired
) {
1162 if (!mSupportsCloning
) {
1166 JS::Rooted
<JSObject
*> obj(aCx
, aObj
);
1168 // See if this is a File/Blob object.
1170 Blob
* blob
= nullptr;
1171 if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob
, &obj
, blob
))) {
1172 return WriteBlob(aWriter
, blob
, this);
1176 // See if this is a Directory object.
1178 Directory
* directory
= nullptr;
1179 if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory
, &obj
, directory
))) {
1180 return WriteDirectory(aWriter
, directory
);
1184 // See if this is a FileList object.
1186 FileList
* fileList
= nullptr;
1187 if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList
, &obj
, fileList
))) {
1188 return WriteFileList(aWriter
, fileList
, this);
1192 // See if this is a FormData object.
1194 FormData
* formData
= nullptr;
1195 if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData
, &obj
, formData
))) {
1196 return WriteFormData(aWriter
, formData
, this);
1200 // See if this is an ImageBitmap object.
1202 ImageBitmap
* imageBitmap
= nullptr;
1203 if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap
, &obj
, imageBitmap
))) {
1204 SameProcessScopeRequired(aSameProcessScopeRequired
);
1206 if (CloneScope() == StructuredCloneScope::SameProcess
) {
1208 ImageBitmap::WriteStructuredClone(aWriter
, GetSurfaces(), imageBitmap
,
1210 return !rv
.MaybeSetPendingException(aCx
);
1216 // See if this is a StructuredCloneBlob object.
1218 StructuredCloneBlob
* holder
= nullptr;
1219 if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder
, &obj
, holder
))) {
1220 return holder
->WriteStructuredClone(aCx
, aWriter
, this);
1224 // See if this is a BrowsingContext object.
1226 BrowsingContext
* holder
= nullptr;
1227 if (NS_SUCCEEDED(UNWRAP_OBJECT(BrowsingContext
, &obj
, holder
))) {
1228 return holder
->WriteStructuredClone(aCx
, aWriter
, this);
1232 // See if this is a ClonedErrorHolder object.
1234 ClonedErrorHolder
* holder
= nullptr;
1235 if (NS_SUCCEEDED(UNWRAP_OBJECT(ClonedErrorHolder
, &obj
, holder
))) {
1236 return holder
->WriteStructuredClone(aCx
, aWriter
, this);
1240 // See if this is a WasmModule.
1241 if (JS::IsWasmModuleObject(obj
)) {
1242 SameProcessScopeRequired(aSameProcessScopeRequired
);
1243 if (CloneScope() == StructuredCloneScope::SameProcess
) {
1244 RefPtr
<JS::WasmModule
> module
= JS::GetWasmModule(obj
);
1247 return WriteWasmModule(aWriter
, module
, this);
1252 // See if this is a VideoFrame object.
1253 if (StaticPrefs::dom_media_webcodecs_enabled()) {
1254 VideoFrame
* videoFrame
= nullptr;
1255 if (NS_SUCCEEDED(UNWRAP_OBJECT(VideoFrame
, &obj
, videoFrame
))) {
1256 SameProcessScopeRequired(aSameProcessScopeRequired
);
1257 return CloneScope() == StructuredCloneScope::SameProcess
1258 ? videoFrame
->WriteStructuredClone(aWriter
, this)
1263 // See if this is a EncodedVideoChunk object.
1264 if (StaticPrefs::dom_media_webcodecs_enabled()) {
1265 EncodedVideoChunk
* encodedVideoChunk
= nullptr;
1267 UNWRAP_OBJECT(EncodedVideoChunk
, &obj
, encodedVideoChunk
))) {
1268 SameProcessScopeRequired(aSameProcessScopeRequired
);
1269 return CloneScope() == StructuredCloneScope::SameProcess
1270 ? encodedVideoChunk
->WriteStructuredClone(aWriter
, this)
1275 // See if this is an AudioData object.
1276 if (StaticPrefs::dom_media_webcodecs_enabled()) {
1277 mozilla::dom::AudioData
* audioData
= nullptr;
1278 if (NS_SUCCEEDED(UNWRAP_OBJECT(AudioData
, &obj
, audioData
))) {
1279 SameProcessScopeRequired(aSameProcessScopeRequired
);
1280 return CloneScope() == StructuredCloneScope::SameProcess
1281 ? audioData
->WriteStructuredClone(aWriter
, this)
1286 // See if this is a EncodedAudioChunk object.
1287 if (StaticPrefs::dom_media_webcodecs_enabled()) {
1288 EncodedAudioChunk
* encodedAudioChunk
= nullptr;
1290 UNWRAP_OBJECT(EncodedAudioChunk
, &obj
, encodedAudioChunk
))) {
1291 SameProcessScopeRequired(aSameProcessScopeRequired
);
1292 return CloneScope() == StructuredCloneScope::SameProcess
1293 ? encodedAudioChunk
->WriteStructuredClone(aWriter
, this)
1299 // We only care about streams, so ReflectorToISupportsStatic is fine.
1300 nsCOMPtr
<nsISupports
> base
= xpc::ReflectorToISupportsStatic(aObj
);
1301 nsCOMPtr
<nsIInputStream
> inputStream
= do_QueryInterface(base
);
1303 return WriteInputStream(aWriter
, inputStream
, this);
1307 return WriteFullySerializableObjects(aCx
, aWriter
, aObj
);
1310 already_AddRefed
<MessagePort
> StructuredCloneHolder::ReceiveMessagePort(
1312 if (NS_WARN_IF(aIndex
>= mPortIdentifiers
.Length())) {
1315 UniqueMessagePortId
portId(mPortIdentifiers
[aIndex
]);
1318 RefPtr
<MessagePort
> port
= MessagePort::Create(mGlobal
, portId
, rv
);
1319 if (NS_WARN_IF(rv
.Failed())) {
1320 rv
.SuppressException();
1324 return port
.forget();
1327 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
1328 MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool
1329 StructuredCloneHolder::CustomReadTransferHandler(
1330 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
1331 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
, void* aContent
,
1332 uint64_t aExtraData
, JS::MutableHandle
<JSObject
*> aReturnObject
) {
1333 MOZ_ASSERT(mSupportsTransferring
);
1335 if (aTag
== SCTAG_DOM_MAP_MESSAGEPORT
) {
1336 if (!CheckExposedGlobals(
1338 sWindowOrWorker
| GlobalNames::AudioWorkletGlobalScope
)) {
1342 if (aExtraData
>= mPortIdentifiers
.Length()) {
1346 RefPtr
<MessagePort
> port
= ReceiveMessagePort(aExtraData
);
1350 mTransferredPorts
.AppendElement(port
);
1352 JS::Rooted
<JS::Value
> value(aCx
);
1353 if (!GetOrCreateDOMReflector(aCx
, port
, &value
)) {
1354 JS_ClearPendingException(aCx
);
1358 aReturnObject
.set(&value
.toObject());
1362 if (aTag
== SCTAG_DOM_CANVAS
&&
1363 CloneScope() == StructuredCloneScope::SameProcess
) {
1364 if (!CheckExposedGlobals(aCx
, mGlobal
, sWindowOrWorker
)) {
1367 MOZ_ASSERT(aContent
);
1368 OffscreenCanvasCloneData
* data
=
1369 static_cast<OffscreenCanvasCloneData
*>(aContent
);
1370 RefPtr
<OffscreenCanvas
> canvas
=
1371 OffscreenCanvas::CreateFromCloneData(mGlobal
, data
);
1374 JS::Rooted
<JS::Value
> value(aCx
);
1375 if (!GetOrCreateDOMReflector(aCx
, canvas
, &value
)) {
1376 JS_ClearPendingException(aCx
);
1380 aReturnObject
.set(&value
.toObject());
1384 if (aTag
== SCTAG_DOM_IMAGEBITMAP
&&
1385 CloneScope() == StructuredCloneScope::SameProcess
) {
1386 if (!CheckExposedGlobals(aCx
, mGlobal
, sWindowOrWorker
)) {
1389 MOZ_ASSERT(aContent
);
1390 ImageBitmapCloneData
* data
= static_cast<ImageBitmapCloneData
*>(aContent
);
1391 RefPtr
<ImageBitmap
> bitmap
=
1392 ImageBitmap::CreateFromCloneData(mGlobal
, data
);
1395 JS::Rooted
<JS::Value
> value(aCx
);
1396 if (!GetOrCreateDOMReflector(aCx
, bitmap
, &value
)) {
1397 JS_ClearPendingException(aCx
);
1401 aReturnObject
.set(&value
.toObject());
1405 if (aTag
== SCTAG_DOM_READABLESTREAM
) {
1407 if (aExtraData
>= mPortIdentifiers
.Length()) {
1411 RefPtr
<MessagePort
> port
= ReceiveMessagePort(aExtraData
);
1415 nsCOMPtr
<nsIGlobalObject
> global
= mGlobal
;
1416 return ReadableStream::ReceiveTransfer(aCx
, global
, *port
, aReturnObject
);
1419 if (aTag
== SCTAG_DOM_WRITABLESTREAM
) {
1421 if (aExtraData
>= mPortIdentifiers
.Length()) {
1425 RefPtr
<MessagePort
> port
= ReceiveMessagePort(aExtraData
);
1429 nsCOMPtr
<nsIGlobalObject
> global
= mGlobal
;
1430 return WritableStream::ReceiveTransfer(aCx
, global
, *port
, aReturnObject
);
1433 if (aTag
== SCTAG_DOM_TRANSFORMSTREAM
) {
1435 if (aExtraData
+ 1 >= mPortIdentifiers
.Length()) {
1439 RefPtr
<MessagePort
> port1
= ReceiveMessagePort(aExtraData
);
1440 RefPtr
<MessagePort
> port2
= ReceiveMessagePort(aExtraData
+ 1);
1441 if (!port1
|| !port2
) {
1444 nsCOMPtr
<nsIGlobalObject
> global
= mGlobal
;
1445 return TransformStream::ReceiveTransfer(aCx
, global
, *port1
, *port2
,
1449 if (StaticPrefs::dom_media_webcodecs_enabled() &&
1450 aTag
== SCTAG_DOM_VIDEOFRAME
&&
1451 CloneScope() == StructuredCloneScope::SameProcess
&&
1452 aCloneDataPolicy
.areIntraClusterClonableSharedObjectsAllowed()) {
1453 MOZ_ASSERT(aContent
);
1455 JS::Rooted
<JSObject
*> globalObj(aCx
, mGlobal
->GetGlobalJSObject());
1456 // aContent will be released in CustomFreeTransferHandler.
1457 if (!VideoFrame_Binding::ConstructorEnabled(aCx
, globalObj
)) {
1461 VideoFrame::TransferredData
* data
=
1462 static_cast<VideoFrame::TransferredData
*>(aContent
);
1463 nsCOMPtr
<nsIGlobalObject
> global
= mGlobal
;
1464 RefPtr
<VideoFrame
> frame
= VideoFrame::FromTransferred(global
.get(), data
);
1465 // aContent will be released in CustomFreeTransferHandler if frame is null.
1472 JS::Rooted
<JS::Value
> value(aCx
);
1473 if (!GetOrCreateDOMReflector(aCx
, frame
, &value
)) {
1474 JS_ClearPendingException(aCx
);
1477 aReturnObject
.set(&value
.toObject());
1481 if (StaticPrefs::dom_media_webcodecs_enabled() &&
1482 aTag
== SCTAG_DOM_AUDIODATA
&&
1483 CloneScope() == StructuredCloneScope::SameProcess
&&
1484 aCloneDataPolicy
.areIntraClusterClonableSharedObjectsAllowed()) {
1485 MOZ_ASSERT(aContent
);
1487 JS::Rooted
<JSObject
*> globalObj(aCx
, mGlobal
->GetGlobalJSObject());
1488 // aContent will be released in CustomFreeTransferHandler.
1489 if (!AudioData_Binding::ConstructorEnabled(aCx
, globalObj
)) {
1493 AudioData::TransferredData
* data
=
1494 static_cast<AudioData::TransferredData
*>(aContent
);
1495 nsCOMPtr
<nsIGlobalObject
> global
= mGlobal
;
1496 RefPtr
<mozilla::dom::AudioData
> audioData
=
1497 AudioData::FromTransferred(global
.get(), data
);
1498 // aContent will be released in CustomFreeTransferHandler if frame is null.
1505 JS::Rooted
<JS::Value
> value(aCx
);
1506 if (!GetOrCreateDOMReflector(aCx
, audioData
, &value
)) {
1507 JS_ClearPendingException(aCx
);
1510 aReturnObject
.set(&value
.toObject());
1517 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
1518 MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool
1519 StructuredCloneHolder::CustomWriteTransferHandler(
1520 JSContext
* aCx
, JS::Handle
<JSObject
*> aObj
, uint32_t* aTag
,
1521 JS::TransferableOwnership
* aOwnership
, void** aContent
,
1522 uint64_t* aExtraData
) {
1523 if (!mSupportsTransferring
) {
1527 JS::Rooted
<JSObject
*> obj(aCx
, aObj
);
1530 MessagePort
* port
= nullptr;
1531 nsresult rv
= UNWRAP_OBJECT(MessagePort
, &obj
, port
);
1532 if (NS_SUCCEEDED(rv
)) {
1533 if (!port
->CanBeCloned()) {
1537 UniqueMessagePortId identifier
;
1538 port
->CloneAndDisentangle(identifier
);
1540 // We use aExtraData to store the index of this new port identifier.
1541 *aExtraData
= mPortIdentifiers
.Length();
1542 mPortIdentifiers
.AppendElement(identifier
.release());
1544 *aTag
= SCTAG_DOM_MAP_MESSAGEPORT
;
1545 *aContent
= nullptr;
1546 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
1551 if (CloneScope() == StructuredCloneScope::SameProcess
) {
1552 OffscreenCanvas
* canvas
= nullptr;
1553 rv
= UNWRAP_OBJECT(OffscreenCanvas
, &obj
, canvas
);
1554 if (NS_SUCCEEDED(rv
)) {
1557 UniquePtr
<OffscreenCanvasCloneData
> clonedCanvas
=
1558 canvas
->ToCloneData(aCx
);
1559 if (!clonedCanvas
) {
1564 *aTag
= SCTAG_DOM_CANVAS
;
1565 *aContent
= clonedCanvas
.release();
1566 MOZ_ASSERT(*aContent
);
1567 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
1572 ImageBitmap
* bitmap
= nullptr;
1573 rv
= UNWRAP_OBJECT(ImageBitmap
, &obj
, bitmap
);
1574 if (NS_SUCCEEDED(rv
)) {
1576 MOZ_ASSERT(!bitmap
->IsWriteOnly());
1579 *aTag
= SCTAG_DOM_IMAGEBITMAP
;
1581 UniquePtr
<ImageBitmapCloneData
> clonedBitmap
= bitmap
->ToCloneData();
1582 if (!clonedBitmap
) {
1586 *aContent
= clonedBitmap
.release();
1587 MOZ_ASSERT(*aContent
);
1588 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
1595 if (StaticPrefs::dom_media_webcodecs_enabled()) {
1596 VideoFrame
* videoFrame
= nullptr;
1597 rv
= UNWRAP_OBJECT(VideoFrame
, &obj
, videoFrame
);
1598 if (NS_SUCCEEDED(rv
)) {
1599 MOZ_ASSERT(videoFrame
);
1602 *aTag
= SCTAG_DOM_VIDEOFRAME
;
1603 *aContent
= nullptr;
1605 UniquePtr
<VideoFrame::TransferredData
> data
= videoFrame
->Transfer();
1609 *aContent
= data
.release();
1610 MOZ_ASSERT(*aContent
);
1611 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
1615 if (StaticPrefs::dom_media_webcodecs_enabled()) {
1616 mozilla::dom::AudioData
* audioData
= nullptr;
1617 rv
= UNWRAP_OBJECT(AudioData
, &obj
, audioData
);
1618 if (NS_SUCCEEDED(rv
)) {
1619 MOZ_ASSERT(audioData
);
1622 *aTag
= SCTAG_DOM_AUDIODATA
;
1623 *aContent
= nullptr;
1625 UniquePtr
<AudioData::TransferredData
> data
= audioData
->Transfer();
1629 *aContent
= data
.release();
1630 MOZ_ASSERT(*aContent
);
1631 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
1638 RefPtr
<ReadableStream
> stream
;
1639 rv
= UNWRAP_OBJECT(ReadableStream
, &obj
, stream
);
1640 if (NS_SUCCEEDED(rv
)) {
1643 *aTag
= SCTAG_DOM_READABLESTREAM
;
1644 *aContent
= nullptr;
1646 UniqueMessagePortId id
;
1647 if (!stream
->Transfer(aCx
, id
)) {
1650 *aExtraData
= mPortIdentifiers
.Length();
1651 mPortIdentifiers
.AppendElement(id
.release());
1652 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
1658 RefPtr
<WritableStream
> stream
;
1659 rv
= UNWRAP_OBJECT(WritableStream
, &obj
, stream
);
1660 if (NS_SUCCEEDED(rv
)) {
1663 *aTag
= SCTAG_DOM_WRITABLESTREAM
;
1664 *aContent
= nullptr;
1666 UniqueMessagePortId id
;
1667 if (!stream
->Transfer(aCx
, id
)) {
1670 *aExtraData
= mPortIdentifiers
.Length();
1671 mPortIdentifiers
.AppendElement(id
.release());
1672 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
1678 RefPtr
<TransformStream
> stream
;
1679 rv
= UNWRAP_OBJECT(TransformStream
, &obj
, stream
);
1680 if (NS_SUCCEEDED(rv
)) {
1683 *aTag
= SCTAG_DOM_TRANSFORMSTREAM
;
1684 *aContent
= nullptr;
1686 UniqueMessagePortId id1
;
1687 UniqueMessagePortId id2
;
1688 if (!stream
->Transfer(aCx
, id1
, id2
)) {
1691 *aExtraData
= mPortIdentifiers
.Length();
1692 mPortIdentifiers
.AppendElement(id1
.release());
1693 mPortIdentifiers
.AppendElement(id2
.release());
1694 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
1703 void StructuredCloneHolder::CustomFreeTransferHandler(
1704 uint32_t aTag
, JS::TransferableOwnership aOwnership
, void* aContent
,
1705 uint64_t aExtraData
) {
1706 MOZ_ASSERT(mSupportsTransferring
);
1708 if (aTag
== SCTAG_DOM_MAP_MESSAGEPORT
) {
1709 MOZ_ASSERT(!aContent
);
1711 if (aExtraData
>= mPortIdentifiers
.Length()) {
1715 MOZ_ASSERT(aExtraData
< mPortIdentifiers
.Length());
1716 MessagePort::ForceClose(mPortIdentifiers
[aExtraData
]);
1720 if (aTag
== SCTAG_DOM_CANVAS
&&
1721 CloneScope() == StructuredCloneScope::SameProcess
) {
1722 MOZ_ASSERT(aContent
);
1723 OffscreenCanvasCloneData
* data
=
1724 static_cast<OffscreenCanvasCloneData
*>(aContent
);
1729 if (aTag
== SCTAG_DOM_IMAGEBITMAP
&&
1730 CloneScope() == StructuredCloneScope::SameProcess
) {
1731 MOZ_ASSERT(aContent
);
1732 ImageBitmapCloneData
* data
= static_cast<ImageBitmapCloneData
*>(aContent
);
1737 if (aTag
== SCTAG_DOM_READABLESTREAM
|| aTag
== SCTAG_DOM_WRITABLESTREAM
) {
1738 MOZ_ASSERT(!aContent
);
1740 if (aExtraData
>= mPortIdentifiers
.Length()) {
1744 MOZ_ASSERT(aExtraData
< mPortIdentifiers
.Length());
1745 MessagePort::ForceClose(mPortIdentifiers
[aExtraData
]);
1749 if (aTag
== SCTAG_DOM_TRANSFORMSTREAM
) {
1750 MOZ_ASSERT(!aContent
);
1752 if (aExtraData
+ 1 >= mPortIdentifiers
.Length()) {
1756 MOZ_ASSERT(aExtraData
+ 1 < mPortIdentifiers
.Length());
1757 MessagePort::ForceClose(mPortIdentifiers
[aExtraData
]);
1758 MessagePort::ForceClose(mPortIdentifiers
[aExtraData
+ 1]);
1762 if (StaticPrefs::dom_media_webcodecs_enabled() &&
1763 aTag
== SCTAG_DOM_VIDEOFRAME
&&
1764 CloneScope() == StructuredCloneScope::SameProcess
) {
1766 VideoFrame::TransferredData
* data
=
1767 static_cast<VideoFrame::TransferredData
*>(aContent
);
1772 if (StaticPrefs::dom_media_webcodecs_enabled() &&
1773 aTag
== SCTAG_DOM_AUDIODATA
&&
1774 CloneScope() == StructuredCloneScope::SameProcess
) {
1776 AudioData::TransferredData
* data
=
1777 static_cast<AudioData::TransferredData
*>(aContent
);
1784 bool StructuredCloneHolder::CustomCanTransferHandler(
1785 JSContext
* aCx
, JS::Handle
<JSObject
*> aObj
,
1786 bool* aSameProcessScopeRequired
) {
1787 if (!mSupportsTransferring
) {
1791 JS::Rooted
<JSObject
*> obj(aCx
, aObj
);
1794 MessagePort
* port
= nullptr;
1795 nsresult rv
= UNWRAP_OBJECT(MessagePort
, &obj
, port
);
1796 if (NS_SUCCEEDED(rv
)) {
1802 OffscreenCanvas
* canvas
= nullptr;
1803 nsresult rv
= UNWRAP_OBJECT(OffscreenCanvas
, &obj
, canvas
);
1804 if (NS_SUCCEEDED(rv
)) {
1805 SameProcessScopeRequired(aSameProcessScopeRequired
);
1806 return CloneScope() == StructuredCloneScope::SameProcess
;
1811 ImageBitmap
* bitmap
= nullptr;
1812 nsresult rv
= UNWRAP_OBJECT(ImageBitmap
, &obj
, bitmap
);
1813 if (NS_SUCCEEDED(rv
)) {
1814 if (bitmap
->IsWriteOnly()) {
1818 SameProcessScopeRequired(aSameProcessScopeRequired
);
1819 return CloneScope() == StructuredCloneScope::SameProcess
;
1824 ReadableStream
* stream
= nullptr;
1825 nsresult rv
= UNWRAP_OBJECT(ReadableStream
, &obj
, stream
);
1826 if (NS_SUCCEEDED(rv
)) {
1827 // https://streams.spec.whatwg.org/#ref-for-transfer-steps
1828 // Step 1: If ! IsReadableStreamLocked(value) is true, throw a
1829 // "DataCloneError" DOMException.
1830 return !stream
->Locked();
1835 WritableStream
* stream
= nullptr;
1836 nsresult rv
= UNWRAP_OBJECT(WritableStream
, &obj
, stream
);
1837 if (NS_SUCCEEDED(rv
)) {
1838 // https://streams.spec.whatwg.org/#ref-for-transfer-stepsâ‘
1839 // Step 1: If ! IsWritableStreamLocked(value) is true, throw a
1840 // "DataCloneError" DOMException.
1841 return !stream
->Locked();
1846 TransformStream
* stream
= nullptr;
1847 nsresult rv
= UNWRAP_OBJECT(TransformStream
, &obj
, stream
);
1848 if (NS_SUCCEEDED(rv
)) {
1849 // https://streams.spec.whatwg.org/#ref-for-transfer-steps②
1850 // Step 3 + 4: If ! Is{Readable,Writable}StreamLocked(value) is true,
1851 // throw a "DataCloneError" DOMException.
1852 return !stream
->Readable()->Locked() && !stream
->Writable()->Locked();
1856 if (StaticPrefs::dom_media_webcodecs_enabled()) {
1857 VideoFrame
* videoframe
= nullptr;
1858 nsresult rv
= UNWRAP_OBJECT(VideoFrame
, &obj
, videoframe
);
1859 if (NS_SUCCEEDED(rv
)) {
1860 SameProcessScopeRequired(aSameProcessScopeRequired
);
1861 return CloneScope() == StructuredCloneScope::SameProcess
;
1865 if (StaticPrefs::dom_media_webcodecs_enabled()) {
1866 mozilla::dom::AudioData
* audioData
= nullptr;
1867 nsresult rv
= UNWRAP_OBJECT(AudioData
, &obj
, audioData
);
1868 if (NS_SUCCEEDED(rv
)) {
1869 SameProcessScopeRequired(aSameProcessScopeRequired
);
1870 return CloneScope() == StructuredCloneScope::SameProcess
;
1877 bool StructuredCloneHolder::TakeTransferredPortsAsSequence(
1878 Sequence
<OwningNonNull
<mozilla::dom::MessagePort
>>& aPorts
) {
1879 nsTArray
<RefPtr
<MessagePort
>> ports
= TakeTransferredPorts();
1882 for (uint32_t i
= 0, len
= ports
.Length(); i
< len
; ++i
) {
1883 if (!aPorts
.AppendElement(ports
[i
].forget(), fallible
)) {
1891 void StructuredCloneHolder::SameProcessScopeRequired(
1892 bool* aSameProcessScopeRequired
) {
1893 MOZ_ASSERT(aSameProcessScopeRequired
);
1894 if (mStructuredCloneScope
== StructuredCloneScope::UnknownDestination
) {
1895 mStructuredCloneScope
= StructuredCloneScope::SameProcess
;
1896 *aSameProcessScopeRequired
= true;
1900 } // namespace mozilla::dom