michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef js_StructuredClone_h michael@0: #define js_StructuredClone_h michael@0: michael@0: #include "mozilla/NullPtr.h" michael@0: michael@0: #include michael@0: michael@0: #include "jstypes.h" michael@0: michael@0: #include "js/RootingAPI.h" michael@0: #include "js/TypeDecls.h" michael@0: #include "js/Value.h" michael@0: michael@0: struct JSRuntime; michael@0: struct JSStructuredCloneReader; michael@0: struct JSStructuredCloneWriter; michael@0: michael@0: // API for the HTML5 internal structured cloning algorithm. michael@0: michael@0: namespace JS { michael@0: enum TransferableOwnership { michael@0: // Transferable data has not been filled in yet michael@0: SCTAG_TMO_UNFILLED = 0, michael@0: michael@0: // Structured clone buffer does not yet own the data michael@0: SCTAG_TMO_UNOWNED = 1, michael@0: michael@0: // All values at least this large are owned by the clone buffer michael@0: SCTAG_TMO_FIRST_OWNED = 2, michael@0: michael@0: // Data is a pointer that can be freed michael@0: SCTAG_TMO_ALLOC_DATA = 2, michael@0: michael@0: // Data is a SharedArrayBufferObject's buffer michael@0: SCTAG_TMO_SHARED_BUFFER = 3, michael@0: michael@0: // Data is a memory mapped pointer michael@0: SCTAG_TMO_MAPPED_DATA = 4, michael@0: michael@0: // Data is embedding-specific. The engine can free it by calling the michael@0: // freeTransfer op. The embedding can also use SCTAG_TMO_USER_MIN and michael@0: // greater, up to 32 bits, to distinguish specific ownership variants. michael@0: SCTAG_TMO_CUSTOM = 5, michael@0: michael@0: SCTAG_TMO_USER_MIN michael@0: }; michael@0: } /* namespace JS */ michael@0: michael@0: // Read structured data from the reader r. This hook is used to read a value michael@0: // previously serialized by a call to the WriteStructuredCloneOp hook. michael@0: // michael@0: // tag and data are the pair of uint32_t values from the header. The callback michael@0: // may use the JS_Read* APIs to read any other relevant parts of the object michael@0: // from the reader r. closure is any value passed to the JS_ReadStructuredClone michael@0: // function. Return the new object on success, nullptr on error/exception. michael@0: typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, michael@0: uint32_t tag, uint32_t data, void *closure); michael@0: michael@0: // Structured data serialization hook. The engine can write primitive values, michael@0: // Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other michael@0: // type of object requires application support. This callback must first use michael@0: // the JS_WriteUint32Pair API to write an object header, passing a value michael@0: // greater than JS_SCTAG_USER to the tag parameter. Then it can use the michael@0: // JS_Write* APIs to write any other relevant parts of the value v to the michael@0: // writer w. closure is any value passed to the JS_WriteStructuredCLone function. michael@0: // michael@0: // Return true on success, false on error/exception. michael@0: typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, michael@0: JS::HandleObject obj, void *closure); michael@0: michael@0: // This is called when JS_WriteStructuredClone is given an invalid transferable. michael@0: // To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException michael@0: // with error set to one of the JS_SCERR_* values. michael@0: typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); michael@0: michael@0: // This is called when JS_ReadStructuredClone receives a transferable object michael@0: // not known to the engine. If this hook does not exist or returns false, the michael@0: // JS engine calls the reportError op if set, otherwise it throws a michael@0: // DATA_CLONE_ERR DOM Exception. This method is called before any other michael@0: // callback and must return a non-null object in returnObject on success. michael@0: typedef bool (*ReadTransferStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, michael@0: uint32_t tag, void *content, uint64_t extraData, michael@0: void *closure, michael@0: JS::MutableHandleObject returnObject); michael@0: michael@0: // Called when JS_WriteStructuredClone receives a transferable object not michael@0: // handled by the engine. If this hook does not exist or returns false, the JS michael@0: // engine will call the reportError hook or fall back to throwing a michael@0: // DATA_CLONE_ERR DOM Exception. This method is called before any other michael@0: // callback. michael@0: // michael@0: // tag: indicates what type of transferable this is. Must be greater than michael@0: // 0xFFFF0201 (value of the internal SCTAG_TRANSFER_MAP_PENDING_ENTRY) michael@0: // michael@0: // ownership: see TransferableOwnership, above. Used to communicate any needed michael@0: // ownership info to the FreeTransferStructuredCloneOp. michael@0: // michael@0: // content, extraData: what the ReadTransferStructuredCloneOp will receive michael@0: // michael@0: typedef bool (*TransferStructuredCloneOp)(JSContext *cx, michael@0: JS::Handle obj, michael@0: void *closure, michael@0: // Output: michael@0: uint32_t *tag, michael@0: JS::TransferableOwnership *ownership, michael@0: void **content, michael@0: uint64_t *extraData); michael@0: michael@0: // Called when JS_ClearStructuredClone has to free an unknown transferable michael@0: // object. Note that it should never trigger a garbage collection (and will michael@0: // assert in a debug build if it does.) michael@0: typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership, michael@0: void *content, uint64_t extraData, void *closure); michael@0: michael@0: // The maximum supported structured-clone serialization format version. Note michael@0: // that this does not need to be bumped for Transferable-only changes, since michael@0: // they are never saved to persistent storage. michael@0: #define JS_STRUCTURED_CLONE_VERSION 2 michael@0: michael@0: struct JSStructuredCloneCallbacks { michael@0: ReadStructuredCloneOp read; michael@0: WriteStructuredCloneOp write; michael@0: StructuredCloneErrorOp reportError; michael@0: ReadTransferStructuredCloneOp readTransfer; michael@0: TransferStructuredCloneOp writeTransfer; michael@0: FreeTransferStructuredCloneOp freeTransfer; michael@0: }; michael@0: michael@0: // Note: if the *data contains transferable objects, it can be read only once. michael@0: JS_PUBLIC_API(bool) michael@0: JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, uint32_t version, michael@0: JS::MutableHandleValue vp, michael@0: const JSStructuredCloneCallbacks *optionalCallbacks, void *closure); michael@0: michael@0: // Note: On success, the caller is responsible for calling michael@0: // JS_ClearStructuredClone(*datap, nbytes, optionalCallbacks, closure). michael@0: JS_PUBLIC_API(bool) michael@0: JS_WriteStructuredClone(JSContext *cx, JS::HandleValue v, uint64_t **datap, size_t *nbytesp, michael@0: const JSStructuredCloneCallbacks *optionalCallbacks, michael@0: void *closure, JS::HandleValue transferable); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_ClearStructuredClone(uint64_t *data, size_t nbytes, michael@0: const JSStructuredCloneCallbacks *optionalCallbacks, michael@0: void *closure); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, bool *hasTransferable); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_StructuredClone(JSContext *cx, JS::HandleValue v, JS::MutableHandleValue vp, michael@0: const JSStructuredCloneCallbacks *optionalCallbacks, void *closure); michael@0: michael@0: // RAII sugar for JS_WriteStructuredClone. michael@0: class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { michael@0: uint64_t *data_; michael@0: size_t nbytes_; michael@0: uint32_t version_; michael@0: const JSStructuredCloneCallbacks *callbacks_; michael@0: void *closure_; michael@0: michael@0: public: michael@0: JSAutoStructuredCloneBuffer() michael@0: : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION), michael@0: callbacks_(nullptr), closure_(nullptr) michael@0: {} michael@0: michael@0: JSAutoStructuredCloneBuffer(const JSStructuredCloneCallbacks *callbacks, void *closure) michael@0: : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION), michael@0: callbacks_(callbacks), closure_(closure) michael@0: {} michael@0: michael@0: JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer &&other); michael@0: JSAutoStructuredCloneBuffer &operator=(JSAutoStructuredCloneBuffer &&other); michael@0: michael@0: ~JSAutoStructuredCloneBuffer() { clear(); } michael@0: michael@0: uint64_t *data() const { return data_; } michael@0: size_t nbytes() const { return nbytes_; } michael@0: michael@0: void clear(); michael@0: michael@0: // Copy some memory. It will be automatically freed by the destructor. michael@0: bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); michael@0: michael@0: // Adopt some memory. It will be automatically freed by the destructor. michael@0: // data must have been allocated by the JS engine (e.g., extracted via michael@0: // JSAutoStructuredCloneBuffer::steal). michael@0: void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); michael@0: michael@0: // Remove the buffer so that it will not be automatically freed. michael@0: // After this, the caller is responsible for feeding the memory back to michael@0: // JSAutoStructuredCloneBuffer::adopt. michael@0: void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=nullptr); michael@0: michael@0: bool read(JSContext *cx, JS::MutableHandleValue vp, michael@0: const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr); michael@0: michael@0: bool write(JSContext *cx, JS::HandleValue v, michael@0: const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr); michael@0: michael@0: bool write(JSContext *cx, JS::HandleValue v, JS::HandleValue transferable, michael@0: const JSStructuredCloneCallbacks *optionalCallbacks=nullptr, void *closure=nullptr); michael@0: michael@0: private: michael@0: // Copy and assignment are not supported. michael@0: JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other) MOZ_DELETE; michael@0: JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other) MOZ_DELETE; michael@0: }; michael@0: michael@0: // The range of tag values the application may use for its own custom object types. michael@0: #define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) michael@0: #define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) michael@0: michael@0: #define JS_SCERR_RECURSION 0 michael@0: #define JS_SCERR_TRANSFERABLE 1 michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_ReadTypedArray(JSStructuredCloneReader *r, JS::MutableHandleValue vp); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::HandleValue v); michael@0: michael@0: #endif /* js_StructuredClone_h */