blob: 6967fa746c6fbcd4da6969ab040fc6c7244a7481 [file] [edit]
<!DOCTYPE html>
<meta charset="utf-8">
<meta name="timeout" content="long">
<title>InputEvent.getTargetRanges() at Backspace</title>
<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>
<div contenteditable></div>
<script>
const kBackspaceKey = "\uE003";
const kArrowLeft = "\uE012";
const kShift = "\uE008";
const kMeta = "\uE03d";
const kControl = "\uE009";
const kAlt = "\uE00A";
const kImgSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==";
let selection = getSelection();
let editor = document.querySelector("div[contenteditable]");
let beforeinput = [];
let input = [];
editor.addEventListener("beforeinput", (e) => {
// NOTE: Blink makes `getTargetRanges()` return empty range after propagation,
// but this test wants to check the result during propagation.
// Therefore, we need to cache the result, but will assert if
// `getTargetRanges()` returns different ranges after checking the
// cached ranges.
e.cachedRanges = e.getTargetRanges();
beforeinput.push(e);
});
editor.addEventListener("input", (e) => {
e.cachedRanges = e.getTargetRanges();
input.push(e);
});
function reset() {
editor.focus();
beforeinput = [];
input = [];
}
function getRangeDescription(range) {
function getNodeDescription(node) {
if (!node) {
return "null";
}
switch (node.nodeType) {
case Node.TEXT_NODE:
case Node.COMMENT_NODE:
case Node.CDATA_SECTION_NODE:
return `${node.nodeName} "${node.data}"`;
case Node.ELEMENT_NODE:
return `<${node.nodeName.toLowerCase()}>`;
default:
return `${node.nodeName}`;
}
}
if (range === null) {
return "null";
}
if (range === undefined) {
return "undefined";
}
return range.startContainer == range.endContainer && range.startOffset == range.endOffset
? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})`
: `(${getNodeDescription(range.startContainer)}, ${range.startOffset}) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`;
}
function getArrayOfRangesDescription(arrayOfRanges) {
if (arrayOfRanges === null) {
return "null";
}
if (arrayOfRanges === undefined) {
return "undefined";
}
if (!Array.isArray(arrayOfRanges)) {
return "Unknown Object";
}
if (arrayOfRanges.length === 0) {
return "[]";
}
let result = "[";
for (let range of arrayOfRanges) {
result += `{${getRangeDescription(range)}},`;
}
result += "]";
return result;
}
function sendBackspaceKey(modifier) {
if (!modifier) {
return new test_driver.Actions()
.keyDown(kBackspaceKey)
.keyUp(kBackspaceKey)
.send();
}
return new test_driver.Actions()
.keyDown(modifier)
.keyDown(kBackspaceKey)
.keyUp(kBackspaceKey)
.keyUp(modifier)
.send();
}
function sendArrowLeftKey() {
return new test_driver.Actions()
.keyDown(kArrowLeft)
.keyUp(kArrowLeft)
.send();
}
function checkGetTargetRangesKeepReturningSameValue(event) {
// https://github.com/w3c/input-events/issues/114
assert_equals(getArrayOfRangesDescription(event.getTargetRanges()),
getArrayOfRangesDescription(event.cachedRanges),
`${event.type}.getTargetRanges() should keep returning the same array of ranges even after its propagation finished`);
}
function checkGetTargetRangesOfBeforeinputOnDeleteSomething(expectedRange) {
assert_equals(beforeinput.length, 1,
"One beforeinput event should be fired if the key operation deletes something");
assert_true(Array.isArray(beforeinput[0].cachedRanges),
"beforeinput[0].getTargetRanges() should return an array of StaticRange instances during propagation");
// Before checking the length of array of ranges, we should check first range
// first because the first range data is more important than whether there
// are additional unexpected ranges.
if (beforeinput[0].cachedRanges.length > 0) {
assert_equals(
getRangeDescription(beforeinput[0].cachedRanges[0]),
getRangeDescription(expectedRange),
`beforeinput.getTargetRanges() should return expected range (inputType is "${beforeinput[0].inputType}")`);
assert_equals(beforeinput[0].cachedRanges.length, 1,
"beforeinput.getTargetRanges() should return one range within an array");
}
assert_equals(beforeinput[0].cachedRanges.length, 1,
"One range should be returned from getTargetRanges() when the key operation deletes something");
checkGetTargetRangesKeepReturningSameValue(beforeinput[0]);
}
function checkGetTargetRangesOfInputOnDeleteSomething() {
assert_equals(input.length, 1,
"One input event should be fired if the key operation deletes something");
// https://github.com/w3c/input-events/issues/113
assert_true(Array.isArray(input[0].cachedRanges),
"input[0].getTargetRanges() should return an array of StaticRange instances during propagation");
assert_equals(input[0].cachedRanges.length, 0,
"input[0].getTargetRanges() should return empty array during propagation");
checkGetTargetRangesKeepReturningSameValue(input[0]);
}
function checkGetTargetRangesOfInputOnDoNothing() {
assert_equals(input.length, 0,
"input event shouldn't be fired when the key operation does not cause modifying the DOM tree");
}
function checkBeforeinputAndInputEventsOnNOOP() {
assert_equals(beforeinput.length, 0,
"beforeinput event shouldn't be fired when the key operation does not cause modifying the DOM tree");
assert_equals(input.length, 0,
"input event shouldn't be fired when the key operation does not cause modifying the DOM tree");
}
// Simply deletes the previous ASCII character of caret position.
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc</p>";
selection.collapse(editor.firstChild.firstChild, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>bc</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: editor.firstChild.firstChild,
startOffset: 0,
endContainer: editor.firstChild.firstChild,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>a[]bc</p>"');
// Simply deletes the previous ASCII character of caret position.
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc</p>";
selection.collapse(editor.firstChild.firstChild, 2);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>ac</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: editor.firstChild.firstChild,
startOffset: 1,
endContainer: editor.firstChild.firstChild,
endOffset: 2,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>ab[]c</p>"');
// Simply deletes the previous ASCII character of caret position.
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc</p>";
selection.collapse(editor.firstChild.firstChild, 3);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>ab</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: editor.firstChild.firstChild,
startOffset: 2,
endContainer: editor.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 () => {
reset();
editor.innerHTML = "<p>a<span>b</span>c</p>";
let c = editor.querySelector("span").nextSibling;
selection.collapse(c, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>ac</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: editor.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 () => {
reset();
editor.innerHTML = "<p>a<span>b</span>c</p>";
let b = editor.querySelector("span").firstChild;
selection.collapse(b, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>ac</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: editor.firstChild,
startOffset: 1,
endContainer: editor.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 () => {
reset();
editor.innerHTML = "<p> abc</p>";
selection.collapse(editor.firstChild.firstChild, 2);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p>bc</p>", "<p> bc</p>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: editor.firstChild.firstChild,
startOffset: editor.firstChild.firstChild.length == 2 ? 0 : 1,
endContainer: editor.firstChild.firstChild,
endOffset: 2,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p> a[]bc</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc</p><p>def</p>";
let p1 = editor.firstChild;
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let def = p2.firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
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 () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let p1 = editor.firstChild;
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let def = p2.firstChild;
selection.collapse(def, 3);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
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 () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let p1 = editor.firstChild;
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let def = p2.firstChild;
selection.collapse(def, 2);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
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 () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let p1 = editor.firstChild;
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let def = p2.firstChild;
selection.collapse(def, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
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 () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let p1 = editor.firstChild;
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let def = p2.firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
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 () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let p1 = editor.firstChild;
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let def = p2.firstChild;
selection.setBaseAndExtent(abc, 6, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 3,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc [</p><p>] def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc</p><p><b>def</b></p>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("b").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc<b>def</b></p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc</p><p><b>[]def</b></p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p><b>abc</b></p><p><b>def</b></p>";
let abc = editor.querySelector("p > b").firstChild;
let def = editor.querySelector("P + p > b").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p><b>abc</b><b>def</b></p>",
"<p><b>abcdef</b></p>"]);
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 () => {
reset();
editor.innerHTML = "<p><i>abc</i></p><p><b>def</b></p>";
let abc = editor.querySelector("i").firstChild;
let def = editor.querySelector("b").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p><i>abc</i><b>def</b></p>");
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 () => {
reset();
editor.innerHTML = "<pre>abc </pre><p> def</p>";
let pre = editor.firstChild;
let abc = pre.firstChild;
let p = pre.nextSibling;
let def = p.firstChild;
selection.collapse(def, 3);
await sendBackspaceKey();
// https://github.com/w3c/input-events/issues/112
// Shouldn't make the invisible white-spaces visible.
assert_equals(editor.innerHTML, "<pre>abc def</pre>");
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 () => {
reset();
editor.innerHTML = "<pre>abc </pre><p> def </p>";
let pre = editor.firstChild;
let abc = pre.firstChild;
let p = pre.nextSibling;
let def = p.firstChild;
selection.collapse(def, 3);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<pre>abc def </pre>");
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 () => {
reset();
editor.innerHTML = "<p>abc </p><pre> def</pre>";
let p = editor.firstChild;
let abc = p.firstChild;
let pre = p.nextSibling;
let def = pre.firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p>abc &nbsp; def</p>",
"<p>abc&nbsp;&nbsp; def</p>",
"<p>abc&nbsp; &nbsp;def</p>",
"<p>abc &nbsp;&nbsp;def</p>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 6,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc </p><pre>[] def</pre>"');
// 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 () => {
reset();
editor.innerHTML = "<p>abc<br></p><p>def</p>";
let p1 = editor.firstChild;
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let def = p2.firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
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 () => {
reset();
editor.innerHTML = "<p>abc<br><br></p><p>def</p>";
let p1 = editor.firstChild;
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let def = p2.firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc<br>def</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p1,
startOffset: 2,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc<br><br></p><p>[]def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc</p><p>def</p>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p + p").firstChild;
selection.setBaseAndExtent(abc, 2, def, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 2,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>ab[c</p><p>d]ef</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p + p").firstChild;
selection.setBaseAndExtent(abc, 2, def, 2);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 2,
endContainer: def,
endOffset: 2,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>ab[c </p><p> d]ef</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p + p").firstChild;
selection.setBaseAndExtent(abc, 2, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abdef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 2,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>ab[c </p><p>] def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p + p").firstChild;
selection.setBaseAndExtent(abc, 4, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc [</p><p>] def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc </p><p> def</p>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p + p").firstChild;
selection.setBaseAndExtent(abc, 4, def, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc [</p><p> ]def</p>"');
// Deleting visible `<br>` element should be contained by a range of
// `getTargetRanges()`.
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc<br>def</p>";
let p = document.querySelector("p");
let def = editor.querySelector("br").nextSibling;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
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 () => {
reset();
editor.innerHTML = "<p>abc <br>def</p>";
let p = editor.querySelector("p");
let def = editor.querySelector("br").nextSibling;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc def</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p,
startOffset: 1,
endContainer: p,
endOffset: 2,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc <br>[]def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = `<p>abc<img src="${kImgSrc}">def</p>`;
let p = editor.querySelector("p");
let def = p.lastChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
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 () => {
reset();
editor.innerHTML = `<p>abc <img src="${kImgSrc}">def</p>`;
let p = editor.querySelector("p");
let def = p.lastChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc def</p>");
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 () => {
reset();
editor.innerHTML = `<p>abc<img src="${kImgSrc}"> def</p>`;
let p = editor.querySelector("p");
let def = p.lastChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc def</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p,
startOffset: 1,
endContainer: p,
endOffset: 2,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc<img>[] def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = `<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`;
let p = editor.querySelector("p");
let abc = p.firstChild;
selection.collapse(p, 2);
await sendBackspaceKey();
assert_equals(editor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p,
startOffset: 1,
endContainer: p,
endOffset: 2,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc<img>{}<img>def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = `<p>abc<img src="${kImgSrc}"><img src="${kImgSrc}">def</p>`;
let p = editor.querySelector("p");
let def = p.lastChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, `<p>abc<img src="${kImgSrc}">def</p>`);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p,
startOffset: 2,
endContainer: p,
endOffset: 3,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc<img><img>[]def</p>"');
// Different from collapsed range around an atomic content, non-collapsed
// range may not be shrunken to select only the atomic content for avoid
// to waste runtime cost.
promise_test(async () => {
reset();
editor.innerHTML = `<p>abc<img src="${kImgSrc}">def</p>`;
let p = editor.querySelector("p");
let abc = p.firstChild;
let def = p.lastChild;
selection.setBaseAndExtent(abc, 3, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, `<p>abcdef</p>`);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc[<img>]def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = `<div>abc<hr>def</div>`;
let div = editor.querySelector("div");
let hr = editor.querySelector("hr");
let def = hr.nextSibling;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef</div>");
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 () => {
reset();
editor.innerHTML = `<div>abc <hr>def</div>`;
let div = editor.querySelector("div");
let abc = div.firstChild;
let hr = editor.querySelector("hr");
let def = hr.nextSibling;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef</div>");
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 () => {
reset();
editor.innerHTML = `<div>abc<hr> def</div>`;
let div = editor.querySelector("div");
let hr = editor.querySelector("hr");
let def = hr.nextSibling;
selection.collapse(def, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef</div>");
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 () => {
reset();
editor.innerHTML = `<div>abc<br><hr>def</div>`;
let div = editor.querySelector("div");
let def = div.lastChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: div,
startOffset: 1,
endContainer: div,
endOffset: 3,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc<br><hr>[]def</div>"');
promise_test(async () => {
reset();
editor.innerHTML = `<div>abc<hr>def</div>`;
let div = editor.querySelector("div");
let abc = div.firstChild;
let def = div.lastChild;
selection.setBaseAndExtent(abc, 3, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc[<hr>]def</div>"');
promise_test(async () => {
reset();
editor.innerHTML = `<div>abc <hr>def</div>`;
let div = editor.querySelector("div");
let abc = div.firstChild;
let def = div.lastChild;
selection.setBaseAndExtent(abc, 4, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc [<hr>]def</div>"');
promise_test(async () => {
reset();
editor.innerHTML = `<div>abc <hr> def</div>`;
let div = editor.querySelector("div");
let abc = div.firstChild;
let def = div.lastChild;
selection.setBaseAndExtent(abc, 4, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc [<hr>] def</div>"');
promise_test(async () => {
reset();
editor.innerHTML = `<div>abc <hr> def</div>`;
let div = editor.querySelector("div");
let abc = div.firstChild;
let def = div.lastChild;
selection.setBaseAndExtent(div, 1, div, 2);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc {<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 () => {
reset();
editor.innerHTML = "<p>abc<br> def</p>";
let p = editor.querySelector("p");
let def = editor.querySelector("br").nextSibling;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p,
startOffset: 1,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc<br>[] def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc<br> def</p>";
let p = editor.querySelector("p");
let def = editor.querySelector("br").nextSibling;
selection.collapse(def, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p,
startOffset: 1,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc<br> []def</p>"');
// Deleting visible `<br>` element should be contained by a range of
// `getTargetRanges()`. However, when only the `<br>` element is selected,
// the range shouldn't start from nor end by surrounding text nodes?
// https://github.com/w3c/input-events/issues/112
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc<br>def</p>";
selection.setBaseAndExtent(editor.firstChild, 1, editor.firstChild, 2);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abcdef</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: editor.firstChild,
startOffset: 1,
endContainer: editor.firstChild,
endOffset: 2,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc{<br>}def</p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<p>def<br>ghi</p></div>";
let p = editor.querySelector("p");
let def = p.firstChild;
let abc = editor.firstChild.firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
"<div>abcdef<br><p>ghi</p></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc<p>[]def<br>ghi</p></div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<p>def<br>ghi</p></div>";
let p = editor.querySelector("p");
let def = p.firstChild;
let abc = editor.firstChild.firstChild;
selection.setBaseAndExtent(abc, 3, def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
"<div>abcdef<br><p>ghi</p></div>"]);
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 () => {
reset();
editor.innerHTML = "<div>abc <p> def<br>ghi</p></div>";
let p = editor.querySelector("p");
let def = p.firstChild;
let abc = editor.firstChild.firstChild;
selection.collapse(def, 3);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
"<div>abcdef<br><p>ghi</p></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 3,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc <p> []def<br>ghi</p></div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc <p> def<br>ghi</p></div>";
let p = editor.querySelector("p");
let def = p.firstChild;
let abc = editor.firstChild.firstChild;
selection.setBaseAndExtent(abc, abc.length, def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div>abcdef<p>ghi</p></div>",
"<div>abcdef<br><p>ghi</p></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 3,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc [<p>] def<br>ghi</p></div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<p><b>def</b></p></div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("b").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abc<b>def</b></div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc<p><b>[]def</b></p></div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div><b>abc</b><p><b>def</b></p></div>";
let abc = editor.querySelector("b").firstChild;
let def = editor.querySelector("p > b").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div><b>abc</b><b>def</b></div>",
"<div><b>abcdef</b></div>"]);
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 () => {
reset();
editor.innerHTML = "<div><i>abc</i><p><b>def</b></p></div>";
let abc = editor.querySelector("i").firstChild;
let def = editor.querySelector("b").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div><i>abc</i><b>def</b></div>");
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 () => {
reset();
editor.innerHTML = "<div><p>abc</p>def</div>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p").nextSibling;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
"<div><p>abcdef<br></p></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div><p>abc</p>[]def</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div><p>abc</p>def</div>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p").nextSibling;
selection.setBaseAndExtent(abc, 3, def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
"<div><p>abcdef<br></p></div>"]);
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 () => {
reset();
editor.innerHTML = "<div><p>abc </p> def</div>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p").nextSibling;
selection.collapse(def, 3);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
"<div><p>abcdef<br></p></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 3,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div><p>abc </p> []def</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div><p>abc </p> def</div>";
let abc = editor.querySelector("p").firstChild;
let def = editor.querySelector("p").nextSibling;
selection.setBaseAndExtent(abc, abc.length, def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div><p>abcdef</p></div>",
"<div><p>abcdef<br></p></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 3,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div><p>abc [</p>] def</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div><p><b>abc</b></p>def</div>";
let abc = editor.querySelector("b").firstChild;
let def = editor.querySelector("p").nextSibling;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div><p><b>abc</b>def</p></div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div><p><b>abc</b></p>[]def</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div><p><b>abc</b></p><b>def</b></div>";
let abc = editor.querySelector("b").firstChild;
let def = editor.querySelector("div > b").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div><p><b>abc</b><b>def</b></p></div>",
"<div><p><b>abcdef</b></p></div>"]);
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 () => {
reset();
editor.innerHTML = "<div><p><b>abc</b></p><i>def</i></div>";
let abc = editor.querySelector("b").firstChild;
let def = editor.querySelector("i").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div><p><b>abc</b><i>def</i></p></div>");
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 () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("li").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc<ul><li>[]def</li></ul>ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc <ul><li> def </li></ul> ghi</div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("li").firstChild;
selection.collapse(def, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc <ul><li> []def </li></ul> ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc <ul><li> def </li></ul> ghi</div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("li").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc <ul><li>[] def </li></ul> ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("li").firstChild;
selection.setBaseAndExtent(abc, 3, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc[<ul><li>]def</li></ul>ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc <ul><li> def </li></ul> ghi</div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("li").firstChild;
selection.setBaseAndExtent(abc, abc.length, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdefghi</div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc [<ul><li>] def </li></ul> ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
let def = editor.querySelector("li").firstChild;
let ghi = editor.querySelector("ul").nextSibling;
selection.collapse(ghi, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul></div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: def,
startOffset: 3,
endContainer: ghi,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc<ul><li>def</li></ul>[]ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc <ul><li> def </li></ul> ghi</div>";
let def = editor.querySelector("li").firstChild;
let ghi = editor.querySelector("ul").nextSibling;
selection.collapse(ghi, 1);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div>abc <ul><li> defghi</li></ul></div>",
"<div>abc <ul><li>defghi</li></ul></div>",
"<div>abc<ul><li>defghi</li></ul></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: def,
startOffset: 5,
endContainer: ghi,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc <ul><li> def </li></ul> []ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc <ul><li> def </li></ul> ghi</div>";
let def = editor.querySelector("li").firstChild;
let ghi = editor.querySelector("ul").nextSibling;
selection.collapse(ghi, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div>abc <ul><li> defghi</li></ul></div>",
"<div>abc <ul><li>defghi</li></ul></div>",
"<div>abc<ul><li>defghi</li></ul></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: def,
startOffset: 5,
endContainer: ghi,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc <ul><li> def </li></ul>[] ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li></ul>ghi</div>";
let def = editor.querySelector("li").firstChild;
let ghi = editor.querySelector("ul").nextSibling;
selection.setBaseAndExtent(def, 3, ghi, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul></div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: def,
startOffset: 3,
endContainer: ghi,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc<ul><li>def[</li></ul>]ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc <ul><li> def </li></ul> ghi</div>";
let def = editor.querySelector("li").firstChild;
let ghi = editor.querySelector("ul").nextSibling;
selection.setBaseAndExtent(def, def.length, ghi, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div>abc <ul><li> defghi</li></ul></div>",
"<div>abc <ul><li>defghi</li></ul></div>",
"<div>abc<ul><li>defghi</li></ul></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: def,
startOffset: 5,
endContainer: ghi,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc <ul><li> def [</li></ul>] ghi</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("li").firstChild;
selection.collapse(def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef<ul><li>ghi</li></ul>jkl</div>");
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 () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("li").firstChild;
selection.setBaseAndExtent(abc, 3, def, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abcdef<ul><li>ghi</li></ul>jkl</div>");
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 () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
let abc = editor.querySelector("div").firstChild;
let def = editor.querySelector("li").firstChild;
let ghi = editor.querySelector("li + li").firstChild;
selection.setBaseAndExtent(abc, 3, ghi, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<div>abcghijkl</div>",
"<div>abcghijkl<br></div>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: ghi,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc[<ul><li>def</li><li>]ghi</li></ul>jkl</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
let def = editor.querySelector("li").firstChild;
let ghi = editor.querySelector("li + li").firstChild;
selection.collapse(ghi, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul>jkl</div>");
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 () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
let def = editor.querySelector("li").firstChild;
let ghi = editor.querySelector("li + li").firstChild;
selection.setBaseAndExtent(def, 3, ghi, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abc<ul><li>defghi</li></ul>jkl</div>");
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 () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
let ghi = editor.querySelector("li + li").firstChild;
let jkl = editor.querySelector("ul").nextSibling;
selection.collapse(jkl, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: ghi,
startOffset: 3,
endContainer: jkl,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc<ul><li>def</li><li>ghi</li></ul>[]jkl</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
let ghi = editor.querySelector("li + li").firstChild;
let jkl = editor.querySelector("ul").nextSibling;
selection.setBaseAndExtent(ghi, 3, jkl, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abc<ul><li>def</li><li>ghijkl</li></ul></div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: ghi,
startOffset: 3,
endContainer: jkl,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<div>abc<ul><li>def</li><li>ghi[</li></ul>]jkl</div>"');
promise_test(async () => {
reset();
editor.innerHTML = "<div>abc<ul><li>def</li><li>ghi</li></ul>jkl</div>";
let def = editor.querySelector("li").firstChild;
let jkl = editor.querySelector("ul").nextSibling;
selection.setBaseAndExtent(def, 3, jkl, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<div>abc<ul><li>defjkl</li></ul></div>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: def,
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 () => {
reset();
editor.innerHTML = "<p>abc</p><p><br></p>";
let p1 = editor.querySelector("p");
let abc = p1.firstChild;
let p2 = p1.nextSibling;
selection.collapse(p2, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc</p>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: p2,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<p>abc</p><p>{}<br></p>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc</p><p><br></p>";
let p1 = editor.querySelector("p");
let abc = p1.firstChild;
let p2 = p1.nextSibling;
selection.setBaseAndExtent(abc, 3, p2, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p>abc</p>",
"<p>abc<br></p>"]);
if (editor.innerHTML === "<p>abc</p>") {
// Include the invisible `<br>` element if it's deleted.
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: p2,
endOffset: 1,
});
} else {
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: p2,
endOffset: 0,
});
}
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 () => {
reset();
editor.innerHTML = "<p>abc</p><p><span></span><br></p>";
let p1 = editor.querySelector("p");
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let span = p2.firstChild;
selection.collapse(span, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc</p>");
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 () => {
reset();
editor.innerHTML = "<p>abc </p><p><br></p>";
let p1 = editor.querySelector("p");
let abc = p1.firstChild;
let p2 = p1.nextSibling;
selection.collapse(p2, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p>abc </p>",
"<p>abc</p>"]);
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 () => {
reset();
editor.innerHTML = "<p>abc<span contenteditable=\"false\">def</span></p><p><br></p>";
let p1 = editor.querySelector("p");
let span = editor.querySelector("span");
let p2 = p1.nextSibling;
selection.collapse(p2, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc<span contenteditable=\"false\">def</span></p>");
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 () => {
reset();
editor.innerHTML = "<p>abc</p><p contenteditable=\"false\">def</p><p><br></p>";
let p1 = editor.querySelector("p");
let abc = p1.firstChild;
let p2 = p1.nextSibling;
let p3 = p2.nextSibling;
selection.collapse(p3, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p>abc</p>",
"<p>abc</p><p contenteditable=\"false\">def</p>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p2.isConnected ? editor : 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 () => {
reset();
editor.innerHTML = "<p>abc<span contenteditable=\"false\">def</span>ghi</p>";
let p = editor.querySelector("p");
let ghi = p.lastChild;
selection.collapse(ghi, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
"<p>abcghi</p>",
"<p>abcghi<br></p>"]);
if (editor.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>"');
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc<span contenteditable=\"false\">def</span>ghi</p>";
let p = editor.querySelector("p");
let abc = p.firstChild;
let ghi = p.lastChild;
selection.setBaseAndExtent(abc, 3, ghi, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p>abc<span contenteditable=\"false\">def</span>ghi</p>",
"<p>abcghi</p>",
"<p>abcghi<br></p>"]);
// Don't need to shrink the range for avoiding to waste runtime cost.
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 3,
endContainer: ghi,
endOffset: 0,
});
if (editor.innerHTML === "<p>abc<span contenteditable=\"false\">def</span>ghi</p>") {
checkGetTargetRangesOfInputOnDoNothing();
} else {
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 () => {
reset();
editor.innerHTML = "<table><tr><td>cell</td></tr></table><p><br></p>";
let table = editor.querySelector("table");
let p = table.nextSibling;
selection.collapse(p, 0);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>cell</td></tr></tbody></table>",
"<table><tbody><tr><td>cell</td></tr></tbody></table><p><br></p>"]);
if (p.isConnected) {
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: p,
startOffset: 0,
endContainer: p,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDoNothing();
} else {
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: editor,
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 () => {
reset();
editor.innerHTML = "<table><tr><td>cell1</td><td><br></td></tr></table>";
let cell1 = editor.querySelector("td");
let cell2 = cell1.nextSibling;
selection.collapse(cell2, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<table><tbody><tr><td>cell1</td><td><br></td></tr></tbody></table>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: cell2,
startOffset: 0,
endContainer: cell2,
endOffset: 0,
});
checkGetTargetRangesOfInputOnDoNothing();
}, 'Backspace at "<table><tr><td>cell1</td><td>{}<br></td></tr></table>"');
// The table structure shouldn't be modified when deleting cell contents,
// in this case, getTargetRanges() should return multiple ranges in each
// cell?
promise_test(async () => {
reset();
editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr></table>";
let abc = editor.querySelector("td").firstChild;
let def = editor.querySelector("td + td").firstChild;
selection.setBaseAndExtent(abc, 2, def, 1);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<table><tbody><tr><td>ab</td><td>ef</td></tr></tbody></table>");
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 2,
endContainer: def,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<table><tr><td>ab[c</td><td>d]ef</td></tr></table>"');
promise_test(async () => {
reset();
editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr><tr><td>ghi</td><td>jkl</td></tr></table>";
let abc = editor.querySelector("td").firstChild;
let jkl = editor.querySelector("tr + tr > td + td").firstChild;
selection.setBaseAndExtent(abc, 2, jkl, 1);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>ab</td><td></td></tr><tr><td></td><td>kl</td></tr></tbody></table>",
"<table><tbody><tr><td>ab</td><td><br></td></tr><tr><td><br></td><td>kl</td></tr></tbody></table>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 2,
endContainer: jkl,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<table><tr><td>ab[c</td><td>def</td></tr><tr><td>ghi</td><td>j]kl</td></tr></table>"');
promise_test(async () => {
reset();
editor.innerHTML = "<table><tr><td>abc</td><td>def</td></tr></table><table><tr><td>ghi</td><td>jkl</td></tr></table>";
let abc = editor.querySelector("td").firstChild;
let jkl = editor.querySelector("table + table td + td").firstChild;
selection.setBaseAndExtent(abc, 2, jkl, 1);
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<table><tbody><tr><td>ab</td><td></td></tr></tbody></table><table><tbody><tr><td></td><td>kl</td></tr></tbody></table>",
"<table><tbody><tr><td>ab</td><td><br></td></tr></tbody></table><table><tbody><tr><td><br></td><td>kl</td></tr></tbody></table>"]);
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: abc,
startOffset: 2,
endContainer: jkl,
endOffset: 1,
});
checkGetTargetRangesOfInputOnDeleteSomething();
}, 'Backspace at "<table><tr><td>ab[c</td><td>def</td></tr></table><table><tr><td>ghi</td><td>j]kl</td></tr></table>"');
// If table caption won't be deleted, target range should be collapsed in the
// caption element.
promise_test(async () => {
reset();
editor.innerHTML = "<p>abc</p><table><caption><br></caption><tr><td>cell</td></tr></table>";
let caption = editor.querySelector("caption");
selection.collapse(caption, 0);
await sendBackspaceKey();
assert_equals(editor.innerHTML, "<p>abc</p><table><caption><br></caption><tbody><tr><td>cell</td></tr></tbody></table>");
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 caret is not adjacent of deleting character, browser may not delete the
// character, but update the caret position for next deletion.
promise_test(async () => {
reset();
editor.innerHTML = "<p>&#x5E9;&#x5DC;&#x5D5;&#x5DD;hello</p>";
let text1 = editor.querySelector("p").firstChild;
let text2 = text1.nextSibling;
selection.collapse(text2 ? text2 : text1, text2 ? 1 : 5);
await sendArrowLeftKey();
await sendBackspaceKey();
assert_in_array(editor.innerHTML, ["<p>\u05E9\u05DC\u05D5\u05DDhello</p>",
"<p>\u05DC\u05D5\u05DDhello</p>",
"<p>\u05E9\u05DC\u05D5hello</p>"]);
if (editor.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 (editor.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 () => {
reset();
const kText = "abc def ghi";
editor.innerHTML = `<p>${kText}</p>`;
let p = editor.querySelector("p");
selection.collapse(p.firstChild, "abc def".length);
await sendBackspaceKey(kShift);
let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
let length = kText.length - p.firstChild.data.length;
assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
`<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
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 () => {
reset();
const kText = "abc def ghi";
editor.innerHTML = `<p>${kText}</p>`;
let p = editor.querySelector("p");
selection.collapse(p.firstChild, "abc def".length);
await sendBackspaceKey(kControl);
let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
let length = kText.length - p.firstChild.data.length;
assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
`<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
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 () => {
reset();
const kText = "abc def ghi";
editor.innerHTML = `<p>${kText}</p>`;
let p = editor.querySelector("p");
selection.collapse(p.firstChild, "abc def".length);
await sendBackspaceKey(kAlt);
let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
let length = kText.length - p.firstChild.data.length;
assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
`<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
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 () => {
reset();
const kText = "abc def ghi";
editor.innerHTML = `<p>${kText}</p>`;
let p = editor.querySelector("p");
selection.collapse(p.firstChild, "abc def".length);
await sendBackspaceKey(kMeta);
let startOffset = getFirstDifferentOffset(p.firstChild.data, kText);
let length = kText.length - p.firstChild.data.length;
assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
`<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
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 () => {
reset();
const kText = "abc def";
editor.innerHTML = `<p> ${kText}</p>`;
let p = editor.querySelector("p");
selection.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.
assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
`<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
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 () => {
reset();
const kText = "abc def";
editor.innerHTML = `<p> ${kText}</p>`;
let p = editor.querySelector("p");
selection.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.
assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
`<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
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 () => {
reset();
const kText = "abc def";
editor.innerHTML = `<p> ${kText}</p>`;
let p = editor.querySelector("p");
selection.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.
assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
`<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
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 () => {
reset();
const kText = "abc def";
editor.innerHTML = `<p> ${kText}</p>`;
let p = editor.querySelector("p");
selection.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.
assert_equals(editor.innerHTML.replace(/&nbsp;/g, " "),
`<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`);
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>