/* 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 lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { EveryWindow: "resource:///modules/EveryWindow.sys.mjs", PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", }); const NOTIFICATION_VALUE = "reduced-protection-reload"; const PREF = "privacy.reducePageProtection.infobar.enabled.pbmode"; export const ReducedProtectionNotification = { _initialized: false, _prefObserved: false, // Store per browsingContext whether we encountered blocked resources to only show the // ReducedProtectionNotification when a reload really would unblock resources _blockedTrackers: new WeakMap(), // We show the notification when the page loads and let it disappear again when the user // initiated a navigation _pendingNotification: new WeakMap(), // Per tab we only want to show the infobar at maximum once per host to not annoy users. // Keep track of hosts, where this infobar already appeared per tab. _shownHosts: new WeakMap(), observePref() { if (this._prefObserved) { return; } this._prefObserved = true; Services.prefs.addObserver(PREF, () => { if (Services.prefs.getBoolPref(PREF, false)) { this.init(); } else { this.uninit(); } }); if (Services.prefs.getBoolPref(PREF, false)) { this.init(); } }, init() { if (this._initialized) { return; } lazy.EveryWindow.registerCallback( "reduced-protection-notification", win => { if (lazy.PrivateBrowsingUtils.isWindowPrivate(win)) { win.gBrowser?.addTabsProgressListener(this); } }, win => { if (lazy.PrivateBrowsingUtils.isWindowPrivate(win)) { win.gBrowser?.removeTabsProgressListener(this); } } ); this._initialized = true; }, uninit() { if (!this._initialized) { return; } lazy.EveryWindow.unregisterCallback("reduced-protection-notification"); this._initialized = false; this._blockedTrackers = new WeakMap(); this._pendingNotification = new WeakMap(); this._shownHosts = new WeakMap(); }, onContentBlockingEvent(aBrowser, aWebProgress, aRequest, aEvent) { if (!aWebProgress.isTopLevel) { return; } if (aEvent & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) { this._blockedTrackers.set(aBrowser, true); } }, onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags) { if (!aWebProgress.isTopLevel) { return; } const START_MASK = Ci.nsIWebProgressListener.STATE_START | Ci.nsIWebProgressListener.STATE_IS_NETWORK; const isStart = (aStateFlags & START_MASK) === START_MASK; const STOP_MASK = Ci.nsIWebProgressListener.STATE_STOP | Ci.nsIWebProgressListener.STATE_IS_NETWORK; const isStop = (aStateFlags & STOP_MASK) === STOP_MASK; const isReload = (aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD) !== 0; const isAddressBarNavigation = (aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_NORMAL) !== 0; if (isStart) { if (isAddressBarNavigation) { this._shownHosts.delete(aBrowser); } if (isReload && this._blockedTrackers.get(aBrowser)) { this._pendingNotification.set(aBrowser, true); this._blockedTrackers.delete(aBrowser); } this.hideNotification(aBrowser); } else if (isStop && this._pendingNotification.get(aBrowser)) { this._pendingNotification.delete(aBrowser); this.showNotification(aBrowser); } }, hideNotification(aBrowser) { const notificationBox = aBrowser .getTabBrowser() ?.readNotificationBox(aBrowser); const notification = notificationBox?.getNotificationWithValue(NOTIFICATION_VALUE); if (notification) { notificationBox.removeNotification(notification); } }, async showNotification(aBrowser) { const tabbrowser = aBrowser.getTabBrowser(); if (!tabbrowser) { return; } const currentURI = aBrowser.currentURI; if (!currentURI) { return; } const host = currentURI.host; if (this._shownHosts.get(aBrowser)?.has(host)) { return; } const notificationBox = tabbrowser.getNotificationBox(aBrowser); if (notificationBox.getNotificationWithValue(NOTIFICATION_VALUE)) { return; } const doc = tabbrowser.ownerDocument; const [buttonLabel] = await doc.l10n.formatValues([ { id: "reduced-protection-infobar-reload-button" }, ]); const notification = await notificationBox.appendNotification( NOTIFICATION_VALUE, { priority: notificationBox.PRIORITY_INFO_LOW, }, [ { label: buttonLabel, callback: () => { const scopedPrefs = aBrowser.browsingContext.scopedPrefs; if (scopedPrefs) { const bc = aBrowser.browsingContext; for (const pref of [ Ci.nsIScopedPrefs.PRIVACY_TRACKINGPROTECTION_ENABLED, Ci.nsIScopedPrefs .PRIVACY_TRACKINGPROTECTION_CRYPTOMINING_ENABLED, Ci.nsIScopedPrefs .PRIVACY_TRACKINGPROTECTION_FINGERPRINTING_ENABLED, Ci.nsIScopedPrefs .PRIVACY_TRACKINGPROTECTION_SOCIALTRACKING_ENABLED, Ci.nsIScopedPrefs .PRIVACY_TRACKINGPROTECTION_EMAILTRACKING_ENABLED, ]) { scopedPrefs.setBoolPrefScoped(pref, bc, false); } } aBrowser.reload(); }, }, ] ); notification.persistence = -1; const msgSpan = doc.createElementNS("http://www.w3.org/1999/xhtml", "span"); msgSpan.setAttribute("slot", "message"); doc.l10n.setAttributes(msgSpan, "reduced-protection-infobar-message"); notification.appendChild(msgSpan); if (!this._shownHosts.has(aBrowser)) { this._shownHosts.set(aBrowser, new Set()); } this._shownHosts.get(aBrowser).add(host); }, };