Line data Source code
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 : /*
8 : * This file implements the structured data algorithms of
9 : * https://html.spec.whatwg.org/multipage/structured-data.html
10 : *
11 : * The spec is in two parts:
12 : *
13 : * - StructuredSerialize examines a JS value and produces a graph of Records.
14 : * - StructuredDeserialize walks the Records and produces a new JS value.
15 : *
16 : * The differences between our implementation and the spec are minor:
17 : *
18 : * - We call the two phases "write" and "read".
19 : * - Our algorithms use an explicit work stack, rather than recursion.
20 : * - Serialized data is a flat array of bytes, not a (possibly cyclic) graph
21 : * of "Records".
22 : * - As a consequence, we handle non-treelike object graphs differently.
23 : * We serialize objects that appear in multiple places in the input as
24 : * backreferences, using sequential integer indexes.
25 : * See `JSStructuredCloneReader::allObjs`, our take on the "memory" map
26 : * in the spec's StructuredDeserialize.
27 : */
28 :
29 : #include "js/StructuredClone.h"
30 :
31 : #include "mozilla/CheckedInt.h"
32 : #include "mozilla/EndianUtils.h"
33 : #include "mozilla/FloatingPoint.h"
34 :
35 : #include <algorithm>
36 : #include <utility>
37 :
38 : #include "jsapi.h"
39 : #include "jsdate.h"
40 :
41 : #include "builtin/DataViewObject.h"
42 : #include "builtin/MapObject.h"
43 : #include "js/Date.h"
44 : #include "js/GCHashTable.h"
45 : #include "js/Wrapper.h"
46 : #include "vm/JSContext.h"
47 : #include "vm/RegExpObject.h"
48 : #include "vm/SavedFrame.h"
49 : #include "vm/SharedArrayObject.h"
50 : #include "vm/TypedArrayObject.h"
51 : #include "vm/WrapperObject.h"
52 : #include "wasm/WasmJS.h"
53 :
54 : #include "vm/JSContext-inl.h"
55 : #include "vm/JSObject-inl.h"
56 :
57 : using namespace js;
58 :
59 : using mozilla::BitwiseCast;
60 : using mozilla::NativeEndian;
61 : using mozilla::NumbersAreIdentical;
62 : using JS::CanonicalizeNaN;
63 :
64 : // When you make updates here, make sure you consider whether you need to bump the
65 : // value of JS_STRUCTURED_CLONE_VERSION in js/public/StructuredClone.h. You will
66 : // likely need to increment the version if anything at all changes in the serialization
67 : // format.
68 : //
69 : // Note that SCTAG_END_OF_KEYS is written into the serialized form and should have
70 : // a stable ID, it need not be at the end of the list and should not be used for
71 : // sizing data structures.
72 :
73 : enum StructuredDataType : uint32_t {
74 : // Structured data types provided by the engine
75 : SCTAG_FLOAT_MAX = 0xFFF00000,
76 : SCTAG_HEADER = 0xFFF10000,
77 : SCTAG_NULL = 0xFFFF0000,
78 : SCTAG_UNDEFINED,
79 : SCTAG_BOOLEAN,
80 : SCTAG_INT32,
81 : SCTAG_STRING,
82 : SCTAG_DATE_OBJECT,
83 : SCTAG_REGEXP_OBJECT,
84 : SCTAG_ARRAY_OBJECT,
85 : SCTAG_OBJECT_OBJECT,
86 : SCTAG_ARRAY_BUFFER_OBJECT,
87 : SCTAG_BOOLEAN_OBJECT,
88 : SCTAG_STRING_OBJECT,
89 : SCTAG_NUMBER_OBJECT,
90 : SCTAG_BACK_REFERENCE_OBJECT,
91 : SCTAG_DO_NOT_USE_1, // Required for backwards compatibility
92 : SCTAG_DO_NOT_USE_2, // Required for backwards compatibility
93 : SCTAG_TYPED_ARRAY_OBJECT,
94 : SCTAG_MAP_OBJECT,
95 : SCTAG_SET_OBJECT,
96 : SCTAG_END_OF_KEYS,
97 : SCTAG_DO_NOT_USE_3, // Required for backwards compatibility
98 : SCTAG_DATA_VIEW_OBJECT,
99 : SCTAG_SAVED_FRAME_OBJECT,
100 :
101 : // No new tags before principals.
102 : SCTAG_JSPRINCIPALS,
103 : SCTAG_NULL_JSPRINCIPALS,
104 : SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM,
105 : SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM,
106 :
107 : SCTAG_SHARED_ARRAY_BUFFER_OBJECT,
108 : SCTAG_SHARED_WASM_MEMORY_OBJECT,
109 :
110 : SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
111 : SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
112 : SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8,
113 : SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int16,
114 : SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint16,
115 : SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int32,
116 : SCTAG_TYPED_ARRAY_V1_UINT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint32,
117 : SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float32,
118 : SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float64,
119 : SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8Clamped,
120 : SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::MaxTypedArrayViewType - 1,
121 :
122 : // Define a separate range of numbers for Transferable-only tags, since
123 : // they are not used for persistent clone buffers and therefore do not
124 : // require bumping JS_STRUCTURED_CLONE_VERSION.
125 : SCTAG_TRANSFER_MAP_HEADER = 0xFFFF0200,
126 : SCTAG_TRANSFER_MAP_PENDING_ENTRY,
127 : SCTAG_TRANSFER_MAP_ARRAY_BUFFER,
128 : SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER,
129 : SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES,
130 :
131 : SCTAG_END_OF_BUILTIN_TYPES
132 : };
133 :
134 : /*
135 : * Format of transfer map:
136 : * <SCTAG_TRANSFER_MAP_HEADER, TransferableMapHeader(UNREAD|TRANSFERRED)>
137 : * numTransferables (64 bits)
138 : * array of:
139 : * <SCTAG_TRANSFER_MAP_*, TransferableOwnership>
140 : * pointer (64 bits)
141 : * extraData (64 bits), eg byte length for ArrayBuffers
142 : */
143 :
144 : // Data associated with an SCTAG_TRANSFER_MAP_HEADER that tells whether the
145 : // contents have been read out yet or not.
146 : enum TransferableMapHeader {
147 : SCTAG_TM_UNREAD = 0,
148 : SCTAG_TM_TRANSFERRED
149 : };
150 :
151 : static inline uint64_t
152 : PairToUInt64(uint32_t tag, uint32_t data)
153 : {
154 27582 : return uint64_t(data) | (uint64_t(tag) << 32);
155 : }
156 :
157 : namespace js {
158 :
159 : template<typename T, typename AllocPolicy>
160 : struct BufferIterator {
161 : typedef mozilla::BufferList<AllocPolicy> BufferList;
162 :
163 : explicit BufferIterator(const BufferList& buffer)
164 : : mBuffer(buffer)
165 : , mIter(buffer.Iter())
166 : {
167 : JS_STATIC_ASSERT(8 % sizeof(T) == 0);
168 : }
169 :
170 347 : explicit BufferIterator(const JSStructuredCloneData& data)
171 : : mBuffer(data.bufList_)
172 694 : , mIter(data.Start())
173 : {
174 : }
175 :
176 0 : BufferIterator& operator=(const BufferIterator& other)
177 : {
178 0 : MOZ_ASSERT(&mBuffer == &other.mBuffer);
179 0 : mIter = other.mIter;
180 0 : return *this;
181 : }
182 :
183 0 : BufferIterator operator++(int) {
184 0 : BufferIterator ret = *this;
185 1 : if (!mIter.AdvanceAcrossSegments(mBuffer, sizeof(T))) {
186 0 : MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
187 : }
188 3 : return ret;
189 : }
190 :
191 0 : BufferIterator& operator+=(size_t size) {
192 1 : if (!mIter.AdvanceAcrossSegments(mBuffer, size)) {
193 0 : MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
194 : }
195 16251 : return *this;
196 : }
197 :
198 0 : size_t operator-(const BufferIterator& other) {
199 0 : MOZ_ASSERT(&mBuffer == &other.mBuffer);
200 0 : return mBuffer.RangeLength(other.mIter, mIter);
201 : }
202 :
203 0 : void next() {
204 1 : if (!mIter.AdvanceAcrossSegments(mBuffer, sizeof(T))) {
205 0 : MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
206 : }
207 31258 : }
208 :
209 : bool done() const {
210 248 : return mIter.Done();
211 : }
212 :
213 : MOZ_MUST_USE bool readBytes(char* outData, size_t size) {
214 16251 : return mBuffer.ReadBytes(mIter, outData, size);
215 : }
216 :
217 0 : void write(const T& data) {
218 0 : MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
219 0 : *reinterpret_cast<T*>(mIter.Data()) = data;
220 5 : }
221 :
222 0 : T peek() const {
223 0 : MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
224 49449 : return *reinterpret_cast<T*>(mIter.Data());
225 : }
226 :
227 : bool canPeek() const {
228 49446 : return mIter.HasRoomFor(sizeof(T));
229 : }
230 :
231 : const BufferList& mBuffer;
232 : typename BufferList::IterImpl mIter;
233 : };
234 :
235 : SharedArrayRawBufferRefs&
236 269 : SharedArrayRawBufferRefs::operator=(SharedArrayRawBufferRefs&& other)
237 : {
238 0 : takeOwnership(std::move(other));
239 269 : return *this;
240 : }
241 :
242 1106 : SharedArrayRawBufferRefs::~SharedArrayRawBufferRefs()
243 : {
244 0 : releaseAll();
245 426 : }
246 :
247 : bool
248 0 : SharedArrayRawBufferRefs::acquire(JSContext* cx, SharedArrayRawBuffer* rawbuf)
249 : {
250 0 : if (!refs_.append(rawbuf)) {
251 0 : ReportOutOfMemory(cx);
252 0 : return false;
253 : }
254 :
255 0 : if (!rawbuf->addReference()) {
256 0 : refs_.popBack();
257 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
258 0 : return false;
259 : }
260 :
261 : return true;
262 : }
263 :
264 : bool
265 0 : SharedArrayRawBufferRefs::acquireAll(JSContext* cx, const SharedArrayRawBufferRefs& that)
266 : {
267 0 : if (!refs_.reserve(refs_.length() + that.refs_.length())) {
268 0 : ReportOutOfMemory(cx);
269 0 : return false;
270 : }
271 :
272 0 : for (auto ref : that.refs_) {
273 0 : if (!ref->addReference()) {
274 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
275 0 : return false;
276 : }
277 0 : MOZ_ALWAYS_TRUE(refs_.append(ref));
278 : }
279 :
280 0 : return true;
281 : }
282 :
283 : void
284 324 : SharedArrayRawBufferRefs::takeOwnership(SharedArrayRawBufferRefs&& other)
285 : {
286 0 : MOZ_ASSERT(refs_.empty());
287 0 : refs_ = std::move(other.refs_);
288 324 : }
289 :
290 : void
291 1092 : SharedArrayRawBufferRefs::releaseAll()
292 : {
293 0 : for (auto ref : refs_)
294 0 : ref->dropReference();
295 0 : refs_.clear();
296 1092 : }
297 :
298 : // SCOutput provides an interface to write raw data -- eg uint64_ts, doubles,
299 : // arrays of bytes -- into a structured clone data output stream. It also knows
300 : // how to free any transferable data within that stream.
301 : //
302 : // Note that it contains a full JSStructuredCloneData object, which holds the
303 : // callbacks necessary to read/write/transfer/free the data. For the purpose of
304 : // this class, only the freeTransfer callback is relevant; the rest of the callbacks
305 : // are used by the higher-level JSStructuredCloneWriter interface.
306 234 : struct SCOutput {
307 : public:
308 : using Iter = BufferIterator<uint64_t, SystemAllocPolicy>;
309 :
310 : SCOutput(JSContext* cx, JS::StructuredCloneScope scope);
311 :
312 : JSContext* context() const { return cx; }
313 235 : JS::StructuredCloneScope scope() const { return buf.scope(); }
314 :
315 : MOZ_MUST_USE bool write(uint64_t u);
316 : MOZ_MUST_USE bool writePair(uint32_t tag, uint32_t data);
317 : MOZ_MUST_USE bool writeDouble(double d);
318 : MOZ_MUST_USE bool writeBytes(const void* p, size_t nbytes);
319 : MOZ_MUST_USE bool writeChars(const Latin1Char* p, size_t nchars);
320 : MOZ_MUST_USE bool writeChars(const char16_t* p, size_t nchars);
321 : MOZ_MUST_USE bool writePtr(const void*);
322 :
323 : template <class T>
324 : MOZ_MUST_USE bool writeArray(const T* p, size_t nbytes);
325 :
326 : void setCallbacks(const JSStructuredCloneCallbacks* callbacks,
327 : void* closure,
328 : OwnTransferablePolicy policy)
329 : {
330 468 : buf.setCallbacks(callbacks, closure, policy);
331 : }
332 234 : void extractBuffer(JSStructuredCloneData* data) { *data = std::move(buf); }
333 : void discardTransferables();
334 :
335 0 : uint64_t tell() const { return buf.Size(); }
336 0 : uint64_t count() const { return buf.Size() / sizeof(uint64_t); }
337 2 : Iter iter() { return Iter(buf); }
338 :
339 : size_t offset(Iter dest) {
340 0 : return dest - iter();
341 : }
342 :
343 : JSContext* cx;
344 : JSStructuredCloneData buf;
345 : };
346 :
347 : class SCInput {
348 : typedef js::BufferIterator<uint64_t, SystemAllocPolicy> BufferIterator;
349 :
350 : public:
351 : SCInput(JSContext* cx, JSStructuredCloneData& data);
352 :
353 : JSContext* context() const { return cx; }
354 :
355 : static void getPtr(uint64_t data, void** ptr);
356 : static void getPair(uint64_t data, uint32_t* tagp, uint32_t* datap);
357 :
358 : MOZ_MUST_USE bool read(uint64_t* p);
359 : MOZ_MUST_USE bool readNativeEndian(uint64_t* p);
360 : MOZ_MUST_USE bool readPair(uint32_t* tagp, uint32_t* datap);
361 : MOZ_MUST_USE bool readDouble(double* p);
362 : MOZ_MUST_USE bool readBytes(void* p, size_t nbytes);
363 : MOZ_MUST_USE bool readChars(Latin1Char* p, size_t nchars);
364 : MOZ_MUST_USE bool readChars(char16_t* p, size_t nchars);
365 : MOZ_MUST_USE bool readPtr(void**);
366 :
367 : MOZ_MUST_USE bool get(uint64_t* p);
368 : MOZ_MUST_USE bool getPair(uint32_t* tagp, uint32_t* datap);
369 :
370 : const BufferIterator& tell() const { return point; }
371 0 : void seekTo(const BufferIterator& pos) { point = pos; }
372 0 : void seekBy(size_t pos) { point += pos; }
373 :
374 : template <class T>
375 : MOZ_MUST_USE bool readArray(T* p, size_t nelems);
376 :
377 : bool reportTruncated() {
378 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
379 0 : "truncated");
380 : return false;
381 : }
382 :
383 : private:
384 : void staticAssertions() {
385 : JS_STATIC_ASSERT(sizeof(char16_t) == 2);
386 : JS_STATIC_ASSERT(sizeof(uint32_t) == 4);
387 : }
388 :
389 : JSContext* cx;
390 : BufferIterator point;
391 : };
392 :
393 : } // namespace js
394 :
395 669 : struct JSStructuredCloneReader {
396 : public:
397 : explicit JSStructuredCloneReader(SCInput& in, JS::StructuredCloneScope scope,
398 : const JSStructuredCloneCallbacks* cb,
399 : void* cbClosure)
400 0 : : in(in), allowedScope(scope), objs(in.context()), allObjs(in.context()),
401 223 : callbacks(cb), closure(cbClosure) { }
402 :
403 : SCInput& input() { return in; }
404 : bool read(MutableHandleValue vp);
405 :
406 : private:
407 118975 : JSContext* context() { return in.context(); }
408 :
409 : bool readHeader();
410 : bool readTransferMap();
411 :
412 : template <typename CharT>
413 : JSString* readStringImpl(uint32_t nchars);
414 : JSString* readString(uint32_t data);
415 :
416 : bool checkDouble(double d);
417 : MOZ_MUST_USE bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
418 : bool v1Read = false);
419 : MOZ_MUST_USE bool readDataView(uint32_t byteLength, MutableHandleValue vp);
420 : MOZ_MUST_USE bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
421 : MOZ_MUST_USE bool readSharedArrayBuffer(MutableHandleValue vp);
422 : MOZ_MUST_USE bool readSharedWasmMemory(uint32_t nbytes, MutableHandleValue vp);
423 : MOZ_MUST_USE bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
424 : JSObject* readSavedFrame(uint32_t principalsTag);
425 : MOZ_MUST_USE bool startRead(MutableHandleValue vp);
426 :
427 : SCInput& in;
428 :
429 : // The widest scope that the caller will accept, where
430 : // SameProcessSameThread is the widest (it can store anything it wants) and
431 : // DifferentProcess is the narrowest (it cannot contain pointers and must
432 : // be valid cross-process.)
433 : JS::StructuredCloneScope allowedScope;
434 :
435 : // Stack of objects with properties remaining to be read.
436 : AutoValueVector objs;
437 :
438 : // Array of all objects read during this deserialization, for resolving
439 : // backreferences.
440 : //
441 : // For backreferences to work correctly, objects must be added to this
442 : // array in exactly the order expected by the version of the Writer that
443 : // created the serialized data, even across years and format versions. This
444 : // is usually no problem, since both algorithms do a single linear pass
445 : // over the serialized data. There is one hitch; see readTypedArray.
446 : //
447 : // The values in this vector are objects, except it can temporarily have
448 : // one `undefined` placeholder value (the readTypedArray hack).
449 : AutoValueVector allObjs;
450 :
451 : // The user defined callbacks that will be used for cloning.
452 : const JSStructuredCloneCallbacks* callbacks;
453 :
454 : // Any value passed to JS_ReadStructuredClone.
455 : void* closure;
456 :
457 : friend bool JS_ReadTypedArray(JSStructuredCloneReader* r, MutableHandleValue vp);
458 : };
459 :
460 : struct JSStructuredCloneWriter {
461 : public:
462 234 : explicit JSStructuredCloneWriter(JSContext* cx,
463 : JS::StructuredCloneScope scope,
464 : JS::CloneDataPolicy cloneDataPolicy,
465 : const JSStructuredCloneCallbacks* cb,
466 : void* cbClosure,
467 : const Value& tVal)
468 234 : : out(cx, scope), objs(out.context()),
469 : counts(out.context()), entries(out.context()),
470 0 : memory(out.context()),
471 0 : transferable(out.context(), tVal),
472 0 : transferableObjects(out.context(), GCHashSet<JSObject*>(cx)),
473 1872 : cloneDataPolicy(cloneDataPolicy)
474 : {
475 0 : out.setCallbacks(cb, cbClosure, OwnTransferablePolicy::NoTransferables);
476 234 : }
477 :
478 : ~JSStructuredCloneWriter();
479 :
480 0 : bool init() {
481 0 : if (!memory.init()) {
482 0 : ReportOutOfMemory(context());
483 0 : return false;
484 : }
485 468 : return parseTransferable() && writeHeader() && writeTransferMap();
486 : }
487 :
488 : bool write(HandleValue v);
489 :
490 113 : SCOutput& output() { return out; }
491 :
492 : void extractBuffer(JSStructuredCloneData* newData) {
493 468 : out.extractBuffer(newData);
494 : }
495 :
496 : private:
497 : JSStructuredCloneWriter() = delete;
498 : JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
499 :
500 218760 : JSContext* context() { return out.context(); }
501 :
502 : bool writeHeader();
503 : bool writeTransferMap();
504 :
505 : bool writeString(uint32_t tag, JSString* str);
506 : bool writeArrayBuffer(HandleObject obj);
507 : bool writeTypedArray(HandleObject obj);
508 : bool writeDataView(HandleObject obj);
509 : bool writeSharedArrayBuffer(HandleObject obj);
510 : bool writeSharedWasmMemory(HandleObject obj);
511 : bool startObject(HandleObject obj, bool* backref);
512 : bool startWrite(HandleValue v);
513 : bool traverseObject(HandleObject obj);
514 : bool traverseMap(HandleObject obj);
515 : bool traverseSet(HandleObject obj);
516 : bool traverseSavedFrame(HandleObject obj);
517 :
518 : bool reportDataCloneError(uint32_t errorId);
519 :
520 : bool parseTransferable();
521 : bool transferOwnership();
522 :
523 : inline void checkStack();
524 :
525 : SCOutput out;
526 :
527 : // Vector of objects with properties remaining to be written.
528 : //
529 : // NB: These can span multiple compartments, so the compartment must be
530 : // entered before any manipulation is performed.
531 : AutoValueVector objs;
532 :
533 : // counts[i] is the number of entries of objs[i] remaining to be written.
534 : // counts.length() == objs.length() and sum(counts) == entries.length().
535 : Vector<size_t> counts;
536 :
537 : // For JSObject: Property IDs as value
538 : // For Map: Key followed by value
539 : // For Set: Key
540 : // For SavedFrame: parent SavedFrame
541 : AutoValueVector entries;
542 :
543 : // The "memory" list described in the HTML5 internal structured cloning
544 : // algorithm. memory is a superset of objs; items are never removed from
545 : // Memory until a serialization operation is finished
546 : using CloneMemory = GCHashMap<JSObject*,
547 : uint32_t,
548 : MovableCellHasher<JSObject*>,
549 : SystemAllocPolicy>;
550 : Rooted<CloneMemory> memory;
551 :
552 : // Set of transferable objects
553 : RootedValue transferable;
554 : Rooted<GCHashSet<JSObject*>> transferableObjects;
555 :
556 : const JS::CloneDataPolicy cloneDataPolicy;
557 :
558 : friend bool JS_WriteString(JSStructuredCloneWriter* w, HandleString str);
559 : friend bool JS_WriteTypedArray(JSStructuredCloneWriter* w, HandleValue v);
560 : friend bool JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj);
561 : };
562 :
563 : JS_FRIEND_API(uint64_t)
564 0 : js::GetSCOffset(JSStructuredCloneWriter* writer)
565 : {
566 0 : MOZ_ASSERT(writer);
567 0 : return writer->output().count() * sizeof(uint64_t);
568 : }
569 :
570 : JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN);
571 : JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX);
572 : JS_STATIC_ASSERT(Scalar::Int8 == 0);
573 :
574 : static void
575 0 : ReportDataCloneError(JSContext* cx,
576 : const JSStructuredCloneCallbacks* callbacks,
577 : uint32_t errorId)
578 : {
579 0 : if (callbacks && callbacks->reportError) {
580 0 : callbacks->reportError(cx, errorId);
581 0 : return;
582 : }
583 :
584 0 : switch (errorId) {
585 : case JS_SCERR_DUP_TRANSFERABLE:
586 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_DUP_TRANSFERABLE);
587 0 : break;
588 :
589 : case JS_SCERR_TRANSFERABLE:
590 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_NOT_TRANSFERABLE);
591 0 : break;
592 :
593 : case JS_SCERR_UNSUPPORTED_TYPE:
594 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_UNSUPPORTED_TYPE);
595 0 : break;
596 :
597 : case JS_SCERR_SHMEM_TRANSFERABLE:
598 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SHMEM_TRANSFERABLE);
599 0 : break;
600 :
601 : default:
602 0 : MOZ_CRASH("Unkown errorId");
603 : break;
604 : }
605 : }
606 :
607 : bool
608 234 : WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp,
609 : JS::StructuredCloneScope scope,
610 : JS::CloneDataPolicy cloneDataPolicy,
611 : const JSStructuredCloneCallbacks* cb, void* cbClosure,
612 : const Value& transferable)
613 : {
614 0 : JSStructuredCloneWriter w(cx, scope, cloneDataPolicy, cb, cbClosure, transferable);
615 234 : if (!w.init())
616 : return false;
617 234 : if (!w.write(v))
618 : return false;
619 0 : w.extractBuffer(bufp);
620 234 : return true;
621 : }
622 :
623 : bool
624 223 : ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data,
625 : JS::StructuredCloneScope scope, MutableHandleValue vp,
626 : const JSStructuredCloneCallbacks* cb, void* cbClosure)
627 : {
628 0 : SCInput in(cx, data);
629 0 : JSStructuredCloneReader r(in, scope, cb, cbClosure);
630 446 : return r.read(vp);
631 : }
632 :
633 : static bool
634 0 : StructuredCloneHasTransferObjects(const JSStructuredCloneData& data)
635 : {
636 0 : if (data.Size() < sizeof(uint64_t))
637 : return false;
638 :
639 : uint64_t u;
640 0 : BufferIterator<uint64_t, SystemAllocPolicy> iter(data);
641 0 : MOZ_ALWAYS_TRUE(iter.readBytes(reinterpret_cast<char*>(&u), sizeof(u)));
642 0 : uint32_t tag = uint32_t(u >> 32);
643 0 : return (tag == SCTAG_TRANSFER_MAP_HEADER);
644 : }
645 :
646 : namespace js {
647 :
648 0 : SCInput::SCInput(JSContext* cx, JSStructuredCloneData& data)
649 446 : : cx(cx), point(data)
650 : {
651 :
652 : static_assert(JSStructuredCloneData::BufferList::kSegmentAlignment % 8 == 0,
653 : "structured clone buffer reads should be aligned");
654 0 : MOZ_ASSERT(data.Size() % 8 == 0);
655 223 : }
656 :
657 : bool
658 31008 : SCInput::read(uint64_t* p)
659 : {
660 1 : if (!point.canPeek()) {
661 0 : *p = 0; // initialize to shut GCC up
662 0 : return reportTruncated();
663 : }
664 0 : *p = NativeEndian::swapFromLittleEndian(point.peek());
665 0 : point.next();
666 31008 : return true;
667 : }
668 :
669 : bool
670 1 : SCInput::readNativeEndian(uint64_t* p)
671 : {
672 1 : if (!point.canPeek()) {
673 0 : *p = 0; // initialize to shut GCC up
674 0 : return reportTruncated();
675 : }
676 0 : *p = point.peek();
677 0 : point.next();
678 1 : return true;
679 : }
680 :
681 : bool
682 0 : SCInput::readPair(uint32_t* tagp, uint32_t* datap)
683 : {
684 : uint64_t u;
685 0 : bool ok = read(&u);
686 0 : if (ok) {
687 0 : *tagp = uint32_t(u >> 32);
688 25972 : *datap = uint32_t(u);
689 : }
690 0 : return ok;
691 : }
692 :
693 : bool
694 18188 : SCInput::get(uint64_t* p)
695 : {
696 0 : if (!point.canPeek())
697 0 : return reportTruncated();
698 0 : *p = NativeEndian::swapFromLittleEndian(point.peek());
699 18188 : return true;
700 : }
701 :
702 : bool
703 0 : SCInput::getPair(uint32_t* tagp, uint32_t* datap)
704 : {
705 0 : uint64_t u = 0;
706 18188 : if (!get(&u))
707 : return false;
708 :
709 0 : *tagp = uint32_t(u >> 32);
710 1 : *datap = uint32_t(u);
711 0 : return true;
712 : }
713 :
714 : void
715 0 : SCInput::getPair(uint64_t data, uint32_t* tagp, uint32_t* datap)
716 : {
717 0 : uint64_t u = NativeEndian::swapFromLittleEndian(data);
718 0 : *tagp = uint32_t(u >> 32);
719 1 : *datap = uint32_t(u);
720 0 : }
721 :
722 : bool
723 48 : SCInput::readDouble(double* p)
724 : {
725 : union {
726 : uint64_t u;
727 : double d;
728 : } pun;
729 48 : if (!read(&pun.u))
730 : return false;
731 0 : *p = CanonicalizeNaN(pun.d);
732 48 : return true;
733 : }
734 :
735 : template <typename T>
736 : static void
737 : swapFromLittleEndianInPlace(T* ptr, size_t nelems)
738 : {
739 : if (nelems > 0)
740 44 : NativeEndian::swapFromLittleEndianInPlace(ptr, nelems);
741 : }
742 :
743 : template <>
744 : void
745 : swapFromLittleEndianInPlace(uint8_t* ptr, size_t nelems)
746 : {}
747 :
748 : // Data is packed into an integral number of uint64_t words. Compute the
749 : // padding required to finish off the final word.
750 : static size_t
751 : ComputePadding(size_t nelems, size_t elemSize)
752 : {
753 : // We want total length mod 8, where total length is nelems * sizeof(T),
754 : // but that might overflow. So reduce nelems to nelems mod 8, since we are
755 : // going to be doing a mod 8 later anyway.
756 0 : size_t leftoverLength = (nelems % sizeof(uint64_t)) * elemSize;
757 33788 : return (-leftoverLength) & (sizeof(uint64_t) - 1);
758 : }
759 :
760 : template <class T>
761 : bool
762 16274 : SCInput::readArray(T* p, size_t nelems)
763 : {
764 16274 : if (!nelems)
765 : return true;
766 :
767 : JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
768 :
769 : // Fail if nelems is so huge that computing the full size will overflow.
770 0 : mozilla::CheckedInt<size_t> size = mozilla::CheckedInt<size_t>(nelems) * sizeof(T);
771 1 : if (!size.isValid())
772 0 : return reportTruncated();
773 :
774 32502 : if (!point.readBytes(reinterpret_cast<char*>(p), size.value()))
775 : return false;
776 :
777 16251 : swapFromLittleEndianInPlace(p, nelems);
778 :
779 16251 : point += ComputePadding(nelems, sizeof(T));
780 :
781 16251 : return true;
782 : }
783 :
784 : bool
785 0 : SCInput::readBytes(void* p, size_t nbytes)
786 : {
787 16230 : return readArray((uint8_t*) p, nbytes);
788 : }
789 :
790 : bool
791 0 : SCInput::readChars(Latin1Char* p, size_t nchars)
792 : {
793 : static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
794 16208 : return readBytes(p, nchars);
795 : }
796 :
797 : bool
798 0 : SCInput::readChars(char16_t* p, size_t nchars)
799 : {
800 : MOZ_ASSERT(sizeof(char16_t) == sizeof(uint16_t));
801 44 : return readArray((uint16_t*) p, nchars);
802 : }
803 :
804 : void
805 0 : SCInput::getPtr(uint64_t data, void** ptr)
806 : {
807 : // No endianness conversion is used for pointers, since they are not sent
808 : // across address spaces anyway.
809 0 : *ptr = reinterpret_cast<void*>(data);
810 0 : }
811 :
812 : bool
813 0 : SCInput::readPtr(void** p)
814 : {
815 : uint64_t u;
816 1 : if (!readNativeEndian(&u))
817 : return false;
818 1 : *p = reinterpret_cast<void*>(NativeEndian::swapFromLittleEndian(u));
819 0 : return true;
820 : }
821 :
822 0 : SCOutput::SCOutput(JSContext* cx, JS::StructuredCloneScope scope)
823 234 : : cx(cx), buf(scope)
824 : {
825 0 : }
826 :
827 : bool
828 33440 : SCOutput::write(uint64_t u)
829 : {
830 0 : uint64_t v = NativeEndian::swapToLittleEndian(u);
831 0 : if (!buf.AppendBytes(reinterpret_cast<char*>(&v), sizeof(u))) {
832 0 : ReportOutOfMemory(context());
833 0 : return false;
834 : }
835 : return true;
836 : }
837 :
838 : bool
839 0 : SCOutput::writePair(uint32_t tag, uint32_t data)
840 : {
841 : // As it happens, the tag word appears after the data word in the output.
842 : // This is because exponents occupy the last 2 bytes of doubles on the
843 : // little-endian platforms we care most about.
844 : //
845 : // For example, TrueValue() is written using writePair(SCTAG_BOOLEAN, 1).
846 : // PairToUInt64 produces the number 0xFFFF000200000001.
847 : // That is written out as the bytes 01 00 00 00 02 00 FF FF.
848 33353 : return write(PairToUInt64(tag, data));
849 : }
850 :
851 : static inline double
852 : ReinterpretPairAsDouble(uint32_t tag, uint32_t data)
853 : {
854 98 : return BitwiseCast<double>(PairToUInt64(tag, data));
855 : }
856 :
857 : bool
858 0 : SCOutput::writeDouble(double d)
859 : {
860 164 : return write(BitwiseCast<uint64_t>(CanonicalizeNaN(d)));
861 : }
862 :
863 : template <class T>
864 : bool
865 87 : SCOutput::writeArray(const T* p, size_t nelems)
866 : {
867 : JS_STATIC_ASSERT(8 % sizeof(T) == 0);
868 : JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
869 :
870 87 : if (nelems == 0)
871 : return true;
872 :
873 0 : for (size_t i = 0; i < nelems; i++) {
874 0 : T value = NativeEndian::swapToLittleEndian(p[i]);
875 1 : if (!buf.AppendBytes(reinterpret_cast<char*>(&value), sizeof(value)))
876 0 : return false;
877 : }
878 :
879 : // Zero-pad to 8 bytes boundary.
880 0 : size_t padbytes = ComputePadding(nelems, sizeof(T));
881 0 : char zeroes[sizeof(uint64_t)] = { 0 };
882 87 : if (!buf.AppendBytes(zeroes, padbytes))
883 : return false;
884 :
885 87 : return true;
886 : }
887 :
888 : template <>
889 : bool
890 17467 : SCOutput::writeArray<uint8_t>(const uint8_t* p, size_t nelems)
891 : {
892 17467 : if (nelems == 0)
893 : return true;
894 :
895 17450 : if (!buf.AppendBytes(reinterpret_cast<const char*>(p), nelems))
896 : return false;
897 :
898 : // zero-pad to 8 bytes boundary
899 0 : size_t padbytes = ComputePadding(nelems, 1);
900 0 : char zeroes[sizeof(uint64_t)] = { 0 };
901 17450 : if (!buf.AppendBytes(zeroes, padbytes))
902 : return false;
903 :
904 17450 : return true;
905 : }
906 :
907 : bool
908 0 : SCOutput::writeBytes(const void* p, size_t nbytes)
909 : {
910 17467 : return writeArray((const uint8_t*) p, nbytes);
911 : }
912 :
913 : bool
914 0 : SCOutput::writeChars(const char16_t* p, size_t nchars)
915 : {
916 : static_assert(sizeof(char16_t) == sizeof(uint16_t),
917 : "required so that treating char16_t[] memory as uint16_t[] "
918 : "memory is permissible");
919 87 : return writeArray((const uint16_t*) p, nchars);
920 : }
921 :
922 : bool
923 0 : SCOutput::writeChars(const Latin1Char* p, size_t nchars)
924 : {
925 : static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
926 17418 : return writeBytes(p, nchars);
927 : }
928 :
929 : bool
930 0 : SCOutput::writePtr(const void* p)
931 : {
932 1 : return write(reinterpret_cast<uint64_t>(p));
933 : }
934 :
935 : void
936 0 : SCOutput::discardTransferables()
937 : {
938 0 : buf.discardTransferables();
939 0 : }
940 :
941 : } // namespace js
942 :
943 :
944 : // If the buffer contains Transferables, free them. Note that custom
945 : // Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
946 : // delete their transferables.
947 : void
948 1504 : JSStructuredCloneData::discardTransferables()
949 : {
950 0 : if (!Size())
951 1504 : return;
952 :
953 326 : if (ownTransferables_ != OwnTransferablePolicy::OwnsTransferablesIfAny)
954 : return;
955 :
956 : // DifferentProcess clones cannot contain pointers, so nothing needs to be
957 : // released.
958 123 : if (scope_ == JS::StructuredCloneScope::DifferentProcess)
959 : return;
960 :
961 0 : FreeTransferStructuredCloneOp freeTransfer = nullptr;
962 0 : if (callbacks_)
963 103 : freeTransfer = callbacks_->freeTransfer;
964 :
965 0 : auto point = BufferIterator<uint64_t, SystemAllocPolicy>(*this);
966 123 : if (point.done())
967 : return; // Empty buffer
968 :
969 : uint32_t tag, data;
970 0 : MOZ_RELEASE_ASSERT(point.canPeek());
971 0 : SCInput::getPair(point.peek(), &tag, &data);
972 123 : point.next();
973 :
974 0 : if (tag == SCTAG_HEADER) {
975 123 : if (point.done())
976 : return;
977 :
978 0 : MOZ_RELEASE_ASSERT(point.canPeek());
979 0 : SCInput::getPair(point.peek(), &tag, &data);
980 123 : point.next();
981 : }
982 :
983 123 : if (tag != SCTAG_TRANSFER_MAP_HEADER)
984 : return;
985 :
986 1 : if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
987 : return;
988 :
989 : // freeTransfer should not GC
990 0 : JS::AutoSuppressGCAnalysis nogc;
991 :
992 0 : if (point.done())
993 : return;
994 :
995 0 : uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek());
996 0 : point.next();
997 0 : while (numTransferables--) {
998 0 : if (!point.canPeek())
999 : return;
1000 :
1001 : uint32_t ownership;
1002 0 : SCInput::getPair(point.peek(), &tag, &ownership);
1003 0 : point.next();
1004 0 : MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
1005 0 : if (!point.canPeek())
1006 : return;
1007 :
1008 : void* content;
1009 0 : SCInput::getPtr(point.peek(), &content);
1010 0 : point.next();
1011 0 : if (!point.canPeek())
1012 : return;
1013 :
1014 0 : uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek());
1015 0 : point.next();
1016 :
1017 0 : if (ownership < JS::SCTAG_TMO_FIRST_OWNED)
1018 : continue;
1019 :
1020 0 : if (ownership == JS::SCTAG_TMO_ALLOC_DATA) {
1021 0 : js_free(content);
1022 0 : } else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) {
1023 0 : JS_ReleaseMappedArrayBufferContents(content, extraData);
1024 0 : } else if (freeTransfer) {
1025 0 : freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, closure_);
1026 : } else {
1027 0 : MOZ_ASSERT(false, "unknown ownership");
1028 : }
1029 : }
1030 : }
1031 :
1032 : JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
1033 :
1034 1170 : JSStructuredCloneWriter::~JSStructuredCloneWriter()
1035 : {
1036 : // Free any transferable data left lying around in the buffer
1037 0 : if (out.count())
1038 0 : out.discardTransferables();
1039 234 : }
1040 :
1041 : bool
1042 234 : JSStructuredCloneWriter::parseTransferable()
1043 : {
1044 : // NOTE: The transferables set is tested for non-emptiness at various
1045 : // junctures in structured cloning, so this set must be initialized
1046 : // by this method in all non-error cases.
1047 468 : MOZ_ASSERT(!transferableObjects.initialized(),
1048 : "parseTransferable called with stale data");
1049 :
1050 0 : if (transferable.isNull() || transferable.isUndefined())
1051 466 : return transferableObjects.init(0);
1052 :
1053 1 : if (!transferable.isObject())
1054 0 : return reportDataCloneError(JS_SCERR_TRANSFERABLE);
1055 :
1056 0 : JSContext* cx = context();
1057 3 : RootedObject array(cx, &transferable.toObject());
1058 : bool isArray;
1059 1 : if (!JS_IsArrayObject(cx, array, &isArray))
1060 : return false;
1061 1 : if (!isArray)
1062 0 : return reportDataCloneError(JS_SCERR_TRANSFERABLE);
1063 :
1064 : uint32_t length;
1065 1 : if (!JS_GetArrayLength(cx, array, &length))
1066 : return false;
1067 :
1068 : // Initialize the set for the provided array's length.
1069 2 : if (!transferableObjects.init(length))
1070 : return false;
1071 :
1072 1 : if (length == 0)
1073 : return true;
1074 :
1075 0 : RootedValue v(context());
1076 3 : RootedObject tObj(context());
1077 :
1078 0 : for (uint32_t i = 0; i < length; ++i) {
1079 0 : if (!CheckForInterrupt(cx))
1080 0 : return false;
1081 :
1082 2 : if (!JS_GetElement(cx, array, i, &v))
1083 : return false;
1084 :
1085 0 : if (!v.isObject())
1086 0 : return reportDataCloneError(JS_SCERR_TRANSFERABLE);
1087 2 : tObj = &v.toObject();
1088 :
1089 0 : RootedObject unwrappedObj(cx, CheckedUnwrap(tObj));
1090 1 : if (!unwrappedObj) {
1091 0 : ReportAccessDenied(cx);
1092 0 : return false;
1093 : }
1094 :
1095 : // Shared memory cannot be transferred because it is not possible (nor
1096 : // desirable) to detach the memory in agents that already hold a
1097 : // reference to it.
1098 :
1099 1 : if (unwrappedObj->is<SharedArrayBufferObject>())
1100 0 : return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE);
1101 :
1102 0 : else if (unwrappedObj->is<WasmMemoryObject>()) {
1103 0 : if (unwrappedObj->as<WasmMemoryObject>().isShared())
1104 0 : return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE);
1105 : }
1106 :
1107 : // External array buffers may be able to be transferred in the future,
1108 : // but that is not currently implemented.
1109 :
1110 0 : else if (unwrappedObj->is<ArrayBufferObject>()) {
1111 1 : if (unwrappedObj->as<ArrayBufferObject>().isExternal())
1112 0 : return reportDataCloneError(JS_SCERR_TRANSFERABLE);
1113 : }
1114 :
1115 : else {
1116 0 : if (!out.buf.callbacks_ || !out.buf.callbacks_->canTransfer)
1117 0 : return reportDataCloneError(JS_SCERR_TRANSFERABLE);
1118 :
1119 0 : JSAutoRealm ar(cx, unwrappedObj);
1120 0 : if (!out.buf.callbacks_->canTransfer(cx, unwrappedObj, out.buf.closure_))
1121 0 : return false;
1122 : }
1123 :
1124 : // No duplicates allowed
1125 0 : auto p = transferableObjects.lookupForAdd(tObj);
1126 0 : if (p)
1127 0 : return reportDataCloneError(JS_SCERR_DUP_TRANSFERABLE);
1128 :
1129 2 : if (!transferableObjects.add(p, tObj))
1130 : return false;
1131 : }
1132 :
1133 : return true;
1134 : }
1135 :
1136 : bool
1137 0 : JSStructuredCloneWriter::reportDataCloneError(uint32_t errorId)
1138 : {
1139 0 : ReportDataCloneError(context(), out.buf.callbacks_, errorId);
1140 0 : return false;
1141 : }
1142 :
1143 : bool
1144 17505 : JSStructuredCloneWriter::writeString(uint32_t tag, JSString* str)
1145 : {
1146 0 : JSLinearString* linear = str->ensureLinear(context());
1147 17505 : if (!linear)
1148 : return false;
1149 :
1150 : static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
1151 :
1152 0 : uint32_t length = linear->length();
1153 0 : uint32_t lengthAndEncoding = length | (uint32_t(linear->hasLatin1Chars()) << 31);
1154 35010 : if (!out.writePair(tag, lengthAndEncoding))
1155 : return false;
1156 :
1157 0 : JS::AutoCheckCannotGC nogc;
1158 0 : return linear->hasLatin1Chars()
1159 0 : ? out.writeChars(linear->latin1Chars(nogc), length)
1160 17679 : : out.writeChars(linear->twoByteChars(nogc), length);
1161 : }
1162 :
1163 : inline void
1164 19632 : JSStructuredCloneWriter::checkStack()
1165 : {
1166 : #ifdef DEBUG
1167 : // To avoid making serialization O(n^2), limit stack-checking at 10.
1168 19632 : const size_t MAX = 10;
1169 :
1170 0 : size_t limit = Min(counts.length(), MAX);
1171 39264 : MOZ_ASSERT(objs.length() == counts.length());
1172 : size_t total = 0;
1173 0 : for (size_t i = 0; i < limit; i++) {
1174 0 : MOZ_ASSERT(total + counts[i] >= total);
1175 103378 : total += counts[i];
1176 : }
1177 0 : if (counts.length() <= MAX)
1178 38954 : MOZ_ASSERT(total == entries.length());
1179 : else
1180 310 : MOZ_ASSERT(total <= entries.length());
1181 :
1182 0 : size_t j = objs.length();
1183 0 : for (size_t i = 0; i < limit; i++) {
1184 0 : --j;
1185 310134 : MOZ_ASSERT(memory.has(&objs[j].toObject()));
1186 : }
1187 : #endif
1188 19632 : }
1189 :
1190 : /*
1191 : * Write out a typed array. Note that post-v1 structured clone buffers do not
1192 : * perform endianness conversion on stored data, so multibyte typed arrays
1193 : * cannot be deserialized into a different endianness machine. Endianness
1194 : * conversion would prevent sharing ArrayBuffers: if you have Int8Array and
1195 : * Int16Array views of the same ArrayBuffer, should the data bytes be
1196 : * byte-swapped when writing or not? The Int8Array requires them to not be
1197 : * swapped; the Int16Array requires that they are.
1198 : */
1199 : bool
1200 1 : JSStructuredCloneWriter::writeTypedArray(HandleObject obj)
1201 : {
1202 0 : Rooted<TypedArrayObject*> tarr(context(), &CheckedUnwrap(obj)->as<TypedArrayObject>());
1203 4 : JSAutoRealm ar(context(), tarr);
1204 :
1205 2 : if (!TypedArrayObject::ensureHasBuffer(context(), tarr))
1206 : return false;
1207 :
1208 2 : if (!out.writePair(SCTAG_TYPED_ARRAY_OBJECT, tarr->length()))
1209 : return false;
1210 0 : uint64_t type = tarr->type();
1211 1 : if (!out.write(type))
1212 : return false;
1213 :
1214 : // Write out the ArrayBuffer tag and contents
1215 0 : RootedValue val(context(), TypedArrayObject::bufferValue(tarr));
1216 1 : if (!startWrite(val))
1217 : return false;
1218 :
1219 2 : return out.write(tarr->byteOffset());
1220 : }
1221 :
1222 : bool
1223 0 : JSStructuredCloneWriter::writeDataView(HandleObject obj)
1224 : {
1225 0 : Rooted<DataViewObject*> view(context(), &CheckedUnwrap(obj)->as<DataViewObject>());
1226 0 : JSAutoRealm ar(context(), view);
1227 :
1228 0 : if (!out.writePair(SCTAG_DATA_VIEW_OBJECT, view->byteLength()))
1229 : return false;
1230 :
1231 : // Write out the ArrayBuffer tag and contents
1232 0 : RootedValue val(context(), DataViewObject::bufferValue(view));
1233 0 : if (!startWrite(val))
1234 : return false;
1235 :
1236 0 : return out.write(view->byteOffset());
1237 : }
1238 :
1239 : bool
1240 0 : JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj)
1241 : {
1242 0 : Rooted<ArrayBufferObject*> buffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
1243 0 : JSAutoRealm ar(context(), buffer);
1244 :
1245 0 : return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer->byteLength()) &&
1246 0 : out.writeBytes(buffer->dataPointer(), buffer->byteLength());
1247 : }
1248 :
1249 : bool
1250 0 : JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj)
1251 : {
1252 0 : MOZ_ASSERT(CheckedUnwrap(obj) && CheckedUnwrap(obj)->is<SharedArrayBufferObject>());
1253 :
1254 0 : if (!cloneDataPolicy.isSharedArrayBufferAllowed()) {
1255 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_NOT_CLONABLE,
1256 0 : "SharedArrayBuffer");
1257 0 : return false;
1258 : }
1259 :
1260 : // We must not transmit SAB pointers (including for WebAssembly.Memory)
1261 : // cross-process. The cloneDataPolicy should have guarded against this;
1262 : // since it did not then throw, with a very explicit message.
1263 :
1264 0 : if (output().scope() > JS::StructuredCloneScope::SameProcessDifferentThread) {
1265 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SHMEM_POLICY);
1266 0 : return false;
1267 : }
1268 :
1269 0 : Rooted<SharedArrayBufferObject*> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
1270 0 : SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject();
1271 :
1272 0 : if (!out.buf.refsHeld_.acquire(context(), rawbuf))
1273 : return false;
1274 :
1275 : // We must serialize the length so that the buffer object arrives in the
1276 : // receiver with the same length, and not with the length read from the
1277 : // rawbuf - that length can be different, and it can change at any time.
1278 :
1279 0 : intptr_t p = reinterpret_cast<intptr_t>(rawbuf);
1280 0 : uint32_t byteLength = sharedArrayBuffer->byteLength();
1281 0 : return out.writePair(SCTAG_SHARED_ARRAY_BUFFER_OBJECT, static_cast<uint32_t>(sizeof(p))) &&
1282 0 : out.writeBytes(&byteLength, sizeof(byteLength)) &&
1283 0 : out.writeBytes(&p, sizeof(p));
1284 : }
1285 :
1286 : bool
1287 0 : JSStructuredCloneWriter::writeSharedWasmMemory(HandleObject obj)
1288 : {
1289 0 : MOZ_ASSERT(CheckedUnwrap(obj) && CheckedUnwrap(obj)->is<WasmMemoryObject>());
1290 :
1291 : // Check the policy here so that we can report a sane error.
1292 0 : if (!cloneDataPolicy.isSharedArrayBufferAllowed()) {
1293 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_NOT_CLONABLE,
1294 0 : "WebAssembly.Memory");
1295 0 : return false;
1296 : }
1297 :
1298 : // If this changes, might need to change what we write.
1299 : MOZ_ASSERT(WasmMemoryObject::RESERVED_SLOTS == 2);
1300 :
1301 0 : Rooted<WasmMemoryObject*> memoryObj(context(), &CheckedUnwrap(obj)->as<WasmMemoryObject>());
1302 0 : Rooted<SharedArrayBufferObject*> sab(context(), &memoryObj->buffer().as<SharedArrayBufferObject>());
1303 :
1304 0 : return out.writePair(SCTAG_SHARED_WASM_MEMORY_OBJECT, 0) &&
1305 0 : writeSharedArrayBuffer(sab);
1306 : }
1307 :
1308 : bool
1309 5492 : JSStructuredCloneWriter::startObject(HandleObject obj, bool* backref)
1310 : {
1311 : // Handle cycles in the object graph.
1312 0 : CloneMemory::AddPtr p = memory.lookupForAdd(obj);
1313 0 : if ((*backref = p.found()))
1314 0 : return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value());
1315 0 : if (!memory.add(p, obj, memory.count())) {
1316 0 : ReportOutOfMemory(context());
1317 0 : return false;
1318 : }
1319 :
1320 1 : if (memory.count() == UINT32_MAX) {
1321 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_NEED_DIET,
1322 0 : "object graph to serialize");
1323 0 : return false;
1324 : }
1325 :
1326 : return true;
1327 : }
1328 :
1329 : bool
1330 5289 : JSStructuredCloneWriter::traverseObject(HandleObject obj)
1331 : {
1332 : // Get enumerable property ids and put them in reverse order so that they
1333 : // will come off the stack in forward order.
1334 0 : AutoIdVector properties(context());
1335 5289 : if (!GetPropertyKeys(context(), obj, JSITER_OWNONLY, &properties))
1336 : return false;
1337 :
1338 0 : for (size_t i = properties.length(); i > 0; --i) {
1339 0 : MOZ_ASSERT(JSID_IS_STRING(properties[i - 1]) || JSID_IS_INT(properties[i - 1]));
1340 0 : RootedValue val(context(), IdToValue(properties[i - 1]));
1341 1 : if (!entries.append(val))
1342 0 : return false;
1343 : }
1344 :
1345 : // Push obj and count to the stack.
1346 26445 : if (!objs.append(ObjectValue(*obj)) || !counts.append(properties.length()))
1347 : return false;
1348 :
1349 5289 : checkStack();
1350 :
1351 : // Write the header for obj.
1352 : ESClass cls;
1353 5289 : if (!GetBuiltinClass(context(), obj, &cls))
1354 : return false;
1355 10578 : return out.writePair(cls == ESClass::Array ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
1356 : }
1357 :
1358 : bool
1359 65 : JSStructuredCloneWriter::traverseMap(HandleObject obj)
1360 : {
1361 390 : Rooted<GCVector<Value>> newEntries(context(), GCVector<Value>(context()));
1362 : {
1363 : // If there is no wrapper, the compartment munging is a no-op.
1364 0 : RootedObject unwrapped(context(), CheckedUnwrap(obj));
1365 0 : MOZ_ASSERT(unwrapped);
1366 0 : JSAutoRealm ar(context(), unwrapped);
1367 1 : if (!MapObject::getKeysAndValuesInterleaved(unwrapped, &newEntries))
1368 0 : return false;
1369 : }
1370 260 : if (!context()->compartment()->wrap(context(), &newEntries))
1371 : return false;
1372 :
1373 0 : for (size_t i = newEntries.length(); i > 0; --i) {
1374 2706 : if (!entries.append(newEntries[i - 1]))
1375 : return false;
1376 : }
1377 :
1378 : // Push obj and count to the stack.
1379 325 : if (!objs.append(ObjectValue(*obj)) || !counts.append(newEntries.length()))
1380 : return false;
1381 :
1382 65 : checkStack();
1383 :
1384 : // Write the header for obj.
1385 130 : return out.writePair(SCTAG_MAP_OBJECT, 0);
1386 : }
1387 :
1388 : bool
1389 56 : JSStructuredCloneWriter::traverseSet(HandleObject obj)
1390 : {
1391 336 : Rooted<GCVector<Value>> keys(context(), GCVector<Value>(context()));
1392 : {
1393 : // If there is no wrapper, the compartment munging is a no-op.
1394 0 : RootedObject unwrapped(context(), CheckedUnwrap(obj));
1395 0 : MOZ_ASSERT(unwrapped);
1396 0 : JSAutoRealm ar(context(), unwrapped);
1397 1 : if (!SetObject::keys(context(), unwrapped, &keys))
1398 0 : return false;
1399 : }
1400 224 : if (!context()->compartment()->wrap(context(), &keys))
1401 : return false;
1402 :
1403 0 : for (size_t i = keys.length(); i > 0; --i) {
1404 234 : if (!entries.append(keys[i - 1]))
1405 : return false;
1406 : }
1407 :
1408 : // Push obj and count to the stack.
1409 280 : if (!objs.append(ObjectValue(*obj)) || !counts.append(keys.length()))
1410 : return false;
1411 :
1412 56 : checkStack();
1413 :
1414 : // Write the header for obj.
1415 112 : return out.writePair(SCTAG_SET_OBJECT, 0);
1416 : }
1417 :
1418 : // Objects are written as a "preorder" traversal of the object graph: object
1419 : // "headers" (the class tag and any data needed for initial construction) are
1420 : // visited first, then the children are recursed through (where children are
1421 : // properties, Set or Map entries, etc.). So for example
1422 : //
1423 : // m = new Map();
1424 : // m.set(key1 = {}, value1 = {})
1425 : //
1426 : // would be stored as
1427 : //
1428 : // <Map tag>
1429 : // <key1 class tag>
1430 : // <value1 class tag>
1431 : // <end-of-children marker for key1>
1432 : // <end-of-children marker for value1>
1433 : // <end-of-children marker for Map>
1434 : //
1435 : // Notice how the end-of-children marker for key1 is sandwiched between the
1436 : // value1 beginning and end.
1437 : bool
1438 0 : JSStructuredCloneWriter::traverseSavedFrame(HandleObject obj)
1439 : {
1440 0 : RootedObject unwrapped(context(), js::CheckedUnwrap(obj));
1441 0 : MOZ_ASSERT(unwrapped && unwrapped->is<SavedFrame>());
1442 :
1443 0 : RootedSavedFrame savedFrame(context(), &unwrapped->as<SavedFrame>());
1444 :
1445 0 : RootedObject parent(context(), savedFrame->getParent());
1446 0 : if (!context()->compartment()->wrap(context(), &parent))
1447 : return false;
1448 :
1449 0 : if (!objs.append(ObjectValue(*obj)) ||
1450 0 : !entries.append(parent ? ObjectValue(*parent) : NullValue()) ||
1451 0 : !counts.append(1))
1452 : {
1453 : return false;
1454 : }
1455 :
1456 0 : checkStack();
1457 :
1458 : // Write the SavedFrame tag and the SavedFrame's principals.
1459 :
1460 0 : if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsSystem) {
1461 0 : if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
1462 : SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM))
1463 : {
1464 : return false;
1465 : };
1466 0 : } else if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsNotSystem) {
1467 0 : if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
1468 : SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM))
1469 : {
1470 : return false;
1471 : }
1472 : } else {
1473 0 : if (auto principals = savedFrame->getPrincipals()) {
1474 0 : if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_JSPRINCIPALS) ||
1475 0 : !principals->write(context(), this))
1476 : {
1477 : return false;
1478 : }
1479 : } else {
1480 0 : if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_NULL_JSPRINCIPALS))
1481 : return false;
1482 : }
1483 : }
1484 :
1485 : // Write the SavedFrame's reserved slots, except for the parent, which is
1486 : // queued on objs for further traversal.
1487 :
1488 0 : RootedValue val(context());
1489 :
1490 0 : context()->markAtom(savedFrame->getSource());
1491 0 : val = StringValue(savedFrame->getSource());
1492 0 : if (!startWrite(val))
1493 : return false;
1494 :
1495 0 : val = NumberValue(savedFrame->getLine());
1496 0 : if (!startWrite(val))
1497 : return false;
1498 :
1499 0 : val = NumberValue(savedFrame->getColumn());
1500 0 : if (!startWrite(val))
1501 : return false;
1502 :
1503 0 : auto name = savedFrame->getFunctionDisplayName();
1504 0 : if (name)
1505 0 : context()->markAtom(name);
1506 0 : val = name ? StringValue(name) : NullValue();
1507 0 : if (!startWrite(val))
1508 : return false;
1509 :
1510 0 : auto cause = savedFrame->getAsyncCause();
1511 0 : if (cause)
1512 0 : context()->markAtom(cause);
1513 0 : val = cause ? StringValue(cause) : NullValue();
1514 0 : if (!startWrite(val))
1515 : return false;
1516 :
1517 0 : return true;
1518 : }
1519 :
1520 : bool
1521 27699 : JSStructuredCloneWriter::startWrite(HandleValue v)
1522 : {
1523 27699 : assertSameCompartment(context(), v);
1524 :
1525 0 : if (v.isString()) {
1526 0 : return writeString(SCTAG_STRING, v.toString());
1527 0 : } else if (v.isInt32()) {
1528 0 : return out.writePair(SCTAG_INT32, v.toInt32());
1529 0 : } else if (v.isDouble()) {
1530 0 : return out.writeDouble(v.toDouble());
1531 0 : } else if (v.isBoolean()) {
1532 0 : return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
1533 0 : } else if (v.isNull()) {
1534 0 : return out.writePair(SCTAG_NULL, 0);
1535 0 : } else if (v.isUndefined()) {
1536 0 : return out.writePair(SCTAG_UNDEFINED, 0);
1537 0 : } else if (v.isObject()) {
1538 10984 : RootedObject obj(context(), &v.toObject());
1539 :
1540 : bool backref;
1541 0 : if (!startObject(obj, &backref))
1542 0 : return false;
1543 5492 : if (backref)
1544 : return true;
1545 :
1546 : ESClass cls;
1547 10962 : if (!GetBuiltinClass(context(), obj, &cls))
1548 : return false;
1549 :
1550 0 : if (cls == ESClass::RegExp) {
1551 0 : RegExpShared* re = RegExpToShared(context(), obj);
1552 0 : if (!re)
1553 : return false;
1554 0 : return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
1555 0 : writeString(SCTAG_STRING, re->getSource());
1556 0 : } else if (cls == ESClass::Date) {
1557 0 : RootedValue unboxed(context());
1558 144 : if (!Unbox(context(), obj, &unboxed))
1559 : return false;
1560 0 : return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
1561 0 : } else if (JS_IsTypedArrayObject(obj)) {
1562 0 : return writeTypedArray(obj);
1563 0 : } else if (JS_IsDataViewObject(obj)) {
1564 0 : return writeDataView(obj);
1565 0 : } else if (JS_IsArrayBufferObject(obj) && JS_ArrayBufferHasData(obj)) {
1566 0 : return writeArrayBuffer(obj);
1567 0 : } else if (JS_IsSharedArrayBufferObject(obj)) {
1568 0 : return writeSharedArrayBuffer(obj);
1569 0 : } else if (wasm::IsSharedWasmMemoryObject(obj)) {
1570 0 : return writeSharedWasmMemory(obj);
1571 0 : } else if (cls == ESClass::Object) {
1572 0 : return traverseObject(obj);
1573 0 : } else if (cls == ESClass::Array) {
1574 0 : return traverseObject(obj);
1575 0 : } else if (cls == ESClass::Boolean) {
1576 0 : RootedValue unboxed(context());
1577 0 : if (!Unbox(context(), obj, &unboxed))
1578 : return false;
1579 0 : return out.writePair(SCTAG_BOOLEAN_OBJECT, unboxed.toBoolean());
1580 0 : } else if (cls == ESClass::Number) {
1581 0 : RootedValue unboxed(context());
1582 0 : if (!Unbox(context(), obj, &unboxed))
1583 : return false;
1584 0 : return out.writePair(SCTAG_NUMBER_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
1585 0 : } else if (cls == ESClass::String) {
1586 0 : RootedValue unboxed(context());
1587 0 : if (!Unbox(context(), obj, &unboxed))
1588 : return false;
1589 0 : return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
1590 0 : } else if (cls == ESClass::Map) {
1591 0 : return traverseMap(obj);
1592 0 : } else if (cls == ESClass::Set) {
1593 0 : return traverseSet(obj);
1594 0 : } else if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
1595 0 : return traverseSavedFrame(obj);
1596 : }
1597 :
1598 0 : if (out.buf.callbacks_ && out.buf.callbacks_->write)
1599 66 : return out.buf.callbacks_->write(context(), this, obj, out.buf.closure_);
1600 : // else fall through
1601 : }
1602 :
1603 0 : return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE);
1604 : }
1605 :
1606 : bool
1607 0 : JSStructuredCloneWriter::writeHeader()
1608 : {
1609 702 : return out.writePair(SCTAG_HEADER, (uint32_t)output().scope());
1610 : }
1611 :
1612 : bool
1613 234 : JSStructuredCloneWriter::writeTransferMap()
1614 : {
1615 468 : if (transferableObjects.empty())
1616 : return true;
1617 :
1618 2 : if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_UNREAD))
1619 : return false;
1620 :
1621 2 : if (!out.write(transferableObjects.count()))
1622 : return false;
1623 :
1624 0 : RootedObject obj(context());
1625 0 : for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
1626 0 : obj = tr.front();
1627 1 : if (!memory.put(obj, memory.count())) {
1628 0 : ReportOutOfMemory(context());
1629 0 : return false;
1630 : }
1631 :
1632 : // Emit a placeholder pointer. We defer stealing the data until later
1633 : // (and, if necessary, detaching this object if it's an ArrayBuffer).
1634 2 : if (!out.writePair(SCTAG_TRANSFER_MAP_PENDING_ENTRY, JS::SCTAG_TMO_UNFILLED))
1635 : return false;
1636 2 : if (!out.writePtr(nullptr)) // Pointer to ArrayBuffer contents.
1637 : return false;
1638 1 : if (!out.write(0)) // extraData
1639 : return false;
1640 : }
1641 :
1642 1 : return true;
1643 : }
1644 :
1645 : bool
1646 234 : JSStructuredCloneWriter::transferOwnership()
1647 : {
1648 468 : if (transferableObjects.empty())
1649 : return true;
1650 :
1651 : // Walk along the transferables and the transfer map at the same time,
1652 : // grabbing out pointers from the transferables and stuffing them into the
1653 : // transfer map.
1654 0 : auto point = out.iter();
1655 0 : MOZ_RELEASE_ASSERT(point.canPeek());
1656 0 : MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_HEADER);
1657 0 : point++;
1658 0 : MOZ_RELEASE_ASSERT(point.canPeek());
1659 0 : MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
1660 0 : point++;
1661 0 : MOZ_RELEASE_ASSERT(point.canPeek());
1662 0 : MOZ_ASSERT(NativeEndian::swapFromLittleEndian(point.peek()) == transferableObjects.count());
1663 1 : point++;
1664 :
1665 0 : JSContext* cx = context();
1666 0 : RootedObject obj(cx);
1667 0 : JS::StructuredCloneScope scope = output().scope();
1668 0 : for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
1669 2 : obj = tr.front();
1670 :
1671 : uint32_t tag;
1672 : JS::TransferableOwnership ownership;
1673 : void* content;
1674 : uint64_t extraData;
1675 :
1676 : #if DEBUG
1677 0 : SCInput::getPair(point.peek(), &tag, (uint32_t*) &ownership);
1678 0 : MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY);
1679 1 : MOZ_ASSERT(ownership == JS::SCTAG_TMO_UNFILLED);
1680 : #endif
1681 :
1682 : ESClass cls;
1683 0 : if (!GetBuiltinClass(cx, obj, &cls))
1684 0 : return false;
1685 :
1686 0 : if (cls == ESClass::ArrayBuffer) {
1687 1 : tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
1688 :
1689 : // The current setup of the array buffer inheritance hierarchy doesn't
1690 : // lend itself well to generic manipulation via proxies.
1691 0 : Rooted<ArrayBufferObject*> arrayBuffer(cx, &CheckedUnwrap(obj)->as<ArrayBufferObject>());
1692 3 : JSAutoRealm ar(cx, arrayBuffer);
1693 :
1694 0 : if (arrayBuffer->isDetached()) {
1695 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1696 0 : return false;
1697 : }
1698 :
1699 1 : size_t nbytes = arrayBuffer->byteLength();
1700 :
1701 0 : if (arrayBuffer->isWasm() || arrayBuffer->isPreparedForAsmJS()) {
1702 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_TRANSFER);
1703 0 : return false;
1704 : }
1705 :
1706 1 : if (arrayBuffer->isDetached()) {
1707 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1708 0 : return false;
1709 : }
1710 :
1711 1 : if (scope == JS::StructuredCloneScope::DifferentProcess ||
1712 : scope == JS::StructuredCloneScope::DifferentProcessForIndexedDB)
1713 : {
1714 : // Write Transferred ArrayBuffers in DifferentProcess scope at
1715 : // the end of the clone buffer, and store the offset within the
1716 : // buffer to where the ArrayBuffer was written. Note that this
1717 : // will invalidate the current position iterator.
1718 :
1719 0 : size_t pointOffset = out.offset(point);
1720 0 : tag = SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER;
1721 0 : ownership = JS::SCTAG_TMO_UNOWNED;
1722 0 : content = nullptr;
1723 0 : extraData = out.tell() - pointOffset; // Offset from tag to current end of buffer
1724 0 : if (!writeArrayBuffer(arrayBuffer)) {
1725 0 : ReportOutOfMemory(cx);
1726 0 : return false;
1727 : }
1728 :
1729 : // Must refresh the point iterator after its collection has
1730 : // been modified.
1731 0 : point = out.iter();
1732 0 : point += pointOffset;
1733 :
1734 0 : if (!JS_DetachArrayBuffer(cx, arrayBuffer))
1735 : return false;
1736 : } else {
1737 1 : bool hasStealableContents = arrayBuffer->hasStealableContents();
1738 :
1739 : ArrayBufferObject::BufferContents bufContents =
1740 0 : ArrayBufferObject::stealContents(cx, arrayBuffer, hasStealableContents);
1741 1 : if (!bufContents)
1742 0 : return false; // out of memory
1743 :
1744 0 : content = bufContents.data();
1745 0 : if (bufContents.kind() == ArrayBufferObject::MAPPED)
1746 0 : ownership = JS::SCTAG_TMO_MAPPED_DATA;
1747 : else
1748 0 : ownership = JS::SCTAG_TMO_ALLOC_DATA;
1749 1 : extraData = nbytes;
1750 : }
1751 : } else {
1752 0 : if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer)
1753 0 : return reportDataCloneError(JS_SCERR_TRANSFERABLE);
1754 0 : if (!out.buf.callbacks_->writeTransfer(cx, obj, out.buf.closure_, &tag, &ownership, &content, &extraData))
1755 : return false;
1756 0 : MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
1757 : }
1758 :
1759 0 : point.write(NativeEndian::swapToLittleEndian(PairToUInt64(tag, ownership)));
1760 0 : point.next();
1761 0 : point.write(NativeEndian::swapToLittleEndian(reinterpret_cast<uint64_t>(content)));
1762 0 : point.next();
1763 0 : point.write(NativeEndian::swapToLittleEndian(extraData));
1764 1 : point.next();
1765 : }
1766 :
1767 : #if DEBUG
1768 : // Make sure there aren't any more transfer map entries after the expected
1769 : // number we read out.
1770 1 : if (!point.done()) {
1771 : uint32_t tag, data;
1772 0 : SCInput::getPair(point.peek(), &tag, &data);
1773 1 : MOZ_ASSERT(tag < SCTAG_TRANSFER_MAP_HEADER || tag >= SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES);
1774 : }
1775 : #endif
1776 : return true;
1777 : }
1778 :
1779 : bool
1780 234 : JSStructuredCloneWriter::write(HandleValue v)
1781 : {
1782 234 : if (!startWrite(v))
1783 : return false;
1784 :
1785 0 : while (!counts.empty()) {
1786 0 : RootedObject obj(context(), &objs.back().toObject());
1787 0 : AutoRealm ar(context(), obj);
1788 0 : if (counts.back()) {
1789 0 : counts.back()--;
1790 0 : RootedValue key(context(), entries.back());
1791 0 : entries.popBack();
1792 13771 : checkStack();
1793 :
1794 : ESClass cls;
1795 0 : if (!GetBuiltinClass(context(), obj, &cls))
1796 0 : return false;
1797 :
1798 0 : if (cls == ESClass::Map) {
1799 0 : counts.back()--;
1800 0 : RootedValue val(context(), entries.back());
1801 0 : entries.popBack();
1802 451 : checkStack();
1803 :
1804 1 : if (!startWrite(key) || !startWrite(val))
1805 0 : return false;
1806 0 : } else if (cls == ESClass::Set || SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
1807 78 : if (!startWrite(key))
1808 : return false;
1809 : } else {
1810 0 : RootedId id(context());
1811 0 : if (!ValueToId<CanGC>(context(), key, &id))
1812 0 : return false;
1813 31666 : MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
1814 :
1815 : // If obj still has an own property named id, write it out.
1816 : // The cost of re-checking could be avoided by using
1817 : // NativeIterators.
1818 : bool found;
1819 39726 : if (!HasOwnProperty(context(), obj, id, &found))
1820 : return false;
1821 :
1822 0 : if (found) {
1823 0 : RootedValue val(context());
1824 0 : if (!startWrite(key) ||
1825 0 : !GetProperty(context(), obj, obj, id, &val) ||
1826 13242 : !startWrite(val))
1827 : {
1828 0 : return false;
1829 : }
1830 : }
1831 : }
1832 : } else {
1833 10820 : if (!out.writePair(SCTAG_END_OF_KEYS, 0))
1834 : return false;
1835 0 : objs.popBack();
1836 5410 : counts.popBack();
1837 : }
1838 : }
1839 :
1840 0 : memory.clear();
1841 234 : return transferOwnership();
1842 : }
1843 :
1844 : bool
1845 97 : JSStructuredCloneReader::checkDouble(double d)
1846 : {
1847 1 : if (!JS::IsCanonicalized(d)) {
1848 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
1849 0 : "unrecognized NaN");
1850 0 : return false;
1851 : }
1852 : return true;
1853 : }
1854 :
1855 : namespace {
1856 :
1857 : template <typename CharT>
1858 : class Chars {
1859 : JSContext* cx;
1860 : CharT* p;
1861 : public:
1862 0 : explicit Chars(JSContext* cx) : cx(cx), p(nullptr) {}
1863 32504 : ~Chars() { js_free(p); }
1864 :
1865 0 : bool allocate(size_t len) {
1866 16252 : MOZ_ASSERT(!p);
1867 : // We're going to null-terminate!
1868 0 : p = cx->pod_malloc<CharT>(len + 1);
1869 0 : if (p) {
1870 0 : p[len] = CharT(0);
1871 16252 : return true;
1872 : }
1873 : return false;
1874 : }
1875 : CharT* get() { return p; }
1876 16252 : void forget() { p = nullptr; }
1877 : };
1878 :
1879 : } // anonymous namespace
1880 :
1881 : template <typename CharT>
1882 : JSString*
1883 16252 : JSStructuredCloneReader::readStringImpl(uint32_t nchars)
1884 : {
1885 1 : if (nchars > JSString::MAX_LENGTH) {
1886 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
1887 : "string length");
1888 0 : return nullptr;
1889 : }
1890 0 : Chars<CharT> chars(context());
1891 32504 : if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars))
1892 : return nullptr;
1893 0 : JSString* str = NewString<CanGC>(context(), chars.get(), nchars);
1894 0 : if (str)
1895 16252 : chars.forget();
1896 : return str;
1897 : }
1898 :
1899 : JSString*
1900 16252 : JSStructuredCloneReader::readString(uint32_t data)
1901 : {
1902 0 : uint32_t nchars = data & JS_BITMASK(31);
1903 0 : bool latin1 = data & (1 << 31);
1904 16252 : return latin1 ? readStringImpl<Latin1Char>(nchars) : readStringImpl<char16_t>(nchars);
1905 : }
1906 :
1907 : static uint32_t
1908 0 : TagToV1ArrayType(uint32_t tag)
1909 : {
1910 0 : MOZ_ASSERT(tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX);
1911 0 : return tag - SCTAG_TYPED_ARRAY_V1_MIN;
1912 : }
1913 :
1914 : bool
1915 1 : JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
1916 : bool v1Read)
1917 : {
1918 1 : if (arrayType > Scalar::Uint8Clamped) {
1919 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
1920 0 : "unhandled typed array element type");
1921 0 : return false;
1922 : }
1923 :
1924 : // Push a placeholder onto the allObjs list to stand in for the typed array.
1925 0 : uint32_t placeholderIndex = allObjs.length();
1926 0 : Value dummy = UndefinedValue();
1927 2 : if (!allObjs.append(dummy))
1928 : return false;
1929 :
1930 : // Read the ArrayBuffer object and its contents (but no properties)
1931 4 : RootedValue v(context());
1932 : uint32_t byteOffset;
1933 1 : if (v1Read) {
1934 0 : if (!readV1ArrayBuffer(arrayType, nelems, &v))
1935 : return false;
1936 : byteOffset = 0;
1937 : } else {
1938 1 : if (!startRead(&v))
1939 0 : return false;
1940 : uint64_t n;
1941 1 : if (!in.read(&n))
1942 : return false;
1943 1 : byteOffset = n;
1944 : }
1945 1 : if (!v.isObject() || !v.toObject().is<ArrayBufferObjectMaybeShared>()) {
1946 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
1947 0 : "typed array must be backed by an ArrayBuffer");
1948 0 : return false;
1949 : }
1950 :
1951 0 : RootedObject buffer(context(), &v.toObject());
1952 4 : RootedObject obj(context(), nullptr);
1953 :
1954 1 : switch (arrayType) {
1955 : case Scalar::Int8:
1956 0 : obj = JS_NewInt8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
1957 0 : break;
1958 : case Scalar::Uint8:
1959 0 : obj = JS_NewUint8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
1960 1 : break;
1961 : case Scalar::Int16:
1962 0 : obj = JS_NewInt16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
1963 0 : break;
1964 : case Scalar::Uint16:
1965 0 : obj = JS_NewUint16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
1966 0 : break;
1967 : case Scalar::Int32:
1968 0 : obj = JS_NewInt32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
1969 0 : break;
1970 : case Scalar::Uint32:
1971 0 : obj = JS_NewUint32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
1972 0 : break;
1973 : case Scalar::Float32:
1974 0 : obj = JS_NewFloat32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
1975 0 : break;
1976 : case Scalar::Float64:
1977 0 : obj = JS_NewFloat64ArrayWithBuffer(context(), buffer, byteOffset, nelems);
1978 0 : break;
1979 : case Scalar::Uint8Clamped:
1980 0 : obj = JS_NewUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems);
1981 0 : break;
1982 : default:
1983 0 : MOZ_CRASH("Can't happen: arrayType range checked above");
1984 : }
1985 :
1986 1 : if (!obj)
1987 : return false;
1988 2 : vp.setObject(*obj);
1989 :
1990 2 : allObjs[placeholderIndex].set(vp);
1991 :
1992 1 : return true;
1993 : }
1994 :
1995 : bool
1996 0 : JSStructuredCloneReader::readDataView(uint32_t byteLength, MutableHandleValue vp)
1997 : {
1998 : // Push a placeholder onto the allObjs list to stand in for the DataView.
1999 0 : uint32_t placeholderIndex = allObjs.length();
2000 0 : Value dummy = UndefinedValue();
2001 0 : if (!allObjs.append(dummy))
2002 : return false;
2003 :
2004 : // Read the ArrayBuffer object and its contents (but no properties).
2005 0 : RootedValue v(context());
2006 0 : if (!startRead(&v))
2007 : return false;
2008 0 : if (!v.isObject() || !v.toObject().is<ArrayBufferObjectMaybeShared>()) {
2009 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
2010 0 : "DataView must be backed by an ArrayBuffer");
2011 0 : return false;
2012 : }
2013 :
2014 : // Read byteOffset.
2015 : uint64_t n;
2016 0 : if (!in.read(&n))
2017 : return false;
2018 0 : uint32_t byteOffset = n;
2019 :
2020 0 : RootedObject buffer(context(), &v.toObject());
2021 0 : RootedObject obj(context(), JS_NewDataView(context(), buffer, byteOffset, byteLength));
2022 0 : if (!obj)
2023 : return false;
2024 0 : vp.setObject(*obj);
2025 :
2026 0 : allObjs[placeholderIndex].set(vp);
2027 :
2028 0 : return true;
2029 : }
2030 :
2031 : bool
2032 0 : JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, MutableHandleValue vp)
2033 : {
2034 0 : JSObject* obj = ArrayBufferObject::create(context(), nbytes);
2035 0 : if (!obj)
2036 : return false;
2037 0 : vp.setObject(*obj);
2038 0 : ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
2039 0 : MOZ_ASSERT(buffer.byteLength() == nbytes);
2040 0 : return in.readArray(buffer.dataPointer(), nbytes);
2041 : }
2042 :
2043 : bool
2044 0 : JSStructuredCloneReader::readSharedArrayBuffer(MutableHandleValue vp)
2045 : {
2046 : uint32_t byteLength;
2047 0 : if (!in.readBytes(&byteLength, sizeof(byteLength)))
2048 0 : return in.reportTruncated();
2049 :
2050 : intptr_t p;
2051 0 : if (!in.readBytes(&p, sizeof(p)))
2052 0 : return in.reportTruncated();
2053 :
2054 0 : SharedArrayRawBuffer* rawbuf = reinterpret_cast<SharedArrayRawBuffer*>(p);
2055 :
2056 : // There's no guarantee that the receiving agent has enabled shared memory
2057 : // even if the transmitting agent has done so. Ideally we'd check at the
2058 : // transmission point, but that's tricky, and it will be a very rare problem
2059 : // in any case. Just fail at the receiving end if we can't handle it.
2060 :
2061 0 : if (!context()->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
2062 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_DISABLED);
2063 0 : return false;
2064 : }
2065 :
2066 : // The new object will have a new reference to the rawbuf.
2067 :
2068 0 : if (!rawbuf->addReference()) {
2069 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
2070 0 : return false;
2071 : }
2072 :
2073 0 : JSObject* obj = SharedArrayBufferObject::New(context(), rawbuf, byteLength);
2074 0 : if (!obj) {
2075 0 : rawbuf->dropReference();
2076 0 : return false;
2077 : }
2078 :
2079 0 : vp.setObject(*obj);
2080 0 : return true;
2081 : }
2082 :
2083 : bool
2084 0 : JSStructuredCloneReader::readSharedWasmMemory(uint32_t nbytes, MutableHandleValue vp)
2085 : {
2086 0 : if (nbytes != 0) {
2087 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
2088 0 : "invalid shared wasm memory tag");
2089 0 : return false;
2090 : }
2091 :
2092 0 : JSContext* cx = context();
2093 :
2094 : // Read the SharedArrayBuffer object.
2095 0 : RootedValue payload(cx);
2096 0 : if (!startRead(&payload))
2097 : return false;
2098 0 : if (!payload.isObject() || !payload.toObject().is<SharedArrayBufferObject>()) {
2099 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
2100 0 : "shared wasm memory must be backed by a SharedArrayBuffer");
2101 0 : return false;
2102 : }
2103 :
2104 : Rooted<ArrayBufferObjectMaybeShared*> sab(
2105 0 : cx, &payload.toObject().as<SharedArrayBufferObject>());
2106 :
2107 : // Construct the memory.
2108 0 : RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
2109 0 : RootedObject memory(cx, WasmMemoryObject::create(cx, sab, proto));
2110 0 : if (!memory)
2111 : return false;
2112 :
2113 0 : vp.setObject(*memory);
2114 0 : return true;
2115 : }
2116 :
2117 : /*
2118 : * Read in the data for a structured clone version 1 ArrayBuffer, performing
2119 : * endianness-conversion while reading.
2120 : */
2121 : bool
2122 0 : JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems,
2123 : MutableHandleValue vp)
2124 : {
2125 0 : if (arrayType > Scalar::Uint8Clamped) {
2126 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
2127 0 : "invalid TypedArray type");
2128 0 : return false;
2129 : }
2130 :
2131 : mozilla::CheckedInt<size_t> nbytes =
2132 0 : mozilla::CheckedInt<size_t>(nelems) *
2133 0 : TypedArrayElemSize(static_cast<Scalar::Type>(arrayType));
2134 0 : if (!nbytes.isValid() || nbytes.value() > UINT32_MAX) {
2135 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
2136 : JSMSG_SC_BAD_SERIALIZED_DATA,
2137 0 : "invalid typed array size");
2138 0 : return false;
2139 : }
2140 :
2141 0 : JSObject* obj = ArrayBufferObject::create(context(), nbytes.value());
2142 0 : if (!obj)
2143 : return false;
2144 0 : vp.setObject(*obj);
2145 0 : ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
2146 0 : MOZ_ASSERT(buffer.byteLength() == nbytes);
2147 :
2148 0 : switch (arrayType) {
2149 : case Scalar::Int8:
2150 : case Scalar::Uint8:
2151 : case Scalar::Uint8Clamped:
2152 0 : return in.readArray((uint8_t*) buffer.dataPointer(), nelems);
2153 : case Scalar::Int16:
2154 : case Scalar::Uint16:
2155 0 : return in.readArray((uint16_t*) buffer.dataPointer(), nelems);
2156 : case Scalar::Int32:
2157 : case Scalar::Uint32:
2158 : case Scalar::Float32:
2159 0 : return in.readArray((uint32_t*) buffer.dataPointer(), nelems);
2160 : case Scalar::Float64:
2161 0 : return in.readArray((uint64_t*) buffer.dataPointer(), nelems);
2162 : default:
2163 0 : MOZ_CRASH("Can't happen: arrayType range checked by caller");
2164 : }
2165 : }
2166 :
2167 : static bool
2168 0 : PrimitiveToObject(JSContext* cx, MutableHandleValue vp)
2169 : {
2170 0 : JSObject* obj = js::PrimitiveToObject(cx, vp);
2171 0 : if (!obj)
2172 : return false;
2173 :
2174 0 : vp.setObject(*obj);
2175 0 : return true;
2176 : }
2177 :
2178 : bool
2179 25727 : JSStructuredCloneReader::startRead(MutableHandleValue vp)
2180 : {
2181 : uint32_t tag, data;
2182 :
2183 51454 : if (!in.readPair(&tag, &data))
2184 : return false;
2185 :
2186 25727 : switch (tag) {
2187 : case SCTAG_NULL:
2188 : vp.setNull();
2189 : break;
2190 :
2191 : case SCTAG_UNDEFINED:
2192 : vp.setUndefined();
2193 : break;
2194 :
2195 : case SCTAG_INT32:
2196 2781 : vp.setInt32(data);
2197 : break;
2198 :
2199 : case SCTAG_BOOLEAN:
2200 : case SCTAG_BOOLEAN_OBJECT:
2201 0 : vp.setBoolean(!!data);
2202 1376 : if (tag == SCTAG_BOOLEAN_OBJECT && !PrimitiveToObject(context(), vp))
2203 : return false;
2204 : break;
2205 :
2206 : case SCTAG_STRING:
2207 : case SCTAG_STRING_OBJECT: {
2208 0 : JSString* str = readString(data);
2209 16252 : if (!str)
2210 : return false;
2211 0 : vp.setString(str);
2212 16252 : if (tag == SCTAG_STRING_OBJECT && !PrimitiveToObject(context(), vp))
2213 : return false;
2214 : break;
2215 : }
2216 :
2217 : case SCTAG_NUMBER_OBJECT: {
2218 : double d;
2219 0 : if (!in.readDouble(&d) || !checkDouble(d))
2220 0 : return false;
2221 0 : vp.setDouble(d);
2222 0 : if (!PrimitiveToObject(context(), vp))
2223 : return false;
2224 0 : break;
2225 : }
2226 :
2227 : case SCTAG_DATE_OBJECT: {
2228 : double d;
2229 1 : if (!in.readDouble(&d) || !checkDouble(d))
2230 0 : return false;
2231 0 : JS::ClippedTime t = JS::TimeClip(d);
2232 1 : if (!NumbersAreIdentical(d, t.toDouble())) {
2233 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
2234 : JSMSG_SC_BAD_SERIALIZED_DATA,
2235 0 : "date");
2236 0 : return false;
2237 : }
2238 0 : JSObject* obj = NewDateObjectMsec(context(), t);
2239 48 : if (!obj)
2240 : return false;
2241 0 : vp.setObject(*obj);
2242 48 : break;
2243 : }
2244 :
2245 : case SCTAG_REGEXP_OBJECT: {
2246 0 : RegExpFlag flags = RegExpFlag(data);
2247 : uint32_t tag2, stringData;
2248 0 : if (!in.readPair(&tag2, &stringData))
2249 0 : return false;
2250 0 : if (tag2 != SCTAG_STRING) {
2251 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
2252 : JSMSG_SC_BAD_SERIALIZED_DATA,
2253 0 : "regexp");
2254 0 : return false;
2255 : }
2256 0 : JSString* str = readString(stringData);
2257 0 : if (!str)
2258 : return false;
2259 :
2260 0 : RootedAtom atom(context(), AtomizeString(context(), str));
2261 0 : if (!atom)
2262 0 : return false;
2263 :
2264 0 : RegExpObject* reobj = RegExpObject::create(context(), atom, flags,
2265 0 : context()->tempLifoAlloc(), GenericObject);
2266 0 : if (!reobj)
2267 : return false;
2268 0 : vp.setObject(*reobj);
2269 0 : break;
2270 : }
2271 :
2272 : case SCTAG_ARRAY_OBJECT:
2273 : case SCTAG_OBJECT_OBJECT: {
2274 : JSObject* obj = (tag == SCTAG_ARRAY_OBJECT)
2275 0 : ? (JSObject*) NewDenseEmptyArray(context())
2276 0 : : (JSObject*) NewBuiltinClassInstance<PlainObject>(context());
2277 14913 : if (!obj || !objs.append(ObjectValue(*obj)))
2278 : return false;
2279 : vp.setObject(*obj);
2280 : break;
2281 : }
2282 :
2283 : case SCTAG_BACK_REFERENCE_OBJECT: {
2284 1 : if (data >= allObjs.length() || !allObjs[data].isObject()) {
2285 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
2286 : JSMSG_SC_BAD_SERIALIZED_DATA,
2287 0 : "invalid back reference in input");
2288 0 : return false;
2289 : }
2290 0 : vp.set(allObjs[data]);
2291 9 : return true;
2292 : }
2293 :
2294 : case SCTAG_TRANSFER_MAP_HEADER:
2295 : case SCTAG_TRANSFER_MAP_PENDING_ENTRY:
2296 : // We should be past all the transfer map tags.
2297 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
2298 0 : "invalid input");
2299 0 : return false;
2300 :
2301 : case SCTAG_ARRAY_BUFFER_OBJECT:
2302 0 : if (!readArrayBuffer(data, vp))
2303 : return false;
2304 : break;
2305 :
2306 : case SCTAG_SHARED_ARRAY_BUFFER_OBJECT:
2307 0 : if (!readSharedArrayBuffer(vp))
2308 : return false;
2309 : break;
2310 :
2311 : case SCTAG_SHARED_WASM_MEMORY_OBJECT:
2312 0 : if (!readSharedWasmMemory(data, vp))
2313 : return false;
2314 : break;
2315 :
2316 : case SCTAG_TYPED_ARRAY_OBJECT: {
2317 : // readTypedArray adds the array to allObjs.
2318 : uint64_t arrayType;
2319 1 : if (!in.read(&arrayType))
2320 : return false;
2321 1 : return readTypedArray(arrayType, data, vp);
2322 : }
2323 :
2324 : case SCTAG_DATA_VIEW_OBJECT: {
2325 : // readDataView adds the array to allObjs.
2326 0 : return readDataView(data, vp);
2327 : }
2328 :
2329 : case SCTAG_MAP_OBJECT: {
2330 0 : JSObject* obj = MapObject::create(context());
2331 27 : if (!obj || !objs.append(ObjectValue(*obj)))
2332 : return false;
2333 : vp.setObject(*obj);
2334 : break;
2335 : }
2336 :
2337 : case SCTAG_SET_OBJECT: {
2338 0 : JSObject* obj = SetObject::create(context());
2339 12 : if (!obj || !objs.append(ObjectValue(*obj)))
2340 : return false;
2341 : vp.setObject(*obj);
2342 : break;
2343 : }
2344 :
2345 : case SCTAG_SAVED_FRAME_OBJECT: {
2346 0 : auto obj = readSavedFrame(data);
2347 0 : if (!obj || !objs.append(ObjectValue(*obj)))
2348 : return false;
2349 : vp.setObject(*obj);
2350 : break;
2351 : }
2352 :
2353 : default: {
2354 0 : if (tag <= SCTAG_FLOAT_MAX) {
2355 0 : double d = ReinterpretPairAsDouble(tag, data);
2356 49 : if (!checkDouble(d))
2357 : return false;
2358 : vp.setNumber(d);
2359 : break;
2360 : }
2361 :
2362 11 : if (SCTAG_TYPED_ARRAY_V1_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
2363 : // A v1-format typed array
2364 : // readTypedArray adds the array to allObjs
2365 0 : return readTypedArray(TagToV1ArrayType(tag), data, vp, true);
2366 : }
2367 :
2368 1 : if (!callbacks || !callbacks->read) {
2369 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
2370 : JSMSG_SC_BAD_SERIALIZED_DATA,
2371 0 : "unsupported type");
2372 0 : return false;
2373 : }
2374 0 : JSObject* obj = callbacks->read(context(), this, tag, data, closure);
2375 11 : if (!obj)
2376 : return false;
2377 : vp.setObject(*obj);
2378 : }
2379 : }
2380 :
2381 30760 : if (vp.isObject() && !allObjs.append(vp))
2382 : return false;
2383 :
2384 25717 : return true;
2385 : }
2386 :
2387 : bool
2388 223 : JSStructuredCloneReader::readHeader()
2389 : {
2390 : uint32_t tag, data;
2391 1 : if (!in.getPair(&tag, &data))
2392 0 : return in.reportTruncated();
2393 :
2394 : JS::StructuredCloneScope storedScope;
2395 0 : if (tag == SCTAG_HEADER) {
2396 446 : MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
2397 : storedScope = JS::StructuredCloneScope(data);
2398 : } else {
2399 : // Old structured clone buffer. We must have read it from disk.
2400 : storedScope = JS::StructuredCloneScope::DifferentProcessForIndexedDB;
2401 : }
2402 :
2403 223 : if (storedScope < JS::StructuredCloneScope::SameProcessSameThread ||
2404 : storedScope > JS::StructuredCloneScope::DifferentProcessForIndexedDB)
2405 : {
2406 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
2407 0 : "invalid structured clone scope");
2408 0 : return false;
2409 : }
2410 :
2411 223 : if (allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) {
2412 : // Bug 1434308 and bug 1458320 - the scopes stored in old IndexedDB
2413 : // clones are incorrect. Treat them as if they were DifferentProcess.
2414 0 : allowedScope = JS::StructuredCloneScope::DifferentProcess;
2415 0 : return true;
2416 : }
2417 :
2418 1 : if (storedScope < allowedScope) {
2419 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
2420 : JSMSG_SC_BAD_SERIALIZED_DATA,
2421 0 : "incompatible structured clone scope");
2422 0 : return false;
2423 : }
2424 :
2425 : return true;
2426 : }
2427 :
2428 : bool
2429 223 : JSStructuredCloneReader::readTransferMap()
2430 : {
2431 0 : JSContext* cx = context();
2432 223 : auto headerPos = in.tell();
2433 :
2434 : uint32_t tag, data;
2435 1 : if (!in.getPair(&tag, &data))
2436 0 : return in.reportTruncated();
2437 :
2438 223 : if (tag != SCTAG_TRANSFER_MAP_HEADER || TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
2439 : return true;
2440 :
2441 : uint64_t numTransferables;
2442 0 : MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
2443 1 : if (!in.read(&numTransferables))
2444 : return false;
2445 :
2446 0 : for (uint64_t i = 0; i < numTransferables; i++) {
2447 1 : auto pos = in.tell();
2448 :
2449 1 : if (!in.readPair(&tag, &data))
2450 0 : return false;
2451 :
2452 0 : MOZ_ASSERT(tag != SCTAG_TRANSFER_MAP_PENDING_ENTRY);
2453 2 : RootedObject obj(cx);
2454 :
2455 : void* content;
2456 1 : if (!in.readPtr(&content))
2457 0 : return false;
2458 :
2459 : uint64_t extraData;
2460 1 : if (!in.read(&extraData))
2461 : return false;
2462 :
2463 0 : if (tag == SCTAG_TRANSFER_MAP_ARRAY_BUFFER) {
2464 1 : if (allowedScope == JS::StructuredCloneScope::DifferentProcess ||
2465 : allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB)
2466 : {
2467 : // Transferred ArrayBuffers in a DifferentProcess clone buffer
2468 : // are treated as if they weren't Transferred at all. We should
2469 : // only see SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER.
2470 0 : ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE);
2471 0 : return false;
2472 : }
2473 :
2474 0 : size_t nbytes = extraData;
2475 1 : MOZ_ASSERT(data == JS::SCTAG_TMO_ALLOC_DATA ||
2476 : data == JS::SCTAG_TMO_MAPPED_DATA);
2477 0 : if (data == JS::SCTAG_TMO_ALLOC_DATA)
2478 0 : obj = JS_NewArrayBufferWithContents(cx, nbytes, content);
2479 0 : else if (data == JS::SCTAG_TMO_MAPPED_DATA)
2480 0 : obj = JS_NewMappedArrayBufferWithContents(cx, nbytes, content);
2481 0 : } else if (tag == SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER) {
2482 0 : auto savedPos = in.tell();
2483 : auto guard = mozilla::MakeScopeExit([&] {
2484 0 : in.seekTo(savedPos);
2485 0 : });
2486 0 : in.seekTo(pos);
2487 0 : in.seekBy(static_cast<size_t>(extraData));
2488 :
2489 : uint32_t tag, data;
2490 0 : if (!in.readPair(&tag, &data))
2491 0 : return false;
2492 0 : MOZ_ASSERT(tag == SCTAG_ARRAY_BUFFER_OBJECT);
2493 0 : RootedValue val(cx);
2494 0 : if (!readArrayBuffer(data, &val))
2495 0 : return false;
2496 0 : obj = &val.toObject();
2497 : } else {
2498 0 : if (!callbacks || !callbacks->readTransfer) {
2499 0 : ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE);
2500 0 : return false;
2501 : }
2502 0 : if (!callbacks->readTransfer(cx, this, tag, content, extraData, closure, &obj))
2503 : return false;
2504 0 : MOZ_ASSERT(obj);
2505 0 : MOZ_ASSERT(!cx->isExceptionPending());
2506 : }
2507 :
2508 : // On failure, the buffer will still own the data (since its ownership
2509 : // will not get set to SCTAG_TMO_UNOWNED), so the data will be freed by
2510 : // DiscardTransferables.
2511 1 : if (!obj)
2512 : return false;
2513 :
2514 : // Mark the SCTAG_TRANSFER_MAP_* entry as no longer owned by the input
2515 : // buffer.
2516 0 : pos.write(PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED));
2517 1 : MOZ_ASSERT(!pos.done());
2518 :
2519 3 : if (!allObjs.append(ObjectValue(*obj)))
2520 : return false;
2521 : }
2522 :
2523 : // Mark the whole transfer map as consumed.
2524 : #ifdef DEBUG
2525 0 : SCInput::getPair(headerPos.peek(), &tag, &data);
2526 0 : MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_HEADER);
2527 1 : MOZ_ASSERT(TransferableMapHeader(data) != SCTAG_TM_TRANSFERRED);
2528 : #endif
2529 1 : headerPos.write(PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED));
2530 :
2531 1 : return true;
2532 : }
2533 :
2534 : JSObject*
2535 0 : JSStructuredCloneReader::readSavedFrame(uint32_t principalsTag)
2536 : {
2537 0 : RootedSavedFrame savedFrame(context(), SavedFrame::create(context()));
2538 0 : if (!savedFrame)
2539 : return nullptr;
2540 :
2541 : JSPrincipals* principals;
2542 0 : if (principalsTag == SCTAG_JSPRINCIPALS) {
2543 0 : if (!context()->runtime()->readPrincipals) {
2544 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
2545 0 : JSMSG_SC_UNSUPPORTED_TYPE);
2546 0 : return nullptr;
2547 : }
2548 :
2549 0 : if (!context()->runtime()->readPrincipals(context(), this, &principals))
2550 : return nullptr;
2551 0 : } else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM) {
2552 0 : principals = &ReconstructedSavedFramePrincipals::IsSystem;
2553 0 : principals->refcount++;
2554 0 : } else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM) {
2555 0 : principals = &ReconstructedSavedFramePrincipals::IsNotSystem;
2556 0 : principals->refcount++;
2557 0 : } else if (principalsTag == SCTAG_NULL_JSPRINCIPALS) {
2558 0 : principals = nullptr;
2559 : } else {
2560 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
2561 0 : "bad SavedFrame principals");
2562 0 : return nullptr;
2563 : }
2564 0 : savedFrame->initPrincipalsAlreadyHeld(principals);
2565 :
2566 0 : RootedValue source(context());
2567 0 : if (!startRead(&source) || !source.isString())
2568 : return nullptr;
2569 0 : auto atomSource = AtomizeString(context(), source.toString());
2570 0 : if (!atomSource)
2571 : return nullptr;
2572 0 : savedFrame->initSource(atomSource);
2573 :
2574 0 : RootedValue lineVal(context());
2575 : uint32_t line;
2576 0 : if (!startRead(&lineVal) || !lineVal.isNumber() || !ToUint32(context(), lineVal, &line))
2577 : return nullptr;
2578 0 : savedFrame->initLine(line);
2579 :
2580 0 : RootedValue columnVal(context());
2581 : uint32_t column;
2582 0 : if (!startRead(&columnVal) || !columnVal.isNumber() || !ToUint32(context(), columnVal, &column))
2583 : return nullptr;
2584 0 : savedFrame->initColumn(column);
2585 :
2586 0 : RootedValue name(context());
2587 0 : if (!startRead(&name) || !(name.isString() || name.isNull()))
2588 : return nullptr;
2589 0 : JSAtom* atomName = nullptr;
2590 0 : if (name.isString()) {
2591 0 : atomName = AtomizeString(context(), name.toString());
2592 0 : if (!atomName)
2593 : return nullptr;
2594 : }
2595 :
2596 0 : savedFrame->initFunctionDisplayName(atomName);
2597 :
2598 0 : RootedValue cause(context());
2599 0 : if (!startRead(&cause) || !(cause.isString() || cause.isNull()))
2600 : return nullptr;
2601 0 : JSAtom* atomCause = nullptr;
2602 0 : if (cause.isString()) {
2603 0 : atomCause = AtomizeString(context(), cause.toString());
2604 0 : if (!atomCause)
2605 : return nullptr;
2606 : }
2607 0 : savedFrame->initAsyncCause(atomCause);
2608 :
2609 0 : return savedFrame;
2610 : }
2611 :
2612 : // Perform the whole recursive reading procedure.
2613 : bool
2614 223 : JSStructuredCloneReader::read(MutableHandleValue vp)
2615 : {
2616 223 : if (!readHeader())
2617 : return false;
2618 :
2619 223 : if (!readTransferMap())
2620 : return false;
2621 :
2622 : // Start out by reading in the main object and pushing it onto the 'objs'
2623 : // stack. The data related to this object and its descendants extends from
2624 : // here to the SCTAG_END_OF_KEYS at the end of the stream.
2625 223 : if (!startRead(vp))
2626 : return false;
2627 :
2628 : // Stop when the stack shows that all objects have been read.
2629 35930 : while (objs.length() != 0) {
2630 : // What happens depends on the top obj on the objs stack.
2631 65971 : RootedObject obj(context(), &objs.back().toObject());
2632 :
2633 : uint32_t tag, data;
2634 0 : if (!in.getPair(&tag, &data))
2635 0 : return false;
2636 :
2637 17742 : if (tag == SCTAG_END_OF_KEYS) {
2638 : // Pop the current obj off the stack, since we are done with it and
2639 : // its children.
2640 0 : MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
2641 0 : objs.popBack();
2642 4997 : continue;
2643 : }
2644 :
2645 : // The input stream contains a sequence of "child" values, whose
2646 : // interpretation depends on the type of obj. These values can be
2647 : // anything, and startRead() will push onto 'objs' for any non-leaf
2648 : // value (i.e., anything that may contain children).
2649 : //
2650 : // startRead() will allocate the (empty) object, but note that when
2651 : // startRead() returns, 'key' is not yet initialized with any of its
2652 : // properties. Those will be filled in by returning to the head of this
2653 : // loop, processing the first child obj, and continuing until all
2654 : // children have been fully created.
2655 : //
2656 : // Note that this means the ordering in the stream is a little funky
2657 : // for things like Map. See the comment above startWrite() for an
2658 : // example.
2659 0 : RootedValue key(context());
2660 0 : if (!startRead(&key))
2661 0 : return false;
2662 :
2663 0 : if (key.isNull() &&
2664 0 : !(obj->is<MapObject>() || obj->is<SetObject>() || obj->is<SavedFrame>()))
2665 : {
2666 : // Backwards compatibility: Null formerly indicated the end of
2667 : // object properties.
2668 0 : objs.popBack();
2669 13 : continue;
2670 : }
2671 :
2672 : // Set object: the values between obj header (from startRead()) and
2673 : // SCTAG_END_OF_KEYS are all interpreted as values to add to the set.
2674 0 : if (obj->is<SetObject>()) {
2675 39 : if (!SetObject::add(context(), obj, key))
2676 : return false;
2677 : continue;
2678 : }
2679 :
2680 : // SavedFrame object: there is one following value, the parent
2681 : // SavedFrame, which is either null or another SavedFrame object.
2682 25490 : if (obj->is<SavedFrame>()) {
2683 : SavedFrame* parentFrame;
2684 0 : if (key.isNull())
2685 : parentFrame = nullptr;
2686 0 : else if (key.isObject() && key.toObject().is<SavedFrame>())
2687 0 : parentFrame = &key.toObject().as<SavedFrame>();
2688 : else
2689 : return false;
2690 :
2691 0 : obj->as<SavedFrame>().initParent(parentFrame);
2692 0 : continue;
2693 : }
2694 :
2695 : // Everything else uses a series of key,value,key,value,... Value
2696 : // objects.
2697 0 : RootedValue val(context());
2698 0 : if (!startRead(&val))
2699 0 : return false;
2700 :
2701 25490 : if (obj->is<MapObject>()) {
2702 : // For a Map, store those <key,value> pairs in the contained map
2703 : // data structure.
2704 584 : if (!MapObject::set(context(), obj, key, val))
2705 : return false;
2706 : } else {
2707 : // For any other Object, interpret them as plain properties.
2708 50396 : RootedId id(context());
2709 :
2710 1 : if (!key.isString() && !key.isInt32()) {
2711 0 : JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
2712 : JSMSG_SC_BAD_SERIALIZED_DATA,
2713 0 : "property key expected");
2714 0 : return false;
2715 : }
2716 :
2717 37797 : if (!ValueToId<CanGC>(context(), key, &id))
2718 : return false;
2719 :
2720 50396 : if (!DefineDataProperty(context(), obj, id, val))
2721 : return false;
2722 : }
2723 : }
2724 :
2725 446 : allObjs.clear();
2726 :
2727 223 : return true;
2728 : }
2729 :
2730 : using namespace js;
2731 :
2732 : JS_PUBLIC_API(bool)
2733 223 : JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& buf,
2734 : uint32_t version, JS::StructuredCloneScope scope,
2735 : MutableHandleValue vp,
2736 : const JSStructuredCloneCallbacks* optionalCallbacks,
2737 : void* closure)
2738 : {
2739 0 : AssertHeapIsIdle();
2740 446 : CHECK_REQUEST(cx);
2741 :
2742 1 : if (version > JS_STRUCTURED_CLONE_VERSION) {
2743 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_BAD_CLONE_VERSION);
2744 0 : return false;
2745 : }
2746 0 : const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
2747 223 : return ReadStructuredClone(cx, buf, scope, vp, callbacks, closure);
2748 : }
2749 :
2750 : JS_PUBLIC_API(bool)
2751 234 : JS_WriteStructuredClone(JSContext* cx, HandleValue value, JSStructuredCloneData* bufp,
2752 : JS::StructuredCloneScope scope,
2753 : JS::CloneDataPolicy cloneDataPolicy,
2754 : const JSStructuredCloneCallbacks* optionalCallbacks,
2755 : void* closure, HandleValue transferable)
2756 : {
2757 0 : AssertHeapIsIdle();
2758 0 : CHECK_REQUEST(cx);
2759 234 : assertSameCompartment(cx, value);
2760 :
2761 0 : const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
2762 0 : return WriteStructuredClone(cx, value, bufp, scope, cloneDataPolicy, callbacks, closure,
2763 468 : transferable);
2764 : }
2765 :
2766 : JS_PUBLIC_API(bool)
2767 0 : JS_StructuredCloneHasTransferables(JSStructuredCloneData& data,
2768 : bool* hasTransferable)
2769 : {
2770 0 : *hasTransferable = StructuredCloneHasTransferObjects(data);
2771 0 : return true;
2772 : }
2773 :
2774 : JS_PUBLIC_API(bool)
2775 20 : JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp,
2776 : const JSStructuredCloneCallbacks* optionalCallbacks,
2777 : void* closure)
2778 : {
2779 0 : AssertHeapIsIdle();
2780 40 : CHECK_REQUEST(cx);
2781 :
2782 : // Strings are associated with zones, not compartments,
2783 : // so we copy the string by wrapping it.
2784 0 : if (value.isString()) {
2785 0 : RootedString strValue(cx, value.toString());
2786 0 : if (!cx->compartment()->wrap(cx, &strValue)) {
2787 : return false;
2788 : }
2789 0 : vp.setString(strValue);
2790 0 : return true;
2791 : }
2792 :
2793 20 : const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
2794 :
2795 20 : JSAutoStructuredCloneBuffer buf(JS::StructuredCloneScope::SameProcessSameThread, callbacks, closure);
2796 : {
2797 : // If we use Maybe<AutoRealm> here, G++ can't tell that the
2798 : // destructor is only called when Maybe::construct was called, and
2799 : // we get warnings about using uninitialized variables.
2800 0 : if (value.isObject()) {
2801 0 : AutoRealm ar(cx, &value.toObject());
2802 0 : if (!buf.write(cx, value, callbacks, closure))
2803 0 : return false;
2804 : } else {
2805 18 : if (!buf.write(cx, value, callbacks, closure))
2806 : return false;
2807 : }
2808 : }
2809 :
2810 20 : return buf.read(cx, vp, callbacks, closure);
2811 : }
2812 :
2813 0 : JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
2814 0 : : scope_(other.scope()), data_(other.scope())
2815 : {
2816 0 : data_.ownTransferables_ = other.data_.ownTransferables_;
2817 0 : other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
2818 0 : }
2819 :
2820 : JSAutoStructuredCloneBuffer&
2821 0 : JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
2822 : {
2823 0 : MOZ_ASSERT(&other != this);
2824 0 : MOZ_ASSERT(scope_ == other.scope_);
2825 0 : clear();
2826 0 : data_.ownTransferables_ = other.data_.ownTransferables_;
2827 0 : other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
2828 0 : return *this;
2829 : }
2830 :
2831 : void
2832 412 : JSAutoStructuredCloneBuffer::clear()
2833 : {
2834 0 : data_.discardTransferables();
2835 0 : data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
2836 0 : data_.refsHeld_.releaseAll();
2837 0 : data_.Clear();
2838 0 : version_ = 0;
2839 412 : }
2840 :
2841 : void
2842 4 : JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t version,
2843 : const JSStructuredCloneCallbacks* callbacks,
2844 : void* closure)
2845 : {
2846 0 : clear();
2847 0 : data_ = std::move(data);
2848 0 : version_ = version;
2849 0 : data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny);
2850 4 : }
2851 :
2852 : void
2853 51 : JSAutoStructuredCloneBuffer::steal(JSStructuredCloneData* data, uint32_t* versionp,
2854 : const JSStructuredCloneCallbacks** callbacks,
2855 : void** closure)
2856 : {
2857 1 : if (versionp)
2858 0 : *versionp = version_;
2859 1 : if (callbacks)
2860 0 : *callbacks = data_.callbacks_;
2861 1 : if (closure)
2862 0 : *closure = data_.closure_;
2863 51 : *data = std::move(data_);
2864 :
2865 0 : version_ = 0;
2866 0 : data_.setCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
2867 51 : }
2868 :
2869 : bool
2870 179 : JSAutoStructuredCloneBuffer::read(JSContext* cx, MutableHandleValue vp,
2871 : const JSStructuredCloneCallbacks* optionalCallbacks,
2872 : void* closure)
2873 : {
2874 0 : MOZ_ASSERT(cx);
2875 0 : return !!JS_ReadStructuredClone(cx, data_, version_, scope_, vp,
2876 179 : optionalCallbacks, closure);
2877 : }
2878 :
2879 : bool
2880 0 : JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
2881 : const JSStructuredCloneCallbacks* optionalCallbacks,
2882 : void* closure)
2883 : {
2884 0 : HandleValue transferable = UndefinedHandleValue;
2885 40 : return write(cx, value, transferable, JS::CloneDataPolicy().denySharedArrayBuffer(), optionalCallbacks, closure);
2886 : }
2887 :
2888 : bool
2889 234 : JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
2890 : HandleValue transferable, JS::CloneDataPolicy cloneDataPolicy,
2891 : const JSStructuredCloneCallbacks* optionalCallbacks,
2892 : void* closure)
2893 : {
2894 0 : clear();
2895 0 : bool ok = JS_WriteStructuredClone(cx, value, &data_,
2896 234 : scope_, cloneDataPolicy,
2897 : optionalCallbacks, closure,
2898 234 : transferable);
2899 :
2900 0 : if (ok) {
2901 234 : data_.ownTransferables_ = OwnTransferablePolicy::OwnsTransferablesIfAny;
2902 : } else {
2903 0 : version_ = JS_STRUCTURED_CLONE_VERSION;
2904 0 : data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
2905 : }
2906 234 : return ok;
2907 : }
2908 :
2909 : JS_PUBLIC_API(bool)
2910 20 : JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1, uint32_t* p2)
2911 : {
2912 40 : return r->input().readPair((uint32_t*) p1, (uint32_t*) p2);
2913 : }
2914 :
2915 : JS_PUBLIC_API(bool)
2916 22 : JS_ReadBytes(JSStructuredCloneReader* r, void* p, size_t len)
2917 : {
2918 44 : return r->input().readBytes(p, len);
2919 : }
2920 :
2921 : JS_PUBLIC_API(bool)
2922 0 : JS_ReadTypedArray(JSStructuredCloneReader* r, MutableHandleValue vp)
2923 : {
2924 : uint32_t tag, nelems;
2925 0 : if (!r->input().readPair(&tag, &nelems))
2926 : return false;
2927 0 : if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
2928 0 : return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true);
2929 0 : } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) {
2930 : uint64_t arrayType;
2931 0 : if (!r->input().read(&arrayType))
2932 : return false;
2933 0 : return r->readTypedArray(arrayType, nelems, vp);
2934 : } else {
2935 0 : JS_ReportErrorNumberASCII(r->context(), GetErrorMessage, nullptr,
2936 : JSMSG_SC_BAD_SERIALIZED_DATA,
2937 0 : "expected type array");
2938 0 : return false;
2939 : }
2940 : }
2941 :
2942 : JS_PUBLIC_API(bool)
2943 64 : JS_WriteUint32Pair(JSStructuredCloneWriter* w, uint32_t tag, uint32_t data)
2944 : {
2945 128 : return w->output().writePair(tag, data);
2946 : }
2947 :
2948 : JS_PUBLIC_API(bool)
2949 49 : JS_WriteBytes(JSStructuredCloneWriter* w, const void* p, size_t len)
2950 : {
2951 98 : return w->output().writeBytes(p, len);
2952 : }
2953 :
2954 : JS_PUBLIC_API(bool)
2955 0 : JS_WriteString(JSStructuredCloneWriter* w, HandleString str)
2956 : {
2957 0 : return w->writeString(SCTAG_STRING, str);
2958 : }
2959 :
2960 : JS_PUBLIC_API(bool)
2961 0 : JS_WriteTypedArray(JSStructuredCloneWriter* w, HandleValue v)
2962 : {
2963 0 : MOZ_ASSERT(v.isObject());
2964 0 : assertSameCompartment(w->context(), v);
2965 0 : RootedObject obj(w->context(), &v.toObject());
2966 0 : return w->writeTypedArray(obj);
2967 : }
2968 :
2969 : JS_PUBLIC_API(bool)
2970 0 : JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj)
2971 : {
2972 0 : w->memory.remove(w->memory.lookup(obj));
2973 :
2974 0 : return true;
2975 : }
2976 :
2977 : JS_PUBLIC_API(JS::StructuredCloneScope)
2978 0 : JS_GetStructuredCloneScope(JSStructuredCloneWriter* w)
2979 : {
2980 : return w->output().scope();
2981 : }
|