tor-browser

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

insertText.html (9761B)


      1 <!doctype html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5 <meta name="timeout" content="long">
      6 <meta name="variant" content="?white-space=normal">
      7 <meta name="variant" content="?white-space=pre">
      8 <meta name="variant" content="?white-space=pre-line">
      9 <meta name="variant" content="?white-space=pre-wrap">
     10 <title>Inserting text in plaintext-only</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 <script>
     18 "use strict";
     19 
     20 const searchParams = new URLSearchParams(document.location.search);
     21 const whiteSpace = searchParams.get("white-space");
     22 const useBR = whiteSpace == "normal";
     23 const collapseWhiteSpaces = whiteSpace == "normal" || whiteSpace == "pre-line";
     24 const isSafari = navigator.platform.includes("Mac") &&
     25    navigator.userAgent.includes("Safari") &&
     26    !navigator.userAgent.includes("Chrome");
     27 
     28 addEventListener("load", () => {
     29  const editingHost = document.createElement("div");
     30  editingHost.style.whiteSpace = whiteSpace;
     31  editingHost.setAttribute("contenteditable", "plaintext-only");
     32  document.body.appendChild(editingHost);
     33  editingHost.focus();
     34  editingHost.getBoundingClientRect();
     35  const utils = new EditorTestUtils(editingHost);
     36 
     37  for (const data of [
     38    {
     39      initialInnerHTML: "{}<br>",
     40      insertText: " ",
     41      expected: collapseWhiteSpaces
     42        ? [" <br>", "&nbsp;"]
     43        : " ",
     44    },
     45    {
     46      initialInnerHTML: "{}<br>",
     47      insertText: "a",
     48      expected: "a",
     49    },
     50    {
     51      initialInnerHTML: "[]\n",
     52      skipIf: () => useBR,
     53      insertText: "a",
     54      expected: "a",
     55    },
     56    {
     57      initialInnerHTML: "A[]B",
     58      insertText: "a",
     59      expected: "AaB",
     60    },
     61    {
     62      initialInnerHTML: "<b>A[]B</b>",
     63      insertText: "a",
     64      expected: "<b>AaB</b>",
     65    },
     66    {
     67      initialInnerHTML: "A[]B",
     68      insertText: " ",
     69      expected: "A B",
     70    },
     71    {
     72      initialInnerHTML: "<b>A[]B</b>",
     73      insertText: " ",
     74      expected: "<b>A B</b>",
     75    },
     76    {
     77      initialInnerHTML: "<b>A[]B</b>",
     78      insertText: "  ",
     79      expected: collapseWhiteSpaces
     80        ? ["<b>A &nbsp;B</b>",  "<b>A&nbsp; B</b>"]
     81        : "<b>A  B</b>",
     82    },
     83    {
     84      initialInnerHTML: `<p style="white-space:normal">A[]B</p>`,
     85      insertText: "  ",
     86      expected: [`<p style="white-space:normal">A &nbsp;B</p>`, `<p style="white-space:normal">A&nbsp; B</p>`],
     87    },
     88    {
     89      initialInnerHTML: `<p style="white-space:pre">A[]B</p>`,
     90      insertText: "  ",
     91      expected: `<p style="white-space:pre">A  B</p>`,
     92    },
     93    {
     94      initialInnerHTML: `<p style="white-space:pre-line">A[]B</p>`,
     95      insertText: "  ",
     96      expected: [`<p style="white-space:pre-line">A &nbsp;B</p>`, `<p style="white-space:pre-line">A&nbsp; B</p>`],
     97    },
     98    {
     99      initialInnerHTML: `<p style="white-space:pre-wrap">A[]B</p>`,
    100      insertText: "  ",
    101      expected: `<p style="white-space:pre-wrap">A  B</p>`,
    102    },
    103    {
    104      initialInnerHTML: "<p><b>[]AB</b></p>",
    105      prepareDescription: "execCommand(\"insertParagraph\")",
    106      prepare: () => document.execCommand("insertParagraph"),
    107      insertText: "a",
    108      // To keep the style of next typing even after lost focus, the placeholder line break in
    109      // the empty paragraph should be wrapped in the <b>.
    110      expected: "<p><b><br></b></p><p><b>aAB</b></p>",
    111    },
    112    {
    113      initialInnerHTML: "<p><b>A[]B</b></p>",
    114      prepareDescription: "execCommand(\"insertParagraph\")",
    115      prepare: () => document.execCommand("insertParagraph"),
    116      insertText: "a",
    117      expected: "<p><b>A</b></p><p><b>aB</b></p>",
    118    },
    119    {
    120      initialInnerHTML: "<p><b>AB[]</b></p>",
    121      prepareDescription: "execCommand(\"insertParagraph\")",
    122      prepare: () => document.execCommand("insertParagraph"),
    123      insertText: "a",
    124      // To keep the style of next typing even after lost focus, the placeholder line break in
    125      // the empty paragraph after "insertParagraph" should be wrapped in the <b>.
    126      expected:  "<p><b>AB</b></p><p><b>a</b></p>",
    127    },
    128    {
    129      initialInnerHTML: "<p><b>[AB]</b></p>",
    130      prepareDescription: "execCommand(\"insertParagraph\")",
    131      prepare: () => document.execCommand("insertParagraph"),
    132      insertText: "a",
    133      expected: "<p><b><br></b></p><p><b>a</b></p>",
    134    },
    135    {
    136      initialInnerHTML: "<p><b>[]AB</b></p>",
    137      prepareDescription: "execCommand(\"insertLineBreak\")",
    138      prepare: () => document.execCommand("insertLineBreak"),
    139      insertText: "a",
    140      expected: useBR
    141        ? "<p><b><br>aAB</b></p>"
    142        : "<p><b>\naAB</b></p>",
    143    },
    144    {
    145      initialInnerHTML: "<p><b>A[]B</b></p>",
    146      prepareDescription: "execCommand(\"insertLineBreak\")",
    147      prepare: () => document.execCommand("insertLineBreak"),
    148      insertText: "a",
    149      expected: useBR
    150        ? "<p><b>A<br>aB</b></p>"
    151        : "<p><b>A\naB</b></p>",
    152    },
    153    {
    154      initialInnerHTML: "<p><b>AB[]</b></p>",
    155      prepareDescription: "execCommand(\"insertLineBreak\")",
    156      prepare: () => document.execCommand("insertLineBreak"),
    157      insertText: "a",
    158      // To keep the style of next typing even after once the paragraph becomes empty,
    159      // the placeholder line break (if there is) should be in <b>.
    160      expected: useBR
    161        ? "<p><b>AB<br>a</b></p>"
    162        : "<p><b>AB\na</b></p>",
    163    },
    164    {
    165      initialInnerHTML: "<p><b>[AB]</b></p>",
    166      prepareDescription: "execCommand(\"insertLineBreak\")",
    167      prepare: () => document.execCommand("insertLineBreak"),
    168      insertText: "a",
    169      // To keep the style of next typing even after once the paragraph becomes empty,
    170      // the placeholder line break (if there is) should be in <b>.
    171      expected: useBR
    172        ? "<p><b><br>a</b></p>"
    173        : "<p><b>\na</b></p>",
    174    },
    175    {
    176      initialInnerHTML: "<p><b>[AB]</b></p>",
    177      prepareDescription: "execCommand(\"delete\")",
    178      prepare: () => document.execCommand("delete"),
    179      insertText: "a",
    180      // To keep the style of next typing even after blur, the placeholder line break
    181      // (if there is) should be in <b>.
    182      expected: "<p><b>a</b></p>",
    183    },
    184    {
    185      initialInnerHTML: "<p><b>A[]</b></p><p>B</p>",
    186      prepareDescription: "execCommand(\"delete\") and move caret to the following paragraph temporarily",
    187      prepare: () => {
    188        document.execCommand("delete");
    189        getSelection().modify("move", "forward", "Line");
    190        getSelection().modify("move", "backward", "Line");
    191      },
    192      insertText: "a",
    193      // Moving caret shouldn't cause loosing the style.
    194      expected: "<p><b>a</b></p><p>B</p>",
    195    },
    196    {
    197      initialInnerHTML: "<p><b>[]A</b></p><p>B</p>",
    198      prepareDescription: "execCommand(\"forwardDelete\") and move caret to the following paragraph temporarily",
    199      prepare: () => {
    200        document.execCommand("forwardDelete");
    201        getSelection().modify("move", "forward", "Line");
    202        getSelection().modify("move", "backward", "Line");
    203      },
    204      insertText: "a",
    205      // Moving caret shouldn't cause loosing the style.
    206      expected: "<p><b>a</b></p><p>B</p>",
    207    },
    208    {
    209      initialInnerHTML: "<p><b>[A]</b></p><p>B</p>",
    210      prepareDescription: "execCommand(\"delete\") and move caret to the following paragraph temporarily",
    211      prepare: () => {
    212        document.execCommand("delete");
    213        getSelection().modify("move", "forward", "Line");
    214        getSelection().modify("move", "backward", "Line");
    215      },
    216      insertText: "a",
    217      // Moving caret shouldn't cause loosing the style.
    218      expected: "<p><b>a</b></p><p>B</p>",
    219    },
    220    {
    221      initialInnerHTML: "<p><b>A[]</b></b>",
    222      prepareDescription: "execCommand(\"insertParagraph\") and move caret to the preceding paragraph temporarily",
    223      prepare: () => {
    224        document.execCommand("insertParagraph");
    225        getSelection().modify("move", "backward", "Line");
    226        getSelection().modify("move", "forward", "Line");
    227      },
    228      insertText: "a",
    229      // Moving caret shouldn't cause loosing the style.
    230      expected: "<p><b>A</b></p><p><b>a</b></p>",
    231    },
    232  ]) {
    233    if (data.skipIf !== undefined && data.skipIf()) {
    234      continue;
    235    }
    236    test(() => {
    237      utils.setupEditingHost(data.initialInnerHTML);
    238      if (data.prepare) {
    239        data.prepare();
    240      }
    241      document.execCommand("insertText", false, data.insertText);
    242      if (Array.isArray(data.expected)) {
    243        assert_in_array(editingHost.innerHTML, data.expected);
    244      } else {
    245        assert_equals(editingHost.innerHTML, data.expected);
    246      }
    247    }, `execCommand("insertText", false, "${data.insertText.replaceAll("\n", "\\n")}") when ${
    248      data.initialInnerHTML.replaceAll("\n", "\\n")
    249    }${
    250      data.prepareDescription ? ` and ${data.prepareDescription.replaceAll("\n", "\\n")}`  : ""
    251    }`);
    252    promise_test(async t => {
    253      utils.setupEditingHost(data.initialInnerHTML);
    254      if (data.prepare) {
    255        data.prepare();
    256      }
    257      for (const char of data.insertText) {
    258        await utils.sendKey(char);
    259      }
    260      if (Array.isArray(data.expected)) {
    261        assert_in_array(editingHost.innerHTML, data.expected);
    262      } else {
    263        assert_equals(editingHost.innerHTML, data.expected);
    264      }
    265    }, `Typing "${data.insertText.replaceAll("\n", "\\n")}" when ${
    266      data.initialInnerHTML.replaceAll("\n", "\\n")
    267    }${
    268      data.prepareDescription ? ` and ${data.prepareDescription.replaceAll("\n", "\\n")}`  : ""
    269    }`);
    270  }
    271 }, {once: true});
    272 </script>
    273 </head>
    274 <body></body>
    275 </html>