ElementInternals-setFormValue.html (14205B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <div id="container"></div> 6 <script> 7 class MyControl extends HTMLElement { 8 static get formAssociated() { return true; } 9 10 constructor() { 11 super(); 12 this.internals_ = this.attachInternals(); 13 this.value_ = ''; 14 } 15 16 get value() { 17 return this.value_; 18 } 19 set value(v) { 20 this.internals_.setFormValue(v); 21 this.value_ = v; 22 } 23 setValues(nameValues) { 24 const formData = new FormData(); 25 for (let p of nameValues) { 26 formData.append(p[0], p[1]); 27 } 28 this.internals_.setFormValue(formData); 29 } 30 } 31 customElements.define('my-control', MyControl); 32 const $ = document.querySelector.bind(document); 33 34 function submitPromise(t, extractFromIframe) { 35 if (!extractFromIframe) { 36 extractFromIframe = (iframe) => iframe.contentWindow.location.search; 37 } 38 return new Promise((resolve, reject) => { 39 const iframe = $('iframe'); 40 iframe.onload = () => resolve(extractFromIframe(iframe)); 41 iframe.onerror = () => reject(new Error('iframe onerror fired')); 42 $('form').submit(); 43 }); 44 } 45 46 function testSerializedEntry({name, value, expected, description}) { 47 // urlencoded 48 { 49 const {name: expectedName, value: expectedValue} = expected.urlencoded; 50 promise_test(async t => { 51 $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + 52 '<my-control></my-control>' + 53 '</form>' + 54 '<iframe name="if1"></iframe>'; 55 if (name !== undefined) { 56 $('my-control').setAttribute("name", name); 57 } 58 if (Array.isArray(value)) { 59 $('my-control').setValues(value); 60 } else { 61 $('my-control').value = value; 62 } 63 const query = await submitPromise(t); 64 assert_equals(query, `?${expectedName}=${expectedValue}`); 65 }, `${description} (urlencoded)`); 66 } 67 68 // formdata 69 { 70 const {name: expectedName, filename: expectedFilename, value: expectedValue} = expected.formdata; 71 promise_test(async t => { 72 $('#container').innerHTML = 73 '<form action="/FileAPI/file/resources/echo-content-escaped.py" method="post" enctype="multipart/form-data" target="if1">' + 74 '<my-control></my-control>' + 75 '</form>' + 76 '<iframe name="if1"></iframe>'; 77 if (name !== undefined) { 78 $('my-control').setAttribute("name", name); 79 } 80 if (Array.isArray(value)) { 81 $('my-control').setValues(value); 82 } else { 83 $('my-control').value = value; 84 } 85 const escaped = await submitPromise(t, iframe => iframe.contentDocument.body.textContent); 86 const formdata = escaped 87 .replace(/\r\n?|\n/g, "\r\n") 88 .replace( 89 /\\x[0-9A-Fa-f]{2}/g, 90 escape => String.fromCodePoint(parseInt(escape.substring(2), 16)) 91 ); 92 const boundary = formdata.split("\r\n")[0]; 93 const expected = [ 94 boundary, 95 ...(() => { 96 if (expectedFilename === undefined) { 97 return [`Content-Disposition: form-data; name="${expectedName}"`]; 98 } else { 99 return [ 100 `Content-Disposition: form-data; name="${expectedName}"; filename="${expectedFilename}"`, 101 "Content-Type: text/plain" 102 ]; 103 } 104 })(), 105 "", 106 expectedValue, 107 boundary + "--", 108 "" 109 ].join("\r\n"); 110 assert_equals(formdata, expected); 111 }, `${description} (formdata)`); 112 } 113 } 114 115 promise_test(t => { 116 $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + 117 '<input name=name-pd1 value="value-pd1">' + 118 '<my-control></my-control>' + 119 '</form>' + 120 '<iframe name="if1"></iframe>'; 121 return submitPromise(t).then(query => { 122 assert_equals(query, '?name-pd1=value-pd1'); 123 }); 124 }, 'Single value - name is missing'); 125 126 promise_test(t => { 127 $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + 128 '<input name=name-pd1 value="value-pd1">' + 129 '<my-control name=""></my-control>' + 130 '<input name=name-pd2 value="value-pd2">' + 131 '</form>' + 132 '<iframe name="if1"></iframe>'; 133 $('my-control').value = 'value-ce1'; 134 return submitPromise(t).then(query => { 135 assert_equals(query, '?name-pd1=value-pd1&name-pd2=value-pd2'); 136 }); 137 }, 'Single value - empty name exists'); 138 139 promise_test(t => { 140 $('#container').innerHTML = '<form action="/common/blank.html" target="if1" accept-charset=utf-8>' + 141 '<input name=name-pd1 value="value-pd1">' + 142 '<my-control name="name-ce1"></my-control>' + 143 '<my-control name="name-usv"></my-control>' + 144 '<my-control name="name-file"></my-control>' + 145 '</form>' + 146 '<iframe name="if1"></iframe>'; 147 const USV_INPUT = 'abc\uDC00\uD800def'; 148 const USV_OUTPUT = 'abc\uFFFD\uFFFDdef'; 149 const FILE_NAME = 'test_file.txt'; 150 $('[name=name-usv]').value = USV_INPUT; 151 $('[name=name-file]').value = new File(['file content'], FILE_NAME); 152 return submitPromise(t).then(query => { 153 assert_equals(query, `?name-pd1=value-pd1&name-usv=${encodeURIComponent(USV_OUTPUT)}&name-file=${FILE_NAME}`); 154 }); 155 }, 'Single value - Non-empty name exists'); 156 157 promise_test(t => { 158 $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + 159 '<input name=name-pd1 value="value-pd1">' + 160 '<my-control name="name-ce1"></my-control>' + 161 '<my-control name="name-ce2"></my-control>' + 162 '</form>' + 163 '<iframe name="if1"></iframe>'; 164 $('my-control').value = null; 165 return submitPromise(t).then(query => { 166 assert_equals(query, '?name-pd1=value-pd1'); 167 }); 168 }, 'Null value should submit nothing'); 169 170 promise_test(t => { 171 $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + 172 '<input name=name-pd1 value="value-pd1">' + 173 '<my-control name=name-ce1></my-control>' + 174 '</form>' + 175 '<iframe name="if1"></iframe>'; 176 $('my-control').value = 'value-ce1'; 177 $('my-control').setValues([]); 178 $('my-control').setValues([['sub1', 'subvalue1'], 179 ['sub2', 'subvalue2'], 180 ['sub2', 'subvalue3']]); 181 return submitPromise(t).then(query => { 182 assert_equals(query, '?name-pd1=value-pd1&sub1=subvalue1&sub2=subvalue2&sub2=subvalue3'); 183 }); 184 }, 'Multiple values - name content attribute is ignored'); 185 186 promise_test(t => { 187 $('#container').innerHTML = '<form action="/common/blank.html" target="if1">' + 188 '<input name=name-pd1 value="value-pd1">' + 189 '<my-control name=name-ce1></my-control>' + 190 '</form>' + 191 '<iframe name="if1"></iframe>'; 192 $('my-control').value = 'value-ce1'; 193 $('my-control').setValues([]); 194 return submitPromise(t).then(query => { 195 assert_equals(query, '?name-pd1=value-pd1'); 196 }); 197 }, 'setFormValue with an empty FormData should submit nothing'); 198 199 testSerializedEntry({ 200 name: 'a\nb', 201 value: 'c', 202 expected: { 203 urlencoded: { 204 name: 'a%0D%0Ab', 205 value: 'c' 206 }, 207 formdata: { 208 name: 'a%0D%0Ab', 209 value: 'c' 210 } 211 }, 212 description: 'Newline normalization - \\n in name' 213 }); 214 215 testSerializedEntry({ 216 name: 'a\rb', 217 value: 'c', 218 expected: { 219 urlencoded: { 220 name: 'a%0D%0Ab', 221 value: 'c' 222 }, 223 formdata: { 224 name: 'a%0D%0Ab', 225 value: 'c' 226 } 227 }, 228 description: 'Newline normalization - \\r in name' 229 }); 230 231 testSerializedEntry({ 232 name: 'a\r\nb', 233 value: 'c', 234 expected: { 235 urlencoded: { 236 name: 'a%0D%0Ab', 237 value: 'c' 238 }, 239 formdata: { 240 name: 'a%0D%0Ab', 241 value: 'c' 242 } 243 }, 244 description: 'Newline normalization - \\r\\n in name' 245 }); 246 247 testSerializedEntry({ 248 name: 'a\n\rb', 249 value: 'c', 250 expected: { 251 urlencoded: { 252 name: 'a%0D%0A%0D%0Ab', 253 value: 'c' 254 }, 255 formdata: { 256 name: 'a%0D%0A%0D%0Ab', 257 value: 'c' 258 } 259 }, 260 description: 'Newline normalization - \\n\\r in name' 261 }); 262 263 testSerializedEntry({ 264 name: 'a', 265 value: 'b\nc', 266 expected: { 267 urlencoded: { 268 name: 'a', 269 value: 'b%0D%0Ac' 270 }, 271 formdata: { 272 name: 'a', 273 value: 'b\r\nc' 274 } 275 }, 276 description: 'Newline normalization - \\n in value' 277 }); 278 279 testSerializedEntry({ 280 name: 'a', 281 value: 'b\rc', 282 expected: { 283 urlencoded: { 284 name: 'a', 285 value: 'b%0D%0Ac' 286 }, 287 formdata: { 288 name: 'a', 289 value: 'b\r\nc' 290 } 291 }, 292 description: 'Newline normalization - \\r in value' 293 }); 294 295 testSerializedEntry({ 296 name: 'a', 297 value: 'b\r\nc', 298 expected: { 299 urlencoded: { 300 name: 'a', 301 value: 'b%0D%0Ac' 302 }, 303 formdata: { 304 name: 'a', 305 value: 'b\r\nc' 306 } 307 }, 308 description: 'Newline normalization - \\r\\n in value' 309 }); 310 311 testSerializedEntry({ 312 name: 'a', 313 value: 'b\n\rc', 314 expected: { 315 urlencoded: { 316 name: 'a', 317 value: 'b%0D%0A%0D%0Ac' 318 }, 319 formdata: { 320 name: 'a', 321 value: 'b\r\n\r\nc' 322 } 323 }, 324 description: 'Newline normalization - \\n\\r in value' 325 }); 326 327 testSerializedEntry({ 328 name: 'a', 329 value: new File([], "b\nc", {type: "text/plain"}), 330 expected: { 331 urlencoded: { 332 name: 'a', 333 value: 'b%0D%0Ac' 334 }, 335 formdata: { 336 name: 'a', 337 filename: 'b%0Ac', 338 value: '' 339 } 340 }, 341 description: 'Newline normalization - \\n in filename' 342 }); 343 344 testSerializedEntry({ 345 name: 'a', 346 value: new File([], "b\rc", {type: "text/plain"}), 347 expected: { 348 urlencoded: { 349 name: 'a', 350 value: 'b%0D%0Ac' 351 }, 352 formdata: { 353 name: 'a', 354 filename: 'b%0Dc', 355 value: '' 356 } 357 }, 358 description: 'Newline normalization - \\r in filename' 359 }); 360 361 testSerializedEntry({ 362 name: 'a', 363 value: new File([], "b\r\nc", {type: "text/plain"}), 364 expected: { 365 urlencoded: { 366 name: 'a', 367 value: 'b%0D%0Ac' 368 }, 369 formdata: { 370 name: 'a', 371 filename: 'b%0D%0Ac', 372 value: '' 373 } 374 }, 375 description: 'Newline normalization - \\r\\n in filename' 376 }); 377 378 testSerializedEntry({ 379 name: 'a', 380 value: new File([], "b\n\rc", {type: "text/plain"}), 381 expected: { 382 urlencoded: { 383 name: 'a', 384 value: 'b%0D%0A%0D%0Ac' 385 }, 386 formdata: { 387 name: 'a', 388 filename: 'b%0A%0Dc', 389 value: '' 390 } 391 }, 392 description: 'Newline normalization - \\n\\r in filename' 393 }); 394 395 testSerializedEntry({ 396 value: [['a\nb', 'c']], 397 expected: { 398 urlencoded: { 399 name: 'a%0D%0Ab', 400 value: 'c' 401 }, 402 formdata: { 403 name: 'a%0D%0Ab', 404 value: 'c' 405 } 406 }, 407 description: 'Newline normalization - \\n in FormData name' 408 }); 409 410 testSerializedEntry({ 411 value: [['a\rb', 'c']], 412 expected: { 413 urlencoded: { 414 name: 'a%0D%0Ab', 415 value: 'c' 416 }, 417 formdata: { 418 name: 'a%0D%0Ab', 419 value: 'c' 420 } 421 }, 422 description: 'Newline normalization - \\r in FormData name' 423 }); 424 425 testSerializedEntry({ 426 value: [['a\r\nb', 'c']], 427 expected: { 428 urlencoded: { 429 name: 'a%0D%0Ab', 430 value: 'c' 431 }, 432 formdata: { 433 name: 'a%0D%0Ab', 434 value: 'c' 435 } 436 }, 437 description: 'Newline normalization - \\r\\n in FormData name' 438 }); 439 440 testSerializedEntry({ 441 value: [['a\n\rb', 'c']], 442 expected: { 443 urlencoded: { 444 name: 'a%0D%0A%0D%0Ab', 445 value: 'c' 446 }, 447 formdata: { 448 name: 'a%0D%0A%0D%0Ab', 449 value: 'c' 450 } 451 }, 452 description: 'Newline normalization - \\n\\r in FormData name' 453 }); 454 455 testSerializedEntry({ 456 value: [['a', 'b\nc']], 457 expected: { 458 urlencoded: { 459 name: 'a', 460 value: 'b%0D%0Ac' 461 }, 462 formdata: { 463 name: 'a', 464 value: 'b\r\nc' 465 } 466 }, 467 description: 'Newline normalization - \\n in FormData value' 468 }); 469 470 testSerializedEntry({ 471 value: [['a', 'b\rc']], 472 expected: { 473 urlencoded: { 474 name: 'a', 475 value: 'b%0D%0Ac' 476 }, 477 formdata: { 478 name: 'a', 479 value: 'b\r\nc' 480 } 481 }, 482 description: 'Newline normalization - \\r in FormData value' 483 }); 484 485 testSerializedEntry({ 486 value: [['a', 'b\r\nc']], 487 expected: { 488 urlencoded: { 489 name: 'a', 490 value: 'b%0D%0Ac' 491 }, 492 formdata: { 493 name: 'a', 494 value: 'b\r\nc' 495 } 496 }, 497 description: 'Newline normalization - \\r\\n in FormData value' 498 }); 499 500 testSerializedEntry({ 501 value: [['a', 'b\n\rc']], 502 expected: { 503 urlencoded: { 504 name: 'a', 505 value: 'b%0D%0A%0D%0Ac' 506 }, 507 formdata: { 508 name: 'a', 509 value: 'b\r\n\r\nc' 510 } 511 }, 512 description: 'Newline normalization - \\n\\r in FormData value' 513 }); 514 515 testSerializedEntry({ 516 value: [['a', new File([], 'b\nc', {type: "text/plain"})]], 517 expected: { 518 urlencoded: { 519 name: 'a', 520 value: 'b%0D%0Ac' 521 }, 522 formdata: { 523 name: 'a', 524 filename: 'b%0Ac', 525 value: '' 526 } 527 }, 528 description: 'Newline normalization - \\n in FormData filename' 529 }); 530 531 testSerializedEntry({ 532 value: [['a', new File([], 'b\rc', {type: "text/plain"})]], 533 expected: { 534 urlencoded: { 535 name: 'a', 536 value: 'b%0D%0Ac' 537 }, 538 formdata: { 539 name: 'a', 540 filename: 'b%0Dc', 541 value: '' 542 } 543 }, 544 description: 'Newline normalization - \\r in FormData filename' 545 }); 546 547 testSerializedEntry({ 548 value: [['a', new File([], 'b\r\nc', {type: "text/plain"})]], 549 expected: { 550 urlencoded: { 551 name: 'a', 552 value: 'b%0D%0Ac' 553 }, 554 formdata: { 555 name: 'a', 556 filename: 'b%0D%0Ac', 557 value: '' 558 } 559 }, 560 description: 'Newline normalization - \\r\\n in FormData filename' 561 }); 562 563 testSerializedEntry({ 564 value: [['a', new File([], 'b\n\rc', {type: "text/plain"})]], 565 expected: { 566 urlencoded: { 567 name: 'a', 568 value: 'b%0D%0A%0D%0Ac' 569 }, 570 formdata: { 571 name: 'a', 572 filename: 'b%0A%0Dc', 573 value: '' 574 } 575 }, 576 description: 'Newline normalization - \\n\\r in FormData filename' 577 }); 578 579 test(() => { 580 class NotFormAssociatedElement extends HTMLElement {} 581 customElements.define('not-form-associated-element', NotFormAssociatedElement); 582 const element = new NotFormAssociatedElement(); 583 const i = element.attachInternals(); 584 assert_throws_dom('NotSupportedError', () => i.setFormValue("test")); 585 }, "ElementInternals.setFormValue() should throw NotSupportedError if the target element is not a form-associated custom element"); 586 587 </script>