tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>