Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / js / public / StructuredClone.h
blobdb78536701e9d8e821e16422948b958cffc4b51e
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"
15 #include <stdint.h>
16 #include <utility>
18 #include "jstypes.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
46 * network.
49 struct JSStructuredCloneReader;
50 struct JSStructuredCloneWriter;
52 /**
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
86 namespace JS {
88 /**
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
113 * nothing.
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
139 * disk.
141 SameProcess = 1,
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.
156 DifferentProcess,
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
168 * DifferentProcess.
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.)
179 Unassigned,
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.
186 UnknownDestination,
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
194 * memory mapping? */
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.
221 SCTAG_TMO_USER_MIN
224 class CloneDataPolicy {
225 bool allowIntraClusterClonableSharedObjects_;
226 bool allowSharedMemoryObjects_;
228 public:
229 // The default is to deny all policy-controlled aspects.
231 CloneDataPolicy()
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_;
254 } /* namespace JS */
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
263 * function.
265 * Return the new object on success, or raise an exception and return nullptr on
266 * error.
268 typedef JSObject* (*ReadStructuredCloneOp)(
269 JSContext* cx, JSStructuredCloneReader* r,
270 const JS::CloneDataPolicy& cloneDataPolicy, uint32_t tag, uint32_t data,
271 void* closure);
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
284 * normally be set.
286 typedef bool (*WriteStructuredCloneOp)(JSContext* cx,
287 JSStructuredCloneWriter* w,
288 JS::HandleObject obj,
289 bool* sameProcessScopeRequired,
290 void* closure);
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
326 * returns false.
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
338 * callback.
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,
350 void* closure,
351 // Output:
352 uint32_t* tag,
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
373 * is destroyed.
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,
387 void* closure);
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
394 * channel.
396 * If the callback returns false then the clone operation (read or write) will
397 * signal a failure.
399 typedef bool (*SharedArrayBufferClonedOp)(JSContext* cx, bool receiving,
400 void* closure);
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).
434 NoTransferables
437 namespace js {
438 class SharedArrayRawBuffer;
440 class SharedArrayRawBufferRefs {
441 public:
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&&);
451 void releaseAll();
453 private:
454 js::Vector<js::SharedArrayRawBuffer*, 0, js::SystemAllocPolicy> refs_;
457 template <typename T, typename AllocPolicy>
458 struct BufferIterator;
459 } // namespace js
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 {
467 public:
468 using BufferList = mozilla::BufferList<js::SystemAllocPolicy>;
469 using Iterator = BufferList::IterImpl;
471 private:
472 static const size_t kStandardCapacity = 4096;
474 BufferList bufList_;
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;
496 public:
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()),
501 scope_(scope) {}
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)),
508 scope_(scope),
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;
517 closure_ = closure;
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;
529 return scope_;
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");
544 scope_ = newScope;
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,
556 size_t size) const {
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,
569 size_t size) const {
570 MOZ_ASSERT(scope() != JS::StructuredCloneScope::Unassigned);
571 while (size > 0) {
572 size_t remaining = iter.RemainingInSegment();
573 size_t nbytes = std::min(remaining, size);
574 memcpy(iter.Data(), data, nbytes);
575 data += nbytes;
576 size -= nbytes;
577 iter.Advance(bufList_, nbytes);
579 return true;
582 char* AllocateBytes(size_t maxSize, size_t* size) {
583 return bufList_.AllocateBytes(maxSize, size);
586 void Clear() {
587 discardTransferables();
588 bufList_.Clear();
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())) {
612 return false;
614 iter.Advance(bufList_, iter.RemainingInSegment());
616 return true;
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();
632 private:
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
655 * is seen.
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
676 * questionable.
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_;
684 uint32_t version_;
686 public:
687 JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
688 const JSStructuredCloneCallbacks* callbacks,
689 void* closure)
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(); }
703 void clear();
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
719 * `data`.
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);
745 private:
746 // Copy and assignment are not supported.
747 JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& other) =
748 delete;
749 JSAutoStructuredCloneBuffer& operator=(
750 const JSAutoStructuredCloneBuffer& other) = delete;
753 // The range of tag values the application may use for its own custom object
754 // types.
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,
770 uint32_t* p2);
772 JS_PUBLIC_API bool JS_ReadBytes(JSStructuredCloneReader* r, void* p,
773 size_t len);
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,
784 uint32_t data);
786 JS_PUBLIC_API bool JS_WriteBytes(JSStructuredCloneWriter* w, const void* p,
787 size_t len);
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,
795 JS::HandleValue v);
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 */