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