summaryrefslogtreecommitdiffstats
path: root/dom/file/tests/common_fileReader.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/file/tests/common_fileReader.js')
-rw-r--r--dom/file/tests/common_fileReader.js848
1 files changed, 848 insertions, 0 deletions
diff --git a/dom/file/tests/common_fileReader.js b/dom/file/tests/common_fileReader.js
new file mode 100644
index 0000000000..9ee6a32b93
--- /dev/null
+++ b/dom/file/tests/common_fileReader.js
@@ -0,0 +1,848 @@
+function test_setup() {
+ return new Promise(resolve => {
+ const minFileSize = 20000;
+
+ // Create strings containing data we'll test with. We'll want long
+ // strings to ensure they span multiple buffers while loading
+ let testTextData = "asd b\tlah\u1234w\u00a0r";
+ while (testTextData.length < minFileSize) {
+ testTextData = testTextData + testTextData;
+ }
+
+ let testASCIIData = "abcdef 123456\n";
+ while (testASCIIData.length < minFileSize) {
+ testASCIIData = testASCIIData + testASCIIData;
+ }
+
+ let testBinaryData = "";
+ for (let i = 0; i < 256; i++) {
+ testBinaryData += String.fromCharCode(i);
+ }
+ while (testBinaryData.length < minFileSize) {
+ testBinaryData = testBinaryData + testBinaryData;
+ }
+
+ let dataurldata0 = testBinaryData.substr(
+ 0,
+ testBinaryData.length - (testBinaryData.length % 3)
+ );
+ let dataurldata1 = testBinaryData.substr(
+ 0,
+ testBinaryData.length - 2 - (testBinaryData.length % 3)
+ );
+ let dataurldata2 = testBinaryData.substr(
+ 0,
+ testBinaryData.length - 1 - (testBinaryData.length % 3)
+ );
+
+ //Set up files for testing
+ let openerURL = SimpleTest.getTestFileURL("fileapi_chromeScript.js");
+ let opener = SpecialPowers.loadChromeScript(openerURL);
+
+ opener.addMessageListener("files.opened", message => {
+ let [
+ asciiFile,
+ binaryFile,
+ nonExistingFile,
+ utf8TextFile,
+ utf16TextFile,
+ emptyFile,
+ dataUrlFile0,
+ dataUrlFile1,
+ dataUrlFile2,
+ ] = message;
+
+ resolve({
+ blobs: {
+ asciiFile,
+ binaryFile,
+ nonExistingFile,
+ utf8TextFile,
+ utf16TextFile,
+ emptyFile,
+ dataUrlFile0,
+ dataUrlFile1,
+ dataUrlFile2,
+ },
+ data: {
+ text: testTextData,
+ ascii: testASCIIData,
+ binary: testBinaryData,
+ url0: dataurldata0,
+ url1: dataurldata1,
+ url2: dataurldata2,
+ },
+ });
+ });
+
+ opener.sendAsyncMessage("files.open", [
+ testASCIIData,
+ testBinaryData,
+ null,
+ convertToUTF8(testTextData),
+ convertToUTF16(testTextData),
+ "",
+ dataurldata0,
+ dataurldata1,
+ dataurldata2,
+ ]);
+ });
+}
+
+function runBasicTests(data) {
+ return test_basic()
+ .then(() => {
+ return test_readAsText(data.blobs.asciiFile, data.data.ascii);
+ })
+ .then(() => {
+ return test_readAsBinaryString(data.blobs.binaryFile, data.data.binary);
+ })
+ .then(() => {
+ return test_readAsArrayBuffer(data.blobs.binaryFile, data.data.binary);
+ });
+}
+
+function runEncodingTests(data) {
+ return test_readAsTextWithEncoding(
+ data.blobs.asciiFile,
+ data.data.ascii,
+ data.data.ascii.length,
+ ""
+ )
+ .then(() => {
+ return test_readAsTextWithEncoding(
+ data.blobs.asciiFile,
+ data.data.ascii,
+ data.data.ascii.length,
+ "iso8859-1"
+ );
+ })
+ .then(() => {
+ return test_readAsTextWithEncoding(
+ data.blobs.utf8TextFile,
+ data.data.text,
+ convertToUTF8(data.data.text).length,
+ "utf8"
+ );
+ })
+ .then(() => {
+ return test_readAsTextWithEncoding(
+ data.blobs.utf16TextFile,
+ data.data.text,
+ convertToUTF16(data.data.text).length,
+ "utf-16"
+ );
+ })
+ .then(() => {
+ return test_readAsTextWithEncoding(data.blobs.emptyFile, "", 0, "");
+ })
+ .then(() => {
+ return test_readAsTextWithEncoding(data.blobs.emptyFile, "", 0, "utf8");
+ })
+ .then(() => {
+ return test_readAsTextWithEncoding(data.blobs.emptyFile, "", 0, "utf-16");
+ });
+}
+
+function runEmptyTests(data) {
+ return test_onlyResult()
+ .then(() => {
+ return test_readAsText(data.blobs.emptyFile, "");
+ })
+ .then(() => {
+ return test_readAsBinaryString(data.blobs.emptyFile, "");
+ })
+ .then(() => {
+ return test_readAsArrayBuffer(data.blobs.emptyFile, "");
+ })
+ .then(() => {
+ return test_readAsDataURL(data.blobs.emptyFile, convertToDataURL(""), 0);
+ });
+}
+
+function runTwiceTests(data) {
+ return test_readAsTextTwice(data.blobs.asciiFile, data.data.ascii)
+ .then(() => {
+ return test_readAsBinaryStringTwice(
+ data.blobs.binaryFile,
+ data.data.binary
+ );
+ })
+ .then(() => {
+ return test_readAsDataURLTwice(
+ data.blobs.binaryFile,
+ convertToDataURL(data.data.binary),
+ data.data.binary.length
+ );
+ })
+ .then(() => {
+ return test_readAsArrayBufferTwice(
+ data.blobs.binaryFile,
+ data.data.binary
+ );
+ })
+ .then(() => {
+ return test_readAsArrayBufferTwice2(
+ data.blobs.binaryFile,
+ data.data.binary
+ );
+ });
+}
+
+function runOtherTests(data) {
+ return test_readAsDataURL_customLength(
+ data.blobs.dataUrlFile0,
+ convertToDataURL(data.data.url0),
+ data.data.url0.length,
+ 0
+ )
+ .then(() => {
+ return test_readAsDataURL_customLength(
+ data.blobs.dataUrlFile1,
+ convertToDataURL(data.data.url1),
+ data.data.url1.length,
+ 1
+ );
+ })
+ .then(() => {
+ return test_readAsDataURL_customLength(
+ data.blobs.dataUrlFile2,
+ convertToDataURL(data.data.url2),
+ data.data.url2.length,
+ 2
+ );
+ })
+ .then(() => {
+ return test_abort(data.blobs.asciiFile);
+ })
+ .then(() => {
+ return test_abort_readAsX(data.blobs.asciiFile, data.data.ascii);
+ })
+ .then(() => {
+ return test_nonExisting(data.blobs.nonExistingFile);
+ });
+}
+
+function convertToUTF16(s) {
+ let res = "";
+ for (let 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);
+}
+
+function loadEventHandler_string(
+ event,
+ resolve,
+ reader,
+ data,
+ dataLength,
+ testName
+) {
+ is(event.target, reader, "Correct target.");
+ is(
+ event.target.readyState,
+ FileReader.DONE,
+ "readyState in test " + testName
+ );
+ is(event.target.error, null, "no error in test " + testName);
+ is(event.target.result, data, "result in test " + testName);
+ is(event.lengthComputable, true, "lengthComputable in test " + testName);
+ is(event.loaded, dataLength, "loaded in test " + testName);
+ is(event.total, dataLength, "total in test " + testName);
+ resolve();
+}
+
+function loadEventHandler_arrayBuffer(event, resolve, reader, data, testName) {
+ 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, data.length, "loaded in test " + testName);
+ is(event.total, data.length, "total in test " + testName);
+ is(
+ event.target.result.byteLength,
+ data.length,
+ "array buffer size in test " + testName
+ );
+
+ let u8v = new Uint8Array(event.target.result);
+ is(
+ String.fromCharCode.apply(String, u8v),
+ data,
+ "array buffer contents in test " + testName
+ );
+ u8v = null;
+
+ if ("SpecialPowers" in self) {
+ SpecialPowers.gc();
+
+ is(
+ event.target.result.byteLength,
+ data.length,
+ "array buffer size after gc in test " + testName
+ );
+ u8v = new Uint8Array(event.target.result);
+ is(
+ String.fromCharCode.apply(String, u8v),
+ data,
+ "array buffer contents after gc in test " + testName
+ );
+ }
+
+ resolve();
+}
+
+function test_basic() {
+ return new Promise(resolve => {
+ is(FileReader.EMPTY, 0, "correct EMPTY value");
+ is(FileReader.LOADING, 1, "correct LOADING value");
+ is(FileReader.DONE, 2, "correct DONE value");
+ resolve();
+ });
+}
+
+function test_readAsText(blob, text) {
+ return new Promise(resolve => {
+ let onloadHasRun = false;
+ let onloadStartHasRun = false;
+
+ let r = new FileReader();
+ is(r.readyState, FileReader.EMPTY, "correct initial text readyState");
+
+ r.onload = event => {
+ loadEventHandler_string(
+ event,
+ resolve,
+ r,
+ text,
+ text.length,
+ "readAsText"
+ );
+ };
+
+ r.addEventListener("load", () => {
+ onloadHasRun = true;
+ });
+ r.addEventListener("loadstart", () => {
+ onloadStartHasRun = true;
+ });
+
+ r.readAsText(blob);
+
+ is(r.readyState, FileReader.LOADING, "correct loading text readyState");
+ is(onloadHasRun, false, "text loading must be async");
+ is(onloadStartHasRun, false, "text loadstart should fire async");
+ });
+}
+
+function test_readAsBinaryString(blob, text) {
+ return new Promise(resolve => {
+ let onloadHasRun = false;
+ let onloadStartHasRun = false;
+
+ let r = new FileReader();
+ is(r.readyState, FileReader.EMPTY, "correct initial binary readyState");
+
+ r.addEventListener("load", function () {
+ onloadHasRun = true;
+ });
+ r.addEventListener("loadstart", function () {
+ onloadStartHasRun = true;
+ });
+
+ r.readAsBinaryString(blob);
+
+ r.onload = event => {
+ loadEventHandler_string(
+ event,
+ resolve,
+ r,
+ text,
+ text.length,
+ "readAsBinaryString"
+ );
+ };
+
+ is(r.readyState, FileReader.LOADING, "correct loading binary readyState");
+ is(onloadHasRun, false, "binary loading must be async");
+ is(onloadStartHasRun, false, "binary loadstart should fire async");
+ });
+}
+
+function test_readAsArrayBuffer(blob, text) {
+ return new Promise(resolve => {
+ let onloadHasRun = false;
+ let onloadStartHasRun = false;
+
+ r = new FileReader();
+ is(
+ r.readyState,
+ FileReader.EMPTY,
+ "correct initial arrayBuffer readyState"
+ );
+
+ r.addEventListener("load", function () {
+ onloadHasRun = true;
+ });
+ r.addEventListener("loadstart", function () {
+ onloadStartHasRun = true;
+ });
+
+ r.readAsArrayBuffer(blob);
+
+ r.onload = event => {
+ loadEventHandler_arrayBuffer(
+ event,
+ resolve,
+ r,
+ text,
+ "readAsArrayBuffer"
+ );
+ };
+
+ is(
+ r.readyState,
+ FileReader.LOADING,
+ "correct loading arrayBuffer readyState"
+ );
+ is(onloadHasRun, false, "arrayBuffer loading must be async");
+ is(onloadStartHasRun, false, "arrayBuffer loadstart should fire sync");
+ });
+}
+
+// Test a variety of encodings, and make sure they work properly
+function test_readAsTextWithEncoding(blob, text, length, charset) {
+ return new Promise(resolve => {
+ let r = new FileReader();
+ r.onload = event => {
+ loadEventHandler_string(
+ event,
+ resolve,
+ r,
+ text,
+ length,
+ "readAsText-" + charset
+ );
+ };
+ r.readAsText(blob, charset);
+ });
+}
+
+// Test get result without reading
+function test_onlyResult() {
+ return new Promise(resolve => {
+ let 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");
+ resolve();
+ });
+}
+
+function test_readAsDataURL(blob, text, length) {
+ return new Promise(resolve => {
+ let r = new FileReader();
+ r.onload = event => {
+ loadEventHandler_string(event, resolve, r, text, length, "readAsDataURL");
+ };
+ r.readAsDataURL(blob);
+ });
+}
+
+// Test reusing a FileReader to read multiple times
+function test_readAsTextTwice(blob, text) {
+ return new Promise(resolve => {
+ let r = new FileReader();
+ r.onload = event => {
+ loadEventHandler_string(
+ event,
+ () => {},
+ r,
+ text,
+ text.length,
+ "readAsText-reused-once"
+ );
+ };
+
+ let anotherListener = event => {
+ let r1 = event.target;
+ r1.removeEventListener("load", anotherListener);
+ r1.onload = evt => {
+ loadEventHandler_string(
+ evt,
+ resolve,
+ r1,
+ text,
+ text.length,
+ "readAsText-reused-twice"
+ );
+ };
+ r1.readAsText(blob);
+ };
+
+ r.addEventListener("load", anotherListener);
+ r.readAsText(blob);
+ });
+}
+
+// Test reusing a FileReader to read multiple times
+function test_readAsBinaryStringTwice(blob, text) {
+ return new Promise(resolve => {
+ let r = new FileReader();
+ r.onload = event => {
+ loadEventHandler_string(
+ event,
+ () => {},
+ r,
+ text,
+ text.length,
+ "readAsBinaryString-reused-once"
+ );
+ };
+
+ let anotherListener = event => {
+ let r1 = event.target;
+ r1.removeEventListener("load", anotherListener);
+ r1.onload = evt => {
+ loadEventHandler_string(
+ evt,
+ resolve,
+ r1,
+ text,
+ text.length,
+ "readAsBinaryString-reused-twice"
+ );
+ };
+ r1.readAsBinaryString(blob);
+ };
+
+ r.addEventListener("load", anotherListener);
+ r.readAsBinaryString(blob);
+ });
+}
+
+function test_readAsDataURLTwice(blob, text, length) {
+ return new Promise(resolve => {
+ let r = new FileReader();
+ r.onload = event => {
+ loadEventHandler_string(
+ event,
+ () => {},
+ r,
+ text,
+ length,
+ "readAsDataURL-reused-once"
+ );
+ };
+
+ let anotherListener = event => {
+ let r1 = event.target;
+ r1.removeEventListener("load", anotherListener);
+ r1.onload = evt => {
+ loadEventHandler_string(
+ evt,
+ resolve,
+ r1,
+ text,
+ length,
+ "readAsDataURL-reused-twice"
+ );
+ };
+ r1.readAsDataURL(blob);
+ };
+
+ r.addEventListener("load", anotherListener);
+ r.readAsDataURL(blob);
+ });
+}
+
+function test_readAsArrayBufferTwice(blob, text) {
+ return new Promise(resolve => {
+ let r = new FileReader();
+ r.onload = event => {
+ loadEventHandler_arrayBuffer(
+ event,
+ () => {},
+ r,
+ text,
+ "readAsArrayBuffer-reused-once"
+ );
+ };
+
+ let anotherListener = event => {
+ let r1 = event.target;
+ r1.removeEventListener("load", anotherListener);
+ r1.onload = evt => {
+ loadEventHandler_arrayBuffer(
+ evt,
+ resolve,
+ r1,
+ text,
+ "readAsArrayBuffer-reused-twice"
+ );
+ };
+ r1.readAsArrayBuffer(blob);
+ };
+
+ r.addEventListener("load", anotherListener);
+ r.readAsArrayBuffer(blob);
+ });
+}
+
+// Test first reading as ArrayBuffer then read as something else (BinaryString)
+// and doesn't crash
+function test_readAsArrayBufferTwice2(blob, text) {
+ return new Promise(resolve => {
+ let r = new FileReader();
+ r.onload = event => {
+ loadEventHandler_arrayBuffer(
+ event,
+ () => {},
+ r,
+ text,
+ "readAsArrayBuffer-reused-once2"
+ );
+ };
+
+ let anotherListener = event => {
+ let r1 = event.target;
+ r1.removeEventListener("load", anotherListener);
+ r1.onload = evt => {
+ loadEventHandler_string(
+ evt,
+ resolve,
+ r1,
+ text,
+ text.length,
+ "readAsArrayBuffer-reused-twice2"
+ );
+ };
+ r1.readAsBinaryString(blob);
+ };
+
+ r.addEventListener("load", anotherListener);
+ r.readAsArrayBuffer(blob);
+ });
+}
+
+function test_readAsDataURL_customLength(blob, text, length, numb) {
+ return new Promise(resolve => {
+ is(length % 3, numb, "Want to test data with length %3 == " + numb);
+ let r = new FileReader();
+ r.onload = event => {
+ loadEventHandler_string(
+ event,
+ resolve,
+ r,
+ text,
+ length,
+ "dataurl reading, %3 = " + numb
+ );
+ };
+ r.readAsDataURL(blob);
+ });
+}
+
+// Test abort()
+function test_abort(blob) {
+ return new Promise(resolve => {
+ let abortHasRun = false;
+ let loadEndHasRun = false;
+
+ let 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");
+ };
+
+ let abortThrew = false;
+ try {
+ r.abort();
+ } catch (e) {
+ abortThrew = true;
+ }
+
+ is(abortThrew, false, "abort() doesn't throw");
+ is(abortHasRun, false, "abort() is a no-op unless loading");
+
+ r.readAsText(blob);
+ r.abort();
+
+ is(abortHasRun, true, "abort should fire sync");
+ is(loadEndHasRun, true, "loadend should fire sync");
+
+ resolve();
+ });
+}
+
+// Test calling readAsX to cause abort()
+function test_abort_readAsX(blob, text) {
+ return new Promise(resolve => {
+ let reuseAbortHasRun = false;
+
+ let 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 fire for nested reads");
+ };
+
+ let abortThrew = false;
+ try {
+ r.abort();
+ } catch (e) {
+ abortThrew = true;
+ }
+
+ is(abortThrew, false, "abort() should not throw");
+ is(reuseAbortHasRun, false, "abort() is a no-op unless loading");
+ r.readAsText(blob);
+
+ let readThrew = false;
+ try {
+ r.readAsText(blob);
+ } catch (e) {
+ readThrew = true;
+ }
+
+ is(readThrew, true, "readAsText() must throw if loading");
+ is(reuseAbortHasRun, false, "abort should not fire");
+
+ r.onload = event => {
+ loadEventHandler_string(
+ event,
+ resolve,
+ r,
+ text,
+ text.length,
+ "reuse-as-abort reading"
+ );
+ };
+ });
+}
+
+// Test reading from nonexistent files
+function test_nonExisting(blob) {
+ return new Promise(resolve => {
+ let r = new FileReader();
+
+ 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"
+ );
+ resolve();
+ };
+ r.onload = function (event) {
+ is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)");
+ };
+
+ let didThrow = false;
+ try {
+ r.readAsDataURL(blob);
+ } 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"
+ );
+ });
+}