Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

1
test/fixtures/wpt/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.py

View File

@ -0,0 +1,68 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Blob methods from detached frame work as expected</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="emptyDocumentIframe" src="../support/empty-document.html"></iframe>
<script>
const BlobPrototypeFromDetachedFramePromise = new Promise(resolve => {
emptyDocumentIframe.onload = () => {
const BlobPrototype = emptyDocumentIframe.contentWindow.Blob.prototype;
emptyDocumentIframe.remove();
resolve(BlobPrototype);
};
});
const charCodeArrayToString = charCodeArray => Array.from(charCodeArray, c => String.fromCharCode(c)).join("");
const charCodeBufferToString = charCodeBuffer => charCodeArrayToString(new Uint8Array(charCodeBuffer));
promise_test(async () => {
const { slice } = await BlobPrototypeFromDetachedFramePromise;
const blob = new Blob(["foobar"]);
const slicedBlob = slice.call(blob, 1, 3);
assert_true(slicedBlob instanceof Blob);
assert_equals(await slicedBlob.text(), "oo");
assert_equals(charCodeBufferToString(await slicedBlob.arrayBuffer()), "oo");
assert_equals(charCodeArrayToString(await slicedBlob.bytes()), "oo");
const reader = slicedBlob.stream().getReader();
const { value } = await reader.read();
assert_equals(charCodeArrayToString(value), "oo");
}, "slice()");
promise_test(async () => {
const { text } = await BlobPrototypeFromDetachedFramePromise;
const blob = new Blob(["foo"]);
assert_equals(await text.call(blob), "foo");
}, "text()");
promise_test(async () => {
const { arrayBuffer } = await BlobPrototypeFromDetachedFramePromise;
const blob = new Blob(["bar"]);
const charCodeBuffer = await arrayBuffer.call(blob);
assert_equals(charCodeBufferToString(charCodeBuffer), "bar");
}, "arrayBuffer()");
promise_test(async () => {
const { bytes } = await BlobPrototypeFromDetachedFramePromise;
const blob = new Blob(["bar"]);
const charCodeBytes = await bytes.call(blob);
assert_equals(charCodeArrayToString(charCodeBytes), "bar");
}, "bytes()");
promise_test(async () => {
const { stream } = await BlobPrototypeFromDetachedFramePromise;
const blob = new Blob(["baz"]);
const reader = stream.call(blob).getReader();
const { value } = await reader.read();
assert_equals(charCodeArrayToString(value), "baz");
}, "stream()");
</script>

View File

@ -0,0 +1,276 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<!-- Pull in executor_path needed by newPopup / newIframe -->
<script src="/html/cross-origin-embedder-policy/credentialless/resources/common.js"></script>
<!-- Pull in importScript / newPopup / newIframe -->
<script src="/html/anonymous-iframe/resources/common.js"></script>
<body>
<script>
const did_revoke_response = "URL.revokeObjectURL did revoke";
const did_not_revoke_response = "URL.revokeObjectURL did not revoke";
const can_blob_url_be_revoked_js = (blob_url, response_queue_name) => `
async function test() {
if (!('revokeObjectURL' in URL)) {
return send("${response_queue_name}", "URL.revokeObjectURL is not exposed");
}
try {
var blob = await fetch("${blob_url}").then(response => response.blob());
await blob.text();
} catch {
return send("${response_queue_name}", "Blob URL invalid");
}
try {
URL.revokeObjectURL("${blob_url}");
} catch(e) {
return send("${response_queue_name}", e.toString());
}
try {
const blob = await fetch("${blob_url}").then(response => response.blob());
} catch(e) {
return send("${response_queue_name}", "${did_revoke_response}");
}
return send("${response_queue_name}", "${did_not_revoke_response}");
}
await test();
`;
const add_iframe_js = (iframe_origin, response_queue_uuid) => `
const importScript = ${importScript};
await importScript("/html/cross-origin-embedder-policy/credentialless" +
"/resources/common.js");
await importScript("/html/anonymous-iframe/resources/common.js");
await importScript("/common/utils.js");
await send("${response_queue_uuid}", newIframe("${iframe_origin}"));
`;
const same_site_origin = get_host_info().HTTPS_ORIGIN;
const cross_site_origin = get_host_info().HTTPS_NOTSAMESITE_ORIGIN;
async function create_test_iframes(t, response_queue_uuid) {
// Create a same-origin iframe in a cross-site popup.
const not_same_site_popup_uuid = newPopup(t, cross_site_origin);
await send(not_same_site_popup_uuid,
add_iframe_js(same_site_origin, response_queue_uuid));
const iframe_1_uuid = await receive(response_queue_uuid);
// Create a same-origin iframe in a same-site popup.
const same_origin_popup_uuid = newPopup(t, same_site_origin);
await send(same_origin_popup_uuid,
add_iframe_js(same_site_origin, response_queue_uuid));
const iframe_2_uuid = await receive(response_queue_uuid);
return [iframe_1_uuid, iframe_2_uuid];
}
promise_test(t => {
return new Promise(async (resolve, reject) => {
try {
const response_queue_uuid = token();
const [iframe_1_uuid, iframe_2_uuid] =
await create_test_iframes(t, response_queue_uuid);
const blob = new Blob(["blob data"], {type : "text/plain"});
const blob_url = window.URL.createObjectURL(blob);
t.add_cleanup(() => window.URL.revokeObjectURL(blob_url));
await send(iframe_1_uuid,
can_blob_url_be_revoked_js(blob_url, response_queue_uuid));
var response = await receive(response_queue_uuid);
if (response !== did_not_revoke_response) {
reject(`Blob URL was revoked in not-same-top-level-site iframe: ${response}`);
}
await send(iframe_2_uuid,
can_blob_url_be_revoked_js(blob_url, response_queue_uuid));
response = await receive(response_queue_uuid);
if (response !== did_revoke_response) {
reject(`Blob URL wasn't revoked in same-top-level-site iframe: ${response}`);
}
resolve();
} catch (e) {
reject(e);
}
});
}, "Blob URL shouldn't be revocable from a cross-partition iframe");
const newWorker = (origin) => {
const worker_token = token();
const worker_url = origin + executor_worker_path + `&uuid=${worker_token}`;
const worker = new Worker(worker_url);
return worker_token;
}
promise_test(t => {
return new Promise(async (resolve, reject) => {
try {
const response_queue_uuid = token();
const create_worker_js = (origin) => `
const importScript = ${importScript};
await importScript("/html/cross-origin-embedder-policy/credentialless" +
"/resources/common.js");
await importScript("/html/anonymous-iframe/resources/common.js");
await importScript("/common/utils.js");
const newWorker = ${newWorker};
await send("${response_queue_uuid}", newWorker("${origin}"));
`;
const [iframe_1_uuid, iframe_2_uuid] =
await create_test_iframes(t, response_queue_uuid);
// Create a dedicated worker in the cross-top-level-site iframe.
await send(iframe_1_uuid, create_worker_js(same_site_origin));
const worker_1_uuid = await receive(response_queue_uuid);
// Create a dedicated worker in the same-top-level-site iframe.
await send(iframe_2_uuid, create_worker_js(same_site_origin));
const worker_2_uuid = await receive(response_queue_uuid);
const blob = new Blob(["blob data"], {type : "text/plain"});
const blob_url = window.URL.createObjectURL(blob);
t.add_cleanup(() => window.URL.revokeObjectURL(blob_url));
await send(worker_1_uuid,
can_blob_url_be_revoked_js(blob_url, response_queue_uuid));
var response = await receive(response_queue_uuid);
if (response !== did_not_revoke_response) {
reject(`Blob URL was revoked in not-same-top-level-site dedicated worker: ${response}`);
}
await send(worker_2_uuid,
can_blob_url_be_revoked_js(blob_url, response_queue_uuid));
response = await receive(response_queue_uuid);
if (response !== did_revoke_response) {
reject(`Blob URL wasn't revoked in same-top-level-site dedicated worker: ${response}`);
}
resolve();
} catch (e) {
reject(e);
}
});
}, "Blob URL shouldn't be revocable from a cross-partition dedicated worker");
const newSharedWorker = (origin) => {
const worker_token = token();
const worker_url = origin + executor_worker_path + `&uuid=${worker_token}`;
const worker = new SharedWorker(worker_url, worker_token);
return worker_token;
}
promise_test(t => {
return new Promise(async (resolve, reject) => {
try {
const response_queue_uuid = token();
const create_worker_js = (origin) => `
const importScript = ${importScript};
await importScript("/html/cross-origin-embedder-policy/credentialless" +
"/resources/common.js");
await importScript("/html/anonymous-iframe/resources/common.js");
await importScript("/common/utils.js");
const newSharedWorker = ${newSharedWorker};
await send("${response_queue_uuid}", newSharedWorker("${origin}"));
`;
const [iframe_1_uuid, iframe_2_uuid] =
await create_test_iframes(t, response_queue_uuid);
// Create a shared worker in the cross-top-level-site iframe.
await send(iframe_1_uuid, create_worker_js(same_site_origin));
const worker_1_uuid = await receive(response_queue_uuid);
// Create a shared worker in the same-top-level-site iframe.
await send(iframe_2_uuid, create_worker_js(same_site_origin));
const worker_2_uuid = await receive(response_queue_uuid);
const blob = new Blob(["blob data"], {type : "text/plain"});
const blob_url = window.URL.createObjectURL(blob);
t.add_cleanup(() => window.URL.revokeObjectURL(blob_url));
await send(worker_1_uuid,
can_blob_url_be_revoked_js(blob_url, response_queue_uuid));
var response = await receive(response_queue_uuid);
if (response !== did_not_revoke_response) {
reject(`Blob URL was revoked in not-same-top-level-site shared worker: ${response}`);
}
await send(worker_2_uuid,
can_blob_url_be_revoked_js(blob_url, response_queue_uuid));
response = await receive(response_queue_uuid);
if (response !== did_revoke_response) {
reject(`Blob URL wasn't revoked in same-top-level-site shared worker: ${response}`);
}
resolve();
} catch (e) {
reject(e);
}
});
}, "Blob URL shouldn't be revocable from a cross-partition shared worker");
const newServiceWorker = async (origin) => {
const worker_token = token();
const worker_url = origin + executor_service_worker_path +
`&uuid=${worker_token}`;
const worker_url_path = executor_service_worker_path.substring(0,
executor_service_worker_path.lastIndexOf('/'));
const scope = worker_url_path + "/not-used/";
const reg = await navigator.serviceWorker.register(worker_url,
{'scope': scope});
return worker_token;
}
promise_test(t => {
return new Promise(async (resolve, reject) => {
try {
const response_queue_uuid = token();
const create_worker_js = (origin) => `
const importScript = ${importScript};
await importScript("/html/cross-origin-embedder-policy/credentialless" +
"/resources/common.js");
await importScript("/html/anonymous-iframe/resources/common.js");
await importScript("/common/utils.js");
const newServiceWorker = ${newServiceWorker};
await send("${response_queue_uuid}", await newServiceWorker("${origin}"));
`;
const [iframe_1_uuid, iframe_2_uuid] =
await create_test_iframes(t, response_queue_uuid);
// Create a service worker in either iframe.
await send(iframe_1_uuid, create_worker_js(same_site_origin));
var worker_1_uuid = await receive(response_queue_uuid);
t.add_cleanup(() =>
send(worker_1_uuid, "self.registration.unregister();"));
const blob = new Blob(["blob data"], {type : "text/plain"});
const blob_url = window.URL.createObjectURL(blob);
t.add_cleanup(() => window.URL.revokeObjectURL(blob_url));
await send(worker_1_uuid,
can_blob_url_be_revoked_js(blob_url, response_queue_uuid));
const response = await receive(response_queue_uuid);
if (response !== "URL.revokeObjectURL is not exposed") {
reject(`URL.revokeObjectURL is exposed in a Service Worker context: ${response}`);
}
resolve();
} catch (e) {
reject(e);
}
});
}, "Blob URL shouldn't be revocable from a service worker");
</script>
</body>

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Blob and File reference URL Test(2)</title>
<link rel=help href="http://dev.w3.org/2006/webapi/FileAPI/#convenienceAPI">
<link rel=author title="Breezewish" href="mailto:me@breeswish.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<form name="upload">
<input type="file" id="fileChooser"><br><input type="button" id="start" value="start">
</form>
<div>
<p>Test steps:</p>
<ol>
<li>Download the <a href="support/file_test2.txt">file</a>.</li>
<li>Select the file in the file inputbox.</li>
<li>Delete the file.</li>
<li>Click the 'start' button.</li>
</ol>
</div>
<div id="log"></div>
<script>
var fileChooser = document.querySelector('#fileChooser');
setup({explicit_done: true});
setup({explicit_timeout: true});
on_event(document.querySelector('#start'), 'click', function() {
async_test(function(t) {
var url = URL.createObjectURL(fileChooser.files[0]);
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = t.step_func(function() {
switch (xhr.readyState) {
case xhr.DONE:
assert_equals(xhr.status, 500, 'status code should be 500.');
t.done();
return;
}
});
xhr.send();
}, 'Check whether the browser response 500 in XHR if the selected file which File/Blob URL refered is not found');
done();
});
</script>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>File API Test: Progress Event - bubbles, cancelable</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="help" href="http://www.w3.org/TR/FileAPI/#events">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
async_test(function(){
var blob = new Blob(["TEST"]);
var reader = new FileReader();
reader.onloadstart = this.step_func(function(evt) {
assert_false(evt.bubbles, "The bubbles must be false when the event is dispatched");
assert_false(evt.cancelable, "The cancelable must be false when the event is dispatched");
});
reader.onload = this.step_func(function(evt) {
assert_false(evt.bubbles, "The bubbles must be false when the event is dispatched");
assert_false(evt.cancelable, "The cancelable must be false when the event is dispatched");
});
reader.onloadend = this.step_func(function(evt) {
assert_false(evt.bubbles, "The bubbles must be false when the event is dispatched");
assert_false(evt.cancelable, "The cancelable must be false when the event is dispatched");
this.done();
});
reader.readAsText(blob);
}, "Check the values of bubbles and cancelable are false when the progress event is dispatched");
</script>

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FileReader Errors Test</title>
<link rel=help href="http://dev.w3.org/2006/webapi/FileAPI/#convenienceAPI">
<link rel=author title="Breezewish" href="mailto:me@breeswish.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<form name="upload">
<input type="file" id="fileChooser"><br><input type="button" id="start" value="start">
</form>
<div>
<p>Test steps:</p>
<ol>
<li>Download the <a href="support/file_test1.txt">file</a>.</li>
<li>Select the file in the file inputbox.</li>
<li>Delete the file.</li>
<li>Click the 'start' button.</li>
</ol>
</div>
<div id="log"></div>
<script>
var fileChooser = document.querySelector('#fileChooser');
setup({explicit_done: true});
setup({explicit_timeout: true});
on_event(fileChooser, 'change', function() {
async_test(function(t) {
var reader = new FileReader();
reader.readAsArrayBuffer(fileChooser.files[0]);
reader.onloadend = t.step_func_done(function(event) {
assert_equals(event.target.readyState, FileReader.DONE);
assert_equals(reader.error, null);
});
}, 'FileReader.error should be null if there are no errors when reading');
});
on_event(document.querySelector('#start'), 'click', function() {
async_test(function(t) {
var reader = new FileReader();
reader.readAsArrayBuffer(fileChooser.files[0]);
reader.onloadend = t.step_func_done(function(event) {
assert_equals(event.target.readyState, FileReader.DONE);
assert_equals(reader.error.code, 8);
assert_true(reader.error instanceof DOMException);
});
}, 'FileReader.error should be NOT_FOUND_ERR if the file is not found when reading');
done();
});
</script>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>FileReader NotReadableError Test</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="help" href="https://w3c.github.io/FileAPI/#dfn-error-codes">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<form name="upload">
<input type="file" id="fileChooser"><br><input type="button" id="start" value="start">
</form>
<div>
<p>Test steps:</p>
<ol>
<li>Download the <a href="support/file_test1.txt">file</a>.</li>
<li>Select the file in the file inputbox.</li>
<li>Delete the file's readable permission.</li>
<li>Click the 'start' button.</li>
</ol>
</div>
<script>
const fileChooser = document.querySelector('#fileChooser');
setup({explicit_done: true});
setup({explicit_timeout: true});
on_event(document.querySelector('#start'), 'click', () => {
async_test(t => {
const reader = new FileReader();
reader.readAsArrayBuffer(fileChooser.files[0]);
reader.onloadend = t.step_func_done(event => {
assert_equals(event.target.readyState, FileReader.DONE);
assert_equals(reader.error.name, "NotReadableError");
});
}, 'FileReader.error should be NotReadableError if the file is unreadable');
done();
});
</script>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>FileReader SecurityError Test</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="help" href="https://w3c.github.io/FileAPI/#dfn-error-codes">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<form name="upload">
<input type="file" id="fileChooser"><br><input type="button" id="start" value="start">
</form>
<div>
<p>Test steps:</p>
<ol>
<li>Select a system sensitive file (e.g. files in /usr/bin, password files,
and other native operating system executables) in the file inputbox.</li>
<li>Click the 'start' button.</li>
</ol>
</div>
<script>
const fileChooser = document.querySelector('#fileChooser');
setup({explicit_done: true});
setup({explicit_timeout: true});
on_event(document.querySelector('#start'), 'click', () => {
async_test(t => {
const reader = new FileReader();
reader.readAsArrayBuffer(fileChooser.files[0]);
reader.onloadend = t.step_func_done(event => {
assert_equals(event.target.readyState, FileReader.DONE);
assert_equals(reader.error.name, "SecurityError");
});
}, 'FileReader.error should be SECURITY_ERROR if the file is a system sensitive file');
done();
});
</script>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
async_test(t => {
function workerCode() {
close();
var blob = new Blob([123]);
var fr = new FileReader();
fr.readAsText(blob);
fr.abort()
fr.readAsArrayBuffer(blob);
postMessage(true);
}
var workerBlob = new Blob([workerCode.toString() + ";workerCode();"], {type:"application/javascript"});
var w = new Worker(URL.createObjectURL(workerBlob));
w.onmessage = function(e) {
assert_true(e.data, "FileReader created during worker shutdown.");
t.done();
}
}, 'FileReader created after a worker self.close()');
</script>

View File

@ -0,0 +1,56 @@
importScripts("/resources/testharness.js");
var blob, empty_blob, readerSync;
setup(() => {
readerSync = new FileReaderSync();
blob = new Blob(["test"]);
empty_blob = new Blob();
});
test(() => {
assert_true(readerSync instanceof FileReaderSync);
}, "Interface");
test(() => {
var text = readerSync.readAsText(blob);
assert_equals(text, "test");
}, "readAsText");
test(() => {
var text = readerSync.readAsText(empty_blob);
assert_equals(text, "");
}, "readAsText with empty blob");
test(() => {
var data = readerSync.readAsDataURL(blob);
assert_equals(data.indexOf("data:"), 0);
}, "readAsDataURL");
test(() => {
var data = readerSync.readAsDataURL(empty_blob);
assert_equals(data.indexOf("data:"), 0);
}, "readAsDataURL with empty blob");
test(() => {
var data = readerSync.readAsBinaryString(blob);
assert_equals(data, "test");
}, "readAsBinaryString");
test(() => {
var data = readerSync.readAsBinaryString(empty_blob);
assert_equals(data, "");
}, "readAsBinaryString with empty blob");
test(() => {
var data = readerSync.readAsArrayBuffer(blob);
assert_true(data instanceof ArrayBuffer);
assert_equals(data.byteLength, "test".length);
}, "readAsArrayBuffer");
test(() => {
var data = readerSync.readAsArrayBuffer(empty_blob);
assert_true(data instanceof ArrayBuffer);
assert_equals(data.byteLength, 0);
}, "readAsArrayBuffer with empty blob");
done();

