Bug 1909020 - Make createDragEventObject() in EventUtils.js aware of HiDPI environments r=smaug

`synthesizePlainDragAndDrop()` sends `drop` event [1] via `PresShell` [2].
Then, the `screenX` and `screenY` values are set to
`WidgetEvent::mRefPoint` [3].  Then, the `drop` event's position is
recorded before dispatch [4].  So, `drop` event is fired on the target
but the `mRefPoint` may be outside the target.  Finally, synthesized
`eMouseMove` after `eDrop` will be fired on the wrong element which is
different from `eDrop`'s target.  This caused the failures of
`test_synthmousemove_after_dnd.html` and `test_dragdrop.html` on Android.

Perhaps, we should improve `nsDOMWindowUtils` or something lower layer
later.  Instead, this patch fixes in `EventUtils.js` level.  This makes
the `createDragEventObject()`.

1. https://searchfox.org/mozilla-central/rev/6a72a6d20eeb1b20b93862a79166938d6ce794a0/testing/mochitest/tests/SimpleTest/EventUtils.js#3893-3900
2. https://searchfox.org/mozilla-central/rev/6a72a6d20eeb1b20b93862a79166938d6ce794a0/testing/mochitest/tests/SimpleTest/EventUtils.js#376-393,400
3. https://searchfox.org/mozilla-central/rev/6a72a6d20eeb1b20b93862a79166938d6ce794a0/dom/events/MouseEvent.cpp#81-84
4. https://searchfox.org/mozilla-central/rev/6a72a6d20eeb1b20b93862a79166938d6ce794a0/layout/base/PresShell.cpp#8987

Differential Revision: https://phabricator.services.mozilla.com/D251091
This commit is contained in:
Masayuki Nakano
2025-05-24 23:34:17 +00:00
committed by masayuki@d-toybox.com
parent 842f61a7ac
commit 735e6414f0
4 changed files with 123 additions and 109 deletions

View File

