fune/browser/base/content/browser-contentblocking.js
Ehsan Akhgari 09d4b61b43 Bug 1501286 - Part 2: Remove support for tracking protection UI from Control Centre r=johannh
Depends on D9519

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

--HG--
extra : moz-landing-system : lando
2018-10-25 16:52:19 +00:00

599 lines
23 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 FastBlock = {
reportBreakageLabel: "fastblock",
telemetryIdentifier: "fb",
PREF_ENABLED: "browser.fastblock.enabled",
PREF_UI_ENABLED: "browser.contentblocking.fastblock.control-center.ui.enabled",
get categoryItem() {
delete this.categoryItem;
return this.categoryItem = document.getElementById("identity-popup-content-blocking-category-fastblock");
},
init() {
XPCOMUtils.defineLazyPreferenceGetter(this, "enabled", this.PREF_ENABLED, false);
XPCOMUtils.defineLazyPreferenceGetter(this, "visible", this.PREF_UI_ENABLED, false);
},
isBlockerActivated(state) {
return state & Ci.nsIWebProgressListener.STATE_BLOCKED_SLOW_TRACKING_CONTENT;
},
};
var TrackingProtection = {
reportBreakageLabel: "trackingprotection",
telemetryIdentifier: "tp",
PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
PREF_UI_ENABLED: "browser.contentblocking.trackingprotection.control-center.ui.enabled",
enabledGlobally: false,
enabledInPrivateWindows: false,
get categoryItem() {
delete this.categoryItem;
return this.categoryItem =
document.getElementById("identity-popup-content-blocking-category-tracking-protection");
},
init() {
this.updateEnabled();
Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this);
Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
XPCOMUtils.defineLazyPreferenceGetter(this, "visible", this.PREF_UI_ENABLED, false);
},
uninit() {
Services.prefs.removeObserver(this.PREF_ENABLED_GLOBALLY, this);
Services.prefs.removeObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
},
observe() {
this.updateEnabled();
},
get enabled() {
return this.enabledGlobally ||
(this.enabledInPrivateWindows &&
PrivateBrowsingUtils.isWindowPrivate(window));
},
updateEnabled() {
this.enabledGlobally =
Services.prefs.getBoolPref(this.PREF_ENABLED_GLOBALLY);
this.enabledInPrivateWindows =
Services.prefs.getBoolPref(this.PREF_ENABLED_IN_PRIVATE_WINDOWS);
},
isBlockerActivated(state) {
return state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
},
};
var ThirdPartyCookies = {
telemetryIdentifier: "cr",
PREF_ENABLED: "network.cookie.cookieBehavior",
PREF_REPORT_BREAKAGE_ENABLED: "browser.contentblocking.rejecttrackers.reportBreakage.enabled",
PREF_ENABLED_VALUES: [
// These values match the ones exposed under the Content Blocking section
// of the Preferences UI.
Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN, // Block all third-party cookies
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER, // Block third-party cookies from trackers
],
PREF_UI_ENABLED: "browser.contentblocking.rejecttrackers.control-center.ui.enabled",
get categoryItem() {
delete this.categoryItem;
return this.categoryItem =
document.getElementById("identity-popup-content-blocking-category-3rdpartycookies");
},
get reportBreakageLabel() {
switch (this.behaviorPref) {
case Ci.nsICookieService.BEHAVIOR_ACCEPT:
return "nocookiesblocked";
case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
return "allthirdpartycookiesblocked";
case Ci.nsICookieService.BEHAVIOR_REJECT:
return "allcookiesblocked";
case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
return "cookiesfromunvisitedsitesblocked";
default:
Cu.reportError(`Error: Unknown cookieBehavior pref observed: ${this.behaviorPref}`);
// fall through
case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
return "cookierestrictions";
}
},
get categoryLabelDefault() {
delete this.categoryLabelDefault;
return this.categoryLabelDefault =
document.getElementById("identity-popup-content-blocking-category-label-default");
},
get categoryLabelTrackers() {
delete this.categoryLabelTrackers;
return this.categoryLabelTrackers =
document.getElementById("identity-popup-content-blocking-category-label-trackers");
},
updateCategoryLabel() {
let rejectTrackers = this.behaviorPref == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
this.categoryLabelDefault.hidden = rejectTrackers;
this.categoryLabelTrackers.hidden = !rejectTrackers;
},
init() {
XPCOMUtils.defineLazyPreferenceGetter(this, "behaviorPref", this.PREF_ENABLED,
Ci.nsICookieService.BEHAVIOR_ACCEPT, this.updateCategoryLabel.bind(this));
XPCOMUtils.defineLazyPreferenceGetter(this, "visible", this.PREF_UI_ENABLED, false);
XPCOMUtils.defineLazyPreferenceGetter(this, "reportBreakageEnabled",
this.PREF_REPORT_BREAKAGE_ENABLED, false);
this.updateCategoryLabel();
},
get enabled() {
return this.PREF_ENABLED_VALUES.includes(this.behaviorPref);
},
isBlockerActivated(state) {
return (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER) != 0 ||
(state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_FOREIGN) != 0;
},
};
var ContentBlocking = {
// If the user ignores the doorhanger, we stop showing it after some time.
MAX_INTROS: 20,
PREF_ENABLED: "browser.contentblocking.enabled",
PREF_ANIMATIONS_ENABLED: "toolkit.cosmeticAnimations.enabled",
PREF_REPORT_BREAKAGE_ENABLED: "browser.contentblocking.reportBreakage.enabled",
PREF_REPORT_BREAKAGE_URL: "browser.contentblocking.reportBreakage.url",
PREF_INTRO_COUNT_CB: "browser.contentblocking.introCount",
PREF_GLOBAL_TOGGLE: "browser.contentblocking.global-toggle.enabled",
content: null,
icon: null,
activeTooltipText: null,
disabledTooltipText: null,
get prefIntroCount() {
return this.PREF_INTRO_COUNT_CB;
},
get appMenuLabel() {
delete this.appMenuLabel;
return this.appMenuLabel = document.getElementById("appMenu-tp-label");
},
get appMenuButton() {
delete this.appMenuButton;
return this.appMenuButton = document.getElementById("appMenu-tp-toggle");
},
get appMenuVerticalSeparator() {
delete this.appMenuVerticalSeparator;
return this.appMenuVerticalSeparator = document.getElementById("appMenu-tp-vertical-separator");
},
strings: {
get enableTooltip() {
delete this.enableTooltip;
return this.enableTooltip =
gNavigatorBundle.getString("contentBlocking.toggle.enable.tooltip");
},
get disableTooltip() {
delete this.disableTooltip;
return this.disableTooltip =
gNavigatorBundle.getString("contentBlocking.toggle.disable.tooltip");
},
get appMenuTitle() {
delete this.appMenuTitle;
return this.appMenuTitle =
gNavigatorBundle.getString("contentBlocking.title");
},
get appMenuTooltip() {
delete this.appMenuTooltip;
return this.appMenuTooltip =
gNavigatorBundle.getString("contentBlocking.tooltip");
},
},
// A list of blockers that will be displayed in the categories list
// when blockable content is detected. A blocker must be an object
// with at least the following two properties:
// - enabled: Whether the blocker is currently turned on.
// - categoryItem: The DOM item that represents the entry in the category list.
//
// It may also contain an init() and uninit() function, which will be called
// on ContentBlocking.init() and ContentBlocking.uninit().
blockers: [FastBlock, TrackingProtection, ThirdPartyCookies],
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.content = $("#identity-popup-content-blocking-content");
this.icon = $("#tracking-protection-icon");
this.iconBox = $("#tracking-protection-icon-box");
this.animatedIcon = $("#tracking-protection-icon-animatable-image");
this.animatedIcon.addEventListener("animationend", () => this.iconBox.removeAttribute("animate"));
this.identityPopupMultiView = $("#identity-popup-multiView");
this.reportBreakageButton = $("#identity-popup-content-blocking-report-breakage");
this.reportBreakageURL = $("#identity-popup-breakageReportView-collection-url");
this.reportBreakageLearnMore = $("#identity-popup-breakageReportView-learn-more");
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
this.reportBreakageLearnMore.href = baseURL + "blocking-breakage";
this.updateGlobalToggleVisibility = () => {
if (Services.prefs.getBoolPref(this.PREF_GLOBAL_TOGGLE, true)) {
this.appMenuButton.removeAttribute("hidden");
this.appMenuVerticalSeparator.removeAttribute("hidden");
} else {
this.appMenuButton.setAttribute("hidden", "true");
this.appMenuVerticalSeparator.setAttribute("hidden", "true");
}
};
this.updateGlobalToggleVisibility();
Services.prefs.addObserver(this.PREF_GLOBAL_TOGGLE, this.updateGlobalToggleVisibility);
this.updateAnimationsEnabled = () => {
this.iconBox.toggleAttribute("animationsenabled",
Services.prefs.getBoolPref(this.PREF_ANIMATIONS_ENABLED, false));
};
for (let blocker of this.blockers) {
if (blocker.init) {
blocker.init();
}
}
this.updateAnimationsEnabled();
Services.prefs.addObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingEnabled", this.PREF_ENABLED, false,
this.updateEnabled.bind(this));
XPCOMUtils.defineLazyPreferenceGetter(this, "reportBreakageEnabled",
this.PREF_REPORT_BREAKAGE_ENABLED, false);
this.updateEnabled();
this.appMenuLabel.setAttribute("label", this.strings.appMenuTitle);
this.appMenuLabel.setAttribute("tooltiptext", this.strings.appMenuTooltip);
this.activeTooltipText =
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip");
this.disabledTooltipText =
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip");
},
uninit() {
for (let blocker of this.blockers) {
if (blocker.uninit) {
blocker.uninit();
}
}
Services.prefs.removeObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
Services.prefs.removeObserver(this.PREF_GLOBAL_TOGGLE, this.updateGlobalToggleVisibility);
},
get enabled() {
return this.contentBlockingEnabled;
},
updateEnabled() {
this.content.toggleAttribute("enabled", this.enabled);
this.appMenuButton.setAttribute("tooltiptext", this.enabled ?
this.strings.disableTooltip : this.strings.enableTooltip);
this.appMenuButton.setAttribute("enabled", this.enabled);
this.appMenuButton.setAttribute("aria-pressed", this.enabled);
// The enabled state of blockers may also change since it depends on this.enabled.
for (let blocker of this.blockers) {
blocker.categoryItem.classList.toggle("blocked", this.enabled && blocker.enabled);
}
},
onGlobalToggleCommand() {
Services.prefs.setBoolPref(this.PREF_ENABLED, !this.enabled);
},
hideIdentityPopupAndReload() {
document.getElementById("identity-popup").hidePopup();
BrowserReload();
},
openPreferences(origin) {
openPreferences("privacy-trackingprotection", { origin });
},
backToMainView() {
this.identityPopupMultiView.goBack();
},
submitBreakageReport() {
document.getElementById("identity-popup").hidePopup();
let reportEndpoint = Services.prefs.getStringPref(this.PREF_REPORT_BREAKAGE_URL);
if (!reportEndpoint) {
return;
}
let formData = new FormData();
formData.set("title", this.reportURI.host);
// Leave the ? at the end of the URL to signify that this URL had its query stripped.
let urlWithoutQuery = this.reportURI.asciiSpec.replace(this.reportURI.query, "");
let body = `Full URL: ${urlWithoutQuery}\n`;
body += `userAgent: ${navigator.userAgent}\n`;
body += "\n**Preferences**\n";
body += `${TrackingProtection.PREF_ENABLED_GLOBALLY}: ${Services.prefs.getBoolPref(TrackingProtection.PREF_ENABLED_GLOBALLY)}\n`;
body += `${TrackingProtection.PREF_ENABLED_IN_PRIVATE_WINDOWS}: ${Services.prefs.getBoolPref(TrackingProtection.PREF_ENABLED_IN_PRIVATE_WINDOWS)}\n`;
body += `${TrackingProtection.PREF_UI_ENABLED}: ${Services.prefs.getBoolPref(TrackingProtection.PREF_UI_ENABLED)}\n`;
body += `urlclassifier.trackingTable: ${Services.prefs.getStringPref("urlclassifier.trackingTable")}\n`;
body += `network.http.referer.defaultPolicy: ${Services.prefs.getIntPref("network.http.referer.defaultPolicy")}\n`;
body += `network.http.referer.defaultPolicy.pbmode: ${Services.prefs.getIntPref("network.http.referer.defaultPolicy.pbmode")}\n`;
body += `${ThirdPartyCookies.PREF_UI_ENABLED}: ${Services.prefs.getBoolPref(ThirdPartyCookies.PREF_UI_ENABLED)}\n`;
body += `${ThirdPartyCookies.PREF_ENABLED}: ${Services.prefs.getIntPref(ThirdPartyCookies.PREF_ENABLED)}\n`;
body += `network.cookie.lifetimePolicy: ${Services.prefs.getIntPref("network.cookie.lifetimePolicy")}\n`;
body += `privacy.restrict3rdpartystorage.expiration: ${Services.prefs.getIntPref("privacy.restrict3rdpartystorage.expiration")}\n`;
body += `${FastBlock.PREF_ENABLED}: ${Services.prefs.getBoolPref(FastBlock.PREF_ENABLED)}\n`;
body += `${FastBlock.PREF_UI_ENABLED}: ${Services.prefs.getBoolPref(FastBlock.PREF_UI_ENABLED)}\n`;
body += `browser.fastblock.timeout: ${Services.prefs.getIntPref("browser.fastblock.timeout")}\n`;
let comments = document.getElementById("identity-popup-breakageReportView-collection-comments");
body += "\n**Comments**\n" + comments.value;
formData.set("body", body);
let activatedBlockers = [];
for (let blocker of this.blockers) {
if (blocker.activated) {
activatedBlockers.push(blocker.reportBreakageLabel);
}
}
if (activatedBlockers.length) {
formData.set("labels", activatedBlockers.join(","));
}
fetch(reportEndpoint, {
method: "POST",
credentials: "omit",
body: formData,
}).then(function(response) {
if (!response.ok) {
Cu.reportError(`Content Blocking report to ${reportEndpoint} failed with status ${response.status}`);
}
}).catch(Cu.reportError);
},
showReportBreakageSubview() {
// Save this URI to make sure that the user really only submits the location
// they see in the report breakage dialog.
this.reportURI = gBrowser.currentURI;
let urlWithoutQuery = this.reportURI.asciiSpec.replace("?" + this.reportURI.query, "");
this.reportBreakageURL.textContent = urlWithoutQuery;
this.identityPopupMultiView.showSubView("identity-popup-breakageReportView");
},
shieldHistogramAdd(value) {
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
return;
}
Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value);
},
onSecurityChange(oldState, state, webProgress, isSimulated,
contentBlockingLogJSON) {
let baseURI = this._baseURIForChannelClassifier;
// Don't deal with about:, file: etc.
if (!baseURI) {
this.iconBox.removeAttribute("animate");
this.iconBox.removeAttribute("active");
this.iconBox.removeAttribute("hasException");
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.iconBox.removeAttribute("animate");
}
let anyBlockerActivated = false;
for (let blocker of this.blockers) {
// Store data on whether the blocker is activated in the current document for
// reporting it using the "report breakage" dialog. Under normal circumstances this
// dialog should only be able to open in the currently selected tab and onSecurityChange
// runs on tab switch, so we can avoid associating the data with the document directly.
blocker.activated = blocker.isBlockerActivated(state);
blocker.categoryItem.classList.toggle("blocked", this.enabled && blocker.enabled);
blocker.categoryItem.hidden = !blocker.visible;
anyBlockerActivated = anyBlockerActivated || blocker.activated;
}
// We consider the shield state "active" when some kind of blocking activity
// occurs on the page. Note that merely allowing the loading of content that
// we could have blocked does not trigger the appearance of the shield.
// This state will be overriden later if there's an exception set for this site.
let active = this.enabled && anyBlockerActivated;
let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
let detected = anyBlockerActivated || isAllowing;
let isBrowserPrivate = PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser);
// Check whether the user has added an exception for this site.
let type = isBrowserPrivate ? "trackingprotection-pb" : "trackingprotection";
let hasException = Services.perms.testExactPermission(baseURI, type) ==
Services.perms.ALLOW_ACTION;
this.content.toggleAttribute("detected", detected);
this.content.toggleAttribute("hasException", hasException);
this.content.toggleAttribute("active", active);
this.iconBox.toggleAttribute("active", active);
this.iconBox.toggleAttribute("hasException", this.enabled && hasException);
// For release (due to the large volume) we only want to receive reports
// for breakage that is directly related to third party cookie blocking.
if (this.reportBreakageEnabled ||
(ThirdPartyCookies.reportBreakageEnabled &&
ThirdPartyCookies.activated &&
!FastBlock.activated &&
!TrackingProtection.activated)) {
this.reportBreakageButton.removeAttribute("hidden");
} else {
this.reportBreakageButton.setAttribute("hidden", "true");
}
if (isSimulated) {
this.iconBox.removeAttribute("animate");
} else if (active && webProgress.isTopLevel) {
this.iconBox.setAttribute("animate", "true");
if (!isBrowserPrivate) {
let introCount = Services.prefs.getIntPref(this.prefIntroCount);
if (introCount < this.MAX_INTROS) {
Services.prefs.setIntPref(this.prefIntroCount, ++introCount);
Services.prefs.savePrefFile(null);
this.showIntroPanel();
}
}
}
if (hasException) {
this.iconBox.setAttribute("tooltiptext", this.disabledTooltipText);
this.shieldHistogramAdd(1);
} else if (active) {
this.iconBox.setAttribute("tooltiptext", this.activeTooltipText);
this.shieldHistogramAdd(2);
} else {
this.iconBox.removeAttribute("tooltiptext");
this.shieldHistogramAdd(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);
}
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");
}
this.hideIdentityPopupAndReload();
},
dontShowIntroPanelAgain() {
if (!PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
Services.prefs.setIntPref(this.prefIntroCount, this.MAX_INTROS);
Services.prefs.savePrefFile(null);
}
},
async showIntroPanel() {
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
let introTitle = gNavigatorBundle.getFormattedString("contentBlocking.intro.title",
[brandShortName]);
let introDescription;
// This will be sent to the onboarding website to let them know which
// UI variation we're showing.
let variation;
// We show a different UI tour variation for users that already have TP
// enabled globally.
if (TrackingProtection.enabledGlobally) {
introDescription = gNavigatorBundle.getString("contentBlocking.intro.v2.description");
variation = 2;
} else {
introDescription = gNavigatorBundle.getFormattedString("contentBlocking.intro.v1.description",
[brandShortName]);
variation = 1;
}
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&variation=${variation}`;
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, introTitle, introDescription, undefined, buttons,
{ closeButtonCallback: () => this.dontShowIntroPanelAgain() });
},
};