6
test/fixtures/wpt/FileAPI/META.yml vendored Normal file
View File

@ -0,0 +1,6 @@
spec: https://w3c.github.io/FileAPI/
suggested_reviewers:
- inexorabletash
- jdm
- mkruisselbrink
- annevk

View File

@ -0,0 +1,45 @@
// META: title=Blob Array Buffer
// META: script=../support/Blob.js
'use strict';
promise_test(async () => {
const input_arr = new TextEncoder().encode("PASS");
const blob = new Blob([input_arr]);
const array_buffer = await blob.arrayBuffer();
assert_true(array_buffer instanceof ArrayBuffer);
assert_equals_typed_array(new Uint8Array(array_buffer), input_arr);
}, "Blob.arrayBuffer()")
promise_test(async () => {
const input_arr = new TextEncoder().encode("");
const blob = new Blob([input_arr]);
const array_buffer = await blob.arrayBuffer();
assert_true(array_buffer instanceof ArrayBuffer);
assert_equals_typed_array(new Uint8Array(array_buffer), input_arr);
}, "Blob.arrayBuffer() empty Blob data")
promise_test(async () => {
const input_arr = new TextEncoder().encode("\u08B8\u000a");
const blob = new Blob([input_arr]);
const array_buffer = await blob.arrayBuffer();
assert_equals_typed_array(new Uint8Array(array_buffer), input_arr);
}, "Blob.arrayBuffer() non-ascii input")
promise_test(async () => {
const input_arr = [8, 241, 48, 123, 151];
const typed_arr = new Uint8Array(input_arr);
const blob = new Blob([typed_arr]);
const array_buffer = await blob.arrayBuffer();
assert_equals_typed_array(new Uint8Array(array_buffer), typed_arr);
}, "Blob.arrayBuffer() non-unicode input")
promise_test(async () => {
const input_arr = new TextEncoder().encode("PASS");
const blob = new Blob([input_arr]);
const array_buffer_results = await Promise.all([blob.arrayBuffer(),
blob.arrayBuffer(), blob.arrayBuffer()]);
for (let array_buffer of array_buffer_results) {
assert_true(array_buffer instanceof ArrayBuffer);
assert_equals_typed_array(new Uint8Array(array_buffer), input_arr);
}
}, "Blob.arrayBuffer() concurrent reads")

View File

@ -0,0 +1,45 @@
// META: title=Blob bytes()
// META: script=../support/Blob.js
'use strict';
promise_test(async () => {
const input_arr = new TextEncoder().encode("PASS");
const blob = new Blob([input_arr]);
const uint8array = await blob.bytes();
assert_true(uint8array instanceof Uint8Array);
assert_equals_typed_array(uint8array, input_arr);
}, "Blob.bytes()")
promise_test(async () => {
const input_arr = new TextEncoder().encode("");
const blob = new Blob([input_arr]);
const uint8array = await blob.bytes();
assert_true(uint8array instanceof Uint8Array);
assert_equals_typed_array(uint8array, input_arr);
}, "Blob.bytes() empty Blob data")
promise_test(async () => {
const input_arr = new TextEncoder().encode("\u08B8\u000a");
const blob = new Blob([input_arr]);
const uint8array = await blob.bytes();
assert_equals_typed_array(uint8array, input_arr);
}, "Blob.bytes() non-ascii input")
promise_test(async () => {
const input_arr = [8, 241, 48, 123, 151];
const typed_arr = new Uint8Array(input_arr);
const blob = new Blob([typed_arr]);
const uint8array = await blob.bytes();
assert_equals_typed_array(uint8array, typed_arr);
}, "Blob.bytes() non-unicode input")
promise_test(async () => {
const input_arr = new TextEncoder().encode("PASS");
const blob = new Blob([input_arr]);
const uint8array_results = await Promise.all([blob.bytes(),
blob.bytes(), blob.bytes()]);
for (let uint8array of uint8array_results) {
assert_true(uint8array instanceof Uint8Array);
assert_equals_typed_array(uint8array, input_arr);
}
}, "Blob.bytes() concurrent reads")

View File

@ -0,0 +1,53 @@
// META: title=Blob constructor
// META: script=../support/Blob.js
'use strict';
var test_error = {
name: "test",
message: "test error",
};
test(function() {
var args = [
document.createElement("div"),
window,
];
args.forEach(function(arg) {
assert_throws_js(TypeError, function() {
new Blob(arg);
}, "Should throw for argument " + format_value(arg) + ".");
});
}, "Passing platform objects for blobParts should throw a TypeError.");
test(function() {
var element = document.createElement("div");
element.appendChild(document.createElement("div"));
element.appendChild(document.createElement("p"));
var list = element.children;
Object.defineProperty(list, "length", {
get: function() { throw test_error; }
});
assert_throws_exactly(test_error, function() {
new Blob(list);
});
}, "A platform object that supports indexed properties should be treated as a sequence for the blobParts argument (overwritten 'length'.)");
test_blob(function() {
var select = document.createElement("select");
select.appendChild(document.createElement("option"));
return new Blob(select);
}, {
expected: "[object HTMLOptionElement]",
type: "",
desc: "Passing an platform object that supports indexed properties as the blobParts array should work (select)."
});
test_blob(function() {
var elm = document.createElement("div");
elm.setAttribute("foo", "bar");
return new Blob(elm.attributes);
}, {
expected: "[object Attr]",
type: "",
desc: "Passing an platform object that supports indexed properties as the blobParts array should work (attributes)."
});

View File

@ -0,0 +1,104 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Blob constructor: endings option</title>
<link rel=help href="https://w3c.github.io/FileAPI/#constructorBlob">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
// Windows platforms use CRLF as the native line ending. All others use LF.
const crlf = navigator.platform.startsWith('Win');
const native_ending = crlf ? '\r\n' : '\n';
function readBlobAsPromise(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsText(blob);
reader.onload = e => resolve(reader.result);
reader.onerror = e => reject(reader.error);
});
}
[
'transparent',
'native'
].forEach(value => test(t => {
assert_class_string(new Blob([], {endings: value}), 'Blob',
`Constructor should allow "${value}" endings`);
}, `Valid "endings" value: ${JSON.stringify(value)}`));
[
null,
'',
'invalidEnumValue',
'Transparent',
'NATIVE',
0,
{}
].forEach(value => test(t => {
assert_throws_js(TypeError, () => new Blob([], {endings: value}),
'Blob constructor should throw');
}, `Invalid "endings" value: ${JSON.stringify(value)}`));
test(t => {
const test_error = {name: 'test'};
assert_throws_exactly(
test_error,
() => new Blob([], { get endings() { throw test_error; }}),
'Blob constructor should propagate exceptions from "endings" property');
}, 'Exception propagation from options');
test(t => {
let got = false;
new Blob([], { get endings() { got = true; } });
assert_true(got, 'The "endings" property was accessed during construction.');
}, 'The "endings" options property is used');
[
{name: 'LF', input: '\n', native: native_ending},
{name: 'CR', input: '\r', native: native_ending},
{name: 'CRLF', input: '\r\n', native: native_ending},
{name: 'CRCR', input: '\r\r', native: native_ending.repeat(2)},
{name: 'LFCR', input: '\n\r', native: native_ending.repeat(2)},
{name: 'LFLF', input: '\n\n', native: native_ending.repeat(2)},
{name: 'CRCRLF', input: '\r\r\n', native: native_ending.repeat(2)},
{name: 'CRLFLF', input: '\r\n\n', native: native_ending.repeat(2)},
{name: 'CRLFCR', input: '\r\n\r\n', native: native_ending.repeat(2)},
{name: 'CRLFCRLF', input: '\r\n\r\n', native: native_ending.repeat(2)},
{name: 'LFCRLFCR', input: '\n\r\n\r', native: native_ending.repeat(3)},
].forEach(testCase => {
promise_test(async t => {
const blob = new Blob([testCase.input]);
assert_equals(
await readBlobAsPromise(blob), testCase.input,
'Newlines should not change with endings unspecified');
}, `Input ${testCase.name} with endings unspecified`);
promise_test(async t => {
const blob = new Blob([testCase.input], {endings: 'transparent'});
assert_equals(
await readBlobAsPromise(blob), testCase.input,
'Newlines should not change with endings "transparent"');
}, `Input ${testCase.name} with endings 'transparent'`);
promise_test(async t => {
const blob = new Blob([testCase.input], {endings: 'native'});
assert_equals(
await readBlobAsPromise(blob), testCase.native,
'Newlines should match the platform with endings "native"');
}, `Input ${testCase.name} with endings 'native'`);
});
promise_test(async t => {
const blob = new Blob(['\r', '\n'], {endings: 'native'});
const expected = native_ending.repeat(2);
assert_equals(
await readBlobAsPromise(blob), expected,
'CR/LF in adjacent strings should be converted to two platform newlines');
}, `CR/LF in adjacent input strings`);
</script>

View File

@ -0,0 +1,469 @@
// META: title=Blob constructor
// META: script=../support/Blob.js
'use strict';
test(function() {
assert_true("Blob" in globalThis, "globalThis should have a Blob property.");
assert_equals(Blob.length, 0, "Blob.length should be 0.");
assert_true(Blob instanceof Function, "Blob should be a function.");
}, "Blob interface object");
// Step 1.
test(function() {
var blob = new Blob();
assert_true(blob instanceof Blob);
assert_equals(String(blob), '[object Blob]');
assert_equals(blob.size, 0);
assert_equals(blob.type, "");
}, "Blob constructor with no arguments");
test(function() {
assert_throws_js(TypeError, function() { var blob = Blob(); });
}, "Blob constructor with no arguments, without 'new'");
test(function() {
var blob = new Blob;
assert_true(blob instanceof Blob);
assert_equals(blob.size, 0);
assert_equals(blob.type, "");
}, "Blob constructor without brackets");
test(function() {
var blob = new Blob(undefined);
assert_true(blob instanceof Blob);
assert_equals(String(blob), '[object Blob]');
assert_equals(blob.size, 0);
assert_equals(blob.type, "");
}, "Blob constructor with undefined as first argument");
// blobParts argument (WebIDL).
test(function() {
var args = [
null,
true,
false,
0,
1,
1.5,
"FAIL",
new Date(),
new RegExp(),
{},
{ 0: "FAIL", length: 1 },
];
args.forEach(function(arg) {
assert_throws_js(TypeError, function() {
new Blob(arg);
}, "Should throw for argument " + format_value(arg) + ".");
});
}, "Passing non-objects, Dates and RegExps for blobParts should throw a TypeError.");
test_blob(function() {
return new Blob({
[Symbol.iterator]: Array.prototype[Symbol.iterator],
});
}, {
expected: "",
type: "",
desc: "A plain object with @@iterator should be treated as a sequence for the blobParts argument."
});
test(t => {
const blob = new Blob({
[Symbol.iterator]() {
var i = 0;
return {next: () => [
{done:false, value:'ab'},
{done:false, value:'cde'},
{done:true}
][i++]
};
}
});
assert_equals(blob.size, 5, 'Custom @@iterator should be treated as a sequence');
}, "A plain object with custom @@iterator should be treated as a sequence for the blobParts argument.");
test_blob(function() {
return new Blob({
[Symbol.iterator]: Array.prototype[Symbol.iterator],
0: "PASS",
length: 1
});
}, {
expected: "PASS",
type: "",
desc: "A plain object with @@iterator and a length property should be treated as a sequence for the blobParts argument."
});
test_blob(function() {
return new Blob(new String("xyz"));
}, {
expected: "xyz",
type: "",
desc: "A String object should be treated as a sequence for the blobParts argument."
});
test_blob(function() {
return new Blob(new Uint8Array([1, 2, 3]));
}, {
expected: "123",
type: "",
desc: "A Uint8Array object should be treated as a sequence for the blobParts argument."
});
var test_error = {
name: "test",
message: "test error",
};
test(function() {
var obj = {
[Symbol.iterator]: Array.prototype[Symbol.iterator],
get length() { throw test_error; }
};
assert_throws_exactly(test_error, function() {
new Blob(obj);
});
}, "The length getter should be invoked and any exceptions should be propagated.");
test(function() {
assert_throws_exactly(test_error, function() {
var obj = {
[Symbol.iterator]: Array.prototype[Symbol.iterator],
length: {
valueOf: null,
toString: function() { throw test_error; }
}
};
new Blob(obj);
});
assert_throws_exactly(test_error, function() {
var obj = {
[Symbol.iterator]: Array.prototype[Symbol.iterator],
length: { valueOf: function() { throw test_error; } }
};
new Blob(obj);
});
}, "ToUint32 should be applied to the length and any exceptions should be propagated.");
test(function() {
var received = [];
var obj = {
get [Symbol.iterator]() {
received.push("Symbol.iterator");
return Array.prototype[Symbol.iterator];
},
get length() {
received.push("length getter");
return {
valueOf: function() {
received.push("length valueOf");
return 3;
}
};
},
get 0() {
received.push("0 getter");
return {
toString: function() {
received.push("0 toString");
return "a";
}
};
},
get 1() {
received.push("1 getter");
throw test_error;
},
get 2() {
received.push("2 getter");
assert_unreached("Should not call the getter for 2 if the getter for 1 threw.");
}
};
assert_throws_exactly(test_error, function() {
new Blob(obj);
});
assert_array_equals(received, [
"Symbol.iterator",
"length getter",
"length valueOf",
"0 getter",
"0 toString",
"length getter",
"length valueOf",
"1 getter",
]);
}, "Getters and value conversions should happen in order until an exception is thrown.");
// XXX should add tests edge cases of ToLength(length)
test(function() {
assert_throws_exactly(test_error, function() {
new Blob([{ toString: function() { throw test_error; } }]);
}, "Throwing toString");
assert_throws_exactly(test_error, function() {
new Blob([{ toString: undefined, valueOf: function() { throw test_error; } }]);
}, "Throwing valueOf");
assert_throws_exactly(test_error, function() {
new Blob([{
toString: function() { throw test_error; },
valueOf: function() { assert_unreached("Should not call valueOf if toString is present."); }
}]);
}, "Throwing toString and valueOf");
assert_throws_js(TypeError, function() {
new Blob([{toString: null, valueOf: null}]);
}, "Null toString and valueOf");
}, "ToString should be called on elements of the blobParts array and any exceptions should be propagated.");
test_blob(function() {
var arr = [
{ toString: function() { arr.pop(); return "PASS"; } },
{ toString: function() { assert_unreached("Should have removed the second element of the array rather than called toString() on it."); } }
];
return new Blob(arr);
}, {
expected: "PASS",
type: "",
desc: "Changes to the blobParts array should be reflected in the returned Blob (pop)."
});
test_blob(function() {
var arr = [
{
toString: function() {
if (arr.length === 3) {
return "A";
}
arr.unshift({
toString: function() {
assert_unreached("Should only access index 0 once.");
}
});
return "P";
}
},
{
toString: function() {
return "SS";
}
}
];
return new Blob(arr);
}, {
expected: "PASS",
type: "",
desc: "Changes to the blobParts array should be reflected in the returned Blob (unshift)."
});
test_blob(function() {
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=17652
return new Blob([
null,
undefined,
true,
false,
0,
1,
new String("stringobject"),
[],
['x', 'y'],
{},
{ 0: "FAIL", length: 1 },
{ toString: function() { return "stringA"; } },
{ toString: undefined, valueOf: function() { return "stringB"; } },
{ valueOf: function() { assert_unreached("Should not call valueOf if toString is present on the prototype."); } }
]);
}, {
expected: "nullundefinedtruefalse01stringobjectx,y[object Object][object Object]stringAstringB[object Object]",
type: "",
desc: "ToString should be called on elements of the blobParts array."
});
test_blob(function() {
return new Blob([
new ArrayBuffer(8)
]);
}, {
expected: "\0\0\0\0\0\0\0\0",
type: "",
desc: "ArrayBuffer elements of the blobParts array should be supported."
});
test_blob(function() {
return new Blob([
new Uint8Array([0x50, 0x41, 0x53, 0x53]),
new Int8Array([0x50, 0x41, 0x53, 0x53]),
new Uint16Array([0x4150, 0x5353]),
new Int16Array([0x4150, 0x5353]),
new Uint32Array([0x53534150]),
new Int32Array([0x53534150]),
new Float16Array([2.65625, 58.59375]),
new Float32Array([0xD341500000])
]);
}, {
expected: "PASSPASSPASSPASSPASSPASSPASSPASS",
type: "",
desc: "Passing typed arrays as elements of the blobParts array should work."
});
test_blob(function() {
return new Blob([
// 0x535 3415053534150
// 0x535 = 0b010100110101 -> Sign = +, Exponent = 1333 - 1023 = 310
// 0x13415053534150 * 2**(-52)
// ==> 0x13415053534150 * 2**258 = 2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680
new Float64Array([2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680])
]);
}, {
expected: "PASSPASS",
type: "",
desc: "Passing a Float64Array as element of the blobParts array should work."
});
test_blob(function() {
return new Blob([
new BigInt64Array([BigInt("0x5353415053534150")]),
new BigUint64Array([BigInt("0x5353415053534150")])
]);
}, {
expected: "PASSPASSPASSPASS",
type: "",
desc: "Passing BigInt typed arrays as elements of the blobParts array should work."
});
var t_ports = async_test("Passing a FrozenArray as the blobParts array should work (FrozenArray<MessagePort>).");
t_ports.step(function() {
var channel = new MessageChannel();
channel.port2.onmessage = this.step_func(function(e) {
var b_ports = new Blob(e.ports);
assert_equals(b_ports.size, "[object MessagePort]".length);
this.done();
});
var channel2 = new MessageChannel();
channel.port1.postMessage('', [channel2.port1]);
});
test_blob(function() {
var blob = new Blob(['foo']);
return new Blob([blob, blob]);
}, {
expected: "foofoo",
type: "",
desc: "Array with two blobs"
});
test_blob_binary(function() {
var view = new Uint8Array([0, 255, 0]);
return new Blob([view.buffer, view.buffer]);
}, {
expected: [0, 255, 0, 0, 255, 0],
type: "",
desc: "Array with two buffers"
});
test_blob_binary(function() {
var view = new Uint8Array([0, 255, 0, 4]);
var blob = new Blob([view, view]);
assert_equals(blob.size, 8);
var view1 = new Uint16Array(view.buffer, 2);
return new Blob([view1, view.buffer, view1]);
}, {
expected: [0, 4, 0, 255, 0, 4, 0, 4],
type: "",
desc: "Array with two bufferviews"
});
test_blob(function() {
var view = new Uint8Array([0]);
var blob = new Blob(["fo"]);
return new Blob([view.buffer, blob, "foo"]);
}, {
expected: "\0fofoo",
type: "",
desc: "Array with mixed types"
});
test(function() {
const accessed = [];
const stringified = [];
new Blob([], {
get type() { accessed.push('type'); },
get endings() { accessed.push('endings'); }
});
new Blob([], {
type: { toString: () => { stringified.push('type'); return ''; } },
endings: { toString: () => { stringified.push('endings'); return 'transparent'; } }
});
assert_array_equals(accessed, ['endings', 'type']);
assert_array_equals(stringified, ['endings', 'type']);
}, "options properties should be accessed in lexicographic order.");
test(function() {
assert_throws_exactly(test_error, function() {
new Blob(
[{ toString: function() { throw test_error } }],
{
get type() { assert_unreached("type getter should not be called."); }
}
);
});
}, "Arguments should be evaluated from left to right.");
[
null,
undefined,
{},
{ unrecognized: true },
/regex/,
function() {}
].forEach(function(arg, idx) {
test_blob(function() {
return new Blob([], arg);
}, {
expected: "",
type: "",
desc: "Passing " + format_value(arg) + " (index " + idx + ") for options should use the defaults."
});
test_blob(function() {
return new Blob(["\na\r\nb\n\rc\r"], arg);
}, {
expected: "\na\r\nb\n\rc\r",
type: "",
desc: "Passing " + format_value(arg) + " (index " + idx + ") for options should use the defaults (with newlines)."
});
});
[
123,
123.4,
true,
'abc'
].forEach(arg => {
test(t => {
assert_throws_js(TypeError, () => new Blob([], arg),
'Blob constructor should throw with invalid property bag');
}, `Passing ${JSON.stringify(arg)} for options should throw`);
});
var type_tests = [
// blobParts, type, expected type
[[], '', ''],
[[], 'a', 'a'],
[[], 'A', 'a'],
[[], 'text/html', 'text/html'],
[[], 'TEXT/HTML', 'text/html'],
[[], 'text/plain;charset=utf-8', 'text/plain;charset=utf-8'],
[[], '\u00E5', ''],
[[], '\uD801\uDC7E', ''], // U+1047E
[[], ' image/gif ', ' image/gif '],
[[], '\timage/gif\t', ''],
[[], 'image/gif;\u007f', ''],
[[], '\u0130mage/gif', ''], // uppercase i with dot
[[], '\u0131mage/gif', ''], // lowercase dotless i
[[], 'image/gif\u0000', ''],
// check that type isn't changed based on sniffing
[[0x3C, 0x48, 0x54, 0x4D, 0x4C, 0x3E], 'unknown/unknown', 'unknown/unknown'], // "<HTML>"
[[0x00, 0xFF], 'text/plain', 'text/plain'],
[[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], 'image/png', 'image/png'], // "GIF89a"
];
type_tests.forEach(function(t) {
test(function() {
var arr = new Uint8Array([t[0]]).buffer;
var b = new Blob([arr], {type:t[1]});
assert_equals(b.type, t[2]);
}, "Blob with type " + format_value(t[1]));
});

