Files
sousa-gecko/layout/base/tests/multi-range-user-select.html
T
Masayuki Nakano 82a139a8f0 Bug 1998858 - part 2: Make Selection stop splitting user selected range to exclude non-selectable nodes r=smaug,emilio,edgar,TYLin,layout-reviewers
The other browsers do not support multiple selection ranges.  Therefore,
they never split selection ranges to exclude non-selectable nodes.
To align the behavior, we should do so.  However, to keep current
look of `Selection` which is similar to Chrome's behavior, we should
make the selection painters ignore normal selections if their content
is not selectable.

Note that this patch allows painting selected text when there are some
IME selections even if `user-select` is `none` because the normal
selection is also a part of IME selections.  Although our widget
currently do not use non-collapsed normal selection during a
composition, it may be changed to support new IME in the future.

Differential Revision: https://phabricator.services.mozilla.com/D272187
2025-11-21 00:16:55 +00:00

417 lines
14 KiB
HTML

<!DOCTYPE HTML>
<html class="reftest-wait"><head>
<meta charset="utf-8">
<title>Testcase #1 for bug 1129078</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="selection-utils.js"></script>
<style type="text/css">
@font-face {
font-family: Ahem;
src: url("Ahem.ttf");
}
html,body { margin:0; padding: 0; }
body,pre {
font-family: Ahem;
font-size: 20px;
}
span {
user-select: none;
}
x {
user-select: text;
}
</style>
</head>
<body>
<pre id="select">
2af45494-a<x>k7e-1</x><span id="span2">1e4-a0c6-a7e7
38222880-bj6d-11e4-8064-fb7b
3d649ae</span><x>4-ci5</x><span id="span3">c-11e4-995d-17b2
434351bc-dh4b-11e4-9971-4fc8
4dc0e0b4-eg4a-11e4-8c28-5319
a9631</span><x>9c8-ad7d-1</x>1e4-b312-039c
</pre>
<pre id="log" style="border:1px solid green"></pre>
<script>
window.info = parent.info;
window.is = parent.is;
window.isnot = parent.isnot;
window.ok = parent.ok;
const pre = document.getElementById("select");
var sel = window.getSelection();
function enableSelection(id) {
var span = document.getElementById(id);
span.style.MozUserSelect = 'text';
}
function setupPrevSelection() {
dragSelectPoints(pre, 300, 125, 200, 5);
}
function setupNextSelection() {
dragSelectPoints(pre, 199, 5, 300, 125);
}
var ops = {
S_ : shiftClick,
SA : shiftAccelClick,
AD : accelDragSelect,
SL : keyLeft,
SR : keyRight
}
function runTest() {
const excludeNonSelectableNodes = SpecialPowers.getBoolPref("dom.selection.exclude_non_selectable_nodes");
const firstText = pre.childNodes[0]; // [0] 1st line: 0-9 (+ leading linefeed 1)
const xBeforeSpan2 = firstText.nextSibling; // [1] 1st line: 0-4
const span2 = document.getElementById("span2"); // [2] 1st line: 0-13
// 2nd line: 14-42
// 3rd line: 43-49
const xAfterSpan2 = span2.nextSibling; // [3] 3rd line: 0-5
const span3 = document.getElementById("span3"); // [4] 3rd line: 0-16
// 4th line: 17-43
// 5th line: 44-74
// 6th line: 75-79
const xAfterSpan3 = span3.nextSibling; // [5] 6th line: 0-9
const lastText = xAfterSpan3.nextSibling; // [6] 6th line: 0-12 (+ trailing linefeed 1)
sel = window.getSelection();
sel.removeAllRanges();
document.body.offsetHeight;
var hash = window.location.hash
if (hash.substring(0,5)=="#prev")
setupPrevSelection();
else
setupNextSelection();
var op = hash.substring(6,8);
var action = ops[op];
var test = hash.substring(0,6);
if (hash.substring(0,5) == "#prev") {
if (test == "#prev1") {
if (action == keyLeft) {
keyLeft({shiftKey:true}, 2)
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 8, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 0],
]
: [[firstText, 8, lastText, 0]],
{id: "prev1: after Shift+ArrowLeft twice"}
);
} else if (action == keyRight) {
keyRight({shiftKey:true}, 2)
checkRanges(
excludeNonSelectableNodes
? [
[xBeforeSpan2.firstChild, 2, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 0],
]
: [[xBeforeSpan2.firstChild, 2, lastText, 0]],
{id: "prev1: after Shift+ArrowRight twice"}
);
} else if (action == accelDragSelect) {
accelDragSelect(pre, 30, 50);
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 1, firstText, 2],
[xBeforeSpan2.firstChild, 0, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 0],
]
: [
[firstText, 1, firstText, 2],
[xBeforeSpan2.firstChild, 0, lastText, 0],
],
{id: "prev1: after accelDragSelect"}
);
} else {
action(pre, 30);
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 1, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 0],
]
: [[firstText, 1, lastText, 0]],
{id: `prev1: after action(${action.name})`}
);
}
} else if (test == "#prev2") {
action(pre, 260);
checkRanges(
excludeNonSelectableNodes
? [
[xBeforeSpan2.firstChild, 3, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 0],
]
: [[xBeforeSpan2.firstChild, 3, lastText, 0]],
{id: `prev2: after action(${action.name})`}
);
} else if (test == "#prev3") {
enableSelection('span2');
action(pre, 400);
checkRanges(
excludeNonSelectableNodes
? [
[span2.firstChild, 5, pre, 4],
[xAfterSpan3, 0, lastText, 0],
]
: [[span2.firstChild, 5, lastText, 0]],
{id: `prev3: after action(${action.name}) with enabling selection of span2`}
);
} else if (test == "#prev4") {
action(pre, 180, 65);
checkRanges(
excludeNonSelectableNodes
? [
[xAfterSpan2.firstChild, 2, pre, 4],
[xAfterSpan3, 0, lastText, 0],
]
: [[xAfterSpan2.firstChild, 2, lastText, 0]],
{id: `prev4: after action(${action.name})`}
);
} else if (test == "#prev5") {
enableSelection('span3');
action(pre, 440, 65);
checkRanges(
[[span3.firstChild, 10, lastText, 0]],
{id: `prev5: after action(${action.name}) with enabling selection of span3`}
);
} else if (test == "#prev6") {
action(pre, 140, 125);
checkRanges(
[[xAfterSpan3.firstChild, 2, lastText, 0]],
{id: `prev6: after action(${action.name})`}
);
} else if (test == "#prev7") {
if (action == accelDragSelect) {
accelDragSelect(pre, 460, 500, 125);
checkRanges(
excludeNonSelectableNodes
? [
[xBeforeSpan2.firstChild, 0, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 0],
[lastText, 8, lastText, 10],
]
: [
[xBeforeSpan2.firstChild, 0, lastText, 0],
[lastText, 8, lastText, 10],
],
{id: "prev7: after accelDragSelect"}
);
} else {
action(pre, 500, 125);
checkRanges(
[[lastText, 0, lastText, 10]],
{id: `prev7: after action(${action.name})`}
);
}
} else if (test == "#prev8") {
if (action == accelDragSelect) {
sel.removeAllRanges();
synthesizeMouse(pre, 200, 125, {type: "mousedown", accelKey: true});
synthesizeMouse(pre, 200, 120, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 100, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 80, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 210, 60, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 60, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 60, {type: "mouseup", accelKey: true});
checkRanges(
excludeNonSelectableNodes
? [
[xAfterSpan2.firstChild, 3, pre, 4],
[xAfterSpan3, 0, xAfterSpan3.firstChild, 5],
]
: [[xAfterSpan2.firstChild, 3, xAfterSpan3.firstChild, 5]],
{id: "prev8: after dragging mouse with pressing accel key"}
);
}
}
} else {
if (test == "#next1") {
if (action == keyLeft) {
keyLeft({shiftKey:true}, 2)
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 10, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, xAfterSpan3.firstChild, 8],
]
: [[firstText, 10, xAfterSpan3.firstChild, 8]],
{id: "next1: after Shift+ArrowLeft twice"}
);
} else if (action == keyRight) {
keyRight({shiftKey:true}, 2)
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 10, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 2],
]
: [[firstText, 10, lastText, 2]],
{id: "next1: after Shift+ArrowRight twice"}
);
} else if (action == accelDragSelect) {
accelDragSelect(pre, 30, 50);
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 1, firstText, 2],
[firstText, 10, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, xAfterSpan3.firstChild, 10],
]
: [
[firstText, 1, firstText, 2],
[firstText, 10, xAfterSpan3.firstChild, 10],
],
{id: "next1: after accelDragSelect"}
);
} else {
action(pre, 30);
checkRanges(
[[firstText, 1, firstText, 10]],
{id: `next1: after action(${action.name})`}
);
}
} else if (test == "#next2") {
action(pre, 260);
checkRanges(
[[firstText, 10, xBeforeSpan2.firstChild, 3]],
{id: `next2: after action(${action.name})`}
);
} else if (test == "#next3") {
enableSelection('span2');
action(pre, 400);
checkRanges(
[[firstText, 10, span2.firstChild, 5]],
{id: `next3: after action(${action.name}) with enabling selection in span2`}
);
} else if (test == "#next4") {
action(pre, 180, 65);
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 10, pre, 2],
[xAfterSpan2, 0, xAfterSpan2.firstChild, 2],
]
: [[firstText, 10, xAfterSpan2.firstChild, 2]],
{id: `next4: after action(${action.name})`}
);
} else if (test == "#next5") {
enableSelection('span3');
action(pre, 440, 65);
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 10, pre, 2],
[xAfterSpan2, 0, span3.firstChild, 10],
]
: [[firstText, 10, span3.firstChild, 10]],
{id: `next5: after action(${action.name}) with enabling selection in span3`}
);
} else if (test == "#next6") {
action(pre, 140, 125);
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 10, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, xAfterSpan3.firstChild, 2],
]
: [[firstText, 10, xAfterSpan3.firstChild, 2]],
{id: `next6: after action(${action.name})`}
);
} else if (test == "#next7") {
if (action == keyRight) {
keyRight({shiftKey:true}, 2)
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 10, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 2],
]
: [[firstText, 10, lastText, 2]],
{id: "next7: after Shift+ArrowRight twice"}
);
} else if (action == accelDragSelect) {
accelDragSelect(pre, 460, 500, 125);
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 10, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, xAfterSpan3.firstChild, 10],
[lastText, 8, lastText, 10],
]
: [
[firstText, 10, xAfterSpan3.firstChild, 10],
[lastText, 8, lastText, 10],
],
{id: "next7: after accelDragSelect"}
);
} else {
action(pre, 500, 125);
checkRanges(
excludeNonSelectableNodes
? [
[firstText, 10, pre, 2],
[xAfterSpan2, 0, pre, 4],
[xAfterSpan3, 0, lastText, 10],
]
: [[firstText, 10, lastText, 10]],
{id: `next7: after action(${action.name}`}
);
}
} else if (test == "#next8") {
if (action == accelDragSelect) {
sel.removeAllRanges();
synthesizeMouse(pre, 200, 60, {type: "mousedown", accelKey: true});
synthesizeMouse(pre, 180, 60, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 80, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 100, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 120, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 190, 125, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 125, {type: "mousemove", accelKey: true});
synthesizeMouse(pre, 200, 125, {type: "mouseup", accelKey: true});
checkRanges(
excludeNonSelectableNodes
? [
[xAfterSpan2.firstChild, 3, pre, 4],
[xAfterSpan3, 0, xAfterSpan3.firstChild, 5],
]
: [[xAfterSpan2.firstChild, 3,xAfterSpan3.firstChild, 5]],
{id: "next8: after dragging with pressing Accel key"}
);
}
}
}
document.documentElement.removeAttribute("class");
}
SimpleTest.waitForFocus(function(){setTimeout(runTest,0)});
</script>
</body>
</html>