Update web-platform-tests to revision be5419e845d39089ba6dc338c1bd0fa279108317

This commit is contained in:
Josh Matthews 2018-01-04 13:44:24 -05:00
parent aa199307c8
commit 2b6f573eb5
3440 changed files with 109438 additions and 41750 deletions

View file

@ -0,0 +1,41 @@
<!doctype html>
<title>Revoking blob URL used with Request/fetch</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
promise_test(function(t) {
const blob = new Blob(["test"]);
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(request).then(response => response.text()).then(text => {
assert_equals(text, 'test');
});
}, "Revoke blob URL after creating Request, will fetch");
promise_test(function(t) {
const blob = new Blob(["test"]);
const url = URL.createObjectURL(blob);
return fetch(url).then(response => response.text()).then(text => {
assert_equals(text, 'test');
});
// Revoke the object URL. fetch should have already resolved the blob URL.
URL.revokeObjectURL(url);
}, "Revoke blob URL after fetch, will fetch");
promise_test(t => {
const blob = new Blob(["test"]);
const url = URL.createObjectURL(blob);
URL.revokeObjectURL(url);
const request = new Request(url);
return promise_rejects(t, new TypeError, fetch(request));
}, "Revoke blob URL before creating Request, network error (after fetch)")
</script>

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(new TypeError(), () => new Blob([], {endings: value}),
'Blob constructor should throw');
}, `Invalid "endings" value: ${JSON.stringify(value)}`));
test(t => {
const test_error = {name: 'test'};
assert_throws(
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

@ -74,6 +74,20 @@ test_blob(function() {
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],
@ -392,26 +406,20 @@ test_blob(function() {
desc: "Array with mixed types"
});
// options argument
test(function() {
new Blob([], { endings: "invalidEnumValue" });
new Blob([], { endings: null });
new Blob([], { endings: undefined });
new Blob([], { endings: 0 });
new Blob([], { get endings() { assert_unreached("Should not call getter"); } });
}, "The 'endings' property should be ignored.");
const accessed = [];
const stringified = [];
test(function() {
assert_throws(test_error, function() {
new Blob([], {
get type() { throw test_error; }
});
new Blob([], {
get type() { accessed.push('type'); },
get endings() { accessed.push('endings'); }
});
assert_throws(test_error, function() {
new Blob([], {
type: { toString: function() { throw test_error; } }
});
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() {
@ -449,19 +457,16 @@ test(function() {
});
});
test_blob(function() {
return new Blob(["\na\r\nb\n\rc\r"], { endings: "transparent" });
}, {
expected: "\na\r\nb\n\rc\r",
type: "",
desc: "Newlines should not change when endings is 'transparent'."
});
test_blob(function() {
return new Blob(["\na\r\nb\n\rc\r"], { endings: "native" });
}, {
expected: "\na\r\nb\n\rc\r",
type: "",
desc: "Newlines should not change when endings is 'native'."
[
123,
123.4,
true,
'abc'
].forEach(arg => {
test(t => {
assert_throws(new TypeError(), () => new Blob([], arg),
'Blob constructor should throw with invalid property bag');
}, `Passing ${JSON.stringify(arg)} for options should throw`);
});
var type_tests = [
@ -471,6 +476,7 @@ var type_tests = [
[[], '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 '],

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(new 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(
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

@ -6,10 +6,20 @@
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
const to_string_obj = { toString: () => 'a string' };
const to_string_throws = { toString: () => { throw new Error('expected'); } };
test(function() {
assert_true("File" in window, "window should have a File property.");
}, "File interface object exists");
test(t => {
assert_throws(new TypeError(), () => new File(),
'Bits argument is required');
assert_throws(new TypeError(), () => new File([]),
'Name argument is required');
}, 'Required arguments');
function test_first_argument(arg1, expectedSize, testName) {
test(function() {
var file = new File(arg1, "dummy");
@ -22,14 +32,48 @@ function test_first_argument(arg1, expectedSize, testName) {
}, 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]"
test_first_argument([document], 21, "HTMLDocument in fileBits"); // "[object HTMLDocument]"
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(new TypeError(), () => new File(arg, 'world.html'),
'Constructor should throw for invalid bits argument');
}, `Invalid bits argument: ${JSON.stringify(arg)}`);
});
test(t => {
assert_throws(new 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() {
@ -41,23 +85,29 @@ function test_second_argument(arg2, expectedFileName, testName) {
test_second_argument("dummy", "dummy", "Using fileName");
test_second_argument("dummy/foo", "dummy:foo", "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");
test_second_argument(document, '[object HTMLDocument]', "Using object fileName");
// testing the third argument
test(function() {
var file = new File(["bits"], "dummy", { type: "text/plain"});
assert_true(file instanceof File);
assert_equals(file.type, "text/plain");
}, "Using type on the File constructor");
test(function() {
var file = new File(["bits"], "dummy", { type: "TEXT/PLAIN"});
assert_true(file instanceof File);
assert_equals(file.type, "text/plain");
}, "Using uppercase characters in type");
test(function() {
var file = new File(["bits"], "dummy", { type: "𝓽𝓮𝔁𝓽/𝔭𝔩𝔞𝔦𝔫"});
assert_true(file instanceof File);
assert_equals(file.type, "");
}, "Using illegal character for type");
[
{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);
@ -68,5 +118,41 @@ test(function() {
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(new 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(new Error(),
() => new File(['bits'], 'name.txt', {type: to_string_throws}),
'Constructor should propagate exceptions');
}, 'Property bag propagates exceptions');
</script>

View file

@ -0,0 +1,71 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Upload files in ISO-2022-JP form (tentative)</title>
<!--
NOTE: This test is tentative because encoding for filename
characters unrepresentable in the form charset is not yet
standardized.
-->
<link rel="help"
href="https://github.com/whatwg/html/issues/3223">
<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,61 @@
<!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-\uF7F0\uF793\uF783\uF7A0.txt'),
});
formPostFileUploadTest({
fileNameSource: 'windows-1252',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.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-★星★.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
formEncoding: 'UTF-8',
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
});
formPostFileUploadTest({
fileNameSource: 'Unicode',
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
formEncoding: 'UTF-8',
expectedEncodedBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
});
</script>

View file

@ -0,0 +1,70 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Upload files in Windows-1252 form (tentative)</title>
<!--
NOTE: This test is tentative because encoding for filename
characters unrepresentable in the form charset is not yet
standardized.
-->
<link rel="help"
href="https://github.com/whatwg/html/issues/3223">
<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-☺😂.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,70 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Upload files in x-user-defined form (tentative)</title>
<!--
NOTE: This test is tentative because encoding for filename
characters unrepresentable in the form charset is not yet
standardized.
-->
<link rel="help"
href="https://github.com/whatwg/html/issues/3223">
<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-𓃠.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

@ -30,8 +30,6 @@ setup(function() {
request.onload = function() {
var idls = request.responseText;
idl_array.add_untested_idls("[PrimaryGlobal] interface Window { };");
idl_array.add_untested_idls("[Exposed=(Window,Worker)] interface ArrayBuffer {};");
idl_array.add_untested_idls("interface URL {};");
idl_array.add_untested_idls("[Exposed=(Window,Worker)] interface EventTarget {};");

View file

@ -0,0 +1,249 @@
'use strict';
// 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';
// NOTE: The expected interpretation of ISO-2022-JP according to
// https://encoding.spec.whatwg.org/#iso-2022-jp-encoder unifies
// single-byte and double-byte katakana.
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)};`);
// NOTE: \uFFFD is used here to replace Windows-1252 bytes to match
// how we will see them in the reflected POST bytes in a frame using
// UTF-8 byte interpretation. The bytes will actually be intact, but
// this code cannot tell and does not really care.
const kTestFallbackWindows1252 =
'ABC~‾\xA5≈\xA4・・\x95∙\xB7☼★星🌟星★☼\xB7∙\x95・・\xA4≈\xA5‾~XYZ'.replace(
/[^\0-\xFF]/gu,
x => `&#${x.codePointAt(0)};`).replace(/[\x80-\xFF]/g, '\uFFFD');
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.
//
// Uses /fetch/api/resources/echo-content.py to echo the upload
// POST with UTF-8 byte interpretation, leading to the "UTF-8 goggles"
// behavior documented below for expectedEncodedBaseName when non-
// UTF-8-compatible byte sequences appear in the formEncoding-encoded
// uploaded data.
//
// 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 with unencodable characters replaced by
// numeric character references and non-7-bit-ASCII bytes seen
// through UTF-8 goggles; subsequences not interpretable as UTF-8
// have each byte represented here by \uFFFD REPLACEMENT CHARACTER.
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: '/fetch/api/resources/echo-content.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
// idiosyncracies.
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(
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 expectedText = [
boundary,
'Content-Disposition: form-data; name="_charset_"',
'',
formEncoding,
boundary,
'Content-Disposition: form-data; name="filename"',
'',
expectedEncodedBaseName,
boundary,
`Content-Disposition: form-data; name="${expectedEncodedBaseName}"`,
'',
'filename',
boundary,
`Content-Disposition: form-data; name="file"; ` +
`filename="${expectedEncodedBaseName}"`,
'Content-Type: text/plain',
'',
kTestChars,
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,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

@ -20,9 +20,8 @@
http.onloadend = function() {
var fileDisplay = document.querySelector("#fileDisplay");
fileDisplay.src = window.URL.createObjectURL(http.response);
takeScreenshot();
fileDisplay.onload = takeScreenshot;
};
http.send();
</script>
</html>