4 <meta content=
"text/html; charset=utf-8" http-equiv=
"content-type" />
5 <title>2.8 Common DOM interfaces - Structured Clone Algorithm
</title>
6 <link rel=
"help" href=
"http://www.w3.org/TR/html5/common-dom-interfaces.html#safe-passing-of-structured-data" />
7 <script src=
"/resources/testharness.js"></script>
8 <script src=
"/resources/testharnessreport.js"></script>
13 <script type=
"text/javascript">
18 //the worker is used for each test in sequence
19 //worker's callback will be set for each test
20 //worker's internal onmessage echoes the data back to this thread through postMessage
21 worker
= new Worker("./resources/echo-worker.js");
24 var t
= async_test("Primitive string is cloned");
26 worker
.onmessage
= t
.step_func(function(e
) {assert_equals("primitive string", e
.data
, "\"primitive string\" === event.data"); t
.done(); });
27 t
.step(function() { worker
.postMessage("primitive string");});
30 var t
= async_test("Primitive integer is cloned");
32 worker
.onmessage
= t
.step_func(function(e
) {assert_equals(2000, e
.data
, "2000 === event.data"); t
.done(); });
33 t
.step(function() { worker
.postMessage(2000);});
36 var t
= async_test("Primitive floating point is cloned");
38 worker
.onmessage
= t
.step_func(function(e
) {assert_equals(111.456, e
.data
, "111.456 === event.data"); t
.done(); });
39 t
.step(function() { worker
.postMessage(111.456);});
42 var t
= async_test("Primitive floating point (negative) is cloned");
44 worker
.onmessage
= t
.step_func(function(e
) {assert_equals(-111.456, e
.data
, "-111.456 === event.data"); t
.done(); });
45 t
.step(function() { worker
.postMessage(-111.456);});
48 var t
= async_test("Primitive number (hex) is cloned");
50 worker
.onmessage
= t
.step_func(function(e
) {assert_equals(0xAB25, e
.data
, "0xAB25 === event.data"); t
.done(); });
51 t
.step(function() { worker
.postMessage(0xAB25);});
54 var t
= async_test("Primitive number (scientific) is cloned");
56 worker
.onmessage
= t
.step_func(function(e
) {assert_equals(15e2
, e
.data
, "15e2 === event.data"); t
.done(); });
57 t
.step(function() { worker
.postMessage(15e2
);});
60 var t
= async_test("Primitive boolean is cloned");
62 worker
.onmessage
= t
.step_func(function(e
) {assert_equals(false, e
.data
, "false === event.data"); t
.done(); });
63 t
.step(function() { worker
.postMessage(false);});
66 var t
= async_test("Instance of Boolean is cloned");
69 t
.step(function() {obj
= new Boolean(false);});
70 worker
.onmessage
= t
.step_func(function(e
) {
71 assert_equals(obj
.constructor, e
.data
.constructor, "Boolean === event.data.constructor");
72 assert_equals(obj
.valueOf(), e
.data
.valueOf(), "(new Boolean(false)).valueof() === event.data.valueOf()");
75 t
.step(function() { worker
.postMessage(obj
);});
77 var t
= async_test("Instance of Number is cloned");
80 t
.step(function() {obj
= new Number(2000);});
81 worker
.onmessage
= t
.step_func(function(e
) {
82 assert_equals(obj
.constructor, e
.data
.constructor, "Number === event.data.constructor");
83 assert_equals(obj
.valueOf(), e
.data
.valueOf(), "(new Number(2000)).valueof() === event.data.valueOf()");
86 t
.step(function() { worker
.postMessage(obj
);});
89 var t
= async_test("Instance of String is cloned");
92 t
.step(function() { obj
= new String("String Object");});
93 worker
.onmessage
= t
.step_func(function(e
) {
94 assert_equals(obj
.constructor, e
.data
.constructor, "String === event.data.constructor");
95 assert_equals(obj
.valueOf(), e
.data
.valueOf(), "(new String(\"String Object\")).valueof() === event.data.valueOf()");
98 t
.step(function() { worker
.postMessage(obj
);});
101 var t
= async_test("Instance of Date is cloned");
104 t
.step(function() { obj
= new Date(2011,1,1);});
105 worker
.onmessage
= t
.step_func(function(e
) {
106 assert_equals(obj
.constructor, e
.data
.constructor, "Date === event.data.constructor");
107 assert_equals(obj
.valueOf(), e
.data
.valueOf(), "(new Date(2011,1,1)).valueof() === event.data.valueOf()");
110 t
.step(function() { worker
.postMessage(obj
);});
113 var t
= async_test("Instance of RegExp is cloned");
116 t
.step(function() {obj
= new RegExp("w3+c","g","i");});
117 worker
.onmessage
= t
.step_func(function(e
) {
118 assert_equals(obj
.constructor, e
.data
.constructor, "RegExp === event.data.constructor");
119 assert_equals(obj
.source
, e
.data
.source
, "canon.source === event.data.source");
120 assert_equals(obj
.multiline
, e
.data
.multiline
, "canon.multiline === event.data.multiline");
121 assert_equals(obj
.global
, e
.data
.global
, "canon.global === event.data.global");
122 assert_equals(obj
.ignoreCase
, e
.data
.ignoreCase
, "canon.ignoreCase === event.data.ignoreCase");
125 t
.step(function() { worker
.postMessage(obj
);});
128 var t
= async_test("Value 'null' is cloned");
130 worker
.onmessage
= t
.step_func(function(e
) {assert_equals(null, e
.data
, "null === event.data"); t
.done(); });
131 t
.step(function() { worker
.postMessage(null);});
134 var t
= async_test("Value 'undefined' is cloned");
136 worker
.onmessage
= t
.step_func(function(e
) {assert_equals(undefined, e
.data
, "undefined === event.data"); t
.done(); });
137 t
.step(function() { worker
.postMessage(undefined);});
140 var t
= async_test("Object properties are cloned");
149 worker
.onmessage
= t
.step_func(function(e
) {
150 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
151 assert_equals(obj
.a
, e
.data
.a
, "canon.a === event.data.a");
152 assert_equals(obj
.b
, e
.data
.b
, "canon.b === event.data.b");
153 assert_equals(obj
.child
, e
.data
.child
, "canon.child === e.data.child");
156 t
.step(function() { worker
.postMessage(obj
);});
159 var t
= async_test("Prototype chains are not walked.");
167 Object
.defineProperty(Custom
.prototype, "b", { enumerable
: true, value
: 100 });
170 worker
.onmessage
= t
.step_func(function(e
) {
171 assert_not_equals(obj
.constructor, e
.data
.constructor, "canon.constructor !== event.data.constructor");
172 assert_equals(Object
, e
.data
.constructor, "Object === e.data.constructor");
173 assert_equals(obj
.a
, e
.data
.a
, "canon.a === e.data.a");
174 assert_equals(undefined, e
.data
.b
, "undefined === e.data.b");
177 t
.step(function() { worker
.postMessage(obj
);});
180 var t
= async_test("Property descriptors of Objects are not cloned");
185 Object
.defineProperty(obj
, "a", { enumerable
: true, writable
: false, value
: 100 });
187 worker
.onmessage
= t
.step_func(function(e
) {
188 var des
= Object
.getOwnPropertyDescriptor(e
.data
, "a");
189 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
190 assert_true(des
.writable
, "Descriptor is writable");
193 t
.step(function() { worker
.postMessage(obj
);});
196 var t
= async_test("Cycles are preserved in Objects");
203 worker
.onmessage
= t
.step_func(function(e
) {
204 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
205 assert_equals(e
.data
, e
.data
.a
, "cycle is preserved");
208 t
.step(function() { worker
.postMessage(obj
);});
211 var t
= async_test("Identity of duplicates is preserved");
218 Object
.defineProperty(ref
, "child", {get: function(){this.called
++;}, enumerable
: true});
220 obj
= {a
:ref
, b
:ref
};
222 worker
.onmessage
= t
.step_func(function(e
) {
223 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
224 assert_equals(e
.data
.b
.called
, 0, "e.data.b.called === 0");
227 t
.step(function() { worker
.postMessage(obj
);});
230 var t
= async_test("Property order is preserved");
234 obj
= { "a": "hello", "b": "w3c", "c": "and world" };
237 worker
.onmessage
= t
.step_func(function(e
) {
238 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
239 var canonNames
= Object
.getOwnPropertyNames(obj
);
240 var testNames
= Object
.getOwnPropertyNames(e
.data
);
241 for (var i
in canonNames
) {
242 assert_equals(canonNames
[i
], testNames
[i
], "canonProperty["+i
+"] === dataProperty["+i
+"]");
246 t
.step(function() { worker
.postMessage(obj
);});
249 var t
= async_test("Enumerable properties of Arrays are cloned");
256 worker
.onmessage
= t
.step_func(function(e
) {
257 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
258 assert_equals(e
.data
["a"], "named1", "e.data[\"a\"] === \"named1\"");
259 assert_equals(e
.data
[0], 0, "e.data[0] === 0");
260 assert_equals(e
.data
[1], 1, "e.data[1] === 1");
263 t
.step(function() { worker
.postMessage(obj
);});
266 var t
= async_test("Property descriptors of Arrays are not cloned");
271 Object
.defineProperty(obj
, "2", { enumerable
: true, writable
: false, value
: 100 });
273 worker
.onmessage
= t
.step_func(function(e
) {
274 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
275 assert_equals(e
.data
[0], 0, "e.data[0] === 0");
276 assert_equals(e
.data
[1], 1, "e.data[1] === 1");
277 var des
= Object
.getOwnPropertyDescriptor(e
.data
, "2");
278 assert_true(des
.writable
, "Descriptor is writable");
281 t
.step(function() { worker
.postMessage(obj
);});
284 var t
= async_test("Cycles are preserved in Arrays");
291 worker
.onmessage
= t
.step_func(function(e
) {
292 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
293 assert_equals(e
.data
[0], 0, "e.data[0] === 0");
294 assert_equals(e
.data
[1], 1, "e.data[1] === 1");
295 assert_equals(e
.data
[2], e
.data
, "e.data[2] === e.data");
298 t
.step(function() { worker
.postMessage(obj
);});
302 var t
= async_test("ImageData object can be cloned");
306 var canvas
= document
.createElement("canvas");
309 var context
= canvas
.getContext('2d');
310 obj
= context
.createImageData(40, 40);
311 assert_true(window
.hasOwnProperty("ImageData"), "ImageData constructor must be present");
312 assert_true(obj
instanceof ImageData
, "ImageData must be returned by .createImageData");
314 worker
.onmessage
= t
.step_func(function(e
) {
315 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
316 assert_not_equals(obj
, e
.data
, "cloned object should be a new instance of ImageData");
317 assert_equals(obj
.width
, e
.data
.width
, "canon.width === e.data.width");
318 assert_equals(obj
.height
, e
.data
.height
, "canon.height === e.data.height");
319 assert_array_equals(obj
.data
, e
.data
.data
, "data arrays are the same");
322 t
.step(function() { worker
.postMessage(obj
);});
325 var t
= async_test("ImageData expandos are not cloned");
329 var canvas
= document
.createElement("canvas");
332 var context
= canvas
.getContext('2d');
333 obj
= context
.createImageData(40, 40);
334 assert_true(window
.hasOwnProperty("ImageData"), "ImageData constructor must be present");
335 assert_true(obj
instanceof ImageData
, "ImageData must be returned by .createImageData");
338 worker
.onmessage
= t
.step_func(function(e
) {
339 assert_equals(obj
.constructor, e
.data
.constructor, "canon.constructor === event.data.constructor");
340 assert_not_equals(obj
, e
.data
, "cloned object should be a new instance of ImageData");
341 assert_equals(obj
.width
, e
.data
.width
, "canon.width === e.data.width");
342 assert_equals(obj
.height
, e
.data
.height
, "canon.height === e.data.height");
343 assert_array_equals(obj
.data
, e
.data
.data
, "data arrays are the same");
344 assert_equals(undefined, e
.data
.foo
, "Expando is lost (undefined === e.data.foo)");
347 t
.step(function() { worker
.postMessage(obj
);});
350 var t
= async_test("Window objects cannot be cloned");
352 worker
.onmessage = function() {}; //no op because exception should be thrown.
354 assert_true(DOMException
.hasOwnProperty('DATA_CLONE_ERR'), "DOMException.DATA_CLONE_ERR is present");
355 assert_equals(DOMException
.DATA_CLONE_ERR
, 25, "DOMException.DATA_CLONE_ERR === 25");
356 assert_throws('DATA_CLONE_ERR', function() {worker
.postMessage(window
)});
361 var t
= async_test("Document objects cannot be cloned");
363 worker
.onmessage = function() {}; //no op because exception should be thrown.
365 assert_true(DOMException
.hasOwnProperty('DATA_CLONE_ERR'), "DOMException.DATA_CLONE_ERR is present");
366 assert_equals(DOMException
.DATA_CLONE_ERR
, 25, "DOMException.DATA_CLONE_ERR === 25");
367 assert_throws('DATA_CLONE_ERR', function() {worker
.postMessage(document
)});
372 }, {explicit_done
:true});
374 //Callback for result_callback
375 //queues the next test in the array testCollection
376 //serves to make test execution sequential from the async worker callbacks
377 //alternatively, we would have to create a worker for each test
378 function testFinished(test
) {
379 if(test
.id
< testCollection
.length
- 1) {
380 //queue the function so that stack remains shallow
381 queue(testCollection
[test
.id
+1]);
383 //when the last test has run, explicitly end test suite
387 function queue(func
) {
388 step_timeout(func
, 10);
391 add_result_callback(testFinished
);
392 //start the first test manually
393 queue(testCollection
[0]);