From 43a97878ce14b72f0981164f87f2e35e14151312 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:22:09 +0200 Subject: Adding upstream version 110.0.1. Signed-off-by: Daniel Baumann --- dom/file/tests/common_blob.js | 395 ++++++++++ dom/file/tests/common_blob_reading.js | 50 ++ dom/file/tests/common_blob_types.js | 82 +++ dom/file/tests/common_fileReader.js | 848 ++++++++++++++++++++++ dom/file/tests/crashtests/1480354.html | 14 + dom/file/tests/crashtests/1562891.html | 16 + dom/file/tests/crashtests/1747185.html | 11 + dom/file/tests/crashtests/1748342.html | 26 + dom/file/tests/crashtests/crashtests.list | 4 + dom/file/tests/create_file_objects.js | 19 + dom/file/tests/file_blobURL_expiring.html | 4 + dom/file/tests/file_mozfiledataurl_audio.ogg | Bin 0 -> 135861 bytes dom/file/tests/file_mozfiledataurl_doc.html | 6 + dom/file/tests/file_mozfiledataurl_img.jpg | Bin 0 -> 2711 bytes dom/file/tests/file_mozfiledataurl_inner.html | 76 ++ dom/file/tests/file_mozfiledataurl_text.txt | 1 + dom/file/tests/file_nonascii_blob_url.html | 24 + dom/file/tests/fileapi_chromeScript.js | 54 ++ dom/file/tests/mochitest.ini | 48 ++ dom/file/tests/test_agentcluster_bloburl.js | 170 +++++ dom/file/tests/test_blobURL_expiring.html | 48 ++ dom/file/tests/test_blob_fragment_and_query.html | 62 ++ dom/file/tests/test_blob_reading.html | 34 + dom/file/tests/test_blobconstructor.html | 246 +++++++ dom/file/tests/test_bloburi.js | 28 + dom/file/tests/test_bug1507893.html | 63 ++ dom/file/tests/test_bug1742540.html | 83 +++ dom/file/tests/test_createFile.js | 52 ++ dom/file/tests/test_file_from_blob.html | 110 +++ dom/file/tests/test_file_negative_date.html | 29 + dom/file/tests/test_fileapi_basic.html | 24 + dom/file/tests/test_fileapi_basic_worker.html | 38 + dom/file/tests/test_fileapi_encoding.html | 24 + dom/file/tests/test_fileapi_encoding_worker.html | 38 + dom/file/tests/test_fileapi_other.html | 24 + dom/file/tests/test_fileapi_other_worker.html | 38 + dom/file/tests/test_fileapi_slice_image.html | 139 ++++ dom/file/tests/test_fileapi_slice_memFile_1.html | 37 + dom/file/tests/test_fileapi_slice_memFile_2.html | 37 + dom/file/tests/test_fileapi_slice_realFile_1.html | 37 + dom/file/tests/test_fileapi_slice_realFile_2.html | 37 + dom/file/tests/test_fileapi_twice.html | 24 + dom/file/tests/test_fileapi_twice_worker.html | 38 + dom/file/tests/test_ipc_messagemanager_blob.js | 102 +++ dom/file/tests/test_mozfiledataurl.html | 224 ++++++ dom/file/tests/test_nonascii_blob_url.html | 28 + dom/file/tests/worker_blob_reading.js | 26 + dom/file/tests/worker_bug1507893.js | 5 + dom/file/tests/worker_bug1742540.js | 5 + dom/file/tests/worker_fileReader.js | 30 + dom/file/tests/xpcshell.ini | 8 + 51 files changed, 3566 insertions(+) create mode 100644 dom/file/tests/common_blob.js create mode 100644 dom/file/tests/common_blob_reading.js create mode 100644 dom/file/tests/common_blob_types.js create mode 100644 dom/file/tests/common_fileReader.js create mode 100644 dom/file/tests/crashtests/1480354.html create mode 100644 dom/file/tests/crashtests/1562891.html create mode 100644 dom/file/tests/crashtests/1747185.html create mode 100644 dom/file/tests/crashtests/1748342.html create mode 100644 dom/file/tests/crashtests/crashtests.list create mode 100644 dom/file/tests/create_file_objects.js create mode 100644 dom/file/tests/file_blobURL_expiring.html create mode 100644 dom/file/tests/file_mozfiledataurl_audio.ogg create mode 100644 dom/file/tests/file_mozfiledataurl_doc.html create mode 100644 dom/file/tests/file_mozfiledataurl_img.jpg create mode 100644 dom/file/tests/file_mozfiledataurl_inner.html create mode 100644 dom/file/tests/file_mozfiledataurl_text.txt create mode 100644 dom/file/tests/file_nonascii_blob_url.html create mode 100644 dom/file/tests/fileapi_chromeScript.js create mode 100644 dom/file/tests/mochitest.ini create mode 100644 dom/file/tests/test_agentcluster_bloburl.js create mode 100644 dom/file/tests/test_blobURL_expiring.html create mode 100644 dom/file/tests/test_blob_fragment_and_query.html create mode 100644 dom/file/tests/test_blob_reading.html create mode 100644 dom/file/tests/test_blobconstructor.html create mode 100644 dom/file/tests/test_bloburi.js create mode 100644 dom/file/tests/test_bug1507893.html create mode 100644 dom/file/tests/test_bug1742540.html create mode 100644 dom/file/tests/test_createFile.js create mode 100644 dom/file/tests/test_file_from_blob.html create mode 100644 dom/file/tests/test_file_negative_date.html create mode 100644 dom/file/tests/test_fileapi_basic.html create mode 100644 dom/file/tests/test_fileapi_basic_worker.html create mode 100644 dom/file/tests/test_fileapi_encoding.html create mode 100644 dom/file/tests/test_fileapi_encoding_worker.html create mode 100644 dom/file/tests/test_fileapi_other.html create mode 100644 dom/file/tests/test_fileapi_other_worker.html create mode 100644 dom/file/tests/test_fileapi_slice_image.html create mode 100644 dom/file/tests/test_fileapi_slice_memFile_1.html create mode 100644 dom/file/tests/test_fileapi_slice_memFile_2.html create mode 100644 dom/file/tests/test_fileapi_slice_realFile_1.html create mode 100644 dom/file/tests/test_fileapi_slice_realFile_2.html create mode 100644 dom/file/tests/test_fileapi_twice.html create mode 100644 dom/file/tests/test_fileapi_twice_worker.html create mode 100644 dom/file/tests/test_ipc_messagemanager_blob.js create mode 100644 dom/file/tests/test_mozfiledataurl.html create mode 100644 dom/file/tests/test_nonascii_blob_url.html create mode 100644 dom/file/tests/worker_blob_reading.js create mode 100644 dom/file/tests/worker_bug1507893.js create mode 100644 dom/file/tests/worker_bug1742540.js create mode 100644 dom/file/tests/worker_fileReader.js create mode 100644 dom/file/tests/xpcshell.ini (limited to 'dom/file/tests') diff --git a/dom/file/tests/common_blob.js b/dom/file/tests/common_blob.js new file mode 100644 index 0000000000..98a4cde743 --- /dev/null +++ b/dom/file/tests/common_blob.js @@ -0,0 +1,395 @@ +const RANGE_1 = 1; +const RANGE_2 = 2; + +function testBlob(file, contents, testName) { + // Load file using FileReader + return ( + new Promise(resolve => { + let r = new FileReader(); + r.onload = event => { + is( + event.target.readyState, + FileReader.DONE, + "[FileReader] readyState in test FileReader.readAsBinaryString of " + + testName + ); + is( + event.target.error, + null, + "[FileReader] no error in test FileReader.readAsBinaryString of " + + testName + ); + // Do not use |is(event.target.result, contents, "...");| that may output raw binary data. + is( + event.target.result.length, + contents.length, + "[FileReader] Length of result in test FileReader.readAsBinaryString of " + + testName + ); + ok( + event.target.result == contents, + "[FileReader] Content of result in test FileReader.readAsBinaryString of " + + testName + ); + is( + event.lengthComputable, + true, + "[FileReader] lengthComputable in test FileReader.readAsBinaryString of " + + testName + ); + is( + event.loaded, + contents.length, + "[FileReader] Loaded length in test FileReader.readAsBinaryString of " + + testName + ); + is( + event.total, + contents.length, + "[FileReader] Total length in test FileReader.readAsBinaryString of " + + testName + ); + resolve(); + }; + r.readAsBinaryString(file); + }) + + // Load file using URL.createObjectURL and XMLHttpRequest + .then(() => { + return new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", URL.createObjectURL(file)); + xhr.onload = event => { + XHRLoadHandler( + event, + resolve, + contents, + "XMLHttpRequest load of " + testName + ); + }; + xhr.overrideMimeType("text/plain; charset=x-user-defined"); + xhr.send(); + }); + }) + + // Send file to server using FormData and XMLHttpRequest + .then(() => { + return new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.onload = function(event) { + checkMPSubmission(JSON.parse(event.target.responseText), [ + { name: "hello", value: "world" }, + { + name: "myfile", + value: contents, + fileName: file.name || "blob", + contentType: file.type || "application/octet-stream", + }, + ]); + resolve(); + }; + xhr.open("POST", "../../../dom/html/test/form_submit_server.sjs"); + + let fd = new FormData(); + fd.append("hello", "world"); + fd.append("myfile", file); + + xhr.send(fd); + }); + }) + + // Send file to server using plain XMLHttpRequest + .then(() => { + return new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.open("POST", "../../../dom/xhr/tests/file_XHRSendData.sjs"); + + xhr.onload = function(event) { + is( + event.target.getResponseHeader("Result-Content-Type"), + file.type ? file.type : null, + "request content-type in XMLHttpRequest send of " + testName + ); + is( + event.target.getResponseHeader("Result-Content-Length"), + String(file.size), + "request content-length in XMLHttpRequest send of " + testName + ); + }; + + xhr.addEventListener("load", event => { + XHRLoadHandler( + event, + resolve, + contents, + "XMLHttpRequest send of " + testName + ); + }); + xhr.overrideMimeType("text/plain; charset=x-user-defined"); + xhr.send(file); + }); + }) + ); +} + +function testSlice(file, size, type, contents, fileType, range) { + is(file.type, type, fileType + " file is correct type"); + is(file.size, size, fileType + " file is correct size"); + if (fileType == "fileFile") { + ok(file instanceof File, fileType + " file is a File"); + } else if (fileType == "memFile") { + ok(!(file instanceof File), fileType + " file is not a File"); + } + ok(file instanceof Blob, fileType + " file is also a Blob"); + + let slice = file.slice(0, size); + ok(slice instanceof Blob, fileType + " fullsize slice is a Blob"); + ok(!(slice instanceof File), fileType + " fullsize slice is not a File"); + + slice = file.slice(0, 1234); + ok(slice instanceof Blob, fileType + " sized slice is a Blob"); + ok(!(slice instanceof File), fileType + " sized slice is not a File"); + + slice = file.slice(0, size, "foo/bar"); + is(slice.type, "foo/bar", fileType + " fullsize slice foo/bar type"); + + slice = file.slice(0, 5432, "foo/bar"); + is(slice.type, "foo/bar", fileType + " sized slice foo/bar type"); + + is(slice.slice(0, 10).type, "", fileType + " slice-slice type"); + is(slice.slice(0, 10).size, 10, fileType + " slice-slice size"); + is( + slice.slice(0, 10, "hello/world").type, + "hello/world", + fileType + " slice-slice hello/world type" + ); + is( + slice.slice(0, 10, "hello/world").size, + 10, + fileType + " slice-slice hello/world size" + ); + + // Start, end, expected size + var indexes_range_1 = [ + [0, size, size], + [0, 1234, 1234], + [size - 500, size, 500], + [size - 500, size + 500, 500], + [size + 500, size + 1500, 0], + [0, 0, 0], + [1000, 1000, 0], + [size, size, 0], + [undefined, undefined, size], + [0, undefined, size], + ]; + + var indexes_range_2 = [ + [100, undefined, size - 100], + [-100, undefined, 100], + [100, -100, size - 200], + [-size - 100, undefined, size], + [-2 * size - 100, 500, 500], + [0, -size - 100, 0], + [100, -size - 100, 0], + [50, -size + 100, 50], + [0, 33000, 33000], + [1000, 34000, 33000], + ]; + + let indexes; + if (range == RANGE_1) { + indexes = indexes_range_1; + } else if (range == RANGE_2) { + indexes = indexes_range_2; + } else { + throw "Invalid range!"; + } + + function runNextTest() { + if (!indexes.length) { + return Promise.resolve(true); + } + + let index = indexes.shift(); + + let sliceContents; + let testName; + if (index[0] == undefined) { + slice = file.slice(); + sliceContents = contents.slice(); + testName = fileType + " slice()"; + } else if (index[1] == undefined) { + slice = file.slice(index[0]); + sliceContents = contents.slice(index[0]); + testName = fileType + " slice(" + index[0] + ")"; + } else { + slice = file.slice(index[0], index[1]); + sliceContents = contents.slice(index[0], index[1]); + testName = fileType + " slice(" + index[0] + ", " + index[1] + ")"; + } + + is(slice.type, "", testName + " type"); + is(slice.size, index[2], testName + " size"); + is(sliceContents.length, index[2], testName + " data size"); + + return testBlob(slice, sliceContents, testName).then(runNextTest); + } + + return runNextTest() + .then(() => { + // Slice of slice + let sliceOfSlice = file.slice(0, 40000); + return testBlob( + sliceOfSlice.slice(5000, 42000), + contents.slice(5000, 40000), + "file slice slice" + ); + }) + .then(() => { + // ...of slice of slice + let sliceOfSlice = file + .slice(0, 40000) + .slice(5000, 42000) + .slice(400, 700); + SpecialPowers.gc(); + return testBlob( + sliceOfSlice, + contents.slice(5400, 5700), + "file slice slice slice" + ); + }); +} + +function convertXHRBinary(s) { + let res = ""; + for (let i = 0; i < s.length; ++i) { + res += String.fromCharCode(s.charCodeAt(i) & 255); + } + return res; +} + +function XHRLoadHandler(event, resolve, contents, testName) { + is(event.target.readyState, 4, "[XHR] readyState in test " + testName); + is(event.target.status, 200, "[XHR] no error in test " + testName); + // Do not use |is(convertXHRBinary(event.target.responseText), contents, "...");| that may output raw binary data. + let convertedData = convertXHRBinary(event.target.responseText); + is( + convertedData.length, + contents.length, + "[XHR] Length of result in test " + testName + ); + ok(convertedData == contents, "[XHR] Content of result in test " + testName); + is( + event.lengthComputable, + event.total != 0, + "[XHR] lengthComputable in test " + testName + ); + is(event.loaded, contents.length, "[XHR] Loaded length in test " + testName); + is(event.total, contents.length, "[XHR] Total length in test " + testName); + resolve(); +} + +function checkMPSubmission(sub, expected) { + function getPropCount(o) { + let x, + l = 0; + for (x in o) { + ++l; + } + return l; + } + + is(sub.length, expected.length, "Correct number of items"); + let i; + for (i = 0; i < expected.length; ++i) { + if (!("fileName" in expected[i])) { + is( + sub[i].headers["Content-Disposition"], + 'form-data; name="' + expected[i].name + '"', + "Correct name (A)" + ); + is(getPropCount(sub[i].headers), 1, "Wrong number of headers (A)"); + } else { + is( + sub[i].headers["Content-Disposition"], + 'form-data; name="' + + expected[i].name + + '"; filename="' + + expected[i].fileName + + '"', + "Correct name (B)" + ); + is( + sub[i].headers["Content-Type"], + expected[i].contentType, + "Correct content type (B)" + ); + is(getPropCount(sub[i].headers), 2, "Wrong number of headers (B)"); + } + // Do not use |is(sub[i].body, expected[i].value, "...");| that may output raw binary data. + is(sub[i].body.length, expected[i].value.length, "Length of correct value"); + ok(sub[i].body == expected[i].value, "Content of correct value"); + } +} + +function createCanvasURL() { + return new Promise(resolve => { + // Create a decent-sized image + let cx = $("canvas").getContext("2d"); + let s = cx.canvas.width; + let grad = cx.createLinearGradient(0, 0, s - 1, s - 1); + for (i = 0; i < 0.95; i += 0.1) { + grad.addColorStop(i, "white"); + grad.addColorStop(i + 0.05, "black"); + } + grad.addColorStop(1, "white"); + cx.fillStyle = grad; + cx.fillRect(0, 0, s - 1, s - 1); + cx.fillStyle = "rgba(200, 0, 0, 0.9)"; + cx.fillRect(0.1 * s, 0.1 * s, 0.7 * s, 0.7 * s); + cx.strokeStyle = "rgba(0, 0, 130, 0.5)"; + cx.lineWidth = 0.14 * s; + cx.beginPath(); + cx.arc(0.6 * s, 0.6 * s, 0.3 * s, 0, Math.PI * 2, true); + cx.stroke(); + cx.closePath(); + cx.fillStyle = "rgb(0, 255, 0)"; + cx.beginPath(); + cx.arc(0.1 * s, 0.8 * s, 0.1 * s, 0, Math.PI * 2, true); + cx.fill(); + cx.closePath(); + + let data = atob( + cx.canvas + .toDataURL("image/png") + .substring("data:text/png;base64,".length + 1) + ); + + // This might fail if we dramatically improve the png encoder. If that happens + // please increase the complexity or size of the image generated above to ensure + // that we're testing with files that are large enough. + ok(data.length > 65536, "test data sufficiently large"); + + resolve(data); + }); +} + +function createFile(data, name) { + return new Promise(resolve => { + SpecialPowers.createFiles([{ name, data }], files => { + resolve(files[0]); + }); + }); +} + +function toBlobPromise(canvas) { + function BlobListener(callback, file) { + var reader = new FileReader(); + reader.onload = () => callback(file); + reader.readAsDataURL(file); + } + + return new Promise(resolve => { + canvas.toBlob(BlobListener.bind(undefined, resolve)); + }); +} diff --git a/dom/file/tests/common_blob_reading.js b/dom/file/tests/common_blob_reading.js new file mode 100644 index 0000000000..776ffececf --- /dev/null +++ b/dom/file/tests/common_blob_reading.js @@ -0,0 +1,50 @@ +async function testBlobText(blob, content) { + let text = await blob.text(); + is(text, content, "blob.text()"); +} + +async function testBlobArrayBuffer(blob, content) { + let ab = await blob.arrayBuffer(); + is(ab.byteLength, content.length, "blob.arrayBuffer()"); +} + +async function testBlobStream(blob, content) { + let s = await blob.stream(); + ok(s instanceof ReadableStream, "We have a ReadableStream"); + + let data = await s.getReader().read(); + ok(!data.done, "Nothing is done yet"); + for (let i = 0; i < data.value.length; ++i) { + is(String.fromCharCode(data.value[i]), content[i], "blob.stream() - " + i); + } +} + +function workify(func, blob, content) { + info("Workifying " + func); + + return new Promise((resolve, reject) => { + let worker = new Worker("worker_blob_reading.js"); + worker.postMessage({ func, blob, content }); + worker.onmessage = function(e) { + if (e.data.type == "done") { + resolve(); + return; + } + + if (e.data.type == "error") { + reject(e.data.message); + return; + } + + if (e.data.type == "test") { + ok(e.data.test, e.data.message); + return; + } + + if (e.data.type == "info") { + info(e.data.message); + return; + } + }; + }); +} diff --git a/dom/file/tests/common_blob_types.js b/dom/file/tests/common_blob_types.js new file mode 100644 index 0000000000..95501e58e5 --- /dev/null +++ b/dom/file/tests/common_blob_types.js @@ -0,0 +1,82 @@ +let blobTypes = [ + { + type: "memory", + factory: async content => { + return new Blob([content]); + }, + blobImplType: "MultipartBlobImpl[StringBlobImpl]", + }, + + { + type: "ipcBlob", + factory: async content => { + return new Promise(resolve => { + let bc1 = new BroadcastChannel("blob tests"); + bc1.onmessage = e => { + resolve(e.data); + }; + + let bc2 = new BroadcastChannel("blob tests"); + bc2.postMessage(new Blob([content])); + }); + }, + blobImplType: + "StreamBlobImpl[StreamBlobImpl[MultipartBlobImpl[StringBlobImpl]]]", + }, + + { + type: "memoryBlob", + factory: async content => { + return new Promise(resolve => { + var xhr = new XMLHttpRequest(); + xhr.open( + "POST", + "http://mochi.test:8888/browser/dom/xhr/tests/temporaryFileBlob.sjs" + ); + xhr.responseType = "blob"; + xhr.send(content); + xhr.onloadend = _ => { + resolve(xhr.response); + }; + }); + }, + blobImplType: "MemoryBlobImpl", + }, + + { + type: "temporaryBlob", + factory: async content => { + await SpecialPowers.pushPrefEnv({ + set: [["dom.blob.memoryToTemporaryFile", 1]], + }); + + return new Promise(resolve => { + var xhr = new XMLHttpRequest(); + xhr.open( + "POST", + "http://mochi.test:8888/browser/dom/xhr/tests/temporaryFileBlob.sjs" + ); + xhr.responseType = "blob"; + xhr.send(content); + xhr.onloadend = _ => { + resolve(xhr.response); + }; + }); + }, + blobImplType: "StreamBlobImpl[TemporaryFileBlobImpl]", + }, +]; + +async function forEachBlobType(content, cb) { + for (let i = 0; i < blobTypes.length; ++i) { + info("Running tests for " + blobTypes[i].type); + let blob = await blobTypes[i].factory(content); + is( + SpecialPowers.wrap(blob).blobImplType, + blobTypes[i].blobImplType, + "Correct blobImplType" + ); + ok(blob instanceof Blob, "Blob created"); + await cb(blob, content); + } +} diff --git a/dom/file/tests/common_fileReader.js b/dom/file/tests/common_fileReader.js new file mode 100644 index 0000000000..3df3a36114 --- /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" + ); + }); +} diff --git a/dom/file/tests/crashtests/1480354.html b/dom/file/tests/crashtests/1480354.html new file mode 100644 index 0000000000..19e53bb1ca --- /dev/null +++ b/dom/file/tests/crashtests/1480354.html @@ -0,0 +1,14 @@ + + + + + diff --git a/dom/file/tests/crashtests/1562891.html b/dom/file/tests/crashtests/1562891.html new file mode 100644 index 0000000000..fff7606a8a --- /dev/null +++ b/dom/file/tests/crashtests/1562891.html @@ -0,0 +1,16 @@ + + + + + diff --git a/dom/file/tests/crashtests/1747185.html b/dom/file/tests/crashtests/1747185.html new file mode 100644 index 0000000000..89af55504f --- /dev/null +++ b/dom/file/tests/crashtests/1747185.html @@ -0,0 +1,11 @@ + + + + + diff --git a/dom/file/tests/crashtests/1748342.html b/dom/file/tests/crashtests/1748342.html new file mode 100644 index 0000000000..5f0811b5bb --- /dev/null +++ b/dom/file/tests/crashtests/1748342.html @@ -0,0 +1,26 @@ + + + + + + + diff --git a/dom/file/tests/crashtests/crashtests.list b/dom/file/tests/crashtests/crashtests.list new file mode 100644 index 0000000000..22eec1962e --- /dev/null +++ b/dom/file/tests/crashtests/crashtests.list @@ -0,0 +1,4 @@ +skip-if(ThreadSanitizer) load 1480354.html +load 1562891.html +skip-if(Android||ThreadSanitizer) load 1747185.html # Crashes on Android, times out on TSan. +load 1748342.html diff --git a/dom/file/tests/create_file_objects.js b/dom/file/tests/create_file_objects.js new file mode 100644 index 0000000000..6243f4b8ab --- /dev/null +++ b/dom/file/tests/create_file_objects.js @@ -0,0 +1,19 @@ +/* eslint-env mozilla/chrome-script */ + +Cu.importGlobalProperties(["File"]); + +addMessageListener("create-file-objects", function(message) { + let files = []; + let promises = []; + for (fileName of message.fileNames) { + promises.push( + File.createFromFileName(fileName).then(function(file) { + files.push(file); + }) + ); + } + + Promise.all(promises).then(function() { + sendAsyncMessage("created-file-objects", files); + }); +}); diff --git a/dom/file/tests/file_blobURL_expiring.html b/dom/file/tests/file_blobURL_expiring.html new file mode 100644 index 0000000000..a1ae725709 --- /dev/null +++ b/dom/file/tests/file_blobURL_expiring.html @@ -0,0 +1,4 @@ + diff --git a/dom/file/tests/file_mozfiledataurl_audio.ogg b/dom/file/tests/file_mozfiledataurl_audio.ogg new file mode 100644 index 0000000000..88b2c1b5b2 Binary files /dev/null and b/dom/file/tests/file_mozfiledataurl_audio.ogg differ diff --git a/dom/file/tests/file_mozfiledataurl_doc.html b/dom/file/tests/file_mozfiledataurl_doc.html new file mode 100644 index 0000000000..763b20a0f9 --- /dev/null +++ b/dom/file/tests/file_mozfiledataurl_doc.html @@ -0,0 +1,6 @@ + + + +

This here is a document!

+ + diff --git a/dom/file/tests/file_mozfiledataurl_img.jpg b/dom/file/tests/file_mozfiledataurl_img.jpg new file mode 100644 index 0000000000..dcd99b9670 Binary files /dev/null and b/dom/file/tests/file_mozfiledataurl_img.jpg differ diff --git a/dom/file/tests/file_mozfiledataurl_inner.html b/dom/file/tests/file_mozfiledataurl_inner.html new file mode 100644 index 0000000000..a2e539bef7 --- /dev/null +++ b/dom/file/tests/file_mozfiledataurl_inner.html @@ -0,0 +1,76 @@ + + + + + +