1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 
13 #include <stdint.h>
14 
15 #include "jstypes.h"
16 
17 #include "js/RootingAPI.h"
18 #include "js/TypeDecls.h"
19 #include "js/Value.h"
20 
21 struct JSRuntime;
22 struct JSStructuredCloneReader;
23 struct JSStructuredCloneWriter;
24 
25 // API for the HTML5 internal structured cloning algorithm.
26 
27 namespace JS {
28 
29 enum class StructuredCloneScope : uint32_t {
30     SameProcessSameThread,
31     SameProcessDifferentThread,
32     DifferentProcess
33 };
34 
35 enum TransferableOwnership {
36     /** Transferable data has not been filled in yet */
37     SCTAG_TMO_UNFILLED = 0,
38 
39     /** Structured clone buffer does not yet own the data */
40     SCTAG_TMO_UNOWNED = 1,
41 
42     /** All values at least this large are owned by the clone buffer */
43     SCTAG_TMO_FIRST_OWNED = 2,
44 
45     /** Data is a pointer that can be freed */
46     SCTAG_TMO_ALLOC_DATA = 2,
47 
48     /** Data is a memory mapped pointer */
49     SCTAG_TMO_MAPPED_DATA = 3,
50 
51     /**
52      * Data is embedding-specific. The engine can free it by calling the
53      * freeTransfer op. The embedding can also use SCTAG_TMO_USER_MIN and
54      * greater, up to 32 bits, to distinguish specific ownership variants.
55      */
56     SCTAG_TMO_CUSTOM = 4,
57 
58     SCTAG_TMO_USER_MIN
59 };
60 
61 class CloneDataPolicy
62 {
63     bool sharedArrayBuffer_;
64 
65   public:
66     // The default is to allow all policy-controlled aspects.
67 
CloneDataPolicy()68     CloneDataPolicy() :
69       sharedArrayBuffer_(true)
70     {}
71 
72     // In the JS engine, SharedArrayBuffers can only be cloned intra-process
73     // because the shared memory areas are allocated in process-private memory.
74     // Clients should therefore deny SharedArrayBuffers when cloning data that
75     // are to be transmitted inter-process.
76     //
77     // Clients should also deny SharedArrayBuffers when cloning data that are to
78     // be transmitted intra-process if policy needs dictate such denial.
79 
denySharedArrayBuffer()80     CloneDataPolicy& denySharedArrayBuffer() {
81         sharedArrayBuffer_ = false;
82         return *this;
83     }
84 
isSharedArrayBufferAllowed()85     bool isSharedArrayBufferAllowed() const {
86         return sharedArrayBuffer_;
87     }
88 };
89 
90 } /* namespace JS */
91 
92 /**
93  * Read structured data from the reader r. This hook is used to read a value
94  * previously serialized by a call to the WriteStructuredCloneOp hook.
95  *
96  * tag and data are the pair of uint32_t values from the header. The callback
97  * may use the JS_Read* APIs to read any other relevant parts of the object
98  * from the reader r. closure is any value passed to the JS_ReadStructuredClone
99  * function. Return the new object on success, nullptr on error/exception.
100  */
101 typedef JSObject* (*ReadStructuredCloneOp)(JSContext* cx, JSStructuredCloneReader* r,
102                                            uint32_t tag, uint32_t data, void* closure);
103 
104 /**
105  * Structured data serialization hook. The engine can write primitive values,
106  * Objects, Arrays, Dates, RegExps, TypedArrays, ArrayBuffers, Sets, Maps,
107  * and SharedTypedArrays. Any other type of object requires application support.
108  * This callback must first use the JS_WriteUint32Pair API to write an object
109  * header, passing a value greater than JS_SCTAG_USER to the tag parameter.
110  * Then it can use the JS_Write* APIs to write any other relevant parts of
111  * the value v to the writer w. closure is any value passed to the
112  * JS_WriteStructuredClone function.
113  *
114  * Return true on success, false on error/exception.
115  */
116 typedef bool (*WriteStructuredCloneOp)(JSContext* cx, JSStructuredCloneWriter* w,
117                                        JS::HandleObject obj, void* closure);
118 
119 /**
120  * This is called when JS_WriteStructuredClone is given an invalid transferable.
121  * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
122  * with error set to one of the JS_SCERR_* values.
123  */
124 typedef void (*StructuredCloneErrorOp)(JSContext* cx, uint32_t errorid);
125 
126 /**
127  * This is called when JS_ReadStructuredClone receives a transferable object
128  * not known to the engine. If this hook does not exist or returns false, the
129  * JS engine calls the reportError op if set, otherwise it throws a
130  * DATA_CLONE_ERR DOM Exception. This method is called before any other
131  * callback and must return a non-null object in returnObject on success.
132  */
133 typedef bool (*ReadTransferStructuredCloneOp)(JSContext* cx, JSStructuredCloneReader* r,
134                                               uint32_t tag, void* content, uint64_t extraData,
135                                               void* closure,
136                                               JS::MutableHandleObject returnObject);
137 
138 /**
139  * Called when JS_WriteStructuredClone receives a transferable object not
140  * handled by the engine. If this hook does not exist or returns false, the JS
141  * engine will call the reportError hook or fall back to throwing a
142  * DATA_CLONE_ERR DOM Exception. This method is called before any other
143  * callback.
144  *
145  *  tag: indicates what type of transferable this is. Must be greater than
146  *       0xFFFF0201 (value of the internal SCTAG_TRANSFER_MAP_PENDING_ENTRY)
147  *
148  *  ownership: see TransferableOwnership, above. Used to communicate any needed
149  *       ownership info to the FreeTransferStructuredCloneOp.
150  *
151  *  content, extraData: what the ReadTransferStructuredCloneOp will receive
152  */
153 typedef bool (*TransferStructuredCloneOp)(JSContext* cx,
154                                           JS::Handle<JSObject*> obj,
155                                           void* closure,
156                                           // Output:
157                                           uint32_t* tag,
158                                           JS::TransferableOwnership* ownership,
159                                           void** content,
160                                           uint64_t* extraData);
161 
162 /**
163  * Called when freeing an unknown transferable object. Note that it
164  * should never trigger a garbage collection (and will assert in a
165  * debug build if it does.)
166  */
167 typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership,
168                                               void* content, uint64_t extraData, void* closure);
169 
170 // The maximum supported structured-clone serialization format version.
171 // Increment this when anything at all changes in the serialization format.
172 // (Note that this does not need to be bumped for Transferable-only changes,
173 // since they are never saved to persistent storage.)
174 #define JS_STRUCTURED_CLONE_VERSION 8
175 
176 struct JSStructuredCloneCallbacks {
177     ReadStructuredCloneOp read;
178     WriteStructuredCloneOp write;
179     StructuredCloneErrorOp reportError;
180     ReadTransferStructuredCloneOp readTransfer;
181     TransferStructuredCloneOp writeTransfer;
182     FreeTransferStructuredCloneOp freeTransfer;
183 };
184 
185 enum OwnTransferablePolicy {
186     OwnsTransferablesIfAny,
187     IgnoreTransferablesIfAny,
188     NoTransferables
189 };
190 
JS_PUBLIC_API(JSStructuredCloneData)191 class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) :
192     public mozilla::BufferList<js::SystemAllocPolicy>
193 {
194     typedef js::SystemAllocPolicy AllocPolicy;
195     typedef mozilla::BufferList<js::SystemAllocPolicy> BufferList;
196 
197     static const size_t kInitialSize = 0;
198     static const size_t kInitialCapacity = 4096;
199     static const size_t kStandardCapacity = 4096;
200 
201     const JSStructuredCloneCallbacks* callbacks_;
202     void* closure_;
203     OwnTransferablePolicy ownTransferables_;
204 
205     void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks,
206                               void* closure,
207                               OwnTransferablePolicy policy) {
208         callbacks_ = callbacks;
209         closure_ = closure;
210         ownTransferables_ = policy;
211     }
212 
213     friend struct JSStructuredCloneWriter;
214     friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer);
215 
216 public:
217     explicit JSStructuredCloneData(AllocPolicy aAP = AllocPolicy())
218         : BufferList(kInitialSize, kInitialCapacity, kStandardCapacity, aAP)
219         , callbacks_(nullptr)
220         , closure_(nullptr)
221         , ownTransferables_(OwnTransferablePolicy::NoTransferables)
222     {}
223     MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers)
224         : BufferList(Move(buffers))
225         , callbacks_(nullptr)
226         , closure_(nullptr)
227         , ownTransferables_(OwnTransferablePolicy::NoTransferables)
228     {}
229     JSStructuredCloneData(JSStructuredCloneData&& other) = default;
230     JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default;
231     ~JSStructuredCloneData();
232 
233     using BufferList::BufferList;
234 };
235 
236 /** Note: if the *data contains transferable objects, it can be read only once. */
237 JS_PUBLIC_API(bool)
238 JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, uint32_t version,
239                        JS::StructuredCloneScope scope,
240                        JS::MutableHandleValue vp,
241                        const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
242 
243 JS_PUBLIC_API(bool)
244 JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, JSStructuredCloneData* data,
245                         JS::StructuredCloneScope scope,
246                         JS::CloneDataPolicy cloneDataPolicy,
247                         const JSStructuredCloneCallbacks* optionalCallbacks,
248                         void* closure, JS::HandleValue transferable);
249 
250 JS_PUBLIC_API(bool)
251 JS_StructuredCloneHasTransferables(JSStructuredCloneData& data, bool* hasTransferable);
252 
253 JS_PUBLIC_API(bool)
254 JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
255                    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
256 
257 /** RAII sugar for JS_WriteStructuredClone. */
JS_PUBLIC_API(JSAutoStructuredCloneBuffer)258 class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
259     const JS::StructuredCloneScope scope_;
260     JSStructuredCloneData data_;
261     uint32_t version_;
262 
263   public:
264     JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
265                                 const JSStructuredCloneCallbacks* callbacks, void* closure)
266         : scope_(scope), version_(JS_STRUCTURED_CLONE_VERSION)
267     {
268         data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
269     }
270 
271     JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
272     JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other);
273 
274     ~JSAutoStructuredCloneBuffer() { clear(); }
275 
276     JSStructuredCloneData& data() { return data_; }
277     bool empty() const { return !data_.Size(); }
278 
279     void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
280 
281     /** Copy some memory. It will be automatically freed by the destructor. */
282     bool copy(const JSStructuredCloneData& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
283               const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
284 
285     /**
286      * Adopt some memory. It will be automatically freed by the destructor.
287      * data must have been allocated by the JS engine (e.g., extracted via
288      * JSAutoStructuredCloneBuffer::steal).
289      */
290     void adopt(JSStructuredCloneData&& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
291                const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
292 
293     /**
294      * Release the buffer and transfer ownership to the caller.
295      */
296     void steal(JSStructuredCloneData* data, uint32_t* versionp=nullptr,
297                const JSStructuredCloneCallbacks** callbacks=nullptr, void** closure=nullptr);
298 
299     /**
300      * Abandon ownership of any transferable objects stored in the buffer,
301      * without freeing the buffer itself. Useful when copying the data out into
302      * an external container, though note that you will need to use adopt() to
303      * properly release that data eventually.
304      */
305     void abandon() { data_.ownTransferables_ = OwnTransferablePolicy::IgnoreTransferablesIfAny; }
306 
307     bool read(JSContext* cx, JS::MutableHandleValue vp,
308               const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
309 
310     bool write(JSContext* cx, JS::HandleValue v,
311                const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
312 
313     bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable,
314                JS::CloneDataPolicy cloneDataPolicy,
315                const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
316 
317   private:
318     // Copy and assignment are not supported.
319     JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& other) = delete;
320     JSAutoStructuredCloneBuffer& operator=(const JSAutoStructuredCloneBuffer& other) = delete;
321 };
322 
323 // The range of tag values the application may use for its own custom object types.
324 #define JS_SCTAG_USER_MIN  ((uint32_t) 0xFFFF8000)
325 #define JS_SCTAG_USER_MAX  ((uint32_t) 0xFFFFFFFF)
326 
327 #define JS_SCERR_RECURSION 0
328 #define JS_SCERR_TRANSFERABLE 1
329 #define JS_SCERR_DUP_TRANSFERABLE 2
330 #define JS_SCERR_UNSUPPORTED_TYPE 3
331 
332 JS_PUBLIC_API(bool)
333 JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1, uint32_t* p2);
334 
335 JS_PUBLIC_API(bool)
336 JS_ReadBytes(JSStructuredCloneReader* r, void* p, size_t len);
337 
338 JS_PUBLIC_API(bool)
339 JS_ReadTypedArray(JSStructuredCloneReader* r, JS::MutableHandleValue vp);
340 
341 JS_PUBLIC_API(bool)
342 JS_WriteUint32Pair(JSStructuredCloneWriter* w, uint32_t tag, uint32_t data);
343 
344 JS_PUBLIC_API(bool)
345 JS_WriteBytes(JSStructuredCloneWriter* w, const void* p, size_t len);
346 
347 JS_PUBLIC_API(bool)
348 JS_WriteString(JSStructuredCloneWriter* w, JS::HandleString str);
349 
350 JS_PUBLIC_API(bool)
351 JS_WriteTypedArray(JSStructuredCloneWriter* w, JS::HandleValue v);
352 
353 JS_PUBLIC_API(bool)
354 JS_ObjectNotWritten(JSStructuredCloneWriter* w, JS::HandleObject obj);
355 
356 JS_PUBLIC_API(JS::StructuredCloneScope)
357 JS_GetStructuredCloneScope(JSStructuredCloneWriter* w);
358 
359 #endif  /* js_StructuredClone_h */
360