@@ -939,9 +939,6 @@ async function doTest() {
})();
// -------- Test dragging contenteditable to same contenteditable
// Bug 1904272: Android Non-XOrigin incorrectly inserts after the 3rd M
// instead of after the 2nd M in some of the following tests.
const isAndroidException = AppConstants.platform === "android" && !isXOrigin;
await (async function test_dragging_from_contenteditable_to_itself() {
const description = "dragging text in contenteditable to same contenteditable";
@@ -972,16 +969,8 @@ async function doTest() {
}
)
) {
const kExpectedOffsets = isAndroidException ? [3,3] : [2,2];
if (isAndroidException) {
todo_is(contenteditable.innerHTML, "<b>bd</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range should be removed from contenteditable`);
isnot(contenteditable.innerHTML, "<b>bd</b> <span>MMMM</span><b>ol</b>",
`${description}: dragged range should be removed from contenteditable`);
} else {
is(contenteditable.innerHTML, "<b>bd</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range should be removed from contenteditable`);
}
is(contenteditable.innerHTML, "<b>bd</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range should be removed from contenteditable`);
is(beforeinputEvents.length, 2,
`${description}: 2 "beforeinput" events should be fired on contenteditable`);
checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
@@ -991,8 +980,8 @@ async function doTest() {
checkInputEvent(beforeinputEvents[1], contenteditable, "insertFromDrop", null,
[{type: "text/html", data: "<b>ol</b>"},
{type: "text/plain", data: "ol"}],
[{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
[{startContainer: lastTextNode, startOffset: 2,
endContainer: lastTextNode, endOffset: 2}],
description);
is(inputEvents.length, 2,
`${description}: 2 "input" events should be fired on contenteditable`);
@@ -1039,16 +1028,8 @@ async function doTest() {
}
)
) {
const kExpectedOffsets = isAndroidException ? [3,3] : [2,2];
if (isAndroidException) {
todo_is(contenteditable.innerHTML, "<b>bold</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range shouldn't be removed from contenteditable`);
isnot(contenteditable.innerHTML, "<b>bold</b> <span>MMMM</span><b>ol</b>",
`${description}: dragged range shouldn't be removed from contenteditable`);
} else {
is(contenteditable.innerHTML, "<b>bold</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range shouldn't be removed from contenteditable`);
}
is(contenteditable.innerHTML, "<b>bold</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range shouldn't be removed from contenteditable`);
is(beforeinputEvents.length, 2,
`${description}: 2 "beforeinput" events should be fired on contenteditable`);
checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
@@ -1058,8 +1039,8 @@ async function doTest() {
checkInputEvent(beforeinputEvents[1], contenteditable, "insertFromDrop", null,
[{type: "text/html", data: "<b>ol</b>"},
{type: "text/plain", data: "ol"}],
[{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
[{startContainer: lastTextNode, startOffset: 2,
endContainer: lastTextNode, endOffset: 2}],
description);
is(inputEvents.length, 1,
`${description}: only one "input" event should be fired on contenteditable`);
@@ -1114,12 +1095,11 @@ async function doTest() {
[{startContainer: selectionContainers[0], startOffset: 1,
endContainer: selectionContainers[1], endOffset: 3}],
description);
const kExpectedOffsets = isAndroidException ? [3,3] : [2,2];
checkInputEvent(beforeinputEvents[1], contenteditable, "insertFromDrop", null,
[{type: "text/html", data: "<b>ol</b>"},
{type: "text/plain", data: "ol"}],
[{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
[{startContainer: lastTextNode, startOffset: 2,
endContainer: lastTextNode, endOffset: 2}],
description);
is(inputEvents.length, 1,
`${description}: only one "input" event should be fired on contenteditable`);
@@ -1162,23 +1142,15 @@ async function doTest() {
}
)
) {
const kExpectedOffsets = isAndroidException ? [3,3] : [2,2];
if (isAndroidException) {
todo_is(contenteditable.innerHTML, "<b>bold</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range shouldn't be removed from contenteditable`);
isnot(contenteditable.innerHTML, "<b>bold</b> <span>MMMM</span><b>ol</b>",
`${description}: dragged range shouldn't be removed from contenteditable`);
} else {
is(contenteditable.innerHTML, "<b>bold</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range shouldn't be removed from contenteditable`);
}
is(contenteditable.innerHTML, "<b>bold</b> <span>MM</span><b>ol</b><span>MM</span>",
`${description}: dragged range shouldn't be removed from contenteditable`);
is(beforeinputEvents.length, 1,
`${description}: only 1 "beforeinput" events should be fired on contenteditable`);
checkInputEvent(beforeinputEvents[0], contenteditable, "insertFromDrop", null,
[{type: "text/html", data: "<b>ol</b>"},
{type: "text/plain", data: "ol"}],
[{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
[{startContainer: lastTextNode, startOffset: 2,
endContainer: lastTextNode, endOffset: 2}],
description);
is(inputEvents.length, 1,
`${description}: only 1 "input" events should be fired on contenteditable`);
@@ -3548,16 +3520,8 @@ async function doTest() {
}
)
) {
const kExpectedOffsets = isAndroidException ? [4,4] : [2,2];
if (isAndroidException) {
todo_is(contenteditable.innerHTML, "!!<span>MM</span>dragme<span>MM</span>",
`${description}: dragged range should be moved in inline contenteditable`);
is(contenteditable.innerHTML, "!!<span>MMMM</span>dragme",
`${description}: dragged range should be moved in inline contenteditable`);
} else {
is(contenteditable.innerHTML, "!!<span>MM</span>dragme<span>MM</span>",
`${description}: dragged range should be moved in inline contenteditable`);
}
is(contenteditable.innerHTML, "!!<span>MM</span>dragme<span>MM</span>",
`${description}: dragged range should be moved in inline contenteditable`);
is(beforeinputEvents.length, 2,
`${description}: 2 "beforeinput" events should be fired on inline contenteditable`);
checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
@@ -3567,8 +3531,8 @@ async function doTest() {
checkInputEvent(beforeinputEvents[1], contenteditable, "insertFromDrop", null,
[{type: "text/html", data: "dragme"},
{type: "text/plain", data: "dragme"}],
[{startContainer: span.firstChild, startOffset: kExpectedOffsets[0],
endContainer: span.firstChild, endOffset: kExpectedOffsets[1]}],
[{startContainer: span.firstChild, startOffset: 2,
endContainer: span.firstChild, endOffset: 2}],
description);
is(inputEvents.length, 2,
`${description}: 2 "input" events should be fired on inline contenteditable`);
@@ -3611,18 +3575,10 @@ async function doTest() {
}
)
) {
const kExpectedOffsets = isAndroidException ? [2,2] : [1,1];
is(contenteditable.innerHTML, "!!",
`${description}: dragged range should be removed from inline contenteditable`);
if (isAndroidException) {
todo_is(otherContenteditable.innerHTML, "MdragmeM",
`${description}: dragged content should be inserted into other inline contenteditable`);
is(otherContenteditable.innerHTML, "MMdragme",
`${description}: dragged content should be inserted into other inline contenteditable`);
} else {
is(otherContenteditable.innerHTML, "MdragmeM",
`${description}: dragged content should be inserted into other inline contenteditable`);
}
is(otherContenteditable.innerHTML, "MdragmeM",
`${description}: dragged content should be inserted into other inline contenteditable`);
is(beforeinputEvents.length, 2,
`${description}: 2 "beforeinput" events should be fired on inline contenteditable`);
checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
@@ -3632,8 +3588,8 @@ async function doTest() {
checkInputEvent(beforeinputEvents[1], otherContenteditable, "insertFromDrop", null,
[{type: "text/html", data: "dragme"},
{type: "text/plain", data: "dragme"}],
[{startContainer: otherContenteditable.firstChild, startOffset: kExpectedOffsets[0],
endContainer: otherContenteditable.firstChild, endOffset: kExpectedOffsets[1]}],
[{startContainer: otherContenteditable.firstChild, startOffset: 1,
endContainer: otherContenteditable.firstChild, endOffset: 1}],
description);
is(inputEvents.length, 2,
`${description}: 2 "input" events should be fired on inline contenteditable`);