View File

@ -0,0 +1,9 @@
importScripts("/resources/testharness.js");
promise_test(async () => {
const data = "TEST";
const blob = new Blob([data], {type: "text/plain"});
assert_equals(await blob.text(), data);
}, 'Create Blob in Worker');
done();

View File

@ -0,0 +1,32 @@
// META: title=Blob slice overflow
'use strict';
var text = '';
for (var i = 0; i < 2000; ++i) {
text += 'A';
}
test(function() {
var blob = new Blob([text]);
var sliceBlob = blob.slice(-1, blob.size);
assert_equals(sliceBlob.size, 1, "Blob slice size");
}, "slice start is negative, relativeStart will be max((size + start), 0)");
test(function() {
var blob = new Blob([text]);
var sliceBlob = blob.slice(blob.size + 1, blob.size);
assert_equals(sliceBlob.size, 0, "Blob slice size");
}, "slice start is greater than blob size, relativeStart will be min(start, size)");
test(function() {
var blob = new Blob([text]);
var sliceBlob = blob.slice(blob.size - 2, -1);
assert_equals(sliceBlob.size, 1, "Blob slice size");
}, "slice end is negative, relativeEnd will be max((size + end), 0)");
test(function() {
var blob = new Blob([text]);
var sliceBlob = blob.slice(blob.size - 2, blob.size + 999);
assert_equals(sliceBlob.size, 2, "Blob slice size");
}, "slice end is greater than blob size, relativeEnd will be min(end, size)");

View File

@ -0,0 +1,231 @@
// META: title=Blob slice
// META: script=../support/Blob.js
'use strict';
test_blob(function() {
var blobTemp = new Blob(["PASS"]);
return blobTemp.slice();
}, {
expected: "PASS",
type: "",
desc: "no-argument Blob slice"
});
test(function() {
var blob1, blob2;
test_blob(function() {
return blob1 = new Blob(["squiggle"]);
}, {
expected: "squiggle",
type: "",
desc: "blob1."
});
test_blob(function() {
return blob2 = new Blob(["steak"], {type: "content/type"});
}, {
expected: "steak",
type: "content/type",
desc: "blob2."
});
test_blob(function() {
return new Blob().slice(0,0,null);
}, {
expected: "",
type: "null",
desc: "null type Blob slice"
});
test_blob(function() {
return new Blob().slice(0,0,undefined);
}, {
expected: "",
type: "",
desc: "undefined type Blob slice"
});
test_blob(function() {
return new Blob().slice(0,0);
}, {
expected: "",
type: "",
desc: "no type Blob slice"
});
var arrayBuffer = new ArrayBuffer(16);
var int8View = new Int8Array(arrayBuffer);
for (var i = 0; i < 16; i++) {
int8View[i] = i + 65;
}
var testData = [
[
["PASSSTRING"],
[{start: -6, contents: "STRING"},
{start: -12, contents: "PASSSTRING"},
{start: 4, contents: "STRING"},
{start: 12, contents: ""},
{start: 0, end: -6, contents: "PASS"},
{start: 0, end: -12, contents: ""},
{start: 0, end: 4, contents: "PASS"},
{start: 0, end: 12, contents: "PASSSTRING"},
{start: 7, end: 4, contents: ""}]
],
// Test 3 strings
[
["foo", "bar", "baz"],
[{start: 0, end: 9, contents: "foobarbaz"},
{start: 0, end: 3, contents: "foo"},
{start: 3, end: 9, contents: "barbaz"},
{start: 6, end: 9, contents: "baz"},
{start: 6, end: 12, contents: "baz"},
{start: 0, end: 9, contents: "foobarbaz"},
{start: 0, end: 11, contents: "foobarbaz"},
{start: 10, end: 15, contents: ""}]
],
// Test string, Blob, string
[
["foo", blob1, "baz"],
[{start: 0, end: 3, contents: "foo"},
{start: 3, end: 11, contents: "squiggle"},
{start: 2, end: 4, contents: "os"},
{start: 10, end: 12, contents: "eb"}]
],
// Test blob, string, blob
[
[blob1, "foo", blob1],
[{start: 0, end: 8, contents: "squiggle"},
{start: 7, end: 9, contents: "ef"},
{start: 10, end: 12, contents: "os"},
{start: 1, end: 4, contents: "qui"},
{start: 12, end: 15, contents: "qui"},
{start: 40, end: 60, contents: ""}]
],
// Test blobs all the way down
[
[blob2, blob1, blob2],
[{start: 0, end: 5, contents: "steak"},
{start: 5, end: 13, contents: "squiggle"},
{start: 13, end: 18, contents: "steak"},
{start: 1, end: 3, contents: "te"},
{start: 6, end: 10, contents: "quig"}]
],
// Test an ArrayBufferView
[
[int8View, blob1, "foo"],
[{start: 0, end: 8, contents: "ABCDEFGH"},
{start: 8, end: 18, contents: "IJKLMNOPsq"},
{start: 17, end: 20, contents: "qui"},
{start: 4, end: 12, contents: "EFGHIJKL"}]
],
// Test a partial ArrayBufferView
[
[new Uint8Array(arrayBuffer, 3, 5), blob1, "foo"],
[{start: 0, end: 8, contents: "DEFGHsqu"},
{start: 8, end: 18, contents: "igglefoo"},
{start: 4, end: 12, contents: "Hsquiggl"}]
],
// Test type coercion of a number
[
[3, int8View, "foo"],
[{start: 0, end: 8, contents: "3ABCDEFG"},
{start: 8, end: 18, contents: "HIJKLMNOPf"},
{start: 17, end: 21, contents: "foo"},
{start: 4, end: 12, contents: "DEFGHIJK"}]
],
[
[(new Uint8Array([0, 255, 0])).buffer,
new Blob(['abcd']),
'efgh',
'ijklmnopqrstuvwxyz'],
[{start: 1, end: 4, contents: "\uFFFD\u0000a"},
{start: 4, end: 8, contents: "bcde"},
{start: 8, end: 12, contents: "fghi"},
{start: 1, end: 12, contents: "\uFFFD\u0000abcdefghi"}]
]
];
testData.forEach(function(data, i) {
var blobs = data[0];
var tests = data[1];
tests.forEach(function(expectations, j) {
test(function() {
var blob = new Blob(blobs);
assert_true(blob instanceof Blob);
assert_false(blob instanceof File);
test_blob(function() {
return expectations.end === undefined
? blob.slice(expectations.start)
: blob.slice(expectations.start, expectations.end);
}, {
expected: expectations.contents,
type: "",
desc: "Slicing test: slice (" + i + "," + j + ")."
});
}, "Slicing test (" + i + "," + j + ").");
});
});
}, "Slices");
var invalidTypes = [
"\xFF",
"te\x09xt/plain",
"te\x00xt/plain",
"te\x1Fxt/plain",
"te\x7Fxt/plain"
];
invalidTypes.forEach(function(type) {
test_blob(function() {
var blob = new Blob(["PASS"]);
return blob.slice(0, 4, type);
}, {
expected: "PASS",
type: "",
desc: "Invalid contentType (" + format_value(type) + ")"
});
});
var validTypes = [
"te(xt/plain",
"te)xt/plain",
"te<xt/plain",
"te>xt/plain",
"te@xt/plain",
"te,xt/plain",
"te;xt/plain",
"te:xt/plain",
"te\\xt/plain",
"te\"xt/plain",
"te/xt/plain",
"te[xt/plain",
"te]xt/plain",
"te?xt/plain",
"te=xt/plain",
"te{xt/plain",
"te}xt/plain",
"te\x20xt/plain",
"TEXT/PLAIN",
"text/plain;charset = UTF-8",
"text/plain;charset=UTF-8"
];
validTypes.forEach(function(type) {
test_blob(function() {
var blob = new Blob(["PASS"]);
return blob.slice(0, 4, type);
}, {
expected: "PASS",
type: type.toLowerCase(),
desc: "Valid contentType (" + format_value(type) + ")"
});
});

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<script type="module">
let a = new Blob(['', '', undefined], { })
let b = a.stream()
let c = new ReadableStreamBYOBReader(b)
let d = new Int16Array(8)
await c.read(d)
c.releaseLock()
await a.text()
await b.cancel()
</script>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<script>
const blob = new Blob([1, 2]);
const readable = blob.stream()
const writable = new WritableStream({}, {
size() {
let xhr = new XMLHttpRequest()
xhr.open("POST", "1", false)
xhr.send()
}
})
readable.pipeThrough({ readable, writable })
</script>

View File

@ -0,0 +1,94 @@
// META: title=Blob Stream
// META: script=../support/Blob.js
// META: script=/common/gc.js
'use strict';
// Helper function that triggers garbage collection while reading a chunk
// if perform_gc is true.
async function read_and_gc(reader, perform_gc) {
// Passing Uint8Array for byte streams; non-byte streams will simply ignore it
const read_promise = reader.read(new Uint8Array(64));
if (perform_gc) {
await garbageCollect();
}
return read_promise;
}
// Takes in a ReadableStream and reads from it until it is done, returning
// an array that contains the results of each read operation. If perform_gc
// is true, garbage collection is triggered while reading every chunk.
async function read_all_chunks(stream, { perform_gc = false, mode } = {}) {
assert_true(stream instanceof ReadableStream);
assert_true('getReader' in stream);
const reader = stream.getReader({ mode });
assert_true('read' in reader);
let read_value = await read_and_gc(reader, perform_gc);
let out = [];
let i = 0;
while (!read_value.done) {
for (let val of read_value.value) {
out[i++] = val;
}
read_value = await read_and_gc(reader, perform_gc);
}
return out;
}
promise_test(async () => {
const blob = new Blob(["PASS"]);
const stream = blob.stream();
const chunks = await read_all_chunks(stream);
for (let [index, value] of chunks.entries()) {
assert_equals(value, "PASS".charCodeAt(index));
}
}, "Blob.stream()")
promise_test(async () => {
const blob = new Blob();
const stream = blob.stream();
const chunks = await read_all_chunks(stream);
assert_array_equals(chunks, []);
}, "Blob.stream() empty Blob")
promise_test(async () => {
const input_arr = [8, 241, 48, 123, 151];
const typed_arr = new Uint8Array(input_arr);
const blob = new Blob([typed_arr]);
const stream = blob.stream();
const chunks = await read_all_chunks(stream);
assert_array_equals(chunks, input_arr);
}, "Blob.stream() non-unicode input")
promise_test(async() => {
const input_arr = [8, 241, 48, 123, 151];
const typed_arr = new Uint8Array(input_arr);
let blob = new Blob([typed_arr]);
const stream = blob.stream();
blob = null;
await garbageCollect();
const chunks = await read_all_chunks(stream, { perform_gc: true });
assert_array_equals(chunks, input_arr);
}, "Blob.stream() garbage collection of blob shouldn't break stream " +
"consumption")
promise_test(async() => {
const input_arr = [8, 241, 48, 123, 151];
const typed_arr = new Uint8Array(input_arr);
let blob = new Blob([typed_arr]);
const chunksPromise = read_all_chunks(blob.stream());
// It somehow matters to do GC here instead of doing `perform_gc: true`
await garbageCollect();
assert_array_equals(await chunksPromise, input_arr);
}, "Blob.stream() garbage collection of stream shouldn't break stream " +
"consumption")
promise_test(async () => {
const input_arr = [8, 241, 48, 123, 151];
const typed_arr = new Uint8Array(input_arr);
let blob = new Blob([typed_arr]);
const stream = blob.stream();
const chunks = await read_all_chunks(stream, { mode: "byob" });
assert_array_equals(chunks, input_arr);
}, "Reading Blob.stream() with BYOB reader")

View File

@ -0,0 +1,64 @@
// META: title=Blob Text
// META: script=../support/Blob.js
'use strict';
promise_test(async () => {
const blob = new Blob(["PASS"]);
const text = await blob.text();
assert_equals(text, "PASS");
}, "Blob.text()")
promise_test(async () => {
const blob = new Blob();
const text = await blob.text();
assert_equals(text, "");
}, "Blob.text() empty blob data")
promise_test(async () => {
const blob = new Blob(["P", "A", "SS"]);
const text = await blob.text();
assert_equals(text, "PASS");
}, "Blob.text() multi-element array in constructor")
promise_test(async () => {
const non_unicode = "\u0061\u030A";
const input_arr = new TextEncoder().encode(non_unicode);
const blob = new Blob([input_arr]);
const text = await blob.text();
assert_equals(text, non_unicode);
}, "Blob.text() non-unicode")
promise_test(async () => {
const blob = new Blob(["PASS"], { type: "text/plain;charset=utf-16le" });
const text = await blob.text();
assert_equals(text, "PASS");
}, "Blob.text() different charset param in type option")
promise_test(async () => {
const non_unicode = "\u0061\u030A";
const input_arr = new TextEncoder().encode(non_unicode);
const blob = new Blob([input_arr], { type: "text/plain;charset=utf-16le" });
const text = await blob.text();
assert_equals(text, non_unicode);
}, "Blob.text() different charset param with non-ascii input")
promise_test(async () => {
const input_arr = new Uint8Array([192, 193, 245, 246, 247, 248, 249, 250, 251,
252, 253, 254, 255]);
const blob = new Blob([input_arr]);
const text = await blob.text();
assert_equals(text, "\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd" +
"\ufffd\ufffd\ufffd\ufffd");
}, "Blob.text() invalid utf-8 input")
promise_test(async () => {
const input_arr = new Uint8Array([192, 193, 245, 246, 247, 248, 249, 250, 251,
252, 253, 254, 255]);
const blob = new Blob([input_arr]);
const text_results = await Promise.all([blob.text(), blob.text(),
blob.text()]);
for (let text of text_results) {
assert_equals(text, "\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd" +
"\ufffd\ufffd\ufffd\ufffd");
}
}, "Blob.text() concurrent reads")

View File

@ -0,0 +1,104 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>File constructor: endings option</title>
<link rel=help href="https://w3c.github.io/FileAPI/#file-constructor">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
// Windows platforms use CRLF as the native line ending. All others use LF.
const crlf = navigator.platform.startsWith('Win');
const native_ending = crlf ? '\r\n' : '\n';
function readBlobAsPromise(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsText(blob);
reader.onload = e => resolve(reader.result);
reader.onerror = e => reject(reader.error);
});
}
[
'transparent',
'native'
].forEach(value => test(t => {
assert_class_string(new File([], "name", {endings: value}), 'File',
`Constructor should allow "${value}" endings`);
}, `Valid "endings" value: ${JSON.stringify(value)}`));
[
null,
'',
'invalidEnumValue',
'Transparent',
'NATIVE',
0,
{}
].forEach(value => test(t => {
assert_throws_js(TypeError, () => new File([], "name", {endings: value}),
'File constructor should throw');
}, `Invalid "endings" value: ${JSON.stringify(value)}`));
test(t => {
const test_error = {name: 'test'};
assert_throws_exactly(
test_error,
() => new File([], "name", { get endings() { throw test_error; }}),
'File constructor should propagate exceptions from "endings" property');
}, 'Exception propagation from options');
test(t => {
let got = false;
new File([], "name", { get endings() { got = true; } });
assert_true(got, 'The "endings" property was accessed during construction.');
}, 'The "endings" options property is used');
[
{name: 'LF', input: '\n', native: native_ending},
{name: 'CR', input: '\r', native: native_ending},
{name: 'CRLF', input: '\r\n', native: native_ending},
{name: 'CRCR', input: '\r\r', native: native_ending.repeat(2)},
{name: 'LFCR', input: '\n\r', native: native_ending.repeat(2)},
{name: 'LFLF', input: '\n\n', native: native_ending.repeat(2)},
{name: 'CRCRLF', input: '\r\r\n', native: native_ending.repeat(2)},
{name: 'CRLFLF', input: '\r\n\n', native: native_ending.repeat(2)},
{name: 'CRLFCR', input: '\r\n\r\n', native: native_ending.repeat(2)},
{name: 'CRLFCRLF', input: '\r\n\r\n', native: native_ending.repeat(2)},
{name: 'LFCRLFCR', input: '\n\r\n\r', native: native_ending.repeat(3)},
].forEach(testCase => {
promise_test(async t => {
const file = new File([testCase.input], "name");
assert_equals(
await readBlobAsPromise(file), testCase.input,
'Newlines should not change with endings unspecified');
}, `Input ${testCase.name} with endings unspecified`);
promise_test(async t => {
const file = new File([testCase.input], "name", {endings: 'transparent'});
assert_equals(
await readBlobAsPromise(file), testCase.input,
'Newlines should not change with endings "transparent"');
}, `Input ${testCase.name} with endings 'transparent'`);
promise_test(async t => {
const file = new File([testCase.input], "name", {endings: 'native'});
assert_equals(
await readBlobAsPromise(file), testCase.native,
'Newlines should match the platform with endings "native"');
}, `Input ${testCase.name} with endings 'native'`);
});
promise_test(async t => {
const file = new File(['\r', '\n'], "name", {endings: 'native'});
const expected = native_ending.repeat(2);
assert_equals(
await readBlobAsPromise(file), expected,
'CR/LF in adjacent strings should be converted to two platform newlines');
}, `CR/LF in adjacent input strings`);
</script>

