summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/popups
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/popups')
-rw-r--r--browser/base/content/test/popups/browser.ini69
-rw-r--r--browser/base/content/test/popups/browser_popupUI.js192
-rw-r--r--browser/base/content/test/popups/browser_popup_blocker.js155
-rw-r--r--browser/base/content/test/popups/browser_popup_blocker_frames.js100
-rw-r--r--browser/base/content/test/popups/browser_popup_blocker_identity_block.js242
-rw-r--r--browser/base/content/test/popups/browser_popup_blocker_iframes.js186
-rw-r--r--browser/base/content/test/popups/browser_popup_close_main_window.js84
-rw-r--r--browser/base/content/test/popups/browser_popup_frames.js128
-rw-r--r--browser/base/content/test/popups/browser_popup_inner_outer_size.js120
-rw-r--r--browser/base/content/test/popups/browser_popup_linux_move.js56
-rw-r--r--browser/base/content/test/popups/browser_popup_linux_resize.js53
-rw-r--r--browser/base/content/test/popups/browser_popup_move.js6
-rw-r--r--browser/base/content/test/popups/browser_popup_move_instant.js6
-rw-r--r--browser/base/content/test/popups/browser_popup_new_window_resize.js51
-rw-r--r--browser/base/content/test/popups/browser_popup_new_window_size.js90
-rw-r--r--browser/base/content/test/popups/browser_popup_resize.js6
-rw-r--r--browser/base/content/test/popups/browser_popup_resize_instant.js6
-rw-r--r--browser/base/content/test/popups/browser_popup_resize_repeat.js6
-rw-r--r--browser/base/content/test/popups/browser_popup_resize_repeat_instant.js6
-rw-r--r--browser/base/content/test/popups/browser_popup_resize_revert.js6
-rw-r--r--browser/base/content/test/popups/browser_popup_resize_revert_instant.js6
-rw-r--r--browser/base/content/test/popups/head.js574
-rw-r--r--browser/base/content/test/popups/popup_blocker.html13
-rw-r--r--browser/base/content/test/popups/popup_blocker2.html10
-rw-r--r--browser/base/content/test/popups/popup_blocker_10_popups.html14
-rw-r--r--browser/base/content/test/popups/popup_blocker_a.html1
-rw-r--r--browser/base/content/test/popups/popup_blocker_b.html1
-rw-r--r--browser/base/content/test/popups/popup_blocker_frame.html27
-rw-r--r--browser/base/content/test/popups/popup_size.html16
29 files changed, 2230 insertions, 0 deletions
diff --git a/browser/base/content/test/popups/browser.ini b/browser/base/content/test/popups/browser.ini
new file mode 100644
index 0000000000..710ea28633
--- /dev/null
+++ b/browser/base/content/test/popups/browser.ini
@@ -0,0 +1,69 @@
+[DEFAULT]
+support-files =
+ head.js
+ popup_blocker_a.html # used as dummy file
+prefs =
+ # TODO: Port browser_popup_{move,move_instant,resize}.js to use move/resizeTo
+ # instead of individual properties.
+ dom.window_position_size_properties_replaceable.enabled=false
+[browser_popupUI.js]
+[browser_popup_blocker.js]
+support-files =
+ popup_blocker.html
+ popup_blocker_a.html
+ popup_blocker_b.html
+ popup_blocker_10_popups.html
+skip-if = (os == 'linux') || debug # Frequent bug 1081925 and bug 1125520 failures
+[browser_popup_blocker_frames.js]
+https_first_disabled = true
+support-files =
+ popup_blocker.html
+ popup_blocker_a.html
+ popup_blocker_b.html
+[browser_popup_blocker_identity_block.js]
+https_first_disabled = true
+support-files =
+ popup_blocker2.html
+ popup_blocker_a.html
+[browser_popup_blocker_iframes.js]
+https_first_disabled = true
+support-files =
+ popup_blocker.html
+ popup_blocker_frame.html
+ popup_blocker_a.html
+ popup_blocker_b.html
+skip-if =
+ debug # This test triggers Bug 1578794 due to opening many popups.
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+[browser_popup_close_main_window.js]
+[browser_popup_frames.js]
+https_first_disabled = true
+support-files =
+ popup_blocker.html
+ popup_blocker_a.html
+ popup_blocker_b.html
+[browser_popup_inner_outer_size.js]
+[browser_popup_linux_move.js]
+run-if = os == 'linux' && !headless # subset of other move tests
+[browser_popup_linux_resize.js]
+run-if = os == 'linux' && !headless # subset of other resize tests
+[browser_popup_move.js]
+skip-if = os == 'linux' && !headless # Wayland doesn't like moving windows, X11/XWayland unreliable current positions
+[browser_popup_move_instant.js]
+skip-if = os == 'linux' && !headless # Wayland doesn't like moving windows, X11/XWayland unreliable current positions
+[browser_popup_new_window_resize.js]
+[browser_popup_new_window_size.js]
+support-files =
+ popup_size.html
+[browser_popup_resize.js]
+skip-if = os == 'linux' && !headless # outdated current sizes
+[browser_popup_resize_instant.js]
+skip-if = os == 'linux' && !headless # outdated current sizes
+[browser_popup_resize_repeat.js]
+skip-if = os == 'linux' && !headless # outdated current sizes
+[browser_popup_resize_repeat_instant.js]
+skip-if = os == 'linux' && !headless # outdated current sizes
+[browser_popup_resize_revert.js]
+skip-if = os == 'linux' && !headless # outdated current sizes
+[browser_popup_resize_revert_instant.js]
+skip-if = os == 'linux' && !headless # outdated current sizes
diff --git a/browser/base/content/test/popups/browser_popupUI.js b/browser/base/content/test/popups/browser_popupUI.js
new file mode 100644
index 0000000000..27423b5868
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popupUI.js
@@ -0,0 +1,192 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+add_task(async function toolbar_ui_visibility() {
+ SpecialPowers.pushPrefEnv({ set: [["dom.disable_open_during_load", false]] });
+
+ let popupOpened = BrowserTestUtils.waitForNewWindow({ url: "about:blank" });
+ BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html,<html><script>popup=open('about:blank','','width=300,height=200')</script>"
+ );
+ const win = await popupOpened;
+ const doc = win.document;
+
+ ok(win.gURLBar, "location bar exists in the popup");
+ isnot(win.gURLBar.clientWidth, 0, "location bar is visible in the popup");
+ ok(win.gURLBar.readOnly, "location bar is read-only in the popup");
+ isnot(
+ doc.getElementById("Browser:OpenLocation").getAttribute("disabled"),
+ "true",
+ "'open location' command is not disabled in the popup"
+ );
+
+ EventUtils.synthesizeKey("t", { accelKey: true }, win);
+ is(
+ win.gBrowser.browsers.length,
+ 1,
+ "Accel+T doesn't open a new tab in the popup"
+ );
+ is(
+ gBrowser.browsers.length,
+ 3,
+ "Accel+T opened a new tab in the parent window"
+ );
+ gBrowser.removeCurrentTab();
+
+ EventUtils.synthesizeKey("w", { accelKey: true }, win);
+ ok(win.closed, "Accel+W closes the popup");
+
+ if (!win.closed) {
+ win.close();
+ }
+ gBrowser.removeCurrentTab();
+});
+
+add_task(async function titlebar_buttons_visibility() {
+ if (!navigator.platform.startsWith("Win")) {
+ ok(true, "Testing only on Windows");
+ return;
+ }
+
+ const BUTTONS_MAY_VISIBLE = true;
+ const BUTTONS_NEVER_VISIBLE = false;
+
+ // Always open a new window.
+ // With default behavior, it opens a new tab, that doesn't affect button
+ // visibility at all.
+ Services.prefs.setIntPref("browser.link.open_newwindow", 2);
+
+ const drawInTitlebarValues = [
+ [1, BUTTONS_MAY_VISIBLE],
+ [0, BUTTONS_NEVER_VISIBLE],
+ ];
+ const windowFeaturesValues = [
+ // Opens a popup
+ ["width=300,height=100", BUTTONS_NEVER_VISIBLE],
+ ["toolbar", BUTTONS_NEVER_VISIBLE],
+ ["menubar", BUTTONS_NEVER_VISIBLE],
+ ["menubar,toolbar", BUTTONS_NEVER_VISIBLE],
+
+ // Opens a new window
+ ["", BUTTONS_MAY_VISIBLE],
+ ];
+ const menuBarShownValues = [true, false];
+
+ for (const [drawInTitlebar, drawInTitlebarButtons] of drawInTitlebarValues) {
+ Services.prefs.setIntPref("browser.tabs.inTitlebar", drawInTitlebar);
+
+ for (const [
+ windowFeatures,
+ windowFeaturesButtons,
+ ] of windowFeaturesValues) {
+ for (const menuBarShown of menuBarShownValues) {
+ CustomizableUI.setToolbarVisibility("toolbar-menubar", menuBarShown);
+
+ const popupPromise = BrowserTestUtils.waitForNewWindow("about:blank");
+ BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ `data:text/html;charset=UTF-8,<html><script>window.open("about:blank","","${windowFeatures}")</script>`
+ );
+ const popupWin = await popupPromise;
+
+ const menubar = popupWin.document.querySelector("#toolbar-menubar");
+ const menubarIsShown =
+ menubar.getAttribute("autohide") != "true" ||
+ menubar.getAttribute("inactive") != "true";
+ const buttonsInMenubar = menubar.querySelector(
+ ".titlebar-buttonbox-container"
+ );
+ const buttonsInMenubarShown =
+ menubarIsShown &&
+ popupWin.getComputedStyle(buttonsInMenubar).display != "none";
+
+ const buttonsInTabbar = popupWin.document.querySelector(
+ "#TabsToolbar .titlebar-buttonbox-container"
+ );
+ const buttonsInTabbarShown =
+ popupWin.getComputedStyle(buttonsInTabbar).display != "none";
+
+ const params = `drawInTitlebar=${drawInTitlebar}, windowFeatures=${windowFeatures}, menuBarShown=${menuBarShown}`;
+ if (
+ drawInTitlebarButtons == BUTTONS_MAY_VISIBLE &&
+ windowFeaturesButtons == BUTTONS_MAY_VISIBLE
+ ) {
+ ok(
+ buttonsInMenubarShown || buttonsInTabbarShown,
+ `Titlebar buttons should be visible: ${params}`
+ );
+ } else {
+ ok(
+ !buttonsInMenubarShown,
+ `Titlebar buttons should not be visible: ${params}`
+ );
+ ok(
+ !buttonsInTabbarShown,
+ `Titlebar buttons should not be visible: ${params}`
+ );
+ }
+
+ const closedPopupPromise = BrowserTestUtils.windowClosed(popupWin);
+ popupWin.close();
+ await closedPopupPromise;
+ gBrowser.removeCurrentTab();
+ }
+ }
+ }
+
+ CustomizableUI.setToolbarVisibility("toolbar-menubar", false);
+ Services.prefs.clearUserPref("browser.tabs.inTitlebar");
+ Services.prefs.clearUserPref("browser.link.open_newwindow");
+});
+
+// Test only `visibility` rule here, to verify bug 1636229 fix.
+// Other styles and ancestors can be different for each OS.
+function isVisible(element) {
+ const style = element.ownerGlobal.getComputedStyle(element);
+ return style.visibility == "visible";
+}
+
+async function testTabBarVisibility() {
+ SpecialPowers.pushPrefEnv({ set: [["dom.disable_open_during_load", false]] });
+
+ const popupOpened = BrowserTestUtils.waitForNewWindow({ url: "about:blank" });
+ BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html,<html><script>popup=open('about:blank','','width=300,height=200')</script>"
+ );
+ const win = await popupOpened;
+ const doc = win.document;
+
+ ok(
+ !isVisible(doc.getElementById("TabsToolbar")),
+ "tabbar should be hidden for popup"
+ );
+
+ const closedPopupPromise = BrowserTestUtils.windowClosed(win);
+ win.close();
+ await closedPopupPromise;
+
+ gBrowser.removeCurrentTab();
+}
+
+add_task(async function tabbar_visibility() {
+ await testTabBarVisibility();
+});
+
+add_task(async function tabbar_visibility_with_theme() {
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ theme: {},
+ },
+ });
+
+ await extension.startup();
+
+ await testTabBarVisibility();
+
+ await extension.unload();
+});
diff --git a/browser/base/content/test/popups/browser_popup_blocker.js b/browser/base/content/test/popups/browser_popup_blocker.js
new file mode 100644
index 0000000000..bfda12331e
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_blocker.js
@@ -0,0 +1,155 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const baseURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+function clearAllPermissionsByPrefix(aPrefix) {
+ for (let perm of Services.perms.all) {
+ if (perm.type.startsWith(aPrefix)) {
+ Services.perms.removePermission(perm);
+ }
+ }
+}
+
+add_setup(async function () {
+ // Enable the popup blocker.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.disable_open_during_load", true]],
+ });
+});
+
+// Tests that we show a special message when popup blocking exceeds
+// a certain maximum of popups per page.
+add_task(async function test_maximum_reported_blocks() {
+ Services.prefs.setIntPref("privacy.popups.maxReported", 5);
+
+ // Open the test page.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ baseURL + "popup_blocker_10_popups.html"
+ );
+
+ // Wait for the popup-blocked notification.
+ let notification = await TestUtils.waitForCondition(() =>
+ gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked")
+ );
+
+ // Slightly hacky way to ensure we show the correct message in this case.
+ ok(
+ notification.messageText.textContent.includes("more than"),
+ "Notification label has 'more than'"
+ );
+ ok(
+ notification.messageText.textContent.includes("5"),
+ "Notification label shows the maximum number of popups"
+ );
+
+ gBrowser.removeTab(tab);
+
+ Services.prefs.clearUserPref("privacy.popups.maxReported");
+});
+
+add_task(async function test_opening_blocked_popups() {
+ // Open the test page.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ baseURL + "popup_blocker.html"
+ );
+
+ await testPopupBlockingToolbar(tab);
+});
+
+add_task(async function test_opening_blocked_popups_privateWindow() {
+ let win = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ // Open the test page.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ win.gBrowser,
+ baseURL + "popup_blocker.html"
+ );
+ await testPopupBlockingToolbar(tab);
+ await BrowserTestUtils.closeWindow(win);
+});
+
+async function testPopupBlockingToolbar(tab) {
+ let win = tab.ownerGlobal;
+ // Wait for the popup-blocked notification.
+ let notification;
+ await TestUtils.waitForCondition(
+ () =>
+ (notification = win.gBrowser
+ .getNotificationBox()
+ .getNotificationWithValue("popup-blocked"))
+ );
+
+ // Show the menu.
+ let popupShown = BrowserTestUtils.waitForEvent(win, "popupshown");
+ let popupFilled = waitForBlockedPopups(2, {
+ doc: win.document,
+ });
+ EventUtils.synthesizeMouseAtCenter(
+ notification.buttonContainer.querySelector("button"),
+ {},
+ win
+ );
+ let popup_event = await popupShown;
+ let menu = popup_event.target;
+ is(menu.id, "blockedPopupOptions", "Blocked popup menu shown");
+
+ await popupFilled;
+
+ // Pressing "allow" should open all blocked popups.
+ let popupTabs = [];
+ function onTabOpen(event) {
+ popupTabs.push(event.target);
+ }
+ win.gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen);
+
+ // Press the button.
+ let allow = win.document.getElementById("blockedPopupAllowSite");
+ allow.doCommand();
+ await TestUtils.waitForCondition(
+ () =>
+ popupTabs.length == 2 &&
+ popupTabs.every(
+ aTab => aTab.linkedBrowser.currentURI.spec != "about:blank"
+ )
+ );
+
+ win.gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen);
+
+ ok(
+ popupTabs[0].linkedBrowser.currentURI.spec.endsWith("popup_blocker_a.html"),
+ "Popup a"
+ );
+ ok(
+ popupTabs[1].linkedBrowser.currentURI.spec.endsWith("popup_blocker_b.html"),
+ "Popup b"
+ );
+
+ let popupPerms = Services.perms.getAllByTypeSince("popup", 0);
+ is(popupPerms.length, 1, "One popup permission added");
+ let popupPerm = popupPerms[0];
+ let expectedExpireType = PrivateBrowsingUtils.isWindowPrivate(win)
+ ? Services.perms.EXPIRE_SESSION
+ : Services.perms.EXPIRE_NEVER;
+ is(
+ popupPerm.expireType,
+ expectedExpireType,
+ "Check expireType is appropriate for the window"
+ );
+
+ // Clean up.
+ win.gBrowser.removeTab(tab);
+ for (let popup of popupTabs) {
+ win.gBrowser.removeTab(popup);
+ }
+ clearAllPermissionsByPrefix("popup");
+ // Ensure the menu closes.
+ menu.hidePopup();
+}
diff --git a/browser/base/content/test/popups/browser_popup_blocker_frames.js b/browser/base/content/test/popups/browser_popup_blocker_frames.js
new file mode 100644
index 0000000000..163fa4a0bb
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_blocker_frames.js
@@ -0,0 +1,100 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const baseURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+
+async function test_opening_blocked_popups(testURL) {
+ // Enable the popup blocker.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.disable_open_during_load", true]],
+ });
+
+ // Open the test page.
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
+
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [baseURL + "popup_blocker.html"],
+ uri => {
+ let iframe = content.document.createElement("iframe");
+ iframe.id = "popupframe";
+ iframe.src = uri;
+ content.document.body.appendChild(iframe);
+ }
+ );
+
+ // Wait for the popup-blocked notification.
+ await TestUtils.waitForCondition(
+ () =>
+ gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked"),
+ "Waiting for the popup-blocked notification."
+ );
+
+ let popupTabs = [];
+ function onTabOpen(event) {
+ popupTabs.push(event.target);
+ }
+ gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen);
+
+ await SpecialPowers.pushPermissions([
+ { type: "popup", allow: true, context: testURL },
+ ]);
+
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [baseURL + "popup_blocker.html"],
+ uri => {
+ content.document.getElementById("popupframe").remove();
+ let iframe = content.document.createElement("iframe");
+ iframe.id = "popupframe";
+ iframe.src = uri;
+ content.document.body.appendChild(iframe);
+ }
+ );
+
+ await TestUtils.waitForCondition(
+ () =>
+ popupTabs.length == 2 &&
+ popupTabs.every(
+ aTab => aTab.linkedBrowser.currentURI.spec != "about:blank"
+ ),
+ "Waiting for two tabs to be opened."
+ );
+
+ ok(
+ popupTabs[0].linkedBrowser.currentURI.spec.endsWith("popup_blocker_a.html"),
+ "Popup a"
+ );
+ ok(
+ popupTabs[1].linkedBrowser.currentURI.spec.endsWith("popup_blocker_b.html"),
+ "Popup b"
+ );
+
+ await SpecialPowers.popPermissions();
+
+ gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen);
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ content.document.getElementById("popupframe").remove();
+ });
+
+ BrowserTestUtils.removeTab(tab);
+ for (let popup of popupTabs) {
+ gBrowser.removeTab(popup);
+ }
+}
+
+add_task(async function () {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ await test_opening_blocked_popups("http://example.com/");
+});
+
+add_task(async function () {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ await test_opening_blocked_popups("http://w3c-test.org/");
+});
diff --git a/browser/base/content/test/popups/browser_popup_blocker_identity_block.js b/browser/base/content/test/popups/browser_popup_blocker_identity_block.js
new file mode 100644
index 0000000000..c277be2c40
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_blocker_identity_block.js
@@ -0,0 +1,242 @@
+"use strict";
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { PermissionTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PermissionTestUtils.sys.mjs"
+);
+
+const baseURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+const URL = baseURL + "popup_blocker2.html";
+const URI = Services.io.newURI(URL);
+const PRINCIPAL = Services.scriptSecurityManager.createContentPrincipal(
+ URI,
+ {}
+);
+
+function openPermissionPopup() {
+ let promise = BrowserTestUtils.waitForEvent(
+ window,
+ "popupshown",
+ true,
+ event => event.target == gPermissionPanel._permissionPopup
+ );
+ gPermissionPanel._identityPermissionBox.click();
+ return promise;
+}
+
+function closePermissionPopup() {
+ let promise = BrowserTestUtils.waitForEvent(
+ gPermissionPanel._permissionPopup,
+ "popuphidden"
+ );
+ gPermissionPanel._permissionPopup.hidePopup();
+ return promise;
+}
+
+add_task(async function enable_popup_blocker() {
+ // Enable popup blocker.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.disable_open_during_load", true]],
+ });
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.disable_open_click_delay", 0]],
+ });
+});
+
+add_task(async function check_blocked_popup_indicator() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+
+ // Blocked popup indicator should not exist in the identity popup when there are no blocked popups.
+ await openPermissionPopup();
+ Assert.equal(document.getElementById("blocked-popup-indicator-item"), null);
+ await closePermissionPopup();
+
+ // Blocked popup notification icon should be hidden in the identity block when no popups are blocked.
+ let icon = gPermissionPanel._identityPermissionBox.querySelector(
+ ".blocked-permission-icon[data-permission-id='popup']"
+ );
+ Assert.equal(icon.hasAttribute("showing"), false);
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ let open = content.document.getElementById("pop");
+ open.click();
+ });
+
+ // Wait for popup block.
+ await TestUtils.waitForCondition(() =>
+ gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked")
+ );
+
+ // Check if blocked popup indicator text is visible in the identity popup. It should be visible.
+ document.getElementById("identity-permission-box").click();
+ await openPermissionPopup();
+ await TestUtils.waitForCondition(
+ () => document.getElementById("blocked-popup-indicator-item") !== null
+ );
+
+ // Check that the default state is correctly set to "Block".
+ let menulist = document.getElementById("permission-popup-menulist");
+ Assert.equal(menulist.value, "0");
+ Assert.equal(menulist.label, "Block");
+
+ await closePermissionPopup();
+
+ // Check if blocked popup icon is visible in the identity block.
+ Assert.equal(icon.getAttribute("showing"), "true");
+
+ gBrowser.removeTab(tab);
+});
+
+// Check if clicking on "Show blocked popups" shows blocked popups.
+add_task(async function check_popup_showing() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ let open = content.document.getElementById("pop");
+ open.click();
+ });
+
+ // Wait for popup block.
+ await TestUtils.waitForCondition(() =>
+ gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked")
+ );
+
+ // Store the popup that opens in this array.
+ let popup;
+ function onTabOpen(event) {
+ popup = event.target;
+ }
+ gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen);
+
+ // Open identity popup and click on "Show blocked popups".
+ await openPermissionPopup();
+ let e = document.getElementById("blocked-popup-indicator-item");
+ let text = e.getElementsByTagName("label")[0];
+ text.click();
+
+ await BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen");
+ await TestUtils.waitForCondition(
+ () => popup.linkedBrowser.currentURI.spec != "about:blank"
+ );
+
+ gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen);
+
+ ok(
+ popup.linkedBrowser.currentURI.spec.endsWith("popup_blocker_a.html"),
+ "Popup a"
+ );
+
+ gBrowser.removeTab(popup);
+ gBrowser.removeTab(tab);
+});
+
+// Test if changing menulist values of blocked popup indicator changes permission state and popup behavior.
+add_task(async function check_permission_state_change() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+
+ // Initially the permission state is BLOCK for popups (set by the prefs).
+ let state = SitePermissions.getForPrincipal(
+ PRINCIPAL,
+ "popup",
+ gBrowser
+ ).state;
+ Assert.equal(state, SitePermissions.BLOCK);
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ let open = content.document.getElementById("pop");
+ open.click();
+ });
+
+ // Wait for popup block.
+ await TestUtils.waitForCondition(() =>
+ gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked")
+ );
+
+ // Open identity popup and change permission state to allow.
+ await openPermissionPopup();
+ let menulist = document.getElementById("permission-popup-menulist");
+ menulist.menupopup.openPopup(); // Open the allow/block menu
+ let menuitem = menulist.getElementsByTagName("menuitem")[0];
+ menuitem.click();
+ await closePermissionPopup();
+
+ state = SitePermissions.getForPrincipal(PRINCIPAL, "popup", gBrowser).state;
+ Assert.equal(state, SitePermissions.ALLOW);
+
+ // Store the popup that opens in this array.
+ let popup;
+ function onTabOpen(event) {
+ popup = event.target;
+ }
+ gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen);
+
+ // Check if a popup opens.
+ await Promise.all([
+ SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ let open = content.document.getElementById("pop");
+ open.click();
+ }),
+ BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen"),
+ ]);
+ await TestUtils.waitForCondition(
+ () => popup.linkedBrowser.currentURI.spec != "about:blank"
+ );
+
+ gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen);
+
+ ok(
+ popup.linkedBrowser.currentURI.spec.endsWith("popup_blocker_a.html"),
+ "Popup a"
+ );
+
+ gBrowser.removeTab(popup);
+
+ // Open identity popup and change permission state to block.
+ await openPermissionPopup();
+ menulist = document.getElementById("permission-popup-menulist");
+ menulist.menupopup.openPopup(); // Open the allow/block menu
+ menuitem = menulist.getElementsByTagName("menuitem")[1];
+ menuitem.click();
+ await closePermissionPopup();
+
+ // Clicking on the "Block" menuitem should remove the permission object(same behavior as UNKNOWN state).
+ // We have already confirmed that popups are blocked when the permission state is BLOCK.
+ state = SitePermissions.getForPrincipal(PRINCIPAL, "popup", gBrowser).state;
+ Assert.equal(state, SitePermissions.BLOCK);
+
+ gBrowser.removeTab(tab);
+});
+
+// Explicitly set the permission to the otherwise default state and check that
+// the label still displays correctly.
+add_task(async function check_explicit_default_permission() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+
+ // DENY only works if triggered through Services.perms (it's very edge-casey),
+ // since SitePermissions.sys.mjs considers setting default permissions to be removal.
+ PermissionTestUtils.add(URI, "popup", Ci.nsIPermissionManager.DENY_ACTION);
+
+ await openPermissionPopup();
+ let menulist = document.getElementById("permission-popup-menulist");
+ Assert.equal(menulist.value, "0");
+ Assert.equal(menulist.label, "Block");
+ await closePermissionPopup();
+
+ PermissionTestUtils.add(URI, "popup", Services.perms.ALLOW_ACTION);
+
+ await openPermissionPopup();
+ menulist = document.getElementById("permission-popup-menulist");
+ Assert.equal(menulist.value, "1");
+ Assert.equal(menulist.label, "Allow");
+ await closePermissionPopup();
+
+ PermissionTestUtils.remove(URI, "popup");
+ gBrowser.removeTab(tab);
+});
diff --git a/browser/base/content/test/popups/browser_popup_blocker_iframes.js b/browser/base/content/test/popups/browser_popup_blocker_iframes.js
new file mode 100644
index 0000000000..aa93a7acac
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_blocker_iframes.js
@@ -0,0 +1,186 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+const testURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org"
+);
+
+const examplecomURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+
+const w3cURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://w3c-test.org"
+);
+
+const examplenetURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.net"
+);
+
+const prefixexamplecomURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://prefixexample.com"
+);
+
+class TestCleanup {
+ constructor() {
+ this.tabs = [];
+ }
+
+ count() {
+ return this.tabs.length;
+ }
+
+ static setup() {
+ let cleaner = new TestCleanup();
+ this.onTabOpen = event => {
+ cleaner.tabs.push(event.target);
+ };
+ gBrowser.tabContainer.addEventListener("TabOpen", this.onTabOpen);
+ return cleaner;
+ }
+
+ clean() {
+ gBrowser.tabContainer.removeEventListener("TabOpen", this.onTabOpen);
+ for (let tab of this.tabs) {
+ gBrowser.removeTab(tab);
+ }
+ }
+}
+
+async function runTest(count, urls, permissions, delayedAllow) {
+ let cleaner = TestCleanup.setup();
+
+ // Enable the popup blocker.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.disable_open_during_load", true]],
+ });
+
+ await SpecialPowers.pushPermissions(permissions);
+
+ // Open the test page.
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
+
+ let contexts = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [...urls, !!delayedAllow],
+ async (url1, url2, url3, url4, delay) => {
+ let iframe1 = content.document.createElement("iframe");
+ let iframe2 = content.document.createElement("iframe");
+ iframe1.id = "iframe1";
+ iframe2.id = "iframe2";
+ iframe1.src = new URL(
+ `popup_blocker_frame.html?delayed=${delay}&base=${url3}`,
+ url1
+ );
+ iframe2.src = new URL(
+ `popup_blocker_frame.html?delayed=${delay}&base=${url4}`,
+ url2
+ );
+
+ let promises = [
+ new Promise(resolve => (iframe1.onload = resolve)),
+ new Promise(resolve => (iframe2.onload = resolve)),
+ ];
+
+ content.document.body.appendChild(iframe1);
+ content.document.body.appendChild(iframe2);
+
+ await Promise.all(promises);
+ return [iframe1.browsingContext, iframe2.browsingContext];
+ }
+ );
+
+ if (delayedAllow) {
+ await delayedAllow();
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ contexts,
+ async function (bc1, bc2) {
+ bc1.window.postMessage("allow", "*");
+ bc2.window.postMessage("allow", "*");
+ }
+ );
+ }
+
+ await TestUtils.waitForCondition(
+ () => cleaner.count() == count,
+ `waiting for ${count} tabs, got ${cleaner.count()}`
+ );
+
+ ok(cleaner.count() == count, `should have ${count} tabs`);
+
+ await SpecialPowers.popPermissions();
+ cleaner.clean();
+}
+
+add_task(async function () {
+ let permission = {
+ type: "popup",
+ allow: true,
+ context: "",
+ };
+
+ let expected = [];
+
+ let tests = [
+ [examplecomURL, w3cURL, prefixexamplecomURL, examplenetURL],
+ [examplecomURL, examplecomURL, prefixexamplecomURL, examplenetURL],
+ [examplecomURL, examplecomURL, prefixexamplecomURL, prefixexamplecomURL],
+ [examplecomURL, w3cURL, prefixexamplecomURL, prefixexamplecomURL],
+ ];
+
+ permission.context = testURL;
+ expected = [5, 5, 3, 3];
+ for (let test in tests) {
+ await runTest(expected[test], tests[test], [permission]);
+ }
+
+ permission.context = examplecomURL;
+ expected = [3, 5, 3, 3];
+ for (let test in tests) {
+ await runTest(expected[test], tests[test], [permission]);
+ }
+
+ permission.context = prefixexamplecomURL;
+ expected = [3, 3, 3, 3];
+ for (let test in tests) {
+ await runTest(expected[test], tests[test], [permission]);
+ }
+
+ async function allowPopup() {
+ await SpecialPowers.pushPermissions([permission]);
+ }
+
+ permission.context = testURL;
+ expected = [5, 5, 3, 3];
+ for (let test in tests) {
+ await runTest(expected[test], tests[test], [], allowPopup);
+ }
+
+ permission.context = examplecomURL;
+ expected = [3, 5, 3, 3];
+ for (let test in tests) {
+ await runTest(expected[test], tests[test], [], allowPopup);
+ }
+
+ permission.context = prefixexamplecomURL;
+ expected = [3, 3, 3, 3];
+ for (let test in tests) {
+ await runTest(expected[test], tests[test], [], allowPopup);
+ }
+});
diff --git a/browser/base/content/test/popups/browser_popup_close_main_window.js b/browser/base/content/test/popups/browser_popup_close_main_window.js
new file mode 100644
index 0000000000..148e937bca
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_close_main_window.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function muffleMainWindowType() {
+ let oldWinType = document.documentElement.getAttribute("windowtype");
+ // Check if we've already done this to allow calling multiple times:
+ if (oldWinType != "navigator:testrunner") {
+ // Make the main test window not count as a browser window any longer
+ document.documentElement.setAttribute("windowtype", "navigator:testrunner");
+
+ registerCleanupFunction(() => {
+ document.documentElement.setAttribute("windowtype", oldWinType);
+ });
+ }
+}
+
+/**
+ * Check that if we close the 1 remaining window, we treat it as quitting on
+ * non-mac.
+ *
+ * Sets the window type for the main browser test window to something else to
+ * avoid having to actually close the main browser window.
+ */
+add_task(async function closing_last_window_equals_quitting() {
+ if (navigator.platform.startsWith("Mac")) {
+ ok(true, "Not testing on mac");
+ return;
+ }
+ muffleMainWindowType();
+
+ let observed = 0;
+ function obs() {
+ observed++;
+ }
+ Services.obs.addObserver(obs, "browser-lastwindow-close-requested");
+ let newWin = await BrowserTestUtils.openNewBrowserWindow();
+ let closedPromise = BrowserTestUtils.windowClosed(newWin);
+ newWin.BrowserTryToCloseWindow();
+ await closedPromise;
+ is(observed, 1, "Got a notification for closing the normal window.");
+ Services.obs.removeObserver(obs, "browser-lastwindow-close-requested");
+});
+
+/**
+ * Check that if we close the 1 remaining window and also have a popup open,
+ * we don't treat it as quitting.
+ *
+ * Sets the window type for the main browser test window to something else to
+ * avoid having to actually close the main browser window.
+ */
+add_task(async function closing_last_window_equals_quitting() {
+ if (navigator.platform.startsWith("Mac")) {
+ ok(true, "Not testing on mac");
+ return;
+ }
+ muffleMainWindowType();
+ let observed = 0;
+ function obs() {
+ observed++;
+ }
+ Services.obs.addObserver(obs, "browser-lastwindow-close-requested");
+ let newWin = await BrowserTestUtils.openNewBrowserWindow();
+ let popupPromise = BrowserTestUtils.waitForNewWindow("https://example.com/");
+ SpecialPowers.spawn(newWin.gBrowser.selectedBrowser, [], function () {
+ content.open("https://example.com/", "_blank", "height=500");
+ });
+ let popupWin = await popupPromise;
+ let closedPromise = BrowserTestUtils.windowClosed(newWin);
+ newWin.BrowserTryToCloseWindow();
+ await closedPromise;
+ is(observed, 0, "Got no notification for closing the normal window.");
+
+ closedPromise = BrowserTestUtils.windowClosed(popupWin);
+ popupWin.BrowserTryToCloseWindow();
+ await closedPromise;
+ is(
+ observed,
+ 0,
+ "Got no notification now that we're closing the last window, as it's a popup."
+ );
+ Services.obs.removeObserver(obs, "browser-lastwindow-close-requested");
+});
diff --git a/browser/base/content/test/popups/browser_popup_frames.js b/browser/base/content/test/popups/browser_popup_frames.js
new file mode 100644
index 0000000000..838eb5c045
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_frames.js
@@ -0,0 +1,128 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const baseURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+
+async function test_opening_blocked_popups(testURL) {
+ // Enable the popup blocker.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.disable_open_during_load", true]],
+ });
+
+ // Open the test page.
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL);
+
+ let popupframeBC = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [baseURL + "popup_blocker.html"],
+ uri => {
+ let iframe = content.document.createElement("iframe");
+ iframe.id = "popupframe";
+ iframe.src = uri;
+ content.document.body.appendChild(iframe);
+ return iframe.browsingContext;
+ }
+ );
+
+ // Wait for the popup-blocked notification.
+ let notification;
+ await TestUtils.waitForCondition(
+ () =>
+ (notification = gBrowser
+ .getNotificationBox()
+ .getNotificationWithValue("popup-blocked")),
+ "Waiting for the popup-blocked notification."
+ );
+
+ ok(notification, "Should have notification.");
+
+ let pageHideHappened = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "pagehide",
+ true
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [baseURL], async function (uri) {
+ let iframe = content.document.createElement("iframe");
+ content.document.body.appendChild(iframe);
+ iframe.src = uri;
+ });
+
+ await pageHideHappened;
+ notification = gBrowser
+ .getNotificationBox()
+ .getNotificationWithValue("popup-blocked");
+ ok(notification, "Should still have notification");
+
+ pageHideHappened = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "pagehide",
+ true
+ );
+ // Now navigate the subframe.
+ await SpecialPowers.spawn(popupframeBC, [], async function () {
+ content.document.location.href = "about:blank";
+ });
+ await pageHideHappened;
+ await TestUtils.waitForCondition(
+ () =>
+ !gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked"),
+ "Notification should go away"
+ );
+ ok(
+ !gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked"),
+ "Should no longer have notification"
+ );
+
+ // Remove the frame and add another one:
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [baseURL + "popup_blocker.html"],
+ uri => {
+ content.document.getElementById("popupframe").remove();
+ let iframe = content.document.createElement("iframe");
+ iframe.id = "popupframe";
+ iframe.src = uri;
+ content.document.body.appendChild(iframe);
+ }
+ );
+
+ // Wait for the popup-blocked notification.
+ await TestUtils.waitForCondition(
+ () =>
+ (notification = gBrowser
+ .getNotificationBox()
+ .getNotificationWithValue("popup-blocked"))
+ );
+
+ ok(notification, "Should have notification.");
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ content.document.getElementById("popupframe").remove();
+ });
+
+ await TestUtils.waitForCondition(
+ () =>
+ !gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked")
+ );
+ ok(
+ !gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked"),
+ "Should no longer have notification"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function () {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ await test_opening_blocked_popups("http://example.com/");
+});
+
+add_task(async function () {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ await test_opening_blocked_popups("http://w3c-test.org/");
+});
diff --git a/browser/base/content/test/popups/browser_popup_inner_outer_size.js b/browser/base/content/test/popups/browser_popup_inner_outer_size.js
new file mode 100644
index 0000000000..7e2e0e43fe
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_inner_outer_size.js
@@ -0,0 +1,120 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+async function checkForDeltaMismatch(aMsg) {
+ let getDelta = () => {
+ return {
+ width: this.content.outerWidth - this.content.innerWidth,
+ height: this.content.outerHeight - this.content.innerHeight,
+ };
+ };
+
+ let initialDelta = getDelta();
+ let latestDelta = initialDelta;
+
+ this.content.testerPromise = new Promise(resolve => {
+ // Called from stopCheck
+ this.content.resolveFunc = resolve;
+ info(`[${aMsg}] Starting interval tester.`);
+ this.content.intervalID = this.content.setInterval(() => {
+ let currentDelta = getDelta();
+ if (
+ latestDelta.width != currentDelta.width ||
+ latestDelta.height != currentDelta.height
+ ) {
+ latestDelta = currentDelta;
+
+ let { innerWidth: iW, outerWidth: oW } = this.content;
+ let { innerHeight: iH, outerHeight: oH } = this.content;
+ info(`[${aMsg}] Delta changed. (inner ${iW}x${iH}, outer ${oW}x${oH})`);
+
+ let { width: w, height: h } = currentDelta;
+ is(w, initialDelta.width, `[${aMsg}] Inner to outer width delta.`);
+ is(h, initialDelta.height, `[${aMsg}] Inner to outer height delta.`);
+ }
+ }, 0);
+ }).then(() => {
+ let { width: w, height: h } = latestDelta;
+ is(w, initialDelta.width, `[${aMsg}] Final inner to outer width delta.`);
+ is(h, initialDelta.height, `[${aMsg}] Final Inner to outer height delta.`);
+ });
+}
+
+async function stopCheck(aMsg) {
+ info(`[${aMsg}] Stopping interval tester.`);
+ this.content.clearInterval(this.content.intervalID);
+ info(`[${aMsg}] Resolving interval tester.`);
+ this.content.resolveFunc();
+ await this.content.testerPromise;
+}
+
+add_task(async function test_innerToOuterDelta() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "https://example.net"
+ );
+ let popupBrowsingContext = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async () => {
+ info("Opening popup.");
+ let popup = this.content.open(
+ "https://example.net",
+ "",
+ "width=200,height=200"
+ );
+ info("Waiting for load event.");
+ await ContentTaskUtils.waitForEvent(popup, "load");
+ return popup.browsingContext;
+ }
+ );
+
+ await SpecialPowers.spawn(
+ popupBrowsingContext,
+ ["Content"],
+ checkForDeltaMismatch
+ );
+ let popupChrome = popupBrowsingContext.topChromeWindow;
+ await SpecialPowers.spawn(popupChrome, ["Chrome"], checkForDeltaMismatch);
+
+ let numResizes = 3;
+ let resizeStep = 5;
+ let { outerWidth: width, outerHeight: height } = popupChrome;
+ let finalWidth = width + numResizes * resizeStep;
+ let finalHeight = height + numResizes * resizeStep;
+
+ info(`Starting ${numResizes} resizes.`);
+ await new Promise(resolve => {
+ let resizeListener = () => {
+ if (
+ popupChrome.outerWidth == finalWidth &&
+ popupChrome.outerHeight == finalHeight
+ ) {
+ popupChrome.removeEventListener("resize", resizeListener);
+ resolve();
+ }
+ };
+ popupChrome.addEventListener("resize", resizeListener);
+
+ let resizeNext = () => {
+ width += resizeStep;
+ height += resizeStep;
+ info(`Resizing to ${width}x${height}`);
+ popupChrome.resizeTo(width, height);
+ numResizes--;
+ if (numResizes > 0) {
+ info(`${numResizes} resizes remaining.`);
+ popupChrome.requestAnimationFrame(resizeNext);
+ }
+ };
+ resizeNext();
+ });
+
+ await SpecialPowers.spawn(popupBrowsingContext, ["Content"], stopCheck);
+ await SpecialPowers.spawn(popupChrome, ["Chrome"], stopCheck);
+
+ await SpecialPowers.spawn(popupBrowsingContext, [], () => {
+ this.content.close();
+ });
+ await BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/popups/browser_popup_linux_move.js b/browser/base/content/test/popups/browser_popup_linux_move.js
new file mode 100644
index 0000000000..f318ee1873
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_linux_move.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function createLinuxMoveTests(aFirstValue, aSecondValue, aMsg) {
+ for (let prop of ["screenX", "screenY"]) {
+ let first = {};
+ first[prop] = aFirstValue;
+ let second = {};
+ second[prop] = aSecondValue;
+ new ResizeMoveTest(
+ [first, second],
+ /* aInstant */ true,
+ `${aMsg} ${prop},${prop}`
+ );
+ new ResizeMoveTest(
+ [first, second],
+ /* aInstant */ false,
+ `${aMsg} ${prop},${prop}`
+ );
+ }
+}
+
+if (AppConstants.platform == "linux" && gfxInfo.windowProtocol == "wayland") {
+ add_task(async () => {
+ let tab = await ResizeMoveTest.GetOrCreateTab();
+ let browsingContext =
+ await ResizeMoveTest.GetOrCreatePopupBrowsingContext();
+ let win = browsingContext.topChromeWindow;
+ let targetX = win.screenX + 10;
+ win.moveTo(targetX, win.screenY);
+ await BrowserTestUtils.waitForCondition(() => win.screenX == targetX).catch(
+ () => {}
+ );
+ todo(win.screenX == targetX, "Moving windows on wayland.");
+ win.close();
+ await BrowserTestUtils.removeTab(tab);
+ });
+} else {
+ createLinuxMoveTests(9, 10, "Move");
+ createLinuxMoveTests(10, 0, "Move revert");
+ createLinuxMoveTests(10, 10, "Move repeat");
+
+ new ResizeMoveTest(
+ [{ screenX: 10 }, { screenY: 10 }, { screenX: 20 }],
+ /* aInstant */ true,
+ "Move sequence",
+ /* aWaitForCompletion */ true
+ );
+
+ new ResizeMoveTest(
+ [{ screenX: 10 }, { screenY: 10 }, { screenX: 20 }],
+ /* aInstant */ false,
+ "Move sequence",
+ /* aWaitForCompletion */ true
+ );
+}
diff --git a/browser/base/content/test/popups/browser_popup_linux_resize.js b/browser/base/content/test/popups/browser_popup_linux_resize.js
new file mode 100644
index 0000000000..6917c5823d
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_linux_resize.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function createLinuxResizeTests(aFirstValue, aSecondValue, aMsg) {
+ for (let prop of ResizeMoveTest.PropInfo.sizeProps) {
+ // For e.g 'outerWidth' this will be 'innerWidth'.
+ let otherProp = ResizeMoveTest.PropInfo.crossBoundsMapping[prop];
+ let first = {};
+ first[prop] = aFirstValue;
+ let second = {};
+ second[otherProp] = aSecondValue;
+ new ResizeMoveTest(
+ [first, second],
+ /* aInstant */ true,
+ `${aMsg} ${prop},${otherProp}`
+ );
+ new ResizeMoveTest(
+ [first, second],
+ /* aInstant */ false,
+ `${aMsg} ${prop},${otherProp}`
+ );
+ }
+}
+
+createLinuxResizeTests(9, 10, "Resize");
+createLinuxResizeTests(10, 0, "Resize revert");
+createLinuxResizeTests(10, 10, "Resize repeat");
+
+new ResizeMoveTest(
+ [
+ { outerWidth: 10 },
+ { innerHeight: 10 },
+ { innerWidth: 20 },
+ { outerHeight: 20 },
+ { outerWidth: 30 },
+ ],
+ /* aInstant */ true,
+ "Resize sequence",
+ /* aWaitForCompletion */ true
+);
+
+new ResizeMoveTest(
+ [
+ { outerWidth: 10 },
+ { innerHeight: 10 },
+ { innerWidth: 20 },
+ { outerHeight: 20 },
+ { outerWidth: 30 },
+ ],
+ /* aInstant */ false,
+ "Resize sequence",
+ /* aWaitForCompletion */ true
+);
diff --git a/browser/base/content/test/popups/browser_popup_move.js b/browser/base/content/test/popups/browser_popup_move.js
new file mode 100644
index 0000000000..d7d47e12f5
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_move.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+createGenericMoveTests(/* aInstant */ false, "Move");
diff --git a/browser/base/content/test/popups/browser_popup_move_instant.js b/browser/base/content/test/popups/browser_popup_move_instant.js
new file mode 100644
index 0000000000..c8a9219d82
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_move_instant.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+createGenericMoveTests(/* aInstant */ true, "Move");
diff --git a/browser/base/content/test/popups/browser_popup_new_window_resize.js b/browser/base/content/test/popups/browser_popup_new_window_resize.js
new file mode 100644
index 0000000000..81d3cf5a9a
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_new_window_resize.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function test_new_window_resize() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "https://example.net"
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ info("Opening popup.");
+ let win = this.content.open(
+ "https://example.net",
+ "",
+ "width=200,height=200"
+ );
+
+ await ContentTaskUtils.waitForEvent(win, "load");
+
+ let { outerWidth: initialWidth, outerHeight: initialHeight } = win;
+ let targetWidth = initialWidth + 100;
+ let targetHeight = initialHeight + 100;
+
+ let observedOurResizeEvent = false;
+ let resizeListener = () => {
+ let { outerWidth: currentWidth, outerHeight: currentHeight } = win;
+ info(`Resize event for ${currentWidth}x${currentHeight}.`);
+ if (currentWidth == targetWidth && currentHeight == targetHeight) {
+ ok(!observedOurResizeEvent, "First time we receive our resize event.");
+ observedOurResizeEvent = true;
+ }
+ };
+ win.addEventListener("resize", resizeListener);
+ win.resizeTo(targetWidth, targetHeight);
+
+ await ContentTaskUtils.waitForCondition(
+ () => observedOurResizeEvent,
+ `Waiting for our resize event (${targetWidth}x${targetHeight}).`
+ );
+
+ info("Waiting for potentially incoming resize events.");
+ for (let i = 0; i < 10; i++) {
+ await new Promise(r => win.requestAnimationFrame(r));
+ }
+ win.removeEventListener("resize", resizeListener);
+ info("Closing popup.");
+ win.close();
+ });
+
+ await BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/popups/browser_popup_new_window_size.js b/browser/base/content/test/popups/browser_popup_new_window_size.js
new file mode 100644
index 0000000000..5f5d57e31e
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_new_window_size.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const baseURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+add_task(async function test_new_window_size() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ baseURL
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ info("Opening popup.");
+ let requestedWidth = 200;
+ let requestedHeight = 200;
+ let win = this.content.open(
+ "popup_size.html",
+ "",
+ `width=${requestedWidth},height=${requestedHeight}`
+ );
+
+ let loadPromise = ContentTaskUtils.waitForEvent(win, "load");
+
+ let { innerWidth: preLoadWidth, innerHeight: preLoadHeight } = win;
+ is(preLoadWidth, requestedWidth, "Width before load event.");
+ is(preLoadHeight, requestedHeight, "Height before load event.");
+
+ await loadPromise;
+
+ let { innerWidth: postLoadWidth, innerHeight: postLoadHeight } = win;
+ is(postLoadWidth, requestedWidth, "Width after load event.");
+ is(postLoadHeight, requestedHeight, "Height after load event.");
+
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ win.innerWidth == requestedWidth && win.innerHeight == requestedHeight,
+ "Waiting for window to become request size."
+ );
+
+ let { innerWidth: finalWidth, innerHeight: finalHeight } = win;
+ is(finalWidth, requestedWidth, "Final width.");
+ is(finalHeight, requestedHeight, "Final height.");
+
+ await SpecialPowers.spawn(
+ win,
+ [{ requestedWidth, requestedHeight }],
+ async input => {
+ let { initialSize, loadSize } = this.content.wrappedJSObject;
+ is(
+ initialSize.width,
+ input.requestedWidth,
+ "Content width before load event."
+ );
+ is(
+ initialSize.height,
+ input.requestedHeight,
+ "Content height before load event."
+ );
+ is(
+ loadSize.width,
+ input.requestedWidth,
+ "Content width after load event."
+ );
+ is(
+ loadSize.height,
+ input.requestedHeight,
+ "Content height after load event."
+ );
+ is(
+ this.content.innerWidth,
+ input.requestedWidth,
+ "Content final width."
+ );
+ is(
+ this.content.innerHeight,
+ input.requestedHeight,
+ "Content final height."
+ );
+ }
+ );
+
+ info("Closing popup.");
+ win.close();
+ });
+
+ await BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/popups/browser_popup_resize.js b/browser/base/content/test/popups/browser_popup_resize.js
new file mode 100644
index 0000000000..c73ffb58c5
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_resize.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+createGenericResizeTests(9, 10, /* aInstant */ false, "Resize");
diff --git a/browser/base/content/test/popups/browser_popup_resize_instant.js b/browser/base/content/test/popups/browser_popup_resize_instant.js
new file mode 100644
index 0000000000..4613e272d6
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_resize_instant.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+createGenericResizeTests(9, 10, /* aInstant */ true, "Resize");
diff --git a/browser/base/content/test/popups/browser_popup_resize_repeat.js b/browser/base/content/test/popups/browser_popup_resize_repeat.js
new file mode 100644
index 0000000000..d76f0fd3a2
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_resize_repeat.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+createGenericResizeTests(10, 10, /* aInstant */ false, "Resize repeat");
diff --git a/browser/base/content/test/popups/browser_popup_resize_repeat_instant.js b/browser/base/content/test/popups/browser_popup_resize_repeat_instant.js
new file mode 100644
index 0000000000..9870813d87
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_resize_repeat_instant.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+createGenericResizeTests(10, 10, /* aInstant */ true, "Resize repeat");
diff --git a/browser/base/content/test/popups/browser_popup_resize_revert.js b/browser/base/content/test/popups/browser_popup_resize_revert.js
new file mode 100644
index 0000000000..e87ef30f69
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_resize_revert.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+createGenericResizeTests(10, 0, /* aInstant */ false, "Resize revert");
diff --git a/browser/base/content/test/popups/browser_popup_resize_revert_instant.js b/browser/base/content/test/popups/browser_popup_resize_revert_instant.js
new file mode 100644
index 0000000000..5fa2ce7d5d
--- /dev/null
+++ b/browser/base/content/test/popups/browser_popup_resize_revert_instant.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+createGenericResizeTests(10, 0, /* aInstant */ true, "Resize revert");
diff --git a/browser/base/content/test/popups/head.js b/browser/base/content/test/popups/head.js
new file mode 100644
index 0000000000..f72bba7dca
--- /dev/null
+++ b/browser/base/content/test/popups/head.js
@@ -0,0 +1,574 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
+ SpecialPowers.Ci.nsIGfxInfo
+);
+
+async function waitForBlockedPopups(numberOfPopups, { doc }) {
+ let toolbarDoc = doc || document;
+ let menupopup = toolbarDoc.getElementById("blockedPopupOptions");
+ await BrowserTestUtils.waitForCondition(() => {
+ let popups = menupopup.querySelectorAll("[popupReportIndex]");
+ return popups.length == numberOfPopups;
+ }, `Waiting for ${numberOfPopups} popups`);
+}
+
+/*
+ * Tests that a sequence of size changes ultimately results in the latest
+ * requested size. The test also fails when an unexpected window size is
+ * observed in a resize event.
+ *
+ * aPropertyDeltas List of objects where keys describe the name of a window
+ * property and the values the difference to its initial
+ * value.
+ *
+ * aInstant Issue changes without additional waiting in between.
+ *
+ * A brief example of the resutling code that is effectively run for the
+ * following list of deltas:
+ * [{outerWidth: 5, outerHeight: 10}, {outerWidth: 10}]
+ *
+ * let initialWidth = win.outerWidth;
+ * let initialHeight = win.outerHeight;
+ *
+ * if (aInstant) {
+ * win.outerWidth = initialWidth + 5;
+ * win.outerHeight = initialHeight + 10;
+ *
+ * win.outerWidth = initialWidth + 10;
+ * } else {
+ * win.requestAnimationFrame(() => {
+ * win.outerWidth = initialWidth + 5;
+ * win.outerHeight = initialHeight + 10;
+ *
+ * win.requestAnimationFrame(() => {
+ * win.outerWidth = initialWidth + 10;
+ * });
+ * });
+ * }
+ */
+async function testPropertyDeltas(
+ aPropertyDeltas,
+ aInstant,
+ aPropInfo,
+ aMsg,
+ aWaitForCompletion
+) {
+ let msg = `[${aMsg}]`;
+
+ let win = this.content.popup || this.content.wrappedJSObject;
+
+ // Property names and mapping from ResizeMoveTest
+ let {
+ sizeProps,
+ positionProps /* can be empty/incomplete as workaround on Linux */,
+ readonlyProps,
+ crossBoundsMapping,
+ } = aPropInfo;
+
+ let stringifyState = state => {
+ let stateMsg = sizeProps
+ .concat(positionProps)
+ .filter(prop => state[prop] !== undefined)
+ .map(prop => `${prop}: ${state[prop]}`)
+ .join(", ");
+ return `{ ${stateMsg} }`;
+ };
+
+ let initialState = {};
+ let finalState = {};
+
+ info("Initializing all values to current state.");
+ for (let prop of sizeProps.concat(positionProps)) {
+ let value = win[prop];
+ initialState[prop] = value;
+ finalState[prop] = value;
+ }
+
+ // List of potential states during resize events. The current state is also
+ // considered valid, as the resize event might still be outstanding.
+ let validResizeStates = [initialState];
+
+ let updateFinalState = (aProp, aDelta) => {
+ if (
+ readonlyProps.includes(aProp) ||
+ !sizeProps.concat(positionProps).includes(aProp)
+ ) {
+ throw new Error(`Unexpected property "${aProp}".`);
+ }
+
+ // Update both properties of the same axis.
+ let otherProp = crossBoundsMapping[aProp];
+ finalState[aProp] = initialState[aProp] + aDelta;
+ finalState[otherProp] = initialState[otherProp] + aDelta;
+
+ // Mark size as valid in resize event.
+ if (sizeProps.includes(aProp)) {
+ let state = {};
+ sizeProps.forEach(p => (state[p] = finalState[p]));
+ validResizeStates.push(state);
+ }
+ };
+
+ info("Adding resize event listener.");
+ let resizeCount = 0;
+ let resizeListener = evt => {
+ resizeCount++;
+
+ let currentSizeState = {};
+ sizeProps.forEach(p => (currentSizeState[p] = win[p]));
+
+ info(
+ `${msg} ${resizeCount}. resize event: ${stringifyState(currentSizeState)}`
+ );
+ let matchingIndex = validResizeStates.findIndex(state =>
+ sizeProps.every(p => state[p] == currentSizeState[p])
+ );
+ if (matchingIndex < 0) {
+ info(`${msg} Size state should have been one of:`);
+ for (let state of validResizeStates) {
+ info(stringifyState(state));
+ }
+ }
+
+ if (win.gBrowser && evt.target != win) {
+ // Without e10s we receive content resize events in chrome windows.
+ todo(false, `${msg} Resize event target is our window.`);
+ return;
+ }
+
+ ok(
+ matchingIndex >= 0,
+ `${msg} Valid intermediate state. Current: ` +
+ stringifyState(currentSizeState)
+ );
+
+ // No longer allow current and preceding states.
+ validResizeStates.splice(0, matchingIndex + 1);
+ };
+ win.addEventListener("resize", resizeListener);
+
+ const useProperties = !Services.prefs.getBoolPref(
+ "dom.window_position_size_properties_replaceable.enabled",
+ true
+ );
+
+ info("Starting property changes.");
+ await new Promise(resolve => {
+ let index = 0;
+ let next = async () => {
+ let pre = `${msg} [${index + 1}/${aPropertyDeltas.length}]`;
+
+ let deltaObj = aPropertyDeltas[index];
+ for (let prop in deltaObj) {
+ updateFinalState(prop, deltaObj[prop]);
+
+ let targetValue = initialState[prop] + deltaObj[prop];
+ info(`${pre} Setting ${prop} to ${targetValue}.`);
+ if (useProperties) {
+ win[prop] = targetValue;
+ } else if (sizeProps.includes(prop)) {
+ win.resizeTo(finalState.outerWidth, finalState.outerHeight);
+ } else {
+ win.moveTo(finalState.screenX, finalState.screenY);
+ }
+ if (aWaitForCompletion) {
+ await ContentTaskUtils.waitForCondition(
+ () => win[prop] == targetValue,
+ `${msg} Waiting for ${prop} to be ${targetValue}.`
+ );
+ }
+ }
+
+ index++;
+ if (index < aPropertyDeltas.length) {
+ scheduleNext();
+ } else {
+ resolve();
+ }
+ };
+
+ let scheduleNext = () => {
+ if (aInstant) {
+ next();
+ } else {
+ info(`${msg} Requesting animation frame.`);
+ win.requestAnimationFrame(next);
+ }
+ };
+ scheduleNext();
+ });
+
+ try {
+ info(`${msg} Waiting for window to match the final state.`);
+ await ContentTaskUtils.waitForCondition(
+ () => sizeProps.concat(positionProps).every(p => win[p] == finalState[p]),
+ "Waiting for final state."
+ );
+ } catch (e) {}
+
+ info(`${msg} Checking final state.`);
+ info(`${msg} Exepected: ${stringifyState(finalState)}`);
+ info(`${msg} Actual: ${stringifyState(win)}`);
+ for (let prop of sizeProps.concat(positionProps)) {
+ is(win[prop], finalState[prop], `${msg} Expected final value for ${prop}`);
+ }
+
+ win.removeEventListener("resize", resizeListener);
+}
+
+function roundedCenter(aDimension, aOrigin) {
+ let center = aOrigin + Math.floor(aDimension / 2);
+ return center - (center % 100);
+}
+
+class ResizeMoveTest {
+ static WindowWidth = 200;
+ static WindowHeight = 200;
+ static WindowLeft = roundedCenter(screen.availWidth - 200, screen.left);
+ static WindowTop = roundedCenter(screen.availHeight - 200, screen.top);
+
+ static PropInfo = {
+ sizeProps: ["outerWidth", "outerHeight", "innerWidth", "innerHeight"],
+ positionProps: [
+ "screenX",
+ "screenY",
+ /* readonly */ "mozInnerScreenX",
+ /* readonly */ "mozInnerScreenY",
+ ],
+ readonlyProps: ["mozInnerScreenX", "mozInnerScreenY"],
+ crossAxisMapping: {
+ outerWidth: "outerHeight",
+ outerHeight: "outerWidth",
+ innerWidth: "innerHeight",
+ innerHeight: "innerWidth",
+ screenX: "screenY",
+ screenY: "screenX",
+ mozInnerScreenX: "mozInnerScreenY",
+ mozInnerScreenY: "mozInnerScreenX",
+ },
+ crossBoundsMapping: {
+ outerWidth: "innerWidth",
+ outerHeight: "innerHeight",
+ innerWidth: "outerWidth",
+ innerHeight: "outerHeight",
+ screenX: "mozInnerScreenX",
+ screenY: "mozInnerScreenY",
+ mozInnerScreenX: "screenX",
+ mozInnerScreenY: "screenY",
+ },
+ };
+
+ constructor(
+ aPropertyDeltas,
+ aInstant = false,
+ aMsg = "ResizeMoveTest",
+ aWaitForCompletion = false
+ ) {
+ this.propertyDeltas = aPropertyDeltas;
+ this.instant = aInstant;
+ this.msg = aMsg;
+ this.waitForCompletion = aWaitForCompletion;
+
+ // Allows to ignore positions while testing.
+ this.ignorePositions = false;
+ // Allows to ignore only mozInnerScreenX/Y properties while testing.
+ this.ignoreMozInnerScreen = false;
+ // Allows to skip checking the restored position after testing.
+ this.ignoreRestoredPosition = false;
+
+ if (AppConstants.platform == "linux" && !SpecialPowers.isHeadless) {
+ // We can occasionally start the test while nsWindow reports a wrong
+ // client offset (gdk origin and root_origin are out of sync). This
+ // results in false expectations for the final mozInnerScreenX/Y values.
+ this.ignoreMozInnerScreen = !ResizeMoveTest.hasCleanUpTask;
+
+ let { positionProps } = ResizeMoveTest.PropInfo;
+ let resizeOnlyTest = aPropertyDeltas.every(deltaObj =>
+ positionProps.every(prop => deltaObj[prop] === undefined)
+ );
+
+ let isWayland = gfxInfo.windowProtocol == "wayland";
+ if (resizeOnlyTest && isWayland) {
+ // On Wayland we can't move the window in general. The window also
+ // doesn't necessarily open our specified position.
+ this.ignoreRestoredPosition = true;
+ // We can catch bad screenX/Y at the start of the first test in a
+ // window.
+ this.ignorePositions = !ResizeMoveTest.hasCleanUpTask;
+ }
+ }
+
+ if (!ResizeMoveTest.hasCleanUpTask) {
+ ResizeMoveTest.hasCleanUpTask = true;
+ registerCleanupFunction(ResizeMoveTest.Cleanup);
+ }
+
+ add_task(async () => {
+ let tab = await ResizeMoveTest.GetOrCreateTab();
+ let browsingContext =
+ await ResizeMoveTest.GetOrCreatePopupBrowsingContext();
+ if (!browsingContext) {
+ return;
+ }
+
+ info("=== Running in content. ===");
+ await this.run(browsingContext, `${this.msg} (content)`);
+ await this.restorePopupState(browsingContext);
+
+ info("=== Running in chrome. ===");
+ let popupChrome = browsingContext.topChromeWindow;
+ await this.run(popupChrome.browsingContext, `${this.msg} (chrome)`);
+ await this.restorePopupState(browsingContext);
+
+ info("=== Running in opener. ===");
+ await this.run(tab.linkedBrowser, `${this.msg} (opener)`);
+ await this.restorePopupState(browsingContext);
+ });
+ }
+
+ async run(aBrowsingContext, aMsg) {
+ let testType = this.instant ? "instant" : "fanned out";
+ let msg = `${aMsg} (${testType})`;
+
+ let propInfo = {};
+ for (let k in ResizeMoveTest.PropInfo) {
+ propInfo[k] = ResizeMoveTest.PropInfo[k];
+ }
+ if (this.ignoreMozInnerScreen) {
+ todo(false, `[${aMsg}] Shouldn't ignore mozInnerScreenX/Y.`);
+ propInfo.positionProps = propInfo.positionProps.filter(
+ prop => !["mozInnerScreenX", "mozInnerScreenY"].includes(prop)
+ );
+ }
+ if (this.ignorePositions) {
+ todo(false, `[${aMsg}] Shouldn't ignore position.`);
+ propInfo.positionProps = [];
+ }
+
+ info(`${msg}: ` + JSON.stringify(this.propertyDeltas));
+ await SpecialPowers.spawn(
+ aBrowsingContext,
+ [
+ this.propertyDeltas,
+ this.instant,
+ propInfo,
+ msg,
+ this.waitForCompletion,
+ ],
+ testPropertyDeltas
+ );
+ }
+
+ async restorePopupState(aBrowsingContext) {
+ info("Restore popup state.");
+
+ let { deltaWidth, deltaHeight } = await SpecialPowers.spawn(
+ aBrowsingContext,
+ [],
+ () => {
+ return {
+ deltaWidth: this.content.outerWidth - this.content.innerWidth,
+ deltaHeight: this.content.outerHeight - this.content.innerHeight,
+ };
+ }
+ );
+
+ let chromeWindow = aBrowsingContext.topChromeWindow;
+ let {
+ WindowLeft: left,
+ WindowTop: top,
+ WindowWidth: width,
+ WindowHeight: height,
+ } = ResizeMoveTest;
+
+ chromeWindow.resizeTo(width + deltaWidth, height + deltaHeight);
+ chromeWindow.moveTo(left, top);
+
+ await SpecialPowers.spawn(
+ aBrowsingContext,
+ [left, top, width, height, this.ignoreRestoredPosition],
+ async (aLeft, aTop, aWidth, aHeight, aIgnorePosition) => {
+ let win = this.content.wrappedJSObject;
+
+ info("Waiting for restored size.");
+ await ContentTaskUtils.waitForCondition(
+ () => win.innerWidth == aWidth && win.innerHeight === aHeight,
+ "Waiting for restored size."
+ );
+ is(win.innerWidth, aWidth, "Restored width.");
+ is(win.innerHeight, aHeight, "Restored height.");
+
+ if (!aIgnorePosition) {
+ info("Waiting for restored position.");
+ await ContentTaskUtils.waitForCondition(
+ () => win.screenX == aLeft && win.screenY === aTop,
+ "Waiting for restored position."
+ );
+ is(win.screenX, aLeft, "Restored screenX.");
+ is(win.screenY, aTop, "Restored screenY.");
+ } else {
+ todo(false, "Shouldn't ignore restored position.");
+ }
+ }
+ );
+ }
+
+ static async GetOrCreateTab() {
+ if (ResizeMoveTest.tab) {
+ return ResizeMoveTest.tab;
+ }
+
+ info("Opening tab.");
+ ResizeMoveTest.tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "https://example.net/browser/browser/base/content/test/popups/popup_blocker_a.html"
+ );
+ return ResizeMoveTest.tab;
+ }
+
+ static async GetOrCreatePopupBrowsingContext() {
+ if (ResizeMoveTest.popupBrowsingContext) {
+ if (!ResizeMoveTest.popupBrowsingContext.isActive) {
+ return undefined;
+ }
+ return ResizeMoveTest.popupBrowsingContext;
+ }
+
+ let tab = await ResizeMoveTest.GetOrCreateTab();
+ info("Opening popup.");
+ ResizeMoveTest.popupBrowsingContext = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [
+ ResizeMoveTest.WindowWidth,
+ ResizeMoveTest.WindowHeight,
+ ResizeMoveTest.WindowLeft,
+ ResizeMoveTest.WindowTop,
+ ],
+ async (aWidth, aHeight, aLeft, aTop) => {
+ let win = this.content.open(
+ this.content.document.location.href,
+ "_blank",
+ `left=${aLeft},top=${aTop},width=${aWidth},height=${aHeight}`
+ );
+ this.content.popup = win;
+
+ await new Promise(r => (win.onload = r));
+
+ return win.browsingContext;
+ }
+ );
+
+ return ResizeMoveTest.popupBrowsingContext;
+ }
+
+ static async Cleanup() {
+ let browsingContext = ResizeMoveTest.popupBrowsingContext;
+ if (browsingContext) {
+ await SpecialPowers.spawn(browsingContext, [], () => {
+ this.content.close();
+ });
+ delete ResizeMoveTest.popupBrowsingContext;
+ }
+
+ let tab = ResizeMoveTest.tab;
+ if (tab) {
+ await BrowserTestUtils.removeTab(tab);
+ delete ResizeMoveTest.tab;
+ }
+ ResizeMoveTest.hasCleanUpTask = false;
+ }
+}
+
+function chaosRequestLongerTimeout(aDoRequest) {
+ if (aDoRequest && parseInt(Services.env.get("MOZ_CHAOSMODE"), 16)) {
+ requestLongerTimeout(2);
+ }
+}
+
+function createGenericResizeTests(aFirstValue, aSecondValue, aInstant, aMsg) {
+ // Runtime almost doubles in chaos mode on Mac.
+ chaosRequestLongerTimeout(AppConstants.platform == "macosx");
+
+ let { crossBoundsMapping, crossAxisMapping } = ResizeMoveTest.PropInfo;
+
+ for (let prop of ["innerWidth", "outerHeight"]) {
+ // Mixing inner and outer property.
+ for (let secondProp of [prop, crossBoundsMapping[prop]]) {
+ let first = {};
+ first[prop] = aFirstValue;
+ let second = {};
+ second[secondProp] = aSecondValue;
+ new ResizeMoveTest(
+ [first, second],
+ aInstant,
+ `${aMsg} ${prop},${secondProp}`
+ );
+ }
+ }
+
+ for (let prop of ["innerHeight", "outerWidth"]) {
+ let first = {};
+ first[prop] = aFirstValue;
+ let second = {};
+ second[prop] = aSecondValue;
+
+ // Setting property of other axis before/between two changes.
+ let otherProps = [
+ crossAxisMapping[prop],
+ crossAxisMapping[crossBoundsMapping[prop]],
+ ];
+ for (let interferenceProp of otherProps) {
+ let interference = {};
+ interference[interferenceProp] = 20;
+ new ResizeMoveTest(
+ [first, interference, second],
+ aInstant,
+ `${aMsg} ${prop},${interferenceProp},${prop}`
+ );
+ new ResizeMoveTest(
+ [interference, first, second],
+ aInstant,
+ `${aMsg} ${interferenceProp},${prop},${prop}`
+ );
+ }
+ }
+}
+
+function createGenericMoveTests(aInstant, aMsg) {
+ // Runtime almost doubles in chaos mode on Mac.
+ chaosRequestLongerTimeout(AppConstants.platform == "macosx");
+
+ let { crossAxisMapping } = ResizeMoveTest.PropInfo;
+
+ for (let prop of ["screenX", "screenY"]) {
+ for (let [v1, v2, msg] of [
+ [9, 10, `${aMsg}`],
+ [11, 11, `${aMsg} repeat`],
+ [12, 0, `${aMsg} revert`],
+ ]) {
+ let first = {};
+ first[prop] = v1;
+ let second = {};
+ second[prop] = v2;
+ new ResizeMoveTest([first, second], aInstant, `${msg} ${prop},${prop}`);
+
+ let interferenceProp = crossAxisMapping[prop];
+ let interference = {};
+ interference[interferenceProp] = 20;
+ new ResizeMoveTest(
+ [first, interference, second],
+ aInstant,
+ `${aMsg} ${prop},${interferenceProp},${prop}`
+ );
+ new ResizeMoveTest(
+ [interference, first, second],
+ aInstant,
+ `${msg} ${interferenceProp},${prop},${prop}`
+ );
+ }
+ }
+}
diff --git a/browser/base/content/test/popups/popup_blocker.html b/browser/base/content/test/popups/popup_blocker.html
new file mode 100644
index 0000000000..8e2d958059
--- /dev/null
+++ b/browser/base/content/test/popups/popup_blocker.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>Page creating two popups</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ window.open("popup_blocker_a.html", "a");
+ window.open("popup_blocker_b.html", "b");
+ </script>
+ </body>
+</html>
diff --git a/browser/base/content/test/popups/popup_blocker2.html b/browser/base/content/test/popups/popup_blocker2.html
new file mode 100644
index 0000000000..ec880c0821
--- /dev/null
+++ b/browser/base/content/test/popups/popup_blocker2.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>Page creating a popup</title>
+ </head>
+ <body>
+ <button id="pop" onclick='window.setTimeout(() => {window.open("popup_blocker_a.html", "a");}, 10);'>Open Popup</button>
+ </body>
+</html>
diff --git a/browser/base/content/test/popups/popup_blocker_10_popups.html b/browser/base/content/test/popups/popup_blocker_10_popups.html
new file mode 100644
index 0000000000..9dc288f472
--- /dev/null
+++ b/browser/base/content/test/popups/popup_blocker_10_popups.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>Page creating ten popups</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ for (let i = 0; i < 10; i++) {
+ window.open("https://example.com");
+ }
+ </script>
+ </body>
+</html>
diff --git a/browser/base/content/test/popups/popup_blocker_a.html b/browser/base/content/test/popups/popup_blocker_a.html
new file mode 100644
index 0000000000..b6f94b5b26
--- /dev/null
+++ b/browser/base/content/test/popups/popup_blocker_a.html
@@ -0,0 +1 @@
+<html><body>a</body></html>
diff --git a/browser/base/content/test/popups/popup_blocker_b.html b/browser/base/content/test/popups/popup_blocker_b.html
new file mode 100644
index 0000000000..954061e2ce
--- /dev/null
+++ b/browser/base/content/test/popups/popup_blocker_b.html
@@ -0,0 +1 @@
+<html><body>b</body></html>
diff --git a/browser/base/content/test/popups/popup_blocker_frame.html b/browser/base/content/test/popups/popup_blocker_frame.html
new file mode 100644
index 0000000000..e29452fb63
--- /dev/null
+++ b/browser/base/content/test/popups/popup_blocker_frame.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>Page with iframe that contains page that opens two popups</title>
+ </head>
+ <body>
+ <iframe id="iframe"></iframe>
+ <script type="text/javascript">
+ let params = new URLSearchParams(location.search);
+ let base = params.get('base') || location.href;
+ let frame = document.getElementById('iframe');
+
+ function addPopupOpeningFrame() {
+ frame.src = new URL("popup_blocker.html", base);
+ }
+
+ if (params.get('delayed') !== 'true') {
+ addPopupOpeningFrame();
+ } else {
+ addEventListener("message", () => {
+ addPopupOpeningFrame();
+ }, {once: true});
+ }
+ </script>
+ </body>
+</html>
diff --git a/browser/base/content/test/popups/popup_size.html b/browser/base/content/test/popups/popup_size.html
new file mode 100644
index 0000000000..c214486dee
--- /dev/null
+++ b/browser/base/content/test/popups/popup_size.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>Page recording its size</title>
+ </head>
+ <body>
+ <script>
+ var initialSize = { width: innerWidth, height: innerHeight };
+ var loadSize;
+ onload = () => {
+ loadSize = { width: innerWidth, height: innerHeight };
+ };
+ </script>
+ </body>
+</html>