summaryrefslogtreecommitdiffstats
path: root/dom/base/test/test_fileapi.html
blob: fc33c7ae6c046b677607e348fa56fb22e61d5162 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
<!DOCTYPE HTML>
<html>
<head>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=414796
-->
  <title>Test for Bug 414796</title>
  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>

<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=414796">Mozilla Bug 414796</a>
<p id="display">
</p>
<div id="content" style="display: none">
</div>

<pre id="test">
<script class="testbody" type="text/javascript;version=1.7">

// File constructors should not work from non-chrome code
try {
  var file = File("/etc/passwd");
  ok(false, "Did not throw on unprivileged attempt to construct a File");
} catch (e) {
  ok(true, "Threw on an unprivileged attempt to construct a File");
}

const minFileSize = 20000;
var testRanCounter = 0;
var expectedTestCount = 0;
var testSetupFinished = false;
SimpleTest.waitForExplicitFinish();

is(FileReader.EMPTY, 0, "correct EMPTY value");
is(FileReader.LOADING, 1, "correct LOADING value");
is(FileReader.DONE, 2, "correct DONE value");

// Create strings containing data we'll test with. We'll want long
// strings to ensure they span multiple buffers while loading
var testTextData = "asd b\tlah\u1234w\u00a0r";
while (testTextData.length < minFileSize) {
  testTextData = testTextData + testTextData;
}

var testASCIIData = "abcdef 123456\n";
while (testASCIIData.length < minFileSize) {
  testASCIIData = testASCIIData + testASCIIData;
}

var testBinaryData = "";
for (var i = 0; i < 256; i++) {
  testBinaryData += String.fromCharCode(i);
}
while (testBinaryData.length < minFileSize) {
  testBinaryData = testBinaryData + testBinaryData;
}

var dataurldata0 = testBinaryData.substr(0, testBinaryData.length -
					 testBinaryData.length % 3);
var dataurldata1 = testBinaryData.substr(0, testBinaryData.length - 2 -
					 testBinaryData.length % 3);
var dataurldata2 = testBinaryData.substr(0, testBinaryData.length - 1 -
					 testBinaryData.length % 3);


//Set up files for testing
var openerURL = SimpleTest.getTestFileURL("fileapi_chromeScript.js");
var opener = SpecialPowers.loadChromeScript(openerURL);
opener.addMessageListener("files.opened", onFilesOpened);
opener.sendAsyncMessage("files.open", [
  testASCIIData,
  testBinaryData,
  null,
  convertToUTF8(testTextData),
  convertToUTF16(testTextData),
  "",
  dataurldata0,
  dataurldata1,
  dataurldata2,
]);