View File

@ -0,0 +1,155 @@
// META: title=File constructor
const to_string_obj = { toString: () => 'a string' };
const to_string_throws = { toString: () => { throw new Error('expected'); } };
test(function() {
assert_true("File" in globalThis, "globalThis should have a File property.");
}, "File interface object exists");
test(t => {
assert_throws_js(TypeError, () => new File(),
'Bits argument is required');
assert_throws_js(TypeError, () => new File([]),
'Name argument is required');
}, 'Required arguments');
function test_first_argument(arg1, expectedSize, testName) {
test(function() {
var file = new File(arg1, "dummy");
assert_true(file instanceof File);
assert_equals(file.name, "dummy");
assert_equals(file.size, expectedSize);
assert_equals(file.type, "");
// assert_false(file.isClosed); XXX: File.isClosed doesn't seem to be implemented
assert_not_equals(file.lastModified, "");
}, testName);
}
test_first_argument([], 0, "empty fileBits");
test_first_argument(["bits"], 4, "DOMString fileBits");
test_first_argument(["𝓽𝓮𝔁𝓽"], 16, "Unicode DOMString fileBits");
test_first_argument([new String('string object')], 13, "String object fileBits");
test_first_argument([new Blob()], 0, "Empty Blob fileBits");
test_first_argument([new Blob(["bits"])], 4, "Blob fileBits");
test_first_argument([new File([], 'world.txt')], 0, "Empty File fileBits");
test_first_argument([new File(["bits"], 'world.txt')], 4, "File fileBits");
test_first_argument([new ArrayBuffer(8)], 8, "ArrayBuffer fileBits");
test_first_argument([new Uint8Array([0x50, 0x41, 0x53, 0x53])], 4, "Typed array fileBits");
test_first_argument(["bits", new Blob(["bits"]), new Blob(), new Uint8Array([0x50, 0x41]),
new Uint16Array([0x5353]), new Uint32Array([0x53534150])], 16, "Various fileBits");
test_first_argument([12], 2, "Number in fileBits");
test_first_argument([[1,2,3]], 5, "Array in fileBits");
test_first_argument([{}], 15, "Object in fileBits"); // "[object Object]"
if (globalThis.document !== undefined) {
test_first_argument([document.body], 24, "HTMLBodyElement in fileBits"); // "[object HTMLBodyElement]"
}
test_first_argument([to_string_obj], 8, "Object with toString in fileBits");
test_first_argument({[Symbol.iterator]() {
let i = 0;
return {next: () => [
{done:false, value:'ab'},
{done:false, value:'cde'},
{done:true}
][i++]};
}}, 5, 'Custom @@iterator');
[
'hello',
0,
null
].forEach(arg => {
test(t => {
assert_throws_js(TypeError, () => new File(arg, 'world.html'),
'Constructor should throw for invalid bits argument');
}, `Invalid bits argument: ${JSON.stringify(arg)}`);
});
test(t => {
assert_throws_js(Error, () => new File([to_string_throws], 'name.txt'),
'Constructor should propagate exceptions');
}, 'Bits argument: object that throws');
function test_second_argument(arg2, expectedFileName, testName) {
test(function() {
var file = new File(["bits"], arg2);
assert_true(file instanceof File);
assert_equals(file.name, expectedFileName);
}, testName);
}
test_second_argument("dummy", "dummy", "Using fileName");
test_second_argument("dummy/foo", "dummy/foo",
"No replacement when using special character in fileName");
test_second_argument(null, "null", "Using null fileName");
test_second_argument(1, "1", "Using number fileName");
test_second_argument('', '', "Using empty string fileName");
if (globalThis.document !== undefined) {
test_second_argument(document.body, '[object HTMLBodyElement]', "Using object fileName");
}
// testing the third argument
[
{type: 'text/plain', expected: 'text/plain'},
{type: 'text/plain;charset=UTF-8', expected: 'text/plain;charset=utf-8'},
{type: 'TEXT/PLAIN', expected: 'text/plain'},
{type: '𝓽𝓮𝔁𝓽/𝔭𝔩𝔞𝔦𝔫', expected: ''},
{type: 'ascii/nonprintable\u001F', expected: ''},
{type: 'ascii/nonprintable\u007F', expected: ''},
{type: 'nonascii\u00EE', expected: ''},
{type: 'nonascii\u1234', expected: ''},
{type: 'nonparsable', expected: 'nonparsable'}
].forEach(testCase => {
test(t => {
var file = new File(["bits"], "dummy", { type: testCase.type});
assert_true(file instanceof File);
assert_equals(file.type, testCase.expected);
}, `Using type in File constructor: ${testCase.type}`);
});
test(function() {
var file = new File(["bits"], "dummy", { lastModified: 42 });
assert_true(file instanceof File);
assert_equals(file.lastModified, 42);
}, "Using lastModified");
test(function() {
var file = new File(["bits"], "dummy", { name: "foo" });
assert_true(file instanceof File);
assert_equals(file.name, "dummy");
}, "Misusing name");
test(function() {
var file = new File(["bits"], "dummy", { unknownKey: "value" });
assert_true(file instanceof File);
assert_equals(file.name, "dummy");
}, "Unknown properties are ignored");
[
123,
123.4,
true,
'abc'
].forEach(arg => {
test(t => {
assert_throws_js(TypeError, () => new File(['bits'], 'name.txt', arg),
'Constructor should throw for invalid property bag type');
}, `Invalid property bag: ${JSON.stringify(arg)}`);
});
[
null,
undefined,
[1,2,3],
/regex/,
function() {}
].forEach(arg => {
test(t => {
assert_equals(new File(['bits'], 'name.txt', arg).size, 4,
'Constructor should accept object-ish property bag type');
}, `Unusual but valid property bag: ${arg}`);
});
test(t => {
assert_throws_js(Error,
() => new File(['bits'], 'name.txt', {type: to_string_throws}),
'Constructor should propagate exceptions');
}, 'Property bag propagates exceptions');

View File

@ -0,0 +1,15 @@
importScripts("/resources/testharness.js");
async_test(function() {
var file = new File(["bits"], "dummy", { 'type': 'text/plain', lastModified: 42 });
var reader = new FileReader();
reader.onload = this.step_func_done(function() {
assert_equals(file.name, "dummy", "file name");
assert_equals(reader.result, "bits", "file content");
assert_equals(file.lastModified, 42, "file lastModified");
});
reader.onerror = this.unreached_func("Unexpected error event");
reader.readAsText(file);
}, "FileReader in Worker");
done();

View File

@ -0,0 +1,113 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>Upload files named using controls</title>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
/>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist"
/>
<link rel="help" href="https://w3c.github.io/FileAPI/#file-constructor" />
<link
rel="author"
title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
"use strict";
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-NUL-[\0].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-NUL-[\0].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-BS-[\b].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-BS-[\b].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VT-[\v].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-VT-[\v].txt",
});
// These have characters that undergo processing in name=,
// filename=, and/or value; formPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-[\n].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-LF-[\n].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-[\r].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-CR-[\r].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-HT-[\t].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-HT-[\t].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FF-[\f].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-FF-[\f].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DEL-[\x7F].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-DEL-[\x7F].txt",
});
// The rest should be passed through unmodified:
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ESC-[\x1B].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-ESC-[\x1B].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SPACE-[ ].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-SPACE-[ ].txt",
});
</script>

View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<meta charset="utf-8">
<meta name=timeout content=long>
<title>Upload files in ISO-2022-JP form</title>
<link rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
<link rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
<link rel="help"
href="https://w3c.github.io/FileAPI/#file-constructor">
<link rel="author" title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
'use strict';
formPostFileUploadTest({
fileNameSource: 'ASCII',
fileBaseName: 'file-for-upload-in-form.txt',
formEncoding: 'ISO-2022-JP',
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
});
formPostFileUploadTest({
fileNameSource: 'x-user-defined',
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
formEncoding: 'ISO-2022-JP',
expectedEncodedBaseName: (
'file-for-upload-in-form-&#63472;&#63379;&#63363;&#63392;.txt'),
});
formPostFileUploadTest({
fileNameSource: 'windows-1252',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'ISO-2022-JP',
expectedEncodedBaseName: (
'file-for-upload-in-form-&#226;&#732;&#186;&#240;&#376;&#732;&#8218;.txt'),
});
formPostFileUploadTest({
fileNameSource: 'JIS X 0201 and JIS X 0208',
fileBaseName: 'file-for-upload-in-form-★星★.txt',
formEncoding: 'ISO-2022-JP',
expectedEncodedBaseName: 'file-for-upload-in-form-\x1B$B!z@1!z\x1B(B.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'ISO-2022-JP',
expectedEncodedBaseName: 'file-for-upload-in-form-&#9786;&#128514;.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
formEncoding: 'ISO-2022-JP',
expectedEncodedBaseName: `file-for-upload-in-form-${
kTestFallbackIso2022jp
}.txt`,
});
</script>

View File

@ -0,0 +1,226 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>Upload files named using punctuation</title>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
/>
<link
rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist"
/>
<link rel="help" href="https://w3c.github.io/FileAPI/#file-constructor" />
<link
rel="author"
title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
"use strict";
// These have characters that undergo processing in name=,
// filename=, and/or value; formPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUOTATION-MARK-[\x22].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-QUOTATION-MARK-[\x22].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: '"file-for-upload-in-form-double-quoted.txt"',
formEncoding: "UTF-8",
expectedEncodedBaseName: '"file-for-upload-in-form-double-quoted.txt"',
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt",
});
// The rest should be passed through unmodified:
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ASTERISK-[*].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-ASTERISK-[*].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COMMA-[,].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-COMMA-[,].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COLON-[:].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-COLON-[:].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName:
"file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-TILDE-[~].txt",
formEncoding: "UTF-8",
expectedEncodedBaseName: "file-for-upload-in-form-TILDE-[~].txt",
});
formPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "'file-for-upload-in-form-single-quoted.txt'",
formEncoding: "UTF-8",
expectedEncodedBaseName: "'file-for-upload-in-form-single-quoted.txt'",
});
</script>

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Upload files in UTF-8 form</title>
<link rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
<link rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
<link rel="help"
href="https://w3c.github.io/FileAPI/#file-constructor">
<link rel="author" title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
'use strict';
formPostFileUploadTest({
fileNameSource: 'ASCII',
fileBaseName: 'file-for-upload-in-form.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
});
formPostFileUploadTest({
fileNameSource: 'x-user-defined',
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: (
'file-for-upload-in-form-\xEF\x9F\xB0\xEF\x9E\x93\xEF\x9E\x83\xEF\x9E\xA0.txt'),
});
formPostFileUploadTest({
fileNameSource: 'windows-1252',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: (
'file-for-upload-in-form-\xC3\xA2\xCB\x9C\xC2\xBA\xC3\xB0\xC5\xB8\xCB\x9C\xE2\x80\x9A.txt'),
});
formPostFileUploadTest({
fileNameSource: 'JIS X 0201 and JIS X 0208',
fileBaseName: 'file-for-upload-in-form-★星★.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form-\xE2\x98\x85\xE6\x98\x9F\xE2\x98\x85.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form-\xE2\x98\xBA\xF0\x9F\x98\x82.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
formEncoding: 'UTF-8',
expectedEncodedBaseName: `file-for-upload-in-form-${kTestFallbackUtf8}.txt`,
});
</script>

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Upload files in Windows-1252 form</title>
<link rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
<link rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
<link rel="help"
href="https://w3c.github.io/FileAPI/#file-constructor">
<link rel="author" title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
'use strict';
formPostFileUploadTest({
fileNameSource: 'ASCII',
fileBaseName: 'file-for-upload-in-form.txt',
formEncoding: 'windows-1252',
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
});
formPostFileUploadTest({
fileNameSource: 'x-user-defined',
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
formEncoding: 'windows-1252',
expectedEncodedBaseName: 'file-for-upload-in-form-&#63472;&#63379;&#63363;&#63392;.txt',
});
formPostFileUploadTest({
fileNameSource: 'windows-1252',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'windows-1252',
expectedEncodedBaseName: 'file-for-upload-in-form-\xE2\x98\xBA\xF0\x9F\x98\x82.txt',
});
formPostFileUploadTest({
fileNameSource: 'JIS X 0201 and JIS X 0208',
fileBaseName: 'file-for-upload-in-form-★星★.txt',
formEncoding: 'windows-1252',
expectedEncodedBaseName: 'file-for-upload-in-form-&#9733;&#26143;&#9733;.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'windows-1252',
expectedEncodedBaseName: 'file-for-upload-in-form-&#9786;&#128514;.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
formEncoding: 'windows-1252',
expectedEncodedBaseName: `file-for-upload-in-form-${
kTestFallbackWindows1252
}.txt`,
});
</script>

View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Upload files in x-user-defined form</title>
<link rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
<link rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
<link rel="help"
href="https://w3c.github.io/FileAPI/#file-constructor">
<link rel="author" title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
'use strict';
formPostFileUploadTest({
fileNameSource: 'ASCII',
fileBaseName: 'file-for-upload-in-form.txt',
formEncoding: 'x-user-defined',
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
});
formPostFileUploadTest({
fileNameSource: 'x-user-defined',
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
formEncoding: 'x-user-defined',
expectedEncodedBaseName: 'file-for-upload-in-form-\xF0\x93\x83\xA0.txt',
});
formPostFileUploadTest({
fileNameSource: 'windows-1252',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'x-user-defined',
expectedEncodedBaseName: ('file-for-upload-in-form-' +
'&#226;&#732;&#186;&#240;&#376;&#732;&#8218;.txt'),
});
formPostFileUploadTest({
fileNameSource: 'JIS X 0201 and JIS X 0208',
fileBaseName: 'file-for-upload-in-form-★星★.txt',
formEncoding: 'x-user-defined',
expectedEncodedBaseName: 'file-for-upload-in-form-&#9733;&#26143;&#9733;.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'x-user-defined',
expectedEncodedBaseName: 'file-for-upload-in-form-&#9786;&#128514;.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
formEncoding: 'x-user-defined',
expectedEncodedBaseName: `file-for-upload-in-form-${
kTestFallbackXUserDefined
}.txt`,
});
</script>

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Upload ASCII-named file in UTF-8 form</title>
<link rel="help"
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
<link rel="help"
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
<link rel="help"
href="https://w3c.github.io/FileAPI/#file-constructor">
<link rel="author" title="Benjamin C. Wiley Sittler"
href="mailto:bsittler@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/send-file-form-helper.js"></script>
<script>
'use strict';
formPostFileUploadTest({
fileNameSource: 'ASCII',
fileBaseName: 'file-for-upload-in-form.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
});
</script>

View File

@ -0,0 +1,69 @@
// META: title=FormData: FormData: Upload files named using controls
// META: script=../support/send-file-formdata-helper.js
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-NUL-[\0].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-BS-[\b].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VT-[\v].txt",
});
// These have characters that undergo processing in name=,
// filename=, and/or value; formDataPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-[\n].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-[\r].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-HT-[\t].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FF-[\f].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DEL-[\x7F].txt",
});
// The rest should be passed through unmodified:
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ESC-[\x1B].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SPACE-[ ].txt",
});

View File

@ -0,0 +1,144 @@
// META: title=FormData: FormData: Upload files named using punctuation
// META: script=../support/send-file-formdata-helper.js
"use strict";
// These have characters that undergo processing in name=,
// filename=, and/or value; formDataPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUOTATION-MARK-[\x22].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: '"file-for-upload-in-form-double-quoted.txt"',
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt",
});
// The rest should be passed through unmodified:
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ASTERISK-[*].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COMMA-[,].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COLON-[:].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-TILDE-[~].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "'file-for-upload-in-form-single-quoted.txt'",
});

View File

@ -0,0 +1,33 @@
// META: title=FormData: FormData: Upload files in UTF-8 fetch()
// META: script=../support/send-file-formdata-helper.js
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form.txt",
});
formDataPostFileUploadTest({
fileNameSource: "x-user-defined",
fileBaseName: "file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt",
});
formDataPostFileUploadTest({
fileNameSource: "windows-1252",
fileBaseName: "file-for-upload-in-form-☺😂.txt",
});
formDataPostFileUploadTest({
fileNameSource: "JIS X 0201 and JIS X 0208",
fileBaseName: "file-for-upload-in-form-★星★.txt",
});
formDataPostFileUploadTest({
fileNameSource: "Unicode",
fileBaseName: "file-for-upload-in-form-☺😂.txt",
});
formDataPostFileUploadTest({
fileNameSource: "Unicode",
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
});

View File

@ -0,0 +1,8 @@
// META: title=FormData: Upload ASCII-named file in UTF-8 form
// META: script=../support/send-file-formdata-helper.js
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form.txt",
});

View File

