After a top-level navigation, the new PresShell has no per-PresShell mouse state until a real mouse event arrives. PresShell::SynthesizeMouseMove bails on that empty state, so a JS-driven cursor or :hover change (e.g. `body.style.cursor = 'none'` triggered by a keypress) does not take effect until the user moves the mouse. PointerEventHandler still holds the static last-mouse state from before the navigation. When the previous owning PresShell has been torn down, claim that state for the new PresShell. The existing path in ProcessSynthMouseMoveEvent then dispatches a synthetic eMouseMove at the cached position. Gate the claim with three checks. Skip if another live PresShell still owns the state (e.g. a popup). Skip unless the widget that originally recorded the state matches aRootPresShell's widget, since PresShell::IsRoot() means root of the in-process presContext tree and the previous owner could have been on a different top-level window in the same content process. And use a new sLastMousePointerId static for the recovered pointerId, since sLastPointerId tracks any pointer (including touch) and would mismatch the eMouseMove dispatched downstream. Differential Revision: https://phabricator.services.mozilla.com/D301688
67 lines
2.3 KiB
HTML
67 lines
2.3 KiB
HTML
<!doctype html>
|
|
<style>
|
|
html, body { margin: 0; padding: 0; height: 100%; }
|
|
body { background: white; }
|
|
body:hover { background: red; }
|
|
</style>
|
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
|
<script>
|
|
async function runTest() {
|
|
const isReloaded = sessionStorage.getItem("bug2038491_reloaded") === "true";
|
|
|
|
if (!isReloaded) {
|
|
// First load: synthesize a real mouse move at a known position so the
|
|
// root PresShell records the mouse state, then reload to drop that
|
|
// PresShell.
|
|
synthesizeMouse(document.body, 100, 100, { type: "mousemove" });
|
|
|
|
// Wait one paint tick so the synth move propagates and :hover is applied.
|
|
await new Promise(r => requestAnimationFrame(r));
|
|
await new Promise(r => requestAnimationFrame(r));
|
|
|
|
const bg = getComputedStyle(document.body).backgroundColor;
|
|
if (bg !== "rgb(255, 0, 0)") {
|
|
opener.reportResult(
|
|
false,
|
|
`Setup: body should be red (hovered) after initial mouse move, got ${bg}`
|
|
);
|
|
window.close();
|
|
return;
|
|
}
|
|
|
|
sessionStorage.setItem("bug2038491_reloaded", "true");
|
|
location.reload();
|
|
return;
|
|
}
|
|
|
|
// Second load: the new root PresShell has no per-PresShell mouse state.
|
|
// PointerEventHandler::sLastMouseInfo still holds the cached position from
|
|
// before the reload, with a dead weak ref to the old PresShell. The fix
|
|
// for bug 2038491 lets PresShell::SynthesizeMouseMove claim that orphaned
|
|
// state. PresShell::UnsuppressAndInvalidate calls SynthesizeMouseMove
|
|
// when painting unsuppresses; with the fix in place a synthetic eMouseMove
|
|
// dispatches and re-evaluates :hover on the new document.
|
|
|
|
// Give the refresh driver several ticks to dispatch the synthetic mouse
|
|
// move.
|
|
for (let i = 0; i < 10; i++) {
|
|
await new Promise(r => requestAnimationFrame(r));
|
|
}
|
|
|
|
const bg = getComputedStyle(document.body).backgroundColor;
|
|
const passed = bg === "rgb(255, 0, 0)";
|
|
sessionStorage.removeItem("bug2038491_reloaded");
|
|
opener.reportResult(
|
|
passed,
|
|
passed
|
|
? "body is red (hovered) after navigation without a fresh mouse move"
|
|
: `body should be red (hovered) after navigation, got ${bg}`
|
|
);
|
|
window.close();
|
|
}
|
|
|
|
SimpleTest.waitForFocus(runTest);
|
|
</script>
|
|
<body></body>
|