| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <div id="container"></div> |
| <script> |
| class MyControl extends HTMLElement { |
| static get formAssociated() { return true; } |
| |
| constructor() { |
| super(); |
| this.internals_ = this.attachInternals(); |
| this.value_ = ''; |
| } |
| |
| get value() { |
| return this.value_; |
| } |
| set value(v) { |
| this.internals_.setFormValue(v); |
| this.value_ = v; |
| } |
| setValues(nameValues) { |
| const formData = new FormData(); |
| for (let p of nameValues) { |
| formData.append(p[0], p[1]); |
| } |
| this.internals_.setFormValue(formData); |
| } |
| } |
| customElements.define('my-control', MyControl); |
| const $ = document.querySelector.bind(document); |
| |
| function submitPromise(t, extractFromIframe) { |
| if (!extractFromIframe) { |
| extractFromIframe = (iframe) => iframe.contentWindow.location.search; |
| } |
| return new Promise((resolve, reject) => { |
| const iframe = $('iframe'); |
| iframe.onload = () => resolve(extractFromIframe(iframe)); |
| iframe.onerror = () => reject(new Error('iframe onerror fired')); |
| $('form').submit(); |
| }); |
| } |
| |
| function testSerializedEntry({name, value, expected, description}) { |
| // urlencoded |
| { |
| const {name: expectedName, value: expectedValue} = expected.urlencoded; |
| promise_test(async t => { |
| $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + |
| '<my-control></my-control>' + |
| '</form>' + |
| '<iframe name="if1"></iframe>'; |
| if (name !== undefined) { |
| $('my-control').setAttribute("name", name); |
| } |
| if (Array.isArray(value)) { |
| $('my-control').setValues(value); |
| } else { |
| $('my-control').value = value; |
| } |
| const query = await submitPromise(t); |
| assert_equals(query, `?${expectedName}=${expectedValue}`); |
| }, `${description} (urlencoded)`); |
| } |
| |
| // formdata |
| { |
| const {name: expectedName, filename: expectedFilename, value: expectedValue} = expected.formdata; |
| promise_test(async t => { |
| $('#container').innerHTML = |
| '<form action="/FileAPI/file/resources/echo-content-escaped.py" method="post" enctype="multipart/form-data" target="if1">' + |
| '<my-control></my-control>' + |
| '</form>' + |
| '<iframe name="if1"></iframe>'; |
| if (name !== undefined) { |
| $('my-control').setAttribute("name", name); |
| } |
| if (Array.isArray(value)) { |
| $('my-control').setValues(value); |
| } else { |
| $('my-control').value = value; |
| } |
| const escaped = await submitPromise(t, iframe => iframe.contentDocument.body.textContent); |
| const formdata = escaped |
| .replace(/\r\n?|\n/g, "\r\n") |
| .replace( |
| /\\x[0-9A-Fa-f]{2}/g, |
| escape => String.fromCodePoint(parseInt(escape.substring(2), 16)) |
| ); |
| const boundary = formdata.split("\r\n")[0]; |
| const expected = [ |
| boundary, |
| ...(() => { |
| if (expectedFilename === undefined) { |
| return [`Content-Disposition: form-data; name="${expectedName}"`]; |
| } else { |
| return [ |
| `Content-Disposition: form-data; name="${expectedName}"; filename="${expectedFilename}"`, |
| "Content-Type: text/plain" |
| ]; |
| } |
| })(), |
| "", |
| expectedValue, |
| boundary + "--", |
| "" |
| ].join("\r\n"); |
| assert_equals(formdata, expected); |
| }, `${description} (formdata)`); |
| } |
| } |
| |
| promise_test(t => { |
| $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + |
| '<input name=name-pd1 value="value-pd1">' + |
| '<my-control></my-control>' + |
| '</form>' + |
| '<iframe name="if1"></iframe>'; |
| return submitPromise(t).then(query => { |
| assert_equals(query, '?name-pd1=value-pd1'); |
| }); |
| }, 'Single value - name is missing'); |
| |
| promise_test(t => { |
| $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + |
| '<input name=name-pd1 value="value-pd1">' + |
| '<my-control name=""></my-control>' + |
| '<input name=name-pd2 value="value-pd2">' + |
| '</form>' + |
| '<iframe name="if1"></iframe>'; |
| $('my-control').value = 'value-ce1'; |
| return submitPromise(t).then(query => { |
| assert_equals(query, '?name-pd1=value-pd1&name-pd2=value-pd2'); |
| }); |
| }, 'Single value - empty name exists'); |
| |
| promise_test(t => { |
| $('#container').innerHTML = '<form action="/common/blank.html" target="if1" accept-charset=utf-8>' + |
| '<input name=name-pd1 value="value-pd1">' + |
| '<my-control name="name-ce1"></my-control>' + |
| '<my-control name="name-usv"></my-control>' + |
| '<my-control name="name-file"></my-control>' + |
| '</form>' + |
| '<iframe name="if1"></iframe>'; |
| const USV_INPUT = 'abc\uDC00\uD800def'; |
| const USV_OUTPUT = 'abc\uFFFD\uFFFDdef'; |
| const FILE_NAME = 'test_file.txt'; |
| $('[name=name-usv]').value = USV_INPUT; |
| $('[name=name-file]').value = new File(['file content'], FILE_NAME); |
| return submitPromise(t).then(query => { |
| assert_equals(query, `?name-pd1=value-pd1&name-usv=${encodeURIComponent(USV_OUTPUT)}&name-file=${FILE_NAME}`); |
| }); |
| }, 'Single value - Non-empty name exists'); |
| |
| promise_test(t => { |
| $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + |
| '<input name=name-pd1 value="value-pd1">' + |
| '<my-control name="name-ce1"></my-control>' + |
| '<my-control name="name-ce2"></my-control>' + |
| '</form>' + |
| '<iframe name="if1"></iframe>'; |
| $('my-control').value = null; |
| return submitPromise(t).then(query => { |
| assert_equals(query, '?name-pd1=value-pd1'); |
| }); |
| }, 'Null value should submit nothing'); |
| |
| promise_test(t => { |
| $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + |
| '<input name=name-pd1 value="value-pd1">' + |
| '<my-control name=name-ce1></my-control>' + |
| '</form>' + |
| '<iframe name="if1"></iframe>'; |
| $('my-control').value = 'value-ce1'; |
| $('my-control').setValues([]); |
| $('my-control').setValues([['sub1', 'subvalue1'], |
| ['sub2', 'subvalue2'], |
| ['sub2', 'subvalue3']]); |
| return submitPromise(t).then(query => { |
| assert_equals(query, '?name-pd1=value-pd1&sub1=subvalue1&sub2=subvalue2&sub2=subvalue3'); |
| }); |
| }, 'Multiple values - name content attribute is ignored'); |
| |
| promise_test(t => { |
| $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + |
| '<input name=name-pd1 value="value-pd1">' + |
| '<my-control name=name-ce1></my-control>' + |
| '</form>' + |
| '<iframe name="if1"></iframe>'; |
| $('my-control').value = 'value-ce1'; |
| $('my-control').setValues([]); |
| return submitPromise(t).then(query => { |
| assert_equals(query, '?name-pd1=value-pd1'); |
| }); |
| }, 'setFormValue with an empty FormData should submit nothing'); |
| |
| testSerializedEntry({ |
| name: 'a\nb', |
| value: 'c', |
| expected: { |
| urlencoded: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| }, |
| formdata: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| } |
| }, |
| description: 'Newline normalization - \\n in name' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a\rb', |
| value: 'c', |
| expected: { |
| urlencoded: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| }, |
| formdata: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| } |
| }, |
| description: 'Newline normalization - \\r in name' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a\r\nb', |
| value: 'c', |
| expected: { |
| urlencoded: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| }, |
| formdata: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| } |
| }, |
| description: 'Newline normalization - \\r\\n in name' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a\n\rb', |
| value: 'c', |
| expected: { |
| urlencoded: { |
| name: 'a%0D%0A%0D%0Ab', |
| value: 'c' |
| }, |
| formdata: { |
| name: 'a%0D%0A%0D%0Ab', |
| value: 'c' |
| } |
| }, |
| description: 'Newline normalization - \\n\\r in name' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a', |
| value: 'b\nc', |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| value: 'b\r\nc' |
| } |
| }, |
| description: 'Newline normalization - \\n in value' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a', |
| value: 'b\rc', |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| value: 'b\r\nc' |
| } |
| }, |
| description: 'Newline normalization - \\r in value' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a', |
| value: 'b\r\nc', |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| value: 'b\r\nc' |
| } |
| }, |
| description: 'Newline normalization - \\r\\n in value' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a', |
| value: 'b\n\rc', |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0A%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| value: 'b\r\n\r\nc' |
| } |
| }, |
| description: 'Newline normalization - \\n\\r in value' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a', |
| value: new File([], "b\nc", {type: "text/plain"}), |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| filename: 'b%0Ac', |
| value: '' |
| } |
| }, |
| description: 'Newline normalization - \\n in filename' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a', |
| value: new File([], "b\rc", {type: "text/plain"}), |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| filename: 'b%0Dc', |
| value: '' |
| } |
| }, |
| description: 'Newline normalization - \\r in filename' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a', |
| value: new File([], "b\r\nc", {type: "text/plain"}), |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| filename: 'b%0D%0Ac', |
| value: '' |
| } |
| }, |
| description: 'Newline normalization - \\r\\n in filename' |
| }); |
| |
| testSerializedEntry({ |
| name: 'a', |
| value: new File([], "b\n\rc", {type: "text/plain"}), |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0A%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| filename: 'b%0A%0Dc', |
| value: '' |
| } |
| }, |
| description: 'Newline normalization - \\n\\r in filename' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a\nb', 'c']], |
| expected: { |
| urlencoded: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| }, |
| formdata: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| } |
| }, |
| description: 'Newline normalization - \\n in FormData name' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a\rb', 'c']], |
| expected: { |
| urlencoded: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| }, |
| formdata: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| } |
| }, |
| description: 'Newline normalization - \\r in FormData name' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a\r\nb', 'c']], |
| expected: { |
| urlencoded: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| }, |
| formdata: { |
| name: 'a%0D%0Ab', |
| value: 'c' |
| } |
| }, |
| description: 'Newline normalization - \\r\\n in FormData name' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a\n\rb', 'c']], |
| expected: { |
| urlencoded: { |
| name: 'a%0D%0A%0D%0Ab', |
| value: 'c' |
| }, |
| formdata: { |
| name: 'a%0D%0A%0D%0Ab', |
| value: 'c' |
| } |
| }, |
| description: 'Newline normalization - \\n\\r in FormData name' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a', 'b\nc']], |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| value: 'b\r\nc' |
| } |
| }, |
| description: 'Newline normalization - \\n in FormData value' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a', 'b\rc']], |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| value: 'b\r\nc' |
| } |
| }, |
| description: 'Newline normalization - \\r in FormData value' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a', 'b\r\nc']], |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| value: 'b\r\nc' |
| } |
| }, |
| description: 'Newline normalization - \\r\\n in FormData value' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a', 'b\n\rc']], |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0A%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| value: 'b\r\n\r\nc' |
| } |
| }, |
| description: 'Newline normalization - \\n\\r in FormData value' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a', new File([], 'b\nc', {type: "text/plain"})]], |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| filename: 'b%0Ac', |
| value: '' |
| } |
| }, |
| description: 'Newline normalization - \\n in FormData filename' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a', new File([], 'b\rc', {type: "text/plain"})]], |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| filename: 'b%0Dc', |
| value: '' |
| } |
| }, |
| description: 'Newline normalization - \\r in FormData filename' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a', new File([], 'b\r\nc', {type: "text/plain"})]], |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| filename: 'b%0D%0Ac', |
| value: '' |
| } |
| }, |
| description: 'Newline normalization - \\r\\n in FormData filename' |
| }); |
| |
| testSerializedEntry({ |
| value: [['a', new File([], 'b\n\rc', {type: "text/plain"})]], |
| expected: { |
| urlencoded: { |
| name: 'a', |
| value: 'b%0D%0A%0D%0Ac' |
| }, |
| formdata: { |
| name: 'a', |
| filename: 'b%0A%0Dc', |
| value: '' |
| } |
| }, |
| description: 'Newline normalization - \\n\\r in FormData filename' |
| }); |
| </script> |