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>", " "] 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 B</b>", "<b>A 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 B</p>`, `<p style="white-space:normal">A 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 B</p>`, `<p style="white-space:pre-line">A 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>