@ -0,0 +1,59 @@
// META: title=FileReader States
'use strict';
test(function () {
assert_true(
"FileReader" in globalThis,
"globalThis should have a FileReader property.",
);
}, "FileReader interface object");
test(function () {
var fileReader = new FileReader();
assert_true(fileReader instanceof FileReader);
}, "no-argument FileReader constructor");
var t_abort = async_test("FileReader States -- abort");
t_abort.step(function () {
var fileReader = new FileReader();
assert_equals(fileReader.readyState, 0);
assert_equals(fileReader.readyState, FileReader.EMPTY);
var blob = new Blob();
fileReader.readAsArrayBuffer(blob);
assert_equals(fileReader.readyState, 1);
assert_equals(fileReader.readyState, FileReader.LOADING);
fileReader.onabort = this.step_func(function (e) {
assert_equals(fileReader.readyState, 2);
assert_equals(fileReader.readyState, FileReader.DONE);
t_abort.done();
});
fileReader.abort();
fileReader.onabort = this.unreached_func("abort event should fire sync");
});
var t_event = async_test("FileReader States -- events");
t_event.step(function () {
var fileReader = new FileReader();
var blob = new Blob();
fileReader.readAsArrayBuffer(blob);
fileReader.onloadstart = this.step_func(function (e) {
assert_equals(fileReader.readyState, 1);
assert_equals(fileReader.readyState, FileReader.LOADING);
});
fileReader.onprogress = this.step_func(function (e) {
assert_equals(fileReader.readyState, 1);
assert_equals(fileReader.readyState, FileReader.LOADING);
});
fileReader.onloadend = this.step_func(function (e) {
assert_equals(fileReader.readyState, 2);
assert_equals(fileReader.readyState, FileReader.DONE);
t_event.done();
});
});

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>FileAPI Test: filelist</title>
<link rel='author' title='Intel' href='http://www.intel.com'>
<link rel='help' href='http://dev.w3.org/2006/webapi/FileAPI/#filelist-section'>
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-length">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-item">
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
</head>
<body>
<form name='uploadData' style="display:none">
<input type='file' id='fileChooser'>
</form>
<div id='log'></div>
<script>
var fileList;
setup(function () {
fileList = document.querySelector('#fileChooser').files;
});
test(function () {
assert_true('FileList' in window, 'window has a FileList property');
}, 'Check if window has a FileList property');
test(function () {
assert_equals(FileList.length, 0, 'FileList.length is 0');
}, 'Check if FileList.length is 0');
test(function () {
assert_true(fileList.item instanceof Function, 'item is a instanceof Function');
}, 'Check if item is a instanceof Function');
test(function() {
assert_inherits(fileList, 'item', 'item is a method of fileList');
}, 'Check if item is a method of fileList');
test(function() {
assert_equals(fileList.item(0), null, 'item method returns null');
}, 'Check if the item method returns null when no file selected');
test(function() {
assert_inherits(fileList, 'length', 'length is fileList attribute');
}, 'Check if length is fileList\'s attribute');
test(function() {
assert_equals(fileList.length, 0, 'fileList length is 0');
}, 'Check if the fileList length is 0 when no file selected');
</script>
</body>
</html>

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>FileAPI Test: filelist_multiple_selected_files</title>
<link rel='author' title='Intel' href='http://www.intel.com'>
<link rel='help' href='http://dev.w3.org/2006/webapi/FileAPI/#filelist-section'>
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-length">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-item">
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
</head>
<body>
<form name='uploadData'>
<input type='file' id='fileChooser' multiple>
</form>
<div>
<p>Test steps:</p>
<ol>
<li>Download <a href='support/upload.txt'>upload.txt</a>, <a href="support/upload.zip">upload.zip</a> to local.</li>
<li>Select the local two files (upload.txt, upload.zip) to run the test.</li>
</ol>
</div>
<div id='log'></div>
<script>
var fileInput = document.querySelector('#fileChooser');
var fileList;
setup({explicit_done: true, explicit_timeout: true});
on_event(fileInput, 'change', function(evt) {
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_equals(fileList.length, 2, 'fileList length is 2');
}, 'Check if the fileList length is 2 when selected two files');
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_true(fileList.item(0) instanceof File, 'item method is instanceof File');
}, 'Check if the item method returns the File interface when selected two files');
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_not_equals(fileList.item(1), null, 'item(1) is not null');
}, 'Check if item(1) is not null when selected two files. Index must be treated by user agents as value for the position of a File object in the FileList, with 0 representing the first file.');
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_equals(fileList.item(2), null, 'item(2) is null');
}, 'Check if item(2) is null when selected two files');
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_array_equals([fileList.item(0).name, fileList.item(1).name], ['upload.txt', 'upload.zip'], 'file name string is the name of selected files "upload.txt", "upload.zip"');
}, 'Check if the file name string is the name of selected files');
done();
});
</script>
</body>
</html>

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>FileAPI Test: filelist_selected_file</title>
<link rel='author' title='Intel' href='http://www.intel.com'>
<link rel='help' href='http://dev.w3.org/2006/webapi/FileAPI/#filelist-section'>
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-length">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-item">
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
</head>
<body>
<form name='uploadData'>
<input type='file' id='fileChooser'>
</form>
<div>
<p>Test steps:</p>
<ol>
<li>Download <a href='support/upload.txt'>upload.txt</a> to local.</li>
<li>Select the local upload.txt file to run the test.</li>
</ol>
</div>
<div id='log'></div>
<script>
var fileInput = document.querySelector('#fileChooser');
var fileList;
setup({explicit_done: true, explicit_timeout: true});
on_event(fileInput, 'change', function(evt) {
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_equals(fileList.length, 1, 'fileList length is 1');
}, 'Check if the fileList length is 1 when selected one file');
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_true(fileList.item(0) instanceof File, 'item method is instanceof File');
}, 'Check if the item method returns the File interface when selected one file');
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_not_equals(fileList.item(0), null, 'item(0) is not null');
}, 'Check if item(0) is not null when selected one file. Index must be treated by user agents as value for the position of a File object in the FileList, with 0 representing the first file.');
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_equals(fileList.item(1), null, 'item(1) is null');
}, 'Check if item(1) is null when selected one file only');
test(function() {
fileList = document.querySelector('#fileChooser').files;
assert_equals(fileList.item(0).name, 'upload.txt', 'file name string is "upload.txt"');
}, 'Check if the file name string is the selected "upload.txt"');
done();
});
</script>
</body>
</html>

View File

@ -0,0 +1 @@
Hello, this is test file for file upload.

Binary file not shown.

View File

@ -0,0 +1,65 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Historical features</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
</head>
<body>
<div id="log"></div>
<script>
var removedFromWindow = [
'toNativeLineEndings',
'FileError',
'FileException',
'FileHandle',
'FileRequest',
'MutableFile',
];
removedFromWindow.forEach(function(name) {
test(function() {
assert_false(name in window);
}, '"' + name + '" should not be supported');
});
test(function() {
var b = new Blob();
var prefixes = ['op', 'moz', 'webkit', 'ms'];
for (var i = 0; i < prefixes.length; ++i) {
assert_false(prefixes[i]+'Slice' in b, "'"+prefixes[i]+"Slice' in b");
assert_false(prefixes[i]+'Slice' in Blob.prototype, "'"+prefixes[i]+"Slice in Blob.prototype");
}
}, 'Blob should not support slice prefixed');
test(function() {
var prefixes = ['', 'O', 'Moz', 'WebKit', 'MS'];
for (var i = 0; i < prefixes.length; ++i) {
assert_false(prefixes[i]+'BlobBuilder' in window, prefixes[i]+'BlobBuilder');
}
}, 'BlobBuilder should not be supported.');
test(function() {
assert_false('createFor' in URL);
}, 'createFor method should not be supported');
test(function() {
var b = new Blob();
assert_false('close' in b, 'close in b');
assert_false('close' in Blob.prototype, 'close in Blob.prototype');
assert_false('isClosed' in b, 'isClosed in b');
assert_false('isClosed' in Blob.prototype, 'isClosed in Blob.prototype');
}, 'Blob.close() should not be supported');
test(() => {
const f = new File([], "");
assert_false("lastModifiedDate" in f);
assert_false("lastModifiedDate" in File.prototype);
}, "File's lastModifiedDate should not be supported");
service_worker_test('support/historical-serviceworker.js', 'Service worker test setup');
</script>
</body>
</html>

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>File API manual IDL tests</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#conformance">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/WebIDLParser.js"></script>
<script src="/resources/idlharness.js"></script>
</head>
<body>
<h1>File API manual IDL tests</h1>
<p>Either download <a href="support/upload.txt">upload.txt</a> and select it below or select an
arbitrary local file.</p>
<form name="uploadData">
<input type="file" id="fileChooser">
</form>
<div id="log"></div>
<script>
const fileInput = document.querySelector("#fileChooser");
setup({explicit_timeout: true});
idl_test(
['FileAPI'],
['dom', 'html', 'url'],
async idl_array => {
await new Promise(resolve => {
on_event(fileInput, "change", resolve);
});
idl_array.add_objects({
FileList: [fileInput.files],
File: [fileInput.files[0]],
});
}
);
</script>
</body>
</html>

View File

@ -0,0 +1,19 @@
// META: script=/resources/WebIDLParser.js
// META: script=/resources/idlharness.js
// META: timeout=long
'use strict';
// https://w3c.github.io/FileAPI/
idl_test(
['FileAPI'],
['dom', 'html', 'url'],
idl_array => {
idl_array.add_objects({
Blob: ['new Blob(["TEST"])'],
File: ['new File(["myFileBits"], "myFileName")'],
FileReader: ['new FileReader()']
});
}
);

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>File API automated IDL tests (requiring dom)</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#conformance">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/WebIDLParser.js"></script>
<script src="/resources/idlharness.js"></script>
</head>
<body>
<h1>File API automated IDL tests</h1>
<div id="log"></div>
<form name="uploadData">
<input type="file" id="fileChooser">
</form>
<script>
'use strict';
idl_test(
['FileAPI'],
['dom', 'html', 'url'],
idl_array => {
idl_array.add_objects({
FileList: ['document.querySelector("#fileChooser").files']
});
}
);
</script>
</body>
</html>

View File

@ -0,0 +1,17 @@
importScripts("/resources/testharness.js");
importScripts("/resources/WebIDLParser.js", "/resources/idlharness.js");
'use strict';
// https://w3c.github.io/FileAPI/
idl_test(
['FileAPI'],
['dom', 'html', 'url'],
idl_array => {
idl_array.add_objects({
FileReaderSync: ['new FileReaderSync()']
});
}
);
done();

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Process Events for FileReader</title>
<link rel=help href="http://dev.w3.org/2006/webapi/FileAPI/#event-handler-attributes-section">
<link rel=author title="Jinks Zhao" href="mailto:jinks@maxthon.com">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
Please choose one file through this input below.<br>
<input type="file" id="filer">
<div id="log"></div>
<script>
var input, reader, progressEventCounter, progressEventTimeList,
lastProgressEventTime;
setup(function() {
input = document.getElementById('filer');
reader = new FileReader();
progressEventCounter = 0;
progressEventTimeList = [];
lastProgressEventTime;
}, { explicit_timeout: true });
var t = async_test("FileReader progress events.")
reader.onprogress = t.step_func(function () {
var newTime = new Date;
var timeout = newTime - lastProgressEventTime;
progressEventTimeList.push(timeout);
lastProgressEventTime = newTime;
progressEventCounter++;
assert_less_than_equal(timeout, 50, "The progress event should be fired every 50ms.");
});
reader.onload = t.step_func_done(function () {
assert_greater_than_equal(progressEventCounter, 1,
"When read completely, the progress event must be fired at least once.")
});
input.onchange = t.step_func(function () {
var files = input.files;
assert_greater_than(files.length, 0);
var file = files[0];
lastProgressEventTime = new Date;
reader.readAsArrayBuffer(file);
});
</script>

View File

@ -0,0 +1,81 @@
// META: title=FileAPI Test: Blob Determining Encoding
var t = async_test("Blob Determing Encoding with encoding argument");
t.step(function() {
// string 'hello'
var data = [0xFE,0xFF,0x00,0x68,0x00,0x65,0x00,0x6C,0x00,0x6C,0x00,0x6F];
var blob = new Blob([new Uint8Array(data)]);
var reader = new FileReader();
reader.onloadend = t.step_func_done (function(event) {
assert_equals(this.result, "hello", "The FileReader should read the ArrayBuffer through UTF-16BE.")
}, reader);
reader.readAsText(blob, "UTF-16BE");
});
var t = async_test("Blob Determing Encoding with type attribute");
t.step(function() {
var data = [0xFE,0xFF,0x00,0x68,0x00,0x65,0x00,0x6C,0x00,0x6C,0x00,0x6F];
var blob = new Blob([new Uint8Array(data)], {type:"text/plain;charset=UTF-16BE"});
var reader = new FileReader();
reader.onloadend = t.step_func_done (function(event) {
assert_equals(this.result, "hello", "The FileReader should read the ArrayBuffer through UTF-16BE.")
}, reader);
reader.readAsText(blob);
});
var t = async_test("Blob Determing Encoding with UTF-8 BOM");
t.step(function() {
var data = [0xEF,0xBB,0xBF,0x68,0x65,0x6C,0x6C,0xC3,0xB6];
var blob = new Blob([new Uint8Array(data)]);
var reader = new FileReader();
reader.onloadend = t.step_func_done (function(event) {
assert_equals(this.result, "hellö", "The FileReader should read the blob with UTF-8.");
}, reader);
reader.readAsText(blob);
});
var t = async_test("Blob Determing Encoding without anything implying charset.");
t.step(function() {
var data = [0x68,0x65,0x6C,0x6C,0xC3,0xB6];
var blob = new Blob([new Uint8Array(data)]);
var reader = new FileReader();
reader.onloadend = t.step_func_done (function(event) {
assert_equals(this.result, "hellö", "The FileReader should read the blob by default with UTF-8.");
}, reader);
reader.readAsText(blob);
});
var t = async_test("Blob Determing Encoding with UTF-16BE BOM");
t.step(function() {
var data = [0xFE,0xFF,0x00,0x68,0x00,0x65,0x00,0x6C,0x00,0x6C,0x00,0x6F];
var blob = new Blob([new Uint8Array(data)]);
var reader = new FileReader();
reader.onloadend = t.step_func_done (function(event) {
assert_equals(this.result, "hello", "The FileReader should read the ArrayBuffer through UTF-16BE.");
}, reader);
reader.readAsText(blob);
});
var t = async_test("Blob Determing Encoding with UTF-16LE BOM");
t.step(function() {
var data = [0xFF,0xFE,0x68,0x00,0x65,0x00,0x6C,0x00,0x6C,0x00,0x6F,0x00];
var blob = new Blob([new Uint8Array(data)]);
var reader = new FileReader();
reader.onloadend = t.step_func_done (function(event) {
assert_equals(this.result, "hello", "The FileReader should read the ArrayBuffer through UTF-16LE.");
}, reader);
reader.readAsText(blob);
});

View File

@ -0,0 +1,17 @@
// META: title=FileReader event handler attributes
var attributes = [
"onloadstart",
"onprogress",
"onload",
"onabort",
"onerror",
"onloadend",
];
attributes.forEach(function(a) {
test(function() {
var reader = new FileReader();
assert_equals(reader[a], null,
"event handler attribute should initially be null");
}, "FileReader." + a + ": initial value");
});

View File

@ -0,0 +1,81 @@
// META: title=FileReader: starting new reads while one is in progress
test(function() {
var blob_1 = new Blob(['TEST000000001'])
var blob_2 = new Blob(['TEST000000002'])
var reader = new FileReader();
reader.readAsText(blob_1)
assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING")
assert_throws_dom("InvalidStateError", function () {
reader.readAsText(blob_2)
})
}, 'test FileReader InvalidStateError exception for readAsText');
test(function() {
var blob_1 = new Blob(['TEST000000001'])
var blob_2 = new Blob(['TEST000000002'])
var reader = new FileReader();
reader.readAsDataURL(blob_1)
assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING")
assert_throws_dom("InvalidStateError", function () {
reader.readAsDataURL(blob_2)
})
}, 'test FileReader InvalidStateError exception for readAsDataURL');
test(function() {
var blob_1 = new Blob(['TEST000000001'])
var blob_2 = new Blob(['TEST000000002'])
var reader = new FileReader();
reader.readAsArrayBuffer(blob_1)
assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING")
assert_throws_dom("InvalidStateError", function () {
reader.readAsArrayBuffer(blob_2)
})
}, 'test FileReader InvalidStateError exception for readAsArrayBuffer');
async_test(function() {
var blob_1 = new Blob(['TEST000000001'])
var blob_2 = new Blob(['TEST000000002'])
var reader = new FileReader();
var triggered = false;
reader.onloadstart = this.step_func_done(function() {
assert_false(triggered, "Only one loadstart event should be dispatched");
triggered = true;
assert_equals(reader.readyState, FileReader.LOADING,
"readyState must be LOADING")
assert_throws_dom("InvalidStateError", function () {
reader.readAsArrayBuffer(blob_2)
})
});
reader.readAsArrayBuffer(blob_1)
assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING")
}, 'test FileReader InvalidStateError exception in onloadstart event for readAsArrayBuffer');
async_test(function() {
var blob_1 = new Blob(['TEST000000001'])
var blob_2 = new Blob(['TEST000000002'])
var reader = new FileReader();
reader.onloadend = this.step_func_done(function() {
assert_equals(reader.readyState, FileReader.DONE,
"readyState must be DONE")
reader.readAsArrayBuffer(blob_2)
assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING")
});
reader.readAsArrayBuffer(blob_1)
assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING")
}, 'test FileReader no InvalidStateError exception in loadend event handler for readAsArrayBuffer');
async_test(function() {
var blob_1 = new Blob([new Uint8Array(0x414141)]);
var blob_2 = new Blob(['TEST000000002']);
var reader = new FileReader();
reader.onloadstart = this.step_func(function() {
reader.abort();
reader.onloadstart = null;
reader.onloadend = this.step_func_done(function() {
assert_equals('TEST000000002', reader.result);
});
reader.readAsText(blob_2);
});
reader.readAsText(blob_1);
}, 'test abort and restart in onloadstart event for readAsText');

View File

@ -0,0 +1,38 @@
// META: title=FileAPI Test: filereader_abort
test(function() {
var readerNoRead = new FileReader();
readerNoRead.abort();
assert_equals(readerNoRead.readyState, readerNoRead.EMPTY);
assert_equals(readerNoRead.result, null);
}, "Aborting before read");
promise_test(t => {
var blob = new Blob(["TEST THE ABORT METHOD"]);
var readerAbort = new FileReader();
var eventWatcher = new EventWatcher(t, readerAbort,
['abort', 'loadstart', 'loadend', 'error', 'load']);
// EventWatcher doesn't let us inspect the state after the abort event,
// so add an extra event handler for that.
readerAbort.addEventListener('abort', t.step_func(e => {
assert_equals(readerAbort.readyState, readerAbort.DONE);
}));
readerAbort.readAsText(blob);
return eventWatcher.wait_for('loadstart')
.then(() => {
assert_equals(readerAbort.readyState, readerAbort.LOADING);
// 'abort' and 'loadend' events are dispatched synchronously, so
// call wait_for before calling abort.
var nextEvent = eventWatcher.wait_for(['abort', 'loadend']);
readerAbort.abort();
return nextEvent;
})
.then(() => {
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=24401
assert_equals(readerAbort.result, null);
assert_equals(readerAbort.readyState, readerAbort.DONE);
});
}, "Aborting after read");

View File

@ -0,0 +1,19 @@
// META: title=FileAPI Test: filereader_error
async_test(function() {
var blob = new Blob(["TEST THE ERROR ATTRIBUTE AND ERROR EVENT"]);
var reader = new FileReader();
assert_equals(reader.error, null, "The error is null when no error occurred");
reader.onload = this.step_func(function(evt) {
assert_unreached("Should not dispatch the load event");
});
reader.onloadend = this.step_func(function(evt) {
assert_equals(reader.result, null, "The result is null");
this.done();
});
reader.readAsText(blob);
reader.abort();
});

View File

