Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
*/
"use strict";
/**
* Bug 2019067: MousePosTracker translates mousemove screen coordinates into
* chrome CSS pixels. When the source document - such as the docked devtools toolbox -
* is at a different zoom (and thus a different devicePixelRatio) than chrome,
* event.screenX/Y are in toolbox CSS pixels and must be rescaled to chrome
* CSS pixels before mozInnerScreenX/Y is subtracted. Without rescaling,
* any chrome-side hover listener (sidebar launcher, fullscreen autohide, etc.)
* sees coordinates that don't match its own rects.
*/
Services.scriptloader.loadSubScript(
this
);
const TOOLBOX_ZOOM = 2.0;
const OFFSET_X = 30;
const OFFSET_Y = 30;
function makeListener(rect) {
return {
enterCount: 0,
leaveCount: 0,
getMouseTargetRect: () => rect,
onMouseEnter() {
this.enterCount++;
},
onMouseLeave() {
this.leaveCount++;
},
};
}
add_task(async function test_mousepostracker_devtools_zoom_rescaling() {
await SpecialPowers.pushPrefEnv({
set: [
["devtools.toolbox.zoomValue", String(TOOLBOX_ZOOM)],
["devtools.toolbox.host", "bottom"],
],
});
await BrowserTestUtils.withNewTab("about:blank", async browser => {
const tab = gBrowser.getTabForBrowser(browser);
const toolbox = await openToolboxForTab(tab);
const toolboxWin = toolbox.win;
Assert.equal(
toolboxWin.browsingContext.fullZoom,
TOOLBOX_ZOOM,
"Toolbox is at the configured zoom"
);
Assert.notEqual(
toolboxWin.devicePixelRatio,
window.devicePixelRatio,
"Toolbox devicePixelRatio differs from chrome window"
);
// The chrome-CSS-pixel point of (OFFSET_X, OFFSET_Y) inside the toolbox
// document is the toolbox browser's chrome-relative top-left plus the
// offset scaled by toolbox-DPR / chrome-DPR.
const devtoolsRect =
toolboxWin.browsingContext.embedderElement.getBoundingClientRect();
const scale = toolboxWin.devicePixelRatio / window.devicePixelRatio;
const expectedX = devtoolsRect.left + OFFSET_X * scale;
const expectedY = devtoolsRect.top + OFFSET_Y * scale;
const insideListener = makeListener({
left: expectedX - 5,
right: expectedX + 5,
top: expectedY - 5,
bottom: expectedY + 5,
});
const outsideListener = makeListener({
left: expectedX + 100,
right: expectedX + 200,
top: expectedY + 100,
bottom: expectedY + 200,
});
const mouseMoved = BrowserTestUtils.waitForEvent(window, "mousemove");
EventUtils.synthesizeMouse(
toolboxWin.document.documentElement,
OFFSET_X,
OFFSET_Y,
{ type: "mousemove" },
toolboxWin
);
await mouseMoved;
// addListener immediately runs the rect-vs-position check. If
// MousePosTracker rescaled the toolbox screenX/Y into chrome CSS pixels
// correctly, _x/_y falls inside insideListener's rect (onMouseEnter
// fires) and outside outsideListener's rect (onMouseEnter does not).
MousePosTracker.addListener(insideListener);
MousePosTracker.addListener(outsideListener);
try {
Assert.equal(
insideListener.enterCount,
1,
"Listener with rect around the synthesized point received onMouseEnter"
);
Assert.equal(
outsideListener.enterCount,
0,
"Listener with rect away from the synthesized point did not receive onMouseEnter"
);
} finally {
MousePosTracker.removeListener(insideListener);
MousePosTracker.removeListener(outsideListener);
}
await closeToolboxForTab(tab);
});
});