tor-browser

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

editing-around-select-element.tentative.html (18091B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>Test execCommand with selection around select element</title>
      4 <meta name="timeout" content="long">
      5 <meta name="variant" content="?delete">
      6 <meta name="variant" content="?forwardDelete">
      7 <meta name="variant" content="?insertText">
      8 <script src=/resources/testharness.js></script>
      9 <script src=/resources/testharnessreport.js></script>
     10 <script>
     11 "use strict";
     12 
     13 const command = document.location.search.substring(1);
     14 const insertText = command === "insertText" ? "XYZ" : "";
     15 
     16 /**
     17 * Typically, browsers do not allow to move caret or select part of <select>,
     18 * <option> and <optgroup>, but Selection API can do it (but browsers don't
     19 * show the result).  In this case, any elements under `<select>` element
     20 * shouldn't be modified (deleted) for avoiding unexpected data loss for the
     21 * users. However, if inserting text when selection range is entirely in the
     22 * <select>, it's fine to insert text around the <select>.
     23 */
     24 
     25 promise_test(async () => {
     26  await new Promise(resolve => {
     27    addEventListener("load", resolve, {once: true});
     28  });
     29 });
     30 
     31 function addPromiseTest(desc, initFunc, expectedResults) {
     32  promise_test(async () => {
     33    initFunc();
     34    document.execCommand(command, false, insertText);
     35    if (Array.isArray(expectedResults)) {
     36      const uniqueArray = [...new Set(expectedResults)];
     37      if (uniqueArray.length > 1) {
     38        assert_in_array(document.body.innerHTML.replace(/(=""|<br>)/g, ""), expectedResults);
     39      } else {
     40        assert_equals(document.body.innerHTML.replace(/(=""|<br>)/g, ""), expectedResults[0]);
     41      }
     42    } else {
     43      assert_equals(document.body.innerHTML.replace(/(=""|<br>)/g, ""), expectedResults);
     44    }
     45  }, `execCommand(${command}, false, "${insertText}") in ${desc}`);
     46 }
     47 
     48 for (const multiple of ["", " multiple"]) {
     49  addPromiseTest(
     50    `<div contenteditable><p>ab[c</p><select${multiple}><option>d]ef</option></select></div>: shouldn't modify in <option>`,
     51    () => {
     52      document.body.innerHTML =
     53        `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select></div>`;
     54      getSelection().setBaseAndExtent(
     55        document.querySelector("p").firstChild,
     56        2,
     57        document.querySelector("option").firstChild,
     58        1
     59      );
     60    },
     61    [
     62      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select></div>`,
     63      `<div contenteditable><p>ab${insertText}</p><select${multiple}><option>def</option></select></div>`,
     64      // </p> is in the range so that <select> may be moved into the <p>.
     65      `<div contenteditable><p>ab${insertText}<select${multiple}><option>def</option></select></p></div>`,
     66    ]
     67  );
     68 
     69  addPromiseTest(
     70    `<div contenteditable><p>abc</p><select${multiple}><option>d[]ef</option></select></div>: shouldn't modify in <option>`,
     71    () => {
     72      document.body.innerHTML =
     73        `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select></div>`;
     74      getSelection().collapse(
     75        document.querySelector("option").firstChild,
     76        1
     77      );
     78    },
     79    [
     80      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select></div>`,
     81      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
     82      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><option>def</option></select></div>`,
     83      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
     84      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select>${insertText}</div>`,
     85    ]
     86  );
     87 
     88  addPromiseTest(
     89    `<div contenteditable><select${multiple}><option>ab[c</option></select><p>d]ef</p></div>: shouldn't modify in <option>`,
     90    () => {
     91      document.body.innerHTML =
     92        `<div contenteditable><select${multiple}><option>abc</option></select><p>def</p></div>`;
     93      getSelection().setBaseAndExtent(
     94        document.querySelector("option").firstChild,
     95        2,
     96        document.querySelector("p").firstChild,
     97        1
     98      );
     99    },
    100    [
    101      `<div contenteditable><select${multiple}><option>abc</option></select><p>def</p></div>`,
    102      `<div contenteditable><select${multiple}><option>abc</option></select><p>${insertText}ef</p></div>`,
    103      // <p> is in the range so that it's fine to move <select> into the <p>.
    104      `<div contenteditable><select${multiple}><option>abc</option></select>${insertText}ef</div>`,
    105    ]
    106  );
    107 
    108  addPromiseTest(
    109    `<div contenteditable><p>abc</p><select${multiple}><option>{}def</option></select><p>ghi</p></div>: shouldn't modify in <option>`,
    110    () => {
    111      document.body.innerHTML =
    112        `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select><p>ghi</p></div>`;
    113      getSelection().collapse(
    114        document.querySelector("option"),
    115        0
    116      );
    117    },
    118    [
    119      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select><p>ghi</p></div>`,
    120      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
    121      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><option>def</option></select><p>ghi</p></div>`,
    122      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
    123      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select>${insertText}<p>ghi</p></div>`,
    124    ]
    125  );
    126 
    127  addPromiseTest(
    128    `<div contenteditable><p>abc</p><select${multiple}><option>def{}</option></select><p>ghi</p></div>: shouldn't modify in <option>`,
    129    () => {
    130      document.body.innerHTML =
    131        `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select><p>ghi</p></div>`;
    132      getSelection().collapse(
    133        document.querySelector("option"),
    134        1
    135      );
    136    },
    137    [
    138      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select><p>ghi</p></div>`,
    139      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
    140      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><option>def</option></select><p>ghi</p></div>`,
    141      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
    142      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select>${insertText}<p>ghi</p></div>`,
    143    ]
    144  );
    145 
    146  addPromiseTest(
    147    `<div contenteditable><p>abc</p><select${multiple}><option>{def}</option></select><p>ghi</p></div>: shouldn't modify in <option>`,
    148    () => {
    149      document.body.innerHTML =
    150        `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select><p>ghi</p></div>`;
    151      getSelection().selectAllChildren(
    152        document.querySelector("option")
    153      );
    154    },
    155    [
    156      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select><p>ghi</p></div>`,
    157      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
    158      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><option>def</option></select><p>ghi</p></div>`,
    159      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
    160      `<div contenteditable><p>abc</p><select${multiple}><option>def</option></select>${insertText}<p>ghi</p></div>`,
    161    ],
    162  );
    163 
    164  addPromiseTest(
    165    `<div contenteditable><p>abc</p><select${multiple}><option>{def</option><option>ghi}</option></select><p>jkl</p></div>: shouldn't join <option>s`,
    166    () => {
    167      document.body.innerHTML =
    168        `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`;
    169      getSelection().setBaseAndExtent(
    170        document.querySelector("option"),
    171        0,
    172        document.querySelector("option + option"),
    173        1,
    174      );
    175    },
    176    [
    177      `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`,
    178      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
    179      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`,
    180      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
    181      `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select>${insertText}<p>jkl</p></div>`,
    182    ]
    183  );
    184 
    185  addPromiseTest(
    186    `<div contenteditable><p>abc</p><select${multiple}>{<option>def</option>}<option>ghi</option></select><p>jkl</p></div>: shouldn't delete <option>`,
    187    () => {
    188      document.body.innerHTML =
    189        `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`;
    190      getSelection().setBaseAndExtent(
    191        document.querySelector("select"),
    192        0,
    193        document.querySelector("select"),
    194        1,
    195      );
    196    },
    197    [
    198      `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`,
    199      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
    200      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`,
    201      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
    202      `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select>${insertText}<p>jkl</p></div>`,
    203    ]
    204  );
    205 
    206  addPromiseTest(
    207    `<div contenteditable><p>abc</p><select${multiple}><option>def</option>{<option>ghi</option>}</select><p>jkl</p></div>: shouldn't delete <option>`,
    208    () => {
    209      document.body.innerHTML =
    210        `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`;
    211      getSelection().setBaseAndExtent(
    212        document.querySelector("select"),
    213        1,
    214        document.querySelector("select"),
    215        2,
    216      );
    217    },
    218    [
    219      `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`,
    220      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
    221      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`,
    222      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
    223      `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select>${insertText}<p>jkl</p></div>`,
    224    ]
    225  );
    226 
    227  addPromiseTest(
    228    `<div contenteditable><p>abc</p><select${multiple}>{<option>def</option><option>ghi</option>}</select><p>jkl</p></div>: shouldn't delete <option>s nor <select${multiple}>`,
    229    () => {
    230      document.body.innerHTML =
    231        `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`;
    232      getSelection().selectAllChildren(
    233        document.querySelector("select")
    234      );
    235    },
    236    [
    237      `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`,
    238      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
    239      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`,
    240      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
    241      `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select>${insertText}<p>jkl</p></div>`,
    242    ]
    243  );
    244 
    245  addPromiseTest(
    246    `<div contenteditable><p>abc</p><select${multiple}><optgroup>{<option>def</option><option>ghi</option>}</optgroup></select><p>jkl</p></div>: shouldn't delete <option>, <optgroup> nor <select${multiple}>`,
    247    () => {
    248      document.body.innerHTML =
    249        `<div contenteditable><p>abc</p><select${multiple}><optgroup><option>def</option><option>ghi</option></optgroup></select><p>jkl</p></div>`;
    250      getSelection().selectAllChildren(
    251        document.querySelector("optgroup")
    252      );
    253    },
    254    [
    255      `<div contenteditable><p>abc</p><select${multiple}><optgroup><option>def</option><option>ghi</option></optgroup></select><p>jkl</p></div>`,
    256      // If selection is treated as collapsed before <selection>, it's fine to insert text before <select>.
    257      `<div contenteditable><p>abc</p>${insertText}<select${multiple}><optgroup><option>def</option><option>ghi</option></optgroup></select><p>jkl</p></div>`,
    258      // If selection is treated as collapsed after <selection>, it's fine to insert text after <select>.
    259      `<div contenteditable><p>abc</p><select${multiple}><optgroup><option>def</option><option>ghi</option></optgroup></select>${insertText}<p>jkl</p></div>`,
    260    ]
    261  );
    262 
    263  addPromiseTest(
    264    `<div contenteditable><p>abc</p>{<select${multiple}><option>def</option><option>ghi</option></select>}<p>jkl</p></div>: <select${multiple}> element itself should be removable`,
    265    () => {
    266      document.body.innerHTML =
    267        `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`;
    268      getSelection().setBaseAndExtent(
    269        document.querySelector("div"),
    270        1,
    271        document.querySelector("div"),
    272        2,
    273      );
    274    },
    275    [
    276      // XXX I think the first should be the best because <select> was between the <p>.
    277      `<div contenteditable><p>abc</p>${insertText}<p>jkl</p></div>`,
    278      // XXX However, some browsers may want to normalize selection into the previous or next <p>.
    279      `<div contenteditable><p>abc${insertText}</p><p>jkl</p></div>`,
    280      `<div contenteditable><p>abc</p><p>${insertText}jkl</p></div>`,
    281    ]
    282  );
    283 
    284  addPromiseTest(
    285    `<div contenteditable><p>abc</p>{<select${multiple}><optgroup><option>def</option><option>ghi</option></optgroup></select>}<p>jkl</p></div>: <select${multiple}> element itself should be removable`,
    286    () => {
    287      document.body.innerHTML =
    288        `<div contenteditable><p>abc</p><select${multiple}><option>def</option><option>ghi</option></select><p>jkl</p></div>`;
    289      getSelection().setBaseAndExtent(
    290        document.querySelector("div"),
    291        1,
    292        document.querySelector("div"),
    293        2,
    294      );
    295    },
    296    [
    297      // XXX I think the first should be the best because <select> was between the <p>.
    298      `<div contenteditable><p>abc</p>${insertText}<p>jkl</p></div>`,
    299      // XXX However, some browsers may want to normalize selection into the previous or next <p>.
    300      `<div contenteditable><p>abc${insertText}</p><p>jkl</p></div>`,
    301      `<div contenteditable><p>abc</p><p>${insertText}jkl</p></div>`,
    302    ]
    303  );
    304 
    305  addPromiseTest(
    306    `<select${multiple} contenteditable>{<option>abc</option><option>def</option>}</select>: shouldn't delete <option>s`,
    307    () => {
    308      document.body.innerHTML =
    309        `<select${multiple} contenteditable><option>abc</option><option>def</option></select>`;
    310      getSelection().selectAllChildren(
    311        document.querySelector("select")
    312      );
    313    },
    314    `<select${multiple} contenteditable><option>abc</option><option>def</option></select>`,
    315  );
    316 
    317  addPromiseTest(
    318    `<select${multiple}><option contenteditable>{abc}</option><option>def</option></select>: shouldn't modify <option>`,
    319    () => {
    320      document.body.innerHTML =
    321        `<select${multiple}><option contenteditable>abc</option><option>def</option></select>`;
    322      getSelection().selectAllChildren(
    323        document.querySelector("option")
    324      );
    325    },
    326    `<select${multiple}><option contenteditable>abc</option><option>def</option></select>`
    327  );
    328 
    329  addPromiseTest(
    330    `<select${multiple}><optgroup contenteditable>{<option>abc</option><option>def</option>}</optgroup></select>: shouldn't delete <option>s`,
    331    () => {
    332      document.body.innerHTML =
    333        `<select${multiple}><optgroup contenteditable><option>abc</option><option>def</option></optgroup></select>`;
    334      getSelection().selectAllChildren(
    335        document.querySelector("optgroup")
    336      );
    337    },
    338    `<select${multiple}><optgroup contenteditable><option>abc</option><option>def</option></optgroup></select>`
    339  );
    340 
    341  addPromiseTest(
    342    `<select${multiple}><optgroup contenteditable><option>{abc}</option><option>def</option></optgroup></select>: shouldn't delete <option>s nor optgroup`,
    343    () => {
    344      document.body.innerHTML =
    345        `<select${multiple}><optgroup contenteditable><option>abc</option><option>def</option></optgroup></select>`;
    346      getSelection().selectAllChildren(
    347        document.querySelector("option")
    348      );
    349    },
    350    `<select${multiple}><optgroup contenteditable><option>abc</option><option>def</option></optgroup></select>`
    351  );
    352 }
    353 
    354 addPromiseTest(
    355  "<optgroup contenteditable><option>{abc}</option><option>def</option></optgroup>: shouldn't delete <option>s nor optgroup",
    356  () => {
    357    document.body.innerHTML =
    358      "<optgroup contenteditable><option>abc</option><option>def</option></optgroup>";
    359    getSelection().selectAllChildren(
    360      document.querySelector("option")
    361    );
    362  },
    363  // XXX It might be fine to modify <optgroup>/<option> not in <select> nor <datalist> if the browser does not replace its content.
    364  `<optgroup contenteditable><option>abc</option><option>def</option></optgroup>`
    365 );
    366 
    367 addPromiseTest(
    368  "<option contenteditable>{abc}</option>: shouldn't modify <option>",
    369  () => {
    370    document.body.innerHTML =
    371      "<option contenteditable>abc</option>";
    372    getSelection().selectAllChildren(
    373      document.querySelector("option")
    374    );
    375  },
    376  // XXX It might be fine to modify <option> not in <select> nor <datalist> if the browser does not replace its content.
    377  `<option contenteditable>abc</option>`
    378 );
    379 </script>
    380 <body></body>