|
1 <!DOCTYPE HTML> |
|
2 <html> |
|
3 <head> |
|
4 <!-- |
|
5 https://bugzilla.mozilla.org/show_bug.cgi?id=414796 |
|
6 --> |
|
7 <title>Test for Bug 414796</title> |
|
8 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> |
|
9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> |
|
10 </head> |
|
11 |
|
12 <body> |
|
13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=414796">Mozilla Bug 414796</a> |
|
14 <p id="display"> |
|
15 <input id="fileList" type="file"></input> |
|
16 </p> |
|
17 <div id="content" style="display: none"> |
|
18 </div> |
|
19 |
|
20 <pre id="test"> |
|
21 <script class="testbody" type="text/javascript"> |
|
22 |
|
23 // File constructors should not work from non-chrome code |
|
24 try { |
|
25 var file = File("/etc/passwd"); |
|
26 ok(false, "Did not throw on unprivileged attempt to construct a File"); |
|
27 } catch (e) { |
|
28 ok(true, "Threw on an unprivileged attempt to construct a File"); |
|
29 } |
|
30 |
|
31 const minFileSize = 20000; |
|
32 var fileNum = 1; |
|
33 var testRanCounter = 0; |
|
34 var expectedTestCount = 0; |
|
35 SimpleTest.waitForExplicitFinish(); |
|
36 |
|
37 is(FileReader.EMPTY, 0, "correct EMPTY value"); |
|
38 is(FileReader.LOADING, 1, "correct LOADING value"); |
|
39 is(FileReader.DONE, 2, "correct DONE value"); |
|
40 |
|
41 // Create strings containing data we'll test with. We'll want long |
|
42 // strings to ensure they span multiple buffers while loading |
|
43 var testTextData = "asd b\tlah\u1234w\u00a0r"; |
|
44 while (testTextData.length < minFileSize) { |
|
45 testTextData = testTextData + testTextData; |
|
46 } |
|
47 |
|
48 var testASCIIData = "abcdef 123456\n"; |
|
49 while (testASCIIData.length < minFileSize) { |
|
50 testASCIIData = testASCIIData + testASCIIData; |
|
51 } |
|
52 |
|
53 var testBinaryData = ""; |
|
54 for (var i = 0; i < 256; i++) { |
|
55 testBinaryData += String.fromCharCode(i); |
|
56 } |
|
57 while (testBinaryData.length < minFileSize) { |
|
58 testBinaryData = testBinaryData + testBinaryData; |
|
59 } |
|
60 |
|
61 |
|
62 //Set up files for testing |
|
63 var asciiFile = createFileWithData(testASCIIData); |
|
64 var binaryFile = createFileWithData(testBinaryData); |
|
65 |
|
66 var fileList = document.getElementById('fileList'); |
|
67 SpecialPowers.wrap(fileList).value = "/none/existing/path/fileAPI/testing"; |
|
68 var nonExistingFile = fileList.files[0]; |
|
69 |
|
70 // Test that plain reading works and fires events as expected, both |
|
71 // for text and binary reading |
|
72 |
|
73 var onloadHasRunText = false; |
|
74 var onloadStartHasRunText = false; |
|
75 r = new FileReader(); |
|
76 is(r.readyState, FileReader.EMPTY, "correct initial text readyState"); |
|
77 r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "plain reading"); |
|
78 r.addEventListener("load", function() { onloadHasRunText = true }, false); |
|
79 r.addEventListener("loadstart", function() { onloadStartHasRunText = true }, false); |
|
80 r.readAsText(asciiFile); |
|
81 is(r.readyState, FileReader.LOADING, "correct loading text readyState"); |
|
82 is(onloadHasRunText, false, "text loading must be async"); |
|
83 is(onloadStartHasRunText, true, "text loadstart should fire sync"); |
|
84 expectedTestCount++; |
|
85 |
|
86 var onloadHasRunBinary = false; |
|
87 var onloadStartHasRunBinary = false; |
|
88 r = new FileReader(); |
|
89 is(r.readyState, FileReader.EMPTY, "correct initial binary readyState"); |
|
90 r.addEventListener("load", function() { onloadHasRunBinary = true }, false); |
|
91 r.addEventListener("loadstart", function() { onloadStartHasRunBinary = true }, false); |
|
92 r.readAsBinaryString(binaryFile); |
|
93 r.onload = getLoadHandler(testBinaryData, testBinaryData.length, "binary reading"); |
|
94 is(r.readyState, FileReader.LOADING, "correct loading binary readyState"); |
|
95 is(onloadHasRunBinary, false, "binary loading must be async"); |
|
96 is(onloadStartHasRunBinary, true, "binary loadstart should fire sync"); |
|
97 expectedTestCount++; |
|
98 |
|
99 var onloadHasRunArrayBuffer = false; |
|
100 var onloadStartHasRunArrayBuffer = false; |
|
101 r = new FileReader(); |
|
102 is(r.readyState, FileReader.EMPTY, "correct initial arrayBuffer readyState"); |
|
103 r.addEventListener("load", function() { onloadHasRunArrayBuffer = true }, false); |
|
104 r.addEventListener("loadstart", function() { onloadStartHasRunArrayBuffer = true }, false); |
|
105 r.readAsArrayBuffer(binaryFile); |
|
106 r.onload = getLoadHandlerForArrayBuffer(testBinaryData, testBinaryData.length, "array buffer reading"); |
|
107 is(r.readyState, FileReader.LOADING, "correct loading arrayBuffer readyState"); |
|
108 is(onloadHasRunArrayBuffer, false, "arrayBuffer loading must be async"); |
|
109 is(onloadStartHasRunArrayBuffer, true, "arrayBuffer loadstart should fire sync"); |
|
110 expectedTestCount++; |
|
111 |
|
112 // Test a variety of encodings, and make sure they work properly |
|
113 r = new FileReader(); |
|
114 r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "no encoding reading"); |
|
115 r.readAsText(asciiFile, ""); |
|
116 expectedTestCount++; |
|
117 |
|
118 r = new FileReader(); |
|
119 r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "iso8859 reading"); |
|
120 r.readAsText(asciiFile, "iso-8859-1"); |
|
121 expectedTestCount++; |
|
122 |
|
123 r = new FileReader(); |
|
124 r.onload = getLoadHandler(testTextData, |
|
125 convertToUTF8(testTextData).length, |
|
126 "utf8 reading"); |
|
127 r.readAsText(createFileWithData(convertToUTF8(testTextData)), "utf8"); |
|
128 expectedTestCount++; |
|
129 |
|
130 r = new FileReader(); |
|
131 r.readAsText(createFileWithData(convertToUTF16(testTextData)), "utf-16"); |
|
132 r.onload = getLoadHandler(testTextData, |
|
133 convertToUTF16(testTextData).length, |
|
134 "utf16 reading"); |
|
135 expectedTestCount++; |
|
136 |
|
137 // Test get result without reading |
|
138 r = new FileReader(); |
|
139 is(r.readyState, FileReader.EMPTY, |
|
140 "readyState in test reader get result without reading"); |
|
141 is(r.error, null, |
|
142 "no error in test reader get result without reading"); |
|
143 is(r.result, null, |
|
144 "result in test reader get result without reading"); |
|
145 |
|
146 // Test loading an empty file works (and doesn't crash!) |
|
147 var emptyFile = createFileWithData(""); |
|
148 dump("hello nurse"); |
|
149 r = new FileReader(); |
|
150 r.onload = getLoadHandler("", 0, "empty no encoding reading"); |
|
151 r.readAsText(emptyFile, ""); |
|
152 expectedTestCount++; |
|
153 |
|
154 r = new FileReader(); |
|
155 r.onload = getLoadHandler("", 0, "empty utf8 reading"); |
|
156 r.readAsText(emptyFile, "utf8"); |
|
157 expectedTestCount++; |
|
158 |
|
159 r = new FileReader(); |
|
160 r.onload = getLoadHandler("", 0, "empty utf16 reading"); |
|
161 r.readAsText(emptyFile, "utf-16"); |
|
162 expectedTestCount++; |
|
163 |
|
164 r = new FileReader(); |
|
165 r.onload = getLoadHandler("", 0, "empty binary string reading"); |
|
166 r.readAsBinaryString(emptyFile); |
|
167 expectedTestCount++; |
|
168 |
|
169 r = new FileReader(); |
|
170 r.onload = getLoadHandlerForArrayBuffer("", 0, "empty array buffer reading"); |
|
171 r.readAsArrayBuffer(emptyFile); |
|
172 expectedTestCount++; |
|
173 |
|
174 r = new FileReader(); |
|
175 r.onload = getLoadHandler(convertToDataURL(""), 0, "empt binary string reading"); |
|
176 r.readAsDataURL(emptyFile); |
|
177 expectedTestCount++; |
|
178 |
|
179 // Test reusing a FileReader to read multiple times |
|
180 r = new FileReader(); |
|
181 r.onload = getLoadHandler(testASCIIData, |
|
182 testASCIIData.length, |
|
183 "to-be-reused reading text") |
|
184 var makeAnotherReadListener = function(event) { |
|
185 r = event.target; |
|
186 r.removeEventListener("load", makeAnotherReadListener, false); |
|
187 r.onload = getLoadHandler(testASCIIData, |
|
188 testASCIIData.length, |
|
189 "reused reading text"); |
|
190 r.readAsText(asciiFile); |
|
191 }; |
|
192 r.addEventListener("load", makeAnotherReadListener, false); |
|
193 r.readAsText(asciiFile); |
|
194 expectedTestCount += 2; |
|
195 |
|
196 r = new FileReader(); |
|
197 r.onload = getLoadHandler(testBinaryData, |
|
198 testBinaryData.length, |
|
199 "to-be-reused reading binary") |
|
200 var makeAnotherReadListener2 = function(event) { |
|
201 r = event.target; |
|
202 r.removeEventListener("load", makeAnotherReadListener2, false); |
|
203 r.onload = getLoadHandler(testBinaryData, |
|
204 testBinaryData.length, |
|
205 "reused reading binary"); |
|
206 r.readAsBinaryString(binaryFile); |
|
207 }; |
|
208 r.addEventListener("load", makeAnotherReadListener2, false); |
|
209 r.readAsBinaryString(binaryFile); |
|
210 expectedTestCount += 2; |
|
211 |
|
212 r = new FileReader(); |
|
213 r.onload = getLoadHandler(convertToDataURL(testBinaryData), |
|
214 testBinaryData.length, |
|
215 "to-be-reused reading data url") |
|
216 var makeAnotherReadListener3 = function(event) { |
|
217 r = event.target; |
|
218 r.removeEventListener("load", makeAnotherReadListener3, false); |
|
219 r.onload = getLoadHandler(convertToDataURL(testBinaryData), |
|
220 testBinaryData.length, |
|
221 "reused reading data url"); |
|
222 r.readAsDataURL(binaryFile); |
|
223 }; |
|
224 r.addEventListener("load", makeAnotherReadListener3, false); |
|
225 r.readAsDataURL(binaryFile); |
|
226 expectedTestCount += 2; |
|
227 |
|
228 r = new FileReader(); |
|
229 r.onload = getLoadHandlerForArrayBuffer(testBinaryData, |
|
230 testBinaryData.length, |
|
231 "to-be-reused reading arrayBuffer") |
|
232 var makeAnotherReadListener4 = function(event) { |
|
233 r = event.target; |
|
234 r.removeEventListener("load", makeAnotherReadListener4, false); |
|
235 r.onload = getLoadHandlerForArrayBuffer(testBinaryData, |
|
236 testBinaryData.length, |
|
237 "reused reading arrayBuffer"); |
|
238 r.readAsArrayBuffer(binaryFile); |
|
239 }; |
|
240 r.addEventListener("load", makeAnotherReadListener4, false); |
|
241 r.readAsArrayBuffer(binaryFile); |
|
242 expectedTestCount += 2; |
|
243 |
|
244 // Test first reading as ArrayBuffer then read as something else |
|
245 // (BinaryString) and doesn't crash |
|
246 r = new FileReader(); |
|
247 r.onload = getLoadHandlerForArrayBuffer(testBinaryData, |
|
248 testBinaryData.length, |
|
249 "to-be-reused reading arrayBuffer") |
|
250 var makeAnotherReadListener5 = function(event) { |
|
251 r = event.target; |
|
252 r.removeEventListener("load", makeAnotherReadListener5, false); |
|
253 r.onload = getLoadHandler(testBinaryData, |
|
254 testBinaryData.length, |
|
255 "reused reading binary string"); |
|
256 r.readAsBinaryString(binaryFile); |
|
257 }; |
|
258 r.addEventListener("load", makeAnotherReadListener5, false); |
|
259 r.readAsArrayBuffer(binaryFile); |
|
260 expectedTestCount += 2; |
|
261 |
|
262 //Test data-URI encoding on differing file sizes |
|
263 dataurldata = testBinaryData.substr(0, testBinaryData.length - |
|
264 testBinaryData.length % 3); |
|
265 is(dataurldata.length % 3, 0, "Want to test data with length % 3 == 0"); |
|
266 r = new FileReader(); |
|
267 r.onload = getLoadHandler(convertToDataURL(dataurldata), |
|
268 dataurldata.length, |
|
269 "dataurl reading, %3 = 0"); |
|
270 r.readAsDataURL(createFileWithData(dataurldata)); |
|
271 expectedTestCount++; |
|
272 |
|
273 dataurldata = testBinaryData.substr(0, testBinaryData.length - 2 - |
|
274 testBinaryData.length % 3); |
|
275 is(dataurldata.length % 3, 1, "Want to test data with length % 3 == 1"); |
|
276 r = new FileReader(); |
|
277 r.onload = getLoadHandler(convertToDataURL(dataurldata), |
|
278 dataurldata.length, |
|
279 "dataurl reading, %3 = 1"); |
|
280 r.readAsDataURL(createFileWithData(dataurldata)); |
|
281 expectedTestCount++; |
|
282 |
|
283 dataurldata = testBinaryData.substr(0, testBinaryData.length - 1 - |
|
284 testBinaryData.length % 3); |
|
285 is(dataurldata.length % 3, 2, "Want to test data with length % 3 == 2"); |
|
286 r = new FileReader(); |
|
287 r.onload = getLoadHandler(convertToDataURL(dataurldata), |
|
288 dataurldata.length, |
|
289 "dataurl reading, %3 = 2"); |
|
290 r.readAsDataURL(createFileWithData(dataurldata)); |
|
291 expectedTestCount++; |
|
292 |
|
293 |
|
294 // Test abort() |
|
295 var abortHasRun = false; |
|
296 var loadEndHasRun = false; |
|
297 r = new FileReader(); |
|
298 r.onabort = function (event) { |
|
299 is(abortHasRun, false, "abort should only fire once"); |
|
300 is(loadEndHasRun, false, "loadend shouldn't have fired yet"); |
|
301 abortHasRun = true; |
|
302 is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); |
|
303 is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); |
|
304 is(event.target.result, null, "file data should be null on aborted reads"); |
|
305 } |
|
306 r.onloadend = function (event) { |
|
307 is(abortHasRun, true, "abort should fire before loadend"); |
|
308 is(loadEndHasRun, false, "loadend should only fire once"); |
|
309 loadEndHasRun = true; |
|
310 is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); |
|
311 is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); |
|
312 is(event.target.result, null, "file data should be null on aborted reads"); |
|
313 } |
|
314 r.onload = function() { ok(false, "load should not fire for aborted reads") }; |
|
315 r.onerror = function() { ok(false, "error should not fire for aborted reads") }; |
|
316 r.onprogress = function() { ok(false, "progress should not fire for aborted reads") }; |
|
317 var abortThrew = false; |
|
318 try { |
|
319 r.abort(); |
|
320 } catch(e) { |
|
321 abortThrew = true; |
|
322 } |
|
323 is(abortThrew, true, "abort() must throw if not loading"); |
|
324 is(abortHasRun, false, "abort() is a no-op unless loading"); |
|
325 r.readAsText(asciiFile); |
|
326 r.abort(); |
|
327 is(abortHasRun, true, "abort should fire sync"); |
|
328 is(loadEndHasRun, true, "loadend should fire sync"); |
|
329 |
|
330 // Test calling readAsX to cause abort() |
|
331 var reuseAbortHasRun = false; |
|
332 r = new FileReader(); |
|
333 r.onabort = function (event) { |
|
334 is(reuseAbortHasRun, false, "abort should only fire once"); |
|
335 reuseAbortHasRun = true; |
|
336 is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); |
|
337 is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); |
|
338 is(event.target.result, null, "file data should be null on aborted reads"); |
|
339 } |
|
340 r.onload = function() { ok(false, "load should not fire for aborted reads") }; |
|
341 var abortThrew = false; |
|
342 try { |
|
343 r.abort(); |
|
344 } catch(e) { |
|
345 abortThrew = true; |
|
346 } |
|
347 is(abortThrew, true, "abort() must throw if not loading"); |
|
348 is(reuseAbortHasRun, false, "abort() is a no-op unless loading"); |
|
349 r.readAsText(asciiFile); |
|
350 r.readAsText(asciiFile); |
|
351 is(reuseAbortHasRun, true, "abort should fire sync"); |
|
352 r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "reuse-as-abort reading"); |
|
353 expectedTestCount++; |
|
354 |
|
355 |
|
356 // Test reading from nonexistent files |
|
357 r = new FileReader(); |
|
358 var didThrow = false; |
|
359 try { |
|
360 r.readAsDataURL(nonExistingFile); |
|
361 } catch(ex) { |
|
362 didThrow = true; |
|
363 } |
|
364 // Once this test passes, we should test that onerror gets called and |
|
365 // that the FileReader object is in the right state during that call. |
|
366 todo(!didThrow, "shouldn't throw when opening nonexistent file, should fire error instead"); |
|
367 |
|
368 |
|
369 function getLoadHandler(expectedResult, expectedLength, testName) { |
|
370 return function (event) { |
|
371 is(event.target.readyState, FileReader.DONE, |
|
372 "readyState in test " + testName); |
|
373 is(event.target.error, null, |
|
374 "no error in test " + testName); |
|
375 is(event.target.result, expectedResult, |
|
376 "result in test " + testName); |
|
377 is(event.lengthComputable, true, |
|
378 "lengthComputable in test " + testName); |
|
379 is(event.loaded, expectedLength, |
|
380 "loaded in test " + testName); |
|
381 is(event.total, expectedLength, |
|
382 "total in test " + testName); |
|
383 testHasRun(); |
|
384 } |
|
385 } |
|
386 |
|
387 function getLoadHandlerForArrayBuffer(expectedResult, expectedLength, testName) { |
|
388 return function (event) { |
|
389 is(event.target.readyState, FileReader.DONE, |
|
390 "readyState in test " + testName); |
|
391 is(event.target.error, null, |
|
392 "no error in test " + testName); |
|
393 is(event.lengthComputable, true, |
|
394 "lengthComputable in test " + testName); |
|
395 is(event.loaded, expectedLength, |
|
396 "loaded in test " + testName); |
|
397 is(event.total, expectedLength, |
|
398 "total in test " + testName); |
|
399 is(event.target.result.byteLength, expectedLength, |
|
400 "array buffer size in test " + testName); |
|
401 var u8v = new Uint8Array(event.target.result); |
|
402 is(String.fromCharCode.apply(String, u8v), expectedResult, |
|
403 "array buffer contents in test " + testName); |
|
404 u8v = null; |
|
405 SpecialPowers.gc(); |
|
406 is(event.target.result.byteLength, expectedLength, |
|
407 "array buffer size after gc in test " + testName); |
|
408 u8v = new Uint8Array(event.target.result); |
|
409 is(String.fromCharCode.apply(String, u8v), expectedResult, |
|
410 "array buffer contents after gc in test " + testName); |
|
411 testHasRun(); |
|
412 } |
|
413 } |
|
414 |
|
415 function testHasRun() { |
|
416 //alert(testRanCounter); |
|
417 ++testRanCounter; |
|
418 if (testRanCounter == expectedTestCount) { |
|
419 is(onloadHasRunText, true, "onload text should have fired by now"); |
|
420 is(onloadHasRunBinary, true, "onload binary should have fired by now"); |
|
421 SimpleTest.finish(); |
|
422 } |
|
423 } |
|
424 |
|
425 function createFileWithData(fileData) { |
|
426 var dirSvc = SpecialPowers.Cc["@mozilla.org/file/directory_service;1"].getService(SpecialPowers.Ci.nsIProperties); |
|
427 var testFile = dirSvc.get("ProfD", SpecialPowers.Ci.nsIFile); |
|
428 testFile.append("fileAPItestfile" + fileNum); |
|
429 fileNum++; |
|
430 var outStream = SpecialPowers.Cc["@mozilla.org/network/file-output-stream;1"].createInstance(SpecialPowers.Ci.nsIFileOutputStream); |
|
431 outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate |
|
432 0666, 0); |
|
433 outStream.write(fileData, fileData.length); |
|
434 outStream.close(); |
|
435 |
|
436 var fileList = document.getElementById('fileList'); |
|
437 SpecialPowers.wrap(fileList).value = testFile.path; |
|
438 |
|
439 return fileList.files[0]; |
|
440 } |
|
441 |
|
442 function convertToUTF16(s) { |
|
443 res = ""; |
|
444 for (var i = 0; i < s.length; ++i) { |
|
445 c = s.charCodeAt(i); |
|
446 res += String.fromCharCode(c & 255, c >>> 8); |
|
447 } |
|
448 return res; |
|
449 } |
|
450 |
|
451 function convertToUTF8(s) { |
|
452 return unescape(encodeURIComponent(s)); |
|
453 } |
|
454 |
|
455 function convertToDataURL(s) { |
|
456 return "data:application/octet-stream;base64," + btoa(s); |
|
457 } |
|
458 |
|
459 </script> |
|
460 </pre> |
|
461 </body> </html> |