tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 7335419dc74e51557d757727b705952bab8bc7c9
parent f1edecc8d8b622292ad619d6542bea759df005d2
Author: moz-mdauer <mdauer@mozilla.com>
Date:   Tue, 23 Dec 2025 18:30:50 +0000

Bug 2006600 - Keep track of dismissed popup-blocker notifications, r=emz

Differential Revision: https://phabricator.services.mozilla.com/D276969

Diffstat:
Mbrowser/base/content/test/popups/browser_popup_blocker.js | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/modules/PopupAndRedirectBlockerObserver.sys.mjs | 24++++++++++++++++++------
Mtoolkit/actors/PopupAndRedirectBlockingParent.sys.mjs | 41++++++++++++++++++++++++++++++++++++++++-
3 files changed, 108 insertions(+), 7 deletions(-)

diff --git a/browser/base/content/test/popups/browser_popup_blocker.js b/browser/base/content/test/popups/browser_popup_blocker.js @@ -107,6 +107,56 @@ add_task(async function test_opening_blocked_popups_about_privatebrowsing() { await testPopupBlockingToolbar(tab); }); +// Bug 2006600. +// When a notification has been dismissed by a user, it should not appear +// again when switching to a different tab and back. +add_task(async function test_dismissed_notification_switch_tabs() { + // Open the test page. + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + baseURL + "popup_blocker.html" + ); + + // Wait for the notification. + let notification; + await TestUtils.waitForCondition( + () => + (notification = gBrowser + .getNotificationBox() + .getNotificationWithValue("popup-blocked")) + ); + + // Click dismiss button. + const mozButton = notification.shadowRoot.querySelector("moz-button.close"); + mozButton.click(); + + // Open a new (foreground) tab and switch back. + const differentTab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:blank" + ); + await BrowserTestUtils.switchTab(gBrowser, tab); + + // Make sure no notification appears. + try { + await TestUtils.waitForCondition( + () => + (notification = gBrowser + .getNotificationBox() + .getNotificationWithValue("popup-blocked")), + null, + 50, + 10 + ); + } catch (e) { + notification = null; + } + ok(!notification, "Notification should not reappear"); + + gBrowser.removeTab(tab); + gBrowser.removeTab(differentTab); +}); + async function testPopupBlockingToolbar(tab) { let win = tab.ownerGlobal; // Wait for the popup-blocked notification. diff --git a/browser/modules/PopupAndRedirectBlockerObserver.sys.mjs b/browser/modules/PopupAndRedirectBlockerObserver.sys.mjs @@ -6,11 +6,10 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; export var PopupAndRedirectBlockerObserver = { /** - * This is to check if we are currently in the process of appending a - * notification. - * `NotificationBox.appendNotification()` runs asynchronously and - * returns a promise. While it is resolving, `NotificationBox.getNotificationWithValue()` - * will still return null. + * Check if we are currently in the process of appending a notification. + * We can't rely on `getNotificationWithValue()`: It returns `null` + * while `appendNotification()` is resolving, so we keep track of the + * promise instead. */ mNotificationPromise: null, @@ -87,6 +86,15 @@ export var PopupAndRedirectBlockerObserver = { }, async showBrowserMessage(aBrowser, aPopupCount, aIsRedirectBlocked) { + const selectedBrowser = aBrowser.selectedBrowser; + const popupAndRedirectBlocker = selectedBrowser.popupAndRedirectBlocker; + + // Check if the notification was previously shown and then dismissed + // by the user. + if (popupAndRedirectBlocker.hasBeenDismissed()) { + return; + } + const l10nId = (() => { if (aPopupCount >= this.maxReportedPopups) { return aIsRedirectBlocked @@ -115,9 +123,13 @@ export var PopupAndRedirectBlockerObserver = { const image = "chrome://browser/skin/notification-icons/popup.svg"; const priority = notificationBox.PRIORITY_INFO_MEDIUM; + const eventCallback = popupAndRedirectBlocker.eventCallback.bind( + popupAndRedirectBlocker + ); + this.mNotificationPromise = notificationBox.appendNotification( "popup-blocked", - { label, image, priority }, + { label, image, priority, eventCallback }, [ { "l10n-id": "popup-warning-button", diff --git a/toolkit/actors/PopupAndRedirectBlockingParent.sys.mjs b/toolkit/actors/PopupAndRedirectBlockingParent.sys.mjs @@ -25,11 +25,20 @@ export class PopupAndRedirectBlocker { * @type {WeakMap<WindowGlobalParent, BrowsingContext>} */ #mBlockedRedirects; + /** + * WeakSet of all the browser's top-level WindowGlobal objects that had + * their notification dismissed by the user. + * If it has been dismissed once, we don't want to show it again. + * + * @type {WeakSet<WindowGlobalParent>} + */ + #mHasBeenDismissed; constructor(aBrowser) { this.#mBrowser = aBrowser; this.#mBlockedPopupCounts = new WeakMap(); this.#mBlockedRedirects = new WeakMap(); + this.#mHasBeenDismissed = new WeakSet(); } getBlockedPopupCount() { @@ -60,12 +69,40 @@ export class PopupAndRedirectBlocker { const browserBC = this.#mBrowser.browsingContext; const browserWG = browserBC.currentWindowGlobal; if (!browserWG) { - return null; + return false; } return this.#mBlockedRedirects.has(browserWG); } + hasBeenDismissed() { + const browserBC = this.#mBrowser.browsingContext; + const browserWG = browserBC.currentWindowGlobal; + if (!browserWG) { + return false; + } + + return this.#mHasBeenDismissed.has(browserWG); + } + + /** + * Event callback for the notification that is shown when a popup or + * redirect is blocked. This is used in the observer. + * + * @param {*} reason + */ + eventCallback(reason) { + if (reason == "dismissed") { + const browserBC = this.#mBrowser.browsingContext; + const browserWG = browserBC.currentWindowGlobal; + if (!browserWG) { + return; + } + + this.#mHasBeenDismissed.add(browserWG); + } + } + async getBlockedPopups() { const contextsToVisit = [this.#mBrowser.browsingContext]; const result = []; @@ -223,6 +260,8 @@ export class PopupAndRedirectBlocker { this.#mBlockedRedirects.delete(browserWG); this.sendObserverUpdateBlockedRedirectEvent(); + + this.#mHasBeenDismissed.delete(browserWG); } } }