StructuredClone.h (31732B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef js_StructuredClone_h 8 #define js_StructuredClone_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/BufferList.h" 12 #include "mozilla/MemoryReporting.h" 13 #include "mozilla/StringBuffer.h" 14 15 #include <stdint.h> 16 #include <utility> 17 18 #include "jstypes.h" 19 20 #include "js/AllocPolicy.h" 21 #include "js/RootingAPI.h" 22 #include "js/TypeDecls.h" 23 #include "js/Vector.h" 24 25 /* 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> 28 * 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: 33 * 34 * - can contain cyclic references. 35 * 36 * - handles Maps, Sets, and some other object types. 37 * 38 * - supports *transferring* objects of certain types from one realm to 39 * another, rather than cloning them. 40 * 41 * - is specified by a living standard, and continues to evolve. 42 * 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. 47 */ 48 49 struct JSStructuredCloneReader; 50 struct JSStructuredCloneWriter; 51 52 /** 53 * The structured-clone serialization format version number. 54 * 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. 60 * 61 * The JS engine can *write* data only in the current format version. 62 * 63 * It can *read* any data written in the current version, and data written for 64 * DifferentProcess scope in earlier versions. 65 * 66 * 67 * ## When to bump this version number 68 * 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. 73 * 74 * If StructuredClone.cpp doesn't contain code that distinguishes between 75 * version 8 and version 9, there should not be a version 9. 76 * 77 * Do not increment for changes that only affect SameProcess encoding. 78 * 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.) 83 */ 84 #define JS_STRUCTURED_CLONE_VERSION 8 85 86 namespace JS { 87 88 /** 89 * Indicates the "scope of validity" of serialized data. 90 * 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. 98 * 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. 104 * 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.) 110 * 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. 114 * 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. 119 */ 120 enum class StructuredCloneScope : uint32_t { 121 /** 122 * The most restrictive scope, with greatest efficiency and features. 123 * 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`. 130 * 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). 135 * 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. 140 */ 141 SameProcess = 1, 142 143 /** 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. 150 * 151 * Transferable objects are limited to ArrayBuffers, whose contents are 152 * copied into the serialized data (rather than just writing a pointer). 153 * 154 * When reading, this means: Do not accept pointers. 155 */ 156 DifferentProcess, 157 158 /** 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. 162 */ 163 LastResolvedScope = DifferentProcess, 164 165 /** 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. 169 * 170 * Do not use this for writing; use DifferentProcess instead. 171 */ 172 DifferentProcessForIndexedDB, 173 174 /** 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.) 178 */ 179 Unassigned, 180 181 /** 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. 185 */ 186 UnknownDestination, 187 }; 188 189 /** Values used to describe the ownership individual Transferables. 190 * 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, 198 199 /** Structured clone buffer does not yet own the data. */ 200 SCTAG_TMO_UNOWNED = 1, 201 202 /** All enum values at least this large are owned by the clone buffer. */ 203 SCTAG_TMO_FIRST_OWNED = 2, 204 205 /** Data is a pointer that can be freed. */ 206 SCTAG_TMO_ALLOC_DATA = SCTAG_TMO_FIRST_OWNED, 207 208 /** Data is a memory mapped pointer. */ 209 SCTAG_TMO_MAPPED_DATA = 3, 210 211 /** 212 * Data is embedding-specific. The engine can free it by calling the 213 * freeTransfer op. */ 214 SCTAG_TMO_CUSTOM = 4, 215 216 /** 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. 220 */ 221 SCTAG_TMO_USER_MIN 222 }; 223 224 class CloneDataPolicy { 225 bool allowIntraClusterClonableSharedObjects_; 226 bool allowSharedMemoryObjects_; 227 228 public: 229 // The default is to deny all policy-controlled aspects. 230 231 CloneDataPolicy() 232 : allowIntraClusterClonableSharedObjects_(false), 233 allowSharedMemoryObjects_(false) {} 234 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. 240 241 void allowIntraClusterClonableSharedObjects() { 242 allowIntraClusterClonableSharedObjects_ = true; 243 } 244 bool areIntraClusterClonableSharedObjectsAllowed() const { 245 return allowIntraClusterClonableSharedObjects_; 246 } 247 248 void allowSharedMemoryObjects() { allowSharedMemoryObjects_ = true; } 249 bool areSharedMemoryObjectsAllowed() const { 250 return allowSharedMemoryObjects_; 251 } 252 }; 253 254 } /* namespace JS */ 255 256 /** 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. 259 * 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. 264 * 265 * Return the new object on success, or raise an exception and return nullptr on 266 * error. 267 */ 268 typedef JSObject* (*ReadStructuredCloneOp)( 269 JSContext* cx, JSStructuredCloneReader* r, 270 const JS::CloneDataPolicy& cloneDataPolicy, uint32_t tag, uint32_t data, 271 void* closure); 272 273 /** 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. 282 * 283 * Return true on success, false on error. On error, an exception should 284 * normally be set. 285 */ 286 typedef bool (*WriteStructuredCloneOp)(JSContext* cx, 287 JSStructuredCloneWriter* w, 288 JS::HandleObject obj, 289 bool* sameProcessScopeRequired, 290 void* closure); 291 292 /** 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. 296 * 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. 302 * 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(). 308 */ 309 typedef void (*StructuredCloneErrorOp)(JSContext* cx, uint32_t errorid, 310 void* closure, const char* errorMessage); 311 312 /** 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. 318 * 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. 322 * 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. 327 */ 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); 332 333 /** 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. 339 * 340 * tag: indicates what type of transferable this is. Must be greater than 341 * 0xFFFF0201 (value of the internal SCTAG_TRANSFER_MAP_PENDING_ENTRY) 342 * 343 * ownership: see TransferableOwnership, above. Used to communicate any needed 344 * ownership info to the FreeTransferStructuredCloneOp. 345 * 346 * content, extraData: what the ReadTransferStructuredCloneOp will receive 347 */ 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); 355 356 /** 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.) 360 * 361 * This callback will be used to release ownership in three situations: 362 * 363 * 1. During serialization: an object is Transferred from, then an error is 364 * encountered later and the incomplete serialization is discarded. 365 * 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. 370 * 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. 374 * 375 */ 376 typedef void (*FreeTransferStructuredCloneOp)( 377 uint32_t tag, JS::TransferableOwnership ownership, void* content, 378 uint64_t extraData, void* closure); 379 380 /** 381 * Called when the transferring objects are checked. If this function returns 382 * false, the serialization ends throwing a DataCloneError exception. 383 */ 384 typedef bool (*CanTransferStructuredCloneOp)(JSContext* cx, 385 JS::Handle<JSObject*> obj, 386 bool* sameProcessScopeRequired, 387 void* closure); 388 389 /** 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. 395 * 396 * If the callback returns false then the clone operation (read or write) will 397 * signal a failure. 398 */ 399 typedef bool (*SharedArrayBufferClonedOp)(JSContext* cx, bool receiving, 400 void* closure); 401 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; 411 }; 412 413 enum OwnTransferablePolicy { 414 /** 415 * The buffer owns any Transferables that it might contain, and should 416 * properly release them upon destruction. 417 */ 418 OwnsTransferablesIfAny, 419 420 /** 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. 427 */ 428 IgnoreTransferablesIfAny, 429 430 /** 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). 433 */ 434 NoTransferables 435 }; 436 437 namespace js { 438 class SharedArrayRawBuffer; 439 440 class SharedArrayRawBufferRefs { 441 public: 442 SharedArrayRawBufferRefs() = default; 443 SharedArrayRawBufferRefs(SharedArrayRawBufferRefs&& other) = default; 444 SharedArrayRawBufferRefs& operator=(SharedArrayRawBufferRefs&& other); 445 ~SharedArrayRawBufferRefs(); 446 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(); 452 453 private: 454 js::Vector<js::SharedArrayRawBuffer*, 0, js::SystemAllocPolicy> refs_; 455 }; 456 457 template <typename T, typename AllocPolicy> 458 struct BufferIterator; 459 } // namespace js 460 461 /** 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. 465 */ 466 class MOZ_NON_MEMMOVABLE JS_PUBLIC_API JSStructuredCloneData { 467 public: 468 using BufferList = mozilla::BufferList<js::SystemAllocPolicy>; 469 using Iterator = BufferList::IterImpl; 470 471 private: 472 static const size_t kStandardCapacity = 4096; 473 474 BufferList bufList_; 475 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_; 480 481 const JSStructuredCloneCallbacks* callbacks_ = nullptr; 482 void* closure_ = nullptr; 483 OwnTransferablePolicy ownTransferables_ = 484 OwnTransferablePolicy::NoTransferables; 485 js::SharedArrayRawBufferRefs refsHeld_; 486 487 using StringBuffers = 488 js::Vector<RefPtr<mozilla::StringBuffer>, 4, js::SystemAllocPolicy>; 489 StringBuffers stringBufferRefsHeld_; 490 491 friend struct JSStructuredCloneWriter; 492 friend class JS_PUBLIC_API JSAutoStructuredCloneBuffer; 493 template <typename T, typename AllocPolicy> 494 friend struct js::BufferIterator; 495 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) {} 502 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(); 513 514 void setCallbacks(const JSStructuredCloneCallbacks* callbacks, void* closure, 515 OwnTransferablePolicy policy) { 516 callbacks_ = callbacks; 517 closure_ = closure; 518 ownTransferables_ = policy; 519 } 520 521 [[nodiscard]] bool Init(size_t initialCapacity = 0) { 522 return bufList_.Init(0, initialCapacity); 523 } 524 525 JS::StructuredCloneScope scope() const { 526 if (scope_ == JS::StructuredCloneScope::UnknownDestination) { 527 return JS::StructuredCloneScope::DifferentProcess; 528 } 529 return scope_; 530 } 531 532 void sameProcessScopeRequired() { 533 if (scope_ == JS::StructuredCloneScope::UnknownDestination) { 534 scope_ = JS::StructuredCloneScope::SameProcess; 535 } 536 } 537 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"); 543 } 544 scope_ = newScope; 545 } 546 547 size_t Size() const { return bufList_.Size(); } 548 549 const Iterator Start() const { return bufList_.Iter(); } 550 551 [[nodiscard]] bool Advance(Iterator& iter, size_t distance) const { 552 return iter.AdvanceAcrossSegments(bufList_, distance); 553 } 554 555 [[nodiscard]] bool ReadBytes(Iterator& iter, char* buffer, 556 size_t size) const { 557 return bufList_.ReadBytes(iter, buffer, size); 558 } 559 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); 564 } 565 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); 578 } 579 return true; 580 } 581 582 char* AllocateBytes(size_t maxSize, size_t* size) { 583 return bufList_.AllocateBytes(maxSize, size); 584 } 585 586 void Clear() { 587 discardTransferables(); 588 bufList_.Clear(); 589 } 590 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); 601 } 602 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; 613 } 614 iter.Advance(bufList_, iter.RemainingInSegment()); 615 } 616 return true; 617 } 618 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); }); 624 } 625 626 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { 627 return bufList_.SizeOfExcludingThis(mallocSizeOf); 628 } 629 630 void discardTransferables(); 631 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_; } 636 }; 637 638 /** 639 * Implements StructuredDeserialize and StructuredDeserializeWithTransfer. 640 * 641 * Note: If `data` contains transferable objects, it can be read only once. 642 */ 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); 648 649 /** 650 * Implements StructuredSerialize, StructuredSerializeForStorage, and 651 * StructuredSerializeWithTransfer. 652 * 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. 656 */ 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); 662 663 JS_PUBLIC_API bool JS_StructuredCloneHasTransferables( 664 JSStructuredCloneData& data, bool* hasTransferable); 665 666 JS_PUBLIC_API bool JS_StructuredClone( 667 JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp, 668 const JSStructuredCloneCallbacks* optionalCallbacks, void* closure); 669 670 /** 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. 677 * 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). 681 */ 682 class JS_PUBLIC_API JSAutoStructuredCloneBuffer { 683 JSStructuredCloneData data_; 684 uint32_t version_; 685 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); 693 } 694 695 JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other); 696 JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other); 697 698 ~JSAutoStructuredCloneBuffer() { clear(); } 699 700 JSStructuredCloneData& data() { return data_; } 701 bool empty() const { return !data_.Size(); } 702 703 void clear(); 704 705 JS::StructuredCloneScope scope() const { return data_.scope(); } 706 707 /** 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). 711 */ 712 void adopt(JSStructuredCloneData&& data, 713 uint32_t version = JS_STRUCTURED_CLONE_VERSION, 714 const JSStructuredCloneCallbacks* callbacks = nullptr, 715 void* closure = nullptr); 716 717 /** 718 * Release ownership of the buffer and assign it and ownership of it to 719 * `data`. 720 */ 721 void giveTo(JSStructuredCloneData* data); 722 723 bool read(JSContext* cx, JS::MutableHandleValue vp, 724 const JS::CloneDataPolicy& cloneDataPolicy = JS::CloneDataPolicy(), 725 const JSStructuredCloneCallbacks* optionalCallbacks = nullptr, 726 void* closure = nullptr); 727 728 bool write(JSContext* cx, JS::HandleValue v, 729 const JSStructuredCloneCallbacks* optionalCallbacks = nullptr, 730 void* closure = nullptr); 731 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); 736 737 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { 738 return data_.SizeOfExcludingThis(mallocSizeOf); 739 } 740 741 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { 742 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 743 } 744 745 private: 746 // Copy and assignment are not supported. 747 JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& other) = 748 delete; 749 JSAutoStructuredCloneBuffer& operator=( 750 const JSAutoStructuredCloneBuffer& other) = delete; 751 }; 752 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) 757 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 768 769 JS_PUBLIC_API bool JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1, 770 uint32_t* p2); 771 772 JS_PUBLIC_API bool JS_ReadBytes(JSStructuredCloneReader* r, void* p, 773 size_t len); 774 775 JS_PUBLIC_API bool JS_ReadString(JSStructuredCloneReader* r, 776 JS::MutableHandleString str); 777 778 JS_PUBLIC_API bool JS_ReadDouble(JSStructuredCloneReader* r, double* v); 779 780 JS_PUBLIC_API bool JS_ReadTypedArray(JSStructuredCloneReader* r, 781 JS::MutableHandleValue vp); 782 783 JS_PUBLIC_API bool JS_WriteUint32Pair(JSStructuredCloneWriter* w, uint32_t tag, 784 uint32_t data); 785 786 JS_PUBLIC_API bool JS_WriteBytes(JSStructuredCloneWriter* w, const void* p, 787 size_t len); 788 789 JS_PUBLIC_API bool JS_WriteString(JSStructuredCloneWriter* w, 790 JS::HandleString str); 791 792 JS_PUBLIC_API bool JS_WriteDouble(JSStructuredCloneWriter* w, double v); 793 794 JS_PUBLIC_API bool JS_WriteTypedArray(JSStructuredCloneWriter* w, 795 JS::HandleValue v); 796 797 JS_PUBLIC_API bool JS_ObjectNotWritten(JSStructuredCloneWriter* w, 798 JS::HandleObject obj); 799 800 JS_PUBLIC_API JS::StructuredCloneScope JS_GetStructuredCloneScope( 801 JSStructuredCloneWriter* w); 802 803 #endif /* js_StructuredClone_h */