summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js')
-rw-r--r--browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js414
1 files changed, 207 insertions, 207 deletions
diff --git a/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js b/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js
index 140c2be2fd..3c652b26a2 100644
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js
@@ -44,22 +44,22 @@ add_setup(async function () {
});
});
+/**
+ * The security delay calculation in PopupNotification.sys.mjs is dependent on
+ * the monotonically increasing value of performance.now. This timestamp is
+ * not relative to a fixed date, but to runtime.
+ * We need to wait for the value performance.now() to be larger than the
+ * security delay in order to observe the bug. Only then does the
+ * timeSinceShown check in PopupNotifications.sys.mjs lead to a timeSinceShown
+ * value that is unconditionally greater than lazy.buttonDelay for
+ * notification.timeShown = null = 0.
+ * See: https://searchfox.org/mozilla-central/rev/f32d5f3949a3f4f185122142b29f2e3ab776836e/toolkit/modules/PopupNotifications.sys.mjs#1870-1872
+ *
+ * When running in automation as part of a larger test suite performance.now()
+ * should usually be already sufficiently high in which case this check should
+ * directly resolve.
+ */
async function ensureSecurityDelayReady() {
- /**
- * The security delay calculation in PopupNotification.sys.mjs is dependent on
- * the monotonically increasing value of performance.now. This timestamp is
- * not relative to a fixed date, but to runtime.
- * We need to wait for the value performance.now() to be larger than the
- * security delay in order to observe the bug. Only then does the
- * timeSinceShown check in PopupNotifications.sys.mjs lead to a timeSinceShown
- * value that is unconditionally greater than lazy.buttonDelay for
- * notification.timeShown = null = 0.
- * See: https://searchfox.org/mozilla-central/rev/f32d5f3949a3f4f185122142b29f2e3ab776836e/toolkit/modules/PopupNotifications.sys.mjs#1870-1872
- *
- * When running in automation as part of a larger test suite performance.now()
- * should usually be already sufficiently high in which case this check should
- * directly resolve.
- */
await TestUtils.waitForCondition(
() => performance.now() > TEST_SECURITY_DELAY,
"Wait for performance.now() > SECURITY_DELAY",
@@ -69,73 +69,73 @@ async function ensureSecurityDelayReady() {
}
/**
- * Tests that when we show a second notification while the panel is open the
- * timeShown attribute is correctly set and the security delay is enforced
- * properly.
+ * Test helper for security delay tests which performs the following steps:
+ * 1. Shows a test notification.
+ * 2. Waits for the notification panel to be shown and checks that the initial
+ * security delay blocks clicks.
+ * 3. Waits for the security delay to expire. Clicks should now be allowed.
+ * 4. Executes the provided onSecurityDelayExpired function. This function
+ * should renew the security delay.
+ * 5. Tests that the security delay was renewed.
+ * 6. Ensures that after the security delay the notification can be closed.
+ *
+ * @param {*} options
+ * @param {function} options.onSecurityDelayExpired - Function to run after the
+ * security delay has expired. This function should trigger a renew of the
+ * security delay.
+ * @param {function} options.cleanupFn - Optional cleanup function to run after
+ * the test has completed.
+ * @returns {Promise<void>} - Resolves when the test has completed.
*/
-add_task(async function test_timeShownMultipleNotifications() {
+async function runPopupNotificationSecurityDelayTest({
+ onSecurityDelayExpired,
+ cleanupFn = () => {},
+}) {
await ensureSecurityDelayReady();
- ok(
- !PopupNotifications.isPanelOpen,
- "PopupNotification panel should not be open initially."
- );
-
- info("Open the first notification.");
+ info("Open a notification.");
let popupShownPromise = waitForNotificationPanel();
showNotification();
await popupShownPromise;
ok(
PopupNotifications.isPanelOpen,
- "PopupNotification should be open after first show call."
+ "PopupNotification should be open after show call."
);
- is(
- PopupNotifications._currentNotifications.length,
- 1,
- "There should only be one notification"
+ // Test that the initial security delay works.
+ info(
+ "Trigger main action via button click during the initial security delay."
);
+ triggerMainCommand(PopupNotifications.panel);
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ ok(PopupNotifications.isPanelOpen, "PopupNotification should still be open.");
let notification = PopupNotifications.getNotification(
"foo",
gBrowser.selectedBrowser
);
- is(notification?.id, "foo", "There should be a notification with id foo");
- ok(notification.timeShown, "The notification should have timeShown set");
-
- info(
- "Call show again with the same notification id while the PopupNotification panel is still open."
- );
- showNotification();
ok(
- PopupNotifications.isPanelOpen,
- "PopupNotification should still open after second show call."
- );
- notification = PopupNotifications.getNotification(
- "foo",
- gBrowser.selectedBrowser
- );
- is(
- PopupNotifications._currentNotifications.length,
- 1,
- "There should still only be one notification"
+ notification,
+ "Notification should still be open because we clicked during the security delay."
);
+ // If the notification is no longer shown (test failure) skip the remaining
+ // checks.
+ if (!notification) {
+ await cleanupFn();
+ return;
+ }
- is(
- notification?.id,
- "foo",
- "There should still be a notification with id foo"
+ info("Wait for security delay to expire.");
+ await new Promise(resolve =>
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(resolve, TEST_SECURITY_DELAY + 500)
);
- ok(notification.timeShown, "The notification should have timeShown set");
- let notificationHiddenPromise = waitForNotificationPanelHidden();
-
- info("Trigger main action via button click during security delay");
-
- // Wait for a tick of the event loop to ensure the button we're clicking
- // has been slotted into moz-button-group
- await new Promise(resolve => setTimeout(resolve, 0));
+ info("Run test specific actions which restarts the security delay.");
+ await onSecurityDelayExpired();
+ info("Trigger main action via button click during the new security delay.");
triggerMainCommand(PopupNotifications.panel);
await new Promise(resolve => setTimeout(resolve, 0));
@@ -149,10 +149,10 @@ add_task(async function test_timeShownMultipleNotifications() {
notification,
"Notification should still be open because we clicked during the security delay."
);
-
// If the notification is no longer shown (test failure) skip the remaining
// checks.
if (!notification) {
+ await cleanupFn();
return;
}
@@ -163,6 +163,7 @@ add_task(async function test_timeShownMultipleNotifications() {
notification.timeShown = performance.now() - fakeTimeShown;
info("Trigger main action via button click outside security delay");
+ let notificationHiddenPromise = waitForNotificationPanelHidden();
triggerMainCommand(PopupNotifications.panel);
info("Wait for panel to be hidden.");
@@ -170,15 +171,19 @@ add_task(async function test_timeShownMultipleNotifications() {
ok(
!PopupNotifications.getNotification("foo", gBrowser.selectedBrowser),
- "Should not longer see the notification."
+ "Should no longer see the notification."
);
-});
+
+ info("Cleanup.");
+ await cleanupFn();
+}
/**
- * Tests that when we reshow a notification after a tab switch the timeShown
- * attribute is correctly reset and the security delay is enforced.
+ * Tests that when we show a second notification while the panel is open the
+ * timeShown attribute is correctly set and the security delay is enforced
+ * properly.
*/
-add_task(async function test_notificationReshowTabSwitch() {
+add_task(async function test_timeShownMultipleNotifications() {
await ensureSecurityDelayReady();
ok(
@@ -195,6 +200,12 @@ add_task(async function test_notificationReshowTabSwitch() {
"PopupNotification should be open after first show call."
);
+ is(
+ PopupNotifications._currentNotifications.length,
+ 1,
+ "There should only be one notification"
+ );
+
let notification = PopupNotifications.getNotification(
"foo",
gBrowser.selectedBrowser
@@ -202,69 +213,39 @@ add_task(async function test_notificationReshowTabSwitch() {
is(notification?.id, "foo", "There should be a notification with id foo");
ok(notification.timeShown, "The notification should have timeShown set");
- info("Trigger main action via button click during security delay");
- triggerMainCommand(PopupNotifications.panel);
-
- await new Promise(resolve => setTimeout(resolve, 0));
-
- ok(PopupNotifications.isPanelOpen, "PopupNotification should still be open.");
- notification = PopupNotifications.getNotification(
- "foo",
- gBrowser.selectedBrowser
- );
- ok(
- notification,
- "Notification should still be open because we clicked during the security delay."
- );
-
- // If the notification is no longer shown (test failure) skip the remaining
- // checks.
- if (!notification) {
- return;
- }
-
- let panelHiddenPromise = waitForNotificationPanelHidden();
- let panelShownPromise;
-
- info("Open a new tab which hides the notification panel.");
- await BrowserTestUtils.withNewTab("https://example.com", async () => {
- info("Wait for panel to be hidden by tab switch.");
- await panelHiddenPromise;
- info(
- "Keep the tab open until the security delay for the original notification show has expired."
- );
- await new Promise(resolve =>
- // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
- setTimeout(resolve, TEST_SECURITY_DELAY + 500)
- );
-
- panelShownPromise = waitForNotificationPanel();
- });
info(
- "Wait for the panel to show again after the tab close. We're showing the original tab again."
+ "Call show again with the same notification id while the PopupNotification panel is still open."
);
- await panelShownPromise;
-
+ showNotification();
ok(
PopupNotifications.isPanelOpen,
- "PopupNotification should be shown after tab close."
+ "PopupNotification should still open after second show call."
);
notification = PopupNotifications.getNotification(
"foo",
gBrowser.selectedBrowser
);
is(
+ PopupNotifications._currentNotifications.length,
+ 1,
+ "There should still only be one notification"
+ );
+
+ is(
notification?.id,
"foo",
"There should still be a notification with id foo"
);
+ ok(notification.timeShown, "The notification should have timeShown set");
let notificationHiddenPromise = waitForNotificationPanelHidden();
- info(
- "Because we re-show the panel after tab close / switch the security delay should have reset."
- );
- info("Trigger main action via button click during the new security delay.");
+ info("Trigger main action via button click during security delay");
+
+ // Wait for a tick of the event loop to ensure the button we're clicking
+ // has been slotted into moz-button-group
+ await new Promise(resolve => setTimeout(resolve, 0));
+
triggerMainCommand(PopupNotifications.panel);
await new Promise(resolve => setTimeout(resolve, 0));
@@ -278,6 +259,7 @@ add_task(async function test_notificationReshowTabSwitch() {
notification,
"Notification should still be open because we clicked during the security delay."
);
+
// If the notification is no longer shown (test failure) skip the remaining
// checks.
if (!notification) {
@@ -298,109 +280,83 @@ add_task(async function test_notificationReshowTabSwitch() {
ok(
!PopupNotifications.getNotification("foo", gBrowser.selectedBrowser),
- "Should not longer see the notification."
+ "Should no longer see the notification."
);
});
/**
+ * Tests that when we reshow a notification after a tab switch the timeShown
+ * attribute is correctly reset and the security delay is enforced.
+ */
+add_task(async function test_notificationReshowTabSwitch() {
+ await runPopupNotificationSecurityDelayTest({
+ onSecurityDelayExpired: async () => {
+ let panelHiddenPromise = waitForNotificationPanelHidden();
+ let panelShownPromise;
+
+ info("Open a new tab which hides the notification panel.");
+ await BrowserTestUtils.withNewTab("https://example.com", async () => {
+ info("Wait for panel to be hidden by tab switch.");
+ await panelHiddenPromise;
+ panelShownPromise = waitForNotificationPanel();
+ });
+ info(
+ "Wait for the panel to show again after the tab close. We're showing the original tab again."
+ );
+ await panelShownPromise;
+
+ ok(
+ PopupNotifications.isPanelOpen,
+ "PopupNotification should be shown after tab close."
+ );
+ let notification = PopupNotifications.getNotification(
+ "foo",
+ gBrowser.selectedBrowser
+ );
+ is(
+ notification?.id,
+ "foo",
+ "There should still be a notification with id foo"
+ );
+
+ info(
+ "Because we re-show the panel after tab close / switch the security delay should have reset."
+ );
+ },
+ });
+});
+
+/**
* Tests that the security delay gets reset when a window is repositioned and
* the PopupNotifications panel position is updated.
*/
add_task(async function test_notificationWindowMove() {
- await ensureSecurityDelayReady();
-
- info("Open a notification.");
- let popupShownPromise = waitForNotificationPanel();
- showNotification();
- await popupShownPromise;
- ok(
- PopupNotifications.isPanelOpen,
- "PopupNotification should be open after show call."
- );
-
- // Test that the initial security delay works.
- info("Trigger main action via button click during the new security delay.");
- triggerMainCommand(PopupNotifications.panel);
-
- await new Promise(resolve => setTimeout(resolve, 0));
-
- ok(PopupNotifications.isPanelOpen, "PopupNotification should still be open.");
- let notification = PopupNotifications.getNotification(
- "foo",
- gBrowser.selectedBrowser
- );
- ok(
- notification,
- "Notification should still be open because we clicked during the security delay."
- );
- // If the notification is no longer shown (test failure) skip the remaining
- // checks.
- if (!notification) {
- return;
- }
-
- info("Wait for security delay to expire.");
- await new Promise(resolve =>
- // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
- setTimeout(resolve, TEST_SECURITY_DELAY + 500)
- );
-
- info("Reposition the window");
- // Remember original window position.
- let { screenX, screenY } = window;
-
- let promisePopupPositioned = BrowserTestUtils.waitForEvent(
- PopupNotifications.panel,
- "popuppositioned"
- );
-
- // Move the window.
- window.moveTo(200, 200);
-
- // Wait for the panel to reposition and the PopupNotifications listener to run.
- await promisePopupPositioned;
- await new Promise(resolve => setTimeout(resolve, 0));
-
- info("Trigger main action via button click during the new security delay.");
- triggerMainCommand(PopupNotifications.panel);
-
- await new Promise(resolve => setTimeout(resolve, 0));
-
- ok(PopupNotifications.isPanelOpen, "PopupNotification should still be open.");
- notification = PopupNotifications.getNotification(
- "foo",
- gBrowser.selectedBrowser
- );
- ok(
- notification,
- "Notification should still be open because we clicked during the security delay."
- );
- // If the notification is no longer shown (test failure) skip the remaining
- // checks.
- if (!notification) {
- return;
- }
-
- // Ensure that once the security delay has passed the notification can be
- // closed again.
- let fakeTimeShown = TEST_SECURITY_DELAY + 500;
- info(`Manually set timeShown to ${fakeTimeShown}ms in the past.`);
- notification.timeShown = performance.now() - fakeTimeShown;
-
- info("Trigger main action via button click outside security delay");
- let notificationHiddenPromise = waitForNotificationPanelHidden();
- triggerMainCommand(PopupNotifications.panel);
-
- info("Wait for panel to be hidden.");
- await notificationHiddenPromise;
-
- ok(
- !PopupNotifications.getNotification("foo", gBrowser.selectedBrowser),
- "Should not longer see the notification."
- );
-
- // Reset window position
- window.moveTo(screenX, screenY);
+ let screenX, screenY;
+
+ await runPopupNotificationSecurityDelayTest({
+ onSecurityDelayExpired: async () => {
+ info("Reposition the window");
+ // Remember original window position.
+ screenX = window.screenX;
+ screenY = window.screenY;
+
+ let promisePopupPositioned = BrowserTestUtils.waitForEvent(
+ PopupNotifications.panel,
+ "popuppositioned"
+ );
+
+ // Move the window.
+ window.moveTo(200, 200);
+
+ // Wait for the panel to reposition and the PopupNotifications listener to run.
+ await promisePopupPositioned;
+ await new Promise(resolve => setTimeout(resolve, 0));
+ },
+ cleanupFn: async () => {
+ // Reset window position
+ window.moveTo(screenX, screenY);
+ },
+ });
});
/**
@@ -563,5 +519,49 @@ add_task(async function test_notificationDuringFullScreenTransition() {
info("Wait for full screen transition end.");
await promiseFullScreenTransitionEnd;
info("Full screen transition end");
+
+ await SpecialPowers.popPrefEnv();
+ });
+});
+
+/**
+ * Tests that the security delay gets extended when pointer lock is entered.
+ */
+add_task(async function test_notificationPointerLock() {
+ // We need a tab to enter pointer lock.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com"
+ );
+
+ await runPopupNotificationSecurityDelayTest({
+ onSecurityDelayExpired: async () => {
+ info("Enter pointer lock");
+ // Move focus to the browser to ensure pointer lock request succeeds.
+ gBrowser.selectedBrowser.focus();
+ let pointerLockEnterPromise = TestUtils.topicObserved(
+ "pointer-lock-entered"
+ );
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ SpecialPowers.wrap(content.document).notifyUserGestureActivation();
+ await content.document.body.requestPointerLock();
+ });
+
+ // Wait for pointer lock to be entered and the PopupNotifications listener to run.
+ await pointerLockEnterPromise;
+ await new Promise(resolve => setTimeout(resolve, 0));
+ },
+ cleanupFn: async () => {
+ // Cleanup.
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ SpecialPowers.wrap(content.document).notifyUserGestureActivation();
+ await content.document.exitPointerLock();
+ });
+ await TestUtils.waitForCondition(
+ () => !window.PointerLock.isActive,
+ "Wait for pointer lock exit."
+ );
+ BrowserTestUtils.removeTab(tab);
+ },
});
});