input-events-get-target-ranges-backspace.tentative.html (75627B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <meta name="timeout" content="long"> 4 <title>InputEvent.getTargetRanges() at Backspace</title> 5 <div contenteditable></div> 6 <script src="input-events-get-target-ranges.js"></script> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/resources/testdriver.js"></script> 10 <script src="/resources/testdriver-vendor.js"></script> 11 <script src="/resources/testdriver-actions.js"></script> 12 <script> 13 "use strict"; 14 15 promise_test(async (t) => { 16 initializeTest("<p>abc</p>"); 17 gSelection.collapse(gEditor.firstChild.firstChild, 0); 18 await sendBackspaceKey(); 19 checkEditorContentResultAsSubTest("<p>abc</p>", t.name); 20 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 21 startContainer: gEditor.firstChild.firstChild, 22 startOffset: 0, 23 endContainer: gEditor.firstChild.firstChild, 24 endOffset: 0, 25 }); 26 checkGetTargetRangesOfInputOnDoNothing(); 27 }, 'Backspace at "<p>[]abc</p>"'); 28 29 // Simply deletes the previous ASCII character of caret position. 30 promise_test(async (t) => { 31 initializeTest("<p>abc</p>"); 32 gSelection.collapse(gEditor.firstChild.firstChild, 1); 33 await sendBackspaceKey(); 34 checkEditorContentResultAsSubTest("<p>bc</p>", t.name); 35 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 36 startContainer: gEditor.firstChild.firstChild, 37 startOffset: 0, 38 endContainer: gEditor.firstChild.firstChild, 39 endOffset: 1, 40 }); 41 checkGetTargetRangesOfInputOnDeleteSomething(); 42 }, 'Backspace at "<p>a[]bc</p>"'); 43 44 // Simply deletes the previous ASCII character of caret position. 45 promise_test(async (t) => { 46 initializeTest("<p>abc</p>"); 47 gSelection.collapse(gEditor.firstChild.firstChild, 2); 48 await sendBackspaceKey(); 49 checkEditorContentResultAsSubTest("<p>ac</p>", t.name); 50 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 51 startContainer: gEditor.firstChild.firstChild, 52 startOffset: 1, 53 endContainer: gEditor.firstChild.firstChild, 54 endOffset: 2, 55 }); 56 checkGetTargetRangesOfInputOnDeleteSomething(); 57 }, 'Backspace at "<p>ab[]c</p>"'); 58 59 // Simply deletes the previous ASCII character of caret position. 60 promise_test(async (t) => { 61 initializeTest("<p>abc</p>"); 62 gSelection.collapse(gEditor.firstChild.firstChild, 3); 63 await sendBackspaceKey(); 64 checkEditorContentResultAsSubTest("<p>ab</p>", t.name); 65 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 66 startContainer: gEditor.firstChild.firstChild, 67 startOffset: 2, 68 endContainer: gEditor.firstChild.firstChild, 69 endOffset: 3, 70 }); 71 checkGetTargetRangesOfInputOnDeleteSomething(); 72 }, 'Backspace at "<p>abc[]</p>"'); 73 74 // Should delete the `<span>` element because it becomes empty. 75 // However, we need discussion whether the `<span>` element should be 76 // contained by a range of `getTargetRanges()`. 77 // https://github.com/w3c/input-events/issues/112 78 promise_test(async (t) => { 79 initializeTest("<p>a<span>b</span>c</p>"); 80 let c = gEditor.querySelector("span").nextSibling; 81 gSelection.collapse(c, 0); 82 await sendBackspaceKey(); 83 checkEditorContentResultAsSubTest("<p>ac</p>", t.name); 84 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 85 startContainer: gEditor.firstChild, 86 startOffset: 1, 87 endContainer: c, 88 endOffset: 0, 89 }); 90 checkGetTargetRangesOfInputOnDeleteSomething(); 91 }, 'Backspace at "<p>a<span>b</span>[]c</p>"'); 92 93 // Should delete the `<span>` element because it becomes empty. 94 // However, we need discussion whether the `<span>` element should be 95 // contained by a range of `getTargetRanges()`. 96 // https://github.com/w3c/input-events/issues/112 97 promise_test(async (t) => { 98 initializeTest("<p>a<span>b</span>c</p>"); 99 let b = gEditor.querySelector("span").firstChild; 100 gSelection.collapse(b, 1); 101 await sendBackspaceKey(); 102 checkEditorContentResultAsSubTest("<p>ac</p>", t.name); 103 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 104 startContainer: gEditor.firstChild, 105 startOffset: 1, 106 endContainer: gEditor.firstChild, 107 endOffset: 2, 108 }); 109 checkGetTargetRangesOfInputOnDeleteSomething(); 110 }, 'Backspace at "<p>a<span>b[]</span>c</p>"'); 111 112 // Invisible leading white-space may be deleted when the first visible 113 // character is deleted. If it's deleted, it should be contained by 114 // the range of `getTargetRanges()`, but needs discussion. 115 // https://github.com/w3c/input-events/issues/112 116 promise_test(async (t) => { 117 initializeTest("<p> abc</p>"); 118 gSelection.collapse(gEditor.firstChild.firstChild, 2); 119 await sendBackspaceKey(); 120 checkEditorContentResultAsSubTest( 121 [ 122 "<p>bc</p>", 123 "<p> bc</p>", 124 ], 125 t.name 126 ); 127 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 128 startContainer: gEditor.firstChild.firstChild, 129 startOffset: gEditor.firstChild.firstChild.length == 2 ? 0 : 1, 130 endContainer: gEditor.firstChild.firstChild, 131 endOffset: 2, 132 }); 133 checkGetTargetRangesOfInputOnDeleteSomething(); 134 }, 'Backspace at "<p> a[]bc</p>"'); 135 136 promise_test(async (t) => { 137 initializeTest("<p>abc</p><p>def</p>"); 138 let p1 = gEditor.firstChild; 139 let abc = p1.firstChild; 140 let p2 = p1.nextSibling; 141 let def = p2.firstChild; 142 gSelection.collapse(def, 0); 143 await sendBackspaceKey(); 144 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 145 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 146 startContainer: abc, 147 startOffset: 3, 148 endContainer: def, 149 endOffset: 0, 150 }); 151 checkGetTargetRangesOfInputOnDeleteSomething(); 152 }, 'Backspace at "<p>abc</p><p>[]def</p>"'); 153 154 // Invisible leading white-spaces in current block and invisible trailing 155 // white-spaces in the previous block should be deleted for avoiding they 156 // becoming visible when the blocks are joined. Perhaps, they should be 157 // contained by the range of `getTargetRanges()`, but needs discussion. 158 // https://github.com/w3c/input-events/issues/112 159 promise_test(async (t) => { 160 initializeTest("<p>abc </p><p> def</p>"); 161 let p1 = gEditor.firstChild; 162 let abc = p1.firstChild; 163 let p2 = p1.nextSibling; 164 let def = p2.firstChild; 165 gSelection.collapse(def, 3); 166 await sendBackspaceKey(); 167 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 168 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 169 startContainer: abc, 170 startOffset: 3, 171 endContainer: def, 172 endOffset: 3, 173 }); 174 checkGetTargetRangesOfInputOnDeleteSomething(); 175 }, 'Backspace at "<p>abc </p><p> []def</p>"'); 176 177 // Invisible leading white-spaces in current block and invisible trailing 178 // white-spaces in the previous block should be deleted for avoiding they 179 // becoming visible when the blocks are joined. Perhaps, they should be 180 // contained by the range of `getTargetRanges()`, but needs discussion. 181 // https://github.com/w3c/input-events/issues/112 182 promise_test(async (t) => { 183 initializeTest("<p>abc </p><p> def</p>"); 184 let p1 = gEditor.firstChild; 185 let abc = p1.firstChild; 186 let p2 = p1.nextSibling; 187 let def = p2.firstChild; 188 gSelection.collapse(def, 2); 189 await sendBackspaceKey(); 190 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 191 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 192 startContainer: abc, 193 startOffset: 3, 194 endContainer: def, 195 endOffset: 3, 196 }); 197 checkGetTargetRangesOfInputOnDeleteSomething(); 198 }, 'Backspace at "<p>abc </p><p> [] def</p>"'); 199 200 // Invisible leading white-spaces in current block and invisible trailing 201 // white-spaces in the previous block should be deleted for avoiding they 202 // becoming visible when the blocks are joined. Perhaps, they should be 203 // contained by the range of `getTargetRanges()`, but needs discussion. 204 // https://github.com/w3c/input-events/issues/112 205 promise_test(async (t) => { 206 initializeTest("<p>abc </p><p> def</p>"); 207 let p1 = gEditor.firstChild; 208 let abc = p1.firstChild; 209 let p2 = p1.nextSibling; 210 let def = p2.firstChild; 211 gSelection.collapse(def, 1); 212 await sendBackspaceKey(); 213 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 214 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 215 startContainer: abc, 216 startOffset: 3, 217 endContainer: def, 218 endOffset: 3, 219 }); 220 checkGetTargetRangesOfInputOnDeleteSomething(); 221 }, 'Backspace at "<p>abc </p><p> [] def</p>"'); 222 223 // Invisible leading white-spaces in current block and invisible trailing 224 // white-spaces in the previous block should be deleted for avoiding they 225 // becoming visible when the blocks are joined. Perhaps, they should be 226 // contained by the range of `getTargetRanges()`, but needs discussion. 227 // https://github.com/w3c/input-events/issues/112 228 promise_test(async (t) => { 229 initializeTest("<p>abc </p><p> def</p>"); 230 let p1 = gEditor.firstChild; 231 let abc = p1.firstChild; 232 let p2 = p1.nextSibling; 233 let def = p2.firstChild; 234 gSelection.collapse(def, 0); 235 await sendBackspaceKey(); 236 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 237 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 238 startContainer: abc, 239 startOffset: 3, 240 endContainer: def, 241 endOffset: 3, 242 }); 243 checkGetTargetRangesOfInputOnDeleteSomething(); 244 }, 'Backspace at "<p>abc </p><p>[] def</p>"'); 245 246 promise_test(async (t) => { 247 initializeTest("<p>abc</p><p><b>def</b></p>"); 248 let abc = gEditor.querySelector("p").firstChild; 249 let def = gEditor.querySelector("b").firstChild; 250 gSelection.collapse(def, 0); 251 await sendBackspaceKey(); 252 checkEditorContentResultAsSubTest("<p>abc<b>def</b></p>", t.name); 253 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 254 startContainer: abc, 255 startOffset: 3, 256 endContainer: def, 257 endOffset: 0, 258 }); 259 checkGetTargetRangesOfInputOnDeleteSomething(); 260 }, 'Backspace at "<p>abc</p><p><b>[]def</b></p>"'); 261 262 promise_test(async (t) => { 263 initializeTest("<p><b>abc</b></p><p><b>def</b></p>"); 264 let abc = gEditor.querySelector("p > b").firstChild; 265 let def = gEditor.querySelector("P + p > b").firstChild; 266 gSelection.collapse(def, 0); 267 await sendBackspaceKey(); 268 checkEditorContentResultAsSubTest( 269 [ 270 "<p><b>abc</b><b>def</b></p>", 271 "<p><b>abcdef</b></p>", 272 ], 273 t.name 274 ); 275 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 276 startContainer: abc, 277 startOffset: 3, 278 endContainer: def, 279 endOffset: 0, 280 }); 281 checkGetTargetRangesOfInputOnDeleteSomething(); 282 }, 'Backspace at "<p><b>abc</b></p><p><b>[]def</b></p>"'); 283 284 promise_test(async (t) => { 285 initializeTest("<p><i>abc</i></p><p><b>def</b></p>"); 286 let abc = gEditor.querySelector("i").firstChild; 287 let def = gEditor.querySelector("b").firstChild; 288 gSelection.collapse(def, 0); 289 await sendBackspaceKey(); 290 checkEditorContentResultAsSubTest("<p><i>abc</i><b>def</b></p>", t.name); 291 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 292 startContainer: abc, 293 startOffset: 3, 294 endContainer: def, 295 endOffset: 0, 296 }); 297 checkGetTargetRangesOfInputOnDeleteSomething(); 298 }, 'Backspace at "<p><i>abc</i></p><p><b>[]def</b></p>"'); 299 300 // Invisible leading white-spaces in the current block should be deleted 301 // for avoiding they becoming visible when the blocks are joined, but 302 // preformatted trailing white-spaces in the first block shouldn't be 303 // deleted. Perhaps, the invisible white-spaces should be contained by 304 // the range of `getTargetRanges()`, but needs discussion. 305 // https://github.com/w3c/input-events/issues/112 306 promise_test(async (t) => { 307 initializeTest("<pre>abc </pre><p> def</p>"); 308 let pre = gEditor.firstChild; 309 let abc = pre.firstChild; 310 let p = pre.nextSibling; 311 let def = p.firstChild; 312 gSelection.collapse(def, 3); 313 await sendBackspaceKey(); 314 // https://github.com/w3c/input-events/issues/112 315 // Shouldn't make the invisible white-spaces visible. 316 checkEditorContentResultAsSubTest("<pre>abc def</pre>", t.name); 317 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 318 startContainer: abc, 319 startOffset: 6, 320 endContainer: def, 321 endOffset: 3, 322 }); 323 checkGetTargetRangesOfInputOnDeleteSomething(); 324 }, 'Backspace at "<pre>abc </pre><p> []def</p>"'); 325 326 // Invisible leading/trailing white-spaces in the current block should be 327 // deleted for avoiding they becoming visible when the blocks are joined, but 328 // preformatted trailing white-spaces in the first block shouldn't be 329 // deleted. Perhaps, the invisible leading white-spaces should be contained 330 // by the range of `getTargetRanges()`, but needs discussion. 331 // And also not sure whether the trailing white-spaces should be contained 332 // by additional range of `getTargetRanges()` or not because of the 333 // implementation cost and runtime cost. Needs discuss. 334 // https://github.com/w3c/input-events/issues/112 335 promise_test(async (t) => { 336 initializeTest("<pre>abc </pre><p> def </p>"); 337 let pre = gEditor.firstChild; 338 let abc = pre.firstChild; 339 let p = pre.nextSibling; 340 let def = p.firstChild; 341 gSelection.collapse(def, 3); 342 await sendBackspaceKey(); 343 checkEditorContentResultAsSubTest("<pre>abc def </pre>", t.name); 344 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 345 startContainer: abc, 346 startOffset: 6, 347 endContainer: def, 348 endOffset: 3, 349 }); 350 checkGetTargetRangesOfInputOnDeleteSomething(); 351 }, 'Backspace at "<pre>abc </pre><p> []def </p>"'); 352 353 // Invisible trailing white-spaces in the first block should be deleted 354 // when the block is joined with the preformatted following block, but 355 // the leading white-spaces in the preformatted block shouldn't be 356 // removed. So, in this case, the invisible trailing white-spaces should 357 // be in the range of `getTargetRanges()`, but not so for the preformatted 358 // visible leading white-spaces. But needs discussion. 359 // https://github.com/w3c/input-events/issues/112 360 promise_test(async (t) => { 361 initializeTest("<p>abc </p><pre> def</pre>"); 362 let p = gEditor.firstChild; 363 let abc = p.firstChild; 364 let pre = p.nextSibling; 365 let def = pre.firstChild; 366 gSelection.collapse(def, 0); 367 await sendBackspaceKey(); 368 checkEditorContentResultAsSubTest( 369 [ 370 "<p>abc def</p>", 371 "<p>abc def</p>", 372 "<p>abc def</p>", 373 "<p>abc def</p>", 374 ], 375 t.name 376 ); 377 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 378 startContainer: abc, 379 startOffset: 6, 380 endContainer: def, 381 endOffset: 0, 382 }); 383 checkGetTargetRangesOfInputOnDeleteSomething(); 384 }, 'Backspace at "<p>abc </p><pre>[] def</pre>"'); 385 386 promise_test(async (t) => { 387 initializeTest('<p style="white-space:pre-line">abc\ndef</p>'); 388 const p = gEditor.firstChild; 389 const text = p.firstChild; 390 gSelection.collapse(text, "abc\n".length); 391 await sendBackspaceKey(); 392 checkEditorContentResultAsSubTest( 393 '<p style="white-space:pre-line">abcdef</p>', 394 t.name 395 ); 396 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 397 startContainer: text, 398 startOffset: "abc".length, 399 endContainer: text, 400 endOffset: "abc\n".length, 401 }); 402 checkGetTargetRangesOfInputOnDeleteSomething(); 403 }, 'Backspace at "<p style="white-space:pre-line">abc\\n[]def</p>"'); 404 405 promise_test(async (t) => { 406 initializeTest('<p style="white-space:pre-line">abc \ndef</p>'); 407 const p = gEditor.firstChild; 408 const text = p.firstChild; 409 gSelection.collapse(text, "abc \n".length); 410 await sendBackspaceKey(); 411 checkEditorContentResultAsSubTest( 412 '<p style="white-space:pre-line">abcdef</p>', 413 t.name 414 ); 415 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 416 startContainer: text, 417 startOffset: "abc".length, 418 endContainer: text, 419 endOffset: "abc \n".length, 420 }); 421 checkGetTargetRangesOfInputOnDeleteSomething(); 422 }, 'Backspace at "<p style="white-space:pre-line">abc \\n[]def</p>"'); 423 424 promise_test(async (t) => { 425 initializeTest('<p style="white-space:pre-line">abc\n def</p>'); 426 const p = gEditor.firstChild; 427 const text = p.firstChild; 428 gSelection.collapse(text, "abc\n ".length); 429 await sendBackspaceKey(); 430 checkEditorContentResultAsSubTest( 431 '<p style="white-space:pre-line">abcdef</p>', 432 t.name 433 ); 434 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 435 startContainer: text, 436 startOffset: "abc".length, 437 endContainer: text, 438 endOffset: "abc\n ".length, 439 }); 440 checkGetTargetRangesOfInputOnDeleteSomething(); 441 }, 'Backspace at "<p style="white-space:pre-line">abc\\n []def</p>"'); 442 443 promise_test(async (t) => { 444 initializeTest('<p style="white-space:pre-line">abc \n def</p>'); 445 const p = gEditor.firstChild; 446 const text = p.firstChild; 447 gSelection.collapse(text, "abc \n ".length); 448 await sendBackspaceKey(); 449 checkEditorContentResultAsSubTest( 450 '<p style="white-space:pre-line">abcdef</p>', 451 t.name 452 ); 453 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 454 startContainer: text, 455 startOffset: "abc".length, 456 endContainer: text, 457 endOffset: "abc \n ".length, 458 }); 459 checkGetTargetRangesOfInputOnDeleteSomething(); 460 }, 'Backspace at "<p style="white-space:pre-line">abc \\n []def</p>"'); 461 462 promise_test(async (t) => { 463 initializeTest('<p style="white-space:pre-line">abc \n \n def</p>'); 464 const p = gEditor.firstChild; 465 const text = p.firstChild; 466 gSelection.collapse(text, "abc \n \n ".length); 467 await sendBackspaceKey(); 468 checkEditorContentResultAsSubTest( 469 '<p style="white-space:pre-line">abc \ndef</p>', 470 t.name 471 ); 472 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 473 startContainer: text, 474 startOffset: "abc \n".length, 475 endContainer: text, 476 endOffset: "abc \n \n ".length, 477 }); 478 checkGetTargetRangesOfInputOnDeleteSomething(); 479 }, 'Backspace at "<p style="white-space:pre-line">abc \\n \\n []def</p>"'); 480 481 // If the first block has invisible `<br>` element and joining it with 482 // the following block, the invisible trailing `<br>` element should be 483 // deleted and join the blocks. Therefore, the target range should contain 484 // the `<br>` element and block boundaries. But maybe needs discussion. 485 // https://github.com/w3c/input-events/issues/112 486 promise_test(async (t) => { 487 initializeTest("<p>abc<br></p><p>def</p>"); 488 let p1 = gEditor.firstChild; 489 let abc = p1.firstChild; 490 let p2 = p1.nextSibling; 491 let def = p2.firstChild; 492 gSelection.collapse(def, 0); 493 await sendBackspaceKey(); 494 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 495 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 496 startContainer: p1, 497 startOffset: 1, 498 endContainer: def, 499 endOffset: 0, 500 }); 501 checkGetTargetRangesOfInputOnDeleteSomething(); 502 }, 'Backspace at "<p>abc<br></p><p>[]def</p>"'); 503 504 // If the first block has invisible `<br>` element for empty last line and 505 // joining it with the following block, the invisible trailing `<br>` element 506 // should be deleted and join the blocks. Therefore, the target range should 507 // contain the `<br>` element and block boundaries. But maybe needs discussion. 508 // https://github.com/w3c/input-events/issues/112 509 promise_test(async (t) => { 510 initializeTest("<p>abc<br><br></p><p>def</p>"); 511 let p1 = gEditor.firstChild; 512 let abc = p1.firstChild; 513 let p2 = p1.nextSibling; 514 let def = p2.firstChild; 515 gSelection.collapse(def, 0); 516 await sendBackspaceKey(); 517 checkEditorContentResultAsSubTest("<p>abc<br>def</p>", t.name); 518 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 519 startContainer: p1, 520 startOffset: 2, 521 endContainer: def, 522 endOffset: 0, 523 }); 524 checkGetTargetRangesOfInputOnDeleteSomething(); 525 }, 'Backspace at "<p>abc<br><br></p><p>[]def</p>"'); 526 527 // Deleting visible `<br>` element should be contained by a range of 528 // `getTargetRanges()`. 529 promise_test(async (t) => { 530 initializeTest("<p>abc<br>def</p>"); 531 let p = document.querySelector("p"); 532 let def = gEditor.querySelector("br").nextSibling; 533 gSelection.collapse(def, 0); 534 await sendBackspaceKey(); 535 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 536 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 537 startContainer: p, 538 startOffset: 1, 539 endContainer: p, 540 endOffset: 2, 541 }); 542 checkGetTargetRangesOfInputOnDeleteSomething(); 543 }, 'Backspace at "<p>abc<br>[]def</p>"'); 544 545 // Deleting visible `<br>` element following white-space should not include 546 // the preceding white-space in the range. 547 promise_test(async (t) => { 548 initializeTest("<p>abc <br>def</p>"); 549 let p = gEditor.querySelector("p"); 550 let def = gEditor.querySelector("br").nextSibling; 551 gSelection.collapse(def, 0); 552 await sendBackspaceKey(); 553 checkEditorContentResultAsSubTest("<p>abc def</p>", t.name); 554 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 555 startContainer: p, 556 startOffset: 1, 557 endContainer: p, 558 endOffset: 2, 559 }); 560 checkGetTargetRangesOfInputOnDeleteSomething(); 561 }, 'Backspace at "<p>abc <br>[]def</p>"'); 562 563 promise_test(async (t) => { 564 initializeTest(`<p>abc<img src="${kImgSrc}">def</p>`); 565 let p = gEditor.querySelector("p"); 566 let def = p.lastChild; 567 gSelection.collapse(def, 0); 568 await sendBackspaceKey(); 569 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 570 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 571 startContainer: p, 572 startOffset: 1, 573 endContainer: p, 574 endOffset: 2, 575 }); 576 checkGetTargetRangesOfInputOnDeleteSomething(); 577 }, 'Backspace at "<p>abc<img>[]def</p>"'); 578 579 // White-spaces around `<img>` element are visible so that they shouldn't 580 // be included into the target ranges. 581 promise_test(async (t) => { 582 initializeTest(`<p>abc <img src="${kImgSrc}">def</p>`); 583 let p = gEditor.querySelector("p"); 584 let def = p.lastChild; 585 gSelection.collapse(def, 0); 586 await sendBackspaceKey(); 587 588 // If the browser does not join `Text` nodes around <img>, it's fine to 589 // convert the preceding white-space to an NBSP. 590 if ( 591 gEditor.querySelector("p")?.childNodes.length == 2 && 592 gEditor.querySelector("p").childNodes[0].length == "abc ".length 593 ) { 594 checkEditorContentResultAsSubTest([ 595 "<p>abc def</p>", 596 "<p>abc def</p>", 597 ], t.name); 598 } else { 599 checkEditorContentResultAsSubTest("<p>abc def</p>", t.name); 600 } 601 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 602 startContainer: p, 603 startOffset: 1, 604 endContainer: p, 605 endOffset: 2, 606 }); 607 checkGetTargetRangesOfInputOnDeleteSomething(); 608 }, 'Backspace at "<p>abc <img>[]def</p>"'); 609 610 // White-spaces around `<img>` element are visible so that they shouldn't 611 // be included into the target ranges. 612 promise_test(async (t) => { 613 initializeTest(`<p>abc<img src="${kImgSrc}"> def</p>`); 614 let p = gEditor.querySelector("p"); 615 let def = p.lastChild; 616 gSelection.collapse(def, 0); 617 await sendBackspaceKey(); 618 // If the browser does not join `Text` nodes around <img>, it's fine to 619 // convert the preceding white-space to an NBSP. 620 if ( 621 gEditor.querySelector("p")?.childNodes.length == 2 && 622 gEditor.querySelector("p").childNodes[0].length == "abc".length 623 ) { 624 checkEditorContentResultAsSubTest([ 625 "<p>abc def</p>", 626 "<p>abc def</p>", 627 ], t.name); 628 } else { 629 checkEditorContentResultAsSubTest("<p>abc def</p>", t.name); 630 } 631 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 632 startContainer: p, 633 startOffset: 1, 634 endContainer: p, 635 endOffset: 2, 636 }); 637 checkGetTargetRangesOfInputOnDeleteSomething(); 638 }, 'Backspace at "<p>abc<img>[] def</p>"'); 639 640 promise_test(async (t) => { 641 initializeTest(`<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`); 642 let p = gEditor.querySelector("p"); 643 let abc = p.firstChild; 644 gSelection.collapse(p, 2); 645 await sendBackspaceKey(); 646 checkEditorContentResultAsSubTest( 647 `<p>abc<img src="${kImgSrc}">def</p>`, 648 t.name 649 ); 650 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 651 startContainer: p, 652 startOffset: 1, 653 endContainer: p, 654 endOffset: 2, 655 }); 656 checkGetTargetRangesOfInputOnDeleteSomething(); 657 }, 'Backspace at "<p>abc<img>{}<img>def</p>"'); 658 659 promise_test(async (t) => { 660 initializeTest(`<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`); 661 let p = gEditor.querySelector("p"); 662 let def = p.lastChild; 663 gSelection.collapse(def, 0); 664 await sendBackspaceKey(); 665 checkEditorContentResultAsSubTest( 666 `<p>abc<img src="${kImgSrc}">def</p>`, 667 t.name 668 ); 669 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 670 startContainer: p, 671 startOffset: 2, 672 endContainer: p, 673 endOffset: 3, 674 }); 675 checkGetTargetRangesOfInputOnDeleteSomething(); 676 }, 'Backspace at "<p>abc<img><img>[]def</p>"'); 677 678 promise_test(async (t) => { 679 initializeTest(`<div>abc<hr>def</div>`); 680 let div = gEditor.querySelector("div"); 681 let hr = gEditor.querySelector("hr"); 682 let def = hr.nextSibling; 683 gSelection.collapse(def, 0); 684 await sendBackspaceKey(); 685 checkEditorContentResultAsSubTest("<div>abcdef</div>", t.name); 686 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 687 startContainer: div, 688 startOffset: 1, 689 endContainer: div, 690 endOffset: 2, 691 }); 692 checkGetTargetRangesOfInputOnDeleteSomething(); 693 }, 'Backspace at "<div>abc<hr>[]def</div>"'); 694 695 // White-spaces around block element are invisible white-spaces so that 696 // they should be included into the target ranges to avoid they bcome 697 // visible. 698 // https://github.com/w3c/input-events/issues/112 699 promise_test(async (t) => { 700 initializeTest(`<div>abc <hr>def</div>`); 701 let div = gEditor.querySelector("div"); 702 let abc = div.firstChild; 703 let hr = gEditor.querySelector("hr"); 704 let def = hr.nextSibling; 705 gSelection.collapse(def, 0); 706 await sendBackspaceKey(); 707 checkEditorContentResultAsSubTest("<div>abcdef</div>", t.name); 708 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 709 startContainer: abc, 710 startOffset: 3, 711 endContainer: div, 712 endOffset: 2, 713 }); 714 checkGetTargetRangesOfInputOnDeleteSomething(); 715 }, 'Backspace at "<div>abc <hr>[]def</div>"'); 716 717 // White-spaces around block element are invisible white-spaces so that 718 // they should be included into the target ranges to avoid they bcome 719 // visible. 720 // https://github.com/w3c/input-events/issues/112 721 promise_test(async (t) => { 722 initializeTest(`<div>abc<hr> def</div>`); 723 let div = gEditor.querySelector("div"); 724 let hr = gEditor.querySelector("hr"); 725 let def = hr.nextSibling; 726 gSelection.collapse(def, 1); 727 await sendBackspaceKey(); 728 checkEditorContentResultAsSubTest("<div>abcdef</div>", t.name); 729 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 730 startContainer: div, 731 startOffset: 1, 732 endContainer: def, 733 endOffset: 1, 734 }); 735 checkGetTargetRangesOfInputOnDeleteSomething(); 736 }, 'Backspace at "<div>abc<hr> []def</div>"'); 737 738 // Invisible `<br>` element immediately before `<hr>` element should be 739 // deleted once, and both of them should be included in the target range. 740 promise_test(async (t) => { 741 initializeTest(`<div>abc<br><hr>def</div>`); 742 let div = gEditor.querySelector("div"); 743 let def = div.lastChild; 744 gSelection.collapse(def, 0); 745 await sendBackspaceKey(); 746 checkEditorContentResultAsSubTest("<div>abcdef</div>", t.name); 747 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 748 startContainer: div, 749 startOffset: 1, 750 endContainer: div, 751 endOffset: 3, 752 }); 753 checkGetTargetRangesOfInputOnDeleteSomething(); 754 }, 'Backspace at "<div>abc<br><hr>[]def</div>"'); 755 756 // Deleting visible `<br>` element followed by white-space should include 757 // the following white-space in the range because it shouldn't become 758 // visible and should be deleted for avoiding it. 759 // https://github.com/w3c/input-events/issues/112 760 promise_test(async (t) => { 761 initializeTest("<p>abc<br> def</p>"); 762 let p = gEditor.querySelector("p"); 763 let def = gEditor.querySelector("br").nextSibling; 764 gSelection.collapse(def, 0); 765 await sendBackspaceKey(); 766 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 767 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 768 startContainer: p, 769 startOffset: 1, 770 endContainer: def, 771 endOffset: 1, 772 }); 773 checkGetTargetRangesOfInputOnDeleteSomething(); 774 }, 'Backspace at "<p>abc<br>[] def</p>"'); 775 promise_test(async (t) => { 776 initializeTest("<p>abc<br> def</p>"); 777 let p = gEditor.querySelector("p"); 778 let def = gEditor.querySelector("br").nextSibling; 779 gSelection.collapse(def, 1); 780 await sendBackspaceKey(); 781 checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); 782 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 783 startContainer: p, 784 startOffset: 1, 785 endContainer: def, 786 endOffset: 1, 787 }); 788 checkGetTargetRangesOfInputOnDeleteSomething(); 789 }, 'Backspace at "<p>abc<br> []def</p>"'); 790 791 promise_test(async (t) => { 792 initializeTest("<div>abc<p>def<br>ghi</p></div>"); 793 let p = gEditor.querySelector("p"); 794 let def = p.firstChild; 795 let abc = gEditor.firstChild.firstChild; 796 gSelection.collapse(def, 0); 797 await sendBackspaceKey(); 798 checkEditorContentResultAsSubTest( 799 [ 800 "<div>abcdef<p>ghi</p></div>", 801 "<div>abcdef<br><p>ghi</p></div>", 802 ], 803 t.name 804 ); 805 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 806 startContainer: abc, 807 startOffset: 3, 808 endContainer: def, 809 endOffset: 0, 810 }); 811 checkGetTargetRangesOfInputOnDeleteSomething(); 812 }, 'Backspace at "<div>abc<p>[]def<br>ghi</p></div>"'); 813 814 // Joining parent block and child block should remove invisible preceding 815 // white-spaces of the child block and invisible leading white-spaces in 816 // the child block, and they should be contained in a range of 817 // `getTargetRanges()`, but maybe needs discussion. 818 // https://github.com/w3c/input-events/issues/112 819 promise_test(async (t) => { 820 initializeTest("<div>abc <p> def<br>ghi</p></div>"); 821 let p = gEditor.querySelector("p"); 822 let def = p.firstChild; 823 let abc = gEditor.firstChild.firstChild; 824 gSelection.collapse(def, 3); 825 await sendBackspaceKey(); 826 checkEditorContentResultAsSubTest( 827 [ 828 "<div>abcdef<p>ghi</p></div>", 829 "<div>abcdef<br><p>ghi</p></div>", 830 ], 831 t.name 832 ); 833 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 834 startContainer: abc, 835 startOffset: 3, 836 endContainer: def, 837 endOffset: 3, 838 }); 839 checkGetTargetRangesOfInputOnDeleteSomething(); 840 }, 'Backspace at "<div>abc <p> []def<br>ghi</p></div>"'); 841 842 promise_test(async (t) => { 843 initializeTest("<div>abc<p><b>def</b></p></div>"); 844 let abc = gEditor.querySelector("div").firstChild; 845 let def = gEditor.querySelector("b").firstChild; 846 gSelection.collapse(def, 0); 847 await sendBackspaceKey(); 848 checkEditorContentResultAsSubTest("<div>abc<b>def</b></div>", t.name); 849 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 850 startContainer: abc, 851 startOffset: 3, 852 endContainer: def, 853 endOffset: 0, 854 }); 855 checkGetTargetRangesOfInputOnDeleteSomething(); 856 }, 'Backspace at "<div>abc<p><b>[]def</b></p></div>"'); 857 858 promise_test(async (t) => { 859 initializeTest("<div><b>abc</b><p><b>def</b></p></div>"); 860 let abc = gEditor.querySelector("b").firstChild; 861 let def = gEditor.querySelector("p > b").firstChild; 862 gSelection.collapse(def, 0); 863 await sendBackspaceKey(); 864 checkEditorContentResultAsSubTest( 865 [ 866 "<div><b>abc</b><b>def</b></div>", 867 "<div><b>abcdef</b></div>", 868 ], 869 t.name 870 ); 871 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 872 startContainer: abc, 873 startOffset: 3, 874 endContainer: def, 875 endOffset: 0, 876 }); 877 checkGetTargetRangesOfInputOnDeleteSomething(); 878 }, 'Backspace at "<div><b>abc</b><p><b>[]def</b></p></div>"'); 879 880 promise_test(async (t) => { 881 initializeTest("<div><i>abc</i><p><b>def</b></p></div>"); 882 let abc = gEditor.querySelector("i").firstChild; 883 let def = gEditor.querySelector("b").firstChild; 884 gSelection.collapse(def, 0); 885 await sendBackspaceKey(); 886 checkEditorContentResultAsSubTest("<div><i>abc</i><b>def</b></div>", t.name); 887 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 888 startContainer: abc, 889 startOffset: 3, 890 endContainer: def, 891 endOffset: 0, 892 }); 893 checkGetTargetRangesOfInputOnDeleteSomething(); 894 }, 'Backspace at "<div><i>abc</i><p><b>[]def</b></p></div>"'); 895 896 promise_test(async (t) => { 897 initializeTest("<div><p>abc</p>def</div>"); 898 let abc = gEditor.querySelector("p").firstChild; 899 let def = gEditor.querySelector("p").nextSibling; 900 gSelection.collapse(def, 0); 901 await sendBackspaceKey(); 902 checkEditorContentResultAsSubTest( 903 [ 904 "<div><p>abcdef</p></div>", 905 "<div><p>abcdef<br></p></div>", 906 ], 907 t.name 908 ); 909 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 910 startContainer: abc, 911 startOffset: 3, 912 endContainer: def, 913 endOffset: 0, 914 }); 915 checkGetTargetRangesOfInputOnDeleteSomething(); 916 }, 'Backspace at "<div><p>abc</p>[]def</div>"'); 917 918 // Joining child block and parent block should remove invisible trailing 919 // white-spaces of the child block and invisible following white-spaces 920 // in the parent block, and they should be contained by a range of 921 // `getTargetRanges()`, but maybe needs discussion. 922 // https://github.com/w3c/input-events/issues/112 923 promise_test(async (t) => { 924 initializeTest("<div><p>abc </p> def</div>"); 925 let abc = gEditor.querySelector("p").firstChild; 926 let def = gEditor.querySelector("p").nextSibling; 927 gSelection.collapse(def, 3); 928 await sendBackspaceKey(); 929 checkEditorContentResultAsSubTest( 930 [ 931 "<div><p>abcdef</p></div>", 932 "<div><p>abcdef<br></p></div>", 933 ], 934 t.name 935 ); 936 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 937 startContainer: abc, 938 startOffset: 3, 939 endContainer: def, 940 endOffset: 3, 941 }); 942 checkGetTargetRangesOfInputOnDeleteSomething(); 943 }, 'Backspace at "<div><p>abc </p> []def</div>"'); 944 945 promise_test(async (t) => { 946 initializeTest("<div><p><b>abc</b></p>def</div>"); 947 let abc = gEditor.querySelector("b").firstChild; 948 let def = gEditor.querySelector("p").nextSibling; 949 gSelection.collapse(def, 0); 950 await sendBackspaceKey(); 951 checkEditorContentResultAsSubTest("<div><p><b>abc</b>def</p></div>", t.name); 952 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 953 startContainer: abc, 954 startOffset: 3, 955 endContainer: def, 956 endOffset: 0, 957 }); 958 checkGetTargetRangesOfInputOnDeleteSomething(); 959 }, 'Backspace at "<div><p><b>abc</b></p>[]def</div>"'); 960 961 promise_test(async (t) => { 962 initializeTest("<div><p><b>abc</b></p><b>def</b></div>"); 963 let abc = gEditor.querySelector("b").firstChild; 964 let def = gEditor.querySelector("div > b").firstChild; 965 gSelection.collapse(def, 0); 966 await sendBackspaceKey(); 967 checkEditorContentResultAsSubTest( 968 [ 969 "<div><p><b>abc</b><b>def</b></p></div>", 970 "<div><p><b>abcdef</b></p></div>", 971 ], 972 t.name 973 ); 974 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 975 startContainer: abc, 976 startOffset: 3, 977 endContainer: def, 978 endOffset: 0, 979 }); 980 checkGetTargetRangesOfInputOnDeleteSomething(); 981 }, 'Backspace at "<div><p><b>abc</b></p><b>[]def</b></div>"'); 982 983 promise_test(async (t) => { 984 initializeTest("<div><p><b>abc</b></p><i>def</i></div>"); 985 let abc = gEditor.querySelector("b").firstChild; 986 let def = gEditor.querySelector("i").firstChild; 987 gSelection.collapse(def, 0); 988 await sendBackspaceKey(); 989 checkEditorContentResultAsSubTest( 990 "<div><p><b>abc</b><i>def</i></p></div>", 991 t.name 992 ); 993 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 994 startContainer: abc, 995 startOffset: 3, 996 endContainer: def, 997 endOffset: 0, 998 }); 999 checkGetTargetRangesOfInputOnDeleteSomething(); 1000 }, 'Backspace at "<div><p><b>abc</b></p><i>[]def</i></div>"'); 1001 1002 promise_test(async (t) => { 1003 initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>"); 1004 let abc = gEditor.querySelector("div").firstChild; 1005 let def = gEditor.querySelector("li").firstChild; 1006 gSelection.collapse(def, 0); 1007 await sendBackspaceKey(); 1008 checkEditorContentResultAsSubTest("<div>abcdefghi</div>", t.name); 1009 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1010 startContainer: abc, 1011 startOffset: 3, 1012 endContainer: def, 1013 endOffset: 0, 1014 }); 1015 checkGetTargetRangesOfInputOnDeleteSomething(); 1016 }, 'Backspace at "<div>abc<ul><li>[]def</li></ul>ghi</div>"'); 1017 1018 promise_test(async (t) => { 1019 initializeTest("<div>abc <ul><li> def </li></ul> ghi</div>"); 1020 let abc = gEditor.querySelector("div").firstChild; 1021 let def = gEditor.querySelector("li").firstChild; 1022 gSelection.collapse(def, 1); 1023 await sendBackspaceKey(); 1024 checkEditorContentResultAsSubTest("<div>abcdefghi</div>", t.name); 1025 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1026 startContainer: abc, 1027 startOffset: 3, 1028 endContainer: def, 1029 endOffset: 1, 1030 }); 1031 checkGetTargetRangesOfInputOnDeleteSomething(); 1032 }, 'Backspace at "<div>abc <ul><li> []def </li></ul> ghi</div>"'); 1033 1034 promise_test(async (t) => { 1035 initializeTest("<div>abc <ul><li> def </li></ul> ghi</div>"); 1036 let abc = gEditor.querySelector("div").firstChild; 1037 let def = gEditor.querySelector("li").firstChild; 1038 gSelection.collapse(def, 0); 1039 await sendBackspaceKey(); 1040 checkEditorContentResultAsSubTest("<div>abcdefghi</div>", t.name); 1041 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1042 startContainer: abc, 1043 startOffset: 3, 1044 endContainer: def, 1045 endOffset: 1, 1046 }); 1047 checkGetTargetRangesOfInputOnDeleteSomething(); 1048 }, 'Backspace at "<div>abc <ul><li>[] def </li></ul> ghi</div>"'); 1049 1050 promise_test(async (t) => { 1051 initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>"); 1052 let def = gEditor.querySelector("li").firstChild; 1053 let ghi = gEditor.querySelector("ul").nextSibling; 1054 gSelection.collapse(ghi, 0); 1055 await sendBackspaceKey(); 1056 checkEditorContentResultAsSubTest( 1057 "<div>abc<ul><li>defghi</li></ul></div>", 1058 t.name 1059 ); 1060 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1061 startContainer: def, 1062 startOffset: 3, 1063 endContainer: ghi, 1064 endOffset: 0, 1065 }); 1066 checkGetTargetRangesOfInputOnDeleteSomething(); 1067 }, 'Backspace at "<div>abc<ul><li>def</li></ul>[]ghi</div>"'); 1068 1069 promise_test(async (t) => { 1070 initializeTest("<div>abc <ul><li> def </li></ul> ghi</div>"); 1071 let def = gEditor.querySelector("li").firstChild; 1072 let ghi = gEditor.querySelector("ul").nextSibling; 1073 gSelection.collapse(ghi, 1); 1074 await sendBackspaceKey(); 1075 checkEditorContentResultAsSubTest( 1076 [ 1077 "<div>abc <ul><li> defghi</li></ul></div>", 1078 "<div>abc <ul><li>defghi</li></ul></div>", 1079 "<div>abc<ul><li>defghi</li></ul></div>", 1080 ], 1081 t.name 1082 ); 1083 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1084 startContainer: def, 1085 startOffset: 5, 1086 endContainer: ghi, 1087 endOffset: 1, 1088 }); 1089 checkGetTargetRangesOfInputOnDeleteSomething(); 1090 }, 'Backspace at "<div>abc <ul><li> def </li></ul> []ghi</div>"'); 1091 1092 promise_test(async (t) => { 1093 initializeTest("<div>abc <ul><li> def </li></ul> ghi</div>"); 1094 let def = gEditor.querySelector("li").firstChild; 1095 let ghi = gEditor.querySelector("ul").nextSibling; 1096 gSelection.collapse(ghi, 0); 1097 await sendBackspaceKey(); 1098 checkEditorContentResultAsSubTest( 1099 [ 1100 "<div>abc <ul><li> defghi</li></ul></div>", 1101 "<div>abc <ul><li>defghi</li></ul></div>", 1102 "<div>abc<ul><li>defghi</li></ul></div>", 1103 ], 1104 t.name 1105 ); 1106 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1107 startContainer: def, 1108 startOffset: 5, 1109 endContainer: ghi, 1110 endOffset: 1, 1111 }); 1112 checkGetTargetRangesOfInputOnDeleteSomething(); 1113 }, 'Backspace at "<div>abc <ul><li> def </li></ul>[] ghi</div>"'); 1114 1115 promise_test(async (t) => { 1116 initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>"); 1117 let abc = gEditor.querySelector("div").firstChild; 1118 let def = gEditor.querySelector("li").firstChild; 1119 gSelection.collapse(def, 0); 1120 await sendBackspaceKey(); 1121 checkEditorContentResultAsSubTest( 1122 "<div>abcdef<ul><li>ghi</li></ul>jkl</div>", 1123 t.name 1124 ); 1125 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1126 startContainer: abc, 1127 startOffset: 3, 1128 endContainer: def, 1129 endOffset: 0, 1130 }); 1131 checkGetTargetRangesOfInputOnDeleteSomething(); 1132 }, 'Backspace at "<div>abc<ul><li>[]def</li><li>ghi</li></ul>jkl</div>"'); 1133 1134 promise_test(async (t) => { 1135 initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>"); 1136 let def = gEditor.querySelector("li").firstChild; 1137 let ghi = gEditor.querySelector("li + li").firstChild; 1138 gSelection.collapse(ghi, 0); 1139 await sendBackspaceKey(); 1140 checkEditorContentResultAsSubTest( 1141 "<div>abc<ul><li>defghi</li></ul>jkl</div>", 1142 t.name 1143 ); 1144 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1145 startContainer: def, 1146 startOffset: 3, 1147 endContainer: ghi, 1148 endOffset: 0, 1149 }); 1150 checkGetTargetRangesOfInputOnDeleteSomething(); 1151 }, 'Backspace at "<div>abc<ul><li>def</li><li>[]ghi</li></ul>jkl</div>"'); 1152 1153 promise_test(async (t) => { 1154 initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>"); 1155 let ghi = gEditor.querySelector("li + li").firstChild; 1156 let jkl = gEditor.querySelector("ul").nextSibling; 1157 gSelection.collapse(jkl, 0); 1158 await sendBackspaceKey(); 1159 assert_equals(gEditor.innerHTML, 1160 "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>", 1161 t.name 1162 ); 1163 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1164 startContainer: ghi, 1165 startOffset: 3, 1166 endContainer: jkl, 1167 endOffset: 0, 1168 }); 1169 checkGetTargetRangesOfInputOnDeleteSomething(); 1170 }, 'Backspace at "<div>abc<ul><li>def</li><li>ghi</li></ul>[]jkl</div>"'); 1171 1172 // Backspace in empty paragraph should remove the empty paragraph. In this 1173 // case, it should be treated as joining with the previous paragraph. 1174 // The target range should include the invisible <br> element in the empty 1175 // paragraph. 1176 promise_test(async (t) => { 1177 initializeTest("<p>abc</p><p><br></p>"); 1178 let p1 = gEditor.querySelector("p"); 1179 let abc = p1.firstChild; 1180 let p2 = p1.nextSibling; 1181 gSelection.collapse(p2, 0); 1182 await sendBackspaceKey(); 1183 checkEditorContentResultAsSubTest("<p>abc</p>", t.name); 1184 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1185 startContainer: abc, 1186 startOffset: 3, 1187 endContainer: p2, 1188 endOffset: 1, 1189 }); 1190 checkGetTargetRangesOfInputOnDeleteSomething(); 1191 }, 'Backspace at "<p>abc</p><p>{}<br></p>"'); 1192 1193 // Delete ignore the empty span and the other things must be same as the 1194 // previous test. 1195 promise_test(async (t) => { 1196 initializeTest("<p>abc</p><p><span></span><br></p>"); 1197 let p1 = gEditor.querySelector("p"); 1198 let abc = p1.firstChild; 1199 let p2 = p1.nextSibling; 1200 let span = p2.firstChild; 1201 gSelection.collapse(span, 0); 1202 await sendBackspaceKey(); 1203 checkEditorContentResultAsSubTest("<p>abc</p>", t.name); 1204 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1205 startContainer: abc, 1206 startOffset: 3, 1207 endContainer: p2, 1208 endOffset: 2, 1209 }); 1210 checkGetTargetRangesOfInputOnDeleteSomething(); 1211 }, 'Backspace at "<p>abc</p><p><span>{}</span><br></p>"'); 1212 1213 // If invisible white-spaces are removed with same action as above tests, 1214 // the range should be included in the target ranges. 1215 promise_test(async (t) => { 1216 initializeTest("<p>abc </p><p><br></p>"); 1217 let p1 = gEditor.querySelector("p"); 1218 let abc = p1.firstChild; 1219 let p2 = p1.nextSibling; 1220 gSelection.collapse(p2, 0); 1221 await sendBackspaceKey(); 1222 checkEditorContentResultAsSubTest( 1223 [ 1224 "<p>abc </p>", 1225 "<p>abc</p>", 1226 ], 1227 t.name 1228 ); 1229 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1230 startContainer: abc, 1231 startOffset: abc.length, 1232 endContainer: p2, 1233 endOffset: 1, 1234 }); 1235 checkGetTargetRangesOfInputOnDeleteSomething(); 1236 }, 'Backspace at "<p>abc </p><p>{}<br></p>"'); 1237 1238 // If the previous block ends with non-editable content, target range 1239 // should be after the non-editable content node. 1240 promise_test(async (t) => { 1241 initializeTest("<p>abc<span contenteditable=\"false\">def</span></p><p><br></p>"); 1242 let p1 = gEditor.querySelector("p"); 1243 let span = gEditor.querySelector("span"); 1244 let p2 = p1.nextSibling; 1245 gSelection.collapse(p2, 0); 1246 await sendBackspaceKey(); 1247 checkEditorContentResultAsSubTest( 1248 "<p>abc<span contenteditable=\"false\">def</span></p>", 1249 t.name 1250 ); 1251 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1252 startContainer: p1, 1253 startOffset: 2, 1254 endContainer: p2, 1255 endOffset: 1, 1256 }); 1257 checkGetTargetRangesOfInputOnDeleteSomething(); 1258 }, 'Backspace at "<p>abc<span contenteditable="false">def</span></p><p>{}<br></p>"'); 1259 1260 // If previous non-editable paragraph is deleted, target range should begin 1261 // with end of the text node in the first paragraph. Otherwise, start from 1262 // after the non-editable paragraph. 1263 promise_test(async (t) => { 1264 initializeTest("<p>abc</p><p contenteditable=\"false\">def</p><p><br></p>"); 1265 let p1 = gEditor.querySelector("p"); 1266 let abc = p1.firstChild; 1267 let p2 = p1.nextSibling; 1268 let p3 = p2.nextSibling; 1269 gSelection.collapse(p3, 0); 1270 await sendBackspaceKey(); 1271 checkEditorContentResultAsSubTest( 1272 [ 1273 "<p>abc</p>", 1274 "<p>abc</p><p contenteditable=\"false\">def</p>", 1275 ], 1276 t.name 1277 ); 1278 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1279 startContainer: p2.isConnected ? gEditor : abc, 1280 startOffset: p2.isConnected ? 2 : abc.length, 1281 endContainer: p3, 1282 endOffset: 1, 1283 }); 1284 checkGetTargetRangesOfInputOnDeleteSomething(); 1285 }, 'Backspace at "<p>abc</p><p contenteditable=\"false\">def</p><p>{}<br></p>"'); 1286 1287 promise_test(async (t) => { 1288 initializeTest("<p>abc<span contenteditable=\"false\">def</span>ghi</p>"); 1289 let p = gEditor.querySelector("p"); 1290 let ghi = p.lastChild; 1291 gSelection.collapse(ghi, 0); 1292 await sendBackspaceKey(); 1293 checkEditorContentResultAsSubTest( 1294 [ 1295 "<p>abc<span contenteditable=\"false\">def</span>ghi</p>", 1296 "<p>abcghi</p>", 1297 "<p>abcghi<br></p>", 1298 ], 1299 t.name 1300 ); 1301 if (gEditor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") { 1302 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1303 startContainer: ghi, 1304 startOffset: 0, 1305 endContainer: ghi, 1306 endOffset: 0, 1307 }); 1308 checkGetTargetRangesOfInputOnDoNothing(); 1309 } else { 1310 // If the non-editable `<span>` is deleted, it should be treated as 1311 // an atomic node. 1312 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1313 startContainer: p, 1314 startOffset: 1, 1315 endContainer: p, 1316 endOffset: 2, 1317 }); 1318 checkGetTargetRangesOfInputOnDeleteSomething(); 1319 } 1320 }, 'Backspace at "<p>abc<span contenteditable=\"false\">def</span>[]ghi</p>"'); 1321 1322 // If just removes the paragraph, target range should start from after the 1323 // table element. 1324 promise_test(async (t) => { 1325 initializeTest("<table><tr><td>cell</td></tr></table><p><br></p>"); 1326 let table = gEditor.querySelector("table"); 1327 let p = table.nextSibling; 1328 gSelection.collapse(p, 0); 1329 await sendBackspaceKey(); 1330 checkEditorContentResultAsSubTest( 1331 [ 1332 "<table><tbody><tr><td>cell</td></tr></tbody></table>", 1333 "<table><tbody><tr><td>cell</td></tr></tbody></table><p><br></p>", 1334 ], 1335 t.name 1336 ); 1337 if (p.isConnected) { 1338 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1339 startContainer: p, 1340 startOffset: 0, 1341 endContainer: p, 1342 endOffset: 0, 1343 }); 1344 checkGetTargetRangesOfInputOnDoNothing(); 1345 } else { 1346 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1347 startContainer: gEditor, 1348 startOffset: 1, 1349 endContainer: p, 1350 endOffset: 1, 1351 }); 1352 checkGetTargetRangesOfInputOnDeleteSomething(); 1353 } 1354 }, 'Backspace at "<table><tr><td>cell</td></tr></table><p>{}<br></p>"'); 1355 1356 // If table cell won't be joined, target range should be collapsed in the 1357 // cell. 1358 promise_test(async (t) => { 1359 initializeTest("<table><tr><td>cell1</td><td><br></td></tr></table>"); 1360 let cell1 = gEditor.querySelector("td"); 1361 let cell2 = cell1.nextSibling; 1362 gSelection.collapse(cell2, 0); 1363 await sendBackspaceKey(); 1364 assert_equals(gEditor.innerHTML, 1365 "<table><tbody><tr><td>cell1</td><td><br></td></tr></tbody></table>", 1366 t.name 1367 ); 1368 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1369 startContainer: cell2, 1370 startOffset: 0, 1371 endContainer: cell2, 1372 endOffset: 0, 1373 }); 1374 checkGetTargetRangesOfInputOnDoNothing(); 1375 }, 'Backspace at "<table><tr><td>cell1</td><td>{}<br></td></tr></table>"'); 1376 1377 // If table caption won't be deleted, target range should be collapsed in the 1378 // caption element. 1379 promise_test(async (t) => { 1380 initializeTest("<p>abc</p><table><caption><br></caption><tr><td>cell</td></tr></table>"); 1381 let caption = gEditor.querySelector("caption"); 1382 gSelection.collapse(caption, 0); 1383 await sendBackspaceKey(); 1384 assert_equals(gEditor.innerHTML, 1385 "<p>abc</p><table><caption><br></caption><tbody><tr><td>cell</td></tr></tbody></table>", 1386 t.name 1387 ); 1388 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1389 startContainer: caption, 1390 startOffset: 0, 1391 endContainer: caption, 1392 endOffset: 0, 1393 }); 1394 checkGetTargetRangesOfInputOnDoNothing(); 1395 }, 'Backspace at "<p>abc</p><table><caption>{}<br></caption><tr><td>cell</td></tr></table>"'); 1396 1397 // If a table cell element is selected, only its content should be deleted. 1398 promise_test(async (t) => { 1399 initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr></table>"); 1400 let cell1 = gEditor.querySelector("td"); 1401 let tr = cell1.parentNode; 1402 gSelection.setBaseAndExtent(tr, 0, tr, 1); 1403 await sendBackspaceKey(); 1404 checkEditorContentResultAsSubTest( 1405 [ 1406 "<table><tbody><tr><td></td><td>cell2</td></tr></tbody></table>", 1407 "<table><tbody><tr><td><br></td><td>cell2</td></tr></tbody></table>", 1408 ], 1409 t.name 1410 ); 1411 // XXX Perhaps, target range should be selecting only all children of 1412 // cell1 instead. 1413 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1414 startContainer: tr, 1415 startOffset: 0, 1416 endContainer: tr, 1417 endOffset: 1, 1418 }); 1419 checkGetTargetRangesOfInputOnDeleteSomething(); 1420 }, 'Backspace at "<table><tr>{<td>cell1</td>}<td>cell2</td></tr></table>"'); 1421 1422 promise_test(async (t) => { 1423 initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr></table>"); 1424 let cell2 = gEditor.querySelector("td + td"); 1425 let tr = cell2.parentNode; 1426 gSelection.setBaseAndExtent(tr, 1, tr, 2); 1427 await sendBackspaceKey(); 1428 checkEditorContentResultAsSubTest( 1429 [ 1430 "<table><tbody><tr><td>cell1</td><td></td></tr></tbody></table>", 1431 "<table><tbody><tr><td>cell1</td><td><br></td></tr></tbody></table>", 1432 ], 1433 t.name 1434 ); 1435 // XXX Perhaps, target range should be selecting only all children of 1436 // cell1 instead. 1437 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1438 startContainer: tr, 1439 startOffset: 1, 1440 endContainer: tr, 1441 endOffset: 2, 1442 }); 1443 checkGetTargetRangesOfInputOnDeleteSomething(); 1444 }, 'Backspace at "<table><tr><td>cell1</td>{<td>cell2</td>}</tr></table>"'); 1445 1446 // If the last table cell element is selected, what browsers should do? 1447 promise_test(async (t) => { 1448 initializeTest("<table><tr><td>cell</td></tr></table>"); 1449 let cell = gEditor.querySelector("td"); 1450 let tr = cell.parentNode; 1451 let table = gEditor.querySelector("table"); 1452 gSelection.setBaseAndExtent(tr, 0, tr, 1); 1453 await sendBackspaceKey(); 1454 checkEditorContentResultAsSubTest( 1455 [ 1456 "<table><tbody><tr><td></td></tr></tbody></table>", 1457 "<table><tbody><tr><td><br></td></tr></tbody></table>", 1458 "<br>", 1459 ], 1460 t.name 1461 ); 1462 if (gEditor.querySelector("table")) { 1463 // XXX Perhaps, target range should be selecting only all children of 1464 // cell1 instead. 1465 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1466 startContainer: tr, 1467 startOffset: 0, 1468 endContainer: tr, 1469 endOffset: 1, 1470 }); 1471 } else { 1472 // If it causes deleting entire the table, the `<table>` element should 1473 // be in the target range. 1474 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1475 startContainer: gEditor, 1476 startOffset: 0, 1477 endContainer: gEditor, 1478 endOffset: 1, 1479 }); 1480 } 1481 checkGetTargetRangesOfInputOnDeleteSomething(); 1482 }, 'Backspace at "<table><tr>{<td>cell</td>}</tr></table>"'); 1483 1484 // Testing multiple cell selection mode. 1485 promise_test(async (t) => { 1486 initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); 1487 let cell1 = gEditor.querySelector("td"); 1488 let cell4 = gEditor.querySelector("tr + tr > td + td"); 1489 let tr1 = cell1.parentNode; 1490 let tr2 = cell4.parentNode; 1491 gSelection.removeAllRanges(); 1492 let range = document.createRange(); 1493 range.selectNode(cell1); 1494 gSelection.addRange(range); 1495 range = document.createRange(); 1496 range.selectNode(cell4); 1497 gSelection.addRange(range); 1498 assert_equals(gSelection.rangeCount, 2, "Should support multiple cell selection"); 1499 await sendBackspaceKey(); 1500 checkEditorContentResultAsSubTest( 1501 [ 1502 "<table><tbody><tr><td></td><td>cell2</td></tr><tr><td>cell3</td><td></td></tr></tbody></table>", 1503 "<table><tbody><tr><td><br></td><td>cell2</td></tr><tr><td>cell3</td><td><br></td></tr></tbody></table>", 1504 ], 1505 t.name 1506 ); 1507 // XXX Perhaps, target range should be selecting only all children of 1508 // cell1 and cell4 instead. 1509 checkGetTargetRangesOfBeforeinputOnDeleteSomething([ 1510 { 1511 startContainer: tr1, 1512 startOffset: 0, 1513 endContainer: tr1, 1514 endOffset: 1, 1515 }, 1516 { 1517 startContainer: tr2, 1518 startOffset: 1, 1519 endContainer: tr2, 1520 endOffset: 2, 1521 }, 1522 ]); 1523 checkGetTargetRangesOfInputOnDeleteSomething(); 1524 }, 'Backspace at "<table><tr>{<td>cell1</td>}<td>cell2</td></tr><tr><td>cell3</td>{<td>cell4</td>}</tr></table>"'); 1525 1526 promise_test(async (t) => { 1527 initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); 1528 let cell1 = gEditor.querySelector("td"); 1529 let cell3 = gEditor.querySelector("tr + tr > td"); 1530 let tr1 = cell1.parentNode; 1531 let tr2 = cell3.parentNode; 1532 gSelection.removeAllRanges(); 1533 let range = document.createRange(); 1534 range.selectNode(cell1); 1535 gSelection.addRange(range); 1536 range = document.createRange(); 1537 range.selectNode(cell3); 1538 gSelection.addRange(range); 1539 assert_equals(gSelection.rangeCount, 2, "Should support multiple cell selection"); 1540 await sendBackspaceKey(); 1541 checkEditorContentResultAsSubTest( 1542 [ 1543 "<table><tbody><tr><td></td><td>cell2</td></tr><tr><td></td><td>cell4</td></tr></tbody></table>", 1544 "<table><tbody><tr><td><br></td><td>cell2</td></tr><tr><td><br></td><td>cell4</td></tr></tbody></table>", 1545 "<table><tbody><tr><td>cell2</td></tr><tr><td>cell4</td></tr></tbody></table>", 1546 ], 1547 t.name 1548 ); 1549 // XXX Perhaps, target range should be selecting only all children of 1550 // cell1 and cell3 instead. 1551 checkGetTargetRangesOfBeforeinputOnDeleteSomething([ 1552 { 1553 startContainer: tr1, 1554 startOffset: 0, 1555 endContainer: tr1, 1556 endOffset: 1, 1557 }, 1558 { 1559 startContainer: tr2, 1560 startOffset: 0, 1561 endContainer: tr2, 1562 endOffset: 1, 1563 }, 1564 ]); 1565 checkGetTargetRangesOfInputOnDeleteSomething(); 1566 }, 'Backspace at "<table><tr>{<td>cell1</td>}<td>cell2</td></tr><tr>{<td>cell3</td>}<td>cell4</td></tr></table>"'); 1567 1568 promise_test(async (t) => { 1569 initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); 1570 let cell1 = gEditor.querySelector("td"); 1571 let cell2 = gEditor.querySelector("td + td"); 1572 let tr1 = cell1.parentNode; 1573 let tbody = tr1.parentNode; 1574 gSelection.removeAllRanges(); 1575 let range = document.createRange(); 1576 range.selectNode(cell1); 1577 gSelection.addRange(range); 1578 range = document.createRange(); 1579 range.selectNode(cell2); 1580 gSelection.addRange(range); 1581 assert_equals(gSelection.rangeCount, 2, "Should support multiple cell selection"); 1582 await sendBackspaceKey(); 1583 checkEditorContentResultAsSubTest( 1584 [ 1585 "<table><tbody><tr><td></td><td></td></tr><tr><td>cell3</td><td>cell4</td></tr></tbody></table>", 1586 "<table><tbody><tr><td><br></td><td><br></td></tr><tr><td>cell3</td><td>cell4</td></tr></tbody></table>", 1587 "<table><tbody><tr><td>cell3</td><td>cell4</td></tr></tbody></table>", 1588 ], 1589 t.name 1590 ); 1591 if (gEditor.querySelector("tr + tr")) { 1592 // XXX Perhaps, target range should be selecting only all children of 1593 // cell1 and cell2 instead. 1594 checkGetTargetRangesOfBeforeinputOnDeleteSomething([ 1595 { 1596 startContainer: tr1, 1597 startOffset: 0, 1598 endContainer: tr1, 1599 endOffset: 1, 1600 }, 1601 { 1602 startContainer: tr1, 1603 startOffset: 1, 1604 endContainer: tr1, 1605 endOffset: 2, 1606 }, 1607 ]); 1608 } else { 1609 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1610 startContainer: tbody, 1611 startOffset: 0, 1612 endContainer: tbody, 1613 endOffset: 1, 1614 }); 1615 } 1616 checkGetTargetRangesOfInputOnDeleteSomething(); 1617 }, 'Backspace at "<table><tr>{<td>cell1</td>}{<td>cell2</td>}</tr><tr><td>cell3</td><td>cell4</td></tr></table>"'); 1618 1619 promise_test(async (t) => { 1620 initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); 1621 let cell3 = gEditor.querySelector("tr + tr > td"); 1622 let cell4 = gEditor.querySelector("tr + tr > td + td"); 1623 let tr2 = cell3.parentNode; 1624 gSelection.removeAllRanges(); 1625 let range = document.createRange(); 1626 range.selectNode(cell3); 1627 gSelection.addRange(range); 1628 range = document.createRange(); 1629 range.selectNode(cell4); 1630 gSelection.addRange(range); 1631 assert_equals(gSelection.rangeCount, 2, "Should support multiple cell selection"); 1632 await sendBackspaceKey(); 1633 checkEditorContentResultAsSubTest( 1634 [ 1635 "<table><tbody><tr><td>cell1</td><td>cell2</td></tr><tr><td></td><td></td></tr></tbody></table>", 1636 "<table><tbody><tr><td>cell1</td><td>cell2</td></tr><tr><td><br></td><td><br></td></tr></tbody></table>", 1637 "<table><tbody><tr><td>cell1</td><td>cell2</td></tr></tbody></table>", 1638 ], 1639 t.name 1640 ); 1641 if (gEditor.querySelector("tr + tr")) { 1642 // XXX Perhaps, target range should be selecting only all children of 1643 // cell3 and cell4 instead. 1644 checkGetTargetRangesOfBeforeinputOnDeleteSomething([ 1645 { 1646 startContainer: tr2, 1647 startOffset: 0, 1648 endContainer: tr2, 1649 endOffset: 1, 1650 }, 1651 { 1652 startContainer: tr2, 1653 startOffset: 1, 1654 endContainer: tr2, 1655 endOffset: 2, 1656 }, 1657 ]); 1658 } else { 1659 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1660 startContainer: tbody, 1661 startOffset: 1, 1662 endContainer: tbody, 1663 endOffset: 2, 1664 }); 1665 } 1666 checkGetTargetRangesOfInputOnDeleteSomething(); 1667 }, 'Backspace at "<table><tr><td>cell1</td><td>cell2</td></tr><tr>{<td>cell3</td>}{<td>cell4</td>}</tr></table>"'); 1668 1669 promise_test(async (t) => { 1670 initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); 1671 let cell1 = gEditor.querySelector("td"); 1672 let cell2 = gEditor.querySelector("td + td"); 1673 let cell3 = gEditor.querySelector("tr + tr > td"); 1674 let cell4 = gEditor.querySelector("tr + tr > td + td"); 1675 let tr1 = cell1.parentNode; 1676 let tr2 = cell3.parentNode; 1677 gSelection.removeAllRanges(); 1678 let range = document.createRange(); 1679 range.selectNode(cell1); 1680 gSelection.addRange(range); 1681 range = document.createRange(); 1682 range.selectNode(cell2); 1683 gSelection.addRange(range); 1684 range = document.createRange(); 1685 range.selectNode(cell3); 1686 gSelection.addRange(range); 1687 range = document.createRange(); 1688 range.selectNode(cell4); 1689 gSelection.addRange(range); 1690 assert_equals(gSelection.rangeCount, 4, "Should support multiple cell selection"); 1691 await sendBackspaceKey(); 1692 checkEditorContentResultAsSubTest( 1693 [ 1694 "<table><tbody><tr><td></td><td></td></tr><tr><td></td><td></td></tr></tbody></table>", 1695 "<table><tbody><tr><td><br></td><td><br></td></tr><tr><td><br></td><td><br></td></tr></tbody></table>", 1696 "<br>", 1697 ], 1698 t.name 1699 ); 1700 if (gEditor.querySelector("table")) { 1701 // XXX Perhaps, target range should be selecting only all children of 1702 // cell1, cell2, cell3 and cell4 instead. 1703 checkGetTargetRangesOfBeforeinputOnDeleteSomething([ 1704 { 1705 startContainer: tr1, 1706 startOffset: 0, 1707 endContainer: tr1, 1708 endOffset: 1, 1709 }, 1710 { 1711 startContainer: tr1, 1712 startOffset: 1, 1713 endContainer: tr1, 1714 endOffset: 2, 1715 }, 1716 { 1717 startContainer: tr2, 1718 startOffset: 0, 1719 endContainer: tr2, 1720 endOffset: 1, 1721 }, 1722 { 1723 startContainer: tr2, 1724 startOffset: 1, 1725 endContainer: tr2, 1726 endOffset: 2, 1727 }, 1728 ]); 1729 } else { 1730 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1731 startContainer: gEditor, 1732 startOffset: 0, 1733 endContainer: gEditor, 1734 endOffset: 1, 1735 }); 1736 } 1737 checkGetTargetRangesOfInputOnDeleteSomething(); 1738 }, 'Backspace at "<table><tr>{<td>cell1</td>}{<td>cell2</td>}</tr><tr>{<td>cell3</td>}{<td>cell4</td>}</tr></table>"'); 1739 1740 promise_test(async (t) => { 1741 initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); 1742 let cell1 = gEditor.querySelector("td"); 1743 let cell2 = gEditor.querySelector("td + td"); 1744 let cell4 = gEditor.querySelector("tr + tr > td + td"); 1745 let tr1 = cell1.parentNode; 1746 let tr2 = cell4.parentNode; 1747 gSelection.removeAllRanges(); 1748 let range = document.createRange(); 1749 range.selectNode(cell1); 1750 gSelection.addRange(range); 1751 range = document.createRange(); 1752 range.setStart(cell2.firstChild, 1); 1753 range.setEnd(cell2.firstChild, 4); 1754 gSelection.addRange(range); 1755 range = document.createRange(); 1756 range.selectNode(cell4); 1757 gSelection.addRange(range); 1758 assert_equals(gSelection.rangeCount, 3, "Should support multiple cell selection"); 1759 await sendBackspaceKey(); 1760 checkEditorContentResultAsSubTest( 1761 [ 1762 "<table><tbody><tr><td></td><td>cell2</td></tr><tr><td>cell3</td><td></td></tr></tbody></table>", 1763 "<table><tbody><tr><td><br></td><td>cell2</td></tr><tr><td>cell3</td><td><br></td></tr></tbody></table>", 1764 "<table><tbody><tr><td></td><td>c2</td></tr><tr><td>cell3</td><td></td></tr></tbody></table>", 1765 "<table><tbody><tr><td><br></td><td>c2</td></tr><tr><td>cell3</td><td><br></td></tr></tbody></table>", 1766 ], 1767 t.name 1768 ); 1769 if (cell2.firstChild.length == "cell2".length) { 1770 // XXX Perhaps, target range should be selecting only all children of 1771 // cell1 and cell4 instead. 1772 checkGetTargetRangesOfBeforeinputOnDeleteSomething([ 1773 { 1774 startContainer: tr1, 1775 startOffset: 0, 1776 endContainer: tr1, 1777 endOffset: 1, 1778 }, 1779 { 1780 startContainer: tr2, 1781 startOffset: 1, 1782 endContainer: tr2, 1783 endOffset: 2, 1784 }, 1785 ]); 1786 } else { 1787 checkGetTargetRangesOfBeforeinputOnDeleteSomething([ 1788 { 1789 startContainer: tr1, 1790 startOffset: 0, 1791 endContainer: tr1, 1792 endOffset: 1, 1793 }, 1794 { 1795 startContainer: cell2.firstChild, 1796 startOffset: 1, 1797 endContainer: cell2.firstChild, 1798 endOffset: 4, 1799 }, 1800 { 1801 startContainer: tr2, 1802 startOffset: 1, 1803 endContainer: tr2, 1804 endOffset: 2, 1805 }, 1806 ]); 1807 } 1808 checkGetTargetRangesOfInputOnDeleteSomething(); 1809 }, 'Backspace at "<table><tr>{<td>cell1</td>}<td>c[ell]2</td></tr><tr>{<td>cell3</td>}<td>cell4</td></tr></table>"'); 1810 1811 // If caret is not adjacent of deleting character, browser may not delete the 1812 // character, but update the caret position for next deletion. 1813 promise_test(async (t) => { 1814 initializeTest("<p>שלוםhello</p>"); 1815 let text1 = gEditor.querySelector("p").firstChild; 1816 let text2 = text1.nextSibling; 1817 gSelection.collapse(text2 ? text2 : text1, text2 ? 1 : 5); 1818 await sendArrowLeftKey(); 1819 await sendBackspaceKey(); 1820 checkEditorContentResultAsSubTest( 1821 [ 1822 "<p>\u05E9\u05DC\u05D5\u05DDhello</p>", 1823 "<p>\u05DC\u05D5\u05DDhello</p>", 1824 "<p>\u05E9\u05DC\u05D5hello</p>", 1825 ], 1826 t.name 1827 ); 1828 if (gEditor.innerHTML === "<p>\u05E9\u05DC\u05D5\u05DDhello</p>") { 1829 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1830 startContainer: text2 ? text2 : text1, 1831 startOffset: text2 ? 0 : 4, 1832 endContainer: text2 ? text2 : text1, 1833 endOffset: text2 ? 0 : 4, 1834 }); 1835 checkGetTargetRangesOfInputOnDoNothing(); 1836 } else if (gEditor.innerHTML === "<p>\u05DC\u05D5\u05DDhello</p>") { 1837 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1838 startContainer: text1, 1839 startOffset: 0, 1840 endContainer: text1, 1841 endOffset: 1, 1842 }); 1843 checkGetTargetRangesOfInputOnDeleteSomething(); 1844 } else { 1845 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1846 startContainer: text1, 1847 startOffset: 3, 1848 endContainer: text1, 1849 endOffset: 4, 1850 }); 1851 checkGetTargetRangesOfInputOnDeleteSomething(); 1852 } 1853 }, 'Backspace at "<p>שלום[]hello</p>"'); 1854 1855 // The following tests check whether the range returned from 1856 // `beforeinput[0].getTargetRanges()` is modified or different range is 1857 // modified instead. I.e., they don't test which type of deletion should 1858 // occur. Therefore, their result depends on browser's key bindings, 1859 // system settings and running OS. 1860 1861 function getFirstDifferentOffset(currentString, originalString) { 1862 for (let i = 0; i < currentString.length; i++) { 1863 if (currentString.charAt(i) !== originalString.charAt(i) && 1864 (originalString.charAt(i) !== " " || !currentString.charAt("\u00A0"))) { 1865 return i; 1866 } 1867 } 1868 return currentString.length; 1869 } 1870 1871 promise_test(async (t) => { 1872 const kText = "abc def ghi"; 1873 initializeTest(`<p>${kText}</p>`); 1874 let p = gEditor.querySelector("p"); 1875 gSelection.collapse(p.firstChild, "abc def".length); 1876 await sendBackspaceKey(kShift); 1877 let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); 1878 let length = kText.length - p.firstChild.data.length; 1879 checkEditorContentResultAsSubTest( 1880 `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, 1881 t.name, 1882 { ignoreWhiteSpaceDifference: true } 1883 ); 1884 if (startOffset === kText.length) { 1885 checkBeforeinputAndInputEventsOnNOOP(); 1886 return; 1887 } 1888 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1889 startContainer: p.firstChild, 1890 startOffset: startOffset, 1891 endContainer: p.firstChild, 1892 endOffset: startOffset + length, 1893 }); 1894 checkGetTargetRangesOfInputOnDeleteSomething(); 1895 }, 'Shift + Backspace at "<p>abc def[] ghi</p>"'); 1896 1897 promise_test(async (t) => { 1898 const kText = "abc def ghi"; 1899 initializeTest(`<p>${kText}</p>`); 1900 let p = gEditor.querySelector("p"); 1901 gSelection.collapse(p.firstChild, "abc def".length); 1902 await sendBackspaceKey(kControl); 1903 let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); 1904 let length = kText.length - p.firstChild.data.length; 1905 checkEditorContentResultAsSubTest( 1906 `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, 1907 t.name, 1908 { ignoreWhiteSpaceDifference: true } 1909 ); 1910 if (startOffset === kText.length) { 1911 checkBeforeinputAndInputEventsOnNOOP(); 1912 return; 1913 } 1914 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1915 startContainer: p.firstChild, 1916 startOffset: startOffset, 1917 endContainer: p.firstChild, 1918 endOffset: startOffset + length, 1919 }); 1920 checkGetTargetRangesOfInputOnDeleteSomething(); 1921 }, 'Control + Backspace at "<p>abc def[] ghi</p>"'); 1922 1923 promise_test(async (t) => { 1924 const kText = "abc def ghi"; 1925 initializeTest(`<p>${kText}</p>`); 1926 let p = gEditor.querySelector("p"); 1927 gSelection.collapse(p.firstChild, "abc def".length); 1928 await sendBackspaceKey(kAlt); 1929 let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); 1930 let length = kText.length - p.firstChild.data.length; 1931 checkEditorContentResultAsSubTest( 1932 `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, 1933 t.name, 1934 { ignoreWhiteSpaceDifference: true } 1935 ); 1936 if (startOffset === kText.length) { 1937 checkBeforeinputAndInputEventsOnNOOP(); 1938 return; 1939 } 1940 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1941 startContainer: p.firstChild, 1942 startOffset: startOffset, 1943 endContainer: p.firstChild, 1944 endOffset: startOffset + length, 1945 }); 1946 checkGetTargetRangesOfInputOnDeleteSomething(); 1947 }, 'Alt + Backspace at "<p>abc def[] ghi</p>"'); 1948 1949 promise_test(async (t) => { 1950 const kText = "abc def ghi"; 1951 initializeTest(`<p>${kText}</p>`); 1952 let p = gEditor.querySelector("p"); 1953 gSelection.collapse(p.firstChild, "abc def".length); 1954 await sendBackspaceKey(kMeta); 1955 let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); 1956 let length = kText.length - p.firstChild.data.length; 1957 checkEditorContentResultAsSubTest( 1958 `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, 1959 t.name, 1960 { ignoreWhiteSpaceDifference: true } 1961 ); 1962 if (startOffset === kText.length) { 1963 checkBeforeinputAndInputEventsOnNOOP(); 1964 return; 1965 } 1966 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1967 startContainer: p.firstChild, 1968 startOffset: startOffset, 1969 endContainer: p.firstChild, 1970 endOffset: startOffset + length, 1971 }); 1972 checkGetTargetRangesOfInputOnDeleteSomething(); 1973 }, 'Meta + Backspace at "<p>abc def[] ghi</p>"'); 1974 1975 promise_test(async (t) => { 1976 const kText = "abc def"; 1977 initializeTest(`<p> ${kText}</p>`); 1978 let p = gEditor.querySelector("p"); 1979 gSelection.collapse(p.firstChild, "abc".length); 1980 await sendBackspaceKey(kShift); 1981 let visibleText = p.firstChild.data.replace(/^\s+/, ""); 1982 let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); 1983 let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); 1984 let length = kText.length + 3 - p.firstChild.data.length; 1985 // If invisible white-spaces are deleted, they should be contained in the target range. 1986 checkEditorContentResultAsSubTest( 1987 `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, 1988 t.name, 1989 { ignoreWhiteSpaceDifference: true } 1990 ); 1991 if (startOffset === kText.length) { 1992 checkBeforeinputAndInputEventsOnNOOP(); 1993 return; 1994 } 1995 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 1996 startContainer: p.firstChild, 1997 startOffset: startOffset, 1998 endContainer: p.firstChild, 1999 endOffset: startOffset + length, 2000 }); 2001 checkGetTargetRangesOfInputOnDeleteSomething(); 2002 }, 'Shift + Backspace at "<p> abc[] def</p>"'); 2003 2004 promise_test(async (t) => { 2005 const kText = "abc def"; 2006 initializeTest(`<p> ${kText}</p>`); 2007 let p = gEditor.querySelector("p"); 2008 gSelection.collapse(p.firstChild, "abc".length); 2009 await sendBackspaceKey(kControl); 2010 let visibleText = p.firstChild.data.replace(/^\s+/, ""); 2011 let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); 2012 let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); 2013 let length = kText.length + 3 - p.firstChild.data.length; 2014 // If invisible white-spaces are deleted, they should be contained in the target range. 2015 checkEditorContentResultAsSubTest( 2016 `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, 2017 t.name, 2018 { ignoreWhiteSpaceDifference: true } 2019 ); 2020 if (startOffset === kText.length) { 2021 checkBeforeinputAndInputEventsOnNOOP(); 2022 return; 2023 } 2024 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 2025 startContainer: p.firstChild, 2026 startOffset: startOffset, 2027 endContainer: p.firstChild, 2028 endOffset: startOffset + length, 2029 }); 2030 checkGetTargetRangesOfInputOnDeleteSomething(); 2031 }, 'Control + Backspace at "<p> abc[] def</p>"'); 2032 2033 promise_test(async (t) => { 2034 const kText = "abc def"; 2035 initializeTest(`<p> ${kText}</p>`); 2036 let p = gEditor.querySelector("p"); 2037 gSelection.collapse(p.firstChild, "abc".length); 2038 await sendBackspaceKey(kAlt); 2039 let visibleText = p.firstChild.data.replace(/^\s+/, ""); 2040 let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); 2041 let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); 2042 let length = kText.length + 3 - p.firstChild.data.length; 2043 // If invisible white-spaces are deleted, they should be contained in the target range. 2044 checkEditorContentResultAsSubTest( 2045 `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, 2046 t.name, 2047 { ignoreWhiteSpaceDifference: true } 2048 ); 2049 if (startOffset === kText.length) { 2050 checkBeforeinputAndInputEventsOnNOOP(); 2051 return; 2052 } 2053 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 2054 startContainer: p.firstChild, 2055 startOffset: startOffset, 2056 endContainer: p.firstChild, 2057 endOffset: startOffset + length, 2058 }); 2059 checkGetTargetRangesOfInputOnDeleteSomething(); 2060 }, 'Alt + Backspace at "<p> abc[] def</p>"'); 2061 2062 promise_test(async (t) => { 2063 const kText = "abc def"; 2064 initializeTest(`<p> ${kText}</p>`); 2065 let p = gEditor.querySelector("p"); 2066 gSelection.collapse(p.firstChild, "abc".length); 2067 await sendBackspaceKey(kMeta); 2068 let visibleText = p.firstChild.data.replace(/^\s+/, ""); 2069 let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); 2070 let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); 2071 let length = kText.length + 3 - p.firstChild.data.length; 2072 // If invisible white-spaces are deleted, they should be contained in the target range. 2073 checkEditorContentResultAsSubTest( 2074 `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, 2075 t.name 2076 ); 2077 if (startOffset === kText.length) { 2078 checkBeforeinputAndInputEventsOnNOOP(); 2079 return; 2080 } 2081 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 2082 startContainer: p.firstChild, 2083 startOffset: startOffset, 2084 endContainer: p.firstChild, 2085 endOffset: startOffset + length, 2086 }); 2087 checkGetTargetRangesOfInputOnDeleteSomething(); 2088 }, 'Meta + Backspace at "<p> abc[] def</p>"'); 2089 2090 // If editing host is nested, editing in the nested one shouldn't cause 2091 // target ranges extended to outside of it. 2092 promise_test(async t => { 2093 const innerHTML = "<div contenteditable=\"false\"><div contenteditable=\"\"><p><br></p></div></div>"; 2094 initializeTest(innerHTML); 2095 const p = gEditor.querySelector("p"); 2096 const innerEditingHost = p.parentNode; 2097 document.activeElement?.blur(); 2098 p.parentNode.focus(); 2099 gSelection.collapse(p, 0); 2100 await sendBackspaceKey(); 2101 if (gEditor.innerHTML == innerHTML) { 2102 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 2103 startContainer: p, 2104 startOffset: 0, 2105 endContainer: p, 2106 endOffset: 0, 2107 }); 2108 checkGetTargetRangesOfInputOnDoNothing(); 2109 } else { 2110 checkEditorContentResultAsSubTest( 2111 "<div contenteditable=\"false\"><div contenteditable=\"\"><br></div></div>", 2112 t.name 2113 ); 2114 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 2115 startContainer: innerEditingHost, 2116 startOffset: 0, 2117 endContainer: p, 2118 endOffset: 1, 2119 }); 2120 checkGetTargetRangesOfInputOnDeleteSomething(); 2121 } 2122 }, 'Backspace at "<div contenteditable="false"><div contenteditable=""><p>{}<br></p></div></div>'); 2123 2124 promise_test(async t => { 2125 const innerHTML = "<div contenteditable=\"false\">\n <div contenteditable=\"\"><p><br></p></div></div>"; 2126 initializeTest(innerHTML); 2127 const p = gEditor.querySelector("p"); 2128 const innerEditingHost = p.parentNode; 2129 document.activeElement?.blur(); 2130 p.parentNode.focus(); 2131 gSelection.collapse(p, 0); 2132 await sendBackspaceKey(); 2133 if (gEditor.innerHTML == innerHTML) { 2134 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 2135 startContainer: p, 2136 startOffset: 0, 2137 endContainer: p, 2138 endOffset: 0, 2139 }); 2140 checkGetTargetRangesOfInputOnDoNothing(); 2141 } else { 2142 checkEditorContentResultAsSubTest( 2143 "<div contenteditable=\"false\">\n <div contenteditable=\"\"><br></div></div>", 2144 t.name 2145 ); 2146 checkGetTargetRangesOfBeforeinputOnDeleteSomething({ 2147 startContainer: innerEditingHost, 2148 startOffset: 0, 2149 endContainer: p, 2150 endOffset: 1, 2151 }); 2152 checkGetTargetRangesOfInputOnDeleteSomething(); 2153 } 2154 }, 'Backspace at "<div contenteditable="false">\n <div contenteditable=""><p>{}<br></p></div></div>'); 2155 2156 </script>