| <!DOCTYPE HTML> |
| <script src="../../resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| <script> |
| function promise_test(func, name, properties) { |
| properties = properties || {}; |
| var test = async_test(name, properties); |
| Promise.resolve(test.step(func, test, test)) |
| .then(function() { test.done(); }) |
| .catch(test.step_func(function(value) { throw value; })); |
| } |
| |
| function structuredClone(o) |
| { |
| return new Promise(function(resolve, reject) { |
| var mc = new MessageChannel(); |
| mc.port2.onmessage = function(e) { resolve(e.data); }; |
| mc.port1.postMessage(o); |
| }); |
| } |
| |
| promise_test(function() { |
| var inner = {}; |
| var orig = { inner: inner }; |
| inner.outer = orig; |
| return structuredClone(orig).then(function(clone) { |
| assert_equals(clone.inner.outer, clone, 'Cycles should be preserved'); |
| }); |
| }, 'Verify: "This algorithm preserves cycles..."'); |
| |
| promise_test(function() { |
| var gen = {name: 'AES-CBC', length: 128}; |
| return crypto.subtle.generateKey(gen, false, ['encrypt']).then(function(key) { |
| var simple = {}; |
| var blob = new Blob(['content']); |
| var orig = { |
| s1: simple, s2: simple, |
| b1: blob, b2: blob, |
| k1: key, k2: key |
| }; |
| return structuredClone(orig).then(function(clone) { |
| assert_equals(clone.s1, clone.s2, 'JS object identity should be preserved'); |
| assert_equals(clone.b1, clone.b2, 'Core object identity should be preserved'); |
| assert_equals(clone.k1, clone.k2, 'Module object identity should be preserved'); |
| }); |
| }); |
| }, 'Verify: "This algorithm preserves... the identity of duplicate objects in graphs."'); |
| |
| promise_test(function() { |
| var name = 'this is a setter'; |
| |
| var orig = {}; |
| orig[name] = 'value'; |
| |
| var setter_called = false; |
| Object.defineProperty(Object.prototype, name, { |
| set: function(a) { |
| setter_called = true; |
| } |
| }); |
| |
| assert_true(orig.hasOwnProperty(name)); |
| |
| return structuredClone(orig).then(function(clone) { |
| assert_equals(typeof clone, 'object', 'Clone should be an object'); |
| assert_false(setter_called, 'Setter should not be called by cloning algorithm.'); |
| assert_true(clone.hasOwnProperty(name), 'Cloning algorithm should add an own property.') |
| assert_equals(clone[name], orig[name], 'Property value should match'); |
| }); |
| }, 'Verify: "Add a new property..." (objects)'); |
| |
| promise_test(function() { |
| var name = '123456'; |
| |
| var orig = []; |
| orig[name] = 'value'; |
| |
| var setter_called = false; |
| Object.defineProperty(Object.prototype, name, { |
| set: function(a) { |
| setter_called = true; |
| } |
| }); |
| |
| assert_true(orig.hasOwnProperty(name)); |
| |
| return structuredClone(orig).then(function(clone) { |
| assert_true(Array.isArray(clone), 'Clone should be an Array'); |
| assert_false(setter_called, 'Setter should not be called by cloning algorithm.'); |
| assert_true(clone.hasOwnProperty(name), 'Cloning algorithm should add an own property.') |
| assert_equals(clone[name], orig[name], 'Property value should match'); |
| }); |
| }, 'Verify: "Add a new property..." (arrays)'); |
| |
| promise_test(function() { |
| var name = '256'; |
| |
| var orig = []; |
| for (var i = 0; i < 256; ++i) { |
| orig[i] = i; |
| } |
| |
| orig[name] = 'value'; |
| |
| var setter_called = false; |
| Object.defineProperty(Object.prototype, name, { |
| set: function(a) { |
| setter_called = true; |
| } |
| }); |
| |
| assert_true(orig.hasOwnProperty(name)); |
| |
| return structuredClone(orig).then(function(clone) { |
| assert_true(Array.isArray(clone), 'Clone should be an Array'); |
| assert_false(setter_called, 'Setter should not be called by cloning algorithm.'); |
| assert_true(clone.hasOwnProperty(name), 'Cloning algorithm should add an own property.') |
| assert_equals(clone[name], orig[name], 'Property value should match'); |
| }); |
| }, 'Verify: "Add a new property..." (dense arrays)'); |
| |
| promise_test(function() { |
| var orig = { |
| emptySet: new Set, |
| set: new Set([1, 2, 3]), |
| emptyMap: new Map, |
| map: new Map([[1, 2], [3, 4]]), |
| }; |
| return structuredClone(orig).then(function(clone) { |
| assert_true(clone.emptySet instanceof Set, 'Clone should be a Set'); |
| assert_true(clone.emptyMap instanceof Map, 'Clone should be a Map'); |
| assert_true(clone.set instanceof Set, 'Clone should be a Set'); |
| assert_true(clone.map instanceof Map, 'Clone should be a Map'); |
| assert_equals(clone.emptySet.size, 0, 'Clone should be the same size'); |
| assert_equals(clone.emptyMap.size, 0, 'Clone should be the same size'); |
| assert_equals(clone.set.size, orig.set.size, 'Clone should be the same size'); |
| assert_equals(clone.map.size, orig.map.size, 'Clone should be the same size'); |
| assert_true(clone.set.has(1) && clone.set.has(2) && clone.set.has(3), 'Cloned set should have the same keys'); |
| assert_true(clone.map.get(1) == 2 && clone.map.get(3) == 4, 'Cloned map should have the same keys and values'); |
| }); |
| }, 'Maps and Sets are cloned'); |
| |
| promise_test(function() { |
| var set = new Set; |
| set.add(set); |
| var map = new Map; |
| map.set(map, map); |
| var orig = { map: map, set: set }; |
| return structuredClone(orig).then(function(clone) { |
| assert_true(clone.set instanceof Set, 'Clone should be a Set'); |
| assert_true(clone.map instanceof Map, 'Clone should be a Map'); |
| assert_equals(clone.set, Array.from(clone.set)[0], 'Recursive sets should preserve identity'); |
| assert_equals(clone.map, Array.from(clone.map)[0][0], 'Recursive maps should preserve identity'); |
| assert_equals(clone.map, Array.from(clone.map)[0][1], 'Recursive maps should preserve identity'); |
| }); |
| }, 'Cloning Maps and Sets preserve cycles'); |
| |
| promise_test(function() { |
| var set = new Set; |
| var map = new Map; |
| var setMutator = { |
| get val() { |
| set.add('mutated'); |
| return 'setMutator'; |
| } |
| } |
| var mapMutator = { |
| get val() { |
| map.set('mutated', true); |
| return 'mapMutator'; |
| } |
| } |
| set.add(setMutator); |
| map.set('mapMutator', mapMutator); |
| var orig = { map: map, set: set }; |
| return structuredClone(orig).then(function(clone) { |
| assert_true(clone.set instanceof Set, 'Clone should be a Set'); |
| assert_true(clone.map instanceof Map, 'Clone should be a Map'); |
| assert_equals(orig.set.size, 2, 'Original set should have been mutated'); |
| assert_equals(orig.map.size, 2, 'Original map should have been mutated'); |
| assert_equals(clone.set.size, 1, 'Cloned set should not reflect mutation'); |
| assert_equals(clone.map.size, 1, 'Cloned map should not reflect mutation'); |
| assert_equals(Array.from(clone.set)[0].val, 'setMutator', 'Cloned set should contain getter return value'); |
| assert_equals(clone.map.get('mapMutator').val, 'mapMutator', 'Cloned map should contain getter return value'); |
| }); |
| }, 'Cloned Maps and Sets do not reflect mutations that occur during cloning'); |
| |
| promise_test(function() { |
| var map = new Map([['key', function(){}]]); |
| return structuredClone(map).then(function(clone) { |
| assert_unreached('Should have thrown an exception'); |
| }, function(ex) { |
| assert_true(ex instanceof DOMException, 'Should throw a DOMException'); |
| assert_equals(ex.code, DOMException.DATA_CLONE_ERR, 'Should be a DataCloneError'); |
| }); |
| }, 'Cloning Maps should fail if they contain non-cloneable things'); |
| |
| promise_test(function() { |
| var set = new Set([function(){}]); |
| return structuredClone(set).then(function(clone) { |
| assert_unreached('Should have thrown an exception'); |
| }, function(ex) { |
| assert_true(ex instanceof DOMException, 'Should throw a DOMException'); |
| assert_equals(ex.code, DOMException.DATA_CLONE_ERR, 'Should be a DataCloneError'); |
| }); |
| }, 'Cloning Sets should fail if they contain non-cloneable things'); |
| |
| promise_test(function() { |
| return structuredClone(Symbol('foo')).then(function(clone) { |
| assert_unreached('Should have thrown an exception'); |
| }, function(ex) { |
| assert_true(ex instanceof DOMException, 'Should throw a DOMException'); |
| assert_equals(ex.code, DOMException.DATA_CLONE_ERR, 'Should be a DataCloneError'); |
| }); |
| }, 'Cloning Symbols should fail'); |
| |
| </script> |