Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Gecko Bug 1668133] Check consistency of getTargetRanges() at calling various timing in an independent WPT #25942

Merged
merged 1 commit into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>InputEvent.getTargetRanges() should return same array in various timings</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";

// https://github.com/w3c/input-events/issues/114
function checkGetTargetRangesKeepReturningSameValue(event, desc) {
assert_equals(
getArrayOfRangesDescription(event.getTargetRanges()),
getArrayOfRangesDescription(event.cachedRanges),
`getTargetRanges() of ${event.type} event should keep returning the same array of ranges ${desc}`);
}

promise_test(async () => {
initializeTest("<p>abc</p>");
let textNode = gEditor.firstChild.firstChild;
gSelection.collapse(textNode, 2);
await sendBackspaceKey();
assert_equals(gEditor.innerHTML, "<p>ac</p>", 'Should "b" be deleted');
checkGetTargetRangesKeepReturningSameValue(gBeforeinput[0], "even after its propagation");
checkGetTargetRangesKeepReturningSameValue(gInput[0], "even after its propagation");
}, "Check consistency of getTargetRanges() result between during propagation and after propagation");

promise_test(async () => {
initializeTest("<p>abc</p>");
let textNode = gEditor.firstChild.firstChild;
gSelection.collapse(textNode, 2);
gEditor.addEventListener("beforeinput", (event) => {
assert_false(typeof event.cachedRanges === "undefined", "The beforeinput event should have cache of getTargetRanges()");
gSelection.collapse(textNode, 3);
checkGetTargetRangesKeepReturningSameValue(event, "even after changing selection");
}, {once: true});
await sendBackspaceKey();
assert_equals(gEditor.innerHTML, "<p>ab</p>", 'Should "c" be deleted');
}, "Check consistency of getTargetRanges() result between before and after changing selection in an event listener");

promise_test(async () => {
initializeTest("<p>abc</p>");
let textNode = gEditor.firstChild.firstChild;
gSelection.collapse(textNode, 2);
window.addEventListener("beforeinput", (event) => {
assert_true(typeof event.cachedRanges === "undefined", "The beforeinput event shouldn't have cached ranges yet");
gSelection.collapse(textNode, 3);
}, {once: true, capture: true});
await sendBackspaceKey();
assert_equals(gEditor.innerHTML, "<p>ab</p>", 'Should "c" be deleted');
checkGetTargetRangesOfBeforeinputOnDeleteSomething({
startContainer: textNode,
startOffset: 1,
endContainer: textNode,
endOffset: 2,
});
}, "The result of getTargetRanges() of beforeinput event should be fixed at being dispatched");

</script>
140 changes: 73 additions & 67 deletions input-events/input-events-get-target-ranges.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,66 @@ const kKeyA = "a";
const kImgSrc =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEElEQVR42mNgaGD4D8YwBgAw9AX9Y9zBwwAAAABJRU5ErkJggg==";

let gSelection = getSelection();
let gEditor = document.querySelector("div[contenteditable]");
let gBeforeinput = [];
let gInput = [];
gEditor.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();
gBeforeinput.push(e);
});
gEditor.addEventListener("input", e => {
e.cachedRanges = e.getTargetRanges();
gInput.push(e);
});
let gSelection, gEditor, gBeforeinput, gInput;

function initializeTest(aInnerHTML) {
function onBeforeinput(event) {
// 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.
event.cachedRanges = event.getTargetRanges();
gBeforeinput.push(event);
}
function onInput(event) {
event.cachedRanges = event.getTargetRanges();
gInput.push(event);
}
if (gEditor !== document.querySelector("div[contenteditable]")) {
if (gEditor) {
gEditor.isListeningToInputEvents = false;
gEditor.removeEventListener("beforeinput", onBeforeinput);
gEditor.removeEventListener("input", onInput);
}
gEditor = document.querySelector("div[contenteditable]");
}
gSelection = getSelection();
gBeforeinput = [];
gInput = [];
if (!gEditor.isListeningToInputEvents) {
gEditor.isListeningToInputEvents = true;
gEditor.addEventListener("beforeinput", onBeforeinput);
gEditor.addEventListener("input", onInput);
}

gEditor.innerHTML = aInnerHTML;
gEditor.focus();
gBeforeinput = [];
gInput = [];
}

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 getRangeDescription(range) {
function getNodeDescription(node) {
if (!node) {
Expand Down Expand Up @@ -68,27 +103,6 @@ function getRangeDescription(range) {
}) - (${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 sendDeleteKey(modifier) {
if (!modifier) {
return new test_driver.Actions()
Expand Down Expand Up @@ -140,46 +154,39 @@ function sendArrowRightKey() {
.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) {
function checkGetTargetRangesOfBeforeinputOnDeleteSomething(expectedRanges) {
assert_equals(
gBeforeinput.length,
1,
"One beforeinput event should be fired if the key operation deletes something"
"One beforeinput event should be fired if the key operation tries to delete something"
);
assert_true(
Array.isArray(gBeforeinput[0].cachedRanges),
"gBeforeinput[0].getTargetRanges() should return an array of StaticRange instances during propagation"
"gBeforeinput[0].getTargetRanges() should return an array of StaticRange instances at least during propagation"
);
// Before checking the length of array of ranges, we should check the first
// range first because the first range data is more important than whether
// there are additional unexpected ranges.
if (gBeforeinput[0].cachedRanges.length > 0) {
let arrayOfExpectedRanges = Array.isArray(expectedRanges)
? expectedRanges
: [expectedRanges];
// Before checking the length of array of ranges, we should check the given
// range first because the ranges are more important than whether there are
// redundant additional unexpected ranges.
for (
let i = 0;
i <
Math.max(arrayOfExpectedRanges.length, gBeforeinput[0].cachedRanges.length);
i++
) {
assert_equals(
getRangeDescription(gBeforeinput[0].cachedRanges[0]),
getRangeDescription(expectedRange),
`gBeforeinput[0].getTargetRanges() should return expected range (inputType is "${gBeforeinput[0].inputType}")`
);
assert_equals(
gBeforeinput[0].cachedRanges.length,
1,
"gBeforeinput[0].getTargetRanges() should return one range within an array"
getRangeDescription(gBeforeinput[0].cachedRanges[i]),
getRangeDescription(arrayOfExpectedRanges[i]),
`gBeforeinput[0].getTargetRanges()[${i}] should return expected range (inputType is "${gBeforeinput[0].inputType}")`
);
}
assert_equals(
gBeforeinput[0].cachedRanges.length,
1,
"One range should be returned from getTargetRanges() when the key operation deletes something"
arrayOfExpectedRanges.length,
`getTargetRanges() of beforeinput event should return ${arrayOfExpectedRanges.length} ranges`
);
checkGetTargetRangesKeepReturningSameValue(gBeforeinput[0]);
}

function checkGetTargetRangesOfInputOnDeleteSomething() {
Expand All @@ -191,14 +198,13 @@ function checkGetTargetRangesOfInputOnDeleteSomething() {
// https://github.com/w3c/input-events/issues/113
assert_true(
Array.isArray(gInput[0].cachedRanges),
"gInput[0].getTargetRanges() should return an array of StaticRange instances during propagation"
"gInput[0].getTargetRanges() should return an array of StaticRange instances at least during propagation"
);
assert_equals(
gInput[0].cachedRanges.length,
0,
"gInput[0].getTargetRanges() should return empty array during propagation"
);
checkGetTargetRangesKeepReturningSameValue(gInput[0]);
}

function checkGetTargetRangesOfInputOnDoNothing() {
Expand Down