function onFilesOpened(message) {
  let [
    asciiFile,
    binaryFile,
    nonExistingFile,
    utf8TextFile,
    utf16TextFile,
    emptyFile,
    dataUrlFile0,
    dataUrlFile1,
    dataUrlFile2,
  ] = message;

  // Test that plain reading works and fires events as expected, both
  // for text and binary reading

  var onloadHasRunText = false;
  var onloadStartHasRunText = false;
  r = new FileReader();
  is(r.readyState, FileReader.EMPTY, "correct initial text readyState");
  r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "plain reading");
  r.addEventListener("load", function() { onloadHasRunText = true }, false);
  r.addEventListener("loadstart", function() { onloadStartHasRunText = true }, false);
  r.readAsText(asciiFile);
  is(r.readyState, FileReader.LOADING, "correct loading text readyState");
  is(onloadHasRunText, false, "text loading must be async");
  is(onloadStartHasRunText, true, "text loadstart should fire sync");
  expectedTestCount++;

  var onloadHasRunBinary = false;
  var onloadStartHasRunBinary = false;
  r = new FileReader();
  is(r.readyState, FileReader.EMPTY, "correct initial binary readyState");
  r.addEventListener("load", function() { onloadHasRunBinary = true }, false);
  r.addEventListener("loadstart", function() { onloadStartHasRunBinary = true }, false);
  r.readAsBinaryString(binaryFile);
  r.onload = getLoadHandler(testBinaryData, testBinaryData.length, "binary reading");
  is(r.readyState, FileReader.LOADING, "correct loading binary readyState");
  is(onloadHasRunBinary, false, "binary loading must be async");
  is(onloadStartHasRunBinary, true, "binary loadstart should fire sync");
  expectedTestCount++;

  var onloadHasRunArrayBuffer = false;
  var onloadStartHasRunArrayBuffer = false;
  r = new FileReader();
  is(r.readyState, FileReader.EMPTY, "correct initial arrayBuffer readyState");
  r.addEventListener("load", function() { onloadHasRunArrayBuffer = true }, false);
  r.addEventListener("loadstart", function() { onloadStartHasRunArrayBuffer = true }, false);
  r.readAsArrayBuffer(binaryFile);
  r.onload = getLoadHandlerForArrayBuffer(testBinaryData, testBinaryData.length, "array buffer reading");
  is(r.readyState, FileReader.LOADING, "correct loading arrayBuffer readyState");
  is(onloadHasRunArrayBuffer, false, "arrayBuffer loading must be async");
  is(onloadStartHasRunArrayBuffer, true, "arrayBuffer loadstart should fire sync");
  expectedTestCount++;

  // Test a variety of encodings, and make sure they work properly
  r = new FileReader();
  r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "no encoding reading");
  r.readAsText(asciiFile, "");
  expectedTestCount++;

  r = new FileReader();
  r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "iso8859 reading");
  r.readAsText(asciiFile, "iso-8859-1");
  expectedTestCount++;

  r = new FileReader();
  r.onload = getLoadHandler(testTextData,
                            convertToUTF8(testTextData).length,
                            "utf8 reading");
  r.readAsText(utf8TextFile, "utf8");
  expectedTestCount++;

  r = new FileReader();
  r.readAsText(utf16TextFile, "utf-16");
  r.onload = getLoadHandler(testTextData,
                            convertToUTF16(testTextData).length,
                            "utf16 reading");
  expectedTestCount++;

  // Test get result without reading
  r = new FileReader();
  is(r.readyState, FileReader.EMPTY,
     "readyState in test reader get result without reading");
  is(r.error, null,
     "no error in test reader get result without reading");
  is(r.result, null,
     "result in test reader get result without reading");

  // Test loading an empty file works (and doesn't crash!)
  r = new FileReader();
  r.onload = getLoadHandler("", 0, "empty no encoding reading");
  r.readAsText(emptyFile, "");
  expectedTestCount++;

  r = new FileReader();
  r.onload = getLoadHandler("", 0, "empty utf8 reading");
  r.readAsText(emptyFile, "utf8");
  expectedTestCount++;

  r = new FileReader();
  r.onload = getLoadHandler("", 0, "empty utf16 reading");
  r.readAsText(emptyFile, "utf-16");
  expectedTestCount++;

  r = new FileReader();
  r.onload = getLoadHandler("", 0, "empty binary string reading");
  r.readAsBinaryString(emptyFile);
  expectedTestCount++;

  r = new FileReader();
  r.onload = getLoadHandlerForArrayBuffer("", 0, "empty array buffer reading");
  r.readAsArrayBuffer(emptyFile);
  expectedTestCount++;

  r = new FileReader();
  r.onload = getLoadHandler(convertToDataURL(""), 0, "empt binary string reading");
  r.readAsDataURL(emptyFile);
  expectedTestCount++;

  // Test reusing a FileReader to read multiple times
  r = new FileReader();
  r.onload = getLoadHandler(testASCIIData,
                            testASCIIData.length,
                            "to-be-reused reading text")
  var makeAnotherReadListener = function(event) {
    r = event.target;
    r.removeEventListener("load", makeAnotherReadListener, false);
    r.onload = getLoadHandler(testASCIIData,
                              testASCIIData.length,
                              "reused reading text");
    r.readAsText(asciiFile);
  };
  r.addEventListener("load", makeAnotherReadListener, false);
  r.readAsText(asciiFile);
  expectedTestCount += 2;

  r = new FileReader();
  r.onload = getLoadHandler(testBinaryData,
                            testBinaryData.length,
                            "to-be-reused reading binary")
  var makeAnotherReadListener2 = function(event) {
    r = event.target;
    r.removeEventListener("load", makeAnotherReadListener2, false);
    r.onload = getLoadHandler(testBinaryData,
                              testBinaryData.length,
                              "reused reading binary");
    r.readAsBinaryString(binaryFile);
  };
  r.addEventListener("load", makeAnotherReadListener2, false);
  r.readAsBinaryString(binaryFile);
  expectedTestCount += 2;

  r = new FileReader();
  r.onload = getLoadHandler(convertToDataURL(testBinaryData),
                            testBinaryData.length,
                            "to-be-reused reading data url")
  var makeAnotherReadListener3 = function(event) {
    r = event.target;
    r.removeEventListener("load", makeAnotherReadListener3, false);
    r.onload = getLoadHandler(convertToDataURL(testBinaryData),
                              testBinaryData.length,
                              "reused reading data url");
    r.readAsDataURL(binaryFile);
  };
  r.addEventListener("load", makeAnotherReadListener3, false);
  r.readAsDataURL(binaryFile);
  expectedTestCount += 2;

  r = new FileReader();
  r.onload = getLoadHandlerForArrayBuffer(testBinaryData,
                                          testBinaryData.length,
                                          "to-be-reused reading arrayBuffer")
  var makeAnotherReadListener4 = function(event) {
    r = event.target;
    r.removeEventListener("load", makeAnotherReadListener4, false);
    r.onload = getLoadHandlerForArrayBuffer(testBinaryData,
                                            testBinaryData.length,
                                            "reused reading arrayBuffer");
    r.readAsArrayBuffer(binaryFile);
  };
  r.addEventListener("load", makeAnotherReadListener4, false);
  r.readAsArrayBuffer(binaryFile);
  expectedTestCount += 2;

  // Test first reading as ArrayBuffer then read as something else
  // (BinaryString) and doesn't crash
  r = new FileReader();
  r.onload = getLoadHandlerForArrayBuffer(testBinaryData,
                                          testBinaryData.length,
                                          "to-be-reused reading arrayBuffer")
  var makeAnotherReadListener5 = function(event) {
    r = event.target;
    r.removeEventListener("load", makeAnotherReadListener5, false);
    r.onload = getLoadHandler(testBinaryData,
                              testBinaryData.length,
                              "reused reading binary string");
    r.readAsBinaryString(binaryFile);
  };
  r.addEventListener("load", makeAnotherReadListener5, false);
  r.readAsArrayBuffer(binaryFile);
  expectedTestCount += 2;

  //Test data-URI encoding on differing file sizes
  is(dataurldata0.length % 3, 0, "Want to test data with length % 3 == 0");
  r = new FileReader();
  r.onload = getLoadHandler(convertToDataURL(dataurldata0),
                            dataurldata0.length,
                            "dataurl reading, %3 = 0");
  r.readAsDataURL(dataUrlFile0);
  expectedTestCount++;

  is(dataurldata1.length % 3, 1, "Want to test data with length % 3 == 1");
  r = new FileReader();
  r.onload = getLoadHandler(convertToDataURL(dataurldata1),
                            dataurldata1.length,
                            "dataurl reading, %3 = 1");
  r.readAsDataURL(dataUrlFile1);
  expectedTestCount++;

  is(dataurldata2.length % 3, 2, "Want to test data with length % 3 == 2");
  r = new FileReader();
  r.onload = getLoadHandler(convertToDataURL(dataurldata2),
                            dataurldata2.length,
                            "dataurl reading, %3 = 2");
  r.readAsDataURL(dataUrlFile2),
  expectedTestCount++;


  // Test abort()
  var abortHasRun = false;
  var loadEndHasRun = false;
  r = new FileReader();
  r.onabort = function (event) {
    is(abortHasRun, false, "abort should only fire once");
    is(loadEndHasRun, false, "loadend shouldn't have fired yet");
    abortHasRun = true;
    is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort");
    is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads");
    is(event.target.result, null, "file data should be null on aborted reads");
  }
  r.onloadend = function (event) {
    is(abortHasRun, true, "abort should fire before loadend");
    is(loadEndHasRun, false, "loadend should only fire once");
    loadEndHasRun = true;
    is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort");
    is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads");
    is(event.target.result, null, "file data should be null on aborted reads");
  }
  r.onload = function() { ok(false, "load should not fire for aborted reads") };
  r.onerror = function() { ok(false, "error should not fire for aborted reads") };
  r.onprogress = function() { ok(false, "progress should not fire for aborted reads") };
  var abortThrew = false;
  try {
    r.abort();
  } catch(e) {
    abortThrew = true;
  }
  is(abortThrew, true, "abort() must throw if not loading");
  is(abortHasRun, false, "abort() is a no-op unless loading");
  r.readAsText(asciiFile);
  r.abort();
  is(abortHasRun, true, "abort should fire sync");
  is(loadEndHasRun, true, "loadend should fire sync");

  // Test calling readAsX to cause abort()
  var reuseAbortHasRun = false;
  r = new FileReader();
  r.onabort = function (event) {
    is(reuseAbortHasRun, false, "abort should only fire once");
    reuseAbortHasRun = true;
    is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort");
    is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads");
    is(event.target.result, null, "file data should be null on aborted reads");
  }
  r.onload = function() { ok(false, "load should not fire for aborted reads") };
  var abortThrew = false;
  try {
    r.abort();
  } catch(e) {
    abortThrew = true;
  }
  is(abortThrew, true, "abort() must throw if not loading");
  is(reuseAbortHasRun, false, "abort() is a no-op unless loading");
  r.readAsText(asciiFile);
  r.readAsText(asciiFile);
  is(reuseAbortHasRun, true, "abort should fire sync");
  r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "reuse-as-abort reading");
  expectedTestCount++;


  // Test reading from nonexistent files
  r = new FileReader();
  var didThrow = false;
  r.onerror = function (event) {
    is(event.target.readyState, FileReader.DONE, "should be DONE while firing onerror");
    is(event.target.error.name, "NotFoundError", "error set to NotFoundError for nonexistent files");
    is(event.target.result, null, "file data should be null on aborted reads");
    testHasRun();
  };
  r.onload = function (event) {
    is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)");
    testHasRun();
  };
  try {
    r.readAsDataURL(nonExistingFile);
    expectedTestCount++;
  } catch(ex) {
    didThrow = true;
  }
  // Once this test passes, we should test that onerror gets called and
  // that the FileReader object is in the right state during that call.
  is(didThrow, false, "shouldn't throw when opening nonexistent file, should fire error instead");


  function getLoadHandler(expectedResult, expectedLength, testName) {
    return function (event) {
      is(event.target.readyState, FileReader.DONE,
         "readyState in test " + testName);
      is(event.target.error, null,
         "no error in test " + testName);
      is(event.target.result, expectedResult,
         "result in test " + testName);
      is(event.lengthComputable, true,
         "lengthComputable in test " + testName);
      is(event.loaded, expectedLength,
         "loaded in test " + testName);
      is(event.total, expectedLength,
         "total in test " + testName);
      testHasRun();
    }
  }

  function getLoadHandlerForArrayBuffer(expectedResult, expectedLength, testName) {
    return function (event) {
      is(event.target.readyState, FileReader.DONE,
         "readyState in test " + testName);
      is(event.target.error, null,
         "no error in test " +  testName);
      is(event.lengthComputable, true,
         "lengthComputable in test " + testName);
      is(event.loaded, expectedLength,
         "loaded in test " + testName);
      is(event.total, expectedLength,
         "total in test " + testName);
      is(event.target.result.byteLength, expectedLength,
         "array buffer size in test " + testName);
      var u8v = new Uint8Array(event.target.result);
      is(String.fromCharCode.apply(String, u8v), expectedResult,
         "array buffer contents in test " + testName);
      u8v = null;
      SpecialPowers.gc();
      is(event.target.result.byteLength, expectedLength,
         "array buffer size after gc in test " + testName);
      u8v = new Uint8Array(event.target.result);
      is(String.fromCharCode.apply(String, u8v), expectedResult,
         "array buffer contents after gc in test " + testName);
      testHasRun();
    }
  }

  function testHasRun() {
    //alert(testRanCounter);
    ++testRanCounter;
    if (testRanCounter == expectedTestCount) {
      is(testSetupFinished, true, "test setup should have finished; check for exceptions");
      is(onloadHasRunText, true, "onload text should have fired by now");
      is(onloadHasRunBinary, true, "onload binary should have fired by now");
      opener.destroy();
      SimpleTest.finish();
    }
  }

  testSetupFinished = true;
}

function convertToUTF16(s) {
  res = "";
  for (var i = 0; i < s.length; ++i) {
    c = s.charCodeAt(i);
    res += String.fromCharCode(c & 255, c >>> 8);
  }
  return res;
}

function convertToUTF8(s) {
  return unescape(encodeURIComponent(s));
}

function convertToDataURL(s) {
  return "data:application/octet-stream;base64," + btoa(s);
}

</script>
</pre>
</body> </html>