Source code
Revision control
Copy as Markdown
Other Tools
/* 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",
});
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 => {
win.gBrowser?.addTabsProgressListener(this);
},
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);
},
};