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