Bug 1893119: Part 5 - Fix coordinates use in dispatchDOMEventViaPresShellForTesting r=masayuki,mconley,places-reviewers,tabbrowser-reviewers,dao

Since we now add the widget to the event in
dispatchDOMEventViaPresShellForTesting, WidgetGUIEvents that are sent in
mochitests via that method need to transform their screen coordinates by
nsIWidget::WidgetToScreenOffset, to mirror the transformation by
BrowserParent::TransformParentToChild that they don't get because they
skip the parent process.

This exposes a bunch of things that were done to work around this bug.  They
are cleaned up here.

Differential Revision: https://phabricator.services.mozilla.com/D211068
This commit is contained in:
David P 2024-07-04 07:48:04 +00:00
Родитель 7c55f66983
Коммит 18f01ba217
6 изменённых файлов: 141 добавлений и 77 удалений

Просмотреть файл

@ -142,9 +142,63 @@ add_task(async function test() {
)
);
// synthesizeDrop will reference the center of the elements we pass. Since
// dragging to anything from the center and below is considered _after_ an
// element, and we are dragging up, we need to drag _just past_ the center
// (1 pixel) to drop before the target element.
let nodebound = placesItems.childNodes[0].getBoundingClientRect();
let [centerX, centerY] = [
nodebound.left + nodebound.width / 2,
nodebound.top + nodebound.height / 2,
];
EventUtils.synthesizeDrop(
placesItems,
placesItems.childNodes[1],
placesItems.childNodes[0],
[
[
{
type: "text/x-moz-place",
data: PlacesUtils.wrapNode(
placesItems.childNodes[0]._placesNode,
"text/x-moz-place"
),
},
],
],
"move",
window,
window,
{
clientX: centerX - 1,
clientY: centerY - 1,
}
);
await urlMoveNotification;
Assert.ok(spy.notCalled, "ShowBookmarksDialog not called on move for url");
info("Moving existing Bookmarklet from position [2] to [1] on Toolbar");
let bookmarkletMoveNotification = PlacesTestUtils.waitForNotification(
"bookmark-moved",
events =>
events.some(
e =>
e.parentGuid === PlacesUtils.bookmarks.toolbarGuid &&
e.oldParentGuid === PlacesUtils.bookmarks.toolbarGuid &&
e.oldIndex == 2 &&
e.index == 1
)
);
nodebound = placesItems.childNodes[1].getBoundingClientRect();
[centerX, centerY] = [
nodebound.left + nodebound.width / 2,
nodebound.top + nodebound.height / 2,
];
EventUtils.synthesizeDrop(
placesItems.childNodes[2],
placesItems.childNodes[1],
[
[
{
@ -157,44 +211,15 @@ add_task(async function test() {
],
],
"move",
window
window,
window,
{
clientX: centerX - 1,
clientY: centerY - 1,
}
);
await urlMoveNotification;
Assert.ok(spy.notCalled, "ShowBookmarksDialog not called on move for url");
info("Moving existing Bookmarklet from position [2] to [1] on Toolbar");
let bookmarkletMoveNotificatio = PlacesTestUtils.waitForNotification(
"bookmark-moved",
events =>
events.some(
e =>
e.parentGuid === PlacesUtils.bookmarks.toolbarGuid &&
e.oldParentGuid === PlacesUtils.bookmarks.toolbarGuid &&
e.oldIndex == 2 &&
e.index == 1
)
);
EventUtils.synthesizeDrop(
toolbar,
placesItems.childNodes[1],
[
[
{
type: "text/x-moz-place",
data: PlacesUtils.wrapNode(
placesItems.childNodes[2]._placesNode,
"text/x-moz-place"
),
},
],
],
"move",
window
);
await bookmarkletMoveNotificatio;
await bookmarkletMoveNotification;
Assert.ok(spy.notCalled, "ShowBookmarksDialog not called on move for url");
sandbox.restore();
});

Просмотреть файл

@ -268,7 +268,7 @@ async function dragAndDrop(
let event = {
ctrlKey: copy,
altKey: copy,
clientX: rect.left + rect.width / 2 + 10 * (afterTab ? 1 : -1),
clientX: rect.left + rect.width / 2 + (afterTab ? 1 : -1),
clientY: rect.top + rect.height / 2,
};

Просмотреть файл

@ -2298,7 +2298,18 @@ NS_IMETHODIMP nsDOMWindowUtils::DispatchDOMEventViaPresShellForTesting(
WidgetGUIEvent* guiEvent = internalEvent->AsGUIEvent();
if (guiEvent && !guiEvent->mWidget) {
auto* pc = GetPresContext();
guiEvent->mWidget = pc ? pc->GetRootWidget() : nullptr;
auto* widget = pc ? pc->GetRootWidget() : nullptr;
// In content, screen coordinates would have been
// transformed by BrowserParent::TransformParentToChild
// so we do that here.
if (widget) {
guiEvent->mWidget = widget;
// Setting the widget makes the event's mRefPoint coordinates
// widget-relative, so we transform them from being
// screen-relative here.
guiEvent->mRefPoint -= widget->WidgetToScreenOffset();
}
}
targetDoc->FlushPendingNotifications(FlushType::Layout);

Просмотреть файл

@ -45,8 +45,13 @@ add_task(async function test_drag_coords() {
await Promise.all(promises);
info(JSON.stringify(coords));
for (let coordType of ["screen", "client"]) {
is(coords.dragend[coordType].x, coords.dragstart[coordType].x + 12, `x ${coordType} is correct`);
is(coords.dragend[coordType].y, coords.dragstart[coordType].y + 12, `y ${coordType} is correct`);
// Let (tx,ty) = (target.x, target.y). Then synthesizePlainDragAndDrop
// sends mousedown/dragstart at (tx + srcX, ty + srcY), or (tx + 2, ty + 2).
// It later sends dragend at (tx + srcX + 2*stepX, ty + srcY + 2*stepY),
// or (tx + 22, ty + 22). In other words, the position at dragend should
// be the position at dragstart, but offset by (20, 20).
is(coords.dragend[coordType].x, coords.dragstart[coordType].x + 20, `x ${coordType} is correct`);
is(coords.dragend[coordType].y, coords.dragstart[coordType].y + 20, `y ${coordType} is correct`);
}
});
</script>

Просмотреть файл

@ -18,6 +18,10 @@
<script type="application/javascript">
const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
SimpleTest.waitForExplicitFinish();
function checkInputEvent(aEvent, aExpectedTarget, aInputType, aData, aDataTransfer, aTargetRanges, aDescription) {
@ -935,6 +939,10 @@ 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";
container.innerHTML = "<div contenteditable><b>bold</b> <span>MMMM</span></div>";
@ -964,10 +972,16 @@ async function doTest() {
}
)
) {
todo_is(contenteditable.innerHTML, "<b>bd</b> <span>MM<b>ol</b>MM</span>",
`${description}: dragged range should be removed from contenteditable`);
todo_isnot(contenteditable.innerHTML, "<b>bd</b> <span>MMMM</span><b>ol</b>",
`${description}: dragged range should be removed from contenteditable`);
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(beforeinputEvents.length, 2,
`${description}: 2 "beforeinput" events should be fired on contenteditable`);
checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
@ -977,8 +991,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: 4,
endContainer: lastTextNode, endOffset: 4}], // XXX unexpected
[{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
description);
is(inputEvents.length, 2,
`${description}: 2 "input" events should be fired on contenteditable`);
@ -1025,10 +1039,16 @@ async function doTest() {
}
)
) {
todo_is(contenteditable.innerHTML, "<b>bold</b> <span>MM<b>ol</b>MM</span>",
`${description}: dragged range shouldn't be removed from contenteditable`);
todo_isnot(contenteditable.innerHTML, "<b>bold</b> <span>MMMM</span><b>ol</b>",
`${description}: dragged range shouldn't be removed from contenteditable`);
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(beforeinputEvents.length, 2,
`${description}: 2 "beforeinput" events should be fired on contenteditable`);
checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
@ -1038,8 +1058,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: 4,
endContainer: lastTextNode, endOffset: 4}], // XXX unexpected
[{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
description);
is(inputEvents.length, 1,
`${description}: only one "input" event should be fired on contenteditable`);
@ -1094,11 +1114,12 @@ 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: 4,
endContainer: lastTextNode, endOffset: 4}],
[{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
description);
is(inputEvents.length, 1,
`${description}: only one "input" event should be fired on contenteditable`);
@ -1141,17 +1162,23 @@ async function doTest() {
}
)
) {
todo_is(contenteditable.innerHTML, "<b>bold</b> <span>MM<b>ol</b>MM</span>",
`${description}: dragged range shouldn't be removed from contenteditable`);
todo_isnot(contenteditable.innerHTML, "<b>bold</b> <span>MMMM</span><b>ol</b>",
`${description}: dragged range shouldn't be removed from contenteditable`);
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(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: 4,
endContainer: lastTextNode, endOffset: 4}], // XXX unexpected
[{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
description);
is(inputEvents.length, 1,
`${description}: only 1 "input" events should be fired on contenteditable`);
@ -2084,9 +2111,7 @@ async function doTest() {
) {
is(textarea.value, "Linne2",
`${description}: dragged range should be removed from <textarea>`);
todo_is(contenteditable.innerHTML, "<div>e1</div><div>Li</div>",
`${description}: dragged content should be inserted into contenteditable`);
todo_isnot(contenteditable.innerHTML, "e1<br>Li<br>",
is(contenteditable.innerHTML, "e1<br>Li<br>",
`${description}: dragged content should be inserted into contenteditable`);
is(beforeinputEvents.length, 2,
`${description}: 2 "beforeinput" events should be fired on <input> and contenteditable`);
@ -2140,9 +2165,7 @@ async function doTest() {
) {
is(textarea.value, "Line1\nLine2",
`${description}: dragged range should be removed from <textarea>`);
todo_is(contenteditable.innerHTML, "<div>e1</div><div>Li</div>",
`${description}: dragged content should be inserted into contenteditable`);
todo_isnot(contenteditable.innerHTML, "e1<br>Li<br>",
is(contenteditable.innerHTML, "e1<br>Li<br>",
`${description}: dragged content should be inserted into contenteditable`);
is(beforeinputEvents.length, 1,
`${description}: only one "beforeinput" events should be fired on contenteditable`);

Просмотреть файл

@ -387,10 +387,10 @@ function sendDragEvent(aEvent, aTarget, aWindow = window) {
cancelableArg,
viewArg,
detailArg,
screenXArg,
screenYArg,
clientXArg,
clientYArg,
Math.round(screenXArg),
Math.round(screenYArg),
Math.round(clientXArg),
Math.round(clientYArg),
ctrlKeyArg,
altKeyArg,
shiftKeyArg,
@ -3082,10 +3082,10 @@ function createDragEventObject(
var destScreenX = aDestWindow.mozInnerScreenX + destClientX;
var destScreenY = aDestWindow.mozInnerScreenY + destClientY;
if ("clientX" in aDragEvent && !("screenX" in aDragEvent)) {
aDragEvent.screenX = aDestWindow.mozInnerScreenX + aDragEvent.clientX;
destScreenX = aDestWindow.mozInnerScreenX + aDragEvent.clientX;
}
if ("clientY" in aDragEvent && !("screenY" in aDragEvent)) {
aDragEvent.screenY = aDestWindow.mozInnerScreenY + aDragEvent.clientY;
destScreenY = aDestWindow.mozInnerScreenY + aDragEvent.clientY;
}
// Wrap only in plain mochitests
@ -3854,12 +3854,12 @@ async function synthesizePlainDragAndDrop(aParams) {
// Since we don't synthesize drop event, we need to set drag end point
// explicitly for "dragEnd" event which will be fired by
// endDragSession().
dragEvent.clientX = finalX;
dragEvent.clientY = finalY;
dragEvent.clientX = srcElement.getBoundingClientRect().x + finalX;
dragEvent.clientY = srcElement.getBoundingClientRect().y + finalY;
let event = createDragEventObject(
"dragend",
destElement || srcElement,
destElement ? srcWindow : destWindow,
srcElement,
srcWindow,
null,
dragEvent
);