Source code
Revision control
Copy as Markdown
Other Tools
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Test for async-scrolling an OOPIF and ensuring hit-testing still works</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<script src="helper_fission_utils.js"></script>
<script src="apz_test_utils.js"></script>
<script src="apz_test_native_event_utils.js"></script>
<script>
const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
async function getWindowProtocol() {
return await SpecialPowers.spawnChrome([], () => {
try {
return Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo)
.windowProtocol;
} catch {
return "";
}
});
}
function getEventPromise(eventName) {
let eventPromise = new Promise(resolve => {
const listener = event => {
if (event.data === eventName) {
window.removeEventListener("message", listener);
resolve();
}
}
window.addEventListener("message", listener);
});
return eventPromise;
}
function getClickPromise() {
let clickPromise = new Promise(resolve => {
window.addEventListener("message", event => {
let data = JSON.parse(event.data);
if ("type" in data && data.type == "clicked") {
let coordinates = {
offsetX: data.coords.offsetX,
offsetY: data.coords.offsetY
};
resolve(coordinates);
}
}, { once: true });
});
return clickPromise;
}
function getScrollYPromise() {
let scrollPromise = new Promise(resolve => {
window.addEventListener("message", event => {
let eventData = JSON.parse(event.data);
if ("type" in eventData && eventData.type == "scrolled") {
resolve(eventData.scrollY);
}
}, { once: true });
});
return scrollPromise;
}
async function clickOnIframe(x, y) {
let clickPromise = getClickPromise();
await synthesizeNativeMouseEventWithAPZ(
{ type: "click", target: document.body, offsetX: x, offsetY: y },
() => dump("Finished synthesizing click, waiting for OOPIF message...\n")
);
return clickPromise;
}
async function makeIframeScrollable(iframe) {
let parentScrollListenerReady = getEventPromise("scrollableReady");
let iframeScrollMaxY = await SpecialPowers.spawn(iframe, [], async () => {
// Ensure the oopif is scrollable, and wait for the paint so that the
// compositor also knows it's scrollable.
content.document.body.style.height = "200vh";
await content.wrappedJSObject.promiseApzFlushedRepaints();
let scrollMaxY = content.window.scrollMaxY;
// Also register a scroll listener for when it actually gets scrolled.
content.window.addEventListener("scroll", function () {
dump(`OOPIF got scroll event, now at ${content.window.scrollY}\n`);
let data = JSON.stringify({
type: "scrolled",
scrollY: content.window.scrollY
});
content.window.parent.postMessage(data, "*");
}, { once: true });
content.window.parent.postMessage("scrollableReady", "*");
return scrollMaxY;
});
await parentScrollListenerReady;
return iframeScrollMaxY;
}
async function registerIframeClickListener(iframe) {
let readyPromise = getEventPromise("listenerReady");
let iframeClickReady = await SpecialPowers.spawn(iframe, [], async () => {
content.document.addEventListener("click", (e) => {
let data = JSON.stringify({
type: "clicked",
coords: {
offsetX: e.clientX,
offsetY: e.clientY
}
});
content.window.parent.postMessage(data, "*");
});
content.window.parent.postMessage("listenerReady", "*");
return true;
});
await readyPromise;
return iframeClickReady;
}
/*------------------------------------------------------------------------*/
async function test() {
// Skip on Wayland
const protocol = await getWindowProtocol();
if (AppConstants.platform === "linux" && protocol === "wayland") {
ok(true, "Skipping this test on Linux with Wayland");
return;
}
ok(SpecialPowers.getBoolPref("apz.paint_skipping.enabled"),
"Paint-skipping is expected to be enabled for this test to be meaningful");
// Set up iframe
let iframe = document.getElementById("testframe");
await setupCrossOriginIFrame(iframe, "helper_fission_plain.html");
// Register click listener in iframe
let iframeClickReady = await registerIframeClickListener(iframe);
ok(iframeClickReady, "Successfully installed click listener in OOP iframe");
// hit-test into the iframe before scrolling
let oldClickPoint = await clickOnIframe(50, 250);
// Ensure parent window is at 0 scroll position before scrolling
is(window.scrollY, 0, "window is at 0 scroll position");
// do an APZ scroll and wait for the main-thread to get the repaint request,
// and queue up a paint-skip scroll notification back to APZ.
let scrollend = promiseScrollend(window);
await promiseMoveMouseAndScrollWheelOver(document.body, 10, 10);
await scrollend;
await promiseOnlyApzControllerFlushed();
await promiseAllPaintsDone();
ok(window.scrollY > 5, "window has scrolled by " + window.scrollY + " pixels");
// hit-test into the iframe after scrolling. The coordinates here are the
// same relative to the body as before, but get computed to be different
// relative to the window/screen.
let newClickPoint = await clickOnIframe(50, 250);
is(newClickPoint.x, oldClickPoint.x, "x-coord of old and new match");
is(newClickPoint.y, oldClickPoint.y, "y-coord of old and new match");
// Also check that we can send scroll events to the OOPIF. Any wheel events
// delivered to this page after this point should result in a failure.
addEventListener("wheel", function () {
ok(false, "Got unwanted wheel event in parent document");
});
// Ensure iframe is at 0 scroll position
const iframeY = await SpecialPowers.spawn(iframe, [], () => {
return content.window.scrollY;
});
is(iframeY, 0, "scrollY of iframe should be 0 initially");
// Ensure the OOPIF is scrollable.
let iframeScrollMaxY = await makeIframeScrollable(iframe);
ok(iframeScrollMaxY > 0, "iframe successfully made scrollable");
// Now scroll over the OOP-iframe (we know it must be under the 50,250 point
// because we just checked that above). Note that listening for wheel/scroll
// events is trickier because they will fire in the OOPIF, so we can't just
// use promiseMoveMouseAndScrollWheelOver directly.
let scrolledPromise = getScrollYPromise();
await synthesizeNativeWheel(document.body, 50, 250, 0, -10);
let iframeScrollY = await scrolledPromise;
ok(iframeScrollY > 0, "Confirmed that oopif is scrollable");
}
waitUntilApzStable()
.then(test)
.then(subtestDone, subtestFailed);
</script>
</head>
<body>
<iframe style="margin-top: 200px" id="testframe"></iframe>
<div style="height: 5000px">tall div to make the page scrollable</div>
</body>
</html>