diff options
Diffstat (limited to 'dom/file/tests')
51 files changed, 3568 insertions, 0 deletions
diff --git a/dom/file/tests/common_blob.js b/dom/file/tests/common_blob.js new file mode 100644 index 0000000000..261909af0d --- /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..5df8419a30 --- /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..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" + ); + }); +} 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 @@ +<html> +<body> + <script> +function createBlob(blocksize) { + var blob = new Blob(); + while (blob.size < 25 * 1024 * 1024) { // 25 MB + blob = new Blob([blob, new Uint8Array(blocksize)]); + } + URL.createObjectURL(blob); +} +createBlob(1024 * 25); + </script> +</body> +</html> 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 @@ +<html> +<head> + <script> + function start () { + const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas') + SpecialPowers.forceGC(); + canvas.toBlob(function (blob) { + blob.stream() + blob.arrayBuffer().then(() => {}) + }) + } + + window.addEventListener('load', start) + </script> +</head> +</html> 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 @@ +<html> +<head> + <script> + window.addEventListener('load', () => { + let a = new Blob([new Uint8Array(2147483647)]) + let b = new File([a], '') + b.stream() + }) + </script> +</head> +</html> 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 @@ +<!DOCTYPE html> +<html> +<head> + <script id="worker1" type="javascript/worker"> + self.onmessage = async function (e) { + self.close() + const reader = new FileReader() + for (let i = 0; i < 25; i++) { + try { reader.readAsBinaryString(e.data[0]) } catch (e) {} + } + reader.addEventListener("progress", () => {}, {}) + } + </script> + <script> + window.addEventListener("load", () => { + const script = new Blob([document.querySelector("#worker1").textContent], { type: "text/javascript" }) + const worker = new Worker(window.URL.createObjectURL(script)) + const data = new Blob(["70\nℽ㮼٠\0𛃧كe۰҅妽𝅧𡴶𝌋쮁偵97\r𐇽0🥂.\b፟+⍳፟D3𣚽🐾�c_߰a<<=𝅦9𝆭𛰅9ௌΐ0�⡖‑뢈/-᭰*٠٪᷁e �‑걢V*=**\u2028שּׁ&0󠄯e\n𛰵𫍰,𝅥t\nl𧶈a𦜠09k䴋�|󠄷🦻𖭄", "*۰0\u2029/\n\r+𖼣k*=\r٪\r慑B�\r\r\n\"\r\\۹c卑4鴗ꛌ\0⌕:\r\n𝚨9ꛅ٠\nJ9\r゙鈷P\u2029҉۹e \b緁︡𤆥^゚|٫揅ᷛ𩊜s2凅9c8H𦰤-\f%٠𨮫‑2𫈮P𝋄窥57\n-゙҄H𣃂-ᷢשּׁ貌솽|𝉃c㙡᭯mL\r"], { + "type": "image/png", + "endings": "transparent" + }) + worker.postMessage([data], []) + }) + </script> +</head> +</html> 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..cf016b239d --- /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 @@ +<script> +var blob = new Blob([123]); +parent.postMessage(URL.createObjectURL(blob), "*"); +</script> diff --git a/dom/file/tests/file_mozfiledataurl_audio.ogg b/dom/file/tests/file_mozfiledataurl_audio.ogg Binary files differnew file mode 100644 index 0000000000..88b2c1b5b2 --- /dev/null +++ b/dom/file/tests/file_mozfiledataurl_audio.ogg 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 @@ +<!doctype html> +<html> +<body> +<p>This here is a document!</p> +<img id=img src="file_mozfiledataurl_img.jpg"> +</html> diff --git a/dom/file/tests/file_mozfiledataurl_img.jpg b/dom/file/tests/file_mozfiledataurl_img.jpg Binary files differnew file mode 100644 index 0000000000..dcd99b9670 --- /dev/null +++ b/dom/file/tests/file_mozfiledataurl_img.jpg 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 @@ +<!doctype html> +<html> +<script type="application/javascript"> +var img; +var audio; +var iframe; + +addEventListener("message", function(e) { + mess = JSON.parse(e.data); + + if ("img" in mess) + img.src = mess.img; + else if ("audio" in mess) + audio.src = mess.audio + else if ("iframe" in mess) + iframe.src = mess.iframe; + else if ("xhr" in mess) { + let xhr = new XMLHttpRequest(); + xhr.onerror = function() { + sendItUp({ didError: true }); + } + xhr.onload = function() { + sendItUp({ text: xhr.responseText }); + } + try { + xhr.open("GET", mess.xhr); + xhr.send(); + } + catch (ex) { + sendItUp({ didThrow: true }); + } + } + +}, false); + +function sendItUp(obj) { + window.parent.postMessage(JSON.stringify(obj), "*"); +} + +function audioNotifyParent(e) { + sendItUp({ type: e.type }); +} + +function imgNotifyParent(e) { + sendItUp({ type: e.type, + width: e.target.width, + height: e.target.height }); +} + +function iframeNotifyParent(e) { + res = { type: e.type }; + try { + res.text = e.target.contentDocument.getElementsByTagName("p")[0].textContent; + } catch (ex) {} + try { + res.imgWidth = e.target.contentDocument.getElementById("img").width; + } catch (ex) {} + + sendItUp(res); +} + +onload = function() { + img = document.getElementById('img'); + img.onerror = img.onload = imgNotifyParent; + iframe = document.getElementById('iframe'); + iframe.onerror = iframe.onload = iframeNotifyParent; + audio = document.getElementById('audio'); + audio.onerror = audio.onloadeddata = audioNotifyParent; +} + +</script> +<body> +<img id=img> +<audio id=audio> +<iframe id=iframe></iframe> +</html> diff --git a/dom/file/tests/file_mozfiledataurl_text.txt b/dom/file/tests/file_mozfiledataurl_text.txt new file mode 100644 index 0000000000..315338aa9b --- /dev/null +++ b/dom/file/tests/file_mozfiledataurl_text.txt @@ -0,0 +1 @@ +Yarr, here be plaintext file, ya landlubber diff --git a/dom/file/tests/file_nonascii_blob_url.html b/dom/file/tests/file_nonascii_blob_url.html new file mode 100644 index 0000000000..89183f4613 --- /dev/null +++ b/dom/file/tests/file_nonascii_blob_url.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test blob URL for non-ascii domain</title> +</head> +<body> + <p id="result"></p> + <script type="application/javascript"> + +window.onmessage = function(e) { + var blob = new Blob([e.data]); + var url = URL.createObjectURL(blob); + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.send(null); + + parent.postMessage(xhr.responseText, '*'); +} + + </script> +</body> +</html> diff --git a/dom/file/tests/fileapi_chromeScript.js b/dom/file/tests/fileapi_chromeScript.js new file mode 100644 index 0000000000..f94b01698b --- /dev/null +++ b/dom/file/tests/fileapi_chromeScript.js @@ -0,0 +1,54 @@ +/* eslint-env mozilla/chrome-script */ + +Cu.importGlobalProperties(["File"]); + +function createFileWithData(fileData) { + var willDelete = fileData === null; + + var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService( + Ci.nsIProperties + ); + + var testFile = dirSvc.get("ProfD", Ci.nsIFile); + testFile.append("fileAPItestfile"); + testFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); + + var outStream = Cc[ + "@mozilla.org/network/file-output-stream;1" + ].createInstance(Ci.nsIFileOutputStream); + outStream.init( + testFile, + 0x02 | 0x08 | 0x20, // write, create, truncate + 0o666, + 0 + ); + if (willDelete) { + fileData = "some irrelevant test data\n"; + } + outStream.write(fileData, fileData.length); + outStream.close(); + return File.createFromNsIFile(testFile).then(domFile => { + if (willDelete) { + testFile.remove(/* recursive: */ false); + } + + return domFile; + }); +} + +addMessageListener("files.open", function (message) { + let promises = []; + let list = []; + + for (let fileData of message) { + promises.push( + createFileWithData(fileData).then(domFile => { + list.push(domFile); + }) + ); + } + + Promise.all(promises).then(() => { + sendAsyncMessage("files.opened", list); + }); +}); diff --git a/dom/file/tests/mochitest.ini b/dom/file/tests/mochitest.ini new file mode 100644 index 0000000000..495c0263d8 --- /dev/null +++ b/dom/file/tests/mochitest.ini @@ -0,0 +1,54 @@ +[DEFAULT] +support-files = + common_blob.js + create_file_objects.js + common_fileReader.js + common_blob_types.js + file_blobURL_expiring.html + file_mozfiledataurl_img.jpg + file_mozfiledataurl_audio.ogg + file_mozfiledataurl_doc.html + file_mozfiledataurl_text.txt + file_mozfiledataurl_inner.html + file_nonascii_blob_url.html + fileapi_chromeScript.js + worker_fileReader.js + !/dom/html/test/form_submit_server.sjs + !/dom/xhr/tests/file_XHRSendData.sjs + !/dom/xhr/tests/temporaryFileBlob.sjs + +[test_blob_fragment_and_query.html] +[test_blobconstructor.html] +[test_blobURL_expiring.html] +[test_file_from_blob.html] +[test_nonascii_blob_url.html] +skip-if = + http3 +[test_file_negative_date.html] +[test_fileapi_basic.html] +[test_fileapi_encoding.html] +[test_fileapi_twice.html] +[test_fileapi_other.html] +[test_fileapi_basic_worker.html] +[test_fileapi_encoding_worker.html] +[test_fileapi_twice_worker.html] +[test_fileapi_other_worker.html] +[test_fileapi_slice_realFile_1.html] +[test_fileapi_slice_realFile_2.html] +skip-if = (verify && !debug && (os == 'win')) +[test_fileapi_slice_memFile_1.html] +[test_fileapi_slice_memFile_2.html] +[test_fileapi_slice_image.html] +[test_mozfiledataurl.html] +skip-if = + toolkit == 'android' #TIMED_OUT + http3 +[test_bug1507893.html] +support-files = worker_bug1507893.js +[test_bug1742540.html] +support-files = worker_bug1742540.js +skip-if = toolkit == 'android' #TIMED_OUT +[test_blob_reading.html] +support-files = common_blob_reading.js worker_blob_reading.js +skip-if = + http3 diff --git a/dom/file/tests/test_agentcluster_bloburl.js b/dom/file/tests/test_agentcluster_bloburl.js new file mode 100644 index 0000000000..2a577116a2 --- /dev/null +++ b/dom/file/tests/test_agentcluster_bloburl.js @@ -0,0 +1,170 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const { CookieXPCShellUtils } = ChromeUtils.importESModule( + "resource://testing-common/CookieXPCShellUtils.sys.mjs" +); + +CookieXPCShellUtils.init(this); + +// Same agent cluster, all works fine: blobURLs can be opened. +add_task(async () => { + do_get_profile(); + + // CookieXPCShellUtils.createServer does not support https + Services.prefs.setBoolPref("dom.security.https_first", false); + + Services.prefs.setBoolPref( + "privacy.partition.bloburl_per_agent_cluster", + true + ); + + const server = CookieXPCShellUtils.createServer({ hosts: ["example.org"] }); + + let result = new Promise(resolve => { + server.registerPathHandler("/result", (metadata, response) => { + resolve(metadata.queryString == "ok"); + + const body = "Done"; + response.bodyOutputStream.write(body, body.length); + }); + }); + + server.registerPathHandler("/test", (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + const body = `<script> + let b = new Blob(["Hello world!"]); + let u = URL.createObjectURL(b); + fetch(u).then(r => r.text()).then(t => { + if (t !== "Hello world!") { + throw new Error(42); + } + }).then(() => fetch("/result?ok"), () => fetch("/result?failure")).then(() => {}); + </script>`; + response.bodyOutputStream.write(body, body.length); + }); + + let contentPage = await CookieXPCShellUtils.loadContentPage( + "http://example.org/test" + ); + + Assert.ok(await result, "BlobURL works"); + await contentPage.close(); +}); + +// Same agent cluster: frames +add_task(async () => { + do_get_profile(); + + // CookieXPCShellUtils.createServer does not support https + Services.prefs.setBoolPref("dom.security.https_first", false); + + const server = CookieXPCShellUtils.createServer({ hosts: ["example.org"] }); + + let result = new Promise(resolve => { + server.registerPathHandler("/result", (metadata, response) => { + resolve(metadata.queryString == "ok"); + + const body = "Done"; + response.bodyOutputStream.write(body, body.length); + }); + }); + + server.registerPathHandler("/iframe", (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + const body = `<script> + fetch("${metadata.queryString}").then(r => r.text()).then(t => { + if (t !== "Hello world!") { + throw new Error(42); + } + }).then(() => fetch("/result?ok"), () => fetch("/result?failure")).then(() => {}); + </script>`; + response.bodyOutputStream.write(body, body.length); + }); + + server.registerPathHandler("/test", (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + const body = `<iframe id="a"></iframe><script> + let b = new Blob(["Hello world!"]); + let u = URL.createObjectURL(b); + document.getElementById("a").src = "/iframe?" + u; + </script>`; + response.bodyOutputStream.write(body, body.length); + }); + + let contentPage = await CookieXPCShellUtils.loadContentPage( + "http://example.org/test" + ); + + Assert.ok(await result, "BlobURL works"); + await contentPage.close(); +}); + +// Cross agent cluster: different tabs +add_task(async () => { + do_get_profile(); + + const server = CookieXPCShellUtils.createServer({ hosts: ["example.org"] }); + + let result = new Promise(resolve => { + server.registerPathHandler("/result", (metadata, response) => { + resolve(metadata.queryString == "ok"); + + const body = "Done"; + response.bodyOutputStream.write(body, body.length); + }); + }); + + const step = new Promise(resolve => { + server.registerPathHandler("/step", (metadata, response) => { + resolve(metadata.queryString); + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + const body = "Thanks!"; + response.bodyOutputStream.write(body, body.length); + }); + }); + + server.registerPathHandler("/test", (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + const body = `<script> + let b = new Blob(["Hello world!"]); + let u = URL.createObjectURL(b); + fetch("/step?" + u).then(() => {}); + </script>`; + response.bodyOutputStream.write(body, body.length); + }); + + let contentPage = await CookieXPCShellUtils.loadContentPage( + "http://example.org/test" + ); + + const blobURL = await step; + Assert.ok(blobURL.length, "We have a blobURL"); + + server.registerPathHandler("/cross-test", (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + const body = `<script> + fetch("${metadata.queryString}").then(r => r.text()).then(t => { + if (t !== "Hello world!") { + throw new Error(42); + } + }).then(() => fetch("/result?ok"), () => fetch("/result?failure")).then(() => {}); + </script>`; + response.bodyOutputStream.write(body, body.length); + }); + + let contentPage2 = await CookieXPCShellUtils.loadContentPage( + "http://example.org/cross-test?" + blobURL + ); + + Assert.ok(!(await result), "BlobURL should not work"); + await contentPage.close(); + await contentPage2.close(); +}); diff --git a/dom/file/tests/test_blobURL_expiring.html b/dom/file/tests/test_blobURL_expiring.html new file mode 100644 index 0000000000..7fdf461371 --- /dev/null +++ b/dom/file/tests/test_blobURL_expiring.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Blob URI expiration</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script> + +onmessage = function(e) { + var blobURL = e.data; + + (new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", blobURL); + xhr.send(); + xhr.onload = function() { + is(xhr.response, "123", "Response matches!"); + resolve(); + } + })).then(function() { + document.body.removeChild(iframe); + }).then(function() { + var xhr = new XMLHttpRequest(); + xhr.open("GET", blobURL); + xhr.onerror = function() { + ok(true, "The URL should be done!"); + SimpleTest.finish(); + } + xhr.onload = function() { + ok(false, "The URL should be done!"); + SimpleTest.finish(); + } + xhr.send(); + }); +} + +var iframe = document.createElement('iframe'); +iframe.src = 'file_blobURL_expiring.html'; +document.body.appendChild(iframe); + +SimpleTest.waitForExplicitFinish(); + + </script> +</body> +</html> diff --git a/dom/file/tests/test_blob_fragment_and_query.html b/dom/file/tests/test_blob_fragment_and_query.html new file mode 100644 index 0000000000..fa3709c419 --- /dev/null +++ b/dom/file/tests/test_blob_fragment_and_query.html @@ -0,0 +1,62 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Blob URI with fragments</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script> + +var blob = new Blob(['hello world']); +ok(blob, "We have a blob."); + +var tests = [ + { part: "", revoke: false, ok: true }, + { part: "", revoke: true, ok: false }, + { part: "?aa", revoke: false, ok: false }, + { part: "?cc#dd", revoke: false, ok: false }, + // Stripping #fragment on fetch + { part: "#bb", revoke: false, ok: true }, + { part: "#ee?ff", revoke: false, ok: true } +]; + +function runTest() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var url = URL.createObjectURL(blob); + ok(url, "We have a URI"); + + var test = tests.shift(); + + if (test.revoke) { + URL.revokeObjectURL(url + test.part); + } + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url + test.part); + + xhr.onload = function() { + ok(test.ok, `URL with "${test.part}" should send()`); + is(xhr.responseText, 'hello world', 'URL: ' + url + test.part); + runTest(); + } + + xhr.onerror = function() { + ok(!test.ok, `URL with "${test.part}" should fail on send()`); + runTest(); + } + + xhr.send(); +} + +SimpleTest.waitForExplicitFinish(); +runTest(); + + </script> +</body> +</html> diff --git a/dom/file/tests/test_blob_reading.html b/dom/file/tests/test_blob_reading.html new file mode 100644 index 0000000000..6efc5c4835 --- /dev/null +++ b/dom/file/tests/test_blob_reading.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Reading blobs</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="common_blob_reading.js"></script> + <script src="common_blob_types.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script> + +SimpleTest.waitForExplicitFinish(); + +async function runAllTests() { + let content = "hello world"; + await forEachBlobType(content, async blob => { + await testBlobText(blob, content); + await workify('testBlobText', blob, content); + + await testBlobArrayBuffer(blob, content); + await workify('testBlobArrayBuffer', blob, content); + + await testBlobStream(blob, content); + await workify('testBlobStream', blob, content); + }); +} + +runAllTests().then(SimpleTest.finish); + + </script> +</body> +</html> diff --git a/dom/file/tests/test_blobconstructor.html b/dom/file/tests/test_blobconstructor.html new file mode 100644 index 0000000000..d95444f9ea --- /dev/null +++ b/dom/file/tests/test_blobconstructor.html @@ -0,0 +1,246 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=721569 +--> +<head> + <title>Test for Blob constructor (Bug 721569)</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="common_blob.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=721569">Mozilla Bug 721569</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +"use strict"; +/** Test for Bug 721569 **/ +var blob = new Blob(); +ok(blob, "Blob should exist"); + +ok(blob.size !== undefined, "Blob should have a size property"); +ok(blob.type !== undefined, "Blob should have a type property"); +ok(blob.slice, "Blob should have a slice method"); + +blob = new Blob([], {type: null}); +ok(blob, "Blob should exist"); +is(blob.type, "null", "Blob type should be stringified"); + +blob = new Blob([], {type: undefined}); +ok(blob, "Blob should exist"); +is(blob.type, "", "Blob type should be treated as missing"); + +try { +blob = new Blob([]); +ok(true, "an empty blobParts argument should not throw"); +} catch(e) { +ok(false, "NOT REACHED"); +} + +try { +blob = new Blob(null); +ok(false, "NOT REACHED"); +} catch(e) { +ok(true, "a null blobParts member should throw"); +} + +try { +blob = new Blob([], null); +ok(true, "a null options member should not throw"); +} catch(e) { +ok(false, "NOT REACHED"); +} + +try { +blob = new Blob([], undefined); +ok(true, "an undefined options member should not throw"); +} catch(e) { +ok(false, "NOT REACHED"); +} + +try { +blob = new Blob([], false); +ok(false, "NOT REACHED"); +} catch(e) { +ok(true, "a boolean options member should throw"); +} + +try { +blob = new Blob([], 0); +ok(false, "NOT REACHED"); +} catch(e) { +ok(true, "a numeric options member should throw"); +} + +try { +blob = new Blob([], ""); +ok(false, "NOT REACHED"); +} catch(e) { +ok(true, "a string options member should throw"); +} + +/** Test for dictionary initialization order **/ +(function() { + var o = {}; + var p = {type: "text/plain", endings: "transparent"}; + var called = []; + function add_to_called(n) { + called.push(n); + return p[n]; + } + ["type", "endings"].forEach(function(n) { + Object.defineProperty(o, n, { get: add_to_called.bind(null, n) }); + }); + var b = new Blob([], o); + is(JSON.stringify(called), JSON.stringify(["endings", "type"]), "dictionary members should be get in lexicographical order"); +})(); + +let blob1 = new Blob(["squiggle"]); +ok(blob1 instanceof Blob, "Blob constructor should produce Blobs"); +ok(!(blob1 instanceof File), "Blob constructor should not produce Files"); +is(blob1.type, "", "Blob constructor with no options should return Blob with empty type"); +is(blob1.size, 8, "Blob constructor should return Blob with correct size"); + +let blob2 = new Blob(["steak"], {type: "content/type"}); +ok(blob2 instanceof Blob, "Blob constructor should produce Blobs"); +ok(!(blob2 instanceof File), "Blob constructor should not produce Files"); +is(blob2.type, "content/type", "Blob constructor with a type option should return Blob with the type"); +is(blob2.size, 5, "Blob constructor should return Blob with correct size"); + + +let aB = new ArrayBuffer(16); +var int8View = new Int8Array(aB); +for (var i = 0; i < 16; i++) { + int8View[i] = i+65; +} + +let testData = + [ + // Test 3 strings + [["foo", "bar", "baz"], {}, + [{start: 0, length: 9, contents: "foobarbaz"}, + {start: 0, length: 3, contents: "foo"}, + {start: 3, length:6, contents: "barbaz"}, + {start: 6, length: 3, contents: "baz"}, + {start: 6, length: 6, contents: "baz"}, + {start: 0, length: 9, contents: "foobarbaz"}, + {start: 0, length: 11, contents: "foobarbaz"}, + {start: 10, length: 5, contents: ""}]], + // Test string, Blob, string + [["foo", blob1, "baz"], {}, + [{start: 0, length: 3, contents: "foo"}, + {start: 3, length: 8, contents: "squiggle"}, + {start: 2, length: 2, contents: "os"}, + {start: 10, length: 2, contents: "eb"}]], + // Test blob, string, blob + [[blob1, "foo", blob1], {}, + [{start: 0, length: 8, contents: "squiggle"}, + {start: 7, length: 2, contents: "ef"}, + {start: 10, length: 2, contents: "os"}, + {start: 1, length: 3, contents: "qui"}, + {start: 12, length: 3, contents: "qui"}, + {start: 40, length: 20, contents: ""}]], + // Test blobs all the way down + [[blob2, blob1, blob2], {}, + [{start: 0, length: 5, contents: "steak"}, + {start: 5, length: 8, contents: "squiggle"}, + {start: 13, length: 5, contents: "steak"}, + {start: 1, length: 2, contents: "te"}, + {start: 6, length: 4, contents: "quig"}]], + // Test an array buffer + [[aB, blob1, "foo"], {}, + [{start: 0, length: 8, contents: "ABCDEFGH"}, + {start: 8, length:10, contents: "IJKLMNOPsq"}, + {start: 17, length: 3, contents: "qui"}, + {start: 4, length: 8, contents: "EFGHIJKL"}]], + // Test an ArrayBufferView + [[int8View, blob1, "foo"], {}, + [{start: 0, length: 8, contents: "ABCDEFGH"}, + {start: 8, length:10, contents: "IJKLMNOPsq"}, + {start: 17, length: 3, contents: "qui"}, + {start: 4, length: 8, contents: "EFGHIJKL"}]], + // Test a partial ArrayBufferView + [[new Uint8Array(aB, 3, 5), blob1, "foo"], {}, + [{start: 0, length: 8, contents: "DEFGHsqu"}, + {start: 8, length:10, contents: "igglefoo"}, + {start: 4, length: 8, contents: "Hsquiggl"}]], + // Test transparent line endings + [["foo\r\n", "bar\r", "baz\n"], { endings: "transparent" }, + [{start: 0, length: 5, contents: "foo\r\n"}, + {start: 5, length: 4, contents: "bar\r"}, + {start: 9, length: 4, contents: "baz\n"}]], + // Test transparent line endings when the second argument is omitted + [["foo\r\n", "bar\r", "baz\n"], undefined, + [{start: 0, length: 5, contents: "foo\r\n"}, + {start: 5, length: 4, contents: "bar\r"}, + {start: 9, length: 4, contents: "baz\n"}]], + // Test native line endings + [["foo\r\n", "bar\r", "baz\n"], { endings: "native" }, + navigator.platform.includes("Win") ? + [{start: 0, length: 5, contents: "foo\r\n"}, + {start: 5, length: 5, contents: "bar\r\n"}, + {start: 10, length: 5, contents: "baz\r\n"}] : + [{start: 0, length: 4, contents: "foo\n"}, + {start: 4, length: 4, contents: "bar\n"}, + {start: 8, length: 4, contents: "baz\n"}]], + // Test type coercion of a number + [[3, int8View, "foo"], {}, + [{start: 0, length: 8, contents: "3ABCDEFG"}, + {start: 8, length:10, contents: "HIJKLMNOPf"}, + {start: 17, length: 4, contents: "foo"}, + {start: 4, length: 8, contents: "DEFGHIJK"}]] + ]; + +let currentTest = null; +let testCounter = 0; + +function runTests() { + if (!currentTest || !currentTest[2].length) { + if (!testData.length) { + SimpleTest.finish(); + return; + } + + currentTest = testData.shift(); + ++testCounter; + } + + let [blobs, options] = currentTest; + let test = currentTest[2].shift(); + + let blob3; + if (options !== undefined) { + blob3 = new Blob(blobs, options); + } else { + blob3 = new Blob(blobs); + } + + ok(blob3, "Test " + testCounter + " got blob"); + ok(blob3 instanceof Blob, "Test " + testCounter + " blob is a Blob"); + ok(!(blob3 instanceof File), "Test " + testCounter + " blob is not a File"); + + let slice = blob3.slice(test.start, test.start + test.length); + ok(slice, "Test " + testCounter + " got slice"); + ok(slice instanceof Blob, "Test " + testCounter + " slice is a Blob"); + ok(!(slice instanceof File), "Test " + testCounter + " slice is not a File"); + is(slice.size, test.contents.length, "Test " + testCounter + " slice is correct size"); + + testBlob(slice, test.contents, "Test " + testCounter).then(() => { + SpecialPowers.gc(); + runTests(); + }); +} + +SimpleTest.requestLongerTimeout(2); +SimpleTest.waitForExplicitFinish(); +runTests(); + +</script> +</pre> +</body> +</html> diff --git a/dom/file/tests/test_bloburi.js b/dom/file/tests/test_bloburi.js new file mode 100644 index 0000000000..3485b8a681 --- /dev/null +++ b/dom/file/tests/test_bloburi.js @@ -0,0 +1,24 @@ +var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + +var uris = [ + { + uri: "blob:https://example.com/230d5d50-35f9-9745-a64a-15e47b731a81", + local: true, + }, + { + uri: "rstp://1.2.3.4/some_path?param=a", + local: false, + }, +]; + +function run_test() { + for (let i = 0; i < uris.length; i++) { + let uri = ios.newURI(uris[i].uri); + let flags = ios.getDynamicProtocolFlags(uri); + + Assert.equal( + Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE & flags, + uris[i].local ? Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE : 0 + ); + } +} diff --git a/dom/file/tests/test_bug1507893.html b/dom/file/tests/test_bug1507893.html new file mode 100644 index 0000000000..f0f83ff9ce --- /dev/null +++ b/dom/file/tests/test_bug1507893.html @@ -0,0 +1,63 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Blob URLs fetched in workers</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script> + +SimpleTest.waitForExplicitFinish(); + +// Let's be positive. +Promise.resolve() + +// Create a file. +.then(_ => { + return new Promise(resolve => { + let openerURL = SimpleTest.getTestFileURL("fileapi_chromeScript.js"); + let opener = SpecialPowers.loadChromeScript(openerURL); + + opener.addMessageListener("files.opened", files => { + resolve(files[0]); + }); + + opener.sendAsyncMessage("files.open", [ "I am the blob content" ]); + }) +}) + +// Just a couple of checks +.then(file => { + ok(file instanceof File, "We want a file"); + ok(file.size > 0, "We have content"); + return file; +}) + +// Let's create a blobURL +.then(file => URL.createObjectURL(file)) + +// Let's send it to a worker. +.then(url => { + return new Promise(resolve => { + let w = new Worker('worker_bug1507893.js'); + w.onmessage = e => { + resolve(e.data); + }; + w.postMessage(url); + }); +}) + +// Let's check the worker's output +.then(blob => { + ok(blob instanceof File, "The worker sends us a blob"); + ok(blob.size > 0, "We have data"); +}) + +// All done. +.then(SimpleTest.finish); + + </script> +</body> +</html> diff --git a/dom/file/tests/test_bug1742540.html b/dom/file/tests/test_bug1742540.html new file mode 100644 index 0000000000..7516f38a85 --- /dev/null +++ b/dom/file/tests/test_bug1742540.html @@ -0,0 +1,83 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1742540</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + <script> + +SimpleTest.waitForExplicitFinish(); + +add_task(function setupPrefs() { + return SpecialPowers.pushPrefEnv({ + set: [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ], + }); +}); + +function get_file() { + return new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", "/dynamic/getMyDirectory.sjs", false); + xhr.send(); + let basePath = xhr.responseText; + + let script = SpecialPowers.loadChromeScript( + SimpleTest.getTestFileURL("create_file_objects.js") + ); + script.addMessageListener("created-file-objects", files => { + resolve(files[0]); + }); + script.sendAsyncMessage("create-file-objects", { + fileNames: [basePath + "file_mozfiledataurl_audio.ogg"], + }); + }); +} + +function wait_for_message(port, expected_message) { + return new Promise(resolve => { + port.onmessage = event => { + port.onmessage = null; + ok(event.data === expected_message, event.data); + resolve(); + }; + }); +} + +function unregister_and_done(registration) { + return registration.unregister().then(() => { + ok(true, "Will find leaks of nsPipe in BloatView without fix."); + SimpleTest.finish; + }); +} + +add_task(async function send_file_to_serviceworker() { + let registration = await navigator.serviceWorker + .register("worker_bug1742540.js", { scope: "./" }) + .then(() => { + return navigator.serviceWorker.ready; + }); + + ok(registration.active, "ServiceWorker is activated"); + + let file = await get_file(); + ok(file.size > 100000, "File size is big enough."); + let message = "ServiceWorker receives a file and did not reference it."; + let channel = new MessageChannel(); + let received = wait_for_message(channel.port1, message); + registration.active.postMessage({ port: channel.port2, message, file }, [ + channel.port2, + ]); + await received; + + let finish = await unregister_and_done(registration); +}); + + </script> +</body> +</html> diff --git a/dom/file/tests/test_createFile.js b/dom/file/tests/test_createFile.js new file mode 100644 index 0000000000..ebda3d4dae --- /dev/null +++ b/dom/file/tests/test_createFile.js @@ -0,0 +1,52 @@ +add_task(async function () { + do_get_profile(); + + let existingFile = Services.dirsvc + .QueryInterface(Ci.nsIProperties) + .get("ProfD", Ci.nsIFile); + existingFile.append("exists.js"); + existingFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); + + var outStream = Cc[ + "@mozilla.org/network/file-output-stream;1" + ].createInstance(Ci.nsIFileOutputStream); + outStream.init( + existingFile, + 0x02 | 0x08 | 0x20, // write, create, truncate + 0o666, + 0 + ); + + var fileData = "Hello World!"; + outStream.write(fileData, fileData.length); + outStream.close(); + + ok(existingFile.exists(), "exists.js exists"); + + let unknownFile = Services.dirsvc + .QueryInterface(Ci.nsIProperties) + .get("TmpD", Ci.nsIFile); + unknownFile.append("wow.txt"); + + ok(!unknownFile.exists(), unknownFile.path + " doesn't exist"); + + let a = await File.createFromNsIFile(existingFile, { existenceCheck: false }); + ok(a.size != 0, "The size is correctly set"); + + let b = await File.createFromNsIFile(unknownFile, { existenceCheck: false }); + ok(b.size == 0, "The size is 0 for unknown file"); + + let c = await File.createFromNsIFile(existingFile, { existenceCheck: true }); + ok(c.size != 0, "The size is correctly set"); + + let d = await File.createFromNsIFile(unknownFile, { + existenceCheck: true, + }).then( + _ => true, + _ => false + ); + ok(d === false, "Exception thrown"); + + existingFile.remove(true); + ok(!existingFile.exists(), "exists.js doesn't exist anymore"); +}); diff --git a/dom/file/tests/test_file_from_blob.html b/dom/file/tests/test_file_from_blob.html new file mode 100644 index 0000000000..8c12a2823a --- /dev/null +++ b/dom/file/tests/test_file_from_blob.html @@ -0,0 +1,110 @@ +<!doctype html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=819900 +--> + <head> +<title>Test for crash caused by unloading and reloading srcdoc iframes</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=819900">Mozilla Bug 819900</a> + +<pre id="test"> +<script> + + var b = new Blob(['1234567890']); + ok(b, 'Blob created'); + is(b.size, 10, 'Blob has the right size'); + + var status = false; + try { + f = new File(b); + } catch(e) { + status = true; + } + ok(status, "File throws if the second argument is missing"); + + status = false; + try { + f = new File(42, 'foobar.txt'); + } catch(e) { + status = true; + } + ok(status, "File throws if the argument is not an array"); + + status = false; + try { + f = new File({}, 'foobar.txt'); + } catch(e) { + status = true; + } + ok(status, "File throws if the argument is not an array"); + + status = false; + try { + f = new File("hello world", 'foobar.txt'); + } catch(e) { + status = true; + } + ok(status, "File throws if the argument is not an array"); + + f = new File(['1234567890'], ''); + ok(f, 'File created'); + is(f.size, 10, 'File has the right size'); + is(f.name, ''); + is(f.type, ''); + + f = new File(['1234567890'], 42); + ok(f, 'File created'); + is(f.size, 10, 'File has the right size'); + is(f.name, '42'); + is(f.type, ''); + + f = new File(['1234567890'], 'text.txt'); + ok(f, 'File created'); + is(f.size, 10, 'File has the right size'); + is(f.name, 'text.txt'); + is(f.type, ''); + + f = new File(['1234567890'], 'text.txt', { type: 'plain/text' }); + ok(f, 'File created'); + is(f.size, 10, 'File has the right size'); + is(f.name, 'text.txt'); + is(f.type, 'plain/text'); + + f = new File([b], 'text.txt'); + ok(f, 'File created'); + is(f.name, 'text.txt'); + is(f.type, ''); + is(f.size, b.size); + + f = new File([b], 'test.txt', { type: 'plain/text' }); + ok(f, 'File created'); + is(f.name, 'test.txt'); + is(f.type, 'plain/text'); + is(f.size, b.size); + + f = new File([b, b], 'test.txt', { type: 'plain/text' }); + ok(f, 'File created'); + is(f.name, 'test.txt'); + is(f.type, 'plain/text'); + is(f.size, b.size * 2); + + var f2 = new File([f, f], 'test.txt', { type: 'plain/text' }); + ok(f2, 'File created'); + is(f2.name, 'test.txt'); + is(f2.type, 'plain/text'); + is(f2.size, f.size * 2); + + var f2 = new File([f, f], 'test.txt', b); + ok(f2, 'File created'); + is(f2.name, 'test.txt'); + is(f2.type, b.type); + is(f2.size, f.size * 2); + +</script> +</pre> +</body> +</html> diff --git a/dom/file/tests/test_file_negative_date.html b/dom/file/tests/test_file_negative_date.html new file mode 100644 index 0000000000..2e8528b88d --- /dev/null +++ b/dom/file/tests/test_file_negative_date.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1158437 +--> +<head> + <title>Test for negative date in File (Bug 1158437)</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script 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=1158437">Mozilla Bug 1158437</a> + +<script class="testbody" type="text/javascript"> +"use strict"; + +var blob = new Blob(['hello world']); +var f1 = new File([blob], 'f1.txt', { lastModified: 0 }); +var f2 = new File([blob], 'f2.txt', { lastModified: -1 }); +var f3 = new File([blob], 'f3.txt', { lastModified: -1000 }); + +is(f1.lastModified, 0, "lastModified == 0 is supported"); +is(f2.lastModified, -1, "lastModified == -1 is supported"); +is(f3.lastModified, -1000, "lastModified == -1000 is supported"); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_basic.html b/dom/file/tests/test_fileapi_basic.html new file mode 100644 index 0000000000..7f7aed788e --- /dev/null +++ b/dom/file/tests/test_fileapi_basic.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader API</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="common_fileReader.js"></script> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(3); + +test_setup() +.then(data => { + return runBasicTests(data); +}) +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_basic_worker.html b/dom/file/tests/test_fileapi_basic_worker.html new file mode 100644 index 0000000000..03db1b4cb6 --- /dev/null +++ b/dom/file/tests/test_fileapi_basic_worker.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader API in workers</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="common_fileReader.js"></script> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(3); + +test_setup() +.then(data => { + let worker = new Worker('worker_fileReader.js'); + worker.postMessage({ tests: 'basic', data }); + + worker.onmessage = event => { + if (event.data.type == 'finish') { + SimpleTest.finish(); + return; + } + + if (event.data.type == 'check') { + ok(event.data.status, event.data.msg); + return; + } + + ok(false, "Unknown message."); + } +}); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_encoding.html b/dom/file/tests/test_fileapi_encoding.html new file mode 100644 index 0000000000..f20a46ac6c --- /dev/null +++ b/dom/file/tests/test_fileapi_encoding.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader API</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="common_fileReader.js"></script> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(3); + +test_setup() +.then(data => { + return runEncodingTests(data); +}) +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_encoding_worker.html b/dom/file/tests/test_fileapi_encoding_worker.html new file mode 100644 index 0000000000..ab6827f086 --- /dev/null +++ b/dom/file/tests/test_fileapi_encoding_worker.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader API in workers</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="common_fileReader.js"></script> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(3); + +test_setup() +.then(data => { + let worker = new Worker('worker_fileReader.js'); + worker.postMessage({ tests: 'encoding', data }); + + worker.onmessage = event => { + if (event.data.type == 'finish') { + SimpleTest.finish(); + return; + } + + if (event.data.type == 'check') { + ok(event.data.status, event.data.msg); + return; + } + + ok(false, "Unknown message."); + } +}); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_other.html b/dom/file/tests/test_fileapi_other.html new file mode 100644 index 0000000000..6c01415be8 --- /dev/null +++ b/dom/file/tests/test_fileapi_other.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader API</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="common_fileReader.js"></script> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(3); + +test_setup() +.then(data => { + return runOtherTests(data); +}) +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_other_worker.html b/dom/file/tests/test_fileapi_other_worker.html new file mode 100644 index 0000000000..a535d3fcb4 --- /dev/null +++ b/dom/file/tests/test_fileapi_other_worker.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader API in workers</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="common_fileReader.js"></script> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(3); + +test_setup() +.then(data => { + let worker = new Worker('worker_fileReader.js'); + worker.postMessage({ tests: 'other', data }); + + worker.onmessage = event => { + if (event.data.type == 'finish') { + SimpleTest.finish(); + return; + } + + if (event.data.type == 'check') { + ok(event.data.status, event.data.msg); + return; + } + + ok(false, "Unknown message."); + } +}); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_slice_image.html b/dom/file/tests/test_fileapi_slice_image.html new file mode 100644 index 0000000000..c873afa438 --- /dev/null +++ b/dom/file/tests/test_fileapi_slice_image.html @@ -0,0 +1,139 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for File API + Slice</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="common_blob.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> + +<p id="display"> + <canvas id=canvas width=1100 height=1100 hidden moz-opaque></canvas> + <canvas id=testcanvas hidden moz-opaque></canvas> +</p> + +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(4); + +// Create files containing data we'll test with. We'll want long +// strings to ensure they span multiple buffers while loading + +let canvasData; +let testBinaryData; + +function imageLoadHandler(event, resolve) { + let origcanvas = $("canvas"); + let testcanvas = $("testcanvas"); + let image = event.target; + is(image.naturalWidth, origcanvas.width, "width correct"); + is(image.naturalHeight, origcanvas.height, "height correct"); + + testcanvas.width = origcanvas.width; + testcanvas.height = origcanvas.height; + testcanvas.getContext("2d").drawImage(image, 0, 0); + // Do not use |is(testcanvas.toDataURL("image/png"), origcanvas.toDataURL("image/png"), "...");| that results in a _very_ long line. + let origDataURL = origcanvas.toDataURL("image/png"); + let testDataURL = testcanvas.toDataURL("image/png"); + is(testDataURL.length, origDataURL.length, + "Length of correct image data"); + ok(testDataURL == origDataURL, + "Content of correct image data"); + resolve(); +} + +createCanvasURL() +.then(data => { + for (var i = 0; i < 256; i++) { + testBinaryData += String.fromCharCode(i); + } + while (testBinaryData.length < 20000) { + testBinaryData += testBinaryData; + } + + canvasData = data; +}) + +// image in the middle +.then(() => { + return createFile(testBinaryData + canvasData + testBinaryData, "middleTestFile"); +}) + +// image in the middle - loader +.then(file => { + return new Promise(resolve => { + is(file.size, canvasData.length + testBinaryData.length * 2, "correct file size (middle)"); + + var img = new Image(); + img.src = URL.createObjectURL(file.slice(testBinaryData.length, + testBinaryData.length + canvasData.length)); + img.onload = event => { + imageLoadHandler(event, resolve); + } + }); +}) + +// image at start +.then(() => { + return createFile(canvasData + testBinaryData, "startTestFile"); +}) + +// image at start - loader +.then(file => { + return new Promise(resolve => { + is(file.size, canvasData.length + testBinaryData.length, "correct file size (start)"); + + var img = new Image(); + img.src = URL.createObjectURL(file.slice(0, canvasData.length)); + img.onload = event => { + imageLoadHandler(event, resolve); + } + }); +}) + +// image at end +.then(() => { + return createFile(testBinaryData + canvasData, "endTestFile"); +}) + +// image at end - loader +.then(file => { + return new Promise(resolve => { + is(file.size, canvasData.length + testBinaryData.length, "correct file size (end)"); + + var img = new Image(); + img.src = URL.createObjectURL(file.slice(testBinaryData.length, + testBinaryData.length + canvasData.length)); + img.onload = event => { + imageLoadHandler(event, resolve); + } + }); +}) + +// image past end +.then(() => { + return createFile(testBinaryData + canvasData, "pastEndTestFile"); +}) + +// image past end - loader +.then(file => { + return new Promise(resolve => { + is(file.size, canvasData.length + testBinaryData.length, "correct file size (end)"); + + var img = new Image(); + img.src = URL.createObjectURL(file.slice(testBinaryData.length, + testBinaryData.length + canvasData.length + 1000)); + img.onload = event => { + imageLoadHandler(event, resolve); + } + }); +}) + +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_slice_memFile_1.html b/dom/file/tests/test_fileapi_slice_memFile_1.html new file mode 100644 index 0000000000..56d63d15ba --- /dev/null +++ b/dom/file/tests/test_fileapi_slice_memFile_1.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for File API + Slice (in memory)</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="common_blob.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> + +<p id="display"> + <canvas id=canvas width=1100 height=1100 hidden moz-opaque></canvas> +</p> + +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(4); + +createCanvasURL() +.then(data => { + let cx = $("canvas").getContext('2d'); + return Promise.all([toBlobPromise(cx.canvas), + Promise.resolve(data)]); +}) + +.then(args => { + let [memFile, data] = args; + return testSlice(memFile, data.length, "image/png", data, "memFile", RANGE_1); +}) + +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_slice_memFile_2.html b/dom/file/tests/test_fileapi_slice_memFile_2.html new file mode 100644 index 0000000000..aef8813bf5 --- /dev/null +++ b/dom/file/tests/test_fileapi_slice_memFile_2.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for File API + Slice (in memory)</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="common_blob.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> + +<p id="display"> + <canvas id=canvas width=1100 height=1100 hidden moz-opaque></canvas> +</p> + +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(4); + +createCanvasURL() +.then(data => { + let cx = $("canvas").getContext('2d'); + return Promise.all([toBlobPromise(cx.canvas), + Promise.resolve(data)]); +}) + +.then(args => { + let [memFile, data] = args; + return testSlice(memFile, data.length, "image/png", data, "memFile", RANGE_2); +}) + +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_slice_realFile_1.html b/dom/file/tests/test_fileapi_slice_realFile_1.html new file mode 100644 index 0000000000..97798d805a --- /dev/null +++ b/dom/file/tests/test_fileapi_slice_realFile_1.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for File API + Slice (in file)</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="common_blob.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> + +<p id="display"> + <canvas id=canvas width=1100 height=1100 hidden moz-opaque></canvas> +</p> + +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(4); + +let canvasData; + +createCanvasURL() +.then(data => { + canvasData = data; + return createFile(data, "basicTestFile1"); +}) + +.then(file => { + return testSlice(file, canvasData.length, "", canvasData, "fileFile", RANGE_1); +}) + +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_slice_realFile_2.html b/dom/file/tests/test_fileapi_slice_realFile_2.html new file mode 100644 index 0000000000..882ce61c03 --- /dev/null +++ b/dom/file/tests/test_fileapi_slice_realFile_2.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for File API + Slice (in file)</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="common_blob.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> + +<p id="display"> + <canvas id=canvas width=1100 height=1100 hidden moz-opaque></canvas> +</p> + +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(4); + +let canvasData; + +createCanvasURL() +.then(data => { + canvasData = data; + return createFile(data, "basicTestFile2"); +}) + +.then(file => { + return testSlice(file, canvasData.length, "", canvasData, "fileFile", RANGE_2); +}) + +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_twice.html b/dom/file/tests/test_fileapi_twice.html new file mode 100644 index 0000000000..96e7febdce --- /dev/null +++ b/dom/file/tests/test_fileapi_twice.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader API</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="common_fileReader.js"></script> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(3); + +test_setup() +.then(data => { + return runTwiceTests(data); +}) +.then(SimpleTest.finish); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_fileapi_twice_worker.html b/dom/file/tests/test_fileapi_twice_worker.html new file mode 100644 index 0000000000..a79d3992b2 --- /dev/null +++ b/dom/file/tests/test_fileapi_twice_worker.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader API in workers</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="common_fileReader.js"></script> +</head> + +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(3); + +test_setup() +.then(data => { + let worker = new Worker('worker_fileReader.js'); + worker.postMessage({ tests: 'twice', data }); + + worker.onmessage = event => { + if (event.data.type == 'finish') { + SimpleTest.finish(); + return; + } + + if (event.data.type == 'check') { + ok(event.data.status, event.data.msg); + return; + } + + ok(false, "Unknown message."); + } +}); + +</script> +</body> +</html> diff --git a/dom/file/tests/test_ipc_messagemanager_blob.js b/dom/file/tests/test_ipc_messagemanager_blob.js new file mode 100644 index 0000000000..dacdc9e7bb --- /dev/null +++ b/dom/file/tests/test_ipc_messagemanager_blob.js @@ -0,0 +1,102 @@ +"use strict"; + +const { XPCShellContentUtils } = ChromeUtils.importESModule( + "resource://testing-common/XPCShellContentUtils.sys.mjs" +); + +XPCShellContentUtils.init(this); + +function childFrameScript() { + addMessageListener("test:ipcClonedMessage", function (message) { + if (!Blob.isInstance(message.json)) { + sendAsyncMessage(message.name, message.json); + return; + } + + let reader = new FileReader(); + reader.addEventListener("load", function () { + let response = + reader.result == "this is a great success!" ? message.json : "error"; + sendAsyncMessage(message.name, response); + }); + reader.readAsText(message.json); + }); +} + +add_task(async function test() { + let page = await XPCShellContentUtils.loadContentPage("about:blank", { + remote: true, + }); + + page.loadFrameScript(childFrameScript); + + const blobString = "this is a great success!"; + + const messages = [ + "hi!", + "", + 2, + -0.04, + 34329873249872400000000000000, + true, + false, + null, + 0, + + // Make sure this one is always last. + new Blob(["this ", "is ", "a ", "great ", "success!"], { + type: "text/plain", + }), + ]; + let receivedMessageIndex = 0; + + let mm = page.browser.messageManager; + let done = new Promise(resolve => { + mm.addMessageListener("test:ipcClonedMessage", async message => { + let data = message.json; + + if (Blob.isInstance(data)) { + equal(receivedMessageIndex, messages.length - 1, "Blob is last"); + equal( + data.size, + messages[receivedMessageIndex].size, + "Correct blob size" + ); + equal( + data.type, + messages[receivedMessageIndex].type, + "Correct blob type" + ); + + let reader1 = new FileReader(); + reader1.readAsText(data); + + let reader2 = new FileReader(); + reader2.readAsText(messages[receivedMessageIndex]); + + await Promise.all([ + new Promise(res => (reader1.onload = res)), + new Promise(res => (reader2.onload = res)), + ]); + + equal(reader1.result, blobString, "Result 1"); + equal(reader2.result, blobString, "Result 2"); + + resolve(); + } else { + equal( + data, + messages[receivedMessageIndex++], + "Got correct round-tripped response" + ); + } + }); + }); + + for (let message of messages) { + mm.sendAsyncMessage("test:ipcClonedMessage", message); + } + + await done; + await page.close(); +}); diff --git a/dom/file/tests/test_mozfiledataurl.html b/dom/file/tests/test_mozfiledataurl.html new file mode 100644 index 0000000000..68c88fa28c --- /dev/null +++ b/dom/file/tests/test_mozfiledataurl.html @@ -0,0 +1,224 @@ +<!DOCTYPE HTML> +<html> +<head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> + <title>Test for File urls</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="start()"> +<p id="display"> +<iframe id=inner></iframe> +<iframe id=iframe></iframe> +<img id=img onload="gen.next(event);"> +<audio id=audio onloadeddata="gen.next(event);"> +</p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="application/javascript"> + +try { + URL.createObjectURL(undefined); +} catch(e) { } + +window.addEventListener("message", function(e) { + gen.next(JSON.parse(e.data)); +}); + +const innerSameSiteURI = "file_mozfiledataurl_inner.html"; +const innerCrossSiteURI = "http://example.com/tests/dom/file/tests/file_mozfiledataurl_inner.html" + +var fileNames = ["file_mozfiledataurl_img.jpg", + "file_mozfiledataurl_audio.ogg", + "file_mozfiledataurl_doc.html", + "file_mozfiledataurl_text.txt"]; + +function start() { + let xhr = new XMLHttpRequest; + xhr.open("GET", "/dynamic/getMyDirectory.sjs", false); + xhr.send(); + let basePath = xhr.responseText; + + let fullFileNames = []; + for (let name of fileNames) { + fullFileNames.push(basePath + name); + } + + var script = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("create_file_objects.js")); + + script.addMessageListener("created-file-objects", function handler(files) { + script.removeMessageListener("created-file-objects", handler); + gen = runTest(files); + gen.next(); + }); + + script.sendAsyncMessage("create-file-objects", {fileNames: fullFileNames}); +}; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.expectAssertions(0, 3); + +function* runTest([imgFile, audioFile, docFile, xhrFile]) { + inner = document.getElementById('inner'); + img = document.getElementById('img'); + audio = document.getElementById('audio'); + iframe = document.getElementById('iframe'); + inner.onload = function() { gen.next("inner loaded"); }; + + // Attempt to load a image in this document + var fileurl = URL.createObjectURL(imgFile); + img.src = fileurl; + var e = (yield); + is(e.type, "load", "loaded successfully"); + is(img.width, 120, "correct width"); + is(img.height, 90, "correct height"); + + // Revoke url and attempt to load a image in this document + img.src = "file_mozfiledataurl_img.jpg"; + is((yield).type, "load", "successfull reset image"); + URL.revokeObjectURL(fileurl); + todo(false, "urls need to act like 404s, not fail to parse"); +/* img.src = fileurl; + var e = (yield); + is(e.type, "error", "failed successfully"); + isnot(img.width, 120, "correct error width"); + isnot(img.height, 90, "correct error height"); +*/ + // Generate new fileurl and make sure it's different from the old + var oldFileurl = fileurl; + fileurl = URL.createObjectURL(imgFile); + isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice"); + + // Attempt to load an image in a different same-origin document + inner.src = innerSameSiteURI; + yield undefined; + inner.contentWindow.postMessage(JSON.stringify({img:fileurl}), "*"); + var res = (yield); + is(res.type, "load", "loaded successfully"); + is(res.width, 120, "correct width"); + is(res.height, 90, "correct height"); + + // Attempt to load an image in a different cross-origin document + inner.src = innerCrossSiteURI; + yield undefined; + inner.contentWindow.postMessage(JSON.stringify({img:fileurl}), "*"); + var res = (yield); + is(res.type, "error", "failed successfully"); + isnot(res.width, 120, "correct error width"); + isnot(res.height, 90, "correct error height"); + + // Attempt to load an audio in this document + fileurl = URL.createObjectURL(audioFile); + audio.src = fileurl; + var e = (yield); + is(e.type, "loadeddata", "loaded successfully"); + + // Revoke url and attempt to load a audio in this document + audio.src = "file_mozfiledataurl_audio.ogg"; + is((yield).type, "loadeddata", "successfully reset audio"); + URL.revokeObjectURL(fileurl); + todo(false, "urls need to act like 404s, not fail to parse"); +/* img.src = fileurl; + var e = (yield); + is(e.type, "error", "failed successfully"); + isnot(img.width, 120, "correct error width"); + isnot(img.height, 90, "correct error height"); +*/ + // Generate new fileurl and make sure it's different from the old + var oldFileurl = fileurl; + fileurl = URL.createObjectURL(audioFile); + isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice"); + + // Attempt to load an audio in a different same-origin document + inner.src = innerSameSiteURI; + yield undefined; + inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*"); + var res = (yield); + is(res.type, "loadeddata", "loaded successfully"); + + // Attempt to load an audio in a different cross-origin document + inner.src = innerCrossSiteURI; + yield undefined; + inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*"); + var res = (yield); + is(res.type, "error", "failed successfully"); + + // Attempt to load a HTML document in an iframe in this document + iframe.onload = function() { gen.next(); }; + iframe.src = "file_mozfiledataurl_doc.html"; + yield undefined; + is(iframe.contentDocument.getElementsByTagName("p")[0].textContent, + "This here is a document!", + "iframe loaded successfully"); + is(iframe.contentDocument.getElementById("img").width, 120, + "image in iframe width"); + is(iframe.contentDocument.getElementById("img").height, 90, + "image in iframe height"); + + // Attempt to load a HTML document in an iframe in this document, using file url + fileurl = URL.createObjectURL(docFile); + iframe.src = fileurl; + yield undefined; + is(iframe.contentDocument.getElementsByTagName("p")[0].textContent, + "This here is a document!", + "iframe loaded successfully"); + isnot(iframe.contentDocument.getElementById("img").width, 120, + "failed image in iframe width"); + isnot(iframe.contentDocument.getElementById("img").height, 90, + "failed image in iframe height"); + + // Attempt to load a HTML document in an iframe in inner document + inner.src = innerSameSiteURI; + is((yield), "inner loaded", "correct gen.next()"); + inner.contentWindow.postMessage(JSON.stringify({iframe:"file_mozfiledataurl_doc.html"}), "*"); + var res = (yield); + is(res.type, "load", "loaded successfully"); + is(res.text, "This here is a document!", "loaded successfully"); + is(res.imgWidth, 120, "correct width"); + + // Attempt to load a HTML document in an iframe in inner document, using file url + inner.contentWindow.postMessage(JSON.stringify({iframe:fileurl}), "*"); + var res = (yield); + is(res.type, "load", "loaded successfully"); + is(res.text, "This here is a document!", "loaded successfully"); + isnot(res.imgWidth, 120, "correct width"); + + // Attempt to load a HTML document in an iframe in inner cross-site document, using file url + inner.src = innerCrossSiteURI; + is((yield), "inner loaded", "correct gen.next()"); + inner.contentWindow.postMessage(JSON.stringify({iframe:fileurl}), "*"); + var res = (yield); + is(res.type, "error", "load failed successfully"); + + // Attempt to load file url using XHR + fileurl = URL.createObjectURL(xhrFile); + xhr = new XMLHttpRequest; + xhr.onload = function() { gen.next("XHR finished"); }; + xhr.open("GET", fileurl); + xhr.send(); + is((yield), "XHR finished", "correct gen.next()"); + xhr.responseText == "Yarr, here be plaintext file, ya landlubber\n"; + + // Attempt to load file url using XHR in inner document + inner.src = innerSameSiteURI; + is((yield), "inner loaded", "correct gen.next()"); + inner.contentWindow.postMessage(JSON.stringify({xhr:fileurl}), "*"); + var res = (yield); + is(res.didThrow, undefined, "load successful"); + is(res.text, "Yarr, here be plaintext file, ya landlubber\n", "load successful"); + + // Attempt to load file url using XHR + inner.src = innerCrossSiteURI; + is((yield), "inner loaded", "correct gen.next()"); + inner.contentWindow.postMessage(JSON.stringify({xhr:fileurl}), "*"); + var res = (yield); + is(res.didError, true, "load failed successfully"); + + SimpleTest.finish(); +} +</script> +</pre> +</body> +</html> diff --git a/dom/file/tests/test_nonascii_blob_url.html b/dom/file/tests/test_nonascii_blob_url.html new file mode 100644 index 0000000000..1c6c833958 --- /dev/null +++ b/dom/file/tests/test_nonascii_blob_url.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test blob URL for non-ascii domain</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<div id="content"></div> +<script class="testbody" type="text/javascript"> + +var iframe = document.createElement('iframe'); +iframe.src = 'http://xn--exmple-cua.test/tests/dom/file/tests/file_nonascii_blob_url.html'; +iframe.onload = function() { + iframe.contentWindow.postMessage('hello world', '*'); + onmessage = function(e) { + is(e.data, 'hello world', "Blob URL for non-ascii domain works"); + SimpleTest.finish(); + } +} + +document.getElementById('content').appendChild(iframe); +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> diff --git a/dom/file/tests/worker_blob_reading.js b/dom/file/tests/worker_blob_reading.js new file mode 100644 index 0000000000..f57161c220 --- /dev/null +++ b/dom/file/tests/worker_blob_reading.js @@ -0,0 +1,26 @@ +importScripts("common_blob_reading.js"); + +function info(message) { + postMessage({ type: "info", message }); +} + +function ok(a, message) { + postMessage({ type: "test", test: !!a, message }); +} + +function is(a, b, message) { + ok(a === b, message); +} + +onmessage = function (e) { + self[e.data.func](e.data.blob, e.data.content).then( + () => { + postMessage({ type: "done" }); + }, + exc => { + dump(exc); + dump(exc.stack); + postMessage({ type: "error", message: exc.toString() }); + } + ); +}; diff --git a/dom/file/tests/worker_bug1507893.js b/dom/file/tests/worker_bug1507893.js new file mode 100644 index 0000000000..06fce2b2ef --- /dev/null +++ b/dom/file/tests/worker_bug1507893.js @@ -0,0 +1,5 @@ +onmessage = e => { + fetch(e.data) + .then(r => r.blob()) + .then(blob => postMessage(blob)); +}; diff --git a/dom/file/tests/worker_bug1742540.js b/dom/file/tests/worker_bug1742540.js new file mode 100644 index 0000000000..c0330ccb56 --- /dev/null +++ b/dom/file/tests/worker_bug1742540.js @@ -0,0 +1,5 @@ +onmessage = e => { + let file = e.data.file; + let port = e.data.port; + port.postMessage(e.data.message); +}; diff --git a/dom/file/tests/worker_fileReader.js b/dom/file/tests/worker_fileReader.js new file mode 100644 index 0000000000..2e8408d1bb --- /dev/null +++ b/dom/file/tests/worker_fileReader.js @@ -0,0 +1,30 @@ +importScripts("common_fileReader.js"); + +function ok(a, msg) { + postMessage({ type: "check", msg, status: !!a }); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +onmessage = event => { + let p; + + if (event.data.tests == "basic") { + p = runBasicTests(event.data.data); + } else if (event.data.tests == "encoding") { + p = runEncodingTests(event.data.data); + } else if (event.data.tests == "twice") { + p = runTwiceTests(event.data.data); + } else if (event.data.tests == "other") { + p = runOtherTests(event.data.data); + } else { + postMessage({ type: "error" }); + return; + } + + p.then(() => { + postMessage({ type: "finish" }); + }); +}; diff --git a/dom/file/tests/xpcshell.ini b/dom/file/tests/xpcshell.ini new file mode 100644 index 0000000000..2f817948d3 --- /dev/null +++ b/dom/file/tests/xpcshell.ini @@ -0,0 +1,8 @@ +[DEFAULT] + +[test_bloburi.js] +[test_createFile.js] +[test_ipc_messagemanager_blob.js] +skip-if = os == "android" +[test_agentcluster_bloburl.js] +skip-if = os == "android" |