commit bc2a7bec5bab70e5e95db2ab04c443553b7d0ef2
parent bc28ca1f46e39e71d041386a78e0845e97c4b236
Author: Timothy Nikkel <tnikkel@gmail.com>
Date: Wed, 5 Nov 2025 01:24:09 +0000
Bug 1995207. Add test. r=hiro
Differential Revision: https://phabricator.services.mozilla.com/D270963
Diffstat:
3 files changed, 175 insertions(+), 0 deletions(-)
diff --git a/gfx/layers/apz/test/mochitest/browser.toml b/gfx/layers/apz/test/mochitest/browser.toml
@@ -52,6 +52,12 @@ support-files = [
"helper_popup_menu_in_parent_process-2.html"
]
+["browser_test_popup_menu_in_parent_process_content.js"]
+support-files = [
+ "helper_popup_menu_in_parent_process_content.html"
+]
+run-if = ["os != 'mac'"] # On Mac popup windows having no remote content doesn't have the compositor
+
["browser_test_popup_menu_in_position_fixed.js"]
support-files = ["helper_popup_menu_in_parent_process-1.html"]
run-if = ["os != 'mac'"] # On Mac popup windows having no remote content doesn't have the compositor
diff --git a/gfx/layers/apz/test/mochitest/browser_test_popup_menu_in_parent_process_content.js b/gfx/layers/apz/test/mochitest/browser_test_popup_menu_in_parent_process_content.js
@@ -0,0 +1,163 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochikit/content/tests/SimpleTest/paint_listener.js",
+ this
+);
+
+Services.scriptloader.loadSubScript(
+ new URL("apz_test_utils.js", gTestPath).href,
+ this
+);
+
+Services.scriptloader.loadSubScript(
+ new URL("apz_test_native_event_utils.js", gTestPath).href,
+ this
+);
+
+/* import-globals-from helper_browser_test_utils.js */
+// For openSelectPopup.
+Services.scriptloader.loadSubScript(
+ new URL("helper_browser_test_utils.js", gTestPath).href,
+ this
+);
+
+// Cleanup for paint_listener.js.
+add_task(() => {
+ registerCleanupFunction(() => {
+ delete window.waitForAllPaintsFlushed;
+ delete window.waitForAllPaints;
+ delete window.promiseAllPaintsDone;
+ });
+});
+
+// Setup preferences.
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["apz.popups.enabled", true],
+ ["apz.popups_without_remote.enabled", true],
+ ],
+ });
+});
+
+async function twoRafsInContent(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ await new Promise(r =>
+ content.requestAnimationFrame(() => content.requestAnimationFrame(r))
+ );
+ });
+}
+
+async function runTest(aTestFile) {
+ // To test this scenario we need to create a content document (so that
+ // isTopLevelContentDocShell and it can zoom) but that lives in the parent
+ // process. This is not done very often in tests, but is used by for example
+ // about:preferences. To do that we load about:blank and use
+ // forceNotRemote: true to put it in the parent process and then
+ // triggeringPrincipal: nullPrincipal to make sure it doesn't gain chrome
+ // privileges. We then get that tab to navigate itself to a chrome uri so
+ // that it stays in that same process but it doesn't gain chrome privileges.
+ const nullPrincipal = Services.scriptSecurityManager.createNullPrincipal({});
+ const tab = gBrowser.addTab("about:blank", {
+ forceNotRemote: true,
+ triggeringPrincipal: nullPrincipal,
+ });
+ gBrowser.selectedTab = tab;
+ const browser = tab.linkedBrowser;
+
+ // Can't wait for about:blank to load, so do this to make sure everything is
+ // settled and ready.
+ await TestUtils.waitForCondition(() => browser.isConnected);
+ await promiseApzFlushedRepaints();
+ await waitUntilApzStable();
+
+ const url_of_test_file = getRootDirectory(gTestPath) + aTestFile;
+ await SpecialPowers.spawn(
+ browser,
+ [url_of_test_file],
+ async function (_url_of_test_file) {
+ content.location = _url_of_test_file;
+ }
+ );
+
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Make sure the document gets loaded in the parent process and is a top
+ // level content document.
+ await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ Assert.ok(SpecialPowers.isMainProcess());
+ Assert.ok(SpecialPowers.wrap(content).docShell.isTopLevelContentDocShell);
+ });
+
+ await promiseApzFlushedRepaints();
+ await waitUntilApzStable();
+
+ let contentWin = browser.contentWindow;
+
+ // Create a popup menu that is inside a transform dynamically and set
+ // `position:fixed` on the menu.
+ const adiv = contentWin.document.createElement("div");
+ adiv.style.transform = "translateX(1px)";
+ contentWin.document.body.appendChild(adiv);
+ const popupset = contentWin.document.createXULElement("popupset");
+ adiv.appendChild(popupset);
+ const popup = contentWin.document.createXULElement("menupopup");
+ popup.style.position = "fixed";
+ popup.style.background = "blue";
+ popup.setAttribute("noautohide", true);
+
+ const scroller = contentWin.document.createElement("div");
+ scroller.style =
+ "width: 100px; height: 100px; overflow: auto; background-color: white;";
+ popup.appendChild(scroller);
+ const spacer = contentWin.document.createElement("div");
+ spacer.style = "width: 200px; height: 200px; background-color: green;";
+ scroller.appendChild(spacer);
+ popupset.appendChild(popup);
+
+ // Open the popup.
+ const popupshownPromise = new Promise(resolve => {
+ popup.addEventListener("popupshown", resolve());
+ });
+ popup.openPopupAtScreen(
+ contentWin.mozInnerScreenX,
+ contentWin.mozInnerScreenY,
+ true
+ );
+ await popupshownPromise;
+
+ // Make sure APZ is ready for the popup.
+ await ensureApzReadyForPopup(popup, contentWin);
+ await promiseApzFlushedRepaints(popup);
+
+ // Do a mouse click in the popup.
+ const popupRect = popup.getBoundingClientRect();
+ ok(popupRect.width > 10, "non-zero popup width");
+ ok(popupRect.height > 10, "non-zero popup height");
+ await synthesizeNativeMouseEventWithAPZ({
+ type: "click",
+ target: popup,
+ offsetX: 10,
+ offsetY: 10,
+ });
+
+ // Just wait to make sure that's processed without hitting an assert.
+ await twoRafsInContent(browser);
+
+ // Close the popup.
+ const popuphiddenPromise = new Promise(resolve => {
+ popup.addEventListener("popuphidden", resolve());
+ });
+ popup.hidePopup();
+ await popuphiddenPromise;
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async () => {
+ await runTest("helper_popup_menu_in_parent_process_content.html");
+});
diff --git a/gfx/layers/apz/test/mochitest/helper_popup_menu_in_parent_process_content.html b/gfx/layers/apz/test/mochitest/helper_popup_menu_in_parent_process_content.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<div style="overflow: auto; height: 100px;">
+ <div style="height: 400px;"></div>
+</div>
+</html>