forked from mirrors/gecko-dev
The identity popup will now show that there's an exception on the site instead of being confused. We previously didn't handle this state because I assumed it wouldn't be possible to enter. Turns out if you open the same (tracking) site in two tabs and then add an exception in one tab, the other tab will be in this state until reload. MozReview-Commit-ID: GVVdNpsAm6h --HG-- extra : rebase_source : 3ace3cd4f99094edf3c678c56a39adebdc37d1f3
362 lines
13 KiB
JavaScript
362 lines
13 KiB
JavaScript
/* 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/. */
|
|
|
|
var TrackingProtection = {
|
|
// If the user ignores the doorhanger, we stop showing it after some time.
|
|
MAX_INTROS: 20,
|
|
PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
|
|
PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
|
|
PREF_APP_MENU_TOGGLE: "privacy.trackingprotection.appMenuToggle.enabled",
|
|
PREF_ANIMATIONS_ENABLED: "toolkit.cosmeticAnimations.enabled",
|
|
enabledGlobally: false,
|
|
enabledInPrivateWindows: false,
|
|
container: null,
|
|
content: null,
|
|
icon: null,
|
|
activeTooltipText: null,
|
|
disabledTooltipText: null,
|
|
|
|
get _baseURIForChannelClassifier() {
|
|
// Convert document URI into the format used by
|
|
// nsChannelClassifier::ShouldEnableTrackingProtection.
|
|
// Any scheme turned into https is correct.
|
|
try {
|
|
return Services.io.newURI("https://" + gBrowser.selectedBrowser.currentURI.hostPort);
|
|
} catch (e) {
|
|
// Getting the hostPort for about: and file: URIs fails, but TP doesn't work with
|
|
// these URIs anyway, so just return null here.
|
|
return null;
|
|
}
|
|
},
|
|
|
|
init() {
|
|
let $ = selector => document.querySelector(selector);
|
|
this.container = $("#tracking-protection-container");
|
|
this.content = $("#tracking-protection-content");
|
|
this.icon = $("#tracking-protection-icon");
|
|
this.appMenuContainer = $("#appMenu-tp-container");
|
|
this.appMenuSeparator = $("#appMenu-tp-separator");
|
|
this.iconBox = $("#tracking-protection-icon-box");
|
|
this.animatedIcon = $("#tracking-protection-icon-animatable-image");
|
|
this.animatedIcon.addEventListener("animationend", () => this.iconBox.removeAttribute("animate"));
|
|
|
|
this.appMenuButton = $("#appMenu-tp-toggle");
|
|
|
|
this.enableTooltip =
|
|
gNavigatorBundle.getString("trackingProtection.toggle.enable.tooltip");
|
|
this.disableTooltip =
|
|
gNavigatorBundle.getString("trackingProtection.toggle.disable.tooltip");
|
|
this.enableTooltipPB =
|
|
gNavigatorBundle.getString("trackingProtection.toggle.enable.pbmode.tooltip");
|
|
this.disableTooltipPB =
|
|
gNavigatorBundle.getString("trackingProtection.toggle.disable.pbmode.tooltip");
|
|
|
|
this.updateAnimationsEnabled = () => {
|
|
this.iconBox.toggleAttribute("animationsenabled",
|
|
Services.prefs.getBoolPref(this.PREF_ANIMATIONS_ENABLED, false));
|
|
};
|
|
|
|
this.updateAnimationsEnabled();
|
|
|
|
Services.prefs.addObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
|
|
|
|
this.updateEnabled();
|
|
|
|
this.updateAppMenuToggle = () => {
|
|
if (Services.prefs.getBoolPref(this.PREF_APP_MENU_TOGGLE, false)) {
|
|
this.appMenuContainer.removeAttribute("hidden");
|
|
this.appMenuSeparator.removeAttribute("hidden");
|
|
} else {
|
|
this.appMenuContainer.setAttribute("hidden", "true");
|
|
this.appMenuSeparator.setAttribute("hidden", "true");
|
|
}
|
|
};
|
|
|
|
Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this);
|
|
Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
|
|
Services.prefs.addObserver(this.PREF_APP_MENU_TOGGLE, this.updateAppMenuToggle);
|
|
|
|
this.updateAppMenuToggle();
|
|
|
|
this.activeTooltipText =
|
|
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip");
|
|
this.disabledTooltipText =
|
|
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip");
|
|
|
|
this.enabledHistogramAdd(this.enabledGlobally);
|
|
this.disabledPBMHistogramAdd(!this.enabledInPrivateWindows);
|
|
},
|
|
|
|
uninit() {
|
|
Services.prefs.removeObserver(this.PREF_ENABLED_GLOBALLY, this);
|
|
Services.prefs.removeObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
|
|
Services.prefs.removeObserver(this.PREF_APP_MENU_TOGGLE, this.updateAppMenuToggle);
|
|
Services.prefs.removeObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
|
|
},
|
|
|
|
observe() {
|
|
this.updateEnabled();
|
|
},
|
|
|
|
get enabled() {
|
|
return this.enabledGlobally ||
|
|
(this.enabledInPrivateWindows &&
|
|
PrivateBrowsingUtils.isWindowPrivate(window));
|
|
},
|
|
|
|
onGlobalToggleCommand() {
|
|
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
|
Services.prefs.setBoolPref(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, !this.enabledInPrivateWindows);
|
|
} else {
|
|
Services.prefs.setBoolPref(this.PREF_ENABLED_GLOBALLY, !this.enabledGlobally);
|
|
}
|
|
},
|
|
|
|
hideIdentityPopupAndReload() {
|
|
document.getElementById("identity-popup").hidePopup();
|
|
BrowserReload();
|
|
},
|
|
|
|
openPreferences(origin) {
|
|
openPreferences("privacy-trackingprotection", { origin });
|
|
},
|
|
|
|
updateEnabled() {
|
|
this.enabledGlobally =
|
|
Services.prefs.getBoolPref(this.PREF_ENABLED_GLOBALLY);
|
|
this.enabledInPrivateWindows =
|
|
Services.prefs.getBoolPref(this.PREF_ENABLED_IN_PRIVATE_WINDOWS);
|
|
|
|
this.content.setAttribute("enabled", this.enabled);
|
|
|
|
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
|
this.appMenuButton.setAttribute("enabled", this.enabledInPrivateWindows);
|
|
this.appMenuButton.setAttribute("aria-pressed", this.enabledInPrivateWindows);
|
|
this.appMenuButton.setAttribute("tooltiptext", this.enabledInPrivateWindows ?
|
|
this.disableTooltipPB : this.enableTooltipPB);
|
|
} else {
|
|
this.appMenuButton.setAttribute("enabled", this.enabledGlobally);
|
|
this.appMenuButton.setAttribute("aria-pressed", this.enabledGlobally);
|
|
this.appMenuButton.setAttribute("tooltiptext", this.enabledGlobally ?
|
|
this.disableTooltip : this.enableTooltip);
|
|
}
|
|
},
|
|
|
|
enabledHistogramAdd(value) {
|
|
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
|
return;
|
|
}
|
|
Services.telemetry.getHistogramById("TRACKING_PROTECTION_ENABLED").add(value);
|
|
},
|
|
|
|
disabledPBMHistogramAdd(value) {
|
|
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
|
return;
|
|
}
|
|
Services.telemetry.getHistogramById("TRACKING_PROTECTION_PBM_DISABLED").add(value);
|
|
},
|
|
|
|
eventsHistogramAdd(value) {
|
|
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
|
return;
|
|
}
|
|
Services.telemetry.getHistogramById("TRACKING_PROTECTION_EVENTS").add(value);
|
|
},
|
|
|
|
shieldHistogramAdd(value) {
|
|
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
|
return;
|
|
}
|
|
Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value);
|
|
},
|
|
|
|
cancelAnimation() {
|
|
let iconAnimation = this.animatedIcon.getAnimations()[0];
|
|
if (iconAnimation && iconAnimation.currentTime) {
|
|
iconAnimation.cancel();
|
|
}
|
|
this.iconBox.removeAttribute("animate");
|
|
},
|
|
|
|
onSecurityChange(state, webProgress, isSimulated) {
|
|
let baseURI = this._baseURIForChannelClassifier;
|
|
|
|
// Don't deal with about:, file: etc.
|
|
if (!baseURI) {
|
|
this.cancelAnimation();
|
|
this.iconBox.removeAttribute("state");
|
|
return;
|
|
}
|
|
|
|
// The user might have navigated before the shield animation
|
|
// finished. In this case, reset the animation to be able to
|
|
// play it in full again and avoid choppiness.
|
|
if (webProgress.isTopLevel) {
|
|
this.cancelAnimation();
|
|
}
|
|
|
|
let isBlocking = state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
|
|
let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
|
|
|
|
// Check whether the user has added an exception for this site.
|
|
let hasException = false;
|
|
if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
|
|
hasException = PrivateBrowsingUtils.existsInTrackingAllowlist(baseURI);
|
|
} else {
|
|
hasException = Services.perms.testExactPermission(baseURI,
|
|
"trackingprotection") == Services.perms.ALLOW_ACTION;
|
|
}
|
|
|
|
if (hasException) {
|
|
this.iconBox.setAttribute("hasException", "true");
|
|
this.content.setAttribute("hasException", "true");
|
|
} else {
|
|
this.iconBox.removeAttribute("hasException");
|
|
this.content.removeAttribute("hasException");
|
|
}
|
|
|
|
if (isBlocking && this.enabled) {
|
|
if (isSimulated) {
|
|
this.cancelAnimation();
|
|
} else if (webProgress.isTopLevel) {
|
|
this.iconBox.setAttribute("animate", "true");
|
|
}
|
|
|
|
this.iconBox.setAttribute("tooltiptext", this.activeTooltipText);
|
|
this.iconBox.setAttribute("state", "blocked-tracking-content");
|
|
this.content.setAttribute("state", "blocked-tracking-content");
|
|
|
|
// Open the tracking protection introduction panel, if applicable.
|
|
if (this.enabledGlobally) {
|
|
let introCount = Services.prefs.getIntPref("privacy.trackingprotection.introCount");
|
|
if (introCount < TrackingProtection.MAX_INTROS) {
|
|
Services.prefs.setIntPref("privacy.trackingprotection.introCount", ++introCount);
|
|
Services.prefs.savePrefFile(null);
|
|
this.showIntroPanel();
|
|
}
|
|
}
|
|
|
|
this.shieldHistogramAdd(2);
|
|
} else if (isAllowing) {
|
|
if (isSimulated) {
|
|
this.cancelAnimation();
|
|
} else if (webProgress.isTopLevel) {
|
|
this.iconBox.setAttribute("animate", "true");
|
|
}
|
|
|
|
// Only show the shield when TP is enabled for now.
|
|
if (this.enabled) {
|
|
this.iconBox.setAttribute("tooltiptext", this.disabledTooltipText);
|
|
this.iconBox.setAttribute("state", "loaded-tracking-content");
|
|
this.shieldHistogramAdd(1);
|
|
} else {
|
|
this.iconBox.removeAttribute("tooltiptext");
|
|
this.iconBox.removeAttribute("state");
|
|
this.shieldHistogramAdd(0);
|
|
}
|
|
|
|
// Warn in the control center even with TP disabled.
|
|
this.content.setAttribute("state", "loaded-tracking-content");
|
|
} else {
|
|
this.iconBox.removeAttribute("tooltiptext");
|
|
this.iconBox.removeAttribute("state");
|
|
this.content.removeAttribute("state");
|
|
|
|
// We didn't show the shield
|
|
this.shieldHistogramAdd(0);
|
|
}
|
|
|
|
// Telemetry for state change.
|
|
this.eventsHistogramAdd(0);
|
|
},
|
|
|
|
disableForCurrentPage() {
|
|
let baseURI = this._baseURIForChannelClassifier;
|
|
|
|
// Add the current host in the 'trackingprotection' consumer of
|
|
// the permission manager using a normalized URI. This effectively
|
|
// places this host on the tracking protection allowlist.
|
|
if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
|
|
PrivateBrowsingUtils.addToTrackingAllowlist(baseURI);
|
|
} else {
|
|
Services.perms.add(baseURI,
|
|
"trackingprotection", Services.perms.ALLOW_ACTION);
|
|
}
|
|
|
|
// Telemetry for disable protection.
|
|
this.eventsHistogramAdd(1);
|
|
|
|
this.hideIdentityPopupAndReload();
|
|
},
|
|
|
|
enableForCurrentPage() {
|
|
// Remove the current host from the 'trackingprotection' consumer
|
|
// of the permission manager. This effectively removes this host
|
|
// from the tracking protection allowlist.
|
|
let baseURI = this._baseURIForChannelClassifier;
|
|
|
|
if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
|
|
PrivateBrowsingUtils.removeFromTrackingAllowlist(baseURI);
|
|
} else {
|
|
Services.perms.remove(baseURI, "trackingprotection");
|
|
}
|
|
|
|
// Telemetry for enable protection.
|
|
this.eventsHistogramAdd(2);
|
|
|
|
this.hideIdentityPopupAndReload();
|
|
},
|
|
|
|
dontShowIntroPanelAgain() {
|
|
// This function may be called in private windows, but it does not change
|
|
// any preference unless Tracking Protection is enabled globally.
|
|
if (this.enabledGlobally) {
|
|
Services.prefs.setIntPref("privacy.trackingprotection.introCount",
|
|
this.MAX_INTROS);
|
|
Services.prefs.savePrefFile(null);
|
|
}
|
|
},
|
|
|
|
async showIntroPanel() {
|
|
let brandBundle = document.getElementById("bundle_brand");
|
|
let brandShortName = brandBundle.getString("brandShortName");
|
|
|
|
let openStep2 = () => {
|
|
// When the user proceeds in the tour, adjust the counter to indicate that
|
|
// the user doesn't need to see the intro anymore.
|
|
this.dontShowIntroPanelAgain();
|
|
|
|
let nextURL = Services.urlFormatter.formatURLPref("privacy.trackingprotection.introURL") +
|
|
"?step=2&newtab=true";
|
|
switchToTabHavingURI(nextURL, true, {
|
|
// Ignore the fragment in case the intro is shown on the tour page
|
|
// (e.g. if the user manually visited the tour or clicked the link from
|
|
// about:privatebrowsing) so we can avoid a reload.
|
|
ignoreFragment: "whenComparingAndReplace",
|
|
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
|
});
|
|
};
|
|
|
|
let buttons = [
|
|
{
|
|
label: gNavigatorBundle.getString("trackingProtection.intro.step1of3"),
|
|
style: "text",
|
|
},
|
|
{
|
|
callback: openStep2,
|
|
label: gNavigatorBundle.getString("trackingProtection.intro.nextButton.label"),
|
|
style: "primary",
|
|
},
|
|
];
|
|
|
|
let panelTarget = await UITour.getTarget(window, "trackingProtection");
|
|
UITour.initForBrowser(gBrowser.selectedBrowser, window);
|
|
UITour.showInfo(window, panelTarget,
|
|
gNavigatorBundle.getString("trackingProtection.intro.title"),
|
|
gNavigatorBundle.getFormattedString("trackingProtection.intro.description2",
|
|
[brandShortName]),
|
|
undefined, buttons,
|
|
{ closeButtonCallback: () => this.dontShowIntroPanelAgain() });
|
|
},
|
|
};
|