diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/input-events/input-events-get-target-ranges-backspace.tentative.html | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/input-events/input-events-get-target-ranges-backspace.tentative.html')
-rw-r--r-- | testing/web-platform/tests/input-events/input-events-get-target-ranges-backspace.tentative.html | 2065 |
1 files changed, 2065 insertions, 0 deletions
diff --git a/testing/web-platform/tests/input-events/input-events-get-target-ranges-backspace.tentative.html b/testing/web-platform/tests/input-events/input-events-get-target-ranges-backspace.tentative.html new file mode 100644 index 0000000000..347ac61c3e --- /dev/null +++ b/testing/web-platform/tests/input-events/input-events-get-target-ranges-backspace.tentative.html @@ -0,0 +1,2065 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="timeout" content="long"> +<title>InputEvent.getTargetRanges() at Backspace</title> +<div contenteditable></div> +<script src="input-events-get-target-ranges.js"></script> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script> +"use strict"; + +promise_test(async (t) => { + initializeTest("<p>abc</p>"); + gSelection.collapse(gEditor.firstChild.firstChild, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abc</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor.firstChild.firstChild, + startOffset: 0, + endContainer: gEditor.firstChild.firstChild, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDoNothing(); +}, 'Backspace at "<p>[]abc</p>"'); + +// Simply deletes the previous ASCII character of caret position. +promise_test(async (t) => { + initializeTest("<p>abc</p>"); + gSelection.collapse(gEditor.firstChild.firstChild, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>bc</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor.firstChild.firstChild, + startOffset: 0, + endContainer: gEditor.firstChild.firstChild, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>a[]bc</p>"'); + +// Simply deletes the previous ASCII character of caret position. +promise_test(async (t) => { + initializeTest("<p>abc</p>"); + gSelection.collapse(gEditor.firstChild.firstChild, 2); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>ac</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor.firstChild.firstChild, + startOffset: 1, + endContainer: gEditor.firstChild.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>ab[]c</p>"'); + +// Simply deletes the previous ASCII character of caret position. +promise_test(async (t) => { + initializeTest("<p>abc</p>"); + gSelection.collapse(gEditor.firstChild.firstChild, 3); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>ab</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor.firstChild.firstChild, + startOffset: 2, + endContainer: gEditor.firstChild.firstChild, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc[]</p>"'); + +// Should delete the `<span>` element because it becomes empty. +// However, we need discussion whether the `<span>` element should be +// contained by a range of `getTargetRanges()`. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>a<span>b</span>c</p>"); + let c = gEditor.querySelector("span").nextSibling; + gSelection.collapse(c, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>ac</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor.firstChild, + startOffset: 1, + endContainer: c, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>a<span>b</span>[]c</p>"'); + +// Should delete the `<span>` element because it becomes empty. +// However, we need discussion whether the `<span>` element should be +// contained by a range of `getTargetRanges()`. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>a<span>b</span>c</p>"); + let b = gEditor.querySelector("span").firstChild; + gSelection.collapse(b, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>ac</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor.firstChild, + startOffset: 1, + endContainer: gEditor.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>a<span>b[]</span>c</p>"'); + +// Invisible leading white-space may be deleted when the first visible +// character is deleted. If it's deleted, it should be contained by +// the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p> abc</p>"); + gSelection.collapse(gEditor.firstChild.firstChild, 2); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<p>bc</p>", + "<p> bc</p>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor.firstChild.firstChild, + startOffset: gEditor.firstChild.firstChild.length == 2 ? 0 : 1, + endContainer: gEditor.firstChild.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p> a[]bc</p>"'); + +promise_test(async (t) => { + initializeTest("<p>abc</p><p>def</p>"); + let p1 = gEditor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc</p><p>[]def</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>abc </p><p> def</p>"); + let p1 = gEditor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + gSelection.collapse(def, 3); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc </p><p> []def</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>abc </p><p> def</p>"); + let p1 = gEditor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + gSelection.collapse(def, 2); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc </p><p> [] def</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>abc </p><p> def</p>"); + let p1 = gEditor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + gSelection.collapse(def, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc </p><p> [] def</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>abc </p><p> def</p>"); + let p1 = gEditor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc </p><p>[] def</p>"'); + +promise_test(async (t) => { + initializeTest("<p>abc</p><p><b>def</b></p>"); + let abc = gEditor.querySelector("p").firstChild; + let def = gEditor.querySelector("b").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abc<b>def</b></p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc</p><p><b>[]def</b></p>"'); + +promise_test(async (t) => { + initializeTest("<p><b>abc</b></p><p><b>def</b></p>"); + let abc = gEditor.querySelector("p > b").firstChild; + let def = gEditor.querySelector("P + p > b").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<p><b>abc</b><b>def</b></p>", + "<p><b>abcdef</b></p>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p><b>abc</b></p><p><b>[]def</b></p>"'); + +promise_test(async (t) => { + initializeTest("<p><i>abc</i></p><p><b>def</b></p>"); + let abc = gEditor.querySelector("i").firstChild; + let def = gEditor.querySelector("b").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p><i>abc</i><b>def</b></p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p><i>abc</i></p><p><b>[]def</b></p>"'); + +// Invisible leading white-spaces in the current block should be deleted +// for avoiding they becoming visible when the blocks are joined, but +// preformatted trailing white-spaces in the first block shouldn't be +// deleted. Perhaps, the invisible white-spaces should be contained by +// the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<pre>abc </pre><p> def</p>"); + let pre = gEditor.firstChild; + let abc = pre.firstChild; + let p = pre.nextSibling; + let def = p.firstChild; + gSelection.collapse(def, 3); + await sendBackspaceKey(); + // https://github.com/w3c/input-events/issues/112 + // Shouldn't make the invisible white-spaces visible. + checkEditorContentResultAsSubTest("<pre>abc def</pre>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 6, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<pre>abc </pre><p> []def</p>"'); + +// Invisible leading/trailing white-spaces in the current block should be +// deleted for avoiding they becoming visible when the blocks are joined, but +// preformatted trailing white-spaces in the first block shouldn't be +// deleted. Perhaps, the invisible leading white-spaces should be contained +// by the range of `getTargetRanges()`, but needs discussion. +// And also not sure whether the trailing white-spaces should be contained +// by additional range of `getTargetRanges()` or not because of the +// implementation cost and runtime cost. Needs discuss. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<pre>abc </pre><p> def </p>"); + let pre = gEditor.firstChild; + let abc = pre.firstChild; + let p = pre.nextSibling; + let def = p.firstChild; + gSelection.collapse(def, 3); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<pre>abc def </pre>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 6, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<pre>abc </pre><p> []def </p>"'); + +// Invisible trailing white-spaces in the first block should be deleted +// when the block is joined with the preformatted following block, but +// the leading white-spaces in the preformatted block shouldn't be +// removed. So, in this case, the invisible trailing white-spaces should +// be in the range of `getTargetRanges()`, but not so for the preformatted +// visible leading white-spaces. But needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>abc </p><pre> def</pre>"); + let p = gEditor.firstChild; + let abc = p.firstChild; + let pre = p.nextSibling; + let def = pre.firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<p>abc def</p>", + "<p>abc def</p>", + "<p>abc def</p>", + "<p>abc def</p>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 6, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc </p><pre>[] def</pre>"'); + +promise_test(async (t) => { + initializeTest('<p style="white-space:pre-line">abc\ndef</p>'); + const p = gEditor.firstChild; + const text = p.firstChild; + gSelection.collapse(text, "abc\n".length); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + '<p style="white-space:pre-line">abcdef</p>', + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: text, + startOffset: "abc".length, + endContainer: text, + endOffset: "abc\n".length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p style="white-space:pre-line">abc\\n[]def</p>"'); + +promise_test(async (t) => { + initializeTest('<p style="white-space:pre-line">abc \ndef</p>'); + const p = gEditor.firstChild; + const text = p.firstChild; + gSelection.collapse(text, "abc \n".length); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + '<p style="white-space:pre-line">abcdef</p>', + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: text, + startOffset: "abc".length, + endContainer: text, + endOffset: "abc \n".length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p style="white-space:pre-line">abc \\n[]def</p>"'); + +promise_test(async (t) => { + initializeTest('<p style="white-space:pre-line">abc\n def</p>'); + const p = gEditor.firstChild; + const text = p.firstChild; + gSelection.collapse(text, "abc\n ".length); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + '<p style="white-space:pre-line">abcdef</p>', + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: text, + startOffset: "abc".length, + endContainer: text, + endOffset: "abc\n ".length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p style="white-space:pre-line">abc\\n []def</p>"'); + +promise_test(async (t) => { + initializeTest('<p style="white-space:pre-line">abc \n def</p>'); + const p = gEditor.firstChild; + const text = p.firstChild; + gSelection.collapse(text, "abc \n ".length); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + '<p style="white-space:pre-line">abcdef</p>', + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: text, + startOffset: "abc".length, + endContainer: text, + endOffset: "abc \n ".length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p style="white-space:pre-line">abc \\n []def</p>"'); + +promise_test(async (t) => { + initializeTest('<p style="white-space:pre-line">abc \n \n def</p>'); + const p = gEditor.firstChild; + const text = p.firstChild; + gSelection.collapse(text, "abc \n \n ".length); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + '<p style="white-space:pre-line">abc \ndef</p>', + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: text, + startOffset: "abc \n".length, + endContainer: text, + endOffset: "abc \n \n ".length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p style="white-space:pre-line">abc \\n \\n []def</p>"'); + +// If the first block has invisible `<br>` element and joining it with +// the following block, the invisible trailing `<br>` element should be +// deleted and join the blocks. Therefore, the target range should contain +// the `<br>` element and block boundaries. But maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>abc<br></p><p>def</p>"); + let p1 = gEditor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p1, + startOffset: 1, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<br></p><p>[]def</p>"'); + +// If the first block has invisible `<br>` element for empty last line and +// joining it with the following block, the invisible trailing `<br>` element +// should be deleted and join the blocks. Therefore, the target range should +// contain the `<br>` element and block boundaries. But maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>abc<br><br></p><p>def</p>"); + let p1 = gEditor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abc<br>def</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p1, + startOffset: 2, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<br><br></p><p>[]def</p>"'); + +// Deleting visible `<br>` element should be contained by a range of +// `getTargetRanges()`. +promise_test(async (t) => { + initializeTest("<p>abc<br>def</p>"); + let p = document.querySelector("p"); + let def = gEditor.querySelector("br").nextSibling; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: p, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<br>[]def</p>"'); + +// Deleting visible `<br>` element following white-space should not include +// the preceding white-space in the range. +promise_test(async (t) => { + initializeTest("<p>abc <br>def</p>"); + let p = gEditor.querySelector("p"); + let def = gEditor.querySelector("br").nextSibling; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abc def</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: p, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc <br>[]def</p>"'); + +promise_test(async (t) => { + initializeTest(`<p>abc<img src="${kImgSrc}">def</p>`); + let p = gEditor.querySelector("p"); + let def = p.lastChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: p, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<img>[]def</p>"'); + +// White-spaces around `<img>` element are visible so that they shouldn't +// be included into the target ranges. +promise_test(async (t) => { + initializeTest(`<p>abc <img src="${kImgSrc}">def</p>`); + let p = gEditor.querySelector("p"); + let def = p.lastChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abc def</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: p, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc <img>[]def</p>"'); + +// White-spaces around `<img>` element are visible so that they shouldn't +// be included into the target ranges. +promise_test(async (t) => { + initializeTest(`<p>abc<img src="${kImgSrc}"> def</p>`); + let p = gEditor.querySelector("p"); + let def = p.lastChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abc def</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: p, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<img>[] def</p>"'); + +promise_test(async (t) => { + initializeTest(`<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`); + let p = gEditor.querySelector("p"); + let abc = p.firstChild; + gSelection.collapse(p, 2); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + `<p>abc<img src="${kImgSrc}">def</p>`, + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: p, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<img>{}<img>def</p>"'); + +promise_test(async (t) => { + initializeTest(`<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`); + let p = gEditor.querySelector("p"); + let def = p.lastChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + `<p>abc<img src="${kImgSrc}">def</p>`, + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 2, + endContainer: p, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<img><img>[]def</p>"'); + +promise_test(async (t) => { + initializeTest(`<div>abc<hr>def</div>`); + let div = gEditor.querySelector("div"); + let hr = gEditor.querySelector("hr"); + let def = hr.nextSibling; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div>abcdef</div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: div, + startOffset: 1, + endContainer: div, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<hr>[]def</div>"'); + +// White-spaces around block element are invisible white-spaces so that +// they should be included into the target ranges to avoid they bcome +// visible. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest(`<div>abc <hr>def</div>`); + let div = gEditor.querySelector("div"); + let abc = div.firstChild; + let hr = gEditor.querySelector("hr"); + let def = hr.nextSibling; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div>abcdef</div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: div, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc <hr>[]def</div>"'); + +// White-spaces around block element are invisible white-spaces so that +// they should be included into the target ranges to avoid they bcome +// visible. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest(`<div>abc<hr> def</div>`); + let div = gEditor.querySelector("div"); + let hr = gEditor.querySelector("hr"); + let def = hr.nextSibling; + gSelection.collapse(def, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div>abcdef</div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: div, + startOffset: 1, + endContainer: def, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<hr> []def</div>"'); + +// Invisible `<br>` element immediately before `<hr>` element should be +// deleted once, and both of them should be included in the target range. +promise_test(async (t) => { + initializeTest(`<div>abc<br><hr>def</div>`); + let div = gEditor.querySelector("div"); + let def = div.lastChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div>abcdef</div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: div, + startOffset: 1, + endContainer: div, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<br><hr>[]def</div>"'); + +// Deleting visible `<br>` element followed by white-space should include +// the following white-space in the range because it shouldn't become +// visible and should be deleted for avoiding it. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<p>abc<br> def</p>"); + let p = gEditor.querySelector("p"); + let def = gEditor.querySelector("br").nextSibling; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: def, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<br>[] def</p>"'); +promise_test(async (t) => { + initializeTest("<p>abc<br> def</p>"); + let p = gEditor.querySelector("p"); + let def = gEditor.querySelector("br").nextSibling; + gSelection.collapse(def, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abcdef</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: def, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<br> []def</p>"'); + +promise_test(async (t) => { + initializeTest("<div>abc<p>def<br>ghi</p></div>"); + let p = gEditor.querySelector("p"); + let def = p.firstChild; + let abc = gEditor.firstChild.firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<div>abcdef<p>ghi</p></div>", + "<div>abcdef<br><p>ghi</p></div>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<p>[]def<br>ghi</p></div>"'); + +// Joining parent block and child block should remove invisible preceding +// white-spaces of the child block and invisible leading white-spaces in +// the child block, and they should be contained in a range of +// `getTargetRanges()`, but maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<div>abc <p> def<br>ghi</p></div>"); + let p = gEditor.querySelector("p"); + let def = p.firstChild; + let abc = gEditor.firstChild.firstChild; + gSelection.collapse(def, 3); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<div>abcdef<p>ghi</p></div>", + "<div>abcdef<br><p>ghi</p></div>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc <p> []def<br>ghi</p></div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc<p><b>def</b></p></div>"); + let abc = gEditor.querySelector("div").firstChild; + let def = gEditor.querySelector("b").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div>abc<b>def</b></div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<p><b>[]def</b></p></div>"'); + +promise_test(async (t) => { + initializeTest("<div><b>abc</b><p><b>def</b></p></div>"); + let abc = gEditor.querySelector("b").firstChild; + let def = gEditor.querySelector("p > b").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<div><b>abc</b><b>def</b></div>", + "<div><b>abcdef</b></div>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div><b>abc</b><p><b>[]def</b></p></div>"'); + +promise_test(async (t) => { + initializeTest("<div><i>abc</i><p><b>def</b></p></div>"); + let abc = gEditor.querySelector("i").firstChild; + let def = gEditor.querySelector("b").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div><i>abc</i><b>def</b></div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div><i>abc</i><p><b>[]def</b></p></div>"'); + +promise_test(async (t) => { + initializeTest("<div><p>abc</p>def</div>"); + let abc = gEditor.querySelector("p").firstChild; + let def = gEditor.querySelector("p").nextSibling; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<div><p>abcdef</p></div>", + "<div><p>abcdef<br></p></div>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div><p>abc</p>[]def</div>"'); + +// Joining child block and parent block should remove invisible trailing +// white-spaces of the child block and invisible following white-spaces +// in the parent block, and they should be contained by a range of +// `getTargetRanges()`, but maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async (t) => { + initializeTest("<div><p>abc </p> def</div>"); + let abc = gEditor.querySelector("p").firstChild; + let def = gEditor.querySelector("p").nextSibling; + gSelection.collapse(def, 3); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<div><p>abcdef</p></div>", + "<div><p>abcdef<br></p></div>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div><p>abc </p> []def</div>"'); + +promise_test(async (t) => { + initializeTest("<div><p><b>abc</b></p>def</div>"); + let abc = gEditor.querySelector("b").firstChild; + let def = gEditor.querySelector("p").nextSibling; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div><p><b>abc</b>def</p></div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div><p><b>abc</b></p>[]def</div>"'); + +promise_test(async (t) => { + initializeTest("<div><p><b>abc</b></p><b>def</b></div>"); + let abc = gEditor.querySelector("b").firstChild; + let def = gEditor.querySelector("div > b").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<div><p><b>abc</b><b>def</b></p></div>", + "<div><p><b>abcdef</b></p></div>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div><p><b>abc</b></p><b>[]def</b></div>"'); + +promise_test(async (t) => { + initializeTest("<div><p><b>abc</b></p><i>def</i></div>"); + let abc = gEditor.querySelector("b").firstChild; + let def = gEditor.querySelector("i").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + "<div><p><b>abc</b><i>def</i></p></div>", + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div><p><b>abc</b></p><i>[]def</i></div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>"); + let abc = gEditor.querySelector("div").firstChild; + let def = gEditor.querySelector("li").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div>abcdefghi</div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<ul><li>[]def</li></ul>ghi</div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc <ul><li> def </li></ul> ghi</div>"); + let abc = gEditor.querySelector("div").firstChild; + let def = gEditor.querySelector("li").firstChild; + gSelection.collapse(def, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div>abcdefghi</div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc <ul><li> []def </li></ul> ghi</div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc <ul><li> def </li></ul> ghi</div>"); + let abc = gEditor.querySelector("div").firstChild; + let def = gEditor.querySelector("li").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<div>abcdefghi</div>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc <ul><li>[] def </li></ul> ghi</div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc<ul><li>def</li></ul>ghi</div>"); + let def = gEditor.querySelector("li").firstChild; + let ghi = gEditor.querySelector("ul").nextSibling; + gSelection.collapse(ghi, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + "<div>abc<ul><li>defghi</li></ul></div>", + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: def, + startOffset: 3, + endContainer: ghi, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<ul><li>def</li></ul>[]ghi</div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc <ul><li> def </li></ul> ghi</div>"); + let def = gEditor.querySelector("li").firstChild; + let ghi = gEditor.querySelector("ul").nextSibling; + gSelection.collapse(ghi, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<div>abc <ul><li> defghi</li></ul></div>", + "<div>abc <ul><li>defghi</li></ul></div>", + "<div>abc<ul><li>defghi</li></ul></div>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: def, + startOffset: 5, + endContainer: ghi, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc <ul><li> def </li></ul> []ghi</div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc <ul><li> def </li></ul> ghi</div>"); + let def = gEditor.querySelector("li").firstChild; + let ghi = gEditor.querySelector("ul").nextSibling; + gSelection.collapse(ghi, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<div>abc <ul><li> defghi</li></ul></div>", + "<div>abc <ul><li>defghi</li></ul></div>", + "<div>abc<ul><li>defghi</li></ul></div>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: def, + startOffset: 5, + endContainer: ghi, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc <ul><li> def </li></ul>[] ghi</div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>"); + let abc = gEditor.querySelector("div").firstChild; + let def = gEditor.querySelector("li").firstChild; + gSelection.collapse(def, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + "<div>abcdef<ul><li>ghi</li></ul>jkl</div>", + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<ul><li>[]def</li><li>ghi</li></ul>jkl</div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>"); + let def = gEditor.querySelector("li").firstChild; + let ghi = gEditor.querySelector("li + li").firstChild; + gSelection.collapse(ghi, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + "<div>abc<ul><li>defghi</li></ul>jkl</div>", + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: def, + startOffset: 3, + endContainer: ghi, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<ul><li>def</li><li>[]ghi</li></ul>jkl</div>"'); + +promise_test(async (t) => { + initializeTest("<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>"); + let ghi = gEditor.querySelector("li + li").firstChild; + let jkl = gEditor.querySelector("ul").nextSibling; + gSelection.collapse(jkl, 0); + await sendBackspaceKey(); + assert_equals(gEditor.innerHTML, + "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>", + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: ghi, + startOffset: 3, + endContainer: jkl, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<div>abc<ul><li>def</li><li>ghi</li></ul>[]jkl</div>"'); + +// Backspace in empty paragraph should remove the empty paragraph. In this +// case, it should be treated as joining with the previous paragraph. +// The target range should include the invisible <br> element in the empty +// paragraph. +promise_test(async (t) => { + initializeTest("<p>abc</p><p><br></p>"); + let p1 = gEditor.querySelector("p"); + let abc = p1.firstChild; + let p2 = p1.nextSibling; + gSelection.collapse(p2, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abc</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: p2, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc</p><p>{}<br></p>"'); + +// Delete ignore the empty span and the other things must be same as the +// previous test. +promise_test(async (t) => { + initializeTest("<p>abc</p><p><span></span><br></p>"); + let p1 = gEditor.querySelector("p"); + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let span = p2.firstChild; + gSelection.collapse(span, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest("<p>abc</p>", t.name); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: p2, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc</p><p><span>{}</span><br></p>"'); + +// If invisible white-spaces are removed with same action as above tests, +// the range should be included in the target ranges. +promise_test(async (t) => { + initializeTest("<p>abc </p><p><br></p>"); + let p1 = gEditor.querySelector("p"); + let abc = p1.firstChild; + let p2 = p1.nextSibling; + gSelection.collapse(p2, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<p>abc </p>", + "<p>abc</p>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: abc.length, + endContainer: p2, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc </p><p>{}<br></p>"'); + +// If the previous block ends with non-editable content, target range +// should be after the non-editable content node. +promise_test(async (t) => { + initializeTest("<p>abc<span contenteditable=\"false\">def</span></p><p><br></p>"); + let p1 = gEditor.querySelector("p"); + let span = gEditor.querySelector("span"); + let p2 = p1.nextSibling; + gSelection.collapse(p2, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + "<p>abc<span contenteditable=\"false\">def</span></p>", + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p1, + startOffset: 2, + endContainer: p2, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc<span contenteditable="false">def</span></p><p>{}<br></p>"'); + +// If previous non-editable paragraph is deleted, target range should begin +// with end of the text node in the first paragraph. Otherwise, start from +// after the non-editable paragraph. +promise_test(async (t) => { + initializeTest("<p>abc</p><p contenteditable=\"false\">def</p><p><br></p>"); + let p1 = gEditor.querySelector("p"); + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let p3 = p2.nextSibling; + gSelection.collapse(p3, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<p>abc</p>", + "<p>abc</p><p contenteditable=\"false\">def</p>", + ], + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p2.isConnected ? gEditor : abc, + startOffset: p2.isConnected ? 2 : abc.length, + endContainer: p3, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<p>abc</p><p contenteditable=\"false\">def</p><p>{}<br></p>"'); + +promise_test(async (t) => { + initializeTest("<p>abc<span contenteditable=\"false\">def</span>ghi</p>"); + let p = gEditor.querySelector("p"); + let ghi = p.lastChild; + gSelection.collapse(ghi, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<p>abc<span contenteditable=\"false\">def</span>ghi</p>", + "<p>abcghi</p>", + "<p>abcghi<br></p>", + ], + t.name + ); + if (gEditor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: ghi, + startOffset: 0, + endContainer: ghi, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDoNothing(); + } else { + // If the non-editable `<span>` is deleted, it should be treated as + // an atomic node. + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 1, + endContainer: p, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); + } +}, 'Backspace at "<p>abc<span contenteditable=\"false\">def</span>[]ghi</p>"'); + +// If just removes the paragraph, target range should start from after the +// table element. +promise_test(async (t) => { + initializeTest("<table><tr><td>cell</td></tr></table><p><br></p>"); + let table = gEditor.querySelector("table"); + let p = table.nextSibling; + gSelection.collapse(p, 0); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td>cell</td></tr></tbody></table>", + "<table><tbody><tr><td>cell</td></tr></tbody></table><p><br></p>", + ], + t.name + ); + if (p.isConnected) { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p, + startOffset: 0, + endContainer: p, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDoNothing(); + } else { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor, + startOffset: 1, + endContainer: p, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); + } +}, 'Backspace at "<table><tr><td>cell</td></tr></table><p>{}<br></p>"'); + +// If table cell won't be joined, target range should be collapsed in the +// cell. +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td><br></td></tr></table>"); + let cell1 = gEditor.querySelector("td"); + let cell2 = cell1.nextSibling; + gSelection.collapse(cell2, 0); + await sendBackspaceKey(); + assert_equals(gEditor.innerHTML, + "<table><tbody><tr><td>cell1</td><td><br></td></tr></tbody></table>", + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: cell2, + startOffset: 0, + endContainer: cell2, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDoNothing(); +}, 'Backspace at "<table><tr><td>cell1</td><td>{}<br></td></tr></table>"'); + +// If table caption won't be deleted, target range should be collapsed in the +// caption element. +promise_test(async (t) => { + initializeTest("<p>abc</p><table><caption><br></caption><tr><td>cell</td></tr></table>"); + let caption = gEditor.querySelector("caption"); + gSelection.collapse(caption, 0); + await sendBackspaceKey(); + assert_equals(gEditor.innerHTML, + "<p>abc</p><table><caption><br></caption><tbody><tr><td>cell</td></tr></tbody></table>", + t.name + ); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: caption, + startOffset: 0, + endContainer: caption, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDoNothing(); +}, 'Backspace at "<p>abc</p><table><caption>{}<br></caption><tr><td>cell</td></tr></table>"'); + +// If a table cell element is selected, only its content should be deleted. +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr></table>"); + let cell1 = gEditor.querySelector("td"); + let tr = cell1.parentNode; + gSelection.setBaseAndExtent(tr, 0, tr, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td></td><td>cell2</td></tr></tbody></table>", + "<table><tbody><tr><td><br></td><td>cell2</td></tr></tbody></table>", + ], + t.name + ); + // XXX Perhaps, target range should be selecting only all children of + // cell1 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: tr, + startOffset: 0, + endContainer: tr, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr>{<td>cell1</td>}<td>cell2</td></tr></table>"'); + +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr></table>"); + let cell2 = gEditor.querySelector("td + td"); + let tr = cell2.parentNode; + gSelection.setBaseAndExtent(tr, 1, tr, 2); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td>cell1</td><td></td></tr></tbody></table>", + "<table><tbody><tr><td>cell1</td><td><br></td></tr></tbody></table>", + ], + t.name + ); + // XXX Perhaps, target range should be selecting only all children of + // cell1 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: tr, + startOffset: 1, + endContainer: tr, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr><td>cell1</td>{<td>cell2</td>}</tr></table>"'); + +// If the last table cell element is selected, what browsers should do? +promise_test(async (t) => { + initializeTest("<table><tr><td>cell</td></tr></table>"); + let cell = gEditor.querySelector("td"); + let tr = cell.parentNode; + let table = gEditor.querySelector("table"); + gSelection.setBaseAndExtent(tr, 0, tr, 1); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td></td></tr></tbody></table>", + "<table><tbody><tr><td><br></td></tr></tbody></table>", + "<br>", + ], + t.name + ); + if (gEditor.querySelector("table")) { + // XXX Perhaps, target range should be selecting only all children of + // cell1 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: tr, + startOffset: 0, + endContainer: tr, + endOffset: 1, + }); + } else { + // If it causes deleting entire the table, the `<table>` element should + // be in the target range. + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor, + startOffset: 0, + endContainer: gEditor, + endOffset: 1, + }); + } + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr>{<td>cell</td>}</tr></table>"'); + +// Testing multiple cell selection mode. +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); + let cell1 = gEditor.querySelector("td"); + let cell4 = gEditor.querySelector("tr + tr > td + td"); + let tr1 = cell1.parentNode; + let tr2 = cell4.parentNode; + gSelection.removeAllRanges(); + let range = document.createRange(); + range.selectNode(cell1); + gSelection.addRange(range); + range = document.createRange(); + range.selectNode(cell4); + gSelection.addRange(range); + assert_equals(gSelection.rangeCount, 2, "Should support multiple cell selection"); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td></td><td>cell2</td></tr><tr><td>cell3</td><td></td></tr></tbody></table>", + "<table><tbody><tr><td><br></td><td>cell2</td></tr><tr><td>cell3</td><td><br></td></tr></tbody></table>", + ], + t.name + ); + // XXX Perhaps, target range should be selecting only all children of + // cell1 and cell4 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething([ + { + startContainer: tr1, + startOffset: 0, + endContainer: tr1, + endOffset: 1, + }, + { + startContainer: tr2, + startOffset: 1, + endContainer: tr2, + endOffset: 2, + }, + ]); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr>{<td>cell1</td>}<td>cell2</td></tr><tr><td>cell3</td>{<td>cell4</td>}</tr></table>"'); + +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); + let cell1 = gEditor.querySelector("td"); + let cell3 = gEditor.querySelector("tr + tr > td"); + let tr1 = cell1.parentNode; + let tr2 = cell3.parentNode; + gSelection.removeAllRanges(); + let range = document.createRange(); + range.selectNode(cell1); + gSelection.addRange(range); + range = document.createRange(); + range.selectNode(cell3); + gSelection.addRange(range); + assert_equals(gSelection.rangeCount, 2, "Should support multiple cell selection"); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td></td><td>cell2</td></tr><tr><td></td><td>cell4</td></tr></tbody></table>", + "<table><tbody><tr><td><br></td><td>cell2</td></tr><tr><td><br></td><td>cell4</td></tr></tbody></table>", + "<table><tbody><tr><td>cell2</td></tr><tr><td>cell4</td></tr></tbody></table>", + ], + t.name + ); + // XXX Perhaps, target range should be selecting only all children of + // cell1 and cell3 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething([ + { + startContainer: tr1, + startOffset: 0, + endContainer: tr1, + endOffset: 1, + }, + { + startContainer: tr2, + startOffset: 0, + endContainer: tr2, + endOffset: 1, + }, + ]); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr>{<td>cell1</td>}<td>cell2</td></tr><tr>{<td>cell3</td>}<td>cell4</td></tr></table>"'); + +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); + let cell1 = gEditor.querySelector("td"); + let cell2 = gEditor.querySelector("td + td"); + let tr1 = cell1.parentNode; + let tbody = tr1.parentNode; + gSelection.removeAllRanges(); + let range = document.createRange(); + range.selectNode(cell1); + gSelection.addRange(range); + range = document.createRange(); + range.selectNode(cell2); + gSelection.addRange(range); + assert_equals(gSelection.rangeCount, 2, "Should support multiple cell selection"); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td></td><td></td></tr><tr><td>cell3</td><td>cell4</td></tr></tbody></table>", + "<table><tbody><tr><td><br></td><td><br></td></tr><tr><td>cell3</td><td>cell4</td></tr></tbody></table>", + "<table><tbody><tr><td>cell3</td><td>cell4</td></tr></tbody></table>", + ], + t.name + ); + if (gEditor.querySelector("tr + tr")) { + // XXX Perhaps, target range should be selecting only all children of + // cell1 and cell2 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething([ + { + startContainer: tr1, + startOffset: 0, + endContainer: tr1, + endOffset: 1, + }, + { + startContainer: tr1, + startOffset: 1, + endContainer: tr1, + endOffset: 2, + }, + ]); + } else { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: tbody, + startOffset: 0, + endContainer: tbody, + endOffset: 1, + }); + } + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr>{<td>cell1</td>}{<td>cell2</td>}</tr><tr><td>cell3</td><td>cell4</td></tr></table>"'); + +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); + let cell3 = gEditor.querySelector("tr + tr > td"); + let cell4 = gEditor.querySelector("tr + tr > td + td"); + let tr2 = cell3.parentNode; + gSelection.removeAllRanges(); + let range = document.createRange(); + range.selectNode(cell3); + gSelection.addRange(range); + range = document.createRange(); + range.selectNode(cell4); + gSelection.addRange(range); + assert_equals(gSelection.rangeCount, 2, "Should support multiple cell selection"); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td>cell1</td><td>cell2</td></tr><tr><td></td><td></td></tr></tbody></table>", + "<table><tbody><tr><td>cell1</td><td>cell2</td></tr><tr><td><br></td><td><br></td></tr></tbody></table>", + "<table><tbody><tr><td>cell1</td><td>cell2</td></tr></tbody></table>", + ], + t.name + ); + if (gEditor.querySelector("tr + tr")) { + // XXX Perhaps, target range should be selecting only all children of + // cell3 and cell4 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething([ + { + startContainer: tr2, + startOffset: 0, + endContainer: tr2, + endOffset: 1, + }, + { + startContainer: tr2, + startOffset: 1, + endContainer: tr2, + endOffset: 2, + }, + ]); + } else { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: tbody, + startOffset: 1, + endContainer: tbody, + endOffset: 2, + }); + } + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr><td>cell1</td><td>cell2</td></tr><tr>{<td>cell3</td>}{<td>cell4</td>}</tr></table>"'); + +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); + let cell1 = gEditor.querySelector("td"); + let cell2 = gEditor.querySelector("td + td"); + let cell3 = gEditor.querySelector("tr + tr > td"); + let cell4 = gEditor.querySelector("tr + tr > td + td"); + let tr1 = cell1.parentNode; + let tr2 = cell3.parentNode; + gSelection.removeAllRanges(); + let range = document.createRange(); + range.selectNode(cell1); + gSelection.addRange(range); + range = document.createRange(); + range.selectNode(cell2); + gSelection.addRange(range); + range = document.createRange(); + range.selectNode(cell3); + gSelection.addRange(range); + range = document.createRange(); + range.selectNode(cell4); + gSelection.addRange(range); + assert_equals(gSelection.rangeCount, 4, "Should support multiple cell selection"); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td></td><td></td></tr><tr><td></td><td></td></tr></tbody></table>", + "<table><tbody><tr><td><br></td><td><br></td></tr><tr><td><br></td><td><br></td></tr></tbody></table>", + "<br>", + ], + t.name + ); + if (gEditor.querySelector("table")) { + // XXX Perhaps, target range should be selecting only all children of + // cell1, cell2, cell3 and cell4 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething([ + { + startContainer: tr1, + startOffset: 0, + endContainer: tr1, + endOffset: 1, + }, + { + startContainer: tr1, + startOffset: 1, + endContainer: tr1, + endOffset: 2, + }, + { + startContainer: tr2, + startOffset: 0, + endContainer: tr2, + endOffset: 1, + }, + { + startContainer: tr2, + startOffset: 1, + endContainer: tr2, + endOffset: 2, + }, + ]); + } else { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: gEditor, + startOffset: 0, + endContainer: gEditor, + endOffset: 1, + }); + } + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr>{<td>cell1</td>}{<td>cell2</td>}</tr><tr>{<td>cell3</td>}{<td>cell4</td>}</tr></table>"'); + +promise_test(async (t) => { + initializeTest("<table><tr><td>cell1</td><td>cell2</td></tr><tr><td>cell3</td><td>cell4</td></tr></table>"); + let cell1 = gEditor.querySelector("td"); + let cell2 = gEditor.querySelector("td + td"); + let cell4 = gEditor.querySelector("tr + tr > td + td"); + let tr1 = cell1.parentNode; + let tr2 = cell4.parentNode; + gSelection.removeAllRanges(); + let range = document.createRange(); + range.selectNode(cell1); + gSelection.addRange(range); + range = document.createRange(); + range.setStart(cell2.firstChild, 1); + range.setEnd(cell2.firstChild, 4); + gSelection.addRange(range); + range = document.createRange(); + range.selectNode(cell4); + gSelection.addRange(range); + assert_equals(gSelection.rangeCount, 3, "Should support multiple cell selection"); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<table><tbody><tr><td></td><td>cell2</td></tr><tr><td>cell3</td><td></td></tr></tbody></table>", + "<table><tbody><tr><td><br></td><td>cell2</td></tr><tr><td>cell3</td><td><br></td></tr></tbody></table>", + "<table><tbody><tr><td></td><td>c2</td></tr><tr><td>cell3</td><td></td></tr></tbody></table>", + "<table><tbody><tr><td><br></td><td>c2</td></tr><tr><td>cell3</td><td><br></td></tr></tbody></table>", + ], + t.name + ); + if (cell2.firstChild.length == "cell2".length) { + // XXX Perhaps, target range should be selecting only all children of + // cell1 and cell4 instead. + checkGetTargetRangesOfBeforeinputOnDeleteSomething([ + { + startContainer: tr1, + startOffset: 0, + endContainer: tr1, + endOffset: 1, + }, + { + startContainer: tr2, + startOffset: 1, + endContainer: tr2, + endOffset: 2, + }, + ]); + } else { + checkGetTargetRangesOfBeforeinputOnDeleteSomething([ + { + startContainer: tr1, + startOffset: 0, + endContainer: tr1, + endOffset: 1, + }, + { + startContainer: cell2.firstChild, + startOffset: 1, + endContainer: cell2.firstChild, + endOffset: 4, + }, + { + startContainer: tr2, + startOffset: 1, + endContainer: tr2, + endOffset: 2, + }, + ]); + } + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Backspace at "<table><tr>{<td>cell1</td>}<td>c[ell]2</td></tr><tr>{<td>cell3</td>}<td>cell4</td></tr></table>"'); + +// If caret is not adjacent of deleting character, browser may not delete the +// character, but update the caret position for next deletion. +promise_test(async (t) => { + initializeTest("<p>שלוםhello</p>"); + let text1 = gEditor.querySelector("p").firstChild; + let text2 = text1.nextSibling; + gSelection.collapse(text2 ? text2 : text1, text2 ? 1 : 5); + await sendArrowLeftKey(); + await sendBackspaceKey(); + checkEditorContentResultAsSubTest( + [ + "<p>\u05E9\u05DC\u05D5\u05DDhello</p>", + "<p>\u05DC\u05D5\u05DDhello</p>", + "<p>\u05E9\u05DC\u05D5hello</p>", + ], + t.name + ); + if (gEditor.innerHTML === "<p>\u05E9\u05DC\u05D5\u05DDhello</p>") { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: text2 ? text2 : text1, + startOffset: text2 ? 0 : 4, + endContainer: text2 ? text2 : text1, + endOffset: text2 ? 0 : 4, + }); + checkGetTargetRangesOfInputOnDoNothing(); + } else if (gEditor.innerHTML === "<p>\u05DC\u05D5\u05DDhello</p>") { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: text1, + startOffset: 0, + endContainer: text1, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); + } else { + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: text1, + startOffset: 3, + endContainer: text1, + endOffset: 4, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); + } +}, 'Backspace at "<p>שלום[]hello</p>"'); + +// The following tests check whether the range returned from +// `beforeinput[0].getTargetRanges()` is modified or different range is +// modified instead. I.e., they don't test which type of deletion should +// occur. Therefore, their result depends on browser's key bindings, +// system settings and running OS. + +function getFirstDifferentOffset(currentString, originalString) { + for (let i = 0; i < currentString.length; i++) { + if (currentString.charAt(i) !== originalString.charAt(i) && + (originalString.charAt(i) !== " " || !currentString.charAt("\u00A0"))) { + return i; + } + } + return currentString.length; +} + +promise_test(async (t) => { + const kText = "abc def ghi"; + initializeTest(`<p>${kText}</p>`); + let p = gEditor.querySelector("p"); + gSelection.collapse(p.firstChild, "abc def".length); + await sendBackspaceKey(kShift); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + checkEditorContentResultAsSubTest( + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, + t.name, + { ignoreWhiteSpaceDifference: true } + ); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Shift + Backspace at "<p>abc def[] ghi</p>"'); + +promise_test(async (t) => { + const kText = "abc def ghi"; + initializeTest(`<p>${kText}</p>`); + let p = gEditor.querySelector("p"); + gSelection.collapse(p.firstChild, "abc def".length); + await sendBackspaceKey(kControl); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + checkEditorContentResultAsSubTest( + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, + t.name, + { ignoreWhiteSpaceDifference: true } + ); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Control + Backspace at "<p>abc def[] ghi</p>"'); + +promise_test(async (t) => { + const kText = "abc def ghi"; + initializeTest(`<p>${kText}</p>`); + let p = gEditor.querySelector("p"); + gSelection.collapse(p.firstChild, "abc def".length); + await sendBackspaceKey(kAlt); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + checkEditorContentResultAsSubTest( + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, + t.name, + { ignoreWhiteSpaceDifference: true } + ); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Alt + Backspace at "<p>abc def[] ghi</p>"'); + +promise_test(async (t) => { + const kText = "abc def ghi"; + initializeTest(`<p>${kText}</p>`); + let p = gEditor.querySelector("p"); + gSelection.collapse(p.firstChild, "abc def".length); + await sendBackspaceKey(kMeta); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + checkEditorContentResultAsSubTest( + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, + t.name, + { ignoreWhiteSpaceDifference: true } + ); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Meta + Backspace at "<p>abc def[] ghi</p>"'); + +promise_test(async (t) => { + const kText = "abc def"; + initializeTest(`<p> ${kText}</p>`); + let p = gEditor.querySelector("p"); + gSelection.collapse(p.firstChild, "abc".length); + await sendBackspaceKey(kShift); + let visibleText = p.firstChild.data.replace(/^\s+/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + checkEditorContentResultAsSubTest( + `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, + t.name, + { ignoreWhiteSpaceDifference: true } + ); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Shift + Backspace at "<p> abc[] def</p>"'); + +promise_test(async (t) => { + const kText = "abc def"; + initializeTest(`<p> ${kText}</p>`); + let p = gEditor.querySelector("p"); + gSelection.collapse(p.firstChild, "abc".length); + await sendBackspaceKey(kControl); + let visibleText = p.firstChild.data.replace(/^\s+/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + checkEditorContentResultAsSubTest( + `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, + t.name, + { ignoreWhiteSpaceDifference: true } + ); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Control + Backspace at "<p> abc[] def</p>"'); + +promise_test(async (t) => { + const kText = "abc def"; + initializeTest(`<p> ${kText}</p>`); + let p = gEditor.querySelector("p"); + gSelection.collapse(p.firstChild, "abc".length); + await sendBackspaceKey(kAlt); + let visibleText = p.firstChild.data.replace(/^\s+/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + checkEditorContentResultAsSubTest( + `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, + t.name, + { ignoreWhiteSpaceDifference: true } + ); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Alt + Backspace at "<p> abc[] def</p>"'); + +promise_test(async (t) => { + const kText = "abc def"; + initializeTest(`<p> ${kText}</p>`); + let p = gEditor.querySelector("p"); + gSelection.collapse(p.firstChild, "abc".length); + await sendBackspaceKey(kMeta); + let visibleText = p.firstChild.data.replace(/^\s+/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + checkEditorContentResultAsSubTest( + `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`, + t.name + ); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Meta + Backspace at "<p> abc[] def</p>"'); + +</script> |