| OLD | NEW |
| 1 <html> | 1 <html> |
| 2 <head></head> | 2 <head></head> |
| 3 <body> | 3 <body> |
| 4 <div id="description"></div> | 4 <div id="description"></div> |
| 5 <input type="file" id="fileInput" multiple></input> | 5 <input type="file" id="fileInput" multiple></input> |
| 6 <div id="console"></div> | 6 <div id="console"></div> |
| 7 <script src="script-tests/postmessage-test.js"></script> | 7 <script src="resources/postmessage-test.js"></script> |
| 8 <script src="script-tests/postmessage-clone.js"></script> | 8 <script> |
| 9 document.getElementById("description").innerHTML = "Tests that we clone object h
ierarchies"; |
| 10 |
| 11 tryPostMessage('null'); |
| 12 tryPostMessage('undefined'); |
| 13 tryPostMessage('1'); |
| 14 tryPostMessage('true'); |
| 15 tryPostMessage('"1"'); |
| 16 tryPostMessage('({})'); |
| 17 tryPostMessage('({a:1})'); |
| 18 tryPostMessage('({a:"a"})'); |
| 19 tryPostMessage('({b:"a", a:"b"})'); |
| 20 tryPostMessage('({p0:"string0", p1:"string1", p2:"string2", p3:"string3", p4:"st
ring4", p5:"string5", p6:"string6", p7:"string7", p8:"string8", p9:"string9", p1
0:"string10", p11:"string11", p12:"string12", p13:"string13", p14:"string14", p1
5:"string15", p16:"string16", p17:"string17", p18:"string18", p19:"string19"})')
; |
| 21 tryPostMessage('({p0:"string1", p1:"string1", p2:"string2", p3:"string3", p4:"st
ring4", p5:"string5", p6:"string6", p7:"string7", p8:"string8", p9:"string9", p1
0:"string10", p11:"string11", p12:"string12", p13:"string13", p14:"string14", p1
5:"string15", p16:"string16", p17:"string17", p18:"string18", p19:"string19"})')
; |
| 22 tryPostMessage('({a:""})'); |
| 23 tryPostMessage('({a:0})'); |
| 24 tryPostMessage('({a:1})'); |
| 25 tryPostMessage('[]'); |
| 26 tryPostMessage('["a", "a", "b", "a", "b"]'); |
| 27 tryPostMessage('["a", "a", "b", {a:"b", b:"a"}]'); |
| 28 tryPostMessage('[1,2,3]'); |
| 29 tryPostMessage('[,,1]'); |
| 30 tryPostMessage('(function(){})', true, null, DOMException.DATA_CLONE_ERR); |
| 31 tryPostMessage('var x = 0; try { eval("badref"); } catch(e) { x = e; } x', true,
null, DOMException.DATA_CLONE_ERR); |
| 32 var arrayBuffer = new ArrayBuffer(1); |
| 33 tryPostMessage('"data"', false, null, 0, [arrayBuffer]); |
| 34 tryPostMessage('arrayBuffer', true, null, DOMException.DATA_CLONE_ERR); |
| 35 var duplicateArrayBuffer = new ArrayBuffer(1); |
| 36 tryPostMessage('"data"', true, null, DOMException.DATA_CLONE_ERR, [duplicateArra
yBuffer, duplicateArrayBuffer]); |
| 37 var uint8Array = new Uint8Array([10]); |
| 38 tryPostMessage('"data"', false, null, 0, [uint8Array.buffer]); |
| 39 tryPostMessage('uint8Array', true, null, DOMException.DATA_CLONE_ERR); |
| 40 tryPostMessage('new Date(1234567890000)'); |
| 41 tryPostMessage('new ConstructorWithPrototype("foo")', false, '({field:"foo"})'); |
| 42 tryPostMessage('new Boolean(true)'); |
| 43 tryPostMessage('new Boolean(false)'); |
| 44 tryPostMessage('new String("gnirts")'); |
| 45 tryPostMessage('new Number(42.0)'); |
| 46 cyclicObject={}; |
| 47 cyclicObject.self = cyclicObject; |
| 48 tryPostMessage('cyclicObject', false, "cyclicObject"); |
| 49 cyclicArray=[]; |
| 50 cyclicArray[0] = cyclicArray; |
| 51 tryPostMessage('cyclicArray', false, "cyclicArray"); |
| 52 var cyclicObjectGetter = {get self() { return cyclicObjectGetter; }}; |
| 53 tryPostMessage('cyclicObjectGetter', false, 'cyclicObject'); |
| 54 objectGraph = {}; |
| 55 object = {}; |
| 56 objectGraph.graph1 = object; |
| 57 objectGraph.graph2 = object; |
| 58 tryPostMessage('objectGraph', false, "objectGraph"); |
| 59 arrayGraph = [object, object]; |
| 60 tryPostMessage('arrayGraph', false, "arrayGraph"); |
| 61 tryPostMessage('window', true); |
| 62 tryPostMessage('({get a() { throw "x" }})', true); |
| 63 |
| 64 if (window.eventSender) { |
| 65 var fileInput = document.getElementById("fileInput"); |
| 66 var fileRect = fileInput.getClientRects()[0]; |
| 67 var targetX = fileRect.left + fileRect.width / 2; |
| 68 var targetY = fileRect.top + fileRect.height / 2; |
| 69 eventSender.beginDragWithFiles(['resources/blank.html', 'resources/child.htm
l']); |
| 70 eventSender.mouseMoveTo(targetX, targetY); |
| 71 eventSender.mouseUp(); |
| 72 } |
| 73 var imageData = document.createElement("canvas").getContext("2d").createImageDat
a(10,10); |
| 74 for (var i = 0; i < imageData.data.length * 4; i++) |
| 75 imageData.data[i] = i % 256; |
| 76 var mutatedImageData = document.createElement("canvas").getContext("2d").createI
mageData(10,10); |
| 77 for (var i = 0; i < imageData.data.length * 4; i++) |
| 78 mutatedImageData.data[i] = i % 256; |
| 79 tryPostMessage('imageData', false, imageData); |
| 80 tryPostMessage('imageData.data', false, imageData.data) |
| 81 tryPostMessage('mutatedImageData', false, imageData); |
| 82 tryPostMessage('mutatedImageData.data', false, imageData.data) |
| 83 for (var i = 0; i < imageData.data.length * 4; i++) |
| 84 mutatedImageData.data[i] = 0; |
| 85 |
| 86 // Test close() special case for Blob (and File.) |
| 87 |
| 88 var blob = new Blob(["Hello"]); |
| 89 var blobSize = blob.size; |
| 90 tryPostMessage('blob', false, "evalThunk", function(v) { doPassFail(v.size === b
lobSize, "Cloned Blob size equal to the original size."); }); |
| 91 tryPostMessage('blob.close(); blob', true, null, DOMException.DATA_CLONE_ERR); |
| 92 |
| 93 var constructedFile = new File(["Hello"], "test"); |
| 94 var fileSize = constructedFile.size; |
| 95 tryPostMessage('constructedFile', false, "evalThunk", function(v) { doPassFail(v
.size === fileSize, "Cloned File size equal to the original size."); }); |
| 96 tryPostMessage('constructedFile.close(); constructedFile', true, null, DOMExcept
ion.DATA_CLONE_ERR); |
| 97 |
| 98 function thunk(s) { |
| 99 return "(function() {" + s + "})()"; |
| 100 } |
| 101 tryPostMessage(thunk('return 42;'), false, '42'); |
| 102 tryPostMessage(thunk('return 42;'), false, thunk('return 40 + 2;')); |
| 103 tryPostMessage(thunk('return 42;'), false, "evalThunk", |
| 104 function(v) { doPassFail(v == 42, "evalThunk OK"); }) |
| 105 |
| 106 // Only enumerable properties should be serialized. |
| 107 tryPostMessage(thunk('var o = {x:"hello"}; Object.defineProperty(o, "y", {value:
"goodbye"}); return o;'), false, '({x:"hello"})'); |
| 108 |
| 109 // It's unclear what we should do if an accessor modifies an object out from und
er us |
| 110 // while we're serializing it; the standard does mandate certain aspects about e
valuation |
| 111 // order, though, including that properties must be processed in their enumerati
on order. |
| 112 tryPostMessage(thunk( |
| 113 'var a = [0, 1, 2]; ' + |
| 114 'var b = { get x() { a[0] = 40; a[2] = 42; a.push(43); return 41; }}; '
+ |
| 115 'a[1] = b; ' + |
| 116 'return a;' |
| 117 ), false, "evalThunk", function(v) { |
| 118 doPassFail(v.length === 3, "length correct"); // undefined |
| 119 doPassFail(v[0] === 0, "evaluation order OK"); // mandatory |
| 120 doPassFail(v[1].x === 41, "evaluation order OK/accessor reached"); // ma
ndatory |
| 121 doPassFail(v[2] === 42, "evaluation order OK"); // mandatory |
| 122 }); |
| 123 |
| 124 tryPostMessage(thunk( |
| 125 'var a = [0, 1, 2]; ' + |
| 126 'var b = { get x() { a.pop(); return 41; } }; ' + |
| 127 'a[1] = b; ' + |
| 128 'return a;' |
| 129 ), false, "evalThunk", function(v) { |
| 130 doPassFail(v.length === 3 || v.length === 2, "length correct"); // undef
ined |
| 131 doPassFail(v[0] === 0, "index 0 OK"); // mandatory |
| 132 doPassFail(v[1].x === 41, "accessor reached"); // mandatory |
| 133 doPassFail(v[2] === undefined, "index 2 undefined"); // undefined |
| 134 }); |
| 135 |
| 136 tryPostMessage(thunk( |
| 137 'var a = [0, 1, 2]; ' + |
| 138 'var b = { get x() { a.pop(); return 41; } }; ' + |
| 139 'a[2] = b; ' + |
| 140 'return a;' |
| 141 ), false, "evalThunk", function(v) { |
| 142 doPassFail(v.length === 3, "length correct"); // undefined |
| 143 doPassFail(v[0] === 0, "index 0 OK"); // mandatory |
| 144 doPassFail(v[1] === 1, "index 1 OK"); // mandatory |
| 145 doPassFail(v[2].x === 41, "index 2 OK"); // undefined |
| 146 }); |
| 147 |
| 148 // Now with objects! This is a little more tricky because the standard does not |
| 149 // define an enumeration order. |
| 150 tryPostMessage(thunk( |
| 151 'var a = {p0: 0, p1: 1}; ' + |
| 152 'Object.defineProperty(a, "p2", {get:function() {delete a.p3; return 42;
}, enumerable: true, configurable: true}); ' + |
| 153 'Object.defineProperty(a, "p3", {get:function() {delete a.p2; return 43;
}, enumerable: true, configurable: true}); ' + |
| 154 'Object.defineProperty(a, "p4", {get:function() { a.p5 = 45; return 44;
}, enumerable: true, configurable: true}); ' + |
| 155 'return a;' |
| 156 ), false, "evalThunk", function(v) { |
| 157 doPassFail(v.p0 === 0 && v.p1 === 1, "basic properties OK"); // mandator
y |
| 158 doPassFail(v.p2 === undefined && v.p3 !== undefined || |
| 159 v.p2 !== undefined && v.p3 === undefined, "one accessor was r
un"); // mandatory |
| 160 doPassFail(v.p2 !== undefined || Object.getOwnPropertyDescriptor(v, "p2"
) === undefined, "property was removed"); // undefined |
| 161 doPassFail(v.p3 !== undefined || Object.getOwnPropertyDescriptor(v, "p3"
) === undefined, "property was removed"); // undefined |
| 162 doPassFail(v.p4 === 44, "accessor was run"); // mandatory |
| 163 doPassFail(Object.getOwnPropertyDescriptor(v, "p5") === undefined, "dyna
mic property not sent"); // undefined |
| 164 }); |
| 165 |
| 166 // Objects returned from accessors should still be coalesced. |
| 167 tryPostMessage(thunk( |
| 168 'var obja = {get p() { return 42; }}; ' + |
| 169 'var msg = {get a() { return obja; }, b: obja}; ' + |
| 170 'return msg;' |
| 171 ), false, "evalThunk", function(v) { |
| 172 // Interestingly, the standard admits two answers here! |
| 173 doPassFail(v.a === v.b, "reference equality preserved"); |
| 174 doPassFail((v.b.p === 42 && v.a.p === 42) || |
| 175 (v.b.p === null && v.a.p === null), "accessors used"); |
| 176 }); |
| 177 tryPostMessage(thunk( |
| 178 'var obja = {get p() { return 42; }}; ' + |
| 179 'var msg = {a: obja, get b() { return obja; }}; ' + |
| 180 'return msg;' |
| 181 ), false, "evalThunk", function(v) { |
| 182 // Interestingly, the standard admits two answers here! |
| 183 doPassFail(v.a === v.b, "reference equality preserved (opposite order)")
; |
| 184 doPassFail((v.b.p === 42 && v.a.p === 42) || |
| 185 (v.b.p === null && v.a.p === null), "accessors used (opposite
order)"); |
| 186 }); |
| 187 |
| 188 // Tests to verify that accessor and non-accessor properties are |
| 189 // treated equivalently. |
| 190 tryPostMessage(thunk( |
| 191 'var obja = {get p() { return 42; }, q: 43}; ' + |
| 192 'return {get a() { return obja; }};' |
| 193 ), false, "evalThunk", function(v) { |
| 194 doPassFail(v.a.p === 42, "accessor value was not nullified"); |
| 195 doPassFail(v.a.q === 43, "non-accessor value was not nullified"); |
| 196 }); |
| 197 tryPostMessage(thunk( |
| 198 'var objb = {get r() { return 44; }, t: 45}; ' + |
| 199 'var obja = {get p() { return 42; }, q: 43, s: objb}; ' + |
| 200 'return {get a() { return obja; }};' |
| 201 ), false, "evalThunk", function(v) { |
| 202 doPassFail(v.a.p === 42, "accessor value was not nullified"); |
| 203 doPassFail(v.a.q === 43, "non-accessor value was not nullified"); |
| 204 doPassFail(v.s !== null, "non-accessor value was not nullified"); |
| 205 doPassFail(v.a.s.r === 44, "accessor value was not nullified"); |
| 206 doPassFail(v.a.s.t === 45, "non-accessor value was not nullified"); |
| 207 }); |
| 208 tryPostMessage(thunk( |
| 209 'var objb = {get r() { return 44; }, t: 45}; ' + |
| 210 'var obja = {get p() { return 42; }, q: 43, s: [objb]}; ' + |
| 211 'return {get c() { return 47; }, get a() { return obja; }, get b() { ret
urn 46; } };' |
| 212 ), false, "evalThunk", function(v) { |
| 213 doPassFail(v.b === 46, "accessor value was not nullified"); |
| 214 doPassFail(v.c === 47, "accessor value was not nullified"); |
| 215 doPassFail(v.a.p === 42, "accessor value was not nullified"); |
| 216 doPassFail(v.a.q === 43, "non-accessor value was not nullified"); |
| 217 doPassFail(v.a.s !== null, "non-accessor value was not nullified"); |
| 218 doPassFail(v.a.s !== undefined, "non-accessor value is defined"); |
| 219 doPassFail(v.a.s[0] !== null, "non-accessor value was not nullified"); |
| 220 doPassFail(v.a.s[0] !== undefined, "non-accessor value is defined"); |
| 221 doPassFail(v.a.s[0].r === 44, "accessor value was not nullified"); |
| 222 doPassFail(v.a.s[0].t === 45, "non-accessor value was not nullified"); |
| 223 }); |
| 224 |
| 225 // We need to pass out the exception raised from internal accessors. |
| 226 tryPostMessage(thunk( |
| 227 'return {get a() { throw "accessor-exn"; }};' |
| 228 ), true, null, 'accessor-exn'); |
| 229 tryPostMessage(thunk( |
| 230 'var obja = {get p() { throw "accessor-exn"; }}; ' + |
| 231 'return {get a() { return obja; }};' |
| 232 ), true, null, 'accessor-exn'); |
| 233 tryPostMessage(thunk( |
| 234 'window.bcalled = undefined; ' + |
| 235 'window.acalled = undefined; ' + |
| 236 'window.pcalled = undefined; ' + |
| 237 'var objb = {get b() { window.bcalled = true; return 42; }}; ' + |
| 238 'var obja = {get a() { window.acalled = true; return objb; }}; ' + |
| 239 'return { get p() { window.pcalled = true; return obja; }};' |
| 240 ), false, "evalThunk", function(v) { |
| 241 doPassFail(v.p.a.b === 42, "accessor value was not nullified"); |
| 242 doPassFail(window.pcalled === true, "window.pcalled === true"); |
| 243 doPassFail(window.acalled === true, "window.acalled === true"); |
| 244 doPassFail(window.bcalled === true, "window.bcalled === true"); |
| 245 }); |
| 246 |
| 247 // Reference equality between Boolean objects must be maintained. |
| 248 tryPostMessage(thunk( |
| 249 'var t1 = new Boolean(true); ' + |
| 250 'var t2 = new Boolean(true); ' + |
| 251 'var f1 = new Boolean(false); ' + |
| 252 'var f2 = new Boolean(false); ' + |
| 253 'return [t1, t1, t2, f1, f1, f2];' |
| 254 ), false, "evalThunk", function(v) { |
| 255 doPassFail(equal(v[0], new Boolean(true)), "Boolean values correct (0)")
; |
| 256 doPassFail(equal(v[3], new Boolean(false)), "Boolean values correct (3)"
); |
| 257 doPassFail(equal(v[1], v[2]), "Boolean values correct (1,2)"); |
| 258 doPassFail(equal(v[4], v[5]), "Boolean values correct (4,5)"); |
| 259 doPassFail(v[0] === v[1], "References to Booleans correct (0,1)"); |
| 260 doPassFail(v[3] === v[4], "References to Booleans correct (3,4)"); |
| 261 doPassFail(v[0] !== v[2], "References to Booleans correct (0,2)"); |
| 262 doPassFail(v[3] !== v[5], "References to Booleans correct (3,5)"); |
| 263 }); |
| 264 |
| 265 // Reference equality between Number objects must be maintained. |
| 266 tryPostMessage(thunk( |
| 267 'var n1 = new Number(42.0); ' + |
| 268 'var n2 = new Number(42.0); ' + |
| 269 'return [n1, n1, n2];' |
| 270 ), false, "evalThunk", function(v) { |
| 271 doPassFail(equal(v[0], new Number(42.0)), "Number values correct (0)"); |
| 272 doPassFail(equal(v[0], v[2]), "Number values correct (0,2)"); |
| 273 doPassFail(v[0] === v[1], "References to numbers correct (0,1)"); |
| 274 doPassFail(v[0] !== v[2], "References to numbers correct (0,2)"); |
| 275 }); |
| 276 |
| 277 // Reference equality between String objects must be maintained. |
| 278 tryPostMessage(thunk( |
| 279 'var s1 = new String("gnirts"); ' + |
| 280 'var s2 = new String("gnirts"); ' + |
| 281 'return [s1, s1, s2];' |
| 282 ), false, "evalThunk", function(v) { |
| 283 doPassFail(equal(v[0], new String("gnirts")), "String values correct (0)
"); |
| 284 doPassFail(equal(v[0], v[2]), "String values correct (0,2)"); |
| 285 doPassFail(v[0] === v[1], "References to strings correct (0,1)"); |
| 286 doPassFail(v[0] !== v[2], "References to strings correct (0,2)"); |
| 287 }); |
| 288 |
| 289 // Properties added to String, Boolean and Number objects should not be serializ
ed. |
| 290 tryPostMessage(thunk( |
| 291 'var s = new String("gnirts"); ' + |
| 292 'var n = new Number(42.0); ' + |
| 293 'var b = new Boolean(true); ' + |
| 294 's.foo = 1; n.foo = 2; b.foo = 3; ' + |
| 295 'return [s, n, b];' |
| 296 ), false, "evalThunk", function(v) { |
| 297 doPassFail(v[0].foo == undefined, "String object properties not serializ
ed"); |
| 298 doPassFail(v[1].foo == undefined, "Number object properties not serializ
ed"); |
| 299 doPassFail(v[2].foo == undefined, "Boolean object properties not seriali
zed"); |
| 300 }); |
| 301 |
| 302 // Reference equality between dates must be maintained. |
| 303 tryPostMessage(thunk( |
| 304 'var d1 = new Date(1,2,3); ' + |
| 305 'var d2 = new Date(1,2,3); ' + |
| 306 'return [d1,d1,d2];' |
| 307 ), false, "evalThunk", function(v) { |
| 308 doPassFail(equal(v[0], new Date(1,2,3)), "Date values correct (0)"); |
| 309 doPassFail(equal(v[0], v[2]), "Date values correct (1)"); |
| 310 doPassFail(v[0] === v[1], "References to dates correct (0)"); |
| 311 doPassFail(v[0] !== v[2], "References to dates correct (1)"); |
| 312 }); |
| 313 |
| 314 // Reference equality between regexps must be preserved. |
| 315 tryPostMessage(thunk( |
| 316 'var rx1 = new RegExp("foo"); ' + |
| 317 'var rx2 = new RegExp("foo"); ' + |
| 318 'var rx3 = new RegExp("foo", "gim"); ' + |
| 319 'rx3.exec("foofoofoo"); ' + |
| 320 'doPassFail(rx3.lastIndex === 3, "lastIndex initially correct: was " + r
x3.lastIndex); ' + |
| 321 'return [rx1,rx1,rx2,rx3];' |
| 322 ), false, "evalThunk", function(v) { |
| 323 doPassFail(v[0].source === "foo", "Regexp value correct (0)"); |
| 324 doPassFail(v[0] === v[1], "References to regexps correct (0)"); |
| 325 doPassFail(v[0] !== v[2], "References to regexps correct (1)"); |
| 326 doPassFail(v[0].global === false, "global set (0)"); |
| 327 doPassFail(v[3].global === true, "global set (1)"); |
| 328 doPassFail(v[0].ignoreCase === false, "ignoreCase set (0)"); |
| 329 doPassFail(v[3].ignoreCase === true, "ignoreCase set (1)"); |
| 330 doPassFail(v[0].multiline === false, "multiline set (0)"); |
| 331 doPassFail(v[3].multiline === true, "multiline set (1)"); |
| 332 doPassFail(v[0].lastIndex === 0, "lastIndex correct (0)"); |
| 333 doPassFail(v[3].lastIndex === 0, "lastIndex correct (1)"); |
| 334 }); |
| 335 |
| 336 if (window.eventSender) { |
| 337 tryPostMessage(thunk( |
| 338 'window.fileList = fileInput.files; ' + |
| 339 'window.file0 = fileList[0]; ' + |
| 340 'window.file1 = fileList[1]; ' + |
| 341 'return window.fileList.length'), false, 2); |
| 342 doPassFail(window.fileList[0] === window.file0, "sanity on file reference eq
uality") |
| 343 // The standard mandates that we do _not_ maintain reference equality for fi
les in a transferred FileList. |
| 344 tryPostMessage(thunk('return [window.file0, window.file0];' |
| 345 ), false, "evalThunk", function(v) { doPassFail(v[0] === v[1], "file ref
erences transfer")}); |
| 346 tryPostMessage(thunk('return [window.fileList, window.file0];' |
| 347 ), false, "evalThunk", function(v) { doPassFail(v[0][0] !== v[1], "FileL
ist should not respect reference equality")}); |
| 348 tryPostMessage(thunk('return [window.file0, window.fileList];' |
| 349 ), false, "evalThunk", function(v) { doPassFail(v[1][0] !== v[0], "FileL
ist should not respect reference equality")}); |
| 350 tryPostMessage(thunk('return [window.fileList, window.fileList];' |
| 351 ), false, "evalThunk", function(v) { doPassFail(v[0] === v[1], "FileList
respects self-reference equality")}); |
| 352 tryPostMessage(thunk('return [window.fileList, window.file0, window.file1]' |
| 353 ), false, "evalThunk", function(v) { |
| 354 doPassFail(v[0].length === window.fileList.length, "FileList length
sent correctly"); |
| 355 doPassFail(v[0][0] !== v[1], "FileList should not respect reference
equality (0)"); |
| 356 doPassFail(v[0][1] !== v[2], "FileList should not respect reference
equality (1)"); |
| 357 doPassFail(v[0][0].name == window.file0.name, "FileList preserves or
der and data (name0)"); |
| 358 doPassFail(v[0][1].name == window.file1.name, "FileList preserves or
der and data (name1)"); |
| 359 doPassFail(equal(v[0][0].lastModifiedDate, window.file0.lastModified
Date), "FileList preserves order and data (date0)"); |
| 360 doPassFail(equal(v[0][1].lastModifiedDate, window.file1.lastModified
Date), "FileList preserves order and data (date1)"); |
| 361 }); |
| 362 } |
| 363 tryPostMessage('"done"'); |
| 364 </script> |
| 9 </body> | 365 </body> |
| 10 </html> | 366 </html> |
| OLD | NEW |