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>