@ -0,0 +1,19 @@
promise_test(async t => {
var reader = new FileReader();
var eventWatcher = new EventWatcher(t, reader, ['loadstart', 'progress', 'abort', 'error', 'load', 'loadend']);
reader.readAsText(new Blob([]));
await eventWatcher.wait_for('loadstart');
// No progress event for an empty blob, as no data is loaded.
await eventWatcher.wait_for('load');
await eventWatcher.wait_for('loadend');
}, 'events are dispatched in the correct order for an empty blob');
promise_test(async t => {
var reader = new FileReader();
var eventWatcher = new EventWatcher(t, reader, ['loadstart', 'progress', 'abort', 'error', 'load', 'loadend']);
reader.readAsText(new Blob(['a']));
await eventWatcher.wait_for('loadstart');
await eventWatcher.wait_for('progress');
await eventWatcher.wait_for('load');
await eventWatcher.wait_for('loadend');
}, 'events are dispatched in the correct order for a non-empty blob');

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FileAPI Test: filereader_file</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#FileReader-interface">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#file">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div>
<p>Test step:</p>
<ol>
<li>Download <a href="support/blue-100x100.png">blue-100x100.png</a> to local.</li>
<li>Select the local file (blue-100x100.png) to run the test.</li>
</ol>
</div>
<form name="uploadData">
<input type="file" id="fileChooser">
</form>
<div id="log"></div>
<script>
var fileInput = document.querySelector('#fileChooser');
var reader = new FileReader();
//readType: 1-> ArrayBuffer, 2-> Text, 3-> DataURL
var readType = 1;
setup({
explicit_done: true,
explicit_timeout: true,
});
on_event(fileInput, "change", function(evt) {
reader.readAsArrayBuffer(fileInput.files[0]);
});
on_event(reader, "load", function(evt) {
if (readType == 1) {
test(function() {
assert_true(reader.result instanceof ArrayBuffer, "The result is instanceof ArrayBuffer");
}, "Check if the readAsArrayBuffer works");
readType++;
reader.readAsText(fileInput.files[0]);
} else if (readType == 2) {
test(function() {
assert_equals(typeof reader.result, "string", "The result is typeof string");
}, "Check if the readAsText works");
readType++;
reader.readAsDataURL(fileInput.files[0]);
} else if (readType == 3) {
test(function() {
assert_equals(typeof reader.result, "string", "The result is typeof string");
assert_equals(reader.result.indexOf("data"), 0, "The result starts with 'data'");
assert_true(reader.result.indexOf("base64") > 0, "The result contains 'base64'");
}, "Check if the readAsDataURL works");
done();
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FileAPI Test: filereader_file_img</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#FileReader-interface">
<link rel="help" href="http://dev.w3.org/2006/webapi/FileAPI/#file">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div>
<p>Test step:</p>
<ol>
<li>Download <a href="support/blue-100x100.png">blue-100x100.png</a> to local.</li>
<li>Select the local file (blue-100x100.png) to run the test.</li>
</ol>
</div>
<form name="uploadData">
<input type="file" id="fileChooser">
</form>
<div id="log"></div>
<script>
var fileInput = document.querySelector('#fileChooser');
var reader = new FileReader();
setup({
explicit_done: true,
explicit_timeout: true,
});
fileInput.addEventListener("change", function(evt) {
reader.readAsDataURL(fileInput.files[0]);
}, false);
reader.addEventListener("loadend", function(evt) {
test(function () {
assert_true(reader.result.indexOf("iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAqklEQVR42u3RsREAMAgDMe+/M4E7ZkhBoeI9gJWkWpfaeToTECACAkRAgAgIEAEB4gQgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgQJwARECACAgQ/W4AQauujc8IdAoAAAAASUVORK5CYII=") != -1, "Encoded image")
}, "Check if readAsDataURL returns correct image");
done();
}, false);
</script>
</body>
</html>

View File

@ -0,0 +1,23 @@
// META: title=FileAPI Test: filereader_readAsArrayBuffer
async_test(function() {
var blob = new Blob(["TEST"]);
var reader = new FileReader();
reader.onload = this.step_func(function(evt) {
assert_equals(reader.result.byteLength, 4, "The byteLength is 4");
assert_true(reader.result instanceof ArrayBuffer, "The result is instanceof ArrayBuffer");
assert_equals(reader.readyState, reader.DONE);
this.done();
});
reader.onloadstart = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.LOADING);
});
reader.onprogress = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.LOADING);
});
reader.readAsArrayBuffer(blob);
});

View File

@ -0,0 +1,23 @@
// META: title=FileAPI Test: filereader_readAsBinaryString
async_test(t => {
const blob = new Blob(["σ"]);
const reader = new FileReader();
reader.onload = t.step_func_done(() => {
assert_equals(typeof reader.result, "string", "The result is string");
assert_equals(reader.result.length, 2, "The result length is 2");
assert_equals(reader.result, "\xcf\x83", "The result is \xcf\x83");
assert_equals(reader.readyState, reader.DONE);
});
reader.onloadstart = t.step_func(() => {
assert_equals(reader.readyState, reader.LOADING);
});
reader.onprogress = t.step_func(() => {
assert_equals(reader.readyState, reader.LOADING);
});
reader.readAsBinaryString(blob);
});

View File

@ -0,0 +1,54 @@
// META: title=FileAPI Test: FileReader.readAsDataURL
async_test(function(testCase) {
var blob = new Blob(["TEST"]);
var reader = new FileReader();
reader.onload = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.DONE);
testCase.done();
});
reader.onloadstart = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.LOADING);
});
reader.onprogress = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.LOADING);
});
reader.readAsDataURL(blob);
}, 'FileReader readyState during readAsDataURL');
async_test(function(testCase) {
var blob = new Blob(["TEST"], { type: 'text/plain' });
var reader = new FileReader();
reader.onload = this.step_func(function() {
assert_equals(reader.result, "data:text/plain;base64,VEVTVA==");
testCase.done();
});
reader.readAsDataURL(blob);
}, 'readAsDataURL result for Blob with specified MIME type');
async_test(function(testCase) {
var blob = new Blob(["TEST"]);
var reader = new FileReader();
reader.onload = this.step_func(function() {
assert_equals(reader.result,
"data:application/octet-stream;base64,VEVTVA==");
testCase.done();
});
reader.readAsDataURL(blob);
}, 'readAsDataURL result for Blob with unspecified MIME type');
async_test(function(testCase) {
var blob = new Blob([]);
var reader = new FileReader();
reader.onload = this.step_func(function() {
assert_equals(reader.result,
"data:application/octet-stream;base64,");
testCase.done();
});
reader.readAsDataURL(blob);
}, 'readAsDataURL result for empty Blob');

View File

@ -0,0 +1,36 @@
// META: title=FileAPI Test: filereader_readAsText
async_test(function() {
var blob = new Blob(["TEST"]);
var reader = new FileReader();
reader.onload = this.step_func(function(evt) {
assert_equals(typeof reader.result, "string", "The result is typeof string");
assert_equals(reader.result, "TEST", "The result is TEST");
this.done();
});
reader.onloadstart = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.LOADING, "The readyState");
});
reader.onprogress = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.LOADING);
});
reader.readAsText(blob);
}, "readAsText should correctly read UTF-8.");
async_test(function() {
var blob = new Blob(["TEST"]);
var reader = new FileReader();
var reader_UTF16 = new FileReader();
reader_UTF16.onload = this.step_func(function(evt) {
// "TEST" in UTF-8 is 0x54 0x45 0x53 0x54.
// Decoded as utf-16 (little-endian), we get 0x4554 0x5453.
assert_equals(reader_UTF16.readyState, reader.DONE, "The readyState");
assert_equals(reader_UTF16.result, "\u4554\u5453", "The result is not TEST");
this.done();
});
reader_UTF16.readAsText(blob, "UTF-16");
}, "readAsText should correctly read UTF-16.");

View File

@ -0,0 +1,19 @@
// META: title=FileAPI Test: filereader_readystate
async_test(function() {
var blob = new Blob(["THIS TEST THE READYSTATE WHEN READ BLOB"]);
var reader = new FileReader();
assert_equals(reader.readyState, reader.EMPTY);
reader.onloadstart = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.LOADING);
});
reader.onloadend = this.step_func(function(evt) {
assert_equals(reader.readyState, reader.DONE);
this.done();
});
reader.readAsDataURL(blob);
});

View File

