tor-browser

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

delete-in-child-of-html.tentative.html (17909B)


      1 <!doctype html>
      2 <html>
      3 <head>
      4 <meta chareset="utf-8">
      5 <meta name="timeout" content="long">
      6 <meta name="variant" content="?designMode=off&method=backspace">
      7 <meta name="variant" content="?designMode=off&method=forwarddelete">
      8 <meta name="variant" content="?designMode=on&method=backspace">
      9 <meta name="variant" content="?designMode=on&method=forwarddelete">
     10 <title>Join paragraphs outside the body</title>
     11 <script src="/resources/testharness.js"></script>
     12 <script src="/resources/testharnessreport.js"></script>
     13 <script src="/resources/testdriver.js"></script>
     14 <script src="/resources/testdriver-vendor.js"></script>
     15 <script src="/resources/testdriver-actions.js"></script>
     16 <script src="../include/editor-test-utils.js"></script>
     17 </head>
     18 <body>
     19 <iframe srcdoc=""></iframe>
     20 <script>
     21 "use strict";
     22 
     23 const searchParams = new URLSearchParams(document.location.search);
     24 const testingBackspace = searchParams.get("method") == "backspace";
     25 const commandName = testingBackspace ? "delete" : "forwarddelete";
     26 const testingDesignMode = searchParams.get("designMode") == "on";
     27 
     28 const iframe = document.querySelector("iframe");
     29 const minimumSrcDoc =
     30  "<html>" +
     31    "<head>" +
     32      "<title>iframe</title>" +
     33      "<script src='/resources/testdriver.js'></" + "script>" +
     34      "<script src='/resources/testdriver-vendor.js'></" + "script>" +
     35      "<script src='/resources/testdriver-actions.js'></" + "script>" +
     36    "</head>" +
     37    "<body><br></body>" +
     38  "</html>";
     39 
     40 async function initializeAndWaitForLoad(iframeElement, srcDocValue) {
     41  const waitForLoad =
     42    new Promise(
     43      resolve => iframeElement.addEventListener("load", resolve, {once: true})
     44    );
     45  iframeElement.srcdoc = srcDocValue;
     46  await waitForLoad;
     47  if (testingDesignMode) {
     48    iframeElement.contentDocument.designMode = "on";
     49  } else {
     50    iframeElement.contentDocument.documentElement.setAttribute("contenteditable", "");
     51  }
     52  iframeElement.contentWindow.focus();
     53  iframeElement.contentDocument.execCommand("defaultParagraphSeparator", false, "div");
     54 }
     55 
     56 function removeResourceScriptElements(node) {
     57  node.querySelectorAll("script").forEach(
     58    element => {
     59      if (element.getAttribute("src")?.startsWith("/resources")) {
     60        element.remove()
     61      }
     62    }
     63  );
     64 }
     65 
     66 // DO NOT USE multi-line comment in this file, then, you can comment out
     67 // unnecessary tests when you need to attach the browser with a debugger.
     68 
     69 // For backward compatibility, normal block elements outside <body> should be
     70 // joined by deletion.
     71 promise_test(async () => {
     72  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
     73  const childDoc = iframe.contentDocument;
     74  const utils = new EditorTestUtils(childDoc.documentElement);
     75  const div1 = childDoc.createElement("div");
     76  div1.innerHTML = "abc";
     77  const div2 = childDoc.createElement("div");
     78  div2.innerHTML = "def";
     79  childDoc.documentElement.appendChild(div1);
     80  childDoc.documentElement.appendChild(div2);
     81  // Now: </head><body><br></body><div>abc</div><div>def</div>
     82  childDoc.getSelection().collapse(
     83    testingBackspace ? div2.firstChild : div1.firstChild,
     84    testingBackspace ? 0 : div1.firstChild.length
     85  );
     86  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
     87  removeResourceScriptElements(childDoc);
     88  assert_in_array(
     89    childDoc.documentElement.innerHTML,
     90    [
     91      '<head><title>iframe</title></head><body><br></body><div>abcdef</div>',
     92      '<head><title>iframe</title></head><body><br></body><div>abcdef<br></div>',
     93    ],
     94    "The <div> elements should be merged"
     95  );
     96  assert_equals(
     97    div1.isConnected ^ div2.isConnected,
     98    1,
     99    "One <div> element should be removed, and the other should stay"
    100  );
    101 }, `${commandName} in <div> elements after <body> should join them`);
    102 
    103 // Deleting around end of the <body> should merge the element after the
    104 // <body> into the <body>.
    105 promise_test(async () => {
    106  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    107  const childDoc = iframe.contentDocument;
    108  const utils = new EditorTestUtils(childDoc.documentElement);
    109  childDoc.body.innerHTML = "abc";
    110  const div = childDoc.createElement("div");
    111  div.innerHTML = "def";
    112  childDoc.documentElement.appendChild(div);
    113  // Now: </head><body>abc</body><div>def</div>
    114  childDoc.getSelection().collapse(
    115    testingBackspace ? div.firstChild : childDoc.body.firstChild,
    116    testingBackspace ? 0 : childDoc.body.firstChild.length
    117  );
    118  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    119  removeResourceScriptElements(childDoc);
    120  assert_in_array(
    121    childDoc.documentElement.innerHTML,
    122    [
    123      '<head><title>iframe</title></head><body>abcdef</body>',
    124      '<head><title>iframe</title></head><body>abcdef<br></body>',
    125    ],
    126    "The text should be merged"
    127  );
    128  assert_false(
    129    div.isConnected,
    130    "The <div> following <body> should be removed"
    131  );
    132 }, `${commandName} should merge <div> after <body> into the <body>`);
    133 
    134 promise_test(async () => {
    135  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    136  const childDoc = iframe.contentDocument;
    137  const utils = new EditorTestUtils(childDoc.documentElement);
    138  const div1 = childDoc.createElement("div");
    139  div1.innerHTML = "abc";
    140  const div2 = childDoc.createElement("div");
    141  div2.innerHTML = "def";
    142  childDoc.body.innerHTML = "";
    143  childDoc.body.appendChild(div1);
    144  childDoc.documentElement.appendChild(div2);
    145  // Now: </head><body><div>abc</div></body><div>def</div>
    146  childDoc.getSelection().collapse(
    147    testingBackspace ? div2.firstChild : div1.firstChild,
    148    testingBackspace ? 0 : div1.firstChild.length
    149  );
    150  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    151  removeResourceScriptElements(childDoc);
    152  assert_in_array(
    153    childDoc.documentElement.innerHTML,
    154    [
    155      '<head><title>iframe</title></head><body><div>abcdef</div></body>',
    156      '<head><title>iframe</title></head><body><div>abcdef<br></div></body>',
    157    ],
    158    "The <div> elements should be merged"
    159  );
    160  assert_true(
    161    !div2.isConnected || (div2.isConnected && div2.parentNode == childDoc.body),
    162    "The <div> following <body> should be removed or moved into the <body>"
    163  );
    164 }, `${commandName} should merge <div> after <body> into the <div> in the <body>`);
    165 
    166 promise_test(async () => {
    167  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    168  const childDoc = iframe.contentDocument;
    169  const utils = new EditorTestUtils(childDoc.documentElement);
    170  const div = childDoc.createElement("div");
    171  div.innerHTML = "abc";
    172  childDoc.documentElement.appendChild(div);
    173  // Now: </head><body><br></body><div>abc</div>
    174  childDoc.getSelection().collapse(
    175    testingBackspace ? div.firstChild : childDoc.body,
    176    0
    177  );
    178  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    179  removeResourceScriptElements(childDoc);
    180  assert_in_array(
    181    childDoc.documentElement.innerHTML,
    182    [
    183      '<head><title>iframe</title></head><body>abc</body>',
    184      '<head><title>iframe</title></head><body>abc<br></body>',
    185    ],
    186    "The <div> element should be merged into the <body>"
    187  );
    188  assert_false(
    189    div.isConnected,
    190    "The <div> element should be removed"
    191  );
    192 }, `${commandName} should merge <div> after <body> into the empty <body>`);
    193 
    194 // Deleting around start of the <body> should merge the element before the
    195 // <body> into the <body>.
    196 promise_test(async () => {
    197  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    198  const childDoc = iframe.contentDocument;
    199  const utils = new EditorTestUtils(childDoc.documentElement);
    200  const div = childDoc.createElement("div");
    201  div.innerHTML = "abc";
    202  childDoc.body.innerHTML = "def";
    203  childDoc.documentElement.insertBefore(div, childDoc.body);
    204  // Now: </head><div>abc</div><body>def</body>
    205  childDoc.getSelection().collapse(
    206    testingBackspace ? childDoc.body.firstChild : div.firstChild,
    207    testingBackspace ? 0 : div.firstChild.length
    208  );
    209  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    210  removeResourceScriptElements(childDoc);
    211  assert_in_array(
    212    childDoc.documentElement.innerHTML,
    213    [
    214      '<head><title>iframe</title></head><body>abcdef</body>',
    215      '<head><title>iframe</title></head><body>abcdef<br></body>',
    216    ],
    217    "The text should be merged"
    218  );
    219  assert_false(
    220    div.isConnected,
    221    "The <div> following <body> should be removed"
    222  );
    223 }, `${commandName} should merge <div> before <body> into the <body>`);
    224 
    225 promise_test(async () => {
    226  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    227  const childDoc = iframe.contentDocument;
    228  const utils = new EditorTestUtils(childDoc.documentElement);
    229  const div1 = childDoc.createElement("div");
    230  div1.innerHTML = "abc";
    231  const div2 = childDoc.createElement("div");
    232  div2.innerHTML = "def";
    233  childDoc.documentElement.insertBefore(div1, childDoc.body);
    234  childDoc.body.innerHTML = "";
    235  childDoc.body.appendChild(div2);
    236  // Now: </head><div>abc</div><body><div>def</div></body>
    237  childDoc.getSelection().collapse(
    238    testingBackspace ? div2.firstChild : div1.firstChild,
    239    testingBackspace ? 0 : div1.firstChild.length
    240  );
    241  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    242  removeResourceScriptElements(childDoc);
    243  assert_in_array(
    244    childDoc.documentElement.innerHTML,
    245    [
    246      '<head><title>iframe</title></head><body><div>abcdef</div></body>',
    247      '<head><title>iframe</title></head><body><div>abcdef<br></div></body>',
    248    ],
    249    "The <div> elements should be merged"
    250  );
    251  assert_true(
    252    !div2.isConnected || (div2.isConnected && div2.parentNode == childDoc.body),
    253    "The <div> following <body> should be removed or moved into the <body>"
    254  );
    255 }, `${commandName} should merge <div> before <body> into the <div> in the <body>`);
    256 
    257 promise_test(async () => {
    258  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    259  const childDoc = iframe.contentDocument;
    260  const utils = new EditorTestUtils(childDoc.documentElement);
    261  const div = childDoc.createElement("div");
    262  div.innerHTML = "abc";
    263  childDoc.documentElement.insertBefore(div, childDoc.body);
    264  // Now: </head><div>abc</div><body><br></body>
    265  childDoc.getSelection().collapse(
    266    testingBackspace ? childDoc.body : div.firstChild,
    267    testingBackspace ? 0: div.firstChild.length
    268  );
    269  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    270  removeResourceScriptElements(childDoc);
    271  assert_in_array(
    272    childDoc.documentElement.innerHTML,
    273    [
    274      '<head><title>iframe</title></head><body>abc</body>',
    275      '<head><title>iframe</title></head><body>abc<br></body>',
    276    ],
    277    "The <div> element should be merged into the <body>"
    278  );
    279  assert_false(
    280    div.isConnected,
    281    "The <div> element should be removed"
    282  );
    283 }, `${commandName} should merge <div> before <body> into the empty <body>`);
    284 
    285 // Deleting around end of the <head> should not delete the <head> element.
    286 if (testingBackspace) {
    287  promise_test(async () => {
    288    await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    289    const childDoc = iframe.contentDocument;
    290    const utils = new EditorTestUtils(childDoc.documentElement);
    291    const div = childDoc.createElement("div");
    292    div.innerHTML = "abc";
    293    childDoc.body.innerHTML = "def";
    294    childDoc.documentElement.insertBefore(div, childDoc.body);
    295    // Now: </head><div>abc</div><body>def</body>
    296    childDoc.getSelection().collapse(div.firstChild, 0);
    297    await utils.sendBackspaceKey();
    298    removeResourceScriptElements(childDoc);
    299    assert_equals(
    300      childDoc.documentElement.innerHTML,
    301      '<head><title>iframe</title></head><div>abc</div><body>def</body>',
    302      "The <div> element should be merged into the <body>"
    303    );
    304    assert_true(
    305      div.isConnected,
    306      "The <div> element should not be removed"
    307    );
    308  }, `delete from <div> following invisible <head> element shouldn't delete the <head> element`);
    309 }
    310 
    311 // Joining elements around <head> element should not delete the <head> element.
    312 promise_test(async () => {
    313  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    314  const childDoc = iframe.contentDocument;
    315  const utils = new EditorTestUtils(childDoc.documentElement);
    316  const div1 = childDoc.createElement("div");
    317  div1.innerHTML = "abc";
    318  const div2 = childDoc.createElement("div");
    319  div2.innerHTML = "def";
    320  childDoc.documentElement.insertBefore(div1, childDoc.head);
    321  childDoc.documentElement.insertBefore(div2, childDoc.body);
    322  // Now: <div>abc</div><head>...</head><div>def</div><body><br></body>
    323  childDoc.getSelection().collapse(
    324    testingBackspace ? div2.firstChild : div1.firstChild,
    325    testingBackspace ? 0 : div1.firstChild.length
    326  );
    327  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    328  removeResourceScriptElements(childDoc);
    329  assert_in_array(
    330    childDoc.documentElement.innerHTML,
    331    [
    332      '<div>abcdef</div><head><title>iframe</title></head><body><br></body>',
    333      '<div>abcdef<br></div><head><title>iframe</title></head><body><br></body>',
    334      '<head><title>iframe</title></head><div>abcdef</div><body><br></body>',
    335      '<head><title>iframe</title></head><div>abcdef<br></div><body><br></body>',
    336    ],
    337    "The <div> element should be merged into the left <div> without deleting the <head>"
    338  );
    339  assert_true(
    340    div1.isConnected ^ div2.isConnected,
    341    "One <div> element should be removed, but the other should stay"
    342  );
    343 }, `${commandName} from <div> around invisible <head> element should not delete the <head>`);
    344 
    345 
    346 // Same as <body> element boundary, allow joining across <head> elements if
    347 // and only if both elements are normal elements.
    348 promise_test(async () => {
    349  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    350  const childDoc = iframe.contentDocument;
    351  childDoc.head.setAttribute("style", "display:block");
    352  const utils = new EditorTestUtils(childDoc.documentElement);
    353  const div1 = childDoc.createElement("div");
    354  div1.innerHTML = "abc";
    355  const div2 = childDoc.createElement("div");
    356  div2.innerHTML = "def";
    357  childDoc.head.appendChild(div1);
    358  childDoc.documentElement.insertBefore(div2, childDoc.body);
    359  // Now: <div>abc</div></head><div>def</div><body><br></body>
    360  childDoc.getSelection().collapse(
    361    testingBackspace ? div2.firstChild : div1.firstChild,
    362    testingBackspace ? 0 : div1.firstChild.length
    363  );
    364  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    365  removeResourceScriptElements(childDoc);
    366  childDoc.head.removeAttribute("style");
    367  assert_in_array(
    368    childDoc.documentElement.innerHTML,
    369    [
    370      '<head><title>iframe</title><div>abcdef</div></head><body><br></body>',
    371      '<head><title>iframe</title><div>abcdef<br></div></head><body><br></body>',
    372    ],
    373    "The <div> element should be merged into the <div> in the <head>"
    374  );
    375  assert_false(
    376    div2.isConnected,
    377    "The <div> element should be removed"
    378  );
    379 }, `${commandName} from <div> following visible <head> element should be merged with the <div> in the <head>`);
    380 
    381 // However, don't allow to join with <script> and <style> elements because
    382 // changing them may not be safe.
    383 promise_test(async () => {
    384  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    385  const childDoc = iframe.contentDocument;
    386  childDoc.head.setAttribute("style", "display:block");
    387  const utils = new EditorTestUtils(childDoc.documentElement);
    388  const style = childDoc.createElement("style");
    389  style.setAttribute("style", "display:block;white-space:pre");
    390  style.innerHTML = "abc";
    391  const div = childDoc.createElement("div");
    392  div.innerHTML = "def";
    393  childDoc.head.appendChild(style);
    394  childDoc.documentElement.insertBefore(div, childDoc.body);
    395  // Now: <style>abc</style></head><div>def</div><body><br></body>
    396  childDoc.getSelection().collapse(
    397    testingBackspace ? div.firstChild : style.firstChild,
    398    testingBackspace ? 0 : style.firstChild.length
    399  );
    400  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    401  removeResourceScriptElements(childDoc);
    402  childDoc.head.removeAttribute("style");
    403  style.removeAttribute("style");
    404  assert_equals(
    405    childDoc.documentElement.innerHTML,
    406    '<head><title>iframe</title><style>abc</style></head><div>def</div><body><br></body>',
    407    "The <div> element should not be merged with the <style> in the <head>"
    408  );
    409  assert_true(
    410    div.isConnected,
    411    "The <div> element should not be removed"
    412  );
    413 }, `${commandName} from <div> following visible <head> element should be merged with the visible <style> in the <head>`);
    414 
    415 promise_test(async () => {
    416  await initializeAndWaitForLoad(iframe, minimumSrcDoc);
    417  const childDoc = iframe.contentDocument;
    418  childDoc.head.setAttribute("style", "display:block");
    419  const utils = new EditorTestUtils(childDoc.documentElement);
    420  const script = childDoc.createElement("script");
    421  script.setAttribute("style", "display:block;white-space:pre");
    422  script.innerHTML = "// abc";
    423  const div = childDoc.createElement("div");
    424  div.innerHTML = "def";
    425  childDoc.head.appendChild(script);
    426  childDoc.documentElement.insertBefore(div, childDoc.body);
    427  // Now: <script>// abc</ script></head><div>def</div><body><br></body>
    428  childDoc.getSelection().collapse(
    429    testingBackspace ? div.firstChild : script.firstChild,
    430    testingBackspace ? 0 : script.firstChild.length
    431  );
    432  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    433  removeResourceScriptElements(childDoc);
    434  childDoc.head.removeAttribute("style");
    435  script.removeAttribute("style");
    436  assert_equals(
    437    childDoc.documentElement.innerHTML,
    438    '<head><title>iframe</title><script>// abc</' + 'script></head><div>def</div><body><br></body>',
    439    "The <div> element should not be merged with the <script> in the <head>"
    440  );
    441  assert_true(
    442    div.isConnected,
    443    "The <div> element should not be removed"
    444  );
    445 }, `${commandName} from <div> following visible <script> element should be merged with the visible <script> in the <head>`);
    446 
    447 </script>
    448 </body>
    449 </html>