1 | 'use strict';
|
2 | var IS_PURE = require('../internals/is-pure');
|
3 | var $ = require('../internals/export');
|
4 | var global = require('../internals/global');
|
5 | var getBuiltIn = require('../internals/get-built-in');
|
6 | var uncurryThis = require('../internals/function-uncurry-this');
|
7 | var fails = require('../internals/fails');
|
8 | var uid = require('../internals/uid');
|
9 | var isCallable = require('../internals/is-callable');
|
10 | var isConstructor = require('../internals/is-constructor');
|
11 | var isNullOrUndefined = require('../internals/is-null-or-undefined');
|
12 | var isObject = require('../internals/is-object');
|
13 | var isSymbol = require('../internals/is-symbol');
|
14 | var iterate = require('../internals/iterate');
|
15 | var anObject = require('../internals/an-object');
|
16 | var classof = require('../internals/classof');
|
17 | var hasOwn = require('../internals/has-own-property');
|
18 | var createProperty = require('../internals/create-property');
|
19 | var createNonEnumerableProperty = require('../internals/create-non-enumerable-property');
|
20 | var lengthOfArrayLike = require('../internals/length-of-array-like');
|
21 | var validateArgumentsLength = require('../internals/validate-arguments-length');
|
22 | var getRegExpFlags = require('../internals/regexp-get-flags');
|
23 | var MapHelpers = require('../internals/map-helpers');
|
24 | var SetHelpers = require('../internals/set-helpers');
|
25 | var setIterate = require('../internals/set-iterate');
|
26 | var detachTransferable = require('../internals/detach-transferable');
|
27 | var ERROR_STACK_INSTALLABLE = require('../internals/error-stack-installable');
|
28 | var PROPER_STRUCTURED_CLONE_TRANSFER = require('../internals/structured-clone-proper-transfer');
|
29 |
|
30 | var Object = global.Object;
|
31 | var Array = global.Array;
|
32 | var Date = global.Date;
|
33 | var Error = global.Error;
|
34 | var TypeError = global.TypeError;
|
35 | var PerformanceMark = global.PerformanceMark;
|
36 | var DOMException = getBuiltIn('DOMException');
|
37 | var Map = MapHelpers.Map;
|
38 | var mapHas = MapHelpers.has;
|
39 | var mapGet = MapHelpers.get;
|
40 | var mapSet = MapHelpers.set;
|
41 | var Set = SetHelpers.Set;
|
42 | var setAdd = SetHelpers.add;
|
43 | var setHas = SetHelpers.has;
|
44 | var objectKeys = getBuiltIn('Object', 'keys');
|
45 | var push = uncurryThis([].push);
|
46 | var thisBooleanValue = uncurryThis(true.valueOf);
|
47 | var thisNumberValue = uncurryThis(1.0.valueOf);
|
48 | var thisStringValue = uncurryThis(''.valueOf);
|
49 | var thisTimeValue = uncurryThis(Date.prototype.getTime);
|
50 | var PERFORMANCE_MARK = uid('structuredClone');
|
51 | var DATA_CLONE_ERROR = 'DataCloneError';
|
52 | var TRANSFERRING = 'Transferring';
|
53 |
|
54 | var checkBasicSemantic = function (structuredCloneImplementation) {
|
55 | return !fails(function () {
|
56 | var set1 = new global.Set([7]);
|
57 | var set2 = structuredCloneImplementation(set1);
|
58 | var number = structuredCloneImplementation(Object(7));
|
59 | return set2 === set1 || !set2.has(7) || !isObject(number) || +number !== 7;
|
60 | }) && structuredCloneImplementation;
|
61 | };
|
62 |
|
63 | var checkErrorsCloning = function (structuredCloneImplementation, $Error) {
|
64 | return !fails(function () {
|
65 | var error = new $Error();
|
66 | var test = structuredCloneImplementation({ a: error, b: error });
|
67 | return !(test && test.a === test.b && test.a instanceof $Error && test.a.stack === error.stack);
|
68 | });
|
69 | };
|
70 |
|
71 |
|
72 | var checkNewErrorsCloningSemantic = function (structuredCloneImplementation) {
|
73 | return !fails(function () {
|
74 | var test = structuredCloneImplementation(new global.AggregateError([1], PERFORMANCE_MARK, { cause: 3 }));
|
75 | return test.name !== 'AggregateError' || test.errors[0] !== 1 || test.message !== PERFORMANCE_MARK || test.cause !== 3;
|
76 | });
|
77 | };
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 | var nativeStructuredClone = global.structuredClone;
|
92 |
|
93 | var FORCED_REPLACEMENT = IS_PURE
|
94 | || !checkErrorsCloning(nativeStructuredClone, Error)
|
95 | || !checkErrorsCloning(nativeStructuredClone, DOMException)
|
96 | || !checkNewErrorsCloningSemantic(nativeStructuredClone);
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | var structuredCloneFromMark = !nativeStructuredClone && checkBasicSemantic(function (value) {
|
110 | return new PerformanceMark(PERFORMANCE_MARK, { detail: value }).detail;
|
111 | });
|
112 |
|
113 | var nativeRestrictedStructuredClone = checkBasicSemantic(nativeStructuredClone) || structuredCloneFromMark;
|
114 |
|
115 | var throwUncloneable = function (type) {
|
116 | throw new DOMException('Uncloneable type: ' + type, DATA_CLONE_ERROR);
|
117 | };
|
118 |
|
119 | var throwUnpolyfillable = function (type, action) {
|
120 | throw new DOMException((action || 'Cloning') + ' of ' + type + ' cannot be properly polyfilled in this engine', DATA_CLONE_ERROR);
|
121 | };
|
122 |
|
123 | var tryNativeRestrictedStructuredClone = function (value, type) {
|
124 | if (!nativeRestrictedStructuredClone) throwUnpolyfillable(type);
|
125 | return nativeRestrictedStructuredClone(value);
|
126 | };
|
127 |
|
128 | var createDataTransfer = function () {
|
129 | var dataTransfer;
|
130 | try {
|
131 | dataTransfer = new global.DataTransfer();
|
132 | } catch (error) {
|
133 | try {
|
134 | dataTransfer = new global.ClipboardEvent('').clipboardData;
|
135 | } catch (error2) { }
|
136 | }
|
137 | return dataTransfer && dataTransfer.items && dataTransfer.files ? dataTransfer : null;
|
138 | };
|
139 |
|
140 | var cloneBuffer = function (value, map, $type) {
|
141 | if (mapHas(map, value)) return mapGet(map, value);
|
142 |
|
143 | var type = $type || classof(value);
|
144 | var clone, length, options, source, target, i;
|
145 |
|
146 | if (type === 'SharedArrayBuffer') {
|
147 | if (nativeRestrictedStructuredClone) clone = nativeRestrictedStructuredClone(value);
|
148 |
|
149 | else clone = value;
|
150 | } else {
|
151 | var DataView = global.DataView;
|
152 |
|
153 |
|
154 |
|
155 | if (!DataView && !isCallable(value.slice)) throwUnpolyfillable('ArrayBuffer');
|
156 |
|
157 | try {
|
158 | if (isCallable(value.slice) && !value.resizable) {
|
159 | clone = value.slice(0);
|
160 | } else {
|
161 | length = value.byteLength;
|
162 | options = 'maxByteLength' in value ? { maxByteLength: value.maxByteLength } : undefined;
|
163 |
|
164 | clone = new ArrayBuffer(length, options);
|
165 | source = new DataView(value);
|
166 | target = new DataView(clone);
|
167 | for (i = 0; i < length; i++) {
|
168 | target.setUint8(i, source.getUint8(i));
|
169 | }
|
170 | }
|
171 | } catch (error) {
|
172 | throw new DOMException('ArrayBuffer is detached', DATA_CLONE_ERROR);
|
173 | }
|
174 | }
|
175 |
|
176 | mapSet(map, value, clone);
|
177 |
|
178 | return clone;
|
179 | };
|
180 |
|
181 | var cloneView = function (value, type, offset, length, map) {
|
182 | var C = global[type];
|
183 |
|
184 |
|
185 | if (!isObject(C)) throwUnpolyfillable(type);
|
186 | return new C(cloneBuffer(value.buffer, map), offset, length);
|
187 | };
|
188 |
|
189 | var structuredCloneInternal = function (value, map) {
|
190 | if (isSymbol(value)) throwUncloneable('Symbol');
|
191 | if (!isObject(value)) return value;
|
192 |
|
193 | if (map) {
|
194 | if (mapHas(map, value)) return mapGet(map, value);
|
195 | } else map = new Map();
|
196 |
|
197 | var type = classof(value);
|
198 | var C, name, cloned, dataTransfer, i, length, keys, key;
|
199 |
|
200 | switch (type) {
|
201 | case 'Array':
|
202 | cloned = Array(lengthOfArrayLike(value));
|
203 | break;
|
204 | case 'Object':
|
205 | cloned = {};
|
206 | break;
|
207 | case 'Map':
|
208 | cloned = new Map();
|
209 | break;
|
210 | case 'Set':
|
211 | cloned = new Set();
|
212 | break;
|
213 | case 'RegExp':
|
214 |
|
215 |
|
216 | cloned = new RegExp(value.source, getRegExpFlags(value));
|
217 | break;
|
218 | case 'Error':
|
219 | name = value.name;
|
220 | switch (name) {
|
221 | case 'AggregateError':
|
222 | cloned = new (getBuiltIn(name))([]);
|
223 | break;
|
224 | case 'EvalError':
|
225 | case 'RangeError':
|
226 | case 'ReferenceError':
|
227 | case 'SuppressedError':
|
228 | case 'SyntaxError':
|
229 | case 'TypeError':
|
230 | case 'URIError':
|
231 | cloned = new (getBuiltIn(name))();
|
232 | break;
|
233 | case 'CompileError':
|
234 | case 'LinkError':
|
235 | case 'RuntimeError':
|
236 | cloned = new (getBuiltIn('WebAssembly', name))();
|
237 | break;
|
238 | default:
|
239 | cloned = new Error();
|
240 | }
|
241 | break;
|
242 | case 'DOMException':
|
243 | cloned = new DOMException(value.message, value.name);
|
244 | break;
|
245 | case 'ArrayBuffer':
|
246 | case 'SharedArrayBuffer':
|
247 | cloned = cloneBuffer(value, map, type);
|
248 | break;
|
249 | case 'DataView':
|
250 | case 'Int8Array':
|
251 | case 'Uint8Array':
|
252 | case 'Uint8ClampedArray':
|
253 | case 'Int16Array':
|
254 | case 'Uint16Array':
|
255 | case 'Int32Array':
|
256 | case 'Uint32Array':
|
257 | case 'Float16Array':
|
258 | case 'Float32Array':
|
259 | case 'Float64Array':
|
260 | case 'BigInt64Array':
|
261 | case 'BigUint64Array':
|
262 | length = type === 'DataView' ? value.byteLength : value.length;
|
263 | cloned = cloneView(value, type, value.byteOffset, length, map);
|
264 | break;
|
265 | case 'DOMQuad':
|
266 | try {
|
267 | cloned = new DOMQuad(
|
268 | structuredCloneInternal(value.p1, map),
|
269 | structuredCloneInternal(value.p2, map),
|
270 | structuredCloneInternal(value.p3, map),
|
271 | structuredCloneInternal(value.p4, map)
|
272 | );
|
273 | } catch (error) {
|
274 | cloned = tryNativeRestrictedStructuredClone(value, type);
|
275 | }
|
276 | break;
|
277 | case 'File':
|
278 | if (nativeRestrictedStructuredClone) try {
|
279 | cloned = nativeRestrictedStructuredClone(value);
|
280 |
|
281 | if (classof(cloned) !== type) cloned = undefined;
|
282 | } catch (error) { }
|
283 | if (!cloned) try {
|
284 | cloned = new File([value], value.name, value);
|
285 | } catch (error) { }
|
286 | if (!cloned) throwUnpolyfillable(type);
|
287 | break;
|
288 | case 'FileList':
|
289 | dataTransfer = createDataTransfer();
|
290 | if (dataTransfer) {
|
291 | for (i = 0, length = lengthOfArrayLike(value); i < length; i++) {
|
292 | dataTransfer.items.add(structuredCloneInternal(value[i], map));
|
293 | }
|
294 | cloned = dataTransfer.files;
|
295 | } else cloned = tryNativeRestrictedStructuredClone(value, type);
|
296 | break;
|
297 | case 'ImageData':
|
298 |
|
299 | try {
|
300 | cloned = new ImageData(
|
301 | structuredCloneInternal(value.data, map),
|
302 | value.width,
|
303 | value.height,
|
304 | { colorSpace: value.colorSpace }
|
305 | );
|
306 | } catch (error) {
|
307 | cloned = tryNativeRestrictedStructuredClone(value, type);
|
308 | } break;
|
309 | default:
|
310 | if (nativeRestrictedStructuredClone) {
|
311 | cloned = nativeRestrictedStructuredClone(value);
|
312 | } else switch (type) {
|
313 | case 'BigInt':
|
314 |
|
315 | cloned = Object(value.valueOf());
|
316 | break;
|
317 | case 'Boolean':
|
318 | cloned = Object(thisBooleanValue(value));
|
319 | break;
|
320 | case 'Number':
|
321 | cloned = Object(thisNumberValue(value));
|
322 | break;
|
323 | case 'String':
|
324 | cloned = Object(thisStringValue(value));
|
325 | break;
|
326 | case 'Date':
|
327 | cloned = new Date(thisTimeValue(value));
|
328 | break;
|
329 | case 'Blob':
|
330 | try {
|
331 | cloned = value.slice(0, value.size, value.type);
|
332 | } catch (error) {
|
333 | throwUnpolyfillable(type);
|
334 | } break;
|
335 | case 'DOMPoint':
|
336 | case 'DOMPointReadOnly':
|
337 | C = global[type];
|
338 | try {
|
339 | cloned = C.fromPoint
|
340 | ? C.fromPoint(value)
|
341 | : new C(value.x, value.y, value.z, value.w);
|
342 | } catch (error) {
|
343 | throwUnpolyfillable(type);
|
344 | } break;
|
345 | case 'DOMRect':
|
346 | case 'DOMRectReadOnly':
|
347 | C = global[type];
|
348 | try {
|
349 | cloned = C.fromRect
|
350 | ? C.fromRect(value)
|
351 | : new C(value.x, value.y, value.width, value.height);
|
352 | } catch (error) {
|
353 | throwUnpolyfillable(type);
|
354 | } break;
|
355 | case 'DOMMatrix':
|
356 | case 'DOMMatrixReadOnly':
|
357 | C = global[type];
|
358 | try {
|
359 | cloned = C.fromMatrix
|
360 | ? C.fromMatrix(value)
|
361 | : new C(value);
|
362 | } catch (error) {
|
363 | throwUnpolyfillable(type);
|
364 | } break;
|
365 | case 'AudioData':
|
366 | case 'VideoFrame':
|
367 | if (!isCallable(value.clone)) throwUnpolyfillable(type);
|
368 | try {
|
369 | cloned = value.clone();
|
370 | } catch (error) {
|
371 | throwUncloneable(type);
|
372 | } break;
|
373 | case 'CropTarget':
|
374 | case 'CryptoKey':
|
375 | case 'FileSystemDirectoryHandle':
|
376 | case 'FileSystemFileHandle':
|
377 | case 'FileSystemHandle':
|
378 | case 'GPUCompilationInfo':
|
379 | case 'GPUCompilationMessage':
|
380 | case 'ImageBitmap':
|
381 | case 'RTCCertificate':
|
382 | case 'WebAssembly.Module':
|
383 | throwUnpolyfillable(type);
|
384 |
|
385 | default:
|
386 | throwUncloneable(type);
|
387 | }
|
388 | }
|
389 |
|
390 | mapSet(map, value, cloned);
|
391 |
|
392 | switch (type) {
|
393 | case 'Array':
|
394 | case 'Object':
|
395 | keys = objectKeys(value);
|
396 | for (i = 0, length = lengthOfArrayLike(keys); i < length; i++) {
|
397 | key = keys[i];
|
398 | createProperty(cloned, key, structuredCloneInternal(value[key], map));
|
399 | } break;
|
400 | case 'Map':
|
401 | value.forEach(function (v, k) {
|
402 | mapSet(cloned, structuredCloneInternal(k, map), structuredCloneInternal(v, map));
|
403 | });
|
404 | break;
|
405 | case 'Set':
|
406 | value.forEach(function (v) {
|
407 | setAdd(cloned, structuredCloneInternal(v, map));
|
408 | });
|
409 | break;
|
410 | case 'Error':
|
411 | createNonEnumerableProperty(cloned, 'message', structuredCloneInternal(value.message, map));
|
412 | if (hasOwn(value, 'cause')) {
|
413 | createNonEnumerableProperty(cloned, 'cause', structuredCloneInternal(value.cause, map));
|
414 | }
|
415 | if (name === 'AggregateError') {
|
416 | cloned.errors = structuredCloneInternal(value.errors, map);
|
417 | } else if (name === 'SuppressedError') {
|
418 | cloned.error = structuredCloneInternal(value.error, map);
|
419 | cloned.suppressed = structuredCloneInternal(value.suppressed, map);
|
420 | }
|
421 | case 'DOMException':
|
422 | if (ERROR_STACK_INSTALLABLE) {
|
423 | createNonEnumerableProperty(cloned, 'stack', structuredCloneInternal(value.stack, map));
|
424 | }
|
425 | }
|
426 |
|
427 | return cloned;
|
428 | };
|
429 |
|
430 | var tryToTransfer = function (rawTransfer, map) {
|
431 | if (!isObject(rawTransfer)) throw new TypeError('Transfer option cannot be converted to a sequence');
|
432 |
|
433 | var transfer = [];
|
434 |
|
435 | iterate(rawTransfer, function (value) {
|
436 | push(transfer, anObject(value));
|
437 | });
|
438 |
|
439 | var i = 0;
|
440 | var length = lengthOfArrayLike(transfer);
|
441 | var buffers = new Set();
|
442 | var value, type, C, transferred, canvas, context;
|
443 |
|
444 | while (i < length) {
|
445 | value = transfer[i++];
|
446 |
|
447 | type = classof(value);
|
448 |
|
449 | if (type === 'ArrayBuffer' ? setHas(buffers, value) : mapHas(map, value)) {
|
450 | throw new DOMException('Duplicate transferable', DATA_CLONE_ERROR);
|
451 | }
|
452 |
|
453 | if (type === 'ArrayBuffer') {
|
454 | setAdd(buffers, value);
|
455 | continue;
|
456 | }
|
457 |
|
458 | if (PROPER_STRUCTURED_CLONE_TRANSFER) {
|
459 | transferred = nativeStructuredClone(value, { transfer: [value] });
|
460 | } else switch (type) {
|
461 | case 'ImageBitmap':
|
462 | C = global.OffscreenCanvas;
|
463 | if (!isConstructor(C)) throwUnpolyfillable(type, TRANSFERRING);
|
464 | try {
|
465 | canvas = new C(value.width, value.height);
|
466 | context = canvas.getContext('bitmaprenderer');
|
467 | context.transferFromImageBitmap(value);
|
468 | transferred = canvas.transferToImageBitmap();
|
469 | } catch (error) { }
|
470 | break;
|
471 | case 'AudioData':
|
472 | case 'VideoFrame':
|
473 | if (!isCallable(value.clone) || !isCallable(value.close)) throwUnpolyfillable(type, TRANSFERRING);
|
474 | try {
|
475 | transferred = value.clone();
|
476 | value.close();
|
477 | } catch (error) { }
|
478 | break;
|
479 | case 'MediaSourceHandle':
|
480 | case 'MessagePort':
|
481 | case 'OffscreenCanvas':
|
482 | case 'ReadableStream':
|
483 | case 'TransformStream':
|
484 | case 'WritableStream':
|
485 | throwUnpolyfillable(type, TRANSFERRING);
|
486 | }
|
487 |
|
488 | if (transferred === undefined) throw new DOMException('This object cannot be transferred: ' + type, DATA_CLONE_ERROR);
|
489 |
|
490 | mapSet(map, value, transferred);
|
491 | }
|
492 |
|
493 | return buffers;
|
494 | };
|
495 |
|
496 | var detachBuffers = function (buffers) {
|
497 | setIterate(buffers, function (buffer) {
|
498 | if (PROPER_STRUCTURED_CLONE_TRANSFER) {
|
499 | nativeRestrictedStructuredClone(buffer, { transfer: [buffer] });
|
500 | } else if (isCallable(buffer.transfer)) {
|
501 | buffer.transfer();
|
502 | } else if (detachTransferable) {
|
503 | detachTransferable(buffer);
|
504 | } else {
|
505 | throwUnpolyfillable('ArrayBuffer', TRANSFERRING);
|
506 | }
|
507 | });
|
508 | };
|
509 |
|
510 |
|
511 |
|
512 | $({ global: true, enumerable: true, sham: !PROPER_STRUCTURED_CLONE_TRANSFER, forced: FORCED_REPLACEMENT }, {
|
513 | structuredClone: function structuredClone(value /* , { transfer } */) {
|
514 | var options = validateArgumentsLength(arguments.length, 1) > 1 && !isNullOrUndefined(arguments[1]) ? anObject(arguments[1]) : undefined;
|
515 | var transfer = options ? options.transfer : undefined;
|
516 | var map, buffers;
|
517 |
|
518 | if (transfer !== undefined) {
|
519 | map = new Map();
|
520 | buffers = tryToTransfer(transfer, map);
|
521 | }
|
522 |
|
523 | var clone = structuredCloneInternal(value, map);
|
524 |
|
525 |
|
526 |
|
527 | if (buffers) detachBuffers(buffers);
|
528 |
|
529 | return clone;
|
530 | }
|
531 | });
|