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 #ifndef js_StructuredClone_h
8 #define js_StructuredClone_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/BufferList.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "mozilla/StringBuffer.h"
20 #include "js/AllocPolicy.h"
21 #include "js/RootingAPI.h"
22 #include "js/TypeDecls.h"
23 #include "js/Vector.h"
26 * API for safe passing of structured data, HTML 2018 Feb 21 section 2.7.
27 * <https://html.spec.whatwg.org/multipage/structured-data.html>
29 * This is a serialization scheme for JS values, somewhat like JSON. It
30 * preserves some aspects of JS objects (strings, numbers, own data properties
31 * with string keys, array elements) but not others (methods, getters and
32 * setters, prototype chains). Unlike JSON, structured data:
34 * - can contain cyclic references.
36 * - handles Maps, Sets, and some other object types.
38 * - supports *transferring* objects of certain types from one realm to
39 * another, rather than cloning them.
41 * - is specified by a living standard, and continues to evolve.
43 * - is encoded in a nonstandard binary format, and is never exposed to Web
44 * content in its serialized form. It's used internally by the browser to
45 * send data from one thread/realm/domain to another, not across the
49 struct JSStructuredCloneReader
;
50 struct JSStructuredCloneWriter
;
53 * The structured-clone serialization format version number.
55 * When serialized data is stored as bytes, e.g. in your Firefox profile, later
56 * versions of the engine may have to read it. When you upgrade Firefox, we
57 * don't crawl through your whole profile converting all saved data from the
58 * previous version of the serialization format to the latest version. So it is
59 * normal to have data in old formats stored in your profile.
61 * The JS engine can *write* data only in the current format version.
63 * It can *read* any data written in the current version, and data written for
64 * DifferentProcess scope in earlier versions.
67 * ## When to bump this version number
69 * When making a change so drastic that the JS engine needs to know whether
70 * it's reading old or new serialized data in order to handle both correctly,
71 * increment this version number. Make sure the engine can still read all
72 * old data written with previous versions.
74 * If StructuredClone.cpp doesn't contain code that distinguishes between
75 * version 8 and version 9, there should not be a version 9.
77 * Do not increment for changes that only affect SameProcess encoding.
79 * Increment only for changes that would otherwise break old serialized data.
80 * Do not increment for new data types. (Rationale: Modulo bugs, older versions
81 * of the JS engine can already correctly throw errors when they encounter new,
82 * unrecognized features. A version number bump does not actually help them.)
84 #define JS_STRUCTURED_CLONE_VERSION 8
89 * Indicates the "scope of validity" of serialized data.
91 * Writing plain JS data produces an array of bytes that can be copied and
92 * read in another process or whatever. The serialized data is Plain Old Data.
93 * However, HTML also supports `Transferable` objects, which, when cloned, can
94 * be moved from the source object into the clone, like when you take a
95 * photograph of someone and it steals their soul.
96 * See <https://developer.mozilla.org/en-US/docs/Web/API/Transferable>.
97 * We support cloning and transferring objects of many types.
99 * For example, when we transfer an ArrayBuffer (within a process), we "detach"
100 * the ArrayBuffer, embed the raw buffer pointer in the serialized data, and
101 * later install it in a new ArrayBuffer in the destination realm. Ownership
102 * of that buffer memory is transferred from the original ArrayBuffer to the
103 * serialized data and then to the clone.
105 * This only makes sense within a single address space. When we transfer an
106 * ArrayBuffer to another process, the contents of the buffer must be copied
107 * into the serialized data. (The original ArrayBuffer is still detached,
108 * though, for consistency; in some cases the caller shouldn't know or care if
109 * the recipient is in the same process.)
111 * ArrayBuffers are actually a lucky case; some objects (like MessagePorts)
112 * can't reasonably be stored by value in serialized data -- it's pointers or
115 * So there is a tradeoff between scope of validity -- how far away the
116 * serialized data may be sent and still make sense -- and efficiency or
117 * features. The read and write algorithms therefore take an argument of this
118 * type, allowing the user to control those trade-offs.
120 enum class StructuredCloneScope
: uint32_t {
122 * The most restrictive scope, with greatest efficiency and features.
124 * When writing, this means: The caller promises that the serialized data
125 * will **not** be shipped off to a different process or stored in a
126 * database. However, it may be shipped to another thread. It's OK to
127 * produce serialized data that contains pointers to data that is safe to
128 * send across threads, such as array buffers. In Rust terms, the
129 * serialized data will be treated as `Send` but not `Copy`.
131 * When reading, this means: Accept transferred objects and buffers
132 * (pointers). The caller promises that the serialized data was written
133 * using this API (otherwise, the serialized data may contain bogus
134 * pointers, leading to undefined behavior).
136 * Starts from 1 because there used to be a SameProcessSameThread enum value
137 * of 0 and these values are encoded into the structured serialization format
138 * as part of the SCTAG_HEADER, and IndexedDB persists the representation to
144 * When writing, this means we're writing for an audience in a different
145 * process. Produce serialized data that can be sent to other processes,
146 * bitwise copied, or even stored as bytes in a database and read by later
147 * versions of Firefox years from now. The HTML5 spec refers to this as
148 * "ForStorage" as in StructuredSerializeForStorage, though we use
149 * DifferentProcess for IPC as well as storage.
151 * Transferable objects are limited to ArrayBuffers, whose contents are
152 * copied into the serialized data (rather than just writing a pointer).
154 * When reading, this means: Do not accept pointers.
159 * Values greater than this are temporary markers used when the actual scope
160 * is not yet known. The allowed scope will be resolved by the time
161 * readHeader() is complete.
163 LastResolvedScope
= DifferentProcess
,
166 * Handle a backwards-compatibility case with IndexedDB (bug 1434308): when
167 * reading, this means to treat legacy SameProcess data as if it were
170 * Do not use this for writing; use DifferentProcess instead.
172 DifferentProcessForIndexedDB
,
175 * Existing code wants to be able to create an uninitialized
176 * JSStructuredCloneData without knowing the scope, then populate it with
177 * data (at which point the scope *is* known.)
182 * This scope is used when the deserialization context is unknown. When
183 * writing, DifferentProcess or SameProcess scope is chosen based on the
184 * nature of the object.
189 /** Values used to describe the ownership individual Transferables.
191 * Note that these *can* show up in DifferentProcess clones, since
192 * DifferentProcess ArrayBuffers can be Transferred. In that case, this will
193 * distinguish the specific ownership mechanism: is it a malloc pointer or a
195 enum TransferableOwnership
{
196 /** Transferable data has not been filled in yet. */
197 SCTAG_TMO_UNFILLED
= 0,
199 /** Structured clone buffer does not yet own the data. */
200 SCTAG_TMO_UNOWNED
= 1,
202 /** All enum values at least this large are owned by the clone buffer. */
203 SCTAG_TMO_FIRST_OWNED
= 2,
205 /** Data is a pointer that can be freed. */
206 SCTAG_TMO_ALLOC_DATA
= SCTAG_TMO_FIRST_OWNED
,
208 /** Data is a memory mapped pointer. */
209 SCTAG_TMO_MAPPED_DATA
= 3,
212 * Data is embedding-specific. The engine can free it by calling the
213 * freeTransfer op. */
214 SCTAG_TMO_CUSTOM
= 4,
217 * Same as SCTAG_TMO_CUSTOM, but the embedding can also use
218 * SCTAG_TMO_USER_MIN and greater, up to 2^32-1, to distinguish specific
219 * ownership variants.
224 class CloneDataPolicy
{
225 bool allowIntraClusterClonableSharedObjects_
;
226 bool allowSharedMemoryObjects_
;
229 // The default is to deny all policy-controlled aspects.
232 : allowIntraClusterClonableSharedObjects_(false),
233 allowSharedMemoryObjects_(false) {}
235 // SharedArrayBuffers and WASM modules can only be cloned intra-process
236 // because the shared memory areas are allocated in process-private memory or
237 // because there are security issues of sharing them cross agent clusters.
238 // y default, we don't allow shared-memory and intra-cluster objects. Clients
239 // should therefore enable these 2 clone features when needed.
241 void allowIntraClusterClonableSharedObjects() {
242 allowIntraClusterClonableSharedObjects_
= true;
244 bool areIntraClusterClonableSharedObjectsAllowed() const {
245 return allowIntraClusterClonableSharedObjects_
;
248 void allowSharedMemoryObjects() { allowSharedMemoryObjects_
= true; }
249 bool areSharedMemoryObjectsAllowed() const {
250 return allowSharedMemoryObjects_
;
257 * Read structured data from the reader r. This hook is used to read a value
258 * previously serialized by a call to the WriteStructuredCloneOp hook.
260 * tag and data are the pair of uint32_t values from the header. The callback
261 * may use the JS_Read* APIs to read any other relevant parts of the object
262 * from the reader r. closure is any value passed to the JS_ReadStructuredClone
265 * Return the new object on success, or raise an exception and return nullptr on
268 typedef JSObject
* (*ReadStructuredCloneOp
)(
269 JSContext
* cx
, JSStructuredCloneReader
* r
,
270 const JS::CloneDataPolicy
& cloneDataPolicy
, uint32_t tag
, uint32_t data
,
274 * Structured data serialization hook. The engine can write primitive values,
275 * Objects, Arrays, Dates, RegExps, TypedArrays, ArrayBuffers, Sets, Maps,
276 * and SharedTypedArrays. Any other type of object requires application support.
277 * This callback must first use the JS_WriteUint32Pair API to write an object
278 * header, passing a value greater than JS_SCTAG_USER to the tag parameter.
279 * Then it can use the JS_Write* APIs to write any other relevant parts of
280 * the value v to the writer w. closure is any value passed to the
281 * JS_WriteStructuredClone function.
283 * Return true on success, false on error. On error, an exception should
286 typedef bool (*WriteStructuredCloneOp
)(JSContext
* cx
,
287 JSStructuredCloneWriter
* w
,
288 JS::HandleObject obj
,
289 bool* sameProcessScopeRequired
,
293 * This is called when serialization or deserialization encounters an error.
294 * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
295 * with error set to one of the JS_SCERR_* values.
297 * Note that if the .reportError field of the JSStructuredCloneCallbacks is
298 * set (to a function with this signature), then an exception will *not* be
299 * set on the JSContext when an error is encountered. The clone operation
300 * will still be aborted and will return false, however, so it is up to the
301 * embedding to do what it needs to for the error.
303 * Example: for the DOM, mozilla::dom::StructuredCloneHolder will save away
304 * the error message during its reportError callback. Then when the overall
305 * operation fails, it will clear any exception that might have been set
306 * from other ways to fail and pass the saved error message to
307 * ErrorResult::ThrowDataCloneError().
309 typedef void (*StructuredCloneErrorOp
)(JSContext
* cx
, uint32_t errorid
,
310 void* closure
, const char* errorMessage
);
313 * This is called when JS_ReadStructuredClone receives a transferable object
314 * not known to the engine. If this hook does not exist or returns false, the
315 * JS engine calls the reportError op if set, otherwise it throws a
316 * DATA_CLONE_ERR DOM Exception. This method is called before any other
317 * callback and must return a non-null object in returnObject on success.
319 * If this readTransfer() hook is called and produces an object, then the
320 * read() hook will *not* be called for the same object, since the main data
321 * will only contain a backreference to the already-read object.
323 * The clone buffer will relinquish ownership of this Transferable if and only
324 * if this hook returns true -- as in, the freeTransfer hook will not be called
325 * on this entry if this hook returns true, but it will still be called if it
328 typedef bool (*ReadTransferStructuredCloneOp
)(
329 JSContext
* cx
, JSStructuredCloneReader
* r
,
330 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t tag
, void* content
,
331 uint64_t extraData
, void* closure
, JS::MutableHandleObject returnObject
);
334 * Called when JS_WriteStructuredClone receives a transferable object not
335 * handled by the engine. If this hook does not exist or returns false, the JS
336 * engine will call the reportError hook or fall back to throwing a
337 * DATA_CLONE_ERR DOM Exception. This method is called before any other
340 * tag: indicates what type of transferable this is. Must be greater than
341 * 0xFFFF0201 (value of the internal SCTAG_TRANSFER_MAP_PENDING_ENTRY)
343 * ownership: see TransferableOwnership, above. Used to communicate any needed
344 * ownership info to the FreeTransferStructuredCloneOp.
346 * content, extraData: what the ReadTransferStructuredCloneOp will receive
348 typedef bool (*TransferStructuredCloneOp
)(JSContext
* cx
,
349 JS::Handle
<JSObject
*> obj
,
353 JS::TransferableOwnership
* ownership
,
354 void** content
, uint64_t* extraData
);
357 * Called when freeing a transferable handled by the embedding. Note that it
358 * should never trigger a garbage collection (and will assert in a
359 * debug build if it does.)
361 * This callback will be used to release ownership in three situations:
363 * 1. During serialization: an object is Transferred from, then an error is
364 * encountered later and the incomplete serialization is discarded.
366 * 2. During deserialization: before an object is Transferred to, an error
367 * is encountered and the incompletely deserialized clone is discarded. This
368 * will happen with internally-implemented Transferables as well as those
369 * where the readTransfer hook returns false.
371 * 3. Serialized data that includes Transferring is never deserialized (eg when
372 * the receiver disappears before reading in the message), and the clone data
376 typedef void (*FreeTransferStructuredCloneOp
)(
377 uint32_t tag
, JS::TransferableOwnership ownership
, void* content
,
378 uint64_t extraData
, void* closure
);
381 * Called when the transferring objects are checked. If this function returns
382 * false, the serialization ends throwing a DataCloneError exception.
384 typedef bool (*CanTransferStructuredCloneOp
)(JSContext
* cx
,
385 JS::Handle
<JSObject
*> obj
,
386 bool* sameProcessScopeRequired
,
390 * Called when a SharedArrayBuffer (including one owned by a Wasm memory object)
391 * has been processed in context `cx` by structured cloning. If `receiving` is
392 * true then the SAB has been received from a channel and a new SAB object has
393 * been created; if false then an existing SAB has been serialized onto a
396 * If the callback returns false then the clone operation (read or write) will
399 typedef bool (*SharedArrayBufferClonedOp
)(JSContext
* cx
, bool receiving
,
402 struct JSStructuredCloneCallbacks
{
403 ReadStructuredCloneOp read
;
404 WriteStructuredCloneOp write
;
405 StructuredCloneErrorOp reportError
;
406 ReadTransferStructuredCloneOp readTransfer
;
407 TransferStructuredCloneOp writeTransfer
;
408 FreeTransferStructuredCloneOp freeTransfer
;
409 CanTransferStructuredCloneOp canTransfer
;
410 SharedArrayBufferClonedOp sabCloned
;
413 enum OwnTransferablePolicy
{
415 * The buffer owns any Transferables that it might contain, and should
416 * properly release them upon destruction.
418 OwnsTransferablesIfAny
,
421 * Do not free any Transferables within this buffer when deleting it. This
422 * is used to mark a clone buffer as containing data from another process,
423 * and so it can't legitimately contain pointers. If the buffer claims to
424 * have transferables, it's a bug or an attack. This is also used for
425 * abandon(), where a buffer still contains raw data but the ownership has
426 * been given over to some other entity.
428 IgnoreTransferablesIfAny
,
431 * A buffer that cannot contain Transferables at all. This usually means
432 * the buffer is empty (not yet filled in, or having been cleared).
438 class SharedArrayRawBuffer
;
440 class SharedArrayRawBufferRefs
{
442 SharedArrayRawBufferRefs() = default;
443 SharedArrayRawBufferRefs(SharedArrayRawBufferRefs
&& other
) = default;
444 SharedArrayRawBufferRefs
& operator=(SharedArrayRawBufferRefs
&& other
);
445 ~SharedArrayRawBufferRefs();
447 [[nodiscard
]] bool acquire(JSContext
* cx
, SharedArrayRawBuffer
* rawbuf
);
448 [[nodiscard
]] bool acquireAll(JSContext
* cx
,
449 const SharedArrayRawBufferRefs
& that
);
450 void takeOwnership(SharedArrayRawBufferRefs
&&);
454 js::Vector
<js::SharedArrayRawBuffer
*, 0, js::SystemAllocPolicy
> refs_
;
457 template <typename T
, typename AllocPolicy
>
458 struct BufferIterator
;
462 * JSStructuredCloneData represents structured clone data together with the
463 * information needed to read/write/transfer/free the records within it, in the
464 * form of a set of callbacks.
466 class MOZ_NON_MEMMOVABLE JS_PUBLIC_API JSStructuredCloneData
{
468 using BufferList
= mozilla::BufferList
<js::SystemAllocPolicy
>;
469 using Iterator
= BufferList::IterImpl
;
472 static const size_t kStandardCapacity
= 4096;
476 // The (address space, thread) scope within which this clone is valid. Note
477 // that this must be either set during construction, or start out as
478 // Unassigned and transition once to something else.
479 JS::StructuredCloneScope scope_
;
481 const JSStructuredCloneCallbacks
* callbacks_
= nullptr;
482 void* closure_
= nullptr;
483 OwnTransferablePolicy ownTransferables_
=
484 OwnTransferablePolicy::NoTransferables
;
485 js::SharedArrayRawBufferRefs refsHeld_
;
487 using StringBuffers
=
488 js::Vector
<RefPtr
<mozilla::StringBuffer
>, 4, js::SystemAllocPolicy
>;
489 StringBuffers stringBufferRefsHeld_
;
491 friend struct JSStructuredCloneWriter
;
492 friend class JS_PUBLIC_API JSAutoStructuredCloneBuffer
;
493 template <typename T
, typename AllocPolicy
>
494 friend struct js::BufferIterator
;
497 // The constructor must be infallible but SystemAllocPolicy is not, so both
498 // the initial size and initial capacity of the BufferList must be zero.
499 explicit JSStructuredCloneData(JS::StructuredCloneScope scope
)
500 : bufList_(0, 0, kStandardCapacity
, js::SystemAllocPolicy()),
503 // Steal the raw data from a BufferList. In this case, we don't know the
504 // scope and none of the callback info is assigned yet.
505 JSStructuredCloneData(BufferList
&& buffers
, JS::StructuredCloneScope scope
,
506 OwnTransferablePolicy ownership
)
507 : bufList_(std::move(buffers
)),
509 ownTransferables_(ownership
) {}
510 JSStructuredCloneData(JSStructuredCloneData
&& other
) = default;
511 JSStructuredCloneData
& operator=(JSStructuredCloneData
&& other
) = default;
512 ~JSStructuredCloneData();
514 void setCallbacks(const JSStructuredCloneCallbacks
* callbacks
, void* closure
,
515 OwnTransferablePolicy policy
) {
516 callbacks_
= callbacks
;
518 ownTransferables_
= policy
;
521 [[nodiscard
]] bool Init(size_t initialCapacity
= 0) {
522 return bufList_
.Init(0, initialCapacity
);
525 JS::StructuredCloneScope
scope() const {
526 if (scope_
== JS::StructuredCloneScope::UnknownDestination
) {
527 return JS::StructuredCloneScope::DifferentProcess
;
532 void sameProcessScopeRequired() {
533 if (scope_
== JS::StructuredCloneScope::UnknownDestination
) {
534 scope_
= JS::StructuredCloneScope::SameProcess
;
538 void initScope(JS::StructuredCloneScope newScope
) {
539 MOZ_ASSERT(Size() == 0, "initScope() of nonempty JSStructuredCloneData");
540 if (scope() != JS::StructuredCloneScope::Unassigned
) {
541 MOZ_ASSERT(scope() == newScope
,
542 "Cannot change scope after it has been initialized");
547 size_t Size() const { return bufList_
.Size(); }
549 const Iterator
Start() const { return bufList_
.Iter(); }
551 [[nodiscard
]] bool Advance(Iterator
& iter
, size_t distance
) const {
552 return iter
.AdvanceAcrossSegments(bufList_
, distance
);
555 [[nodiscard
]] bool ReadBytes(Iterator
& iter
, char* buffer
,
557 return bufList_
.ReadBytes(iter
, buffer
, size
);
560 // Append new data to the end of the buffer.
561 [[nodiscard
]] bool AppendBytes(const char* data
, size_t size
) {
562 MOZ_ASSERT(scope() != JS::StructuredCloneScope::Unassigned
);
563 return bufList_
.WriteBytes(data
, size
);
566 // Update data stored within the existing buffer. There must be at least
567 // 'size' bytes between the position of 'iter' and the end of the buffer.
568 [[nodiscard
]] bool UpdateBytes(Iterator
& iter
, const char* data
,
570 MOZ_ASSERT(scope() != JS::StructuredCloneScope::Unassigned
);
572 size_t remaining
= iter
.RemainingInSegment();
573 size_t nbytes
= std::min(remaining
, size
);
574 memcpy(iter
.Data(), data
, nbytes
);
577 iter
.Advance(bufList_
, nbytes
);
582 char* AllocateBytes(size_t maxSize
, size_t* size
) {
583 return bufList_
.AllocateBytes(maxSize
, size
);
587 discardTransferables();
591 // Return a new read-only JSStructuredCloneData that "borrows" the contents
592 // of |this|. Its lifetime should not exceed the donor's. This is only
593 // allowed for DifferentProcess clones, so finalization of the borrowing
594 // clone will do nothing.
595 JSStructuredCloneData
Borrow(Iterator
& iter
, size_t size
,
596 bool* success
) const {
597 MOZ_ASSERT(scope() == JS::StructuredCloneScope::DifferentProcess
);
598 return JSStructuredCloneData(
599 bufList_
.Borrow
<js::SystemAllocPolicy
>(iter
, size
, success
), scope(),
600 IgnoreTransferablesIfAny
);
603 // Iterate over all contained data, one BufferList segment's worth at a
604 // time, and invoke the given FunctionToApply with the data pointer and
605 // size. The function should return a bool value, and this loop will exit
606 // with false if the function ever returns false.
607 template <typename FunctionToApply
>
608 bool ForEachDataChunk(FunctionToApply
&& function
) const {
609 Iterator iter
= bufList_
.Iter();
610 while (!iter
.Done()) {
611 if (!function(iter
.Data(), iter
.RemainingInSegment())) {
614 iter
.Advance(bufList_
, iter
.RemainingInSegment());
619 // Append the entire contents of other's bufList_ to our own.
620 [[nodiscard
]] bool Append(const JSStructuredCloneData
& other
) {
621 MOZ_ASSERT(scope() == other
.scope());
622 return other
.ForEachDataChunk(
623 [&](const char* data
, size_t size
) { return AppendBytes(data
, size
); });
626 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) {
627 return bufList_
.SizeOfExcludingThis(mallocSizeOf
);
630 void discardTransferables();
633 // This internal method exposes the real value of scope_. It's meant to be
634 // used only when starting the writing.
635 JS::StructuredCloneScope
scopeForInternalWriting() const { return scope_
; }
639 * Implements StructuredDeserialize and StructuredDeserializeWithTransfer.
641 * Note: If `data` contains transferable objects, it can be read only once.
643 JS_PUBLIC_API
bool JS_ReadStructuredClone(
644 JSContext
* cx
, const JSStructuredCloneData
& data
, uint32_t version
,
645 JS::StructuredCloneScope scope
, JS::MutableHandleValue vp
,
646 const JS::CloneDataPolicy
& cloneDataPolicy
,
647 const JSStructuredCloneCallbacks
* optionalCallbacks
, void* closure
);
650 * Implements StructuredSerialize, StructuredSerializeForStorage, and
651 * StructuredSerializeWithTransfer.
653 * Note: If the scope is DifferentProcess then the cloneDataPolicy must deny
654 * shared-memory objects, or an error will be signaled if a shared memory object
657 JS_PUBLIC_API
bool JS_WriteStructuredClone(
658 JSContext
* cx
, JS::HandleValue v
, JSStructuredCloneData
* data
,
659 JS::StructuredCloneScope scope
, const JS::CloneDataPolicy
& cloneDataPolicy
,
660 const JSStructuredCloneCallbacks
* optionalCallbacks
, void* closure
,
661 JS::HandleValue transferable
);
663 JS_PUBLIC_API
bool JS_StructuredCloneHasTransferables(
664 JSStructuredCloneData
& data
, bool* hasTransferable
);
666 JS_PUBLIC_API
bool JS_StructuredClone(
667 JSContext
* cx
, JS::HandleValue v
, JS::MutableHandleValue vp
,
668 const JSStructuredCloneCallbacks
* optionalCallbacks
, void* closure
);
671 * The C-style API calls to read and write structured clones are fragile --
672 * they rely on the caller to properly handle ownership of the clone data, and
673 * the handling of the input data as well as the interpretation of the contents
674 * of the clone buffer are dependent on the callbacks passed in. If you
675 * serialize and deserialize with different callbacks, the results are
678 * JSAutoStructuredCloneBuffer wraps things up in an RAII class for data
679 * management, and uses the same callbacks for both writing and reading
680 * (serializing and deserializing).
682 class JS_PUBLIC_API JSAutoStructuredCloneBuffer
{
683 JSStructuredCloneData data_
;
687 JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope
,
688 const JSStructuredCloneCallbacks
* callbacks
,
690 : data_(scope
), version_(JS_STRUCTURED_CLONE_VERSION
) {
691 data_
.setCallbacks(callbacks
, closure
,
692 OwnTransferablePolicy::NoTransferables
);
695 JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer
&& other
);
696 JSAutoStructuredCloneBuffer
& operator=(JSAutoStructuredCloneBuffer
&& other
);
698 ~JSAutoStructuredCloneBuffer() { clear(); }
700 JSStructuredCloneData
& data() { return data_
; }
701 bool empty() const { return !data_
.Size(); }
705 JS::StructuredCloneScope
scope() const { return data_
.scope(); }
708 * Adopt some memory. It will be automatically freed by the destructor.
709 * data must have been allocated by the JS engine (e.g., extracted via
710 * JSAutoStructuredCloneBuffer::steal).
712 void adopt(JSStructuredCloneData
&& data
,
713 uint32_t version
= JS_STRUCTURED_CLONE_VERSION
,
714 const JSStructuredCloneCallbacks
* callbacks
= nullptr,
715 void* closure
= nullptr);
718 * Release ownership of the buffer and assign it and ownership of it to
721 void giveTo(JSStructuredCloneData
* data
);
723 bool read(JSContext
* cx
, JS::MutableHandleValue vp
,
724 const JS::CloneDataPolicy
& cloneDataPolicy
= JS::CloneDataPolicy(),
725 const JSStructuredCloneCallbacks
* optionalCallbacks
= nullptr,
726 void* closure
= nullptr);
728 bool write(JSContext
* cx
, JS::HandleValue v
,
729 const JSStructuredCloneCallbacks
* optionalCallbacks
= nullptr,
730 void* closure
= nullptr);
732 bool write(JSContext
* cx
, JS::HandleValue v
, JS::HandleValue transferable
,
733 const JS::CloneDataPolicy
& cloneDataPolicy
,
734 const JSStructuredCloneCallbacks
* optionalCallbacks
= nullptr,
735 void* closure
= nullptr);
737 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) {
738 return data_
.SizeOfExcludingThis(mallocSizeOf
);
741 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) {
742 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf
);
746 // Copy and assignment are not supported.
747 JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer
& other
) =
749 JSAutoStructuredCloneBuffer
& operator=(
750 const JSAutoStructuredCloneBuffer
& other
) = delete;
753 // The range of tag values the application may use for its own custom object
755 #define JS_SCTAG_USER_MIN ((uint32_t)0xFFFF8000)
756 #define JS_SCTAG_USER_MAX ((uint32_t)0xFFFFFFFF)
758 #define JS_SCERR_RECURSION 0
759 #define JS_SCERR_TRANSFERABLE 1
760 #define JS_SCERR_DUP_TRANSFERABLE 2
761 #define JS_SCERR_UNSUPPORTED_TYPE 3
762 #define JS_SCERR_SHMEM_TRANSFERABLE 4
763 #define JS_SCERR_TYPED_ARRAY_DETACHED 5
764 #define JS_SCERR_WASM_NO_TRANSFER 6
765 #define JS_SCERR_NOT_CLONABLE 7
766 #define JS_SCERR_NOT_CLONABLE_WITH_COOP_COEP 8
767 #define JS_SCERR_TRANSFERABLE_TWICE 9
769 JS_PUBLIC_API
bool JS_ReadUint32Pair(JSStructuredCloneReader
* r
, uint32_t* p1
,
772 JS_PUBLIC_API
bool JS_ReadBytes(JSStructuredCloneReader
* r
, void* p
,
775 JS_PUBLIC_API
bool JS_ReadString(JSStructuredCloneReader
* r
,
776 JS::MutableHandleString str
);
778 JS_PUBLIC_API
bool JS_ReadDouble(JSStructuredCloneReader
* r
, double* v
);
780 JS_PUBLIC_API
bool JS_ReadTypedArray(JSStructuredCloneReader
* r
,
781 JS::MutableHandleValue vp
);
783 JS_PUBLIC_API
bool JS_WriteUint32Pair(JSStructuredCloneWriter
* w
, uint32_t tag
,
786 JS_PUBLIC_API
bool JS_WriteBytes(JSStructuredCloneWriter
* w
, const void* p
,
789 JS_PUBLIC_API
bool JS_WriteString(JSStructuredCloneWriter
* w
,
790 JS::HandleString str
);
792 JS_PUBLIC_API
bool JS_WriteDouble(JSStructuredCloneWriter
* w
, double v
);
794 JS_PUBLIC_API
bool JS_WriteTypedArray(JSStructuredCloneWriter
* w
,
797 JS_PUBLIC_API
bool JS_ObjectNotWritten(JSStructuredCloneWriter
* w
,
798 JS::HandleObject obj
);
800 JS_PUBLIC_API
JS::StructuredCloneScope
JS_GetStructuredCloneScope(
801 JSStructuredCloneWriter
* w
);
803 #endif /* js_StructuredClone_h */