123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- 'use strict';
- var IS_PURE = require('../internals/is-pure');
- var $ = require('../internals/export');
- var global = require('../internals/global');
- var getBuiltin = require('../internals/get-built-in');
- var uncurryThis = require('../internals/function-uncurry-this');
- var fails = require('../internals/fails');
- var uid = require('../internals/uid');
- var isCallable = require('../internals/is-callable');
- var isConstructor = require('../internals/is-constructor');
- var isNullOrUndefined = require('../internals/is-null-or-undefined');
- var isObject = require('../internals/is-object');
- var isSymbol = require('../internals/is-symbol');
- var iterate = require('../internals/iterate');
- var anObject = require('../internals/an-object');
- var classof = require('../internals/classof');
- var hasOwn = require('../internals/has-own-property');
- var createProperty = require('../internals/create-property');
- var createNonEnumerableProperty = require('../internals/create-non-enumerable-property');
- var lengthOfArrayLike = require('../internals/length-of-array-like');
- var validateArgumentsLength = require('../internals/validate-arguments-length');
- var getRegExpFlags = require('../internals/regexp-get-flags');
- var MapHelpers = require('../internals/map-helpers');
- var SetHelpers = require('../internals/set-helpers');
- var arrayBufferTransfer = require('../internals/array-buffer-transfer');
- var ERROR_STACK_INSTALLABLE = require('../internals/error-stack-installable');
- var PROPER_STRUCTURED_CLONE_TRANSFER = require('../internals/structured-clone-proper-transfer');
- var Object = global.Object;
- var Array = global.Array;
- var Date = global.Date;
- var Error = global.Error;
- var EvalError = global.EvalError;
- var RangeError = global.RangeError;
- var ReferenceError = global.ReferenceError;
- var SyntaxError = global.SyntaxError;
- var TypeError = global.TypeError;
- var URIError = global.URIError;
- var PerformanceMark = global.PerformanceMark;
- var WebAssembly = global.WebAssembly;
- var CompileError = WebAssembly && WebAssembly.CompileError || Error;
- var LinkError = WebAssembly && WebAssembly.LinkError || Error;
- var RuntimeError = WebAssembly && WebAssembly.RuntimeError || Error;
- var DOMException = getBuiltin('DOMException');
- var Map = MapHelpers.Map;
- var mapHas = MapHelpers.has;
- var mapGet = MapHelpers.get;
- var mapSet = MapHelpers.set;
- var Set = SetHelpers.Set;
- var setAdd = SetHelpers.add;
- var objectKeys = getBuiltin('Object', 'keys');
- var push = uncurryThis([].push);
- var thisBooleanValue = uncurryThis(true.valueOf);
- var thisNumberValue = uncurryThis(1.0.valueOf);
- var thisStringValue = uncurryThis(''.valueOf);
- var thisTimeValue = uncurryThis(Date.prototype.getTime);
- var PERFORMANCE_MARK = uid('structuredClone');
- var DATA_CLONE_ERROR = 'DataCloneError';
- var TRANSFERRING = 'Transferring';
- var checkBasicSemantic = function (structuredCloneImplementation) {
- return !fails(function () {
- var set1 = new global.Set([7]);
- var set2 = structuredCloneImplementation(set1);
- var number = structuredCloneImplementation(Object(7));
- return set2 === set1 || !set2.has(7) || typeof number != 'object' || +number !== 7;
- }) && structuredCloneImplementation;
- };
- var checkErrorsCloning = function (structuredCloneImplementation, $Error) {
- return !fails(function () {
- var error = new $Error();
- var test = structuredCloneImplementation({ a: error, b: error });
- return !(test && test.a === test.b && test.a instanceof $Error && test.a.stack === error.stack);
- });
- };
- // https://github.com/whatwg/html/pull/5749
- var checkNewErrorsCloningSemantic = function (structuredCloneImplementation) {
- return !fails(function () {
- var test = structuredCloneImplementation(new global.AggregateError([1], PERFORMANCE_MARK, { cause: 3 }));
- return test.name !== 'AggregateError' || test.errors[0] !== 1 || test.message !== PERFORMANCE_MARK || test.cause !== 3;
- });
- };
- // FF94+, Safari 15.4+, Chrome 98+, NodeJS 17.0+, Deno 1.13+
- // FF<103 and Safari implementations can't clone errors
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1556604
- // FF103 can clone errors, but `.stack` of clone is an empty string
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1778762
- // FF104+ fixed it on usual errors, but not on DOMExceptions
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1777321
- // Chrome <102 returns `null` if cloned object contains multiple references to one error
- // https://bugs.chromium.org/p/v8/issues/detail?id=12542
- // NodeJS implementation can't clone DOMExceptions
- // https://github.com/nodejs/node/issues/41038
- // only FF103+ supports new (html/5749) error cloning semantic
- var nativeStructuredClone = global.structuredClone;
- var FORCED_REPLACEMENT = IS_PURE
- || !checkErrorsCloning(nativeStructuredClone, Error)
- || !checkErrorsCloning(nativeStructuredClone, DOMException)
- || !checkNewErrorsCloningSemantic(nativeStructuredClone);
- // Chrome 82+, Safari 14.1+, Deno 1.11+
- // Chrome 78-81 implementation swaps `.name` and `.message` of cloned `DOMException`
- // Chrome returns `null` if cloned object contains multiple references to one error
- // Safari 14.1 implementation doesn't clone some `RegExp` flags, so requires a workaround
- // Safari implementation can't clone errors
- // Deno 1.2-1.10 implementations too naive
- // NodeJS 16.0+ does not have `PerformanceMark` constructor
- // NodeJS <17.2 structured cloning implementation from `performance.mark` is too naive
- // and can't clone, for example, `RegExp` or some boxed primitives
- // https://github.com/nodejs/node/issues/40840
- // no one of those implementations supports new (html/5749) error cloning semantic
- var structuredCloneFromMark = !nativeStructuredClone && checkBasicSemantic(function (value) {
- return new PerformanceMark(PERFORMANCE_MARK, { detail: value }).detail;
- });
- var nativeRestrictedStructuredClone = checkBasicSemantic(nativeStructuredClone) || structuredCloneFromMark;
- var throwUncloneable = function (type) {
- throw new DOMException('Uncloneable type: ' + type, DATA_CLONE_ERROR);
- };
- var throwUnpolyfillable = function (type, action) {
- throw new DOMException((action || 'Cloning') + ' of ' + type + ' cannot be properly polyfilled in this engine', DATA_CLONE_ERROR);
- };
- var tryNativeRestrictedStructuredClone = function (value, type) {
- if (!nativeRestrictedStructuredClone) throwUnpolyfillable(type);
- return nativeRestrictedStructuredClone(value);
- };
- var createDataTransfer = function () {
- var dataTransfer;
- try {
- dataTransfer = new global.DataTransfer();
- } catch (error) {
- try {
- dataTransfer = new global.ClipboardEvent('').clipboardData;
- } catch (error2) { /* empty */ }
- }
- return dataTransfer && dataTransfer.items && dataTransfer.files ? dataTransfer : null;
- };
- var cloneBuffer = function (value, map, $type) {
- if (mapHas(map, value)) return mapGet(map, value);
- var type = $type || classof(value);
- var clone, length, options, source, target, i;
- if (type === 'SharedArrayBuffer') {
- if (nativeRestrictedStructuredClone) clone = nativeRestrictedStructuredClone(value);
- // SharedArrayBuffer should use shared memory, we can't polyfill it, so return the original
- else clone = value;
- } else {
- var DataView = global.DataView;
- // `ArrayBuffer#slice` is not available in IE10
- // `ArrayBuffer#slice` and `DataView` are not available in old FF
- if (!DataView && typeof value.slice != 'function') throwUnpolyfillable('ArrayBuffer');
- // detached buffers throws in `DataView` and `.slice`
- try {
- if (typeof value.slice == 'function' && !value.resizable) {
- clone = value.slice(0);
- } else {
- length = value.byteLength;
- options = 'maxByteLength' in value ? { maxByteLength: value.maxByteLength } : undefined;
- clone = new ArrayBuffer(length, options);
- source = new DataView(value);
- target = new DataView(clone);
- for (i = 0; i < length; i++) {
- target.setUint8(i, source.getUint8(i));
- }
- }
- } catch (error) {
- throw new DOMException('ArrayBuffer is detached', DATA_CLONE_ERROR);
- }
- }
- mapSet(map, value, clone);
- return clone;
- };
- var cloneView = function (value, type, offset, length, map) {
- var C = global[type];
- // in some old engines like Safari 9, typeof C is 'object'
- // on Uint8ClampedArray or some other constructors
- if (!isObject(C)) throwUnpolyfillable(type);
- return new C(cloneBuffer(value.buffer, map), offset, length);
- };
- var Placeholder = function (object, type, metadata) {
- this.object = object;
- this.type = type;
- this.metadata = metadata;
- };
- var structuredCloneInternal = function (value, map, transferredBuffers) {
- if (isSymbol(value)) throwUncloneable('Symbol');
- if (!isObject(value)) return value;
- // effectively preserves circular references
- if (map) {
- if (mapHas(map, value)) return mapGet(map, value);
- } else map = new Map();
- var type = classof(value);
- var C, name, cloned, dataTransfer, i, length, keys, key;
- switch (type) {
- case 'Array':
- cloned = Array(lengthOfArrayLike(value));
- break;
- case 'Object':
- cloned = {};
- break;
- case 'Map':
- cloned = new Map();
- break;
- case 'Set':
- cloned = new Set();
- break;
- case 'RegExp':
- // in this block because of a Safari 14.1 bug
- // old FF does not clone regexes passed to the constructor, so get the source and flags directly
- cloned = new RegExp(value.source, getRegExpFlags(value));
- break;
- case 'Error':
- name = value.name;
- switch (name) {
- case 'AggregateError':
- cloned = new (getBuiltin('AggregateError'))([]);
- break;
- case 'EvalError':
- cloned = new EvalError();
- break;
- case 'RangeError':
- cloned = new RangeError();
- break;
- case 'ReferenceError':
- cloned = new ReferenceError();
- break;
- case 'SyntaxError':
- cloned = new SyntaxError();
- break;
- case 'TypeError':
- cloned = new TypeError();
- break;
- case 'URIError':
- cloned = new URIError();
- break;
- case 'CompileError':
- cloned = new CompileError();
- break;
- case 'LinkError':
- cloned = new LinkError();
- break;
- case 'RuntimeError':
- cloned = new RuntimeError();
- break;
- default:
- cloned = new Error();
- }
- break;
- case 'DOMException':
- cloned = new DOMException(value.message, value.name);
- break;
- case 'ArrayBuffer':
- case 'SharedArrayBuffer':
- cloned = transferredBuffers
- ? new Placeholder(value, type)
- : cloneBuffer(value, map, type);
- break;
- case 'DataView':
- case 'Int8Array':
- case 'Uint8Array':
- case 'Uint8ClampedArray':
- case 'Int16Array':
- case 'Uint16Array':
- case 'Int32Array':
- case 'Uint32Array':
- case 'Float16Array':
- case 'Float32Array':
- case 'Float64Array':
- case 'BigInt64Array':
- case 'BigUint64Array':
- length = type === 'DataView' ? value.byteLength : value.length;
- cloned = transferredBuffers
- ? new Placeholder(value, type, { offset: value.byteOffset, length: length })
- : cloneView(value, type, value.byteOffset, length, map);
- break;
- case 'DOMQuad':
- try {
- cloned = new DOMQuad(
- structuredCloneInternal(value.p1, map, transferredBuffers),
- structuredCloneInternal(value.p2, map, transferredBuffers),
- structuredCloneInternal(value.p3, map, transferredBuffers),
- structuredCloneInternal(value.p4, map, transferredBuffers)
- );
- } catch (error) {
- cloned = tryNativeRestrictedStructuredClone(value, type);
- }
- break;
- case 'File':
- if (nativeRestrictedStructuredClone) try {
- cloned = nativeRestrictedStructuredClone(value);
- // NodeJS 20.0.0 bug, https://github.com/nodejs/node/issues/47612
- if (classof(cloned) !== type) cloned = undefined;
- } catch (error) { /* empty */ }
- if (!cloned) try {
- cloned = new File([value], value.name, value);
- } catch (error) { /* empty */ }
- if (!cloned) throwUnpolyfillable(type);
- break;
- case 'FileList':
- dataTransfer = createDataTransfer();
- if (dataTransfer) {
- for (i = 0, length = lengthOfArrayLike(value); i < length; i++) {
- dataTransfer.items.add(structuredCloneInternal(value[i], map, transferredBuffers));
- }
- cloned = dataTransfer.files;
- } else cloned = tryNativeRestrictedStructuredClone(value, type);
- break;
- case 'ImageData':
- // Safari 9 ImageData is a constructor, but typeof ImageData is 'object'
- try {
- cloned = new ImageData(
- structuredCloneInternal(value.data, map, transferredBuffers),
- value.width,
- value.height,
- { colorSpace: value.colorSpace }
- );
- } catch (error) {
- cloned = tryNativeRestrictedStructuredClone(value, type);
- } break;
- default:
- if (nativeRestrictedStructuredClone) {
- cloned = nativeRestrictedStructuredClone(value);
- } else switch (type) {
- case 'BigInt':
- // can be a 3rd party polyfill
- cloned = Object(value.valueOf());
- break;
- case 'Boolean':
- cloned = Object(thisBooleanValue(value));
- break;
- case 'Number':
- cloned = Object(thisNumberValue(value));
- break;
- case 'String':
- cloned = Object(thisStringValue(value));
- break;
- case 'Date':
- cloned = new Date(thisTimeValue(value));
- break;
- case 'Blob':
- try {
- cloned = value.slice(0, value.size, value.type);
- } catch (error) {
- throwUnpolyfillable(type);
- } break;
- case 'DOMPoint':
- case 'DOMPointReadOnly':
- C = global[type];
- try {
- cloned = C.fromPoint
- ? C.fromPoint(value)
- : new C(value.x, value.y, value.z, value.w);
- } catch (error) {
- throwUnpolyfillable(type);
- } break;
- case 'DOMRect':
- case 'DOMRectReadOnly':
- C = global[type];
- try {
- cloned = C.fromRect
- ? C.fromRect(value)
- : new C(value.x, value.y, value.width, value.height);
- } catch (error) {
- throwUnpolyfillable(type);
- } break;
- case 'DOMMatrix':
- case 'DOMMatrixReadOnly':
- C = global[type];
- try {
- cloned = C.fromMatrix
- ? C.fromMatrix(value)
- : new C(value);
- } catch (error) {
- throwUnpolyfillable(type);
- } break;
- case 'AudioData':
- case 'VideoFrame':
- if (!isCallable(value.clone)) throwUnpolyfillable(type);
- try {
- cloned = value.clone();
- } catch (error) {
- throwUncloneable(type);
- } break;
- case 'CropTarget':
- case 'CryptoKey':
- case 'FileSystemDirectoryHandle':
- case 'FileSystemFileHandle':
- case 'FileSystemHandle':
- case 'GPUCompilationInfo':
- case 'GPUCompilationMessage':
- case 'ImageBitmap':
- case 'RTCCertificate':
- case 'WebAssembly.Module':
- throwUnpolyfillable(type);
- // break omitted
- default:
- throwUncloneable(type);
- }
- }
- mapSet(map, value, cloned);
- switch (type) {
- case 'Array':
- case 'Object':
- keys = objectKeys(value);
- for (i = 0, length = lengthOfArrayLike(keys); i < length; i++) {
- key = keys[i];
- createProperty(cloned, key, structuredCloneInternal(value[key], map, transferredBuffers));
- } break;
- case 'Map':
- value.forEach(function (v, k) {
- mapSet(cloned, structuredCloneInternal(k, map, transferredBuffers), structuredCloneInternal(v, map, transferredBuffers));
- });
- break;
- case 'Set':
- value.forEach(function (v) {
- setAdd(cloned, structuredCloneInternal(v, map, transferredBuffers));
- });
- break;
- case 'Error':
- createNonEnumerableProperty(cloned, 'message', structuredCloneInternal(value.message, map, transferredBuffers));
- if (hasOwn(value, 'cause')) {
- createNonEnumerableProperty(cloned, 'cause', structuredCloneInternal(value.cause, map, transferredBuffers));
- }
- if (name === 'AggregateError') {
- cloned.errors = structuredCloneInternal(value.errors, map, transferredBuffers);
- } // break omitted
- case 'DOMException':
- if (ERROR_STACK_INSTALLABLE) {
- createNonEnumerableProperty(cloned, 'stack', structuredCloneInternal(value.stack, map, transferredBuffers));
- }
- }
- return cloned;
- };
- var replacePlaceholders = function (value, map) {
- if (!isObject(value)) return value;
- if (mapHas(map, value)) return mapGet(map, value);
- var type, object, metadata, i, length, keys, key, replacement;
- if (value instanceof Placeholder) {
- type = value.type;
- object = value.object;
- switch (type) {
- case 'ArrayBuffer':
- case 'SharedArrayBuffer':
- replacement = cloneBuffer(object, map, type);
- break;
- case 'DataView':
- case 'Int8Array':
- case 'Uint8Array':
- case 'Uint8ClampedArray':
- case 'Int16Array':
- case 'Uint16Array':
- case 'Int32Array':
- case 'Uint32Array':
- case 'Float16Array':
- case 'Float32Array':
- case 'Float64Array':
- case 'BigInt64Array':
- case 'BigUint64Array':
- metadata = value.metadata;
- replacement = cloneView(object, type, metadata.offset, metadata.length, map);
- }
- } else switch (classof(value)) {
- case 'Array':
- case 'Object':
- keys = objectKeys(value);
- for (i = 0, length = lengthOfArrayLike(keys); i < length; i++) {
- key = keys[i];
- value[key] = replacePlaceholders(value[key], map);
- } break;
- case 'Map':
- replacement = new Map();
- value.forEach(function (v, k) {
- mapSet(replacement, replacePlaceholders(k, map), replacePlaceholders(v, map));
- });
- break;
- case 'Set':
- replacement = new Set();
- value.forEach(function (v) {
- setAdd(replacement, replacePlaceholders(v, map));
- });
- break;
- case 'Error':
- value.message = replacePlaceholders(value.message, map);
- if (hasOwn(value, 'cause')) {
- value.cause = replacePlaceholders(value.cause, map);
- }
- if (value.name === 'AggregateError') {
- value.errors = replacePlaceholders(value.errors, map);
- } // break omitted
- case 'DOMException':
- if (ERROR_STACK_INSTALLABLE) {
- value.stack = replacePlaceholders(value.stack, map);
- }
- }
- mapSet(map, value, replacement || value);
- return replacement || value;
- };
- var tryToTransfer = function (rawTransfer, map) {
- if (!isObject(rawTransfer)) throw new TypeError('Transfer option cannot be converted to a sequence');
- var transfer = [];
- iterate(rawTransfer, function (value) {
- push(transfer, anObject(value));
- });
- var i = 0;
- var length = lengthOfArrayLike(transfer);
- var buffers = [];
- var value, type, C, transferred, canvas, context;
- while (i < length) {
- value = transfer[i++];
- type = classof(value);
- if (type === 'ArrayBuffer') {
- push(buffers, value);
- continue;
- }
- if (mapHas(map, value)) throw new DOMException('Duplicate transferable', DATA_CLONE_ERROR);
- if (PROPER_STRUCTURED_CLONE_TRANSFER) {
- transferred = nativeStructuredClone(value, { transfer: [value] });
- } else switch (type) {
- case 'ImageBitmap':
- C = global.OffscreenCanvas;
- if (!isConstructor(C)) throwUnpolyfillable(type, TRANSFERRING);
- try {
- canvas = new C(value.width, value.height);
- context = canvas.getContext('bitmaprenderer');
- context.transferFromImageBitmap(value);
- transferred = canvas.transferToImageBitmap();
- } catch (error) { /* empty */ }
- break;
- case 'AudioData':
- case 'VideoFrame':
- if (!isCallable(value.clone) || !isCallable(value.close)) throwUnpolyfillable(type, TRANSFERRING);
- try {
- transferred = value.clone();
- value.close();
- } catch (error) { /* empty */ }
- break;
- case 'MediaSourceHandle':
- case 'MessagePort':
- case 'OffscreenCanvas':
- case 'ReadableStream':
- case 'TransformStream':
- case 'WritableStream':
- throwUnpolyfillable(type, TRANSFERRING);
- }
- if (transferred === undefined) throw new DOMException('This object cannot be transferred: ' + type, DATA_CLONE_ERROR);
- mapSet(map, value, transferred);
- }
- return buffers;
- };
- var tryToTransferBuffers = function (transfer, map) {
- var i = 0;
- var length = lengthOfArrayLike(transfer);
- var value, transferred;
- while (i < length) {
- value = transfer[i++];
- if (mapHas(map, value)) throw new DOMException('Duplicate transferable', DATA_CLONE_ERROR);
- if (arrayBufferTransfer) {
- transferred = arrayBufferTransfer(value, undefined, true);
- } else {
- if (!isCallable(value.transfer)) throwUnpolyfillable('ArrayBuffer', TRANSFERRING);
- transferred = value.transfer();
- }
- mapSet(map, value, transferred);
- }
- };
- // `structuredClone` method
- // https://html.spec.whatwg.org/multipage/structured-data.html#dom-structuredclone
- $({ global: true, enumerable: true, sham: !PROPER_STRUCTURED_CLONE_TRANSFER, forced: FORCED_REPLACEMENT }, {
- structuredClone: function structuredClone(value /* , { transfer } */) {
- var options = validateArgumentsLength(arguments.length, 1) > 1 && !isNullOrUndefined(arguments[1]) ? anObject(arguments[1]) : undefined;
- var transfer = options ? options.transfer : undefined;
- var transferredBuffers = false;
- var map, buffers;
- if (transfer !== undefined) {
- map = new Map();
- buffers = tryToTransfer(transfer, map);
- transferredBuffers = !!lengthOfArrayLike(buffers);
- }
- var clone = structuredCloneInternal(value, map, transferredBuffers);
- // since of an issue with cloning views of transferred buffers, we a forced to transfer / clone them in 2 steps
- // https://github.com/zloirock/core-js/issues/1265
- if (transferredBuffers) {
- map = new Map();
- tryToTransferBuffers(transfer, map);
- clone = replacePlaceholders(clone, map);
- }
- return clone;
- }
- });
|