/work/obj-fuzz/dist/include/js/StructuredClone.h
Line | Count | Source (jump to first uncovered line) |
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 | | #include "mozilla/MemoryReporting.h" |
13 | | #include "mozilla/Move.h" |
14 | | |
15 | | #include <stdint.h> |
16 | | |
17 | | #include "jstypes.h" |
18 | | |
19 | | #include "js/RootingAPI.h" |
20 | | #include "js/TypeDecls.h" |
21 | | #include "js/Value.h" |
22 | | #include "js/Vector.h" |
23 | | |
24 | | /* |
25 | | * API for safe passing of structured data, HTML 2018 Feb 21 section 2.7. |
26 | | * <https://html.spec.whatwg.org/multipage/structured-data.html> |
27 | | * |
28 | | * This is a serialization scheme for JS values, somewhat like JSON. It |
29 | | * preserves some aspects of JS objects (strings, numbers, own data properties |
30 | | * with string keys, array elements) but not others (methods, getters and |
31 | | * setters, prototype chains). Unlike JSON, structured data: |
32 | | * |
33 | | * - can contain cyclic references. |
34 | | * |
35 | | * - handles Maps, Sets, and some other object types. |
36 | | * |
37 | | * - supports *transferring* objects of certain types from one realm to |
38 | | * another, rather than cloning them. |
39 | | * |
40 | | * - is specified by a living standard, and continues to evolve. |
41 | | * |
42 | | * - is encoded in a nonstandard binary format, and is never exposed to Web |
43 | | * content in its serialized form. It's used internally by the browser to |
44 | | * send data from one thread/realm/domain to another, not across the |
45 | | * network. |
46 | | */ |
47 | | |
48 | | struct JSStructuredCloneReader; |
49 | | struct JSStructuredCloneWriter; |
50 | | |
51 | | /** |
52 | | * The structured-clone serialization format version number. |
53 | | * |
54 | | * When serialized data is stored as bytes, e.g. in your Firefox profile, later |
55 | | * versions of the engine may have to read it. When you upgrade Firefox, we |
56 | | * don't crawl through your whole profile converting all saved data from the |
57 | | * previous version of the serialization format to the latest version. So it is |
58 | | * normal to have data in old formats stored in your profile. |
59 | | * |
60 | | * The JS engine can *write* data only in the current format version. |
61 | | * |
62 | | * It can *read* any data written in the current version, and data written for |
63 | | * DifferentProcess scope in earlier versions. |
64 | | * |
65 | | * |
66 | | * ## When to bump this version number |
67 | | * |
68 | | * When making a change so drastic that the JS engine needs to know whether |
69 | | * it's reading old or new serialized data in order to handle both correctly, |
70 | | * increment this version number. Make sure the engine can still read all |
71 | | * old data written with previous versions. |
72 | | * |
73 | | * If StructuredClone.cpp doesn't contain code that distinguishes between |
74 | | * version 8 and version 9, there should not be a version 9. |
75 | | * |
76 | | * Do not increment for changes that only affect SameProcess encoding. |
77 | | * |
78 | | * Increment only for changes that would otherwise break old serialized data. |
79 | | * Do not increment for new data types. (Rationale: Modulo bugs, older versions |
80 | | * of the JS engine can already correctly throw errors when they encounter new, |
81 | | * unrecognized features. A version number bump does not actually help them.) |
82 | | */ |
83 | 3 | #define JS_STRUCTURED_CLONE_VERSION 8 |
84 | | |
85 | | namespace JS { |
86 | | |
87 | | /** |
88 | | * Indicates the "scope of validity" of serialized data. |
89 | | * |
90 | | * Writing plain JS data produces an array of bytes that can be copied and |
91 | | * read in another process or whatever. The serialized data is Plain Old Data. |
92 | | * However, HTML also supports `Transferable` objects, which, when cloned, can |
93 | | * be moved from the source object into the clone, like when you take a |
94 | | * photograph of someone and it steals their soul. |
95 | | * See <https://developer.mozilla.org/en-US/docs/Web/API/Transferable>. |
96 | | * We support cloning and transferring objects of many types. |
97 | | * |
98 | | * For example, when we transfer an ArrayBuffer (within a process), we "detach" |
99 | | * the ArrayBuffer, embed the raw buffer pointer in the serialized data, and |
100 | | * later install it in a new ArrayBuffer in the destination realm. Ownership |
101 | | * of that buffer memory is transferred from the original ArrayBuffer to the |
102 | | * serialized data and then to the clone. |
103 | | * |
104 | | * This only makes sense within a single address space. When we transfer an |
105 | | * ArrayBuffer to another process, the contents of the buffer must be copied |
106 | | * into the serialized data. (The original ArrayBuffer is still detached, |
107 | | * though, for consistency; in some cases the caller shouldn't know or care if |
108 | | * the recipient is in the same process.) |
109 | | * |
110 | | * ArrayBuffers are actually a lucky case; some objects (like MessagePorts) |
111 | | * can't reasonably be stored by value in serialized data -- it's pointers or |
112 | | * nothing. |
113 | | * |
114 | | * So there is a tradeoff between scope of validity -- how far away the |
115 | | * serialized data may be sent and still make sense -- and efficiency or |
116 | | * features. The read and write algorithms therefore take an argument of this |
117 | | * type, allowing the user to control those trade-offs. |
118 | | */ |
119 | | enum class StructuredCloneScope : uint32_t { |
120 | | /** |
121 | | * The most restrictive scope, with greatest efficiency and features. |
122 | | * |
123 | | * When writing, this means we're writing for an audience in the same |
124 | | * process and same thread. The caller promises that the serialized data |
125 | | * will **not** be shipped off to a different thread/process or stored in a |
126 | | * database. It's OK to produce serialized data that contains pointers. In |
127 | | * Rust terms, the serialized data will be treated as `!Send`. |
128 | | * |
129 | | * When reading, this means: Accept transferred objects and buffers |
130 | | * (pointers). The caller promises that the serialized data was written |
131 | | * using this API (otherwise, the serialized data may contain bogus |
132 | | * pointers, leading to undefined behavior). |
133 | | */ |
134 | | SameProcessSameThread, |
135 | | |
136 | | /** |
137 | | * When writing, this means: The caller promises that the serialized data |
138 | | * will **not** be shipped off to a different process or stored in a |
139 | | * database. However, it may be shipped to another thread. It's OK to |
140 | | * produce serialized data that contains pointers to data that is safe to |
141 | | * send across threads, such as array buffers. In Rust terms, the |
142 | | * serialized data will be treated as `Send` but not `Copy`. |
143 | | * |
144 | | * When reading, this means the same thing as SameProcessSameThread; |
145 | | * the distinction only matters when writing. |
146 | | */ |
147 | | SameProcessDifferentThread, |
148 | | |
149 | | /** |
150 | | * When writing, this means we're writing for an audience in a different |
151 | | * process. Produce serialized data that can be sent to other processes, |
152 | | * bitwise copied, or even stored as bytes in a database and read by later |
153 | | * versions of Firefox years from now. The HTML5 spec refers to this as |
154 | | * "ForStorage" as in StructuredSerializeForStorage, though we use |
155 | | * DifferentProcess for IPC as well as storage. |
156 | | * |
157 | | * Transferable objects are limited to ArrayBuffers, whose contents are |
158 | | * copied into the serialized data (rather than just writing a pointer). |
159 | | * |
160 | | * When reading, this means: Do not accept pointers. |
161 | | */ |
162 | | DifferentProcess, |
163 | | |
164 | | /** |
165 | | * Handle a backwards-compatibility case with IndexedDB (bug 1434308): when |
166 | | * reading, this means to treat legacy SameProcessSameThread data as if it |
167 | | * were DifferentProcess. |
168 | | * |
169 | | * Do not use this for writing; use DifferentProcess instead. |
170 | | */ |
171 | | DifferentProcessForIndexedDB, |
172 | | |
173 | | /** |
174 | | * Existing code wants to be able to create an uninitialized |
175 | | * JSStructuredCloneData without knowing the scope, then populate it with |
176 | | * data (at which point the scope *is* known.) |
177 | | */ |
178 | | Unassigned |
179 | | }; |
180 | | |
181 | | enum TransferableOwnership { |
182 | | /** Transferable data has not been filled in yet */ |
183 | | SCTAG_TMO_UNFILLED = 0, |
184 | | |
185 | | /** Structured clone buffer does not yet own the data */ |
186 | | SCTAG_TMO_UNOWNED = 1, |
187 | | |
188 | | /** All values at least this large are owned by the clone buffer */ |
189 | | SCTAG_TMO_FIRST_OWNED = 2, |
190 | | |
191 | | /** Data is a pointer that can be freed */ |
192 | | SCTAG_TMO_ALLOC_DATA = 2, |
193 | | |
194 | | /** Data is a memory mapped pointer */ |
195 | | SCTAG_TMO_MAPPED_DATA = 3, |
196 | | |
197 | | /** |
198 | | * Data is embedding-specific. The engine can free it by calling the |
199 | | * freeTransfer op. The embedding can also use SCTAG_TMO_USER_MIN and |
200 | | * greater, up to 32 bits, to distinguish specific ownership variants. |
201 | | */ |
202 | | SCTAG_TMO_CUSTOM = 4, |
203 | | |
204 | | SCTAG_TMO_USER_MIN |
205 | | }; |
206 | | |
207 | | class CloneDataPolicy |
208 | | { |
209 | | bool sharedArrayBuffer_; |
210 | | |
211 | | public: |
212 | | // The default is to allow all policy-controlled aspects. |
213 | | |
214 | | CloneDataPolicy() : |
215 | | sharedArrayBuffer_(true) |
216 | 3 | {} |
217 | | |
218 | | // In the JS engine, SharedArrayBuffers can only be cloned intra-process |
219 | | // because the shared memory areas are allocated in process-private memory. |
220 | | // Clients should therefore deny SharedArrayBuffers when cloning data that |
221 | | // are to be transmitted inter-process. |
222 | | // |
223 | | // Clients should also deny SharedArrayBuffers when cloning data that are to |
224 | | // be transmitted intra-process if policy needs dictate such denial. |
225 | | |
226 | 3 | CloneDataPolicy& denySharedArrayBuffer() { |
227 | 3 | sharedArrayBuffer_ = false; |
228 | 3 | return *this; |
229 | 3 | } |
230 | | |
231 | 0 | bool isSharedArrayBufferAllowed() const { |
232 | 0 | return sharedArrayBuffer_; |
233 | 0 | } |
234 | | }; |
235 | | |
236 | | } /* namespace JS */ |
237 | | |
238 | | /** |
239 | | * Read structured data from the reader r. This hook is used to read a value |
240 | | * previously serialized by a call to the WriteStructuredCloneOp hook. |
241 | | * |
242 | | * tag and data are the pair of uint32_t values from the header. The callback |
243 | | * may use the JS_Read* APIs to read any other relevant parts of the object |
244 | | * from the reader r. closure is any value passed to the JS_ReadStructuredClone |
245 | | * function. Return the new object on success, nullptr on error/exception. |
246 | | */ |
247 | | typedef JSObject* (*ReadStructuredCloneOp)(JSContext* cx, JSStructuredCloneReader* r, |
248 | | uint32_t tag, uint32_t data, void* closure); |
249 | | |
250 | | /** |
251 | | * Structured data serialization hook. The engine can write primitive values, |
252 | | * Objects, Arrays, Dates, RegExps, TypedArrays, ArrayBuffers, Sets, Maps, |
253 | | * and SharedTypedArrays. Any other type of object requires application support. |
254 | | * This callback must first use the JS_WriteUint32Pair API to write an object |
255 | | * header, passing a value greater than JS_SCTAG_USER to the tag parameter. |
256 | | * Then it can use the JS_Write* APIs to write any other relevant parts of |
257 | | * the value v to the writer w. closure is any value passed to the |
258 | | * JS_WriteStructuredClone function. |
259 | | * |
260 | | * Return true on success, false on error/exception. |
261 | | */ |
262 | | typedef bool (*WriteStructuredCloneOp)(JSContext* cx, JSStructuredCloneWriter* w, |
263 | | JS::HandleObject obj, void* closure); |
264 | | |
265 | | /** |
266 | | * This is called when JS_WriteStructuredClone is given an invalid transferable. |
267 | | * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException |
268 | | * with error set to one of the JS_SCERR_* values. |
269 | | */ |
270 | | typedef void (*StructuredCloneErrorOp)(JSContext* cx, uint32_t errorid); |
271 | | |
272 | | /** |
273 | | * This is called when JS_ReadStructuredClone receives a transferable object |
274 | | * not known to the engine. If this hook does not exist or returns false, the |
275 | | * JS engine calls the reportError op if set, otherwise it throws a |
276 | | * DATA_CLONE_ERR DOM Exception. This method is called before any other |
277 | | * callback and must return a non-null object in returnObject on success. |
278 | | */ |
279 | | typedef bool (*ReadTransferStructuredCloneOp)(JSContext* cx, JSStructuredCloneReader* r, |
280 | | uint32_t tag, void* content, uint64_t extraData, |
281 | | void* closure, |
282 | | JS::MutableHandleObject returnObject); |
283 | | |
284 | | /** |
285 | | * Called when JS_WriteStructuredClone receives a transferable object not |
286 | | * handled by the engine. If this hook does not exist or returns false, the JS |
287 | | * engine will call the reportError hook or fall back to throwing a |
288 | | * DATA_CLONE_ERR DOM Exception. This method is called before any other |
289 | | * callback. |
290 | | * |
291 | | * tag: indicates what type of transferable this is. Must be greater than |
292 | | * 0xFFFF0201 (value of the internal SCTAG_TRANSFER_MAP_PENDING_ENTRY) |
293 | | * |
294 | | * ownership: see TransferableOwnership, above. Used to communicate any needed |
295 | | * ownership info to the FreeTransferStructuredCloneOp. |
296 | | * |
297 | | * content, extraData: what the ReadTransferStructuredCloneOp will receive |
298 | | */ |
299 | | typedef bool (*TransferStructuredCloneOp)(JSContext* cx, |
300 | | JS::Handle<JSObject*> obj, |
301 | | void* closure, |
302 | | // Output: |
303 | | uint32_t* tag, |
304 | | JS::TransferableOwnership* ownership, |
305 | | void** content, |
306 | | uint64_t* extraData); |
307 | | |
308 | | /** |
309 | | * Called when freeing an unknown transferable object. Note that it |
310 | | * should never trigger a garbage collection (and will assert in a |
311 | | * debug build if it does.) |
312 | | */ |
313 | | typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership, |
314 | | void* content, uint64_t extraData, void* closure); |
315 | | |
316 | | /** |
317 | | * Called when the transferring objects are checked. If this function returns false, the |
318 | | * serialization ends throwing a DataCloneError exception. |
319 | | */ |
320 | | typedef bool (*CanTransferStructuredCloneOp)(JSContext* cx, |
321 | | JS::Handle<JSObject*> obj, |
322 | | void* closure); |
323 | | |
324 | | struct JSStructuredCloneCallbacks { |
325 | | ReadStructuredCloneOp read; |
326 | | WriteStructuredCloneOp write; |
327 | | StructuredCloneErrorOp reportError; |
328 | | ReadTransferStructuredCloneOp readTransfer; |
329 | | TransferStructuredCloneOp writeTransfer; |
330 | | FreeTransferStructuredCloneOp freeTransfer; |
331 | | CanTransferStructuredCloneOp canTransfer; |
332 | | }; |
333 | | |
334 | | enum OwnTransferablePolicy { |
335 | | /** |
336 | | * The buffer owns any Transferables that it might contain, and should |
337 | | * properly release them upon destruction. |
338 | | */ |
339 | | OwnsTransferablesIfAny, |
340 | | |
341 | | /** |
342 | | * Do not free any Transferables within this buffer when deleting it. This |
343 | | * is used to mark as clone buffer as containing data from another process, |
344 | | * and so it can't legitimately contain pointers. If the buffer claims to |
345 | | * have transferables, it's a bug or an attack. This is also used for |
346 | | * abandon(), where a buffer still contains raw data but the ownership has |
347 | | * been given over to some other entity. |
348 | | */ |
349 | | IgnoreTransferablesIfAny, |
350 | | |
351 | | /** |
352 | | * A buffer that cannot contain Transferables at all. This usually means |
353 | | * the buffer is empty (not yet filled in, or having been cleared). |
354 | | */ |
355 | | NoTransferables |
356 | | }; |
357 | | |
358 | | namespace js |
359 | | { |
360 | | class SharedArrayRawBuffer; |
361 | | |
362 | | class SharedArrayRawBufferRefs |
363 | | { |
364 | | public: |
365 | 6 | SharedArrayRawBufferRefs() = default; |
366 | 0 | SharedArrayRawBufferRefs(SharedArrayRawBufferRefs&& other) = default; |
367 | | SharedArrayRawBufferRefs& operator=(SharedArrayRawBufferRefs&& other); |
368 | | ~SharedArrayRawBufferRefs(); |
369 | | |
370 | | MOZ_MUST_USE bool acquire(JSContext* cx, SharedArrayRawBuffer* rawbuf); |
371 | | MOZ_MUST_USE bool acquireAll(JSContext* cx, const SharedArrayRawBufferRefs& that); |
372 | | void takeOwnership(SharedArrayRawBufferRefs&&); |
373 | | void releaseAll(); |
374 | | |
375 | | private: |
376 | | js::Vector<js::SharedArrayRawBuffer*, 0, js::SystemAllocPolicy> refs_; |
377 | | }; |
378 | | |
379 | | template <typename T, typename AllocPolicy> struct BufferIterator; |
380 | | } |
381 | | |
382 | | /** |
383 | | * JSStructuredCloneData represents structured clone data together with the |
384 | | * information needed to read/write/transfer/free the records within it, in the |
385 | | * form of a set of callbacks. |
386 | | */ |
387 | | class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) { |
388 | | public: |
389 | | using BufferList = mozilla::BufferList<js::SystemAllocPolicy>; |
390 | | using Iterator = BufferList::IterImpl; |
391 | | |
392 | | private: |
393 | | static const size_t kStandardCapacity = 4096; |
394 | | |
395 | | BufferList bufList_; |
396 | | |
397 | | // The (address space, thread) scope within which this clone is valid. Note |
398 | | // that this must be either set during construction, or start out as |
399 | | // Unassigned and transition once to something else. |
400 | | JS::StructuredCloneScope scope_; |
401 | | |
402 | | const JSStructuredCloneCallbacks* callbacks_ = nullptr; |
403 | | void* closure_ = nullptr; |
404 | | OwnTransferablePolicy ownTransferables_ = OwnTransferablePolicy::NoTransferables; |
405 | | js::SharedArrayRawBufferRefs refsHeld_; |
406 | | |
407 | | friend struct JSStructuredCloneWriter; |
408 | | friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer); |
409 | | template <typename T, typename AllocPolicy> friend struct js::BufferIterator; |
410 | | |
411 | | public: |
412 | | // The constructor must be infallible but SystemAllocPolicy is not, so both |
413 | | // the initial size and initial capacity of the BufferList must be zero. |
414 | | explicit JSStructuredCloneData(JS::StructuredCloneScope scope) |
415 | | : bufList_(0, 0, kStandardCapacity, js::SystemAllocPolicy()) |
416 | | , scope_(scope) |
417 | | , callbacks_(nullptr) |
418 | | , closure_(nullptr) |
419 | | , ownTransferables_(OwnTransferablePolicy::NoTransferables) |
420 | 6 | {} |
421 | | |
422 | | // Steal the raw data from a BufferList. In this case, we don't know the |
423 | | // scope and none of the callback info is assigned yet. |
424 | | JSStructuredCloneData(BufferList&& buffers, JS::StructuredCloneScope scope) |
425 | | : bufList_(std::move(buffers)) |
426 | | , scope_(scope) |
427 | | , callbacks_(nullptr) |
428 | | , closure_(nullptr) |
429 | | , ownTransferables_(OwnTransferablePolicy::NoTransferables) |
430 | 0 | {} |
431 | | MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers) |
432 | | : JSStructuredCloneData(std::move(buffers), JS::StructuredCloneScope::Unassigned) |
433 | 0 | {} |
434 | 0 | JSStructuredCloneData(JSStructuredCloneData&& other) = default; |
435 | 3 | JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default; |
436 | 6 | ~JSStructuredCloneData() { discardTransferables(); } |
437 | | |
438 | | void setCallbacks(const JSStructuredCloneCallbacks* callbacks, |
439 | | void* closure, |
440 | | OwnTransferablePolicy policy) |
441 | 6 | { |
442 | 6 | callbacks_ = callbacks; |
443 | 6 | closure_ = closure; |
444 | 6 | ownTransferables_ = policy; |
445 | 6 | } |
446 | | |
447 | 0 | bool Init(size_t initialCapacity = 0) { return bufList_.Init(0, initialCapacity); } |
448 | | |
449 | 3 | JS::StructuredCloneScope scope() const { return scope_; } |
450 | | |
451 | 0 | void initScope(JS::StructuredCloneScope scope) { |
452 | 0 | MOZ_ASSERT(Size() == 0, "initScope() of nonempty JSStructuredCloneData"); |
453 | 0 | if (scope_ != JS::StructuredCloneScope::Unassigned) { |
454 | 0 | MOZ_ASSERT(scope_ == scope, "Cannot change scope after it has been initialized"); |
455 | 0 | } |
456 | 0 | scope_ = scope; |
457 | 0 | } |
458 | | |
459 | 21 | size_t Size() const { return bufList_.Size(); } |
460 | | |
461 | 6 | const Iterator Start() const { return bufList_.Iter(); } |
462 | | |
463 | 0 | bool Advance(Iterator& iter, size_t distance) const { |
464 | 0 | return iter.AdvanceAcrossSegments(bufList_, distance); |
465 | 0 | } |
466 | | |
467 | 0 | bool ReadBytes(Iterator& iter, char* buffer, size_t size) const { |
468 | 0 | return bufList_.ReadBytes(iter, buffer, size); |
469 | 0 | } |
470 | | |
471 | | // Append new data to the end of the buffer. |
472 | 6 | bool AppendBytes(const char* data, size_t size) { |
473 | 6 | MOZ_ASSERT(scope_ != JS::StructuredCloneScope::Unassigned); |
474 | 6 | return bufList_.WriteBytes(data, size); |
475 | 6 | } |
476 | | |
477 | | // Update data stored within the existing buffer. There must be at least |
478 | | // 'size' bytes between the position of 'iter' and the end of the buffer. |
479 | 0 | bool UpdateBytes(Iterator& iter, const char* data, size_t size) const { |
480 | 0 | MOZ_ASSERT(scope_ != JS::StructuredCloneScope::Unassigned); |
481 | 0 | while (size > 0) { |
482 | 0 | size_t remaining = iter.RemainingInSegment(); |
483 | 0 | size_t nbytes = std::min(remaining, size); |
484 | 0 | memcpy(iter.Data(), data, nbytes); |
485 | 0 | data += nbytes; |
486 | 0 | size -= nbytes; |
487 | 0 | iter.Advance(bufList_, nbytes); |
488 | 0 | } |
489 | 0 | return true; |
490 | 0 | } |
491 | | |
492 | 0 | char* AllocateBytes(size_t maxSize, size_t* size) { |
493 | 0 | return bufList_.AllocateBytes(maxSize, size); |
494 | 0 | } |
495 | | |
496 | 6 | void Clear() { |
497 | 6 | discardTransferables(); |
498 | 6 | bufList_.Clear(); |
499 | 6 | } |
500 | | |
501 | | // Return a new read-only JSStructuredCloneData that "borrows" the contents |
502 | | // of |this|. Its lifetime should not exceed the donor's. This is only |
503 | | // allowed for DifferentProcess clones, so finalization of the borrowing |
504 | | // clone will do nothing. |
505 | | JSStructuredCloneData Borrow(Iterator& iter, size_t size, bool* success) const |
506 | 0 | { |
507 | 0 | MOZ_ASSERT(scope_ == JS::StructuredCloneScope::DifferentProcess); |
508 | 0 | return JSStructuredCloneData(bufList_.Borrow<js::SystemAllocPolicy>(iter, size, success), |
509 | 0 | scope_); |
510 | 0 | } |
511 | | |
512 | | // Iterate over all contained data, one BufferList segment's worth at a |
513 | | // time, and invoke the given FunctionToApply with the data pointer and |
514 | | // size. The function should return a bool value, and this loop will exit |
515 | | // with false if the function ever returns false. |
516 | | template <typename FunctionToApply> |
517 | 0 | bool ForEachDataChunk(FunctionToApply&& function) const { |
518 | 0 | Iterator iter = bufList_.Iter(); |
519 | 0 | while (!iter.Done()) { |
520 | 0 | if (!function(iter.Data(), iter.RemainingInSegment())) { |
521 | 0 | return false; |
522 | 0 | } |
523 | 0 | iter.Advance(bufList_, iter.RemainingInSegment()); |
524 | 0 | } |
525 | 0 | return true; |
526 | 0 | } Unexecuted instantiation: bool JSStructuredCloneData::ForEachDataChunk<JSStructuredCloneData::Append(JSStructuredCloneData const&)::{lambda(char const*, unsigned long)#1}>(JSStructuredCloneData::Append(JSStructuredCloneData const&)::{lambda(char const*, unsigned long)#1}&&) const Unexecuted instantiation: bool JSStructuredCloneData::ForEachDataChunk<IPC::ParamTraits<JSStructuredCloneData>::Write(IPC::Message*, JSStructuredCloneData const&)::{lambda(char const*, unsigned long)#1}>(IPC::ParamTraits<JSStructuredCloneData>::Write(IPC::Message*, JSStructuredCloneData const&)::{lambda(char const*, unsigned long)#1}&&) const Unexecuted instantiation: Unified_cpp_dom_base4.cpp:bool JSStructuredCloneData::ForEachDataChunk<mozilla::dom::StructuredCloneBlob::WriteStructuredClone(JSContext*, JSStructuredCloneWriter*, mozilla::dom::StructuredCloneHolder*)::$_0>(mozilla::dom::StructuredCloneBlob::WriteStructuredClone(JSContext*, JSStructuredCloneWriter*, mozilla::dom::StructuredCloneHolder*)::$_0&&) const Unexecuted instantiation: Unified_cpp_dom_ipc0.cpp:bool JSStructuredCloneData::ForEachDataChunk<mozilla::dom::ipc::SharedMap::Entry::ExtractData(char*, unsigned int, unsigned short)::$_7>(mozilla::dom::ipc::SharedMap::Entry::ExtractData(char*, unsigned int, unsigned short)::$_7&&) const Unexecuted instantiation: Unified_cpp_mozapps_extensions0.cpp:bool JSStructuredCloneData::ForEachDataChunk<mozilla::AddonManagerStartup::EncodeBlob(JS::Handle<JS::Value>, JSContext*, JS::MutableHandle<JS::Value>)::$_0>(mozilla::AddonManagerStartup::EncodeBlob(JS::Handle<JS::Value>, JSContext*, JS::MutableHandle<JS::Value>)::$_0&&) const |
527 | | |
528 | | // Append the entire contents of other's bufList_ to our own. |
529 | 0 | bool Append(const JSStructuredCloneData& other) { |
530 | 0 | MOZ_ASSERT(scope_ == other.scope()); |
531 | 0 | return other.ForEachDataChunk([&](const char* data, size_t size) { |
532 | 0 | return AppendBytes(data, size); |
533 | 0 | }); |
534 | 0 | } |
535 | | |
536 | 0 | size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { |
537 | 0 | return bufList_.SizeOfExcludingThis(mallocSizeOf); |
538 | 0 | } |
539 | | |
540 | | void discardTransferables(); |
541 | | }; |
542 | | |
543 | | /** |
544 | | * Implements StructuredDeserialize and StructuredDeserializeWithTransfer. |
545 | | * |
546 | | * Note: If `data` contains transferable objects, it can be read only once. |
547 | | */ |
548 | | JS_PUBLIC_API(bool) |
549 | | JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, uint32_t version, |
550 | | JS::StructuredCloneScope scope, |
551 | | JS::MutableHandleValue vp, |
552 | | const JSStructuredCloneCallbacks* optionalCallbacks, void* closure); |
553 | | |
554 | | /** |
555 | | * Implements StructuredSerialize, StructuredSerializeForStorage, and |
556 | | * StructuredSerializeWithTransfer. |
557 | | * |
558 | | * Note: If the scope is DifferentProcess then the cloneDataPolicy must deny |
559 | | * shared-memory objects, or an error will be signaled if a shared memory object |
560 | | * is seen. |
561 | | */ |
562 | | JS_PUBLIC_API(bool) |
563 | | JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, JSStructuredCloneData* data, |
564 | | JS::StructuredCloneScope scope, |
565 | | JS::CloneDataPolicy cloneDataPolicy, |
566 | | const JSStructuredCloneCallbacks* optionalCallbacks, |
567 | | void* closure, JS::HandleValue transferable); |
568 | | |
569 | | JS_PUBLIC_API(bool) |
570 | | JS_StructuredCloneHasTransferables(JSStructuredCloneData& data, bool* hasTransferable); |
571 | | |
572 | | JS_PUBLIC_API(bool) |
573 | | JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp, |
574 | | const JSStructuredCloneCallbacks* optionalCallbacks, void* closure); |
575 | | |
576 | | /** |
577 | | * The C-style API calls to read and write structured clones are fragile -- |
578 | | * they rely on the caller to properly handle ownership of the clone data, and |
579 | | * the handling of the input data as well as the interpretation of the contents |
580 | | * of the clone buffer are dependent on the callbacks passed in. If you |
581 | | * serialize and deserialize with different callbacks, the results are |
582 | | * questionable. |
583 | | * |
584 | | * JSAutoStructuredCloneBuffer wraps things up in an RAII class for data |
585 | | * management, and uses the same callbacks for both writing and reading |
586 | | * (serializing and deserializing). |
587 | | */ |
588 | | class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { |
589 | | const JS::StructuredCloneScope scope_; |
590 | | JSStructuredCloneData data_; |
591 | | uint32_t version_; |
592 | | |
593 | | public: |
594 | | JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope, |
595 | | const JSStructuredCloneCallbacks* callbacks, void* closure) |
596 | | : scope_(scope), data_(scope), version_(JS_STRUCTURED_CLONE_VERSION) |
597 | 3 | { |
598 | 3 | data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables); |
599 | 3 | } |
600 | | |
601 | | JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other); |
602 | | JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other); |
603 | | |
604 | 3 | ~JSAutoStructuredCloneBuffer() { clear(); } |
605 | | |
606 | 0 | JSStructuredCloneData& data() { return data_; } |
607 | 0 | bool empty() const { return !data_.Size(); } |
608 | | |
609 | | void clear(); |
610 | | |
611 | 0 | JS::StructuredCloneScope scope() const { return scope_; } |
612 | | |
613 | | /** |
614 | | * Adopt some memory. It will be automatically freed by the destructor. |
615 | | * data must have been allocated by the JS engine (e.g., extracted via |
616 | | * JSAutoStructuredCloneBuffer::steal). |
617 | | */ |
618 | | void adopt(JSStructuredCloneData&& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION, |
619 | | const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr); |
620 | | |
621 | | /** |
622 | | * Release the buffer and transfer ownership to the caller. |
623 | | */ |
624 | | void steal(JSStructuredCloneData* data, uint32_t* versionp=nullptr, |
625 | | const JSStructuredCloneCallbacks** callbacks=nullptr, void** closure=nullptr); |
626 | | |
627 | | /** |
628 | | * Abandon ownership of any transferable objects stored in the buffer, |
629 | | * without freeing the buffer itself. Useful when copying the data out into |
630 | | * an external container, though note that you will need to use adopt() to |
631 | | * properly release that data eventually. |
632 | | */ |
633 | 0 | void abandon() { data_.ownTransferables_ = OwnTransferablePolicy::IgnoreTransferablesIfAny; } |
634 | | |
635 | | bool read(JSContext* cx, JS::MutableHandleValue vp, |
636 | | const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr); |
637 | | |
638 | | bool write(JSContext* cx, JS::HandleValue v, |
639 | | const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr); |
640 | | |
641 | | bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable, |
642 | | JS::CloneDataPolicy cloneDataPolicy, |
643 | | const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr); |
644 | | |
645 | | size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) |
646 | 0 | { |
647 | 0 | return data_.SizeOfExcludingThis(mallocSizeOf); |
648 | 0 | } |
649 | | |
650 | | size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) |
651 | 0 | { |
652 | 0 | return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); |
653 | 0 | } |
654 | | |
655 | | private: |
656 | | // Copy and assignment are not supported. |
657 | | JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& other) = delete; |
658 | | JSAutoStructuredCloneBuffer& operator=(const JSAutoStructuredCloneBuffer& other) = delete; |
659 | | }; |
660 | | |
661 | | // The range of tag values the application may use for its own custom object types. |
662 | 0 | #define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) |
663 | | #define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) |
664 | | |
665 | | #define JS_SCERR_RECURSION 0 |
666 | 0 | #define JS_SCERR_TRANSFERABLE 1 |
667 | 0 | #define JS_SCERR_DUP_TRANSFERABLE 2 |
668 | 0 | #define JS_SCERR_UNSUPPORTED_TYPE 3 |
669 | 0 | #define JS_SCERR_SHMEM_TRANSFERABLE 4 |
670 | | |
671 | | JS_PUBLIC_API(bool) |
672 | | JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1, uint32_t* p2); |
673 | | |
674 | | JS_PUBLIC_API(bool) |
675 | | JS_ReadBytes(JSStructuredCloneReader* r, void* p, size_t len); |
676 | | |
677 | | JS_PUBLIC_API(bool) |
678 | | JS_ReadTypedArray(JSStructuredCloneReader* r, JS::MutableHandleValue vp); |
679 | | |
680 | | JS_PUBLIC_API(bool) |
681 | | JS_WriteUint32Pair(JSStructuredCloneWriter* w, uint32_t tag, uint32_t data); |
682 | | |
683 | | JS_PUBLIC_API(bool) |
684 | | JS_WriteBytes(JSStructuredCloneWriter* w, const void* p, size_t len); |
685 | | |
686 | | JS_PUBLIC_API(bool) |
687 | | JS_WriteString(JSStructuredCloneWriter* w, JS::HandleString str); |
688 | | |
689 | | JS_PUBLIC_API(bool) |
690 | | JS_WriteTypedArray(JSStructuredCloneWriter* w, JS::HandleValue v); |
691 | | |
692 | | JS_PUBLIC_API(bool) |
693 | | JS_ObjectNotWritten(JSStructuredCloneWriter* w, JS::HandleObject obj); |
694 | | |
695 | | JS_PUBLIC_API(JS::StructuredCloneScope) |
696 | | JS_GetStructuredCloneScope(JSStructuredCloneWriter* w); |
697 | | |
698 | | #endif /* js_StructuredClone_h */ |