tor-browser

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

inserttext.tentative.html (26157B)


      1 <!doctype html>
      2 <html>
      3 <head>
      4 <meta charset=utf-8>
      5 <meta name="flags" content="may">
      6 <title>Testing normalizing white-space sequence after execCommand("inserttext", false, "foo")</title>
      7 <script src="/resources/testharness.js"></script>
      8 <script src="/resources/testharnessreport.js"></script>
      9 </head>
     10 <body>
     11 <script>
     12 "use strict";
     13 
     14 setup({explicit_done: true});
     15 
     16 function runTests() {
     17  // README:
     18  // These tests based on the behavior of Chrome 83.  This test does NOT define
     19  // nor suggest any standard behavior (actually, some expected results might
     20  // look odd), but this test must help you to understand how other browsers
     21  // use different logic to normalize white-space sequence.
     22 
     23  document.body.innerHTML = "<div contenteditable></div>";
     24  let editor = document.querySelector("div[contenteditable]");
     25  editor.focus();
     26  let selection = document.getSelection();
     27 
     28  function toPlaintext(str) {
     29    return str.replace(/&nbsp;/g, "\u00A0");
     30  }
     31  function escape(str) {
     32    return typeof(str) === "string" ? str.replace(/\u00A0/ig, "&nbsp;") : "";
     33  }
     34 
     35  function generateWhiteSpaces(num, lastIsAlwaysNBSP) {
     36    if (!num) {
     37      return "";
     38    }
     39    let str = "";
     40    for (let i = 0; i < num - 1; i++) {
     41      str += i % 2 ? " " : "\u00A0";
     42    }
     43    str += lastIsAlwaysNBSP || num % 2 ? "\u00A0" : " ";
     44    return escape(str);
     45  }
     46  function getDescriptionForTextNode(textNode) {
     47    return selection.focusNode === textNode ?
     48      `${escape(textNode.data.slice(0, selection.focusOffset))}[]${escape(textNode.data.slice(selection.focusOffset))}` :
     49      escape(textNode);
     50  }
     51 
     52  for (let i = 0; i < 12; i++) {
     53    editor.innerHTML = `a${i === 1 ? " " : generateWhiteSpaces(i, false)}b`;
     54    selection.collapse(editor.firstChild, i + 1);
     55    test(function () {
     56      document.execCommand("inserttext", false, " ");
     57      assert_equals(escape(editor.firstChild.data),
     58                    `a${i === 0 ? " " : escape(generateWhiteSpaces(i + 1, false))}b`,
     59                    "Modified text is wrong");
     60    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}"`);
     61  }
     62 
     63  for (let i = 0; i < 12; i++) {
     64    editor.innerHTML = `a${generateWhiteSpaces(i, true)}`;
     65    selection.collapse(editor.firstChild, i + 1);
     66    test(function () {
     67      document.execCommand("inserttext", false, " ");
     68      assert_equals(escape(editor.firstChild.data),
     69                    `a${escape(generateWhiteSpaces(i + 1, true))}`,
     70                    "Modified text is wrong");
     71    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}"`);
     72  }
     73 
     74  for (let i = 0; i < 12; i++) {
     75    editor.innerHTML = `${generateWhiteSpaces(i, false)}b`;
     76    selection.collapse(editor.firstChild, i);
     77    test(function () {
     78      document.execCommand("inserttext", false, " ");
     79      assert_equals(escape(editor.firstChild.data),
     80      `${i === 0 ? "&nbsp;" : escape(generateWhiteSpaces(i + 1, false))}b`,
     81      "Modified text is wrong");
     82    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}"`);
     83  }
     84 
     85  for (let i = 0; i < 12; i++) {
     86    editor.innerHTML = `a${i === 0 ? " " : generateWhiteSpaces(i + 1, false)}b`;
     87    selection.collapse(editor.firstChild, i + 1);
     88    test(function () {
     89      document.execCommand("inserttext", false, " ");
     90      assert_equals(escape(editor.firstChild.data),
     91                    `a${i === 0 ? "&nbsp; " : escape(generateWhiteSpaces(i + 2, false))}b`,
     92                    "Modified text is wrong");
     93    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}"`);
     94  }
     95 
     96  editor.innerHTML = "a&nbsp;b";
     97  selection.collapse(editor.firstChild, 1);
     98  test(function () {
     99    document.execCommand("inserttext", false, " ");
    100    assert_equals(escape(editor.firstChild.data), "a&nbsp; b", "Modified text is wrong");
    101  }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    102 
    103  for (let i = 1; i <= 3; i++) {
    104    editor.innerHTML = "a&nbsp;&nbsp;b";
    105    selection.collapse(editor.firstChild, i);
    106    test(function () {
    107      document.execCommand("inserttext", false, " ");
    108      assert_equals(escape(editor.firstChild.data),
    109                    `a${escape(generateWhiteSpaces(3, false))}b`,
    110                    "Modified text is wrong");
    111    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    112  }
    113 
    114  for (let i = 1; i <= 6; i++) {
    115    editor.innerHTML = "a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b";
    116    selection.collapse(editor.firstChild, i);
    117    test(function () {
    118      document.execCommand("inserttext", false, " ");
    119      assert_equals(escape(editor.firstChild.data),
    120                    `a${escape(generateWhiteSpaces(6, false))}b`,
    121                    "Modified text is wrong");
    122    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    123  }
    124 
    125  for (let i = 1; i <= 7; i++) {
    126    editor.innerHTML = "a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b";
    127    selection.collapse(editor.firstChild, i);
    128    test(function () {
    129      document.execCommand("inserttext", false, " ");
    130      assert_equals(escape(editor.firstChild.data),
    131                    `a${escape(generateWhiteSpaces(7, false))}b`,
    132                    "Modified text is wrong");
    133    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    134  }
    135 
    136  for (let i = 0; i < 12; i++) {
    137    editor.innerHTML = `a${generateWhiteSpaces(i)}b`;
    138    selection.collapse(editor.firstChild, i + 1);
    139    test(function () {
    140      document.execCommand("inserttext", false, "\u00A0");
    141      assert_equals(escape(editor.firstChild.data),
    142                    `a${i === 0 ? " " : escape(generateWhiteSpaces(i + 1, false))}b`,
    143                    "Modified text is wrong");
    144    }, `execCommand("inserttext", false, "\\u00A0") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    145  }
    146 
    147  for (let i = 0; i < 5; i++) {
    148    editor.innerHTML = `a<span>${i === 0 ? " " : generateWhiteSpaces(i + 1)}</span>b`;
    149    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    150    test(function () {
    151      document.execCommand("inserttext", false, " ");
    152      assert_equals(editor.innerHTML,
    153                    `a<span>${escape(generateWhiteSpaces(i + 2, true))}</span>b`,
    154                    "Modified text is wrong");
    155    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span>b"`);
    156  }
    157 
    158  for (let i = 0; i < 5; i++) {
    159    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span>c`;
    160    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    161    test(function () {
    162      document.execCommand("inserttext", false, " ");
    163      assert_equals(editor.innerHTML,
    164                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span>c`,
    165                    "Modified text is wrong");
    166    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span>c"`);
    167  }
    168 
    169  for (let i = 0; i < 5; i++) {
    170    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span> c`;
    171    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    172    test(function () {
    173      document.execCommand("inserttext", false, " ");
    174      assert_equals(editor.innerHTML,
    175                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span> c`,
    176                    "Modified text is wrong");
    177    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span> c"`);
    178  }
    179 
    180  for (let i = 0; i < 5; i++) {
    181    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span>&nbsp;c`;
    182    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    183    test(function () {
    184      document.execCommand("inserttext", false, " ");
    185      assert_equals(editor.innerHTML,
    186                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span>&nbsp;c`,
    187                    "Modified text is wrong");
    188    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span>&nbsp;c"`);
    189  }
    190 
    191  for (let i = 0; i < 5; i++) {
    192    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span><span>c</span>`;
    193    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    194    test(function () {
    195      document.execCommand("inserttext", false, " ");
    196      assert_equals(editor.innerHTML,
    197                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span><span>c</span>`,
    198                    "Modified text is wrong");
    199    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span><span>c</span>"`);
    200  }
    201 
    202  for (let i = 0; i < 5; i++) {
    203    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span><span> c</span>`;
    204    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    205    test(function () {
    206      document.execCommand("inserttext", false, " ");
    207      assert_equals(editor.innerHTML,
    208                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span><span> c</span>`,
    209                    "Modified text is wrong");
    210    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span><span> c</span>"`);
    211  }
    212 
    213  for (let i = 0; i < 5; i++) {
    214    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span><span>&nbsp;c</span>`;
    215    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    216    test(function () {
    217      document.execCommand("inserttext", false, " ");
    218      assert_equals(editor.innerHTML,
    219                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span><span>&nbsp;c</span>`,
    220                    "Modified text is wrong");
    221    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span><span>&nbsp;c</span>"`);
    222  }
    223 
    224  for (let i = 0; i < 5; i++) {
    225    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span><span><span>c</span></span>`;
    226    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    227    test(function () {
    228      document.execCommand("inserttext", false, " ");
    229      assert_equals(editor.innerHTML,
    230                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span><span><span>c</span></span>`,
    231                    "Modified text is wrong");
    232    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span><span><span>c</span></span>"`);
    233  }
    234 
    235  for (let i = 0; i < 5; i++) {
    236    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span><span><span> c</span></span>`;
    237    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    238    test(function () {
    239      document.execCommand("inserttext", false, " ");
    240      assert_equals(editor.innerHTML,
    241                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span><span><span> c</span></span>`,
    242                    "Modified text is wrong");
    243    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span><span><span> c</span></span>"`);
    244  }
    245 
    246  for (let i = 0; i < 5; i++) {
    247    editor.innerHTML = `a<span>b${generateWhiteSpaces(i, true)}</span><span><span>&nbsp;c</span></span>`;
    248    selection.collapse(editor.querySelector("span").firstChild, i + 1);
    249    test(function () {
    250      document.execCommand("inserttext", false, " ");
    251      assert_equals(editor.innerHTML,
    252                    `a<span>b${escape(generateWhiteSpaces(i + 1, true))}</span><span><span>&nbsp;c</span></span>`,
    253                    "Modified text is wrong");
    254    }, `execCommand("inserttext", false, " ") at "a<span>${getDescriptionForTextNode(editor.querySelector("span").firstChild)}</span><span><span>&nbsp;c</span></span>"`);
    255  }
    256 
    257  for (let i = 0; i < 5; i++) {
    258    editor.innerHTML = `a<span><span>b${generateWhiteSpaces(i, true)}</span></span><span>c</span>`;
    259    selection.collapse(editor.querySelector("span span").firstChild, i + 1);
    260    test(function () {
    261      document.execCommand("inserttext", false, " ");
    262      assert_equals(editor.innerHTML,
    263                    `a<span><span>b${escape(generateWhiteSpaces(i + 1, true))}</span></span><span>c</span>`,
    264                    "Modified text is wrong");
    265    }, `execCommand("inserttext", false, " ") at "a<span><span>${getDescriptionForTextNode(editor.querySelector("span span").firstChild)}</span></span><span>c</span>"`);
    266  }
    267 
    268  for (let i = 0; i < 5; i++) {
    269    editor.innerHTML = `a<span><span>b${generateWhiteSpaces(i, true)}</span></span><span> c</span>`;
    270    selection.collapse(editor.querySelector("span span").firstChild, i + 1);
    271    test(function () {
    272      document.execCommand("inserttext", false, " ");
    273      assert_equals(editor.innerHTML,
    274                    `a<span><span>b${escape(generateWhiteSpaces(i + 1, true))}</span></span><span> c</span>`,
    275                    "Modified text is wrong");
    276    }, `execCommand("inserttext", false, " ") at "a<span><span>${getDescriptionForTextNode(editor.querySelector("span span").firstChild)}</span></span><span> c</span>"`);
    277  }
    278 
    279  for (let i = 0; i < 5; i++) {
    280    editor.innerHTML = `a<span><span>b${generateWhiteSpaces(i, true)}</span></span><span>&nbsp;c</span>`;
    281    selection.collapse(editor.querySelector("span span").firstChild, i + 1);
    282    test(function () {
    283      document.execCommand("inserttext", false, " ");
    284      assert_equals(editor.innerHTML,
    285                    `a<span><span>b${escape(generateWhiteSpaces(i + 1, true))}</span></span><span>&nbsp;c</span>`,
    286                    "Modified text is wrong");
    287    }, `execCommand("inserttext", false, " ") at "a<span><span>${getDescriptionForTextNode(editor.querySelector("span span").firstChild)}</span></span><span>&nbsp;c</span>"`);
    288  }
    289 
    290  for (let i = 2; i < 8; i++) {
    291    editor.innerHTML = "ab";
    292    selection.collapse(editor.firstChild, 1);
    293    test(function () {
    294      document.execCommand("inserttext", false, " ".repeat(i));
    295      assert_equals(escape(editor.firstChild.data),
    296                    `a${i > 0 ? escape(generateWhiteSpaces(i, false)) : " "}b`,
    297                    "Modified text is wrong");
    298    }, `execCommand("inserttext", false, "${" ".repeat(i)}") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    299  }
    300 
    301  for (let i = 2; i < 8; i++) {
    302    editor.innerHTML = "a";
    303    selection.collapse(editor.firstChild, 1);
    304    test(function () {
    305      document.execCommand("inserttext", false, " ".repeat(i));
    306      assert_equals(escape(editor.firstChild.data),
    307                    `a${i > 0 ? escape(generateWhiteSpaces(i, true)) : " "}`,
    308                    "Modified text is wrong");
    309    }, `execCommand("inserttext", false, "${" ".repeat(i)}") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    310  }
    311 
    312  for (let i = 2; i < 8; i++) {
    313    editor.innerHTML = "ab";
    314    selection.collapse(editor.firstChild, 1);
    315    test(function () {
    316      document.execCommand("inserttext", false, "\u00A0".repeat(i));
    317      assert_equals(escape(editor.firstChild.data),
    318                    `a${i > 0 ? escape(generateWhiteSpaces(i, false)) : " "}b`,
    319                    "Modified text is wrong");
    320    }, `execCommand("inserttext", false, "${"\\u00A0".repeat(i)}") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    321  }
    322 
    323  for (let i = 2; i < 8; i++) {
    324    editor.innerHTML = "a";
    325    selection.collapse(editor.firstChild, 1);
    326    test(function () {
    327      document.execCommand("inserttext", false, "\u00A0".repeat(i));
    328      assert_equals(escape(editor.firstChild.data),
    329                    `a${i > 0 ? escape(generateWhiteSpaces(i, true)) : " "}`,
    330                    "Modified text is wrong");
    331    }, `execCommand("inserttext", false, "${"\\u00A0".repeat(i)}") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    332  }
    333 
    334  for (let i = 0; i < 5; i++) {
    335    editor.innerHTML = `a${generateWhiteSpaces(i, true)}<span style=white-space:pre>b</span>`;
    336    selection.collapse(editor.firstChild, i + 1);
    337    test(function () {
    338      document.execCommand("inserttext", false, " ");
    339      assert_equals(editor.innerHTML,
    340                    `a${escape(generateWhiteSpaces(i + 1, true))}<span style=\"white-space:pre\">b</span>`,
    341                    "Modified text is wrong");
    342    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}<span style=white-space:pre>b</span>"`);
    343  }
    344 
    345  for (let i = 0; i < 5; i++) {
    346    editor.innerHTML = `a${generateWhiteSpaces(i, true)}<span style=white-space:pre> </span>`;
    347    selection.collapse(editor.firstChild, i + 1);
    348    test(function () {
    349      document.execCommand("inserttext", false, " ");
    350      assert_equals(editor.innerHTML,
    351                    `a${escape(generateWhiteSpaces(i + 1, true))}<span style=\"white-space:pre\"> </span>`,
    352                    "Modified text is wrong");
    353    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}<span style=white-space:pre> </span>"`);
    354  }
    355 
    356  for (let i = 0; i < 5; i++) {
    357    editor.innerHTML = `a${generateWhiteSpaces(i, true)}<span style=white-space:pre>&nbsp;</span>`;
    358    selection.collapse(editor.firstChild, i + 1);
    359    test(function () {
    360      document.execCommand("inserttext", false, " ");
    361      assert_equals(editor.innerHTML,
    362                    `a${escape(generateWhiteSpaces(i + 1, true))}<span style=\"white-space:pre\">&nbsp;</span>`,
    363                    "Modified text is wrong");
    364    }, `execCommand("inserttext", false, " ") at "${getDescriptionForTextNode(editor.firstChild)}<span style=white-space:pre>&nbsp;</span>"`);
    365  }
    366 
    367  for (let i = 0; i < 5; i++) {
    368    editor.innerHTML = `a${generateWhiteSpaces(i, true)}<span style=white-space:pre>b</span>`;
    369    selection.collapse(editor.firstChild, i + 1);
    370    test(function () {
    371      document.execCommand("inserttext", false, "\u00A0");
    372      assert_equals(editor.innerHTML,
    373                    `a${escape(generateWhiteSpaces(i + 1, true))}<span style=\"white-space:pre\">b</span>`,
    374                    "Modified text is wrong");
    375    }, `execCommand("inserttext", false, "\\u00A0") at "${getDescriptionForTextNode(editor.firstChild)}<span style=white-space:pre>b</span>"`);
    376  }
    377 
    378  for (let i = 0; i < 5; i++) {
    379    editor.innerHTML = `a${generateWhiteSpaces(i, true)}<span style=white-space:pre> </span>`;
    380    selection.collapse(editor.firstChild, i + 1);
    381    test(function () {
    382      document.execCommand("inserttext", false, "\u00A0");
    383      assert_equals(editor.innerHTML,
    384                    `a${escape(generateWhiteSpaces(i + 1, true))}<span style=\"white-space:pre\"> </span>`,
    385                    "Modified text is wrong");
    386    }, `execCommand("inserttext", false, "\\u00A0") at "${getDescriptionForTextNode(editor.firstChild)}<span style=white-space:pre> </span>"`);
    387  }
    388 
    389  for (let i = 0; i < 5; i++) {
    390    editor.innerHTML = `a${generateWhiteSpaces(i, true)}<span style=white-space:pre>&nbsp;</span>`;
    391    selection.collapse(editor.firstChild, i + 1);
    392    test(function () {
    393      document.execCommand("inserttext", false, "\u00A0");
    394      assert_equals(editor.innerHTML,
    395                    `a${escape(generateWhiteSpaces(i + 1, true))}<span style=\"white-space:pre\">&nbsp;</span>`,
    396                    "Modified text is wrong");
    397    }, `execCommand("inserttext", false, "\\u00A0") at "${getDescriptionForTextNode(editor.firstChild)}<span style=white-space:pre>&nbsp;</span>"`);
    398  }
    399 
    400  editor.innerHTML = "a&nbsp;&nbsp;c";
    401  selection.collapse(editor.firstChild, 2);
    402  test(function () {
    403    document.execCommand("inserttext", false, "b");
    404    assert_equals(escape(editor.firstChild.data), "a b c", "Modified text is wrong");
    405  }, `execCommand("inserttext", false, "b") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    406 
    407  editor.innerHTML = "a&nbsp;&nbsp;&nbsp;&nbsp;c";
    408  selection.collapse(editor.firstChild, 1);
    409  test(function () {
    410    document.execCommand("inserttext", false, "b");
    411    assert_equals(escape(editor.firstChild.data), `ab${escape(generateWhiteSpaces(4))}c`, "Modified text is wrong");
    412  }, `execCommand("inserttext", false, "b") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    413 
    414  editor.innerHTML = "a&nbsp;&nbsp;&nbsp;&nbsp;c";
    415  selection.collapse(editor.firstChild, 2);
    416  test(function () {
    417    document.execCommand("inserttext", false, "b");
    418    assert_equals(escape(editor.firstChild.data), `a b${escape(generateWhiteSpaces(3))}c`, "Modified text is wrong");
    419  }, `execCommand("inserttext", false, "b") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    420 
    421  editor.innerHTML = "a&nbsp;&nbsp;&nbsp;&nbsp;c";
    422  selection.collapse(editor.firstChild, 3);
    423  test(function () {
    424    document.execCommand("inserttext", false, "b");
    425    assert_equals(escape(editor.firstChild.data),
    426                  `a${escape(generateWhiteSpaces(2))}b${escape(generateWhiteSpaces(2))}c`,
    427                  "Modified text is wrong");
    428  }, `execCommand("inserttext", false, "b") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    429 
    430  editor.innerHTML = "a&nbsp;&nbsp;&nbsp;&nbsp;c";
    431  selection.collapse(editor.firstChild, 4);
    432  test(function () {
    433    document.execCommand("inserttext", false, "b");
    434    assert_equals(escape(editor.firstChild.data),
    435                  `a${escape(generateWhiteSpaces(3))}b c`,
    436                  "Modified text is wrong");
    437  }, `execCommand("inserttext", false, "b") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    438 
    439  editor.innerHTML = "a&nbsp;&nbsp;&nbsp;&nbsp;c";
    440  selection.collapse(editor.firstChild, 5);
    441  test(function () {
    442    document.execCommand("inserttext", false, "b");
    443    assert_equals(escape(editor.firstChild.data), `a${escape(generateWhiteSpaces(4))}bc`, "Modified text is wrong");
    444  }, `execCommand("inserttext", false, "b") at "${getDescriptionForTextNode(editor.firstChild)}"`);
    445 
    446  // Test white space sequence split to multiple text node.
    447  //  - initialText: Set to data of text nodes.  This must have "|" at least one.
    448  //                 Then, the text will be split at every "|".
    449  //                 Same as above test, only &nbsp; is handled at setting.
    450  //                 "[]" means that caret position.
    451  //  - expectedText: Set to data of all text nodes as an array.
    452  //                  Same as above test, only &nbsp; is handled before comparing.
    453  for (const currentTest of [
    454    { initialText: "a[]|b", expectedText: ["a", "b"] },
    455    { initialText: "a []|b", expectedText: ["a ", "b"] },
    456    { initialText: "a |[]b", expectedText: ["a ", "b"] },
    457    { initialText: "a[]&nbsp;|b", expectedText: ["a&nbsp;", "b"] },
    458    { initialText: "a&nbsp;[]|b", expectedText: ["a&nbsp;", "b"] },
    459    { initialText: "a&nbsp;|[]b", expectedText: ["a&nbsp;", "b"] },
    460    { initialText: "a[]|&nbsp;b", expectedText: ["a", "&nbsp;b"] },
    461    { initialText: "a|[]&nbsp;b", expectedText: ["a", "&nbsp;b"] },
    462    { initialText: "a|&nbsp;[]b", expectedText: ["a", "&nbsp;b"] },
    463    { initialText: "a[] |&nbsp;b", expectedText: ["a ", "&nbsp;b"] },
    464    { initialText: "a []|&nbsp;b", expectedText: ["a ", "&nbsp;b"] },
    465    { initialText: "a |[]&nbsp;b", expectedText: ["a ", "&nbsp;b"] },
    466    { initialText: "a |&nbsp;[]b", expectedText: ["a ", "&nbsp;b"] },
    467    { initialText: "a[]&nbsp;| b", expectedText: ["a&nbsp;", " b"] },
    468    { initialText: "a&nbsp;[]| b", expectedText: ["a&nbsp;", " b"] },
    469    { initialText: "a&nbsp;|[] b", expectedText: ["a&nbsp;", " b"] },
    470    { initialText: "a&nbsp;| []b", expectedText: ["a&nbsp;", " b"] },
    471    { initialText: "a[]&nbsp;|&nbsp;b", expectedText: ["a&nbsp;", "&nbsp;b"] },
    472    { initialText: "a&nbsp;[]|&nbsp;b", expectedText: ["a&nbsp;", "&nbsp;b"] },
    473    { initialText: "a&nbsp;|[]&nbsp;b", expectedText: ["a&nbsp;", "&nbsp;b"] },
    474    { initialText: "a&nbsp;|&nbsp;[]b", expectedText: ["a&nbsp;", "&nbsp;b"] },
    475  ]) {
    476    test(function () {
    477      editor.innerHTML = "";
    478      let caret = { container: null, offset: -1 };
    479      for (let text of toPlaintext(currentTest.initialText).split("|")) {
    480        let caretOffset = text.indexOf("[]");
    481        if (caretOffset >= 0) {
    482          text = text.slice(0, caretOffset) + text.slice(caretOffset + 2);
    483        }
    484        let textNode = document.createTextNode(text);
    485        editor.appendChild(textNode);
    486        if (caretOffset >= 0) {
    487          caret = { container: textNode, offset: caretOffset };
    488        }
    489      }
    490      selection.collapse(caret.container, caret.offset);
    491      document.execCommand("inserttext", false, "");
    492      let child = editor.firstChild;
    493      for (let expectedText of currentTest.expectedText) {
    494        expectedText = toPlaintext(expectedText);
    495        let caretOffset = expectedText.indexOf("[]");
    496        if (caretOffset >= 0) {
    497          expectedText = expectedText.slice(0, caretOffset) + expectedText.slice(caretOffset + 2);
    498        }
    499        if (!child || child.nodeName !== "#text") {
    500          assert_equals("", escape(expectedText), "Expected text node is not there");
    501          if (caretOffset >= 0) {
    502            assert_equals(-1, caretOffset, "Selection should be contained in this node");
    503          }
    504        } else {
    505          assert_equals(escape(child.data), escape(expectedText), "Modified text is wrong");
    506          if (caretOffset >= 0) {
    507            assert_equals(selection.focusNode, child, "Selection focus node is wrong");
    508            assert_equals(selection.focusOffset, caretOffset, "Selection focus offset is wrong");
    509            assert_equals(selection.anchorNode, child, "Selection anchor node is wrong");
    510            assert_equals(selection.anchorOffset, caretOffset, "Selection anchor offset is wrong");
    511          }
    512        }
    513        child = child.nextSibling;
    514      }
    515      if (child && child.nodeName === "#text") {
    516        assert_equals(escape(child.data), "", "Unexpected text node is there");
    517      }
    518    }, `execCommand("inserttext", false, ""): "${currentTest.initialText}"`);
    519  }
    520 
    521  done();
    522 }
    523 
    524 window.addEventListener("load", runTests, {once: true});
    525 </script>
    526 </body>
    527 </html>