summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/input-events/input-events-get-target-ranges-backspace.tentative.html
diff options
context:
space:
mode:
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.html2131
1 files changed, 2131 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..90755042a6
--- /dev/null
+++ b/testing/web-platform/tests/input-events/input-events-get-target-ranges-backspace.tentative.html
@@ -0,0 +1,2131 @@
+<!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 &nbsp; def</p>",
+ "<p>abc&nbsp;&nbsp; def</p>",
+ "<p>abc&nbsp; &nbsp;def</p>",
+ "<p>abc &nbsp;&nbsp;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>&#x5E9;&#x5DC;&#x5D5;&#x5DD;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>&#x5E9;&#x5DC;&#x5D5;&#x5DD;[]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>"');
+
+// If editing host is nested, editing in the nested one shouldn't cause
+// target ranges extended to outside of it.
+promise_test(async t => {
+ const innerHTML = "<div contenteditable=\"false\"><div contenteditable=\"\"><p><br></p></div></div>";
+ initializeTest(innerHTML);
+ const p = gEditor.querySelector("p");
+ const innerEditingHost = p.parentNode;
+ document.activeElement?.blur();
+ p.parentNode.focus();
+ gSelection.collapse(p, 0);
+ await sendBackspaceKey();
+ if (gEditor.innerHTML == innerHTML) {
+ checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+ startContainer: p,
+ startOffset: 0,
+ endContainer: p,
+ endOffset: 0,
+ });
+ checkGetTargetRangesOfInputOnDoNothing();
+ } else {
+ checkEditorContentResultAsSubTest(
+ "<div contenteditable=\"false\"><div contenteditable=\"\"><br></div></div>",
+ t.name
+ );
+ checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+ startContainer: innerEditingHost,
+ startOffset: 0,
+ endContainer: p,
+ endOffset: 1,
+ });
+ checkGetTargetRangesOfInputOnDeleteSomething();
+ }
+}, 'Backspace at "<div contenteditable="false"><div contenteditable=""><p>{}<br></p></div></div>');
+
+promise_test(async t => {
+ const innerHTML = "<div contenteditable=\"false\">\n <div contenteditable=\"\"><p><br></p></div></div>";
+ initializeTest(innerHTML);
+ const p = gEditor.querySelector("p");
+ const innerEditingHost = p.parentNode;
+ document.activeElement?.blur();
+ p.parentNode.focus();
+ gSelection.collapse(p, 0);
+ await sendBackspaceKey();
+ if (gEditor.innerHTML == innerHTML) {
+ checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+ startContainer: p,
+ startOffset: 0,
+ endContainer: p,
+ endOffset: 0,
+ });
+ checkGetTargetRangesOfInputOnDoNothing();
+ } else {
+ checkEditorContentResultAsSubTest(
+ "<div contenteditable=\"false\">\n <div contenteditable=\"\"><br></div></div>",
+ t.name
+ );
+ checkGetTargetRangesOfBeforeinputOnDeleteSomething({
+ startContainer: innerEditingHost,
+ startOffset: 0,
+ endContainer: p,
+ endOffset: 1,
+ });
+ checkGetTargetRangesOfInputOnDeleteSomething();
+ }
+}, 'Backspace at "<div contenteditable="false">\n <div contenteditable=""><p>{}<br></p></div></div>');
+
+</script>