Source code

Revision control

Copy as Markdown

Other Tools

<meta charset="utf-8">
<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>
.then(loadOOPIFrame("testframe", "helper_fission_empty.html"))
.then(FissionTestHelper.subtestDone, FissionTestHelper.subtestFailed);
let code_for_oopif_to_run = function() {
document.addEventListener("click", function(e) {
dump(`OOPIF got click at ${e.clientX},${e.clientY}\n`);
let result = { x: e.clientX, y: e.clientY };
FissionTestHelper.fireEventInEmbedder("OOPIF:ClickData", result);
dump("OOPIF registered click listener\n");
return true;
async function clickOnIframe(x, y) {
let iframePromise = promiseOneEvent(window, "OOPIF:ClickData", null);
await synthesizeNativeMouseEventWithAPZ(
{ type: "click", target: document.body, offsetX: x, offsetY: y },
() => dump("Finished synthesizing click, waiting for OOPIF message...\n")
let iframeResponse = await iframePromise;
dump("OOPIF response: " + JSON.stringify( + "\n");
let oopif_scroll_pos = function() {
dump(`OOPIF scroll position is y=${window.scrollY}\n`);
let result = { y: window.scrollY };
FissionTestHelper.fireEventInEmbedder("OOPIF:ScrollPos", result);
return true;
async function getIframeScrollY() {
let iframeElement = document.getElementById("testframe");
let iframePromise = promiseOneEvent(window, "OOPIF:ScrollPos", null);
ok(await FissionTestHelper.sendToOopif(iframeElement, `(${oopif_scroll_pos})()`), "Sent scrollY request");
let iframeResponse = await iframePromise;
dump("OOPIF response for scrollPos: " + JSON.stringify( + "\n");
let make_oopif_scrollable = function() {
// ensure the oopif is scrollable, and wait for the paint so that the
// compositor also knows it's scrollable. = "200vh";
promiseApzFlushedRepaints().then(() => {
let result = { y: window.scrollMaxY };
FissionTestHelper.fireEventInEmbedder("OOPIF:Scrollable", result);
// Also register a scroll listener for when it actually gets scrolled.
window.addEventListener("scroll", function(e) {
dump(`OOPIF got scroll event, now at ${window.scrollY}\n`);
let result = { y: window.scrollY };
FissionTestHelper.fireEventInEmbedder("OOPIF:Scrolled", result);
}, {once: true});
return true;
function failsafe(eventType) {
// Catch and fail faster on the case where the event ends up not going to
// the iframe like it should. Otherwise the test hangs until timeout which
// is more painful.
document.addEventListener(eventType, function(e) {
dump(`${location.href} got ${e.type} at ${e.clientX},${e.clientY}\n`);
ok(false, `The OOPIF hosting page should not have gotten the ${eventType}`);
setTimeout(FissionTestHelper.subtestFailed, 0);
}, {once: true});
// The actual test
async function test() {
"paint-skipping is expected to be enabled for this test to be meaningful");
let iframeElement = document.getElementById("testframe");
let iframeResponse = await FissionTestHelper.sendToOopif(iframeElement, `(${code_for_oopif_to_run})()`);
dump("OOPIF response: " + JSON.stringify(iframeResponse) + "\n");
ok(iframeResponse, "code_for_oopif_to_run successfully installed");
is(window.scrollY, 0, "window is at 0 scroll position");
// hit-test into the iframe before scrolling
let oldClickPoint = await clickOnIframe(50, 250);
// 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.
await promiseMoveMouseAndScrollWheelOver(document.body, 10, 10);
// The wheel scroll might have started an APZ animation, so run that to the end
var utils = SpecialPowers.getDOMWindowUtils(window);
for (var i = 0; i < 60; i++) {
// Let the repaint requests get processed
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.
let iframeY = await getIframeScrollY();
is(iframeY, 0, "scrollY of iframe should be 0 initially");
// Ensure the OOPIF is scrollable.
let scrollablePromise = promiseOneEvent(window, "OOPIF:Scrollable", null);
ok(await FissionTestHelper.sendToOopif(iframeElement, `(${make_oopif_scrollable})()`), "Made OOPIF scrollable");
let oopifScrollMaxY = (await scrollablePromise).data.y;
ok(oopifScrollMaxY > 0, "Confirmed that oopif is 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 = promiseOneEvent(window, "OOPIF:Scrolled", null);
await synthesizeNativeWheel(document.body, 50, 250, 0, -10);
iframeY = (await scrolledPromise).data.y;
ok(iframeY > 0, "scrollY of iframe should be >0 after scrolling");
<body onload="failsafe('click')">
<iframe style="margin-top: 200px" id="testframe"></iframe>
<div style="height: 5000px">tall div to make the page scrollable</div>