Bug 1557872 - Add a new JS module for manipulating the Content Blocking allow list; r=nhnt11

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

--HG--
extra : source : 832579daee7c9e328d220611ab8929a1a4cf150b
This commit is contained in:
Ehsan Akhgari 2019-06-11 22:28:31 +00:00
Родитель ea1728721a
Коммит caea64f895
6 изменённых файлов: 151 добавлений и 115 удалений

Просмотреть файл

@ -2,6 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
ChromeUtils.defineModuleGetter(this, "ContentBlockingAllowList",
"resource://gre/modules/ContentBlockingAllowList.jsm");
var Fingerprinting = { var Fingerprinting = {
PREF_ENABLED: "privacy.trackingprotection.fingerprinting.enabled", PREF_ENABLED: "privacy.trackingprotection.fingerprinting.enabled",
reportBreakageLabel: "fingerprinting", reportBreakageLabel: "fingerprinting",
@ -1087,19 +1090,15 @@ var ContentBlocking = {
onLocationChange() { onLocationChange() {
// Reset blocking and exception status so that we can send telemetry // Reset blocking and exception status so that we can send telemetry
this.hadShieldState = false; this.hadShieldState = false;
let baseURI = this._baseURIForChannelClassifier;
// Don't deal with about:, file: etc. // Don't deal with about:, file: etc.
if (!baseURI) { if (!ContentBlockingAllowList.canHandle(gBrowser.selectedBrowser)) {
return; return;
} }
let isBrowserPrivate = PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser);
// Check whether the user has added an exception for this site. // Check whether the user has added an exception for this site.
let type = isBrowserPrivate ? "trackingprotection-pb" : "trackingprotection"; let hasException =
let hasException = Services.perms.testExactPermission(baseURI, type) == ContentBlockingAllowList.includes(gBrowser.selectedBrowser);
Services.perms.ALLOW_ACTION;
this.content.toggleAttribute("hasException", hasException); this.content.toggleAttribute("hasException", hasException);
this.protectionsPopup.toggleAttribute("hasException", hasException); this.protectionsPopup.toggleAttribute("hasException", hasException);
@ -1113,10 +1112,9 @@ var ContentBlocking = {
onContentBlockingEvent(event, webProgress, isSimulated) { onContentBlockingEvent(event, webProgress, isSimulated) {
let previousState = gBrowser.securityUI.contentBlockingEvent; let previousState = gBrowser.securityUI.contentBlockingEvent;
let baseURI = this._baseURIForChannelClassifier;
// Don't deal with about:, file: etc. // Don't deal with about:, file: etc.
if (!baseURI) { if (!ContentBlockingAllowList.canHandle(gBrowser.selectedBrowser)) {
this.iconBox.removeAttribute("animate"); this.iconBox.removeAttribute("animate");
this.iconBox.removeAttribute("active"); this.iconBox.removeAttribute("active");
this.iconBox.removeAttribute("hasException"); this.iconBox.removeAttribute("hasException");
@ -1139,12 +1137,9 @@ var ContentBlocking = {
anyBlocking = anyBlocking || blocker.activated; anyBlocking = anyBlocking || blocker.activated;
} }
let isBrowserPrivate = PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser);
// Check whether the user has added an exception for this site. // Check whether the user has added an exception for this site.
let type = isBrowserPrivate ? "trackingprotection-pb" : "trackingprotection"; let hasException =
let hasException = Services.perms.testExactPermission(baseURI, type) == ContentBlockingAllowList.includes(gBrowser.selectedBrowser);
Services.perms.ALLOW_ACTION;
// Reset the animation in case the user is switching tabs or if no blockers were detected // Reset the animation in case the user is switching tabs or if no blockers were detected
// (this is most likely happening because the user navigated on to a different site). This // (this is most likely happening because the user navigated on to a different site). This
@ -1156,6 +1151,7 @@ var ContentBlocking = {
} else if (anyBlocking && !this.iconBox.hasAttribute("active")) { } else if (anyBlocking && !this.iconBox.hasAttribute("active")) {
this.iconBox.setAttribute("animate", "true"); this.iconBox.setAttribute("animate", "true");
let isBrowserPrivate = PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser);
if (!isBrowserPrivate) { if (!isBrowserPrivate) {
let introCount = Services.prefs.getIntPref(this.prefIntroCount); let introCount = Services.prefs.getIntPref(this.prefIntroCount);
let installStamp = Services.prefs.getIntPref( let installStamp = Services.prefs.getIntPref(
@ -1230,32 +1226,13 @@ var ContentBlocking = {
}, },
disableForCurrentPage() { disableForCurrentPage() {
let baseURI = this._baseURIForChannelClassifier; ContentBlockingAllowList.add(gBrowser.selectedBrowser);
// 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(); this.hideIdentityPopupAndReload();
}, },
enableForCurrentPage() { enableForCurrentPage() {
// Remove the current host from the 'trackingprotection' consumer ContentBlockingAllowList.remove(gBrowser.selectedBrowser);
// 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(); this.hideIdentityPopupAndReload();
}, },

Просмотреть файл

@ -181,22 +181,17 @@ async function testContentBlocking(tab) {
testBenignPage(); testBenignPage();
info("Load a test page not containing tracking elements which has an exception."); info("Load a test page not containing tracking elements which has an exception.");
let isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(tab.ownerGlobal);
let uri = Services.io.newURI("https://example.org/");
if (isPrivateBrowsing) {
PrivateBrowsingUtils.addToTrackingAllowlist(uri);
} else {
Services.perms.add(uri, "trackingprotection", Services.perms.ALLOW_ACTION);
}
await promiseTabLoadEvent(tab, uri.spec); await promiseTabLoadEvent(tab, "https://example.org/?round=1");
ContentBlockingAllowList.add(tab.linkedBrowser);
// Load another page from the same origin to ensure there is an onlocationchange
// notification which would trigger an oncontentblocking notification for us.
await promiseTabLoadEvent(tab, "https://example.org/?round=2");
testBenignPageWithException(); testBenignPageWithException();
if (isPrivateBrowsing) { ContentBlockingAllowList.remove(tab.linkedBrowser);
PrivateBrowsingUtils.removeFromTrackingAllowlist(uri);
} else {
Services.perms.remove(uri, "trackingprotection");
}
info("Load a test page containing tracking elements"); info("Load a test page containing tracking elements");
await promiseTabLoadEvent(tab, gTrackingPageURL); await promiseTabLoadEvent(tab, gTrackingPageURL);
@ -206,6 +201,7 @@ async function testContentBlocking(tab) {
let tabReloadPromise = promiseTabLoadEvent(tab); let tabReloadPromise = promiseTabLoadEvent(tab);
clickButton("#tracking-action-unblock"); clickButton("#tracking-action-unblock");
await tabReloadPromise; await tabReloadPromise;
let isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(tab.ownerGlobal);
let blockedByTP = areTrackersBlocked(isPrivateBrowsing); let blockedByTP = areTrackersBlocked(isPrivateBrowsing);
testTrackingPageUnblocked(blockedByTP, tab.ownerGlobal); testTrackingPageUnblocked(blockedByTP, tab.ownerGlobal);

Просмотреть файл

@ -111,6 +111,9 @@ ChromeUtils.defineModuleGetter(this, "FormLikeFactory",
ChromeUtils.defineModuleGetter(this, "GeckoViewAutoFill", ChromeUtils.defineModuleGetter(this, "GeckoViewAutoFill",
"resource://gre/modules/GeckoViewAutoFill.jsm"); "resource://gre/modules/GeckoViewAutoFill.jsm");
ChromeUtils.defineModuleGetter(this, "ContentBlockingAllowList",
"resource://gre/modules/ContentBlockingAllowList.jsm");
var GlobalEventDispatcher = EventDispatcher.instance; var GlobalEventDispatcher = EventDispatcher.instance;
var WindowEventDispatcher = EventDispatcher.for(window); var WindowEventDispatcher = EventDispatcher.for(window);
@ -1881,29 +1884,14 @@ var BrowserApp = {
} }
if (data.contentType === "tracking") { if (data.contentType === "tracking") {
// Convert document URI into the format used by
// nsChannelClassifier::ShouldEnableTrackingProtection
// (any scheme turned into https is correct)
let normalizedUrl = Services.io.newURI("https://" + browser.currentURI.hostPort);
if (data.allowContent) { if (data.allowContent) {
// Add the current host in the 'trackingprotection' consumer of ContentBlockingAllowList.add(browser);
// the permission manager using a normalized URI. This effectively if (!PrivateBrowsingUtils.isBrowserPrivate(browser)) {
// places this host on the tracking protection white list.
if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
PrivateBrowsingUtils.addToTrackingAllowlist(normalizedUrl);
} else {
Services.perms.addFromPrincipal(browser.contentPrincipal, "trackingprotection", Services.perms.ALLOW_ACTION);
Telemetry.addData("TRACKING_PROTECTION_EVENTS", 1); Telemetry.addData("TRACKING_PROTECTION_EVENTS", 1);
} }
} else { } else {
// Remove the current host from the 'trackingprotection' consumer ContentBlockingAllowList.remove(browser);
// of the permission manager. This effectively removes this host if (!PrivateBrowsingUtils.isBrowserPrivate(browser)) {
// from the tracking protection white list (any list actually).
// eslint-disable-next-line no-lonely-if
if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
PrivateBrowsingUtils.removeFromTrackingAllowlist(normalizedUrl);
} else {
Services.perms.removeFromPrincipal(browser.contentPrincipal, "trackingprotection");
Telemetry.addData("TRACKING_PROTECTION_EVENTS", 2); Telemetry.addData("TRACKING_PROTECTION_EVENTS", 2);
} }
} }

Просмотреть файл

@ -0,0 +1,119 @@
/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = [ "ContentBlockingAllowList" ];
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
/**
* A helper module to manage the Content Blocking Allow List.
*
* This module provides a couple of utility APIs for adding or
* removing a given browser object to the Content Blocking allow
* list.
*/
const ContentBlockingAllowList = {
_observingLastPBContext: false,
_maybeSetupLastPBContextObserver() {
if (!this._observingLastPBContext) {
this._observer = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
observe(subject, topic, data) {
if (topic == "last-pb-context-exited") {
Services.perms.removeByType("trackingprotection-pb");
}
},
};
Services.obs.addObserver(this._observer, "last-pb-context-exited", true);
this._observingLastPBContext = true;
}
},
_baseURIForAntiTrackingCommon(browser) {
// Convert document URI into the format used by
// AntiTrackingCommon::IsOnContentBlockingAllowList.
// Any scheme turned into https is correct.
try {
return Services.io.newURI("https://" + browser.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;
}
},
_basePrincipalForAntiTrackingCommon(browser) {
let baseURI = this._baseURIForAntiTrackingCommon(browser);
if (!baseURI) {
return null;
}
let attrs = browser.contentPrincipal.originAttributes;
return Services.scriptSecurityManager
.createCodebasePrincipal(baseURI, attrs);
},
_permissionTypeFor(browser) {
return PrivateBrowsingUtils.isBrowserPrivate(browser) ?
"trackingprotection-pb" :
"trackingprotection";
},
_expiryFor(browser) {
return PrivateBrowsingUtils.isBrowserPrivate(browser) ?
Ci.nsIPermissionManager.EXPIRE_SESSION :
Ci.nsIPermissionManager.EXPIRE_NEVER;
},
/**
* Returns false if this module cannot handle the current document loaded in
* the browser object. This can happen for example for about: or file:
* documents.
*/
canHandle(browser) {
return this._basePrincipalForAntiTrackingCommon(browser) != null;
},
/**
* Add the given browser object to the Content Blocking allow list.
*/
add(browser) {
// Start observing PB last-context-exit notification to do the needed cleanup.
this._maybeSetupLastPBContextObserver();
let prin = this._basePrincipalForAntiTrackingCommon(browser);
let type = this._permissionTypeFor(browser);
let expire = this._expiryFor(browser);
Services.perms.addFromPrincipal(prin, type,
Services.perms.ALLOW_ACTION,
expire);
},
/**
* Remove the given browser object from the Content Blocking allow list.
*/
remove(browser) {
let prin = this._basePrincipalForAntiTrackingCommon(browser);
let type = this._permissionTypeFor(browser);
Services.perms.removeFromPrincipal(prin, type);
},
/**
* Returns true if the current browser has loaded a document that is on the
* Content Blocking allow list.
*/
includes(browser) {
let prin = this._basePrincipalForAntiTrackingCommon(browser);
let type = this._permissionTypeFor(browser);
return Services.perms.testExactPermissionFromPrincipal(prin, type) ==
Services.perms.ALLOW_ACTION;
},
};

Просмотреть файл

@ -33,6 +33,10 @@ UNIFIED_SOURCES += [
'StoragePrincipalHelper.cpp', 'StoragePrincipalHelper.cpp',
] ]
EXTRA_JS_MODULES += [
'ContentBlockingAllowList.jsm',
]
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [
'/extensions/permissions', '/extensions/permissions',
] ]

Просмотреть файл

@ -6,41 +6,6 @@ var EXPORTED_SYMBOLS = ["PrivateBrowsingUtils"];
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
function PrivateBrowsingContentBlockingAllowList() {
Services.obs.addObserver(this, "last-pb-context-exited", true);
}
PrivateBrowsingContentBlockingAllowList.prototype = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
/**
* Add the provided URI to the list of allowed tracking sites.
*
* @param uri nsIURI
* The URI to add to the list.
*/
addToAllowList(uri) {
Services.perms.add(uri, "trackingprotection-pb", Ci.nsIPermissionManager.ALLOW_ACTION,
Ci.nsIPermissionManager.EXPIRE_SESSION);
},
/**
* Remove the provided URI from the list of allowed tracking sites.
*
* @param uri nsIURI
* The URI to remove from the list.
*/
removeFromAllowList(uri) {
Services.perms.remove(uri, "trackingprotection-pb");
},
observe(subject, topic, data) {
if (topic == "last-pb-context-exited") {
Services.perms.removeByType("trackingprotection-pb");
}
},
};
const kAutoStartPref = "browser.privatebrowsing.autostart"; const kAutoStartPref = "browser.privatebrowsing.autostart";
// This will be set to true when the PB mode is autostarted from the command // This will be set to true when the PB mode is autostarted from the command
@ -86,19 +51,6 @@ var PrivateBrowsingUtils = {
return aWindow.docShell.QueryInterface(Ci.nsILoadContext); return aWindow.docShell.QueryInterface(Ci.nsILoadContext);
}, },
get _pbCBAllowList() {
delete this._pbCBAllowList;
return this._pbCBAllowList = new PrivateBrowsingContentBlockingAllowList();
},
addToTrackingAllowlist(aURI) {
this._pbCBAllowList.addToAllowList(aURI);
},
removeFromTrackingAllowlist(aURI) {
this._pbCBAllowList.removeFromAllowList(aURI);
},
get permanentPrivateBrowsing() { get permanentPrivateBrowsing() {
try { try {
return gTemporaryAutoStartMode || return gTemporaryAutoStartMode ||