@ -0,0 +1,82 @@
// META: title=FileAPI Test: filereader_result
var blob, blob2;
setup(function() {
blob = new Blob(["This test the result attribute"]);
blob2 = new Blob(["This is a second blob"]);
});
async_test(function() {
var readText = new FileReader();
assert_equals(readText.result, null);
readText.onloadend = this.step_func(function(evt) {
assert_equals(typeof readText.result, "string", "The result type is string");
assert_equals(readText.result, "This test the result attribute", "The result is correct");
this.done();
});
readText.readAsText(blob);
}, "readAsText");
async_test(function() {
var readDataURL = new FileReader();
assert_equals(readDataURL.result, null);
readDataURL.onloadend = this.step_func(function(evt) {
assert_equals(typeof readDataURL.result, "string", "The result type is string");
assert_true(readDataURL.result.indexOf("VGhpcyB0ZXN0IHRoZSByZXN1bHQgYXR0cmlidXRl") != -1, "return the right base64 string");
this.done();
});
readDataURL.readAsDataURL(blob);
}, "readAsDataURL");
async_test(function() {
var readArrayBuffer = new FileReader();
assert_equals(readArrayBuffer.result, null);
readArrayBuffer.onloadend = this.step_func(function(evt) {
assert_true(readArrayBuffer.result instanceof ArrayBuffer, "The result is instanceof ArrayBuffer");
this.done();
});
readArrayBuffer.readAsArrayBuffer(blob);
}, "readAsArrayBuffer");
async_test(function() {
var readBinaryString = new FileReader();
assert_equals(readBinaryString.result, null);
readBinaryString.onloadend = this.step_func(function(evt) {
assert_equals(typeof readBinaryString.result, "string", "The result type is string");
assert_equals(readBinaryString.result, "This test the result attribute", "The result is correct");
this.done();
});
readBinaryString.readAsBinaryString(blob);
}, "readAsBinaryString");
for (let event of ['loadstart', 'progress']) {
for (let method of ['readAsText', 'readAsDataURL', 'readAsArrayBuffer', 'readAsBinaryString']) {
promise_test(async function(t) {
var reader = new FileReader();
assert_equals(reader.result, null, 'result is null before read');
var eventWatcher = new EventWatcher(t, reader,
[event, 'loadend']);
reader[method](blob);
assert_equals(reader.result, null, 'result is null after first read call');
await eventWatcher.wait_for(event);
assert_equals(reader.result, null, 'result is null during event');
await eventWatcher.wait_for('loadend');
assert_not_equals(reader.result, null);
reader[method](blob);
assert_equals(reader.result, null, 'result is null after second read call');
await eventWatcher.wait_for(event);
assert_equals(reader.result, null, 'result is null during second read event');
}, 'result is null during "' + event + '" event for ' + method);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

View File

@ -0,0 +1,70 @@
'use strict'
self.test_blob = (fn, expectations) => {
var expected = expectations.expected,
type = expectations.type,
desc = expectations.desc;
var t = async_test(desc);
t.step(function() {
var blob = fn();
assert_true(blob instanceof Blob);
assert_false(blob instanceof File);
assert_equals(blob.type, type);
assert_equals(blob.size, expected.length);
var fr = new FileReader();
fr.onload = t.step_func_done(function(event) {
assert_equals(this.result, expected);
}, fr);
fr.onerror = t.step_func(function(e) {
assert_unreached("got error event on FileReader");
});
fr.readAsText(blob, "UTF-8");
});
}
self.test_blob_binary = (fn, expectations) => {
var expected = expectations.expected,
type = expectations.type,
desc = expectations.desc;
var t = async_test(desc);
t.step(function() {
var blob = fn();
assert_true(blob instanceof Blob);
assert_false(blob instanceof File);
assert_equals(blob.type, type);
assert_equals(blob.size, expected.length);
var fr = new FileReader();
fr.onload = t.step_func_done(function(event) {
assert_true(this.result instanceof ArrayBuffer,
"Result should be an ArrayBuffer");
assert_array_equals(new Uint8Array(this.result), expected);
}, fr);
fr.onerror = t.step_func(function(e) {
assert_unreached("got error event on FileReader");
});
fr.readAsArrayBuffer(blob);
});
}
// Assert that two TypedArray objects have the same byte values
self.assert_equals_typed_array = (array1, array2) => {
const [view1, view2] = [array1, array2].map((array) => {
assert_true(array.buffer instanceof ArrayBuffer,
'Expect input ArrayBuffers to contain field `buffer`');
return new DataView(array.buffer, array.byteOffset, array.byteLength);
});
assert_equals(view1.byteLength, view2.byteLength,
'Expect both arrays to be of the same byte length');
const byteLength = view1.byteLength;
for (let i = 0; i < byteLength; ++i) {
assert_equals(view1.getUint8(i), view2.getUint8(i),
`Expect byte at buffer position ${i} to be equal`);
}
}

View File

@ -0,0 +1,7 @@
<!DOCTYPE html>
<title>Relevant/current/blob source page used as a test helper</title>
<script>
"use strict";
document.domain = "{{host}}";
</script>

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="utf-8">
<body>

View File

@ -0,0 +1,5 @@
importScripts('/resources/testharness.js');
test(() => {
assert_false('FileReaderSync' in self);
}, '"FileReaderSync" should not be supported in service workers');

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<title>Incumbent page used as a test helper</title>
<iframe src="//{{domains[www1]}}:{{location[port]}}/FileAPI/support/document-domain-setter.sub.html" id="c"></iframe>
<iframe src="//{{domains[www2]}}:{{location[port]}}/FileAPI/support/document-domain-setter.sub.html" id="r"></iframe>
<iframe src="//{{domains[élève]}}:{{location[port]}}/FileAPI/support/document-domain-setter.sub.html" id="bs"></iframe>
<script>
"use strict";
document.domain = "{{host}}";
window.createBlobURL = () => {
const current = document.querySelector("#c").contentWindow;
const relevant = document.querySelector("#r").contentWindow;
const blobSource = document.querySelector("#bs").contentWindow;
const blob = new blobSource.Blob(["Test Blob"]);
return current.URL.createObjectURL.call(relevant, blob);
};
</script>

View File

@ -0,0 +1,282 @@
'use strict';
// See /FileAPI/file/resources/echo-content-escaped.py
function escapeString(string) {
return string.replace(/\\/g, "\\\\").replace(
/[^\x20-\x7E]/g,
(x) => {
let hex = x.charCodeAt(0).toString(16);
if (hex.length < 2) hex = "0" + hex;
return `\\x${hex}`;
},
).replace(/\\x0d\\x0a/g, "\r\n");
}
// Rationale for this particular test character sequence, which is
// used in filenames and also in file contents:
//
// - ABC~ ensures the string starts with something we can read to
// ensure it is from the correct source; ~ is used because even
// some 1-byte otherwise-ASCII-like parts of ISO-2022-JP
// interpret it differently.
// - ‾¥ are inside a single-byte range of ISO-2022-JP and help
// diagnose problems due to filesystem encoding or locale
// - ≈ is inside IBM437 and helps diagnose problems due to filesystem
// encoding or locale
// - ¤ is inside Latin-1 and helps diagnose problems due to
// filesystem encoding or locale; it is also the "simplest" case
// needing substitution in ISO-2022-JP
// - ・ is inside a single-byte range of ISO-2022-JP in some variants
// and helps diagnose problems due to filesystem encoding or locale;
// on the web it is distinct when decoding but unified when encoding
// - ・ is inside a double-byte range of ISO-2022-JP and helps
// diagnose problems due to filesystem encoding or locale
// - • is inside Windows-1252 and helps diagnose problems due to
// filesystem encoding or locale and also ensures these aren't
// accidentally turned into e.g. control codes
// - ∙ is inside IBM437 and helps diagnose problems due to filesystem
// encoding or locale
// - · is inside Latin-1 and helps diagnose problems due to
// filesystem encoding or locale and also ensures HTML named
// character references (e.g. &middot;) are not used
// - ☼ is inside IBM437 shadowing C0 and helps diagnose problems due to
// filesystem encoding or locale and also ensures these aren't
// accidentally turned into e.g. control codes
// - ★ is inside ISO-2022-JP on a non-Kanji page and makes correct
// output easier to spot
// - 星 is inside ISO-2022-JP on a Kanji page and makes correct
// output easier to spot
// - 🌟 is outside the BMP and makes incorrect surrogate pair
// substitution detectable and ensures substitutions work
// correctly immediately after Kanji 2-byte ISO-2022-JP
// - 星 repeated here ensures the correct codec state is used
// after a non-BMP substitution
// - ★ repeated here also makes correct output easier to spot
// - ☼ is inside IBM437 shadowing C0 and helps diagnose problems due to
// filesystem encoding or locale and also ensures these aren't
// accidentally turned into e.g. control codes and also ensures
// substitutions work correctly immediately after non-Kanji
// 2-byte ISO-2022-JP
// - · is inside Latin-1 and helps diagnose problems due to
// filesystem encoding or locale and also ensures HTML named
// character references (e.g. &middot;) are not used
// - ∙ is inside IBM437 and helps diagnose problems due to filesystem
// encoding or locale
// - • is inside Windows-1252 and again helps diagnose problems
// due to filesystem encoding or locale
// - ・ is inside a double-byte range of ISO-2022-JP and helps
// diagnose problems due to filesystem encoding or locale
// - ・ is inside a single-byte range of ISO-2022-JP in some variants
// and helps diagnose problems due to filesystem encoding or locale;
// on the web it is distinct when decoding but unified when encoding
// - ¤ is inside Latin-1 and helps diagnose problems due to
// filesystem encoding or locale; again it is a "simple"
// substitution case
// - ≈ is inside IBM437 and helps diagnose problems due to filesystem
// encoding or locale
// - ¥‾ are inside a single-byte range of ISO-2022-JP and help
// diagnose problems due to filesystem encoding or locale
// - ~XYZ ensures earlier errors don't lead to misencoding of
// simple ASCII
//
// Overall the near-symmetry makes common I18N mistakes like
// off-by-1-after-non-BMP easier to spot. All the characters
// are also allowed in Windows Unicode filenames.
const kTestChars = 'ABC~‾¥≈¤・・•∙·☼★星🌟星★☼·∙•・・¤≈¥‾~XYZ';
// The kTestFallback* strings represent the expected byte sequence from
// encoding kTestChars with the given encoding with "html" replacement
// mode, isomorphic-decoded. That means, characters that can't be
// encoded in that encoding get HTML-escaped, but no further
// `escapeString`-like escapes are needed.
const kTestFallbackUtf8 = (
"ABC~\xE2\x80\xBE\xC2\xA5\xE2\x89\x88\xC2\xA4\xEF\xBD\xA5\xE3\x83\xBB\xE2" +
"\x80\xA2\xE2\x88\x99\xC2\xB7\xE2\x98\xBC\xE2\x98\x85\xE6\x98\x9F\xF0\x9F" +
"\x8C\x9F\xE6\x98\x9F\xE2\x98\x85\xE2\x98\xBC\xC2\xB7\xE2\x88\x99\xE2\x80" +
"\xA2\xE3\x83\xBB\xEF\xBD\xA5\xC2\xA4\xE2\x89\x88\xC2\xA5\xE2\x80\xBE~XYZ"
);
const kTestFallbackIso2022jp = (
("ABC~\x1B(J~\\≈¤\x1B$B!&!&\x1B(B•∙·☼\x1B$B!z@1\x1B(B🌟" +
"\x1B$B@1!z\x1B(B☼·∙•\x1B$B!&!&\x1B(B¤≈\x1B(J\\~\x1B(B~XYZ")
.replace(/[^\0-\x7F]/gu, (x) => `&#${x.codePointAt(0)};`)
);
const kTestFallbackWindows1252 = (
"ABC~‾\xA5≈\xA4・・\x95∙\xB7☼★星🌟星★☼\xB7∙\x95・・\xA4≈\xA5‾~XYZ".replace(
/[^\0-\xFF]/gu,
(x) => `&#${x.codePointAt(0)};`,
)
);
const kTestFallbackXUserDefined = kTestChars.replace(
/[^\0-\x7F]/gu,
(x) => `&#${x.codePointAt(0)};`,
);
// formPostFileUploadTest - verifies multipart upload structure and
// numeric character reference replacement for filenames, field names,
// and field values using form submission.
//
// Uses /FileAPI/file/resources/echo-content-escaped.py to echo the
// upload POST with controls and non-ASCII bytes escaped. This is done
// because navigations whose response body contains [\0\b\v] may get
// treated as a download, which is not what we want. Use the
// `escapeString` function to replicate that kind of escape (note that
// it takes an isomorphic-decoded string, not a byte sequence).
//
// Fields in the parameter object:
//
// - fileNameSource: purely explanatory and gives a clue about which
// character encoding is the source for the non-7-bit-ASCII parts of
// the fileBaseName, or Unicode if no smaller-than-Unicode source
// contains all the characters. Used in the test name.
// - fileBaseName: the not-necessarily-just-7-bit-ASCII file basename
// used for the constructed test file. Used in the test name.
// - formEncoding: the acceptCharset of the form used to submit the
// test file. Used in the test name.
// - expectedEncodedBaseName: the expected formEncoding-encoded
// version of fileBaseName, isomorphic-decoded. That means, characters
// that can't be encoded in that encoding get HTML-escaped, but no
// further `escapeString`-like escapes are needed.
const formPostFileUploadTest = ({
fileNameSource,
fileBaseName,
formEncoding,
expectedEncodedBaseName,
}) => {
promise_test(async testCase => {
if (document.readyState !== 'complete') {
await new Promise(resolve => addEventListener('load', resolve));
}
const formTargetFrame = Object.assign(document.createElement('iframe'), {
name: 'formtargetframe',
});
document.body.append(formTargetFrame);
testCase.add_cleanup(() => {
document.body.removeChild(formTargetFrame);
});
const form = Object.assign(document.createElement('form'), {
acceptCharset: formEncoding,
action: '/FileAPI/file/resources/echo-content-escaped.py',
method: 'POST',
enctype: 'multipart/form-data',
target: formTargetFrame.name,
});
document.body.append(form);
testCase.add_cleanup(() => {
document.body.removeChild(form);
});
// Used to verify that the browser agrees with the test about
// which form charset is used.
form.append(Object.assign(document.createElement('input'), {
type: 'hidden',
name: '_charset_',
}));
// Used to verify that the browser agrees with the test about
// field value replacement and encoding independently of file system
// idiosyncrasies.
form.append(Object.assign(document.createElement('input'), {
type: 'hidden',
name: 'filename',
value: fileBaseName,
}));
// Same, but with name and value reversed to ensure field names
// get the same treatment.
form.append(Object.assign(document.createElement('input'), {
type: 'hidden',
name: fileBaseName,
value: 'filename',
}));
const fileInput = Object.assign(document.createElement('input'), {
type: 'file',
name: 'file',
});
form.append(fileInput);
// Removes c:\fakepath\ or other pseudofolder and returns just the
// final component of filePath; allows both / and \ as segment
// delimiters.
const baseNameOfFilePath = filePath => filePath.split(/[\/\\]/).pop();
await new Promise(resolve => {
const dataTransfer = new DataTransfer;
dataTransfer.items.add(
new File([kTestChars], fileBaseName, {type: 'text/plain'}));
fileInput.files = dataTransfer.files;
// For historical reasons .value will be prefixed with
// c:\fakepath\, but the basename should match the file name
// exposed through the newer .files[0].name API. This check
// verifies that assumption.
assert_equals(
baseNameOfFilePath(fileInput.files[0].name),
baseNameOfFilePath(fileInput.value),
`The basename of the field's value should match its files[0].name`);
form.submit();
formTargetFrame.onload = resolve;
});
const formDataText = formTargetFrame.contentDocument.body.textContent;
const formDataLines = formDataText.split('\n');
if (formDataLines.length && !formDataLines[formDataLines.length - 1]) {
--formDataLines.length;
}
assert_greater_than(
formDataLines.length,
2,
`${fileBaseName}: multipart form data must have at least 3 lines: ${
JSON.stringify(formDataText)
}`);
const boundary = formDataLines[0];
assert_equals(
formDataLines[formDataLines.length - 1],
boundary + '--',
`${fileBaseName}: multipart form data must end with ${boundary}--: ${
JSON.stringify(formDataText)
}`);
const asValue = expectedEncodedBaseName.replace(/\r\n?|\n/g, "\r\n");
const asName = asValue.replace(/[\r\n"]/g, encodeURIComponent);
const asFilename = expectedEncodedBaseName.replace(/[\r\n"]/g, encodeURIComponent);
// The response body from echo-content-escaped.py has controls and non-ASCII
// bytes escaped, so any caller-provided field that might contain such bytes
// must be passed to `escapeString`, after any other expected
// transformations.
const expectedText = [
boundary,
'Content-Disposition: form-data; name="_charset_"',
'',
formEncoding,
boundary,
'Content-Disposition: form-data; name="filename"',
'',
// Unlike for names and filenames, multipart/form-data values don't escape
// \r\n linebreaks, and when they're read from an iframe they become \n.
escapeString(asValue).replace(/\r\n/g, "\n"),
boundary,
`Content-Disposition: form-data; name="${escapeString(asName)}"`,
'',
'filename',
boundary,
`Content-Disposition: form-data; name="file"; ` +
`filename="${escapeString(asFilename)}"`,
'Content-Type: text/plain',
'',
escapeString(kTestFallbackUtf8),
boundary + '--',
].join('\n');
assert_true(
formDataText.startsWith(expectedText),
`Unexpected multipart-shaped form data received:\n${
formDataText
}\nExpected:\n${expectedText}`);
}, `Upload ${fileBaseName} (${fileNameSource}) in ${formEncoding} form`);
};

View File

@ -0,0 +1,99 @@
"use strict";
const kTestChars = "ABC~‾¥≈¤・・•∙·☼★星🌟星★☼·∙•・・¤≈¥‾~XYZ";
// formDataPostFileUploadTest - verifies multipart upload structure and
// numeric character reference replacement for filenames, field names,
// and field values using FormData and fetch().
//
// Uses /fetch/api/resources/echo-content.py to echo the upload
// POST (unlike in send-file-form-helper.js, here we expect all
// multipart/form-data request bodies to be UTF-8, so we don't need to
// escape controls and non-ASCII bytes).
//
// Fields in the parameter object:
//
// - fileNameSource: purely explanatory and gives a clue about which
// character encoding is the source for the non-7-bit-ASCII parts of
// the fileBaseName, or Unicode if no smaller-than-Unicode source
// contains all the characters. Used in the test name.
// - fileBaseName: the not-necessarily-just-7-bit-ASCII file basename
// used for the constructed test file. Used in the test name.
const formDataPostFileUploadTest = ({
fileNameSource,
fileBaseName,
}) => {
promise_test(async (testCase) => {
const formData = new FormData();
let file = new Blob([kTestChars], { type: "text/plain" });
try {
// Switch to File in browsers that allow this
file = new File([file], fileBaseName, { type: file.type });
} catch (ignoredException) {
}
// Used to verify that the browser agrees with the test about
// field value replacement and encoding independently of file system
// idiosyncrasies.
formData.append("filename", fileBaseName);
// Same, but with name and value reversed to ensure field names
// get the same treatment.
formData.append(fileBaseName, "filename");
formData.append("file", file, fileBaseName);
const formDataText = await (await fetch(
`/fetch/api/resources/echo-content.py`,
{
method: "POST",
body: formData,
},
)).text();
const formDataLines = formDataText.split("\r\n");
if (formDataLines.length && !formDataLines[formDataLines.length - 1]) {
--formDataLines.length;
}
assert_greater_than(
formDataLines.length,
2,
`${fileBaseName}: multipart form data must have at least 3 lines: ${
JSON.stringify(formDataText)
}`,
);
const boundary = formDataLines[0];
assert_equals(
formDataLines[formDataLines.length - 1],
boundary + "--",
`${fileBaseName}: multipart form data must end with ${boundary}--: ${
JSON.stringify(formDataText)
}`,
);
const asValue = fileBaseName.replace(/\r\n?|\n/g, "\r\n");
const asName = asValue.replace(/[\r\n"]/g, encodeURIComponent);
const asFilename = fileBaseName.replace(/[\r\n"]/g, encodeURIComponent);
const expectedText = [
boundary,
'Content-Disposition: form-data; name="filename"',
"",
asValue,
boundary,
`Content-Disposition: form-data; name="${asName}"`,
"",
"filename",
boundary,
`Content-Disposition: form-data; name="file"; ` +
`filename="${asFilename}"`,
"Content-Type: text/plain",
"",
kTestChars,
boundary + "--",
].join("\r\n");
assert_true(
formDataText.startsWith(expectedText),
`Unexpected multipart-shaped form data received:\n${formDataText}\nExpected:\n${expectedText}`,
);
}, `Upload ${fileBaseName} (${fileNameSource}) in fetch with FormData`);
};

View File

@ -0,0 +1 @@
Hello

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<script>
const blob = new Blob(["Test Blob"]);
const url = URL.createObjectURL(blob);
window.parent.postMessage({url: url}, '*');
</script>

46
test/fixtures/wpt/FileAPI/unicode.html vendored Normal file
View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Blob/Unicode interaction: normalization and encoding</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
'use strict';
const OMICRON_WITH_OXIA = '\u1F79'; // NFC normalized to U+3CC
const CONTAINS_UNPAIRED_SURROGATES = 'abc\uDC00def\uD800ghi';
const REPLACED = 'abc\uFFFDdef\uFFFDghi';
function readBlobAsPromise(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsText(blob);
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
});
}
promise_test(async t => {
const blob = new Blob([OMICRON_WITH_OXIA]);
const result = await readBlobAsPromise(blob);
assert_equals(result, OMICRON_WITH_OXIA, 'String should not be normalized');
}, 'Test that strings are not NFC normalized by Blob constructor');
promise_test(async t => {
const file = new File([OMICRON_WITH_OXIA], 'name');
const result = await readBlobAsPromise(file);
assert_equals(result, OMICRON_WITH_OXIA, 'String should not be normalized');
}, 'Test that strings are not NFC normalized by File constructor');
promise_test(async t => {
const blob = new Blob([CONTAINS_UNPAIRED_SURROGATES]);
const result = await readBlobAsPromise(blob);
assert_equals(result, REPLACED, 'Unpaired surrogates should be replaced.');
}, 'Test that unpaired surrogates are replaced by Blob constructor');
promise_test(async t => {
const file = new File([CONTAINS_UNPAIRED_SURROGATES], 'name');
const result = await readBlobAsPromise(file);
assert_equals(result, REPLACED, 'Unpaired surrogates should be replaced.');
}, 'Test that unpaired surrogates are replaced by File constructor');
</script>

View File

@ -0,0 +1,62 @@
<!doctype html>
<meta charset="utf-8">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<body>
<script>
async_test(t => {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
const frame = document.createElement('iframe');
frame.setAttribute('style', 'display:none;');
frame.src = 'resources/revoke-helper.html';
document.body.appendChild(frame);
frame.onload = t.step_func(e => {
frame.contentWindow.postMessage({url: url}, '*');
});
self.addEventListener('message', t.step_func(e => {
if (e.source !== frame.contentWindow) return;
assert_equals(e.data, 'revoked');
promise_rejects_js(t, TypeError, fetch(url)).then(t.step_func_done());
}));
}, 'It is possible to revoke same-origin blob URLs from different frames.');
async_test(t => {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
const worker = new Worker('resources/revoke-helper.js');
worker.onmessage = t.step_func(e => {
assert_equals(e.data, 'revoked');
promise_rejects_js(t, TypeError, fetch(url)).then(t.step_func_done());
});
worker.postMessage({url: url});
}, 'It is possible to revoke same-origin blob URLs from a different worker global.');
async_test(t => {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
const frame = document.createElement('iframe');
frame.setAttribute('style', 'display:none;');
frame.src = get_host_info().HTTP_REMOTE_ORIGIN + '/FileAPI/url/resources/revoke-helper.html';
document.body.appendChild(frame);
frame.onload = t.step_func(e => {
frame.contentWindow.postMessage({url: url}, '*');
});
self.addEventListener('message', t.step_func(e => {
if (e.source !== frame.contentWindow) return;
assert_equals(e.data, 'revoked');
fetch(url).then(response => response.text()).then(t.step_func_done(text => {
assert_equals(text, blob_contents);
}), t.unreached_func('Unexpected promise rejection'));
}));
}, 'It is not possible to revoke cross-origin blob URLs.');
</script>

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Blob URL serialization (specifically the origin) in multi-global situations</title>
<link rel="help" href="https://w3c.github.io/FileAPI/#unicodeBlobURL">
<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<!-- this page is the entry global -->
<iframe src="//{{domains[www]}}:{{location[port]}}/FileAPI/support/incumbent.sub.html"></iframe>
<script>
"use strict";
setup({ single_test: true });
document.domain = "{{host}}";
window.onload = () => {
const url = frames[0].createBlobURL();
const desired = "blob:{{location[scheme]}}://www1";
assert_equals(url.substring(0, desired.length), desired,
"Origin should contain www1, from the current settings object");
done();
};
</script>

View File

@ -0,0 +1,7 @@
<!doctype html>
<script>
self.addEventListener('message', e => {
let url = URL.createObjectURL(e.data.blob);
e.source.postMessage({url: url}, '*');
});
</script>

View File

@ -0,0 +1,4 @@
self.addEventListener('message', e => {
let url = URL.createObjectURL(e.data.blob);
self.postMessage({url: url});
});

View File

@ -0,0 +1,71 @@
// This method generates a number of tests verifying fetching of blob URLs,
// allowing the same tests to be used both with fetch() and XMLHttpRequest.
//
// |fetch_method| is only used in test names, and should describe the
// (javascript) method being used by the other two arguments (i.e. 'fetch' or 'XHR').
//
// |fetch_should_succeed| is a callback that is called with the Test and a URL.
// Fetching the URL is expected to succeed. The callback should return a promise
// resolved with whatever contents were fetched.
//
// |fetch_should_fail| similarly is a callback that is called with the Test, a URL
// to fetch, and optionally a method to use to do the fetch. If no method is
// specified the callback should use the 'GET' method. Fetching of these URLs is
// expected to fail, and the callback should return a promise that resolves iff
// fetching did indeed fail.
function fetch_tests(fetch_method, fetch_should_succeed, fetch_should_fail) {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
promise_test(t => {
const url = URL.createObjectURL(blob);
return fetch_should_succeed(t, url).then(text => {
assert_equals(text, blob_contents);
});
}, 'Blob URLs can be used in ' + fetch_method);
promise_test(t => {
const url = URL.createObjectURL(blob);
return fetch_should_succeed(t, url + '#fragment').then(text => {
assert_equals(text, blob_contents);
});
}, fetch_method + ' with a fragment should succeed');
promise_test(t => {
const url = URL.createObjectURL(blob);
URL.revokeObjectURL(url);
return fetch_should_fail(t, url);
}, fetch_method + ' of a revoked URL should fail');
promise_test(t => {
const url = URL.createObjectURL(blob);
URL.revokeObjectURL(url + '#fragment');
return fetch_should_succeed(t, url).then(text => {
assert_equals(text, blob_contents);
});
}, 'Only exact matches should revoke URLs, using ' + fetch_method);
promise_test(t => {
const url = URL.createObjectURL(blob);
return fetch_should_fail(t, url + '?querystring');
}, 'Appending a query string should cause ' + fetch_method + ' to fail');
promise_test(t => {
const url = URL.createObjectURL(blob);
return fetch_should_fail(t, url + '/path');
}, 'Appending a path should cause ' + fetch_method + ' to fail');
for (const method of ['HEAD', 'POST', 'DELETE', 'OPTIONS', 'PUT', 'CUSTOM']) {
const url = URL.createObjectURL(blob);
promise_test(t => {
return fetch_should_fail(t, url, method);
}, fetch_method + ' with method "' + method + '" should fail');
}
}

View File

@ -0,0 +1,7 @@
<!doctype html>
<script>
self.addEventListener('message', e => {
URL.revokeObjectURL(e.data.url);
e.source.postMessage('revoked', '*');
});
</script>

View File

@ -0,0 +1,9 @@
self.addEventListener('message', e => {
URL.revokeObjectURL(e.data.url);
// Registering a new object URL will make absolutely sure that the revocation
// has propagated. Without this at least in chrome it is possible for the
// below postMessage to arrive at its destination before the revocation has
// been fully processed.
URL.createObjectURL(new Blob([]));
self.postMessage('revoked');
});

View File

@ -0,0 +1,32 @@
<!doctype html>
<meta charset="utf-8">
<title>FileAPI Test: Verify behavior of Blob URL in unique origins</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="sandboxed-iframe" sandbox="allow-scripts"></iframe>
<script>
const iframe_scripts = [
'resources/fetch-tests.js',
'url-format.any.js',
'url-in-tags.window.js',
'url-with-xhr.any.js',
'url-with-fetch.any.js',
];
let html = '<!doctype html>\n<meta charset="utf-8">\n<body>\n';
html = html + '<script src="/resources/testharness.js"></' + 'script>\n';
html = html + '<script>setup({"explicit_timeout": true});</' + 'script>\n';
for (const script of iframe_scripts)
html = html + '<script src="' + script + '"></' + 'script>\n';
const frame = document.querySelector('#sandboxed-iframe');
frame.setAttribute('srcdoc', html);
frame.setAttribute('style', 'display:none;');
fetch_tests_from_window(frame.contentWindow);
</script>

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>FileAPI Test: Verify origin of Blob URL</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
async_test(t => {
const frame = document.createElement('iframe');
self.addEventListener('message', t.step_func(e => {
if (e.source != frame.contentWindow) return;
const url = e.data.url;
assert_false(url.includes('天気の良い日'),
'Origin should be ascii rather than unicode');
assert_equals(new URL(url).origin, e.origin,
'Origin of URL should match origin of frame');
assert_true(url.startsWith('blob:{{location[scheme]}}://xn--'));
t.done();
}));
frame.src = '{{location[scheme]}}://{{domains[天気の良い日]}}:{{location[port]}}/FileAPI/support/url-origin.html';
document.body.appendChild(frame);
}, 'Verify serialization of non-ascii origin in Blob URLs');
</script>

View File

@ -0,0 +1,34 @@
async_test(t => {
// This could be detected as ISO-2022-JP, in which case there would be no
// <textarea>, and thus the script inside would be interpreted as actual
// script.
const blob = new Blob(
[
`aaa\u001B$@<textarea>\u001B(B<script>/* xss */<\/script></textarea>bbb`
],
{type: 'text/html;charset=utf-8'});
const url = URL.createObjectURL(blob);
const win = window.open(url);
t.add_cleanup(() => {
win.close();
});
win.onload = t.step_func_done(() => {
assert_equals(win.document.charset, 'UTF-8');
});
}, 'Blob charset should override any auto-detected charset.');
async_test(t => {
const blob = new Blob(
[`<!doctype html>\n<meta charset="ISO-8859-1">`],
{type: 'text/html;charset=utf-8'});
const url = URL.createObjectURL(blob);
const win = window.open(url);
t.add_cleanup(() => {
win.close();
});
win.onload = t.step_func_done(() => {
assert_equals(win.document.charset, 'UTF-8');
});
}, 'Blob charset should override <meta charset>.');

View File

@ -0,0 +1,70 @@
// META: timeout=long
const blob = new Blob(['test']);
const file = new File(['test'], 'name');
test(t => {
const url_count = 5000;
let list = [];
t.add_cleanup(() => {
for (let url of list) {
URL.revokeObjectURL(url);
}
});
for (let i = 0; i < url_count; ++i)
list.push(URL.createObjectURL(blob));
list.sort();
for (let i = 1; i < list.length; ++i)
assert_not_equals(list[i], list[i-1], 'generated Blob URLs should be unique');
}, 'Generated Blob URLs are unique');
test(() => {
const url = URL.createObjectURL(blob);
assert_equals(typeof url, 'string');
assert_true(url.startsWith('blob:'));
}, 'Blob URL starts with "blob:"');
test(() => {
const url = URL.createObjectURL(file);
assert_equals(typeof url, 'string');
assert_true(url.startsWith('blob:'));
}, 'Blob URL starts with "blob:" for Files');
test(() => {
const url = URL.createObjectURL(blob);
assert_equals(new URL(url).origin, location.origin);
if (location.origin !== 'null') {
assert_true(url.includes(location.origin));
assert_true(url.startsWith('blob:' + location.protocol));
}
}, 'Origin of Blob URL matches our origin');
test(() => {
const url = URL.createObjectURL(blob);
const url_record = new URL(url);
assert_equals(url_record.protocol, 'blob:');
assert_equals(url_record.origin, location.origin);
assert_equals(url_record.host, '', 'host should be an empty string');
assert_equals(url_record.port, '', 'port should be an empty string');
const uuid_path_re = /\/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
assert_true(uuid_path_re.test(url_record.pathname), 'Path must end with a valid UUID');
if (location.origin !== 'null') {
const nested_url = new URL(url_record.pathname);
assert_equals(nested_url.origin, location.origin);
assert_equals(nested_url.pathname.search(uuid_path_re), 0, 'Path must be a valid UUID');
assert_true(url.includes(location.origin));
assert_true(url.startsWith('blob:' + location.protocol));
}
}, 'Blob URL parses correctly');
test(() => {
const url = URL.createObjectURL(file);
assert_equals(new URL(url).origin, location.origin);
if (location.origin !== 'null') {
assert_true(url.includes(location.origin));
assert_true(url.startsWith('blob:' + location.protocol));
}
}, 'Origin of Blob URL matches our origin for Files');

View File

@ -0,0 +1,115 @@
// META: timeout=long
async_test(t => {
const run_result = 'test_frame_OK';
const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
'<script>window.test_result = "' + run_result + '";</script>';
const blob = new Blob([blob_contents], {type: 'text/html'});
const url = URL.createObjectURL(blob);
const frame = document.createElement('iframe');
frame.setAttribute('src', url);
frame.setAttribute('style', 'display:none;');
document.body.appendChild(frame);
URL.revokeObjectURL(url);
frame.onload = t.step_func_done(() => {
assert_equals(frame.contentWindow.test_result, run_result);
});
}, 'Fetching a blob URL immediately before revoking it works in an iframe.');
async_test(t => {
const run_result = 'test_frame_OK';
const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
'<script>window.test_result = "' + run_result + '";</script>';
const blob = new Blob([blob_contents], {type: 'text/html'});
const url = URL.createObjectURL(blob);
const frame = document.createElement('iframe');
frame.setAttribute('src', '/common/blank.html');
frame.setAttribute('style', 'display:none;');
document.body.appendChild(frame);
frame.onload = t.step_func(() => {
frame.contentWindow.location = url;
URL.revokeObjectURL(url);
frame.onload = t.step_func_done(() => {
assert_equals(frame.contentWindow.test_result, run_result);
});
});
}, 'Fetching a blob URL immediately before revoking it works in an iframe navigation.');
async_test(t => {
const run_result = 'test_frame_OK';
const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
'<script>window.test_result = "' + run_result + '";</script>';
const blob = new Blob([blob_contents], {type: 'text/html'});
const url = URL.createObjectURL(blob);
const win = window.open(url);
URL.revokeObjectURL(url);
add_completion_callback(() => { win.close(); });
win.onload = t.step_func_done(() => {
assert_equals(win.test_result, run_result);
});
}, 'Opening a blob URL in a new window immediately before revoking it works.');
function receive_message_on_channel(t, channel_name) {
const channel = new BroadcastChannel(channel_name);
return new Promise(resolve => {
channel.addEventListener('message', t.step_func(e => {
resolve(e.data);
}));
});
}
function window_contents_for_channel(channel_name) {
return '<!doctype html>\n' +
'<script>\n' +
'new BroadcastChannel("' + channel_name + '").postMessage("foobar");\n' +
'self.close();\n' +
'</script>';
}
async_test(t => {
const channel_name = 'noopener-window-test';
const blob = new Blob([window_contents_for_channel(channel_name)], {type: 'text/html'});
receive_message_on_channel(t, channel_name).then(t.step_func_done(t => {
assert_equals(t, 'foobar');
}));
const url = URL.createObjectURL(blob);
const win = window.open();
win.opener = null;
win.location = url;
URL.revokeObjectURL(url);
}, 'Opening a blob URL in a noopener about:blank window immediately before revoking it works.');
async_test(t => {
const run_result = 'test_script_OK';
const blob_contents = 'window.script_test_result = "' + run_result + '";';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
const e = document.createElement('script');
e.setAttribute('src', url);
e.onload = t.step_func_done(() => {
assert_equals(window.script_test_result, run_result);
});
document.body.appendChild(e);
URL.revokeObjectURL(url);
}, 'Fetching a blob URL immediately before revoking it works in <script> tags.');
async_test(t => {
const channel_name = 'a-click-test';
const blob = new Blob([window_contents_for_channel(channel_name)], {type: 'text/html'});
receive_message_on_channel(t, channel_name).then(t.step_func_done(t => {
assert_equals(t, 'foobar');
}));
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = url;
anchor.target = '_blank';
document.body.appendChild(anchor);
anchor.click();
URL.revokeObjectURL(url);
}, 'Opening a blob URL in a new window by clicking an <a> tag works immediately before revoking the URL.');

View File

@ -0,0 +1,48 @@
async_test(t => {
const run_result = 'test_script_OK';
const blob_contents = 'window.test_result = "' + run_result + '";';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
const e = document.createElement('script');
e.setAttribute('src', url);
e.onload = t.step_func_done(() => {
assert_equals(window.test_result, run_result);
});
document.body.appendChild(e);
}, 'Blob URLs can be used in <script> tags');
async_test(t => {
const run_result = 'test_frame_OK';
const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
'<script>window.test_result = "' + run_result + '";</script>';
const blob = new Blob([blob_contents], {type: 'text/html'});
const url = URL.createObjectURL(blob);
const frame = document.createElement('iframe');
frame.setAttribute('src', url);
frame.setAttribute('style', 'display:none;');
document.body.appendChild(frame);
frame.onload = t.step_func_done(() => {
assert_equals(frame.contentWindow.test_result, run_result);
});
}, 'Blob URLs can be used in iframes, and are treated same origin');
async_test(t => {
const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
'<style>body { margin: 0; } .block { height: 5000px; }</style>\n' +
'<body>\n' +
'<a id="block1"></a><div class="block"></div>\n' +
'<a id="block2"></a><div class="block"></div>';
const blob = new Blob([blob_contents], {type: 'text/html'});
const url = URL.createObjectURL(blob);
const frame = document.createElement('iframe');
frame.setAttribute('src', url + '#block2');
document.body.appendChild(frame);
frame.contentWindow.onscroll = t.step_func_done(() => {
assert_equals(frame.contentWindow.scrollY, 5000);
});
}, 'Blob URL fragment is implemented.');

View File

@ -0,0 +1,56 @@
<!doctype html>
<meta charset="utf-8">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
promise_test(t => {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const worker = new Worker('resources/create-helper.js');
let url;
return new Promise(resolve => {
worker.onmessage = e => resolve(e.data);
worker.postMessage({blob: blob});
}).then(data => {
url = data.url;
let result = fetch(url);
worker.terminate();
return result;
}).then(response => response.text()).then(text => {
assert_equals(text, blob_contents);
return new Promise(resolve => t.step_timeout(resolve, 100));
}).then(() => promise_rejects_js(t, TypeError, fetch(url)));
}, 'Terminating worker revokes its URLs');
promise_test(t => {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const frame = document.createElement('iframe');
frame.setAttribute('style', 'display:none;');
frame.src = 'resources/create-helper.html';
document.body.appendChild(frame);
let url;
return new Promise(resolve => {
frame.onload = t.step_func(e => {
resolve(e);
});
}).then(e => {
frame.contentWindow.postMessage({blob: blob}, '*');
return new Promise(resolve => {
self.addEventListener('message', t.step_func(e => {
if (e.source === frame.contentWindow) resolve(e);
}));
});
}).then(e => {
url = e.data.url;
let fetch_result = fetch(url);
document.body.removeChild(frame);
return fetch_result;
}).then(response => response.text()).then(text => {
assert_equals(text, blob_contents);
return new Promise(resolve => t.step_timeout(resolve, 100));
}).then(() => promise_rejects_js(t, TypeError, fetch(url)));
}, 'Removing an iframe revokes its URLs');
</script>

View File

@ -0,0 +1,36 @@
function blob_url_reload_test(t, revoke_before_reload) {
const run_result = 'test_frame_OK';
const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
'<script>window.test_result = "' + run_result + '";</script>';
const blob = new Blob([blob_contents], {type: 'text/html'});
const url = URL.createObjectURL(blob);
const frame = document.createElement('iframe');
frame.setAttribute('src', url);
frame.setAttribute('style', 'display:none;');
document.body.appendChild(frame);
frame.onload = t.step_func(() => {
if (revoke_before_reload)
URL.revokeObjectURL(url);
assert_equals(frame.contentWindow.test_result, run_result);
frame.contentWindow.test_result = null;
frame.onload = t.step_func_done(() => {
assert_equals(frame.contentWindow.test_result, run_result);
});
// Slight delay before reloading to ensure revoke actually has had a chance
// to be processed.
t.step_timeout(() => {
frame.contentWindow.location.reload();
}, 250);
});
}
async_test(t => {
blob_url_reload_test(t, false);
}, 'Reloading a blob URL succeeds.');
async_test(t => {
blob_url_reload_test(t, true);
}, 'Reloading a blob URL succeeds even if the URL was revoked.');

View File

@ -0,0 +1,72 @@
// META: script=resources/fetch-tests.js
// META: script=/common/gc.js
function fetch_should_succeed(test, request) {
return fetch(request).then(response => response.text());
}
function fetch_should_fail(test, url, method = 'GET') {
return promise_rejects_js(test, TypeError, fetch(url, {method: method}));
}
fetch_tests('fetch', fetch_should_succeed, fetch_should_fail);
promise_test(t => {
const blob_contents = 'test blob contents';
const blob_type = 'image/png';
const blob = new Blob([blob_contents], {type: blob_type});
const url = URL.createObjectURL(blob);
return fetch(url).then(response => {
assert_equals(response.headers.get('Content-Type'), blob_type);
});
}, 'fetch should return Content-Type from Blob');
promise_test(t => {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
const request = new Request(url);
// Revoke the object URL. Request should take a reference to the blob as
// soon as it receives it in open(), so the request succeeds even though we
// revoke the URL before calling fetch().
URL.revokeObjectURL(url);
return fetch_should_succeed(t, request).then(text => {
assert_equals(text, blob_contents);
});
}, 'Revoke blob URL after creating Request, will fetch');
promise_test(async t => {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
let request = new Request(url);
// Revoke the object URL. Request should take a reference to the blob as
// soon as it receives it in open(), so the request succeeds even though we
// revoke the URL before calling fetch().
URL.revokeObjectURL(url);
request = request.clone();
await garbageCollect();
const text = await fetch_should_succeed(t, request);
assert_equals(text, blob_contents);
}, 'Revoke blob URL after creating Request, then clone Request, will fetch');
promise_test(function(t) {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
const result = fetch_should_succeed(t, url).then(text => {
assert_equals(text, blob_contents);
});
// Revoke the object URL. fetch should have already resolved the blob URL.
URL.revokeObjectURL(url);
return result;
}, 'Revoke blob URL after calling fetch, fetch should succeed');

View File

@ -0,0 +1,68 @@
// META: script=resources/fetch-tests.js
function xhr_should_succeed(test, url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = test.step_func(() => {
assert_equals(xhr.status, 200);
assert_equals(xhr.statusText, 'OK');
resolve(xhr.response);
});
xhr.onerror = () => reject('Got unexpected error event');
xhr.send();
});
}
function xhr_should_fail(test, url, method = 'GET') {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
const result1 = new Promise((resolve, reject) => {
xhr.onload = () => reject('Got unexpected load event');
xhr.onerror = resolve;
});
const result2 = new Promise(resolve => {
xhr.onreadystatechange = test.step_func(() => {
if (xhr.readyState !== xhr.DONE) return;
assert_equals(xhr.status, 0);
resolve();
});
});
xhr.send();
return Promise.all([result1, result2]);
}
fetch_tests('XHR', xhr_should_succeed, xhr_should_fail);
async_test(t => {
const blob_contents = 'test blob contents';
const blob_type = 'image/png';
const blob = new Blob([blob_contents], {type: blob_type});
const url = URL.createObjectURL(blob);
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onloadend = t.step_func_done(() => {
assert_equals(xhr.getResponseHeader('Content-Type'), blob_type);
});
xhr.send();
}, 'XHR should return Content-Type from Blob');
async_test(t => {
const blob_contents = 'test blob contents';
const blob = new Blob([blob_contents]);
const url = URL.createObjectURL(blob);
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
// Revoke the object URL. XHR should take a reference to the blob as soon as
// it receives it in open(), so the request succeeds even though we revoke the
// URL before calling send().
URL.revokeObjectURL(url);
xhr.onload = t.step_func_done(() => {
assert_equals(xhr.response, blob_contents);
});
xhr.onerror = t.unreached_func('Got unexpected error event');
xhr.send();
}, 'Revoke blob URL after open(), will fetch');

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>FileAPI Test: Creating Blob URL with File</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="author" title="JunChen Xia" href="mailto:xjconlyme@gmail.com">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div>
<p>Test steps:</p>
<ol>
<li>Download <a href="/images/blue96x96.png">blue96x96.png</a> to local.</li>
<li>Select the local file (blue96x96.png) to run the test.</li>
</ol>
</div>
<form name="uploadData">
<input type="file" id="fileChooser">
</form>
<div id="log"></div>
<script>
async_test(function(t) {
var fileInput = document.querySelector('#fileChooser');
fileInput.onchange = t.step_func(function(e) {
var blobURL, file = fileInput.files[0];
test(function() {
assert_true(file instanceof File, "FileList contains File");
}, "Check if FileList contains File");
test(function() {
blobURL = window.URL.createObjectURL(file);
assert_equals(typeof blobURL, "string", "Blob URL is type of string");
assert_equals(blobURL.indexOf("blob"), 0, "Blob URL's scheme is blob");
}, "Check if URL.createObjectURL(File) returns a Blob URL");
t.done();
});
});
</script>

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>FileAPI Test: Creating Blob URL with File as image source</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="author" title="JunChen Xia" href="mailto:xjconlyme@gmail.com">
<div>
<p>Test steps:</p>
<ol>
<li>Download <a href="/images/blue96x96.png">blue96x96.png</a> to local.</li>
<li>Select the local file (blue96x96.png) to run the test.</li>
</ol>
<p>Pass/fail criteria:</p>
<p>Test passes if there is a filled blue square.</p>
<p><input type="file" accept="image/*" id="fileChooser"></p>
<p><img id="displayImage"></img></p>
</div>
<script>
var fileInput = document.querySelector("#fileChooser");
var img = document.querySelector("#displayImage");
fileInput.addEventListener("change", function(evt) {
img.src = window.URL.createObjectURL(fileInput.files[0]);
}, false);
</script>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>FileAPI Reference File</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="author" title="JunChen Xia" href="mailto:xjconlyme@gmail.com">
<p>Test passes if there is a filled blue square.</p>
<p>
<img id="fileDisplay" src="/images/blue96x96.png">
</p>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>FileAPI Test: Creating Blob URL via XMLHttpRequest as image source</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<link rel="author" title="JunChen Xia" href="mailto:xjconlyme@gmail.com">
<link rel="match" href="url_xmlhttprequest_img-ref.html">
<p>Test passes if there is a filled blue square.</p>
<p>
<img id="fileDisplay">
</p>
<script src="/common/reftest-wait.js"></script>
<script>
var http = new XMLHttpRequest();
http.open("GET", "/images/blue96x96.png", true);
http.responseType = "blob";
http.onloadend = function() {
var fileDisplay = document.querySelector("#fileDisplay");
fileDisplay.src = window.URL.createObjectURL(http.response);
fileDisplay.onload = takeScreenshot;
};
http.send();
</script>
</html>

11
test/fixtures/wpt/LICENSE.md vendored Normal file
View File

@ -0,0 +1,11 @@
# The 3-Clause BSD License
Copyright © web-platform-tests contributors
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

42
test/fixtures/wpt/README.md vendored Normal file
View File

@ -0,0 +1,42 @@
# Web Platform Test Fixtures
The files in this folder, including this document,
are generated by [`git node wpt`][].
This folder contains a subset of the [Web Platform Tests][] for the
implementation of Web APIs in Node.js.
See [test/wpt](../../wpt/README.md) for information on how these tests are run.
Last update:
- common: https://github.com/web-platform-tests/wpt/tree/dbd648158d/common
- compression: https://github.com/web-platform-tests/wpt/tree/67880a4eb8/compression
- console: https://github.com/web-platform-tests/wpt/tree/e48251b778/console
- dom/abort: https://github.com/web-platform-tests/wpt/tree/0143fe244b/dom/abort
- dom/events: https://github.com/web-platform-tests/wpt/tree/0a811c5161/dom/events
- encoding: https://github.com/web-platform-tests/wpt/tree/1ac8deee08/encoding
- fetch/data-urls/resources: https://github.com/web-platform-tests/wpt/tree/7c79d998ff/fetch/data-urls/resources
- FileAPI: https://github.com/web-platform-tests/wpt/tree/cceaf3628d/FileAPI
- hr-time: https://github.com/web-platform-tests/wpt/tree/34cafd797e/hr-time
- html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob
- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing
- html/webappapis/structured-clone: https://github.com/web-platform-tests/wpt/tree/47d3fb280c/html/webappapis/structured-clone
- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/5873f2d8f1/html/webappapis/timers
- interfaces: https://github.com/web-platform-tests/wpt/tree/e1b27be06b/interfaces
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/94caab7038/performance-timeline
- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing
- resources: https://github.com/web-platform-tests/wpt/tree/1e140d63ec/resources
- streams: https://github.com/web-platform-tests/wpt/tree/bc9dcbbf1a/streams
- url: https://github.com/web-platform-tests/wpt/tree/9504a83e01/url
- urlpattern: https://github.com/web-platform-tests/wpt/tree/f07d05f49c/urlpattern
- user-timing: https://github.com/web-platform-tests/wpt/tree/5ae85bf826/user-timing
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/591c95ce61/WebCryptoAPI
- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions
- webmessaging/broadcastchannel: https://github.com/web-platform-tests/wpt/tree/6495c91853/webmessaging/broadcastchannel
- webstorage: https://github.com/web-platform-tests/wpt/tree/1291340aaa/webstorage
[Web Platform Tests]: https://github.com/web-platform-tests/wpt
[`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/main/docs/git-node.md#git-node-wpt

View File

@ -0,0 +1,3 @@
spec: https://w3c.github.io/webcrypto/
suggested_reviewers:
- twiss

Some files were not shown because too many files have changed in this diff Show More